@qijenchen/design-system 0.1.0-beta.85 → 0.1.0-beta.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +210 -0
- package/CLAUDE.md +17 -191
- package/cli-init.mjs +30 -2
- package/dist/components/Accordion/accordion.d.ts +2 -2
- package/dist/components/Accordion/accordion.d.ts.map +1 -1
- package/dist/components/Accordion/accordion.js.map +1 -1
- package/dist/components/Avatar/avatar.js +1 -1
- package/dist/components/Avatar/avatar.js.map +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.d.ts +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.d.ts.map +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.js +3 -3
- package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
- package/dist/components/Button/button-group.d.ts +2 -2
- package/dist/components/Button/button-group.d.ts.map +1 -1
- package/dist/components/Button/button-group.js +2 -2
- package/dist/components/Button/button-group.js.map +1 -1
- package/dist/components/Button/button.d.ts +2 -1
- package/dist/components/Button/button.d.ts.map +1 -1
- package/dist/components/Button/button.js +1 -1
- package/dist/components/Button/button.js.map +1 -1
- package/dist/components/Carousel/carousel.d.ts +14 -8
- package/dist/components/Carousel/carousel.d.ts.map +1 -1
- package/dist/components/Carousel/carousel.js +64 -37
- package/dist/components/Carousel/carousel.js.map +1 -1
- package/dist/components/Checkbox/checkbox-group.d.ts.map +1 -1
- package/dist/components/Checkbox/checkbox-group.js +21 -14
- package/dist/components/Checkbox/checkbox-group.js.map +1 -1
- package/dist/components/Checkbox/checkbox.d.ts +2 -2
- package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox/checkbox.js +49 -10
- package/dist/components/Checkbox/checkbox.js.map +1 -1
- package/dist/components/Combobox/combobox.d.ts +15 -6
- package/dist/components/Combobox/combobox.d.ts.map +1 -1
- package/dist/components/Combobox/combobox.js +13 -7
- package/dist/components/Combobox/combobox.js.map +1 -1
- package/dist/components/Command/command.d.ts.map +1 -1
- package/dist/components/Command/command.js +11 -2
- package/dist/components/Command/command.js.map +1 -1
- package/dist/components/DataTable/cell-registry.d.ts +5 -5
- package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
- package/dist/components/DataTable/cell-registry.js +41 -38
- package/dist/components/DataTable/cell-registry.js.map +1 -1
- package/dist/components/DataTable/column-types.d.ts +1 -17
- package/dist/components/DataTable/column-types.d.ts.map +1 -1
- package/dist/components/DataTable/column-types.js.map +1 -1
- package/dist/components/DataTable/data-table-column-visibility-panel.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-column-visibility-panel.js +71 -64
- package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -1
- package/dist/components/DataTable/data-table-filter-panel.d.ts +0 -2
- package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-panel.js +3 -4
- package/dist/components/DataTable/data-table-filter-panel.js.map +1 -1
- package/dist/components/DataTable/data-table-interaction-layer.d.ts +2 -2
- package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
- package/dist/components/DataTable/data-table-sort-manager.js +2 -2
- package/dist/components/DataTable/data-table-sort-manager.js.map +1 -1
- package/dist/components/DataTable/data-table.d.ts +1 -1
- package/dist/components/DataTable/data-table.d.ts.map +1 -1
- package/dist/components/DataTable/data-table.js +44 -32
- package/dist/components/DataTable/data-table.js.map +1 -1
- package/dist/components/DatePicker/date-picker.d.ts +3 -3
- package/dist/components/DatePicker/date-picker.js +4 -4
- package/dist/components/DatePicker/date-picker.js.map +1 -1
- package/dist/components/DescriptionList/description-list.d.ts +3 -3
- package/dist/components/DescriptionList/description-list.d.ts.map +1 -1
- package/dist/components/DescriptionList/description-list.js +6 -6
- package/dist/components/DescriptionList/description-list.js.map +1 -1
- package/dist/components/Dialog/dialog.d.ts +2 -2
- package/dist/components/Dialog/dialog.d.ts.map +1 -1
- package/dist/components/Dialog/dialog.js.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts +8 -3
- package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.js +7 -7
- package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
- package/dist/components/Field/field-context.d.ts +16 -10
- package/dist/components/Field/field-context.d.ts.map +1 -1
- package/dist/components/Field/field-context.js.map +1 -1
- package/dist/components/Field/field-types.d.ts +2 -2
- package/dist/components/Field/field-types.d.ts.map +1 -1
- package/dist/components/Field/field-types.js.map +1 -1
- package/dist/components/Field/field-wrapper.d.ts +20 -4
- package/dist/components/Field/field-wrapper.d.ts.map +1 -1
- package/dist/components/Field/field-wrapper.js +30 -31
- package/dist/components/Field/field-wrapper.js.map +1 -1
- package/dist/components/Field/field.d.ts +1 -1
- package/dist/components/Field/field.d.ts.map +1 -1
- package/dist/components/Field/field.js +25 -3
- package/dist/components/Field/field.js.map +1 -1
- package/dist/components/Field/use-form-validation.d.ts +1 -1
- package/dist/components/Field/use-form-validation.js.map +1 -1
- package/dist/components/FileItem/file-item.d.ts.map +1 -1
- package/dist/components/FileItem/file-item.js +2 -5
- package/dist/components/FileItem/file-item.js.map +1 -1
- package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
- package/dist/components/FileUpload/file-upload.js +5 -1
- package/dist/components/FileUpload/file-upload.js.map +1 -1
- package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
- package/dist/components/FileViewer/file-viewer.js +2 -1
- package/dist/components/FileViewer/file-viewer.js.map +1 -1
- package/dist/components/HoverCard/hover-card.d.ts +2 -2
- package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
- package/dist/components/HoverCard/hover-card.js +4 -2
- package/dist/components/HoverCard/hover-card.js.map +1 -1
- package/dist/components/InlineEdit/inline-edit.d.ts +23 -16
- package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -1
- package/dist/components/InlineEdit/inline-edit.js +104 -62
- package/dist/components/InlineEdit/inline-edit.js.map +1 -1
- package/dist/components/Input/input.d.ts +1 -1
- package/dist/components/Input/input.d.ts.map +1 -1
- package/dist/components/Input/input.js +6 -6
- package/dist/components/Input/input.js.map +1 -1
- package/dist/components/LinkInput/link-input.d.ts +5 -5
- package/dist/components/LinkInput/link-input.d.ts.map +1 -1
- package/dist/components/LinkInput/link-input.js +3 -3
- package/dist/components/LinkInput/link-input.js.map +1 -1
- package/dist/components/Notice/notice.d.ts +17 -1
- package/dist/components/Notice/notice.d.ts.map +1 -1
- package/dist/components/Notice/notice.js +7 -1
- package/dist/components/Notice/notice.js.map +1 -1
- package/dist/components/NumberInput/number-input.d.ts +1 -1
- package/dist/components/NumberInput/number-input.d.ts.map +1 -1
- package/dist/components/NumberInput/number-input.js +12 -1
- package/dist/components/NumberInput/number-input.js.map +1 -1
- package/dist/components/PeoplePicker/avatar-stack-overflow.d.ts +1 -1
- package/dist/components/PeoplePicker/avatar-stack-overflow.js.map +1 -1
- package/dist/components/PeoplePicker/people-picker.d.ts +5 -5
- package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
- package/dist/components/PeoplePicker/people-picker.js +7 -5
- package/dist/components/PeoplePicker/people-picker.js.map +1 -1
- package/dist/components/PeoplePicker/person-display.d.ts +1 -1
- package/dist/components/PeoplePicker/person-display.js.map +1 -1
- package/dist/components/ProfileCard/profile-card.d.ts +1 -1
- package/dist/components/ProfileCard/profile-card.js.map +1 -1
- package/dist/components/RadioGroup/radio-group.d.ts +3 -3
- package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
- package/dist/components/RadioGroup/radio-group.js +1 -1
- package/dist/components/RadioGroup/radio-group.js.map +1 -1
- package/dist/components/Rating/rating.d.ts.map +1 -1
- package/dist/components/Rating/rating.js +4 -0
- package/dist/components/Rating/rating.js.map +1 -1
- package/dist/components/ScrollArea/scroll-area.d.ts +3 -2
- package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
- package/dist/components/ScrollArea/scroll-area.js.map +1 -1
- package/dist/components/SegmentedControl/segmented-control.d.ts +4 -3
- package/dist/components/SegmentedControl/segmented-control.d.ts.map +1 -1
- package/dist/components/SegmentedControl/segmented-control.js.map +1 -1
- package/dist/components/Select/select.d.ts +16 -6
- package/dist/components/Select/select.d.ts.map +1 -1
- package/dist/components/Select/select.js +17 -5
- package/dist/components/Select/select.js.map +1 -1
- package/dist/components/SelectMenu/select-menu.d.ts +17 -1
- package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
- package/dist/components/SelectMenu/select-menu.js +17 -3
- package/dist/components/SelectMenu/select-menu.js.map +1 -1
- package/dist/components/SelectionControl/boolean-value.d.ts +1 -1
- package/dist/components/SelectionControl/boolean-value.js.map +1 -1
- package/dist/components/Sheet/sheet.d.ts +2 -2
- package/dist/components/Sheet/sheet.d.ts.map +1 -1
- package/dist/components/Sheet/sheet.js.map +1 -1
- package/dist/components/Sidebar/sidebar.d.ts +1 -1
- package/dist/components/Sidebar/sidebar.js +2 -2
- package/dist/components/Sidebar/sidebar.js.map +1 -1
- package/dist/components/Slider/slider.d.ts.map +1 -1
- package/dist/components/Slider/slider.js +6 -3
- package/dist/components/Slider/slider.js.map +1 -1
- package/dist/components/Steps/steps.d.ts +6 -4
- package/dist/components/Steps/steps.d.ts.map +1 -1
- package/dist/components/Steps/steps.js +19 -5
- package/dist/components/Steps/steps.js.map +1 -1
- package/dist/components/Switch/switch.d.ts +4 -4
- package/dist/components/Switch/switch.d.ts.map +1 -1
- package/dist/components/Switch/switch.js +3 -1
- package/dist/components/Switch/switch.js.map +1 -1
- package/dist/components/Tabs/tabs.d.ts +1 -1
- package/dist/components/Tabs/tabs.d.ts.map +1 -1
- package/dist/components/Tabs/tabs.js +73 -23
- package/dist/components/Tabs/tabs.js.map +1 -1
- package/dist/components/Tag/tag.d.ts +3 -1
- package/dist/components/Tag/tag.d.ts.map +1 -1
- package/dist/components/Tag/tag.js +2 -2
- package/dist/components/Tag/tag.js.map +1 -1
- package/dist/components/Textarea/textarea.d.ts +4 -4
- package/dist/components/Textarea/textarea.d.ts.map +1 -1
- package/dist/components/Textarea/textarea.js +15 -15
- package/dist/components/Textarea/textarea.js.map +1 -1
- package/dist/components/TimePicker/time-picker.d.ts +4 -4
- package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
- package/dist/components/TimePicker/time-picker.js +8 -5
- package/dist/components/TimePicker/time-picker.js.map +1 -1
- package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
- package/dist/components/Tooltip/tooltip.js +27 -23
- package/dist/components/Tooltip/tooltip.js.map +1 -1
- package/dist/components/TreeView/tree-view.d.ts.map +1 -1
- package/dist/components/TreeView/tree-view.js +16 -0
- package/dist/components/TreeView/tree-view.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -13
- package/dist/index.js.map +1 -1
- package/dist/lib/drag-visual.d.ts +2 -2
- package/dist/lib/drag-visual.js.map +1 -1
- package/dist/lib/utils.d.ts +1 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +21 -1
- package/dist/lib/utils.js.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.d.ts +2 -0
- package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.js +2 -2
- package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
- package/dist/tokens/categorical-color.d.ts +3 -3
- package/dist/tokens/categorical-color.d.ts.map +1 -1
- package/dist/tokens/categorical-color.js +2 -2
- package/dist/tokens/categorical-color.js.map +1 -1
- package/ds-canonical/fork/AGENTS.md +213 -0
- package/ds-canonical/fork/codex/agents/skills/independent-review/SKILL.md +33 -0
- package/ds-canonical/fork/codex/hooks.json +16 -0
- package/ds-canonical/fork/governance.lock +22 -7
- package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +22 -1
- package/ds-canonical/fork/hooks/inject_deploy_url_after_push.sh +14 -2
- package/ds-canonical/fork/manifest.json +10 -1
- package/ds-canonical/fork/preamble.md +3 -2
- package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +7 -7
- package/ds-canonical/fork/skills/ux-audit/SKILL.md +1 -1
- package/ds-canonical/fork/skills/visual-audit/SKILL.md +0 -1
- package/ds-canonical/hooks/check_codex_brief_invariants.sh +9 -3
- package/ds-canonical/hooks/check_ds_anchor_preflight.sh +22 -1
- package/ds-canonical/hooks/check_overlay_open_focus_escape_probe.sh +11 -2
- package/ds-canonical/hooks/check_plugin_fork_health.sh +17 -1
- package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +21 -4
- package/ds-canonical/hooks/inject_deploy_url_after_push.sh +14 -2
- package/ds-canonical/hooks/session_start_governance_check.sh +10 -2
- package/ds-canonical/references/certified-surfaces.md +20 -0
- package/ds-canonical/references/preflight-gate-baseline.json +5 -0
- package/ds-canonical/references/principle-dim-map.json +20 -2
- package/ds-canonical/references/props-naming.md +2 -1
- package/ds-canonical/references/ssot-index.md +2 -2
- package/ds-canonical/references/story-baseline-registry.json +2 -2
- package/ds-canonical/rules/story-rules.md +1 -1
- package/ds-canonical/rules/ui-development.md +2 -1
- package/ds-canonical/skills/codex-collab/SKILL.md +7 -7
- package/ds-canonical/skills/codex-collab/references/brief-template.md +5 -1
- package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
- package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +5 -5
- package/ds-canonical/skills/design-system-audit/SKILL.md +5 -5
- package/ds-canonical/skills/product-ui-audit/SKILL.md +7 -7
- package/ds-canonical/skills/ux-audit/SKILL.md +1 -1
- package/ds-canonical/skills/visual-audit/SKILL.md +0 -1
- package/ds-story-manifest.json +21 -21
- package/llms-full.txt +3 -3
- package/llms.txt +1 -1
- package/package.json +2 -1
- package/src/components/Accordion/accordion.anatomy.stories.tsx +11 -11
- package/src/components/Accordion/accordion.principles.stories.tsx +21 -6
- package/src/components/Accordion/accordion.spec.md +10 -1
- package/src/components/Accordion/accordion.stories.tsx +10 -5
- package/src/components/Accordion/accordion.tsx +7 -2
- package/src/components/Alert/alert.principles.stories.tsx +2 -8
- package/src/components/AppShell/app-shell.anatomy.stories.tsx +19 -13
- package/src/components/AppShell/app-shell.principles.stories.tsx +21 -21
- package/src/components/AppShell/app-shell.spec.md +4 -0
- package/src/components/AppShell/app-shell.stories.tsx +4 -5
- package/src/components/Avatar/avatar.tsx +1 -1
- package/src/components/Badge/badge.spec.md +2 -2
- package/src/components/Breadcrumb/breadcrumb.anatomy.stories.tsx +7 -3
- package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +4 -1
- package/src/components/Breadcrumb/breadcrumb.stories.tsx +1 -1
- package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +6 -6
- package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +18 -6
- package/src/components/BulkActionBar/bulk-action-bar.spec.md +15 -5
- package/src/components/BulkActionBar/bulk-action-bar.stories.tsx +8 -24
- package/src/components/BulkActionBar/bulk-action-bar.tsx +4 -4
- package/src/components/Button/button-group.tsx +4 -4
- package/src/components/Button/button.anatomy.stories.tsx +26 -7
- package/src/components/Button/button.principles.stories.tsx +1 -1
- package/src/components/Button/button.spec.md +2 -2
- package/src/components/Button/button.stories.tsx +22 -28
- package/src/components/Button/button.tsx +2 -1
- package/src/components/Calendar/calendar.anatomy.stories.tsx +1 -1
- package/src/components/Calendar/calendar.spec.md +1 -1
- package/src/components/Carousel/carousel.spec.md +17 -1
- package/src/components/Carousel/carousel.tsx +93 -42
- package/src/components/Checkbox/checkbox-group.tsx +26 -15
- package/src/components/Checkbox/checkbox.anatomy.stories.tsx +3 -3
- package/src/components/Checkbox/checkbox.spec.md +12 -3
- package/src/components/Checkbox/checkbox.stories.tsx +4 -4
- package/src/components/Checkbox/checkbox.tsx +79 -19
- package/src/components/Coachmark/coachmark.anatomy.stories.tsx +5 -5
- package/src/components/Coachmark/coachmark.principles.stories.tsx +1 -1
- package/src/components/Combobox/combobox.anatomy.stories.tsx +11 -4
- package/src/components/Combobox/combobox.principles.stories.tsx +10 -11
- package/src/components/Combobox/combobox.spec.md +6 -2
- package/src/components/Combobox/combobox.stories.tsx +9 -36
- package/src/components/Combobox/combobox.tsx +42 -27
- package/src/components/Command/command.spec.md +3 -3
- package/src/components/Command/command.tsx +9 -2
- package/src/components/DataTable/cell-registry.tsx +75 -76
- package/src/components/DataTable/column-types.ts +4 -17
- package/src/components/DataTable/data-table-column-visibility-panel.tsx +20 -7
- package/src/components/DataTable/data-table-filter-panel.tsx +6 -6
- package/src/components/DataTable/data-table-interaction-layer.tsx +3 -3
- package/src/components/DataTable/data-table-sort-manager.tsx +2 -2
- package/src/components/DataTable/data-table.anatomy.stories.tsx +25 -24
- package/src/components/DataTable/data-table.css +2 -3
- package/src/components/DataTable/data-table.principles.stories.tsx +17 -11
- package/src/components/DataTable/data-table.spec.md +7 -7
- package/src/components/DataTable/data-table.stories.tsx +39 -29
- package/src/components/DataTable/data-table.tsx +90 -67
- package/src/components/DataTable/filter-operators.spec.md +2 -0
- package/src/components/DateGrid/date-grid.spec.md +1 -1
- package/src/components/DatePicker/date-picker.anatomy.stories.tsx +9 -9
- package/src/components/DatePicker/date-picker.principles.stories.tsx +4 -4
- package/src/components/DatePicker/date-picker.spec.md +9 -9
- package/src/components/DatePicker/date-picker.stories.tsx +9 -9
- package/src/components/DatePicker/date-picker.tsx +13 -13
- package/src/components/DescriptionList/description-list.anatomy.stories.tsx +5 -5
- package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
- package/src/components/DescriptionList/description-list.spec.md +12 -12
- package/src/components/DescriptionList/description-list.stories.tsx +3 -3
- package/src/components/DescriptionList/description-list.tsx +10 -10
- package/src/components/Dialog/dialog.tsx +4 -1
- package/src/components/DropdownMenu/dropdown-menu.tsx +27 -14
- package/src/components/Field/field-context.ts +17 -11
- package/src/components/Field/field-controls.spec.md +44 -37
- package/src/components/Field/field-types.ts +14 -12
- package/src/components/Field/field-wrapper.tsx +59 -43
- package/src/components/Field/field.anatomy.stories.tsx +6 -6
- package/src/components/Field/field.principles.stories.tsx +5 -28
- package/src/components/Field/field.spec.md +4 -4
- package/src/components/Field/field.stories.tsx +103 -72
- package/src/components/Field/field.tsx +36 -4
- package/src/components/Field/form-validation.spec.md +2 -0
- package/src/components/Field/use-form-validation.ts +1 -1
- package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +3 -3
- package/src/components/FieldControlGroup/field-control-group.spec.md +3 -3
- package/src/components/FileItem/file-item.tsx +10 -8
- package/src/components/FileUpload/file-upload.principles.stories.tsx +7 -7
- package/src/components/FileUpload/file-upload.spec.md +3 -2
- package/src/components/FileUpload/file-upload.tsx +5 -1
- package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +1 -1
- package/src/components/FileViewer/file-viewer.principles.stories.tsx +1 -1
- package/src/components/FileViewer/file-viewer.spec.md +1 -1
- package/src/components/FileViewer/file-viewer.stories.tsx +1 -1
- package/src/components/FileViewer/file-viewer.tsx +3 -2
- package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -4
- package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -1
- package/src/components/HoverCard/hover-card.spec.md +2 -2
- package/src/components/HoverCard/hover-card.tsx +4 -2
- package/src/components/InlineEdit/inline-edit.anatomy.stories.tsx +184 -0
- package/src/components/InlineEdit/inline-edit.principles.stories.tsx +142 -0
- package/src/components/InlineEdit/inline-edit.spec.md +71 -46
- package/src/components/InlineEdit/inline-edit.stories.tsx +20 -19
- package/src/components/InlineEdit/inline-edit.tsx +211 -125
- package/src/components/Input/input.anatomy.stories.tsx +15 -15
- package/src/components/Input/input.principles.stories.tsx +3 -3
- package/src/components/Input/input.spec.md +4 -4
- package/src/components/Input/input.stories.tsx +3 -3
- package/src/components/Input/input.tsx +13 -13
- package/src/components/LinkInput/link-input.anatomy.stories.tsx +2 -2
- package/src/components/LinkInput/link-input.principles.stories.tsx +1 -1
- package/src/components/LinkInput/link-input.spec.md +6 -2
- package/src/components/LinkInput/link-input.stories.tsx +8 -8
- package/src/components/LinkInput/link-input.tsx +21 -19
- package/src/components/Notice/notice.principles.stories.tsx +1 -1
- package/src/components/Notice/notice.tsx +8 -2
- package/src/components/NumberInput/number-input.anatomy.stories.tsx +6 -6
- package/src/components/NumberInput/number-input.principles.stories.tsx +4 -4
- package/src/components/NumberInput/number-input.spec.md +6 -6
- package/src/components/NumberInput/number-input.stories.tsx +4 -4
- package/src/components/NumberInput/number-input.tsx +17 -5
- package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +8 -4
- package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +6 -7
- package/src/components/OverflowIndicator/overflow-indicator.spec.md +1 -1
- package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +6 -30
- package/src/components/PeoplePicker/avatar-stack-overflow.ts +1 -1
- package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +13 -10
- package/src/components/PeoplePicker/people-picker.principles.stories.tsx +5 -1
- package/src/components/PeoplePicker/people-picker.stories.tsx +5 -5
- package/src/components/PeoplePicker/people-picker.tsx +39 -20
- package/src/components/PeoplePicker/person-display.tsx +3 -3
- package/src/components/Popover/popover.anatomy.stories.tsx +10 -7
- package/src/components/Popover/popover.principles.stories.tsx +49 -28
- package/src/components/Popover/popover.spec.md +4 -2
- package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
- package/src/components/ProfileCard/profile-card.tsx +1 -1
- package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +14 -13
- package/src/components/ProgressBar/progress-bar.principles.stories.tsx +8 -23
- package/src/components/ProgressBar/progress-bar.stories.tsx +8 -2
- package/src/components/README.md +1 -0
- package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +8 -3
- package/src/components/RadioGroup/radio-group.stories.tsx +3 -3
- package/src/components/RadioGroup/radio-group.tsx +19 -14
- package/src/components/Rating/rating.anatomy.stories.tsx +1 -1
- package/src/components/Rating/rating.principles.stories.tsx +3 -3
- package/src/components/Rating/rating.spec.md +2 -2
- package/src/components/Rating/rating.stories.tsx +4 -18
- package/src/components/Rating/rating.tsx +5 -1
- package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.spec.md +1 -1
- package/src/components/ScrollArea/scroll-area.tsx +5 -2
- package/src/components/SegmentedControl/segmented-control.principles.stories.tsx +4 -1
- package/src/components/SegmentedControl/segmented-control.spec.md +5 -10
- package/src/components/SegmentedControl/segmented-control.tsx +6 -3
- package/src/components/Select/select.anatomy.stories.tsx +13 -7
- package/src/components/Select/select.principles.stories.tsx +3 -3
- package/src/components/Select/select.spec.md +9 -1
- package/src/components/Select/select.stories.tsx +41 -27
- package/src/components/Select/select.tsx +44 -26
- package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +44 -26
- package/src/components/SelectMenu/select-menu.principles.stories.tsx +0 -1
- package/src/components/SelectMenu/select-menu.stories.tsx +7 -19
- package/src/components/SelectMenu/select-menu.tsx +38 -5
- package/src/components/SelectionControl/boolean-value.tsx +2 -2
- package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +1 -1
- package/src/components/SelectionControl/selection-item.principles.stories.tsx +4 -4
- package/src/components/Separator/separator.principles.stories.tsx +1 -1
- package/src/components/Sheet/sheet.principles.stories.tsx +5 -3
- package/src/components/Sheet/sheet.tsx +4 -1
- package/src/components/Sidebar/sidebar.tsx +1 -1
- package/src/components/Slider/slider.anatomy.stories.tsx +5 -4
- package/src/components/Slider/slider.principles.stories.tsx +6 -6
- package/src/components/Slider/slider.spec.md +13 -1
- package/src/components/Slider/slider.stories.tsx +44 -32
- package/src/components/Slider/slider.tsx +13 -4
- package/src/components/Steps/steps.anatomy.stories.tsx +7 -7
- package/src/components/Steps/steps.principles.stories.tsx +1 -1
- package/src/components/Steps/steps.spec.md +4 -2
- package/src/components/Steps/steps.stories.tsx +4 -56
- package/src/components/Steps/steps.tsx +31 -7
- package/src/components/Switch/switch.anatomy.stories.tsx +3 -3
- package/src/components/Switch/switch.principles.stories.tsx +1 -1
- package/src/components/Switch/switch.spec.md +4 -4
- package/src/components/Switch/switch.stories.tsx +3 -3
- package/src/components/Switch/switch.tsx +16 -10
- package/src/components/Tabs/tabs.anatomy.stories.tsx +6 -0
- package/src/components/Tabs/tabs.principles.stories.tsx +10 -2
- package/src/components/Tabs/tabs.spec.md +5 -2
- package/src/components/Tabs/tabs.stories.tsx +36 -44
- package/src/components/Tabs/tabs.tsx +101 -36
- package/src/components/Tag/tag.anatomy.stories.tsx +2 -2
- package/src/components/Tag/tag.principles.stories.tsx +4 -4
- package/src/components/Tag/tag.spec.md +3 -1
- package/src/components/Tag/tag.tsx +5 -3
- package/src/components/Textarea/textarea.anatomy.stories.tsx +6 -6
- package/src/components/Textarea/textarea.spec.md +5 -5
- package/src/components/Textarea/textarea.tsx +24 -24
- package/src/components/TimePicker/time-picker.anatomy.stories.tsx +12 -11
- package/src/components/TimePicker/time-picker.principles.stories.tsx +37 -44
- package/src/components/TimePicker/time-picker.spec.md +6 -5
- package/src/components/TimePicker/time-picker.stories.tsx +3 -3
- package/src/components/TimePicker/time-picker.tsx +24 -14
- package/src/components/Toast/toast.principles.stories.tsx +1 -1
- package/src/components/Toast/toast.stories.tsx +1 -1
- package/src/components/Tooltip/tooltip.anatomy.stories.tsx +15 -6
- package/src/components/Tooltip/tooltip.principles.stories.tsx +4 -4
- package/src/components/Tooltip/tooltip.spec.md +9 -3
- package/src/components/Tooltip/tooltip.stories.tsx +1 -1
- package/src/components/Tooltip/tooltip.tsx +12 -2
- package/src/components/TreeView/tree-view.anatomy.stories.tsx +16 -14
- package/src/components/TreeView/tree-view.principles.stories.tsx +44 -11
- package/src/components/TreeView/tree-view.spec.md +8 -6
- package/src/components/TreeView/tree-view.stories.tsx +9 -43
- package/src/components/TreeView/tree-view.tsx +36 -0
- package/src/index.ts +13 -10
- package/src/lib/drag-visual.ts +2 -2
- package/src/lib/utils.ts +19 -0
- package/src/patterns/element-anatomy/inline-action.spec.md +1 -1
- package/src/patterns/element-anatomy/item-anatomy.tsx +4 -2
- package/src/patterns/header-canonical/header-canonical.spec.md +9 -0
- package/src/patterns/header-canonical/header-canonical.stories.tsx +31 -5
- package/src/tokens/categorical-color.ts +5 -5
- package/src/tokens/color/color.spec.md +4 -4
- package/src/tokens/color/primitives.css +9 -2
- package/src/tokens/opacity/opacity.spec.md +1 -1
- package/src/tokens/orphan-tokens.spec.md +1 -1
- package/src/tokens/typography/typography.spec.md +2 -0
- package/src/tokens/uiSize/uiSize.css +5 -0
- package/src/tokens/uiSize/uiSize.spec.md +1 -1
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// - data-table-filter-panel.tsx + data-table-sort-manager.tsx + data-table-column-visibility-panel.tsx(panel state 隔離)
|
|
8
8
|
// - data-table-filter-group.tsx + data-table-filter-value-picker.tsx(@internal;2026-07-14 filter panel
|
|
9
9
|
// 過 800 hard cap 拆檔 — row/nested-group renderer + ValueShape picker,只被 panel 消費)
|
|
10
|
-
// - cell-registry.tsx(column type → cell
|
|
10
|
+
// - cell-registry.tsx(column type → cell view / edit 解析 SSOT)
|
|
11
11
|
// - data-table-interaction-layer.tsx + active-editor-controller.ts(spreadsheet overlay + portal editor)
|
|
12
12
|
// - column-types.ts + filter-operators.ts(✓ Rule-of-3 SSOT,3+ consumer)
|
|
13
13
|
// - filter-tree.ts(pure data + eval,test isolation)
|
|
@@ -96,7 +96,7 @@ export interface DataTableProps<TData>
|
|
|
96
96
|
/**
|
|
97
97
|
* Issue 9 cell error system(2026-05-10)。
|
|
98
98
|
*
|
|
99
|
-
* Map of `${rowId}:${colId}` → error message(string | string[])。Cell
|
|
99
|
+
* Map of `${rowId}:${colId}` → error message(string | string[])。Cell view mode 在 content
|
|
100
100
|
* 下方渲 `text-body text-error` 訊息,gap-1 spacing。Multi-error 用 array → ul / li 分行渲。
|
|
101
101
|
*
|
|
102
102
|
* **使用建議**:
|
|
@@ -279,13 +279,13 @@ export interface DataTablePaginationOptions {
|
|
|
279
279
|
//
|
|
280
280
|
// 原 `renderTypedValue` switch + `EditableCellContent` switch 兩條平行 type-switch
|
|
281
281
|
// 已 collapse 為單一 `cellRegistry`(./cell-registry.ts)— 每個 type → 一個 cell component,
|
|
282
|
-
// 同時處理
|
|
282
|
+
// 同時處理 view + edit mode(底層 Field control 的 mode prop)。對齊 M17 SSOT consolidation。
|
|
283
283
|
//
|
|
284
284
|
// 對齊 Notion / Airtable type-aware inline edit canonical(詳 spec §十二):
|
|
285
285
|
// - string / number / currency:cell click → inline edit
|
|
286
286
|
// - date / time / select / multiSelect / person / multiPerson:cell click → 進 edit mode
|
|
287
287
|
// - boolean:不分 read/edit mode,直接 `<Checkbox>` 點即 toggle + commit
|
|
288
|
-
// - url:read = LinkInput
|
|
288
|
+
// - url:read = LinkInput view + hover Pencil button,click Pencil 才進 edit mode
|
|
289
289
|
//
|
|
290
290
|
// Cell id format: `${rowId}__${columnId}`(編輯狀態 keying)
|
|
291
291
|
// Commit: blur OR Enter OR overlay close → 呼叫 onCellCommit
|
|
@@ -393,10 +393,10 @@ function columnSizeStyle(
|
|
|
393
393
|
//
|
|
394
394
|
// **flex-basis: baseSize(2026-05-06 v14.2)**:把 baseSize 當 explicit basis(不是 `0%`)。
|
|
395
395
|
// 為什麼:flex item base = basis + padding(box-sizing: border-box content-box 行為)。前 `0%`
|
|
396
|
-
// basis → cell padding 變 base 一部分。
|
|
396
|
+
// basis → cell padding 變 base 一部分。view(padding 12)vs edit(padding 0,Field 接管)
|
|
397
397
|
// 兩態 base 不同 → flex 重分配 → user 報「Price cell 進 edit 寬度縮 12px」
|
|
398
|
-
// (量測:Price
|
|
399
|
-
// explicit basis = baseSize 讓 padding 不參與 base 計算 →
|
|
398
|
+
// (量測:Price view 130.5 → edit 118.5 = -12px)。
|
|
399
|
+
// explicit basis = baseSize 讓 padding 不參與 base 計算 → view↔edit 寬度穩定。
|
|
400
400
|
return { flex: `1 1 ${baseSize}px`, minWidth: baseSize, maxWidth: maxSize }
|
|
401
401
|
}
|
|
402
402
|
|
|
@@ -649,12 +649,33 @@ function DraggableHeaderCell({
|
|
|
649
649
|
: dropIndicatorSide === 'after'
|
|
650
650
|
? dropIndicatorColumn.pseudoAfter
|
|
651
651
|
: ''
|
|
652
|
+
// dnd-kit KeyboardSensor 的 activator = listeners.onKeyDown,掛在整個 header cell 上(column
|
|
653
|
+
// 無獨立 activator node,不像 row drag 的 button-only handle)。巢狀互動 descendant(more-menu
|
|
654
|
+
// 按鈕 / resize separator / 排序 chevron)的 Enter/Space 冒泡到此 → dnd-kit preventDefault 啟動
|
|
655
|
+
// 欄位拖曳,吃掉按鈕原生啟動。包一層 guard:事件來自互動 descendant(非 header cell 本身)時
|
|
656
|
+
// 交回原生,不啟動拖曳。對齊 dnd-kit「activator node 排除互動 descendant」idiom。
|
|
657
|
+
const rawListeners = draggable.listeners as Record<string, ((e: React.SyntheticEvent) => void) | undefined> | undefined
|
|
658
|
+
const guardedListeners: Record<string, unknown> | undefined = rawListeners
|
|
659
|
+
? {
|
|
660
|
+
...rawListeners,
|
|
661
|
+
onKeyDown: (e: React.KeyboardEvent) => {
|
|
662
|
+
const target = e.target as HTMLElement
|
|
663
|
+
if (
|
|
664
|
+
target !== e.currentTarget &&
|
|
665
|
+
target.closest('button, a, input, select, textarea, [role="menuitem"], [role="separator"]')
|
|
666
|
+
) {
|
|
667
|
+
return
|
|
668
|
+
}
|
|
669
|
+
rawListeners.onKeyDown?.(e)
|
|
670
|
+
},
|
|
671
|
+
}
|
|
672
|
+
: rawListeners
|
|
652
673
|
return React.cloneElement(children as React.ReactElement<Record<string, unknown>>, {
|
|
653
674
|
ref: setRefs,
|
|
654
675
|
style: { ...(childProps.style ?? {}), ...dragStyle },
|
|
655
676
|
'data-column-id': id,
|
|
656
677
|
'data-column-locked': isLocked || undefined,
|
|
657
|
-
...(disabled ? {} : { ...draggableAttrs, ...(
|
|
678
|
+
...(disabled ? {} : { ...draggableAttrs, ...(guardedListeners ?? {}) }),
|
|
658
679
|
// 2026-05-06 v14.9 cursor canonical(對齊 Notion / Jira):
|
|
659
680
|
// **idle hover NOT 顯 cursor-grab** — header click 觸發 sort,grab cursor 會誤導 user 以為「點 = 拖」;
|
|
660
681
|
// **drag activation 後**(isDragging=true,過 8px activationConstraint)才顯 cursor-grabbing。
|
|
@@ -869,7 +890,7 @@ function RowDragHandle({ disabled, anyDragActive }: { disabled: boolean; anyDrag
|
|
|
869
890
|
// 但 renderCellContent 每次 render 傳 3 個全新 inline closure(onCommit / onCommitLive /
|
|
870
891
|
// onRequestEdit)+ `meta ?? {}` 新 object → memo shallow-compare 100% fail,防線靜默失效
|
|
871
892
|
// (DataTableInner 任何 re-render — scroll / resize / drag / spreadsheet 選取 / portal keystroke —
|
|
872
|
-
// 都讓全部 visible cells 的 Field
|
|
893
|
+
// 都讓全部 visible cells 的 Field view subtree 整棵重渲)。
|
|
873
894
|
// Fix:中介 memo slot — 從 parent 接 stable callback(commitCell / cancelCellEdit / onCellCommit /
|
|
874
895
|
// enterCellEdit)+ primitive rowId / colId,在 slot 內 useCallback 綁 per-cell closure;
|
|
875
896
|
// props 全 primitive / stable reference → memo 真命中,unchanged cells render 成本歸零。
|
|
@@ -884,11 +905,10 @@ interface MemoCellSlotProps {
|
|
|
884
905
|
value: any
|
|
885
906
|
// any-allow: free-form consumer meta bag(同 CellComponentProps.meta)
|
|
886
907
|
meta: Record<string, any>
|
|
887
|
-
mode: '
|
|
908
|
+
mode: 'view' | 'edit'
|
|
888
909
|
size: 'sm' | 'md' | 'lg'
|
|
889
910
|
autoRowHeight: boolean
|
|
890
911
|
isEditable: boolean
|
|
891
|
-
isDisabled: boolean
|
|
892
912
|
onCommitCell: (rowId: string, colId: string, next: unknown) => void
|
|
893
913
|
onCancelCell: (rowId: string, colId: string) => void
|
|
894
914
|
onCellCommitLive?: (rowId: string, colId: string, next: unknown) => void
|
|
@@ -897,13 +917,12 @@ interface MemoCellSlotProps {
|
|
|
897
917
|
|
|
898
918
|
const MemoCellSlot = React.memo(function MemoCellSlot({
|
|
899
919
|
Cell, rowId, colId, value, meta, mode, size, autoRowHeight,
|
|
900
|
-
isEditable,
|
|
920
|
+
isEditable, onCommitCell, onCancelCell, onCellCommitLive, onEnterEdit,
|
|
901
921
|
}: MemoCellSlotProps) {
|
|
902
922
|
const onCommit = React.useCallback((next: unknown) => onCommitCell(rowId, colId, next), [onCommitCell, rowId, colId])
|
|
903
923
|
const onCommitLive = React.useCallback((next: unknown) => onCellCommitLive?.(rowId, colId, next), [onCellCommitLive, rowId, colId])
|
|
904
924
|
const onCancel = React.useCallback(() => onCancelCell(rowId, colId), [onCancelCell, rowId, colId])
|
|
905
|
-
|
|
906
|
-
const onRequestEdit = React.useCallback(() => { if (!isDisabled) onEnterEdit(rowId, colId) }, [onEnterEdit, isDisabled, rowId, colId])
|
|
925
|
+
const onRequestEdit = React.useCallback(() => onEnterEdit(rowId, colId), [onEnterEdit, rowId, colId])
|
|
907
926
|
return (
|
|
908
927
|
<Cell
|
|
909
928
|
value={value}
|
|
@@ -912,7 +931,6 @@ const MemoCellSlot = React.memo(function MemoCellSlot({
|
|
|
912
931
|
size={size}
|
|
913
932
|
autoRowHeight={autoRowHeight}
|
|
914
933
|
isEditable={isEditable}
|
|
915
|
-
isDisabled={isDisabled}
|
|
916
934
|
onCommit={onCommit}
|
|
917
935
|
onCommitLive={onCommitLive}
|
|
918
936
|
onCancel={onCancel}
|
|
@@ -1040,28 +1058,13 @@ function DataTableInner<TData>(
|
|
|
1040
1058
|
},
|
|
1041
1059
|
[],
|
|
1042
1060
|
)
|
|
1043
|
-
// 2026-
|
|
1044
|
-
//
|
|
1045
|
-
//
|
|
1046
|
-
// 透過 isDisabled prop 走 mode='disabled'(各 Field type 內部具體 disabled token,非 wrapper blanket opacity),
|
|
1047
|
-
// (c) edit entry condition: `cellEditable && !cellDisabled`。
|
|
1048
|
-
const isCellDisabled = React.useCallback(
|
|
1049
|
-
// any-allow: free-form consumer meta — same rationale as L143 renderTypedValue
|
|
1050
|
-
(meta: Record<string, any> | undefined, row: unknown): boolean => {
|
|
1051
|
-
const d = meta?.disabled
|
|
1052
|
-
if (typeof d === 'function') return d(row) === true
|
|
1053
|
-
return d === true
|
|
1054
|
-
},
|
|
1055
|
-
[],
|
|
1056
|
-
)
|
|
1057
|
-
// 2026-05-13:canEditCell helper consolidation(per codex V4 follow-up + user「沒理由不做」拍板)
|
|
1058
|
-
// 抽 4-site repeated `editable && !disabled` pattern。3 path sites(keyboard / Tab / InteractionLayer)
|
|
1059
|
-
// call canEditCell;另 2 sites(renderCellContent + onEditableCellClick)用 already-computed `editable`/
|
|
1060
|
-
// `disabled` 變數(因為 disabled 還要單獨給 isDisabled prop + bg-disabled class),不 collapse to helper。
|
|
1061
|
+
// 2026-07-16 round16:cell「disabled」態廢除(世界級 grid 無 disabled cell;meta.disabled 全庫 0 消費)。
|
|
1062
|
+
// 鎖定 cell = `editable:(row)=>false`(= isCellEditable 模型),非灰化 disabled 態。原 isCellDisabled
|
|
1063
|
+
// helper + bg-disabled TD 灰化 + isDisabled prop 全移除;canEditCell 收斂為 isCellEditable。
|
|
1061
1064
|
const canEditCell = React.useCallback(
|
|
1062
1065
|
(meta: Record<string, unknown> | undefined, row: unknown): boolean =>
|
|
1063
|
-
isCellEditable(meta, row)
|
|
1064
|
-
[isCellEditable
|
|
1066
|
+
isCellEditable(meta, row),
|
|
1067
|
+
[isCellEditable],
|
|
1065
1068
|
)
|
|
1066
1069
|
const [sorting, setSorting] = React.useState<SortingState>(tableOptions?.state?.sorting as SortingState ?? [])
|
|
1067
1070
|
|
|
@@ -1502,7 +1505,7 @@ function DataTableInner<TData>(
|
|
|
1502
1505
|
const hoverProps = (_idx: number): Record<string, never> => ({})
|
|
1503
1506
|
|
|
1504
1507
|
// ── Cell render(Phase C 2026-05-05 — type-keyed registry SSOT)──
|
|
1505
|
-
// 命中 columnType → 走 cellRegistry(
|
|
1508
|
+
// 命中 columnType → 走 cellRegistry(view / edit mode 同元件 with `mode` prop);
|
|
1506
1509
|
// 無 columnType → consumer 自訂 cell.columnDef.cell。
|
|
1507
1510
|
const renderCellContent = (cell: ReturnType<typeof rows[number]['getVisibleCells']>[number]) => {
|
|
1508
1511
|
const meta = cell.column.columnDef.meta
|
|
@@ -1510,27 +1513,25 @@ function DataTableInner<TData>(
|
|
|
1510
1513
|
const wrap = autoRowHeight && meta?.wrap === true
|
|
1511
1514
|
// 已知 compound 欄位(Tag / PersonDisplay / LinkInput 等自帶 layout)直接 bypass TruncateCell,
|
|
1512
1515
|
// 因為 `truncate` 的 inline baseline context 會破壞自訂 layout 的垂直對齊。
|
|
1513
|
-
// 2026-05-09 D-path:date / time 加入(showDisplayEndIcon → Field naked-
|
|
1516
|
+
// 2026-05-09 D-path:date / time 加入(showDisplayEndIcon → Field naked-view 需 full width 才能
|
|
1514
1517
|
// 右對齊 ItemSuffix。TruncateCell 的 `<span truncate min-w-0>` block-display 會 collapse Field
|
|
1515
1518
|
// to content size,讓 Calendar / Clock icon 緊貼 value text 而非右邊緣)。
|
|
1516
1519
|
const isKnownCompound = colType === 'select' || colType === 'multiSelect' || colType === 'person' || colType === 'multiPerson' || colType === 'url' || colType === 'date' || colType === 'time'
|
|
1517
1520
|
const rowId = cell.row.id
|
|
1518
1521
|
const colId = cell.column.id
|
|
1519
1522
|
const editable = isCellEditable(meta, cell.row.original)
|
|
1520
|
-
const disabled = isCellDisabled(meta, cell.row.original)
|
|
1521
1523
|
const isEditingThisCell = editingCellId === cellEditId(rowId, colId)
|
|
1522
1524
|
|
|
1523
1525
|
let content: React.ReactNode
|
|
1524
1526
|
if (colType) {
|
|
1525
1527
|
const Cell = resolveCellComponent(colType)
|
|
1526
1528
|
// 2026-05-10 Slice D Step 5(D.3 portal Field):當 portal flag 啟 + cell 編輯中 →
|
|
1527
|
-
// cell 保持
|
|
1528
|
-
// 預設 inline-edit:isEditingThisCell ? edit :
|
|
1529
|
-
|
|
1530
|
-
const cellMode: 'edit' | 'display' =
|
|
1529
|
+
// cell 保持 view mode(SSOT preserved per codex Q6.2),portal layer 渲 edit Field 在上。
|
|
1530
|
+
// 預設 inline-edit:isEditingThisCell ? edit : view。
|
|
1531
|
+
const cellMode: 'edit' | 'view' =
|
|
1531
1532
|
(experimentalActiveEditorController && isEditingThisCell)
|
|
1532
|
-
? '
|
|
1533
|
-
:
|
|
1533
|
+
? 'view'
|
|
1534
|
+
: isEditingThisCell ? 'edit' : 'view'
|
|
1534
1535
|
// 2026-07-05 D3 perf fix:改經 MemoCellSlot — 原 inline closure props 讓 CellWithSurface
|
|
1535
1536
|
// memo 100% miss(詳 MemoCellSlot 檔頭註解)。
|
|
1536
1537
|
content = (
|
|
@@ -1544,7 +1545,6 @@ function DataTableInner<TData>(
|
|
|
1544
1545
|
size={size}
|
|
1545
1546
|
autoRowHeight={autoRowHeight}
|
|
1546
1547
|
isEditable={editable}
|
|
1547
|
-
isDisabled={disabled}
|
|
1548
1548
|
onCommitCell={commitCell}
|
|
1549
1549
|
onCancelCell={cancelCellEdit}
|
|
1550
1550
|
onCellCommitLive={onCellCommit}
|
|
@@ -1572,8 +1572,8 @@ function DataTableInner<TData>(
|
|
|
1572
1572
|
const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']
|
|
1573
1573
|
|
|
1574
1574
|
// 2026-05-09 D-path retired:`getEditIndicator(colType)` parallel system 移除。
|
|
1575
|
-
// Indicator authority 從 DataTable cellEl 移交 **Field naked-
|
|
1576
|
-
// — Select / TimePicker / DatePicker / Combobox / PeoplePicker 5 picker 的
|
|
1575
|
+
// Indicator authority 從 DataTable cellEl 移交 **Field naked-view branch via `showDisplayEndIcon` opt-in**
|
|
1576
|
+
// — Select / TimePicker / DatePicker / Combobox / PeoplePicker 5 picker 的 view mode 內建
|
|
1577
1577
|
// `<ItemSuffix>` 渲對應 trigger icon(同 edit DOM 結構)。LinkInput URL anchor 例外(無 suffix)。
|
|
1578
1578
|
// SSOT chain:cell-registry.tsx(opt-in props)→ Field component(intrinsic icon + ItemSuffix DOM)→
|
|
1579
1579
|
// item-anatomy ItemPrefix/ItemSuffix layout SSOT。詳 `.claude/planning/cell-indicator-ssot-rfc.md`。
|
|
@@ -1681,17 +1681,15 @@ function DataTableInner<TData>(
|
|
|
1681
1681
|
const cellRowId = cell.row.id
|
|
1682
1682
|
const cellColId = cell.column.id
|
|
1683
1683
|
const cellEditable = isCellEditable(meta, cell.row.original)
|
|
1684
|
-
const cellDisabled = isCellDisabled(meta, cell.row.original)
|
|
1685
1684
|
const isEditingThisCell = editingCellId === cellEditId(cellRowId, cellColId)
|
|
1686
|
-
// Indicator canonical(2026-05-09 D-path retire):**Field naked-
|
|
1685
|
+
// Indicator canonical(2026-05-09 D-path retire):**Field naked-view branch own** via
|
|
1687
1686
|
// `showDisplayEndIcon` opt-in(per-picker `<ItemSuffix>` 渲 ChevronDown/Calendar/Clock)。
|
|
1688
1687
|
// DataTable cellEl 不再 render parallel indicator — SSOT 對齊 Field family。
|
|
1689
1688
|
// 詳 `.claude/planning/cell-indicator-ssot-rfc.md` Step 9。
|
|
1690
1689
|
// Cell click → 進 edit mode(boolean 不需 — 自己 toggle;url 不需 — 走內部 Pencil button,Phase C 由 UrlCell 內處理)
|
|
1691
1690
|
const cellSpreadsheetId = `${cellRowId}:${cellColId}`
|
|
1692
1691
|
const isSelectedCell = spreadsheetMode && selectedCellId === cellSpreadsheetId
|
|
1693
|
-
|
|
1694
|
-
const onEditableCellClick = cellEditable && !cellDisabled && colType !== 'boolean' && colType !== 'url' && !isEditingThisCell
|
|
1692
|
+
const onEditableCellClick = cellEditable && colType !== 'boolean' && colType !== 'url' && !isEditingThisCell
|
|
1695
1693
|
? (e: React.MouseEvent) => {
|
|
1696
1694
|
if (spreadsheetMode) {
|
|
1697
1695
|
// Slice D Step 4 spreadsheet semantics(2026-05-10 user 拍板,2026-05-12 v2 fix):
|
|
@@ -1767,7 +1765,7 @@ function DataTableInner<TData>(
|
|
|
1767
1765
|
role="cell"
|
|
1768
1766
|
// group/cell + data-row-mode:讓 Field naked 用 `group-data-[row-mode=...]/cell:items-X`
|
|
1769
1767
|
// 從 cell 取 alignment(autoRowHeight=auto 頂對齊 / fixed=fixed 置中)。CSS propagation,
|
|
1770
|
-
// Field API 不變;每個 mode 內
|
|
1768
|
+
// Field API 不變;每個 mode 內 view↔edit 同 alignment(同 Field, 同 group → 同 items)。
|
|
1771
1769
|
// H1(2026-05-10):per-row error → effectiveAutoRowForCell 同 row.tsx effectiveAutoRow
|
|
1772
1770
|
data-row-mode={effectiveAutoRowForCell ? 'auto' : 'fixed'}
|
|
1773
1771
|
data-column-id={cell.column.id}
|
|
@@ -1777,8 +1775,6 @@ function DataTableInner<TData>(
|
|
|
1777
1775
|
// Phase 9 Issue 1 fix(2026-05-10):range cell bg fill via CSS [data-range-cell],
|
|
1778
1776
|
// 不在 overlay layer(避免 layer fixed-position bg 蓋 cell content)。
|
|
1779
1777
|
data-range-cell={spreadsheetMode && rangeCellIdSet.has(`${cell.row.id}:${cell.column.id}`) ? '' : undefined}
|
|
1780
|
-
// 2026-05-13 V1.6:data-cell-disabled attribute 給 CSS `:not([data-cell-disabled])` 過濾,disabled cell 不被 range fill 蓋
|
|
1781
|
-
data-cell-disabled={cellDisabled ? 'true' : undefined}
|
|
1782
1778
|
// Issue 9 cell error(2026-05-10):aria-describedby 接 error message id 給 AT 讀
|
|
1783
1779
|
aria-describedby={cellErrorId}
|
|
1784
1780
|
aria-invalid={hasCellError || undefined}
|
|
@@ -1817,19 +1813,16 @@ function DataTableInner<TData>(
|
|
|
1817
1813
|
// Field naked edit border 仍 own(per Field SSOT)— 編輯時 Field 自帶 border 1px,
|
|
1818
1814
|
// 跟 cell 4 邊 inset divider 視覺相疊(同 pixel)= 1 line visual,不雙線。
|
|
1819
1815
|
inlineEdit && 'dtCellGrid',
|
|
1820
|
-
onEditableCellClick && ['cursor-pointer', nakedCellEditableDisplayHover], // editable cell
|
|
1816
|
+
onEditableCellClick && ['cursor-pointer', nakedCellEditableDisplayHover], // editable cell view hover affordance(對齊 Notion / Airtable hover-cell-shows-border canonical)
|
|
1821
1817
|
// a11y(2026-07-14 dim-10 修):非 spreadsheet inlineEdit cell 可 Tab 聚焦(見下方
|
|
1822
1818
|
// tabIndex/onKeyDown)— focus-visible ring 對齊本檔 expand button / sortable header canonical。
|
|
1823
1819
|
onEditableCellClick && !spreadsheetMode && 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset',
|
|
1824
|
-
// 2026-05-13 Q3 cell disabled SSOT(per codex Q3 verdict + user 拍板「全部馬不停蹄做完」):
|
|
1825
|
-
// bg `--bg-disabled` component-state token(color.spec.md:671 owner)+ cursor 抑制 click affordance。
|
|
1826
|
-
cellDisabled && 'bg-disabled cursor-not-allowed',
|
|
1827
1820
|
// z-10 raise inline-edit cell;portal mode 不需(layer z-3 already on top)。
|
|
1828
1821
|
isEditingThisCell && !experimentalActiveEditorController && 'z-10',
|
|
1829
1822
|
)}
|
|
1830
1823
|
style={{
|
|
1831
1824
|
...columnSizeStyle(cell.column, { resize: enableColumnResize, isSystemCol: isSystemColumn(cell.column.id) }),
|
|
1832
|
-
// Padding override 只在 inline-edit cell(naked Field 撐滿 cell);portal mode cell 走正常
|
|
1825
|
+
// Padding override 只在 inline-edit cell(naked Field 撐滿 cell);portal mode cell 走正常 view padding
|
|
1833
1826
|
...(isEditingThisCell && !experimentalActiveEditorController ? {} : cellPadding),
|
|
1834
1827
|
// Slice D Step 2(2026-05-10):flag 開時 set CSS variable 抑制 Field naked hover outline,
|
|
1835
1828
|
// 讓 overlay layer 接管 hover ring paint(per RFC Contract 8 「one geometry owner, two paint owners」)。
|
|
@@ -1839,7 +1832,7 @@ function DataTableInner<TData>(
|
|
|
1839
1832
|
onClick={onEditableCellClick}
|
|
1840
1833
|
// a11y(2026-07-14 dim-10 修):非 spreadsheet 的 inlineEdit cell 原本只有 onClick —
|
|
1841
1834
|
// 鍵盤 user 無法聚焦 cell、無法進入編輯(WCAG 2.1.1)。對齊 Atlassian InlineEdit
|
|
1842
|
-
// read-view-focusable idiom:editable
|
|
1835
|
+
// read-view-focusable idiom:editable view cell 可 Tab 聚焦,Enter / F2 進 edit。
|
|
1843
1836
|
// spreadsheetMode 不加(該模式走 table-level selectedCellId 鍵盤導覽 SSOT,per-cell
|
|
1844
1837
|
// tab stop 會跟 roving 模型衝突);cell 內 embedded 控件事件不攔(target guard)。
|
|
1845
1838
|
tabIndex={onEditableCellClick && !spreadsheetMode ? 0 : undefined}
|
|
@@ -1852,7 +1845,7 @@ function DataTableInner<TData>(
|
|
|
1852
1845
|
} : undefined}
|
|
1853
1846
|
>
|
|
1854
1847
|
{/* Issue 9 cell error(2026-05-10):有 error → cell 內外結構切 flex-col,
|
|
1855
|
-
上 row 渲既有 nested + content,下 row 渲 error message
|
|
1848
|
+
上 row 渲既有 nested + content,下 row 渲 error message `text-body text-error`。
|
|
1856
1849
|
無 error 時走原 flex-row(backward-compat 0 layout shift)。 */}
|
|
1857
1850
|
{hasCellError ? (
|
|
1858
1851
|
<span className="flex flex-col self-stretch w-full min-w-0 gap-1">
|
|
@@ -2085,8 +2078,7 @@ function DataTableInner<TData>(
|
|
|
2085
2078
|
else if (e.key === 'ArrowLeft' && curColIdx > 0) { nextColIdx = curColIdx - 1 }
|
|
2086
2079
|
else if (e.key === 'ArrowRight' && curColIdx < allCols.length - 1) { nextColIdx = curColIdx + 1 }
|
|
2087
2080
|
else if (e.key === 'Enter' || e.key === 'F2') {
|
|
2088
|
-
// Enter / F2 → 進 edit(若 cell editable + 非 boolean / url
|
|
2089
|
-
// 2026-05-13 codex V1 fix:加 `!isCellDisabled(meta, row)` gate(對齊 mouse click invariant)
|
|
2081
|
+
// Enter / F2 → 進 edit(若 cell editable + 非 boolean / url)。edit 入口統一走 canEditCell(= isCellEditable)。
|
|
2090
2082
|
const row = table.getRowModel().rowsById[curRowId]
|
|
2091
2083
|
const colDef = table.getAllLeafColumns().find((c) => c.id === curColId)
|
|
2092
2084
|
// any-allow: column meta free-form
|
|
@@ -2142,7 +2134,7 @@ function DataTableInner<TData>(
|
|
|
2142
2134
|
},
|
|
2143
2135
|
// D3 dep-hygiene fix(2026-07-06):handler 內(portal edit Enter 分支)呼叫的是 canEditCell,
|
|
2144
2136
|
// 非 isCellEditable — 原 deps 列 isCellEditable 是靠 canEditCell identity-stable 才無實害的
|
|
2145
|
-
// stale-closure 地雷;改列真實依賴 canEditCell(其 useCallback deps 已含 isCellEditable
|
|
2137
|
+
// stale-closure 地雷;改列真實依賴 canEditCell(其 useCallback deps 已含 isCellEditable)。
|
|
2146
2138
|
[enabled, mode, hasAnySelection, selectableVisibleIds, setSelection,
|
|
2147
2139
|
spreadsheetMode, selectedCellId, editingCellId, table, canEditCell]
|
|
2148
2140
|
)
|
|
@@ -2315,16 +2307,40 @@ function DataTableInner<TData>(
|
|
|
2315
2307
|
const colOptIn = colMeta?.resizable !== false
|
|
2316
2308
|
const isResizable = enableColumnResize && !isSystemColumn(colId) && colOptIn
|
|
2317
2309
|
const isResizing = header.column.getIsResizing?.()
|
|
2310
|
+
// 鍵盤 resize(WAI-ARIA window-splitter):方向鍵調整欄寬,公開目前值 + 下限。
|
|
2311
|
+
// effectiveMinWidth 對齊 drag 路徑(resolveColumnSizing minSize),step 16px。
|
|
2312
|
+
const effectiveMinWidth = header.column.columnDef.minSize ?? MIN_COLUMN_WIDTH
|
|
2313
|
+
const currentWidth = Math.round(header.column.getSize())
|
|
2314
|
+
const RESIZE_KEY_STEP = 16
|
|
2318
2315
|
// H2: 不論 showDivider,只要 isResizable 就 render hot zone(panel boundary col 仍可拖)
|
|
2319
2316
|
if (!showDivider && !isResizable) return null
|
|
2320
2317
|
return (
|
|
2321
2318
|
<span
|
|
2322
2319
|
role={isResizable ? 'separator' : undefined}
|
|
2323
2320
|
aria-orientation={isResizable ? 'vertical' : undefined}
|
|
2324
|
-
aria-label={isResizable ? '
|
|
2321
|
+
aria-label={isResizable ? '調整欄寬' : undefined}
|
|
2322
|
+
// 鍵盤可操作 separator(WAI-ARIA window-splitter):focusable + 方向鍵調整 +
|
|
2323
|
+
// 公開目前寬度 / 下限。aria-valuetext 給 SR px 值(避免無 valuemax 時被誤讀成百分比)。
|
|
2324
|
+
tabIndex={isResizable ? 0 : undefined}
|
|
2325
|
+
aria-valuenow={isResizable ? currentWidth : undefined}
|
|
2326
|
+
aria-valuemin={isResizable ? effectiveMinWidth : undefined}
|
|
2327
|
+
aria-valuetext={isResizable ? `${currentWidth}px` : undefined}
|
|
2328
|
+
onKeyDown={isResizable ? (e: React.KeyboardEvent<HTMLSpanElement>) => {
|
|
2329
|
+
let next: number | null = null
|
|
2330
|
+
if (e.key === 'ArrowLeft') next = Math.max(effectiveMinWidth, currentWidth - RESIZE_KEY_STEP)
|
|
2331
|
+
else if (e.key === 'ArrowRight') next = currentWidth + RESIZE_KEY_STEP
|
|
2332
|
+
else if (e.key === 'Home') next = effectiveMinWidth
|
|
2333
|
+
if (next == null) return
|
|
2334
|
+
// stopPropagation:不讓方向鍵冒泡到 header 排序 / 欄位拖曳 keyboard listener。
|
|
2335
|
+
e.preventDefault()
|
|
2336
|
+
e.stopPropagation()
|
|
2337
|
+
table.setColumnSizing((prev) => ({ ...prev, [colId]: next as number }))
|
|
2338
|
+
// onColumnResize 由 columnSizing useEffect 自動 fire(非 drag 路徑不經 isResizingColumn)。
|
|
2339
|
+
} : undefined}
|
|
2325
2340
|
className={cn(
|
|
2326
2341
|
'group/resize absolute top-0 bottom-0 right-0 -mr-[3px] w-[7px]',
|
|
2327
2342
|
isResizable && 'cursor-col-resize select-none',
|
|
2343
|
+
isResizable && 'focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring',
|
|
2328
2344
|
)}
|
|
2329
2345
|
// 2026-05-12 fix v2(user 抓 R3 stopPropagation 沒生效):dnd-kit PointerSensor
|
|
2330
2346
|
// 監聽 `pointerdown`,我前一輪只 stop `onMouseDown` → pointerdown 仍冒泡 →
|
|
@@ -2721,12 +2737,19 @@ function DataTableInner<TData>(
|
|
|
2721
2737
|
// Center body 同時擁有 H + V scroll;maxHeight 限制讓 H scrollbar 落在 visible 底部
|
|
2722
2738
|
data-datatable-hscroll
|
|
2723
2739
|
data-datatable-panel="center"
|
|
2740
|
+
// a11y(scrollable-region-focusable,對齊 DS ScrollArea Viewport canonical):唯讀表格
|
|
2741
|
+
// 的可捲動 body 若無任何 focusable descendant,鍵盤使用者無法捲動。read-only 模式
|
|
2742
|
+
// (非 enabled / spreadsheet)outer table 也不 focusable → 這裡補 tabIndex=0 + 具名 +
|
|
2743
|
+
// inset focus ring,讓鍵盤可聚焦後方向鍵捲動;互動模式已由 outer table keyboard handler
|
|
2744
|
+
// + focusable cells 覆蓋,不重複加 tab stop。
|
|
2745
|
+
tabIndex={enabled || spreadsheetMode ? undefined : 0}
|
|
2746
|
+
aria-label={enabled || spreadsheetMode ? undefined : '表格內容,可捲動'}
|
|
2724
2747
|
// overflow-x/y: auto — 沒 overflow 就不顯 bar。wrapper minWidth 仍 trigger H 真 overflow。
|
|
2725
2748
|
// **不**用 scrollbar-gutter: stable — 那會永遠保留 V 軸 15px 空間,
|
|
2726
2749
|
// content fit 時看起來像「永遠有 V 捲軸」(Image #5 bug)。
|
|
2727
2750
|
// trade-off:V scroll 出現時 body 內側少 15px,header 不縮 → 右端微 misalign,
|
|
2728
2751
|
// 但 content fit 視覺乾淨優先(Mac 用戶 overlay scrollbar 不可見)。
|
|
2729
|
-
className="flex-1 min-w-0 overflow-x-auto overflow-y-auto"
|
|
2752
|
+
className="flex-1 min-w-0 overflow-x-auto overflow-y-auto focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-ring"
|
|
2730
2753
|
// isFillHeight:用 JS 算的 px(bodyMaxHeight),bypass CSS % 在 flex 場景的不可靠 shrink。
|
|
2731
2754
|
// 固定 px(300px etc):直接套 height。
|
|
2732
2755
|
style={
|
|
@@ -2779,7 +2802,7 @@ function DataTableInner<TData>(
|
|
|
2779
2802
|
activeEditorEnabled={experimentalActiveEditorController}
|
|
2780
2803
|
// Slice D Step 5(D.3 portal Field,2026-05-10):real portal Field render callback。
|
|
2781
2804
|
// Layer 在 ActiveEditorHost(z 3 float rect)render `<Cell mode="edit" />` 同 registry。
|
|
2782
|
-
// Cell wrapper 保持 mode="
|
|
2805
|
+
// Cell wrapper 保持 mode="view"(SSOT preserved per codex Q6.2)。
|
|
2783
2806
|
activeEditorRender={experimentalActiveEditorController ? (cellId) => {
|
|
2784
2807
|
const lastColon = cellId.lastIndexOf(':')
|
|
2785
2808
|
if (lastColon < 0) return null
|
|
@@ -245,6 +245,8 @@
|
|
|
245
245
|
- **巢狀深度**:1-level cap 由 TypeScript 型別鎖死(`FilterGroup.children: FilterCondition[]`),無 runtime guard;繞過型別塞入的非 condition 物件缺 `field`/`op` → incomplete → pass-through,不 crash
|
|
246
246
|
- **relative 區間邊界**:全部 local time、雙端 inclusive(`startOfDay`–`endOfDay`);`past_7_days` = 7 天前 00:00 起**含今天整天**;week 以週一起算(`weekStartsOn: 1`)
|
|
247
247
|
|
|
248
|
+
**A11y / Loading(明文 N/A)**:本 spec 是 Operator × ColumnType × ValueShape 資料模型 SSOT(非 UI 元件)——filter panel 的互動 a11y / loading 狀態屬 UI 層 owner(`data-table.spec.md` + filter panel 家族元件)與各 Field 控件 spec。
|
|
249
|
+
|
|
248
250
|
## 9. Phase B 進度
|
|
249
251
|
|
|
250
252
|
- [x] ValueShape × DS 元件對照 — 完成,coverage 8/12 直接消費 + 4 預留;datetime 走 `<DatePicker showTime>` / `<DatePickerRange showTime>` prop variant(M21,非新元件)
|
|
@@ -58,7 +58,7 @@ DateGrid 是 internal primitive(見「定位」),一般 consumer 經 `DatePicker
|
|
|
58
58
|
| 場景 | 改用 | 原因 |
|
|
59
59
|
|------|------|------|
|
|
60
60
|
| 日期輸入欄位 | `DatePicker` | DateGrid 是 inline,欄位需要 trigger + popup 結構 |
|
|
61
|
-
| 純顯示單日期 | `<DatePicker mode="
|
|
61
|
+
| 純顯示單日期 | `<DatePicker mode="view">` / `Intl.DateTimeFormat` | 不需 interactive 月曆 |
|
|
62
62
|
| 時間選擇(時分) | `TimePicker`(見 `../TimePicker/time-picker.spec.md`)| DateGrid 只處理日期層級 |
|
|
63
63
|
| 事件行事曆(日程本) | 專用行事曆元件 | DateGrid 是日期選擇;事件日誌需要 event overlay / drag / week/month view 切換 |
|
|
64
64
|
|
|
@@ -248,7 +248,7 @@ export const Overview = {
|
|
|
248
248
|
<thead><tr><Th>Prop</Th><Th>Type</Th><Th>Default</Th><Th>說明</Th></tr></thead>
|
|
249
249
|
<tbody>
|
|
250
250
|
{[
|
|
251
|
-
['mode', "'edit' | '
|
|
251
|
+
['mode', "'edit' | 'view' | 'readonly' | 'disabled'", "'edit'", "顯示模式;view 為純內容輸出(DataTable cell 用)。優先序:顯式 mode prop 永遠最優先,未傳 mode 時 disabled 才升 disabled(useResolvedFieldMode);disabled 以 aria-disabled 表達(trigger 為 div role=combobox,非原生 disabled 屬性)"],
|
|
252
252
|
['error', 'boolean', 'false', '紅色邊框 + aria-invalid,僅 edit 模式生效'],
|
|
253
253
|
['size', "'sm' | 'md' | 'lg'", "'md'", '高度與字體,與 Button 共用 field-height token'],
|
|
254
254
|
['value', 'string | null', '—', 'ISO date string(YYYY-MM-DD)'],
|
|
@@ -708,12 +708,12 @@ export const StateBehavior = {
|
|
|
708
708
|
<span className="text-caption font-medium text-fg-secondary">格式化 — 預設 YYYY/MM/DD 直接組;傳 formatOptions / locale 走 Intl.DateTimeFormat</span>
|
|
709
709
|
<div className="overflow-x-auto">
|
|
710
710
|
<table className="border-collapse text-caption">
|
|
711
|
-
<thead><tr><Th>設定</Th><Th>readonly 預覽</Th><Th>
|
|
711
|
+
<thead><tr><Th>設定</Th><Th>readonly 預覽</Th><Th>view 預覽</Th></tr></thead>
|
|
712
712
|
<tbody>
|
|
713
713
|
<tr>
|
|
714
714
|
<Td>預設(YYYY/MM/DD)</Td>
|
|
715
715
|
<td className="p-2 border-b border-divider"><DatePicker mode="readonly" value="2026-04-02" /></td>
|
|
716
|
-
<td className="p-2 border-b border-divider"><DatePicker mode="
|
|
716
|
+
<td className="p-2 border-b border-divider"><DatePicker mode="view" value="2026-04-02" /></td>
|
|
717
717
|
</tr>
|
|
718
718
|
<tr>
|
|
719
719
|
<Td mono>month: 'short'</Td>
|
|
@@ -721,7 +721,7 @@ export const StateBehavior = {
|
|
|
721
721
|
<DatePicker mode="readonly" value="2026-04-02" formatOptions={{ year: 'numeric', month: 'short', day: 'numeric' }} />
|
|
722
722
|
</td>
|
|
723
723
|
<td className="p-2 border-b border-divider">
|
|
724
|
-
<DatePicker mode="
|
|
724
|
+
<DatePicker mode="view" value="2026-04-02" formatOptions={{ year: 'numeric', month: 'short', day: 'numeric' }} />
|
|
725
725
|
</td>
|
|
726
726
|
</tr>
|
|
727
727
|
<tr>
|
|
@@ -730,7 +730,7 @@ export const StateBehavior = {
|
|
|
730
730
|
<DatePicker mode="readonly" value="2026-04-02" formatOptions={{ year: 'numeric', month: 'long', day: 'numeric' }} />
|
|
731
731
|
</td>
|
|
732
732
|
<td className="p-2 border-b border-divider">
|
|
733
|
-
<DatePicker mode="
|
|
733
|
+
<DatePicker mode="view" value="2026-04-02" formatOptions={{ year: 'numeric', month: 'long', day: 'numeric' }} />
|
|
734
734
|
</td>
|
|
735
735
|
</tr>
|
|
736
736
|
<tr>
|
|
@@ -739,13 +739,13 @@ export const StateBehavior = {
|
|
|
739
739
|
<DatePicker mode="readonly" value="2026-04-02" locale="zh-TW" formatOptions={{ year: 'numeric', month: 'long', day: 'numeric' }} />
|
|
740
740
|
</td>
|
|
741
741
|
<td className="p-2 border-b border-divider">
|
|
742
|
-
<DatePicker mode="
|
|
742
|
+
<DatePicker mode="view" value="2026-04-02" locale="zh-TW" formatOptions={{ year: 'numeric', month: 'long', day: 'numeric' }} />
|
|
743
743
|
</td>
|
|
744
744
|
</tr>
|
|
745
745
|
</tbody>
|
|
746
746
|
</table>
|
|
747
747
|
</div>
|
|
748
|
-
<span className="text-[11px] text-fg-muted">edit trigger 顯示文字與
|
|
748
|
+
<span className="text-[11px] text-fg-muted">edit trigger 顯示文字與 view 都用 Intl.DateTimeFormat 格式化(formatOptions / locale prop 對兩者皆生效,跨模式一致)。</span>
|
|
749
749
|
</div>
|
|
750
750
|
|
|
751
751
|
{/* Empty value display */}
|
|
@@ -761,8 +761,8 @@ export const StateBehavior = {
|
|
|
761
761
|
<DatePicker mode="disabled" value={null} className="w-48" />
|
|
762
762
|
</div>
|
|
763
763
|
<div className="flex items-center gap-3">
|
|
764
|
-
<span className="text-[11px] text-fg-muted w-20 shrink-0">
|
|
765
|
-
<DatePicker mode="
|
|
764
|
+
<span className="text-[11px] text-fg-muted w-20 shrink-0">view</span>
|
|
765
|
+
<DatePicker mode="view" value={null} />
|
|
766
766
|
</div>
|
|
767
767
|
</div>
|
|
768
768
|
</div>
|
|
@@ -51,7 +51,7 @@ export const UsageGuidance: Story = {
|
|
|
51
51
|
<li><LinkTo kind="Design System/Components/DatePicker/展示" name="可清除"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">篩選器的選填截止日,填錯一鍵清空(可清除)</span></LinkTo></li>
|
|
52
52
|
<li><LinkTo kind="Design System/Components/DatePicker/展示" name="尺寸"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">緊湊工具列與標準表單的尺寸對應(尺寸)</span></LinkTo></li>
|
|
53
53
|
<li><LinkTo kind="Design System/Components/DatePicker/展示" name="範圍模式:訂房 / 訂機票情境"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">Range:訂房 / 訂機票情境</span></LinkTo></li>
|
|
54
|
-
<li><LinkTo kind="Design System/Components/DatePicker/展示" name="
|
|
54
|
+
<li><LinkTo kind="Design System/Components/DatePicker/展示" name="檢視樣式"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">審批詳情頁唯讀展示申請日期(展示樣式)</span></LinkTo></li>
|
|
55
55
|
</ul>
|
|
56
56
|
<p className="text-fg-muted mt-3">判斷不確定時:對照 spec.md「何時用 / 何時不用」段;若仍不符,改用近親元件(見下方 vs 近親 段)。</p>
|
|
57
57
|
</div>
|
|
@@ -139,7 +139,7 @@ export const FormattingRule: Story = {
|
|
|
139
139
|
<div>
|
|
140
140
|
<Rule
|
|
141
141
|
title="格式化跨模式一致 — 預設 YYYY/MM/DD,傳 formatOptions / locale 走 Intl.DateTimeFormat"
|
|
142
|
-
note="未傳格式 prop 時預設直接組 YYYY/MM/DD(locale-independent);傳 formatOptions + locale 才啟用 Intl.DateTimeFormat,同時控制 readonly / disabled
|
|
142
|
+
note="未傳格式 prop 時預設直接組 YYYY/MM/DD(locale-independent);傳 formatOptions + locale 才啟用 Intl.DateTimeFormat,同時控制 view / readonly / disabled 的格式化文字與 edit trigger 顯示文字。跨模式一致、跨頁面可預期。Calendar popup 的月份語言由 Calendar 內部 locale 處理"
|
|
143
143
|
>
|
|
144
144
|
<DatePicker
|
|
145
145
|
mode="readonly"
|
|
@@ -157,7 +157,7 @@ export const FormattingRule: Story = {
|
|
|
157
157
|
|
|
158
158
|
<Rule
|
|
159
159
|
title="Edit trigger 文字也受 formatOptions / locale 控制"
|
|
160
|
-
note={'原本原生 <input type="date"> 的 trigger 格式受瀏覽器 locale 控制無法干預;改自建後,edit trigger 顯示的已選日期也走 Intl.DateTimeFormat——跟
|
|
160
|
+
note={'原本原生 <input type="date"> 的 trigger 格式受瀏覽器 locale 控制無法干預;改自建後,edit trigger 顯示的已選日期也走 Intl.DateTimeFormat——跟 view 模式完全一致'}
|
|
161
161
|
>
|
|
162
162
|
<DatePicker
|
|
163
163
|
value="2026-05-15"
|
|
@@ -170,7 +170,7 @@ export const FormattingRule: Story = {
|
|
|
170
170
|
|
|
171
171
|
<Rule
|
|
172
172
|
title="null 值統一顯示半形連字號"
|
|
173
|
-
note="
|
|
173
|
+
note="view 模式 null / undefined 顯示半形 -(foreground,同 readonly value 色),與其他 Field 元件一致"
|
|
174
174
|
>
|
|
175
175
|
<DatePicker mode="readonly" value={null} />
|
|
176
176
|
</Rule>
|
|
@@ -68,7 +68,7 @@ open 軸同樣只開最小 API — **uncontrolled-only**:`defaultOpen`(初始開
|
|
|
68
68
|
|------|------|------|
|
|
69
69
|
| 日期範圍(from → to) | **`<DatePicker.Range>`**(本檔下「DatePicker.Range」段,2026-04-21 新增) | 仿 Ant `DatePicker.RangePicker` 架構:雙 input + 箭頭 + 共用 calendar icon,Popover 展開兩月份並列 |
|
|
70
70
|
| 日期 + 時間(含時分) | **本元件加 `showTime` prop**(canonical 2026-05-02,Ant idiom) | DatePicker `showTime` / DatePickerRange `showTime` 內建 TimeColumns + 此刻/確定 footer;value 變 ISO datetime |
|
|
71
|
-
| 相對時間(「3 天前」「昨天」)|
|
|
71
|
+
| 相對時間(「3 天前」「昨天」)| 自訂展示元件 | DatePicker 的 view 態是絕對日期;相對時間需要計算 + locale 格式化 |
|
|
72
72
|
| 純文字 YYYY-MM-DD(不需要 picker)| `Input` | 如 API debug 介面、不需互動的純記錄 |
|
|
73
73
|
| 生日等「只有月日、不需要年」的欄位 | 目前用 DatePicker 忍受年份 | 多數情境可接受;要極致可自訂 MonthDayPicker |
|
|
74
74
|
| Notion / Google 式 event calendar 檢視 | **`<Calendar>`** 元件(見 `../Calendar/calendar.spec.md`,月/週/日 event view);**非本元件語義** | DatePicker 是「選日期」的 form control;Calendar 是「看事件」的頁面 canvas,兩者雖名字都有 calendar 但是不同 DS 元件 |
|
|
@@ -232,7 +232,7 @@ DateGrid cell 有 5 種語意視覺,每種用不同形狀/色彩語言避免混
|
|
|
232
232
|
- **needConfirm 獨立於 showTime**(2026-07-05 D4 codify):date-only `<DatePicker needConfirm>` 亦成立,footer 渲染 gate = `showTime || needConfirm`(single 與 Range 一致)——needConfirm 時「確定」是唯一 commit 路徑,漏渲 footer = draft 永遠無法 commit 的死路(2026-07-05 修 single 漏 `|| needConfirm`)
|
|
233
233
|
- showSeconds=false(default)→ TimeColumns 只顯示 H/M(對齊 Ant 預設)
|
|
234
234
|
- Range cell disable(對齊 Ant `useRangeDisabledDate`):activeEnd='start' + end 已選 → date > end disabled;activeEnd='end' + start 已選 → date < start disabled
|
|
235
|
-
- Range stadium pattern(canonical 2026-05-03 v8):
|
|
235
|
+
- Range stadium pattern(canonical 2026-05-03 v8):SSOT → `../DateGrid/date-grid.spec.md`(`range_start` / `range_end` 半圓 stadium 規則;class 細節見 `date-grid.tsx`)
|
|
236
236
|
|
|
237
237
|
### 為什麼用 prop 而非分離 `<DateTimePicker>` 元件
|
|
238
238
|
|
|
@@ -258,7 +258,7 @@ DateGrid cell 有 5 種語意視覺,每種用不同形狀/色彩語言避免混
|
|
|
258
258
|
| `formatOptions` | `Intl.DateTimeFormatOptions` | `{ year: 'numeric', month: 'short', day: 'numeric' }` |
|
|
259
259
|
| `locale` | BCP 47 locale | `'zh-TW'`、`'en-US'` |
|
|
260
260
|
|
|
261
|
-
預設(未傳 `formatOptions` / `locale`)直接組 `YYYY/MM/DD`(locale-independent,對齊 Ant year-first;見 date-picker.tsx `formatDate` 註解);傳 `formatOptions` / `locale` 才走 `Intl.DateTimeFormat`。
|
|
261
|
+
預設(未傳 `formatOptions` / `locale`)直接組 `YYYY/MM/DD`(locale-independent,對齊 Ant year-first;見 date-picker.tsx `formatDate` 註解);傳 `formatOptions` / `locale` 才走 `Intl.DateTimeFormat`。View / readonly / disabled 模式(含 DataTable cell)與 Edit 模式 trigger 文字同一條格式化路徑,兩者一致。
|
|
262
262
|
|
|
263
263
|
---
|
|
264
264
|
|
|
@@ -267,7 +267,7 @@ DateGrid cell 有 5 種語意視覺,每種用不同形狀/色彩語言避免混
|
|
|
267
267
|
`clearable` prop 在有值時顯示 clear 按鈕(endAction)。
|
|
268
268
|
|
|
269
269
|
- 只在 edit 模式顯示
|
|
270
|
-
- 清除後
|
|
270
|
+
- 清除後 `onChange?.('')`(空字串 = 空值;view 態顯示半形 -,text-foreground)
|
|
271
271
|
- **Dual-state sync canonical**(2026-05-03 v10):X 點擊必同時 `onChange?.('')` + `setDraft(null)`(Range 同),否則 `needConfirm=true`(showTime 預設)且 popover 開著時 `displayValue=draft` 仍顯示舊值,trigger 看起來「沒清」。X 在 trigger 上是 standard clear affordance,不走 needConfirm「等確定」語義 — 立刻 commit + 同步 draft
|
|
272
272
|
|
|
273
273
|
---
|
|
@@ -284,16 +284,16 @@ DateGrid cell 有 5 種語意視覺,每種用不同形狀/色彩語言避免混
|
|
|
284
284
|
|
|
285
285
|
DatePicker 套 `React.forwardRef` + `displayName`;`DatePickerProps` extends `Omit<React.HTMLAttributes<HTMLDivElement>, 'value' | 'onChange' | 'placeholder' | 'defaultValue'>`,剩餘 DOM attrs `...props` spread 到**當前 mode 的 root wrapper**(edit trigger `<div role="combobox">` / readonly · disabled wrapper div / Range wrapper div)。
|
|
286
286
|
|
|
287
|
-
**例外**:`mode="
|
|
287
|
+
**例外**:`mode="view"` 預設(未 opt-in `showDisplayEndIcon`)輸出裸 `<span>`,**不** spread `...props`——consumer 傳 DOM attrs(`data-testid` / `onFocus` 等)時注意 view 模式不生效;`className` 各 mode 皆顯式接收(套在該 mode 的 root 元素)。
|
|
288
288
|
|
|
289
|
-
**`asChild` 不支援**:trigger 是 compound(
|
|
289
|
+
**`asChild` 不支援**:trigger 是 compound(view 裸 span / edit `div role="combobox"` 不同 render tree),無單一 Slot 目標。consumer 若要自訂 trigger 視覺,改用 `<DatePicker mode="view">` + 自家 Button composition。
|
|
290
290
|
|
|
291
291
|
---
|
|
292
292
|
|
|
293
293
|
## A11y 預設
|
|
294
294
|
|
|
295
295
|
- Trigger:`role="combobox"` + `aria-haspopup="dialog"` + `aria-expanded={open}` + 必含 accessible name(`aria-label` / 或外層 `<label>` / 或 fieldCtx label)
|
|
296
|
-
- Popover content:`role="dialog"`;單一日期 popover
|
|
296
|
+
- Popover content:`role="dialog"`;單一日期 popover 的 PopoverContent 帶 `aria-label="日期選擇"`(date-picker.tsx:650,DS default dialog label),Range popover 加 `aria-label="日期區間選擇"`
|
|
297
297
|
- DateGrid 鍵盤:Arrow keys 切日 / PageUp/Down 切月 / Home/End 行首尾(react-day-picker v9 內建)
|
|
298
298
|
- Trigger 鍵盤(Space / Enter open;Esc close + 回焦):單一 DatePicker 的 `<div role="combobox">` 無 native Enter/Space→click,由元件**自建 `onKeyDown`** 開 popover(Radix PopoverTrigger 只 compose onClick;date-picker.tsx:554),Esc 關閉後靠 PopoverTrigger 的 Radix **內建** `triggerRef.focus()` 回焦;Range 用 native `<button>` onClick 開、只掛 PopoverAnchor(triggerRef 恆 null → 內建回焦 no-op),改由**自建 `onCloseAutoFocus`** 手動回焦 active 端 button(date-picker.tsx:1020-1026,守 WCAG 2.4.3)
|
|
299
299
|
- Range 雙 trigger:`activeEnd` state 指向當前編輯端,`aria-expanded` 對應只當該 trigger active 時 true
|
|
@@ -311,11 +311,11 @@ DatePicker 套 `React.forwardRef` + `displayName`;`DatePickerProps` extends `Omi
|
|
|
311
311
|
|
|
312
312
|
## 邊界案例
|
|
313
313
|
|
|
314
|
-
- **Disabled**:Field SSOT own;resolvedMode='disabled' 走 readonly / disabled 純 wrapper 分支(無 Popover trigger、`aria-disabled`、不開 picker),文字與 Calendar icon 切 `text-fg-disabled`。readonly 不顯示 Calendar icon(類型身份 indicator 規則,SSOT field-controls.spec.md
|
|
314
|
+
- **Disabled**:Field SSOT own;resolvedMode='disabled' 走 readonly / disabled 純 wrapper 分支(無 Popover trigger、`aria-disabled`、不開 picker),文字與 Calendar icon 切 `text-fg-disabled`。readonly 不顯示 Calendar icon(類型身份 indicator 規則,SSOT field-controls.spec.md「下拉箭頭與類型身份 indicator」段 L266-276);disabled 保留並切 `fg-disabled`。顯式 `mode="view"` 永遠最優先(useResolvedFieldMode step 1,見 field-context.ts):view + disabled 同傳走 view 分支不套 disabled token;要 disabled chrome 傳 `disabled` 或 `mode="disabled"`。
|
|
315
315
|
- **Loading(server-rendered grid)**:DatePicker 為 sync UI 不獨立 own loading。若 consumer 場景需 async date constraint fetch(如後端 disabled-dates list),consumer 應先 disable trigger 直到 fetch 完成,或在 popover 開啟後 body 切 `<Empty icon={<CircularProgress/>}/>`(對齊 panel-body loading SSOT)。本 spec scope 內不渲 loading state。
|
|
316
316
|
- **Empty(no value)**:`value=null` → trigger 顯 placeholder(預設 `YYYY/MM/DD`,showTime 時 `YYYY/MM/DD HH:MM`;consumer 可傳 `placeholder` 覆寫)。無導覽目標時鍵盤焦點停留(react-day-picker v9 內建)。
|
|
317
317
|
- **Invalid date input**:Field validation 處理 `aria-invalid="true"` + error border + 下方 error message;DatePicker 本身不 own validation 規則。
|
|
318
|
-
- **極長格式化日期**:trigger 文字單行 `truncate`(ellipsis),不換行不撐高(
|
|
318
|
+
- **極長格式化日期**:trigger 文字單行 `truncate`(ellipsis),不換行不撐高(View / Edit / typeable / Range 雙 input 皆同)。
|
|
319
319
|
- **RTL**:未實作方向鏡像(trigger / Range 雙 input / DateGrid 皆以 LTR physical 方向設計);RTL 屬 DS-wide 決策,未定(與 Chip / Breadcrumb 同口徑)。
|
|
320
320
|
- **Dark mode / density**:走 Field + Popover SSOT 自動 adapt;DateGrid 內 cell 尺寸 density-aware(消費 `--field-height-sm`:md=28×28 / lg=32×32,對齊 L128 + `date-grid.spec.md:120`),隨 density 縮放而非固定。
|
|
321
321
|
|