feat(shell): fluid layout with collapsible sidebar drawer + fix query-tool MultiSelect

Convert portal shell from block-centered (max-width 1600px) layout to full-viewport
fluid flexbox with collapsible sidebar: desktop push-mode (240px → 0), mobile overlay
drawer with backdrop. Rename .content → .shell-content to avoid CSS collision with
page-level classes. Override page-level max-width constraints when embedded in shell.

Also replace native <select multiple> in query-tool with shared MultiSelect component
for equipment and workcenter group filters, matching resource-status/history UX.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
egg
2026-02-11 18:04:55 +08:00
parent 1e7f8f4498
commit 35d83d424c
17 changed files with 826 additions and 53 deletions

View File

@@ -0,0 +1,70 @@
## Purpose
Define stable requirements for collapsible-sidebar-drawer.
## Requirements
### Requirement: Sidebar SHALL be collapsible via a toggle button
The portal shell SHALL provide a toggle button in the header that collapses and expands the sidebar. On desktop viewports (>900px), collapsing SHALL animate the sidebar width from 240px to 0px using push mode, causing the content area to resize and fill the freed space. Expanding SHALL reverse the animation.
#### Scenario: Desktop sidebar collapse
- **WHEN** a user clicks the sidebar toggle button on a desktop viewport
- **AND** the sidebar is currently expanded
- **THEN** the sidebar width SHALL animate to 0px over 300ms
- **THEN** the content area SHALL expand to fill the full viewport width
#### Scenario: Desktop sidebar expand
- **WHEN** a user clicks the sidebar toggle button on a desktop viewport
- **AND** the sidebar is currently collapsed
- **THEN** the sidebar width SHALL animate to 240px over 300ms
- **THEN** the content area SHALL shrink to accommodate the sidebar
### Requirement: Mobile sidebar SHALL use overlay drawer mode
On mobile viewports (<=900px), the sidebar SHALL behave as a fixed-position overlay drawer that slides in from the left, with a semi-transparent backdrop covering the content area.
#### Scenario: Mobile sidebar open
- **WHEN** a user taps the toggle button on a mobile viewport
- **AND** the sidebar is currently hidden
- **THEN** the sidebar SHALL slide in from the left as a fixed overlay (280px width)
- **THEN** a semi-transparent backdrop SHALL appear behind the sidebar and above the content
#### Scenario: Mobile sidebar close via backdrop
- **WHEN** a user taps the backdrop overlay
- **THEN** the sidebar SHALL slide out to the left
- **THEN** the backdrop SHALL fade out
#### Scenario: Mobile sidebar close via Escape key
- **WHEN** the mobile sidebar overlay is open
- **AND** a user presses the Escape key
- **THEN** the sidebar SHALL close
#### Scenario: Mobile sidebar closes on navigation
- **WHEN** the mobile sidebar overlay is open
- **AND** a user clicks a navigation link
- **THEN** the sidebar SHALL automatically close after the route change
### Requirement: Sidebar state SHALL persist within the browser session
The collapsed/expanded state of the desktop sidebar SHALL be persisted to `sessionStorage` so that the preference survives page refreshes within the same browser tab.
#### Scenario: State persistence on refresh
- **WHEN** a user collapses the sidebar and refreshes the page
- **THEN** the sidebar SHALL remain collapsed after reload
#### Scenario: New tab starts expanded
- **WHEN** a user opens the portal in a new browser tab
- **THEN** the sidebar SHALL default to expanded regardless of other tabs' state
### Requirement: Sidebar transitions SHALL respect reduced-motion preference
All sidebar transitions (width animation, overlay slide, backdrop fade) SHALL be disabled when the user has `prefers-reduced-motion: reduce` enabled.
#### Scenario: Reduced motion disables sidebar animation
- **WHEN** the user's OS or browser has reduced-motion enabled
- **AND** the user toggles the sidebar
- **THEN** the sidebar state SHALL change instantly without animation
### Requirement: Toggle button SHALL be accessible
The sidebar toggle button SHALL include `aria-label` and `aria-expanded` attributes for screen reader accessibility.
#### Scenario: Screen reader announces toggle state
- **WHEN** a screen reader user focuses the toggle button
- **THEN** the button SHALL announce its current expanded/collapsed state via `aria-expanded`
- **THEN** the button SHALL have an `aria-label` describing its purpose

View File

@@ -2,12 +2,29 @@
Define stable requirements for spa-shell-navigation.
## Requirements
### Requirement: Portal SHALL provide a SPA shell driven by Vue Router
The portal frontend SHALL use a single SPA shell entry and Vue Router to render page modules without iframe embedding, and SHALL route each page through either native route-view integration or a temporary wrapper component.
The portal frontend SHALL use a single SPA shell entry and Vue Router to render page modules without iframe embedding, and SHALL route each page through native route-view integration. The shell layout SHALL use a full-viewport fluid layout with flexbox, removing all max-width constraints and block-centered styling. The main content area (`.shell-content`) SHALL fill available space as a flex child, and the sidebar SHALL be a collapsible flex child that pushes content when expanded on desktop. The content area class SHALL be `.shell-content` (not `.content`) to avoid CSS collision with page-level `.content` classes.
#### Scenario: Drawer navigation renders integrated route view
- **WHEN** a user clicks a sidebar page entry whose migration mode is `native`
- **THEN** the active route SHALL be updated through Vue Router
- **THEN** the main content area SHALL render the corresponding page module inside shell route-view without iframe usage
- **THEN** the content area SHALL fill the available viewport width minus the sidebar width (if sidebar is expanded)
#### Scenario: Shell layout fills full viewport
- **WHEN** the portal shell renders
- **THEN** the shell SHALL span the full viewport width with no max-width constraint
- **THEN** the header SHALL span edge-to-edge with no border-radius
- **THEN** the sidebar and content area SHALL have no outer borders or border-radius
#### Scenario: Page-level max-width constraints are removed when embedded
- **WHEN** a page module registered in the shell route contracts renders inside `.shell-content`
- **THEN** page-level max-width constraints SHALL be overridden to allow full-width rendering
- **THEN** page-level duplicate padding SHALL be removed to avoid double spacing
- **THEN** standalone page rendering (outside the shell) SHALL remain unaffected
#### Scenario: Shell content class avoids collision with page-level classes
- **WHEN** a page module that uses its own `.content` class renders inside the shell
- **THEN** the shell's content wrapper (`.shell-content`) SHALL NOT interfere with the page's `.content` styling
#### Scenario: Wrapper route remains available during migration
- **WHEN** a user clicks a sidebar page entry whose migration mode is `wrapper`

View File

@@ -4,13 +4,19 @@ Define stable requirements for tailwind-design-system.
## Requirements
### Requirement: Frontend styles SHALL be governed by Tailwind design tokens
The frontend SHALL define a Tailwind-based design token system for color, spacing, typography, radius, and elevation to ensure consistent styling across modules.
The frontend SHALL define a Tailwind-based design token system for color, spacing, typography, radius, and elevation to ensure consistent styling across modules. The `--portal-shell-max-width` CSS variable SHALL be set to `none` to support the fluid layout. A `--portal-sidebar-width` variable SHALL be added for sidebar width reference. The `.u-content-shell` utility class SHALL use `width: 100%` instead of `max-width` constraint.
#### Scenario: Shared token usage across modules
- **WHEN** two report modules render equivalent UI elements (e.g., card, filter chip, primary button)
- **THEN** they SHALL use the same token-backed style semantics
- **THEN** visual output SHALL remain consistent across modules
#### Scenario: Fluid layout tokens
- **WHEN** the portal shell renders
- **THEN** `--portal-shell-max-width` SHALL resolve to `none`
- **THEN** `--portal-sidebar-width` SHALL resolve to `240px`
- **THEN** `.u-content-shell` SHALL apply `width: 100%` without max-width constraint
### Requirement: Tailwind migration SHALL support coexistence with legacy CSS
The migration SHALL allow Tailwind and existing page CSS to coexist during phased rollout without breaking existing pages.