@qijenchen/design-system 0.1.0-beta.95 → 0.1.0-beta.99
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/README.md +1 -0
- package/dist/components/AccountMenu/account-menu.d.ts +86 -0
- package/dist/components/AccountMenu/account-menu.d.ts.map +1 -0
- package/dist/components/AccountMenu/account-menu.js +64 -0
- package/dist/components/AccountMenu/account-menu.js.map +1 -0
- package/dist/components/AccountMenu/index.d.ts +2 -0
- package/dist/components/AccountMenu/index.d.ts.map +1 -0
- package/dist/components/AccountMenu/index.js +6 -0
- package/dist/components/AccountMenu/index.js.map +1 -0
- package/dist/components/AppShell/_demo-helpers.d.ts.map +1 -1
- package/dist/components/AppShell/app-shell.d.ts.map +1 -1
- package/dist/components/AppShell/app-shell.js +2 -2
- package/dist/components/AppShell/app-shell.js.map +1 -1
- package/dist/components/Badge/badge.d.ts +1 -1
- package/dist/components/Button/button.d.ts +3 -3
- package/dist/components/Button/button.d.ts.map +1 -1
- package/dist/components/Button/button.js +3 -2
- package/dist/components/Button/button.js.map +1 -1
- package/dist/components/Calendar/calendar.d.ts +10 -21
- package/dist/components/Calendar/calendar.d.ts.map +1 -1
- package/dist/components/Calendar/calendar.js +52 -59
- package/dist/components/Calendar/calendar.js.map +1 -1
- package/dist/components/Combobox/combobox.d.ts.map +1 -1
- package/dist/components/Combobox/combobox.js +1 -0
- 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 +3 -1
- package/dist/components/Command/command.js.map +1 -1
- package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
- package/dist/components/DataTable/cell-registry.js +1 -5
- package/dist/components/DataTable/cell-registry.js.map +1 -1
- package/dist/components/DataTable/data-table-filter-group.d.ts +6 -2
- package/dist/components/DataTable/data-table-filter-group.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-group.js +32 -27
- package/dist/components/DataTable/data-table-filter-group.js.map +1 -1
- package/dist/components/DataTable/data-table-filter-labels.d.ts +44 -0
- package/dist/components/DataTable/data-table-filter-labels.d.ts.map +1 -0
- package/dist/components/DataTable/data-table-filter-labels.js +55 -0
- package/dist/components/DataTable/data-table-filter-labels.js.map +1 -0
- package/dist/components/DataTable/data-table-filter-panel.d.ts +11 -0
- package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-panel.js +33 -10
- package/dist/components/DataTable/data-table-filter-panel.js.map +1 -1
- package/dist/components/DataTable/data-table-filter-value-picker.d.ts +4 -1
- package/dist/components/DataTable/data-table-filter-value-picker.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-value-picker.js +22 -8
- package/dist/components/DataTable/data-table-filter-value-picker.js.map +1 -1
- package/dist/components/DataTable/data-table.d.ts.map +1 -1
- package/dist/components/DataTable/data-table.js +5 -4
- package/dist/components/DataTable/data-table.js.map +1 -1
- package/dist/components/DataTable/index.js +2 -0
- package/dist/components/DataTable/index.js.map +1 -1
- package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
- package/dist/components/DatePicker/date-picker.js +187 -156
- package/dist/components/DatePicker/date-picker.js.map +1 -1
- package/dist/components/Dialog/dialog.d.ts +1 -1
- package/dist/components/Dialog/dialog.d.ts.map +1 -1
- package/dist/components/Dialog/dialog.js +4 -2
- package/dist/components/Dialog/dialog.js.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts +4 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.js +46 -14
- package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
- package/dist/components/Field/field-context.d.ts +12 -0
- package/dist/components/Field/field-context.d.ts.map +1 -1
- package/dist/components/Field/field-context.js +10 -1
- package/dist/components/Field/field-context.js.map +1 -1
- package/dist/components/Field/field-wrapper.d.ts.map +1 -1
- package/dist/components/Field/field-wrapper.js +7 -1
- package/dist/components/Field/field-wrapper.js.map +1 -1
- package/dist/components/Field/field.d.ts.map +1 -1
- package/dist/components/Field/field.js +11 -2
- package/dist/components/Field/field.js.map +1 -1
- package/dist/components/FieldControlGroup/field-control-group.d.ts +2 -5
- package/dist/components/FieldControlGroup/field-control-group.d.ts.map +1 -1
- package/dist/components/FieldControlGroup/field-control-group.js +2 -2
- package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
- package/dist/components/FileUpload/file-upload.js +1 -1
- package/dist/components/FileUpload/file-upload.js.map +1 -1
- package/dist/components/FileViewer/file-viewer.d.ts +1 -1
- package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
- package/dist/components/FileViewer/file-viewer.js +20 -3
- package/dist/components/FileViewer/file-viewer.js.map +1 -1
- package/dist/components/InlineEdit/inline-edit.d.ts +5 -0
- package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -1
- package/dist/components/InlineEdit/inline-edit.js +65 -33
- package/dist/components/InlineEdit/inline-edit.js.map +1 -1
- package/dist/components/Input/input.d.ts.map +1 -1
- package/dist/components/Input/input.js +3 -2
- package/dist/components/Input/input.js.map +1 -1
- package/dist/components/LinkInput/link-input.d.ts.map +1 -1
- package/dist/components/LinkInput/link-input.js +2 -1
- package/dist/components/LinkInput/link-input.js.map +1 -1
- package/dist/components/Menu/menu-item.d.ts.map +1 -1
- package/dist/components/Menu/menu-item.js +4 -2
- package/dist/components/Menu/menu-item.js.map +1 -1
- package/dist/components/PeoplePicker/people-picker.d.ts +2 -0
- package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
- package/dist/components/PeoplePicker/people-picker.js +4 -0
- package/dist/components/PeoplePicker/people-picker.js.map +1 -1
- package/dist/components/Popover/popover.d.ts.map +1 -1
- package/dist/components/Popover/popover.js +4 -2
- package/dist/components/Popover/popover.js.map +1 -1
- package/dist/components/ScrollArea/scroll-area.d.ts +4 -3
- package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
- package/dist/components/ScrollArea/scroll-area.js +1 -1
- package/dist/components/ScrollArea/scroll-area.js.map +1 -1
- package/dist/components/Select/select.d.ts.map +1 -1
- package/dist/components/Select/select.js +59 -37
- package/dist/components/Select/select.js.map +1 -1
- package/dist/components/SelectMenu/select-menu.d.ts +2 -0
- package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
- package/dist/components/SelectMenu/select-menu.js +3 -1
- package/dist/components/SelectMenu/select-menu.js.map +1 -1
- package/dist/components/Sheet/sheet.d.ts +1 -1
- package/dist/components/Sidebar/sidebar.js +2 -2
- package/dist/components/Sidebar/sidebar.js.map +1 -1
- package/dist/components/Tabs/tabs.d.ts.map +1 -1
- package/dist/components/Tabs/tabs.js +3 -0
- package/dist/components/Tabs/tabs.js.map +1 -1
- package/dist/components/Tag/tag.d.ts +1 -1
- package/dist/components/Tag/tag.d.ts.map +1 -1
- package/dist/components/Tag/tag.js +4 -3
- package/dist/components/Tag/tag.js.map +1 -1
- package/dist/components/Textarea/textarea.d.ts +11 -0
- package/dist/components/Textarea/textarea.d.ts.map +1 -1
- package/dist/components/Textarea/textarea.js +22 -5
- package/dist/components/Textarea/textarea.js.map +1 -1
- package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
- package/dist/components/TimePicker/time-picker.js +4 -3
- package/dist/components/TimePicker/time-picker.js.map +1 -1
- package/dist/components/Tooltip/tooltip.d.ts +1 -1
- package/dist/components/Tooltip/tooltip.d.ts.map +1 -1
- package/dist/components/Tooltip/tooltip.js +26 -1
- package/dist/components/Tooltip/tooltip.js.map +1 -1
- package/dist/components/TreeView/tree-view.d.ts +2 -0
- package/dist/components/TreeView/tree-view.d.ts.map +1 -1
- package/dist/components/TreeView/tree-view.js +3 -2
- package/dist/components/TreeView/tree-view.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.d.ts +10 -1
- package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.js +77 -29
- package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
- package/dist/patterns/header-canonical/chrome-header.d.ts +11 -4
- package/dist/patterns/header-canonical/chrome-header.d.ts.map +1 -1
- package/dist/patterns/header-canonical/chrome-header.js +1 -1
- package/dist/patterns/header-canonical/chrome-header.js.map +1 -1
- package/dist/patterns/overlay-surface/overlay-surface.d.ts +4 -3
- package/dist/patterns/overlay-surface/overlay-surface.d.ts.map +1 -1
- package/dist/patterns/overlay-surface/overlay-surface.js +1 -0
- package/dist/patterns/overlay-surface/overlay-surface.js.map +1 -1
- package/dist/patterns/resize-handle/resize-handle.d.ts +9 -11
- package/dist/patterns/resize-handle/resize-handle.d.ts.map +1 -1
- package/dist/patterns/resize-handle/resize-handle.js +3 -6
- package/dist/patterns/resize-handle/resize-handle.js.map +1 -1
- package/ds-canonical/adapters/claude/agents/canonical-reviewer.md +1 -1
- package/ds-canonical/adapters/claude-root-instructions.md +1 -1
- package/ds-canonical/adapters/claude-settings-base.json +2 -5
- package/ds-canonical/fork/AGENTS.md +21 -10
- package/ds-canonical/fork/codex/hooks.json +1 -1
- package/ds-canonical/fork/common/instruction.md +21 -10
- package/ds-canonical/fork/consumer/governance-check.mjs +101 -13
- package/ds-canonical/fork/consumer/lock.json +14 -15
- package/ds-canonical/fork/consumer/managed-files/46f10bc55cdfdf834d0749eebff42ea0bb8576f361b36d2b6ba1851354c50150.blob +26 -3
- package/ds-canonical/fork/consumer/managed-files/63061e05969667005020166ea2ae7df3c96663f7a928e7dd3eebc1471022df56.blob +61 -5
- package/ds-canonical/fork/consumer/managed-files/93a89f16f6717d1e4e4e27ec20ef916e3355c7aa890ca74cd9b12db30d8a899f.blob +3 -10
- package/ds-canonical/fork/consumer/managed-files/bc44cabad181f766611dab6a8f52d81100afadd50ac59799015e8ba7bf46d9eb.blob +3 -2
- package/ds-canonical/fork/governance.lock +17 -33
- package/ds-canonical/fork/hooks/check_consumer_code_quality.sh +33 -0
- package/ds-canonical/fork/hooks/check_pattern_invariants.sh +39 -0
- package/ds-canonical/fork/hooks/check_sidebar_menu_button_implicit_wrap.sh +6 -4
- package/ds-canonical/fork/launchers/provider-hook-normalization.mjs +5 -11
- package/ds-canonical/fork/launchers/settings-hooks.json +2 -2
- package/ds-canonical/fork/manifest.json +11 -13
- package/ds-canonical/fork/providers/claude/hook-config.json +1 -7
- package/ds-canonical/fork/providers/codex/hook-config.json +1 -1
- package/ds-canonical/fork/references/story-baseline-registry.json +7 -1
- package/ds-canonical/hooks/check_consumer_code_quality.sh +33 -0
- package/ds-canonical/hooks/check_pattern_invariants.sh +39 -0
- package/ds-canonical/hooks/check_sidebar_menu_button_implicit_wrap.sh +6 -4
- package/ds-canonical/hooks/session_start_governance_check.sh +5 -6
- package/ds-canonical/hooks/tests/test_check_consumer_code_quality.sh +23 -1
- package/ds-canonical/hooks/tests/test_check_pattern_invariants.sh +36 -2
- package/ds-canonical/references/audit-coverage-vs-24-checklist.md +1 -1
- package/ds-canonical/references/failure-class-registry.json +151 -144
- package/ds-canonical/references/preflight-gate-baseline.json +16 -12
- package/ds-canonical/references/story-baseline-registry.json +7 -1
- package/ds-canonical/rules/meta-patterns.md +3 -3
- package/ds-canonical/skills/design-system-audit/SKILL.md +1 -1
- package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +1 -1
- package/ds-canonical/templates/dashboard-app.tsx +31 -8
- package/llms-full.txt +6 -1
- package/llms.txt +2 -1
- package/package.json +1 -1
- package/src/components/Accordion/accordion.principles.stories.tsx +16 -26
- package/src/components/Accordion/accordion.stories.tsx +22 -40
- package/src/components/AccountMenu/account-menu.anatomy.stories.tsx +115 -0
- package/src/components/AccountMenu/account-menu.principles.stories.tsx +99 -0
- package/src/components/AccountMenu/account-menu.spec.md +101 -0
- package/src/components/AccountMenu/account-menu.stories.tsx +61 -0
- package/src/components/AccountMenu/account-menu.tsx +170 -0
- package/src/components/AccountMenu/index.ts +5 -0
- package/src/components/AppShell/_demo-helpers.tsx +53 -53
- package/src/components/AppShell/app-shell.spec.md +5 -1
- package/src/components/AppShell/app-shell.tsx +14 -12
- package/src/components/Avatar/avatar.principles.stories.tsx +4 -4
- package/src/components/Avatar/avatar.stories.tsx +20 -5
- package/src/components/Breadcrumb/breadcrumb.spec.md +1 -1
- package/src/components/Button/button.spec.md +1 -1
- package/src/components/Button/button.stories.tsx +21 -9
- package/src/components/Button/button.tsx +5 -4
- package/src/components/Calendar/calendar.anatomy.stories.tsx +19 -17
- package/src/components/Calendar/calendar.principles.stories.tsx +5 -6
- package/src/components/Calendar/calendar.spec.md +25 -29
- package/src/components/Calendar/calendar.stories.tsx +5 -7
- package/src/components/Calendar/calendar.tsx +75 -87
- package/src/components/Carousel/carousel.principles.stories.tsx +5 -1
- package/src/components/Chart/chart.spec.md +1 -1
- package/src/components/Checkbox/checkbox.anatomy.stories.tsx +14 -5
- package/src/components/Chip/chip.spec.md +1 -1
- package/src/components/CircularProgress/circular-progress.anatomy.stories.tsx +7 -6
- package/src/components/CircularProgress/circular-progress.principles.stories.tsx +8 -6
- package/src/components/CircularProgress/circular-progress.stories.tsx +3 -3
- package/src/components/Combobox/combobox.tsx +1 -0
- package/src/components/Command/command.spec.md +1 -0
- package/src/components/Command/command.tsx +4 -1
- package/src/components/DataTable/cell-registry.tsx +3 -7
- package/src/components/DataTable/data-table-filter-group.tsx +44 -27
- package/src/components/DataTable/data-table-filter-labels.ts +100 -0
- package/src/components/DataTable/data-table-filter-panel.tsx +48 -8
- package/src/components/DataTable/data-table-filter-value-picker.tsx +25 -8
- package/src/components/DataTable/data-table.spec.md +18 -1
- package/src/components/DataTable/data-table.stories.tsx +346 -33
- package/src/components/DataTable/data-table.tsx +11 -4
- package/src/components/DataTable/filter-operators.spec.md +2 -2
- package/src/components/DateGrid/date-grid.anatomy.stories.tsx +24 -20
- package/src/components/DateGrid/date-grid.principles.stories.tsx +27 -21
- package/src/components/DateGrid/date-grid.stories.tsx +22 -13
- package/src/components/DatePicker/date-picker.anatomy.stories.tsx +3 -3
- package/src/components/DatePicker/date-picker.spec.md +4 -4
- package/src/components/DatePicker/date-picker.stories.tsx +33 -9
- package/src/components/DatePicker/date-picker.tsx +210 -147
- package/src/components/Dialog/dialog.spec.md +1 -1
- package/src/components/Dialog/dialog.tsx +8 -2
- package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +1 -1
- package/src/components/DropdownMenu/dropdown-menu.spec.md +5 -0
- package/src/components/DropdownMenu/dropdown-menu.stories.tsx +3 -17
- package/src/components/DropdownMenu/dropdown-menu.tsx +58 -16
- package/src/components/Field/field-context.ts +25 -1
- package/src/components/Field/field-wrapper.tsx +7 -1
- package/src/components/Field/field.spec.md +8 -1
- package/src/components/Field/field.tsx +14 -2
- package/src/components/FieldControlGroup/field-control-group.anatomy.stories.tsx +3 -3
- package/src/components/FieldControlGroup/field-control-group.spec.md +2 -3
- package/src/components/FieldControlGroup/field-control-group.stories.tsx +2 -1
- package/src/components/FieldControlGroup/field-control-group.tsx +6 -9
- package/src/components/FileItem/file-item.spec.md +1 -1
- package/src/components/FileItem/file-item.stories.tsx +1 -1
- package/src/components/FileUpload/file-upload.stories.tsx +32 -0
- package/src/components/FileUpload/file-upload.tsx +1 -1
- package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +1 -1
- package/src/components/FileViewer/file-viewer.spec.md +1 -1
- package/src/components/FileViewer/file-viewer.stories.tsx +7 -0
- package/src/components/FileViewer/file-viewer.tsx +23 -5
- package/src/components/InlineEdit/inline-edit.spec.md +12 -0
- package/src/components/InlineEdit/inline-edit.stories.tsx +30 -19
- package/src/components/InlineEdit/inline-edit.tsx +94 -31
- package/src/components/Input/input.anatomy.stories.tsx +28 -12
- package/src/components/Input/input.principles.stories.tsx +23 -18
- package/src/components/Input/input.stories.tsx +9 -1
- package/src/components/Input/input.tsx +7 -2
- package/src/components/LinkInput/link-input.principles.stories.tsx +7 -7
- package/src/components/LinkInput/link-input.spec.md +1 -1
- package/src/components/LinkInput/link-input.tsx +5 -1
- package/src/components/Menu/menu-item.anatomy.stories.tsx +1 -1
- package/src/components/Menu/menu-item.tsx +9 -2
- package/src/components/NumberInput/number-input.anatomy.stories.tsx +44 -10
- package/src/components/NumberInput/number-input.principles.stories.tsx +20 -14
- package/src/components/NumberInput/number-input.spec.md +1 -1
- package/src/components/NumberInput/number-input.stories.tsx +12 -5
- package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +7 -0
- package/src/components/PeoplePicker/people-picker.spec.md +3 -1
- package/src/components/PeoplePicker/people-picker.tsx +5 -0
- package/src/components/Popover/popover.tsx +7 -2
- package/src/components/ProgressBar/progress-bar.principles.stories.tsx +14 -12
- package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +5 -1
- package/src/components/RadioGroup/radio-group.spec.md +1 -1
- package/src/components/Rating/rating.principles.stories.tsx +2 -2
- package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.spec.md +3 -3
- package/src/components/ScrollArea/scroll-area.tsx +5 -4
- package/src/components/Select/select.principles.stories.tsx +4 -3
- package/src/components/Select/select.stories.tsx +7 -6
- package/src/components/Select/select.tsx +61 -29
- package/src/components/SelectMenu/select-menu.spec.md +1 -1
- package/src/components/SelectMenu/select-menu.tsx +4 -1
- package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +19 -8
- package/src/components/SelectionControl/selection-item.stories.tsx +7 -7
- package/src/components/Sheet/sheet.spec.md +1 -1
- package/src/components/Sidebar/sidebar.spec.md +3 -2
- package/src/components/Sidebar/sidebar.stories.tsx +67 -37
- package/src/components/Sidebar/sidebar.tsx +3 -3
- package/src/components/Slider/slider.principles.stories.tsx +9 -9
- package/src/components/Steps/steps.spec.md +1 -0
- package/src/components/Steps/steps.stories.tsx +18 -1
- package/src/components/Switch/switch.anatomy.stories.tsx +11 -11
- package/src/components/Switch/switch.principles.stories.tsx +11 -8
- package/src/components/Tabs/tabs.principles.stories.tsx +24 -0
- package/src/components/Tabs/tabs.spec.md +6 -4
- package/src/components/Tabs/tabs.tsx +11 -0
- package/src/components/Tag/tag.anatomy.stories.tsx +5 -2
- package/src/components/Tag/tag.spec.md +4 -3
- package/src/components/Tag/tag.stories.tsx +19 -0
- package/src/components/Tag/tag.tsx +14 -5
- package/src/components/Textarea/textarea.anatomy.stories.tsx +15 -9
- package/src/components/Textarea/textarea.principles.stories.tsx +21 -5
- package/src/components/Textarea/textarea.spec.md +10 -1
- package/src/components/Textarea/textarea.tsx +38 -5
- package/src/components/TimePicker/time-picker.tsx +11 -5
- package/src/components/Tooltip/tooltip.spec.md +10 -0
- package/src/components/Tooltip/tooltip.tsx +34 -3
- package/src/components/TreeView/tree-view.spec.md +3 -3
- package/src/components/TreeView/tree-view.stories.tsx +68 -9
- package/src/components/TreeView/tree-view.tsx +7 -2
- package/src/index.ts +9 -1
- package/src/patterns/element-anatomy/inline-action.spec.md +12 -8
- package/src/patterns/element-anatomy/item-anatomy.spec.md +8 -1
- package/src/patterns/element-anatomy/item-anatomy.stories.tsx +24 -5
- package/src/patterns/element-anatomy/item-anatomy.tsx +120 -35
- package/src/patterns/element-anatomy/truncated-text.spec.md +13 -2
- package/src/patterns/header-canonical/chrome-header.tsx +14 -5
- package/src/patterns/header-canonical/header-canonical.spec.md +10 -7
- package/src/patterns/header-canonical/header-canonical.stories.tsx +77 -0
- package/src/patterns/horizontal-overflow/horizontal-overflow.stories.tsx +2 -0
- package/src/patterns/overlay-surface/overlay-surface.spec.md +1 -0
- package/src/patterns/overlay-surface/overlay-surface.stories.tsx +17 -1
- package/src/patterns/overlay-surface/overlay-surface.tsx +8 -4
- package/src/patterns/resize-handle/resize-handle.spec.md +11 -13
- package/src/patterns/resize-handle/resize-handle.stories.tsx +1 -3
- package/src/patterns/resize-handle/resize-handle.tsx +13 -18
- package/src/tokens/color/semantic.css +3 -1
- package/src/tokens/layoutSpace/layoutSpace.stories.tsx +1 -1
- package/src/tokens/opacity/opacity.stories.tsx +1 -1
- package/src/tokens/radius/radius.stories.tsx +3 -3
- package/src/tokens/uiSize/uiSize.css +11 -0
- package/src/tokens/uiSize/uiSize.spec.md +1 -0
- package/ds-canonical/commands/gov-status.md +0 -9
- package/ds-canonical/fork/consumer/managed-files/77e06f62670dfee01e9b5e8b0f8dad085ce711d75538c1fb0679d9818acaca82.blob +0 -424
- package/ds-canonical/fork/preamble.md +0 -500
- package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +0 -8
- package/ds-canonical/hooks/tests/test_check_post_main_ssot_propagate.sh +0 -37
|
@@ -46,10 +46,10 @@ export const TaskTitle: Story = {
|
|
|
46
46
|
/* ── 真實業務:詳情面板多個 metadata 欄位逐欄就地編輯 ── */
|
|
47
47
|
function MetaRow({ label, value, placeholder }: { label: string; value: string; placeholder?: string }) {
|
|
48
48
|
return (
|
|
49
|
-
<
|
|
50
|
-
<
|
|
49
|
+
<Field orientation="horizontal" labelWidth="128px">
|
|
50
|
+
<FieldLabel>{label}</FieldLabel>
|
|
51
51
|
<StatefulInlineEdit value={value} label={label} placeholder={placeholder} size="sm" />
|
|
52
|
-
</
|
|
52
|
+
</Field>
|
|
53
53
|
)
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -108,11 +108,11 @@ export const States: Story = {
|
|
|
108
108
|
),
|
|
109
109
|
play: async ({ canvasElement }) => {
|
|
110
110
|
const { userEvent } = await import('@storybook/test')
|
|
111
|
-
//
|
|
111
|
+
// 只標記第二列;真實 CSS :hover 由 visual-audit 的 Playwright pointer 建立。
|
|
112
112
|
const hoverBtn = canvasElement
|
|
113
113
|
.querySelector('[data-testid="inline-edit-hover-row"]')
|
|
114
114
|
?.querySelector('button')
|
|
115
|
-
if (hoverBtn)
|
|
115
|
+
if (hoverBtn) hoverBtn.setAttribute('data-visual-hover-target', '')
|
|
116
116
|
// edit 態:點擊第三列 → 切成真正的 Input 控件
|
|
117
117
|
const editBtn = canvasElement
|
|
118
118
|
.querySelector('[data-testid="inline-edit-edit-row"]')
|
|
@@ -139,16 +139,21 @@ export const SelectTagField: Story = {
|
|
|
139
139
|
render: () => {
|
|
140
140
|
const [status, setStatus] = React.useState('in-progress')
|
|
141
141
|
return (
|
|
142
|
-
<
|
|
143
|
-
|
|
142
|
+
<Field
|
|
143
|
+
orientation="horizontal"
|
|
144
|
+
labelWidth="128px"
|
|
145
|
+
className="w-[440px] rounded-lg border border-border bg-surface p-[var(--layout-space-loose)]"
|
|
146
|
+
>
|
|
147
|
+
<FieldLabel>Status</FieldLabel>
|
|
144
148
|
<InlineEdit<string>
|
|
145
149
|
value={status}
|
|
146
150
|
onCommit={setStatus}
|
|
147
151
|
label="status"
|
|
148
152
|
size="sm"
|
|
149
153
|
// view:Select 的 view mode 輸出 Tag(格式化 SSOT 住在 Select,InlineEdit 不自刻)。
|
|
150
|
-
// Tag
|
|
151
|
-
//
|
|
154
|
+
// Tag **盒子左緣**對齊同欄其他純文字值(horizontal Field:值落內容欄左緣 + field-px,
|
|
155
|
+
// 不用 -mx),tag 文字被 pill 內距自然縮排 —— 對齊 Meegle 實測
|
|
156
|
+
// (PIL 量測 2026-07-10:Meegle pill box=值左緣、text 縮排 ~8px@1x)。**不做 text outdent**。
|
|
152
157
|
renderRead={(v) => (
|
|
153
158
|
<Select mode="view" display="tag" size="sm" value={v} options={STATUS_OPTIONS} />
|
|
154
159
|
)}
|
|
@@ -168,7 +173,7 @@ export const SelectTagField: Story = {
|
|
|
168
173
|
/>
|
|
169
174
|
)}
|
|
170
175
|
/>
|
|
171
|
-
</
|
|
176
|
+
</Field>
|
|
172
177
|
)
|
|
173
178
|
},
|
|
174
179
|
}
|
|
@@ -183,14 +188,16 @@ export const MultilineDescription: Story = {
|
|
|
183
188
|
)
|
|
184
189
|
return (
|
|
185
190
|
<div className="w-[560px] rounded-lg border border-border bg-surface p-[var(--layout-space-loose)]">
|
|
186
|
-
<
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
191
|
+
<Field orientation="vertical">
|
|
192
|
+
<FieldLabel>DESCRIPTION</FieldLabel>
|
|
193
|
+
<InlineEdit
|
|
194
|
+
multiline
|
|
195
|
+
value={desc}
|
|
196
|
+
onCommit={setDesc}
|
|
197
|
+
label="description"
|
|
198
|
+
placeholder="Add a description…"
|
|
199
|
+
/>
|
|
200
|
+
</Field>
|
|
194
201
|
</div>
|
|
195
202
|
)
|
|
196
203
|
},
|
|
@@ -201,6 +208,10 @@ export const MultilineDescription: Story = {
|
|
|
201
208
|
export const VerticalFieldForm: Story = {
|
|
202
209
|
name: '詳情面板:垂直欄位就地編輯',
|
|
203
210
|
render: () => {
|
|
211
|
+
// Assignee 的 state 提到 story 本體(不包 wrapper 元件)—— InlineEdit 必須是 <Field> 的**直接子**,
|
|
212
|
+
// Field 才讀得到靜態 fieldPreferredSize='sm';包一層 wrapper 會遮蔽它 → 控件槽退回 md(32px),
|
|
213
|
+
// 與同表其他 sm(28px)列不齊(對照 inline-edit.anatomy.stories.tsx CascadeDemo)。
|
|
214
|
+
const [assignee, setAssignee] = React.useState('Alex Rivera')
|
|
204
215
|
const [status, setStatus] = React.useState('in-progress')
|
|
205
216
|
const [desc, setDesc] = React.useState('Safari 上點擊結帳時,e2e 測試會間歇性失敗。')
|
|
206
217
|
return (
|
|
@@ -210,7 +221,7 @@ export const VerticalFieldForm: Story = {
|
|
|
210
221
|
>
|
|
211
222
|
<Field orientation="vertical">
|
|
212
223
|
<FieldLabel>Assignee</FieldLabel>
|
|
213
|
-
<
|
|
224
|
+
<InlineEdit value={assignee} onCommit={setAssignee} label="assignee" />
|
|
214
225
|
</Field>
|
|
215
226
|
<Field orientation="vertical">
|
|
216
227
|
<FieldLabel>Status</FieldLabel>
|
|
@@ -26,9 +26,11 @@ import { cn } from '@/lib/utils'
|
|
|
26
26
|
import { Input } from '@/design-system/components/Input/input'
|
|
27
27
|
import { Textarea } from '@/design-system/components/Textarea/textarea'
|
|
28
28
|
import type { FieldSize } from '@/design-system/components/Field/field-context'
|
|
29
|
-
import { useFieldContext, useResolvedFieldSize, FieldSurfaceProvider } from '@/design-system/components/Field/field-context'
|
|
29
|
+
import { useFieldContext, useResolvedFieldSize, useRegisterFieldPreferredSize, FieldSurfaceProvider } from '@/design-system/components/Field/field-context'
|
|
30
30
|
import { fieldDisplayTextClass, fieldViewGeometry } from '@/design-system/components/Field/field-wrapper'
|
|
31
31
|
import { makeEditSettleKeyHandler } from '@/design-system/components/Field/field-edit-keys'
|
|
32
|
+
import { useTruncated } from '@/design-system/hooks/use-truncated'
|
|
33
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '@/design-system/components/Tooltip/tooltip'
|
|
32
34
|
|
|
33
35
|
// ── Types ───────────────────────────────────────────────────────────────────
|
|
34
36
|
|
|
@@ -71,6 +73,11 @@ export interface InlineEditProps<T = string> {
|
|
|
71
73
|
renderRead?: (value: T) => React.ReactNode
|
|
72
74
|
/** 多行欄位(Jira description 類):view 態換行 + 預設 edit 態用 `<Textarea>`,Enter=換行、Cmd/Ctrl+Enter 或 blur 提交 */
|
|
73
75
|
multiline?: boolean
|
|
76
|
+
/**
|
|
77
|
+
* 多行 edit 態的高度上限(行數)。預設無上限 —— 高度隨內容,與 view 態同源(= DataTable cell 語意)。
|
|
78
|
+
* 設定後超過行數改為捲動。僅在 `multiline` 時有意義。
|
|
79
|
+
*/
|
|
80
|
+
editMaxRows?: number
|
|
74
81
|
/**
|
|
75
82
|
* 是否可編輯(預設 true)。false → view 態無 hover 入口、無藍框、不可點進 edit(對齊世界級就地編輯
|
|
76
83
|
* **無 disabled 態**,鎖定 = 純 view 無入口、**不灰化**;cite 見檔頭 MUI X / AG Grid URL)。
|
|
@@ -113,6 +120,7 @@ function InlineEditImpl<T = string>(
|
|
|
113
120
|
renderEdit,
|
|
114
121
|
renderRead,
|
|
115
122
|
multiline = false,
|
|
123
|
+
editMaxRows,
|
|
116
124
|
editable = true,
|
|
117
125
|
label,
|
|
118
126
|
placeholder,
|
|
@@ -123,9 +131,11 @@ function InlineEditImpl<T = string>(
|
|
|
123
131
|
}: InlineEditProps<T>,
|
|
124
132
|
ref: React.Ref<HTMLDivElement>,
|
|
125
133
|
) {
|
|
126
|
-
// size:接 fieldCtx.size cascade,
|
|
127
|
-
//
|
|
134
|
+
// size:接 fieldCtx.size cascade,fallback sm。Field 只有在 consumer 顯式指定或已解析出偏好時
|
|
135
|
+
// 才壓過此 fallback(sizeExplicit),因此包一層 wrapper 也不會靜默退回 md。
|
|
128
136
|
const size = useResolvedFieldSize(sizeProp, 'sm')
|
|
137
|
+
// 同時向外層 Field 註冊偏好,讓 Field 的控件槽高度跟上(靜態 fieldPreferredSize 被 wrapper 遮蔽時的補位)。
|
|
138
|
+
useRegisterFieldPreferredSize('sm')
|
|
129
139
|
// orientation:vertical(值貼 label 左緣 → 用 -mx)/ horizontal(值落內容欄左緣+field-px = 對齊 sibling 控件 → 不用 -mx)。
|
|
130
140
|
const fieldCtx = useFieldContext()
|
|
131
141
|
const orientation = fieldCtx?.orientation ?? 'vertical'
|
|
@@ -238,6 +248,35 @@ function InlineEditImpl<T = string>(
|
|
|
238
248
|
value == null || (value as unknown) === '' || (Array.isArray(value) && value.length === 0)
|
|
239
249
|
const Tag = as as React.ElementType
|
|
240
250
|
|
|
251
|
+
// 單行純值 view 的截斷 tooltip:量測內層值 span,但 **trigger = 整個 view 容器**。
|
|
252
|
+
// 不能用 <TruncatedText>(trigger=值 span 本身):view 上疊著透明 Pressable(click-to-edit
|
|
253
|
+
// 入口,absolute inset-0),所有 hover 都被按鈕吞掉,值 span 永遠收不到 pointerenter →
|
|
254
|
+
// tooltip 永不開(2026-07-28 user 實測抓到)。truncated-text.spec 對「trigger 需自控」場景
|
|
255
|
+
// 的指定解 = 直接消費 public useTruncated 自組;DataTable cell 無疊層所以能用 primitive。
|
|
256
|
+
const singleLinePlainView = !renderRead && !multiline
|
|
257
|
+
const { ref: truncationRef, isTruncated } = useTruncated<HTMLSpanElement>({
|
|
258
|
+
deps: [singleLinePlainView && !isEmpty ? String(value) : ''],
|
|
259
|
+
})
|
|
260
|
+
const plainTruncationActive = singleLinePlainView && !isEmpty && isTruncated
|
|
261
|
+
|
|
262
|
+
// 委派 view(renderRead)同規則(tooltip.spec.md「截斷文字 → tooltip,截斷才顯」):委派控件
|
|
263
|
+
// 自帶的截斷 tooltip(Input <TruncatedText> / Tag)trigger 在內層,editable 時同樣被透明
|
|
264
|
+
// Pressable 吞掉 hover → 永不開,故外層容器 Tooltip 一併服務委派路徑。量測 = use-truncated
|
|
265
|
+
// options.measure 自訂策略(檔頭明載 Tag「observe root、量測內層」即同款開口):走訪 wrapper
|
|
266
|
+
// 子樹,任一 `.truncate` 後代溢出即視為截斷(委派控件截斷元素皆帶 truncate class:Tag 內層
|
|
267
|
+
// span / TruncatedText span / Select view span)。editable=false 不渲染 Pressable → 控件自身
|
|
268
|
+
// tooltip 可達,外層讓位避免雙重顯示。
|
|
269
|
+
const delegatedView = !!renderRead
|
|
270
|
+
const { ref: delegatedViewRef, isTruncated: delegatedTruncated } = useTruncated<HTMLDivElement>({
|
|
271
|
+
measure: (el) =>
|
|
272
|
+
Array.from(el.querySelectorAll<HTMLElement>('.truncate')).some(
|
|
273
|
+
(node) => node.scrollWidth > node.clientWidth,
|
|
274
|
+
),
|
|
275
|
+
deps: [delegatedView && editable && !isEmpty ? value : null],
|
|
276
|
+
})
|
|
277
|
+
const delegatedTruncationActive = delegatedView && editable && !isEmpty && delegatedTruncated
|
|
278
|
+
const truncationTooltipActive = plainTruncationActive || delegatedTruncationActive
|
|
279
|
+
|
|
241
280
|
// 對齊盒(orientation-aware,Model A;2026-07-17 移除 fieldCtx gate — root cause 修):
|
|
242
281
|
// vertical(預設,含 standalone)→ `-mx-field-px` + `w-calc` 把整塊拉到欄左緣(值貼 label 左緣;
|
|
243
282
|
// view 委派控件在 `surface='inline-edit'` 下 px 統一 field-px → 被 -mx 精準抵消 → 落 x=0 對齊 label)。
|
|
@@ -269,6 +308,9 @@ function InlineEditImpl<T = string>(
|
|
|
269
308
|
<Textarea
|
|
270
309
|
autoFocus
|
|
271
310
|
mode="edit"
|
|
311
|
+
// view 態高度由內容決定,edit 態必須同源,否則 read↔edit 之間跳高(DataTable cell 早就是
|
|
312
|
+
// 內容驅動,standalone InlineEdit 原本卡在 rows=3)。consumer 要上限就傳 editMaxRows。
|
|
313
|
+
autoSize={editMaxRows === undefined ? true : { maxRows: editMaxRows }}
|
|
272
314
|
value={draft as unknown as string}
|
|
273
315
|
size={size}
|
|
274
316
|
aria-label={label}
|
|
@@ -310,7 +352,8 @@ function InlineEditImpl<T = string>(
|
|
|
310
352
|
// surface='inline-edit' 保留為 per-control 逃生訊號(若某控件 bare view 有內部 inset 破壞對齊,該控件可
|
|
311
353
|
// 讀 surface 自抑;目前全控件 bare view=0px 不需)。
|
|
312
354
|
<FieldSurfaceProvider surface="inline-edit">
|
|
313
|
-
|
|
355
|
+
{/* delegatedViewRef = 委派路徑截斷量測根(walk `.truncate` 後代;見上方 useTruncated 註解) */}
|
|
356
|
+
<div ref={delegatedViewRef} className={fieldViewGeometry(size, multiline)}>{renderRead(value)}</div>
|
|
314
357
|
</FieldSurfaceProvider>
|
|
315
358
|
) : (
|
|
316
359
|
<Tag
|
|
@@ -327,37 +370,57 @@ function InlineEditImpl<T = string>(
|
|
|
327
370
|
isEmpty && 'text-fg-muted',
|
|
328
371
|
)}
|
|
329
372
|
>
|
|
330
|
-
{isEmpty
|
|
373
|
+
{isEmpty || multiline
|
|
374
|
+
? (isEmpty ? placeholder : String(value))
|
|
375
|
+
// 單行 view 恆一行:溢出改 ellipsis(tooltip.spec.md「截斷文字 → tooltip」);
|
|
376
|
+
// tooltip 掛在外層 view 容器(見 truncationRef 註解——隱形 Pressable 吞 hover)。
|
|
377
|
+
: <span ref={truncationRef} className="truncate min-w-0">{String(value)}</span>}
|
|
331
378
|
</Tag>
|
|
332
379
|
)
|
|
333
380
|
|
|
334
381
|
return (
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
382
|
+
// 截斷 tooltip:trigger = 整個 view 容器(隱形 Pressable 吞掉內層 hover,故不能掛在值 span);
|
|
383
|
+
// 純值路徑量內層值 span、委派路徑量 wrapper 子樹(見上方兩段 useTruncated 註解);
|
|
384
|
+
// 恆 wrap 保 DOM 生命週期穩定(truncated-text 檔頭 always-wrap 教訓),未截斷時 open={false} 靜默。
|
|
385
|
+
<Tooltip open={truncationTooltipActive ? undefined : false}>
|
|
386
|
+
<TooltipTrigger asChild>
|
|
387
|
+
<div
|
|
388
|
+
ref={ref}
|
|
389
|
+
data-editing={false}
|
|
390
|
+
data-editable={editable || undefined}
|
|
391
|
+
className={cn(
|
|
392
|
+
'relative flex min-w-0 rounded-md border border-transparent transition-colors duration-150',
|
|
393
|
+
alignBleed,
|
|
394
|
+
// editable 才有 hover 底色 + 鍵盤 focus 藍框(Field focus 語言,非 Button ring);
|
|
395
|
+
// editable=false = 純 view 鎖定,無入口、無藍框、不灰化。
|
|
396
|
+
editable && 'hover:bg-neutral-hover [&:has(button:focus-visible)]:border-primary',
|
|
397
|
+
className,
|
|
398
|
+
)}
|
|
399
|
+
>
|
|
400
|
+
{viewNode}
|
|
401
|
+
{/* 隱形 Pressable(僅 editable):提供 click + 鍵盤 Tab focus + Enter/Space 進 edit;透明疊於內容上。
|
|
402
|
+
hover 底色 + focus 藍框由外層 div 承載(Field focus 語言),故本 button 只需 outline-none 消瀏覽器預設外框。 */}
|
|
403
|
+
{editable && (
|
|
404
|
+
<button
|
|
405
|
+
ref={readButtonRef}
|
|
406
|
+
type="button"
|
|
407
|
+
aria-label={label ? `編輯 ${label}` : '編輯'}
|
|
408
|
+
onClick={enterEdit}
|
|
409
|
+
className="absolute inset-0 cursor-text rounded-md focus-visible:outline-none"
|
|
410
|
+
/>
|
|
411
|
+
)}
|
|
412
|
+
</div>
|
|
413
|
+
</TooltipTrigger>
|
|
414
|
+
<TooltipContent>
|
|
415
|
+
{delegatedTruncationActive
|
|
416
|
+
? // 委派路徑內容 = wrapper 子樹當下 DOM 的 textContent(完整格式化文字;
|
|
417
|
+
// 非 string T 不可 String(value) —— 如 string[] / PersonValue)
|
|
418
|
+
delegatedViewRef.current?.textContent
|
|
419
|
+
: plainTruncationActive
|
|
420
|
+
? String(value)
|
|
421
|
+
: null}
|
|
422
|
+
</TooltipContent>
|
|
423
|
+
</Tooltip>
|
|
361
424
|
)
|
|
362
425
|
}
|
|
363
426
|
|
|
@@ -214,9 +214,9 @@ export const Overview = {
|
|
|
214
214
|
{/* view + readonly 讀 controlled value:兩者「空值」皆走 <span> 顯 '-'(input.tsx:121),
|
|
215
215
|
demo 要展示有值狀態故傳 value;edit / disabled 用 defaultValue 保持 uncontrolled */}
|
|
216
216
|
{(m === 'view' || m === 'readonly') ? (
|
|
217
|
-
<Input mode={m} value="Wireless Bluetooth Headphones" size="md" />
|
|
217
|
+
<Input mode={m} value="Wireless Bluetooth Headphones" size="md" aria-label={`商品名稱(${m} 模式)`} />
|
|
218
218
|
) : (
|
|
219
|
-
<Input mode={m} defaultValue="Wireless Bluetooth Headphones" size="md" />
|
|
219
|
+
<Input mode={m} defaultValue="Wireless Bluetooth Headphones" size="md" aria-label={`商品名稱(${m} 模式)`} />
|
|
220
220
|
)}
|
|
221
221
|
</div>
|
|
222
222
|
<span className="text-caption text-fg-secondary">{MODE_DESC[m]}</span>
|
|
@@ -227,7 +227,7 @@ export const Overview = {
|
|
|
227
227
|
<span className="text-[11px] text-fg-muted font-medium">+ error prop(僅 edit 模式生效)</span>
|
|
228
228
|
<div className="flex items-center gap-4">
|
|
229
229
|
<div className="w-64 shrink-0">
|
|
230
|
-
<Input error defaultValue="invalid-email@" size="md" />
|
|
230
|
+
<Input error defaultValue="invalid-email@" size="md" aria-label="Email(錯誤狀態)" />
|
|
231
231
|
</div>
|
|
232
232
|
<span className="text-caption text-fg-secondary">border-error — 錯誤訊息由 Form help text 提供</span>
|
|
233
233
|
</div>
|
|
@@ -237,7 +237,7 @@ export const Overview = {
|
|
|
237
237
|
{/* Props table */}
|
|
238
238
|
<div className="flex flex-col gap-3">
|
|
239
239
|
<H3>Props</H3>
|
|
240
|
-
<div className="overflow-x-auto">
|
|
240
|
+
<div className="overflow-x-auto" tabIndex={0} aria-label="Input props 表格">
|
|
241
241
|
<table className="text-caption border-collapse">
|
|
242
242
|
<thead><tr><Th>Prop</Th><Th>Type</Th><Th>Default</Th><Th>說明</Th></tr></thead>
|
|
243
243
|
<tbody>
|
|
@@ -332,6 +332,7 @@ const InspectorInner = () => {
|
|
|
332
332
|
mode={mode}
|
|
333
333
|
size={size}
|
|
334
334
|
error={error}
|
|
335
|
+
aria-label="商品名稱(互動檢閱器)"
|
|
335
336
|
startIcon={hasStartIcon ? Search : undefined}
|
|
336
337
|
endAction={hasEndAction && isEdit ? { icon: X, label: '清除', onClick: () => {} } : undefined}
|
|
337
338
|
// view mode 讀 value 渲染 <span>;其餘 mode 用 defaultValue 保持 uncontrolled
|
|
@@ -505,6 +506,7 @@ export const ColorMatrix = {
|
|
|
505
506
|
error={st === 'error'}
|
|
506
507
|
defaultValue="Wireless Bluetooth Headphones"
|
|
507
508
|
size="md"
|
|
509
|
+
aria-label={`商品名稱(${st} 狀態)`}
|
|
508
510
|
/>
|
|
509
511
|
</td>
|
|
510
512
|
<td className="p-3 border-b border-divider align-top">
|
|
@@ -529,7 +531,12 @@ export const ColorMatrix = {
|
|
|
529
531
|
<tr key={m}>
|
|
530
532
|
<td className="p-3 border-b border-divider font-mono text-caption font-medium align-top w-24">{m}</td>
|
|
531
533
|
<td className="p-3 border-b border-divider align-top min-w-[240px]">
|
|
532
|
-
<Input
|
|
534
|
+
<Input
|
|
535
|
+
mode={m}
|
|
536
|
+
defaultValue="Wireless Bluetooth Headphones"
|
|
537
|
+
size="md"
|
|
538
|
+
aria-label={`商品名稱(${m} 模式)`}
|
|
539
|
+
/>
|
|
533
540
|
</td>
|
|
534
541
|
<td className="p-3 border-b border-divider align-top">
|
|
535
542
|
<TokenAnnotation colors={COLOR_MAP[m].default!} />
|
|
@@ -634,7 +641,13 @@ export const SizeMatrix = {
|
|
|
634
641
|
<Td mono>{sz}</Td>
|
|
635
642
|
{MODES.map((m) => (
|
|
636
643
|
<td key={m} className="p-2 border-b border-divider">
|
|
637
|
-
<Input
|
|
644
|
+
<Input
|
|
645
|
+
mode={m}
|
|
646
|
+
size={sz}
|
|
647
|
+
defaultValue="Headphones"
|
|
648
|
+
aria-label={`商品名稱(${sz} 尺寸、${m} 模式)`}
|
|
649
|
+
className="w-48"
|
|
650
|
+
/>
|
|
638
651
|
</td>
|
|
639
652
|
))}
|
|
640
653
|
<td className="p-2 border-b border-divider">
|
|
@@ -643,6 +656,7 @@ export const SizeMatrix = {
|
|
|
643
656
|
startIcon={Search}
|
|
644
657
|
endAction={{ icon: X, label: '清除', onClick: () => {} }}
|
|
645
658
|
defaultValue="Headphones"
|
|
659
|
+
aria-label={`商品搜尋(${sz} 尺寸)`}
|
|
646
660
|
className="w-48"
|
|
647
661
|
/>
|
|
648
662
|
</td>
|
|
@@ -678,8 +692,8 @@ export const StateBehavior = {
|
|
|
678
692
|
<span className="text-caption font-medium text-fg-secondary">Focus — border-primary(1px),無 ring(readonly 例外:ring-2 ring-ring focus 指示,WCAG 2.4.7)</span>
|
|
679
693
|
<div className="flex flex-col gap-2 max-w-sm">
|
|
680
694
|
<span className="text-[11px] text-fg-muted">文字輸入永遠 focus-visible(瀏覽器規範),click 和 Tab 觸發相同效果。點擊下方 input 或用 Tab 切換觀察。</span>
|
|
681
|
-
<Input placeholder="點擊或 Tab 觀察 focus 邊框" />
|
|
682
|
-
<Input placeholder="第二個,測試 Tab 切換" />
|
|
695
|
+
<Input placeholder="點擊或 Tab 觀察 focus 邊框" aria-label="第一個焦點測試欄位" />
|
|
696
|
+
<Input placeholder="第二個,測試 Tab 切換" aria-label="第二個焦點測試欄位" />
|
|
683
697
|
</div>
|
|
684
698
|
</div>
|
|
685
699
|
|
|
@@ -689,12 +703,12 @@ export const StateBehavior = {
|
|
|
689
703
|
<div className="flex flex-col gap-3 max-w-sm">
|
|
690
704
|
<div>
|
|
691
705
|
<span className="text-[11px] text-fg-muted block mb-1">border-error,hover 時 border-error-hover</span>
|
|
692
|
-
<Input error defaultValue="invalid-email@" />
|
|
706
|
+
<Input error defaultValue="invalid-email@" aria-label="Email(錯誤狀態)" />
|
|
693
707
|
<p className="text-caption text-error mt-1">請輸入有效的 email 地址</p>
|
|
694
708
|
</div>
|
|
695
709
|
<div>
|
|
696
710
|
<span className="text-[11px] text-fg-muted block mb-1">focus 時 border-error(不切回 primary)</span>
|
|
697
|
-
<Input error defaultValue="bad-url" startIcon={Search} />
|
|
711
|
+
<Input error defaultValue="bad-url" startIcon={Search} aria-label="網址(錯誤焦點狀態)" />
|
|
698
712
|
</div>
|
|
699
713
|
</div>
|
|
700
714
|
</div>
|
|
@@ -708,6 +722,7 @@ export const StateBehavior = {
|
|
|
708
722
|
startIcon={Search}
|
|
709
723
|
value={query}
|
|
710
724
|
onChange={(e) => setQuery(e.target.value)}
|
|
725
|
+
aria-label="搜尋商品"
|
|
711
726
|
endAction={query ? { icon: X, label: '清除搜尋', onClick: () => setQuery('') } : undefined}
|
|
712
727
|
/>
|
|
713
728
|
</div>
|
|
@@ -722,6 +737,7 @@ export const StateBehavior = {
|
|
|
722
737
|
<Input
|
|
723
738
|
type={showPwd ? 'text' : 'password'}
|
|
724
739
|
defaultValue="my-secret-123"
|
|
740
|
+
aria-label="密碼"
|
|
725
741
|
endAction={{
|
|
726
742
|
icon: showPwd ? EyeOff : Eye,
|
|
727
743
|
label: showPwd ? '隱藏密碼' : '顯示密碼',
|
|
@@ -737,10 +753,10 @@ export const StateBehavior = {
|
|
|
737
753
|
<span className="text-caption font-medium text-fg-secondary">Disabled — endAction 不渲染,停用原因由外部承擔</span>
|
|
738
754
|
<div className="flex flex-col gap-3 max-w-sm">
|
|
739
755
|
<div className="flex items-center gap-3">
|
|
740
|
-
<Input mode="edit" startIcon={Mail} defaultValue="alice@example.com"
|
|
756
|
+
<Input mode="edit" startIcon={Mail} defaultValue="alice@example.com" aria-label="Email(可編輯)"
|
|
741
757
|
endAction={{ icon: X, label: '清除', onClick: () => {} }} className="w-56" />
|
|
742
758
|
<span className="text-fg-muted text-caption">→</span>
|
|
743
|
-
<Input mode="disabled" startIcon={Mail} defaultValue="alice@example.com"
|
|
759
|
+
<Input mode="disabled" startIcon={Mail} defaultValue="alice@example.com" aria-label="Email(停用)"
|
|
744
760
|
endAction={{ icon: X, label: '清除', onClick: () => {} }} className="w-56" />
|
|
745
761
|
</div>
|
|
746
762
|
<span className="text-[11px] text-fg-muted">左:edit(有 endAction)→ 右:disabled(endAction 消失,icon 變 fg-disabled)</span>
|
|
@@ -73,11 +73,11 @@ export const UsageGuidance: Story = {
|
|
|
73
73
|
title="❌ 不用 Input 顯示需要格式化的資料 → NumberInput / DatePicker"
|
|
74
74
|
note="數字、日期、金額都需要格式化(千分位、時區、幣別符號)。每種資料類型有專屬元件,擁有該類型的格式化邏輯(唯一真實來源),同時服務 Form 和 DataTable"
|
|
75
75
|
>
|
|
76
|
-
<Input defaultValue="1234567" placeholder="金額" />
|
|
76
|
+
<Input defaultValue="1234567" placeholder="金額" aria-label="金額(不建議用 Input)" />
|
|
77
77
|
<Label warn>↑ 金額 → 用 NumberInput + currency mode(自動 $1,234,567)</Label>
|
|
78
|
-
<Input defaultValue="2026-04-18" placeholder="日期" />
|
|
78
|
+
<Input defaultValue="2026-04-18" placeholder="日期" aria-label="日期(不建議用 Input)" />
|
|
79
79
|
<Label warn>↑ 日期 → 用 DatePicker(in-locale 顯示、避免時區誤判)</Label>
|
|
80
|
-
<Input defaultValue="1234567" placeholder="數量" />
|
|
80
|
+
<Input defaultValue="1234567" placeholder="數量" aria-label="數量(不建議用 Input)" />
|
|
81
81
|
<Label warn>↑ 數字 → 用 NumberInput(千分位、min/max、step)</Label>
|
|
82
82
|
</Rule>
|
|
83
83
|
|
|
@@ -85,9 +85,9 @@ export const UsageGuidance: Story = {
|
|
|
85
85
|
title="✅ Input 只用於純文字——沒有格式化需求的 value"
|
|
86
86
|
note="姓名、URL、搜尋字串、email。格式化邏輯是 identity(value → value)"
|
|
87
87
|
>
|
|
88
|
-
<Input placeholder="姓名" defaultValue="Ada Chen" />
|
|
89
|
-
<Input startIcon={Globe} defaultValue="https://example.com" />
|
|
90
|
-
<Input startIcon={Search} placeholder="搜尋..." />
|
|
88
|
+
<Input placeholder="姓名" defaultValue="Ada Chen" aria-label="姓名" />
|
|
89
|
+
<Input startIcon={Globe} defaultValue="https://example.com" aria-label="網址" />
|
|
90
|
+
<Input startIcon={Search} placeholder="搜尋..." aria-label="搜尋" />
|
|
91
91
|
</Rule>
|
|
92
92
|
</Section>
|
|
93
93
|
</div>
|
|
@@ -102,14 +102,14 @@ export const ModeRule: Story = {
|
|
|
102
102
|
title="edit — 表單中可編輯的欄位(預設)"
|
|
103
103
|
note="Focus 時邊框變 primary,使用者正在輸入或修改"
|
|
104
104
|
>
|
|
105
|
-
<Input defaultValue="Wireless Bluetooth Headphones" />
|
|
105
|
+
<Input defaultValue="Wireless Bluetooth Headphones" aria-label="商品名稱" />
|
|
106
106
|
</Rule>
|
|
107
107
|
|
|
108
108
|
<Rule
|
|
109
109
|
title="readonly — 表單中顯示但當下不可編輯"
|
|
110
110
|
note="同 edit 高度(neutral-2 底色、無邊框),確保與其他欄位並排對齊。常見場景:viewing mode、送出後鎖定但仍在表單脈絡"
|
|
111
111
|
>
|
|
112
|
-
<Input mode="readonly" value="alice@example.com" />
|
|
112
|
+
<Input mode="readonly" value="alice@example.com" aria-label="Email(唯讀)" />
|
|
113
113
|
</Rule>
|
|
114
114
|
|
|
115
115
|
<Rule
|
|
@@ -119,7 +119,7 @@ export const ModeRule: Story = {
|
|
|
119
119
|
<Tooltip>
|
|
120
120
|
<TooltipTrigger asChild>
|
|
121
121
|
<div>
|
|
122
|
-
<Input mode="disabled" defaultValue="僅付費方案可用" />
|
|
122
|
+
<Input mode="disabled" defaultValue="僅付費方案可用" aria-label="付費方案欄位(停用)" />
|
|
123
123
|
</div>
|
|
124
124
|
</TooltipTrigger>
|
|
125
125
|
<TooltipContent>此欄位在免費方案中不可編輯</TooltipContent>
|
|
@@ -130,7 +130,7 @@ export const ModeRule: Story = {
|
|
|
130
130
|
title="❌ 不用 readonly 作為 DataTable cell"
|
|
131
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
|
-
<Input mode="readonly" value="alice@example.com" />
|
|
133
|
+
<Input mode="readonly" value="alice@example.com" aria-label="Email(資料表誤用示範)" />
|
|
134
134
|
<Label warn>↑ 在 table cell 中有多餘底色 chrome → 用 <Input mode="view" /></Label>
|
|
135
135
|
</Rule>
|
|
136
136
|
</div>
|
|
@@ -145,18 +145,18 @@ export const StartIconRule: Story = {
|
|
|
145
145
|
title="startIcon 描述 input 的「用途」,不是 value 的類型"
|
|
146
146
|
note="icon 幫助使用者一眼辨識這個欄位要填什麼——搜尋、Email、密碼、網址等。與 Button 的 startIcon 同理(描述動作),這裡描述欄位身份"
|
|
147
147
|
>
|
|
148
|
-
<Input startIcon={Search} placeholder="搜尋商品..." />
|
|
149
|
-
<Input startIcon={Mail} defaultValue="alice@example.com" />
|
|
150
|
-
<Input startIcon={Lock} type="password" defaultValue="secret-123" />
|
|
151
|
-
<Input startIcon={Globe} placeholder="https://example.com" />
|
|
148
|
+
<Input startIcon={Search} placeholder="搜尋商品..." aria-label="搜尋商品" />
|
|
149
|
+
<Input startIcon={Mail} defaultValue="alice@example.com" aria-label="Email" />
|
|
150
|
+
<Input startIcon={Lock} type="password" defaultValue="secret-123" aria-label="密碼" />
|
|
151
|
+
<Input startIcon={Globe} placeholder="https://example.com" aria-label="網址" />
|
|
152
152
|
</Rule>
|
|
153
153
|
|
|
154
154
|
<Rule
|
|
155
155
|
title="❌ startIcon 不可隨 value 變化"
|
|
156
156
|
note="icon 是「身份」不是「狀態」——value 變了但欄位用途沒變,icon 不該跟著跳動。狀態回饋用 error border 或 Form help text"
|
|
157
157
|
>
|
|
158
|
-
<Input startIcon={AlertCircle} defaultValue="invalid-email" />
|
|
159
|
-
<Input startIcon={CheckCircle2} defaultValue="alice@example.com" />
|
|
158
|
+
<Input startIcon={AlertCircle} defaultValue="invalid-email" aria-label="Email(錯誤圖示誤用)" />
|
|
159
|
+
<Input startIcon={CheckCircle2} defaultValue="alice@example.com" aria-label="Email(成功圖示誤用)" />
|
|
160
160
|
<Label warn>↑ 用 icon 切換暗示「有效/無效」——status 應該走邊框和 help text,不佔 startIcon 語意槽</Label>
|
|
161
161
|
</Rule>
|
|
162
162
|
</div>
|
|
@@ -177,6 +177,7 @@ export const EndActionRule: Story = {
|
|
|
177
177
|
<Input
|
|
178
178
|
type={showPwd ? 'text' : 'password'}
|
|
179
179
|
defaultValue="my-secret-123"
|
|
180
|
+
aria-label="密碼"
|
|
180
181
|
endAction={{
|
|
181
182
|
icon: showPwd ? EyeOff : Eye,
|
|
182
183
|
label: showPwd ? '隱藏密碼' : '顯示密碼',
|
|
@@ -187,6 +188,7 @@ export const EndActionRule: Story = {
|
|
|
187
188
|
startIcon={Search}
|
|
188
189
|
value={query}
|
|
189
190
|
onChange={(e) => setQuery(e.target.value)}
|
|
191
|
+
aria-label="搜尋商品"
|
|
190
192
|
endAction={query ? { icon: X, label: '清除搜尋', onClick: () => setQuery('') } : undefined}
|
|
191
193
|
/>
|
|
192
194
|
</Rule>
|
|
@@ -199,11 +201,13 @@ export const EndActionRule: Story = {
|
|
|
199
201
|
startIcon={Search}
|
|
200
202
|
value=""
|
|
201
203
|
placeholder="輸入後會出現清除按鈕"
|
|
204
|
+
aria-label="搜尋商品(空值)"
|
|
202
205
|
readOnly
|
|
203
206
|
/>
|
|
204
207
|
<Input
|
|
205
208
|
startIcon={Search}
|
|
206
209
|
defaultValue="Bluetooth"
|
|
210
|
+
aria-label="搜尋商品(有值)"
|
|
207
211
|
endAction={{ icon: X, label: '清除搜尋', onClick: () => {} }}
|
|
208
212
|
/>
|
|
209
213
|
</Rule>
|
|
@@ -215,6 +219,7 @@ export const EndActionRule: Story = {
|
|
|
215
219
|
<Input
|
|
216
220
|
error
|
|
217
221
|
defaultValue="invalid-email@"
|
|
222
|
+
aria-label="Email(錯誤狀態圖示誤用)"
|
|
218
223
|
endAction={{ icon: AlertCircle, label: '錯誤', onClick: () => {} }}
|
|
219
224
|
/>
|
|
220
225
|
<Label warn>↑ 錯誤已由紅邊框傳達,endAction 放 AlertCircle 會讓使用者誤以為可點擊解決</Label>
|
|
@@ -233,7 +238,7 @@ export const ErrorRule: Story = {
|
|
|
233
238
|
note="紅色邊框已足夠傳達「這個欄位有問題」,具體訊息由 Form 層的 help text 補充。Input 尾部不放 ⚠️ 狀態 icon"
|
|
234
239
|
>
|
|
235
240
|
<div>
|
|
236
|
-
<Input error defaultValue="invalid-email@" />
|
|
241
|
+
<Input error defaultValue="invalid-email@" aria-label="Email(錯誤狀態)" />
|
|
237
242
|
<p className="text-caption text-error mt-1">請輸入有效的 email 地址</p>
|
|
238
243
|
</div>
|
|
239
244
|
</Rule>
|
|
@@ -245,6 +250,7 @@ export const ErrorRule: Story = {
|
|
|
245
250
|
<Input
|
|
246
251
|
error
|
|
247
252
|
defaultValue="invalid-email@"
|
|
253
|
+
aria-label="Email(錯誤狀態圖示誤用)"
|
|
248
254
|
endAction={{ icon: AlertCircle, label: '錯誤', onClick: () => {} }}
|
|
249
255
|
/>
|
|
250
256
|
<Label warn>↑ 邊框已紅,再加尾部 icon = 雙重傳達 + 佔用 action 槽</Label>
|
|
@@ -252,4 +258,3 @@ export const ErrorRule: Story = {
|
|
|
252
258
|
</div>
|
|
253
259
|
),
|
|
254
260
|
}
|
|
255
|
-
|
|
@@ -58,7 +58,12 @@ export const SizeAlignment: Story = {
|
|
|
58
58
|
<div className="flex flex-col gap-4">
|
|
59
59
|
{(['sm', 'md', 'lg'] as const).map(size => (
|
|
60
60
|
<div key={size} className="flex items-center gap-3">
|
|
61
|
-
<Input
|
|
61
|
+
<Input
|
|
62
|
+
size={size}
|
|
63
|
+
defaultValue="Wireless Bluetooth Headphones"
|
|
64
|
+
aria-label={`商品名稱(${size} 尺寸)`}
|
|
65
|
+
className="max-w-xs"
|
|
66
|
+
/>
|
|
62
67
|
<Button variant="primary" size={size}>送出</Button>
|
|
63
68
|
<span className="text-caption text-fg-muted">size="{size}"</span>
|
|
64
69
|
</div>
|
|
@@ -83,6 +88,7 @@ export const EndAction: Story = {
|
|
|
83
88
|
<Input
|
|
84
89
|
type={showPwd ? 'text' : 'password'}
|
|
85
90
|
defaultValue="my-secret-123"
|
|
91
|
+
aria-label="密碼"
|
|
86
92
|
endAction={{
|
|
87
93
|
icon: showPwd ? EyeOff : Eye,
|
|
88
94
|
label: showPwd ? '隱藏密碼' : '顯示密碼',
|
|
@@ -96,6 +102,7 @@ export const EndAction: Story = {
|
|
|
96
102
|
startIcon={Search}
|
|
97
103
|
value={query}
|
|
98
104
|
onChange={(e) => setQuery(e.target.value)}
|
|
105
|
+
aria-label="搜尋商品"
|
|
99
106
|
endAction={query ? { icon: X, label: '清除搜尋', onClick: () => setQuery('') } : undefined}
|
|
100
107
|
/>
|
|
101
108
|
</div>
|
|
@@ -106,6 +113,7 @@ export const EndAction: Story = {
|
|
|
106
113
|
startIcon={Search}
|
|
107
114
|
value={queryLg}
|
|
108
115
|
onChange={(e) => setQueryLg(e.target.value)}
|
|
116
|
+
aria-label="搜尋商品(大尺寸)"
|
|
109
117
|
endAction={queryLg ? { icon: X, label: '清除搜尋', onClick: () => setQueryLg('') } : undefined}
|
|
110
118
|
/>
|
|
111
119
|
</div>
|
|
@@ -8,6 +8,7 @@ import { fieldWrapperStyles, bareInputStyles } from '@/design-system/components/
|
|
|
8
8
|
import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid, useFieldEmptyDisplay, fieldEmptyColorClass } from '@/design-system/components/Field/field-context'
|
|
9
9
|
import { useControllable } from '@/design-system/hooks/use-controllable'
|
|
10
10
|
import { ItemInlineAction, ItemPrefix, type InlineActionConfig } from '@/design-system/patterns/element-anatomy/item-anatomy'
|
|
11
|
+
import { TruncatedText } from '@/design-system/patterns/element-anatomy/truncated-text'
|
|
11
12
|
import { CircularProgress } from '@/design-system/components/CircularProgress/circular-progress'
|
|
12
13
|
import { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'
|
|
13
14
|
|
|
@@ -184,11 +185,15 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
184
185
|
// B1 fix(2026-05-05):view mode 單行 ellipsis 截斷(對齊 Notion / Airtable / Linear
|
|
185
186
|
// cell display canonical:single-line value 過長 → ellipsis。Textarea view 走 wrap path,
|
|
186
187
|
// 不在此處;Input view 永遠 single-line。)
|
|
187
|
-
'
|
|
188
|
+
'min-w-0',
|
|
188
189
|
displayValue == null && fieldEmptyColorClass(resolvedMode),
|
|
189
190
|
)}
|
|
190
191
|
>
|
|
191
|
-
{
|
|
192
|
+
{/* 2026-07-28:截斷必附 tooltip(tooltip.spec.md)—— 原本只有 `truncate` 沒有 tooltip,
|
|
193
|
+
長值被裁掉就再也讀不到。改消費 truncated-text primitive(截斷才顯,未截斷靜默)。 */}
|
|
194
|
+
{displayValue == null
|
|
195
|
+
? emptyDisplay
|
|
196
|
+
: <TruncatedText>{displayValue}</TruncatedText>}
|
|
192
197
|
</span>
|
|
193
198
|
</div>
|
|
194
199
|
)
|