@qijenchen/design-system 0.1.0-beta.61 → 0.1.0-beta.63
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 +6 -6
- package/dist/components/Accordion/accordion.d.ts +1 -1
- package/dist/components/Accordion/accordion.js +1 -1
- package/dist/components/Accordion/accordion.js.map +1 -1
- package/dist/components/Alert/alert.d.ts +2 -2
- package/dist/components/Alert/alert.js +4 -3
- package/dist/components/Alert/alert.js.map +1 -1
- package/dist/components/AppShell/app-shell.d.ts.map +1 -1
- package/dist/components/AppShell/app-shell.js +4 -3
- package/dist/components/AppShell/app-shell.js.map +1 -1
- package/dist/components/Avatar/avatar.d.ts +1 -1
- package/dist/components/Avatar/avatar.js +2 -1
- package/dist/components/Avatar/avatar.js.map +1 -1
- package/dist/components/Badge/badge.d.ts +1 -1
- package/dist/components/Badge/badge.js +2 -1
- package/dist/components/Badge/badge.js.map +1 -1
- package/dist/components/Breadcrumb/breadcrumb.d.ts +1 -1
- package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
- package/dist/components/Button/button.d.ts +5 -5
- package/dist/components/Button/button.d.ts.map +1 -1
- package/dist/components/Button/button.js +4 -3
- package/dist/components/Button/button.js.map +1 -1
- package/dist/components/Calendar/calendar.d.ts +1 -1
- package/dist/components/Calendar/calendar.d.ts.map +1 -1
- package/dist/components/Calendar/calendar.js +80 -69
- 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 +5 -3
- package/dist/components/Carousel/carousel.js.map +1 -1
- package/dist/components/Checkbox/checkbox-group.d.ts +2 -1
- package/dist/components/Checkbox/checkbox-group.d.ts.map +1 -1
- package/dist/components/Checkbox/checkbox-group.js.map +1 -1
- package/dist/components/Checkbox/checkbox.d.ts +9 -3
- package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox/checkbox.js +8 -3
- package/dist/components/Checkbox/checkbox.js.map +1 -1
- package/dist/components/Chip/chip.d.ts +2 -2
- package/dist/components/Chip/chip.js +3 -2
- package/dist/components/Chip/chip.js.map +1 -1
- package/dist/components/CircularProgress/circular-progress.d.ts +1 -1
- package/dist/components/CircularProgress/circular-progress.js +2 -1
- package/dist/components/CircularProgress/circular-progress.js.map +1 -1
- package/dist/components/Coachmark/coachmark.d.ts +4 -4
- package/dist/components/Coachmark/coachmark.js +2 -1
- package/dist/components/Coachmark/coachmark.js.map +1 -1
- package/dist/components/Combobox/combobox.d.ts.map +1 -1
- package/dist/components/Combobox/combobox.js +17 -13
- package/dist/components/Combobox/combobox.js.map +1 -1
- package/dist/components/Command/command.d.ts +4 -0
- package/dist/components/Command/command.d.ts.map +1 -1
- package/dist/components/Command/command.js.map +1 -1
- package/dist/components/DataTable/data-table.d.ts.map +1 -1
- package/dist/components/DataTable/data-table.js.map +1 -1
- package/dist/components/DateGrid/date-grid.d.ts +8 -4
- package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
- package/dist/components/DateGrid/date-grid.js +2 -2
- package/dist/components/DateGrid/date-grid.js.map +1 -1
- package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
- package/dist/components/DatePicker/date-picker.js +6 -4
- package/dist/components/DatePicker/date-picker.js.map +1 -1
- package/dist/components/DescriptionList/description-list.js +1 -1
- package/dist/components/DescriptionList/description-list.js.map +1 -1
- package/dist/components/Dialog/dialog.d.ts.map +1 -1
- package/dist/components/Dialog/dialog.js.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.js +0 -1
- package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
- package/dist/components/Empty/empty.d.ts +1 -1
- package/dist/components/Empty/empty.js.map +1 -1
- package/dist/components/Field/field-wrapper.js +1 -1
- package/dist/components/Field/field-wrapper.js.map +1 -1
- package/dist/components/Field/field.d.ts.map +1 -1
- package/dist/components/Field/field.js +1 -0
- package/dist/components/Field/field.js.map +1 -1
- package/dist/components/FieldControlGroup/field-control-group.d.ts.map +1 -1
- package/dist/components/FieldControlGroup/field-control-group.js +1 -1
- package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
- package/dist/components/FileUpload/file-upload.d.ts +8 -1
- package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
- package/dist/components/FileUpload/file-upload.js +7 -3
- package/dist/components/FileUpload/file-upload.js.map +1 -1
- package/dist/components/FileViewer/file-viewer.d.ts +1 -1
- package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
- package/dist/components/FileViewer/file-viewer.js +25 -14
- package/dist/components/FileViewer/file-viewer.js.map +1 -1
- package/dist/components/HoverCard/hover-card.d.ts +5 -1
- package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
- package/dist/components/HoverCard/hover-card.js +2 -1
- package/dist/components/HoverCard/hover-card.js.map +1 -1
- package/dist/components/Menu/menu-item.d.ts +6 -2
- package/dist/components/Menu/menu-item.d.ts.map +1 -1
- package/dist/components/Menu/menu-item.js.map +1 -1
- package/dist/components/Notice/notice.d.ts +5 -1
- package/dist/components/Notice/notice.d.ts.map +1 -1
- package/dist/components/Notice/notice.js +2 -1
- package/dist/components/Notice/notice.js.map +1 -1
- package/dist/components/OverflowIndicator/overflow-indicator.d.ts +4 -0
- package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
- package/dist/components/OverflowIndicator/overflow-indicator.js +2 -2
- package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
- package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
- package/dist/components/PeoplePicker/people-picker.js +6 -2
- package/dist/components/PeoplePicker/people-picker.js.map +1 -1
- package/dist/components/Popover/popover.d.ts +1 -1
- package/dist/components/Popover/popover.js +2 -1
- package/dist/components/Popover/popover.js.map +1 -1
- package/dist/components/ProfileCard/profile-card.d.ts +9 -4
- package/dist/components/ProfileCard/profile-card.d.ts.map +1 -1
- package/dist/components/ProfileCard/profile-card.js.map +1 -1
- package/dist/components/RadioGroup/radio-group.d.ts +6 -0
- package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
- package/dist/components/RadioGroup/radio-group.js +4 -0
- package/dist/components/RadioGroup/radio-group.js.map +1 -1
- package/dist/components/ScrollArea/scroll-area.js +1 -1
- package/dist/components/ScrollArea/scroll-area.js.map +1 -1
- package/dist/components/Select/select.d.ts.map +1 -1
- package/dist/components/Select/select.js +14 -4
- package/dist/components/Select/select.js.map +1 -1
- package/dist/components/SelectMenu/select-menu.d.ts +4 -0
- package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
- package/dist/components/SelectMenu/select-menu.js +6 -3
- package/dist/components/SelectMenu/select-menu.js.map +1 -1
- package/dist/components/SelectionControl/selection-item.d.ts +5 -1
- package/dist/components/SelectionControl/selection-item.d.ts.map +1 -1
- package/dist/components/SelectionControl/selection-item.js +3 -1
- package/dist/components/SelectionControl/selection-item.js.map +1 -1
- package/dist/components/Sheet/sheet.d.ts +6 -5
- package/dist/components/Sheet/sheet.d.ts.map +1 -1
- package/dist/components/Sheet/sheet.js.map +1 -1
- package/dist/components/Sidebar/sidebar.d.ts +1 -1
- package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
- package/dist/components/Sidebar/sidebar.js +3 -2
- package/dist/components/Sidebar/sidebar.js.map +1 -1
- package/dist/components/Skeleton/skeleton.d.ts +1 -1
- package/dist/components/Skeleton/skeleton.js +2 -1
- package/dist/components/Skeleton/skeleton.js.map +1 -1
- package/dist/components/Slider/slider.d.ts +1 -1
- package/dist/components/Slider/slider.d.ts.map +1 -1
- package/dist/components/Slider/slider.js +7 -3
- package/dist/components/Slider/slider.js.map +1 -1
- package/dist/components/Steps/steps.d.ts +1 -1
- package/dist/components/Steps/steps.d.ts.map +1 -1
- package/dist/components/Steps/steps.js +9 -6
- package/dist/components/Steps/steps.js.map +1 -1
- package/dist/components/Switch/switch.d.ts +5 -5
- package/dist/components/Switch/switch.js +3 -3
- package/dist/components/Switch/switch.js.map +1 -1
- package/dist/components/Tabs/tabs.js +1 -1
- package/dist/components/Tabs/tabs.js.map +1 -1
- package/dist/components/Textarea/textarea.js +1 -1
- package/dist/components/Textarea/textarea.js.map +1 -1
- package/dist/components/TimePicker/time-columns.js +1 -1
- 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 +2 -1
- package/dist/components/TimePicker/time-picker.js.map +1 -1
- package/dist/components/Toast/toast.js.map +1 -1
- package/dist/components/Tooltip/tooltip.d.ts +1 -1
- package/dist/components/Tooltip/tooltip.js +2 -1
- package/dist/components/Tooltip/tooltip.js.map +1 -1
- package/dist/components/TreeView/tree-view.d.ts +4 -4
- package/dist/components/TreeView/tree-view.d.ts.map +1 -1
- package/dist/components/TreeView/tree-view.js +3 -3
- package/dist/components/TreeView/tree-view.js.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.d.ts +3 -3
- package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
- package/dist/patterns/header-canonical/chrome-header.d.ts +3 -2
- package/dist/patterns/header-canonical/chrome-header.d.ts.map +1 -1
- package/dist/patterns/header-canonical/chrome-header.js.map +1 -1
- package/dist/patterns/overlay-surface/overlay-surface.d.ts +3 -2
- package/dist/patterns/overlay-surface/overlay-surface.d.ts.map +1 -1
- package/dist/patterns/overlay-surface/overlay-surface.js.map +1 -1
- package/ds-canonical/hooks/_log-fire.sh +3 -1
- package/ds-canonical/hooks/check_audit_post_report_validator.sh +21 -1
- package/ds-canonical/hooks/check_audit_sample_escape.sh +6 -3
- package/ds-canonical/hooks/check_chrome_header_avatar_canonical.sh +8 -0
- package/ds-canonical/hooks/check_consumer_app_invariants.sh +321 -0
- package/ds-canonical/hooks/check_datatable_invariants.sh +75 -12
- package/ds-canonical/hooks/check_escape_marker_abuse.sh +15 -1
- package/ds-canonical/hooks/check_field_family_invariants.sh +12 -2
- package/ds-canonical/hooks/check_plugin_fork_health.sh +137 -0
- package/ds-canonical/hooks/check_propose_discipline.sh +147 -0
- package/ds-canonical/hooks/check_solo_workflow.sh +11 -1
- package/ds-canonical/hooks/check_story_invariants.sh +63 -9
- package/ds-canonical/hooks/check_storybook_addon_packaging.sh +151 -0
- package/ds-canonical/hooks/check_tailwind_wildcard_in_docs.sh +8 -2
- package/ds-canonical/hooks/lib/_overlay_handcraft.sh +25 -4
- package/ds-canonical/hooks/lib/_token_hygiene.sh +9 -1
- package/ds-canonical/hooks/session_start_governance_check.sh +7 -2
- package/ds-canonical/hooks/stop_self_audit.sh +2 -2
- package/ds-canonical/hooks/tests/test_check_addon_subdir_ship.sh +3 -2
- package/ds-canonical/hooks/tests/test_check_consumer_app_invariants.sh +12 -0
- package/ds-canonical/hooks/tests/test_check_consumer_app_story_title.sh +3 -2
- package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +6 -4
- package/ds-canonical/hooks/tests/test_check_consumer_no_ds_catalog.sh +7 -4
- package/ds-canonical/hooks/tests/test_check_consumer_story_baseline.sh +6 -4
- package/ds-canonical/hooks/tests/test_check_data_table_size_num_to_meta_width.sh +3 -2
- package/ds-canonical/hooks/tests/test_check_fork_user_plugin_install.sh +3 -2
- package/ds-canonical/hooks/tests/test_check_plugin_fork_health.sh +9 -0
- package/ds-canonical/hooks/tests/test_check_propose_cite_required.sh +3 -2
- package/ds-canonical/hooks/tests/test_check_propose_discipline.sh +10 -0
- package/ds-canonical/hooks/tests/test_check_propose_plain_chinese.sh +3 -2
- package/ds-canonical/hooks/tests/test_check_storybook_addon_packaging.sh +10 -0
- package/ds-canonical/hooks/tests/test_check_storybook_addon_preset_cjs.sh +3 -2
- package/ds-canonical/references/build-ui-canonicals.md +2 -2
- package/ds-canonical/references/composition-fidelity.md +3 -3
- package/ds-canonical/references/naming-conventions.md +1 -0
- package/ds-canonical/references/ssot-consultation.md +1 -1
- package/ds-canonical/references/ssot-index.md +7 -7
- package/ds-canonical/rules/meta-patterns.md +5 -5
- package/ds-canonical/rules/self-verify.md +1 -1
- package/ds-canonical/rules/story-rules.md +2 -0
- package/ds-canonical/rules/ui-development.md +1 -1
- package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +11 -2
- package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +1 -1
- package/ds-canonical/skills/design-system-audit/SKILL.md +15 -15
- package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +4 -3
- package/ds-story-manifest.json +15 -21
- package/llms-full.txt +9 -5
- package/llms.txt +2 -2
- package/package.json +1 -1
- package/src/components/Accordion/accordion.principles.stories.tsx +1 -1
- package/src/components/Accordion/accordion.spec.md +22 -5
- package/src/components/Accordion/accordion.tsx +1 -1
- package/src/components/Alert/alert.anatomy.stories.tsx +4 -4
- package/src/components/Alert/alert.principles.stories.tsx +5 -5
- package/src/components/Alert/alert.spec.md +16 -11
- package/src/components/Alert/alert.stories.tsx +5 -5
- package/src/components/Alert/alert.tsx +2 -2
- package/src/components/AppShell/app-shell.principles.stories.tsx +68 -21
- package/src/components/AppShell/app-shell.spec.md +17 -24
- package/src/components/AppShell/app-shell.tsx +4 -3
- package/src/components/AspectRatio/aspect-ratio.anatomy.stories.tsx +3 -3
- package/src/components/AspectRatio/aspect-ratio.spec.md +15 -1
- package/src/components/Avatar/avatar.anatomy.stories.tsx +1 -1
- package/src/components/Avatar/avatar.principles.stories.tsx +8 -8
- package/src/components/Avatar/avatar.spec.md +25 -54
- package/src/components/Avatar/avatar.tsx +2 -2
- package/src/components/Badge/badge.principles.stories.tsx +2 -2
- package/src/components/Badge/badge.spec.md +22 -13
- package/src/components/Badge/badge.tsx +1 -1
- package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +5 -2
- package/src/components/Breadcrumb/breadcrumb.spec.md +25 -25
- package/src/components/Breadcrumb/breadcrumb.stories.tsx +7 -32
- package/src/components/Breadcrumb/breadcrumb.tsx +1 -1
- package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +2 -2
- package/src/components/BulkActionBar/bulk-action-bar.spec.md +20 -6
- package/src/components/BulkActionBar/bulk-action-bar.tsx +2 -2
- package/src/components/Button/button.anatomy.stories.tsx +2 -2
- package/src/components/Button/button.spec.md +19 -17
- package/src/components/Button/button.stories.tsx +1 -1
- package/src/components/Button/button.tsx +10 -9
- package/src/components/Calendar/calendar.anatomy.stories.tsx +1 -1
- package/src/components/Calendar/calendar.spec.md +37 -17
- package/src/components/Calendar/calendar.tsx +26 -22
- package/src/components/Carousel/carousel.anatomy.stories.tsx +6 -6
- package/src/components/Carousel/carousel.principles.stories.tsx +1 -1
- package/src/components/Carousel/carousel.spec.md +23 -3
- package/src/components/Carousel/carousel.tsx +19 -4
- package/src/components/Chart/chart.anatomy.stories.tsx +1 -1
- package/src/components/Chart/chart.spec.md +20 -1
- package/src/components/Checkbox/checkbox-group.tsx +2 -1
- package/src/components/Checkbox/checkbox.anatomy.stories.tsx +4 -4
- package/src/components/Checkbox/checkbox.principles.stories.tsx +2 -2
- package/src/components/Checkbox/checkbox.spec.md +35 -15
- package/src/components/Checkbox/checkbox.stories.tsx +1 -1
- package/src/components/Checkbox/checkbox.tsx +14 -3
- package/src/components/Chip/chip.anatomy.stories.tsx +5 -5
- package/src/components/Chip/chip.principles.stories.tsx +9 -3
- package/src/components/Chip/chip.spec.md +8 -9
- package/src/components/Chip/chip.tsx +2 -2
- package/src/components/CircularProgress/circular-progress.anatomy.stories.tsx +6 -6
- package/src/components/CircularProgress/circular-progress.spec.md +10 -11
- package/src/components/CircularProgress/circular-progress.stories.tsx +36 -43
- package/src/components/CircularProgress/circular-progress.tsx +1 -1
- package/src/components/Coachmark/coachmark.anatomy.stories.tsx +1 -1
- package/src/components/Coachmark/coachmark.principles.stories.tsx +14 -29
- package/src/components/Coachmark/coachmark.spec.md +21 -13
- package/src/components/Coachmark/coachmark.tsx +4 -4
- package/src/components/Combobox/combobox.anatomy.stories.tsx +28 -28
- package/src/components/Combobox/combobox.principles.stories.tsx +1 -1
- package/src/components/Combobox/combobox.spec.md +25 -7
- package/src/components/Combobox/combobox.tsx +12 -3
- package/src/components/Command/command.anatomy.stories.tsx +1 -1
- package/src/components/Command/command.principles.stories.tsx +8 -8
- package/src/components/Command/command.spec.md +18 -8
- package/src/components/Command/command.tsx +8 -3
- package/src/components/DataTable/data-table-sort-manager.tsx +1 -1
- package/src/components/DataTable/data-table.anatomy.stories.tsx +9 -7
- package/src/components/DataTable/data-table.principles.stories.tsx +3 -3
- package/src/components/DataTable/data-table.spec.md +15 -11
- package/src/components/DataTable/data-table.stories.tsx +58 -13
- package/src/components/DataTable/data-table.tsx +13 -0
- package/src/components/DataTable/filter-operators.spec.md +19 -27
- package/src/components/DateGrid/date-grid.anatomy.stories.tsx +8 -8
- package/src/components/DateGrid/date-grid.principles.stories.tsx +7 -7
- package/src/components/DateGrid/date-grid.spec.md +17 -14
- package/src/components/DateGrid/date-grid.tsx +8 -4
- package/src/components/DatePicker/date-picker.anatomy.stories.tsx +11 -15
- package/src/components/DatePicker/date-picker.principles.stories.tsx +8 -8
- package/src/components/DatePicker/date-picker.spec.md +40 -36
- package/src/components/DatePicker/date-picker.stories.tsx +23 -53
- package/src/components/DatePicker/date-picker.tsx +12 -6
- package/src/components/DescriptionList/description-list.anatomy.stories.tsx +2 -2
- package/src/components/DescriptionList/description-list.principles.stories.tsx +4 -18
- package/src/components/DescriptionList/description-list.spec.md +9 -3
- package/src/components/DescriptionList/description-list.stories.tsx +2 -18
- package/src/components/DescriptionList/description-list.tsx +1 -1
- package/src/components/Dialog/dialog.principles.stories.tsx +1 -1
- package/src/components/Dialog/dialog.spec.md +26 -11
- package/src/components/Dialog/dialog.stories.tsx +159 -184
- package/src/components/Dialog/dialog.tsx +2 -1
- package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +2 -2
- package/src/components/DropdownMenu/dropdown-menu.principles.stories.tsx +16 -17
- package/src/components/DropdownMenu/dropdown-menu.spec.md +10 -15
- package/src/components/DropdownMenu/dropdown-menu.stories.tsx +13 -15
- package/src/components/DropdownMenu/dropdown-menu.tsx +12 -2
- package/src/components/Empty/empty.spec.md +19 -18
- package/src/components/Empty/empty.tsx +1 -1
- package/src/components/Field/field-controls.spec.md +52 -41
- package/src/components/Field/field-wrapper.tsx +2 -2
- package/src/components/Field/field.anatomy.stories.tsx +6 -5
- package/src/components/Field/field.principles.stories.tsx +8 -7
- package/src/components/Field/field.spec.md +42 -20
- package/src/components/Field/field.stories.tsx +9 -7
- package/src/components/Field/field.tsx +6 -3
- package/src/components/Field/form-validation.spec.md +20 -19
- package/src/components/FieldControlGroup/field-control-group.anatomy.stories.tsx +16 -15
- package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +5 -1
- package/src/components/FieldControlGroup/field-control-group.spec.md +17 -1
- package/src/components/FieldControlGroup/field-control-group.stories.tsx +9 -9
- package/src/components/FieldControlGroup/field-control-group.tsx +4 -3
- package/src/components/FileItem/file-item.anatomy.stories.tsx +7 -17
- package/src/components/FileItem/file-item.principles.stories.tsx +7 -7
- package/src/components/FileItem/file-item.spec.md +24 -24
- package/src/components/FileUpload/file-upload.anatomy.stories.tsx +4 -2
- package/src/components/FileUpload/file-upload.principles.stories.tsx +10 -12
- package/src/components/FileUpload/file-upload.spec.md +37 -8
- package/src/components/FileUpload/file-upload.tsx +5 -3
- package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +16 -11
- package/src/components/FileViewer/file-viewer.principles.stories.tsx +4 -4
- package/src/components/FileViewer/file-viewer.spec.md +7 -19
- package/src/components/FileViewer/file-viewer.stories.tsx +3 -3
- package/src/components/FileViewer/file-viewer.tsx +42 -16
- package/src/components/HoverCard/hover-card.spec.md +20 -10
- package/src/components/HoverCard/hover-card.stories.tsx +1 -1
- package/src/components/HoverCard/hover-card.tsx +12 -1
- package/src/components/Input/input.anatomy.stories.tsx +4 -4
- package/src/components/Input/input.principles.stories.tsx +4 -31
- package/src/components/Input/input.spec.md +16 -4
- 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.spec.md +14 -1
- package/src/components/Menu/menu-item.principles.stories.tsx +5 -5
- package/src/components/Menu/menu-item.spec.md +24 -12
- package/src/components/Menu/menu-item.tsx +7 -3
- package/src/components/Notice/notice.principles.stories.tsx +7 -7
- package/src/components/Notice/notice.spec.md +7 -5
- package/src/components/Notice/notice.tsx +5 -1
- package/src/components/NumberInput/number-input.anatomy.stories.tsx +4 -4
- package/src/components/NumberInput/number-input.principles.stories.tsx +13 -35
- package/src/components/NumberInput/number-input.spec.md +19 -0
- package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +4 -3
- package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +2 -5
- package/src/components/OverflowIndicator/overflow-indicator.spec.md +9 -1
- package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +6 -42
- package/src/components/OverflowIndicator/overflow-indicator.tsx +9 -3
- package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +11 -2
- package/src/components/PeoplePicker/people-picker.spec.md +26 -12
- package/src/components/PeoplePicker/people-picker.stories.tsx +14 -15
- package/src/components/PeoplePicker/people-picker.tsx +12 -4
- package/src/components/Popover/popover.spec.md +20 -11
- package/src/components/Popover/popover.tsx +2 -2
- package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
- package/src/components/ProfileCard/profile-card.principles.stories.tsx +29 -25
- package/src/components/ProfileCard/profile-card.spec.md +30 -10
- package/src/components/ProfileCard/profile-card.tsx +12 -7
- package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +1 -1
- package/src/components/ProgressBar/progress-bar.principles.stories.tsx +7 -5
- package/src/components/ProgressBar/progress-bar.spec.md +9 -1
- package/src/components/ProgressBar/progress-bar.stories.tsx +1 -1
- package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +6 -3
- package/src/components/RadioGroup/radio-group.principles.stories.tsx +6 -13
- package/src/components/RadioGroup/radio-group.spec.md +7 -1
- package/src/components/RadioGroup/radio-group.tsx +10 -0
- package/src/components/Rating/rating.anatomy.stories.tsx +1 -0
- package/src/components/Rating/rating.principles.stories.tsx +9 -6
- package/src/components/Rating/rating.spec.md +13 -7
- package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +44 -24
- package/src/components/ScrollArea/scroll-area.principles.stories.tsx +51 -57
- package/src/components/ScrollArea/scroll-area.spec.md +5 -7
- package/src/components/ScrollArea/scroll-area.stories.tsx +110 -60
- package/src/components/ScrollArea/scroll-area.tsx +1 -1
- package/src/components/SegmentedControl/segmented-control.anatomy.stories.tsx +1 -1
- package/src/components/SegmentedControl/segmented-control.spec.md +15 -14
- package/src/components/SegmentedControl/segmented-control.stories.tsx +1 -1
- package/src/components/Select/select.anatomy.stories.tsx +12 -11
- package/src/components/Select/select.principles.stories.tsx +3 -3
- package/src/components/Select/select.spec.md +34 -8
- package/src/components/Select/select.stories.tsx +2 -2
- package/src/components/Select/select.tsx +18 -3
- package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +2 -2
- package/src/components/SelectMenu/select-menu.principles.stories.tsx +5 -5
- package/src/components/SelectMenu/select-menu.spec.md +19 -12
- package/src/components/SelectMenu/select-menu.stories.tsx +34 -27
- package/src/components/SelectMenu/select-menu.tsx +14 -3
- package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +1 -1
- package/src/components/SelectionControl/selection-item.spec.md +13 -2
- package/src/components/SelectionControl/selection-item.stories.tsx +1 -1
- package/src/components/SelectionControl/selection-item.tsx +8 -2
- package/src/components/Separator/separator.principles.stories.tsx +2 -2
- package/src/components/Separator/separator.spec.md +11 -5
- package/src/components/Sheet/sheet.anatomy.stories.tsx +7 -6
- package/src/components/Sheet/sheet.principles.stories.tsx +18 -38
- package/src/components/Sheet/sheet.spec.md +9 -11
- package/src/components/Sheet/sheet.stories.tsx +2 -2
- package/src/components/Sheet/sheet.tsx +6 -5
- package/src/components/Sidebar/sidebar.anatomy.stories.tsx +42 -33
- package/src/components/Sidebar/sidebar.principles.stories.tsx +4 -4
- package/src/components/Sidebar/sidebar.spec.md +22 -17
- package/src/components/Sidebar/sidebar.stories.tsx +16 -0
- package/src/components/Sidebar/sidebar.tsx +15 -7
- package/src/components/Skeleton/skeleton.anatomy.stories.tsx +2 -2
- package/src/components/Skeleton/skeleton.principles.stories.tsx +20 -7
- package/src/components/Skeleton/skeleton.spec.md +6 -4
- package/src/components/Skeleton/skeleton.stories.tsx +34 -26
- package/src/components/Skeleton/skeleton.tsx +1 -1
- package/src/components/Slider/slider.principles.stories.tsx +3 -3
- package/src/components/Slider/slider.spec.md +16 -12
- package/src/components/Slider/slider.stories.tsx +2 -18
- package/src/components/Slider/slider.tsx +6 -2
- package/src/components/Steps/steps.anatomy.stories.tsx +3 -3
- package/src/components/Steps/steps.principles.stories.tsx +9 -12
- package/src/components/Steps/steps.spec.md +31 -25
- package/src/components/Steps/steps.stories.tsx +5 -40
- package/src/components/Steps/steps.tsx +17 -12
- package/src/components/Switch/switch.principles.stories.tsx +1 -1
- package/src/components/Switch/switch.spec.md +13 -0
- package/src/components/Switch/switch.tsx +3 -3
- package/src/components/Tabs/tabs.anatomy.stories.tsx +4 -4
- package/src/components/Tabs/tabs.principles.stories.tsx +1 -1
- package/src/components/Tabs/tabs.spec.md +13 -6
- package/src/components/Tabs/tabs.stories.tsx +3 -3
- package/src/components/Tabs/tabs.tsx +2 -2
- package/src/components/Tag/tag.anatomy.stories.tsx +15 -15
- package/src/components/Tag/tag.principles.stories.tsx +1 -1
- package/src/components/Tag/tag.spec.md +25 -5
- package/src/components/Textarea/textarea.anatomy.stories.tsx +3 -3
- package/src/components/Textarea/textarea.principles.stories.tsx +7 -7
- package/src/components/Textarea/textarea.spec.md +15 -3
- package/src/components/Textarea/textarea.tsx +1 -1
- package/src/components/TimePicker/time-columns.tsx +1 -1
- package/src/components/TimePicker/time-picker.anatomy.stories.tsx +8 -2
- package/src/components/TimePicker/time-picker.spec.md +23 -3
- package/src/components/TimePicker/time-picker.tsx +5 -3
- package/src/components/Toast/toast.anatomy.stories.tsx +1 -1
- package/src/components/Toast/toast.spec.md +7 -4
- package/src/components/Toast/toast.stories.tsx +12 -6
- package/src/components/Toast/toast.tsx +1 -1
- package/src/components/Tooltip/tooltip.anatomy.stories.tsx +1 -1
- package/src/components/Tooltip/tooltip.spec.md +11 -8
- package/src/components/Tooltip/tooltip.tsx +1 -1
- package/src/components/TreeView/tree-view.anatomy.stories.tsx +25 -9
- package/src/components/TreeView/tree-view.principles.stories.tsx +5 -5
- package/src/components/TreeView/tree-view.spec.md +11 -9
- package/src/components/TreeView/tree-view.stories.tsx +5 -3
- package/src/components/TreeView/tree-view.tsx +8 -5
- package/src/patterns/action-bar/action-bar.spec.md +11 -4
- package/src/patterns/element-anatomy/element-anatomy.spec.md +8 -6
- package/src/patterns/element-anatomy/inline-action.spec.md +18 -18
- package/src/patterns/element-anatomy/item-anatomy.spec.md +47 -42
- package/src/patterns/element-anatomy/item-anatomy.stories.tsx +6 -6
- package/src/patterns/element-anatomy/item-anatomy.tsx +3 -3
- package/src/patterns/header-canonical/chrome-header.tsx +3 -2
- package/src/patterns/header-canonical/header-canonical.spec.md +24 -11
- package/src/patterns/header-canonical/header-canonical.stories.tsx +9 -9
- package/src/patterns/horizontal-overflow/horizontal-overflow.spec.md +22 -12
- package/src/patterns/overlay-surface/overlay-surface.spec.md +43 -39
- package/src/patterns/overlay-surface/overlay-surface.tsx +3 -2
- package/src/patterns/resize-handle/resize-handle.spec.md +10 -1
- package/src/patterns/resize-handle/resize-handle.stories.tsx +7 -7
- package/src/tokens/README.md +5 -2
- package/src/tokens/color/color.spec.md +17 -10
- package/src/tokens/color/semantic.css +2 -0
- package/src/tokens/density/density.spec.md +4 -14
- package/src/tokens/elevation/elevation.spec.md +7 -8
- package/src/tokens/layoutSpace/layoutSpace.spec.md +20 -27
- package/src/tokens/motion/motion.spec.md +15 -7
- package/src/tokens/opacity/opacity.spec.md +15 -26
- package/src/tokens/orphan-tokens.spec.md +14 -3
- package/src/tokens/radius/radius.spec.md +13 -15
- package/src/tokens/token-system.spec.md +10 -13
- package/src/tokens/typography/typography.spec.md +8 -12
- package/src/tokens/uiSize/uiSize.spec.md +28 -16
- /package/ds-canonical/hooks/{check_addon_subdir_ship.sh → retired/2026-06-11-prune-merge/check_addon_subdir_ship.sh} +0 -0
- /package/ds-canonical/hooks/{check_consumer_app_story_title.sh → retired/2026-06-11-prune-merge/check_consumer_app_story_title.sh} +0 -0
- /package/ds-canonical/hooks/{check_consumer_ds_primitive_misuse.sh → retired/2026-06-11-prune-merge/check_consumer_ds_primitive_misuse.sh} +0 -0
- /package/ds-canonical/hooks/{check_consumer_no_ds_catalog.sh → retired/2026-06-11-prune-merge/check_consumer_no_ds_catalog.sh} +0 -0
- /package/ds-canonical/hooks/{check_consumer_story_baseline.sh → retired/2026-06-11-prune-merge/check_consumer_story_baseline.sh} +0 -0
- /package/ds-canonical/hooks/{check_data_table_size_num_to_meta_width.sh → retired/2026-06-11-prune-merge/check_data_table_size_num_to_meta_width.sh} +0 -0
- /package/ds-canonical/hooks/{check_fork_user_plugin_install.sh → retired/2026-06-11-prune-merge/check_fork_user_plugin_install.sh} +0 -0
- /package/ds-canonical/hooks/{check_plugin_freshness.sh → retired/2026-06-11-prune-merge/check_plugin_freshness.sh} +0 -0
- /package/ds-canonical/hooks/{check_propose_cite_required.sh → retired/2026-06-11-prune-merge/check_propose_cite_required.sh} +0 -0
- /package/ds-canonical/hooks/{check_propose_plain_chinese.sh → retired/2026-06-11-prune-merge/check_propose_plain_chinese.sh} +0 -0
- /package/ds-canonical/hooks/{check_storybook_addon_preset_cjs.sh → retired/2026-06-11-prune-merge/check_storybook_addon_preset_cjs.sh} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"description-list.js","sources":["../../../src/components/DescriptionList/description-list.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 { cn } from '@/lib/utils'\n\n/**\n * DescriptionList — 唯讀 label + value 展示\n *\n * HTML 語義:dl + dt + dd(跟 Atlassian、Shopify Polaris 對齊)。\n *\n * ── Typography(兩個方向都一致)──\n * label (dt): text-body (14px) text-fg-secondary (neutral-8)\n * value (dd): text-body (14px) text-foreground (neutral-9)\n * 兩者都是 14px × 1.5 行高——層級靠色彩區分,不靠字體大小。\n *\n * ── direction(2026-04-20 新增)──\n * vertical(預設):label 在上 / value 在下,適合長 value(地址、bio、說明段落)、\n * form-like 資訊展示\n * horizontal :label 左 / value 右對齊,適合短 value 的 metadata 列\n * (檔案資訊、訂單詳情、settings summary)— Google Drive /\n * Notion file info panel 模式\n *\n * ── divided(horizontal 專用,預設 false)──\n * 每個 item 下方加 `border-b border-divider`,rows 視覺對齊。長列表、key 長度\n * 不一時需要對齊格線才易讀 → 開 divided;短列表(< 4 rows)不需要。\n *\n * ── 間距 ──\n * vertical: label → value(同 item 內): `var(--item-gap-label-desc-reading)` token(2px,item-anatomy reading tier SSOT;code L115 dd 消費)\n * horizontal: label ↔ value: gap-x-4(16px)最小間距\n * items 之間垂直 gap: layout-space-tight(density-aware)\n * divided horizontal 模式:每 item py-[var(--layout-space-tight)](cell-like row 高度)\n */\n\nexport type DescriptionDirection = 'vertical' | 'horizontal'\n\ninterface DescriptionContextValue {\n direction: DescriptionDirection\n divided: boolean\n}\n\nconst DescriptionContext = React.createContext<DescriptionContextValue>({\n direction: 'vertical',\n divided: false,\n})\n\nexport interface DescriptionListProps extends React.HTMLAttributes<HTMLDListElement> {\n /** grid 欄數(vertical 才生效;horizontal 永遠單欄),預設 1 */\n cols?: 1 | 2 | 3\n /** 項目排列方向,預設 vertical(label 在上 / value 在下) */\n direction?: DescriptionDirection\n /**\n * horizontal 模式下每個 item 下方加分隔線以對齊 rows。預設 false。\n * 短列表(< 4 rows)不需要;檔案 metadata 等長列表、key 長度不一時建議開。\n */\n divided?: boolean\n}\n\nconst colsClass: Record<number, string> = {\n 1: 'grid-cols-1',\n 2: 'grid-cols-2',\n 3: 'grid-cols-3',\n}\n\nconst DescriptionList = React.forwardRef<HTMLDListElement, DescriptionListProps>(\n ({ cols = 1, direction = 'vertical', divided = false, className, ...props }, ref) => {\n const isHorizontal = direction === 'horizontal'\n // Memoize provider value(2026-04-22 D3 perf audit):避免每 render 重建 2-field object\n const ctxValue = React.useMemo(() => ({ direction, divided }), [direction, divided])\n return (\n <DescriptionContext.Provider value={ctxValue}>\n <dl\n ref={ref}\n className={cn(\n isHorizontal\n ? 'flex flex-col'\n : cn('grid gap-x-4 gap-y-[var(--layout-space-tight)]', colsClass[cols]),\n className,\n )}\n {...props}\n />\n </DescriptionContext.Provider>\n )\n },\n)\nDescriptionList.displayName = 'DescriptionList'\n\nexport interface DescriptionItemProps extends React.HTMLAttributes<HTMLDivElement> {\n label: string\n children: React.ReactNode\n}\n\nconst DescriptionItem = React.forwardRef<HTMLDivElement, DescriptionItemProps>(\n ({ label, children, className, ...props }, ref) => {\n const { direction, divided } = React.useContext(DescriptionContext)\n if (direction === 'horizontal') {\n return (\n <div\n ref={ref}\n className={cn(\n 'flex items-baseline justify-between gap-4',\n divided\n ? 'py-[var(--layout-space-tight)] border-b border-divider last:border-b-0'\n : 'mb-[var(--layout-space-tight)] last:mb-0',\n className,\n )}\n {...props}\n >\n <dt className=\"text-body text-fg-secondary shrink-0\">{label}</dt>\n <dd className=\"text-body text-foreground text-right break-all min-w-0\">{children}</dd>\n </div>\n )\n }\n return (\n <div ref={ref} className={cn('flex flex-col', className)} {...props}>\n <dt className=\"text-body text-fg-secondary\">{label}</dt>\n <dd className=\"text-body mt-[var(--item-gap-label-desc-reading)]\">{children}</dd>\n </div>\n )\n },\n)\nDescriptionItem.displayName = 'DescriptionItem'\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const descriptionListMeta = {\n component: 'DescriptionList',\n family: null, // non-family composite / overlay / layout\n variants: {\n\n },\n sizes: {\n\n },\n states: [], // 純唯讀 dl/dt/dd,無 hover/active/focus/disabled 互動 state(spec
|
|
1
|
+
{"version":3,"file":"description-list.js","sources":["../../../src/components/DescriptionList/description-list.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 { cn } from '@/lib/utils'\n\n/**\n * DescriptionList — 唯讀 label + value 展示\n *\n * HTML 語義:dl + dt + dd(跟 Atlassian、Shopify Polaris 對齊)。\n *\n * ── Typography(兩個方向都一致)──\n * label (dt): text-body (14px) text-fg-secondary (neutral-8)\n * value (dd): text-body (14px) text-foreground (neutral-9)\n * 兩者都是 14px × 1.5 行高——層級靠色彩區分,不靠字體大小。\n *\n * ── direction(2026-04-20 新增)──\n * vertical(預設):label 在上 / value 在下,適合長 value(地址、bio、說明段落)、\n * form-like 資訊展示\n * horizontal :label 左 / value 右對齊,適合短 value 的 metadata 列\n * (檔案資訊、訂單詳情、settings summary)— Google Drive /\n * Notion file info panel 模式\n *\n * ── divided(horizontal 專用,預設 false)──\n * 每個 item 下方加 `border-b border-divider`,rows 視覺對齊。長列表、key 長度\n * 不一時需要對齊格線才易讀 → 開 divided;短列表(< 4 rows)不需要。\n *\n * ── 間距 ──\n * vertical: label → value(同 item 內): `var(--item-gap-label-desc-reading)` token(2px,item-anatomy reading tier SSOT;code L115 dd 消費)\n * horizontal: label ↔ value: gap-x-4(16px)最小間距\n * items 之間垂直 gap: layout-space-tight(density-aware)\n * divided horizontal 模式:每 item py-[var(--layout-space-tight)](cell-like row 高度)\n */\n\nexport type DescriptionDirection = 'vertical' | 'horizontal'\n\ninterface DescriptionContextValue {\n direction: DescriptionDirection\n divided: boolean\n}\n\nconst DescriptionContext = React.createContext<DescriptionContextValue>({\n direction: 'vertical',\n divided: false,\n})\n\nexport interface DescriptionListProps extends React.HTMLAttributes<HTMLDListElement> {\n /** grid 欄數(vertical 才生效;horizontal 永遠單欄),預設 1 */\n cols?: 1 | 2 | 3\n /** 項目排列方向,預設 vertical(label 在上 / value 在下) */\n direction?: DescriptionDirection\n /**\n * horizontal 模式下每個 item 下方加分隔線以對齊 rows。預設 false。\n * 短列表(< 4 rows)不需要;檔案 metadata 等長列表、key 長度不一時建議開。\n */\n divided?: boolean\n}\n\nconst colsClass: Record<number, string> = {\n 1: 'grid-cols-1',\n 2: 'grid-cols-2',\n 3: 'grid-cols-3',\n}\n\nconst DescriptionList = React.forwardRef<HTMLDListElement, DescriptionListProps>(\n ({ cols = 1, direction = 'vertical', divided = false, className, ...props }, ref) => {\n const isHorizontal = direction === 'horizontal'\n // Memoize provider value(2026-04-22 D3 perf audit):避免每 render 重建 2-field object\n const ctxValue = React.useMemo(() => ({ direction, divided }), [direction, divided])\n return (\n <DescriptionContext.Provider value={ctxValue}>\n <dl\n ref={ref}\n className={cn(\n isHorizontal\n ? 'flex flex-col'\n : cn('grid gap-x-4 gap-y-[var(--layout-space-tight)]', colsClass[cols]),\n className,\n )}\n {...props}\n />\n </DescriptionContext.Provider>\n )\n },\n)\nDescriptionList.displayName = 'DescriptionList'\n\nexport interface DescriptionItemProps extends React.HTMLAttributes<HTMLDivElement> {\n label: string\n children: React.ReactNode\n}\n\nconst DescriptionItem = React.forwardRef<HTMLDivElement, DescriptionItemProps>(\n ({ label, children, className, ...props }, ref) => {\n const { direction, divided } = React.useContext(DescriptionContext)\n if (direction === 'horizontal') {\n return (\n <div\n ref={ref}\n className={cn(\n 'flex items-baseline justify-between gap-4',\n divided\n ? 'py-[var(--layout-space-tight)] border-b border-divider last:border-b-0'\n : 'mb-[var(--layout-space-tight)] last:mb-0',\n className,\n )}\n {...props}\n >\n <dt className=\"text-body text-fg-secondary shrink-0\">{label}</dt>\n <dd className=\"text-body text-foreground text-right break-all min-w-0\">{children}</dd>\n </div>\n )\n }\n return (\n <div ref={ref} className={cn('flex flex-col', className)} {...props}>\n <dt className=\"text-body text-fg-secondary\">{label}</dt>\n <dd className=\"text-body mt-[var(--item-gap-label-desc-reading)]\">{children}</dd>\n </div>\n )\n },\n)\nDescriptionItem.displayName = 'DescriptionItem'\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const descriptionListMeta = {\n component: 'DescriptionList',\n family: null, // non-family composite / overlay / layout\n variants: {\n\n },\n sizes: {\n\n },\n states: [], // 純唯讀 dl/dt/dd,無 hover/active/focus/disabled 互動 state(spec「為何無 ColorMatrix / SizeMatrix」段)\n tokens: {\n bg: [],\n fg: ['text-fg-secondary', 'text-foreground'],\n ring: [],\n },\n} as const\n\nexport { DescriptionList, DescriptionItem }\n"],"names":[],"mappings":";;;AAuCA,MAAM,qBAAqB,MAAM,cAAuC;AAAA,EACtE,WAAW;AAAA,EACX,SAAS;AACX,CAAC;AAcD,MAAM,YAAoC;AAAA,EACxC,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AACL;AAEA,MAAM,kBAAkB,MAAM;AAAA,EAC5B,CAAC,EAAE,OAAO,GAAG,YAAY,YAAY,UAAU,OAAO,WAAW,GAAG,MAAA,GAAS,QAAQ;AACnF,UAAM,eAAe,cAAc;AAEnC,UAAM,WAAW,MAAM,QAAQ,OAAO,EAAE,WAAW,QAAA,IAAY,CAAC,WAAW,OAAO,CAAC;AACnF,WACE,oBAAC,mBAAmB,UAAnB,EAA4B,OAAO,UAClC,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAW;AAAA,UACT,eACI,kBACA,GAAG,kDAAkD,UAAU,IAAI,CAAC;AAAA,UACxE;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,MAAA;AAAA,IAAA,GAER;AAAA,EAEJ;AACF;AACA,gBAAgB,cAAc;AAO9B,MAAM,kBAAkB,MAAM;AAAA,EAC5B,CAAC,EAAE,OAAO,UAAU,WAAW,GAAG,MAAA,GAAS,QAAQ;AACjD,UAAM,EAAE,WAAW,QAAA,IAAY,MAAM,WAAW,kBAAkB;AAClE,QAAI,cAAc,cAAc;AAC9B,aACE;AAAA,QAAC;AAAA,QAAA;AAAA,UACC;AAAA,UACA,WAAW;AAAA,YACT;AAAA,YACA,UACI,2EACA;AAAA,YACJ;AAAA,UAAA;AAAA,UAED,GAAG;AAAA,UAEJ,UAAA;AAAA,YAAA,oBAAC,MAAA,EAAG,WAAU,wCAAwC,UAAA,OAAM;AAAA,YAC5D,oBAAC,MAAA,EAAG,WAAU,0DAA0D,SAAA,CAAS;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IAGvF;AACA,WACE,qBAAC,SAAI,KAAU,WAAW,GAAG,iBAAiB,SAAS,GAAI,GAAG,OAC5D,UAAA;AAAA,MAAA,oBAAC,MAAA,EAAG,WAAU,+BAA+B,UAAA,OAAM;AAAA,MACnD,oBAAC,MAAA,EAAG,WAAU,qDAAqD,SAAA,CAAS;AAAA,IAAA,GAC9E;AAAA,EAEJ;AACF;AACA,gBAAgB,cAAc;AAIvB,MAAM,sBAAsB;AAAA,EACjC,WAAW;AAAA,EACX,QAAQ;AAAA;AAAA,EACR,UAAU,CAAA;AAAA,EAGV,OAAO,CAAA;AAAA,EAGP,QAAQ,CAAA;AAAA;AAAA,EACR,QAAQ;AAAA,IACN,IAAI,CAAA;AAAA,IACJ,IAAI,CAAC,qBAAqB,iBAAiB;AAAA,IAC3C,MAAM,CAAA;AAAA,EAAC;AAEX;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../src/components/Dialog/dialog.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAA;AAKzD,OAAO,EAAgC,KAAK,kBAAkB,EAAE,MAAM,0DAA0D,CAAA;AAGhI;;;;;;;;;;;;;;;GAeG;AAEH,QAAA,MAAM,MAAM,uCAAuB,CAAA;AACnC,QAAA,MAAM,aAAa,8GAA0B,CAAA;AAC7C,QAAA,MAAM,YAAY,6CAAyB,CAAA;AAC3C,QAAA,MAAM,WAAW,4GAAwB,CAAA;AAKzC,QAAA,MAAM,aAAa,8JAcjB,CAAA;AAGF,UAAU,kBAAmB,SAAQ,KAAK,CAAC,wBAAwB,CAAC,OAAO,eAAe,CAAC,OAAO,CAAC;IACjG,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC1B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,QAAA,MAAM,aAAa,2FAiEjB,CAAA;AASF,QAAA,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../src/components/Dialog/dialog.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAA;AAKzD,OAAO,EAAgC,KAAK,kBAAkB,EAAE,MAAM,0DAA0D,CAAA;AAGhI;;;;;;;;;;;;;;;GAeG;AAEH,QAAA,MAAM,MAAM,uCAAuB,CAAA;AACnC,QAAA,MAAM,aAAa,8GAA0B,CAAA;AAC7C,QAAA,MAAM,YAAY,6CAAyB,CAAA;AAC3C,QAAA,MAAM,WAAW,4GAAwB,CAAA;AAKzC,QAAA,MAAM,aAAa,8JAcjB,CAAA;AAGF,UAAU,kBAAmB,SAAQ,KAAK,CAAC,wBAAwB,CAAC,OAAO,eAAe,CAAC,OAAO,CAAC;IACjG,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC1B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,QAAA,MAAM,aAAa,2FAiEjB,CAAA;AASF,QAAA,MAAM,YAAY,2FAuBhB,CAAA;AA0BF,QAAA,MAAM,UAAU,oOAcd,CAAA;AAIF,QAAA,MAAM,YAAY,6GAGiE,CAAA;AAGnF,QAAA,MAAM,WAAW,oKASf,CAAA;AAGF,QAAA,MAAM,iBAAiB,8KAWrB,CAAA;AAKF,eAAO,MAAM,UAAU;;;;;;;;;;;CAeb,CAAA;AAEV,OAAO,EACL,MAAM,EACN,YAAY,EACZ,aAAa,EACb,WAAW,EACX,aAAa,EACb,aAAa,EACb,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,WAAW,EACX,iBAAiB,GAClB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dialog.js","sources":["../../../src/components/Dialog/dialog.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 * as DialogPrimitive from \"@radix-ui/react-dialog\"\nimport { X as XIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/design-system/components/Button/button\"\nimport { SurfaceHeader, SurfaceFooter, type SurfaceHeaderProps } from \"@/design-system/patterns/overlay-surface/overlay-surface\"\nimport { ScrollArea } from \"@/design-system/components/ScrollArea/scroll-area\"\n\n/**\n * Dialog (Modal) — Radix Dialog + 設計系統 token\n *\n * ── Layout ──\n * px = layout-space-loose, header/footer py = layout-space-tight。\n * Body pt = layout-space-tight, pb = layout-space-bottom。\n * Density:繼承 page `data-density`(v5 校準,跟 Sheet 對齊;header 自動對齊\n * `--chrome-header-height` 48/56)。詳 dialog.spec.md「Density」節。\n *\n * ── Viewport Inset ──\n * Modal 與 viewport 四邊保持 layout-space-bottom (48px) 最小間距。\n *\n * ── 高度行為 ──\n * 預設:height 填滿 viewport(扣除 inset),body 捲動。防止動態內容跳動。\n * height=\"auto\":高度隨內容,超過 viewport 時 max-height 安全帽。\n */\n\nconst Dialog = DialogPrimitive.Root\nconst DialogTrigger = DialogPrimitive.Trigger\nconst DialogPortal = DialogPrimitive.Portal\nconst DialogClose = DialogPrimitive.Close\n\n// Modal 與 viewport 四邊的最小間距 = layout-space-bottom (48px)\nconst DIALOG_INSET_VAR = 'var(--layout-space-bottom)'\n\nconst DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n \"fixed inset-0 z-50 bg-overlay\",\n \"data-[state=open]:animate-in data-[state=closed]:animate-out motion-reduce:animate-none\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className,\n )}\n {...props}\n />\n))\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName\n\ninterface DialogContentProps extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {\n /** 最大寬度。預設 512px。傳 number 視為 px。 */\n maxWidth?: string | number\n /**\n * 高度模式。\n * - 不傳(預設):填滿 viewport(height = 100vh - inset*2),body 捲動。防止內容跳動。\n * - true:高度隨內容,超過 viewport 時捲動(max-height 安全帽)。\n */\n autoHeight?: boolean\n}\n\nconst DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n DialogContentProps\n>(({ className, maxWidth = '512px', autoHeight, children, style, ...props }, ref) => {\n const insetCalc = `${DIALOG_INSET_VAR} * 2`\n const viewportH = `calc(100vh - ${insetCalc})`\n const maxWidthCss = typeof maxWidth === 'number' ? `${maxWidth}px` : maxWidth\n\n const heightStyle: React.CSSProperties = autoHeight\n ? { maxHeight: viewportH }\n : { height: viewportH }\n\n // AutoFocus canonical(對齊 Material / Polaris / Atlassian)—\n // 開啟時 focus 落在 body 第一個有意義互動元素(input / button),不是 chrome close X。\n // 預設 Radix 會 focus first tabbable = close X → Button iconOnly 的 focus-triggered\n // tooltip 會立即顯示「關閉」,user-hostile。此 callback 攔截:先找 body 第一個\n // input/textarea/select/button(排除 data-dismiss)focus;找不到就 focus container(不 focus X)。\n const handleOpenAutoFocus = (e: Event) => {\n e.preventDefault()\n const content = e.currentTarget as HTMLElement\n const firstBodyTarget = content.querySelector<HTMLElement>(\n '[data-dialog-body] input:not([disabled]),[data-dialog-body] textarea:not([disabled]),[data-dialog-body] select:not([disabled]),[data-dialog-body] button:not([disabled]):not([data-dismiss])'\n )\n const firstFooterButton = content.querySelector<HTMLElement>(\n '[data-dialog-footer] button:not([disabled]):not([data-dismiss])'\n )\n ;(firstBodyTarget ?? firstFooterButton ?? content).focus({ preventScroll: true })\n }\n\n return (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n // Density canonical(2026-04-22 v5 校準):Dialog 繼承 page density(跟 Sheet 對齊\n // sheet.tsx line 111 canonical),不自設 data-layout-space=\"lg\" 或 data-density。\n //\n // 先前曾設 `data-layout-space=\"lg\"` 給 header/body 寬鬆呼吸,但跟 chrome-header-height\n // canonical 衝突(md page dialog header 期望 48,強設 lg 會變 56)。\n // 世界級對照:Polaris Modal horizontal padding 16 / Material M3 24 / Atlassian 24 — 16 是\n // 合理 lower bound;md page 用 16 loose body padding 可接受,lg page 自動 24。\n // 詳 overlay-surface.spec.md「Chrome dismiss size canonical」\n onOpenAutoFocus={handleOpenAutoFocus}\n className={cn(\n \"fixed left-1/2 top-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2\",\n \"flex flex-col bg-surface-raised rounded-lg border border-border\",\n \"data-[state=open]:animate-in data-[state=closed]:animate-out motion-reduce:animate-none\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%]\",\n \"data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]\",\n className,\n )}\n style={{\n boxShadow: 'var(--elevation-200)',\n maxWidth: `min(${maxWidthCss}, calc(100vw - ${insetCalc}))`,\n ...heightStyle,\n ...style,\n }}\n {...props}\n >\n {children}\n </DialogPrimitive.Content>\n </DialogPortal>\n )\n})\nDialogContent.displayName = DialogPrimitive.Content.displayName\n\n// DialogHeader: SurfaceHeader + Close 按鈕(Dialog 特有)\n// justify-between 讓 children 與 Close 分左右;Close 用 Radix DialogPrimitive.Close 包裝。\n// 2026-05-18 audit gap fix:type 用 SurfaceHeaderProps 對齊 — DialogHeader 是 SurfaceHeader\n// 薄包裝,withTabs / lockDensity 等 props 透過 spread 已 forward,但 TS type 沒 expose\n// 導致 consumer 不能用 `<DialogHeader withTabs>` 而只能寫 `as any` 繞。Type lift 修\n// per header-canonical.spec.md W1 跨 6 consumer 同契約。\nconst DialogHeader = React.forwardRef<\n HTMLDivElement,\n SurfaceHeaderProps\n>(({ className, children, ...props }, ref) => (\n // 2026-05-18:className 不再硬加 justify-between(冗餘:row 1 是 flex items-center gap-2,\n // 第一 child flex-1 grow 自然 push close X 靠右,跟 justify-between 同視覺)。\n // 並且 column mode(tabsSlot 提供)justify-between 會把 row 1 / row 2 上下推開 = 破裂。\n // tabsSlot via `...props` spread 自動 forward(type 來自 SurfaceHeaderProps)。\n <SurfaceHeader\n ref={ref}\n className={className}\n {...props}\n >\n <div className=\"flex-1 min-w-0\">{children}</div>\n {/* Dismiss X(chrome-slot canonical,v5):Button 本身 native sm(28 md / 32 lg,touch target 亦同),\n 但 data-dismiss attribute 讓 SurfaceHeader CSS rule 套負 my 讓 layout 佔位 = 24,\n header = 24 + 2×tight = 48 / 56 chrome-header-height ✓。\n 詳 overlay-surface.spec.md「Chrome dismiss size canonical」*/}\n <DialogPrimitive.Close asChild>\n <Button data-dismiss iconOnly dismiss size=\"sm\" startIcon={XIcon} aria-label=\"關閉\" />\n </DialogPrimitive.Close>\n </SurfaceHeader>\n))\nDialogHeader.displayName = \"DialogHeader\"\n\n// DialogBody: flex-1 ScrollArea + chrome padding(對齊 overlay-surface SSOT + ScrollArea canonical)\n// 捲軸必用 ScrollArea(跨 OS 一致、不吃寬度)— 不自寫 overflow-y-auto。\n// padding 搬進 viewport inner div:px-loose / pt-tight / pb-bottom(Dialog 「大容器」底部多一拍呼吸)。\n// data-dialog-body:讓 DialogContent onOpenAutoFocus 找得到 body 第一個有意義互動元素(避免 focus 到 close X)\n//\n// ── List-as-region 場景(menu group / Cmd+K)──\n// 不再提供 `flush` variant(2026-05-01 移除,先前曾叫 `variant=\"list\"`)。\n// **canonical pattern** = consumer 自管 list outer wrapper + 用 `className` override 撤掉 chrome padding:\n// ```tsx\n// <DialogBody className=\"!px-0 !pt-0 !pb-0\">\n// <div className=\"py-2\"> {/* list outer wrapper 自帶 py-2(menu group canonical)*/}\n// {items.map(item => <MenuItem className=\"px-[var(--layout-space-loose)] rounded-md\" />)}\n// </div>\n// </DialogBody>\n// ```\n// **rationale**:flush 只為 list-only body 省一行 className,但 (a) 多一個 row(search / banner)\n// 就破功 → 保留 chrome padding 反而更穩,(b) 加新 variant 不解決底層脆弱(consumer 仍要管 list py\n// 且 item px-loose),反而把 1 個 surface decision 拆兩 API。世界級主流(Material/Atlassian/Mantine/\n// shadcn)無 universal LayoutBody flush variant,Polaris flush API 只用於極窄 scope。\n// 詳 `tokens/layoutSpace/layoutSpace.spec.md`「List-as-region in overlay body」節\n// `className` forward 到 **inner content div**(非外層 ScrollArea wrapper)——\n// consumer `<DialogBody className=\"flex flex-col gap-X\">` 期望作用於 children 排列;\n// 套在 ScrollArea 上會 0 效果(children 住 inner div),曾造成 modal form field 完全貼邊。\nconst DialogBody = React.forwardRef<\n HTMLDivElement,\n React.ComponentPropsWithoutRef<typeof ScrollArea>\n>(({ className, children, ...props }, ref) => (\n <ScrollArea ref={ref} data-dialog-body className=\"flex-1 min-h-0\" {...props}>\n <div\n className={cn(\n \"px-[var(--layout-space-loose)] pt-[var(--layout-space-tight)] pb-[var(--layout-space-bottom)]\",\n className,\n )}\n >\n {children}\n </div>\n </ScrollArea>\n))\nDialogBody.displayName = \"DialogBody\"\n\n// DialogFooter: SurfaceFooter wrap 加 data-dialog-footer(autoFocus fallback target)\nconst DialogFooter = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ ...props }, ref) => <SurfaceFooter ref={ref} data-dialog-footer {...props} />)\nDialogFooter.displayName = \"DialogFooter\"\n\nconst DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn(\"text-body-lg font-medium truncate\", className)}\n {...props}\n />\n))\nDialogTitle.displayName = DialogPrimitive.Title.displayName\n\nconst DialogDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n // title → description 間距 canonical:DialogTitle 是 body-lg(16)+ desc body(14)→ reading-lg token\n // (label tier 決定 token 選擇;item-anatomy Family 2 reading-family token 對照表)\n className={cn(\"mt-[var(--item-gap-label-desc-reading-lg)] text-body text-fg-secondary\", className)}\n {...props}\n />\n))\nDialogDescription.displayName = DialogPrimitive.Description.displayName\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const dialogMeta = {\n component: 'Dialog',\n family: null, // non-family composite / overlay / layout\n variants: {\n\n },\n sizes: {\n\n },\n states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-surface-raised'],\n fg: ['text-fg-secondary'],\n ring: [],\n },\n} as const\n\nexport {\n Dialog,\n DialogPortal,\n DialogOverlay,\n DialogClose,\n DialogTrigger,\n DialogContent,\n DialogHeader,\n DialogBody,\n DialogFooter,\n DialogTitle,\n DialogDescription,\n}\n"],"names":["XIcon"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dialog.js","sources":["../../../src/components/Dialog/dialog.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 * as DialogPrimitive from \"@radix-ui/react-dialog\"\nimport { X as XIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/design-system/components/Button/button\"\nimport { SurfaceHeader, SurfaceFooter, type SurfaceHeaderProps } from \"@/design-system/patterns/overlay-surface/overlay-surface\"\nimport { ScrollArea } from \"@/design-system/components/ScrollArea/scroll-area\"\n\n/**\n * Dialog (Modal) — Radix Dialog + 設計系統 token\n *\n * ── Layout ──\n * px = layout-space-loose, header/footer py = layout-space-tight。\n * Body pt = layout-space-tight, pb = layout-space-bottom。\n * Density:繼承 page `data-density`(v5 校準,跟 Sheet 對齊;header 自動對齊\n * `--chrome-header-height` 48/56)。詳 dialog.spec.md「Density」節。\n *\n * ── Viewport Inset ──\n * Modal 與 viewport 四邊保持 layout-space-bottom (48px) 最小間距。\n *\n * ── 高度行為 ──\n * 預設:height 填滿 viewport(扣除 inset),body 捲動。防止動態內容跳動。\n * height=\"auto\":高度隨內容,超過 viewport 時 max-height 安全帽。\n */\n\nconst Dialog = DialogPrimitive.Root\nconst DialogTrigger = DialogPrimitive.Trigger\nconst DialogPortal = DialogPrimitive.Portal\nconst DialogClose = DialogPrimitive.Close\n\n// Modal 與 viewport 四邊的最小間距 = layout-space-bottom (48px)\nconst DIALOG_INSET_VAR = 'var(--layout-space-bottom)'\n\nconst DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n \"fixed inset-0 z-50 bg-overlay\",\n \"data-[state=open]:animate-in data-[state=closed]:animate-out motion-reduce:animate-none\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className,\n )}\n {...props}\n />\n))\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName\n\ninterface DialogContentProps extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {\n /** 最大寬度。預設 512px。傳 number 視為 px。 */\n maxWidth?: string | number\n /**\n * 高度模式。\n * - 不傳(預設):填滿 viewport(height = 100vh - inset*2),body 捲動。防止內容跳動。\n * - true:高度隨內容,超過 viewport 時捲動(max-height 安全帽)。\n */\n autoHeight?: boolean\n}\n\nconst DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n DialogContentProps\n>(({ className, maxWidth = '512px', autoHeight, children, style, ...props }, ref) => {\n const insetCalc = `${DIALOG_INSET_VAR} * 2`\n const viewportH = `calc(100vh - ${insetCalc})`\n const maxWidthCss = typeof maxWidth === 'number' ? `${maxWidth}px` : maxWidth\n\n const heightStyle: React.CSSProperties = autoHeight\n ? { maxHeight: viewportH }\n : { height: viewportH }\n\n // AutoFocus canonical(對齊 Material / Polaris / Atlassian)—\n // 開啟時 focus 落在 body 第一個有意義互動元素(input / button),不是 chrome close X。\n // 預設 Radix 會 focus first tabbable = close X → Button iconOnly 的 focus-triggered\n // tooltip 會立即顯示「關閉」,user-hostile。此 callback 攔截:先找 body 第一個\n // input/textarea/select/button(排除 data-dismiss)focus;找不到就 focus container(不 focus X)。\n const handleOpenAutoFocus = (e: Event) => {\n e.preventDefault()\n const content = e.currentTarget as HTMLElement\n const firstBodyTarget = content.querySelector<HTMLElement>(\n '[data-dialog-body] input:not([disabled]),[data-dialog-body] textarea:not([disabled]),[data-dialog-body] select:not([disabled]),[data-dialog-body] button:not([disabled]):not([data-dismiss])'\n )\n const firstFooterButton = content.querySelector<HTMLElement>(\n '[data-dialog-footer] button:not([disabled]):not([data-dismiss])'\n )\n ;(firstBodyTarget ?? firstFooterButton ?? content).focus({ preventScroll: true })\n }\n\n return (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n // Density canonical(2026-04-22 v5 校準):Dialog 繼承 page density(跟 Sheet 對齊\n // sheet.tsx line 111 canonical),不自設 data-layout-space=\"lg\" 或 data-density。\n //\n // 先前曾設 `data-layout-space=\"lg\"` 給 header/body 寬鬆呼吸,但跟 chrome-header-height\n // canonical 衝突(md page dialog header 期望 48,強設 lg 會變 56)。\n // 世界級對照:Polaris Modal horizontal padding 16 / Material M3 24 / Atlassian 24 — 16 是\n // 合理 lower bound;md page 用 16 loose body padding 可接受,lg page 自動 24。\n // 詳 overlay-surface.spec.md「Chrome dismiss size canonical」\n onOpenAutoFocus={handleOpenAutoFocus}\n className={cn(\n \"fixed left-1/2 top-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2\",\n \"flex flex-col bg-surface-raised rounded-lg border border-border\",\n \"data-[state=open]:animate-in data-[state=closed]:animate-out motion-reduce:animate-none\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%]\",\n \"data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]\",\n className,\n )}\n style={{\n boxShadow: 'var(--elevation-200)',\n maxWidth: `min(${maxWidthCss}, calc(100vw - ${insetCalc}))`,\n ...heightStyle,\n ...style,\n }}\n {...props}\n >\n {children}\n </DialogPrimitive.Content>\n </DialogPortal>\n )\n})\nDialogContent.displayName = DialogPrimitive.Content.displayName\n\n// DialogHeader: SurfaceHeader + Close 按鈕(Dialog 特有)\n// justify-between 讓 children 與 Close 分左右;Close 用 Radix DialogPrimitive.Close 包裝。\n// 2026-05-18 audit gap fix:type 用 SurfaceHeaderProps 對齊 — DialogHeader 是 SurfaceHeader\n// 薄包裝,withTabs / lockDensity 等 props 透過 spread 已 forward,但 TS type 沒 expose\n// 導致 consumer 不能用 `<DialogHeader withTabs>` 而只能寫 `as any` 繞。Type lift 修\n// per header-canonical.spec.md W1 跨 6 consumer 同契約。\nconst DialogHeader = React.forwardRef<\n HTMLDivElement,\n SurfaceHeaderProps\n>(({ className, children, ...props }, ref) => (\n // 2026-05-18:className 不再硬加 justify-between(冗餘:row 1 是 flex items-center gap-2,\n // 第一 child flex-1 grow 自然 push close X 靠右,跟 justify-between 同視覺)。\n // 並且 column mode(tabsSlot 提供)justify-between 會把 row 1 / row 2 上下推開 = 破裂。\n // tabsSlot via `...props` spread 自動 forward(type 來自 SurfaceHeaderProps)。\n <SurfaceHeader\n ref={ref}\n className={className}\n {...props}\n >\n <div className=\"flex-1 min-w-0\">{children}</div>\n {/* Dismiss X(chrome-slot canonical,v5):Button 本身 native sm(28 md / 32 lg,touch target 亦同),\n 但 `dismiss` prop 自動標 `data-unbounded`,SurfaceHeader CSS rule 對其套負 my 讓\n layout 佔位 = 24(`data-dismiss` 僅作 openAutoFocus 排除 marker,與縮位無關),\n header = 24 + 2×tight = 48 / 56 chrome-header-height ✓。\n 詳 overlay-surface.spec.md「Chrome dismiss size canonical」*/}\n <DialogPrimitive.Close asChild>\n <Button data-dismiss iconOnly dismiss size=\"sm\" startIcon={XIcon} aria-label=\"關閉\" />\n </DialogPrimitive.Close>\n </SurfaceHeader>\n))\nDialogHeader.displayName = \"DialogHeader\"\n\n// DialogBody: flex-1 ScrollArea + chrome padding(對齊 overlay-surface SSOT + ScrollArea canonical)\n// 捲軸必用 ScrollArea(跨 OS 一致、不吃寬度)— 不自寫 overflow-y-auto。\n// padding 搬進 viewport inner div:px-loose / pt-tight / pb-bottom(Dialog 「大容器」底部多一拍呼吸)。\n// data-dialog-body:讓 DialogContent onOpenAutoFocus 找得到 body 第一個有意義互動元素(避免 focus 到 close X)\n//\n// ── List-as-region 場景(menu group / Cmd+K)──\n// 不再提供 `flush` variant(2026-05-01 移除,先前曾叫 `variant=\"list\"`)。\n// **canonical pattern** = consumer 自管 list outer wrapper + 用 `className` override 撤掉 chrome padding:\n// ```tsx\n// <DialogBody className=\"!px-0 !pt-0 !pb-0\">\n// <div className=\"py-2\"> {/* list outer wrapper 自帶 py-2(menu group canonical)*/}\n// {items.map(item => <MenuItem className=\"px-[var(--layout-space-loose)] rounded-md\" />)}\n// </div>\n// </DialogBody>\n// ```\n// **rationale**:flush 只為 list-only body 省一行 className,但 (a) 多一個 row(search / banner)\n// 就破功 → 保留 chrome padding 反而更穩,(b) 加新 variant 不解決底層脆弱(consumer 仍要管 list py\n// 且 item px-loose),反而把 1 個 surface decision 拆兩 API。世界級主流(Material/Atlassian/Mantine/\n// shadcn)無 universal LayoutBody flush variant,Polaris flush API 只用於極窄 scope。\n// 詳 `tokens/layoutSpace/layoutSpace.spec.md`「List-as-region in overlay body」節\n// `className` forward 到 **inner content div**(非外層 ScrollArea wrapper)——\n// consumer `<DialogBody className=\"flex flex-col gap-X\">` 期望作用於 children 排列;\n// 套在 ScrollArea 上會 0 效果(children 住 inner div),曾造成 modal form field 完全貼邊。\nconst DialogBody = React.forwardRef<\n HTMLDivElement,\n React.ComponentPropsWithoutRef<typeof ScrollArea>\n>(({ className, children, ...props }, ref) => (\n <ScrollArea ref={ref} data-dialog-body className=\"flex-1 min-h-0\" {...props}>\n <div\n className={cn(\n \"px-[var(--layout-space-loose)] pt-[var(--layout-space-tight)] pb-[var(--layout-space-bottom)]\",\n className,\n )}\n >\n {children}\n </div>\n </ScrollArea>\n))\nDialogBody.displayName = \"DialogBody\"\n\n// DialogFooter: SurfaceFooter wrap 加 data-dialog-footer(autoFocus fallback target)\nconst DialogFooter = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ ...props }, ref) => <SurfaceFooter ref={ref} data-dialog-footer {...props} />)\nDialogFooter.displayName = \"DialogFooter\"\n\nconst DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn(\"text-body-lg font-medium truncate\", className)}\n {...props}\n />\n))\nDialogTitle.displayName = DialogPrimitive.Title.displayName\n\nconst DialogDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n // title → description 間距 canonical:DialogTitle 是 body-lg(16)+ desc body(14)→ reading-lg token\n // (label tier 決定 token 選擇;item-anatomy Family 2 reading-family token 對照表)\n className={cn(\"mt-[var(--item-gap-label-desc-reading-lg)] text-body text-fg-secondary\", className)}\n {...props}\n />\n))\nDialogDescription.displayName = DialogPrimitive.Description.displayName\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const dialogMeta = {\n component: 'Dialog',\n family: null, // non-family composite / overlay / layout\n variants: {\n\n },\n sizes: {\n\n },\n states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-surface-raised'],\n fg: ['text-fg-secondary'],\n ring: [],\n },\n} as const\n\nexport {\n Dialog,\n DialogPortal,\n DialogOverlay,\n DialogClose,\n DialogTrigger,\n DialogContent,\n DialogHeader,\n DialogBody,\n DialogFooter,\n DialogTitle,\n DialogDescription,\n}\n"],"names":["XIcon"],"mappings":"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"}
|
|
@@ -36,7 +36,7 @@ declare const DropdownMenuSub: React.FC<DropdownMenuPrimitive.DropdownMenuSubPro
|
|
|
36
36
|
declare const DropdownMenuRadioGroup: React.ForwardRefExoticComponent<DropdownMenuPrimitive.DropdownMenuRadioGroupProps & React.RefAttributes<HTMLDivElement>>;
|
|
37
37
|
interface DropdownMenuContentProps extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> {
|
|
38
38
|
size?: SizeKey;
|
|
39
|
-
/** 最小寬度(px
|
|
39
|
+
/** 最小寬度(px),預設 `max(180px, 觸發元件寬度)`——窄 trigger 時吃 180px 地板 */
|
|
40
40
|
minWidth?: number;
|
|
41
41
|
/** 最大高度(px),超過時捲動 */
|
|
42
42
|
maxHeight?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown-menu.d.ts","sourceRoot":"","sources":["../../../src/components/DropdownMenu/dropdown-menu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,qBAAqB,MAAM,+BAA+B,CAAA;AACtE,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAA;AAG5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAA;AAI1E,OAAO,EAIL,KAAK,OAAO,EACb,MAAM,uDAAuD,CAAA;AAE9D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,QAAA,MAAM,kBAAkB,QAQb,CAAA;AAGX,KAAK,OAAO,GAAG,OAAO,CAAA;AActB,QAAA,MAAM,YAAY,mDAA6B,CAAA;AAC/C,QAAA,MAAM,mBAAmB,gLASvB,CAAA;AAmBF,QAAA,MAAM,iBAAiB,wKASrB,CAAA;AAGF,QAAA,MAAM,kBAAkB,yDAA+B,CAAA;AACvD,QAAA,MAAM,eAAe,sDAA4B,CAAA;AACjD,QAAA,MAAM,sBAAsB,0HAAmC,CAAA;AAG/D,UAAU,wBACR,SAAQ,KAAK,CAAC,wBAAwB,CAAC,OAAO,qBAAqB,CAAC,OAAO,CAAC;IAC5E,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,
|
|
1
|
+
{"version":3,"file":"dropdown-menu.d.ts","sourceRoot":"","sources":["../../../src/components/DropdownMenu/dropdown-menu.tsx"],"names":[],"mappings":"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"}
|
|
@@ -53,7 +53,6 @@ const DropdownMenuContent = React.forwardRef(({ className, size = "md", sideOffs
|
|
|
53
53
|
collisionPadding,
|
|
54
54
|
align,
|
|
55
55
|
"data-density": "md",
|
|
56
|
-
onCloseAutoFocus: (e) => e.preventDefault(),
|
|
57
56
|
className: cn(floatingLayerClass, !maxHeight && "py-2", className),
|
|
58
57
|
style: {
|
|
59
58
|
boxShadow: "var(--elevation-200)",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown-menu.js","sources":["../../../src/components/DropdownMenu/dropdown-menu.tsx"],"sourcesContent":["import * as React from \"react\"\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\"\nimport { ChevronRight, type LucideIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport type { AvatarData } from \"@/design-system/components/Avatar/avatar\"\nimport { MenuItem } from \"@/design-system/components/Menu/menu-item\"\nimport { ScrollArea } from \"@/design-system/components/ScrollArea/scroll-area\"\nimport { OVERLAY_SIDE_OFFSET, OVERLAY_COLLISION_PADDING } from \"@/design-system/tokens/elevation/overlay-geometry\"\nimport {\n RowSizeProvider,\n useRowSize,\n ICON_SIZE as ROW_ICON_SIZE,\n type RowSize,\n} from \"@/design-system/patterns/element-anatomy/item-anatomy\"\n\n/**\n * DropdownMenu — Radix DropdownMenu + MenuItem visual layer\n *\n * 架構分工:\n * - Radix primitives:behavior(keyboard nav, focus management, aria roles)\n * - MenuItem:visual(layout, padding, icon alignment, typography)\n *\n * Radix primitive 是外層容器,控制 `data-[highlighted]:bg-neutral-hover`。\n * MenuItem 內層只負責佈局,不加互動樣式。\n *\n * ── Hover / highlight canonical(2026-04-22 修正)──\n * 用 Radix 官方的 `data-[highlighted]` attribute,**不用 `:focus-visible` / `:hover` /\n * `:focus`**:\n * - Radix 在 **mouse hover、keyboard arrow nav、focus move in** 時自動 set `data-highlighted`\n * - mouse leave / focus move out / menu close 時自動清掉\n * - 不會在 click 後留殘影(Radix 內部已處理)\n * - 跨瀏覽器一致(不依賴 `:focus-visible` 的 heuristic)\n *\n * 曾經用過 `focus-visible:bg-neutral-hover` 的理由:避免 click 後殘影。但實測:mouse hover\n * 觸發 Radix 程式化 `.focus()`,Chromium / Safari / Firefox 對 programmatic focus 是否 fire\n * `:focus-visible` 行為不一致,導致 mouse hover 有時無 bg。改用 `data-[highlighted]:` 後行為\n * 一致 —— 世界級 canonical(shadcn / Radix docs / Ariakit 皆此)。\n */\n\n// ── Floating layer 共用樣式 ──\nconst floatingLayerClass = [\n 'z-50 overflow-hidden rounded-lg border border-border bg-surface-raised',\n 'data-[state=open]:animate-in data-[state=closed]:animate-out motion-reduce:animate-none',\n 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',\n 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',\n 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',\n 'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',\n 'origin-[var(--radix-dropdown-menu-content-transform-origin)]',\n].join(' ')\n\n// ── Size:統一用 RowSizeContext(item-layout module),消除本地 SizeContext 漂移 ──\ntype SizeKey = RowSize\n// Re-export for backward compat(內部命名)\nconst ICON_SIZE = ROW_ICON_SIZE\n\n// ── Shared item classes on Radix primitive ──\n// Highlight(hover + keyboard nav): 用 Radix `data-[highlighted]` canonical(見 docblock)\nconst radixItemClass = [\n 'relative cursor-pointer select-none outline-none',\n 'transition-colors duration-150',\n 'data-[highlighted]:bg-neutral-hover',\n 'data-[disabled]:pointer-events-none data-[disabled]:text-fg-disabled data-[disabled]:cursor-default',\n].join(' ')\n\n// ── Root ──\nconst DropdownMenu = DropdownMenuPrimitive.Root\nconst DropdownMenuTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Trigger>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Trigger\n ref={ref}\n className={cn('outline-none', className)}\n {...props}\n />\n))\nDropdownMenuTrigger.displayName = DropdownMenuPrimitive.Trigger.displayName\n// DropdownMenuGroup — 對齊 MenuGroup 的 group separation 設計語言\n//\n// 設計語言(跨 Menu-like 元件統一,SSOT 見 item-anatomy.spec.md\n// 「Group auto-separation」):\n// 每個 group 上下各 8px padding,相鄰 group 之間用 border-divider 分隔\n// 兩個 group 之間視覺 gap = 8px(上一個 bottom)+ 8px(下一個 top)= 16px + border\n//\n// MenuGroup(menu-item.tsx)實作:`py-2 [&+&]:border-t [&+&]:border-divider`\n// (在 Command.List 下提供 Content 邊界 8px + group 間 16px gap)\n//\n// DropdownMenuGroup(本元件)實作:`[&+&]:mt-2 [&+&]:pt-2 [&+&]:border-t\n// [&+&]:border-divider`(因為 DropdownMenuContent 已有 py-2 提供 Content 邊界\n// 的 8px,只需在第二個起的 group 加 8+8 = 16px gap + border)\n//\n// **視覺結果等同**:兩種實作的 visual output 一致,只是「padding 住在哪層」\n// 不同。不強制統一 CSS 表達式——DropdownMenuContent 的 py-2 是既有 Radix\n// 期望的行為,移除會影響 trigger 鍵盤導覽的 focus offset。\nconst DropdownMenuGroup = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Group>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Group>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Group\n ref={ref}\n className={cn('[&+&]:border-t [&+&]:border-divider [&+&]:mt-2 [&+&]:pt-2', className)}\n {...props}\n />\n))\nDropdownMenuGroup.displayName = 'DropdownMenuGroup'\n\nconst DropdownMenuPortal = DropdownMenuPrimitive.Portal\nconst DropdownMenuSub = DropdownMenuPrimitive.Sub\nconst DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup\n\n// ── Content ──\ninterface DropdownMenuContentProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> {\n size?: SizeKey\n /** 最小寬度(px),預設跟隨觸發元件寬度 */\n minWidth?: number\n /** 最大高度(px),超過時捲動 */\n maxHeight?: number\n}\n\nconst DropdownMenuContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n DropdownMenuContentProps\n>(({ className, size = 'md', sideOffset = OVERLAY_SIDE_OFFSET, collisionPadding = OVERLAY_COLLISION_PADDING, align = 'start', minWidth, maxHeight, children, ...props }, ref) => (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n collisionPadding={collisionPadding}\n align={align}\n data-density=\"md\"\n onCloseAutoFocus={(e) => e.preventDefault()}\n className={cn(floatingLayerClass, !maxHeight && 'py-2', className)}\n style={{\n boxShadow: 'var(--elevation-200)',\n minWidth: minWidth ?? 'max(180px, var(--radix-dropdown-menu-trigger-width))',\n maxHeight,\n }}\n {...props}\n >\n <RowSizeProvider value={size}>\n {maxHeight ? (\n // 長選單用 ScrollArea 跨 OS 一致捲動(不吃寬度,macOS/Windows 視覺一致)\n // py-2 移到內層,ScrollArea Viewport 才能 scroll 整個 padded 區\n <ScrollArea className=\"max-h-[inherit]\">\n <div className=\"py-2\">{children}</div>\n </ScrollArea>\n ) : (\n children\n )}\n </RowSizeProvider>\n </DropdownMenuPrimitive.Content>\n </DropdownMenuPrimitive.Portal>\n))\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName\n\n// ── SubContent ──\nconst DropdownMenuSubContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, children, ...props }, ref) => {\n const size = useRowSize()\n return (\n <DropdownMenuPrimitive.SubContent\n ref={ref}\n sideOffset={OVERLAY_SIDE_OFFSET}\n className={cn(floatingLayerClass, 'py-2', className)}\n style={{ boxShadow: 'var(--elevation-200)', minWidth: 180 }}\n {...props}\n >\n <RowSizeProvider value={size}>\n {children}\n </RowSizeProvider>\n </DropdownMenuPrimitive.SubContent>\n )\n})\nDropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName\n\n// ── Helper: build endContent from badge + endIcon + shortcut ──\nfunction buildEndContent(\n size: SizeKey,\n badge?: React.ReactNode,\n endIcon?: LucideIcon,\n shortcut?: string,\n): React.ReactNode | undefined {\n const EndIcon = endIcon\n if (!badge && !EndIcon && !shortcut) return undefined\n const iconPx = ICON_SIZE[size]\n return (\n <>\n {badge}\n {EndIcon && <EndIcon size={iconPx} className=\"text-fg-muted\" aria-hidden />}\n {shortcut && <span className=\"text-caption text-fg-muted tracking-shortcut\">{shortcut}</span>}\n </>\n )\n}\n\n// ── Item ──\ninterface DropdownMenuItemProps\n extends Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item>, 'children'> {\n children: React.ReactNode\n /** 左側 icon */\n startIcon?: LucideIcon\n /** 左側頭像資料(AvatarData),與 startIcon 互斥 */\n avatar?: AvatarData\n /** 次要說明文字 */\n description?: React.ReactNode\n /** 後綴 Tag(ReactNode) */\n tag?: React.ReactNode\n /** 後綴 Badge(ReactNode) */\n badge?: React.ReactNode\n /** 後綴指示型 icon(LucideIcon),fg-muted */\n endIcon?: LucideIcon\n /** 鍵盤快捷鍵提示(canonical)——渲染進 MenuItem `endContent` 正規後綴 slot。\n * 替代方案是 `<DropdownMenuShortcut>` child(composition escape-hatch);**同 item 勿混用**。 */\n shortcut?: string\n /** 單選選中(bg-neutral-selected,持續選中狀態)*/\n selected?: boolean\n}\n\nconst DropdownMenuItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n DropdownMenuItemProps\n>(({ className, children, startIcon, avatar, description, tag, badge, endIcon, shortcut, selected, disabled, ...props }, ref) => {\n const size = useRowSize()\n const endContent = buildEndContent(size, badge, endIcon, shortcut)\n\n return (\n <DropdownMenuPrimitive.Item\n ref={ref}\n disabled={disabled}\n className={cn(\n radixItemClass,\n selected && 'bg-neutral-selected',\n className,\n )}\n {...props}\n >\n <MenuItem\n size={size}\n startIcon={startIcon}\n avatar={avatar}\n description={description}\n tag={tag}\n endContent={endContent}\n disabled={disabled}\n // Pure visual — Radix parent handles role/aria/interaction\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.Item>\n )\n})\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName\n\n// ── SubTrigger(子選單觸發器,自動附加 ChevronRight)──\ninterface DropdownMenuSubTriggerProps\n extends Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger>, 'children'> {\n children: React.ReactNode\n /** 左側 icon */\n startIcon?: LucideIcon\n /** 子選單目前狀態值文字(如 \"深色\") */\n value?: string\n /** 子選單狀態 badge */\n badge?: React.ReactNode\n}\n\nconst DropdownMenuSubTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n DropdownMenuSubTriggerProps\n>(({ className, children, startIcon, value, badge, ...props }, ref) => {\n const size = useRowSize()\n const iconPx = ICON_SIZE[size]\n\n // SubTrigger suffix: [value?] [badge?] [ChevronRight] with gap-1\n const endContent = (\n <div className=\"flex items-center gap-1\">\n {value && <span className=\"text-fg-muted\">{value}</span>}\n {badge}\n <ChevronRight size={iconPx} className=\"text-fg-muted\" />\n </div>\n )\n\n return (\n <DropdownMenuPrimitive.SubTrigger\n ref={ref}\n className={cn(\n radixItemClass,\n 'data-[state=open]:bg-neutral-hover',\n className,\n )}\n {...props}\n >\n <MenuItem\n size={size}\n startIcon={startIcon}\n endContent={endContent}\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.SubTrigger>\n )\n})\nDropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName\n\n// ── CheckboxItem ──\ninterface DropdownMenuCheckboxItemProps\n extends Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>, 'children'> {\n children: React.ReactNode\n /** 左側 icon */\n startIcon?: LucideIcon\n /** 次要說明文字 */\n description?: React.ReactNode\n}\n\nconst DropdownMenuCheckboxItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n DropdownMenuCheckboxItemProps\n>(({ className, children, startIcon, description, checked, disabled, ...props }, ref) => {\n const size = useRowSize()\n\n return (\n <DropdownMenuPrimitive.CheckboxItem\n ref={ref}\n checked={checked}\n disabled={disabled}\n onSelect={(e) => e.preventDefault()}\n className={cn(radixItemClass, className)}\n {...props}\n >\n <MenuItem\n size={size}\n checkbox\n checked={!!checked}\n startIcon={startIcon}\n description={description}\n disabled={disabled}\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.CheckboxItem>\n )\n})\nDropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName\n\n// ── Label(群組標題)──\nconst DropdownMenuLabel = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label>\n>(({ className, children, ...props }, ref) => {\n const size = useRowSize()\n return (\n <DropdownMenuPrimitive.Label\n ref={ref}\n className={cn('outline-none', className)}\n {...props}\n >\n <MenuItem\n size={size}\n header\n role=\"presentation\"\n className=\"pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.Label>\n )\n})\nDropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName\n\n// ── Separator ──\nconst DropdownMenuSeparator = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn(\"my-2 h-px bg-divider\", className)}\n {...props}\n />\n))\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName\n\n// ── RadioItem(單選,排序方式等)──\n// Radix handles checked state; visual用 MenuItem 的 selected highlight。\ninterface DropdownMenuRadioItemProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> {\n /** Prefix icon(LucideIcon) */\n startIcon?: LucideIcon\n /** 次要說明文字 */\n description?: React.ReactNode\n}\n\nconst DropdownMenuRadioItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,\n DropdownMenuRadioItemProps\n>(({ className, children, startIcon, description, disabled, ...props }, ref) => {\n const size = useRowSize()\n\n return (\n <DropdownMenuPrimitive.RadioItem\n ref={ref}\n disabled={disabled}\n onSelect={(e) => e.preventDefault()}\n // 2026-05-31 #10:selected 底色套在外層 Radix RadioItem 本身(非 `[&>*]` 子 MenuItem),\n // 因內層 MenuItem 自帶 `!bg-transparent` 會蓋掉子層 bg → 選中底色從不顯示。\n // 改 parent-bg pattern(對齊 DropdownMenuItem selected):RadioItem 上底色,MenuItem 透明讓它透出。\n className={cn(radixItemClass, 'data-[state=checked]:bg-neutral-selected', className)}\n {...props}\n >\n <MenuItem\n size={size}\n startIcon={startIcon}\n description={description}\n disabled={disabled}\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.RadioItem>\n )\n})\nDropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName\n\n// ── Shortcut(鍵盤快捷鍵提示,ml-auto 靠右)──\n// **Canonical 是 `<DropdownMenuItem shortcut=\"⌘C\">`** prop —— 走 MenuItem `endContent` 正規後綴 slot\n// (跟 badge / endIcon 同槽、gap/對齊一致)。本 child 是 **composition escape-hatch**(對齊 shadcn\n// DropdownMenuShortcut idiom),供需手動 compose children 的少數場景;它用 `ml-auto` 塞在 children\n// 內(繞過 endContent slot)。**同一 item 只用一種,勿混用**(見 spec.md 禁止事項)。\n// 視覺統一:text-caption + tracking-shortcut + fg-muted(對齊 prop 後綴 + CommandShortcut)。\nconst DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => (\n <span\n className={cn('ml-auto text-caption text-fg-muted tracking-shortcut', className)}\n {...props}\n />\n)\nDropdownMenuShortcut.displayName = 'DropdownMenuShortcut'\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const dropdownMenuMeta = {\n component: 'DropdownMenu',\n family: null, // non-family composite / overlay / layout\n variants: {\n\n },\n sizes: {\n\n },\n states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-neutral-hover', 'bg-surface-raised', 'bg-transparent'],\n fg: ['text-fg-disabled', 'text-fg-muted'],\n ring: [],\n },\n} as const\n\nexport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuGroup,\n DropdownMenuPortal,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuShortcut,\n floatingLayerClass,\n}\nexport type { SizeKey, DropdownMenuItemProps }\n"],"names":["ROW_ICON_SIZE"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dropdown-menu.js","sources":["../../../src/components/DropdownMenu/dropdown-menu.tsx"],"sourcesContent":["import * as React from \"react\"\nimport * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\"\nimport { ChevronRight, type LucideIcon } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport type { AvatarData } from \"@/design-system/components/Avatar/avatar\"\nimport { MenuItem } from \"@/design-system/components/Menu/menu-item\"\nimport { ScrollArea } from \"@/design-system/components/ScrollArea/scroll-area\"\nimport { OVERLAY_SIDE_OFFSET, OVERLAY_COLLISION_PADDING } from \"@/design-system/tokens/elevation/overlay-geometry\"\nimport {\n RowSizeProvider,\n useRowSize,\n ICON_SIZE as ROW_ICON_SIZE,\n type RowSize,\n} from \"@/design-system/patterns/element-anatomy/item-anatomy\"\n\n/**\n * DropdownMenu — Radix DropdownMenu + MenuItem visual layer\n *\n * 架構分工:\n * - Radix primitives:behavior(keyboard nav, focus management, aria roles)\n * - MenuItem:visual(layout, padding, icon alignment, typography)\n *\n * Radix primitive 是外層容器,控制 `data-[highlighted]:bg-neutral-hover`。\n * MenuItem 內層只負責佈局,不加互動樣式。\n *\n * ── Hover / highlight canonical(2026-04-22 修正)──\n * 用 Radix 官方的 `data-[highlighted]` attribute,**不用 `:focus-visible` / `:hover` /\n * `:focus`**:\n * - Radix 在 **mouse hover、keyboard arrow nav、focus move in** 時自動 set `data-highlighted`\n * - mouse leave / focus move out / menu close 時自動清掉\n * - 不會在 click 後留殘影(Radix 內部已處理)\n * - 跨瀏覽器一致(不依賴 `:focus-visible` 的 heuristic)\n *\n * 曾經用過 `focus-visible:bg-neutral-hover` 的理由:避免 click 後殘影。但實測:mouse hover\n * 觸發 Radix 程式化 `.focus()`,Chromium / Safari / Firefox 對 programmatic focus 是否 fire\n * `:focus-visible` 行為不一致,導致 mouse hover 有時無 bg。改用 `data-[highlighted]:` 後行為\n * 一致 —— 世界級 canonical(shadcn / Radix docs / Ariakit 皆此)。\n */\n\n// ── Floating layer 共用樣式 ──\nconst floatingLayerClass = [\n 'z-50 overflow-hidden rounded-lg border border-border bg-surface-raised',\n 'data-[state=open]:animate-in data-[state=closed]:animate-out motion-reduce:animate-none',\n 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',\n 'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',\n 'data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2',\n 'data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',\n 'origin-[var(--radix-dropdown-menu-content-transform-origin)]',\n].join(' ')\n\n// ── Size:統一用 RowSizeContext(item-layout module),消除本地 SizeContext 漂移 ──\ntype SizeKey = RowSize\n// Re-export for backward compat(內部命名)\nconst ICON_SIZE = ROW_ICON_SIZE\n\n// ── Shared item classes on Radix primitive ──\n// Highlight(hover + keyboard nav): 用 Radix `data-[highlighted]` canonical(見 docblock)\nconst radixItemClass = [\n 'relative cursor-pointer select-none outline-none',\n 'transition-colors duration-150',\n 'data-[highlighted]:bg-neutral-hover',\n 'data-[disabled]:pointer-events-none data-[disabled]:text-fg-disabled data-[disabled]:cursor-default',\n].join(' ')\n\n// ── Root ──\nconst DropdownMenu = DropdownMenuPrimitive.Root\nconst DropdownMenuTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Trigger>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Trigger\n ref={ref}\n className={cn('outline-none', className)}\n {...props}\n />\n))\nDropdownMenuTrigger.displayName = DropdownMenuPrimitive.Trigger.displayName\n// DropdownMenuGroup — 對齊 MenuGroup 的 group separation 設計語言\n//\n// 設計語言(跨 Menu-like 元件統一,SSOT 見 item-anatomy.spec.md\n// 「Group auto-separation」):\n// 每個 group 上下各 8px padding,相鄰 group 之間用 border-divider 分隔\n// 兩個 group 之間視覺 gap = 8px(上一個 bottom)+ 8px(下一個 top)= 16px + border\n//\n// MenuGroup(menu-item.tsx)實作:`py-2 [&+&]:border-t [&+&]:border-divider`\n// (在 Command.List 下提供 Content 邊界 8px + group 間 16px gap)\n//\n// DropdownMenuGroup(本元件)實作:`[&+&]:mt-2 [&+&]:pt-2 [&+&]:border-t\n// [&+&]:border-divider`(因為 DropdownMenuContent 已有 py-2 提供 Content 邊界\n// 的 8px,只需在第二個起的 group 加 8+8 = 16px gap + border)\n//\n// **視覺結果等同**:兩種實作的 visual output 一致,只是「padding 住在哪層」\n// 不同。不強制統一 CSS 表達式——DropdownMenuContent 的 py-2 是既有 Radix\n// 期望的行為,移除會影響 trigger 鍵盤導覽的 focus offset。\nconst DropdownMenuGroup = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Group>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Group>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Group\n ref={ref}\n className={cn('[&+&]:border-t [&+&]:border-divider [&+&]:mt-2 [&+&]:pt-2', className)}\n {...props}\n />\n))\nDropdownMenuGroup.displayName = 'DropdownMenuGroup'\n\nconst DropdownMenuPortal = DropdownMenuPrimitive.Portal\nconst DropdownMenuSub = DropdownMenuPrimitive.Sub\nconst DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup\n\n// ── Content ──\ninterface DropdownMenuContentProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> {\n size?: SizeKey\n /** 最小寬度(px),預設 `max(180px, 觸發元件寬度)`——窄 trigger 時吃 180px 地板 */\n minWidth?: number\n /** 最大高度(px),超過時捲動 */\n maxHeight?: number\n}\n\nconst DropdownMenuContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n DropdownMenuContentProps\n>(({ className, size = 'md', sideOffset = OVERLAY_SIDE_OFFSET, collisionPadding = OVERLAY_COLLISION_PADDING, align = 'start', minWidth, maxHeight, children, ...props }, ref) => (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n collisionPadding={collisionPadding}\n align={align}\n data-density=\"md\"\n // Focus return on close:不 override `onCloseAutoFocus` — 用 Radix 內建 default\n // (close 時 focus 還 trigger;outside-interaction 例外由 Radix `hasInteractedOutsideRef` 自管)。\n // W3C APG menubar「Escape: …return focus to the element…from which the menu was opened」\n // (w3.org/WAI/ARIA/apg/patterns/menubar/);shadcn dropdown-menu 同樣不 override。\n // 2026-06-11 移除 2026-04-08 的 `(e) => e.preventDefault()` hardcode:它跑在 Radix\n // composeEventHandlers 內建 handler 之前,defaultPrevented → trigger focus 被 skip →\n // Esc 關閉後 focus 掉到 body(APG violation)。原動機「mouse close 後 trigger 殘留\n // focus ring」不構成 override 理由:實測(2026-06-12 playwright)pointer item-click\n // close 後 programmatic refocus 在 Chromium 仍可 match `:focus-visible`(UA heuristic),\n // 但與 shadcn 官方 live demo 同流程 DOM 比對 IDENTICAL — Radix 生態一致接受此\n // tradeoff:APG keyboard focus-return 優先於 cosmetic ring。\n className={cn(floatingLayerClass, !maxHeight && 'py-2', className)}\n style={{\n boxShadow: 'var(--elevation-200)',\n minWidth: minWidth ?? 'max(180px, var(--radix-dropdown-menu-trigger-width))',\n maxHeight,\n }}\n {...props}\n >\n <RowSizeProvider value={size}>\n {maxHeight ? (\n // 長選單用 ScrollArea 跨 OS 一致捲動(不吃寬度,macOS/Windows 視覺一致)\n // py-2 移到內層,ScrollArea Viewport 才能 scroll 整個 padded 區\n <ScrollArea className=\"max-h-[inherit]\">\n <div className=\"py-2\">{children}</div>\n </ScrollArea>\n ) : (\n children\n )}\n </RowSizeProvider>\n </DropdownMenuPrimitive.Content>\n </DropdownMenuPrimitive.Portal>\n))\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName\n\n// ── SubContent ──\nconst DropdownMenuSubContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, children, ...props }, ref) => {\n const size = useRowSize()\n return (\n <DropdownMenuPrimitive.SubContent\n ref={ref}\n sideOffset={OVERLAY_SIDE_OFFSET}\n className={cn(floatingLayerClass, 'py-2', className)}\n style={{ boxShadow: 'var(--elevation-200)', minWidth: 180 }}\n {...props}\n >\n <RowSizeProvider value={size}>\n {children}\n </RowSizeProvider>\n </DropdownMenuPrimitive.SubContent>\n )\n})\nDropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName\n\n// ── Helper: build endContent from badge + endIcon + shortcut ──\nfunction buildEndContent(\n size: SizeKey,\n badge?: React.ReactNode,\n endIcon?: LucideIcon,\n shortcut?: string,\n): React.ReactNode | undefined {\n const EndIcon = endIcon\n if (!badge && !EndIcon && !shortcut) return undefined\n const iconPx = ICON_SIZE[size]\n return (\n <>\n {badge}\n {EndIcon && <EndIcon size={iconPx} className=\"text-fg-muted\" aria-hidden />}\n {shortcut && <span className=\"text-caption text-fg-muted tracking-shortcut\">{shortcut}</span>}\n </>\n )\n}\n\n// ── Item ──\ninterface DropdownMenuItemProps\n extends Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item>, 'children'> {\n children: React.ReactNode\n /** 左側 icon */\n startIcon?: LucideIcon\n /** 左側頭像資料(AvatarData),與 startIcon 互斥 */\n avatar?: AvatarData\n /** 次要說明文字 */\n description?: React.ReactNode\n /** 後綴 Tag(ReactNode) */\n tag?: React.ReactNode\n /** 後綴 Badge(ReactNode) */\n badge?: React.ReactNode\n /** 後綴指示型 icon(LucideIcon),fg-muted */\n endIcon?: LucideIcon\n /** 鍵盤快捷鍵提示(canonical)——渲染進 MenuItem `endContent` 正規後綴 slot。\n * 替代方案是 `<DropdownMenuShortcut>` child(composition escape-hatch);**同 item 勿混用**。 */\n shortcut?: string\n /** 單選選中(bg-neutral-selected,持續選中狀態)*/\n selected?: boolean\n}\n\nconst DropdownMenuItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n DropdownMenuItemProps\n>(({ className, children, startIcon, avatar, description, tag, badge, endIcon, shortcut, selected, disabled, ...props }, ref) => {\n const size = useRowSize()\n const endContent = buildEndContent(size, badge, endIcon, shortcut)\n\n return (\n <DropdownMenuPrimitive.Item\n ref={ref}\n disabled={disabled}\n className={cn(\n radixItemClass,\n selected && 'bg-neutral-selected',\n className,\n )}\n {...props}\n >\n <MenuItem\n size={size}\n startIcon={startIcon}\n avatar={avatar}\n description={description}\n tag={tag}\n endContent={endContent}\n disabled={disabled}\n // Pure visual — Radix parent handles role/aria/interaction\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.Item>\n )\n})\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName\n\n// ── SubTrigger(子選單觸發器,自動附加 ChevronRight)──\ninterface DropdownMenuSubTriggerProps\n extends Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger>, 'children'> {\n children: React.ReactNode\n /** 左側 icon */\n startIcon?: LucideIcon\n /** 子選單目前狀態值文字(如 \"深色\") */\n value?: string\n /** 子選單狀態 badge */\n badge?: React.ReactNode\n}\n\nconst DropdownMenuSubTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n DropdownMenuSubTriggerProps\n>(({ className, children, startIcon, value, badge, ...props }, ref) => {\n const size = useRowSize()\n const iconPx = ICON_SIZE[size]\n\n // SubTrigger suffix: [value?] [badge?] [ChevronRight] with gap-1\n const endContent = (\n <div className=\"flex items-center gap-1\">\n {value && <span className=\"text-fg-muted\">{value}</span>}\n {badge}\n <ChevronRight size={iconPx} className=\"text-fg-muted\" />\n </div>\n )\n\n return (\n <DropdownMenuPrimitive.SubTrigger\n ref={ref}\n className={cn(\n radixItemClass,\n 'data-[state=open]:bg-neutral-hover',\n className,\n )}\n {...props}\n >\n <MenuItem\n size={size}\n startIcon={startIcon}\n endContent={endContent}\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.SubTrigger>\n )\n})\nDropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName\n\n// ── CheckboxItem ──\ninterface DropdownMenuCheckboxItemProps\n extends Omit<React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>, 'children'> {\n children: React.ReactNode\n /** 左側 icon */\n startIcon?: LucideIcon\n /** 次要說明文字 */\n description?: React.ReactNode\n}\n\nconst DropdownMenuCheckboxItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n DropdownMenuCheckboxItemProps\n>(({ className, children, startIcon, description, checked, disabled, ...props }, ref) => {\n const size = useRowSize()\n\n return (\n <DropdownMenuPrimitive.CheckboxItem\n ref={ref}\n checked={checked}\n disabled={disabled}\n onSelect={(e) => e.preventDefault()}\n className={cn(radixItemClass, className)}\n {...props}\n >\n <MenuItem\n size={size}\n checkbox\n checked={!!checked}\n startIcon={startIcon}\n description={description}\n disabled={disabled}\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.CheckboxItem>\n )\n})\nDropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName\n\n// ── Label(群組標題)──\nconst DropdownMenuLabel = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label>\n>(({ className, children, ...props }, ref) => {\n const size = useRowSize()\n return (\n <DropdownMenuPrimitive.Label\n ref={ref}\n className={cn('outline-none', className)}\n {...props}\n >\n <MenuItem\n size={size}\n header\n role=\"presentation\"\n className=\"pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.Label>\n )\n})\nDropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName\n\n// ── Separator ──\nconst DropdownMenuSeparator = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn(\"my-2 h-px bg-divider\", className)}\n {...props}\n />\n))\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName\n\n// ── RadioItem(單選,排序方式等)──\n// Radix handles checked state; visual用 MenuItem 的 selected highlight。\ninterface DropdownMenuRadioItemProps\n extends React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem> {\n /** Prefix icon(LucideIcon) */\n startIcon?: LucideIcon\n /** 次要說明文字 */\n description?: React.ReactNode\n}\n\nconst DropdownMenuRadioItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,\n DropdownMenuRadioItemProps\n>(({ className, children, startIcon, description, disabled, ...props }, ref) => {\n const size = useRowSize()\n\n return (\n <DropdownMenuPrimitive.RadioItem\n ref={ref}\n disabled={disabled}\n onSelect={(e) => e.preventDefault()}\n // 2026-05-31 #10:selected 底色套在外層 Radix RadioItem 本身(非 `[&>*]` 子 MenuItem),\n // 因內層 MenuItem 自帶 `!bg-transparent` 會蓋掉子層 bg → 選中底色從不顯示。\n // 改 parent-bg pattern(對齊 DropdownMenuItem selected):RadioItem 上底色,MenuItem 透明讓它透出。\n className={cn(radixItemClass, 'data-[state=checked]:bg-neutral-selected', className)}\n {...props}\n >\n <MenuItem\n size={size}\n startIcon={startIcon}\n description={description}\n disabled={disabled}\n role=\"presentation\"\n className=\"!bg-transparent hover:!bg-transparent pointer-events-none\"\n >\n {children}\n </MenuItem>\n </DropdownMenuPrimitive.RadioItem>\n )\n})\nDropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName\n\n// ── Shortcut(鍵盤快捷鍵提示,ml-auto 靠右)──\n// **Canonical 是 `<DropdownMenuItem shortcut=\"⌘C\">`** prop —— 走 MenuItem `endContent` 正規後綴 slot\n// (跟 badge / endIcon 同槽、gap/對齊一致)。本 child 是 **composition escape-hatch**(對齊 shadcn\n// DropdownMenuShortcut idiom),供需手動 compose children 的少數場景;它用 `ml-auto` 塞在 children\n// 內(繞過 endContent slot)。**同一 item 只用一種,勿混用**(見 spec.md 禁止事項)。\n// 視覺統一:text-caption + tracking-shortcut + fg-muted(對齊 prop 後綴 + CommandShortcut)。\nconst DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => (\n <span\n className={cn('ml-auto text-caption text-fg-muted tracking-shortcut', className)}\n {...props}\n />\n)\nDropdownMenuShortcut.displayName = 'DropdownMenuShortcut'\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const dropdownMenuMeta = {\n component: 'DropdownMenu',\n family: null, // non-family composite / overlay / layout\n variants: {\n\n },\n sizes: {\n\n },\n states: ['default', 'hover', 'active', 'focus-visible', 'disabled'],\n tokens: {\n bg: ['bg-neutral-hover', 'bg-surface-raised', 'bg-transparent'],\n fg: ['text-fg-disabled', 'text-fg-muted'],\n ring: [],\n },\n} as const\n\nexport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuGroup,\n DropdownMenuPortal,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuShortcut,\n floatingLayerClass,\n}\nexport type { SizeKey, DropdownMenuItemProps }\n"],"names":["ROW_ICON_SIZE"],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ import type { LucideIcon } from 'lucide-react';
|
|
|
9
9
|
* 間距固定,不隨 density 變(Empty 是展示性元件,不是工作區域元件):
|
|
10
10
|
* icon → text = mb-4(16px)
|
|
11
11
|
* desc → action = mt-6(24px)
|
|
12
|
-
* title → desc = `var(--item-gap-label-desc)`(token
|
|
12
|
+
* title → desc = `var(--item-gap-label-desc-reading-lg)`(reading-lg tier token,item-anatomy SSOT;bare `--item-gap-label-desc` 已 retire)
|
|
13
13
|
*
|
|
14
14
|
* Outer padding 由 consumer 容器決定(py-12 / py-6 / py-16 等)。
|
|
15
15
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"empty.js","sources":["../../../src/components/Empty/empty.tsx"],"sourcesContent":["import * as React from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { Avatar } from '@/design-system/components/Avatar/avatar'\nimport { useRowSize } from '@/design-system/patterns/element-anatomy/item-anatomy'\n\n/**\n * Empty — 空狀態視覺元件\n *\n * 居中垂直堆疊:icon(Avatar) → title → description → action。\n * 所有 slot 皆可選,預設只需 description。\n *\n * 間距固定,不隨 density 變(Empty 是展示性元件,不是工作區域元件):\n * icon → text = mb-4(16px)\n * desc → action = mt-6(24px)\n * title → desc = `var(--item-gap-label-desc)`(token
|
|
1
|
+
{"version":3,"file":"empty.js","sources":["../../../src/components/Empty/empty.tsx"],"sourcesContent":["import * as React from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@/lib/utils'\nimport { Avatar } from '@/design-system/components/Avatar/avatar'\nimport { useRowSize } from '@/design-system/patterns/element-anatomy/item-anatomy'\n\n/**\n * Empty — 空狀態視覺元件\n *\n * 居中垂直堆疊:icon(Avatar) → title → description → action。\n * 所有 slot 皆可選,預設只需 description。\n *\n * 間距固定,不隨 density 變(Empty 是展示性元件,不是工作區域元件):\n * icon → text = mb-4(16px)\n * desc → action = mt-6(24px)\n * title → desc = `var(--item-gap-label-desc-reading-lg)`(reading-lg tier token,item-anatomy SSOT;bare `--item-gap-label-desc` 已 retire)\n *\n * Outer padding 由 consumer 容器決定(py-12 / py-6 / py-16 等)。\n */\n\nexport interface EmptyProps extends React.HTMLAttributes<HTMLDivElement> {\n /** LucideIcon → 自動包 Avatar 48px neutral;ReactElement → 原樣渲染 */\n icon?: LucideIcon | React.ReactElement\n /** 標題(可選,font-medium,適用於首次引導) */\n title?: string\n /** 說明文字 */\n description?: string\n /** 行動按鈕(可選) */\n action?: React.ReactNode\n /**\n * Disabled context(2026-06-03 加 — FileUpload disabled 等情境消費):title / description 轉\n * `text-fg-disabled`(語意 disabled token,非 opacity)。icon glyph 也 → fg-disabled(icon 是文字一環);icon-circle bg 維持 muted。\n * 預設 false,不影響既有 consumer。\n */\n disabled?: boolean\n}\n\nconst Empty = React.forwardRef<HTMLDivElement, EmptyProps>(\n ({ icon, title, description, action, disabled = false, className, ...props }, ref) => {\n // 字體 tier:讀 RowSizeContext(menu 內自動對齊 menu items 的字體)\n // 沒有 context(standalone)→ fallback 'md' → text-body (14px)\n const rowSize = useRowSize('md')\n const descFont = rowSize === 'lg' ? 'text-body-lg' : 'text-body'\n\n // Icon rendering: ReactElement → as-is;LucideIcon(component,包括 forwardRef 物件)→ 包 Avatar\n // 注意:Lucide v0.577+ icons 是 forwardRef 物件(`typeof === 'object'`),不是 function。\n // 必用 React.isValidElement 判斷 element vs component(typeof 會把 forwardRef 物件誤歸 object)。\n let iconElement: React.ReactNode = null\n if (icon) {\n if (React.isValidElement(icon)) {\n iconElement = icon\n } else {\n const Icon = icon as LucideIcon\n // disabled:glyph → fg-disabled([&_svg]:!… 蓋過 Avatar 內聯 color;circle bg 維持 muted)。icon 是文字一環,隨 disabled 變淡。\n iconElement = <Avatar icon={Icon} size={48} color=\"neutral\" className={disabled ? '[&_svg]:!text-fg-disabled' : undefined} />\n }\n }\n\n return (\n <div\n ref={ref}\n className={cn('flex flex-col items-center text-center', className)}\n {...props}\n >\n {iconElement && (\n <div className=\"mb-4\">{iconElement}</div>\n )}\n {title && (\n <span className={cn('text-body-lg font-medium', disabled ? 'text-fg-disabled' : 'text-foreground')}>\n {title}\n </span>\n )}\n {description && (\n <span\n className={cn(\n // 字體跟 RowSizeContext 對齊:sm/md = text-body (14px),lg = text-body-lg (16px)\n // 在 menu 內自動對齊 menu items;standalone 時 fallback text-body\n descFont,\n disabled ? 'text-fg-disabled' : (title || action) ? 'text-fg-secondary' : 'text-fg-muted',\n // Empty title 永遠 body-lg(16)→ 用 reading-lg token(label tier 決定)\n title && 'mt-[var(--item-gap-label-desc-reading-lg)]',\n )}\n >\n {description}\n </span>\n )}\n {action && (\n <div className=\"mt-6\">{action}</div>\n )}\n </div>\n )\n },\n)\nEmpty.displayName = 'Empty'\n\n// Story auto-compile metadata — Phase 1 mechanical migration(2026-04-24)\n// Phase 2 fill needed: purpose descriptions + when rationale + world-class refs\nexport const emptyMeta = {\n component: 'Empty',\n family: null, // non-family composite / overlay / layout\n variants: {\n\n },\n sizes: {\n\n },\n states: ['default', 'disabled'],\n tokens: {\n bg: [],\n fg: ['text-fg-muted', 'text-fg-secondary', 'text-foreground', 'text-fg-disabled'],\n ring: [],\n },\n} as const\n\nexport { Empty }\n"],"names":[],"mappings":";;;;;AAqCA,MAAM,QAAQ,MAAM;AAAA,EAClB,CAAC,EAAE,MAAM,OAAO,aAAa,QAAQ,WAAW,OAAO,WAAW,GAAG,MAAA,GAAS,QAAQ;AAGpF,UAAM,UAAU,WAAW,IAAI;AAC/B,UAAM,WAAW,YAAY,OAAO,iBAAiB;AAKrD,QAAI,cAA+B;AACnC,QAAI,MAAM;AACR,UAAI,MAAM,eAAe,IAAI,GAAG;AAC9B,sBAAc;AAAA,MAChB,OAAO;AACL,cAAM,OAAO;AAEb,sBAAc,oBAAC,QAAA,EAAO,MAAM,MAAM,MAAM,IAAI,OAAM,WAAU,WAAW,WAAW,8BAA8B,OAAA,CAAW;AAAA,MAC7H;AAAA,IACF;AAEA,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAW,GAAG,0CAA0C,SAAS;AAAA,QAChE,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,eACC,oBAAC,OAAA,EAAI,WAAU,QAAQ,UAAA,aAAY;AAAA,UAEpC,SACC,oBAAC,QAAA,EAAK,WAAW,GAAG,4BAA4B,WAAW,qBAAqB,iBAAiB,GAC9F,UAAA,MAAA,CACH;AAAA,UAED,eACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAW;AAAA;AAAA;AAAA,gBAGT;AAAA,gBACA,WAAW,qBAAsB,SAAS,SAAU,sBAAsB;AAAA;AAAA,gBAE1E,SAAS;AAAA,cAAA;AAAA,cAGV,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,UAGJ,UACC,oBAAC,OAAA,EAAI,WAAU,QAAQ,UAAA,OAAA,CAAO;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAItC;AACF;AACA,MAAM,cAAc;AAIb,MAAM,YAAY;AAAA,EACvB,WAAW;AAAA,EACX,QAAQ;AAAA;AAAA,EACR,UAAU,CAAA;AAAA,EAGV,OAAO,CAAA;AAAA,EAGP,QAAQ,CAAC,WAAW,UAAU;AAAA,EAC9B,QAAQ;AAAA,IACN,IAAI,CAAA;AAAA,IACJ,IAAI,CAAC,iBAAiB,qBAAqB,mBAAmB,kBAAkB;AAAA,IAChF,MAAM,CAAA;AAAA,EAAC;AAEX;"}
|
|
@@ -88,7 +88,7 @@ const fieldWrapperStyles = cva(
|
|
|
88
88
|
{
|
|
89
89
|
mode: "readonly",
|
|
90
90
|
variant: "default",
|
|
91
|
-
className: "bg-
|
|
91
|
+
className: "bg-readonly border border-transparent"
|
|
92
92
|
},
|
|
93
93
|
{
|
|
94
94
|
// 2026-05-13 R3.5(per codex Q3 verdict + user 拍「想盡辦法 auto-handle prereq」):
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-wrapper.js","sources":["../../../src/components/Field/field-wrapper.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 { cva } from 'class-variance-authority'\n\n// ── Field Wrapper Styles ────────────────────────────────────────────────────\n// 所有 Field 元件共用的 input wrapper 樣式。\n//\n// 4 種模式(2026-05-05 expand):\n// edit — bg-surface, border, hover/focus 回饋(可編輯 input)\n// display — 純展示(無 input chrome、無 affordance);語意「read-only 內容,展示給人看」。\n// 對齊 Carbon read-only / PatternFly inline-edit hidden-input。\n// readonly — bg-disabled(neutral-2), 無邊框, 文字正常色(input chrome 但鎖定)\n// disabled — bg-disabled(neutral-2), 無邊框, 文字灰化\n//\n// 2 種視覺外殼(variant):\n// default — 完整 chrome(form input 場景)\n// bare — 透明 chrome(cell-as-input substrate / Toolbar inline editing)\n//\n// 高度:固定 h = field-height token(rem),與 Button 共用同一組 token。\n\nexport const fieldWrapperStyles = cva(\n [\n // K10 fix(2026-05-04):`group/field` 讓 inner placeholder/text 可透過 `group-data-[field-mode=...]/field:` 變體\n // 各 Field 元件 wrapper 同時加 `data-field-mode={resolvedMode}` 屬性,bareInputStyles 即可\n // 依 mode 切 placeholder color。User canonical:disabled 顯著性優於 muted。\n 'group/field',\n // 2026-05-15 H1 root cause fix(user #1 verbatim 拍板「照你跟codex有共識的最佳建議做」+ codex round 1 verify cite 5/5):\n // 加 `min-w-0` 於 base — Field wrapper 為 cell-as-input substrate(DataTable / Form 上下文),\n // parent grid/flex cell 限寬時 wrapper 子 flex children 需 min-w-0 才能縮 + truncate。\n // 之前 SSOT 缺 → `selectedItemRenderer` / value text / Multi tag area 在 narrow cell 無法\n // truncate-with-ellipsis(`Alexander Hamilton Zhang` 直接被 cell overflow-hidden 硬裁無 `...`\n // 甚至蓋住相鄰 cell `—` indicator,圖二 user round 2 直接抓 trigger 越界證據)。\n // 修一處全 Field family 跟動(Input/Select/Combobox/DatePicker/TimePicker/LinkInput/\n // Textarea/NumberInput/PeoplePicker)— 對齊 M17/M19/M23 一處 SSOT + data-table.spec.md:233\n // 「禁硬裁無 ellipsis」DS canonical + MUI X DataGrid / Ant Table column.ellipsis 共識。\n 'inline-flex items-center w-full min-w-0 rounded-md',\n 'text-foreground font-normal',\n 'transition-colors duration-150',\n ],\n {\n variants: {\n mode: {\n edit: '',\n display: '',\n readonly: '',\n disabled: '',\n },\n variant: {\n // default — 完整 Field wrapper chrome(bg-surface、明顯 border、hover/focus 回饋)\n default: '',\n // bare — 透明 variant,hover / focus 才出現 border。適用 Toolbar inline editing\n // (FileViewer zoom input / chart config / rich text toolbar number input 等)。\n // 世界級對照:VS Code settings / Figma toolbar number / Notion prop input。\n bare: '',\n // naked — 完全無 variant,hover/focus 也不出 border。適用 cell-as-input\n // (host cell 自管 border + focus visual,內部 input 純文字承載)。\n // 世界級對照:Airtable / Notion / Excel / Google Sheets cell editing。\n naked: '',\n },\n size: {\n sm: 'text-body h-field-sm px-3 gap-2',\n md: 'text-body h-field-md px-3 gap-2',\n lg: 'text-body-lg h-field-lg px-3 gap-2',\n },\n },\n // mode x variant 交叉:visual chrome 由 compoundVariants 決定\n //\n // Overlay trigger active state(canonical 2026-05-02):當 Field 是 Popover/DropdownMenu/\n // Combobox trigger 用 asChild,Radix 自動 set `data-state=\"open\"` on trigger root → trigger\n // 視覺維持 hover 樣式直到浮層關閉(對齊 inline-action.spec.md「狀態極簡派」)。\n compoundVariants: [\n // default variant chrome by mode\n {\n mode: 'edit',\n variant: 'default',\n className: [\n 'bg-surface border border-border',\n 'hover:border-border-hover',\n // 2026-05-06 v13.3 SSOT canonical:focus-within `!important` 強制勝過 data-state attribute\n // selector(specificity tie at 0,2,0;source order 後者勝)。\n //\n // 設計原則:**focus dominates everything**(M11 fix「focus-dominates-hover」延伸成\n // 「focus-dominates-{hover,open,error-rest}」)。Cursor 在輸入框 = user 編輯中 = 必藍。\n //\n // 對齊世界級三家共識:\n // - Material Design 3:focus → primary line color\n // - Polaris(Shopify):focus state border-focus(藍)overrides hover/open\n // - Ant Design 5:`.ant-select-focused` blue,popover open + select option close 後\n // trigger 仍 focused → blue stays(focus return canonical via Radix `onCloseAutoFocus`)\n //\n // 副作用 — Ant 風「選後藍 / 取消灰」自動達成:\n // - 選 option close popover → Radix focus return to trigger → focus-within fires → 藍\n // - 點外取消 close popover → focus 移外 → focus-within 不 fire → 灰\n 'focus-within:!border-primary focus-within:hover:!border-primary',\n 'data-[state=open]:border-border-hover',\n ],\n },\n {\n mode: 'display',\n variant: 'default',\n // 2026-05-13 Q3 Path Ⅰ(user 拍板 Path Ⅰ 全 zero chrome + codex V2 verdict + field-controls.spec.md (d)):\n // default display = zero chrome — !px-0 !py-0 override size token 的 px-3,跟 Select / Combobox\n // / DatePicker / TimePicker / LinkInput non-D-path bare-span idiom 一致(Carbon read-only / Stripe\n // display / Notion property / Polaris readonly TextField 全 zero chrome)。\n className: 'bg-transparent border border-transparent !px-0 !py-0',\n },\n {\n mode: 'readonly',\n variant: 'default',\n className: 'bg-disabled border border-transparent',\n },\n {\n // 2026-05-13 R3.5(per codex Q3 verdict + user 拍「想盡辦法 auto-handle prereq」):\n // 移除 `opacity-disabled` blanket — Avatar 已 fieldCtx-aware self-dim(avatar.tsx self-managed\n // via `isDisabledInField` derivation)。Field wrapper 不再 host-control Avatar opacity。\n // Inner content(text-fg-disabled / Avatar self-opacity)走具體 disabled token per color.spec.md:729。\n mode: 'disabled',\n variant: 'default',\n className: 'bg-disabled border border-transparent cursor-not-allowed',\n },\n // bare variant chrome by mode\n {\n mode: 'edit',\n variant: 'bare',\n className: [\n 'bg-transparent border border-transparent',\n 'hover:border-border',\n // 同 default chrome v13.3 SSOT:focus-within !important 強制勝過 data-state\n 'focus-within:!border-primary focus-within:hover:!border-primary',\n 'data-[state=open]:border-border',\n ],\n },\n {\n mode: 'display',\n variant: 'bare',\n // bare + display:cell-as-input default state(無 variant,完全融入 cell;hover/focus 才有 affordance 等 user 點下去切 edit mode)\n className: 'bg-transparent border border-transparent',\n },\n {\n mode: 'readonly',\n variant: 'bare',\n className: 'bg-transparent border border-transparent',\n },\n {\n // 2026-05-13 R3.5:移除 `opacity-disabled` blanket(per Avatar self-dim canonical)\n mode: 'disabled',\n variant: 'bare',\n className: 'bg-transparent border border-transparent cursor-not-allowed',\n },\n // naked variant — cell-as-input substrate(Notion / Airtable / Excel canonical)\n //\n // ── 2026-05-06 v14:revert v12 → v9 baseline + keep v13.3 ──\n // v12 `!absolute -inset-px` autoRowHeight 不相容(Field 抽 layout flow → cell 塌 42px;\n // user production 報「Field 沒撐滿 cell, 比沒改之前還糟糕一百萬倍」)→ revert。\n //\n // v14 = v9 baseline border-based state machine + v13.3 focus !important。\n // 暫接受視覺:Field.border-l 跟 prev cell.border-r 視覺 2px 雙線(待另案研究 seamless\n // 方案,約束:SSOT 留 Field state machine + ring 顏色自動跟 border state 同步)。\n // Phase 9 Issue 5 fix(2026-05-10 user 撞 + codex 重比稿 verdict ADOPT):\n // Field naked variant 之前全寫 `!gap-0` strip Field family slot gap → indicator(chevron /\n // calendar / clock)緊連 value 沒間距。違反 item-anatomy slot SSOT。\n // Codex verdict:「naked 把 chrome stripping 跟 slot anatomy stripping 混在一起。\n // chrome stripping 合理(去 padding / border / rounded);slot anatomy stripping 不合理\n // (prefix / content / suffix gap 仍是 item anatomy slot canonical `gap-2`)」\n // Fix:移除 `!gap-0`,讓 Field family base `gap-2`(field-wrapper.tsx:50)透出來。\n // Cite:item-anatomy.spec.md L46-50 / L113-122 partial consumer canonical;\n // field-controls.spec.md L22 Field Controls 視覺對齊 Family 1 Menu item layout。\n // 特殊 stack / multi-pill 場景若需 zero-gap,該 component spec 明文例外,不全域 strip。\n {\n mode: 'edit',\n variant: 'naked',\n className: [\n 'bg-transparent !rounded-none !h-full',\n '!px-[var(--table-cell-px)] !py-[var(--table-cell-py)]',\n 'border border-border',\n 'hover:border-border-hover',\n // v13.3 SSOT canonical:focus-within !important(同 default + bare)\n 'focus-within:!border-primary focus-within:hover:!border-primary',\n 'data-[state=open]:border-border-hover',\n 'group-data-[row-mode=auto]/cell:!items-start',\n ],\n },\n {\n // 2026-05-12 fix v2(M32 root invariant audit):\n // Q1 root invariant?:cell-as-input display 視覺位置 = `cell.items-{X}` × `Field.height`\n // 兩變數函數;canonical = autoRow → Field intrinsic + cell.items-start\n // (text 在 cell top + cellPadding-y),fixed → Field h-field-md +\n // cell.items-center(text 在 cell vertical center)。\n // Q2 symptom?:`h-field-md` 32px 在 autoRow tall cell + cell items-start → Field 32px\n // sitting at top,inside items-center default → text center = top+15 ≠ top。\n // 即使 `group-data-[row-mode=auto]/cell:!items-start` 加進來,Field height\n // 還是 32 → text 在 Field 內 top,但 Field 自己 height ≠ 0,offset 13~32px。\n // Q3 fix layer?:root-layer fix = Field 在 autoRow context 必 `h-auto` 才能讓 text 真正\n // flush 到 cell.top + padding。前 v1 只 remove `!h-full` 是 surface-fix\n // (沒解決 h-field-md 32px persistence)。v2 真根因 fix:加 `!h-auto`\n // override h-field-md → Field intrinsic line-height → cell items-start\n // 真實 anchor text at cell.top + padding。\n // Edit mode 不動(`!h-full` 保留 — border 必滿格對齊 cell border)。\n mode: 'display',\n variant: 'naked',\n className: [\n 'bg-transparent !rounded-none !px-0 !py-0 !h-auto',\n 'border border-transparent',\n ],\n },\n {\n mode: 'readonly',\n variant: 'naked',\n className: [\n 'bg-transparent !rounded-none !px-0 !py-0 !h-auto',\n 'border border-transparent',\n ],\n },\n {\n // 2026-05-13 Q3 fix(per codex Q3 verdict + color.spec.md:729 逃生艙 rule):\n // 移除 `opacity-disabled` blanket — naked wrapper 只負責 substrate(透明 border + 抑制 cursor),\n // 內部 content(text / icon / avatar)各自 disabled context 處理(text-fg-disabled / Avatar opacity 等具體 token)。\n // 對齊 DataTable cell-disabled TD 加 `bg-disabled` 表達 cell-level disabled state 的 SSOT 分權。\n // **default/bare disabled variant(line 107, 135)deferred R3.5** — 仍依賴 wrapper opacity-disabled\n // for Avatar dim(Avatar 尚未 fieldCtx-aware self-dim);Avatar self-dim 實作後再連帶移除。\n mode: 'disabled',\n variant: 'naked',\n className: [\n 'bg-transparent !rounded-none cursor-not-allowed !px-0 !py-0 !h-auto',\n 'border border-transparent',\n ],\n },\n ],\n defaultVariants: {\n mode: 'edit',\n variant: 'default',\n size: 'md',\n },\n }\n)\n\n// ── Bare Input Styles ───────────────────────────────────────────────────────\n\nexport const bareInputStyles = [\n // 2026-05-15 Q1 真 root cause fix(per codex Round 4 cite-based verdict):\n // 加 `truncate` — 原 bareInputStyles 含 `flex-1 min-w-0` 但無 `truncate / text-overflow` policy。\n // 當 Select `searchable && open` 切 raw `<input placeholder=...>` branch(select.tsx:178),\n // bareInputStyles 套上,placeholder 無 ellipsis → narrow cell(<160px)硬裁無 `...`(user round 3\n // 圖一證據)。加 truncate 後 `<input>` `text-overflow: ellipsis` 啟動,符合 user 「placeholder 直接被截掉沒有變...」\n // 該顯 ellipsis 的 SSOT。對齊 data-table.spec.md:233「禁硬裁無 ellipsis」+ field-controls.spec.md:286\n // 共享 contract(a)「display/readonly/disabled/edit 4 mode 共享同一 renderer」semantic 對齊。\n 'flex-1 min-w-0 truncate bg-transparent',\n 'outline-none border-none p-0',\n 'text-[inherit] font-[inherit] leading-[inherit]',\n // A3 fix(2026-05-05):`<input>` UA stylesheet 強制 `text-align: start`,阻斷 parent 的\n // `text-right`/`text-center` 繼承。顯式 `text-align: inherit` 復原(對齊 NumberCell /\n // CurrencyCell right-align canonical:column meta.align='right' → cell text-right →\n // input 跟著 right-align)。\n '[text-align:inherit]',\n 'placeholder:text-fg-muted',\n // K10 fix(2026-05-04):wrapper data-field-mode=disabled 時,placeholder/text 都切 fg-disabled\n // 依賴 fieldWrapperStyles 的 `group/field` + 各 Field 元件 wrapper 加 `data-field-mode={resolvedMode}`\n // User canonical:disabled state 顯著性優於 muted(neutral-6 > neutral-7)\n 'group-data-[field-mode=disabled]/field:placeholder:text-fg-disabled',\n 'group-data-[field-mode=disabled]/field:text-fg-disabled',\n].join(' ')\n\n// ── Naked Variant Cell Row-Mode Alignment Propagation ──────────────────────\n// SSOT canonical(M19 / 2026-05-05):cell-as-input naked variant 元件**所有內部\n// wrapper**(`<span>` 包 Avatar+name 等)必 import + apply 此 SSOT,host cell\n// `data-row-mode` 屬性自動 propagate alignment(autoRow → items-start / fixed → items-center)。\n//\n// 不 propagate 的後果:autoRow 場景下 People / Select / Combobox 內部用\n// `inline-flex items-center` hardcode → 視覺垂直置中於 wrapper 自身高度,**沒**頂對齊\n// → 跟其他純文字 cell baseline 視覺漂移。\n//\n// 世界級對照:\n// - HTML <td> default `vertical-align: baseline`(瀏覽器自動 first-baseline align)\n// - AG Grid `cellStyle` + `cellRendererSelector`,row context 共享(closed source 部分)\n// - Material X-Grid `gridClasses.cell` wrapper 不允許 cell content override alignment\n// - Notion / Airtable cell content 從 host 繼承,不 hardcode self alignment\n//\n// Hook:`check_naked_row_mode_propagation.sh`(write-time BLOCKER)\n// Audit:design-system-audit Group N M27(periodic batch verify)\nexport const nakedCellRowModeAlign = 'group-data-[row-mode=auto]/cell:items-start'\n\n// ── Cell-as-input Display Hover Ring(2026-05-05 v9 — sole remaining ring const)─\n// editable cell **display mode hover 提示**(「這 cell 可編,點 → 進 edit」affordance 信號)。\n// 對齊 Notion / Airtable hover-cell-shows-border canonical。\n//\n// **為何只剩這一個**:Field naked **edit/focus/open state ring 已下沉到 Field default state\n// machine**(border-based,2026-05-05 v9 architectural rewrite),不需 outline 平行系統;\n// 但 display mode 沒 Field state(display = 純展示無互動),hover 提示需 cell wrapper 自加。\n//\n// **2026-05-09 v15.17 revert v15.16(user 未同意 ship)— 維持 v15.13 outline+offset:-1**\n//\n// User 2026-05-09 後續 message:「設計決策的東西你應該要先問過我讓我決策吧?為什麼就直接開跑」\n// 我 commit 698ff58 ship v15.16(box-shadow inset Spec 2 不蓋 grid)= 設計決策結論未 user 同意\n// 直接 ship = workflow 違反。Revert 回 v15.13 outline+offset:-1(原 user-accepted 路徑),\n// 等 user 拍板 4-邊覆蓋路徑才 ship。\n//\n// User 並指出我視覺分析又錯:「我就是只有看到只有右邊被蓋掉,上面下面左邊都會露出 cell 的邊框」\n// 我之前錯說「right + bottom 都覆蓋」,bottom 真實 row border-b 在 cell.outer 外 1px,outline 蓋不到。\n//\n// 真實 4-邊狀態(re-verified):\n// - right: outline 199-200 = cell own border-r 199-200 → 蓋 1 條線 ✓\n// - top: outline 0-1,前一 row border-b 在 cell.outer 外 → 露 2 條線\n// - bottom: outline 38-39 在 cell 內,row border-b 在 row.outer 內 = cell.outer 外 1px → 露 2 條線\n// - left: outline 0-1,前一 cell border-r 在 cell.outer 外 → 露 2 條線\n//\n// User 想要 4 邊都覆蓋 = 等 codex unframed brief + user 拍板,本次不 ship。\n//\n// 之前 v15.13 / 14 / 16 / 17 緣由 → tsc comment 之前版本 + planning RFC\n// (跑錯方向 4 次 = 沒 1 次 verify 全面向 + 沒 user 拍板 set design)。\n//\n// Color `--border-hover` 對齊 Field default hover state token。\n//\n// **2026-05-10 Slice D Step 2 — Cell host CSS variable suppression**:\n// outline color 改用 `var(--cell-hover-outline-color, var(--border-hover))`,\n// allow DataTable cell host(spreadsheet overlay enabled 時)set `--cell-hover-outline-color: transparent`\n// 抑制 outline,讓 overlay layer 接管 hover ring paint(per RFC Contract 8 「one geometry owner, two paint owners」)。\n// Backward-compat:flag 關時 default `--border-hover`,既有行為不變。\nexport const nakedCellEditableDisplayHover = 'hover:outline hover:outline-1 hover:outline-offset-[-1px] hover:outline-[var(--cell-hover-outline-color,var(--border-hover))]'\n\n// ── Cell-as-input Edge Slot SSOT(2026-05-05 v8 — retire 平行 SSOT,改 L1 消費)───\n//\n// 前身 `nakedCellPrefixSlot` / `nakedCellSuffixSlot` 是 M1+M17 違反:平行 SSOT 跟\n// `patterns/element-anatomy` 的 `<ItemPrefix>` / `<ItemSuffix>` primitive 撞 home。\n// 已 retire — Field naked variant 內 prefix / suffix slot 直接消費 L1 primitive:\n//\n// import { ItemPrefix, ItemSuffix } from '@/design-system/patterns/element-anatomy/item-anatomy'\n// <ItemPrefix><StartIcon /></ItemPrefix> // 對 Input.startIcon / Select.startIcon\n// <ItemSuffix>{chevron}</ItemSuffix> // 對 Combobox / DatePicker / PeoplePicker chevron\n//\n// **`h-[1lh]` 普世正確**(item-anatomy.spec.md:190-191 verbatim):\n// - 單行 wrapper items-center → slot 1lh 在 cell 高度中心 = 第一行中線(視覺 = items-center)\n// - 多行 wrapper items-start → slot 1lh 鎖頂 = 第一行中線\n// 不需 conditional `group-data-[row-mode=auto]/cell:` — 我前 v4 自加的 conditional 是過度設計。\n//\n// State ring 3 const 仍留(下方)— 是 Field naked 專屬,MenuItem / TreeView 用 bg hover 不用 outline。\n// `nakedCellRowModeAlign`(wrapper 級)仍留 — 是 cell-context row-mode → wrapper alignment 適配,正交 slot 級。\n\n// ── Empty Value Display ─────────────────────────────────────────────────────\n\nexport const EMPTY_DISPLAY = '—'\n\n/**\n * 2026-05-14 I2 fix(per field-controls.spec.md contract (e) display typography canonical):\n * Field family display path bare-span helper — `sm/md → text-body` / `lg → text-body-lg`,\n * 跨 9 元件 display 視覺尺寸統一(user 抓 LinkInput 字體跟其他 Field 不一致 = SSOT 違反)。\n * Consumer:LinkInput / Select / Combobox / DatePicker / TimePicker non-D-path bare-span 套此 class。\n */\nexport const fieldDisplayTextClass = (size: 'sm' | 'md' | 'lg'): string =>\n size === 'lg' ? 'text-body-lg' : 'text-body'\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"field-wrapper.js","sources":["../../../src/components/Field/field-wrapper.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 { cva } from 'class-variance-authority'\n\n// ── Field Wrapper Styles ────────────────────────────────────────────────────\n// 所有 Field 元件共用的 input wrapper 樣式。\n//\n// 4 種模式(2026-05-05 expand):\n// edit — bg-surface, border, hover/focus 回饋(可編輯 input)\n// display — 純展示(無 input chrome、無 affordance);語意「read-only 內容,展示給人看」。\n// 對齊 Carbon read-only / PatternFly inline-edit hidden-input。\n// readonly — bg-readonly(neutral-2), 無邊框, 文字正常色(input chrome 但鎖定;token 獨立於 disabled)\n// disabled — bg-disabled(neutral-2), 無邊框, 文字灰化\n//\n// 2 種視覺外殼(variant):\n// default — 完整 chrome(form input 場景)\n// bare — 透明 chrome(cell-as-input substrate / Toolbar inline editing)\n//\n// 高度:固定 h = field-height token(rem),與 Button 共用同一組 token。\n\nexport const fieldWrapperStyles = cva(\n [\n // K10 fix(2026-05-04):`group/field` 讓 inner placeholder/text 可透過 `group-data-[field-mode=...]/field:` 變體\n // 各 Field 元件 wrapper 同時加 `data-field-mode={resolvedMode}` 屬性,bareInputStyles 即可\n // 依 mode 切 placeholder color。User canonical:disabled 顯著性優於 muted。\n 'group/field',\n // 2026-05-15 H1 root cause fix(user #1 verbatim 拍板「照你跟codex有共識的最佳建議做」+ codex round 1 verify cite 5/5):\n // 加 `min-w-0` 於 base — Field wrapper 為 cell-as-input substrate(DataTable / Form 上下文),\n // parent grid/flex cell 限寬時 wrapper 子 flex children 需 min-w-0 才能縮 + truncate。\n // 之前 SSOT 缺 → `selectedItemRenderer` / value text / Multi tag area 在 narrow cell 無法\n // truncate-with-ellipsis(`Alexander Hamilton Zhang` 直接被 cell overflow-hidden 硬裁無 `...`\n // 甚至蓋住相鄰 cell `—` indicator,圖二 user round 2 直接抓 trigger 越界證據)。\n // 修一處全 Field family 跟動(Input/Select/Combobox/DatePicker/TimePicker/LinkInput/\n // Textarea/NumberInput/PeoplePicker)— 對齊 M17/M19/M23 一處 SSOT + data-table.spec.md:233\n // 「禁硬裁無 ellipsis」DS canonical + MUI X DataGrid / Ant Table column.ellipsis 共識。\n 'inline-flex items-center w-full min-w-0 rounded-md',\n 'text-foreground font-normal',\n 'transition-colors duration-150',\n ],\n {\n variants: {\n mode: {\n edit: '',\n display: '',\n readonly: '',\n disabled: '',\n },\n variant: {\n // default — 完整 Field wrapper chrome(bg-surface、明顯 border、hover/focus 回饋)\n default: '',\n // bare — 透明 variant,hover / focus 才出現 border。適用 Toolbar inline editing\n // (FileViewer zoom input / chart config / rich text toolbar number input 等)。\n // 世界級對照:VS Code settings / Figma toolbar number / Notion prop input。\n bare: '',\n // naked — 完全無 variant,hover/focus 也不出 border。適用 cell-as-input\n // (host cell 自管 border + focus visual,內部 input 純文字承載)。\n // 世界級對照:Airtable / Notion / Excel / Google Sheets cell editing。\n naked: '',\n },\n size: {\n sm: 'text-body h-field-sm px-3 gap-2',\n md: 'text-body h-field-md px-3 gap-2',\n lg: 'text-body-lg h-field-lg px-3 gap-2',\n },\n },\n // mode x variant 交叉:visual chrome 由 compoundVariants 決定\n //\n // Overlay trigger active state(canonical 2026-05-02):當 Field 是 Popover/DropdownMenu/\n // Combobox trigger 用 asChild,Radix 自動 set `data-state=\"open\"` on trigger root → trigger\n // 視覺維持 hover 樣式直到浮層關閉(對齊 inline-action.spec.md「狀態極簡派」)。\n compoundVariants: [\n // default variant chrome by mode\n {\n mode: 'edit',\n variant: 'default',\n className: [\n 'bg-surface border border-border',\n 'hover:border-border-hover',\n // 2026-05-06 v13.3 SSOT canonical:focus-within `!important` 強制勝過 data-state attribute\n // selector(specificity tie at 0,2,0;source order 後者勝)。\n //\n // 設計原則:**focus dominates everything**(M11 fix「focus-dominates-hover」延伸成\n // 「focus-dominates-{hover,open,error-rest}」)。Cursor 在輸入框 = user 編輯中 = 必藍。\n //\n // 對齊世界級三家共識:\n // - Material Design 3:focus → primary line color\n // - Polaris(Shopify):focus state border-focus(藍)overrides hover/open\n // - Ant Design 5:`.ant-select-focused` blue,popover open + select option close 後\n // trigger 仍 focused → blue stays(focus return canonical via Radix `onCloseAutoFocus`)\n //\n // 副作用 — Ant 風「選後藍 / 取消灰」自動達成:\n // - 選 option close popover → Radix focus return to trigger → focus-within fires → 藍\n // - 點外取消 close popover → focus 移外 → focus-within 不 fire → 灰\n 'focus-within:!border-primary focus-within:hover:!border-primary',\n 'data-[state=open]:border-border-hover',\n ],\n },\n {\n mode: 'display',\n variant: 'default',\n // 2026-05-13 Q3 Path Ⅰ(user 拍板 Path Ⅰ 全 zero chrome + codex V2 verdict + field-controls.spec.md (d)):\n // default display = zero chrome — !px-0 !py-0 override size token 的 px-3,跟 Select / Combobox\n // / DatePicker / TimePicker / LinkInput non-D-path bare-span idiom 一致(Carbon read-only / Stripe\n // display / Notion property / Polaris readonly TextField 全 zero chrome)。\n className: 'bg-transparent border border-transparent !px-0 !py-0',\n },\n {\n mode: 'readonly',\n variant: 'default',\n className: 'bg-readonly border border-transparent',\n },\n {\n // 2026-05-13 R3.5(per codex Q3 verdict + user 拍「想盡辦法 auto-handle prereq」):\n // 移除 `opacity-disabled` blanket — Avatar 已 fieldCtx-aware self-dim(avatar.tsx self-managed\n // via `isDisabledInField` derivation)。Field wrapper 不再 host-control Avatar opacity。\n // Inner content(text-fg-disabled / Avatar self-opacity)走具體 disabled token per color.spec.md:729。\n mode: 'disabled',\n variant: 'default',\n className: 'bg-disabled border border-transparent cursor-not-allowed',\n },\n // bare variant chrome by mode\n {\n mode: 'edit',\n variant: 'bare',\n className: [\n 'bg-transparent border border-transparent',\n 'hover:border-border',\n // 同 default chrome v13.3 SSOT:focus-within !important 強制勝過 data-state\n 'focus-within:!border-primary focus-within:hover:!border-primary',\n 'data-[state=open]:border-border',\n ],\n },\n {\n mode: 'display',\n variant: 'bare',\n // bare + display:cell-as-input default state(無 variant,完全融入 cell;hover/focus 才有 affordance 等 user 點下去切 edit mode)\n className: 'bg-transparent border border-transparent',\n },\n {\n mode: 'readonly',\n variant: 'bare',\n className: 'bg-transparent border border-transparent',\n },\n {\n // 2026-05-13 R3.5:移除 `opacity-disabled` blanket(per Avatar self-dim canonical)\n mode: 'disabled',\n variant: 'bare',\n className: 'bg-transparent border border-transparent cursor-not-allowed',\n },\n // naked variant — cell-as-input substrate(Notion / Airtable / Excel canonical)\n //\n // ── 2026-05-06 v14:revert v12 → v9 baseline + keep v13.3 ──\n // v12 `!absolute -inset-px` autoRowHeight 不相容(Field 抽 layout flow → cell 塌 42px;\n // user production 報「Field 沒撐滿 cell, 比沒改之前還糟糕一百萬倍」)→ revert。\n //\n // v14 = v9 baseline border-based state machine + v13.3 focus !important。\n // 暫接受視覺:Field.border-l 跟 prev cell.border-r 視覺 2px 雙線(待另案研究 seamless\n // 方案,約束:SSOT 留 Field state machine + ring 顏色自動跟 border state 同步)。\n // Phase 9 Issue 5 fix(2026-05-10 user 撞 + codex 重比稿 verdict ADOPT):\n // Field naked variant 之前全寫 `!gap-0` strip Field family slot gap → indicator(chevron /\n // calendar / clock)緊連 value 沒間距。違反 item-anatomy slot SSOT。\n // Codex verdict:「naked 把 chrome stripping 跟 slot anatomy stripping 混在一起。\n // chrome stripping 合理(去 padding / border / rounded);slot anatomy stripping 不合理\n // (prefix / content / suffix gap 仍是 item anatomy slot canonical `gap-2`)」\n // Fix:移除 `!gap-0`,讓 Field family base `gap-2`(field-wrapper.tsx:50)透出來。\n // Cite:item-anatomy.spec.md L46-50 / L113-122 partial consumer canonical;\n // field-controls.spec.md L22 Field Controls 視覺對齊 Family 1 Menu item layout。\n // 特殊 stack / multi-pill 場景若需 zero-gap,該 component spec 明文例外,不全域 strip。\n {\n mode: 'edit',\n variant: 'naked',\n className: [\n 'bg-transparent !rounded-none !h-full',\n '!px-[var(--table-cell-px)] !py-[var(--table-cell-py)]',\n 'border border-border',\n 'hover:border-border-hover',\n // v13.3 SSOT canonical:focus-within !important(同 default + bare)\n 'focus-within:!border-primary focus-within:hover:!border-primary',\n 'data-[state=open]:border-border-hover',\n 'group-data-[row-mode=auto]/cell:!items-start',\n ],\n },\n {\n // 2026-05-12 fix v2(M32 root invariant audit):\n // Q1 root invariant?:cell-as-input display 視覺位置 = `cell.items-{X}` × `Field.height`\n // 兩變數函數;canonical = autoRow → Field intrinsic + cell.items-start\n // (text 在 cell top + cellPadding-y),fixed → Field h-field-md +\n // cell.items-center(text 在 cell vertical center)。\n // Q2 symptom?:`h-field-md` 32px 在 autoRow tall cell + cell items-start → Field 32px\n // sitting at top,inside items-center default → text center = top+15 ≠ top。\n // 即使 `group-data-[row-mode=auto]/cell:!items-start` 加進來,Field height\n // 還是 32 → text 在 Field 內 top,但 Field 自己 height ≠ 0,offset 13~32px。\n // Q3 fix layer?:root-layer fix = Field 在 autoRow context 必 `h-auto` 才能讓 text 真正\n // flush 到 cell.top + padding。前 v1 只 remove `!h-full` 是 surface-fix\n // (沒解決 h-field-md 32px persistence)。v2 真根因 fix:加 `!h-auto`\n // override h-field-md → Field intrinsic line-height → cell items-start\n // 真實 anchor text at cell.top + padding。\n // Edit mode 不動(`!h-full` 保留 — border 必滿格對齊 cell border)。\n mode: 'display',\n variant: 'naked',\n className: [\n 'bg-transparent !rounded-none !px-0 !py-0 !h-auto',\n 'border border-transparent',\n ],\n },\n {\n mode: 'readonly',\n variant: 'naked',\n className: [\n 'bg-transparent !rounded-none !px-0 !py-0 !h-auto',\n 'border border-transparent',\n ],\n },\n {\n // 2026-05-13 Q3 fix(per codex Q3 verdict + color.spec.md:729 逃生艙 rule):\n // 移除 `opacity-disabled` blanket — naked wrapper 只負責 substrate(透明 border + 抑制 cursor),\n // 內部 content(text / icon / avatar)各自 disabled context 處理(text-fg-disabled / Avatar opacity 等具體 token)。\n // 對齊 DataTable cell-disabled TD 加 `bg-disabled` 表達 cell-level disabled state 的 SSOT 分權。\n // **default/bare disabled variant(line 107, 135)deferred R3.5** — 仍依賴 wrapper opacity-disabled\n // for Avatar dim(Avatar 尚未 fieldCtx-aware self-dim);Avatar self-dim 實作後再連帶移除。\n mode: 'disabled',\n variant: 'naked',\n className: [\n 'bg-transparent !rounded-none cursor-not-allowed !px-0 !py-0 !h-auto',\n 'border border-transparent',\n ],\n },\n ],\n defaultVariants: {\n mode: 'edit',\n variant: 'default',\n size: 'md',\n },\n }\n)\n\n// ── Bare Input Styles ───────────────────────────────────────────────────────\n\nexport const bareInputStyles = [\n // 2026-05-15 Q1 真 root cause fix(per codex Round 4 cite-based verdict):\n // 加 `truncate` — 原 bareInputStyles 含 `flex-1 min-w-0` 但無 `truncate / text-overflow` policy。\n // 當 Select `searchable && open` 切 raw `<input placeholder=...>` branch(select.tsx:178),\n // bareInputStyles 套上,placeholder 無 ellipsis → narrow cell(<160px)硬裁無 `...`(user round 3\n // 圖一證據)。加 truncate 後 `<input>` `text-overflow: ellipsis` 啟動,符合 user 「placeholder 直接被截掉沒有變...」\n // 該顯 ellipsis 的 SSOT。對齊 data-table.spec.md:233「禁硬裁無 ellipsis」+ field-controls.spec.md:286\n // 共享 contract(a)「display/readonly/disabled/edit 4 mode 共享同一 renderer」semantic 對齊。\n 'flex-1 min-w-0 truncate bg-transparent',\n 'outline-none border-none p-0',\n 'text-[inherit] font-[inherit] leading-[inherit]',\n // A3 fix(2026-05-05):`<input>` UA stylesheet 強制 `text-align: start`,阻斷 parent 的\n // `text-right`/`text-center` 繼承。顯式 `text-align: inherit` 復原(對齊 NumberCell /\n // CurrencyCell right-align canonical:column meta.align='right' → cell text-right →\n // input 跟著 right-align)。\n '[text-align:inherit]',\n 'placeholder:text-fg-muted',\n // K10 fix(2026-05-04):wrapper data-field-mode=disabled 時,placeholder/text 都切 fg-disabled\n // 依賴 fieldWrapperStyles 的 `group/field` + 各 Field 元件 wrapper 加 `data-field-mode={resolvedMode}`\n // User canonical:disabled state 顯著性優於 muted(neutral-6 > neutral-7)\n 'group-data-[field-mode=disabled]/field:placeholder:text-fg-disabled',\n 'group-data-[field-mode=disabled]/field:text-fg-disabled',\n].join(' ')\n\n// ── Naked Variant Cell Row-Mode Alignment Propagation ──────────────────────\n// SSOT canonical(M19 / 2026-05-05):cell-as-input naked variant 元件**所有內部\n// wrapper**(`<span>` 包 Avatar+name 等)必 import + apply 此 SSOT,host cell\n// `data-row-mode` 屬性自動 propagate alignment(autoRow → items-start / fixed → items-center)。\n//\n// 不 propagate 的後果:autoRow 場景下 People / Select / Combobox 內部用\n// `inline-flex items-center` hardcode → 視覺垂直置中於 wrapper 自身高度,**沒**頂對齊\n// → 跟其他純文字 cell baseline 視覺漂移。\n//\n// 世界級對照:\n// - HTML <td> default `vertical-align: baseline`(瀏覽器自動 first-baseline align)\n// - AG Grid `cellStyle` + `cellRendererSelector`,row context 共享(closed source 部分)\n// - Material X-Grid `gridClasses.cell` wrapper 不允許 cell content override alignment\n// - Notion / Airtable cell content 從 host 繼承,不 hardcode self alignment\n//\n// Hook:`check_naked_row_mode_propagation.sh`(write-time BLOCKER)\n// Audit:design-system-audit Group N M27(periodic batch verify)\nexport const nakedCellRowModeAlign = 'group-data-[row-mode=auto]/cell:items-start'\n\n// ── Cell-as-input Display Hover Ring(2026-05-05 v9 — sole remaining ring const)─\n// editable cell **display mode hover 提示**(「這 cell 可編,點 → 進 edit」affordance 信號)。\n// 對齊 Notion / Airtable hover-cell-shows-border canonical。\n//\n// **為何只剩這一個**:Field naked **edit/focus/open state ring 已下沉到 Field default state\n// machine**(border-based,2026-05-05 v9 architectural rewrite),不需 outline 平行系統;\n// 但 display mode 沒 Field state(display = 純展示無互動),hover 提示需 cell wrapper 自加。\n//\n// **2026-05-09 v15.17 revert v15.16(user 未同意 ship)— 維持 v15.13 outline+offset:-1**\n//\n// User 2026-05-09 後續 message:「設計決策的東西你應該要先問過我讓我決策吧?為什麼就直接開跑」\n// 我 commit 698ff58 ship v15.16(box-shadow inset Spec 2 不蓋 grid)= 設計決策結論未 user 同意\n// 直接 ship = workflow 違反。Revert 回 v15.13 outline+offset:-1(原 user-accepted 路徑),\n// 等 user 拍板 4-邊覆蓋路徑才 ship。\n//\n// User 並指出我視覺分析又錯:「我就是只有看到只有右邊被蓋掉,上面下面左邊都會露出 cell 的邊框」\n// 我之前錯說「right + bottom 都覆蓋」,bottom 真實 row border-b 在 cell.outer 外 1px,outline 蓋不到。\n//\n// 真實 4-邊狀態(re-verified):\n// - right: outline 199-200 = cell own border-r 199-200 → 蓋 1 條線 ✓\n// - top: outline 0-1,前一 row border-b 在 cell.outer 外 → 露 2 條線\n// - bottom: outline 38-39 在 cell 內,row border-b 在 row.outer 內 = cell.outer 外 1px → 露 2 條線\n// - left: outline 0-1,前一 cell border-r 在 cell.outer 外 → 露 2 條線\n//\n// User 想要 4 邊都覆蓋 = 等 codex unframed brief + user 拍板,本次不 ship。\n//\n// 之前 v15.13 / 14 / 16 / 17 緣由 → tsc comment 之前版本 + planning RFC\n// (跑錯方向 4 次 = 沒 1 次 verify 全面向 + 沒 user 拍板 set design)。\n//\n// Color `--border-hover` 對齊 Field default hover state token。\n//\n// **2026-05-10 Slice D Step 2 — Cell host CSS variable suppression**:\n// outline color 改用 `var(--cell-hover-outline-color, var(--border-hover))`,\n// allow DataTable cell host(spreadsheet overlay enabled 時)set `--cell-hover-outline-color: transparent`\n// 抑制 outline,讓 overlay layer 接管 hover ring paint(per RFC Contract 8 「one geometry owner, two paint owners」)。\n// Backward-compat:flag 關時 default `--border-hover`,既有行為不變。\nexport const nakedCellEditableDisplayHover = 'hover:outline hover:outline-1 hover:outline-offset-[-1px] hover:outline-[var(--cell-hover-outline-color,var(--border-hover))]'\n\n// ── Cell-as-input Edge Slot SSOT(2026-05-05 v8 — retire 平行 SSOT,改 L1 消費)───\n//\n// 前身 `nakedCellPrefixSlot` / `nakedCellSuffixSlot` 是 M1+M17 違反:平行 SSOT 跟\n// `patterns/element-anatomy` 的 `<ItemPrefix>` / `<ItemSuffix>` primitive 撞 home。\n// 已 retire — Field naked variant 內 prefix / suffix slot 直接消費 L1 primitive:\n//\n// import { ItemPrefix, ItemSuffix } from '@/design-system/patterns/element-anatomy/item-anatomy'\n// <ItemPrefix><StartIcon /></ItemPrefix> // 對 Input.startIcon / Select.startIcon\n// <ItemSuffix>{chevron}</ItemSuffix> // 對 Combobox / DatePicker / PeoplePicker chevron\n//\n// **`h-[1lh]` 普世正確**(item-anatomy.spec.md:190-191 verbatim):\n// - 單行 wrapper items-center → slot 1lh 在 cell 高度中心 = 第一行中線(視覺 = items-center)\n// - 多行 wrapper items-start → slot 1lh 鎖頂 = 第一行中線\n// 不需 conditional `group-data-[row-mode=auto]/cell:` — 我前 v4 自加的 conditional 是過度設計。\n//\n// State ring 3 const 仍留(下方)— 是 Field naked 專屬,MenuItem / TreeView 用 bg hover 不用 outline。\n// `nakedCellRowModeAlign`(wrapper 級)仍留 — 是 cell-context row-mode → wrapper alignment 適配,正交 slot 級。\n\n// ── Empty Value Display ─────────────────────────────────────────────────────\n\nexport const EMPTY_DISPLAY = '—'\n\n/**\n * 2026-05-14 I2 fix(per field-controls.spec.md contract (e) display typography canonical):\n * Field family display path bare-span helper — `sm/md → text-body` / `lg → text-body-lg`,\n * 跨 9 元件 display 視覺尺寸統一(user 抓 LinkInput 字體跟其他 Field 不一致 = SSOT 違反)。\n * Consumer:LinkInput / Select / Combobox / DatePicker / TimePicker non-D-path bare-span 套此 class。\n */\nexport const fieldDisplayTextClass = (size: 'sm' | 'md' | 'lg'): string =>\n size === 'lg' ? 'text-body-lg' : 'text-body'\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../../../src/components/Field/field.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,kBAAkB,EAAqB,MAAM,iBAAiB,CAAA;AAqDlI,MAAM,WAAW,UAAW,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC;IAClF,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,WAAW,CAAC,EAAE,gBAAgB,CAAA;IAC9B,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,kBAAkB,CAAA;CACnC;AAWD,QAAA,MAAM,KAAK,mFA2JV,CAAA;AAKD,MAAM,WAAW,eAAgB,SAAQ,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC;IAClF;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAGD,QAAA,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../../../src/components/Field/field.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,kBAAkB,EAAqB,MAAM,iBAAiB,CAAA;AAqDlI,MAAM,WAAW,UAAW,SAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC;IAClF,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,WAAW,CAAC,EAAE,gBAAgB,CAAA;IAC9B,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,kBAAkB,CAAA;CACnC;AAWD,QAAA,MAAM,KAAK,mFA2JV,CAAA;AAKD,MAAM,WAAW,eAAgB,SAAQ,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC;IAClF;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAGD,QAAA,MAAM,UAAU,0FA8Ff,CAAA;AAKD,QAAA,MAAM,gBAAgB,yHAsBpB,CAAA;AAKF,QAAA,MAAM,UAAU,yHAqBd,CAAA;AAOF,MAAM,WAAW,eAAgB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC3E,uCAAuC;IACvC,GAAG,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAA;IACpC;;;;;;;;;;OAUG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAC9B;AAED,QAAA,MAAM,UAAU,wFAkBf,CAAA;AAKD,eAAO,MAAM,SAAS;;;;;;;;;;;CAeZ,CAAA;AAEV,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,UAAU,EAAE,CAAA"}
|
|
@@ -184,6 +184,7 @@ const FieldLabel = React.forwardRef(
|
|
|
184
184
|
style: { ...horizontalBlockStyle, ...style },
|
|
185
185
|
"data-field-slot": "label",
|
|
186
186
|
"data-field-disabled": disabled ? "" : void 0,
|
|
187
|
+
"aria-disabled": disabled || void 0,
|
|
187
188
|
...props,
|
|
188
189
|
children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
189
190
|
/* @__PURE__ */ jsxs("span", { children: [
|