@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
|
@@ -4,7 +4,7 @@ import React from 'react'
|
|
|
4
4
|
import LinkTo from '@storybook/addon-links/react'
|
|
5
5
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
6
6
|
import { Upload } from 'lucide-react'
|
|
7
|
-
import { Field, FieldLabel, FieldDescription,
|
|
7
|
+
import { Field, FieldLabel, FieldDescription, FieldGroup } from './field'
|
|
8
8
|
import { Input } from '@/design-system/components/Input/input'
|
|
9
9
|
import { Select } from '@/design-system/components/Select/select'
|
|
10
10
|
import { Button } from '@/design-system/components/Button/button'
|
|
@@ -141,33 +141,10 @@ export const UsageGuidance: Story = {
|
|
|
141
141
|
),
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
<Rule
|
|
149
|
-
title="Field 的職責 — Layout + Context,不擁有資料邏輯"
|
|
150
|
-
note="Field 只負責:label / control / description / error 的空間關係 + 把 mode / disabled / required / invalid / id 透過 context 傳給子元件。與資料相關的一切(格式化、驗證、readonly 呈現、DataTable cell 顯示)住在 Field Control 元件本身"
|
|
151
|
-
>
|
|
152
|
-
<div className="max-w-sm">
|
|
153
|
-
<FieldGroup>
|
|
154
|
-
<Field required>
|
|
155
|
-
<FieldLabel>電子郵件</FieldLabel>
|
|
156
|
-
<Input type="email" placeholder="name@example.com" />
|
|
157
|
-
<FieldDescription>我們不會公開您的電子郵件</FieldDescription>
|
|
158
|
-
</Field>
|
|
159
|
-
<Field invalid>
|
|
160
|
-
<FieldLabel>密碼</FieldLabel>
|
|
161
|
-
<Input type="password" />
|
|
162
|
-
<FieldError>密碼必須至少 8 個字元</FieldError>
|
|
163
|
-
</Field>
|
|
164
|
-
</FieldGroup>
|
|
165
|
-
</div>
|
|
166
|
-
<Label>↑ Field 管 layout + required 星號 + error 顯示,Input 管 type=email / 驗證邏輯</Label>
|
|
167
|
-
</Rule>
|
|
168
|
-
</div>
|
|
169
|
-
),
|
|
170
|
-
}
|
|
144
|
+
// ResponsibilityRule retired 2026-07-17 per audit Dim 24 —— 與 anatomy Overview「子元件責任邊界」
|
|
145
|
+
// 責任表 + 逐字相同的 電子郵件/密碼 範例重複;職責原則亦已由上方 UsageGuidance「❌ Field 試圖承載
|
|
146
|
+
// 資料型別邏輯」涵蓋(earn-existence 2-test 皆 NO)。UsageGuidance 使 principles universal-core 仍 ≥ 2,
|
|
147
|
+
// 無 LinkTo 引用此 story,退役零斷鏈。
|
|
171
148
|
|
|
172
149
|
export const OrientationRule: Story = {
|
|
173
150
|
name: '方向 選擇',
|
|
@@ -32,7 +32,7 @@ Field 是**表單欄位的佈局容器**。只負責排版(label / control / d
|
|
|
32
32
|
Field 和 Field Controls(Input / NumberInput / DatePicker / Select / Combobox / LinkInput / PeoplePicker / Textarea)是兩件事:
|
|
33
33
|
|
|
34
34
|
- **Field**(本元件):只管佈局 + 狀態 context
|
|
35
|
-
- **Field Controls / Checkbox / Switch / RadioGroup**:管自己的資料型別 + edit/readonly/disabled 三態 + 格式化(`mode="
|
|
35
|
+
- **Field Controls / Checkbox / Switch / RadioGroup**:管自己的資料型別 + edit/readonly/disabled 三態 + 格式化(`mode="view"` 渲染供 DataTable cell 共用)
|
|
36
36
|
|
|
37
37
|
這麼拆的理由:**Checkbox 在 table cell、form field、settings row 應該是同一個 primitive**,不該為了進到 form 就被包一層 CheckboxField;form 的高度對齊由 Field 的 control area 負責,不由 primitive 本身負責。Field Controls 的詳細共用規則見 `Field/field-controls.spec.md`。
|
|
38
38
|
|
|
@@ -206,7 +206,7 @@ Field 透過 Context 暴露以下狀態給子元件(Primitive 可以透過 `us
|
|
|
206
206
|
| `id` | FieldLabel 自動 `htmlFor`、Input 自動 `id` |
|
|
207
207
|
| `descriptionId` | FieldDescription 的 id,Input 自動 `aria-describedby` |
|
|
208
208
|
| `errorId` | FieldError 的 id,Input 自動 `aria-errormessage` |
|
|
209
|
-
| `mode` | `edit` / `
|
|
209
|
+
| `mode` | `edit` / `view` / `readonly` / `disabled`,Control 決定自己的視覺形態 |
|
|
210
210
|
| `disabled` | Control 顯示 disabled 樣式 |
|
|
211
211
|
| `required` | FieldLabel 自動渲染 `*`;Input 自動 `aria-required` |
|
|
212
212
|
| `invalid` | Control 顯示 error 邊框;Input 自動 `aria-invalid` |
|
|
@@ -416,9 +416,9 @@ Field 內的資料輸入控件(Input / NumberInput / DatePicker / Select / Com
|
|
|
416
416
|
| **L1 Slot** | `patterns/element-anatomy/item-anatomy.tsx` | `<ItemPrefix>` / `<ItemSuffix>`(永遠 `h-[1lh]` 對齊第 1 行) |
|
|
417
417
|
| **L2 Align** | `field-wrapper.tsx` `nakedCellRowModeAlign` | host cell `data-row-mode` propagate(autoRow→items-start / fixed→items-center)|
|
|
418
418
|
| **L3 State machine** | **繼承 Field default v13.3**(↑ 上方 SSOT)| 不重定義 |
|
|
419
|
-
| **L4 Display hover** | `field-wrapper.tsx` `nakedCellEditableDisplayHover` | editable cell
|
|
419
|
+
| **L4 Display hover** | `field-wrapper.tsx` `nakedCellEditableDisplayHover` | editable cell view mode `outline-1 outline-offset:-1 outline-[var(--cell-hover-outline-color,var(--border-hover))]`(cell wrapper outline;2026-05-10 Slice D:spreadsheet overlay 啟用時 DataTable cell host 可 set `--cell-hover-outline-color: transparent` 抑制 outline、讓 overlay layer 接管 hover ring paint,fallback 即 `--border-hover` 既有行為不變)。**v15.13 design intent(user-accepted 2026-05-07)**:hover ring 蓋 cell.borderRight。**known asymmetry**(2026-05-09 user 重抓):只 **right** 邊 outline 跟 cell own border-r 同位置覆蓋成 1 條;top/left/bottom 邊 outline 在 cell.outer 內 1px,grid lines(前 row border-b / 前 cell border-r / row 自己 border-b 在 cell.outer 外 1px)露出 → 視覺 4 邊不對稱。**Pending design decision**(等 user 拍板才 ship):4 邊都蓋(spec 1 selection-style)vs 4 邊都不蓋(spec 2 inset 1px)vs 維持現狀。三層 SSOT:state source = cell wrapper / paint owner = 混合(hover=cell / edit=Field)/ grid divider = DataTable own |
|
|
420
420
|
| **L5 Position(v14,2026-05-06 revert v12)** | naked compoundVariant flow | Field 留 layout flow,**不**用 `!absolute`(v12 absolute 跟 autoRowHeight 不相容)。視覺接受 cell `border-r grid` + Field border 2px 雙線;cell border-r divider 永遠保留(對齊 L4 Invariant divider-owner / editor-owner 分離)|
|
|
421
|
-
| **L6 Cell trigger indicator(v16,2026-07-08 user 拍板 A 案)** | naked-
|
|
421
|
+
| **L6 Cell trigger indicator(v16,2026-07-08 user 拍板 A 案)** | naked-view branch + `showDisplayEndIcon?: boolean` opt-in(**default off,production 不再傳**)| **view 態零恆顯型別 icon** — editable affordance 統一 = L4 hover outline。Benchmark 6/6(Ant editable-cells 純文字+hover border / MUI X singleSelect view 純文字 https://mui.com/x/react-data-grid/editing/ / AG Grid 無內建 indicator https://www.ag-grid.com/react-data-grid/cell-editing/ / Atlaskit v2.0.0 移除 hover-pencil 後 9 版未回歸 https://github.com/pioug/atlassian-frontend-mirror/tree/main/design-system/inline-edit / Notion / Airtable click-to-edit)view 態皆無恆顯 icon;唯一反例 Google Sheets Chip/Arrow 為 spreadsheet 域可選檔位。prop 保留為 spreadsheet-flavored 消費端 opt-in 逃生門:傳 true → `<ItemSuffix>` 渲型別 icon(Select/Combobox/PeoplePicker → ChevronDown / DatePicker → Calendar / TimePicker → Clock / **LinkInput 例外 = wrapper-only 無 icon**)。cell-registry 現況:6 picker 站不傳;url 站傳 `isEditable === true`(取 wrapper chrome 對齊 edit Input,非 icon)。edit 態 chevron 照舊(edit-trigger 通道,不受此 prop 管)。**值內容 vs affordance 分層(2026-07-08 user Q2 拍板)**:值內容(`selectedItemRenderer` 輸出 / avatar / type icon 等語意呈現)歸 value renderer,view 態照常渲染,恆顯**不**違反本條;本條管的是 affordance(chevron / pencil / outline)。前身 v15 恆顯制(2026-05-10/06-26 拍板)由本輪 benchmark 重判推翻。詳 `.claude/planning/cell-indicator-ssot-rfc.md`(六家實查 SSOT)。|
|
|
422
422
|
|
|
423
423
|
**前身 retire**:v4-v8 `outline-2` 平行 state(已下沉 L3)/ v9 border-only(focus 跟 open specificity tie)/ per-control `open && 'border-primary'`(v13.3 retire)/ `nakedCell{Hover,Focus,Open}Ring` 三 const / `nakedCell{Prefix,Suffix}Slot`(下沉 L1)/ **v12 `!absolute -inset-px` seamless grid**(2026-05-06 revert — autoRowHeight 不相容)/ **DataTable parallel `getEditIndicator`**(2026-05-09 D-path retire — 由 Field naked-display 透過 `showDisplayEndIcon` opt-in 自渲 ItemSuffix,跨元件 SSOT 統一)。
|
|
424
424
|
|
|
@@ -91,21 +91,21 @@ export const StateCascade: Story = {
|
|
|
91
91
|
<Field mode="readonly" className="w-44"><FieldLabel>負責人</FieldLabel><Input value="王小明" onChange={() => {}} /></Field>
|
|
92
92
|
<Field mode="readonly" className="w-44"><FieldLabel>同意條款</FieldLabel><Checkbox checked /></Field>
|
|
93
93
|
<Field mode="readonly" className="w-44"><FieldLabel>已啟用</FieldLabel><Switch checked /></Field>
|
|
94
|
-
<Field mode="readonly" className="w-44"><FieldLabel
|
|
94
|
+
<Field mode="readonly" className="w-44"><FieldLabel>行銷電子報</FieldLabel><Checkbox /></Field>
|
|
95
95
|
<Field mode="readonly" className="w-44"><FieldLabel>付款方式</FieldLabel><RadioGroup value="card"><RadioGroupItem value="card" label="信用卡" /><RadioGroupItem value="cash" label="貨到付款" /></RadioGroup></Field>
|
|
96
96
|
<Field mode="readonly" className="w-44"><FieldLabel>滿意度</FieldLabel><Rating value={4} aria-label="滿意度" /></Field>
|
|
97
97
|
<Field mode="readonly" className="w-44"><FieldLabel>完成度</FieldLabel><Slider defaultValue={[40]} aria-label="完成度" /></Field>
|
|
98
98
|
</div>
|
|
99
99
|
</div>
|
|
100
100
|
<div>
|
|
101
|
-
<p className="text-body-sm font-medium text-fg-secondary mb-3">{'<Field mode="
|
|
101
|
+
<p className="text-body-sm font-medium text-fg-secondary mb-3">{'<Field mode="view"> — 有展示態的控件自動切純展示(Select / DatePicker / Checkbox / Switch 修復後生效)'}</p>
|
|
102
102
|
<div className="flex flex-wrap gap-x-8 gap-y-4 max-w-3xl">
|
|
103
|
-
<Field mode="
|
|
104
|
-
<Field mode="
|
|
105
|
-
<Field mode="
|
|
106
|
-
<Field mode="
|
|
107
|
-
<Field mode="
|
|
108
|
-
<Field mode="
|
|
103
|
+
<Field mode="view" className="w-44"><FieldLabel>負責人</FieldLabel><Input value="王小明" onChange={() => {}} /></Field>
|
|
104
|
+
<Field mode="view" className="w-44"><FieldLabel>優先級</FieldLabel><Select options={CASCADE_PRIORITY} value="high" aria-label="優先級" /></Field>
|
|
105
|
+
<Field mode="view" className="w-44"><FieldLabel>截止日</FieldLabel><DatePicker value="2026-06-20" onChange={() => {}} /></Field>
|
|
106
|
+
<Field mode="view" className="w-44"><FieldLabel>數量</FieldLabel><NumberInput value={42} onChange={() => {}} /></Field>
|
|
107
|
+
<Field mode="view" className="w-44"><FieldLabel>已啟用</FieldLabel><Switch checked /></Field>
|
|
108
|
+
<Field mode="view" className="w-44"><FieldLabel>同意條款</FieldLabel><Checkbox checked /></Field>
|
|
109
109
|
</div>
|
|
110
110
|
</div>
|
|
111
111
|
</div>
|
|
@@ -143,7 +143,7 @@ export const Horizontal: Story = {
|
|
|
143
143
|
// ── Horizontal 垂直對齊公式驗證 ─────────────────────────────────────────
|
|
144
144
|
|
|
145
145
|
export const HorizontalLabelAlignment: Story = {
|
|
146
|
-
name: '
|
|
146
|
+
name: '水平:標籤與控件垂直對齊',
|
|
147
147
|
render: () => (
|
|
148
148
|
<div className="max-w-3xl flex flex-col gap-8">
|
|
149
149
|
<div>
|
|
@@ -153,8 +153,8 @@ export const HorizontalLabelAlignment: Story = {
|
|
|
153
153
|
這是 padding-top: calc((field-height - 1lh) / 2) 的基本情境。
|
|
154
154
|
</p>
|
|
155
155
|
<Field orientation="horizontal" labelWidth="160px">
|
|
156
|
-
<FieldLabel
|
|
157
|
-
<Input placeholder="
|
|
156
|
+
<FieldLabel>職稱</FieldLabel>
|
|
157
|
+
<Input placeholder="如:資深產品設計師" />
|
|
158
158
|
</Field>
|
|
159
159
|
</div>
|
|
160
160
|
|
|
@@ -165,8 +165,8 @@ export const HorizontalLabelAlignment: Story = {
|
|
|
165
165
|
視覺上 label 從 input 中線往下延伸,而不是從 input 頂部開始。
|
|
166
166
|
</p>
|
|
167
167
|
<Field orientation="horizontal" labelWidth="160px">
|
|
168
|
-
<FieldLabel
|
|
169
|
-
<Input placeholder="
|
|
168
|
+
<FieldLabel>緊急聯絡人手機(發生狀況時優先撥打)</FieldLabel>
|
|
169
|
+
<Input placeholder="如:0912-345-678" />
|
|
170
170
|
</Field>
|
|
171
171
|
</div>
|
|
172
172
|
|
|
@@ -177,18 +177,27 @@ export const HorizontalLabelAlignment: Story = {
|
|
|
177
177
|
無需 JS 測量。
|
|
178
178
|
</p>
|
|
179
179
|
<div className="flex flex-col gap-3">
|
|
180
|
-
<
|
|
181
|
-
<
|
|
182
|
-
<
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
<
|
|
190
|
-
|
|
191
|
-
|
|
180
|
+
<div>
|
|
181
|
+
<div className="text-caption text-fg-muted mb-1">小尺寸 sm — 控件高 28px</div>
|
|
182
|
+
<Field orientation="horizontal" labelWidth="100px" size="sm">
|
|
183
|
+
<FieldLabel>電子郵件</FieldLabel>
|
|
184
|
+
<Input size="sm" placeholder="name@example.com" />
|
|
185
|
+
</Field>
|
|
186
|
+
</div>
|
|
187
|
+
<div>
|
|
188
|
+
<div className="text-caption text-fg-muted mb-1">中尺寸 md — 控件高 32px</div>
|
|
189
|
+
<Field orientation="horizontal" labelWidth="100px" size="md">
|
|
190
|
+
<FieldLabel>電子郵件</FieldLabel>
|
|
191
|
+
<Input size="md" placeholder="name@example.com" />
|
|
192
|
+
</Field>
|
|
193
|
+
</div>
|
|
194
|
+
<div>
|
|
195
|
+
<div className="text-caption text-fg-muted mb-1">大尺寸 lg — 控件高 36px</div>
|
|
196
|
+
<Field orientation="horizontal" labelWidth="100px" size="lg">
|
|
197
|
+
<FieldLabel>電子郵件</FieldLabel>
|
|
198
|
+
<Input size="lg" placeholder="name@example.com" />
|
|
199
|
+
</Field>
|
|
200
|
+
</div>
|
|
192
201
|
</div>
|
|
193
202
|
</div>
|
|
194
203
|
</div>
|
|
@@ -279,31 +288,40 @@ export const SegmentedControlInField: Story = {
|
|
|
279
288
|
整個欄位改尺寸時,分段控制器也會跟著一起縮放。
|
|
280
289
|
</p>
|
|
281
290
|
<FieldGroup>
|
|
282
|
-
<
|
|
283
|
-
<
|
|
284
|
-
<
|
|
285
|
-
<
|
|
286
|
-
<
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
291
|
+
<div>
|
|
292
|
+
<div className="text-caption text-fg-muted mb-1">小尺寸 sm</div>
|
|
293
|
+
<Field size="sm">
|
|
294
|
+
<FieldLabel>檢視模式</FieldLabel>
|
|
295
|
+
<SegmentedControl defaultValue="list">
|
|
296
|
+
<SegmentedControlItem value="list">清單</SegmentedControlItem>
|
|
297
|
+
<SegmentedControlItem value="board">看板</SegmentedControlItem>
|
|
298
|
+
<SegmentedControlItem value="calendar">行事曆</SegmentedControlItem>
|
|
299
|
+
</SegmentedControl>
|
|
300
|
+
<FieldDescription>切換內容呈現方式</FieldDescription>
|
|
301
|
+
</Field>
|
|
302
|
+
</div>
|
|
303
|
+
<div>
|
|
304
|
+
<div className="text-caption text-fg-muted mb-1">中尺寸 md</div>
|
|
305
|
+
<Field size="md">
|
|
306
|
+
<FieldLabel>檢視模式</FieldLabel>
|
|
307
|
+
<SegmentedControl defaultValue="list">
|
|
308
|
+
<SegmentedControlItem value="list">清單</SegmentedControlItem>
|
|
309
|
+
<SegmentedControlItem value="board">看板</SegmentedControlItem>
|
|
310
|
+
<SegmentedControlItem value="calendar">行事曆</SegmentedControlItem>
|
|
311
|
+
</SegmentedControl>
|
|
312
|
+
</Field>
|
|
313
|
+
</div>
|
|
314
|
+
<div>
|
|
315
|
+
<div className="text-caption text-fg-muted mb-1">大尺寸 lg</div>
|
|
316
|
+
<Field size="lg">
|
|
317
|
+
<FieldLabel>檢視模式</FieldLabel>
|
|
318
|
+
<SegmentedControl defaultValue="list">
|
|
319
|
+
<SegmentedControlItem value="list">清單</SegmentedControlItem>
|
|
320
|
+
<SegmentedControlItem value="board">看板</SegmentedControlItem>
|
|
321
|
+
<SegmentedControlItem value="calendar">行事曆</SegmentedControlItem>
|
|
322
|
+
</SegmentedControl>
|
|
323
|
+
</Field>
|
|
324
|
+
</div>
|
|
307
325
|
</FieldGroup>
|
|
308
326
|
</div>
|
|
309
327
|
|
|
@@ -478,23 +496,35 @@ export const LabelWidth: Story = {
|
|
|
478
496
|
name: '標籤寬度',
|
|
479
497
|
render: () => (
|
|
480
498
|
<div className="max-w-2xl flex flex-col gap-4">
|
|
481
|
-
<p className="text-caption text-fg-muted">labelWidth 支援任何 CSS length
|
|
482
|
-
<
|
|
483
|
-
<
|
|
484
|
-
<
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
<
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
499
|
+
<p className="text-caption text-fg-muted">labelWidth 支援任何 CSS length 值(80px / 160px / 240px / 30% 等);同一表單依最長的 label 挑一個固定寬,讓控件起點對齊。</p>
|
|
500
|
+
<div>
|
|
501
|
+
<div className="text-caption text-fg-muted mb-1">labelWidth = 80px</div>
|
|
502
|
+
<Field orientation="horizontal" labelWidth="80px">
|
|
503
|
+
<FieldLabel>城市</FieldLabel>
|
|
504
|
+
<Input placeholder="如:台北市" />
|
|
505
|
+
</Field>
|
|
506
|
+
</div>
|
|
507
|
+
<div>
|
|
508
|
+
<div className="text-caption text-fg-muted mb-1">labelWidth = 160px(預設常用)</div>
|
|
509
|
+
<Field orientation="horizontal" labelWidth="160px">
|
|
510
|
+
<FieldLabel>公司統一編號</FieldLabel>
|
|
511
|
+
<Input placeholder="8 碼數字" />
|
|
512
|
+
</Field>
|
|
513
|
+
</div>
|
|
514
|
+
<div>
|
|
515
|
+
<div className="text-caption text-fg-muted mb-1">labelWidth = 240px(寬 label 欄)</div>
|
|
516
|
+
<Field orientation="horizontal" labelWidth="240px">
|
|
517
|
+
<FieldLabel>發票寄送電子郵件地址</FieldLabel>
|
|
518
|
+
<Input placeholder="billing@company.com" />
|
|
519
|
+
</Field>
|
|
520
|
+
</div>
|
|
521
|
+
<div>
|
|
522
|
+
<div className="text-caption text-fg-muted mb-1">labelWidth = 30%(比例)</div>
|
|
523
|
+
<Field orientation="horizontal" labelWidth="30%">
|
|
524
|
+
<FieldLabel>備註</FieldLabel>
|
|
525
|
+
<Input placeholder="選填" />
|
|
526
|
+
</Field>
|
|
527
|
+
</div>
|
|
498
528
|
</div>
|
|
499
529
|
),
|
|
500
530
|
}
|
|
@@ -531,9 +561,9 @@ export const WithSlider: Story = {
|
|
|
531
561
|
</Field>
|
|
532
562
|
|
|
533
563
|
<Field>
|
|
534
|
-
<FieldLabel
|
|
564
|
+
<FieldLabel>儲存空間配額</FieldLabel>
|
|
535
565
|
<Slider defaultValue={[40]} disabled />
|
|
536
|
-
<FieldDescription
|
|
566
|
+
<FieldDescription>由系統管理員設定,無法自行調整</FieldDescription>
|
|
537
567
|
</Field>
|
|
538
568
|
</FieldGroup>
|
|
539
569
|
</div>
|
|
@@ -602,9 +632,9 @@ export const SliderWithLiveNumberInput: Story = {
|
|
|
602
632
|
max={200}
|
|
603
633
|
/>
|
|
604
634
|
</div>
|
|
635
|
+
{/* Slider 與 NumberInput 透過 `h-field-md` 對齊高度——實作細節,不進使用者可見文案 */}
|
|
605
636
|
<FieldDescription>
|
|
606
|
-
|
|
607
|
-
slider。兩個元件高度透過 `h-field-md` 對齊。
|
|
637
|
+
拖曳滑桿或直接輸入數字都能即時調整縮放比例,範圍 0–200%。
|
|
608
638
|
</FieldDescription>
|
|
609
639
|
</Field>
|
|
610
640
|
</div>
|
|
@@ -640,7 +670,7 @@ function UpdateProjectSettingsForm() {
|
|
|
640
670
|
<Field required invalid={!!form.errors.name}>
|
|
641
671
|
<FieldLabel>專案名稱</FieldLabel>
|
|
642
672
|
<Input {...form.getInputProps('name')} />
|
|
643
|
-
<FieldDescription
|
|
673
|
+
<FieldDescription>專案名稱不得與其他專案重複</FieldDescription>
|
|
644
674
|
<FieldError>{form.errors.name}</FieldError>
|
|
645
675
|
</Field>
|
|
646
676
|
<Field required invalid={!!form.errors.ownerEmail}>
|
|
@@ -700,6 +730,7 @@ export const FormValidation: Story = {
|
|
|
700
730
|
<p className="text-caption text-fg-muted max-w-80">
|
|
701
731
|
按鈕沒改不亮;打字中不報錯(blur 才驗);已出錯欄位一編輯立即清 error;
|
|
702
732
|
Escape 回復原值;空 submit / 格式錯 → anchor 到第一個錯誤欄位。
|
|
733
|
+
試著把名稱改成「產品路線圖 Q3」再儲存,會觸發「名稱重複」業務驗證。
|
|
703
734
|
</p>
|
|
704
735
|
<UpdateProjectSettingsForm />
|
|
705
736
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// code-quality-allow: file-size — foundational composite(Field + FieldLabel + FieldDescription + FieldError + context + 8 layout variants),拆檔會讓 Field 家族互相 import 循環
|
|
2
2
|
import * as React from 'react'
|
|
3
|
+
import { cva } from 'class-variance-authority'
|
|
3
4
|
import { Info as InfoIcon } from 'lucide-react'
|
|
4
5
|
import { cn } from '@/lib/utils'
|
|
5
6
|
import { Tooltip, TooltipTrigger, TooltipContent } from '@/design-system/components/Tooltip/tooltip'
|
|
@@ -102,6 +103,20 @@ function detectControlLayout(controlNodes: React.ReactNode[]): FieldControlLayou
|
|
|
102
103
|
return 'inline'
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
/**
|
|
107
|
+
* 偵測 control child 的偏好尺寸——control primitive 在自己的檔案掛 static `fieldPreferredSize`
|
|
108
|
+
* (如 InlineEdit = 'sm',view 態無邊框,md 過大會讓版面鬆散),Field 未顯式指定 size 時收此偏好。
|
|
109
|
+
* 對齊 detectControlLayout 讀 `type.fieldLayout` 的機制(first-declared-wins);全部缺宣告 → null。
|
|
110
|
+
*/
|
|
111
|
+
function detectPreferredSize(controlNodes: React.ReactNode[]): FieldSize | null {
|
|
112
|
+
for (const node of controlNodes) {
|
|
113
|
+
if (!React.isValidElement(node)) continue
|
|
114
|
+
const pref = (node.type as { fieldPreferredSize?: FieldSize } | null | undefined)?.fieldPreferredSize
|
|
115
|
+
if (pref) return pref
|
|
116
|
+
}
|
|
117
|
+
return null
|
|
118
|
+
}
|
|
119
|
+
|
|
105
120
|
// ── Field ───────────────────────────────────────────────────────────────────
|
|
106
121
|
|
|
107
122
|
export interface FieldProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'id'> {
|
|
@@ -111,7 +126,7 @@ export interface FieldProps extends Omit<React.HTMLAttributes<HTMLDivElement>, '
|
|
|
111
126
|
* 視覺外殼(2026-05-05)。
|
|
112
127
|
* - `default`(預設)— 含 border + bg(一般 form input)
|
|
113
128
|
* - `naked` — cell-as-input(@internal;不在公開 FieldVariant 型別 — 2026-07-14 收窄至 FieldVariantInternal;2026-07-09 `bare` 退役)。edit×naked 自畫 border-based state
|
|
114
|
-
* machine(rest/hover/focus/error);
|
|
129
|
+
* machine(rest/hover/focus/error);view×naked 用 transparent border,host cell 提供視覺邊框
|
|
115
130
|
*
|
|
116
131
|
* 透傳機制:Field 一次宣告,所有 child Field control 自動繼承(per-control prop override 可覆寫)。
|
|
117
132
|
*/
|
|
@@ -155,7 +170,7 @@ const Field = React.forwardRef<HTMLDivElement, FieldProps>(
|
|
|
155
170
|
mode = 'edit',
|
|
156
171
|
variant = 'default',
|
|
157
172
|
orientation = 'vertical',
|
|
158
|
-
size
|
|
173
|
+
size: sizeProp,
|
|
159
174
|
required = false,
|
|
160
175
|
disabled: disabledProp = false,
|
|
161
176
|
invalid = false,
|
|
@@ -199,6 +214,10 @@ const Field = React.forwardRef<HTMLDivElement, FieldProps>(
|
|
|
199
214
|
const controlLayout: FieldControlLayout =
|
|
200
215
|
controlLayoutProp ?? detectControlLayout(controlNodes)
|
|
201
216
|
|
|
217
|
+
// 解析 size:consumer 顯式 > control primitive 偏好(如 InlineEdit='sm')> 預設 md
|
|
218
|
+
// (對齊 controlLayout 三層優先序;InlineEdit 進 Field 不指定 size 時自動收 sm)
|
|
219
|
+
const size: FieldSize = sizeProp ?? detectPreferredSize(controlNodes) ?? 'md'
|
|
220
|
+
|
|
202
221
|
const contextValue = React.useMemo<FieldContextValue>(
|
|
203
222
|
() => ({
|
|
204
223
|
id,
|
|
@@ -496,9 +515,22 @@ export interface FieldGroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
496
515
|
horizontalLabelWidth?: string
|
|
497
516
|
}
|
|
498
517
|
|
|
518
|
+
// gap variant 收進 cva(className-only 差異 → cva,per ui-development.md「cva 適用」);值仍走 Tailwind gap utility。
|
|
519
|
+
const fieldGroupVariants = cva('flex flex-col min-w-0', {
|
|
520
|
+
variants: {
|
|
521
|
+
gap: {
|
|
522
|
+
compact: 'gap-3',
|
|
523
|
+
normal: 'gap-4',
|
|
524
|
+
loose: 'gap-6',
|
|
525
|
+
},
|
|
526
|
+
},
|
|
527
|
+
defaultVariants: {
|
|
528
|
+
gap: 'normal',
|
|
529
|
+
},
|
|
530
|
+
})
|
|
531
|
+
|
|
499
532
|
const FieldGroup = React.forwardRef<HTMLDivElement, FieldGroupProps>(
|
|
500
533
|
({ className, gap = 'normal', horizontalLabelWidth, ...props }, ref) => {
|
|
501
|
-
const gapClass = gap === 'compact' ? 'gap-3' : gap === 'loose' ? 'gap-6' : 'gap-4'
|
|
502
534
|
const groupCtxValue = React.useMemo(
|
|
503
535
|
() => ({ horizontalLabelWidth }),
|
|
504
536
|
[horizontalLabelWidth],
|
|
@@ -507,7 +539,7 @@ const FieldGroup = React.forwardRef<HTMLDivElement, FieldGroupProps>(
|
|
|
507
539
|
<FieldGroupContext.Provider value={groupCtxValue}>
|
|
508
540
|
<div
|
|
509
541
|
ref={ref}
|
|
510
|
-
className={cn(
|
|
542
|
+
className={cn(fieldGroupVariants({ gap }), className)}
|
|
511
543
|
data-field-group=""
|
|
512
544
|
{...props}
|
|
513
545
|
/>
|
|
@@ -156,6 +156,8 @@ Form validation 的 ARIA / 鍵盤行為(對齊 WCAG 3.3.1 Error Identification +
|
|
|
156
156
|
- **Required indicator**:label 的 `*` 為純視覺、對讀屏隱藏(`aria-hidden="true"`,field.tsx:395);required 語意由內部輸入控件的 `aria-required`(input.tsx:188)承擔,避免讀屏讀出「asterisk」語義不清
|
|
157
157
|
- **Color-only error 警告**:error border 紅色之外必有文字訊息(WCAG 1.4.1 不僅靠顏色)— 由 `<FieldError>` 文字承擔;DS **不**在 input 內放 error 狀態 icon(見 `field-controls.spec.md`「禁止事項」)
|
|
158
158
|
|
|
159
|
+
**空值 / Loading(明文 N/A)**:本 spec 是跨 Field 控件的驗證時機原則(非 UI 元件)——空值渲染與 loading 狀態分屬各控件 spec(SSOT → `field-controls.spec.md`「null / undefined 值」+「Loading state」段)。
|
|
160
|
+
|
|
159
161
|
## 相關
|
|
160
162
|
|
|
161
163
|
- `field.spec.md` — Field wrapper 的 error 視覺 chrome(紅框 + error message slot)
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* errors store / resetField;驗證「時機」由本 hook own(RHF 的 mode/reValidateMode 不外露)。
|
|
13
13
|
*
|
|
14
14
|
* ── 與 Field 家族的關係(engine-agnostic 分層,field.spec.md「定位」段)──
|
|
15
|
-
* Field 保持純 layout + context(MUI FormControl 派,可用於 cell /
|
|
15
|
+
* Field 保持純 layout + context(MUI FormControl 派,可用於 cell / view / 無引擎場景);
|
|
16
16
|
* 本 hook 住 form 層,錯誤經 consumer 一行 `<Field invalid={!!form.errors.x}>` 接入——
|
|
17
17
|
* Field 層零耦合。這是「A 派的自由 + B 派的 DX」混合位置。
|
|
18
18
|
*
|
|
@@ -91,13 +91,13 @@ export const UsageGuidance: Story = {
|
|
|
91
91
|
<ul className="text-body text-fg-secondary list-disc pl-5 space-y-1">
|
|
92
92
|
<li>Children 同 size — 不混用 sm/md/lg(視覺高度需一致)</li>
|
|
93
93
|
<li>Children 自管 width — fixed selects + flex input 是典型 pattern</li>
|
|
94
|
-
<li
|
|
94
|
+
<li>繼承 Field 尺寸(包進 Field):size 從 Field context 自動繼承,children 不傳 size</li>
|
|
95
95
|
<li>Children scope:Input / NumberInput / Select / Combobox / DatePicker / Button(對齊 Ant 限定 list)</li>
|
|
96
96
|
</ul>
|
|
97
97
|
</section>
|
|
98
98
|
|
|
99
99
|
<section>
|
|
100
|
-
<h2 className="text-body-lg font-bold mb-3"
|
|
100
|
+
<h2 className="text-body-lg font-bold mb-3">繼承 Field 尺寸 — 包進 Field 當 control slot</h2>
|
|
101
101
|
<Field>
|
|
102
102
|
<FieldLabel>聯絡電話</FieldLabel>
|
|
103
103
|
<FieldControlGroup block>
|
|
@@ -109,7 +109,7 @@ export const UsageGuidance: Story = {
|
|
|
109
109
|
</section>
|
|
110
110
|
|
|
111
111
|
<section>
|
|
112
|
-
<h2 className="text-body-lg font-bold mb-3">
|
|
112
|
+
<h2 className="text-body-lg font-bold mb-3">Standalone 逐一設尺寸 — Filter row 場景</h2>
|
|
113
113
|
<FieldControlGroup block>
|
|
114
114
|
<Select className="!w-[140px]" options={FIELDS} value="name" onChange={() => {}} aria-label="篩選欄位" />
|
|
115
115
|
<Select className="!w-[120px]" options={OPS} value="contains" onChange={() => {}} aria-label="篩選運算子" />
|
|
@@ -89,7 +89,7 @@ interface FieldControlGroupProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
89
89
|
|
|
90
90
|
**FCG `size` prop 目前為 no-op**:本元件只負責 border-collapse 接合(border / radius / z-index),不傳遞 size 給 children(無 Context Provider / 無 cloneElement)。Children 的尺寸完全由 child 自己決定 — 以下三種來源:
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
**繼承 Field 尺寸**(包進 Field 當 control slot):children 各自透過 `useFieldContext().size` 讀外層 `<Field>` 的 size(是 Field 的 context,不是 FCG 的)。
|
|
93
93
|
```tsx
|
|
94
94
|
<Field size="lg">
|
|
95
95
|
<FieldLabel>電話</FieldLabel>
|
|
@@ -100,7 +100,7 @@ interface FieldControlGroupProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
100
100
|
</Field>
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
**
|
|
103
|
+
**Standalone 逐一設尺寸**:**逐一給每個 child 設 `size`**(設 `<FieldControlGroup size>` 無效)。
|
|
104
104
|
```tsx
|
|
105
105
|
<FieldControlGroup>
|
|
106
106
|
<Select size="md" className="!w-[120px]" options={fields} />
|
|
@@ -108,7 +108,7 @@ interface FieldControlGroupProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
108
108
|
</FieldControlGroup>
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
-
**
|
|
111
|
+
**Popover 鎖 md**(進 Popover,density 鎖 md):children 自然吃 md(Popover density canonical)。
|
|
112
112
|
|
|
113
113
|
## A11y 預設
|
|
114
114
|
|
|
@@ -5,7 +5,7 @@ import { cn } from '@/lib/utils'
|
|
|
5
5
|
import { Avatar } from '@/design-system/components/Avatar/avatar'
|
|
6
6
|
import { Button } from '@/design-system/components/Button/button'
|
|
7
7
|
import { ProgressBar } from '@/design-system/components/ProgressBar/progress-bar'
|
|
8
|
-
import { ItemContent, ItemPrefix } from '@/design-system/patterns/element-anatomy/item-anatomy'
|
|
8
|
+
import { ItemContent, ItemPrefix, ItemSuffix } from '@/design-system/patterns/element-anatomy/item-anatomy'
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* FileItem — 檔案顯示 / 上傳進度
|
|
@@ -134,9 +134,8 @@ const FileItem = React.forwardRef<HTMLDivElement, FileItemProps>(
|
|
|
134
134
|
) : null
|
|
135
135
|
|
|
136
136
|
// suffix 對齊 label 第一行(item-anatomy「24px 閾值對齊規則」小 suffix canonical):
|
|
137
|
-
// icons 16 ≤ 24 屬小 suffix,統一
|
|
138
|
-
//
|
|
139
|
-
const suffixAlign = 'h-[1lh]'
|
|
137
|
+
// icons 16 ≤ 24 屬小 suffix,統一 h-[1lh] inline(由 ItemSuffix primitive 提供),
|
|
138
|
+
// 不因 desc wrap 改公式。兩 mode 同公式,跟 item-anatomy 一致。
|
|
140
139
|
|
|
141
140
|
// Status slot 幾何(2026-04-23 user 統一):rich + compact 都用 `var(--field-height-xs)`(24)
|
|
142
141
|
// 容器,裡面 Button xs iconOnly variant="text"(auto data-unbounded)。
|
|
@@ -181,11 +180,14 @@ const FileItem = React.forwardRef<HTMLDivElement, FileItemProps>(
|
|
|
181
180
|
</span>
|
|
182
181
|
) : null
|
|
183
182
|
|
|
183
|
+
// 2026-07-16 dim 39/DA3:消費 ItemSuffix primitive(照 Notice 2026-06-15 已遷模式)。
|
|
184
|
+
// ItemSuffix base geometry(h-[1lh] shrink-0 ml-auto flex items-center gap-2)= 原手刻
|
|
185
|
+
// div 的 flex items-center gap-2 shrink-0 h-[1lh];多出的 ml-auto 為 no-op(左鄰
|
|
186
|
+
// ItemContent 是 flex-1,已把 suffix 推到右緣)→ 像素相等。hoverReveal 預設 false,
|
|
187
|
+
// 不干擾本檔自有的 statusSlot hover-swap 機制(group-hover/row 系)。
|
|
184
188
|
const suffix = (
|
|
185
|
-
<
|
|
189
|
+
<ItemSuffix
|
|
186
190
|
className={cn(
|
|
187
|
-
'flex items-center gap-2 shrink-0',
|
|
188
|
-
suffixAlign,
|
|
189
191
|
// data-unbounded chrome-canonical trick:let Button xs (24) live inside h-[1lh]
|
|
190
192
|
// wrapper(compact ~18.2 / rich ~18.2 scanning)without pushing row height。
|
|
191
193
|
// 視覺/hit area 仍 24,layout footprint 收斂到 1lh。同 overlay-surface
|
|
@@ -201,7 +203,7 @@ const FileItem = React.forwardRef<HTMLDivElement, FileItemProps>(
|
|
|
201
203
|
)}
|
|
202
204
|
{statusSlot}
|
|
203
205
|
{actions}
|
|
204
|
-
</
|
|
206
|
+
</ItemSuffix>
|
|
205
207
|
)
|
|
206
208
|
|
|
207
209
|
// content row — 消費 ItemContent primitive(封裝 label + desc + mt-gap token SSOT)。
|
|
@@ -166,10 +166,10 @@ export const MultipleRule: Story = {
|
|
|
166
166
|
note="每個欄位只收一份檔案時用預設 multiple=false。世界級對照:LinkedIn 履歷上傳、Instagram 大頭貼、Notion page cover——使用者心智模型是「這個位置放一個檔案」。"
|
|
167
167
|
>
|
|
168
168
|
<FileUpload
|
|
169
|
-
accept="
|
|
170
|
-
maxSize={
|
|
171
|
-
title="
|
|
172
|
-
description="
|
|
169
|
+
accept="image/png,image/jpeg"
|
|
170
|
+
maxSize={2_000_000}
|
|
171
|
+
title="上傳大頭貼"
|
|
172
|
+
description="PNG / JPG,最大 2 MB"
|
|
173
173
|
onUpload={noop}
|
|
174
174
|
/>
|
|
175
175
|
<Label>↑ 多拖幾個只取第一個,符合 Ant Upload.Dragger / Polaris DropZone 慣例</Label>
|
|
@@ -212,7 +212,7 @@ export const DivisionOfLaborRule: Story = {
|
|
|
212
212
|
<div>
|
|
213
213
|
<Rule
|
|
214
214
|
title="✅ FileUpload 觸發 + FileItem 顯示"
|
|
215
|
-
note="FileUpload
|
|
215
|
+
note="本範例走 consumer 自組清單:FileUpload 負責拖放偵測 + 觸發選檔,已上傳清單由 consumer 用 FileItem map 渲染。DS 同時支援第二條路——把清單交給 FileUpload 的 files prop 內建渲染(見下一則規則);兩條路都合法,差別只在觸發與清單狀態由誰持有,重點是都不重畫 FileItem 內部視覺。這正是 Notion / Slack / Figma 的附件 flow 做法。"
|
|
216
216
|
>
|
|
217
217
|
<FileUpload
|
|
218
218
|
multiple
|
|
@@ -253,9 +253,9 @@ export const DivisionOfLaborRule: Story = {
|
|
|
253
253
|
|
|
254
254
|
<Rule
|
|
255
255
|
title="❌ 不在 FileUpload 裡自己重刻 FileItem primitives"
|
|
256
|
-
note="
|
|
256
|
+
note="DS 提供兩條清單管理路徑:一是把清單交給 FileUpload 的 files prop,由它內部組合 FileItem 渲染;二是 consumer 自己用 files.map 逐項渲染 FileItem。兩條路都合法,真正禁止的是「自己重畫 FileItem 的內部視覺規格」——狀態圖示、縮圖比例、進度條、列排版都屬 FileItem 的 SSOT。世界級(Material / Ant / Carbon)也都是自己擁有清單、組合自家的 FileItem primitive,不另刻一套。"
|
|
257
257
|
>
|
|
258
|
-
<Label warn>⚠️ 要顯示清單 → 傳 `files` prop(
|
|
258
|
+
<Label warn>⚠️ 要顯示清單 → 傳 `files` prop(內建)或自組 FileItem map;但不要自己重畫 FileItem 內部結構</Label>
|
|
259
259
|
</Rule>
|
|
260
260
|
</div>
|
|
261
261
|
)
|