@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
|
@@ -51,7 +51,7 @@ export const UsageGuidance: Story = {
|
|
|
51
51
|
<p>適合 Input 的真實業務場景(點擊跳轉「展示」頁範例):</p>
|
|
52
52
|
<ul className="space-y-1">
|
|
53
53
|
<li><LinkTo kind="Design System/Components/Input/展示" name="四模式"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">商品名稱欄位的編輯 / 純展示 / 唯讀 / 停用四模式</span></LinkTo></li>
|
|
54
|
-
<li><LinkTo kind="Design System/Components/Input/展示" name="
|
|
54
|
+
<li><LinkTo kind="Design System/Components/Input/展示" name="三種尺寸"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">搜尋列 — 輸入框與按鈕並排等高</span></LinkTo></li>
|
|
55
55
|
<li><LinkTo kind="Design System/Components/Input/展示" name="尾端操作"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">密碼顯示切換 / 搜尋清除等尾端操作</span></LinkTo></li>
|
|
56
56
|
<li><LinkTo kind="Design System/Components/Input/展示" name="錯誤狀態"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">Email 格式錯誤的驗證回饋</span></LinkTo></li>
|
|
57
57
|
</ul>
|
|
@@ -128,10 +128,10 @@ export const ModeRule: Story = {
|
|
|
128
128
|
|
|
129
129
|
<Rule
|
|
130
130
|
title="❌ 不用 readonly 作為 DataTable cell"
|
|
131
|
-
note="readonly
|
|
131
|
+
note="readonly 有底色 chrome(bg-readonly),密集列表會顯得鬆散。Table cell 用 `<Input mode='view'>`(Model A:edit 幾何減 chrome —— 保留 px 與高度,只拔 border/bg;cell 內經 naked variant 由 host cell 供 padding)"
|
|
132
132
|
>
|
|
133
133
|
<Input mode="readonly" value="alice@example.com" />
|
|
134
|
-
<Label warn>↑ 在 table cell
|
|
134
|
+
<Label warn>↑ 在 table cell 中有多餘底色 chrome → 用 <Input mode="view" /></Label>
|
|
135
135
|
</Rule>
|
|
136
136
|
</div>
|
|
137
137
|
),
|
|
@@ -92,17 +92,17 @@ endAction(host 內嵌 inline action)vs 獨立 `<Button iconOnly>` 的分界詳 `
|
|
|
92
92
|
|
|
93
93
|
---
|
|
94
94
|
|
|
95
|
-
##
|
|
95
|
+
## View
|
|
96
96
|
|
|
97
|
-
`<Input mode="
|
|
97
|
+
`<Input mode="view">` 是 identity 顯示:
|
|
98
98
|
- 有值:原樣輸出
|
|
99
|
-
- null / undefined / 空字串:半形 `-`(hyphen),`text-foreground`(
|
|
99
|
+
- null / undefined / 空字串:半形 `-`(hyphen),`text-foreground`(view/readonly 供檢視值同色;disabled → fg-disabled)
|
|
100
100
|
|
|
101
101
|
---
|
|
102
102
|
|
|
103
103
|
## Loading
|
|
104
104
|
|
|
105
|
-
`loading?: boolean`(Field SSOT,詳 `Field/field-controls.spec.md
|
|
105
|
+
`loading?: boolean`(Field SSOT,詳 `Field/field-controls.spec.md`「Loading state」段 ~L93-138):右側 endAction 自動顯 `<CircularProgress/>` + `aria-busy="true"`;input 維持可編輯(Ant Input.Search editable 派,反 Material readonly 派,適合 search debounce)。
|
|
106
106
|
|
|
107
107
|
## 禁止事項
|
|
108
108
|
|
|
@@ -26,9 +26,9 @@ export const Modes: Story = {
|
|
|
26
26
|
<Input defaultValue="Wireless Bluetooth Headphones" placeholder="輸入商品名稱" aria-label="商品名稱(edit mode demo)" />
|
|
27
27
|
</div>
|
|
28
28
|
<div>
|
|
29
|
-
<h3 className="text-body font-bold text-foreground mb-2">
|
|
29
|
+
<h3 className="text-body font-bold text-foreground mb-2">view</h3>
|
|
30
30
|
<p className="text-caption text-fg-muted mb-3">純展示(read-only 內容)— 無 input chrome / 無互動 affordance</p>
|
|
31
|
-
<Input mode="
|
|
31
|
+
<Input mode="view" value="Wireless Bluetooth Headphones" aria-label="商品名稱(view 模式示範)" />
|
|
32
32
|
</div>
|
|
33
33
|
<div>
|
|
34
34
|
<h3 className="text-body font-bold text-foreground mb-2">readonly</h3>
|
|
@@ -53,7 +53,7 @@ export const Modes: Story = {
|
|
|
53
53
|
|
|
54
54
|
/* ── 尺寸與 Button 對齊 ── */
|
|
55
55
|
export const SizeAlignment: Story = {
|
|
56
|
-
name: '
|
|
56
|
+
name: '三種尺寸',
|
|
57
57
|
render: () => (
|
|
58
58
|
<div className="flex flex-col gap-4">
|
|
59
59
|
{(['sm', 'md', 'lg'] as const).map(size => (
|
|
@@ -16,7 +16,7 @@ import { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'
|
|
|
16
16
|
export interface InputProps
|
|
17
17
|
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'>,
|
|
18
18
|
Omit<VariantProps<typeof fieldWrapperStyles>, 'mode' | 'variant' | 'width'> {
|
|
19
|
-
/** Field
|
|
19
|
+
/** Field mode */
|
|
20
20
|
mode?: FieldMode
|
|
21
21
|
/**
|
|
22
22
|
* Visual variant(正交於 mode),對齊 FieldContext.variant 透傳。
|
|
@@ -108,24 +108,24 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
108
108
|
const resolvedMode: FieldMode = useResolvedFieldMode({ mode: modeProp, disabled, readOnly })
|
|
109
109
|
const emptyDisplay = useFieldEmptyDisplay()
|
|
110
110
|
const isEditable = resolvedMode === 'edit'
|
|
111
|
-
const
|
|
111
|
+
const isView = resolvedMode === 'view'
|
|
112
112
|
// error 合併:自身 error prop OR Field context invalid
|
|
113
113
|
const resolvedError = useResolvedFieldInvalid(error)
|
|
114
114
|
// 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反 7+ 重複 ternary)
|
|
115
115
|
const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']
|
|
116
116
|
const iconColor = resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-fg-muted'
|
|
117
117
|
|
|
118
|
-
// ──
|
|
118
|
+
// ── view mode(+ readonly 空值)純展示,渲染 <span> 取代 <input> ──
|
|
119
119
|
// 對齊 Carbon read-only / PatternFly inline-edit hidden-input / Cloudscape display-mode
|
|
120
|
-
// 2026-07-08 user 拍板:readonly 空值也走
|
|
120
|
+
// 2026-07-08 user 拍板:readonly 空值也走 view-span 顯 '-'(readonly **有值** 仍走下方
|
|
121
121
|
// native <input readOnly> 保留選取/複製語意 — field-controls.spec.md「null / undefined 值」)。
|
|
122
122
|
// 2026-07-14 audit Dim 26(dual-mode coherence):uncontrolled `defaultValue` 也要被
|
|
123
|
-
//
|
|
123
|
+
// view / readonly-空值判定認得 — 原本只讀 `value`,uncontrolled Input 切 view 會誤顯 '-'。
|
|
124
124
|
// 2026-07-14 R2(dual-model consensus,取代同日稍早 onChange-mirror 版):resolved value 改走
|
|
125
125
|
// useControllable 內部 SSOT(Radix idiom,同 Select / Pagination 既有消費)。uncontrolled 時
|
|
126
126
|
// native input 由 value={resolved} 內部驅動(defaultValue 只作初始值,不落 DOM attribute),
|
|
127
127
|
// 修 mirror 版兩個真缺陷:
|
|
128
|
-
// (1) remount-stale — 切
|
|
128
|
+
// (1) remount-stale — 切 view / readonly(native input unmount)再回 edit,native input 從
|
|
129
129
|
// defaultValue attribute 重掛 → 顯示 stale 初始值,與 mirror 判定分歧;
|
|
130
130
|
// (2) form.reset() stale — HTML 標準 reset 不發 input event,onChange mirror 停在 reset 前的值
|
|
131
131
|
// (下方 reset bridge 修)。
|
|
@@ -137,10 +137,10 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
137
137
|
defaultValue: defaultValue ?? '',
|
|
138
138
|
})
|
|
139
139
|
const displayValue = resolved != null && resolved !== '' ? String(resolved) : null
|
|
140
|
-
const showDisplaySpan =
|
|
140
|
+
const showDisplaySpan = isView || (resolvedMode === 'readonly' && displayValue == null)
|
|
141
141
|
|
|
142
142
|
// form.reset() bridge(uncontrolled only):reset 恢復 defaultValue 但不發 input event →
|
|
143
|
-
// 手動把 resolved 歸位 defaultValue。keyed on showDisplaySpan:
|
|
143
|
+
// 手動把 resolved 歸位 defaultValue。keyed on showDisplaySpan:view 分支不掛 native input,
|
|
144
144
|
// 回 edit 重掛時 effect 重跑補掛 listener。
|
|
145
145
|
const innerRef = React.useRef<HTMLInputElement | null>(null)
|
|
146
146
|
React.useEffect(() => {
|
|
@@ -159,7 +159,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
159
159
|
}, [ref])
|
|
160
160
|
|
|
161
161
|
if (showDisplaySpan) {
|
|
162
|
-
const spanMode =
|
|
162
|
+
const spanMode = isView ? 'view' : 'readonly'
|
|
163
163
|
return (
|
|
164
164
|
<div
|
|
165
165
|
className={cn(
|
|
@@ -181,9 +181,9 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
181
181
|
<span
|
|
182
182
|
className={cn(
|
|
183
183
|
bareInputStyles,
|
|
184
|
-
// B1 fix(2026-05-05):
|
|
185
|
-
// cell display canonical:single-line value 過長 → ellipsis。Textarea
|
|
186
|
-
// 不在此處;Input
|
|
184
|
+
// B1 fix(2026-05-05):view mode 單行 ellipsis 截斷(對齊 Notion / Airtable / Linear
|
|
185
|
+
// cell display canonical:single-line value 過長 → ellipsis。Textarea view 走 wrap path,
|
|
186
|
+
// 不在此處;Input view 永遠 single-line。)
|
|
187
187
|
'truncate',
|
|
188
188
|
displayValue == null && fieldEmptyColorClass(resolvedMode),
|
|
189
189
|
)}
|
|
@@ -254,7 +254,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
254
254
|
)
|
|
255
255
|
Input.displayName = 'Input'
|
|
256
256
|
|
|
257
|
-
// Phase B1(2026-05-05):InputDisplay 退場。改用 `<Input mode="
|
|
257
|
+
// Phase B1(2026-05-05):InputDisplay 退場。改用 `<Input mode="view" value={...} />`
|
|
258
258
|
// 對齊 Carbon read-only / PatternFly inline-edit hidden-input / Cloudscape display-mode 統一 mode 模型。
|
|
259
259
|
|
|
260
260
|
// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)
|
|
@@ -256,7 +256,7 @@ export const Overview = {
|
|
|
256
256
|
<thead><tr><Th>Prop</Th><Th>Type</Th><Th>Default</Th><Th>說明</Th></tr></thead>
|
|
257
257
|
<tbody>
|
|
258
258
|
{[
|
|
259
|
-
['mode', "'edit' | '
|
|
259
|
+
['mode', "'edit' | 'view' | 'readonly' | 'disabled'", "'edit'", 'FieldMode 四模式'],
|
|
260
260
|
['variant', "'default'", "'default'", 'Field chrome variant(naked:cell-as-input 透明 chrome,@internal,2026-07-14 型別收窄至 FieldVariantInternal)'],
|
|
261
261
|
['size', "'sm' | 'md' | 'lg'", "'md'", '尺寸,與 Button 共用 token'],
|
|
262
262
|
['error', 'boolean', 'false', '錯誤狀態,紅色邊框 + aria-invalid'],
|
|
@@ -265,7 +265,7 @@ export const Overview = {
|
|
|
265
265
|
['placeholder', 'string', "'https://'", '空值時的佔位文字'],
|
|
266
266
|
['disabled', 'boolean', 'false', '等同 mode="disabled"'],
|
|
267
267
|
['label', 'string', '—', '自訂顯示文字(覆蓋自動 hostname 提取)'],
|
|
268
|
-
['showDisplayEndIcon', 'boolean', 'false', '
|
|
268
|
+
['showDisplayEndIcon', 'boolean', 'false', 'view 態改包 Field naked wrapper(D-path opt-in,DataTable cell view↔edit 像素對齊;本元件 edit 無 endIcon,故僅 wrapper 無 ItemSuffix)'],
|
|
269
269
|
].map(([p, t, d, desc]) => (
|
|
270
270
|
<tr key={p}><Td mono>{p}</Td><Td mono>{t}</Td><Td mono>{d}</Td><Td>{desc}</Td></tr>
|
|
271
271
|
))}
|
|
@@ -59,7 +59,7 @@ export const UsageGuidance: Story = {
|
|
|
59
59
|
<LinkTo kind="Design System/Components/LinkInput/展示" name="四模式"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">四模式</span></LinkTo>
|
|
60
60
|
</li>
|
|
61
61
|
<li>
|
|
62
|
-
<LinkTo kind="Design System/Components/LinkInput/展示" name="
|
|
62
|
+
<LinkTo kind="Design System/Components/LinkInput/展示" name="檢視樣式"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">展示樣式</span></LinkTo>
|
|
63
63
|
</li>
|
|
64
64
|
</ul>
|
|
65
65
|
<p className="text-fg-muted mt-3">判斷不確定時:先確認這個值是不是「儲存後使用者會想直接點開的完整網址」。若不是(例如純字串代號、email、內部路徑),改用 Input。下方「vs 近親」段有正反對照範例。</p>
|
|
@@ -61,6 +61,10 @@ LinkInput 是 **URL 的**輸入與顯示元件。外觀基於 Input,但 value
|
|
|
61
61
|
- blur 時驗證格式,合法則自動切回 link 狀態
|
|
62
62
|
- 格式不合法維持 input 狀態 + error 邊框,直到格式正確
|
|
63
63
|
|
|
64
|
+
### View 模式幾何(Model A 明文例外)
|
|
65
|
+
|
|
66
|
+
`mode="view"` 預設(`showDisplayEndIcon=false`)渲染**裸 anchor / span**——不包 `fieldWrapperStyles`(無 px 內距、無 h-field 高度),是 `../Field/field-controls.spec.md` 共享 contract (d)「view×default = edit 幾何減 chrome(留 px)」的**明文例外**(backward compat;2026-07-16 VERIFY 拍板記錄為例外而非對齊 code)。需要與 cell edit 像素對齊時走 `showDisplayEndIcon=true` opt-in(Field naked wrapper 包覆 anchor)。typography 仍消費 contract (e)(`fieldDisplayTextClass`)。
|
|
67
|
+
|
|
64
68
|
---
|
|
65
69
|
|
|
66
70
|
## 驗證
|
|
@@ -85,7 +89,7 @@ URL 格式要求:必須包含 `http://` 或 `https://` protocol。
|
|
|
85
89
|
## 極長 URL(邊界)
|
|
86
90
|
|
|
87
91
|
- **顯示文字 = hostname**:link 狀態預設只顯示 hostname(去 `www.`,如 `https://github.com/org/repo` → `github.com`),非完整 URL;`label` prop 可覆寫顯示文字。完整 URL 載於 `href`
|
|
88
|
-
- **單行 truncate**:link / readonly /
|
|
92
|
+
- **單行 truncate**:link / readonly / view 狀態超寬時 ellipsis 截斷,不換行
|
|
89
93
|
- **編輯態**:原生 input 水平捲動,無長度上限
|
|
90
94
|
|
|
91
95
|
---
|
|
@@ -108,7 +112,7 @@ URL 格式要求:必須包含 `http://` 或 `https://` protocol。
|
|
|
108
112
|
|
|
109
113
|
## 為何無 StateBehavior
|
|
110
114
|
|
|
111
|
-
LinkInput 是 **Field Controls family 成員**——互動狀態(focus / invalid / disabled / readonly)完全繼承 `../Field/field-controls.spec.md` SSOT「Mode 狀態」。LinkInput 特有的狀態(edit 輸入 vs
|
|
115
|
+
LinkInput 是 **Field Controls family 成員**——互動狀態(focus / invalid / disabled / readonly)完全繼承 `../Field/field-controls.spec.md` SSOT「Mode 狀態」。LinkInput 特有的狀態(edit 輸入 vs view link-chip)已在 `Overview` 中說明。重寫 StateBehavior = 與 field-controls SSOT 漂移。
|
|
112
116
|
|
|
113
117
|
對應 anatomy story:保留 `Overview` + `Inspector` + `ColorMatrix` + `SizeMatrix` + `Accessibility`。互動 state 見 Input 的 `StateBehavior` + field-controls.spec.md。
|
|
114
118
|
|
|
@@ -100,8 +100,8 @@ export const Modes: Story = {
|
|
|
100
100
|
</div>
|
|
101
101
|
{/* @story-trait-rationale: pre-existing trait gaps tracked separately */}
|
|
102
102
|
<div>
|
|
103
|
-
<h3 className="text-body font-bold text-foreground mb-2">
|
|
104
|
-
<LinkInput mode="
|
|
103
|
+
<h3 className="text-body font-bold text-foreground mb-2">view</h3>
|
|
104
|
+
<LinkInput mode="view" value="https://github.com" />
|
|
105
105
|
</div>
|
|
106
106
|
<div>
|
|
107
107
|
<h3 className="text-body font-bold text-foreground mb-2">readonly</h3>
|
|
@@ -119,14 +119,14 @@ export const Modes: Story = {
|
|
|
119
119
|
),
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
/* ──
|
|
123
|
-
export const
|
|
124
|
-
name: '
|
|
122
|
+
/* ── View(DataTable cell 用)── */
|
|
123
|
+
export const View: Story = {
|
|
124
|
+
name: '檢視樣式',
|
|
125
125
|
render: () => (
|
|
126
126
|
<div className="flex flex-col gap-3">
|
|
127
|
-
<LinkInput mode="
|
|
128
|
-
<LinkInput mode="
|
|
129
|
-
<LinkInput mode="
|
|
127
|
+
<LinkInput mode="view" value="https://www.example.com/path/to/page" />
|
|
128
|
+
<LinkInput mode="view" value="https://github.com/user/repo" label="GitHub Repo" />
|
|
129
|
+
<LinkInput mode="view" value={null} />
|
|
130
130
|
</div>
|
|
131
131
|
),
|
|
132
132
|
}
|
|
@@ -27,10 +27,10 @@ function formatHostname(url: string): string {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
// ──
|
|
30
|
+
// ── View rendering(inline,2026-05-05 Phase B3 retire LinkInputDisplay)──
|
|
31
31
|
// 取代 LinkInputDisplay sub-component:純展示 a tag,無 input chrome、無 hover affordance。
|
|
32
32
|
// edit mode 內 link state(showLink branch)也共用此 helper,確保「編輯態的 link 顯示」與
|
|
33
|
-
//
|
|
33
|
+
// view mode 的視覺完全一致(SSOT)。
|
|
34
34
|
function renderLinkAnchor(value: string, label?: string) {
|
|
35
35
|
const displayText = label || formatHostname(value)
|
|
36
36
|
return (
|
|
@@ -59,7 +59,7 @@ export interface LinkInputProps
|
|
|
59
59
|
* - `'default'`(預設)— Field wrapper 完整 chrome(form / Field 內嵌)。
|
|
60
60
|
* (2026-07-09 `bare` variant 退役;naked = cell-as-input substrate,@internal)
|
|
61
61
|
*
|
|
62
|
-
* mode='
|
|
62
|
+
* mode='view' 時 chrome 無視覺意義(view 完全無 wrapper);chrome 僅作用於 edit / readonly / disabled。
|
|
63
63
|
*/
|
|
64
64
|
variant?: FieldVariant
|
|
65
65
|
error?: boolean
|
|
@@ -71,11 +71,11 @@ export interface LinkInputProps
|
|
|
71
71
|
/** 自訂顯示文字(非編輯時) */
|
|
72
72
|
label?: string
|
|
73
73
|
/**
|
|
74
|
-
*
|
|
75
|
-
* — DataTable cell
|
|
76
|
-
* 設 true 時
|
|
74
|
+
* View 態是否包 Field naked wrapper(D-path opt-in,2026-05-08)
|
|
75
|
+
* — DataTable cell view↔edit 像素級對齊用。預設 false(裸 anchor,backward compat)。
|
|
76
|
+
* 設 true 時 view 走 fieldWrapperStyles(naked variant)包覆 anchor,
|
|
77
77
|
* 與 cell edit (`<Input naked>`) 同 DOM 結構,消除 Layer-B padding mismatch。
|
|
78
|
-
* **本元件 edit 無 endIcon(UrlCell 用 plain Input edit)→
|
|
78
|
+
* **本元件 edit 無 endIcon(UrlCell 用 plain Input edit)→ view 也無 ItemSuffix**(僅 wrapper)。
|
|
79
79
|
*/
|
|
80
80
|
showDisplayEndIcon?: boolean
|
|
81
81
|
}
|
|
@@ -117,12 +117,12 @@ const LinkInput = React.forwardRef<HTMLInputElement, LinkInputProps>(
|
|
|
117
117
|
// 2026-07-05 D4:invalid 經 useResolvedFieldInvalid 統一接 fieldCtx.invalid(field-controls.spec.md
|
|
118
118
|
// resolver 契約「error/invalid = prop OR fieldCtx.invalid」)— 原本紅框只吃 errorProp||localError、
|
|
119
119
|
// aria-invalid 卻 inline 加 fieldCtx?.invalid → <Field invalid> 下 AT 聽到 invalid 視覺卻正常框
|
|
120
|
-
// (聚焦還變藍)。紅框與 aria 自此同源。宣告於
|
|
120
|
+
// (聚焦還變藍)。紅框與 aria 自此同源。宣告於 view early return 之前(Rules-of-Hooks)。
|
|
121
121
|
const fieldInvalid = useResolvedFieldInvalid(errorProp)
|
|
122
122
|
|
|
123
123
|
// ── Local state / refs ──────────────────────────────────────────────────
|
|
124
|
-
// 2026-07-04 Rules-of-Hooks 修:hooks 必在
|
|
125
|
-
// 否則 mode 於 render 間切換
|
|
124
|
+
// 2026-07-04 Rules-of-Hooks 修:hooks 必在 view early return 之前宣告,
|
|
125
|
+
// 否則 mode 於 render 間切換 view↔edit 會改變 hook 呼叫數 → React #310 crash
|
|
126
126
|
// (同 beta.76 Combobox 同款修法;僅搬位置,hook 邏輯不變)。
|
|
127
127
|
const [editing, setEditing] = React.useState(false)
|
|
128
128
|
const [localValue, setLocalValue] = React.useState(value ?? '')
|
|
@@ -145,25 +145,27 @@ const LinkInput = React.forwardRef<HTMLInputElement, LinkInputProps>(
|
|
|
145
145
|
const focusRafIdRef = React.useRef<number>(0)
|
|
146
146
|
React.useEffect(() => () => { if (focusRafIdRef.current) cancelAnimationFrame(focusRafIdRef.current) }, [])
|
|
147
147
|
|
|
148
|
-
// ── mode='
|
|
148
|
+
// ── mode='view' ─────────────────────────────────────────────────────
|
|
149
149
|
// 純展示:無 input chrome / 無 hover affordance / 無 Pencil edit 入口。
|
|
150
150
|
// 取代既有 LinkInputDisplay sub-component(2026-05-05 Phase B3 retire)。
|
|
151
|
-
// Default(showDisplayEndIcon=false):無 wrapper 裸 anchor — backward compat
|
|
151
|
+
// Default(showDisplayEndIcon=false):無 wrapper 裸 anchor — backward compat;為 contract (d)
|
|
152
|
+
// Model A(view×default = edit 幾何減 chrome、留 px)之明文例外,見 link-input.spec.md
|
|
153
|
+
// 「View 模式幾何(Model A 明文例外)」。
|
|
152
154
|
// Opt-in(showDisplayEndIcon=true,2026-05-08 D-path):Field naked wrapper 包覆 anchor,
|
|
153
155
|
// 與 cell edit (`<Input naked>`) 同 DOM 結構消除像素偏移(無 ItemSuffix,因 edit 也無 endIcon)。
|
|
154
|
-
if (resolvedMode === '
|
|
156
|
+
if (resolvedMode === 'view') {
|
|
155
157
|
if (!showDisplayEndIcon) {
|
|
156
|
-
// 2026-05-14 I2 fix(spec contract (e)
|
|
158
|
+
// 2026-05-14 I2 fix(spec contract (e) view typography canonical):非 D-path bare
|
|
157
159
|
// anchor / span 必套 `fieldDisplayTextClass(size)`(sm/md→text-body,lg→text-body-lg)
|
|
158
|
-
// — 對齊跨 Field family
|
|
159
|
-
// 字體 → 跟其他 Field
|
|
160
|
+
// — 對齊跨 Field family view 視覺尺寸統一。原無 font-size class → 用 browser default
|
|
161
|
+
// 字體 → 跟其他 Field view 不一致(user 抓 I2)。truncate 同需,長 URL ellipsis(I1)。
|
|
160
162
|
if (!value) return <span className={cn(fieldDisplayTextClass(size), fieldEmptyColorClass(resolvedMode), 'block truncate')}>{emptyDisplay}</span>
|
|
161
163
|
return <span className={cn(fieldDisplayTextClass(size), 'block truncate')}>{renderLinkAnchor(value, label)}</span>
|
|
162
164
|
}
|
|
163
165
|
return (
|
|
164
166
|
<div
|
|
165
|
-
className={cn(fieldWrapperStyles({ mode: '
|
|
166
|
-
data-field-mode="
|
|
167
|
+
className={cn(fieldWrapperStyles({ mode: 'view', variant: resolvedVariant, size }), className)}
|
|
168
|
+
data-field-mode="view"
|
|
167
169
|
>
|
|
168
170
|
<span className="flex-1 min-w-0 truncate">
|
|
169
171
|
{value
|
|
@@ -243,7 +245,7 @@ const LinkInput = React.forwardRef<HTMLInputElement, LinkInputProps>(
|
|
|
243
245
|
)
|
|
244
246
|
}
|
|
245
247
|
|
|
246
|
-
// edit — link
|
|
248
|
+
// edit — link 顯示分支(有合法 URL 且未在編輯中)
|
|
247
249
|
if (showLink) {
|
|
248
250
|
return (
|
|
249
251
|
<div
|
|
@@ -74,7 +74,7 @@ export const CompositionRules: Story = {
|
|
|
74
74
|
<div>
|
|
75
75
|
<Section title="Pattern 1 — Alert(persistent inline announcement)">
|
|
76
76
|
<div className="prose prose-sm max-w-prose">
|
|
77
|
-
<p>需要「持續顯示直到 user 主動 dismiss」→ <LinkTo kind="Design System/Components/Alert/展示" name="
|
|
77
|
+
<p>需要「持續顯示直到 user 主動 dismiss」→ <LinkTo kind="Design System/Components/Alert/展示" name="單行(低調 vs 實心對照)"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">Alert 低調單行</span></LinkTo>。Alert 內部消費 Notice + 加上 dismiss button + live region ARIA(error/warning → role="alert" assertive;info/success/neutral → role="status" polite)。</p>
|
|
78
78
|
<ul>
|
|
79
79
|
<li>典型場景:付款失敗 banner / workspace 警告 / 重要通知 user 必看</li>
|
|
80
80
|
<li>對齊世界級:Polaris <code>Banner</code> / Material <code>Banner</code> / Atlassian <code>InlineMessage</code> — 共識用「persistent + dismissible」</li>
|
|
@@ -208,12 +208,18 @@ export function useInverseTheme(): 'dark' | 'light' {
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)
|
|
211
|
-
//
|
|
211
|
+
// 2026-07-16 dim 78/DA3:variants 補齊 — 對齊 notice.spec.md frontmatter 5 variants(SSOT)
|
|
212
|
+
// + alertMeta `{ purpose }` idiom(Alert 是 Notice 直接 consumer,同 5-variant 語意)。
|
|
213
|
+
// variant 語意 = status icon 選擇(per spec「Variant」表);色彩由消費者 data-theme 控制。
|
|
212
214
|
export const noticeMeta = {
|
|
213
215
|
component: 'Notice',
|
|
214
216
|
family: 2, // Family 2(List item layout)消費者 — 對齊 notice.spec.md frontmatter family: 2 + body「Layout Family」段
|
|
215
217
|
variants: {
|
|
216
|
-
|
|
218
|
+
neutral: { purpose: '一般訊息、系統公告;無情緒色、不掛 status icon(title-only icon-less 形態)' },
|
|
219
|
+
info: { purpose: '資訊提示(版本更新、流程說明);Info icon' },
|
|
220
|
+
success: { purpose: '操作成功確認;CircleCheck icon' },
|
|
221
|
+
warning: { purpose: '可恢復警告(未儲存變更、連線不穩);TriangleAlert icon' },
|
|
222
|
+
error: { purpose: '錯誤(操作失敗、權限不足);XCircle icon' },
|
|
217
223
|
},
|
|
218
224
|
sizes: {
|
|
219
225
|
|
|
@@ -19,12 +19,12 @@ type StateKey = 'default' | 'hover' | 'focus' | 'error' | 'disabled'
|
|
|
19
19
|
type SizeKey = 'sm' | 'md' | 'lg'
|
|
20
20
|
type ColorSpec = { bg: string; text: string; border: string }
|
|
21
21
|
|
|
22
|
-
const MODES: ModeKey[] = ['edit', '
|
|
22
|
+
const MODES: ModeKey[] = ['edit', 'view', 'readonly', 'disabled']
|
|
23
23
|
const SIZES: SizeKey[] = ['sm', 'md', 'lg']
|
|
24
24
|
|
|
25
25
|
const MODE_DESC: Record<ModeKey, string> = {
|
|
26
26
|
edit: '表單可編輯欄位,有邊框、hover / focus 回饋',
|
|
27
|
-
|
|
27
|
+
view: '純展示值(非表單)— edit 幾何減 chrome(透明 bg/border、保留 px 內距與高度,Model A)。用在 cell / InlineEdit / 詳情',
|
|
28
28
|
readonly: '表單中不可編輯但可見,灰底、無邊框(input chrome 但鎖定)',
|
|
29
29
|
disabled: '停用狀態,灰底、灰字、cursor-not-allowed',
|
|
30
30
|
}
|
|
@@ -37,7 +37,7 @@ const TOKEN_MAP: Record<ModeKey, Record<StateKey, ColorSpec>> = {
|
|
|
37
37
|
error: { bg: '--surface', text: '--foreground', border: '--error' },
|
|
38
38
|
disabled: { bg: '--bg-disabled', text: '--fg-disabled', border: 'transparent' },
|
|
39
39
|
},
|
|
40
|
-
|
|
40
|
+
view: {
|
|
41
41
|
default: { bg: 'transparent', text: '--foreground', border: 'transparent' },
|
|
42
42
|
hover: { bg: 'transparent', text: '--foreground', border: 'transparent' },
|
|
43
43
|
focus: { bg: 'transparent', text: '--foreground', border: 'transparent' },
|
|
@@ -218,7 +218,7 @@ export const Overview = {
|
|
|
218
218
|
{/* Format options */}
|
|
219
219
|
<div className="flex flex-col gap-3">
|
|
220
220
|
<H3>格式化選項</H3>
|
|
221
|
-
<Desc>格式化在 readonly / disabled 和 `mode="
|
|
221
|
+
<Desc>格式化在 readonly / disabled 和 `mode="view"` 生效。edit 模式輸入 raw 數值(不套格式),確保使用者能直覺地修改。</Desc>
|
|
222
222
|
<div className="flex flex-col gap-2">
|
|
223
223
|
{([
|
|
224
224
|
{ label: 'prefix="$"', props: { prefix: '$' }, value: 2490 },
|
|
@@ -245,7 +245,7 @@ export const Overview = {
|
|
|
245
245
|
<thead><tr><Th>Prop</Th><Th>Type</Th><Th>Default</Th><Th>說明</Th></tr></thead>
|
|
246
246
|
<tbody>
|
|
247
247
|
{[
|
|
248
|
-
['mode', "'edit'|'
|
|
248
|
+
['mode', "'edit'|'view'|'readonly'|'disabled'", "'edit'", '顯示模式'],
|
|
249
249
|
['error', 'boolean', 'false', '紅色邊框 + aria-invalid(僅 edit 生效)'],
|
|
250
250
|
['size', "'sm'|'md'|'lg'", "'md'", '尺寸,與 Button 同 size 對齊'],
|
|
251
251
|
['value', 'number | null', '—', '數值'],
|
|
@@ -717,7 +717,7 @@ export const StateBehavior = {
|
|
|
717
717
|
<span className="text-caption font-medium text-fg-secondary">格式化行為</span>
|
|
718
718
|
<div className="overflow-x-auto">
|
|
719
719
|
<table className="border-collapse text-caption">
|
|
720
|
-
<thead><tr><Th>情境</Th><Th>edit 模式</Th><Th>readonly /
|
|
720
|
+
<thead><tr><Th>情境</Th><Th>edit 模式</Th><Th>readonly / view</Th></tr></thead>
|
|
721
721
|
<tbody>
|
|
722
722
|
{[
|
|
723
723
|
['value = 2490, prefix="$"', '2490(raw 數值)', '$2,490(格式化)'],
|
|
@@ -52,7 +52,7 @@ export const UsageGuidance: Story = {
|
|
|
52
52
|
<p>適合 NumberInput 的真實業務場景(點擊跳轉「展示」頁範例):</p>
|
|
53
53
|
<ul className="space-y-1">
|
|
54
54
|
<li><LinkTo kind="Design System/Components/NumberInput/展示" name="四模式"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">四模式</span></LinkTo></li>
|
|
55
|
-
<li><LinkTo kind="Design System/Components/NumberInput/展示" name="
|
|
55
|
+
<li><LinkTo kind="Design System/Components/NumberInput/展示" name="三種尺寸"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">尺寸與 Button 對齊</span></LinkTo></li>
|
|
56
56
|
<li><LinkTo kind="Design System/Components/NumberInput/展示" name="格式化選項"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">格式化選項</span></LinkTo></li>
|
|
57
57
|
<li><LinkTo kind="Design System/Components/NumberInput/展示" name="DataTable 整合"><span className="text-primary hover:text-primary-hover font-medium cursor-pointer">DataTable 整合</span></LinkTo></li>
|
|
58
58
|
</ul>
|
|
@@ -63,9 +63,9 @@ export const UsageGuidance: Story = {
|
|
|
63
63
|
<Section title="何時不用 + 替代方案">
|
|
64
64
|
<Rule
|
|
65
65
|
title="❌ 不用 NumberInput 做 DataTable 列表展示"
|
|
66
|
-
note="Table cell 用 `mode='
|
|
66
|
+
note="Table cell 用 `mode='view'` 渲染,不用 edit 模式輸入元件。DataTable 的 currency 欄位自動消費 `<NumberInput mode='view'>`"
|
|
67
67
|
>
|
|
68
|
-
<Label warn>Table cell 顯示值 → `mode='
|
|
68
|
+
<Label warn>Table cell 顯示值 → `mode='view'` 渲染,不用 edit Input</Label>
|
|
69
69
|
</Rule>
|
|
70
70
|
</Section>
|
|
71
71
|
|
|
@@ -162,7 +162,7 @@ export const FormatOptionsRule: Story = {
|
|
|
162
162
|
<div>
|
|
163
163
|
<Rule
|
|
164
164
|
title="prefix — 置於數字前的符號(貨幣是最常見場景)"
|
|
165
|
-
note="prefix 出現在 readonly /
|
|
165
|
+
note="prefix 出現在 readonly / view / disabled 與 DataTable cell 的格式化值;edit 模式輸入 raw 純數字、不渲染 prefix——使用者不需要自己輸入 $"
|
|
166
166
|
>
|
|
167
167
|
<NumberInput mode="readonly" value={2490} prefix="$" />
|
|
168
168
|
<NumberInput mode="readonly" value={12500} prefix="NT$" precision={0} />
|
|
@@ -61,11 +61,11 @@ NumberInput 是**數值的**輸入與顯示元件。格式化邏輯:`toLocaleS
|
|
|
61
61
|
## 對齊
|
|
62
62
|
|
|
63
63
|
- **Edit 模式(input)**:靠左——input 內打字是左到右
|
|
64
|
-
- **Table cell(
|
|
64
|
+
- **Table cell(View)**:靠右——縱向比較位數需要右對齊(由 DataTable 的 column type `number` / `currency` 控制)
|
|
65
65
|
|
|
66
|
-
##
|
|
66
|
+
## View
|
|
67
67
|
|
|
68
|
-
`<NumberInput mode="
|
|
68
|
+
`<NumberInput mode="view">` 在 table cell 和 Form readonly 共用 `formatNumber()` 格式化函式。
|
|
69
69
|
|
|
70
70
|
- 有值:格式化輸出(prefix + localized number + suffix)
|
|
71
71
|
- null / undefined:半形 `-`(hyphen),`text-foreground`(disabled → fg-disabled)
|
|
@@ -103,9 +103,9 @@ col.accessor('price', {
|
|
|
103
103
|
|
|
104
104
|
## 邊界案例
|
|
105
105
|
|
|
106
|
-
- **Disabled**:由 Field SSOT own(`Field/field-controls.spec.md` State machine 段)。視覺:wrapper bg → `bg-disabled`(neutral-2,`semantic.css` `--bg-disabled`)、formatted text → `text-fg-disabled`(M24 state>emphasis)。
|
|
106
|
+
- **Disabled**:由 Field SSOT own(`Field/field-controls.spec.md` State machine 段)。視覺:wrapper bg → `bg-disabled`(neutral-2,`semantic.css` `--bg-disabled`)、formatted text → `text-fg-disabled`(M24 state>emphasis)。View mode + disabled 維持格式化輸出但 token 切 disabled。
|
|
107
107
|
- **Loading**:NumberInput 不提供 loading state(`NumberInputProps` 無 `loading` prop、無 `CircularProgress` / `aria-busy` 邏輯)。數值輸入為同步操作,無 async fetch 語意;若需 async 場景(如遠端校驗)請走外層 Field validation + `endSlot` 自訂 spinner。
|
|
108
|
-
- **Empty(null / undefined / 空字串)**:
|
|
108
|
+
- **Empty(null / undefined / 空字串)**:View / readonly mode 渲半形 `-`(hyphen + `text-foreground`,disabled → fg-disabled);Edit mode placeholder 走 default placeholder color。
|
|
109
109
|
- **Invalid input**(non-numeric):input 為 `type="text" inputMode="decimal"`,onChange 以 `Number(raw)` parse,NaN 時忽略不觸發 `onChange`(value 維持原值);搭配 Field validation 渲 error variant(`aria-invalid="true"` + wrapper `border-error`(hover 時 `border-error-hover`)+ 下方 error message)。
|
|
110
110
|
- **輸入中間態**(`-`、`1.`、`0.0` 等 parse 後 lossy 的 raw 字串,2026-07-05 D4 codify):edit mode 以 internal draft string 承載——輸入期間 DOM 顯示 draft 原字串(負號、尾點不被 controlled 回寫吃掉),parse 成功即時同步 `onChange(parsed)`;blur / Enter commit(清 draft、顯示回 committed value,不合法殘字自然還原)、Escape 棄未提交 draft 字串、顯示回 committed value(NumberInput 為 live-commit,每次合法 parse 即 onChange,故非回復 pre-edit 原值;真 revert 需另存 snapshot,屬 API 擴充)。value 面語意不變:`''` / `'-'` 視為 `null`。對齊 Ant InputNumber / Adobe Spectrum NumberField internal draft canonical。 <!-- @benchmark-unverified -->
|
|
111
111
|
- **負數 / 超大數字**:`toLocaleString(locale)` 自動處理負號與千分位分組,無特殊 prop。
|
|
@@ -125,7 +125,7 @@ col.accessor('price', {
|
|
|
125
125
|
|
|
126
126
|
## A11y 預設
|
|
127
127
|
|
|
128
|
-
**ARIA / Pattern**:ARIA / keyboard 機制**僅 edit mode 適用**——edit mode 渲染 native `<input>` element(預設 a11y),且把 `...props`(含 `aria-label` / `aria-describedby` 等)轉發到該 input;該 native input 自身消費 `fieldCtx` 設定 `aria-invalid` / `aria-required` / `aria-describedby` / `aria-errormessage`(非由 Field wrapper 注入,對齊 Select / Combobox trigger 自設機制)。`
|
|
128
|
+
**ARIA / Pattern**:ARIA / keyboard 機制**僅 edit mode 適用**——edit mode 渲染 native `<input>` element(預設 a11y),且把 `...props`(含 `aria-label` / `aria-describedby` 等)轉發到該 input;該 native input 自身消費 `fieldCtx` 設定 `aria-invalid` / `aria-required` / `aria-describedby` / `aria-errormessage`(非由 Field wrapper 注入,對齊 Select / Combobox trigger 自設機制)。`view` / `readonly` / `disabled` 三 mode 渲染的是無互動純展示文字(`<div>` + `<span>`,不轉發 `aria-*`),螢幕報讀僅得格式化後的靜態值(對齊 Carbon read-only / Stripe display 慣例)。
|
|
129
129
|
|
|
130
130
|
**Keyboard 行為**:
|
|
131
131
|
|
|
@@ -18,7 +18,7 @@ const meta: Meta<typeof NumberInput> = {
|
|
|
18
18
|
export default meta
|
|
19
19
|
type Story = StoryObj<typeof NumberInput>
|
|
20
20
|
|
|
21
|
-
// @story-trait-rationale: pre-existing trait gaps (Default/WithError) tracked separately; this PR scope = add
|
|
21
|
+
// @story-trait-rationale: pre-existing trait gaps (Default/WithError) tracked separately; this PR scope = add view mode card to Modes story only.
|
|
22
22
|
/* ── 四模式 ── */
|
|
23
23
|
export const Modes: Story = {
|
|
24
24
|
name: '四模式',
|
|
@@ -31,8 +31,8 @@ export const Modes: Story = {
|
|
|
31
31
|
<NumberInput value={value} onChange={setValue} prefix="$" aria-label="價格(edit mode demo)" />
|
|
32
32
|
</div>
|
|
33
33
|
<div>
|
|
34
|
-
<h3 className="text-body font-bold text-foreground mb-2">
|
|
35
|
-
<NumberInput mode="
|
|
34
|
+
<h3 className="text-body font-bold text-foreground mb-2">view</h3>
|
|
35
|
+
<NumberInput mode="view" value={value} prefix="$" aria-label="價格(view 模式示範)" />
|
|
36
36
|
</div>
|
|
37
37
|
<div>
|
|
38
38
|
<h3 className="text-body font-bold text-foreground mb-2">readonly</h3>
|
|
@@ -49,7 +49,7 @@ export const Modes: Story = {
|
|
|
49
49
|
|
|
50
50
|
/* ── 尺寸與 Button 對齊 ── */
|
|
51
51
|
export const SizeAlignment: Story = {
|
|
52
|
-
name: '
|
|
52
|
+
name: '三種尺寸',
|
|
53
53
|
render: () => {
|
|
54
54
|
const [sm, setSm] = React.useState<number | null>(2490)
|
|
55
55
|
const [md, setMd] = React.useState<number | null>(2490)
|
|
@@ -27,14 +27,26 @@ function formatNumber(
|
|
|
27
27
|
): string {
|
|
28
28
|
if (value == null) return ''
|
|
29
29
|
const { precision, prefix = '', suffix = '', locale = 'en-US' } = options
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
// 2026-07-18 決策4:precision guard —— 負數/小數/超界進 Intl.NumberFormat 會 RangeError(crash)。
|
|
31
|
+
// clamp 回合法 Intl 範圍 [0,20](spec canonical ≤6);超界 dev-warn。維持 number 型別(對齊世界級 4 家)。
|
|
32
|
+
let safePrecision = precision
|
|
33
|
+
if (precision != null) {
|
|
34
|
+
const rounded = Math.round(precision)
|
|
35
|
+
if (rounded !== precision || precision < 0 || precision > 6) {
|
|
36
|
+
if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
|
|
37
|
+
console.warn(`[DS] NumberInput precision ${precision} 超出 canonical 範圍 0-6(或非整數)→ clamp。請傳 0-6 整數。`)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
safePrecision = Math.min(20, Math.max(0, rounded))
|
|
41
|
+
}
|
|
42
|
+
const formatted = safePrecision != null
|
|
43
|
+
? value.toLocaleString(locale, { minimumFractionDigits: safePrecision, maximumFractionDigits: safePrecision })
|
|
32
44
|
: value.toLocaleString(locale)
|
|
33
45
|
return `${prefix}${formatted}${suffix}`
|
|
34
46
|
}
|
|
35
47
|
|
|
36
48
|
// Phase B1(2026-05-05):NumberInputDisplay 退場。
|
|
37
|
-
// 改用 `<NumberInput mode="
|
|
49
|
+
// 改用 `<NumberInput mode="view" value={...} prefix={...} ... />`,format 邏輯在 mode='view' 分支重用。
|
|
38
50
|
|
|
39
51
|
// ── Types ───────────────────────────────────────────────────────────────────
|
|
40
52
|
|
|
@@ -45,7 +57,7 @@ export interface NumberInputProps
|
|
|
45
57
|
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'value' | 'defaultValue' | 'onChange' | 'type'>,
|
|
46
58
|
Omit<VariantProps<typeof fieldWrapperStyles>, 'mode' | 'variant' | 'width'>,
|
|
47
59
|
NumberFormatOptions {
|
|
48
|
-
/** Field
|
|
60
|
+
/** Field mode */
|
|
49
61
|
mode?: FieldMode
|
|
50
62
|
/**
|
|
51
63
|
* Visual chrome(正交於 mode);Phase B1(2026-05-05)新增。
|
|
@@ -122,7 +134,7 @@ const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
|
|
|
122
134
|
// 同 LinkInput 2026-07-04 修法)。
|
|
123
135
|
const [draft, setDraft] = React.useState<string | null>(null)
|
|
124
136
|
|
|
125
|
-
//
|
|
137
|
+
// view / readonly / disabled 都顯示格式化值(span 取代 input)
|
|
126
138
|
if (resolvedMode !== 'edit') {
|
|
127
139
|
return (
|
|
128
140
|
<div
|