@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.
|
|
2
|
-
// @renderer-symmetry-allow: ComboboxTagStack(
|
|
2
|
+
// @renderer-symmetry-allow: ComboboxTagStack(view path)接 consumer tagRenderer 是 Stream C 下 cycle 工作 — 2026-05-12 先 ship Issues 2/3/4 surgical fixes(placeholder vocabulary + cell surface metrics + placeholder truncate),tagRenderer view-path unify deferred per field-controls.spec.md 共享 contract a。當前 multi=1 顯示已透過 PeoplePicker tagRenderer(people-picker.tsx:426-444)PersonDisplay SSOT 對齊;其他 Combobox consumer 走 default `<Tag>` 純文字 backward-compat。
|
|
3
3
|
// code-quality-allow: file-size — Combobox 含 NativeCombobox/CustomCombobox/useOverflowCount/OverflowTagList/ComboboxTagStack 5 子元件 + 共用 helpers,split-into-files 會破壞 measurement closures + 重複 type definitions。
|
|
4
4
|
import * as React from 'react'
|
|
5
5
|
import { X, ChevronDown } from 'lucide-react'
|
|
@@ -237,7 +237,7 @@ interface OverflowTagListProps {
|
|
|
237
237
|
* 2026-05-14 I4 fix(per codex M31 verdict + user 抓「display overflow 有 avatar / edit 無」):
|
|
238
238
|
* Optional renderer for hidden items in `+N` overflow popover。Default fallback = `<Tag>{label}</Tag>`
|
|
239
239
|
* (純文字 chip,backward-compat)。Consumer pass 此 prop 讓 hidden items 顯示同 avatar 視覺
|
|
240
|
-
* (對齊
|
|
240
|
+
* (對齊 view path MultiPersonDisplay overflow popover Tag avatar SSOT)。
|
|
241
241
|
*/
|
|
242
242
|
renderHiddenTag?: (item: { value: string; label: string }) => React.ReactNode
|
|
243
243
|
onRemove?: (value: string) => void
|
|
@@ -297,7 +297,7 @@ function OverflowTagList({ containerRef, items, size, wrap, renderTag, renderHid
|
|
|
297
297
|
{items.map((item, i) => (
|
|
298
298
|
// 2026-05-14 I5 fix(per codex M31 verdict + user 抓「avatar stack 堆疊方向不一致」):
|
|
299
299
|
// 加 z-index per-index — 前 item z 高(對齊 MultiPersonDisplay zIndex: visible.length - i
|
|
300
|
-
// canonical + MUI AvatarGroup surplus pattern)。
|
|
300
|
+
// canonical + MUI AvatarGroup surplus pattern)。view + edit stack 堆疊方向統一。
|
|
301
301
|
<div key={item.value} ref={el => { tagEls.current[i] = el }} className={cn('shrink-0 max-w-full', tagWrapperClassName)} style={{ zIndex: items.length - i }}>{renderTag(item, i)}</div>
|
|
302
302
|
))}
|
|
303
303
|
<div ref={overflowEl} className={cn('shrink-0', overflowWrapperClassName)}>
|
|
@@ -316,9 +316,9 @@ function OverflowTagList({ containerRef, items, size, wrap, renderTag, renderHid
|
|
|
316
316
|
)
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
-
// ── Internal tag-stack renderer (consumed by ReadonlyMultiSelect / mode='
|
|
319
|
+
// ── Internal tag-stack renderer (consumed by ReadonlyMultiSelect / mode='view') ───
|
|
320
320
|
//
|
|
321
|
-
// Phase B2(2026-05-05):原 ComboboxDisplay sub-component 已 retire,改 inline `<Combobox mode="
|
|
321
|
+
// Phase B2(2026-05-05):原 ComboboxDisplay sub-component 已 retire,改 inline `<Combobox mode="view">`。
|
|
322
322
|
// 本 helper 只負責 tag-stack 內容渲染(OverflowTagList 消費),不包 Field wrapper。
|
|
323
323
|
function ComboboxTagStack({
|
|
324
324
|
value, options, tagSize = 'md', wrap = false, containerRef: externalRef, disabled = false,
|
|
@@ -328,7 +328,7 @@ function ComboboxTagStack({
|
|
|
328
328
|
}) {
|
|
329
329
|
const emptyDisplay = useFieldEmptyDisplay()
|
|
330
330
|
const ownRef = React.useRef<HTMLDivElement>(null)
|
|
331
|
-
if (!value || value.length === 0) return <span className={fieldEmptyColorClass(disabled ? 'disabled' : '
|
|
331
|
+
if (!value || value.length === 0) return <span className={fieldEmptyColorClass(disabled ? 'disabled' : 'view')}>{emptyDisplay}</span>
|
|
332
332
|
const items = value.map(v => ({ value: v, label: options?.find(o => o.value === v)?.label ?? v }))
|
|
333
333
|
const disabledClass = disabled ? 'bg-disabled text-fg-disabled' : undefined
|
|
334
334
|
|
|
@@ -345,10 +345,10 @@ function ComboboxTagStack({
|
|
|
345
345
|
)
|
|
346
346
|
|
|
347
347
|
if (externalRef) return content
|
|
348
|
-
// 2026-05-05 v9 fix(Bug 4):
|
|
348
|
+
// 2026-05-05 v9 fix(Bug 4):view path 內 wrapper 必須 `flex-1 min-w-0`,否則在 cell flex
|
|
349
349
|
// parent 下不認領完整可用寬度 → OverflowTagList 量得寬度小於 edit path → 顯 `+N` 多於 edit。
|
|
350
350
|
// edit path tagAreaRef wrapper 已是 `flex-1 min-w-0`(NativeCombobox/CustomCombobox line 258 / 354),
|
|
351
|
-
//
|
|
351
|
+
// view 必對稱才 SSOT。
|
|
352
352
|
// 2026-05-15 F1 Q3 fix(per user round 3 verbatim「單人選取時 Tag 越界蓋 indicator」):
|
|
353
353
|
// `overflow-visible` → `overflow-hidden` 讓 narrow cell width 強制 clip(Tag 內建 truncate
|
|
354
354
|
// 處理 text ellipsis,stack `-ml-0.5` 負 margin 在 wrapper 內不受影響)。對齊
|
|
@@ -384,8 +384,9 @@ export interface ComboboxProps {
|
|
|
384
384
|
/** 啟用搜尋 */
|
|
385
385
|
searchable?: boolean
|
|
386
386
|
/** Loading state(2026-05-15 audit B fix per user verbatim「dropdown 隨時可開,讀取在 panel 中間 CircularProgress」)。
|
|
387
|
-
* Forward 給 SelectMenu primitive SSOT;
|
|
388
|
-
* Trigger 不變(user 隨時可開)
|
|
387
|
+
* Forward 給 SelectMenu primitive SSOT;spinner 只在無可顯示選項時佔 CommandEmpty slot 顯 CircularProgress
|
|
388
|
+
* (已有 options 保留顯示,不取代)。Trigger 不變(user 隨時可開)。
|
|
389
|
+
* 對齊 MUI Autocomplete「loading 只在無 suggestions 時顯 loadingText」+ Field SSOT + Empty 元件 compose。*/
|
|
389
390
|
loading?: boolean
|
|
390
391
|
/** 搜尋框位置:menu(浮層內,預設)或 trigger(inline input) */
|
|
391
392
|
searchIn?: 'menu' | 'trigger'
|
|
@@ -397,6 +398,14 @@ export interface ComboboxProps {
|
|
|
397
398
|
emptyPlaceholder?: string
|
|
398
399
|
/** 搜尋無結果提示(2026-07-04 Q4 拍板接線)— forward SelectMenu primitive SSOT(default「沒有符合的選項」) */
|
|
399
400
|
emptyText?: string
|
|
401
|
+
/** 可建立新選項(creatable tag,2026-07-18 user 拍板 forward)—— 搜尋非空且無完全同名既有選項時,
|
|
402
|
+
* dropdown 顯 create row(Plus + `createLabel`);forward 給底層 SelectMenu(邏輯/顯示/互動 SSOT 住在 SelectMenu)。
|
|
403
|
+
* 僅 searchable 桌機路徑生效(需打字);native mobile 路徑不支援。對齊 Ant tags / react-select Creatable。 */
|
|
404
|
+
creatable?: boolean
|
|
405
|
+
/** 建立新選項 callback(forward SelectMenu `onCreate`)。 */
|
|
406
|
+
onCreate?: (value: string) => void
|
|
407
|
+
/** create row 文字格式(forward SelectMenu `createLabel`,預設「直接使用「{query}」」)。 */
|
|
408
|
+
createLabel?: (query: string) => string
|
|
400
409
|
/** a11y:無 Field wrapper 時提供 role='combobox' 的 accessible name(axe aria-input-field-name) */
|
|
401
410
|
'aria-label'?: string
|
|
402
411
|
/** Initial open state(uncontrolled)— 對齊 Select.defaultOpen / Radix Popover canonical。
|
|
@@ -416,8 +425,8 @@ export interface ComboboxProps {
|
|
|
416
425
|
tagRenderer?: (item: { value: string; label: string }, onRemove: () => void) => React.ReactNode
|
|
417
426
|
/**
|
|
418
427
|
* 2026-05-14 I4 fix:Optional renderer for hidden items in `+N` overflow popover
|
|
419
|
-
* (對齊
|
|
420
|
-
* pass 此 prop 讓 hidden items 顯 avatar + name(同
|
|
428
|
+
* (對齊 view path MultiPersonDisplay overflow popover 含 avatar SSOT)。PeoplePicker stack
|
|
429
|
+
* pass 此 prop 讓 hidden items 顯 avatar + name(同 view path)。Default fallback
|
|
421
430
|
* `<Tag>{label}</Tag>` 純文字 backward-compat。
|
|
422
431
|
*/
|
|
423
432
|
renderHiddenTag?: (item: { value: string; label: string }) => React.ReactNode
|
|
@@ -477,8 +486,8 @@ export interface ComboboxProps {
|
|
|
477
486
|
visibleCountOverride?: number
|
|
478
487
|
/**
|
|
479
488
|
* Display 是否渲 ChevronDown + Field naked wrapper(D-path opt-in,2026-05-08)
|
|
480
|
-
* — DataTable cell
|
|
481
|
-
* 設 true 時
|
|
489
|
+
* — DataTable cell view↔edit 像素級對齊用。預設 false(裸 tag stack,backward compat)。
|
|
490
|
+
* 設 true 時 view 走 fieldWrapperStyles(naked variant)+ ItemSuffix ChevronDown,
|
|
482
491
|
* 與 edit 同 DOM 結構,消除 Layer-B padding mismatch。
|
|
483
492
|
*/
|
|
484
493
|
showDisplayEndIcon?: boolean
|
|
@@ -487,7 +496,7 @@ export interface ComboboxProps {
|
|
|
487
496
|
// 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反)
|
|
488
497
|
const getIconSize = (size: string) => ICON_SIZE[size as 'sm' | 'md' | 'lg']
|
|
489
498
|
|
|
490
|
-
// ── Shared readonly/disabled/
|
|
499
|
+
// ── Shared readonly/disabled/view render ─────────────────────────────────
|
|
491
500
|
|
|
492
501
|
function ReadonlyMultiSelect({
|
|
493
502
|
mode, variant: variantProp, width, size, options, value, wrap, className, showDisplayEndIcon = false,
|
|
@@ -504,13 +513,13 @@ function ReadonlyMultiSelect({
|
|
|
504
513
|
const containerRef = React.useRef<HTMLDivElement>(null)
|
|
505
514
|
const hasTags = (value?.length ?? 0) > 0
|
|
506
515
|
|
|
507
|
-
// mode='
|
|
516
|
+
// mode='view'(Phase B2 2026-05-05):純內容輸出 — tag stack 不包 Field wrapper / 不 reserve 高度。
|
|
508
517
|
// 對齊原 ComboboxDisplay sub-component(retired)。
|
|
509
518
|
// Opt-in(showDisplayEndIcon=true,2026-05-08 D-path):Field naked wrapper + ItemSuffix ChevronDown,
|
|
510
|
-
// 與 edit 同結構消除 cell
|
|
511
|
-
if (resolvedMode === '
|
|
519
|
+
// 與 edit 同結構消除 cell view↔edit 像素偏移(Layer-B padding mismatch)。
|
|
520
|
+
if (resolvedMode === 'view') {
|
|
512
521
|
if (!showDisplayEndIcon) {
|
|
513
|
-
// 2026-05-14 I2 fix(spec contract (e)
|
|
522
|
+
// 2026-05-14 I2 fix(spec contract (e) view typography canonical):empty bare span 套
|
|
514
523
|
// `fieldDisplayTextClass(sz)`(sm/md→text-body,lg→text-body-lg)— 對齊跨 Field family 統一。
|
|
515
524
|
if (!hasTags) return <span className={cn(fieldDisplayTextClass(sz), fieldEmptyColorClass(resolvedMode), className)}>{emptyDisplay}</span>
|
|
516
525
|
return (
|
|
@@ -519,8 +528,8 @@ function ReadonlyMultiSelect({
|
|
|
519
528
|
}
|
|
520
529
|
return (
|
|
521
530
|
<div
|
|
522
|
-
className={cn(fieldWrapperStyles({ mode: '
|
|
523
|
-
data-field-mode="
|
|
531
|
+
className={cn(fieldWrapperStyles({ mode: 'view', variant, width, size: sz }), hasTags && tagPadding[sz], className)}
|
|
532
|
+
data-field-mode="view"
|
|
524
533
|
>
|
|
525
534
|
{hasTags ? (
|
|
526
535
|
<ComboboxTagStack value={value} options={options} tagSize={sz} wrap={wrap} />
|
|
@@ -538,8 +547,8 @@ function ReadonlyMultiSelect({
|
|
|
538
547
|
<div ref={containerRef}
|
|
539
548
|
className={cn(fieldWrapperStyles({ mode: resolvedMode, variant, width, size: sz }), hasTags && tagPadding[sz],
|
|
540
549
|
// 2026-05-18 #6A Round 1 Step 1/4(per user 拍板「決策6選a」+ codex M31 Step 5 verdict cite combobox.tsx:451):
|
|
541
|
-
// readonly/disabled path 對齊 L293
|
|
542
|
-
// M10 propagation:原 overflow-visible 讓 readonly tag 越界蓋 indicator,跟
|
|
550
|
+
// readonly/disabled path 對齊 L293 view wrapper 已 ship 的 overflow-hidden fix。
|
|
551
|
+
// M10 propagation:原 overflow-visible 讓 readonly tag 越界蓋 indicator,跟 view 不對稱。
|
|
543
552
|
// 2026-06-27 對齊 edit path(L598-617):wrap 時 items-start + chevron self-start/tagHeight 鎖第一行;
|
|
544
553
|
// paddingRight: var(--field-px) re-assert 右緣 12px(tagPadding 對稱 calc 會吃掉右緣,跟 edit 一致)。
|
|
545
554
|
wrap ? 'flex-wrap items-start py-1' : 'overflow-hidden', className)}
|
|
@@ -586,7 +595,7 @@ function NativeCombobox({
|
|
|
586
595
|
const fieldCtx = useFieldContext()
|
|
587
596
|
const disabled = useResolvedFieldDisabled(disabledProp)
|
|
588
597
|
const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)
|
|
589
|
-
// 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode="
|
|
598
|
+
// 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode="view"> 漏 cascade
|
|
590
599
|
const resolvedMode = useResolvedFieldMode({ mode, disabled })
|
|
591
600
|
const iconSize = getIconSize(size)
|
|
592
601
|
const showClear = clearable && value.length > 0 && resolvedMode === 'edit'
|
|
@@ -633,7 +642,7 @@ function NativeCombobox({
|
|
|
633
642
|
onClick={(e) => { if (e.target === e.currentTarget) { selectRef.current?.showPicker?.(); selectRef.current?.focus() } }}>
|
|
634
643
|
{/* 2026-05-18 F2 sync(per user verbatim「modifying 修好 PeoplePicker stack 後改壞 Combobox tag display」
|
|
635
644
|
+ 「tag 應該要判斷所在空間最多可以呈現幾個tag(包括+n)去自動判斷何時要變成+n」):
|
|
636
|
-
edit path tagArea 對齊
|
|
645
|
+
edit path tagArea 對齊 view path L293 已 ship 的 `overflow-hidden` fix。原 `overflow-visible`
|
|
637
646
|
讓 tag 視覺越界蓋 chevron / +N indicator(useOverflowCount measurement 對但 CSS overflow 仍露)。
|
|
638
647
|
M10 violation root cause:2026-05-15 F1 Q3 只 fix display path,edit + Native(L518)沒同步。 */}
|
|
639
648
|
<div ref={tagAreaRef} className={cn('flex-1 min-w-0 flex items-center relative', nakedCellRowModeAlign, wrap ? 'flex-wrap' : 'overflow-hidden')} style={{ gap: GAP }}
|
|
@@ -670,6 +679,9 @@ function CustomCombobox({
|
|
|
670
679
|
searchAriaLabel = '搜尋選項', // i18n-allow: DS default
|
|
671
680
|
emptyPlaceholder = '選擇…', // i18n-allow: DS default
|
|
672
681
|
emptyText,
|
|
682
|
+
creatable = false,
|
|
683
|
+
onCreate,
|
|
684
|
+
createLabel,
|
|
673
685
|
defaultOpen = false,
|
|
674
686
|
onOpenChange,
|
|
675
687
|
__triggerRef,
|
|
@@ -688,7 +700,7 @@ function CustomCombobox({
|
|
|
688
700
|
const fieldCtx = useFieldContext()
|
|
689
701
|
const error = useResolvedFieldInvalid(errorProp)
|
|
690
702
|
const disabled = useResolvedFieldDisabled(disabledProp)
|
|
691
|
-
// 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode="
|
|
703
|
+
// 2026-06-08 SSOT:mode 經 useResolvedFieldMode;修 <Field mode="view"> 漏 cascade
|
|
692
704
|
const resolvedMode = useResolvedFieldMode({ mode, disabled })
|
|
693
705
|
const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)
|
|
694
706
|
const iconSize = getIconSize(size)
|
|
@@ -797,7 +809,7 @@ function CustomCombobox({
|
|
|
797
809
|
if (e.key === 'Escape') setOpen(false)
|
|
798
810
|
}}>
|
|
799
811
|
{/* 2026-05-18 #6A Round 1 Step 2/4(per user 拍板「決策6選a」+ codex M31 Step 5 verdict cite combobox.tsx:648):
|
|
800
|
-
CustomCombobox edit non-wrap tagArea 對齊 L293
|
|
812
|
+
CustomCombobox edit non-wrap tagArea 對齊 L293 view + L451 readonly + L518 native edit 已 ship 的 overflow-hidden fix。
|
|
801
813
|
原 overflow-visible 讓 tag 越界蓋 chevron / +N indicator(user 圖三)。M10 propagation 完整 4-path align。 */}
|
|
802
814
|
<div ref={tagAreaRef} className={cn('flex-1 min-w-0 flex items-center relative', nakedCellRowModeAlign, wrap ? 'flex-wrap' : 'overflow-hidden')} style={{ gap: tagAreaGap, paddingLeft: tagAreaPaddingLeftPx }}>
|
|
803
815
|
{value.length > 0 ? (
|
|
@@ -863,6 +875,9 @@ function CustomCombobox({
|
|
|
863
875
|
value={value}
|
|
864
876
|
onValueChange={onChange as (value: string | string[]) => void}
|
|
865
877
|
multiple
|
|
878
|
+
creatable={creatable}
|
|
879
|
+
onCreate={onCreate}
|
|
880
|
+
createLabel={createLabel}
|
|
866
881
|
searchable={searchable && searchIn === 'menu'}
|
|
867
882
|
searchPlaceholder={searchPlaceholder}
|
|
868
883
|
size={size}
|
|
@@ -20,7 +20,7 @@ Command 是**搜尋 + 鍵盤導覽的指令清單**——提供搜尋框、分
|
|
|
20
20
|
|
|
21
21
|
**實作基礎**:shadcn passthrough——基於 cmdk + Radix Dialog(Command Palette 模式)。本 DS 保留 shadcn 原結構,視覺樣式以 inline Tailwind class 直接掛在 cmdk primitive 上(`command.tsx`),token 值對齊(非結構繼承)MenuItem / Input / Empty 的 canonical token——改 MenuItem 不會自動連動 Command。
|
|
22
22
|
|
|
23
|
-
**分類**:Internal primitive——由 SelectMenu 消費(Select / Combobox / PeoplePicker 透過 SelectMenu 使用)。App
|
|
23
|
+
**分類**:Internal primitive——由 SelectMenu 消費(Select / Combobox / PeoplePicker 透過 SelectMenu 使用)。App **不從 root barrel front-door 直接 import** Command 家族;若要 Command Palette 類 UX,經 per-component subpath `@qijenchen/design-system/components/Command` 取用 `CommandDialog`(自行包裝確認後使用,對齊 `.claude/rules/ui-development.md`「Root barrel front-door 排除」— internal ≠ 禁用;cmdk + Radix Dialog 包裝,showcase「CommandPalette」story 消費)。
|
|
24
24
|
|
|
25
25
|
**Layout Family**:非上述 family — composite / multi-section(多區塊組合,自 own layout)。
|
|
26
26
|
|
|
@@ -66,7 +66,7 @@ Command 通常由 `SelectMenu` 或自訂 Command Palette 元件消費——直
|
|
|
66
66
|
- **SelectMenu**:Select / Combobox / PeoplePicker 的 searchable 浮層(**由 Field 觸發**,帶有 input field + 選中值回填)
|
|
67
67
|
- **獨立 Command Palette**(Cmd+K):也用 Command 但**不在 Field 觸發**——透過全域快捷鍵開啟,用於跨頁面搜尋 / 快速動作入口
|
|
68
68
|
|
|
69
|
-
兩者底層都消費 Command。Field 場景用 SelectMenu(已處理 input binding);非 Field 場景(Cmd+K)用 `CommandDialog`(cmdk + Radix Dialog 包裝)。
|
|
69
|
+
兩者底層都消費 Command。Field 場景用 SelectMenu(已處理 input binding);非 Field 場景(Cmd+K)用 `CommandDialog`(經 subpath `@qijenchen/design-system/components/Command` 取用,cmdk + Radix Dialog 包裝)。
|
|
70
70
|
|
|
71
71
|
---
|
|
72
72
|
|
|
@@ -97,7 +97,7 @@ Consumer 無需額外處理 a11y,保留 cmdk 原結構 + 使用 `<CommandInput>`
|
|
|
97
97
|
- **Disabled item**:`<CommandItem disabled>` 透過 cmdk 內建支援,視覺繼承 MenuItem SSOT(`text-fg-disabled` + `aria-disabled=true` + 鍵盤導覽自動 skip + Enter 不觸發 onSelect)。skip 是「不進導覽清單」(cmdk `:not([aria-disabled="true"])` selector)— ↑/↓ 直接跳到下一個可選項,無中途 highlight。
|
|
98
98
|
- **Group heading 不可選中**:heading 是 `aria-hidden` 裝飾 div(group container `role="presentation"`),不參與導覽與選取;SR 經 items wrapper `role="group"` + `aria-labelledby` 取得群組語意。
|
|
99
99
|
- **Empty 後持續打字**:cmdk 每個 keystroke 重新過濾(`filtered.count` 驅動),`<CommandEmpty>` 持續顯示直到有 match;input 不鎖、不清空。
|
|
100
|
-
- **Loading**:Command 本身非 async surface。async option fetch 應由 consumer(SelectMenu / Cmd+K palette)在外層處理 —
|
|
100
|
+
- **Loading**:Command 本身非 async surface。async option fetch 應由 consumer(SelectMenu / Cmd+K palette)在外層處理 — 在 `<CommandEmpty>` slot 內渲 `<Empty icon={<CircularProgress size={48}/>}/>`(spinner 只在無可顯示選項時出現;已有 options 保留顯示,不取代 — 對齊 `select.spec.md`「Loading」2026-07-04 拍板措辭 + `empty.spec.md`「現有消費者」SelectMenu loading SSOT,禁止事項見其「禁止事項」loading 條)。Command primitive 不獨立 own loading prop。
|
|
101
101
|
- **Empty(no results)**:`<CommandEmpty>` 自動偵測 search filter result = 0 時渲;consumer 必傳 fallback 文案(預設 cmdk 不渲)。
|
|
102
102
|
- **Dark mode / density**:Command 的 inline Tailwind class 直接消費 semantic token(`bg-neutral-hover` / `text-fg-muted` / `text-fg-disabled` 等),token 在 dark mode 自動切值——非透過 MenuItem / Input / Empty primitive 連動。density 不獨立 own(由 consumer SelectMenu 控)。
|
|
103
103
|
|
|
@@ -48,7 +48,7 @@ const CommandInput = React.forwardRef<
|
|
|
48
48
|
React.ElementRef<typeof CommandPrimitive.Input>,
|
|
49
49
|
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
|
|
50
50
|
>(({ className, ...props }, ref) => (
|
|
51
|
-
<div className="flex items-center border-b border-divider px-3" cmdk-input-wrapper="">
|
|
51
|
+
<div className="flex shrink-0 items-center border-b border-divider px-3" cmdk-input-wrapper="">
|
|
52
52
|
<Search className="mr-2 h-4 w-4 shrink-0 text-fg-muted" />
|
|
53
53
|
<CommandPrimitive.Input
|
|
54
54
|
ref={ref}
|
|
@@ -80,7 +80,14 @@ const CommandList = React.forwardRef<
|
|
|
80
80
|
React.ElementRef<typeof CommandPrimitive.List>,
|
|
81
81
|
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
|
|
82
82
|
>(({ className, ...props }, ref) => (
|
|
83
|
-
|
|
83
|
+
/* @story-baseline: overlay-surface.spec.md#Viewport-aware-scroll-chain-invariant(M25 SSOT owner)
|
|
84
|
+
owner spec: overlay-surface.spec.md:34/53/347-362 —— 「浮層 body 永遠 flex-1 min-h-0 overflow-y-auto;
|
|
85
|
+
中間 wrapper 都必 flex flex-col h-full min-h-0;viewport 太小 body 內壓縮捲動」。
|
|
86
|
+
conflicting code(修前): 本 ScrollArea 固定 max-h-300 無 flex-1 → 在夾住的 SelectMenu PopoverContent
|
|
87
|
+
(max-h=available-height)內撐破外殼、底部選項被裁(320px viewport 實測 bottom 425 > 320 溢出)。
|
|
88
|
+
修: 加 flex-1 min-h-0(對齊 M25 canonical + HoverCard/Popover),max-h-300 降為上限。Command root 已
|
|
89
|
+
`flex h-full flex-col`(command.tsx:23)= chain 完整;非 flex 容器內 flex-1 為 no-op(spec:34 backward compat)。 */
|
|
90
|
+
<ScrollArea className="flex-1 min-h-0 max-h-[var(--menu-max-height,300px)]">
|
|
84
91
|
<CommandPrimitive.List ref={ref} className={cn("overflow-x-hidden", className)} {...props} />
|
|
85
92
|
</ScrollArea>
|
|
86
93
|
))
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
//
|
|
5
5
|
// 對齊 M17 SSOT consolidation + audit recommendation:
|
|
6
6
|
// 原 `renderTypedValue` switch + `EditableCellContent` switch 兩條平行 type-switch 已 collapse
|
|
7
|
-
// 為**一張 type → cell component** registry。每個 cell component 同時處理
|
|
7
|
+
// 為**一張 type → cell component** registry。每個 cell component 同時處理 view / edit mode,
|
|
8
8
|
// 靠底層 Field control 的 `mode` prop 切換。
|
|
9
9
|
//
|
|
10
10
|
// 設計原則:
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import * as React from 'react'
|
|
20
20
|
import type { ComponentType } from 'react'
|
|
21
21
|
import { Pencil } from 'lucide-react'
|
|
22
|
-
import { cn } from '@/lib/utils'
|
|
22
|
+
import { cn, lineClampClass } from '@/lib/utils'
|
|
23
23
|
import type { ColumnType } from './column-types'
|
|
24
24
|
import { Input as InputPublic } from '@/design-system/components/Input/input'
|
|
25
25
|
import { Textarea as TextareaPublic } from '@/design-system/components/Textarea/textarea'
|
|
@@ -55,7 +55,7 @@ const Checkbox = CheckboxPublic as WithFieldVariantInternal<typeof CheckboxPubli
|
|
|
55
55
|
|
|
56
56
|
// ── Types ────────────────────────────────────────────────────────────────────
|
|
57
57
|
|
|
58
|
-
export type CellMode = '
|
|
58
|
+
export type CellMode = 'view' | 'edit'
|
|
59
59
|
export type CellSize = 'sm' | 'md' | 'lg'
|
|
60
60
|
|
|
61
61
|
export interface CellComponentProps {
|
|
@@ -65,13 +65,13 @@ export interface CellComponentProps {
|
|
|
65
65
|
meta: Record<string, any>
|
|
66
66
|
mode: CellMode
|
|
67
67
|
size: CellSize
|
|
68
|
+
/** 該 cell 的 accessible name(= 欄標題)。naked Field 控件在 cell 內無 FieldLabel,
|
|
69
|
+
* 漏此名 → editor 成無名控件(axe aria-input-field-name / select-name)。由 renderCellContent
|
|
70
|
+
* 從 columnDef.header 解析後注入。對齊 AG Grid / MUI X「editor aria-label = column headerName」。 */
|
|
71
|
+
ariaLabel?: string
|
|
68
72
|
autoRowHeight: boolean
|
|
69
73
|
/** 該 cell 是否可編。replaces 舊 `meta._editable` 私有 flag(Phase C M1 hack 移除)。 */
|
|
70
74
|
isEditable?: boolean
|
|
71
|
-
/** 2026-05-13:cell 是否 disabled(state overlay,orthogonal to display/edit lifecycle)。
|
|
72
|
-
* per codex Q3 verdict:不擴 CellMode='disabled',加 prop。各 Cell function 收 true 時
|
|
73
|
-
* 傳 `mode='disabled'` 給 inner Field control,各 Field 內部走具體 disabled token(非 wrapper opacity)。 */
|
|
74
|
-
isDisabled?: boolean
|
|
75
75
|
/** Cell 進 edit mode → 提交新值(blur / Enter / option select 都觸發)— 提交後**自動 exit edit**。
|
|
76
76
|
* 適用 single-shot commit:string / number / select(single)/ person(single)/ date / time / boolean / url。 */
|
|
77
77
|
onCommit?: (next: unknown) => void
|
|
@@ -112,44 +112,41 @@ function makeKeyHandler(
|
|
|
112
112
|
|
|
113
113
|
const sizeForInput = (size: CellSize): CellSize => size
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
* 非 wrapper blanket opacity-disabled 逃生艙(per color.spec.md:729)。 */
|
|
119
|
-
const displayOrDisabled = (isDisabled?: boolean): 'display' | 'disabled' =>
|
|
120
|
-
isDisabled ? 'disabled' : 'display'
|
|
115
|
+
// 2026-07-16 round16:cell「disabled」態廢除(世界級 grid 無 disabled cell — MUI X / AG Grid /
|
|
116
|
+
// Handsontable 皆只有 editable/readonly;meta.disabled 全庫 0 消費)。cell view 態一律 mode="view",
|
|
117
|
+
// 鎖定用 `editable:(row)=>false` 閘(= isCellEditable 模型)。原 displayOrDisabled helper 已移除。
|
|
121
118
|
|
|
122
119
|
// ── Cell Components ──────────────────────────────────────────────────────────
|
|
123
120
|
|
|
124
|
-
function StringCell({ value, meta, mode, size,
|
|
121
|
+
function StringCell({ value, meta, mode, size, autoRowHeight, onCommit, onCancel, onDraft, ariaLabel }: CellComponentProps) {
|
|
125
122
|
// 2026-05-14 I9 fix(per codex+Layer A 共識):meta.maxLines opt-in line-clamp。
|
|
126
|
-
//
|
|
123
|
+
// view autoRow 用 Tailwind arbitrary line-clamp 支援 N rows;edit textarea field-sizing
|
|
127
124
|
// 已 auto-grow to content,natural match clamp。
|
|
128
125
|
// 2026-05-16 Round 5 audit Dim 27 fix:narrow type 取代 `as any` cast。
|
|
129
126
|
const maxLines: number | undefined = (meta as { maxLines?: number } | undefined)?.maxLines
|
|
130
|
-
const clampClass =
|
|
127
|
+
const clampClass = autoRowHeight ? lineClampClass(maxLines) : undefined
|
|
131
128
|
// string type canonical(2026-05-05 v2 user 校正:input space ≥ display space):
|
|
132
|
-
// - autoRowHeight: Textarea(
|
|
129
|
+
// - autoRowHeight: Textarea(view + edit)— view wrap text 撐高 row,edit textarea
|
|
133
130
|
// 多行輸入、`!h-full` 填 cell。對齊 Notion long-text cell canonical。
|
|
134
|
-
// - fixed: Input(
|
|
135
|
-
// 高 = cell 高 = h-field-md,文字位置
|
|
131
|
+
// - fixed: Input(view + edit)— 單行 truncate view,單行 input edit;Field naked intrinsic
|
|
132
|
+
// 高 = cell 高 = h-field-md,文字位置 view↔edit 完全一致。對齊 AG Grid / Material X-Grid。
|
|
136
133
|
// - autoRowHeight 是 table 框架決定(consumer 不需 per-column 設 meta.wrap)。
|
|
137
134
|
// - 互動(Textarea):Esc cancel / Cmd|Ctrl+Enter commit / blur commit;Enter 保留換行
|
|
138
135
|
// - 互動(Input):Esc cancel / Enter commit / blur commit
|
|
139
136
|
const v = value != null ? String(value) : ''
|
|
140
|
-
if (mode === '
|
|
137
|
+
if (mode === 'view') {
|
|
141
138
|
// size 必傳:DataTable cell 字級隨 size 變(sm/md text-body / lg text-body-lg),
|
|
142
139
|
// 對齊 Field family size→font SSOT(field-wrapper.tsx:60-64)。漏傳 → fallback md → lg 表格
|
|
143
140
|
// 字卡 14px 跟 Select/Date 等有傳 size 的 cell 不一致(2026-06-08 user 抓 frozen string 欄字偏小)。
|
|
144
141
|
return autoRowHeight
|
|
145
|
-
? <Textarea variant="naked" mode=
|
|
146
|
-
: <Input variant="naked" mode=
|
|
142
|
+
? <Textarea variant="naked" mode="view" value={v} size={size} className={clampClass} aria-label={ariaLabel} />
|
|
143
|
+
: <Input variant="naked" mode="view" value={v} size={size} aria-label={ariaLabel} />
|
|
147
144
|
}
|
|
148
145
|
if (autoRowHeight) {
|
|
149
146
|
// 2026-05-14 I8 fix(per codex verdict + user 抓「edit cell shrink」):
|
|
150
147
|
// 原 `wrapRows = value.length / 40` 字元估算不準(對應實際 column width 不同
|
|
151
148
|
// → cell 進 edit shrink)。改 CSS `field-sizing: content`(Chrome 123+ / FF 122+ /
|
|
152
|
-
// Safari 17+)讓 textarea 自動 grow to content,匹配
|
|
149
|
+
// Safari 17+)讓 textarea 自動 grow to content,匹配 view wrap 真實高度。
|
|
153
150
|
// Fallback rows 仍保留給舊 browser(rows attr 在 field-sizing 支援時被覆蓋)。
|
|
154
151
|
const newlineRows = (v.match(/\n/g) || []).length + 1
|
|
155
152
|
const wrapRows = Math.ceil(v.length / 40)
|
|
@@ -158,6 +155,7 @@ function StringCell({ value, meta, mode, size, isDisabled, autoRowHeight, onComm
|
|
|
158
155
|
<Textarea
|
|
159
156
|
autoFocus
|
|
160
157
|
variant="naked"
|
|
158
|
+
aria-label={ariaLabel}
|
|
161
159
|
size={sizeForInput(size)}
|
|
162
160
|
rows={estimateRows}
|
|
163
161
|
defaultValue={v}
|
|
@@ -180,6 +178,7 @@ function StringCell({ value, meta, mode, size, isDisabled, autoRowHeight, onComm
|
|
|
180
178
|
<Input
|
|
181
179
|
autoFocus
|
|
182
180
|
variant="naked"
|
|
181
|
+
aria-label={ariaLabel}
|
|
183
182
|
size={sizeForInput(size)}
|
|
184
183
|
defaultValue={v}
|
|
185
184
|
onChange={(e) => onDraft?.(e.target.value)}
|
|
@@ -189,20 +188,21 @@ function StringCell({ value, meta, mode, size, isDisabled, autoRowHeight, onComm
|
|
|
189
188
|
)
|
|
190
189
|
}
|
|
191
190
|
|
|
192
|
-
function NumberCell({ value, meta, mode, size,
|
|
191
|
+
function NumberCell({ value, meta, mode, size, onCommit, onCancel, onDraft, ariaLabel }: CellComponentProps) {
|
|
193
192
|
// currency 透過 columnType-aware prefix:type='currency' → 預設 '$'(可 override)
|
|
194
193
|
const isCurrency = meta?.type === 'currency'
|
|
195
194
|
const prefix = isCurrency ? (meta?.prefix ?? '$') : meta?.prefix
|
|
196
|
-
// React #310 fix:useState 必在
|
|
197
|
-
// 在
|
|
195
|
+
// React #310 fix:useState 必在 view early-return 前無條件呼叫。同一 memo'd cell instance
|
|
196
|
+
// 在 view↔edit 切換時被重用(render site 無 key={mode},data-table.tsx:1352),hook 數量不可變,
|
|
198
197
|
// 否則 Rules of Hooks violation → React #310 crash。對齊 combobox/select hoist pattern。
|
|
199
198
|
const initial = typeof value === 'number' ? value : null
|
|
200
199
|
const [localValue, setLocalValue] = React.useState<number | null>(initial)
|
|
201
|
-
if (mode === '
|
|
200
|
+
if (mode === 'view') {
|
|
202
201
|
return (
|
|
203
202
|
<NumberInput
|
|
204
203
|
variant="naked"
|
|
205
|
-
mode=
|
|
204
|
+
mode="view"
|
|
205
|
+
aria-label={ariaLabel}
|
|
206
206
|
value={value as number | null}
|
|
207
207
|
// size 必傳(同 StringCell)— currency/number cell 字級隨 DataTable size 變,對齊 Field SSOT。
|
|
208
208
|
size={size}
|
|
@@ -215,13 +215,14 @@ function NumberCell({ value, meta, mode, size, isDisabled, onCommit, onCancel, o
|
|
|
215
215
|
}
|
|
216
216
|
// Edit mode value pre-fill canonical(2026-05-05):NumberInput edit 強制 controlled
|
|
217
217
|
// (`value={value ?? ''}`)— 若 NumberCell 以 `defaultValue` 傳入,NumberInput value=undefined → ''
|
|
218
|
-
// empty。對齊 cell-as-input「edit mode 自動帶入
|
|
218
|
+
// empty。對齊 cell-as-input「edit mode 自動帶入 view 值」(對齊 Notion / Airtable 共識),
|
|
219
219
|
// 改用 local state controlled。User typing → setLocalValue;blur/Enter → onCommit(localValue)。
|
|
220
|
-
// (initial + useState 已 hoist 到
|
|
220
|
+
// (initial + useState 已 hoist 到 view-return 前 — 見上方 React #310 fix。)
|
|
221
221
|
return (
|
|
222
222
|
<NumberInput
|
|
223
223
|
autoFocus
|
|
224
224
|
variant="naked"
|
|
225
|
+
aria-label={ariaLabel}
|
|
225
226
|
size={sizeForInput(size)}
|
|
226
227
|
value={localValue}
|
|
227
228
|
onChange={(v) => { setLocalValue(v); onDraft?.(v) }}
|
|
@@ -244,25 +245,25 @@ function NumberCell({ value, meta, mode, size, isDisabled, onCommit, onCancel, o
|
|
|
244
245
|
// (對齊 Airtable / Notion canonical:click 外即關)。
|
|
245
246
|
const dismissOnClose = (onCancel?: () => void) => (open: boolean) => { if (!open) onCancel?.() }
|
|
246
247
|
|
|
247
|
-
// Mode-keyed remount canonical(2026-05-05):
|
|
248
|
-
// position 會重用 instance,導致 `useState(defaultOpen)` 只在首次 mount 跑(那時 mode='
|
|
248
|
+
// Mode-keyed remount canonical(2026-05-05):view↔edit 切換時,因 React reconciliation 同 type 同
|
|
249
|
+
// position 會重用 instance,導致 `useState(defaultOpen)` 只在首次 mount 跑(那時 mode='view'
|
|
249
250
|
// defaultOpen 沒給→預設 false)。後續 mode='edit' 即使傳 defaultOpen=true 也無效。
|
|
250
251
|
// Fix:`key={mode}` 強制 React unmount + remount,每次切 mode 都重跑 useState init。
|
|
251
|
-
// 對齊 Notion / Airtable cell-as-input「
|
|
252
|
+
// 對齊 Notion / Airtable cell-as-input「view 跟 edit 是不同 mount cycle」語義。
|
|
252
253
|
|
|
253
|
-
function DateCell({ value, meta, mode, size,
|
|
254
|
-
if (mode === '
|
|
254
|
+
function DateCell({ value, meta, mode, size, onCommit, onCancel }: CellComponentProps) {
|
|
255
|
+
if (mode === 'view') {
|
|
255
256
|
return (
|
|
256
257
|
<DatePicker
|
|
257
|
-
key="
|
|
258
|
+
key="view"
|
|
258
259
|
variant="naked"
|
|
259
|
-
mode=
|
|
260
|
+
mode="view"
|
|
260
261
|
value={value as string | null}
|
|
261
262
|
size={size}
|
|
262
263
|
formatOptions={meta?.formatOptions}
|
|
263
264
|
locale={meta?.locale}
|
|
264
265
|
// 2026-07-08 user 拍板 A 案(推翻 2026-05-10/06-26 兩次拍板,per 6 家 benchmark 6/6 共識
|
|
265
|
-
// Ant/MUI X/AG Grid/Atlaskit/Notion/Airtable:
|
|
266
|
+
// Ant/MUI X/AG Grid/Atlaskit/Notion/Airtable:view 態零恆顯型別 icon;editable
|
|
266
267
|
// affordance 統一 = hover outline(field.spec.md L4)。showDisplayEndIcon 不再傳
|
|
267
268
|
//(prop 保留為 spreadsheet-flavored 消費端 opt-in 逃生門,見 field.spec.md L6)。
|
|
268
269
|
/>
|
|
@@ -283,18 +284,18 @@ function DateCell({ value, meta, mode, size, isDisabled, onCommit, onCancel }: C
|
|
|
283
284
|
)
|
|
284
285
|
}
|
|
285
286
|
|
|
286
|
-
function TimeCell({ value, meta, mode, size,
|
|
287
|
-
if (mode === '
|
|
287
|
+
function TimeCell({ value, meta, mode, size, onCommit, onCancel }: CellComponentProps) {
|
|
288
|
+
if (mode === 'view') {
|
|
288
289
|
return (
|
|
289
290
|
<TimePicker
|
|
290
|
-
key="
|
|
291
|
+
key="view"
|
|
291
292
|
variant="naked"
|
|
292
|
-
mode=
|
|
293
|
+
mode="view"
|
|
293
294
|
value={value as string | null}
|
|
294
295
|
size={size}
|
|
295
296
|
formatOptions={meta?.formatOptions}
|
|
296
297
|
locale={meta?.locale}
|
|
297
|
-
// showDisplayEndIcon 不傳 — 2026-07-08 A 案:
|
|
298
|
+
// showDisplayEndIcon 不傳 — 2026-07-08 A 案:view 態零恆顯 icon(同 DateCell 註)
|
|
298
299
|
/>
|
|
299
300
|
)
|
|
300
301
|
}
|
|
@@ -314,22 +315,22 @@ function TimeCell({ value, meta, mode, size, isDisabled, onCommit, onCancel }: C
|
|
|
314
315
|
)
|
|
315
316
|
}
|
|
316
317
|
|
|
317
|
-
function SelectCell({ value, meta, mode, size,
|
|
318
|
-
//
|
|
318
|
+
function SelectCell({ value, meta, mode, size, onCommit, onCancel }: CellComponentProps) {
|
|
319
|
+
// View canonical(2026-05-05):cell IS variant,default plain text(no Tag pill 疊在 cell border 內)。
|
|
319
320
|
// Consumer 可在 column meta.display='tag' opt-in 內容導向的 Tag 視覺(category 含色彩標籤等)。
|
|
320
321
|
// 對齊 JTable / AG Grid「renderer/editor 視覺一致」canonical。
|
|
321
322
|
const displayMode = (meta?.display as 'plain' | 'tag' | undefined) ?? 'plain'
|
|
322
|
-
if (mode === '
|
|
323
|
+
if (mode === 'view') {
|
|
323
324
|
return (
|
|
324
325
|
<Select
|
|
325
|
-
key="
|
|
326
|
+
key="view"
|
|
326
327
|
variant="naked"
|
|
327
|
-
mode=
|
|
328
|
+
mode="view"
|
|
328
329
|
value={value as string | null}
|
|
329
330
|
options={meta?.options ?? []}
|
|
330
331
|
size={size}
|
|
331
332
|
display={displayMode}
|
|
332
|
-
// showDisplayEndIcon 不傳 — 2026-07-08 A 案:
|
|
333
|
+
// showDisplayEndIcon 不傳 — 2026-07-08 A 案:view 態零恆顯 icon(同 DateCell 註)
|
|
333
334
|
selectedItemRenderer={meta?.selectedItemRenderer} // 2026-07-08 WM 戰役:status 類彩色 cell 顯示通道
|
|
334
335
|
/>
|
|
335
336
|
)
|
|
@@ -354,19 +355,19 @@ function SelectCell({ value, meta, mode, size, isDisabled, onCommit, onCancel }:
|
|
|
354
355
|
)
|
|
355
356
|
}
|
|
356
357
|
|
|
357
|
-
function MultiSelectCell({ value, meta, mode, size,
|
|
358
|
+
function MultiSelectCell({ value, meta, mode, size, autoRowHeight, onCommitLive, onCancel }: CellComponentProps) {
|
|
358
359
|
const wrap = autoRowHeight && meta?.wrap === true
|
|
359
|
-
if (mode === '
|
|
360
|
+
if (mode === 'view') {
|
|
360
361
|
return (
|
|
361
362
|
<Combobox
|
|
362
|
-
key="
|
|
363
|
+
key="view"
|
|
363
364
|
variant="naked"
|
|
364
|
-
mode=
|
|
365
|
+
mode="view"
|
|
365
366
|
value={(value as string[] | null) ?? []}
|
|
366
367
|
options={meta?.options ?? []}
|
|
367
368
|
wrap={wrap}
|
|
368
369
|
size={size}
|
|
369
|
-
// showDisplayEndIcon 不傳 — 2026-07-08 A 案:
|
|
370
|
+
// showDisplayEndIcon 不傳 — 2026-07-08 A 案:view 態零恆顯 icon(同 DateCell 註)
|
|
370
371
|
/>
|
|
371
372
|
)
|
|
372
373
|
}
|
|
@@ -386,10 +387,10 @@ function MultiSelectCell({ value, meta, mode, size, isDisabled, autoRowHeight, o
|
|
|
386
387
|
)
|
|
387
388
|
}
|
|
388
389
|
|
|
389
|
-
function PersonCell({ value, mode, size,
|
|
390
|
-
if (mode === '
|
|
391
|
-
// 2026-07-08 A 案:
|
|
392
|
-
return <PeoplePicker key="
|
|
390
|
+
function PersonCell({ value, mode, size, onCommit, onCancel, meta }: CellComponentProps) {
|
|
391
|
+
if (mode === 'view') {
|
|
392
|
+
// 2026-07-08 A 案:view 態零恆顯 icon(同 DateCell 註)
|
|
393
|
+
return <PeoplePicker key="view" variant="naked" mode="view" value={value as PersonValue | null} size={size} />
|
|
393
394
|
}
|
|
394
395
|
return (
|
|
395
396
|
<PeoplePicker
|
|
@@ -406,10 +407,10 @@ function PersonCell({ value, mode, size, isDisabled, onCommit, onCancel, meta }:
|
|
|
406
407
|
)
|
|
407
408
|
}
|
|
408
409
|
|
|
409
|
-
function MultiPersonCell({ value, mode, size,
|
|
410
|
-
if (mode === '
|
|
411
|
-
// 2026-07-08 A 案:
|
|
412
|
-
return <PeoplePicker key="
|
|
410
|
+
function MultiPersonCell({ value, mode, size, onCommitLive, onCancel, meta }: CellComponentProps) {
|
|
411
|
+
if (mode === 'view') {
|
|
412
|
+
// 2026-07-08 A 案:view 態零恆顯 icon(同 DateCell 註)
|
|
413
|
+
return <PeoplePicker key="view" variant="naked" mode="view" value={(value as PersonValue[]) ?? []} size={size} />
|
|
413
414
|
}
|
|
414
415
|
// Multi 用 onCommitLive(commit 但不 exit edit)— 每勾一人即時生效,popover 持續開
|
|
415
416
|
// 直到點外面;onOpenChange(false) → onCancel exit edit。對齊 multiSelect canonical。
|
|
@@ -427,14 +428,12 @@ function MultiPersonCell({ value, mode, size, isDisabled, onCommitLive, onCancel
|
|
|
427
428
|
)
|
|
428
429
|
}
|
|
429
430
|
|
|
430
|
-
function BooleanCell({ value, mode, meta, size, isEditable,
|
|
431
|
-
// boolean 不分 read/edit mode —
|
|
432
|
-
//
|
|
433
|
-
|
|
434
|
-
// 走 display branch,Checkbox 拿 disabled mode + 不接 onCheckedChange。
|
|
435
|
-
if (mode === 'display' && (!isEditable || isDisabled)) {
|
|
431
|
+
function BooleanCell({ value, mode, meta, size, isEditable, onCommit }: CellComponentProps) {
|
|
432
|
+
// boolean 不分 read/edit mode — view 渲 mode='view' 純展示;editable 時直接 toggle Checkbox。
|
|
433
|
+
// 不可編(!isEditable)→ 走 view branch,Checkbox mode="view" 純展示不接 onCheckedChange。
|
|
434
|
+
if (mode === 'view' && !isEditable) {
|
|
436
435
|
// size 傳入 → boolean 值 icon(Check/X)@lg 縮放對齊其他 cell(對齊 editable 分支同 size 邏輯)
|
|
437
|
-
return <Checkbox variant="naked" size={size === 'lg' ? 'lg' : 'md'} mode=
|
|
436
|
+
return <Checkbox variant="naked" size={size === 'lg' ? 'lg' : 'md'} mode="view" checked={value === true} />
|
|
438
437
|
}
|
|
439
438
|
return (
|
|
440
439
|
<Checkbox
|
|
@@ -448,21 +447,21 @@ function BooleanCell({ value, mode, meta, size, isEditable, isDisabled, onCommit
|
|
|
448
447
|
|
|
449
448
|
/**
|
|
450
449
|
* UrlCell(2026-07-14 docblock 對齊實作):
|
|
451
|
-
*
|
|
450
|
+
* view branch = `<LinkInput mode="view">`(URL parse / hostname
|
|
452
451
|
* 顯示一致性 SSOT);editable 互動:hover 時右側出 Pencil 鈕 → 進 edit(保留 link click 語意)。
|
|
453
452
|
* edit branch = plain `<Input variant="naked">`(LinkInput edit 的 controlled value 衝突
|
|
454
453
|
* revert,詳下方 edit branch 註解);URL 驗證 deferred 到 onCommit(consumer 端 validate)。
|
|
455
454
|
*/
|
|
456
|
-
function UrlCell({ value, meta, mode, size,
|
|
457
|
-
if (mode === '
|
|
455
|
+
function UrlCell({ value, meta, mode, size, isEditable, onRequestEdit, onCommit, onCancel }: CellComponentProps) {
|
|
456
|
+
if (mode === 'view') {
|
|
458
457
|
// showDisplayEndIcon ← D path Phase 2(2026-05-08):Field naked wrapper 包 anchor,與 Input edit 同 chrome。
|
|
459
458
|
// LinkInput 此 prop = wrapper-only(無 icon,field.spec.md L6 例外)→ A 案(2026-07-08)不撤;
|
|
460
459
|
// 但修 I1:原無條件 true 違反「= isEditable」knob 等式(field-controls.spec.md cell 例外),改等式。
|
|
461
460
|
const display = (
|
|
462
|
-
<LinkInput variant="naked" mode=
|
|
461
|
+
<LinkInput variant="naked" mode="view" value={value as string | null} label={meta?.linkLabel} size={size} showDisplayEndIcon={isEditable === true} />
|
|
463
462
|
)
|
|
464
|
-
//
|
|
465
|
-
if (!isEditable
|
|
463
|
+
// 不可編 URL 不顯 Pencil affordance(純 view 顯示連結,無編輯入口)
|
|
464
|
+
if (!isEditable) return display
|
|
466
465
|
// editable read mode:hover Pencil 鈕(對齊 spec 第十二段「url:read = 連結 + Pencil」)
|
|
467
466
|
return (
|
|
468
467
|
<span className="group/cell relative flex items-center w-full"> {/* @naked-row-mode-allow: URL hover-Pencil 是 inline action 不是 value content,items-center 鎖 Pencil 對齊行高第一行(autoRow 跟 fixed 皆同視覺正確) */}
|
|
@@ -540,7 +539,7 @@ function buildCellWithSurface(Inner: ComponentType<CellComponentProps>, key: str
|
|
|
540
539
|
漏傳 size 的 cell 也自動繼承(根治「新 cell 漏傳 size」class);size primitive 不破壞 memo identity。*/}
|
|
541
540
|
<FieldSurfaceSizeProvider size={props.size}>
|
|
542
541
|
{/* 2026-07-08 user 拍板:cell 可編輯訊號經獨立 boolean context 注給 child Field controls,
|
|
543
|
-
讓 useFieldEmptyDisplay 分流「可編輯 cell 空
|
|
542
|
+
讓 useFieldEmptyDisplay 分流「可編輯 cell 空 view → 空白」vs「不可編輯 cell → '-'」。
|
|
544
543
|
boolean primitive 不破壞 memo identity(同 size context)。 */}
|
|
545
544
|
<FieldSurfaceEditableProvider isEditable={props.isEditable ?? false}>
|
|
546
545
|
<Inner {...props} />
|