@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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select-menu.js","sources":["../../../src/components/SelectMenu/select-menu.tsx"],"sourcesContent":["/**\n * @internal — DS-internal 單元(per `.claude/rules/ui-development.md` Public vs Internal canonical;spec frontmatter `isInternal`)。\n * 不進 root barrel front-door;由 Select / Combobox wrap 消費,end-user app 請用 wrapper 元件。\n */\n// @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.\nimport * as React from 'react'\nimport { Plus, Search } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { useControllable } from '@/design-system/hooks/use-controllable'\nimport type { AvatarData } from '@/design-system/components/Avatar/avatar'\nimport { Popover, PopoverContent, PopoverTrigger } from '@/design-system/components/Popover/popover'\nimport { Command, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator } from '@/design-system/components/Command/command'\nimport { Command as CommandPrimitive, useCommandState } from 'cmdk'\nimport { MenuItem, MenuFooter } from '@/design-system/components/Menu/menu-item'\nimport { Empty } from '@/design-system/components/Empty/empty'\nimport { CircularProgress } from '@/design-system/components/CircularProgress/circular-progress'\nimport { OVERLAY_SIDE_OFFSET } from '@/design-system/tokens/elevation/overlay-geometry'\nimport { getMenuListMinHeight } from '@/design-system/components/Field/field-types'\nimport { RowSizeProvider } from '@/design-system/patterns/element-anatomy/item-anatomy'\nimport { applySelectAll, clearSelection } from '@/design-system/lib/multi-select-ordering'\nimport { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'\n\n/**\n * SelectMenu — Popover + Command 組成的完整下拉選單\n *\n * ── 功能 ──\n * 單選 / 多選、搜尋過濾、分組、可建立新選項(creatable)\n * 多選有 footer「全部」checkbox\n *\n * ── 架構 ──\n * Popover(浮動容器)\n * └── Command(cmdk,搜尋 + 鍵盤導覽)\n * ├── CommandInput(搜尋框)\n * ├── CommandList(選項列表)\n * │ └── CommandGroup → MenuItem\n * └── Footer(多選全選)\n */\n\n// ── Types ──\n\nexport interface SelectMenuOption {\n value: string\n label: string\n description?: string\n icon?: LucideIcon\n /** icon 染色 className(2026-07-08:status 類彩色 option;M30 primitive schema 擴充) */\n iconClassName?: string\n avatar?: AvatarData\n disabled?: boolean\n group?: string\n}\n\nexport interface SelectMenuGroupConfig {\n key: string\n label: string\n}\n\ntype SizeKey = 'sm' | 'md' | 'lg'\n\n// ── Component ──\n\nexport interface SelectMenuProps {\n /** 選項列表 */\n options: SelectMenuOption[]\n /** 群組定義(key 對應 option.group) */\n groups?: SelectMenuGroupConfig[]\n\n /** 當前值(單選 string,多選 string[]) */\n value?: string | string[] | null\n /** 值變更 callback */\n onValueChange?: (value: string | string[]) => void\n\n /** 多選模式 */\n multiple?: boolean\n /** 顯示搜尋框 */\n searchable?: boolean\n /** 可建立新選項 */\n creatable?: boolean\n /** 建立新選項 callback */\n onCreate?: (value: string) => void\n /** creatable 的 label 格式,預設 '直接使用「{query}」' */\n createLabel?: (query: string) => string\n\n /** 觸發元件(asChild) */\n children: React.ReactNode\n /** 搜尋框 placeholder */\n searchPlaceholder?: string\n /** 空選項提示 */\n emptyText?: string\n /** 多選 footer 全選列文字(2026-07-05 D4:原「全部」字面 hardcode,無法覆寫也無法 i18n) */\n selectAllLabel?: string\n /** Loading 狀態(2026-05-15 audit B fix;2026-07-04 Q3 拍板措辭修訂)\n * true → 無可顯示選項時 empty slot(cmdk CommandEmpty)渲 `<Empty icon={<CircularProgress size={48}/>} className=\"py-6\" />`\n * (純 spinner,無 description);已有 options 時保留顯示不清空(MUI Autocomplete「only if there are\n * no suggestions」共識)。trigger 不變,user 隨時可開 dropdown。DS `empty.spec.md`「loading = Empty +\n * CircularProgress compose」SSOT。\n */\n loading?: boolean\n\n /** 尺寸 */\n size?: SizeKey\n /** 對齊方式 */\n align?: 'start' | 'end'\n /** 列表最少顯示幾行選項高度(預設 3),影響空狀態最小高度 */\n minRows?: number\n /** 最小寬度(px),預設跟隨觸發元件 */\n minWidth?: number\n\n /** 受控 open 狀態 */\n open?: boolean\n /** 預設打開(uncontrolled initial state)— 2026-05-15 audit Dim 26 V1 fix per user verbatim「A:1」approval */\n defaultOpen?: boolean\n /** open 狀態變更 callback */\n onOpenChange?: (open: boolean) => void\n\n /** 自訂選項 label 渲染(預設渲染 option.label 純文字) */\n renderLabel?: (option: SelectMenuOption) => React.ReactNode\n /** 攔截 PopoverContent 的 onOpenAutoFocus(如 Select searchable 需阻止 focus 搶走) */\n onOpenAutoFocus?: (e: Event) => void\n\n /**\n * Popover 內容容器的 DOM id(set 在 PopoverContent 外層 div,**非** cmdk 內層 `role=\"listbox\"` 本身)。\n * Combobox / 自定 trigger 用 `aria-controls` 指向此 id 時,指向的是 **popover 容器(listbox 的 ancestor)**——\n * AT 經此容器可定位到內部 cmdk listbox(cmdk List 自帶 auto-generated `role=\"listbox\"` id)。\n */\n contentId?: string\n\n className?: string\n}\n\n// ── SR live status(2026-07-05 D4:empty / loading 空狀態對 SR 不可感知修)──\n// cmdk CommandEmpty 渲染為 role=\"presentation\" div、cmdk 全鏈無 aria-live,且 DOM focus 停在\n// combobox input(aria-activedescendant 虛擬焦點)→ SR 使用者搜尋到 0 結果或 loading 佔位時\n// 聽不到任何播報(loading spinner 的 CircularProgress 無 label 時更是 aria-hidden)。\n// 補 visually-hidden polite live region,鏡射 CommandEmpty 可見內容(cmdk Empty 只在\n// filtered.count === 0 渲染:loading → 載入文案;否則 → emptyText)。對齊 react-select A11yText /\n// APG combobox no-results 播報 + empty.spec.md「動態 filter no-results 容器需 aria-live=\"polite\"」。\n// SSOT 放 SelectMenu 一處 → Select / Combobox / PeoplePicker 全體受益。\nfunction SelectMenuLiveStatus({ loading, emptyText }: { loading: boolean; emptyText: string }) {\n const filteredCount = useCommandState((state) => state.filtered.count)\n return (\n <div role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n {filteredCount === 0\n ? (loading ? '載入中…' /* i18n-allow: DS default SR-only 播報文案 */ : emptyText)\n : null}\n </div>\n )\n}\n\n// shadcn canonical:forwardRef + displayName 統一。SelectMenu 是 Popover + Command\n// composite,自身無 DOM host(trigger 由 consumer 以 asChild children 提供),ref 簽名\n// 保留但不附著(consumer 想取 trigger DOM 直接在 children 上自己 ref)。className 合併到\n// PopoverContent(contextually 最接近 user-facing surface)。\nconst SelectMenu = React.forwardRef<HTMLElement, SelectMenuProps>(function SelectMenu({\n options,\n groups,\n value,\n onValueChange,\n multiple = false,\n searchable = false,\n creatable = false,\n onCreate,\n createLabel = (q) => `直接使用「${q}」`,\n children,\n searchPlaceholder = '搜尋…', // i18n-allow: DS default; consumer override via searchPlaceholder prop\n emptyText = '沒有符合的選項', // i18n-allow: DS default; consumer override via emptyText prop\n selectAllLabel = '全部', // i18n-allow: DS default; consumer override via selectAllLabel prop\n loading = false,\n size = 'md',\n align = 'start',\n minRows = 3,\n minWidth,\n open: controlledOpen,\n defaultOpen,\n onOpenChange: controlledOnOpenChange,\n renderLabel,\n onOpenAutoFocus,\n contentId,\n className,\n}, _ref) {\n // ── State ──\n // 2026-06-11 R2 bug fix:原手寫 `setOpen = controlledOnOpenChange ?? setInternalOpen` 在\n // uncontrolled + onOpenChange listener 場景(傳 onOpenChange 不傳 open)會讓 listener 蓋掉\n // internal setter → menu 開不了。改消費 DS 既有 useControllable(select.tsx 同 canonical):\n // uncontrolled 時 internal state 為準、onOpenChange 僅通知。\n const [open, setOpen] = useControllable<boolean>({\n value: controlledOpen,\n defaultValue: defaultOpen ?? false,\n onChange: controlledOnOpenChange,\n })\n const [search, setSearch] = React.useState('')\n\n // ── Value helpers ──\n const selectedValues = React.useMemo<string[]>(() => {\n if (value == null) return []\n return Array.isArray(value) ? value : [value]\n }, [value])\n\n const isSelected = React.useCallback(\n (v: string) => selectedValues.includes(v),\n [selectedValues]\n )\n\n // 2026-07-05 P2:單選已選 option — 供 cmdk defaultValue 定 cursor 起點(見下方 <Command>)\n const selectedOption = React.useMemo(\n () => (!multiple ? options.find((o) => o.value === selectedValues[0]) : undefined),\n [multiple, options, selectedValues]\n )\n\n const handleSelect = React.useCallback(\n (optionValue: string) => {\n if (multiple) {\n const next = isSelected(optionValue)\n ? selectedValues.filter((v) => v !== optionValue)\n : [...selectedValues, optionValue]\n onValueChange?.(next)\n } else {\n onValueChange?.(optionValue)\n setOpen(false)\n }\n },\n [multiple, selectedValues, isSelected, onValueChange, setOpen]\n )\n\n // ── Multi-select: select all ──\n const selectableOptions = React.useMemo(\n () => options.filter((o) => !o.disabled),\n [options]\n )\n\n const allState: boolean | 'indeterminate' = React.useMemo(() => {\n if (!multiple) return false\n const count = selectableOptions.filter((o) => isSelected(o.value)).length\n if (count === 0) return false\n if (count === selectableOptions.length) return true\n return 'indeterminate'\n }, [multiple, selectableOptions, isSelected])\n\n // 2026-05-16 SSOT canonical fix(Claude+Codex M31 Round 4 共識 + user verbatim「就照你們\n // 的共識做到完美確保有 SSOT」):\n //\n // 原 fully-replace `selectableOptions.map(v)` = source order reset,但**Ant Design 跨元件 grep\n // 證據顯示 source-reset 沒 Ant precedent**(Transfer + Table rowSelection 都是 preserve+append)。\n // 改 `applySelectAll(selectedValues, all)` SSOT primitive 對齊 Ant Transfer canonical:\n // `Array.from(new Set([...prevKeys, ...keys]))` — preserve existing + append unselected。\n //\n // SSOT in `@/design-system/lib/multi-select-ordering` — 未來新 multi-select with Select All\n // footer 必 consume 此 primitive(hook `check_select_all_canonical.sh` 機械強制),\n // 不再各自 reimplement → 防 ordering policy drift。\n const handleSelectAll = React.useCallback(() => {\n if (!multiple) return\n if (allState === true) {\n onValueChange?.(clearSelection())\n } else {\n onValueChange?.(applySelectAll(selectedValues, selectableOptions.map((o) => o.value)))\n }\n }, [multiple, allState, selectableOptions, selectedValues, onValueChange])\n\n // ── Creatable ──\n const showCreate = React.useMemo(() => {\n if (!creatable || !search.trim()) return false\n return !options.some(\n (o) => o.label.toLowerCase() === search.trim().toLowerCase()\n )\n }, [creatable, search, options])\n\n // ── Grouping ──\n const groupedOptions = React.useMemo(() => {\n if (!groups?.length) return [{ key: '__default', label: '', options }]\n const grouped = groups.map((g) => ({\n ...g,\n options: options.filter((o) => o.group === g.key),\n }))\n const ungrouped = options.filter((o) => !o.group)\n if (ungrouped.length) {\n grouped.unshift({ key: '__default', label: '', options: ungrouped })\n }\n return grouped\n }, [groups, options])\n\n // ── Reset search on close ──\n React.useEffect(() => {\n if (!open) setSearch('')\n }, [open])\n\n // 2026-06-01 Select/Combobox #15(user 拍板 A):非搜尋時開選單把 focus 移到 cmdk-root,\n // 讓 cmdk 內建方向鍵 / Enter / Home / End 導覽生效。原 PopoverContent default autofocus 找 body\n // input/button,非搜尋無 input + 選項是 role=option div → focus 落在 content wrapper,cmdk 的 keydown\n // handler 綁在 cmdk-root 收不到事件 → 桌機鍵盤導覽不可達(WAI-ARIA combobox 違反)。\n // **純鍵盤路由**:滑鼠點擊路徑完全不碰;cmdk active-option highlight 是 cmdk 內部 state(非 DOM focus\n // 驅動)故視覺不變。selector miss 時 root=null → no-op fallback(無回歸)。\n // SSOT 在 SelectMenu → Select / Combobox / 所有 non-searchable SelectMenu consumer 自動受益。\n const handleNonSearchableAutoFocus = React.useCallback((e: Event) => {\n e.preventDefault()\n const root = (e.currentTarget as HTMLElement).querySelector<HTMLElement>('[cmdk-root]')\n root?.focus({ preventScroll: true })\n }, [])\n\n // RowSizeProvider 讓 PopoverContent 子樹內任何 <ItemIcon> / <ItemAvatar> /\n // <ItemInlineAction> 都自動讀到對的 size,跟 SidebarProvider / TreeView 同一條規則。\n // (注:Popover 透過 Portal 渲染,context 仍然會跨 portal 傳遞——React context 是 tree-based\n // 不是 DOM-based,Portal 不影響 context propagation)\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>{children}</PopoverTrigger>\n <RowSizeProvider value={size}>\n <PopoverContent\n id={contentId}\n // w-auto override PopoverContent default w-72(rich-popover canonical)— SelectMenu 走「跟 trigger 同寬」\n // canonical(spec L72)。minWidth = max(trigger-width, 240px sensible-min)— 對齊 shadcn / Material / Ant\n // select dropdown 共識(2026-05-04 D1 verify SelectMenu spec implementation)。\n className={cn(\n 'p-0 w-auto rounded-lg border border-border bg-surface-raised overflow-hidden',\n className\n )}\n style={{\n boxShadow: 'var(--elevation-200)',\n minWidth: minWidth ?? 'max(var(--radix-popover-trigger-width), 15rem)',\n }}\n align={align}\n sideOffset={OVERLAY_SIDE_OFFSET}\n onOpenAutoFocus={onOpenAutoFocus ?? (!searchable ? handleNonSearchableAutoFocus : undefined)}\n // **2026-05-07 v15.16 nested portal fix**:Tag dismiss inside trigger\n // 區的 OverflowIndicator HoverCard popup(獨立 Radix portal,DOM 不在\n // PopoverContent 內)— Radix DismissableLayer document-level outside\n // detection 跨 portal 視為「outside」→ SelectMenu 被誤關閉。\n // 攔 `onPointerDownOutside`,檢查 click target 是否在另一個 Radix portal\n // 內,是 → preventDefault 取消 close。對齊 Ant Design Select multiSelect\n // tagRender 行為(連續移除不關 dropdown)。\n // SSOT propagation:fix 在 SelectMenu level → Combobox / 其他 SelectMenu\n // consumer 自動受益。\n // **2026-05-07 v15.16 nested portal fix**:Tag dismiss inside trigger 區的\n // OverflowIndicator HoverCard popup(獨立 Radix portal,DOM 不在 SelectMenu\n // PopoverContent 內)— Radix DismissableLayer document-level pointerdown +\n // focusin 偵測「outside」→ SelectMenu 被誤關閉。\n // 攔 `onInteractOutside`(統一 pointerdown + focusin),檢查 click target 是否\n // 在另一個 Radix portal wrapper(`[data-radix-popper-content-wrapper]`),\n // 是 → preventDefault 取消 close。對齊 Ant Design Select multiSelect tagRender\n // 行為(連續移除不關 dropdown)。\n // SSOT propagation:fix 在 SelectMenu level → Combobox / 所有 SelectMenu\n // consumer 自動受益。\n onInteractOutside={(e) => {\n const target = e.detail.originalEvent.target as HTMLElement | null\n if (target?.closest('[data-radix-popper-content-wrapper]')) {\n e.preventDefault()\n }\n }}\n >\n <Command\n shouldFilter={searchable}\n // 2026-07-06 cursor 起點修:單選已有值時 cmdk virtual focus 落在已選項而非第一項。\n // cmdk 1.1.1 初始 state 取 defaultValue、item mount 的 selectFirstItem 有\n // `state.value ||` guard 不覆蓋(dist source 驗證);Popover 關閉即 unmount(無\n // forceMount)→ 每次開啟 remount 重新生效。需搭配下方 CommandItem value={opt.value}。\n defaultValue={selectedOption?.value}\n // 2026-07-06 A11y:combobox accessible name。2026-07-14 修正註解(cmdk 1.1.1 dist 實證):\n // cmdk **無條件**渲 sr-only <label htmlFor={inputId}>(dist/index.mjs\n // `createElement(\"label\",{htmlFor:U.inputId,id:U.labelId,...},b)` — label prop 只決定\n // 文字內容,不決定 label 元素是否渲染)。故 non-searchable 時「空 label + htmlFor 指向\n // 不存在的 input」仍存在(upstream cmdk 限制;空 label 無 accessible name,不產生朗讀\n // 內容)。僅 searchable(真的有 input)時傳內容 = 避免 non-searchable 帶 search\n // placeholder 的 accessible name 卻無對應 input;真要消除 orphan label 需 upstream 修。\n label={searchable ? searchPlaceholder : undefined}\n className=\"bg-transparent\"\n >\n {searchable && (\n <div className={cn(\n 'flex items-center gap-2 px-3 py-1 border-b border-divider',\n size === 'lg' ? 'min-h-[calc(var(--field-height-lg)+8px)]'\n : size === 'sm' ? 'min-h-[calc(var(--field-height-sm)+8px)]'\n : 'min-h-[calc(var(--field-height-md)+8px)]',\n )}>\n <Search size={ICON_SIZE[size as 'sm' | 'md' | 'lg']} className=\"shrink-0 text-fg-muted\" aria-hidden />\n <CommandPrimitive.Input\n placeholder={searchPlaceholder}\n value={search}\n onValueChange={setSearch}\n className={cn(\n 'flex w-full bg-transparent outline-none placeholder:text-fg-muted',\n // M24 disabled state precedence:disabled 時 placeholder 切 fg-disabled(audit dim 34)\n 'disabled:placeholder:text-fg-disabled disabled:text-fg-disabled disabled:cursor-not-allowed',\n size === 'lg' ? 'text-body-lg leading-compact' : 'text-body leading-compact',\n )}\n />\n </div>\n )}\n {/* **2026-05-07 v15.13 R2 fix**:minHeight 從 CommandList 搬到 CommandEmpty。\n 原本 CommandList 永遠套 `minHeight = field-height × minRows + 16px`,結果\n user 過濾出 < minRows 個 match 時 list 底下空一片(eg. 打 'c' 出 2 個 match\n 卻撐高到 3 row 容量,1 row 留白)。 Fix:只有 empty state 才需要 minHeight 撐\n 起 placeholder 視覺;有 results 時 CommandList 自然 fit content。 */}\n {/* aria-busy(2026-07-04):loading 時標注 listbox 忙碌——兌現 select.spec.md「Loading」段\n 「+ aria-busy」承諾(cmdk List 本身即 role=\"listbox\" 容器,wrapper forward props)。 */}\n <CommandList\n className=\"relative\"\n aria-busy={loading || undefined}\n // 2026-07-06 A11y:cmdk List 的 aria-label 由其 `label` prop 渲染(內部 spread 後 override,\n // 直接傳 aria-label 會被 cmdk default \"Suggestions\" 蓋掉 silent 失效)— 必走 label prop。\n label=\"選項\" // i18n-allow: DS default; listbox accessible name\n >\n <CommandEmpty\n className=\"flex items-center justify-center\"\n style={{ minHeight: getMenuListMinHeight(size, minRows) }}\n >\n {loading\n ? <Empty icon={<CircularProgress size={48}/>} className=\"py-6\" />\n : <Empty description={emptyText} className=\"py-6\" />}\n </CommandEmpty>\n\n {groupedOptions.map((group, gi) => (\n <React.Fragment key={group.key}>\n {gi > 0 && <CommandSeparator />}\n <CommandGroup\n className=\"p-0 py-2 [&_[cmdk-group-heading]]:p-0\"\n // 2026-07-06 A11y:群組標題走 cmdk `heading`(自動產 cmdk-group-heading id,選項容器\n // role=\"group\" + aria-labelledby 指向之)取代手刻 child row(原本 AT 不可感知;\n // aria-label fallback 對 role=\"presentation\" group 無效)。[&_[cmdk-group-heading]]:p-0\n // 中和 command.tsx base 的 px-3 py-1.5(tailwind-merge p-0 勝);字級 / 顏色由 MenuItem\n // header 自帶(text-body leading-compact + font-medium + text-fg-muted),視覺不變。\n heading={group.label ? <MenuItem size={size} header>{group.label}</MenuItem> : undefined}\n >\n {group.options.map((opt) => (\n <CommandItem\n key={opt.value}\n // 2026-07-06 識別值修:cmdk 以 value 當 item identity + selection state。原用\n // opt.label → 重複 label 時雙亮 + Enter 選錯;改唯一的 opt.value,label 移入\n // keywords 保搜尋照樣命中(cmdk filter 對 value + keywords 計分)。\n value={opt.value}\n keywords={opt.description ? [opt.label, opt.description] : [opt.label]}\n disabled={opt.disabled}\n onSelect={() => handleSelect(opt.value)}\n // 2026-07-05 D4 P0 修:原 data-[selected=true]:bg-transparent 蓋掉 command.tsx base\n // 的 data-[selected=true]:bg-neutral-hover = cmdk virtual focus(aria-activedescendant)\n // 鍵盤 cursor 唯一視覺通道被抹掉 → 方向鍵移動畫面零變化(影響 Select/Combobox/PeoplePicker\n // 全家)。鏡射 DropdownMenu wrapper pattern:互動 bg 單一 owner 在外層 CommandItem\n //(base highlight 恢復;persistent selection 上移;selected×cursor 加深一階 —\n // 對齊本日 DropdownMenu Q2 決策),內層 MenuItem 全透明。\n className={cn(\n 'p-0 rounded-none',\n !multiple && isSelected(opt.value) && 'bg-neutral-selected data-[selected=true]:bg-neutral-selected-active',\n )}\n >\n <MenuItem\n size={size}\n startIcon={opt.icon}\n startIconClassName={opt.iconClassName}\n avatar={opt.avatar}\n description={opt.description}\n checkbox={multiple}\n checked={isSelected(opt.value)}\n selected={!multiple && isSelected(opt.value)}\n disabled={opt.disabled}\n // 2026-07-05 D4:巢狀 role 修 — MenuItem 預設 role=\"option\" + aria-selected(persistent\n // selection)巢狀在 cmdk CommandItem(本身 role=\"option\" + aria-selected=cursor)內,\n // AT 讀到 option 包 option 且內外 aria-selected 語意相反 → 朗讀錯亂。鏡射 DropdownMenu\n // canonical(dropdown-menu.tsx「Pure visual — Radix parent handles role/aria」):\n // 內層純視覺 role=\"presentation\",cmdk CommandItem 是唯一 option 節點。\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent\"\n >\n {renderLabel ? renderLabel(opt) : opt.label}\n </MenuItem>\n </CommandItem>\n ))}\n </CommandGroup>\n </React.Fragment>\n ))}\n\n {/* Creatable item */}\n {showCreate && (\n <>\n <CommandSeparator />\n <CommandGroup className=\"p-0 py-2\">\n <CommandItem\n // 2026-07-06:`__create__` 前綴防 identity 撞名 — 選項 row 改用 opt.value 識別後,\n // search 恰等於某 option.value 時裸 search 會與該 row 同 value(cmdk 雙亮 + Enter\n // 選錯)。value 含 search 子字串,cmdk filter 照樣命中;onSelect closure 讀 search\n // 不受影響。\n value={`__create__${search}`}\n onSelect={() => {\n onCreate?.(search.trim())\n setSearch('')\n }}\n className=\"p-0 rounded-none\"\n >\n {/* 2026-07-05 D4:同上方選項 row 的巢狀 role 修 — creatable row 同樣是\n MenuItem 巢狀在 cmdk CommandItem(option)內,內層純視覺 role=\"presentation\"。 */}\n <MenuItem size={size} startIcon={Plus} role=\"presentation\" className=\"!bg-transparent hover:!bg-transparent\">\n {createLabel(search.trim())}\n </MenuItem>\n </CommandItem>\n </CommandGroup>\n </>\n )}\n </CommandList>\n\n {/* SR 播報 empty / loading 空狀態(見 SelectMenuLiveStatus docblock,2026-07-05 D4) */}\n <SelectMenuLiveStatus loading={loading} emptyText={emptyText} />\n\n {/* Multi-select footer: Select All\n - 沒有選項時不顯示(selectableOptions.length === 0)\n - 搜尋有文字時不顯示(search 非空 = 使用者在找特定項目,「全選」沒意義) */}\n {multiple && selectableOptions.length > 0 && !search && (\n <MenuFooter>\n {/* 2026-07-05 D4:全選列鍵盤可達修 — 原裸 MenuItem(div 預設 role=\"option\" 無 tabIndex)\n 位於 CommandList 之外:cmdk 方向鍵只導覽 [cmdk-item]、Tab 也到不了 div → 鍵盤使用者\n 完全無法操作全選(WCAG 2.1.1),且該 role=\"option\" 無 listbox 祖先(orphan,axe\n aria-required-parent)。改真實 focusable checkbox 語意:tabIndex=0 + role=\"checkbox\" +\n aria-checked(indeterminate → \"mixed\")+ Enter / Space 觸發;preventDefault 讓 cmdk root\n onKeyDown(源碼檢查 e.defaultPrevented)不會再對 active option 重複觸發 Enter。\n aria-selected 顯式蓋回 undefined(MenuItem 內建 aria-selected 對 role=\"checkbox\" 無效)。 */}\n <MenuItem\n size={size}\n checkbox\n checked={allState}\n onClick={handleSelectAll}\n role=\"checkbox\"\n aria-checked={allState === 'indeterminate' ? 'mixed' : allState}\n aria-selected={undefined}\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleSelectAll()\n }\n }}\n >\n {selectAllLabel}\n </MenuItem>\n </MenuFooter>\n )}\n </Command>\n </PopoverContent>\n </RowSizeProvider>\n </Popover>\n )\n})\n\nSelectMenu.displayName = 'SelectMenu'\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const selectMenuMeta = {\n component: 'SelectMenu',\n // 2026-07-14 修 family 自我矛盾:原 Phase-1 mechanical 填 4(Field control),與 spec body\n // 「Layout Family:非上述 family — composite / multi-section」矛盾。SelectMenu 是 Popover +\n // Command composite overlay(trigger 由 consumer Select / Combobox own,Family 4 是 trigger\n // 側 field control 的事);對齊 DS composite 慣例(Accordion / Command / Popover meta 皆\n // family: null + spec frontmatter composite)。\n family: null, // non-family composite / overlay(對齊 spec frontmatter composite)\n variants: {\n\n },\n sizes: {\n\n },\n // 'selected' = 單選 option 持續選中(bg-neutral-selected);'active' 保留 — cmdk virtual-focus on\n // selected 走 bg-neutral-selected-active(active 階 token;2026-07-07 詞彙統一補修)。\n states: ['default', 'hover', 'active', 'selected', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-neutral-selected', 'bg-neutral-selected-active', 'bg-surface-raised', 'bg-transparent'],\n fg: ['text-fg-muted'],\n ring: [],\n },\n} as const\n\n/**\n * 2026-07-05 D4 P0 修(searchable 鍵盤死路):trigger 內的裸 <input> 與 portal 內的 cmdk root\n * 在不同 DOM 子樹 — 鍵盤事件永遠 bubble 不到 cmdk 的 ArrowUp/Down/Enter handler([cmdk-root]\n * onKeyDown)→ searchable Select / PeoplePicker single / Combobox searchIn='trigger' 開啟後\n * 只能 Esc。修法 = APG combobox-with-list:trigger input 把三鍵 re-dispatch 給 cmdk root\n * (native KeyboardEvent bubbles 經 React root delegation 觸發 cmdk synthetic handler)。\n * Home/End 刻意不轉送(文字輸入的 caret 語意優先,對齊 MUI/Ant Autocomplete)。\n * aria-activedescendant 綁回 trigger input → 見下方 useActiveDescendant(2026-07-05 D4 補齊)。\n */\nexport function forwardKeyToListbox(contentId: string | undefined, e: React.KeyboardEvent): boolean {\n if (!contentId) return false\n if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp' && e.key !== 'Enter') return false\n const root = document.getElementById(contentId)?.querySelector<HTMLElement>('[cmdk-root]')\n if (!root) return false\n e.preventDefault()\n root.dispatchEvent(new KeyboardEvent('keydown', { key: e.key, bubbles: true, cancelable: true }))\n return true\n}\n\n/**\n * 2026-07-05 D4 補齊(APG combobox aria-activedescendant):追蹤 cmdk 目前 virtual-focus item\n * 的 DOM id,供 trigger 端搜尋 input 綁 `aria-activedescendant` —— SR 才會在方向鍵導覽 /\n * 打字過濾時播報 active option 名。機制:trigger 與 portal 內 cmdk 分屬不同 DOM 子樹,cmdk\n * 只把 active id 綁在自己的 Command.Input / List(cmdk source:item 自帶 auto-generated id +\n * `data-selected=\"true\"` 標記 virtual focus)→ trigger 端用 MutationObserver 監聽 popover 容器\n * (contentId = PopoverContent id)內 `data-selected` 屬性變化 + childList(打字過濾 re-render\n * 換 item 節點),單一機制涵蓋全部更新路徑:開啟初始 auto-highlight / forwardKeyToListbox\n * 方向鍵轉送 / 搜尋過濾後 cmdk 自動移 cursor / pointer hover。\n * 關閉時清 undefined —— ARIA 要求 id 必指向存在於 DOM 的節點,不可留 stale id。\n */\nexport function useActiveDescendant(contentId: string | undefined, open: boolean): string | undefined {\n const [activeId, setActiveId] = React.useState<string | undefined>(undefined)\n React.useEffect(() => {\n if (!open || !contentId) {\n setActiveId(undefined)\n return\n }\n // PopoverContent 與 trigger 同一個 React commit mount(open state 同批 render)→ effect 跑時已在 DOM\n const container = document.getElementById(contentId)\n if (!container) return\n const read = () => {\n setActiveId(container.querySelector<HTMLElement>('[cmdk-item][data-selected=\"true\"]')?.id || undefined)\n }\n // 初始補讀:MutationObserver 只看「觀察開始後」的變化;cmdk 初始 auto-highlight(layout effect\n // 排程)可能已 commit → rAF 讀當下狀態兜底,與 observer 互補、誰先到都不漏。\n const raf = requestAnimationFrame(read)\n const observer = new MutationObserver(read)\n observer.observe(container, { subtree: true, childList: true, attributes: true, attributeFilter: ['data-selected'] })\n return () => {\n cancelAnimationFrame(raf)\n observer.disconnect()\n }\n }, [open, contentId])\n return activeId\n}\n\nexport { SelectMenu }\n"],"names":["SelectMenu","CommandPrimitive"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"select-menu.js","sources":["../../../src/components/SelectMenu/select-menu.tsx"],"sourcesContent":["/**\n * @internal — DS-internal 單元(per `.claude/rules/ui-development.md` Public vs Internal canonical;spec frontmatter `isInternal`)。\n * 不進 root barrel front-door;由 Select / Combobox wrap 消費,end-user app 請用 wrapper 元件。\n */\n// @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.\nimport * as React from 'react'\nimport { Plus, Search } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { useControllable } from '@/design-system/hooks/use-controllable'\nimport type { AvatarData } from '@/design-system/components/Avatar/avatar'\nimport { Popover, PopoverContent, PopoverTrigger } from '@/design-system/components/Popover/popover'\nimport { Command, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator } from '@/design-system/components/Command/command'\nimport { Command as CommandPrimitive, useCommandState } from 'cmdk'\nimport { MenuItem, MenuFooter } from '@/design-system/components/Menu/menu-item'\nimport { Empty } from '@/design-system/components/Empty/empty'\nimport { CircularProgress } from '@/design-system/components/CircularProgress/circular-progress'\nimport { OVERLAY_SIDE_OFFSET } from '@/design-system/tokens/elevation/overlay-geometry'\nimport { getMenuListMinHeight } from '@/design-system/components/Field/field-types'\nimport { RowSizeProvider } from '@/design-system/patterns/element-anatomy/item-anatomy'\nimport { applySelectAll, clearSelection } from '@/design-system/lib/multi-select-ordering'\nimport { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'\n\n/**\n * SelectMenu — Popover + Command 組成的完整下拉選單\n *\n * ── 功能 ──\n * 單選 / 多選、搜尋過濾、分組、可建立新選項(creatable)\n * 多選有 footer「全部」checkbox\n *\n * ── 架構 ──\n * Popover(浮動容器)\n * └── Command(cmdk,搜尋 + 鍵盤導覽)\n * ├── CommandPrimitive.Input(搜尋框,raw cmdk + 自建 icon/min-h wrapper,非 DS CommandInput)\n * ├── CommandList(選項列表)\n * │ └── CommandGroup → MenuItem\n * └── Footer(多選全選)\n */\n\n// ── Types ──\n\nexport interface SelectMenuOption {\n value: string\n label: string\n description?: string\n icon?: LucideIcon\n /** icon 染色 className(2026-07-08:status 類彩色 option;M30 primitive schema 擴充) */\n iconClassName?: string\n avatar?: AvatarData\n disabled?: boolean\n group?: string\n}\n\nexport interface SelectMenuGroupConfig {\n key: string\n label: string\n}\n\ntype SizeKey = 'sm' | 'md' | 'lg'\n\n// ── Component ──\n\nexport interface SelectMenuProps {\n /** 選項列表 */\n options: SelectMenuOption[]\n /** 群組定義(key 對應 option.group) */\n groups?: SelectMenuGroupConfig[]\n\n /** 當前值(單選 string,多選 string[]) */\n value?: string | string[] | null\n /** 值變更 callback */\n onValueChange?: (value: string | string[]) => void\n\n /** 多選模式 */\n multiple?: boolean\n /** 顯示搜尋框 */\n searchable?: boolean\n /** 可建立新選項 */\n creatable?: boolean\n /** 建立新選項 callback */\n onCreate?: (value: string) => void\n /** creatable 的 label 格式,預設 '直接使用「{query}」' */\n createLabel?: (query: string) => string\n /**\n * 受控搜尋字串(2026-07-18 決策11:讓 consumer 用**外部搜尋**(如 Select 的 trigger 內嵌 input)\n * 驅動 creatable create-row 顯隱)。傳入 = 受控(SelectMenu 不自管 search、close 不 reset,由\n * parent 負責);不傳 = 內部 uncontrolled(既有行為,零影響)。搭配 `searchable=false` +\n * `creatable` 時,SelectMenu 不畫自己的 input,但 create-row 仍依此 search 顯示。\n */\n search?: string\n /** 受控搜尋變更通知(選配;SelectMenu 內部無 input 時通常由 parent 單向驅動,不需此) */\n onSearchChange?: (value: string) => void\n\n /** 觸發元件(asChild) */\n children: React.ReactNode\n /** 搜尋框 placeholder */\n searchPlaceholder?: string\n /** 空選項提示 */\n emptyText?: string\n /** 多選 footer 全選列文字(2026-07-05 D4:原「全部」字面 hardcode,無法覆寫也無法 i18n) */\n selectAllLabel?: string\n /** Loading 狀態(2026-05-15 audit B fix;2026-07-04 Q3 拍板措辭修訂)\n * true → 無可顯示選項時 empty slot(cmdk CommandEmpty)渲 `<Empty icon={<CircularProgress size={48}/>} className=\"py-6\" />`\n * (純 spinner,無 description);已有 options 時保留顯示不清空(MUI Autocomplete「only if there are\n * no suggestions」共識)。trigger 不變,user 隨時可開 dropdown。DS `empty.spec.md`「loading = Empty +\n * CircularProgress compose」SSOT。\n */\n loading?: boolean\n\n /** 尺寸 */\n size?: SizeKey\n /** 對齊方式 */\n align?: 'start' | 'end'\n /** 列表最少顯示幾行選項高度(預設 3),影響空狀態最小高度 */\n minRows?: number\n /** 最小寬度(px),預設跟隨觸發元件 */\n minWidth?: number\n\n /** 受控 open 狀態 */\n open?: boolean\n /** 預設打開(uncontrolled initial state)— 2026-05-15 audit Dim 26 V1 fix per user verbatim「A:1」approval */\n defaultOpen?: boolean\n /** open 狀態變更 callback */\n onOpenChange?: (open: boolean) => void\n\n /** 自訂選項 label 渲染(預設渲染 option.label 純文字) */\n renderLabel?: (option: SelectMenuOption) => React.ReactNode\n /** 攔截 PopoverContent 的 onOpenAutoFocus(如 Select searchable 需阻止 focus 搶走) */\n onOpenAutoFocus?: (e: Event) => void\n\n /**\n * Popover 內容容器的 DOM id(set 在 PopoverContent 外層 div,**非** cmdk 內層 `role=\"listbox\"` 本身)。\n * Combobox / 自定 trigger 用 `aria-controls` 指向此 id 時,指向的是 **popover 容器(listbox 的 ancestor)**——\n * AT 經此容器可定位到內部 cmdk listbox(cmdk List 自帶 auto-generated `role=\"listbox\"` id)。\n */\n contentId?: string\n\n /**\n * 浮層 accessible name(2026-07-17 Dim 10 a11y 修)。Radix Popover 的 content 為\n * `role=\"dialog\"` 但無自動命名機制 → 無 aria-label 時 dialog 無 accessible name。\n * 預設「選項清單」讓 SR 使用者知道浮層用途;consumer(Combobox / Select / PeoplePicker)\n * 可傳更 contextual 的名(如欄位名)。SSOT 放此一處 → 全 SelectMenu consumer 受益。\n */\n 'aria-label'?: string\n\n className?: string\n}\n\n// ── SR live status(2026-07-05 D4:empty / loading 空狀態對 SR 不可感知修)──\n// cmdk CommandEmpty 渲染為 role=\"presentation\" div、cmdk 全鏈無 aria-live,且 DOM focus 停在\n// combobox input(aria-activedescendant 虛擬焦點)→ SR 使用者搜尋到 0 結果或 loading 佔位時\n// 聽不到任何播報(loading spinner 的 CircularProgress 無 label 時更是 aria-hidden)。\n// 補 visually-hidden polite live region,鏡射 CommandEmpty 可見內容(cmdk Empty 只在\n// filtered.count === 0 渲染:loading → 載入文案;否則 → emptyText)。對齊 react-select A11yText /\n// APG combobox no-results 播報 + empty.spec.md「動態 filter no-results 容器需 aria-live=\"polite\"」。\n// SSOT 放 SelectMenu 一處 → Select / Combobox / PeoplePicker 全體受益。\nfunction SelectMenuLiveStatus({ loading, emptyText }: { loading: boolean; emptyText: string }) {\n const filteredCount = useCommandState((state) => state.filtered.count)\n return (\n <div role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n {filteredCount === 0\n ? (loading ? '載入中…' /* i18n-allow: DS default SR-only 播報文案 */ : emptyText)\n : null}\n </div>\n )\n}\n\n// shadcn canonical:forwardRef + displayName 統一。SelectMenu 是 Popover + Command\n// composite,自身無 DOM host(trigger 由 consumer 以 asChild children 提供),ref 簽名\n// 保留但不附著(consumer 想取 trigger DOM 直接在 children 上自己 ref)。className 合併到\n// PopoverContent(contextually 最接近 user-facing surface)。\nconst SelectMenu = React.forwardRef<HTMLElement, SelectMenuProps>(function SelectMenu({\n options,\n groups,\n value,\n onValueChange,\n multiple = false,\n searchable = false,\n creatable = false,\n onCreate,\n createLabel = (q) => `直接使用「${q}」`,\n search: controlledSearch,\n onSearchChange,\n children,\n searchPlaceholder = '搜尋…', // i18n-allow: DS default; consumer override via searchPlaceholder prop\n emptyText = '沒有符合的選項', // i18n-allow: DS default; consumer override via emptyText prop\n selectAllLabel = '全部', // i18n-allow: DS default; consumer override via selectAllLabel prop\n loading = false,\n size = 'md',\n align = 'start',\n minRows = 3,\n minWidth,\n open: controlledOpen,\n defaultOpen,\n onOpenChange: controlledOnOpenChange,\n renderLabel,\n onOpenAutoFocus,\n contentId,\n 'aria-label': ariaLabel = '選項清單', // i18n-allow: DS default; consumer override via aria-label prop\n className,\n}, _ref) {\n // ── State ──\n // 2026-06-11 R2 bug fix:原手寫 `setOpen = controlledOnOpenChange ?? setInternalOpen` 在\n // uncontrolled + onOpenChange listener 場景(傳 onOpenChange 不傳 open)會讓 listener 蓋掉\n // internal setter → menu 開不了。改消費 DS 既有 useControllable(select.tsx 同 canonical):\n // uncontrolled 時 internal state 為準、onOpenChange 僅通知。\n const [open, setOpen] = useControllable<boolean>({\n value: controlledOpen,\n defaultValue: defaultOpen ?? false,\n onChange: controlledOnOpenChange,\n })\n // search:選配受控(決策11)。傳 `search` prop = 受控(parent 驅動,如 Select trigger input);\n // 不傳 = 內部 uncontrolled(既有行為)。setSearch 同步內部 + 通知 onSearchChange。\n const isSearchControlled = controlledSearch !== undefined\n const [internalSearch, setInternalSearch] = React.useState('')\n const search = isSearchControlled ? controlledSearch : internalSearch\n const setSearch = React.useCallback(\n (next: string) => {\n if (!isSearchControlled) setInternalSearch(next)\n onSearchChange?.(next)\n },\n [isSearchControlled, onSearchChange],\n )\n\n // ── Value helpers ──\n const selectedValues = React.useMemo<string[]>(() => {\n if (value == null) return []\n return Array.isArray(value) ? value : [value]\n }, [value])\n\n const isSelected = React.useCallback(\n (v: string) => selectedValues.includes(v),\n [selectedValues]\n )\n\n // 2026-07-05 P2:單選已選 option — 供 cmdk defaultValue 定 cursor 起點(見下方 <Command>)\n const selectedOption = React.useMemo(\n () => (!multiple ? options.find((o) => o.value === selectedValues[0]) : undefined),\n [multiple, options, selectedValues]\n )\n\n const handleSelect = React.useCallback(\n (optionValue: string) => {\n if (multiple) {\n const next = isSelected(optionValue)\n ? selectedValues.filter((v) => v !== optionValue)\n : [...selectedValues, optionValue]\n onValueChange?.(next)\n } else {\n onValueChange?.(optionValue)\n setOpen(false)\n }\n },\n [multiple, selectedValues, isSelected, onValueChange, setOpen]\n )\n\n // ── Multi-select: select all ──\n const selectableOptions = React.useMemo(\n () => options.filter((o) => !o.disabled),\n [options]\n )\n\n const allState: boolean | 'indeterminate' = React.useMemo(() => {\n if (!multiple) return false\n const count = selectableOptions.filter((o) => isSelected(o.value)).length\n if (count === 0) return false\n if (count === selectableOptions.length) return true\n return 'indeterminate'\n }, [multiple, selectableOptions, isSelected])\n\n // 2026-05-16 SSOT canonical fix(Claude+Codex M31 Round 4 共識 + user verbatim「就照你們\n // 的共識做到完美確保有 SSOT」):\n //\n // 原 fully-replace `selectableOptions.map(v)` = source order reset,但**Ant Design 跨元件 grep\n // 證據顯示 source-reset 沒 Ant precedent**(Transfer + Table rowSelection 都是 preserve+append)。\n // 改 `applySelectAll(selectedValues, all)` SSOT primitive 對齊 Ant Transfer canonical:\n // `Array.from(new Set([...prevKeys, ...keys]))` — preserve existing + append unselected。\n //\n // SSOT in `@/design-system/lib/multi-select-ordering` — 未來新 multi-select with Select All\n // footer 必 consume 此 primitive(hook `check_select_all_canonical.sh` 機械強制),\n // 不再各自 reimplement → 防 ordering policy drift。\n const handleSelectAll = React.useCallback(() => {\n if (!multiple) return\n if (allState === true) {\n onValueChange?.(clearSelection())\n } else {\n onValueChange?.(applySelectAll(selectedValues, selectableOptions.map((o) => o.value)))\n }\n }, [multiple, allState, selectableOptions, selectedValues, onValueChange])\n\n // ── Creatable ──\n const showCreate = React.useMemo(() => {\n if (!creatable || !search.trim()) return false\n return !options.some(\n (o) => o.label.toLowerCase() === search.trim().toLowerCase()\n )\n }, [creatable, search, options])\n\n // ── Grouping ──\n const groupedOptions = React.useMemo(() => {\n if (!groups?.length) return [{ key: '__default', label: '', options }]\n const grouped = groups.map((g) => ({\n ...g,\n options: options.filter((o) => o.group === g.key),\n }))\n const ungrouped = options.filter((o) => !o.group)\n if (ungrouped.length) {\n grouped.unshift({ key: '__default', label: '', options: ungrouped })\n }\n return grouped\n }, [groups, options])\n\n // ── Reset search on close(僅 uncontrolled;受控時由 parent 負責 reset)──\n React.useEffect(() => {\n if (!open && !isSearchControlled) setInternalSearch('')\n }, [open, isSearchControlled])\n\n // 2026-06-01 Select/Combobox #15(user 拍板 A):非搜尋時開選單把 focus 移到 cmdk-root,\n // 讓 cmdk 內建方向鍵 / Enter / Home / End 導覽生效。原 PopoverContent default autofocus 找 body\n // input/button,非搜尋無 input + 選項是 role=option div → focus 落在 content wrapper,cmdk 的 keydown\n // handler 綁在 cmdk-root 收不到事件 → 桌機鍵盤導覽不可達(WAI-ARIA combobox 違反)。\n // **純鍵盤路由**:滑鼠點擊路徑完全不碰;cmdk active-option highlight 是 cmdk 內部 state(非 DOM focus\n // 驅動)故視覺不變。selector miss 時 root=null → no-op fallback(無回歸)。\n // SSOT 在 SelectMenu → Select / Combobox / 所有 non-searchable SelectMenu consumer 自動受益。\n const handleNonSearchableAutoFocus = React.useCallback((e: Event) => {\n e.preventDefault()\n const root = (e.currentTarget as HTMLElement).querySelector<HTMLElement>('[cmdk-root]')\n root?.focus({ preventScroll: true })\n }, [])\n\n // RowSizeProvider 讓 PopoverContent 子樹內任何 <ItemIcon> / <ItemAvatar> /\n // <ItemInlineAction> 都自動讀到對的 size,跟 SidebarProvider / TreeView 同一條規則。\n // (注:Popover 透過 Portal 渲染,context 仍然會跨 portal 傳遞——React context 是 tree-based\n // 不是 DOM-based,Portal 不影響 context propagation)\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>{children}</PopoverTrigger>\n <RowSizeProvider value={size}>\n <PopoverContent\n id={contentId}\n // 2026-07-17 Dim 10 a11y 修:role=\"dialog\" 浮層 accessible name(Radix Popover 無自動命名)\n aria-label={ariaLabel}\n // w-auto override PopoverContent default w-72(rich-popover canonical)— SelectMenu 走「跟 trigger 同寬」\n // canonical(spec L72)。minWidth = max(trigger-width, 240px sensible-min)— 對齊 shadcn / Material / Ant\n // select dropdown 共識(2026-05-04 D1 verify SelectMenu spec implementation)。\n className={cn(\n 'p-0 w-auto rounded-lg border border-border bg-surface-raised overflow-hidden',\n className\n )}\n style={{\n boxShadow: 'var(--elevation-200)',\n minWidth: minWidth ?? 'max(var(--radix-popover-trigger-width), 15rem)',\n }}\n align={align}\n sideOffset={OVERLAY_SIDE_OFFSET}\n onOpenAutoFocus={onOpenAutoFocus ?? (!searchable ? handleNonSearchableAutoFocus : undefined)}\n // **2026-05-07 v15.16 nested portal fix**:Tag dismiss inside trigger\n // 區的 OverflowIndicator HoverCard popup(獨立 Radix portal,DOM 不在\n // PopoverContent 內)— Radix DismissableLayer document-level outside\n // detection 跨 portal 視為「outside」→ SelectMenu 被誤關閉。\n // 攔 `onPointerDownOutside`,檢查 click target 是否在另一個 Radix portal\n // 內,是 → preventDefault 取消 close。對齊 Ant Design Select multiSelect\n // tagRender 行為(連續移除不關 dropdown)。\n // SSOT propagation:fix 在 SelectMenu level → Combobox / 其他 SelectMenu\n // consumer 自動受益。\n // **2026-05-07 v15.16 nested portal fix**:Tag dismiss inside trigger 區的\n // OverflowIndicator HoverCard popup(獨立 Radix portal,DOM 不在 SelectMenu\n // PopoverContent 內)— Radix DismissableLayer document-level pointerdown +\n // focusin 偵測「outside」→ SelectMenu 被誤關閉。\n // 攔 `onInteractOutside`(統一 pointerdown + focusin),檢查 click target 是否\n // 在另一個 Radix portal wrapper(`[data-radix-popper-content-wrapper]`),\n // 是 → preventDefault 取消 close。對齊 Ant Design Select multiSelect tagRender\n // 行為(連續移除不關 dropdown)。\n // SSOT propagation:fix 在 SelectMenu level → Combobox / 所有 SelectMenu\n // consumer 自動受益。\n onInteractOutside={(e) => {\n const target = e.detail.originalEvent.target as HTMLElement | null\n if (target?.closest('[data-radix-popper-content-wrapper]')) {\n e.preventDefault()\n }\n }}\n >\n <Command\n shouldFilter={searchable}\n // 2026-07-06 cursor 起點修:單選已有值時 cmdk virtual focus 落在已選項而非第一項。\n // cmdk 1.1.1 初始 state 取 defaultValue、item mount 的 selectFirstItem 有\n // `state.value ||` guard 不覆蓋(dist source 驗證);Popover 關閉即 unmount(無\n // forceMount)→ 每次開啟 remount 重新生效。需搭配下方 CommandItem value={opt.value}。\n defaultValue={selectedOption?.value}\n // 2026-07-06 A11y:combobox accessible name。2026-07-14 修正註解(cmdk 1.1.1 dist 實證):\n // cmdk **無條件**渲 sr-only <label htmlFor={inputId}>(dist/index.mjs\n // `createElement(\"label\",{htmlFor:U.inputId,id:U.labelId,...},b)` — label prop 只決定\n // 文字內容,不決定 label 元素是否渲染)。故 non-searchable 時「空 label + htmlFor 指向\n // 不存在的 input」仍存在(upstream cmdk 限制;空 label 無 accessible name,不產生朗讀\n // 內容)。僅 searchable(真的有 input)時傳內容 = 避免 non-searchable 帶 search\n // placeholder 的 accessible name 卻無對應 input;真要消除 orphan label 需 upstream 修。\n label={searchable ? searchPlaceholder : undefined}\n className=\"bg-transparent\"\n >\n {searchable && (\n <div className={cn(\n 'flex items-center gap-2 px-3 py-1 border-b border-divider',\n size === 'lg' ? 'min-h-[calc(var(--field-height-lg)+8px)]'\n : size === 'sm' ? 'min-h-[calc(var(--field-height-sm)+8px)]'\n : 'min-h-[calc(var(--field-height-md)+8px)]',\n )}>\n <Search size={ICON_SIZE[size as 'sm' | 'md' | 'lg']} className=\"shrink-0 text-fg-muted\" aria-hidden />\n <CommandPrimitive.Input\n placeholder={searchPlaceholder}\n value={search}\n onValueChange={setSearch}\n className={cn(\n 'flex w-full bg-transparent outline-none placeholder:text-fg-muted',\n // M24 disabled state precedence:disabled 時 placeholder 切 fg-disabled(audit dim 34)\n 'disabled:placeholder:text-fg-disabled disabled:text-fg-disabled disabled:cursor-not-allowed',\n size === 'lg' ? 'text-body-lg leading-compact' : 'text-body leading-compact',\n )}\n />\n </div>\n )}\n {/* **2026-05-07 v15.13 R2 fix**:minHeight 從 CommandList 搬到 CommandEmpty。\n 原本 CommandList 永遠套 `minHeight = field-height × minRows + 16px`,結果\n user 過濾出 < minRows 個 match 時 list 底下空一片(eg. 打 'c' 出 2 個 match\n 卻撐高到 3 row 容量,1 row 留白)。 Fix:只有 empty state 才需要 minHeight 撐\n 起 placeholder 視覺;有 results 時 CommandList 自然 fit content。 */}\n {/* aria-busy(2026-07-04):loading 時標注 listbox 忙碌——兌現 select.spec.md「Loading」段\n 「+ aria-busy」承諾(cmdk List 本身即 role=\"listbox\" 容器,wrapper forward props)。 */}\n <CommandList\n className=\"relative\"\n aria-busy={loading || undefined}\n // 2026-07-06 A11y:cmdk List 的 aria-label 由其 `label` prop 渲染(內部 spread 後 override,\n // 直接傳 aria-label 會被 cmdk default \"Suggestions\" 蓋掉 silent 失效)— 必走 label prop。\n label=\"選項\" // i18n-allow: DS default; listbox accessible name\n >\n <CommandEmpty\n className=\"flex items-center justify-center\"\n style={{ minHeight: getMenuListMinHeight(size, minRows) }}\n >\n {loading\n ? <Empty icon={<CircularProgress size={48}/>} className=\"py-6\" />\n : <Empty description={emptyText} className=\"py-6\" />}\n </CommandEmpty>\n\n {groupedOptions.map((group, gi) => (\n <React.Fragment key={group.key}>\n {gi > 0 && <CommandSeparator />}\n <CommandGroup\n className=\"p-0 py-2 [&_[cmdk-group-heading]]:p-0\"\n // 2026-07-06 A11y:群組標題走 cmdk `heading`(自動產 cmdk-group-heading id,選項容器\n // role=\"group\" + aria-labelledby 指向之)取代手刻 child row(原本 AT 不可感知;\n // aria-label fallback 對 role=\"presentation\" group 無效)。[&_[cmdk-group-heading]]:p-0\n // 中和 command.tsx base 的 px-3 py-1.5(tailwind-merge p-0 勝);字級 / 顏色由 MenuItem\n // header 自帶(text-body leading-compact + font-medium + text-fg-muted),視覺不變。\n heading={group.label ? <MenuItem size={size} header>{group.label}</MenuItem> : undefined}\n >\n {group.options.map((opt) => (\n <CommandItem\n key={opt.value}\n // 2026-07-06 識別值修:cmdk 以 value 當 item identity + selection state。原用\n // opt.label → 重複 label 時雙亮 + Enter 選錯;改唯一的 opt.value,label 移入\n // keywords 保搜尋照樣命中(cmdk filter 對 value + keywords 計分)。\n value={opt.value}\n keywords={opt.description ? [opt.label, opt.description] : [opt.label]}\n disabled={opt.disabled}\n onSelect={() => handleSelect(opt.value)}\n // 2026-07-05 D4 P0 修:原 data-[selected=true]:bg-transparent 蓋掉 command.tsx base\n // 的 data-[selected=true]:bg-neutral-hover = cmdk virtual focus(aria-activedescendant)\n // 鍵盤 cursor 唯一視覺通道被抹掉 → 方向鍵移動畫面零變化(影響 Select/Combobox/PeoplePicker\n // 全家)。鏡射 DropdownMenu wrapper pattern:互動 bg 單一 owner 在外層 CommandItem\n //(base highlight 恢復;persistent selection 上移;selected×cursor 加深一階 —\n // 對齊本日 DropdownMenu Q2 決策),內層 MenuItem 全透明。\n className={cn(\n 'p-0 rounded-none',\n !multiple && isSelected(opt.value) && 'bg-neutral-selected data-[selected=true]:bg-neutral-selected-active',\n )}\n >\n <MenuItem\n size={size}\n startIcon={opt.icon}\n startIconClassName={opt.iconClassName}\n avatar={opt.avatar}\n description={opt.description}\n checkbox={multiple}\n checked={isSelected(opt.value)}\n selected={!multiple && isSelected(opt.value)}\n disabled={opt.disabled}\n // 2026-07-05 D4:巢狀 role 修 — MenuItem 預設 role=\"option\" + aria-selected(persistent\n // selection)巢狀在 cmdk CommandItem(本身 role=\"option\" + aria-selected=cursor)內,\n // AT 讀到 option 包 option 且內外 aria-selected 語意相反 → 朗讀錯亂。鏡射 DropdownMenu\n // canonical(dropdown-menu.tsx「Pure visual — Radix parent handles role/aria」):\n // 內層純視覺 role=\"presentation\",cmdk CommandItem 是唯一 option 節點。\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent\"\n >\n {renderLabel ? renderLabel(opt) : opt.label}\n </MenuItem>\n </CommandItem>\n ))}\n </CommandGroup>\n </React.Fragment>\n ))}\n\n {/* Creatable item */}\n {showCreate && (\n <>\n <CommandSeparator />\n <CommandGroup className=\"p-0 py-2\">\n <CommandItem\n // 2026-07-06:`__create__` 前綴防 identity 撞名 — 選項 row 改用 opt.value 識別後,\n // search 恰等於某 option.value 時裸 search 會與該 row 同 value(cmdk 雙亮 + Enter\n // 選錯)。value 含 search 子字串,cmdk filter 照樣命中;onSelect closure 讀 search\n // 不受影響。\n value={`__create__${search}`}\n onSelect={() => {\n onCreate?.(search.trim())\n setSearch('')\n }}\n className=\"p-0 rounded-none\"\n >\n {/* 2026-07-05 D4:同上方選項 row 的巢狀 role 修 — creatable row 同樣是\n MenuItem 巢狀在 cmdk CommandItem(option)內,內層純視覺 role=\"presentation\"。 */}\n <MenuItem size={size} startIcon={Plus} role=\"presentation\" className=\"!bg-transparent hover:!bg-transparent\">\n {createLabel(search.trim())}\n </MenuItem>\n </CommandItem>\n </CommandGroup>\n </>\n )}\n </CommandList>\n\n {/* SR 播報 empty / loading 空狀態(見 SelectMenuLiveStatus docblock,2026-07-05 D4) */}\n <SelectMenuLiveStatus loading={loading} emptyText={emptyText} />\n\n {/* Multi-select footer: Select All\n - 沒有選項時不顯示(selectableOptions.length === 0)\n - 搜尋有文字時不顯示(search 非空 = 使用者在找特定項目,「全選」沒意義) */}\n {multiple && selectableOptions.length > 0 && !search && (\n <MenuFooter>\n {/* 2026-07-05 D4:全選列鍵盤可達修 — 原裸 MenuItem(div 預設 role=\"option\" 無 tabIndex)\n 位於 CommandList 之外:cmdk 方向鍵只導覽 [cmdk-item]、Tab 也到不了 div → 鍵盤使用者\n 完全無法操作全選(WCAG 2.1.1),且該 role=\"option\" 無 listbox 祖先(orphan,axe\n aria-required-parent)。改真實 focusable checkbox 語意:tabIndex=0 + role=\"checkbox\" +\n aria-checked(indeterminate → \"mixed\")+ Enter / Space 觸發;preventDefault 讓 cmdk root\n onKeyDown(源碼檢查 e.defaultPrevented)不會再對 active option 重複觸發 Enter。\n aria-selected 顯式蓋回 undefined(MenuItem 內建 aria-selected 對 role=\"checkbox\" 無效)。 */}\n <MenuItem\n size={size}\n checkbox\n checked={allState}\n onClick={handleSelectAll}\n role=\"checkbox\"\n aria-checked={allState === 'indeterminate' ? 'mixed' : allState}\n aria-selected={undefined}\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n handleSelectAll()\n }\n }}\n >\n {selectAllLabel}\n </MenuItem>\n </MenuFooter>\n )}\n </Command>\n </PopoverContent>\n </RowSizeProvider>\n </Popover>\n )\n})\n\nSelectMenu.displayName = 'SelectMenu'\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const selectMenuMeta = {\n component: 'SelectMenu',\n // 2026-07-14 修 family 自我矛盾:原 Phase-1 mechanical 填 4(Field control),與 spec body\n // 「Layout Family:非上述 family — composite / multi-section」矛盾。SelectMenu 是 Popover +\n // Command composite overlay(trigger 由 consumer Select / Combobox own,Family 4 是 trigger\n // 側 field control 的事);對齊 DS composite 慣例(Accordion / Command / Popover meta 皆\n // family: null + spec frontmatter composite)。\n family: null, // non-family composite / overlay(對齊 spec frontmatter composite)\n variants: {\n\n },\n sizes: {\n\n },\n // 'selected' = 單選 option 持續選中(bg-neutral-selected);'active' 保留 — cmdk virtual-focus on\n // selected 走 bg-neutral-selected-active(active 階 token;2026-07-07 詞彙統一補修)。\n states: ['default', 'hover', 'active', 'selected', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-neutral-selected', 'bg-neutral-selected-active', 'bg-surface-raised', 'bg-transparent'],\n fg: ['text-fg-muted'],\n ring: [],\n },\n} as const\n\n/**\n * 2026-07-05 D4 P0 修(searchable 鍵盤死路):trigger 內的裸 <input> 與 portal 內的 cmdk root\n * 在不同 DOM 子樹 — 鍵盤事件永遠 bubble 不到 cmdk 的 ArrowUp/Down/Enter handler([cmdk-root]\n * onKeyDown)→ searchable Select / PeoplePicker single / Combobox searchIn='trigger' 開啟後\n * 只能 Esc。修法 = APG combobox-with-list:trigger input 把三鍵 re-dispatch 給 cmdk root\n * (native KeyboardEvent bubbles 經 React root delegation 觸發 cmdk synthetic handler)。\n * Home/End 刻意不轉送(文字輸入的 caret 語意優先,對齊 MUI/Ant Autocomplete)。\n * aria-activedescendant 綁回 trigger input → 見下方 useActiveDescendant(2026-07-05 D4 補齊)。\n */\nexport function forwardKeyToListbox(contentId: string | undefined, e: React.KeyboardEvent): boolean {\n if (!contentId) return false\n if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp' && e.key !== 'Enter') return false\n const root = document.getElementById(contentId)?.querySelector<HTMLElement>('[cmdk-root]')\n if (!root) return false\n e.preventDefault()\n root.dispatchEvent(new KeyboardEvent('keydown', { key: e.key, bubbles: true, cancelable: true }))\n return true\n}\n\n/**\n * 2026-07-05 D4 補齊(APG combobox aria-activedescendant):追蹤 cmdk 目前 virtual-focus item\n * 的 DOM id,供 trigger 端搜尋 input 綁 `aria-activedescendant` —— SR 才會在方向鍵導覽 /\n * 打字過濾時播報 active option 名。機制:trigger 與 portal 內 cmdk 分屬不同 DOM 子樹,cmdk\n * 只把 active id 綁在自己的 Command.Input / List(cmdk source:item 自帶 auto-generated id +\n * `data-selected=\"true\"` 標記 virtual focus)→ trigger 端用 MutationObserver 監聽 popover 容器\n * (contentId = PopoverContent id)內 `data-selected` 屬性變化 + childList(打字過濾 re-render\n * 換 item 節點),單一機制涵蓋全部更新路徑:開啟初始 auto-highlight / forwardKeyToListbox\n * 方向鍵轉送 / 搜尋過濾後 cmdk 自動移 cursor / pointer hover。\n * 關閉時清 undefined —— ARIA 要求 id 必指向存在於 DOM 的節點,不可留 stale id。\n */\nexport function useActiveDescendant(contentId: string | undefined, open: boolean): string | undefined {\n const [activeId, setActiveId] = React.useState<string | undefined>(undefined)\n React.useEffect(() => {\n if (!open || !contentId) {\n setActiveId(undefined)\n return\n }\n // PopoverContent 與 trigger 同一個 React commit mount(open state 同批 render)→ effect 跑時已在 DOM\n const container = document.getElementById(contentId)\n if (!container) return\n const read = () => {\n setActiveId(container.querySelector<HTMLElement>('[cmdk-item][data-selected=\"true\"]')?.id || undefined)\n }\n // 初始補讀:MutationObserver 只看「觀察開始後」的變化;cmdk 初始 auto-highlight(layout effect\n // 排程)可能已 commit → rAF 讀當下狀態兜底,與 observer 互補、誰先到都不漏。\n const raf = requestAnimationFrame(read)\n const observer = new MutationObserver(read)\n observer.observe(container, { subtree: true, childList: true, attributes: true, attributeFilter: ['data-selected'] })\n return () => {\n cancelAnimationFrame(raf)\n observer.disconnect()\n }\n }, [open, contentId])\n return activeId\n}\n\nexport { SelectMenu }\n"],"names":["SelectMenu","CommandPrimitive"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { FieldSize } from '../../components/Field/field-context';
|
|
2
2
|
/**
|
|
3
3
|
* BooleanValueIcon — boolean 值的展示符號(true = Check 勾 / false = X 叉),中性 `text-foreground` 色。
|
|
4
|
-
* Checkbox / Switch 的
|
|
4
|
+
* Checkbox / Switch 的 view + readonly 態、DataTable boolean cell(經 Checkbox)全消費此 SSOT。
|
|
5
5
|
*/
|
|
6
6
|
export declare function BooleanValueIcon({ checked, size, className, }: {
|
|
7
7
|
checked: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boolean-value.js","sources":["../../../src/components/SelectionControl/boolean-value.tsx"],"sourcesContent":["// @internal — boolean 值展示符號 SSOT(
|
|
1
|
+
{"version":3,"file":"boolean-value.js","sources":["../../../src/components/SelectionControl/boolean-value.tsx"],"sourcesContent":["// @internal — boolean 值展示符號 SSOT(view / readonly 態);consumer 用 Checkbox / Switch 不直用。\n// ── 消費的 SSOT ──\n// - lucide-react Check / X(DS icon canonical:成功/勾 = Check、關閉/否 = X,見 ui-development.md「Icon canonical」)\n// - components/Field/field-context.ts(FieldSize 型別 → icon 尺寸對齊 value 文字)\n// - tokens/color:text-foreground(展示值中性色,同 readonly value / 空值 canonical)\n//\n// 世界級對照(M22,2026-07-09 本 session 查證):read-only boolean 顯示 = 勾/叉 icon,中性色(非紅綠):\n// MUI X DataGrid boolean cell = Check / Close(X)icon,default 中性色(非 red/green)\n// Refine(Ant)BooleanField https://refine.dev/docs/ui-integrations/ant-design/components/fields/boolean-field/ = 勾 / 叉\n// React-admin BooleanField https://marmelab.com/react-admin/BooleanField.html = TrueIcon / FalseIcon(預設 勾 / 叉)\n// 為何中性色不用紅叉綠勾:false = 「否」值本身,非「錯誤 / 被拒」;error / warning 是另一條 error token 通道,\n// 不靠此 glyph 表達。true/false 對稱同色 = MUI/React-admin 共識。\nimport { Check, X } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport type { FieldSize } from '@/design-system/components/Field/field-context'\n\n// icon 尺寸對齊 value 文字(fieldDisplayTextClass:sm/md → text-body 14px、lg → text-body-lg 16px)。\nconst booleanIconSize: Record<FieldSize, string> = {\n sm: 'size-3.5', // 14px\n md: 'size-3.5', // 14px\n lg: 'size-4', // 16px\n}\n\n/**\n * BooleanValueIcon — boolean 值的展示符號(true = Check 勾 / false = X 叉),中性 `text-foreground` 色。\n * Checkbox / Switch 的 view + readonly 態、DataTable boolean cell(經 Checkbox)全消費此 SSOT。\n */\nexport function BooleanValueIcon({\n checked,\n size = 'md',\n className,\n}: {\n checked: boolean\n size?: FieldSize\n className?: string\n}) {\n const Icon = checked ? Check : X\n return <Icon aria-hidden className={cn(booleanIconSize[size], 'text-foreground', className)} />\n}\n"],"names":[],"mappings":";;;AAiBA,MAAM,kBAA6C;AAAA,EACjD,IAAI;AAAA;AAAA,EACJ,IAAI;AAAA;AAAA,EACJ,IAAI;AAAA;AACN;AAMO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA,OAAO;AAAA,EACP;AACF,GAIG;AACD,QAAM,OAAO,UAAU,QAAQ;AAC/B,SAAO,oBAAC,MAAA,EAAK,eAAW,MAAC,WAAW,GAAG,gBAAgB,IAAI,GAAG,mBAAmB,SAAS,EAAA,CAAG;AAC/F;"}
|
|
@@ -33,14 +33,14 @@ declare const SheetOverlay: React.ForwardRefExoticComponent<Omit<SheetPrimitive.
|
|
|
33
33
|
declare const sheetVariants: (props?: ({
|
|
34
34
|
side?: "bottom" | "left" | "right" | "top" | null | undefined;
|
|
35
35
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
36
|
-
interface SheetContentProps extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>, VariantProps<typeof sheetVariants> {
|
|
36
|
+
interface SheetContentProps extends Omit<React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>, 'asChild'>, VariantProps<typeof sheetVariants> {
|
|
37
37
|
}
|
|
38
38
|
/** @internal DS 預設 open-focus(首個 body 互動元素)。consumer 傳自訂 onOpenAutoFocus 會覆寫
|
|
39
39
|
* 本預設({...props} 在後),需自訂前置行為(如 AppShellAside opener snapshot)時 import 接力呼叫。 */
|
|
40
40
|
export declare const handleSheetOpenAutoFocus: (e: Event) => void;
|
|
41
41
|
declare const SheetContent: React.ForwardRefExoticComponent<SheetContentProps & React.RefAttributes<HTMLDivElement>>;
|
|
42
42
|
declare const SheetHeader: React.ForwardRefExoticComponent<SurfaceHeaderProps & React.RefAttributes<HTMLDivElement>>;
|
|
43
|
-
declare const SheetBody: React.ForwardRefExoticComponent<Omit<Omit<import("@radix-ui/react-scroll-area").ScrollAreaProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
|
|
43
|
+
declare const SheetBody: React.ForwardRefExoticComponent<Omit<Omit<Omit<import("@radix-ui/react-scroll-area").ScrollAreaProps & React.RefAttributes<HTMLDivElement>, "ref">, "asChild"> & {
|
|
44
44
|
fillX?: boolean;
|
|
45
45
|
viewportTabIndex?: 0 | -1;
|
|
46
46
|
} & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sheet.d.ts","sourceRoot":"","sources":["../../../src/components/Sheet/sheet.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,cAAc,MAAM,wBAAwB,CAAA;AACxD,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAIjE,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,0DAA0D,CAAA;AAKjE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,QAAA,MAAM,KAAK,sCAAsB,CAAA;AAEjC,QAAA,MAAM,YAAY,6GAAyB,CAAA;AAE3C,QAAA,MAAM,UAAU,2GAAuB,CAAA;AAEvC,QAAA,MAAM,WAAW,4CAAwB,CAAA;AAEzC,QAAA,MAAM,YAAY,6JAahB,CAAA;AAMF,QAAA,MAAM,aAAa;;8EAqBlB,CAAA;
|
|
1
|
+
{"version":3,"file":"sheet.d.ts","sourceRoot":"","sources":["../../../src/components/Sheet/sheet.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,cAAc,MAAM,wBAAwB,CAAA;AACxD,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAIjE,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,0DAA0D,CAAA;AAKjE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,QAAA,MAAM,KAAK,sCAAsB,CAAA;AAEjC,QAAA,MAAM,YAAY,6GAAyB,CAAA;AAE3C,QAAA,MAAM,UAAU,2GAAuB,CAAA;AAEvC,QAAA,MAAM,WAAW,4CAAwB,CAAA;AAEzC,QAAA,MAAM,YAAY,6JAahB,CAAA;AAMF,QAAA,MAAM,aAAa;;8EAqBlB,CAAA;AAKD,UAAU,iBACR,SAAQ,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,OAAO,cAAc,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,EACpF,YAAY,CAAC,OAAO,aAAa,CAAC;CAAG;AAGzC;gFACgF;AAChF,eAAO,MAAM,wBAAwB,GAAI,GAAG,KAAK,SAUhD,CAAA;AAED,QAAA,MAAM,YAAY,0FAgBhB,CAAA;AAOF,QAAA,MAAM,WAAW,2FAgBf,CAAA;AAeF,QAAA,MAAM,SAAS;;;sFAcb,CAAA;AAIF,QAAA,MAAM,WAAW,6GAGiE,CAAA;AAGlF,QAAA,MAAM,UAAU,mKASd,CAAA;AAGF,QAAA,MAAM,gBAAgB,6KAWpB,CAAA;AAKF,eAAO,MAAM,SAAS;;;;;;;;;;;CAgBZ,CAAA;AAEV,OAAO,EACL,KAAK,EACL,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,WAAW,EACX,SAAS,EACT,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,aAAa,GACd,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sheet.js","sources":["../../../src/components/Sheet/sheet.tsx"],"sourcesContent":["// @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.\nimport * as React from \"react\"\nimport * as SheetPrimitive from \"@radix-ui/react-dialog\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { X as XIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport {\n SurfaceHeader,\n SurfaceFooter,\n type SurfaceHeaderProps,\n} from \"@/design-system/patterns/overlay-surface/overlay-surface\"\nimport { Button } from \"@/design-system/components/Button/button\"\nimport { ScrollArea } from \"@/design-system/components/ScrollArea/scroll-area\"\nimport { surfaceMotion } from \"@/design-system/tokens/motion/overlay-motion\"\n\n/**\n * Sheet — **右側 Dialog primitive**(給消費者的 canonical)。\n *\n * ── 定位(2026-04-21 canonical)──\n * Sheet 給**消費者**用的唯一合法形式 = **右側開啟的 modal**(side=\"right\"),\n * 內部結構跟 `Dialog` 一致:`SheetHeader` / `SheetBody` / `SheetFooter`(Header / Footer 消費\n * `SurfaceHeader` / `SurfaceFooter` primitive;Body = `ScrollArea` + 內層 padding div,\n * padding token SSOT 在 `patterns/overlay-surface/`)。side=\"right\" 是 defaultVariants,消費者不傳 side。\n *\n * ── 其他 side(top / bottom / left)——**非消費者 API**,內部基建用 ──\n * top / bottom / left 變體保留給 DS 內部基建(例:Sidebar 在小尺寸視口時從 left 滑入)。\n * 消費者 code **禁止** 傳 `side=\"top\" | \"bottom\" | \"left\"` — 這些用途需 user 授權。\n *\n * ── 跟 Dialog 的差異 ──\n * - Dialog = 中央 modal,用於「明確決策 / 表單 / 確認」\n * - Sheet(side=\"right\")= 側滑 modal,用於「補充資訊 / 多欄位表單 / 編輯 flow」\n * - 兩者 API 結構 1:1 對應,差異只在 side / 動畫 / 初始寬度\n *\n * ── Header / Footer 消費 SurfaceXxx SSOT;Body 走 ScrollArea canonical ──\n * 避免 padding 漂移 — Dialog / Popover / Sheet / Coachmark 共用同一套 overlay-surface\n * padding token(px-loose / py-tight),改 overlay-surface.tsx 四者自動跟進;\n * SheetBody 同 DialogBody:ScrollArea + 內層 px-loose / pt-tight / pb-bottom(詳 SheetBody comment)。\n */\n\nconst Sheet = SheetPrimitive.Root\n\nconst SheetTrigger = SheetPrimitive.Trigger\n\nconst SheetClose = SheetPrimitive.Close\n\nconst SheetPortal = SheetPrimitive.Portal\n\nconst SheetOverlay = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Overlay\n // motion-reduce:animate-none — prefers-reduced-motion 豁免,對齊 DialogOverlay canonical(dialog.tsx DialogOverlay 同 token,2026-07-05 P2 收乾)\n className={cn(\n \"fixed inset-0 z-50 bg-overlay data-[state=open]:animate-in data-[state=closed]:animate-out motion-reduce:animate-none data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n ref={ref}\n />\n))\nSheetOverlay.displayName = SheetPrimitive.Overlay.displayName\n\n// ── sheetVariants ─────────────────────────────────────────────────────────\n// side=\"right\" 給**消費者**。top/bottom/left 給 **DS 內部基建**用(如 Sidebar 在\n// narrow viewport 時切 side=\"left\")。消費者 code 不傳 side,用 default。\nconst sheetVariants = cva(\n // 核心容器 — 無 padding(由 SheetBody / SheetHeader / SheetFooter 自理 padding,\n // 對齊 overlay-surface pattern + Dialog canonical)\n // Animation canonical:panel = surfaceMotion 250ms(--motion-duration-surface)雙向一致\n // (D4 audit:500ms 太久 sluggish)+ motion-reduce 豁免\n `fixed z-50 flex flex-col bg-surface-raised shadow-[var(--elevation-200)] transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out ${surfaceMotion}`,\n {\n variants: {\n side: {\n top: \"inset-x-0 top-0 border-b border-divider data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top\",\n bottom:\n \"inset-x-0 bottom-0 border-t border-divider data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom\",\n left: \"inset-y-0 left-0 h-full w-3/4 border-r border-divider data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-md\",\n right:\n \"inset-y-0 right-0 h-full w-3/4 border-l border-divider data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-md\",\n },\n },\n defaultVariants: {\n side: \"right\",\n },\n }\n)\n\ninterface SheetContentProps\n extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,\n VariantProps<typeof sheetVariants> {}\n\n// AutoFocus canonical(對齊 Dialog / Material / Polaris)— 見 dialog.tsx handleOpenAutoFocus 註解\n/** @internal DS 預設 open-focus(首個 body 互動元素)。consumer 傳自訂 onOpenAutoFocus 會覆寫\n * 本預設({...props} 在後),需自訂前置行為(如 AppShellAside opener snapshot)時 import 接力呼叫。 */\nexport const handleSheetOpenAutoFocus = (e: Event) => {\n e.preventDefault()\n const content = e.currentTarget as HTMLElement\n const firstBodyTarget = content.querySelector<HTMLElement>(\n '[data-sheet-body] input:not([disabled]),[data-sheet-body] textarea:not([disabled]),[data-sheet-body] select:not([disabled]),[data-sheet-body] button:not([disabled]):not([data-dismiss])'\n )\n const firstFooterButton = content.querySelector<HTMLElement>(\n '[data-sheet-footer] button:not([disabled]):not([data-dismiss])'\n )\n ;(firstBodyTarget ?? firstFooterButton ?? content).focus({ preventScroll: true })\n}\n\nconst SheetContent = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Content>,\n SheetContentProps\n>(({ side = \"right\", className, children, ...props }, ref) => (\n <SheetPortal>\n <SheetOverlay />\n <SheetPrimitive.Content\n ref={ref}\n onOpenAutoFocus={handleSheetOpenAutoFocus}\n // Sheet 不自設 density,繼承 page 層級的 `html[data-density]`(2026-04-21 canonical 定案)\n className={cn(sheetVariants({ side }), className)}\n {...props}\n >\n {children}\n </SheetPrimitive.Content>\n </SheetPortal>\n))\nSheetContent.displayName = SheetPrimitive.Content.displayName\n\n// ── SheetHeader:SurfaceHeader + Close X(對齊 DialogHeader canonical)──────────\n// 2026-05-18 audit gap fix:type 對齊 SurfaceHeaderProps,withTabs / tabsSlot expose\n// 給 consumer(per header-canonical.spec.md W1 跨 6 consumer 同契約)。Spread 早 forward\n// 過去,只是 TS type 沒 expose 導致 consumer 不能 type-safe 用 `<SheetHeader withTabs>`。\nconst SheetHeader = React.forwardRef<\n HTMLDivElement,\n SurfaceHeaderProps\n>(({ className, children, ...props }, ref) => (\n // 2026-05-18:className 不再硬加 justify-between(同 DialogHeader 邏輯,避 column mode 破裂)。\n <SurfaceHeader\n ref={ref}\n className={className}\n {...props}\n >\n <div className=\"flex-1 min-w-0\">{children}</div>\n {/* Dismiss X = native sm,SurfaceHeader 負 my trick 讓 layout 佔位 24 → chrome-header-height */}\n <SheetPrimitive.Close asChild>\n <Button data-dismiss iconOnly dismiss size=\"sm\" startIcon={XIcon} aria-label=\"關閉\" />\n </SheetPrimitive.Close>\n </SurfaceHeader>\n))\nSheetHeader.displayName = \"SheetHeader\"\n\n// ── SheetBody:flex-1 ScrollArea + chrome padding(對齊 DialogBody + ScrollArea canonical) ──\n// 捲軸必用 ScrollArea(跨 OS 一致、不吃寬度)— 不自寫 overflow-y-auto。\n// padding 搬進 viewport inner div:px-loose / pt-tight / pb-bottom。\n// data-sheet-body:讓 SheetContent onOpenAutoFocus 找得到 body 第一個互動元素\n//\n// ── List-as-region 場景(menu / nav / settings list)──\n// 不再提供 `flush` variant(2026-05-01 移除)。canonical = consumer 用 className override:\n// `<SheetBody className=\"!px-0 !pt-0 !pb-0\"><div className=\"py-2\">{items}</div></SheetBody>` ← @tabs-content-gap-ok: JSDoc 文件範例(list-as-region canonical,非 tabs !pt-0 hack;對齊 dialog.tsx 同款 marker)\n// 詳 DialogBody comment + `tokens/layoutSpace/layoutSpace.spec.md`「List-as-region in overlay body」\n// `className` forward 到 **inner content div**(非外層 ScrollArea wrapper)——\n// consumer `<SheetBody className=\"flex flex-col gap-X\">` 期望作用於 children 排列;\n// 套在 ScrollArea 上會 0 效果(children 住 inner div),曾造成 Sheet form field 完全貼邊。\nconst SheetBody = React.forwardRef<\n HTMLDivElement,\n React.ComponentPropsWithoutRef<typeof ScrollArea>\n>(({ className, children, ...props }, ref) => (\n <ScrollArea ref={ref} data-sheet-body className=\"flex-1 min-h-0\" {...props}>\n <div\n className={cn(\n \"px-[var(--layout-space-loose)] pt-[var(--layout-space-tight)] pb-[var(--layout-space-bottom)]\",\n className,\n )}\n >\n {children}\n </div>\n </ScrollArea>\n))\nSheetBody.displayName = \"SheetBody\"\n\n// ── SheetFooter:SurfaceFooter wrap 加 data-sheet-footer(autoFocus fallback target)──\nconst SheetFooter = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ ...props }, ref) => <SurfaceFooter ref={ref} data-sheet-footer {...props} />)\nSheetFooter.displayName = \"SheetFooter\"\n\nconst SheetTitle = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Title\n ref={ref}\n className={cn(\"text-body-lg font-medium truncate\", className)}\n {...props}\n />\n))\nSheetTitle.displayName = SheetPrimitive.Title.displayName\n\nconst SheetDescription = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Description\n ref={ref}\n // title → description 間距 canonical:SheetTitle body-lg(16)+ desc body(14)→ reading-lg token\n // (label tier 決定;對齊 Dialog canonical。Tailwind preflight reset h2/p margin=0 → 必顯式 mt)\n className={cn(\"mt-[var(--item-gap-label-desc-reading-lg)] text-body text-fg-secondary\", className)}\n {...props}\n />\n))\nSheetDescription.displayName = SheetPrimitive.Description.displayName\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const sheetMeta = {\n component: 'Sheet',\n family: null, // non-family composite / overlay / layout\n variants: {\n\n },\n sizes: {\n\n },\n // 2026-07-04 audit 對齊:Sheet 本身無 disabled state(surface container,spec 邊界案例明文;對齊 popover/dialog meta pattern)\n states: ['default'],\n tokens: {\n bg: ['bg-surface-raised'],\n fg: ['text-fg-secondary', 'text-foreground'],\n ring: [],\n },\n} as const\n\nexport {\n Sheet,\n SheetPortal,\n SheetOverlay,\n SheetTrigger,\n SheetClose,\n SheetContent,\n SheetHeader,\n SheetBody,\n SheetFooter,\n SheetTitle,\n SheetDescription,\n sheetVariants,\n}\n"],"names":["SheetPrimitive","XIcon"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sheet.js","sources":["../../../src/components/Sheet/sheet.tsx"],"sourcesContent":["// @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.\nimport * as React from \"react\"\nimport * as SheetPrimitive from \"@radix-ui/react-dialog\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { X as XIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport {\n SurfaceHeader,\n SurfaceFooter,\n type SurfaceHeaderProps,\n} from \"@/design-system/patterns/overlay-surface/overlay-surface\"\nimport { Button } from \"@/design-system/components/Button/button\"\nimport { ScrollArea } from \"@/design-system/components/ScrollArea/scroll-area\"\nimport { surfaceMotion } from \"@/design-system/tokens/motion/overlay-motion\"\n\n/**\n * Sheet — **右側 Dialog primitive**(給消費者的 canonical)。\n *\n * ── 定位(2026-04-21 canonical)──\n * Sheet 給**消費者**用的唯一合法形式 = **右側開啟的 modal**(side=\"right\"),\n * 內部結構跟 `Dialog` 一致:`SheetHeader` / `SheetBody` / `SheetFooter`(Header / Footer 消費\n * `SurfaceHeader` / `SurfaceFooter` primitive;Body = `ScrollArea` + 內層 padding div,\n * padding token SSOT 在 `patterns/overlay-surface/`)。side=\"right\" 是 defaultVariants,消費者不傳 side。\n *\n * ── 其他 side(top / bottom / left)——**非消費者 API**,內部基建用 ──\n * top / bottom / left 變體保留給 DS 內部基建(例:Sidebar 在小尺寸視口時從 left 滑入)。\n * 消費者 code **禁止** 傳 `side=\"top\" | \"bottom\" | \"left\"` — 這些用途需 user 授權。\n *\n * ── 跟 Dialog 的差異 ──\n * - Dialog = 中央 modal,用於「明確決策 / 表單 / 確認」\n * - Sheet(side=\"right\")= 側滑 modal,用於「補充資訊 / 多欄位表單 / 編輯 flow」\n * - 兩者 API 結構 1:1 對應,差異只在 side / 動畫 / 初始寬度\n *\n * ── Header / Footer 消費 SurfaceXxx SSOT;Body 走 ScrollArea canonical ──\n * 避免 padding 漂移 — Dialog / Popover / Sheet / Coachmark 共用同一套 overlay-surface\n * padding token(px-loose / py-tight),改 overlay-surface.tsx 四者自動跟進;\n * SheetBody 同 DialogBody:ScrollArea + 內層 px-loose / pt-tight / pb-bottom(詳 SheetBody comment)。\n */\n\nconst Sheet = SheetPrimitive.Root\n\nconst SheetTrigger = SheetPrimitive.Trigger\n\nconst SheetClose = SheetPrimitive.Close\n\nconst SheetPortal = SheetPrimitive.Portal\n\nconst SheetOverlay = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Overlay\n // motion-reduce:animate-none — prefers-reduced-motion 豁免,對齊 DialogOverlay canonical(dialog.tsx DialogOverlay 同 token,2026-07-05 P2 收乾)\n className={cn(\n \"fixed inset-0 z-50 bg-overlay data-[state=open]:animate-in data-[state=closed]:animate-out motion-reduce:animate-none data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n ref={ref}\n />\n))\nSheetOverlay.displayName = SheetPrimitive.Overlay.displayName\n\n// ── sheetVariants ─────────────────────────────────────────────────────────\n// side=\"right\" 給**消費者**。top/bottom/left 給 **DS 內部基建**用(如 Sidebar 在\n// narrow viewport 時切 side=\"left\")。消費者 code 不傳 side,用 default。\nconst sheetVariants = cva(\n // 核心容器 — 無 padding(由 SheetBody / SheetHeader / SheetFooter 自理 padding,\n // 對齊 overlay-surface pattern + Dialog canonical)\n // Animation canonical:panel = surfaceMotion 250ms(--motion-duration-surface)雙向一致\n // (D4 audit:500ms 太久 sluggish)+ motion-reduce 豁免\n `fixed z-50 flex flex-col bg-surface-raised shadow-[var(--elevation-200)] transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out ${surfaceMotion}`,\n {\n variants: {\n side: {\n top: \"inset-x-0 top-0 border-b border-divider data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top\",\n bottom:\n \"inset-x-0 bottom-0 border-t border-divider data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom\",\n left: \"inset-y-0 left-0 h-full w-3/4 border-r border-divider data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-md\",\n right:\n \"inset-y-0 right-0 h-full w-3/4 border-l border-divider data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-md\",\n },\n },\n defaultVariants: {\n side: \"right\",\n },\n }\n)\n\n// asChild Omit(2026-07-18 決策2):SheetContent 是固定 edge-anchored surface(overlay + sheetVariants\n// 定位/slide 動畫 + onOpenAutoFocus),恆渲染 {children}(consumer body 多節點)→ <Content asChild>\n// Radix Slot React.Children.only crash。children 保留(consumer body)。同 Dialog 收窄。\ninterface SheetContentProps\n extends Omit<React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>, 'asChild'>,\n VariantProps<typeof sheetVariants> {}\n\n// AutoFocus canonical(對齊 Dialog / Material / Polaris)— 見 dialog.tsx handleOpenAutoFocus 註解\n/** @internal DS 預設 open-focus(首個 body 互動元素)。consumer 傳自訂 onOpenAutoFocus 會覆寫\n * 本預設({...props} 在後),需自訂前置行為(如 AppShellAside opener snapshot)時 import 接力呼叫。 */\nexport const handleSheetOpenAutoFocus = (e: Event) => {\n e.preventDefault()\n const content = e.currentTarget as HTMLElement\n const firstBodyTarget = content.querySelector<HTMLElement>(\n '[data-sheet-body] input:not([disabled]),[data-sheet-body] textarea:not([disabled]),[data-sheet-body] select:not([disabled]),[data-sheet-body] button:not([disabled]):not([data-dismiss])'\n )\n const firstFooterButton = content.querySelector<HTMLElement>(\n '[data-sheet-footer] button:not([disabled]):not([data-dismiss])'\n )\n ;(firstBodyTarget ?? firstFooterButton ?? content).focus({ preventScroll: true })\n}\n\nconst SheetContent = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Content>,\n SheetContentProps\n>(({ side = \"right\", className, children, ...props }, ref) => (\n <SheetPortal>\n <SheetOverlay />\n <SheetPrimitive.Content\n ref={ref}\n onOpenAutoFocus={handleSheetOpenAutoFocus}\n // Sheet 不自設 density,繼承 page 層級的 `html[data-density]`(2026-04-21 canonical 定案)\n className={cn(sheetVariants({ side }), className)}\n {...props}\n >\n {children}\n </SheetPrimitive.Content>\n </SheetPortal>\n))\nSheetContent.displayName = SheetPrimitive.Content.displayName\n\n// ── SheetHeader:SurfaceHeader + Close X(對齊 DialogHeader canonical)──────────\n// 2026-05-18 audit gap fix:type 對齊 SurfaceHeaderProps,withTabs / tabsSlot expose\n// 給 consumer(per header-canonical.spec.md W1 跨 6 consumer 同契約)。Spread 早 forward\n// 過去,只是 TS type 沒 expose 導致 consumer 不能 type-safe 用 `<SheetHeader withTabs>`。\nconst SheetHeader = React.forwardRef<\n HTMLDivElement,\n SurfaceHeaderProps\n>(({ className, children, ...props }, ref) => (\n // 2026-05-18:className 不再硬加 justify-between(同 DialogHeader 邏輯,避 column mode 破裂)。\n <SurfaceHeader\n ref={ref}\n className={className}\n {...props}\n >\n <div className=\"flex-1 min-w-0\">{children}</div>\n {/* Dismiss X = native sm,SurfaceHeader 負 my trick 讓 layout 佔位 24 → chrome-header-height */}\n <SheetPrimitive.Close asChild>\n <Button data-dismiss iconOnly dismiss size=\"sm\" startIcon={XIcon} aria-label=\"關閉\" />\n </SheetPrimitive.Close>\n </SurfaceHeader>\n))\nSheetHeader.displayName = \"SheetHeader\"\n\n// ── SheetBody:flex-1 ScrollArea + chrome padding(對齊 DialogBody + ScrollArea canonical) ──\n// 捲軸必用 ScrollArea(跨 OS 一致、不吃寬度)— 不自寫 overflow-y-auto。\n// padding 搬進 viewport inner div:px-loose / pt-tight / pb-bottom。\n// data-sheet-body:讓 SheetContent onOpenAutoFocus 找得到 body 第一個互動元素\n//\n// ── List-as-region 場景(menu / nav / settings list)──\n// 不再提供 `flush` variant(2026-05-01 移除)。canonical = consumer 用 className override:\n// `<SheetBody className=\"!px-0 !pt-0 !pb-0\"><div className=\"py-2\">{items}</div></SheetBody>` ← @tabs-content-gap-ok: JSDoc 文件範例(list-as-region canonical,非 tabs !pt-0 hack;對齊 dialog.tsx 同款 marker)\n// 詳 DialogBody comment + `tokens/layoutSpace/layoutSpace.spec.md`「List-as-region in overlay body」\n// `className` forward 到 **inner content div**(非外層 ScrollArea wrapper)——\n// consumer `<SheetBody className=\"flex flex-col gap-X\">` 期望作用於 children 排列;\n// 套在 ScrollArea 上會 0 效果(children 住 inner div),曾造成 Sheet form field 完全貼邊。\nconst SheetBody = React.forwardRef<\n HTMLDivElement,\n React.ComponentPropsWithoutRef<typeof ScrollArea>\n>(({ className, children, ...props }, ref) => (\n <ScrollArea ref={ref} data-sheet-body className=\"flex-1 min-h-0\" {...props}>\n <div\n className={cn(\n \"px-[var(--layout-space-loose)] pt-[var(--layout-space-tight)] pb-[var(--layout-space-bottom)]\",\n className,\n )}\n >\n {children}\n </div>\n </ScrollArea>\n))\nSheetBody.displayName = \"SheetBody\"\n\n// ── SheetFooter:SurfaceFooter wrap 加 data-sheet-footer(autoFocus fallback target)──\nconst SheetFooter = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ ...props }, ref) => <SurfaceFooter ref={ref} data-sheet-footer {...props} />)\nSheetFooter.displayName = \"SheetFooter\"\n\nconst SheetTitle = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Title\n ref={ref}\n className={cn(\"text-body-lg font-medium truncate\", className)}\n {...props}\n />\n))\nSheetTitle.displayName = SheetPrimitive.Title.displayName\n\nconst SheetDescription = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Description\n ref={ref}\n // title → description 間距 canonical:SheetTitle body-lg(16)+ desc body(14)→ reading-lg token\n // (label tier 決定;對齊 Dialog canonical。Tailwind preflight reset h2/p margin=0 → 必顯式 mt)\n className={cn(\"mt-[var(--item-gap-label-desc-reading-lg)] text-body text-fg-secondary\", className)}\n {...props}\n />\n))\nSheetDescription.displayName = SheetPrimitive.Description.displayName\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const sheetMeta = {\n component: 'Sheet',\n family: null, // non-family composite / overlay / layout\n variants: {\n\n },\n sizes: {\n\n },\n // 2026-07-04 audit 對齊:Sheet 本身無 disabled state(surface container,spec 邊界案例明文;對齊 popover/dialog meta pattern)\n states: ['default'],\n tokens: {\n bg: ['bg-surface-raised'],\n fg: ['text-fg-secondary', 'text-foreground'],\n ring: [],\n },\n} as const\n\nexport {\n Sheet,\n SheetPortal,\n SheetOverlay,\n SheetTrigger,\n SheetClose,\n SheetContent,\n SheetHeader,\n SheetBody,\n SheetFooter,\n SheetTitle,\n SheetDescription,\n sheetVariants,\n}\n"],"names":["SheetPrimitive","XIcon"],"mappings":"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"}
|
|
@@ -165,7 +165,7 @@ declare const SidebarMenuSkeleton: React.ForwardRefExoticComponent<Omit<React.Cl
|
|
|
165
165
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
166
166
|
export declare const sidebarMeta: {
|
|
167
167
|
readonly component: "Sidebar";
|
|
168
|
-
readonly family:
|
|
168
|
+
readonly family: 1;
|
|
169
169
|
readonly variants: {
|
|
170
170
|
readonly default: {
|
|
171
171
|
readonly when: "標準導覽 row,參與 single-selection";
|
|
@@ -799,8 +799,8 @@ const SidebarMenuSkeleton = React.forwardRef(({ className, showIcon = false, siz
|
|
|
799
799
|
SidebarMenuSkeleton.displayName = "SidebarMenuSkeleton";
|
|
800
800
|
const sidebarMeta = {
|
|
801
801
|
component: "Sidebar",
|
|
802
|
-
family:
|
|
803
|
-
//
|
|
802
|
+
family: 1,
|
|
803
|
+
// Family 1(Menu item layout)消費者 — 對齊 sidebar.spec.md frontmatter family: 1(SidebarMenuButton rows 消費 item-anatomy;同 tree-view.tsx 慣例)
|
|
804
804
|
variants: {
|
|
805
805
|
default: { when: "標準導覽 row,參與 single-selection" },
|
|
806
806
|
meta: { when: "Section 底部命令 row(Show more / 新增),不參與 selection" }
|