@qijenchen/design-system 0.1.0-beta.95 → 0.1.0-beta.99
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/README.md +1 -0
- package/dist/components/AccountMenu/account-menu.d.ts +86 -0
- package/dist/components/AccountMenu/account-menu.d.ts.map +1 -0
- package/dist/components/AccountMenu/account-menu.js +64 -0
- package/dist/components/AccountMenu/account-menu.js.map +1 -0
- package/dist/components/AccountMenu/index.d.ts +2 -0
- package/dist/components/AccountMenu/index.d.ts.map +1 -0
- package/dist/components/AccountMenu/index.js +6 -0
- package/dist/components/AccountMenu/index.js.map +1 -0
- package/dist/components/AppShell/_demo-helpers.d.ts.map +1 -1
- package/dist/components/AppShell/app-shell.d.ts.map +1 -1
- package/dist/components/AppShell/app-shell.js +2 -2
- package/dist/components/AppShell/app-shell.js.map +1 -1
- package/dist/components/Badge/badge.d.ts +1 -1
- package/dist/components/Button/button.d.ts +3 -3
- package/dist/components/Button/button.d.ts.map +1 -1
- package/dist/components/Button/button.js +3 -2
- package/dist/components/Button/button.js.map +1 -1
- package/dist/components/Calendar/calendar.d.ts +10 -21
- package/dist/components/Calendar/calendar.d.ts.map +1 -1
- package/dist/components/Calendar/calendar.js +52 -59
- package/dist/components/Calendar/calendar.js.map +1 -1
- package/dist/components/Combobox/combobox.d.ts.map +1 -1
- package/dist/components/Combobox/combobox.js +1 -0
- package/dist/components/Combobox/combobox.js.map +1 -1
- package/dist/components/Command/command.d.ts.map +1 -1
- package/dist/components/Command/command.js +3 -1
- 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 +1 -5
- package/dist/components/DataTable/cell-registry.js.map +1 -1
- package/dist/components/DataTable/data-table-filter-group.d.ts +6 -2
- package/dist/components/DataTable/data-table-filter-group.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-group.js +32 -27
- package/dist/components/DataTable/data-table-filter-group.js.map +1 -1
- package/dist/components/DataTable/data-table-filter-labels.d.ts +44 -0
- package/dist/components/DataTable/data-table-filter-labels.d.ts.map +1 -0
- package/dist/components/DataTable/data-table-filter-labels.js +55 -0
- package/dist/components/DataTable/data-table-filter-labels.js.map +1 -0
- package/dist/components/DataTable/data-table-filter-panel.d.ts +11 -0
- package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-panel.js +33 -10
- package/dist/components/DataTable/data-table-filter-panel.js.map +1 -1
- package/dist/components/DataTable/data-table-filter-value-picker.d.ts +4 -1
- package/dist/components/DataTable/data-table-filter-value-picker.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-value-picker.js +22 -8
- package/dist/components/DataTable/data-table-filter-value-picker.js.map +1 -1
- package/dist/components/DataTable/data-table.d.ts.map +1 -1
- package/dist/components/DataTable/data-table.js +5 -4
- package/dist/components/DataTable/data-table.js.map +1 -1
- package/dist/components/DataTable/index.js +2 -0
- package/dist/components/DataTable/index.js.map +1 -1
- package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
- package/dist/components/DatePicker/date-picker.js +187 -156
- package/dist/components/DatePicker/date-picker.js.map +1 -1
- package/dist/components/Dialog/dialog.d.ts +1 -1
- package/dist/components/Dialog/dialog.d.ts.map +1 -1
- package/dist/components/Dialog/dialog.js +4 -2
- package/dist/components/Dialog/dialog.js.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts +4 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.js +46 -14
- package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
- package/dist/components/Field/field-context.d.ts +12 -0
- package/dist/components/Field/field-context.d.ts.map +1 -1
- package/dist/components/Field/field-context.js +10 -1
- package/dist/components/Field/field-context.js.map +1 -1
- package/dist/components/Field/field-wrapper.d.ts.map +1 -1
- package/dist/components/Field/field-wrapper.js +7 -1
- package/dist/components/Field/field-wrapper.js.map +1 -1
- package/dist/components/Field/field.d.ts.map +1 -1
- package/dist/components/Field/field.js +11 -2
- package/dist/components/Field/field.js.map +1 -1
- package/dist/components/FieldControlGroup/field-control-group.d.ts +2 -5
- package/dist/components/FieldControlGroup/field-control-group.d.ts.map +1 -1
- package/dist/components/FieldControlGroup/field-control-group.js +2 -2
- package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
- package/dist/components/FileUpload/file-upload.js +1 -1
- 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 +20 -3
- package/dist/components/FileViewer/file-viewer.js.map +1 -1
- package/dist/components/InlineEdit/inline-edit.d.ts +5 -0
- package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -1
- package/dist/components/InlineEdit/inline-edit.js +65 -33
- 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 +3 -2
- 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 +2 -1
- package/dist/components/LinkInput/link-input.js.map +1 -1
- package/dist/components/Menu/menu-item.d.ts.map +1 -1
- package/dist/components/Menu/menu-item.js +4 -2
- package/dist/components/Menu/menu-item.js.map +1 -1
- package/dist/components/PeoplePicker/people-picker.d.ts +2 -0
- package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
- package/dist/components/PeoplePicker/people-picker.js +4 -0
- package/dist/components/PeoplePicker/people-picker.js.map +1 -1
- package/dist/components/Popover/popover.d.ts.map +1 -1
- package/dist/components/Popover/popover.js +4 -2
- package/dist/components/Popover/popover.js.map +1 -1
- package/dist/components/ScrollArea/scroll-area.d.ts +4 -3
- package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
- package/dist/components/ScrollArea/scroll-area.js +1 -1
- package/dist/components/ScrollArea/scroll-area.js.map +1 -1
- package/dist/components/Select/select.d.ts.map +1 -1
- package/dist/components/Select/select.js +59 -37
- package/dist/components/Select/select.js.map +1 -1
- package/dist/components/SelectMenu/select-menu.d.ts +2 -0
- package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
- package/dist/components/SelectMenu/select-menu.js +3 -1
- package/dist/components/SelectMenu/select-menu.js.map +1 -1
- package/dist/components/Sheet/sheet.d.ts +1 -1
- package/dist/components/Sidebar/sidebar.js +2 -2
- package/dist/components/Sidebar/sidebar.js.map +1 -1
- package/dist/components/Tabs/tabs.d.ts.map +1 -1
- package/dist/components/Tabs/tabs.js +3 -0
- 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 +4 -3
- package/dist/components/Tag/tag.js.map +1 -1
- package/dist/components/Textarea/textarea.d.ts +11 -0
- package/dist/components/Textarea/textarea.d.ts.map +1 -1
- package/dist/components/Textarea/textarea.js +22 -5
- package/dist/components/Textarea/textarea.js.map +1 -1
- package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
- package/dist/components/TimePicker/time-picker.js +4 -3
- package/dist/components/TimePicker/time-picker.js.map +1 -1
- package/dist/components/Tooltip/tooltip.d.ts +1 -1
- package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
- package/dist/components/Tooltip/tooltip.js +26 -1
- package/dist/components/Tooltip/tooltip.js.map +1 -1
- package/dist/components/TreeView/tree-view.d.ts +2 -0
- package/dist/components/TreeView/tree-view.d.ts.map +1 -1
- package/dist/components/TreeView/tree-view.js +3 -2
- package/dist/components/TreeView/tree-view.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.d.ts +10 -1
- package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.js +77 -29
- package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
- package/dist/patterns/header-canonical/chrome-header.d.ts +11 -4
- package/dist/patterns/header-canonical/chrome-header.d.ts.map +1 -1
- package/dist/patterns/header-canonical/chrome-header.js +1 -1
- package/dist/patterns/header-canonical/chrome-header.js.map +1 -1
- package/dist/patterns/overlay-surface/overlay-surface.d.ts +4 -3
- package/dist/patterns/overlay-surface/overlay-surface.d.ts.map +1 -1
- package/dist/patterns/overlay-surface/overlay-surface.js +1 -0
- package/dist/patterns/overlay-surface/overlay-surface.js.map +1 -1
- package/dist/patterns/resize-handle/resize-handle.d.ts +9 -11
- package/dist/patterns/resize-handle/resize-handle.d.ts.map +1 -1
- package/dist/patterns/resize-handle/resize-handle.js +3 -6
- package/dist/patterns/resize-handle/resize-handle.js.map +1 -1
- package/ds-canonical/adapters/claude/agents/canonical-reviewer.md +1 -1
- package/ds-canonical/adapters/claude-root-instructions.md +1 -1
- package/ds-canonical/adapters/claude-settings-base.json +2 -5
- package/ds-canonical/fork/AGENTS.md +21 -10
- package/ds-canonical/fork/codex/hooks.json +1 -1
- package/ds-canonical/fork/common/instruction.md +21 -10
- package/ds-canonical/fork/consumer/governance-check.mjs +101 -13
- package/ds-canonical/fork/consumer/lock.json +14 -15
- package/ds-canonical/fork/consumer/managed-files/46f10bc55cdfdf834d0749eebff42ea0bb8576f361b36d2b6ba1851354c50150.blob +26 -3
- package/ds-canonical/fork/consumer/managed-files/63061e05969667005020166ea2ae7df3c96663f7a928e7dd3eebc1471022df56.blob +61 -5
- package/ds-canonical/fork/consumer/managed-files/93a89f16f6717d1e4e4e27ec20ef916e3355c7aa890ca74cd9b12db30d8a899f.blob +3 -10
- package/ds-canonical/fork/consumer/managed-files/bc44cabad181f766611dab6a8f52d81100afadd50ac59799015e8ba7bf46d9eb.blob +3 -2
- package/ds-canonical/fork/governance.lock +17 -33
- package/ds-canonical/fork/hooks/check_consumer_code_quality.sh +33 -0
- package/ds-canonical/fork/hooks/check_pattern_invariants.sh +39 -0
- package/ds-canonical/fork/hooks/check_sidebar_menu_button_implicit_wrap.sh +6 -4
- package/ds-canonical/fork/launchers/provider-hook-normalization.mjs +5 -11
- package/ds-canonical/fork/launchers/settings-hooks.json +2 -2
- package/ds-canonical/fork/manifest.json +11 -13
- package/ds-canonical/fork/providers/claude/hook-config.json +1 -7
- package/ds-canonical/fork/providers/codex/hook-config.json +1 -1
- package/ds-canonical/fork/references/story-baseline-registry.json +7 -1
- package/ds-canonical/hooks/check_consumer_code_quality.sh +33 -0
- package/ds-canonical/hooks/check_pattern_invariants.sh +39 -0
- package/ds-canonical/hooks/check_sidebar_menu_button_implicit_wrap.sh +6 -4
- package/ds-canonical/hooks/session_start_governance_check.sh +5 -6
- package/ds-canonical/hooks/tests/test_check_consumer_code_quality.sh +23 -1
- package/ds-canonical/hooks/tests/test_check_pattern_invariants.sh +36 -2
- package/ds-canonical/references/audit-coverage-vs-24-checklist.md +1 -1
- package/ds-canonical/references/failure-class-registry.json +151 -144
- package/ds-canonical/references/preflight-gate-baseline.json +16 -12
- package/ds-canonical/references/story-baseline-registry.json +7 -1
- package/ds-canonical/rules/meta-patterns.md +3 -3
- package/ds-canonical/skills/design-system-audit/SKILL.md +1 -1
- package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +1 -1
- package/ds-canonical/templates/dashboard-app.tsx +31 -8
- package/llms-full.txt +6 -1
- package/llms.txt +2 -1
- package/package.json +1 -1
- package/src/components/Accordion/accordion.principles.stories.tsx +16 -26
- package/src/components/Accordion/accordion.stories.tsx +22 -40
- package/src/components/AccountMenu/account-menu.anatomy.stories.tsx +115 -0
- package/src/components/AccountMenu/account-menu.principles.stories.tsx +99 -0
- package/src/components/AccountMenu/account-menu.spec.md +101 -0
- package/src/components/AccountMenu/account-menu.stories.tsx +61 -0
- package/src/components/AccountMenu/account-menu.tsx +170 -0
- package/src/components/AccountMenu/index.ts +5 -0
- package/src/components/AppShell/_demo-helpers.tsx +53 -53
- package/src/components/AppShell/app-shell.spec.md +5 -1
- package/src/components/AppShell/app-shell.tsx +14 -12
- package/src/components/Avatar/avatar.principles.stories.tsx +4 -4
- package/src/components/Avatar/avatar.stories.tsx +20 -5
- package/src/components/Breadcrumb/breadcrumb.spec.md +1 -1
- package/src/components/Button/button.spec.md +1 -1
- package/src/components/Button/button.stories.tsx +21 -9
- package/src/components/Button/button.tsx +5 -4
- package/src/components/Calendar/calendar.anatomy.stories.tsx +19 -17
- package/src/components/Calendar/calendar.principles.stories.tsx +5 -6
- package/src/components/Calendar/calendar.spec.md +25 -29
- package/src/components/Calendar/calendar.stories.tsx +5 -7
- package/src/components/Calendar/calendar.tsx +75 -87
- package/src/components/Carousel/carousel.principles.stories.tsx +5 -1
- package/src/components/Chart/chart.spec.md +1 -1
- package/src/components/Checkbox/checkbox.anatomy.stories.tsx +14 -5
- package/src/components/Chip/chip.spec.md +1 -1
- package/src/components/CircularProgress/circular-progress.anatomy.stories.tsx +7 -6
- package/src/components/CircularProgress/circular-progress.principles.stories.tsx +8 -6
- package/src/components/CircularProgress/circular-progress.stories.tsx +3 -3
- package/src/components/Combobox/combobox.tsx +1 -0
- package/src/components/Command/command.spec.md +1 -0
- package/src/components/Command/command.tsx +4 -1
- package/src/components/DataTable/cell-registry.tsx +3 -7
- package/src/components/DataTable/data-table-filter-group.tsx +44 -27
- package/src/components/DataTable/data-table-filter-labels.ts +100 -0
- package/src/components/DataTable/data-table-filter-panel.tsx +48 -8
- package/src/components/DataTable/data-table-filter-value-picker.tsx +25 -8
- package/src/components/DataTable/data-table.spec.md +18 -1
- package/src/components/DataTable/data-table.stories.tsx +346 -33
- package/src/components/DataTable/data-table.tsx +11 -4
- package/src/components/DataTable/filter-operators.spec.md +2 -2
- package/src/components/DateGrid/date-grid.anatomy.stories.tsx +24 -20
- package/src/components/DateGrid/date-grid.principles.stories.tsx +27 -21
- package/src/components/DateGrid/date-grid.stories.tsx +22 -13
- package/src/components/DatePicker/date-picker.anatomy.stories.tsx +3 -3
- package/src/components/DatePicker/date-picker.spec.md +4 -4
- package/src/components/DatePicker/date-picker.stories.tsx +33 -9
- package/src/components/DatePicker/date-picker.tsx +210 -147
- package/src/components/Dialog/dialog.spec.md +1 -1
- package/src/components/Dialog/dialog.tsx +8 -2
- package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +1 -1
- package/src/components/DropdownMenu/dropdown-menu.spec.md +5 -0
- package/src/components/DropdownMenu/dropdown-menu.stories.tsx +3 -17
- package/src/components/DropdownMenu/dropdown-menu.tsx +58 -16
- package/src/components/Field/field-context.ts +25 -1
- package/src/components/Field/field-wrapper.tsx +7 -1
- package/src/components/Field/field.spec.md +8 -1
- package/src/components/Field/field.tsx +14 -2
- package/src/components/FieldControlGroup/field-control-group.anatomy.stories.tsx +3 -3
- package/src/components/FieldControlGroup/field-control-group.spec.md +2 -3
- package/src/components/FieldControlGroup/field-control-group.stories.tsx +2 -1
- package/src/components/FieldControlGroup/field-control-group.tsx +6 -9
- package/src/components/FileItem/file-item.spec.md +1 -1
- package/src/components/FileItem/file-item.stories.tsx +1 -1
- package/src/components/FileUpload/file-upload.stories.tsx +32 -0
- package/src/components/FileUpload/file-upload.tsx +1 -1
- package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +1 -1
- package/src/components/FileViewer/file-viewer.spec.md +1 -1
- package/src/components/FileViewer/file-viewer.stories.tsx +7 -0
- package/src/components/FileViewer/file-viewer.tsx +23 -5
- package/src/components/InlineEdit/inline-edit.spec.md +12 -0
- package/src/components/InlineEdit/inline-edit.stories.tsx +30 -19
- package/src/components/InlineEdit/inline-edit.tsx +94 -31
- package/src/components/Input/input.anatomy.stories.tsx +28 -12
- package/src/components/Input/input.principles.stories.tsx +23 -18
- package/src/components/Input/input.stories.tsx +9 -1
- package/src/components/Input/input.tsx +7 -2
- package/src/components/LinkInput/link-input.principles.stories.tsx +7 -7
- package/src/components/LinkInput/link-input.spec.md +1 -1
- package/src/components/LinkInput/link-input.tsx +5 -1
- package/src/components/Menu/menu-item.anatomy.stories.tsx +1 -1
- package/src/components/Menu/menu-item.tsx +9 -2
- package/src/components/NumberInput/number-input.anatomy.stories.tsx +44 -10
- package/src/components/NumberInput/number-input.principles.stories.tsx +20 -14
- package/src/components/NumberInput/number-input.spec.md +1 -1
- package/src/components/NumberInput/number-input.stories.tsx +12 -5
- package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +7 -0
- package/src/components/PeoplePicker/people-picker.spec.md +3 -1
- package/src/components/PeoplePicker/people-picker.tsx +5 -0
- package/src/components/Popover/popover.tsx +7 -2
- package/src/components/ProgressBar/progress-bar.principles.stories.tsx +14 -12
- package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +5 -1
- package/src/components/RadioGroup/radio-group.spec.md +1 -1
- package/src/components/Rating/rating.principles.stories.tsx +2 -2
- package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.spec.md +3 -3
- package/src/components/ScrollArea/scroll-area.tsx +5 -4
- package/src/components/Select/select.principles.stories.tsx +4 -3
- package/src/components/Select/select.stories.tsx +7 -6
- package/src/components/Select/select.tsx +61 -29
- package/src/components/SelectMenu/select-menu.spec.md +1 -1
- package/src/components/SelectMenu/select-menu.tsx +4 -1
- package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +19 -8
- package/src/components/SelectionControl/selection-item.stories.tsx +7 -7
- package/src/components/Sheet/sheet.spec.md +1 -1
- package/src/components/Sidebar/sidebar.spec.md +3 -2
- package/src/components/Sidebar/sidebar.stories.tsx +67 -37
- package/src/components/Sidebar/sidebar.tsx +3 -3
- package/src/components/Slider/slider.principles.stories.tsx +9 -9
- package/src/components/Steps/steps.spec.md +1 -0
- package/src/components/Steps/steps.stories.tsx +18 -1
- package/src/components/Switch/switch.anatomy.stories.tsx +11 -11
- package/src/components/Switch/switch.principles.stories.tsx +11 -8
- package/src/components/Tabs/tabs.principles.stories.tsx +24 -0
- package/src/components/Tabs/tabs.spec.md +6 -4
- package/src/components/Tabs/tabs.tsx +11 -0
- package/src/components/Tag/tag.anatomy.stories.tsx +5 -2
- package/src/components/Tag/tag.spec.md +4 -3
- package/src/components/Tag/tag.stories.tsx +19 -0
- package/src/components/Tag/tag.tsx +14 -5
- package/src/components/Textarea/textarea.anatomy.stories.tsx +15 -9
- package/src/components/Textarea/textarea.principles.stories.tsx +21 -5
- package/src/components/Textarea/textarea.spec.md +10 -1
- package/src/components/Textarea/textarea.tsx +38 -5
- package/src/components/TimePicker/time-picker.tsx +11 -5
- package/src/components/Tooltip/tooltip.spec.md +10 -0
- package/src/components/Tooltip/tooltip.tsx +34 -3
- package/src/components/TreeView/tree-view.spec.md +3 -3
- package/src/components/TreeView/tree-view.stories.tsx +68 -9
- package/src/components/TreeView/tree-view.tsx +7 -2
- package/src/index.ts +9 -1
- package/src/patterns/element-anatomy/inline-action.spec.md +12 -8
- package/src/patterns/element-anatomy/item-anatomy.spec.md +8 -1
- package/src/patterns/element-anatomy/item-anatomy.stories.tsx +24 -5
- package/src/patterns/element-anatomy/item-anatomy.tsx +120 -35
- package/src/patterns/element-anatomy/truncated-text.spec.md +13 -2
- package/src/patterns/header-canonical/chrome-header.tsx +14 -5
- package/src/patterns/header-canonical/header-canonical.spec.md +10 -7
- package/src/patterns/header-canonical/header-canonical.stories.tsx +77 -0
- package/src/patterns/horizontal-overflow/horizontal-overflow.stories.tsx +2 -0
- package/src/patterns/overlay-surface/overlay-surface.spec.md +1 -0
- package/src/patterns/overlay-surface/overlay-surface.stories.tsx +17 -1
- package/src/patterns/overlay-surface/overlay-surface.tsx +8 -4
- package/src/patterns/resize-handle/resize-handle.spec.md +11 -13
- package/src/patterns/resize-handle/resize-handle.stories.tsx +1 -3
- package/src/patterns/resize-handle/resize-handle.tsx +13 -18
- package/src/tokens/color/semantic.css +3 -1
- package/src/tokens/layoutSpace/layoutSpace.stories.tsx +1 -1
- package/src/tokens/opacity/opacity.stories.tsx +1 -1
- package/src/tokens/radius/radius.stories.tsx +3 -3
- package/src/tokens/uiSize/uiSize.css +11 -0
- package/src/tokens/uiSize/uiSize.spec.md +1 -0
- package/ds-canonical/commands/gov-status.md +0 -9
- package/ds-canonical/fork/consumer/managed-files/77e06f62670dfee01e9b5e8b0f8dad085ce711d75538c1fb0679d9818acaca82.blob +0 -424
- package/ds-canonical/fork/preamble.md +0 -500
- package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +0 -8
- package/ds-canonical/hooks/tests/test_check_post_main_ssot_propagate.sh +0 -37
|
@@ -122,7 +122,7 @@ export const UsageGuidance: Story = {
|
|
|
122
122
|
note="status 三選一是系統決定的語意。consumer 要紅、綠、藍以外的色 → 提到系統層討論是否新增 status,不要每個消費者自己用 className override fill 色。"
|
|
123
123
|
>
|
|
124
124
|
<Frame>
|
|
125
|
-
<ProgressBar value={55} status="inProgress" />
|
|
125
|
+
<ProgressBar value={55} status="inProgress" aria-label="標準進度色示範" />
|
|
126
126
|
</Frame>
|
|
127
127
|
<Label>✅ 走 status token(info / success / error),不 override 色值</Label>
|
|
128
128
|
</Rule>
|
|
@@ -139,7 +139,7 @@ export const UsageGuidance: Story = {
|
|
|
139
139
|
<span className="text-body flex-1">匯出客戶名單 CSV</span>
|
|
140
140
|
<span className="text-caption text-fg-muted tabular-nums">812 / 1,250 筆</span>
|
|
141
141
|
</div>
|
|
142
|
-
<ProgressBar value={65} status="inProgress" />
|
|
142
|
+
<ProgressBar value={65} status="inProgress" aria-label="匯出客戶名單進度" />
|
|
143
143
|
</Frame>
|
|
144
144
|
<Label>✅ CSV 匯出 / 批次處理:總量已知 → ProgressBar(檔案上傳改用 FileItem,見下方)</Label>
|
|
145
145
|
|
|
@@ -159,7 +159,7 @@ export const UsageGuidance: Story = {
|
|
|
159
159
|
<FileText size={16} className="text-fg-muted" />
|
|
160
160
|
<span className="text-body flex-1">產生季度報表中...</span>
|
|
161
161
|
</div>
|
|
162
|
-
<ProgressBar value={0} status="inProgress" />
|
|
162
|
+
<ProgressBar value={0} status="inProgress" aria-label="無法量化的報表產生進度(誤用)" />
|
|
163
163
|
</Frame>
|
|
164
164
|
<Label warn>❌ 若無法量化卻硬用 ProgressBar(永遠卡 0% 或亂跳):使用者會以為壞掉 → 改用 CircularProgress(indeterminate)</Label>
|
|
165
165
|
</Rule>
|
|
@@ -174,7 +174,7 @@ export const UsageGuidance: Story = {
|
|
|
174
174
|
<span className="text-body flex-1">附件_會議記錄.docx</span>
|
|
175
175
|
<span className="text-caption text-fg-muted">58%</span>
|
|
176
176
|
</div>
|
|
177
|
-
<ProgressBar value={58} status="inProgress" />
|
|
177
|
+
<ProgressBar value={58} status="inProgress" aria-label="附件上傳進度(自組誤用)" />
|
|
178
178
|
</Frame>
|
|
179
179
|
<Label warn>❌ Consumer 自組檔名 + Paperclip + ProgressBar 做上傳列表 → 應該用 FileItem</Label>
|
|
180
180
|
|
|
@@ -184,7 +184,7 @@ export const UsageGuidance: Story = {
|
|
|
184
184
|
<span className="text-body-lg font-medium flex-1">匯入客戶名單</span>
|
|
185
185
|
<span className="text-caption text-fg-muted tabular-nums">812 / 1,250 筆</span>
|
|
186
186
|
</div>
|
|
187
|
-
<ProgressBar value={65} status="inProgress" affix="value" />
|
|
187
|
+
<ProgressBar value={65} status="inProgress" affix="value" aria-label="匯入客戶名單進度" />
|
|
188
188
|
<p className="text-footnote text-fg-muted mt-2">處理中,預計剩餘 28 秒</p>
|
|
189
189
|
</Frame>
|
|
190
190
|
<Label>✅ CSV 批次匯入 / 報表生成 → 直接用 ProgressBar(非檔案情境,FileItem 不適用)</Label>
|
|
@@ -207,7 +207,7 @@ export const StatusRule: Story = {
|
|
|
207
207
|
<Paperclip size={16} className="text-fg-muted" />
|
|
208
208
|
<span className="text-body flex-1">Q1_財報.xlsx</span>
|
|
209
209
|
</div>
|
|
210
|
-
<ProgressBar value={42} status="inProgress" affix="value" />
|
|
210
|
+
<ProgressBar value={42} status="inProgress" affix="value" aria-label="Q1 財報上傳進度" />
|
|
211
211
|
</Frame>
|
|
212
212
|
<Label>✅ 上傳中用 inProgress,affix 顯示進度百分比</Label>
|
|
213
213
|
|
|
@@ -216,7 +216,7 @@ export const StatusRule: Story = {
|
|
|
216
216
|
<Paperclip size={16} className="text-fg-muted" />
|
|
217
217
|
<span className="text-body flex-1">簡報_final.pptx</span>
|
|
218
218
|
</div>
|
|
219
|
-
<ProgressBar value={100} status="success" affix="status-icon" />
|
|
219
|
+
<ProgressBar value={100} status="success" affix="status-icon" aria-label="簡報上傳完成" />
|
|
220
220
|
</Frame>
|
|
221
221
|
<Label>✅ 完成用 success + 勾 icon(終態指示)</Label>
|
|
222
222
|
|
|
@@ -226,7 +226,7 @@ export const StatusRule: Story = {
|
|
|
226
226
|
<span className="text-body flex-1">影片素材.mp4</span>
|
|
227
227
|
<span className="text-caption text-error">檔案過大</span>
|
|
228
228
|
</div>
|
|
229
|
-
<ProgressBar value={68} status="error" affix="status-icon" />
|
|
229
|
+
<ProgressBar value={68} status="error" affix="status-icon" aria-label="影片素材上傳失敗" />
|
|
230
230
|
</Frame>
|
|
231
231
|
<Label>✅ 失敗用 error + 叉 icon,旁邊可補上具體錯誤說明</Label>
|
|
232
232
|
</Rule>
|
|
@@ -240,7 +240,7 @@ export const StatusRule: Story = {
|
|
|
240
240
|
<span className="text-body flex-1">儲存空間</span>
|
|
241
241
|
<span className="text-caption text-error tabular-nums">95% 使用</span>
|
|
242
242
|
</div>
|
|
243
|
-
<ProgressBar value={95} status="error" />
|
|
243
|
+
<ProgressBar value={95} status="error" aria-label="儲存空間使用率" />
|
|
244
244
|
</Frame>
|
|
245
245
|
<Label>✅ 配額快滿:consumer 判斷「95% 以上顯示警示」後自行把 status 切成 error</Label>
|
|
246
246
|
</Rule>
|
|
@@ -260,7 +260,7 @@ export const AffixRule: Story = {
|
|
|
260
260
|
<div className="flex items-center gap-2 mb-2">
|
|
261
261
|
<span className="text-body flex-1">儲存空間</span>
|
|
262
262
|
</div>
|
|
263
|
-
<ProgressBar value={78} status="inProgress" affix="value" />
|
|
263
|
+
<ProgressBar value={78} status="inProgress" affix="value" aria-label="儲存空間使用率" />
|
|
264
264
|
</Frame>
|
|
265
265
|
<Label>✅ 配額顯示:affix=value,使用者要知道確切百分比</Label>
|
|
266
266
|
|
|
@@ -269,7 +269,7 @@ export const AffixRule: Story = {
|
|
|
269
269
|
<Paperclip size={16} className="text-fg-muted" />
|
|
270
270
|
<span className="text-body flex-1">提案書_終版.pdf</span>
|
|
271
271
|
</div>
|
|
272
|
-
<ProgressBar value={100} status="success" affix="status-icon" />
|
|
272
|
+
<ProgressBar value={100} status="success" affix="status-icon" aria-label="提案書上傳完成" />
|
|
273
273
|
</Frame>
|
|
274
274
|
<Label>✅ 上傳完成:affix=status-icon,終態不需要再看百分比</Label>
|
|
275
275
|
</Rule>
|
|
@@ -286,6 +286,7 @@ export const AffixRule: Story = {
|
|
|
286
286
|
<ProgressBar
|
|
287
287
|
value={42}
|
|
288
288
|
status="inProgress"
|
|
289
|
+
aria-label="設計規範上傳進度"
|
|
289
290
|
affix={<Button variant="text" size="xs" iconOnly startIcon={X} aria-label="取消上傳" />}
|
|
290
291
|
/>
|
|
291
292
|
</Frame>
|
|
@@ -299,6 +300,7 @@ export const AffixRule: Story = {
|
|
|
299
300
|
<ProgressBar
|
|
300
301
|
value={66}
|
|
301
302
|
status="inProgress"
|
|
303
|
+
aria-label="影片素材上傳進度"
|
|
302
304
|
affix={<span className="text-caption text-fg-muted tabular-nums shrink-0">3.3 / 5.0 MB</span>}
|
|
303
305
|
/>
|
|
304
306
|
</Frame>
|
|
@@ -315,7 +317,7 @@ export const AffixRule: Story = {
|
|
|
315
317
|
<span className="text-body flex-1">附件.zip</span>
|
|
316
318
|
<span className="text-caption text-fg-muted tabular-nums">55%</span>
|
|
317
319
|
</div>
|
|
318
|
-
<ProgressBar value={55} status="inProgress" />
|
|
320
|
+
<ProgressBar value={55} status="inProgress" aria-label="附件上傳進度" />
|
|
319
321
|
</Frame>
|
|
320
322
|
<Label>✅ 上方已有 55% 文字 → bar 不加 affix,避免重複</Label>
|
|
321
323
|
</Rule>
|
|
@@ -429,7 +429,11 @@ export const ColorMatrix = {
|
|
|
429
429
|
return (
|
|
430
430
|
<td key={st} className="p-3 border-b border-divider align-top min-w-[160px]">
|
|
431
431
|
<RadioGroup defaultValue={isChecked ? 'demo' : undefined}>
|
|
432
|
-
<RadioGroupItem
|
|
432
|
+
<RadioGroupItem
|
|
433
|
+
value="demo"
|
|
434
|
+
disabled={isDisabled}
|
|
435
|
+
aria-label={`${c} ${st} Radio`}
|
|
436
|
+
/>
|
|
433
437
|
</RadioGroup>
|
|
434
438
|
<TokenAnnotation colors={TOKEN_MAP[c][st]} />
|
|
435
439
|
</td>
|
|
@@ -111,7 +111,7 @@ Item-level default / hover / checked / disabled **色彩**與 Checkbox 共用同
|
|
|
111
111
|
- **No value selected**:`value=null` 為合法 initial state(所有 radio 都 unchecked);Field validation 在 required + null 時觸發 error。
|
|
112
112
|
- **極長 label / description**:預設不截斷、自然換行(`labelMaxLines` / `descMaxLines` 預設 `'none'`,Clamp 政策 SSOT 見 `checkbox.spec.md`「Clamp 政策」)。
|
|
113
113
|
- **icon / avatar prefix**:`RadioGroupItem` 一級 `icon` / `avatar` props 轉發 `SelectionItem` prefix 槽(2026-06-12 M30;規則 SSOT 見 `checkbox.spec.md` 同段 + `selection-item.spec.md`)。
|
|
114
|
-
- **RTL
|
|
114
|
+
- **RTL**:不支援;全域 LTR-only compatibility contract 見 `packages/design-system/README.md#compatibility-matrix`。
|
|
115
115
|
- **Dark mode / density**:走 SelectionItem semantic token 自動 adapt;垂直行距由 SelectionItem `py = (field-height − 1lh) / 2` 公式消費 uiSize token(`--field-height-*`)生成,隨 density 自動調整(零外部 gap 機制 SSOT 見 `checkbox.spec.md`「群組模式」)。
|
|
116
116
|
|
|
117
117
|
---
|
|
@@ -84,8 +84,8 @@ export const UsageGuidance: Story = {
|
|
|
84
84
|
<Label>✅ `aria-label="平均評分 4.5 星,共 5 星"`</Label>
|
|
85
85
|
</div>
|
|
86
86
|
<div className="flex flex-col gap-2 w-[320px] p-4 border border-border rounded-md">
|
|
87
|
-
<Rating value={4.5} readOnly precision="half" size="md" />
|
|
88
|
-
<Label warn>❌
|
|
87
|
+
<Rating value={4.5} readOnly precision="half" size="md" aria-label="平均評分 4.5 星,共 5 星" />
|
|
88
|
+
<Label warn>❌ 若省略 aria-label,螢幕閱讀器會讀不出分數;此可執行示範仍保留名稱,避免 demo 本身成為違規來源</Label>
|
|
89
89
|
</div>
|
|
90
90
|
</Rule>
|
|
91
91
|
|
|
@@ -140,7 +140,7 @@ export const Overview: Story = {
|
|
|
140
140
|
['className', 'string', '—', '傳入容器 className(常見:設定 height / border / rounded)'],
|
|
141
141
|
['children', 'ReactNode', '—', '要被捲動的內容'],
|
|
142
142
|
['type', "'auto'|'always'|'scroll'|'hover'", "'hover'", 'Radix:scrollbar 顯示時機(hover = 游標進入才顯示)'],
|
|
143
|
-
['dir', "'ltr'
|
|
143
|
+
['dir', "'ltr' only", "'ltr'", '全域 compatibility contract 為 LTR-only;不宣稱 RTL 覆蓋'],
|
|
144
144
|
['scrollHideDelay', 'number', '600', 'type=scroll / hover 時 scrollbar 隱藏前的延遲(ms)'],
|
|
145
145
|
].map(([p, t, d, desc]) => (
|
|
146
146
|
<tr key={p}><Td mono>{p}</Td><Td mono>{t}</Td><Td mono>{d}</Td><Td>{desc}</Td></tr>
|
|
@@ -16,7 +16,7 @@ ScrollArea 是**跨 OS 視覺一致的自訂捲動容器**——用 overlay 捲
|
|
|
16
16
|
|
|
17
17
|
**Layout Family**:非上述 family — self-contained primitive(純捲動容器,無 slot 結構)。
|
|
18
18
|
|
|
19
|
-
**實作基礎**:基於 Radix ScrollArea primitive(shadcn passthrough 結構)——由 Radix 處理 viewport / scrollbar / thumb 的 pointer 拖曳與 overflow-detection,本 DS 橋接 token(scrollbar 寬度、thumb bg、hover 反饋)與 a11y 行為(Viewport `tabIndex={0}` 預設 + `viewportTabIndex={-1}` opt-out + DS focus ring;Radix 不處理 keyboard,亦不自動標 Viewport focusable)。
|
|
19
|
+
**實作基礎**:基於 Radix ScrollArea primitive(shadcn passthrough 結構)——由 Radix 處理 viewport / scrollbar / thumb 的 pointer 拖曳與 overflow-detection,本 DS 橋接 token(scrollbar 寬度、thumb bg、hover 反饋)與 a11y 行為(Viewport `tabIndex={0}` 預設 + `viewportTabIndex={-1}` 雙 tab-stop opt-out + `viewportTabIndex={null}` ARIA composite wrapper opt-out + DS focus ring;Radix 不處理 keyboard,亦不自動標 Viewport focusable)。
|
|
20
20
|
|
|
21
21
|
**世界級對照**:shadcn `ScrollArea` / Radix primitive 本身。Polaris / Carbon 未提供等效元件(預設相信 native scrollbar);我們採用 shadcn / Radix 派:為了跨 OS 視覺一致、避免 DataTable / Sheet / Dialog 遇 Windows always-visible scrollbar 吃寬度跑版。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
|
|
22
22
|
|
|
@@ -100,12 +100,12 @@ ScrollArea 用 Radix 自訂 overlay 捲軸 → **跨 OS 一致、不吃寬度、
|
|
|
100
100
|
|
|
101
101
|
Radix primitive + 本 DS a11y 橋接:
|
|
102
102
|
|
|
103
|
-
- **鍵盤捲動**:本 DS 在 Viewport 加 `tabIndex={0}`(預設)使其可被鍵盤聚焦(Radix 不自動把 scroll container 標 focusable,Safari 尤其需要;此即 axe `scrollable-region-focusable` fix)。聚焦後 `ArrowUp/Down/Left/Right` / `PageUp/Down` / `Home/End` 捲動為瀏覽器原生行為,非 Radix 提供。**`viewportTabIndex={-1}` opt-out(2026-07-14)**:內容已有自帶 focusable + 鍵盤處理的元素(如 TimePicker `role="listbox"` `tabIndex={0}`)時傳 `-1`,消除「Viewport+內容雙 tab stop、焦點落 Viewport 時方向鍵觸發原生捲動非元件行為」;axe 由內部 focusable
|
|
103
|
+
- **鍵盤捲動**:本 DS 在 Viewport 加 `tabIndex={0}`(預設)使其可被鍵盤聚焦(Radix 不自動把 scroll container 標 focusable,Safari 尤其需要;此即 axe `scrollable-region-focusable` fix)。聚焦後 `ArrowUp/Down/Left/Right` / `PageUp/Down` / `Home/End` 捲動為瀏覽器原生行為,非 Radix 提供。**`viewportTabIndex={-1}` opt-out(2026-07-14)**:內容已有自帶 focusable + 鍵盤處理的元素(如 TimePicker `role="listbox"` `tabIndex={0}`)時傳 `-1`,消除「Viewport+內容雙 tab stop、焦點落 Viewport 時方向鍵觸發原生捲動非元件行為」;axe 由內部 focusable 內容滿足。**`viewportTabIndex={null}` composite opt-out(2026-07-31)**:ScrollArea 被 `role="menu"` 等 ARIA composite 當純結構 wrapper、鍵盤與 focus 由其 item family own 時,完全省略 viewport `tabindex`;`-1` 仍會留下 `div[tabindex]`,不能滿足 composite 的 required-children 結構
|
|
104
104
|
- **Focus 可見**:聚焦的 Viewport 顯示 DS focus ring(`focus-visible:outline-ring`,inset 2px),非瀏覽器原生 focus ring
|
|
105
105
|
- **Scrollbar 非 tab stop**:scrollbar thumb 不搶焦點,使用鍵盤的使用者透過 viewport 捲動(Radix 內建)
|
|
106
106
|
- **Pointer 支援**:thumb 可拖曳,track 可 click-to-jump(Radix 內建)
|
|
107
107
|
|
|
108
|
-
本元件已內建 `tabIndex`(預設 0;`viewportTabIndex={-1}`
|
|
108
|
+
本元件已內建 `tabIndex`(預設 0;`viewportTabIndex={-1}` / `{null}` 依上述兩種 owner 情境 opt-out)+ focus ring(滿足 axe `scrollable-region-focusable`)。但 Viewport 預設**無 `role` / accessible name**——**若 scroll 區域有具體語意(如「留言列表」「程式碼區塊」),consumer 應提供 `aria-label`**,否則 SR 只報「可捲動區域」無內容描述;純視覺裝飾容器可省略。(2026-05-31 #31:修正原「consumer 無需額外處理 a11y」過度宣稱)
|
|
109
109
|
|
|
110
110
|
---
|
|
111
111
|
|
|
@@ -43,9 +43,10 @@ const ScrollArea = React.forwardRef<
|
|
|
43
43
|
* 場景勿開 — table wrapper 是其 min-content 溢出機制)。 */
|
|
44
44
|
fillX?: boolean
|
|
45
45
|
/** Viewport tabIndex(預設 0 = axe scrollable-region-focusable fix)。內容已有自帶
|
|
46
|
-
* focusable 元素(如 TimePicker role=listbox tabIndex=0)時傳 -1 消除雙 tab stop
|
|
47
|
-
*
|
|
48
|
-
|
|
46
|
+
* focusable 元素(如 TimePicker role=listbox tabIndex=0)時傳 -1 消除雙 tab stop。
|
|
47
|
+
* 當 ScrollArea 是 ARIA composite(role=menu 等)的純結構 wrapper 時傳 null,完全省略
|
|
48
|
+
* tabindex,避免在 composite 直屬樹插入不合法的可聚焦 generic div。 */
|
|
49
|
+
viewportTabIndex?: 0 | -1 | null
|
|
49
50
|
}
|
|
50
51
|
>(({ className, children, fillX, viewportTabIndex = 0, ...props }, ref) => (
|
|
51
52
|
<ScrollAreaPrimitive.Root
|
|
@@ -63,7 +64,7 @@ const ScrollArea = React.forwardRef<
|
|
|
63
64
|
才能 keyboard focus(Safari 不自動把 scroll container 標 focusable)。focus-
|
|
64
65
|
visible:outline 用 DS focus ring,不破壞視覺。 */}
|
|
65
66
|
<ScrollAreaPrimitive.Viewport
|
|
66
|
-
tabIndex={viewportTabIndex}
|
|
67
|
+
tabIndex={viewportTabIndex ?? undefined}
|
|
67
68
|
className={cn(
|
|
68
69
|
'flex-1 min-h-0 w-full rounded-[inherit] focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring',
|
|
69
70
|
fillX && '[&>div]:!block [&>div]:!w-full [&>div]:!min-w-0',
|
|
@@ -7,6 +7,7 @@ import LinkTo from '@storybook/addon-links/react'
|
|
|
7
7
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
8
8
|
import { Flag } from 'lucide-react'
|
|
9
9
|
import { Select } from './select'
|
|
10
|
+
import { Field, FieldLabel } from '@/design-system/components/Field/field'
|
|
10
11
|
|
|
11
12
|
const meta: Meta = {
|
|
12
13
|
title: 'Design System/Components/Select/設計原則',
|
|
@@ -216,10 +217,10 @@ export const ImmediateVsSubmitRule: Story = {
|
|
|
216
217
|
note="建立/編輯表單、對話框設定。onChange 寫進 React state,直到 submit button 被按才送出。有「取消」可回復"
|
|
217
218
|
>
|
|
218
219
|
<div className="border border-border rounded-lg p-4 space-y-3">
|
|
219
|
-
<
|
|
220
|
-
<
|
|
220
|
+
<Field>
|
|
221
|
+
<FieldLabel>Category</FieldLabel>
|
|
221
222
|
<Select options={categoryOptions} value={draft} onChange={setDraft} />
|
|
222
|
-
</
|
|
223
|
+
</Field>
|
|
223
224
|
<div className="flex gap-2 pt-2">
|
|
224
225
|
<button className="h-field-md px-3 text-body rounded-md bg-primary text-inverse-fg">儲存</button>
|
|
225
226
|
<button className="h-field-md px-3 text-body rounded-md border border-border">取消</button>
|
|
@@ -6,6 +6,7 @@ import type { Meta, StoryObj } from '@storybook/react'
|
|
|
6
6
|
import { createColumnHelper } from '@tanstack/react-table'
|
|
7
7
|
import { Select } from './select'
|
|
8
8
|
import { Button } from '@/design-system/components/Button/button'
|
|
9
|
+
import { Field, FieldLabel } from '@/design-system/components/Field/field'
|
|
9
10
|
import { DataTable } from '@/design-system/components/DataTable/data-table'
|
|
10
11
|
import '@/design-system/components/DataTable/column-types'
|
|
11
12
|
|
|
@@ -73,12 +74,12 @@ export const HugWidth: Story = {
|
|
|
73
74
|
return (
|
|
74
75
|
// 任務詳情右欄場景:label 上、hug 寬 select 下 — 框線互動與一般 field 完全相同,只有寬度依 value 收縮
|
|
75
76
|
<div className="flex w-72 flex-col gap-[var(--layout-space-loose)]">
|
|
76
|
-
<
|
|
77
|
-
<
|
|
77
|
+
<Field>
|
|
78
|
+
<FieldLabel>Status</FieldLabel>
|
|
78
79
|
<Select width="hug" size="sm" options={statusOptions} value={status} onChange={setStatus} aria-label="Status(hug width demo)" />
|
|
79
|
-
</
|
|
80
|
-
<
|
|
81
|
-
<
|
|
80
|
+
</Field>
|
|
81
|
+
<Field>
|
|
82
|
+
<FieldLabel>Priority</FieldLabel>
|
|
82
83
|
<Select
|
|
83
84
|
width="hug"
|
|
84
85
|
size="sm"
|
|
@@ -91,7 +92,7 @@ export const HugWidth: Story = {
|
|
|
91
92
|
onChange={setPriority}
|
|
92
93
|
aria-label="Priority(hug width demo)"
|
|
93
94
|
/>
|
|
94
|
-
</
|
|
95
|
+
</Field>
|
|
95
96
|
</div>
|
|
96
97
|
)
|
|
97
98
|
},
|
|
@@ -9,6 +9,9 @@ import type { FieldMode, FieldVariant, FieldVariantInternal, FieldWidth } from '
|
|
|
9
9
|
import { fieldWrapperStyles, bareInputStyles, nakedCellRowModeAlign, fieldDisplayTextClass } from '@/design-system/components/Field/field-wrapper'
|
|
10
10
|
import { Tag } from '@/design-system/components/Tag/tag'
|
|
11
11
|
import { ItemInlineAction, ItemPrefix, ItemSuffix } from '@/design-system/patterns/element-anatomy/item-anatomy'
|
|
12
|
+
import { TruncatedText } from '@/design-system/patterns/element-anatomy/truncated-text'
|
|
13
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '@/design-system/components/Tooltip/tooltip'
|
|
14
|
+
import { useTruncated } from '@/design-system/hooks/use-truncated'
|
|
12
15
|
import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid, useFieldEmptyDisplay, fieldEmptyColorClass } from '@/design-system/components/Field/field-context'
|
|
13
16
|
import { SelectMenu, forwardKeyToListbox, useActiveDescendant, type SelectMenuOption } from '@/design-system/components/SelectMenu/select-menu'
|
|
14
17
|
import { useIsTouchDevice } from '@/design-system/hooks/use-is-touch-device'
|
|
@@ -242,6 +245,7 @@ function CustomSelectTriggerContent({
|
|
|
242
245
|
ariaLabel,
|
|
243
246
|
labelId,
|
|
244
247
|
selectedItemRenderer,
|
|
248
|
+
truncationRef,
|
|
245
249
|
}: {
|
|
246
250
|
searchable: boolean
|
|
247
251
|
open: boolean
|
|
@@ -261,6 +265,8 @@ function CustomSelectTriggerContent({
|
|
|
261
265
|
ariaLabel?: string
|
|
262
266
|
labelId?: string
|
|
263
267
|
selectedItemRenderer?: (selectedOpt: SelectOption) => React.ReactNode
|
|
268
|
+
/** 截斷 tooltip 量測點(text display 的值 span;tooltip trigger 在外層 combobox host,見 CustomSelect triggerTruncationRef 註解)*/
|
|
269
|
+
truncationRef?: React.Ref<HTMLSpanElement>
|
|
264
270
|
}): React.ReactNode {
|
|
265
271
|
// Searchable + open: 顯示搜尋 input
|
|
266
272
|
// 2026-05-15 Bug 2 fix(Claude+Codex Step 5 比稿 consensus,user verbatim「就 A」):
|
|
@@ -332,7 +338,7 @@ function CustomSelectTriggerContent({
|
|
|
332
338
|
<>
|
|
333
339
|
{StartIcon && <ItemPrefix><StartIcon size={iconSize} className="text-fg-muted pointer-events-none" aria-hidden /></ItemPrefix>}
|
|
334
340
|
{!StartIcon && SelectedIcon && value && <ItemPrefix><SelectedIcon size={iconSize} className="pointer-events-none" aria-hidden /></ItemPrefix>}
|
|
335
|
-
<span className={cn('flex-1 min-w-0 truncate', !value && 'text-fg-muted')}>
|
|
341
|
+
<span ref={truncationRef} className={cn('flex-1 min-w-0 truncate', !value && 'text-fg-muted')}>
|
|
336
342
|
{value ? selectedLabel : (placeholder ?? '選擇…')}
|
|
337
343
|
</span>
|
|
338
344
|
</>
|
|
@@ -399,7 +405,9 @@ function ReadonlyDisplay({
|
|
|
399
405
|
</span>
|
|
400
406
|
)
|
|
401
407
|
}
|
|
402
|
-
|
|
408
|
+
// 截斷必附 tooltip(tooltip.spec.md:32)— 改消費 truncated-text primitive(同 input.tsx:196 同型修;
|
|
409
|
+
// display='tag' 路徑由 Tag 內建 useTruncated 自管,text 路徑原本裸 truncate = gap)
|
|
410
|
+
if (isTextDisplay) return <TruncatedText className={cn(fieldDisplayTextClass(sz), className)}>{label}</TruncatedText>
|
|
403
411
|
const tVariant = selectedOpt?.tagVariant as 'blue' | 'green' | 'red' | 'yellow' | 'neutral' | undefined
|
|
404
412
|
return <Tag size={sz} color={tVariant} className={className}>{label}</Tag>
|
|
405
413
|
}
|
|
@@ -416,9 +424,10 @@ function ReadonlyDisplay({
|
|
|
416
424
|
{selectedItemRenderer(selectedOpt)}
|
|
417
425
|
</span>
|
|
418
426
|
) : isTextDisplay ? (
|
|
419
|
-
|
|
427
|
+
// 截斷必附 tooltip(tooltip.spec.md:32;data-table.spec.md:183 view 態元件自管 truncation)
|
|
428
|
+
<TruncatedText className={cn(bareInputStyles, 'flex-1 min-w-0', !value && emptyColorCls)}>
|
|
420
429
|
{value ? label : emptyText}
|
|
421
|
-
</
|
|
430
|
+
</TruncatedText>
|
|
422
431
|
) : value ? (
|
|
423
432
|
<Tag size={sz} color={tVariant}>{label}</Tag>
|
|
424
433
|
) : (
|
|
@@ -457,9 +466,10 @@ function ReadonlyDisplay({
|
|
|
457
466
|
return (
|
|
458
467
|
<div className={cn(fieldWrapperStyles({ mode: resolvedMode, variant, width, size: sz }), className)} data-field-mode={resolvedMode} aria-disabled={ariaDisabled}>
|
|
459
468
|
{StartIcon && <ItemPrefix><StartIcon size={iconSize} className={cn('pointer-events-none', iconColor)} aria-hidden /></ItemPrefix>}
|
|
460
|
-
|
|
469
|
+
{/* 截斷必附 tooltip(tooltip.spec.md:32)— readonly/disabled 無下拉入口,tooltip 是唯一補救路徑 */}
|
|
470
|
+
<TruncatedText className={cn('flex-1', resolvedMode === 'disabled' && 'text-fg-disabled')}>
|
|
461
471
|
{value ? label : <span className={emptyColorCls}>{emptyText}</span>}
|
|
462
|
-
</
|
|
472
|
+
</TruncatedText>
|
|
463
473
|
{showIndicator && <ItemSuffix className="pointer-events-none"><ChevronDown size={iconSize} className={cn('shrink-0', iconColor)} aria-hidden /></ItemSuffix>}
|
|
464
474
|
</div>
|
|
465
475
|
)
|
|
@@ -631,6 +641,17 @@ const CustomSelect = React.forwardRef<HTMLDivElement, SelectProps>(
|
|
|
631
641
|
// 級 fallback `?? value`。
|
|
632
642
|
const selectedLabel = selectedOpt?.label ?? value ?? ''
|
|
633
643
|
const SelectedIcon = selectedOpt?.icon
|
|
644
|
+
// 截斷 tooltip(tooltip.spec.md:32 截斷 → 顯完整內容,僅實際截斷時):closed trigger text
|
|
645
|
+
// display 的值 span 是截斷元素;trigger = 整個 combobox host(interactive host,hover 直達)、
|
|
646
|
+
// 量測內層值 span — useTruncated 自組 pattern(truncated-text.spec.md「trigger 需自控」指定解;
|
|
647
|
+
// 範本 inline-edit.tsx:251。tag display 由 Tag 內建 useTruncated 自管,不經此路)。
|
|
648
|
+
// deps 含 open:searchable && open 切搜尋 input branch 時值 span 會 unmount,關閉重掛後
|
|
649
|
+
// effect 需重跑重新 observe(Tag always-wrap 教訓:RO 卡在 detached node → isTruncated 凍結)。
|
|
650
|
+
// hook 必在下方 resolvedMode early return 之前(React #310 hook-count 鐵律,見上方註解)。
|
|
651
|
+
const { ref: triggerTruncationRef, isTruncated: triggerTruncated } = useTruncated<HTMLSpanElement>({
|
|
652
|
+
deps: [open, isTextDisplay && value ? selectedLabel : ''],
|
|
653
|
+
})
|
|
654
|
+
const triggerTooltipActive = isTextDisplay && !!value && triggerTruncated && !open
|
|
634
655
|
// ── 過濾選項 ──
|
|
635
656
|
// 2026-07-18:filter 用 trim 過的 search,對齊 SelectMenu creatable 的 `search.trim()` create-row 判定 —
|
|
636
657
|
// 否則尾隨空白(如 "Bug ")會讓 filter 漏掉完全同名選項、SelectMenu 卻誤判「無同名」提議重複建立。
|
|
@@ -748,6 +769,7 @@ const CustomSelect = React.forwardRef<HTMLDivElement, SelectProps>(
|
|
|
748
769
|
ariaLabel={ariaLabel}
|
|
749
770
|
labelId={fieldCtx?.labelId}
|
|
750
771
|
selectedItemRenderer={selectedItemRenderer}
|
|
772
|
+
truncationRef={triggerTruncationRef}
|
|
751
773
|
/>
|
|
752
774
|
)
|
|
753
775
|
|
|
@@ -828,29 +850,39 @@ const CustomSelect = React.forwardRef<HTMLDivElement, SelectProps>(
|
|
|
828
850
|
)
|
|
829
851
|
|
|
830
852
|
return (
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
853
|
+
// 截斷 tooltip:恆 wrap(truncated-text.spec.md always-wrap),未截斷或選單開啟時 open={false}
|
|
854
|
+
// 靜默。TooltipTrigger 放 SelectMenu 的 PopoverTrigger asChild 內側(緊貼 trigger div):
|
|
855
|
+
// PopoverTrigger 注入的 data-state(Field open 邊框樣式依賴)在 TooltipTrigger 內以
|
|
856
|
+
// triggerProps 後 spread 勝出;反序會被 tooltip 的 data-state 蓋掉。Tooltip root / Content
|
|
857
|
+
// 都不產 DOM(Content portal),SelectMenu children 結構與佈局不變。
|
|
858
|
+
<Tooltip open={triggerTooltipActive ? undefined : false}>
|
|
859
|
+
<SelectMenu
|
|
860
|
+
options={menuOptions}
|
|
861
|
+
groups={groups}
|
|
862
|
+
value={value ?? null}
|
|
863
|
+
onValueChange={handleValueChange}
|
|
864
|
+
searchable={false}
|
|
865
|
+
creatable={creatable}
|
|
866
|
+
onCreate={onCreate}
|
|
867
|
+
createLabel={createLabel}
|
|
868
|
+
search={searchable ? search : undefined}
|
|
869
|
+
onSearchChange={searchable ? setSearch : undefined}
|
|
870
|
+
loading={loading}
|
|
871
|
+
emptyText={emptyText}
|
|
872
|
+
size={size}
|
|
873
|
+
minRows={minRows}
|
|
874
|
+
open={open}
|
|
875
|
+
onOpenChange={(o) => { setOpen(o); onOpenChange?.(o) }}
|
|
876
|
+
contentId={listboxId}
|
|
877
|
+
renderLabel={renderLabel}
|
|
878
|
+
onOpenAutoFocus={searchable ? (e) => { e.preventDefault(); inputRef.current?.focus() } : undefined}
|
|
879
|
+
>
|
|
880
|
+
<TooltipTrigger asChild>
|
|
881
|
+
{trigger}
|
|
882
|
+
</TooltipTrigger>
|
|
883
|
+
</SelectMenu>
|
|
884
|
+
<TooltipContent>{triggerTooltipActive ? selectedLabel : null}</TooltipContent>
|
|
885
|
+
</Tooltip>
|
|
854
886
|
)
|
|
855
887
|
}
|
|
856
888
|
)
|
|
@@ -213,7 +213,7 @@ SelectMenu 是 **composite**(Popover trigger + Command search + 滾動 MenuItem
|
|
|
213
213
|
|
|
214
214
|
## A11y 預設
|
|
215
215
|
|
|
216
|
-
**ARIA / Pattern**:基於 `cmdk` library a11y(combobox / listbox / option role + aria-activedescendant)。詳 [cmdk a11y](https://cmdk.paco.me/#accessibility)。選項 row 的內層 `MenuItem` 傳 `role="presentation"`(cmdk CommandItem 是唯一 option 節點,避免 option 巢狀 option + 內外 `aria-selected` 語意相反;鏡射 DropdownMenu canonical,2026-07-05 D4)。分組標題走 cmdk `CommandGroup heading`(自動產 `cmdk-group-heading` id,選項容器 `role="group"` + `aria-labelledby` 指向之,AT 可感知);combobox accessible name 來自 `Command label`(= `
|
|
216
|
+
**ARIA / Pattern**:基於 `cmdk` library a11y(combobox / listbox / option role + aria-activedescendant)。詳 [cmdk a11y](https://cmdk.paco.me/#accessibility)。選項 row 的內層 `MenuItem` 傳 `role="presentation"`(cmdk CommandItem 是唯一 option 節點,避免 option 巢狀 option + 內外 `aria-selected` 語意相反;鏡射 DropdownMenu canonical,2026-07-05 D4)。分組標題走 cmdk `CommandGroup heading`(自動產 `cmdk-group-heading` id,選項容器 `role="group"` + `aria-labelledby` 指向之,AT 可感知);combobox accessible name 來自 `Command label`(= `searchAriaLabel`,default「搜尋選項」,僅 searchable 時傳),與可見 `searchPlaceholder` 分離;listbox 容器經 cmdk `List label` 預設「選項」取代 cmdk 內建英文 "Suggestions"(2026-07-06)。多選 footer 全選列為 `role="checkbox"` + `aria-checked`(indeterminate → `"mixed"`)。空狀態 / loading 經 visually-hidden `role="status"` + `aria-live="polite"` live region 對 SR 播報(cmdk CommandEmpty 是 `role="presentation"`,SR 原本聽不到;2026-07-05 D4)。
|
|
217
217
|
|
|
218
218
|
**Keyboard 行為**:
|
|
219
219
|
|
|
@@ -95,6 +95,8 @@ export interface SelectMenuProps {
|
|
|
95
95
|
children: React.ReactNode
|
|
96
96
|
/** 搜尋框 placeholder */
|
|
97
97
|
searchPlaceholder?: string
|
|
98
|
+
/** 搜尋框 accessible name;與可見 placeholder 分離。 */
|
|
99
|
+
searchAriaLabel?: string
|
|
98
100
|
/** 空選項提示 */
|
|
99
101
|
emptyText?: string
|
|
100
102
|
/** 多選 footer 全選列文字(2026-07-05 D4:原「全部」字面 hardcode,無法覆寫也無法 i18n) */
|
|
@@ -183,6 +185,7 @@ const SelectMenu = React.forwardRef<HTMLElement, SelectMenuProps>(function Selec
|
|
|
183
185
|
onSearchChange,
|
|
184
186
|
children,
|
|
185
187
|
searchPlaceholder = '搜尋…', // i18n-allow: DS default; consumer override via searchPlaceholder prop
|
|
188
|
+
searchAriaLabel = '搜尋選項', // i18n-allow: DS default; consumer override via searchAriaLabel prop
|
|
186
189
|
emptyText = '沒有符合的選項', // i18n-allow: DS default; consumer override via emptyText prop
|
|
187
190
|
selectAllLabel = '全部', // i18n-allow: DS default; consumer override via selectAllLabel prop
|
|
188
191
|
loading = false,
|
|
@@ -394,7 +397,7 @@ const SelectMenu = React.forwardRef<HTMLElement, SelectMenuProps>(function Selec
|
|
|
394
397
|
// 不存在的 input」仍存在(upstream cmdk 限制;空 label 無 accessible name,不產生朗讀
|
|
395
398
|
// 內容)。僅 searchable(真的有 input)時傳內容 = 避免 non-searchable 帶 search
|
|
396
399
|
// placeholder 的 accessible name 卻無對應 input;真要消除 orphan label 需 upstream 修。
|
|
397
|
-
label={searchable ?
|
|
400
|
+
label={searchable ? searchAriaLabel : undefined}
|
|
398
401
|
className="bg-transparent"
|
|
399
402
|
>
|
|
400
403
|
{searchable && (
|
|
@@ -83,7 +83,7 @@ export const Overview: Story = {
|
|
|
83
83
|
</Desc>
|
|
84
84
|
<div className="border border-divider rounded-md p-4 bg-muted max-w-xl">
|
|
85
85
|
<SelectionItem
|
|
86
|
-
control={<Checkbox defaultChecked />}
|
|
86
|
+
control={<Checkbox defaultChecked aria-label="讀取 email 信箱" />}
|
|
87
87
|
icon={Mail}
|
|
88
88
|
label="讀取 email 信箱"
|
|
89
89
|
description="僅用於通知,永遠不會外流"
|
|
@@ -246,7 +246,14 @@ function SelectionInspector() {
|
|
|
246
246
|
<div className="border border-divider rounded-md p-4 bg-muted">
|
|
247
247
|
<SelectionItem
|
|
248
248
|
size={size}
|
|
249
|
-
control={
|
|
249
|
+
control={
|
|
250
|
+
<Checkbox
|
|
251
|
+
size={size}
|
|
252
|
+
defaultChecked
|
|
253
|
+
disabled={disabled}
|
|
254
|
+
aria-label="Admin 層級操作"
|
|
255
|
+
/>
|
|
256
|
+
}
|
|
250
257
|
icon={prefix === 'icon' ? Shield : undefined}
|
|
251
258
|
avatar={prefix === 'avatar' ? { alt: 'Ada Chen', color: 'indigo' } : undefined}
|
|
252
259
|
label="Admin 層級操作"
|
|
@@ -438,7 +445,7 @@ export const PrefixAlignment: Story = {
|
|
|
438
445
|
</span>
|
|
439
446
|
<div className="border border-divider rounded-md p-4 bg-muted">
|
|
440
447
|
<SelectionItem
|
|
441
|
-
control={<Checkbox defaultChecked />}
|
|
448
|
+
control={<Checkbox defaultChecked aria-label="選取 Ada Chen(單行)" />}
|
|
442
449
|
avatar={{ src: 'https://i.pravatar.cc/48?u=ada-chen', alt: 'Ada Chen', color: 'indigo' }}
|
|
443
450
|
label="Ada Chen"
|
|
444
451
|
/>
|
|
@@ -450,7 +457,7 @@ export const PrefixAlignment: Story = {
|
|
|
450
457
|
</span>
|
|
451
458
|
<div className="border border-divider rounded-md p-4 bg-muted">
|
|
452
459
|
<SelectionItem
|
|
453
|
-
control={<Checkbox defaultChecked />}
|
|
460
|
+
control={<Checkbox defaultChecked aria-label="選取 Ada Chen(含職稱)" />}
|
|
454
461
|
avatar={{ src: 'https://i.pravatar.cc/64?u=ada-chen', alt: 'Ada Chen', color: 'indigo' }}
|
|
455
462
|
label="Ada Chen"
|
|
456
463
|
description="Design Engineer · Frontend team"
|
|
@@ -467,11 +474,15 @@ export const PrefixAlignment: Story = {
|
|
|
467
474
|
</Desc>
|
|
468
475
|
<div className="grid grid-cols-2 gap-6 max-w-4xl">
|
|
469
476
|
<div className="border border-divider rounded-md p-4 bg-muted">
|
|
470
|
-
<SelectionItem
|
|
477
|
+
<SelectionItem
|
|
478
|
+
control={<Checkbox defaultChecked aria-label="存取 Drive 檔案(單行)" />}
|
|
479
|
+
icon={Folder}
|
|
480
|
+
label="存取 Drive 檔案"
|
|
481
|
+
/>
|
|
471
482
|
</div>
|
|
472
483
|
<div className="border border-divider rounded-md p-4 bg-muted">
|
|
473
484
|
<SelectionItem
|
|
474
|
-
control={<Checkbox defaultChecked />}
|
|
485
|
+
control={<Checkbox defaultChecked aria-label="存取 Drive 檔案(含說明)" />}
|
|
475
486
|
icon={Folder}
|
|
476
487
|
label="存取 Drive 檔案"
|
|
477
488
|
description="讀取並修改你授權的資料夾"
|
|
@@ -538,13 +549,13 @@ export const SizeMatrix: Story = {
|
|
|
538
549
|
<div className="border border-divider rounded-md bg-muted flex flex-col">
|
|
539
550
|
<SelectionItem
|
|
540
551
|
size={s}
|
|
541
|
-
control={<Checkbox size={s} defaultChecked />}
|
|
552
|
+
control={<Checkbox size={s} defaultChecked aria-label={`${s} 尺寸產品更新電子報`} />}
|
|
542
553
|
icon={Mail}
|
|
543
554
|
label="產品更新電子報"
|
|
544
555
|
/>
|
|
545
556
|
<SelectionItem
|
|
546
557
|
size={s}
|
|
547
|
-
control={<Checkbox size={s} />}
|
|
558
|
+
control={<Checkbox size={s} aria-label={`${s} 尺寸安全性警告`} />}
|
|
548
559
|
icon={Shield}
|
|
549
560
|
label="安全性警告"
|
|
550
561
|
description="登入裝置變動、密碼變更等重要事件"
|
|
@@ -73,24 +73,24 @@ export const WithPrefixIcon: Story = {
|
|
|
73
73
|
Figma-like permission scope picker — control + icon(24px 閾值內 inline)+ label + description。
|
|
74
74
|
</p>
|
|
75
75
|
<SelectionItem
|
|
76
|
-
control={<Checkbox defaultChecked />}
|
|
76
|
+
control={<Checkbox defaultChecked aria-label="讀取 email 信箱" />}
|
|
77
77
|
icon={Mail}
|
|
78
78
|
label="讀取 email 信箱"
|
|
79
79
|
description="僅用於通知,永遠不會外流"
|
|
80
80
|
/>
|
|
81
81
|
<SelectionItem
|
|
82
|
-
control={<Checkbox />}
|
|
82
|
+
control={<Checkbox aria-label="存取 Drive 檔案" />}
|
|
83
83
|
icon={Folder}
|
|
84
84
|
label="存取 Drive 檔案"
|
|
85
85
|
description="讀取並修改你授權的資料夾"
|
|
86
86
|
/>
|
|
87
87
|
<SelectionItem
|
|
88
|
-
control={<Checkbox defaultChecked />}
|
|
88
|
+
control={<Checkbox defaultChecked aria-label="推播通知權限" />}
|
|
89
89
|
icon={Bell}
|
|
90
90
|
label="推播通知權限"
|
|
91
91
|
/>
|
|
92
92
|
<SelectionItem
|
|
93
|
-
control={<Checkbox />}
|
|
93
|
+
control={<Checkbox aria-label="Admin 層級操作" />}
|
|
94
94
|
icon={Shield}
|
|
95
95
|
label="Admin 層級操作"
|
|
96
96
|
description="僅限 workspace owner 開啟"
|
|
@@ -113,19 +113,19 @@ export const WithPrefixAvatarBlock: Story = {
|
|
|
113
113
|
role。
|
|
114
114
|
</p>
|
|
115
115
|
<SelectionItem
|
|
116
|
-
control={<Checkbox defaultChecked />}
|
|
116
|
+
control={<Checkbox defaultChecked aria-label="選取 Ada Chen" />}
|
|
117
117
|
avatar={{ src: 'https://i.pravatar.cc/64?u=ada-chen', alt: 'Ada Chen', color: 'indigo' }}
|
|
118
118
|
label="Ada Chen"
|
|
119
119
|
description="Design Engineer · Frontend team"
|
|
120
120
|
/>
|
|
121
121
|
<SelectionItem
|
|
122
|
-
control={<Checkbox />}
|
|
122
|
+
control={<Checkbox aria-label="選取張美真" />}
|
|
123
123
|
avatar={{ src: 'https://i.pravatar.cc/64?u=zhang-meizhen', alt: '張美真', color: 'magenta' }}
|
|
124
124
|
label="張美真"
|
|
125
125
|
description="Product Designer · Platform team"
|
|
126
126
|
/>
|
|
127
127
|
<SelectionItem
|
|
128
|
-
control={<Checkbox defaultChecked />}
|
|
128
|
+
control={<Checkbox defaultChecked aria-label="選取林伯彥" />}
|
|
129
129
|
avatar={{ src: 'https://i.pravatar.cc/64?u=lin-boyan', alt: '林伯彥', color: 'green' }}
|
|
130
130
|
label="林伯彥"
|
|
131
131
|
description="Staff Engineer · Infra team"
|
|
@@ -108,7 +108,7 @@ Sheet 為容器,內容由 consumer 決定;focus trap + Escape close + `aria-labe
|
|
|
108
108
|
- **Empty(no content)**:Sheet 必含 title(`aria-labelledby` 強制要求);body 為空時 consumer 應渲 `<Empty>` placeholder,不渲空白 panel。
|
|
109
109
|
- **Scroll(body 內容超過視口高度)**:`SheetBody` = `<ScrollArea>`(`flex-1 min-h-0`),超高內容在 body 內部捲動,header / footer 固定不動;consumer 不需自寫 `overflow-y-auto`。
|
|
110
110
|
- **誤嵌套降級(nested Sheet)**:禁止事項已禁 nested;若 consumer 不慎嵌套,Radix 的 Esc 與 focus trap 只作用於**最上層** layer(DismissableLayer 只 dismiss highest layer;FocusScope stack 最新 scope active),關閉由上而下逐層。
|
|
111
|
-
- **RTL
|
|
111
|
+
- **RTL**:不支援;全域 LTR-only compatibility contract 見 `packages/design-system/README.md#compatibility-matrix`。`side` 維持 physical direction。
|
|
112
112
|
- **Dark mode**:overlay-surface SSOT 自動 adapt(`overlay-surface.spec.md`);Sheet 不獨立 own dark token。
|
|
113
113
|
- **Density**:Sheet body density 由 consumer 控(若內含 form / DataTable / Menu 各自有 density 規則);Sheet container 自身不持 density。
|
|
114
114
|
|