@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,5 +1,5 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
-
import { Users, Settings, Bell, FileText } from 'lucide-react'
|
|
2
|
+
import { Users, Settings, Bell, FileText, CreditCard, Plug, Shield, KeyRound } from 'lucide-react'
|
|
3
3
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from './tabs'
|
|
4
4
|
import { Badge } from '@/design-system/components/Badge/badge'
|
|
5
5
|
import { H3, Desc, Td, Th, TokenCell } from '@/design-system/stories-helpers/anatomy/anatomy-utils'
|
|
@@ -132,7 +132,7 @@ export const Inspector: Story = {
|
|
|
132
132
|
description: '當前 selected tab',
|
|
133
133
|
},
|
|
134
134
|
withIcons: { control: 'boolean', description: 'startIcon(對應專案 / 成員 / 設定 icon)' },
|
|
135
|
-
withBadges: { control: 'boolean', description: '成員 tab 顯示 3
|
|
135
|
+
withBadges: { control: 'boolean', description: '成員 tab 顯示 3、設定顯示 12(Badge suffix)' },
|
|
136
136
|
},
|
|
137
137
|
render: (args) => {
|
|
138
138
|
const { size, overflow, value, withIcons, withBadges } = args as InspectorArgs
|
|
@@ -158,12 +158,15 @@ export const Inspector: Story = {
|
|
|
158
158
|
>
|
|
159
159
|
設定
|
|
160
160
|
</TabsTrigger>
|
|
161
|
+
{/* 2026-07-14 audit Dim 68:overflow 額外 triggers 也跟 withIcons 全有全無
|
|
162
|
+
(tabs.spec.md 禁止事項「同一組 Tabs 內 startIcon 全有或全無」— 原本前四個有
|
|
163
|
+
icon、後三個沒有,Inspector withIcons=true + overflow 時違反自家 spec)*/}
|
|
161
164
|
{(overflow === 'scroll' || overflow === 'menu') && (
|
|
162
165
|
<>
|
|
163
|
-
<TabsTrigger value="billing" startIcon={withIcons ?
|
|
164
|
-
<TabsTrigger value="integrations">整合</TabsTrigger>
|
|
165
|
-
<TabsTrigger value="security">安全性</TabsTrigger>
|
|
166
|
-
<TabsTrigger value="api">API keys</TabsTrigger>
|
|
166
|
+
<TabsTrigger value="billing" startIcon={withIcons ? CreditCard : undefined}>計費</TabsTrigger>
|
|
167
|
+
<TabsTrigger value="integrations" startIcon={withIcons ? Plug : undefined}>整合</TabsTrigger>
|
|
168
|
+
<TabsTrigger value="security" startIcon={withIcons ? Shield : undefined}>安全性</TabsTrigger>
|
|
169
|
+
<TabsTrigger value="api" startIcon={withIcons ? KeyRound : undefined}>API keys</TabsTrigger>
|
|
167
170
|
</>
|
|
168
171
|
)}
|
|
169
172
|
</TabsList>
|
|
@@ -258,7 +261,7 @@ export const ColorMatrix: Story = {
|
|
|
258
261
|
<Td mono>selected</Td>
|
|
259
262
|
<Td><TokenCell token="--foreground" display="foreground" /></Td>
|
|
260
263
|
<Td>—(transparent)</Td>
|
|
261
|
-
<Td><TokenCell token="--primary
|
|
264
|
+
<Td><TokenCell token="--primary" display="2px primary" /></Td>
|
|
262
265
|
</tr>
|
|
263
266
|
<tr>
|
|
264
267
|
<Td mono>disabled</Td>
|
|
@@ -211,7 +211,7 @@ DS 詞彙 **selected**(持續選中);Radix DOM attr 為 `data-state="activ
|
|
|
211
211
|
| State | 視覺 |
|
|
212
212
|
|---|---|
|
|
213
213
|
| selected | `text-foreground` + `font-medium` + 底部 2px `bg-primary` 底線(semantic.css 選中規則;2026-07-06 拍板持續選中站 base,Ant inkBarColor = colorPrimary 同款)|
|
|
214
|
-
| 未選 | `text-fg-secondary
|
|
214
|
+
| 未選 | `text-fg-secondary` + `font-medium`(與 selected 同 weight,差異僅文字色 + 有無底線,非 weight)、無底線;hover 時文字色轉 `text-foreground` |
|
|
215
215
|
| disabled | `text-fg-disabled`;無 hover 色變化、無底線;`cursor-not-allowed`;不接受鍵盤 focus |
|
|
216
216
|
| focus-visible | `ring-2 ring-ring ring-offset-1`,與 Button 一致;鍵盤導覽(左右箭頭)由 Radix 原生處理 |
|
|
217
217
|
|
|
@@ -82,6 +82,25 @@ function getElementRef(element: React.ReactElement): React.Ref<HTMLElement> | un
|
|
|
82
82
|
)
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
// 遞迴攤平 Fragment:React.Children.toArray 不展開 <>…</>(把 Fragment 當單一 element),
|
|
86
|
+
// 導致 menu 模式 dropdown 漏掉被 Fragment 包起來的 trigger(如條件渲染的溢出 tabs)+ cloneElement
|
|
87
|
+
// 對 Fragment 傳 ref 會噴 React warning。攤平後每個 keyless child 補 path-index key,對齊
|
|
88
|
+
// React.Children.toArray 的自動 key 行為,避免 list key warning。
|
|
89
|
+
function flattenTabTriggers(children: React.ReactNode, prefix = ''): React.ReactElement[] {
|
|
90
|
+
const out: React.ReactElement[] = []
|
|
91
|
+
React.Children.forEach(children, (child, i) => {
|
|
92
|
+
if (!React.isValidElement(child)) return
|
|
93
|
+
if (child.type === React.Fragment) {
|
|
94
|
+
out.push(
|
|
95
|
+
...flattenTabTriggers((child.props as { children?: React.ReactNode }).children, `${prefix}${i}.`),
|
|
96
|
+
)
|
|
97
|
+
} else {
|
|
98
|
+
out.push(child.key != null ? child : React.cloneElement(child, { key: `tab-${prefix}${i}` }))
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
return out
|
|
102
|
+
}
|
|
103
|
+
|
|
85
104
|
const Tabs = React.forwardRef<
|
|
86
105
|
React.ElementRef<typeof TabsPrimitive.Root>,
|
|
87
106
|
TabsProps
|
|
@@ -284,8 +303,9 @@ const MenuTabsList = React.forwardRef<
|
|
|
284
303
|
[]
|
|
285
304
|
)
|
|
286
305
|
|
|
306
|
+
// Fragment-safe:攤平 <>…</> 讓條件渲染的溢出 tabs 也進 dropdown / ref map(見 flattenTabTriggers)
|
|
287
307
|
const items = React.useMemo(
|
|
288
|
-
() =>
|
|
308
|
+
() => flattenTabTriggers(children),
|
|
289
309
|
[children]
|
|
290
310
|
)
|
|
291
311
|
|
|
@@ -205,7 +205,7 @@ export const ColorMatrix: Story = {
|
|
|
205
205
|
<Td>透明(zero-chrome,host context 提供邊界)</Td>
|
|
206
206
|
<Td>透明(無邊框)</Td>
|
|
207
207
|
<Td><TokenCell token="--foreground" display="foreground" /></Td>
|
|
208
|
-
<Td>—(display 無 placeholder
|
|
208
|
+
<Td>—(display 無 placeholder;空值顯半形 -(hyphen)foreground)</Td>
|
|
209
209
|
</tr>
|
|
210
210
|
<tr>
|
|
211
211
|
<Td mono>readonly</Td>
|
|
@@ -154,7 +154,7 @@ export const NoIconRule: Story = {
|
|
|
154
154
|
<div>
|
|
155
155
|
<Rule
|
|
156
156
|
title="Textarea 不支援 startIcon / endAction"
|
|
157
|
-
note="Textarea 是多行閱讀區,icon 放在框內會跟多行文字位置衝突(icon 是跟第一行對齊還是 center?)。世界級 DS(Material / Chakra / Ant)textarea 都不放內嵌 icon。需要 icon 請放
|
|
157
|
+
note="Textarea 是多行閱讀區,icon 放在框內會跟多行文字位置衝突(icon 是跟第一行對齊還是 center?)。世界級 DS(Material / Chakra / Ant)textarea 都不放內嵌 icon。需要 icon 請放 Textarea 外部(例如 Field 的標題 FieldLabel 旁)"
|
|
158
158
|
>
|
|
159
159
|
<Label warn>(型別設計)TextareaProps 不接受 startIcon / endAction,設計上就擋住</Label>
|
|
160
160
|
</Rule>
|
|
@@ -104,11 +104,11 @@ native `<textarea>` 自帶 `value` / `defaultValue` / `onChange` triplet — Tex
|
|
|
104
104
|
|
|
105
105
|
### Readonly 特例
|
|
106
106
|
|
|
107
|
-
不同於 Input 的 readonly(同高度、緊湊底色),`variant="default"` 的 Textarea readonly **保留 padding,並填上 `bg-readonly` 底色標示閱讀區**——多行內容需要明確的閱讀區域邊界訊號,沒有這塊填色區會無法與周圍純文字內容區分。`variant="
|
|
107
|
+
不同於 Input 的 readonly(同高度、緊湊底色),`variant="default"` 的 Textarea readonly **保留 padding,並填上 `bg-readonly` 底色標示閱讀區**——多行內容需要明確的閱讀區域邊界訊號,沒有這塊填色區會無法與周圍純文字內容區分。`variant="naked"`(cell-as-input substrate)readonly 透明且 `!px-0 !py-0`——邊界訊號由 host context(table cell)提供(`textarea.tsx` compoundVariants,intentional divergence;`bare` variant 2026-07-09 退役)。
|
|
108
108
|
|
|
109
109
|
### 邊界案例
|
|
110
110
|
|
|
111
|
-
- **空值**:edit 態顯示 placeholder;display 態 value 為空(`null` / `''`)
|
|
111
|
+
- **空值**:edit 態顯示 placeholder;display / readonly 態 value 為空(`null` / `''`)時渲染半形 `-`(`EMPTY_DISPLAY`)+ `text-foreground`(disabled → fg-disabled)
|
|
112
112
|
- **極長文字**:edit 態高度固定於 rows / resize 結果,內容超出時 native 內部捲動;display 態 `<div>` 隨內容增高(`whitespace-pre-wrap break-words`),無截斷
|
|
113
113
|
- **resize 上限**:`resize-y` 預設無上限;需限制時 consumer 傳 `max-h-*` className(native resize 受 max-height 約束)
|
|
114
114
|
|
|
@@ -120,7 +120,7 @@ native `<textarea>` 自帶 `value` / `defaultValue` / `onChange` triplet — Tex
|
|
|
120
120
|
- ❌ Textarea 裡放 startIcon / endAction——textarea 慣例無 icon 框內
|
|
121
121
|
- ❌ Textarea 啟用水平 resize(`resize-x` / `resize: both`)——破壞 form 佈局
|
|
122
122
|
- ❌ 把 Textarea 當 code editor 用(無 syntax highlight / auto-complete)
|
|
123
|
-
- ❌ `variant="default"` readonly 移除底色填充 + padding——多行內容需要閱讀區域邊界訊號(
|
|
123
|
+
- ❌ `variant="default"` readonly 移除底色填充 + padding——多行內容需要閱讀區域邊界訊號(naked 變體本就透明、邊界由 host context 提供,不在此限)
|
|
124
124
|
|
|
125
125
|
---
|
|
126
126
|
|
|
@@ -151,7 +151,7 @@ Textarea 是 **Field Controls family 的多行變體**,共用規則由 `../Field
|
|
|
151
151
|
- 字母鍵 — 輸入
|
|
152
152
|
- Enter — 換行(不觸發 form submit,與 Input 不同)
|
|
153
153
|
|
|
154
|
-
**Focus**:DS focus
|
|
154
|
+
**Focus**:DS focus 邊框由 `<textarea>` 自身的 cva compoundVariants 直接套用,無 fieldWrapper composite——Textarea 是裸 native textarea(見「定位」L30)。兩種 chrome 的 focus 觸發不同:**default chrome** 用 `focus-within:!border-primary`(滑鼠點擊或鍵盤聚焦皆亮主色邊框,對齊 form 場景);**naked chrome**(cell-as-input)用 `focus-visible:!border-primary`(僅鍵盤聚焦亮,滑鼠由 host cell 提供 frame)。readonly 有值態另用 ring idiom(`focus-visible:ring-2 ring-ring ring-offset-1`,鍵盤限定;見 `../Field/field-controls.spec.md`「readonly focus ring」)。standalone 使用即取得對應 focus 指示。
|
|
155
155
|
|
|
156
156
|
**驗證**:Storybook a11y addon panel 應 0 critical violation;鍵盤完整可操作(無需滑鼠)。WCAG AA contrast ≥ 4.5:1(text)/ 3:1(UI)。
|
|
157
157
|
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
import * as React from 'react'
|
|
3
3
|
import { cva, 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'
|
|
5
|
+
import type { FieldMode, FieldVariant, FieldVariantInternal } from '@/design-system/components/Field/field-types'
|
|
6
6
|
import { useFieldContext, useResolvedFieldSize, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid } from '@/design-system/components/Field/field-context'
|
|
7
|
-
import {
|
|
7
|
+
import { useFieldEmptyDisplay, fieldEmptyColorClass } from '@/design-system/components/Field/field-context'
|
|
8
|
+
import { useControllable } from '@/design-system/hooks/use-controllable'
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Textarea — 多行文字輸入
|
|
@@ -29,8 +30,8 @@ import { EMPTY_DISPLAY } from '@/design-system/components/Field/field-wrapper'
|
|
|
29
30
|
* 預設 rows={3}。消費者可透過 rows prop 調整,或透過 min-h-* className 覆寫。
|
|
30
31
|
*/
|
|
31
32
|
|
|
32
|
-
// Phase B1(2026-05-05)
|
|
33
|
-
// compoundVariants 決定,鏡射 fieldWrapperStyles 對齊 canonical(Phase D 將整併進 fieldWrapperStyles)。
|
|
33
|
+
// Phase B1(2026-05-05):chrome variant(default / naked;`bare` 2026-07-09 退役),mode×chrome 的
|
|
34
|
+
// chrome 規則由 compoundVariants 決定,鏡射 fieldWrapperStyles 對齊 canonical(Phase D 將整併進 fieldWrapperStyles)。
|
|
34
35
|
const textareaVariants = cva(
|
|
35
36
|
[
|
|
36
37
|
'w-full rounded-md',
|
|
@@ -51,10 +52,9 @@ const textareaVariants = cva(
|
|
|
51
52
|
readonly: '',
|
|
52
53
|
disabled: '',
|
|
53
54
|
},
|
|
54
|
-
// chrome 對齊 fieldWrapperStyles.variant(default / bare
|
|
55
|
+
// chrome 對齊 fieldWrapperStyles.variant(default / naked;2026-07-09 `bare` 退役)。
|
|
55
56
|
variant: {
|
|
56
57
|
default: '',
|
|
57
|
-
bare: '',
|
|
58
58
|
naked: '',
|
|
59
59
|
},
|
|
60
60
|
size: {
|
|
@@ -89,46 +89,24 @@ const textareaVariants = cva(
|
|
|
89
89
|
{
|
|
90
90
|
mode: 'readonly',
|
|
91
91
|
variant: 'default',
|
|
92
|
-
|
|
92
|
+
// 2026-07-13 a11y(WCAG 2.4.7):readonly 有值渲 native <textarea readOnly> 可鍵盤聚焦,base
|
|
93
|
+
// outline-none + border-transparent 無焦點指示 → 鏡射 field-wrapper.tsx readonly ring idiom
|
|
94
|
+
// (field-controls.spec.md「readonly focus ring」)。textarea 為裸 focusable element,直接用
|
|
95
|
+
// focus-visible:(非 :has()),僅鍵盤聚焦顯示、滑鼠點擊不觸發。
|
|
96
|
+
className: 'bg-readonly border border-transparent focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1',
|
|
93
97
|
},
|
|
94
98
|
{
|
|
95
99
|
mode: 'disabled',
|
|
96
100
|
variant: 'default',
|
|
97
101
|
className: 'bg-disabled border border-transparent cursor-not-allowed text-fg-disabled',
|
|
98
102
|
},
|
|
99
|
-
// bare chrome × mode
|
|
100
|
-
{
|
|
101
|
-
mode: 'edit',
|
|
102
|
-
variant: 'bare',
|
|
103
|
-
className: [
|
|
104
|
-
'bg-transparent border border-transparent',
|
|
105
|
-
'hover:border-border',
|
|
106
|
-
],
|
|
107
|
-
},
|
|
108
|
-
{ mode: 'edit', variant: 'bare', error: false, className: 'focus-within:!border-primary focus-within:hover:!border-primary' },
|
|
103
|
+
// (2026-07-09 `bare` chrome × mode compounds 退役已移除;error:true 為 variant-agnostic 保留)
|
|
109
104
|
{
|
|
110
105
|
mode: 'edit',
|
|
111
106
|
error: true,
|
|
112
107
|
className: 'border-error hover:border-error-hover focus-within:!border-error focus-within:hover:!border-error',
|
|
113
108
|
},
|
|
114
|
-
|
|
115
|
-
mode: 'display',
|
|
116
|
-
variant: 'bare',
|
|
117
|
-
className: 'bg-transparent border border-transparent',
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
mode: 'readonly',
|
|
121
|
-
variant: 'bare',
|
|
122
|
-
className: 'bg-transparent border border-transparent',
|
|
123
|
-
},
|
|
124
|
-
// 2026-07-05 fix(deep-audit A.1b,鏡射下方 naked 2026-05-13 codex V2 fix):移除 bare×disabled
|
|
125
|
-
// 的 `opacity-disabled` blanket — 同 class 已用具體 `text-fg-disabled` token swap,兩策略同時
|
|
126
|
-
// 套用違 opacity.spec.md「不可混用」canonical(0.45 × token 疊加 = 過度褪色)。
|
|
127
|
-
{
|
|
128
|
-
mode: 'disabled',
|
|
129
|
-
variant: 'bare',
|
|
130
|
-
className: 'bg-transparent border border-transparent cursor-not-allowed text-fg-disabled',
|
|
131
|
-
},
|
|
109
|
+
// (2026-07-09 display×bare / readonly×bare / disabled×bare compounds 退役已移除)
|
|
132
110
|
// naked chrome × mode — cell-as-input substrate(2026-05-06 v14 revert v12)。
|
|
133
111
|
// v12 `!absolute -inset-px` autoRowHeight 不相容 → revert v9 baseline + 保留 v13.3
|
|
134
112
|
// focus !important。focus-visible 用 textarea 自身 selector(focusable element)。
|
|
@@ -176,7 +154,7 @@ export interface TextareaProps
|
|
|
176
154
|
/**
|
|
177
155
|
* Visual chrome(正交於 mode);Phase B1(2026-05-05)新增。透傳自 FieldContext.variant,per-prop override。
|
|
178
156
|
* - `'default'` — 完整 chrome(form 場景)
|
|
179
|
-
* - `
|
|
157
|
+
* (2026-07-09 `bare` variant 退役;naked = cell-as-input substrate,@internal)
|
|
180
158
|
* - `'naked'` — 完全無 chrome,cell-as-input(host cell 提供 border + focus frame,對齊 Airtable / Notion / Excel cell editing)
|
|
181
159
|
*/
|
|
182
160
|
variant?: FieldVariant
|
|
@@ -197,6 +175,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
197
175
|
readOnly,
|
|
198
176
|
rows = 3,
|
|
199
177
|
value,
|
|
178
|
+
defaultValue,
|
|
200
179
|
id: idProp,
|
|
201
180
|
'aria-describedby': ariaDescribedByProp,
|
|
202
181
|
'aria-errormessage': ariaErrorMessageProp,
|
|
@@ -207,44 +186,84 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
207
186
|
// Field context 整合:disabled / mode / chrome / invalid / size / id 都能從 context 繼承
|
|
208
187
|
const fieldCtx = useFieldContext()
|
|
209
188
|
// chrome 透傳:per-prop override context
|
|
210
|
-
const variant:
|
|
189
|
+
const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)
|
|
211
190
|
const error = useResolvedFieldInvalid(errorProp)
|
|
212
191
|
const size = useResolvedFieldSize(sizeProp)
|
|
213
192
|
// 2026-06-08 SSOT:mode 經 useResolvedFieldMode 統一解析(prop > 有效 disabled > fieldCtx.mode > readOnly > 'edit')
|
|
214
193
|
const resolvedMode: FieldMode = useResolvedFieldMode({ mode: modeProp, disabled, readOnly })
|
|
194
|
+
const emptyDisplay = useFieldEmptyDisplay()
|
|
215
195
|
const isEditable = resolvedMode === 'edit'
|
|
216
196
|
const isDisplay = resolvedMode === 'display'
|
|
217
197
|
const inputId = idProp ?? fieldCtx?.id
|
|
218
198
|
const ariaDescribedBy = ariaDescribedByProp ?? fieldCtx?.descriptionId
|
|
219
199
|
const ariaErrorMessage = ariaErrorMessageProp ?? (error ? fieldCtx?.errorId : undefined)
|
|
220
200
|
|
|
221
|
-
// ── display mode
|
|
201
|
+
// ── display mode(+ readonly 空值)純展示,渲染 <div> 取代 <textarea>(white-space:pre-wrap 保留多行) ──
|
|
222
202
|
// 對齊 Carbon read-only / Cloudscape display-mode
|
|
223
|
-
|
|
224
|
-
|
|
203
|
+
// 2026-07-08 user 拍板:readonly 空值也走 display-div 顯 '-'(readonly **有值** 仍走下方
|
|
204
|
+
// native <textarea readOnly> 保留選取/複製語意 — field-controls.spec.md「null / undefined 值」)。
|
|
205
|
+
// 2026-07-13 fix:空值判斷需認得 uncontrolled defaultValue — readonly / display 也要顯示內容,
|
|
206
|
+
// 不可誤判為空值渲 '-'(原為靜態 value ?? defaultValue)。
|
|
207
|
+
// 2026-07-14 R2(dual-model consensus,同 Input input.tsx):resolved value 改走 useControllable
|
|
208
|
+
// 內部 SSOT(Radix idiom)。原靜態版有 typing-stale gap:uncontrolled 打字後外部切
|
|
209
|
+
// display / readonly 仍顯示 stale defaultValue。uncontrolled 時 native textarea 由
|
|
210
|
+
// value={resolved} 內部驅動(defaultValue 只作初始值,不落 DOM attribute),同時修
|
|
211
|
+
// remount-stale(切 display 再回 edit 從 defaultValue attribute 重掛)+ form.reset() stale
|
|
212
|
+
// (HTML 標準 reset 不發 input event,下方 reset bridge 修)。controlled(傳 value)時
|
|
213
|
+
// useControllable 純 passthrough,行為與原本完全一致。consumer onChange 不接進 hook
|
|
214
|
+
// (保留 native event signature),於 <textarea> onChange 另行轉發。
|
|
215
|
+
const isControlled = value !== undefined
|
|
216
|
+
const [resolved, setResolved] = useControllable<string | number | readonly string[]>({
|
|
217
|
+
value: value as string | number | readonly string[] | undefined,
|
|
218
|
+
defaultValue: defaultValue ?? '',
|
|
219
|
+
})
|
|
220
|
+
const displayValue = resolved != null && resolved !== '' ? String(resolved) : null
|
|
221
|
+
const showDisplaySpan = isDisplay || (resolvedMode === 'readonly' && displayValue == null)
|
|
222
|
+
|
|
223
|
+
// form.reset() bridge(uncontrolled only):reset 恢復 defaultValue 但不發 input event →
|
|
224
|
+
// 手動把 resolved 歸位 defaultValue。keyed on showDisplaySpan:display 分支不掛 native
|
|
225
|
+
// textarea,回 edit 重掛時 effect 重跑補掛 listener。
|
|
226
|
+
const innerRef = React.useRef<HTMLTextAreaElement | null>(null)
|
|
227
|
+
React.useEffect(() => {
|
|
228
|
+
if (isControlled || showDisplaySpan) return
|
|
229
|
+
const form = innerRef.current?.form
|
|
230
|
+
if (!form) return
|
|
231
|
+
const handleReset = () => setResolved(defaultValue ?? '')
|
|
232
|
+
form.addEventListener('reset', handleReset)
|
|
233
|
+
return () => form.removeEventListener('reset', handleReset)
|
|
234
|
+
}, [isControlled, showDisplaySpan, defaultValue, setResolved])
|
|
235
|
+
// Merge refs(LinkInput setRef idiom,link-input.tsx:138)
|
|
236
|
+
const setRef = React.useCallback((el: HTMLTextAreaElement | null) => {
|
|
237
|
+
innerRef.current = el
|
|
238
|
+
if (typeof ref === 'function') ref(el)
|
|
239
|
+
else if (ref) (ref as React.MutableRefObject<HTMLTextAreaElement | null>).current = el
|
|
240
|
+
}, [ref])
|
|
241
|
+
|
|
242
|
+
if (showDisplaySpan) {
|
|
243
|
+
const spanMode = isDisplay ? 'display' : 'readonly'
|
|
225
244
|
return (
|
|
226
245
|
<div
|
|
227
246
|
id={inputId}
|
|
228
|
-
data-field-mode=
|
|
247
|
+
data-field-mode={spanMode}
|
|
229
248
|
aria-describedby={ariaDescribedBy}
|
|
230
249
|
className={cn(
|
|
231
|
-
textareaVariants({ mode:
|
|
250
|
+
textareaVariants({ mode: spanMode, variant: variant, size }),
|
|
232
251
|
'whitespace-pre-wrap break-words',
|
|
233
|
-
displayValue == null &&
|
|
252
|
+
displayValue == null && fieldEmptyColorClass(resolvedMode),
|
|
234
253
|
className,
|
|
235
254
|
)}
|
|
236
255
|
>
|
|
237
|
-
{displayValue ??
|
|
256
|
+
{displayValue ?? emptyDisplay}
|
|
238
257
|
</div>
|
|
239
258
|
)
|
|
240
259
|
}
|
|
241
260
|
|
|
242
261
|
return (
|
|
243
262
|
<textarea
|
|
244
|
-
ref={
|
|
263
|
+
ref={setRef}
|
|
245
264
|
id={inputId}
|
|
246
265
|
rows={rows}
|
|
247
|
-
value={
|
|
266
|
+
value={resolved}
|
|
248
267
|
disabled={resolvedMode === 'disabled'}
|
|
249
268
|
readOnly={resolvedMode === 'readonly'}
|
|
250
269
|
aria-invalid={error || undefined}
|
|
@@ -258,6 +277,11 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
258
277
|
className
|
|
259
278
|
)}
|
|
260
279
|
{...props}
|
|
280
|
+
// 置於 {...props} 後:寫回 resolved SSOT(controlled 時 useControllable 內部為 no-op)再轉發 consumer onChange。
|
|
281
|
+
onChange={(e) => {
|
|
282
|
+
setResolved(e.target.value)
|
|
283
|
+
props.onChange?.(e)
|
|
284
|
+
}}
|
|
261
285
|
/>
|
|
262
286
|
)
|
|
263
287
|
}
|
|
@@ -280,6 +304,8 @@ export const textareaMeta = {
|
|
|
280
304
|
},
|
|
281
305
|
// states 對齊 cva compoundVariants + anatomy ColorMatrix 真實 state 集合;
|
|
282
306
|
// text input 無 'active'(按下)視覺態(Material/Polaris/Carbon TextArea 共識)。
|
|
307
|
+
// 'focus-visible' 泛指 focus 態:default chrome 實為 focus-within(滑鼠+鍵盤皆亮),naked chrome
|
|
308
|
+
// 為 focus-visible(鍵盤限定),readonly 有值態為 focus-visible ring(見上方 compoundVariants)。
|
|
283
309
|
states: ['default', 'hover', 'focus-visible', 'readonly', 'disabled', 'error'],
|
|
284
310
|
tokens: {
|
|
285
311
|
bg: ['bg-disabled', 'bg-readonly', 'bg-surface'], // 2026-07-04 補:readonly×default cva 實際消費(spec「Readonly 特例」主打 token)
|
|
@@ -147,12 +147,16 @@ function TimeColumn({ values, selected, disabledSet, label, onSelect, withDivide
|
|
|
147
147
|
// - TimePicker:wrap in h-[216px] container(預設 ~7 items)
|
|
148
148
|
// - DatePicker showTime / Range:flex-row items-stretch + calendar 一起決定高度(自動同高)
|
|
149
149
|
return (
|
|
150
|
-
|
|
150
|
+
// viewportTabIndex=-1:焦點直接落 listbox(有 keydown),消除「Viewport+listbox 雙 tab stop
|
|
151
|
+
// + 焦點落 Viewport 時 ↑/↓ 觸發原生捲動非選值」a11y bug(axe 由 listbox tabIndex=0 滿足)
|
|
152
|
+
<ScrollArea viewportTabIndex={-1} className={cn('flex-1 h-full', withDivider && 'border-r border-divider')}>
|
|
151
153
|
<div
|
|
152
154
|
ref={listRef}
|
|
153
155
|
role="listbox"
|
|
154
156
|
aria-label={label}
|
|
155
|
-
|
|
157
|
+
// guard values.includes:off-step value(如 minuteStep=15 但分=07)的 option 未渲染,
|
|
158
|
+
// 指向未渲染 id = 懸空 ARIA 參照(AT 讀空);僅真實渲染的 option 才掛
|
|
159
|
+
aria-activedescendant={selected != null && values.includes(selected) ? `${baseId}-opt-${selected}` : undefined}
|
|
156
160
|
tabIndex={0}
|
|
157
161
|
onKeyDown={handleKeyDown}
|
|
158
162
|
className="flex flex-col py-2 focus-visible:outline-2 focus-visible:outline-ring focus-visible:outline-offset-[-2px]"
|
|
@@ -61,7 +61,7 @@ export const UsageGuidance: Story = {
|
|
|
61
61
|
<div className="prose prose-sm max-w-prose space-y-4">
|
|
62
62
|
<p>TimePicker 只管時間,以下情境改用其他元件:</p>
|
|
63
63
|
<ul className="list-disc list-inside space-y-1 text-fg-secondary">
|
|
64
|
-
<li><strong>同時選日期和時間</strong> → DatePicker + TimePicker
|
|
64
|
+
<li><strong>同時選日期和時間</strong> → DatePicker 加 showTime prop(canonical 2026-05-02,Ant idiom;非 DatePicker + TimePicker 並列,見 DatePicker 設計原則)。Linear 的 reminder「日期 + 時間」是單一 datetime picker</li>
|
|
65
65
|
<li><strong>時間範圍(from-to)</strong> → 兩個 TimePicker 並列。Google Calendar 的 event time 是兩個 picker</li>
|
|
66
66
|
<li><strong>純文字時間輸入</strong> → Input + validation。開發者工具用 Input</li>
|
|
67
67
|
<li><strong>倒數計時或相對時間</strong> → 自訂計時器。TimePicker 是 wall-clock time(09:30),不是 duration</li>
|
|
@@ -66,7 +66,7 @@ open 軸同樣只開最小 API — **uncontrolled-only**:`defaultOpen`(初始開
|
|
|
66
66
|
|
|
67
67
|
| 場景 | 改用 | 原因 |
|
|
68
68
|
|------|------|------|
|
|
69
|
-
| 同時需要日期 + 時間 |
|
|
69
|
+
| 同時需要日期 + 時間 | **`<DatePicker showTime>`**(canonical 2026-05-02,Ant idiom;非獨立並列組合,見 date-picker.spec「showTime」段) | TimePicker 本身不帶日期;datetime 是單一 picker 職責 |
|
|
70
70
|
| 時間範圍(from-to) | **兩個 `<TimePicker>`** 並列 + 之間 `<ArrowRight>` | TimePicker **MVP 不支援 range**,用組合達成(對齊 Ant `TimePicker.RangePicker` 的 composition 思路 — [source](https://github.com/react-component/picker/blob/master/src/PickerInput/RangePicker.tsx)) |
|
|
71
71
|
| 純文字時間輸入(秒有意義的科學測量) | `<Input>` + mask / regex validation | TimePicker 的 column UX 不適合大量精確輸入 |
|
|
72
72
|
| 倒數計時 / 相對時間 | 自行渲染 `MM:SS` 倒數 | TimePicker 是 wall-clock time,不是 duration |
|
|
@@ -199,7 +199,7 @@ Panel 展開後的 column picker 結構:
|
|
|
199
199
|
詳見 `../Field/field-controls.spec.md`(共用規則)。
|
|
200
200
|
|
|
201
201
|
### Empty 值
|
|
202
|
-
`value={null}` / `value=""` / `value=undefined` 都視為空,trigger 顯示 `placeholder`。Display
|
|
202
|
+
`value={null}` / `value=""` / `value=undefined` 都視為空,trigger 顯示 `placeholder`。Display 模式空值顯示半形 `-`(對齊 `EMPTY_DISPLAY`,text-foreground)。
|
|
203
203
|
|
|
204
204
|
### 驗證時機
|
|
205
205
|
- **每次欄位選取(時 / 分 / 秒任一改變)當下即 commit value 給 `onChange`**(eager commit,非 OK 才送)。OK 鈕(「確定」)只負責關閉 Panel,不另行 commit。`此刻` 按鈕則一次 commit 當前時間並關閉 Panel
|
|
@@ -274,7 +274,7 @@ trigger 的互動狀態(focus / invalid / disabled / readonly)完全繼承 `../F
|
|
|
274
274
|
|
|
275
275
|
- **Disabled**:Field SSOT own;trigger 自動 disabled(`text-fg-disabled` + 不開 picker),Display mode + disabled 維持時間格式但 token 切 disabled。
|
|
276
276
|
- **Loading**:TimePicker 為 sync UI(time math 在 client),無 loading state。極端 case(後端 disabled-times list)應由 consumer 先 disable trigger 直到 fetch 完成。
|
|
277
|
-
- **Empty(no value)**:`value=null` 為合法 initial state,trigger 顯 placeholder;**未傳 `placeholder` 時 default = 格式遮罩**(`HH:MM`,showSeconds=true 時 `HH:MM:SS`),非固定文案。`null` + Display mode
|
|
277
|
+
- **Empty(no value)**:`value=null` 為合法 initial state,trigger 顯 placeholder;**未傳 `placeholder` 時 default = 格式遮罩**(`HH:MM`,showSeconds=true 時 `HH:MM:SS`),非固定文案。`null` + Display mode 顯半形 `-`(hyphen + `text-foreground`,disabled → fg-disabled)對齊 Input display empty 慣例。
|
|
278
278
|
- **Empty(disabled all times)**:極端場景(`disabledHours` / `disabledMinutes` 覆蓋全範圍),panel column 全 disabled,鍵盤焦點停留無導覽目標。
|
|
279
279
|
- **Panel 開啟中外部 value 變更**:controlled 單向資料流——欄位選中即時反映新 value,並 scroll-into-view(`behavior:'smooth'`,mount 後的變更;見「Spacing + 結構」的 Scroll-into-view 條)。
|
|
280
280
|
- **Dark mode / density**:走 Field + Popover SSOT 自動 adapt;panel column item 由 MenuItem SSOT 控 density。
|
|
@@ -3,16 +3,15 @@ import * as React from 'react'
|
|
|
3
3
|
import { X, Clock } from 'lucide-react'
|
|
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'
|
|
6
|
+
import type { FieldMode, FieldVariant, FieldVariantInternal } from '@/design-system/components/Field/field-types'
|
|
7
7
|
import {
|
|
8
8
|
fieldWrapperStyles,
|
|
9
9
|
bareInputStyles,
|
|
10
|
-
EMPTY_DISPLAY,
|
|
11
10
|
fieldDisplayTextClass,
|
|
12
11
|
} from '@/design-system/components/Field/field-wrapper'
|
|
13
12
|
import { ItemInlineAction, ItemSuffix } from '@/design-system/patterns/element-anatomy/item-anatomy'
|
|
14
13
|
import { Popover, PopoverTrigger, PopoverContent } from '@/design-system/components/Popover/popover'
|
|
15
|
-
import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid } from '@/design-system/components/Field/field-context'
|
|
14
|
+
import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid, useFieldEmptyDisplay, fieldEmptyColorClass } from '@/design-system/components/Field/field-context'
|
|
16
15
|
import { Button } from '@/design-system/components/Button/button'
|
|
17
16
|
import { SurfaceFooter } from '@/design-system/patterns/overlay-surface/overlay-surface'
|
|
18
17
|
import {
|
|
@@ -191,7 +190,8 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
|
|
|
191
190
|
const disabled = useResolvedFieldDisabled(disabledProp)
|
|
192
191
|
// 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode="display"> 漏 cascade
|
|
193
192
|
const resolvedMode = useResolvedFieldMode({ mode, disabled })
|
|
194
|
-
const
|
|
193
|
+
const emptyDisplay = useFieldEmptyDisplay()
|
|
194
|
+
const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)
|
|
195
195
|
const isEditable = resolvedMode === 'edit'
|
|
196
196
|
// 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反 7+ 重複 ternary)
|
|
197
197
|
const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']
|
|
@@ -204,7 +204,7 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
|
|
|
204
204
|
const setOpen = React.useCallback((next: boolean) => { setOpenState(next); onOpenChange?.(next) }, [onOpenChange])
|
|
205
205
|
|
|
206
206
|
const currentParts = React.useMemo(() => isoToTimeParts(value), [value])
|
|
207
|
-
// draft
|
|
207
|
+
// draft = panel 開啟期間的暫存:供 eager commit(每次欄選 commitDraft 即 commit)+ disabledTime 重算;OK 按鈕只 setOpen(false) 關 Panel,不做 commit
|
|
208
208
|
const [draft, setDraft] = React.useState<TimeParts>(
|
|
209
209
|
() => currentParts ?? { hours: 0, minutes: 0, seconds: 0 },
|
|
210
210
|
)
|
|
@@ -260,7 +260,7 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
|
|
|
260
260
|
if (!showDisplayEndIcon) {
|
|
261
261
|
// 2026-05-14 I2 fix(spec contract (e) display typography canonical):bare span 套
|
|
262
262
|
// `fieldDisplayTextClass(size)`(sm/md→text-body,lg→text-body-lg)— 對齊 Field family 統一。
|
|
263
|
-
if (!value) return <span className={cn(fieldDisplayTextClass(size),
|
|
263
|
+
if (!value) return <span className={cn(fieldDisplayTextClass(size), fieldEmptyColorClass(resolvedMode), className)}>{emptyDisplay}</span>
|
|
264
264
|
return <span className={cn(fieldDisplayTextClass(size), 'truncate', className)}>{formatTime(value, { formatOptions, locale })}</span>
|
|
265
265
|
}
|
|
266
266
|
return (
|
|
@@ -268,8 +268,8 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
|
|
|
268
268
|
className={cn(fieldWrapperStyles({ mode: 'display', variant, size }), className)}
|
|
269
269
|
data-field-mode="display"
|
|
270
270
|
>
|
|
271
|
-
<span className={cn(bareInputStyles, 'flex-1 min-w-0 truncate', !value &&
|
|
272
|
-
{value ? formatTime(value, { formatOptions, locale }) :
|
|
271
|
+
<span className={cn(bareInputStyles, 'flex-1 min-w-0 truncate', !value && fieldEmptyColorClass(resolvedMode))}>
|
|
272
|
+
{value ? formatTime(value, { formatOptions, locale }) : emptyDisplay}
|
|
273
273
|
</span>
|
|
274
274
|
{EndIconCmp && (
|
|
275
275
|
<ItemSuffix className="pointer-events-none">
|
|
@@ -297,7 +297,7 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
|
|
|
297
297
|
>
|
|
298
298
|
{value
|
|
299
299
|
? formatTime(value, { formatOptions, locale })
|
|
300
|
-
: <span className=
|
|
300
|
+
: <span className={fieldEmptyColorClass(resolvedMode)}>{emptyDisplay}</span>
|
|
301
301
|
}
|
|
302
302
|
</span>
|
|
303
303
|
{/* 2026-06-26 類型身份 indicator:edit 顯示 / readonly 不顯示 / disabled 保留(fg-disabled);naked cell 依 showDisplayEndIcon=isEditable(修 disabled cell 漏顯 bug)*/}
|
|
@@ -344,6 +344,10 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
|
|
|
344
344
|
aria-expanded={open}
|
|
345
345
|
onKeyDown={(e) => {
|
|
346
346
|
if (disabled) return
|
|
347
|
+
// 2026-07-14 dim-10 修:內層「清除時間」ItemInlineAction button 的 Enter/Space
|
|
348
|
+
// 會 bubble 到本 handler 被 preventDefault 吞掉(按鈕 activation 被取消、panel 反而開)。
|
|
349
|
+
// trigger 鍵盤邏輯只在事件源自 trigger div 本身時執行。
|
|
350
|
+
if (e.target !== e.currentTarget) return
|
|
347
351
|
// Enter / Space / ArrowDown / Alt+ArrowDown → 開 panel(APG combobox required + Select canonical)
|
|
348
352
|
if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {
|
|
349
353
|
e.preventDefault()
|
|
@@ -385,7 +389,9 @@ const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>(
|
|
|
385
389
|
)}
|
|
386
390
|
</div>
|
|
387
391
|
</PopoverTrigger>
|
|
388
|
-
|
|
392
|
+
{/* a11y(2026-07-14 dim-10):Radix PopoverContent 輸出 role="dialog",無 title 需
|
|
393
|
+
accessible name(WAI-ARIA dialog required);對齊 date-picker.tsx:1040「日期區間選擇」同款 DS default。 */}
|
|
394
|
+
<PopoverContent className="w-auto p-0" align="start" aria-label="選擇時間" /* i18n-allow: DS default dialog label */>
|
|
389
395
|
{/* Panel 對齊 ref/timepicker.png:2-3 個 SelectMenu 式欄位並排,分隔線分開。
|
|
390
396
|
Width 依欄數由 TimeColumns 決定:2 欄 w-40 / 3 欄 w-60。
|
|
391
397
|
Height 由 wrapper 控:216px 預設(扣 footer 後 list 約可見 5-6 items)。
|
|
@@ -10,26 +10,29 @@ const meta: Meta = {
|
|
|
10
10
|
export default meta
|
|
11
11
|
|
|
12
12
|
// 2026-07-04 audit:補 'info'(NoticeVariant 5 值,原漏 → 靜態雙 theme 對照未涵蓋全 variant)
|
|
13
|
+
// 2026-07-14 Dim 68 修:原 warning =「訂閱 7 天後停用 + 續訂 CTA」= 持久帳務狀態 + 強制 CTA,
|
|
14
|
+
// 違反 toast.spec.md「持久性系統狀態(方案過期)→ Alert」+「❌ 不把需要 user action 的內容
|
|
15
|
+
// 放 Toast(action 僅限復原類非必要後手)」。改為短暫操作回饋(匯入部分完成)+ 非必要「查看」後手。
|
|
13
16
|
const VARIANTS: NoticeVariant[] = ['neutral', 'info', 'success', 'warning', 'error']
|
|
14
17
|
const LABELS: Record<string, string> = {
|
|
15
18
|
neutral: '檔案已複製到剪貼簿',
|
|
16
19
|
info: '新版本 v2.4 可用',
|
|
17
20
|
success: '專案已儲存',
|
|
18
|
-
warning: '
|
|
21
|
+
warning: '匯入部分完成',
|
|
19
22
|
error: '無法連線伺服器',
|
|
20
23
|
}
|
|
21
24
|
const ACTIONS: Record<string, string> = {
|
|
22
25
|
neutral: '復原',
|
|
23
26
|
info: '查看更新',
|
|
24
27
|
success: '查看',
|
|
25
|
-
warning: '
|
|
28
|
+
warning: '查看',
|
|
26
29
|
error: '重試',
|
|
27
30
|
}
|
|
28
31
|
const DESCRIPTIONS: Record<string, string> = {
|
|
29
32
|
neutral: '「Q3 營收報表.xlsx」已加入剪貼簿',
|
|
30
33
|
info: '重新整理頁面即可套用新功能',
|
|
31
34
|
success: '變更已同步到所有成員',
|
|
32
|
-
warning: '
|
|
35
|
+
warning: '120 筆已匯入,3 筆格式錯誤被略過',
|
|
33
36
|
error: '請檢查網路後再試一次',
|
|
34
37
|
}
|
|
35
38
|
|
|
@@ -120,7 +123,7 @@ export const Interactive = {
|
|
|
120
123
|
<div className="flex flex-wrap gap-2">
|
|
121
124
|
<Button variant="secondary" onClick={() => toast({ title: '檔案已複製到剪貼簿', action: { label: '復原', onClick: () => {} } })}>複製檔案</Button>
|
|
122
125
|
<Button variant="secondary" onClick={() => toast({ variant: 'success', title: '專案已儲存', description: '變更已同步到所有成員', action: { label: '查看', onClick: () => {} } })}>儲存專案</Button>
|
|
123
|
-
<Button variant="secondary" onClick={() => toast({ variant: 'warning', title: '
|
|
126
|
+
<Button variant="secondary" onClick={() => toast({ variant: 'warning', title: '匯入部分完成', description: '120 筆已匯入,3 筆格式錯誤被略過', action: { label: '查看', onClick: () => {} } })}>匯入資料</Button>
|
|
124
127
|
<Button variant="secondary" onClick={() => toast({ variant: 'error', title: '無法連線伺服器', description: '請檢查網路後再試一次', action: { label: '重試', onClick: () => {} } })}>斷線示範</Button>
|
|
125
128
|
</div>
|
|
126
129
|
<Toaster />
|
|
@@ -159,7 +159,7 @@ export const Overview = {
|
|
|
159
159
|
<thead><tr><Th>零件</Th><Th>元素</Th><Th>說明</Th></tr></thead>
|
|
160
160
|
<tbody>
|
|
161
161
|
{[
|
|
162
|
-
['TooltipProvider', '<TooltipPrimitive.Provider>', '全域 Provider,控制
|
|
162
|
+
['TooltipProvider', '<TooltipPrimitive.Provider>', '全域 Provider,控制 skip window 與共享暖機狀態(全產品一個);單一 tooltip 的 delay 由 Root 預設 500ms 決定,優先於 Provider'],
|
|
163
163
|
['Tooltip', '<TooltipPrimitive.Root>', '單一 tooltip 實例的容器'],
|
|
164
164
|
['TooltipTrigger', '<TooltipPrimitive.Trigger>', '觸發器,搭配 asChild 套用至任意子元素'],
|
|
165
165
|
['TooltipContent', '<TooltipPrimitive.Content>', '浮動內容區,透過 Portal 渲染'],
|
|
@@ -195,7 +195,7 @@ export const Overview = {
|
|
|
195
195
|
{/* Warm-up pattern */}
|
|
196
196
|
<div className="flex flex-col gap-3">
|
|
197
197
|
<H3>Warm-up Pattern(暖機模式)</H3>
|
|
198
|
-
<Desc>全產品統一一組時間參數,不因 tooltip
|
|
198
|
+
<Desc>全產品統一一組時間參數,不因 tooltip 類型或位置而異:首次 hover 需停留 500ms 才顯示(確認是刻意停留);顯示後 300ms 內把游標移到下一個觸發器可即時切換、不用重新等待。</Desc>
|
|
199
199
|
<div className="flex flex-col gap-1.5 text-caption max-w-[600px]">
|
|
200
200
|
<div className="flex items-start gap-2 py-1.5 border-b border-divider">
|
|
201
201
|
<span className="text-fg-muted w-5 shrink-0 font-mono">1.</span>
|
|
@@ -338,7 +338,7 @@ export const Inspector = {
|
|
|
338
338
|
<div className="flex flex-col gap-4">
|
|
339
339
|
<div className="flex flex-col gap-1">
|
|
340
340
|
<H3>元件檢閱器</H3>
|
|
341
|
-
<Desc>切換方向查看 tooltip 的 placement
|
|
341
|
+
<Desc>切換方向查看 tooltip 的 placement。右側面板列出主要 design token 及其對應值。</Desc>
|
|
342
342
|
</div>
|
|
343
343
|
<InspectorInner />
|
|
344
344
|
</div>
|
|
@@ -407,7 +407,7 @@ export const Accessibility = {
|
|
|
407
407
|
render: () => (
|
|
408
408
|
<div className="max-w-3xl text-body text-fg-secondary">
|
|
409
409
|
<h3 className="text-h5 text-foreground mb-2">無障礙設計</h3>
|
|
410
|
-
<p className="whitespace-pre-line">{"ARIA / Pattern:沿用 Radix tooltip 的無障礙預設(role / aria-* / 鍵盤導覽)。詳 [Radix Accessibility docs](https://www.radix-ui.com/primitives/docs/components/tooltip#accessibility)。\n\n 鍵盤行為:\n\n- Tab — focus 到觸發器時顯示\n- Esc — 關閉\n\n 焦點:hover / focus 浮層,非 modal——focus 開啟浮層但不鎖住焦點,焦點仍留在觸發器,按 Esc
|
|
410
|
+
<p className="whitespace-pre-line">{"ARIA / Pattern:沿用 Radix tooltip 的無障礙預設(role / aria-* / 鍵盤導覽)。詳 [Radix Accessibility docs](https://www.radix-ui.com/primitives/docs/components/tooltip#accessibility)。\n\n 鍵盤行為:\n\n- Tab — focus 到觸發器時顯示\n- Esc — 關閉\n\n 焦點:hover / focus 浮層,非 modal——focus 開啟浮層但不鎖住焦點,焦點仍留在觸發器,按 Esc 關閉。觸發器的 focus ring 由 asChild 包的可聚焦子元件提供(Tooltip 本身是透傳 Trigger、不加樣式)。\n\n 驗證:Storybook a11y addon 面板應 0 個 critical 違規;純鍵盤即可完整操作(無需滑鼠)。文字對比 ≥ 4.5:1、UI 元素對比 ≥ 3:1(WCAG AA)。"}</p>
|
|
411
411
|
</div>
|
|
412
412
|
),
|
|
413
413
|
}
|