@qijenchen/design-system 0.1.0-beta.82 → 0.1.0-beta.84
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-panel.d.ts.map +1 -1
- package/dist/components/DataTable/data-table-filter-panel.js +607 -0
- package/dist/components/DataTable/data-table-filter-panel.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/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 +93 -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 +37 -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 +17 -5
- 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 +36 -0
- package/ds-canonical/fork/hooks/check_story_determinism.sh +39 -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 +20 -0
- package/ds-canonical/fork/preamble.md +22 -18
- package/ds-canonical/fork/skills/bug-fix-rhythm/SKILL.md +9 -3
- package/ds-canonical/fork/skills/code-quality-audit/SKILL.md +3 -3
- package/ds-canonical/fork/skills/delivery-handoff/SKILL.md +7 -0
- package/ds-canonical/fork/skills/performance-audit/SKILL.md +4 -2
- 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 +10 -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_benchmark_citation.sh +5 -3
- 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_ds_anchor_preflight.sh +17 -5
- 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 +36 -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_propose_without_benchmark.sh +6 -2
- package/ds-canonical/hooks/check_ssot_header_declaration.sh +55 -0
- package/ds-canonical/hooks/check_story_determinism.sh +39 -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_benchmark_citation.sh +3 -3
- 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_fork_user_plugin_install.sh +3 -1
- package/ds-canonical/hooks/tests/test_check_opacity_token_usage.sh +41 -2
- package/ds-canonical/hooks/tests/test_check_ssot_header_declaration.sh +72 -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/spec-rules.md +1 -1
- package/ds-canonical/rules/story-rules.md +2 -2
- package/ds-canonical/rules/ui-development.md +4 -2
- package/ds-canonical/skills/bug-fix-rhythm/SKILL.md +9 -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 +57 -68
- 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 +5 -0
- package/ds-canonical/skills/design-system-audit/SKILL.md +27 -26
- 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 +2 -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 +7 -5
- package/ds-canonical/skills/new-component/references/new-component-checklist.md +1 -1
- package/ds-canonical/skills/performance-audit/SKILL.md +2 -2
- 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 +10 -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 +11 -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-panel.tsx +4 -1
- 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 +32 -12
- package/src/components/DataTable/data-table.stories.tsx +16 -17
- package/src/components/DataTable/data-table.tsx +103 -60
- 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/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 +14 -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 +27 -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 +584 -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 +3 -3
- 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.spec.md +1 -1
- package/src/tokens/uiSize/uiSize.stories.tsx +4 -2
- package/src/tokens/utility-registry.json +160 -23
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-edit.d.ts","sourceRoot":"","sources":["../../../src/components/InlineEdit/inline-edit.tsx"],"names":[],"mappings":"AAoBA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gDAAgD,CAAA;AAO/E,yFAAyF;AACzF,MAAM,WAAW,qBAAqB,CAAC,CAAC,GAAG,MAAM;IAC/C,wCAAwC;IACxC,KAAK,EAAE,CAAC,CAAA;IACR,WAAW;IACX,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAA;IAC3B,uCAAuC;IACvC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,8BAA8B;IAC9B,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,2BAA2B;IAC3B,SAAS,EAAE,OAAO,CAAA;IAClB,mCAAmC;IACnC,IAAI,EAAE,SAAS,CAAA;IACf,4BAA4B;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,KAAK,OAAO,GAAG,MAAM,GAAG,GAAG,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAE7E,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,MAAM;IACzC,oFAAoF;IACpF,KAAK,EAAE,CAAC,CAAA;IACR,qCAAqC;IACrC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAA;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,SAAS,CAAA;IACjE;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAA;IAC1C,oGAAoG;IACpG,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mDAAmD;IACnD,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,mDAAmD;IACnD,EAAE,CAAC,EAAE,OAAO,CAAA;IACZ,0CAA0C;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,qBAAqB;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAuMD,QAAA,MAAM,UAAU,EAAuC,CAAC,CAAC,CAAC,GAAG,MAAM,EACjE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;CAAE,KAC5D,KAAK,CAAC,YAAY,CAAC,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAGnD,OAAO,EAAE,UAAU,EAAE,CAAA"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
import { Input } from "../Input/input.js";
|
|
5
|
+
import { Textarea } from "../Textarea/textarea.js";
|
|
6
|
+
import { cva } from "class-variance-authority";
|
|
7
|
+
import { fieldDisplayTextClass } from "../Field/field-wrapper.js";
|
|
8
|
+
import { makeEditSettleKeyHandler } from "../Field/field-edit-keys.js";
|
|
9
|
+
const inlineEditReadStyles = cva(
|
|
10
|
+
// relative(疊隱形 Pressable)/ border-transparent(預留 1px 邊框盒 → 態切換零位移;focus 才染色)/
|
|
11
|
+
// px-[--field-px](文字↔底色水平內距 = Field 內距 SSOT,read=edit x 位置零跳)/ hover 底色 tint /
|
|
12
|
+
// [&:has(button:focus-visible)]:border-primary(鍵盤 focus = Field edit 藍框語言,非 Button ring)。
|
|
13
|
+
"relative flex rounded-md border border-transparent px-[var(--field-px)] transition-colors duration-150 hover:bg-neutral-hover [&:has(button:focus-visible)]:border-primary",
|
|
14
|
+
{
|
|
15
|
+
variants: {
|
|
16
|
+
size: {
|
|
17
|
+
sm: "min-h-[var(--field-height-sm)]",
|
|
18
|
+
md: "min-h-[var(--field-height-md)]",
|
|
19
|
+
lg: "min-h-[var(--field-height-lg)]"
|
|
20
|
+
},
|
|
21
|
+
multiline: {
|
|
22
|
+
false: "items-center",
|
|
23
|
+
true: "items-start py-1.5"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
// 預設 sm + 單行(2026-07-09 user 拍板):read 態無邊框、視覺 = 純文字,尺寸過大會讓版面鬆散。
|
|
27
|
+
defaultVariants: { size: "sm", multiline: false }
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
function InlineEditImpl({
|
|
31
|
+
value,
|
|
32
|
+
onCommit,
|
|
33
|
+
renderEdit,
|
|
34
|
+
renderRead,
|
|
35
|
+
multiline = false,
|
|
36
|
+
label,
|
|
37
|
+
placeholder,
|
|
38
|
+
size = "sm",
|
|
39
|
+
as = "span",
|
|
40
|
+
readClassName,
|
|
41
|
+
className
|
|
42
|
+
}, ref) {
|
|
43
|
+
const [editing, setEditing] = React.useState(false);
|
|
44
|
+
const [draft, setDraft] = React.useState(value);
|
|
45
|
+
const draftRef = React.useRef(draft);
|
|
46
|
+
const finalizedRef = React.useRef(false);
|
|
47
|
+
const returnFocusRef = React.useRef(false);
|
|
48
|
+
const readButtonRef = React.useRef(null);
|
|
49
|
+
const setDraftValue = React.useCallback((next) => {
|
|
50
|
+
draftRef.current = next;
|
|
51
|
+
setDraft(next);
|
|
52
|
+
}, []);
|
|
53
|
+
const enterEdit = React.useCallback(() => {
|
|
54
|
+
finalizedRef.current = false;
|
|
55
|
+
setDraftValue(value);
|
|
56
|
+
setEditing(true);
|
|
57
|
+
}, [value, setDraftValue]);
|
|
58
|
+
const commit = React.useCallback(() => {
|
|
59
|
+
if (finalizedRef.current) return;
|
|
60
|
+
finalizedRef.current = true;
|
|
61
|
+
returnFocusRef.current = true;
|
|
62
|
+
setEditing(false);
|
|
63
|
+
if (draftRef.current !== value) onCommit(draftRef.current);
|
|
64
|
+
}, [value, onCommit]);
|
|
65
|
+
const cancel = React.useCallback(() => {
|
|
66
|
+
if (finalizedRef.current) return;
|
|
67
|
+
finalizedRef.current = true;
|
|
68
|
+
returnFocusRef.current = true;
|
|
69
|
+
setEditing(false);
|
|
70
|
+
}, []);
|
|
71
|
+
React.useEffect(() => {
|
|
72
|
+
var _a;
|
|
73
|
+
if (!editing && returnFocusRef.current) {
|
|
74
|
+
returnFocusRef.current = false;
|
|
75
|
+
(_a = readButtonRef.current) == null ? void 0 : _a.focus();
|
|
76
|
+
}
|
|
77
|
+
}, [editing]);
|
|
78
|
+
const isEmpty = value == null || value === "" || Array.isArray(value) && value.length === 0;
|
|
79
|
+
const Tag = as;
|
|
80
|
+
const rootClass = cn("-mx-[var(--field-px)] w-[calc(100%_+_2_*_var(--field-px))]", className);
|
|
81
|
+
if (editing) {
|
|
82
|
+
const editProps = {
|
|
83
|
+
value: draft,
|
|
84
|
+
onChange: setDraftValue,
|
|
85
|
+
commit,
|
|
86
|
+
cancel,
|
|
87
|
+
autoFocus: true,
|
|
88
|
+
size,
|
|
89
|
+
"aria-label": label
|
|
90
|
+
};
|
|
91
|
+
const defaultEditNode = multiline ? /* @__PURE__ */ jsx(
|
|
92
|
+
Textarea,
|
|
93
|
+
{
|
|
94
|
+
autoFocus: true,
|
|
95
|
+
value: draft,
|
|
96
|
+
size,
|
|
97
|
+
"aria-label": label,
|
|
98
|
+
onChange: (e) => setDraftValue(e.target.value),
|
|
99
|
+
onBlur: commit,
|
|
100
|
+
onKeyDown: makeEditSettleKeyHandler({ onCommit: commit, onCancel: cancel, commitOnEnter: false })
|
|
101
|
+
}
|
|
102
|
+
) : /* @__PURE__ */ jsx(
|
|
103
|
+
Input,
|
|
104
|
+
{
|
|
105
|
+
autoFocus: true,
|
|
106
|
+
value: draft,
|
|
107
|
+
size,
|
|
108
|
+
"aria-label": label,
|
|
109
|
+
onChange: (e) => setDraftValue(e.target.value),
|
|
110
|
+
onBlur: commit,
|
|
111
|
+
onKeyDown: makeEditSettleKeyHandler({ onCommit: commit, onCancel: cancel })
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
const editNode = renderEdit ? renderEdit(editProps) : defaultEditNode;
|
|
115
|
+
return /* @__PURE__ */ jsx("div", { ref, "data-editing": editing, className: rootClass, children: editNode });
|
|
116
|
+
}
|
|
117
|
+
return /* @__PURE__ */ jsxs(
|
|
118
|
+
"div",
|
|
119
|
+
{
|
|
120
|
+
ref,
|
|
121
|
+
"data-editing": editing,
|
|
122
|
+
className: cn(rootClass, inlineEditReadStyles({ size, multiline })),
|
|
123
|
+
children: [
|
|
124
|
+
renderRead ? (
|
|
125
|
+
// 格式化 read view(consumer 傳 <Select mode="display"> 等)。invisible Pressable 疊其上供點擊進 edit。
|
|
126
|
+
/* @__PURE__ */ jsx("div", { className: "w-full min-w-0", children: renderRead(value) })
|
|
127
|
+
) : /* @__PURE__ */ jsx(
|
|
128
|
+
Tag,
|
|
129
|
+
{
|
|
130
|
+
"data-empty": isEmpty,
|
|
131
|
+
className: cn(
|
|
132
|
+
"w-full min-w-0",
|
|
133
|
+
// read 態 typography 預設消費 Field display SSOT(sm/md→text-body,lg→text-body-lg)——
|
|
134
|
+
// plain 用法 read 字級 = edit <Input>(text-body)= <Input mode="display"> 字級,三者一致零跳字。
|
|
135
|
+
fieldDisplayTextClass(size),
|
|
136
|
+
// 多行:保留換行(pre-wrap)+ 長字斷行 —— read 忠實呈現 Textarea 內容(Jira description 類),
|
|
137
|
+
// 對齊 Textarea mode="display" 的 `whitespace-pre-wrap break-words`。
|
|
138
|
+
multiline && "whitespace-pre-wrap break-words",
|
|
139
|
+
readClassName,
|
|
140
|
+
isEmpty && "text-fg-muted"
|
|
141
|
+
),
|
|
142
|
+
children: isEmpty ? placeholder : String(value)
|
|
143
|
+
}
|
|
144
|
+
),
|
|
145
|
+
/* @__PURE__ */ jsx(
|
|
146
|
+
"button",
|
|
147
|
+
{
|
|
148
|
+
ref: readButtonRef,
|
|
149
|
+
type: "button",
|
|
150
|
+
"aria-label": label ? `編輯 ${label}` : "編輯",
|
|
151
|
+
onClick: enterEdit,
|
|
152
|
+
className: "absolute inset-0 cursor-text rounded-md focus-visible:outline-none"
|
|
153
|
+
}
|
|
154
|
+
)
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
const InlineEdit = React.forwardRef(InlineEditImpl);
|
|
160
|
+
InlineEdit.displayName = "InlineEdit";
|
|
161
|
+
export {
|
|
162
|
+
InlineEdit
|
|
163
|
+
};
|
|
164
|
+
//# sourceMappingURL=inline-edit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-edit.js","sources":["../../../src/components/InlineEdit/inline-edit.tsx"],"sourcesContent":["// ── 消費的 SSOT ──\n// - components/Field/field-controls.spec.md(「Field 框架地圖」段:InlineEdit = read↔edit 疊在 Field 之上;edit-in-place)\n// - components/Field/field-edit-keys.ts(makeEditSettleKeyHandler = Enter/Esc + 中文 IME guard 結算 SSOT,與 DataTable cell 同源)\n// - components/Input/input.tsx / Textarea/textarea.tsx(預設 renderEdit;不重刻 input chrome)\n// - components/Field/field-context.ts(FieldSize 型別)\n// - tokens/uiSize/uiSize.css(--field-px 水平內距 SSOT / --field-height-{sm,md,lg} read 態高度)\n// - tokens/color/color.spec.md(bg-neutral-hover = hover 回饋)\n// - components/Field/field-wrapper.tsx(read focus = border-primary,消費 Field edit focus 藍框語言;非 Button ring)\n// - 世界級對照(泛型 readView/editView 就地編輯 + read 態底色 tint + 隱形 Pressable):\n// Atlassian inline-edit(readView/editView 兩 render prop + 泛型 <FieldValue>)\n// https://github.com/pioug/atlassian-frontend-mirror/blob/main/design-system/inline-edit/src/types.tsx\n// read-view hover = 背景 tint 非邊框 + 隱形 Pressable 供鍵盤 focus+Enter:\n// https://github.com/pioug/atlassian-frontend-mirror/blob/main/design-system/inline-edit/src/internal/read-view.tsx\n//\n// ── 型別化 read / 多行(2026-07-09 L2,edit-in-place SSOT 研究落地)──\n// InlineEdit 現為泛型 <T>(非只 string)。**格式化 read view 的 SSOT 鐵律**:renderRead 要顯示\n// Tag / 日期 / label 等格式化值時,**渲染對應 Field 控件的 `mode=\"display\"`**(如 `<Select mode=\"display\"\n// display=\"tag\">`),**不可自刻 Tag / 格式化**——「select 值 → Tag」的 SSOT 住在 Select 的 display mode\n// (= DataTable cell 消費的同一份)。這保證 read 與 edit 用同一個控件、格式零分歧。多行(multiline)\n// 供 Jira description 類欄位。\nimport * as React from 'react'\nimport { cn } from '@/lib/utils'\nimport { Input } from '@/design-system/components/Input/input'\nimport { Textarea } from '@/design-system/components/Textarea/textarea'\nimport type { FieldSize } from '@/design-system/components/Field/field-context'\nimport { cva } from 'class-variance-authority'\nimport { fieldDisplayTextClass } from '@/design-system/components/Field/field-wrapper'\nimport { makeEditSettleKeyHandler } from '@/design-system/components/Field/field-edit-keys'\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\n/** renderEdit 回呼收到的受控介面 —— consumer 換任意 Field 控件(Textarea / Select / DatePicker …)接線用 */\nexport interface InlineEditRenderProps<T = string> {\n /** 目前編輯中的草稿值(內部管理,非已 commit 的 value) */\n value: T\n /** 草稿變更 */\n onChange: (next: T) => void\n /** 提交草稿 → onCommit(next) + 回 read 態 */\n commit: () => void\n /** 放棄草稿 → 回 read 態不 commit */\n cancel: () => void\n /** 進 edit 態時自動 focus 控件 */\n autoFocus: boolean\n /** 尺寸(cascade 給控件,與 read 態高度對齊) */\n size: FieldSize\n /** 控件無障礙標籤(= label prop) */\n 'aria-label'?: string\n}\n\ntype ReadTag = 'span' | 'p' | 'div' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'\n\nexport interface InlineEditProps<T = string> {\n /** 已提交的欄位值(read 態顯示;edit 進入時作草稿初值)。泛型:string / number / string[] / PersonValue 等 */\n value: T\n /** 提交回呼 —— blur / Enter 且草稿有變更時觸發 */\n onCommit: (next: T) => void\n /**\n * 自訂 edit 態控件(換 Textarea / Select / DatePicker 等 Field 控件)。\n * 不傳則預設:multiline → `<Textarea>`,否則 `<Input>`(僅 T=string 時預設有效;非 string 值必傳此 prop)。\n */\n renderEdit?: (props: InlineEditRenderProps<T>) => React.ReactNode\n /**\n * 自訂 read 態格式化顯示。不傳則顯示 `String(value)`(空值顯 placeholder)。\n * **SSOT 鐵律**:要顯示 Tag / 日期格式 / option label 等,**渲染對應控件的 `mode=\"display\"`**\n * (如 `renderRead={(v) => <Select mode=\"display\" display=\"tag\" value={v} options={opts} />}`),\n * 不可自刻 —— 格式化 SSOT 住在該控件的 display mode(見檔頭)。\n */\n renderRead?: (value: T) => React.ReactNode\n /** 多行欄位(Jira description 類):read 態換行 + 預設 edit 態用 `<Textarea>`,Enter=換行、Cmd/Ctrl+Enter 或 blur 提交 */\n multiline?: boolean\n /** 欄位人類可讀名稱 —— 組 read 按鈕 aria-label(「編輯 {label}」)+ 控件 aria-label */\n label?: string\n /** 空值時 read 態顯示的提示文字(fg-muted) */\n placeholder?: string\n /** 尺寸 —— read 態最小高度 + 預設 Input 尺寸(與 Field 家族一致) */\n size?: FieldSize\n /** read 態語意標籤(標題場景傳 'h1'/'h2' 維持文件大綱;預設 'span') */\n as?: ReadTag\n /** read 態文字樣式(如標題 `text-h4 font-bold`) */\n readClassName?: string\n /** 外層容器 className */\n className?: string\n}\n\n// ── Read-view geometry cva ────────────────────────────────────────────────────\n// base = 排版結構;size variant = min-height(與 edit 態 Field 高度一致,降低態切換位移)。\n// multiline variant:單行 → `items-center`(文字垂直置中於 field-height);多行 → `items-start py-1.5`\n// (頂對齊 + 上下內距,對齊 Textarea `py-2` 多行閱讀語意,內容換行往下長)。\n// read 態幾何典範對照(M22 cite,2026-07-09 逐行核對原始碼,見檔頭 read-view URL):\n// hover = 底色 tint 非邊框;focus = 實線 solid border;wrapper 恆帶透明 border 預留邊框盒。\nconst inlineEditReadStyles = cva(\n // relative(疊隱形 Pressable)/ border-transparent(預留 1px 邊框盒 → 態切換零位移;focus 才染色)/\n // px-[--field-px](文字↔底色水平內距 = Field 內距 SSOT,read=edit x 位置零跳)/ hover 底色 tint /\n // [&:has(button:focus-visible)]:border-primary(鍵盤 focus = Field edit 藍框語言,非 Button ring)。\n 'relative flex rounded-md border border-transparent px-[var(--field-px)] transition-colors duration-150 hover:bg-neutral-hover [&:has(button:focus-visible)]:border-primary',\n {\n variants: {\n size: {\n sm: 'min-h-[var(--field-height-sm)]',\n md: 'min-h-[var(--field-height-md)]',\n lg: 'min-h-[var(--field-height-lg)]',\n },\n multiline: {\n false: 'items-center',\n true: 'items-start py-1.5',\n },\n },\n // 預設 sm + 單行(2026-07-09 user 拍板):read 態無邊框、視覺 = 純文字,尺寸過大會讓版面鬆散。\n defaultVariants: { size: 'sm', multiline: false },\n },\n)\n\n/**\n * InlineEdit — 就地編輯 primitive(read ↔ edit 二態,泛型 <T>)\n *\n * 互動模型 world-class 對照見檔頭「消費的 SSOT」+ inline-edit.spec.md benchmark 表:\n * - **read 態** = 純文字 / 格式化 renderRead、寬度 fill 容器、透明邊框(預留)→ 視覺 = 純內容\n * - **hover(read)** = 灰色底色 `bg-neutral-hover` + `rounded-md`(**非邊框**)+ cursor\n * - **鍵盤 focus(read)** = 外框染 `border-primary` 藍框(Field focus 語言,非 Button ring)\n * - **click / Enter/Space** = 切 edit 態 = 真正的 Field 控件(border + focus + input);多行走 Textarea\n * - **退出**:blur / Enter → commit;Esc → cancel 還原不 commit;focus 返回 read 按鈕\n * - **鍵盤結算**:消費 `makeEditSettleKeyHandler`(含中文 IME guard,與 DataTable cell 同源)\n *\n * read 態用隱形 Pressable(絕對定位 `<button>` 疊於內容上)—— 讓 `as=\"h1\"` 等標題標籤保留文件大綱\n * a11y,同時提供鍵盤 focus + Enter 進 edit(HTML 內容模型不允許 heading 巢狀於 button,故分離)。\n */\nfunction InlineEditImpl<T = string>(\n {\n value,\n onCommit,\n renderEdit,\n renderRead,\n multiline = false,\n label,\n placeholder,\n size = 'sm',\n as = 'span',\n readClassName,\n className,\n }: InlineEditProps<T>,\n ref: React.Ref<HTMLDivElement>,\n) {\n const [editing, setEditing] = React.useState(false)\n const [draft, setDraft] = React.useState<T>(value)\n // draft 最新值 ref —— commit 從事件 handler 呼叫時避免 stale closure\n const draftRef = React.useRef(draft)\n // 單次結算 guard —— Enter/Esc 已結算後,unmount 觸發的 onBlur 不再重複 commit\n const finalizedRef = React.useRef(false)\n // 鍵盤結算後把 focus 送回 read 按鈕(a11y:焦點不遺失)\n const returnFocusRef = React.useRef(false)\n const readButtonRef = React.useRef<HTMLButtonElement>(null)\n\n const setDraftValue = React.useCallback((next: T) => {\n draftRef.current = next\n setDraft(next)\n }, [])\n\n const enterEdit = React.useCallback(() => {\n finalizedRef.current = false\n setDraftValue(value)\n setEditing(true)\n }, [value, setDraftValue])\n\n const commit = React.useCallback(() => {\n if (finalizedRef.current) return\n finalizedRef.current = true\n returnFocusRef.current = true\n setEditing(false)\n if (draftRef.current !== value) onCommit(draftRef.current)\n }, [value, onCommit])\n\n const cancel = React.useCallback(() => {\n if (finalizedRef.current) return\n finalizedRef.current = true\n returnFocusRef.current = true\n setEditing(false)\n }, [])\n\n // 結算後把 focus 送回 read 按鈕(所有結算路徑含 blur / Enter / Esc 皆 return —— commit/cancel\n // 一律 set returnFocusRef=true;對齊 spec「任一路徑結算後 focus 送回 read 按鈕」L39/L141「焦點不遺失」)\n React.useEffect(() => {\n if (!editing && returnFocusRef.current) {\n returnFocusRef.current = false\n readButtonRef.current?.focus()\n }\n }, [editing])\n\n const isEmpty =\n value == null || (value as unknown) === '' || (Array.isArray(value) && value.length === 0)\n const Tag = as as React.ElementType\n // 外框(read/edit 兩態共用同一幾何 → 保證「底色範圍 = 輸入框範圍」逐 pixel 一致):\n // -mx-[--field-px] 左右各外擴 field-px;w-[calc(100% + 2*--field-px)] 對稱補回 → 兩側等寬填滿。\n const rootClass = cn('-mx-[var(--field-px)] w-[calc(100%_+_2_*_var(--field-px))]', className)\n\n if (editing) {\n const editProps: InlineEditRenderProps<T> = {\n value: draft,\n onChange: setDraftValue,\n commit,\n cancel,\n autoFocus: true,\n size,\n 'aria-label': label,\n }\n // 預設 edit 控件:multiline → Textarea(Enter=換行、Cmd/Ctrl+Enter/blur 提交);否則 Input。\n // 兩者 Enter/Esc 結算都走 makeEditSettleKeyHandler(含 IME guard)。string-only 預設路徑;\n // 非 string 值必傳 renderEdit。\n const defaultEditNode = multiline ? (\n <Textarea\n autoFocus\n value={draft as unknown as string}\n size={size}\n aria-label={label}\n onChange={(e) => setDraftValue(e.target.value as unknown as T)}\n onBlur={commit}\n // 多行:plain Enter=換行、Esc=cancel、Cmd/Ctrl+Enter=commit —— 全走\n // makeEditSettleKeyHandler SSOT(含 IME guard + Cmd/Ctrl+Enter),不再手刻 capture。\n onKeyDown={makeEditSettleKeyHandler({ onCommit: commit, onCancel: cancel, commitOnEnter: false })}\n />\n ) : (\n <Input\n autoFocus\n value={draft as unknown as string}\n size={size}\n aria-label={label}\n onChange={(e) => setDraftValue(e.target.value as unknown as T)}\n onBlur={commit}\n onKeyDown={makeEditSettleKeyHandler({ onCommit: commit, onCancel: cancel })}\n />\n )\n const editNode = renderEdit ? renderEdit(editProps) : defaultEditNode\n return (\n <div ref={ref} data-editing={editing} className={rootClass}>\n {editNode}\n </div>\n )\n }\n\n return (\n <div\n ref={ref}\n data-editing={editing}\n // 外框(rootClass 幾何)+ read chrome(inlineEditReadStyles:border 預留 / px / min-h / hover 底色 / focus 藍框)\n className={cn(rootClass, inlineEditReadStyles({ size, multiline }))}\n >\n {renderRead ? (\n // 格式化 read view(consumer 傳 <Select mode=\"display\"> 等)。invisible Pressable 疊其上供點擊進 edit。\n <div className=\"w-full min-w-0\">{renderRead(value)}</div>\n ) : (\n <Tag\n data-empty={isEmpty}\n className={cn(\n 'w-full min-w-0',\n // read 態 typography 預設消費 Field display SSOT(sm/md→text-body,lg→text-body-lg)——\n // plain 用法 read 字級 = edit <Input>(text-body)= <Input mode=\"display\"> 字級,三者一致零跳字。\n fieldDisplayTextClass(size),\n // 多行:保留換行(pre-wrap)+ 長字斷行 —— read 忠實呈現 Textarea 內容(Jira description 類),\n // 對齊 Textarea mode=\"display\" 的 `whitespace-pre-wrap break-words`。\n multiline && 'whitespace-pre-wrap break-words',\n readClassName,\n isEmpty && 'text-fg-muted',\n )}\n >\n {isEmpty ? placeholder : String(value)}\n </Tag>\n )}\n {/* 隱形 Pressable:提供 click + 鍵盤 Tab focus + Enter/Space 進 edit;透明疊於內容上。\n hover 底色 + focus 藍框(border-primary)由外層 div 承載(Field focus 語言,非 ring),\n 故本 button 只需 outline-none 消除瀏覽器預設外框。 */}\n <button\n ref={readButtonRef}\n type=\"button\"\n aria-label={label ? `編輯 ${label}` : '編輯'}\n onClick={enterEdit}\n className=\"absolute inset-0 cursor-text rounded-md focus-visible:outline-none\"\n />\n </div>\n )\n}\n\n// 泛型 forwardRef:forwardRef 不原生支援泛型,以 cast 還原公開型別(標準 workaround)。\nconst InlineEdit = React.forwardRef(InlineEditImpl) as (<T = string>(\n props: InlineEditProps<T> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement) & { displayName?: string }\nInlineEdit.displayName = 'InlineEdit'\n\nexport { InlineEdit }\n"],"names":[],"mappings":"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"}
|
|
@@ -4,19 +4,17 @@ import type { LucideIcon } from 'lucide-react';
|
|
|
4
4
|
import type { FieldMode, FieldVariant } from '../../components/Field/field-types';
|
|
5
5
|
import { fieldWrapperStyles } from '../../components/Field/field-wrapper';
|
|
6
6
|
import { type InlineActionConfig } from '../../patterns/element-anatomy/item-anatomy';
|
|
7
|
-
export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'>, Omit<VariantProps<typeof fieldWrapperStyles>, 'mode' | 'variant'> {
|
|
7
|
+
export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'>, Omit<VariantProps<typeof fieldWrapperStyles>, 'mode' | 'variant' | 'width'> {
|
|
8
8
|
/** Field display mode */
|
|
9
9
|
mode?: FieldMode;
|
|
10
10
|
/**
|
|
11
|
-
* Visual
|
|
12
|
-
* 公開 variant
|
|
11
|
+
* Visual variant(正交於 mode),對齊 FieldContext.variant 透傳。
|
|
12
|
+
* 公開 variant 一個(2026-07-09 `bare` 退役,詳 field-types.ts FieldVariant note;naked 2026-07-14 型別收窄至 FieldVariantInternal):
|
|
13
13
|
* - `'default'`(預設)— Field wrapper 完整 variant:bg-surface + 明顯 border + hover/focus 回饋。適用表單、Field 內嵌。
|
|
14
|
-
* - `'bare'` — 透明 variant,hover / focus 才出現 border。適用 Toolbar inline editing(如 FileViewer zoom input / chart config toolbar / rich text toolbar number input)。保留 padding / typography / height,只拿掉背景和常態 border。
|
|
15
14
|
*
|
|
16
|
-
* @internal `'naked'` — 完全無 chrome / 無 border / 無 focus ring。單獨使用無視覺邊界,**不可直接 standalone 用**;僅供 DS 內部 cell-as-input 組合(
|
|
15
|
+
* @internal `'naked'` — 完全無 chrome / 無 border / 無 focus ring。單獨使用無視覺邊界,**不可直接 standalone 用**;僅供 DS 內部 cell-as-input 組合(edit×naked 的 border/hover/focus/error 由 field-wrapper 自渲 state machine 提供,非 host cell 自管;詳 field-wrapper.tsx)。consumer 請用 `default`(2026-07-14 起型別層已擋:公開 FieldVariant 不含 naked,僅 DataTable cell-registry 經 WithFieldVariantInternal 通道傳入)。
|
|
17
16
|
*
|
|
18
|
-
* 透傳:在 `<Field variant="default
|
|
19
|
-
* 世界級對照(bare):VS Code settings input / Figma toolbar number / Notion prop input。
|
|
17
|
+
* 透傳:在 `<Field variant="default">` 內自動繼承 context.variant;per-prop override context。
|
|
20
18
|
*/
|
|
21
19
|
variant?: FieldVariant;
|
|
22
20
|
/** Error 狀態(正交於 mode)。border-error + aria-invalid。 */
|
|
@@ -53,7 +51,7 @@ export interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElem
|
|
|
53
51
|
* **不要用在**:表單 Field(Field 需要欄寬對齊,不該隨值跳動)
|
|
54
52
|
*
|
|
55
53
|
* **fallback**:不支援 `field-sizing` 的瀏覽器會退化為 `w-auto`(wrapper 縮到 content 尺寸,
|
|
56
|
-
* input
|
|
54
|
+
* input 為 min-w-0,依 content/placeholder 撐寬)。UX 上稍不一致但不致斷;若必須精準對齊所有瀏覽器,
|
|
57
55
|
* consumer 可自行傳 `style={{ width: ... }}` 顯式寬度,不走 auto。
|
|
58
56
|
*/
|
|
59
57
|
autoWidth?: boolean;
|
|
@@ -66,9 +64,6 @@ export declare const inputMeta: {
|
|
|
66
64
|
readonly default: {
|
|
67
65
|
readonly purpose: "Field wrapper 完整 chrome(bg-surface + border + hover/focus 回饋)— 表單 / Field 內嵌";
|
|
68
66
|
};
|
|
69
|
-
readonly bare: {
|
|
70
|
-
readonly purpose: "透明 chrome,hover / focus 才現 border — Toolbar inline editing(zoom input / config toolbar)";
|
|
71
|
-
};
|
|
72
67
|
};
|
|
73
68
|
readonly sizes: {
|
|
74
69
|
readonly sm: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../src/components/Input/input.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9C,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../src/components/Input/input.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAE9C,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAwB,MAAM,8CAA8C,CAAA;AACjH,OAAO,EAAE,kBAAkB,EAAmB,MAAM,gDAAgD,CAAA;AAGpG,OAAO,EAAgC,KAAK,kBAAkB,EAAE,MAAM,uDAAuD,CAAA;AAM7H,MAAM,WAAW,UACf,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC,EAC/D,IAAI,CAAC,YAAY,CAAC,OAAO,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IAC7E,yBAAyB;IACzB,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,sDAAsD;IACtD,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,oDAAoD;IACpD,SAAS,CAAC,EAAE,UAAU,CAAA;IACtB,qDAAqD;IACrD,SAAS,CAAC,EAAE,kBAAkB,CAAA;IAC9B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAKD,QAAA,MAAM,KAAK,qFAoLV,CAAA;AAUD,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmBZ,CAAA;AAEV,OAAO,EAAE,KAAK,EAAE,CAAA"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { cn } from "../../lib/utils.js";
|
|
4
|
-
import {
|
|
5
|
-
import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldVariant, useResolvedFieldMode, useResolvedFieldInvalid } from "../Field/field-context.js";
|
|
4
|
+
import { bareInputStyles, fieldWrapperStyles } from "../Field/field-wrapper.js";
|
|
5
|
+
import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldVariant, useResolvedFieldMode, useFieldEmptyDisplay, useResolvedFieldInvalid, fieldEmptyColorClass } from "../Field/field-context.js";
|
|
6
|
+
import { useControllable } from "../../hooks/use-controllable.js";
|
|
6
7
|
import { ICON_SIZE, ItemPrefix, ItemInlineAction } from "../../patterns/element-anatomy/item-anatomy.js";
|
|
7
8
|
import { CircularProgress } from "../CircularProgress/circular-progress.js";
|
|
8
9
|
const Input = React.forwardRef(
|
|
@@ -20,6 +21,7 @@ const Input = React.forwardRef(
|
|
|
20
21
|
disabled: disabledProp,
|
|
21
22
|
readOnly,
|
|
22
23
|
value,
|
|
24
|
+
defaultValue,
|
|
23
25
|
id: idProp,
|
|
24
26
|
"aria-describedby": ariaDescribedByProp,
|
|
25
27
|
"aria-errormessage": ariaErrorMessageProp,
|
|
@@ -30,22 +32,45 @@ const Input = React.forwardRef(
|
|
|
30
32
|
const disabled = useResolvedFieldDisabled(disabledProp);
|
|
31
33
|
const variant = useResolvedFieldVariant(variantProp);
|
|
32
34
|
const resolvedMode = useResolvedFieldMode({ mode: modeProp, disabled, readOnly });
|
|
35
|
+
const emptyDisplay = useFieldEmptyDisplay();
|
|
33
36
|
const isEditable = resolvedMode === "edit";
|
|
34
37
|
const isDisplay = resolvedMode === "display";
|
|
35
38
|
const resolvedError = useResolvedFieldInvalid(error);
|
|
36
39
|
const iconSize = ICON_SIZE[size];
|
|
37
40
|
const iconColor = resolvedMode === "disabled" ? "text-fg-disabled" : "text-fg-muted";
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
const isControlled = value !== void 0;
|
|
42
|
+
const [resolved, setResolved] = useControllable({
|
|
43
|
+
value,
|
|
44
|
+
defaultValue: defaultValue ?? ""
|
|
45
|
+
});
|
|
46
|
+
const displayValue = resolved != null && resolved !== "" ? String(resolved) : null;
|
|
47
|
+
const showDisplaySpan = isDisplay || resolvedMode === "readonly" && displayValue == null;
|
|
48
|
+
const innerRef = React.useRef(null);
|
|
49
|
+
React.useEffect(() => {
|
|
50
|
+
var _a;
|
|
51
|
+
if (isControlled || showDisplaySpan) return;
|
|
52
|
+
const form = (_a = innerRef.current) == null ? void 0 : _a.form;
|
|
53
|
+
if (!form) return;
|
|
54
|
+
const handleReset = () => setResolved(defaultValue ?? "");
|
|
55
|
+
form.addEventListener("reset", handleReset);
|
|
56
|
+
return () => form.removeEventListener("reset", handleReset);
|
|
57
|
+
}, [isControlled, showDisplaySpan, defaultValue, setResolved]);
|
|
58
|
+
const setRef = React.useCallback((el) => {
|
|
59
|
+
innerRef.current = el;
|
|
60
|
+
if (typeof ref === "function") ref(el);
|
|
61
|
+
else if (ref) ref.current = el;
|
|
62
|
+
}, [ref]);
|
|
63
|
+
if (showDisplaySpan) {
|
|
64
|
+
const spanMode = isDisplay ? "display" : "readonly";
|
|
40
65
|
return /* @__PURE__ */ jsxs(
|
|
41
66
|
"div",
|
|
42
67
|
{
|
|
43
68
|
className: cn(
|
|
44
|
-
fieldWrapperStyles({ mode:
|
|
69
|
+
fieldWrapperStyles({ mode: spanMode, variant, size }),
|
|
45
70
|
autoWidth && "inline-flex w-auto",
|
|
46
71
|
className
|
|
47
72
|
),
|
|
48
|
-
"data-field-mode":
|
|
73
|
+
"data-field-mode": spanMode,
|
|
49
74
|
children: [
|
|
50
75
|
StartIcon && /* @__PURE__ */ jsx(ItemPrefix, { children: /* @__PURE__ */ jsx(
|
|
51
76
|
StartIcon,
|
|
@@ -64,9 +89,9 @@ const Input = React.forwardRef(
|
|
|
64
89
|
// cell display canonical:single-line value 過長 → ellipsis。Textarea display 走 wrap path,
|
|
65
90
|
// 不在此處;Input display 永遠 single-line。)
|
|
66
91
|
"truncate",
|
|
67
|
-
displayValue == null &&
|
|
92
|
+
displayValue == null && fieldEmptyColorClass(resolvedMode)
|
|
68
93
|
),
|
|
69
|
-
children: displayValue ??
|
|
94
|
+
children: displayValue ?? emptyDisplay
|
|
70
95
|
}
|
|
71
96
|
)
|
|
72
97
|
]
|
|
@@ -97,10 +122,10 @@ const Input = React.forwardRef(
|
|
|
97
122
|
/* @__PURE__ */ jsx(
|
|
98
123
|
"input",
|
|
99
124
|
{
|
|
100
|
-
ref,
|
|
125
|
+
ref: setRef,
|
|
101
126
|
type: "text",
|
|
102
127
|
id: idProp ?? (fieldCtx == null ? void 0 : fieldCtx.id),
|
|
103
|
-
value,
|
|
128
|
+
value: resolved,
|
|
104
129
|
readOnly: resolvedMode === "readonly",
|
|
105
130
|
disabled: resolvedMode === "disabled",
|
|
106
131
|
"aria-invalid": resolvedError || void 0,
|
|
@@ -114,7 +139,12 @@ const Input = React.forwardRef(
|
|
|
114
139
|
// w-auto 關掉預設 w-full;min-w-0 讓 flex shrink 不卡住。
|
|
115
140
|
autoWidth && "[field-sizing:content] w-auto min-w-0"
|
|
116
141
|
),
|
|
117
|
-
...props
|
|
142
|
+
...props,
|
|
143
|
+
onChange: (e) => {
|
|
144
|
+
var _a;
|
|
145
|
+
setResolved(e.target.value);
|
|
146
|
+
(_a = props.onChange) == null ? void 0 : _a.call(props, e);
|
|
147
|
+
}
|
|
118
148
|
}
|
|
119
149
|
),
|
|
120
150
|
loading ? /* @__PURE__ */ jsx(CircularProgress, { size: iconSize, className: "shrink-0" }) : endSlot && isEditable ? (
|
|
@@ -131,8 +161,8 @@ const inputMeta = {
|
|
|
131
161
|
component: "Input",
|
|
132
162
|
family: 4,
|
|
133
163
|
variants: {
|
|
134
|
-
default: { purpose: "Field wrapper 完整 chrome(bg-surface + border + hover/focus 回饋)— 表單 / Field 內嵌" }
|
|
135
|
-
|
|
164
|
+
default: { purpose: "Field wrapper 完整 chrome(bg-surface + border + hover/focus 回饋)— 表單 / Field 內嵌" }
|
|
165
|
+
// (2026-07-09 `bare` variant 退役 — 見 field-types.ts FieldVariant note)
|
|
136
166
|
},
|
|
137
167
|
sizes: {
|
|
138
168
|
sm: { fieldHeight: 28, iconSize: 16, typography: "body" },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.js","sources":["../../../src/components/Input/input.tsx"],"sourcesContent":["// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.\nimport * as React from 'react'\nimport { type VariantProps } from 'class-variance-authority'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport type { FieldMode, FieldVariant } from '@/design-system/components/Field/field-types'\nimport { fieldWrapperStyles, bareInputStyles, EMPTY_DISPLAY } from '@/design-system/components/Field/field-wrapper'\nimport { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid } from '@/design-system/components/Field/field-context'\nimport { ItemInlineAction, ItemPrefix, type InlineActionConfig } from '@/design-system/patterns/element-anatomy/item-anatomy'\nimport { CircularProgress } from '@/design-system/components/CircularProgress/circular-progress'\nimport { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\nexport interface InputProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'>,\n Omit<VariantProps<typeof fieldWrapperStyles>, 'mode' | 'variant'> {\n /** Field display mode */\n mode?: FieldMode\n /**\n * Visual chrome(正交於 mode);Phase B1(2026-05-05)從 `variant` 改名 `chrome`,對齊 FieldContext.variant 透傳。\n * 公開 variant 兩個:\n * - `'default'`(預設)— Field wrapper 完整 variant:bg-surface + 明顯 border + hover/focus 回饋。適用表單、Field 內嵌。\n * - `'bare'` — 透明 variant,hover / focus 才出現 border。適用 Toolbar inline editing(如 FileViewer zoom input / chart config toolbar / rich text toolbar number input)。保留 padding / typography / height,只拿掉背景和常態 border。\n *\n * @internal `'naked'` — 完全無 chrome / 無 border / 無 focus ring。單獨使用無視覺邊界,**不可直接 standalone 用**;僅供 DS 內部 cell-as-input 組合(host cell 自管 border + focus visual,正被 `FieldSurfaceContext='table-cell'` 取代)。consumer 請用 `default` / `bare`。\n *\n * 透傳:在 `<Field variant=\"default|bare\">` 內自動繼承 context.variant;per-prop override context。\n * 世界級對照(bare):VS Code settings input / Figma toolbar number / Notion prop input。\n */\n variant?: FieldVariant\n /** Error 狀態(正交於 mode)。border-error + aria-invalid。 */\n error?: boolean\n /** 左側靜態 icon — 輔助理解 input 用途(如 Search)。fg-muted。 */\n startIcon?: LucideIcon\n /** 右側 inline action — 宣告式 API,Field 根據 size 自動渲染。 */\n endAction?: InlineActionConfig\n /**\n * 右側 slot(ReactNode)— escape hatch 供 consumer 放自訂元素(如 DropdownMenuTrigger asChild + ItemInlineActionButton)。\n * 跟 `endAction` 互斥(同時傳 endSlot 會優先,endAction 被忽略)。\n *\n * **使用情境**:ZoomInput 需要 chevron 作 DropdownMenuTrigger anchor,config-only API 無法做到。\n * **禁止情境**:表單欄位 / 一般 inline action → 用 `endAction` 宣告式 API。\n */\n endSlot?: React.ReactNode\n /**\n * Loading 狀態(async 驗證 / debounce fetch 中)。\n * - **input 保持可編輯**(user 可以邊改邊讀,debounce 場景 UX 最好)\n * - 世界級對照:Ant Input.Search 派(input editable during loading);非 Material readonly 派\n * - 自動在 endAction slot 塞 `<CircularProgress size={iconSize}/>`(與 endAction prop 互斥)\n * - 宣告 `aria-busy=\"true\"` 讓 screen reader 感知處理中\n */\n loading?: boolean\n /**\n * Auto-width:Input 寬度 = 內容寬(value / placeholder 文字寬)+ startIcon + endAction + padding。\n * 使用 CSS `field-sizing: content`(Chrome 123+ / Safari 17.4+;Firefox 還在實驗)。\n *\n * **使用情境**:\n * - Inline edit(VS Code setting row / Figma property toolbar number input)\n * - ZoomInput(FileViewer 縮放比例:輸入「100%」自動縮到三位數寬)\n * - Tag / Chip 內 inline rename\n *\n * **不要用在**:表單 Field(Field 需要欄寬對齊,不該隨值跳動)\n *\n * **fallback**:不支援 `field-sizing` 的瀏覽器會退化為 `w-auto`(wrapper 縮到 content 尺寸,\n * input 本身有 min-width 避免消失)。UX 上稍不一致但不致斷;若必須精準對齊所有瀏覽器,\n * consumer 可自行傳 `style={{ width: ... }}` 顯式寬度,不走 auto。\n */\n autoWidth?: boolean\n}\n\n// ── Component ───────────────────────────────────────────────────────────────\n\n// code-quality-allow: long-function — foundational composite main body — 拆 sub-fn 會複雜化 local state / ref / context binding\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n (\n {\n mode: modeProp,\n variant: variantProp,\n error = false,\n size: sizeProp,\n startIcon: StartIcon,\n endAction,\n endSlot,\n loading = false,\n autoWidth = false,\n className,\n disabled: disabledProp,\n readOnly,\n value,\n id: idProp,\n 'aria-describedby': ariaDescribedByProp,\n 'aria-errormessage': ariaErrorMessageProp,\n ...props\n },\n ref\n ) => {\n // ── FieldContext 自動讀取(在 <Field> 內時,invalid / disabled / mode / chrome 由 context 接管) ──\n const fieldCtx = useFieldContext()\n // 2026-05-31 #11/#12:size + disabled 從 Field context cascade(對齊 NumberInput number-input.tsx:100-101\n // + MUI FormControl)。原 Input 不讀 fieldCtx.size/disabled → <Field size=\"lg\"> / <Field disabled> 對 Input 無效。\n const size = useResolvedFieldSize(sizeProp)\n const disabled = useResolvedFieldDisabled(disabledProp)\n // chrome 透傳:per-prop override context;context 沒值則 'default'\n const variant: FieldVariant = useResolvedFieldVariant(variantProp)\n // 2026-06-08 SSOT:mode 經 useResolvedFieldMode 統一解析(prop > 有效 disabled > fieldCtx.mode > readOnly > 'edit')。\n // loading 期間 input 保持可編輯(Ant Input.Search 派),只用 aria-busy + endAction Spinner,不動 mode。\n const resolvedMode: FieldMode = useResolvedFieldMode({ mode: modeProp, disabled, readOnly })\n const isEditable = resolvedMode === 'edit'\n const isDisplay = resolvedMode === 'display'\n // error 合併:自身 error prop OR Field context invalid\n const resolvedError = useResolvedFieldInvalid(error)\n // 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反 7+ 重複 ternary)\n const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']\n const iconColor = resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-fg-muted'\n\n // ── display mode:純展示,渲染 <span> 取代 <input> ──\n // 對齊 Carbon read-only / PatternFly inline-edit hidden-input / Cloudscape display-mode\n if (isDisplay) {\n const displayValue = value != null && value !== '' ? String(value) : null\n return (\n <div\n className={cn(\n fieldWrapperStyles({ mode: 'display', variant: variant, size }),\n autoWidth && 'inline-flex w-auto',\n className,\n )}\n data-field-mode=\"display\"\n >\n {StartIcon && (\n <ItemPrefix>\n <StartIcon\n size={iconSize}\n className={cn('pointer-events-none', iconColor)}\n aria-hidden\n />\n </ItemPrefix>\n )}\n <span\n className={cn(\n bareInputStyles,\n // B1 fix(2026-05-05):display mode 單行 ellipsis 截斷(對齊 Notion / Airtable / Linear\n // cell display canonical:single-line value 過長 → ellipsis。Textarea display 走 wrap path,\n // 不在此處;Input display 永遠 single-line。)\n 'truncate',\n displayValue == null && 'text-fg-muted',\n )}\n >\n {displayValue ?? EMPTY_DISPLAY}\n </span>\n </div>\n )\n }\n\n return (\n <div\n className={cn(\n fieldWrapperStyles({ mode: resolvedMode, variant: variant, size, error: resolvedError }),\n // autoWidth:wrapper 縮到 inline-flex + w-auto,讓寬度由 startIcon + input(field-sizing: content)+ endAction 自然累加\n autoWidth && 'inline-flex w-auto',\n className,\n )}\n data-field-mode={resolvedMode}\n data-error={isEditable && resolvedError ? '' : undefined}\n aria-busy={loading || undefined}\n >\n {StartIcon && (\n <ItemPrefix>\n <StartIcon\n size={iconSize}\n className={cn('pointer-events-none', iconColor)}\n aria-hidden\n />\n </ItemPrefix>\n )}\n <input\n ref={ref}\n type=\"text\"\n id={idProp ?? fieldCtx?.id}\n value={value as string | number | readonly string[] | undefined}\n readOnly={resolvedMode === 'readonly'}\n disabled={resolvedMode === 'disabled'}\n aria-invalid={resolvedError || undefined}\n aria-required={fieldCtx?.required || undefined}\n aria-describedby={ariaDescribedByProp ?? fieldCtx?.descriptionId}\n aria-errormessage={ariaErrorMessageProp ?? (resolvedError ? fieldCtx?.errorId : undefined)}\n className={cn(\n bareInputStyles,\n resolvedMode === 'disabled' && 'text-fg-disabled placeholder:text-fg-disabled cursor-not-allowed',\n // autoWidth:input 本身 field-sizing:content(Chrome 123+ / Safari 17.4+),寬度跟 value 文字寬。\n // w-auto 關掉預設 w-full;min-w-0 讓 flex shrink 不卡住。\n autoWidth && '[field-sizing:content] w-auto min-w-0',\n )}\n {...props}\n />\n {loading ? (\n <CircularProgress size={iconSize} className=\"shrink-0\" />\n ) : endSlot && isEditable ? (\n // endSlot escape hatch:consumer 自控右側 slot(如 DropdownMenuTrigger asChild wrap)\n endSlot\n ) : endAction && isEditable ? (\n <ItemInlineAction action={endAction} size={size ?? 'md'} />\n ) : null}\n </div>\n )\n }\n)\nInput.displayName = 'Input'\n\n// Phase B1(2026-05-05):InputDisplay 退場。改用 `<Input mode=\"display\" value={...} />`\n// 對齊 Carbon read-only / PatternFly inline-edit hidden-input / Cloudscape display-mode 統一 mode 模型。\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// 2026-07-05 Phase 2 fill(deep-audit A.1b):variants 按 input.tsx variant jsdoc、sizes 按\n// fieldWrapperStyles cva 真值(h-field-sm/md/lg = 28/32/36,uiSize.css)+ ICON_SIZE SSOT\n// (sm/md 16、lg 20)填。`naked` 為 @internal cell-as-input substrate → 不列入 public variants。\nexport const inputMeta = {\n component: 'Input',\n family: 4,\n variants: {\n default: { purpose: 'Field wrapper 完整 chrome(bg-surface + border + hover/focus 回饋)— 表單 / Field 內嵌' },\n bare: { purpose: '透明 chrome,hover / focus 才現 border — Toolbar inline editing(zoom input / config toolbar)' },\n },\n sizes: {\n sm: { fieldHeight: 28, iconSize: 16, typography: 'body' },\n md: { fieldHeight: 32, iconSize: 16, typography: 'body' },\n lg: { fieldHeight: 36, iconSize: 20, typography: 'body-lg' },\n },\n // 'active' 移除 — Field chrome 無按壓態(對齊 Textarea / LinkInput meta 既有寫法)(2026-07-07 詞彙統一 DS-wide 按壓訊號盤點:檔內 0 active: utility / 0 *-active token)。\n states: ['default', 'hover', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-surface'],\n fg: ['text-fg-disabled', 'text-fg-muted'],\n ring: [],\n },\n} as const\n\nexport { Input }\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"input.js","sources":["../../../src/components/Input/input.tsx"],"sourcesContent":["// @benchmark-unverified-blanket: file-level retraction per M22 (d) — claims herein not individually URL-cited; treat as unverified visual/usage rumor unless retrofit per-claim. Hook escape preserved.\nimport * as React from 'react'\nimport { type VariantProps } from 'class-variance-authority'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport type { FieldMode, FieldVariant, FieldVariantInternal } from '@/design-system/components/Field/field-types'\nimport { fieldWrapperStyles, bareInputStyles } from '@/design-system/components/Field/field-wrapper'\nimport { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid, useFieldEmptyDisplay, fieldEmptyColorClass } from '@/design-system/components/Field/field-context'\nimport { useControllable } from '@/design-system/hooks/use-controllable'\nimport { ItemInlineAction, ItemPrefix, type InlineActionConfig } from '@/design-system/patterns/element-anatomy/item-anatomy'\nimport { CircularProgress } from '@/design-system/components/CircularProgress/circular-progress'\nimport { ICON_SIZE } from '@/design-system/tokens/uiSize/icon-size'\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\nexport interface InputProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'>,\n Omit<VariantProps<typeof fieldWrapperStyles>, 'mode' | 'variant' | 'width'> {\n /** Field display mode */\n mode?: FieldMode\n /**\n * Visual variant(正交於 mode),對齊 FieldContext.variant 透傳。\n * 公開 variant 一個(2026-07-09 `bare` 退役,詳 field-types.ts FieldVariant note;naked 2026-07-14 型別收窄至 FieldVariantInternal):\n * - `'default'`(預設)— Field wrapper 完整 variant:bg-surface + 明顯 border + hover/focus 回饋。適用表單、Field 內嵌。\n *\n * @internal `'naked'` — 完全無 chrome / 無 border / 無 focus ring。單獨使用無視覺邊界,**不可直接 standalone 用**;僅供 DS 內部 cell-as-input 組合(edit×naked 的 border/hover/focus/error 由 field-wrapper 自渲 state machine 提供,非 host cell 自管;詳 field-wrapper.tsx)。consumer 請用 `default`(2026-07-14 起型別層已擋:公開 FieldVariant 不含 naked,僅 DataTable cell-registry 經 WithFieldVariantInternal 通道傳入)。\n *\n * 透傳:在 `<Field variant=\"default\">` 內自動繼承 context.variant;per-prop override context。\n */\n variant?: FieldVariant\n /** Error 狀態(正交於 mode)。border-error + aria-invalid。 */\n error?: boolean\n /** 左側靜態 icon — 輔助理解 input 用途(如 Search)。fg-muted。 */\n startIcon?: LucideIcon\n /** 右側 inline action — 宣告式 API,Field 根據 size 自動渲染。 */\n endAction?: InlineActionConfig\n /**\n * 右側 slot(ReactNode)— escape hatch 供 consumer 放自訂元素(如 DropdownMenuTrigger asChild + ItemInlineActionButton)。\n * 跟 `endAction` 互斥(同時傳 endSlot 會優先,endAction 被忽略)。\n *\n * **使用情境**:ZoomInput 需要 chevron 作 DropdownMenuTrigger anchor,config-only API 無法做到。\n * **禁止情境**:表單欄位 / 一般 inline action → 用 `endAction` 宣告式 API。\n */\n endSlot?: React.ReactNode\n /**\n * Loading 狀態(async 驗證 / debounce fetch 中)。\n * - **input 保持可編輯**(user 可以邊改邊讀,debounce 場景 UX 最好)\n * - 世界級對照:Ant Input.Search 派(input editable during loading);非 Material readonly 派\n * - 自動在 endAction slot 塞 `<CircularProgress size={iconSize}/>`(與 endAction prop 互斥)\n * - 宣告 `aria-busy=\"true\"` 讓 screen reader 感知處理中\n */\n loading?: boolean\n /**\n * Auto-width:Input 寬度 = 內容寬(value / placeholder 文字寬)+ startIcon + endAction + padding。\n * 使用 CSS `field-sizing: content`(Chrome 123+ / Safari 17.4+;Firefox 還在實驗)。\n *\n * **使用情境**:\n * - Inline edit(VS Code setting row / Figma property toolbar number input)\n * - ZoomInput(FileViewer 縮放比例:輸入「100%」自動縮到三位數寬)\n * - Tag / Chip 內 inline rename\n *\n * **不要用在**:表單 Field(Field 需要欄寬對齊,不該隨值跳動)\n *\n * **fallback**:不支援 `field-sizing` 的瀏覽器會退化為 `w-auto`(wrapper 縮到 content 尺寸,\n * input 為 min-w-0,依 content/placeholder 撐寬)。UX 上稍不一致但不致斷;若必須精準對齊所有瀏覽器,\n * consumer 可自行傳 `style={{ width: ... }}` 顯式寬度,不走 auto。\n */\n autoWidth?: boolean\n}\n\n// ── Component ───────────────────────────────────────────────────────────────\n\n// code-quality-allow: long-function — foundational composite main body — 拆 sub-fn 會複雜化 local state / ref / context binding\nconst Input = React.forwardRef<HTMLInputElement, InputProps>(\n (\n {\n mode: modeProp,\n variant: variantProp,\n error = false,\n size: sizeProp,\n startIcon: StartIcon,\n endAction,\n endSlot,\n loading = false,\n autoWidth = false,\n className,\n disabled: disabledProp,\n readOnly,\n value,\n defaultValue,\n id: idProp,\n 'aria-describedby': ariaDescribedByProp,\n 'aria-errormessage': ariaErrorMessageProp,\n ...props\n },\n ref\n ) => {\n // ── FieldContext 自動讀取(在 <Field> 內時,invalid / disabled / mode / chrome 由 context 接管) ──\n const fieldCtx = useFieldContext()\n // 2026-05-31 #11/#12:size + disabled 從 Field context cascade(對齊 NumberInput number-input.tsx:100-101\n // + MUI FormControl)。原 Input 不讀 fieldCtx.size/disabled → <Field size=\"lg\"> / <Field disabled> 對 Input 無效。\n const size = useResolvedFieldSize(sizeProp)\n const disabled = useResolvedFieldDisabled(disabledProp)\n // chrome 透傳:per-prop override context;context 沒值則 'default'\n const variant: FieldVariantInternal = useResolvedFieldVariant(variantProp)\n // 2026-06-08 SSOT:mode 經 useResolvedFieldMode 統一解析(prop > 有效 disabled > fieldCtx.mode > readOnly > 'edit')。\n // loading 期間 input 保持可編輯(Ant Input.Search 派),只用 aria-busy + endAction Spinner,不動 mode。\n const resolvedMode: FieldMode = useResolvedFieldMode({ mode: modeProp, disabled, readOnly })\n const emptyDisplay = useFieldEmptyDisplay()\n const isEditable = resolvedMode === 'edit'\n const isDisplay = resolvedMode === 'display'\n // error 合併:自身 error prop OR Field context invalid\n const resolvedError = useResolvedFieldInvalid(error)\n // 2026-05-18 改 import ICON_SIZE SSOT(per user『做完』approval,消除 M17 違反 7+ 重複 ternary)\n const iconSize = ICON_SIZE[size as 'sm' | 'md' | 'lg']\n const iconColor = resolvedMode === 'disabled' ? 'text-fg-disabled' : 'text-fg-muted'\n\n // ── display mode(+ readonly 空值)純展示,渲染 <span> 取代 <input> ──\n // 對齊 Carbon read-only / PatternFly inline-edit hidden-input / Cloudscape display-mode\n // 2026-07-08 user 拍板:readonly 空值也走 display-span 顯 '-'(readonly **有值** 仍走下方\n // native <input readOnly> 保留選取/複製語意 — field-controls.spec.md「null / undefined 值」)。\n // 2026-07-14 audit Dim 26(dual-mode coherence):uncontrolled `defaultValue` 也要被\n // display / readonly-空值判定認得 — 原本只讀 `value`,uncontrolled Input 切 display 會誤顯 '-'。\n // 2026-07-14 R2(dual-model consensus,取代同日稍早 onChange-mirror 版):resolved value 改走\n // useControllable 內部 SSOT(Radix idiom,同 Select / Pagination 既有消費)。uncontrolled 時\n // native input 由 value={resolved} 內部驅動(defaultValue 只作初始值,不落 DOM attribute),\n // 修 mirror 版兩個真缺陷:\n // (1) remount-stale — 切 display / readonly(native input unmount)再回 edit,native input 從\n // defaultValue attribute 重掛 → 顯示 stale 初始值,與 mirror 判定分歧;\n // (2) form.reset() stale — HTML 標準 reset 不發 input event,onChange mirror 停在 reset 前的值\n // (下方 reset bridge 修)。\n // controlled(傳 value)時 useControllable 純 passthrough,行為與原本完全一致。\n // consumer onChange 不接進 hook(保留 native event signature),於 <input> onChange 另行轉發。\n const isControlled = value !== undefined\n const [resolved, setResolved] = useControllable<string | number | readonly string[]>({\n value: value as string | number | readonly string[] | undefined,\n defaultValue: defaultValue ?? '',\n })\n const displayValue = resolved != null && resolved !== '' ? String(resolved) : null\n const showDisplaySpan = isDisplay || (resolvedMode === 'readonly' && displayValue == null)\n\n // form.reset() bridge(uncontrolled only):reset 恢復 defaultValue 但不發 input event →\n // 手動把 resolved 歸位 defaultValue。keyed on showDisplaySpan:display 分支不掛 native input,\n // 回 edit 重掛時 effect 重跑補掛 listener。\n const innerRef = React.useRef<HTMLInputElement | null>(null)\n React.useEffect(() => {\n if (isControlled || showDisplaySpan) return\n const form = innerRef.current?.form\n if (!form) return\n const handleReset = () => setResolved(defaultValue ?? '')\n form.addEventListener('reset', handleReset)\n return () => form.removeEventListener('reset', handleReset)\n }, [isControlled, showDisplaySpan, defaultValue, setResolved])\n // Merge refs(LinkInput setRef idiom,link-input.tsx:138)\n const setRef = React.useCallback((el: HTMLInputElement | null) => {\n innerRef.current = el\n if (typeof ref === 'function') ref(el)\n else if (ref) (ref as React.MutableRefObject<HTMLInputElement | null>).current = el\n }, [ref])\n\n if (showDisplaySpan) {\n const spanMode = isDisplay ? 'display' : 'readonly'\n return (\n <div\n className={cn(\n fieldWrapperStyles({ mode: spanMode, variant: variant, size }),\n autoWidth && 'inline-flex w-auto',\n className,\n )}\n data-field-mode={spanMode}\n >\n {StartIcon && (\n <ItemPrefix>\n <StartIcon\n size={iconSize}\n className={cn('pointer-events-none', iconColor)}\n aria-hidden\n />\n </ItemPrefix>\n )}\n <span\n className={cn(\n bareInputStyles,\n // B1 fix(2026-05-05):display mode 單行 ellipsis 截斷(對齊 Notion / Airtable / Linear\n // cell display canonical:single-line value 過長 → ellipsis。Textarea display 走 wrap path,\n // 不在此處;Input display 永遠 single-line。)\n 'truncate',\n displayValue == null && fieldEmptyColorClass(resolvedMode),\n )}\n >\n {displayValue ?? emptyDisplay}\n </span>\n </div>\n )\n }\n\n return (\n <div\n className={cn(\n fieldWrapperStyles({ mode: resolvedMode, variant: variant, size, error: resolvedError }),\n // autoWidth:wrapper 縮到 inline-flex + w-auto,讓寬度由 startIcon + input(field-sizing: content)+ endAction 自然累加\n autoWidth && 'inline-flex w-auto',\n className,\n )}\n data-field-mode={resolvedMode}\n data-error={isEditable && resolvedError ? '' : undefined}\n aria-busy={loading || undefined}\n >\n {StartIcon && (\n <ItemPrefix>\n <StartIcon\n size={iconSize}\n className={cn('pointer-events-none', iconColor)}\n aria-hidden\n />\n </ItemPrefix>\n )}\n <input\n ref={setRef}\n type=\"text\"\n id={idProp ?? fieldCtx?.id}\n value={resolved}\n readOnly={resolvedMode === 'readonly'}\n disabled={resolvedMode === 'disabled'}\n aria-invalid={resolvedError || undefined}\n aria-required={fieldCtx?.required || undefined}\n aria-describedby={ariaDescribedByProp ?? fieldCtx?.descriptionId}\n aria-errormessage={ariaErrorMessageProp ?? (resolvedError ? fieldCtx?.errorId : undefined)}\n className={cn(\n bareInputStyles,\n resolvedMode === 'disabled' && 'text-fg-disabled placeholder:text-fg-disabled cursor-not-allowed',\n // autoWidth:input 本身 field-sizing:content(Chrome 123+ / Safari 17.4+),寬度跟 value 文字寬。\n // w-auto 關掉預設 w-full;min-w-0 讓 flex shrink 不卡住。\n autoWidth && '[field-sizing:content] w-auto min-w-0',\n )}\n {...props}\n // 置於 {...props} 後:寫回 resolved SSOT(controlled 時 useControllable 內部為 no-op)再轉發 consumer onChange。\n onChange={(e) => {\n setResolved(e.target.value)\n props.onChange?.(e)\n }}\n />\n {loading ? (\n <CircularProgress size={iconSize} className=\"shrink-0\" />\n ) : endSlot && isEditable ? (\n // endSlot escape hatch:consumer 自控右側 slot(如 DropdownMenuTrigger asChild wrap)\n endSlot\n ) : endAction && isEditable ? (\n <ItemInlineAction action={endAction} size={size ?? 'md'} />\n ) : null}\n </div>\n )\n }\n)\nInput.displayName = 'Input'\n\n// Phase B1(2026-05-05):InputDisplay 退場。改用 `<Input mode=\"display\" value={...} />`\n// 對齊 Carbon read-only / PatternFly inline-edit hidden-input / Cloudscape display-mode 統一 mode 模型。\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// 2026-07-05 Phase 2 fill(deep-audit A.1b):variants 按 input.tsx variant jsdoc、sizes 按\n// fieldWrapperStyles cva 真值(h-field-sm/md/lg = 28/32/36,uiSize.css)+ ICON_SIZE SSOT\n// (sm/md 16、lg 20)填。`naked` 為 @internal cell-as-input substrate → 不列入 public variants。\nexport const inputMeta = {\n component: 'Input',\n family: 4,\n variants: {\n default: { purpose: 'Field wrapper 完整 chrome(bg-surface + border + hover/focus 回饋)— 表單 / Field 內嵌' },\n // (2026-07-09 `bare` variant 退役 — 見 field-types.ts FieldVariant note)\n },\n sizes: {\n sm: { fieldHeight: 28, iconSize: 16, typography: 'body' },\n md: { fieldHeight: 32, iconSize: 16, typography: 'body' },\n lg: { fieldHeight: 36, iconSize: 20, typography: 'body-lg' },\n },\n // 'active' 移除 — Field chrome 無按壓態(對齊 Textarea / LinkInput meta 既有寫法)(2026-07-07 詞彙統一 DS-wide 按壓訊號盤點:檔內 0 active: utility / 0 *-active token)。\n states: ['default', 'hover', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-surface'],\n fg: ['text-fg-disabled', 'text-fg-muted'],\n ring: [],\n },\n} as const\n\nexport { Input }\n"],"names":[],"mappings":"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"}
|
|
@@ -2,12 +2,12 @@ import * as React from 'react';
|
|
|
2
2
|
import type { VariantProps } from 'class-variance-authority';
|
|
3
3
|
import type { FieldMode, FieldVariant } from '../../components/Field/field-types';
|
|
4
4
|
import { fieldWrapperStyles } from '../../components/Field/field-wrapper';
|
|
5
|
-
export interface LinkInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'value' | 'defaultValue' | 'onChange'>, Omit<VariantProps<typeof fieldWrapperStyles>, 'mode' | 'variant'> {
|
|
5
|
+
export interface LinkInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'value' | 'defaultValue' | 'onChange'>, Omit<VariantProps<typeof fieldWrapperStyles>, 'mode' | 'variant' | 'width'> {
|
|
6
6
|
mode?: FieldMode;
|
|
7
7
|
/**
|
|
8
8
|
* Visual chrome(2026-05-05 Phase B3)。對齊 FieldContext.variant 透傳。
|
|
9
9
|
* - `'default'`(預設)— Field wrapper 完整 chrome(form / Field 內嵌)。
|
|
10
|
-
* - `
|
|
10
|
+
* (2026-07-09 `bare` variant 退役;naked = cell-as-input substrate,@internal)
|
|
11
11
|
*
|
|
12
12
|
* mode='display' 時 chrome 無視覺意義(display 完全無 wrapper);chrome 僅作用於 edit / readonly / disabled。
|
|
13
13
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"link-input.d.ts","sourceRoot":"","sources":["../../../src/components/LinkInput/link-input.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAE5D,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"link-input.d.ts","sourceRoot":"","sources":["../../../src/components/LinkInput/link-input.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAE5D,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAwB,MAAM,8CAA8C,CAAA;AACjH,OAAO,EAAE,kBAAkB,EAA0C,MAAM,gDAAgD,CAAA;AA4C3H,MAAM,WAAW,cAIf,SAAQ,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,cAAc,GAAG,UAAU,CAAC,EACvG,IAAI,CAAC,YAAY,CAAC,OAAO,kBAAkB,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IAC7E,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAGD,QAAA,MAAM,SAAS,yFAoNd,CAAA;AAKD,eAAO,MAAM,aAAa;;;;;;;;;;;CAkBhB,CAAA;AAEV,OAAO,EAAE,SAAS,EAAE,CAAA"}
|
|
@@ -2,8 +2,8 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { Pencil } from "lucide-react";
|
|
4
4
|
import { cn } from "../../lib/utils.js";
|
|
5
|
-
import {
|
|
6
|
-
import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useResolvedFieldVariant, useResolvedFieldInvalid } from "../Field/field-context.js";
|
|
5
|
+
import { fieldDisplayTextClass, fieldWrapperStyles, bareInputStyles } from "../Field/field-wrapper.js";
|
|
6
|
+
import { useFieldContext, useResolvedFieldSize, useResolvedFieldDisabled, useResolvedFieldMode, useFieldEmptyDisplay, useResolvedFieldVariant, useResolvedFieldInvalid, fieldEmptyColorClass } from "../Field/field-context.js";
|
|
7
7
|
import { ItemInlineAction } from "../../patterns/element-anatomy/item-anatomy.js";
|
|
8
8
|
function isValidUrl(value) {
|
|
9
9
|
if (!value) return true;
|
|
@@ -57,6 +57,7 @@ const LinkInput = React.forwardRef(
|
|
|
57
57
|
const size = useResolvedFieldSize(sizeProp);
|
|
58
58
|
const disabled = useResolvedFieldDisabled(disabledProp);
|
|
59
59
|
const resolvedMode = useResolvedFieldMode({ mode: modeProp, disabled, readOnly });
|
|
60
|
+
const emptyDisplay = useFieldEmptyDisplay();
|
|
60
61
|
const isEditable = resolvedMode === "edit";
|
|
61
62
|
const resolvedVariant = useResolvedFieldVariant(variantProp);
|
|
62
63
|
const fieldInvalid = useResolvedFieldInvalid(errorProp);
|
|
@@ -78,7 +79,7 @@ const LinkInput = React.forwardRef(
|
|
|
78
79
|
}, []);
|
|
79
80
|
if (resolvedMode === "display") {
|
|
80
81
|
if (!showDisplayEndIcon) {
|
|
81
|
-
if (!value) return /* @__PURE__ */ jsx("span", { className: cn(fieldDisplayTextClass(size), "
|
|
82
|
+
if (!value) return /* @__PURE__ */ jsx("span", { className: cn(fieldDisplayTextClass(size), fieldEmptyColorClass(resolvedMode), "block truncate"), children: emptyDisplay });
|
|
82
83
|
return /* @__PURE__ */ jsx("span", { className: cn(fieldDisplayTextClass(size), "block truncate"), children: renderLinkAnchor(value, label) });
|
|
83
84
|
}
|
|
84
85
|
return /* @__PURE__ */ jsx(
|
|
@@ -86,7 +87,7 @@ const LinkInput = React.forwardRef(
|
|
|
86
87
|
{
|
|
87
88
|
className: cn(fieldWrapperStyles({ mode: "display", variant: resolvedVariant, size }), className),
|
|
88
89
|
"data-field-mode": "display",
|
|
89
|
-
children: /* @__PURE__ */ jsx("span", { className: "flex-1 min-w-0 truncate", children: value ? renderLinkAnchor(value, label) : /* @__PURE__ */ jsx("span", { className:
|
|
90
|
+
children: /* @__PURE__ */ jsx("span", { className: "flex-1 min-w-0 truncate", children: value ? renderLinkAnchor(value, label) : /* @__PURE__ */ jsx("span", { className: fieldEmptyColorClass(resolvedMode), children: emptyDisplay }) })
|
|
90
91
|
}
|
|
91
92
|
);
|
|
92
93
|
}
|
|
@@ -137,7 +138,7 @@ const LinkInput = React.forwardRef(
|
|
|
137
138
|
{
|
|
138
139
|
className: cn(fieldWrapperStyles({ mode: resolvedMode, variant: resolvedVariant, size }), className),
|
|
139
140
|
"data-field-mode": resolvedMode,
|
|
140
|
-
children: /* @__PURE__ */ jsx("span", { className: "flex-1 min-w-0 truncate", children: resolvedMode === "disabled" ? displayText ? /* @__PURE__ */ jsx("span", { className: "text-fg-disabled", children: displayText }) : /* @__PURE__ */ jsx("span", { className:
|
|
141
|
+
children: /* @__PURE__ */ jsx("span", { className: "flex-1 min-w-0 truncate", children: resolvedMode === "disabled" ? displayText ? /* @__PURE__ */ jsx("span", { className: "text-fg-disabled", children: displayText }) : /* @__PURE__ */ jsx("span", { className: fieldEmptyColorClass(resolvedMode), children: emptyDisplay }) : value ? renderLinkAnchor(value, label) : /* @__PURE__ */ jsx("span", { className: fieldEmptyColorClass(resolvedMode), children: emptyDisplay }) })
|
|
141
142
|
}
|
|
142
143
|
);
|
|
143
144
|
}
|