@qijenchen/design-system 0.1.0-beta.85 → 0.1.0-beta.87
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/AGENTS.md +210 -0
- package/CLAUDE.md +17 -191
- package/cli-init.mjs +30 -2
- package/dist/components/Accordion/accordion.d.ts +2 -2
- package/dist/components/Accordion/accordion.d.ts.map +1 -1
- package/dist/components/Accordion/accordion.js.map +1 -1
- package/dist/components/Avatar/avatar.js +1 -1
- package/dist/components/Avatar/avatar.js.map +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.d.ts +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.d.ts.map +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.js +3 -3
- package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
- package/dist/components/Button/button-group.d.ts +2 -2
- package/dist/components/Button/button-group.d.ts.map +1 -1
- package/dist/components/Button/button-group.js +2 -2
- package/dist/components/Button/button-group.js.map +1 -1
- package/dist/components/Button/button.d.ts +2 -1
- package/dist/components/Button/button.d.ts.map +1 -1
- package/dist/components/Button/button.js +1 -1
- package/dist/components/Button/button.js.map +1 -1
- package/dist/components/Carousel/carousel.d.ts +14 -8
- package/dist/components/Carousel/carousel.d.ts.map +1 -1
- package/dist/components/Carousel/carousel.js +64 -37
- package/dist/components/Carousel/carousel.js.map +1 -1
- package/dist/components/Checkbox/checkbox-group.d.ts.map +1 -1
- package/dist/components/Checkbox/checkbox-group.js +21 -14
- package/dist/components/Checkbox/checkbox-group.js.map +1 -1
- package/dist/components/Checkbox/checkbox.d.ts +2 -2
- package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox/checkbox.js +49 -10
- package/dist/components/Checkbox/checkbox.js.map +1 -1
- package/dist/components/Combobox/combobox.d.ts +15 -6
- package/dist/components/Combobox/combobox.d.ts.map +1 -1
- package/dist/components/Combobox/combobox.js +13 -7
- 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 +11 -2
- package/dist/components/Command/command.js.map +1 -1
- package/dist/components/DataTable/cell-registry.d.ts +5 -5
- package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
- package/dist/components/DataTable/cell-registry.js +41 -38
- package/dist/components/DataTable/cell-registry.js.map +1 -1
- package/dist/components/DataTable/column-types.d.ts +1 -17
- package/dist/components/DataTable/column-types.d.ts.map +1 -1
- package/dist/components/DataTable/column-types.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 +71 -64
- package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -1
- package/dist/components/DataTable/data-table-filter-panel.d.ts +0 -2
- package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-panel.js +3 -4
- package/dist/components/DataTable/data-table-filter-panel.js.map +1 -1
- package/dist/components/DataTable/data-table-interaction-layer.d.ts +2 -2
- package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
- package/dist/components/DataTable/data-table-sort-manager.js +2 -2
- package/dist/components/DataTable/data-table-sort-manager.js.map +1 -1
- package/dist/components/DataTable/data-table.d.ts +1 -1
- package/dist/components/DataTable/data-table.d.ts.map +1 -1
- package/dist/components/DataTable/data-table.js +44 -32
- package/dist/components/DataTable/data-table.js.map +1 -1
- package/dist/components/DatePicker/date-picker.d.ts +3 -3
- package/dist/components/DatePicker/date-picker.js +4 -4
- package/dist/components/DatePicker/date-picker.js.map +1 -1
- package/dist/components/DescriptionList/description-list.d.ts +3 -3
- package/dist/components/DescriptionList/description-list.d.ts.map +1 -1
- package/dist/components/DescriptionList/description-list.js +6 -6
- package/dist/components/DescriptionList/description-list.js.map +1 -1
- package/dist/components/Dialog/dialog.d.ts +2 -2
- package/dist/components/Dialog/dialog.d.ts.map +1 -1
- package/dist/components/Dialog/dialog.js.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts +8 -3
- package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.js +7 -7
- package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
- package/dist/components/Field/field-context.d.ts +16 -10
- package/dist/components/Field/field-context.d.ts.map +1 -1
- package/dist/components/Field/field-context.js.map +1 -1
- package/dist/components/Field/field-types.d.ts +2 -2
- package/dist/components/Field/field-types.d.ts.map +1 -1
- package/dist/components/Field/field-types.js.map +1 -1
- package/dist/components/Field/field-wrapper.d.ts +20 -4
- package/dist/components/Field/field-wrapper.d.ts.map +1 -1
- package/dist/components/Field/field-wrapper.js +30 -31
- package/dist/components/Field/field-wrapper.js.map +1 -1
- package/dist/components/Field/field.d.ts +1 -1
- package/dist/components/Field/field.d.ts.map +1 -1
- package/dist/components/Field/field.js +25 -3
- package/dist/components/Field/field.js.map +1 -1
- package/dist/components/Field/use-form-validation.d.ts +1 -1
- package/dist/components/Field/use-form-validation.js.map +1 -1
- package/dist/components/FileItem/file-item.d.ts.map +1 -1
- package/dist/components/FileItem/file-item.js +2 -5
- package/dist/components/FileItem/file-item.js.map +1 -1
- package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
- package/dist/components/FileUpload/file-upload.js +5 -1
- package/dist/components/FileUpload/file-upload.js.map +1 -1
- package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
- package/dist/components/FileViewer/file-viewer.js +2 -1
- package/dist/components/FileViewer/file-viewer.js.map +1 -1
- package/dist/components/HoverCard/hover-card.d.ts +2 -2
- package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
- package/dist/components/HoverCard/hover-card.js +4 -2
- package/dist/components/HoverCard/hover-card.js.map +1 -1
- package/dist/components/InlineEdit/inline-edit.d.ts +23 -16
- package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -1
- package/dist/components/InlineEdit/inline-edit.js +104 -62
- package/dist/components/InlineEdit/inline-edit.js.map +1 -1
- package/dist/components/Input/input.d.ts +1 -1
- package/dist/components/Input/input.d.ts.map +1 -1
- package/dist/components/Input/input.js +6 -6
- package/dist/components/Input/input.js.map +1 -1
- package/dist/components/LinkInput/link-input.d.ts +5 -5
- package/dist/components/LinkInput/link-input.d.ts.map +1 -1
- package/dist/components/LinkInput/link-input.js +3 -3
- package/dist/components/LinkInput/link-input.js.map +1 -1
- package/dist/components/Notice/notice.d.ts +17 -1
- package/dist/components/Notice/notice.d.ts.map +1 -1
- package/dist/components/Notice/notice.js +7 -1
- package/dist/components/Notice/notice.js.map +1 -1
- package/dist/components/NumberInput/number-input.d.ts +1 -1
- package/dist/components/NumberInput/number-input.d.ts.map +1 -1
- package/dist/components/NumberInput/number-input.js +12 -1
- package/dist/components/NumberInput/number-input.js.map +1 -1
- package/dist/components/PeoplePicker/avatar-stack-overflow.d.ts +1 -1
- package/dist/components/PeoplePicker/avatar-stack-overflow.js.map +1 -1
- package/dist/components/PeoplePicker/people-picker.d.ts +5 -5
- package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
- package/dist/components/PeoplePicker/people-picker.js +7 -5
- package/dist/components/PeoplePicker/people-picker.js.map +1 -1
- package/dist/components/PeoplePicker/person-display.d.ts +1 -1
- package/dist/components/PeoplePicker/person-display.js.map +1 -1
- package/dist/components/ProfileCard/profile-card.d.ts +1 -1
- package/dist/components/ProfileCard/profile-card.js.map +1 -1
- package/dist/components/RadioGroup/radio-group.d.ts +3 -3
- package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
- package/dist/components/RadioGroup/radio-group.js +1 -1
- package/dist/components/RadioGroup/radio-group.js.map +1 -1
- package/dist/components/Rating/rating.d.ts.map +1 -1
- package/dist/components/Rating/rating.js +4 -0
- package/dist/components/Rating/rating.js.map +1 -1
- package/dist/components/ScrollArea/scroll-area.d.ts +3 -2
- package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
- package/dist/components/ScrollArea/scroll-area.js.map +1 -1
- package/dist/components/SegmentedControl/segmented-control.d.ts +4 -3
- package/dist/components/SegmentedControl/segmented-control.d.ts.map +1 -1
- package/dist/components/SegmentedControl/segmented-control.js.map +1 -1
- package/dist/components/Select/select.d.ts +16 -6
- package/dist/components/Select/select.d.ts.map +1 -1
- package/dist/components/Select/select.js +17 -5
- package/dist/components/Select/select.js.map +1 -1
- package/dist/components/SelectMenu/select-menu.d.ts +17 -1
- package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
- package/dist/components/SelectMenu/select-menu.js +17 -3
- package/dist/components/SelectMenu/select-menu.js.map +1 -1
- package/dist/components/SelectionControl/boolean-value.d.ts +1 -1
- package/dist/components/SelectionControl/boolean-value.js.map +1 -1
- package/dist/components/Sheet/sheet.d.ts +2 -2
- package/dist/components/Sheet/sheet.d.ts.map +1 -1
- package/dist/components/Sheet/sheet.js.map +1 -1
- package/dist/components/Sidebar/sidebar.d.ts +1 -1
- package/dist/components/Sidebar/sidebar.js +2 -2
- package/dist/components/Sidebar/sidebar.js.map +1 -1
- package/dist/components/Slider/slider.d.ts.map +1 -1
- package/dist/components/Slider/slider.js +6 -3
- package/dist/components/Slider/slider.js.map +1 -1
- package/dist/components/Steps/steps.d.ts +6 -4
- package/dist/components/Steps/steps.d.ts.map +1 -1
- package/dist/components/Steps/steps.js +19 -5
- package/dist/components/Steps/steps.js.map +1 -1
- package/dist/components/Switch/switch.d.ts +4 -4
- package/dist/components/Switch/switch.d.ts.map +1 -1
- package/dist/components/Switch/switch.js +3 -1
- 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 +73 -23
- package/dist/components/Tabs/tabs.js.map +1 -1
- package/dist/components/Tag/tag.d.ts +3 -1
- package/dist/components/Tag/tag.d.ts.map +1 -1
- package/dist/components/Tag/tag.js +2 -2
- package/dist/components/Tag/tag.js.map +1 -1
- package/dist/components/Textarea/textarea.d.ts +4 -4
- package/dist/components/Textarea/textarea.d.ts.map +1 -1
- package/dist/components/Textarea/textarea.js +15 -15
- package/dist/components/Textarea/textarea.js.map +1 -1
- package/dist/components/TimePicker/time-picker.d.ts +4 -4
- package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
- package/dist/components/TimePicker/time-picker.js +8 -5
- package/dist/components/TimePicker/time-picker.js.map +1 -1
- package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
- package/dist/components/Tooltip/tooltip.js +27 -23
- package/dist/components/Tooltip/tooltip.js.map +1 -1
- package/dist/components/TreeView/tree-view.d.ts.map +1 -1
- package/dist/components/TreeView/tree-view.js +16 -0
- package/dist/components/TreeView/tree-view.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -13
- package/dist/index.js.map +1 -1
- package/dist/lib/drag-visual.d.ts +2 -2
- package/dist/lib/drag-visual.js.map +1 -1
- package/dist/lib/utils.d.ts +1 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +21 -1
- package/dist/lib/utils.js.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.d.ts +2 -0
- package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.js +2 -2
- package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
- package/dist/tokens/categorical-color.d.ts +3 -3
- package/dist/tokens/categorical-color.d.ts.map +1 -1
- package/dist/tokens/categorical-color.js +2 -2
- package/dist/tokens/categorical-color.js.map +1 -1
- package/ds-canonical/fork/AGENTS.md +213 -0
- package/ds-canonical/fork/codex/agents/skills/independent-review/SKILL.md +33 -0
- package/ds-canonical/fork/codex/hooks.json +16 -0
- package/ds-canonical/fork/governance.lock +22 -7
- package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +22 -1
- package/ds-canonical/fork/hooks/inject_deploy_url_after_push.sh +14 -2
- package/ds-canonical/fork/manifest.json +10 -1
- package/ds-canonical/fork/preamble.md +3 -2
- package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +7 -7
- package/ds-canonical/fork/skills/ux-audit/SKILL.md +1 -1
- package/ds-canonical/fork/skills/visual-audit/SKILL.md +0 -1
- package/ds-canonical/hooks/check_codex_brief_invariants.sh +9 -3
- package/ds-canonical/hooks/check_ds_anchor_preflight.sh +22 -1
- package/ds-canonical/hooks/check_overlay_open_focus_escape_probe.sh +19 -2
- package/ds-canonical/hooks/check_plugin_fork_health.sh +17 -1
- package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +21 -4
- package/ds-canonical/hooks/inject_deploy_url_after_push.sh +14 -2
- package/ds-canonical/hooks/session_start_governance_check.sh +10 -2
- package/ds-canonical/references/certified-surfaces.md +20 -0
- package/ds-canonical/references/preflight-gate-baseline.json +5 -0
- package/ds-canonical/references/principle-dim-map.json +20 -2
- package/ds-canonical/references/props-naming.md +2 -1
- package/ds-canonical/references/ssot-index.md +2 -2
- package/ds-canonical/references/story-baseline-registry.json +2 -2
- package/ds-canonical/rules/story-rules.md +1 -1
- package/ds-canonical/rules/ui-development.md +2 -1
- package/ds-canonical/skills/codex-collab/SKILL.md +7 -7
- package/ds-canonical/skills/codex-collab/references/brief-template.md +5 -1
- package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
- package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +5 -5
- package/ds-canonical/skills/design-system-audit/SKILL.md +5 -5
- package/ds-canonical/skills/product-ui-audit/SKILL.md +7 -7
- package/ds-canonical/skills/ux-audit/SKILL.md +1 -1
- package/ds-canonical/skills/visual-audit/SKILL.md +0 -1
- package/ds-story-manifest.json +21 -21
- package/llms-full.txt +3 -3
- package/llms.txt +1 -1
- package/package.json +2 -1
- package/src/components/Accordion/accordion.anatomy.stories.tsx +11 -11
- package/src/components/Accordion/accordion.principles.stories.tsx +21 -6
- package/src/components/Accordion/accordion.spec.md +10 -1
- package/src/components/Accordion/accordion.stories.tsx +10 -5
- package/src/components/Accordion/accordion.tsx +7 -2
- package/src/components/Alert/alert.principles.stories.tsx +2 -8
- package/src/components/AppShell/app-shell.anatomy.stories.tsx +19 -13
- package/src/components/AppShell/app-shell.principles.stories.tsx +21 -21
- package/src/components/AppShell/app-shell.spec.md +4 -0
- package/src/components/AppShell/app-shell.stories.tsx +4 -5
- package/src/components/Avatar/avatar.tsx +1 -1
- package/src/components/Badge/badge.spec.md +2 -2
- package/src/components/Breadcrumb/breadcrumb.anatomy.stories.tsx +7 -3
- package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +4 -1
- package/src/components/Breadcrumb/breadcrumb.stories.tsx +1 -1
- package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +6 -6
- package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +18 -6
- package/src/components/BulkActionBar/bulk-action-bar.spec.md +15 -5
- package/src/components/BulkActionBar/bulk-action-bar.stories.tsx +8 -24
- package/src/components/BulkActionBar/bulk-action-bar.tsx +4 -4
- package/src/components/Button/button-group.tsx +4 -4
- package/src/components/Button/button.anatomy.stories.tsx +26 -7
- package/src/components/Button/button.principles.stories.tsx +1 -1
- package/src/components/Button/button.spec.md +2 -2
- package/src/components/Button/button.stories.tsx +22 -28
- package/src/components/Button/button.tsx +2 -1
- package/src/components/Calendar/calendar.anatomy.stories.tsx +1 -1
- package/src/components/Calendar/calendar.spec.md +1 -1
- package/src/components/Carousel/carousel.spec.md +17 -1
- package/src/components/Carousel/carousel.tsx +93 -42
- package/src/components/Checkbox/checkbox-group.tsx +26 -15
- package/src/components/Checkbox/checkbox.anatomy.stories.tsx +3 -3
- package/src/components/Checkbox/checkbox.spec.md +12 -3
- package/src/components/Checkbox/checkbox.stories.tsx +4 -4
- package/src/components/Checkbox/checkbox.tsx +79 -19
- package/src/components/Coachmark/coachmark.anatomy.stories.tsx +5 -5
- package/src/components/Coachmark/coachmark.principles.stories.tsx +1 -1
- package/src/components/Combobox/combobox.anatomy.stories.tsx +11 -4
- package/src/components/Combobox/combobox.principles.stories.tsx +10 -11
- package/src/components/Combobox/combobox.spec.md +6 -2
- package/src/components/Combobox/combobox.stories.tsx +9 -36
- package/src/components/Combobox/combobox.tsx +42 -27
- package/src/components/Command/command.spec.md +3 -3
- package/src/components/Command/command.tsx +9 -2
- package/src/components/DataTable/cell-registry.tsx +75 -76
- package/src/components/DataTable/column-types.ts +4 -17
- package/src/components/DataTable/data-table-column-visibility-panel.tsx +20 -7
- package/src/components/DataTable/data-table-filter-panel.tsx +6 -6
- package/src/components/DataTable/data-table-interaction-layer.tsx +3 -3
- package/src/components/DataTable/data-table-sort-manager.tsx +2 -2
- package/src/components/DataTable/data-table.anatomy.stories.tsx +25 -24
- package/src/components/DataTable/data-table.css +2 -3
- package/src/components/DataTable/data-table.principles.stories.tsx +17 -11
- package/src/components/DataTable/data-table.spec.md +8 -7
- package/src/components/DataTable/data-table.stories.tsx +39 -29
- package/src/components/DataTable/data-table.tsx +90 -67
- package/src/components/DataTable/filter-operators.spec.md +2 -0
- package/src/components/DateGrid/date-grid.spec.md +1 -1
- package/src/components/DatePicker/date-picker.anatomy.stories.tsx +9 -9
- package/src/components/DatePicker/date-picker.principles.stories.tsx +4 -4
- package/src/components/DatePicker/date-picker.spec.md +10 -9
- package/src/components/DatePicker/date-picker.stories.tsx +9 -9
- package/src/components/DatePicker/date-picker.tsx +13 -13
- package/src/components/DescriptionList/description-list.anatomy.stories.tsx +5 -5
- package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
- package/src/components/DescriptionList/description-list.spec.md +12 -12
- package/src/components/DescriptionList/description-list.stories.tsx +3 -3
- package/src/components/DescriptionList/description-list.tsx +10 -10
- package/src/components/Dialog/dialog.tsx +4 -1
- package/src/components/DropdownMenu/dropdown-menu.tsx +27 -14
- package/src/components/Empty/empty.spec.md +0 -1
- package/src/components/Field/field-context.ts +17 -11
- package/src/components/Field/field-controls.spec.md +46 -37
- package/src/components/Field/field-types.ts +14 -12
- package/src/components/Field/field-wrapper.tsx +59 -43
- package/src/components/Field/field.anatomy.stories.tsx +6 -6
- package/src/components/Field/field.principles.stories.tsx +5 -28
- package/src/components/Field/field.spec.md +5 -4
- package/src/components/Field/field.stories.tsx +103 -72
- package/src/components/Field/field.tsx +36 -4
- package/src/components/Field/form-validation.spec.md +4 -0
- package/src/components/Field/use-form-validation.ts +1 -1
- package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +3 -3
- package/src/components/FieldControlGroup/field-control-group.spec.md +3 -3
- package/src/components/FileItem/file-item.tsx +10 -8
- package/src/components/FileUpload/file-upload.principles.stories.tsx +7 -7
- package/src/components/FileUpload/file-upload.spec.md +3 -2
- package/src/components/FileUpload/file-upload.tsx +5 -1
- package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +1 -1
- package/src/components/FileViewer/file-viewer.principles.stories.tsx +1 -1
- package/src/components/FileViewer/file-viewer.spec.md +1 -1
- package/src/components/FileViewer/file-viewer.stories.tsx +1 -1
- package/src/components/FileViewer/file-viewer.tsx +3 -2
- package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -4
- package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -1
- package/src/components/HoverCard/hover-card.spec.md +2 -2
- package/src/components/HoverCard/hover-card.tsx +4 -2
- package/src/components/InlineEdit/inline-edit.anatomy.stories.tsx +184 -0
- package/src/components/InlineEdit/inline-edit.principles.stories.tsx +142 -0
- package/src/components/InlineEdit/inline-edit.spec.md +71 -46
- package/src/components/InlineEdit/inline-edit.stories.tsx +20 -19
- package/src/components/InlineEdit/inline-edit.tsx +211 -125
- package/src/components/Input/input.anatomy.stories.tsx +15 -15
- package/src/components/Input/input.principles.stories.tsx +3 -3
- package/src/components/Input/input.spec.md +4 -4
- package/src/components/Input/input.stories.tsx +3 -3
- package/src/components/Input/input.tsx +13 -13
- package/src/components/LinkInput/link-input.anatomy.stories.tsx +2 -2
- package/src/components/LinkInput/link-input.principles.stories.tsx +1 -1
- package/src/components/LinkInput/link-input.spec.md +6 -2
- package/src/components/LinkInput/link-input.stories.tsx +8 -8
- package/src/components/LinkInput/link-input.tsx +21 -19
- package/src/components/Notice/notice.principles.stories.tsx +1 -1
- package/src/components/Notice/notice.tsx +8 -2
- package/src/components/NumberInput/number-input.anatomy.stories.tsx +6 -6
- package/src/components/NumberInput/number-input.principles.stories.tsx +4 -4
- package/src/components/NumberInput/number-input.spec.md +6 -6
- package/src/components/NumberInput/number-input.stories.tsx +4 -4
- package/src/components/NumberInput/number-input.tsx +17 -5
- package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +8 -4
- package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +6 -7
- package/src/components/OverflowIndicator/overflow-indicator.spec.md +1 -1
- package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +6 -30
- package/src/components/PeoplePicker/avatar-stack-overflow.ts +1 -1
- package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +13 -10
- package/src/components/PeoplePicker/people-picker.principles.stories.tsx +5 -1
- package/src/components/PeoplePicker/people-picker.stories.tsx +5 -5
- package/src/components/PeoplePicker/people-picker.tsx +39 -20
- package/src/components/PeoplePicker/person-display.tsx +3 -3
- package/src/components/Popover/popover.anatomy.stories.tsx +10 -7
- package/src/components/Popover/popover.principles.stories.tsx +49 -28
- package/src/components/Popover/popover.spec.md +4 -2
- package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
- package/src/components/ProfileCard/profile-card.tsx +1 -1
- package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +14 -13
- package/src/components/ProgressBar/progress-bar.principles.stories.tsx +8 -23
- package/src/components/ProgressBar/progress-bar.stories.tsx +8 -2
- package/src/components/README.md +1 -0
- package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +8 -3
- package/src/components/RadioGroup/radio-group.stories.tsx +3 -3
- package/src/components/RadioGroup/radio-group.tsx +19 -14
- package/src/components/Rating/rating.anatomy.stories.tsx +1 -1
- package/src/components/Rating/rating.principles.stories.tsx +3 -3
- package/src/components/Rating/rating.spec.md +2 -2
- package/src/components/Rating/rating.stories.tsx +4 -18
- package/src/components/Rating/rating.tsx +5 -1
- package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.spec.md +1 -1
- package/src/components/ScrollArea/scroll-area.tsx +5 -2
- package/src/components/SegmentedControl/segmented-control.principles.stories.tsx +4 -1
- package/src/components/SegmentedControl/segmented-control.spec.md +5 -10
- package/src/components/SegmentedControl/segmented-control.tsx +6 -3
- package/src/components/Select/select.anatomy.stories.tsx +13 -7
- package/src/components/Select/select.principles.stories.tsx +3 -3
- package/src/components/Select/select.spec.md +10 -1
- package/src/components/Select/select.stories.tsx +41 -27
- package/src/components/Select/select.tsx +44 -26
- package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +44 -26
- package/src/components/SelectMenu/select-menu.principles.stories.tsx +0 -1
- package/src/components/SelectMenu/select-menu.stories.tsx +7 -19
- package/src/components/SelectMenu/select-menu.tsx +38 -5
- package/src/components/SelectionControl/boolean-value.tsx +2 -2
- package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +1 -1
- package/src/components/SelectionControl/selection-item.principles.stories.tsx +4 -4
- package/src/components/Separator/separator.principles.stories.tsx +1 -1
- package/src/components/Sheet/sheet.principles.stories.tsx +5 -3
- package/src/components/Sheet/sheet.tsx +4 -1
- package/src/components/Sidebar/sidebar.tsx +1 -1
- package/src/components/Slider/slider.anatomy.stories.tsx +5 -4
- package/src/components/Slider/slider.principles.stories.tsx +6 -6
- package/src/components/Slider/slider.spec.md +13 -1
- package/src/components/Slider/slider.stories.tsx +44 -32
- package/src/components/Slider/slider.tsx +13 -4
- package/src/components/Steps/steps.anatomy.stories.tsx +7 -7
- package/src/components/Steps/steps.principles.stories.tsx +1 -1
- package/src/components/Steps/steps.spec.md +4 -2
- package/src/components/Steps/steps.stories.tsx +4 -56
- package/src/components/Steps/steps.tsx +31 -7
- package/src/components/Switch/switch.anatomy.stories.tsx +3 -3
- package/src/components/Switch/switch.principles.stories.tsx +1 -1
- package/src/components/Switch/switch.spec.md +4 -4
- package/src/components/Switch/switch.stories.tsx +3 -3
- package/src/components/Switch/switch.tsx +16 -10
- package/src/components/Tabs/tabs.anatomy.stories.tsx +6 -0
- package/src/components/Tabs/tabs.principles.stories.tsx +10 -2
- package/src/components/Tabs/tabs.spec.md +5 -2
- package/src/components/Tabs/tabs.stories.tsx +36 -44
- package/src/components/Tabs/tabs.tsx +101 -36
- package/src/components/Tag/tag.anatomy.stories.tsx +2 -2
- package/src/components/Tag/tag.principles.stories.tsx +4 -4
- package/src/components/Tag/tag.spec.md +4 -1
- package/src/components/Tag/tag.tsx +5 -3
- package/src/components/Textarea/textarea.anatomy.stories.tsx +6 -6
- package/src/components/Textarea/textarea.spec.md +5 -5
- package/src/components/Textarea/textarea.tsx +24 -24
- package/src/components/TimePicker/time-picker.anatomy.stories.tsx +12 -11
- package/src/components/TimePicker/time-picker.principles.stories.tsx +37 -44
- package/src/components/TimePicker/time-picker.spec.md +6 -5
- package/src/components/TimePicker/time-picker.stories.tsx +3 -3
- package/src/components/TimePicker/time-picker.tsx +24 -14
- package/src/components/Toast/toast.principles.stories.tsx +1 -1
- package/src/components/Toast/toast.stories.tsx +1 -1
- package/src/components/Tooltip/tooltip.anatomy.stories.tsx +15 -6
- package/src/components/Tooltip/tooltip.principles.stories.tsx +4 -4
- package/src/components/Tooltip/tooltip.spec.md +9 -3
- package/src/components/Tooltip/tooltip.stories.tsx +1 -1
- package/src/components/Tooltip/tooltip.tsx +12 -2
- package/src/components/TreeView/tree-view.anatomy.stories.tsx +16 -14
- package/src/components/TreeView/tree-view.principles.stories.tsx +44 -11
- package/src/components/TreeView/tree-view.spec.md +8 -6
- package/src/components/TreeView/tree-view.stories.tsx +9 -43
- package/src/components/TreeView/tree-view.tsx +36 -0
- package/src/index.ts +13 -10
- package/src/lib/drag-visual.ts +2 -2
- package/src/lib/utils.ts +19 -0
- package/src/patterns/action-bar/action-bar.spec.md +1 -0
- package/src/patterns/element-anatomy/inline-action.spec.md +1 -1
- package/src/patterns/element-anatomy/item-anatomy.spec.md +4 -0
- package/src/patterns/element-anatomy/item-anatomy.tsx +4 -2
- package/src/patterns/header-canonical/header-canonical.spec.md +9 -0
- package/src/patterns/header-canonical/header-canonical.stories.tsx +31 -5
- package/src/tokens/categorical-color.ts +5 -5
- package/src/tokens/color/color.spec.md +4 -4
- package/src/tokens/color/primitives.css +9 -2
- package/src/tokens/motion/motion.spec.md +6 -7
- package/src/tokens/opacity/opacity.spec.md +1 -1
- package/src/tokens/orphan-tokens.spec.md +1 -1
- package/src/tokens/typography/typography.spec.md +2 -0
- package/src/tokens/uiSize/uiSize.css +5 -0
- package/src/tokens/uiSize/uiSize.spec.md +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @benchmark-
|
|
1
|
+
// @benchmark-unverified-blanket: file-level retraction per M22 (d) — 產品架構參照(Linear / Notion / Figma / GitHub / Slack / Gmail 等)為 usage 觀察,非逐句 URL-cited;視為 unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
3
3
|
import LinkTo from '@storybook/addon-links/react'
|
|
4
4
|
|
|
@@ -62,11 +62,11 @@ export const UsageGuidance: Story = {
|
|
|
62
62
|
<section>
|
|
63
63
|
<h2 className="text-h4 mb-2">Consumer 紀律</h2>
|
|
64
64
|
<ul className="text-body space-y-1">
|
|
65
|
-
<li>❌ 禁:`<AppShell>` 內塞另一個 `<AppShell>`(
|
|
66
|
-
<li>❌ 禁:`sidebar
|
|
67
|
-
<li>❌ 禁:`header
|
|
68
|
-
<li>❌
|
|
69
|
-
<li>✅ 必:Main
|
|
65
|
+
<li>❌ 禁:`<AppShell>` 內塞另一個 `<AppShell>`(整頁框架只能有一個)</li>
|
|
66
|
+
<li>❌ 禁:`sidebar` 傳裸 `<div>` → 必傳 `<Sidebar>`,才能維持一致的展開/收合、行動裝置抽屜、鍵盤操作與視覺;傳其他容器雖然型別過得了,卻會失去這些導覽行為</li>
|
|
67
|
+
<li>❌ 禁:`header` 傳裸 `<header>` → 應傳 `<ChromeHeader>`(或沿用同一套標準標頭結構自訂),才能得到一致的標頭高度、內距與對齊</li>
|
|
68
|
+
<li>❌ 禁:硬性撐開 Main 區域的內距 → Main 本身不加留白,由內容自己決定(自帶邊框的卡片、資料表各自帶內距)</li>
|
|
69
|
+
<li>✅ 必:Main 內容的間距沿用既有版面間距原則——頁面標頭、卡片、資料表、純列表各按對應規則處理</li>
|
|
70
70
|
</ul>
|
|
71
71
|
</section>
|
|
72
72
|
</div>
|
|
@@ -81,37 +81,37 @@ export const LayoutModeRule: Story = {
|
|
|
81
81
|
render: () => (
|
|
82
82
|
<div className="prose max-w-2xl space-y-6 px-[var(--layout-space-loose)] py-[var(--layout-space-tight)]">
|
|
83
83
|
<section>
|
|
84
|
-
<h2 className="text-h4 mb-2"
|
|
84
|
+
<h2 className="text-h4 mb-2">唯一判準:頂部標頭服務的範圍</h2>
|
|
85
85
|
<p className="text-body mb-2">
|
|
86
|
-
|
|
87
|
-
<em>不是</em
|
|
88
|
-
|
|
86
|
+
唯一的區分標準 = <strong>頂部標頭服務的是「目前這一頁」還是「整個產品」</strong>,
|
|
87
|
+
<em>不是</em>工作區(workspace)的數量多寡(Notion / Gmail 都支援多工作區,卻分屬不同派)。
|
|
88
|
+
問自己:頂部那條標頭列服務的是當前頁面,還是整個產品?
|
|
89
89
|
</p>
|
|
90
90
|
<ul className="text-body space-y-1">
|
|
91
91
|
<li>
|
|
92
|
-
<strong>primary-sidebar</strong
|
|
93
|
-
|
|
92
|
+
<strong>primary-sidebar</strong>:頂部是<strong>服務當前頁的工具列</strong> —
|
|
93
|
+
放麵包屑、頁面層級操作、篩選。Sidebar 頂天立地,工作區品牌(`WorkspaceBrand`)放在 sidebar 頂。
|
|
94
94
|
參考 Linear / Notion / Figma。
|
|
95
95
|
</li>
|
|
96
96
|
<li>
|
|
97
|
-
<strong>primary-header</strong
|
|
98
|
-
|
|
99
|
-
<em>仍在</em
|
|
97
|
+
<strong>primary-header</strong>:多一條<strong>橫跨頂部、服務整個產品的全域標頭</strong> —
|
|
98
|
+
放帳號、工作區切換、跨頁搜尋、通知;服務當前頁的工具列
|
|
99
|
+
<em>仍在</em>主欄頂部,只是上面多了這條全域標頭。工作區品牌改放全域標頭(`globalHeader`)左側。
|
|
100
100
|
參考 GitHub / Slack / Gmail。完整組合見{' '}
|
|
101
101
|
<LinkTo kind="Design System/Components/AppShell/展示" name="主標頭佈局 — 全域+本地兩層(GitHub/Gmail/Slack 派)"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">展示 → 主標頭佈局 — 全域+本地兩層</span></LinkTo>
|
|
102
102
|
</li>
|
|
103
103
|
</ul>
|
|
104
104
|
<p className="text-caption text-fg-secondary mt-2">
|
|
105
|
-
|
|
105
|
+
兩種模式是產品的角色定位——啟動時就固定,不該在執行期切換。視覺對照圖見{' '}
|
|
106
106
|
<LinkTo kind="Design System/Components/AppShell/設計規格" name="兩種布局模式對照圖"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">設計規格 → 兩種布局模式對照圖</span></LinkTo>
|
|
107
107
|
</p>
|
|
108
108
|
</section>
|
|
109
109
|
|
|
110
110
|
<section>
|
|
111
|
-
<h2 className="text-h4 mb-2"
|
|
111
|
+
<h2 className="text-h4 mb-2">常見誤解:全域標頭會取代當前頁工具列</h2>
|
|
112
112
|
<p className="text-body">
|
|
113
|
-
錯。primary-header = primary-sidebar 的所有東西 + <strong>額外一條</strong
|
|
114
|
-
`header` slot
|
|
113
|
+
錯。primary-header = primary-sidebar 的所有東西 + <strong>額外一條</strong>全域標頭在頂;
|
|
114
|
+
當前頁工具列(`header` slot)<strong>仍然存在</strong>。GitHub / Slack / Gmail 全部同時保有兩層,
|
|
115
115
|
不是二選一。
|
|
116
116
|
</p>
|
|
117
117
|
</section>
|
|
@@ -129,9 +129,9 @@ export const LayoutModeRule: Story = {
|
|
|
129
129
|
<h2 className="text-h4 mb-2">帳號入口(個人設定)跟著 mode 走(只能出現一次)</h2>
|
|
130
130
|
<ul className="text-body space-y-1">
|
|
131
131
|
<li>• <strong>primary-sidebar</strong>:帳號 / 個人設定放 Sidebar 底部(`<SidebarFooter>`)— Linear / Notion / Figma</li>
|
|
132
|
-
<li>• <strong>primary-header</strong>:帳號入口改放 globalHeader 右側 avatar(品牌左、帳號右,左右對稱),sidebar
|
|
132
|
+
<li>• <strong>primary-header</strong>:帳號入口改放 globalHeader 右側 avatar(品牌左、帳號右,左右對稱),sidebar 不放帳號頁尾 — GitHub / Gmail / Slack 帳號一律在全域標頭右上</li>
|
|
133
133
|
<li>• 開「個人資料 / 設定 / 登出」帳號選單(`<DropdownMenu>`),<strong>不用 ProfileCard</strong>(ProfileCard 是看別人的人員卡,預設動作 Chat/通話用在自己身上不對)</li>
|
|
134
|
-
<li>• 帳號 avatar = 24px,跟左側品牌 avatar 同尺寸(
|
|
134
|
+
<li>• 帳號 avatar = 24px,跟左側品牌 avatar 同尺寸(沿用標準標頭的頭像尺寸規範);右側邊距與品牌距分割線對稱</li>
|
|
135
135
|
<li>❌ 禁:`primary-header` mode 同時在 globalHeader + sidebar footer 各放一份 = 入口混淆(帳號入口視覺 SSOT,只能出現一次)</li>
|
|
136
136
|
</ul>
|
|
137
137
|
</section>
|
|
@@ -67,6 +67,10 @@ benchmark:
|
|
|
67
67
|
|
|
68
68
|
Sub-component:`<AppShellAside title={...} width={400}>`(width consumer 自決 — `number` 或 `{ md, xl }` breakpoint-keyed,clamp `min-width: 240` / `max-width: 640`;**title prop required**,modal mode 走 Sheet → `aria-labelledby` 強制,per `sheet.spec.md`「禁止事項」(無 title → aria-labelledby 強制))。
|
|
69
69
|
|
|
70
|
+
**Aside body padding(內容自帶,刻意 Pattern B)**:`<AppShellAside>` 的 body(ScrollArea 內)**不自帶 px/py**,由 consumer 塞入的 children 自行負責留白(對齊 `layoutSpace.spec.md` 規則 1B「父層/內容管 padding」的裸結構路線,同 Table / DataTable)。**理由**:aside 內容異質(清單需 hover 貼邊滿版、表格需 flush、表單才需 padding),框架統一加 padding 反而破壞清單/表格。與 Dialog / Sheet body 自帶 `px-loose py-tight` 的差異是**刻意**的(那兩者內容較單一)。consumer 放需留白的內容時,自帶 `px-[var(--layout-space-loose)] py-[var(--layout-space-tight)]`(範例 `app-shell.stories.tsx` 即如此)。
|
|
71
|
+
|
|
72
|
+
**Aside 開關控制(刻意單向,2026-07-17 user 拍板)**:`asideOpen` / `onAsideOpenChange` 提供完整 **controlled** 路徑;uncontrolled fallback 初值**刻意固定關閉**(`useState(false)`),**不提供 `defaultAsideOpen`**。理由:側邊詳情面板實務上幾乎一定由 app 的 route / 選取狀態驅動(modal mode 更明文要求 controlled),初值固定關閉是合理預設,額外加 `defaultAsideOpen` 偏 YAGNI。對齊 Dim 26 dual-mode canonical 的「刻意單一模式 → 補 rationale」例外機制。未來真有 uncontrolled 初值需求再加(additive,不破壞)。
|
|
73
|
+
|
|
70
74
|
### Hook export:`useAppShell()`(2026-05-21 D2 codify per Phase B codex catch)
|
|
71
75
|
|
|
72
76
|
Public compound API hook,讓 consumer 自拼 custom aside / 自管 modal layout(對齊 Radix `useDialogContext` / MUI `useFormControl` / Mantine `useAppShellContext` 慣例)。
|
|
@@ -68,7 +68,7 @@ const ISSUE_COLUMNS = [
|
|
|
68
68
|
*
|
|
69
69
|
* Per codex Layer B D3(2026-05-20):row-driven Aside trigger 才符合 Linear/Notion/Jira/Airtable
|
|
70
70
|
* production 真實情境。row 右側 dedicated action button(`Button variant="text" size="xs" iconOnly`
|
|
71
|
-
* per data-table.spec
|
|
71
|
+
* per data-table.spec L153 rowActions canonical)→ click 開 Aside + 被選 issue active visual(pressed prop)。
|
|
72
72
|
* AppShell header toggle 降級為 secondary show/hide 不是主入口。
|
|
73
73
|
*/
|
|
74
74
|
function IssuesView({ selectedId, asideOpen, onSelectIssue }: { selectedId?: string; asideOpen: boolean; onSelectIssue: (issue: Issue) => void }) {
|
|
@@ -118,7 +118,6 @@ function IssuesView({ selectedId, asideOpen, onSelectIssue }: { selectedId?: str
|
|
|
118
118
|
</PopoverTrigger>
|
|
119
119
|
<PopoverContent align="end" className="w-auto p-0">
|
|
120
120
|
<DataTableFilterPanel
|
|
121
|
-
mode="flat"
|
|
122
121
|
columns={ISSUE_COLUMNS as any}
|
|
123
122
|
value={filterTree}
|
|
124
123
|
onChange={setFilterTree}
|
|
@@ -150,7 +149,7 @@ function IssuesView({ selectedId, asideOpen, onSelectIssue }: { selectedId?: str
|
|
|
150
149
|
</div>
|
|
151
150
|
</div>
|
|
152
151
|
{/* DataTable:naked structure,layoutSpace 規則 1B 父層 mx 對齊 chrome 內容左右邊界。
|
|
153
|
-
rowActions:per data-table.spec
|
|
152
|
+
rowActions:per data-table.spec L153 rowActions canonical(Button text xs iconOnly,固定 24px)+
|
|
154
153
|
pressed={row.id === selectedId} 顯示當前選中(per codex D3 active visual)。 */}
|
|
155
154
|
<div className="flex-1 min-h-0 mx-[var(--layout-space-loose)] mb-[var(--layout-space-loose)]">
|
|
156
155
|
<DataTable
|
|
@@ -226,7 +225,7 @@ function IssueDetail({ issue }: { issue: Issue | null }) {
|
|
|
226
225
|
<Tag size="sm" color={PRIORITY_COLOR[issue.priority]}>{issue.priority}</Tag>
|
|
227
226
|
</div>
|
|
228
227
|
</div>
|
|
229
|
-
<DescriptionList
|
|
228
|
+
<DescriptionList orientation="horizontal" divided>
|
|
230
229
|
<DescriptionItem label="Assignee">{issue.assignee}</DescriptionItem>
|
|
231
230
|
<DescriptionItem label="Due">{issue.due}</DescriptionItem>
|
|
232
231
|
</DescriptionList>
|
|
@@ -374,7 +373,7 @@ export const PrimarySidebarWithTabs: Story = {
|
|
|
374
373
|
/**
|
|
375
374
|
* primary-header mode(GitHub / Gmail / Slack 派)— global header 橫跨頂部 + local header 仍在 main col 頂。
|
|
376
375
|
*
|
|
377
|
-
* @usage-ref: app-shell.spec.md
|
|
376
|
+
* @usage-ref: app-shell.spec.md「primary-header = primary-sidebar + 一條 global header」段(2-layer model)
|
|
378
377
|
* @usage-consumes: ChromeHeader + Sidebar viewportInsetTop + GlobalHeader + PageHeader + IssuesView
|
|
379
378
|
*
|
|
380
379
|
* 2026-05-21 v2 per user clarification「primary-header = primary-sidebar + 一條 global header」:
|
|
@@ -375,7 +375,7 @@ export const avatarMeta = {
|
|
|
375
375
|
// — item-anatomy AVATAR_SIZE 查表 / ProfileCard 40 等),無 discrete tier → 兩欄留空為 intentional 非漏填
|
|
376
376
|
variants: {},
|
|
377
377
|
sizes: {},
|
|
378
|
-
states: ['default', 'focus-visible', 'disabled'], // 2026-06-11 R2:本身無自有 hover/active(spec
|
|
378
|
+
states: ['default', 'focus-visible', 'disabled'], // 2026-06-11 R2:本身無自有 hover/active(spec「StateBehavior 範疇」段;hoverCard 互動屬 HoverCard),
|
|
379
379
|
tokens: {
|
|
380
380
|
bg: ['bg-surface-raised'],
|
|
381
381
|
fg: ['--foreground', '--on-emphasis'],
|
|
@@ -379,14 +379,14 @@ Badge 是**計數 / 狀態視覺指示器**,本身**非互動元件**:
|
|
|
379
379
|
- Badge 通常依附在互動元件上(Button / Nav Item),那些元件有自己的 state——Badge 隨父層視覺連動,不擁有獨立 state。
|
|
380
380
|
- 計數變化(`count` 更新)屬資料更新不是 UI state 切換。
|
|
381
381
|
|
|
382
|
-
對應 anatomy story:保留 `Overview` + `Inspector` + `ColorMatrix` + `SizeMatrix`。
|
|
382
|
+
對應 anatomy story:保留 `Overview` + `Inspector` + `ColorMatrix` + `SizeMatrix` + `Accessibility`。
|
|
383
383
|
|
|
384
384
|
---
|
|
385
385
|
|
|
386
386
|
## 相關
|
|
387
387
|
|
|
388
388
|
- `../Tag/tag.spec.md` — 分類標籤、狀態標記(Badge vs Tag 的詳細對照在本 spec 定位段落)
|
|
389
|
-
- `../Avatar/avatar.spec.md` — `badgeCount` 內部消費 `<Badge variant="critical">`(遵循本 spec);`status` 是 Avatar 自有 presence
|
|
389
|
+
- `../Avatar/avatar.spec.md` — `badgeCount` 內部消費 `<Badge variant="critical">`(遵循本 spec);`status` 是 Avatar 自有 presence dot(內部 `span` + inline backgroundColor,非 Badge 系統),兩者可並存,詳該 spec
|
|
390
390
|
- `../Button/button.spec.md` — iconOnly Button + Badge overlay 通知 icon 的組合模式
|
|
391
391
|
- `../Chip/chip.spec.md` — 可互動 filter(不是 Badge 的用途)
|
|
392
392
|
- `../CircularProgress/circular-progress.spec.md` — Loading 狀態指示(取代 Spinner)
|
|
@@ -342,19 +342,23 @@ export const StateBehavior: Story = {
|
|
|
342
342
|
<Breadcrumb>
|
|
343
343
|
<BreadcrumbList>
|
|
344
344
|
<BreadcrumbItem>
|
|
345
|
-
<BreadcrumbLink href="#"
|
|
345
|
+
<BreadcrumbLink href="#">團隊空間</BreadcrumbLink>
|
|
346
346
|
</BreadcrumbItem>
|
|
347
347
|
<BreadcrumbSeparator />
|
|
348
348
|
<BreadcrumbItem>
|
|
349
|
-
<BreadcrumbLink href="#"
|
|
349
|
+
<BreadcrumbLink href="#">設計系統</BreadcrumbLink>
|
|
350
350
|
</BreadcrumbItem>
|
|
351
351
|
<BreadcrumbSeparator />
|
|
352
352
|
<BreadcrumbItem>
|
|
353
|
-
<BreadcrumbPage
|
|
353
|
+
<BreadcrumbPage>元件規格</BreadcrumbPage>
|
|
354
354
|
</BreadcrumbItem>
|
|
355
355
|
</BreadcrumbList>
|
|
356
356
|
</Breadcrumb>
|
|
357
357
|
</div>
|
|
358
|
+
<p className="text-footnote text-fg-muted mt-3">
|
|
359
|
+
「團隊空間」「設計系統」為可點連結(預設 fg-secondary,移上去 hover 轉 primary-hover);
|
|
360
|
+
末項「元件規格」為當前頁(foreground,不可點)。
|
|
361
|
+
</p>
|
|
358
362
|
</div>
|
|
359
363
|
</div>
|
|
360
364
|
),
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
DropdownMenuContent,
|
|
19
19
|
DropdownMenuItem,
|
|
20
20
|
} from '@/design-system/components/DropdownMenu/dropdown-menu'
|
|
21
|
-
import { Tabs, TabsList, TabsTrigger } from '@/design-system/components/Tabs/tabs'
|
|
21
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/design-system/components/Tabs/tabs'
|
|
22
22
|
import { Button } from '@/design-system/components/Button/button'
|
|
23
23
|
|
|
24
24
|
const meta: Meta = {
|
|
@@ -159,6 +159,9 @@ export const UsageGuidance: Story = {
|
|
|
159
159
|
<TabsTrigger value="customers">顧客</TabsTrigger>
|
|
160
160
|
<TabsTrigger value="products">產品</TabsTrigger>
|
|
161
161
|
</TabsList>
|
|
162
|
+
<TabsContent value="orders" className="text-body text-fg-muted mt-2">訂單列表、篩選與批次出貨</TabsContent>
|
|
163
|
+
<TabsContent value="customers" className="text-body text-fg-muted mt-2">顧客名單、分群與生命週期價值</TabsContent>
|
|
164
|
+
<TabsContent value="products" className="text-body text-fg-muted mt-2">商品目錄、庫存與上下架狀態</TabsContent>
|
|
162
165
|
</Tabs>
|
|
163
166
|
</div>
|
|
164
167
|
</Rule>
|
|
@@ -190,7 +190,7 @@ export const DeclarativeAutoCollapse: Story = {
|
|
|
190
190
|
<Breadcrumb>
|
|
191
191
|
<BreadcrumbList
|
|
192
192
|
items={[
|
|
193
|
-
{ label: '
|
|
193
|
+
{ label: 'Global Platform Infrastructure Group', href: '/org' },
|
|
194
194
|
{ label: 'Product Engineering Team', href: '/team' },
|
|
195
195
|
{ label: 'Design System Component Refactor Sprint 23' },
|
|
196
196
|
]}
|
|
@@ -46,7 +46,7 @@ export const Overview: Story = {
|
|
|
46
46
|
<Button variant="tertiary" size="md" startIcon={Trash2} danger>刪除</Button>
|
|
47
47
|
</>
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
onClearSelection={() => {}}
|
|
50
50
|
/>
|
|
51
51
|
</div>
|
|
52
52
|
</section>
|
|
@@ -87,7 +87,7 @@ export const Overview: Story = {
|
|
|
87
87
|
<BulkActionBar
|
|
88
88
|
selection={Array.from({ length: 50 }, (_, i) => `f-${i}`)}
|
|
89
89
|
actions={<Button variant="tertiary" size="md" startIcon={Download}>下載</Button>}
|
|
90
|
-
|
|
90
|
+
onClearSelection={() => {}}
|
|
91
91
|
/>
|
|
92
92
|
</div>
|
|
93
93
|
</div>
|
|
@@ -115,7 +115,7 @@ function SelectionStateDemo() {
|
|
|
115
115
|
<Button variant="tertiary" size="md" startIcon={Trash2} danger>刪除</Button>
|
|
116
116
|
</>
|
|
117
117
|
}
|
|
118
|
-
|
|
118
|
+
onClearSelection={() => setCount(0)}
|
|
119
119
|
/>
|
|
120
120
|
{count === 0 && <span className="text-caption text-fg-muted italic px-3 py-2 inline-block">↑ count=0 → BulkActionBar 不渲染</span>}
|
|
121
121
|
</div>
|
|
@@ -162,7 +162,7 @@ function InspectorInner() {
|
|
|
162
162
|
<Button variant="tertiary" size="md" startIcon={Trash2} danger>刪除</Button>
|
|
163
163
|
</>
|
|
164
164
|
}
|
|
165
|
-
|
|
165
|
+
onClearSelection={() => setSelectionCount(0)}
|
|
166
166
|
/>
|
|
167
167
|
{selectionCount === 0 && <span className="text-caption text-fg-muted italic px-3 py-2 inline-block">selection=0 → 不渲染</span>}
|
|
168
168
|
</div>
|
|
@@ -196,7 +196,7 @@ export const Accessibility: Story = {
|
|
|
196
196
|
<section>
|
|
197
197
|
<H3>鍵盤(預期 consumer 在 page 層級監聽)</H3>
|
|
198
198
|
<ul className="list-disc list-inside text-caption text-fg-secondary space-y-1">
|
|
199
|
-
<li><kbd>Esc</kbd> → 觸發 <code>
|
|
199
|
+
<li><kbd>Esc</kbd> → 觸發 <code>onClearSelection()</code>(consumer 在 page-level keydown 監聽)</li>
|
|
200
200
|
<li>Tab 序:close → actions(count 是純文字 / aria 朗讀對象,非 tab stop)</li>
|
|
201
201
|
<li>Hint CTA 是 <code>{`<button>`}</code>,鍵盤可達</li>
|
|
202
202
|
</ul>
|
|
@@ -216,7 +216,7 @@ export const Accessibility: Story = {
|
|
|
216
216
|
<Button variant="tertiary" size="md" startIcon={Trash2} danger disabled>刪除(無權限)</Button>
|
|
217
217
|
</>
|
|
218
218
|
}
|
|
219
|
-
|
|
219
|
+
onClearSelection={() => {}}
|
|
220
220
|
/>
|
|
221
221
|
</div>
|
|
222
222
|
</section>
|
|
@@ -83,7 +83,7 @@ export const UsageGuidance: Story = {
|
|
|
83
83
|
<Button variant="tertiary" size="md" startIcon={Trash2} danger>刪除</Button>
|
|
84
84
|
</>
|
|
85
85
|
}
|
|
86
|
-
|
|
86
|
+
onClearSelection={() => {}}
|
|
87
87
|
/>
|
|
88
88
|
<Label>↑ selection {`>`} 0 浮現,= 0 完全藏</Label>
|
|
89
89
|
</Rule>
|
|
@@ -160,7 +160,7 @@ export const ActionVariantRule: Story = {
|
|
|
160
160
|
<Button variant="tertiary" size="md" startIcon={Trash2} danger>刪除</Button>
|
|
161
161
|
</>
|
|
162
162
|
}
|
|
163
|
-
|
|
163
|
+
onClearSelection={() => {}}
|
|
164
164
|
/>
|
|
165
165
|
<Label>↑ tertiary 主 / tertiary danger 破壞性</Label>
|
|
166
166
|
</Rule>
|
|
@@ -177,7 +177,7 @@ export const ActionVariantRule: Story = {
|
|
|
177
177
|
<Button variant="primary" size="md" startIcon={Trash2} danger>刪除</Button>
|
|
178
178
|
</>
|
|
179
179
|
}
|
|
180
|
-
|
|
180
|
+
onClearSelection={() => {}}
|
|
181
181
|
/>
|
|
182
182
|
<Label warn>↑ 兩顆都 primary → 視覺重量過高,跟 page Submit 競爭</Label>
|
|
183
183
|
</Rule>
|
|
@@ -211,7 +211,7 @@ export const HintBannerRule: Story = {
|
|
|
211
211
|
<BulkActionBar
|
|
212
212
|
selection={Array.from({ length: 50 }, (_, i) => `f-${i}`)}
|
|
213
213
|
actions={<Button variant="tertiary" size="md">下載</Button>}
|
|
214
|
-
|
|
214
|
+
onClearSelection={() => {}}
|
|
215
215
|
/>
|
|
216
216
|
</div>
|
|
217
217
|
</div>
|
|
@@ -222,7 +222,19 @@ export const HintBannerRule: Story = {
|
|
|
222
222
|
title="❌ 自刻 hint banner / 用 contrast 底色 div"
|
|
223
223
|
note="banner 是 Alert 的職責(自帶 a11y role / aria-live / 視覺一致性)。自刻會破壞跟其他 Alert 的視覺一致性,也漏 a11y"
|
|
224
224
|
>
|
|
225
|
-
<
|
|
225
|
+
<div className="border border-divider rounded-md overflow-hidden">
|
|
226
|
+
<div className="bg-info-subtle text-info-text px-[var(--layout-space-loose)] py-[var(--layout-space-tight)] text-footnote">
|
|
227
|
+
已選取本頁全部 50 個。點此選取全部 5370 個項目
|
|
228
|
+
</div>
|
|
229
|
+
<div className="border-t border-divider">
|
|
230
|
+
<BulkActionBar
|
|
231
|
+
selection={Array.from({ length: 50 }, (_, i) => `f-${i}`)}
|
|
232
|
+
actions={<Button variant="tertiary" size="md">下載</Button>}
|
|
233
|
+
onClearSelection={() => {}}
|
|
234
|
+
/>
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
<Label warn>↑ 手刻 div 當 banner:無 role="status" / aria-live,SR 不會朗讀狀態變化;底色跟其他 Alert 不一致;「點此選取」是純文字非可操作連結</Label>
|
|
226
238
|
</Rule>
|
|
227
239
|
|
|
228
240
|
<Rule
|
|
@@ -233,7 +245,7 @@ export const HintBannerRule: Story = {
|
|
|
233
245
|
selection={['a', 'b', 'c']}
|
|
234
246
|
hiddenByFilter={2}
|
|
235
247
|
actions={<Button variant="tertiary" size="md" startIcon={Archive}>封存</Button>}
|
|
236
|
-
|
|
248
|
+
onClearSelection={() => {}}
|
|
237
249
|
/>
|
|
238
250
|
<Label>↑ count 文字 inline:「已選 3 項 · 2 個被 filter 隱藏」</Label>
|
|
239
251
|
</Rule>
|
|
@@ -81,9 +81,9 @@ benchmark:
|
|
|
81
81
|
|
|
82
82
|
### Slot
|
|
83
83
|
|
|
84
|
-
- **`actions`**:consumer 提供 **md** Buttons(size rationale 見「Size canonical」)
|
|
84
|
+
- **`actions`**:consumer 提供 **md** Buttons(size rationale 見「Size canonical」);**一律 `variant=tertiary`**(含 destructive — `Trash` 等 icon + label 已辨識危險)— **不用 primary、destructive 不上 danger 紅填色**(2026-07-17 user 拍板:批次刪除鈕點下去是**開確認 dialog**非直接刪,紅色強調留給 dialog 內確認最終不可逆 action;對齊 UX Movement / GitLab Pajamas「紅留給不可逆最終確認」+ button.spec.md「Inline destructive 不用高強調」)
|
|
85
85
|
- **count 區**:`已選 {N} 項`(內建)+ inline filter hidden status `· {M} 個被 filter 隱藏`(`hiddenByFilter` prop 傳入時)
|
|
86
|
-
- **clear**:`<Button iconOnly size=md variant=text dismiss />`(內建,觸發 `
|
|
86
|
+
- **clear**:`<Button iconOnly size=md variant=text dismiss />`(內建,觸發 `onClearSelection`)
|
|
87
87
|
|
|
88
88
|
#### Size canonical(2026-05-04 升 SSOT)
|
|
89
89
|
|
|
@@ -143,7 +143,7 @@ interface BulkActionBarProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
143
143
|
/** 已選 ID;length === 0 且無 totalSelected 時自動隱藏(回 null,完整判準見「Extend dataset pattern」) */
|
|
144
144
|
selection: readonly string[]
|
|
145
145
|
/** Clear 觸發,user 點 X icon(consumer 在 page-level 監聽 Esc 觸發) */
|
|
146
|
-
|
|
146
|
+
onClearSelection?: () => void
|
|
147
147
|
/** 批次 actions(consumer 提供 md Button,variant=tertiary 或 tertiary+danger,不用 primary) */
|
|
148
148
|
actions?: React.ReactNode
|
|
149
149
|
/** Filter 模式:hidden 數量,顯示在 count 區 inline 「已選 {N} 項 · {M} 個被 filter 隱藏」 */
|
|
@@ -169,6 +169,16 @@ interface BulkActionBarLabels {
|
|
|
169
169
|
|
|
170
170
|
**Hint banner(擴 dataset 提示)不在本 API**:由 consumer 用 `<Alert variant="info" placement="fixed">` 配 ReactNode title 帶 inline link 自組,黏在 BulkActionBar 上方。Alert / Notice 的 `title` + `description` 已支援 ReactNode(2026-04-28)。
|
|
171
171
|
|
|
172
|
+
### 批次操作進行中(loading / 防重複提交 / 選取保留)
|
|
173
|
+
|
|
174
|
+
`actions` 由 consumer 注入 ReactNode,**批次操作的非同步生命週期由 consumer 擁有**(BulkActionBar 是 stateless 呈現層,不介入 action 執行):
|
|
175
|
+
|
|
176
|
+
- **Loading**:非同步 action(批次刪除 / 匯出)進行中,對應 action Button 傳 `loading`(既有 API,spinner + 自動 disabled)——不在 bar 內另加全域 spinner
|
|
177
|
+
- **防重複提交**:同一批次操作進行中,consumer 對該 action Button 設 `loading` / `disabled` 阻止連點觸發多次(語義同 `../Field/form-validation.spec.md` double-submit 防護,但 owner 在 consumer 而非 bar)
|
|
178
|
+
- **選取保留**:操作進行中 `selection` 維持不變、bar 持續可見;**成功**後才由 consumer 清空 `selection`(觸發 bar 自動隱藏),**失敗**則保留選取讓 user 重試
|
|
179
|
+
|
|
180
|
+
對齊 Polaris BulkActions(action `loading` 由 consumer 提供)/ Material DataGrid batch action 慣例。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
|
|
181
|
+
|
|
172
182
|
### Extend dataset pattern(totalSelected)
|
|
173
183
|
|
|
174
184
|
「本頁全選 → hint 點擊 → 擴選整個 dataset」2-step 後,consumer 把 `totalSelected` 設為 dataset 真總數,count 區改顯示該值(否則 fallback `selection.length`)——避免 Alert 顯「已選 5370」但 bar 仍顯「已選 50」的不同步(2026-05-13 ship)。對齊 Gmail / Linear / Notion 全選 dataset hint pattern。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
|
|
@@ -183,7 +193,7 @@ interface BulkActionBarLabels {
|
|
|
183
193
|
- count 文字用 `aria-live="polite"` + `aria-atomic="true"`(selection 變更時 SR 整句重讀「已選 3 項」)
|
|
184
194
|
- Clear button:`aria-label="清除選取"`
|
|
185
195
|
- Hint banner 用 `role="status"` + `aria-live="polite"`(state 切換時通知)
|
|
186
|
-
- 鍵盤:Esc → `
|
|
196
|
+
- 鍵盤:Esc → `onClearSelection()`(consumer 應監聽 page-level keydown 觸發);Tab 序按 DOM 順序 = clear(X)→ actions(count 是純文字 span,非互動元素 → 不參與 Tab)
|
|
187
197
|
- Disabled action(無權限等)用 Button `disabled` + tooltip 解釋,**不藏 action**(避免 user 困惑)
|
|
188
198
|
|
|
189
199
|
---
|
|
@@ -194,7 +204,7 @@ interface BulkActionBarLabels {
|
|
|
194
204
|
- **底色**:**無底色 contrast**,跟 page 同色(`bg-canvas` / `bg-surface` 視 placement 繼承)。對齊 Notion / Linear minimalist — 用文字內容切換呈現「mode」,**不**用底色 highlight。**不像 Polaris 那種顯著底色變化** <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
|
|
195
205
|
- **邊界**:**無外框邊界**(融入 page chrome)— 恆 **`border-top` border-divider 切割 layout**(bar 是 page 結構,不是 floating overlay,不用 box-shadow 製造「浮層」誤導)。top-toolbar 變體為未來項(見「Size canonical」)
|
|
196
206
|
- **與 table 的關係**:inline composition — bar 接在 DataTable 下方,toolbar 永遠保留(見「Placement」)
|
|
197
|
-
- **Action variant
|
|
207
|
+
- **Action variant**:**一律 `tertiary`**(含 destructive,icon+label 辨識危險)— **不用 primary、不用 danger 紅填色**(紅色強調留給 dialog 內確認最終 action,見「Slot」rationale)
|
|
198
208
|
|
|
199
209
|
---
|
|
200
210
|
|
|
@@ -21,13 +21,13 @@ export const Default: Story = {
|
|
|
21
21
|
return (
|
|
22
22
|
<BulkActionBar
|
|
23
23
|
selection={selection}
|
|
24
|
-
|
|
24
|
+
onClearSelection={() => setSelection([])}
|
|
25
25
|
actions={
|
|
26
26
|
<>
|
|
27
27
|
<Button variant="tertiary" size="md" startIcon={Archive}>封存</Button>
|
|
28
28
|
<Button variant="tertiary" size="md" startIcon={TagIcon}>加標籤</Button>
|
|
29
29
|
<Button variant="tertiary" size="md" startIcon={MoveRight}>移動</Button>
|
|
30
|
-
<Button variant="tertiary" size="md" startIcon={Trash2}
|
|
30
|
+
<Button variant="tertiary" size="md" startIcon={Trash2}>刪除</Button>
|
|
31
31
|
</>
|
|
32
32
|
}
|
|
33
33
|
/>
|
|
@@ -35,26 +35,10 @@ export const Default: Story = {
|
|
|
35
35
|
},
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const [selection, setSelection] = useState<string[]>(['issue-1', 'issue-2', 'issue-3'])
|
|
43
|
-
return (
|
|
44
|
-
<BulkActionBar
|
|
45
|
-
selection={selection}
|
|
46
|
-
onClear={() => setSelection([])}
|
|
47
|
-
hiddenByFilter={2}
|
|
48
|
-
actions={
|
|
49
|
-
<>
|
|
50
|
-
<Button variant="tertiary" size="md" startIcon={Archive}>封存</Button>
|
|
51
|
-
<Button variant="tertiary" size="md" startIcon={Trash2} danger>刪除</Button>
|
|
52
|
-
</>
|
|
53
|
-
}
|
|
54
|
-
/>
|
|
55
|
-
)
|
|
56
|
-
},
|
|
57
|
-
}
|
|
38
|
+
// @story-trait-rationale: WithFilterHidden(selection=3 + hiddenByFilter=2)retired 2026-07-17 per audit Dim 24 —
|
|
39
|
+
// principles.stories.tsx HintBannerRule 第三 Rule 已用同情境(selection 3 + hiddenByFilter=2)且多教「filter hidden
|
|
40
|
+
// inline 進 count 區、不開 hint banner」規則;展示版無新增約束(earn-existence 2-test 雙 NO)→ retire。
|
|
41
|
+
// hiddenByFilter inline 呈現 canonical home = principles HintBannerRule。
|
|
58
42
|
|
|
59
43
|
// Hint banner via Alert primitive(擴 dataset 提示)— 對齊 ref 圖
|
|
60
44
|
// hint banner 唯一 trigger condition:本頁全選 + 還有 dataset 沒選到
|
|
@@ -108,11 +92,11 @@ export const WithExtendDatasetHint: Story = {
|
|
|
108
92
|
<BulkActionBar
|
|
109
93
|
selection={selection}
|
|
110
94
|
totalSelected={allSelected ? TOTAL : undefined}
|
|
111
|
-
|
|
95
|
+
onClearSelection={() => { setSelection([]); setAllSelected(false) }}
|
|
112
96
|
actions={
|
|
113
97
|
<>
|
|
114
98
|
<Button variant="tertiary" size="md" startIcon={Download}>下載</Button>
|
|
115
|
-
<Button variant="tertiary" size="md" startIcon={Trash2}
|
|
99
|
+
<Button variant="tertiary" size="md" startIcon={Trash2}>刪除</Button>
|
|
116
100
|
</>
|
|
117
101
|
}
|
|
118
102
|
/>
|
|
@@ -41,7 +41,7 @@ export interface BulkActionBarProps extends React.HTMLAttributes<HTMLDivElement>
|
|
|
41
41
|
/** 已選 ID,length === 0 時自動隱藏(回傳 null) */
|
|
42
42
|
selection: readonly string[]
|
|
43
43
|
/** Clear 觸發,user 點 X icon 或 Esc(consumer 在 page-level 監聽) */
|
|
44
|
-
|
|
44
|
+
onClearSelection?: () => void
|
|
45
45
|
/** 批次 actions(consumer 提供 md Button,variant=tertiary 或 tertiary+danger;不用 primary) */
|
|
46
46
|
actions?: React.ReactNode
|
|
47
47
|
/** Filter 模式:hidden 數量,顯示在 count 區 inline「已選 {N} 項 · {M} 個被 filter 隱藏」 */
|
|
@@ -77,7 +77,7 @@ export interface BulkActionBarProps extends React.HTMLAttributes<HTMLDivElement>
|
|
|
77
77
|
|
|
78
78
|
const BulkActionBar = React.forwardRef<HTMLDivElement, BulkActionBarProps>(
|
|
79
79
|
function BulkActionBar(
|
|
80
|
-
{ selection,
|
|
80
|
+
{ selection, onClearSelection, actions, hiddenByFilter, totalSelected, labels: labelsOverride, className, ...props },
|
|
81
81
|
ref
|
|
82
82
|
) {
|
|
83
83
|
const labels: BulkActionBarLabels = React.useMemo(
|
|
@@ -109,7 +109,7 @@ const BulkActionBar = React.forwardRef<HTMLDivElement, BulkActionBarProps>(
|
|
|
109
109
|
{/* X close — md dismiss(2026-05-04 spec update:default placement = footer variant,
|
|
110
110
|
visual weight 對齊 Dialog footer commitment buttons md;same-row consistency 維持)
|
|
111
111
|
未來若有 top-toolbar variant(覆蓋 sm-density toolbar)→ 該 variant override sm */}
|
|
112
|
-
{
|
|
112
|
+
{onClearSelection && (
|
|
113
113
|
<Button
|
|
114
114
|
variant="text"
|
|
115
115
|
size="md"
|
|
@@ -117,7 +117,7 @@ const BulkActionBar = React.forwardRef<HTMLDivElement, BulkActionBarProps>(
|
|
|
117
117
|
dismiss
|
|
118
118
|
startIcon={X}
|
|
119
119
|
aria-label={labels.clear}
|
|
120
|
-
onClick={
|
|
120
|
+
onClick={onClearSelection}
|
|
121
121
|
/>
|
|
122
122
|
)}
|
|
123
123
|
|
|
@@ -23,14 +23,14 @@ import { ButtonGroupContext } from './button'
|
|
|
23
23
|
* </ButtonGroup>
|
|
24
24
|
*
|
|
25
25
|
* // 垂直排列(fullWidth 自動套用)
|
|
26
|
-
* <ButtonGroup
|
|
26
|
+
* <ButtonGroup orientation="vertical">
|
|
27
27
|
* <Button variant="primary">確認</Button>
|
|
28
28
|
* <Button variant="tertiary">取消</Button>
|
|
29
29
|
* </ButtonGroup>
|
|
30
30
|
*/
|
|
31
31
|
interface ButtonGroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
32
32
|
/** 排列方向,預設 horizontal */
|
|
33
|
-
|
|
33
|
+
orientation?: 'horizontal' | 'vertical'
|
|
34
34
|
/** 水平對齊,預設 start */
|
|
35
35
|
align?: 'start' | 'center' | 'end'
|
|
36
36
|
}
|
|
@@ -40,8 +40,8 @@ const BUTTON_GROUP_CTX_VERTICAL = { fullWidth: true } as const
|
|
|
40
40
|
const BUTTON_GROUP_CTX_HORIZONTAL = { fullWidth: false } as const
|
|
41
41
|
|
|
42
42
|
const ButtonGroup = React.forwardRef<HTMLDivElement, ButtonGroupProps>(
|
|
43
|
-
({
|
|
44
|
-
const isVertical =
|
|
43
|
+
({ orientation = 'horizontal', align = 'start', className, children, ...props }, ref) => {
|
|
44
|
+
const isVertical = orientation === 'vertical'
|
|
45
45
|
|
|
46
46
|
return (
|
|
47
47
|
<ButtonGroupContext.Provider value={isVertical ? BUTTON_GROUP_CTX_VERTICAL : BUTTON_GROUP_CTX_HORIZONTAL}>
|
|
@@ -102,6 +102,23 @@ const VARIANT_DESC: Record<VariantKey, string> = {
|
|
|
102
102
|
link: '外觀像連結的按鈕,不用於操作列',
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
// variant 名稱只放在左側 mono 標籤欄;按鈕本體用真實動作文案(禁 variant 名當 label)
|
|
106
|
+
const VARIANT_LABEL: Record<VariantKey, string> = {
|
|
107
|
+
primary: '新增任務',
|
|
108
|
+
secondary: '儲存變更',
|
|
109
|
+
tertiary: '取消',
|
|
110
|
+
text: '略過此步',
|
|
111
|
+
link: '查看說明',
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const DANGER_LABEL: Record<VariantKey, string> = {
|
|
115
|
+
primary: '刪除專案',
|
|
116
|
+
secondary: '移除成員',
|
|
117
|
+
tertiary: '清除紀錄',
|
|
118
|
+
text: '撤銷授權',
|
|
119
|
+
link: '刪除',
|
|
120
|
+
}
|
|
121
|
+
|
|
105
122
|
/* ═══════════════════════════════════════════════════════════════════════════
|
|
106
123
|
Shared UI Components
|
|
107
124
|
|
|
@@ -224,7 +241,8 @@ export const Overview = {
|
|
|
224
241
|
<div className="flex flex-col gap-2">
|
|
225
242
|
{VARIANTS.map((v) => (
|
|
226
243
|
<div key={v} className="flex items-center gap-4">
|
|
227
|
-
<
|
|
244
|
+
<span className="w-16 shrink-0 text-[11px] font-mono text-fg-muted">{v}</span>
|
|
245
|
+
<div className="w-28 shrink-0"><Button variant={v} size="sm" startIcon={Plus}>{VARIANT_LABEL[v]}</Button></div>
|
|
228
246
|
<span className="text-caption text-fg-secondary">{VARIANT_DESC[v]}</span>
|
|
229
247
|
</div>
|
|
230
248
|
))}
|
|
@@ -232,7 +250,8 @@ export const Overview = {
|
|
|
232
250
|
<span className="text-[11px] text-fg-muted font-medium">+ danger prop</span>
|
|
233
251
|
{DANGER_VARIANTS.map((v) => (
|
|
234
252
|
<div key={v} className="flex items-center gap-4">
|
|
235
|
-
<
|
|
253
|
+
<span className="w-16 shrink-0 text-[11px] font-mono text-fg-muted">{v}</span>
|
|
254
|
+
<div className="w-28 shrink-0"><Button variant={v} danger size="sm" startIcon={Trash2}>{DANGER_LABEL[v]}</Button></div>
|
|
236
255
|
<span className="text-caption text-fg-secondary">
|
|
237
256
|
{v === 'primary' ? '立即不可逆——點下去就執行' : v === 'secondary' ? '有警示意圖,後面還有確認' : '低強調的危險操作'}
|
|
238
257
|
</span>
|
|
@@ -503,7 +522,7 @@ export const ColorMatrix = {
|
|
|
503
522
|
<td className="p-3 border-b border-divider font-mono text-caption font-medium align-top">{v}</td>
|
|
504
523
|
{STATES.map((st) => (
|
|
505
524
|
<td key={st} className="p-3 border-b border-divider align-top min-w-[160px]">
|
|
506
|
-
<Button variant={v} size="sm" startIcon={Plus} disabled={st === 'disabled'}>{v}</Button>
|
|
525
|
+
<Button variant={v} size="sm" startIcon={Plus} disabled={st === 'disabled'}>{VARIANT_LABEL[v]}</Button>
|
|
507
526
|
<TokenAnnotation colors={TOKEN_MAP[v][st]} />
|
|
508
527
|
</td>
|
|
509
528
|
))}
|
|
@@ -523,7 +542,7 @@ export const ColorMatrix = {
|
|
|
523
542
|
<td className="p-3 border-b border-divider font-mono text-caption font-medium align-top">{v} + danger</td>
|
|
524
543
|
{STATES.map((st) => (
|
|
525
544
|
<td key={st} className="p-3 border-b border-divider align-top min-w-[160px]">
|
|
526
|
-
<Button variant={v} danger size="sm" startIcon={Trash2} disabled={st === 'disabled'}>{v}</Button>
|
|
545
|
+
<Button variant={v} danger size="sm" startIcon={Trash2} disabled={st === 'disabled'}>{DANGER_LABEL[v]}</Button>
|
|
527
546
|
<TokenAnnotation colors={DANGER_MAP[v]![st]} />
|
|
528
547
|
</td>
|
|
529
548
|
))}
|
|
@@ -612,10 +631,10 @@ export const SizeMatrix = {
|
|
|
612
631
|
<tr key={v}>
|
|
613
632
|
<Td mono>{v}</Td>
|
|
614
633
|
{SIZES.map((sz) => (
|
|
615
|
-
<Td key={sz}><Button variant={v} size={sz} startIcon={Plus}>{v}</Button></Td>
|
|
634
|
+
<Td key={sz}><Button variant={v} size={sz} startIcon={Plus}>{VARIANT_LABEL[v]}</Button></Td>
|
|
616
635
|
))}
|
|
617
636
|
{SIZES.map((sz) => (
|
|
618
|
-
<Td key={`io-${sz}`}><Button variant={v} size={sz} iconOnly startIcon={Plus} aria-label={v} /></Td>
|
|
637
|
+
<Td key={`io-${sz}`}><Button variant={v} size={sz} iconOnly startIcon={Plus} aria-label={VARIANT_LABEL[v]} /></Td>
|
|
619
638
|
))}
|
|
620
639
|
</tr>
|
|
621
640
|
))}
|
|
@@ -661,7 +680,7 @@ export const StateBehavior = {
|
|
|
661
680
|
<div className="flex flex-wrap gap-3">
|
|
662
681
|
{VARIANTS.map((v) => (
|
|
663
682
|
<div key={v} className="flex flex-col items-center gap-1">
|
|
664
|
-
<Button variant={v} disabled startIcon={Plus}>{v}</Button>
|
|
683
|
+
<Button variant={v} disabled startIcon={Plus}>{VARIANT_LABEL[v]}</Button>
|
|
665
684
|
<span className="text-[10px] text-fg-muted">{v}</span>
|
|
666
685
|
</div>
|
|
667
686
|
))}
|