@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,30 +1,33 @@
|
|
|
1
1
|
// ── 消費的 SSOT ──
|
|
2
|
-
// - components/Field/field-controls.spec.md(
|
|
2
|
+
// - components/Field/field-controls.spec.md(「軸二 就地編輯 host」段:InlineEdit = view↔edit 二態疊在 Field 之上;edit-in-place + editable 閘)
|
|
3
3
|
// - components/Field/field-edit-keys.ts(makeEditSettleKeyHandler = Enter/Esc + 中文 IME guard 結算 SSOT,與 DataTable cell 同源)
|
|
4
|
+
// - components/Field/field-wrapper.tsx(fieldViewGeometry = view 幾何 class SSOT〔純值/標題路徑〕;fieldDisplayTextClass = view 字級)
|
|
4
5
|
// - components/Input/input.tsx / Textarea/textarea.tsx(預設 renderEdit;不重刻 input chrome)
|
|
5
|
-
// - components/Field/field-context.ts(
|
|
6
|
-
// - tokens/uiSize/uiSize.css(--field-px 水平內距 SSOT / --field-height-{sm,md,lg}
|
|
6
|
+
// - components/Field/field-context.ts(useFieldContext / useResolvedFieldSize;接 size/orientation/labelId cascade)
|
|
7
|
+
// - tokens/uiSize/uiSize.css(--field-px 水平內距 SSOT / --field-height-{sm,md,lg} view 態高度)
|
|
7
8
|
// - tokens/color/color.spec.md(bg-neutral-hover = hover 回饋)
|
|
8
|
-
// - components/Field/field-wrapper.tsx(read focus = border-primary,消費 Field edit focus 藍框語言;非 Button ring)
|
|
9
9
|
// - 世界級對照(泛型 readView/editView 就地編輯 + read 態底色 tint + 隱形 Pressable):
|
|
10
|
-
// Atlassian inline-edit(readView/editView 兩 render prop +
|
|
11
|
-
// https://github.com/pioug/atlassian-frontend-mirror/blob/main/design-system/inline-edit/src/types.tsx
|
|
12
|
-
// read-view hover = 背景 tint 非邊框 + 隱形 Pressable 供鍵盤 focus+Enter:
|
|
10
|
+
// Atlassian inline-edit(readView/editView 兩 render prop + read wrapper 靠負邊距對齊 edit)
|
|
13
11
|
// https://github.com/pioug/atlassian-frontend-mirror/blob/main/design-system/inline-edit/src/internal/read-view.tsx
|
|
12
|
+
// 就地編輯無 disabled 態(鎖定 = 純 view 無入口、不灰化):MUI X isCellEditable / cellMode
|
|
13
|
+
// https://mui.com/x/react-data-grid/editing/ + AG Grid editable https://www.ag-grid.com/react-data-grid/cell-editing/
|
|
14
14
|
//
|
|
15
|
-
// ──
|
|
16
|
-
// InlineEdit
|
|
17
|
-
// Tag /
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
15
|
+
// ── Model A(2026-07-16 round16,user GO)──────────────────────────────────────
|
|
16
|
+
// InlineEdit **不自帶 geometry cva**(消 M17 重複):
|
|
17
|
+
// - 值-格式化(Select→Tag / Date / avatar)→ renderRead 委派 `<Control mode="view">`,幾何 + 內部間距
|
|
18
|
+
// 皆由控件 view×default 提供(read=edit 同一顆 → 天生一致、多 tag/avatar 間距零偏移)。
|
|
19
|
+
// - 純值 / 標題 `<Tag as="h1">` → 套 `fieldViewGeometry(size, multiline)` helper(= view×default 幾何 SSOT)。
|
|
20
|
+
// InlineEdit 本體只給:`-mx`(orientation-aware,拉到欄左緣,= 上方 read-view 負邊距對照)+ hover bg + focus 藍框 + 隱形 Pressable。
|
|
21
|
+
// read↔edit 零跳 = view 與 edit 同一顆控件、只差 chrome。
|
|
22
|
+
// **格式化 read 鐵律**:renderRead 要顯示 Tag/日期/label,**渲染對應控件的 `mode="view"`**
|
|
23
|
+
// (如 `<Select mode="view" display="tag">`),不可自刻 —— 格式化 SSOT 住在該控件的 view mode。
|
|
21
24
|
import * as React from 'react'
|
|
22
25
|
import { cn } from '@/lib/utils'
|
|
23
26
|
import { Input } from '@/design-system/components/Input/input'
|
|
24
27
|
import { Textarea } from '@/design-system/components/Textarea/textarea'
|
|
25
28
|
import type { FieldSize } from '@/design-system/components/Field/field-context'
|
|
26
|
-
import {
|
|
27
|
-
import { fieldDisplayTextClass } from '@/design-system/components/Field/field-wrapper'
|
|
29
|
+
import { useFieldContext, useResolvedFieldSize, FieldSurfaceProvider } from '@/design-system/components/Field/field-context'
|
|
30
|
+
import { fieldDisplayTextClass, fieldViewGeometry } from '@/design-system/components/Field/field-wrapper'
|
|
28
31
|
import { makeEditSettleKeyHandler } from '@/design-system/components/Field/field-edit-keys'
|
|
29
32
|
|
|
30
33
|
// ── Types ───────────────────────────────────────────────────────────────────
|
|
@@ -35,13 +38,13 @@ export interface InlineEditRenderProps<T = string> {
|
|
|
35
38
|
value: T
|
|
36
39
|
/** 草稿變更 */
|
|
37
40
|
onChange: (next: T) => void
|
|
38
|
-
/** 提交草稿 → onCommit(next) + 回
|
|
41
|
+
/** 提交草稿 → onCommit(next) + 回 view 態 */
|
|
39
42
|
commit: () => void
|
|
40
|
-
/** 放棄草稿 → 回
|
|
43
|
+
/** 放棄草稿 → 回 view 態不 commit */
|
|
41
44
|
cancel: () => void
|
|
42
45
|
/** 進 edit 態時自動 focus 控件 */
|
|
43
46
|
autoFocus: boolean
|
|
44
|
-
/** 尺寸(cascade 給控件,與
|
|
47
|
+
/** 尺寸(cascade 給控件,與 view 態高度對齊) */
|
|
45
48
|
size: FieldSize
|
|
46
49
|
/** 控件無障礙標籤(= label prop) */
|
|
47
50
|
'aria-label'?: string
|
|
@@ -50,78 +53,57 @@ export interface InlineEditRenderProps<T = string> {
|
|
|
50
53
|
type ReadTag = 'span' | 'p' | 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
|
|
51
54
|
|
|
52
55
|
export interface InlineEditProps<T = string> {
|
|
53
|
-
/** 已提交的欄位值(
|
|
56
|
+
/** 已提交的欄位值(view 態顯示;edit 進入時作草稿初值)。泛型:string / number / string[] / PersonValue 等 */
|
|
54
57
|
value: T
|
|
55
58
|
/** 提交回呼 —— blur / Enter 且草稿有變更時觸發 */
|
|
56
59
|
onCommit: (next: T) => void
|
|
57
60
|
/**
|
|
58
|
-
* 自訂 edit 態控件(換 Textarea / Select / DatePicker 等 Field
|
|
59
|
-
* 不傳則預設:multiline → `<Textarea>`,否則 `<Input>`(僅 T=string 時預設有效;非 string 值必傳此 prop)。
|
|
61
|
+
* 自訂 edit 態控件(換 Textarea / Select / DatePicker 等 Field 控件,必用 `mode="edit"`)。
|
|
62
|
+
* 不傳則預設:multiline → `<Textarea mode="edit">`,否則 `<Input mode="edit">`(僅 T=string 時預設有效;非 string 值必傳此 prop)。
|
|
60
63
|
*/
|
|
61
64
|
renderEdit?: (props: InlineEditRenderProps<T>) => React.ReactNode
|
|
62
65
|
/**
|
|
63
|
-
* 自訂
|
|
64
|
-
* **SSOT 鐵律**:要顯示 Tag / 日期格式 / option label 等,**渲染對應控件的 `mode="
|
|
65
|
-
* (如 `renderRead={(v) => <Select mode="
|
|
66
|
-
* 不可自刻 —— 格式化 SSOT 住在該控件的
|
|
66
|
+
* 自訂 view 態格式化顯示。不傳則顯示 `String(value)`(空值顯 placeholder)。
|
|
67
|
+
* **SSOT 鐵律**:要顯示 Tag / 日期格式 / option label 等,**渲染對應控件的 `mode="view"`**
|
|
68
|
+
* (如 `renderRead={(v) => <Select mode="view" display="tag" value={v} options={opts} />}`),
|
|
69
|
+
* 不可自刻 —— 格式化 + 內部間距 SSOT 住在該控件的 view mode(見檔頭 Model A)。
|
|
67
70
|
*/
|
|
68
71
|
renderRead?: (value: T) => React.ReactNode
|
|
69
|
-
/** 多行欄位(Jira description 類):
|
|
72
|
+
/** 多行欄位(Jira description 類):view 態換行 + 預設 edit 態用 `<Textarea>`,Enter=換行、Cmd/Ctrl+Enter 或 blur 提交 */
|
|
70
73
|
multiline?: boolean
|
|
71
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* 是否可編輯(預設 true)。false → view 態無 hover 入口、無藍框、不可點進 edit(對齊世界級就地編輯
|
|
76
|
+
* **無 disabled 態**,鎖定 = 純 view 無入口、**不灰化**;cite 見檔頭 MUI X / AG Grid URL)。
|
|
77
|
+
* 永久唯讀資料 → 用 `<Control mode="view">` 或此 prop false;不用 disabled。
|
|
78
|
+
*/
|
|
79
|
+
editable?: boolean
|
|
80
|
+
/** 欄位人類可讀名稱 —— 組 view 按鈕 aria-label(「編輯 {label}」)+ 控件 aria-label */
|
|
72
81
|
label?: string
|
|
73
|
-
/** 空值時
|
|
82
|
+
/** 空值時 view 態顯示的提示文字(fg-muted) */
|
|
74
83
|
placeholder?: string
|
|
75
|
-
/** 尺寸 ——
|
|
84
|
+
/** 尺寸 —— view 態最小高度 + 預設 Input 尺寸(不傳則經 useResolvedFieldSize 接 Field size cascade,standalone fallback sm) */
|
|
76
85
|
size?: FieldSize
|
|
77
|
-
/**
|
|
86
|
+
/** view 態語意標籤(標題場景傳 'h1'/'h2' 維持文件大綱;預設 'span') */
|
|
78
87
|
as?: ReadTag
|
|
79
|
-
/**
|
|
88
|
+
/** view 態文字樣式(如標題 `text-h4 font-bold`) */
|
|
80
89
|
readClassName?: string
|
|
81
90
|
/** 外層容器 className */
|
|
82
91
|
className?: string
|
|
83
92
|
}
|
|
84
93
|
|
|
85
|
-
// ── Read-view geometry cva ────────────────────────────────────────────────────
|
|
86
|
-
// base = 排版結構;size variant = min-height(與 edit 態 Field 高度一致,降低態切換位移)。
|
|
87
|
-
// multiline variant:單行 → `items-center`(文字垂直置中於 field-height);多行 → `items-start py-1.5`
|
|
88
|
-
// (頂對齊 + 上下內距,對齊 Textarea `py-2` 多行閱讀語意,內容換行往下長)。
|
|
89
|
-
// read 態幾何典範對照(M22 cite,2026-07-09 逐行核對原始碼,見檔頭 read-view URL):
|
|
90
|
-
// hover = 底色 tint 非邊框;focus = 實線 solid border;wrapper 恆帶透明 border 預留邊框盒。
|
|
91
|
-
const inlineEditReadStyles = cva(
|
|
92
|
-
// relative(疊隱形 Pressable)/ border-transparent(預留 1px 邊框盒 → 態切換零位移;focus 才染色)/
|
|
93
|
-
// px-[--field-px](文字↔底色水平內距 = Field 內距 SSOT,read=edit x 位置零跳)/ hover 底色 tint /
|
|
94
|
-
// [&:has(button:focus-visible)]:border-primary(鍵盤 focus = Field edit 藍框語言,非 Button ring)。
|
|
95
|
-
'relative flex rounded-md border border-transparent px-[var(--field-px)] transition-colors duration-150 hover:bg-neutral-hover [&:has(button:focus-visible)]:border-primary',
|
|
96
|
-
{
|
|
97
|
-
variants: {
|
|
98
|
-
size: {
|
|
99
|
-
sm: 'min-h-[var(--field-height-sm)]',
|
|
100
|
-
md: 'min-h-[var(--field-height-md)]',
|
|
101
|
-
lg: 'min-h-[var(--field-height-lg)]',
|
|
102
|
-
},
|
|
103
|
-
multiline: {
|
|
104
|
-
false: 'items-center',
|
|
105
|
-
true: 'items-start py-1.5',
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
// 預設 sm + 單行(2026-07-09 user 拍板):read 態無邊框、視覺 = 純文字,尺寸過大會讓版面鬆散。
|
|
109
|
-
defaultVariants: { size: 'sm', multiline: false },
|
|
110
|
-
},
|
|
111
|
-
)
|
|
112
|
-
|
|
113
94
|
/**
|
|
114
|
-
* InlineEdit — 就地編輯 primitive(
|
|
95
|
+
* InlineEdit — 就地編輯 primitive(view ↔ edit 二態,泛型 <T>)
|
|
115
96
|
*
|
|
116
97
|
* 互動模型 world-class 對照見檔頭「消費的 SSOT」+ inline-edit.spec.md benchmark 表:
|
|
117
|
-
* - **
|
|
118
|
-
* - **hover(
|
|
119
|
-
* - **鍵盤 focus(
|
|
120
|
-
* - **click / Enter/Space** = 切 edit 態 = 真正的 Field 控件(border + focus + input);多行走 Textarea
|
|
121
|
-
* - **退出**:blur
|
|
98
|
+
* - **view 態** = 純文字 / 格式化 renderRead、寬度 fill 容器、透明邊框(預留)→ 視覺 = 純內容
|
|
99
|
+
* - **hover(view,editable)** = 灰色底色 `bg-neutral-hover` + `rounded-md`(**非邊框**)+ cursor
|
|
100
|
+
* - **鍵盤 focus(view,editable)** = 外框染 `border-primary` 藍框(Field focus 語言,非 Button ring)
|
|
101
|
+
* - **click / Enter/Space** = 切 edit 態 = 真正的 Field 控件(mode="edit":border + focus + input);多行走 Textarea
|
|
102
|
+
* - **退出**:blur(滑鼠)→ commit + 純 view 不搶焦;Enter(鍵盤)→ commit + 焦點回 view 按鈕 + 藍框;Esc → cancel 還原
|
|
122
103
|
* - **鍵盤結算**:消費 `makeEditSettleKeyHandler`(含中文 IME guard,與 DataTable cell 同源)
|
|
104
|
+
* - **editable=false**:view 無 hover / 無 Pressable / 不可進 edit、**不灰化**(detail-pane 鎖定語意)
|
|
123
105
|
*
|
|
124
|
-
*
|
|
106
|
+
* view 態用隱形 Pressable(絕對定位 `<button>` 疊於內容上)—— 讓 `as="h1"` 等標題標籤保留文件大綱
|
|
125
107
|
* a11y,同時提供鍵盤 focus + Enter 進 edit(HTML 內容模型不允許 heading 巢狀於 button,故分離)。
|
|
126
108
|
*/
|
|
127
109
|
function InlineEditImpl<T = string>(
|
|
@@ -131,24 +113,43 @@ function InlineEditImpl<T = string>(
|
|
|
131
113
|
renderEdit,
|
|
132
114
|
renderRead,
|
|
133
115
|
multiline = false,
|
|
116
|
+
editable = true,
|
|
134
117
|
label,
|
|
135
118
|
placeholder,
|
|
136
|
-
size
|
|
119
|
+
size: sizeProp,
|
|
137
120
|
as = 'span',
|
|
138
121
|
readClassName,
|
|
139
122
|
className,
|
|
140
123
|
}: InlineEditProps<T>,
|
|
141
124
|
ref: React.Ref<HTMLDivElement>,
|
|
142
125
|
) {
|
|
126
|
+
// size:接 fieldCtx.size cascade,standalone fallback sm(InlineEdit 靜態 fieldPreferredSize='sm'
|
|
127
|
+
// 讓外層 `<Field>` 自動收 sm — 見檔尾 static)。
|
|
128
|
+
const size = useResolvedFieldSize(sizeProp, 'sm')
|
|
129
|
+
// orientation:vertical(值貼 label 左緣 → 用 -mx)/ horizontal(值落內容欄左緣+field-px = 對齊 sibling 控件 → 不用 -mx)。
|
|
130
|
+
const fieldCtx = useFieldContext()
|
|
131
|
+
const orientation = fieldCtx?.orientation ?? 'vertical'
|
|
132
|
+
|
|
143
133
|
const [editing, setEditing] = React.useState(false)
|
|
144
134
|
const [draft, setDraft] = React.useState<T>(value)
|
|
145
135
|
// draft 最新值 ref —— commit 從事件 handler 呼叫時避免 stale closure
|
|
146
136
|
const draftRef = React.useRef(draft)
|
|
147
137
|
// 單次結算 guard —— Enter/Esc 已結算後,unmount 觸發的 onBlur 不再重複 commit
|
|
148
138
|
const finalizedRef = React.useRef(false)
|
|
149
|
-
// 鍵盤結算後把 focus 送回
|
|
139
|
+
// 鍵盤結算後把 focus 送回 view 按鈕(a11y:焦點不遺失);滑鼠 blur 不送回(不搶焦)
|
|
150
140
|
const returnFocusRef = React.useRef(false)
|
|
151
141
|
const readButtonRef = React.useRef<HTMLButtonElement>(null)
|
|
142
|
+
// edit 態容器 ref(pointerdown-outside 用);與 forwarded ref merge。
|
|
143
|
+
// 顯式 `| null` → mutable overload(手動賦值 .current;`useRef<T>(null)` 會匹配 readonly RefObject overload)。
|
|
144
|
+
const editContainerRef = React.useRef<HTMLDivElement | null>(null)
|
|
145
|
+
const setEditContainerRef = React.useCallback(
|
|
146
|
+
(node: HTMLDivElement | null) => {
|
|
147
|
+
editContainerRef.current = node
|
|
148
|
+
if (typeof ref === 'function') ref(node)
|
|
149
|
+
else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node
|
|
150
|
+
},
|
|
151
|
+
[ref],
|
|
152
|
+
)
|
|
152
153
|
|
|
153
154
|
const setDraftValue = React.useCallback((next: T) => {
|
|
154
155
|
draftRef.current = next
|
|
@@ -161,23 +162,47 @@ function InlineEditImpl<T = string>(
|
|
|
161
162
|
setEditing(true)
|
|
162
163
|
}, [value, setDraftValue])
|
|
163
164
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
165
|
+
// commit(returnFocus):鍵盤結算傳 true(焦點回 view 按鈕);滑鼠 blur 傳 false(不搶焦、純 view)
|
|
166
|
+
const commit = React.useCallback(
|
|
167
|
+
(returnFocus: boolean) => {
|
|
168
|
+
if (finalizedRef.current) return
|
|
169
|
+
finalizedRef.current = true
|
|
170
|
+
returnFocusRef.current = returnFocus
|
|
171
|
+
setEditing(false)
|
|
172
|
+
if (draftRef.current !== value) onCommit(draftRef.current)
|
|
173
|
+
},
|
|
174
|
+
[value, onCommit],
|
|
175
|
+
)
|
|
171
176
|
|
|
172
177
|
const cancel = React.useCallback(() => {
|
|
173
178
|
if (finalizedRef.current) return
|
|
174
179
|
finalizedRef.current = true
|
|
175
|
-
returnFocusRef.current = true
|
|
180
|
+
returnFocusRef.current = true // Esc = 鍵盤路徑,焦點回 view 按鈕
|
|
176
181
|
setEditing(false)
|
|
177
182
|
}, [])
|
|
178
183
|
|
|
179
|
-
//
|
|
180
|
-
//
|
|
184
|
+
// ── Exit-on-blur SSOT(2026-07-17 root cause 修:blur 後 chrome 殘留)──────────────
|
|
185
|
+
// InlineEdit **保證**任何 edit 控件(預設 Input + 自訂 renderEdit 的 Select/DatePicker/Combobox…)
|
|
186
|
+
// blur 都回 view,**不靠** consumer 在 renderEdit 自接 onBlur(舊設計缺口:story Select 只接
|
|
187
|
+
// onChange→commit,不選直接 blur → commit 從沒呼叫 → 卡 editing=true,edit chrome 殘留)。
|
|
188
|
+
// portal-aware:overlay 控件(Select/DatePicker)開的 Radix popper/listbox 是 portaled 出子樹,
|
|
189
|
+
// 焦點在其中時**不誤結算**(dropdown 操作中);真正離開(都不在 edit 子樹 + 不在本 edit 的 overlay)才 commit。
|
|
190
|
+
// rAF 延一拍讓焦點落定(Radix 關 popover 時焦點先回 trigger,避免 blur 瞬間誤判)。
|
|
191
|
+
const handleEditBlur = React.useCallback((e: React.FocusEvent<HTMLDivElement>) => {
|
|
192
|
+
const wrapper = e.currentTarget
|
|
193
|
+
requestAnimationFrame(() => {
|
|
194
|
+
if (finalizedRef.current) return
|
|
195
|
+
const active = document.activeElement
|
|
196
|
+
if (active && wrapper.contains(active)) return // 仍在 edit 子樹(如 trigger)
|
|
197
|
+
// 焦點在本 edit 開的 Radix overlay 內 → dropdown 操作中,不結算
|
|
198
|
+
if (active && active.closest('[data-radix-popper-content-wrapper],[data-radix-select-viewport],[role="listbox"],[role="dialog"]')) return
|
|
199
|
+
commit(false) // blur 離開 → 回 view(滑鼠路徑不搶焦)
|
|
200
|
+
})
|
|
201
|
+
}, [commit])
|
|
202
|
+
|
|
203
|
+
// 鍵盤結算後把 focus 送回 view 按鈕(commit(true)/cancel);滑鼠 blur(commit(false))刻意不送回——
|
|
204
|
+
// 對齊 inline-edit.spec.md「退出 edit 態(focus 分流)」:鍵盤結算焦點回 view 按鈕 + 藍框、
|
|
205
|
+
// 滑鼠 blur 回純 view 不搶焦。focus-visible 保證只有鍵盤路徑顯藍框。
|
|
181
206
|
React.useEffect(() => {
|
|
182
207
|
if (!editing && returnFocusRef.current) {
|
|
183
208
|
returnFocusRef.current = false
|
|
@@ -185,95 +210,153 @@ function InlineEditImpl<T = string>(
|
|
|
185
210
|
}
|
|
186
211
|
}, [editing])
|
|
187
212
|
|
|
213
|
+
// ── Pointerdown-outside exit(2026-07-17,blur 殘留 root cause 真修)──────────────
|
|
214
|
+
// focusout 不夠:overlay 控件(Select)點外面關 popover 時 Radix 把焦點送回 trigger(在 edit 子樹內)
|
|
215
|
+
// → onBlur 不觸發 → 卡 editing。改用 document-level pointerdown-outside 偵測(對齊 Radix
|
|
216
|
+
// DismissableLayer onPointerDownOutside https://www.radix-ui.com/primitives/docs/utilities/dismissable-layer
|
|
217
|
+
// + Atlassian inline-edit click-outside 退出
|
|
218
|
+
// https://github.com/pioug/atlassian-frontend-mirror/blob/main/design-system/inline-edit/src/inline-edit.tsx):
|
|
219
|
+
// editing 時點在 edit 子樹外且不在本 edit popover 內的 pointerdown → commit 回 view。
|
|
220
|
+
// 保證任何控件(含 overlay)點外面都回 display,不靠 consumer 在 renderEdit 自接。
|
|
221
|
+
React.useEffect(() => {
|
|
222
|
+
if (!editing) return
|
|
223
|
+
const onPointerDown = (e: PointerEvent) => {
|
|
224
|
+
if (finalizedRef.current) return
|
|
225
|
+
const target = e.target as Element | null
|
|
226
|
+
if (!target) return
|
|
227
|
+
const box = editContainerRef.current
|
|
228
|
+
if (box && box.contains(target)) return // 點在 edit 子樹內(trigger 等)
|
|
229
|
+
// 點在本 edit 開的 Radix overlay 內(popper / listbox / dialog)→ dropdown 操作中,不結算
|
|
230
|
+
if (target.closest('[data-radix-popper-content-wrapper],[data-radix-select-viewport],[role="listbox"],[role="dialog"]')) return
|
|
231
|
+
commit(false)
|
|
232
|
+
}
|
|
233
|
+
document.addEventListener('pointerdown', onPointerDown, true)
|
|
234
|
+
return () => document.removeEventListener('pointerdown', onPointerDown, true)
|
|
235
|
+
}, [editing, commit])
|
|
236
|
+
|
|
188
237
|
const isEmpty =
|
|
189
238
|
value == null || (value as unknown) === '' || (Array.isArray(value) && value.length === 0)
|
|
190
239
|
const Tag = as as React.ElementType
|
|
191
|
-
|
|
192
|
-
//
|
|
193
|
-
|
|
240
|
+
|
|
241
|
+
// 對齊盒(orientation-aware,Model A;2026-07-17 移除 fieldCtx gate — root cause 修):
|
|
242
|
+
// vertical(預設,含 standalone)→ `-mx-field-px` + `w-calc` 把整塊拉到欄左緣(值貼 label 左緣;
|
|
243
|
+
// view 委派控件在 `surface='inline-edit'` 下 px 統一 field-px → 被 -mx 精準抵消 → 落 x=0 對齊 label)。
|
|
244
|
+
// horizontal → 不用 -mx(純 `w-full`,值內縮 px = 對齊 sibling 控件)。
|
|
245
|
+
// ⚠️ **移除舊 `fieldCtx &&` gate(root cause)**:standalone 預設 orientation='vertical'(:131),舊 gate 讓
|
|
246
|
+
// standalone 掉到 `w-full` 無 -mx → 內容內縮 field-px 不對齊 label(圖一/圖二 bug)。spec Model A
|
|
247
|
+
// (field-controls.spec.md:71「orientation-aware -mx / vertical 貼 label」)只依 orientation、不依 fieldCtx。
|
|
248
|
+
// -mx bleed field-px 到容器 padding = 刻意(hover/edit 才顯真實邊界)→ consumer 放 ≥field-px padding 容器
|
|
249
|
+
// (detail panel / card;stories 容器 layout-space-loose 16px > field-px 12px 天然吸收)。
|
|
250
|
+
const alignBleed =
|
|
251
|
+
orientation === 'vertical'
|
|
252
|
+
? '-mx-[var(--field-px)] w-[calc(100%_+_2_*_var(--field-px))]'
|
|
253
|
+
: 'w-full min-w-0'
|
|
194
254
|
|
|
195
255
|
if (editing) {
|
|
196
256
|
const editProps: InlineEditRenderProps<T> = {
|
|
197
257
|
value: draft,
|
|
198
258
|
onChange: setDraftValue,
|
|
199
|
-
commit,
|
|
259
|
+
commit: () => commit(true),
|
|
200
260
|
cancel,
|
|
201
261
|
autoFocus: true,
|
|
202
262
|
size,
|
|
203
263
|
'aria-label': label,
|
|
204
264
|
}
|
|
205
265
|
// 預設 edit 控件:multiline → Textarea(Enter=換行、Cmd/Ctrl+Enter/blur 提交);否則 Input。
|
|
206
|
-
//
|
|
207
|
-
//
|
|
266
|
+
// 兩者皆 mode="edit"(edit 態 = 真正的 edit-mode Field 控件),Enter/Esc 結算走 makeEditSettleKeyHandler(含 IME guard)。
|
|
267
|
+
// string-only 預設路徑;非 string 值必傳 renderEdit。
|
|
208
268
|
const defaultEditNode = multiline ? (
|
|
209
269
|
<Textarea
|
|
210
270
|
autoFocus
|
|
271
|
+
mode="edit"
|
|
211
272
|
value={draft as unknown as string}
|
|
212
273
|
size={size}
|
|
213
274
|
aria-label={label}
|
|
214
275
|
onChange={(e) => setDraftValue(e.target.value as unknown as T)}
|
|
215
|
-
onBlur={commit}
|
|
216
|
-
// 多行:plain Enter=換行、Esc=cancel、Cmd/Ctrl+Enter=commit —— 全走
|
|
217
|
-
|
|
218
|
-
onKeyDown={makeEditSettleKeyHandler({ onCommit: commit, onCancel: cancel, commitOnEnter: false })}
|
|
276
|
+
onBlur={() => commit(false)}
|
|
277
|
+
// 多行:plain Enter=換行、Esc=cancel、Cmd/Ctrl+Enter=commit —— 全走 makeEditSettleKeyHandler SSOT。
|
|
278
|
+
onKeyDown={makeEditSettleKeyHandler({ onCommit: () => commit(true), onCancel: cancel, commitOnEnter: false })}
|
|
219
279
|
/>
|
|
220
280
|
) : (
|
|
221
281
|
<Input
|
|
222
282
|
autoFocus
|
|
283
|
+
mode="edit"
|
|
223
284
|
value={draft as unknown as string}
|
|
224
285
|
size={size}
|
|
225
286
|
aria-label={label}
|
|
226
287
|
onChange={(e) => setDraftValue(e.target.value as unknown as T)}
|
|
227
|
-
onBlur={commit}
|
|
228
|
-
onKeyDown={makeEditSettleKeyHandler({ onCommit: commit, onCancel: cancel })}
|
|
288
|
+
onBlur={() => commit(false)}
|
|
289
|
+
onKeyDown={makeEditSettleKeyHandler({ onCommit: () => commit(true), onCancel: cancel })}
|
|
229
290
|
/>
|
|
230
291
|
)
|
|
231
292
|
const editNode = renderEdit ? renderEdit(editProps) : defaultEditNode
|
|
232
293
|
return (
|
|
233
|
-
|
|
294
|
+
// exit SSOT 雙保險:onBlur(focusout,tab-out / 非 overlay 控件)+ document pointerdown-outside(overlay
|
|
295
|
+
// 點外面 Radix 送焦回 trigger 時 focusout 不觸發之補位)。任何 edit 控件離開 → 回 view。
|
|
296
|
+
// edit 態**不套** surface='inline-edit'(完全繼承原 field,含 tagPadding — Jira 式,user 2026-07-17 拍板)。
|
|
297
|
+
<div ref={setEditContainerRef} data-editing className={cn(alignBleed, className)} onBlur={handleEditBlur}>
|
|
234
298
|
{editNode}
|
|
235
299
|
</div>
|
|
236
300
|
)
|
|
237
301
|
}
|
|
238
302
|
|
|
303
|
+
// view 態:外框只承載對齊盒 + (editable 時) hover bg + 鍵盤 focus 藍框;幾何由內容(委派控件 view / fieldViewGeometry)提供。
|
|
304
|
+
const viewNode = renderRead ? (
|
|
305
|
+
// 格式化 view(consumer 傳 <Control mode="view"> 等)。**對齊 SSOT(2026-07-17)**:委派控件的
|
|
306
|
+
// bare view 一律 0px 左內距(audit 證實 Select/Combobox/DatePicker/TimePicker/PeoplePicker/LinkInput
|
|
307
|
+
// 預設 view 皆裸 span/Tag),故 InlineEdit 用**與純值路徑同一個 `fieldViewGeometry(field-px + min-h +
|
|
308
|
+
// py)`** 包住 renderRead → 委派控件 view 左 px 統一 field-px、高度統一 field-height → 固定
|
|
309
|
+
// `-mx-field-px` 精準抵消 → tag/avatar/值左緣落 label x=0。不碰任何控件 tagPadding(bare view 本就不用)。
|
|
310
|
+
// surface='inline-edit' 保留為 per-control 逃生訊號(若某控件 bare view 有內部 inset 破壞對齊,該控件可
|
|
311
|
+
// 讀 surface 自抑;目前全控件 bare view=0px 不需)。
|
|
312
|
+
<FieldSurfaceProvider surface="inline-edit">
|
|
313
|
+
<div className={fieldViewGeometry(size, multiline)}>{renderRead(value)}</div>
|
|
314
|
+
</FieldSurfaceProvider>
|
|
315
|
+
) : (
|
|
316
|
+
<Tag
|
|
317
|
+
data-empty={isEmpty}
|
|
318
|
+
className={cn(
|
|
319
|
+
// 純值/標題共用 view 幾何 SSOT(px-field-px + min-h-field + 單行 items-center / 多行 items-start py-2)
|
|
320
|
+
fieldViewGeometry(size, multiline),
|
|
321
|
+
// view 態 typography 預設消費 Field view SSOT(sm/md→text-body,lg→text-body-lg)——
|
|
322
|
+
// plain 用法 view 字級 = edit <Input>(text-body)= <Input mode="view"> 字級,三者一致零跳字。
|
|
323
|
+
fieldDisplayTextClass(size),
|
|
324
|
+
// 多行:保留換行(pre-wrap)+ 長字斷行 —— view 忠實呈現 Textarea 內容(Jira description 類)。
|
|
325
|
+
multiline && 'whitespace-pre-wrap break-words',
|
|
326
|
+
readClassName,
|
|
327
|
+
isEmpty && 'text-fg-muted',
|
|
328
|
+
)}
|
|
329
|
+
>
|
|
330
|
+
{isEmpty ? placeholder : String(value)}
|
|
331
|
+
</Tag>
|
|
332
|
+
)
|
|
333
|
+
|
|
239
334
|
return (
|
|
240
335
|
<div
|
|
241
336
|
ref={ref}
|
|
242
|
-
data-editing={
|
|
243
|
-
|
|
244
|
-
className={cn(
|
|
337
|
+
data-editing={false}
|
|
338
|
+
data-editable={editable || undefined}
|
|
339
|
+
className={cn(
|
|
340
|
+
'relative flex min-w-0 rounded-md border border-transparent transition-colors duration-150',
|
|
341
|
+
alignBleed,
|
|
342
|
+
// editable 才有 hover 底色 + 鍵盤 focus 藍框(Field focus 語言,非 Button ring);
|
|
343
|
+
// editable=false = 純 view 鎖定,無入口、無藍框、不灰化。
|
|
344
|
+
editable && 'hover:bg-neutral-hover [&:has(button:focus-visible)]:border-primary',
|
|
345
|
+
className,
|
|
346
|
+
)}
|
|
245
347
|
>
|
|
246
|
-
{
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
<
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
// 多行:保留換行(pre-wrap)+ 長字斷行 —— read 忠實呈現 Textarea 內容(Jira description 類),
|
|
258
|
-
// 對齊 Textarea mode="display" 的 `whitespace-pre-wrap break-words`。
|
|
259
|
-
multiline && 'whitespace-pre-wrap break-words',
|
|
260
|
-
readClassName,
|
|
261
|
-
isEmpty && 'text-fg-muted',
|
|
262
|
-
)}
|
|
263
|
-
>
|
|
264
|
-
{isEmpty ? placeholder : String(value)}
|
|
265
|
-
</Tag>
|
|
348
|
+
{viewNode}
|
|
349
|
+
{/* 隱形 Pressable(僅 editable):提供 click + 鍵盤 Tab focus + Enter/Space 進 edit;透明疊於內容上。
|
|
350
|
+
hover 底色 + focus 藍框由外層 div 承載(Field focus 語言),故本 button 只需 outline-none 消瀏覽器預設外框。 */}
|
|
351
|
+
{editable && (
|
|
352
|
+
<button
|
|
353
|
+
ref={readButtonRef}
|
|
354
|
+
type="button"
|
|
355
|
+
aria-label={label ? `編輯 ${label}` : '編輯'}
|
|
356
|
+
onClick={enterEdit}
|
|
357
|
+
className="absolute inset-0 cursor-text rounded-md focus-visible:outline-none"
|
|
358
|
+
/>
|
|
266
359
|
)}
|
|
267
|
-
{/* 隱形 Pressable:提供 click + 鍵盤 Tab focus + Enter/Space 進 edit;透明疊於內容上。
|
|
268
|
-
hover 底色 + focus 藍框(border-primary)由外層 div 承載(Field focus 語言,非 ring),
|
|
269
|
-
故本 button 只需 outline-none 消除瀏覽器預設外框。 */}
|
|
270
|
-
<button
|
|
271
|
-
ref={readButtonRef}
|
|
272
|
-
type="button"
|
|
273
|
-
aria-label={label ? `編輯 ${label}` : '編輯'}
|
|
274
|
-
onClick={enterEdit}
|
|
275
|
-
className="absolute inset-0 cursor-text rounded-md focus-visible:outline-none"
|
|
276
|
-
/>
|
|
277
360
|
</div>
|
|
278
361
|
)
|
|
279
362
|
}
|
|
@@ -281,7 +364,10 @@ function InlineEditImpl<T = string>(
|
|
|
281
364
|
// 泛型 forwardRef:forwardRef 不原生支援泛型,以 cast 還原公開型別(標準 workaround)。
|
|
282
365
|
const InlineEdit = React.forwardRef(InlineEditImpl) as (<T = string>(
|
|
283
366
|
props: InlineEditProps<T> & { ref?: React.Ref<HTMLDivElement> },
|
|
284
|
-
) => React.ReactElement) & { displayName?: string }
|
|
367
|
+
) => React.ReactElement) & { displayName?: string; fieldPreferredSize?: FieldSize }
|
|
285
368
|
InlineEdit.displayName = 'InlineEdit'
|
|
369
|
+
// 靜態偏好尺寸:外層 `<Field>` render 時讀 `child.type.fieldPreferredSize` → Field.size 未顯式指定時收 sm
|
|
370
|
+
// (對齊 detectControlLayout 讀 fieldLayout 的機制;InlineEdit view 態無邊框,md 過大會讓版面鬆散)。
|
|
371
|
+
InlineEdit.fieldPreferredSize = 'sm'
|
|
286
372
|
|
|
287
373
|
export { InlineEdit }
|
|
@@ -13,12 +13,12 @@ export default meta
|
|
|
13
13
|
Types & Data
|
|
14
14
|
═══════════════════════════════════════════════════════════════════════════ */
|
|
15
15
|
|
|
16
|
-
type ModeKey = 'edit' | '
|
|
16
|
+
type ModeKey = 'edit' | 'view' | 'readonly' | 'disabled'
|
|
17
17
|
type StateKey = 'default' | 'hover' | 'focus' | 'error' | 'disabled'
|
|
18
18
|
type SizeKey = 'sm' | 'md' | 'lg'
|
|
19
19
|
type ColorSpec = { bg: string; text: string; border: string; placeholder: string }
|
|
20
20
|
|
|
21
|
-
const MODES: ModeKey[] = ['edit', '
|
|
21
|
+
const MODES: ModeKey[] = ['edit', 'view', 'readonly', 'disabled']
|
|
22
22
|
const EDIT_STATES: StateKey[] = ['default', 'hover', 'focus', 'error', 'disabled']
|
|
23
23
|
const SIZES: SizeKey[] = ['sm', 'md', 'lg']
|
|
24
24
|
|
|
@@ -30,7 +30,7 @@ const COLOR_MAP: Record<ModeKey, Partial<Record<StateKey, ColorSpec>>> = {
|
|
|
30
30
|
error: { bg: '--surface', text: '--foreground', border: '--error', placeholder: '--fg-muted' },
|
|
31
31
|
disabled: { bg: '--bg-disabled', text: '--fg-disabled', border: 'transparent', placeholder: '--fg-disabled' },
|
|
32
32
|
},
|
|
33
|
-
|
|
33
|
+
view: {
|
|
34
34
|
default: { bg: 'transparent', text: '--foreground', border: 'transparent', placeholder: '--fg-muted' },
|
|
35
35
|
},
|
|
36
36
|
readonly: {
|
|
@@ -58,7 +58,7 @@ const SIZE_SPECS: Record<SizeKey, SizeSpec> = {
|
|
|
58
58
|
|
|
59
59
|
const MODE_DESC: Record<ModeKey, string> = {
|
|
60
60
|
edit: '表單可編輯欄位 — bg-surface + border + hover/focus 回饋',
|
|
61
|
-
|
|
61
|
+
view: '純展示值(非表單)— edit 幾何減 chrome(transparent bg/border,**保留 px-field-px 內距與高度**,Model A round16)+ 文字正常色,空值顯示半形 -(hyphen,同 value 前景色)。用在 cell / InlineEdit / 詳情',
|
|
62
62
|
readonly: '不可編輯但可見 — bg-readonly(neutral-2) + 無邊框 + 文字正常色',
|
|
63
63
|
disabled: '被停用的欄位 — bg-disabled(neutral-2) + 無邊框 + 文字灰化',
|
|
64
64
|
}
|
|
@@ -211,9 +211,9 @@ export const Overview = {
|
|
|
211
211
|
{MODES.map((m) => (
|
|
212
212
|
<div key={m} className="flex items-center gap-4">
|
|
213
213
|
<div className="w-64 shrink-0">
|
|
214
|
-
{/*
|
|
214
|
+
{/* view + readonly 讀 controlled value:兩者「空值」皆走 <span> 顯 '-'(input.tsx:121),
|
|
215
215
|
demo 要展示有值狀態故傳 value;edit / disabled 用 defaultValue 保持 uncontrolled */}
|
|
216
|
-
{(m === '
|
|
216
|
+
{(m === 'view' || m === 'readonly') ? (
|
|
217
217
|
<Input mode={m} value="Wireless Bluetooth Headphones" size="md" />
|
|
218
218
|
) : (
|
|
219
219
|
<Input mode={m} defaultValue="Wireless Bluetooth Headphones" size="md" />
|
|
@@ -242,8 +242,8 @@ export const Overview = {
|
|
|
242
242
|
<thead><tr><Th>Prop</Th><Th>Type</Th><Th>Default</Th><Th>說明</Th></tr></thead>
|
|
243
243
|
<tbody>
|
|
244
244
|
{[
|
|
245
|
-
['mode', "'edit' | '
|
|
246
|
-
['variant', "'default'", "'default'", '視覺 chrome(正交於 mode;公開型別僅 default,2026-07-14 收窄);naked =
|
|
245
|
+
['mode', "'edit' | 'view' | 'readonly' | 'disabled'", "'edit'", 'mode 未顯式指定時依序推導:有效 disabled(prop 或 <Field disabled>)→ fieldCtx.mode → native readOnly → edit;顯式 mode prop 永遠最優先(field-context.ts useResolvedFieldMode)'],
|
|
246
|
+
['variant', "'default'", "'default'", '視覺 chrome(正交於 mode;公開型別僅 default,2026-07-14 收窄);naked = cell-as-input substrate(@internal):edit×naked 自畫 border-based state machine(border → hover → focus-within primary → error 紅框),view×naked 才由 host cell 提供視覺邊框;bare 2026-07-09 退役'],
|
|
247
247
|
['error', 'boolean', 'false', '紅色邊框僅 edit 模式生效;aria-invalid 不分 mode(readonly / disabled 渲染的 input 同樣帶)'],
|
|
248
248
|
['size', "'sm' | 'md' | 'lg'", "'md'", '高度與字體,與 Button 共用 field-height token'],
|
|
249
249
|
['startIcon', 'LucideIcon', '—', '左側靜態 icon,fg-muted,pointer-events-none'],
|
|
@@ -334,8 +334,8 @@ const InspectorInner = () => {
|
|
|
334
334
|
error={error}
|
|
335
335
|
startIcon={hasStartIcon ? Search : undefined}
|
|
336
336
|
endAction={hasEndAction && isEdit ? { icon: X, label: '清除', onClick: () => {} } : undefined}
|
|
337
|
-
//
|
|
338
|
-
{...(mode === '
|
|
337
|
+
// view mode 讀 value 渲染 <span>;其餘 mode 用 defaultValue 保持 uncontrolled
|
|
338
|
+
{...(mode === 'view'
|
|
339
339
|
? { value: 'Wireless Bluetooth Headphones' }
|
|
340
340
|
: { defaultValue: 'Wireless Bluetooth Headphones' })}
|
|
341
341
|
key={mode}
|
|
@@ -361,7 +361,7 @@ const InspectorInner = () => {
|
|
|
361
361
|
</div>
|
|
362
362
|
<div className="flex items-center">
|
|
363
363
|
<div className="flex items-center rounded-md overflow-hidden" style={{ height: 52, outline: `2px solid ${Z.dim.text}22` }}>
|
|
364
|
-
<BpZone w={44} color={Z.pad} label={
|
|
364
|
+
<BpZone w={44} color={Z.pad} label={s.pxToken} sub={`${s.px}px`} />
|
|
365
365
|
{hasStartIcon && (
|
|
366
366
|
<>
|
|
367
367
|
<BpZone w={44} color={Z.icon} label={`${s.icon}px`} sub="icon" />
|
|
@@ -375,7 +375,7 @@ const InspectorInner = () => {
|
|
|
375
375
|
<BpZone w={44} color={Z.action} label={`${s.icon}px`} sub="action" />
|
|
376
376
|
</>
|
|
377
377
|
)}
|
|
378
|
-
<BpZone w={44} color={Z.pad} label={
|
|
378
|
+
<BpZone w={44} color={Z.pad} label={s.pxToken} sub={`${s.px}px`} />
|
|
379
379
|
</div>
|
|
380
380
|
<div className="ml-3 flex items-center" style={{ height: 52 }}>
|
|
381
381
|
<svg width="10" height="52" className="shrink-0">
|
|
@@ -426,7 +426,7 @@ const InspectorInner = () => {
|
|
|
426
426
|
<div className="px-4 py-1">
|
|
427
427
|
<div className="py-2 border-b border-divider"><span className="text-[10px] font-semibold text-fg-muted uppercase tracking-wider">Layout</span></div>
|
|
428
428
|
<PropRow label="高度" dot={Z.dim.text}><TkVal token={s.heightToken} value={s.height} /></PropRow>
|
|
429
|
-
<PropRow label="左右內距" dot={Z.pad.text}><TkVal token={
|
|
429
|
+
<PropRow label="左右內距" dot={Z.pad.text}><TkVal token={s.pxToken} value={`${s.px}px(view 保留 = edit 幾何減 chrome)`} /></PropRow>
|
|
430
430
|
<PropRow label="元素間距" dot={Z.gap.text}><TkVal token={s.gapToken} value={`${s.gap}px`} /></PropRow>
|
|
431
431
|
{hasStartIcon && (
|
|
432
432
|
<PropRow label="Icon 尺寸" dot={Z.icon.text}>{s.icon}px</PropRow>
|
|
@@ -452,7 +452,7 @@ const InspectorInner = () => {
|
|
|
452
452
|
<div className="py-2 border-b border-divider"><span className="text-[10px] font-semibold text-fg-muted uppercase tracking-wider">Style</span></div>
|
|
453
453
|
<PropRow label="Radius"><TkVal token="rounded-md" value="4px" /></PropRow>
|
|
454
454
|
<PropRow label="Border"><TkVal token="border" value="1px solid" /></PropRow>
|
|
455
|
-
<PropRow label="Focus"><TkVal token="border-primary" value="1px — 無 ring" /></PropRow>
|
|
455
|
+
<PropRow label="Focus"><TkVal token="border-primary" value="1px — 無 ring(readonly 例外:ring-2 ring-ring,WCAG 2.4.7)" /></PropRow>
|
|
456
456
|
<PropRow label="Transition"><TkVal token="transition-colors" value="150ms" /></PropRow>
|
|
457
457
|
</div>
|
|
458
458
|
</div>
|
|
@@ -675,7 +675,7 @@ export const StateBehavior = {
|
|
|
675
675
|
|
|
676
676
|
{/* Focus behavior */}
|
|
677
677
|
<div className="flex flex-col gap-4">
|
|
678
|
-
<span className="text-caption font-medium text-fg-secondary">Focus — border-primary(1px),無 ring
|
|
678
|
+
<span className="text-caption font-medium text-fg-secondary">Focus — border-primary(1px),無 ring(readonly 例外:ring-2 ring-ring focus 指示,WCAG 2.4.7)</span>
|
|
679
679
|
<div className="flex flex-col gap-2 max-w-sm">
|
|
680
680
|
<span className="text-[11px] text-fg-muted">文字輸入永遠 focus-visible(瀏覽器規範),click 和 Tab 觸發相同效果。點擊下方 input 或用 Tab 切換觀察。</span>
|
|
681
681
|
<Input placeholder="點擊或 Tab 觀察 focus 邊框" />
|