@ecoma-io/loom 0.5.0 → 0.6.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 +188 -101
- package/dist/a11y.js +4 -3
- package/dist/composition/center/src/Center.js.map +1 -0
- package/dist/composition/center/src/Center.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/composition/dashboard-grid/src/DashboardGrid.js +7 -0
- package/dist/composition/dashboard-grid/src/DashboardGrid.js.map +1 -0
- package/dist/composition/dashboard-grid/src/DashboardGrid.vue_vue_type_script_setup_true_lang.js +45 -0
- package/dist/composition/dashboard-grid/src/DashboardGrid.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/composition/frame/src/Frame.js.map +1 -0
- package/dist/composition/frame/src/Frame.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/composition/grid/src/Grid.js.map +1 -0
- package/dist/composition/grid/src/Grid.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/composition/inline/src/Inline.js.map +1 -0
- package/dist/composition/inline/src/Inline.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/composition/scroll-reel/src/ScrollReel.js.map +1 -0
- package/dist/composition/scroll-reel/src/ScrollReel.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/composition/sidebar/src/Sidebar.js.map +1 -0
- package/dist/composition/sidebar/src/Sidebar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/composition/split/src/Split.js.map +1 -0
- package/dist/composition/split/src/Split.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/composition/stack/src/Stack.js.map +1 -0
- package/dist/composition/stack/src/Stack.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/core/src/a11y-contract.js +558 -0
- package/dist/core/src/a11y-contract.js.map +1 -0
- package/dist/core/src/a11y-scope.js +151 -0
- package/dist/core/src/a11y-scope.js.map +1 -0
- package/dist/core/src/attrs.js.map +1 -0
- package/dist/core/src/cn.js +61 -0
- package/dist/core/src/cn.js.map +1 -0
- package/dist/core/src/icon-defaults.js.map +1 -0
- package/dist/core/src/index.js +9 -0
- package/dist/core/src/motion.js +31 -0
- package/dist/core/src/motion.js.map +1 -0
- package/dist/core/src/props.js.map +1 -0
- package/dist/core/src/theme.js.map +1 -0
- package/dist/index.js +287 -285
- package/dist/labels/src/ancestor-disabled.js.map +1 -0
- package/dist/labels/src/count-labels.js.map +1 -0
- package/dist/labels/src/date-labels.js +206 -0
- package/dist/labels/src/date-labels.js.map +1 -0
- package/dist/labels/src/field-context.js.map +1 -0
- package/dist/labels/src/labels.js.map +1 -0
- package/dist/layouts/app-shell/src/AppShell.js.map +1 -0
- package/dist/layouts/app-shell/src/AppShell.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/layouts/centered/src/Centered.js.map +1 -0
- package/dist/layouts/centered/src/Centered.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/layouts/dashboard/src/Dashboard.js.map +1 -0
- package/dist/layouts/dashboard/src/Dashboard.vue_vue_type_script_setup_true_lang.js +65 -0
- package/dist/layouts/dashboard/src/Dashboard.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/layouts/desktop-app-shell/src/DesktopAppShell.js +7 -0
- package/dist/layouts/desktop-app-shell/src/DesktopAppShell.js.map +1 -0
- package/dist/layouts/desktop-app-shell/src/DesktopAppShell.vue_vue_type_script_setup_true_lang.js +76 -0
- package/dist/layouts/desktop-app-shell/src/DesktopAppShell.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/layouts/form-layout/src/FormLayout.js.map +1 -0
- package/dist/layouts/form-layout/src/FormLayout.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/layouts/master-detail/src/MasterDetail.js.map +1 -0
- package/dist/layouts/master-detail/src/MasterDetail.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/layouts/reading/src/Reading.js.map +1 -0
- package/dist/layouts/reading/src/Reading.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/layouts/settings/src/Settings.js.map +1 -0
- package/dist/layouts/settings/src/Settings.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/layouts/split-layout/src/SplitLayout.js.map +1 -0
- package/dist/layouts/split-layout/src/SplitLayout.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/composition/dashboard-grid/src/DashboardGrid.vue.d.ts +26 -0
- package/dist/packages/composition/dashboard-grid/src/DashboardGrid.vue.d.ts.map +1 -0
- package/dist/packages/composition/dashboard-grid/src/index.d.ts.map +1 -0
- package/dist/packages/composition/split/src/Split.vue.d.ts.map +1 -1
- package/dist/packages/core/src/a11y-contract.d.ts +417 -0
- package/dist/packages/core/src/a11y-contract.d.ts.map +1 -0
- package/dist/packages/core/src/a11y-scope.d.ts +5 -3
- package/dist/packages/core/src/a11y-scope.d.ts.map +1 -1
- package/dist/packages/core/src/index.d.ts +7 -1
- package/dist/packages/core/src/index.d.ts.map +1 -1
- package/dist/packages/core/src/motion.d.ts +15 -6
- package/dist/packages/core/src/motion.d.ts.map +1 -1
- package/dist/packages/core/src/responsive-contract.d.ts +155 -0
- package/dist/packages/core/src/responsive-contract.d.ts.map +1 -0
- package/dist/packages/labels/src/component-shapes.d.ts +2 -0
- package/dist/packages/labels/src/component-shapes.d.ts.map +1 -1
- package/dist/packages/layouts/dashboard/src/Dashboard.vue.d.ts.map +1 -1
- package/dist/packages/layouts/desktop-app-shell/src/DesktopAppShell.vue.d.ts +103 -0
- package/dist/packages/layouts/desktop-app-shell/src/index.d.ts.map +1 -0
- package/dist/packages/loom/src/a11y.d.ts +2 -1
- package/dist/packages/loom/src/index.d.ts +23 -23
- package/dist/packages/patterns/app-header/src/AppHeader.vue.d.ts +82 -0
- package/dist/packages/patterns/app-header/src/AppHeader.vue.d.ts.map +1 -0
- package/dist/packages/patterns/app-header/src/index.d.ts.map +1 -0
- package/dist/packages/patterns/empty-state/src/EmptyState.vue.d.ts +54 -0
- package/dist/packages/patterns/empty-state/src/EmptyState.vue.d.ts.map +1 -0
- package/dist/packages/patterns/empty-state/src/index.d.ts.map +1 -0
- package/dist/packages/patterns/error-state/src/ErrorState.vue.d.ts +44 -0
- package/dist/packages/patterns/error-state/src/ErrorState.vue.d.ts.map +1 -0
- package/dist/packages/patterns/error-state/src/index.d.ts.map +1 -0
- package/dist/packages/patterns/form-actions/src/FormActions.vue.d.ts +56 -0
- package/dist/packages/patterns/form-actions/src/FormActions.vue.d.ts.map +1 -0
- package/dist/packages/patterns/form-actions/src/index.d.ts.map +1 -0
- package/dist/packages/patterns/form-section/src/FormSection.vue.d.ts +85 -0
- package/dist/packages/patterns/form-section/src/FormSection.vue.d.ts.map +1 -0
- package/dist/packages/patterns/form-section/src/index.d.ts.map +1 -0
- package/dist/packages/patterns/loading-state/src/LoadingState.vue.d.ts.map +1 -0
- package/dist/packages/patterns/loading-state/src/index.d.ts.map +1 -0
- package/dist/packages/patterns/metric-card/src/MetricCard.vue.d.ts +77 -0
- package/dist/packages/patterns/metric-card/src/MetricCard.vue.d.ts.map +1 -0
- package/dist/packages/patterns/metric-card/src/index.d.ts.map +1 -0
- package/dist/packages/patterns/page-header/src/PageHeader.vue.d.ts.map +1 -0
- package/dist/packages/patterns/page-header/src/index.d.ts.map +1 -0
- package/dist/packages/patterns/row-actions/src/RowActions.vue.d.ts.map +1 -0
- package/dist/packages/patterns/row-actions/src/index.d.ts.map +1 -0
- package/dist/packages/patterns/sidebar-nav/src/SidebarNav.vue.d.ts.map +1 -0
- package/dist/packages/patterns/sidebar-nav/src/index.d.ts.map +1 -0
- package/dist/packages/patterns/title-bar/src/TitleBar.vue.d.ts +83 -0
- package/dist/packages/patterns/title-bar/src/index.d.ts.map +1 -0
- package/dist/packages/patterns/toast-stack/src/index.d.ts.map +1 -0
- package/dist/packages/primitives/list/src/ListItem.vue.d.ts +1 -1
- package/dist/packages/primitives/list/src/ListItem.vue.d.ts.map +1 -1
- package/dist/packages/primitives/surface/src/Surface.vue.d.ts +2 -2
- package/dist/packages/primitives/surface/src/Surface.vue.d.ts.map +1 -1
- package/dist/packages/primitives/table/src/TableRow.vue.d.ts +2 -2
- package/dist/packages/primitives/table/src/TableRow.vue.d.ts.map +1 -1
- package/dist/patterns/app-header/src/AppHeader.js +7 -0
- package/dist/patterns/app-header/src/AppHeader.js.map +1 -0
- package/dist/patterns/app-header/src/AppHeader.vue_vue_type_script_setup_true_lang.js +96 -0
- package/dist/patterns/app-header/src/AppHeader.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/patterns/empty-state/src/EmptyState.js +7 -0
- package/dist/patterns/empty-state/src/EmptyState.js.map +1 -0
- package/dist/patterns/empty-state/src/EmptyState.vue_vue_type_script_setup_true_lang.js +73 -0
- package/dist/patterns/empty-state/src/EmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/patterns/error-state/src/ErrorState.js +7 -0
- package/dist/patterns/error-state/src/ErrorState.js.map +1 -0
- package/dist/patterns/error-state/src/ErrorState.vue_vue_type_script_setup_true_lang.js +60 -0
- package/dist/patterns/error-state/src/ErrorState.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/patterns/error-summary/src/ErrorSummary.js +7 -0
- package/dist/patterns/error-summary/src/ErrorSummary.js.map +1 -0
- package/dist/patterns/error-summary/src/ErrorSummary.vue_vue_type_script_setup_true_lang.js +90 -0
- package/dist/patterns/error-summary/src/ErrorSummary.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/patterns/form-actions/src/FormActions.js +7 -0
- package/dist/patterns/form-actions/src/FormActions.js.map +1 -0
- package/dist/patterns/form-actions/src/FormActions.vue_vue_type_script_setup_true_lang.js +22 -0
- package/dist/patterns/form-actions/src/FormActions.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/patterns/form-section/src/FormSection.js +7 -0
- package/dist/patterns/form-section/src/FormSection.js.map +1 -0
- package/dist/patterns/form-section/src/FormSection.vue_vue_type_script_setup_true_lang.js +64 -0
- package/dist/patterns/form-section/src/FormSection.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/patterns/loading-state/src/LoadingState.js +7 -0
- package/dist/patterns/loading-state/src/LoadingState.js.map +1 -0
- package/dist/patterns/loading-state/src/LoadingState.vue_vue_type_script_setup_true_lang.js +59 -0
- package/dist/patterns/loading-state/src/LoadingState.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/patterns/metric-card/src/MetricCard.js +7 -0
- package/dist/patterns/metric-card/src/MetricCard.js.map +1 -0
- package/dist/patterns/metric-card/src/MetricCard.vue_vue_type_script_setup_true_lang.js +87 -0
- package/dist/patterns/metric-card/src/MetricCard.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/patterns/page-header/src/PageHeader.js +7 -0
- package/dist/patterns/page-header/src/PageHeader.js.map +1 -0
- package/dist/patterns/page-header/src/PageHeader.vue_vue_type_script_setup_true_lang.js +68 -0
- package/dist/patterns/page-header/src/PageHeader.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/patterns/row-actions/src/RowActions.js +7 -0
- package/dist/patterns/row-actions/src/RowActions.js.map +1 -0
- package/dist/patterns/row-actions/src/RowActions.vue_vue_type_script_setup_true_lang.js +47 -0
- package/dist/patterns/row-actions/src/RowActions.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/patterns/sidebar-nav/src/SidebarNav.js +7 -0
- package/dist/patterns/sidebar-nav/src/SidebarNav.js.map +1 -0
- package/dist/patterns/sidebar-nav/src/SidebarNav.vue_vue_type_script_setup_true_lang.js +129 -0
- package/dist/patterns/sidebar-nav/src/SidebarNav.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/patterns/title-bar/src/TitleBar.js +7 -0
- package/dist/patterns/title-bar/src/TitleBar.js.map +1 -0
- package/dist/patterns/title-bar/src/TitleBar.vue_vue_type_script_setup_true_lang.js +69 -0
- package/dist/patterns/title-bar/src/TitleBar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/patterns/toast-stack/src/ToastStack.js +7 -0
- package/dist/patterns/toast-stack/src/ToastStack.js.map +1 -0
- package/dist/patterns/toast-stack/src/ToastStack.vue_vue_type_script_setup_true_lang.js +52 -0
- package/dist/patterns/toast-stack/src/ToastStack.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/accordion/src/Accordion.js.map +1 -0
- package/dist/primitives/accordion/src/Accordion.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/alert/src/Alert.js.map +1 -0
- package/dist/primitives/alert/src/Alert.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/alert-dialog/src/AlertDialog.js.map +1 -0
- package/dist/primitives/alert-dialog/src/AlertDialog.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/avatar/src/Avatar.js.map +1 -0
- package/dist/primitives/avatar/src/Avatar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/avatar-group/src/AvatarGroup.js.map +1 -0
- package/dist/primitives/avatar-group/src/AvatarGroup.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/badge/src/Badge.js.map +1 -0
- package/dist/primitives/badge/src/Badge.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/breadcrumb/src/Breadcrumb.js.map +1 -0
- package/dist/primitives/breadcrumb/src/Breadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/button/src/Button.js.map +1 -0
- package/dist/primitives/button/src/Button.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/calendar/src/Calendar.js.map +1 -0
- package/dist/primitives/calendar/src/Calendar.vue_vue_type_script_setup_true_lang.js +202 -0
- package/dist/primitives/calendar/src/Calendar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/card/src/Card.js.map +1 -0
- package/dist/primitives/card/src/Card.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/carousel/src/Carousel.js.map +1 -0
- package/dist/primitives/carousel/src/Carousel.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/checkbox/src/Checkbox.js.map +1 -0
- package/dist/primitives/checkbox/src/Checkbox.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/chip/src/Chip.js.map +1 -0
- package/dist/primitives/chip/src/Chip.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/collapse/src/Collapse.js.map +1 -0
- package/dist/primitives/collapse/src/Collapse.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/color-picker/src/ColorPicker.js.map +1 -0
- package/dist/primitives/color-picker/src/ColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/combobox/src/Combobox.js.map +1 -0
- package/dist/primitives/combobox/src/Combobox.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/command/src/Command.js.map +1 -0
- package/dist/primitives/command/src/Command.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/context-menu/src/ContextMenu.js.map +1 -0
- package/dist/primitives/context-menu/src/ContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/copy-button/src/CopyButton.js.map +1 -0
- package/dist/primitives/copy-button/src/CopyButton.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/copy-button/src/index.js +3 -0
- package/dist/primitives/data-grid/src/DataGrid.js.map +1 -0
- package/dist/primitives/data-grid/src/DataGrid.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/date-picker/src/DatePicker.js.map +1 -0
- package/dist/primitives/date-picker/src/DatePicker.vue_vue_type_script_setup_true_lang.js +292 -0
- package/dist/primitives/date-picker/src/DatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/date-range-picker/src/DateRangePicker.js.map +1 -0
- package/dist/primitives/date-range-picker/src/DateRangePicker.vue_vue_type_script_setup_true_lang.js +417 -0
- package/dist/primitives/date-range-picker/src/DateRangePicker.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/date-time-picker/src/DateTimePicker.js.map +1 -0
- package/dist/primitives/date-time-picker/src/DateTimePicker.vue_vue_type_script_setup_true_lang.js +359 -0
- package/dist/primitives/date-time-picker/src/DateTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/date-time-range-picker/src/DateTimeRangePicker.js.map +1 -0
- package/dist/primitives/date-time-range-picker/src/DateTimeRangePicker.vue_vue_type_script_setup_true_lang.js +563 -0
- package/dist/primitives/date-time-range-picker/src/DateTimeRangePicker.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/dialog/src/Dialog.js.map +1 -0
- package/dist/primitives/dialog/src/Dialog.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/drawer/src/Drawer.js.map +1 -0
- package/dist/primitives/drawer/src/Drawer.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/dropdown-menu/src/DropdownMenu.js.map +1 -0
- package/dist/primitives/dropdown-menu/src/DropdownMenu.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/editable/src/Editable.js.map +1 -0
- package/dist/primitives/editable/src/Editable.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/field/src/Field.js.map +1 -0
- package/dist/primitives/field/src/Field.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/fieldset/src/Fieldset.js.map +1 -0
- package/dist/primitives/fieldset/src/Fieldset.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/file-upload/src/FileUpload.js.map +1 -0
- package/dist/primitives/file-upload/src/FileUpload.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/hover-card/src/HoverCard.js.map +1 -0
- package/dist/primitives/hover-card/src/HoverCard.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/icon-button/src/IconButton.js.map +1 -0
- package/dist/primitives/icon-button/src/IconButton.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/indicator/src/Indicator.js.map +1 -0
- package/dist/primitives/indicator/src/Indicator.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/inline-error/src/InlineError.js.map +1 -0
- package/dist/primitives/inline-error/src/InlineError.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/kbd/src/Kbd.js.map +1 -0
- package/dist/primitives/kbd/src/Kbd.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/link/src/Link.js.map +1 -0
- package/dist/primitives/link/src/Link.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/list/src/List.js.map +1 -0
- package/dist/primitives/list/src/List.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/list/src/ListItem.js.map +1 -0
- package/dist/primitives/list/src/ListItem.vue_vue_type_script_setup_true_lang.js +101 -0
- package/dist/primitives/list/src/ListItem.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/live-region/src/LiveRegion.js.map +1 -0
- package/dist/primitives/live-region/src/LiveRegion.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/live-region/src/context.js.map +1 -0
- package/dist/primitives/live-region/src/useAnnounce.js.map +1 -0
- package/dist/primitives/menubar/src/Menubar.js.map +1 -0
- package/dist/primitives/menubar/src/Menubar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/meter/src/Meter.js.map +1 -0
- package/dist/primitives/meter/src/Meter.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/navigation-menu/src/NavigationMenu.js.map +1 -0
- package/dist/primitives/navigation-menu/src/NavigationMenu.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/number-field/src/NumberField.js.map +1 -0
- package/dist/primitives/number-field/src/NumberField.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/otp-input/src/OtpInput.js.map +1 -0
- package/dist/primitives/otp-input/src/OtpInput.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/pagination/src/Pagination.js.map +1 -0
- package/dist/primitives/pagination/src/Pagination.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/popover/src/Popover.js.map +1 -0
- package/dist/primitives/popover/src/Popover.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/progress/src/Progress.js.map +1 -0
- package/dist/primitives/progress/src/Progress.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/radial-progress/src/RadialProgress.js.map +1 -0
- package/dist/primitives/radial-progress/src/RadialProgress.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/radio-group/src/RadioGroup.js.map +1 -0
- package/dist/primitives/radio-group/src/RadioGroup.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/rating/src/Rating.js.map +1 -0
- package/dist/primitives/rating/src/Rating.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/scroll-area/src/ScrollArea.js.map +1 -0
- package/dist/primitives/scroll-area/src/ScrollArea.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/segmented-control/src/SegmentedControl.js.map +1 -0
- package/dist/primitives/segmented-control/src/SegmentedControl.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/select/src/Select.js.map +1 -0
- package/dist/primitives/select/src/Select.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/separator/src/Separator.js.map +1 -0
- package/dist/primitives/separator/src/Separator.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/skeleton/src/Skeleton.js.map +1 -0
- package/dist/primitives/skeleton/src/Skeleton.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/skip-link/src/SkipLink.js.map +1 -0
- package/dist/primitives/skip-link/src/SkipLink.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/slider/src/Slider.js.map +1 -0
- package/dist/primitives/slider/src/Slider.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/speed-dial/src/SpeedDial.js.map +1 -0
- package/dist/primitives/speed-dial/src/SpeedDial.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/spinner/src/Spinner.js.map +1 -0
- package/dist/primitives/spinner/src/Spinner.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/stepper/src/Stepper.js.map +1 -0
- package/dist/primitives/stepper/src/Stepper.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/surface/src/Surface.js.map +1 -0
- package/dist/primitives/surface/src/Surface.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/switch/src/Switch.js.map +1 -0
- package/dist/primitives/switch/src/Switch.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/table/src/Table.js.map +1 -0
- package/dist/primitives/table/src/Table.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/table/src/TableCell.js.map +1 -0
- package/dist/primitives/table/src/TableCell.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/table/src/TableHead.js.map +1 -0
- package/dist/primitives/table/src/TableHead.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/table/src/TableRow.js.map +1 -0
- package/dist/primitives/table/src/TableRow.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/tabs/src/Tabs.js.map +1 -0
- package/dist/primitives/tabs/src/Tabs.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/tags-input/src/TagsInput.js.map +1 -0
- package/dist/primitives/tags-input/src/TagsInput.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/text-field/src/TextField.js.map +1 -0
- package/dist/primitives/text-field/src/TextField.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/textarea/src/Textarea.js.map +1 -0
- package/dist/primitives/textarea/src/Textarea.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/time-picker/src/TimePicker.js.map +1 -0
- package/dist/primitives/time-picker/src/TimePicker.vue_vue_type_script_setup_true_lang.js +189 -0
- package/dist/primitives/time-picker/src/TimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/timeline/src/Timeline.js.map +1 -0
- package/dist/primitives/timeline/src/Timeline.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/timeline/src/TimelineItem.js.map +1 -0
- package/dist/primitives/timeline/src/TimelineItem.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/timeline/src/index.js +5 -0
- package/dist/primitives/toast/src/Toast.js.map +1 -0
- package/dist/primitives/toast/src/Toast.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/toast/src/ToastItem.js.map +1 -0
- package/dist/primitives/toast/src/ToastItem.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/toggle-group/src/ToggleGroup.js.map +1 -0
- package/dist/primitives/toggle-group/src/ToggleGroup.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/toolbar/src/Toolbar.js.map +1 -0
- package/dist/primitives/toolbar/src/Toolbar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/toolbar/src/ToolbarSeparator.js.map +1 -0
- package/dist/primitives/toolbar/src/ToolbarSeparator.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/tooltip/src/Tooltip.js.map +1 -0
- package/dist/primitives/tooltip/src/Tooltip.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/tree-view/src/TreeView.js.map +1 -0
- package/dist/primitives/tree-view/src/TreeView.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/tree-view/src/TreeViewNode.js.map +1 -0
- package/dist/primitives/tree-view/src/TreeViewNode.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/tree-view/src/context.js.map +1 -0
- package/dist/primitives/visually-hidden/src/VisuallyHidden.js.map +1 -0
- package/dist/primitives/visually-hidden/src/VisuallyHidden.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/window-controls/src/WindowControls.js.map +1 -0
- package/dist/primitives/window-controls/src/WindowControls.vue_vue_type_script_setup_true_lang.js +127 -0
- package/dist/primitives/window-controls/src/WindowControls.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/styles/theme.css +30 -6
- package/dist/theme.js +2 -2
- package/package.json +5 -4
- package/dist/node_modules/.pnpm/@internationalized_date@3.12.4/node_modules/@internationalized/date/dist/private/CalendarDate.js +0 -236
- package/dist/node_modules/.pnpm/@internationalized_date@3.12.4/node_modules/@internationalized/date/dist/private/CalendarDate.js.map +0 -1
- package/dist/node_modules/.pnpm/@internationalized_date@3.12.4/node_modules/@internationalized/date/dist/private/calendars/GregorianCalendar.js +0 -117
- package/dist/node_modules/.pnpm/@internationalized_date@3.12.4/node_modules/@internationalized/date/dist/private/calendars/GregorianCalendar.js.map +0 -1
- package/dist/node_modules/.pnpm/@internationalized_date@3.12.4/node_modules/@internationalized/date/dist/private/conversion.js +0 -142
- package/dist/node_modules/.pnpm/@internationalized_date@3.12.4/node_modules/@internationalized/date/dist/private/conversion.js.map +0 -1
- package/dist/node_modules/.pnpm/@internationalized_date@3.12.4/node_modules/@internationalized/date/dist/private/manipulation.js +0 -280
- package/dist/node_modules/.pnpm/@internationalized_date@3.12.4/node_modules/@internationalized/date/dist/private/manipulation.js.map +0 -1
- package/dist/node_modules/.pnpm/@internationalized_date@3.12.4/node_modules/@internationalized/date/dist/private/queries.js +0 -40
- package/dist/node_modules/.pnpm/@internationalized_date@3.12.4/node_modules/@internationalized/date/dist/private/queries.js.map +0 -1
- package/dist/node_modules/.pnpm/@internationalized_date@3.12.4/node_modules/@internationalized/date/dist/private/string.js +0 -69
- package/dist/node_modules/.pnpm/@internationalized_date@3.12.4/node_modules/@internationalized/date/dist/private/string.js.map +0 -1
- package/dist/node_modules/.pnpm/@internationalized_date@3.12.4/node_modules/@internationalized/date/dist/private/utils.js +0 -8
- package/dist/node_modules/.pnpm/@internationalized_date@3.12.4/node_modules/@internationalized/date/dist/private/utils.js.map +0 -1
- package/dist/packages/blocks/app-header/src/AppHeader.js +0 -7
- package/dist/packages/blocks/app-header/src/AppHeader.js.map +0 -1
- package/dist/packages/blocks/app-header/src/AppHeader.vue.d.ts +0 -82
- package/dist/packages/blocks/app-header/src/AppHeader.vue.d.ts.map +0 -1
- package/dist/packages/blocks/app-header/src/AppHeader.vue_vue_type_script_setup_true_lang.js +0 -96
- package/dist/packages/blocks/app-header/src/AppHeader.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/blocks/app-header/src/index.d.ts.map +0 -1
- package/dist/packages/blocks/dashboard-grid/src/DashboardGrid.js +0 -7
- package/dist/packages/blocks/dashboard-grid/src/DashboardGrid.js.map +0 -1
- package/dist/packages/blocks/dashboard-grid/src/DashboardGrid.vue.d.ts +0 -42
- package/dist/packages/blocks/dashboard-grid/src/DashboardGrid.vue.d.ts.map +0 -1
- package/dist/packages/blocks/dashboard-grid/src/DashboardGrid.vue_vue_type_script_setup_true_lang.js +0 -45
- package/dist/packages/blocks/dashboard-grid/src/DashboardGrid.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/blocks/dashboard-grid/src/index.d.ts.map +0 -1
- package/dist/packages/blocks/desktop-app-shell/src/DesktopAppShell.js +0 -7
- package/dist/packages/blocks/desktop-app-shell/src/DesktopAppShell.js.map +0 -1
- package/dist/packages/blocks/desktop-app-shell/src/DesktopAppShell.vue.d.ts +0 -102
- package/dist/packages/blocks/desktop-app-shell/src/DesktopAppShell.vue_vue_type_script_setup_true_lang.js +0 -76
- package/dist/packages/blocks/desktop-app-shell/src/DesktopAppShell.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/blocks/desktop-app-shell/src/index.d.ts.map +0 -1
- package/dist/packages/blocks/empty-state/src/EmptyState.js +0 -7
- package/dist/packages/blocks/empty-state/src/EmptyState.js.map +0 -1
- package/dist/packages/blocks/empty-state/src/EmptyState.vue.d.ts +0 -50
- package/dist/packages/blocks/empty-state/src/EmptyState.vue.d.ts.map +0 -1
- package/dist/packages/blocks/empty-state/src/EmptyState.vue_vue_type_script_setup_true_lang.js +0 -70
- package/dist/packages/blocks/empty-state/src/EmptyState.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/blocks/empty-state/src/index.d.ts.map +0 -1
- package/dist/packages/blocks/error-state/src/ErrorState.js +0 -7
- package/dist/packages/blocks/error-state/src/ErrorState.js.map +0 -1
- package/dist/packages/blocks/error-state/src/ErrorState.vue.d.ts +0 -44
- package/dist/packages/blocks/error-state/src/ErrorState.vue.d.ts.map +0 -1
- package/dist/packages/blocks/error-state/src/ErrorState.vue_vue_type_script_setup_true_lang.js +0 -60
- package/dist/packages/blocks/error-state/src/ErrorState.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/blocks/error-state/src/index.d.ts.map +0 -1
- package/dist/packages/blocks/error-summary/src/ErrorSummary.js +0 -7
- package/dist/packages/blocks/error-summary/src/ErrorSummary.js.map +0 -1
- package/dist/packages/blocks/error-summary/src/ErrorSummary.vue_vue_type_script_setup_true_lang.js +0 -90
- package/dist/packages/blocks/error-summary/src/ErrorSummary.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/blocks/form-actions/src/FormActions.js +0 -7
- package/dist/packages/blocks/form-actions/src/FormActions.js.map +0 -1
- package/dist/packages/blocks/form-actions/src/FormActions.vue.d.ts +0 -51
- package/dist/packages/blocks/form-actions/src/FormActions.vue.d.ts.map +0 -1
- package/dist/packages/blocks/form-actions/src/FormActions.vue_vue_type_script_setup_true_lang.js +0 -22
- package/dist/packages/blocks/form-actions/src/FormActions.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/blocks/form-actions/src/index.d.ts.map +0 -1
- package/dist/packages/blocks/form-section/src/FormSection.js +0 -7
- package/dist/packages/blocks/form-section/src/FormSection.js.map +0 -1
- package/dist/packages/blocks/form-section/src/FormSection.vue.d.ts +0 -85
- package/dist/packages/blocks/form-section/src/FormSection.vue.d.ts.map +0 -1
- package/dist/packages/blocks/form-section/src/FormSection.vue_vue_type_script_setup_true_lang.js +0 -64
- package/dist/packages/blocks/form-section/src/FormSection.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/blocks/form-section/src/index.d.ts.map +0 -1
- package/dist/packages/blocks/loading-state/src/LoadingState.js +0 -7
- package/dist/packages/blocks/loading-state/src/LoadingState.js.map +0 -1
- package/dist/packages/blocks/loading-state/src/LoadingState.vue.d.ts.map +0 -1
- package/dist/packages/blocks/loading-state/src/LoadingState.vue_vue_type_script_setup_true_lang.js +0 -59
- package/dist/packages/blocks/loading-state/src/LoadingState.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/blocks/loading-state/src/index.d.ts.map +0 -1
- package/dist/packages/blocks/metric-card/src/MetricCard.js +0 -7
- package/dist/packages/blocks/metric-card/src/MetricCard.js.map +0 -1
- package/dist/packages/blocks/metric-card/src/MetricCard.vue.d.ts +0 -77
- package/dist/packages/blocks/metric-card/src/MetricCard.vue.d.ts.map +0 -1
- package/dist/packages/blocks/metric-card/src/MetricCard.vue_vue_type_script_setup_true_lang.js +0 -87
- package/dist/packages/blocks/metric-card/src/MetricCard.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/blocks/metric-card/src/index.d.ts.map +0 -1
- package/dist/packages/blocks/page-header/src/PageHeader.js +0 -7
- package/dist/packages/blocks/page-header/src/PageHeader.js.map +0 -1
- package/dist/packages/blocks/page-header/src/PageHeader.vue.d.ts.map +0 -1
- package/dist/packages/blocks/page-header/src/PageHeader.vue_vue_type_script_setup_true_lang.js +0 -68
- package/dist/packages/blocks/page-header/src/PageHeader.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/blocks/page-header/src/index.d.ts.map +0 -1
- package/dist/packages/blocks/row-actions/src/RowActions.js +0 -7
- package/dist/packages/blocks/row-actions/src/RowActions.js.map +0 -1
- package/dist/packages/blocks/row-actions/src/RowActions.vue.d.ts.map +0 -1
- package/dist/packages/blocks/row-actions/src/RowActions.vue_vue_type_script_setup_true_lang.js +0 -47
- package/dist/packages/blocks/row-actions/src/RowActions.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/blocks/row-actions/src/index.d.ts.map +0 -1
- package/dist/packages/blocks/sidebar-nav/src/SidebarNav.js +0 -7
- package/dist/packages/blocks/sidebar-nav/src/SidebarNav.js.map +0 -1
- package/dist/packages/blocks/sidebar-nav/src/SidebarNav.vue.d.ts.map +0 -1
- package/dist/packages/blocks/sidebar-nav/src/SidebarNav.vue_vue_type_script_setup_true_lang.js +0 -129
- package/dist/packages/blocks/sidebar-nav/src/SidebarNav.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/blocks/sidebar-nav/src/index.d.ts.map +0 -1
- package/dist/packages/blocks/title-bar/src/TitleBar.js +0 -7
- package/dist/packages/blocks/title-bar/src/TitleBar.js.map +0 -1
- package/dist/packages/blocks/title-bar/src/TitleBar.vue.d.ts +0 -83
- package/dist/packages/blocks/title-bar/src/TitleBar.vue_vue_type_script_setup_true_lang.js +0 -69
- package/dist/packages/blocks/title-bar/src/TitleBar.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/blocks/title-bar/src/index.d.ts.map +0 -1
- package/dist/packages/blocks/toast-stack/src/ToastStack.js +0 -7
- package/dist/packages/blocks/toast-stack/src/ToastStack.js.map +0 -1
- package/dist/packages/blocks/toast-stack/src/ToastStack.vue_vue_type_script_setup_true_lang.js +0 -52
- package/dist/packages/blocks/toast-stack/src/ToastStack.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/blocks/toast-stack/src/index.d.ts.map +0 -1
- package/dist/packages/composition/center/src/Center.js.map +0 -1
- package/dist/packages/composition/center/src/Center.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/composition/frame/src/Frame.js.map +0 -1
- package/dist/packages/composition/frame/src/Frame.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/composition/grid/src/Grid.js.map +0 -1
- package/dist/packages/composition/grid/src/Grid.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/composition/inline/src/Inline.js.map +0 -1
- package/dist/packages/composition/inline/src/Inline.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/composition/scroll-reel/src/ScrollReel.js.map +0 -1
- package/dist/packages/composition/scroll-reel/src/ScrollReel.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/composition/sidebar/src/Sidebar.js.map +0 -1
- package/dist/packages/composition/sidebar/src/Sidebar.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/composition/split/src/Split.js.map +0 -1
- package/dist/packages/composition/split/src/Split.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/composition/stack/src/Stack.js.map +0 -1
- package/dist/packages/composition/stack/src/Stack.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/core/src/a11y-scope.js +0 -149
- package/dist/packages/core/src/a11y-scope.js.map +0 -1
- package/dist/packages/core/src/attrs.js.map +0 -1
- package/dist/packages/core/src/cn.js +0 -61
- package/dist/packages/core/src/cn.js.map +0 -1
- package/dist/packages/core/src/icon-defaults.js.map +0 -1
- package/dist/packages/core/src/index.js +0 -8
- package/dist/packages/core/src/motion.js +0 -27
- package/dist/packages/core/src/motion.js.map +0 -1
- package/dist/packages/core/src/props.js.map +0 -1
- package/dist/packages/core/src/theme.js.map +0 -1
- package/dist/packages/labels/src/ancestor-disabled.js.map +0 -1
- package/dist/packages/labels/src/count-labels.js.map +0 -1
- package/dist/packages/labels/src/date-labels.js +0 -206
- package/dist/packages/labels/src/date-labels.js.map +0 -1
- package/dist/packages/labels/src/field-context.js.map +0 -1
- package/dist/packages/labels/src/labels.js.map +0 -1
- package/dist/packages/layouts/app-shell/src/AppShell.js.map +0 -1
- package/dist/packages/layouts/app-shell/src/AppShell.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/layouts/centered/src/Centered.js.map +0 -1
- package/dist/packages/layouts/centered/src/Centered.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/layouts/dashboard/src/Dashboard.js.map +0 -1
- package/dist/packages/layouts/dashboard/src/Dashboard.vue_vue_type_script_setup_true_lang.js +0 -65
- package/dist/packages/layouts/dashboard/src/Dashboard.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/layouts/form-layout/src/FormLayout.js.map +0 -1
- package/dist/packages/layouts/form-layout/src/FormLayout.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/layouts/master-detail/src/MasterDetail.js.map +0 -1
- package/dist/packages/layouts/master-detail/src/MasterDetail.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/layouts/reading/src/Reading.js.map +0 -1
- package/dist/packages/layouts/reading/src/Reading.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/layouts/settings/src/Settings.js.map +0 -1
- package/dist/packages/layouts/settings/src/Settings.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/layouts/split-layout/src/SplitLayout.js.map +0 -1
- package/dist/packages/layouts/split-layout/src/SplitLayout.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/accordion/src/Accordion.js.map +0 -1
- package/dist/packages/primitives/accordion/src/Accordion.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/alert/src/Alert.js.map +0 -1
- package/dist/packages/primitives/alert/src/Alert.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/alert-dialog/src/AlertDialog.js.map +0 -1
- package/dist/packages/primitives/alert-dialog/src/AlertDialog.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/avatar/src/Avatar.js.map +0 -1
- package/dist/packages/primitives/avatar/src/Avatar.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/avatar-group/src/AvatarGroup.js.map +0 -1
- package/dist/packages/primitives/avatar-group/src/AvatarGroup.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/badge/src/Badge.js.map +0 -1
- package/dist/packages/primitives/badge/src/Badge.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/breadcrumb/src/Breadcrumb.js.map +0 -1
- package/dist/packages/primitives/breadcrumb/src/Breadcrumb.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/button/src/Button.js.map +0 -1
- package/dist/packages/primitives/button/src/Button.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/calendar/src/Calendar.js.map +0 -1
- package/dist/packages/primitives/calendar/src/Calendar.vue_vue_type_script_setup_true_lang.js +0 -204
- package/dist/packages/primitives/calendar/src/Calendar.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/card/src/Card.js.map +0 -1
- package/dist/packages/primitives/card/src/Card.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/carousel/src/Carousel.js.map +0 -1
- package/dist/packages/primitives/carousel/src/Carousel.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/checkbox/src/Checkbox.js.map +0 -1
- package/dist/packages/primitives/checkbox/src/Checkbox.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/chip/src/Chip.js.map +0 -1
- package/dist/packages/primitives/chip/src/Chip.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/collapse/src/Collapse.js.map +0 -1
- package/dist/packages/primitives/collapse/src/Collapse.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/color-picker/src/ColorPicker.js.map +0 -1
- package/dist/packages/primitives/color-picker/src/ColorPicker.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/combobox/src/Combobox.js.map +0 -1
- package/dist/packages/primitives/combobox/src/Combobox.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/command/src/Command.js.map +0 -1
- package/dist/packages/primitives/command/src/Command.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/context-menu/src/ContextMenu.js.map +0 -1
- package/dist/packages/primitives/context-menu/src/ContextMenu.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/copy-button/src/CopyButton.js.map +0 -1
- package/dist/packages/primitives/copy-button/src/CopyButton.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/copy-button/src/index.js +0 -3
- package/dist/packages/primitives/data-grid/src/DataGrid.js.map +0 -1
- package/dist/packages/primitives/data-grid/src/DataGrid.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/date-picker/src/DatePicker.js.map +0 -1
- package/dist/packages/primitives/date-picker/src/DatePicker.vue_vue_type_script_setup_true_lang.js +0 -294
- package/dist/packages/primitives/date-picker/src/DatePicker.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/date-range-picker/src/DateRangePicker.js.map +0 -1
- package/dist/packages/primitives/date-range-picker/src/DateRangePicker.vue_vue_type_script_setup_true_lang.js +0 -419
- package/dist/packages/primitives/date-range-picker/src/DateRangePicker.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/date-time-picker/src/DateTimePicker.js.map +0 -1
- package/dist/packages/primitives/date-time-picker/src/DateTimePicker.vue_vue_type_script_setup_true_lang.js +0 -361
- package/dist/packages/primitives/date-time-picker/src/DateTimePicker.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/date-time-range-picker/src/DateTimeRangePicker.js.map +0 -1
- package/dist/packages/primitives/date-time-range-picker/src/DateTimeRangePicker.vue_vue_type_script_setup_true_lang.js +0 -565
- package/dist/packages/primitives/date-time-range-picker/src/DateTimeRangePicker.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/dialog/src/Dialog.js.map +0 -1
- package/dist/packages/primitives/dialog/src/Dialog.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/drawer/src/Drawer.js.map +0 -1
- package/dist/packages/primitives/drawer/src/Drawer.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/dropdown-menu/src/DropdownMenu.js.map +0 -1
- package/dist/packages/primitives/dropdown-menu/src/DropdownMenu.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/editable/src/Editable.js.map +0 -1
- package/dist/packages/primitives/editable/src/Editable.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/field/src/Field.js.map +0 -1
- package/dist/packages/primitives/field/src/Field.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/fieldset/src/Fieldset.js.map +0 -1
- package/dist/packages/primitives/fieldset/src/Fieldset.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/file-upload/src/FileUpload.js.map +0 -1
- package/dist/packages/primitives/file-upload/src/FileUpload.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/hover-card/src/HoverCard.js.map +0 -1
- package/dist/packages/primitives/hover-card/src/HoverCard.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/icon-button/src/IconButton.js.map +0 -1
- package/dist/packages/primitives/icon-button/src/IconButton.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/indicator/src/Indicator.js.map +0 -1
- package/dist/packages/primitives/indicator/src/Indicator.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/inline-error/src/InlineError.js.map +0 -1
- package/dist/packages/primitives/inline-error/src/InlineError.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/kbd/src/Kbd.js.map +0 -1
- package/dist/packages/primitives/kbd/src/Kbd.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/link/src/Link.js.map +0 -1
- package/dist/packages/primitives/link/src/Link.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/list/src/List.js.map +0 -1
- package/dist/packages/primitives/list/src/List.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/list/src/ListItem.js.map +0 -1
- package/dist/packages/primitives/list/src/ListItem.vue_vue_type_script_setup_true_lang.js +0 -101
- package/dist/packages/primitives/list/src/ListItem.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/live-region/src/LiveRegion.js.map +0 -1
- package/dist/packages/primitives/live-region/src/LiveRegion.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/live-region/src/context.js.map +0 -1
- package/dist/packages/primitives/live-region/src/useAnnounce.js.map +0 -1
- package/dist/packages/primitives/menubar/src/Menubar.js.map +0 -1
- package/dist/packages/primitives/menubar/src/Menubar.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/meter/src/Meter.js.map +0 -1
- package/dist/packages/primitives/meter/src/Meter.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/navigation-menu/src/NavigationMenu.js.map +0 -1
- package/dist/packages/primitives/navigation-menu/src/NavigationMenu.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/number-field/src/NumberField.js.map +0 -1
- package/dist/packages/primitives/number-field/src/NumberField.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/otp-input/src/OtpInput.js.map +0 -1
- package/dist/packages/primitives/otp-input/src/OtpInput.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/pagination/src/Pagination.js.map +0 -1
- package/dist/packages/primitives/pagination/src/Pagination.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/popover/src/Popover.js.map +0 -1
- package/dist/packages/primitives/popover/src/Popover.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/progress/src/Progress.js.map +0 -1
- package/dist/packages/primitives/progress/src/Progress.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/radial-progress/src/RadialProgress.js.map +0 -1
- package/dist/packages/primitives/radial-progress/src/RadialProgress.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/radio-group/src/RadioGroup.js.map +0 -1
- package/dist/packages/primitives/radio-group/src/RadioGroup.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/rating/src/Rating.js.map +0 -1
- package/dist/packages/primitives/rating/src/Rating.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/scroll-area/src/ScrollArea.js.map +0 -1
- package/dist/packages/primitives/scroll-area/src/ScrollArea.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/segmented-control/src/SegmentedControl.js.map +0 -1
- package/dist/packages/primitives/segmented-control/src/SegmentedControl.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/select/src/Select.js.map +0 -1
- package/dist/packages/primitives/select/src/Select.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/separator/src/Separator.js.map +0 -1
- package/dist/packages/primitives/separator/src/Separator.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/skeleton/src/Skeleton.js.map +0 -1
- package/dist/packages/primitives/skeleton/src/Skeleton.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/skip-link/src/SkipLink.js.map +0 -1
- package/dist/packages/primitives/skip-link/src/SkipLink.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/slider/src/Slider.js.map +0 -1
- package/dist/packages/primitives/slider/src/Slider.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/speed-dial/src/SpeedDial.js.map +0 -1
- package/dist/packages/primitives/speed-dial/src/SpeedDial.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/spinner/src/Spinner.js.map +0 -1
- package/dist/packages/primitives/spinner/src/Spinner.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/stepper/src/Stepper.js.map +0 -1
- package/dist/packages/primitives/stepper/src/Stepper.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/surface/src/Surface.js.map +0 -1
- package/dist/packages/primitives/surface/src/Surface.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/switch/src/Switch.js.map +0 -1
- package/dist/packages/primitives/switch/src/Switch.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/table/src/Table.js.map +0 -1
- package/dist/packages/primitives/table/src/Table.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/table/src/TableCell.js.map +0 -1
- package/dist/packages/primitives/table/src/TableCell.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/table/src/TableHead.js.map +0 -1
- package/dist/packages/primitives/table/src/TableHead.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/table/src/TableRow.js.map +0 -1
- package/dist/packages/primitives/table/src/TableRow.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/tabs/src/Tabs.js.map +0 -1
- package/dist/packages/primitives/tabs/src/Tabs.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/tags-input/src/TagsInput.js.map +0 -1
- package/dist/packages/primitives/tags-input/src/TagsInput.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/text-field/src/TextField.js.map +0 -1
- package/dist/packages/primitives/text-field/src/TextField.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/textarea/src/Textarea.js.map +0 -1
- package/dist/packages/primitives/textarea/src/Textarea.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/time-picker/src/TimePicker.js.map +0 -1
- package/dist/packages/primitives/time-picker/src/TimePicker.vue_vue_type_script_setup_true_lang.js +0 -189
- package/dist/packages/primitives/time-picker/src/TimePicker.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/timeline/src/Timeline.js.map +0 -1
- package/dist/packages/primitives/timeline/src/Timeline.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/timeline/src/TimelineItem.js.map +0 -1
- package/dist/packages/primitives/timeline/src/TimelineItem.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/timeline/src/index.js +0 -5
- package/dist/packages/primitives/toast/src/Toast.js.map +0 -1
- package/dist/packages/primitives/toast/src/Toast.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/toast/src/ToastItem.js.map +0 -1
- package/dist/packages/primitives/toast/src/ToastItem.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/toggle-group/src/ToggleGroup.js.map +0 -1
- package/dist/packages/primitives/toggle-group/src/ToggleGroup.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/toolbar/src/Toolbar.js.map +0 -1
- package/dist/packages/primitives/toolbar/src/Toolbar.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/toolbar/src/ToolbarSeparator.js.map +0 -1
- package/dist/packages/primitives/toolbar/src/ToolbarSeparator.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/tooltip/src/Tooltip.js.map +0 -1
- package/dist/packages/primitives/tooltip/src/Tooltip.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/tree-view/src/TreeView.js.map +0 -1
- package/dist/packages/primitives/tree-view/src/TreeView.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/tree-view/src/TreeViewNode.js.map +0 -1
- package/dist/packages/primitives/tree-view/src/TreeViewNode.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/tree-view/src/context.js.map +0 -1
- package/dist/packages/primitives/visually-hidden/src/VisuallyHidden.js.map +0 -1
- package/dist/packages/primitives/visually-hidden/src/VisuallyHidden.vue_vue_type_script_setup_true_lang.js.map +0 -1
- package/dist/packages/primitives/window-controls/src/WindowControls.js.map +0 -1
- package/dist/packages/primitives/window-controls/src/WindowControls.vue_vue_type_script_setup_true_lang.js +0 -124
- package/dist/packages/primitives/window-controls/src/WindowControls.vue_vue_type_script_setup_true_lang.js.map +0 -1
- /package/dist/{packages/composition → composition}/center/src/Center.js +0 -0
- /package/dist/{packages/composition → composition}/center/src/Center.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/composition → composition}/center/src/index.js +0 -0
- /package/dist/{packages/blocks → composition}/dashboard-grid/src/index.js +0 -0
- /package/dist/{packages/composition → composition}/frame/src/Frame.js +0 -0
- /package/dist/{packages/composition → composition}/frame/src/Frame.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/composition → composition}/frame/src/index.js +0 -0
- /package/dist/{packages/composition → composition}/grid/src/Grid.js +0 -0
- /package/dist/{packages/composition → composition}/grid/src/Grid.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/composition → composition}/grid/src/index.js +0 -0
- /package/dist/{packages/composition → composition}/inline/src/Inline.js +0 -0
- /package/dist/{packages/composition → composition}/inline/src/Inline.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/composition → composition}/inline/src/index.js +0 -0
- /package/dist/{packages/composition → composition}/scroll-reel/src/ScrollReel.js +0 -0
- /package/dist/{packages/composition → composition}/scroll-reel/src/ScrollReel.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/composition → composition}/scroll-reel/src/index.js +0 -0
- /package/dist/{packages/composition → composition}/sidebar/src/Sidebar.js +0 -0
- /package/dist/{packages/composition → composition}/sidebar/src/Sidebar.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/composition → composition}/sidebar/src/index.js +0 -0
- /package/dist/{packages/composition → composition}/split/src/Split.js +0 -0
- /package/dist/{packages/composition → composition}/split/src/Split.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/composition → composition}/split/src/index.js +0 -0
- /package/dist/{packages/composition → composition}/stack/src/Stack.js +0 -0
- /package/dist/{packages/composition → composition}/stack/src/Stack.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/composition → composition}/stack/src/index.js +0 -0
- /package/dist/{packages/core → core}/src/attrs.js +0 -0
- /package/dist/{packages/core → core}/src/icon-defaults.js +0 -0
- /package/dist/{packages/core → core}/src/props.js +0 -0
- /package/dist/{packages/core → core}/src/theme.js +0 -0
- /package/dist/{packages/labels → labels}/src/ancestor-disabled.js +0 -0
- /package/dist/{packages/labels → labels}/src/count-labels.js +0 -0
- /package/dist/{packages/labels → labels}/src/field-context.js +0 -0
- /package/dist/{packages/labels → labels}/src/index.js +0 -0
- /package/dist/{packages/labels → labels}/src/labels.js +0 -0
- /package/dist/{packages/layouts → layouts}/app-shell/src/AppShell.js +0 -0
- /package/dist/{packages/layouts → layouts}/app-shell/src/AppShell.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/layouts → layouts}/app-shell/src/index.js +0 -0
- /package/dist/{packages/layouts → layouts}/centered/src/Centered.js +0 -0
- /package/dist/{packages/layouts → layouts}/centered/src/Centered.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/layouts → layouts}/centered/src/index.js +0 -0
- /package/dist/{packages/layouts → layouts}/dashboard/src/Dashboard.js +0 -0
- /package/dist/{packages/layouts → layouts}/dashboard/src/index.js +0 -0
- /package/dist/{packages/blocks → layouts}/desktop-app-shell/src/index.js +0 -0
- /package/dist/{packages/layouts → layouts}/form-layout/src/FormLayout.js +0 -0
- /package/dist/{packages/layouts → layouts}/form-layout/src/FormLayout.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/layouts → layouts}/form-layout/src/index.js +0 -0
- /package/dist/{packages/layouts → layouts}/master-detail/src/MasterDetail.js +0 -0
- /package/dist/{packages/layouts → layouts}/master-detail/src/MasterDetail.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/layouts → layouts}/master-detail/src/index.js +0 -0
- /package/dist/{packages/layouts → layouts}/reading/src/Reading.js +0 -0
- /package/dist/{packages/layouts → layouts}/reading/src/Reading.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/layouts → layouts}/reading/src/index.js +0 -0
- /package/dist/{packages/layouts → layouts}/settings/src/Settings.js +0 -0
- /package/dist/{packages/layouts → layouts}/settings/src/Settings.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/layouts → layouts}/settings/src/index.js +0 -0
- /package/dist/{packages/layouts → layouts}/split-layout/src/SplitLayout.js +0 -0
- /package/dist/{packages/layouts → layouts}/split-layout/src/SplitLayout.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/layouts → layouts}/split-layout/src/index.js +0 -0
- /package/dist/packages/{blocks → composition}/dashboard-grid/src/index.d.ts +0 -0
- /package/dist/packages/{blocks → layouts}/desktop-app-shell/src/index.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/app-header/src/index.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/empty-state/src/index.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/error-state/src/index.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/error-summary/src/ErrorSummary.vue.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/error-summary/src/index.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/form-actions/src/index.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/form-section/src/index.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/loading-state/src/LoadingState.vue.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/loading-state/src/index.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/metric-card/src/index.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/page-header/src/PageHeader.vue.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/page-header/src/index.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/row-actions/src/RowActions.vue.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/row-actions/src/index.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/sidebar-nav/src/SidebarNav.vue.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/sidebar-nav/src/index.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/title-bar/src/index.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/toast-stack/src/ToastStack.vue.d.ts +0 -0
- /package/dist/packages/{blocks → patterns}/toast-stack/src/index.d.ts +0 -0
- /package/dist/{packages/blocks → patterns}/app-header/src/index.js +0 -0
- /package/dist/{packages/blocks → patterns}/empty-state/src/index.js +0 -0
- /package/dist/{packages/blocks → patterns}/error-state/src/index.js +0 -0
- /package/dist/{packages/blocks → patterns}/error-summary/src/index.js +0 -0
- /package/dist/{packages/blocks → patterns}/form-actions/src/index.js +0 -0
- /package/dist/{packages/blocks → patterns}/form-section/src/index.js +0 -0
- /package/dist/{packages/blocks → patterns}/loading-state/src/index.js +0 -0
- /package/dist/{packages/blocks → patterns}/metric-card/src/index.js +0 -0
- /package/dist/{packages/blocks → patterns}/page-header/src/index.js +0 -0
- /package/dist/{packages/blocks → patterns}/row-actions/src/index.js +0 -0
- /package/dist/{packages/blocks → patterns}/sidebar-nav/src/index.js +0 -0
- /package/dist/{packages/blocks → patterns}/title-bar/src/index.js +0 -0
- /package/dist/{packages/blocks → patterns}/toast-stack/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/accordion/src/Accordion.js +0 -0
- /package/dist/{packages/primitives → primitives}/accordion/src/Accordion.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/accordion/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/alert/src/Alert.js +0 -0
- /package/dist/{packages/primitives → primitives}/alert/src/Alert.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/alert/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/alert-dialog/src/AlertDialog.js +0 -0
- /package/dist/{packages/primitives → primitives}/alert-dialog/src/AlertDialog.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/alert-dialog/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/avatar/src/Avatar.js +0 -0
- /package/dist/{packages/primitives → primitives}/avatar/src/Avatar.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/avatar/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/avatar-group/src/AvatarGroup.js +0 -0
- /package/dist/{packages/primitives → primitives}/avatar-group/src/AvatarGroup.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/avatar-group/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/badge/src/Badge.js +0 -0
- /package/dist/{packages/primitives → primitives}/badge/src/Badge.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/badge/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/breadcrumb/src/Breadcrumb.js +0 -0
- /package/dist/{packages/primitives → primitives}/breadcrumb/src/Breadcrumb.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/breadcrumb/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/button/src/Button.js +0 -0
- /package/dist/{packages/primitives → primitives}/button/src/Button.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/button/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/calendar/src/Calendar.js +0 -0
- /package/dist/{packages/primitives → primitives}/calendar/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/card/src/Card.js +0 -0
- /package/dist/{packages/primitives → primitives}/card/src/Card.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/card/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/carousel/src/Carousel.js +0 -0
- /package/dist/{packages/primitives → primitives}/carousel/src/Carousel.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/carousel/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/checkbox/src/Checkbox.js +0 -0
- /package/dist/{packages/primitives → primitives}/checkbox/src/Checkbox.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/checkbox/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/chip/src/Chip.js +0 -0
- /package/dist/{packages/primitives → primitives}/chip/src/Chip.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/chip/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/collapse/src/Collapse.js +0 -0
- /package/dist/{packages/primitives → primitives}/collapse/src/Collapse.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/collapse/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/color-picker/src/ColorPicker.js +0 -0
- /package/dist/{packages/primitives → primitives}/color-picker/src/ColorPicker.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/color-picker/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/combobox/src/Combobox.js +0 -0
- /package/dist/{packages/primitives → primitives}/combobox/src/Combobox.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/combobox/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/command/src/Command.js +0 -0
- /package/dist/{packages/primitives → primitives}/command/src/Command.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/command/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/context-menu/src/ContextMenu.js +0 -0
- /package/dist/{packages/primitives → primitives}/context-menu/src/ContextMenu.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/context-menu/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/copy-button/src/CopyButton.js +0 -0
- /package/dist/{packages/primitives → primitives}/copy-button/src/CopyButton.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/data-grid/src/DataGrid.js +0 -0
- /package/dist/{packages/primitives → primitives}/data-grid/src/DataGrid.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/data-grid/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/date-picker/src/DatePicker.js +0 -0
- /package/dist/{packages/primitives → primitives}/date-picker/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/date-range-picker/src/DateRangePicker.js +0 -0
- /package/dist/{packages/primitives → primitives}/date-range-picker/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/date-time-picker/src/DateTimePicker.js +0 -0
- /package/dist/{packages/primitives → primitives}/date-time-picker/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/date-time-range-picker/src/DateTimeRangePicker.js +0 -0
- /package/dist/{packages/primitives → primitives}/date-time-range-picker/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/dialog/src/Dialog.js +0 -0
- /package/dist/{packages/primitives → primitives}/dialog/src/Dialog.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/dialog/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/drawer/src/Drawer.js +0 -0
- /package/dist/{packages/primitives → primitives}/drawer/src/Drawer.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/drawer/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/dropdown-menu/src/DropdownMenu.js +0 -0
- /package/dist/{packages/primitives → primitives}/dropdown-menu/src/DropdownMenu.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/dropdown-menu/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/editable/src/Editable.js +0 -0
- /package/dist/{packages/primitives → primitives}/editable/src/Editable.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/editable/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/field/src/Field.js +0 -0
- /package/dist/{packages/primitives → primitives}/field/src/Field.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/field/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/fieldset/src/Fieldset.js +0 -0
- /package/dist/{packages/primitives → primitives}/fieldset/src/Fieldset.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/fieldset/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/file-upload/src/FileUpload.js +0 -0
- /package/dist/{packages/primitives → primitives}/file-upload/src/FileUpload.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/file-upload/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/hover-card/src/HoverCard.js +0 -0
- /package/dist/{packages/primitives → primitives}/hover-card/src/HoverCard.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/hover-card/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/icon-button/src/IconButton.js +0 -0
- /package/dist/{packages/primitives → primitives}/icon-button/src/IconButton.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/icon-button/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/indicator/src/Indicator.js +0 -0
- /package/dist/{packages/primitives → primitives}/indicator/src/Indicator.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/indicator/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/inline-error/src/InlineError.js +0 -0
- /package/dist/{packages/primitives → primitives}/inline-error/src/InlineError.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/inline-error/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/kbd/src/Kbd.js +0 -0
- /package/dist/{packages/primitives → primitives}/kbd/src/Kbd.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/kbd/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/link/src/Link.js +0 -0
- /package/dist/{packages/primitives → primitives}/link/src/Link.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/link/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/list/src/List.js +0 -0
- /package/dist/{packages/primitives → primitives}/list/src/List.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/list/src/ListItem.js +0 -0
- /package/dist/{packages/primitives → primitives}/list/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/live-region/src/LiveRegion.js +0 -0
- /package/dist/{packages/primitives → primitives}/live-region/src/LiveRegion.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/live-region/src/context.js +0 -0
- /package/dist/{packages/primitives → primitives}/live-region/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/live-region/src/useAnnounce.js +0 -0
- /package/dist/{packages/primitives → primitives}/menubar/src/Menubar.js +0 -0
- /package/dist/{packages/primitives → primitives}/menubar/src/Menubar.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/menubar/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/meter/src/Meter.js +0 -0
- /package/dist/{packages/primitives → primitives}/meter/src/Meter.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/meter/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/navigation-menu/src/NavigationMenu.js +0 -0
- /package/dist/{packages/primitives → primitives}/navigation-menu/src/NavigationMenu.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/navigation-menu/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/number-field/src/NumberField.js +0 -0
- /package/dist/{packages/primitives → primitives}/number-field/src/NumberField.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/number-field/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/otp-input/src/OtpInput.js +0 -0
- /package/dist/{packages/primitives → primitives}/otp-input/src/OtpInput.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/otp-input/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/pagination/src/Pagination.js +0 -0
- /package/dist/{packages/primitives → primitives}/pagination/src/Pagination.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/pagination/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/popover/src/Popover.js +0 -0
- /package/dist/{packages/primitives → primitives}/popover/src/Popover.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/popover/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/progress/src/Progress.js +0 -0
- /package/dist/{packages/primitives → primitives}/progress/src/Progress.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/progress/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/radial-progress/src/RadialProgress.js +0 -0
- /package/dist/{packages/primitives → primitives}/radial-progress/src/RadialProgress.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/radial-progress/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/radio-group/src/RadioGroup.js +0 -0
- /package/dist/{packages/primitives → primitives}/radio-group/src/RadioGroup.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/radio-group/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/rating/src/Rating.js +0 -0
- /package/dist/{packages/primitives → primitives}/rating/src/Rating.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/rating/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/scroll-area/src/ScrollArea.js +0 -0
- /package/dist/{packages/primitives → primitives}/scroll-area/src/ScrollArea.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/scroll-area/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/segmented-control/src/SegmentedControl.js +0 -0
- /package/dist/{packages/primitives → primitives}/segmented-control/src/SegmentedControl.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/segmented-control/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/select/src/Select.js +0 -0
- /package/dist/{packages/primitives → primitives}/select/src/Select.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/select/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/separator/src/Separator.js +0 -0
- /package/dist/{packages/primitives → primitives}/separator/src/Separator.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/separator/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/skeleton/src/Skeleton.js +0 -0
- /package/dist/{packages/primitives → primitives}/skeleton/src/Skeleton.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/skeleton/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/skip-link/src/SkipLink.js +0 -0
- /package/dist/{packages/primitives → primitives}/skip-link/src/SkipLink.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/skip-link/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/slider/src/Slider.js +0 -0
- /package/dist/{packages/primitives → primitives}/slider/src/Slider.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/slider/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/speed-dial/src/SpeedDial.js +0 -0
- /package/dist/{packages/primitives → primitives}/speed-dial/src/SpeedDial.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/speed-dial/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/spinner/src/Spinner.js +0 -0
- /package/dist/{packages/primitives → primitives}/spinner/src/Spinner.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/spinner/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/stepper/src/Stepper.js +0 -0
- /package/dist/{packages/primitives → primitives}/stepper/src/Stepper.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/stepper/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/surface/src/Surface.js +0 -0
- /package/dist/{packages/primitives → primitives}/surface/src/Surface.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/surface/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/switch/src/Switch.js +0 -0
- /package/dist/{packages/primitives → primitives}/switch/src/Switch.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/switch/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/table/src/Table.js +0 -0
- /package/dist/{packages/primitives → primitives}/table/src/Table.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/table/src/TableCell.js +0 -0
- /package/dist/{packages/primitives → primitives}/table/src/TableCell.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/table/src/TableHead.js +0 -0
- /package/dist/{packages/primitives → primitives}/table/src/TableHead.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/table/src/TableRow.js +0 -0
- /package/dist/{packages/primitives → primitives}/table/src/TableRow.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/table/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/tabs/src/Tabs.js +0 -0
- /package/dist/{packages/primitives → primitives}/tabs/src/Tabs.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/tabs/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/tags-input/src/TagsInput.js +0 -0
- /package/dist/{packages/primitives → primitives}/tags-input/src/TagsInput.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/tags-input/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/text-field/src/TextField.js +0 -0
- /package/dist/{packages/primitives → primitives}/text-field/src/TextField.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/text-field/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/textarea/src/Textarea.js +0 -0
- /package/dist/{packages/primitives → primitives}/textarea/src/Textarea.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/textarea/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/time-picker/src/TimePicker.js +0 -0
- /package/dist/{packages/primitives → primitives}/time-picker/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/timeline/src/Timeline.js +0 -0
- /package/dist/{packages/primitives → primitives}/timeline/src/Timeline.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/timeline/src/TimelineItem.js +0 -0
- /package/dist/{packages/primitives → primitives}/timeline/src/TimelineItem.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/toast/src/Toast.js +0 -0
- /package/dist/{packages/primitives → primitives}/toast/src/Toast.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/toast/src/ToastItem.js +0 -0
- /package/dist/{packages/primitives → primitives}/toast/src/ToastItem.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/toast/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/toggle-group/src/ToggleGroup.js +0 -0
- /package/dist/{packages/primitives → primitives}/toggle-group/src/ToggleGroup.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/toggle-group/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/toolbar/src/Toolbar.js +0 -0
- /package/dist/{packages/primitives → primitives}/toolbar/src/Toolbar.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/toolbar/src/ToolbarSeparator.js +0 -0
- /package/dist/{packages/primitives → primitives}/toolbar/src/ToolbarSeparator.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/toolbar/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/tooltip/src/Tooltip.js +0 -0
- /package/dist/{packages/primitives → primitives}/tooltip/src/Tooltip.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/tooltip/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/tree-view/src/TreeView.js +0 -0
- /package/dist/{packages/primitives → primitives}/tree-view/src/TreeView.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/tree-view/src/TreeViewNode.js +0 -0
- /package/dist/{packages/primitives → primitives}/tree-view/src/TreeViewNode.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/tree-view/src/context.js +0 -0
- /package/dist/{packages/primitives → primitives}/tree-view/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/visually-hidden/src/VisuallyHidden.js +0 -0
- /package/dist/{packages/primitives → primitives}/visually-hidden/src/VisuallyHidden.vue_vue_type_script_setup_true_lang.js +0 -0
- /package/dist/{packages/primitives → primitives}/visually-hidden/src/index.js +0 -0
- /package/dist/{packages/primitives → primitives}/window-controls/src/WindowControls.js +0 -0
- /package/dist/{packages/primitives → primitives}/window-controls/src/index.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OtpInput.js","names":[],"sources":["../../../../packages/primitives/otp-input/src/OtpInput.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { OtpInputLabels, OtpInputType } from \"@ecoma-io/loom-labels\";\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 OTP_INPUT_LABELS: OtpInputLabels = {\n cell: ({ index, length, type }) =>\n `${type === \"numeric\" ? \"Digit\" : \"Character\"} ${String(index)} of ${String(length)}`,\n};\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, ref, watch } from \"vue\";\nimport { PinInputInput, PinInputRoot } from \"reka-ui\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useFieldControl } from \"@ecoma-io/loom-labels\";\nimport { useLabels, type LabelOverrides } from \"@ecoma-io/loom-labels\";\nimport { optional } from \"@ecoma-io/loom-core\";\n\n/**\n * OtpInput — the row of single-character cells a one-time code or a short PIN\n * is typed into. Reach for it when the value really is a fixed-length code\n * that arrives out of band: the cells are what let `autocomplete=\"one-time-code\"`\n * offer the code the phone has just received, and `inputmode=\"numeric\"` is what\n * raises the digit keypad rather than the alphabet. Both are the reason this\n * exists at all rather than a TextField with a `maxlength` — and both are also\n * why anything a reader would want to read back, correct in the middle, or\n * paste in parts belongs in a TextField instead.\n *\n * Built on Reka UI's PinInput, which supplies the per-cell behaviour: typing\n * advances to the next cell, arrow keys move between them, Backspace on an\n * empty cell steps back and clears the one before it, and a pasted code is\n * distributed across the whole row rather than crammed into one cell.\n *\n * Two things are decided here that Reka leaves open.\n *\n * **The value is a scalar `string`.** Reka models it as an array of cells;\n * this exposes `\"1234\"`, exactly as Slider exposes a scalar where Reka models\n * an array — a code is what a host posts to an API and compares against, and\n * neither of those wants a six-element array. In numeric mode Reka's array is\n * even an array of *numbers*, which loses a leading zero the moment anyone\n * treats it as one value. The consequence is in `onUpdate`.\n *\n * **The row is one control, not `length` of them.** Reka names each cell and\n * leaves each one tabbable, which is how this control usually fails: six\n * inputs each announcing themselves in full, and six Tab presses to step over\n * a field a sighted user reads as one. The name of the thing being typed is\n * moved onto a labelled `role=\"group\"` so it is announced once on entering the\n * row, each cell is left with its position alone, and a roving tabindex makes\n * the row a single Tab stop.\n *\n * Inside a [Field](../Field/Field.vue) it wires itself through\n * `useFieldControl()`, and this control is the one where the resolved values\n * do not all land on the same node. The id of the row's hint or error line\n * describes the whole code and goes on the group; `required` and `invalid` are\n * states of the boxes being typed into and go on the cells, where the roles\n * that support them are — `role=\"group\"` supports neither. The row's id and\n * the name the code is submitted under both go to Reka's hidden input: `id` is\n * a declared prop of its root rather than a fallthrough attr, and that input is\n * the better target anyway, since `<label for>` associates only with a\n * labelable element and hands focus to the first cell from there.\n *\n * What a Field still cannot do here is *name* the row: its `<label>` reaches\n * the hidden input, which is `aria-hidden`, so no accessible name is computed\n * from it. Give the name with `ariaLabel` or `ariaLabelledby` as you would\n * outside a row.\n *\n * A row's `readonly` is deliberately ignored. There is nothing to read in an\n * uneditable code box that a line of text would not show better, and the cells\n * exist to be typed into. So the library's three resting appearances collapse\n * to two here: a cell goes straight from `background` to the disabled row's\n * drained fill, muted character and slackened rim, and the lifted `subtle` fill\n * that marks a value on show never appears on this control.\n */\nconst props = withDefaults(\n defineProps<{\n /** The code entered so far. Shorter than `length` while it is incomplete. */\n modelValue?: string;\n /** How many cells the code is typed into. */\n length?: number;\n /** What a cell accepts: digits only, or any character. */\n type?: OtpInputType;\n /** Renders every filled cell as a dot instead of its character, for a PIN typed in public. */\n mask?: boolean;\n /** Unavailable: dims the row and refuses every cell. Unset defers to a wrapping Field. */\n disabled?: boolean | undefined;\n /** Error state: paints the destructive border and ring, and sets `aria-invalid` on every cell. Unset defers to a wrapping Field's `error`. */\n invalid?: boolean | undefined;\n /** The accessible name of the whole row, when no visible element already labels it. */\n ariaLabel?: string;\n /** The id of the visible element that labels the row — a heading, or a field's own label. */\n ariaLabelledby?: string;\n /**\n * The name each cell gives itself, as any subset of `OtpInputLabels` — the\n * rest stay as the host's `provideLoomLabels` vocabulary left them, and\n * then as Loom's English.\n *\n * This is the per-instance correction, not the place to localise an\n * application: the case it exists for is a row whose cells are neither\n * digits nor characters in the reader's terms — the groups of a licence\n * key, say — where the noun has to say so however well the application is\n * translated.\n */\n labels?: LabelOverrides<OtpInputLabels>;\n }>(),\n {\n length: 6,\n type: \"numeric\",\n mask: false,\n // Not `false`, for either. `false` is a caller saying \"this row is fine /\n // enabled even though the field around it is not\"; absent is a caller\n // saying nothing, and only `undefined` survives to mean that past Vue's\n // absent-Boolean-casts-to-false rule — see TextField.vue, which carries\n // the full reasoning.\n disabled: undefined,\n invalid: undefined,\n },\n);\n\nconst emit = defineEmits<{\n /** The code so far, as one string. Fires on every cell that fills or clears. */\n \"update:modelValue\": [value: string];\n /** The finished code. Fires once, as the last empty cell takes its character. */\n complete: [value: string];\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(\"otpInput\", OTP_INPUT_LABELS, () => props.labels);\n\n// Every fallthrough attribute lands on the group: an `aria-describedby`\n// pointing at an error line, a `data-testid`, an `id` — each describes the\n// control as a whole, and copying one onto `length` inputs would have a screen\n// reader repeat it on every cell. `class` is pulled out of that spread only so\n// it can go through `cn()`, which is what lets a caller's utility beat the\n// row's own instead of losing to whichever Tailwind emitted last.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: groupAttrs } = useSplitAttrs();\n\n// `id` and `aria-describedby` reach this control as fallthrough attrs rather\n// than props, so they are read off `attrs` and handed in as this caller's own\n// values — a caller who sets either still beats the row, and the row's\n// description is merged into theirs rather than replacing it. `name`,\n// `readonly` and `required` are absent from this object because there is no\n// prop to oppose the row with; the row's answer stands unopposed, which is\n// what is wanted.\nconst field = useFieldControl(() => ({\n id: attrs.id as string | undefined,\n describedBy: attrs[\"aria-describedby\"] as string | undefined,\n ariaLabelledby: props.ariaLabelledby,\n disabled: props.disabled,\n invalid: props.invalid,\n}));\n\n// Three of the resolved keys do not travel with the bag, because the bag lands\n// on the group and the group is the wrong node for all three.\n//\n// `name` would be a `name` on a `<div>`, which no form reads; Reka's own\n// hidden input carries the joined code, so the row's name is given to the root\n// as a prop instead.\n//\n// `aria-required` and `aria-invalid` are states of the boxes a reader types\n// into, and `role=\"group\"` supports neither — `aria-invalid` is a global so it\n// would at least be legal there, `aria-required` is not and would fail\n// `aria-allowed-attr`. Both are bound per cell below, off the same resolved\n// values, so the row's error reaches the element a screen reader is actually\n// sitting on.\n//\n// `id` stays in the bag and still does not land here: it is a declared prop of\n// Reka's root, so it is taken as a prop rather than falling through, and Reka\n// puts it on that same hidden input — which is the labelable node a\n// `<label for>` needs, and the one that hands focus to the first cell.\nconst groupFieldAttrs = computed(() => {\n // Removed from a copy rather than listed positively, so a key added to the\n // resolved bag later reaches this group without anyone remembering to come\n // back here.\n const group: Record<string, unknown> = { ...field.attrs };\n delete group.name;\n delete group[\"aria-required\"];\n delete group[\"aria-invalid\"];\n return group;\n});\n\n// The single source of truth for the code we know about: seeded from the prop,\n// then advanced on every update Reka reports. Feeding this back into Reka\n// rather than `props.modelValue` is what keeps the row usable for a host that\n// binds no `modelValue` at all — the same buffer Slider keeps, for the same\n// reason. An absent prop leaves it empty rather than sticking the row on one\n// value, so nothing here needs `optional()`.\nconst code = ref(props.modelValue ?? \"\");\nwatch(\n () => props.modelValue,\n (value) => {\n if (value !== undefined) code.value = value;\n },\n);\n\n// Longer than the row is the host's mistake to make, not ours to render: the\n// extra characters have no cell to live in.\nconst cells = computed(() => code.value.slice(0, props.length).split(\"\"));\n\n// Reka calls the digits-only mode \"number\". The prop says \"numeric\" because\n// nothing about the value is a number — a code with a leading zero is a\n// string, and this control never parses one.\nconst pinType = computed(() => (props.type === \"numeric\" ? \"number\" : \"text\"));\n\n// The row is one Tab stop. The stop is the first empty cell, which is also\n// where Reka's own `otp` handling redirects focus, or the last cell once the\n// code is full — so Tab lands where the next character goes.\nconst tabStopIndex = computed(() => Math.min(code.value.length, props.length - 1));\n\n// Typed wider than the declared emit on purpose: in numeric mode the array\n// really does come back holding numbers, and a cleared middle cell comes back\n// as a hole. Both are why a scalar is the exposed shape.\nfunction onUpdate(next: (string | number)[]) {\n // `join` is where the scalar is decided, and it closes gaps: Reka can hold a\n // cleared middle cell as a hole, a string cannot, so the cells after it\n // shift left. That is the only reading that keeps the model and the row\n // agreeing on one value — and it is what a single text input does when a\n // character in the middle is deleted.\n const value = next.join(\"\");\n // Reka reports the whole row on every write, including a write that changed\n // nothing a string can see — pasting the same code over itself is the\n // reachable one. Stopping here keeps one changed code to one emitted value,\n // and one filled code to one `complete`.\n if (value === code.value) return;\n code.value = value;\n emit(\"update:modelValue\", value);\n // Derived here rather than forwarded from Reka's own `complete`, which fires\n // from a deep watcher on its model — and that model is set twice for one\n // keystroke once the row is controlled, once by Reka and once by the value\n // coming back around. Forwarding it would announce one filled code twice.\n if (value.length === props.length) emit(\"complete\", value);\n}\n</script>\n\n<template>\n <PinInputRoot\n v-bind=\"{ ...groupAttrs, ...groupFieldAttrs, ...optional({ name: field.name }) }\"\n :model-value=\"cells\"\n :type=\"pinType\"\n :mask=\"mask\"\n :disabled=\"field.disabled\"\n otp\n role=\"group\"\n :aria-label=\"ariaLabelledby ? undefined : ariaLabel\"\n :aria-labelledby=\"ariaLabelledby || (!ariaLabel ? field.labelledBy : undefined)\"\n :data-invalid=\"field.invalid || undefined\"\n :class=\"cn('inline-flex items-center gap-2', attrs.class as string)\"\n @update:model-value=\"onUpdate\"\n >\n <!-- The `:aria-label` replaces the one Reka writes inside its own render\n function — \"pin input 3 of 6\", the field's whole purpose restated once\n per cell. It reaches the DOM node as a fallthrough attribute, which Vue\n merges last, so it is a replacement rather than a second name. Removing\n it does not fall back to nothing; it falls back to Reka's English. The\n group carries the purpose, and a cell carries only where it sits. -->\n <PinInputInput\n v-for=\"cell in length\"\n :key=\"cell\"\n :index=\"cell - 1\"\n :tabindex=\"cell - 1 === tabStopIndex ? 0 : -1\"\n :aria-label=\"text.cell({ index: cell, length, type })\"\n :aria-required=\"field.required || undefined\"\n :aria-invalid=\"field.invalid || undefined\"\n :data-filled=\"cell <= code.length || undefined\"\n :class=\"\n cn(\n // Square, on the control scale's tallest step: a code cell is a\n // touch target before it is a form row, and 44px is the size a thumb\n // is aimed at. `tabular` keeps the six glyphs on one width so the\n // row does not breathe as it fills.\n 'tabular h-11 w-11 rounded-md border border-input bg-background text-center text-base text-foreground',\n 'transition-[color,background-color,border-color,box-shadow] duration-fast ease-out',\n // Rim-lit at rest; the ring blooms on the cell being typed into.\n 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring',\n !field.invalid && 'focus-visible:shadow-halo',\n // A filled cell is legible as filled from its character — or its\n // mask dot — alone. The heavier border is a second, redundant cue,\n // and a weight rather than a hue, so it survives both a colour\n // deficiency and forced-colors.\n //\n // Withheld from an unavailable row, and only because the border is\n // also the third channel of the disabled state below: two rules\n // moving the same rim in opposite directions would leave a\n // half-filled disabled row with a heavier edge than an empty one,\n // which reads as the more available of the two. The cue is redundant\n // by construction, so the state wins it.\n !field.disabled && 'data-[filled]:border-border-strong',\n // Unavailable is a *colour* and a *weight*, never an opacity. A cell\n // holds one character — or one mask dot — and that is the entire\n // content of the control, so `opacity-50` here did not dim a code so\n // much as erase it: `--color-foreground` measures 14.09:1 on the\n // resting fill and 2.99:1 once composited at half alpha. The drained\n // fill carries the state instead, the character stays at a measured\n // 4.68:1, and the rim slackens from `input` to `border` so the state\n // is not told in hue alone.\n //\n // The fill and the text colour are `disabled:` variants rather than\n // plain classes, so each outranks the resting `bg-background\n // text-foreground` above on specificity rather than on where Tailwind\n // happened to emit it. The border cannot join them: a `disabled:`\n // variant would outrank the plain `border-destructive` below, and a\n // row that is both in error and unavailable has to keep saying so.\n 'disabled:cursor-not-allowed disabled:bg-muted disabled:text-muted-foreground',\n field.disabled && 'border-border',\n field.invalid && 'border-destructive focus-visible:outline-destructive',\n )\n \"\n />\n </PinInputRoot>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OtpInput.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../../packages/primitives/otp-input/src/OtpInput.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { OtpInputLabels, OtpInputType } from \"@ecoma-io/loom-labels\";\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 OTP_INPUT_LABELS: OtpInputLabels = {\n cell: ({ index, length, type }) =>\n `${type === \"numeric\" ? \"Digit\" : \"Character\"} ${String(index)} of ${String(length)}`,\n};\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, ref, watch } from \"vue\";\nimport { PinInputInput, PinInputRoot } from \"reka-ui\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useFieldControl } from \"@ecoma-io/loom-labels\";\nimport { useLabels, type LabelOverrides } from \"@ecoma-io/loom-labels\";\nimport { optional } from \"@ecoma-io/loom-core\";\n\n/**\n * OtpInput — the row of single-character cells a one-time code or a short PIN\n * is typed into. Reach for it when the value really is a fixed-length code\n * that arrives out of band: the cells are what let `autocomplete=\"one-time-code\"`\n * offer the code the phone has just received, and `inputmode=\"numeric\"` is what\n * raises the digit keypad rather than the alphabet. Both are the reason this\n * exists at all rather than a TextField with a `maxlength` — and both are also\n * why anything a reader would want to read back, correct in the middle, or\n * paste in parts belongs in a TextField instead.\n *\n * Built on Reka UI's PinInput, which supplies the per-cell behaviour: typing\n * advances to the next cell, arrow keys move between them, Backspace on an\n * empty cell steps back and clears the one before it, and a pasted code is\n * distributed across the whole row rather than crammed into one cell.\n *\n * Two things are decided here that Reka leaves open.\n *\n * **The value is a scalar `string`.** Reka models it as an array of cells;\n * this exposes `\"1234\"`, exactly as Slider exposes a scalar where Reka models\n * an array — a code is what a host posts to an API and compares against, and\n * neither of those wants a six-element array. In numeric mode Reka's array is\n * even an array of *numbers*, which loses a leading zero the moment anyone\n * treats it as one value. The consequence is in `onUpdate`.\n *\n * **The row is one control, not `length` of them.** Reka names each cell and\n * leaves each one tabbable, which is how this control usually fails: six\n * inputs each announcing themselves in full, and six Tab presses to step over\n * a field a sighted user reads as one. The name of the thing being typed is\n * moved onto a labelled `role=\"group\"` so it is announced once on entering the\n * row, each cell is left with its position alone, and a roving tabindex makes\n * the row a single Tab stop.\n *\n * Inside a [Field](../Field/Field.vue) it wires itself through\n * `useFieldControl()`, and this control is the one where the resolved values\n * do not all land on the same node. The id of the row's hint or error line\n * describes the whole code and goes on the group; `required` and `invalid` are\n * states of the boxes being typed into and go on the cells, where the roles\n * that support them are — `role=\"group\"` supports neither. The row's id and\n * the name the code is submitted under both go to Reka's hidden input: `id` is\n * a declared prop of its root rather than a fallthrough attr, and that input is\n * the better target anyway, since `<label for>` associates only with a\n * labelable element and hands focus to the first cell from there.\n *\n * What a Field still cannot do here is *name* the row: its `<label>` reaches\n * the hidden input, which is `aria-hidden`, so no accessible name is computed\n * from it. Give the name with `ariaLabel` or `ariaLabelledby` as you would\n * outside a row.\n *\n * A row's `readonly` is deliberately ignored. There is nothing to read in an\n * uneditable code box that a line of text would not show better, and the cells\n * exist to be typed into. So the library's three resting appearances collapse\n * to two here: a cell goes straight from `background` to the disabled row's\n * drained fill, muted character and slackened rim, and the lifted `subtle` fill\n * that marks a value on show never appears on this control.\n */\nconst props = withDefaults(\n defineProps<{\n /** The code entered so far. Shorter than `length` while it is incomplete. */\n modelValue?: string;\n /** How many cells the code is typed into. */\n length?: number;\n /** What a cell accepts: digits only, or any character. */\n type?: OtpInputType;\n /** Renders every filled cell as a dot instead of its character, for a PIN typed in public. */\n mask?: boolean;\n /** Unavailable: dims the row and refuses every cell. Unset defers to a wrapping Field. */\n disabled?: boolean | undefined;\n /** Error state: paints the destructive border and ring, and sets `aria-invalid` on every cell. Unset defers to a wrapping Field's `error`. */\n invalid?: boolean | undefined;\n /** The accessible name of the whole row, when no visible element already labels it. */\n ariaLabel?: string;\n /** The id of the visible element that labels the row — a heading, or a field's own label. */\n ariaLabelledby?: string;\n /**\n * The name each cell gives itself, as any subset of `OtpInputLabels` — the\n * rest stay as the host's `provideLoomLabels` vocabulary left them, and\n * then as Loom's English.\n *\n * This is the per-instance correction, not the place to localise an\n * application: the case it exists for is a row whose cells are neither\n * digits nor characters in the reader's terms — the groups of a licence\n * key, say — where the noun has to say so however well the application is\n * translated.\n */\n labels?: LabelOverrides<OtpInputLabels>;\n }>(),\n {\n length: 6,\n type: \"numeric\",\n mask: false,\n // Not `false`, for either. `false` is a caller saying \"this row is fine /\n // enabled even though the field around it is not\"; absent is a caller\n // saying nothing, and only `undefined` survives to mean that past Vue's\n // absent-Boolean-casts-to-false rule — see TextField.vue, which carries\n // the full reasoning.\n disabled: undefined,\n invalid: undefined,\n },\n);\n\nconst emit = defineEmits<{\n /** The code so far, as one string. Fires on every cell that fills or clears. */\n \"update:modelValue\": [value: string];\n /** The finished code. Fires once, as the last empty cell takes its character. */\n complete: [value: string];\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(\"otpInput\", OTP_INPUT_LABELS, () => props.labels);\n\n// Every fallthrough attribute lands on the group: an `aria-describedby`\n// pointing at an error line, a `data-testid`, an `id` — each describes the\n// control as a whole, and copying one onto `length` inputs would have a screen\n// reader repeat it on every cell. `class` is pulled out of that spread only so\n// it can go through `cn()`, which is what lets a caller's utility beat the\n// row's own instead of losing to whichever Tailwind emitted last.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: groupAttrs } = useSplitAttrs();\n\n// `id` and `aria-describedby` reach this control as fallthrough attrs rather\n// than props, so they are read off `attrs` and handed in as this caller's own\n// values — a caller who sets either still beats the row, and the row's\n// description is merged into theirs rather than replacing it. `name`,\n// `readonly` and `required` are absent from this object because there is no\n// prop to oppose the row with; the row's answer stands unopposed, which is\n// what is wanted.\nconst field = useFieldControl(() => ({\n id: attrs.id as string | undefined,\n describedBy: attrs[\"aria-describedby\"] as string | undefined,\n ariaLabelledby: props.ariaLabelledby,\n disabled: props.disabled,\n invalid: props.invalid,\n}));\n\n// Three of the resolved keys do not travel with the bag, because the bag lands\n// on the group and the group is the wrong node for all three.\n//\n// `name` would be a `name` on a `<div>`, which no form reads; Reka's own\n// hidden input carries the joined code, so the row's name is given to the root\n// as a prop instead.\n//\n// `aria-required` and `aria-invalid` are states of the boxes a reader types\n// into, and `role=\"group\"` supports neither — `aria-invalid` is a global so it\n// would at least be legal there, `aria-required` is not and would fail\n// `aria-allowed-attr`. Both are bound per cell below, off the same resolved\n// values, so the row's error reaches the element a screen reader is actually\n// sitting on.\n//\n// `id` stays in the bag and still does not land here: it is a declared prop of\n// Reka's root, so it is taken as a prop rather than falling through, and Reka\n// puts it on that same hidden input — which is the labelable node a\n// `<label for>` needs, and the one that hands focus to the first cell.\nconst groupFieldAttrs = computed(() => {\n // Removed from a copy rather than listed positively, so a key added to the\n // resolved bag later reaches this group without anyone remembering to come\n // back here.\n const group: Record<string, unknown> = { ...field.attrs };\n delete group.name;\n delete group[\"aria-required\"];\n delete group[\"aria-invalid\"];\n return group;\n});\n\n// The single source of truth for the code we know about: seeded from the prop,\n// then advanced on every update Reka reports. Feeding this back into Reka\n// rather than `props.modelValue` is what keeps the row usable for a host that\n// binds no `modelValue` at all — the same buffer Slider keeps, for the same\n// reason. An absent prop leaves it empty rather than sticking the row on one\n// value, so nothing here needs `optional()`.\nconst code = ref(props.modelValue ?? \"\");\nwatch(\n () => props.modelValue,\n (value) => {\n if (value !== undefined) code.value = value;\n },\n);\n\n// Longer than the row is the host's mistake to make, not ours to render: the\n// extra characters have no cell to live in.\nconst cells = computed(() => code.value.slice(0, props.length).split(\"\"));\n\n// Reka calls the digits-only mode \"number\". The prop says \"numeric\" because\n// nothing about the value is a number — a code with a leading zero is a\n// string, and this control never parses one.\nconst pinType = computed(() => (props.type === \"numeric\" ? \"number\" : \"text\"));\n\n// The row is one Tab stop. The stop is the first empty cell, which is also\n// where Reka's own `otp` handling redirects focus, or the last cell once the\n// code is full — so Tab lands where the next character goes.\nconst tabStopIndex = computed(() => Math.min(code.value.length, props.length - 1));\n\n// Typed wider than the declared emit on purpose: in numeric mode the array\n// really does come back holding numbers, and a cleared middle cell comes back\n// as a hole. Both are why a scalar is the exposed shape.\nfunction onUpdate(next: (string | number)[]) {\n // `join` is where the scalar is decided, and it closes gaps: Reka can hold a\n // cleared middle cell as a hole, a string cannot, so the cells after it\n // shift left. That is the only reading that keeps the model and the row\n // agreeing on one value — and it is what a single text input does when a\n // character in the middle is deleted.\n const value = next.join(\"\");\n // Reka reports the whole row on every write, including a write that changed\n // nothing a string can see — pasting the same code over itself is the\n // reachable one. Stopping here keeps one changed code to one emitted value,\n // and one filled code to one `complete`.\n if (value === code.value) return;\n code.value = value;\n emit(\"update:modelValue\", value);\n // Derived here rather than forwarded from Reka's own `complete`, which fires\n // from a deep watcher on its model — and that model is set twice for one\n // keystroke once the row is controlled, once by Reka and once by the value\n // coming back around. Forwarding it would announce one filled code twice.\n if (value.length === props.length) emit(\"complete\", value);\n}\n</script>\n\n<template>\n <PinInputRoot\n v-bind=\"{ ...groupAttrs, ...groupFieldAttrs, ...optional({ name: field.name }) }\"\n :model-value=\"cells\"\n :type=\"pinType\"\n :mask=\"mask\"\n :disabled=\"field.disabled\"\n otp\n role=\"group\"\n :aria-label=\"ariaLabelledby ? undefined : ariaLabel\"\n :aria-labelledby=\"ariaLabelledby || (!ariaLabel ? field.labelledBy : undefined)\"\n :data-invalid=\"field.invalid || undefined\"\n :class=\"cn('inline-flex items-center gap-2', attrs.class as string)\"\n @update:model-value=\"onUpdate\"\n >\n <!-- The `:aria-label` replaces the one Reka writes inside its own render\n function — \"pin input 3 of 6\", the field's whole purpose restated once\n per cell. It reaches the DOM node as a fallthrough attribute, which Vue\n merges last, so it is a replacement rather than a second name. Removing\n it does not fall back to nothing; it falls back to Reka's English. The\n group carries the purpose, and a cell carries only where it sits. -->\n <PinInputInput\n v-for=\"cell in length\"\n :key=\"cell\"\n :index=\"cell - 1\"\n :tabindex=\"cell - 1 === tabStopIndex ? 0 : -1\"\n :aria-label=\"text.cell({ index: cell, length, type })\"\n :aria-required=\"field.required || undefined\"\n :aria-invalid=\"field.invalid || undefined\"\n :data-filled=\"cell <= code.length || undefined\"\n :class=\"\n cn(\n // Square, on the control scale's tallest step: a code cell is a\n // touch target before it is a form row, and 44px is the size a thumb\n // is aimed at. `tabular` keeps the six glyphs on one width so the\n // row does not breathe as it fills.\n 'tabular h-11 w-11 rounded-md border border-input bg-background text-center text-base text-foreground',\n 'transition-[color,background-color,border-color,box-shadow] duration-fast ease-out',\n // Rim-lit at rest; the ring blooms on the cell being typed into.\n 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring',\n !field.invalid && 'focus-visible:shadow-halo',\n // A filled cell is legible as filled from its character — or its\n // mask dot — alone. The heavier border is a second, redundant cue,\n // and a weight rather than a hue, so it survives both a colour\n // deficiency and forced-colors.\n //\n // Withheld from an unavailable row, and only because the border is\n // also the third channel of the disabled state below: two rules\n // moving the same rim in opposite directions would leave a\n // half-filled disabled row with a heavier edge than an empty one,\n // which reads as the more available of the two. The cue is redundant\n // by construction, so the state wins it.\n !field.disabled && 'data-[filled]:border-border-strong',\n // Unavailable is a *colour* and a *weight*, never an opacity. A cell\n // holds one character — or one mask dot — and that is the entire\n // content of the control, so `opacity-50` here did not dim a code so\n // much as erase it: `--color-foreground` measures 14.09:1 on the\n // resting fill and 2.99:1 once composited at half alpha. The drained\n // fill carries the state instead, the character stays at a measured\n // 4.68:1, and the rim slackens from `input` to `border` so the state\n // is not told in hue alone.\n //\n // The fill and the text colour are `disabled:` variants rather than\n // plain classes, so each outranks the resting `bg-background\n // text-foreground` above on specificity rather than on where Tailwind\n // happened to emit it. The border cannot join them: a `disabled:`\n // variant would outrank the plain `border-destructive` below, and a\n // row that is both in error and unavailable has to keep saying so.\n 'disabled:cursor-not-allowed disabled:bg-muted disabled:text-muted-foreground',\n field.disabled && 'border-border',\n field.invalid && 'border-destructive focus-visible:outline-destructive',\n )\n \"\n />\n </PinInputRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;;AAOA,IAAa,mBAAmC,EAC9C,OAAO,EAAE,OAAO,QAAQ,WACtB,GAAG,SAAS,YAAY,UAAU,YAAY,GAAG,OAAO,KAAK,EAAE,MAAM,OAAO,MAAM,IACtF;;;;;;;;;;;;;;;;;;;;;;;;;;EAmEA,MAAM,QAAQ;EA6Cd,MAAM,OAAO;EAUb,MAAM,OAAO,UAAU,YAAY,wBAAwB,MAAM,MAAM;EASvE,MAAM,EAAE,OAAO,MAAM,eAAe,cAAc;EASlD,MAAM,QAAQ,uBAAuB;GACnC,IAAI,MAAM;GACV,aAAa,MAAM;GACnB,gBAAgB,MAAM;GACtB,UAAU,MAAM;GAChB,SAAS,MAAM;EACjB,EAAE;EAoBF,MAAM,kBAAkB,eAAe;GAIrC,MAAM,QAAiC,EAAE,GAAG,MAAM,MAAM;GACxD,OAAO,MAAM;GACb,OAAO,MAAM;GACb,OAAO,MAAM;GACb,OAAO;EACT,CAAC;EAQD,MAAM,OAAO,IAAI,MAAM,cAAc,EAAE;EACvC,YACQ,MAAM,aACX,UAAU;GACT,IAAI,UAAU,KAAA,GAAW,KAAK,QAAQ;EACxC,CACF;EAIA,MAAM,QAAQ,eAAe,KAAK,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC;EAKxE,MAAM,UAAU,eAAgB,MAAM,SAAS,YAAY,WAAW,MAAO;EAK7E,MAAM,eAAe,eAAe,KAAK,IAAI,KAAK,MAAM,QAAQ,MAAM,SAAS,CAAC,CAAC;EAKjF,SAAS,SAAS,MAA2B;GAM3C,MAAM,QAAQ,KAAK,KAAK,EAAE;GAK1B,IAAI,UAAU,KAAK,OAAO;GAC1B,KAAK,QAAQ;GACb,KAAK,qBAAqB,KAAK;GAK/B,IAAI,MAAM,WAAW,MAAM,QAAQ,KAAK,YAAY,KAAK;EAC3D;;GAIE,OAAA,UAAA,GAAA,YAyEe,MAAA,YAAA,GAzEf,WAyEe;IAAA,GAxEA,MAAA,UAAA;IAAU,GAAK,gBAAA;IAAe,GAAK,MAAA,QAAA,CAAQ,CAAA,EAAA,MAAS,MAAA,KAAA,CAAK,CAAC,KAAI,CAAA;GAAA,GAAA;IAC1E,eAAa,MAAA;IACb,MAAM,QAAA;IACN,MAAM,QAAA;IACN,UAAU,MAAA,KAAA,CAAK,CAAC;IACjB,KAAA;IACA,MAAK;IACJ,cAAY,QAAA,iBAAiB,KAAA,IAAY,QAAA;IACzC,mBAAiB,QAAA,mBAAc,CAAM,QAAA,YAAY,MAAA,KAAA,CAAK,CAAC,aAAa,KAAA;IACpE,gBAAc,MAAA,KAAA,CAAK,CAAC,WAAW,KAAA;IAC/B,OAAO,MAAA,EAAA,CAAE,CAAA,kCAAmC,MAAA,KAAA,CAAK,CAAC,KAAK;IACvD,uBAAoB;;IASnB,SAAA,cAAsB,EADxB,UAAA,IAAA,GAAA,mBAoDE,UAAA,MAAA,WAnDe,QAAA,SAAR,SAAI;KADb,OAAA,UAAA,GAAA,YAoDE,MAAA,aAAA,GAAA;MAlDC,KAAK;MACL,OAAO,OAAI;MACX,UAAU,OAAI,MAAS,aAAA,QAAY,IAAA;MACnC,cAAY,MAAA,IAAA,CAAI,CAAC,KAAI;OAAA,OAAU;OAAI,QAAE,QAAA;OAAM,MAAE,QAAA;MAAI,CAAA;MACjD,iBAAe,MAAA,KAAA,CAAK,CAAC,YAAY,KAAA;MACjC,gBAAc,MAAA,KAAA,CAAK,CAAC,WAAW,KAAA;MAC/B,eAAa,QAAQ,KAAA,MAAK,UAAU,KAAA;MACpC,OAAK,eAAW,MAAA,EAAA,CAAE,CAAA,wGAAA,sFAAA,qFAAiqB,CAAA,MAAA,KAAA,CAAK,CAAC,WAAO,6BAAwvB,CAAA,MAAA,KAAA,CAAK,CAAC,YAAQ,sCAAA,gFAA2rC,MAAA,KAAA,CAAK,CAAC,YAAQ,iBAA+B,MAAA,KAAA,CAAK,CAAC,WAAO,sDAAA,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pagination.js","names":[],"sources":["../../../../packages/primitives/pagination/src/Pagination.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { PaginationLabels } from \"@ecoma-io/loom-labels\";\nimport { cva } from \"class-variance-authority\";\n\n/**\n\n/**\n * Loom's English, co-located with the component so it tree-shakes with it: a\n * consumer who never imports Pagination ships none of these bytes.\n *\n * Exported so a host can build a partial vocabulary against the real thing\n * rather than a transcription of it.\n */\nexport const PAGINATION_LABELS: PaginationLabels = {\n nav: \"Pagination\",\n first: \"First page\",\n previous: \"Previous page\",\n next: \"Next page\",\n last: \"Last page\",\n page: ({ page }) => `Page ${String(page)}`,\n position: ({ page, pageCount }) => `Page ${String(page)} of ${String(pageCount)}`,\n ellipsis: \"More pages\",\n};\n\n/**\n * How much of the paging apparatus is rendered. All three drive the same\n * model and carry the same semantics — they differ only in how much room the\n * navigation is worth on the surface it sits on.\n *\n * • `full` — the numbered row with ellipses, plus first/prev/next/last.\n * For a table where jumping to a known page is a real task.\n * • `compact` — prev, \"Page 3 of 12\", next. The position stays legible\n * without spending a whole row on it.\n * • `simple` — prev and next alone, for a feed nobody navigates by index.\n */\nexport type PaginationVariant = \"full\" | \"compact\" | \"simple\";\n\nexport const paginationVariants = cva(\"inline-flex items-center\", {\n // `satisfies` keeps the two halves honest: the union above is the name a\n // consumer imports and the documentation prints, the map below is the\n // classes. Adding a member to one and not the other stops compiling.\n variants: {\n variant: {\n // The numbered row is a run of same-size squares and reads as one strip\n // at gap-1; the compact form is a sentence between two controls and\n // needs the wider gap to stop reading as a third button.\n full: \"gap-1\",\n compact: \"gap-2\",\n simple: \"gap-1\",\n } satisfies Record<PaginationVariant, string>,\n },\n defaultVariants: { variant: \"full\" },\n});\n</script>\n\n<script setup lang=\"ts\">\nimport { nextTick } from \"vue\";\nimport {\n PaginationRoot,\n PaginationList,\n PaginationListItem,\n PaginationFirst,\n PaginationPrev,\n PaginationNext,\n PaginationLast,\n PaginationEllipsis,\n} from \"reka-ui\";\nimport { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from \"@lucide/vue\";\nimport { buttonVariants } from \"@ecoma-io/loom-button\";\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 * Pagination — moving through a paged result set, one page at a time or by\n * jumping to a numbered one.\n *\n * It renders **buttons, never links**. Loom cannot know whether a consumer's\n * pages are addressable URLs, and an `<a>` with no `href` is not a link at\n * all — it is a div that lies to a screen reader and drops out of the tab\n * order. A product whose pages are real, crawlable URLs wants its own\n * `<a href>` row instead of this component; nothing here can be made to\n * produce one.\n *\n * Reach for a different control when the list is not paged: an infinite feed\n * wants a \"load more\" button, and a long list a reader scans rather than\n * indexes wants a virtualised scroller.\n *\n * Built on Reka UI's Pagination, which owns the arithmetic — the page count,\n * the sliding window of numbers around the current page, and where the\n * ellipses fall. That window is fiddly enough at the boundaries that\n * recomputing it here would only be a second, subtly different answer.\n */\nconst props = withDefaults(\n defineProps<{\n /**\n * The current page, 1-based. Leave it unset to let the component keep its\n * own page and simply report changes.\n */\n page?: number;\n /** How many items exist in total, across every page. */\n total: number;\n /** How many items one page holds. With `total`, this is what fixes the page count. */\n itemsPerPage?: number;\n /** How much of the control renders: the numbered row, a position readout, or arrows alone. */\n variant?: PaginationVariant;\n /** How many numbers sit either side of the current page, in the `full` variant. */\n siblingCount?: number;\n /** Pins page 1 and the last page into the numbered row, with ellipses standing in for the gap. */\n showEdges?: boolean;\n /** Unavailable: every control dims and refuses to move the page. */\n disabled?: boolean;\n /**\n * Names for everything this control says out loud, as any subset of\n * `PaginationLabels` — the rest stay as the host's `provideLoomLabels`\n * vocabulary left them, and then as Loom's English.\n *\n * This is the per-instance correction, not the place to localise an\n * application: the case it exists for is the two paginations above and\n * below one table, which must not share `labels.nav`.\n */\n labels?: LabelOverrides<PaginationLabels>;\n }>(),\n {\n itemsPerPage: 10,\n variant: \"full\",\n siblingCount: 1,\n // Reka defaults this off, which drops the ellipses entirely and lets the\n // numbered row float free of both ends. The anchored form is what people\n // mean by a full pagination, so Loom turns it on.\n showEdges: true,\n disabled: false,\n },\n);\n\nconst emit = defineEmits<{\n /** The 1-based page the reader asked for. A controlled host decides whether to honour it. */\n \"update:page\": [page: number];\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(\"pagination\", PAGINATION_LABELS, () => props.labels);\n\n// The root renders the `<nav>`, so every fallthrough attribute belongs on it —\n// there is no second node they could sensibly describe. `class` is pulled out\n// of that spread and merged through `cn()` instead: spreading it alongside the\n// nav's own `:class` only concatenates the two lists, so a caller's `gap-2`\n// would win or lose depending on which utility Tailwind happened to emit last.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: navAttrs } = useSplitAttrs();\n\n// The edge controls used to keep the dim on the theory that a chevron has no\n// text for an alpha to take down — but the library-wide rule settled since is\n// drained, not dimmed, full stop: the same well every unavailable control\n// wears, so a disabled pager reads as one treatment rather than two. WCAG\n// exempts an inactive control from 1.4.11 either way; what decides it here is\n// that a drained plate is how \"no further this way\" looks everywhere else in\n// Loom.\nconst edgeClass = cn(\n buttonVariants({ variant: \"ghost\", size: \"icon-sm\" }),\n // The edges assert `w-8`, a fixed 32px square, but they sit in a flex row\n // with `flex-shrink: 1` and no min-width, so a row narrower than its content\n // (a phone-width column) collapses them to the 16px chevron — a touch target\n // below WCAG 2.5.8's 24px floor. The page numbers already floor themselves\n // with `min-w-8`; the edges must refuse to shrink instead, leaving the row to\n // overflow rather than ever compressing a control's own target.\n \"shrink-0 disabled:cursor-not-allowed disabled:bg-muted disabled:text-muted-foreground disabled:shadow-none\",\n);\n\n// The page buttons borrow Button's own variants rather than restating its\n// press language, so the row cannot drift from every other pressable thing in\n// the library. `min-w-8` with tabular figures is what keeps the row from\n// reflowing as the numbers under it grow a digit.\nfunction pageClass(selected: boolean): string {\n return cn(\n buttonVariants({ variant: selected ? \"primary\" : \"ghost\", size: \"sm\" }),\n \"min-w-8 px-2 tabular\",\n // No `opacity` here, unlike the edges above, because this button's content\n // *is* text — the page number, the one thing a reader needs from a pager\n // that is briefly out of service. `disabled:opacity-50` composited the\n // ghost variant's `--color-muted-foreground` down to 2.05:1 and the current\n // page's white-on-primary label down to 2.48:1; halving the alpha more than\n // halves the contrast. Unavailability is painted in measured colours\n // instead.\n //\n // The two branches drain to different wells on purpose, which is Chip's\n // rule applied to a row of numbers: an unselected page falls to the neutral\n // well at 4.67:1, and the current page keeps the primary wash it already wears\n // at 6.84:1. Which page a reader is on is information, and a disabled pager\n // that forgets it has answered the question by refusing to answer it.\n selected\n ? \"disabled:cursor-not-allowed disabled:bg-primary-muted disabled:text-primary-text disabled:shadow-none\"\n : \"disabled:cursor-not-allowed disabled:bg-muted disabled:text-muted-foreground\",\n );\n}\n\n/** Which edge control stands in for which, when the one that was pressed goes away. */\nconst MIRROR: Record<string, string | undefined> = {\n first: \"last\",\n prev: \"next\",\n next: \"prev\",\n last: \"first\",\n};\n\n// Reka disables Prev/First at page 1 and Next/Last at the last page, and that\n// is the honest state: a control that looks unavailable and still fires is\n// worse than one that is not there at all. The cost is real, though — a\n// browser blurs a button the instant it becomes disabled, so a reader holding\n// Enter on Next arrives at the last page with focus dumped on `<body>` and\n// their place in the document gone.\n//\n// So the control that fired the change hands focus on: to the current page\n// button where the variant has one — which announces \"Page 12, current page\",\n// the very thing the reader was trying to find out — and to its mirror control\n// otherwise, which is the nearest thing still able to move the page.\n//\n// Only for a keyboard activation. `detail === 0` is what separates a click\n// synthesised by Enter or Space from a pointer click, and a pointer user has\n// no focus position to lose; moving focus for them would raise a ring nobody\n// asked for.\nlet pressed: HTMLButtonElement | null = null;\n\nfunction rememberKeyboardPress(event: MouseEvent): void {\n const target = event.target as HTMLElement | null;\n pressed = event.detail === 0 ? (target?.closest<HTMLButtonElement>(\"button\") ?? null) : null;\n}\n\nfunction handOffFocus(): void {\n const origin = pressed;\n pressed = null;\n if (!origin?.disabled) return;\n\n // Only rescue focus that was actually lost. A browser leaves `<body>`\n // focused after disabling the button; jsdom leaves the button itself. Any\n // other active element means the reader has already moved on, and stealing\n // focus back from them would be the worse bug.\n const active = document.activeElement;\n if (active !== origin && active !== document.body) return;\n\n const nav = origin.closest(\"nav\");\n if (!nav) return;\n\n const mirror = MIRROR[origin.getAttribute(\"data-edge\") ?? \"\"];\n const target =\n nav.querySelector<HTMLButtonElement>('[aria-current=\"page\"]:not([disabled])') ??\n (mirror\n ? nav.querySelector<HTMLButtonElement>(`[data-edge=\"${mirror}\"]:not([disabled])`)\n : null);\n target?.focus();\n}\n\nfunction onPageChange(value: number): void {\n emit(\"update:page\", value);\n // After the re-render rather than before it: the button that was pressed\n // only becomes disabled once the new page has been applied.\n void nextTick(handOffFocus);\n}\n</script>\n\n<template>\n <PaginationRoot\n v-slot=\"{ page: current, pageCount }\"\n v-bind=\"{ ...navAttrs, ...optional({ page }) }\"\n :total=\"total\"\n :items-per-page=\"itemsPerPage\"\n :sibling-count=\"siblingCount\"\n :show-edges=\"showEdges\"\n :disabled=\"disabled\"\n :aria-label=\"text.nav\"\n :class=\"cn(paginationVariants({ variant }), attrs.class as string)\"\n @click=\"rememberKeyboardPress\"\n @update:page=\"onPageChange\"\n >\n <!-- Every `:aria-label` from here down replaces one Reka writes in English\n inside its own render function. It reaches the DOM node as a\n fallthrough attribute, which Vue merges last, so it is a replacement\n rather than a second name. Removing one does not fall back to nothing;\n it falls back to Reka's English, which is the failure this whole seam\n exists to close and which no test of Loom's own strings would catch. -->\n <PaginationFirst\n v-if=\"variant === 'full'\"\n data-edge=\"first\"\n :aria-label=\"text.first\"\n :class=\"edgeClass\"\n >\n <ChevronsLeft class=\"h-4 w-4\" aria-hidden=\"true\" />\n </PaginationFirst>\n\n <PaginationPrev data-edge=\"prev\" :aria-label=\"text.previous\" :class=\"edgeClass\">\n <ChevronLeft class=\"h-4 w-4\" aria-hidden=\"true\" />\n </PaginationPrev>\n\n <PaginationList v-if=\"variant === 'full'\" v-slot=\"{ items }\" class=\"flex items-center gap-1\">\n <!-- Keyed by page number, never by index. The row is furniture: a page\n that stays inside the window across a change keeps its own DOM node,\n so nothing remounts, nothing re-animates, and the numbers do not\n shuffle under a reader who has just pressed Next. -->\n <template\n v-for=\"(item, index) in items\"\n :key=\"item.type === 'page' ? `page-${item.value}` : `ellipsis-${index}`\"\n >\n <PaginationListItem\n v-if=\"item.type === 'page'\"\n :value=\"item.value\"\n :aria-label=\"text.page({ page: item.value })\"\n :class=\"pageClass(item.value === current)\"\n />\n <!-- The ellipsis is not a control, so it renders as a span rather than\n Reka's default div-with-a-glyph inside a row of buttons: nothing to\n focus, nothing that looks pressable, and the glyph itself hidden\n from assistive technology because \"horizontal ellipsis\" is not what\n it means. The words are what it means. -->\n <PaginationEllipsis\n v-else\n as=\"span\"\n class=\"inline-flex h-8 min-w-8 select-none items-center justify-center text-sm text-muted-foreground\"\n >\n <span aria-hidden=\"true\">…</span>\n <span class=\"sr-only\">{{ text.ellipsis }}</span>\n </PaginationEllipsis>\n </template>\n </PaginationList>\n\n <!-- The position, for the two variants that have no numbered row to read it\n off. `role=\"status\"` because pressing Next in those variants otherwise\n changes nothing a screen reader would notice — focus stays put and no\n `aria-current` moves. The `full` variant deliberately has no live\n region: its number row already carries the state, and announcing it\n again on top of the focus hand-off below would say everything twice. -->\n <!-- One string rather than three nodes with the numbers between them: a\n language that puts the count before the word, or drops the preposition\n entirely, cannot reorder markup this component emits. `tabular` moves\n from the two number spans to the whole readout, which is the only real\n cost — the \"of\" between them is now tabular too, and the row of digits\n it was steadying is not rendered in either of these variants. -->\n <span\n v-if=\"variant !== 'full'\"\n role=\"status\"\n :class=\"variant === 'compact' ? 'px-1 text-sm tabular text-muted-foreground' : 'sr-only'\"\n >\n {{ text.position({ page: current, pageCount }) }}\n </span>\n\n <PaginationNext data-edge=\"next\" :aria-label=\"text.next\" :class=\"edgeClass\">\n <ChevronRight class=\"h-4 w-4\" aria-hidden=\"true\" />\n </PaginationNext>\n\n <PaginationLast\n v-if=\"variant === 'full'\"\n data-edge=\"last\"\n :aria-label=\"text.last\"\n :class=\"edgeClass\"\n >\n <ChevronsRight class=\"h-4 w-4\" aria-hidden=\"true\" />\n </PaginationLast>\n </PaginationRoot>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pagination.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../../packages/primitives/pagination/src/Pagination.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { PaginationLabels } from \"@ecoma-io/loom-labels\";\nimport { cva } from \"class-variance-authority\";\n\n/**\n\n/**\n * Loom's English, co-located with the component so it tree-shakes with it: a\n * consumer who never imports Pagination ships none of these bytes.\n *\n * Exported so a host can build a partial vocabulary against the real thing\n * rather than a transcription of it.\n */\nexport const PAGINATION_LABELS: PaginationLabels = {\n nav: \"Pagination\",\n first: \"First page\",\n previous: \"Previous page\",\n next: \"Next page\",\n last: \"Last page\",\n page: ({ page }) => `Page ${String(page)}`,\n position: ({ page, pageCount }) => `Page ${String(page)} of ${String(pageCount)}`,\n ellipsis: \"More pages\",\n};\n\n/**\n * How much of the paging apparatus is rendered. All three drive the same\n * model and carry the same semantics — they differ only in how much room the\n * navigation is worth on the surface it sits on.\n *\n * • `full` — the numbered row with ellipses, plus first/prev/next/last.\n * For a table where jumping to a known page is a real task.\n * • `compact` — prev, \"Page 3 of 12\", next. The position stays legible\n * without spending a whole row on it.\n * • `simple` — prev and next alone, for a feed nobody navigates by index.\n */\nexport type PaginationVariant = \"full\" | \"compact\" | \"simple\";\n\nexport const paginationVariants = cva(\"inline-flex items-center\", {\n // `satisfies` keeps the two halves honest: the union above is the name a\n // consumer imports and the documentation prints, the map below is the\n // classes. Adding a member to one and not the other stops compiling.\n variants: {\n variant: {\n // The numbered row is a run of same-size squares and reads as one strip\n // at gap-1; the compact form is a sentence between two controls and\n // needs the wider gap to stop reading as a third button.\n full: \"gap-1\",\n compact: \"gap-2\",\n simple: \"gap-1\",\n } satisfies Record<PaginationVariant, string>,\n },\n defaultVariants: { variant: \"full\" },\n});\n</script>\n\n<script setup lang=\"ts\">\nimport { nextTick } from \"vue\";\nimport {\n PaginationRoot,\n PaginationList,\n PaginationListItem,\n PaginationFirst,\n PaginationPrev,\n PaginationNext,\n PaginationLast,\n PaginationEllipsis,\n} from \"reka-ui\";\nimport { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from \"@lucide/vue\";\nimport { buttonVariants } from \"@ecoma-io/loom-button\";\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 * Pagination — moving through a paged result set, one page at a time or by\n * jumping to a numbered one.\n *\n * It renders **buttons, never links**. Loom cannot know whether a consumer's\n * pages are addressable URLs, and an `<a>` with no `href` is not a link at\n * all — it is a div that lies to a screen reader and drops out of the tab\n * order. A product whose pages are real, crawlable URLs wants its own\n * `<a href>` row instead of this component; nothing here can be made to\n * produce one.\n *\n * Reach for a different control when the list is not paged: an infinite feed\n * wants a \"load more\" button, and a long list a reader scans rather than\n * indexes wants a virtualised scroller.\n *\n * Built on Reka UI's Pagination, which owns the arithmetic — the page count,\n * the sliding window of numbers around the current page, and where the\n * ellipses fall. That window is fiddly enough at the boundaries that\n * recomputing it here would only be a second, subtly different answer.\n */\nconst props = withDefaults(\n defineProps<{\n /**\n * The current page, 1-based. Leave it unset to let the component keep its\n * own page and simply report changes.\n */\n page?: number;\n /** How many items exist in total, across every page. */\n total: number;\n /** How many items one page holds. With `total`, this is what fixes the page count. */\n itemsPerPage?: number;\n /** How much of the control renders: the numbered row, a position readout, or arrows alone. */\n variant?: PaginationVariant;\n /** How many numbers sit either side of the current page, in the `full` variant. */\n siblingCount?: number;\n /** Pins page 1 and the last page into the numbered row, with ellipses standing in for the gap. */\n showEdges?: boolean;\n /** Unavailable: every control dims and refuses to move the page. */\n disabled?: boolean;\n /**\n * Names for everything this control says out loud, as any subset of\n * `PaginationLabels` — the rest stay as the host's `provideLoomLabels`\n * vocabulary left them, and then as Loom's English.\n *\n * This is the per-instance correction, not the place to localise an\n * application: the case it exists for is the two paginations above and\n * below one table, which must not share `labels.nav`.\n */\n labels?: LabelOverrides<PaginationLabels>;\n }>(),\n {\n itemsPerPage: 10,\n variant: \"full\",\n siblingCount: 1,\n // Reka defaults this off, which drops the ellipses entirely and lets the\n // numbered row float free of both ends. The anchored form is what people\n // mean by a full pagination, so Loom turns it on.\n showEdges: true,\n disabled: false,\n },\n);\n\nconst emit = defineEmits<{\n /** The 1-based page the reader asked for. A controlled host decides whether to honour it. */\n \"update:page\": [page: number];\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(\"pagination\", PAGINATION_LABELS, () => props.labels);\n\n// The root renders the `<nav>`, so every fallthrough attribute belongs on it —\n// there is no second node they could sensibly describe. `class` is pulled out\n// of that spread and merged through `cn()` instead: spreading it alongside the\n// nav's own `:class` only concatenates the two lists, so a caller's `gap-2`\n// would win or lose depending on which utility Tailwind happened to emit last.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: navAttrs } = useSplitAttrs();\n\n// The edge controls used to keep the dim on the theory that a chevron has no\n// text for an alpha to take down — but the library-wide rule settled since is\n// drained, not dimmed, full stop: the same well every unavailable control\n// wears, so a disabled pager reads as one treatment rather than two. WCAG\n// exempts an inactive control from 1.4.11 either way; what decides it here is\n// that a drained plate is how \"no further this way\" looks everywhere else in\n// Loom.\nconst edgeClass = cn(\n buttonVariants({ variant: \"ghost\", size: \"icon-sm\" }),\n // The edges assert `w-8`, a fixed 32px square, but they sit in a flex row\n // with `flex-shrink: 1` and no min-width, so a row narrower than its content\n // (a phone-width column) collapses them to the 16px chevron — a touch target\n // below WCAG 2.5.8's 24px floor. The page numbers already floor themselves\n // with `min-w-8`; the edges must refuse to shrink instead, leaving the row to\n // overflow rather than ever compressing a control's own target.\n \"shrink-0 disabled:cursor-not-allowed disabled:bg-muted disabled:text-muted-foreground disabled:shadow-none\",\n);\n\n// The page buttons borrow Button's own variants rather than restating its\n// press language, so the row cannot drift from every other pressable thing in\n// the library. `min-w-8` with tabular figures is what keeps the row from\n// reflowing as the numbers under it grow a digit.\nfunction pageClass(selected: boolean): string {\n return cn(\n buttonVariants({ variant: selected ? \"primary\" : \"ghost\", size: \"sm\" }),\n \"min-w-8 px-2 tabular\",\n // No `opacity` here, unlike the edges above, because this button's content\n // *is* text — the page number, the one thing a reader needs from a pager\n // that is briefly out of service. `disabled:opacity-50` composited the\n // ghost variant's `--color-muted-foreground` down to 2.05:1 and the current\n // page's white-on-primary label down to 2.48:1; halving the alpha more than\n // halves the contrast. Unavailability is painted in measured colours\n // instead.\n //\n // The two branches drain to different wells on purpose, which is Chip's\n // rule applied to a row of numbers: an unselected page falls to the neutral\n // well at 4.67:1, and the current page keeps the primary wash it already wears\n // at 6.84:1. Which page a reader is on is information, and a disabled pager\n // that forgets it has answered the question by refusing to answer it.\n selected\n ? \"disabled:cursor-not-allowed disabled:bg-primary-muted disabled:text-primary-text disabled:shadow-none\"\n : \"disabled:cursor-not-allowed disabled:bg-muted disabled:text-muted-foreground\",\n );\n}\n\n/** Which edge control stands in for which, when the one that was pressed goes away. */\nconst MIRROR: Record<string, string | undefined> = {\n first: \"last\",\n prev: \"next\",\n next: \"prev\",\n last: \"first\",\n};\n\n// Reka disables Prev/First at page 1 and Next/Last at the last page, and that\n// is the honest state: a control that looks unavailable and still fires is\n// worse than one that is not there at all. The cost is real, though — a\n// browser blurs a button the instant it becomes disabled, so a reader holding\n// Enter on Next arrives at the last page with focus dumped on `<body>` and\n// their place in the document gone.\n//\n// So the control that fired the change hands focus on: to the current page\n// button where the variant has one — which announces \"Page 12, current page\",\n// the very thing the reader was trying to find out — and to its mirror control\n// otherwise, which is the nearest thing still able to move the page.\n//\n// Only for a keyboard activation. `detail === 0` is what separates a click\n// synthesised by Enter or Space from a pointer click, and a pointer user has\n// no focus position to lose; moving focus for them would raise a ring nobody\n// asked for.\nlet pressed: HTMLButtonElement | null = null;\n\nfunction rememberKeyboardPress(event: MouseEvent): void {\n const target = event.target as HTMLElement | null;\n pressed = event.detail === 0 ? (target?.closest<HTMLButtonElement>(\"button\") ?? null) : null;\n}\n\nfunction handOffFocus(): void {\n const origin = pressed;\n pressed = null;\n if (!origin?.disabled) return;\n\n // Only rescue focus that was actually lost. A browser leaves `<body>`\n // focused after disabling the button; jsdom leaves the button itself. Any\n // other active element means the reader has already moved on, and stealing\n // focus back from them would be the worse bug.\n const active = document.activeElement;\n if (active !== origin && active !== document.body) return;\n\n const nav = origin.closest(\"nav\");\n if (!nav) return;\n\n const mirror = MIRROR[origin.getAttribute(\"data-edge\") ?? \"\"];\n const target =\n nav.querySelector<HTMLButtonElement>('[aria-current=\"page\"]:not([disabled])') ??\n (mirror\n ? nav.querySelector<HTMLButtonElement>(`[data-edge=\"${mirror}\"]:not([disabled])`)\n : null);\n target?.focus();\n}\n\nfunction onPageChange(value: number): void {\n emit(\"update:page\", value);\n // After the re-render rather than before it: the button that was pressed\n // only becomes disabled once the new page has been applied.\n void nextTick(handOffFocus);\n}\n</script>\n\n<template>\n <PaginationRoot\n v-slot=\"{ page: current, pageCount }\"\n v-bind=\"{ ...navAttrs, ...optional({ page }) }\"\n :total=\"total\"\n :items-per-page=\"itemsPerPage\"\n :sibling-count=\"siblingCount\"\n :show-edges=\"showEdges\"\n :disabled=\"disabled\"\n :aria-label=\"text.nav\"\n :class=\"cn(paginationVariants({ variant }), attrs.class as string)\"\n @click=\"rememberKeyboardPress\"\n @update:page=\"onPageChange\"\n >\n <!-- Every `:aria-label` from here down replaces one Reka writes in English\n inside its own render function. It reaches the DOM node as a\n fallthrough attribute, which Vue merges last, so it is a replacement\n rather than a second name. Removing one does not fall back to nothing;\n it falls back to Reka's English, which is the failure this whole seam\n exists to close and which no test of Loom's own strings would catch. -->\n <PaginationFirst\n v-if=\"variant === 'full'\"\n data-edge=\"first\"\n :aria-label=\"text.first\"\n :class=\"edgeClass\"\n >\n <ChevronsLeft class=\"h-4 w-4\" aria-hidden=\"true\" />\n </PaginationFirst>\n\n <PaginationPrev data-edge=\"prev\" :aria-label=\"text.previous\" :class=\"edgeClass\">\n <ChevronLeft class=\"h-4 w-4\" aria-hidden=\"true\" />\n </PaginationPrev>\n\n <PaginationList v-if=\"variant === 'full'\" v-slot=\"{ items }\" class=\"flex items-center gap-1\">\n <!-- Keyed by page number, never by index. The row is furniture: a page\n that stays inside the window across a change keeps its own DOM node,\n so nothing remounts, nothing re-animates, and the numbers do not\n shuffle under a reader who has just pressed Next. -->\n <template\n v-for=\"(item, index) in items\"\n :key=\"item.type === 'page' ? `page-${item.value}` : `ellipsis-${index}`\"\n >\n <PaginationListItem\n v-if=\"item.type === 'page'\"\n :value=\"item.value\"\n :aria-label=\"text.page({ page: item.value })\"\n :class=\"pageClass(item.value === current)\"\n />\n <!-- The ellipsis is not a control, so it renders as a span rather than\n Reka's default div-with-a-glyph inside a row of buttons: nothing to\n focus, nothing that looks pressable, and the glyph itself hidden\n from assistive technology because \"horizontal ellipsis\" is not what\n it means. The words are what it means. -->\n <PaginationEllipsis\n v-else\n as=\"span\"\n class=\"inline-flex h-8 min-w-8 select-none items-center justify-center text-sm text-muted-foreground\"\n >\n <span aria-hidden=\"true\">…</span>\n <span class=\"sr-only\">{{ text.ellipsis }}</span>\n </PaginationEllipsis>\n </template>\n </PaginationList>\n\n <!-- The position, for the two variants that have no numbered row to read it\n off. `role=\"status\"` because pressing Next in those variants otherwise\n changes nothing a screen reader would notice — focus stays put and no\n `aria-current` moves. The `full` variant deliberately has no live\n region: its number row already carries the state, and announcing it\n again on top of the focus hand-off below would say everything twice. -->\n <!-- One string rather than three nodes with the numbers between them: a\n language that puts the count before the word, or drops the preposition\n entirely, cannot reorder markup this component emits. `tabular` moves\n from the two number spans to the whole readout, which is the only real\n cost — the \"of\" between them is now tabular too, and the row of digits\n it was steadying is not rendered in either of these variants. -->\n <span\n v-if=\"variant !== 'full'\"\n role=\"status\"\n :class=\"variant === 'compact' ? 'px-1 text-sm tabular text-muted-foreground' : 'sr-only'\"\n >\n {{ text.position({ page: current, pageCount }) }}\n </span>\n\n <PaginationNext data-edge=\"next\" :aria-label=\"text.next\" :class=\"edgeClass\">\n <ChevronRight class=\"h-4 w-4\" aria-hidden=\"true\" />\n </PaginationNext>\n\n <PaginationLast\n v-if=\"variant === 'full'\"\n data-edge=\"last\"\n :aria-label=\"text.last\"\n :class=\"edgeClass\"\n >\n <ChevronsRight class=\"h-4 w-4\" aria-hidden=\"true\" />\n </PaginationLast>\n </PaginationRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAaA,IAAa,oBAAsC;CACjD,KAAK;CACL,OAAO;CACP,UAAU;CACV,MAAM;CACN,MAAM;CACN,OAAO,EAAE,WAAW,QAAQ,OAAO,IAAI;CACvC,WAAW,EAAE,MAAM,gBAAgB,QAAQ,OAAO,IAAI,EAAE,MAAM,OAAO,SAAS;CAC9E,UAAU;AACZ;AAeA,IAAa,qBAAqB,IAAI,4BAA4B;CAIhE,UAAU,EACR,SAAS;EAIP,MAAM;EACN,SAAS;EACT,QAAQ;CACV,EACF;CACA,iBAAiB,EAAE,SAAS,OAAO;AACrC,CAAC;;;;;;;;;;;;;;;;;;;;;;EA0CD,MAAM,QAAQ;EA0Cd,MAAM,OAAO;EAQb,MAAM,OAAO,UAAU,cAAc,yBAAyB,MAAM,MAAM;EAQ1E,MAAM,EAAE,OAAO,MAAM,aAAa,cAAc;EAShD,MAAM,YAAY,GAChB,eAAe;GAAE,SAAS;GAAS,MAAM;EAAU,CAAC,GAOpD,4GACF;EAMA,SAAS,UAAU,UAA2B;GAC5C,OAAO,GACL,eAAe;IAAE,SAAS,WAAW,YAAY;IAAS,MAAM;GAAK,CAAC,GACtE,wBAcA,WACI,0GACA,8EACN;EACF;;EAGA,MAAM,SAA6C;GACjD,OAAO;GACP,MAAM;GACN,MAAM;GACN,MAAM;EACR;EAkBA,IAAI,UAAoC;EAExC,SAAS,sBAAsB,OAAyB;GACtD,MAAM,SAAS,MAAM;GACrB,UAAU,MAAM,WAAW,IAAK,QAAQ,QAA2B,QAAQ,KAAK,OAAQ;EAC1F;EAEA,SAAS,eAAqB;GAC5B,MAAM,SAAS;GACf,UAAU;GACV,IAAI,CAAC,QAAQ,UAAU;GAMvB,MAAM,SAAS,SAAS;GACxB,IAAI,WAAW,UAAU,WAAW,SAAS,MAAM;GAEnD,MAAM,MAAM,OAAO,QAAQ,KAAK;GAChC,IAAI,CAAC,KAAK;GAEV,MAAM,SAAS,OAAO,OAAO,aAAa,WAAW,KAAK;GAM1D,CAJE,IAAI,cAAiC,yCAAuC,MAC3E,SACG,IAAI,cAAiC,eAAe,OAAO,mBAAmB,IAC9E,MAAA,EACE,MAAM;EAChB;EAEA,SAAS,aAAa,OAAqB;GACzC,KAAK,eAAe,KAAK;GAGzB,SAAc,YAAY;EAC5B;;GAIE,OAAA,UAAA,GAAA,YA+FiB,MAAA,cAAA,GA/FjB,WA+FiB;IAAA,GA7FF,MAAA,QAAA;IAAQ,GAAK,MAAA,QAAA,CAAQ,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA;GAAA,GAAA;IACxC,OAAO,QAAA;IACP,kBAAgB,QAAA;IAChB,iBAAe,QAAA;IACf,cAAY,QAAA;IACZ,UAAU,QAAA;IACV,cAAY,MAAA,IAAA,CAAI,CAAC;IACjB,OAAO,MAAA,EAAA,CAAE,CAAC,MAAA,kBAAA,CAAkB,CAAA,EAAA,SAAG,QAAA,QAAO,CAAA,GAAK,MAAA,KAAA,CAAK,CAAC,KAAK;IACtD,SAAO;IACP,iBAAa;;IAQd,SAAA,SAOkB,EAAA,MAzBF,SAAS,gBAAS;KAmB1B,QAAA,YAAO,UADf,UAAA,GAAA,YAOkB,MAAA,eAAA,GAAA;;MALhB,aAAU;MACT,cAAY,MAAA,IAAA,CAAI,CAAC;MACjB,OAAK,eAAE,MAAA,SAAA,CAAS;;MAEjB,SAAA,cAAmD,CAAnD,YAAmD,MAAA,YAAA,GAAA;OAArC,OAAM;OAAU,eAAY;;;;KAG5C,YAEiB,MAAA,cAAA,GAAA;MAFD,aAAU;MAAQ,cAAY,MAAA,IAAA,CAAI,CAAC;MAAW,OAAK,eAAE,MAAA,SAAA,CAAS;;MAC5E,SAAA,cAAkD,CAAlD,YAAkD,MAAA,WAAA,GAAA;OAArC,OAAM;OAAU,eAAY;;;;KAGrB,QAAA,YAAO,UAA7B,UAAA,GAAA,YA6BiB,MAAA,cAAA,GAAA;;MA7B4C,OAAM;;MAM/D,SAAA,SAA8B,EANkB,YAAK,EAKvD,UAAA,IAAA,GAAA,mBAuBW,UAAA,MAAA,WAtBe,QAAhB,MAAM,UAAK;OACb,OAAA,UAAA,GAAA,mBAAA,UAAA,EAAA,KAAA,KAAK,SAAI,SAAA,QAAsB,KAAK,UAAK,YAAiB,QAAA,GAAA,CAGxD,KAAK,SAAI,UADjB,UAAA,GAAA,YAKE,MAAA,kBAAA,GAAA;;QAHC,OAAO,KAAK;QACZ,cAAY,MAAA,IAAA,CAAI,CAAC,KAAI,EAAA,MAAS,KAAK,MAAK,CAAA;QACxC,OAAK,eAAE,UAAU,KAAK,UAAU,OAAO,CAAA;;;;;OAO1C,CAAA,MAAA,UAAA,GAAA,YAOqB,MAAA,kBAAA,GAAA;;QALnB,IAAG;QACH,OAAM;;QAEN,SAAA,cAAiC,CAAjC,OAAA,OAAA,OAAA,KAAA,mBAAiC,QAAA,EAA3B,eAAY,OAAM,GAAC,KAAC,EAAA,IAC1B,mBAAgD,QAAhD,YAAgD,gBAAvB,MAAA,IAAA,CAAI,CAAC,QAAQ,GAAA,CAAA,CAAA,CAAA;;;;;;KAkBpC,QAAA,YAAO,UADf,UAAA,GAAA,mBAMO,QAAA;;MAJL,MAAK;MACJ,OAAK,eAAE,QAAA,YAAO,YAAA,+CAAA,SAAA;KAEZ,GAAA,gBAAA,MAAA,IAAA,CAAI,CAAC,SAAQ;MAAA,MAAS;MAAS;KAAS,CAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;KAG7C,YAEiB,MAAA,cAAA,GAAA;MAFD,aAAU;MAAQ,cAAY,MAAA,IAAA,CAAI,CAAC;MAAO,OAAK,eAAE,MAAA,SAAA,CAAS;;MACxE,SAAA,cAAmD,CAAnD,YAAmD,MAAA,YAAA,GAAA;OAArC,OAAM;OAAU,eAAY;;;;KAIpC,QAAA,YAAO,UADf,UAAA,GAAA,YAOiB,MAAA,cAAA,GAAA;;MALf,aAAU;MACT,cAAY,MAAA,IAAA,CAAI,CAAC;MACjB,OAAK,eAAE,MAAA,SAAA,CAAS;;MAEjB,SAAA,cAAoD,CAApD,YAAoD,MAAA,aAAA,GAAA;OAArC,OAAM;OAAU,eAAY"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Popover.js","names":[],"sources":["../../../../packages/primitives/popover/src/Popover.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Which edge of the trigger the panel prefers. It is a preference and not a\n * guarantee: Reka flips the panel to the opposite edge when it would collide\n * with the viewport, which is why a caller sets the side it *wants* rather\n * than the side it will get.\n *\n * The members mirror Reka's own `Side`. Nothing enforces that in this\n * declaration — the template binding does: give this union a member Reka does\n * not accept and `:side=\"side\"` stops type-checking. Declaring it here rather\n * than aliasing `PopoverContentProps[\"side\"]` is what puts the four values in\n * the generated props table, because the documentation reads syntax and an\n * indexed access type has none to read.\n */\nexport type PopoverSide = \"top\" | \"right\" | \"bottom\" | \"left\";\n\n/** Where the panel sits along that edge. */\nexport type PopoverAlign = \"start\" | \"center\" | \"end\";\n</script>\n\n<script setup lang=\"ts\">\nimport { PopoverRoot, PopoverTrigger, PopoverPortal, PopoverContent, PopoverArrow } from \"reka-ui\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { optional } from \"@ecoma-io/loom-core\";\n\n/**\n * Popover — a floating panel anchored to a trigger, carrying secondary content\n * the user opts into: a filter set, a colour picker, a details card.\n *\n * The escape contract, which is the whole reason to reach for an overlay\n * primitive rather than an absolutely positioned div: opening moves focus into\n * the panel, Esc and an outside click both close it, closing returns focus to\n * the trigger, and the page behind keeps scrolling because a popover is not\n * modal. Focus is *not* trapped — Tab leaves the panel, which is correct for\n * content the user can walk past. `Dialog` is the one that traps.\n *\n * Reach for `DropdownMenu` when the panel is a list of commands, `Tooltip` for\n * a hint that must not take focus at all, and `Dialog` when the task has to\n * block the rest of the UI.\n */\nwithDefaults(\n defineProps<{\n /** Drive the panel from the host with `v-model:open`; omit it and the popover owns its own state. */\n open?: boolean | undefined;\n /** The edge of the trigger to anchor against, before collision flipping. */\n side?: PopoverSide;\n /** Where the panel sits along that edge — `start` keeps its leading edge flush with the trigger's. */\n align?: PopoverAlign;\n /** Gap in pixels between the trigger and the panel. */\n sideOffset?: number;\n /** Draw the pointer notch. Turn it off where the panel reads better flush against its trigger. */\n arrow?: boolean;\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, side: \"bottom\", align: \"center\", sideOffset: 6, arrow: true },\n);\n\ndefineEmits<{ \"update:open\": [value: boolean] }>();\n</script>\n\n<template>\n <PopoverRoot v-bind=\"optional({ open })\" @update:open=\"$emit('update:open', $event)\">\n <!-- @slot The control that opens the panel. Rendered `as-child`, so the\n caller's own element *is* the trigger and no wrapper swallows its\n accessible name. -->\n <PopoverTrigger as-child>\n <slot name=\"trigger\" />\n </PopoverTrigger>\n\n <PopoverPortal>\n <PopoverContent\n :side=\"side\"\n :align=\"align\"\n :side-offset=\"sideOffset\"\n :class=\"\n cn(\n 'z-overlay min-w-[12rem] max-w-[min(90vw,22rem)] rounded-md border border-border bg-popover p-3 text-sm 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 panel over the\n // page, swallowing clicks. Scoped, the closed element computes\n // `animation-name: none` and Presence takes its immediate-unmount\n // branch instead; the paired exit is precisely what Presence is\n // waiting to hold it for. The origin below is what makes that scale\n // grow out of the trigger rather than out of the panel's own\n // centre — on the 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 <!-- @slot The panel body. -->\n <slot />\n <PopoverArrow v-if=\"arrow\" class=\"fill-popover\" :width=\"12\" :height=\"6\" />\n </PopoverContent>\n </PopoverPortal>\n </PopoverRoot>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Popover.vue_vue_type_script_setup_true_lang.js","names":["$emit"],"sources":["../../../../packages/primitives/popover/src/Popover.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Which edge of the trigger the panel prefers. It is a preference and not a\n * guarantee: Reka flips the panel to the opposite edge when it would collide\n * with the viewport, which is why a caller sets the side it *wants* rather\n * than the side it will get.\n *\n * The members mirror Reka's own `Side`. Nothing enforces that in this\n * declaration — the template binding does: give this union a member Reka does\n * not accept and `:side=\"side\"` stops type-checking. Declaring it here rather\n * than aliasing `PopoverContentProps[\"side\"]` is what puts the four values in\n * the generated props table, because the documentation reads syntax and an\n * indexed access type has none to read.\n */\nexport type PopoverSide = \"top\" | \"right\" | \"bottom\" | \"left\";\n\n/** Where the panel sits along that edge. */\nexport type PopoverAlign = \"start\" | \"center\" | \"end\";\n</script>\n\n<script setup lang=\"ts\">\nimport { PopoverRoot, PopoverTrigger, PopoverPortal, PopoverContent, PopoverArrow } from \"reka-ui\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { optional } from \"@ecoma-io/loom-core\";\n\n/**\n * Popover — a floating panel anchored to a trigger, carrying secondary content\n * the user opts into: a filter set, a colour picker, a details card.\n *\n * The escape contract, which is the whole reason to reach for an overlay\n * primitive rather than an absolutely positioned div: opening moves focus into\n * the panel, Esc and an outside click both close it, closing returns focus to\n * the trigger, and the page behind keeps scrolling because a popover is not\n * modal. Focus is *not* trapped — Tab leaves the panel, which is correct for\n * content the user can walk past. `Dialog` is the one that traps.\n *\n * Reach for `DropdownMenu` when the panel is a list of commands, `Tooltip` for\n * a hint that must not take focus at all, and `Dialog` when the task has to\n * block the rest of the UI.\n */\nwithDefaults(\n defineProps<{\n /** Drive the panel from the host with `v-model:open`; omit it and the popover owns its own state. */\n open?: boolean | undefined;\n /** The edge of the trigger to anchor against, before collision flipping. */\n side?: PopoverSide;\n /** Where the panel sits along that edge — `start` keeps its leading edge flush with the trigger's. */\n align?: PopoverAlign;\n /** Gap in pixels between the trigger and the panel. */\n sideOffset?: number;\n /** Draw the pointer notch. Turn it off where the panel reads better flush against its trigger. */\n arrow?: boolean;\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, side: \"bottom\", align: \"center\", sideOffset: 6, arrow: true },\n);\n\ndefineEmits<{ \"update:open\": [value: boolean] }>();\n</script>\n\n<template>\n <PopoverRoot v-bind=\"optional({ open })\" @update:open=\"$emit('update:open', $event)\">\n <!-- @slot The control that opens the panel. Rendered `as-child`, so the\n caller's own element *is* the trigger and no wrapper swallows its\n accessible name. -->\n <PopoverTrigger as-child>\n <slot name=\"trigger\" />\n </PopoverTrigger>\n\n <PopoverPortal>\n <PopoverContent\n :side=\"side\"\n :align=\"align\"\n :side-offset=\"sideOffset\"\n :class=\"\n cn(\n 'z-overlay min-w-[12rem] max-w-[min(90vw,22rem)] rounded-md border border-border bg-popover p-3 text-sm 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 panel over the\n // page, swallowing clicks. Scoped, the closed element computes\n // `animation-name: none` and Presence takes its immediate-unmount\n // branch instead; the paired exit is precisely what Presence is\n // waiting to hold it for. The origin below is what makes that scale\n // grow out of the trigger rather than out of the panel's own\n // centre — on the 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 <!-- @slot The panel body. -->\n <slot />\n <PopoverArrow v-if=\"arrow\" class=\"fill-popover\" :width=\"12\" :height=\"6\" />\n </PopoverContent>\n </PopoverPortal>\n </PopoverRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;GAgEE,OAAA,UAAA,GAAA,YAoCc,MAAA,WAAA,GApCd,WAAqB,MAoCP,QAAA,CApCe,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAA,EAAM,iBAAW,OAAA,OAAA,OAAA,MAAA,WAAEA,KAAAA,MAAK,eAAgB,MAAM,GAAA,CAAA,GAAA;IAIhF,SAAA,cAEiB,CAFjB,YAEiB,MAAA,cAAA,GAAA,EAFD,YAAA,GAAQ,GAAA;KACtB,SAAA,cAAuB,CAAvB,WAAuB,KAAA,QAAA,SAAA,CAAA,CAAA;;IAGzB,CAAA,GAAA,YA2BgB,MAAA,aAAA,GAAA,MAAA;KA1Bd,SAAA,cAyBiB,CAzBjB,YAyBiB,MAAA,cAAA,GAAA;MAxBd,MAAM,QAAA;MACN,OAAO,QAAA;MACP,eAAa,QAAA;MACb,OAAK,eAAa,MAAA,EAAA,CAAE,CAAA,yJAAA,0EAAA,CAAA;MAgBrB,OAAA,EAAA,oBAAA,sCAAA;;MAGA,SAAA,cAAQ,CAAR,WAAQ,KAAA,QAAA,SAAA,GACY,QAAA,SAApB,UAAA,GAAA,YAA0E,MAAA,YAAA,GAAA;;OAA/C,OAAM;OAAgB,OAAO;OAAK,QAAQ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Progress.js","names":[],"sources":["../../../../packages/primitives/progress/src/Progress.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { ProgressLabels } from \"@ecoma-io/loom-labels\";\nimport { cva } from \"class-variance-authority\";\n\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 PROGRESS_LABELS: ProgressLabels = {\n value: ({ value, max }) => `${String(Math.round((value / max) * 100))}%`,\n indeterminate: \"—\",\n};\n\n/**\n * The track's thickness. Deliberately *not* the shared control-height scale a\n * Select or a TextField uses: a bar is a stroke laid under a heading or inside\n * a table row, never a control a person clicks into, so lining it up with a\n * 36px input would only make it a slab. `md` is the thickness every caller got\n * before this prop existed and stays the default for exactly that reason.\n */\nexport type ProgressSize = \"sm\" | \"md\" | \"lg\";\n\nexport const progressVariants = cva(\"relative w-full overflow-hidden rounded-full bg-muted\", {\n variants: {\n size: {\n sm: \"h-1\",\n md: \"h-2\",\n lg: \"h-3\",\n } satisfies Record<ProgressSize, string>,\n },\n defaultVariants: { size: \"md\" },\n});\n</script>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { ProgressRoot, ProgressIndicator } from \"reka-ui\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useLabels, type LabelOverrides } from \"@ecoma-io/loom-labels\";\n\n/**\n * Progress — determinate progress bar for a task whose extent as a percentage\n * is known (upload %, step 3 of 5). `modelValue` null/undefined renders the\n * *indeterminate* state — an empty track, since there is no percentage yet to\n * paint. For a wait of unknown duration with no percentage at all, use\n * Spinner instead of guessing a Progress value. For the same number drawn as\n * a ring — a dashboard tile, a quota, a score where the circle is the visual\n * — use RadialProgress, which mirrors this contract exactly.\n *\n * Built on Reka UI's Progress: role=\"progressbar\" + aria-valuenow/min/max set\n * on the root automatically. `ariaLabel`/`ariaLabelledby` are bound onto\n * ProgressRoot (fallthrough overrides Reka's own generated \"`NN%`\" label) so\n * the accessible name describes *what* is loading, not just how much.\n */\nconst props = withDefaults(\n defineProps<{\n /** The completed amount, 0–`max`; `null`/omitted renders indeterminate. */\n modelValue?: number | null;\n /** The value that reads as 100% complete. */\n max?: number;\n /** Track thickness. Its own scale, not the control-height scale — a bar is a stroke, not a form control. */\n size?: ProgressSize;\n /** Print the rounded percentage as text beside the track. While indeterminate it prints an em dash, never a number. */\n showValue?: boolean;\n /** Accessible name describing the task, overriding Reka's default `NN%` label. */\n ariaLabel?: string;\n /** Accessible name sourced from another element's id, in place of `ariaLabel`. */\n ariaLabelledby?: string;\n /**\n * How the readout is written, as any subset of `ProgressLabels` — the rest\n * stay as the host's `provideLoomLabels` vocabulary left them, and then as\n * Loom's English.\n *\n * The per-instance case is a bar counting something a percentage is the\n * wrong reading of: `value: ({ value, max }) => \\`Step ${value} of ${max}\\``.\n */\n labels?: LabelOverrides<ProgressLabels>;\n }>(),\n {\n max: 100,\n size: \"md\",\n showValue: false,\n },\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(\"progress\", PROGRESS_LABELS, () => props.labels);\n\n/** The readout tracks the track: a 1px bar with 14px digits beside it is not one object. */\nconst VALUE_TEXT = {\n sm: \"text-micro\",\n md: \"text-xs\",\n lg: \"text-sm\",\n} satisfies Record<ProgressSize, string>;\n\n// `showValue` changes which node is outermost, so fallthrough can no longer be\n// left to Vue: `class` goes to whatever the caller sees as the component's box\n// — the flex row when there is a readout, the track itself when the track is\n// all there is — and everything else (aria-describedby, data-testid) goes to\n// ProgressRoot, which is the element carrying role=\"progressbar\" and therefore\n// the one those attributes are describing.\n//\n// The two branches are written out rather than always wrapping the track in a\n// row, because a bare Progress has to keep the exact box it had before\n// `showValue` existed — a wrapper, even an empty one, changes how it sits in\n// a flex or grid parent.\n//\n// Nothing may sit beside those two branches at the top level of the template,\n// comments included: a second root node turns the component into a fragment,\n// and the attributes routed by hand above then have nowhere single to land.\n//\n// Routing `class` through `cn()` also settles a fight that used to be decided\n// by stylesheet order: Vue's own fallthrough merge concatenates, so a caller's\n// `bg-primary` sat alongside the track's `bg-muted` rather than replacing it.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest } = useSplitAttrs();\n\n/**\n * The value the bar itself is given, clamped into [0, max]; null while\n * indeterminate. The clamp has to happen here rather than only on the way to\n * the transform below: `aria-valuenow` is generated from whatever ProgressRoot\n * receives, so handing it the raw prop announces a number outside the range\n * `aria-valuemin`/`aria-valuemax` declare — \"150 percent\" read out over a bar\n * painted at 100.\n */\nconst value = computed<number | null>(() =>\n props.modelValue === null || props.modelValue === undefined\n ? null\n : Math.min(Math.max(props.modelValue, 0), props.max),\n);\n\n/** Percentage 0–100, derived from the clamped value; null while indeterminate. */\nconst pct = computed<number | null>(() =>\n value.value === null ? null : (value.value / props.max) * 100,\n);\n\n/**\n * Everything ProgressRoot is given in both branches of the template. `rest`\n * lands last so a caller passing a bare `aria-label` attribute still wins over\n * the prop, which is the precedence Vue's own fallthrough gave before this\n * component took the attributes over by hand.\n */\nconst rootAttrs = computed(() => ({\n modelValue: value.value,\n max: props.max,\n \"aria-label\": props.ariaLabel,\n \"aria-labelledby\": props.ariaLabelledby,\n ...rest.value,\n}));\n\n// Determinate: a full-width bar slid left by the remaining percentage\n// (transform eased over --duration-slow). Indeterminate: a short segment\n// sweeping the track on a loop, since there is no percentage to paint.\n// Completion beat: at 100% the fill turns success (Loom law — a\n// finished piece of work reads as done at a glance), eased in over the same\n// --duration-slow lane via the background transition.\nconst indicatorClass = computed(() =>\n cn(\n \"h-full\",\n pct.value === 100 ? \"bg-success\" : \"bg-primary\",\n pct.value === null && \"w-1/3 animate-progress-indeterminate rounded-full\",\n ),\n);\n\nconst indicatorStyle = computed(() =>\n pct.value === null\n ? undefined\n : {\n transition:\n \"transform var(--duration-slow) var(--ease-out), background-color var(--duration-slow) var(--ease-out)\",\n transform: `translateX(-${100 - pct.value}%)`,\n },\n);\n\n/**\n * An indeterminate bar has no percentage, so the readout prints the placeholder\n * rather than the `0%` a naive `pct ?? 0` would produce — \"we do not know yet\"\n * and \"none of it is done\" are different facts, and only one of them is true.\n *\n * The determinate branch hands over `value` and `max` rather than the\n * percentage it already computed for the transform: rounding, the position of\n * the per-cent sign and the shape of the digits are all the host's, and `pct`\n * is a division this component happens to need for the paint.\n */\nconst readout = computed(() =>\n value.value === null\n ? text.value.indeterminate\n : text.value.value({ value: value.value, max: props.max }),\n);\n\ndefineExpose({ pct });\n</script>\n\n<template>\n <ProgressRoot\n v-if=\"!showValue\"\n v-bind=\"rootAttrs\"\n :class=\"cn(progressVariants({ size }), attrs.class as string)\"\n >\n <ProgressIndicator :class=\"indicatorClass\" :style=\"indicatorStyle\" />\n </ProgressRoot>\n\n <div v-else :class=\"cn('flex w-full items-center gap-3', attrs.class as string)\">\n <ProgressRoot\n v-bind=\"rootAttrs\"\n :class=\"cn(progressVariants({ size }), 'w-auto min-w-0 flex-1')\"\n >\n <ProgressIndicator :class=\"indicatorClass\" :style=\"indicatorStyle\" />\n </ProgressRoot>\n <!-- Hidden from assistive tech: the progressbar already announces this\n number through `aria-valuenow`, and a visible copy beside it is read\n out a second time as loose text. Fixed width so the track does not\n resize itself every time the digit count changes. -->\n <span\n aria-hidden=\"true\"\n :class=\"cn('tabular w-10 shrink-0 text-right text-muted-foreground', VALUE_TEXT[size])\"\n >{{ readout }}</span\n >\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Progress.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../../packages/primitives/progress/src/Progress.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { ProgressLabels } from \"@ecoma-io/loom-labels\";\nimport { cva } from \"class-variance-authority\";\n\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 PROGRESS_LABELS: ProgressLabels = {\n value: ({ value, max }) => `${String(Math.round((value / max) * 100))}%`,\n indeterminate: \"—\",\n};\n\n/**\n * The track's thickness. Deliberately *not* the shared control-height scale a\n * Select or a TextField uses: a bar is a stroke laid under a heading or inside\n * a table row, never a control a person clicks into, so lining it up with a\n * 36px input would only make it a slab. `md` is the thickness every caller got\n * before this prop existed and stays the default for exactly that reason.\n */\nexport type ProgressSize = \"sm\" | \"md\" | \"lg\";\n\nexport const progressVariants = cva(\"relative w-full overflow-hidden rounded-full bg-muted\", {\n variants: {\n size: {\n sm: \"h-1\",\n md: \"h-2\",\n lg: \"h-3\",\n } satisfies Record<ProgressSize, string>,\n },\n defaultVariants: { size: \"md\" },\n});\n</script>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { ProgressRoot, ProgressIndicator } from \"reka-ui\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useLabels, type LabelOverrides } from \"@ecoma-io/loom-labels\";\n\n/**\n * Progress — determinate progress bar for a task whose extent as a percentage\n * is known (upload %, step 3 of 5). `modelValue` null/undefined renders the\n * *indeterminate* state — an empty track, since there is no percentage yet to\n * paint. For a wait of unknown duration with no percentage at all, use\n * Spinner instead of guessing a Progress value. For the same number drawn as\n * a ring — a dashboard tile, a quota, a score where the circle is the visual\n * — use RadialProgress, which mirrors this contract exactly.\n *\n * Built on Reka UI's Progress: role=\"progressbar\" + aria-valuenow/min/max set\n * on the root automatically. `ariaLabel`/`ariaLabelledby` are bound onto\n * ProgressRoot (fallthrough overrides Reka's own generated \"`NN%`\" label) so\n * the accessible name describes *what* is loading, not just how much.\n */\nconst props = withDefaults(\n defineProps<{\n /** The completed amount, 0–`max`; `null`/omitted renders indeterminate. */\n modelValue?: number | null;\n /** The value that reads as 100% complete. */\n max?: number;\n /** Track thickness. Its own scale, not the control-height scale — a bar is a stroke, not a form control. */\n size?: ProgressSize;\n /** Print the rounded percentage as text beside the track. While indeterminate it prints an em dash, never a number. */\n showValue?: boolean;\n /** Accessible name describing the task, overriding Reka's default `NN%` label. */\n ariaLabel?: string;\n /** Accessible name sourced from another element's id, in place of `ariaLabel`. */\n ariaLabelledby?: string;\n /**\n * How the readout is written, as any subset of `ProgressLabels` — the rest\n * stay as the host's `provideLoomLabels` vocabulary left them, and then as\n * Loom's English.\n *\n * The per-instance case is a bar counting something a percentage is the\n * wrong reading of: `value: ({ value, max }) => \\`Step ${value} of ${max}\\``.\n */\n labels?: LabelOverrides<ProgressLabels>;\n }>(),\n {\n max: 100,\n size: \"md\",\n showValue: false,\n },\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(\"progress\", PROGRESS_LABELS, () => props.labels);\n\n/** The readout tracks the track: a 1px bar with 14px digits beside it is not one object. */\nconst VALUE_TEXT = {\n sm: \"text-micro\",\n md: \"text-xs\",\n lg: \"text-sm\",\n} satisfies Record<ProgressSize, string>;\n\n// `showValue` changes which node is outermost, so fallthrough can no longer be\n// left to Vue: `class` goes to whatever the caller sees as the component's box\n// — the flex row when there is a readout, the track itself when the track is\n// all there is — and everything else (aria-describedby, data-testid) goes to\n// ProgressRoot, which is the element carrying role=\"progressbar\" and therefore\n// the one those attributes are describing.\n//\n// The two branches are written out rather than always wrapping the track in a\n// row, because a bare Progress has to keep the exact box it had before\n// `showValue` existed — a wrapper, even an empty one, changes how it sits in\n// a flex or grid parent.\n//\n// Nothing may sit beside those two branches at the top level of the template,\n// comments included: a second root node turns the component into a fragment,\n// and the attributes routed by hand above then have nowhere single to land.\n//\n// Routing `class` through `cn()` also settles a fight that used to be decided\n// by stylesheet order: Vue's own fallthrough merge concatenates, so a caller's\n// `bg-primary` sat alongside the track's `bg-muted` rather than replacing it.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest } = useSplitAttrs();\n\n/**\n * The value the bar itself is given, clamped into [0, max]; null while\n * indeterminate. The clamp has to happen here rather than only on the way to\n * the transform below: `aria-valuenow` is generated from whatever ProgressRoot\n * receives, so handing it the raw prop announces a number outside the range\n * `aria-valuemin`/`aria-valuemax` declare — \"150 percent\" read out over a bar\n * painted at 100.\n */\nconst value = computed<number | null>(() =>\n props.modelValue === null || props.modelValue === undefined\n ? null\n : Math.min(Math.max(props.modelValue, 0), props.max),\n);\n\n/** Percentage 0–100, derived from the clamped value; null while indeterminate. */\nconst pct = computed<number | null>(() =>\n value.value === null ? null : (value.value / props.max) * 100,\n);\n\n/**\n * Everything ProgressRoot is given in both branches of the template. `rest`\n * lands last so a caller passing a bare `aria-label` attribute still wins over\n * the prop, which is the precedence Vue's own fallthrough gave before this\n * component took the attributes over by hand.\n */\nconst rootAttrs = computed(() => ({\n modelValue: value.value,\n max: props.max,\n \"aria-label\": props.ariaLabel,\n \"aria-labelledby\": props.ariaLabelledby,\n ...rest.value,\n}));\n\n// Determinate: a full-width bar slid left by the remaining percentage\n// (transform eased over --duration-slow). Indeterminate: a short segment\n// sweeping the track on a loop, since there is no percentage to paint.\n// Completion beat: at 100% the fill turns success (Loom law — a\n// finished piece of work reads as done at a glance), eased in over the same\n// --duration-slow lane via the background transition.\nconst indicatorClass = computed(() =>\n cn(\n \"h-full\",\n pct.value === 100 ? \"bg-success\" : \"bg-primary\",\n pct.value === null && \"w-1/3 animate-progress-indeterminate rounded-full\",\n ),\n);\n\nconst indicatorStyle = computed(() =>\n pct.value === null\n ? undefined\n : {\n transition:\n \"transform var(--duration-slow) var(--ease-out), background-color var(--duration-slow) var(--ease-out)\",\n transform: `translateX(-${100 - pct.value}%)`,\n },\n);\n\n/**\n * An indeterminate bar has no percentage, so the readout prints the placeholder\n * rather than the `0%` a naive `pct ?? 0` would produce — \"we do not know yet\"\n * and \"none of it is done\" are different facts, and only one of them is true.\n *\n * The determinate branch hands over `value` and `max` rather than the\n * percentage it already computed for the transform: rounding, the position of\n * the per-cent sign and the shape of the digits are all the host's, and `pct`\n * is a division this component happens to need for the paint.\n */\nconst readout = computed(() =>\n value.value === null\n ? text.value.indeterminate\n : text.value.value({ value: value.value, max: props.max }),\n);\n\ndefineExpose({ pct });\n</script>\n\n<template>\n <ProgressRoot\n v-if=\"!showValue\"\n v-bind=\"rootAttrs\"\n :class=\"cn(progressVariants({ size }), attrs.class as string)\"\n >\n <ProgressIndicator :class=\"indicatorClass\" :style=\"indicatorStyle\" />\n </ProgressRoot>\n\n <div v-else :class=\"cn('flex w-full items-center gap-3', attrs.class as string)\">\n <ProgressRoot\n v-bind=\"rootAttrs\"\n :class=\"cn(progressVariants({ size }), 'w-auto min-w-0 flex-1')\"\n >\n <ProgressIndicator :class=\"indicatorClass\" :style=\"indicatorStyle\" />\n </ProgressRoot>\n <!-- Hidden from assistive tech: the progressbar already announces this\n number through `aria-valuenow`, and a visible copy beside it is read\n out a second time as loose text. Fixed width so the track does not\n resize itself every time the digit count changes. -->\n <span\n aria-hidden=\"true\"\n :class=\"cn('tabular w-10 shrink-0 text-right text-muted-foreground', VALUE_TEXT[size])\"\n >{{ readout }}</span\n >\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;AAUA,IAAa,kBAAkC;CAC7C,QAAQ,EAAE,OAAO,UAAU,GAAG,OAAO,KAAK,MAAO,QAAQ,MAAO,GAAG,CAAC,EAAE;CACtE,eAAe;AACjB;AAWA,IAAa,mBAAmB,IAAI,yDAAyD;CAC3F,UAAU,EACR,MAAM;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN,EACF;CACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CAAC;;;;;;;;;;;;;;;;;EAwBD,MAAM,QAAQ;EAkCd,MAAM,OAAO,UAAU,YAAY,uBAAuB,MAAM,MAAM;;EAGtE,MAAM,aAAa;GACjB,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;EAsBA,MAAM,EAAE,OAAO,SAAS,cAAc;;;;;;;;;EAUtC,MAAM,QAAQ,eACZ,MAAM,eAAe,QAAQ,MAAM,eAAe,KAAA,IAC9C,OACA,KAAK,IAAI,KAAK,IAAI,MAAM,YAAY,CAAC,GAAG,MAAM,GAAG,CACvD;;EAGA,MAAM,MAAM,eACV,MAAM,UAAU,OAAO,OAAQ,MAAM,QAAQ,MAAM,MAAO,GAC5D;;;;;;;EAQA,MAAM,YAAY,gBAAgB;GAChC,YAAY,MAAM;GAClB,KAAK,MAAM;GACX,cAAc,MAAM;GACpB,mBAAmB,MAAM;GACzB,GAAG,KAAK;EACV,EAAE;EAQF,MAAM,iBAAiB,eACrB,GACE,UACA,IAAI,UAAU,MAAM,eAAe,cACnC,IAAI,UAAU,QAAQ,mDACxB,CACF;EAEA,MAAM,iBAAiB,eACrB,IAAI,UAAU,OACV,KAAA,IACA;GACE,YACE;GACF,WAAW,eAAe,MAAM,IAAI,MAAM;EAC5C,CACN;;;;;;;;;;;EAYA,MAAM,UAAU,eACd,MAAM,UAAU,OACZ,KAAK,MAAM,gBACX,KAAK,MAAM,MAAM;GAAE,OAAO,MAAM;GAAO,KAAK,MAAM;EAAI,CAAC,CAC7D;EAEA,SAAa,EAAE,IAAI,CAAC;;GAKT,OAAA,CAAA,QAAA,aADT,UAAA,GAAA,YAMe,MAAA,YAAA,GANf,WAMe,EAAA,KAAA,EAAA,GAJL,UAAA,OAAS,EAChB,OAAO,MAAA,EAAA,CAAE,CAAC,MAAA,gBAAA,CAAgB,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAK,MAAA,KAAA,CAAK,CAAC,KAAK,EAAA,CAAA,GAAA;IAElD,SAAA,cAAqE,CAArE,YAAqE,MAAA,iBAAA,GAAA;KAAjD,OAAK,eAAE,eAAA,KAAc;KAAG,OAAK,eAAE,eAAA,KAAc;;;GAGnE,GAAA,IAAA,CAAA,OAAA,CAAA,MAAA,UAAA,GAAA,mBAgBM,OAAA;;IAhBO,OAAK,eAAE,MAAA,EAAA,CAAE,CAAA,kCAAmC,MAAA,KAAA,CAAK,CAAC,KAAK,CAAA;GAClE,GAAA,CAAA,YAKe,MAAA,YAAA,GALf,WACU,UAIK,OAJI,EAChB,OAAO,MAAA,EAAA,CAAE,CAAC,MAAA,gBAAA,CAAgB,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAA,uBAAA,EAAA,CAAA,GAAA;IAElC,SAAA,cAAqE,CAArE,YAAqE,MAAA,iBAAA,GAAA;KAAjD,OAAK,eAAE,eAAA,KAAc;KAAG,OAAK,eAAE,eAAA,KAAc;;;GAMnE,GAAA,IAAA,CAAA,OAAA,CAAA,GAAA,mBAIC,QAAA;IAHC,eAAY;IACX,OAAK,eAAE,MAAA,EAAA,CAAE,CAAA,0DAA2D,WAAW,QAAA,KAAI,CAAA;GAChF,GAAA,gBAAA,QAAA,KAAO,GAAA,CAAA,CAAA,GAAA,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RadialProgress.js","names":[],"sources":["../../../../packages/primitives/radial-progress/src/RadialProgress.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { RadialProgressLabels } from \"@ecoma-io/loom-labels\";\nimport { cva } from \"class-variance-authority\";\n\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 RADIAL_PROGRESS_LABELS: RadialProgressLabels = {\n value: ({ value, max }) => `${String(Math.round((value / max) * 100))}%`,\n indeterminate: \"—\",\n};\n\n/**\n * The ring's outer diameter. `sm` is a table-row or list-item marker, `md` a\n * card corner, `lg` the one on a dashboard tile where the ring is the whole\n * visual. Its own scale rather than the control-height scale a Select uses: a\n * ring is not a form control and never has to line up beside a text input.\n */\nexport type RadialProgressSize = \"sm\" | \"md\" | \"lg\";\n\n/**\n * How heavy the ring's stroke is. Measured in the SVG's own units, so the\n * proportion of stroke to diameter holds at every `size` — a `thick` ring\n * reads as thick whether it is 40px or 96px across.\n */\nexport type RadialProgressThickness = \"thin\" | \"regular\" | \"thick\";\n\nexport const radialProgressVariants = cva(\n \"relative inline-flex shrink-0 items-center justify-center\",\n {\n variants: {\n size: {\n sm: \"h-10 w-10\",\n md: \"h-16 w-16\",\n lg: \"h-24 w-24\",\n } satisfies Record<RadialProgressSize, string>,\n },\n defaultVariants: { size: \"md\" },\n },\n);\n</script>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { ProgressRoot } from \"reka-ui\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useLabels, type LabelOverrides } from \"@ecoma-io/loom-labels\";\n\n/**\n * RadialProgress — the same information Progress carries, drawn as a ring: a\n * dashboard tile, a quota, a score out of 100 where the circle *is* the\n * visual. Reach for Progress instead wherever the bar sits in a row of text\n * and its length is doing the comparing, and for Spinner when there is no\n * percentage at all — a ring with nothing to fill is a spinner wearing a\n * gauge's clothes.\n *\n * Its contract is Progress's, deliberately and to the letter: `null`/omitted\n * `modelValue` is indeterminate, the value is clamped into [0, max] for the\n * same reason, the arc turns `success` at 100%, and the accessible name comes\n * from `ariaLabel`/`ariaLabelledby`. Two components answering the same\n * question differently is worse than either answer.\n *\n * Reka's ProgressRoot is kept as the wrapper even though the fill below is an\n * SVG arc rather than a ProgressIndicator: the root is what emits\n * `role=\"progressbar\"` and `aria-valuenow`/`aria-valuemin`/`aria-valuemax`,\n * and it is the only way this component and Progress cannot drift into\n * announcing the same number two different ways. ProgressIndicator is left out\n * because it exists to be translated inside an overflow-hidden track, which is\n * not how an arc is painted.\n */\nconst props = withDefaults(\n defineProps<{\n /** The completed amount, 0–`max`; `null`/omitted renders indeterminate. */\n modelValue?: number | null;\n /** The value that reads as 100% complete. */\n max?: number;\n /** The ring's outer diameter. */\n size?: RadialProgressSize;\n /** The weight of the ring's stroke, in proportion to its diameter. */\n thickness?: RadialProgressThickness;\n /** Print the rounded percentage in the ring's centre. On by default — an empty ring is a spinner. While indeterminate it prints an em dash, never a number. */\n showValue?: boolean;\n /** Accessible name describing the task, overriding Reka's default `NN%` label. */\n ariaLabel?: string;\n /** Accessible name sourced from another element's id, in place of `ariaLabel`. */\n ariaLabelledby?: string;\n /**\n * How the centre readout is written, as any subset of\n * `RadialProgressLabels` — the rest stay as the host's `provideLoomLabels`\n * vocabulary left them, and then as Loom's English.\n */\n labels?: LabelOverrides<RadialProgressLabels>;\n }>(),\n {\n max: 100,\n size: \"md\",\n thickness: \"regular\",\n // On, unlike Progress's opt-in: a bar has a length a reader can compare\n // against its own track, and a ring does not — 70% and 80% are the same\n // arc to the eye. The number is what makes the ring readable.\n showValue: true,\n },\n);\n\n/**\n * The drawing is done in a fixed 100×100 user-unit box and scaled to whatever\n * `size` renders, so every number below is a proportion rather than a pixel\n * count and none of them has to be recomputed per size.\n */\nconst VIEW = 100;\nconst CENTRE = VIEW / 2;\n\nconst STROKE = {\n thin: 6,\n regular: 10,\n thick: 14,\n} satisfies Record<RadialProgressThickness, number>;\n\n/** The centre readout scales with the ring, or it overflows it at `sm`. */\nconst VALUE_TEXT = {\n sm: \"text-micro\",\n md: \"text-sm\",\n lg: \"text-lg\",\n} satisfies Record<RadialProgressSize, string>;\n\n// One rendered node, but `class` is still routed by hand: Vue's fallthrough\n// merge concatenates, so a caller's `h-20 w-20` only beat the size variant's\n// `h-16 w-16` if Tailwind happened to emit it later. `cn()` decides it. The\n// rest of the fallthrough (aria-describedby, data-testid) lands on the same\n// node, which is the one carrying role=\"progressbar\".\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest } = useSplitAttrs();\n\n/**\n * Clamped into [0, max] for the reason Progress's own comment gives:\n * `aria-valuenow` is generated from whatever ProgressRoot receives, so an\n * unclamped 150 is announced against an `aria-valuemax` of 100 while the arc\n * is painted full.\n */\nconst value = computed<number | null>(() =>\n props.modelValue === null || props.modelValue === undefined\n ? null\n : Math.min(Math.max(props.modelValue, 0), props.max),\n);\n\n/** Percentage 0–100, derived from the clamped value; null while indeterminate. */\nconst pct = computed<number | null>(() =>\n value.value === null ? null : (value.value / props.max) * 100,\n);\n\nconst strokeWidth = computed(() => STROKE[props.thickness]);\n\n/**\n * An SVG stroke is centred on its path, so half of it falls outside the\n * circle's own radius. A radius of `CENTRE` therefore paints the outer half of\n * the stroke past the viewBox edge and the browser clips it — the flat top\n * that makes a radial progress look broken. Half the stroke width comes off\n * the radius so the ring's outer edge lands exactly on the box.\n */\nconst radius = computed(() => CENTRE - strokeWidth.value / 2);\n\n/**\n * The dash pattern is measured along the path, so it depends on the radius —\n * which depends on `thickness`. Hard-coding a circumference works for exactly\n * one thickness and silently mis-fills every other one.\n */\nconst circumference = computed(() => 2 * Math.PI * radius.value);\n\n/**\n * Determinate: one dash as long as the whole path, pushed back by the fraction\n * still to do. Indeterminate: a fixed quarter-turn segment, with the gap set\n * to the full circumference so nothing wraps round behind it while it spins.\n */\nconst dashArray = computed(() =>\n pct.value === null\n ? `${circumference.value * 0.25} ${circumference.value}`\n : `${circumference.value}`,\n);\n\nconst dashOffset = computed(() =>\n pct.value === null ? 0 : circumference.value * (1 - pct.value / 100),\n);\n\n/**\n * A round cap at exactly 0% still paints a dot at twelve o'clock — a ring\n * showing nothing that reads as a couple of percent. Nothing done draws\n * nothing, so the cap squares off for that one value.\n */\nconst lineCap = computed(() => (pct.value === 0 ? \"butt\" : \"round\"));\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(\"radialProgress\", RADIAL_PROGRESS_LABELS, () => props.labels);\n\n/**\n * Matches Progress: a placeholder for \"no percentage yet\", never a false `0%`,\n * and both raw numbers handed over rather than a percentage already written in\n * English.\n */\nconst readout = computed(() =>\n value.value === null\n ? text.value.indeterminate\n : text.value.value({ value: value.value, max: props.max }),\n);\n\ndefineExpose({ pct });\n</script>\n\n<template>\n <ProgressRoot\n :model-value=\"value\"\n :max=\"max\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"ariaLabelledby\"\n v-bind=\"rest\"\n :class=\"cn(radialProgressVariants({ size }), attrs.class as string)\"\n >\n <!-- Hidden from assistive tech in one piece: everything it draws is\n already announced by the progressbar above it, and the centre readout\n would otherwise be read a second time as loose text.\n\n Indeterminate spins the whole SVG rather than the arc inside it. The\n rotation is then an ordinary CSS transform on an HTML-positioned box,\n whose transform-origin is its own centre at every size — where a\n transform on an SVG child resolves against the viewBox and needs\n `transform-box` reasoning to stay centred. It is a CSS animation and\n not a JS ticker so the global prefers-reduced-motion rule in\n global.css collapses it, which a requestAnimationFrame loop would\n escape. The readout sits outside the SVG for the same reason: it must\n not spin with it. -->\n <svg\n :viewBox=\"`0 0 ${VIEW} ${VIEW}`\"\n fill=\"none\"\n aria-hidden=\"true\"\n :class=\"cn('absolute inset-0 h-full w-full', pct === null && 'animate-spin')\"\n >\n <circle\n :cx=\"CENTRE\"\n :cy=\"CENTRE\"\n :r=\"radius\"\n :stroke-width=\"strokeWidth\"\n class=\"stroke-muted\"\n />\n <!-- Rotated by the SVG `transform` attribute about the box's centre\n rather than a CSS `-rotate-90`: the attribute is expressed in\n viewBox units, so twelve o'clock stays twelve o'clock at any\n rendered size, with no dependency on how the engine resolves\n transform-origin for an SVG child. An unrotated circle starts at\n three. -->\n <circle\n :cx=\"CENTRE\"\n :cy=\"CENTRE\"\n :r=\"radius\"\n :stroke-width=\"strokeWidth\"\n :stroke-dasharray=\"dashArray\"\n :stroke-dashoffset=\"dashOffset\"\n :stroke-linecap=\"lineCap\"\n :transform=\"`rotate(-90 ${CENTRE} ${CENTRE})`\"\n :class=\"pct === 100 ? 'stroke-success' : 'stroke-primary'\"\n :style=\"\n pct === null\n ? undefined\n : {\n transition:\n 'stroke-dashoffset var(--duration-slow) var(--ease-out), stroke var(--duration-slow) var(--ease-out)',\n }\n \"\n />\n </svg>\n <span\n v-if=\"showValue\"\n aria-hidden=\"true\"\n :class=\"cn('tabular text-foreground', VALUE_TEXT[size])\"\n >{{ readout }}</span\n >\n </ProgressRoot>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RadialProgress.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../../packages/primitives/radial-progress/src/RadialProgress.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { RadialProgressLabels } from \"@ecoma-io/loom-labels\";\nimport { cva } from \"class-variance-authority\";\n\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 RADIAL_PROGRESS_LABELS: RadialProgressLabels = {\n value: ({ value, max }) => `${String(Math.round((value / max) * 100))}%`,\n indeterminate: \"—\",\n};\n\n/**\n * The ring's outer diameter. `sm` is a table-row or list-item marker, `md` a\n * card corner, `lg` the one on a dashboard tile where the ring is the whole\n * visual. Its own scale rather than the control-height scale a Select uses: a\n * ring is not a form control and never has to line up beside a text input.\n */\nexport type RadialProgressSize = \"sm\" | \"md\" | \"lg\";\n\n/**\n * How heavy the ring's stroke is. Measured in the SVG's own units, so the\n * proportion of stroke to diameter holds at every `size` — a `thick` ring\n * reads as thick whether it is 40px or 96px across.\n */\nexport type RadialProgressThickness = \"thin\" | \"regular\" | \"thick\";\n\nexport const radialProgressVariants = cva(\n \"relative inline-flex shrink-0 items-center justify-center\",\n {\n variants: {\n size: {\n sm: \"h-10 w-10\",\n md: \"h-16 w-16\",\n lg: \"h-24 w-24\",\n } satisfies Record<RadialProgressSize, string>,\n },\n defaultVariants: { size: \"md\" },\n },\n);\n</script>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { ProgressRoot } from \"reka-ui\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useLabels, type LabelOverrides } from \"@ecoma-io/loom-labels\";\n\n/**\n * RadialProgress — the same information Progress carries, drawn as a ring: a\n * dashboard tile, a quota, a score out of 100 where the circle *is* the\n * visual. Reach for Progress instead wherever the bar sits in a row of text\n * and its length is doing the comparing, and for Spinner when there is no\n * percentage at all — a ring with nothing to fill is a spinner wearing a\n * gauge's clothes.\n *\n * Its contract is Progress's, deliberately and to the letter: `null`/omitted\n * `modelValue` is indeterminate, the value is clamped into [0, max] for the\n * same reason, the arc turns `success` at 100%, and the accessible name comes\n * from `ariaLabel`/`ariaLabelledby`. Two components answering the same\n * question differently is worse than either answer.\n *\n * Reka's ProgressRoot is kept as the wrapper even though the fill below is an\n * SVG arc rather than a ProgressIndicator: the root is what emits\n * `role=\"progressbar\"` and `aria-valuenow`/`aria-valuemin`/`aria-valuemax`,\n * and it is the only way this component and Progress cannot drift into\n * announcing the same number two different ways. ProgressIndicator is left out\n * because it exists to be translated inside an overflow-hidden track, which is\n * not how an arc is painted.\n */\nconst props = withDefaults(\n defineProps<{\n /** The completed amount, 0–`max`; `null`/omitted renders indeterminate. */\n modelValue?: number | null;\n /** The value that reads as 100% complete. */\n max?: number;\n /** The ring's outer diameter. */\n size?: RadialProgressSize;\n /** The weight of the ring's stroke, in proportion to its diameter. */\n thickness?: RadialProgressThickness;\n /** Print the rounded percentage in the ring's centre. On by default — an empty ring is a spinner. While indeterminate it prints an em dash, never a number. */\n showValue?: boolean;\n /** Accessible name describing the task, overriding Reka's default `NN%` label. */\n ariaLabel?: string;\n /** Accessible name sourced from another element's id, in place of `ariaLabel`. */\n ariaLabelledby?: string;\n /**\n * How the centre readout is written, as any subset of\n * `RadialProgressLabels` — the rest stay as the host's `provideLoomLabels`\n * vocabulary left them, and then as Loom's English.\n */\n labels?: LabelOverrides<RadialProgressLabels>;\n }>(),\n {\n max: 100,\n size: \"md\",\n thickness: \"regular\",\n // On, unlike Progress's opt-in: a bar has a length a reader can compare\n // against its own track, and a ring does not — 70% and 80% are the same\n // arc to the eye. The number is what makes the ring readable.\n showValue: true,\n },\n);\n\n/**\n * The drawing is done in a fixed 100×100 user-unit box and scaled to whatever\n * `size` renders, so every number below is a proportion rather than a pixel\n * count and none of them has to be recomputed per size.\n */\nconst VIEW = 100;\nconst CENTRE = VIEW / 2;\n\nconst STROKE = {\n thin: 6,\n regular: 10,\n thick: 14,\n} satisfies Record<RadialProgressThickness, number>;\n\n/** The centre readout scales with the ring, or it overflows it at `sm`. */\nconst VALUE_TEXT = {\n sm: \"text-micro\",\n md: \"text-sm\",\n lg: \"text-lg\",\n} satisfies Record<RadialProgressSize, string>;\n\n// One rendered node, but `class` is still routed by hand: Vue's fallthrough\n// merge concatenates, so a caller's `h-20 w-20` only beat the size variant's\n// `h-16 w-16` if Tailwind happened to emit it later. `cn()` decides it. The\n// rest of the fallthrough (aria-describedby, data-testid) lands on the same\n// node, which is the one carrying role=\"progressbar\".\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest } = useSplitAttrs();\n\n/**\n * Clamped into [0, max] for the reason Progress's own comment gives:\n * `aria-valuenow` is generated from whatever ProgressRoot receives, so an\n * unclamped 150 is announced against an `aria-valuemax` of 100 while the arc\n * is painted full.\n */\nconst value = computed<number | null>(() =>\n props.modelValue === null || props.modelValue === undefined\n ? null\n : Math.min(Math.max(props.modelValue, 0), props.max),\n);\n\n/** Percentage 0–100, derived from the clamped value; null while indeterminate. */\nconst pct = computed<number | null>(() =>\n value.value === null ? null : (value.value / props.max) * 100,\n);\n\nconst strokeWidth = computed(() => STROKE[props.thickness]);\n\n/**\n * An SVG stroke is centred on its path, so half of it falls outside the\n * circle's own radius. A radius of `CENTRE` therefore paints the outer half of\n * the stroke past the viewBox edge and the browser clips it — the flat top\n * that makes a radial progress look broken. Half the stroke width comes off\n * the radius so the ring's outer edge lands exactly on the box.\n */\nconst radius = computed(() => CENTRE - strokeWidth.value / 2);\n\n/**\n * The dash pattern is measured along the path, so it depends on the radius —\n * which depends on `thickness`. Hard-coding a circumference works for exactly\n * one thickness and silently mis-fills every other one.\n */\nconst circumference = computed(() => 2 * Math.PI * radius.value);\n\n/**\n * Determinate: one dash as long as the whole path, pushed back by the fraction\n * still to do. Indeterminate: a fixed quarter-turn segment, with the gap set\n * to the full circumference so nothing wraps round behind it while it spins.\n */\nconst dashArray = computed(() =>\n pct.value === null\n ? `${circumference.value * 0.25} ${circumference.value}`\n : `${circumference.value}`,\n);\n\nconst dashOffset = computed(() =>\n pct.value === null ? 0 : circumference.value * (1 - pct.value / 100),\n);\n\n/**\n * A round cap at exactly 0% still paints a dot at twelve o'clock — a ring\n * showing nothing that reads as a couple of percent. Nothing done draws\n * nothing, so the cap squares off for that one value.\n */\nconst lineCap = computed(() => (pct.value === 0 ? \"butt\" : \"round\"));\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(\"radialProgress\", RADIAL_PROGRESS_LABELS, () => props.labels);\n\n/**\n * Matches Progress: a placeholder for \"no percentage yet\", never a false `0%`,\n * and both raw numbers handed over rather than a percentage already written in\n * English.\n */\nconst readout = computed(() =>\n value.value === null\n ? text.value.indeterminate\n : text.value.value({ value: value.value, max: props.max }),\n);\n\ndefineExpose({ pct });\n</script>\n\n<template>\n <ProgressRoot\n :model-value=\"value\"\n :max=\"max\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"ariaLabelledby\"\n v-bind=\"rest\"\n :class=\"cn(radialProgressVariants({ size }), attrs.class as string)\"\n >\n <!-- Hidden from assistive tech in one piece: everything it draws is\n already announced by the progressbar above it, and the centre readout\n would otherwise be read a second time as loose text.\n\n Indeterminate spins the whole SVG rather than the arc inside it. The\n rotation is then an ordinary CSS transform on an HTML-positioned box,\n whose transform-origin is its own centre at every size — where a\n transform on an SVG child resolves against the viewBox and needs\n `transform-box` reasoning to stay centred. It is a CSS animation and\n not a JS ticker so the global prefers-reduced-motion rule in\n global.css collapses it, which a requestAnimationFrame loop would\n escape. The readout sits outside the SVG for the same reason: it must\n not spin with it. -->\n <svg\n :viewBox=\"`0 0 ${VIEW} ${VIEW}`\"\n fill=\"none\"\n aria-hidden=\"true\"\n :class=\"cn('absolute inset-0 h-full w-full', pct === null && 'animate-spin')\"\n >\n <circle\n :cx=\"CENTRE\"\n :cy=\"CENTRE\"\n :r=\"radius\"\n :stroke-width=\"strokeWidth\"\n class=\"stroke-muted\"\n />\n <!-- Rotated by the SVG `transform` attribute about the box's centre\n rather than a CSS `-rotate-90`: the attribute is expressed in\n viewBox units, so twelve o'clock stays twelve o'clock at any\n rendered size, with no dependency on how the engine resolves\n transform-origin for an SVG child. An unrotated circle starts at\n three. -->\n <circle\n :cx=\"CENTRE\"\n :cy=\"CENTRE\"\n :r=\"radius\"\n :stroke-width=\"strokeWidth\"\n :stroke-dasharray=\"dashArray\"\n :stroke-dashoffset=\"dashOffset\"\n :stroke-linecap=\"lineCap\"\n :transform=\"`rotate(-90 ${CENTRE} ${CENTRE})`\"\n :class=\"pct === 100 ? 'stroke-success' : 'stroke-primary'\"\n :style=\"\n pct === null\n ? undefined\n : {\n transition:\n 'stroke-dashoffset var(--duration-slow) var(--ease-out), stroke var(--duration-slow) var(--ease-out)',\n }\n \"\n />\n </svg>\n <span\n v-if=\"showValue\"\n aria-hidden=\"true\"\n :class=\"cn('tabular text-foreground', VALUE_TEXT[size])\"\n >{{ readout }}</span\n >\n </ProgressRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAUA,IAAa,yBAA+C;CAC1D,QAAQ,EAAE,OAAO,UAAU,GAAG,OAAO,KAAK,MAAO,QAAQ,MAAO,GAAG,CAAC,EAAE;CACtE,eAAe;AACjB;AAiBA,IAAa,yBAAyB,IACpC,6DACA;CACE,UAAU,EACR,MAAM;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;CACN,EACF;CACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CACF;;;;;;;;;;;;;;;;;;EAgCA,MAAM,QAAQ;;;;;;EAuCd,MAAM,OAAO;EACb,MAAM,SAAS,OAAO;EAEtB,MAAM,SAAS;GACb,MAAM;GACN,SAAS;GACT,OAAO;EACT;;EAGA,MAAM,aAAa;GACjB,IAAI;GACJ,IAAI;GACJ,IAAI;EACN;EAQA,MAAM,EAAE,OAAO,SAAS,cAAc;;;;;;;EAQtC,MAAM,QAAQ,eACZ,MAAM,eAAe,QAAQ,MAAM,eAAe,KAAA,IAC9C,OACA,KAAK,IAAI,KAAK,IAAI,MAAM,YAAY,CAAC,GAAG,MAAM,GAAG,CACvD;;EAGA,MAAM,MAAM,eACV,MAAM,UAAU,OAAO,OAAQ,MAAM,QAAQ,MAAM,MAAO,GAC5D;EAEA,MAAM,cAAc,eAAe,OAAO,MAAM,UAAU;;;;;;;;EAS1D,MAAM,SAAS,eAAe,SAAS,YAAY,QAAQ,CAAC;;;;;;EAO5D,MAAM,gBAAgB,eAAe,IAAI,KAAK,KAAK,OAAO,KAAK;;;;;;EAO/D,MAAM,YAAY,eAChB,IAAI,UAAU,OACV,GAAG,cAAc,QAAQ,IAAK,GAAG,cAAc,UAC/C,GAAG,cAAc,OACvB;EAEA,MAAM,aAAa,eACjB,IAAI,UAAU,OAAO,IAAI,cAAc,SAAS,IAAI,IAAI,QAAQ,IAClE;;;;;;EAOA,MAAM,UAAU,eAAgB,IAAI,UAAU,IAAI,SAAS,OAAQ;EAKnE,MAAM,OAAO,UAAU,kBAAkB,8BAA8B,MAAM,MAAM;;;;;;EAOnF,MAAM,UAAU,eACd,MAAM,UAAU,OACZ,KAAK,MAAM,gBACX,KAAK,MAAM,MAAM;GAAE,OAAO,MAAM;GAAO,KAAK,MAAM;EAAI,CAAC,CAC7D;EAEA,SAAa,EAAE,IAAI,CAAC;;GAIlB,OAAA,UAAA,GAAA,YAkEe,MAAA,YAAA,GAlEf,WAkEe;IAjEZ,eAAa,MAAA;IACb,KAAK,QAAA;IACL,cAAY,QAAA;IACZ,mBAAiB,QAAA;GACV,GAAA,MAAA,IAAA,GAAI,EACX,OAAO,MAAA,EAAA,CAAE,CAAC,MAAA,sBAAA,CAAsB,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAK,MAAA,KAAA,CAAK,CAAC,KAAK,EAAA,CAAA,GAAA;IAexD,SAAA,cAsCM,EAtCN,UAAA,GAAA,mBAsCM,OAAA;KArCH,SAAO,OAAS,KAAI,GAAI;KACzB,MAAK;KACL,eAAY;KACX,OAAK,eAAE,MAAA,EAAA,CAAE,CAAA,kCAAmC,IAAA,UAAG,QAAA,cAAA,CAAA;IAEhD,GAAA,CAAA,mBAME,UAAA;KALC,IAAI;KACJ,IAAI;KACJ,GAAG,OAAA;KACH,gBAAc,YAAA;KACf,OAAM;IAQR,GAAA,MAAA,GAAA,UAAA,GAAA,mBAkBE,UAAA;KAjBC,IAAI;KACJ,IAAI;KACJ,GAAG,OAAA;KACH,gBAAc,YAAA;KACd,oBAAkB,UAAA;KAClB,qBAAmB,WAAA;KACnB,kBAAgB,QAAA;KAChB,WAAS,cAAgB,OAAM,GAAI,OAAM;KACzC,OAAK,eAAE,IAAA,UAAG,MAAA,mBAAA,gBAAA;KACV,OAAK,eAAa,IAAA,UAAG,OAAwB,KAAA,IAAA,EAAA,YAAA,sGAAA,CAAA;IAW1C,GAAA,MAAA,IAAA,UAAA,CAAA,GAAA,IAAA,UAAA,IAAA,QAAA,aADR,UAAA,GAAA,mBAKC,QAAA;;KAHC,eAAY;KACX,OAAK,eAAE,MAAA,EAAA,CAAE,CAAA,2BAA4B,WAAW,QAAA,KAAI,CAAA;IACjD,GAAA,gBAAA,QAAA,KAAO,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RadioGroup.js","names":[],"sources":["../../../../packages/primitives/radio-group/src/RadioGroup.vue"],"sourcesContent":["<script lang=\"ts\">\n/** One selectable row in a {@link RadioGroup}. */\nexport interface RadioOption {\n /** Matched against `modelValue`. */\n value: string;\n /** Visible label, rendered beside the control. */\n label: string;\n /** Secondary text, rendered small under the label. */\n description?: string;\n /** Disables this row alone, leaving its siblings pickable. */\n disabled?: boolean;\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { RadioGroupIndicator, RadioGroupItem, RadioGroupRoot } from \"reka-ui\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { optional } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useFieldControl } from \"@ecoma-io/loom-labels\";\n\n/**\n * RadioGroup — a vertical list of mutually exclusive options, each with a\n * visible (and optionally described) label — for longer or explained\n * choices and settings lists. For a compact horizontal toggle among 2-5\n * short options that are all visible at once, reach for SegmentedControl\n * instead. Built on Reka UI's RadioGroup: roving tabindex (the group is one\n * Tab stop, not N) with arrow-key navigation between items.\n *\n * Inside a [Field](../Field/Field.vue) the row's description, `name`,\n * `required` and `invalid` reach the group through `useFieldControl()`, and\n * `name` below still wins wherever it is set — the row and this prop are one\n * concept, not two that can disagree.\n *\n * **A row's label does not name this group.** `<label for>` names a labelable\n * element and this renders a `div[role=\"radiogroup\"]`, so the row's label\n * resolves to the element and names nobody. Name it with a\n * [Fieldset](../Fieldset/Fieldset.vue)'s real `<legend>`, or with an\n * `aria-label`/`aria-labelledby` of its own.\n *\n * There is no `readonly`, and a row's is ignored rather than approximated: each\n * option is a `<button role=\"radio\">` with no native read-only state to put it\n * in, and a group whose options cannot be picked is a disabled group.\n */\nconst props = withDefaults(\n defineProps<{\n /** The selected option's `value`. */\n modelValue?: string;\n /** The rows to render, in order. */\n options: RadioOption[];\n /** Disables every row at once, distinct from a single option's own `disabled`. Unset defers to a wrapping Field. */\n disabled?: boolean | undefined;\n /** Set when this group posts inside a real `<form>` — becomes the submitted field name. Unset defers to a wrapping Field. */\n name?: string;\n }>(),\n {\n // Not `false`: absent has to stay tellable from `false` for a Field above\n // to disable the group and for `:disabled=\"false\"` to overrule one that\n // does. Vue casts an absent Boolean prop with no declared default to\n // `false`, which would leave the context wired and inert.\n disabled: undefined,\n },\n);\n\ndefineEmits<{ \"update:modelValue\": [value: string] }>();\n\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: groupAttrs } = useSplitAttrs();\n\n// One rendered node, so the split is only about `class` — a caller's class\n// needs a Tailwind-aware merge rather than Vue's concatenating fallthrough.\n//\n// `id` and `aria-describedby` arrive as fallthrough attrs rather than props, so\n// they are handed in as this caller's own values; `field.attrs` then spreads\n// **after** `groupAttrs`, and that position is the contract (see TextField.vue).\n// `name` leaves `optional()` below with it: resolved once here, it reaches\n// RadioGroupRoot's own `name` prop, which is what mints the hidden input a\n// `<form>` submits.\n//\n// `required` and `invalid` are omitted because this component has no prop for\n// either — the row's answer stands unopposed. `readonly` is omitted for the\n// reason the docblock gives.\n//\n// The resolved `required` then reaches the group through Reka's own prop rather\n// than the `aria-required` in `field.attrs`: RadioGroupRoot writes that\n// attribute itself, so leaving it to the bag is a race with a `false` — see\n// Checkbox.vue, where the merge order makes the same race a certainty.\nconst field = useFieldControl(() => ({\n id: attrs.id as string | undefined,\n describedBy: attrs[\"aria-describedby\"] as string | undefined,\n name: props.name,\n disabled: props.disabled,\n}));\n</script>\n\n<template>\n <RadioGroupRoot\n v-bind=\"{ ...optional({ modelValue }), ...groupAttrs, ...field.attrs }\"\n :disabled=\"field.disabled\"\n :required=\"field.required\"\n orientation=\"vertical\"\n :class=\"cn('flex flex-col gap-3', attrs.class as string)\"\n @update:model-value=\"$emit('update:modelValue', String($event))\"\n >\n <!-- RadioGroupItem is a labelable <button role=\"radio\">, so wrapping it in\n <label> names it exactly like a native control; the rule's\n nesting/id heuristic just doesn't recognize reka-ui's custom element\n as a \"control\". -->\n <!-- eslint-disable-next-line vuejs-accessibility/label-has-for -->\n <label\n v-for=\"opt in options\"\n :key=\"opt.value\"\n :class=\"\n cn(\n 'flex min-h-6 items-start gap-2 text-sm text-foreground',\n // Disabled text stays readable (AA on the light ground) — only the\n // control glyph dims; quiet, not illegible.\n field.disabled || opt.disabled\n ? 'cursor-not-allowed text-muted-foreground'\n : 'cursor-pointer',\n )\n \"\n >\n <RadioGroupItem\n :value=\"opt.value\"\n :class=\"\n cn(\n 'mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full border border-input bg-background',\n // Springy transform (press squish), steady color — the transform\n // rides --ease-spring while border stays instant --ease-out (style\n // below), same press language as Button/Switch/Checkbox.\n 'active:scale-90',\n // Selected = a decision (Loom law): the ring turns primary.\n 'data-[state=checked]:border-primary',\n // Focus draws the weave tight: the brand ring blooms.\n 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo',\n // Drained, not dimmed — same verdict as Checkbox beside it. The\n // unchecked circle falls to `bg-muted` with the rim every\n // unavailable control slackens to; a checked one keeps its\n // primary ring and dot, because selected is information (the\n // native `disabled` attribute carries unavailability regardless).\n // Compound form deliberate: Tailwind sorts data variants after\n // plain `disabled:`, so a bare `disabled:bg-muted` would lose to\n // `data-[state=*]:*` and never fire.\n 'disabled:cursor-not-allowed disabled:data-[state=unchecked]:bg-muted disabled:data-[state=unchecked]:border-border',\n )\n \"\n :disabled=\"opt.disabled ?? false\"\n style=\"\n transition:\n transform var(--duration-fast) var(--ease-spring),\n border-color var(--duration-instant) var(--ease-out),\n box-shadow var(--duration-fast) var(--ease-out);\n \"\n >\n <!-- The dot pops in with scale-in — a small causal \"it's selected now\". -->\n <RadioGroupIndicator class=\"flex animate-scale-in items-center justify-center\">\n <span class=\"h-2 w-2 rounded-full bg-primary\" />\n </RadioGroupIndicator>\n </RadioGroupItem>\n <span class=\"flex flex-col gap-0.5\">\n <span>{{ opt.label }}</span>\n <span v-if=\"opt.description\" class=\"text-xs text-muted-foreground\">{{\n opt.description\n }}</span>\n </span>\n </label>\n </RadioGroupRoot>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RadioGroup.vue_vue_type_script_setup_true_lang.js","names":["$emit"],"sources":["../../../../packages/primitives/radio-group/src/RadioGroup.vue"],"sourcesContent":["<script lang=\"ts\">\n/** One selectable row in a {@link RadioGroup}. */\nexport interface RadioOption {\n /** Matched against `modelValue`. */\n value: string;\n /** Visible label, rendered beside the control. */\n label: string;\n /** Secondary text, rendered small under the label. */\n description?: string;\n /** Disables this row alone, leaving its siblings pickable. */\n disabled?: boolean;\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { RadioGroupIndicator, RadioGroupItem, RadioGroupRoot } from \"reka-ui\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { optional } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useFieldControl } from \"@ecoma-io/loom-labels\";\n\n/**\n * RadioGroup — a vertical list of mutually exclusive options, each with a\n * visible (and optionally described) label — for longer or explained\n * choices and settings lists. For a compact horizontal toggle among 2-5\n * short options that are all visible at once, reach for SegmentedControl\n * instead. Built on Reka UI's RadioGroup: roving tabindex (the group is one\n * Tab stop, not N) with arrow-key navigation between items.\n *\n * Inside a [Field](../Field/Field.vue) the row's description, `name`,\n * `required` and `invalid` reach the group through `useFieldControl()`, and\n * `name` below still wins wherever it is set — the row and this prop are one\n * concept, not two that can disagree.\n *\n * **A row's label does not name this group.** `<label for>` names a labelable\n * element and this renders a `div[role=\"radiogroup\"]`, so the row's label\n * resolves to the element and names nobody. Name it with a\n * [Fieldset](../Fieldset/Fieldset.vue)'s real `<legend>`, or with an\n * `aria-label`/`aria-labelledby` of its own.\n *\n * There is no `readonly`, and a row's is ignored rather than approximated: each\n * option is a `<button role=\"radio\">` with no native read-only state to put it\n * in, and a group whose options cannot be picked is a disabled group.\n */\nconst props = withDefaults(\n defineProps<{\n /** The selected option's `value`. */\n modelValue?: string;\n /** The rows to render, in order. */\n options: RadioOption[];\n /** Disables every row at once, distinct from a single option's own `disabled`. Unset defers to a wrapping Field. */\n disabled?: boolean | undefined;\n /** Set when this group posts inside a real `<form>` — becomes the submitted field name. Unset defers to a wrapping Field. */\n name?: string;\n }>(),\n {\n // Not `false`: absent has to stay tellable from `false` for a Field above\n // to disable the group and for `:disabled=\"false\"` to overrule one that\n // does. Vue casts an absent Boolean prop with no declared default to\n // `false`, which would leave the context wired and inert.\n disabled: undefined,\n },\n);\n\ndefineEmits<{ \"update:modelValue\": [value: string] }>();\n\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: groupAttrs } = useSplitAttrs();\n\n// One rendered node, so the split is only about `class` — a caller's class\n// needs a Tailwind-aware merge rather than Vue's concatenating fallthrough.\n//\n// `id` and `aria-describedby` arrive as fallthrough attrs rather than props, so\n// they are handed in as this caller's own values; `field.attrs` then spreads\n// **after** `groupAttrs`, and that position is the contract (see TextField.vue).\n// `name` leaves `optional()` below with it: resolved once here, it reaches\n// RadioGroupRoot's own `name` prop, which is what mints the hidden input a\n// `<form>` submits.\n//\n// `required` and `invalid` are omitted because this component has no prop for\n// either — the row's answer stands unopposed. `readonly` is omitted for the\n// reason the docblock gives.\n//\n// The resolved `required` then reaches the group through Reka's own prop rather\n// than the `aria-required` in `field.attrs`: RadioGroupRoot writes that\n// attribute itself, so leaving it to the bag is a race with a `false` — see\n// Checkbox.vue, where the merge order makes the same race a certainty.\nconst field = useFieldControl(() => ({\n id: attrs.id as string | undefined,\n describedBy: attrs[\"aria-describedby\"] as string | undefined,\n name: props.name,\n disabled: props.disabled,\n}));\n</script>\n\n<template>\n <RadioGroupRoot\n v-bind=\"{ ...optional({ modelValue }), ...groupAttrs, ...field.attrs }\"\n :disabled=\"field.disabled\"\n :required=\"field.required\"\n orientation=\"vertical\"\n :class=\"cn('flex flex-col gap-3', attrs.class as string)\"\n @update:model-value=\"$emit('update:modelValue', String($event))\"\n >\n <!-- RadioGroupItem is a labelable <button role=\"radio\">, so wrapping it in\n <label> names it exactly like a native control; the rule's\n nesting/id heuristic just doesn't recognize reka-ui's custom element\n as a \"control\". -->\n <!-- eslint-disable-next-line vuejs-accessibility/label-has-for -->\n <label\n v-for=\"opt in options\"\n :key=\"opt.value\"\n :class=\"\n cn(\n 'flex min-h-6 items-start gap-2 text-sm text-foreground',\n // Disabled text stays readable (AA on the light ground) — only the\n // control glyph dims; quiet, not illegible.\n field.disabled || opt.disabled\n ? 'cursor-not-allowed text-muted-foreground'\n : 'cursor-pointer',\n )\n \"\n >\n <RadioGroupItem\n :value=\"opt.value\"\n :class=\"\n cn(\n 'mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full border border-input bg-background',\n // Springy transform (press squish), steady color — the transform\n // rides --ease-spring while border stays instant --ease-out (style\n // below), same press language as Button/Switch/Checkbox.\n 'active:scale-90',\n // Selected = a decision (Loom law): the ring turns primary.\n 'data-[state=checked]:border-primary',\n // Focus draws the weave tight: the brand ring blooms.\n 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo',\n // Drained, not dimmed — same verdict as Checkbox beside it. The\n // unchecked circle falls to `bg-muted` with the rim every\n // unavailable control slackens to; a checked one keeps its\n // primary ring and dot, because selected is information (the\n // native `disabled` attribute carries unavailability regardless).\n // Compound form deliberate: Tailwind sorts data variants after\n // plain `disabled:`, so a bare `disabled:bg-muted` would lose to\n // `data-[state=*]:*` and never fire.\n 'disabled:cursor-not-allowed disabled:data-[state=unchecked]:bg-muted disabled:data-[state=unchecked]:border-border',\n )\n \"\n :disabled=\"opt.disabled ?? false\"\n style=\"\n transition:\n transform var(--duration-fast) var(--ease-spring),\n border-color var(--duration-instant) var(--ease-out),\n box-shadow var(--duration-fast) var(--ease-out);\n \"\n >\n <!-- The dot pops in with scale-in — a small causal \"it's selected now\". -->\n <RadioGroupIndicator class=\"flex animate-scale-in items-center justify-center\">\n <span class=\"h-2 w-2 rounded-full bg-primary\" />\n </RadioGroupIndicator>\n </RadioGroupItem>\n <span class=\"flex flex-col gap-0.5\">\n <span>{{ opt.label }}</span>\n <span v-if=\"opt.description\" class=\"text-xs text-muted-foreground\">{{\n opt.description\n }}</span>\n </span>\n </label>\n </RadioGroupRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4CA,MAAM,QAAQ;EAuBd,MAAM,EAAE,OAAO,MAAM,eAAe,cAAc;EAoBlD,MAAM,QAAQ,uBAAuB;GACnC,IAAI,MAAM;GACV,aAAa,MAAM;GACnB,MAAM,MAAM;GACZ,UAAU,MAAM;EAClB,EAAE;;GAIA,OAAA,UAAA,GAAA,YAuEiB,MAAA,cAAA,GAvEjB,WAuEiB;IAAA,GAtEF,MAAA,QAAA,CAAQ,CAAA,EAAA,YAAG,QAAA,WAAU,CAAA;IAAA,GAAQ,MAAA,UAAA;IAAU,GAAK,MAAA,KAAA,CAAK,CAAC;GAAK,GAAA;IACnE,UAAU,MAAA,KAAA,CAAK,CAAC;IAChB,UAAU,MAAA,KAAA,CAAK,CAAC;IACjB,aAAY;IACX,OAAO,MAAA,EAAA,CAAE,CAAA,uBAAwB,MAAA,KAAA,CAAK,CAAC,KAAK;IAC5C,uBAAkB,OAAA,OAAA,OAAA,MAAA,WAAEA,KAAAA,MAAK,qBAAsB,OAAO,MAAM,CAAA;;IAQ3D,SAAA,cAAsB,EADxB,UAAA,IAAA,GAAA,mBAyDQ,UAAA,MAAA,WAxDQ,QAAA,UAAP,QAAG;KADZ,OAAA,UAAA,GAAA,mBAyDQ,SAAA;MAvDL,KAAK,IAAI;MACT,OAAK,eAAW,MAAA,EAAA,CAAE,CAAA,0DAAqN,MAAA,KAAA,CAAK,CAAC,YAAY,IAAI,WAAA,6CAAA,gBAAA,CAAA;KAW9P,GAAA,CAAA,YAoCiB,MAAA,cAAA,GAAA;MAnCd,OAAO,IAAI;MACX,OAAK,eAAa,MAAA,EAAA,CAAE,CAAA,2GAAA,mBAAA,uCAAA,+GAAA,oHAAA,CAAA;MAsBpB,UAAU,IAAI,YAAQ;MACvB,OAAA,EAAA,cAAA,qLAAA;;MAQA,SAAA,cAEsB,CAFtB,YAEsB,MAAA,mBAAA,GAAA,EAFD,OAAM,oDAAmD,GAAA;OAC5E,SAAA,cAAgD,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAhD,mBAAgD,QAAA,EAA1C,OAAM,kCAAiC,GAAA,MAAA,EAAA,CAAA,EAAA,CAAA;;;;;;;;KAGjD,CAAA,GAAA,mBAKO,QALP,YAKO,CAJL,mBAA4B,QAAA,MAAA,gBAAnB,IAAI,KAAK,GAAA,CAAA,GACN,IAAI,eAAhB,UAAA,GAAA,mBAES,QAFT,YAES,gBADP,IAAI,WAAW,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,CAAA,GAAA,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Rating.js","names":[],"sources":["../../../../packages/primitives/rating/src/Rating.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { RatingLabels } from \"@ecoma-io/loom-labels\";\nimport { cva } from \"class-variance-authority\";\n\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 RATING_LABELS: RatingLabels = {\n score: ({ score, length }) => `${String(Number(score.toFixed(2)))} of ${String(length)} stars`,\n};\n\n/**\n * Star size. Deliberately *not* the control-height scale the text input,\n * Select and Button share: a rating is a row of glyphs rather than a box a\n * form row aligns against, so it sizes by the icon. It follows the icon scale\n * instead — 16 / 20 / 24px — which is the same ladder Spinner uses.\n */\nexport type RatingSize = \"sm\" | \"md\" | \"lg\";\n\n/**\n * One star's box, and it is applied to three nodes at once: the item, the\n * empty glyph and the filled glyph.\n *\n * The half-star is a clip, and a clip needs a box to be a fraction *of*. The\n * filled glyph inside a 50%-wide window has to keep the width of a whole star\n * or it renders as a squashed whole star rather than as half of one — so its\n * size comes from this map rather than from the icon's own intrinsic width,\n * and changing `size` moves the window and the glyph together.\n */\nconst starVariants = cva(\"block shrink-0\", {\n variants: {\n size: { sm: \"h-4 w-4\", md: \"h-5 w-5\", lg: \"h-6 w-6\" } satisfies Record<RatingSize, string>,\n },\n defaultVariants: { size: \"md\" },\n});\n\n/** The row itself: the gap grows with the stars so the rhythm holds at every size. */\nconst rowVariants = cva(\"inline-flex min-h-6 items-center\", {\n variants: {\n size: { sm: \"gap-0.5\", md: \"gap-1\", lg: \"gap-1.5\" } satisfies Record<RatingSize, string>,\n },\n defaultVariants: { size: \"md\" },\n});\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, useTemplateRef } from \"vue\";\nimport { RatingRoot, RatingItem, RatingItemIndicator } from \"reka-ui\";\nimport { Star } 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 { useAncestorDisabled } from \"@ecoma-io/loom-labels\";\nimport { useFieldControl } from \"@ecoma-io/loom-labels\";\nimport { useLabels, type LabelOverrides } from \"@ecoma-io/loom-labels\";\n\n/**\n * Rating — a score on a small, fixed, ordinal scale, drawn as a row of stars.\n * Reach for it where the scale itself is the vocabulary a reader already\n * knows: how good was this, how confident are you, how would you rate it.\n *\n * Its neighbours: a value on a continuous range whose ends are the\n * information — a volume, a ratio — is a Slider; a choice between named\n * options that happen to be ordered (\"poor\", \"fair\", \"good\") is a\n * SegmentedControl or a RadioGroup, because the names carry meaning stars\n * cannot; and a number with no meaningful ceiling is a NumberField.\n *\n * **`readonly` and `disabled` are different controls, not two dials on one.**\n * A product's average score on a card is `readonly`: it is a picture of a\n * number, so it renders as `role=\"img\"` with the score in its accessible\n * name, is not a Tab stop, is not in the radio group at all, and is *not*\n * dimmed — nothing about it is unavailable, it was never an input. A rating\n * inside a form the reader may not edit yet is `disabled`: it stays a radio\n * group, dims, and refuses the pointer and the keyboard. Conflating the two\n * is the standard defect in this control — a read-only average rendered as a\n * disabled input reads as \"broken\" to a sighted reader and as \"unavailable\n * input\" to a screen reader, and neither is true.\n *\n * Built on Reka UI's Rating, which is a RadioGroup underneath: the row is one\n * Tab stop with roving focus, arrow keys move between steps and select as\n * they go, Home and End reach the ends.\n *\n * **`step` is narrowed on purpose.** Reka's `RatingRoot` also accepts `0.25`\n * and `0.1`. A quarter of a 20px star is a 5px-wide pointer target and a\n * tenth is 2px, and at that size the difference between 4.3 and 4.4 stars is\n * not perceptible in the rendering either — so the value could be neither\n * aimed at nor read back. An option that cannot be used correctly is not an\n * option, so the two are dropped here rather than forwarded.\n *\n * `step` governs what can be *aimed at*, which is a question only the\n * interactive control asks. A `readonly` rating therefore ignores it and\n * paints the exact value it was given: an average of 4.2 shows and announces\n * 4.2, not the nearest half.\n *\n * Inside a [Field](../Field/Field.vue) it wires itself through\n * `useFieldControl()`: the row's description, `name`, `required` and `invalid`\n * land on the radio group, its `name` mints the hidden input a real `<form>`\n * submits, and its `readonly` switches this control into the picture above —\n * which is the one place this family's `readonly` does not mean \"a native\n * control with the native attribute set\". There is no native element to set it\n * on: each step is a `<button role=\"radio\">`, and `readonly` is as inert on a\n * radio as it is on a checkbox. So \"still a Tab stop, still submitted\" is\n * unreachable here, and the picture is what is left that is honest — a score\n * that was never an input, and, above all, not something dimmed to look\n * unavailable.\n *\n * **A row's label does not name this control.** `<label for>` names a labelable\n * element and both branches render a `div`, so name the group with a\n * [Fieldset](../Fieldset/Fieldset.vue)'s real `<legend>` or with an\n * `aria-label` of its own. A read-only rating already names itself from its\n * score.\n */\nconst props = withDefaults(\n defineProps<{\n /** The score, in stars. Leave it unset to let the rating manage its own. */\n modelValue?: number;\n /** How many stars the scale has — the maximum, and what a reader is scored out of. */\n length?: number;\n /** Granularity of one arrow-key step and of the pointer: whole stars, or halves. */\n step?: 1 | 0.5;\n /** Renders the score as a picture of a number rather than an input: no Tab stop, no dimming, and the exact value regardless of `step`. Unset defers to a wrapping Field. */\n readonly?: boolean | undefined;\n /** Lets a reader clear the score by choosing the star that is already chosen, which emits `0`. */\n clearable?: boolean;\n /** Previews the score under the pointer while hovering, before anything is chosen. */\n hoverable?: boolean;\n /** Unavailable: dims the row and refuses both the pointer and the keyboard. For a score that was never editable, use `readonly`. Unset defers to a wrapping Field. */\n disabled?: boolean | undefined;\n /** Star size. It follows the icon scale, not the control-height scale. */\n size?: RatingSize;\n /**\n * The name it gives a score, as any subset of `RatingLabels` — the rest\n * stay as the host's `provideLoomLabels` vocabulary left them, and then as\n * Loom's English.\n *\n * This is the per-instance correction, not the place to localise an\n * application: the case it exists for is a scale whose stars are not stars\n * — five chillies for heat, five bars for confidence — where the noun in\n * the name has to say so however well the application is translated.\n */\n labels?: LabelOverrides<RatingLabels>;\n }>(),\n {\n length: 5,\n step: 1,\n clearable: false,\n hoverable: false,\n // Not `false`, for either. `false` is a caller saying \"this rating is\n // editable / live even though its row is not\"; absent is a caller saying\n // nothing, and only `undefined` survives to mean that past Vue's\n // absent-Boolean-casts-to-false rule — see TextField.vue, which carries the\n // full reasoning.\n readonly: undefined,\n disabled: undefined,\n size: \"md\",\n },\n);\n\ndefineEmits<{\n /** The newly chosen score in stars, or `0` when a `clearable` rating is cleared. */\n \"update:modelValue\": [value: number];\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(\"rating\", RATING_LABELS, () => props.labels);\n\n// Two roots, one branch each, so `class` needs a Tailwind-aware merge on\n// whichever one renders — Vue's own fallthrough merge only concatenates, and a\n// caller's `gap-2` would then win or lose depending on which utility Tailwind\n// happened to emit last. Every other attribute (`aria-label` naming the group,\n// a test hook) rides `rest` onto the same node.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest } = useSplitAttrs();\n\n/**\n * Each step is a real `<button>`, so a `<fieldset disabled>` above this row\n * already makes the score unsettable — measured on this branch, a click and an\n * arrow key both left the value where it was. What it does not reach is Reka's\n * roving focus, which is driven by the row's own `disabled`: every step went\n * `:disabled` while the `role=\"radiogroup\"` container kept `tabindex=\"0\"`, so\n * Tab still stopped on an unavailable control. Its own `disabled` prop sets\n * that to `-1`.\n *\n * Reading the state here rather than painting it with a CSS variant is what\n * makes the two disabled renderings the same rendering, `data-disabled` and\n * all, instead of two class lists that have to be kept in step by hand.\n *\n * Read off the DOM rather than taken from the Field context, which publishes no\n * `disabled` on purpose — see `../../lib/ancestor-disabled.ts`.\n */\nconst root = useTemplateRef<{ $el?: Element }>(\"root\");\nconst groupDisabled = useAncestorDisabled(() => root.value?.$el);\n\n// `id` and `aria-describedby` arrive as fallthrough attrs rather than props, so\n// they are handed in as this caller's own values: a caller who sets either\n// still beats the row, and the row's description is merged into theirs rather\n// than replacing it. `name`, `required` and `invalid` are absent because this\n// component has no prop for any of them — the row's answer stands unopposed.\n//\n// The resolved `required` then reaches the group through Reka's own prop rather\n// than the `aria-required` in `field.attrs`: RadioGroupRoot underneath writes\n// that attribute itself, so leaving it to the bag is a race — see Checkbox.vue,\n// where the merge order makes the same race a certainty.\nconst field = useFieldControl(() => ({\n id: attrs.id as string | undefined,\n describedBy: attrs[\"aria-describedby\"] as string | undefined,\n // The fieldset beats the prop in both directions, exactly as it does for the\n // step buttons themselves.\n disabled: groupDisabled.value ? true : props.disabled,\n readonly: props.readonly,\n}));\n\n/**\n * The two keys of the resolved bag that a picture of a number may carry.\n *\n * The read-only branch is a `role=\"img\"`, and ARIA allows neither\n * `aria-required` nor `name` on one — `aria-allowed-attr` is a WCAG 4.1.2 rule\n * `e2e/accessibility.e2e.ts` runs over every docs page, so the whole bag\n * spread here would turn that gate red. They would also both be claims about\n * an input, and this branch renders none: nothing is required of a reader who\n * is being shown a number, and nothing is submitted.\n */\nconst pictureAttrs = computed(() =>\n optional({ id: field.id, \"aria-describedby\": field.attrs[\"aria-describedby\"] }),\n);\n\n/**\n * The score as it is drawn and announced, clamped into the range the stars can\n * actually show. Without the clamp a value of 9 on a 5-star scale paints five\n * stars and announces \"9 of 5\", which is a number no reader can reconcile with\n * the picture beside it.\n */\nconst score = computed(() => Math.min(Math.max(props.modelValue ?? 0, 0), props.length));\n\nconst stars = computed(() => Array.from({ length: props.length }, (_, index) => index + 1));\n\n/**\n * How much of star `n` is painted, as a percentage — the read-only clip\n * window. Rounded, because binary floating point turns a score of 4.2 into a\n * width of `20.000000000000018%`: correct to the pixel and unreadable in a\n * DOM inspector or a test failure.\n */\nfunction fillOf(star: number): string {\n const filled = Math.min(Math.max(score.value - (star - 1), 0), 1);\n return `${String(Number((filled * 100).toFixed(2)))}%`;\n}\n</script>\n\n<template>\n <!-- The read-only branch renders no radio group, because there is no choice\n to offer: one `role=\"img\"` node carrying the score as its name, and\n nothing inside it that a Tab reaches. -->\n <div\n v-if=\"field.readonly\"\n v-bind=\"{ ...rest, ...pictureAttrs }\"\n role=\"img\"\n :aria-label=\"text.score({ score, length })\"\n :class=\"cn(rowVariants({ size }), attrs.class as string)\"\n >\n <span v-for=\"star in stars\" :key=\"star\" :class=\"cn(starVariants({ size }), 'relative')\">\n <!-- The empty stars are what say how many there were to earn, so they\n are a graphical object carrying information and are held to 1.4.11's\n 3:1 rather than painted as a faint hairline — hence the foreground\n neutral and not a border token. -->\n <Star\n aria-hidden=\"true\"\n :class=\"cn(starVariants({ size }), 'absolute inset-0 text-muted-foreground')\"\n />\n <!-- The fill is a window over a whole star, not a second glyph: a\n fraction of a star drawn by clipping is the only version that stays\n a star at any width. -->\n <span class=\"absolute inset-y-0 left-0 overflow-hidden\" :style=\"{ width: fillOf(star) }\">\n <Star\n aria-hidden=\"true\"\n :class=\"cn(starVariants({ size }), 'fill-primary text-primary-text')\"\n />\n </span>\n </span>\n </div>\n\n <RatingRoot\n v-else\n ref=\"root\"\n v-slot=\"{ items }\"\n v-bind=\"{ ...rest, ...optional({ modelValue }), ...field.attrs }\"\n :length=\"length\"\n :step=\"step\"\n :clearable=\"clearable\"\n :hoverable=\"hoverable\"\n :disabled=\"field.disabled\"\n :required=\"field.required\"\n :class=\"\n cn(\n rowVariants({ size }),\n // Unavailability is painted in measured greys, never as an alpha over\n // the available state: `opacity-50` here took the empty stars from\n // 5.25:1 to 2.05:1 on the page ground, and the stars are the control's\n // *data* — the filled ones are the score, the empty ones the maximum —\n // which WCAG 1.4.11 holds to 3:1 as graphical objects. The two glyphs\n // drain to their own tokens below, keyed off `data-disabled` so the\n // prop and the enclosing fieldset paint the same picture.\n 'group data-[disabled]:cursor-not-allowed',\n attrs.class as string,\n )\n \"\n @update:model-value=\"$emit('update:modelValue', $event)\"\n >\n <!-- `as=\"span\"`: Reka renders the item as a `<label>` by default, and a\n label with no text of its own names nothing while still claiming to.\n The names live on the radios below, one per step. -->\n <RatingItem\n v-for=\"item in items\"\n :key=\"item\"\n v-slot=\"{ steps }\"\n as=\"span\"\n :item=\"item\"\n :class=\"cn(starVariants({ size }), 'relative')\"\n >\n <Star\n aria-hidden=\"true\"\n :class=\"\n cn(\n starVariants({ size }),\n // The empty star is the maximum, so when the row is unavailable it\n // keeps a colour of its own rather than taking the row's old\n // alpha — one measured step lighter than the score, held to the\n // 3:1 floor `muted-foreground` itself was measured against.\n 'absolute inset-0 text-muted-foreground group-data-[disabled]:text-muted-foreground-soft',\n )\n \"\n />\n <!-- One radio per step, stacked over the empty star and clipped to the\n fraction it stands for: with `step` 0.5 the left half of the glyph\n is a 50%-wide button sitting above the whole-star one, so the\n pointer lands on the half a reader aimed at. All three custom\n properties come from Reka — the width of this step, whether a half\n step is currently worth showing, and the stacking order that keeps\n the narrower button reachable. -->\n <RatingItemIndicator\n v-for=\"stepValue in steps\"\n :key=\"stepValue\"\n :step=\"stepValue\"\n :aria-label=\"text.score({ score: stepValue, length })\"\n class=\"group/step absolute inset-y-0 left-0 min-h-6 cursor-pointer overflow-hidden rounded-sm focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo disabled:cursor-not-allowed\"\n style=\"\n width: var(--reka-rating-item-step-width);\n opacity: var(--reka-rating-item-step-opacity);\n z-index: var(--reka-rating-item-step-z-index);\n \"\n >\n <!-- Two of the button's own states, read one level down. `data-state`\n is `active` on every step at or below the score — or below the\n pointer, while hovering — and that one only fades: a preview that\n lags the pointer feels broken, so it rides --duration-instant.\n `aria-checked` is true on the single step that *is* the score, so\n the star a reader just chose settles on the spring while the ones\n it dragged along behind it merely fill.\n Both are scoped to the state rather than declared bare, which is\n what lets the animation replay on each new choice instead of\n running once at mount and never again. -->\n <Star\n aria-hidden=\"true\"\n :class=\"\n cn(\n starVariants({ size }),\n 'fill-primary text-primary-text opacity-0 transition-opacity duration-instant ease-out',\n // Unavailable, the score drains to the neutral well like every\n // other control's value — solid still, so it stays distinct from\n // the hollow maximum beside it.\n 'group-data-[disabled]/step:fill-muted-foreground group-data-[disabled]/step:text-muted-foreground',\n 'group-data-[state=active]/step:opacity-100',\n 'group-aria-checked/step:animate-scale-in',\n )\n \"\n />\n </RatingItemIndicator>\n </RatingItem>\n </RatingRoot>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Rating.vue_vue_type_script_setup_true_lang.js","names":["$emit"],"sources":["../../../../packages/primitives/rating/src/Rating.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { RatingLabels } from \"@ecoma-io/loom-labels\";\nimport { cva } from \"class-variance-authority\";\n\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 RATING_LABELS: RatingLabels = {\n score: ({ score, length }) => `${String(Number(score.toFixed(2)))} of ${String(length)} stars`,\n};\n\n/**\n * Star size. Deliberately *not* the control-height scale the text input,\n * Select and Button share: a rating is a row of glyphs rather than a box a\n * form row aligns against, so it sizes by the icon. It follows the icon scale\n * instead — 16 / 20 / 24px — which is the same ladder Spinner uses.\n */\nexport type RatingSize = \"sm\" | \"md\" | \"lg\";\n\n/**\n * One star's box, and it is applied to three nodes at once: the item, the\n * empty glyph and the filled glyph.\n *\n * The half-star is a clip, and a clip needs a box to be a fraction *of*. The\n * filled glyph inside a 50%-wide window has to keep the width of a whole star\n * or it renders as a squashed whole star rather than as half of one — so its\n * size comes from this map rather than from the icon's own intrinsic width,\n * and changing `size` moves the window and the glyph together.\n */\nconst starVariants = cva(\"block shrink-0\", {\n variants: {\n size: { sm: \"h-4 w-4\", md: \"h-5 w-5\", lg: \"h-6 w-6\" } satisfies Record<RatingSize, string>,\n },\n defaultVariants: { size: \"md\" },\n});\n\n/** The row itself: the gap grows with the stars so the rhythm holds at every size. */\nconst rowVariants = cva(\"inline-flex min-h-6 items-center\", {\n variants: {\n size: { sm: \"gap-0.5\", md: \"gap-1\", lg: \"gap-1.5\" } satisfies Record<RatingSize, string>,\n },\n defaultVariants: { size: \"md\" },\n});\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, useTemplateRef } from \"vue\";\nimport { RatingRoot, RatingItem, RatingItemIndicator } from \"reka-ui\";\nimport { Star } 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 { useAncestorDisabled } from \"@ecoma-io/loom-labels\";\nimport { useFieldControl } from \"@ecoma-io/loom-labels\";\nimport { useLabels, type LabelOverrides } from \"@ecoma-io/loom-labels\";\n\n/**\n * Rating — a score on a small, fixed, ordinal scale, drawn as a row of stars.\n * Reach for it where the scale itself is the vocabulary a reader already\n * knows: how good was this, how confident are you, how would you rate it.\n *\n * Its neighbours: a value on a continuous range whose ends are the\n * information — a volume, a ratio — is a Slider; a choice between named\n * options that happen to be ordered (\"poor\", \"fair\", \"good\") is a\n * SegmentedControl or a RadioGroup, because the names carry meaning stars\n * cannot; and a number with no meaningful ceiling is a NumberField.\n *\n * **`readonly` and `disabled` are different controls, not two dials on one.**\n * A product's average score on a card is `readonly`: it is a picture of a\n * number, so it renders as `role=\"img\"` with the score in its accessible\n * name, is not a Tab stop, is not in the radio group at all, and is *not*\n * dimmed — nothing about it is unavailable, it was never an input. A rating\n * inside a form the reader may not edit yet is `disabled`: it stays a radio\n * group, dims, and refuses the pointer and the keyboard. Conflating the two\n * is the standard defect in this control — a read-only average rendered as a\n * disabled input reads as \"broken\" to a sighted reader and as \"unavailable\n * input\" to a screen reader, and neither is true.\n *\n * Built on Reka UI's Rating, which is a RadioGroup underneath: the row is one\n * Tab stop with roving focus, arrow keys move between steps and select as\n * they go, Home and End reach the ends.\n *\n * **`step` is narrowed on purpose.** Reka's `RatingRoot` also accepts `0.25`\n * and `0.1`. A quarter of a 20px star is a 5px-wide pointer target and a\n * tenth is 2px, and at that size the difference between 4.3 and 4.4 stars is\n * not perceptible in the rendering either — so the value could be neither\n * aimed at nor read back. An option that cannot be used correctly is not an\n * option, so the two are dropped here rather than forwarded.\n *\n * `step` governs what can be *aimed at*, which is a question only the\n * interactive control asks. A `readonly` rating therefore ignores it and\n * paints the exact value it was given: an average of 4.2 shows and announces\n * 4.2, not the nearest half.\n *\n * Inside a [Field](../Field/Field.vue) it wires itself through\n * `useFieldControl()`: the row's description, `name`, `required` and `invalid`\n * land on the radio group, its `name` mints the hidden input a real `<form>`\n * submits, and its `readonly` switches this control into the picture above —\n * which is the one place this family's `readonly` does not mean \"a native\n * control with the native attribute set\". There is no native element to set it\n * on: each step is a `<button role=\"radio\">`, and `readonly` is as inert on a\n * radio as it is on a checkbox. So \"still a Tab stop, still submitted\" is\n * unreachable here, and the picture is what is left that is honest — a score\n * that was never an input, and, above all, not something dimmed to look\n * unavailable.\n *\n * **A row's label does not name this control.** `<label for>` names a labelable\n * element and both branches render a `div`, so name the group with a\n * [Fieldset](../Fieldset/Fieldset.vue)'s real `<legend>` or with an\n * `aria-label` of its own. A read-only rating already names itself from its\n * score.\n */\nconst props = withDefaults(\n defineProps<{\n /** The score, in stars. Leave it unset to let the rating manage its own. */\n modelValue?: number;\n /** How many stars the scale has — the maximum, and what a reader is scored out of. */\n length?: number;\n /** Granularity of one arrow-key step and of the pointer: whole stars, or halves. */\n step?: 1 | 0.5;\n /** Renders the score as a picture of a number rather than an input: no Tab stop, no dimming, and the exact value regardless of `step`. Unset defers to a wrapping Field. */\n readonly?: boolean | undefined;\n /** Lets a reader clear the score by choosing the star that is already chosen, which emits `0`. */\n clearable?: boolean;\n /** Previews the score under the pointer while hovering, before anything is chosen. */\n hoverable?: boolean;\n /** Unavailable: dims the row and refuses both the pointer and the keyboard. For a score that was never editable, use `readonly`. Unset defers to a wrapping Field. */\n disabled?: boolean | undefined;\n /** Star size. It follows the icon scale, not the control-height scale. */\n size?: RatingSize;\n /**\n * The name it gives a score, as any subset of `RatingLabels` — the rest\n * stay as the host's `provideLoomLabels` vocabulary left them, and then as\n * Loom's English.\n *\n * This is the per-instance correction, not the place to localise an\n * application: the case it exists for is a scale whose stars are not stars\n * — five chillies for heat, five bars for confidence — where the noun in\n * the name has to say so however well the application is translated.\n */\n labels?: LabelOverrides<RatingLabels>;\n }>(),\n {\n length: 5,\n step: 1,\n clearable: false,\n hoverable: false,\n // Not `false`, for either. `false` is a caller saying \"this rating is\n // editable / live even though its row is not\"; absent is a caller saying\n // nothing, and only `undefined` survives to mean that past Vue's\n // absent-Boolean-casts-to-false rule — see TextField.vue, which carries the\n // full reasoning.\n readonly: undefined,\n disabled: undefined,\n size: \"md\",\n },\n);\n\ndefineEmits<{\n /** The newly chosen score in stars, or `0` when a `clearable` rating is cleared. */\n \"update:modelValue\": [value: number];\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(\"rating\", RATING_LABELS, () => props.labels);\n\n// Two roots, one branch each, so `class` needs a Tailwind-aware merge on\n// whichever one renders — Vue's own fallthrough merge only concatenates, and a\n// caller's `gap-2` would then win or lose depending on which utility Tailwind\n// happened to emit last. Every other attribute (`aria-label` naming the group,\n// a test hook) rides `rest` onto the same node.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest } = useSplitAttrs();\n\n/**\n * Each step is a real `<button>`, so a `<fieldset disabled>` above this row\n * already makes the score unsettable — measured on this branch, a click and an\n * arrow key both left the value where it was. What it does not reach is Reka's\n * roving focus, which is driven by the row's own `disabled`: every step went\n * `:disabled` while the `role=\"radiogroup\"` container kept `tabindex=\"0\"`, so\n * Tab still stopped on an unavailable control. Its own `disabled` prop sets\n * that to `-1`.\n *\n * Reading the state here rather than painting it with a CSS variant is what\n * makes the two disabled renderings the same rendering, `data-disabled` and\n * all, instead of two class lists that have to be kept in step by hand.\n *\n * Read off the DOM rather than taken from the Field context, which publishes no\n * `disabled` on purpose — see `../../lib/ancestor-disabled.ts`.\n */\nconst root = useTemplateRef<{ $el?: Element }>(\"root\");\nconst groupDisabled = useAncestorDisabled(() => root.value?.$el);\n\n// `id` and `aria-describedby` arrive as fallthrough attrs rather than props, so\n// they are handed in as this caller's own values: a caller who sets either\n// still beats the row, and the row's description is merged into theirs rather\n// than replacing it. `name`, `required` and `invalid` are absent because this\n// component has no prop for any of them — the row's answer stands unopposed.\n//\n// The resolved `required` then reaches the group through Reka's own prop rather\n// than the `aria-required` in `field.attrs`: RadioGroupRoot underneath writes\n// that attribute itself, so leaving it to the bag is a race — see Checkbox.vue,\n// where the merge order makes the same race a certainty.\nconst field = useFieldControl(() => ({\n id: attrs.id as string | undefined,\n describedBy: attrs[\"aria-describedby\"] as string | undefined,\n // The fieldset beats the prop in both directions, exactly as it does for the\n // step buttons themselves.\n disabled: groupDisabled.value ? true : props.disabled,\n readonly: props.readonly,\n}));\n\n/**\n * The two keys of the resolved bag that a picture of a number may carry.\n *\n * The read-only branch is a `role=\"img\"`, and ARIA allows neither\n * `aria-required` nor `name` on one — `aria-allowed-attr` is a WCAG 4.1.2 rule\n * `e2e/accessibility.e2e.ts` runs over every docs page, so the whole bag\n * spread here would turn that gate red. They would also both be claims about\n * an input, and this branch renders none: nothing is required of a reader who\n * is being shown a number, and nothing is submitted.\n */\nconst pictureAttrs = computed(() =>\n optional({ id: field.id, \"aria-describedby\": field.attrs[\"aria-describedby\"] }),\n);\n\n/**\n * The score as it is drawn and announced, clamped into the range the stars can\n * actually show. Without the clamp a value of 9 on a 5-star scale paints five\n * stars and announces \"9 of 5\", which is a number no reader can reconcile with\n * the picture beside it.\n */\nconst score = computed(() => Math.min(Math.max(props.modelValue ?? 0, 0), props.length));\n\nconst stars = computed(() => Array.from({ length: props.length }, (_, index) => index + 1));\n\n/**\n * How much of star `n` is painted, as a percentage — the read-only clip\n * window. Rounded, because binary floating point turns a score of 4.2 into a\n * width of `20.000000000000018%`: correct to the pixel and unreadable in a\n * DOM inspector or a test failure.\n */\nfunction fillOf(star: number): string {\n const filled = Math.min(Math.max(score.value - (star - 1), 0), 1);\n return `${String(Number((filled * 100).toFixed(2)))}%`;\n}\n</script>\n\n<template>\n <!-- The read-only branch renders no radio group, because there is no choice\n to offer: one `role=\"img\"` node carrying the score as its name, and\n nothing inside it that a Tab reaches. -->\n <div\n v-if=\"field.readonly\"\n v-bind=\"{ ...rest, ...pictureAttrs }\"\n role=\"img\"\n :aria-label=\"text.score({ score, length })\"\n :class=\"cn(rowVariants({ size }), attrs.class as string)\"\n >\n <span v-for=\"star in stars\" :key=\"star\" :class=\"cn(starVariants({ size }), 'relative')\">\n <!-- The empty stars are what say how many there were to earn, so they\n are a graphical object carrying information and are held to 1.4.11's\n 3:1 rather than painted as a faint hairline — hence the foreground\n neutral and not a border token. -->\n <Star\n aria-hidden=\"true\"\n :class=\"cn(starVariants({ size }), 'absolute inset-0 text-muted-foreground')\"\n />\n <!-- The fill is a window over a whole star, not a second glyph: a\n fraction of a star drawn by clipping is the only version that stays\n a star at any width. -->\n <span class=\"absolute inset-y-0 left-0 overflow-hidden\" :style=\"{ width: fillOf(star) }\">\n <Star\n aria-hidden=\"true\"\n :class=\"cn(starVariants({ size }), 'fill-primary text-primary-text')\"\n />\n </span>\n </span>\n </div>\n\n <RatingRoot\n v-else\n ref=\"root\"\n v-slot=\"{ items }\"\n v-bind=\"{ ...rest, ...optional({ modelValue }), ...field.attrs }\"\n :length=\"length\"\n :step=\"step\"\n :clearable=\"clearable\"\n :hoverable=\"hoverable\"\n :disabled=\"field.disabled\"\n :required=\"field.required\"\n :class=\"\n cn(\n rowVariants({ size }),\n // Unavailability is painted in measured greys, never as an alpha over\n // the available state: `opacity-50` here took the empty stars from\n // 5.25:1 to 2.05:1 on the page ground, and the stars are the control's\n // *data* — the filled ones are the score, the empty ones the maximum —\n // which WCAG 1.4.11 holds to 3:1 as graphical objects. The two glyphs\n // drain to their own tokens below, keyed off `data-disabled` so the\n // prop and the enclosing fieldset paint the same picture.\n 'group data-[disabled]:cursor-not-allowed',\n attrs.class as string,\n )\n \"\n @update:model-value=\"$emit('update:modelValue', $event)\"\n >\n <!-- `as=\"span\"`: Reka renders the item as a `<label>` by default, and a\n label with no text of its own names nothing while still claiming to.\n The names live on the radios below, one per step. -->\n <RatingItem\n v-for=\"item in items\"\n :key=\"item\"\n v-slot=\"{ steps }\"\n as=\"span\"\n :item=\"item\"\n :class=\"cn(starVariants({ size }), 'relative')\"\n >\n <Star\n aria-hidden=\"true\"\n :class=\"\n cn(\n starVariants({ size }),\n // The empty star is the maximum, so when the row is unavailable it\n // keeps a colour of its own rather than taking the row's old\n // alpha — one measured step lighter than the score, held to the\n // 3:1 floor `muted-foreground` itself was measured against.\n 'absolute inset-0 text-muted-foreground group-data-[disabled]:text-muted-foreground-soft',\n )\n \"\n />\n <!-- One radio per step, stacked over the empty star and clipped to the\n fraction it stands for: with `step` 0.5 the left half of the glyph\n is a 50%-wide button sitting above the whole-star one, so the\n pointer lands on the half a reader aimed at. All three custom\n properties come from Reka — the width of this step, whether a half\n step is currently worth showing, and the stacking order that keeps\n the narrower button reachable. -->\n <RatingItemIndicator\n v-for=\"stepValue in steps\"\n :key=\"stepValue\"\n :step=\"stepValue\"\n :aria-label=\"text.score({ score: stepValue, length })\"\n class=\"group/step absolute inset-y-0 left-0 min-h-6 cursor-pointer overflow-hidden rounded-sm focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo disabled:cursor-not-allowed\"\n style=\"\n width: var(--reka-rating-item-step-width);\n opacity: var(--reka-rating-item-step-opacity);\n z-index: var(--reka-rating-item-step-z-index);\n \"\n >\n <!-- Two of the button's own states, read one level down. `data-state`\n is `active` on every step at or below the score — or below the\n pointer, while hovering — and that one only fades: a preview that\n lags the pointer feels broken, so it rides --duration-instant.\n `aria-checked` is true on the single step that *is* the score, so\n the star a reader just chose settles on the spring while the ones\n it dragged along behind it merely fill.\n Both are scoped to the state rather than declared bare, which is\n what lets the animation replay on each new choice instead of\n running once at mount and never again. -->\n <Star\n aria-hidden=\"true\"\n :class=\"\n cn(\n starVariants({ size }),\n 'fill-primary text-primary-text opacity-0 transition-opacity duration-instant ease-out',\n // Unavailable, the score drains to the neutral well like every\n // other control's value — solid still, so it stays distinct from\n // the hollow maximum beside it.\n 'group-data-[disabled]/step:fill-muted-foreground group-data-[disabled]/step:text-muted-foreground',\n 'group-data-[state=active]/step:opacity-100',\n 'group-aria-checked/step:animate-scale-in',\n )\n \"\n />\n </RatingItemIndicator>\n </RatingItem>\n </RatingRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAUA,IAAa,gBAA8B,EACzC,QAAQ,EAAE,OAAO,aAAa,GAAG,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,OAAO,MAAM,EAAE,QACzF;;;;;;;;;;;AAoBA,IAAM,eAAe,IAAI,kBAAkB;CACzC,UAAU,EACR,MAAM;EAAE,IAAI;EAAW,IAAI;EAAW,IAAI;CAAU,EACtD;CACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CAAC;;AAGD,IAAM,cAAc,IAAI,oCAAoC;CAC1D,UAAU,EACR,MAAM;EAAE,IAAI;EAAW,IAAI;EAAS,IAAI;CAAU,EACpD;CACA,iBAAiB,EAAE,MAAM,KAAK;AAChC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsED,MAAM,QAAQ;EAsDd,MAAM,OAAO,UAAU,UAAU,qBAAqB,MAAM,MAAM;EAQlE,MAAM,EAAE,OAAO,SAAS,cAAc;;;;;;;;;;;;;;;;;EAkBtC,MAAM,OAAO,eAAkC,MAAM;EACrD,MAAM,gBAAgB,0BAA0B,KAAK,OAAO,GAAG;EAY/D,MAAM,QAAQ,uBAAuB;GACnC,IAAI,MAAM;GACV,aAAa,MAAM;GAGnB,UAAU,cAAc,QAAQ,OAAO,MAAM;GAC7C,UAAU,MAAM;EAClB,EAAE;;;;;;;;;;;EAYF,MAAM,eAAe,eACnB,SAAS;GAAE,IAAI,MAAM;GAAI,oBAAoB,MAAM,MAAM;EAAoB,CAAC,CAChF;;;;;;;EAQA,MAAM,QAAQ,eAAe,KAAK,IAAI,KAAK,IAAI,MAAM,cAAc,GAAG,CAAC,GAAG,MAAM,MAAM,CAAC;EAEvF,MAAM,QAAQ,eAAe,MAAM,KAAK,EAAE,QAAQ,MAAM,OAAO,IAAI,GAAG,UAAU,QAAQ,CAAC,CAAC;;;;;;;EAQ1F,SAAS,OAAO,MAAsB;GACpC,MAAM,SAAS,KAAK,IAAI,KAAK,IAAI,MAAM,SAAS,OAAO,IAAI,CAAC,GAAG,CAAC;GAChE,OAAO,GAAG,OAAO,QAAQ,SAAS,IAAA,CAAK,QAAQ,CAAC,CAAC,CAAC,EAAE;EACtD;;GAQU,OAAA,MAAA,KAAA,CAAK,CAAC,YADd,UAAA,GAAA,mBA0BM,OA1BN,WA0BM,EAAA,KAAA,EAAA,GAAA;IAAA,GAxBS,MAAA,IAAA;IAAI,GAAK,aAAA;GAAY,GAAA;IAClC,MAAK;IACJ,cAAY,MAAA,IAAA,CAAI,CAAC,MAAK;KAAA,OAAG,MAAA;KAAK,QAAE,QAAA;IAAM,CAAA;IACtC,OAAO,MAAA,EAAA,CAAE,CAAC,MAAA,WAAA,CAAW,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAK,MAAA,KAAA,CAAK,CAAC,KAAK;GAE7C,CAAA,GAAA,EAAA,UAAA,IAAA,GAAA,mBAkBO,UAAA,MAAA,WAlBc,MAAA,QAAR,SAAI;IAAjB,OAAA,UAAA,GAAA,mBAkBO,QAAA;KAlBsB,KAAK;KAAO,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,MAAA,YAAA,CAAY,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAA,UAAA,CAAA;IAKpE,GAAA,CAAA,YAGE,MAAA,IAAA,GAAA;KAFA,eAAY;KACX,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,MAAA,YAAA,CAAY,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAA,wCAAA,CAAA;IAKhC,GAAA,MAAA,GAAA,CAAA,OAAA,CAAA,GAAA,mBAKO,QAAA;KALD,OAAM;KAA6C,OAAK,eAAA,EAAA,OAAW,OAAO,IAAI,EAAA,CAAA;IAClF,GAAA,CAAA,YAGE,MAAA,IAAA,GAAA;KAFA,eAAY;KACX,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,MAAA,YAAA,CAAY,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAA,gCAAA,CAAA;;GAMtC,CAAA,GAAA,GAAA,EAAA,GAAA,IAAA,UAAA,MAAA,UAAA,GAAA,YAiGa,MAAA,UAAA,GAjGb,WAiGa;;IA/FP,SAAA;IAAJ,KAAI;GAES,GAAA;IAAA,GAAA,MAAA,IAAA;IAAI,GAAK,MAAA,QAAA,CAAQ,CAAA,EAAA,YAAG,QAAA,WAAU,CAAA;IAAA,GAAQ,MAAA,KAAA,CAAK,CAAC;GAAK,GAAA;IAC7D,QAAQ,QAAA;IACR,MAAM,QAAA;IACN,WAAW,QAAA;IACX,WAAW,QAAA;IACX,UAAU,MAAA,KAAA,CAAK,CAAC;IAChB,UAAU,MAAA,KAAA,CAAK,CAAC;IAChB,OAAc,MAAA,EAAA,CAAE,CAAU,MAAA,WAAA,CAAW,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAA,4CAA2lB,MAAA,KAAA,CAAK,CAAC,KAAA;IAc7oB,uBAAkB,OAAA,OAAA,OAAA,MAAA,WAAEA,KAAAA,MAAK,qBAAsB,MAAM;;IAMpD,SAAA,SAAqB,EA5Bb,YAAK,EA2Bf,UAAA,IAAA,GAAA,mBAkEa,UAAA,MAAA,WAjEI,QAAR,SAAI;KADb,OAAA,UAAA,GAAA,YAkEa,MAAA,UAAA,GAAA;MAhEV,KAAK;MAEN,IAAG;MACI;MACN,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,MAAA,YAAA,CAAY,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAA,UAAA,CAAA;;MAE9B,SAAA,SAYE,EAjBQ,YAAK,CAKf,YAYE,MAAA,IAAA,GAAA;OAXA,eAAY;OACX,OAAK,eAAa,MAAA,EAAA,CAAE,CAAc,MAAA,YAAA,CAAY,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAA,yFAAA,CAAA;MAkBxD,GAAA,MAAA,GAAA,CAAA,OAAA,CAAA,IAAA,UAAA,IAAA,GAAA,mBAqCsB,UAAA,MAAA,WApCA,QAAb,cAAS;OADlB,OAAA,UAAA,GAAA,YAqCsB,MAAA,mBAAA,GAAA;QAnCnB,KAAK;QACL,MAAM;QACN,cAAY,MAAA,IAAA,CAAI,CAAC,MAAK;SAAA,OAAU;SAAS,QAAE,QAAA;QAAM,CAAA;QAClD,OAAM;QACN,OAAA;SAAA,SAAA;SAAA,WAAA;SAAA,WAAA;QAAA;;QAgBA,SAAA,cAcE,CAdF,YAcE,MAAA,IAAA,GAAA;SAbA,eAAY;SACX,OAAK,eAAe,MAAA,EAAA,CAAE,CAAgB,MAAA,YAAA,CAAY,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAA,yFAAA,qGAAA,8CAAA,0CAAA,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScrollArea.js","names":[],"sources":["../../../../packages/primitives/scroll-area/src/ScrollArea.vue"],"sourcesContent":["<script lang=\"ts\">\n/** Which scrollbars the area shows. */\nexport type ScrollAreaOrientation = \"vertical\" | \"horizontal\" | \"both\";\n</script>\n\n<script setup lang=\"ts\">\nimport {\n ScrollAreaCorner,\n ScrollAreaRoot,\n ScrollAreaScrollbar,\n ScrollAreaThumb,\n ScrollAreaViewport,\n} from \"reka-ui\";\nimport { cn } from \"@ecoma-io/loom-core\";\n\n/**\n * ScrollArea — custom-styled scrollbars for overflow content, replacing the\n * browser's native scrollbar with a thin track that expands on hover. Built on\n * Reka UI's ScrollArea primitives, which handle viewport sizing, pointer and\n * keyboard scrolling, and the scrollbar–thumb relationship.\n */\nwithDefaults(\n defineProps<{\n /** Which scrollbars to show. */\n orientation?: ScrollAreaOrientation;\n /** Optional class forwarded to each scrollbar element. */\n scrollbarClass?: string;\n }>(),\n { orientation: \"vertical\" },\n);\n</script>\n\n<template>\n <ScrollAreaRoot :class=\"cn('relative overflow-hidden', $attrs.class)\">\n <ScrollAreaViewport class=\"h-full w-full\">\n <slot />\n </ScrollAreaViewport>\n <ScrollAreaScrollbar\n v-if=\"orientation !== 'horizontal'\"\n orientation=\"vertical\"\n :class=\"\n cn(\n 'flex touch-none select-none w-2.5 border-l border-l-transparent p-px transition-colors',\n 'hover:w-3',\n scrollbarClass,\n )\n \"\n >\n <ScrollAreaThumb class=\"relative flex-1 rounded-full bg-border\" />\n </ScrollAreaScrollbar>\n <ScrollAreaScrollbar\n v-if=\"orientation !== 'vertical'\"\n orientation=\"horizontal\"\n :class=\"\n cn(\n 'flex touch-none select-none h-2.5 border-t border-t-transparent p-px transition-colors',\n 'hover:h-3',\n scrollbarClass,\n )\n \"\n >\n <ScrollAreaThumb class=\"relative flex-1 rounded-full bg-border\" />\n </ScrollAreaScrollbar>\n <ScrollAreaCorner v-if=\"orientation === 'both'\" />\n </ScrollAreaRoot>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScrollArea.vue_vue_type_script_setup_true_lang.js","names":["$attrs"],"sources":["../../../../packages/primitives/scroll-area/src/ScrollArea.vue"],"sourcesContent":["<script lang=\"ts\">\n/** Which scrollbars the area shows. */\nexport type ScrollAreaOrientation = \"vertical\" | \"horizontal\" | \"both\";\n</script>\n\n<script setup lang=\"ts\">\nimport {\n ScrollAreaCorner,\n ScrollAreaRoot,\n ScrollAreaScrollbar,\n ScrollAreaThumb,\n ScrollAreaViewport,\n} from \"reka-ui\";\nimport { cn } from \"@ecoma-io/loom-core\";\n\n/**\n * ScrollArea — custom-styled scrollbars for overflow content, replacing the\n * browser's native scrollbar with a thin track that expands on hover. Built on\n * Reka UI's ScrollArea primitives, which handle viewport sizing, pointer and\n * keyboard scrolling, and the scrollbar–thumb relationship.\n */\nwithDefaults(\n defineProps<{\n /** Which scrollbars to show. */\n orientation?: ScrollAreaOrientation;\n /** Optional class forwarded to each scrollbar element. */\n scrollbarClass?: string;\n }>(),\n { orientation: \"vertical\" },\n);\n</script>\n\n<template>\n <ScrollAreaRoot :class=\"cn('relative overflow-hidden', $attrs.class)\">\n <ScrollAreaViewport class=\"h-full w-full\">\n <slot />\n </ScrollAreaViewport>\n <ScrollAreaScrollbar\n v-if=\"orientation !== 'horizontal'\"\n orientation=\"vertical\"\n :class=\"\n cn(\n 'flex touch-none select-none w-2.5 border-l border-l-transparent p-px transition-colors',\n 'hover:w-3',\n scrollbarClass,\n )\n \"\n >\n <ScrollAreaThumb class=\"relative flex-1 rounded-full bg-border\" />\n </ScrollAreaScrollbar>\n <ScrollAreaScrollbar\n v-if=\"orientation !== 'vertical'\"\n orientation=\"horizontal\"\n :class=\"\n cn(\n 'flex touch-none select-none h-2.5 border-t border-t-transparent p-px transition-colors',\n 'hover:h-3',\n scrollbarClass,\n )\n \"\n >\n <ScrollAreaThumb class=\"relative flex-1 rounded-full bg-border\" />\n </ScrollAreaScrollbar>\n <ScrollAreaCorner v-if=\"orientation === 'both'\" />\n </ScrollAreaRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;GAiCE,OAAA,UAAA,GAAA,YA+BiB,MAAA,cAAA,GAAA,EA/BA,OAAK,eAAE,MAAA,EAAA,CAAE,CAAA,4BAA6BA,KAAAA,OAAO,KAAK,CAAA,EAAA,GAAA;IACjE,SAAA,cAEqB;KAFrB,YAEqB,MAAA,kBAAA,GAAA,EAFD,OAAM,gBAAe,GAAA;MACvC,SAAA,cAAQ,CAAR,WAAQ,KAAA,QAAA,SAAA,CAAA,CAAA;;;KAGF,QAAA,gBAAW,gBADnB,UAAA,GAAA,YAYsB,MAAA,mBAAA,GAAA;;MAVpB,aAAY;MACX,OAAK,eAAW,MAAA,EAAA,CAAE,CAAA,0FAAA,aAAuI,QAAA,cAAA,CAAA;;MAQ1J,SAAA,cAAkE,CAAlE,YAAkE,MAAA,eAAA,GAAA,EAAjD,OAAM,yCAAwC,CAAA,CAAA,CAAA;;;KAGzD,QAAA,gBAAW,cADnB,UAAA,GAAA,YAYsB,MAAA,mBAAA,GAAA;;MAVpB,aAAY;MACX,OAAK,eAAW,MAAA,EAAA,CAAE,CAAA,0FAAA,aAAuI,QAAA,cAAA,CAAA;;MAQ1J,SAAA,cAAkE,CAAlE,YAAkE,MAAA,eAAA,GAAA,EAAjD,OAAM,yCAAwC,CAAA,CAAA,CAAA;;;KAEzC,QAAA,gBAAW,UAAnC,UAAA,GAAA,YAAkD,MAAA,gBAAA,GAAA,EAAA,KAAA,EAAA,CAAA,KAAA,mBAAA,IAAA,IAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SegmentedControl.js","names":[],"sources":["../../../../packages/primitives/segmented-control/src/SegmentedControl.vue"],"sourcesContent":["<script lang=\"ts\">\n/** One segment in a {@link SegmentedControl}. */\nexport interface SegmentedControlOption {\n /** Matched against `modelValue`. */\n value: string;\n /** Visible label, rendered inside the segment. */\n label: string;\n /** Disables this segment alone, leaving its neighbours pickable. */\n disabled?: boolean;\n /** Forwarded to the segment's DOM node — lets hosts keep stable test hooks. */\n testId?: string;\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { nextTick, onMounted, onUnmounted, reactive, ref, useTemplateRef, watch } from \"vue\";\nimport { RadioGroupRoot, RadioGroupItem } from \"reka-ui\";\nimport { cn, optional } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useAncestorDisabled } from \"@ecoma-io/loom-labels\";\nimport { useFieldControl } from \"@ecoma-io/loom-labels\";\n\n/**\n * SegmentedControl — pick one of 2-5 mutually exclusive options, all visible\n * at once; one is always active. Built on Reka UI's RadioGroup (single\n * choice, roving tabindex — the group is one Tab stop, not N — with\n * arrow-key navigation). Styled with Loom tokens.\n *\n * Inside a [Field](../Field/Field.vue) the row's description, `name`,\n * `required` and `invalid` reach the group through `useFieldControl()`.\n *\n * **A row's label does not name this group.** `<label for>` names a labelable\n * element and this renders a `div[role=\"radiogroup\"]`, so the row's label\n * resolves to the element and names nobody. Name it with a\n * [Fieldset](../Fieldset/Fieldset.vue)'s real `<legend>`, or with an\n * `aria-label`/`aria-labelledby` of its own — which is what every example\n * already does, because the control carries no label text.\n *\n * There is no `readonly`, and a row's is ignored rather than approximated: each\n * segment is a `<button role=\"radio\">` with no native read-only state to put it\n * in, and one segment is always active, so a read-only control here would be a\n * disabled one showing an answer.\n */\nconst props = withDefaults(\n defineProps<{\n /** The selected option's `value`. */\n modelValue?: string;\n /** The segments to render, in order. */\n options: SegmentedControlOption[];\n /** Disables every segment at once, distinct from a single option's own `disabled`. Unset defers to a wrapping Field. */\n disabled?: boolean | undefined;\n /** `\"sm\"` is the compressed form for dense chrome — e.g. a status bar density level. */\n size?: \"default\" | \"sm\";\n }>(),\n {\n // Not `false`: absent has to stay tellable from `false` for a Field above\n // to disable the control and for `:disabled=\"false\"` to overrule one that\n // does. Vue casts an absent Boolean prop with no declared default to\n // `false`, which would leave the context wired and inert.\n disabled: undefined,\n size: \"default\",\n },\n);\n\ndefineEmits<{ \"update:modelValue\": [value: string] }>();\n\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: groupAttrs } = useSplitAttrs();\n\n/**\n * The segments are real `<button>`s, so a `<fieldset disabled>` above this\n * control already makes each of them inert — but Reka's roving focus is what\n * decides where Tab lands, and it is driven by the group's own `disabled`,\n * which the fieldset never reaches. Measured on this branch: every segment went\n * `:disabled` while the `role=\"radiogroup\"` container kept `tabindex=\"0\"`, so\n * Tab still stopped on an unavailable control and left the reader nowhere. Its\n * own `disabled` prop sets that to `-1`.\n *\n * Reading the group state here rather than painting it with a CSS variant is\n * also what keeps the appearance honest, and this is the control where the\n * distinction bites: one option may carry `disabled` of its own, so \"a\n * `:disabled` button is inside this track\" and \"this track is unavailable\" are\n * different facts. Only the fieldset says the second.\n *\n * Read off the DOM rather than taken from the Field context, which publishes no\n * `disabled` on purpose — see `../../lib/ancestor-disabled.ts`.\n */\nconst root = useTemplateRef<{ $el?: Element }>(\"root\");\nconst groupDisabled = useAncestorDisabled(() => root.value?.$el);\n\n// One rendered node, so the split is only about `class` — a caller's class\n// needs a Tailwind-aware merge rather than Vue's concatenating fallthrough.\n//\n// `id` and `aria-describedby` arrive as fallthrough attrs rather than props, so\n// they are handed in as this caller's own values; `field.attrs` then spreads\n// **after** `groupAttrs`, and that position is the contract (see TextField.vue).\n//\n// `name`, `required` and `invalid` are omitted because this component has no\n// prop for any of them — the row's answer stands unopposed, and its `name`\n// reaches RadioGroupRoot's own prop, which mints the hidden input a `<form>`\n// submits. `readonly` is omitted for the reason the docblock gives.\n//\n// The resolved `required` then reaches the group through Reka's own prop rather\n// than the `aria-required` in `field.attrs`: RadioGroupRoot writes that\n// attribute itself, so leaving it to the bag is a race with a `false` — see\n// Checkbox.vue, where the merge order makes the same race a certainty.\nconst field = useFieldControl(() => ({\n id: attrs.id as string | undefined,\n describedBy: attrs[\"aria-describedby\"] as string | undefined,\n // The fieldset beats the prop in both directions, exactly as it does for the\n // segments themselves.\n disabled: groupDisabled.value ? true : props.disabled,\n}));\n\n// Shared sliding indicator behind the checked segment. Measured off the live\n// DOM (offsetLeft/offsetWidth of the [data-state=checked] item), not derived\n// from index/width math, so \"sm\"/\"default\" sizing and variable-width labels\n// stay correct without per-size indicator code.\nconst indicatorEl = ref<HTMLElement | null>(null);\nconst indicatorStyle = reactive({ left: \"0px\", width: \"0px\", opacity: \"0\" });\n\nlet resizeObserver: ResizeObserver | undefined;\n\n// Re-targets onto the checked item itself (not the container) so a late\n// width settle on exactly that element — e.g. the `font-medium` weight swap\n// on data-[state=checked] reflowing after this function's own read — is\n// caught by the observer and re-measured, instead of going unnoticed because\n// the container's total box happened not to change.\nfunction updateIndicator() {\n const container = indicatorEl.value?.parentElement;\n const active = container?.querySelector<HTMLElement>('[role=\"radio\"][data-state=\"checked\"]');\n if (!active) {\n indicatorStyle.opacity = \"0\";\n resizeObserver?.disconnect();\n return;\n }\n indicatorStyle.left = `${active.offsetLeft}px`;\n indicatorStyle.width = `${active.offsetWidth}px`;\n indicatorStyle.opacity = \"1\";\n resizeObserver?.disconnect();\n resizeObserver?.observe(active);\n}\n\nonMounted(() => {\n resizeObserver = new ResizeObserver(updateIndicator);\n updateIndicator();\n});\nonUnmounted(() => resizeObserver?.disconnect());\n\n// One watcher for both re-measure triggers, and deliberately **not** deep on\n// `options`. The two separate watches each scheduled the same\n// `nextTick(updateIndicator)`, so one host tick that changed both inputs paid\n// the measurement twice; merging them costs nothing and removes the double.\n// Dropping `deep:` is not a regression: an in-place mutation of an option\n// object only matters to the indicator if it changes a *width* — a label edit,\n// say — and the ResizeObserver above is already watching exactly the checked\n// element for that, so the deep traversal bought nothing the observer does not\n// deliver sooner. A new array identity or a new selection still fires this.\nwatch([() => props.modelValue, () => props.options], () => nextTick(updateIndicator));\n</script>\n\n<template>\n <RadioGroupRoot\n ref=\"root\"\n v-bind=\"{ ...optional({ modelValue }), ...groupAttrs, ...field.attrs }\"\n :disabled=\"field.disabled\"\n :required=\"field.required\"\n orientation=\"horizontal\"\n :class=\"\n cn(\n 'relative inline-flex items-center gap-0.5 rounded-md border border-input bg-muted p-0.5',\n // The one thing the *track* has left to say. Its fill is already\n // `bg-muted` — the well every other unavailable control drains to — so\n // the fill channel is spent before the state arrives, and the segments\n // inside carry the rest. The rim slackening from `input` to the lighter\n // `border` is what makes a wholly unavailable control tellable from an\n // available one at the group's own edge rather than only cell by cell.\n field.disabled && 'cursor-not-allowed border-border',\n attrs.class as string,\n )\n \"\n @update:model-value=\"$emit('update:modelValue', String($event))\"\n >\n <span\n ref=\"indicatorEl\"\n aria-hidden=\"true\"\n class=\"pointer-events-none absolute inset-y-0.5 rounded-sm bg-card shadow-sm\"\n style=\"\n transition:\n left var(--duration-fast) var(--ease-spring),\n width var(--duration-fast) var(--ease-spring);\n \"\n :style=\"{\n left: indicatorStyle.left,\n width: indicatorStyle.width,\n opacity: indicatorStyle.opacity,\n }\"\n />\n <RadioGroupItem\n v-for=\"opt in options\"\n :key=\"opt.value\"\n :value=\"opt.value\"\n :disabled=\"opt.disabled ?? false\"\n v-bind=\"optional({ 'data-testid': opt.testId })\"\n style=\"\n transition:\n transform var(--duration-fast) var(--ease-spring),\n color var(--duration-fast) var(--ease-out);\n \"\n :class=\"\n cn(\n // Named, not the bare `group`: a segmented control is dropped into\n // application chrome, and a bare `group-*` would also answer to any\n // ancestor a host happened to mark up as one.\n // `min-h-6`/`min-w-6` are the WCAG 2.5.8 target floors made\n // explicit: the sm segment's box was previously a product of whatever\n // line-height the surrounding text happened to hand down — swapping\n // to the named `text-micro` (line-height 1.4) dropped it to 23px,\n // and a micro label (a one-character density toggle) collapses the\n // width under 24 too. Floors that hold regardless of type metrics\n // are the honest encoding.\n 'group/segment relative z-10 inline-flex min-h-6 min-w-6 items-center justify-center rounded-sm text-muted-foreground',\n size === 'sm' ? 'px-1.5 py-px text-micro' : 'px-3 py-1 text-sm',\n 'data-[state=checked]:font-medium data-[state=checked]:text-foreground',\n 'data-[state=unchecked]:hover:bg-subtle',\n 'active:scale-press',\n 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo',\n // No `opacity` here. A segment *is* its label — there is no glyph and\n // no fill of its own to dim — so `disabled:opacity-50` had nothing to\n // act on but the text, taking an unchecked segment to 1.96:1 and a\n // chosen one to 3.13:1 on its thumb.\n //\n // Unavailability is a fill instead, and it is the one step this\n // system already treats as meaningful: the cell drains up a rung of\n // the elevation rhythm, from the track's `bg-muted` to the page's own\n // `bg-background`, so it reads as a hole in the track rather than as\n // part of it. On a *chosen* segment that same fill covers the raised\n // white thumb, which is the whole signal — the value is still shown\n // and it is visibly no longer settable.\n 'disabled:pointer-events-none data-[disabled]:bg-background',\n )\n \"\n >\n <!-- The mute lives on this span rather than on the segment, and it has\n to. `data-[disabled]:` sorts *before* `data-[state=checked]:` in\n Tailwind's variant order, so a disabled segment that is also the\n chosen one would take `text-foreground` from the button — and a\n colour set on the button is only inherited here. Declared on the span\n it wins whatever the segment resolved to: 5.25:1 over the drained\n fill, against the 1.96:1 the dim used to leave. -->\n <span class=\"group-data-[disabled]/segment:text-muted-foreground\">{{ opt.label }}</span>\n </RadioGroupItem>\n </RadioGroupRoot>\n</template>\n"],"mappings":""}
|