@qijenchen/design-system 0.1.0-beta.85 → 0.1.0-beta.87
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +210 -0
- package/CLAUDE.md +17 -191
- package/cli-init.mjs +30 -2
- package/dist/components/Accordion/accordion.d.ts +2 -2
- package/dist/components/Accordion/accordion.d.ts.map +1 -1
- package/dist/components/Accordion/accordion.js.map +1 -1
- package/dist/components/Avatar/avatar.js +1 -1
- package/dist/components/Avatar/avatar.js.map +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.d.ts +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.d.ts.map +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.js +3 -3
- package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
- package/dist/components/Button/button-group.d.ts +2 -2
- package/dist/components/Button/button-group.d.ts.map +1 -1
- package/dist/components/Button/button-group.js +2 -2
- package/dist/components/Button/button-group.js.map +1 -1
- package/dist/components/Button/button.d.ts +2 -1
- package/dist/components/Button/button.d.ts.map +1 -1
- package/dist/components/Button/button.js +1 -1
- package/dist/components/Button/button.js.map +1 -1
- package/dist/components/Carousel/carousel.d.ts +14 -8
- package/dist/components/Carousel/carousel.d.ts.map +1 -1
- package/dist/components/Carousel/carousel.js +64 -37
- package/dist/components/Carousel/carousel.js.map +1 -1
- package/dist/components/Checkbox/checkbox-group.d.ts.map +1 -1
- package/dist/components/Checkbox/checkbox-group.js +21 -14
- package/dist/components/Checkbox/checkbox-group.js.map +1 -1
- package/dist/components/Checkbox/checkbox.d.ts +2 -2
- package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox/checkbox.js +49 -10
- package/dist/components/Checkbox/checkbox.js.map +1 -1
- package/dist/components/Combobox/combobox.d.ts +15 -6
- package/dist/components/Combobox/combobox.d.ts.map +1 -1
- package/dist/components/Combobox/combobox.js +13 -7
- package/dist/components/Combobox/combobox.js.map +1 -1
- package/dist/components/Command/command.d.ts.map +1 -1
- package/dist/components/Command/command.js +11 -2
- package/dist/components/Command/command.js.map +1 -1
- package/dist/components/DataTable/cell-registry.d.ts +5 -5
- package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
- package/dist/components/DataTable/cell-registry.js +41 -38
- package/dist/components/DataTable/cell-registry.js.map +1 -1
- package/dist/components/DataTable/column-types.d.ts +1 -17
- package/dist/components/DataTable/column-types.d.ts.map +1 -1
- package/dist/components/DataTable/column-types.js.map +1 -1
- package/dist/components/DataTable/data-table-column-visibility-panel.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-column-visibility-panel.js +71 -64
- package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -1
- package/dist/components/DataTable/data-table-filter-panel.d.ts +0 -2
- package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-panel.js +3 -4
- package/dist/components/DataTable/data-table-filter-panel.js.map +1 -1
- package/dist/components/DataTable/data-table-interaction-layer.d.ts +2 -2
- package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
- package/dist/components/DataTable/data-table-sort-manager.js +2 -2
- package/dist/components/DataTable/data-table-sort-manager.js.map +1 -1
- package/dist/components/DataTable/data-table.d.ts +1 -1
- package/dist/components/DataTable/data-table.d.ts.map +1 -1
- package/dist/components/DataTable/data-table.js +44 -32
- package/dist/components/DataTable/data-table.js.map +1 -1
- package/dist/components/DatePicker/date-picker.d.ts +3 -3
- package/dist/components/DatePicker/date-picker.js +4 -4
- package/dist/components/DatePicker/date-picker.js.map +1 -1
- package/dist/components/DescriptionList/description-list.d.ts +3 -3
- package/dist/components/DescriptionList/description-list.d.ts.map +1 -1
- package/dist/components/DescriptionList/description-list.js +6 -6
- package/dist/components/DescriptionList/description-list.js.map +1 -1
- package/dist/components/Dialog/dialog.d.ts +2 -2
- package/dist/components/Dialog/dialog.d.ts.map +1 -1
- package/dist/components/Dialog/dialog.js.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts +8 -3
- package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.js +7 -7
- package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
- package/dist/components/Field/field-context.d.ts +16 -10
- package/dist/components/Field/field-context.d.ts.map +1 -1
- package/dist/components/Field/field-context.js.map +1 -1
- package/dist/components/Field/field-types.d.ts +2 -2
- package/dist/components/Field/field-types.d.ts.map +1 -1
- package/dist/components/Field/field-types.js.map +1 -1
- package/dist/components/Field/field-wrapper.d.ts +20 -4
- package/dist/components/Field/field-wrapper.d.ts.map +1 -1
- package/dist/components/Field/field-wrapper.js +30 -31
- package/dist/components/Field/field-wrapper.js.map +1 -1
- package/dist/components/Field/field.d.ts +1 -1
- package/dist/components/Field/field.d.ts.map +1 -1
- package/dist/components/Field/field.js +25 -3
- package/dist/components/Field/field.js.map +1 -1
- package/dist/components/Field/use-form-validation.d.ts +1 -1
- package/dist/components/Field/use-form-validation.js.map +1 -1
- package/dist/components/FileItem/file-item.d.ts.map +1 -1
- package/dist/components/FileItem/file-item.js +2 -5
- package/dist/components/FileItem/file-item.js.map +1 -1
- package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
- package/dist/components/FileUpload/file-upload.js +5 -1
- package/dist/components/FileUpload/file-upload.js.map +1 -1
- package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
- package/dist/components/FileViewer/file-viewer.js +2 -1
- package/dist/components/FileViewer/file-viewer.js.map +1 -1
- package/dist/components/HoverCard/hover-card.d.ts +2 -2
- package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
- package/dist/components/HoverCard/hover-card.js +4 -2
- package/dist/components/HoverCard/hover-card.js.map +1 -1
- package/dist/components/InlineEdit/inline-edit.d.ts +23 -16
- package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -1
- package/dist/components/InlineEdit/inline-edit.js +104 -62
- package/dist/components/InlineEdit/inline-edit.js.map +1 -1
- package/dist/components/Input/input.d.ts +1 -1
- package/dist/components/Input/input.d.ts.map +1 -1
- package/dist/components/Input/input.js +6 -6
- package/dist/components/Input/input.js.map +1 -1
- package/dist/components/LinkInput/link-input.d.ts +5 -5
- package/dist/components/LinkInput/link-input.d.ts.map +1 -1
- package/dist/components/LinkInput/link-input.js +3 -3
- package/dist/components/LinkInput/link-input.js.map +1 -1
- package/dist/components/Notice/notice.d.ts +17 -1
- package/dist/components/Notice/notice.d.ts.map +1 -1
- package/dist/components/Notice/notice.js +7 -1
- package/dist/components/Notice/notice.js.map +1 -1
- package/dist/components/NumberInput/number-input.d.ts +1 -1
- package/dist/components/NumberInput/number-input.d.ts.map +1 -1
- package/dist/components/NumberInput/number-input.js +12 -1
- package/dist/components/NumberInput/number-input.js.map +1 -1
- package/dist/components/PeoplePicker/avatar-stack-overflow.d.ts +1 -1
- package/dist/components/PeoplePicker/avatar-stack-overflow.js.map +1 -1
- package/dist/components/PeoplePicker/people-picker.d.ts +5 -5
- package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
- package/dist/components/PeoplePicker/people-picker.js +7 -5
- package/dist/components/PeoplePicker/people-picker.js.map +1 -1
- package/dist/components/PeoplePicker/person-display.d.ts +1 -1
- package/dist/components/PeoplePicker/person-display.js.map +1 -1
- package/dist/components/ProfileCard/profile-card.d.ts +1 -1
- package/dist/components/ProfileCard/profile-card.js.map +1 -1
- package/dist/components/RadioGroup/radio-group.d.ts +3 -3
- package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
- package/dist/components/RadioGroup/radio-group.js +1 -1
- package/dist/components/RadioGroup/radio-group.js.map +1 -1
- package/dist/components/Rating/rating.d.ts.map +1 -1
- package/dist/components/Rating/rating.js +4 -0
- package/dist/components/Rating/rating.js.map +1 -1
- package/dist/components/ScrollArea/scroll-area.d.ts +3 -2
- package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
- package/dist/components/ScrollArea/scroll-area.js.map +1 -1
- package/dist/components/SegmentedControl/segmented-control.d.ts +4 -3
- package/dist/components/SegmentedControl/segmented-control.d.ts.map +1 -1
- package/dist/components/SegmentedControl/segmented-control.js.map +1 -1
- package/dist/components/Select/select.d.ts +16 -6
- package/dist/components/Select/select.d.ts.map +1 -1
- package/dist/components/Select/select.js +17 -5
- package/dist/components/Select/select.js.map +1 -1
- package/dist/components/SelectMenu/select-menu.d.ts +17 -1
- package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
- package/dist/components/SelectMenu/select-menu.js +17 -3
- package/dist/components/SelectMenu/select-menu.js.map +1 -1
- package/dist/components/SelectionControl/boolean-value.d.ts +1 -1
- package/dist/components/SelectionControl/boolean-value.js.map +1 -1
- package/dist/components/Sheet/sheet.d.ts +2 -2
- package/dist/components/Sheet/sheet.d.ts.map +1 -1
- package/dist/components/Sheet/sheet.js.map +1 -1
- package/dist/components/Sidebar/sidebar.d.ts +1 -1
- package/dist/components/Sidebar/sidebar.js +2 -2
- package/dist/components/Sidebar/sidebar.js.map +1 -1
- package/dist/components/Slider/slider.d.ts.map +1 -1
- package/dist/components/Slider/slider.js +6 -3
- package/dist/components/Slider/slider.js.map +1 -1
- package/dist/components/Steps/steps.d.ts +6 -4
- package/dist/components/Steps/steps.d.ts.map +1 -1
- package/dist/components/Steps/steps.js +19 -5
- package/dist/components/Steps/steps.js.map +1 -1
- package/dist/components/Switch/switch.d.ts +4 -4
- package/dist/components/Switch/switch.d.ts.map +1 -1
- package/dist/components/Switch/switch.js +3 -1
- package/dist/components/Switch/switch.js.map +1 -1
- package/dist/components/Tabs/tabs.d.ts +1 -1
- package/dist/components/Tabs/tabs.d.ts.map +1 -1
- package/dist/components/Tabs/tabs.js +73 -23
- package/dist/components/Tabs/tabs.js.map +1 -1
- package/dist/components/Tag/tag.d.ts +3 -1
- package/dist/components/Tag/tag.d.ts.map +1 -1
- package/dist/components/Tag/tag.js +2 -2
- package/dist/components/Tag/tag.js.map +1 -1
- package/dist/components/Textarea/textarea.d.ts +4 -4
- package/dist/components/Textarea/textarea.d.ts.map +1 -1
- package/dist/components/Textarea/textarea.js +15 -15
- package/dist/components/Textarea/textarea.js.map +1 -1
- package/dist/components/TimePicker/time-picker.d.ts +4 -4
- package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
- package/dist/components/TimePicker/time-picker.js +8 -5
- package/dist/components/TimePicker/time-picker.js.map +1 -1
- package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
- package/dist/components/Tooltip/tooltip.js +27 -23
- package/dist/components/Tooltip/tooltip.js.map +1 -1
- package/dist/components/TreeView/tree-view.d.ts.map +1 -1
- package/dist/components/TreeView/tree-view.js +16 -0
- package/dist/components/TreeView/tree-view.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -13
- package/dist/index.js.map +1 -1
- package/dist/lib/drag-visual.d.ts +2 -2
- package/dist/lib/drag-visual.js.map +1 -1
- package/dist/lib/utils.d.ts +1 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +21 -1
- package/dist/lib/utils.js.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.d.ts +2 -0
- package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.js +2 -2
- package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
- package/dist/tokens/categorical-color.d.ts +3 -3
- package/dist/tokens/categorical-color.d.ts.map +1 -1
- package/dist/tokens/categorical-color.js +2 -2
- package/dist/tokens/categorical-color.js.map +1 -1
- package/ds-canonical/fork/AGENTS.md +213 -0
- package/ds-canonical/fork/codex/agents/skills/independent-review/SKILL.md +33 -0
- package/ds-canonical/fork/codex/hooks.json +16 -0
- package/ds-canonical/fork/governance.lock +22 -7
- package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +22 -1
- package/ds-canonical/fork/hooks/inject_deploy_url_after_push.sh +14 -2
- package/ds-canonical/fork/manifest.json +10 -1
- package/ds-canonical/fork/preamble.md +3 -2
- package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +7 -7
- package/ds-canonical/fork/skills/ux-audit/SKILL.md +1 -1
- package/ds-canonical/fork/skills/visual-audit/SKILL.md +0 -1
- package/ds-canonical/hooks/check_codex_brief_invariants.sh +9 -3
- package/ds-canonical/hooks/check_ds_anchor_preflight.sh +22 -1
- package/ds-canonical/hooks/check_overlay_open_focus_escape_probe.sh +19 -2
- package/ds-canonical/hooks/check_plugin_fork_health.sh +17 -1
- package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +21 -4
- package/ds-canonical/hooks/inject_deploy_url_after_push.sh +14 -2
- package/ds-canonical/hooks/session_start_governance_check.sh +10 -2
- package/ds-canonical/references/certified-surfaces.md +20 -0
- package/ds-canonical/references/preflight-gate-baseline.json +5 -0
- package/ds-canonical/references/principle-dim-map.json +20 -2
- package/ds-canonical/references/props-naming.md +2 -1
- package/ds-canonical/references/ssot-index.md +2 -2
- package/ds-canonical/references/story-baseline-registry.json +2 -2
- package/ds-canonical/rules/story-rules.md +1 -1
- package/ds-canonical/rules/ui-development.md +2 -1
- package/ds-canonical/skills/codex-collab/SKILL.md +7 -7
- package/ds-canonical/skills/codex-collab/references/brief-template.md +5 -1
- package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
- package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +5 -5
- package/ds-canonical/skills/design-system-audit/SKILL.md +5 -5
- package/ds-canonical/skills/product-ui-audit/SKILL.md +7 -7
- package/ds-canonical/skills/ux-audit/SKILL.md +1 -1
- package/ds-canonical/skills/visual-audit/SKILL.md +0 -1
- package/ds-story-manifest.json +21 -21
- package/llms-full.txt +3 -3
- package/llms.txt +1 -1
- package/package.json +2 -1
- package/src/components/Accordion/accordion.anatomy.stories.tsx +11 -11
- package/src/components/Accordion/accordion.principles.stories.tsx +21 -6
- package/src/components/Accordion/accordion.spec.md +10 -1
- package/src/components/Accordion/accordion.stories.tsx +10 -5
- package/src/components/Accordion/accordion.tsx +7 -2
- package/src/components/Alert/alert.principles.stories.tsx +2 -8
- package/src/components/AppShell/app-shell.anatomy.stories.tsx +19 -13
- package/src/components/AppShell/app-shell.principles.stories.tsx +21 -21
- package/src/components/AppShell/app-shell.spec.md +4 -0
- package/src/components/AppShell/app-shell.stories.tsx +4 -5
- package/src/components/Avatar/avatar.tsx +1 -1
- package/src/components/Badge/badge.spec.md +2 -2
- package/src/components/Breadcrumb/breadcrumb.anatomy.stories.tsx +7 -3
- package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +4 -1
- package/src/components/Breadcrumb/breadcrumb.stories.tsx +1 -1
- package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +6 -6
- package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +18 -6
- package/src/components/BulkActionBar/bulk-action-bar.spec.md +15 -5
- package/src/components/BulkActionBar/bulk-action-bar.stories.tsx +8 -24
- package/src/components/BulkActionBar/bulk-action-bar.tsx +4 -4
- package/src/components/Button/button-group.tsx +4 -4
- package/src/components/Button/button.anatomy.stories.tsx +26 -7
- package/src/components/Button/button.principles.stories.tsx +1 -1
- package/src/components/Button/button.spec.md +2 -2
- package/src/components/Button/button.stories.tsx +22 -28
- package/src/components/Button/button.tsx +2 -1
- package/src/components/Calendar/calendar.anatomy.stories.tsx +1 -1
- package/src/components/Calendar/calendar.spec.md +1 -1
- package/src/components/Carousel/carousel.spec.md +17 -1
- package/src/components/Carousel/carousel.tsx +93 -42
- package/src/components/Checkbox/checkbox-group.tsx +26 -15
- package/src/components/Checkbox/checkbox.anatomy.stories.tsx +3 -3
- package/src/components/Checkbox/checkbox.spec.md +12 -3
- package/src/components/Checkbox/checkbox.stories.tsx +4 -4
- package/src/components/Checkbox/checkbox.tsx +79 -19
- package/src/components/Coachmark/coachmark.anatomy.stories.tsx +5 -5
- package/src/components/Coachmark/coachmark.principles.stories.tsx +1 -1
- package/src/components/Combobox/combobox.anatomy.stories.tsx +11 -4
- package/src/components/Combobox/combobox.principles.stories.tsx +10 -11
- package/src/components/Combobox/combobox.spec.md +6 -2
- package/src/components/Combobox/combobox.stories.tsx +9 -36
- package/src/components/Combobox/combobox.tsx +42 -27
- package/src/components/Command/command.spec.md +3 -3
- package/src/components/Command/command.tsx +9 -2
- package/src/components/DataTable/cell-registry.tsx +75 -76
- package/src/components/DataTable/column-types.ts +4 -17
- package/src/components/DataTable/data-table-column-visibility-panel.tsx +20 -7
- package/src/components/DataTable/data-table-filter-panel.tsx +6 -6
- package/src/components/DataTable/data-table-interaction-layer.tsx +3 -3
- package/src/components/DataTable/data-table-sort-manager.tsx +2 -2
- package/src/components/DataTable/data-table.anatomy.stories.tsx +25 -24
- package/src/components/DataTable/data-table.css +2 -3
- package/src/components/DataTable/data-table.principles.stories.tsx +17 -11
- package/src/components/DataTable/data-table.spec.md +8 -7
- package/src/components/DataTable/data-table.stories.tsx +39 -29
- package/src/components/DataTable/data-table.tsx +90 -67
- package/src/components/DataTable/filter-operators.spec.md +2 -0
- package/src/components/DateGrid/date-grid.spec.md +1 -1
- package/src/components/DatePicker/date-picker.anatomy.stories.tsx +9 -9
- package/src/components/DatePicker/date-picker.principles.stories.tsx +4 -4
- package/src/components/DatePicker/date-picker.spec.md +10 -9
- package/src/components/DatePicker/date-picker.stories.tsx +9 -9
- package/src/components/DatePicker/date-picker.tsx +13 -13
- package/src/components/DescriptionList/description-list.anatomy.stories.tsx +5 -5
- package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
- package/src/components/DescriptionList/description-list.spec.md +12 -12
- package/src/components/DescriptionList/description-list.stories.tsx +3 -3
- package/src/components/DescriptionList/description-list.tsx +10 -10
- package/src/components/Dialog/dialog.tsx +4 -1
- package/src/components/DropdownMenu/dropdown-menu.tsx +27 -14
- package/src/components/Empty/empty.spec.md +0 -1
- package/src/components/Field/field-context.ts +17 -11
- package/src/components/Field/field-controls.spec.md +46 -37
- package/src/components/Field/field-types.ts +14 -12
- package/src/components/Field/field-wrapper.tsx +59 -43
- package/src/components/Field/field.anatomy.stories.tsx +6 -6
- package/src/components/Field/field.principles.stories.tsx +5 -28
- package/src/components/Field/field.spec.md +5 -4
- package/src/components/Field/field.stories.tsx +103 -72
- package/src/components/Field/field.tsx +36 -4
- package/src/components/Field/form-validation.spec.md +4 -0
- package/src/components/Field/use-form-validation.ts +1 -1
- package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +3 -3
- package/src/components/FieldControlGroup/field-control-group.spec.md +3 -3
- package/src/components/FileItem/file-item.tsx +10 -8
- package/src/components/FileUpload/file-upload.principles.stories.tsx +7 -7
- package/src/components/FileUpload/file-upload.spec.md +3 -2
- package/src/components/FileUpload/file-upload.tsx +5 -1
- package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +1 -1
- package/src/components/FileViewer/file-viewer.principles.stories.tsx +1 -1
- package/src/components/FileViewer/file-viewer.spec.md +1 -1
- package/src/components/FileViewer/file-viewer.stories.tsx +1 -1
- package/src/components/FileViewer/file-viewer.tsx +3 -2
- package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -4
- package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -1
- package/src/components/HoverCard/hover-card.spec.md +2 -2
- package/src/components/HoverCard/hover-card.tsx +4 -2
- package/src/components/InlineEdit/inline-edit.anatomy.stories.tsx +184 -0
- package/src/components/InlineEdit/inline-edit.principles.stories.tsx +142 -0
- package/src/components/InlineEdit/inline-edit.spec.md +71 -46
- package/src/components/InlineEdit/inline-edit.stories.tsx +20 -19
- package/src/components/InlineEdit/inline-edit.tsx +211 -125
- package/src/components/Input/input.anatomy.stories.tsx +15 -15
- package/src/components/Input/input.principles.stories.tsx +3 -3
- package/src/components/Input/input.spec.md +4 -4
- package/src/components/Input/input.stories.tsx +3 -3
- package/src/components/Input/input.tsx +13 -13
- package/src/components/LinkInput/link-input.anatomy.stories.tsx +2 -2
- package/src/components/LinkInput/link-input.principles.stories.tsx +1 -1
- package/src/components/LinkInput/link-input.spec.md +6 -2
- package/src/components/LinkInput/link-input.stories.tsx +8 -8
- package/src/components/LinkInput/link-input.tsx +21 -19
- package/src/components/Notice/notice.principles.stories.tsx +1 -1
- package/src/components/Notice/notice.tsx +8 -2
- package/src/components/NumberInput/number-input.anatomy.stories.tsx +6 -6
- package/src/components/NumberInput/number-input.principles.stories.tsx +4 -4
- package/src/components/NumberInput/number-input.spec.md +6 -6
- package/src/components/NumberInput/number-input.stories.tsx +4 -4
- package/src/components/NumberInput/number-input.tsx +17 -5
- package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +8 -4
- package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +6 -7
- package/src/components/OverflowIndicator/overflow-indicator.spec.md +1 -1
- package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +6 -30
- package/src/components/PeoplePicker/avatar-stack-overflow.ts +1 -1
- package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +13 -10
- package/src/components/PeoplePicker/people-picker.principles.stories.tsx +5 -1
- package/src/components/PeoplePicker/people-picker.stories.tsx +5 -5
- package/src/components/PeoplePicker/people-picker.tsx +39 -20
- package/src/components/PeoplePicker/person-display.tsx +3 -3
- package/src/components/Popover/popover.anatomy.stories.tsx +10 -7
- package/src/components/Popover/popover.principles.stories.tsx +49 -28
- package/src/components/Popover/popover.spec.md +4 -2
- package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
- package/src/components/ProfileCard/profile-card.tsx +1 -1
- package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +14 -13
- package/src/components/ProgressBar/progress-bar.principles.stories.tsx +8 -23
- package/src/components/ProgressBar/progress-bar.stories.tsx +8 -2
- package/src/components/README.md +1 -0
- package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +8 -3
- package/src/components/RadioGroup/radio-group.stories.tsx +3 -3
- package/src/components/RadioGroup/radio-group.tsx +19 -14
- package/src/components/Rating/rating.anatomy.stories.tsx +1 -1
- package/src/components/Rating/rating.principles.stories.tsx +3 -3
- package/src/components/Rating/rating.spec.md +2 -2
- package/src/components/Rating/rating.stories.tsx +4 -18
- package/src/components/Rating/rating.tsx +5 -1
- package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.spec.md +1 -1
- package/src/components/ScrollArea/scroll-area.tsx +5 -2
- package/src/components/SegmentedControl/segmented-control.principles.stories.tsx +4 -1
- package/src/components/SegmentedControl/segmented-control.spec.md +5 -10
- package/src/components/SegmentedControl/segmented-control.tsx +6 -3
- package/src/components/Select/select.anatomy.stories.tsx +13 -7
- package/src/components/Select/select.principles.stories.tsx +3 -3
- package/src/components/Select/select.spec.md +10 -1
- package/src/components/Select/select.stories.tsx +41 -27
- package/src/components/Select/select.tsx +44 -26
- package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +44 -26
- package/src/components/SelectMenu/select-menu.principles.stories.tsx +0 -1
- package/src/components/SelectMenu/select-menu.stories.tsx +7 -19
- package/src/components/SelectMenu/select-menu.tsx +38 -5
- package/src/components/SelectionControl/boolean-value.tsx +2 -2
- package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +1 -1
- package/src/components/SelectionControl/selection-item.principles.stories.tsx +4 -4
- package/src/components/Separator/separator.principles.stories.tsx +1 -1
- package/src/components/Sheet/sheet.principles.stories.tsx +5 -3
- package/src/components/Sheet/sheet.tsx +4 -1
- package/src/components/Sidebar/sidebar.tsx +1 -1
- package/src/components/Slider/slider.anatomy.stories.tsx +5 -4
- package/src/components/Slider/slider.principles.stories.tsx +6 -6
- package/src/components/Slider/slider.spec.md +13 -1
- package/src/components/Slider/slider.stories.tsx +44 -32
- package/src/components/Slider/slider.tsx +13 -4
- package/src/components/Steps/steps.anatomy.stories.tsx +7 -7
- package/src/components/Steps/steps.principles.stories.tsx +1 -1
- package/src/components/Steps/steps.spec.md +4 -2
- package/src/components/Steps/steps.stories.tsx +4 -56
- package/src/components/Steps/steps.tsx +31 -7
- package/src/components/Switch/switch.anatomy.stories.tsx +3 -3
- package/src/components/Switch/switch.principles.stories.tsx +1 -1
- package/src/components/Switch/switch.spec.md +4 -4
- package/src/components/Switch/switch.stories.tsx +3 -3
- package/src/components/Switch/switch.tsx +16 -10
- package/src/components/Tabs/tabs.anatomy.stories.tsx +6 -0
- package/src/components/Tabs/tabs.principles.stories.tsx +10 -2
- package/src/components/Tabs/tabs.spec.md +5 -2
- package/src/components/Tabs/tabs.stories.tsx +36 -44
- package/src/components/Tabs/tabs.tsx +101 -36
- package/src/components/Tag/tag.anatomy.stories.tsx +2 -2
- package/src/components/Tag/tag.principles.stories.tsx +4 -4
- package/src/components/Tag/tag.spec.md +4 -1
- package/src/components/Tag/tag.tsx +5 -3
- package/src/components/Textarea/textarea.anatomy.stories.tsx +6 -6
- package/src/components/Textarea/textarea.spec.md +5 -5
- package/src/components/Textarea/textarea.tsx +24 -24
- package/src/components/TimePicker/time-picker.anatomy.stories.tsx +12 -11
- package/src/components/TimePicker/time-picker.principles.stories.tsx +37 -44
- package/src/components/TimePicker/time-picker.spec.md +6 -5
- package/src/components/TimePicker/time-picker.stories.tsx +3 -3
- package/src/components/TimePicker/time-picker.tsx +24 -14
- package/src/components/Toast/toast.principles.stories.tsx +1 -1
- package/src/components/Toast/toast.stories.tsx +1 -1
- package/src/components/Tooltip/tooltip.anatomy.stories.tsx +15 -6
- package/src/components/Tooltip/tooltip.principles.stories.tsx +4 -4
- package/src/components/Tooltip/tooltip.spec.md +9 -3
- package/src/components/Tooltip/tooltip.stories.tsx +1 -1
- package/src/components/Tooltip/tooltip.tsx +12 -2
- package/src/components/TreeView/tree-view.anatomy.stories.tsx +16 -14
- package/src/components/TreeView/tree-view.principles.stories.tsx +44 -11
- package/src/components/TreeView/tree-view.spec.md +8 -6
- package/src/components/TreeView/tree-view.stories.tsx +9 -43
- package/src/components/TreeView/tree-view.tsx +36 -0
- package/src/index.ts +13 -10
- package/src/lib/drag-visual.ts +2 -2
- package/src/lib/utils.ts +19 -0
- package/src/patterns/action-bar/action-bar.spec.md +1 -0
- package/src/patterns/element-anatomy/inline-action.spec.md +1 -1
- package/src/patterns/element-anatomy/item-anatomy.spec.md +4 -0
- package/src/patterns/element-anatomy/item-anatomy.tsx +4 -2
- package/src/patterns/header-canonical/header-canonical.spec.md +9 -0
- package/src/patterns/header-canonical/header-canonical.stories.tsx +31 -5
- package/src/tokens/categorical-color.ts +5 -5
- package/src/tokens/color/color.spec.md +4 -4
- package/src/tokens/color/primitives.css +9 -2
- package/src/tokens/motion/motion.spec.md +6 -7
- package/src/tokens/opacity/opacity.spec.md +1 -1
- package/src/tokens/orphan-tokens.spec.md +1 -1
- package/src/tokens/typography/typography.spec.md +2 -0
- package/src/tokens/uiSize/uiSize.css +5 -0
- package/src/tokens/uiSize/uiSize.spec.md +1 -1
|
@@ -63,18 +63,33 @@ const dataWithNotes: Product[] = sampleData.map((p, i) => ({
|
|
|
63
63
|
: 'Standard delivery.',
|
|
64
64
|
}))
|
|
65
65
|
|
|
66
|
+
// 真實商品名池(品名 × 規格版本組合,避免 "Product item N" 佔位符)
|
|
67
|
+
const PRODUCT_NAMES: Record<string, string[]> = {
|
|
68
|
+
Electronics: ['Wireless Bluetooth Headphones', 'USB-C 7-in-1 Hub', 'Mechanical Keyboard', '4K Webcam', 'Portable SSD 1TB', '65W GaN Charger', 'Noise-Cancelling Earbuds', 'Ergonomic Wireless Mouse'],
|
|
69
|
+
Furniture: ['Ergonomic Office Chair', 'Standing Desk Converter', '5-Tier Bookshelf', 'Storage Cabinet', '3-Seat Fabric Sofa', 'Bedside Table', 'Coat Rack Stand', 'Adjustable Monitor Arm'],
|
|
70
|
+
Food: ['Organic Green Tea 100 Bags', 'Single-Origin Coffee Beans', 'Mixed Nuts Gift Box', 'Dark Chocolate Bar', 'Rolled Oats 1kg', 'Manuka Honey Jar', 'Herbal Tea Sampler', 'Cold-Pressed Olive Oil'],
|
|
71
|
+
Lifestyle: ['Stainless Steel Water Bottle', 'Scented Candle Set', 'Cordless Vacuum Cleaner', 'HEPA Air Purifier', 'Leather Notebook A5', 'Non-Slip Yoga Mat', 'Aroma Diffuser', 'Weighted Blanket'],
|
|
72
|
+
}
|
|
73
|
+
const PRODUCT_EDITIONS = ['Standard', 'Pro', 'Lite', 'Max', 'Mini', '2-Pack', 'Bundle', 'Refill']
|
|
74
|
+
|
|
66
75
|
function generateLargeData(count: number): Product[] {
|
|
67
76
|
const categories = ['Electronics', 'Furniture', 'Food', 'Lifestyle']
|
|
68
77
|
const stocks = ['In stock', 'Low stock', 'Out of stock', 'Pre-order']
|
|
69
|
-
return Array.from({ length: count }, (_, i) =>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
return Array.from({ length: count }, (_, i) => {
|
|
79
|
+
const category = categories[i % 4]
|
|
80
|
+
const names = PRODUCT_NAMES[category]
|
|
81
|
+
const baseName = names[i % names.length]
|
|
82
|
+
const edition = PRODUCT_EDITIONS[Math.floor(i / names.length) % PRODUCT_EDITIONS.length]
|
|
83
|
+
return {
|
|
84
|
+
sku: `PRD-${String(i + 1).padStart(4, '0')}`,
|
|
85
|
+
name: `${baseName} (${edition})`,
|
|
86
|
+
category,
|
|
87
|
+
stock: stocks[i % 4],
|
|
88
|
+
seller: SELLERS[i % SELLERS.length],
|
|
89
|
+
updatedAt: `2025/03/${String(1 + (i % 28)).padStart(2, '0')}`,
|
|
90
|
+
price: Math.round(100 + Math.random() * 9900),
|
|
91
|
+
}
|
|
92
|
+
})
|
|
78
93
|
}
|
|
79
94
|
|
|
80
95
|
// ── Column Definitions ───────────────────────────────────────────────────────
|
|
@@ -230,7 +245,7 @@ export const ColumnReorder: Story = {
|
|
|
230
245
|
|
|
231
246
|
/* ── 行高模式 — autoRowHeight prop(每 row 內容驅動高度) ── */
|
|
232
247
|
export const RowAutoHeightInlineEdit: Story = {
|
|
233
|
-
name: '自動行高 ×
|
|
248
|
+
name: '自動行高 × 就地編輯',
|
|
234
249
|
render: () => {
|
|
235
250
|
// 2026-05-14 I7 fix(per codex verdict):note 初始化進 state 一次,commit 寫同一 state。
|
|
236
251
|
// 原 `dataWithNotes = list.map(...)` 每 render derive 覆蓋 commit value → user 看似不能編。
|
|
@@ -258,7 +273,7 @@ export const RowAutoHeightInlineEdit: Story = {
|
|
|
258
273
|
<div className="max-w-5xl">
|
|
259
274
|
<p className="text-caption text-fg-muted mb-2">
|
|
260
275
|
autoRowHeight=true。Note 欄位 wrap text 撐高 row。其他單行 cell 在高 row 中應**頂對齊**。
|
|
261
|
-
Click 任一 cell 進 edit:文字位置
|
|
276
|
+
Click 任一 cell 進 edit:文字位置 view↔edit 不偏移(仍頂對齊),
|
|
262
277
|
frame 填 cell,Field 自帶 state ring(focus-within → primary)。
|
|
263
278
|
</p>
|
|
264
279
|
<DataTable
|
|
@@ -594,7 +609,7 @@ export const InlineEdit: Story = {
|
|
|
594
609
|
* 3. `spreadsheetMode` — Excel-like cell selection: click 1=select / click 2=edit /
|
|
595
610
|
* Shift+click=range / ArrowKeys=nav
|
|
596
611
|
* 4. `experimentalActiveEditorController` — portal Field active editor(Slice D Step 5 D.3,
|
|
597
|
-
* cell 永遠 mode="
|
|
612
|
+
* cell 永遠 mode="view" SSOT preserved + portal host renders mode="edit")
|
|
598
613
|
*
|
|
599
614
|
* 驗證點:
|
|
600
615
|
* - hover editable cell → 1px overlay 邊框 var(--border-hover)
|
|
@@ -651,7 +666,7 @@ export const InlineEditWithSpreadsheetOverlay: Story = {
|
|
|
651
666
|
|
|
652
667
|
/**
|
|
653
668
|
* Issue 9 cell error system(2026-05-10):consumer-supplied `cellErrors` map shows error message
|
|
654
|
-
* 14px text-error 在
|
|
669
|
+
* 14px text-error 在 view content 下方,gap-1 spacing。Edit cell 自動 clear visual error。
|
|
655
670
|
* Multi-error 用 array → ul li 分行。aria-describedby + aria-invalid for AT。
|
|
656
671
|
*
|
|
657
672
|
* 對齊 AG Grid `cellClassRules='ag-cell-error'` + Material X-DataGrid `errorMessage` cell prop +
|
|
@@ -1002,7 +1017,6 @@ export const WithBulkActions: Story = {
|
|
|
1002
1017
|
</PopoverTrigger>
|
|
1003
1018
|
<PopoverContent align="end" className="w-auto p-0">
|
|
1004
1019
|
<DataTableFilterPanel
|
|
1005
|
-
mode="flat"
|
|
1006
1020
|
columns={baseColumns}
|
|
1007
1021
|
value={filterTree}
|
|
1008
1022
|
onChange={setFilterTree}
|
|
@@ -1132,7 +1146,7 @@ export const WithBulkActions: Story = {
|
|
|
1132
1146
|
<BulkActionBar
|
|
1133
1147
|
selection={visibleSelectedIds}
|
|
1134
1148
|
totalSelected={selectedCount}
|
|
1135
|
-
|
|
1149
|
+
onClearSelection={() => setSelection({ mode: 'include', ids: [] })}
|
|
1136
1150
|
actions={
|
|
1137
1151
|
<>
|
|
1138
1152
|
<Button variant="tertiary" size="md" startIcon={Download}>下載</Button>
|
|
@@ -1266,7 +1280,6 @@ export const FilterPanelEmpty: Story = {
|
|
|
1266
1280
|
return (
|
|
1267
1281
|
<div className="w-full max-w-[680px]">
|
|
1268
1282
|
<DataTableFilterPanel
|
|
1269
|
-
mode="flat"
|
|
1270
1283
|
columns={[...FILTER_COLUMNS]}
|
|
1271
1284
|
value={value}
|
|
1272
1285
|
onChange={setValue}
|
|
@@ -1292,7 +1305,6 @@ export const FilterPanelWithConditions: Story = {
|
|
|
1292
1305
|
return (
|
|
1293
1306
|
<div className="w-full max-w-[680px]">
|
|
1294
1307
|
<DataTableFilterPanel
|
|
1295
|
-
mode="flat"
|
|
1296
1308
|
columns={[...FILTER_COLUMNS]}
|
|
1297
1309
|
value={value}
|
|
1298
1310
|
onChange={setValue}
|
|
@@ -1329,7 +1341,6 @@ export const FilterPanelNested: Story = {
|
|
|
1329
1341
|
return (
|
|
1330
1342
|
<div className="w-full max-w-[680px]">
|
|
1331
1343
|
<DataTableFilterPanel
|
|
1332
|
-
mode="nested"
|
|
1333
1344
|
columns={[...FILTER_COLUMNS]}
|
|
1334
1345
|
value={value}
|
|
1335
1346
|
onChange={setValue}
|
|
@@ -1354,7 +1365,6 @@ export const FilterPanelRelativeDate: Story = {
|
|
|
1354
1365
|
<div className="w-full max-w-[680px]">
|
|
1355
1366
|
<p className="text-caption text-fg-muted mb-3">時間下拉分 過去 / 目前 / 未來 三組(Linear/Notion 共識)。</p>
|
|
1356
1367
|
<DataTableFilterPanel
|
|
1357
|
-
mode="flat"
|
|
1358
1368
|
columns={[...FILTER_COLUMNS]}
|
|
1359
1369
|
value={value}
|
|
1360
1370
|
onChange={setValue}
|
|
@@ -1382,7 +1392,6 @@ export const FilterPanelModified: Story = {
|
|
|
1382
1392
|
<div className="w-full max-w-[680px]">
|
|
1383
1393
|
<p className="text-caption text-fg-muted mb-3">值偏離 default 時 header 出現 ↻ — 點擊 reset 回 default。</p>
|
|
1384
1394
|
<DataTableFilterPanel
|
|
1385
|
-
mode="flat"
|
|
1386
1395
|
columns={[...FILTER_COLUMNS]}
|
|
1387
1396
|
value={value}
|
|
1388
1397
|
defaultValue={initial}
|
|
@@ -1424,7 +1433,6 @@ export const FilterPanelLongTagOverflow: Story = {
|
|
|
1424
1433
|
<div className="w-full max-w-[680px]">
|
|
1425
1434
|
<p className="text-caption text-fg-muted mb-3">產品分類名稱很長(製造業 ERP 常見)時,已選的多個標籤超出單行寬度,Combobox 自動收斂為 +N 摘要。</p>
|
|
1426
1435
|
<DataTableFilterPanel
|
|
1427
|
-
mode="flat"
|
|
1428
1436
|
columns={[...longLabelColumns]}
|
|
1429
1437
|
value={value}
|
|
1430
1438
|
onChange={setValue}
|
|
@@ -1603,6 +1611,9 @@ const ROADMAP_DATA: RoadmapItem[] = [
|
|
|
1603
1611
|
{ id: 'RDM-008', title: 'Dark mode 全站支援', status: 'in-progress', priority: 'P2', owner: SELLERS[1], reviewers: [SELLERS[0], SELLERS[3]], tags: ['frontend', 'design'], startDate: '2026-04-20', dueDate: '2026-06-10', progress: 75, spec: 'https://notion.so/dark-mode', hours: 90, standup: '10:00' },
|
|
1604
1612
|
]
|
|
1605
1613
|
|
|
1614
|
+
// 大量 roadmap row 衍生用的真實工作範疇後綴(避免 "(#N)" 佔位符計數)
|
|
1615
|
+
const ROADMAP_SCOPES = ['行動版', '桌面版', 'API 層', '後台管理', '資料遷移', '效能優化', '無障礙改善', '國際化', '監控告警', '權限控管']
|
|
1616
|
+
|
|
1606
1617
|
// Roadmap shared columns helper(主 demo + 5 stress stories 共用)
|
|
1607
1618
|
function useRoadmapColumns() {
|
|
1608
1619
|
const col = createColumnHelper<RoadmapItem>()
|
|
@@ -1666,13 +1677,13 @@ export const RoadmapAllInOne: Story = {
|
|
|
1666
1677
|
const item: RoadmapItem = {
|
|
1667
1678
|
...base,
|
|
1668
1679
|
id,
|
|
1669
|
-
title: `${base.title}
|
|
1680
|
+
title: `${base.title} — ${ROADMAP_SCOPES[i % ROADMAP_SCOPES.length]}`,
|
|
1670
1681
|
}
|
|
1671
1682
|
// 前 8 個 top row 補 sub-tasks(展示 nested row + drag canonical:top-level only)
|
|
1672
1683
|
if (i < 8) {
|
|
1673
1684
|
item.children = [
|
|
1674
|
-
{ ...base, id: `${id}-1`, title: `${base.title}
|
|
1675
|
-
{ ...base, id: `${id}-2`, title: `${base.title}
|
|
1685
|
+
{ ...base, id: `${id}-1`, title: `${base.title}:介面設計與前端實作`, progress: Math.min(100, base.progress + 10), hours: Math.max(0, Math.floor(base.hours / 3)) },
|
|
1686
|
+
{ ...base, id: `${id}-2`, title: `${base.title}:API 整合與 QA 驗收`, progress: Math.max(0, base.progress - 10), hours: Math.max(0, Math.floor(base.hours / 4)) },
|
|
1676
1687
|
]
|
|
1677
1688
|
}
|
|
1678
1689
|
arr.push(item)
|
|
@@ -1762,7 +1773,6 @@ export const RoadmapAllInOne: Story = {
|
|
|
1762
1773
|
</PopoverTrigger>
|
|
1763
1774
|
<PopoverContent align="end" className="w-auto p-0">
|
|
1764
1775
|
<DataTableFilterPanel
|
|
1765
|
-
mode="flat"
|
|
1766
1776
|
columns={columns}
|
|
1767
1777
|
value={filterTree}
|
|
1768
1778
|
onChange={setFilterTree}
|
|
@@ -1867,7 +1877,7 @@ export const RoadmapAllInOne: Story = {
|
|
|
1867
1877
|
{selection.length > 0 && (
|
|
1868
1878
|
<BulkActionBar
|
|
1869
1879
|
selection={selection}
|
|
1870
|
-
|
|
1880
|
+
onClearSelection={() => setSelection([])}
|
|
1871
1881
|
actions={
|
|
1872
1882
|
<>
|
|
1873
1883
|
<Button variant="tertiary" size="md" startIcon={Download}>匯出</Button>
|
|
@@ -1901,7 +1911,7 @@ export const RoadmapAllInOne: Story = {
|
|
|
1901
1911
|
scripts/runtime-perf-datatable.mjs:20 仍指向本 story,gate 形同不可跑):──────────────────
|
|
1902
1912
|
獨立 perf 量測 story — 同 Roadmap 13 cols rich-cell data(useRoadmapColumns SSOT),但**禁用**
|
|
1903
1913
|
row drag / column reorder / column resize / selection / spreadsheet overlay,只保 inline edit
|
|
1904
|
-
|
|
1914
|
+
view。目的:隔離 feature-stack 疊加 cost(SortableRowProvider / filter / sort / columnOrder
|
|
1905
1915
|
state),驗證 rich cell 本體 scroll budget(avg ≤ 50ms / p95 ≤ 80ms / long-task ≤ 1,CPU 4x
|
|
1906
1916
|
throttle)。**不動** RoadmapAllInOne demo IA(user 2026-05-10 directive 全合一)。
|
|
1907
1917
|
重建版去掉原手刻說明 banner(px-loose + border-b border-divider = R9 chrome-header drift
|
|
@@ -1916,7 +1926,7 @@ export const RoadmapPerfBudget: Story = {
|
|
|
1916
1926
|
for (let i = 0; i < 500; i++) {
|
|
1917
1927
|
const base = ROADMAP_DATA[i % ROADMAP_DATA.length]
|
|
1918
1928
|
const id = `RDM-${String(i + 100).padStart(3, '0')}`
|
|
1919
|
-
arr.push({ ...base, id, title: `${base.title}
|
|
1929
|
+
arr.push({ ...base, id, title: `${base.title} — ${ROADMAP_SCOPES[i % ROADMAP_SCOPES.length]}` })
|
|
1920
1930
|
}
|
|
1921
1931
|
return arr
|
|
1922
1932
|
}, [])
|
|
@@ -1956,7 +1966,7 @@ const ORDER_ROWS: OrderRow[] = Array.from({ length: 128 }, (_, i) => ({
|
|
|
1956
1966
|
const orderColumns: ColumnDef<OrderRow>[] = [
|
|
1957
1967
|
{ accessorKey: 'orderNo', header: '訂單編號' },
|
|
1958
1968
|
{ accessorKey: 'customer', header: '客戶' },
|
|
1959
|
-
{ accessorKey: 'amount', header: '金額',
|
|
1969
|
+
{ accessorKey: 'amount', header: '金額', meta: { type: 'currency', prefix: '$' } },
|
|
1960
1970
|
{ accessorKey: 'placedAt', header: '成立日期' },
|
|
1961
1971
|
]
|
|
1962
1972
|
|
|
@@ -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
|
|