@qijenchen/design-system 0.1.0-beta.85 → 0.1.0-beta.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +210 -0
- package/CLAUDE.md +17 -191
- package/cli-init.mjs +30 -2
- package/dist/components/Accordion/accordion.d.ts +2 -2
- package/dist/components/Accordion/accordion.d.ts.map +1 -1
- package/dist/components/Accordion/accordion.js.map +1 -1
- package/dist/components/Avatar/avatar.js +1 -1
- package/dist/components/Avatar/avatar.js.map +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.d.ts +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.d.ts.map +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.js +3 -3
- package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
- package/dist/components/Button/button-group.d.ts +2 -2
- package/dist/components/Button/button-group.d.ts.map +1 -1
- package/dist/components/Button/button-group.js +2 -2
- package/dist/components/Button/button-group.js.map +1 -1
- package/dist/components/Button/button.d.ts +2 -1
- package/dist/components/Button/button.d.ts.map +1 -1
- package/dist/components/Button/button.js +1 -1
- package/dist/components/Button/button.js.map +1 -1
- package/dist/components/Carousel/carousel.d.ts +14 -8
- package/dist/components/Carousel/carousel.d.ts.map +1 -1
- package/dist/components/Carousel/carousel.js +64 -37
- package/dist/components/Carousel/carousel.js.map +1 -1
- package/dist/components/Checkbox/checkbox-group.d.ts.map +1 -1
- package/dist/components/Checkbox/checkbox-group.js +21 -14
- package/dist/components/Checkbox/checkbox-group.js.map +1 -1
- package/dist/components/Checkbox/checkbox.d.ts +2 -2
- package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox/checkbox.js +49 -10
- package/dist/components/Checkbox/checkbox.js.map +1 -1
- package/dist/components/Combobox/combobox.d.ts +15 -6
- package/dist/components/Combobox/combobox.d.ts.map +1 -1
- package/dist/components/Combobox/combobox.js +13 -7
- package/dist/components/Combobox/combobox.js.map +1 -1
- package/dist/components/Command/command.d.ts.map +1 -1
- package/dist/components/Command/command.js +11 -2
- package/dist/components/Command/command.js.map +1 -1
- package/dist/components/DataTable/cell-registry.d.ts +5 -5
- package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
- package/dist/components/DataTable/cell-registry.js +41 -38
- package/dist/components/DataTable/cell-registry.js.map +1 -1
- package/dist/components/DataTable/column-types.d.ts +1 -17
- package/dist/components/DataTable/column-types.d.ts.map +1 -1
- package/dist/components/DataTable/column-types.js.map +1 -1
- package/dist/components/DataTable/data-table-column-visibility-panel.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-column-visibility-panel.js +71 -64
- package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -1
- package/dist/components/DataTable/data-table-filter-panel.d.ts +0 -2
- package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-panel.js +3 -4
- package/dist/components/DataTable/data-table-filter-panel.js.map +1 -1
- package/dist/components/DataTable/data-table-interaction-layer.d.ts +2 -2
- package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
- package/dist/components/DataTable/data-table-sort-manager.js +2 -2
- package/dist/components/DataTable/data-table-sort-manager.js.map +1 -1
- package/dist/components/DataTable/data-table.d.ts +1 -1
- package/dist/components/DataTable/data-table.d.ts.map +1 -1
- package/dist/components/DataTable/data-table.js +44 -32
- package/dist/components/DataTable/data-table.js.map +1 -1
- package/dist/components/DatePicker/date-picker.d.ts +3 -3
- package/dist/components/DatePicker/date-picker.js +4 -4
- package/dist/components/DatePicker/date-picker.js.map +1 -1
- package/dist/components/DescriptionList/description-list.d.ts +3 -3
- package/dist/components/DescriptionList/description-list.d.ts.map +1 -1
- package/dist/components/DescriptionList/description-list.js +6 -6
- package/dist/components/DescriptionList/description-list.js.map +1 -1
- package/dist/components/Dialog/dialog.d.ts +2 -2
- package/dist/components/Dialog/dialog.d.ts.map +1 -1
- package/dist/components/Dialog/dialog.js.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts +8 -3
- package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.js +7 -7
- package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
- package/dist/components/Field/field-context.d.ts +16 -10
- package/dist/components/Field/field-context.d.ts.map +1 -1
- package/dist/components/Field/field-context.js.map +1 -1
- package/dist/components/Field/field-types.d.ts +2 -2
- package/dist/components/Field/field-types.d.ts.map +1 -1
- package/dist/components/Field/field-types.js.map +1 -1
- package/dist/components/Field/field-wrapper.d.ts +20 -4
- package/dist/components/Field/field-wrapper.d.ts.map +1 -1
- package/dist/components/Field/field-wrapper.js +30 -31
- package/dist/components/Field/field-wrapper.js.map +1 -1
- package/dist/components/Field/field.d.ts +1 -1
- package/dist/components/Field/field.d.ts.map +1 -1
- package/dist/components/Field/field.js +25 -3
- package/dist/components/Field/field.js.map +1 -1
- package/dist/components/Field/use-form-validation.d.ts +1 -1
- package/dist/components/Field/use-form-validation.js.map +1 -1
- package/dist/components/FileItem/file-item.d.ts.map +1 -1
- package/dist/components/FileItem/file-item.js +2 -5
- package/dist/components/FileItem/file-item.js.map +1 -1
- package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
- package/dist/components/FileUpload/file-upload.js +5 -1
- package/dist/components/FileUpload/file-upload.js.map +1 -1
- package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
- package/dist/components/FileViewer/file-viewer.js +2 -1
- package/dist/components/FileViewer/file-viewer.js.map +1 -1
- package/dist/components/HoverCard/hover-card.d.ts +2 -2
- package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
- package/dist/components/HoverCard/hover-card.js +4 -2
- package/dist/components/HoverCard/hover-card.js.map +1 -1
- package/dist/components/InlineEdit/inline-edit.d.ts +23 -16
- package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -1
- package/dist/components/InlineEdit/inline-edit.js +104 -62
- package/dist/components/InlineEdit/inline-edit.js.map +1 -1
- package/dist/components/Input/input.d.ts +1 -1
- package/dist/components/Input/input.d.ts.map +1 -1
- package/dist/components/Input/input.js +6 -6
- package/dist/components/Input/input.js.map +1 -1
- package/dist/components/LinkInput/link-input.d.ts +5 -5
- package/dist/components/LinkInput/link-input.d.ts.map +1 -1
- package/dist/components/LinkInput/link-input.js +3 -3
- package/dist/components/LinkInput/link-input.js.map +1 -1
- package/dist/components/Notice/notice.d.ts +17 -1
- package/dist/components/Notice/notice.d.ts.map +1 -1
- package/dist/components/Notice/notice.js +7 -1
- package/dist/components/Notice/notice.js.map +1 -1
- package/dist/components/NumberInput/number-input.d.ts +1 -1
- package/dist/components/NumberInput/number-input.d.ts.map +1 -1
- package/dist/components/NumberInput/number-input.js +12 -1
- package/dist/components/NumberInput/number-input.js.map +1 -1
- package/dist/components/PeoplePicker/avatar-stack-overflow.d.ts +1 -1
- package/dist/components/PeoplePicker/avatar-stack-overflow.js.map +1 -1
- package/dist/components/PeoplePicker/people-picker.d.ts +5 -5
- package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
- package/dist/components/PeoplePicker/people-picker.js +7 -5
- package/dist/components/PeoplePicker/people-picker.js.map +1 -1
- package/dist/components/PeoplePicker/person-display.d.ts +1 -1
- package/dist/components/PeoplePicker/person-display.js.map +1 -1
- package/dist/components/ProfileCard/profile-card.d.ts +1 -1
- package/dist/components/ProfileCard/profile-card.js.map +1 -1
- package/dist/components/RadioGroup/radio-group.d.ts +3 -3
- package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
- package/dist/components/RadioGroup/radio-group.js +1 -1
- package/dist/components/RadioGroup/radio-group.js.map +1 -1
- package/dist/components/Rating/rating.d.ts.map +1 -1
- package/dist/components/Rating/rating.js +4 -0
- package/dist/components/Rating/rating.js.map +1 -1
- package/dist/components/ScrollArea/scroll-area.d.ts +3 -2
- package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
- package/dist/components/ScrollArea/scroll-area.js.map +1 -1
- package/dist/components/SegmentedControl/segmented-control.d.ts +4 -3
- package/dist/components/SegmentedControl/segmented-control.d.ts.map +1 -1
- package/dist/components/SegmentedControl/segmented-control.js.map +1 -1
- package/dist/components/Select/select.d.ts +16 -6
- package/dist/components/Select/select.d.ts.map +1 -1
- package/dist/components/Select/select.js +17 -5
- package/dist/components/Select/select.js.map +1 -1
- package/dist/components/SelectMenu/select-menu.d.ts +17 -1
- package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
- package/dist/components/SelectMenu/select-menu.js +17 -3
- package/dist/components/SelectMenu/select-menu.js.map +1 -1
- package/dist/components/SelectionControl/boolean-value.d.ts +1 -1
- package/dist/components/SelectionControl/boolean-value.js.map +1 -1
- package/dist/components/Sheet/sheet.d.ts +2 -2
- package/dist/components/Sheet/sheet.d.ts.map +1 -1
- package/dist/components/Sheet/sheet.js.map +1 -1
- package/dist/components/Sidebar/sidebar.d.ts +1 -1
- package/dist/components/Sidebar/sidebar.js +2 -2
- package/dist/components/Sidebar/sidebar.js.map +1 -1
- package/dist/components/Slider/slider.d.ts.map +1 -1
- package/dist/components/Slider/slider.js +6 -3
- package/dist/components/Slider/slider.js.map +1 -1
- package/dist/components/Steps/steps.d.ts +6 -4
- package/dist/components/Steps/steps.d.ts.map +1 -1
- package/dist/components/Steps/steps.js +19 -5
- package/dist/components/Steps/steps.js.map +1 -1
- package/dist/components/Switch/switch.d.ts +4 -4
- package/dist/components/Switch/switch.d.ts.map +1 -1
- package/dist/components/Switch/switch.js +3 -1
- package/dist/components/Switch/switch.js.map +1 -1
- package/dist/components/Tabs/tabs.d.ts +1 -1
- package/dist/components/Tabs/tabs.d.ts.map +1 -1
- package/dist/components/Tabs/tabs.js +73 -23
- package/dist/components/Tabs/tabs.js.map +1 -1
- package/dist/components/Tag/tag.d.ts +3 -1
- package/dist/components/Tag/tag.d.ts.map +1 -1
- package/dist/components/Tag/tag.js +2 -2
- package/dist/components/Tag/tag.js.map +1 -1
- package/dist/components/Textarea/textarea.d.ts +4 -4
- package/dist/components/Textarea/textarea.d.ts.map +1 -1
- package/dist/components/Textarea/textarea.js +15 -15
- package/dist/components/Textarea/textarea.js.map +1 -1
- package/dist/components/TimePicker/time-picker.d.ts +4 -4
- package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
- package/dist/components/TimePicker/time-picker.js +8 -5
- package/dist/components/TimePicker/time-picker.js.map +1 -1
- package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
- package/dist/components/Tooltip/tooltip.js +27 -23
- package/dist/components/Tooltip/tooltip.js.map +1 -1
- package/dist/components/TreeView/tree-view.d.ts.map +1 -1
- package/dist/components/TreeView/tree-view.js +16 -0
- package/dist/components/TreeView/tree-view.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -13
- package/dist/index.js.map +1 -1
- package/dist/lib/drag-visual.d.ts +2 -2
- package/dist/lib/drag-visual.js.map +1 -1
- package/dist/lib/utils.d.ts +1 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +21 -1
- package/dist/lib/utils.js.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.d.ts +2 -0
- package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.js +2 -2
- package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
- package/dist/tokens/categorical-color.d.ts +3 -3
- package/dist/tokens/categorical-color.d.ts.map +1 -1
- package/dist/tokens/categorical-color.js +2 -2
- package/dist/tokens/categorical-color.js.map +1 -1
- package/ds-canonical/fork/AGENTS.md +213 -0
- package/ds-canonical/fork/codex/agents/skills/independent-review/SKILL.md +33 -0
- package/ds-canonical/fork/codex/hooks.json +16 -0
- package/ds-canonical/fork/governance.lock +22 -7
- package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +22 -1
- package/ds-canonical/fork/hooks/inject_deploy_url_after_push.sh +14 -2
- package/ds-canonical/fork/manifest.json +10 -1
- package/ds-canonical/fork/preamble.md +3 -2
- package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +7 -7
- package/ds-canonical/fork/skills/ux-audit/SKILL.md +1 -1
- package/ds-canonical/fork/skills/visual-audit/SKILL.md +0 -1
- package/ds-canonical/hooks/check_codex_brief_invariants.sh +9 -3
- package/ds-canonical/hooks/check_ds_anchor_preflight.sh +22 -1
- package/ds-canonical/hooks/check_overlay_open_focus_escape_probe.sh +11 -2
- package/ds-canonical/hooks/check_plugin_fork_health.sh +17 -1
- package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +21 -4
- package/ds-canonical/hooks/inject_deploy_url_after_push.sh +14 -2
- package/ds-canonical/hooks/session_start_governance_check.sh +10 -2
- package/ds-canonical/references/certified-surfaces.md +20 -0
- package/ds-canonical/references/preflight-gate-baseline.json +5 -0
- package/ds-canonical/references/principle-dim-map.json +20 -2
- package/ds-canonical/references/props-naming.md +2 -1
- package/ds-canonical/references/ssot-index.md +2 -2
- package/ds-canonical/references/story-baseline-registry.json +2 -2
- package/ds-canonical/rules/story-rules.md +1 -1
- package/ds-canonical/rules/ui-development.md +2 -1
- package/ds-canonical/skills/codex-collab/SKILL.md +7 -7
- package/ds-canonical/skills/codex-collab/references/brief-template.md +5 -1
- package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
- package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +5 -5
- package/ds-canonical/skills/design-system-audit/SKILL.md +5 -5
- package/ds-canonical/skills/product-ui-audit/SKILL.md +7 -7
- package/ds-canonical/skills/ux-audit/SKILL.md +1 -1
- package/ds-canonical/skills/visual-audit/SKILL.md +0 -1
- package/ds-story-manifest.json +21 -21
- package/llms-full.txt +3 -3
- package/llms.txt +1 -1
- package/package.json +2 -1
- package/src/components/Accordion/accordion.anatomy.stories.tsx +11 -11
- package/src/components/Accordion/accordion.principles.stories.tsx +21 -6
- package/src/components/Accordion/accordion.spec.md +10 -1
- package/src/components/Accordion/accordion.stories.tsx +10 -5
- package/src/components/Accordion/accordion.tsx +7 -2
- package/src/components/Alert/alert.principles.stories.tsx +2 -8
- package/src/components/AppShell/app-shell.anatomy.stories.tsx +19 -13
- package/src/components/AppShell/app-shell.principles.stories.tsx +21 -21
- package/src/components/AppShell/app-shell.spec.md +4 -0
- package/src/components/AppShell/app-shell.stories.tsx +4 -5
- package/src/components/Avatar/avatar.tsx +1 -1
- package/src/components/Badge/badge.spec.md +2 -2
- package/src/components/Breadcrumb/breadcrumb.anatomy.stories.tsx +7 -3
- package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +4 -1
- package/src/components/Breadcrumb/breadcrumb.stories.tsx +1 -1
- package/src/components/BulkActionBar/bulk-action-bar.anatomy.stories.tsx +6 -6
- package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +18 -6
- package/src/components/BulkActionBar/bulk-action-bar.spec.md +15 -5
- package/src/components/BulkActionBar/bulk-action-bar.stories.tsx +8 -24
- package/src/components/BulkActionBar/bulk-action-bar.tsx +4 -4
- package/src/components/Button/button-group.tsx +4 -4
- package/src/components/Button/button.anatomy.stories.tsx +26 -7
- package/src/components/Button/button.principles.stories.tsx +1 -1
- package/src/components/Button/button.spec.md +2 -2
- package/src/components/Button/button.stories.tsx +22 -28
- package/src/components/Button/button.tsx +2 -1
- package/src/components/Calendar/calendar.anatomy.stories.tsx +1 -1
- package/src/components/Calendar/calendar.spec.md +1 -1
- package/src/components/Carousel/carousel.spec.md +17 -1
- package/src/components/Carousel/carousel.tsx +93 -42
- package/src/components/Checkbox/checkbox-group.tsx +26 -15
- package/src/components/Checkbox/checkbox.anatomy.stories.tsx +3 -3
- package/src/components/Checkbox/checkbox.spec.md +12 -3
- package/src/components/Checkbox/checkbox.stories.tsx +4 -4
- package/src/components/Checkbox/checkbox.tsx +79 -19
- package/src/components/Coachmark/coachmark.anatomy.stories.tsx +5 -5
- package/src/components/Coachmark/coachmark.principles.stories.tsx +1 -1
- package/src/components/Combobox/combobox.anatomy.stories.tsx +11 -4
- package/src/components/Combobox/combobox.principles.stories.tsx +10 -11
- package/src/components/Combobox/combobox.spec.md +6 -2
- package/src/components/Combobox/combobox.stories.tsx +9 -36
- package/src/components/Combobox/combobox.tsx +42 -27
- package/src/components/Command/command.spec.md +3 -3
- package/src/components/Command/command.tsx +9 -2
- package/src/components/DataTable/cell-registry.tsx +75 -76
- package/src/components/DataTable/column-types.ts +4 -17
- package/src/components/DataTable/data-table-column-visibility-panel.tsx +20 -7
- package/src/components/DataTable/data-table-filter-panel.tsx +6 -6
- package/src/components/DataTable/data-table-interaction-layer.tsx +3 -3
- package/src/components/DataTable/data-table-sort-manager.tsx +2 -2
- package/src/components/DataTable/data-table.anatomy.stories.tsx +25 -24
- package/src/components/DataTable/data-table.css +2 -3
- package/src/components/DataTable/data-table.principles.stories.tsx +17 -11
- package/src/components/DataTable/data-table.spec.md +7 -7
- package/src/components/DataTable/data-table.stories.tsx +39 -29
- package/src/components/DataTable/data-table.tsx +90 -67
- package/src/components/DataTable/filter-operators.spec.md +2 -0
- package/src/components/DateGrid/date-grid.spec.md +1 -1
- package/src/components/DatePicker/date-picker.anatomy.stories.tsx +9 -9
- package/src/components/DatePicker/date-picker.principles.stories.tsx +4 -4
- package/src/components/DatePicker/date-picker.spec.md +9 -9
- package/src/components/DatePicker/date-picker.stories.tsx +9 -9
- package/src/components/DatePicker/date-picker.tsx +13 -13
- package/src/components/DescriptionList/description-list.anatomy.stories.tsx +5 -5
- package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
- package/src/components/DescriptionList/description-list.spec.md +12 -12
- package/src/components/DescriptionList/description-list.stories.tsx +3 -3
- package/src/components/DescriptionList/description-list.tsx +10 -10
- package/src/components/Dialog/dialog.tsx +4 -1
- package/src/components/DropdownMenu/dropdown-menu.tsx +27 -14
- package/src/components/Field/field-context.ts +17 -11
- package/src/components/Field/field-controls.spec.md +44 -37
- package/src/components/Field/field-types.ts +14 -12
- package/src/components/Field/field-wrapper.tsx +59 -43
- package/src/components/Field/field.anatomy.stories.tsx +6 -6
- package/src/components/Field/field.principles.stories.tsx +5 -28
- package/src/components/Field/field.spec.md +4 -4
- package/src/components/Field/field.stories.tsx +103 -72
- package/src/components/Field/field.tsx +36 -4
- package/src/components/Field/form-validation.spec.md +2 -0
- package/src/components/Field/use-form-validation.ts +1 -1
- package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +3 -3
- package/src/components/FieldControlGroup/field-control-group.spec.md +3 -3
- package/src/components/FileItem/file-item.tsx +10 -8
- package/src/components/FileUpload/file-upload.principles.stories.tsx +7 -7
- package/src/components/FileUpload/file-upload.spec.md +3 -2
- package/src/components/FileUpload/file-upload.tsx +5 -1
- package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +1 -1
- package/src/components/FileViewer/file-viewer.principles.stories.tsx +1 -1
- package/src/components/FileViewer/file-viewer.spec.md +1 -1
- package/src/components/FileViewer/file-viewer.stories.tsx +1 -1
- package/src/components/FileViewer/file-viewer.tsx +3 -2
- package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -4
- package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -1
- package/src/components/HoverCard/hover-card.spec.md +2 -2
- package/src/components/HoverCard/hover-card.tsx +4 -2
- package/src/components/InlineEdit/inline-edit.anatomy.stories.tsx +184 -0
- package/src/components/InlineEdit/inline-edit.principles.stories.tsx +142 -0
- package/src/components/InlineEdit/inline-edit.spec.md +71 -46
- package/src/components/InlineEdit/inline-edit.stories.tsx +20 -19
- package/src/components/InlineEdit/inline-edit.tsx +211 -125
- package/src/components/Input/input.anatomy.stories.tsx +15 -15
- package/src/components/Input/input.principles.stories.tsx +3 -3
- package/src/components/Input/input.spec.md +4 -4
- package/src/components/Input/input.stories.tsx +3 -3
- package/src/components/Input/input.tsx +13 -13
- package/src/components/LinkInput/link-input.anatomy.stories.tsx +2 -2
- package/src/components/LinkInput/link-input.principles.stories.tsx +1 -1
- package/src/components/LinkInput/link-input.spec.md +6 -2
- package/src/components/LinkInput/link-input.stories.tsx +8 -8
- package/src/components/LinkInput/link-input.tsx +21 -19
- package/src/components/Notice/notice.principles.stories.tsx +1 -1
- package/src/components/Notice/notice.tsx +8 -2
- package/src/components/NumberInput/number-input.anatomy.stories.tsx +6 -6
- package/src/components/NumberInput/number-input.principles.stories.tsx +4 -4
- package/src/components/NumberInput/number-input.spec.md +6 -6
- package/src/components/NumberInput/number-input.stories.tsx +4 -4
- package/src/components/NumberInput/number-input.tsx +17 -5
- package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +8 -4
- package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +6 -7
- package/src/components/OverflowIndicator/overflow-indicator.spec.md +1 -1
- package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +6 -30
- package/src/components/PeoplePicker/avatar-stack-overflow.ts +1 -1
- package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +13 -10
- package/src/components/PeoplePicker/people-picker.principles.stories.tsx +5 -1
- package/src/components/PeoplePicker/people-picker.stories.tsx +5 -5
- package/src/components/PeoplePicker/people-picker.tsx +39 -20
- package/src/components/PeoplePicker/person-display.tsx +3 -3
- package/src/components/Popover/popover.anatomy.stories.tsx +10 -7
- package/src/components/Popover/popover.principles.stories.tsx +49 -28
- package/src/components/Popover/popover.spec.md +4 -2
- package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
- package/src/components/ProfileCard/profile-card.tsx +1 -1
- package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +14 -13
- package/src/components/ProgressBar/progress-bar.principles.stories.tsx +8 -23
- package/src/components/ProgressBar/progress-bar.stories.tsx +8 -2
- package/src/components/README.md +1 -0
- package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +8 -3
- package/src/components/RadioGroup/radio-group.stories.tsx +3 -3
- package/src/components/RadioGroup/radio-group.tsx +19 -14
- package/src/components/Rating/rating.anatomy.stories.tsx +1 -1
- package/src/components/Rating/rating.principles.stories.tsx +3 -3
- package/src/components/Rating/rating.spec.md +2 -2
- package/src/components/Rating/rating.stories.tsx +4 -18
- package/src/components/Rating/rating.tsx +5 -1
- package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.spec.md +1 -1
- package/src/components/ScrollArea/scroll-area.tsx +5 -2
- package/src/components/SegmentedControl/segmented-control.principles.stories.tsx +4 -1
- package/src/components/SegmentedControl/segmented-control.spec.md +5 -10
- package/src/components/SegmentedControl/segmented-control.tsx +6 -3
- package/src/components/Select/select.anatomy.stories.tsx +13 -7
- package/src/components/Select/select.principles.stories.tsx +3 -3
- package/src/components/Select/select.spec.md +9 -1
- package/src/components/Select/select.stories.tsx +41 -27
- package/src/components/Select/select.tsx +44 -26
- package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +44 -26
- package/src/components/SelectMenu/select-menu.principles.stories.tsx +0 -1
- package/src/components/SelectMenu/select-menu.stories.tsx +7 -19
- package/src/components/SelectMenu/select-menu.tsx +38 -5
- package/src/components/SelectionControl/boolean-value.tsx +2 -2
- package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +1 -1
- package/src/components/SelectionControl/selection-item.principles.stories.tsx +4 -4
- package/src/components/Separator/separator.principles.stories.tsx +1 -1
- package/src/components/Sheet/sheet.principles.stories.tsx +5 -3
- package/src/components/Sheet/sheet.tsx +4 -1
- package/src/components/Sidebar/sidebar.tsx +1 -1
- package/src/components/Slider/slider.anatomy.stories.tsx +5 -4
- package/src/components/Slider/slider.principles.stories.tsx +6 -6
- package/src/components/Slider/slider.spec.md +13 -1
- package/src/components/Slider/slider.stories.tsx +44 -32
- package/src/components/Slider/slider.tsx +13 -4
- package/src/components/Steps/steps.anatomy.stories.tsx +7 -7
- package/src/components/Steps/steps.principles.stories.tsx +1 -1
- package/src/components/Steps/steps.spec.md +4 -2
- package/src/components/Steps/steps.stories.tsx +4 -56
- package/src/components/Steps/steps.tsx +31 -7
- package/src/components/Switch/switch.anatomy.stories.tsx +3 -3
- package/src/components/Switch/switch.principles.stories.tsx +1 -1
- package/src/components/Switch/switch.spec.md +4 -4
- package/src/components/Switch/switch.stories.tsx +3 -3
- package/src/components/Switch/switch.tsx +16 -10
- package/src/components/Tabs/tabs.anatomy.stories.tsx +6 -0
- package/src/components/Tabs/tabs.principles.stories.tsx +10 -2
- package/src/components/Tabs/tabs.spec.md +5 -2
- package/src/components/Tabs/tabs.stories.tsx +36 -44
- package/src/components/Tabs/tabs.tsx +101 -36
- package/src/components/Tag/tag.anatomy.stories.tsx +2 -2
- package/src/components/Tag/tag.principles.stories.tsx +4 -4
- package/src/components/Tag/tag.spec.md +3 -1
- package/src/components/Tag/tag.tsx +5 -3
- package/src/components/Textarea/textarea.anatomy.stories.tsx +6 -6
- package/src/components/Textarea/textarea.spec.md +5 -5
- package/src/components/Textarea/textarea.tsx +24 -24
- package/src/components/TimePicker/time-picker.anatomy.stories.tsx +12 -11
- package/src/components/TimePicker/time-picker.principles.stories.tsx +37 -44
- package/src/components/TimePicker/time-picker.spec.md +6 -5
- package/src/components/TimePicker/time-picker.stories.tsx +3 -3
- package/src/components/TimePicker/time-picker.tsx +24 -14
- package/src/components/Toast/toast.principles.stories.tsx +1 -1
- package/src/components/Toast/toast.stories.tsx +1 -1
- package/src/components/Tooltip/tooltip.anatomy.stories.tsx +15 -6
- package/src/components/Tooltip/tooltip.principles.stories.tsx +4 -4
- package/src/components/Tooltip/tooltip.spec.md +9 -3
- package/src/components/Tooltip/tooltip.stories.tsx +1 -1
- package/src/components/Tooltip/tooltip.tsx +12 -2
- package/src/components/TreeView/tree-view.anatomy.stories.tsx +16 -14
- package/src/components/TreeView/tree-view.principles.stories.tsx +44 -11
- package/src/components/TreeView/tree-view.spec.md +8 -6
- package/src/components/TreeView/tree-view.stories.tsx +9 -43
- package/src/components/TreeView/tree-view.tsx +36 -0
- package/src/index.ts +13 -10
- package/src/lib/drag-visual.ts +2 -2
- package/src/lib/utils.ts +19 -0
- package/src/patterns/element-anatomy/inline-action.spec.md +1 -1
- package/src/patterns/element-anatomy/item-anatomy.tsx +4 -2
- package/src/patterns/header-canonical/header-canonical.spec.md +9 -0
- package/src/patterns/header-canonical/header-canonical.stories.tsx +31 -5
- package/src/tokens/categorical-color.ts +5 -5
- package/src/tokens/color/color.spec.md +4 -4
- package/src/tokens/color/primitives.css +9 -2
- package/src/tokens/opacity/opacity.spec.md +1 -1
- package/src/tokens/orphan-tokens.spec.md +1 -1
- package/src/tokens/typography/typography.spec.md +2 -0
- package/src/tokens/uiSize/uiSize.css +5 -0
- package/src/tokens/uiSize/uiSize.spec.md +1 -1
|
@@ -12,7 +12,7 @@ traits:
|
|
|
12
12
|
# Field Controls 設計原則
|
|
13
13
|
|
|
14
14
|
> **Foundational SSOT rationale**(cap 800,2026-04-25 approved):
|
|
15
|
-
> Family 4 (Field Control Layout) SSOT owner。Input / NumberInput / DatePicker / Select / Combobox / LinkInput / TimePicker / Textarea / PeoplePicker 等皆消費 `fieldWrapperStyles` / edit-readonly-disabled 三態 mode architecture / endAction 處理 / `mode="
|
|
15
|
+
> Family 4 (Field Control Layout) SSOT owner。Input / NumberInput / DatePicker / Select / Combobox / LinkInput / TimePicker / Textarea / PeoplePicker 等皆消費 `fieldWrapperStyles` / edit-readonly-disabled 三態 mode architecture / endAction 處理 / `mode="view"` 渲染 pattern / Inline Action canonical(後者也 cascade 到 Sidebar / TreeView / DropdownMenu)。scope 本質 > 單一元件。
|
|
16
16
|
|
|
17
17
|
> **注意**:此文件是 Field Controls(Input / NumberInput / DatePicker / Select / Combobox / LinkInput / PeoplePicker 等)**共用**的設計原則,與 `Field/field.spec.md`(表單 Layout 容器)**不是同一個東西**。
|
|
18
18
|
>
|
|
@@ -26,7 +26,7 @@ traits:
|
|
|
26
26
|
Field Controls 是資料輸入與顯示的基礎元件。每種資料類型(text、number、date、select...)對應一個元件,同時服務 Form 和 DataTable:
|
|
27
27
|
|
|
28
28
|
- **Form**:用 Field Controls 的 edit / readonly / disabled 三態(在 Field 容器內)
|
|
29
|
-
- **DataTable**:以 Field Controls 的 `mode="
|
|
29
|
+
- **DataTable**:以 Field Controls 的 `mode="view"` 渲染 cell
|
|
30
30
|
|
|
31
31
|
每個元件擁有該類型的格式化邏輯(唯一真實來源),Form 和 DataTable 消費同一份 code。
|
|
32
32
|
|
|
@@ -42,19 +42,19 @@ components/
|
|
|
42
42
|
│ ├── field-controls.spec.md ← 本文件
|
|
43
43
|
│ ├── field-types.ts ← FieldMode / FieldVariant 共用型別 + getMenuListMinHeight(InlineActionConfig 住 patterns/element-anatomy/item-anatomy.tsx)
|
|
44
44
|
│ └── field-wrapper.tsx ← 共用 wrapper 樣式、bareInputStyles、EMPTY_DISPLAY
|
|
45
|
-
├── Input/ ← Input(含 mode="
|
|
46
|
-
├── NumberInput/ ← NumberInput(含 mode="
|
|
47
|
-
├── DatePicker/ ← DatePicker(含 mode="
|
|
48
|
-
├── Select/ ← Select(含 mode="
|
|
49
|
-
├── Combobox/ ← Combobox(含 mode="
|
|
50
|
-
├── LinkInput/ ← LinkInput(含 mode="
|
|
45
|
+
├── Input/ ← Input(含 mode="view";與 Field 平行的兄弟目錄,以下同)
|
|
46
|
+
├── NumberInput/ ← NumberInput(含 mode="view" + formatNumber)
|
|
47
|
+
├── DatePicker/ ← DatePicker(含 mode="view" + formatDate)
|
|
48
|
+
├── Select/ ← Select(含 mode="view")
|
|
49
|
+
├── Combobox/ ← Combobox(含 mode="view")
|
|
50
|
+
├── LinkInput/ ← LinkInput(含 mode="view")
|
|
51
51
|
├── PeoplePicker/ ← PeoplePicker + PersonDisplay(cross-component primitive)
|
|
52
52
|
└── Textarea/ ← Textarea(多行)
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
每個元件統一以 `mode` prop 切換樣態:
|
|
56
56
|
1. **edit / readonly / disabled** — Form 用,可編輯 / 鎖定 / 不可用
|
|
57
|
-
2. **`mode="
|
|
57
|
+
2. **`mode="view"`** — DataTable cell 用,純格式化顯示(取代過往的 `XxxDisplay` 子元件)
|
|
58
58
|
|
|
59
59
|
---
|
|
60
60
|
|
|
@@ -62,19 +62,23 @@ components/
|
|
|
62
62
|
|
|
63
63
|
Field 家族 = **一個 `fieldWrapperStyles` cva + 兩條正交軸**;`InlineEdit` 是**疊在其上的組合 primitive**,不屬於任一軸。此段為「框架為何長這樣」的單一住所。
|
|
64
64
|
|
|
65
|
-
**軸一 `mode`(4)× 軸二 `variant`(2)
|
|
65
|
+
**軸一 `mode`(4)× 軸二 `variant`(2)**(2026-07-16 round16 `display`→`view` 更名 + Model A):`variant` 只有 `default`(公開,完整 chrome)與 `naked`(`@internal`,cell-as-input:edit×naked 自畫 border-based state machine,view×naked 用 transparent border 由 host cell 供視覺邊框;只 DataTable `cell-registry.tsx` 消費)。`bare`(透明外殼)**2026-07-09 退役**;SSOT `field-types.ts` `FieldVariant`。有效組合:default × {edit/view/readonly/disabled};naked × {edit/view}。**`readonly×naked` / `disabled×naked` 死格 2026-07-16 移除**——DataTable cell「disabled」態廢除 + readonly 從不入 naked,全庫 0 消費;軸對稱靠 cva 省略天然覆蓋。readonly 2026-07-09 補鍵盤 focus ring(WCAG 2.4.7)。**型別層 public/internal union(2026-07-14 API 策展 E)**:公開 `FieldVariant = 'default'`;`naked` 收 `FieldVariantInternal`(`@internal`),cell-registry 經 `WithFieldVariantInternal` 消費。
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
**軸一 `view` mode = Model A(2026-07-16,user GO)**:`view`(原 `display`)= 非表單的值呈現(cell / InlineEdit / 詳情)。**`view×default` = edit 幾何減 chrome**——保留 `px-[--field-px]` + `py` + `h-field`,只拔 border/bg(透明);推翻 2026-05-13 Path Ⅰ 的 `!px-0 !py-0`。理由:view 對齊「edit 值位置」(非 label 左緣)→ 水平垂直都留 → view 與 edit **同一顆控件、只差 chrome** → read↔edit 零跳。**`view×naked` = bare**(host TD 給 padding),與 view×default **不同用途、不 collapse**。世界級對照 [Atlassian inline-edit](https://github.com/pioug/atlassian-frontend-mirror/blob/main/design-system/inline-edit/src/internal/read-view.tsx)(read=edit 幾何)+ Bootstrap [`.form-control-plaintext`](https://github.com/twbs/bootstrap/blob/main/scss/forms/_form-control.scss)(留 padding)。geometry 抽 `fieldViewGeometry(size, multiline)` helper(view×default 幾何 SSOT)。
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
**軸二 就地編輯 host(InlineEdit = DataTable cell,同一份語義,2026-07-16)**:只有 **`view ↔ edit` 二態 + `editable` 判準閘**(布林/callback,預設 true),**無 disabled 態**——世界級就地編輯 detail-pane / grid 皆無 disabled cell:[MUI X isCellEditable](https://mui.com/x/react-data-grid/editing/) / [AG Grid editable](https://www.ag-grid.com/react-data-grid/cell-editing/)。`editable=false` → view 無 hover 入口、無藍框、**不灰化**。永久唯讀資料 → 用 `<Control mode="view">`,不用 disabled。
|
|
70
70
|
|
|
71
|
-
**InlineEdit
|
|
71
|
+
**InlineEdit ≠ mode ≠ variant**:它是 **view↔edit 二態切換**——靜止 = 純值/格式化(內容 + 隱形 Pressable),點擊/Enter 才生一個 **edit-mode Field 控件**(預設 `<Input mode="edit">`,可 `renderEdit` 換 Textarea/Select)。站在 mode 軸**之上**。**Model A:InlineEdit 不自帶 geometry cva**——**兩條 view 路徑都用 `fieldViewGeometry`(field-px)**(2026-07-17 對齊 root cause 修):(a) 值-格式化路徑用 `fieldViewGeometry` **包住** `<Control mode="view">`(委派控件 bare view 皆 0px → 包住統一 field-px + 內部格式化間距仍由控件提供);(b) 純值/標題 `<Tag>` 直接套 `fieldViewGeometry`。本體只給:**orientation-aware `-mx`(vertical 含 standalone 貼 label / horizontal 對齊 sibling;不依 fieldCtx)**+ exit 保證(pointerdown-outside)+ hover bg + focus 藍框 + 隱形 Pressable。edit 態完全繼承原 field(Jira 式)。
|
|
72
|
+
|
|
73
|
+
**`naked` vs `InlineEdit`(不同層級,非冗餘)**:`naked` = 視覺外殼變體(chrome 層),靜止態**仍是控件**(裸 input),對應 in-cell 即時編輯;`InlineEdit` = 互動組合(behavior 層),靜止態**不是控件**(純值),對應 click-to-edit。各自獨立。
|
|
74
|
+
|
|
75
|
+
**InlineEdit 多行 / padding / 零位移契約(Model A)**:read↔edit 零跳 = view 與 edit **同一顆控件、只差 chrome**。**多行(`multiline`)**:edit 自動用 `<Textarea mode="edit">`,view 用 `fieldViewGeometry(size, true)` = `items-start py-2`(**= Textarea edit `py-2`**)。**多 tag / avatar / prefix 換行的 wrap py 由委派控件 view mode 提供**(= 它 edit 的 py)—— 故 view×default **不可** collapse 成 `!py-0`。水平:InlineEdit `-mx-[--field-px]` 拉整塊到欄左緣、view(`fieldViewGeometry` 統一 `px-[--field-px]`,含委派控件 bare view 被包住)被 -mx 抵消 → 值/tag 落 label x=0。edit 態繼承原 field(不統一 px)→ 純文字零跳、tag ~6px 微移(Jira 式)。pixel 鎖 `scripts/probe-inline-edit-align.mjs`。
|
|
72
76
|
|
|
73
77
|
---
|
|
74
78
|
|
|
75
|
-
## Mode — 表單三態 (
|
|
79
|
+
## Mode — 表單三態 (view 見下方 View 段)
|
|
76
80
|
|
|
77
|
-
下表涵蓋 Form-context 三態(edit / readonly / disabled);完整 `FieldMode` 為四值(`'edit' | '
|
|
81
|
+
下表涵蓋 Form-context 三態(edit / readonly / disabled);完整 `FieldMode` 為四值(`'edit' | 'view' | 'readonly' | 'disabled'`,2026-07-16 `display`→`view` 更名),`view`(非表單值呈現)於下方 `## View` 段記載。
|
|
78
82
|
|
|
79
83
|
| Mode | 底色 | 邊框 | 文字色 | 用途 |
|
|
80
84
|
|------|------|------|--------|------|
|
|
@@ -84,7 +88,7 @@ Field 家族 = **一個 `fieldWrapperStyles` cva + 兩條正交軸**;`InlineEdit
|
|
|
84
88
|
|
|
85
89
|
三種模式共用同一個 wrapper 結構(`fieldWrapperStyles`),只有底色、邊框、文字色不同。
|
|
86
90
|
|
|
87
|
-
**Boolean / 單選控件的 readonly(2026-06-12 user 拍板)**:Field 內 readonly 的 Checkbox / Switch = 同一 `fieldWrapperStyles` readonly 灰框 + ✓/—(
|
|
91
|
+
**Boolean / 單選控件的 readonly(2026-06-12 user 拍板)**:Field 內 readonly 的 Checkbox / Switch = 同一 `fieldWrapperStyles` readonly 灰框 + ✓/—(view 同款值語言);RadioGroup = 灰框 + 選中項 label(= Select readonly 同款呈現)。理由:同一張 readonly 表單中,文字控件有灰框鎖定訊號、boolean 保留全彩控件會誤導「仍可操作」(世界級 0/4 採原樣鎖互動:Salesforce = ✓ 無框靜態 glyph / SAP = 靜態文字 / Atlassian = readView / Ant Pro = 文字)。standalone readOnly(settings list / SelectionItem row)維持原樣鎖互動。**邊界**:Rating readonly = 星星本身(星星即值語言,role=img,全業界 review-stars canonical,不包灰框);Slider 在 `<Field mode="readonly">` 內 = 鎖互動保留正常視覺(value 可讀不降色,pointer-events-none + thumb tabIndex=-1)。
|
|
88
92
|
|
|
89
93
|
### Loading state(async 驗證 / debounce fetch 中)
|
|
90
94
|
|
|
@@ -175,11 +179,11 @@ Field wrapper 透過 context 注入的 key 是 `invalid`(**非 `error`**;field.t
|
|
|
175
179
|
|
|
176
180
|
判準 = **「這個元件是不是承載/編輯一個欄位值的互動控件?」**(對齊 MUI FormControl 對 form control 的 cascade、Ant `Form disabled` 排除非表單控件如 Segmented/Tabs):
|
|
177
181
|
|
|
178
|
-
- **承載欄位值的互動控件** → 完整 cascade(disabled + 有
|
|
182
|
+
- **承載欄位值的互動控件** → 完整 cascade(disabled + 有 view 態者含 mode):Input / NumberInput / Textarea / LinkInput / Select / Combobox / DatePicker / TimePicker / PeoplePicker / Switch / Checkbox / RadioGroup / Slider / SegmentedControl / Rating。
|
|
179
183
|
- **欄位內的展示元素**(Avatar)→ 跟隨 `<Field disabled>` / `<Field mode="disabled">` **變淡**(視覺一致),用 fieldCtx 存在性 scope(DataTable cell 無 fieldCtx → 不影響)。
|
|
180
184
|
- **獨立 action 元件**(Button)→ **不**自動 cascade;由 consumer 自控 `disabled`(對齊 MUI Button 無 FormControl 整合 + Ant 排除 custom/非表單控件)。
|
|
181
185
|
|
|
182
|
-
注:有
|
|
186
|
+
注:有 view 渲染分支者(Input 家族 / Select / Combobox / DatePicker / TimePicker / PeoplePicker / **Checkbox** / **Switch**,後二者 view = ✓/—)完整響應 `<Field mode="view"/"readonly">` + `<Field disabled>`;**Slider / Rating 無 view 態但有 readonly cascade**(2026-06-12 補:Slider readonly = 鎖互動保留視覺;Rating readonly = 星星鎖定 role=img)+ 響應 `<Field disabled>`;**SegmentedControl 無 view/readonly 態**(僅 enabled/disabled)→ 只響應 `<Field disabled>`。**group 控件(Checkbox/RadioGroup/Switch/SegmentedControl)雖非 fieldWrapperStyles 消費者,仍一律經 resolver hook 解析**(gate Check 1b/2 強制)。
|
|
183
187
|
|
|
184
188
|
**機械強制**:`scripts/check-field-cascade-resolve.mjs`(ci + release:preflight)—— 消費 `fieldWrapperStyles` 的控件若散落手刻 `fieldCtx?.{disabled,mode}` 解析(而非走 resolver hook)= fail,防新控件重演 cascade 漏接。
|
|
185
189
|
|
|
@@ -265,10 +269,10 @@ Select / Combobox 的 ChevronDown、DatePicker 的 Calendar、TimePicker 的 Clo
|
|
|
265
269
|
|
|
266
270
|
- edit:`fg-muted`;**readonly:不顯示 indicator**;**disabled:`fg-disabled`**(對齊上方 Icon 色彩原則)
|
|
267
271
|
- 不可互動(`pointer-events-none`)——下拉由 select 元素本身觸發
|
|
268
|
-
- **Cell(naked variant)**:**
|
|
272
|
+
- **Cell(naked variant)**:**view 態零恆顯 indicator(2026-07-08 user 拍板 A 案,推翻 2026-05-10「indicator = editable affordance」)** —— editable affordance 統一 = hover outline(field.spec.md L4)。Benchmark 6/6 product-table 域(Ant editable-cells / MUI X singleSelect / AG Grid / Atlaskit inline-edit(v2.0.0 移除 hover-pencil 後 9 版未回歸)/ Notion / Airtable)view 態皆純值零 icon;恆顯派僅 Google Sheets Chip/Arrow(spreadsheet 域可選檔位)。`showDisplayEndIcon` prop 保留為 opt-in 逃生門(spreadsheet-flavored 消費端);cell-registry 6 個 picker 站不再傳、url 站傳 `isEditable === true`(LinkInput 例外 = wrapper-only 無 icon,取 display↔edit 像素對齊)。edit 態 indicator 照舊(本節上方 form 規則);opt-in 時 (opt-in 時保留)(同表單邏輯)
|
|
269
273
|
- locked(readonly/disabled)wrapper 並設 `aria-disabled`(disabled 時)——styled-disabled 非原生元素需明告 AT inactive,亦使 axe 正確套用 WCAG 1.4.3 inactive-UI 豁免
|
|
270
274
|
- clearable 有值時:clear X 在左,ChevronDown 在右
|
|
271
|
-
- **右側元素(clear / chevron / calendar / clock)右緣水平內距 = `--field-px`(12px,SSOT `tokens/uiSize/uiSize.css`),edit / readonly / disabled /
|
|
275
|
+
- **右側元素(clear / chevron / calendar / clock)右緣水平內距 = `--field-px`(12px,SSOT `tokens/uiSize/uiSize.css`),edit / readonly / disabled / view 全 mode 一致**(跟 Input 一致)。**tag 模式特例**:左側 `tagPadding` 用對稱 px-calc(≈8px)貼齊 tags、會吃掉右緣,故 tag 容器(含 readonly/disabled)**必 re-assert `paddingRight: var(--field-px)`** 對齊 edit;漏接 = chevron 右緣偏移 bug(2026-06-27 修 Select:354 / Combobox ReadonlyMultiSelect)
|
|
272
276
|
- **多行(Combobox tag wrap)垂直對齊**:tags 換行、容器動態變高時,右側 chevron **鎖第一行 tag 中線**(非整體置中)——容器 `items-start` + `ItemSuffix self-start` + `style={{ height: tagHeight }}`(sm 20 / md+lg 24)。對齊 item-anatomy「suffix 永遠 `h-[1lh]` 對齊第一行」canonical;edit / readonly / disabled 全 mode 一致(2026-06-27 補 readonly/disabled wrap 漏接)
|
|
273
277
|
|
|
274
278
|
## Select 顯示模式
|
|
@@ -282,7 +286,7 @@ Select 支援兩種顯示模式(`display` prop):
|
|
|
282
286
|
|
|
283
287
|
`plain` 模式可搭配 `startIcon`(代表 value 的圖示,如狀態 icon;2026-05-01 由 `text` 改名 `plain`,rationale 見 `select.spec.md`)。
|
|
284
288
|
|
|
285
|
-
`selectedItemRenderer` 設定時優先於 plain / tag 預設呈現,且 **4 mode(edit/
|
|
289
|
+
`selectedItemRenderer` 設定時優先於 plain / tag 預設呈現,且 **4 mode(edit/view/readonly/disabled)共享**(共享 contract (a),見下方)— view 態渲染 renderer 輸出(值內容),無 chrome 無 chevron。
|
|
286
290
|
|
|
287
291
|
`tag` 模式的 edit 用 hidden select overlay(跟 Combobox 同模式),Tag 用 `pointer-events-none`,點擊穿透到 select。右側元素右緣 = `--field-px`(見上方「右側元素」canonical;tag 模式 readonly/disabled 必 re-assert)。
|
|
288
292
|
|
|
@@ -318,33 +322,36 @@ Icon 色彩遵循 Inline Action 統一規則:預設 `fg-muted`,hover 時 `foregr
|
|
|
318
322
|
|
|
319
323
|
---
|
|
320
324
|
|
|
321
|
-
##
|
|
325
|
+
## View — 值呈現(mode="view",Model A;2026-07-16 原 `display` 更名)
|
|
322
326
|
|
|
323
|
-
每個 Field 元件以 `mode="
|
|
327
|
+
每個 Field 元件以 `mode="view"` 渲染分支把 raw value 格式化為純展示輸出(取代過往 `XxxDisplay` 子元件;唯一現存 cross-component view primitive 是 PeoplePicker 的 `PersonDisplay`)。**Model A 幾何**:`view×default` = edit 幾何減 chrome(留 `px`/`py`/`h-field`,只拔 border/bg)——見上方「軸一 view mode」。
|
|
324
328
|
|
|
325
|
-
|
|
326
|
-
- **DataTable cell**:cell-registry 根據 `meta.type` 選對應 Field 元件並傳 `mode="
|
|
329
|
+
View 的消費者:
|
|
330
|
+
- **DataTable cell**:cell-registry 根據 `meta.type` 選對應 Field 元件並傳 `mode="view"`(variant="naked";2026-07-16 cell「disabled」態廢除,鎖定用 `editable:(row)=>false`)
|
|
331
|
+
- **InlineEdit view 態**:委派 `<Control mode="view">` 取格式化 + 幾何(read=edit 同一顆控件)
|
|
327
332
|
- **Field readonly 模式**:內部使用相同的格式化邏輯
|
|
328
333
|
|
|
334
|
+
**例外 — LinkInput / PeoplePicker 預設 view 路徑不包 wrapper**(code 為準,2026-07-16 明文):LinkInput `mode="view"` 預設(`showDisplayEndIcon=false`)= 裸 span/anchor(`fieldDisplayTextClass` + truncate),**不消費** `fieldWrapperStyles` / `fieldViewGeometry` —— view 值是可點擊連結,inline 嵌入取 flush 呈現(backward compat);`showDisplayEndIcon=true` opt-in 才包 `fieldWrapperStyles(view × resolvedVariant)` 取 cell view↔edit 像素對齊。PeoplePicker 預設 view 同模式(裸 `PersonDisplay`/`MultiPersonDisplay`)。詳 `link-input.tsx` / `people-picker.tsx` docblock。
|
|
335
|
+
|
|
329
336
|
### null / undefined 值(2026-07-08 user 拍板 — 半形 hyphen + editable × surface 分流)
|
|
330
337
|
|
|
331
338
|
> **user verbatim**:「table cell 不可編輯的空值,單獨不可編輯的 display 的空值,單獨的 readonly 的空值都用"-";單獨可以編輯的 edit 輸入框的空值則是 placeholder;table cell 內可編輯的 display 的空值就是為空」+「我從頭到尾哪裡有說要用全形的」。
|
|
332
339
|
|
|
333
|
-
空值符號 = **半形 hyphen `-`(U+002D)**,**非**全形 em dash `—`(U+2014)。分流看 **surface × 是否可編輯 × mode**(SSOT 機械層 = `field-context.ts` `useFieldEmptyDisplay()` + `EMPTY_DISPLAY` 常數 `field-wrapper.tsx`;全 Field family
|
|
340
|
+
空值符號 = **半形 hyphen `-`(U+002D)**,**非**全形 em dash `—`(U+2014)。分流看 **surface × 是否可編輯 × mode**(SSOT 機械層 = `field-context.ts` `useFieldEmptyDisplay()` + `EMPTY_DISPLAY` 常數 `field-wrapper.tsx`;全 Field family view/readonly/disabled 空值渲染必經此 hook,**禁**直接引 `EMPTY_DISPLAY` 常數):
|
|
334
341
|
|
|
335
342
|
| 情境 | 判準 | 空值顯示 | 依據 |
|
|
336
343
|
|---|---|---|---|
|
|
337
|
-
| 不可編輯 — standalone
|
|
344
|
+
| 不可編輯 — standalone view / readonly | mode ∈ {view, readonly} 且非可編輯 table cell | **半形 `-` + `text-foreground`** | 唯讀資料「此欄無值」明示;Ant ProTable `columnEmptyText` 預設 `'-'`(https://github.com/ant-design/pro-components/blob/master/src/table/Table.tsx)|
|
|
338
345
|
| 不可編輯 — table cell(readonly cell)| `surface==='table-cell'` 且 `isEditable===false` | **半形 `-`** | 同上;非可編欄位視同唯讀資料 |
|
|
339
|
-
| **可編輯** — table cell
|
|
346
|
+
| **可編輯** — table cell view 靜止態 | `surface==='table-cell'` 且 `isEditable===true` 且 mode='view' | **全空白 `''`** | grid 域壓倒性共識(MUI X `valueToRender?.toString()` null 短路 / AG Grid `_toString(null)`→不設 textContent / Ant core rc-table 裸值 / Notion / Airtable);表格密集時「-」海 = 視覺噪音。空 editable cell 的 affordance = hover outline(field.spec.md L4),非佔位符 |
|
|
340
347
|
| **可編輯** — form / panel edit 輸入框 | mode='edit' 且非 table cell | **native placeholder** | 標準表單輸入提示;「hover 才顯 placeholder」十路查證零家採用,**禁** |
|
|
341
348
|
| **可編輯** — table cell edit / focus | `surface==='table-cell'` 且 mode='edit' | **全空白**(DataTable cell 不接 placeholder)| 對齊 Notion / Airtable cell 編輯裸輸入 |
|
|
342
349
|
|
|
343
350
|
收斂式:`surface==='table-cell' && isEditable ? '' : '-'`(可編輯 form edit 走 native placeholder,不經此 hook)。`isEditable` 由 DataTable cell registry 經 `FieldSurfaceEditableProvider` boolean context 注入(standalone / form 無此 context → 預設 false → `-`)。
|
|
344
351
|
|
|
345
|
-
**readonly native input 兩派統一**(2026-07-08):Input / Textarea 的 readonly **空值** 改走
|
|
352
|
+
**readonly native input 兩派統一**(2026-07-08):Input / Textarea 的 readonly **空值** 改走 view-span 顯 `-`(原走 native `<input readOnly>` 吐 native placeholder / 空白);readonly **有值** 仍走 native input 保留選取/複製語意。NumberInput 早已 `resolvedMode !== 'edit'` 統一走 span,無需改。「有值」判定(2026-07-14 補註,dual-model consensus;不改空值顯 `-` 規則本身):uncontrolled(只傳 `defaultValue`)同樣算有值 —— 機械層 = `useControllable` 內部 resolved value(defaultValue 初始 + 打字寫回,native element 由 `value={resolved}` 內部驅動)+ form.reset() bridge(HTML reset 不發 input event → uncontrolled 掛 form `reset` listener 把 resolved 歸位 defaultValue),切 view / readonly(native element unmount/remount)後判定與 DOM 真值一致、不顯 stale 值;controlled 純 passthrough 行為不變。
|
|
346
353
|
|
|
347
|
-
**空值符號顏色**(2026-07-09 user 拍板 verbatim「「-」代表的是不可編輯只拿來供檢視的值所以應該跟readonly 的value同樣顏色吧」):不可編輯「-」= `text-foreground`(同 readonly value 色,非 placeholder 提示的裝飾語意);disabled → `text-fg-disabled`(M24)。SSOT helper = `field-context.ts` `fieldEmptyColorClass(resolvedMode)`,全 Field family
|
|
354
|
+
**空值符號顏色**(2026-07-09 user 拍板 verbatim「「-」代表的是不可編輯只拿來供檢視的值所以應該跟readonly 的value同樣顏色吧」):不可編輯「-」= `text-foreground`(同 readonly value 色,非 placeholder 提示的裝飾語意);disabled → `text-fg-disabled`(M24)。SSOT helper = `field-context.ts` `fieldEmptyColorClass(resolvedMode)`,全 Field family view/readonly/disabled 空值 span 消費(禁散寫 `text-fg-muted`)。
|
|
348
355
|
|
|
349
356
|
**例外表**(唯一去處,禁散落各元件 spec):
|
|
350
357
|
- **boolean** → 顯示 unchecked 狀態(非空白非 dash)
|
|
@@ -353,7 +360,7 @@ Display 的消費者:
|
|
|
353
360
|
|
|
354
361
|
### DataTable 整合
|
|
355
362
|
|
|
356
|
-
DataTable 根據 column 的 `meta.type` 自動選擇 Field 元件(以 `mode="
|
|
363
|
+
DataTable 根據 column 的 `meta.type` 自動選擇 Field 元件(以 `mode="view"` 渲染):
|
|
357
364
|
|
|
358
365
|
```tsx
|
|
359
366
|
// 自動渲染——不需要手寫 cell
|
|
@@ -375,7 +382,7 @@ col.accessor('status', {
|
|
|
375
382
|
|
|
376
383
|
## 共享 contract(2026-05-12 Stream C — Selected renderer / Placeholder vocabulary / Cell surface)
|
|
377
384
|
|
|
378
|
-
**(a) Selected value renderer**:rich display(avatar+name/icon+label)元件**必**提供 consumer renderer slot,**
|
|
385
|
+
**(a) Selected value renderer**:rich display(avatar+name/icon+label)元件**必**提供 consumer renderer slot,**view/readonly/disabled/edit** 4 mode 共享同一 renderer(禁 edit-only)。`Select.selectedItemRenderer`(4 mode 已全接 — 2026-07-08 A 案回歸修正,view/readonly/disabled 的 ReadonlyDisplay 消費 renderer 輸出;renderer 輸出屬「值內容」,view 態照常渲染,與 affordance 分層見 `field.spec.md` L6)/ `Combobox.tagRenderer`(edit 已接;view path 走 ComboboxTagStack 預設 Tag,consumer tagRenderer unify 仍 deferred,見 `combobox.tsx` 檔頭 `@renderer-symmetry-allow`;現行唯一 tagRenderer consumer = PeoplePicker,其 view 走 MultiPersonDisplay 不經此 path,無實際丟失)/ PeoplePicker 走 `PersonDisplay`+`MultiPersonDisplay`+`Combobox.tagRenderer`。對齊 MUI Autocomplete `renderValue` / Ant Select `tagRender`+`labelRender`+`optionRender` / MUI DataGrid `renderCell`+`renderEditCell` 共享 params。 <!-- @benchmark-unverified -->
|
|
379
386
|
|
|
380
387
|
**(b) Placeholder vocabulary**(3 props 對 3 UI state,**不可混用**):
|
|
381
388
|
- `placeholder` — trigger empty(沒選值,例「請選擇人員」)— Ant/Polaris/Carbon canonical
|
|
@@ -386,15 +393,15 @@ col.accessor('status', {
|
|
|
386
393
|
|
|
387
394
|
**(c) Cell surface metrics**:Field family 在 cell 內**禁** hardcode padding(`tagAreaPaddingLeftPx={isEmpty ? undefined : 8}` 反 pattern)。改 **`FieldSurface` context**(`'form' | 'toolbar' | 'table-cell'`):`useFieldSurface()` 取值,`<FieldSurfaceProvider surface="table-cell">` 自動套於 `cell-registry.resolveCellComponent`。Consumer 用 `surface === 'table-cell'` 顯式 query(取代 `variant === 'naked'` heuristic)。**risk mitigation**:`avatar.left = cell.left + computed(--table-cell-px)`,禁再加 magic 8px(double-count)。**Token scope**:`--table-cell-px/py` 是 DataTable-scoped metric(CSS 定義在 `data-table.css`,Field naked variant 是 DataTable cell substrate sub-component 故 cross-path reference 不算真 cross-component),per 2026-05-13 codex Q2 verdict + AG Grid `cellHorizontalPadding`(grid theme param)/ MUI X `cellClassName`(per-cell)/ Carbon spacing scale primitive(不升 cell padding 全域 token)idiom — **不**升 `tokens/layoutSpace/` canonical。對齊 AG Grid cellRendererSelector / Material X DataGrid 共享 params / Notion property type registry。Hook `check_field_controls_contracts.sh` (contract c) 機械強制。 <!-- @benchmark-unverified -->
|
|
388
395
|
|
|
389
|
-
**(d)
|
|
396
|
+
**(d) View×default = edit 幾何減 chrome(Model A)SSOT**(2026-07-16 round16 user GO;推翻 2026-05-13 Path Ⅰ「zero chrome + `!px-0 !py-0`」拍板):default `mode='view'` = **edit 幾何減 chrome** —— 保留 size 軸 `px-[var(--field-px)]` + `h-field-*`(多行留 py),只拔 border/bg(透明)→ view 與 edit 同一顆控件、只差 chrome → read↔edit 零跳(與上方「軸一 view mode」段同一 canonical)。真要包 chrome 走 `readonly` 或 `showDisplayEndIcon=true` opt-in。**Impl**:`field-wrapper.tsx` view×default compound(`bg-transparent border-transparent`,幾何由 base + size 軸天然保留)+ `fieldViewGeometry(size, multiline)` helper(view×default 幾何 SSOT,供 InlineEdit 純值/標題路徑消費)。世界級對照:[Atlassian inline-edit read-view](https://github.com/pioug/atlassian-frontend-mirror/blob/main/design-system/inline-edit/src/internal/read-view.tsx)(read=edit 幾何)+ Bootstrap [`.form-control-plaintext`](https://github.com/twbs/bootstrap/blob/main/scss/forms/_form-control.scss)(留 padding)。機械鎖:`scripts/inline-edit-view-geometry-invariant.mjs`(multiline py-2 = Textarea edit py-2 契約;注:hook `check_field_controls_contracts.sh` 的 contract (d) 為 field-px token 檢查,非本條)。
|
|
390
397
|
|
|
391
|
-
**(e)
|
|
398
|
+
**(e) View typography canonical**(2026-05-14 user I2 + codex M31 verdict):Field family view path **必** consume `fieldWrapperStyles` size variants typography token — `sm/md → text-body`(14px line-height 1.5)/ `lg → text-body-lg`(16px)。**禁**:LinkInput / Select / Combobox 非 D-path 的 bare-span 直接 render 無 font-size class(瀏覽器 default 字體);**必**包 `text-body` (sm/md) / `text-body-lg` (lg) class。對齊跨 Field family view 視覺尺寸統一(歷史 anchor:user 抓 LinkInput view 字體跟其他 Field 不一致 = SSOT 違反 漏接 typography token)。**Impl**:LinkInput / Select / Combobox / DatePicker / TimePicker non-D-path bare-span 加 size-aware text class(helper = `fieldDisplayTextClass`)。world-class cite:MUI X DataGrid `Typography` consistent / Atlassian @atlaskit/textfield size-prop typography token / Polaris TextField typographyToken size-aware。 <!-- @benchmark-unverified -->
|
|
392
399
|
|
|
393
400
|
---
|
|
394
401
|
|
|
395
402
|
## 邊界案例(家族共用 pointer)
|
|
396
403
|
|
|
397
|
-
- **極長輸入溢出**:由各元件 spec own — Input 超寬走原生水平捲動(`input.spec.md`「邊界(內容超寬)」)、Textarea 內容超出時 native 內部捲動 /
|
|
404
|
+
- **極長輸入溢出**:由各元件 spec own — Input 超寬走原生水平捲動(`input.spec.md`「邊界(內容超寬)」)、Textarea 內容超出時 native 內部捲動 / view 態隨內容增高(`textarea.spec.md`「極長文字」)。
|
|
398
405
|
- **常見誤解 — disabled 時 label 該隱藏?**:不隱藏 — label 保留但變灰(`FieldLabel` disabled 灰化、required 星號同步 `text-fg-disabled`,SSOT `Field/field.spec.md`);停用原因由外部 Tooltip / help text 承擔(見「disabled 的停用原因」)。
|
|
399
406
|
|
|
400
407
|
## 表單驗證原則
|
|
@@ -407,8 +414,8 @@ col.accessor('status', {
|
|
|
407
414
|
- ❌ 不在 input 尾部放 error 狀態 icon——邊框顏色已傳達 error
|
|
408
415
|
- ❌ endAction 不可傳入 ReactNode——使用 InlineActionConfig 宣告式 API
|
|
409
416
|
- ❌ endAction 的 inline action 不可省略 `aria-label`(即 `label` 欄位)
|
|
410
|
-
- ❌ 不可編輯
|
|
411
|
-
- ❌ Field 的 readonly 模式不可用於 DataTable cell——readonly 有底色和 wrapper 開銷,table cell 用
|
|
417
|
+
- ❌ 不可編輯 view / readonly 空值用全形 em dash `—` 或 `text-fg-muted`——半形 `-` + `text-foreground`(「-」是供檢視的值,同 readonly value 色;disabled → fg-disabled);可編輯 table cell view 空值為空白(見「null / undefined 值」分流表)
|
|
418
|
+
- ❌ Field 的 readonly 模式不可用於 DataTable cell——readonly 有底色和 wrapper 開銷,table cell 用 `mode="view"`
|
|
412
419
|
|
|
413
420
|
## 被引用(auto-maintained,Dim 3 reciprocal audit)
|
|
414
421
|
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
// ── Field Mode ───────────────────────────────────────────────────────────────
|
|
2
2
|
//
|
|
3
|
-
// 4 模式 canonical(2026-05-05 expand
|
|
3
|
+
// 4 模式 canonical(2026-05-05 expand;2026-07-16 round16 `display`→`view` 更名 + Model A 幾何):
|
|
4
4
|
// edit — 一般可編輯 input(預設 variant:border + bg)
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
5
|
+
// view — **純展示值(非表單)**;語意「要呈現給人看的值本體」(cell / InlineEdit / 詳情)。
|
|
6
|
+
// **Model A**:view = edit 幾何減 chrome(透明 bg/border,**保留 px 內距 + 高度**)→ read↔edit
|
|
7
|
+
// 零跳(view 與 edit 同一顆控件、只差 chrome)。對齊 Atlassian inline-edit(read=edit 幾何)
|
|
8
|
+
// + Bootstrap `.form-control-plaintext`(留 padding)。詳 field-controls.spec.md「軸一 view mode」。
|
|
9
|
+
// readonly — input chrome + non-editable(bg-readonly + a11y ring「這是 input 但鎖了」);表單語境、值仍算數可選取
|
|
10
|
+
// disabled — input chrome + disabled state(灰底,不可互動,不送出,語意「目前不適用」);表單專屬
|
|
10
11
|
//
|
|
11
|
-
// `
|
|
12
|
-
// -
|
|
13
|
-
// -
|
|
12
|
+
// `view` vs `readonly` 判別:
|
|
13
|
+
// - 「純展示值、非表單」(DataTable cell / InlineEdit view / ProfileCard meta) → `view`
|
|
14
|
+
// - 「表單欄位但目前不可改、值仍送出」(form 鎖部分欄位) → `readonly`
|
|
14
15
|
//
|
|
16
|
+
// 命名(命名 3-test 全過):MUI X cellMode=view / Atlassian readVIEW 字根 / 與 edit 成對。
|
|
15
17
|
// World-class refs(M22 verified):
|
|
16
18
|
// Carbon: https://carbondesignsystem.com/patterns/read-only-states-pattern/
|
|
17
19
|
// PatternFly: https://www.patternfly.org/components/inline-edit/design-guidelines/
|
|
18
20
|
// Cloudscape: https://cloudscape.design/patterns/general/disabled-and-read-only-states/
|
|
19
|
-
export type FieldMode = 'edit' | '
|
|
21
|
+
export type FieldMode = 'edit' | 'view' | 'readonly' | 'disabled'
|
|
20
22
|
|
|
21
23
|
// ── Field Variant ────────────────────────────────────────────────────────────
|
|
22
24
|
//
|
|
@@ -25,7 +27,7 @@ export type FieldMode = 'edit' | 'display' | 'readonly' | 'disabled'
|
|
|
25
27
|
// default — 含 border + bg(一般 form input,= Ant outlined / MUI outlined)。公開 FieldVariant 唯一值
|
|
26
28
|
// naked — @internal(FieldVariantInternal)。cell-as-input(host cell substrate)。edit×naked 自畫
|
|
27
29
|
// border-based state machine(rest → hover → focus-within:border-primary → error 紅框);
|
|
28
|
-
//
|
|
30
|
+
// view×naked 用 transparent border,由 host cell 提供視覺邊框。
|
|
29
31
|
// 對齊 Airtable / Notion / Excel cell editing。唯一合法消費者 = DataTable cell-registry
|
|
30
32
|
// (field-controls.spec.md「軸二 variant」明文)— consumer 直傳 `<Input variant="naked">`
|
|
31
33
|
// 會繞過 canonical chrome / focus,故公開型別排除
|
|
@@ -93,7 +95,7 @@ const FIELD_HEIGHT_TOKEN: Record<string, string> = {
|
|
|
93
95
|
lg: 'var(--field-height-lg)',
|
|
94
96
|
}
|
|
95
97
|
|
|
96
|
-
/** CommandList 最小高度 = field-height × rows + 16px(CommandGroup py-2 上下 padding
|
|
98
|
+
/** @internal — CommandList 最小高度 = field-height × rows + 16px(CommandGroup py-2 上下 padding);Command/Select 內部 helper,consumer 不直接 import。root barrel 排除(subpath 仍可用)。 */
|
|
97
99
|
export function getMenuListMinHeight(size: string, rows: number = 3): string {
|
|
98
100
|
const token = FIELD_HEIGHT_TOKEN[size] ?? FIELD_HEIGHT_TOKEN.md
|
|
99
101
|
return `calc(${token} * ${rows} + 16px)`
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
// @internal — DS-internal 單元(Field 家族 chrome 基底,consumer 用 Field/Input 等 wrapper 不直用);不隨 index.ts re-export 進 npm public surface。
|
|
2
2
|
// @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.
|
|
3
3
|
import { cva } from 'class-variance-authority'
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
4
5
|
|
|
5
6
|
// ── Field Wrapper Styles ────────────────────────────────────────────────────
|
|
6
7
|
// 所有 Field 元件共用的 input wrapper 樣式。
|
|
7
8
|
//
|
|
8
|
-
// 4 種模式(2026-05-05 expand):
|
|
9
|
+
// 4 種模式(2026-05-05 expand;2026-07-16 round16 `display`→`view` 更名 + Model A):
|
|
9
10
|
// edit — bg-surface, border, hover/focus 回饋(可編輯 input)
|
|
10
|
-
//
|
|
11
|
-
//
|
|
11
|
+
// view — 純展示值(非表單);**Model A = edit 幾何減 chrome**(透明 bg/border,**保留 px 內距 + 高度**),
|
|
12
|
+
// 故 read↔edit 零跳。對齊 Atlassian inline-edit(read=edit 幾何)+ Bootstrap plaintext(留 padding)。
|
|
12
13
|
// readonly — bg-readonly(neutral-2), 無邊框, 文字正常色(input chrome 但鎖定;token 獨立於 disabled)
|
|
13
14
|
// disabled — bg-disabled(neutral-2), 無邊框, 文字灰化
|
|
14
15
|
//
|
|
15
16
|
// 2 種視覺外殼(variant;2026-07-09 `bare` 退役後):
|
|
16
|
-
// default — 完整 chrome(form input 場景)
|
|
17
|
+
// default — 完整 chrome(form input 場景);view×default = edit 幾何減 chrome(留 px/py)
|
|
17
18
|
// naked — cell-as-input substrate(DataTable);**edit×naked 自畫 border-based state machine**
|
|
18
19
|
// (border-border → hover:border-border-hover → focus-within:border-primary → error 紅框);
|
|
19
|
-
//
|
|
20
|
+
// view×naked = bare(host TD 給 padding)。**view×default ≠ view×naked**(不 collapse)。
|
|
21
|
+
// (2026-07-16 cell disabled 態廢除 → readonly×naked/disabled×naked 死格已移除)
|
|
20
22
|
//
|
|
21
23
|
// 高度:固定 h = field-height token(rem),與 Button 共用同一組 token。
|
|
22
24
|
|
|
@@ -45,7 +47,7 @@ export const fieldWrapperStyles = cva(
|
|
|
45
47
|
variants: {
|
|
46
48
|
mode: {
|
|
47
49
|
edit: '',
|
|
48
|
-
|
|
50
|
+
view: '',
|
|
49
51
|
readonly: '',
|
|
50
52
|
disabled: '',
|
|
51
53
|
},
|
|
@@ -54,7 +56,7 @@ export const fieldWrapperStyles = cva(
|
|
|
54
56
|
default: '',
|
|
55
57
|
// naked — cell-as-input(Notion / Airtable / Excel canonical)。**edit×naked 自畫** border +
|
|
56
58
|
// hover:border-border-hover + focus-within:border-primary + error 紅框(v14 border-based state
|
|
57
|
-
// machine,見下方 compoundVariants L182-196);
|
|
59
|
+
// machine,見下方 compoundVariants L182-196);view×naked 用 transparent
|
|
58
60
|
// border 由 host cell 提供視覺邊框,內部 input 純文字承載。
|
|
59
61
|
// 世界級對照:Airtable / Notion / Excel / Google Sheets cell editing。
|
|
60
62
|
// (2026-07-09 `bare` variant 已退役 — 見 field-types.ts FieldVariant 註解;
|
|
@@ -126,13 +128,18 @@ export const fieldWrapperStyles = cva(
|
|
|
126
128
|
},
|
|
127
129
|
{ mode: 'edit', variant: 'default', error: false, className: 'focus-within:!border-primary focus-within:hover:!border-primary' },
|
|
128
130
|
{
|
|
129
|
-
|
|
131
|
+
// 2026-07-16 round16 Model A(user GO,推翻 2026-05-13 Path Ⅰ 的 `!px-0 !py-0`):
|
|
132
|
+
// view×default = **edit 幾何減 chrome** — 保留 size 軸的 `px-[var(--field-px)]` + `h-field-*`,
|
|
133
|
+
// 只拔 border/bg(透明)。理由 = view 用在 cell/inline-edit/詳情,要對齊的是「edit 的值位置」
|
|
134
|
+
// (非 label 左緣),故水平垂直都留 → view 與 edit 同一顆控件、只差 chrome → read↔edit 零跳。
|
|
135
|
+
// 世界級對照:Atlassian inline-edit(read=edit 幾何,靠容器負邊距對齊)+ Bootstrap
|
|
136
|
+
// `.form-control-plaintext`(`padding: $input-padding-y 0` 留 padding);我們比 Bootstrap 更徹底
|
|
137
|
+
// (連水平 px 也留),因用例是 align-to-edit 非 align-to-label。詳 field-controls.spec.md「軸一 view mode」
|
|
138
|
+
// + planning/2026-07-15-inline-edit-field-mode-remediation.md round16。
|
|
139
|
+
// ⚠️ view×default ≠ view×naked:naked = bare(cell substrate,host TD 給 padding);default = 留幾何。
|
|
140
|
+
mode: 'view',
|
|
130
141
|
variant: 'default',
|
|
131
|
-
|
|
132
|
-
// default display = zero chrome — !px-0 !py-0 override size token 的 px-[var(--field-px)],跟 Select / Combobox
|
|
133
|
-
// / DatePicker / TimePicker / LinkInput non-D-path bare-span idiom 一致(Carbon read-only / Stripe
|
|
134
|
-
// display / Notion property / Polaris readonly TextField 全 zero chrome)。
|
|
135
|
-
className: 'bg-transparent border border-transparent !px-0 !py-0',
|
|
142
|
+
className: 'bg-transparent border border-transparent',
|
|
136
143
|
},
|
|
137
144
|
{
|
|
138
145
|
mode: 'readonly',
|
|
@@ -203,7 +210,7 @@ export const fieldWrapperStyles = cva(
|
|
|
203
210
|
{ mode: 'edit', variant: 'naked', error: false, className: 'focus-within:!border-primary focus-within:hover:!border-primary' },
|
|
204
211
|
{
|
|
205
212
|
// 2026-05-12 fix v2(M32 root invariant audit):
|
|
206
|
-
// Q1 root invariant?:cell-as-input
|
|
213
|
+
// Q1 root invariant?:cell-as-input view 視覺位置 = `cell.items-{X}` × `Field.height`
|
|
207
214
|
// 兩變數函數;canonical = autoRow → Field intrinsic + cell.items-start
|
|
208
215
|
// (text 在 cell top + cellPadding-y),fixed → Field h-field-md +
|
|
209
216
|
// cell.items-center(text 在 cell vertical center)。
|
|
@@ -217,33 +224,18 @@ export const fieldWrapperStyles = cva(
|
|
|
217
224
|
// override h-field-md → Field intrinsic line-height → cell items-start
|
|
218
225
|
// 真實 anchor text at cell.top + padding。
|
|
219
226
|
// Edit mode 不動(`!h-full` 保留 — border 必滿格對齊 cell border)。
|
|
220
|
-
|
|
227
|
+
// 2026-07-16 round16:view×naked 保持 bare(cell substrate,host TD 給 padding)— 與 view×default
|
|
228
|
+
// (edit 幾何減 chrome、留 px/py)本質不同用途,**不 collapse**(round14 合併是錯的)。
|
|
229
|
+
mode: 'view',
|
|
221
230
|
variant: 'naked',
|
|
222
231
|
className: [
|
|
223
232
|
'bg-transparent !rounded-none !px-0 !py-0 !h-auto',
|
|
224
233
|
'border border-transparent',
|
|
225
234
|
],
|
|
226
235
|
},
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
className: [
|
|
231
|
-
'bg-transparent !rounded-none !px-0 !py-0 !h-auto',
|
|
232
|
-
'border border-transparent',
|
|
233
|
-
],
|
|
234
|
-
},
|
|
235
|
-
{
|
|
236
|
-
// 2026-05-13 Q3 fix(per codex Q3 verdict + color.spec.md:729 逃生艙 rule):
|
|
237
|
-
// 移除 `opacity-disabled` blanket — naked wrapper 只負責 substrate(透明 border + 抑制 cursor),
|
|
238
|
-
// 內部 content(text / icon / avatar)各自 disabled context 處理(text-fg-disabled / Avatar opacity 等具體 token)。
|
|
239
|
-
// 對齊 DataTable cell-disabled TD 加 `bg-disabled` 表達 cell-level disabled state 的 SSOT 分權。
|
|
240
|
-
mode: 'disabled',
|
|
241
|
-
variant: 'naked',
|
|
242
|
-
className: [
|
|
243
|
-
'bg-transparent !rounded-none cursor-not-allowed !px-0 !py-0 !h-auto',
|
|
244
|
-
'border border-transparent',
|
|
245
|
-
],
|
|
246
|
-
},
|
|
236
|
+
// 2026-07-16 round16:`readonly×naked` / `disabled×naked` 死格移除 —— DataTable cell 廢除
|
|
237
|
+
// disabled 態(view 一律 mode="view";displayOrDisabled helper 已刪)+ readonly 從不入 naked
|
|
238
|
+
// 路徑(全庫 0 消費)。軸對稱靠 cva 省略即天然覆蓋(base+size+透明),不需 dead compound 佔位。
|
|
247
239
|
],
|
|
248
240
|
defaultVariants: {
|
|
249
241
|
mode: 'edit',
|
|
@@ -264,7 +256,7 @@ export const bareInputStyles = [
|
|
|
264
256
|
// bareInputStyles 套上,placeholder 無 ellipsis → narrow cell(<160px)硬裁無 `...`(user round 3
|
|
265
257
|
// 圖一證據)。加 truncate 後 `<input>` `text-overflow: ellipsis` 啟動,符合 user 「placeholder 直接被截掉沒有變...」
|
|
266
258
|
// 該顯 ellipsis 的 SSOT。對齊 data-table.spec.md:233「禁硬裁無 ellipsis」+ field-controls.spec.md:286
|
|
267
|
-
// 共享 contract(a)「
|
|
259
|
+
// 共享 contract(a)「view/readonly/disabled/edit 4 mode 共享同一 renderer」semantic 對齊。
|
|
268
260
|
'flex-1 min-w-0 truncate bg-transparent',
|
|
269
261
|
'outline-none border-none p-0',
|
|
270
262
|
'text-[inherit] font-[inherit] leading-[inherit]',
|
|
@@ -300,13 +292,13 @@ export const bareInputStyles = [
|
|
|
300
292
|
// Audit:design-system-audit Group N(periodic batch verify)
|
|
301
293
|
export const nakedCellRowModeAlign = 'group-data-[row-mode=auto]/cell:items-start'
|
|
302
294
|
|
|
303
|
-
// ── Cell-as-input
|
|
304
|
-
// editable cell **
|
|
295
|
+
// ── Cell-as-input View Hover Ring(2026-05-05 v9 — sole remaining ring const)─
|
|
296
|
+
// editable cell **view mode hover 提示**(「這 cell 可編,點 → 進 edit」affordance 信號)。
|
|
305
297
|
// 對齊 Notion / Airtable hover-cell-shows-border canonical。
|
|
306
298
|
//
|
|
307
299
|
// **為何只剩這一個**:Field naked **edit/focus/open state ring 已下沉到 Field default state
|
|
308
300
|
// machine**(border-based,2026-05-05 v9 architectural rewrite),不需 outline 平行系統;
|
|
309
|
-
// 但
|
|
301
|
+
// 但 view mode 沒 Field state(view = 純展示無互動),hover 提示需 cell wrapper 自加。
|
|
310
302
|
//
|
|
311
303
|
// **2026-05-09 v15.17 revert v15.16(user 未同意 ship)— 維持 v15.13 outline+offset:-1**
|
|
312
304
|
//
|
|
@@ -359,7 +351,7 @@ export const nakedCellEditableDisplayHover = 'hover:outline hover:outline-1 hove
|
|
|
359
351
|
// ── Empty Value Display ─────────────────────────────────────────────────────
|
|
360
352
|
|
|
361
353
|
// 2026-07-08 user 拍板(verbatim「我從頭到尾哪裡有說要用全形的」):空值符號 = **半形 hyphen
|
|
362
|
-
// `-`(U+002D)**,非全形 em dash `—`(U+2014)。不可編輯(readonly / standalone
|
|
354
|
+
// `-`(U+002D)**,非全形 em dash `—`(U+2014)。不可編輯(readonly / standalone view /
|
|
363
355
|
// table 不可編輯 cell)空值一律此符號 + `text-fg-muted`。分流邏輯 SSOT = field-context.ts
|
|
364
356
|
// `useFieldEmptyDisplay()`(table-cell 可編輯 → 空白;其餘 → 本常數)。
|
|
365
357
|
// 世界級對照:Ant ProTable `columnEmptyText` 預設 `'-'`(U+002D 半形)
|
|
@@ -367,10 +359,34 @@ export const nakedCellEditableDisplayHover = 'hover:outline hover:outline-1 hove
|
|
|
367
359
|
export const EMPTY_DISPLAY = '-'
|
|
368
360
|
|
|
369
361
|
/**
|
|
370
|
-
* 2026-05-14 I2 fix(per field-controls.spec.md contract (e)
|
|
371
|
-
* Field family
|
|
372
|
-
* 跨 9 元件
|
|
362
|
+
* 2026-05-14 I2 fix(per field-controls.spec.md contract (e) view typography canonical):
|
|
363
|
+
* Field family view path bare-span helper — `sm/md → text-body` / `lg → text-body-lg`,
|
|
364
|
+
* 跨 9 元件 view 視覺尺寸統一(user 抓 LinkInput 字體跟其他 Field 不一致 = SSOT 違反)。
|
|
373
365
|
* Consumer:LinkInput / Select / Combobox / DatePicker / TimePicker non-D-path bare-span 套此 class。
|
|
374
366
|
*/
|
|
375
367
|
export const fieldDisplayTextClass = (size: 'sm' | 'md' | 'lg'): string =>
|
|
376
368
|
size === 'lg' ? 'text-body-lg' : 'text-body'
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* 2026-07-16 round16 Model A(user GO):Field view mode 幾何 SSOT(= view×default compound 的
|
|
372
|
+
* 幾何部分,抽成 class helper 供 **InlineEdit 純值/標題路徑** 消費,避免 InlineEdit 自帶重複的
|
|
373
|
+
* geometry cva〔M17 消重〕)。
|
|
374
|
+
*
|
|
375
|
+
* 回傳「edit 幾何減 chrome」的排版盒:
|
|
376
|
+
* - 水平:`px-[var(--field-px)]`(= edit 內距,靠 InlineEdit `-mx` 拉回欄左緣後,值落 edit 值位置)
|
|
377
|
+
* - 單行:`min-h-[var(--field-height-N)]` N∈{sm,md,lg} + `items-center`(垂直置中於 field 高度,= edit 單行)
|
|
378
|
+
* - 多行:`items-start` + `py-2`(頂對齊 + 上下內距,**= Textarea edit `py-2`**,textarea.tsx base;
|
|
379
|
+
* read↔edit 零跳)。⚠️ py-2 與 Textarea 同值 = SSOT 契約,`scripts/inline-edit-view-geometry-invariant.mjs`
|
|
380
|
+
* 機械鎖(Textarea py 一改即紅)。
|
|
381
|
+
*
|
|
382
|
+
* 值-格式化路徑(Select→Tag / Date / avatar)**不用**此 helper —— 直接委派 `<Control mode="view">`,
|
|
383
|
+
* 幾何 + 內部間距皆由控件 view×default 提供(read=edit 同一顆 → 天生一致)。
|
|
384
|
+
*/
|
|
385
|
+
export const fieldViewGeometry = (size: 'sm' | 'md' | 'lg', multiline?: boolean): string =>
|
|
386
|
+
cn(
|
|
387
|
+
'flex w-full min-w-0 px-[var(--field-px)]',
|
|
388
|
+
size === 'sm' && 'min-h-[var(--field-height-sm)]',
|
|
389
|
+
size === 'md' && 'min-h-[var(--field-height-md)]',
|
|
390
|
+
size === 'lg' && 'min-h-[var(--field-height-lg)]',
|
|
391
|
+
multiline ? 'items-start py-2' : 'items-center',
|
|
392
|
+
)
|
|
@@ -53,7 +53,7 @@ export const Overview: Story = {
|
|
|
53
53
|
<tr><Td>資料格式化(千分位/貨幣/locale)</Td><Td>—</Td><Td>✓ 管</Td></tr>
|
|
54
54
|
<tr><Td>驗證邏輯</Td><Td>—</Td><Td>✓ 管(+ zod schema 於 form 層)</Td></tr>
|
|
55
55
|
<tr><Td>readonly 呈現</Td><Td>—</Td><Td>✓ 管</Td></tr>
|
|
56
|
-
<tr><Td>DataTable cell 顯示</Td><Td>—</Td><Td>✓ 管(mode="
|
|
56
|
+
<tr><Td>DataTable cell 顯示</Td><Td>—</Td><Td>✓ 管(mode="view" 渲染分支)</Td></tr>
|
|
57
57
|
</tbody>
|
|
58
58
|
</table>
|
|
59
59
|
</div>
|
|
@@ -72,8 +72,8 @@ export const Overview: Story = {
|
|
|
72
72
|
['invalid', 'boolean', 'false', 'error 狀態 + context.invalid(觸發 aria-invalid)'],
|
|
73
73
|
['disabled', 'boolean', 'false', 'context.disabled 傳給 control'],
|
|
74
74
|
['size', "'sm' | 'md' | 'lg'", "'md'", 'context.size 傳給 input-class control(Input / NumberInput / Select);primitive 不讀'],
|
|
75
|
-
['mode', "'edit' | '
|
|
76
|
-
['variant', "'default'", "'default'", "視覺外殼(公開型別僅 'default',2026-07-14 收窄;naked = @internal FieldVariantInternal):default 含 border+bg / naked = DataTable cell substrate:edit×naked 自畫 border-based state machine(rest/hover/focus/error),
|
|
75
|
+
['mode', "'edit' | 'view' | 'readonly' | 'disabled'", "'edit'", 'context.mode 傳給 Field Controls'],
|
|
76
|
+
['variant', "'default'", "'default'", "視覺外殼(公開型別僅 'default',2026-07-14 收窄;naked = @internal FieldVariantInternal):default 含 border+bg / naked = DataTable cell substrate:edit×naked 自畫 border-based state machine(rest/hover/focus/error),view×naked 用 transparent border;2026-07-09 `bare` 退役"],
|
|
77
77
|
['controlLayout', "'inline' | 'block'", '(自動偵測)', '逃生艙:覆寫 control area 佈局(consumer 手寫 JSX 當 control 無法偵測時用)'],
|
|
78
78
|
].map(([p, t, d, desc]) => (
|
|
79
79
|
<tr key={p}><Td mono>{p}</Td><Td mono>{t}</Td><Td mono>{d}</Td><Td>{desc}</Td></tr>
|
|
@@ -109,7 +109,7 @@ export const Overview: Story = {
|
|
|
109
109
|
interface InspectorArgs {
|
|
110
110
|
orientation: 'vertical' | 'horizontal'
|
|
111
111
|
size: 'sm' | 'md' | 'lg'
|
|
112
|
-
mode: 'edit' | '
|
|
112
|
+
mode: 'edit' | 'view' | 'readonly' | 'disabled'
|
|
113
113
|
required: boolean
|
|
114
114
|
invalid: boolean
|
|
115
115
|
disabled: boolean
|
|
@@ -148,8 +148,8 @@ export const Inspector: Story = {
|
|
|
148
148
|
},
|
|
149
149
|
mode: {
|
|
150
150
|
control: 'radio',
|
|
151
|
-
options: ['edit', '
|
|
152
|
-
description: 'Context 傳給控制元件:edit 可編輯 /
|
|
151
|
+
options: ['edit', 'view', 'readonly', 'disabled'],
|
|
152
|
+
description: 'Context 傳給控制元件:edit 可編輯 / view 純展示 / readonly 鎖定但保留輸入外觀 / disabled 停用',
|
|
153
153
|
},
|
|
154
154
|
required: { control: 'boolean', description: 'label 文字前加 * + aria-required' },
|
|
155
155
|
invalid: { control: 'boolean', description: '觸發 error border + aria-invalid;本例以 FieldError 條件替換 FieldDescription(consumer 慣例,非元件行為)' },
|