@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
|
@@ -418,7 +418,6 @@ const PeoplePicker = React.forwardRef<HTMLDivElement, PeoplePickerProps>(functio
|
|
|
418
418
|
// 型別跟 PeoplePicker `HTMLAttributes<HTMLDivElement>` 不一致(`onCopy` / `onChange` 等)。
|
|
419
419
|
// Runtime spread 等效 — DOM 收到 attrs 不挑剔(非 allowlist attrs 落 CustomSelect rest,
|
|
420
420
|
// 續 spread 到 trigger div,runtime 行為不變)。
|
|
421
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
422
421
|
// any-allow: rest 含 `onChange: FormEventHandler` 跟 Select onChange signature 衝突 — DOM runtime spread 安全(per codex P2 forward)
|
|
423
422
|
{...(rest as any)}
|
|
424
423
|
/>
|
|
@@ -465,7 +464,6 @@ const PeoplePicker = React.forwardRef<HTMLDivElement, PeoplePickerProps>(functio
|
|
|
465
464
|
className={className}
|
|
466
465
|
aria-label={ariaLabel}
|
|
467
466
|
// codex P2 forward(see Select branch comment for type-cast rationale)
|
|
468
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
469
467
|
// any-allow: rest 含 `onChange: FormEventHandler` 跟 Combobox onChange signature 衝突 — DOM runtime spread 安全(per codex P2 forward)
|
|
470
468
|
{...(rest as any)}
|
|
471
469
|
// **Tag SSOT canonical**:用 `avatar` prop(不塞 children),Tag 內部統一
|
|
@@ -558,7 +556,7 @@ const PeoplePicker = React.forwardRef<HTMLDivElement, PeoplePickerProps>(functio
|
|
|
558
556
|
// 跟 avatar 同 slot(等寬同 step,non-overlapping 多 24px 區塊不再 saw)。對齊 user
|
|
559
557
|
// 「avatars 和 +N 都是同尺寸圓形,空間最多容固定數量圓形」物理模型 directive +
|
|
560
558
|
// MUI AvatarGroup / Primer AvatarStack 共識(`AvatarGroup.js` L54-59 same negative margin)。
|
|
561
|
-
overflowWrapperClassName="-ml-
|
|
559
|
+
overflowWrapperClassName="-ml-[var(--avatar-stack-overlap)] first:ml-0 relative inline-flex"
|
|
562
560
|
tagAreaGapPx={0}
|
|
563
561
|
tagAreaPaddingLeftPx={undefined}
|
|
564
562
|
// 2026-05-12 Round 7 fix(user 抓 image 2「+N tag 應該圓形不是矩形」+ 對齊 GitHub picker idiom):
|
|
@@ -595,7 +593,6 @@ const PeoplePicker = React.forwardRef<HTMLDivElement, PeoplePickerProps>(functio
|
|
|
595
593
|
</Tag>
|
|
596
594
|
)
|
|
597
595
|
}}
|
|
598
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
599
596
|
// any-allow: rest 含 `onChange: FormEventHandler` 跟 Combobox onChange signature 衝突 — DOM runtime spread 安全(per codex P2 forward)
|
|
600
597
|
{...(rest as any)}
|
|
601
598
|
tagRenderer={(item, onRemove) => {
|
|
@@ -5,7 +5,7 @@ import { cn } from '@/lib/utils'
|
|
|
5
5
|
import { useFieldEmptyDisplay } from '@/design-system/components/Field/field-context'
|
|
6
6
|
import { Tag } from '@/design-system/components/Tag/tag'
|
|
7
7
|
import { OverflowIndicator } from '@/design-system/components/OverflowIndicator/overflow-indicator'
|
|
8
|
-
import { Avatar } from '@/design-system/components/Avatar/avatar'
|
|
8
|
+
import { Avatar, AVATAR_STACK_CLASS, AVATAR_STACK_ITEM_CLASS, AVATAR_DISMISS_OVERLAY_PX, avatarStackItemStyle } from '@/design-system/components/Avatar/avatar'
|
|
9
9
|
import { ProfileCard, ProfileCardDefaultActions } from '@/design-system/components/ProfileCard/profile-card'
|
|
10
10
|
import { useTableIsScrolling } from '@/design-system/components/Field/field-context'
|
|
11
11
|
import { ItemPrefix } from '@/design-system/patterns/element-anatomy/item-anatomy'
|
|
@@ -87,9 +87,9 @@ function buildPersonProfileCard(person: PersonData): React.ReactNode {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
// ── Avatar Size ─────────────────────────────────────────────────────────────
|
|
90
|
-
// 與 Tag 高度對齊:sm=20px, md/lg=24px
|
|
91
|
-
|
|
92
|
-
const AVATAR_PX
|
|
90
|
+
// 唯一住所在 avatar-stack-overflow.ts `AVATAR_STACK_AVATAR_PX`(與 Tag 高度對齊:sm=20px, md/lg=24px);
|
|
91
|
+
// 這裡只是別名,不再另寫一份(2026-09-27,M17)。
|
|
92
|
+
const AVATAR_PX = AVATAR_STACK_AVATAR_PX
|
|
93
93
|
|
|
94
94
|
// ── PersonAvatar ────────────────────────────────────────────────────────────
|
|
95
95
|
// Consume DS `Avatar` primitive(2026-04-22 refactor,M1 SSOT consumption)+ 預設 ProfileCard
|
|
@@ -112,6 +112,8 @@ function PersonAvatar({
|
|
|
112
112
|
className = '',
|
|
113
113
|
style,
|
|
114
114
|
disabled = false,
|
|
115
|
+
stacked = false,
|
|
116
|
+
dismissCutout = false,
|
|
115
117
|
}: {
|
|
116
118
|
person: PersonData
|
|
117
119
|
size?: 'sm' | 'md' | 'lg'
|
|
@@ -123,6 +125,10 @@ function PersonAvatar({
|
|
|
123
125
|
* PeoplePicker disabled 分支無 fieldCtx,Avatar self-dim 搆不到,host 補)。
|
|
124
126
|
* 對齊 people-picker.spec.md「disabled:灰化整個 field,不可互動」。 */
|
|
125
127
|
disabled?: boolean
|
|
128
|
+
/** 頭像堆疊裡的一項 → 轉給 Avatar `stacked`(挖空;avatar.spec.md「頭像堆疊(疊在一起時)」) */
|
|
129
|
+
stacked?: boolean
|
|
130
|
+
/** 右上浮著移除 ×(AvatarDismissOverlay)→ 轉給 Avatar `dismissCutout`(× 顯示時把它底下連縫挖掉) */
|
|
131
|
+
dismissCutout?: boolean
|
|
126
132
|
}) {
|
|
127
133
|
const isTableScrolling = useTableIsScrolling()
|
|
128
134
|
const nameCard = React.useMemo(
|
|
@@ -137,6 +143,8 @@ function PersonAvatar({
|
|
|
137
143
|
className={cn(className, disabled && 'opacity-disabled')}
|
|
138
144
|
style={style}
|
|
139
145
|
hoverCard={nameCard}
|
|
146
|
+
stacked={stacked}
|
|
147
|
+
dismissCutout={dismissCutout}
|
|
140
148
|
/>
|
|
141
149
|
)
|
|
142
150
|
}
|
|
@@ -174,7 +182,10 @@ PersonDisplay.displayName = 'PersonDisplay'
|
|
|
174
182
|
|
|
175
183
|
// ── Multi Person Display ────────────────────────────────────────────────────
|
|
176
184
|
// 多人堆疊:avatar 重疊(-2px),不顯示人名。
|
|
177
|
-
// 第一個 avatar z-index 最高(在最上面)
|
|
185
|
+
// 第一個 avatar z-index 最高(在最上面),依此類推;+N 在最下面。
|
|
186
|
+
// 疊在一起的地方用挖空分開(不是外圈):幾何與層次都消費 Avatar 的頭像堆疊 SSOT
|
|
187
|
+
//(`AVATAR_STACK_CLASS` / `AVATAR_STACK_ITEM_CLASS` + `avatarStackItemStyle` + Avatar `stacked` + OverflowIndicator circle;avatar.spec.md「頭像堆疊(疊在一起時)」、
|
|
188
|
+
// people-picker.spec.md §D row 1)。
|
|
178
189
|
// 溢出時顯示 +N 指示器,hover 出 tooltip 列出溢出的人(avatar + 人名)。
|
|
179
190
|
|
|
180
191
|
function MultiPersonDisplay({
|
|
@@ -302,9 +313,11 @@ function MultiPersonDisplay({
|
|
|
302
313
|
|
|
303
314
|
// 2026-05-14 item-anatomy SSOT fix(per codex+Layer A 共識):outer items-start + avatar stack
|
|
304
315
|
// 鎖 first-line baseline(整 stack 是 prefix slot,h-[1lh] 對齊 first line)。
|
|
316
|
+
// 項目總數含 +N(它是最後一項、在最下層)。
|
|
317
|
+
const stackCount = visible.length + (overflow > 0 ? 1 : 0)
|
|
305
318
|
return (
|
|
306
319
|
<span ref={containerRef} className="inline-flex items-start min-w-0">
|
|
307
|
-
<ItemPrefix className="!justify-start"><span className=
|
|
320
|
+
<ItemPrefix className="!justify-start"><span className={cn('inline-flex items-center min-w-0', AVATAR_STACK_CLASS)}>
|
|
308
321
|
{visible.map((person, i) => {
|
|
309
322
|
// **2026-05-07 v15.11 Bug D 升級 SSOT**:visible avatar 也支援 inline dismiss
|
|
310
323
|
// (對齊 user directive「avatar = tag」)。Dismiss overlay 走 `AvatarDismissOverlay`
|
|
@@ -312,11 +325,11 @@ function MultiPersonDisplay({
|
|
|
312
325
|
// 都用同一視覺 — 紅圈 X 對齊 avatar 右上,hover/focus-visible 才顯。
|
|
313
326
|
const handleDismiss = onRemove ? () => onRemove(value![i]) : undefined
|
|
314
327
|
return (
|
|
315
|
-
<span key={person.name + i} className={
|
|
328
|
+
<span key={person.name + i} className={cn(AVATAR_STACK_ITEM_CLASS, 'group/avatar')} style={avatarStackItemStyle(i, stackCount)}>
|
|
316
329
|
<PersonAvatar
|
|
317
330
|
person={person}
|
|
318
331
|
size={size}
|
|
319
|
-
|
|
332
|
+
stacked
|
|
320
333
|
disabled={disabled}
|
|
321
334
|
/>
|
|
322
335
|
{handleDismiss && <AvatarDismissOverlay onRemove={handleDismiss} label={person.name} />}
|
|
@@ -324,10 +337,11 @@ function MultiPersonDisplay({
|
|
|
324
337
|
)
|
|
325
338
|
})}
|
|
326
339
|
{overflow > 0 && (
|
|
340
|
+
// +N 也是堆疊的一項:包一層 item,結構與頭像項目相同(item > 觸發點 > 圓),挖空才對得上。
|
|
341
|
+
<span className={AVATAR_STACK_ITEM_CLASS} style={avatarStackItemStyle(visible.length, stackCount)}>
|
|
327
342
|
<OverflowIndicator
|
|
328
343
|
count={overflow}
|
|
329
344
|
size={size}
|
|
330
|
-
className="ring-2 ring-[var(--surface)] -ml-0.5"
|
|
331
345
|
>
|
|
332
346
|
{hidden.map((person, i) => (
|
|
333
347
|
<Tag
|
|
@@ -353,6 +367,7 @@ function MultiPersonDisplay({
|
|
|
353
367
|
</Tag>
|
|
354
368
|
))}
|
|
355
369
|
</OverflowIndicator>
|
|
370
|
+
</span>
|
|
356
371
|
)}
|
|
357
372
|
</span></ItemPrefix>
|
|
358
373
|
</span>
|
|
@@ -365,8 +380,9 @@ MultiPersonDisplay.displayName = 'MultiPersonDisplay'
|
|
|
365
380
|
//
|
|
366
381
|
// **Visual canonical**(對齊 DS new token `--surface-strong`):
|
|
367
382
|
// - **12×12 圓**(固定,不隨 field size 變)
|
|
368
|
-
// - **bg `--surface-strong`**(neutral-6),hover → `--surface-strong-hover`
|
|
369
|
-
//
|
|
383
|
+
// - **bg `--surface-strong`**(neutral-6-opaque),hover → `--surface-strong-hover`(自己的配對 = 下一階,
|
|
384
|
+
// color.spec.md「Hover 換色配對總則」)。兩個主題都是 neutral-7-opaque:淺 #BFBFBF→#8C8C8C 變深、
|
|
385
|
+
// 深 #737373→#A6A6A6 變亮(semantic.css Surface 段)。2026-09-25 更正:原註解寫「light=neutral-5」與實值不符。
|
|
370
386
|
// - **X icon size=12 strokeWidth=3**(icon 跟底色一樣大,對齊 checkbox checkmark
|
|
371
387
|
// sm/md stroke 規格;2026-06-12 同步 checkbox 2026-05-18 簡化 3.5→3,SSOT →
|
|
372
388
|
// packages/design-system/ds-canonical/references/ui-dev-rules.md「小尺寸 icon stroke 補償」)
|
|
@@ -387,22 +403,19 @@ function AvatarDismissOverlay({ onRemove, label }: { onRemove: () => void; label
|
|
|
387
403
|
onClick={(e) => { e.stopPropagation(); onRemove() }}
|
|
388
404
|
data-collection-remove
|
|
389
405
|
aria-label={`移除 ${label}`}
|
|
406
|
+
// **Position(2026-05-07 v15.15 user-confirmed)**:asymmetric top -1px / right -4px — field padding-y
|
|
407
|
+
// (4px sm/md)緊 → top 只 -1px 安全;padding-x 12px 寬鬆 → right 凸 4px 達 badge canonical visual。
|
|
408
|
+
// 對齊 ClickUp 世界級 idiom(asymmetric offset by avatar/field size constraint)。
|
|
409
|
+
// 幾何只有一個住所 `AVATAR_DISMISS_OVERLAY_PX`(avatar.tsx):Avatar 用同一組數字在頭像上挖 × 底下的洞。
|
|
410
|
+
style={{ top: AVATAR_DISMISS_OVERLAY_PX.top, right: AVATAR_DISMISS_OVERLAY_PX.right, width: AVATAR_DISMISS_OVERLAY_PX.size, height: AVATAR_DISMISS_OVERLAY_PX.size }}
|
|
390
411
|
className={[
|
|
391
|
-
|
|
392
|
-
// (top -1px / right -4px)— field padding-y(4px sm/md)緊 → top 只 -1px 安全;
|
|
393
|
-
// padding-x 12px 寬鬆 → right 凸 4px 達 badge canonical visual。對齊 ClickUp
|
|
394
|
-
// 世界級 idiom(asymmetric offset by avatar/field size constraint)。
|
|
395
|
-
'absolute -top-px -right-1 z-10',
|
|
412
|
+
'absolute z-10',
|
|
396
413
|
'inline-flex items-center justify-center',
|
|
397
|
-
// **12×12
|
|
398
|
-
//
|
|
399
|
-
//
|
|
400
|
-
//
|
|
401
|
-
|
|
402
|
-
// 實測相反(當時焦點還走 ring 通道)。兩者最終都寫同一個 CSS `box-shadow` 屬性,而帶偽類的那條
|
|
403
|
-
// 帶偽類、特異性較高 → 聚焦當下白環**整層被藍環取代**。
|
|
404
|
-
// 這不是缺陷(聚焦時本來就該讓焦點指示器出線),但註解不能寫成相反的事實。
|
|
405
|
-
'w-3 h-3 rounded-full [box-shadow:0_0_0_2px_var(--surface)]',
|
|
414
|
+
// **12×12,與底下頭像之間的縫是從頭像上挖出來的**(Avatar `dismissCutout`,由 PersonAvatarTag 傳;2026-09-26 待辦總帳 N49):
|
|
415
|
+
// 此前是 `[box-shadow:0 0 0 2px var(--surface)]` 畫一圈當縫,深色 `--surface` 半透明 → 在頁面 / 卡片 / 滑過列上各是一圈錯色;
|
|
416
|
+
// 挖空露出真正的底,不需要知道底色(同 avatar.tsx 狀態圓點 / 計數徽章 / 頭像堆疊)。
|
|
417
|
+
// 聚焦時的藍框由全域 `:focus-visible` 外描邊畫,不再與白環搶同一個 box-shadow 屬性。
|
|
418
|
+
'rounded-full',
|
|
406
419
|
// bg-surface-strong = neutral-6-opaque / hover = neutral-7-opaque(both modes,
|
|
407
420
|
// step-7 dark 公式自動 lighter → engaged 跨 mode 對稱)
|
|
408
421
|
'bg-surface-strong text-on-emphasis hover:bg-surface-strong-hover',
|
|
@@ -416,7 +429,7 @@ function AvatarDismissOverlay({ onRemove, label }: { onRemove: () => void; label
|
|
|
416
429
|
// 的 X 都秀出來,這樣看起來超亂」)。觸控的多人移除改由 PeoplePicker 自動降階為既有
|
|
417
430
|
// pill 型態(Combobox tag SSOT,每顆 pill 自帶 X)承擔 — 見 people-picker.spec.md
|
|
418
431
|
// 「觸控裝置(native 分支)」。本 overlay 維持 hover / focus 才顯的桌機語意。
|
|
419
|
-
|
|
432
|
+
// 瞬間出現,不寫 transition-opacity(tokens/motion/motion.spec.md「hover 回饋不做過渡」;2026-09-26 延伸到滑過才出現的按鈕,待辦總帳 L9 / N4(3))。
|
|
420
433
|
].join(' ')}
|
|
421
434
|
>
|
|
422
435
|
<X size={12} strokeWidth={3} aria-hidden />
|
|
@@ -429,11 +442,15 @@ function AvatarDismissOverlay({ onRemove, label }: { onRemove: () => void; label
|
|
|
429
442
|
// (那是 pill mode),改 render 此元件 — Avatar overlap 視覺 + AvatarDismissOverlay。
|
|
430
443
|
// 對齊 user directive「avatar = tag 概念,差別只在視覺,SSOT 一致」(2026-05-07 v15.13)。
|
|
431
444
|
//
|
|
432
|
-
// **架構**(v15.13 重構):本元件**不自包** `group/avatar` / `-ml-
|
|
445
|
+
// **架構**(v15.13 重構):本元件**不自包** `group/avatar` / `-ml-[var(--avatar-stack-overlap)]` overlap wrapper,
|
|
433
446
|
// 因 Combobox `tagRenderer` 結果會被內部 `<div shrink-0>` 包成 measurement wrapper
|
|
434
447
|
// (useOverflowCount 必要)。把 overlap + group 拉到 Combobox 的 `tagWrapperClassName`
|
|
435
448
|
// 上,sibling-level overlap + group selector 才能正確 chain → AvatarDismissOverlay 的
|
|
436
449
|
// `group-hover/avatar:opacity-100` 才會通。
|
|
450
|
+
//
|
|
451
|
+
// **挖空(2026-09-26)**:本元件拿不到自己是第幾顆,但不需要 —— Avatar `stacked` 的挖空由結構決定
|
|
452
|
+
//(所在 wrapper 前面有看得見的堆疊項目才挖;Combobox 量寬時 `hidden` 的 wrapper 不算),
|
|
453
|
+
// 見 avatar.tsx「頭像堆疊」段、people-picker.spec.md §D row 1。
|
|
437
454
|
function PersonAvatarTag({
|
|
438
455
|
person, size = 'md', onRemove,
|
|
439
456
|
}: {
|
|
@@ -443,7 +460,7 @@ function PersonAvatarTag({
|
|
|
443
460
|
}) {
|
|
444
461
|
return (
|
|
445
462
|
<>
|
|
446
|
-
<PersonAvatar person={person} size={size}
|
|
463
|
+
<PersonAvatar person={person} size={size} stacked dismissCutout={!!onRemove} />
|
|
447
464
|
{onRemove && <AvatarDismissOverlay onRemove={onRemove} label={person.name} />}
|
|
448
465
|
</>
|
|
449
466
|
)
|
|
@@ -276,7 +276,7 @@ export const Accessibility = {
|
|
|
276
276
|
render: () => (
|
|
277
277
|
<div className="max-w-3xl text-body text-fg-secondary">
|
|
278
278
|
<h3 className="text-h5 text-foreground mb-2">無障礙設計</h3>
|
|
279
|
-
<p className="whitespace-pre-line">{"焦點 / 鍵盤 / ARIA 行為(部分為 DS 覆寫,部分為 Radix 內建):\n\n- 開啟焦點(DS 覆寫) :DS 以 onOpenAutoFocus 覆寫 Radix 預設,開啟時把焦點落在 body
|
|
279
|
+
<p className="whitespace-pre-line">{"焦點 / 鍵盤 / ARIA 行為(部分為 DS 覆寫,部分為 Radix 內建):\n\n- 開啟焦點(DS 覆寫) :DS 以 onOpenAutoFocus 覆寫 Radix 預設,開啟時把焦點落在 body 第一個表單控件(排除右上 close X;查無時退回 footer 按鈕、再無則 content 容器),避免 Radix 預設先 focus close X 觸發 tooltip leak\n- 關閉返回(Radix 內建) :關閉時 focus return to trigger\n- Esc 關閉(Radix 內建) :按 Esc 自動關閉並返回焦點\n- Focus trap(Radix 內建,僅 modal) : modal={true} 時焦點鎖在 content 內\n- Tab 留在面板裡(Radix 內建,modal / non-modal 皆同) :最後一格按 Tab 回第一格、第一格按 Shift+Tab 回最後一格,離開面板 = 按 Esc(W3C 對話框規則;2026-09-25 更正:原寫「non-modal 不鎖焦點」與實作不符)\n- 點外 / 焦點被移走即關(Radix 內建,non-modal) :non-modal 不 trap 的意思是滑鼠點別處、程式把焦點移走時可以離開,一離開就自動關閉(dismiss,不是「Tab 可以走出去」)\n- 例外:選單類浮層按 Tab = 收起並往下走(DropdownMenu、單選下拉,見各自 spec)\n- ARIA(Radix 內建) :trigger 自動 aria-expanded / aria-controls ,content role=\"dialog\" \n- Accessible name(DS 覆寫) :content role=\"dialog\" 預設無 accessible name, PopoverTitle 掛載時自動接上 content 的 aria-labelledby (consumer 自傳 aria-label / aria-labelledby 優先)\n\n保留 Radix data-state 屬性即可;無 PopoverTitle 的 naked popover 需 consumer 自傳 aria-label ,否則 role=\"dialog\" 無 accessible name。"}</p>
|
|
280
280
|
</div>
|
|
281
281
|
),
|
|
282
282
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import React from 'react'
|
|
4
4
|
import LinkTo from '@storybook/addon-links/react'
|
|
5
5
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
6
|
-
import { Filter, Copy, Edit, Trash2
|
|
6
|
+
import { Filter, Copy, Edit, Trash2 } from 'lucide-react'
|
|
7
7
|
import {
|
|
8
8
|
Popover,
|
|
9
9
|
PopoverTrigger,
|
|
@@ -28,11 +28,7 @@ import {
|
|
|
28
28
|
DropdownMenuContent,
|
|
29
29
|
DropdownMenuItem,
|
|
30
30
|
} from '@/design-system/components/DropdownMenu/dropdown-menu'
|
|
31
|
-
import {
|
|
32
|
-
Tooltip,
|
|
33
|
-
TooltipTrigger,
|
|
34
|
-
TooltipContent,
|
|
35
|
-
} from '@/design-system/components/Tooltip/tooltip'
|
|
31
|
+
import { FieldLabel } from '@/design-system/components/Field/field'
|
|
36
32
|
import { Button } from '@/design-system/components/Button/button'
|
|
37
33
|
import { Checkbox } from '@/design-system/components/Checkbox/checkbox'
|
|
38
34
|
import { CheckboxGroup } from '@/design-system/components/Checkbox/checkbox-group'
|
|
@@ -201,12 +197,7 @@ export const UsageGuidance: Story = {
|
|
|
201
197
|
title="Tooltip — hover 觸發,純文字輔助說明"
|
|
202
198
|
note="解釋 icon 按鈕含義、欄位說明、快捷鍵提示。使用者 hover 看到、移開就消失,內容不可互動。典型案例:Figma toolbar icon tooltip、Slack 按鈕功能提示、Gmail 的快捷鍵說明"
|
|
203
199
|
>
|
|
204
|
-
<Tooltip
|
|
205
|
-
<TooltipTrigger asChild>
|
|
206
|
-
<Button variant="text" size="sm" iconOnly startIcon={Info} aria-label="說明" />
|
|
207
|
-
</TooltipTrigger>
|
|
208
|
-
<TooltipContent>此欄位只接受英文與數字</TooltipContent>
|
|
209
|
-
</Tooltip>
|
|
200
|
+
<FieldLabel info="此欄位只接受英文與數字">專案代碼</FieldLabel>{/* 只給資訊的 ⓘ 用 FieldLabel info(內部就是 Tooltip;平常淡、滑過深一階、一般箭頭),不用 Button:Button 會帶按鈕底色與手形,iconOnly 還會自己再掛一層提示 → 同時浮出兩個 */}
|
|
210
201
|
<Label>↑ 純文字輔助 → Tooltip(hover 觸發,移開消失)</Label>
|
|
211
202
|
</Rule>
|
|
212
203
|
|
|
@@ -56,7 +56,7 @@ Popover 是**點擊觸發的浮層容器**——提供定位、動畫、焦點
|
|
|
56
56
|
- **含標題**:組合 `<PopoverHeader>` + `<PopoverBody>`(下分隔線由 Header 提供)
|
|
57
57
|
- **含操作按鈕列**:再加 `<PopoverFooter>`(上分隔線由 Footer 提供)
|
|
58
58
|
|
|
59
|
-
**Dismiss 預設**(與 Dialog 一致):Popover **預設 dismissible** —— 點擊 trigger 外 / 按 Esc /
|
|
59
|
+
**Dismiss 預設**(與 Dialog 一致):Popover **預設 dismissible** —— 點擊 trigger 外 / 按 Esc / 焦點被移到 content 樹之外(滑鼠點到別處、程式把焦點移走),三者任一皆關閉。由 Radix 內建 `onPointerDownOutside` / `onFocusOutside` / `onEscapeKeyDown` 行為提供,consumer 無需額外 wire。**鍵盤 Tab 不是離開的路**:面板裡按 Tab / Shift+Tab 在面板內繞圈、走不到 content 樹之外(見「A11y 預設」;2026-09-25 更正,原句「focus 離開 content 樹」被讀成 Tab 可以走出去,與實測不符)。如需強制 modal(必須按 Close 才能關),傳 `modal={true}` 並在 Header 放 Close 按鈕;但這是例外,不是預設——Popover 的本質是「使用者可以忽略、繼續主流程」。
|
|
60
60
|
|
|
61
61
|
> **跨家族 SSOT pointer**:PopoverHeader 屬 **Padding-based overlay header 家族**;border / padding / dismiss size / withTabs(tabs 進 PopoverHeader 時 border auto-suppress + tabs size sm)的跨家族視覺契約 SSOT 詳 `patterns/header-canonical/header-canonical.spec.md`。本節僅 codify Popover 特有 close X v5 unbounded canonical。
|
|
62
62
|
|
|
@@ -169,8 +169,10 @@ canonical 判斷:「使用者 click 單項是否立即改變系統狀態?」是
|
|
|
169
169
|
- **開啟焦點(DS 覆寫)**:DS 以 `onOpenAutoFocus` 覆寫底層 primitive 的預設 autofocus(見 `popover.tsx` `handlePopoverOpenAutoFocus`),開啟時把焦點落在 body 第一個**表單控件**(`input` / `textarea` / `select` / `button`,排除右上 close X);查無表單控件時退回 footer 按鈕,再無則落在 content 容器(`role="dialog"`)本身。原預設會先 focus 右上 close X,DS 覆寫以避免觸發 tooltip leak,selector 與 `dialog.tsx handleOpenAutoFocus` 共用同一套 DS canonical。**注意**:(1) 若移除此 default handler,行為會回退成底層預設 focus close X;(2) selector 只認表單控件——body 若只含 `a[href]` / `[tabindex]` / `contenteditable` 類可聚焦內容,焦點會落在 content 容器而非該內容,consumer 需自傳 `onOpenAutoFocus` 指定要聚焦的元素。
|
|
170
170
|
- **關閉返回(Radix 內建)**:關閉時 focus return to trigger
|
|
171
171
|
- **Esc 關閉(Radix 內建)**:按 Esc 自動關閉並返回焦點
|
|
172
|
-
- **
|
|
173
|
-
-
|
|
172
|
+
- **Tab 留在面板裡(Radix 內建,modal / non-modal 皆同)**:焦點在面板裡的可 Tab 元素上時,在最後一格按 Tab 回到第一格、在第一格按 Shift+Tab 回到最後一格,鍵盤走不出面板(面板裡沒有可 Tab 的元素、焦點落在 content 容器本身時,Tab 原地不動);要離開面板 = 按 Esc(焦點回 trigger)。機制:Radix Popover 內容的 FocusScope `loop: true`(`@radix-ui/react-popover` 1.1.15 `dist/index.mjs:225-226`;non-modal 時 `trapped: false`,見下一條)。這就是 W3C 對話框的規則 ——「Like non-modal dialogs, modal dialogs contain their tab sequence. That is, Tab and Shift + Tab do not move focus outside the dialog.」(<https://github.com/w3c/aria-practices/blob/3f094fde1c81b25dfa69162563bf28d093f854d4/content/patterns/dialog-modal/dialog-modal-pattern.html#L29-L30>),而 Popover content 本身就是 `role="dialog"`。2026-09-25 更正:本條原寫「預設 non-modal **不鎖焦點**」,R15 實測 Tab 出不去,與實作不符(待辦總帳 B11)
|
|
173
|
+
- **Focus trap(Radix 內建,僅 modal)**:`modal={true}` 時連滑鼠點外面 / 程式移焦都出不去(`trapped: true`),焦點鎖在 content 內
|
|
174
|
+
- **點外 / 焦點被移走即關(Radix 內建,non-modal)**:non-modal 不 trap 的意思是**滑鼠點到別處、程式把焦點移走**時焦點可以離開,一離開就由 DismissableLayer 觸發 dismiss 自動關閉——這是 dismiss-on-focus-out 機制,**不是**「Tab 可以走出去」
|
|
175
|
+
- **例外:選單類浮層按 Tab = 收起並往下走**:DropdownMenu 選單與單選下拉(Select / PeoplePicker single)不是對話框,開著按 Tab 收起、焦點從觸發鈕往下(Shift+Tab 往上)走;規則在各自 spec(`../SelectMenu/select-menu.spec.md`「A11y 預設」、`../DropdownMenu/dropdown-menu.spec.md`),來源 = 待辦總帳 B11。多選下拉(有全選鈕)面板裡有按鈕 / 搜尋框,是對話框型,照上面「Tab 留在面板裡」
|
|
174
176
|
- **ARIA(Radix 內建)**:trigger 自動 `aria-expanded` / `aria-controls`,content `role="dialog"`
|
|
175
177
|
- **Accessible name(DS 覆寫)**:Radix Popover 沒有 Radix Dialog Title 式的自動接線,content `role="dialog"` 預設**無 accessible name**。DS 以 context 補同機制(見 `popover.tsx` `PopoverTitleContext`):`<PopoverTitle>` 掛載時自動把 id 接上 content 的 `aria-labelledby`;consumer 自傳 `aria-label` / `aria-labelledby` 一律優先
|
|
176
178
|
|
|
@@ -201,10 +203,12 @@ canonical 判斷:「使用者 click 單項是否立即改變系統狀態?」是
|
|
|
201
203
|
|
|
202
204
|
> 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
|
|
203
205
|
|
|
206
|
+
- `agent-panel.spec.md`
|
|
204
207
|
- `coachmark.spec.md`
|
|
205
208
|
- `dialog.spec.md`
|
|
206
209
|
- `dropdown-menu.spec.md`
|
|
207
210
|
- `hover-card.spec.md`
|
|
211
|
+
- `overlay-chrome-sizing.spec.md`
|
|
208
212
|
- `overlay-surface.spec.md`
|
|
209
213
|
- `select-menu.spec.md`
|
|
210
214
|
- `tooltip.spec.md`
|
|
@@ -360,7 +360,7 @@ export const HoverCardIntegration: Story = {
|
|
|
360
360
|
].map(p => (
|
|
361
361
|
<HoverCard key={p.name}>
|
|
362
362
|
<HoverCardTrigger asChild>
|
|
363
|
-
<span className="inline-block cursor-
|
|
363
|
+
<span className="inline-block cursor-default">{/* 滑過只浮出名片、點了不做事 → 一般箭頭(hit-area-canonical.md 三-1;同 hover-card.stories 頭像觸發) */}
|
|
364
364
|
<Avatar src={p.avatarUrl} alt={p.name} size={40} status={p.status} />
|
|
365
365
|
</span>
|
|
366
366
|
</HoverCardTrigger>
|
|
@@ -108,7 +108,7 @@ ProfileCard 的 default actions **是 `Chat + Audio call`**(chat app 標配,cano
|
|
|
108
108
|
|
|
109
109
|
## Status 區
|
|
110
110
|
|
|
111
|
-
非互動狀態標籤以 `bg-muted` 承載(不用 `bg-secondary`——Muted
|
|
111
|
+
非互動狀態標籤以 `bg-muted` 承載(不用 `bg-secondary`——Muted 視覺重量更低;同用 `bg-muted` 的是 Skeleton,登記在 `../../tokens/color/color.spec.md`「Static Subtle Background」段的 `bg-muted` 列)。2026-09-25 更正引用(待辦總帳 C2 / R12 W2):原寫「對齊 Badge / Skeleton family」,但 Badge 的 `low` 用的是 `bg-secondary`(`../Badge/badge.tsx` `badgeVariants` 的 `low`),不是本條的先例。狀態點顏色走 `--status-*` presence token(2026-04-20):`online` / `away` / `busy` / `offline`——跟 Avatar status 同源、獨立於 success/warning/error(presence 不是 validation state)。訊息必須包在 `<DescriptionList>` 內以 `<DescriptionItem>` 呈現(不可孤立 dt/dd),**完整顯示不截斷**——內容過長時由 Body 區 `<ScrollArea>` 在固定高度內捲動,不 clamp 行數。
|
|
112
112
|
|
|
113
113
|
## Info Fields
|
|
114
114
|
|
|
@@ -46,7 +46,6 @@ export const ProfileCardDefaultActions = ({
|
|
|
46
46
|
// Dev-mode warning(對齊本檔 defaultFieldValues dev-warn 先例):預設 actions 沒接 handler
|
|
47
47
|
// = 按鈕點了永遠沒反應(2026-07-06 D4 finding);production 必傳 callback 或自組 actions。
|
|
48
48
|
if (process.env.NODE_ENV !== 'production' && !onChat && !onCall) {
|
|
49
|
-
// eslint-disable-next-line no-console
|
|
50
49
|
console.warn(
|
|
51
50
|
'[DS ProfileCard] ProfileCardDefaultActions 未接 onChat/onCall——按鈕點擊無反應。請傳入 callback(PersonData.onChat/onCall 會自動 forward)或自組 actions。'
|
|
52
51
|
)
|
|
@@ -199,7 +198,6 @@ const ProfileCard = React.forwardRef<HTMLDivElement, ProfileCardProps>(
|
|
|
199
198
|
Object.values(DEFAULT_FIELD_LABEL).includes(f.label as string),
|
|
200
199
|
)
|
|
201
200
|
if (legacyEntry) {
|
|
202
|
-
// eslint-disable-next-line no-console
|
|
203
201
|
console.warn(
|
|
204
202
|
`[ProfileCard] "${name}":legacy pattern — fields[].label="${legacyEntry.label}" ` +
|
|
205
203
|
`is a default field. Migrate to defaultFieldValues={{ id, employeeNumber }} prop ` +
|
|
@@ -210,7 +208,6 @@ const ProfileCard = React.forwardRef<HTMLDivElement, ProfileCardProps>(
|
|
|
210
208
|
|
|
211
209
|
// Dev mode warn:consumer 沒傳 default field 任何 key → 提示補完(避免漂移成 placeholder-only)
|
|
212
210
|
if (process.env.NODE_ENV !== 'production' && !defaultFieldValues) {
|
|
213
|
-
// eslint-disable-next-line no-console
|
|
214
211
|
console.warn(
|
|
215
212
|
`[ProfileCard] "${name}":no defaultFieldValues passed — sections will render placeholders. ` +
|
|
216
213
|
`Pass at least { id, employeeNumber } via defaultFieldValues prop. ` +
|
|
@@ -12,6 +12,7 @@ import { useState } from 'react'
|
|
|
12
12
|
import { CircleCheck, XCircle, X } from 'lucide-react'
|
|
13
13
|
import { ProgressBar } from './progress-bar'
|
|
14
14
|
import { Button } from '@/design-system/components/Button/button'
|
|
15
|
+
import { SegmentedControl, SegmentedControlItem } from '@/design-system/components/SegmentedControl/segmented-control'
|
|
15
16
|
import { H3, Desc, Th, Td, TokenCell } from '@/design-system/stories-helpers/anatomy/anatomy-utils'
|
|
16
17
|
|
|
17
18
|
const meta: Meta = {
|
|
@@ -138,18 +139,6 @@ export const Overview = {
|
|
|
138
139
|
2. 元件檢閱器
|
|
139
140
|
═══════════════════════════════════════════════════════════════════════════ */
|
|
140
141
|
|
|
141
|
-
const Tab = ({ active, onClick, children }: { active: boolean; onClick: () => void; children: React.ReactNode }) => (
|
|
142
|
-
<button
|
|
143
|
-
type="button"
|
|
144
|
-
onClick={onClick}
|
|
145
|
-
className={`px-2.5 py-1 text-[12px] font-mono rounded-md cursor-pointer transition-colors ${
|
|
146
|
-
active ? 'bg-primary text-white font-semibold' : 'bg-neutral-hover text-fg-secondary hover:bg-neutral-active'
|
|
147
|
-
}`}
|
|
148
|
-
>
|
|
149
|
-
{children}
|
|
150
|
-
</button>
|
|
151
|
-
)
|
|
152
|
-
|
|
153
142
|
const PropRow = ({ label, children }: { label: string; children: React.ReactNode }) => (
|
|
154
143
|
<div className="flex items-start gap-3 py-2 border-b border-divider last:border-b-0">
|
|
155
144
|
<span className="text-[11px] text-fg-muted font-medium w-[72px] shrink-0 pt-0.5">{label}</span>
|
|
@@ -171,21 +160,22 @@ const InspectorInner = () => {
|
|
|
171
160
|
|
|
172
161
|
return (
|
|
173
162
|
<div className="flex flex-col gap-6">
|
|
174
|
-
{/* Controls
|
|
163
|
+
{/* Controls — 互斥切換消費 SegmentedControl(segmented-control.spec.md「何時用」2–5 個互斥選項;
|
|
164
|
+
取代手刻 Tab:它靜止借 neutral-hover、hover 借 neutral-active,是 color.spec.md 成對 token 的錯配) */}
|
|
175
165
|
<div className="flex flex-col gap-2.5">
|
|
176
166
|
<div className="flex items-center gap-2">
|
|
177
167
|
<span className="text-[11px] text-fg-muted w-16 shrink-0">Status</span>
|
|
178
|
-
<
|
|
179
|
-
{STATUSES.map((s) => <
|
|
180
|
-
</
|
|
168
|
+
<SegmentedControl size="sm" aria-label="Status" value={status} onValueChange={(v) => setStatus(v as StatusKey)}>
|
|
169
|
+
{STATUSES.map((s) => <SegmentedControlItem key={s} value={s}>{s}</SegmentedControlItem>)}
|
|
170
|
+
</SegmentedControl>
|
|
181
171
|
</div>
|
|
182
172
|
<div className="flex items-center gap-2">
|
|
183
173
|
<span className="text-[11px] text-fg-muted w-16 shrink-0">Affix</span>
|
|
184
|
-
<
|
|
185
|
-
<
|
|
186
|
-
<
|
|
187
|
-
<
|
|
188
|
-
</
|
|
174
|
+
<SegmentedControl size="sm" aria-label="Affix" value={affix} onValueChange={(v) => setAffix(v as AffixKey)}>
|
|
175
|
+
<SegmentedControlItem value="none">none</SegmentedControlItem>
|
|
176
|
+
<SegmentedControlItem value="value">value</SegmentedControlItem>
|
|
177
|
+
<SegmentedControlItem value="status-icon">status-icon</SegmentedControlItem>
|
|
178
|
+
</SegmentedControl>
|
|
189
179
|
</div>
|
|
190
180
|
<div className="flex items-center gap-2">
|
|
191
181
|
<span className="text-[11px] text-fg-muted w-16 shrink-0">Value</span>
|
|
@@ -50,7 +50,7 @@ ProgressBar 是**量化 linear 進度** primitive——consumer 必須能回答
|
|
|
50
50
|
| 骨架載入(預期內容形狀) | `Skeleton` | Skeleton 保留 layout,ProgressBar 只傳達「執行中量化」 |
|
|
51
51
|
| 步驟導覽(顯示 step 1/2/3 結構) | `Steps` | Steps 強調 step 本身是什麼、ProgressBar 只看整體百分比 |
|
|
52
52
|
| 容量 / 配額靜態顯示(非動態進行中) | 可用 ProgressBar(不傳 status,預設 inProgress 即可) | 可接受——ProgressBar 也能表達靜態 ratio,但若只是裝飾比例、非「進行中」語意,考慮 Chart bar 類元件 |
|
|
53
|
-
| 評分顯示(
|
|
53
|
+
| 評分顯示(例:「★ 4.7」) | `Rating`(唯讀精簡版) | Rating 有離散刻度語意 |
|
|
54
54
|
| 小空間 / inline icon 位置的量化進度 | `CircularProgress`(有 value,determinate) | ProgressBar 在 16–24px 的小空間視覺比例不如 circular arc |
|
|
55
55
|
|
|
56
56
|
---
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Meta } from '@storybook/react'
|
|
2
2
|
import { useState } from 'react'
|
|
3
3
|
import { RadioGroup, RadioGroupItem } from './radio-group'
|
|
4
|
+
import { SegmentedControl, SegmentedControlItem } from '@/design-system/components/SegmentedControl/segmented-control'
|
|
4
5
|
|
|
5
6
|
const meta: Meta = {
|
|
6
7
|
title: 'Design System/Components/RadioGroup/設計規格',
|
|
@@ -99,15 +100,6 @@ const TokenAnnotation = ({ colors }: { colors: ColorSpec }) => (
|
|
|
99
100
|
</div>
|
|
100
101
|
)
|
|
101
102
|
|
|
102
|
-
const Tab = ({ active, onClick, children }: { active: boolean; onClick: () => void; children: React.ReactNode }) => (
|
|
103
|
-
<button type="button" onClick={onClick}
|
|
104
|
-
className={`px-2.5 py-1 text-[12px] font-mono rounded-md cursor-pointer transition-colors ${
|
|
105
|
-
active ? 'bg-primary text-white font-semibold' : 'bg-neutral-hover text-fg-secondary hover:bg-neutral-active'
|
|
106
|
-
}`}>
|
|
107
|
-
{children}
|
|
108
|
-
</button>
|
|
109
|
-
)
|
|
110
|
-
|
|
111
103
|
const PropRow = ({ label, dot, children }: { label: string; dot?: string; children: React.ReactNode }) => (
|
|
112
104
|
<div className="flex items-start gap-3 py-2 border-b border-divider last:border-b-0">
|
|
113
105
|
<span className="text-[11px] text-fg-muted font-medium w-[72px] shrink-0 pt-0.5 flex items-center gap-1.5">
|
|
@@ -254,32 +246,33 @@ const InspectorInner = () => {
|
|
|
254
246
|
|
|
255
247
|
return (
|
|
256
248
|
<div className="flex flex-col gap-6">
|
|
257
|
-
{/* Controls
|
|
249
|
+
{/* Controls — 互斥切換消費 SegmentedControl(segmented-control.spec.md「何時用」2–5 個互斥選項;
|
|
250
|
+
取代手刻 Tab:它靜止借 neutral-hover、hover 借 neutral-active,是 color.spec.md 成對 token 的錯配) */}
|
|
258
251
|
<div className="flex flex-col gap-2.5">
|
|
259
252
|
<div className="flex items-center gap-2">
|
|
260
253
|
<span className="text-[11px] text-fg-muted w-16 shrink-0">Checked</span>
|
|
261
|
-
<
|
|
262
|
-
{CHECKED_STATES.map((c) => <
|
|
263
|
-
</
|
|
254
|
+
<SegmentedControl size="sm" aria-label="Checked" value={checked} onValueChange={(v) => setChecked(v as CheckedKey)}>
|
|
255
|
+
{CHECKED_STATES.map((c) => <SegmentedControlItem key={c} value={c}>{c}</SegmentedControlItem>)}
|
|
256
|
+
</SegmentedControl>
|
|
264
257
|
</div>
|
|
265
258
|
<div className="flex items-center gap-2">
|
|
266
259
|
<span className="text-[11px] text-fg-muted w-16 shrink-0">State</span>
|
|
267
|
-
<
|
|
268
|
-
{STATES.map((st) => <
|
|
269
|
-
</
|
|
260
|
+
<SegmentedControl size="sm" aria-label="State" value={state} onValueChange={(v) => setState(v as StateKey)}>
|
|
261
|
+
{STATES.map((st) => <SegmentedControlItem key={st} value={st}>{st}</SegmentedControlItem>)}
|
|
262
|
+
</SegmentedControl>
|
|
270
263
|
</div>
|
|
271
264
|
<div className="flex items-center gap-2">
|
|
272
265
|
<span className="text-[11px] text-fg-muted w-16 shrink-0">Size</span>
|
|
273
|
-
<
|
|
274
|
-
{SIZES.map((sz) => <
|
|
275
|
-
</
|
|
266
|
+
<SegmentedControl size="sm" aria-label="Size" value={size} onValueChange={(v) => setSize(v as SizeKey)}>
|
|
267
|
+
{SIZES.map((sz) => <SegmentedControlItem key={sz} value={sz}>{sz}</SegmentedControlItem>)}
|
|
268
|
+
</SegmentedControl>
|
|
276
269
|
</div>
|
|
277
270
|
<div className="flex items-center gap-2">
|
|
278
271
|
<span className="text-[11px] text-fg-muted w-16 shrink-0">Description</span>
|
|
279
|
-
<
|
|
280
|
-
<
|
|
281
|
-
<
|
|
282
|
-
</
|
|
272
|
+
<SegmentedControl size="sm" aria-label="Description" value={withDescription ? 'on' : 'off'} onValueChange={(v) => setWithDescription(v === 'on')}>
|
|
273
|
+
<SegmentedControlItem value="off">off</SegmentedControlItem>
|
|
274
|
+
<SegmentedControlItem value="on">on</SegmentedControlItem>
|
|
275
|
+
</SegmentedControl>
|
|
283
276
|
<span className="text-[11px] text-fg-muted">label 下方的次要說明</span>
|
|
284
277
|
</div>
|
|
285
278
|
</div>
|
|
@@ -379,7 +372,7 @@ const InspectorInner = () => {
|
|
|
379
372
|
<div className="py-2 border-b border-divider"><span className="text-[10px] font-semibold text-fg-muted uppercase tracking-wider">Style</span></div>
|
|
380
373
|
<PropRow label="Radius"><TkVal token="rounded-full" value="9999px" /></PropRow>
|
|
381
374
|
<PropRow label="Border"><TkVal token="border" value="1px solid" /></PropRow>
|
|
382
|
-
<PropRow label="Focus"><TkVal token="
|
|
375
|
+
<PropRow label="Focus"><TkVal token=":focus-visible(全域規則,無 class)" value="outline: 2px solid var(--ring) / outline-offset: 2px" /></PropRow>
|
|
383
376
|
<PropRow label="Transition"><TkVal token="duration-150" value="colors" /></PropRow>
|
|
384
377
|
</div>
|
|
385
378
|
</div>
|
|
@@ -134,7 +134,7 @@ Item-level default / hover / checked / disabled **色彩**與 Checkbox 共用同
|
|
|
134
134
|
- ↑/↓ 與 ←/→ — 切 option(Radix roving focus,焦點移動即選取;設 `orientation` 時限對應軸向)
|
|
135
135
|
- Space — 選擇
|
|
136
136
|
|
|
137
|
-
**Focus**:Radix primitive 以 roving tabindex 管理群組內焦點(整組只佔一個 Tab 停留點,方向鍵在選項間移動);**非** focus trap——Tab
|
|
137
|
+
**Focus**:Radix primitive 以 roving tabindex 管理群組內焦點(整組只佔一個 Tab 停留點,方向鍵在選項間移動);**非** focus trap——Tab 仍會離開群組、跳到下一個表單欄位。聚焦時顯示焦點框:全域 `:focus-visible` 外描邊(`outline: 2px solid var(--ring)`,往外 2px;radio-group.tsx 不寫任何焦點 class,也不寫 `outline-none`,per design-system focus-visible canonical)。
|
|
138
138
|
|
|
139
139
|
**驗證**:Storybook a11y addon panel 應 0 critical violation;鍵盤完整可操作(無需滑鼠)。WCAG AA contrast ≥ 4.5:1(text)/ 3:1(UI)。
|
|
140
140
|
|
|
@@ -164,8 +164,19 @@ const RadioGroup = React.forwardRef<
|
|
|
164
164
|
<div
|
|
165
165
|
{...restDomProps}
|
|
166
166
|
ref={setRef}
|
|
167
|
-
role="radiogroup"
|
|
168
|
-
|
|
167
|
+
// ⛔ 2026-09-24:這裡原本是 role="radiogroup" + aria-readonly="true",是**空頭承諾**。
|
|
168
|
+
// 這個分支只渲一個 <span> 顯示選中值,**一個 role="radio" 子元素都沒有**,也沒有任何方向鍵
|
|
169
|
+
// —— 螢幕閱讀器會宣告「單選群組」並提供方向鍵,按下去什麼都沒有;而且 radiogroup 的
|
|
170
|
+
// aria-required-children 就是 radio,這棵樹在結構上本來就是壞的。
|
|
171
|
+
//
|
|
172
|
+
// 改成 role="group" 是**對齊本 DS 既有先例**(M23):Select 的 ReadonlyDisplay
|
|
173
|
+
//(select.tsx:374 起)就是純 <span> / <div>,**不掛任何 role**。
|
|
174
|
+
// aria-readonly 同時拿掉 —— WAI-ARIA 1.2 沒把它列進 group 的支援屬性
|
|
175
|
+
//(axe 的 aria-allowed-attr 會抓;rating.tsx 唯讀分支 role=img 記過同一個坑)。
|
|
176
|
+
// 唯讀語意已由「裡面根本沒有可互動控件」表達,與 Select 一致。
|
|
177
|
+
// 規則 → ds-canonical/references/keyboard-model-canonical.md「宣告了 composite 角色,
|
|
178
|
+
// 就必須真的實作那套鍵盤」;機械閘 scripts/composite-role-keyboard-invariant.mjs。
|
|
179
|
+
role="group"
|
|
169
180
|
aria-labelledby={fieldCtx?.labelId}
|
|
170
181
|
aria-invalid={fieldCtx?.invalid || undefined}
|
|
171
182
|
data-readonly="true"
|
|
@@ -229,6 +240,9 @@ const radioItemVariants = cva(
|
|
|
229
240
|
[
|
|
230
241
|
'grid place-content-center shrink-0 rounded-full',
|
|
231
242
|
'border border-border bg-surface',
|
|
243
|
+
// @hover-transition-allow: 這條過渡的主人是 checked ↔ unchecked 的狀態切換(外框轉主色、圓點出現),與 checkbox.tsx 同一個例外,
|
|
244
|
+
// 不是 hover;而且它是控件大小的點目標,不是指標掃過去的列面。例外清單的唯一住所 = tokens/motion/motion.spec.md「唯一的例外」
|
|
245
|
+
// (2026-09-26 補登,待辦總帳 L9 / N4(3):hover 延伸到外框時盤點出 Radio 與 Checkbox 同一種過渡,先前漏列)。
|
|
232
246
|
'transition-colors duration-150',
|
|
233
247
|
'hover:border-border-hover',
|
|
234
248
|
'data-[state=checked]:border-primary data-[state=checked]:text-primary',
|
|
@@ -237,7 +251,11 @@ const radioItemVariants = cva(
|
|
|
237
251
|
'disabled:data-[state=checked]:bg-disabled disabled:data-[state=checked]:border-transparent disabled:data-[state=checked]:text-fg-disabled',
|
|
238
252
|
// readOnly:鎖定互動但維持 checked/unchecked 視覺
|
|
239
253
|
'data-[readonly=true]:pointer-events-none data-[readonly=true]:cursor-default',
|
|
240
|
-
|
|
254
|
+
// readOnly × hover:框與點都釘在自己的靜止值,不升 hover 階(checkbox.spec.md「狀態 › Radio」表 readonly 列)。
|
|
255
|
+
// 指標停在 SelectionItem 的 <label for> 上時控件照樣進入 :hover(pointer-events-none 擋不住),原本只釘了
|
|
256
|
+
// 未選中的框 → 已選中的框與點(text-current)仍轉 primary-hover。寫法與 checkbox.tsx 同一組,`enabled:` 理由同該檔。
|
|
257
|
+
'data-[readonly=true]:enabled:data-[state=unchecked]:hover:border-border',
|
|
258
|
+
'data-[readonly=true]:enabled:data-[state=checked]:hover:border-primary data-[readonly=true]:enabled:data-[state=checked]:hover:text-primary',
|
|
241
259
|
],
|
|
242
260
|
{
|
|
243
261
|
variants: {
|
|
@@ -410,7 +428,7 @@ export const radioGroupMeta = {
|
|
|
410
428
|
tokens: {
|
|
411
429
|
bg: ['bg-disabled', 'bg-surface'],
|
|
412
430
|
fg: ['text-fg-disabled', 'text-fg-secondary', 'text-foreground', 'text-primary'],
|
|
413
|
-
ring: ['ring
|
|
431
|
+
ring: ['--ring'],
|
|
414
432
|
},
|
|
415
433
|
defaultSize: 'md',
|
|
416
434
|
} as const
|