@qijenchen/design-system 0.1.0-beta.83 → 0.1.0-beta.85
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-group.d.ts +47 -0
- package/dist/components/DataTable/data-table-filter-group.d.ts.map +1 -0
- package/dist/components/DataTable/data-table-filter-group.js +154 -0
- package/dist/components/DataTable/data-table-filter-group.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 +285 -0
- package/dist/components/DataTable/data-table-filter-panel.js.map +1 -0
- package/dist/components/DataTable/data-table-filter-value-picker.d.ts +29 -0
- package/dist/components/DataTable/data-table-filter-value-picker.d.ts.map +1 -0
- package/dist/components/DataTable/data-table-filter-value-picker.js +187 -0
- package/dist/components/DataTable/data-table-filter-value-picker.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/Field/index.d.ts +1 -0
- package/dist/components/Field/index.d.ts.map +1 -1
- package/dist/components/Field/index.js +2 -0
- package/dist/components/Field/index.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 +95 -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 +31 -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 +2 -1
- 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 +28 -0
- package/ds-canonical/fork/hooks/check_story_determinism.sh +2 -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 +15 -0
- package/ds-canonical/fork/preamble.md +20 -18
- package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +3 -3
- package/ds-canonical/fork/skills/code-quality-audit/SKILL.md +3 -3
- package/ds-canonical/fork/skills/delivery-handoff/SKILL.md +1 -1
- 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 +2 -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_codex_brief_invariants.sh +28 -1
- package/ds-canonical/hooks/check_consumer_app_invariants.sh +145 -13
- package/ds-canonical/hooks/check_datatable_invariants.sh +6 -3
- package/ds-canonical/hooks/check_ds_anchor_preflight.sh +2 -1
- 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 +28 -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_ssot_header_declaration.sh +3 -1
- package/ds-canonical/hooks/check_story_determinism.sh +2 -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_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_opacity_token_usage.sh +39 -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/story-rules.md +2 -2
- package/ds-canonical/rules/ui-development.md +2 -2
- package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +3 -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 +51 -58
- 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 +1 -1
- package/ds-canonical/skills/design-system-audit/SKILL.md +26 -25
- 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 +1 -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 +5 -5
- package/ds-canonical/skills/new-component/references/new-component-checklist.md +1 -1
- 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 +2 -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 +13 -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-group.tsx +231 -0
- package/src/components/DataTable/data-table-filter-panel.tsx +16 -430
- package/src/components/DataTable/data-table-filter-value-picker.tsx +237 -0
- 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 +33 -13
- package/src/components/DataTable/data-table.stories.tsx +16 -17
- package/src/components/DataTable/data-table.tsx +107 -61
- package/src/components/DataTable/filter-operators.spec.md +3 -3
- 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/Field/index.ts +1 -0
- 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 +5 -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 +29 -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 +588 -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 +4 -4
- 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.css +1 -1
- 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,8 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: Input
|
|
3
3
|
family: 4
|
|
4
|
-
variants:
|
|
5
|
-
|
|
4
|
+
variants:
|
|
5
|
+
default: { when: "Field wrapper 完整 chrome(bg-surface + border + hover/focus 回饋)— 表單 / Field 內嵌" }
|
|
6
|
+
sizes:
|
|
7
|
+
sm: { when: "field-height 28、icon 16、text-body" }
|
|
8
|
+
md: { when: "default 一般表單(field-height 32、icon 16)" } # ★ default
|
|
9
|
+
lg: { when: "touch / 大字級(field-height 36、icon 20、text-body-lg)" }
|
|
6
10
|
traits:
|
|
7
11
|
- hasInteractiveStates
|
|
8
12
|
- isInputLike
|
|
@@ -92,7 +96,7 @@ endAction(host 內嵌 inline action)vs 獨立 `<Button iconOnly>` 的分界詳 `
|
|
|
92
96
|
|
|
93
97
|
`<Input mode="display">` 是 identity 顯示:
|
|
94
98
|
- 有值:原樣輸出
|
|
95
|
-
- null / undefined /
|
|
99
|
+
- null / undefined / 空字串:半形 `-`(hyphen),`text-foreground`(display/readonly 供檢視值同色;disabled → fg-disabled)
|
|
96
100
|
|
|
97
101
|
---
|
|
98
102
|
|
|
@@ -125,26 +129,17 @@ endAction(host 內嵌 inline action)vs 獨立 `<Button iconOnly>` 的分界詳 `
|
|
|
125
129
|
|
|
126
130
|
## Variant(visual chrome,正交於 mode)
|
|
127
131
|
|
|
128
|
-
Input
|
|
132
|
+
Input 有**一個公開** visual chrome variant `default`(+ 一個 `@internal` 的 `naked`),**獨立於 mode**(mode 是 state,variant 是 chrome look):
|
|
129
133
|
|
|
130
134
|
| Variant | 原則 | 適用場景 | 世界級對照 |
|
|
131
135
|
|---------|------|---------|-----------|
|
|
132
|
-
| `'default'`(預設) | 完整 field chrome(背景 + 常態 border + hover/focus 回饋)——明確邀請輸入 | 表單 / Field 內嵌 / 標準 edit UI | Material Input / Ant Input default |
|
|
133
|
-
| `'bare'` | 常態無 chrome,hover / focus 才現 border;sizing(padding / typography / height)不變 | **Toolbar inline editing**(FileViewer zoom input / chart config / rich text toolbar number input / Sidebar inline rename) | VS Code settings input / Figma toolbar number / Notion prop input |
|
|
136
|
+
| `'default'`(預設) | 完整 field chrome(背景 + 常態 border + hover/focus 回饋)——明確邀請輸入 | 表單 / Field 內嵌 / 標準 edit UI / toolbar 內小尺寸輸入(如 FileViewer zoom,搭 `size="sm" autoWidth`) | [Material Input](https://github.com/material-components/material-web/blob/main/docs/components/text-field.md) / [Ant Input](https://github.com/ant-design/ant-design/blob/master/components/input/index.en-US.md) default |
|
|
134
137
|
|
|
135
|
-
|
|
138
|
+
**透傳**:在 `<Field variant="default">` 內自動繼承 context variant,per-prop override(code:`useResolvedFieldVariant`)。
|
|
136
139
|
|
|
137
|
-
|
|
140
|
+
> **內部 variant `naked`(@internal,非公開)**:`FieldVariant` 型別含第二值 `'naked'`,但**不是公開 Input variant**——單獨使用無視覺邊界,**不可 standalone**。naked = 剝除 chrome(無 padding / bg / rounded),但 **edit 態仍由自身 border-based state machine 渲染 border + focus 藍框**(field-wrapper.tsx edit×naked compound = cell-as-input state machine;非「完全無 border」、亦非「host cell 自管」);正被 `FieldSurfaceContext='table-cell'` 取代。consumer 只用 `default`。對齊 public-vs-internal canonical(`ui-development.md`:能直接被使用且用在合理地方才公開)。
|
|
138
141
|
|
|
139
|
-
>
|
|
140
|
-
|
|
141
|
-
**`bare` 使用情境的 canonical 要求**:
|
|
142
|
-
- 外層 chrome 必須已提供「這是可編輯」的 affordance(Toolbar 的 icon / prop label / row structure);否則 user 看不到 input chrome 找不到可編輯位置
|
|
143
|
-
- 保留 DS field-height(`h-field-sm/md/lg`)、typography、icon tier、error 視覺——**bare 只動 chrome 不動 sizing**
|
|
144
|
-
|
|
145
|
-
**禁止**:
|
|
146
|
-
- ❌ 在**表單** context 用 `bare`——表單需要明確的 field chrome 邀請輸入,bare 會失去 affordance
|
|
147
|
-
- ❌ 拿 `bare` 來當「空白 div 替代品」——variant 不是拿掉視覺的工具,而是特定 chrome context 的 canonical
|
|
142
|
+
> **`bare` variant 退役(2026-07-09,user 拍板)**:原「透明外殼、hover/focus 才顯 border」variant,宣稱用途 = toolbar inline editing,但全 repo(DS + apps + stories)**零真實 JSX 消費者**——FileViewer zoom 實為 `<Input size="sm" autoWidth>`(default variant),非 bare;唯一出現處是一個 ❌ 反例 story + 過時註解。toolbar 小輸入用 `default`(對齊「邊框給互動元素、輸入框就該有邊框」原則),cell-as-input 用 `naked` → `bare` 純冗餘,移除。Benchmark:[Ant Input](https://github.com/ant-design/ant-design/blob/master/components/input/index.en-US.md) 有 `variant="borderless"`(概念合法),但 [Material M3](https://github.com/material-components/material-web/blob/main/docs/components/text-field.md) 只有 filled/outlined、無無邊框變體;我們無真實消費場景 → 不留純預留 variant。詳 `field-types.ts` FieldVariant note。
|
|
148
143
|
|
|
149
144
|
---
|
|
150
145
|
|
|
@@ -166,7 +161,7 @@ Input 有兩個 visual chrome variant,**獨立於 mode**(mode 是 state,variant
|
|
|
166
161
|
|
|
167
162
|
**禁止**:
|
|
168
163
|
- ❌ **表單 Field 內**——Field 欄位必須欄寬對齊,寬度隨值跳動會破壞 grid layout
|
|
169
|
-
- ❌
|
|
164
|
+
- ❌ **放在主表單欄位區**——auto-width 讓欄寬隨值跳動、破壞 grid 對齊;autoWidth 適用 toolbar / page-body inline 場景,搭 `variant="default"`(小尺寸有框輸入,如 FileViewer zoom;`bare` 2026-07-09 退役)
|
|
170
165
|
|
|
171
166
|
**世界級對照**:VS Code settings inline input 用同 pattern;Notion property field、Airtable cell edit 皆 auto-size。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
|
|
172
167
|
|
|
@@ -3,9 +3,10 @@ import * as React from 'react'
|
|
|
3
3
|
import { type VariantProps } from 'class-variance-authority'
|
|
4
4
|
import type { LucideIcon } from 'lucide-react'
|
|
5
5
|
import { cn } from '@/lib/utils'
|
|
6
|
-
import type { FieldMode, FieldVariant } from '@/design-system/components/Field/field-types'
|
|
7
|
-
import { fieldWrapperStyles, bareInputStyles
|
|
8
|
-
import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid } from '@/design-system/components/Field/field-context'
|
|
6
|
+
import type { FieldMode, FieldVariant, FieldVariantInternal } from '@/design-system/components/Field/field-types'
|
|
7
|
+
import { fieldWrapperStyles, bareInputStyles } from '@/design-system/components/Field/field-wrapper'
|
|
8
|
+
import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid, useFieldEmptyDisplay, fieldEmptyColorClass } from '@/design-system/components/Field/field-context'
|
|
9
|
+
import { useControllable } from '@/design-system/hooks/use-controllable'
|
|
9
10
|
import { ItemInlineAction, ItemPrefix, type InlineActionConfig } from '@/design-system/patterns/element-anatomy/item-anatomy'
|
|
10
11
|
import { CircularProgress } from '@/design-system/components/CircularProgress/circular-progress'
|
|
11
12
|
import { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'
|
|
@@ -14,19 +15,17 @@ import { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'
|
|
|
14
15
|
|
|
15
16
|
export interface InputProps
|
|
16
17
|
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'>,
|
|
17
|
-
Omit<VariantProps<typeof fieldWrapperStyles>, 'mode' | 'variant'> {
|
|
18
|
+
Omit<VariantProps<typeof fieldWrapperStyles>, 'mode' | 'variant' | 'width'> {
|
|
18
19
|
/** Field display mode */
|
|
19
20
|
mode?: FieldMode
|
|
20
21
|
/**
|
|
21
|
-
* Visual
|
|
22
|
-
* 公開 variant
|
|
22
|
+
* Visual variant(正交於 mode),對齊 FieldContext.variant 透傳。
|
|
23
|
+
* 公開 variant 一個(2026-07-09 `bare` 退役,詳 field-types.ts FieldVariant note;naked 2026-07-14 型別收窄至 FieldVariantInternal):
|
|
23
24
|
* - `'default'`(預設)— Field wrapper 完整 variant:bg-surface + 明顯 border + hover/focus 回饋。適用表單、Field 內嵌。
|
|
24
|
-
* - `'bare'` — 透明 variant,hover / focus 才出現 border。適用 Toolbar inline editing(如 FileViewer zoom input / chart config toolbar / rich text toolbar number input)。保留 padding / typography / height,只拿掉背景和常態 border。
|
|
25
25
|
*
|
|
26
|
-
* @internal `'naked'` — 完全無 chrome / 無 border / 無 focus ring。單獨使用無視覺邊界,**不可直接 standalone 用**;僅供 DS 內部 cell-as-input 組合(
|
|
26
|
+
* @internal `'naked'` — 完全無 chrome / 無 border / 無 focus ring。單獨使用無視覺邊界,**不可直接 standalone 用**;僅供 DS 內部 cell-as-input 組合(edit×naked 的 border/hover/focus/error 由 field-wrapper 自渲 state machine 提供,非 host cell 自管;詳 field-wrapper.tsx)。consumer 請用 `default`(2026-07-14 起型別層已擋:公開 FieldVariant 不含 naked,僅 DataTable cell-registry 經 WithFieldVariantInternal 通道傳入)。
|
|
27
27
|
*
|
|
28
|
-
* 透傳:在 `<Field variant="default
|
|
29
|
-
* 世界級對照(bare):VS Code settings input / Figma toolbar number / Notion prop input。
|
|
28
|
+
* 透傳:在 `<Field variant="default">` 內自動繼承 context.variant;per-prop override context。
|
|
30
29
|
*/
|
|
31
30
|
variant?: FieldVariant
|
|
32
31
|
/** Error 狀態(正交於 mode)。border-error + aria-invalid。 */
|
|
@@ -63,7 +62,7 @@ export interface InputProps
|
|
|
63
62
|
* **不要用在**:表單 Field(Field 需要欄寬對齊,不該隨值跳動)
|
|
64
63
|
*
|
|
65
64
|
* **fallback**:不支援 `field-sizing` 的瀏覽器會退化為 `w-auto`(wrapper 縮到 content 尺寸,
|
|
66
|
-
* input
|
|
65
|
+
* input 為 min-w-0,依 content/placeholder 撐寬)。UX 上稍不一致但不致斷;若必須精準對齊所有瀏覽器,
|
|
67
66
|
* consumer 可自行傳 `style={{ width: ... }}` 顯式寬度,不走 auto。
|
|
68
67
|
*/
|
|
69
68
|
autoWidth?: boolean
|
|
@@ -88,6 +87,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
88
87
|
disabled: disabledProp,
|
|
89
88
|
readOnly,
|
|
90
89
|
value,
|
|
90
|
+
defaultValue,
|
|
91
91
|
id: idProp,
|
|
92
92
|
'aria-describedby': ariaDescribedByProp,
|
|
93
93
|
'aria-errormessage': ariaErrorMessageProp,
|
|
@@ -102,10 +102,11 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
102
102
|
const size = useResolvedFieldSize(sizeProp)
|
|
103
103
|
const disabled = useResolvedFieldDisabled(disabledProp)
|
|
104
104
|
// chrome 透傳:per-prop override context;context 沒值則 'default'
|
|
105
|
-
const variant:
|
|
105
|
+
const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)
|
|
106
106
|
// 2026-06-08 SSOT:mode 經 useResolvedFieldMode 統一解析(prop > 有效 disabled > fieldCtx.mode > readOnly > 'edit')。
|
|
107
107
|
// loading 期間 input 保持可編輯(Ant Input.Search 派),只用 aria-busy + endAction Spinner,不動 mode。
|
|
108
108
|
const resolvedMode: FieldMode = useResolvedFieldMode({ mode: modeProp, disabled, readOnly })
|
|
109
|
+
const emptyDisplay = useFieldEmptyDisplay()
|
|
109
110
|
const isEditable = resolvedMode === 'edit'
|
|
110
111
|
const isDisplay = resolvedMode === 'display'
|
|
111
112
|
// error 合併:自身 error prop OR Field context invalid
|
|
@@ -114,18 +115,59 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
114
115
|
const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']
|
|
115
116
|
const iconColor = resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-fg-muted'
|
|
116
117
|
|
|
117
|
-
// ── display mode
|
|
118
|
+
// ── display mode(+ readonly 空值)純展示,渲染 <span> 取代 <input> ──
|
|
118
119
|
// 對齊 Carbon read-only / PatternFly inline-edit hidden-input / Cloudscape display-mode
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
// 2026-07-08 user 拍板:readonly 空值也走 display-span 顯 '-'(readonly **有值** 仍走下方
|
|
121
|
+
// native <input readOnly> 保留選取/複製語意 — field-controls.spec.md「null / undefined 值」)。
|
|
122
|
+
// 2026-07-14 audit Dim 26(dual-mode coherence):uncontrolled `defaultValue` 也要被
|
|
123
|
+
// display / readonly-空值判定認得 — 原本只讀 `value`,uncontrolled Input 切 display 會誤顯 '-'。
|
|
124
|
+
// 2026-07-14 R2(dual-model consensus,取代同日稍早 onChange-mirror 版):resolved value 改走
|
|
125
|
+
// useControllable 內部 SSOT(Radix idiom,同 Select / Pagination 既有消費)。uncontrolled 時
|
|
126
|
+
// native input 由 value={resolved} 內部驅動(defaultValue 只作初始值,不落 DOM attribute),
|
|
127
|
+
// 修 mirror 版兩個真缺陷:
|
|
128
|
+
// (1) remount-stale — 切 display / readonly(native input unmount)再回 edit,native input 從
|
|
129
|
+
// defaultValue attribute 重掛 → 顯示 stale 初始值,與 mirror 判定分歧;
|
|
130
|
+
// (2) form.reset() stale — HTML 標準 reset 不發 input event,onChange mirror 停在 reset 前的值
|
|
131
|
+
// (下方 reset bridge 修)。
|
|
132
|
+
// controlled(傳 value)時 useControllable 純 passthrough,行為與原本完全一致。
|
|
133
|
+
// consumer onChange 不接進 hook(保留 native event signature),於 <input> onChange 另行轉發。
|
|
134
|
+
const isControlled = value !== undefined
|
|
135
|
+
const [resolved, setResolved] = useControllable<string | number | readonly string[]>({
|
|
136
|
+
value: value as string | number | readonly string[] | undefined,
|
|
137
|
+
defaultValue: defaultValue ?? '',
|
|
138
|
+
})
|
|
139
|
+
const displayValue = resolved != null && resolved !== '' ? String(resolved) : null
|
|
140
|
+
const showDisplaySpan = isDisplay || (resolvedMode === 'readonly' && displayValue == null)
|
|
141
|
+
|
|
142
|
+
// form.reset() bridge(uncontrolled only):reset 恢復 defaultValue 但不發 input event →
|
|
143
|
+
// 手動把 resolved 歸位 defaultValue。keyed on showDisplaySpan:display 分支不掛 native input,
|
|
144
|
+
// 回 edit 重掛時 effect 重跑補掛 listener。
|
|
145
|
+
const innerRef = React.useRef<HTMLInputElement | null>(null)
|
|
146
|
+
React.useEffect(() => {
|
|
147
|
+
if (isControlled || showDisplaySpan) return
|
|
148
|
+
const form = innerRef.current?.form
|
|
149
|
+
if (!form) return
|
|
150
|
+
const handleReset = () => setResolved(defaultValue ?? '')
|
|
151
|
+
form.addEventListener('reset', handleReset)
|
|
152
|
+
return () => form.removeEventListener('reset', handleReset)
|
|
153
|
+
}, [isControlled, showDisplaySpan, defaultValue, setResolved])
|
|
154
|
+
// Merge refs(LinkInput setRef idiom,link-input.tsx:138)
|
|
155
|
+
const setRef = React.useCallback((el: HTMLInputElement | null) => {
|
|
156
|
+
innerRef.current = el
|
|
157
|
+
if (typeof ref === 'function') ref(el)
|
|
158
|
+
else if (ref) (ref as React.MutableRefObject<HTMLInputElement | null>).current = el
|
|
159
|
+
}, [ref])
|
|
160
|
+
|
|
161
|
+
if (showDisplaySpan) {
|
|
162
|
+
const spanMode = isDisplay ? 'display' : 'readonly'
|
|
121
163
|
return (
|
|
122
164
|
<div
|
|
123
165
|
className={cn(
|
|
124
|
-
fieldWrapperStyles({ mode:
|
|
166
|
+
fieldWrapperStyles({ mode: spanMode, variant: variant, size }),
|
|
125
167
|
autoWidth && 'inline-flex w-auto',
|
|
126
168
|
className,
|
|
127
169
|
)}
|
|
128
|
-
data-field-mode=
|
|
170
|
+
data-field-mode={spanMode}
|
|
129
171
|
>
|
|
130
172
|
{StartIcon && (
|
|
131
173
|
<ItemPrefix>
|
|
@@ -143,10 +185,10 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
143
185
|
// cell display canonical:single-line value 過長 → ellipsis。Textarea display 走 wrap path,
|
|
144
186
|
// 不在此處;Input display 永遠 single-line。)
|
|
145
187
|
'truncate',
|
|
146
|
-
displayValue == null &&
|
|
188
|
+
displayValue == null && fieldEmptyColorClass(resolvedMode),
|
|
147
189
|
)}
|
|
148
190
|
>
|
|
149
|
-
{displayValue ??
|
|
191
|
+
{displayValue ?? emptyDisplay}
|
|
150
192
|
</span>
|
|
151
193
|
</div>
|
|
152
194
|
)
|
|
@@ -174,10 +216,10 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
174
216
|
</ItemPrefix>
|
|
175
217
|
)}
|
|
176
218
|
<input
|
|
177
|
-
ref={
|
|
219
|
+
ref={setRef}
|
|
178
220
|
type="text"
|
|
179
221
|
id={idProp ?? fieldCtx?.id}
|
|
180
|
-
value={
|
|
222
|
+
value={resolved}
|
|
181
223
|
readOnly={resolvedMode === 'readonly'}
|
|
182
224
|
disabled={resolvedMode === 'disabled'}
|
|
183
225
|
aria-invalid={resolvedError || undefined}
|
|
@@ -192,6 +234,11 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
192
234
|
autoWidth && '[field-sizing:content] w-auto min-w-0',
|
|
193
235
|
)}
|
|
194
236
|
{...props}
|
|
237
|
+
// 置於 {...props} 後:寫回 resolved SSOT(controlled 時 useControllable 內部為 no-op)再轉發 consumer onChange。
|
|
238
|
+
onChange={(e) => {
|
|
239
|
+
setResolved(e.target.value)
|
|
240
|
+
props.onChange?.(e)
|
|
241
|
+
}}
|
|
195
242
|
/>
|
|
196
243
|
{loading ? (
|
|
197
244
|
<CircularProgress size={iconSize} className="shrink-0" />
|
|
@@ -219,7 +266,7 @@ export const inputMeta = {
|
|
|
219
266
|
family: 4,
|
|
220
267
|
variants: {
|
|
221
268
|
default: { purpose: 'Field wrapper 完整 chrome(bg-surface + border + hover/focus 回饋)— 表單 / Field 內嵌' },
|
|
222
|
-
|
|
269
|
+
// (2026-07-09 `bare` variant 退役 — 見 field-types.ts FieldVariant note)
|
|
223
270
|
},
|
|
224
271
|
sizes: {
|
|
225
272
|
sm: { fieldHeight: 28, iconSize: 16, typography: 'body' },
|
|
@@ -257,7 +257,7 @@ export const Overview = {
|
|
|
257
257
|
<tbody>
|
|
258
258
|
{[
|
|
259
259
|
['mode', "'edit' | 'display' | 'readonly' | 'disabled'", "'edit'", 'FieldMode 四模式'],
|
|
260
|
-
['variant', "'default'
|
|
260
|
+
['variant', "'default'", "'default'", 'Field chrome variant(naked:cell-as-input 透明 chrome,@internal,2026-07-14 型別收窄至 FieldVariantInternal)'],
|
|
261
261
|
['size', "'sm' | 'md' | 'lg'", "'md'", '尺寸,與 Button 共用 token'],
|
|
262
262
|
['error', 'boolean', 'false', '錯誤狀態,紅色邊框 + aria-invalid'],
|
|
263
263
|
['value', 'string | null', '—', 'URL 值'],
|
|
@@ -172,8 +172,8 @@ export const EmptyRule: Story = {
|
|
|
172
172
|
</Rule>
|
|
173
173
|
|
|
174
174
|
<Rule
|
|
175
|
-
title="readonly 空值 →
|
|
176
|
-
note="table cell / 個人資料展示頁,沒填 website
|
|
175
|
+
title="readonly 空值 → 顯示半形連字號(-)"
|
|
176
|
+
note="table cell / 個人資料展示頁,沒填 website 時以半形連字號 - 表達「未填」,跟其他 Field readonly 空值呈現一致"
|
|
177
177
|
>
|
|
178
178
|
<LinkInput mode="readonly" value={null} />
|
|
179
179
|
</Rule>
|
|
@@ -2,9 +2,9 @@ import * as React from 'react'
|
|
|
2
2
|
import { Pencil } from 'lucide-react'
|
|
3
3
|
import type { VariantProps } from 'class-variance-authority'
|
|
4
4
|
import { cn } from '@/lib/utils'
|
|
5
|
-
import type { FieldMode, FieldVariant } from '@/design-system/components/Field/field-types'
|
|
6
|
-
import { fieldWrapperStyles, bareInputStyles,
|
|
7
|
-
import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid } from '@/design-system/components/Field/field-context'
|
|
5
|
+
import type { FieldMode, FieldVariant, FieldVariantInternal } from '@/design-system/components/Field/field-types'
|
|
6
|
+
import { fieldWrapperStyles, bareInputStyles, fieldDisplayTextClass } from '@/design-system/components/Field/field-wrapper'
|
|
7
|
+
import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid, useFieldEmptyDisplay, fieldEmptyColorClass } from '@/design-system/components/Field/field-context'
|
|
8
8
|
import { ItemInlineAction } from '@/design-system/patterns/element-anatomy/item-anatomy'
|
|
9
9
|
|
|
10
10
|
// ── URL Validation ──────────────────────────────────────────────────────────
|
|
@@ -52,12 +52,12 @@ export interface LinkInputProps
|
|
|
52
52
|
// defaultValue」宣稱的型別面機械封鎖 — 原本仍在型別 surface 且經 {...props} spread 到已
|
|
53
53
|
// controlled 的 input(React dev warning)。
|
|
54
54
|
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'value' | 'defaultValue' | 'onChange'>,
|
|
55
|
-
Omit<VariantProps<typeof fieldWrapperStyles>, 'mode' | 'variant'> {
|
|
55
|
+
Omit<VariantProps<typeof fieldWrapperStyles>, 'mode' | 'variant' | 'width'> {
|
|
56
56
|
mode?: FieldMode
|
|
57
57
|
/**
|
|
58
58
|
* Visual chrome(2026-05-05 Phase B3)。對齊 FieldContext.variant 透傳。
|
|
59
59
|
* - `'default'`(預設)— Field wrapper 完整 chrome(form / Field 內嵌)。
|
|
60
|
-
* - `
|
|
60
|
+
* (2026-07-09 `bare` variant 退役;naked = cell-as-input substrate,@internal)
|
|
61
61
|
*
|
|
62
62
|
* mode='display' 時 chrome 無視覺意義(display 完全無 wrapper);chrome 僅作用於 edit / readonly / disabled。
|
|
63
63
|
*/
|
|
@@ -109,9 +109,10 @@ const LinkInput = React.forwardRef<HTMLInputElement, LinkInputProps>(
|
|
|
109
109
|
// 2026-06-08 SSOT:mode 經 useResolvedFieldMode 統一解析(prop > 有效 disabled > fieldCtx.mode > readOnly > 'edit')。
|
|
110
110
|
// spec field-controls.spec.md L125「readOnly 原生屬性自動覆蓋 mode」契約落地。
|
|
111
111
|
const resolvedMode: FieldMode = useResolvedFieldMode({ mode: modeProp, disabled, readOnly })
|
|
112
|
+
const emptyDisplay = useFieldEmptyDisplay()
|
|
112
113
|
const isEditable = resolvedMode === 'edit'
|
|
113
114
|
// chrome resolution:per-prop > context > 'default'
|
|
114
|
-
const resolvedVariant:
|
|
115
|
+
const resolvedVariant: FieldVariantInternal = useResolvedFieldVariant(variantProp)
|
|
115
116
|
|
|
116
117
|
// 2026-07-05 D4:invalid 經 useResolvedFieldInvalid 統一接 fieldCtx.invalid(field-controls.spec.md
|
|
117
118
|
// resolver 契約「error/invalid = prop OR fieldCtx.invalid」)— 原本紅框只吃 errorProp||localError、
|
|
@@ -156,7 +157,7 @@ const LinkInput = React.forwardRef<HTMLInputElement, LinkInputProps>(
|
|
|
156
157
|
// anchor / span 必套 `fieldDisplayTextClass(size)`(sm/md→text-body,lg→text-body-lg)
|
|
157
158
|
// — 對齊跨 Field family display 視覺尺寸統一。原無 font-size class → 用 browser default
|
|
158
159
|
// 字體 → 跟其他 Field display 不一致(user 抓 I2)。truncate 同需,長 URL ellipsis(I1)。
|
|
159
|
-
if (!value) return <span className={cn(fieldDisplayTextClass(size), '
|
|
160
|
+
if (!value) return <span className={cn(fieldDisplayTextClass(size), fieldEmptyColorClass(resolvedMode), 'block truncate')}>{emptyDisplay}</span>
|
|
160
161
|
return <span className={cn(fieldDisplayTextClass(size), 'block truncate')}>{renderLinkAnchor(value, label)}</span>
|
|
161
162
|
}
|
|
162
163
|
return (
|
|
@@ -167,7 +168,7 @@ const LinkInput = React.forwardRef<HTMLInputElement, LinkInputProps>(
|
|
|
167
168
|
<span className="flex-1 min-w-0 truncate">
|
|
168
169
|
{value
|
|
169
170
|
? renderLinkAnchor(value, label)
|
|
170
|
-
: <span className=
|
|
171
|
+
: <span className={fieldEmptyColorClass(resolvedMode)}>{emptyDisplay}</span>
|
|
171
172
|
}
|
|
172
173
|
</span>
|
|
173
174
|
</div>
|
|
@@ -232,10 +233,10 @@ const LinkInput = React.forwardRef<HTMLInputElement, LinkInputProps>(
|
|
|
232
233
|
{resolvedMode === 'disabled'
|
|
233
234
|
? (displayText
|
|
234
235
|
? <span className="text-fg-disabled">{displayText}</span>
|
|
235
|
-
: <span className=
|
|
236
|
+
: <span className={fieldEmptyColorClass(resolvedMode)}>{emptyDisplay}</span>)
|
|
236
237
|
: (value
|
|
237
238
|
? renderLinkAnchor(value, label)
|
|
238
|
-
: <span className=
|
|
239
|
+
: <span className={fieldEmptyColorClass(resolvedMode)}>{emptyDisplay}</span>)
|
|
239
240
|
}
|
|
240
241
|
</span>
|
|
241
242
|
</div>
|
|
@@ -551,7 +551,7 @@ export const ColorMatrix = {
|
|
|
551
551
|
<div className="flex flex-col gap-8">
|
|
552
552
|
<div className="flex flex-col gap-1">
|
|
553
553
|
<H3>State 色彩對照</H3>
|
|
554
|
-
<Desc>單選用 bg-neutral-selected 背景指示選中,多選用 Checkbox 勾選——背景不變。disabled
|
|
554
|
+
<Desc>單選用 bg-neutral-selected 背景指示選中,多選用 Checkbox 勾選——背景不變。disabled 時文字 / startIcon 用 fg-disabled、tag / endContent / startContent 用 opacity-disabled、checkbox 走自身 disabled、avatar 不變暗。色塊即時渲染,切 dark mode 自動更新。</Desc>
|
|
555
555
|
</div>
|
|
556
556
|
|
|
557
557
|
{/* Single-select */}
|
|
@@ -745,10 +745,10 @@ export const SizeMatrix = {
|
|
|
745
745
|
<MenuItem size={sz} startIcon={Mail} description="每日摘要信件">
|
|
746
746
|
電子郵件通知
|
|
747
747
|
</MenuItem>
|
|
748
|
-
<MenuItem size={sz} avatar={{ alt: "Alice", color: "indigo" as const }}>
|
|
748
|
+
<MenuItem size={sz} avatar={{ src: "https://i.pravatar.cc/48?u=alice-chen", alt: "Alice", color: "indigo" as const }}>
|
|
749
749
|
Alice Chen
|
|
750
750
|
</MenuItem>
|
|
751
|
-
<MenuItem size={sz} avatar={{ alt: "Bob", color: "magenta" as const }} description="工程部門">
|
|
751
|
+
<MenuItem size={sz} avatar={{ src: "https://i.pravatar.cc/48?u=bob-wang", alt: "Bob", color: "magenta" as const }} description="工程部門">
|
|
752
752
|
Bob Wang
|
|
753
753
|
</MenuItem>
|
|
754
754
|
<MenuItem size={sz} checkbox checked={true} startIcon={Bell}>
|
|
@@ -74,7 +74,7 @@ export const CompositionRules: Story = {
|
|
|
74
74
|
<h4>Pattern 3 — ContextMenu + MenuItem(右鍵選單,未來)</h4>
|
|
75
75
|
<p>右鍵觸發 contextual action → 預留消費者(Radix ContextMenu + MenuItem 同 layout primitive)。</p>
|
|
76
76
|
|
|
77
|
-
<p className="text-fg-muted">禁止:在產品頁面裡自己用 <code><div></code> 拼一列選單(像 <code><div className="flex px-2 py-1.5"></code>)—
|
|
77
|
+
<p className="text-fg-muted">禁止:在產品頁面裡自己用 <code><div></code> 拼一列選單(像 <code><div className="flex px-2 py-1.5"></code>)— 一律透過上方的 MenuItem 系選單元件(DropdownMenu / SelectMenu 等,內部消費 MenuItem),才能確保所有選單列的對齊與間距一致。MenuItem 本身是 internal 排版元件,在產品頁面直接裸用會失去外層選單的無障礙 / 鍵盤 / 焦點管理(詳 spec「何時用 / 何時不用」)。各區塊的對齊規則(左側圖示/頭像在 24px 內對齊、文字往下堆疊、右側標記可有可無)由選項列共用設計準則統一規範。</p>
|
|
78
78
|
</div>
|
|
79
79
|
</div>
|
|
80
80
|
),
|
|
@@ -28,14 +28,16 @@ MenuItem 是所有 menu 類元件的**共用視覺佈局層**——處理 prefix
|
|
|
28
28
|
## 結構
|
|
29
29
|
|
|
30
30
|
```
|
|
31
|
-
prefix
|
|
32
|
-
[checkbox?] [icon? | avatar?] [label + description?] [tag?]
|
|
31
|
+
prefix content suffix
|
|
32
|
+
[checkbox?] [icon? | avatar? | content?] [label + description?] [tag?]
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
- **Prefix**:checkbox(多選,由父層注入)+ startIcon 或 avatar
|
|
35
|
+
- **Prefix**:checkbox(多選,由父層注入)+ startIcon 或 avatar 或 startContent(三者互斥)
|
|
36
36
|
- **Content**:label + 可選 description
|
|
37
37
|
- **Suffix**:tag(ml-auto 靠右)
|
|
38
38
|
|
|
39
|
+
**`startContent`(2026-07-10 加,WM TypeIcon 彩色徽章需求)**:LucideIcon / Avatar 表達不了的自訂 leading 視覺(類型徽章 = 彩底方塊 + glyph、status 組合點等)。**渲染在同一個 prefix 對齊容器內——佔位與排列邏輯跟 startIcon 一模一樣**(gap / 24px 閾值對齊 / shrink 全繼承 `itemPrefixAlignVariants` SSOT,零平行 layout),consumer 唯一責任是節點尺寸對齊 `ICON_SIZE[size]`(sm/md=16px、lg=20px)。與 `endContent` 對稱(同為自訂 ReactNode slot)。disabled 時容器套 `opacity-disabled`(對齊 `tag` / `endContent` 自訂節點同款處理;**與 `avatar` 不變暗的分界**:Avatar 有自己的 self-dim 體系〔`fieldCtx.disabled` 觸發,同一顆 `opacity-disabled` token,avatar.spec.md「Disabled」段〕,MenuItem 非 Field context 故不觸發;startContent 是任意 consumer 節點、無任何 DS context 可自 dim → 容器套 opacity 是唯一可靠機制。放頭像請用 `avatar` prop 非 startContent)。
|
|
40
|
+
|
|
39
41
|
prefix icon 跟 label 同色(foreground),不是 fg-muted。詳見 item-anatomy.spec.md 的 Icon 色彩原則。
|
|
40
42
|
|
|
41
43
|
---
|
|
@@ -59,14 +61,19 @@ prefix icon 跟 label 同色(foreground),不是 fg-muted。詳見 item-ana
|
|
|
59
61
|
|
|
60
62
|
---
|
|
61
63
|
|
|
62
|
-
## startIcon vs avatar
|
|
64
|
+
## startIcon vs avatar vs startContent(怎麼選)
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
|
67
|
-
|
|
68
|
-
|
|
|
69
|
-
|
|
|
66
|
+
**一句話判準:看內容「是什麼」,不是看喜好** —— 線性圖示 → `startIcon`;人/實體頭像 → `avatar`;其他任何自訂視覺 → `startContent`。每槽綁不同的自動行為,型別即理由:
|
|
67
|
+
|
|
68
|
+
| | `startIcon` | `avatar` | `startContent` |
|
|
69
|
+
|---|---|---|---|
|
|
70
|
+
| 型別 | `LucideIcon` | `AvatarData` | `ReactNode` |
|
|
71
|
+
| 角色 | **描述**性質(形容詞) | **代表**身份(名詞) | 圖示/頭像表達不了的視覺(類型徽章、狀態組合點) |
|
|
72
|
+
| 尺寸 | 16/20px(**元件自動控**) | 由 description 決定:無 desc → 20/24/24px,有 desc → 32/32/40px(**元件自動推導**) | consumer 自理,對齊 `ICON_SIZE[size]` |
|
|
73
|
+
| disabled | 自動 `fg-disabled` 換色 | 不變暗(自有 fieldCtx self-dim 體系) | 容器自動 `opacity-disabled` |
|
|
74
|
+
| 對齊 | 永遠 inline(≤ 24px) | 無 desc → inline;有 desc → block | inline(同 startIcon 容器) |
|
|
75
|
+
|
|
76
|
+
**為什麼三槽而不是一個 ReactNode 口**:合成一口(Atlassian `iconBefore: ReactNode` 式)會失去前兩槽的自動行為 —— 每個 consumer 都得自己記尺寸、自己處理 disabled、自己接 hoverCard = 漂移溫床。世界級同構:MUI List 正是三分 —— [`ListItemIcon`](https://mui.com/material-ui/api/list-item-icon/) 與 [`ListItemAvatar`](https://mui.com/material-ui/api/list-item-avatar/) 是**兩個獨立 wrapper 元件**、任意節點則直接放 —— 我們用 props 表達同一套分工。
|
|
70
77
|
|
|
71
78
|
### avatar 尺寸推導
|
|
72
79
|
|
|
@@ -150,7 +157,7 @@ Menu 的設計目的是「**快速掃視多個選項挑一個**」。垂直空
|
|
|
150
157
|
| default | transparent | foreground | — |
|
|
151
158
|
| hover | `--neutral-hover` | foreground | 滑鼠 hover |
|
|
152
159
|
| selected(單選) | `--neutral-selected` | foreground | bg 高亮(不用 ✓ 勾號,避免影響 prefix 對齊) |
|
|
153
|
-
| **selected × hover(疊加)** | `--neutral-selected`(**selected 勝**) | foreground | 2026-07-04 Q2 拍板 M5 明文:bg 是唯一選中指示器,hover 不得洗掉。code = selected 時關 hover bg(menu-item.tsx)。對齊 Ant Menu `:not(-item-selected)` / VS Code list `:hover:not(.selected)
|
|
160
|
+
| **selected × hover(疊加)** | `--neutral-selected`(**selected 勝**) | foreground | 2026-07-04 Q2 拍板 M5 明文:bg 是唯一選中指示器,hover 不得洗掉。code = selected 時關 hover bg(menu-item.tsx)。對齊 Ant Menu `:not(-item-selected)` / VS Code list `:hover:not(.selected)`。**消費者差異**:DropdownMenu 在 selected × highlighted(鍵盤 cursor)深化一階為 `--neutral-selected-active`(2026-07-05 D4,WCAG 2.4.7 cursor 可見;dropdown-menu.tsx `selected && data-[highlighted]:bg-neutral-selected-active`)——此為 wrapper 層鍵盤 cursor 增強,MenuItem primitive 的 selected × hover(滑鼠)仍維持 `--neutral-selected` 不深化 |
|
|
154
161
|
| selected(多選) | transparent | foreground | checkbox 勾選 |
|
|
155
162
|
| disabled | transparent | `--fg-disabled` | disabled prop |
|
|
156
163
|
|
|
@@ -163,7 +170,7 @@ Menu 的設計目的是「**快速掃視多個選項挑一個**」。垂直空
|
|
|
163
170
|
|
|
164
171
|
單選和 DropdownMenu RadioItem 統一用 `bg-neutral-selected`。不用 ✓ 勾號——勾號在 prefix 會影響群組內對齊。
|
|
165
172
|
|
|
166
|
-
disabled 時:文字(label / description)與 `startIcon` 套 `fg-disabled`;`checkbox` 套用自身 `disabled` 樣式(非 `fg-disabled`);`tag` / `endContent` 容器套 `opacity-disabled
|
|
173
|
+
disabled 時:文字(label / description)與 `startIcon` 套 `fg-disabled`;`checkbox` 套用自身 `disabled` 樣式(非 `fg-disabled`);`tag` / `endContent` / `startContent` 容器套 `opacity-disabled`(自訂節點自帶配色、不能硬換字色,降透明是唯一通用機制;MUI 佐證:disabled MenuItem 整列 root 直接套 `opacity: disabledOpacity` 連 avatar 都 dim — 我們比它細粒度);`avatar` 在 MenuItem 內不變暗(Avatar 僅在 disabled Field wrapper context 內自 dim,MenuItem 非 Field wrapper)。
|
|
167
174
|
|
|
168
175
|
### 浮層關閉行為
|
|
169
176
|
|
|
@@ -206,10 +213,10 @@ Menu item 的 prefix icon 跟 label 同色(foreground),不是 fg-muted。P
|
|
|
206
213
|
|
|
207
214
|
## 禁止事項
|
|
208
215
|
|
|
209
|
-
- ❌ `startIcon`
|
|
216
|
+
- ❌ `startIcon` / `avatar` / `startContent` 三者不可同時使用(同一 prefix 位置)
|
|
210
217
|
- ❌ 無 `description` 時不可使用 > 24px 的 avatar
|
|
211
218
|
- ❌ disabled item 不可有 hover 效果
|
|
212
|
-
- ❌ disabled item 內的子元件不可保持 enabled 外觀——文字 / `startIcon` 套 `fg-disabled`、`checkbox` 用自身 `disabled` 樣式、`tag` / `endContent` 套 `opacity-disabled`(`avatar` 在 MenuItem 內維持原樣,因 Avatar 僅在 disabled Field wrapper context 內自 dim)
|
|
219
|
+
- ❌ disabled item 內的子元件不可保持 enabled 外觀——文字 / `startIcon` 套 `fg-disabled`、`checkbox` 用自身 `disabled` 樣式、`tag` / `endContent` / `startContent` 套 `opacity-disabled`(`avatar` 在 MenuItem 內維持原樣,因 Avatar 僅在 disabled Field wrapper context 內自 dim)
|
|
213
220
|
- ❌ header item 不可被選中
|
|
214
221
|
- ❌ 不在 item 內放獨立互動元素(如 Button)——item 本身就是互動單位
|
|
215
222
|
|
|
@@ -217,7 +224,7 @@ Menu item 的 prefix icon 跟 label 同色(foreground),不是 fg-muted。P
|
|
|
217
224
|
|
|
218
225
|
## 何時用 / 何時不用
|
|
219
226
|
|
|
220
|
-
**MenuItem 是 internal primitive
|
|
227
|
+
**MenuItem 是 internal primitive**——不進 root barrel front-door、不裸用;一般透過 `SelectMenu` / `DropdownMenu` 等外層 menu 元件消費;consumer 自建 nav row 等場景可經 per-component subpath 包裝並自行確認後使用(`.claude/rules/ui-development.md`「Root barrel front-door 排除」SSOT,internal ≠ 禁用)。
|
|
221
228
|
|
|
222
229
|
| 場景 | 正確做法 |
|
|
223
230
|
|------|---------|
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
3
3
|
import { useState } from 'react'
|
|
4
|
-
import { Mail, Star, Bell, Settings, Plus, Folder, FileText, BarChart3 } from 'lucide-react'
|
|
4
|
+
import { Mail, Star, Bell, Settings, Plus, Folder, FileText, BarChart3, Bug, Rocket } from 'lucide-react'
|
|
5
5
|
import { MenuItem, MenuGroup, MenuFooter } from './menu-item'
|
|
6
|
+
import { CAT_SOLID, type CategoricalHue } from '@/design-system/tokens/categorical-color'
|
|
6
7
|
import { ProfileCard, ProfileCardDefaultActions } from '@/design-system/components/ProfileCard/profile-card'
|
|
7
8
|
|
|
8
9
|
// DS-wide canonical:person avatar 必 hover → ProfileCard(含 status / statusMessage / fields
|
|
9
10
|
// / actions / onViewMore,profile-card.spec.md 重要資訊)。demo helper:
|
|
11
|
+
// 真實人像(2026-07-08 sweep):固定 u= key 保 determinism(對齊 avatar.stories.tsx pravatar 慣例)
|
|
12
|
+
const avatarSrc = (name: string, px = 96) => `https://i.pravatar.cc/${px}?u=${name.toLowerCase().replace(/\s+/g, '-')}`
|
|
10
13
|
const personHover = (name: string, subtitle?: string) => (
|
|
11
14
|
<ProfileCard
|
|
12
15
|
name={name}
|
|
13
16
|
subtitle={subtitle ?? 'Design|D-0042|EMP-1001'}
|
|
14
|
-
avatar={{ alt: name }}
|
|
17
|
+
avatar={{ src: avatarSrc(name), alt: name }}
|
|
15
18
|
status="online"
|
|
16
19
|
statusMessage="Out of Office: Back on Monday!"
|
|
17
20
|
actions={<ProfileCardDefaultActions />}
|
|
@@ -77,9 +80,9 @@ export const AvatarInline: Story = {
|
|
|
77
80
|
name: '頭像',
|
|
78
81
|
render: () => (
|
|
79
82
|
<MenuContainer><MenuGroup>
|
|
80
|
-
<MenuItem avatar={{ alt: "Alice Chen", color: "indigo", hoverCard: personHover('Alice Chen') }}>Alice Chen</MenuItem>
|
|
81
|
-
<MenuItem avatar={{ alt: "Bob Wang", color: "magenta", hoverCard: personHover('Bob Wang') }}>Bob Wang</MenuItem>
|
|
82
|
-
<MenuItem avatar={{ alt: "Carol Lin", color: "green", hoverCard: personHover('Carol Lin') }}>Carol Lin</MenuItem>
|
|
83
|
+
<MenuItem avatar={{ src: avatarSrc('Alice Chen', 48), alt: "Alice Chen", color: "indigo", hoverCard: personHover('Alice Chen') }}>Alice Chen</MenuItem>
|
|
84
|
+
<MenuItem avatar={{ src: avatarSrc('Bob Wang', 48), alt: "Bob Wang", color: "magenta", hoverCard: personHover('Bob Wang') }}>Bob Wang</MenuItem>
|
|
85
|
+
<MenuItem avatar={{ src: avatarSrc('Carol Lin', 48), alt: "Carol Lin", color: "green", hoverCard: personHover('Carol Lin') }}>Carol Lin</MenuItem>
|
|
83
86
|
</MenuGroup></MenuContainer>
|
|
84
87
|
),
|
|
85
88
|
}
|
|
@@ -90,13 +93,38 @@ export const AvatarBlock: Story = {
|
|
|
90
93
|
name: '頭像 + 說明文字',
|
|
91
94
|
render: () => (
|
|
92
95
|
<MenuContainer><MenuGroup>
|
|
93
|
-
<MenuItem avatar={{ alt: "Alice Chen", color: "indigo", hoverCard: personHover('Alice Chen', '設計部門') }} description="設計部門">Alice Chen</MenuItem>
|
|
94
|
-
<MenuItem avatar={{ alt: "Bob Wang", color: "magenta", hoverCard: personHover('Bob Wang', '工程部門') }} description="工程部門">Bob Wang</MenuItem>
|
|
95
|
-
<MenuItem avatar={{ alt: "Carol Lin", color: "green", hoverCard: personHover('Carol Lin', '行銷部門') }} description="行銷部門">Carol Lin</MenuItem>
|
|
96
|
+
<MenuItem avatar={{ src: avatarSrc('Alice Chen', 64), alt: "Alice Chen", color: "indigo", hoverCard: personHover('Alice Chen', '設計部門') }} description="設計部門">Alice Chen</MenuItem>
|
|
97
|
+
<MenuItem avatar={{ src: avatarSrc('Bob Wang', 64), alt: "Bob Wang", color: "magenta", hoverCard: personHover('Bob Wang', '工程部門') }} description="工程部門">Bob Wang</MenuItem>
|
|
98
|
+
<MenuItem avatar={{ src: avatarSrc('Carol Lin', 64), alt: "Carol Lin", color: "green", hoverCard: personHover('Carol Lin', '行銷部門') }} description="行銷部門">Carol Lin</MenuItem>
|
|
96
99
|
</MenuGroup></MenuContainer>
|
|
97
100
|
),
|
|
98
101
|
}
|
|
99
102
|
|
|
103
|
+
// ── startContent(自訂 leading 徽章;佔位排列 = startIcon 同容器,見 menu-item.spec.md「結構」)──
|
|
104
|
+
|
|
105
|
+
/** 類型徽章 demo helper — 消費 CAT_SOLID token pair(彩底 + 深/白字由 token 決定);size-4 = ICON_SIZE sm/md */
|
|
106
|
+
const TypeBadge = ({ hue, glyph: Glyph }: { hue: CategoricalHue; glyph: typeof FileText }) => (
|
|
107
|
+
<span aria-hidden className={`inline-flex size-4 shrink-0 items-center justify-center rounded-md ${CAT_SOLID[hue]}`}>
|
|
108
|
+
<Glyph className="size-3" strokeWidth={2.5} />
|
|
109
|
+
</span>
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
export const WithStartContent: Story = {
|
|
113
|
+
name: '自訂前綴內容',
|
|
114
|
+
render: () => (
|
|
115
|
+
// 工作項類型設定的左側導覽(Jira / Meegle type settings):類型徽章 = 彩底 + glyph,
|
|
116
|
+
// LucideIcon 表達不了 → startContent;selected = 目前編輯中的類型
|
|
117
|
+
<MenuContainer width={224}>
|
|
118
|
+
<MenuGroup>
|
|
119
|
+
<MenuItem size="sm" startContent={<TypeBadge hue="indigo" glyph={FileText} />} selected>Task</MenuItem>
|
|
120
|
+
<MenuItem size="sm" startContent={<TypeBadge hue="red" glyph={Bug} />}>Bug</MenuItem>
|
|
121
|
+
<MenuItem size="sm" startContent={<TypeBadge hue="turquoise" glyph={Rocket} />}>Release</MenuItem>
|
|
122
|
+
<MenuItem size="sm" startContent={<TypeBadge hue="purple" glyph={FileText} />} disabled>Epic(已停用)</MenuItem>
|
|
123
|
+
</MenuGroup>
|
|
124
|
+
</MenuContainer>
|
|
125
|
+
),
|
|
126
|
+
}
|
|
127
|
+
|
|
100
128
|
// ── 狀態 ──
|
|
101
129
|
|
|
102
130
|
export const States: Story = {
|
|
@@ -197,9 +225,9 @@ const FullExampleDemo = () => {
|
|
|
197
225
|
<MenuContainer>
|
|
198
226
|
<MenuGroup>
|
|
199
227
|
<MenuItem header>成員</MenuItem>
|
|
200
|
-
<MenuItem checkbox checked={selected.alice} onClick={() => toggle('alice')} avatar={{ alt: "Alice", color: "indigo" }} description="設計部門">Alice Chen</MenuItem>
|
|
201
|
-
<MenuItem checkbox checked={selected.bob} onClick={() => toggle('bob')} avatar={{ alt: "Bob", color: "magenta" }} description="工程部門">Bob Wang</MenuItem>
|
|
202
|
-
<MenuItem checkbox checked={false} avatar={{ alt: "Carol", color: "green" }} description="行銷部門" disabled>Carol Lin(已離職)</MenuItem>
|
|
228
|
+
<MenuItem checkbox checked={selected.alice} onClick={() => toggle('alice')} avatar={{ src: avatarSrc('Alice Chen', 64), alt: "Alice", color: "indigo" }} description="設計部門">Alice Chen</MenuItem>
|
|
229
|
+
<MenuItem checkbox checked={selected.bob} onClick={() => toggle('bob')} avatar={{ src: avatarSrc('Bob Wang', 64), alt: "Bob", color: "magenta" }} description="工程部門">Bob Wang</MenuItem>
|
|
230
|
+
<MenuItem checkbox checked={false} avatar={{ src: avatarSrc('Carol Lin', 64), alt: "Carol", color: "green" }} description="行銷部門" disabled>Carol Lin(已離職)</MenuItem>
|
|
203
231
|
</MenuGroup>
|
|
204
232
|
<MenuFooter>
|
|
205
233
|
<MenuItem checkbox checked={allState} onClick={toggleAll}>全部</MenuItem>
|