@qijenchen/design-system 0.1.0-beta.145 → 0.1.0-beta.147
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Accordion/accordion.d.ts +1 -1
- package/dist/components/Accordion/accordion.d.ts.map +1 -1
- package/dist/components/Accordion/accordion.js +8 -3
- package/dist/components/Accordion/accordion.js.map +1 -1
- package/dist/components/AccountMenu/account-menu.d.ts.map +1 -1
- package/dist/components/AccountMenu/account-menu.js.map +1 -1
- package/dist/components/AgentPanel/agent-panel-fab.d.ts +7 -1
- package/dist/components/AgentPanel/agent-panel-fab.d.ts.map +1 -1
- package/dist/components/AgentPanel/agent-panel-fab.js +36 -14
- package/dist/components/AgentPanel/agent-panel-fab.js.map +1 -1
- package/dist/components/AgentPanel/agent-panel-logo.d.ts +3 -3
- package/dist/components/AgentPanel/agent-panel-logo.js.map +1 -1
- package/dist/components/AgentPanel/agent-panel.d.ts +1 -1
- package/dist/components/AgentPanel/agent-panel.d.ts.map +1 -1
- package/dist/components/AgentPanel/agent-panel.js +28 -22
- package/dist/components/AgentPanel/agent-panel.js.map +1 -1
- package/dist/components/AppShell/app-shell.d.ts +1 -1
- package/dist/components/AppShell/app-shell.js +1 -1
- package/dist/components/AppShell/app-shell.js.map +1 -1
- package/dist/components/Avatar/avatar.d.ts +95 -1
- package/dist/components/Avatar/avatar.d.ts.map +1 -1
- package/dist/components/Avatar/avatar.js +106 -15
- package/dist/components/Avatar/avatar.js.map +1 -1
- package/dist/components/Avatar/index.js +14 -2
- package/dist/components/Breadcrumb/breadcrumb.d.ts +4 -2
- package/dist/components/Breadcrumb/breadcrumb.d.ts.map +1 -1
- package/dist/components/Breadcrumb/breadcrumb.js +2 -2
- package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.d.ts.map +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.js +27 -2
- package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
- package/dist/components/Button/button.d.ts +2 -2
- package/dist/components/Button/button.d.ts.map +1 -1
- package/dist/components/Button/button.js +118 -22
- package/dist/components/Button/button.js.map +1 -1
- package/dist/components/Calendar/calendar.d.ts +34 -10
- package/dist/components/Calendar/calendar.d.ts.map +1 -1
- package/dist/components/Calendar/calendar.js +210 -36
- package/dist/components/Calendar/calendar.js.map +1 -1
- package/dist/components/Carousel/carousel.d.ts +1 -1
- package/dist/components/Carousel/carousel.d.ts.map +1 -1
- package/dist/components/Carousel/carousel.js +3 -2
- package/dist/components/Carousel/carousel.js.map +1 -1
- package/dist/components/Chart/chart.d.ts +1 -0
- package/dist/components/Chart/chart.d.ts.map +1 -1
- package/dist/components/Chart/chart.js +8 -0
- package/dist/components/Chart/chart.js.map +1 -1
- package/dist/components/Checkbox/checkbox.d.ts +1 -1
- package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox/checkbox.js +10 -2
- package/dist/components/Checkbox/checkbox.js.map +1 -1
- package/dist/components/Chip/chip.d.ts +1 -1
- package/dist/components/Chip/chip.d.ts.map +1 -1
- package/dist/components/Chip/chip.js +4 -2
- package/dist/components/Chip/chip.js.map +1 -1
- package/dist/components/Combobox/combobox.d.ts +1 -1
- package/dist/components/Combobox/combobox.d.ts.map +1 -1
- package/dist/components/Combobox/combobox.js +37 -11
- package/dist/components/Combobox/combobox.js.map +1 -1
- package/dist/components/Command/command.d.ts +1 -1
- package/dist/components/Command/command.d.ts.map +1 -1
- package/dist/components/Command/command.js +106 -8
- package/dist/components/Command/command.js.map +1 -1
- package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
- package/dist/components/DataTable/cell-registry.js +3 -2
- package/dist/components/DataTable/cell-registry.js.map +1 -1
- package/dist/components/DataTable/data-table-column-visibility-panel.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-column-visibility-panel.js +24 -24
- package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -1
- package/dist/components/DataTable/data-table-interaction-layer.d.ts +2 -1
- package/dist/components/DataTable/data-table-interaction-layer.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-interaction-layer.js +3 -4
- package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
- package/dist/components/DataTable/data-table-sort-manager.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-sort-manager.js +20 -20
- package/dist/components/DataTable/data-table-sort-manager.js.map +1 -1
- package/dist/components/DataTable/data-table.d.ts +8 -5
- package/dist/components/DataTable/data-table.d.ts.map +1 -1
- package/dist/components/DataTable/data-table.js +197 -71
- package/dist/components/DataTable/data-table.js.map +1 -1
- package/dist/components/DateGrid/date-grid.d.ts +1 -1
- package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
- package/dist/components/DateGrid/date-grid.js +358 -225
- package/dist/components/DateGrid/date-grid.js.map +1 -1
- package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
- package/dist/components/DatePicker/date-picker.js +16 -1
- package/dist/components/DatePicker/date-picker.js.map +1 -1
- package/dist/components/Dialog/dialog.d.ts.map +1 -1
- package/dist/components/Dialog/dialog.js +55 -43
- package/dist/components/Dialog/dialog.js.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu-keyboard.d.ts +86 -0
- package/dist/components/DropdownMenu/dropdown-menu-keyboard.d.ts.map +1 -0
- package/dist/components/DropdownMenu/dropdown-menu-keyboard.js +172 -0
- package/dist/components/DropdownMenu/dropdown-menu-keyboard.js.map +1 -0
- package/dist/components/DropdownMenu/dropdown-menu.d.ts +6 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.js +50 -39
- package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
- package/dist/components/Field/field-context.d.ts +4 -0
- package/dist/components/Field/field-context.d.ts.map +1 -1
- package/dist/components/Field/field-context.js +1 -2
- package/dist/components/Field/field-context.js.map +1 -1
- package/dist/components/Field/field-edit-keys.d.ts +8 -0
- package/dist/components/Field/field-edit-keys.d.ts.map +1 -1
- package/dist/components/Field/field-edit-keys.js +3 -0
- package/dist/components/Field/field-edit-keys.js.map +1 -1
- package/dist/components/Field/field-wrapper.d.ts +18 -1
- package/dist/components/Field/field-wrapper.d.ts.map +1 -1
- package/dist/components/Field/field-wrapper.js +39 -10
- package/dist/components/Field/field-wrapper.js.map +1 -1
- package/dist/components/Field/field.d.ts +3 -2
- package/dist/components/Field/field.d.ts.map +1 -1
- package/dist/components/Field/field.js +2 -1
- package/dist/components/Field/field.js.map +1 -1
- package/dist/components/FileItem/file-item.d.ts +26 -2
- package/dist/components/FileItem/file-item.d.ts.map +1 -1
- package/dist/components/FileItem/file-item.js +82 -25
- package/dist/components/FileItem/file-item.js.map +1 -1
- package/dist/components/FileItem/index.js +4 -2
- package/dist/components/FileUpload/file-upload.d.ts +1 -1
- package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
- package/dist/components/FileUpload/file-upload.js +132 -55
- package/dist/components/FileUpload/file-upload.js.map +1 -1
- package/dist/components/FileViewer/file-viewer.d.ts +1 -1
- package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
- package/dist/components/FileViewer/file-viewer.js +9 -4
- package/dist/components/FileViewer/file-viewer.js.map +1 -1
- package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -1
- package/dist/components/InlineEdit/inline-edit.js +1 -1
- package/dist/components/InlineEdit/inline-edit.js.map +1 -1
- package/dist/components/Input/input.d.ts.map +1 -1
- package/dist/components/Input/input.js +6 -1
- package/dist/components/Input/input.js.map +1 -1
- package/dist/components/LinkInput/link-input.d.ts.map +1 -1
- package/dist/components/LinkInput/link-input.js +20 -4
- package/dist/components/LinkInput/link-input.js.map +1 -1
- package/dist/components/Notice/notice.js +1 -1
- package/dist/components/Notice/notice.js.map +1 -1
- package/dist/components/NumberInput/number-input.d.ts.map +1 -1
- package/dist/components/NumberInput/number-input.js +6 -1
- package/dist/components/NumberInput/number-input.js.map +1 -1
- package/dist/components/OverflowIndicator/index.js +2 -1
- package/dist/components/OverflowIndicator/overflow-indicator.d.ts +19 -1
- package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
- package/dist/components/OverflowIndicator/overflow-indicator.js +47 -27
- package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
- package/dist/components/Pagination/pagination.d.ts +1 -1
- package/dist/components/Pagination/pagination.js +1 -1
- package/dist/components/Pagination/pagination.js.map +1 -1
- package/dist/components/PeoplePicker/avatar-stack-overflow.d.ts +7 -6
- package/dist/components/PeoplePicker/avatar-stack-overflow.d.ts.map +1 -1
- package/dist/components/PeoplePicker/avatar-stack-overflow.js +6 -7
- package/dist/components/PeoplePicker/avatar-stack-overflow.js.map +1 -1
- package/dist/components/PeoplePicker/people-picker-helpers.d.ts.map +1 -1
- package/dist/components/PeoplePicker/people-picker-helpers.js +1 -1
- package/dist/components/PeoplePicker/people-picker-helpers.js.map +1 -1
- package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
- package/dist/components/PeoplePicker/people-picker.js +2 -2
- package/dist/components/PeoplePicker/people-picker.js.map +1 -1
- package/dist/components/PeoplePicker/person-display.d.ts.map +1 -1
- package/dist/components/PeoplePicker/person-display.js +26 -28
- package/dist/components/PeoplePicker/person-display.js.map +1 -1
- package/dist/components/ProfileCard/profile-card.d.ts.map +1 -1
- package/dist/components/ProfileCard/profile-card.js.map +1 -1
- package/dist/components/RadioGroup/radio-group.d.ts +1 -1
- package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
- package/dist/components/RadioGroup/radio-group.js +10 -4
- package/dist/components/RadioGroup/radio-group.js.map +1 -1
- package/dist/components/Rating/rating.d.ts +28 -6
- package/dist/components/Rating/rating.d.ts.map +1 -1
- package/dist/components/Rating/rating.js +97 -80
- package/dist/components/Rating/rating.js.map +1 -1
- package/dist/components/ScrollArea/scroll-area.d.ts +1 -1
- package/dist/components/ScrollArea/scroll-area.js +1 -1
- package/dist/components/ScrollArea/scroll-area.js.map +1 -1
- package/dist/components/SegmentedControl/segmented-control.d.ts +1 -1
- package/dist/components/SegmentedControl/segmented-control.d.ts.map +1 -1
- package/dist/components/SegmentedControl/segmented-control.js +3 -2
- package/dist/components/SegmentedControl/segmented-control.js.map +1 -1
- package/dist/components/Select/select.d.ts.map +1 -1
- package/dist/components/Select/select.js +15 -8
- package/dist/components/Select/select.js.map +1 -1
- package/dist/components/SelectMenu/index.js +2 -1
- package/dist/components/SelectMenu/index.js.map +1 -1
- package/dist/components/SelectMenu/select-menu-keyboard.d.ts +59 -0
- package/dist/components/SelectMenu/select-menu-keyboard.d.ts.map +1 -0
- package/dist/components/SelectMenu/select-menu-keyboard.js +107 -0
- package/dist/components/SelectMenu/select-menu-keyboard.js.map +1 -0
- package/dist/components/SelectMenu/select-menu.d.ts +1 -22
- package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
- package/dist/components/SelectMenu/select-menu.js +18 -34
- package/dist/components/SelectMenu/select-menu.js.map +1 -1
- package/dist/components/SelectionControl/selection-item.d.ts.map +1 -1
- package/dist/components/SelectionControl/selection-item.js +1 -0
- package/dist/components/SelectionControl/selection-item.js.map +1 -1
- package/dist/components/Sidebar/sidebar.d.ts +7 -9
- package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
- package/dist/components/Sidebar/sidebar.js +171 -50
- package/dist/components/Sidebar/sidebar.js.map +1 -1
- package/dist/components/Slider/slider.d.ts +1 -1
- package/dist/components/Slider/slider.d.ts.map +1 -1
- package/dist/components/Slider/slider.js +9 -4
- package/dist/components/Slider/slider.js.map +1 -1
- package/dist/components/Steps/steps.d.ts.map +1 -1
- package/dist/components/Steps/steps.js +54 -26
- package/dist/components/Steps/steps.js.map +1 -1
- package/dist/components/Switch/switch.d.ts +1 -1
- package/dist/components/Switch/switch.d.ts.map +1 -1
- package/dist/components/Switch/switch.js +7 -2
- package/dist/components/Switch/switch.js.map +1 -1
- package/dist/components/Tabs/tabs.d.ts +1 -1
- package/dist/components/Tabs/tabs.d.ts.map +1 -1
- package/dist/components/Tabs/tabs.js +39 -4
- package/dist/components/Tabs/tabs.js.map +1 -1
- package/dist/components/Tag/tag.d.ts +1 -1
- package/dist/components/Tag/tag.d.ts.map +1 -1
- package/dist/components/Tag/tag.js +3 -2
- package/dist/components/Tag/tag.js.map +1 -1
- package/dist/components/Textarea/textarea.d.ts.map +1 -1
- package/dist/components/Textarea/textarea.js +8 -2
- package/dist/components/Textarea/textarea.js.map +1 -1
- package/dist/components/TimePicker/time-columns.d.ts.map +1 -1
- package/dist/components/TimePicker/time-columns.js +8 -4
- package/dist/components/TimePicker/time-columns.js.map +1 -1
- package/dist/components/TimePicker/time-picker.d.ts +1 -1
- package/dist/components/TimePicker/time-picker.js +1 -1
- package/dist/components/TimePicker/time-picker.js.map +1 -1
- package/dist/components/TreeView/tree-view.d.ts +9 -5
- package/dist/components/TreeView/tree-view.d.ts.map +1 -1
- package/dist/components/TreeView/tree-view.js +266 -214
- package/dist/components/TreeView/tree-view.js.map +1 -1
- package/dist/hooks/use-input-modality.d.ts +12 -17
- package/dist/hooks/use-input-modality.d.ts.map +1 -1
- package/dist/hooks/use-input-modality.js +4 -34
- package/dist/hooks/use-input-modality.js.map +1 -1
- package/dist/hooks/use-overflow-items.d.ts.map +1 -1
- package/dist/hooks/use-overflow-items.js +2 -0
- package/dist/hooks/use-overflow-items.js.map +1 -1
- package/dist/hooks/use-truncated.d.ts.map +1 -1
- package/dist/hooks/use-truncated.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/drag-announcements.d.ts +32 -0
- package/dist/lib/drag-announcements.d.ts.map +1 -1
- package/dist/lib/drag-announcements.js +39 -1
- package/dist/lib/drag-announcements.js.map +1 -1
- package/dist/lib/focus-after-trigger.d.ts +45 -0
- package/dist/lib/focus-after-trigger.d.ts.map +1 -0
- package/dist/lib/focus-after-trigger.js +66 -0
- package/dist/lib/focus-after-trigger.js.map +1 -0
- package/dist/lib/overlay-coexistence.js +1 -1
- package/dist/lib/roving-list-keyboard.d.ts +208 -0
- package/dist/lib/roving-list-keyboard.d.ts.map +1 -0
- package/dist/lib/roving-list-keyboard.js +174 -0
- package/dist/lib/roving-list-keyboard.js.map +1 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +6 -1
- package/dist/lib/utils.js.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.d.ts +4 -4
- package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.js +14 -4
- package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
- package/dist/patterns/horizontal-overflow/horizontal-overflow.d.ts.map +1 -1
- package/dist/patterns/horizontal-overflow/horizontal-overflow.js +25 -0
- package/dist/patterns/horizontal-overflow/horizontal-overflow.js.map +1 -1
- package/dist/patterns/overlay-surface/overlay-surface.d.ts.map +1 -1
- package/dist/patterns/overlay-surface/overlay-surface.js.map +1 -1
- package/dist/tokens/categorical-color.d.ts +6 -1
- package/dist/tokens/categorical-color.d.ts.map +1 -1
- package/dist/tokens/categorical-color.js.map +1 -1
- package/dist/tokens/uiSize/icon-size.d.ts +1 -1
- package/dist/tokens/uiSize/stack-gap.d.ts +18 -0
- package/dist/tokens/uiSize/stack-gap.d.ts.map +1 -0
- package/dist/tokens/uiSize/stack-gap.js +5 -0
- package/dist/tokens/uiSize/stack-gap.js.map +1 -0
- package/ds-canonical/fork/codex/agents/skills/performance-audit/SKILL.md +1 -1
- package/ds-canonical/fork/codex/hooks.json +1 -1
- package/ds-canonical/fork/consumer/lock.json +17 -14
- package/ds-canonical/fork/consumer/managed-files/423729840acdd43c812f17c245b871716c6b60f76e4d3e94864d0e34a815bf8d.blob +9 -0
- package/ds-canonical/fork/consumer/managed-files/46f10bc55cdfdf834d0749eebff42ea0bb8576f361b36d2b6ba1851354c50150.blob +47 -9
- package/ds-canonical/fork/consumer/managed-files/5a0d134a2645114c8caac20458070b00a7f0fd55b3e8805b7325cb4cf52fa15e.blob +110 -0
- package/ds-canonical/fork/consumer/managed-files/5af0a46760af338d51fd0f7950675f8e97f7d2f09386b8818fcd7a58e6654e75.blob +790 -0
- package/ds-canonical/fork/consumer/managed-files/68bfcddff9c4a16388c248cd34fb66163e9e64b3d809a48ef3984f6ae4ff6d12.blob +211 -0
- package/ds-canonical/fork/consumer/managed-files/7eb6cc60abfdabaf6617c336058fad1454ccde08889295767bfd9b7fd153cc6e.blob +45 -6
- package/ds-canonical/fork/consumer/managed-files/92e4828cdcb96ff0d01daa16f6885f24045a8e72820665650f2a1890dc3a446c.blob +42 -2
- package/ds-canonical/fork/consumer/managed-files/ab072e7f2eca51f0e18a5e18c4b333260ee5d219a12cc6df5bb2d683f0b2734d.blob +27 -1
- package/ds-canonical/fork/consumer/managed-files/e71c65f45c6af7ce12b6b3c0d8028f185da5b719e2050c9b2a6b243d41ae4878.blob +62 -14
- package/ds-canonical/fork/governance.lock +41 -17
- package/ds-canonical/fork/hooks/check_consumer_app_invariants.sh +16 -4
- package/ds-canonical/fork/hooks/check_consumer_code_quality.sh +3 -1
- package/ds-canonical/fork/hooks/check_pattern_invariants.sh +13 -9
- package/ds-canonical/fork/launchers/settings-hooks.json +1 -1
- package/ds-canonical/fork/manifest.json +17 -11
- package/ds-canonical/fork/providers/claude/skills/performance-audit/SKILL.md +1 -1
- package/ds-canonical/fork/providers/codex/hook-config.json +1 -1
- package/ds-canonical/fork/providers/codex/skills/performance-audit/SKILL.md +1 -1
- package/ds-canonical/fork/skills/performance-audit/SKILL.md +1 -1
- package/ds-canonical/hooks/check_consumer_app_invariants.sh +16 -4
- package/ds-canonical/hooks/check_consumer_code_quality.sh +3 -1
- package/ds-canonical/hooks/check_datatable_invariants.sh +4 -1
- package/ds-canonical/hooks/check_pattern_invariants.sh +13 -9
- package/ds-canonical/hooks/check_plugin_fork_health.sh +8 -1
- package/ds-canonical/hooks/check_propose_discipline.sh +8 -1
- package/ds-canonical/hooks/check_storybook_addon_packaging.sh +8 -1
- package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +64 -2
- package/ds-canonical/hooks/lib/_overlay_handcraft.sh +5 -2
- package/ds-canonical/hooks/registrations.json +12 -7
- package/ds-canonical/hooks/session_start_governance_check.sh +16 -0
- package/ds-canonical/hooks/tests/test_check_consumer_app_invariants.sh +25 -0
- package/ds-canonical/hooks/tests/test_check_consumer_code_quality.sh +4 -0
- package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +14 -0
- package/ds-canonical/hooks/tests/test_check_datatable_invariants.sh +24 -0
- package/ds-canonical/hooks/tests/test_check_overlay_handcraft.sh +32 -0
- package/ds-canonical/hooks/tests/test_check_pattern_invariants.sh +59 -0
- package/ds-canonical/hooks/tests/test_check_plugin_fork_health.sh +32 -1
- package/ds-canonical/hooks/tests/test_check_propose_discipline.sh +21 -0
- package/ds-canonical/hooks/tests/test_check_storybook_addon_packaging.sh +21 -0
- package/ds-canonical/hooks/tests/test_check_substantive_edit_approval_preflight.sh +97 -0
- package/ds-canonical/hooks/tests/test_session_start_governance_check.sh +49 -0
- package/ds-canonical/references/README.md +12 -6
- package/ds-canonical/references/drag-canonical.md +3 -1
- package/ds-canonical/references/focus-canonical.md +26 -13
- package/ds-canonical/references/governance-audit-coverage.md +1 -1
- package/ds-canonical/references/hit-area-canonical.md +361 -0
- package/ds-canonical/references/item-anatomy-recipe.md +4 -2
- package/ds-canonical/references/keyboard-model-canonical.md +559 -0
- package/ds-canonical/rules/meta-patterns.md +4 -4
- package/ds-canonical/rules/self-verify.md +4 -4
- package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +1 -1
- package/ds-canonical/skills/design-system-audit/references/historical-bugs.md +96 -0
- package/ds-canonical/skills/knowledge-prune/SKILL.md +4 -1
- package/ds-canonical/skills/performance-audit/SKILL.md +1 -1
- package/ds-story-manifest.json +19 -7
- package/llms-full.txt +2 -2
- package/llms.txt +2 -2
- package/package.json +3 -1
- package/src/components/Accordion/accordion.anatomy.stories.tsx +3 -3
- package/src/components/Accordion/accordion.spec.md +1 -1
- package/src/components/Accordion/accordion.tsx +8 -3
- package/src/components/AccountMenu/account-menu.spec.md +2 -3
- package/src/components/AccountMenu/account-menu.tsx +0 -1
- package/src/components/AgentPanel/agent-decision-card.spec.md +88 -0
- package/src/components/AgentPanel/agent-panel-fab.spec.md +151 -0
- package/src/components/AgentPanel/agent-panel-fab.tsx +67 -24
- package/src/components/AgentPanel/agent-panel-logo.spec.md +87 -0
- package/src/components/AgentPanel/agent-panel-logo.tsx +3 -3
- package/src/components/AgentPanel/agent-panel.anatomy.stories.tsx +1 -1
- package/src/components/AgentPanel/agent-panel.principles.stories.tsx +1 -1
- package/src/components/AgentPanel/agent-panel.spec.md +30 -275
- package/src/components/AgentPanel/agent-panel.stories.tsx +113 -8
- package/src/components/AgentPanel/agent-panel.tsx +59 -22
- package/src/components/Alert/alert.anatomy.stories.tsx +1 -1
- package/src/components/AppShell/app-shell.tsx +1 -1
- package/src/components/AspectRatio/aspect-ratio.anatomy.stories.tsx +10 -15
- package/src/components/Avatar/avatar.anatomy.stories.tsx +23 -26
- package/src/components/Avatar/avatar.principles.stories.tsx +10 -10
- package/src/components/Avatar/avatar.spec.md +45 -6
- package/src/components/Avatar/avatar.stories.tsx +8 -8
- package/src/components/Avatar/avatar.tsx +227 -21
- package/src/components/Badge/badge.anatomy.stories.tsx +16 -21
- package/src/components/Breadcrumb/breadcrumb.anatomy.stories.tsx +3 -3
- package/src/components/Breadcrumb/breadcrumb.spec.md +3 -3
- package/src/components/Breadcrumb/breadcrumb.tsx +5 -3
- package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +1 -1
- package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +1 -1
- package/src/components/BulkActionBar/bulk-action-bar.spec.md +1 -0
- package/src/components/BulkActionBar/bulk-action-bar.stories.tsx +57 -2
- package/src/components/BulkActionBar/bulk-action-bar.tsx +32 -2
- package/src/components/Button/button.anatomy.stories.tsx +174 -33
- package/src/components/Button/button.spec.md +23 -8
- package/src/components/Button/button.tsx +116 -21
- package/src/components/Calendar/calendar.anatomy.stories.tsx +34 -11
- package/src/components/Calendar/calendar.principles.stories.tsx +44 -1
- package/src/components/Calendar/calendar.spec.md +97 -20
- package/src/components/Calendar/calendar.stories.tsx +101 -4
- package/src/components/Calendar/calendar.tsx +379 -74
- package/src/components/Carousel/carousel.anatomy.stories.tsx +16 -17
- package/src/components/Carousel/carousel.spec.md +48 -2
- package/src/components/Carousel/carousel.tsx +9 -3
- package/src/components/Chart/chart.anatomy.stories.tsx +7 -5
- package/src/components/Chart/chart.spec.md +32 -7
- package/src/components/Chart/chart.tsx +9 -0
- package/src/components/Checkbox/checkbox.anatomy.stories.tsx +21 -28
- package/src/components/Checkbox/checkbox.spec.md +4 -2
- package/src/components/Checkbox/checkbox.tsx +10 -2
- package/src/components/Chip/chip.anatomy.stories.tsx +1 -1
- package/src/components/Chip/chip.spec.md +2 -1
- package/src/components/Chip/chip.tsx +4 -2
- package/src/components/Combobox/combobox.anatomy.stories.tsx +30 -37
- package/src/components/Combobox/combobox.spec.md +11 -3
- package/src/components/Combobox/combobox.tsx +43 -12
- package/src/components/Command/command.spec.md +11 -0
- package/src/components/Command/command.tsx +143 -10
- package/src/components/DataTable/cell-registry.tsx +5 -1
- package/src/components/DataTable/data-table-column-visibility-panel.tsx +5 -6
- package/src/components/DataTable/data-table-interaction-layer.tsx +27 -14
- package/src/components/DataTable/data-table-known-defects.spec.md +72 -0
- package/src/components/DataTable/data-table-scroll-performance.spec.md +295 -0
- package/src/components/DataTable/data-table-sort-manager.tsx +5 -6
- package/src/components/DataTable/data-table.anatomy.stories.tsx +1 -1
- package/src/components/DataTable/data-table.css +64 -0
- package/src/components/DataTable/data-table.spec.md +70 -346
- package/src/components/DataTable/data-table.stories.tsx +56 -9
- package/src/components/DataTable/data-table.tsx +392 -103
- package/src/components/DateGrid/date-grid.anatomy.stories.tsx +2 -2
- package/src/components/DateGrid/date-grid.spec.md +163 -1
- package/src/components/DateGrid/date-grid.tsx +270 -7
- package/src/components/DatePicker/date-picker.anatomy.stories.tsx +18 -27
- package/src/components/DatePicker/date-picker.spec.md +6 -2
- package/src/components/DatePicker/date-picker.tsx +20 -1
- package/src/components/Dialog/dialog.spec.md +6 -2
- package/src/components/Dialog/dialog.stories.tsx +18 -9
- package/src/components/Dialog/dialog.tsx +83 -48
- package/src/components/DropdownMenu/dropdown-menu-keyboard.ts +255 -0
- package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +32 -33
- package/src/components/DropdownMenu/dropdown-menu.spec.md +18 -5
- package/src/components/DropdownMenu/dropdown-menu.stories.tsx +58 -1
- package/src/components/DropdownMenu/dropdown-menu.tsx +70 -33
- package/src/components/Empty/empty.spec.md +1 -2
- package/src/components/Field/field-context.ts +9 -4
- package/src/components/Field/field-controls.spec.md +5 -2
- package/src/components/Field/field-edit-keys.ts +12 -0
- package/src/components/Field/field-wrapper.tsx +76 -10
- package/src/components/Field/field.anatomy.stories.tsx +1 -1
- package/src/components/Field/field.spec.md +11 -6
- package/src/components/Field/field.stories.tsx +24 -1
- package/src/components/Field/field.tsx +10 -4
- package/src/components/FileItem/file-item.anatomy.stories.tsx +20 -10
- package/src/components/FileItem/file-item.principles.stories.tsx +14 -7
- package/src/components/FileItem/file-item.spec.md +50 -48
- package/src/components/FileItem/file-item.stories.tsx +41 -28
- package/src/components/FileItem/file-item.tsx +184 -50
- package/src/components/FileUpload/file-upload.anatomy.stories.tsx +11 -15
- package/src/components/FileUpload/file-upload.spec.md +27 -1
- package/src/components/FileUpload/file-upload.stories.tsx +33 -0
- package/src/components/FileUpload/file-upload.tsx +173 -43
- package/src/components/FileViewer/file-viewer.tsx +9 -4
- package/src/components/HoverCard/hover-card.stories.tsx +2 -2
- package/src/components/InlineEdit/inline-edit.spec.md +2 -0
- package/src/components/InlineEdit/inline-edit.tsx +8 -3
- package/src/components/Input/input.anatomy.stories.tsx +23 -31
- package/src/components/Input/input.spec.md +11 -0
- package/src/components/Input/input.tsx +6 -1
- package/src/components/LinkInput/link-input.anatomy.stories.tsx +18 -21
- package/src/components/LinkInput/link-input.principles.stories.tsx +4 -4
- package/src/components/LinkInput/link-input.spec.md +12 -1
- package/src/components/LinkInput/link-input.tsx +37 -4
- package/src/components/Menu/menu-item.anatomy.stories.tsx +50 -56
- package/src/components/Menu/menu-item.spec.md +1 -1
- package/src/components/Menu/menu-item.stories.tsx +48 -42
- package/src/components/Notice/notice.anatomy.stories.tsx +8 -16
- package/src/components/Notice/notice.spec.md +1 -1
- package/src/components/Notice/notice.tsx +1 -1
- package/src/components/NumberInput/number-input.anatomy.stories.tsx +21 -28
- package/src/components/NumberInput/number-input.spec.md +2 -0
- package/src/components/NumberInput/number-input.tsx +8 -1
- package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +28 -48
- package/src/components/OverflowIndicator/overflow-indicator.spec.md +13 -1
- package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +12 -9
- package/src/components/OverflowIndicator/overflow-indicator.tsx +32 -12
- package/src/components/Pagination/pagination.spec.md +1 -0
- package/src/components/Pagination/pagination.tsx +1 -1
- package/src/components/PeoplePicker/avatar-stack-overflow.ts +14 -13
- package/src/components/PeoplePicker/people-picker-helpers.ts +5 -2
- package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +1 -1
- package/src/components/PeoplePicker/people-picker.spec.md +4 -2
- package/src/components/PeoplePicker/people-picker.tsx +1 -4
- package/src/components/PeoplePicker/person-display.tsx +45 -28
- package/src/components/Popover/popover.anatomy.stories.tsx +1 -1
- package/src/components/Popover/popover.principles.stories.tsx +3 -12
- package/src/components/Popover/popover.spec.md +7 -3
- package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
- package/src/components/ProfileCard/profile-card.spec.md +1 -1
- package/src/components/ProfileCard/profile-card.tsx +0 -3
- package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +11 -21
- package/src/components/ProgressBar/progress-bar.spec.md +1 -1
- package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +17 -24
- package/src/components/RadioGroup/radio-group.spec.md +1 -1
- package/src/components/RadioGroup/radio-group.tsx +22 -4
- package/src/components/Rating/rating.anatomy.stories.tsx +131 -124
- package/src/components/Rating/rating.principles.stories.tsx +69 -43
- package/src/components/Rating/rating.spec.md +142 -63
- package/src/components/Rating/rating.stories.tsx +11 -15
- package/src/components/Rating/rating.tsx +131 -117
- package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.principles.stories.tsx +6 -3
- package/src/components/ScrollArea/scroll-area.spec.md +3 -1
- package/src/components/ScrollArea/scroll-area.stories.tsx +5 -2
- package/src/components/ScrollArea/scroll-area.tsx +1 -1
- package/src/components/SegmentedControl/segmented-control.anatomy.stories.tsx +1 -1
- package/src/components/SegmentedControl/segmented-control.spec.md +2 -2
- package/src/components/SegmentedControl/segmented-control.tsx +3 -2
- package/src/components/Select/select.anatomy.stories.tsx +26 -35
- package/src/components/Select/select.spec.md +4 -3
- package/src/components/Select/select.tsx +11 -4
- package/src/components/SelectMenu/select-menu-keyboard.ts +218 -0
- package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +6 -17
- package/src/components/SelectMenu/select-menu.spec.md +12 -4
- package/src/components/SelectMenu/select-menu.tsx +21 -56
- package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +9 -33
- package/src/components/SelectionControl/selection-item.spec.md +2 -2
- package/src/components/SelectionControl/selection-item.tsx +3 -1
- package/src/components/Separator/separator.stories.tsx +4 -4
- package/src/components/Sheet/sheet.principles.stories.tsx +31 -13
- package/src/components/Sidebar/sidebar.anatomy.stories.tsx +1 -1
- package/src/components/Sidebar/sidebar.spec.md +73 -6
- package/src/components/Sidebar/sidebar.stories.tsx +82 -4
- package/src/components/Sidebar/sidebar.tsx +281 -64
- package/src/components/Skeleton/skeleton.spec.md +1 -1
- package/src/components/Slider/slider.anatomy.stories.tsx +1 -1
- package/src/components/Slider/slider.spec.md +6 -5
- package/src/components/Slider/slider.tsx +13 -8
- package/src/components/Steps/steps-state-visuals.spec.md +157 -0
- package/src/components/Steps/steps.anatomy.stories.tsx +1 -0
- package/src/components/Steps/steps.spec.md +59 -142
- package/src/components/Steps/steps.stories.tsx +1 -1
- package/src/components/Steps/steps.tsx +125 -33
- package/src/components/Switch/switch.spec.md +3 -3
- package/src/components/Switch/switch.tsx +6 -1
- package/src/components/Tabs/tabs.spec.md +5 -1
- package/src/components/Tabs/tabs.tsx +42 -3
- package/src/components/Tag/tag.anatomy.stories.tsx +24 -28
- package/src/components/Tag/tag.spec.md +10 -11
- package/src/components/Tag/tag.tsx +3 -3
- package/src/components/Textarea/textarea.tsx +7 -1
- package/src/components/TimePicker/time-columns.tsx +9 -4
- package/src/components/TimePicker/time-picker.spec.md +4 -1
- package/src/components/TimePicker/time-picker.tsx +1 -1
- package/src/components/Tooltip/tooltip.anatomy.stories.tsx +6 -15
- package/src/components/Tooltip/tooltip.principles.stories.tsx +4 -16
- package/src/components/Tooltip/tooltip.spec.md +2 -1
- package/src/components/TreeView/tree-view.anatomy.stories.tsx +35 -13
- package/src/components/TreeView/tree-view.spec.md +67 -38
- package/src/components/TreeView/tree-view.stories.tsx +221 -12
- package/src/components/TreeView/tree-view.tsx +463 -356
- package/src/hooks/use-input-modality.ts +17 -46
- package/src/hooks/use-overflow-items.ts +4 -0
- package/src/hooks/use-truncated.ts +3 -2
- package/src/index.ts +17 -0
- package/src/lib/README.md +2 -0
- package/src/lib/drag-announcements.ts +78 -0
- package/src/lib/focus-after-trigger.ts +102 -0
- package/src/lib/roving-list-keyboard.ts +400 -0
- package/src/lib/utils.ts +5 -0
- package/src/patterns/action-bar/action-bar.spec.md +2 -0
- package/src/patterns/element-anatomy/inline-action.spec.md +16 -12
- package/src/patterns/element-anatomy/item-anatomy.spec.md +98 -6
- package/src/patterns/element-anatomy/item-anatomy.stories.tsx +75 -70
- package/src/patterns/element-anatomy/item-anatomy.tsx +17 -6
- package/src/patterns/element-anatomy/truncated-text.spec.md +1 -0
- package/src/patterns/header-canonical/header-canonical.spec.md +3 -2
- package/src/patterns/horizontal-overflow/horizontal-overflow.spec.md +2 -0
- package/src/patterns/horizontal-overflow/horizontal-overflow.stories.tsx +36 -0
- package/src/patterns/horizontal-overflow/horizontal-overflow.tsx +28 -0
- package/src/patterns/overlay-surface/overlay-chrome-sizing.spec.md +180 -0
- package/src/patterns/overlay-surface/overlay-surface.spec.md +8 -162
- package/src/patterns/overlay-surface/overlay-surface.tsx +5 -3
- package/src/patterns/resize-handle/resize-handle.spec.md +3 -3
- package/src/styles/base.css +1 -1
- package/src/tokens/categorical-color.ts +6 -1
- package/src/tokens/color/color.spec.md +91 -24
- package/src/tokens/color/color.stories.tsx +50 -2
- package/src/tokens/color/primitives.css +41 -38
- package/src/tokens/color/semantic.css +105 -36
- package/src/tokens/layoutSpace/layoutSpace.spec.md +2 -0
- package/src/tokens/motion/motion.spec.md +31 -8
- package/src/tokens/orphan-tokens.spec.md +1 -1
- package/src/tokens/uiSize/icon-size.ts +1 -1
- package/src/tokens/uiSize/stack-gap.ts +17 -0
- package/src/tokens/uiSize/uiSize.css +12 -0
- package/src/tokens/uiSize/uiSize.spec.md +17 -16
- package/src/tokens/uiSize/uiSize.stories.tsx +1 -1
- package/dist/node_modules/aria-hidden/dist/es2015/index.js +0 -147
- package/dist/node_modules/aria-hidden/dist/es2015/index.js.map +0 -1
|
@@ -46,7 +46,7 @@ export const Overview: Story = {
|
|
|
46
46
|
{[
|
|
47
47
|
['name', 'string', '必填', '檔名'],
|
|
48
48
|
['mode', "'compact' | 'rich'", "'compact'", 'compact=Paperclip 16px icon / rich=Avatar 48px 縮圖'],
|
|
49
|
-
['surface', "'form' | 'upload-manager'", "'form'", '所在容器 context:form=rich 為 border card / upload-manager=rich 無邊框無 bg(面板自身是容器,avatar 作 item 邊界)
|
|
49
|
+
['surface', "'form' | 'upload-manager'", "'form'", '所在容器 context:form=rich 為 border card / upload-manager=rich 無邊框無 bg(面板自身是容器,avatar 作 item 邊界);upload-manager 的列自帶左右 loose、上下 tight/2,鋪滿面板寬(面板 body 左右 0,2026-09-25 待辦總帳 B12)。詳 spec「邊框 / 背景」「Padding」表'],
|
|
50
50
|
['status', "'uploading' | 'completed' | 'error'", '—', '上傳狀態(不傳=已上傳靜態)'],
|
|
51
51
|
['progress', 'number', '0', '上傳進度 0-100(有 status 時顯示 bar;completed 強制 100%)'],
|
|
52
52
|
['description', 'ReactNode', '—', 'rich 任意場景 / compact 只有 error 才顯示。ReactNode — 可含 inline clickable link(如「View log」)'],
|
|
@@ -54,7 +54,7 @@ export const Overview: Story = {
|
|
|
54
54
|
['actions', 'ReactNode', '—', 'suffix actions(例:delete / cancel button)'],
|
|
55
55
|
['onDownload', '() => void', '—', "hover-swap:status='completed' 時,滑鼠移上整列,綠勾 ✓ 換成下載 ↓。兩種 mode 都用 Button xs(24)iconOnly,符合列內操作 ≤ 24 上限"],
|
|
56
56
|
['onRetry', '() => void', '—', "hover-swap:status='error' 時,滑鼠移上整列,紅叉 ✗ 換成重試 ⟲。幾何同上 — 兩種 mode 都用 Button xs(24)"],
|
|
57
|
-
['onClick', '() => void', '—', '傳入後整個 item
|
|
57
|
+
['onClick', '() => void', '—', '傳入後整個 item 變可點擊:cursor-pointer + 滑過底色(卡片疊一層 / 小膠囊換 secondary-hover / 透明列換 neutral-hover);不傳就沒有滑過底色 —— 點了會有反應的才加(2026-09-25 待辦總帳 B12)'],
|
|
58
58
|
].map(([p, t, d, desc]) => (
|
|
59
59
|
<tr key={p}><Td mono>{p}</Td><Td mono>{t}</Td><Td mono>{d}</Td><Td>{desc}</Td></tr>
|
|
60
60
|
))}
|
|
@@ -102,7 +102,7 @@ export const ColorMatrix: Story = {
|
|
|
102
102
|
<H3>Status × 元素 色彩矩陣</H3>
|
|
103
103
|
<Desc>
|
|
104
104
|
FileItem 本身無色彩變體——text 走 item-anatomy row primitive 共用 token
|
|
105
|
-
(`--foreground` / `--fg-secondary`);background 依 mode × surface 決定(rich + surface=form = `--surface` + border / rich + surface=upload-manager = 無邊框無 bg / compact 無 status = `--secondary` / compact 有 status = transparent)
|
|
105
|
+
(`--foreground` / `--fg-secondary`);background 依 mode × surface 決定(rich + surface=form = `--surface` + border / rich + surface=upload-manager = 無邊框無 bg / compact 無 status = `--secondary` / compact 有 status = transparent);滑過底色只在有 onClick 時出現(見下方 Container background table)。
|
|
106
106
|
Status 才驅動色彩:progress bar 色(inProgress / success / error)+ status icon 色(check / X)+ description 色(error 時升階)。
|
|
107
107
|
</Desc>
|
|
108
108
|
<div className="overflow-x-auto mb-4">
|
|
@@ -151,9 +151,11 @@ export const ColorMatrix: Story = {
|
|
|
151
151
|
</div>
|
|
152
152
|
|
|
153
153
|
<div>
|
|
154
|
-
<H3>Container background(per mode
|
|
154
|
+
<H3>Container background(per mode + 有 onClick 時的滑過)</H3>
|
|
155
155
|
<Desc>
|
|
156
|
-
FileItem
|
|
156
|
+
只有點了會有反應(傳了 onClick)的 FileItem 才有滑過底色,沒傳就沒有(2026-09-25 待辦總帳 B12,取代 2026-04-23「永不顯示 hover-bg」)。
|
|
157
|
+
換上什麼色依平常底色配對(color.spec.md「Hover 換色配對總則」):「底」疊一層、自己的填色換下一階、透明換 neutral-hover。
|
|
158
|
+
列內按鈕的滑過色疊在列的滑過色上;不加按住那一階(待辦總帳 N4 未決)。詳 spec「滑過」段。
|
|
157
159
|
</Desc>
|
|
158
160
|
<div className="overflow-x-auto">
|
|
159
161
|
<table className="text-caption border-collapse">
|
|
@@ -162,10 +164,13 @@ export const ColorMatrix: Story = {
|
|
|
162
164
|
</thead>
|
|
163
165
|
<tbody>
|
|
164
166
|
<tr><Td mono>rich + surface=form(all status)</Td><Td><span className="inline-flex items-center gap-1.5"><Swatch value="--surface" size="sm" /><span className="font-mono">--surface</span> + border</span></Td><Td>card(border + rounded + bg-surface)——Slack / Notion attachment 慣例</Td></tr>
|
|
165
|
-
<tr><Td mono>rich + surface=upload-manager(all status)</Td><Td><span className="font-mono">無邊框無 bg(
|
|
167
|
+
<tr><Td mono>rich + surface=upload-manager(all status)</Td><Td><span className="font-mono">無邊框無 bg、直角(列鋪滿面板寬)</span></Td><Td>面板自身是容器,avatar 作 item 邊界。詳 spec「邊框 / 背景」表</Td></tr>
|
|
166
168
|
<tr><Td mono>compact 無 status</Td><Td><span className="inline-flex items-center gap-1.5"><Swatch value="--secondary" size="sm" /><span className="font-mono">--secondary</span>(= neutral-3)</span></Td><Td>靜態 pill,對齊 Badge low / ProgressBar track SSOT</Td></tr>
|
|
167
169
|
<tr><Td mono>compact 有 status</Td><Td><span className="font-mono">transparent</span></Td><Td>底部 progress bar 作 permanent affordance(分隔線型)</Td></tr>
|
|
168
|
-
<tr><Td mono
|
|
170
|
+
<tr><Td mono>滑過:rich + surface=form + onClick</Td><Td><span className="inline-flex items-center gap-1.5"><Swatch value="--surface" size="sm" /><span className="font-mono">--surface</span> + 疊 <span className="font-mono">--neutral-hover</span>(bg-interaction-hover)</span></Td><Td>「底」不換色,疊一層</Td></tr>
|
|
171
|
+
<tr><Td mono>滑過:compact 無 status + onClick</Td><Td><span className="inline-flex items-center gap-1.5"><Swatch value="--secondary-hover" size="sm" /><span className="font-mono">--secondary-hover</span></span></Td><Td>自己的填色換成下一階,不借透明底的配對</Td></tr>
|
|
172
|
+
<tr><Td mono>滑過:透明的列(compact 有 status、upload-manager)+ onClick</Td><Td><span className="inline-flex items-center gap-1.5"><Swatch value="--neutral-hover" size="sm" /><span className="font-mono">--neutral-hover</span></span></Td><Td>透明底的配對;upload-manager 的列鋪滿面板寬</Td></tr>
|
|
173
|
+
<tr><Td mono>滑過:沒有 onClick</Td><Td><span className="font-mono">無變化</span></Td><Td>點了沒反應 → 不上色(B12);hover-swap 的鈕仍有自己的滑過色</Td></tr>
|
|
169
174
|
<tr><Td mono>error</Td><Td><span className="font-mono">容器不變</span></Td><Td>只升階 description / bar / icon,不染容器——避免整 row 轉紅蓋過其他 metadata</Td></tr>
|
|
170
175
|
</tbody>
|
|
171
176
|
</table>
|
|
@@ -288,6 +293,10 @@ export const StateBehavior: Story = {
|
|
|
288
293
|
<div className="flex flex-col gap-4 max-w-lg">
|
|
289
294
|
<div>
|
|
290
295
|
<H3>所有狀態對照</H3>
|
|
296
|
+
<Desc>
|
|
297
|
+
只有最後一張(static.pdf)傳了 onClick:游標移上去,卡片的白底不換、疊一層淡灰;游標再移到它的下載鈕上,鈕自己的淡灰再疊上去。
|
|
298
|
+
前三張沒有 onClick,點卡片本身沒反應,所以不上滑過底色(2026-09-25 待辦總帳 B12)。
|
|
299
|
+
</Desc>
|
|
291
300
|
<div className="flex flex-col gap-2">
|
|
292
301
|
<FileItem name="uploading.pdf" description="2.4 MB · 上傳中 60%" status="uploading" progress={60} mode="rich" actions={<Button variant="text" size="xs" iconOnly startIcon={X} aria-label="取消" />} />
|
|
293
302
|
<FileItem name="completed.pdf" description="2.4 MB · 已上傳" status="completed" mode="rich" actions={<Button variant="text" size="xs" iconOnly startIcon={Download} aria-label="下載" />} />
|
|
@@ -328,7 +337,7 @@ export const Accessibility = {
|
|
|
328
337
|
</li>
|
|
329
338
|
<li>
|
|
330
339
|
<strong className="text-foreground">狀態 icon 換成按鈕時的語音切換</strong>:滑鼠移上整列時,
|
|
331
|
-
被動的狀態 icon(綠勾 / 紅叉)
|
|
340
|
+
被動的狀態 icon(綠勾 / 紅叉)會立刻換成操作按鈕(下載 / 重試)。被動 icon 對螢幕報讀軟體隱藏,
|
|
332
341
|
換上的操作按鈕自帶語音標籤,使用者不會聽到視覺切換的雜訊。
|
|
333
342
|
</li>
|
|
334
343
|
<li>
|
|
@@ -337,8 +346,9 @@ export const Accessibility = {
|
|
|
337
346
|
只寫「下載」「刪除」缺檔名,螢幕報讀使用者無法分辨是哪一列。
|
|
338
347
|
</li>
|
|
339
348
|
<li>
|
|
340
|
-
<strong className="text-foreground"
|
|
341
|
-
|
|
349
|
+
<strong className="text-foreground">整列不是一顆按鈕,但鍵盤到得了整列</strong>:為避免與列內操作按鈕互相干擾(巢狀互動),
|
|
350
|
+
列本身不設成按鈕;傳 onClick 時列裡鋪一顆看不見的整列按鈕(Tab / Enter / Space,名稱「開啟 檔名」),焦點框畫在列上,列內操作按鈕各自一站(file-item.spec.md「Row primary action 鍵盤可達」);滑鼠照樣點整列觸發 onClick。
|
|
351
|
+
放進 FileUpload 內建清單(有 onRemove)時,整串清單改成一個 Tab 停靠點:列本身拿焦點、↑↓ 換列、→ 進移除鈕(file-upload.spec.md「A11y 預設 › 檔案清單鍵盤」,待辦總帳 B9)。
|
|
342
352
|
</li>
|
|
343
353
|
</ul>
|
|
344
354
|
</div>
|
|
@@ -5,6 +5,7 @@ import type { Meta, StoryObj } from '@storybook/react'
|
|
|
5
5
|
import { X, Download, RotateCw } from 'lucide-react'
|
|
6
6
|
import { FileItem } from './file-item'
|
|
7
7
|
import { Button } from '@/design-system/components/Button/button'
|
|
8
|
+
import { SurfaceBody } from '@/design-system/patterns/overlay-surface/overlay-surface'
|
|
8
9
|
|
|
9
10
|
const meta: Meta = {
|
|
10
11
|
title: 'Design System/Components/FileItem/設計原則',
|
|
@@ -147,13 +148,19 @@ export const StatusProgressRule: Story = {
|
|
|
147
148
|
title="completed — 成功 icon + 100% 完成條"
|
|
148
149
|
note="完成後保留 100% 綠色完成條(surface=upload-manager narrative —— 上傳狀態是重點,Dropbox / Google Drive 慣例),成功 icon 再加一層明確表達「完成」。檔案變可點擊(下載)"
|
|
149
150
|
>
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
151
|
+
{/* surface="upload-manager" 的列只在上傳管理器面板裡成立(列自帶左右 16px,面板 body 左右 0;B12)——
|
|
152
|
+
單獨放會多出內距、跟同頁其他範例對不齊,所以照 file-item.stories.tsx UploadManagerSurface 的面板組合包起來(M23(d)) */}
|
|
153
|
+
<div className="max-w-md flex flex-col rounded-lg border border-border bg-surface-raised shadow-[var(--elevation-200)]">
|
|
154
|
+
<SurfaceBody className="flex flex-col gap-0 !px-0 !py-[calc(var(--layout-space-tight)/2)]">
|
|
155
|
+
<FileItem
|
|
156
|
+
name="users-export.csv"
|
|
157
|
+
description="1.2 MB"
|
|
158
|
+
status="completed"
|
|
159
|
+
surface="upload-manager"
|
|
160
|
+
mode="rich"
|
|
161
|
+
/>
|
|
162
|
+
</SurfaceBody>
|
|
163
|
+
</div>
|
|
157
164
|
</Rule>
|
|
158
165
|
|
|
159
166
|
<Rule
|
|
@@ -113,7 +113,7 @@ compact 為預設——多數 upload 清單是「快速掃視多檔」場景,
|
|
|
113
113
|
item-anatomy「> 24 prefix + 有 desc」規則要求 prefix 對齊 content 塊**中心**(items-center)。
|
|
114
114
|
FileItem rich **刻意不遵守**這條,使用 `items-start`(top-align),原因:
|
|
115
115
|
|
|
116
|
-
> **當 FileItem rich 被放在「檔案上傳管理 box」等 tight-stack 情境**(`surface=upload-manager`
|
|
116
|
+
> **當 FileItem rich 被放在「檔案上傳管理 box」等 tight-stack 情境**(`surface=upload-manager` 無邊框、縮圖與縮圖相距 `--layout-space-tight` 12px 緊貼時,見「List wrapper canonical」),avatar 作為**每個 item 的視覺邊界引導**——若 avatar 中心對齊,連續 item 的 avatar 會失去「每筆檔案一個 thumbnail」的視覺節奏。top-align 讓 avatar 明確標示每個 item 的起點。
|
|
117
117
|
|
|
118
118
|
### Avatar 尺寸(固定 48px)
|
|
119
119
|
|
|
@@ -124,11 +124,11 @@ Avatar **固定 48px square**,不隨 content 高度變化。content(label + desc
|
|
|
124
124
|
| Mode × surface | 規則 |
|
|
125
125
|
|---|---|
|
|
126
126
|
| compact(`surface=form` 預設) | `px-3 py-2`(FileItem 無 size prop,不走 `ROW_PADDING_BY_SIZE` 公式;`gap-2` 對齊 item-anatomy row) |
|
|
127
|
-
| compact(`surface=upload-manager`) |
|
|
127
|
+
| compact(`surface=upload-manager`) | **左右 = 列自帶 `--layout-space-loose`**;上 `tight/2`、下 `tight/2 + 0.5rem`(0.5rem = 原 `py-2` 的下緣:文字↔bar 6 + bar 2),bar 離列底 `tight/2`。**2026-09-25 待辦總帳 B12 推翻 06-03 的「`px-0`、左右交給面板」**:可點的列滑過底色要鋪到面板左右邊,gutter 只能由列自己帶(見下方「item 與容器分工」) |
|
|
128
128
|
| rich(`surface=form` 預設) | `px-3 py-3` border card(py 固定,高度由 avatar 決定不走 row 公式)|
|
|
129
|
-
| rich(`surface=upload-manager`) | **左右 +
|
|
129
|
+
| rich(`surface=upload-manager`) | **左右 = 列自帶 `--layout-space-loose`、上下各 `tight/2`**(列高 = avatar 48 + tight)。06-03 的 `px-0 py-0` 同樣由 B12 推翻;上下補 `tight/2` 是為了滑過底色不貼縮圖(AI 推導) |
|
|
130
130
|
|
|
131
|
-
**item
|
|
131
|
+
**item 與容器分工**:判準是「誰負責左右 gutter」(`../../patterns/overlay-surface/overlay-surface.spec.md`「底部區域:按鈕列 vs 列式」)—— 列有整列滑過底色、要鋪到容器邊時,gutter 由列自己帶、容器給 0(`surface=upload-manager`);form 的列不鋪到容器邊,內距如上表。**`surface=upload-manager` 浮層面板(容器)的 padding / 列間 gap → 見「List wrapper canonical」的「upload-manager 浮層面板 composition」段**(SSOT 不在此重述,避免 drift)。
|
|
132
132
|
|
|
133
133
|
## 邊框 / 背景(AR15-21 canonical,2026-04-21 · 2026-04-22 擴充)
|
|
134
134
|
|
|
@@ -137,36 +137,24 @@ Avatar **固定 48px square**,不隨 content 高度變化。content(label + desc
|
|
|
137
137
|
| Mode × surface | 容器視覺 | Rationale |
|
|
138
138
|
|------|---------|-----------|
|
|
139
139
|
| **rich + `surface=form`**(預設,表單/訊息附件)| `border border-divider rounded-md bg-surface` | Rich = 「檔案 card」自立輪廓——Slack / Notion / Linear attachment 慣例;邊框讓每 row 視覺獨立 |
|
|
140
|
-
| **rich + `surface=upload-manager`**(Google Drive / Dropbox 背景上傳 box)| **無邊框 + 無 bg
|
|
140
|
+
| **rich + `surface=upload-manager`**(Google Drive / Dropbox 背景上傳 box)| **無邊框 + 無 bg + 直角**(2026-09-25 B12 起列鋪滿面板寬,圓角碰到面板直邊會缺角,AI 推導;縮圖切角是 Avatar `shape="square"` 自己的圓角,不靠列);avatar 作每筆 item 視覺邊界 | box 自身已是容器 → card border 多餘=雙層容器;avatar thumbnail 提供「每筆檔案」邊界節奏。2026-06-03 codify(原僅 L116 旁註「consumer 自己移除 border」,現 `surface` prop 機械化)|
|
|
141
141
|
| **compact + 有 status**(uploading / error / upload-manager completed,有 progress bar)| 無背景、無邊框,只靠 progress bar 提供 affordance | 「正在發生」/「剛發生」的動態 narrative,progress 本身就是視覺焦點 |
|
|
142
142
|
| **compact + 無 progress**(form attachment 靜態態) | `bg-secondary rounded-md`(= neutral-3 色) | 靜態清單(form / 訊息附件)背景色區隔出「檔案 row」邊界,跟純文字內容區分。**為何 `bg-secondary` 不 `bg-neutral-3`**:`--secondary` 是 semantic token 經 `@theme inline` 橋接成合法 Tailwind utility,`--color-neutral-3` 是 primitive token(僅 `:root` CSS var)不生成 utility,寫 `bg-neutral-3` 會 silent 失效。對齊 Badge low / ProgressBar track SSOT(同色) |
|
|
143
143
|
|
|
144
|
-
###
|
|
144
|
+
### 滑過(2026-09-25,待辦總帳 B12;取代 2026-04-23「FileItem 永不顯示 hover-bg」)
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
**只有點了會有反應(傳了 `onClick`)的 FileItem 才有滑過底色;沒傳就沒有。** user 原話:「要點了會有反應的才加,並確保加上去之後不會有任何視覺奇怪的地方,且按鈕的互動樣式也是自然疊加上去吧?用再亮一層這樣的措辭是否不夠精準?」換上什麼色依平常底色配對,規則住在 `../../tokens/color/color.spec.md`「Hover 換色配對總則」,本表只列 FileItem 的對應:
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
|
151
|
-
|
|
152
|
-
|
|
|
153
|
-
| rich(`surface=upload-manager`) | Avatar 48 thumbnail 作 item 邊界(無邊框/無 bg,面板自身是容器) | thumbnail 已提供「每筆檔案」節奏,hover-bg 多餘 |
|
|
154
|
-
| compact 無 status(靜態) | `bg-secondary rounded-md` 永遠 pill | pill bg + hover-bg neutral-hover 兩層相近灰,視覺雜 |
|
|
155
|
-
| compact 有 status(uploading / error / completed with bar) | 底部 2px progress bar(分隔線型 permanent affordance) | bar + hover-bg 同時並存,affordance 重複 |
|
|
156
|
-
|
|
157
|
-
三種型態**都已 anchored**,hover-bg 是多餘的視覺層。Cursor + click 本身已是足夠互動 affordance,世界級檔案 card / attachment 皆如此。
|
|
158
|
-
|
|
159
|
-
**世界級對照**(M8 / M12 benchmark):
|
|
160
|
-
|
|
161
|
-
| DS | File card / attachment hover 行為 | 跟本 canonical 對齊? |
|
|
162
|
-
|----|----------------------------------|----------------------|
|
|
163
|
-
| Slack file tile | border highlight,**無 bg 變化** | ✓ |
|
|
164
|
-
| Notion file callout | **無 bg 變化**,action icons fade in | ✓ |
|
|
165
|
-
| Figma file card | shadow lift,**無 bg 變化** | ✓ |
|
|
166
|
-
| Gmail attachment chip | **無 bg 變化**,download icon fade in | ✓ |
|
|
167
|
-
| Dropbox / Google Drive file **row**(flush transparent 無 permanent anchor) | **有** hover-bg | Opposite case(證明 canonical:anchored → 無、flush → 有) |
|
|
148
|
+
| 型態 | 平常底色 | 有 `onClick` 時滑過 |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| rich `surface=form`(卡片) | `bg-surface`(「底」) | 底不換,疊一層 `hover:bg-interaction-hover` |
|
|
151
|
+
| compact 無 status(小膠囊) | `bg-secondary` | 換成自己的下一階 `hover:bg-secondary-hover` |
|
|
152
|
+
| compact 有 status、`surface=upload-manager` 的兩 mode | 透明 | 換成 `hover:bg-neutral-hover`;upload-manager 的列鋪滿面板寬(見「Padding」) |
|
|
168
153
|
|
|
169
|
-
|
|
154
|
+
- **沒有 `onClick` 的不加**:上傳管理器裡只會在滑過時把 ✓ 換成下載鈕、點列本身沒反應的列也不加(待辦總帳 A3 → B12)—— 換位照舊,只有那顆鈕有自己的滑過色。
|
|
155
|
+
- **列內按鈕**(刪除、下載、重試)滑過時換上自己的 `--neutral-hover`(半透明),疊在列的滑過色上,不寫特例;規則在 `color.spec.md`「巢狀滑過」段。
|
|
156
|
+
- **不加按住那一階**:FileItem「按下深一階」是待辦總帳 N4 / F 模型 D3(2),尚未提問(AI 推導:不先替 user 決定)。
|
|
157
|
+
- **撤回的舊理由**(「三型態皆常駐錨點,加底色 = 雙重強調」):有框、有常駐底色的附件卡可點時照樣換底色 —— [shadcn Attachment](https://github.com/shadcn-ui/ui/blob/98a1fe67b439324ddc857f47fbdce056600a4329/apps/v4/registry/bases/radix/ui/attachment.tsx#L9)(`has-[>a,>button]:hover:bg-muted/50`,放了整卡觸發層才換);不可點的清單列不加滑過層 —— [Material Web list item](https://github.com/material-components/material-web/blob/cbd34a8921915af94d5ef65c2a69eece41d5b4f3/list/internal/listitem/list-item.ts#L161-L169)(`type="text"` 不渲染 ripple)。舊對照表(Slack / Notion / Figma / Gmail)沒有任何來源,一併撤回。不可點也上色的另一派([Ant Design Upload 文字清單](https://github.com/ant-design/ant-design/blob/4f9fe53933922d69f89bcd79de9de87b5124f522/components/upload/style/list.ts#L20-L32))與上面 user 原話不符,不採用。
|
|
170
158
|
|
|
171
159
|
**❌ 反例**:
|
|
172
160
|
- Rich `surface=form` 無邊框 → 失去 card 自立輪廓,與一般 list item 無法區分,跟 MenuItem 混淆(`surface=upload-manager` 例外:面板自身是容器 + avatar 作邊界,rich 刻意無邊框,見上表)
|
|
@@ -188,7 +176,7 @@ Avatar **固定 48px square**,不隨 content 高度變化。content(label + desc
|
|
|
188
176
|
| `status` | `uploading` / `error` / `completed`(**completed 持續保留不清除** = 此情境精髓) |
|
|
189
177
|
| Progress bar | 隨 status 顯示;completed = 100% 完成條(不隱藏) |
|
|
190
178
|
| Status icon | uploading 無 / completed 綠 ✓ / error 紅 ✗ |
|
|
191
|
-
| hover 行為 | **status slot hover-swap**:✓ → Download ↓(icon button)觸發 `onDownload` |
|
|
179
|
+
| hover 行為 | **status slot hover-swap**:✓ → Download ↓(icon button)觸發 `onDownload`;有 `onClick` 的列另有整列滑過底色(透明 → `neutral-hover`,鋪到面板左右邊),沒有 `onClick` 的列不上色(見「滑過」段) |
|
|
192
180
|
| Row-click | optional `onClick` 讓整 row 可點 → 預設 FileViewer 開啟;可與 hover-swap 並存 |
|
|
193
181
|
| Rich 容器 | **無邊框 + 無 bg**(面板自身是容器,avatar 作 item 邊界)。**2026-06-03 修正:原寫「border card 永遠」與 surface=upload-manager 無邊框矛盾** |
|
|
194
182
|
| Compact 容器 | 無 bg(progress bar 提供 affordance) |
|
|
@@ -214,7 +202,7 @@ Avatar **固定 48px square**,不隨 content 高度變化。content(label + desc
|
|
|
214
202
|
| `status` | 上傳中暫時 `uploading` / `error`;**靜態態 `undefined`**(清除) |
|
|
215
203
|
| Progress bar | 上傳中顯示;靜態態無 |
|
|
216
204
|
| Status icon | 同上 |
|
|
217
|
-
| hover 行為 | `cursor-pointer`(
|
|
205
|
+
| hover 行為 | 有 `onClick` 時 `cursor-pointer` + 滑過底色(卡片疊一層 / 小膠囊 `secondary-hover` / 有進度條的列 `neutral-hover`,見「滑過」段);沒有 `onClick` 不上色 |
|
|
218
206
|
| Row-click | **`onClick` 為主要 affordance** → **預設 FileViewer 開啟**(consumer 決定,也可下載) |
|
|
219
207
|
| Rich 容器 | `border card`(永遠) |
|
|
220
208
|
| Compact 容器 | 靜態態 `bg-secondary`(灰底區隔「這是檔案 row」;= `--color-neutral-3` semantic 橋接名,見邊框 / 背景章節);上傳中有 bar 時無灰底 |
|
|
@@ -247,14 +235,14 @@ Description 是 ReactNode,**不限純文字**。常見場景:
|
|
|
247
235
|
description={
|
|
248
236
|
<>
|
|
249
237
|
Network timeout.{' '}
|
|
250
|
-
<a href="#logs" className="underline
|
|
238
|
+
<a href="#logs" className="underline">View log</a>
|
|
251
239
|
</>
|
|
252
240
|
}
|
|
253
241
|
onRetry={noop}
|
|
254
242
|
/>
|
|
255
243
|
```
|
|
256
244
|
|
|
257
|
-
|
|
245
|
+
連結**沿用錯誤訊息的字色**、平常就有底線,**滑過不換色**(user 2026-09-26 選「乙 紅字 + 底線,滑過不變」)。底線是整句紅字裡唯一標出「這段可點」的訊號;滑過不必有變化(`../../ds-canonical/references/hit-area-canonical.md` 已同意的原則:點得到的地方不一定要有滑過變化)。**不可**寫 `hover:text-error-hover`:`--error-hover` 是紅色填色 `--error` 的配對,套在紅字上淺色會變淡(對比 7.2 → 3.3)、深色與 `--error-text` 同一格而完全不變。世界級同款:Polaris 放在 Banner 裡的 Link 自動 monochrome —— 「Makes the link color the same as the current text color and adds an underline」,平常與滑過都是 `color:inherit`([Link.out.css @13.9.5](https://cdn.jsdelivr.net/npm/@shopify/polaris@13.9.5/build/esnext/components/Link/Link.out.css))。顏色規則的例外寫在 `../../tokens/color/color.spec.md`「不該用 `--{hue}-text` 的場景」。
|
|
258
246
|
|
|
259
247
|
### 不可混用 invariants
|
|
260
248
|
|
|
@@ -273,33 +261,30 @@ upload-manager 的 completed(100% bar + ✓)屬「剛完成的 upload session」
|
|
|
273
261
|
|
|
274
262
|
## List wrapper canonical(多 item 間距)
|
|
275
263
|
|
|
276
|
-
**規則:gap 由「item 視覺密度」決定** —— rich form(有邊框 card)`gap-2`(8px,邊框不相黏);
|
|
264
|
+
**規則:gap 由「item 視覺密度」決定** —— rich form(有邊框 card)`gap-2`(8px,邊框不相黏);compact form `gap-1`(4px);`surface=upload-manager`(兩 mode)`gap-0`,列間距改由列自帶的上下 `tight/2` 相加(2026-09-25 B12)。
|
|
277
265
|
|
|
278
266
|
| Mode × surface | List wrapper gap | Rationale |
|
|
279
267
|
|------|----------------|-----------|
|
|
280
268
|
| **Rich + `surface=form`**(border card)| `gap-2`(8px) | card 邊框不相黏(standalone card invariant) |
|
|
281
|
-
|
|
|
282
|
-
| **Compact
|
|
269
|
+
| **`surface=upload-manager`**(rich + compact,無邊框浮層面板)| `gap-0` | 縮圖↔縮圖、bar↔下一列文字仍是 tight(12px):**2026-06-03 圖五 user 校準的 12px**(rich 初版誤設 4px 後校正)不變,只是從「列間 gap」改由兩列各自的上下 `tight/2` 相加得出 —— 可點的列滑過底色上下相接,不在兩列之間留一條沒上色的縫(B12,AI 推導) |
|
|
270
|
+
| **Compact + `surface=form`** | `gap-1`(4px) | 統一 — 有 status only / 無 status only / mixed 都 gap-1(2026-04-23 user 指示簡化:原條件式「全上傳中 → 0 gap」是 consumer 心智負擔 + state 轉換時 fragile,故捨棄 0-gap)|
|
|
283
271
|
|
|
284
272
|
**control→list gap(FileUpload 內建 list 消費)**:dropzone / button 控制項 ↔ 第一個 FileItem 的間距 = **同上 form gap 同值**(rich card 8px / compact bg-pill 4px),由 `file-upload.tsx` 依 `fileListMode` 套用。**FileUpload 內建 list 一律 `surface=form`**(dropzone 是表單上傳框,非獨立浮層 upload manager),故不套用下方 upload-manager 的 12px / 面板 padding。
|
|
285
273
|
|
|
286
274
|
### upload-manager 浮層面板 composition(Google Drive 右下角類獨立面板,**非** FileUpload dropzone)
|
|
287
275
|
|
|
288
|
-
`surface=upload-manager` 的 FileItem list 裝在獨立浮層面板(header + 列表)
|
|
276
|
+
`surface=upload-manager` 的 FileItem list 裝在獨立浮層面板(header + 列表);左右 gutter 由列自己帶、面板 body 給 0,上下由列與 body 各給一半(2026-09-25 B12):
|
|
289
277
|
|
|
290
278
|
- **它是 popover-class 浮層 surface,但不是 Radix `<Popover>`**(常駐面板:不靠 trigger 開、不 outside-click 關、用 chevron 收合非 X dismiss)→ **不包 `<Popover>`**,而是直接消費 overlay-surface 三件套 primitive。
|
|
291
279
|
- **殼 + header + body 全消費 overlay-surface SSOT(禁手刻)**:
|
|
292
280
|
- 殼:消費 Popover-class surface contract(border / radius / elevation / raised surface),但保持 persistent panel 語意;確切 utility 由對應 story / component source 擁有。
|
|
293
281
|
- header:`<SurfaceHeader className={cn("justify-between", COMPACT_HEADER_SLOT)}>` + `<PopoverTitle>`(輕量浮層 header SSOT,slot 走 `COMPACT_HEADER_SLOT`=21 衍生自 text-body title;padding = px-loose py-tight + border-b + unbounded-slot 負 my trick)。
|
|
294
|
-
- body:**`<SurfaceBody
|
|
282
|
+
- body:**`<SurfaceBody className="flex flex-col gap-0 !px-0 !py-[calc(var(--layout-space-tight)/2)]">`**(body SSOT 的 flex-1 scroll 鏈照用,padding 用 className override,見下)。左右 0 是「列式」(同 `../../patterns/overlay-surface/overlay-surface.spec.md`「誰負責左右 gutter」);它**仍不是**「List-as-region」—— 那個判定還要求上下也撤掉、清單外層自帶 `py-2`,owner 在同檔「List-as-region in overlay body」,本檔不複述。**scroll(consumer 注意)**:SurfaceBody 的 `flex-1 / overflow-y-auto` 只在 shell 有 `max-h` + `overflow-hidden` 時生效;常駐面板若檔案數可超 viewport,shell 須加 `max-h`(對齊 overlay-surface.spec.md「viewport-aware scroll」),demo 短內容不需。
|
|
295
283
|
- **禁手刻**:header / body 必直接消費 overlay-surface primitives;另建等價 wrapper 會形成第二份 spacing 與 surface authority。Hook `check_story_invariants.sh R9` 機械攔截。
|
|
296
284
|
|
|
297
|
-
-
|
|
298
|
-
-
|
|
299
|
-
|
|
300
|
-
- **compact**:item 上方自帶 `py-2`(8px)→ top 補 `4`(4+8=12);**進度條 `absolute bottom-0` 貼 item 底、下方無留白(inset 0)**→ bottom 留 SurfaceBody 預設 `py-tight`(12,12+0=12)。故 compact 用 **`SurfaceBody className="!pt-1"`** override 成上下不對稱(top=4 / bottom=12)。**為何用 `!`(important)**:twMerge 不 strip SurfaceBody 基底 `py-[tight]`,非 important 的 `pt-1` 跟基底 `py` 競爭 top 看 Tailwind stylesheet 生成順序(非決定性 → 可能 silent 變 12);`!pt-1` 強制決定性勝。對齊 List-as-region `!px-0` SurfaceBody override 慣例。
|
|
301
|
-
- **列間 gap**:套在 SurfaceBody className;值見上方「List wrapper canonical」gap 表(SSOT,不在此重述:rich upload-manager 12px / compact 4px)。
|
|
302
|
-
- **為何 compact container 上下不對稱**(2026-06-03 圖一研究校準):compact 進度條 absolute 貼底,item 的 py-2 那 8px 落在「文字↔bar」之間、bar 下方無 padding;若上下都用同值 → 下邊距(bar→邊緣)只剩容器值、比上邊距(含 item 8px)小。故用「12 − ink inset」逐側補。世界級對照:密集 list / dropdown 容器上下 padding 慣例 4–8px(Atlassian space.050–100 / 8px-base 共識);此處目標 12px 是「邊緣→ink」視覺值(含 item 自身留白),非容器裸值。
|
|
285
|
+
- **左右**:body `!px-0`,列自帶 `--layout-space-loose`(16px)→ item 內容左緣仍對齊 header 標題(x 與 06-03 相同),可點列的滑過底色則鋪到面板左右邊(`overlay-surface.spec.md` M11 state walk 三題:底色邊 = chrome 邊、內容對齊標題、內容離底色邊 ≥ loose)。**推翻 2026-06-03「左右交給面板」**:那時列沒有滑過底色;B12 後可點的列有了,底色若只到內容邊會貼著縮圖與文字(同檔「❌ 禁止:Item `px=0` 讓 content 直接觸 hover bg 邊」)。
|
|
286
|
+
- **上下:目標不變 = 邊緣到 item「ink」(可見內容)、ink 到 ink 都是 `var(--layout-space-tight)`(12px)**,通則仍是「容器該側 padding = 12 − item 在該側自己的留白(ink inset)」。B12 後兩 mode 的列上下各自帶 `tight/2`(compact 的 bar 離列底 `tight/2`,見「Padding」)→ body 上下都給 `tight/2`、列間 gap 0,兩 mode 同一個 body 寫法;06-03 compact 的 `!pt-1` 上下不對稱(因 bar 貼列底)隨之取消。**為何用 `!`(important)**:沿用 List-as-region `!px-0` 與 06-03 `!pt-1` 的寫法 —— 覆寫的勝負不依賴 twMerge 對 arbitrary 值的分組判斷,也不依賴 Tailwind stylesheet 的生成順序。
|
|
287
|
+
- **列間 gap**:套在 SurfaceBody className;值見上方「List wrapper canonical」gap 表(SSOT,不在此重述)。
|
|
303
288
|
- Demo:`file-item.stories.tsx` 的 `UploadManagerSurface`(rich)/ `UploadManagerCompactSurface`(compact)。
|
|
304
289
|
|
|
305
290
|
**Rich + Compact 不可混用**(見 Invariant 1 上方),故無「混用 gap」決策。
|
|
@@ -331,6 +316,7 @@ upload-manager 的 completed(100% bar + ✓)屬「剛完成的 upload session」
|
|
|
331
316
|
- FileItem 提供 `onClick` prop,consumer 傳入即進 clickable 模式:滑鼠保留整列 hit area;鍵盤透過同層透明 native button 以 Tab / Enter / Space 觸發,並以 `actionAriaLabel`(預設「開啟 {name}」)命名。row 本身不加互動 role,避免包住 trailing actions 形成 nested-interactive。
|
|
332
317
|
- 兩種 surface 都可以用 `onClick`(upload-manager 可跟 `onDownload` hover-swap 並存)
|
|
333
318
|
- consumer 決定具體行為(download / FileViewer),元件只提供 row 可點擊能力
|
|
319
|
+
- **為什麼是「列不互動 + 覆蓋控件」而不是「列自己是 button」**:因為本元件的列裡裝了必須被輔助科技讀到的結構——`ProgressBar`(自帶 `role="progressbar"`)、`Avatar`、以及 hover-swap 的 `<Button>`;依 `../../patterns/element-anatomy/item-anatomy.spec.md`「整列可點時,誰當那顆控件」表,這是**第二類**。Sidebar 的列只有文字與圖示,走**第一類**(列自己就是 `<button>`)。判準與規範逐字出處在該表,本檔不重述。
|
|
334
320
|
|
|
335
321
|
## ProgressBar
|
|
336
322
|
|
|
@@ -345,6 +331,22 @@ upload-manager 的 completed(100% bar + ✓)屬「剛完成的 upload session」
|
|
|
345
331
|
|
|
346
332
|
改動進度條視覺(高度 / 色 / 動畫) → 去 ProgressBar 改,**本元件無本地 bar 實作**。
|
|
347
333
|
|
|
334
|
+
### 焦點框 × 貼著列底的進度條(2026-09-26)
|
|
335
|
+
|
|
336
|
+
**適用範圍**:只有 `surface="form"` 的 compact 列、而且有 `status`(進度條貼列底)。這種列拿到鍵盤焦點時(本元件的整列焦點框,或 FileUpload 清單列的焦點框),往內 2px 的焦點框底邊與 2px 的進度條落在同一條線上。`upload-manager` 的進度條離列底 `tight/2`、rich 的進度條在內容區,都碰不到框,不適用本段。
|
|
337
|
+
|
|
338
|
+
**問題(實測)**:進度條本來就畫在框的**上面**,但它沒有把框蓋住 —— 軌道 `--secondary` 是半透明(淺色 6% 黑、深色 12% 白),框的藍從軌道透出來;填色 `--info` 又與框 `--ring` 同為 blue-6。聚焦時填色對軌道只剩 1.10:1(深色 1.17:1),平常是 4.55:1(深色 4.04:1);填色與框黏成一條,看不出進度從哪裡開始、到哪裡。
|
|
339
|
+
|
|
340
|
+
**規則**:進度條留在框的上面(user 2026-09-26 選的方向,逐字:「我喜歡墊在鍵盤焦點上的方向,依此方向仔細研究怎樣最好」),框在**進度條那一段挖空**,連同進度條兩端各多挖 2px 的縫(挖空與縫寬是 AI 研究後的建議,user 同日回「我覺得方向可以,確保整個設計符合我們一致的設計語言且不違背世界級的設計就照你建議」)。聚焦時底邊讀起來是「框|縫|進度條|縫|框」。
|
|
341
|
+
|
|
342
|
+
- **挖空,不是墊底色**:挖掉的地方露出的是真正在後面的東西(列自己的滑過色、卡片、面板、頁面),所以聚焦時進度條的每個像素與平常相同,任何底色上都對。墊一塊固定底色(例如 `--canvas`)只在那一種底上對:深色卡片上軌道會從平常的 `#383838` 變成 `#272727`;列正被滑過時也會少掉滑過色。改用「容器宣告自己底色」的變數也不行 —— 深色的 `--surface` 本身是白 8% 半透明,卡片放在浮層面板裡時真正的底是「面板 + 白 8%」,變數表達不了疊了幾層(實測軌道 `#383838` 對平常 `#4a4a4a`)。與 `../Avatar/avatar.spec.md`「頭像堆疊」選挖空、不畫外圈是同一個理由。
|
|
343
|
+
- **縫 = `--stack-gap`(2px)**:與全域外描邊離元件的間隙、頭像堆疊的縫、步驟條外圈的縫同一個 token —— DS 把「疊在一起的兩樣東西分開」的縫都是它(`tokens/uiSize/uiSize.css`;設計理由住 `../Avatar/avatar.spec.md`「頭像堆疊」段,2026-09-26 抽成 token 前是 6 處字面值)。縫讓同色的藍分開:進度的起點(填色左端)與框的底邊之間隔著一道底色,不再黏成一條;進度條右端與框之間同樣留縫,兩端對稱。
|
|
344
|
+
- **世界級對照**(一手原始碼,釘版本):同色的兩樣東西相鄰時,用一道底色的縫分開 —— Material 的線性進度條在填色與軌道之間留 4dp 的縫(MDC-Android 1.14.0 `app:indicatorTrackGapSize`:「size of the gap between the indicator and the track, 4dp by default」,[ProgressIndicator.md#L317-L325](https://github.com/material-components/material-components-android/blob/1.14.0/docs/components/ProgressIndicator.md#L317-L325)),而且那道縫是**不畫**,軌道從填色尾端加上縫之後才開始畫([DeterminateDrawable.java#L373-L391](https://github.com/material-components/material-components-android/blob/1.14.0/lib/java/com/google/android/material/progressindicator/DeterminateDrawable.java#L373-L391))—— 與本段的挖空同一種做法。IBM Carbon 的按鈕焦點框與主色填色同藍時,中間墊一道 `$background` 內線(`inset 0 0 0 $button-border-width $background`,[button/_mixins.scss#L133-L137 @v11.117.0](https://github.com/carbon-design-system/carbon/blob/v11.117.0/packages/styles/scss/components/button/_mixins.scss#L133-L137));那是固定底色 token,本段不採用固定底色的理由見上一條。
|
|
345
|
+
- **怎麼做到**:框改畫在與列同形同大的 `::before`「框圖層」上(`FILE_ITEM_RING_LAYER_CLASS`),遮罩只挖這一層(`fileItemRingCutoutStyle`,位置與進度條同源 —— 都讀 `compactBarInset`)。框若畫在列本身,列是進度條的祖先,遮罩會連進度條一起挖掉。線寬、顏色、往內 2px、圓角全部照舊是 `focus-ring-inset`,不是第四種幾何(`ds-canonical/references/focus-canonical.md`「框怎麼畫」框圖層列)。FileUpload 的清單列用同一個框圖層與同一個遮罩(`../FileUpload/file-upload.spec.md`「A11y 預設 › 檔案清單鍵盤」)。
|
|
346
|
+
- **代價(寫明)**:框的底邊在進度條那一段(含兩端縫)不畫,框在那一段由進度條本身接上;框的上、左、右三邊與底邊兩端(含圓角)完整。這是「進度條留在框上面」這個方向本身的代價,不是實作取捨。
|
|
347
|
+
- **平常的樣子一個像素都不動**:框圖層只在聚焦時有東西可畫,遮罩也只作用在這一層;進度條的位置、顏色、高度都不變(user 2026-09-26:「我他媽不想要動到這邊的一般視覺」)。
|
|
348
|
+
- 不走的其他改法(逐條對應的規則):聚焦時把填色換成別的顏色 → `focus-canonical.md`「一個項目只有一個指示器」且狀態色被改掉;框往內多退 → 同檔「只准三種幾何」禁手寫其他退距;聚焦時把進度條抬離框 → 不是 user 選的方向。
|
|
349
|
+
|
|
348
350
|
## Actions(suffix,row dedicated region canonical)
|
|
349
351
|
|
|
350
352
|
Consumer 自行組合。按 `patterns/element-anatomy/item-anatomy.spec.md`「Predicate」+「Row action 絕對值 cap」,**row dedicated action 絕對值 cap = ≤ 24px,不隨 row tier 放大**。依 row 高度分兩種實作:
|
|
@@ -354,7 +356,7 @@ Consumer 自行組合。按 `patterns/element-anatomy/item-anatomy.spec.md`「Pr
|
|
|
354
356
|
| Mode | 實作 | 尺寸 |
|
|
355
357
|
|------|------|------|
|
|
356
358
|
| `rich` | **Button iconOnly `size="xs"`**(24 固定,不隨 row 放大) | 24 |
|
|
357
|
-
| `compact` | **Button iconOnly `size="xs"`**(同 rich;靠 suffix wrapper `[&>[data-unbounded]]:my-[calc((1lh-var(--field-height-xs))/2)]` trick 把 24 footprint 收斂到 1lh,不撐高 row
|
|
359
|
+
| `compact` | **Button iconOnly `size="xs"`**(同 rich;靠 suffix wrapper `[&>[data-unbounded]]:my-[calc((1lh-var(--field-height-xs))/2)]` trick 把 24 footprint 收斂到 1lh,不撐高 row,視覺與命中區仍 24) | 24 |
|
|
358
360
|
|
|
359
361
|
```tsx
|
|
360
362
|
// Rich + Compact 統一 → Button xs iconOnly 固定 24(≤ 24 cap)
|
|
@@ -381,7 +383,7 @@ Consumer 自行組合。按 `patterns/element-anatomy/item-anatomy.spec.md`「Pr
|
|
|
381
383
|
|
|
382
384
|
**幾何一致性(2026-04-23 統一 canonical · row action ≤ 24 cap)**:status slot 容器大小 **= consumer 的 delete action 尺寸**,兩 mode 統一:
|
|
383
385
|
- `mode="rich"` → `var(--field-height-xs)`(24 固定,與 Button xs iconOnly 同)
|
|
384
|
-
- `mode="compact"` → `var(--field-height-xs)`(24,同 rich;compact 靠 status slot wrapper 的 `[&>[data-unbounded]]:my-[calc((1lh-var(--field-height-xs))/2)]` 把 24 footprint 收斂到 1lh,不撐高 row
|
|
386
|
+
- `mode="compact"` → `var(--field-height-xs)`(24,同 rich;compact 靠 status slot wrapper 的 `[&>[data-unbounded]]:my-[calc((1lh-var(--field-height-xs))/2)]` 把 24 footprint 收斂到 1lh,不撐高 row,視覺與命中區仍 24 —— 命中 ≡ 可視,owner = `ds-canonical/references/hit-area-canonical.md`;2026-09-24 把本檔兩處原文的「touch target」正名為命中區,本 DS 以滑鼠指標的精度為前提,尺寸不以觸控門檻推導)
|
|
385
387
|
|
|
386
388
|
Passive status icon 置中於 action-sized 容器,hover 時 active action 填滿同一容器。這讓 flex gap token 測量的是**兩個同尺寸 action slot 之間的真實 gap**,不被 hover bg overflow 吃掉——status slot 尺寸 = 同 size delete slot,gap token 才能如實呈現;歷史 bug 細節見 `.claude/skills/design-system-audit/references/historical-bugs.md`。
|
|
387
389
|
|
|
@@ -418,7 +420,7 @@ Passive status icon 置中於 action-sized 容器,hover 時 active action 填滿
|
|
|
418
420
|
- **ProgressBar 整合(進度 context 帶檔名)**:消費的 `<ProgressBar>` 自帶 `role="progressbar"` + `aria-valuenow` / `aria-valuemax`(Radix Progress primitive 提供),本元件再傳 `aria-label={檔名 上傳進度}` 作 context;keyboard 不需 focus progress bar(被動指示器,非互動元素)。
|
|
419
421
|
- **Action button labels**:Download / retry / remove 等 inline action 必傳 `aria-label`(中文 / consumer locale)— 「下載 report.pdf」/「重試上傳」/「移除附件」,單純「下載」/「刪除」缺檔名 context SR user 無法區分多 row。
|
|
420
422
|
- **Status icon hover-swap a11y**:hover-swap 不改變 SR 語意 — passive status icon `aria-hidden`,active action button 自帶 `aria-label`,避免 SR user 收到視覺 swap 噪音。
|
|
421
|
-
- **Row primary action 鍵盤可達且不 nested-interactive**:傳 `onClick` 時,row 保持非互動容器並保留整列 pointer hit area;另渲染與 trailing actions 同層的透明 full-row native button(pointer-events none,只承接 Tab / Enter / Space),focus-visible
|
|
423
|
+
- **Row primary action 鍵盤可達且不 nested-interactive**:傳 `onClick` 時,row 保持非互動容器並保留整列 pointer hit area;另渲染與 trailing actions 同層的透明 full-row native button(pointer-events none,只承接 Tab / Enter / Space),focus-visible 的框畫在 row 上(往內畫的內描邊;form 的 compact 列有 `status` 時畫在 row 的 `::before` 框圖層、進度條那段挖空,見「焦點框 × 貼著列底的進度條」)。**為什麼是內描邊,理由是結構性的**:拿到焦點的是那顆 `opacity-0` 的透明整列 button,它自己畫不出框,所以指示器改畫在 row 上 —— 這正是 `ds-canonical/references/focus-canonical.md` 認可的「指示器畫在別的元素上,而且必須指得出承擔者」形狀,承擔者就是 row。而全域外描邊只作用在「被聚焦的那個元素」身上,套不到非焦點的 row;能掛在 row 上的只有 `focus-ring-inset` 與填色專用的 `focus-ring-inset-emphasis`,row 不是主色填色 → `focus-ring-inset`。**不是**因為四周淨空(表單的列間 4–8px;上傳管理器的列 2026-09-25 起鋪滿面板、列間 0 —— 兩種幾何都一樣用內描邊),**也不是**因為捲動容器會裁切(捲動與否明文不進判準)。幾何 SSOT = `ds-canonical/references/focus-canonical.md`「框怎麼畫」。`actionAriaLabel` 預設「開啟 {name}」且可由 consumer 覆寫。Primary button 與下載/重試/移除皆為 sibling,禁止把 row 本身改成 `role="button"` 包住互動後代。
|
|
422
424
|
- **status / error 不額外加 row ARIA**:`status="uploading"` / `status="error"` 不在 row 上加 `aria-busy` / `role="status"` / `aria-live`;狀態由 progress bar 的 `role="progressbar"` 與 description 文字本身傳達。若 consumer 需要上傳完成 / 失敗的即時 announce,由外層上傳流程容器(FileUpload)統一管理 live region,避免每列各自宣告造成 SR 噪音。
|
|
423
425
|
|
|
424
426
|
---
|
|
@@ -449,7 +451,7 @@ Passive status icon 置中於 action-sized 容器,hover 時 active action 填滿
|
|
|
449
451
|
- ❌ **不混用 rich + compact 在同一 list**(詳「Invariant 1」)— 高度差破壞 row rhythm,prefix 視覺語言衝突。
|
|
450
452
|
- ❌ **不用 FileItem 做下載進度**(瀏覽器原生下載 UX 已足夠)— FileItem 為 upload narrative 設計,download progress 走自訂元件。
|
|
451
453
|
|
|
452
|
-
**常見誤解**:FileItem
|
|
454
|
+
**常見誤解**:FileItem 一律有 / 一律沒有滑過底色 → 都不對,有 `onClick`(點了會有反應)才有(見「滑過」段);status 會染整 row 底色 → 只升階 progress bar / status icon / description(見「Inspector 與矩陣的教學分工」);`surface` 影響進度條 → 進度條只看 `status`(見「可下載狀態 canonical」)。
|
|
453
455
|
|
|
454
456
|
---
|
|
455
457
|
|
|
@@ -467,7 +469,7 @@ Passive status icon 置中於 action-sized 容器,hover 時 active action 填滿
|
|
|
467
469
|
|
|
468
470
|
FileItem 決策維度是 `mode`(compact / rich)× `status`(uploading / completed / error / static)。anatomy 同時提供 `Inspector`(右側 Controls 即時切 `mode` / `status` / `progress` / `description` 試玩單值)與 `ColorMatrix` / `SizeMatrix` / `StateBehavior` 結構矩陣——兩者分工:Inspector 給「單一組合長怎樣」的即時試玩,矩陣給「跨 status 並排比對」的 side-by-side 決策。
|
|
469
471
|
|
|
470
|
-
ColorMatrix 已建:展示 status × 元素(filename / description / progress bar / status icon)色彩矩陣,明示 status 只驅動 **progress bar + status icon + description** 升階,**不染容器背景**(避免整 row 轉紅蓋過其他 metadata)
|
|
472
|
+
ColorMatrix 已建:展示 status × 元素(filename / description / progress bar / status icon)色彩矩陣,明示 status 只驅動 **progress bar + status icon + description** 升階,**不染容器背景**(避免整 row 轉紅蓋過其他 metadata)。容器本身沒有 selected / disabled state(interface 無 `disabled` prop);滑過底色只在有 `onClick` 時出現,由 `StateBehavior` 示範(詳「滑過」段)。
|
|
471
473
|
|
|
472
474
|
## 與 shadcn Attachment 的分界(2026-07-07 codify,目錄新增元件謂詞 anchor)
|
|
473
475
|
|
|
@@ -10,11 +10,12 @@ import { FileViewer, type FileInfo } from '@/design-system/components/FileViewer
|
|
|
10
10
|
import { SurfaceHeader, SurfaceBody, COMPACT_HEADER_SLOT } from '@/design-system/patterns/overlay-surface/overlay-surface'
|
|
11
11
|
import { PopoverTitle } from '@/design-system/components/Popover/popover'
|
|
12
12
|
|
|
13
|
-
// 錯誤 description 範例(含 clickable "View log"):consumer 自由 ReactNode
|
|
13
|
+
// 錯誤 description 範例(含 clickable "View log"):consumer 自由 ReactNode。
|
|
14
|
+
// 連結沿用錯誤訊息的紅 + 底線、滑過不換色(user 2026-09-26 選「乙 紅字 + 底線,滑過不變」;file-item.spec.md「Description ReactNode 可含 clickable 元素」)
|
|
14
15
|
const errorDescWithLog = (
|
|
15
16
|
<>
|
|
16
17
|
There’s something wrong.{' '}
|
|
17
|
-
<a href="#" className="underline
|
|
18
|
+
<a href="#" className="underline" onClick={(e) => e.preventDefault()}>
|
|
18
19
|
View log
|
|
19
20
|
</a>
|
|
20
21
|
</>
|
|
@@ -38,7 +39,8 @@ const keyboardOpen = fn()
|
|
|
38
39
|
// **Row action 絕對值 cap = ≤ 24px,不隨 row tier 放大**。rich + compact 統一用
|
|
39
40
|
// Button size="xs" iconOnly variant="text"(24 固定,≤ cap):
|
|
40
41
|
// compact row 透過 FileItem 內部 suffix wrapper `[&>[data-unbounded]]:my-[calc((1lh-var(--field-height-xs))/2)]`
|
|
41
|
-
// trick 讓 Button(24)layout footprint 收斂到 1lh(~18px)不撐高 row
|
|
42
|
+
// trick 讓 Button(24)layout footprint 收斂到 1lh(~18px)不撐高 row,視覺與命中區仍 24
|
|
43
|
+
// (命中 ≡ 可視,見 ds-canonical/references/hit-area-canonical.md;2026-09-24 把原文的「觸控範圍」正名為命中區)。
|
|
42
44
|
// Trash/Delete 非 dismiss 語意(dismiss 嚴格 = X close overlay),不套 `dismiss` prop——
|
|
43
45
|
// Button variant="text" 本來就 fg-muted,視覺已弱化(兩 mode 同)。
|
|
44
46
|
// 詳 item-anatomy.spec.md「Predicate」+「Row action 絕對值 cap」
|
|
@@ -70,6 +72,15 @@ export const Rich = {
|
|
|
70
72
|
action.focus()
|
|
71
73
|
await userEvent.keyboard('{Enter}')
|
|
72
74
|
await expect(keyboardOpen).toHaveBeenCalledTimes(1)
|
|
75
|
+
|
|
76
|
+
// 點同一列 trailing action 的刪除鈕:只該觸發刪除,**不得**連帶觸發整列 onClick。
|
|
77
|
+
// 2026-09-24 補。先前這支 play 只驗了鍵盤 Enter 那一條,於是「點刪除連帶把檔案開起來」
|
|
78
|
+
// 在我們自己的示範裡一路沒人看見 —— 那個洞不是沒人踩到,是**沒有人在量**。
|
|
79
|
+
// `action` 是覆蓋整列的隱形鈕,它的 parentElement 就是列本身;用它把搜尋範圍縮到同一列,
|
|
80
|
+
// 才不會抓到別列的刪除鈕(三列都有一顆)。
|
|
81
|
+
const row = action.parentElement as HTMLElement
|
|
82
|
+
await userEvent.click(within(row).getByRole('button', { name: '刪除' }))
|
|
83
|
+
await expect(keyboardOpen).toHaveBeenCalledTimes(1)
|
|
73
84
|
},
|
|
74
85
|
}
|
|
75
86
|
|
|
@@ -93,7 +104,8 @@ export const HoverSwap = {
|
|
|
93
104
|
<div className="flex flex-col max-w-md gap-4">
|
|
94
105
|
<div>
|
|
95
106
|
<div className="text-caption text-fg-muted mb-2">
|
|
96
|
-
↓ 游標移入任一 row:completed 綠 ✓ 變 Download ↓,error 紅 ✗ 變 Retry
|
|
107
|
+
↓ 游標移入任一 row:completed 綠 ✓ 變 Download ↓,error 紅 ✗ 變 Retry ⟲。
|
|
108
|
+
這幾列沒有傳 onClick(點列本身沒反應),所以整列不上滑過底色,只有換上的那顆鈕有自己的滑過色(待辦總帳 B12)
|
|
97
109
|
</div>
|
|
98
110
|
{/* Rich border card list:無外框 + `gap-2`(item-anatomy「連續 item 貼邊合法性」) */}
|
|
99
111
|
<div className="flex flex-col gap-2">
|
|
@@ -101,7 +113,7 @@ export const HoverSwap = {
|
|
|
101
113
|
description="Uploaded to URL" thumbnailSrc="https://i.pravatar.cc/80?u=xls"
|
|
102
114
|
onDownload={noop} actions={deleteBtn} />
|
|
103
115
|
<FileItem mode="rich" name="合約草案 v3.pdf" status="error"
|
|
104
|
-
description=
|
|
116
|
+
description={errorDescWithLog}
|
|
105
117
|
thumbnailSrc="https://i.pravatar.cc/80?u=pdf"
|
|
106
118
|
onRetry={noop} actions={deleteBtn} />
|
|
107
119
|
</div>
|
|
@@ -115,7 +127,7 @@ export const HoverSwap = {
|
|
|
115
127
|
<FileItem mode="compact" name="data-2024-q1.csv" status="completed"
|
|
116
128
|
onDownload={noop} actions={deleteBtnXs} />
|
|
117
129
|
<FileItem mode="compact" name="backup-failed.json" status="error"
|
|
118
|
-
description={<>Network timeout. <a href="#" className="underline
|
|
130
|
+
description={<>Network timeout. <a href="#" className="underline" onClick={(e) => e.preventDefault()}>View log</a></>}
|
|
119
131
|
onRetry={noop} actions={deleteBtnXs} />
|
|
120
132
|
</div>
|
|
121
133
|
</div>
|
|
@@ -225,7 +237,7 @@ export const CompactMixed = {
|
|
|
225
237
|
<FileItem mode="compact" name="圖片草稿.png" status="uploading" progress={40} actions={deleteBtnXs} />
|
|
226
238
|
<FileItem mode="compact" name="回覆範本.docx" onClick={noop} actions={deleteBtnXs} />
|
|
227
239
|
<FileItem mode="compact" name="backup-failed.json" status="error"
|
|
228
|
-
description={<>Network timeout. <a href="#" className="underline
|
|
240
|
+
description={<>Network timeout. <a href="#" className="underline" onClick={(e) => e.preventDefault()}>View log</a></>}
|
|
229
241
|
onRetry={noop} actions={deleteBtnXs} />
|
|
230
242
|
<FileItem mode="compact" name="附件封面.pdf" onClick={noop} actions={deleteBtnXs} />
|
|
231
243
|
</div>
|
|
@@ -242,14 +254,17 @@ export const CompactMixed = {
|
|
|
242
254
|
* 沒傳就退回 passive 是給既有 consumer 的相容行為,不是這裡要示範的東西。
|
|
243
255
|
* 寫成共用常數而不是「兩邊都記得傳」:**能力集合從此不可能只在其中一則出現**。
|
|
244
256
|
*/
|
|
245
|
-
|
|
257
|
+
// 完成的檔案可以點開(onClick),上傳中 / 失敗的不行 —— 同一個面板裡兩種列並存,示範「點了會有反應的列才有滑過底色」
|
|
258
|
+
// (2026-09-25 待辦總帳 B12:user「要點了會有反應的才加…」)。上傳中還不能開是真實情境,不是示範取巧。
|
|
259
|
+
const UPLOAD_MANAGER_COMPLETED = { status: 'completed', onDownload: noop, onClick: noop } as const
|
|
246
260
|
const UPLOAD_MANAGER_ERROR = { status: 'error', onRetry: noop } as const
|
|
247
261
|
|
|
248
|
-
// surface="upload-manager":Google Drive / Dropbox 上傳管理面板。面板組合 canonical(
|
|
249
|
-
// -
|
|
250
|
-
//
|
|
251
|
-
// -
|
|
252
|
-
//
|
|
262
|
+
// surface="upload-manager":Google Drive / Dropbox 上傳管理面板。面板組合 canonical(file-item.spec.md「upload-manager 浮層面板 composition」):
|
|
263
|
+
// - 2026-09-25 待辦總帳 B12 推翻 06-03 的「左右交給面板」:可點的列滑過底色要鋪到面板左右邊 → 列自帶左右 loose(16px,
|
|
264
|
+
// 文字 x 與 06-03 相同、仍對齊 header 標題),面板 body 左右 0(判準 = overlay-surface.spec.md「誰負責左右 gutter」)
|
|
265
|
+
// - 上下目標不變:「邊緣→item ink」「ink→ink」= tight(12px)。兩 mode 的列上下各自帶 tight/2 → body 上下也給 tight/2、列間 gap 0
|
|
266
|
+
// (06-03 compact 的 !pt-1 上下不對稱隨之取消);滑過底色上下各留 tight/2,不貼縮圖、不貼進度條
|
|
267
|
+
// - 只有完成的列有 onClick(UPLOAD_MANAGER_COMPLETED),上傳中 / 失敗的列點了沒反應 → 不上滑過底色
|
|
253
268
|
export const UploadManagerSurface = {
|
|
254
269
|
name: '上傳管理器 · 豐富(無邊框)',
|
|
255
270
|
render: () => (
|
|
@@ -260,12 +275,12 @@ export const UploadManagerSurface = {
|
|
|
260
275
|
<div className="flex-1 min-w-0"><PopoverTitle>正在上傳 3 個項目</PopoverTitle></div>
|
|
261
276
|
<Button iconOnly variant="text" size="sm" startIcon={ChevronDown} aria-label="收合" onClick={noop} />
|
|
262
277
|
</SurfaceHeader>
|
|
263
|
-
{/* body 消費 overlay-surface SurfaceBody(
|
|
264
|
-
|
|
265
|
-
<SurfaceBody className="flex flex-col gap-[var(--layout-space-tight)]">
|
|
278
|
+
{/* body 消費 overlay-surface SurfaceBody(非手刻;flex-1 scroll 鏈照用)。左右 0、上下 tight/2、列間 0:
|
|
279
|
+
gutter 與另一半上下由列自己帶(B12,見上方註解);縮圖↔縮圖、邊緣↔縮圖仍是 12px */}
|
|
280
|
+
<SurfaceBody className="flex flex-col gap-0 !px-0 !py-[calc(var(--layout-space-tight)/2)]">
|
|
266
281
|
<FileItem mode="rich" surface="upload-manager" name="Alan Profile.png" status="uploading" progress={40}
|
|
267
282
|
description="5.7 MB of 7.5 MB" thumbnailSrc="https://i.pravatar.cc/80?u=alan" actions={deleteBtn} />
|
|
268
|
-
<FileItem mode="rich" surface="upload-manager" name="Q1 營收報表.xlsx" {...UPLOAD_MANAGER_COMPLETED}
|
|
283
|
+
<FileItem mode="rich" surface="upload-manager" name="Q1 營收報表.xlsx" {...UPLOAD_MANAGER_COMPLETED} data-visual-hover-target
|
|
269
284
|
description="2.4 MB" thumbnailSrc="https://i.pravatar.cc/80?u=xls" actions={deleteBtn} />
|
|
270
285
|
<FileItem mode="rich" surface="upload-manager" name="合約草案 v3.pdf" {...UPLOAD_MANAGER_ERROR}
|
|
271
286
|
description={errorDescWithLog} thumbnailSrc="https://i.pravatar.cc/80?u=pdf" actions={deleteBtn} />
|
|
@@ -274,9 +289,9 @@ export const UploadManagerSurface = {
|
|
|
274
289
|
),
|
|
275
290
|
}
|
|
276
291
|
|
|
277
|
-
// surface="upload-manager" 的 compact list
|
|
278
|
-
//
|
|
279
|
-
// 對比 rich panel(
|
|
292
|
+
// surface="upload-manager" 的 compact list:body 寫法與 rich 相同(左右 0、上下 tight/2、列間 0),差別只在列本身 ——
|
|
293
|
+
// compact 列上 tight/2、下 tight/2 + 8(文字↔bar 6 + bar 2),bar 離列底 tight/2(file-item.tsx「compact 內距」段)。
|
|
294
|
+
// 對比 rich panel(列高 = 縮圖 48 + tight),demo 兩 mode 密度差異。
|
|
280
295
|
export const UploadManagerCompactSurface = {
|
|
281
296
|
name: '上傳管理器 · 緊湊(無邊框)',
|
|
282
297
|
render: () => (
|
|
@@ -286,16 +301,14 @@ export const UploadManagerCompactSurface = {
|
|
|
286
301
|
<div className="flex-1 min-w-0"><PopoverTitle>同步 3 個檔案</PopoverTitle></div>
|
|
287
302
|
<Button iconOnly variant="text" size="sm" startIcon={ChevronDown} aria-label="收合" onClick={noop} />
|
|
288
303
|
</SurfaceHeader>
|
|
289
|
-
{/* body
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
列間 gap-1(4px,密集列)。 */}
|
|
294
|
-
<SurfaceBody className="flex flex-col gap-1 !pt-1">
|
|
304
|
+
{/* body 同 rich 面板:左右 0、上下 tight/2、列間 0(B12)。用 `!` 沿用 List-as-region `!px-0` 慣例,
|
|
305
|
+
覆寫勝負不依賴 twMerge 分組與 stylesheet 生成順序。 */}
|
|
306
|
+
<SurfaceBody className="flex flex-col gap-0 !px-0 !py-[calc(var(--layout-space-tight)/2)]">
|
|
307
|
+
{/* 上傳中的列不能開(沒有 onClick)→ 不上滑過底色;與 rich 面板同一組能力(B12) */}
|
|
295
308
|
<FileItem mode="compact" surface="upload-manager" name="季度報告.docx" status="uploading" progress={60}
|
|
296
|
-
|
|
309
|
+
actions={deleteBtn} />
|
|
297
310
|
<FileItem mode="compact" surface="upload-manager" name="客戶名單.csv" {...UPLOAD_MANAGER_COMPLETED}
|
|
298
|
-
|
|
311
|
+
actions={deleteBtn} />
|
|
299
312
|
<FileItem mode="compact" surface="upload-manager" name="封面.png" {...UPLOAD_MANAGER_ERROR}
|
|
300
313
|
description={errorDescWithLog} actions={deleteBtn} />
|
|
301
314
|
</SurfaceBody>
|