@ecoma-io/loom 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -9
- package/dist/a11y.js +2 -2
- package/dist/index.js +73 -6
- package/dist/node_modules/.pnpm/{@internationalized_date@3.12.3 → @internationalized_date@3.12.4}/node_modules/@internationalized/date/dist/private/CalendarDate.js +1 -1
- package/dist/node_modules/.pnpm/{@internationalized_date@3.12.3 → @internationalized_date@3.12.4}/node_modules/@internationalized/date/dist/private/CalendarDate.js.map +1 -1
- package/dist/node_modules/.pnpm/{@internationalized_date@3.12.3 → @internationalized_date@3.12.4}/node_modules/@internationalized/date/dist/private/calendars/GregorianCalendar.js +1 -1
- package/dist/node_modules/.pnpm/{@internationalized_date@3.12.3 → @internationalized_date@3.12.4}/node_modules/@internationalized/date/dist/private/calendars/GregorianCalendar.js.map +1 -1
- package/dist/node_modules/.pnpm/{@internationalized_date@3.12.3 → @internationalized_date@3.12.4}/node_modules/@internationalized/date/dist/private/conversion.js +1 -1
- package/dist/node_modules/.pnpm/{@internationalized_date@3.12.3 → @internationalized_date@3.12.4}/node_modules/@internationalized/date/dist/private/conversion.js.map +1 -1
- package/dist/node_modules/.pnpm/{@internationalized_date@3.12.3 → @internationalized_date@3.12.4}/node_modules/@internationalized/date/dist/private/manipulation.js +1 -1
- package/dist/node_modules/.pnpm/{@internationalized_date@3.12.3 → @internationalized_date@3.12.4}/node_modules/@internationalized/date/dist/private/manipulation.js.map +1 -1
- package/dist/node_modules/.pnpm/{@internationalized_date@3.12.3 → @internationalized_date@3.12.4}/node_modules/@internationalized/date/dist/private/queries.js +1 -1
- package/dist/node_modules/.pnpm/{@internationalized_date@3.12.3 → @internationalized_date@3.12.4}/node_modules/@internationalized/date/dist/private/queries.js.map +1 -1
- package/dist/node_modules/.pnpm/{@internationalized_date@3.12.3 → @internationalized_date@3.12.4}/node_modules/@internationalized/date/dist/private/string.js +1 -1
- package/dist/node_modules/.pnpm/{@internationalized_date@3.12.3 → @internationalized_date@3.12.4}/node_modules/@internationalized/date/dist/private/string.js.map +1 -1
- package/dist/node_modules/.pnpm/{@internationalized_date@3.12.3 → @internationalized_date@3.12.4}/node_modules/@internationalized/date/dist/private/utils.js +1 -1
- package/dist/node_modules/.pnpm/{@internationalized_date@3.12.3 → @internationalized_date@3.12.4}/node_modules/@internationalized/date/dist/private/utils.js.map +1 -1
- package/dist/packages/blocks/app-header/src/AppHeader.js.map +1 -1
- package/dist/packages/blocks/app-header/src/AppHeader.vue.d.ts.map +1 -1
- package/dist/packages/blocks/app-header/src/AppHeader.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/packages/blocks/app-header/src/AppHeader.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/blocks/desktop-app-shell/src/DesktopAppShell.vue.d.ts +3 -3
- package/dist/packages/blocks/empty-state/src/EmptyState.js.map +1 -1
- package/dist/packages/blocks/empty-state/src/EmptyState.vue.d.ts.map +1 -1
- package/dist/packages/blocks/empty-state/src/EmptyState.vue_vue_type_script_setup_true_lang.js +5 -6
- package/dist/packages/blocks/empty-state/src/EmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/blocks/error-summary/src/ErrorSummary.js +7 -0
- package/dist/packages/blocks/error-summary/src/ErrorSummary.js.map +1 -0
- package/dist/packages/blocks/error-summary/src/ErrorSummary.vue.d.ts +31 -0
- package/dist/packages/blocks/error-summary/src/ErrorSummary.vue_vue_type_script_setup_true_lang.js +90 -0
- package/dist/packages/blocks/error-summary/src/ErrorSummary.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/blocks/error-summary/src/index.d.ts +4 -0
- package/dist/packages/blocks/error-summary/src/index.js +3 -0
- package/dist/packages/blocks/metric-card/src/MetricCard.js.map +1 -1
- package/dist/packages/blocks/metric-card/src/MetricCard.vue.d.ts +22 -4
- package/dist/packages/blocks/metric-card/src/MetricCard.vue.d.ts.map +1 -1
- package/dist/packages/blocks/metric-card/src/MetricCard.vue_vue_type_script_setup_true_lang.js +38 -15
- package/dist/packages/blocks/metric-card/src/MetricCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/blocks/title-bar/src/TitleBar.vue.d.ts +3 -3
- package/dist/packages/blocks/toast-stack/src/ToastStack.vue.d.ts +3 -3
- package/dist/packages/composition/scroll-reel/src/ScrollReel.js +2 -2
- package/dist/packages/composition/scroll-reel/src/ScrollReel.js.map +1 -1
- package/dist/packages/composition/scroll-reel/src/ScrollReel.vue.d.ts +26 -0
- package/dist/packages/composition/scroll-reel/src/ScrollReel.vue_vue_type_script_setup_true_lang.js +28 -7
- package/dist/packages/composition/scroll-reel/src/ScrollReel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/composition/scroll-reel/src/index.d.ts +2 -1
- package/dist/packages/composition/scroll-reel/src/index.js +2 -1
- package/dist/packages/composition/sidebar/src/Sidebar.js.map +1 -1
- package/dist/packages/composition/sidebar/src/Sidebar.vue.d.ts +4 -0
- package/dist/packages/composition/sidebar/src/Sidebar.vue.d.ts.map +1 -1
- package/dist/packages/composition/sidebar/src/Sidebar.vue_vue_type_script_setup_true_lang.js +13 -4
- package/dist/packages/composition/sidebar/src/Sidebar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/composition/split/src/Split.js.map +1 -1
- package/dist/packages/composition/split/src/Split.vue.d.ts +11 -12
- package/dist/packages/composition/split/src/Split.vue.d.ts.map +1 -1
- package/dist/packages/composition/split/src/Split.vue_vue_type_script_setup_true_lang.js +15 -5
- package/dist/packages/composition/split/src/Split.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/core/src/a11y-scope.d.ts +71 -5
- package/dist/packages/core/src/a11y-scope.d.ts.map +1 -1
- package/dist/packages/core/src/a11y-scope.js +126 -6
- package/dist/packages/core/src/a11y-scope.js.map +1 -1
- package/dist/packages/core/src/index.d.ts +2 -2
- package/dist/packages/core/src/index.d.ts.map +1 -1
- package/dist/packages/core/src/index.js +3 -3
- package/dist/packages/core/src/motion.d.ts +13 -0
- package/dist/packages/core/src/motion.d.ts.map +1 -1
- package/dist/packages/core/src/motion.js +20 -13
- package/dist/packages/core/src/motion.js.map +1 -1
- package/dist/packages/core/src/theme.d.ts +9 -0
- package/dist/packages/core/src/theme.d.ts.map +1 -1
- package/dist/packages/core/src/theme.js +36 -10
- package/dist/packages/core/src/theme.js.map +1 -1
- package/dist/packages/labels/src/ancestor-disabled.d.ts +5 -1
- package/dist/packages/labels/src/ancestor-disabled.d.ts.map +1 -1
- package/dist/packages/labels/src/ancestor-disabled.js +5 -1
- package/dist/packages/labels/src/ancestor-disabled.js.map +1 -1
- package/dist/packages/labels/src/component-shapes.d.ts +236 -1
- package/dist/packages/labels/src/component-shapes.d.ts.map +1 -1
- package/dist/packages/labels/src/date-labels.d.ts +86 -7
- package/dist/packages/labels/src/date-labels.d.ts.map +1 -1
- package/dist/packages/labels/src/date-labels.js +58 -9
- package/dist/packages/labels/src/date-labels.js.map +1 -1
- package/dist/packages/labels/src/field-context.d.ts +2 -2
- package/dist/packages/labels/src/field-context.js.map +1 -1
- package/dist/packages/labels/src/index.d.ts +3 -3
- package/dist/packages/labels/src/index.d.ts.map +1 -1
- package/dist/packages/labels/src/index.js +2 -2
- package/dist/packages/labels/src/label-registry.d.ts +15 -1
- package/dist/packages/labels/src/label-registry.d.ts.map +1 -1
- package/dist/packages/layouts/dashboard/src/Dashboard.js.map +1 -1
- package/dist/packages/layouts/dashboard/src/Dashboard.vue.d.ts +4 -4
- package/dist/packages/layouts/dashboard/src/Dashboard.vue.d.ts.map +1 -1
- package/dist/packages/layouts/dashboard/src/Dashboard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/layouts/reading/src/Reading.js.map +1 -1
- package/dist/packages/layouts/reading/src/Reading.vue.d.ts +16 -15
- package/dist/packages/layouts/reading/src/Reading.vue.d.ts.map +1 -1
- package/dist/packages/layouts/reading/src/Reading.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/layouts/reading/src/index.d.ts +1 -0
- package/dist/packages/layouts/reading/src/index.d.ts.map +1 -1
- package/dist/packages/layouts/settings/src/Settings.js.map +1 -1
- package/dist/packages/layouts/settings/src/Settings.vue.d.ts +3 -3
- package/dist/packages/layouts/settings/src/Settings.vue.d.ts.map +1 -1
- package/dist/packages/layouts/settings/src/Settings.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/layouts/split-layout/src/SplitLayout.js.map +1 -1
- package/dist/packages/layouts/split-layout/src/SplitLayout.vue.d.ts +4 -6
- package/dist/packages/layouts/split-layout/src/SplitLayout.vue.d.ts.map +1 -1
- package/dist/packages/layouts/split-layout/src/SplitLayout.vue_vue_type_script_setup_true_lang.js +20 -5
- package/dist/packages/layouts/split-layout/src/SplitLayout.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/layouts/split-layout/src/index.d.ts +1 -1
- package/dist/packages/layouts/split-layout/src/index.d.ts.map +1 -1
- package/dist/packages/loom/src/a11y.d.ts +1 -1
- package/dist/packages/loom/src/index.d.ts +217 -174
- package/dist/packages/loom/src/theme.d.ts +3 -0
- package/dist/packages/primitives/alert/src/Alert.js +7 -0
- package/dist/packages/primitives/alert/src/Alert.js.map +1 -0
- package/dist/packages/primitives/alert/src/Alert.vue.d.ts +91 -0
- package/dist/packages/primitives/alert/src/Alert.vue_vue_type_script_setup_true_lang.js +130 -0
- package/dist/packages/primitives/alert/src/Alert.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/alert/src/index.d.ts +4 -0
- package/dist/packages/primitives/alert/src/index.js +3 -0
- package/dist/packages/primitives/alert-dialog/src/AlertDialog.js.map +1 -1
- package/dist/packages/primitives/alert-dialog/src/AlertDialog.vue.d.ts +2 -2
- package/dist/packages/primitives/alert-dialog/src/AlertDialog.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/packages/primitives/alert-dialog/src/AlertDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/alert-dialog/src/index.d.ts +1 -1
- package/dist/packages/primitives/avatar/src/Avatar.js.map +1 -1
- package/dist/packages/primitives/avatar/src/Avatar.vue.d.ts +2 -2
- package/dist/packages/primitives/avatar/src/Avatar.vue.d.ts.map +1 -1
- package/dist/packages/primitives/avatar/src/Avatar.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/avatar/src/Avatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/avatar-group/src/AvatarGroup.vue.d.ts +3 -3
- package/dist/packages/primitives/avatar-group/src/index.d.ts +1 -1
- package/dist/packages/primitives/badge/src/Badge.js.map +1 -1
- package/dist/packages/primitives/badge/src/Badge.vue.d.ts.map +1 -1
- package/dist/packages/primitives/badge/src/Badge.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/badge/src/Badge.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/breadcrumb/src/Breadcrumb.js +2 -2
- package/dist/packages/primitives/breadcrumb/src/Breadcrumb.js.map +1 -1
- package/dist/packages/primitives/breadcrumb/src/Breadcrumb.vue.d.ts +28 -0
- package/dist/packages/primitives/breadcrumb/src/Breadcrumb.vue_vue_type_script_setup_true_lang.js +16 -5
- package/dist/packages/primitives/breadcrumb/src/Breadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/breadcrumb/src/index.d.ts +2 -1
- package/dist/packages/primitives/breadcrumb/src/index.js +2 -1
- package/dist/packages/primitives/button/src/Button.js.map +1 -1
- package/dist/packages/primitives/button/src/Button.vue.d.ts.map +1 -1
- package/dist/packages/primitives/button/src/Button.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/button/src/index.js +2 -2
- package/dist/packages/primitives/calendar/src/Calendar.js +7 -0
- package/dist/packages/primitives/calendar/src/Calendar.js.map +1 -0
- package/dist/packages/primitives/calendar/src/Calendar.vue.d.ts +95 -0
- package/dist/packages/primitives/calendar/src/Calendar.vue_vue_type_script_setup_true_lang.js +204 -0
- package/dist/packages/primitives/calendar/src/Calendar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/calendar/src/index.d.ts +3 -0
- package/dist/packages/primitives/calendar/src/index.js +3 -0
- package/dist/packages/primitives/card/src/Card.js +7 -0
- package/dist/packages/primitives/card/src/Card.js.map +1 -0
- package/dist/packages/primitives/card/src/Card.vue.d.ts +72 -0
- package/dist/packages/primitives/card/src/Card.vue_vue_type_script_setup_true_lang.js +65 -0
- package/dist/packages/primitives/card/src/Card.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/card/src/index.d.ts +3 -0
- package/dist/packages/primitives/card/src/index.d.ts.map +1 -0
- package/dist/packages/primitives/card/src/index.js +2 -0
- package/dist/packages/primitives/carousel/src/Carousel.js +7 -0
- package/dist/packages/primitives/carousel/src/Carousel.js.map +1 -0
- package/dist/packages/primitives/carousel/src/Carousel.vue.d.ts +34 -0
- package/dist/packages/primitives/carousel/src/Carousel.vue_vue_type_script_setup_true_lang.js +175 -0
- package/dist/packages/primitives/carousel/src/Carousel.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/carousel/src/index.d.ts +3 -0
- package/dist/packages/primitives/carousel/src/index.js +3 -0
- package/dist/packages/primitives/checkbox/src/Checkbox.js.map +1 -1
- package/dist/packages/primitives/checkbox/src/Checkbox.vue.d.ts +1 -1
- package/dist/packages/primitives/checkbox/src/Checkbox.vue.d.ts.map +1 -1
- package/dist/packages/primitives/checkbox/src/Checkbox.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/packages/primitives/checkbox/src/Checkbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/chip/src/Chip.js.map +1 -1
- package/dist/packages/primitives/chip/src/Chip.vue.d.ts.map +1 -1
- package/dist/packages/primitives/chip/src/Chip.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/chip/src/Chip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/collapse/src/Collapse.js +7 -0
- package/dist/packages/primitives/collapse/src/Collapse.js.map +1 -0
- package/dist/packages/primitives/collapse/src/Collapse.vue.d.ts +56 -0
- package/dist/packages/primitives/collapse/src/Collapse.vue.d.ts.map +1 -0
- package/dist/packages/primitives/collapse/src/Collapse.vue_vue_type_script_setup_true_lang.js +81 -0
- package/dist/packages/primitives/collapse/src/Collapse.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/collapse/src/index.d.ts +2 -0
- package/dist/packages/primitives/collapse/src/index.d.ts.map +1 -0
- package/dist/packages/primitives/collapse/src/index.js +2 -0
- package/dist/packages/primitives/color-picker/src/ColorPicker.js.map +1 -1
- package/dist/packages/primitives/color-picker/src/ColorPicker.vue.d.ts +4 -4
- package/dist/packages/primitives/color-picker/src/ColorPicker.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/packages/primitives/color-picker/src/ColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/color-picker/src/index.d.ts +1 -1
- package/dist/packages/primitives/combobox/src/Combobox.js.map +1 -1
- package/dist/packages/primitives/combobox/src/Combobox.vue.d.ts +3 -3
- package/dist/packages/primitives/combobox/src/Combobox.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/packages/primitives/combobox/src/Combobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/combobox/src/index.d.ts +1 -1
- package/dist/packages/primitives/command/src/Command.js +7 -0
- package/dist/packages/primitives/command/src/Command.js.map +1 -0
- package/dist/packages/primitives/command/src/Command.vue.d.ts +77 -0
- package/dist/packages/primitives/command/src/Command.vue_vue_type_script_setup_true_lang.js +333 -0
- package/dist/packages/primitives/command/src/Command.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/command/src/index.d.ts +4 -0
- package/dist/packages/primitives/command/src/index.js +3 -0
- package/dist/packages/primitives/context-menu/src/ContextMenu.js.map +1 -1
- package/dist/packages/primitives/context-menu/src/ContextMenu.vue.d.ts +12 -0
- package/dist/packages/primitives/context-menu/src/ContextMenu.vue.d.ts.map +1 -1
- package/dist/packages/primitives/context-menu/src/ContextMenu.vue_vue_type_script_setup_true_lang.js +7 -3
- package/dist/packages/primitives/context-menu/src/ContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/copy-button/src/CopyButton.js +7 -0
- package/dist/packages/primitives/copy-button/src/CopyButton.js.map +1 -0
- package/dist/packages/primitives/copy-button/src/CopyButton.vue.d.ts +64 -0
- package/dist/packages/primitives/copy-button/src/CopyButton.vue_vue_type_script_setup_true_lang.js +113 -0
- package/dist/packages/primitives/copy-button/src/CopyButton.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/copy-button/src/index.d.ts +4 -0
- package/dist/packages/primitives/copy-button/src/index.js +3 -0
- package/dist/packages/primitives/data-grid/src/DataGrid.js +7 -0
- package/dist/packages/primitives/data-grid/src/DataGrid.js.map +1 -0
- package/dist/packages/primitives/data-grid/src/DataGrid.vue.d.ts +128 -0
- package/dist/packages/primitives/data-grid/src/DataGrid.vue_vue_type_script_setup_true_lang.js +347 -0
- package/dist/packages/primitives/data-grid/src/DataGrid.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/data-grid/src/index.d.ts +4 -0
- package/dist/packages/primitives/data-grid/src/index.js +3 -0
- package/dist/packages/primitives/date-picker/src/DatePicker.js.map +1 -1
- package/dist/packages/primitives/date-picker/src/DatePicker.vue.d.ts +2 -2
- package/dist/packages/primitives/date-picker/src/DatePicker.vue_vue_type_script_setup_true_lang.js +14 -14
- package/dist/packages/primitives/date-picker/src/DatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/date-range-picker/src/DateRangePicker.js.map +1 -1
- package/dist/packages/primitives/date-range-picker/src/DateRangePicker.vue.d.ts +3 -3
- package/dist/packages/primitives/date-range-picker/src/DateRangePicker.vue_vue_type_script_setup_true_lang.js +22 -19
- package/dist/packages/primitives/date-range-picker/src/DateRangePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/date-range-picker/src/index.d.ts +1 -1
- package/dist/packages/primitives/date-time-picker/src/DateTimePicker.js.map +1 -1
- package/dist/packages/primitives/date-time-picker/src/DateTimePicker.vue.d.ts +2 -2
- package/dist/packages/primitives/date-time-picker/src/DateTimePicker.vue_vue_type_script_setup_true_lang.js +18 -17
- package/dist/packages/primitives/date-time-picker/src/DateTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/date-time-range-picker/src/DateTimeRangePicker.js.map +1 -1
- package/dist/packages/primitives/date-time-range-picker/src/DateTimeRangePicker.vue.d.ts +3 -3
- package/dist/packages/primitives/date-time-range-picker/src/DateTimeRangePicker.vue_vue_type_script_setup_true_lang.js +18 -17
- package/dist/packages/primitives/date-time-range-picker/src/DateTimeRangePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/date-time-range-picker/src/index.d.ts +1 -1
- package/dist/packages/primitives/dialog/src/Dialog.js.map +1 -1
- package/dist/packages/primitives/dialog/src/Dialog.vue.d.ts +27 -11
- package/dist/packages/primitives/dialog/src/Dialog.vue_vue_type_script_setup_true_lang.js +6 -4
- package/dist/packages/primitives/dialog/src/Dialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/dialog/src/index.d.ts +1 -1
- package/dist/packages/primitives/drawer/src/Drawer.js.map +1 -1
- package/dist/packages/primitives/drawer/src/Drawer.vue.d.ts +12 -4
- package/dist/packages/primitives/drawer/src/Drawer.vue_vue_type_script_setup_true_lang.js +13 -5
- package/dist/packages/primitives/drawer/src/Drawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/drawer/src/index.d.ts +1 -1
- package/dist/packages/primitives/dropdown-menu/src/DropdownMenu.js.map +1 -1
- package/dist/packages/primitives/dropdown-menu/src/DropdownMenu.vue.d.ts +26 -0
- package/dist/packages/primitives/dropdown-menu/src/DropdownMenu.vue.d.ts.map +1 -1
- package/dist/packages/primitives/dropdown-menu/src/DropdownMenu.vue_vue_type_script_setup_true_lang.js +10 -4
- package/dist/packages/primitives/dropdown-menu/src/DropdownMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/editable/src/Editable.js.map +1 -1
- package/dist/packages/primitives/editable/src/Editable.vue.d.ts +2 -2
- package/dist/packages/primitives/editable/src/Editable.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/editable/src/Editable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/editable/src/index.d.ts +1 -1
- package/dist/packages/primitives/file-upload/src/FileUpload.js.map +1 -1
- package/dist/packages/primitives/file-upload/src/FileUpload.vue.d.ts +2 -2
- package/dist/packages/primitives/file-upload/src/FileUpload.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/file-upload/src/FileUpload.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/file-upload/src/index.d.ts +1 -1
- package/dist/packages/primitives/hover-card/src/HoverCard.js.map +1 -1
- package/dist/packages/primitives/hover-card/src/HoverCard.vue.d.ts.map +1 -1
- package/dist/packages/primitives/hover-card/src/HoverCard.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/hover-card/src/HoverCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/hover-card/src/index.d.ts.map +1 -1
- package/dist/packages/primitives/icon-button/src/IconButton.js.map +1 -1
- package/dist/packages/primitives/icon-button/src/IconButton.vue.d.ts +10 -4
- package/dist/packages/primitives/icon-button/src/IconButton.vue.d.ts.map +1 -1
- package/dist/packages/primitives/icon-button/src/IconButton.vue_vue_type_script_setup_true_lang.js +18 -6
- package/dist/packages/primitives/icon-button/src/IconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/indicator/src/Indicator.vue.d.ts +2 -2
- package/dist/packages/primitives/indicator/src/index.d.ts +1 -1
- package/dist/packages/primitives/kbd/src/Kbd.js +7 -0
- package/dist/packages/primitives/kbd/src/Kbd.js.map +1 -0
- package/dist/packages/primitives/kbd/src/Kbd.vue.d.ts +30 -0
- package/dist/packages/primitives/kbd/src/Kbd.vue.d.ts.map +1 -0
- package/dist/packages/primitives/kbd/src/Kbd.vue_vue_type_script_setup_true_lang.js +29 -0
- package/dist/packages/primitives/kbd/src/Kbd.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/kbd/src/index.d.ts +3 -0
- package/dist/packages/primitives/kbd/src/index.d.ts.map +1 -0
- package/dist/packages/primitives/kbd/src/index.js +3 -0
- package/dist/packages/primitives/link/src/Link.js.map +1 -1
- package/dist/packages/primitives/link/src/Link.vue.d.ts.map +1 -1
- package/dist/packages/primitives/link/src/Link.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/packages/primitives/link/src/Link.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/list/src/List.js +7 -0
- package/dist/packages/primitives/list/src/List.js.map +1 -0
- package/dist/packages/primitives/list/src/List.vue.d.ts +22 -0
- package/dist/packages/primitives/list/src/List.vue.d.ts.map +1 -0
- package/dist/packages/primitives/list/src/List.vue_vue_type_script_setup_true_lang.js +27 -0
- package/dist/packages/primitives/list/src/List.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/list/src/ListItem.js +7 -0
- package/dist/packages/primitives/list/src/ListItem.js.map +1 -0
- package/dist/packages/primitives/list/src/ListItem.vue.d.ts +75 -0
- package/dist/packages/primitives/list/src/ListItem.vue.d.ts.map +1 -0
- package/dist/packages/primitives/list/src/ListItem.vue_vue_type_script_setup_true_lang.js +101 -0
- package/dist/packages/primitives/list/src/ListItem.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/list/src/index.d.ts +4 -0
- package/dist/packages/primitives/list/src/index.d.ts.map +1 -0
- package/dist/packages/primitives/list/src/index.js +3 -0
- package/dist/packages/primitives/live-region/src/LiveRegion.js +7 -0
- package/dist/packages/primitives/live-region/src/LiveRegion.js.map +1 -0
- package/dist/packages/primitives/live-region/src/LiveRegion.vue.d.ts +36 -0
- package/dist/packages/primitives/live-region/src/LiveRegion.vue.d.ts.map +1 -0
- package/dist/packages/primitives/live-region/src/LiveRegion.vue_vue_type_script_setup_true_lang.js +59 -0
- package/dist/packages/primitives/live-region/src/LiveRegion.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/live-region/src/context.d.ts +40 -0
- package/dist/packages/primitives/live-region/src/context.d.ts.map +1 -0
- package/dist/packages/primitives/live-region/src/context.js +85 -0
- package/dist/packages/primitives/live-region/src/context.js.map +1 -0
- package/dist/packages/primitives/live-region/src/index.d.ts +4 -0
- package/dist/packages/primitives/live-region/src/index.d.ts.map +1 -0
- package/dist/packages/primitives/live-region/src/index.js +3 -0
- package/dist/packages/primitives/live-region/src/useAnnounce.d.ts +17 -0
- package/dist/packages/primitives/live-region/src/useAnnounce.d.ts.map +1 -0
- package/dist/packages/primitives/live-region/src/useAnnounce.js +25 -0
- package/dist/packages/primitives/live-region/src/useAnnounce.js.map +1 -0
- package/dist/packages/primitives/menubar/src/Menubar.js.map +1 -1
- package/dist/packages/primitives/menubar/src/Menubar.vue.d.ts +20 -6
- package/dist/packages/primitives/menubar/src/Menubar.vue.d.ts.map +1 -1
- package/dist/packages/primitives/menubar/src/Menubar.vue_vue_type_script_setup_true_lang.js +211 -64
- package/dist/packages/primitives/menubar/src/Menubar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/meter/src/Meter.js +7 -0
- package/dist/packages/primitives/meter/src/Meter.js.map +1 -0
- package/dist/packages/primitives/meter/src/Meter.vue.d.ts +87 -0
- package/dist/packages/primitives/meter/src/Meter.vue_vue_type_script_setup_true_lang.js +236 -0
- package/dist/packages/primitives/meter/src/Meter.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/meter/src/index.d.ts +4 -0
- package/dist/packages/primitives/meter/src/index.js +3 -0
- package/dist/packages/primitives/navigation-menu/src/NavigationMenu.js +7 -0
- package/dist/packages/primitives/navigation-menu/src/NavigationMenu.js.map +1 -0
- package/dist/packages/primitives/navigation-menu/src/NavigationMenu.vue.d.ts +69 -0
- package/dist/packages/primitives/navigation-menu/src/NavigationMenu.vue.d.ts.map +1 -0
- package/dist/packages/primitives/navigation-menu/src/NavigationMenu.vue_vue_type_script_setup_true_lang.js +118 -0
- package/dist/packages/primitives/navigation-menu/src/NavigationMenu.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/navigation-menu/src/index.d.ts +3 -0
- package/dist/packages/primitives/navigation-menu/src/index.d.ts.map +1 -0
- package/dist/packages/primitives/navigation-menu/src/index.js +2 -0
- package/dist/packages/primitives/number-field/src/NumberField.js.map +1 -1
- package/dist/packages/primitives/number-field/src/NumberField.vue.d.ts +2 -2
- package/dist/packages/primitives/number-field/src/NumberField.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/packages/primitives/number-field/src/NumberField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/number-field/src/index.d.ts +1 -1
- package/dist/packages/primitives/otp-input/src/OtpInput.vue.d.ts +2 -2
- package/dist/packages/primitives/otp-input/src/index.d.ts +1 -1
- package/dist/packages/primitives/pagination/src/Pagination.js.map +1 -1
- package/dist/packages/primitives/pagination/src/Pagination.vue.d.ts +3 -3
- package/dist/packages/primitives/pagination/src/Pagination.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/pagination/src/Pagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/pagination/src/index.d.ts +1 -1
- package/dist/packages/primitives/popover/src/Popover.js.map +1 -1
- package/dist/packages/primitives/popover/src/Popover.vue.d.ts.map +1 -1
- package/dist/packages/primitives/popover/src/Popover.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/popover/src/Popover.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/progress/src/Progress.vue.d.ts +2 -2
- package/dist/packages/primitives/progress/src/index.d.ts +1 -1
- package/dist/packages/primitives/radial-progress/src/RadialProgress.vue.d.ts +2 -2
- package/dist/packages/primitives/radial-progress/src/index.d.ts +1 -1
- package/dist/packages/primitives/radio-group/src/RadioGroup.js.map +1 -1
- package/dist/packages/primitives/radio-group/src/RadioGroup.vue.d.ts.map +1 -1
- package/dist/packages/primitives/radio-group/src/RadioGroup.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/radio-group/src/RadioGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/rating/src/Rating.vue.d.ts +2 -2
- package/dist/packages/primitives/rating/src/index.d.ts +1 -1
- package/dist/packages/primitives/segmented-control/src/SegmentedControl.js.map +1 -1
- package/dist/packages/primitives/segmented-control/src/SegmentedControl.vue.d.ts.map +1 -1
- package/dist/packages/primitives/segmented-control/src/SegmentedControl.vue_vue_type_script_setup_true_lang.js +2 -3
- package/dist/packages/primitives/segmented-control/src/SegmentedControl.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/select/src/Select.js.map +1 -1
- package/dist/packages/primitives/select/src/Select.vue.d.ts.map +1 -1
- package/dist/packages/primitives/select/src/Select.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/select/src/Select.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/skip-link/src/SkipLink.js +7 -0
- package/dist/packages/primitives/skip-link/src/SkipLink.js.map +1 -0
- package/dist/packages/primitives/skip-link/src/SkipLink.vue.d.ts +62 -0
- package/dist/packages/primitives/skip-link/src/SkipLink.vue_vue_type_script_setup_true_lang.js +64 -0
- package/dist/packages/primitives/skip-link/src/SkipLink.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/skip-link/src/index.d.ts +3 -0
- package/dist/packages/primitives/skip-link/src/index.js +3 -0
- package/dist/packages/primitives/slider/src/Slider.js.map +1 -1
- package/dist/packages/primitives/slider/src/Slider.vue.d.ts +6 -3
- package/dist/packages/primitives/slider/src/Slider.vue.d.ts.map +1 -1
- package/dist/packages/primitives/slider/src/Slider.vue_vue_type_script_setup_true_lang.js +15 -10
- package/dist/packages/primitives/slider/src/Slider.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/stepper/src/Stepper.js.map +1 -1
- package/dist/packages/primitives/stepper/src/Stepper.vue.d.ts +2 -2
- package/dist/packages/primitives/stepper/src/Stepper.vue_vue_type_script_setup_true_lang.js +7 -5
- package/dist/packages/primitives/stepper/src/Stepper.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/stepper/src/index.d.ts +1 -1
- package/dist/packages/primitives/switch/src/Switch.js.map +1 -1
- package/dist/packages/primitives/switch/src/Switch.vue.d.ts +1 -1
- package/dist/packages/primitives/switch/src/Switch.vue.d.ts.map +1 -1
- package/dist/packages/primitives/switch/src/Switch.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/packages/primitives/switch/src/Switch.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/table/src/Table.js +7 -0
- package/dist/packages/primitives/table/src/Table.js.map +1 -0
- package/dist/packages/primitives/table/src/Table.vue.d.ts +45 -0
- package/dist/packages/primitives/table/src/Table.vue_vue_type_script_setup_true_lang.js +72 -0
- package/dist/packages/primitives/table/src/Table.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/table/src/TableCell.js +7 -0
- package/dist/packages/primitives/table/src/TableCell.js.map +1 -0
- package/dist/packages/primitives/table/src/TableCell.vue.d.ts +19 -0
- package/dist/packages/primitives/table/src/TableCell.vue.d.ts.map +1 -0
- package/dist/packages/primitives/table/src/TableCell.vue_vue_type_script_setup_true_lang.js +17 -0
- package/dist/packages/primitives/table/src/TableCell.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/table/src/TableHead.js +7 -0
- package/dist/packages/primitives/table/src/TableHead.js.map +1 -0
- package/dist/packages/primitives/table/src/TableHead.vue.d.ts +56 -0
- package/dist/packages/primitives/table/src/TableHead.vue_vue_type_script_setup_true_lang.js +79 -0
- package/dist/packages/primitives/table/src/TableHead.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/table/src/TableRow.js +7 -0
- package/dist/packages/primitives/table/src/TableRow.js.map +1 -0
- package/dist/packages/primitives/table/src/TableRow.vue.d.ts +39 -0
- package/dist/packages/primitives/table/src/TableRow.vue.d.ts.map +1 -0
- package/dist/packages/primitives/table/src/TableRow.vue_vue_type_script_setup_true_lang.js +61 -0
- package/dist/packages/primitives/table/src/TableRow.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/table/src/index.d.ts +10 -0
- package/dist/packages/primitives/table/src/index.js +8 -0
- package/dist/packages/primitives/tabs/src/Tabs.js.map +1 -1
- package/dist/packages/primitives/tabs/src/Tabs.vue.d.ts.map +1 -1
- package/dist/packages/primitives/tabs/src/Tabs.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/tabs/src/Tabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/tags-input/src/TagsInput.js.map +1 -1
- package/dist/packages/primitives/tags-input/src/TagsInput.vue.d.ts +2 -2
- package/dist/packages/primitives/tags-input/src/TagsInput.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/tags-input/src/TagsInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/tags-input/src/index.d.ts +1 -1
- package/dist/packages/primitives/text-field/src/TextField.js.map +1 -1
- package/dist/packages/primitives/text-field/src/TextField.vue.d.ts +2 -2
- package/dist/packages/primitives/text-field/src/TextField.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/text-field/src/TextField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/text-field/src/index.d.ts +1 -1
- package/dist/packages/primitives/text-field/src/index.js +2 -2
- package/dist/packages/primitives/textarea/src/Textarea.js.map +1 -1
- package/dist/packages/primitives/textarea/src/Textarea.vue.d.ts +2 -2
- package/dist/packages/primitives/textarea/src/Textarea.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/textarea/src/Textarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/textarea/src/index.d.ts +1 -1
- package/dist/packages/primitives/textarea/src/index.js +2 -2
- package/dist/packages/primitives/time-picker/src/TimePicker.js.map +1 -1
- package/dist/packages/primitives/time-picker/src/TimePicker.vue.d.ts +2 -2
- package/dist/packages/primitives/time-picker/src/TimePicker.vue_vue_type_script_setup_true_lang.js +10 -8
- package/dist/packages/primitives/time-picker/src/TimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/timeline/src/Timeline.js +7 -0
- package/dist/packages/primitives/timeline/src/Timeline.js.map +1 -0
- package/dist/packages/primitives/timeline/src/Timeline.vue.d.ts +37 -0
- package/dist/packages/primitives/timeline/src/Timeline.vue_vue_type_script_setup_true_lang.js +44 -0
- package/dist/packages/primitives/timeline/src/Timeline.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/timeline/src/TimelineItem.js +7 -0
- package/dist/packages/primitives/timeline/src/TimelineItem.js.map +1 -0
- package/dist/packages/primitives/timeline/src/TimelineItem.vue.d.ts +50 -0
- package/dist/packages/primitives/timeline/src/TimelineItem.vue_vue_type_script_setup_true_lang.js +96 -0
- package/dist/packages/primitives/timeline/src/TimelineItem.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/timeline/src/index.d.ts +5 -0
- package/dist/packages/primitives/timeline/src/index.js +5 -0
- package/dist/packages/primitives/toast/src/Toast.vue.d.ts +2 -2
- package/dist/packages/primitives/toast/src/ToastItem.js.map +1 -1
- package/dist/packages/primitives/toast/src/ToastItem.vue.d.ts +2 -2
- package/dist/packages/primitives/toast/src/ToastItem.vue_vue_type_script_setup_true_lang.js +5 -3
- package/dist/packages/primitives/toast/src/ToastItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/toast/src/index.d.ts +1 -1
- package/dist/packages/primitives/toggle-group/src/ToggleGroup.js +7 -0
- package/dist/packages/primitives/toggle-group/src/ToggleGroup.js.map +1 -0
- package/dist/packages/primitives/toggle-group/src/ToggleGroup.vue.d.ts +53 -0
- package/dist/packages/primitives/toggle-group/src/ToggleGroup.vue.d.ts.map +1 -0
- package/dist/packages/primitives/toggle-group/src/ToggleGroup.vue_vue_type_script_setup_true_lang.js +111 -0
- package/dist/packages/primitives/toggle-group/src/ToggleGroup.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/toggle-group/src/index.d.ts +3 -0
- package/dist/packages/primitives/toggle-group/src/index.d.ts.map +1 -0
- package/dist/packages/primitives/toggle-group/src/index.js +2 -0
- package/dist/packages/primitives/toolbar/src/Toolbar.js +7 -0
- package/dist/packages/primitives/toolbar/src/Toolbar.js.map +1 -0
- package/dist/packages/primitives/toolbar/src/Toolbar.vue.d.ts +31 -0
- package/dist/packages/primitives/toolbar/src/Toolbar.vue.d.ts.map +1 -0
- package/dist/packages/primitives/toolbar/src/Toolbar.vue_vue_type_script_setup_true_lang.js +149 -0
- package/dist/packages/primitives/toolbar/src/Toolbar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/toolbar/src/ToolbarSeparator.js +7 -0
- package/dist/packages/primitives/toolbar/src/ToolbarSeparator.js.map +1 -0
- package/dist/packages/primitives/toolbar/src/ToolbarSeparator.vue.d.ts +24 -0
- package/dist/packages/primitives/toolbar/src/ToolbarSeparator.vue.d.ts.map +1 -0
- package/dist/packages/primitives/toolbar/src/ToolbarSeparator.vue_vue_type_script_setup_true_lang.js +40 -0
- package/dist/packages/primitives/toolbar/src/ToolbarSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/toolbar/src/index.d.ts +4 -0
- package/dist/packages/primitives/toolbar/src/index.d.ts.map +1 -0
- package/dist/packages/primitives/toolbar/src/index.js +3 -0
- package/dist/packages/primitives/tooltip/src/Tooltip.js.map +1 -1
- package/dist/packages/primitives/tooltip/src/Tooltip.vue.d.ts.map +1 -1
- package/dist/packages/primitives/tooltip/src/Tooltip.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/tooltip/src/Tooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/tree-view/src/TreeView.js +7 -0
- package/dist/packages/primitives/tree-view/src/TreeView.js.map +1 -0
- package/dist/packages/primitives/tree-view/src/TreeView.vue.d.ts +99 -0
- package/dist/packages/primitives/tree-view/src/TreeView.vue_vue_type_script_setup_true_lang.js +310 -0
- package/dist/packages/primitives/tree-view/src/TreeView.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/tree-view/src/TreeViewNode.js +7 -0
- package/dist/packages/primitives/tree-view/src/TreeViewNode.js.map +1 -0
- package/dist/packages/primitives/tree-view/src/TreeViewNode.vue.d.ts +15 -0
- package/dist/packages/primitives/tree-view/src/TreeViewNode.vue.d.ts.map +1 -0
- package/dist/packages/primitives/tree-view/src/TreeViewNode.vue_vue_type_script_setup_true_lang.js +103 -0
- package/dist/packages/primitives/tree-view/src/TreeViewNode.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/tree-view/src/context.d.ts +39 -0
- package/dist/packages/primitives/tree-view/src/context.d.ts.map +1 -0
- package/dist/packages/primitives/tree-view/src/context.js +6 -0
- package/dist/packages/primitives/tree-view/src/context.js.map +1 -0
- package/dist/packages/primitives/tree-view/src/index.d.ts +4 -0
- package/dist/packages/primitives/tree-view/src/index.js +3 -0
- package/dist/packages/primitives/visually-hidden/src/VisuallyHidden.js +7 -0
- package/dist/packages/primitives/visually-hidden/src/VisuallyHidden.js.map +1 -0
- package/dist/packages/primitives/visually-hidden/src/VisuallyHidden.vue.d.ts +21 -0
- package/dist/packages/primitives/visually-hidden/src/VisuallyHidden.vue.d.ts.map +1 -0
- package/dist/packages/primitives/visually-hidden/src/VisuallyHidden.vue_vue_type_script_setup_true_lang.js +27 -0
- package/dist/packages/primitives/visually-hidden/src/VisuallyHidden.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/visually-hidden/src/index.d.ts +3 -0
- package/dist/packages/primitives/visually-hidden/src/index.d.ts.map +1 -0
- package/dist/packages/primitives/visually-hidden/src/index.js +2 -0
- package/dist/packages/primitives/window-controls/src/WindowControls.vue.d.ts +2 -2
- package/dist/packages/primitives/window-controls/src/index.d.ts +1 -1
- package/dist/styles/global.css +59 -0
- package/dist/styles/theme.css +13 -0
- package/dist/theme.js +3 -0
- package/package.json +37 -30
- package/dist/packages/blocks/desktop-app-shell/src/DesktopAppShell.vue.d.ts.map +0 -1
- package/dist/packages/blocks/title-bar/src/TitleBar.vue.d.ts.map +0 -1
- package/dist/packages/blocks/toast-stack/src/ToastStack.vue.d.ts.map +0 -1
- package/dist/packages/composition/scroll-reel/src/ScrollReel.vue.d.ts.map +0 -1
- package/dist/packages/composition/scroll-reel/src/index.d.ts.map +0 -1
- package/dist/packages/loom/src/a11y.d.ts.map +0 -1
- package/dist/packages/loom/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/alert-dialog/src/AlertDialog.vue.d.ts.map +0 -1
- package/dist/packages/primitives/alert-dialog/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/avatar-group/src/AvatarGroup.vue.d.ts.map +0 -1
- package/dist/packages/primitives/avatar-group/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/breadcrumb/src/Breadcrumb.vue.d.ts.map +0 -1
- package/dist/packages/primitives/breadcrumb/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/color-picker/src/ColorPicker.vue.d.ts.map +0 -1
- package/dist/packages/primitives/color-picker/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/combobox/src/Combobox.vue.d.ts.map +0 -1
- package/dist/packages/primitives/combobox/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/date-picker/src/DatePicker.vue.d.ts.map +0 -1
- package/dist/packages/primitives/date-range-picker/src/DateRangePicker.vue.d.ts.map +0 -1
- package/dist/packages/primitives/date-range-picker/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/date-time-picker/src/DateTimePicker.vue.d.ts.map +0 -1
- package/dist/packages/primitives/date-time-range-picker/src/DateTimeRangePicker.vue.d.ts.map +0 -1
- package/dist/packages/primitives/date-time-range-picker/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/dialog/src/Dialog.vue.d.ts.map +0 -1
- package/dist/packages/primitives/dialog/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/drawer/src/Drawer.vue.d.ts.map +0 -1
- package/dist/packages/primitives/drawer/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/editable/src/Editable.vue.d.ts.map +0 -1
- package/dist/packages/primitives/editable/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/file-upload/src/FileUpload.vue.d.ts.map +0 -1
- package/dist/packages/primitives/file-upload/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/indicator/src/Indicator.vue.d.ts.map +0 -1
- package/dist/packages/primitives/indicator/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/number-field/src/NumberField.vue.d.ts.map +0 -1
- package/dist/packages/primitives/number-field/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/otp-input/src/OtpInput.vue.d.ts.map +0 -1
- package/dist/packages/primitives/otp-input/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/pagination/src/Pagination.vue.d.ts.map +0 -1
- package/dist/packages/primitives/pagination/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/progress/src/Progress.vue.d.ts.map +0 -1
- package/dist/packages/primitives/progress/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/radial-progress/src/RadialProgress.vue.d.ts.map +0 -1
- package/dist/packages/primitives/radial-progress/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/rating/src/Rating.vue.d.ts.map +0 -1
- package/dist/packages/primitives/rating/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/stepper/src/Stepper.vue.d.ts.map +0 -1
- package/dist/packages/primitives/stepper/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/tags-input/src/TagsInput.vue.d.ts.map +0 -1
- package/dist/packages/primitives/tags-input/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/text-field/src/TextField.vue.d.ts.map +0 -1
- package/dist/packages/primitives/text-field/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/textarea/src/Textarea.vue.d.ts.map +0 -1
- package/dist/packages/primitives/textarea/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/time-picker/src/TimePicker.vue.d.ts.map +0 -1
- package/dist/packages/primitives/toast/src/Toast.vue.d.ts.map +0 -1
- package/dist/packages/primitives/toast/src/ToastItem.vue.d.ts.map +0 -1
- package/dist/packages/primitives/toast/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/window-controls/src/WindowControls.vue.d.ts.map +0 -1
- package/dist/packages/primitives/window-controls/src/index.d.ts.map +0 -1
- package/dist/styles/theme.contrast.test.ts +0 -460
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Drawer.js","names":[],"sources":["../../../../../packages/primitives/drawer/src/Drawer.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { DrawerLabels } from \"@ecoma-io/loom-labels\";\n/**\n\n/**\n * Loom's English, co-located with the component so it tree-shakes with it, and\n * exported so a host can build a partial vocabulary against the real thing.\n */\nexport const DRAWER_LABELS: DrawerLabels = {\n close: \"Close\",\n};\n\n/**\n * Which edge of the viewport the panel is anchored to. `right` is the default\n * because a drawer is most often opened *from* a row or a toolbar to show more\n * about what is already on screen, and left-to-right readers expect that\n * detail to arrive on the trailing edge. `left` is where navigation lives,\n * `bottom` is the sheet a narrow screen gets, and `top` is the rare one — a\n * command surface dropping in over the header.\n */\nexport type DrawerSide = \"left\" | \"right\" | \"top\" | \"bottom\";\n\n/**\n * How far the panel reaches along its own axis — width for a `left`/`right`\n * drawer, height for a `top`/`bottom` one. Like Dialog's width, this is the\n * primitive's decision rather than a class a caller passes, so two drawers\n * opened from two screens cannot be two different sizes: `sm` for a filter rail\n * or a navigation sheet, `md` for a record's detail, `lg` for a working surface\n * with a table or a multi-section form in it.\n */\nexport type DrawerSize = \"sm\" | \"md\" | \"lg\";\n\n/**\n * One row per edge, because `side` is not one decision but four made together —\n * which edge the panel is pinned to, which way it slides in from, which\n * dimension `size` then means, and which pair of corners is rounded — plus the\n * direction a dismissing swipe has to travel. Split across a `cva` map and\n * three lookups they drift, and the drift is invisible: a panel pinned to the\n * left that slides in from the right still renders, still passes its own tests,\n * and looks like a rendering bug nobody can name. `satisfies` is the other half\n * — adding a member to `DrawerSide` and not to this table stops compiling.\n *\n * `slide` and `exit` are the same motion in two directions and deliberately not\n * the same mechanism, which is worth reading before either is \"unified\".\n *\n * `slide` is a `starting:` variant rather than a keyframe, and it is a\n * transform rather than an inset or a width. A full-height panel animated on\n * `width`/`right` relayouts the document on every frame; `translate` runs on\n * the compositor. `@starting-style` supplies the before-value for the entry\n * transition at the moment Reka's `Presence` inserts the node, which is the one\n * thing a plain class cannot do. It also composes with the live swipe below:\n * the gesture writes `transform` while the entry owns `translate`, and a\n * keyframe on `transform` would overwrite a drag mid-finger.\n *\n * `exit` has to be a keyframe, for the opposite reason. `Presence` decides how\n * long to keep a closing panel mounted by reading `animation-name` and waiting\n * for `animationend` — it never observes `transitionend`. So a closed panel\n * carrying only a transition is removed from the DOM in the same frame and the\n * transition never runs, which is exactly what a drawer did until these tokens\n * existed: a 320ms slide in, answered by a hard cut. The keyframes animate\n * `translate` rather than `transform` for the same composition reason the entry\n * does, so a panel released mid-swipe leaves from where the finger left it.\n *\n * `handle` positions the grab affordance on the panel's *inner* edge, running\n * along it: a vertical pill on a side drawer, a horizontal one on a sheet.\n */\nconst SIDES = {\n left: {\n edge: \"inset-y-0 left-0 border-r\",\n corners: \"rounded-r-lg\",\n slide: \"starting:-translate-x-full\",\n exit: \"data-[state=closed]:animate-slide-out-to-left\",\n extent: \"width\",\n swipe: \"left\",\n handle: \"right-1.5 top-1/2 h-10 w-1.5 -translate-y-1/2\",\n },\n right: {\n edge: \"inset-y-0 right-0 border-l\",\n corners: \"rounded-l-lg\",\n slide: \"starting:translate-x-full\",\n exit: \"data-[state=closed]:animate-slide-out-to-right\",\n extent: \"width\",\n swipe: \"right\",\n handle: \"left-1.5 top-1/2 h-10 w-1.5 -translate-y-1/2\",\n },\n top: {\n edge: \"inset-x-0 top-0 border-b\",\n corners: \"rounded-b-lg\",\n slide: \"starting:-translate-y-full\",\n exit: \"data-[state=closed]:animate-slide-out-to-top\",\n extent: \"height\",\n swipe: \"up\",\n handle: \"bottom-1.5 left-1/2 h-1.5 w-10 -translate-x-1/2\",\n },\n bottom: {\n edge: \"inset-x-0 bottom-0 border-t\",\n corners: \"rounded-t-lg\",\n slide: \"starting:translate-y-full\",\n exit: \"data-[state=closed]:animate-slide-out-to-bottom\",\n extent: \"height\",\n swipe: \"down\",\n handle: \"top-1.5 left-1/2 h-1.5 w-10 -translate-x-1/2\",\n },\n} satisfies Record<\n DrawerSide,\n {\n edge: string;\n corners: string;\n slide: string;\n exit: string;\n extent: \"width\" | \"height\";\n swipe: \"left\" | \"right\" | \"up\" | \"down\";\n handle: string;\n }\n>;\n\n/**\n * The size scale, indexed by the dimension the anchored edge turned it into.\n * Two maps rather than twelve rows on `SIDES`, because `left` and `right` want\n * the same widths and `top` and `bottom` the same heights — duplicating them\n * per edge is four places for a size to be adjusted in three of.\n *\n * Each is capped against the viewport, so a `lg` drawer on a laptop still\n * leaves the page it is anchored over visible behind it. That gap is the whole\n * difference between a drawer and a full-screen takeover.\n */\nconst EXTENT = {\n width: {\n sm: \"w-[min(90vw,20rem)]\",\n md: \"w-[min(92vw,26rem)]\",\n lg: \"w-[min(94vw,40rem)]\",\n },\n height: {\n sm: \"h-[min(90vh,16rem)]\",\n md: \"h-[min(92vh,24rem)]\",\n lg: \"h-[min(94vh,36rem)]\",\n },\n} satisfies Record<\"width\" | \"height\", Record<DrawerSize, string>>;\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, useId } from \"vue\";\nimport {\n DrawerRoot,\n DrawerTrigger,\n DrawerPortal,\n DrawerOverlay,\n DrawerContent,\n DrawerHandle,\n DrawerTitle,\n DrawerDescription,\n DrawerClose,\n} from \"reka-ui\";\nimport { X } from \"@lucide/vue\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { optional } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useLabels, type LabelOverrides } from \"@ecoma-io/loom-labels\";\n\n/**\n * Drawer — a panel anchored to an edge of the viewport that slides in over the\n * page: a filter rail tuned while a table stays visible behind it, a record's\n * detail opened from a row, a navigation sheet on a narrow screen.\n *\n * **Drawer against Dialog**, because this is the pair most often picked wrongly.\n * `Dialog` is a centred task surface for a decision or a short form: it\n * interrupts, it sits in the middle of the screen, and the page behind it is\n * context rather than something to look at. A Drawer is edge-anchored and\n * deliberately leaves the page visible along its open side, because the work\n * happens *alongside* that content — you tune a filter and watch the rows\n * change. If the user cannot proceed until they answer, that is a Dialog. If\n * they are reading or adjusting something about what is already on screen, it\n * is a Drawer.\n *\n * **It is still modal**, and that is a deliberate choice rather than an\n * oversight: focus is trapped in the panel, Tab never reaches the page behind,\n * everything outside is hidden from assistive technology, and the page does not\n * scroll. Visible is not the same as reachable — a panel you can Tab out of\n * silently strands a keyboard user behind a scrim they cannot see past. One\n * consequence to know about is in `docs/components/drawer.md`: locking the body\n * removes the scrollbar, and Reka compensates with a `padding-right` that\n * viewport-pinned furniture does not inherit.\n *\n * **The way out is never a gesture alone.** Esc closes, the close control in the\n * corner closes, and either one returns focus to whatever opened it. The drag\n * handle is a pointer affordance sitting on top of those, never instead of\n * them — it does not exist for a keyboard, and on a desktop pointer it is a\n * hint rather than a route.\n *\n * `title` is required for the same reason it is on `Dialog`: it is the panel's\n * accessible name, and a drawer without one announces itself as a dialog that\n * says nothing about what it holds.\n *\n * Reach for `Dialog` when the answer must come before anything else happens,\n * `Popover` when the content is small, non-blocking and belongs next to its\n * trigger, and a transient notification when the user need not act at all.\n */\nconst props = withDefaults(\n defineProps<{\n /** Drive the drawer from the host with `v-model:open`; omit it and the drawer owns its own state. */\n open?: boolean | undefined;\n /** The accessible name, and the panel's heading. Name what is in it (\"Filters\", \"Scene 12\"), not the verb that opened it. */\n title: string;\n /** Wired as the drawer's accessible description — one line on what the panel is for, announced with the title. */\n description?: string;\n /** Which edge the panel is anchored to. `right` for detail, `left` for navigation, `bottom` for a sheet. */\n side?: DrawerSide;\n /** How far the panel reaches along its own axis — width on a side drawer, height on a sheet. */\n size?: DrawerSize;\n /** Whether a press outside the panel dismisses it, and whether the drag handle is offered. Esc and the close control work either way. */\n dismissible?: boolean;\n /**\n * The name on the close control, as any subset of `DrawerLabels` — what it\n * leaves out stays as the host's `provideLoomLabels` vocabulary left it,\n * and then as Loom's English.\n *\n * A whole application's language belongs in that vocabulary rather than\n * here. This is the per-instance correction, for the drawer whose close\n * means something more specific than \"Close\" — \"Close filters\", say, on a\n * page carrying two of them.\n */\n labels?: LabelOverrides<DrawerLabels>;\n }>(),\n // `open: undefined` is load-bearing, not a redundant default — it is what\n // keeps the uncontrolled third state reachable past Vue's absent-Boolean\n // casting. `optional()` in the template is the other half; its docblock\n // carries the reasoning for both. `description` needs no such entry: a\n // string prop is already `undefined` when absent.\n { open: undefined, side: \"right\", size: \"md\", dismissible: true },\n);\n\ndefineEmits<{\n /** The drawer opened or closed, for `v-model:open`. The host stays the source of truth — the drawer reports the request and waits. */\n \"update:open\": [value: boolean];\n}>();\n\n// `text`, not `labels`: the prop of that name is one of the three sources this\n// resolves, and a template reading the raw prop would be reading the overrides\n// rather than the answer.\nconst text = useLabels(\"drawer\", DRAWER_LABELS, () => props.labels);\n\nconst anchor = computed(() => SIDES[props.side]);\n\n// `DrawerRoot` renders no DOM node at all, so fallthrough attributes would be\n// dropped on the floor rather than landing anywhere. They go to the panel,\n// which is the element they actually describe — a `data-testid` or an\n// `aria-*` on `<Drawer>` reads as being about the drawer, not about the button\n// that happens to open it.\n//\n// `class` is pulled out of that spread and merged through `cn()` instead:\n// spreading it alongside the panel's own `:class` only concatenates the two\n// lists — Vue's merge is generic, not Tailwind-aware — so a caller's\n// `w-[30rem]` would lose to the size scale's own width depending on which\n// utility Tailwind happened to emit last.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: panelAttrs } = useSplitAttrs();\n\n/**\n * The `dismissible: false` half. Reka reads a prevented `interactOutside` as\n * \"this layer declines to be dismissed\", which covers the pointer press and the\n * focus that follows it in one place. Esc is deliberately untouched: a panel\n * with no keyboard exit is a trap, and \"do not close this by accident\" is a\n * different request from \"do not let this be closed\".\n */\nfunction guardOutsidePress(event: Event): void {\n if (!props.dismissible) event.preventDefault();\n}\n\n// The `dismissible: false` other half — the swipe. Reka wires\n// `useSwipeDismiss` unconditionally inside `DrawerContentImpl` with\n// `enabled: open` and exposes no prop to turn it off, so a drag on the panel\n// body toward the anchored edge used to dismiss a drawer whose `dismissible`\n// was `false` — defeating exactly the case the prop exists for, on touch,\n// where the reader is least likely to have meant it.\n//\n// There is no event Reka emits for the gesture that a handler could veto\n// from outside; the only lever is stopping its pointer handlers before they\n// start a swipe. Reka listens on the panel in the bubble phase, so a\n// capture-phase listener on `document` runs first for every event that\n// reaches the panel, and `stopImmediatePropagation` clears the rest of the\n// path — Reka's listener included — while leaving click, focus, scroll and\n// selection alone, which are separate events.\n//\n// Two costs are accepted rather than designed around. A consumer's own\n// pointerdown handler inside a non-dismissible drawer is stopped too, which\n// is the same neighbourhood as `interact-outside` vetoing their outside\n// press: `dismissible: false` declines pointer gestures, full stop. And a\n// non-dismissible drawer nested inside another non-dismissible one vetoes\n// gestures for both, which is the direction the pointer would have to travel\n// anyway.\n//\n// Pointer and touch are two paths in Reka (`onPointerDown` returns early for\n// touch pointers, which arrive via `onTouchStart`), so both events are\n// watched. The `data-loom-drawer-panel` marker scopes the veto to this\n// drawer's panel — and to nobody else's. That last half is the part that\n// bites: the marker is on every panel, so a naive `closest` check makes one\n// non-dismissible drawer on a page veto the gestures of every drawer on it,\n// dismissing nothing but the sibling's ability to be swiped. The marker\n// therefore carries this instance's own id, and the veto demands a match on\n// the value, not just the attribute. The attribute itself still has to\n// survive the panel being portalled away from the drawer's own subtree,\n// which an element-membership check would not.\nconst PANEL_MARKER = \"data-loom-drawer-panel\";\n// `useId` like the rest of the library: stable across server and client, so\n// the marker value a listener armed during hydration checks is the value the\n// portalled panel renders with.\nconst panelMarkerId = useId();\n\nfunction vetoSwipeStart(event: PointerEvent | TouchEvent): void {\n if (props.dismissible) return;\n const target = event.target;\n if (!(target instanceof Element)) return;\n const panel = target.closest(`[${PANEL_MARKER}]`);\n if (!(panel instanceof Element) || panel.getAttribute(PANEL_MARKER) !== panelMarkerId) return;\n event.stopImmediatePropagation();\n}\n\n// The listeners are on `document`, not the panel, because the panel's whole\n// lifecycle is Reka's — the portal content mounts with the drawer and is\n// removed with it, so a listener attached to the panel would need re-arming\n// on every open. A document listener is armed once for the component's own\n// lifetime, and `onBeforeUnmount` is the pairing that keeps it from leaking\n// into the next drawer or the host's page.\nonMounted(() => {\n document.addEventListener(\"pointerdown\", vetoSwipeStart, { capture: true });\n document.addEventListener(\"touchstart\", vetoSwipeStart, { capture: true, passive: true });\n});\nonBeforeUnmount(() => {\n document.removeEventListener(\"pointerdown\", vetoSwipeStart, { capture: true });\n document.removeEventListener(\"touchstart\", vetoSwipeStart, { capture: true });\n});\n</script>\n\n<template>\n <DrawerRoot\n v-bind=\"optional({ open })\"\n :swipe-direction=\"anchor.swipe\"\n @update:open=\"$emit('update:open', $event)\"\n >\n <!-- @slot The control that opens the drawer. Optional — omit it and drive\n the drawer entirely from the host with `v-model:open`. -->\n <DrawerTrigger v-if=\"$slots.trigger\" as-child>\n <slot name=\"trigger\" />\n </DrawerTrigger>\n\n <DrawerPortal>\n <!-- `scrim-light` rather than Dialog's `scrim`, and the token now carries\n the reason: a drawer's page is still the thing being worked on. It\n fades rather than moves, so nothing behind the panel appears to\n shift.\n\n Both states are scoped, never unconditional. Reka's Presence keeps\n closed content mounted until an animationend arrives, and a\n mount-only animation never fires a second one — which here would\n strand a full-screen, input-blocking scrim over a page with no drawer\n on it. The paired exit is what makes the close a timed fade instead\n of a cut, and it keeps that guarantee: `fade-out` ends, so Presence\n unmounts on its own animationend rather than on the absence of one. -->\n <DrawerOverlay\n class=\"fixed inset-0 z-50 bg-foreground/scrim-light data-[state=open]:animate-fade data-[state=closed]:animate-fade-out\"\n />\n\n <DrawerContent\n v-bind=\"panelAttrs\"\n :data-loom-drawer-panel=\"panelMarkerId\"\n :class=\"\n cn(\n 'fixed z-50 flex flex-col border-border bg-popover text-popover-foreground shadow-lg outline-none',\n anchor.edge,\n anchor.corners,\n EXTENT[anchor.extent][size],\n // `slow`, and this is the one place this component reaches past the\n // `--duration-normal` feedback ceiling. The motion foundations page\n // names a panel taking over the screen as exactly what the\n // unhurried lane is for, and a full-height panel crossing the\n // viewport at 200ms reads as a flinch rather than a slide.\n 'transition-transform duration-slow ease-out',\n anchor.slide,\n // The other half of the same gesture. `SIDES` carries why the two\n // directions are a transition and a keyframe rather than one\n // mechanism run both ways; the short version is that Presence\n // watches `animationend` and nothing else, so a transition on the\n // way out never gets a frame to run in.\n anchor.exit,\n // Reka's swipe-to-dismiss writes the live drag onto the panel as\n // CSS custom properties and leaves the rendering to us. Applying\n // them as `transform` keeps them off `translate`, which the slide\n // above owns — the two properties compose instead of overwriting\n // each other, so a half-finished swipe is not fighting the entry\n // transition. The `0px` fallbacks cover the browsers where\n // `CSS.registerProperty` is unavailable and the vars have no\n // registered initial value.\n '[transform:translate(var(--drawer-swipe-movement-x,0px),var(--drawer-swipe-movement-y,0px))]',\n // While a finger is down the panel must track it exactly; a 320ms\n // ease on every pointermove is a panel lagging behind the hand.\n 'data-[swiping]:transition-none',\n attrs.class as string,\n )\n \"\n @interact-outside=\"guardOutsidePress\"\n >\n <!-- Reka renders the handle `aria-hidden` and unfocusable, which is\n correct and is why it can never become the only exit: it is not on\n the keyboard's path at all. It is drawn only where it means\n something — with `dismissible` off there is nothing to drag towards. -->\n <DrawerHandle\n v-if=\"dismissible\"\n :class=\"cn('absolute rounded-full bg-border-strong', anchor.handle)\"\n />\n\n <!-- The header is `shrink-0` and lives outside the scrolling region on\n purpose: a drawer taller than its content's needs scrolls its body\n while the title — the panel's accessible name and the only thing\n saying what is on screen — stays put. `pr-12` keeps a long title\n clear of the close control. -->\n <div class=\"shrink-0 px-6 pb-4 pr-12 pt-6\">\n <DrawerTitle class=\"text-base font-semibold\">{{ title }}</DrawerTitle>\n <DrawerDescription v-if=\"description\" class=\"mt-1 text-sm text-muted-foreground\">\n {{ description }}\n </DrawerDescription>\n </div>\n\n <!-- `min-h-0` is what makes `flex-1` able to shrink below its content's\n height; without it a flex item's automatic minimum size wins and the\n body grows the panel past the viewport instead of scrolling.\n `overscroll-contain` stops a scroll that reaches the end of this\n region from chaining out to the locked page behind. -->\n <div class=\"min-h-0 flex-1 overflow-y-auto overscroll-contain px-6 pb-6\">\n <!-- @slot The drawer body. -->\n <slot />\n </div>\n\n <!-- @slot The action row, aligned to the trailing edge. Pinned below the\n scrolling body so its actions stay reachable in a long panel. -->\n <div\n v-if=\"$slots.footer\"\n class=\"flex shrink-0 items-center justify-end gap-2 border-t border-border px-6 py-4\"\n >\n <slot name=\"footer\" />\n </div>\n\n <!-- Unconditional, unlike Dialog's `closable`. A dialog can drop its\n corner button because the footer already carries an explicit\n decision; a drawer often has no footer at all, and one of its two\n keyboard-independent exits must always be visible. -->\n <DrawerClose\n :aria-label=\"text.close\"\n class=\"absolute right-4 top-4 inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors duration-fast ease-out hover:bg-subtle hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\"\n >\n <X class=\"h-4 w-4\" />\n </DrawerClose>\n </DrawerContent>\n </DrawerPortal>\n </DrawerRoot>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Drawer.js","names":[],"sources":["../../../../../packages/primitives/drawer/src/Drawer.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { DrawerLabels } from \"@ecoma-io/loom-labels\";\n/**\n\n/**\n * Loom's English, co-located with the component so it tree-shakes with it, and\n * exported so a host can build a partial vocabulary against the real thing.\n */\nexport const DRAWER_LABELS: DrawerLabels = {\n close: \"Close\",\n};\n\n/**\n * Which edge of the viewport the panel is anchored to. `right` is the default\n * because a drawer is most often opened *from* a row or a toolbar to show more\n * about what is already on screen, and left-to-right readers expect that\n * detail to arrive on the trailing edge. `left` is where navigation lives,\n * `bottom` is the sheet a narrow screen gets, and `top` is the rare one — a\n * command surface dropping in over the header.\n */\nexport type DrawerSide = \"left\" | \"right\" | \"top\" | \"bottom\";\n\n/**\n * How far the panel reaches along its own axis — width for a `left`/`right`\n * drawer, height for a `top`/`bottom` one. Like Dialog's width, this is the\n * primitive's decision rather than a class a caller passes, so two drawers\n * opened from two screens cannot be two different sizes: `sm` for a filter rail\n * or a navigation sheet, `md` for a record's detail, `lg` for a working surface\n * with a table or a multi-section form in it, `xl` for an authoring-scale\n * surface pinned to the edge.\n *\n * The union is the same one Dialog's `size` takes, so a host parametrising\n * both surfaces can pass one size through. The ends match exactly: `xl` is\n * the dialog's own width pair, adopted rather than reinvented. `sm`, `md`\n * and `lg` keep the extents they have always had here, which are shallower\n * than the dialog's middle — an edge-anchored panel deliberately leaves the\n * page visible beside it, so it earns less of the axis than a centred one.\n */\nexport type DrawerSize = \"sm\" | \"md\" | \"lg\" | \"xl\";\n\n/**\n * One row per edge, because `side` is not one decision but four made together —\n * which edge the panel is pinned to, which way it slides in from, which\n * dimension `size` then means, and which pair of corners is rounded — plus the\n * direction a dismissing swipe has to travel. Split across a `cva` map and\n * three lookups they drift, and the drift is invisible: a panel pinned to the\n * left that slides in from the right still renders, still passes its own tests,\n * and looks like a rendering bug nobody can name. `satisfies` is the other half\n * — adding a member to `DrawerSide` and not to this table stops compiling.\n *\n * `slide` and `exit` are the same motion in two directions and deliberately not\n * the same mechanism, which is worth reading before either is \"unified\".\n *\n * `slide` is a `starting:` variant rather than a keyframe, and it is a\n * transform rather than an inset or a width. A full-height panel animated on\n * `width`/`right` relayouts the document on every frame; `translate` runs on\n * the compositor. `@starting-style` supplies the before-value for the entry\n * transition at the moment Reka's `Presence` inserts the node, which is the one\n * thing a plain class cannot do. It also composes with the live swipe below:\n * the gesture writes `transform` while the entry owns `translate`, and a\n * keyframe on `transform` would overwrite a drag mid-finger.\n *\n * `exit` has to be a keyframe, for the opposite reason. `Presence` decides how\n * long to keep a closing panel mounted by reading `animation-name` and waiting\n * for `animationend` — it never observes `transitionend`. So a closed panel\n * carrying only a transition is removed from the DOM in the same frame and the\n * transition never runs, which is exactly what a drawer did until these tokens\n * existed: a 320ms slide in, answered by a hard cut. The keyframes animate\n * `translate` rather than `transform` for the same composition reason the entry\n * does, so a panel released mid-swipe leaves from where the finger left it.\n *\n * `handle` positions the grab affordance on the panel's *inner* edge, running\n * along it: a vertical pill on a side drawer, a horizontal one on a sheet.\n */\nconst SIDES = {\n left: {\n edge: \"inset-y-0 left-0 border-r\",\n corners: \"rounded-r-lg\",\n slide: \"starting:-translate-x-full\",\n exit: \"data-[state=closed]:animate-slide-out-to-left\",\n extent: \"width\",\n swipe: \"left\",\n handle: \"right-1.5 top-1/2 h-10 w-1.5 -translate-y-1/2\",\n },\n right: {\n edge: \"inset-y-0 right-0 border-l\",\n corners: \"rounded-l-lg\",\n slide: \"starting:translate-x-full\",\n exit: \"data-[state=closed]:animate-slide-out-to-right\",\n extent: \"width\",\n swipe: \"right\",\n handle: \"left-1.5 top-1/2 h-10 w-1.5 -translate-y-1/2\",\n },\n top: {\n edge: \"inset-x-0 top-0 border-b\",\n corners: \"rounded-b-lg\",\n slide: \"starting:-translate-y-full\",\n exit: \"data-[state=closed]:animate-slide-out-to-top\",\n extent: \"height\",\n swipe: \"up\",\n handle: \"bottom-1.5 left-1/2 h-1.5 w-10 -translate-x-1/2\",\n },\n bottom: {\n edge: \"inset-x-0 bottom-0 border-t\",\n corners: \"rounded-t-lg\",\n slide: \"starting:translate-y-full\",\n exit: \"data-[state=closed]:animate-slide-out-to-bottom\",\n extent: \"height\",\n swipe: \"down\",\n handle: \"top-1.5 left-1/2 h-1.5 w-10 -translate-x-1/2\",\n },\n} satisfies Record<\n DrawerSide,\n {\n edge: string;\n corners: string;\n slide: string;\n exit: string;\n extent: \"width\" | \"height\";\n swipe: \"left\" | \"right\" | \"up\" | \"down\";\n handle: string;\n }\n>;\n\n/**\n * The size scale, indexed by the dimension the anchored edge turned it into.\n * Two maps rather than sixteen rows on `SIDES`, because `left` and `right` want\n * the same widths and `top` and `bottom` the same heights — duplicating them\n * per edge is four places for a size to be adjusted in three of.\n *\n * Each is capped against the viewport, so a `lg` drawer on a laptop still\n * leaves the page it is anchored over visible behind it. That gap is the whole\n * difference between a drawer and a full-screen takeover.\n *\n * `xl` on the width axis is Dialog's own top-end pair: the tier existed there\n * first, and sharing it byte-for-byte is what lets a host size-match the two\n * surfaces. The heights answer to their own ladder instead — every step down\n * this axis has been ×1.5 (16 → 24 → 36), so 54 continues it rather than\n * borrowing a width number.\n */\nconst EXTENT = {\n width: {\n sm: \"w-[min(90vw,20rem)]\",\n md: \"w-[min(92vw,26rem)]\",\n lg: \"w-[min(94vw,40rem)]\",\n xl: \"w-[min(94vw,64rem)]\",\n },\n height: {\n sm: \"h-[min(90vh,16rem)]\",\n md: \"h-[min(92vh,24rem)]\",\n lg: \"h-[min(94vh,36rem)]\",\n xl: \"h-[min(94vh,54rem)]\",\n },\n} satisfies Record<\"width\" | \"height\", Record<DrawerSize, string>>;\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, useId } from \"vue\";\nimport {\n DrawerRoot,\n DrawerTrigger,\n DrawerPortal,\n DrawerOverlay,\n DrawerContent,\n DrawerHandle,\n DrawerTitle,\n DrawerDescription,\n DrawerClose,\n} from \"reka-ui\";\nimport { X } from \"@lucide/vue\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { optional } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useLabels, type LabelOverrides } from \"@ecoma-io/loom-labels\";\n\n/**\n * Drawer — a panel anchored to an edge of the viewport that slides in over the\n * page: a filter rail tuned while a table stays visible behind it, a record's\n * detail opened from a row, a navigation sheet on a narrow screen.\n *\n * **Drawer against Dialog**, because this is the pair most often picked wrongly.\n * `Dialog` is a centred task surface for a decision or a short form: it\n * interrupts, it sits in the middle of the screen, and the page behind it is\n * context rather than something to look at. A Drawer is edge-anchored and\n * deliberately leaves the page visible along its open side, because the work\n * happens *alongside* that content — you tune a filter and watch the rows\n * change. If the user cannot proceed until they answer, that is a Dialog. If\n * they are reading or adjusting something about what is already on screen, it\n * is a Drawer.\n *\n * **It is still modal**, and that is a deliberate choice rather than an\n * oversight: focus is trapped in the panel, Tab never reaches the page behind,\n * everything outside is hidden from assistive technology, and the page does not\n * scroll. Visible is not the same as reachable — a panel you can Tab out of\n * silently strands a keyboard user behind a scrim they cannot see past. One\n * consequence to know about is in `docs/components/drawer.md`: locking the body\n * removes the scrollbar, and Reka compensates with a `padding-right` that\n * viewport-pinned furniture does not inherit.\n *\n * **The way out is never a gesture alone.** Esc closes, the close control in the\n * corner closes, and either one returns focus to whatever opened it. The drag\n * handle is a pointer affordance sitting on top of those, never instead of\n * them — it does not exist for a keyboard, and on a desktop pointer it is a\n * hint rather than a route.\n *\n * `title` is required for the same reason it is on `Dialog`: it is the panel's\n * accessible name, and a drawer without one announces itself as a dialog that\n * says nothing about what it holds.\n *\n * Reach for `Dialog` when the answer must come before anything else happens,\n * `Popover` when the content is small, non-blocking and belongs next to its\n * trigger, and a transient notification when the user need not act at all.\n */\nconst props = withDefaults(\n defineProps<{\n /** Drive the drawer from the host with `v-model:open`; omit it and the drawer owns its own state. */\n open?: boolean | undefined;\n /** The accessible name, and the panel's heading. Name what is in it (\"Filters\", \"Scene 12\"), not the verb that opened it. */\n title: string;\n /** Wired as the drawer's accessible description — one line on what the panel is for, announced with the title. */\n description?: string;\n /** Which edge the panel is anchored to. `right` for detail, `left` for navigation, `bottom` for a sheet. */\n side?: DrawerSide;\n /** How far the panel reaches along its own axis — width on a side drawer, height on a sheet. */\n size?: DrawerSize;\n /** Whether a press outside the panel dismisses it, and whether the drag handle is offered. Esc and the close control work either way. */\n dismissible?: boolean;\n /**\n * The name on the close control, as any subset of `DrawerLabels` — what it\n * leaves out stays as the host's `provideLoomLabels` vocabulary left it,\n * and then as Loom's English.\n *\n * A whole application's language belongs in that vocabulary rather than\n * here. This is the per-instance correction, for the drawer whose close\n * means something more specific than \"Close\" — \"Close filters\", say, on a\n * page carrying two of them.\n */\n labels?: LabelOverrides<DrawerLabels>;\n }>(),\n // `open: undefined` is load-bearing, not a redundant default — it is what\n // keeps the uncontrolled third state reachable past Vue's absent-Boolean\n // casting. `optional()` in the template is the other half; its docblock\n // carries the reasoning for both. `description` needs no such entry: a\n // string prop is already `undefined` when absent.\n { open: undefined, side: \"right\", size: \"md\", dismissible: true },\n);\n\ndefineEmits<{\n /** The drawer opened or closed, for `v-model:open`. The host stays the source of truth — the drawer reports the request and waits. */\n \"update:open\": [value: boolean];\n}>();\n\n// `text`, not `labels`: the prop of that name is one of the three sources this\n// resolves, and a template reading the raw prop would be reading the overrides\n// rather than the answer.\nconst text = useLabels(\"drawer\", DRAWER_LABELS, () => props.labels);\n\nconst anchor = computed(() => SIDES[props.side]);\n\n// `DrawerRoot` renders no DOM node at all, so fallthrough attributes would be\n// dropped on the floor rather than landing anywhere. They go to the panel,\n// which is the element they actually describe — a `data-testid` or an\n// `aria-*` on `<Drawer>` reads as being about the drawer, not about the button\n// that happens to open it.\n//\n// `class` is pulled out of that spread and merged through `cn()` instead:\n// spreading it alongside the panel's own `:class` only concatenates the two\n// lists — Vue's merge is generic, not Tailwind-aware — so a caller's\n// `w-[30rem]` would lose to the size scale's own width depending on which\n// utility Tailwind happened to emit last.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: panelAttrs } = useSplitAttrs();\n\n/**\n * The `dismissible: false` half. Reka reads a prevented `interactOutside` as\n * \"this layer declines to be dismissed\", which covers the pointer press and the\n * focus that follows it in one place. Esc is deliberately untouched: a panel\n * with no keyboard exit is a trap, and \"do not close this by accident\" is a\n * different request from \"do not let this be closed\".\n */\nfunction guardOutsidePress(event: Event): void {\n if (!props.dismissible) event.preventDefault();\n}\n\n// The `dismissible: false` other half — the swipe. Reka wires\n// `useSwipeDismiss` unconditionally inside `DrawerContentImpl` with\n// `enabled: open` and exposes no prop to turn it off, so a drag on the panel\n// body toward the anchored edge used to dismiss a drawer whose `dismissible`\n// was `false` — defeating exactly the case the prop exists for, on touch,\n// where the reader is least likely to have meant it.\n//\n// There is no event Reka emits for the gesture that a handler could veto\n// from outside; the only lever is stopping its pointer handlers before they\n// start a swipe. Reka listens on the panel in the bubble phase, so a\n// capture-phase listener on `document` runs first for every event that\n// reaches the panel, and `stopImmediatePropagation` clears the rest of the\n// path — Reka's listener included — while leaving click, focus, scroll and\n// selection alone, which are separate events.\n//\n// Two costs are accepted rather than designed around. A consumer's own\n// pointerdown handler inside a non-dismissible drawer is stopped too, which\n// is the same neighbourhood as `interact-outside` vetoing their outside\n// press: `dismissible: false` declines pointer gestures, full stop. And a\n// non-dismissible drawer nested inside another non-dismissible one vetoes\n// gestures for both, which is the direction the pointer would have to travel\n// anyway.\n//\n// Pointer and touch are two paths in Reka (`onPointerDown` returns early for\n// touch pointers, which arrive via `onTouchStart`), so both events are\n// watched. The `data-loom-drawer-panel` marker scopes the veto to this\n// drawer's panel — and to nobody else's. That last half is the part that\n// bites: the marker is on every panel, so a naive `closest` check makes one\n// non-dismissible drawer on a page veto the gestures of every drawer on it,\n// dismissing nothing but the sibling's ability to be swiped. The marker\n// therefore carries this instance's own id, and the veto demands a match on\n// the value, not just the attribute. The attribute itself still has to\n// survive the panel being portalled away from the drawer's own subtree,\n// which an element-membership check would not.\nconst PANEL_MARKER = \"data-loom-drawer-panel\";\n// `useId` like the rest of the library: stable across server and client, so\n// the marker value a listener armed during hydration checks is the value the\n// portalled panel renders with.\nconst panelMarkerId = useId();\n\nfunction vetoSwipeStart(event: PointerEvent | TouchEvent): void {\n if (props.dismissible) return;\n const target = event.target;\n if (!(target instanceof Element)) return;\n const panel = target.closest(`[${PANEL_MARKER}]`);\n if (!(panel instanceof Element) || panel.getAttribute(PANEL_MARKER) !== panelMarkerId) return;\n event.stopImmediatePropagation();\n}\n\n// The listeners are on `document`, not the panel, because the panel's whole\n// lifecycle is Reka's — the portal content mounts with the drawer and is\n// removed with it, so a listener attached to the panel would need re-arming\n// on every open. A document listener is armed once for the component's own\n// lifetime, and `onBeforeUnmount` is the pairing that keeps it from leaking\n// into the next drawer or the host's page.\nonMounted(() => {\n document.addEventListener(\"pointerdown\", vetoSwipeStart, { capture: true });\n document.addEventListener(\"touchstart\", vetoSwipeStart, { capture: true, passive: true });\n});\nonBeforeUnmount(() => {\n document.removeEventListener(\"pointerdown\", vetoSwipeStart, { capture: true });\n document.removeEventListener(\"touchstart\", vetoSwipeStart, { capture: true });\n});\n</script>\n\n<template>\n <DrawerRoot\n v-bind=\"optional({ open })\"\n :swipe-direction=\"anchor.swipe\"\n @update:open=\"$emit('update:open', $event)\"\n >\n <!-- @slot The control that opens the drawer. Optional — omit it and drive\n the drawer entirely from the host with `v-model:open`. -->\n <DrawerTrigger v-if=\"$slots.trigger\" as-child>\n <slot name=\"trigger\" />\n </DrawerTrigger>\n\n <DrawerPortal>\n <!-- `scrim-light` rather than Dialog's `scrim`, and the token now carries\n the reason: a drawer's page is still the thing being worked on. It\n fades rather than moves, so nothing behind the panel appears to\n shift.\n\n Both states are scoped, never unconditional. Reka's Presence keeps\n closed content mounted until an animationend arrives, and a\n mount-only animation never fires a second one — which here would\n strand a full-screen, input-blocking scrim over a page with no drawer\n on it. The paired exit is what makes the close a timed fade instead\n of a cut, and it keeps that guarantee: `fade-out` ends, so Presence\n unmounts on its own animationend rather than on the absence of one. -->\n <DrawerOverlay\n class=\"fixed inset-0 z-overlay bg-foreground/scrim-light data-[state=open]:animate-fade data-[state=closed]:animate-fade-out\"\n />\n\n <DrawerContent\n v-bind=\"panelAttrs\"\n :data-loom-drawer-panel=\"panelMarkerId\"\n :class=\"\n cn(\n 'fixed z-overlay flex flex-col border-border bg-popover text-popover-foreground shadow-lg outline-none',\n anchor.edge,\n anchor.corners,\n EXTENT[anchor.extent][size],\n // `slow`, and this is the one place this component reaches past the\n // `--duration-normal` feedback ceiling. The motion foundations page\n // names a panel taking over the screen as exactly what the\n // unhurried lane is for, and a full-height panel crossing the\n // viewport at 200ms reads as a flinch rather than a slide.\n 'transition-transform duration-slow ease-out',\n anchor.slide,\n // The other half of the same gesture. `SIDES` carries why the two\n // directions are a transition and a keyframe rather than one\n // mechanism run both ways; the short version is that Presence\n // watches `animationend` and nothing else, so a transition on the\n // way out never gets a frame to run in.\n anchor.exit,\n // Reka's swipe-to-dismiss writes the live drag onto the panel as\n // CSS custom properties and leaves the rendering to us. Applying\n // them as `transform` keeps them off `translate`, which the slide\n // above owns — the two properties compose instead of overwriting\n // each other, so a half-finished swipe is not fighting the entry\n // transition. The `0px` fallbacks cover the browsers where\n // `CSS.registerProperty` is unavailable and the vars have no\n // registered initial value.\n '[transform:translate(var(--drawer-swipe-movement-x,0px),var(--drawer-swipe-movement-y,0px))]',\n // While a finger is down the panel must track it exactly; a 320ms\n // ease on every pointermove is a panel lagging behind the hand.\n 'data-[swiping]:transition-none',\n attrs.class as string,\n )\n \"\n @interact-outside=\"guardOutsidePress\"\n >\n <!-- Reka renders the handle `aria-hidden` and unfocusable, which is\n correct and is why it can never become the only exit: it is not on\n the keyboard's path at all. It is drawn only where it means\n something — with `dismissible` off there is nothing to drag towards. -->\n <DrawerHandle\n v-if=\"dismissible\"\n :class=\"cn('absolute rounded-full bg-border-strong', anchor.handle)\"\n />\n\n <!-- The header is `shrink-0` and lives outside the scrolling region on\n purpose: a drawer taller than its content's needs scrolls its body\n while the title — the panel's accessible name and the only thing\n saying what is on screen — stays put. `pr-12` keeps a long title\n clear of the close control. -->\n <div class=\"shrink-0 px-6 pb-4 pr-12 pt-6\">\n <DrawerTitle class=\"text-base font-semibold\">{{ title }}</DrawerTitle>\n <DrawerDescription v-if=\"description\" class=\"mt-1 text-sm text-muted-foreground\">\n {{ description }}\n </DrawerDescription>\n </div>\n\n <!-- `min-h-0` is what makes `flex-1` able to shrink below its content's\n height; without it a flex item's automatic minimum size wins and the\n body grows the panel past the viewport instead of scrolling.\n `overscroll-contain` stops a scroll that reaches the end of this\n region from chaining out to the locked page behind. -->\n <div class=\"min-h-0 flex-1 overflow-y-auto overscroll-contain px-6 pb-6\">\n <!-- @slot The drawer body. -->\n <slot />\n </div>\n\n <!-- @slot The action row, aligned to the trailing edge. Pinned below the\n scrolling body so its actions stay reachable in a long panel. -->\n <div\n v-if=\"$slots.footer\"\n class=\"flex shrink-0 items-center justify-end gap-2 border-t border-border px-6 py-4\"\n >\n <slot name=\"footer\" />\n </div>\n\n <!-- Unconditional, unlike Dialog's `closable`. A dialog can drop its\n corner button because the footer already carries an explicit\n decision; a drawer often has no footer at all, and one of its two\n keyboard-independent exits must always be visible. -->\n <DrawerClose\n :aria-label=\"text.close\"\n class=\"absolute right-4 top-4 inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors duration-fast ease-out hover:bg-subtle hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\"\n >\n <X class=\"h-4 w-4\" />\n </DrawerClose>\n </DrawerContent>\n </DrawerPortal>\n </DrawerRoot>\n</template>\n"],"mappings":""}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type LabelOverrides } from "
|
|
2
|
-
import type { DrawerLabels } from "
|
|
1
|
+
import { type LabelOverrides } from "../../../labels/src/index";
|
|
2
|
+
import type { DrawerLabels } from "../../../labels/src/index";
|
|
3
3
|
/**
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -22,9 +22,17 @@ export type DrawerSide = "left" | "right" | "top" | "bottom";
|
|
|
22
22
|
* primitive's decision rather than a class a caller passes, so two drawers
|
|
23
23
|
* opened from two screens cannot be two different sizes: `sm` for a filter rail
|
|
24
24
|
* or a navigation sheet, `md` for a record's detail, `lg` for a working surface
|
|
25
|
-
* with a table or a multi-section form in it
|
|
25
|
+
* with a table or a multi-section form in it, `xl` for an authoring-scale
|
|
26
|
+
* surface pinned to the edge.
|
|
27
|
+
*
|
|
28
|
+
* The union is the same one Dialog's `size` takes, so a host parametrising
|
|
29
|
+
* both surfaces can pass one size through. The ends match exactly: `xl` is
|
|
30
|
+
* the dialog's own width pair, adopted rather than reinvented. `sm`, `md`
|
|
31
|
+
* and `lg` keep the extents they have always had here, which are shallower
|
|
32
|
+
* than the dialog's middle — an edge-anchored panel deliberately leaves the
|
|
33
|
+
* page visible beside it, so it earns less of the axis than a centred one.
|
|
26
34
|
*/
|
|
27
|
-
export type DrawerSize = "sm" | "md" | "lg";
|
|
35
|
+
export type DrawerSize = "sm" | "md" | "lg" | "xl";
|
|
28
36
|
declare const _default: typeof __VLS_export;
|
|
29
37
|
export default _default;
|
|
30
38
|
declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
@@ -95,24 +95,32 @@ var SIDES = {
|
|
|
95
95
|
};
|
|
96
96
|
/**
|
|
97
97
|
* The size scale, indexed by the dimension the anchored edge turned it into.
|
|
98
|
-
* Two maps rather than
|
|
98
|
+
* Two maps rather than sixteen rows on `SIDES`, because `left` and `right` want
|
|
99
99
|
* the same widths and `top` and `bottom` the same heights — duplicating them
|
|
100
100
|
* per edge is four places for a size to be adjusted in three of.
|
|
101
101
|
*
|
|
102
102
|
* Each is capped against the viewport, so a `lg` drawer on a laptop still
|
|
103
103
|
* leaves the page it is anchored over visible behind it. That gap is the whole
|
|
104
104
|
* difference between a drawer and a full-screen takeover.
|
|
105
|
+
*
|
|
106
|
+
* `xl` on the width axis is Dialog's own top-end pair: the tier existed there
|
|
107
|
+
* first, and sharing it byte-for-byte is what lets a host size-match the two
|
|
108
|
+
* surfaces. The heights answer to their own ladder instead — every step down
|
|
109
|
+
* this axis has been ×1.5 (16 → 24 → 36), so 54 continues it rather than
|
|
110
|
+
* borrowing a width number.
|
|
105
111
|
*/
|
|
106
112
|
var EXTENT = {
|
|
107
113
|
width: {
|
|
108
114
|
sm: "w-[min(90vw,20rem)]",
|
|
109
115
|
md: "w-[min(92vw,26rem)]",
|
|
110
|
-
lg: "w-[min(94vw,40rem)]"
|
|
116
|
+
lg: "w-[min(94vw,40rem)]",
|
|
117
|
+
xl: "w-[min(94vw,64rem)]"
|
|
111
118
|
},
|
|
112
119
|
height: {
|
|
113
120
|
sm: "h-[min(90vh,16rem)]",
|
|
114
121
|
md: "h-[min(92vh,24rem)]",
|
|
115
|
-
lg: "h-[min(94vh,36rem)]"
|
|
122
|
+
lg: "h-[min(94vh,36rem)]",
|
|
123
|
+
xl: "h-[min(94vh,54rem)]"
|
|
116
124
|
}
|
|
117
125
|
};
|
|
118
126
|
var Drawer_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
@@ -182,9 +190,9 @@ var Drawer_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCom
|
|
|
182
190
|
default: withCtx(() => [renderSlot(_ctx.$slots, "trigger")]),
|
|
183
191
|
_: 3
|
|
184
192
|
})) : createCommentVNode("", true), createVNode(unref(DrawerPortal), null, {
|
|
185
|
-
default: withCtx(() => [createVNode(unref(DrawerOverlay), { class: "fixed inset-0 z-
|
|
193
|
+
default: withCtx(() => [createVNode(unref(DrawerOverlay), { class: "fixed inset-0 z-overlay bg-foreground/scrim-light data-[state=open]:animate-fade data-[state=closed]:animate-fade-out" }), createVNode(unref(DrawerContent), mergeProps(unref(panelAttrs), {
|
|
186
194
|
"data-loom-drawer-panel": unref(panelMarkerId),
|
|
187
|
-
class: unref(cn)("fixed z-
|
|
195
|
+
class: unref(cn)("fixed z-overlay flex flex-col border-border bg-popover text-popover-foreground shadow-lg outline-none", anchor.value.edge, anchor.value.corners, EXTENT[anchor.value.extent][__props.size], "transition-transform duration-slow ease-out", anchor.value.slide, anchor.value.exit, "[transform:translate(var(--drawer-swipe-movement-x,0px),var(--drawer-swipe-movement-y,0px))]", "data-[swiping]:transition-none", unref(attrs).class),
|
|
188
196
|
onInteractOutside: guardOutsidePress
|
|
189
197
|
}), {
|
|
190
198
|
default: withCtx(() => [
|
package/dist/packages/primitives/drawer/src/Drawer.vue_vue_type_script_setup_true_lang.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Drawer.vue_vue_type_script_setup_true_lang.js","names":["$emit","$slots"],"sources":["../../../../../packages/primitives/drawer/src/Drawer.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { DrawerLabels } from \"@ecoma-io/loom-labels\";\n/**\n\n/**\n * Loom's English, co-located with the component so it tree-shakes with it, and\n * exported so a host can build a partial vocabulary against the real thing.\n */\nexport const DRAWER_LABELS: DrawerLabels = {\n close: \"Close\",\n};\n\n/**\n * Which edge of the viewport the panel is anchored to. `right` is the default\n * because a drawer is most often opened *from* a row or a toolbar to show more\n * about what is already on screen, and left-to-right readers expect that\n * detail to arrive on the trailing edge. `left` is where navigation lives,\n * `bottom` is the sheet a narrow screen gets, and `top` is the rare one — a\n * command surface dropping in over the header.\n */\nexport type DrawerSide = \"left\" | \"right\" | \"top\" | \"bottom\";\n\n/**\n * How far the panel reaches along its own axis — width for a `left`/`right`\n * drawer, height for a `top`/`bottom` one. Like Dialog's width, this is the\n * primitive's decision rather than a class a caller passes, so two drawers\n * opened from two screens cannot be two different sizes: `sm` for a filter rail\n * or a navigation sheet, `md` for a record's detail, `lg` for a working surface\n * with a table or a multi-section form in it.\n */\nexport type DrawerSize = \"sm\" | \"md\" | \"lg\";\n\n/**\n * One row per edge, because `side` is not one decision but four made together —\n * which edge the panel is pinned to, which way it slides in from, which\n * dimension `size` then means, and which pair of corners is rounded — plus the\n * direction a dismissing swipe has to travel. Split across a `cva` map and\n * three lookups they drift, and the drift is invisible: a panel pinned to the\n * left that slides in from the right still renders, still passes its own tests,\n * and looks like a rendering bug nobody can name. `satisfies` is the other half\n * — adding a member to `DrawerSide` and not to this table stops compiling.\n *\n * `slide` and `exit` are the same motion in two directions and deliberately not\n * the same mechanism, which is worth reading before either is \"unified\".\n *\n * `slide` is a `starting:` variant rather than a keyframe, and it is a\n * transform rather than an inset or a width. A full-height panel animated on\n * `width`/`right` relayouts the document on every frame; `translate` runs on\n * the compositor. `@starting-style` supplies the before-value for the entry\n * transition at the moment Reka's `Presence` inserts the node, which is the one\n * thing a plain class cannot do. It also composes with the live swipe below:\n * the gesture writes `transform` while the entry owns `translate`, and a\n * keyframe on `transform` would overwrite a drag mid-finger.\n *\n * `exit` has to be a keyframe, for the opposite reason. `Presence` decides how\n * long to keep a closing panel mounted by reading `animation-name` and waiting\n * for `animationend` — it never observes `transitionend`. So a closed panel\n * carrying only a transition is removed from the DOM in the same frame and the\n * transition never runs, which is exactly what a drawer did until these tokens\n * existed: a 320ms slide in, answered by a hard cut. The keyframes animate\n * `translate` rather than `transform` for the same composition reason the entry\n * does, so a panel released mid-swipe leaves from where the finger left it.\n *\n * `handle` positions the grab affordance on the panel's *inner* edge, running\n * along it: a vertical pill on a side drawer, a horizontal one on a sheet.\n */\nconst SIDES = {\n left: {\n edge: \"inset-y-0 left-0 border-r\",\n corners: \"rounded-r-lg\",\n slide: \"starting:-translate-x-full\",\n exit: \"data-[state=closed]:animate-slide-out-to-left\",\n extent: \"width\",\n swipe: \"left\",\n handle: \"right-1.5 top-1/2 h-10 w-1.5 -translate-y-1/2\",\n },\n right: {\n edge: \"inset-y-0 right-0 border-l\",\n corners: \"rounded-l-lg\",\n slide: \"starting:translate-x-full\",\n exit: \"data-[state=closed]:animate-slide-out-to-right\",\n extent: \"width\",\n swipe: \"right\",\n handle: \"left-1.5 top-1/2 h-10 w-1.5 -translate-y-1/2\",\n },\n top: {\n edge: \"inset-x-0 top-0 border-b\",\n corners: \"rounded-b-lg\",\n slide: \"starting:-translate-y-full\",\n exit: \"data-[state=closed]:animate-slide-out-to-top\",\n extent: \"height\",\n swipe: \"up\",\n handle: \"bottom-1.5 left-1/2 h-1.5 w-10 -translate-x-1/2\",\n },\n bottom: {\n edge: \"inset-x-0 bottom-0 border-t\",\n corners: \"rounded-t-lg\",\n slide: \"starting:translate-y-full\",\n exit: \"data-[state=closed]:animate-slide-out-to-bottom\",\n extent: \"height\",\n swipe: \"down\",\n handle: \"top-1.5 left-1/2 h-1.5 w-10 -translate-x-1/2\",\n },\n} satisfies Record<\n DrawerSide,\n {\n edge: string;\n corners: string;\n slide: string;\n exit: string;\n extent: \"width\" | \"height\";\n swipe: \"left\" | \"right\" | \"up\" | \"down\";\n handle: string;\n }\n>;\n\n/**\n * The size scale, indexed by the dimension the anchored edge turned it into.\n * Two maps rather than twelve rows on `SIDES`, because `left` and `right` want\n * the same widths and `top` and `bottom` the same heights — duplicating them\n * per edge is four places for a size to be adjusted in three of.\n *\n * Each is capped against the viewport, so a `lg` drawer on a laptop still\n * leaves the page it is anchored over visible behind it. That gap is the whole\n * difference between a drawer and a full-screen takeover.\n */\nconst EXTENT = {\n width: {\n sm: \"w-[min(90vw,20rem)]\",\n md: \"w-[min(92vw,26rem)]\",\n lg: \"w-[min(94vw,40rem)]\",\n },\n height: {\n sm: \"h-[min(90vh,16rem)]\",\n md: \"h-[min(92vh,24rem)]\",\n lg: \"h-[min(94vh,36rem)]\",\n },\n} satisfies Record<\"width\" | \"height\", Record<DrawerSize, string>>;\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, useId } from \"vue\";\nimport {\n DrawerRoot,\n DrawerTrigger,\n DrawerPortal,\n DrawerOverlay,\n DrawerContent,\n DrawerHandle,\n DrawerTitle,\n DrawerDescription,\n DrawerClose,\n} from \"reka-ui\";\nimport { X } from \"@lucide/vue\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { optional } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useLabels, type LabelOverrides } from \"@ecoma-io/loom-labels\";\n\n/**\n * Drawer — a panel anchored to an edge of the viewport that slides in over the\n * page: a filter rail tuned while a table stays visible behind it, a record's\n * detail opened from a row, a navigation sheet on a narrow screen.\n *\n * **Drawer against Dialog**, because this is the pair most often picked wrongly.\n * `Dialog` is a centred task surface for a decision or a short form: it\n * interrupts, it sits in the middle of the screen, and the page behind it is\n * context rather than something to look at. A Drawer is edge-anchored and\n * deliberately leaves the page visible along its open side, because the work\n * happens *alongside* that content — you tune a filter and watch the rows\n * change. If the user cannot proceed until they answer, that is a Dialog. If\n * they are reading or adjusting something about what is already on screen, it\n * is a Drawer.\n *\n * **It is still modal**, and that is a deliberate choice rather than an\n * oversight: focus is trapped in the panel, Tab never reaches the page behind,\n * everything outside is hidden from assistive technology, and the page does not\n * scroll. Visible is not the same as reachable — a panel you can Tab out of\n * silently strands a keyboard user behind a scrim they cannot see past. One\n * consequence to know about is in `docs/components/drawer.md`: locking the body\n * removes the scrollbar, and Reka compensates with a `padding-right` that\n * viewport-pinned furniture does not inherit.\n *\n * **The way out is never a gesture alone.** Esc closes, the close control in the\n * corner closes, and either one returns focus to whatever opened it. The drag\n * handle is a pointer affordance sitting on top of those, never instead of\n * them — it does not exist for a keyboard, and on a desktop pointer it is a\n * hint rather than a route.\n *\n * `title` is required for the same reason it is on `Dialog`: it is the panel's\n * accessible name, and a drawer without one announces itself as a dialog that\n * says nothing about what it holds.\n *\n * Reach for `Dialog` when the answer must come before anything else happens,\n * `Popover` when the content is small, non-blocking and belongs next to its\n * trigger, and a transient notification when the user need not act at all.\n */\nconst props = withDefaults(\n defineProps<{\n /** Drive the drawer from the host with `v-model:open`; omit it and the drawer owns its own state. */\n open?: boolean | undefined;\n /** The accessible name, and the panel's heading. Name what is in it (\"Filters\", \"Scene 12\"), not the verb that opened it. */\n title: string;\n /** Wired as the drawer's accessible description — one line on what the panel is for, announced with the title. */\n description?: string;\n /** Which edge the panel is anchored to. `right` for detail, `left` for navigation, `bottom` for a sheet. */\n side?: DrawerSide;\n /** How far the panel reaches along its own axis — width on a side drawer, height on a sheet. */\n size?: DrawerSize;\n /** Whether a press outside the panel dismisses it, and whether the drag handle is offered. Esc and the close control work either way. */\n dismissible?: boolean;\n /**\n * The name on the close control, as any subset of `DrawerLabels` — what it\n * leaves out stays as the host's `provideLoomLabels` vocabulary left it,\n * and then as Loom's English.\n *\n * A whole application's language belongs in that vocabulary rather than\n * here. This is the per-instance correction, for the drawer whose close\n * means something more specific than \"Close\" — \"Close filters\", say, on a\n * page carrying two of them.\n */\n labels?: LabelOverrides<DrawerLabels>;\n }>(),\n // `open: undefined` is load-bearing, not a redundant default — it is what\n // keeps the uncontrolled third state reachable past Vue's absent-Boolean\n // casting. `optional()` in the template is the other half; its docblock\n // carries the reasoning for both. `description` needs no such entry: a\n // string prop is already `undefined` when absent.\n { open: undefined, side: \"right\", size: \"md\", dismissible: true },\n);\n\ndefineEmits<{\n /** The drawer opened or closed, for `v-model:open`. The host stays the source of truth — the drawer reports the request and waits. */\n \"update:open\": [value: boolean];\n}>();\n\n// `text`, not `labels`: the prop of that name is one of the three sources this\n// resolves, and a template reading the raw prop would be reading the overrides\n// rather than the answer.\nconst text = useLabels(\"drawer\", DRAWER_LABELS, () => props.labels);\n\nconst anchor = computed(() => SIDES[props.side]);\n\n// `DrawerRoot` renders no DOM node at all, so fallthrough attributes would be\n// dropped on the floor rather than landing anywhere. They go to the panel,\n// which is the element they actually describe — a `data-testid` or an\n// `aria-*` on `<Drawer>` reads as being about the drawer, not about the button\n// that happens to open it.\n//\n// `class` is pulled out of that spread and merged through `cn()` instead:\n// spreading it alongside the panel's own `:class` only concatenates the two\n// lists — Vue's merge is generic, not Tailwind-aware — so a caller's\n// `w-[30rem]` would lose to the size scale's own width depending on which\n// utility Tailwind happened to emit last.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: panelAttrs } = useSplitAttrs();\n\n/**\n * The `dismissible: false` half. Reka reads a prevented `interactOutside` as\n * \"this layer declines to be dismissed\", which covers the pointer press and the\n * focus that follows it in one place. Esc is deliberately untouched: a panel\n * with no keyboard exit is a trap, and \"do not close this by accident\" is a\n * different request from \"do not let this be closed\".\n */\nfunction guardOutsidePress(event: Event): void {\n if (!props.dismissible) event.preventDefault();\n}\n\n// The `dismissible: false` other half — the swipe. Reka wires\n// `useSwipeDismiss` unconditionally inside `DrawerContentImpl` with\n// `enabled: open` and exposes no prop to turn it off, so a drag on the panel\n// body toward the anchored edge used to dismiss a drawer whose `dismissible`\n// was `false` — defeating exactly the case the prop exists for, on touch,\n// where the reader is least likely to have meant it.\n//\n// There is no event Reka emits for the gesture that a handler could veto\n// from outside; the only lever is stopping its pointer handlers before they\n// start a swipe. Reka listens on the panel in the bubble phase, so a\n// capture-phase listener on `document` runs first for every event that\n// reaches the panel, and `stopImmediatePropagation` clears the rest of the\n// path — Reka's listener included — while leaving click, focus, scroll and\n// selection alone, which are separate events.\n//\n// Two costs are accepted rather than designed around. A consumer's own\n// pointerdown handler inside a non-dismissible drawer is stopped too, which\n// is the same neighbourhood as `interact-outside` vetoing their outside\n// press: `dismissible: false` declines pointer gestures, full stop. And a\n// non-dismissible drawer nested inside another non-dismissible one vetoes\n// gestures for both, which is the direction the pointer would have to travel\n// anyway.\n//\n// Pointer and touch are two paths in Reka (`onPointerDown` returns early for\n// touch pointers, which arrive via `onTouchStart`), so both events are\n// watched. The `data-loom-drawer-panel` marker scopes the veto to this\n// drawer's panel — and to nobody else's. That last half is the part that\n// bites: the marker is on every panel, so a naive `closest` check makes one\n// non-dismissible drawer on a page veto the gestures of every drawer on it,\n// dismissing nothing but the sibling's ability to be swiped. The marker\n// therefore carries this instance's own id, and the veto demands a match on\n// the value, not just the attribute. The attribute itself still has to\n// survive the panel being portalled away from the drawer's own subtree,\n// which an element-membership check would not.\nconst PANEL_MARKER = \"data-loom-drawer-panel\";\n// `useId` like the rest of the library: stable across server and client, so\n// the marker value a listener armed during hydration checks is the value the\n// portalled panel renders with.\nconst panelMarkerId = useId();\n\nfunction vetoSwipeStart(event: PointerEvent | TouchEvent): void {\n if (props.dismissible) return;\n const target = event.target;\n if (!(target instanceof Element)) return;\n const panel = target.closest(`[${PANEL_MARKER}]`);\n if (!(panel instanceof Element) || panel.getAttribute(PANEL_MARKER) !== panelMarkerId) return;\n event.stopImmediatePropagation();\n}\n\n// The listeners are on `document`, not the panel, because the panel's whole\n// lifecycle is Reka's — the portal content mounts with the drawer and is\n// removed with it, so a listener attached to the panel would need re-arming\n// on every open. A document listener is armed once for the component's own\n// lifetime, and `onBeforeUnmount` is the pairing that keeps it from leaking\n// into the next drawer or the host's page.\nonMounted(() => {\n document.addEventListener(\"pointerdown\", vetoSwipeStart, { capture: true });\n document.addEventListener(\"touchstart\", vetoSwipeStart, { capture: true, passive: true });\n});\nonBeforeUnmount(() => {\n document.removeEventListener(\"pointerdown\", vetoSwipeStart, { capture: true });\n document.removeEventListener(\"touchstart\", vetoSwipeStart, { capture: true });\n});\n</script>\n\n<template>\n <DrawerRoot\n v-bind=\"optional({ open })\"\n :swipe-direction=\"anchor.swipe\"\n @update:open=\"$emit('update:open', $event)\"\n >\n <!-- @slot The control that opens the drawer. Optional — omit it and drive\n the drawer entirely from the host with `v-model:open`. -->\n <DrawerTrigger v-if=\"$slots.trigger\" as-child>\n <slot name=\"trigger\" />\n </DrawerTrigger>\n\n <DrawerPortal>\n <!-- `scrim-light` rather than Dialog's `scrim`, and the token now carries\n the reason: a drawer's page is still the thing being worked on. It\n fades rather than moves, so nothing behind the panel appears to\n shift.\n\n Both states are scoped, never unconditional. Reka's Presence keeps\n closed content mounted until an animationend arrives, and a\n mount-only animation never fires a second one — which here would\n strand a full-screen, input-blocking scrim over a page with no drawer\n on it. The paired exit is what makes the close a timed fade instead\n of a cut, and it keeps that guarantee: `fade-out` ends, so Presence\n unmounts on its own animationend rather than on the absence of one. -->\n <DrawerOverlay\n class=\"fixed inset-0 z-50 bg-foreground/scrim-light data-[state=open]:animate-fade data-[state=closed]:animate-fade-out\"\n />\n\n <DrawerContent\n v-bind=\"panelAttrs\"\n :data-loom-drawer-panel=\"panelMarkerId\"\n :class=\"\n cn(\n 'fixed z-50 flex flex-col border-border bg-popover text-popover-foreground shadow-lg outline-none',\n anchor.edge,\n anchor.corners,\n EXTENT[anchor.extent][size],\n // `slow`, and this is the one place this component reaches past the\n // `--duration-normal` feedback ceiling. The motion foundations page\n // names a panel taking over the screen as exactly what the\n // unhurried lane is for, and a full-height panel crossing the\n // viewport at 200ms reads as a flinch rather than a slide.\n 'transition-transform duration-slow ease-out',\n anchor.slide,\n // The other half of the same gesture. `SIDES` carries why the two\n // directions are a transition and a keyframe rather than one\n // mechanism run both ways; the short version is that Presence\n // watches `animationend` and nothing else, so a transition on the\n // way out never gets a frame to run in.\n anchor.exit,\n // Reka's swipe-to-dismiss writes the live drag onto the panel as\n // CSS custom properties and leaves the rendering to us. Applying\n // them as `transform` keeps them off `translate`, which the slide\n // above owns — the two properties compose instead of overwriting\n // each other, so a half-finished swipe is not fighting the entry\n // transition. The `0px` fallbacks cover the browsers where\n // `CSS.registerProperty` is unavailable and the vars have no\n // registered initial value.\n '[transform:translate(var(--drawer-swipe-movement-x,0px),var(--drawer-swipe-movement-y,0px))]',\n // While a finger is down the panel must track it exactly; a 320ms\n // ease on every pointermove is a panel lagging behind the hand.\n 'data-[swiping]:transition-none',\n attrs.class as string,\n )\n \"\n @interact-outside=\"guardOutsidePress\"\n >\n <!-- Reka renders the handle `aria-hidden` and unfocusable, which is\n correct and is why it can never become the only exit: it is not on\n the keyboard's path at all. It is drawn only where it means\n something — with `dismissible` off there is nothing to drag towards. -->\n <DrawerHandle\n v-if=\"dismissible\"\n :class=\"cn('absolute rounded-full bg-border-strong', anchor.handle)\"\n />\n\n <!-- The header is `shrink-0` and lives outside the scrolling region on\n purpose: a drawer taller than its content's needs scrolls its body\n while the title — the panel's accessible name and the only thing\n saying what is on screen — stays put. `pr-12` keeps a long title\n clear of the close control. -->\n <div class=\"shrink-0 px-6 pb-4 pr-12 pt-6\">\n <DrawerTitle class=\"text-base font-semibold\">{{ title }}</DrawerTitle>\n <DrawerDescription v-if=\"description\" class=\"mt-1 text-sm text-muted-foreground\">\n {{ description }}\n </DrawerDescription>\n </div>\n\n <!-- `min-h-0` is what makes `flex-1` able to shrink below its content's\n height; without it a flex item's automatic minimum size wins and the\n body grows the panel past the viewport instead of scrolling.\n `overscroll-contain` stops a scroll that reaches the end of this\n region from chaining out to the locked page behind. -->\n <div class=\"min-h-0 flex-1 overflow-y-auto overscroll-contain px-6 pb-6\">\n <!-- @slot The drawer body. -->\n <slot />\n </div>\n\n <!-- @slot The action row, aligned to the trailing edge. Pinned below the\n scrolling body so its actions stay reachable in a long panel. -->\n <div\n v-if=\"$slots.footer\"\n class=\"flex shrink-0 items-center justify-end gap-2 border-t border-border px-6 py-4\"\n >\n <slot name=\"footer\" />\n </div>\n\n <!-- Unconditional, unlike Dialog's `closable`. A dialog can drop its\n corner button because the footer already carries an explicit\n decision; a drawer often has no footer at all, and one of its two\n keyboard-independent exits must always be visible. -->\n <DrawerClose\n :aria-label=\"text.close\"\n class=\"absolute right-4 top-4 inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors duration-fast ease-out hover:bg-subtle hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\"\n >\n <X class=\"h-4 w-4\" />\n </DrawerClose>\n </DrawerContent>\n </DrawerPortal>\n </DrawerRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAQA,IAAa,gBAA8B,EACzC,OAAO,QACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDA,IAAM,QAAQ;CACZ,MAAM;EACJ,MAAM;EACN,SAAS;EACT,OAAO;EACP,MAAM;EACN,QAAQ;EACR,OAAO;EACP,QAAQ;CACV;CACA,OAAO;EACL,MAAM;EACN,SAAS;EACT,OAAO;EACP,MAAM;EACN,QAAQ;EACR,OAAO;EACP,QAAQ;CACV;CACA,KAAK;EACH,MAAM;EACN,SAAS;EACT,OAAO;EACP,MAAM;EACN,QAAQ;EACR,OAAO;EACP,QAAQ;CACV;CACA,QAAQ;EACN,MAAM;EACN,SAAS;EACT,OAAO;EACP,MAAM;EACN,QAAQ;EACR,OAAO;EACP,QAAQ;CACV;AACF;;;;;;;;;;;AAuBA,IAAM,SAAS;CACb,OAAO;EACL,IAAI;EACJ,IAAI;EACJ,IAAI;CACN;CACA,QAAQ;EACN,IAAI;EACJ,IAAI;EACJ,IAAI;CACN;AACF;;;;;;;;;;;;;;;;;;;;;EA4DA,MAAM,QAAQ;EA0Cd,MAAM,OAAO,UAAU,UAAU,qBAAqB,MAAM,MAAM;EAElE,MAAM,SAAS,eAAe,MAAM,MAAM,KAAK;EAc/C,MAAM,EAAE,OAAO,MAAM,eAAe,cAAc;;;;;;;;EASlD,SAAS,kBAAkB,OAAoB;GAC7C,IAAI,CAAC,MAAM,aAAa,MAAM,eAAe;EAC/C;EAoCA,MAAM,eAAe;EAIrB,MAAM,gBAAgB,MAAM;EAE5B,SAAS,eAAe,OAAwC;GAC9D,IAAI,MAAM,aAAa;GACvB,MAAM,SAAS,MAAM;GACrB,IAAI,EAAE,kBAAkB,UAAU;GAClC,MAAM,QAAQ,OAAO,QAAQ,IAAI,aAAa,EAAE;GAChD,IAAI,EAAE,iBAAiB,YAAY,MAAM,aAAa,YAAY,MAAM,eAAe;GACvF,MAAM,yBAAyB;EACjC;EAQA,gBAAgB;GACd,SAAS,iBAAiB,eAAe,gBAAgB,EAAE,SAAS,KAAK,CAAC;GAC1E,SAAS,iBAAiB,cAAc,gBAAgB;IAAE,SAAS;IAAM,SAAS;GAAK,CAAC;EAC1F,CAAC;EACD,sBAAsB;GACpB,SAAS,oBAAoB,eAAe,gBAAgB,EAAE,SAAS,KAAK,CAAC;GAC7E,SAAS,oBAAoB,cAAc,gBAAgB,EAAE,SAAS,KAAK,CAAC;EAC9E,CAAC;;GAIC,OAAA,UAAA,GAAA,YAuHa,MAAA,UAAA,GAvHb,WACU,MAsHG,QAAA,CAtHK,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAA;IACtB,mBAAiB,OAAA,MAAO;IACxB,iBAAW,OAAA,OAAA,OAAA,MAAA,WAAEA,KAAAA,MAAK,eAAgB,MAAM;;IAIzC,SAAA,cAEgB,CAFKC,KAAAA,OAAO,WAA5B,UAAA,GAAA,YAEgB,MAAA,aAAA,GAAA;;KAFqB,YAAA;;KACnC,SAAA,cAAuB,CAAvB,WAAuB,KAAA,QAAA,SAAA,CAAA,CAAA;;IAGzB,CAAA,KAAA,mBAAA,IAAA,IAAA,GAAA,YA2Ge,MAAA,YAAA,GAAA,MAAA;KA9Fb,SAAA,cAEE,CAFF,YAEE,MAAA,aAAA,GAAA,EADA,OAAM,mHAAkH,CAAA,GAG1H,YAyFgB,MAAA,aAAA,GAzFhB,WACU,MAwFM,UAAA,GAxFI;MACjB,0BAAwB,MAAA,aAAA;MACxB,OAAkB,MAAA,EAAA,CAAE,CAAA,oGAA8H,OAAA,MAAO,MAAkB,OAAA,MAAO,SAAqB,OAAO,OAAA,MAAO,OAAM,CAAE,QAAA,OAAA,+CAA2c,OAAA,MAAO,OAA2X,OAAA,MAAO,MAAA,gGAAA,kCAA04B,MAAA,KAAA,CAAK,CAAC,KAAA;MAkCj8D,mBAAkB;;MAMnB,SAAA,cAGE;OAFM,QAAA,eADR,UAAA,GAAA,YAGE,MAAA,YAAA,GAAA;;QADC,OAAK,eAAE,MAAA,EAAA,CAAE,CAAA,0CAA2C,OAAA,MAAO,MAAM,CAAA;;OAQpE,mBAKM,OALN,YAKM,CAJJ,YAAsE,MAAA,WAAA,GAAA,EAAzD,OAAM,0BAAyB,GAAA;QAAC,SAAA,cAAW,CAAR,gBAAA,gBAAA,QAAA,KAAK,GAAA,CAAA,CAAA,CAAA;;OAC5B,CAAA,GAAA,QAAA,eAAzB,UAAA,GAAA,YAEoB,MAAA,iBAAA,GAAA;;QAFkB,OAAM;;QAC1C,SAAA,cAAiB,CAAd,gBAAA,gBAAA,QAAA,WAAW,GAAA,CAAA,CAAA,CAAA;;;OASlB,mBAGM,OAHN,YAGM,CADJ,WAAQ,KAAA,QAAA,SAAA,CAAA,CAAA;OAMFA,KAAAA,OAAO,UADf,UAAA,GAAA,mBAKM,OALN,YAKM,CADJ,WAAsB,KAAA,QAAA,QAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;OAOxB,YAKc,MAAA,WAAA,GAAA;QAJX,cAAY,MAAA,IAAA,CAAI,CAAC;QAClB,OAAM;;QAEN,SAAA,cAAqB,CAArB,YAAqB,MAAA,CAAA,GAAA,EAAlB,OAAM,UAAS,CAAA,CAAA,CAAA"}
|
|
1
|
+
{"version":3,"file":"Drawer.vue_vue_type_script_setup_true_lang.js","names":["$emit","$slots"],"sources":["../../../../../packages/primitives/drawer/src/Drawer.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { DrawerLabels } from \"@ecoma-io/loom-labels\";\n/**\n\n/**\n * Loom's English, co-located with the component so it tree-shakes with it, and\n * exported so a host can build a partial vocabulary against the real thing.\n */\nexport const DRAWER_LABELS: DrawerLabels = {\n close: \"Close\",\n};\n\n/**\n * Which edge of the viewport the panel is anchored to. `right` is the default\n * because a drawer is most often opened *from* a row or a toolbar to show more\n * about what is already on screen, and left-to-right readers expect that\n * detail to arrive on the trailing edge. `left` is where navigation lives,\n * `bottom` is the sheet a narrow screen gets, and `top` is the rare one — a\n * command surface dropping in over the header.\n */\nexport type DrawerSide = \"left\" | \"right\" | \"top\" | \"bottom\";\n\n/**\n * How far the panel reaches along its own axis — width for a `left`/`right`\n * drawer, height for a `top`/`bottom` one. Like Dialog's width, this is the\n * primitive's decision rather than a class a caller passes, so two drawers\n * opened from two screens cannot be two different sizes: `sm` for a filter rail\n * or a navigation sheet, `md` for a record's detail, `lg` for a working surface\n * with a table or a multi-section form in it, `xl` for an authoring-scale\n * surface pinned to the edge.\n *\n * The union is the same one Dialog's `size` takes, so a host parametrising\n * both surfaces can pass one size through. The ends match exactly: `xl` is\n * the dialog's own width pair, adopted rather than reinvented. `sm`, `md`\n * and `lg` keep the extents they have always had here, which are shallower\n * than the dialog's middle — an edge-anchored panel deliberately leaves the\n * page visible beside it, so it earns less of the axis than a centred one.\n */\nexport type DrawerSize = \"sm\" | \"md\" | \"lg\" | \"xl\";\n\n/**\n * One row per edge, because `side` is not one decision but four made together —\n * which edge the panel is pinned to, which way it slides in from, which\n * dimension `size` then means, and which pair of corners is rounded — plus the\n * direction a dismissing swipe has to travel. Split across a `cva` map and\n * three lookups they drift, and the drift is invisible: a panel pinned to the\n * left that slides in from the right still renders, still passes its own tests,\n * and looks like a rendering bug nobody can name. `satisfies` is the other half\n * — adding a member to `DrawerSide` and not to this table stops compiling.\n *\n * `slide` and `exit` are the same motion in two directions and deliberately not\n * the same mechanism, which is worth reading before either is \"unified\".\n *\n * `slide` is a `starting:` variant rather than a keyframe, and it is a\n * transform rather than an inset or a width. A full-height panel animated on\n * `width`/`right` relayouts the document on every frame; `translate` runs on\n * the compositor. `@starting-style` supplies the before-value for the entry\n * transition at the moment Reka's `Presence` inserts the node, which is the one\n * thing a plain class cannot do. It also composes with the live swipe below:\n * the gesture writes `transform` while the entry owns `translate`, and a\n * keyframe on `transform` would overwrite a drag mid-finger.\n *\n * `exit` has to be a keyframe, for the opposite reason. `Presence` decides how\n * long to keep a closing panel mounted by reading `animation-name` and waiting\n * for `animationend` — it never observes `transitionend`. So a closed panel\n * carrying only a transition is removed from the DOM in the same frame and the\n * transition never runs, which is exactly what a drawer did until these tokens\n * existed: a 320ms slide in, answered by a hard cut. The keyframes animate\n * `translate` rather than `transform` for the same composition reason the entry\n * does, so a panel released mid-swipe leaves from where the finger left it.\n *\n * `handle` positions the grab affordance on the panel's *inner* edge, running\n * along it: a vertical pill on a side drawer, a horizontal one on a sheet.\n */\nconst SIDES = {\n left: {\n edge: \"inset-y-0 left-0 border-r\",\n corners: \"rounded-r-lg\",\n slide: \"starting:-translate-x-full\",\n exit: \"data-[state=closed]:animate-slide-out-to-left\",\n extent: \"width\",\n swipe: \"left\",\n handle: \"right-1.5 top-1/2 h-10 w-1.5 -translate-y-1/2\",\n },\n right: {\n edge: \"inset-y-0 right-0 border-l\",\n corners: \"rounded-l-lg\",\n slide: \"starting:translate-x-full\",\n exit: \"data-[state=closed]:animate-slide-out-to-right\",\n extent: \"width\",\n swipe: \"right\",\n handle: \"left-1.5 top-1/2 h-10 w-1.5 -translate-y-1/2\",\n },\n top: {\n edge: \"inset-x-0 top-0 border-b\",\n corners: \"rounded-b-lg\",\n slide: \"starting:-translate-y-full\",\n exit: \"data-[state=closed]:animate-slide-out-to-top\",\n extent: \"height\",\n swipe: \"up\",\n handle: \"bottom-1.5 left-1/2 h-1.5 w-10 -translate-x-1/2\",\n },\n bottom: {\n edge: \"inset-x-0 bottom-0 border-t\",\n corners: \"rounded-t-lg\",\n slide: \"starting:translate-y-full\",\n exit: \"data-[state=closed]:animate-slide-out-to-bottom\",\n extent: \"height\",\n swipe: \"down\",\n handle: \"top-1.5 left-1/2 h-1.5 w-10 -translate-x-1/2\",\n },\n} satisfies Record<\n DrawerSide,\n {\n edge: string;\n corners: string;\n slide: string;\n exit: string;\n extent: \"width\" | \"height\";\n swipe: \"left\" | \"right\" | \"up\" | \"down\";\n handle: string;\n }\n>;\n\n/**\n * The size scale, indexed by the dimension the anchored edge turned it into.\n * Two maps rather than sixteen rows on `SIDES`, because `left` and `right` want\n * the same widths and `top` and `bottom` the same heights — duplicating them\n * per edge is four places for a size to be adjusted in three of.\n *\n * Each is capped against the viewport, so a `lg` drawer on a laptop still\n * leaves the page it is anchored over visible behind it. That gap is the whole\n * difference between a drawer and a full-screen takeover.\n *\n * `xl` on the width axis is Dialog's own top-end pair: the tier existed there\n * first, and sharing it byte-for-byte is what lets a host size-match the two\n * surfaces. The heights answer to their own ladder instead — every step down\n * this axis has been ×1.5 (16 → 24 → 36), so 54 continues it rather than\n * borrowing a width number.\n */\nconst EXTENT = {\n width: {\n sm: \"w-[min(90vw,20rem)]\",\n md: \"w-[min(92vw,26rem)]\",\n lg: \"w-[min(94vw,40rem)]\",\n xl: \"w-[min(94vw,64rem)]\",\n },\n height: {\n sm: \"h-[min(90vh,16rem)]\",\n md: \"h-[min(92vh,24rem)]\",\n lg: \"h-[min(94vh,36rem)]\",\n xl: \"h-[min(94vh,54rem)]\",\n },\n} satisfies Record<\"width\" | \"height\", Record<DrawerSize, string>>;\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, useId } from \"vue\";\nimport {\n DrawerRoot,\n DrawerTrigger,\n DrawerPortal,\n DrawerOverlay,\n DrawerContent,\n DrawerHandle,\n DrawerTitle,\n DrawerDescription,\n DrawerClose,\n} from \"reka-ui\";\nimport { X } from \"@lucide/vue\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { optional } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useLabels, type LabelOverrides } from \"@ecoma-io/loom-labels\";\n\n/**\n * Drawer — a panel anchored to an edge of the viewport that slides in over the\n * page: a filter rail tuned while a table stays visible behind it, a record's\n * detail opened from a row, a navigation sheet on a narrow screen.\n *\n * **Drawer against Dialog**, because this is the pair most often picked wrongly.\n * `Dialog` is a centred task surface for a decision or a short form: it\n * interrupts, it sits in the middle of the screen, and the page behind it is\n * context rather than something to look at. A Drawer is edge-anchored and\n * deliberately leaves the page visible along its open side, because the work\n * happens *alongside* that content — you tune a filter and watch the rows\n * change. If the user cannot proceed until they answer, that is a Dialog. If\n * they are reading or adjusting something about what is already on screen, it\n * is a Drawer.\n *\n * **It is still modal**, and that is a deliberate choice rather than an\n * oversight: focus is trapped in the panel, Tab never reaches the page behind,\n * everything outside is hidden from assistive technology, and the page does not\n * scroll. Visible is not the same as reachable — a panel you can Tab out of\n * silently strands a keyboard user behind a scrim they cannot see past. One\n * consequence to know about is in `docs/components/drawer.md`: locking the body\n * removes the scrollbar, and Reka compensates with a `padding-right` that\n * viewport-pinned furniture does not inherit.\n *\n * **The way out is never a gesture alone.** Esc closes, the close control in the\n * corner closes, and either one returns focus to whatever opened it. The drag\n * handle is a pointer affordance sitting on top of those, never instead of\n * them — it does not exist for a keyboard, and on a desktop pointer it is a\n * hint rather than a route.\n *\n * `title` is required for the same reason it is on `Dialog`: it is the panel's\n * accessible name, and a drawer without one announces itself as a dialog that\n * says nothing about what it holds.\n *\n * Reach for `Dialog` when the answer must come before anything else happens,\n * `Popover` when the content is small, non-blocking and belongs next to its\n * trigger, and a transient notification when the user need not act at all.\n */\nconst props = withDefaults(\n defineProps<{\n /** Drive the drawer from the host with `v-model:open`; omit it and the drawer owns its own state. */\n open?: boolean | undefined;\n /** The accessible name, and the panel's heading. Name what is in it (\"Filters\", \"Scene 12\"), not the verb that opened it. */\n title: string;\n /** Wired as the drawer's accessible description — one line on what the panel is for, announced with the title. */\n description?: string;\n /** Which edge the panel is anchored to. `right` for detail, `left` for navigation, `bottom` for a sheet. */\n side?: DrawerSide;\n /** How far the panel reaches along its own axis — width on a side drawer, height on a sheet. */\n size?: DrawerSize;\n /** Whether a press outside the panel dismisses it, and whether the drag handle is offered. Esc and the close control work either way. */\n dismissible?: boolean;\n /**\n * The name on the close control, as any subset of `DrawerLabels` — what it\n * leaves out stays as the host's `provideLoomLabels` vocabulary left it,\n * and then as Loom's English.\n *\n * A whole application's language belongs in that vocabulary rather than\n * here. This is the per-instance correction, for the drawer whose close\n * means something more specific than \"Close\" — \"Close filters\", say, on a\n * page carrying two of them.\n */\n labels?: LabelOverrides<DrawerLabels>;\n }>(),\n // `open: undefined` is load-bearing, not a redundant default — it is what\n // keeps the uncontrolled third state reachable past Vue's absent-Boolean\n // casting. `optional()` in the template is the other half; its docblock\n // carries the reasoning for both. `description` needs no such entry: a\n // string prop is already `undefined` when absent.\n { open: undefined, side: \"right\", size: \"md\", dismissible: true },\n);\n\ndefineEmits<{\n /** The drawer opened or closed, for `v-model:open`. The host stays the source of truth — the drawer reports the request and waits. */\n \"update:open\": [value: boolean];\n}>();\n\n// `text`, not `labels`: the prop of that name is one of the three sources this\n// resolves, and a template reading the raw prop would be reading the overrides\n// rather than the answer.\nconst text = useLabels(\"drawer\", DRAWER_LABELS, () => props.labels);\n\nconst anchor = computed(() => SIDES[props.side]);\n\n// `DrawerRoot` renders no DOM node at all, so fallthrough attributes would be\n// dropped on the floor rather than landing anywhere. They go to the panel,\n// which is the element they actually describe — a `data-testid` or an\n// `aria-*` on `<Drawer>` reads as being about the drawer, not about the button\n// that happens to open it.\n//\n// `class` is pulled out of that spread and merged through `cn()` instead:\n// spreading it alongside the panel's own `:class` only concatenates the two\n// lists — Vue's merge is generic, not Tailwind-aware — so a caller's\n// `w-[30rem]` would lose to the size scale's own width depending on which\n// utility Tailwind happened to emit last.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: panelAttrs } = useSplitAttrs();\n\n/**\n * The `dismissible: false` half. Reka reads a prevented `interactOutside` as\n * \"this layer declines to be dismissed\", which covers the pointer press and the\n * focus that follows it in one place. Esc is deliberately untouched: a panel\n * with no keyboard exit is a trap, and \"do not close this by accident\" is a\n * different request from \"do not let this be closed\".\n */\nfunction guardOutsidePress(event: Event): void {\n if (!props.dismissible) event.preventDefault();\n}\n\n// The `dismissible: false` other half — the swipe. Reka wires\n// `useSwipeDismiss` unconditionally inside `DrawerContentImpl` with\n// `enabled: open` and exposes no prop to turn it off, so a drag on the panel\n// body toward the anchored edge used to dismiss a drawer whose `dismissible`\n// was `false` — defeating exactly the case the prop exists for, on touch,\n// where the reader is least likely to have meant it.\n//\n// There is no event Reka emits for the gesture that a handler could veto\n// from outside; the only lever is stopping its pointer handlers before they\n// start a swipe. Reka listens on the panel in the bubble phase, so a\n// capture-phase listener on `document` runs first for every event that\n// reaches the panel, and `stopImmediatePropagation` clears the rest of the\n// path — Reka's listener included — while leaving click, focus, scroll and\n// selection alone, which are separate events.\n//\n// Two costs are accepted rather than designed around. A consumer's own\n// pointerdown handler inside a non-dismissible drawer is stopped too, which\n// is the same neighbourhood as `interact-outside` vetoing their outside\n// press: `dismissible: false` declines pointer gestures, full stop. And a\n// non-dismissible drawer nested inside another non-dismissible one vetoes\n// gestures for both, which is the direction the pointer would have to travel\n// anyway.\n//\n// Pointer and touch are two paths in Reka (`onPointerDown` returns early for\n// touch pointers, which arrive via `onTouchStart`), so both events are\n// watched. The `data-loom-drawer-panel` marker scopes the veto to this\n// drawer's panel — and to nobody else's. That last half is the part that\n// bites: the marker is on every panel, so a naive `closest` check makes one\n// non-dismissible drawer on a page veto the gestures of every drawer on it,\n// dismissing nothing but the sibling's ability to be swiped. The marker\n// therefore carries this instance's own id, and the veto demands a match on\n// the value, not just the attribute. The attribute itself still has to\n// survive the panel being portalled away from the drawer's own subtree,\n// which an element-membership check would not.\nconst PANEL_MARKER = \"data-loom-drawer-panel\";\n// `useId` like the rest of the library: stable across server and client, so\n// the marker value a listener armed during hydration checks is the value the\n// portalled panel renders with.\nconst panelMarkerId = useId();\n\nfunction vetoSwipeStart(event: PointerEvent | TouchEvent): void {\n if (props.dismissible) return;\n const target = event.target;\n if (!(target instanceof Element)) return;\n const panel = target.closest(`[${PANEL_MARKER}]`);\n if (!(panel instanceof Element) || panel.getAttribute(PANEL_MARKER) !== panelMarkerId) return;\n event.stopImmediatePropagation();\n}\n\n// The listeners are on `document`, not the panel, because the panel's whole\n// lifecycle is Reka's — the portal content mounts with the drawer and is\n// removed with it, so a listener attached to the panel would need re-arming\n// on every open. A document listener is armed once for the component's own\n// lifetime, and `onBeforeUnmount` is the pairing that keeps it from leaking\n// into the next drawer or the host's page.\nonMounted(() => {\n document.addEventListener(\"pointerdown\", vetoSwipeStart, { capture: true });\n document.addEventListener(\"touchstart\", vetoSwipeStart, { capture: true, passive: true });\n});\nonBeforeUnmount(() => {\n document.removeEventListener(\"pointerdown\", vetoSwipeStart, { capture: true });\n document.removeEventListener(\"touchstart\", vetoSwipeStart, { capture: true });\n});\n</script>\n\n<template>\n <DrawerRoot\n v-bind=\"optional({ open })\"\n :swipe-direction=\"anchor.swipe\"\n @update:open=\"$emit('update:open', $event)\"\n >\n <!-- @slot The control that opens the drawer. Optional — omit it and drive\n the drawer entirely from the host with `v-model:open`. -->\n <DrawerTrigger v-if=\"$slots.trigger\" as-child>\n <slot name=\"trigger\" />\n </DrawerTrigger>\n\n <DrawerPortal>\n <!-- `scrim-light` rather than Dialog's `scrim`, and the token now carries\n the reason: a drawer's page is still the thing being worked on. It\n fades rather than moves, so nothing behind the panel appears to\n shift.\n\n Both states are scoped, never unconditional. Reka's Presence keeps\n closed content mounted until an animationend arrives, and a\n mount-only animation never fires a second one — which here would\n strand a full-screen, input-blocking scrim over a page with no drawer\n on it. The paired exit is what makes the close a timed fade instead\n of a cut, and it keeps that guarantee: `fade-out` ends, so Presence\n unmounts on its own animationend rather than on the absence of one. -->\n <DrawerOverlay\n class=\"fixed inset-0 z-overlay bg-foreground/scrim-light data-[state=open]:animate-fade data-[state=closed]:animate-fade-out\"\n />\n\n <DrawerContent\n v-bind=\"panelAttrs\"\n :data-loom-drawer-panel=\"panelMarkerId\"\n :class=\"\n cn(\n 'fixed z-overlay flex flex-col border-border bg-popover text-popover-foreground shadow-lg outline-none',\n anchor.edge,\n anchor.corners,\n EXTENT[anchor.extent][size],\n // `slow`, and this is the one place this component reaches past the\n // `--duration-normal` feedback ceiling. The motion foundations page\n // names a panel taking over the screen as exactly what the\n // unhurried lane is for, and a full-height panel crossing the\n // viewport at 200ms reads as a flinch rather than a slide.\n 'transition-transform duration-slow ease-out',\n anchor.slide,\n // The other half of the same gesture. `SIDES` carries why the two\n // directions are a transition and a keyframe rather than one\n // mechanism run both ways; the short version is that Presence\n // watches `animationend` and nothing else, so a transition on the\n // way out never gets a frame to run in.\n anchor.exit,\n // Reka's swipe-to-dismiss writes the live drag onto the panel as\n // CSS custom properties and leaves the rendering to us. Applying\n // them as `transform` keeps them off `translate`, which the slide\n // above owns — the two properties compose instead of overwriting\n // each other, so a half-finished swipe is not fighting the entry\n // transition. The `0px` fallbacks cover the browsers where\n // `CSS.registerProperty` is unavailable and the vars have no\n // registered initial value.\n '[transform:translate(var(--drawer-swipe-movement-x,0px),var(--drawer-swipe-movement-y,0px))]',\n // While a finger is down the panel must track it exactly; a 320ms\n // ease on every pointermove is a panel lagging behind the hand.\n 'data-[swiping]:transition-none',\n attrs.class as string,\n )\n \"\n @interact-outside=\"guardOutsidePress\"\n >\n <!-- Reka renders the handle `aria-hidden` and unfocusable, which is\n correct and is why it can never become the only exit: it is not on\n the keyboard's path at all. It is drawn only where it means\n something — with `dismissible` off there is nothing to drag towards. -->\n <DrawerHandle\n v-if=\"dismissible\"\n :class=\"cn('absolute rounded-full bg-border-strong', anchor.handle)\"\n />\n\n <!-- The header is `shrink-0` and lives outside the scrolling region on\n purpose: a drawer taller than its content's needs scrolls its body\n while the title — the panel's accessible name and the only thing\n saying what is on screen — stays put. `pr-12` keeps a long title\n clear of the close control. -->\n <div class=\"shrink-0 px-6 pb-4 pr-12 pt-6\">\n <DrawerTitle class=\"text-base font-semibold\">{{ title }}</DrawerTitle>\n <DrawerDescription v-if=\"description\" class=\"mt-1 text-sm text-muted-foreground\">\n {{ description }}\n </DrawerDescription>\n </div>\n\n <!-- `min-h-0` is what makes `flex-1` able to shrink below its content's\n height; without it a flex item's automatic minimum size wins and the\n body grows the panel past the viewport instead of scrolling.\n `overscroll-contain` stops a scroll that reaches the end of this\n region from chaining out to the locked page behind. -->\n <div class=\"min-h-0 flex-1 overflow-y-auto overscroll-contain px-6 pb-6\">\n <!-- @slot The drawer body. -->\n <slot />\n </div>\n\n <!-- @slot The action row, aligned to the trailing edge. Pinned below the\n scrolling body so its actions stay reachable in a long panel. -->\n <div\n v-if=\"$slots.footer\"\n class=\"flex shrink-0 items-center justify-end gap-2 border-t border-border px-6 py-4\"\n >\n <slot name=\"footer\" />\n </div>\n\n <!-- Unconditional, unlike Dialog's `closable`. A dialog can drop its\n corner button because the footer already carries an explicit\n decision; a drawer often has no footer at all, and one of its two\n keyboard-independent exits must always be visible. -->\n <DrawerClose\n :aria-label=\"text.close\"\n class=\"absolute right-4 top-4 inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors duration-fast ease-out hover:bg-subtle hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\"\n >\n <X class=\"h-4 w-4\" />\n </DrawerClose>\n </DrawerContent>\n </DrawerPortal>\n </DrawerRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAQA,IAAa,gBAA8B,EACzC,OAAO,QACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgEA,IAAM,QAAQ;CACZ,MAAM;EACJ,MAAM;EACN,SAAS;EACT,OAAO;EACP,MAAM;EACN,QAAQ;EACR,OAAO;EACP,QAAQ;CACV;CACA,OAAO;EACL,MAAM;EACN,SAAS;EACT,OAAO;EACP,MAAM;EACN,QAAQ;EACR,OAAO;EACP,QAAQ;CACV;CACA,KAAK;EACH,MAAM;EACN,SAAS;EACT,OAAO;EACP,MAAM;EACN,QAAQ;EACR,OAAO;EACP,QAAQ;CACV;CACA,QAAQ;EACN,MAAM;EACN,SAAS;EACT,OAAO;EACP,MAAM;EACN,QAAQ;EACR,OAAO;EACP,QAAQ;CACV;AACF;;;;;;;;;;;;;;;;;AA6BA,IAAM,SAAS;CACb,OAAO;EACL,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN;CACA,QAAQ;EACN,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN;AACF;;;;;;;;;;;;;;;;;;;;;EA4DA,MAAM,QAAQ;EA0Cd,MAAM,OAAO,UAAU,UAAU,qBAAqB,MAAM,MAAM;EAElE,MAAM,SAAS,eAAe,MAAM,MAAM,KAAK;EAc/C,MAAM,EAAE,OAAO,MAAM,eAAe,cAAc;;;;;;;;EASlD,SAAS,kBAAkB,OAAoB;GAC7C,IAAI,CAAC,MAAM,aAAa,MAAM,eAAe;EAC/C;EAoCA,MAAM,eAAe;EAIrB,MAAM,gBAAgB,MAAM;EAE5B,SAAS,eAAe,OAAwC;GAC9D,IAAI,MAAM,aAAa;GACvB,MAAM,SAAS,MAAM;GACrB,IAAI,EAAE,kBAAkB,UAAU;GAClC,MAAM,QAAQ,OAAO,QAAQ,IAAI,aAAa,EAAE;GAChD,IAAI,EAAE,iBAAiB,YAAY,MAAM,aAAa,YAAY,MAAM,eAAe;GACvF,MAAM,yBAAyB;EACjC;EAQA,gBAAgB;GACd,SAAS,iBAAiB,eAAe,gBAAgB,EAAE,SAAS,KAAK,CAAC;GAC1E,SAAS,iBAAiB,cAAc,gBAAgB;IAAE,SAAS;IAAM,SAAS;GAAK,CAAC;EAC1F,CAAC;EACD,sBAAsB;GACpB,SAAS,oBAAoB,eAAe,gBAAgB,EAAE,SAAS,KAAK,CAAC;GAC7E,SAAS,oBAAoB,cAAc,gBAAgB,EAAE,SAAS,KAAK,CAAC;EAC9E,CAAC;;GAIC,OAAA,UAAA,GAAA,YAuHa,MAAA,UAAA,GAvHb,WACU,MAsHG,QAAA,CAtHK,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAA;IACtB,mBAAiB,OAAA,MAAO;IACxB,iBAAW,OAAA,OAAA,OAAA,MAAA,WAAEA,KAAAA,MAAK,eAAgB,MAAM;;IAIzC,SAAA,cAEgB,CAFKC,KAAAA,OAAO,WAA5B,UAAA,GAAA,YAEgB,MAAA,aAAA,GAAA;;KAFqB,YAAA;;KACnC,SAAA,cAAuB,CAAvB,WAAuB,KAAA,QAAA,SAAA,CAAA,CAAA;;IAGzB,CAAA,KAAA,mBAAA,IAAA,IAAA,GAAA,YA2Ge,MAAA,YAAA,GAAA,MAAA;KA9Fb,SAAA,cAEE,CAFF,YAEE,MAAA,aAAA,GAAA,EADA,OAAM,wHAAuH,CAAA,GAG/H,YAyFgB,MAAA,aAAA,GAzFhB,WACU,MAwFM,UAAA,GAxFI;MACjB,0BAAwB,MAAA,aAAA;MACxB,OAAkB,MAAA,EAAA,CAAE,CAAA,yGAAmI,OAAA,MAAO,MAAkB,OAAA,MAAO,SAAqB,OAAO,OAAA,MAAO,OAAM,CAAE,QAAA,OAAA,+CAA2c,OAAA,MAAO,OAA2X,OAAA,MAAO,MAAA,gGAAA,kCAA04B,MAAA,KAAA,CAAK,CAAC,KAAA;MAkCt8D,mBAAkB;;MAMnB,SAAA,cAGE;OAFM,QAAA,eADR,UAAA,GAAA,YAGE,MAAA,YAAA,GAAA;;QADC,OAAK,eAAE,MAAA,EAAA,CAAE,CAAA,0CAA2C,OAAA,MAAO,MAAM,CAAA;;OAQpE,mBAKM,OALN,YAKM,CAJJ,YAAsE,MAAA,WAAA,GAAA,EAAzD,OAAM,0BAAyB,GAAA;QAAC,SAAA,cAAW,CAAR,gBAAA,gBAAA,QAAA,KAAK,GAAA,CAAA,CAAA,CAAA;;OAC5B,CAAA,GAAA,QAAA,eAAzB,UAAA,GAAA,YAEoB,MAAA,iBAAA,GAAA;;QAFkB,OAAM;;QAC1C,SAAA,cAAiB,CAAd,gBAAA,gBAAA,QAAA,WAAW,GAAA,CAAA,CAAA,CAAA;;;OASlB,mBAGM,OAHN,YAGM,CADJ,WAAQ,KAAA,QAAA,SAAA,CAAA,CAAA;OAMFA,KAAAA,OAAO,UADf,UAAA,GAAA,mBAKM,OALN,YAKM,CADJ,WAAsB,KAAA,QAAA,QAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;OAOxB,YAKc,MAAA,WAAA,GAAA;QAJX,cAAY,MAAA,IAAA,CAAI,CAAC;QAClB,OAAM;;QAEN,SAAA,cAAqB,CAArB,YAAqB,MAAA,CAAA,GAAA,EAAlB,OAAM,UAAS,CAAA,CAAA,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { default, DRAWER_LABELS } from "./Drawer.vue";
|
|
2
2
|
export type { DrawerSide, DrawerSize } from "./Drawer.vue";
|
|
3
|
-
export type { DrawerLabels } from "
|
|
3
|
+
export type { DrawerLabels } from "../../../labels/src/index";
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DropdownMenu.js","names":[],"sources":["../../../../../packages/primitives/dropdown-menu/src/DropdownMenu.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * One row of the menu, and the three kinds a menu is built from are told apart\n * by which fields are set rather than by a `kind` discriminator: a `separator`\n * is a rule, a `heading` is a non-interactive group label, and anything else\n * is a command.\n *\n * The list is data rather than markup because the primitive stays free of app\n * logic — selecting a row emits its `value` and the host decides what that\n * command means.\n */\nexport interface DropdownMenuEntry {\n /** The visible text of a command or a heading. */\n label?: string;\n /** The command id emitted on select. An entry without one is not selectable. */\n value?: string;\n /** Accelerator hint shown at the trailing edge, e.g. `⌘S`. Display only — the primitive binds no keys. */\n shortcut?: string;\n /** Render a divider. Every other field is ignored. */\n separator?: boolean;\n /** Render a non-interactive section heading rather than a command. */\n heading?: boolean;\n /** A destructive command — painted in the destructive token so it is not one indistinguishable row. */\n danger?: boolean;\n /** Visible but inert: announced as disabled, and selecting it emits nothing. */\n disabled?: boolean;\n}\n</script>\n\n<script setup lang=\"ts\">\nimport {\n DropdownMenuRoot,\n DropdownMenuTrigger,\n DropdownMenuPortal,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n} from \"reka-ui\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { listStaggerDelay } from \"@ecoma-io/loom-core\";\nimport { optional } from \"@ecoma-io/loom-core\";\n\n/**\n * DropdownMenu — one button that opens a list of commands, with roving focus,\n * typeahead and arrow-key navigation.\n *\n * The escape contract: opening moves focus into the menu and onto the first\n * command; Esc and an outside click both close it; closing returns focus to the\n * trigger. The menu is modal, so while it is open the page behind does not\n * scroll and the rest of the page is hidden from assistive technology — a\n * command list is never scrolled out from under the pointer mid-choice. Focus\n * stays in the menu while it is open, but not the way `Dialog` traps it: a menu\n * has no internal tab order at all. Tab is inert, and the arrow keys plus\n * typeahead are how a row is reached.\n *\n * Selecting an entry emits `select` with its `value`; the host maps that id to\n * an action, which is what keeps the primitive free of app logic.\n *\n * Reach for `Popover` when the panel is interactive content rather than a\n * command list, and for a select control when the list *is* a field's value.\n */\nwithDefaults(\n defineProps<{\n /** The rows, in order. Three kinds, told apart by which fields are set. */\n items: DropdownMenuEntry[];\n /** Drive the menu from the host with `v-model:open`; omit it and the menu owns its own state. */\n open?: boolean | undefined;\n }>(),\n // `open: undefined` is load-bearing, not a redundant default — it is what\n // keeps the uncontrolled third state reachable past Vue's absent-Boolean\n // casting. `optional()` in the template is the other half; its docblock\n // carries the reasoning for both.\n { open: undefined },\n);\nconst emit = defineEmits<{ select: [value: string]; \"update:open\": [value: boolean] }>();\n\n/**\n * A disabled entry, and an entry with no command id, select nothing. Reka\n * already blocks pointer events on the disabled row; this is the second half,\n * for the keyboard path that reaches it anyway.\n */\nfunction choose(item: DropdownMenuEntry): void {\n if (item.disabled || item.value === undefined) return;\n emit(\"select\", item.value);\n}\n</script>\n\n<template>\n <DropdownMenuRoot v-bind=\"optional({ open })\" @update:open=\"$emit('update:open', $event)\">\n <!-- @slot The button that opens the menu. Rendered `as-child`, so the\n caller's own element *is* the trigger. -->\n <DropdownMenuTrigger as-child>\n <slot name=\"trigger\" />\n </DropdownMenuTrigger>\n\n <DropdownMenuPortal>\n <DropdownMenuContent\n :side-offset=\"6\"\n align=\"start\"\n :class=\"\n cn(\n 'z-
|
|
1
|
+
{"version":3,"file":"DropdownMenu.js","names":[],"sources":["../../../../../packages/primitives/dropdown-menu/src/DropdownMenu.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * One row of the menu, and the three kinds a menu is built from are told apart\n * by which fields are set rather than by a `kind` discriminator: a `separator`\n * is a rule, a `heading` is a non-interactive group label, and anything else\n * is a command.\n *\n * The list is data rather than markup because the primitive stays free of app\n * logic — selecting a row emits its `value` and the host decides what that\n * command means.\n */\nexport interface DropdownMenuEntry {\n /** The visible text of a command or a heading. */\n label?: string;\n /** The command id emitted on select. An entry without one is not selectable. */\n value?: string;\n /** Accelerator hint shown at the trailing edge, e.g. `⌘S`. Display only — the primitive binds no keys. */\n shortcut?: string;\n /** Render a divider. Every other field is ignored. */\n separator?: boolean;\n /** Render a non-interactive section heading rather than a command. */\n heading?: boolean;\n /** A destructive command — painted in the destructive token so it is not one indistinguishable row. */\n danger?: boolean;\n /** Visible but inert: announced as disabled, and selecting it emits nothing. */\n disabled?: boolean;\n}\n</script>\n\n<script setup lang=\"ts\">\nimport {\n DropdownMenuRoot,\n DropdownMenuTrigger,\n DropdownMenuPortal,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n} from \"reka-ui\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { listStaggerDelay } from \"@ecoma-io/loom-core\";\nimport { optional } from \"@ecoma-io/loom-core\";\n\n/**\n * DropdownMenu — one button that opens a list of commands, with roving focus,\n * typeahead and arrow-key navigation.\n *\n * The escape contract: opening moves focus into the menu and onto the first\n * command; Esc and an outside click both close it; closing returns focus to the\n * trigger. The menu is modal, so while it is open the page behind does not\n * scroll and the rest of the page is hidden from assistive technology — a\n * command list is never scrolled out from under the pointer mid-choice. Focus\n * stays in the menu while it is open, but not the way `Dialog` traps it: a menu\n * has no internal tab order at all. Tab is inert, and the arrow keys plus\n * typeahead are how a row is reached.\n *\n * Selecting an entry emits `select` with its `value`; the host maps that id to\n * an action, which is what keeps the primitive free of app logic.\n *\n * Reach for `Popover` when the panel is interactive content rather than a\n * command list, and for a select control when the list *is* a field's value.\n */\nwithDefaults(\n defineProps<{\n /** The rows, in order. Three kinds, told apart by which fields are set. */\n items: DropdownMenuEntry[];\n /**\n * Let the arrow keys wrap at the ends of the list. Off by default — a\n * cursor that stops at the last row reads as \"there is nothing further\",\n * which is the honest answer for most command lists; long lists are where\n * cycling back to the top pays for itself.\n */\n loop?: boolean;\n /** Drive the menu from the host with `v-model:open`; omit it and the menu owns its own state. */\n open?: boolean | undefined;\n /**\n * Reading direction for the menu and its arrow-key navigation. Reka\n * mirrors traversal from this; without forwarding it a right-to-left\n * host gets left-to-right menus no matter what the page declares.\n */\n dir?: \"ltr\" | \"rtl\";\n }>(),\n // `open: undefined` is load-bearing, not a redundant default — it is what\n // keeps the uncontrolled third state reachable past Vue's absent-Boolean\n // casting. `optional()` in the template is the other half; its docblock\n // carries the reasoning for both.\n { open: undefined },\n);\nconst emit = defineEmits<{ select: [value: string]; \"update:open\": [value: boolean] }>();\n\n/**\n * A disabled entry, and an entry with no command id, select nothing. Reka\n * already blocks pointer events on the disabled row; this is the second half,\n * for the keyboard path that reaches it anyway.\n */\nfunction choose(item: DropdownMenuEntry): void {\n if (item.disabled || item.value === undefined) return;\n emit(\"select\", item.value);\n}\n</script>\n\n<template>\n <DropdownMenuRoot v-bind=\"optional({ open, dir })\" @update:open=\"$emit('update:open', $event)\">\n <!-- @slot The button that opens the menu. Rendered `as-child`, so the\n caller's own element *is* the trigger. -->\n <DropdownMenuTrigger as-child>\n <slot name=\"trigger\" />\n </DropdownMenuTrigger>\n\n <DropdownMenuPortal>\n <DropdownMenuContent\n :side-offset=\"6\"\n align=\"start\"\n :loop=\"loop ?? false\"\n :class=\"\n cn(\n 'z-overlay min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md outline-none',\n // Both states scoped, never unconditional. Reka's Presence\n // keeps closed content mounted until an animationend arrives, and a\n // mount-only animation never fires a second one — so an\n // unconditional animation class strands an invisible menu over the\n // page, catching clicks meant for what is behind it. Scoped, the\n // closed element computes `animation-name: none` and Presence\n // unmounts it at once; the paired exit is precisely what Presence\n // is waiting to hold it for. The origin below makes the menu grow\n // out of its trigger rather than out of its own centre — on the\n // way out as well as in.\n 'data-[state=open]:animate-scale-in data-[state=closed]:animate-scale-out',\n )\n \"\n style=\"transform-origin: var(--reka-popper-transform-origin)\"\n >\n <template v-for=\"(item, i) in items\" :key=\"i\">\n <DropdownMenuSeparator v-if=\"item.separator\" class=\"my-1 h-px bg-border\" />\n <DropdownMenuLabel\n v-else-if=\"item.heading\"\n class=\"px-2 py-1.5 text-micro font-medium uppercase tracking-wide text-muted-foreground\"\n >\n {{ item.label }}\n </DropdownMenuLabel>\n <!-- The reveal delay comes from the shared stagger vocabulary, capped\n there so a long menu does not tail off, rather than from a\n per-menu number written by hand.\n\n `disabled` is coerced rather than forwarded raw: an entry that\n simply omits the field is not disabled, and Reka's own prop does\n not accept `undefined` as a way of saying so. -->\n <DropdownMenuItem\n v-else\n :disabled=\"item.disabled ?? false\"\n :class=\"\n cn(\n 'flex cursor-pointer items-center justify-between gap-6 rounded-sm px-2 py-1.5 text-sm text-foreground outline-none',\n 'transition-colors duration-fast ease-out',\n // The rows reveal in sequence as the menu opens, which reads as\n // a list arriving rather than a block appearing. The hover\n // colour transition above is a separate channel, so a row\n // highlighted mid-reveal still responds at once.\n 'animate-fade-rise',\n 'data-[highlighted]:bg-subtle',\n // The row's colour, not its alpha. A menu row is nothing but\n // its label, and `data-[disabled]:opacity-40` composited that\n // label to 2.40:1 on the popover and its shortcut to 1.79:1 —\n // a disabled row a reader cannot make out tells them nothing\n // about what is unavailable, which is the only thing it is\n // there to say. Muted instead: 5.76:1, and a clear drop from\n // the 15.46:1 an available row wears.\n //\n // It takes the danger colour with it, deliberately. An\n // attribute selector outranks the plain `text-destructive-text`\n // below whatever order Tailwind emits them in, and that is the\n // right way round: red is a warning about an action, and there\n // is no action here to warn about.\n 'data-[disabled]:pointer-events-none data-[disabled]:text-muted-foreground',\n item.danger &&\n 'text-destructive-text data-[highlighted]:bg-destructive/10 data-[highlighted]:text-destructive-text',\n )\n \"\n :style=\"{ animationDelay: listStaggerDelay(i) }\"\n @select=\"choose(item)\"\n >\n <span>{{ item.label }}</span>\n <span v-if=\"item.shortcut\" class=\"tabular text-micro text-muted-foreground\">\n {{ item.shortcut }}\n </span>\n </DropdownMenuItem>\n </template>\n </DropdownMenuContent>\n </DropdownMenuPortal>\n </DropdownMenuRoot>\n</template>\n"],"mappings":""}
|
|
@@ -29,16 +29,42 @@ export default _default;
|
|
|
29
29
|
declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
30
30
|
/** The rows, in order. Three kinds, told apart by which fields are set. */
|
|
31
31
|
items: DropdownMenuEntry[];
|
|
32
|
+
/**
|
|
33
|
+
* Let the arrow keys wrap at the ends of the list. Off by default — a
|
|
34
|
+
* cursor that stops at the last row reads as "there is nothing further",
|
|
35
|
+
* which is the honest answer for most command lists; long lists are where
|
|
36
|
+
* cycling back to the top pays for itself.
|
|
37
|
+
*/
|
|
38
|
+
loop?: boolean;
|
|
32
39
|
/** Drive the menu from the host with `v-model:open`; omit it and the menu owns its own state. */
|
|
33
40
|
open?: boolean | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Reading direction for the menu and its arrow-key navigation. Reka
|
|
43
|
+
* mirrors traversal from this; without forwarding it a right-to-left
|
|
44
|
+
* host gets left-to-right menus no matter what the page declares.
|
|
45
|
+
*/
|
|
46
|
+
dir?: "ltr" | "rtl";
|
|
34
47
|
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
35
48
|
select: (value: string) => any;
|
|
36
49
|
"update:open": (value: boolean) => any;
|
|
37
50
|
}, string, import("vue").PublicProps, Readonly<{
|
|
38
51
|
/** The rows, in order. Three kinds, told apart by which fields are set. */
|
|
39
52
|
items: DropdownMenuEntry[];
|
|
53
|
+
/**
|
|
54
|
+
* Let the arrow keys wrap at the ends of the list. Off by default — a
|
|
55
|
+
* cursor that stops at the last row reads as "there is nothing further",
|
|
56
|
+
* which is the honest answer for most command lists; long lists are where
|
|
57
|
+
* cycling back to the top pays for itself.
|
|
58
|
+
*/
|
|
59
|
+
loop?: boolean;
|
|
40
60
|
/** Drive the menu from the host with `v-model:open`; omit it and the menu owns its own state. */
|
|
41
61
|
open?: boolean | undefined;
|
|
62
|
+
/**
|
|
63
|
+
* Reading direction for the menu and its arrow-key navigation. Reka
|
|
64
|
+
* mirrors traversal from this; without forwarding it a right-to-left
|
|
65
|
+
* host gets left-to-right menus no matter what the page declares.
|
|
66
|
+
*/
|
|
67
|
+
dir?: "ltr" | "rtl";
|
|
42
68
|
}> & Readonly<{
|
|
43
69
|
onSelect?: (value: string) => any;
|
|
44
70
|
"onUpdate:open"?: (value: boolean) => any;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DropdownMenu.vue.d.ts","sourceRoot":"","sources":["../../../../../packages/primitives/dropdown-menu/src/DropdownMenu.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"DropdownMenu.vue.d.ts","sourceRoot":"","sources":["../../../../../packages/primitives/dropdown-menu/src/DropdownMenu.vue"],"names":[],"mappings":"AAgNA;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0GAA0G;IAC1G,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sEAAsE;IACtE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uGAAuG;IACvG,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;wBACoB,OAAO,YAAY;AAAxC,wBAAyC;AACzC,QAAA,MAAM,YAAY;IAqBd,2EAA2E;WACpE,iBAAiB,EAAE;IAC1B;;;;;OAKG;WACI,OAAO;IACd,iGAAiG;WAC1F,OAAO,GAAG,SAAS;IAC1B;;;;OAIG;UACG,KAAK,GAAG,KAAK;;;;;IAhBnB,2EAA2E;WACpE,iBAAiB,EAAE;IAC1B;;;;;OAKG;WACI,OAAO;IACd,iGAAiG;WAC1F,OAAO,GAAG,SAAS;IAC1B;;;;OAIG;UACG,KAAK,GAAG,KAAK;;;;;UANZ,OAAO,GAAG,SAAS;;cA2ThB,CAAC,KAAK,IAAiB,KAAK,GAAG;EAWzC,CAAC;AAWL,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -13,10 +13,12 @@ var DropdownMenu_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
|
|
|
13
13
|
__name: "DropdownMenu",
|
|
14
14
|
props: {
|
|
15
15
|
items: {},
|
|
16
|
+
loop: { type: Boolean },
|
|
16
17
|
open: {
|
|
17
18
|
type: Boolean,
|
|
18
19
|
default: void 0
|
|
19
|
-
}
|
|
20
|
+
},
|
|
21
|
+
dir: {}
|
|
20
22
|
},
|
|
21
23
|
emits: ["select", "update:open"],
|
|
22
24
|
setup(__props, { emit: __emit }) {
|
|
@@ -31,7 +33,10 @@ var DropdownMenu_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
|
|
|
31
33
|
emit("select", item.value);
|
|
32
34
|
}
|
|
33
35
|
return (_ctx, _cache) => {
|
|
34
|
-
return openBlock(), createBlock(unref(DropdownMenuRoot), mergeProps(unref(optional)({
|
|
36
|
+
return openBlock(), createBlock(unref(DropdownMenuRoot), mergeProps(unref(optional)({
|
|
37
|
+
open: __props.open,
|
|
38
|
+
dir: __props.dir
|
|
39
|
+
}), { "onUpdate:open": _cache[0] || (_cache[0] = ($event) => _ctx.$emit("update:open", $event)) }), {
|
|
35
40
|
default: withCtx(() => [createVNode(unref(DropdownMenuTrigger), { "as-child": "" }, {
|
|
36
41
|
default: withCtx(() => [renderSlot(_ctx.$slots, "trigger")]),
|
|
37
42
|
_: 3
|
|
@@ -39,7 +44,8 @@ var DropdownMenu_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
|
|
|
39
44
|
default: withCtx(() => [createVNode(unref(DropdownMenuContent), {
|
|
40
45
|
"side-offset": 6,
|
|
41
46
|
align: "start",
|
|
42
|
-
|
|
47
|
+
loop: __props.loop ?? false,
|
|
48
|
+
class: normalizeClass(unref(cn)("z-overlay min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md outline-none", "data-[state=open]:animate-scale-in data-[state=closed]:animate-scale-out")),
|
|
43
49
|
style: { "transform-origin": "var(--reka-popper-transform-origin)" }
|
|
44
50
|
}, {
|
|
45
51
|
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.items, (item, i) => {
|
|
@@ -69,7 +75,7 @@ var DropdownMenu_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
|
|
|
69
75
|
]))], 64);
|
|
70
76
|
}), 128))]),
|
|
71
77
|
_: 1
|
|
72
|
-
}, 8, ["class"])]),
|
|
78
|
+
}, 8, ["loop", "class"])]),
|
|
73
79
|
_: 1
|
|
74
80
|
})]),
|
|
75
81
|
_: 3
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DropdownMenu.vue_vue_type_script_setup_true_lang.js","names":["$emit"],"sources":["../../../../../packages/primitives/dropdown-menu/src/DropdownMenu.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * One row of the menu, and the three kinds a menu is built from are told apart\n * by which fields are set rather than by a `kind` discriminator: a `separator`\n * is a rule, a `heading` is a non-interactive group label, and anything else\n * is a command.\n *\n * The list is data rather than markup because the primitive stays free of app\n * logic — selecting a row emits its `value` and the host decides what that\n * command means.\n */\nexport interface DropdownMenuEntry {\n /** The visible text of a command or a heading. */\n label?: string;\n /** The command id emitted on select. An entry without one is not selectable. */\n value?: string;\n /** Accelerator hint shown at the trailing edge, e.g. `⌘S`. Display only — the primitive binds no keys. */\n shortcut?: string;\n /** Render a divider. Every other field is ignored. */\n separator?: boolean;\n /** Render a non-interactive section heading rather than a command. */\n heading?: boolean;\n /** A destructive command — painted in the destructive token so it is not one indistinguishable row. */\n danger?: boolean;\n /** Visible but inert: announced as disabled, and selecting it emits nothing. */\n disabled?: boolean;\n}\n</script>\n\n<script setup lang=\"ts\">\nimport {\n DropdownMenuRoot,\n DropdownMenuTrigger,\n DropdownMenuPortal,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n} from \"reka-ui\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { listStaggerDelay } from \"@ecoma-io/loom-core\";\nimport { optional } from \"@ecoma-io/loom-core\";\n\n/**\n * DropdownMenu — one button that opens a list of commands, with roving focus,\n * typeahead and arrow-key navigation.\n *\n * The escape contract: opening moves focus into the menu and onto the first\n * command; Esc and an outside click both close it; closing returns focus to the\n * trigger. The menu is modal, so while it is open the page behind does not\n * scroll and the rest of the page is hidden from assistive technology — a\n * command list is never scrolled out from under the pointer mid-choice. Focus\n * stays in the menu while it is open, but not the way `Dialog` traps it: a menu\n * has no internal tab order at all. Tab is inert, and the arrow keys plus\n * typeahead are how a row is reached.\n *\n * Selecting an entry emits `select` with its `value`; the host maps that id to\n * an action, which is what keeps the primitive free of app logic.\n *\n * Reach for `Popover` when the panel is interactive content rather than a\n * command list, and for a select control when the list *is* a field's value.\n */\nwithDefaults(\n defineProps<{\n /** The rows, in order. Three kinds, told apart by which fields are set. */\n items: DropdownMenuEntry[];\n /** Drive the menu from the host with `v-model:open`; omit it and the menu owns its own state. */\n open?: boolean | undefined;\n }>(),\n // `open: undefined` is load-bearing, not a redundant default — it is what\n // keeps the uncontrolled third state reachable past Vue's absent-Boolean\n // casting. `optional()` in the template is the other half; its docblock\n // carries the reasoning for both.\n { open: undefined },\n);\nconst emit = defineEmits<{ select: [value: string]; \"update:open\": [value: boolean] }>();\n\n/**\n * A disabled entry, and an entry with no command id, select nothing. Reka\n * already blocks pointer events on the disabled row; this is the second half,\n * for the keyboard path that reaches it anyway.\n */\nfunction choose(item: DropdownMenuEntry): void {\n if (item.disabled || item.value === undefined) return;\n emit(\"select\", item.value);\n}\n</script>\n\n<template>\n <DropdownMenuRoot v-bind=\"optional({ open })\" @update:open=\"$emit('update:open', $event)\">\n <!-- @slot The button that opens the menu. Rendered `as-child`, so the\n caller's own element *is* the trigger. -->\n <DropdownMenuTrigger as-child>\n <slot name=\"trigger\" />\n </DropdownMenuTrigger>\n\n <DropdownMenuPortal>\n <DropdownMenuContent\n :side-offset=\"6\"\n align=\"start\"\n :class=\"\n cn(\n 'z-50 min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md outline-none',\n // Scoped to the open state, never unconditional. Reka's Presence\n // keeps closed content mounted until an animationend arrives, and a\n // mount-only animation never fires a second one — so an\n // unconditional animation class strands an invisible menu over the\n // page, catching clicks meant for what is behind it. Scoped, the\n // closed element computes `animation-name: none` and Presence\n // unmounts it at once. The origin below makes the menu grow out of\n // its trigger rather than out of its own centre.\n 'data-[state=open]:animate-scale-in',\n )\n \"\n style=\"transform-origin: var(--reka-popper-transform-origin)\"\n >\n <template v-for=\"(item, i) in items\" :key=\"i\">\n <DropdownMenuSeparator v-if=\"item.separator\" class=\"my-1 h-px bg-border\" />\n <DropdownMenuLabel\n v-else-if=\"item.heading\"\n class=\"px-2 py-1.5 text-micro font-medium uppercase tracking-wide text-muted-foreground\"\n >\n {{ item.label }}\n </DropdownMenuLabel>\n <!-- The reveal delay comes from the shared stagger vocabulary, capped\n there so a long menu does not tail off, rather than from a\n per-menu number written by hand.\n\n `disabled` is coerced rather than forwarded raw: an entry that\n simply omits the field is not disabled, and Reka's own prop does\n not accept `undefined` as a way of saying so. -->\n <DropdownMenuItem\n v-else\n :disabled=\"item.disabled ?? false\"\n :class=\"\n cn(\n 'flex cursor-pointer items-center justify-between gap-6 rounded-sm px-2 py-1.5 text-sm text-foreground outline-none',\n 'transition-colors duration-fast ease-out',\n // The rows reveal in sequence as the menu opens, which reads as\n // a list arriving rather than a block appearing. The hover\n // colour transition above is a separate channel, so a row\n // highlighted mid-reveal still responds at once.\n 'animate-fade-rise',\n 'data-[highlighted]:bg-subtle',\n // The row's colour, not its alpha. A menu row is nothing but\n // its label, and `data-[disabled]:opacity-40` composited that\n // label to 2.40:1 on the popover and its shortcut to 1.79:1 —\n // a disabled row a reader cannot make out tells them nothing\n // about what is unavailable, which is the only thing it is\n // there to say. Muted instead: 5.76:1, and a clear drop from\n // the 15.46:1 an available row wears.\n //\n // It takes the danger colour with it, deliberately. An\n // attribute selector outranks the plain `text-destructive-text`\n // below whatever order Tailwind emits them in, and that is the\n // right way round: red is a warning about an action, and there\n // is no action here to warn about.\n 'data-[disabled]:pointer-events-none data-[disabled]:text-muted-foreground',\n item.danger &&\n 'text-destructive-text data-[highlighted]:bg-destructive/10 data-[highlighted]:text-destructive-text',\n )\n \"\n :style=\"{ animationDelay: listStaggerDelay(i) }\"\n @select=\"choose(item)\"\n >\n <span>{{ item.label }}</span>\n <span v-if=\"item.shortcut\" class=\"tabular text-micro text-muted-foreground\">\n {{ item.shortcut }}\n </span>\n </DropdownMenuItem>\n </template>\n </DropdownMenuContent>\n </DropdownMenuPortal>\n </DropdownMenuRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;EA2EA,MAAM,OAAO;;;;;;EAOb,SAAS,OAAO,MAA+B;GAC7C,IAAI,KAAK,YAAY,KAAK,UAAU,KAAA,GAAW;GAC/C,KAAK,UAAU,KAAK,KAAK;EAC3B;;GAIE,OAAA,UAAA,GAAA,YAoFmB,MAAA,gBAAA,GApFnB,WAA0B,MAoFP,QAAA,CApFe,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAA,EAAM,iBAAW,OAAA,OAAA,OAAA,MAAA,WAAEA,KAAAA,MAAK,eAAgB,MAAM,GAAA,CAAA,GAAA;IAGrF,SAAA,cAEsB,CAFtB,YAEsB,MAAA,mBAAA,GAAA,EAFD,YAAA,GAAQ,GAAA;KAC3B,SAAA,cAAuB,CAAvB,WAAuB,KAAA,QAAA,SAAA,CAAA,CAAA;;IAGzB,CAAA,GAAA,YA4EqB,MAAA,kBAAA,GAAA,MAAA;KA3EnB,SAAA,cA0EsB,CA1EtB,YA0EsB,MAAA,mBAAA,GAAA;MAzEnB,eAAa;MACd,OAAM;MACL,OAAK,eAAa,MAAA,EAAA,CAAE,CAAA,oHAAA,oCAAA,CAAA;MAcrB,OAAA,EAAA,oBAAA,sCAAA;;MAEU,SAAA,cAA0B,EAApC,UAAA,IAAA,GAAA,mBAsDW,UAAA,MAAA,WAtDmB,QAAA,QAAZ,MAAM,MAAC;OAAkB,OAAA,UAAA,GAAA,mBAAA,UAAA,EAAA,KAAA,EAAC,GAAA,CACb,KAAK,aAAlC,UAAA,GAAA,YAA2E,MAAA,qBAAA,GAAA;;QAA9B,OAAM;OAEtC,CAAA,KAAA,KAAK,WADlB,UAAA,GAAA,YAKoB,MAAA,iBAAA,GAAA;;QAHlB,OAAM;;QAEN,SAAA,cAAgB,CAAb,gBAAA,gBAAA,KAAK,KAAK,GAAA,CAAA,CAAA,CAAA;;OASf,GAAA,IAAA,MAAA,UAAA,GAAA,YAsCmB,MAAA,gBAAA,GAAA;;QApChB,UAAU,KAAK,YAAQ;QACvB,OAAK,eAAiB,MAAA,EAAA,CAAE,CAAA,sHAAA,4CAAA,qBAAA,gCAAA,6EAA2jD,KAAK,UAAA,qGAAA,CAAA;QA4BxlD,OAAK,eAAA,EAAA,gBAAoB,MAAA,gBAAA,CAAgB,CAAC,CAAC,EAAA,CAAA;QAC3C,WAAM,WAAE,OAAO,IAAI;;QAEpB,SAAA,cAA6B,CAA7B,mBAA6B,QAAA,MAAA,gBAApB,KAAK,KAAK,GAAA,CAAA,GACP,KAAK,YAAjB,UAAA,GAAA,mBAEO,QAFP,YAEO,gBADF,KAAK,QAAQ,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA"}
|
|
1
|
+
{"version":3,"file":"DropdownMenu.vue_vue_type_script_setup_true_lang.js","names":["$emit"],"sources":["../../../../../packages/primitives/dropdown-menu/src/DropdownMenu.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * One row of the menu, and the three kinds a menu is built from are told apart\n * by which fields are set rather than by a `kind` discriminator: a `separator`\n * is a rule, a `heading` is a non-interactive group label, and anything else\n * is a command.\n *\n * The list is data rather than markup because the primitive stays free of app\n * logic — selecting a row emits its `value` and the host decides what that\n * command means.\n */\nexport interface DropdownMenuEntry {\n /** The visible text of a command or a heading. */\n label?: string;\n /** The command id emitted on select. An entry without one is not selectable. */\n value?: string;\n /** Accelerator hint shown at the trailing edge, e.g. `⌘S`. Display only — the primitive binds no keys. */\n shortcut?: string;\n /** Render a divider. Every other field is ignored. */\n separator?: boolean;\n /** Render a non-interactive section heading rather than a command. */\n heading?: boolean;\n /** A destructive command — painted in the destructive token so it is not one indistinguishable row. */\n danger?: boolean;\n /** Visible but inert: announced as disabled, and selecting it emits nothing. */\n disabled?: boolean;\n}\n</script>\n\n<script setup lang=\"ts\">\nimport {\n DropdownMenuRoot,\n DropdownMenuTrigger,\n DropdownMenuPortal,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n} from \"reka-ui\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { listStaggerDelay } from \"@ecoma-io/loom-core\";\nimport { optional } from \"@ecoma-io/loom-core\";\n\n/**\n * DropdownMenu — one button that opens a list of commands, with roving focus,\n * typeahead and arrow-key navigation.\n *\n * The escape contract: opening moves focus into the menu and onto the first\n * command; Esc and an outside click both close it; closing returns focus to the\n * trigger. The menu is modal, so while it is open the page behind does not\n * scroll and the rest of the page is hidden from assistive technology — a\n * command list is never scrolled out from under the pointer mid-choice. Focus\n * stays in the menu while it is open, but not the way `Dialog` traps it: a menu\n * has no internal tab order at all. Tab is inert, and the arrow keys plus\n * typeahead are how a row is reached.\n *\n * Selecting an entry emits `select` with its `value`; the host maps that id to\n * an action, which is what keeps the primitive free of app logic.\n *\n * Reach for `Popover` when the panel is interactive content rather than a\n * command list, and for a select control when the list *is* a field's value.\n */\nwithDefaults(\n defineProps<{\n /** The rows, in order. Three kinds, told apart by which fields are set. */\n items: DropdownMenuEntry[];\n /**\n * Let the arrow keys wrap at the ends of the list. Off by default — a\n * cursor that stops at the last row reads as \"there is nothing further\",\n * which is the honest answer for most command lists; long lists are where\n * cycling back to the top pays for itself.\n */\n loop?: boolean;\n /** Drive the menu from the host with `v-model:open`; omit it and the menu owns its own state. */\n open?: boolean | undefined;\n /**\n * Reading direction for the menu and its arrow-key navigation. Reka\n * mirrors traversal from this; without forwarding it a right-to-left\n * host gets left-to-right menus no matter what the page declares.\n */\n dir?: \"ltr\" | \"rtl\";\n }>(),\n // `open: undefined` is load-bearing, not a redundant default — it is what\n // keeps the uncontrolled third state reachable past Vue's absent-Boolean\n // casting. `optional()` in the template is the other half; its docblock\n // carries the reasoning for both.\n { open: undefined },\n);\nconst emit = defineEmits<{ select: [value: string]; \"update:open\": [value: boolean] }>();\n\n/**\n * A disabled entry, and an entry with no command id, select nothing. Reka\n * already blocks pointer events on the disabled row; this is the second half,\n * for the keyboard path that reaches it anyway.\n */\nfunction choose(item: DropdownMenuEntry): void {\n if (item.disabled || item.value === undefined) return;\n emit(\"select\", item.value);\n}\n</script>\n\n<template>\n <DropdownMenuRoot v-bind=\"optional({ open, dir })\" @update:open=\"$emit('update:open', $event)\">\n <!-- @slot The button that opens the menu. Rendered `as-child`, so the\n caller's own element *is* the trigger. -->\n <DropdownMenuTrigger as-child>\n <slot name=\"trigger\" />\n </DropdownMenuTrigger>\n\n <DropdownMenuPortal>\n <DropdownMenuContent\n :side-offset=\"6\"\n align=\"start\"\n :loop=\"loop ?? false\"\n :class=\"\n cn(\n 'z-overlay min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md outline-none',\n // Both states scoped, never unconditional. Reka's Presence\n // keeps closed content mounted until an animationend arrives, and a\n // mount-only animation never fires a second one — so an\n // unconditional animation class strands an invisible menu over the\n // page, catching clicks meant for what is behind it. Scoped, the\n // closed element computes `animation-name: none` and Presence\n // unmounts it at once; the paired exit is precisely what Presence\n // is waiting to hold it for. The origin below makes the menu grow\n // out of its trigger rather than out of its own centre — on the\n // way out as well as in.\n 'data-[state=open]:animate-scale-in data-[state=closed]:animate-scale-out',\n )\n \"\n style=\"transform-origin: var(--reka-popper-transform-origin)\"\n >\n <template v-for=\"(item, i) in items\" :key=\"i\">\n <DropdownMenuSeparator v-if=\"item.separator\" class=\"my-1 h-px bg-border\" />\n <DropdownMenuLabel\n v-else-if=\"item.heading\"\n class=\"px-2 py-1.5 text-micro font-medium uppercase tracking-wide text-muted-foreground\"\n >\n {{ item.label }}\n </DropdownMenuLabel>\n <!-- The reveal delay comes from the shared stagger vocabulary, capped\n there so a long menu does not tail off, rather than from a\n per-menu number written by hand.\n\n `disabled` is coerced rather than forwarded raw: an entry that\n simply omits the field is not disabled, and Reka's own prop does\n not accept `undefined` as a way of saying so. -->\n <DropdownMenuItem\n v-else\n :disabled=\"item.disabled ?? false\"\n :class=\"\n cn(\n 'flex cursor-pointer items-center justify-between gap-6 rounded-sm px-2 py-1.5 text-sm text-foreground outline-none',\n 'transition-colors duration-fast ease-out',\n // The rows reveal in sequence as the menu opens, which reads as\n // a list arriving rather than a block appearing. The hover\n // colour transition above is a separate channel, so a row\n // highlighted mid-reveal still responds at once.\n 'animate-fade-rise',\n 'data-[highlighted]:bg-subtle',\n // The row's colour, not its alpha. A menu row is nothing but\n // its label, and `data-[disabled]:opacity-40` composited that\n // label to 2.40:1 on the popover and its shortcut to 1.79:1 —\n // a disabled row a reader cannot make out tells them nothing\n // about what is unavailable, which is the only thing it is\n // there to say. Muted instead: 5.76:1, and a clear drop from\n // the 15.46:1 an available row wears.\n //\n // It takes the danger colour with it, deliberately. An\n // attribute selector outranks the plain `text-destructive-text`\n // below whatever order Tailwind emits them in, and that is the\n // right way round: red is a warning about an action, and there\n // is no action here to warn about.\n 'data-[disabled]:pointer-events-none data-[disabled]:text-muted-foreground',\n item.danger &&\n 'text-destructive-text data-[highlighted]:bg-destructive/10 data-[highlighted]:text-destructive-text',\n )\n \"\n :style=\"{ animationDelay: listStaggerDelay(i) }\"\n @select=\"choose(item)\"\n >\n <span>{{ item.label }}</span>\n <span v-if=\"item.shortcut\" class=\"tabular text-micro text-muted-foreground\">\n {{ item.shortcut }}\n </span>\n </DropdownMenuItem>\n </template>\n </DropdownMenuContent>\n </DropdownMenuPortal>\n </DropdownMenuRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;EAwFA,MAAM,OAAO;;;;;;EAOb,SAAS,OAAO,MAA+B;GAC7C,IAAI,KAAK,YAAY,KAAK,UAAU,KAAA,GAAW;GAC/C,KAAK,UAAU,KAAK,KAAK;EAC3B;;GAIE,OAAA,UAAA,GAAA,YAuFmB,MAAA,gBAAA,GAvFnB,WAA0B,MAuFP,QAAA,CAvFe,CAAA;IAAA,MAAG,QAAA;IAAI,KAAE,QAAA;GAAG,CAAA,GAAA,EAAM,iBAAW,OAAA,OAAA,OAAA,MAAA,WAAEA,KAAAA,MAAK,eAAgB,MAAM,GAAA,CAAA,GAAA;IAG1F,SAAA,cAEsB,CAFtB,YAEsB,MAAA,mBAAA,GAAA,EAFD,YAAA,GAAQ,GAAA;KAC3B,SAAA,cAAuB,CAAvB,WAAuB,KAAA,QAAA,SAAA,CAAA,CAAA;;IAGzB,CAAA,GAAA,YA+EqB,MAAA,kBAAA,GAAA,MAAA;KA9EnB,SAAA,cA6EsB,CA7EtB,YA6EsB,MAAA,mBAAA,GAAA;MA5EnB,eAAa;MACd,OAAM;MACL,MAAM,QAAA,QAAI;MACV,OAAK,eAAa,MAAA,EAAA,CAAE,CAAA,yHAAA,0EAAA,CAAA;MAgBrB,OAAA,EAAA,oBAAA,sCAAA;;MAEU,SAAA,cAA0B,EAApC,UAAA,IAAA,GAAA,mBAsDW,UAAA,MAAA,WAtDmB,QAAA,QAAZ,MAAM,MAAC;OAAkB,OAAA,UAAA,GAAA,mBAAA,UAAA,EAAA,KAAA,EAAC,GAAA,CACb,KAAK,aAAlC,UAAA,GAAA,YAA2E,MAAA,qBAAA,GAAA;;QAA9B,OAAM;OAEtC,CAAA,KAAA,KAAK,WADlB,UAAA,GAAA,YAKoB,MAAA,iBAAA,GAAA;;QAHlB,OAAM;;QAEN,SAAA,cAAgB,CAAb,gBAAA,gBAAA,KAAK,KAAK,GAAA,CAAA,CAAA,CAAA;;OASf,GAAA,IAAA,MAAA,UAAA,GAAA,YAsCmB,MAAA,gBAAA,GAAA;;QApChB,UAAU,KAAK,YAAQ;QACvB,OAAK,eAAiB,MAAA,EAAA,CAAE,CAAA,sHAAA,4CAAA,qBAAA,gCAAA,6EAA2jD,KAAK,UAAA,qGAAA,CAAA;QA4BxlD,OAAK,eAAA,EAAA,gBAAoB,MAAA,gBAAA,CAAgB,CAAC,CAAC,EAAA,CAAA;QAC3C,WAAM,WAAE,OAAO,IAAI;;QAEpB,SAAA,cAA6B,CAA7B,mBAA6B,QAAA,MAAA,gBAApB,KAAK,KAAK,GAAA,CAAA,GACP,KAAK,YAAjB,UAAA,GAAA,mBAEO,QAFP,YAEO,gBADF,KAAK,QAAQ,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA"}
|