@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
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
List, LayoutGrid, Calendar,
|
|
7
7
|
} from 'lucide-react'
|
|
8
8
|
import { SegmentedControl, SegmentedControlItem } from './segmented-control'
|
|
9
|
-
import { Tabs, TabsList, TabsTrigger } from '@/design-system/components/Tabs/tabs'
|
|
9
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/design-system/components/Tabs/tabs'
|
|
10
10
|
import { Button } from '@/design-system/components/Button/button'
|
|
11
11
|
import { Badge } from '@/design-system/components/Badge/badge'
|
|
12
12
|
|
|
@@ -87,6 +87,9 @@ export const UsageGuidance: Story = {
|
|
|
87
87
|
<TabsTrigger value="customers">顧客</TabsTrigger>
|
|
88
88
|
<TabsTrigger value="products">產品</TabsTrigger>
|
|
89
89
|
</TabsList>
|
|
90
|
+
<TabsContent value="orders" className="text-body text-fg-muted mt-2">訂單列表、篩選與批次出貨(各自的 toolbar / table)</TabsContent>
|
|
91
|
+
<TabsContent value="customers" className="text-body text-fg-muted mt-2">顧客名單、分群與生命週期價值</TabsContent>
|
|
92
|
+
<TabsContent value="products" className="text-body text-fg-muted mt-2">商品目錄、庫存與上下架狀態</TabsContent>
|
|
90
93
|
</Tabs>
|
|
91
94
|
</div>
|
|
92
95
|
</Rule>
|
|
@@ -62,17 +62,11 @@ SegmentedControl 是**互斥多選一的 compact control**——從 2–5 個選
|
|
|
62
62
|
|
|
63
63
|
---
|
|
64
64
|
|
|
65
|
-
## 與 Tabs
|
|
65
|
+
## 與 Tabs 的分界
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
**完整判斷 SSOT 在 `../Tabs/tabs.spec.md` 的「Tabs 與 SegmentedControl 的分界」段落**(三角度判斷:規模 / 角色 / 值生命週期 + 灰色地帶對照表)——以該段為 single source of truth,此處不重複列舉、也不平行維護,避免漂移。
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
2. **結構角色**:SegmentedControl 是 **control**(可跟 Button / Input 並排),Tabs 是 **container 的結構元件**(跨越父容器整行)
|
|
71
|
-
3. **值生命週期**:SegmentedControl 的值常綁 form state / URL param / 會被持久化,Tabs 的切換通常不進表單狀態
|
|
72
|
-
|
|
73
|
-
**Fallback**:跟 Input / Button 並排不違和 → SegmentedControl;必須佔一整行當 section header → Tabs。
|
|
74
|
-
|
|
75
|
-
**完整灰色地帶對照表在 `tabs.spec.md` 的「Tabs 與 SegmentedControl 的分界」段落**——以該段為 single source of truth,此處不重複列舉。
|
|
69
|
+
本元件視角的一句話界線:切「局部內容的變體」(單一 chart / list / form section,可跟 Button / Input 並排)用 SegmentedControl;切「整塊 container」(有自己的 header / toolbar / 多個 section、必須佔一整行當 section header)改用 Tabs。
|
|
76
70
|
|
|
77
71
|
---
|
|
78
72
|
|
|
@@ -240,6 +234,7 @@ Items 之間 `-ml-px`(除了第一個)讓相鄰 border 重疊、視覺上只
|
|
|
240
234
|
- **個別 item disabled**:Radix roving focus 跳過該 item(`focusable: !disabled`),方向鍵不停留;disabled item 不得是當前 value(contract,見「狀態 › disabled」)
|
|
241
235
|
- **所有 item disabled**:整組無可聚焦 item,Radix roving focus 將 group 的 tab 停留點設為 `tabIndex=-1`,Tab 直接跳過
|
|
242
236
|
- **disabled × fullWidth 並存**:兩者正交——fullWidth 只控寬度等分,disabled 只控互動與色彩,無交互規則
|
|
237
|
+
- **表單驗證 / invalid**:SegmentedControl **恆有一值**(radio 語意),故「必填未選」的 required 驗證天然不適用;元件**無 `invalid` / `error` prop**,不渲染 error 邊框或 `aria-invalid`(它讀 Field context 的 `disabled` / `size`,但**不讀 `invalid`**,與 Input / Select 等可空控件不同)。值的業務合法性(是否落在允許選項)由 consumer 保證,需要 invalid 視覺提示時由外層 Field / consumer 承擔——屬 consumer-owned,非本元件狀態
|
|
243
238
|
|
|
244
239
|
---
|
|
245
240
|
|
|
@@ -269,7 +264,7 @@ Items 之間 `-ml-px`(除了第一個)讓相鄰 border 重疊、視覺上只
|
|
|
269
264
|
|
|
270
265
|
- `../Button/button.spec.md` — item 內部結構與 size 系統的來源
|
|
271
266
|
- `../Tabs/tabs.spec.md` — container 切換 vs control 切換的判斷(完整分界 SSOT 在 Tabs spec「Tabs 與 SegmentedControl 的分界」段落)
|
|
272
|
-
- `../Field/field-controls.spec.md` — SegmentedControl 作為 Field control 時的 size
|
|
267
|
+
- `../Field/field-controls.spec.md` — SegmentedControl 作為 Field control 時的 `size` / `disabled` 繼承機制(透過 `useResolvedFieldSize` / `useResolvedFieldDisabled`);**不**繼承 `invalid` 驗證軸——表單驗證契約見上「規模限制與邊界案例 › 表單驗證 / invalid」(恆有一值,required / invalid 為 N/A 或 consumer-owned)
|
|
273
268
|
- `../Checkbox/checkbox.spec.md` — RadioGroup 共用規則(選項數量多或需要描述文字時的替代)
|
|
274
269
|
|
|
275
270
|
## A11y 預設
|
|
@@ -15,8 +15,9 @@ import { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'
|
|
|
15
15
|
* 內部 item 結構鏡射 Button,但兩者型別獨立。
|
|
16
16
|
*
|
|
17
17
|
* ── 定位 ──
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* 互斥多選一的 compact control;選值可驅動下方內容變化(ViewSwitcher 合法——
|
|
19
|
+
* 「切 value 不能切 view」是錯的二分法,spec「定位」段)。塞得進 Field,Tabs 不行。
|
|
20
|
+
* 與 Tabs 分界看規模 / 結構角色(spec「與 Tabs 的分界」段);單一 on/off → Button pressed;>5 個選項 → Select。
|
|
20
21
|
*
|
|
21
22
|
* ── Size(對齊 field-height / Button 系列)──
|
|
22
23
|
* xs h-field-xs(24 固定)
|
|
@@ -217,7 +218,9 @@ const itemVariants = cva(
|
|
|
217
218
|
// 但當 group iconOnly = true 時,每個 item 必須提供 startIcon + aria-label——
|
|
218
219
|
// 這是語意契約,在 render 階段檢查並在 dev mode 發出警告(TS 層做不到這層檢查)。
|
|
219
220
|
export interface SegmentedControlItemProps
|
|
220
|
-
|
|
221
|
+
// asChild Omit(2026-07-18 決策2):item 固定渲染 [startIcon?, <span>{children}</span>, badge?]
|
|
222
|
+
// 多節點陣列 → Radix Slot React.Children.only 恆 crash。children(label)保留合法。對齊 Chip 收窄。
|
|
223
|
+
extends Omit<React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item>, 'asChild'> {
|
|
221
224
|
/** 左側 icon(LucideIcon)。group iconOnly = true 時必填。 */
|
|
222
225
|
startIcon?: LucideIcon
|
|
223
226
|
/** 右側 suffix badge(通常是計數指示器)。group iconOnly = true 時會被忽略。 */
|
|
@@ -258,9 +258,15 @@ export const Overview = {
|
|
|
258
258
|
<div className="flex flex-col gap-2 items-start">
|
|
259
259
|
<span className="text-[11px] text-fg-muted font-medium">edit</span>
|
|
260
260
|
<div className="inline-flex items-center border-2 border-dashed border-primary/30 rounded-md px-3 py-2.5 gap-2">
|
|
261
|
-
{
|
|
262
|
-
|
|
263
|
-
|
|
261
|
+
{/* turquoise/magenta 為 primitive(無 semantic token)→ 顯式 --color-{hue}-{6,1,7}(ProgressBar 同款修法) */}
|
|
262
|
+
{[
|
|
263
|
+
{ name: 'Tag', border: 'var(--color-turquoise-6)', bg: 'var(--color-turquoise-1)', text: 'var(--color-turquoise-7)' },
|
|
264
|
+
{ name: 'Tag', border: 'var(--success)', bg: 'var(--success-subtle)', text: 'var(--success)' },
|
|
265
|
+
{ name: 'Tag', border: 'var(--info)', bg: 'var(--info-subtle)', text: 'var(--info)' },
|
|
266
|
+
{ name: 'Tag', border: 'var(--color-magenta-6)', bg: 'var(--color-magenta-1)', text: 'var(--color-magenta-7)' },
|
|
267
|
+
].map((s, i) => (
|
|
268
|
+
<span key={i} className="rounded px-2 py-1 text-[11px] font-mono border border-dashed"
|
|
269
|
+
style={{ borderColor: s.border, backgroundColor: s.bg, color: s.text }}>{s.name}</span>
|
|
264
270
|
))}
|
|
265
271
|
</div>
|
|
266
272
|
<span className="text-[10px] text-fg-muted font-mono">Tag: pointer-events-none · 點擊穿透到觸發點開選單</span>
|
|
@@ -304,7 +310,7 @@ export const Overview = {
|
|
|
304
310
|
<thead><tr><Th>Prop</Th><Th>Type</Th><Th>Default</Th><Th>說明</Th></tr></thead>
|
|
305
311
|
<tbody>
|
|
306
312
|
{[
|
|
307
|
-
['mode', "'edit'|'
|
|
313
|
+
['mode', "'edit'|'view'|'readonly'|'disabled'", "'edit'", 'FieldMode 四模式——edit 可編輯 / view 純展示 / readonly 顯示值不可改 / disabled 灰化'],
|
|
308
314
|
['variant', "'default'", "'default'", 'Field chrome variant(預設繼承 context.variant;naked = @internal cell-as-input,2026-07-14 型別收窄至 FieldVariantInternal)'],
|
|
309
315
|
['display', "'plain'|'tag'", "'plain'", '顯示模式——plain 純文字,tag 用 Tag 元件呈現'],
|
|
310
316
|
['size', "'sm'|'md'|'lg'", "'md'", '尺寸,與 Button 同 size 並排高度一致'],
|
|
@@ -316,14 +322,14 @@ export const Overview = {
|
|
|
316
322
|
['placeholder', 'string', '—', '未選值時的提示文字'],
|
|
317
323
|
['clearable', 'boolean', 'false', '有值時顯示 clear 按鈕(僅 edit 模式)'],
|
|
318
324
|
['searchable', 'boolean', 'false', '啟用搜尋(desktop 時 field 變 input,打字即篩選)'],
|
|
319
|
-
['loading', 'boolean', 'false', 'dropdown
|
|
325
|
+
['loading', 'boolean', 'false', 'dropdown 開啟且無可顯示選項時,empty slot 顯 CircularProgress(已有選項時保留顯示、不取代;對齊 spec「Loading」2026-07-04 拍板)'],
|
|
320
326
|
['minRows', 'number', '3', 'menu list 最小列數(空 / 選項少時視覺一致)'],
|
|
321
327
|
['startIcon', 'LucideIcon', '—', '左側 field-level 指示 icon(muted,僅 plain 模式;代表 value 的圖示走 option.icon)'],
|
|
322
328
|
['error', 'boolean', 'false', '紅色邊框 + aria-invalid(僅 edit 模式有視覺效果)'],
|
|
323
329
|
['defaultOpen', 'boolean', 'false', 'uncontrolled 初始開啟(DataTable cell click→1 step open)'],
|
|
324
330
|
['onOpenChange', '(open: boolean) => void', '—', 'open 狀態變更 callback'],
|
|
325
|
-
['showDisplayEndIcon', 'boolean', 'false', '
|
|
326
|
-
['selectedItemRenderer', '(opt: SelectOption) => ReactNode', '—', '已選項目客製 render;4 mode 共享(edit/
|
|
331
|
+
['showDisplayEndIcon', 'boolean', 'false', 'view 模式渲 ChevronDown(opt-in 逃生門;2026-07-08 A 案後 DataTable 預設不傳 — view 態零恆顯 icon)'],
|
|
332
|
+
['selectedItemRenderer', '(opt: SelectOption) => ReactNode', '—', '已選項目客製 render;4 mode 共享(edit/view/readonly/disabled,contract (a))— view 態渲值內容無 chrome(PeoplePicker / status 欄用)'],
|
|
327
333
|
['disabled', 'boolean', '—', '原生屬性;未傳 mode 時 resolve 為 disabled 樣式(顯式 mode prop 恆優先,見 useResolvedFieldMode)'],
|
|
328
334
|
].map(([p, t, d, desc]) => (
|
|
329
335
|
<tr key={p}><Td mono>{p}</Td><Td mono>{t}</Td><Td mono>{d}</Td><Td>{desc}</Td></tr>
|
|
@@ -70,10 +70,10 @@ export const UsageGuidance: Story = {
|
|
|
70
70
|
<LinkTo kind="Design System/Components/Select/展示" name="四模式"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">四模式</span></LinkTo>
|
|
71
71
|
</li>
|
|
72
72
|
<li>
|
|
73
|
-
<LinkTo kind="Design System/Components/Select/展示" name="
|
|
73
|
+
<LinkTo kind="Design System/Components/Select/展示" name="三種尺寸"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">尺寸與 Button 對齊</span></LinkTo>
|
|
74
74
|
</li>
|
|
75
75
|
<li>
|
|
76
|
-
<LinkTo kind="Design System/Components/Select/展示" name="
|
|
76
|
+
<LinkTo kind="Design System/Components/Select/展示" name="建立新選項"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">建立新選項</span></LinkTo>
|
|
77
77
|
</li>
|
|
78
78
|
<li>
|
|
79
79
|
<LinkTo kind="Design System/Components/Select/展示" name="可清除"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">可清除</span></LinkTo>
|
|
@@ -276,7 +276,7 @@ export const SearchableRule: Story = {
|
|
|
276
276
|
|
|
277
277
|
<Rule
|
|
278
278
|
title="次要啟發:數量 > 50 幾乎必開(但仍看 label 性質)"
|
|
279
|
-
note="100
|
|
279
|
+
note="100 個依字母排序的國家名不需要搜尋(自然語言掃視快,手機原生 select 還有 type-to-jump 直達),5 個產品代碼(SKU-4837)反而需要搜尋。純數量 threshold 會誤判這兩端,詳見 spec"
|
|
280
280
|
>
|
|
281
281
|
<Label>判斷優先序:label 性質 → 數量次要啟發</Label>
|
|
282
282
|
</Rule>
|
|
@@ -222,6 +222,14 @@ Select 的值套用時機是**由 onChange handler 的副作用決定**,不是
|
|
|
222
222
|
|
|
223
223
|
純量化規則會誤判這兩端。label 性質是唯一可靠的主判準。
|
|
224
224
|
|
|
225
|
+
### 建立新選項(`creatable`,2026-07-18 決策11)
|
|
226
|
+
|
|
227
|
+
- `creatable` **需搭配 `searchable`**——搜尋字串非空且無完全同名既有選項時,dropdown 底部出現 create row(`Plus` icon + `createLabel`,預設「直接使用「{query}」」),點擊觸發 `onCreate(query)`。
|
|
228
|
+
- **邏輯 / 顯示 / 互動 SSOT 住在 `SelectMenu`**(與 `Combobox.creatable` 同一底層):Select 的 trigger 內嵌搜尋 input 以**受控 `search`** 驅動 SelectMenu 的 create-row 顯隱(`SelectMenu.search` prop,選配受控;不傳 = 內部 uncontrolled,既有 consumer 零影響)。
|
|
229
|
+
- `onCreate` 由 consumer 實作(通常:加進 options + 選取新值);create row 選取後 SelectMenu 清 search(受控時回呼 `onSearchChange`)。
|
|
230
|
+
- **非 searchable 時無 create 通道**(沒有輸入處);`creatable` 單獨傳無效。
|
|
231
|
+
- 何時用:label 是使用者自訂的自由文字集合(標籤 / 分類 / 專案名),清單非窮舉、允許擴充。何時不用:固定 enum(狀態 / 角色 / 國家)——不該讓使用者新增。
|
|
232
|
+
|
|
225
233
|
---
|
|
226
234
|
|
|
227
235
|
## 顯示模式(`display` prop)
|
|
@@ -233,7 +241,7 @@ Select 的值套用時機是**由 onChange handler 的副作用決定**,不是
|
|
|
233
241
|
|
|
234
242
|
**命名 rationale**(2026-05-01 從 `'text'` 改 `'plain'`):跨元件 prop value 衝突防避 — `Button variant="text"` 表「無 chrome 文字按鈕」,跟 Select.display 的「純文字呈現」是兩個不同概念,共用 `'text'` 字面值會造成 consumer 認知衝突(命名三 test 第 3 條)。改 `'plain'` 後語意專屬:plain = 樸素文字呈現,對立 tag = 用 Tag 元件呈現。
|
|
235
243
|
|
|
236
|
-
**`selectedItemRenderer` 優先於兩種顯示模式**(2026-07-08 A 案回歸修正):設定時已選值改渲 consumer renderer 輸出(status icon+text / PersonDisplay 等),且 **edit /
|
|
244
|
+
**`selectedItemRenderer` 優先於兩種顯示模式**(2026-07-08 A 案回歸修正):設定時已選值改渲 consumer renderer 輸出(status icon+text / PersonDisplay 等),且 **edit / view / readonly / disabled 4 mode 共享同一 renderer**(`field-controls.spec.md` 共享 contract (a),禁 edit-only)。renderer 輸出屬「值內容」— `mode="view"` 照常渲染(無 chrome 無 chevron);與 affordance(chevron/outline)分層見 `field.spec.md` L6。
|
|
237
245
|
|
|
238
246
|
### plain 模式
|
|
239
247
|
|
|
@@ -345,6 +353,7 @@ Select 是 **Field Controls family 成員**——互動狀態(focus / invalid /
|
|
|
345
353
|
|
|
346
354
|
- `checkbox.spec.md`
|
|
347
355
|
- `combobox.spec.md`
|
|
356
|
+
- `command.spec.md`
|
|
348
357
|
- `people-picker.spec.md`
|
|
349
358
|
- `radio-group.spec.md`
|
|
350
359
|
- `select-menu.spec.md`
|
|
@@ -44,8 +44,8 @@ export const Modes: Story = {
|
|
|
44
44
|
</div>
|
|
45
45
|
{/* @story-trait-rationale: pre-existing trait gaps tracked separately */}
|
|
46
46
|
<div>
|
|
47
|
-
<h3 className="text-body font-bold text-foreground mb-2">
|
|
48
|
-
<Select mode="
|
|
47
|
+
<h3 className="text-body font-bold text-foreground mb-2">view</h3>
|
|
48
|
+
<Select mode="view" options={statusOptions} value={value} aria-label="狀態(view 模式示範)" />
|
|
49
49
|
</div>
|
|
50
50
|
<div>
|
|
51
51
|
<h3 className="text-body font-bold text-foreground mb-2">readonly</h3>
|
|
@@ -97,34 +97,14 @@ export const HugWidth: Story = {
|
|
|
97
97
|
},
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const [textVal, setTextVal] = React.useState('in_stock')
|
|
105
|
-
const [tagVal, setTagVal] = React.useState('in_stock')
|
|
106
|
-
return (
|
|
107
|
-
<div className="flex flex-col gap-6 max-w-xs">
|
|
108
|
-
<div>
|
|
109
|
-
<h3 className="text-body font-bold text-foreground mb-2">plain(預設)</h3>
|
|
110
|
-
<p className="text-caption text-fg-muted mb-2">純文字,readonly/disabled 跟 Input 一致</p>
|
|
111
|
-
<Select options={statusOptions} value={textVal} onChange={setTextVal} />
|
|
112
|
-
<div className="mt-2"><Select mode="readonly" options={statusOptions} value={textVal} /></div>
|
|
113
|
-
</div>
|
|
114
|
-
<div>
|
|
115
|
-
<h3 className="text-body font-bold text-foreground mb-2">tag</h3>
|
|
116
|
-
<p className="text-caption text-fg-muted mb-2">Tag 標籤 + 顏色(tagVariant),readonly/disabled 用 tagPadding</p>
|
|
117
|
-
<Select display="tag" options={statusOptions} value={tagVal} onChange={setTagVal} />
|
|
118
|
-
<div className="mt-2"><Select display="tag" mode="readonly" options={statusOptions} value={tagVal} /></div>
|
|
119
|
-
</div>
|
|
120
|
-
</div>
|
|
121
|
-
)
|
|
122
|
-
},
|
|
123
|
-
}
|
|
100
|
+
// @story-trait-rationale: DisplayMode(plain/tag × edit/readonly)retired 2026-07-17 per audit Dim 24 —
|
|
101
|
+
// 為 anatomy.stories.tsx ColorMatrix(plain/tag × edit/readonly/disabled 完整矩陣)的嚴格子集;
|
|
102
|
+
// plain vs tag 的選用原則另由 principles.stories.tsx DisplayModeRule 承載。展示版無新增約束
|
|
103
|
+
// (earn-existence 2-test 雙 NO)→ retire。display 模式 canonical home = anatomy ColorMatrix + principles DisplayModeRule。
|
|
124
104
|
|
|
125
105
|
/* ── 尺寸與 Button 對齊 ── */
|
|
126
106
|
export const SizeAlignment: Story = {
|
|
127
|
-
name: '
|
|
107
|
+
name: '三種尺寸',
|
|
128
108
|
render: () => {
|
|
129
109
|
const [sm, setSm] = React.useState('in_stock')
|
|
130
110
|
const [md, setMd] = React.useState('in_stock')
|
|
@@ -192,6 +172,40 @@ export const Searchable: Story = {
|
|
|
192
172
|
},
|
|
193
173
|
}
|
|
194
174
|
|
|
175
|
+
/* ── Creatable(搜尋 + 建立新選項)── */
|
|
176
|
+
export const Creatable: Story = {
|
|
177
|
+
name: '建立新選項',
|
|
178
|
+
render: () => {
|
|
179
|
+
const [labels, setLabels] = React.useState([
|
|
180
|
+
{ value: 'bug', label: 'Bug' },
|
|
181
|
+
{ value: 'feature', label: 'Feature' },
|
|
182
|
+
{ value: 'design', label: 'Design' },
|
|
183
|
+
{ value: 'docs', label: 'Docs' },
|
|
184
|
+
])
|
|
185
|
+
const [value, setValue] = React.useState<string>('')
|
|
186
|
+
return (
|
|
187
|
+
<div className="flex flex-col gap-4 max-w-xs">
|
|
188
|
+
<p className="text-caption text-fg-muted">
|
|
189
|
+
searchable + creatable — 搜尋不到既有標籤時,dropdown 底部出現「直接使用「…」」建立列,點擊即新增並選取
|
|
190
|
+
</p>
|
|
191
|
+
<Select
|
|
192
|
+
options={labels}
|
|
193
|
+
value={value}
|
|
194
|
+
onChange={setValue}
|
|
195
|
+
searchable
|
|
196
|
+
creatable
|
|
197
|
+
onCreate={(q) => {
|
|
198
|
+
const newLabel = { value: q.toLowerCase().replace(/\s+/g, '-'), label: q }
|
|
199
|
+
setLabels((prev) => [...prev, newLabel])
|
|
200
|
+
setValue(newLabel.value)
|
|
201
|
+
}}
|
|
202
|
+
placeholder="選擇或建立標籤…"
|
|
203
|
+
/>
|
|
204
|
+
</div>
|
|
205
|
+
)
|
|
206
|
+
},
|
|
207
|
+
}
|
|
208
|
+
|
|
195
209
|
/* ── DataTable 整合 ── */
|
|
196
210
|
export const InDataTable: Story = {
|
|
197
211
|
name: 'DataTable 整合',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.
|
|
2
2
|
// code-quality-allow: file-size — Select 含 3 子元件(NativeSelect/CustomSelect/ReadonlyDisplay)+ helpers + 4-mode renderer + Field SSOT consumption,split-into-files 會破壞 file-local helper closure
|
|
3
|
-
// @renderer-symmetry-allow: 2026-07-08 WM 戰役 A 案回歸修正 — ReadonlyDisplay 現已消費 selectedItemRenderer(
|
|
3
|
+
// @renderer-symmetry-allow: 2026-07-08 WM 戰役 A 案回歸修正 — ReadonlyDisplay 現已消費 selectedItemRenderer(view bare-span / D-path / readonly / disabled 四分支),對齊 field-controls.spec.md 共享 contract (a)「view/readonly/disabled/edit 4 mode 共享同一 renderer」。前 note「display→edit unify deferred」已兌現(值內容層);chrome 結構 unify(D-path)仍為 opt-in showDisplayEndIcon。
|
|
4
4
|
import * as React from 'react'
|
|
5
5
|
import { X, ChevronDown } from 'lucide-react'
|
|
6
6
|
import type { LucideIcon } from 'lucide-react'
|
|
@@ -141,10 +141,10 @@ export interface SelectProps
|
|
|
141
141
|
* DataTable cell-as-input 用:open=false 時 cell 自動 exit edit mode(避免 dismiss 後卡住)。 */
|
|
142
142
|
onOpenChange?: (open: boolean) => void
|
|
143
143
|
/**
|
|
144
|
-
*
|
|
145
|
-
* 預設 false:`mode="
|
|
146
|
-
* `variant="naked" && mode="
|
|
147
|
-
* Field naked-
|
|
144
|
+
* View mode 顯 picker intrinsic end icon(2026-05-08 D path Phase 1)。
|
|
145
|
+
* 預設 false:`mode="view"` 純展示 bare span(向後相容)。
|
|
146
|
+
* `variant="naked" && mode="view"` 場景(DataTable cell)opt-in 設 true → wrap 進
|
|
147
|
+
* Field naked-view + 渲 ChevronDown ItemSuffix。**只 view mode 生效**;readonly /
|
|
148
148
|
* disabled / edit 已有 Field wrapper + suffix(不受此 prop 影響)。
|
|
149
149
|
* Authority:`data-table.spec.md:204` + `inline-action.spec.md:157`「Field family endAction(自動繼承)」。
|
|
150
150
|
* @default false
|
|
@@ -154,9 +154,9 @@ export interface SelectProps
|
|
|
154
154
|
* 「已選項目」客製 render(2026-05-07 v15.5;2026-07-08 A 案回歸修正擴及 4 mode)。
|
|
155
155
|
*
|
|
156
156
|
* 設了 → 已選值不走純文字 / Tag 預設 path,改用 consumer 提供的 ReactNode(收 selectedOpt)。
|
|
157
|
-
* **
|
|
157
|
+
* **view / readonly / disabled / edit 4 mode 共享同一 renderer**(field-controls.spec.md
|
|
158
158
|
* 共享 contract (a),禁 edit-only)— renderer 輸出屬「值內容」(status icon+text 等語意呈現),
|
|
159
|
-
*
|
|
159
|
+
* view 態照常渲染;A 案撤的是 affordance(chevron/outline),非值內容(field.spec.md L6 分層)。
|
|
160
160
|
* Searchable+open 仍走 input(搜尋優先)。Empty value(no selection)仍走 placeholder。
|
|
161
161
|
*
|
|
162
162
|
* 用例:PeoplePicker 用此 slot 把 single 選中的 person render 成 PersonDisplay
|
|
@@ -168,6 +168,16 @@ export interface SelectProps
|
|
|
168
168
|
* 僅 Custom path(searchable dropdown)生效,Native path 無搜尋空狀態。
|
|
169
169
|
* 對齊 MUI noOptionsText / Ant notFoundContent / Polaris emptyState 三家公開覆寫點共識。 */
|
|
170
170
|
emptyText?: string
|
|
171
|
+
/**
|
|
172
|
+
* 可建立新選項(creatable,2026-07-18 決策11 user 拍板 forward 到 Select)。**需搭配 `searchable`**
|
|
173
|
+
* ——搜尋非空且無完全同名既有選項時,dropdown 顯 create row(Plus + `createLabel`)。
|
|
174
|
+
* 邏輯/顯示/互動 SSOT 住在 SelectMenu(Select 的 trigger 搜尋以受控 `search` 驅動 SelectMenu creatable
|
|
175
|
+
* 顯隱)。與 Combobox.creatable 同一底層。非 searchable 時無 create 通道(無輸入處)。 */
|
|
176
|
+
creatable?: boolean
|
|
177
|
+
/** 建立新選項 callback(收使用者輸入的搜尋字串)。 */
|
|
178
|
+
onCreate?: (value: string) => void
|
|
179
|
+
/** create row 的 label 格式,預設 '直接使用「{query}」'(forward SelectMenu SSOT)。 */
|
|
180
|
+
createLabel?: (query: string) => string
|
|
171
181
|
}
|
|
172
182
|
|
|
173
183
|
// ── Icon / size helpers ─────────────────────────────────────────────────────
|
|
@@ -343,7 +353,7 @@ function CustomSelectTriggerContent({
|
|
|
343
353
|
}
|
|
344
354
|
CustomSelectTriggerContent.displayName = 'CustomSelectTriggerContent'
|
|
345
355
|
|
|
346
|
-
// ── Shared readonly/disabled/
|
|
356
|
+
// ── Shared readonly/disabled/view render ─────────────────────────────────
|
|
347
357
|
function ReadonlyDisplay({
|
|
348
358
|
mode, variant: variantProp, width, size, options, value, display, startIcon: StartIcon, className, placeholder, showDisplayEndIcon, selectedItemRenderer,
|
|
349
359
|
}: Pick<SelectProps, 'mode' | 'width' | 'size' | 'options' | 'value' | 'display' | 'startIcon' | 'className' | 'placeholder' | 'showDisplayEndIcon' | 'selectedItemRenderer'> & {
|
|
@@ -355,7 +365,7 @@ function ReadonlyDisplay({
|
|
|
355
365
|
const variant = variantProp ?? 'default'
|
|
356
366
|
const sz = size ?? 'md'
|
|
357
367
|
const iconSize = getIconSize(sz)
|
|
358
|
-
// selectedOpt 提前 hoist(2026-07-08 A 案回歸修正):
|
|
368
|
+
// selectedOpt 提前 hoist(2026-07-08 A 案回歸修正):view / D-path / readonly / disabled
|
|
359
369
|
// 四分支都需要 — renderer 值內容(field-controls.spec.md contract (a) 4-mode 共享)+ tagVariant 查找。
|
|
360
370
|
const selectedOpt = options?.find(o => o.value === value)
|
|
361
371
|
const label = selectedOpt?.label ?? value
|
|
@@ -366,20 +376,20 @@ function ReadonlyDisplay({
|
|
|
366
376
|
const emptyColorCls = fieldEmptyColorClass(resolvedMode)
|
|
367
377
|
const emptyText = placeholder ?? emptyDisplay
|
|
368
378
|
|
|
369
|
-
// mode='
|
|
379
|
+
// mode='view':2 path(2026-05-08 D path Phase 1 Select canary)
|
|
370
380
|
// ❌ 預設(無 showDisplayEndIcon):純內容輸出 bare span/Tag(原行為,backward compat)
|
|
371
381
|
// 對齊原 SelectDisplay sub-component(retired)。readonly / disabled 仍走下方 fieldWrapperStyles。
|
|
372
|
-
// ✅ showDisplayEndIcon=true(DataTable cell opt-in):Field naked-
|
|
382
|
+
// ✅ showDisplayEndIcon=true(DataTable cell opt-in):Field naked-view wrapper +
|
|
373
383
|
// ChevronDown ItemSuffix。SSOT canonical 跟 readonly/edit/disabled mode 同 DOM 結構。
|
|
374
384
|
// Authority: data-table.spec.md:204 + inline-action.spec.md:157「Field family endAction」
|
|
375
|
-
if (resolvedMode === '
|
|
385
|
+
if (resolvedMode === 'view') {
|
|
376
386
|
if (!showDisplayEndIcon) {
|
|
377
|
-
// 2026-05-14 I2 fix(spec contract (e)
|
|
387
|
+
// 2026-05-14 I2 fix(spec contract (e) view typography canonical):bare span 必套
|
|
378
388
|
// `fieldDisplayTextClass(sz)`(sm/md→text-body,lg→text-body-lg)— 對齊跨 Field
|
|
379
|
-
// family
|
|
389
|
+
// family view 視覺尺寸統一。
|
|
380
390
|
if (!value) return <span className={cn(fieldDisplayTextClass(sz), emptyColorCls, className)}>{emptyText}</span>
|
|
381
391
|
// 2026-07-08 A 案回歸修正:selectedItemRenderer(值內容:status icon+text 等語意呈現)
|
|
382
|
-
//
|
|
392
|
+
// view 態照常渲染 — 無 chrome 無 chevron,只有值內容本身。分層原則(field.spec.md L6):
|
|
383
393
|
// 本 renderer 歸「值內容」,恆顯;A 案撤的是 affordance(chevron/outline),非值內容。
|
|
384
394
|
// 對齊 field-controls.spec.md 共享 contract (a)「4 mode 共享同一 renderer(禁 edit-only)」。
|
|
385
395
|
if (selectedItemRenderer && selectedOpt) {
|
|
@@ -393,12 +403,12 @@ function ReadonlyDisplay({
|
|
|
393
403
|
const tVariant = selectedOpt?.tagVariant as 'blue' | 'green' | 'red' | 'yellow' | 'neutral' | undefined
|
|
394
404
|
return <Tag size={sz} color={tVariant} className={className}>{label}</Tag>
|
|
395
405
|
}
|
|
396
|
-
// D path opt-in: Field naked-
|
|
406
|
+
// D path opt-in: Field naked-view wrapper + ItemSuffix ChevronDown
|
|
397
407
|
const tVariant = selectedOpt?.tagVariant as 'blue' | 'green' | 'red' | 'yellow' | 'neutral' | undefined
|
|
398
408
|
return (
|
|
399
409
|
<div
|
|
400
|
-
className={cn(fieldWrapperStyles({ mode: '
|
|
401
|
-
data-field-mode="
|
|
410
|
+
className={cn(fieldWrapperStyles({ mode: 'view', variant, width, size: sz }), value && !isTextDisplay && tagPadding[sz], className)}
|
|
411
|
+
data-field-mode="view"
|
|
402
412
|
>
|
|
403
413
|
{selectedItemRenderer && value && selectedOpt ? (
|
|
404
414
|
// renderer 優先(同 CustomSelectTriggerContent 優先序):值內容歸 renderer,chevron 歸 D-path chrome
|
|
@@ -473,16 +483,16 @@ const NativeSelect = React.forwardRef<HTMLSelectElement, SelectProps>(
|
|
|
473
483
|
// 2026-07-04:Custom-path-only props 顯式 destructure 丟棄(不 spread 到原生 <select> 消 React
|
|
474
484
|
// unknown-prop warning);各 prop docblock 已註「僅 Custom path 生效」語意
|
|
475
485
|
// 2026-07-08 A 案回歸修正:selectedItemRenderer 從丟棄名單移出 — Native path 的
|
|
476
|
-
// ReadonlyDisplay(
|
|
486
|
+
// ReadonlyDisplay(view/readonly/disabled)同樣消費值內容 renderer(contract (a) 4-mode
|
|
477
487
|
// 共享,值內容不因 pointer type 而異);native <select> edit 路徑仍不消費(原生 option 無法客製 render)。
|
|
478
|
-
searchable: _searchable, groups: _groups, loading: _loading, minRows: _minRows, emptyText: _emptyText, defaultOpen: _defaultOpen, onOpenChange: _onOpenChange, selectedItemRenderer,
|
|
488
|
+
searchable: _searchable, groups: _groups, loading: _loading, minRows: _minRows, emptyText: _emptyText, creatable: _creatable, onCreate: _onCreate, createLabel: _createLabel, defaultOpen: _defaultOpen, onOpenChange: _onOpenChange, selectedItemRenderer,
|
|
479
489
|
...props }, ref) => {
|
|
480
490
|
const fieldCtx = useFieldContext()
|
|
481
491
|
const error = useResolvedFieldInvalid(errorProp)
|
|
482
492
|
const disabled = useResolvedFieldDisabled(disabledProp)
|
|
483
493
|
// 2026-05-31 #11:size 從 Field context cascade(對齊 Input/NumberInput + MUI FormControl)
|
|
484
494
|
const size = useResolvedFieldSize(sizeProp)
|
|
485
|
-
// 2026-06-08 SSOT:mode 經 useResolvedFieldMode(prop > 有效 disabled > fieldCtx.mode > 'edit');修 <Field mode="
|
|
495
|
+
// 2026-06-08 SSOT:mode 經 useResolvedFieldMode(prop > 有效 disabled > fieldCtx.mode > 'edit');修 <Field mode="view"> 漏 cascade
|
|
486
496
|
const resolvedMode = useResolvedFieldMode({ mode, disabled })
|
|
487
497
|
const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)
|
|
488
498
|
const iconSize = getIconSize(size)
|
|
@@ -573,7 +583,7 @@ NativeSelect.displayName = 'NativeSelect'
|
|
|
573
583
|
|
|
574
584
|
// code-quality-allow: long-function — foundational composite main body — 拆 sub-fn 會複雜化 local state / ref / context binding
|
|
575
585
|
const CustomSelect = React.forwardRef<HTMLDivElement, SelectProps>(
|
|
576
|
-
({ mode, variant: variantProp, width, error: errorProp = false, size: sizeProp, options, groups, value: valueProp, defaultValue, onChange, placeholder, className, disabled: disabledProp, name, required, form, clearable = false, display = 'plain', startIcon: StartIcon, searchable = false, loading, minRows, emptyText, defaultOpen = false, onOpenChange, selectedItemRenderer, showDisplayEndIcon, id: idProp, 'aria-describedby': ariaDescribedByProp, 'aria-errormessage': ariaErrorMessageProp, 'aria-label': ariaLabel, onKeyDown: onKeyDownProp, style: styleProp,
|
|
586
|
+
({ mode, variant: variantProp, width, error: errorProp = false, size: sizeProp, options, groups, value: valueProp, defaultValue, onChange, placeholder, className, disabled: disabledProp, name, required, form, clearable = false, display = 'plain', startIcon: StartIcon, searchable = false, loading, minRows, emptyText, creatable = false, onCreate, createLabel, defaultOpen = false, onOpenChange, selectedItemRenderer, showDisplayEndIcon, id: idProp, 'aria-describedby': ariaDescribedByProp, 'aria-errormessage': ariaErrorMessageProp, 'aria-label': ariaLabel, onKeyDown: onKeyDownProp, style: styleProp,
|
|
577
587
|
// 2026-07-14 API 策展 D:mobile-only props(allowlist 註記)desktop 顯式丟棄 — div trigger 無原生
|
|
578
588
|
// 對應,不 spread 進 DOM(對稱 NativeSelect 丟棄 custom-path-only props 的既有 pattern)
|
|
579
589
|
autoFocus: _autoFocus, autoComplete: _autoComplete, ...rest }, ref) => {
|
|
@@ -582,7 +592,7 @@ const CustomSelect = React.forwardRef<HTMLDivElement, SelectProps>(
|
|
|
582
592
|
const disabled = useResolvedFieldDisabled(disabledProp)
|
|
583
593
|
// 2026-05-31 #11:size 從 Field context cascade(對齊 Input/NumberInput + MUI FormControl)
|
|
584
594
|
const size = useResolvedFieldSize(sizeProp)
|
|
585
|
-
// 2026-06-08 SSOT:mode 經 useResolvedFieldMode(prop > 有效 disabled > fieldCtx.mode > 'edit');修 <Field mode="
|
|
595
|
+
// 2026-06-08 SSOT:mode 經 useResolvedFieldMode(prop > 有效 disabled > fieldCtx.mode > 'edit');修 <Field mode="view"> 漏 cascade
|
|
586
596
|
const resolvedMode = useResolvedFieldMode({ mode, disabled })
|
|
587
597
|
const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)
|
|
588
598
|
const iconSize = getIconSize(size)
|
|
@@ -622,8 +632,11 @@ const CustomSelect = React.forwardRef<HTMLDivElement, SelectProps>(
|
|
|
622
632
|
const selectedLabel = selectedOpt?.label ?? value ?? ''
|
|
623
633
|
const SelectedIcon = selectedOpt?.icon
|
|
624
634
|
// ── 過濾選項 ──
|
|
625
|
-
|
|
626
|
-
|
|
635
|
+
// 2026-07-18:filter 用 trim 過的 search,對齊 SelectMenu creatable 的 `search.trim()` create-row 判定 —
|
|
636
|
+
// 否則尾隨空白(如 "Bug ")會讓 filter 漏掉完全同名選項、SelectMenu 卻誤判「無同名」提議重複建立。
|
|
637
|
+
const trimmedSearch = search.trim()
|
|
638
|
+
const filteredOptions = searchable && trimmedSearch
|
|
639
|
+
? options.filter(o => o.label.toLowerCase().includes(trimmedSearch.toLowerCase()))
|
|
627
640
|
: options
|
|
628
641
|
// ── 轉換 SelectOption → SelectMenuOption(必在 early return 前) ──
|
|
629
642
|
// Issue 4(2026-05-10):forward avatar / description / disabled SSOT(per SelectMenuOption schema)。
|
|
@@ -661,7 +674,7 @@ const CustomSelect = React.forwardRef<HTMLDivElement, SelectProps>(
|
|
|
661
674
|
[setValue]
|
|
662
675
|
)
|
|
663
676
|
|
|
664
|
-
// Early return AFTER all hooks(disabled / readonly /
|
|
677
|
+
// Early return AFTER all hooks(disabled / readonly / view mode 走 ReadonlyDisplay)
|
|
665
678
|
if (resolvedMode !== 'edit') {
|
|
666
679
|
return <ReadonlyDisplay mode={resolvedMode} variant={variant} width={width} size={size} options={options} value={value} display={display} startIcon={StartIcon} className={className} placeholder={placeholder} showDisplayEndIcon={showDisplayEndIcon} selectedItemRenderer={selectedItemRenderer} />
|
|
667
680
|
}
|
|
@@ -821,6 +834,11 @@ const CustomSelect = React.forwardRef<HTMLDivElement, SelectProps>(
|
|
|
821
834
|
value={value ?? null}
|
|
822
835
|
onValueChange={handleValueChange}
|
|
823
836
|
searchable={false}
|
|
837
|
+
creatable={creatable}
|
|
838
|
+
onCreate={onCreate}
|
|
839
|
+
createLabel={createLabel}
|
|
840
|
+
search={searchable ? search : undefined}
|
|
841
|
+
onSearchChange={searchable ? setSearch : undefined}
|
|
824
842
|
loading={loading}
|
|
825
843
|
emptyText={emptyText}
|
|
826
844
|
size={size}
|
|
@@ -183,7 +183,7 @@ export const Overview: Story = {
|
|
|
183
183
|
<Td mono>creatable</Td>
|
|
184
184
|
<Td mono>boolean</Td>
|
|
185
185
|
<Td mono>false</Td>
|
|
186
|
-
<Td
|
|
186
|
+
<Td>無結果時顯示「直接使用「法務審核」」option(createLabel 可自訂文案)</Td>
|
|
187
187
|
</tr>
|
|
188
188
|
<tr>
|
|
189
189
|
<Td mono>groups</Td>
|
|
@@ -275,30 +275,48 @@ function SelectMenuInspector() {
|
|
|
275
275
|
</div>
|
|
276
276
|
|
|
277
277
|
<div>
|
|
278
|
-
<H3
|
|
278
|
+
<H3>浮層結構</H3>
|
|
279
279
|
<div className="border border-divider rounded-md p-4 bg-muted">
|
|
280
|
-
<
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
280
|
+
<p className="text-caption text-fg-muted mb-3">
|
|
281
|
+
size={size} · multiple={String(multiple)} · searchable={String(searchable)} · creatable={String(creatable)}
|
|
282
|
+
</p>
|
|
283
|
+
<ul className="text-footnote text-fg-secondary space-y-1 [&_ul]:mt-1 [&_ul]:ml-3 [&_ul]:space-y-1 [&_ul]:border-l [&_ul]:border-divider [&_ul]:pl-3">
|
|
284
|
+
<li>
|
|
285
|
+
<span className="font-medium text-foreground">Popover</span> — bg-surface-raised · elevation-200 · rounded-lg
|
|
286
|
+
<ul>
|
|
287
|
+
<li>
|
|
288
|
+
<span className="font-medium text-foreground">Command</span> — cmdk root 容器
|
|
289
|
+
<ul>
|
|
290
|
+
{searchable && (
|
|
291
|
+
<li>
|
|
292
|
+
<span className="font-medium text-foreground">CommandInput</span> — 搜尋框,min-h = field-height({size}) + 8px,底部 border-b
|
|
293
|
+
</li>
|
|
294
|
+
)}
|
|
295
|
+
<li>
|
|
296
|
+
<span className="font-medium text-foreground">CommandList</span> — 依內容撐高(空狀態 min-h = field × 3 + 16px)
|
|
297
|
+
<ul>
|
|
298
|
+
<li>
|
|
299
|
+
<span className="font-medium text-foreground">CommandGroup</span> — p-0 py-2
|
|
300
|
+
<ul>
|
|
301
|
+
<li>MenuItem — size={size}{multiple ? ' · checkbox' : ''} · label + description</li>
|
|
302
|
+
<li>MenuItem — 後續選項…</li>
|
|
303
|
+
</ul>
|
|
304
|
+
</li>
|
|
305
|
+
</ul>
|
|
306
|
+
</li>
|
|
307
|
+
{multiple && (
|
|
308
|
+
<li>
|
|
309
|
+
<span className="font-medium text-foreground">MenuFooter</span> — border-t · 全選列(全部)
|
|
310
|
+
</li>
|
|
311
|
+
)}
|
|
312
|
+
</ul>
|
|
313
|
+
</li>
|
|
314
|
+
</ul>
|
|
315
|
+
</li>
|
|
316
|
+
</ul>
|
|
317
|
+
<p className="text-caption text-fg-muted mt-3">
|
|
318
|
+
RowSizeProvider value={size} 傳遞到所有 item slot(MenuItem / ItemIcon / ItemAvatar 自動讀取)
|
|
319
|
+
</p>
|
|
302
320
|
</div>
|
|
303
321
|
</div>
|
|
304
322
|
</div>
|
|
@@ -696,12 +714,12 @@ export const StateBehavior: Story = {
|
|
|
696
714
|
<H3>搜尋 empty state</H3>
|
|
697
715
|
<Desc>
|
|
698
716
|
搜尋無結果時顯示 Empty primitive,以 flex items-center justify-center + minHeight 撐高並垂直水平置中。
|
|
699
|
-
Creatable
|
|
717
|
+
Creatable 模式會額外顯示「建立『林采潔』」option。
|
|
700
718
|
</Desc>
|
|
701
719
|
<div className="flex items-center gap-4">
|
|
702
720
|
<SelectMenu options={reviewerOptions} value="" onValueChange={() => {}} searchable>
|
|
703
721
|
<Button variant="tertiary" size="md">
|
|
704
|
-
|
|
722
|
+
點擊後搜尋清單外的名字(如「林采潔」)
|
|
705
723
|
</Button>
|
|
706
724
|
</SelectMenu>
|
|
707
725
|
</div>
|
|
@@ -32,7 +32,6 @@ export const UsageGuidance: Story = {
|
|
|
32
32
|
<li><LinkTo kind="Design System/Internal/SelectMenu/展示" name="搜尋"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">數百人組織選負責人,輸入過濾(搜尋)</span></LinkTo></li>
|
|
33
33
|
<li><LinkTo kind="Design System/Internal/SelectMenu/展示" name="多選"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">工單同時貼多個標籤(多選)</span></LinkTo></li>
|
|
34
34
|
<li><LinkTo kind="Design System/Internal/SelectMenu/展示" name="多選 + 搜尋"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">大量標籤庫中過濾並複選(多選 + 搜尋)</span></LinkTo></li>
|
|
35
|
-
<li><LinkTo kind="Design System/Internal/SelectMenu/展示" name="可清除"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">篩選列一鍵清空已選條件(可清除)</span></LinkTo></li>
|
|
36
35
|
</ul>
|
|
37
36
|
<p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見下方 vs 近親 段)。</p>
|
|
38
37
|
</div>
|