142 lines
5.1 KiB
Markdown
142 lines
5.1 KiB
Markdown
# To AI Agent: Refactor Itinerary Timeline into an Exclusive File Tree Directory System (Single Expansion Mode)
|
|
|
|
## 1. Context & Architectural Analogy
|
|
We are refactoring the `ItineraryTimeline` component on the mobile view (`yotrip.labz.io.vn`) using a **File Tree Directory System** analogy:
|
|
- **Parent Folders ("Thư mục mẹ"):** Represented by the Stages ("Chặng 1", "Chặng 2"...).
|
|
- **Child Nodes ("Thư mục con"):** Represented by the Locations/Destinations inside that stage (`Trần Cao Vân`, `Đồng Khởi`...).
|
|
|
|
### CRITICAL LOGIC CONSTRAINT (Exclusive Accordion):
|
|
- When a Parent Folder (Stage) is **collapsed**, all of its Child Nodes (Locations) must immediately hide cleanly inside it.
|
|
- **Single Expansion Rule:** The system must enforce an **exclusive single-expansion mode**. On the entire timeline screen, **MAXIMUM ONE** Parent Folder can be expanded at any given time.
|
|
- Opening/Expanding a new Stage folder must **automatically collapse** whichever Stage folder was previously open.
|
|
|
|
---
|
|
|
|
## 2. State Management Specification (For Script/Logic Implementation)
|
|
|
|
To enforce the "Maximum 1 Expanded Folder" rule, do NOT use isolated individual boolean flags for each stage. Instead, implement a centralized single-active-state variable:
|
|
|
|
```javascript
|
|
// Example React State Hook Blueprint:
|
|
// Track the ID or index of the single active expanded stage folder.
|
|
// If null, all stages are collapsed.
|
|
const [expandedStageId, setExpandedStageId] = useState(initialStageId);
|
|
|
|
const handleStageToggle = (stageId) => {
|
|
// If clicking the already open folder, close it. Otherwise, open the new one and shut the rest.
|
|
setExpandedStageId(prevId => prevId === stageId ? null : stageId);
|
|
};
|
|
|
|
## 3. Component DOM Tree & CSS Blueprint
|
|
### A. Component Layout Structure
|
|
|
|
<div class="file-tree-itinerary-container">
|
|
|
|
<div class="fixed-top-header-block">
|
|
<header class="main-tour-header">...</header>
|
|
<nav class="sub-navigation-tabs">...</nav>
|
|
</div>
|
|
|
|
<main class="directory-scroll-viewport">
|
|
|
|
<section class="folder-node-wrapper collapsed">
|
|
<div class="folder-header-row" onclick="handleStageToggle('stage_1')">
|
|
<span class="folder-badge-index">1</span>
|
|
<h3 class="folder-title">Chặng khởi đầu</h3>
|
|
</div>
|
|
<div class="folder-child-content-box">
|
|
</div>
|
|
</section>
|
|
|
|
<section class="folder-node-wrapper expanded">
|
|
<div class="folder-header-row" onclick="handleStageToggle('stage_2')">
|
|
<span class="folder-badge-index">2</span>
|
|
<h3 class="folder-title">Chặng 2</h3>
|
|
</div>
|
|
|
|
<div class="folder-child-content-box">
|
|
<div class="child-nodes-list">
|
|
<div class="location-child-card">Trần Cao Vân</div>
|
|
<div class="location-child-card">Đồng Khởi</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
</main>
|
|
</div>
|
|
|
|
### B. Core CSS Layout Styles
|
|
|
|
.file-tree-itinerary-container {
|
|
display: flex !important;
|
|
flex-direction: column !important;
|
|
height: 100dvh !important;
|
|
width: 100vw !important;
|
|
overflow: hidden !important;
|
|
}
|
|
|
|
.directory-scroll-viewport {
|
|
flex: 1 1 0% !important;
|
|
overflow-y: auto !important;
|
|
padding: 0 !important;
|
|
margin: 0 !important;
|
|
background-color: #f8fafc;
|
|
}
|
|
|
|
/* Base style for folder blocks stacked flush against each other */
|
|
.folder-node-wrapper {
|
|
width: 100% !important;
|
|
background-color: #ffffff;
|
|
margin-bottom: 1px !important; /* Micro hair-line divider between folders */
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.folder-header-row {
|
|
width: 100%;
|
|
padding: 12px 16px !important;
|
|
display: flex;
|
|
align-items: center;
|
|
background-color: #ffffff;
|
|
cursor: pointer;
|
|
/* Sticky behavior remains active when scrolling within an open directory */
|
|
position: sticky !important;
|
|
top: 0px;
|
|
z-index: 30;
|
|
}
|
|
|
|
/* CSS Grid Transition Engine for smooth directory expansions */
|
|
.folder-child-content-box {
|
|
display: grid !important;
|
|
grid-template-rows: 0fr;
|
|
transition: grid-template-rows 0.25s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
|
width: 100%;
|
|
}
|
|
|
|
.child-nodes-list {
|
|
overflow: hidden;
|
|
min-height: 0px;
|
|
padding: 16px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
/* --- MUTUALLY EXCLUSIVE STATE STYLES --- */
|
|
|
|
/* Collapsed Folder: Sub-files shrink immediately to 0 height */
|
|
.folder-node-wrapper.collapsed .folder-child-content-box {
|
|
grid-template-rows: 0fr !important;
|
|
}
|
|
|
|
/* Expanded Folder: Opens dynamically to accommodate dynamic content height */
|
|
.folder-node-wrapper.expanded .folder-child-content-box {
|
|
grid-template-rows: 1fr !important;
|
|
}
|
|
|
|
## 4. Acceptance Criteria for AI Agent Verification
|
|
[ ] Mutually Exclusive Test: Clicking an inactive Stage header while another stage is open must trigger a simultaneous transition: the old stage collapses back into a tight header row, and the clicked stage expands its location cards.
|
|
|
|
[ ] Zero-Gap Validation: All collapsed folder components must sit completely flush against the top tab block and one another, eliminating all gray margin bleeding.
|
|
|
|
[ ] Under-Clip Containment: When scrolling a long open folder, the list items must slide behind their sticky active parent folder row and disappear without visual overlapping artifacts. |