@qijenchen/design-system 0.1.0-beta.83 → 0.1.0-beta.85
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/CLAUDE.md +8 -8
- package/README.md +3 -2
- package/dist/components/Alert/alert.js.map +1 -1
- package/dist/components/AppShell/_demo-helpers.d.ts.map +1 -1
- package/dist/components/AppShell/app-shell.d.ts +1 -1
- package/dist/components/AppShell/app-shell.d.ts.map +1 -1
- package/dist/components/AppShell/app-shell.js +22 -2
- package/dist/components/AppShell/app-shell.js.map +1 -1
- package/dist/components/Avatar/avatar.d.ts.map +1 -1
- package/dist/components/Avatar/avatar.js +4 -4
- package/dist/components/Avatar/avatar.js.map +1 -1
- package/dist/components/Breadcrumb/breadcrumb.d.ts.map +1 -1
- package/dist/components/Breadcrumb/breadcrumb.js +1 -1
- package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
- package/dist/components/Button/button.d.ts +2 -2
- package/dist/components/Button/button.d.ts.map +1 -1
- package/dist/components/Button/button.js +12 -3
- package/dist/components/Button/button.js.map +1 -1
- package/dist/components/Calendar/calendar.d.ts +13 -5
- package/dist/components/Calendar/calendar.d.ts.map +1 -1
- package/dist/components/Calendar/calendar.js +7 -1
- package/dist/components/Calendar/calendar.js.map +1 -1
- package/dist/components/Carousel/carousel.d.ts.map +1 -1
- package/dist/components/Carousel/carousel.js +8 -3
- package/dist/components/Carousel/carousel.js.map +1 -1
- package/dist/components/Chart/chart.d.ts.map +1 -1
- package/dist/components/Chart/chart.js +1 -0
- package/dist/components/Chart/chart.js.map +1 -1
- package/dist/components/Checkbox/checkbox.d.ts +1 -1
- package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox/checkbox.js +8 -4
- package/dist/components/Checkbox/checkbox.js.map +1 -1
- package/dist/components/Chip/chip.d.ts +1 -1
- package/dist/components/Chip/chip.d.ts.map +1 -1
- package/dist/components/Chip/chip.js.map +1 -1
- package/dist/components/Coachmark/coachmark.d.ts +10 -3
- package/dist/components/Coachmark/coachmark.d.ts.map +1 -1
- package/dist/components/Coachmark/coachmark.js.map +1 -1
- package/dist/components/Combobox/combobox.d.ts +5 -1
- package/dist/components/Combobox/combobox.d.ts.map +1 -1
- package/dist/components/Combobox/combobox.js +30 -13
- package/dist/components/Combobox/combobox.js.map +1 -1
- package/dist/components/DataTable/cell-registry.d.ts.map +1 -1
- package/dist/components/DataTable/cell-registry.js +53 -62
- package/dist/components/DataTable/cell-registry.js.map +1 -1
- package/dist/components/DataTable/column-types.d.ts +10 -4
- 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 +1 -1
- package/dist/components/DataTable/data-table-column-visibility-panel.js +183 -0
- package/dist/components/DataTable/data-table-column-visibility-panel.js.map +1 -0
- package/dist/components/DataTable/data-table-filter-group.d.ts +47 -0
- package/dist/components/DataTable/data-table-filter-group.d.ts.map +1 -0
- package/dist/components/DataTable/data-table-filter-group.js +154 -0
- package/dist/components/DataTable/data-table-filter-group.js.map +1 -0
- package/dist/components/DataTable/data-table-filter-panel.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-panel.js +285 -0
- package/dist/components/DataTable/data-table-filter-panel.js.map +1 -0
- package/dist/components/DataTable/data-table-filter-value-picker.d.ts +29 -0
- package/dist/components/DataTable/data-table-filter-value-picker.d.ts.map +1 -0
- package/dist/components/DataTable/data-table-filter-value-picker.js +187 -0
- package/dist/components/DataTable/data-table-filter-value-picker.js.map +1 -0
- package/dist/components/DataTable/data-table-interaction-layer.d.ts +11 -15
- package/dist/components/DataTable/data-table-interaction-layer.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-interaction-layer.js +1 -3
- package/dist/components/DataTable/data-table-interaction-layer.js.map +1 -1
- package/dist/components/DataTable/data-table-sort-manager.js +140 -0
- package/dist/components/DataTable/data-table-sort-manager.js.map +1 -0
- package/dist/components/DataTable/data-table.d.ts +5 -6
- package/dist/components/DataTable/data-table.d.ts.map +1 -1
- package/dist/components/DataTable/data-table.js +36 -15
- package/dist/components/DataTable/data-table.js.map +1 -1
- package/dist/components/DataTable/filter-operators.js +140 -0
- package/dist/components/DataTable/filter-operators.js.map +1 -0
- package/dist/components/DataTable/filter-tree.js +312 -0
- package/dist/components/DataTable/filter-tree.js.map +1 -0
- package/dist/components/DataTable/index.d.ts +3 -0
- package/dist/components/DataTable/index.d.ts.map +1 -1
- package/dist/components/DataTable/index.js +12 -1
- package/dist/components/DataTable/index.js.map +1 -1
- package/dist/components/DataTable/lib/column-meta.js +25 -0
- package/dist/components/DataTable/lib/column-meta.js.map +1 -0
- package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
- package/dist/components/DateGrid/date-grid.js +3 -1
- package/dist/components/DateGrid/date-grid.js.map +1 -1
- package/dist/components/DatePicker/date-picker.d.ts +7 -1
- package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
- package/dist/components/DatePicker/date-picker.js +105 -74
- package/dist/components/DatePicker/date-picker.js.map +1 -1
- package/dist/components/Dialog/dialog.d.ts +20 -3
- package/dist/components/Dialog/dialog.d.ts.map +1 -1
- package/dist/components/Dialog/dialog.js +38 -20
- package/dist/components/Dialog/dialog.js.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.js +3 -1
- package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
- package/dist/components/Field/field-context.d.ts +45 -5
- package/dist/components/Field/field-context.d.ts.map +1 -1
- package/dist/components/Field/field-context.js +19 -0
- package/dist/components/Field/field-context.js.map +1 -1
- package/dist/components/Field/field-edit-keys.d.ts +21 -0
- package/dist/components/Field/field-edit-keys.d.ts.map +1 -0
- package/dist/components/Field/field-edit-keys.js +19 -0
- package/dist/components/Field/field-edit-keys.js.map +1 -0
- package/dist/components/Field/field-types.d.ts +20 -1
- 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 +3 -2
- package/dist/components/Field/field-wrapper.d.ts.map +1 -1
- package/dist/components/Field/field-wrapper.js +38 -39
- package/dist/components/Field/field-wrapper.js.map +1 -1
- package/dist/components/Field/field.d.ts +2 -1
- package/dist/components/Field/field.d.ts.map +1 -1
- package/dist/components/Field/field.js.map +1 -1
- package/dist/components/Field/index.d.ts +1 -0
- package/dist/components/Field/index.d.ts.map +1 -1
- package/dist/components/Field/index.js +2 -0
- package/dist/components/Field/index.js.map +1 -1
- package/dist/components/FieldControlGroup/field-control-group.d.ts +1 -1
- package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
- package/dist/components/FileItem/file-item.d.ts.map +1 -1
- package/dist/components/FileItem/file-item.js +1 -1
- package/dist/components/FileItem/file-item.js.map +1 -1
- package/dist/components/FileUpload/file-upload.d.ts +1 -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 +7 -2
- package/dist/components/FileViewer/file-viewer.js.map +1 -1
- package/dist/components/FileViewer/image-renderer.d.ts.map +1 -1
- package/dist/components/FileViewer/image-renderer.js +22 -1
- package/dist/components/FileViewer/image-renderer.js.map +1 -1
- package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
- package/dist/components/HoverCard/hover-card.js +5 -3
- package/dist/components/HoverCard/hover-card.js.map +1 -1
- package/dist/components/InlineEdit/index.d.ts +2 -0
- package/dist/components/InlineEdit/index.d.ts.map +1 -0
- package/dist/components/InlineEdit/index.js +5 -0
- package/dist/components/InlineEdit/index.js.map +1 -0
- package/dist/components/InlineEdit/inline-edit.d.ts +59 -0
- package/dist/components/InlineEdit/inline-edit.d.ts.map +1 -0
- package/dist/components/InlineEdit/inline-edit.js +164 -0
- package/dist/components/InlineEdit/inline-edit.js.map +1 -0
- package/dist/components/Input/input.d.ts +6 -11
- package/dist/components/Input/input.d.ts.map +1 -1
- package/dist/components/Input/input.js +43 -13
- package/dist/components/Input/input.js.map +1 -1
- package/dist/components/LinkInput/link-input.d.ts +2 -2
- package/dist/components/LinkInput/link-input.d.ts.map +1 -1
- package/dist/components/LinkInput/link-input.js +6 -5
- package/dist/components/LinkInput/link-input.js.map +1 -1
- package/dist/components/Menu/menu-item.d.ts +14 -2
- package/dist/components/Menu/menu-item.d.ts.map +1 -1
- package/dist/components/Menu/menu-item.js +25 -3
- package/dist/components/Menu/menu-item.js.map +1 -1
- package/dist/components/NumberInput/number-input.d.ts +3 -3
- package/dist/components/NumberInput/number-input.d.ts.map +1 -1
- package/dist/components/NumberInput/number-input.js +6 -5
- package/dist/components/NumberInput/number-input.js.map +1 -1
- package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
- package/dist/components/OverflowIndicator/overflow-indicator.js +2 -6
- package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
- package/dist/components/Pagination/pagination.js +1 -1
- package/dist/components/Pagination/pagination.js.map +1 -1
- package/dist/components/PeoplePicker/index.js +7 -1
- package/dist/components/PeoplePicker/index.js.map +1 -1
- package/dist/components/PeoplePicker/people-picker.d.ts +9 -2
- package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
- package/dist/components/PeoplePicker/people-picker.js +23 -10
- package/dist/components/PeoplePicker/people-picker.js.map +1 -1
- package/dist/components/PeoplePicker/person-display.d.ts +6 -3
- package/dist/components/PeoplePicker/person-display.d.ts.map +1 -1
- package/dist/components/PeoplePicker/person-display.js +19 -14
- package/dist/components/PeoplePicker/person-display.js.map +1 -1
- package/dist/components/Popover/popover.d.ts.map +1 -1
- package/dist/components/Popover/popover.js +3 -1
- package/dist/components/Popover/popover.js.map +1 -1
- package/dist/components/ProfileCard/profile-card.d.ts +2 -2
- package/dist/components/ProfileCard/profile-card.js +3 -3
- package/dist/components/ProfileCard/profile-card.js.map +1 -1
- package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
- package/dist/components/RadioGroup/radio-group.js +15 -9
- package/dist/components/RadioGroup/radio-group.js.map +1 -1
- package/dist/components/ScrollArea/scroll-area.d.ts +11 -1
- package/dist/components/ScrollArea/scroll-area.d.ts.map +1 -1
- package/dist/components/ScrollArea/scroll-area.js +6 -3
- package/dist/components/ScrollArea/scroll-area.js.map +1 -1
- package/dist/components/Select/select.d.ts +46 -5
- package/dist/components/Select/select.d.ts.map +1 -1
- package/dist/components/Select/select.js +109 -28
- package/dist/components/Select/select.js.map +1 -1
- package/dist/components/SelectMenu/select-menu.d.ts +3 -1
- package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
- package/dist/components/SelectMenu/select-menu.js +8 -1
- package/dist/components/SelectMenu/select-menu.js.map +1 -1
- package/dist/components/SelectionControl/boolean-value.d.ts +11 -0
- package/dist/components/SelectionControl/boolean-value.d.ts.map +1 -0
- package/dist/components/SelectionControl/boolean-value.js +23 -0
- package/dist/components/SelectionControl/boolean-value.js.map +1 -0
- package/dist/components/SelectionControl/index.d.ts +1 -0
- package/dist/components/SelectionControl/index.d.ts.map +1 -1
- package/dist/components/SelectionControl/index.js +2 -0
- package/dist/components/SelectionControl/index.js.map +1 -1
- package/dist/components/Sheet/index.js +2 -1
- package/dist/components/Sheet/sheet.d.ts +7 -1
- package/dist/components/Sheet/sheet.d.ts.map +1 -1
- package/dist/components/Sheet/sheet.js +5 -2
- package/dist/components/Sheet/sheet.js.map +1 -1
- package/dist/components/Sidebar/sidebar.d.ts +9 -4
- package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
- package/dist/components/Sidebar/sidebar.js +8 -10
- 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.map +1 -1
- package/dist/components/Switch/switch.d.ts +6 -6
- package/dist/components/Switch/switch.d.ts.map +1 -1
- package/dist/components/Switch/switch.js +3 -2
- package/dist/components/Switch/switch.js.map +1 -1
- package/dist/components/Tabs/tabs.d.ts.map +1 -1
- package/dist/components/Tabs/tabs.js +15 -1
- package/dist/components/Tabs/tabs.js.map +1 -1
- package/dist/components/Textarea/textarea.d.ts +2 -2
- package/dist/components/Textarea/textarea.d.ts.map +1 -1
- package/dist/components/Textarea/textarea.js +50 -42
- package/dist/components/Textarea/textarea.js.map +1 -1
- package/dist/components/TimePicker/time-columns.d.ts.map +1 -1
- package/dist/components/TimePicker/time-columns.js +42 -38
- package/dist/components/TimePicker/time-columns.js.map +1 -1
- package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
- package/dist/components/TimePicker/time-picker.js +8 -6
- 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 +7 -5
- package/dist/components/Tooltip/tooltip.js.map +1 -1
- package/dist/components/TreeView/tree-view.d.ts +56 -1
- package/dist/components/TreeView/tree-view.d.ts.map +1 -1
- package/dist/components/TreeView/tree-view.js +211 -12
- package/dist/components/TreeView/tree-view.js.map +1 -1
- package/dist/index.d.ts +115 -58
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +95 -108
- package/dist/index.js.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.d.ts +7 -6
- package/dist/patterns/element-anatomy/item-anatomy.d.ts.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
- package/dist/tokens/categorical-color.js +15 -0
- package/dist/tokens/categorical-color.js.map +1 -1
- package/dist/tokens/motion/motion.d.ts +7 -6
- package/dist/tokens/motion/motion.d.ts.map +1 -1
- package/dist/tokens/motion/motion.js +6 -6
- package/dist/tokens/motion/motion.js.map +1 -1
- package/dist/tokens/motion/overlay-motion.d.ts +19 -0
- package/dist/tokens/motion/overlay-motion.d.ts.map +1 -0
- package/dist/tokens/motion/overlay-motion.js +9 -0
- package/dist/tokens/motion/overlay-motion.js.map +1 -0
- package/dist/tokens/uiSize/icon-size.js +5 -0
- package/dist/tokens/uiSize/icon-size.js.map +1 -0
- package/ds-canonical/fork/governance.lock +31 -19
- package/ds-canonical/fork/hooks/_log-fire.sh +46 -0
- package/ds-canonical/fork/hooks/block_prototype_imports.py +15 -4
- package/ds-canonical/fork/hooks/check_consumer_app_invariants.sh +145 -13
- package/ds-canonical/fork/hooks/check_ds_anchor_preflight.sh +2 -1
- package/ds-canonical/fork/hooks/check_dynamic_tailwind_class.sh +47 -0
- package/ds-canonical/fork/hooks/check_escape_marker_abuse.sh +27 -0
- package/ds-canonical/fork/hooks/check_field_family_invariants.sh +61 -1
- package/ds-canonical/fork/hooks/check_layout_space_magic_numbers.sh +28 -4
- package/ds-canonical/fork/hooks/check_opacity_token_usage.sh +28 -0
- package/ds-canonical/fork/hooks/check_story_determinism.sh +2 -0
- package/ds-canonical/fork/hooks/check_story_invariants.sh +119 -0
- package/ds-canonical/fork/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
- package/ds-canonical/fork/manifest.json +15 -0
- package/ds-canonical/fork/preamble.md +20 -18
- package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +3 -3
- package/ds-canonical/fork/skills/code-quality-audit/SKILL.md +3 -3
- package/ds-canonical/fork/skills/delivery-handoff/SKILL.md +1 -1
- package/ds-canonical/fork/skills/product-ui-audit/SKILL.md +3 -3
- package/ds-canonical/fork/skills/propose-options/SKILL.md +3 -3
- package/ds-canonical/fork/skills/prototype/SKILL.md +1 -1
- package/ds-canonical/fork/skills/scan-similar-bugs/SKILL.md +2 -2
- package/ds-canonical/fork/skills/visual-audit/SKILL.md +2 -2
- package/ds-canonical/hooks/README.md +1 -1
- package/ds-canonical/hooks/block_prototype_imports.py +15 -4
- package/ds-canonical/hooks/check_audit_post_report_validator.sh +65 -7
- package/ds-canonical/hooks/check_codex_brief_invariants.sh +28 -1
- package/ds-canonical/hooks/check_consumer_app_invariants.sh +145 -13
- package/ds-canonical/hooks/check_datatable_invariants.sh +6 -3
- package/ds-canonical/hooks/check_ds_anchor_preflight.sh +2 -1
- package/ds-canonical/hooks/check_dynamic_tailwind_class.sh +47 -0
- package/ds-canonical/hooks/check_escape_marker_abuse.sh +27 -0
- package/ds-canonical/hooks/check_field_family_invariants.sh +61 -1
- package/ds-canonical/hooks/check_layout_space_magic_numbers.sh +28 -4
- package/ds-canonical/hooks/check_naming_and_abstraction.sh +1 -1
- package/ds-canonical/hooks/check_opacity_token_usage.sh +28 -0
- package/ds-canonical/hooks/check_pixel_quantified_audit.sh +3 -2
- package/ds-canonical/hooks/check_post_main_ssot_propagate.sh +6 -1
- package/ds-canonical/hooks/check_ssot_header_declaration.sh +3 -1
- package/ds-canonical/hooks/check_story_determinism.sh +2 -0
- package/ds-canonical/hooks/check_substantive_edit_approval_preflight.sh +8 -7
- package/ds-canonical/hooks/check_tabs_content_chrome_body_double_gap.sh +29 -0
- package/ds-canonical/hooks/lib/_story_compile_drift.sh +1 -1
- package/ds-canonical/hooks/lib/_token_hygiene.sh +10 -1
- package/ds-canonical/hooks/stop_self_audit.sh +31 -0
- package/ds-canonical/hooks/tests/test_block_prototype_imports.sh +24 -0
- package/ds-canonical/hooks/tests/test_check_audit_post_report_validator.sh +44 -0
- package/ds-canonical/hooks/tests/test_check_codex_brief_invariants.sh +43 -2
- package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +112 -0
- package/ds-canonical/hooks/tests/test_check_consumer_story_baseline.sh +9 -2
- package/ds-canonical/hooks/tests/test_check_dynamic_tailwind_class.sh +76 -0
- package/ds-canonical/hooks/tests/test_check_escape_marker_abuse.sh +24 -0
- package/ds-canonical/hooks/tests/test_check_field_family_invariants.sh +27 -2
- package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +39 -0
- package/ds-canonical/hooks/tests/test_check_tabs_content_chrome_body_double_gap.sh +76 -0
- package/ds-canonical/references/audit-coverage-vs-24-checklist.md +1 -1
- package/ds-canonical/references/build-ui-canonicals.md +2 -1
- package/ds-canonical/references/failure-class-registry.json +145 -0
- package/ds-canonical/references/governance-audit-coverage.md +33 -0
- package/ds-canonical/references/item-anatomy-recipe.md +2 -2
- package/ds-canonical/references/scenario-definition.md +6 -9
- package/ds-canonical/references/ssot-consultation.md +2 -2
- package/ds-canonical/references/ssot-index.md +5 -4
- package/ds-canonical/references/story-baseline-registry.json +8 -2
- package/ds-canonical/references/structural-token-retention.md +6 -9
- package/ds-canonical/rules/meta-patterns.md +8 -8
- package/ds-canonical/rules/self-verify.md +4 -3
- package/ds-canonical/rules/story-rules.md +2 -2
- package/ds-canonical/rules/ui-development.md +2 -2
- package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +3 -3
- package/ds-canonical/skills/code-quality-audit/SKILL.md +3 -3
- package/ds-canonical/skills/codex-collab/SKILL.md +9 -7
- package/ds-canonical/skills/codex-collab/references/brief-template.md +1 -1
- package/ds-canonical/skills/codify-principle/SKILL.md +2 -2
- package/ds-canonical/skills/component-quality-gate/SKILL.md +1 -1
- package/ds-canonical/skills/component-quality-gate/references/checklist.md +2 -2
- package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +51 -58
- package/ds-canonical/skills/deep-audit-cross-codex/references/phase-a-workflow.md +29 -0
- package/ds-canonical/skills/deep-audit-cross-codex/references/phase-b-codex-brief.md +29 -10
- package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +21 -0
- package/ds-canonical/skills/delivery-handoff/SKILL.md +1 -1
- package/ds-canonical/skills/design-system-audit/SKILL.md +26 -25
- package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +13 -1
- package/ds-canonical/skills/design-system-audit/references/rule-placement.md +1 -1
- package/ds-canonical/skills/governance-health/SKILL.md +1 -1
- package/ds-canonical/skills/knowledge-prune/SKILL.md +3 -0
- package/ds-canonical/skills/knowledge-prune/references/prune-dimensions-d5-d10.md +1 -0
- package/ds-canonical/skills/new-component/SKILL.md +5 -5
- package/ds-canonical/skills/new-component/references/new-component-checklist.md +1 -1
- package/ds-canonical/skills/product-ui-audit/SKILL.md +3 -3
- package/ds-canonical/skills/propose-options/SKILL.md +3 -3
- package/ds-canonical/skills/prototype/SKILL.md +1 -1
- package/ds-canonical/skills/scan-similar-bugs/SKILL.md +2 -2
- package/ds-canonical/skills/story-auto-compile-migrate/SKILL.md +3 -3
- package/ds-canonical/skills/story-writing/SKILL.md +3 -3
- package/ds-canonical/skills/visual-audit/SKILL.md +2 -2
- package/ds-story-manifest.json +29 -20
- package/llms-full.txt +22 -1
- package/llms.txt +5 -4
- package/package.json +7 -3
- package/src/components/Accordion/accordion.principles.stories.tsx +1 -1
- package/src/components/Accordion/accordion.stories.tsx +16 -1
- package/src/components/Alert/alert.principles.stories.tsx +1 -1
- package/src/components/Alert/alert.spec.md +8 -10
- package/src/components/Alert/alert.stories.tsx +28 -28
- package/src/components/Alert/alert.tsx +1 -1
- package/src/components/AppShell/_demo-helpers.tsx +6 -3
- package/src/components/AppShell/app-shell.spec.md +4 -4
- package/src/components/AppShell/app-shell.tsx +28 -2
- package/src/components/AspectRatio/aspect-ratio.spec.md +1 -1
- package/src/components/AspectRatio/aspect-ratio.stories.tsx +7 -89
- package/src/components/Avatar/avatar.anatomy.stories.tsx +7 -3
- package/src/components/Avatar/avatar.principles.stories.tsx +19 -15
- package/src/components/Avatar/avatar.spec.md +2 -2
- package/src/components/Avatar/avatar.stories.tsx +8 -6
- package/src/components/Avatar/avatar.tsx +13 -5
- package/src/components/Badge/badge.principles.stories.tsx +1 -1
- package/src/components/Badge/badge.spec.md +2 -0
- package/src/components/Badge/badge.stories.tsx +4 -2
- package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +6 -4
- package/src/components/Breadcrumb/breadcrumb.stories.tsx +6 -5
- package/src/components/Breadcrumb/breadcrumb.tsx +3 -2
- package/src/components/BulkActionBar/bulk-action-bar.spec.md +4 -2
- package/src/components/Button/button.principles.stories.tsx +1 -1
- package/src/components/Button/button.spec.md +2 -2
- package/src/components/Button/button.tsx +18 -3
- package/src/components/Calendar/calendar.anatomy.stories.tsx +12 -10
- package/src/components/Calendar/calendar.principles.stories.tsx +14 -6
- package/src/components/Calendar/calendar.spec.md +5 -4
- package/src/components/Calendar/calendar.stories.tsx +7 -4
- package/src/components/Calendar/calendar.tsx +26 -7
- package/src/components/Carousel/carousel.anatomy.stories.tsx +5 -4
- package/src/components/Carousel/carousel.spec.md +2 -2
- package/src/components/Carousel/carousel.tsx +11 -3
- package/src/components/Chart/chart.anatomy.stories.tsx +7 -4
- package/src/components/Chart/chart.principles.stories.tsx +1 -1
- package/src/components/Chart/chart.stories.tsx +2 -8
- package/src/components/Chart/chart.tsx +3 -0
- package/src/components/Checkbox/checkbox.anatomy.stories.tsx +28 -49
- package/src/components/Checkbox/checkbox.principles.stories.tsx +1 -1
- package/src/components/Checkbox/checkbox.spec.md +13 -26
- package/src/components/Checkbox/checkbox.stories.tsx +11 -8
- package/src/components/Checkbox/checkbox.tsx +18 -13
- package/src/components/Chip/chip.anatomy.stories.tsx +44 -24
- package/src/components/Chip/chip.principles.stories.tsx +1 -1
- package/src/components/Chip/chip.spec.md +5 -4
- package/src/components/Chip/chip.tsx +5 -1
- package/src/components/CircularProgress/circular-progress.principles.stories.tsx +7 -4
- package/src/components/CircularProgress/circular-progress.spec.md +3 -3
- package/src/components/CircularProgress/circular-progress.stories.tsx +2 -2
- package/src/components/Coachmark/coachmark.anatomy.stories.tsx +2 -2
- package/src/components/Coachmark/coachmark.principles.stories.tsx +2 -1
- package/src/components/Coachmark/coachmark.spec.md +5 -5
- package/src/components/Coachmark/coachmark.stories.tsx +3 -1
- package/src/components/Coachmark/coachmark.tsx +10 -3
- package/src/components/Combobox/combobox.anatomy.stories.tsx +2 -2
- package/src/components/Combobox/combobox.tsx +53 -19
- package/src/components/Command/command.anatomy.stories.tsx +2 -2
- package/src/components/Command/command.principles.stories.tsx +1 -1
- package/src/components/Command/command.stories.tsx +42 -10
- package/src/components/DataTable/cell-registry.tsx +86 -63
- package/src/components/DataTable/column-types.ts +10 -1
- package/src/components/DataTable/data-table-column-visibility-panel.tsx +1 -1
- package/src/components/DataTable/data-table-filter-group.tsx +231 -0
- package/src/components/DataTable/data-table-filter-panel.tsx +16 -430
- package/src/components/DataTable/data-table-filter-value-picker.tsx +237 -0
- package/src/components/DataTable/data-table-interaction-layer.tsx +15 -19
- package/src/components/DataTable/data-table.anatomy.stories.tsx +15 -13
- package/src/components/DataTable/data-table.principles.stories.tsx +9 -9
- package/src/components/DataTable/data-table.spec.md +33 -13
- package/src/components/DataTable/data-table.stories.tsx +16 -17
- package/src/components/DataTable/data-table.tsx +107 -61
- package/src/components/DataTable/filter-operators.spec.md +3 -3
- package/src/components/DataTable/index.ts +3 -0
- package/src/components/DateGrid/date-grid.anatomy.stories.tsx +4 -4
- package/src/components/DateGrid/date-grid.principles.stories.tsx +1 -1
- package/src/components/DateGrid/date-grid.spec.md +1 -1
- package/src/components/DateGrid/date-grid.tsx +3 -1
- package/src/components/DatePicker/date-picker.anatomy.stories.tsx +8 -8
- package/src/components/DatePicker/date-picker.principles.stories.tsx +3 -3
- package/src/components/DatePicker/date-picker.spec.md +3 -3
- package/src/components/DatePicker/date-picker.stories.tsx +4 -5
- package/src/components/DatePicker/date-picker.tsx +99 -45
- package/src/components/DescriptionList/description-list.anatomy.stories.tsx +12 -10
- package/src/components/DescriptionList/description-list.principles.stories.tsx +2 -2
- package/src/components/DescriptionList/description-list.spec.md +3 -3
- package/src/components/DescriptionList/description-list.stories.tsx +7 -6
- package/src/components/Dialog/dialog.anatomy.stories.tsx +1 -0
- package/src/components/Dialog/dialog.spec.md +9 -0
- package/src/components/Dialog/dialog.stories.tsx +66 -5
- package/src/components/Dialog/dialog.tsx +60 -25
- package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +1 -1
- package/src/components/DropdownMenu/dropdown-menu.principles.stories.tsx +8 -6
- package/src/components/DropdownMenu/dropdown-menu.spec.md +5 -5
- package/src/components/DropdownMenu/dropdown-menu.tsx +18 -9
- package/src/components/Empty/empty.anatomy.stories.tsx +1 -1
- package/src/components/Empty/empty.spec.md +2 -2
- package/src/components/Field/field-context.ts +69 -5
- package/src/components/Field/field-controls.spec.md +62 -8
- package/src/components/Field/field-edit-keys.ts +66 -0
- package/src/components/Field/field-types.ts +58 -9
- package/src/components/Field/field-wrapper.tsx +50 -42
- package/src/components/Field/field.anatomy.stories.tsx +9 -6
- package/src/components/Field/field.spec.md +1 -1
- package/src/components/Field/field.stories.tsx +1 -1
- package/src/components/Field/field.tsx +2 -1
- package/src/components/Field/form-validation.spec.md +3 -3
- package/src/components/Field/index.ts +1 -0
- package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +6 -6
- package/src/components/FieldControlGroup/field-control-group.spec.md +9 -4
- package/src/components/FieldControlGroup/field-control-group.tsx +1 -1
- package/src/components/FileItem/file-item.anatomy.stories.tsx +1 -1
- package/src/components/FileItem/file-item.spec.md +5 -8
- package/src/components/FileItem/file-item.stories.tsx +36 -25
- package/src/components/FileItem/file-item.tsx +8 -6
- package/src/components/FileUpload/file-upload.anatomy.stories.tsx +6 -6
- package/src/components/FileUpload/file-upload.principles.stories.tsx +5 -5
- package/src/components/FileUpload/file-upload.spec.md +1 -1
- package/src/components/FileUpload/file-upload.tsx +1 -1
- package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +4 -3
- package/src/components/FileViewer/file-viewer.principles.stories.tsx +3 -3
- package/src/components/FileViewer/file-viewer.spec.md +30 -12
- package/src/components/FileViewer/file-viewer.stories.tsx +1 -2
- package/src/components/FileViewer/file-viewer.tsx +12 -4
- package/src/components/FileViewer/image-renderer.tsx +35 -7
- package/src/components/HoverCard/hover-card.anatomy.stories.tsx +6 -2
- package/src/components/HoverCard/hover-card.principles.stories.tsx +6 -3
- package/src/components/HoverCard/hover-card.spec.md +5 -5
- package/src/components/HoverCard/hover-card.stories.tsx +16 -16
- package/src/components/HoverCard/hover-card.tsx +5 -3
- package/src/components/InlineEdit/index.ts +5 -0
- package/src/components/InlineEdit/inline-edit.spec.md +149 -0
- package/src/components/InlineEdit/inline-edit.stories.tsx +246 -0
- package/src/components/InlineEdit/inline-edit.tsx +287 -0
- package/src/components/Input/input.anatomy.stories.tsx +8 -7
- package/src/components/Input/input.principles.stories.tsx +2 -12
- package/src/components/Input/input.spec.md +13 -18
- package/src/components/Input/input.tsx +69 -22
- package/src/components/LinkInput/link-input.anatomy.stories.tsx +1 -1
- package/src/components/LinkInput/link-input.principles.stories.tsx +2 -2
- package/src/components/LinkInput/link-input.tsx +11 -10
- package/src/components/Menu/menu-item.anatomy.stories.tsx +3 -3
- package/src/components/Menu/menu-item.principles.stories.tsx +1 -1
- package/src/components/Menu/menu-item.spec.md +22 -15
- package/src/components/Menu/menu-item.stories.tsx +39 -11
- package/src/components/Menu/menu-item.tsx +38 -4
- package/src/components/Notice/notice.principles.stories.tsx +1 -1
- package/src/components/Notice/notice.spec.md +16 -3
- package/src/components/NumberInput/number-input.anatomy.stories.tsx +3 -3
- package/src/components/NumberInput/number-input.principles.stories.tsx +1 -1
- package/src/components/NumberInput/number-input.spec.md +4 -4
- package/src/components/NumberInput/number-input.tsx +19 -12
- package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +10 -8
- package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +3 -3
- package/src/components/OverflowIndicator/overflow-indicator.spec.md +4 -2
- package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +71 -63
- package/src/components/OverflowIndicator/overflow-indicator.tsx +9 -8
- package/src/components/Pagination/pagination.spec.md +1 -1
- package/src/components/Pagination/pagination.stories.tsx +2 -6
- package/src/components/Pagination/pagination.tsx +1 -1
- package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +4 -4
- package/src/components/PeoplePicker/people-picker.principles.stories.tsx +2 -2
- package/src/components/PeoplePicker/people-picker.spec.md +6 -6
- package/src/components/PeoplePicker/people-picker.tsx +56 -16
- package/src/components/PeoplePicker/person-display.tsx +32 -16
- package/src/components/Popover/popover.anatomy.stories.tsx +2 -2
- package/src/components/Popover/popover.tsx +3 -1
- package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +12 -23
- package/src/components/ProfileCard/profile-card.spec.md +3 -3
- package/src/components/ProfileCard/profile-card.tsx +6 -6
- package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +3 -1
- package/src/components/ProgressBar/progress-bar.principles.stories.tsx +1 -1
- package/src/components/ProgressBar/progress-bar.stories.tsx +50 -42
- package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +10 -28
- package/src/components/RadioGroup/radio-group.stories.tsx +29 -32
- package/src/components/RadioGroup/radio-group.tsx +41 -15
- package/src/components/Rating/rating.spec.md +11 -3
- package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.principles.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.spec.md +7 -3
- package/src/components/ScrollArea/scroll-area.stories.tsx +1 -1
- package/src/components/ScrollArea/scroll-area.tsx +17 -4
- package/src/components/Select/select.anatomy.stories.tsx +3 -3
- package/src/components/Select/select.spec.md +29 -17
- package/src/components/Select/select.stories.tsx +33 -0
- package/src/components/Select/select.tsx +190 -36
- package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +4 -4
- package/src/components/SelectMenu/select-menu.spec.md +15 -7
- package/src/components/SelectMenu/select-menu.tsx +16 -3
- package/src/components/SelectionControl/boolean-value.tsx +39 -0
- package/src/components/SelectionControl/index.ts +1 -0
- package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +2 -2
- package/src/components/SelectionControl/selection-item.principles.stories.tsx +2 -2
- package/src/components/SelectionControl/selection-item.spec.md +3 -3
- package/src/components/SelectionControl/selection-item.stories.tsx +3 -3
- package/src/components/Separator/separator.spec.md +2 -2
- package/src/components/Separator/separator.stories.tsx +6 -27
- package/src/components/Sheet/sheet.anatomy.stories.tsx +1 -1
- package/src/components/Sheet/sheet.principles.stories.tsx +1 -1
- package/src/components/Sheet/sheet.spec.md +2 -2
- package/src/components/Sheet/sheet.tsx +9 -5
- package/src/components/Sidebar/sidebar.anatomy.stories.tsx +16 -12
- package/src/components/Sidebar/sidebar.principles.stories.tsx +10 -8
- package/src/components/Sidebar/sidebar.spec.md +13 -15
- package/src/components/Sidebar/sidebar.stories.tsx +8 -7
- package/src/components/Sidebar/sidebar.tsx +37 -25
- package/src/components/Skeleton/skeleton.stories.tsx +4 -11
- package/src/components/Slider/slider.anatomy.stories.tsx +3 -3
- package/src/components/Slider/slider.principles.stories.tsx +2 -2
- package/src/components/Slider/slider.spec.md +5 -5
- package/src/components/Slider/slider.tsx +3 -1
- package/src/components/Steps/steps.anatomy.stories.tsx +4 -4
- package/src/components/Steps/steps.principles.stories.tsx +1 -1
- package/src/components/Steps/steps.spec.md +1 -1
- package/src/components/Switch/switch.anatomy.stories.tsx +7 -7
- package/src/components/Switch/switch.principles.stories.tsx +4 -4
- package/src/components/Switch/switch.spec.md +8 -8
- package/src/components/Switch/switch.stories.tsx +9 -24
- package/src/components/Switch/switch.tsx +17 -13
- package/src/components/Tabs/tabs.anatomy.stories.tsx +10 -7
- package/src/components/Tabs/tabs.spec.md +1 -1
- package/src/components/Tabs/tabs.tsx +21 -1
- package/src/components/Textarea/textarea.anatomy.stories.tsx +1 -1
- package/src/components/Textarea/textarea.principles.stories.tsx +1 -1
- package/src/components/Textarea/textarea.spec.md +4 -4
- package/src/components/Textarea/textarea.tsx +72 -46
- package/src/components/TimePicker/time-columns.tsx +6 -2
- package/src/components/TimePicker/time-picker.principles.stories.tsx +1 -1
- package/src/components/TimePicker/time-picker.spec.md +3 -3
- package/src/components/TimePicker/time-picker.tsx +16 -10
- package/src/components/Toast/toast.stories.tsx +7 -4
- package/src/components/Tooltip/tooltip.anatomy.stories.tsx +4 -4
- package/src/components/Tooltip/tooltip.principles.stories.tsx +3 -3
- package/src/components/Tooltip/tooltip.spec.md +1 -1
- package/src/components/Tooltip/tooltip.stories.tsx +4 -39
- package/src/components/Tooltip/tooltip.tsx +8 -6
- package/src/components/TreeView/tree-view.anatomy.stories.tsx +6 -3
- package/src/components/TreeView/tree-view.spec.md +42 -11
- package/src/components/TreeView/tree-view.stories.tsx +1 -0
- package/src/components/TreeView/tree-view.tsx +322 -17
- package/src/index.ts +588 -60
- package/src/patterns/action-bar/action-bar.spec.md +9 -0
- package/src/patterns/action-bar/action-bar.stories.tsx +6 -3
- package/src/patterns/element-anatomy/inline-action.spec.md +2 -0
- package/src/patterns/element-anatomy/item-anatomy.spec.md +14 -5
- package/src/patterns/element-anatomy/item-anatomy.stories.tsx +6 -4
- package/src/patterns/element-anatomy/item-anatomy.tsx +7 -6
- package/src/patterns/header-canonical/header-canonical.spec.md +5 -4
- package/src/patterns/overlay-surface/overlay-surface.spec.md +22 -1
- package/src/styles/tokens.css +3 -0
- package/src/tokens/README.md +1 -1
- package/src/tokens/color/color.spec.md +4 -4
- package/src/tokens/color/color.stories.tsx +3 -3
- package/src/tokens/density/density.stories.tsx +16 -3
- package/src/tokens/layoutSpace/layoutSpace.spec.md +3 -1
- package/src/tokens/motion/motion.css +33 -21
- package/src/tokens/motion/motion.spec.md +32 -15
- package/src/tokens/motion/motion.stories.tsx +58 -5
- package/src/tokens/motion/motion.ts +7 -6
- package/src/tokens/motion/overlay-motion.ts +26 -0
- package/src/tokens/orphan-tokens.spec.md +4 -4
- package/src/tokens/token-system.spec.md +1 -0
- package/src/tokens/uiSize/uiSize.css +1 -1
- package/src/tokens/uiSize/uiSize.spec.md +1 -1
- package/src/tokens/uiSize/uiSize.stories.tsx +4 -2
- package/src/tokens/utility-registry.json +160 -23
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
TransformWrapper,
|
|
5
5
|
TransformComponent,
|
|
6
6
|
type ReactZoomPanPinchRef,
|
|
7
|
+
type ReactZoomPanPinchContentRef,
|
|
7
8
|
} from 'react-zoom-pan-pinch'
|
|
8
9
|
import type { FileRendererProps } from './file-viewer-types'
|
|
9
10
|
|
|
@@ -41,7 +42,7 @@ export const ImageRenderer: React.FC<FileRendererProps> = ({
|
|
|
41
42
|
fitRequest,
|
|
42
43
|
onCapabilitiesChange,
|
|
43
44
|
}) => {
|
|
44
|
-
const apiRef = React.useRef<
|
|
45
|
+
const apiRef = React.useRef<ReactZoomPanPinchContentRef | null>(null)
|
|
45
46
|
const imgRef = React.useRef<HTMLImageElement | null>(null)
|
|
46
47
|
const containerRef = React.useRef<HTMLDivElement | null>(null)
|
|
47
48
|
const [loaded, setLoaded] = React.useState(false)
|
|
@@ -123,12 +124,38 @@ export const ImageRenderer: React.FC<FileRendererProps> = ({
|
|
|
123
124
|
const container = containerRef.current
|
|
124
125
|
if (!container) return
|
|
125
126
|
let rafId = 0
|
|
127
|
+
// manual mode 視覺中心保持需要前次容器尺寸(比對 delta 反推新 position)
|
|
128
|
+
let prevW = container.clientWidth
|
|
129
|
+
let prevH = container.clientHeight
|
|
126
130
|
const obs = new ResizeObserver(() => {
|
|
127
131
|
if (rafId) cancelAnimationFrame(rafId)
|
|
128
132
|
rafId = requestAnimationFrame(() => {
|
|
129
133
|
rafId = 0
|
|
130
134
|
const mode = lastFitModeRef.current
|
|
131
|
-
|
|
135
|
+
const newW = container.clientWidth
|
|
136
|
+
const newH = container.clientHeight
|
|
137
|
+
if (mode === 'manual') {
|
|
138
|
+
// 2026-07-08 Q2 補洞(user「畫布在各種情況都能完美適應」):manual zoom 下容器 resize
|
|
139
|
+
//(InfoPanel 開合 / Filmstrip 增減 / 視窗 RWD / 裝置旋轉 — 同一咽喉點全涵蓋)
|
|
140
|
+
// = **scale 不變、視覺中心點保持**(Figma / Photoshop canonical;原本 early-return
|
|
141
|
+
// 什麼都不做 → transform 座標絕對值,容器變窄內容偏移)。
|
|
142
|
+
// 幾何:舊容器中心對到的內容點 (cx,cy),resize 後把同一內容點放到新容器中心。
|
|
143
|
+
const api = apiRef.current
|
|
144
|
+
if (api && (newW !== prevW || newH !== prevH)) {
|
|
145
|
+
const { positionX, positionY, scale } = api.state
|
|
146
|
+
const cx = (prevW / 2 - positionX) / scale
|
|
147
|
+
const cy = (prevH / 2 - positionY) / scale
|
|
148
|
+
programmaticZoomRef.current = true
|
|
149
|
+
api.setTransform(newW / 2 - cx * scale, newH / 2 - cy * scale, scale, 0)
|
|
150
|
+
// setTransform 同步無動畫;下一 frame 解 flag(防 onTransform 誤標 manual→已是 manual,保守處理)
|
|
151
|
+
requestAnimationFrame(() => { programmaticZoomRef.current = false })
|
|
152
|
+
}
|
|
153
|
+
prevW = newW
|
|
154
|
+
prevH = newH
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
prevW = newW
|
|
158
|
+
prevH = newH
|
|
132
159
|
const scale = computeFitScale(mode)
|
|
133
160
|
if (scale == null) return
|
|
134
161
|
onZoomChange(clampToPct(scale))
|
|
@@ -196,8 +223,8 @@ export const ImageRenderer: React.FC<FileRendererProps> = ({
|
|
|
196
223
|
return (
|
|
197
224
|
<div ref={containerRef} className="w-full h-full overflow-hidden" onDoubleClick={handleDoubleClick}>
|
|
198
225
|
<TransformWrapper
|
|
199
|
-
//
|
|
200
|
-
ref={apiRef
|
|
226
|
+
// TransformWrapper ref = ReactZoomPanPinchContentRef(dist/index.d.ts:351 + export L399);型別有 export → 用真型別,不需 as any
|
|
227
|
+
ref={apiRef}
|
|
201
228
|
initialScale={1}
|
|
202
229
|
minScale={MIN_SCALE}
|
|
203
230
|
maxScale={MAX_SCALE}
|
|
@@ -211,9 +238,10 @@ export const ImageRenderer: React.FC<FileRendererProps> = ({
|
|
|
211
238
|
// Wheel zoom canonical:
|
|
212
239
|
// - `step: 0.03` = 每 tick ~3% scale,對齊 Figma / Preview.app 細緻度
|
|
213
240
|
// (原 0.1 = 10% 太粗,接近 Google Slides 離散慣例)
|
|
214
|
-
// -
|
|
215
|
-
//
|
|
216
|
-
//
|
|
241
|
+
// - wheel zoom = `scale + delta × step` 加法線性(handleCalculateWheelZoom,
|
|
242
|
+
// dist/index.esm.js:1265;非 multiplicative/log —— 乘算 exp 只用於 setup smooth 的按鈕 zoom path)
|
|
243
|
+
// 註:lib 型別無 `smoothStep` / `smoothScroll` key(僅 setup `smooth?:boolean` 走按鈕 exp、
|
|
244
|
+
// 不影響 wheel);trackpad 平滑度目前無對應 wheel prop 可調
|
|
217
245
|
wheel={{ step: 0.03 }}
|
|
218
246
|
// Q3 雙擊改自定 handler:lib `mode: 'reset'` 永遠 reset 到 initialScale=1 → 100%,
|
|
219
247
|
// 失去 fit ↔ 100% toggle UX(Apple Photos / Drive canonical)。disabled lib 預設 + 自定 onDoubleClick。
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
// shadow / padding / size 全由 consumer 透過 className 決定;無 own variant /
|
|
5
5
|
// state / size。Overview + VisualVariants 兩個 story 已涵蓋 consumer 兩種
|
|
6
6
|
// 常見視覺(ProfileCard 亮色 vs OverflowIndicator 深色 tooltip)。
|
|
7
|
+
// @story-trait-allow: missing-opensnapshot — 同 hover-card.stories.tsx 檔頭 rationale:
|
|
8
|
+
// hover-trigger 行為 primitive 的 anatomy doc 內多個 inline hover demo,forced defaultOpen
|
|
9
|
+
// 會讓多張浮層互疊蓋住規格表;open-state 視覺快照由 consumer canonical(ProfileCard /
|
|
10
|
+
// OverflowIndicator)各自的 OpenSnapshot 涵蓋。
|
|
7
11
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
8
12
|
import { HoverCard, HoverCardTrigger, HoverCardContent } from './hover-card'
|
|
9
13
|
import { Avatar } from '@/design-system/components/Avatar/avatar'
|
|
@@ -27,7 +31,7 @@ export const Overview: Story = {
|
|
|
27
31
|
<div className="flex items-center gap-6">
|
|
28
32
|
<HoverCard>
|
|
29
33
|
<HoverCardTrigger asChild>
|
|
30
|
-
<Avatar alt="Ada Chen" size={40} />
|
|
34
|
+
<Avatar src="https://i.pravatar.cc/80?u=ada-chen" alt="Ada Chen" size={40} />
|
|
31
35
|
</HoverCardTrigger>
|
|
32
36
|
<HoverCardContent className="bg-surface-raised border border-border rounded-lg p-4 shadow-[var(--elevation-200)]">
|
|
33
37
|
<div className="flex flex-col gap-2 w-56">
|
|
@@ -139,7 +143,7 @@ export const Accessibility = {
|
|
|
139
143
|
render: () => (
|
|
140
144
|
<div className="max-w-3xl text-body text-fg-secondary">
|
|
141
145
|
<h3 className="text-h5 text-foreground mb-2">無障礙設計</h3>
|
|
142
|
-
<p className="whitespace-pre-line">{"ARIA 與互動模式:Radix HoverCard 刻意不設角色語意 / aria-* 屬性(定位為 sighted-mouse-user 增強,卡片內容不暴露給輔助技術),只提供 hover / focus 開、Esc 關 + data-state。觸發元素的可聚焦性需 consumer 自補(例如 Avatar 的 hoverCard 在元件內手動加 tabIndex=0 + role=\"
|
|
146
|
+
<p className="whitespace-pre-line">{"ARIA 與互動模式:Radix HoverCard 刻意不設角色語意 / aria-* 屬性(定位為 sighted-mouse-user 增強,卡片內容不暴露給輔助技術),只提供 hover / focus 開、Esc 關 + data-state。觸發元素的可聚焦性需 consumer 自補(例如 Avatar 的 hoverCard 在元件內手動加 tabIndex=0 + role=\"img\" + aria-label;2026-07-06 拆除 role=\"button\" / aria-haspopup=\"dialog\" 空承諾 — 無 Enter/Space 行為卻宣告 popup ARIA = 對輔助技術的假承諾,對齊 avatar.tsx:252-256 + hover-card.spec.md:144)。\n\n鍵盤操作:\n\n- Tab — 焦點移到觸發元素\n- Hover 或 focus — 開啟浮層卡片\n- Esc — 關閉卡片\n\n焦點行為:這是非阻斷(non-modal)的浮層,focus 會開啟卡片但不會把焦點鎖在裡面;焦點仍留在觸發元素,按 Esc 即可關閉。觸發元素 focus 時顯示可見的外框(2px 實線,使用 --ring 色),與整個設計系統的 focus-visible 樣式一致。\n\n鍵盤限制:Radix HoverCard 會把卡片內所有可 Tab 的節點強制設成 tabindex=\"-1\",因此卡片內部的按鈕 / 連結鍵盤 Tab 到不了 — 鍵盤使用者僅能 focus 觸發元素開卡 + Esc 關卡,無法進入卡片操作互動內容。關鍵互動勿放 HoverCard 內,需鍵盤可達的互動浮層請用 Popover(click 觸發)。\n\n驗證:Storybook 無障礙檢測面板應為 0 項嚴重違規;文字對比至少 4.5:1、UI 元素對比至少 3:1,符合 WCAG AA。"}</p>
|
|
143
147
|
</div>
|
|
144
148
|
),
|
|
145
149
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// @principles-rationale: Merged WhenToUse + VsTooltipRule + NotForCriticalInfoRule
|
|
2
2
|
// into a single `UsageGuidance` story (3 sections) per 2026-04-26 user mandate to
|
|
3
3
|
// consolidate decision-related stories. PureBehaviorPrimitiveRule kept as separate principle.
|
|
4
|
+
// @story-trait-allow: missing-opensnapshot — 同 hover-card.stories.tsx 檔頭 rationale:
|
|
5
|
+
// 原則頁多個 inline hover demo,forced defaultOpen 會多張浮層互疊;open-state 快照由
|
|
6
|
+
// consumer canonical(ProfileCard / OverflowIndicator)的 OpenSnapshot 涵蓋。
|
|
4
7
|
import React from 'react'
|
|
5
8
|
import LinkTo from '@storybook/addon-links/react'
|
|
6
9
|
import type { Meta, StoryObj } from '@storybook/react'
|
|
@@ -121,7 +124,7 @@ export const UsageGuidance: Story = {
|
|
|
121
124
|
<HoverCardContent className="bg-surface-raised border border-border rounded-lg p-4 shadow-[var(--elevation-200)]">
|
|
122
125
|
<div className="flex flex-col gap-2 w-64">
|
|
123
126
|
<div className="flex items-center gap-3">
|
|
124
|
-
<Avatar alt="Ada Chen" size={40} />
|
|
127
|
+
<Avatar src="https://i.pravatar.cc/80?u=ada-chen" alt="Ada Chen" size={40} />
|
|
125
128
|
<div>
|
|
126
129
|
<div className="text-body font-medium">Ada Chen</div>
|
|
127
130
|
<div className="text-caption text-fg-muted">Design Engineer</div>
|
|
@@ -149,7 +152,7 @@ export const UsageGuidance: Story = {
|
|
|
149
152
|
<div className="flex flex-col gap-1">
|
|
150
153
|
<HoverCard>
|
|
151
154
|
<HoverCardTrigger asChild>
|
|
152
|
-
<Avatar alt="Ada Chen" size={32} />
|
|
155
|
+
<Avatar src="https://i.pravatar.cc/64?u=ada-chen" alt="Ada Chen" size={32} />
|
|
153
156
|
</HoverCardTrigger>
|
|
154
157
|
<HoverCardContent className="bg-surface-raised border border-border rounded-lg p-3">
|
|
155
158
|
<div className="flex flex-col gap-2 w-56">
|
|
@@ -206,7 +209,7 @@ export const PureBehaviorPrimitiveRule: Story = {
|
|
|
206
209
|
note="人員類 Avatar 透過 `hoverCard` prop 自動 wrap HoverCardTrigger,不需要 consumer 手動組合。統一人員 hover 提供 ProfileCard 的體驗"
|
|
207
210
|
>
|
|
208
211
|
<div className="flex items-center gap-3">
|
|
209
|
-
<Avatar alt="Ada Chen" size={40} hoverCard={
|
|
212
|
+
<Avatar src="https://i.pravatar.cc/80?u=ada-chen" alt="Ada Chen" size={40} hoverCard={
|
|
210
213
|
<div className="flex flex-col gap-2 w-56">
|
|
211
214
|
<div className="text-body font-medium">Ada Chen</div>
|
|
212
215
|
<div className="text-caption text-fg-muted">Design Engineer · 台北</div>
|
|
@@ -19,10 +19,10 @@ Hover 觸發的**可互動浮層**,基於 Radix HoverCard。
|
|
|
19
19
|
|
|
20
20
|
**實作基礎**:基於 Radix HoverCard——純行為 primitive,只提供觸發邏輯、定位、動畫,不含視覺樣式。
|
|
21
21
|
|
|
22
|
-
**分類**:Internal primitive
|
|
22
|
+
**分類**:Internal primitive——不進 root barrel front-door、不裸用;DS 內由 Avatar(`hoverCard`)/ OverflowIndicator / ProfileCard 場景組合消費;consumer 需要時可經 per-component subpath 包裝並自行確認後使用(`.claude/rules/ui-development.md`「Root barrel front-door 排除」SSOT,internal ≠ 禁用)(frontmatter `isInternal`:root barrel 排除,storybook `Design System/Internal/HoverCard`)。
|
|
23
23
|
|
|
24
|
-
- **是**:hover
|
|
25
|
-
- **不是**:Tooltip(純文字提示、不可互動、hover
|
|
24
|
+
- **是**:hover 顯示可互動內容(按鈕、連結、可選取文字——**滑鼠互動**;鍵盤進不了卡片,見「A11y 預設」)的浮層容器,定位為滑鼠 / focus 使用者的補充閱讀 surface
|
|
25
|
+
- **不是**:Tooltip(純文字提示、不可互動、hover 離開即消失);也不是鍵盤可操作的互動浮層(那是 Popover)
|
|
26
26
|
|
|
27
27
|
**Layout Family**:非上述 family — composite / multi-section(多區塊組合,自 own layout)。
|
|
28
28
|
|
|
@@ -53,12 +53,12 @@ Hover 觸發的**可互動浮層**,基於 Radix HoverCard。
|
|
|
53
53
|
| | HoverCard | Tooltip |
|
|
54
54
|
|---|---|---|
|
|
55
55
|
| 觸發 | hover | hover |
|
|
56
|
-
| **內容可互動** |
|
|
56
|
+
| **內容可互動** | 是——限滑鼠(Radix 設 content 內 tabbable node 為 `tabindex="-1"`,鍵盤不可達,見「A11y 預設」) | 否(純文字) |
|
|
57
57
|
| 停留行為 | 滑鼠移到浮層上不消失 | 滑鼠離開 trigger 即消失 |
|
|
58
58
|
| 視覺樣式 | 由 consumer 決定(亮色 card 或深色 tooltip 風格皆可) | 統一深色背景 |
|
|
59
59
|
| 典型用例 | ProfileCard、內容預覽、溢出清單 | icon 用途、shortcut、截斷文字補全 |
|
|
60
60
|
|
|
61
|
-
**Fallback**:需要放按鈕 /
|
|
61
|
+
**Fallback**:需要放按鈕 / 連結(滑鼠增強)→ HoverCard;鍵盤必須可操作的互動浮層 → Popover;一句話純文字 → Tooltip。
|
|
62
62
|
|
|
63
63
|
**本節是 HoverCard vs Tooltip 的 SSOT**,Tooltip spec 指回本節。
|
|
64
64
|
|
|
@@ -11,7 +11,7 @@ import { HoverCard, HoverCardTrigger, HoverCardContent } from './hover-card'
|
|
|
11
11
|
import { Avatar } from '@/design-system/components/Avatar/avatar'
|
|
12
12
|
import { Button } from '@/design-system/components/Button/button'
|
|
13
13
|
import { ProfileCard } from '@/design-system/components/ProfileCard/profile-card'
|
|
14
|
-
import {
|
|
14
|
+
import { MOTION_DELAY_RICH_MS, MOTION_DELAY_CLOSE_MS } from '@/design-system/tokens/motion/motion'
|
|
15
15
|
|
|
16
16
|
const meta: Meta = {
|
|
17
17
|
title: 'Design System/Internal/HoverCard/展示',
|
|
@@ -45,7 +45,7 @@ export const PersonProfileCard: Story = {
|
|
|
45
45
|
<HoverCard>
|
|
46
46
|
<HoverCardTrigger asChild>
|
|
47
47
|
<button type="button" aria-label="Ada Chen 個人資訊" className="cursor-pointer rounded-full">
|
|
48
|
-
<Avatar alt="Ada Chen" color="indigo" size={28} />
|
|
48
|
+
<Avatar src="https://i.pravatar.cc/56?u=ada-chen" alt="Ada Chen" color="indigo" size={28} />
|
|
49
49
|
</button>
|
|
50
50
|
</HoverCardTrigger>
|
|
51
51
|
<HoverCardContent
|
|
@@ -54,7 +54,7 @@ export const PersonProfileCard: Story = {
|
|
|
54
54
|
>
|
|
55
55
|
<ProfileCard
|
|
56
56
|
name="Ada Chen"
|
|
57
|
-
avatar={{ alt: 'Ada Chen', color: 'indigo' }}
|
|
57
|
+
avatar={{ src: 'https://i.pravatar.cc/80?u=ada-chen', alt: 'Ada Chen', color: 'indigo' }}
|
|
58
58
|
subtitle="Design Engineer · Frontend"
|
|
59
59
|
status="online"
|
|
60
60
|
statusMessage="正在處理 login 頁重構"
|
|
@@ -130,9 +130,9 @@ export const OverflowList: Story = {
|
|
|
130
130
|
name: '溢出清單',
|
|
131
131
|
render: () => {
|
|
132
132
|
const hidden = [
|
|
133
|
-
{ name: '黃怡君', color: 'turquoise' as const },
|
|
134
|
-
{ name: '王文彬', color: 'purple' as const },
|
|
135
|
-
{ name: '李思妤', color: 'yellow' as const },
|
|
133
|
+
{ name: '黃怡君', color: 'turquoise' as const, avatarUrl: 'https://i.pravatar.cc/40?u=huang-yijun' },
|
|
134
|
+
{ name: '王文彬', color: 'purple' as const, avatarUrl: 'https://i.pravatar.cc/40?u=wang-wenbin' },
|
|
135
|
+
{ name: '李思妤', color: 'yellow' as const, avatarUrl: 'https://i.pravatar.cc/40?u=li-siyu' },
|
|
136
136
|
]
|
|
137
137
|
return (
|
|
138
138
|
<div className="flex flex-col gap-3 max-w-md">
|
|
@@ -140,10 +140,10 @@ export const OverflowList: Story = {
|
|
|
140
140
|
此樣式由 OverflowIndicator 使用(深色 tooltip-style)—— +N hover 看完整清單。
|
|
141
141
|
</p>
|
|
142
142
|
<div className="flex items-center gap-1">
|
|
143
|
-
<Avatar alt="Ada Chen" color="indigo" size={24} />
|
|
144
|
-
<Avatar alt="張美真" color="magenta" size={24} />
|
|
145
|
-
<Avatar alt="林伯彥" color="green" size={24} />
|
|
146
|
-
<HoverCard openDelay={
|
|
143
|
+
<Avatar src="https://i.pravatar.cc/48?u=ada-chen" alt="Ada Chen" color="indigo" size={24} />
|
|
144
|
+
<Avatar src="https://i.pravatar.cc/48?u=zhang-meizhen" alt="張美真" color="magenta" size={24} />
|
|
145
|
+
<Avatar src="https://i.pravatar.cc/48?u=lin-boyan" alt="林伯彥" color="green" size={24} />
|
|
146
|
+
<HoverCard openDelay={MOTION_DELAY_RICH_MS} closeDelay={MOTION_DELAY_CLOSE_MS}>
|
|
147
147
|
<HoverCardTrigger asChild>
|
|
148
148
|
<button
|
|
149
149
|
type="button"
|
|
@@ -159,7 +159,7 @@ export const OverflowList: Story = {
|
|
|
159
159
|
<div className="flex flex-col gap-1 min-w-[160px]">
|
|
160
160
|
{hidden.map((p) => (
|
|
161
161
|
<div key={p.name} className="flex items-center gap-2 text-caption">
|
|
162
|
-
<Avatar alt={p.name} color={p.color} size={20} />
|
|
162
|
+
<Avatar src={p.avatarUrl} alt={p.name} color={p.color} size={20} />
|
|
163
163
|
<span>{p.name}</span>
|
|
164
164
|
</div>
|
|
165
165
|
))}
|
|
@@ -247,10 +247,10 @@ export const TriggerShowcase: Story = {
|
|
|
247
247
|
<div className="flex items-center gap-6 flex-wrap">
|
|
248
248
|
<div className="flex flex-col gap-2">
|
|
249
249
|
<span className="text-footnote text-fg-muted font-mono">trigger: Avatar</span>
|
|
250
|
-
<HoverCard openDelay={
|
|
250
|
+
<HoverCard openDelay={MOTION_DELAY_RICH_MS} closeDelay={MOTION_DELAY_CLOSE_MS}>
|
|
251
251
|
<HoverCardTrigger asChild>
|
|
252
252
|
<button type="button" aria-label="Ada Chen 個人資訊" className="cursor-pointer rounded-full">
|
|
253
|
-
<Avatar alt="Ada Chen" color="indigo" size={32} />
|
|
253
|
+
<Avatar src="https://i.pravatar.cc/64?u=ada-chen" alt="Ada Chen" color="indigo" size={32} />
|
|
254
254
|
</button>
|
|
255
255
|
</HoverCardTrigger>
|
|
256
256
|
<HoverCardContent className="bg-surface-raised border border-border rounded-lg p-3">
|
|
@@ -261,7 +261,7 @@ export const TriggerShowcase: Story = {
|
|
|
261
261
|
|
|
262
262
|
<div className="flex flex-col gap-2">
|
|
263
263
|
<span className="text-footnote text-fg-muted font-mono">trigger: Button</span>
|
|
264
|
-
<HoverCard openDelay={
|
|
264
|
+
<HoverCard openDelay={MOTION_DELAY_RICH_MS} closeDelay={MOTION_DELAY_CLOSE_MS}>
|
|
265
265
|
<HoverCardTrigger asChild>
|
|
266
266
|
<Button variant="tertiary" size="sm">
|
|
267
267
|
查看位置
|
|
@@ -278,7 +278,7 @@ export const TriggerShowcase: Story = {
|
|
|
278
278
|
|
|
279
279
|
<div className="flex flex-col gap-2">
|
|
280
280
|
<span className="text-footnote text-fg-muted font-mono">trigger: text link</span>
|
|
281
|
-
<HoverCard openDelay={
|
|
281
|
+
<HoverCard openDelay={MOTION_DELAY_RICH_MS} closeDelay={MOTION_DELAY_CLOSE_MS}>
|
|
282
282
|
<HoverCardTrigger asChild>
|
|
283
283
|
<a
|
|
284
284
|
href="#"
|
|
@@ -296,7 +296,7 @@ export const TriggerShowcase: Story = {
|
|
|
296
296
|
</div>
|
|
297
297
|
|
|
298
298
|
<div className="text-footnote text-fg-muted">
|
|
299
|
-
預設 delay 已內建(<code className="font-mono mx-1">
|
|
299
|
+
預設 delay 已內建(<code className="font-mono mx-1">MOTION_DELAY_RICH_MS / MOTION_DELAY_CLOSE_MS</code> SSOT),特殊 tier 才需 override
|
|
300
300
|
(motion.spec.md,當前 700ms / 200ms)——避免 hover 過路誤觸發 fetch waterfall;close 延遲讓 user 誤滑出可回來。
|
|
301
301
|
</div>
|
|
302
302
|
</div>
|
|
@@ -7,7 +7,8 @@ import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
|
|
|
7
7
|
|
|
8
8
|
import { cn } from "@/lib/utils"
|
|
9
9
|
import { OVERLAY_SIDE_OFFSET } from "@/design-system/tokens/elevation/overlay-geometry"
|
|
10
|
-
import {
|
|
10
|
+
import { MOTION_DELAY_RICH_MS, MOTION_DELAY_CLOSE_MS } from "@/design-system/tokens/motion/motion"
|
|
11
|
+
import { overlayMotion } from "@/design-system/tokens/motion/overlay-motion"
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* HoverCard — hover 顯示可互動內容的浮層(行為 primitive)
|
|
@@ -25,7 +26,7 @@ import { HOVER_DELAY_RICH_MS, HOVER_DELAY_CLOSE_MS } from "@/design-system/token
|
|
|
25
26
|
// (rich 700ms open / close 200ms)— 對齊 tooltip.tsx 同 pattern(2026-05-18 拍板 #3A)。
|
|
26
27
|
// 原裸用 Radix 預設 open 700 / close 300:open 巧合同值、close 300 ≠ canonical 200(drift)。
|
|
27
28
|
// Consumer 仍可 per-instance override(Avatar 同值傳入、OverflowIndicator 傳 plain)。
|
|
28
|
-
const HoverCard = ({ openDelay =
|
|
29
|
+
const HoverCard = ({ openDelay = MOTION_DELAY_RICH_MS, closeDelay = MOTION_DELAY_CLOSE_MS, ...props }: React.ComponentProps<typeof HoverCardPrimitive.Root>) => (
|
|
29
30
|
<HoverCardPrimitive.Root openDelay={openDelay} closeDelay={closeDelay} {...props} />
|
|
30
31
|
)
|
|
31
32
|
|
|
@@ -56,7 +57,8 @@ const HoverCardContent = React.forwardRef<
|
|
|
56
57
|
"max-h-[var(--radix-hover-card-content-available-height,100vh)] flex flex-col overflow-hidden min-h-0",
|
|
57
58
|
// 2026-07-05 補 motion-reduce 豁免:對齊 overlay 家族 canonical(popover.tsx / dialog.tsx /
|
|
58
59
|
// dropdown-menu.tsx / tooltip.tsx 同位置寫法)— prefers-reduced-motion 下停用進出場動畫。
|
|
59
|
-
|
|
60
|
+
overlayMotion,
|
|
61
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
60
62
|
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
61
63
|
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
62
64
|
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// AUTO-GENERATED by scripts/gen-component-indexes.mjs — DO NOT EDIT MANUALLY.
|
|
2
|
+
// Re-export pattern enables package.json exports map subpath:
|
|
3
|
+
// import { ... } from '@qijenchen/design-system/{components,patterns}/<Dir>'
|
|
4
|
+
// Regenerate: node scripts/gen-component-indexes.mjs
|
|
5
|
+
export * from './inline-edit'
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: InlineEdit
|
|
3
|
+
family: composite
|
|
4
|
+
variants: {}
|
|
5
|
+
sizes: {}
|
|
6
|
+
traits:
|
|
7
|
+
- hasInteractiveStates
|
|
8
|
+
benchmark:
|
|
9
|
+
- Atlassian inline-edit read-view: https://github.com/pioug/atlassian-frontend-mirror/blob/main/design-system/inline-edit/src/internal/read-view.tsx
|
|
10
|
+
- PatternFly inline-edit: https://www.patternfly.org/components/inline-edit/design-guidelines
|
|
11
|
+
- Notion database title cell: https://www.notion.com/help/intro-to-databases
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# InlineEdit 設計原則
|
|
15
|
+
|
|
16
|
+
**Layout Family**:composite。**edit 態委派 Family 4(Field Control Layout)**——直接渲染 Field 控件(預設 `<Input>`),幾何/邊框/focus 全繼承 `fieldWrapperStyles`,不新增 layout primitive。**read 態為 self-contained 純文字承載 + 隱形 Pressable**(絕對定位透明 `<button>`),只負責「看起來像純文字、hover 才有 affordance、點擊進 edit」。
|
|
17
|
+
|
|
18
|
+
## 定位
|
|
19
|
+
|
|
20
|
+
**實作基礎:自建 + composes Field 家族**(`renderEdit` 預設 `<Input>`)。
|
|
21
|
+
|
|
22
|
+
為何自建而非 Field prop/variant:InlineEdit 的本質是**兩個顯示態之間的 toggle 狀態機**(純文字 read view ↔ 完整 Field 控件 edit view)+ **就地編輯的進入/退出/commit/cancel 生命週期**。這是 Field 控件之上的**互動編排層**,不是某個 Field 控件的視覺 variant——Field 控件本身永遠是「一直可編輯的輸入框」,InlineEdit 才決定「何時顯示成純文字、何時顯示成輸入框」。故它 **compose** Field(消費 `<Input>` 當 edit 態),而非擴充 Field。
|
|
23
|
+
|
|
24
|
+
一句話:**Field 控件回答「這個值長什麼樣、怎麼編輯」;InlineEdit 回答「這個值平常是純文字,想改時才變成 Field 控件」**。
|
|
25
|
+
|
|
26
|
+
## 互動模型(read / hover / edit 三態 canonical)
|
|
27
|
+
|
|
28
|
+
| 態 | 視覺 | 行為 |
|
|
29
|
+
|----|------|------|
|
|
30
|
+
| **read**(靜態) | 純文字,寬度 fill 容器,**透明邊框(預留、不可見)、無底色**(視覺 = 純文字) | 隱形 Pressable 提供 click / 鍵盤 Tab focus / Enter·Space 進 edit |
|
|
31
|
+
| **read + hover** | **灰色底色 `bg-neutral-hover` + `rounded-md`**(**非邊框**)+ cursor | 提示「這裡可點擊編輯」 |
|
|
32
|
+
| **read + focus-visible**(鍵盤) | 外框染 **`border-primary` 藍框**(消費 Field edit focus 語言,**非 Button ring**) | Enter / Space → 進 edit |
|
|
33
|
+
| **edit** | **真正的 Field 控件**(完整 chrome:border + focus 藍框 + input) | 回歸 field-controls edit canonical;blur/Enter → commit、Esc → cancel |
|
|
34
|
+
|
|
35
|
+
**退出 edit 態**:
|
|
36
|
+
- **blur**(點外面)→ `commit`(草稿有變更才觸發 `onCommit`)
|
|
37
|
+
- **Enter** → `commit`
|
|
38
|
+
- **Esc** → `cancel`(放棄草稿,還原 read 態,不 commit)
|
|
39
|
+
- 任一路徑結算後,focus 送回 read 按鈕(鍵盤焦點不遺失)
|
|
40
|
+
|
|
41
|
+
## 幾何典範(2026-07-09 user 拍板,每值消費既有 SSOT,零自創)
|
|
42
|
+
|
|
43
|
+
| 面向 | 定義 | 消費的 SSOT |
|
|
44
|
+
|------|------|------------|
|
|
45
|
+
| 外框(read/edit 共用) | `-mx-[var(--field-px)]` + `w-[calc(100% + 2*var(--field-px))]` → 左右各對稱外擴 `--field-px`、填滿容器 | `--field-px` |
|
|
46
|
+
| 水平內距 | 文字 `px-[var(--field-px)]`(read=edit 同值)→ 文字左緣與相鄰內容切齊、底色/框外擴 gutter | `--field-px` |
|
|
47
|
+
| 垂直 | `min-h-[var(--field-height-{size})]` + 垂直置中(**無 py token**——Field 家族本就以「固定高度 + 置中」定義呼吸,非 padding;故 InlineEdit 同法,隨 size/density 自動縮放不漂移) | `--field-height-{sm/md/lg}` |
|
|
48
|
+
| hover(read) | `bg-neutral-hover` + `rounded-md`(底色 tint,**非邊框**) | color token + field radius |
|
|
49
|
+
| focus(read/鍵盤) | 透明 `border` 預留、focus 染 `border-primary`(Field focus 藍框語言,**非 ring**) | Field focus canonical |
|
|
50
|
+
| edit | 真正 `<Input>`(border + focus + input),width=fill 填滿外框 | field-controls edit canonical |
|
|
51
|
+
|
|
52
|
+
**「底色範圍 = 輸入框範圍」不變量**:read 與 edit 兩態共用同一外框幾何(同 `-mx`+calc 寬、同 `min-h`=field-height、同 1px 邊框盒),故 hover 底色與 edit 輸入框逐 pixel 對齊、態切換零位移。(舊版 read 外框 `w-full`+`-mx` 疊加 → 右側短一截、整塊左偏,已修。)
|
|
53
|
+
|
|
54
|
+
**尺寸預設 = `sm`**(2026-07-09 user 拍板):read 態無邊框、視覺即純文字,尺寸過大會使版面鬆散;consumer 可傳 `size` 覆寫,標題場景另用 `readClassName` 疊大字級。
|
|
55
|
+
|
|
56
|
+
**核心不變量:read 態 hover = 背景 tint;read 態 focus = Field 藍框(`border-primary`),與 edit 態同源。** hover(滑鼠懸停)用底色、focus(鍵盤選中)用藍框——藍框是 edit focus 的預覽,read→edit 一路都是藍框、不會 ring→框跳一下;靜態(無 hover/focus)則透明邊框不可見 = 純文字。
|
|
57
|
+
|
|
58
|
+
## 型別化 read view + 多行 + 鍵盤 SSOT(2026-07-09 L2)
|
|
59
|
+
|
|
60
|
+
InlineEdit 為**泛型 `<T>`**(非只 string):`value: T` / `onCommit: (next: T) => void`。三個 render 面向:
|
|
61
|
+
|
|
62
|
+
| Prop | 作用 | 預設 | SSOT 鐵律 |
|
|
63
|
+
|---|---|---|---|
|
|
64
|
+
| `renderRead?(value)` | read 態格式化顯示 | `String(value)`(空 → placeholder) | 要顯 **Tag / 日期 / option label**,**渲染對應控件的 `mode="display"`**(如 `<Select mode="display" display="tag">`),**禁自刻**——格式化 SSOT 住在該控件的 display mode |
|
|
65
|
+
| `renderEdit?(props)` | edit 態控件 | `multiline`→`<Textarea>` / 否則 `<Input>` | 非 string 值必傳(預設路徑僅 string) |
|
|
66
|
+
| `multiline?` | 多行(Jira description 類) | false | read `items-start` 換行、edit Textarea(Enter=換行、Cmd/Ctrl+Enter 或 blur 提交) |
|
|
67
|
+
|
|
68
|
+
**Tag 顯示的 SSOT 對齊(user 2026-07-09 問)**:「select 值 → Tag」的格式化住在 **Select 的 `mode="display" display="tag"`**(`select.tsx` ReadonlyDisplay Tag 分支)—— 這正是 **DataTable 格子消費的同一份**(`cell-registry.tsx` SelectCell display 分支)。InlineEdit read 態顯 Tag = `renderRead={(v) => <Select mode="display" display="tag" value={v} options={opts} />}`,**read 與 edit 用同一個 Select、只切 mode → 格式零分歧**。此即「乾淨對齊」:InlineEdit 從不重刻 Tag,格式化永遠委派給控件的 display mode。示範見 `inline-edit.stories.tsx` `SelectTagField`。
|
|
69
|
+
|
|
70
|
+
**鍵盤結算 SSOT**:Enter=commit / Esc=cancel + **中文 IME 組字 guard** 抽為 `../Field/field-edit-keys.ts` `makeEditSettleKeyHandler`,**InlineEdit 與 DataTable cell(`cell-registry.tsx`)同源消費**(2026-07-09 抽出;原本 InlineEdit 漏 IME guard = 中文選字 Enter 誤提交半截組字 bug,現已修)。edit-in-place SSOT 研究(2026-07-09,6-agent + 世界級)結論:兩 host 的 draft 擁有權 / state locus(單實例 vs 表格級 keyed)/ focus model 根本不同,故**不抽大共用狀態機**(違 M21 + 破壞表格虛擬捲動效能),只抽此純鍵盤結算片段。世界級共識 = **field 控件 + 型別 registry 可共用,edit-in-place orchestration 各自實作**(見 `../Field/field-controls.spec.md`「Field 框架地圖」段 cite)。
|
|
71
|
+
|
|
72
|
+
## 世界級對照
|
|
73
|
+
|
|
74
|
+
| DS | read 態 | hover affordance | 進 edit | 鍵盤 |
|
|
75
|
+
|----|---------|-----------------|---------|------|
|
|
76
|
+
| **Atlassian inline-edit**([read-view.tsx](https://github.com/pioug/atlassian-frontend-mirror/blob/main/design-system/inline-edit/src/internal/read-view.tsx)) | 純值 + 隱形 Pressable | **背景 tint**(`background.neutral.subtle.hovered`,**非邊框**) | click read view | 隱形 button focus + Enter;5px drag threshold 防選字誤觸 |
|
|
77
|
+
| **PatternFly inline-edit** | value 文字 | 可編輯提示 | value/input 二態 toggle | Enter commit / Esc cancel |
|
|
78
|
+
| **Notion title cell** | 純文字 | hover 浮 affordance | click 編輯 | — |
|
|
79
|
+
|
|
80
|
+
三家共識:read 態 = 純值、hover = 輕背景提示(非重邊框)、click 或鍵盤進 edit、edit = 真正的輸入控件。本 DS 對齊此模型;`bg-neutral-hover` = 本 DS 的「neutral subtle hover」等價 token。
|
|
81
|
+
|
|
82
|
+
**read 態 focus canonical**(2026-07-09):Atlassian read-view 的隱形 Pressable focus 時,外框顯 `color.border.focused` **實線 solid border**(非 ring)——[read-view.tsx](https://github.com/pioug/atlassian-frontend-mirror/blob/main/design-system/inline-edit/src/internal/read-view.tsx) `&:focus + div { borderColor: token('color.border.focused') }`。本 DS 對齊此語言:read focus = `border-primary`(= Field edit focus 藍框),使 read→edit focus 視覺連續,不引入第三種(ring)focus 語言。
|
|
83
|
+
|
|
84
|
+
**read 態 typography canonical**(2026-07-09):Atlassian inline-edit / PatternFly 將讀態字級**委派**給 consumer(read view 不強加字體,由外層 context 決定)。本 DS 更 opinionated——read 態預設消費 `fieldDisplayTextClass(size)`(sm/md→`text-body`,lg→`text-body-lg`,`../Field/field-wrapper.tsx` Field display 字體 SSOT),使 **plain 用法**(`<InlineEdit value onCommit />`)read 字級即 = edit `<Input>`(`text-body`)= `<Input mode="display">` 字級,三態零跳字;`readClassName`(如標題 `text-h4 font-bold`)僅作特例 override 疊其後。對齊 [Atlassian inline-edit read-view](https://github.com/pioug/atlassian-frontend-mirror/blob/main/design-system/inline-edit/src/internal/read-view.tsx) 讀態委派模型,並補上「plain 用法即 canonical」的預設。
|
|
85
|
+
|
|
86
|
+
**狀態 data 屬性**(可測性):外層容器 `data-editing`(read/edit 態)、read 元素 `data-empty`(空值)——供 audit / Playwright 斷言態切換與空值渲染(Dice UI data-attribute idiom,對齊 M32 pixel/DOM 可驗)。
|
|
87
|
+
|
|
88
|
+
## 何時用
|
|
89
|
+
|
|
90
|
+
- **標題就地編輯**:任務/文件/專案標題,平常是大標題純文字,點一下改(`as="h1"` + `readClassName` 帶標題字級)。
|
|
91
|
+
- **detail pane metadata 就地編輯**:單一值欄位(負責人備註、外部連結、簡短描述)平常純文字呈現,點擊即編。
|
|
92
|
+
- 語意 = **「這個值大部分時間是被閱讀的,偶爾才編輯」**——閱讀優先、編輯次要。
|
|
93
|
+
|
|
94
|
+
## 何時不用
|
|
95
|
+
|
|
96
|
+
- **表單欄位** → 直接用 `<Field>` + Field 控件(表單本來就是編輯情境,不需要 read 態偽裝成純文字)。
|
|
97
|
+
- **DataTable 密集可編輯格** → 用 naked cell(host cell 自管 border + focus、hover outline affordance;見 `field-controls.spec.md`「Cell(naked variant)」),不是每格包一個 InlineEdit 狀態機。
|
|
98
|
+
- **一律唯讀展示**(不可編) → 用 `<Input mode="display">` / 純文字,不需要 Pressable。
|
|
99
|
+
- **多欄位一次編輯** → 用表單(進入一次編輯全部欄位),不是逐欄 inline edit。
|
|
100
|
+
|
|
101
|
+
## 近親分界
|
|
102
|
+
|
|
103
|
+
| 元件 | 分界 |
|
|
104
|
+
|------|------|
|
|
105
|
+
| **Field 控件**(Input/Select/Textarea…) | 一直可編輯的輸入框;InlineEdit 讓它「平常是純文字,想改才現身」 |
|
|
106
|
+
| **`<Input mode="display">`** | 純展示(無互動、不可編);InlineEdit 的 read 態可點擊進 edit |
|
|
107
|
+
| **naked cell**(DataTable) | 表格密集網格的 cell-as-input,host cell 自管 chrome;InlineEdit 是單一 standalone 欄位的閱讀↔編輯 toggle |
|
|
108
|
+
|
|
109
|
+
## 常見誤解
|
|
110
|
+
|
|
111
|
+
- **「read 態 hover 該加邊框」** → 錯。hover = 背景 tint(`bg-neutral-hover`);邊框是 edit 態語言。
|
|
112
|
+
- **「edit 態要自己刻 input chrome」** → 錯。edit 態 = 真正的 Field 控件(預設 `<Input>`),完全回歸 field-controls edit canonical,不重刻。
|
|
113
|
+
- **「大標題 inline edit 要自己做 focus/commit/esc」** → 錯。用 InlineEdit,生命週期(commit/cancel/Esc/focus 返回)由 primitive 統一處理。
|
|
114
|
+
|
|
115
|
+
## 空值
|
|
116
|
+
|
|
117
|
+
`value` 為空字串 / null 時,read 態顯示 `placeholder`(若有)+ `text-fg-muted`;無 placeholder 則空白。edit 態進入時草稿初值 = 空字串,顯示空 input。(不用唯讀展示語言的半形 `-` dash——InlineEdit 是可編輯欄位,空值 = 「還沒填、點來填」,dash 是唯讀展示語言。)
|
|
118
|
+
|
|
119
|
+
## 驗證
|
|
120
|
+
|
|
121
|
+
InlineEdit 本身不含驗證邏輯;若 edit 態需要驗證,consumer 透過 `renderEdit` 傳入帶 `error` 的 Field 控件,並在 `onCommit` 做值檢查(reject 時可不呼叫真正的資料更新)。此分層對齊 field-controls「error 正交於 mode」原則。
|
|
122
|
+
|
|
123
|
+
## Loading
|
|
124
|
+
|
|
125
|
+
InlineEdit 不含 async loading state。若 commit 需要 async 儲存,consumer 在 `onCommit` 內自行處理 optimistic update / toast;edit 態的 debounce/loading 由 `renderEdit` 傳入的控件(如 `<Input loading>`)承擔。
|
|
126
|
+
|
|
127
|
+
## A11y 預設
|
|
128
|
+
|
|
129
|
+
**ARIA**:
|
|
130
|
+
- read 態隱形 Pressable = `<button type="button">`,帶 `aria-label`(預設「編輯 {label}」)——螢幕閱讀器朗讀「可編輯」意圖。
|
|
131
|
+
- read 態語意標籤(`as`,預設 `span`;標題傳 `h1`/`h2`)獨立於 button 之外——`as="h1"` 保留文件大綱結構(HTML 內容模型不允許 heading 巢狀於 button,故隱形 Pressable 與語意標籤分離,對齊 [read-view.tsx](https://github.com/pioug/atlassian-frontend-mirror/blob/main/design-system/inline-edit/src/internal/read-view.tsx) hidden-Pressable pattern)。
|
|
132
|
+
- edit 態控件帶 `aria-label`(= `label`)。
|
|
133
|
+
|
|
134
|
+
**Keyboard**:
|
|
135
|
+
| 鍵 | read 態 | edit 態 |
|
|
136
|
+
|----|---------|---------|
|
|
137
|
+
| `Tab` | focus read 按鈕(外框染 `border-primary` 藍框,**非** ring;對齊本 spec 其餘段) | 依控件內部 focus 行為 |
|
|
138
|
+
| `Enter` / `Space` | 進 edit | `Enter` commit |
|
|
139
|
+
| `Esc` | — | cancel(還原不 commit) |
|
|
140
|
+
|
|
141
|
+
**Focus 管理**:進 edit 態自動 focus 控件(`autoFocus`);commit/cancel 結算後 focus 返回 read 按鈕(焦點不遺失)。
|
|
142
|
+
|
|
143
|
+
## 禁止事項
|
|
144
|
+
|
|
145
|
+
- ❌ read 態 hover 用邊框——用 `bg-neutral-hover` 背景 tint(邊框是 edit 態語言)
|
|
146
|
+
- ❌ edit 態手刻 input chrome——用 Field 控件(預設 `<Input>`)
|
|
147
|
+
- ❌ 用 InlineEdit 包 DataTable 密集可編輯格——用 naked cell
|
|
148
|
+
- ❌ 空值顯示唯讀展示的半形 `-` dash——InlineEdit 是可編輯欄位,空值 = placeholder 或空白
|
|
149
|
+
- ❌ read 態語意標籤用 heading 時把它塞進 button——用隱形 Pressable 疊加,保留文件大綱
|