@qijenchen/design-system 0.1.0-beta.82 → 0.1.0-beta.84
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/CLAUDE.md +8 -8
- package/README.md +3 -2
- package/dist/components/Alert/alert.js.map +1 -1
- package/dist/components/AppShell/_demo-helpers.d.ts.map +1 -1
- package/dist/components/AppShell/app-shell.d.ts +1 -1
- package/dist/components/AppShell/app-shell.d.ts.map +1 -1
- package/dist/components/AppShell/app-shell.js +22 -2
- package/dist/components/AppShell/app-shell.js.map +1 -1
- package/dist/components/Avatar/avatar.d.ts.map +1 -1
- package/dist/components/Avatar/avatar.js +4 -4
- package/dist/components/Avatar/avatar.js.map +1 -1
- package/dist/components/Breadcrumb/breadcrumb.d.ts.map +1 -1
- package/dist/components/Breadcrumb/breadcrumb.js +1 -1
- package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
- package/dist/components/Button/button.d.ts +2 -2
- package/dist/components/Button/button.d.ts.map +1 -1
- package/dist/components/Button/button.js +12 -3
- package/dist/components/Button/button.js.map +1 -1
- package/dist/components/Calendar/calendar.d.ts +13 -5
- package/dist/components/Calendar/calendar.d.ts.map +1 -1
- package/dist/components/Calendar/calendar.js +7 -1
- package/dist/components/Calendar/calendar.js.map +1 -1
- package/dist/components/Carousel/carousel.d.ts.map +1 -1
- package/dist/components/Carousel/carousel.js +8 -3
- package/dist/components/Carousel/carousel.js.map +1 -1
- package/dist/components/Chart/chart.d.ts.map +1 -1
- package/dist/components/Chart/chart.js +1 -0
- package/dist/components/Chart/chart.js.map +1 -1
- package/dist/components/Checkbox/checkbox.d.ts +1 -1
- package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox/checkbox.js +8 -4
- package/dist/components/Checkbox/checkbox.js.map +1 -1
- package/dist/components/Chip/chip.d.ts +1 -1
- package/dist/components/Chip/chip.d.ts.map +1 -1
- package/dist/components/Chip/chip.js.map +1 -1
- package/dist/components/Coachmark/coachmark.d.ts +10 -3
- package/dist/components/Coachmark/coachmark.d.ts.map +1 -1
- package/dist/components/Coachmark/coachmark.js.map +1 -1
- package/dist/components/Combobox/combobox.d.ts +5 -1
- package/dist/components/Combobox/combobox.d.ts.map +1 -1
- package/dist/components/Combobox/combobox.js +30 -13
- package/dist/components/Combobox/combobox.js.map +1 -1
- package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
- package/dist/components/DataTable/cell-registry.js +53 -62
- package/dist/components/DataTable/cell-registry.js.map +1 -1
- package/dist/components/DataTable/column-types.d.ts +10 -4
- 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 +1 -1
- package/dist/components/DataTable/data-table-column-visibility-panel.js +183 -0
- package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -0
- package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-panel.js +607 -0
- package/dist/components/DataTable/data-table-filter-panel.js.map +1 -0
- package/dist/components/DataTable/data-table-interaction-layer.d.ts +11 -15
- package/dist/components/DataTable/data-table-interaction-layer.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-interaction-layer.js +1 -3
- package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
- package/dist/components/DataTable/data-table-sort-manager.js +140 -0
- package/dist/components/DataTable/data-table-sort-manager.js.map +1 -0
- package/dist/components/DataTable/data-table.d.ts +5 -6
- package/dist/components/DataTable/data-table.d.ts.map +1 -1
- package/dist/components/DataTable/data-table.js +36 -15
- package/dist/components/DataTable/data-table.js.map +1 -1
- package/dist/components/DataTable/filter-operators.js +140 -0
- package/dist/components/DataTable/filter-operators.js.map +1 -0
- package/dist/components/DataTable/filter-tree.js +312 -0
- package/dist/components/DataTable/filter-tree.js.map +1 -0
- package/dist/components/DataTable/index.d.ts +3 -0
- package/dist/components/DataTable/index.d.ts.map +1 -1
- package/dist/components/DataTable/index.js +12 -1
- package/dist/components/DataTable/index.js.map +1 -1
- package/dist/components/DataTable/lib/column-meta.js +25 -0
- package/dist/components/DataTable/lib/column-meta.js.map +1 -0
- package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
- package/dist/components/DateGrid/date-grid.js +3 -1
- package/dist/components/DateGrid/date-grid.js.map +1 -1
- package/dist/components/DatePicker/date-picker.d.ts +7 -1
- package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
- package/dist/components/DatePicker/date-picker.js +105 -74
- package/dist/components/DatePicker/date-picker.js.map +1 -1
- package/dist/components/Dialog/dialog.d.ts +20 -3
- package/dist/components/Dialog/dialog.d.ts.map +1 -1
- package/dist/components/Dialog/dialog.js +38 -20
- package/dist/components/Dialog/dialog.js.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.js +3 -1
- package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
- package/dist/components/Field/field-context.d.ts +45 -5
- package/dist/components/Field/field-context.d.ts.map +1 -1
- package/dist/components/Field/field-context.js +19 -0
- package/dist/components/Field/field-context.js.map +1 -1
- package/dist/components/Field/field-edit-keys.d.ts +21 -0
- package/dist/components/Field/field-edit-keys.d.ts.map +1 -0
- package/dist/components/Field/field-edit-keys.js +19 -0
- package/dist/components/Field/field-edit-keys.js.map +1 -0
- package/dist/components/Field/field-types.d.ts +20 -1
- 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 +3 -2
- package/dist/components/Field/field-wrapper.d.ts.map +1 -1
- package/dist/components/Field/field-wrapper.js +38 -39
- package/dist/components/Field/field-wrapper.js.map +1 -1
- package/dist/components/Field/field.d.ts +2 -1
- package/dist/components/Field/field.d.ts.map +1 -1
- package/dist/components/Field/field.js.map +1 -1
- package/dist/components/FieldControlGroup/field-control-group.d.ts +1 -1
- package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
- package/dist/components/FileItem/file-item.d.ts.map +1 -1
- package/dist/components/FileItem/file-item.js +1 -1
- package/dist/components/FileItem/file-item.js.map +1 -1
- package/dist/components/FileUpload/file-upload.d.ts +1 -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 +7 -2
- package/dist/components/FileViewer/file-viewer.js.map +1 -1
- package/dist/components/FileViewer/image-renderer.d.ts.map +1 -1
- package/dist/components/FileViewer/image-renderer.js +22 -1
- package/dist/components/FileViewer/image-renderer.js.map +1 -1
- package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
- package/dist/components/HoverCard/hover-card.js +5 -3
- package/dist/components/HoverCard/hover-card.js.map +1 -1
- package/dist/components/InlineEdit/index.d.ts +2 -0
- package/dist/components/InlineEdit/index.d.ts.map +1 -0
- package/dist/components/InlineEdit/index.js +5 -0
- package/dist/components/InlineEdit/index.js.map +1 -0
- package/dist/components/InlineEdit/inline-edit.d.ts +59 -0
- package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -0
- package/dist/components/InlineEdit/inline-edit.js +164 -0
- package/dist/components/InlineEdit/inline-edit.js.map +1 -0
- package/dist/components/Input/input.d.ts +6 -11
- package/dist/components/Input/input.d.ts.map +1 -1
- package/dist/components/Input/input.js +43 -13
- package/dist/components/Input/input.js.map +1 -1
- package/dist/components/LinkInput/link-input.d.ts +2 -2
- package/dist/components/LinkInput/link-input.d.ts.map +1 -1
- package/dist/components/LinkInput/link-input.js +6 -5
- package/dist/components/LinkInput/link-input.js.map +1 -1
- package/dist/components/Menu/menu-item.d.ts +14 -2
- package/dist/components/Menu/menu-item.d.ts.map +1 -1
- package/dist/components/Menu/menu-item.js +25 -3
- package/dist/components/Menu/menu-item.js.map +1 -1
- package/dist/components/NumberInput/number-input.d.ts +3 -3
- package/dist/components/NumberInput/number-input.d.ts.map +1 -1
- package/dist/components/NumberInput/number-input.js +6 -5
- package/dist/components/NumberInput/number-input.js.map +1 -1
- package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
- package/dist/components/OverflowIndicator/overflow-indicator.js +2 -6
- package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
- package/dist/components/Pagination/pagination.js +1 -1
- package/dist/components/Pagination/pagination.js.map +1 -1
- package/dist/components/PeoplePicker/index.js +7 -1
- package/dist/components/PeoplePicker/index.js.map +1 -1
- package/dist/components/PeoplePicker/people-picker.d.ts +9 -2
- package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
- package/dist/components/PeoplePicker/people-picker.js +23 -10
- package/dist/components/PeoplePicker/people-picker.js.map +1 -1
- package/dist/components/PeoplePicker/person-display.d.ts +6 -3
- package/dist/components/PeoplePicker/person-display.d.ts.map +1 -1
- package/dist/components/PeoplePicker/person-display.js +19 -14
- package/dist/components/PeoplePicker/person-display.js.map +1 -1
- package/dist/components/Popover/popover.d.ts.map +1 -1
- package/dist/components/Popover/popover.js +3 -1
- package/dist/components/Popover/popover.js.map +1 -1
- package/dist/components/ProfileCard/profile-card.d.ts +2 -2
- package/dist/components/ProfileCard/profile-card.js +3 -3
- package/dist/components/ProfileCard/profile-card.js.map +1 -1
- package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
- package/dist/components/RadioGroup/radio-group.js +15 -9
- package/dist/components/RadioGroup/radio-group.js.map +1 -1
- package/dist/components/ScrollArea/scroll-area.d.ts +11 -1
- package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
- package/dist/components/ScrollArea/scroll-area.js +6 -3
- package/dist/components/ScrollArea/scroll-area.js.map +1 -1
- package/dist/components/Select/select.d.ts +46 -5
- package/dist/components/Select/select.d.ts.map +1 -1
- package/dist/components/Select/select.js +109 -28
- package/dist/components/Select/select.js.map +1 -1
- package/dist/components/SelectMenu/select-menu.d.ts +3 -1
- package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
- package/dist/components/SelectMenu/select-menu.js +8 -1
- package/dist/components/SelectMenu/select-menu.js.map +1 -1
- package/dist/components/SelectionControl/boolean-value.d.ts +11 -0
- package/dist/components/SelectionControl/boolean-value.d.ts.map +1 -0
- package/dist/components/SelectionControl/boolean-value.js +23 -0
- package/dist/components/SelectionControl/boolean-value.js.map +1 -0
- package/dist/components/SelectionControl/index.d.ts +1 -0
- package/dist/components/SelectionControl/index.d.ts.map +1 -1
- package/dist/components/SelectionControl/index.js +2 -0
- package/dist/components/SelectionControl/index.js.map +1 -1
- package/dist/components/Sheet/index.js +2 -1
- package/dist/components/Sheet/sheet.d.ts +7 -1
- package/dist/components/Sheet/sheet.d.ts.map +1 -1
- package/dist/components/Sheet/sheet.js +5 -2
- package/dist/components/Sheet/sheet.js.map +1 -1
- package/dist/components/Sidebar/sidebar.d.ts +9 -4
- package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
- package/dist/components/Sidebar/sidebar.js +8 -10
- 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.map +1 -1
- package/dist/components/Switch/switch.d.ts +6 -6
- package/dist/components/Switch/switch.d.ts.map +1 -1
- package/dist/components/Switch/switch.js +3 -2
- package/dist/components/Switch/switch.js.map +1 -1
- package/dist/components/Tabs/tabs.d.ts.map +1 -1
- package/dist/components/Tabs/tabs.js +15 -1
- package/dist/components/Tabs/tabs.js.map +1 -1
- package/dist/components/Textarea/textarea.d.ts +2 -2
- package/dist/components/Textarea/textarea.d.ts.map +1 -1
- package/dist/components/Textarea/textarea.js +50 -42
- package/dist/components/Textarea/textarea.js.map +1 -1
- package/dist/components/TimePicker/time-columns.d.ts.map +1 -1
- package/dist/components/TimePicker/time-columns.js +42 -38
- package/dist/components/TimePicker/time-columns.js.map +1 -1
- package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
- package/dist/components/TimePicker/time-picker.js +8 -6
- 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 +7 -5
- package/dist/components/Tooltip/tooltip.js.map +1 -1
- package/dist/components/TreeView/tree-view.d.ts +56 -1
- package/dist/components/TreeView/tree-view.d.ts.map +1 -1
- package/dist/components/TreeView/tree-view.js +211 -12
- package/dist/components/TreeView/tree-view.js.map +1 -1
- package/dist/index.d.ts +115 -58
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +93 -108
- package/dist/index.js.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.d.ts +7 -6
- package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
- package/dist/tokens/categorical-color.js +15 -0
- package/dist/tokens/categorical-color.js.map +1 -1
- package/dist/tokens/motion/motion.d.ts +7 -6
- package/dist/tokens/motion/motion.d.ts.map +1 -1
- package/dist/tokens/motion/motion.js +6 -6
- package/dist/tokens/motion/motion.js.map +1 -1
- package/dist/tokens/motion/overlay-motion.d.ts +19 -0
- package/dist/tokens/motion/overlay-motion.d.ts.map +1 -0
- package/dist/tokens/motion/overlay-motion.js +9 -0
- package/dist/tokens/motion/overlay-motion.js.map +1 -0
- package/dist/tokens/uiSize/icon-size.js +5 -0
- package/dist/tokens/uiSize/icon-size.js.map +1 -0
- package/ds-canonical/fork/governance.lock +37 -19
- package/ds-canonical/fork/hooks/_log-fire.sh +46 -0
- package/ds-canonical/fork/hooks/block_prototype_imports.py +15 -4
- package/ds-canonical/fork/hooks/check_consumer_app_invariants.sh +145 -13
- package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +17 -5
- package/ds-canonical/fork/hooks/check_dynamic_tailwind_class.sh +47 -0
- package/ds-canonical/fork/hooks/check_escape_marker_abuse.sh +27 -0
- package/ds-canonical/fork/hooks/check_field_family_invariants.sh +61 -1
- package/ds-canonical/fork/hooks/check_layout_space_magic_numbers.sh +28 -4
- package/ds-canonical/fork/hooks/check_opacity_token_usage.sh +36 -0
- package/ds-canonical/fork/hooks/check_story_determinism.sh +39 -0
- package/ds-canonical/fork/hooks/check_story_invariants.sh +119 -0
- package/ds-canonical/fork/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
- package/ds-canonical/fork/manifest.json +20 -0
- package/ds-canonical/fork/preamble.md +22 -18
- package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +9 -3
- package/ds-canonical/fork/skills/code-quality-audit/SKILL.md +3 -3
- package/ds-canonical/fork/skills/delivery-handoff/SKILL.md +7 -0
- package/ds-canonical/fork/skills/performance-audit/SKILL.md +4 -2
- package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +3 -3
- package/ds-canonical/fork/skills/propose-options/SKILL.md +3 -3
- package/ds-canonical/fork/skills/prototype/SKILL.md +1 -1
- package/ds-canonical/fork/skills/scan-similar-bugs/SKILL.md +2 -2
- package/ds-canonical/fork/skills/visual-audit/SKILL.md +10 -2
- package/ds-canonical/hooks/README.md +1 -1
- package/ds-canonical/hooks/block_prototype_imports.py +15 -4
- package/ds-canonical/hooks/check_audit_post_report_validator.sh +65 -7
- package/ds-canonical/hooks/check_benchmark_citation.sh +5 -3
- package/ds-canonical/hooks/check_codex_brief_invariants.sh +28 -1
- package/ds-canonical/hooks/check_consumer_app_invariants.sh +145 -13
- package/ds-canonical/hooks/check_ds_anchor_preflight.sh +17 -5
- package/ds-canonical/hooks/check_dynamic_tailwind_class.sh +47 -0
- package/ds-canonical/hooks/check_escape_marker_abuse.sh +27 -0
- package/ds-canonical/hooks/check_field_family_invariants.sh +61 -1
- package/ds-canonical/hooks/check_layout_space_magic_numbers.sh +28 -4
- package/ds-canonical/hooks/check_naming_and_abstraction.sh +1 -1
- package/ds-canonical/hooks/check_opacity_token_usage.sh +36 -0
- package/ds-canonical/hooks/check_pixel_quantified_audit.sh +3 -2
- package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +6 -1
- package/ds-canonical/hooks/check_propose_without_benchmark.sh +6 -2
- package/ds-canonical/hooks/check_ssot_header_declaration.sh +55 -0
- package/ds-canonical/hooks/check_story_determinism.sh +39 -0
- package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +8 -7
- package/ds-canonical/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
- package/ds-canonical/hooks/lib/_story_compile_drift.sh +1 -1
- package/ds-canonical/hooks/lib/_token_hygiene.sh +10 -1
- package/ds-canonical/hooks/stop_self_audit.sh +31 -0
- package/ds-canonical/hooks/tests/test_block_prototype_imports.sh +24 -0
- package/ds-canonical/hooks/tests/test_check_audit_post_report_validator.sh +44 -0
- package/ds-canonical/hooks/tests/test_check_benchmark_citation.sh +3 -3
- package/ds-canonical/hooks/tests/test_check_codex_brief_invariants.sh +43 -2
- package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +112 -0
- package/ds-canonical/hooks/tests/test_check_consumer_story_baseline.sh +9 -2
- package/ds-canonical/hooks/tests/test_check_dynamic_tailwind_class.sh +76 -0
- package/ds-canonical/hooks/tests/test_check_escape_marker_abuse.sh +24 -0
- package/ds-canonical/hooks/tests/test_check_field_family_invariants.sh +27 -2
- package/ds-canonical/hooks/tests/test_check_fork_user_plugin_install.sh +3 -1
- package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +41 -2
- package/ds-canonical/hooks/tests/test_check_ssot_header_declaration.sh +72 -0
- package/ds-canonical/hooks/tests/test_check_tabs_content_chrome_body_double_gap.sh +76 -0
- package/ds-canonical/references/audit-coverage-vs-24-checklist.md +1 -1
- package/ds-canonical/references/build-ui-canonicals.md +2 -1
- package/ds-canonical/references/failure-class-registry.json +145 -0
- package/ds-canonical/references/governance-audit-coverage.md +33 -0
- package/ds-canonical/references/item-anatomy-recipe.md +2 -2
- package/ds-canonical/references/scenario-definition.md +6 -9
- package/ds-canonical/references/ssot-consultation.md +2 -2
- package/ds-canonical/references/ssot-index.md +5 -4
- package/ds-canonical/references/story-baseline-registry.json +8 -2
- package/ds-canonical/references/structural-token-retention.md +6 -9
- package/ds-canonical/rules/meta-patterns.md +8 -8
- package/ds-canonical/rules/self-verify.md +4 -3
- package/ds-canonical/rules/spec-rules.md +1 -1
- package/ds-canonical/rules/story-rules.md +2 -2
- package/ds-canonical/rules/ui-development.md +4 -2
- package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +9 -3
- package/ds-canonical/skills/code-quality-audit/SKILL.md +3 -3
- package/ds-canonical/skills/codex-collab/SKILL.md +9 -7
- package/ds-canonical/skills/codex-collab/references/brief-template.md +1 -1
- package/ds-canonical/skills/codify-principle/SKILL.md +2 -2
- package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
- package/ds-canonical/skills/component-quality-gate/references/checklist.md +2 -2
- package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +57 -68
- package/ds-canonical/skills/deep-audit-cross-codex/references/phase-a-workflow.md +29 -0
- package/ds-canonical/skills/deep-audit-cross-codex/references/phase-b-codex-brief.md +29 -10
- package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +21 -0
- package/ds-canonical/skills/delivery-handoff/SKILL.md +5 -0
- package/ds-canonical/skills/design-system-audit/SKILL.md +27 -26
- package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +13 -1
- package/ds-canonical/skills/design-system-audit/references/rule-placement.md +1 -1
- package/ds-canonical/skills/governance-health/SKILL.md +2 -1
- package/ds-canonical/skills/knowledge-prune/SKILL.md +3 -0
- package/ds-canonical/skills/knowledge-prune/references/prune-dimensions-d5-d10.md +1 -0
- package/ds-canonical/skills/new-component/SKILL.md +7 -5
- package/ds-canonical/skills/new-component/references/new-component-checklist.md +1 -1
- package/ds-canonical/skills/performance-audit/SKILL.md +2 -2
- package/ds-canonical/skills/product-ui-audit/SKILL.md +3 -3
- package/ds-canonical/skills/propose-options/SKILL.md +3 -3
- package/ds-canonical/skills/prototype/SKILL.md +1 -1
- package/ds-canonical/skills/scan-similar-bugs/SKILL.md +2 -2
- package/ds-canonical/skills/story-auto-compile-migrate/SKILL.md +3 -3
- package/ds-canonical/skills/story-writing/SKILL.md +3 -3
- package/ds-canonical/skills/visual-audit/SKILL.md +10 -2
- package/ds-story-manifest.json +29 -20
- package/llms-full.txt +22 -1
- package/llms.txt +5 -4
- package/package.json +7 -3
- package/src/components/Accordion/accordion.principles.stories.tsx +1 -1
- package/src/components/Accordion/accordion.stories.tsx +16 -1
- package/src/components/Alert/alert.principles.stories.tsx +1 -1
- package/src/components/Alert/alert.spec.md +8 -10
- package/src/components/Alert/alert.stories.tsx +28 -28
- package/src/components/Alert/alert.tsx +1 -1
- package/src/components/AppShell/_demo-helpers.tsx +6 -3
- package/src/components/AppShell/app-shell.spec.md +4 -4
- package/src/components/AppShell/app-shell.tsx +28 -2
- package/src/components/AspectRatio/aspect-ratio.spec.md +1 -1
- package/src/components/AspectRatio/aspect-ratio.stories.tsx +7 -89
- package/src/components/Avatar/avatar.anatomy.stories.tsx +7 -3
- package/src/components/Avatar/avatar.principles.stories.tsx +19 -15
- package/src/components/Avatar/avatar.spec.md +2 -2
- package/src/components/Avatar/avatar.stories.tsx +8 -6
- package/src/components/Avatar/avatar.tsx +13 -5
- package/src/components/Badge/badge.principles.stories.tsx +1 -1
- package/src/components/Badge/badge.spec.md +2 -0
- package/src/components/Badge/badge.stories.tsx +4 -2
- package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +6 -4
- package/src/components/Breadcrumb/breadcrumb.stories.tsx +6 -5
- package/src/components/Breadcrumb/breadcrumb.tsx +3 -2
- package/src/components/BulkActionBar/bulk-action-bar.spec.md +4 -2
- package/src/components/Button/button.principles.stories.tsx +1 -1
- package/src/components/Button/button.spec.md +2 -2
- package/src/components/Button/button.tsx +18 -3
- package/src/components/Calendar/calendar.anatomy.stories.tsx +12 -10
- package/src/components/Calendar/calendar.principles.stories.tsx +14 -6
- package/src/components/Calendar/calendar.spec.md +5 -4
- package/src/components/Calendar/calendar.stories.tsx +7 -4
- package/src/components/Calendar/calendar.tsx +26 -7
- package/src/components/Carousel/carousel.anatomy.stories.tsx +5 -4
- package/src/components/Carousel/carousel.spec.md +2 -2
- package/src/components/Carousel/carousel.tsx +11 -3
- package/src/components/Chart/chart.anatomy.stories.tsx +7 -4
- package/src/components/Chart/chart.principles.stories.tsx +1 -1
- package/src/components/Chart/chart.stories.tsx +2 -8
- package/src/components/Chart/chart.tsx +3 -0
- package/src/components/Checkbox/checkbox.anatomy.stories.tsx +28 -49
- package/src/components/Checkbox/checkbox.principles.stories.tsx +1 -1
- package/src/components/Checkbox/checkbox.spec.md +11 -26
- package/src/components/Checkbox/checkbox.stories.tsx +11 -8
- package/src/components/Checkbox/checkbox.tsx +18 -13
- package/src/components/Chip/chip.anatomy.stories.tsx +44 -24
- package/src/components/Chip/chip.principles.stories.tsx +1 -1
- package/src/components/Chip/chip.spec.md +5 -4
- package/src/components/Chip/chip.tsx +5 -1
- package/src/components/CircularProgress/circular-progress.principles.stories.tsx +7 -4
- package/src/components/CircularProgress/circular-progress.spec.md +3 -3
- package/src/components/CircularProgress/circular-progress.stories.tsx +2 -2
- package/src/components/Coachmark/coachmark.anatomy.stories.tsx +2 -2
- package/src/components/Coachmark/coachmark.principles.stories.tsx +2 -1
- package/src/components/Coachmark/coachmark.spec.md +5 -5
- package/src/components/Coachmark/coachmark.stories.tsx +3 -1
- package/src/components/Coachmark/coachmark.tsx +10 -3
- package/src/components/Combobox/combobox.anatomy.stories.tsx +2 -2
- package/src/components/Combobox/combobox.tsx +53 -19
- package/src/components/Command/command.anatomy.stories.tsx +2 -2
- package/src/components/Command/command.principles.stories.tsx +1 -1
- package/src/components/Command/command.stories.tsx +42 -10
- package/src/components/DataTable/cell-registry.tsx +86 -63
- package/src/components/DataTable/column-types.ts +10 -1
- package/src/components/DataTable/data-table-column-visibility-panel.tsx +1 -1
- package/src/components/DataTable/data-table-filter-panel.tsx +4 -1
- package/src/components/DataTable/data-table-interaction-layer.tsx +15 -19
- package/src/components/DataTable/data-table.anatomy.stories.tsx +15 -13
- package/src/components/DataTable/data-table.principles.stories.tsx +9 -9
- package/src/components/DataTable/data-table.spec.md +32 -12
- package/src/components/DataTable/data-table.stories.tsx +16 -17
- package/src/components/DataTable/data-table.tsx +103 -60
- package/src/components/DataTable/index.ts +3 -0
- package/src/components/DateGrid/date-grid.anatomy.stories.tsx +4 -4
- package/src/components/DateGrid/date-grid.principles.stories.tsx +1 -1
- package/src/components/DateGrid/date-grid.spec.md +1 -1
- package/src/components/DateGrid/date-grid.tsx +3 -1
- package/src/components/DatePicker/date-picker.anatomy.stories.tsx +8 -8
- package/src/components/DatePicker/date-picker.principles.stories.tsx +3 -3
- package/src/components/DatePicker/date-picker.spec.md +3 -3
- package/src/components/DatePicker/date-picker.stories.tsx +4 -5
- package/src/components/DatePicker/date-picker.tsx +99 -45
- package/src/components/DescriptionList/description-list.anatomy.stories.tsx +12 -10
- package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
- package/src/components/DescriptionList/description-list.spec.md +3 -3
- package/src/components/DescriptionList/description-list.stories.tsx +7 -6
- package/src/components/Dialog/dialog.anatomy.stories.tsx +1 -0
- package/src/components/Dialog/dialog.spec.md +9 -0
- package/src/components/Dialog/dialog.stories.tsx +66 -5
- package/src/components/Dialog/dialog.tsx +60 -25
- package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +1 -1
- package/src/components/DropdownMenu/dropdown-menu.principles.stories.tsx +8 -6
- package/src/components/DropdownMenu/dropdown-menu.spec.md +5 -5
- package/src/components/DropdownMenu/dropdown-menu.tsx +18 -9
- package/src/components/Empty/empty.anatomy.stories.tsx +1 -1
- package/src/components/Empty/empty.spec.md +2 -2
- package/src/components/Field/field-context.ts +69 -5
- package/src/components/Field/field-controls.spec.md +62 -8
- package/src/components/Field/field-edit-keys.ts +66 -0
- package/src/components/Field/field-types.ts +58 -9
- package/src/components/Field/field-wrapper.tsx +50 -42
- package/src/components/Field/field.anatomy.stories.tsx +9 -6
- package/src/components/Field/field.spec.md +1 -1
- package/src/components/Field/field.stories.tsx +1 -1
- package/src/components/Field/field.tsx +2 -1
- package/src/components/Field/form-validation.spec.md +3 -3
- package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +6 -6
- package/src/components/FieldControlGroup/field-control-group.spec.md +9 -4
- package/src/components/FieldControlGroup/field-control-group.tsx +1 -1
- package/src/components/FileItem/file-item.anatomy.stories.tsx +1 -1
- package/src/components/FileItem/file-item.spec.md +14 -8
- package/src/components/FileItem/file-item.stories.tsx +36 -25
- package/src/components/FileItem/file-item.tsx +8 -6
- package/src/components/FileUpload/file-upload.anatomy.stories.tsx +6 -6
- package/src/components/FileUpload/file-upload.principles.stories.tsx +5 -5
- package/src/components/FileUpload/file-upload.spec.md +1 -1
- package/src/components/FileUpload/file-upload.tsx +1 -1
- package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +4 -3
- package/src/components/FileViewer/file-viewer.principles.stories.tsx +3 -3
- package/src/components/FileViewer/file-viewer.spec.md +30 -12
- package/src/components/FileViewer/file-viewer.stories.tsx +1 -2
- package/src/components/FileViewer/file-viewer.tsx +12 -4
- package/src/components/FileViewer/image-renderer.tsx +35 -7
- package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -2
- package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -3
- package/src/components/HoverCard/hover-card.spec.md +5 -5
- package/src/components/HoverCard/hover-card.stories.tsx +16 -16
- package/src/components/HoverCard/hover-card.tsx +5 -3
- package/src/components/InlineEdit/index.ts +5 -0
- package/src/components/InlineEdit/inline-edit.spec.md +149 -0
- package/src/components/InlineEdit/inline-edit.stories.tsx +246 -0
- package/src/components/InlineEdit/inline-edit.tsx +287 -0
- package/src/components/Input/input.anatomy.stories.tsx +8 -7
- package/src/components/Input/input.principles.stories.tsx +2 -12
- package/src/components/Input/input.spec.md +13 -18
- package/src/components/Input/input.tsx +69 -22
- package/src/components/LinkInput/link-input.anatomy.stories.tsx +1 -1
- package/src/components/LinkInput/link-input.principles.stories.tsx +2 -2
- package/src/components/LinkInput/link-input.tsx +11 -10
- package/src/components/Menu/menu-item.anatomy.stories.tsx +3 -3
- package/src/components/Menu/menu-item.principles.stories.tsx +1 -1
- package/src/components/Menu/menu-item.spec.md +22 -15
- package/src/components/Menu/menu-item.stories.tsx +39 -11
- package/src/components/Menu/menu-item.tsx +38 -4
- package/src/components/Notice/notice.principles.stories.tsx +1 -1
- package/src/components/Notice/notice.spec.md +16 -3
- package/src/components/NumberInput/number-input.anatomy.stories.tsx +3 -3
- package/src/components/NumberInput/number-input.principles.stories.tsx +1 -1
- package/src/components/NumberInput/number-input.spec.md +4 -4
- package/src/components/NumberInput/number-input.tsx +19 -12
- package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +10 -8
- package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +3 -3
- package/src/components/OverflowIndicator/overflow-indicator.spec.md +4 -2
- package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +71 -63
- package/src/components/OverflowIndicator/overflow-indicator.tsx +9 -8
- package/src/components/Pagination/pagination.spec.md +1 -1
- package/src/components/Pagination/pagination.stories.tsx +2 -6
- package/src/components/Pagination/pagination.tsx +1 -1
- package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +4 -4
- package/src/components/PeoplePicker/people-picker.principles.stories.tsx +2 -2
- package/src/components/PeoplePicker/people-picker.spec.md +6 -6
- package/src/components/PeoplePicker/people-picker.tsx +56 -16
- package/src/components/PeoplePicker/person-display.tsx +32 -16
- package/src/components/Popover/popover.anatomy.stories.tsx +2 -2
- package/src/components/Popover/popover.tsx +3 -1
- package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +12 -23
- package/src/components/ProfileCard/profile-card.spec.md +3 -3
- package/src/components/ProfileCard/profile-card.tsx +6 -6
- package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +3 -1
- package/src/components/ProgressBar/progress-bar.principles.stories.tsx +1 -1
- package/src/components/ProgressBar/progress-bar.stories.tsx +50 -42
- package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +10 -28
- package/src/components/RadioGroup/radio-group.stories.tsx +29 -32
- package/src/components/RadioGroup/radio-group.tsx +41 -15
- package/src/components/Rating/rating.spec.md +11 -3
- package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.spec.md +7 -3
- package/src/components/ScrollArea/scroll-area.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.tsx +17 -4
- package/src/components/Select/select.anatomy.stories.tsx +3 -3
- package/src/components/Select/select.spec.md +27 -17
- package/src/components/Select/select.stories.tsx +33 -0
- package/src/components/Select/select.tsx +190 -36
- package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +4 -4
- package/src/components/SelectMenu/select-menu.spec.md +15 -7
- package/src/components/SelectMenu/select-menu.tsx +16 -3
- package/src/components/SelectionControl/boolean-value.tsx +39 -0
- package/src/components/SelectionControl/index.ts +1 -0
- package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +2 -2
- package/src/components/SelectionControl/selection-item.principles.stories.tsx +2 -2
- package/src/components/SelectionControl/selection-item.spec.md +3 -3
- package/src/components/SelectionControl/selection-item.stories.tsx +3 -3
- package/src/components/Separator/separator.spec.md +2 -2
- package/src/components/Separator/separator.stories.tsx +6 -27
- package/src/components/Sheet/sheet.anatomy.stories.tsx +1 -1
- package/src/components/Sheet/sheet.principles.stories.tsx +1 -1
- package/src/components/Sheet/sheet.spec.md +2 -2
- package/src/components/Sheet/sheet.tsx +9 -5
- package/src/components/Sidebar/sidebar.anatomy.stories.tsx +16 -12
- package/src/components/Sidebar/sidebar.principles.stories.tsx +10 -8
- package/src/components/Sidebar/sidebar.spec.md +13 -15
- package/src/components/Sidebar/sidebar.stories.tsx +8 -7
- package/src/components/Sidebar/sidebar.tsx +37 -25
- package/src/components/Skeleton/skeleton.stories.tsx +4 -11
- package/src/components/Slider/slider.anatomy.stories.tsx +3 -3
- package/src/components/Slider/slider.principles.stories.tsx +2 -2
- package/src/components/Slider/slider.spec.md +5 -5
- package/src/components/Slider/slider.tsx +3 -1
- package/src/components/Steps/steps.anatomy.stories.tsx +4 -4
- package/src/components/Steps/steps.principles.stories.tsx +1 -1
- package/src/components/Steps/steps.spec.md +1 -1
- package/src/components/Switch/switch.anatomy.stories.tsx +7 -7
- package/src/components/Switch/switch.principles.stories.tsx +4 -4
- package/src/components/Switch/switch.spec.md +8 -8
- package/src/components/Switch/switch.stories.tsx +9 -24
- package/src/components/Switch/switch.tsx +17 -13
- package/src/components/Tabs/tabs.anatomy.stories.tsx +10 -7
- package/src/components/Tabs/tabs.spec.md +1 -1
- package/src/components/Tabs/tabs.tsx +21 -1
- package/src/components/Textarea/textarea.anatomy.stories.tsx +1 -1
- package/src/components/Textarea/textarea.principles.stories.tsx +1 -1
- package/src/components/Textarea/textarea.spec.md +4 -4
- package/src/components/Textarea/textarea.tsx +72 -46
- package/src/components/TimePicker/time-columns.tsx +6 -2
- package/src/components/TimePicker/time-picker.principles.stories.tsx +1 -1
- package/src/components/TimePicker/time-picker.spec.md +3 -3
- package/src/components/TimePicker/time-picker.tsx +16 -10
- package/src/components/Toast/toast.stories.tsx +7 -4
- package/src/components/Tooltip/tooltip.anatomy.stories.tsx +4 -4
- package/src/components/Tooltip/tooltip.principles.stories.tsx +3 -3
- package/src/components/Tooltip/tooltip.spec.md +1 -1
- package/src/components/Tooltip/tooltip.stories.tsx +4 -39
- package/src/components/Tooltip/tooltip.tsx +8 -6
- package/src/components/TreeView/tree-view.anatomy.stories.tsx +6 -3
- package/src/components/TreeView/tree-view.spec.md +42 -11
- package/src/components/TreeView/tree-view.stories.tsx +1 -0
- package/src/components/TreeView/tree-view.tsx +322 -17
- package/src/index.ts +584 -60
- package/src/patterns/action-bar/action-bar.spec.md +9 -0
- package/src/patterns/action-bar/action-bar.stories.tsx +6 -3
- package/src/patterns/element-anatomy/inline-action.spec.md +2 -0
- package/src/patterns/element-anatomy/item-anatomy.spec.md +14 -5
- package/src/patterns/element-anatomy/item-anatomy.stories.tsx +6 -4
- package/src/patterns/element-anatomy/item-anatomy.tsx +7 -6
- package/src/patterns/header-canonical/header-canonical.spec.md +5 -4
- package/src/patterns/overlay-surface/overlay-surface.spec.md +22 -1
- package/src/styles/tokens.css +3 -0
- package/src/tokens/README.md +1 -1
- package/src/tokens/color/color.spec.md +3 -3
- package/src/tokens/color/color.stories.tsx +3 -3
- package/src/tokens/density/density.stories.tsx +16 -3
- package/src/tokens/layoutSpace/layoutSpace.spec.md +3 -1
- package/src/tokens/motion/motion.css +33 -21
- package/src/tokens/motion/motion.spec.md +32 -15
- package/src/tokens/motion/motion.stories.tsx +58 -5
- package/src/tokens/motion/motion.ts +7 -6
- package/src/tokens/motion/overlay-motion.ts +26 -0
- package/src/tokens/orphan-tokens.spec.md +4 -4
- package/src/tokens/token-system.spec.md +1 -0
- package/src/tokens/uiSize/uiSize.spec.md +1 -1
- package/src/tokens/uiSize/uiSize.stories.tsx +4 -2
- package/src/tokens/utility-registry.json +160 -23
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.js","sources":["../../../src/components/Select/select.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.\n// code-quality-allow: file-size — Select 含 3 子元件(NativeSelect/CustomSelect/ReadonlyDisplay)+ helpers + 4-mode renderer + Field SSOT consumption,split-into-files 會破壞 file-local helper closure\n// @renderer-symmetry-allow: pre-existing Select architecture(2026-05-08 D-path)— selectedItemRenderer 由 CustomSelectTriggerContent 消費(edit + trigger 模式),ReadonlyDisplay 走 separate bare-span path(no D-path)。display→edit unify deferred 下 cycle per spec contract (a) note。本 turn 只加 `nakedCellRowModeAlign` import,no behavior change to renderer symmetry contract。\nimport * as React from 'react'\nimport { X, ChevronDown } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport type { FieldMode, FieldVariant } from '@/design-system/components/Field/field-types'\nimport { fieldWrapperStyles, bareInputStyles, EMPTY_DISPLAY, nakedCellRowModeAlign, fieldDisplayTextClass } from '@/design-system/components/Field/field-wrapper'\nimport { Tag } from '@/design-system/components/Tag/tag'\nimport { ItemInlineAction, ItemPrefix, ItemSuffix } from '@/design-system/patterns/element-anatomy/item-anatomy'\nimport { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid } from '@/design-system/components/Field/field-context'\nimport { SelectMenu, forwardKeyToListbox, useActiveDescendant, type SelectMenuOption } from '@/design-system/components/SelectMenu/select-menu'\nimport { useIsTouchDevice } from '@/design-system/hooks/use-is-touch-device'\nimport { useControllable } from '@/design-system/hooks/use-controllable'\nimport { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'\n\n// ── Tag padding per size ────────────────────────────────────────────────────\nconst tagPadding: Record<string, string> = {\n sm: 'px-[calc((var(--field-height-sm)_-_1.25rem)_/_2)]',\n md: 'px-[calc((var(--field-height-md)_-_1.5rem)_/_2)]',\n lg: 'px-[calc((var(--field-height-lg)_-_1.5rem)_/_2)]',\n}\n\n// ── Display ─────────────────────────────────────────────────────────────────\n\n/**\n * Select 用的 option schema(2026-05-10 Issue 4 + post-prune unify):**explicit extends\n * SelectMenuOption(primitive SSOT)** — 任何 SelectMenuOption 加 field 都自動繼承,不會 drift。\n *\n * Why `extends SelectMenuOption`(per user 「全盤檢查避免下次又改壞或是偏移」要求):\n * - **schema SSOT 機械強制**:TypeScript inheritance 跟著 primitive 走,wrapper consumer 永遠\n * 拿得到 primitive 所有 surface field\n * - **Hook lint**(M30 `check_wrapper_primitive_schema_drift.sh`):grep `interface .*Option`\n * 未 `extends` SelectMenuOption / 同名重複 declare 直接 BLOCK\n *\n * Wrapper-only field(`tagVariant`)— Select 獨有 `display='tag'` 用,SelectMenu primitive 不該知道\n * 此 wrapper-only concern,所以 wrapper 層 extend 加上,不污染 primitive。\n *\n * 對齊 Polaris ChoiceList / Material Autocomplete / Carbon Dropdown 的 wrapper-vs-primitive\n * schema-extension idiom。\n */\nexport interface SelectOption extends SelectMenuOption {\n /** Tag 模式的顏色。只在 display='tag' 時生效,對應 Tag 的 variant。Wrapper-only。 */\n tagVariant?: string\n}\n\n/** 分組設定 — 對齊 SelectMenuGroupConfig SSOT */\nexport interface SelectGroupConfig {\n key: string\n label: string\n}\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\nexport interface SelectProps\n extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'size' | 'value' | 'defaultValue' | 'onChange'> {\n mode?: FieldMode\n /** Field chrome variant. Default = context.variant ?? 'default'. Per-prop override. */\n variant?: FieldVariant\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n options: SelectOption[]\n /** 分組顯示(對齊 SelectMenu groups SSOT)。option.group 對應 groups[].key */\n groups?: SelectGroupConfig[]\n /** Controlled value(consumer 自管 state)。傳 `value` + `onChange` 表示 controlled mode。 */\n value?: string | null\n /** Uncontrolled 初始值(2026-05-21 D3 audit add per user verbatim「決策三照妳建議」+「都給我做到好」)。\n * 不傳 `value` 時 Select 自管 internal state,以 `defaultValue` 為初始值,選變更時 fire `onChange`\n * callback 通知 consumer(但 state 仍歸 Select)。對齊 Radix Select(`defaultValue`)+ shadcn Input\n * (`defaultValue`)+ React `<input>` dual-mode canonical。\n * 互斥規則:同時傳 `value` + `defaultValue` 走 controlled(value 勝),`defaultValue` 僅 first-mount 用。 */\n defaultValue?: string | null\n onChange?: (value: string) => void\n placeholder?: string\n clearable?: boolean\n display?: 'plain' | 'tag'\n startIcon?: LucideIcon\n /** 啟用搜尋(desktop 時 field 變 input,打字即篩選) */\n searchable?: boolean\n /** Loading state(2026-05-15 audit B fix;2026-07-04 Q3 拍板措辭修訂)。\n * Forward 給 SelectMenu primitive SSOT;spinner 只在**無可顯示選項時**佔 empty slot(cmdk CommandEmpty\n * 機制)— 已有 stale options 時保留顯示不清空(對齊 MUI Autocomplete「only if there are no suggestions」)。\n * Trigger 不變(chevron 保留 user 隨時可點開)。*/\n loading?: boolean\n\n /** Menu list 最小列數(空狀態 / 選項少時的視覺一致 reserve)。預設 3 — 選項 < 3 時顯式縮(如 And/Or 兩選項) */\n minRows?: number\n /** Initial open state(uncontrolled)。對齊 Radix Popover defaultOpen canonical;DataTable cell-as-input\n * click → 1 step open menu(Airtable / Notion canonical),consumer pass `defaultOpen` 達成。\n * Note:Native Select(mobile)無 popover 概念,此 prop 僅 Custom path 生效。 */\n defaultOpen?: boolean\n /** open state 變更 callback(對齊 Radix Popover onOpenChange canonical)。\n * DataTable cell-as-input 用:open=false 時 cell 自動 exit edit mode(避免 dismiss 後卡住)。 */\n onOpenChange?: (open: boolean) => void\n /**\n * Display mode 顯 picker intrinsic end icon(2026-05-08 D path Phase 1)。\n * 預設 false:`mode=\"display\"` 純展示 bare span(向後相容)。\n * `variant=\"naked\" && mode=\"display\"` 場景(DataTable cell)opt-in 設 true → wrap 進\n * Field naked-display + 渲 ChevronDown ItemSuffix。**只 display mode 生效**;readonly /\n * disabled / edit 已有 Field wrapper + suffix(不受此 prop 影響)。\n * Authority:`data-table.spec.md:204` + `inline-action.spec.md:157`「Field family endAction(自動繼承)」。\n * @default false\n */\n showDisplayEndIcon?: boolean\n /**\n * Trigger 內「已選項目」客製 render(2026-05-07 v15.5)。\n *\n * 設了 → trigger 不走純文字 / Tag 預設 path,改用 consumer 提供的 ReactNode(收 selectedOpt)。\n * Searchable+open 仍走 input(搜尋優先)。Empty value(no selection)仍走 placeholder。\n *\n * 用例:PeoplePicker 用此 slot 把 single 選中的 person render 成 PersonDisplay\n * (avatar + name)而非純文字 label。對齊 PeoplePicker = Select wrapper SSOT。\n */\n selectedItemRenderer?: (selectedOpt: SelectOption) => React.ReactNode\n /** 搜尋無結果提示(2026-07-04 Q4 拍板接線 — 原 spec 宣稱可覆寫但 prop 從未轉發)。\n * Forward 給 SelectMenu primitive SSOT(default 在 SelectMenu 層「沒有符合的選項」);\n * 僅 Custom path(searchable dropdown)生效,Native path 無搜尋空狀態。\n * 對齊 MUI noOptionsText / Ant notFoundContent / Polaris emptyState 三家公開覆寫點共識。 */\n emptyText?: string\n}\n\n// ── Icon / size helpers ─────────────────────────────────────────────────────\n// 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反)\nconst getIconSize = (size: string) => ICON_SIZE[size as 'sm' | 'md' | 'lg']\n\n// ── Shared sub-components ───────────────────────────────────────────────────\n\n/**\n * Inline clear button for Select trigger.\n * 共用 SSOT — Native + Custom 兩變體統一消費。差別僅 onClick 內是否 stopPropagation\n * (Custom trigger 是 combobox `<div>`,點 clear 不可冒泡到打開 menu;Native `<select>` 自有原生\n * 行為,不需 stopPropagation)。\n *\n * 消費的 SSOT:\n * - patterns/element-anatomy/item-anatomy.spec.md → ItemInlineAction(canonical row inline action)\n */\nfunction SelectClearButton({\n size,\n onClear,\n stopPropagation = false,\n}: {\n size: 'sm' | 'md' | 'lg'\n onClear: () => void\n stopPropagation?: boolean\n}) {\n return (\n <span className=\"relative z-10\">\n <ItemInlineAction\n size={size}\n action={{\n icon: X,\n label: '清除選取', // i18n-allow: DS default inline-action label\n onClick: stopPropagation ? (e) => { e?.stopPropagation(); onClear() } : () => onClear(),\n }}\n />\n </span>\n )\n}\nSelectClearButton.displayName = 'SelectClearButton'\n\n/**\n * Trigger content for CustomSelect — 三種顯示模式分支(searchable+open / text / tag)\n * 抽出降低 `CustomSelect` forwardRef body 長度;邏輯本質是純展示分流,無 hook / ref。\n */\nfunction CustomSelectTriggerContent({\n searchable,\n open,\n isTextDisplay,\n size,\n value,\n selectedLabel,\n selectedOpt,\n SelectedIcon,\n StartIcon,\n iconSize,\n placeholder,\n search,\n setSearch,\n inputRef,\n activeDescendantId,\n ariaLabel,\n labelId,\n selectedItemRenderer,\n}: {\n searchable: boolean\n open: boolean\n isTextDisplay: boolean\n size: 'sm' | 'md' | 'lg'\n value?: string | null\n selectedLabel: string\n selectedOpt?: SelectOption\n SelectedIcon?: LucideIcon\n StartIcon?: LucideIcon\n iconSize: number\n placeholder?: string\n search: string\n setSearch: (v: string) => void\n inputRef: React.RefObject<HTMLInputElement | null>\n activeDescendantId?: string\n ariaLabel?: string\n labelId?: string\n selectedItemRenderer?: (selectedOpt: SelectOption) => React.ReactNode\n}): React.ReactNode {\n // Searchable + open: 顯示搜尋 input\n // 2026-05-15 Bug 2 fix(Claude+Codex Step 5 比稿 consensus,user verbatim「就 A」):\n // 撤掉 native `<input placeholder=selectedLabel>` 不可靠 ellipsis renderer(browser-specific\n // placeholder painting,user 抓「placeholder 直接被截掉沒 ellipsis」)。改 span overlay:\n // - input native placeholder 限「搜尋…」/「請選擇人員」trigger empty hint(無 selectedLabel)\n // - sibling `<span aria-hidden pointer-events-none absolute inset-0 truncate>` 在 search='' 且\n // 有 selectedLabel 時 overlay 顯該人名(memory aid,truncate-with-ellipsis 可控)\n // 對齊 spec.md §B row 4「open + inline-search + 選 1 人 → input cursor + placeholder = 該人名 + ellipsis」。\n // a11y(2026-07-05 backlog 補接):搜尋 input 是 open 時實際聚焦的元素,accessible name 必接在\n // input 本身(原 codex Q2 guard 註解宣稱「來自 field/label/aria-label」,但接線全在外層 trigger\n // div,焦點所在的 input 無 name)— aria-labelledby 接 FieldLabel labelId、consumer aria-label\n // 優先(對齊 trigger div 同款 guard),兩者皆無時 fallback aria-label「搜尋選項」(對齊\n // combobox.tsx searchAriaLabel canonical default);**不**依賴 placeholder 當 label;overlay\n // span aria-hidden + pointer-events-none。APG「combobox role 移到 input 本身」屬中期重構另案。\n if (searchable && open) {\n const triggerEmptyPlaceholder = placeholder || '搜尋…' // i18n-allow: DS fallback\n const showSelectedOverlay = !search && selectedLabel\n return (\n <span className=\"relative flex-1 min-w-0 inline-flex items-center\">\n {StartIcon && <ItemPrefix><StartIcon size={iconSize} className=\"text-fg-muted pointer-events-none\" aria-hidden /></ItemPrefix>}\n <input\n ref={inputRef as React.RefObject<HTMLInputElement>}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n // a11y(2026-07-05 D4):cmdk active item id(useActiveDescendant)→ SR 播報方向鍵導覽中的 option\n aria-activedescendant={activeDescendantId}\n // accessible name:aria-labelledby(Field label)> consumer aria-label > DS default(accname 優先序)\n aria-label={ariaLabel ?? '搜尋選項'} // i18n-allow: DS default(對齊 combobox.tsx searchAriaLabel canonical)\n aria-labelledby={ariaLabel ? undefined : labelId}\n // Native placeholder 限 trigger empty hint(無 selectedLabel 時);若已 selected,留空交給 overlay span\n placeholder={showSelectedOverlay ? '' : triggerEmptyPlaceholder}\n className={cn(bareInputStyles, 'cursor-text')}\n autoFocus\n />\n {showSelectedOverlay && (\n // 2026-05-16 Bug B 真 root cause fix(Claude+Codex M31 Step 5 比稿 consensus,user verbatim\n // 「修了一百次還沒好」+ codex cite W3C CSS Overflow / MDN / Mozilla Bug 972664#c1):\n // 原 `inline-flex items-center truncate` 套同一 span,text 變 anonymous flex item →\n // `text-overflow:ellipsis` 對 anonymous item 不 styleable → ellipsis dots 不可見(text 純 clip)。\n // 對齊 `person-display.tsx:148` 既有 DS canonical:outer flex container + inner truncate 真實 box。\n // DS-wide grep 29 個 truncate 都遵此 pattern,只本處違反 — 修齊。\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute inset-0 flex items-center text-fg-muted\"\n >\n <span className=\"min-w-0 flex-1 truncate\">{selectedLabel}</span>\n </span>\n )}\n </span>\n )\n }\n // **selectedItemRenderer slot**(2026-05-07 v15.5):consumer 客製 selected display(e.g.\n // PeoplePicker 接 PersonDisplay)。優先於 isTextDisplay / Tag 預設 path,但 empty value\n // 仍走 placeholder。對齊 PeoplePicker = Select wrapper SSOT。\n if (selectedItemRenderer && value && selectedOpt) {\n return (\n <>\n {StartIcon && <ItemPrefix><StartIcon size={iconSize} className=\"text-fg-muted pointer-events-none\" aria-hidden /></ItemPrefix>}\n {/* 2026-05-14 item-anatomy SSOT fix(per codex H2 propagation 斷點):加 nakedCellRowModeAlign\n → autoRowHeight cell 內 selected renderer 也對齊 first-line,不再 vertical-center 整 row。 */}\n <span className={cn(\"flex-1 min-w-0 inline-flex items-center\", nakedCellRowModeAlign)}>{selectedItemRenderer(selectedOpt)}</span>\n </>\n )\n }\n // Text display: 純文字 + optional value icon\n if (isTextDisplay) {\n return (\n <>\n {StartIcon && <ItemPrefix><StartIcon size={iconSize} className=\"text-fg-muted pointer-events-none\" aria-hidden /></ItemPrefix>}\n {!StartIcon && SelectedIcon && value && <ItemPrefix><SelectedIcon size={iconSize} className=\"pointer-events-none\" aria-hidden /></ItemPrefix>}\n <span className={cn('flex-1 min-w-0 truncate', !value && 'text-fg-muted')}>\n {value ? selectedLabel : (placeholder ?? '選擇…')}\n </span>\n </>\n )\n }\n // Tag display: 用 option 的 tagVariant\n return (\n <>\n {value && selectedOpt?.tagVariant\n ? <Tag size={size} color={selectedOpt.tagVariant as 'blue' | 'green' | 'red' | 'yellow' | 'neutral'} className=\"shrink-0 pointer-events-none\">{selectedLabel}</Tag>\n : value\n ? <Tag size={size} className=\"shrink-0 pointer-events-none\">{selectedLabel}</Tag>\n : <span className=\"text-fg-muted\">{placeholder ?? '選擇…'}</span>\n }\n <span className=\"flex-1\" />\n </>\n )\n}\nCustomSelectTriggerContent.displayName = 'CustomSelectTriggerContent'\n\n// ── Shared readonly/disabled/display render ─────────────────────────────────\nfunction ReadonlyDisplay({\n mode, variant: variantProp, size, options, value, display, startIcon: StartIcon, className, placeholder, showDisplayEndIcon,\n}: Pick<SelectProps, 'mode' | 'variant' | 'size' | 'options' | 'value' | 'display' | 'startIcon' | 'className' | 'placeholder' | 'showDisplayEndIcon'>) {\n const resolvedMode = mode ?? 'readonly'\n const variant = variantProp ?? 'default'\n const sz = size ?? 'md'\n const iconSize = getIconSize(sz)\n const label = options?.find(o => o.value === value)?.label ?? value\n const iconColor = resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-fg-muted'\n const isTextDisplay = display !== 'tag'\n // K10+K14 fix(2026-05-04):disabled mode placeholder/empty 顯示色 → fg-disabled(neutral-6),非 fg-muted(neutral-7)\n // user canonical:disabled 顯著性優於 muted。同時 plain mode 必須 respect placeholder prop(之前忽略 = bug)\n const emptyColorCls = resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-fg-muted'\n const emptyText = placeholder ?? EMPTY_DISPLAY\n\n // mode='display':2 path(2026-05-08 D path Phase 1 Select canary)\n // ❌ 預設(無 showDisplayEndIcon):純內容輸出 bare span/Tag(原行為,backward compat)\n // 對齊原 SelectDisplay sub-component(retired)。readonly / disabled 仍走下方 fieldWrapperStyles。\n // ✅ showDisplayEndIcon=true(DataTable cell opt-in):Field naked-display wrapper +\n // ChevronDown ItemSuffix。SSOT canonical 跟 readonly/edit/disabled mode 同 DOM 結構。\n // Authority: data-table.spec.md:204 + inline-action.spec.md:157「Field family endAction」\n if (resolvedMode === 'display') {\n if (!showDisplayEndIcon) {\n // 2026-05-14 I2 fix(spec contract (e) display typography canonical):bare span 必套\n // `fieldDisplayTextClass(sz)`(sm/md→text-body,lg→text-body-lg)— 對齊跨 Field\n // family display 視覺尺寸統一。\n if (!value) return <span className={cn(fieldDisplayTextClass(sz), 'text-fg-muted', className)}>{emptyText}</span>\n if (isTextDisplay) return <span className={cn(fieldDisplayTextClass(sz), 'truncate', className)}>{label}</span>\n const selOpt = options?.find(o => o.value === value)\n const tVariant = selOpt?.tagVariant as 'blue' | 'green' | 'red' | 'yellow' | 'neutral' | undefined\n return <Tag size={sz} color={tVariant} className={className}>{label}</Tag>\n }\n // D path opt-in: Field naked-display wrapper + ItemSuffix ChevronDown\n const selOpt = options?.find(o => o.value === value)\n const tVariant = selOpt?.tagVariant as 'blue' | 'green' | 'red' | 'yellow' | 'neutral' | undefined\n return (\n <div\n className={cn(fieldWrapperStyles({ mode: 'display', variant, size: sz }), value && !isTextDisplay && tagPadding[sz], className)}\n data-field-mode=\"display\"\n >\n {isTextDisplay ? (\n <span className={cn(bareInputStyles, 'flex-1 min-w-0 truncate', !value && emptyColorCls)}>\n {value ? label : emptyText}\n </span>\n ) : value ? (\n <Tag size={sz} color={tVariant}>{label}</Tag>\n ) : (\n <span className={cn('flex-1 min-w-0', emptyColorCls)}>{emptyText}</span>\n )}\n <ItemSuffix><ChevronDown size={iconSize} className=\"text-fg-muted pointer-events-none\" aria-hidden /></ItemSuffix>\n </div>\n )\n }\n\n // 2026-06-26 類型身份 indicator 規則:edit 顯示 / readonly 不顯示(純值、不可開下拉,箭頭會誤導) /\n // disabled 保留(fg-disabled,對齊原生 <select disabled> 灰示箭頭 + Accordion M24 precedent)。\n // naked cell 情境依 showDisplayEndIcon = isEditable,維持 2026-05-10 cell canonical「非可編欄不顯」。\n // aria-disabled:styled-disabled(非 native disabled 元素)需明告 AT「inactive」,同時讓 axe 正確\n // 豁免 disabled 文字的 color-contrast(WCAG 1.4.3 inactive UI 例外)。\n const showIndicator = variant === 'naked' ? !!showDisplayEndIcon : resolvedMode === 'disabled'\n const ariaDisabled = resolvedMode === 'disabled' ? true : undefined\n\n if (isTextDisplay) {\n return (\n <div className={cn(fieldWrapperStyles({ mode: resolvedMode, variant, size: sz }), className)} data-field-mode={resolvedMode} aria-disabled={ariaDisabled}>\n {StartIcon && <ItemPrefix><StartIcon size={iconSize} className={cn('pointer-events-none', iconColor)} aria-hidden /></ItemPrefix>}\n <span className={cn('flex-1 min-w-0 truncate', resolvedMode === 'disabled' && 'text-fg-disabled')}>\n {value ? label : <span className={emptyColorCls}>{emptyText}</span>}\n </span>\n {showIndicator && <ItemSuffix className=\"pointer-events-none\"><ChevronDown size={iconSize} className={cn('shrink-0', iconColor)} aria-hidden /></ItemSuffix>}\n </div>\n )\n }\n\n const selectedOpt = options?.find(o => o.value === value)\n const tagVariant = selectedOpt?.tagVariant as 'blue' | 'green' | 'red' | 'yellow' | 'neutral' | undefined\n\n return (\n <div className={cn(fieldWrapperStyles({ mode: resolvedMode, variant, size: sz }), value && tagPadding[sz], className)} style={{ paddingRight: 'var(--field-px)' }} data-field-mode={resolvedMode} aria-disabled={ariaDisabled}>\n {value ? <Tag size={sz} color={tagVariant}>{label}</Tag> : <span className={emptyColorCls}>{emptyText}</span>}\n {showIndicator && <ItemSuffix className=\"pointer-events-none\"><ChevronDown size={iconSize} className={cn('shrink-0', iconColor)} aria-hidden /></ItemSuffix>}\n </div>\n )\n}\n\n// ── Native Select (mobile) ─────────────────────────────────────────────\n\n// code-quality-allow: long-function — foundational composite main body — 拆 sub-fn 會複雜化 local state / ref / context binding\nconst NativeSelect = React.forwardRef<HTMLSelectElement, SelectProps>(\n ({ mode, variant: variantProp, error: errorProp = false, size: sizeProp, options, value: valueProp, defaultValue, onChange, placeholder, className, disabled: disabledProp, clearable = false, display = 'plain', startIcon: StartIcon, showDisplayEndIcon, id: idProp, 'aria-describedby': ariaDescribedByProp, 'aria-errormessage': ariaErrorMessageProp,\n // 2026-07-04:Custom-path-only props 顯式 destructure 丟棄(不 spread 到原生 <select> 消 React\n // unknown-prop warning);各 prop docblock 已註「僅 Custom path 生效」語意\n searchable: _searchable, groups: _groups, loading: _loading, minRows: _minRows, emptyText: _emptyText, defaultOpen: _defaultOpen, onOpenChange: _onOpenChange, selectedItemRenderer: _selectedItemRenderer,\n ...props }, ref) => {\n const fieldCtx = useFieldContext()\n const error = useResolvedFieldInvalid(errorProp)\n const disabled = useResolvedFieldDisabled(disabledProp)\n // 2026-05-31 #11:size 從 Field context cascade(對齊 Input/NumberInput + MUI FormControl)\n const size = useResolvedFieldSize(sizeProp)\n // 2026-06-08 SSOT:mode 經 useResolvedFieldMode(prop > 有效 disabled > fieldCtx.mode > 'edit');修 <Field mode=\"display\"> 漏 cascade\n const resolvedMode = useResolvedFieldMode({ mode, disabled })\n const variant: FieldVariant = useResolvedFieldVariant(variantProp)\n const iconSize = getIconSize(size)\n // 2026-05-21 D3 audit:Controlled / Uncontrolled dual-mode via 既有 SSOT hook(同 CustomSelect)\n const [value, setValue] = useControllable<string | null>({\n value: valueProp,\n defaultValue: defaultValue ?? null,\n onChange: onChange ? (next) => onChange(next ?? '') : undefined,\n })\n const handleNativeChange = (v: string) => setValue(v)\n const showClear = clearable && value && resolvedMode === 'edit'\n const isTextDisplay = display === 'plain'\n const selectRef = React.useRef<HTMLSelectElement | null>(null)\n const setSelectRef = React.useCallback((el: HTMLSelectElement | null) => {\n selectRef.current = el\n if (typeof ref === 'function') ref(el)\n else if (ref) (ref as React.MutableRefObject<HTMLSelectElement | null>).current = el\n }, [ref])\n\n if (resolvedMode !== 'edit') {\n return <ReadonlyDisplay mode={resolvedMode} variant={variant} size={size} options={options} value={value} display={display} startIcon={StartIcon} className={className} placeholder={placeholder} showDisplayEndIcon={showDisplayEndIcon} />\n }\n\n const selectEl = (\n <select\n ref={setSelectRef}\n id={idProp ?? fieldCtx?.id}\n value={value ?? ''}\n onChange={(e) => handleNativeChange(e.target.value)}\n disabled={disabled}\n aria-invalid={error || undefined}\n aria-required={fieldCtx?.required || undefined}\n aria-describedby={ariaDescribedByProp ?? fieldCtx?.descriptionId}\n aria-errormessage={ariaErrorMessageProp ?? (error ? fieldCtx?.errorId : undefined)}\n className={cn(bareInputStyles, 'cursor-pointer appearance-none', !value && 'text-fg-muted', !isTextDisplay && value && 'absolute inset-0 w-full h-full opacity-0 z-0')}\n {...props}\n >\n {placeholder && <option value=\"\" disabled>{placeholder}</option>}\n {/* disabled forward(2026-07-04):對齊桌機 path menuOptions disabled forward — 觸控原生 option 同樣不可選(spec「disabled 選項」邊界案例) */}\n {options.map(opt => <option key={opt.value} value={opt.value} disabled={opt.disabled}>{opt.label}</option>)}\n </select>\n )\n\n const clearEl = showClear ? (\n <SelectClearButton size={size ?? 'md'} onClear={() => handleNativeChange('')} />\n ) : null\n\n const chevronEl = (\n <ItemSuffix className=\"relative z-10 pointer-events-none\">\n <ChevronDown size={iconSize} className=\"text-fg-muted\" aria-hidden />\n </ItemSuffix>\n )\n const selectedOpt = options?.find(o => o.value === value)\n const label = selectedOpt?.label ?? value\n const nativeTagVariant = selectedOpt?.tagVariant as 'blue' | 'green' | 'red' | 'yellow' | 'neutral' | undefined\n const SelectedOptIcon = selectedOpt?.icon\n\n if (!isTextDisplay) {\n return (\n <div className={cn(fieldWrapperStyles({ mode: 'edit', variant: variant, size, error }), value && tagPadding[size], 'relative',\n className)}\n style={{ paddingRight: 'var(--field-px)' }} data-field-mode=\"edit\" data-error={error ? '' : undefined}>\n {value ? <Tag size={size} color={nativeTagVariant} className=\"shrink-0 relative z-10 pointer-events-none\">{label}</Tag> : <span className=\"text-fg-muted\">{placeholder ?? '選擇...'}</span>}\n {selectEl}\n <span className=\"flex-1\" />\n {clearEl}\n {chevronEl}\n </div>\n )\n }\n\n return (\n <div className={cn(fieldWrapperStyles({ mode: 'edit', variant: variant, size, error }),\n className)}\n data-field-mode=\"edit\" data-error={error ? '' : undefined}>\n {StartIcon && <ItemPrefix><StartIcon size={iconSize} className=\"text-fg-muted pointer-events-none\" aria-hidden /></ItemPrefix>}\n {!StartIcon && SelectedOptIcon && value && <ItemPrefix><SelectedOptIcon size={iconSize} className=\"pointer-events-none\" aria-hidden /></ItemPrefix>}\n {selectEl}\n {clearEl}\n {chevronEl}\n </div>\n )\n }\n)\nNativeSelect.displayName = 'NativeSelect'\n\n// ── Custom Select (desktop — consumes SelectMenu) ────────────────────────\n\n// code-quality-allow: long-function — foundational composite main body — 拆 sub-fn 會複雜化 local state / ref / context binding\nconst CustomSelect = React.forwardRef<HTMLDivElement, SelectProps>(\n ({ mode, variant: variantProp, error: errorProp = false, size: sizeProp, options, groups, value: valueProp, defaultValue, onChange, placeholder, className, disabled: disabledProp, clearable = false, display = 'plain', startIcon: StartIcon, searchable = false, loading, minRows, emptyText, defaultOpen = false, onOpenChange, selectedItemRenderer, showDisplayEndIcon, id: idProp, 'aria-describedby': ariaDescribedByProp, 'aria-errormessage': ariaErrorMessageProp, 'aria-label': ariaLabel }, ref) => {\n const fieldCtx = useFieldContext()\n const error = useResolvedFieldInvalid(errorProp)\n const disabled = useResolvedFieldDisabled(disabledProp)\n // 2026-05-31 #11:size 從 Field context cascade(對齊 Input/NumberInput + MUI FormControl)\n const size = useResolvedFieldSize(sizeProp)\n // 2026-06-08 SSOT:mode 經 useResolvedFieldMode(prop > 有效 disabled > fieldCtx.mode > 'edit');修 <Field mode=\"display\"> 漏 cascade\n const resolvedMode = useResolvedFieldMode({ mode, disabled })\n const variant: FieldVariant = useResolvedFieldVariant(variantProp)\n const iconSize = getIconSize(size)\n // 2026-05-21 D3 audit:Controlled / Uncontrolled dual-mode via 既有 SSOT hook(M17 對齊,取代自刻 isControlled pattern)。\n // Phase B codex 抓:之前 Custom clear 走 `onChange?.('')` 沒 setInternalValue → uncontrolled clear 失效。useControllable 統一 setter 修。\n // onChange forward coerce null → ''(consumer 簽名 `(value: string) => void`,null 是 internal empty signal)。\n const [value, setValue] = useControllable<string | null>({\n value: valueProp,\n defaultValue: defaultValue ?? null,\n onChange: onChange ? (next) => onChange(next ?? '') : undefined,\n })\n const showClear = clearable && value && resolvedMode === 'edit'\n const isTextDisplay = display === 'plain'\n\n const [open, setOpen] = React.useState(defaultOpen)\n const [search, setSearch] = React.useState('')\n const inputRef = React.useRef<HTMLInputElement>(null)\n // a11y(2026-07-04):listbox 容器 id——trigger aria-controls 指向 SelectMenu PopoverContent\n // (對齊姊妹元件 combobox.tsx:677 既有 canonical;React.useId SSR/CSR 穩定)。\n const listboxId = React.useId()\n // a11y(2026-07-05 D4):追蹤 cmdk active item id → searchable trigger input 綁 aria-activedescendant\n // (機制詳 select-menu.tsx useActiveDescendant docblock;必在 early return 前呼叫 — React #310 hook 順序)。\n const activeOptionId = useActiveDescendant(listboxId, open)\n\n // 關閉時清搜尋\n React.useEffect(() => { if (!open) setSearch('') }, [open])\n\n // **React #310 fix(2026-05-04)**:所有 hooks 必在任何 early return 前 call,\n // 否則 disabled→edit 切換時 hook count 變動 → React 死亡。\n // 原本 useMemo(L280, L291) 在 early return 之後 = latent bug,K13 觸發(filter Op 從 disabled\n // 變 edit 當 user 選欄位)。修法:把所有 useMemo 提到 early return 之前。\n const selectedOpt = options?.find(o => o.value === value)\n // 2026-05-06 v9.1:value 不在 options 也要顯示原值(不沉默丟失)。原 fallback `''` 致\n // SelectCell 開 edit 時若 cell value 不在當前 options(e.g. 上游資料漂移 / options async\n // 後到 / 跨 dataset),trigger 顯示空白 — user 報「value 不見」。對齊 ReadonlyDisplay 同\n // 級 fallback `?? value`。\n const selectedLabel = selectedOpt?.label ?? value ?? ''\n const SelectedIcon = selectedOpt?.icon\n // ── 過濾選項 ──\n const filteredOptions = searchable && search\n ? options.filter(o => o.label.toLowerCase().includes(search.toLowerCase()))\n : options\n // ── 轉換 SelectOption → SelectMenuOption(必在 early return 前) ──\n // Issue 4(2026-05-10):forward avatar / description / disabled SSOT(per SelectMenuOption schema)。\n const menuOptions: SelectMenuOption[] = React.useMemo(\n () => filteredOptions.map(opt => ({\n value: opt.value,\n label: opt.label,\n icon: isTextDisplay ? opt.icon : undefined,\n avatar: opt.avatar,\n description: opt.description,\n disabled: opt.disabled,\n group: opt.group,\n })),\n [filteredOptions, isTextDisplay]\n )\n // ── Tag display 自訂 label 渲染(必在 early return 前) ──\n const renderLabel = React.useMemo(() => {\n if (isTextDisplay) return undefined\n return (menuOpt: SelectMenuOption) => {\n const srcOpt = options.find(o => o.value === menuOpt.value)\n if (srcOpt?.tagVariant) {\n return <Tag size={size} color={srcOpt.tagVariant as 'blue' | 'green' | 'red' | 'yellow' | 'neutral'}>{menuOpt.label}</Tag>\n }\n return menuOpt.label\n }\n }, [isTextDisplay, options, size])\n\n // **React #310 fix v2(2026-05-04)**:`handleValueChange` useCallback 也必在 early return 前\n // 原本 L306(early return 後)→ disabled→edit 切換時 hook count 仍變 → #310 持續\n // 2026-05-21 D3:`useControllable` 統一 controlled / uncontrolled state + onChange forward,不再手動 if-branch。\n const handleValueChange = React.useCallback(\n (newValue: string | string[]) => {\n setValue(Array.isArray(newValue) ? newValue[0] : newValue)\n },\n [setValue]\n )\n\n // Early return AFTER all hooks(disabled / readonly / display mode 走 ReadonlyDisplay)\n if (resolvedMode !== 'edit') {\n return <ReadonlyDisplay mode={resolvedMode} variant={variant} size={size} options={options} value={value} display={display} startIcon={StartIcon} className={className} placeholder={placeholder} showDisplayEndIcon={showDisplayEndIcon} />\n }\n\n // 2026-05-21 D3 Phase B codex 抓:Custom clear 用 setValue 不直接 onChange,uncontrolled clear 才能真清 internal state。\n const clearEl = showClear ? (\n <SelectClearButton size={size ?? 'md'} onClear={() => setValue('')} stopPropagation />\n ) : null\n\n const chevronEl = (\n <ItemSuffix>\n <ChevronDown size={iconSize} className={cn('text-fg-muted transition-transform', open && 'rotate-180')} aria-hidden />\n </ItemSuffix>\n )\n\n const triggerContent = (\n <CustomSelectTriggerContent\n searchable={searchable}\n open={open}\n isTextDisplay={isTextDisplay}\n size={size}\n value={value}\n selectedLabel={selectedLabel}\n selectedOpt={selectedOpt}\n SelectedIcon={SelectedIcon}\n StartIcon={StartIcon}\n iconSize={iconSize}\n placeholder={placeholder}\n search={search}\n setSearch={setSearch}\n inputRef={inputRef}\n activeDescendantId={activeOptionId}\n ariaLabel={ariaLabel}\n labelId={fieldCtx?.labelId}\n selectedItemRenderer={selectedItemRenderer}\n />\n )\n\n // hooks(filteredOptions / menuOptions / renderLabel / handleValueChange)已全 hoist(React #310 fix v2)\n\n const trigger = (\n <div\n ref={ref}\n id={idProp ?? fieldCtx?.id}\n role=\"combobox\"\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n aria-controls={listboxId}\n aria-label={ariaLabel}\n // a11y(2026-07-04):div-based role=combobox 的 <label for> 無效,接 FieldLabel labelId\n // (field-context.ts labelId jsDoc 明文);consumer aria-label 優先(對齊 slider.tsx:166 guard canonical)。\n aria-labelledby={ariaLabel ? undefined : fieldCtx?.labelId}\n aria-invalid={error || undefined}\n aria-required={fieldCtx?.required || undefined}\n aria-describedby={ariaDescribedByProp ?? fieldCtx?.descriptionId}\n aria-errormessage={ariaErrorMessageProp ?? (error ? fieldCtx?.errorId : undefined)}\n tabIndex={0}\n className={cn(\n fieldWrapperStyles({ mode: 'edit', variant: variant, size, error }),\n !isTextDisplay && value && !searchable && tagPadding[size],\n // 2026-05-06 v13.3 SSOT retire:per-control `open && 'border-primary'` 移除。Field default\n // state machine `data-[state=open]:border-border-hover`(灰深)處理 open。\n // 2026-07-04 Q1:error border 同樣收進 fieldWrapperStyles error variant(error 勝 focus 色)。\n 'cursor-pointer',\n className,\n )}\n style={!isTextDisplay ? { paddingRight: 'var(--field-px)' } : undefined}\n data-field-mode=\"edit\"\n data-error={error ? '' : undefined}\n onKeyDown={(e) => {\n // 2026-07-05 D4 P0:open 後 ArrowUp/Down/Enter 轉送 cmdk root(見 select-menu.tsx\n // forwardKeyToListbox docblock — 原「open 後不攔讓方向鍵導覽」在跨 DOM 子樹機制上不成立)\n if (open && forwardKeyToListbox(listboxId, e)) return\n if (e.key === 'Enter' || e.key === ' ') {\n // 2026-06-11 P0 a11y(R2 deep-audit):原 guard `!searchable` 連「關閉時的鍵盤開啟」一起擋\n // → searchable Select / PeoplePicker single 鍵盤打不開(WCAG 2.1.1)。原意只是開啟後\n // 別吃掉搜尋框的 Space/Enter → 正確 guard = !open(關閉才攔;開啟後不干擾 input 輸入)。\n if (!open) { e.preventDefault(); setOpen(true) }\n }\n // APG combobox 展開鍵:ArrowDown 也可開(對齊 combobox.tsx 同 pattern;open 後不攔讓方向鍵導覽選單)\n if (e.key === 'ArrowDown' && !open) { e.preventDefault(); setOpen(true) }\n if (e.key === 'Escape') setOpen(false)\n }}\n >\n {triggerContent}\n {clearEl}\n {chevronEl}\n </div>\n )\n\n return (\n <SelectMenu\n options={menuOptions}\n groups={groups}\n value={value ?? null}\n onValueChange={handleValueChange}\n searchable={false}\n loading={loading}\n emptyText={emptyText}\n size={size}\n minRows={minRows}\n open={open}\n onOpenChange={(o) => { setOpen(o); onOpenChange?.(o) }}\n contentId={listboxId}\n renderLabel={renderLabel}\n onOpenAutoFocus={searchable ? (e) => { e.preventDefault(); inputRef.current?.focus() } : undefined}\n >\n {trigger}\n </SelectMenu>\n )\n }\n)\nCustomSelect.displayName = 'CustomSelect'\n\n// ── Public component(自動偵測 mobile / desktop)──────────────────────────────\n\nconst Select = React.forwardRef<HTMLSelectElement | HTMLDivElement, SelectProps>(\n (props, ref) => {\n const isMobile = useIsTouchDevice()\n\n if (isMobile) {\n return <NativeSelect ref={ref as React.Ref<HTMLSelectElement>} {...props} />\n }\n\n return <CustomSelect ref={ref as React.Ref<HTMLDivElement>} {...props} />\n }\n)\nSelect.displayName = 'Select'\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 selectMeta = {\n component: 'Select',\n family: 4,\n variants: {\n\n },\n sizes: {\n\n },\n // 'active' 移除 — trigger 走 Field chrome、menu row 走 MenuItem(其 meta 無 active)(2026-07-07 詞彙統一 DS-wide 按壓訊號盤點:檔內 0 active: utility / 0 *-active token)。\n states: ['default', 'hover', 'focus-visible', 'disabled'],\n tokens: {\n bg: [],\n fg: ['text-fg-disabled', 'text-fg-muted'],\n ring: [],\n },\n} as const\n\nexport { Select }\n"],"names":["selOpt","tVariant"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"select.js","sources":["../../../src/components/Select/select.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.\n// code-quality-allow: file-size — Select 含 3 子元件(NativeSelect/CustomSelect/ReadonlyDisplay)+ helpers + 4-mode renderer + Field SSOT consumption,split-into-files 會破壞 file-local helper closure\n// @renderer-symmetry-allow: 2026-07-08 WM 戰役 A 案回歸修正 — ReadonlyDisplay 現已消費 selectedItemRenderer(display bare-span / D-path / readonly / disabled 四分支),對齊 field-controls.spec.md 共享 contract (a)「display/readonly/disabled/edit 4 mode 共享同一 renderer」。前 note「display→edit unify deferred」已兌現(值內容層);chrome 結構 unify(D-path)仍為 opt-in showDisplayEndIcon。\nimport * as React from 'react'\nimport { X, ChevronDown } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport type { FieldMode, FieldVariant, FieldVariantInternal, FieldWidth } from '@/design-system/components/Field/field-types'\nimport { fieldWrapperStyles, bareInputStyles, nakedCellRowModeAlign, fieldDisplayTextClass } from '@/design-system/components/Field/field-wrapper'\nimport { Tag } from '@/design-system/components/Tag/tag'\nimport { ItemInlineAction, ItemPrefix, ItemSuffix } from '@/design-system/patterns/element-anatomy/item-anatomy'\nimport { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid, useFieldEmptyDisplay, fieldEmptyColorClass } from '@/design-system/components/Field/field-context'\nimport { SelectMenu, forwardKeyToListbox, useActiveDescendant, type SelectMenuOption } from '@/design-system/components/SelectMenu/select-menu'\nimport { useIsTouchDevice } from '@/design-system/hooks/use-is-touch-device'\nimport { useControllable } from '@/design-system/hooks/use-controllable'\nimport { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'\n\n// ── Tag padding per size ────────────────────────────────────────────────────\nconst tagPadding: Record<string, string> = {\n sm: 'px-[calc((var(--field-height-sm)_-_1.25rem)_/_2)]',\n md: 'px-[calc((var(--field-height-md)_-_1.5rem)_/_2)]',\n lg: 'px-[calc((var(--field-height-lg)_-_1.5rem)_/_2)]',\n}\n\n// ── Display ─────────────────────────────────────────────────────────────────\n\n/**\n * Select 用的 option schema(2026-05-10 Issue 4 + post-prune unify):**explicit extends\n * SelectMenuOption(primitive SSOT)** — 任何 SelectMenuOption 加 field 都自動繼承,不會 drift。\n *\n * Why `extends SelectMenuOption`(per user 「全盤檢查避免下次又改壞或是偏移」要求):\n * - **schema SSOT 機械強制**:TypeScript inheritance 跟著 primitive 走,wrapper consumer 永遠\n * 拿得到 primitive 所有 surface field\n * - **Hook lint**(M30 `check_wrapper_primitive_schema_drift.sh`):grep `interface .*Option`\n * 未 `extends` SelectMenuOption / 同名重複 declare 直接 BLOCK\n *\n * Wrapper-only field(`tagVariant`)— Select 獨有 `display='tag'` 用,SelectMenu primitive 不該知道\n * 此 wrapper-only concern,所以 wrapper 層 extend 加上,不污染 primitive。\n *\n * 對齊 Polaris ChoiceList / Material Autocomplete / Carbon Dropdown 的 wrapper-vs-primitive\n * schema-extension idiom。\n */\nexport interface SelectOption extends SelectMenuOption {\n /** Tag 模式的顏色。只在 display='tag' 時生效,對應 Tag 的 variant。Wrapper-only。 */\n tagVariant?: string\n}\n\n/** 分組設定 — 對齊 SelectMenuGroupConfig SSOT */\nexport interface SelectGroupConfig {\n key: string\n label: string\n}\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\n/**\n * 原生 `<select>` 屬性 allowlist(2026-07-14 API 策展 D,user 拍板「全部收窄」)。\n *\n * 舊契約 `extends Omit<SelectHTMLAttributes, ...>` 型別承諾**全部**原生 select 屬性,但桌機\n * 自訂 combobox(div trigger + hidden input mirror)只有部分真生效 — 其餘 silent no-op /\n * 被元件內部覆寫,「型別承諾 ≠ 實作出口」= footgun。改 `Pick<>` 顯式 allowlist,**只承諾\n * 兩路徑(desktop CustomSelect / mobile NativeSelect)皆有實作出口的屬性**,盤點以 code 為準:\n *\n * - `id` — 桌機 trigger div / mobile `<select>`,皆 `idProp ?? fieldCtx?.id`\n * - `name` / `form` / `required` — 桌機經 hidden input mirror(D2 2026-07-13,見 hiddenInputEl\n * docblock)/ mobile 原生 `<select>` spread\n * - `disabled` — 兩路徑 `useResolvedFieldDisabled` cascade → mode 分流\n * - `className` / `style` — 兩路徑 field wrapper\n * - `onFocus` / `onBlur` — 桌機 rest spread 到 focusable trigger(tabIndex=0)/ mobile spread\n * - `onKeyDown` — 桌機 consumer handler 先跑再走元件導覽(dim-9)/ mobile 原生\n * - `aria-label` / `aria-describedby` / `aria-errormessage` — 兩路徑顯式接線(fieldCtx fallback)\n * - `autoFocus` / `autoComplete` — **mobile-only**:NativeSelect 原生 `<select>` 生效;桌機\n * custom combobox 無原生對應 = documented no-op(桌機 autofill 由 hidden input mirror 的\n * option-match onChange guard 承擔)\n * - `data-*` — 兩路徑 spread passthrough(dim-9 契約;index signature 顯式承諾,見下)\n *\n * 被砍者(原 extends 承諾但從未生效 / 被元件覆寫,不再進型別):`multiple` / `children` /\n * `tabIndex`(桌機恆 0)/ `aria-labelledby`(桌機由 fieldCtx.labelId / aria-label 管理,\n * consumer 值會被覆寫)/ 其餘 generic HTMLAttributes(title / dir / onClick 等)。\n * 需要時先接線(兩路徑等效)再入表,**不回 extends 全開**。\n *\n * 世界級對照:自訂 combobox 皆走 curated 原生屬性子集,無人 extends 全部 SelectHTMLAttributes —\n * Radix Select.Root 顯式列舉 name/disabled/required/form/autoComplete\n * (radix-ui.com/primitives/docs/components/select#root)/ Headless UI Listbox name/form/disabled\n * (headlessui.com/react/listbox#using-with-html-forms)/ React Aria Select name/autoComplete/\n * isDisabled/isRequired(react-spectrum.adobe.com/react-aria/Select.html)。\n */\nexport interface SelectProps\n extends Pick<\n React.SelectHTMLAttributes<HTMLSelectElement>,\n // ── 兩路徑皆真生效 ──\n | 'id' | 'className' | 'style'\n | 'name' | 'form' | 'required' | 'disabled'\n | 'onFocus' | 'onBlur' | 'onKeyDown'\n | 'aria-label' | 'aria-describedby' | 'aria-errormessage'\n // ── mobile-only(NativeSelect 原生 <select> 生效;桌機 custom combobox documented no-op)──\n | 'autoFocus' | 'autoComplete'\n > {\n /** `data-*` passthrough(桌機 trigger div rest spread / mobile `<select>` spread;dim-9 契約)。 */\n [dataAttribute: `data-${string}`]: unknown\n mode?: FieldMode\n /** Field chrome variant. Default = context.variant ?? 'default'. Per-prop override. */\n variant?: FieldVariant\n /** 寬度軸 — `fill` 填滿容器(default)/ `hug` 依內容收縮:value 寬 + slot 元素寬 + gap +\n * field 內 padding;chrome 與互動不變。場景 = detail pane inline metadata。\n * SSOT → field-controls.spec.md「寬度軸(width: fill / hug)」。 */\n width?: FieldWidth\n error?: boolean\n size?: 'sm' | 'md' | 'lg'\n options: SelectOption[]\n /** 分組顯示(對齊 SelectMenu groups SSOT)。option.group 對應 groups[].key */\n groups?: SelectGroupConfig[]\n /** Controlled value(consumer 自管 state)。傳 `value` + `onChange` 表示 controlled mode。 */\n value?: string | null\n /** Uncontrolled 初始值(2026-05-21 D3 audit add per user verbatim「決策三照妳建議」+「都給我做到好」)。\n * 不傳 `value` 時 Select 自管 internal state,以 `defaultValue` 為初始值,選變更時 fire `onChange`\n * callback 通知 consumer(但 state 仍歸 Select)。對齊 Radix Select(`defaultValue`)+ shadcn Input\n * (`defaultValue`)+ React `<input>` dual-mode canonical。\n * 互斥規則:同時傳 `value` + `defaultValue` 走 controlled(value 勝),`defaultValue` 僅 first-mount 用。 */\n defaultValue?: string | null\n onChange?: (value: string) => void\n placeholder?: string\n clearable?: boolean\n display?: 'plain' | 'tag'\n startIcon?: LucideIcon\n /** 啟用搜尋(desktop 時 field 變 input,打字即篩選) */\n searchable?: boolean\n /** Loading state(2026-05-15 audit B fix;2026-07-04 Q3 拍板措辭修訂)。\n * Forward 給 SelectMenu primitive SSOT;spinner 只在**無可顯示選項時**佔 empty slot(cmdk CommandEmpty\n * 機制)— 已有 stale options 時保留顯示不清空(對齊 MUI Autocomplete「only if there are no suggestions」)。\n * Trigger 不變(chevron 保留 user 隨時可點開)。*/\n loading?: boolean\n\n /** Menu list 最小列數(空狀態 / 選項少時的視覺一致 reserve)。預設 3 — 選項 < 3 時顯式縮(如 And/Or 兩選項) */\n minRows?: number\n /** Initial open state(uncontrolled)。對齊 Radix Popover defaultOpen canonical;DataTable cell-as-input\n * click → 1 step open menu(Airtable / Notion canonical),consumer pass `defaultOpen` 達成。\n * Note:Native Select(mobile)無 popover 概念,此 prop 僅 Custom path 生效。 */\n defaultOpen?: boolean\n /** open state 變更 callback(對齊 Radix Popover onOpenChange canonical)。\n * DataTable cell-as-input 用:open=false 時 cell 自動 exit edit mode(避免 dismiss 後卡住)。 */\n onOpenChange?: (open: boolean) => void\n /**\n * Display mode 顯 picker intrinsic end icon(2026-05-08 D path Phase 1)。\n * 預設 false:`mode=\"display\"` 純展示 bare span(向後相容)。\n * `variant=\"naked\" && mode=\"display\"` 場景(DataTable cell)opt-in 設 true → wrap 進\n * Field naked-display + 渲 ChevronDown ItemSuffix。**只 display mode 生效**;readonly /\n * disabled / edit 已有 Field wrapper + suffix(不受此 prop 影響)。\n * Authority:`data-table.spec.md:204` + `inline-action.spec.md:157`「Field family endAction(自動繼承)」。\n * @default false\n */\n showDisplayEndIcon?: boolean\n /**\n * 「已選項目」客製 render(2026-05-07 v15.5;2026-07-08 A 案回歸修正擴及 4 mode)。\n *\n * 設了 → 已選值不走純文字 / Tag 預設 path,改用 consumer 提供的 ReactNode(收 selectedOpt)。\n * **display / readonly / disabled / edit 4 mode 共享同一 renderer**(field-controls.spec.md\n * 共享 contract (a),禁 edit-only)— renderer 輸出屬「值內容」(status icon+text 等語意呈現),\n * display 態照常渲染;A 案撤的是 affordance(chevron/outline),非值內容(field.spec.md L6 分層)。\n * Searchable+open 仍走 input(搜尋優先)。Empty value(no selection)仍走 placeholder。\n *\n * 用例:PeoplePicker 用此 slot 把 single 選中的 person render 成 PersonDisplay\n * (avatar + name)而非純文字 label;DataTable status 欄 icon+text。對齊 PeoplePicker = Select wrapper SSOT。\n */\n selectedItemRenderer?: (selectedOpt: SelectOption) => React.ReactNode\n /** 搜尋無結果提示(2026-07-04 Q4 拍板接線 — 原 spec 宣稱可覆寫但 prop 從未轉發)。\n * Forward 給 SelectMenu primitive SSOT(default 在 SelectMenu 層「沒有符合的選項」);\n * 僅 Custom path(searchable dropdown)生效,Native path 無搜尋空狀態。\n * 對齊 MUI noOptionsText / Ant notFoundContent / Polaris emptyState 三家公開覆寫點共識。 */\n emptyText?: string\n}\n\n// ── Icon / size helpers ─────────────────────────────────────────────────────\n// 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反)\nconst getIconSize = (size: string) => ICON_SIZE[size as 'sm' | 'md' | 'lg']\n\n// ── Shared sub-components ───────────────────────────────────────────────────\n\n/**\n * Inline clear button for Select trigger.\n * 共用 SSOT — Native + Custom 兩變體統一消費。差別僅 onClick 內是否 stopPropagation\n * (Custom trigger 是 combobox `<div>`,點 clear 不可冒泡到打開 menu;Native `<select>` 自有原生\n * 行為,不需 stopPropagation)。\n *\n * 消費的 SSOT:\n * - patterns/element-anatomy/item-anatomy.spec.md → ItemInlineAction(canonical row inline action)\n */\nfunction SelectClearButton({\n size,\n onClear,\n stopPropagation = false,\n}: {\n size: 'sm' | 'md' | 'lg'\n onClear: () => void\n stopPropagation?: boolean\n}) {\n return (\n <span className=\"relative z-10\">\n <ItemInlineAction\n size={size}\n action={{\n icon: X,\n label: '清除選取', // i18n-allow: DS default inline-action label\n onClick: stopPropagation ? (e) => { e?.stopPropagation(); onClear() } : () => onClear(),\n }}\n />\n </span>\n )\n}\nSelectClearButton.displayName = 'SelectClearButton'\n\n/**\n * Trigger content for CustomSelect — 三種顯示模式分支(searchable+open / text / tag)\n * 抽出降低 `CustomSelect` forwardRef body 長度;邏輯本質是純展示分流,無 hook / ref。\n */\nfunction CustomSelectTriggerContent({\n searchable,\n open,\n isTextDisplay,\n size,\n value,\n selectedLabel,\n selectedOpt,\n SelectedIcon,\n StartIcon,\n iconSize,\n placeholder,\n search,\n setSearch,\n inputRef,\n activeDescendantId,\n ariaLabel,\n labelId,\n selectedItemRenderer,\n}: {\n searchable: boolean\n open: boolean\n isTextDisplay: boolean\n size: 'sm' | 'md' | 'lg'\n value?: string | null\n selectedLabel: string\n selectedOpt?: SelectOption\n SelectedIcon?: LucideIcon\n StartIcon?: LucideIcon\n iconSize: number\n placeholder?: string\n search: string\n setSearch: (v: string) => void\n inputRef: React.RefObject<HTMLInputElement | null>\n activeDescendantId?: string\n ariaLabel?: string\n labelId?: string\n selectedItemRenderer?: (selectedOpt: SelectOption) => React.ReactNode\n}): React.ReactNode {\n // Searchable + open: 顯示搜尋 input\n // 2026-05-15 Bug 2 fix(Claude+Codex Step 5 比稿 consensus,user verbatim「就 A」):\n // 撤掉 native `<input placeholder=selectedLabel>` 不可靠 ellipsis renderer(browser-specific\n // placeholder painting,user 抓「placeholder 直接被截掉沒 ellipsis」)。改 span overlay:\n // - input native placeholder 限「搜尋…」/「請選擇人員」trigger empty hint(無 selectedLabel)\n // - sibling `<span aria-hidden pointer-events-none absolute inset-0 truncate>` 在 search='' 且\n // 有 selectedLabel 時 overlay 顯該人名(memory aid,truncate-with-ellipsis 可控)\n // 對齊 spec.md §B row 4「open + inline-search + 選 1 人 → input cursor + placeholder = 該人名 + ellipsis」。\n // a11y(2026-07-05 backlog 補接):搜尋 input 是 open 時實際聚焦的元素,accessible name 必接在\n // input 本身(原 codex Q2 guard 註解宣稱「來自 field/label/aria-label」,但接線全在外層 trigger\n // div,焦點所在的 input 無 name)— aria-labelledby 接 FieldLabel labelId、consumer aria-label\n // 優先(對齊 trigger div 同款 guard),兩者皆無時 fallback aria-label「搜尋選項」(對齊\n // combobox.tsx searchAriaLabel canonical default);**不**依賴 placeholder 當 label;overlay\n // span aria-hidden + pointer-events-none。APG「combobox role 移到 input 本身」屬中期重構另案。\n if (searchable && open) {\n const triggerEmptyPlaceholder = placeholder || '搜尋…' // i18n-allow: DS fallback\n const showSelectedOverlay = !search && selectedLabel\n return (\n <span className=\"relative flex-1 min-w-0 inline-flex items-center\">\n {StartIcon && <ItemPrefix><StartIcon size={iconSize} className=\"text-fg-muted pointer-events-none\" aria-hidden /></ItemPrefix>}\n <input\n ref={inputRef as React.RefObject<HTMLInputElement>}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n // a11y(2026-07-05 D4):cmdk active item id(useActiveDescendant)→ SR 播報方向鍵導覽中的 option\n aria-activedescendant={activeDescendantId}\n // accessible name:aria-labelledby(Field label)> consumer aria-label > DS default(accname 優先序)\n aria-label={ariaLabel ?? '搜尋選項'} // i18n-allow: DS default(對齊 combobox.tsx searchAriaLabel canonical)\n aria-labelledby={ariaLabel ? undefined : labelId}\n // Native placeholder 限 trigger empty hint(無 selectedLabel 時);若已 selected,留空交給 overlay span\n placeholder={showSelectedOverlay ? '' : triggerEmptyPlaceholder}\n className={cn(bareInputStyles, 'cursor-text')}\n autoFocus\n />\n {showSelectedOverlay && (\n // 2026-05-16 Bug B 真 root cause fix(Claude+Codex M31 Step 5 比稿 consensus,user verbatim\n // 「修了一百次還沒好」+ codex cite W3C CSS Overflow / MDN / Mozilla Bug 972664#c1):\n // 原 `inline-flex items-center truncate` 套同一 span,text 變 anonymous flex item →\n // `text-overflow:ellipsis` 對 anonymous item 不 styleable → ellipsis dots 不可見(text 純 clip)。\n // 對齊 `person-display.tsx:148` 既有 DS canonical:outer flex container + inner truncate 真實 box。\n // DS-wide grep 29 個 truncate 都遵此 pattern,只本處違反 — 修齊。\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute inset-0 flex items-center text-fg-muted\"\n >\n <span className=\"min-w-0 flex-1 truncate\">{selectedLabel}</span>\n </span>\n )}\n </span>\n )\n }\n // **selectedItemRenderer slot**(2026-05-07 v15.5):consumer 客製 selected display(e.g.\n // PeoplePicker 接 PersonDisplay)。優先於 isTextDisplay / Tag 預設 path,但 empty value\n // 仍走 placeholder。對齊 PeoplePicker = Select wrapper SSOT。\n if (selectedItemRenderer && value && selectedOpt) {\n return (\n <>\n {StartIcon && <ItemPrefix><StartIcon size={iconSize} className=\"text-fg-muted pointer-events-none\" aria-hidden /></ItemPrefix>}\n {/* 2026-05-14 item-anatomy SSOT fix(per codex H2 propagation 斷點):加 nakedCellRowModeAlign\n → autoRowHeight cell 內 selected renderer 也對齊 first-line,不再 vertical-center 整 row。 */}\n <span className={cn(\"flex-1 min-w-0 inline-flex items-center\", nakedCellRowModeAlign)}>{selectedItemRenderer(selectedOpt)}</span>\n </>\n )\n }\n // Text display: 純文字 + optional value icon\n if (isTextDisplay) {\n return (\n <>\n {StartIcon && <ItemPrefix><StartIcon size={iconSize} className=\"text-fg-muted pointer-events-none\" aria-hidden /></ItemPrefix>}\n {!StartIcon && SelectedIcon && value && <ItemPrefix><SelectedIcon size={iconSize} className=\"pointer-events-none\" aria-hidden /></ItemPrefix>}\n <span className={cn('flex-1 min-w-0 truncate', !value && 'text-fg-muted')}>\n {value ? selectedLabel : (placeholder ?? '選擇…')}\n </span>\n </>\n )\n }\n // Tag display: 用 option 的 tagVariant\n return (\n <>\n {value && selectedOpt?.tagVariant\n ? <Tag size={size} color={selectedOpt.tagVariant as 'blue' | 'green' | 'red' | 'yellow' | 'neutral'} className=\"shrink-0 pointer-events-none\">{selectedLabel}</Tag>\n : value\n ? <Tag size={size} className=\"shrink-0 pointer-events-none\">{selectedLabel}</Tag>\n : <span className=\"text-fg-muted\">{placeholder ?? '選擇…'}</span>\n }\n <span className=\"flex-1\" />\n </>\n )\n}\nCustomSelectTriggerContent.displayName = 'CustomSelectTriggerContent'\n\n// ── Shared readonly/disabled/display render ─────────────────────────────────\nfunction ReadonlyDisplay({\n mode, variant: variantProp, width, size, options, value, display, startIcon: StartIcon, className, placeholder, showDisplayEndIcon, selectedItemRenderer,\n}: Pick<SelectProps, 'mode' | 'width' | 'size' | 'options' | 'value' | 'display' | 'startIcon' | 'className' | 'placeholder' | 'showDisplayEndIcon' | 'selectedItemRenderer'> & {\n /** @internal 2026-07-14 API 策展 E:內部 render helper 吃 FieldVariantInternal(naked 由 cell-registry 通道傳入)*/\n variant?: FieldVariantInternal\n}) {\n const resolvedMode = mode ?? 'readonly'\n const emptyDisplay = useFieldEmptyDisplay()\n const variant = variantProp ?? 'default'\n const sz = size ?? 'md'\n const iconSize = getIconSize(sz)\n // selectedOpt 提前 hoist(2026-07-08 A 案回歸修正):display / D-path / readonly / disabled\n // 四分支都需要 — renderer 值內容(field-controls.spec.md contract (a) 4-mode 共享)+ tagVariant 查找。\n const selectedOpt = options?.find(o => o.value === value)\n const label = selectedOpt?.label ?? value\n const iconColor = resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-fg-muted'\n const isTextDisplay = display !== 'tag'\n // K10+K14 fix(2026-05-04):disabled mode placeholder/empty 顯示色 → fg-disabled(neutral-6),非 fg-muted(neutral-7)\n // user canonical:disabled 顯著性優於 muted。同時 plain mode 必須 respect placeholder prop(之前忽略 = bug)\n const emptyColorCls = fieldEmptyColorClass(resolvedMode)\n const emptyText = placeholder ?? emptyDisplay\n\n // mode='display':2 path(2026-05-08 D path Phase 1 Select canary)\n // ❌ 預設(無 showDisplayEndIcon):純內容輸出 bare span/Tag(原行為,backward compat)\n // 對齊原 SelectDisplay sub-component(retired)。readonly / disabled 仍走下方 fieldWrapperStyles。\n // ✅ showDisplayEndIcon=true(DataTable cell opt-in):Field naked-display wrapper +\n // ChevronDown ItemSuffix。SSOT canonical 跟 readonly/edit/disabled mode 同 DOM 結構。\n // Authority: data-table.spec.md:204 + inline-action.spec.md:157「Field family endAction」\n if (resolvedMode === 'display') {\n if (!showDisplayEndIcon) {\n // 2026-05-14 I2 fix(spec contract (e) display typography canonical):bare span 必套\n // `fieldDisplayTextClass(sz)`(sm/md→text-body,lg→text-body-lg)— 對齊跨 Field\n // family display 視覺尺寸統一。\n if (!value) return <span className={cn(fieldDisplayTextClass(sz), emptyColorCls, className)}>{emptyText}</span>\n // 2026-07-08 A 案回歸修正:selectedItemRenderer(值內容:status icon+text 等語意呈現)\n // display 態照常渲染 — 無 chrome 無 chevron,只有值內容本身。分層原則(field.spec.md L6):\n // 本 renderer 歸「值內容」,恆顯;A 案撤的是 affordance(chevron/outline),非值內容。\n // 對齊 field-controls.spec.md 共享 contract (a)「4 mode 共享同一 renderer(禁 edit-only)」。\n if (selectedItemRenderer && selectedOpt) {\n return (\n <span className={cn(fieldDisplayTextClass(sz), 'inline-flex items-center min-w-0', nakedCellRowModeAlign, className)}>\n {selectedItemRenderer(selectedOpt)}\n </span>\n )\n }\n if (isTextDisplay) return <span className={cn(fieldDisplayTextClass(sz), 'truncate', className)}>{label}</span>\n const tVariant = selectedOpt?.tagVariant as 'blue' | 'green' | 'red' | 'yellow' | 'neutral' | undefined\n return <Tag size={sz} color={tVariant} className={className}>{label}</Tag>\n }\n // D path opt-in: Field naked-display wrapper + ItemSuffix ChevronDown\n const tVariant = selectedOpt?.tagVariant as 'blue' | 'green' | 'red' | 'yellow' | 'neutral' | undefined\n return (\n <div\n className={cn(fieldWrapperStyles({ mode: 'display', variant, width, size: sz }), value && !isTextDisplay && tagPadding[sz], className)}\n data-field-mode=\"display\"\n >\n {selectedItemRenderer && value && selectedOpt ? (\n // renderer 優先(同 CustomSelectTriggerContent 優先序):值內容歸 renderer,chevron 歸 D-path chrome\n <span className={cn('flex-1 min-w-0 inline-flex items-center', nakedCellRowModeAlign)}>\n {selectedItemRenderer(selectedOpt)}\n </span>\n ) : isTextDisplay ? (\n <span className={cn(bareInputStyles, 'flex-1 min-w-0 truncate', !value && emptyColorCls)}>\n {value ? label : emptyText}\n </span>\n ) : value ? (\n <Tag size={sz} color={tVariant}>{label}</Tag>\n ) : (\n <span className={cn('flex-1 min-w-0', emptyColorCls)}>{emptyText}</span>\n )}\n <ItemSuffix><ChevronDown size={iconSize} className=\"text-fg-muted pointer-events-none\" aria-hidden /></ItemSuffix>\n </div>\n )\n }\n\n // 2026-06-26 類型身份 indicator 規則:edit 顯示 / readonly 不顯示(純值、不可開下拉,箭頭會誤導) /\n // disabled 保留(fg-disabled,對齊原生 <select disabled> 灰示箭頭 + Accordion M24 precedent)。\n // naked cell 情境依 showDisplayEndIcon = isEditable,維持 2026-05-10 cell canonical「非可編欄不顯」。\n // aria-disabled:styled-disabled(非 native disabled 元素)需明告 AT「inactive」,同時讓 axe 正確\n // 豁免 disabled 文字的 color-contrast(WCAG 1.4.3 inactive UI 例外)。\n const showIndicator = variant === 'naked' ? !!showDisplayEndIcon : resolvedMode === 'disabled'\n const ariaDisabled = resolvedMode === 'disabled' ? true : undefined\n\n // 2026-07-08 A 案回歸修正:readonly / disabled 同樣消費 selectedItemRenderer(值內容 4-mode\n // 共享,field-controls.spec.md contract (a);對照 PeoplePicker readonly/disabled 渲 PersonDisplay\n // 先例)。renderer 優先於 isTextDisplay / Tag 預設 path(同 CustomSelectTriggerContent 優先序);\n // disabled 時 wrapper span 帶 text-fg-disabled 供未自帶色的文字繼承(M24 disabled > muted)。\n if (selectedItemRenderer && value && selectedOpt) {\n return (\n <div className={cn(fieldWrapperStyles({ mode: resolvedMode, variant, width, size: sz }), className)} data-field-mode={resolvedMode} aria-disabled={ariaDisabled}>\n {StartIcon && <ItemPrefix><StartIcon size={iconSize} className={cn('pointer-events-none', iconColor)} aria-hidden /></ItemPrefix>}\n <span className={cn('flex-1 min-w-0 inline-flex items-center', nakedCellRowModeAlign, resolvedMode === 'disabled' && 'text-fg-disabled')}>\n {selectedItemRenderer(selectedOpt)}\n </span>\n {showIndicator && <ItemSuffix className=\"pointer-events-none\"><ChevronDown size={iconSize} className={cn('shrink-0', iconColor)} aria-hidden /></ItemSuffix>}\n </div>\n )\n }\n\n if (isTextDisplay) {\n return (\n <div className={cn(fieldWrapperStyles({ mode: resolvedMode, variant, width, size: sz }), className)} data-field-mode={resolvedMode} aria-disabled={ariaDisabled}>\n {StartIcon && <ItemPrefix><StartIcon size={iconSize} className={cn('pointer-events-none', iconColor)} aria-hidden /></ItemPrefix>}\n <span className={cn('flex-1 min-w-0 truncate', resolvedMode === 'disabled' && 'text-fg-disabled')}>\n {value ? label : <span className={emptyColorCls}>{emptyText}</span>}\n </span>\n {showIndicator && <ItemSuffix className=\"pointer-events-none\"><ChevronDown size={iconSize} className={cn('shrink-0', iconColor)} aria-hidden /></ItemSuffix>}\n </div>\n )\n }\n\n const tagVariant = selectedOpt?.tagVariant as 'blue' | 'green' | 'red' | 'yellow' | 'neutral' | undefined\n\n return (\n <div className={cn(fieldWrapperStyles({ mode: resolvedMode, variant, width, size: sz }), value && tagPadding[sz], className)} style={{ paddingRight: 'var(--field-px)' }} data-field-mode={resolvedMode} aria-disabled={ariaDisabled}>\n {value ? <Tag size={sz} color={tagVariant}>{label}</Tag> : <span className={emptyColorCls}>{emptyText}</span>}\n {showIndicator && <ItemSuffix className=\"pointer-events-none\"><ChevronDown size={iconSize} className={cn('shrink-0', iconColor)} aria-hidden /></ItemSuffix>}\n </div>\n )\n}\n\n// ── Native Select (mobile) ─────────────────────────────────────────────\n\n// code-quality-allow: long-function — foundational composite main body — 拆 sub-fn 會複雜化 local state / ref / context binding\nconst NativeSelect = React.forwardRef<HTMLSelectElement, SelectProps>(\n ({ mode, variant: variantProp, width, error: errorProp = false, size: sizeProp, options, value: valueProp, defaultValue, onChange, placeholder, className, disabled: disabledProp, clearable = false, display = 'plain', startIcon: StartIcon, showDisplayEndIcon, id: idProp, 'aria-describedby': ariaDescribedByProp, 'aria-errormessage': ariaErrorMessageProp,\n // 2026-07-04:Custom-path-only props 顯式 destructure 丟棄(不 spread 到原生 <select> 消 React\n // unknown-prop warning);各 prop docblock 已註「僅 Custom path 生效」語意\n // 2026-07-08 A 案回歸修正:selectedItemRenderer 從丟棄名單移出 — Native path 的\n // ReadonlyDisplay(display/readonly/disabled)同樣消費值內容 renderer(contract (a) 4-mode\n // 共享,值內容不因 pointer type 而異);native <select> edit 路徑仍不消費(原生 option 無法客製 render)。\n searchable: _searchable, groups: _groups, loading: _loading, minRows: _minRows, emptyText: _emptyText, defaultOpen: _defaultOpen, onOpenChange: _onOpenChange, selectedItemRenderer,\n ...props }, ref) => {\n const fieldCtx = useFieldContext()\n const error = useResolvedFieldInvalid(errorProp)\n const disabled = useResolvedFieldDisabled(disabledProp)\n // 2026-05-31 #11:size 從 Field context cascade(對齊 Input/NumberInput + MUI FormControl)\n const size = useResolvedFieldSize(sizeProp)\n // 2026-06-08 SSOT:mode 經 useResolvedFieldMode(prop > 有效 disabled > fieldCtx.mode > 'edit');修 <Field mode=\"display\"> 漏 cascade\n const resolvedMode = useResolvedFieldMode({ mode, disabled })\n const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)\n const iconSize = getIconSize(size)\n // 2026-05-21 D3 audit:Controlled / Uncontrolled dual-mode via 既有 SSOT hook(同 CustomSelect)\n const [value, setValue] = useControllable<string | null>({\n value: valueProp,\n defaultValue: defaultValue ?? null,\n onChange: onChange ? (next) => onChange(next ?? '') : undefined,\n })\n const handleNativeChange = (v: string) => setValue(v)\n const showClear = clearable && value && resolvedMode === 'edit'\n const isTextDisplay = display === 'plain'\n const selectRef = React.useRef<HTMLSelectElement | null>(null)\n const setSelectRef = React.useCallback((el: HTMLSelectElement | null) => {\n selectRef.current = el\n if (typeof ref === 'function') ref(el)\n else if (ref) (ref as React.MutableRefObject<HTMLSelectElement | null>).current = el\n }, [ref])\n\n if (resolvedMode !== 'edit') {\n return <ReadonlyDisplay mode={resolvedMode} variant={variant} width={width} size={size} options={options} value={value} display={display} startIcon={StartIcon} className={className} placeholder={placeholder} showDisplayEndIcon={showDisplayEndIcon} selectedItemRenderer={selectedItemRenderer} />\n }\n\n const selectEl = (\n <select\n ref={setSelectRef}\n id={idProp ?? fieldCtx?.id}\n value={value ?? ''}\n onChange={(e) => handleNativeChange(e.target.value)}\n disabled={disabled}\n aria-invalid={error || undefined}\n aria-required={fieldCtx?.required || undefined}\n aria-describedby={ariaDescribedByProp ?? fieldCtx?.descriptionId}\n aria-errormessage={ariaErrorMessageProp ?? (error ? fieldCtx?.errorId : undefined)}\n className={cn(bareInputStyles, 'cursor-pointer appearance-none', !value && 'text-fg-muted', !isTextDisplay && value && 'absolute inset-0 w-full h-full opacity-0 z-0')}\n {...props}\n >\n {placeholder && <option value=\"\" disabled>{placeholder}</option>}\n {/* disabled forward(2026-07-04):對齊桌機 path menuOptions disabled forward — 觸控原生 option 同樣不可選(spec「disabled 選項」邊界案例) */}\n {options.map(opt => <option key={opt.value} value={opt.value} disabled={opt.disabled}>{opt.label}</option>)}\n </select>\n )\n\n const clearEl = showClear ? (\n <SelectClearButton size={size ?? 'md'} onClear={() => handleNativeChange('')} />\n ) : null\n\n const chevronEl = (\n <ItemSuffix className=\"relative z-10 pointer-events-none\">\n <ChevronDown size={iconSize} className=\"text-fg-muted\" aria-hidden />\n </ItemSuffix>\n )\n const selectedOpt = options?.find(o => o.value === value)\n const label = selectedOpt?.label ?? value\n const nativeTagVariant = selectedOpt?.tagVariant as 'blue' | 'green' | 'red' | 'yellow' | 'neutral' | undefined\n const SelectedOptIcon = selectedOpt?.icon\n\n if (!isTextDisplay) {\n return (\n <div className={cn(fieldWrapperStyles({ mode: 'edit', variant: variant, width, size, error }), value && tagPadding[size], 'relative',\n className)}\n style={{ paddingRight: 'var(--field-px)' }} data-field-mode=\"edit\" data-error={error ? '' : undefined}>\n {value ? <Tag size={size} color={nativeTagVariant} className=\"shrink-0 relative z-10 pointer-events-none\">{label}</Tag> : <span className=\"text-fg-muted\">{placeholder ?? '選擇...'}</span>}\n {selectEl}\n <span className=\"flex-1\" />\n {clearEl}\n {chevronEl}\n </div>\n )\n }\n\n return (\n <div className={cn(fieldWrapperStyles({ mode: 'edit', variant: variant, width, size, error }),\n className)}\n data-field-mode=\"edit\" data-error={error ? '' : undefined}>\n {StartIcon && <ItemPrefix><StartIcon size={iconSize} className=\"text-fg-muted pointer-events-none\" aria-hidden /></ItemPrefix>}\n {!StartIcon && SelectedOptIcon && value && <ItemPrefix><SelectedOptIcon size={iconSize} className=\"pointer-events-none\" aria-hidden /></ItemPrefix>}\n {selectEl}\n {clearEl}\n {chevronEl}\n </div>\n )\n }\n)\nNativeSelect.displayName = 'NativeSelect'\n\n// ── Custom Select (desktop — consumes SelectMenu) ────────────────────────\n\n// code-quality-allow: long-function — foundational composite main body — 拆 sub-fn 會複雜化 local state / ref / context binding\nconst CustomSelect = React.forwardRef<HTMLDivElement, SelectProps>(\n ({ mode, variant: variantProp, width, error: errorProp = false, size: sizeProp, options, groups, value: valueProp, defaultValue, onChange, placeholder, className, disabled: disabledProp, name, required, form, clearable = false, display = 'plain', startIcon: StartIcon, searchable = false, loading, minRows, emptyText, defaultOpen = false, onOpenChange, selectedItemRenderer, showDisplayEndIcon, id: idProp, 'aria-describedby': ariaDescribedByProp, 'aria-errormessage': ariaErrorMessageProp, 'aria-label': ariaLabel, onKeyDown: onKeyDownProp, style: styleProp,\n // 2026-07-14 API 策展 D:mobile-only props(allowlist 註記)desktop 顯式丟棄 — div trigger 無原生\n // 對應,不 spread 進 DOM(對稱 NativeSelect 丟棄 custom-path-only props 的既有 pattern)\n autoFocus: _autoFocus, autoComplete: _autoComplete, ...rest }, ref) => {\n const fieldCtx = useFieldContext()\n const error = useResolvedFieldInvalid(errorProp)\n const disabled = useResolvedFieldDisabled(disabledProp)\n // 2026-05-31 #11:size 從 Field context cascade(對齊 Input/NumberInput + MUI FormControl)\n const size = useResolvedFieldSize(sizeProp)\n // 2026-06-08 SSOT:mode 經 useResolvedFieldMode(prop > 有效 disabled > fieldCtx.mode > 'edit');修 <Field mode=\"display\"> 漏 cascade\n const resolvedMode = useResolvedFieldMode({ mode, disabled })\n const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)\n const iconSize = getIconSize(size)\n // 2026-05-21 D3 audit:Controlled / Uncontrolled dual-mode via 既有 SSOT hook(M17 對齊,取代自刻 isControlled pattern)。\n // Phase B codex 抓:之前 Custom clear 走 `onChange?.('')` 沒 setInternalValue → uncontrolled clear 失效。useControllable 統一 setter 修。\n // onChange forward coerce null → ''(consumer 簽名 `(value: string) => void`,null 是 internal empty signal)。\n const [value, setValue] = useControllable<string | null>({\n value: valueProp,\n defaultValue: defaultValue ?? null,\n onChange: onChange ? (next) => onChange(next ?? '') : undefined,\n })\n const showClear = clearable && value && resolvedMode === 'edit'\n const isTextDisplay = display === 'plain'\n\n const [open, setOpen] = React.useState(defaultOpen)\n const [search, setSearch] = React.useState('')\n const inputRef = React.useRef<HTMLInputElement>(null)\n // a11y(2026-07-04):listbox 容器 id——trigger aria-controls 指向 SelectMenu PopoverContent\n // (對齊姊妹元件 combobox.tsx:677 既有 canonical;React.useId SSR/CSR 穩定)。\n const listboxId = React.useId()\n // a11y(2026-07-05 D4):追蹤 cmdk active item id → searchable trigger input 綁 aria-activedescendant\n // (機制詳 select-menu.tsx useActiveDescendant docblock;必在 early return 前呼叫 — React #310 hook 順序)。\n const activeOptionId = useActiveDescendant(listboxId, open)\n\n // 關閉時清搜尋\n React.useEffect(() => { if (!open) setSearch('') }, [open])\n\n // **React #310 fix(2026-05-04)**:所有 hooks 必在任何 early return 前 call,\n // 否則 disabled→edit 切換時 hook count 變動 → React 死亡。\n // 原本 useMemo(L280, L291) 在 early return 之後 = latent bug,K13 觸發(filter Op 從 disabled\n // 變 edit 當 user 選欄位)。修法:把所有 useMemo 提到 early return 之前。\n const selectedOpt = options?.find(o => o.value === value)\n // 2026-05-06 v9.1:value 不在 options 也要顯示原值(不沉默丟失)。原 fallback `''` 致\n // SelectCell 開 edit 時若 cell value 不在當前 options(e.g. 上游資料漂移 / options async\n // 後到 / 跨 dataset),trigger 顯示空白 — user 報「value 不見」。對齊 ReadonlyDisplay 同\n // 級 fallback `?? value`。\n const selectedLabel = selectedOpt?.label ?? value ?? ''\n const SelectedIcon = selectedOpt?.icon\n // ── 過濾選項 ──\n const filteredOptions = searchable && search\n ? options.filter(o => o.label.toLowerCase().includes(search.toLowerCase()))\n : options\n // ── 轉換 SelectOption → SelectMenuOption(必在 early return 前) ──\n // Issue 4(2026-05-10):forward avatar / description / disabled SSOT(per SelectMenuOption schema)。\n const menuOptions: SelectMenuOption[] = React.useMemo(\n () => filteredOptions.map(opt => ({\n value: opt.value,\n label: opt.label,\n icon: isTextDisplay ? opt.icon : undefined,\n avatar: opt.avatar,\n description: opt.description,\n disabled: opt.disabled,\n group: opt.group,\n })),\n [filteredOptions, isTextDisplay]\n )\n // ── Tag display 自訂 label 渲染(必在 early return 前) ──\n const renderLabel = React.useMemo(() => {\n if (isTextDisplay) return undefined\n return (menuOpt: SelectMenuOption) => {\n const srcOpt = options.find(o => o.value === menuOpt.value)\n if (srcOpt?.tagVariant) {\n return <Tag size={size} color={srcOpt.tagVariant as 'blue' | 'green' | 'red' | 'yellow' | 'neutral'}>{menuOpt.label}</Tag>\n }\n return menuOpt.label\n }\n }, [isTextDisplay, options, size])\n\n // **React #310 fix v2(2026-05-04)**:`handleValueChange` useCallback 也必在 early return 前\n // 原本 L306(early return 後)→ disabled→edit 切換時 hook count 仍變 → #310 持續\n // 2026-05-21 D3:`useControllable` 統一 controlled / uncontrolled state + onChange forward,不再手動 if-branch。\n const handleValueChange = React.useCallback(\n (newValue: string | string[]) => {\n setValue(Array.isArray(newValue) ? newValue[0] : newValue)\n },\n [setValue]\n )\n\n // Early return AFTER all hooks(disabled / readonly / display mode 走 ReadonlyDisplay)\n if (resolvedMode !== 'edit') {\n return <ReadonlyDisplay mode={resolvedMode} variant={variant} width={width} size={size} options={options} value={value} display={display} startIcon={StartIcon} className={className} placeholder={placeholder} showDisplayEndIcon={showDisplayEndIcon} selectedItemRenderer={selectedItemRenderer} />\n }\n\n // 2026-05-21 D3 Phase B codex 抓:Custom clear 用 setValue 不直接 onChange,uncontrolled clear 才能真清 internal state。\n const clearEl = showClear ? (\n <SelectClearButton size={size ?? 'md'} onClear={() => setValue('')} stopPropagation />\n ) : null\n\n const chevronEl = (\n <ItemSuffix>\n <ChevronDown size={iconSize} className={cn('text-fg-muted transition-transform', open && 'rotate-180')} aria-hidden />\n </ItemSuffix>\n )\n\n // ── Hidden input form mirror(2026-07-13 D2 拍板 Option A;MUI SelectInput idiom)──\n // 桌機自訂 combobox 無原生 form control,`name` / `required` / `form` 歷史上被靜默丟棄\n // (mobile NativeSelect 卻經 {...props} 生效 = 雙路徑提交語義不對稱 footgun)。\n // 補 visually-hidden `<input>` 鏡像,讓桌機也參與原生表單(裸 <form> submit / FormData):\n // - value **單向鏡射**受控 state(useControllable)= projection,非第二 SSOT(M17);\n // 空值時 value=''(不採 Radix hidden <select> 完整 option tree — 其無空 option 時\n // default 第一項,radix-ui/primitives issue #3521 documented footgun)\n // - 原生 required 僅收顯式 `required` prop(裸表單 opt-in);fieldCtx.required 續走\n // aria-required + useFormValidation(RHF)方法論(form-validation.spec.md 規則 1-9),\n // 不對既有 Field+RHF consumer 注入瀏覽器原生 bubble\n // - onChange 唯一用途 = 瀏覽器 autofill:match 既有非 disabled option 才經 canonical\n // setter 寫回(對齊 MUI SelectInput handleChange 同款 guard),其餘輸入忽略\n // - aria-hidden + tabIndex={-1}:AT 與 Tab 序皆不見(semantics 由 role=combobox trigger\n // own);form-validation 規則 8 focus-first-error 以 DOM name 定位 → 可聚焦 mirror →\n // trigger focus-within 顯示 focus 邊框(桌機 Select 脫離「非 native 控件略過」fallback)\n // 世界級對照:MUI Select 非原生模式 opacity-0 hidden input 攜 name/required\n // (github.com/mui/material-ui .../Select/SelectInput.js;Select.test.js 官方測試涵蓋\n // required 阻止提交 + FormData 值)+ React Spectrum HiddenSelect(github.com/adobe/\n // react-spectrum .../@react-aria/select/src/HiddenSelect.tsx)+ Headless UI Listbox\n // name → hidden input kept in sync(headlessui.com/react/listbox#using-with-html-forms)。\n const hiddenInputEl = (\n <input\n type=\"text\"\n name={name}\n form={form}\n value={value ?? ''}\n onChange={(e) => {\n const matched = options.find((o) => o.value === e.target.value && !o.disabled)\n if (matched) setValue(matched.value)\n }}\n required={required || undefined}\n disabled={disabled}\n aria-hidden=\"true\"\n tabIndex={-1}\n className=\"absolute bottom-0 left-0 w-full opacity-0 pointer-events-none\"\n />\n )\n\n const triggerContent = (\n <CustomSelectTriggerContent\n searchable={searchable}\n open={open}\n isTextDisplay={isTextDisplay}\n size={size}\n value={value}\n selectedLabel={selectedLabel}\n selectedOpt={selectedOpt}\n SelectedIcon={SelectedIcon}\n StartIcon={StartIcon}\n iconSize={iconSize}\n placeholder={placeholder}\n search={search}\n setSearch={setSearch}\n inputRef={inputRef}\n activeDescendantId={activeOptionId}\n ariaLabel={ariaLabel}\n labelId={fieldCtx?.labelId}\n selectedItemRenderer={selectedItemRenderer}\n />\n )\n\n // hooks(filteredOptions / menuOptions / renderLabel / handleValueChange)已全 hoist(React #310 fix v2)\n\n const trigger = (\n <div\n // passthrough(2026-07-14 dim-9 修;同日 API 策展 D 收窄):SelectProps allowlist\n // (Pick<SelectHTMLAttributes>,見 SelectProps docblock)承諾的 data-* / onFocus /\n // onBlur 等 attrs,mobile NativeSelect 經 {...props} spread 到 <select> 生效,desktop\n // 原本完整列舉全丟 → 同一 <Select> API 隨 pointer type 靜默失效(consumer data-testid /\n // onBlur mobile 有、desktop 無)。rest 先 spread —— component 契約 attrs(role / aria-* /\n // id / tabIndex / onKeyDown / className)在後,不可被覆寫;consumer onKeyDown 經\n // onKeyDownProp 在 handler 內先跑(見下)。mobile-only attrs(autoFocus / autoComplete)\n // 已在 destructure 層丟棄不進 rest(div trigger 無原生語意;desktop 表單語意由 hidden\n // input mirror 承擔,見 D2 docblock)。\n {...(rest as unknown as React.HTMLAttributes<HTMLDivElement>)}\n ref={ref}\n id={idProp ?? fieldCtx?.id}\n role=\"combobox\"\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n aria-controls={listboxId}\n aria-label={ariaLabel}\n // a11y(2026-07-04):div-based role=combobox 的 <label for> 無效,接 FieldLabel labelId\n // (field-context.ts labelId jsDoc 明文);consumer aria-label 優先(對齊 slider.tsx:166 guard canonical)。\n aria-labelledby={ariaLabel ? undefined : fieldCtx?.labelId}\n aria-invalid={error || undefined}\n // D2(2026-07-13):顯式 required prop 同步進 aria-required(mirror 本身 aria-hidden,\n // AT 看不到其 required;語意由 trigger 播報 — 對齊 mobile 原生 <select required> 行為)\n aria-required={(required || fieldCtx?.required) || undefined}\n aria-describedby={ariaDescribedByProp ?? fieldCtx?.descriptionId}\n aria-errormessage={ariaErrorMessageProp ?? (error ? fieldCtx?.errorId : undefined)}\n tabIndex={0}\n className={cn(\n fieldWrapperStyles({ mode: 'edit', variant: variant, width, size, error }),\n !isTextDisplay && value && !searchable && tagPadding[size],\n // 2026-05-06 v13.3 SSOT retire:per-control `open && 'border-primary'` 移除。Field default\n // state machine `data-[state=open]:border-border-hover`(灰深)處理 open。\n // 2026-07-04 Q1:error border 同樣收進 fieldWrapperStyles error variant(error 勝 focus 色)。\n // D2(2026-07-13):`relative` = hidden input mirror 的 absolute 定位容器\n // (fieldWrapperStyles base 無 relative;對齊 NativeSelect tag 分支同款顯式加法)。\n 'relative cursor-pointer',\n className,\n )}\n style={styleProp || !isTextDisplay ? { ...styleProp, ...(!isTextDisplay ? { paddingRight: 'var(--field-px)' } : undefined) } : undefined}\n data-field-mode=\"edit\"\n data-error={error ? '' : undefined}\n onKeyDown={(e) => {\n // passthrough(dim-9):consumer onKeyDown 先跑 —— 對齊 native path(<select> 上\n // consumer handler 同樣最先收到);component 導覽邏輯照舊在後。\n onKeyDownProp?.(e as unknown as React.KeyboardEvent<HTMLSelectElement>)\n // 2026-07-14 dim-10 修:內層清除按鈕(SelectClearButton → ItemInlineAction <button>)\n // 的 Enter/Space 會 bubble 到本 handler 被 preventDefault 吞掉 → 鍵盤無法清除。\n // 事件源自 descendant button 時不執行 trigger 鍵盤邏輯(保留原生 activation)。\n // 注意:不能一刀切 e.target !== e.currentTarget — searchable inline <input> 的\n // 鍵盤事件仍需下方 forwardKeyToListbox 轉送選單導覽。\n if (e.target !== e.currentTarget && (e.target as HTMLElement).closest?.('button')) return\n // 2026-07-05 D4 P0:open 後 ArrowUp/Down/Enter 轉送 cmdk root(見 select-menu.tsx\n // forwardKeyToListbox docblock — 原「open 後不攔讓方向鍵導覽」在跨 DOM 子樹機制上不成立)\n if (open && forwardKeyToListbox(listboxId, e)) return\n if (e.key === 'Enter' || e.key === ' ') {\n // 2026-06-11 P0 a11y(R2 deep-audit):原 guard `!searchable` 連「關閉時的鍵盤開啟」一起擋\n // → searchable Select / PeoplePicker single 鍵盤打不開(WCAG 2.1.1)。原意只是開啟後\n // 別吃掉搜尋框的 Space/Enter → 正確 guard = !open(關閉才攔;開啟後不干擾 input 輸入)。\n if (!open) { e.preventDefault(); setOpen(true) }\n }\n // APG combobox 展開鍵:ArrowDown 也可開(對齊 combobox.tsx 同 pattern;open 後不攔讓方向鍵導覽選單)\n if (e.key === 'ArrowDown' && !open) { e.preventDefault(); setOpen(true) }\n if (e.key === 'Escape') setOpen(false)\n }}\n >\n {triggerContent}\n {clearEl}\n {chevronEl}\n {hiddenInputEl}\n </div>\n )\n\n return (\n <SelectMenu\n options={menuOptions}\n groups={groups}\n value={value ?? null}\n onValueChange={handleValueChange}\n searchable={false}\n loading={loading}\n emptyText={emptyText}\n size={size}\n minRows={minRows}\n open={open}\n onOpenChange={(o) => { setOpen(o); onOpenChange?.(o) }}\n contentId={listboxId}\n renderLabel={renderLabel}\n onOpenAutoFocus={searchable ? (e) => { e.preventDefault(); inputRef.current?.focus() } : undefined}\n >\n {trigger}\n </SelectMenu>\n )\n }\n)\nCustomSelect.displayName = 'CustomSelect'\n\n// ── Public component(自動偵測 mobile / desktop)──────────────────────────────\n\nconst Select = React.forwardRef<HTMLSelectElement | HTMLDivElement, SelectProps>(\n (props, ref) => {\n const isMobile = useIsTouchDevice()\n\n if (isMobile) {\n return <NativeSelect ref={ref as React.Ref<HTMLSelectElement>} {...props} />\n }\n\n return <CustomSelect ref={ref as React.Ref<HTMLDivElement>} {...props} />\n }\n)\nSelect.displayName = 'Select'\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 selectMeta = {\n component: 'Select',\n family: 4,\n variants: {\n\n },\n sizes: {\n\n },\n // 'active' 移除 — trigger 走 Field chrome、menu row 走 MenuItem(其 meta 無 active)(2026-07-07 詞彙統一 DS-wide 按壓訊號盤點:檔內 0 active: utility / 0 *-active token)。\n states: ['default', 'hover', 'focus-visible', 'disabled'],\n tokens: {\n bg: [],\n fg: ['text-fg-disabled', 'text-fg-muted'],\n ring: [],\n },\n} as const\n\nexport { Select }\n"],"names":["tVariant"],"mappings":"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"}
|
|
@@ -25,6 +25,8 @@ export interface SelectMenuOption {
|
|
|
25
25
|
label: string;
|
|
26
26
|
description?: string;
|
|
27
27
|
icon?: LucideIcon;
|
|
28
|
+
/** icon 染色 className(2026-07-08:status 類彩色 option;M30 primitive schema 擴充) */
|
|
29
|
+
iconClassName?: string;
|
|
28
30
|
avatar?: AvatarData;
|
|
29
31
|
disabled?: boolean;
|
|
30
32
|
group?: string;
|
|
@@ -97,7 +99,7 @@ export interface SelectMenuProps {
|
|
|
97
99
|
declare const SelectMenu: React.ForwardRefExoticComponent<SelectMenuProps & React.RefAttributes<HTMLElement>>;
|
|
98
100
|
export declare const selectMenuMeta: {
|
|
99
101
|
readonly component: "SelectMenu";
|
|
100
|
-
readonly family:
|
|
102
|
+
readonly family: null;
|
|
101
103
|
readonly variants: {};
|
|
102
104
|
readonly sizes: {};
|
|
103
105
|
readonly states: readonly ["default", "hover", "active", "selected", "focus-visible", "disabled"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select-menu.d.ts","sourceRoot":"","sources":["../../../src/components/SelectMenu/select-menu.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAG9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAA;AAa1E;;;;;;;;;;;;;;GAcG;AAIH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;CACd;AAED,KAAK,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAIjC,MAAM,WAAW,eAAe;IAC9B,WAAW;IACX,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,gCAAgC;IAChC,MAAM,CAAC,EAAE,qBAAqB,EAAE,CAAA;IAEhC,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAA;IAChC,mBAAmB;IACnB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAA;IAElD,WAAW;IACX,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,YAAY;IACZ,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,aAAa;IACb,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,8CAA8C;IAC9C,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IAEvC,oBAAoB;IACpB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,sBAAsB;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,YAAY;IACZ,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB,SAAS;IACT,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,WAAW;IACX,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAAA;IACvB,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,wBAAwB;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB,iBAAiB;IACjB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,sGAAsG;IACtG,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,yBAAyB;IACzB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IAEtC,2CAA2C;IAC3C,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAA;IAC3D,4EAA4E;IAC5E,eAAe,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,IAAI,CAAA;IAEpC;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAyBD,QAAA,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"select-menu.d.ts","sourceRoot":"","sources":["../../../src/components/SelectMenu/select-menu.tsx"],"names":[],"mappings":"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"}
|
|
@@ -211,6 +211,7 @@ const SelectMenu = React.forwardRef(function SelectMenu2({
|
|
|
211
211
|
{
|
|
212
212
|
size,
|
|
213
213
|
startIcon: opt.icon,
|
|
214
|
+
startIconClassName: opt.iconClassName,
|
|
214
215
|
avatar: opt.avatar,
|
|
215
216
|
description: opt.description,
|
|
216
217
|
checkbox: multiple,
|
|
@@ -277,7 +278,13 @@ const SelectMenu = React.forwardRef(function SelectMenu2({
|
|
|
277
278
|
SelectMenu.displayName = "SelectMenu";
|
|
278
279
|
const selectMenuMeta = {
|
|
279
280
|
component: "SelectMenu",
|
|
280
|
-
family
|
|
281
|
+
// 2026-07-14 修 family 自我矛盾:原 Phase-1 mechanical 填 4(Field control),與 spec body
|
|
282
|
+
// 「Layout Family:非上述 family — composite / multi-section」矛盾。SelectMenu 是 Popover +
|
|
283
|
+
// Command composite overlay(trigger 由 consumer Select / Combobox own,Family 4 是 trigger
|
|
284
|
+
// 側 field control 的事);對齊 DS composite 慣例(Accordion / Command / Popover meta 皆
|
|
285
|
+
// family: null + spec frontmatter composite)。
|
|
286
|
+
family: null,
|
|
287
|
+
// non-family composite / overlay(對齊 spec frontmatter composite)
|
|
281
288
|
variants: {},
|
|
282
289
|
sizes: {},
|
|
283
290
|
// 'selected' = 單選 option 持續選中(bg-neutral-selected);'active' 保留 — cmdk virtual-focus on
|
|
@@ -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 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 — cmdk 永遠渲 sr-only <label htmlFor={inputId}>,\n // 僅 searchable(真的有 input)時傳,避免 orphan label 指向不存在的 input id。\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 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 family: 4,\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 * ├── 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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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FieldSize } from '../../components/Field/field-context';
|
|
2
|
+
/**
|
|
3
|
+
* BooleanValueIcon — boolean 值的展示符號(true = Check 勾 / false = X 叉),中性 `text-foreground` 色。
|
|
4
|
+
* Checkbox / Switch 的 display + readonly 態、DataTable boolean cell(經 Checkbox)全消費此 SSOT。
|
|
5
|
+
*/
|
|
6
|
+
export declare function BooleanValueIcon({ checked, size, className, }: {
|
|
7
|
+
checked: boolean;
|
|
8
|
+
size?: FieldSize;
|
|
9
|
+
className?: string;
|
|
10
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=boolean-value.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boolean-value.d.ts","sourceRoot":"","sources":["../../../src/components/SelectionControl/boolean-value.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gDAAgD,CAAA;AAS/E;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,IAAW,EACX,SAAS,GACV,EAAE;IACD,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,2CAGA"}
|