@rotki/ui-library 3.1.3 → 3.1.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"RuiCategoryPicker.js","names":["_sfc_main"],"sources":["../../../../src/components/forms/category-picker/RuiCategoryPicker.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue, TItem\">\nimport type { KeyOfType } from '@/composables/dropdown-menu';\nimport type { VueClassValue } from '@/types/class-value';\nimport { breakpointsTailwind, useBreakpoints } from '@vueuse/core';\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport { categoryPickerActivatorStyles, categoryPickerStyles } from '@/components/forms/category-picker/category-picker-styles';\nimport RuiFieldLabel from '@/components/forms/field-label/RuiFieldLabel.vue';\nimport RuiTextField from '@/components/forms/text-field/RuiTextField.vue';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiDialog, { type DialogProps } from '@/components/overlays/dialog/RuiDialog.vue';\nimport { activatorHandlers } from '@/components/overlays/menu/activator-handlers';\nimport RuiMenu from '@/components/overlays/menu/RuiMenu.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { type LabelPlacement, useLabelPlacement } from '@/composables/defaults/field';\nimport { Placement } from '@/composables/floating';\nimport { useCategoryPicker } from '@/composables/forms/category-picker/use-category-picker';\nimport { type FormDetails, useFormDetails, useFormTextDetail } from '@/utils/form-text-detail';\nimport { cn } from '@/utils/tv';\n\nexport interface RuiCategoryPickerClassNames {\n root?: VueClassValue;\n rail?: VueClassValue;\n detail?: VueClassValue;\n search?: VueClassValue;\n}\n\nexport type CategoryPickerBreakpoint = 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\nexport interface RuiCategoryPickerProps<TValue, TItem> {\n /** The full catalogue of selectable items. */\n items: TItem[];\n /** Resolve an item's category label. Takes precedence over `categoryAttr`. */\n categoryOf?: (item: TItem) => string;\n /** Resolve an item's category from one of its own properties. */\n categoryAttr?: keyof TItem;\n /** Property whose value becomes the model value. Omit to use the item itself. */\n keyAttr?: KeyOfType<TItem, TValue extends (infer U)[] ? U : TValue>;\n /** Property that produces an item's display text. */\n textAttr?: keyof TItem;\n /** Field label, and the title of the mobile sheet. */\n label?: string;\n /** Placeholder shown in the field while it is empty or being searched. */\n placeholder?: string;\n /** Allow filtering: type in the field (desktop) or the sheet's search box (mobile). */\n searchable?: boolean;\n /**\n * Custom predicate. Receives the resolved category label as the third\n * argument, matching the `RuiAutoComplete` filter signature.\n */\n filter?: (item: TItem, query: string, category: string) => boolean;\n /** Show the synthetic \"All\" pseudo-category as the first rail entry. */\n showAll?: boolean;\n /** Label for the \"All\" pseudo-category. */\n allLabel?: string;\n /** Text shown when no item matches the current search. */\n emptyText?: string;\n disabled?: boolean;\n readOnly?: boolean;\n dense?: boolean;\n clearable?: boolean;\n loading?: boolean;\n /** Where the label shows; falls back to the nearest `RuiFieldDefaults`, then the app default, then `top`. */\n labelPlacement?: LabelPlacement;\n /** Marks the trigger field as required (renders the asterisk). */\n required?: boolean;\n /** Validation errors rendered under the trigger field. */\n errorMessages?: string | string[];\n /** Success messages rendered under the trigger field. */\n successMessages?: string | string[];\n /** Hint rendered under the trigger field. */\n hint?: string;\n /** The line under the trigger field for its hint and messages: `reserve`, `auto` or `hidden`; falls back to the nearest `RuiFieldDefaults`, then the app default, then `reserve`. */\n details?: FormDetails;\n /** @deprecated Use `details=\"hidden\"`. Removed in 4.0. */\n hideDetails?: boolean;\n /** Below this breakpoint the picker collapses to a single-pane drill-in. */\n mobileBreakpoint?: CategoryPickerBreakpoint;\n /** Forwarded to the mobile bottom-sheet (`RuiDialog`). */\n dialogOptions?: DialogProps;\n classNames?: RuiCategoryPickerClassNames;\n}\n\ndefineOptions({\n name: 'RuiCategoryPicker',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<TValue>();\nconst searchInput = defineModel<string>('search', { default: '' });\n\nconst {\n items,\n categoryOf = undefined,\n categoryAttr = undefined,\n keyAttr = undefined,\n textAttr = undefined,\n label = 'Select',\n placeholder = 'Search',\n searchable = true,\n filter = undefined,\n showAll = true,\n allLabel = 'All',\n emptyText = 'No results',\n disabled = false,\n readOnly = false,\n dense = false,\n clearable = false,\n loading = false,\n labelPlacement = undefined,\n required = false,\n errorMessages = [],\n successMessages = [],\n hint = '',\n details = undefined,\n hideDetails = false,\n mobileBreakpoint = 'md',\n dialogOptions = undefined,\n classNames = undefined,\n} = defineProps<RuiCategoryPickerProps<TValue, TItem>>();\n\nconst emit = defineEmits<{\n select: [item: TItem];\n}>();\n\ndefineSlots<{\n 'activator'?: (props: { attrs: { onClick?: () => void; onMouseover?: () => void; onMouseleave?: () => void }; isOpen: boolean; open: () => void; item: TItem | undefined; text: string | undefined }) => any;\n 'selection'?: (props: { item: TItem }) => any;\n 'selection.prepend'?: (props: { item: TItem }) => any;\n 'category'?: (props: { category: string | null; label: string; active: boolean; count: number }) => any;\n 'item'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'item.prepend'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'item.append'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'empty'?: () => any;\n 'header'?: () => any;\n 'footer'?: (props: { close: () => void }) => any;\n}>();\n\nconst isOpen = ref<boolean>(false);\nconst focusedPane = ref<'rail' | 'detail'>('rail');\nconst highlightedItemIndex = ref<number>(0);\nconst drillStep = ref<'category' | 'detail'>('category');\n\nconst railRef = useTemplateRef<HTMLDivElement>('railRef');\nconst detailRef = useTemplateRef<HTMLDivElement>('detailRef');\nconst activatorRef = useTemplateRef<HTMLInputElement>('activatorRef');\nconst fieldRef = useTemplateRef<HTMLElement>('fieldRef');\nconst isHovered = ref<boolean>(false);\n\nconst { focused } = useFocus(activatorRef);\nconst { hasError, hasSuccess } = useFormTextDetail(() => errorMessages, () => successMessages);\nconst detailsMode = useFormDetails(() => details, () => hideDetails);\n\nconst baseId = useId();\nconst breakpoints = useBreakpoints(breakpointsTailwind);\n\nfunction resolveCategory(item: TItem): string {\n if (categoryOf)\n return categoryOf(item);\n if (categoryAttr !== undefined)\n return String(item[categoryAttr] ?? '');\n return '';\n}\n\nconst {\n activeCategory,\n detailItems,\n detailRows,\n getIdentifier,\n getText,\n isEmpty,\n isSelected,\n railEntries,\n setActiveCategory,\n syncActiveToSelection,\n} = useCategoryPicker<TValue, TItem>({\n allLabel: () => allLabel,\n categoryOf: resolveCategory,\n filter: () => filter,\n items: () => items,\n keyAttr,\n modelValue,\n search: searchInput,\n searchable: () => searchable,\n showAll: () => showAll,\n textAttr,\n});\n\nconst isMobile = computed<boolean>(() => get(breakpoints.smaller(mobileBreakpoint)));\n\nconst mode = computed<'twoPane' | 'drill'>(() => (get(isMobile) ? 'drill' : 'twoPane'));\n\n/**\n * Desktop anchors an outside-click popover under the field (RuiMenu); narrow\n * width shows a full-width bottom sheet (RuiDialog).\n */\nconst shell = computed<typeof RuiMenu | typeof RuiDialog>(() => (get(isMobile) ? RuiDialog : RuiMenu));\n\n/**\n * The bottom sheet's props. Each shell is given only its own props, so the\n * other component's never leak through as attributes.\n */\nfunction dialogShellProps(): Record<string, unknown> {\n return {\n ariaLabel: dialogOptions?.ariaLabel ?? label,\n bottomSheet: dialogOptions?.bottomSheet ?? true,\n maxWidth: dialogOptions?.maxWidth,\n persistent: dialogOptions?.persistent,\n width: dialogOptions?.width ?? '100%',\n };\n}\n\n/**\n * The popover's props. It anchors to the field box rather than the wrapper, so\n * it sits directly under the input instead of below the reserved details row,\n * and drops the menu's default vertical padding so the popover box equals the\n * panel: the size middleware caps the panel to the available height, and any\n * extra chrome padding would push the popover past the viewport edge. The\n * popover is a dialog, as the activator's `aria-haspopup` says: it holds a\n * tablist and a listbox, which a `menu` may not contain.\n */\nfunction menuShellProps(): Record<string, unknown> {\n return {\n anchorEl: get(fieldRef) ?? undefined,\n classNames: { content: 'py-0' },\n closeOnContentClick: false,\n disableAutoFocus: true,\n fullWidth: true,\n menuLabel: label,\n options: { placement: Placement.bottomStart, size: true },\n persistent: dialogOptions?.persistent,\n persistOnActivatorClick: true,\n role: 'dialog',\n };\n}\n\nconst shellProps = computed<Record<string, unknown>>(() =>\n get(isMobile) ? dialogShellProps() : menuShellProps());\n\nconst ui = computed<ReturnType<typeof categoryPickerStyles>>(() => categoryPickerStyles({\n dense,\n mode: get(mode),\n}));\n\nconst selectedItem = computed<TItem | undefined>(() => {\n const value = get(modelValue);\n if (value === undefined)\n return undefined;\n return items.find(item => getIdentifier(item) === value);\n});\n\nconst selectedText = computed<string | undefined>(() => {\n const selected = get(selectedItem);\n return selected ? getText(selected) : undefined;\n});\n\nconst placement = useLabelPlacement(() => labelPlacement);\n\n/**\n * Desktop lets the user type in the field itself (no separate search box);\n * narrow width keeps a read-only trigger that opens the sheet's own search.\n */\nconst canType = computed<boolean>(() => searchable && !get(isMobile) && !readOnly && !disabled);\n\nconst isTyping = computed<boolean>(() => get(isOpen) && get(canType));\n\n/**\n * Drives both the clear button itself and the room the selection overlay has\n * to leave for it, so the two can never disagree.\n */\nconst showClear = computed<boolean>(() =>\n clearable && get(selectedItem) !== undefined && !disabled && !readOnly);\n\nconst activatorUi = computed<ReturnType<typeof categoryPickerActivatorStyles>>(() => categoryPickerActivatorStyles({\n dense,\n disabled,\n hasError: get(hasError),\n hasSuccess: get(hasSuccess) && !get(hasError),\n hovered: get(isHovered),\n opened: get(isOpen),\n readonly: readOnly,\n withClear: get(showClear),\n}));\n\nconst activeRailIndex = computed<number>(() =>\n get(railEntries).findIndex(entry => entry.category === get(activeCategory)));\n\nconst showRail = computed<boolean>(() => get(mode) === 'twoPane' || get(drillStep) === 'category');\nconst showDetail = computed<boolean>(() => get(mode) === 'twoPane' || get(drillStep) === 'detail');\n\nfunction clamp(value: number, max: number): number {\n if (value < 0)\n return 0;\n if (value > max)\n return max;\n return value;\n}\n\nfunction open(): void {\n if (disabled)\n return;\n set(isOpen, true);\n}\n\nfunction close(): void {\n set(isOpen, false);\n}\n\nfunction onInput(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement))\n return;\n set(searchInput, target.value);\n if (!get(isOpen))\n open();\n}\n\nfunction selectItem(item: TItem): void {\n if (readOnly)\n return;\n set(modelValue, getIdentifier(item));\n emit('select', item);\n close();\n}\n\nfunction clearSelection(): void {\n set(modelValue, undefined);\n}\n\nfunction onCategoryClick(category: string | null): void {\n setActiveCategory(category);\n if (get(mode) === 'drill')\n set(drillStep, 'detail');\n else\n focusPane('detail');\n}\n\n/**\n * Moves focus to one of the panes. The focus call prevents scrolling because\n * the panes sit inside a teleported popover, and without it the browser\n * scrolls the page behind to reveal the pane.\n */\nfunction focusPane(pane: 'rail' | 'detail'): void {\n set(focusedPane, pane);\n nextTick(() => {\n (pane === 'rail' ? get(railRef) : get(detailRef))?.focus({ preventScroll: true });\n });\n}\n\nfunction moveRail(direction: 1 | -1): void {\n const entries = get(railEntries);\n if (entries.length === 0)\n return;\n const next = clamp(get(activeRailIndex) + direction, entries.length - 1);\n setActiveCategory(entries[next]?.category ?? null);\n}\n\nfunction moveItem(direction: 1 | -1): void {\n const total = get(detailItems).length;\n if (total === 0)\n return;\n set(highlightedItemIndex, clamp(get(highlightedItemIndex) + direction, total - 1));\n}\n\nfunction enterDetail(): void {\n if (get(detailItems).length === 0)\n return;\n resetHighlightToSelection();\n if (get(mode) === 'drill')\n set(drillStep, 'detail');\n focusPane('detail');\n}\n\nfunction selectHighlighted(): void {\n const item = get(detailItems)[get(highlightedItemIndex)];\n if (item !== undefined)\n selectItem(item);\n}\n\n/**\n * Enter from the field commits the best match for the current query, so a\n * type-to-pick flow (type a few letters, press Enter) works without leaving\n * the input. Does nothing when the field is empty.\n */\nfunction selectTopResult(): void {\n if (!get(searchInput).trim())\n return;\n const item = get(detailItems)[0];\n if (item !== undefined)\n selectItem(item);\n}\n\nfunction resetHighlightToSelection(): void {\n const index = get(detailItems).findIndex(item => isSelected(item));\n set(highlightedItemIndex, index >= 0 ? index : 0);\n}\n\nfunction backToRail(): void {\n if (get(mode) === 'drill')\n set(drillStep, 'category');\n else\n focusPane('rail');\n}\n\nfunction scrollActiveDescendantIntoView(container: HTMLElement | null, id: string): void {\n if (!container)\n return;\n nextTick(() => {\n container.querySelector(`#${id}`)?.scrollIntoView({ block: 'nearest' });\n });\n}\n\n// Reset the item highlight whenever the active category changes.\nwatch(activeCategory, () => {\n resetHighlightToSelection();\n});\n\nwatch(highlightedItemIndex, (index) => {\n scrollActiveDescendantIntoView(get(detailRef), `${baseId}-opt-${index}`);\n});\n\nwatch(activeRailIndex, (index) => {\n if (index >= 0)\n scrollActiveDescendantIntoView(get(railRef), `${baseId}-cat-${index}`);\n});\n\n/**\n * Opening takes focus where the user types next: desktop is a combobox, so\n * focus stays in the field and ArrowDown moves into the panes, while the\n * mobile sheet has no typing field and focuses the rail instead. Closing\n * resets the query, so the field shows the selection again next time, and\n * returns focus to the trigger, which the APG dialog contract asks for.\n */\nfunction onOpenChanged(value: boolean): void {\n if (value) {\n syncActiveToSelection();\n set(focusedPane, 'rail');\n set(drillStep, 'category');\n resetHighlightToSelection();\n nextTick(() => (get(canType) ? get(activatorRef) : get(railRef))?.focus({ preventScroll: true }));\n }\n else {\n set(searchInput, '');\n nextTick(() => get(activatorRef)?.focus({ preventScroll: true }));\n }\n}\n\nwatch(isOpen, onOpenChanged);\n</script>\n\n<template>\n <component\n :is=\"shell\"\n v-model=\"isOpen\"\n v-bind=\"{ ...$attrs, ...shellProps }\"\n >\n <template #activator=\"{ attrs }\">\n <slot\n name=\"activator\"\n v-bind=\"{ attrs, isOpen, item: selectedItem, open, text: selectedText }\"\n >\n <div :class=\"activatorUi.wrapper()\">\n <RuiFieldLabel\n v-if=\"label\"\n :id=\"`${baseId}-label`\"\n :text=\"label\"\n :hidden=\"placement === 'hidden'\"\n :required=\"required\"\n :disabled=\"disabled\"\n />\n <div\n ref=\"fieldRef\"\n role=\"combobox\"\n data-id=\"activator\"\n :class=\"activatorUi.activator()\"\n :aria-labelledby=\"label ? `${baseId}-label` : undefined\"\n aria-haspopup=\"dialog\"\n :aria-controls=\"isOpen ? `${baseId}-panel` : undefined\"\n :aria-expanded=\"isOpen\"\n :aria-disabled=\"disabled || undefined\"\n :aria-readonly=\"(readOnly || !canType) || undefined\"\n :aria-required=\"required || undefined\"\n :aria-invalid=\"hasError || undefined\"\n :aria-busy=\"loading || undefined\"\n v-bind=\"readOnly ? {} : activatorHandlers(attrs)\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n >\n <span\n v-if=\"selectedItem && !isTyping && $slots.selection\"\n data-id=\"selection\"\n :class=\"activatorUi.selection()\"\n >\n <slot\n name=\"selection.prepend\"\n v-bind=\"{ item: selectedItem }\"\n />\n <slot\n name=\"selection\"\n v-bind=\"{ item: selectedItem }\"\n />\n </span>\n <input\n ref=\"activatorRef\"\n data-id=\"search-input\"\n :value=\"isTyping ? searchInput : (($slots.selection && selectedItem) ? '' : (selectedText ?? ''))\"\n :placeholder=\"isTyping ? placeholder : ''\"\n :readonly=\"!canType\"\n :disabled=\"disabled\"\n :aria-invalid=\"hasError || undefined\"\n :aria-labelledby=\"label ? `${baseId}-label` : undefined\"\n class=\"bg-transparent outline-hidden\"\n :class=\"[activatorUi.value(), !canType && 'cursor-pointer']\"\n @input=\"onInput($event)\"\n @keydown.down.prevent=\"focusPane('rail')\"\n @keydown.enter.prevent=\"selectTopResult()\"\n @keydown.esc=\"close()\"\n />\n <span\n v-if=\"showClear\"\n data-id=\"clear\"\n :class=\"[activatorUi.clear(), focused && 'visible!', { 'mr-2': !dense }]\"\n @click.stop.prevent=\"clearSelection()\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"16\"\n />\n </span>\n <span\n data-id=\"chevron\"\n :class=\"activatorUi.iconWrapper()\"\n >\n <RuiIcon\n :class=\"activatorUi.icon()\"\n :size=\"dense ? 16 : 20\"\n name=\"lu-chevron-down\"\n />\n </span>\n <RuiProgress\n v-if=\"loading\"\n :class=\"activatorUi.progress()\"\n color=\"primary\"\n thickness=\"3\"\n variant=\"indeterminate\"\n />\n <fieldset :class=\"activatorUi.fieldset()\" />\n </div>\n <RuiFormTextDetail\n v-if=\"detailsMode !== 'hidden'\"\n :details=\"detailsMode\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n />\n </div>\n </slot>\n </template>\n\n <div\n :id=\"`${baseId}-panel`\"\n :class=\"ui.root({ class: cn(classNames?.root) })\"\n data-id=\"panel\"\n >\n <!-- On desktop the field itself is the search input, so the panel only\n needs its own search box in the mobile sheet. -->\n <div\n v-if=\"isMobile || $slots.header\"\n :class=\"ui.header()\"\n >\n <slot name=\"header\">\n <div :class=\"ui.title()\">\n {{ label }}\n </div>\n </slot>\n <RuiTextField\n v-if=\"searchable && isMobile\"\n v-model=\"searchInput\"\n :class=\"cn(classNames?.search)\"\n :placeholder=\"placeholder\"\n :dense=\"dense\"\n prepend-icon=\"lu-search\"\n details=\"hidden\"\n clearable\n data-id=\"search\"\n />\n </div>\n\n <div :class=\"ui.body()\">\n <!-- Category rail (master) -->\n <div\n v-show=\"showRail\"\n ref=\"railRef\"\n :class=\"ui.rail({ class: cn(classNames?.rail) })\"\n role=\"tablist\"\n aria-orientation=\"vertical\"\n :aria-label=\"label\"\n :aria-activedescendant=\"focusedPane === 'rail' && activeRailIndex >= 0 ? `${baseId}-cat-${activeRailIndex}` : undefined\"\n tabindex=\"0\"\n data-id=\"rail\"\n @focus=\"focusedPane = 'rail'\"\n @keydown.down.prevent=\"moveRail(1)\"\n @keydown.up.prevent=\"moveRail(-1)\"\n @keydown.right.prevent=\"enterDetail()\"\n @keydown.enter.prevent=\"enterDetail()\"\n @keydown.home.prevent=\"setActiveCategory(railEntries[0]?.category ?? null)\"\n @keydown.end.prevent=\"setActiveCategory(railEntries[railEntries.length - 1]?.category ?? null)\"\n >\n <RuiButton\n v-for=\"(entry, index) in railEntries\"\n :id=\"`${baseId}-cat-${index}`\"\n :key=\"entry.category ?? '__all__'\"\n role=\"tab\"\n variant=\"list\"\n :size=\"dense ? 'sm' : undefined\"\n :active=\"entry.category === activeCategory\"\n :aria-selected=\"entry.category === activeCategory\"\n tabindex=\"-1\"\n :data-highlighted=\"focusedPane === 'rail' && index === activeRailIndex\"\n :data-category=\"entry.category ?? '__all__'\"\n @click=\"onCategoryClick(entry.category)\"\n >\n <slot\n name=\"category\"\n v-bind=\"{ active: entry.category === activeCategory, category: entry.category, count: entry.count, label: entry.label }\"\n >\n <span class=\"truncate\">{{ entry.label }}</span>\n <span :class=\"ui.railCount()\">{{ entry.count }}</span>\n </slot>\n </RuiButton>\n </div>\n\n <!-- Detail pane -->\n <div\n v-show=\"showDetail\"\n ref=\"detailRef\"\n :class=\"ui.detail({ class: cn(classNames?.detail) })\"\n role=\"listbox\"\n :aria-label=\"activeCategory ?? allLabel\"\n :aria-activedescendant=\"focusedPane === 'detail' && !isEmpty ? `${baseId}-opt-${highlightedItemIndex}` : undefined\"\n tabindex=\"0\"\n data-id=\"detail\"\n @focus=\"focusedPane = 'detail'\"\n @keydown.down.prevent=\"moveItem(1)\"\n @keydown.up.prevent=\"moveItem(-1)\"\n @keydown.left.prevent=\"backToRail()\"\n @keydown.home.prevent=\"highlightedItemIndex = 0\"\n @keydown.end.prevent=\"highlightedItemIndex = detailItems.length - 1\"\n @keydown.enter.prevent=\"selectHighlighted()\"\n @keydown.space.prevent=\"selectHighlighted()\"\n >\n <template v-if=\"mode === 'drill' && showDetail\">\n <button\n type=\"button\"\n :class=\"ui.backButton()\"\n data-id=\"back\"\n @click=\"backToRail()\"\n >\n <RuiIcon\n name=\"lu-chevron-left\"\n size=\"18\"\n />\n <span>{{ activeCategory ?? allLabel }}</span>\n </button>\n </template>\n\n <slot\n v-if=\"isEmpty\"\n name=\"empty\"\n >\n <div :class=\"ui.empty()\">\n {{ emptyText }}\n </div>\n </slot>\n\n <template\n v-for=\"row in detailRows\"\n v-else\n :key=\"row.type === 'header' ? `h-${row.category}` : `i-${row.index}`\"\n >\n <div\n v-if=\"row.type === 'header'\"\n :class=\"ui.subheader()\"\n role=\"presentation\"\n >\n {{ row.category }}\n </div>\n <RuiButton\n v-else\n :id=\"`${baseId}-opt-${row.index}`\"\n role=\"option\"\n variant=\"list\"\n :size=\"dense ? 'sm' : undefined\"\n :active=\"isSelected(row.item)\"\n :aria-selected=\"isSelected(row.item)\"\n tabindex=\"-1\"\n :data-highlighted=\"focusedPane === 'detail' && row.index === highlightedItemIndex\"\n :class=\"{ [ui.highlighted()]: focusedPane === 'detail' && row.index === highlightedItemIndex && !isSelected(row.item) }\"\n @click=\"selectItem(row.item)\"\n >\n <template\n v-if=\"$slots['item.prepend']\"\n #prepend\n >\n <slot\n name=\"item.prepend\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n >\n {{ getText(row.item) }}\n </slot>\n <template\n v-if=\"$slots['item.append']\"\n #append\n >\n <slot\n name=\"item.append\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n />\n </template>\n </RuiButton>\n </template>\n </div>\n </div>\n\n <div\n v-if=\"$slots.footer\"\n :class=\"ui.footer()\"\n >\n <slot\n name=\"footer\"\n v-bind=\"{ close }\"\n />\n </div>\n </div>\n </component>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"RuiCategoryPicker.js","names":["_sfc_main"],"sources":["../../../../src/components/forms/category-picker/RuiCategoryPicker.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue, TItem\">\nimport type { KeyOfType } from '@/composables/dropdown-menu';\nimport type { VueClassValue } from '@/types/class-value';\nimport { breakpointsTailwind, useBreakpoints } from '@vueuse/core';\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport { categoryPickerActivatorStyles, categoryPickerStyles } from '@/components/forms/category-picker/category-picker-styles';\nimport RuiFieldLabel from '@/components/forms/field-label/RuiFieldLabel.vue';\nimport RuiTextField from '@/components/forms/text-field/RuiTextField.vue';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiDialog, { type DialogProps } from '@/components/overlays/dialog/RuiDialog.vue';\nimport { activatorHandlers } from '@/components/overlays/menu/activator-handlers';\nimport RuiMenu from '@/components/overlays/menu/RuiMenu.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { type LabelPlacement, useLabelPlacement } from '@/composables/defaults/field';\nimport { Placement } from '@/composables/floating';\nimport { useCategoryPicker } from '@/composables/forms/category-picker/use-category-picker';\nimport { type FormDetails, useFormDetails, useFormTextDetail } from '@/utils/form-text-detail';\nimport { cn } from '@/utils/tv';\n\nexport interface RuiCategoryPickerClassNames {\n root?: VueClassValue;\n rail?: VueClassValue;\n detail?: VueClassValue;\n search?: VueClassValue;\n}\n\nexport type CategoryPickerBreakpoint = 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\nexport interface RuiCategoryPickerProps<TValue, TItem> {\n /** The full catalogue of selectable items. */\n items: TItem[];\n /** Resolve an item's category label. Takes precedence over `categoryAttr`. */\n categoryOf?: (item: TItem) => string;\n /** Resolve an item's category from one of its own properties. */\n categoryAttr?: keyof TItem;\n /** Property whose value becomes the model value. Omit to use the item itself. */\n keyAttr?: KeyOfType<TItem, TValue extends (infer U)[] ? U : TValue>;\n /** Property that produces an item's display text. */\n textAttr?: keyof TItem;\n /** Field label, and the title of the mobile sheet. */\n label?: string;\n /** Placeholder shown in the field while it is empty or being searched. */\n placeholder?: string;\n /** Allow filtering: type in the field (desktop) or the sheet's search box (mobile). */\n searchable?: boolean;\n /**\n * Custom predicate. Receives the resolved category label as the third\n * argument, matching the `RuiAutoComplete` filter signature.\n */\n filter?: (item: TItem, query: string, category: string) => boolean;\n /** Show the synthetic \"All\" pseudo-category as the first rail entry. */\n showAll?: boolean;\n /** Label for the \"All\" pseudo-category. */\n allLabel?: string;\n /** Text shown when no item matches the current search. */\n emptyText?: string;\n disabled?: boolean;\n readOnly?: boolean;\n dense?: boolean;\n clearable?: boolean;\n loading?: boolean;\n /** Where the label shows; falls back to the nearest `RuiFieldDefaults`, then the app default, then `top`. */\n labelPlacement?: LabelPlacement;\n /** Marks the trigger field as required (renders the asterisk). */\n required?: boolean;\n /** Validation errors rendered under the trigger field. */\n errorMessages?: string | string[];\n /** Success messages rendered under the trigger field. */\n successMessages?: string | string[];\n /** Hint rendered under the trigger field. */\n hint?: string;\n /** The line under the trigger field for its hint and messages: `reserve`, `auto` or `hidden`; falls back to the nearest `RuiFieldDefaults`, then the app default, then `reserve`. */\n details?: FormDetails;\n /** @deprecated Use `details=\"hidden\"`. Removed in 4.0. */\n hideDetails?: boolean;\n /** Below this breakpoint the picker collapses to a single-pane drill-in. */\n mobileBreakpoint?: CategoryPickerBreakpoint;\n /** Forwarded to the mobile bottom-sheet (`RuiDialog`). */\n dialogOptions?: DialogProps;\n classNames?: RuiCategoryPickerClassNames;\n}\n\ndefineOptions({\n name: 'RuiCategoryPicker',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<TValue>();\nconst searchInput = defineModel<string>('search', { default: '' });\n\nconst {\n items,\n categoryOf = undefined,\n categoryAttr = undefined,\n keyAttr = undefined,\n textAttr = undefined,\n label = 'Select',\n placeholder = 'Search',\n searchable = true,\n filter = undefined,\n showAll = true,\n allLabel = 'All',\n emptyText = 'No results',\n disabled = false,\n readOnly = false,\n dense = false,\n clearable = false,\n loading = false,\n labelPlacement = undefined,\n required = false,\n errorMessages = [],\n successMessages = [],\n hint = '',\n details = undefined,\n hideDetails = false,\n mobileBreakpoint = 'md',\n dialogOptions = undefined,\n classNames = undefined,\n} = defineProps<RuiCategoryPickerProps<TValue, TItem>>();\n\nconst emit = defineEmits<{\n select: [item: TItem];\n}>();\n\ndefineSlots<{\n 'activator'?: (props: { attrs: { onClick?: () => void; onMouseover?: () => void; onMouseleave?: () => void }; isOpen: boolean; open: () => void; item: TItem | undefined; text: string | undefined }) => any;\n 'selection'?: (props: { item: TItem }) => any;\n 'selection.prepend'?: (props: { item: TItem }) => any;\n 'category'?: (props: { category: string | null; label: string; active: boolean; count: number }) => any;\n 'item'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'item.prepend'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'item.append'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'empty'?: () => any;\n 'header'?: () => any;\n 'footer'?: (props: { close: () => void }) => any;\n}>();\n\nconst isOpen = ref<boolean>(false);\nconst focusedPane = ref<'rail' | 'detail'>('rail');\nconst highlightedItemIndex = ref<number>(0);\nconst drillStep = ref<'category' | 'detail'>('category');\n\nconst railRef = useTemplateRef<HTMLDivElement>('railRef');\nconst detailRef = useTemplateRef<HTMLDivElement>('detailRef');\nconst activatorRef = useTemplateRef<HTMLInputElement>('activatorRef');\nconst fieldRef = useTemplateRef<HTMLElement>('fieldRef');\nconst isHovered = ref<boolean>(false);\n\nconst { focused } = useFocus(activatorRef);\nconst { hasError, hasSuccess } = useFormTextDetail(() => errorMessages, () => successMessages);\nconst detailsMode = useFormDetails(() => details, () => hideDetails);\n\nconst baseId = useId();\nconst breakpoints = useBreakpoints(breakpointsTailwind);\n\nfunction resolveCategory(item: TItem): string {\n if (categoryOf)\n return categoryOf(item);\n if (categoryAttr !== undefined)\n return String(item[categoryAttr] ?? '');\n return '';\n}\n\nconst {\n activeCategory,\n detailItems,\n detailRows,\n getIdentifier,\n getText,\n isEmpty,\n isSelected,\n railEntries,\n setActiveCategory,\n syncActiveToSelection,\n} = useCategoryPicker<TValue, TItem>({\n allLabel: () => allLabel,\n categoryOf: resolveCategory,\n filter: () => filter,\n items: () => items,\n keyAttr,\n modelValue,\n search: searchInput,\n searchable: () => searchable,\n showAll: () => showAll,\n textAttr,\n});\n\nconst isMobile = computed<boolean>(() => get(breakpoints.smaller(mobileBreakpoint)));\n\nconst mode = computed<'twoPane' | 'drill'>(() => (get(isMobile) ? 'drill' : 'twoPane'));\n\n/**\n * Desktop anchors an outside-click popover under the field (RuiMenu); narrow\n * width shows a full-width bottom sheet (RuiDialog).\n */\nconst shell = computed<typeof RuiMenu | typeof RuiDialog>(() => (get(isMobile) ? RuiDialog : RuiMenu));\n\n/**\n * The bottom sheet's props. Each shell is given only its own props, so the\n * other component's never leak through as attributes.\n */\nfunction dialogShellProps(): Record<string, unknown> {\n return {\n ariaLabel: dialogOptions?.ariaLabel ?? label,\n bottomSheet: dialogOptions?.bottomSheet ?? true,\n maxWidth: dialogOptions?.maxWidth,\n persistent: dialogOptions?.persistent,\n width: dialogOptions?.width ?? '100%',\n };\n}\n\n/**\n * The popover's props. It anchors to the field box rather than the wrapper, so\n * it sits directly under the input instead of below the reserved details row,\n * and drops the menu's default vertical padding so the popover box equals the\n * panel: the size middleware caps the panel to the available height, and any\n * extra chrome padding would push the popover past the viewport edge. The\n * popover is a dialog, as the activator's `aria-haspopup` says: it holds a\n * tablist and a listbox, which a `menu` may not contain.\n */\nfunction menuShellProps(): Record<string, unknown> {\n return {\n anchorEl: get(fieldRef) ?? undefined,\n classNames: { content: 'py-0' },\n closeOnContentClick: false,\n disableAutoFocus: true,\n fullWidth: true,\n menuLabel: label,\n options: { placement: Placement.bottomStart, size: true },\n persistent: dialogOptions?.persistent,\n persistOnActivatorClick: true,\n role: 'dialog',\n };\n}\n\nconst shellProps = computed<Record<string, unknown>>(() =>\n get(isMobile) ? dialogShellProps() : menuShellProps());\n\nconst ui = computed<ReturnType<typeof categoryPickerStyles>>(() => categoryPickerStyles({\n dense,\n mode: get(mode),\n}));\n\nconst selectedItem = computed<TItem | undefined>(() => {\n const value = get(modelValue);\n if (value === undefined)\n return undefined;\n return items.find(item => getIdentifier(item) === value);\n});\n\nconst selectedText = computed<string | undefined>(() => {\n const selected = get(selectedItem);\n return selected ? getText(selected) : undefined;\n});\n\nconst placement = useLabelPlacement(() => labelPlacement);\n\n/**\n * Desktop lets the user type in the field itself (no separate search box);\n * narrow width keeps a read-only trigger that opens the sheet's own search.\n */\nconst canType = computed<boolean>(() => searchable && !get(isMobile) && !readOnly && !disabled);\n\nconst isTyping = computed<boolean>(() => get(isOpen) && get(canType));\n\n/**\n * Drives both the clear button itself and the room the selection overlay has\n * to leave for it, so the two can never disagree.\n */\nconst showClear = computed<boolean>(() =>\n clearable && get(selectedItem) !== undefined && !disabled && !readOnly);\n\nconst activatorUi = computed<ReturnType<typeof categoryPickerActivatorStyles>>(() => categoryPickerActivatorStyles({\n dense,\n disabled,\n hasError: get(hasError),\n hasSuccess: get(hasSuccess) && !get(hasError),\n hovered: get(isHovered),\n opened: get(isOpen),\n readonly: readOnly,\n withClear: get(showClear),\n}));\n\nconst activeRailIndex = computed<number>(() =>\n get(railEntries).findIndex(entry => entry.category === get(activeCategory)));\n\nconst showRail = computed<boolean>(() => get(mode) === 'twoPane' || get(drillStep) === 'category');\nconst showDetail = computed<boolean>(() => get(mode) === 'twoPane' || get(drillStep) === 'detail');\n\nfunction clamp(value: number, max: number): number {\n if (value < 0)\n return 0;\n if (value > max)\n return max;\n return value;\n}\n\nfunction open(): void {\n if (disabled)\n return;\n set(isOpen, true);\n}\n\nfunction close(): void {\n set(isOpen, false);\n}\n\nfunction onInput(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement))\n return;\n set(searchInput, target.value);\n if (!get(isOpen))\n open();\n}\n\nfunction selectItem(item: TItem): void {\n if (readOnly)\n return;\n set(modelValue, getIdentifier(item));\n emit('select', item);\n close();\n}\n\nfunction clearSelection(): void {\n set(modelValue, undefined);\n}\n\nfunction onCategoryClick(category: string | null): void {\n setActiveCategory(category);\n if (get(mode) === 'drill')\n set(drillStep, 'detail');\n else\n focusPane('detail');\n}\n\n/**\n * Moves focus to one of the panes. The focus call prevents scrolling because\n * the panes sit inside a teleported popover, and without it the browser\n * scrolls the page behind to reveal the pane.\n */\nfunction focusPane(pane: 'rail' | 'detail'): void {\n set(focusedPane, pane);\n nextTick(() => {\n (pane === 'rail' ? get(railRef) : get(detailRef))?.focus({ preventScroll: true });\n });\n}\n\nfunction moveRail(direction: 1 | -1): void {\n const entries = get(railEntries);\n if (entries.length === 0)\n return;\n const next = clamp(get(activeRailIndex) + direction, entries.length - 1);\n setActiveCategory(entries[next]?.category ?? null);\n}\n\nfunction moveItem(direction: 1 | -1): void {\n const total = get(detailItems).length;\n if (total === 0)\n return;\n set(highlightedItemIndex, clamp(get(highlightedItemIndex) + direction, total - 1));\n}\n\nfunction enterDetail(): void {\n if (get(detailItems).length === 0)\n return;\n resetHighlightToSelection();\n if (get(mode) === 'drill')\n set(drillStep, 'detail');\n focusPane('detail');\n}\n\nfunction selectHighlighted(): void {\n const item = get(detailItems)[get(highlightedItemIndex)];\n if (item !== undefined)\n selectItem(item);\n}\n\n/**\n * Enter from the field commits the best match for the current query, so a\n * type-to-pick flow (type a few letters, press Enter) works without leaving\n * the input. Does nothing when the field is empty.\n */\nfunction selectTopResult(): void {\n if (!get(searchInput).trim())\n return;\n const item = get(detailItems)[0];\n if (item !== undefined)\n selectItem(item);\n}\n\n/**\n * Keys in the search field: ArrowDown moves into the rail, Enter picks the top\n * match, Escape closes.\n *\n * An IME that is composing owns all three: the arrows walk its candidate list,\n * Enter confirms a candidate and Escape cancels it. While composing the field\n * leaves them alone, default action included, so neither a pick nor a close\n * lands mid-word.\n */\nfunction onSearchKeydown(event: KeyboardEvent): void {\n if (event.isComposing)\n return;\n\n if (event.key === 'ArrowDown') {\n event.preventDefault();\n focusPane('rail');\n }\n else if (event.key === 'Enter') {\n event.preventDefault();\n selectTopResult();\n }\n else if (event.key === 'Escape') {\n close();\n }\n}\n\nfunction resetHighlightToSelection(): void {\n const index = get(detailItems).findIndex(item => isSelected(item));\n set(highlightedItemIndex, index >= 0 ? index : 0);\n}\n\nfunction backToRail(): void {\n if (get(mode) === 'drill')\n set(drillStep, 'category');\n else\n focusPane('rail');\n}\n\nfunction scrollActiveDescendantIntoView(container: HTMLElement | null, id: string): void {\n if (!container)\n return;\n nextTick(() => {\n container.querySelector(`#${id}`)?.scrollIntoView({ block: 'nearest' });\n });\n}\n\n// Reset the item highlight whenever the active category changes.\nwatch(activeCategory, () => {\n resetHighlightToSelection();\n});\n\nwatch(highlightedItemIndex, (index) => {\n scrollActiveDescendantIntoView(get(detailRef), `${baseId}-opt-${index}`);\n});\n\nwatch(activeRailIndex, (index) => {\n if (index >= 0)\n scrollActiveDescendantIntoView(get(railRef), `${baseId}-cat-${index}`);\n});\n\n/**\n * Opening takes focus where the user types next: desktop is a combobox, so\n * focus stays in the field and ArrowDown moves into the panes, while the\n * mobile sheet has no typing field and focuses the rail instead. Closing\n * resets the query, so the field shows the selection again next time, and\n * returns focus to the trigger, which the APG dialog contract asks for.\n */\nfunction onOpenChanged(value: boolean): void {\n if (value) {\n syncActiveToSelection();\n set(focusedPane, 'rail');\n set(drillStep, 'category');\n resetHighlightToSelection();\n nextTick(() => (get(canType) ? get(activatorRef) : get(railRef))?.focus({ preventScroll: true }));\n }\n else {\n set(searchInput, '');\n nextTick(() => get(activatorRef)?.focus({ preventScroll: true }));\n }\n}\n\nwatch(isOpen, onOpenChanged);\n</script>\n\n<template>\n <component\n :is=\"shell\"\n v-model=\"isOpen\"\n v-bind=\"{ ...$attrs, ...shellProps }\"\n >\n <template #activator=\"{ attrs }\">\n <slot\n name=\"activator\"\n v-bind=\"{ attrs, isOpen, item: selectedItem, open, text: selectedText }\"\n >\n <div :class=\"activatorUi.wrapper()\">\n <RuiFieldLabel\n v-if=\"label\"\n :id=\"`${baseId}-label`\"\n :text=\"label\"\n :hidden=\"placement === 'hidden'\"\n :required=\"required\"\n :disabled=\"disabled\"\n />\n <div\n ref=\"fieldRef\"\n role=\"combobox\"\n data-id=\"activator\"\n :class=\"activatorUi.activator()\"\n :aria-labelledby=\"label ? `${baseId}-label` : undefined\"\n aria-haspopup=\"dialog\"\n :aria-controls=\"isOpen ? `${baseId}-panel` : undefined\"\n :aria-expanded=\"isOpen\"\n :aria-disabled=\"disabled || undefined\"\n :aria-readonly=\"(readOnly || !canType) || undefined\"\n :aria-required=\"required || undefined\"\n :aria-invalid=\"hasError || undefined\"\n :aria-busy=\"loading || undefined\"\n v-bind=\"readOnly ? {} : activatorHandlers(attrs)\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n >\n <span\n v-if=\"selectedItem && !isTyping && $slots.selection\"\n data-id=\"selection\"\n :class=\"activatorUi.selection()\"\n >\n <slot\n name=\"selection.prepend\"\n v-bind=\"{ item: selectedItem }\"\n />\n <slot\n name=\"selection\"\n v-bind=\"{ item: selectedItem }\"\n />\n </span>\n <input\n ref=\"activatorRef\"\n data-id=\"search-input\"\n :value=\"isTyping ? searchInput : (($slots.selection && selectedItem) ? '' : (selectedText ?? ''))\"\n :placeholder=\"isTyping ? placeholder : ''\"\n :readonly=\"!canType\"\n :disabled=\"disabled\"\n :aria-invalid=\"hasError || undefined\"\n :aria-labelledby=\"label ? `${baseId}-label` : undefined\"\n class=\"bg-transparent outline-hidden\"\n :class=\"[activatorUi.value(), !canType && 'cursor-pointer']\"\n @input=\"onInput($event)\"\n @keydown=\"onSearchKeydown($event)\"\n />\n <span\n v-if=\"showClear\"\n data-id=\"clear\"\n :class=\"[activatorUi.clear(), focused && 'visible!', { 'mr-2': !dense }]\"\n @click.stop.prevent=\"clearSelection()\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"16\"\n />\n </span>\n <span\n data-id=\"chevron\"\n :class=\"activatorUi.iconWrapper()\"\n >\n <RuiIcon\n :class=\"activatorUi.icon()\"\n :size=\"dense ? 16 : 20\"\n name=\"lu-chevron-down\"\n />\n </span>\n <RuiProgress\n v-if=\"loading\"\n :class=\"activatorUi.progress()\"\n color=\"primary\"\n thickness=\"3\"\n variant=\"indeterminate\"\n />\n <fieldset :class=\"activatorUi.fieldset()\" />\n </div>\n <RuiFormTextDetail\n v-if=\"detailsMode !== 'hidden'\"\n :details=\"detailsMode\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n />\n </div>\n </slot>\n </template>\n\n <div\n :id=\"`${baseId}-panel`\"\n :class=\"ui.root({ class: cn(classNames?.root) })\"\n data-id=\"panel\"\n >\n <!-- On desktop the field itself is the search input, so the panel only\n needs its own search box in the mobile sheet. -->\n <div\n v-if=\"isMobile || $slots.header\"\n :class=\"ui.header()\"\n >\n <slot name=\"header\">\n <div :class=\"ui.title()\">\n {{ label }}\n </div>\n </slot>\n <RuiTextField\n v-if=\"searchable && isMobile\"\n v-model=\"searchInput\"\n :class=\"cn(classNames?.search)\"\n :placeholder=\"placeholder\"\n :dense=\"dense\"\n prepend-icon=\"lu-search\"\n details=\"hidden\"\n clearable\n data-id=\"search\"\n />\n </div>\n\n <div :class=\"ui.body()\">\n <!-- Category rail (master) -->\n <div\n v-show=\"showRail\"\n ref=\"railRef\"\n :class=\"ui.rail({ class: cn(classNames?.rail) })\"\n role=\"tablist\"\n aria-orientation=\"vertical\"\n :aria-label=\"label\"\n :aria-activedescendant=\"focusedPane === 'rail' && activeRailIndex >= 0 ? `${baseId}-cat-${activeRailIndex}` : undefined\"\n tabindex=\"0\"\n data-id=\"rail\"\n @focus=\"focusedPane = 'rail'\"\n @keydown.down.prevent=\"moveRail(1)\"\n @keydown.up.prevent=\"moveRail(-1)\"\n @keydown.right.prevent=\"enterDetail()\"\n @keydown.enter.prevent=\"enterDetail()\"\n @keydown.home.prevent=\"setActiveCategory(railEntries[0]?.category ?? null)\"\n @keydown.end.prevent=\"setActiveCategory(railEntries[railEntries.length - 1]?.category ?? null)\"\n >\n <RuiButton\n v-for=\"(entry, index) in railEntries\"\n :id=\"`${baseId}-cat-${index}`\"\n :key=\"entry.category ?? '__all__'\"\n role=\"tab\"\n variant=\"list\"\n :size=\"dense ? 'sm' : undefined\"\n :active=\"entry.category === activeCategory\"\n :aria-selected=\"entry.category === activeCategory\"\n tabindex=\"-1\"\n :data-highlighted=\"focusedPane === 'rail' && index === activeRailIndex\"\n :data-category=\"entry.category ?? '__all__'\"\n @click=\"onCategoryClick(entry.category)\"\n >\n <slot\n name=\"category\"\n v-bind=\"{ active: entry.category === activeCategory, category: entry.category, count: entry.count, label: entry.label }\"\n >\n <span class=\"truncate\">{{ entry.label }}</span>\n <span :class=\"ui.railCount()\">{{ entry.count }}</span>\n </slot>\n </RuiButton>\n </div>\n\n <!-- Detail pane -->\n <div\n v-show=\"showDetail\"\n ref=\"detailRef\"\n :class=\"ui.detail({ class: cn(classNames?.detail) })\"\n role=\"listbox\"\n :aria-label=\"activeCategory ?? allLabel\"\n :aria-activedescendant=\"focusedPane === 'detail' && !isEmpty ? `${baseId}-opt-${highlightedItemIndex}` : undefined\"\n tabindex=\"0\"\n data-id=\"detail\"\n @focus=\"focusedPane = 'detail'\"\n @keydown.down.prevent=\"moveItem(1)\"\n @keydown.up.prevent=\"moveItem(-1)\"\n @keydown.left.prevent=\"backToRail()\"\n @keydown.home.prevent=\"highlightedItemIndex = 0\"\n @keydown.end.prevent=\"highlightedItemIndex = detailItems.length - 1\"\n @keydown.enter.prevent=\"selectHighlighted()\"\n @keydown.space.prevent=\"selectHighlighted()\"\n >\n <template v-if=\"mode === 'drill' && showDetail\">\n <button\n type=\"button\"\n :class=\"ui.backButton()\"\n data-id=\"back\"\n @click=\"backToRail()\"\n >\n <RuiIcon\n name=\"lu-chevron-left\"\n size=\"18\"\n />\n <span>{{ activeCategory ?? allLabel }}</span>\n </button>\n </template>\n\n <slot\n v-if=\"isEmpty\"\n name=\"empty\"\n >\n <div :class=\"ui.empty()\">\n {{ emptyText }}\n </div>\n </slot>\n\n <template\n v-for=\"row in detailRows\"\n v-else\n :key=\"row.type === 'header' ? `h-${row.category}` : `i-${row.index}`\"\n >\n <div\n v-if=\"row.type === 'header'\"\n :class=\"ui.subheader()\"\n role=\"presentation\"\n >\n {{ row.category }}\n </div>\n <RuiButton\n v-else\n :id=\"`${baseId}-opt-${row.index}`\"\n role=\"option\"\n variant=\"list\"\n :size=\"dense ? 'sm' : undefined\"\n :active=\"isSelected(row.item)\"\n :aria-selected=\"isSelected(row.item)\"\n tabindex=\"-1\"\n :data-highlighted=\"focusedPane === 'detail' && row.index === highlightedItemIndex\"\n :class=\"{ [ui.highlighted()]: focusedPane === 'detail' && row.index === highlightedItemIndex && !isSelected(row.item) }\"\n @click=\"selectItem(row.item)\"\n >\n <template\n v-if=\"$slots['item.prepend']\"\n #prepend\n >\n <slot\n name=\"item.prepend\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n >\n {{ getText(row.item) }}\n </slot>\n <template\n v-if=\"$slots['item.append']\"\n #append\n >\n <slot\n name=\"item.append\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n />\n </template>\n </RuiButton>\n </template>\n </div>\n </div>\n\n <div\n v-if=\"$slots.footer\"\n :class=\"ui.footer()\"\n >\n <slot\n name=\"footer\"\n v-bind=\"{ close }\"\n />\n </div>\n </div>\n </component>\n</template>\n"],"mappings":""}
@@ -305,6 +305,25 @@ var RuiCategoryPicker_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
305
305
  const item = get(detailItems)[0];
306
306
  if (item !== void 0) selectItem(item);
307
307
  }
308
+ /**
309
+ * Keys in the search field: ArrowDown moves into the rail, Enter picks the top
310
+ * match, Escape closes.
311
+ *
312
+ * An IME that is composing owns all three: the arrows walk its candidate list,
313
+ * Enter confirms a candidate and Escape cancels it. While composing the field
314
+ * leaves them alone, default action included, so neither a pick nor a close
315
+ * lands mid-word.
316
+ */
317
+ function onSearchKeydown(event) {
318
+ if (event.isComposing) return;
319
+ if (event.key === "ArrowDown") {
320
+ event.preventDefault();
321
+ focusPane("rail");
322
+ } else if (event.key === "Enter") {
323
+ event.preventDefault();
324
+ selectTopResult();
325
+ } else if (event.key === "Escape") close();
326
+ }
308
327
  function resetHighlightToSelection() {
309
328
  const index = get(detailItems).findIndex((item) => isSelected(item));
310
329
  set(highlightedItemIndex, index >= 0 ? index : 0);
@@ -351,7 +370,7 @@ var RuiCategoryPicker_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
351
370
  return (_ctx, _cache) => {
352
371
  return openBlock(), createBlock(resolveDynamicComponent(unref(shell)), mergeProps({
353
372
  modelValue: unref(isOpen),
354
- "onUpdate:modelValue": _cache[24] || (_cache[24] = ($event) => isRef(isOpen) ? isOpen.value = $event : null)
373
+ "onUpdate:modelValue": _cache[22] || (_cache[22] = ($event) => isRef(isOpen) ? isOpen.value = $event : null)
355
374
  }, {
356
375
  ..._ctx.$attrs,
357
376
  ...unref(shellProps)
@@ -393,8 +412,8 @@ var RuiCategoryPicker_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
393
412
  "aria-invalid": unref(hasError) || void 0,
394
413
  "aria-busy": __props.loading || void 0
395
414
  }, __props.readOnly ? {} : unref(activatorHandlers)(attrs), {
396
- onMouseenter: _cache[5] || (_cache[5] = ($event) => isHovered.value = true),
397
- onMouseleave: _cache[6] || (_cache[6] = ($event) => isHovered.value = false)
415
+ onMouseenter: _cache[3] || (_cache[3] = ($event) => isHovered.value = true),
416
+ onMouseleave: _cache[4] || (_cache[4] = ($event) => isHovered.value = false)
398
417
  }), [
399
418
  unref(selectedItem) && !unref(isTyping) && _ctx.$slots.selection ? (openBlock(), createElementBlock("span", {
400
419
  key: 0,
@@ -413,11 +432,7 @@ var RuiCategoryPicker_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
413
432
  "aria-labelledby": __props.label ? `${unref(baseId)}-label` : void 0,
414
433
  class: normalizeClass(["bg-transparent outline-hidden", [unref(activatorUi).value(), !unref(canType) && "cursor-pointer"]]),
415
434
  onInput: _cache[0] || (_cache[0] = ($event) => onInput($event)),
416
- onKeydown: [
417
- _cache[1] || (_cache[1] = withKeys(withModifiers(($event) => focusPane("rail"), ["prevent"]), ["down"])),
418
- _cache[2] || (_cache[2] = withKeys(withModifiers(($event) => selectTopResult(), ["prevent"]), ["enter"])),
419
- _cache[3] || (_cache[3] = withKeys(($event) => close(), ["esc"]))
420
- ]
435
+ onKeydown: _cache[1] || (_cache[1] = ($event) => onSearchKeydown($event))
421
436
  }, null, 42, _hoisted_2),
422
437
  unref(showClear) ? (openBlock(), createElementBlock("span", {
423
438
  key: 1,
@@ -427,7 +442,7 @@ var RuiCategoryPicker_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
427
442
  unref(focused) && "visible!",
428
443
  { "mr-2": !__props.dense }
429
444
  ]),
430
- onClick: _cache[4] || (_cache[4] = withModifiers(($event) => clearSelection(), ["stop", "prevent"]))
445
+ onClick: _cache[2] || (_cache[2] = withModifiers(($event) => clearSelection(), ["stop", "prevent"]))
431
446
  }, [createVNode(RuiIcon_default, {
432
447
  name: "lu-x",
433
448
  size: "16"
@@ -473,7 +488,7 @@ var RuiCategoryPicker_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
473
488
  }, [renderSlot(_ctx.$slots, "header", {}, () => [createElementVNode("div", { class: normalizeClass(unref(ui).title()) }, toDisplayString(__props.label), 3)]), __props.searchable && unref(isMobile) ? (openBlock(), createBlock(RuiTextField_default, {
474
489
  key: 0,
475
490
  modelValue: searchInput.value,
476
- "onUpdate:modelValue": _cache[7] || (_cache[7] = ($event) => searchInput.value = $event),
491
+ "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => searchInput.value = $event),
477
492
  class: normalizeClass(unref(cn)(__props.classNames?.search)),
478
493
  placeholder: __props.placeholder,
479
494
  dense: __props.dense,
@@ -497,14 +512,14 @@ var RuiCategoryPicker_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
497
512
  "aria-activedescendant": unref(focusedPane) === "rail" && unref(activeRailIndex) >= 0 ? `${unref(baseId)}-cat-${unref(activeRailIndex)}` : void 0,
498
513
  tabindex: "0",
499
514
  "data-id": "rail",
500
- onFocus: _cache[8] || (_cache[8] = ($event) => focusedPane.value = "rail"),
515
+ onFocus: _cache[6] || (_cache[6] = ($event) => focusedPane.value = "rail"),
501
516
  onKeydown: [
502
- _cache[9] || (_cache[9] = withKeys(withModifiers(($event) => moveRail(1), ["prevent"]), ["down"])),
503
- _cache[10] || (_cache[10] = withKeys(withModifiers(($event) => moveRail(-1), ["prevent"]), ["up"])),
504
- _cache[11] || (_cache[11] = withKeys(withModifiers(($event) => enterDetail(), ["prevent"]), ["right"])),
505
- _cache[12] || (_cache[12] = withKeys(withModifiers(($event) => enterDetail(), ["prevent"]), ["enter"])),
506
- _cache[13] || (_cache[13] = withKeys(withModifiers(($event) => unref(setActiveCategory)(unref(railEntries)[0]?.category ?? null), ["prevent"]), ["home"])),
507
- _cache[14] || (_cache[14] = withKeys(withModifiers(($event) => unref(setActiveCategory)(unref(railEntries)[unref(railEntries).length - 1]?.category ?? null), ["prevent"]), ["end"]))
517
+ _cache[7] || (_cache[7] = withKeys(withModifiers(($event) => moveRail(1), ["prevent"]), ["down"])),
518
+ _cache[8] || (_cache[8] = withKeys(withModifiers(($event) => moveRail(-1), ["prevent"]), ["up"])),
519
+ _cache[9] || (_cache[9] = withKeys(withModifiers(($event) => enterDetail(), ["prevent"]), ["right"])),
520
+ _cache[10] || (_cache[10] = withKeys(withModifiers(($event) => enterDetail(), ["prevent"]), ["enter"])),
521
+ _cache[11] || (_cache[11] = withKeys(withModifiers(($event) => unref(setActiveCategory)(unref(railEntries)[0]?.category ?? null), ["prevent"]), ["home"])),
522
+ _cache[12] || (_cache[12] = withKeys(withModifiers(($event) => unref(setActiveCategory)(unref(railEntries)[unref(railEntries).length - 1]?.category ?? null), ["prevent"]), ["end"]))
508
523
  ]
509
524
  }, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(railEntries), (entry, index) => {
510
525
  return openBlock(), createBlock(RuiButton_default, {
@@ -545,22 +560,22 @@ var RuiCategoryPicker_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*
545
560
  "aria-activedescendant": unref(focusedPane) === "detail" && !unref(isEmpty) ? `${unref(baseId)}-opt-${unref(highlightedItemIndex)}` : void 0,
546
561
  tabindex: "0",
547
562
  "data-id": "detail",
548
- onFocus: _cache[16] || (_cache[16] = ($event) => focusedPane.value = "detail"),
563
+ onFocus: _cache[14] || (_cache[14] = ($event) => focusedPane.value = "detail"),
549
564
  onKeydown: [
550
- _cache[17] || (_cache[17] = withKeys(withModifiers(($event) => moveItem(1), ["prevent"]), ["down"])),
551
- _cache[18] || (_cache[18] = withKeys(withModifiers(($event) => moveItem(-1), ["prevent"]), ["up"])),
552
- _cache[19] || (_cache[19] = withKeys(withModifiers(($event) => backToRail(), ["prevent"]), ["left"])),
553
- _cache[20] || (_cache[20] = withKeys(withModifiers(($event) => highlightedItemIndex.value = 0, ["prevent"]), ["home"])),
554
- _cache[21] || (_cache[21] = withKeys(withModifiers(($event) => highlightedItemIndex.value = unref(detailItems).length - 1, ["prevent"]), ["end"])),
555
- _cache[22] || (_cache[22] = withKeys(withModifiers(($event) => selectHighlighted(), ["prevent"]), ["enter"])),
556
- _cache[23] || (_cache[23] = withKeys(withModifiers(($event) => selectHighlighted(), ["prevent"]), ["space"]))
565
+ _cache[15] || (_cache[15] = withKeys(withModifiers(($event) => moveItem(1), ["prevent"]), ["down"])),
566
+ _cache[16] || (_cache[16] = withKeys(withModifiers(($event) => moveItem(-1), ["prevent"]), ["up"])),
567
+ _cache[17] || (_cache[17] = withKeys(withModifiers(($event) => backToRail(), ["prevent"]), ["left"])),
568
+ _cache[18] || (_cache[18] = withKeys(withModifiers(($event) => highlightedItemIndex.value = 0, ["prevent"]), ["home"])),
569
+ _cache[19] || (_cache[19] = withKeys(withModifiers(($event) => highlightedItemIndex.value = unref(detailItems).length - 1, ["prevent"]), ["end"])),
570
+ _cache[20] || (_cache[20] = withKeys(withModifiers(($event) => selectHighlighted(), ["prevent"]), ["enter"])),
571
+ _cache[21] || (_cache[21] = withKeys(withModifiers(($event) => selectHighlighted(), ["prevent"]), ["space"]))
557
572
  ]
558
573
  }, [unref(mode) === "drill" && unref(showDetail) ? (openBlock(), createElementBlock("button", {
559
574
  key: 0,
560
575
  type: "button",
561
576
  class: normalizeClass(unref(ui).backButton()),
562
577
  "data-id": "back",
563
- onClick: _cache[15] || (_cache[15] = ($event) => backToRail())
578
+ onClick: _cache[13] || (_cache[13] = ($event) => backToRail())
564
579
  }, [createVNode(RuiIcon_default, {
565
580
  name: "lu-chevron-left",
566
581
  size: "18"
@@ -1 +1 @@
1
- {"version":3,"file":"RuiCategoryPicker.vue_vue_type_script_setup_true_lang.js","names":["$attrs","$slots","_useModel","RuiDialog","RuiMenu","_openBlock","_createBlock","_resolveDynamicComponent","_unref","_mergeProps","_isRef","_withCtx","_renderSlot","_normalizeProps","_guardReactiveProps","_createElementVNode","_normalizeClass","RuiFieldLabel","_createCommentVNode","_createElementBlock","_withKeys","_withModifiers","_createVNode","RuiIcon","RuiProgress","RuiFormTextDetail","_toDisplayString","RuiTextField","_withDirectives","_Fragment","_renderList","RuiButton","_vShow","_createSlots","_createTextVNode"],"sources":["../../../../src/components/forms/category-picker/RuiCategoryPicker.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue, TItem\">\nimport type { KeyOfType } from '@/composables/dropdown-menu';\nimport type { VueClassValue } from '@/types/class-value';\nimport { breakpointsTailwind, useBreakpoints } from '@vueuse/core';\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport { categoryPickerActivatorStyles, categoryPickerStyles } from '@/components/forms/category-picker/category-picker-styles';\nimport RuiFieldLabel from '@/components/forms/field-label/RuiFieldLabel.vue';\nimport RuiTextField from '@/components/forms/text-field/RuiTextField.vue';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiDialog, { type DialogProps } from '@/components/overlays/dialog/RuiDialog.vue';\nimport { activatorHandlers } from '@/components/overlays/menu/activator-handlers';\nimport RuiMenu from '@/components/overlays/menu/RuiMenu.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { type LabelPlacement, useLabelPlacement } from '@/composables/defaults/field';\nimport { Placement } from '@/composables/floating';\nimport { useCategoryPicker } from '@/composables/forms/category-picker/use-category-picker';\nimport { type FormDetails, useFormDetails, useFormTextDetail } from '@/utils/form-text-detail';\nimport { cn } from '@/utils/tv';\n\nexport interface RuiCategoryPickerClassNames {\n root?: VueClassValue;\n rail?: VueClassValue;\n detail?: VueClassValue;\n search?: VueClassValue;\n}\n\nexport type CategoryPickerBreakpoint = 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\nexport interface RuiCategoryPickerProps<TValue, TItem> {\n /** The full catalogue of selectable items. */\n items: TItem[];\n /** Resolve an item's category label. Takes precedence over `categoryAttr`. */\n categoryOf?: (item: TItem) => string;\n /** Resolve an item's category from one of its own properties. */\n categoryAttr?: keyof TItem;\n /** Property whose value becomes the model value. Omit to use the item itself. */\n keyAttr?: KeyOfType<TItem, TValue extends (infer U)[] ? U : TValue>;\n /** Property that produces an item's display text. */\n textAttr?: keyof TItem;\n /** Field label, and the title of the mobile sheet. */\n label?: string;\n /** Placeholder shown in the field while it is empty or being searched. */\n placeholder?: string;\n /** Allow filtering: type in the field (desktop) or the sheet's search box (mobile). */\n searchable?: boolean;\n /**\n * Custom predicate. Receives the resolved category label as the third\n * argument, matching the `RuiAutoComplete` filter signature.\n */\n filter?: (item: TItem, query: string, category: string) => boolean;\n /** Show the synthetic \"All\" pseudo-category as the first rail entry. */\n showAll?: boolean;\n /** Label for the \"All\" pseudo-category. */\n allLabel?: string;\n /** Text shown when no item matches the current search. */\n emptyText?: string;\n disabled?: boolean;\n readOnly?: boolean;\n dense?: boolean;\n clearable?: boolean;\n loading?: boolean;\n /** Where the label shows; falls back to the nearest `RuiFieldDefaults`, then the app default, then `top`. */\n labelPlacement?: LabelPlacement;\n /** Marks the trigger field as required (renders the asterisk). */\n required?: boolean;\n /** Validation errors rendered under the trigger field. */\n errorMessages?: string | string[];\n /** Success messages rendered under the trigger field. */\n successMessages?: string | string[];\n /** Hint rendered under the trigger field. */\n hint?: string;\n /** The line under the trigger field for its hint and messages: `reserve`, `auto` or `hidden`; falls back to the nearest `RuiFieldDefaults`, then the app default, then `reserve`. */\n details?: FormDetails;\n /** @deprecated Use `details=\"hidden\"`. Removed in 4.0. */\n hideDetails?: boolean;\n /** Below this breakpoint the picker collapses to a single-pane drill-in. */\n mobileBreakpoint?: CategoryPickerBreakpoint;\n /** Forwarded to the mobile bottom-sheet (`RuiDialog`). */\n dialogOptions?: DialogProps;\n classNames?: RuiCategoryPickerClassNames;\n}\n\ndefineOptions({\n name: 'RuiCategoryPicker',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<TValue>();\nconst searchInput = defineModel<string>('search', { default: '' });\n\nconst {\n items,\n categoryOf = undefined,\n categoryAttr = undefined,\n keyAttr = undefined,\n textAttr = undefined,\n label = 'Select',\n placeholder = 'Search',\n searchable = true,\n filter = undefined,\n showAll = true,\n allLabel = 'All',\n emptyText = 'No results',\n disabled = false,\n readOnly = false,\n dense = false,\n clearable = false,\n loading = false,\n labelPlacement = undefined,\n required = false,\n errorMessages = [],\n successMessages = [],\n hint = '',\n details = undefined,\n hideDetails = false,\n mobileBreakpoint = 'md',\n dialogOptions = undefined,\n classNames = undefined,\n} = defineProps<RuiCategoryPickerProps<TValue, TItem>>();\n\nconst emit = defineEmits<{\n select: [item: TItem];\n}>();\n\ndefineSlots<{\n 'activator'?: (props: { attrs: { onClick?: () => void; onMouseover?: () => void; onMouseleave?: () => void }; isOpen: boolean; open: () => void; item: TItem | undefined; text: string | undefined }) => any;\n 'selection'?: (props: { item: TItem }) => any;\n 'selection.prepend'?: (props: { item: TItem }) => any;\n 'category'?: (props: { category: string | null; label: string; active: boolean; count: number }) => any;\n 'item'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'item.prepend'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'item.append'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'empty'?: () => any;\n 'header'?: () => any;\n 'footer'?: (props: { close: () => void }) => any;\n}>();\n\nconst isOpen = ref<boolean>(false);\nconst focusedPane = ref<'rail' | 'detail'>('rail');\nconst highlightedItemIndex = ref<number>(0);\nconst drillStep = ref<'category' | 'detail'>('category');\n\nconst railRef = useTemplateRef<HTMLDivElement>('railRef');\nconst detailRef = useTemplateRef<HTMLDivElement>('detailRef');\nconst activatorRef = useTemplateRef<HTMLInputElement>('activatorRef');\nconst fieldRef = useTemplateRef<HTMLElement>('fieldRef');\nconst isHovered = ref<boolean>(false);\n\nconst { focused } = useFocus(activatorRef);\nconst { hasError, hasSuccess } = useFormTextDetail(() => errorMessages, () => successMessages);\nconst detailsMode = useFormDetails(() => details, () => hideDetails);\n\nconst baseId = useId();\nconst breakpoints = useBreakpoints(breakpointsTailwind);\n\nfunction resolveCategory(item: TItem): string {\n if (categoryOf)\n return categoryOf(item);\n if (categoryAttr !== undefined)\n return String(item[categoryAttr] ?? '');\n return '';\n}\n\nconst {\n activeCategory,\n detailItems,\n detailRows,\n getIdentifier,\n getText,\n isEmpty,\n isSelected,\n railEntries,\n setActiveCategory,\n syncActiveToSelection,\n} = useCategoryPicker<TValue, TItem>({\n allLabel: () => allLabel,\n categoryOf: resolveCategory,\n filter: () => filter,\n items: () => items,\n keyAttr,\n modelValue,\n search: searchInput,\n searchable: () => searchable,\n showAll: () => showAll,\n textAttr,\n});\n\nconst isMobile = computed<boolean>(() => get(breakpoints.smaller(mobileBreakpoint)));\n\nconst mode = computed<'twoPane' | 'drill'>(() => (get(isMobile) ? 'drill' : 'twoPane'));\n\n/**\n * Desktop anchors an outside-click popover under the field (RuiMenu); narrow\n * width shows a full-width bottom sheet (RuiDialog).\n */\nconst shell = computed<typeof RuiMenu | typeof RuiDialog>(() => (get(isMobile) ? RuiDialog : RuiMenu));\n\n/**\n * The bottom sheet's props. Each shell is given only its own props, so the\n * other component's never leak through as attributes.\n */\nfunction dialogShellProps(): Record<string, unknown> {\n return {\n ariaLabel: dialogOptions?.ariaLabel ?? label,\n bottomSheet: dialogOptions?.bottomSheet ?? true,\n maxWidth: dialogOptions?.maxWidth,\n persistent: dialogOptions?.persistent,\n width: dialogOptions?.width ?? '100%',\n };\n}\n\n/**\n * The popover's props. It anchors to the field box rather than the wrapper, so\n * it sits directly under the input instead of below the reserved details row,\n * and drops the menu's default vertical padding so the popover box equals the\n * panel: the size middleware caps the panel to the available height, and any\n * extra chrome padding would push the popover past the viewport edge. The\n * popover is a dialog, as the activator's `aria-haspopup` says: it holds a\n * tablist and a listbox, which a `menu` may not contain.\n */\nfunction menuShellProps(): Record<string, unknown> {\n return {\n anchorEl: get(fieldRef) ?? undefined,\n classNames: { content: 'py-0' },\n closeOnContentClick: false,\n disableAutoFocus: true,\n fullWidth: true,\n menuLabel: label,\n options: { placement: Placement.bottomStart, size: true },\n persistent: dialogOptions?.persistent,\n persistOnActivatorClick: true,\n role: 'dialog',\n };\n}\n\nconst shellProps = computed<Record<string, unknown>>(() =>\n get(isMobile) ? dialogShellProps() : menuShellProps());\n\nconst ui = computed<ReturnType<typeof categoryPickerStyles>>(() => categoryPickerStyles({\n dense,\n mode: get(mode),\n}));\n\nconst selectedItem = computed<TItem | undefined>(() => {\n const value = get(modelValue);\n if (value === undefined)\n return undefined;\n return items.find(item => getIdentifier(item) === value);\n});\n\nconst selectedText = computed<string | undefined>(() => {\n const selected = get(selectedItem);\n return selected ? getText(selected) : undefined;\n});\n\nconst placement = useLabelPlacement(() => labelPlacement);\n\n/**\n * Desktop lets the user type in the field itself (no separate search box);\n * narrow width keeps a read-only trigger that opens the sheet's own search.\n */\nconst canType = computed<boolean>(() => searchable && !get(isMobile) && !readOnly && !disabled);\n\nconst isTyping = computed<boolean>(() => get(isOpen) && get(canType));\n\n/**\n * Drives both the clear button itself and the room the selection overlay has\n * to leave for it, so the two can never disagree.\n */\nconst showClear = computed<boolean>(() =>\n clearable && get(selectedItem) !== undefined && !disabled && !readOnly);\n\nconst activatorUi = computed<ReturnType<typeof categoryPickerActivatorStyles>>(() => categoryPickerActivatorStyles({\n dense,\n disabled,\n hasError: get(hasError),\n hasSuccess: get(hasSuccess) && !get(hasError),\n hovered: get(isHovered),\n opened: get(isOpen),\n readonly: readOnly,\n withClear: get(showClear),\n}));\n\nconst activeRailIndex = computed<number>(() =>\n get(railEntries).findIndex(entry => entry.category === get(activeCategory)));\n\nconst showRail = computed<boolean>(() => get(mode) === 'twoPane' || get(drillStep) === 'category');\nconst showDetail = computed<boolean>(() => get(mode) === 'twoPane' || get(drillStep) === 'detail');\n\nfunction clamp(value: number, max: number): number {\n if (value < 0)\n return 0;\n if (value > max)\n return max;\n return value;\n}\n\nfunction open(): void {\n if (disabled)\n return;\n set(isOpen, true);\n}\n\nfunction close(): void {\n set(isOpen, false);\n}\n\nfunction onInput(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement))\n return;\n set(searchInput, target.value);\n if (!get(isOpen))\n open();\n}\n\nfunction selectItem(item: TItem): void {\n if (readOnly)\n return;\n set(modelValue, getIdentifier(item));\n emit('select', item);\n close();\n}\n\nfunction clearSelection(): void {\n set(modelValue, undefined);\n}\n\nfunction onCategoryClick(category: string | null): void {\n setActiveCategory(category);\n if (get(mode) === 'drill')\n set(drillStep, 'detail');\n else\n focusPane('detail');\n}\n\n/**\n * Moves focus to one of the panes. The focus call prevents scrolling because\n * the panes sit inside a teleported popover, and without it the browser\n * scrolls the page behind to reveal the pane.\n */\nfunction focusPane(pane: 'rail' | 'detail'): void {\n set(focusedPane, pane);\n nextTick(() => {\n (pane === 'rail' ? get(railRef) : get(detailRef))?.focus({ preventScroll: true });\n });\n}\n\nfunction moveRail(direction: 1 | -1): void {\n const entries = get(railEntries);\n if (entries.length === 0)\n return;\n const next = clamp(get(activeRailIndex) + direction, entries.length - 1);\n setActiveCategory(entries[next]?.category ?? null);\n}\n\nfunction moveItem(direction: 1 | -1): void {\n const total = get(detailItems).length;\n if (total === 0)\n return;\n set(highlightedItemIndex, clamp(get(highlightedItemIndex) + direction, total - 1));\n}\n\nfunction enterDetail(): void {\n if (get(detailItems).length === 0)\n return;\n resetHighlightToSelection();\n if (get(mode) === 'drill')\n set(drillStep, 'detail');\n focusPane('detail');\n}\n\nfunction selectHighlighted(): void {\n const item = get(detailItems)[get(highlightedItemIndex)];\n if (item !== undefined)\n selectItem(item);\n}\n\n/**\n * Enter from the field commits the best match for the current query, so a\n * type-to-pick flow (type a few letters, press Enter) works without leaving\n * the input. Does nothing when the field is empty.\n */\nfunction selectTopResult(): void {\n if (!get(searchInput).trim())\n return;\n const item = get(detailItems)[0];\n if (item !== undefined)\n selectItem(item);\n}\n\nfunction resetHighlightToSelection(): void {\n const index = get(detailItems).findIndex(item => isSelected(item));\n set(highlightedItemIndex, index >= 0 ? index : 0);\n}\n\nfunction backToRail(): void {\n if (get(mode) === 'drill')\n set(drillStep, 'category');\n else\n focusPane('rail');\n}\n\nfunction scrollActiveDescendantIntoView(container: HTMLElement | null, id: string): void {\n if (!container)\n return;\n nextTick(() => {\n container.querySelector(`#${id}`)?.scrollIntoView({ block: 'nearest' });\n });\n}\n\n// Reset the item highlight whenever the active category changes.\nwatch(activeCategory, () => {\n resetHighlightToSelection();\n});\n\nwatch(highlightedItemIndex, (index) => {\n scrollActiveDescendantIntoView(get(detailRef), `${baseId}-opt-${index}`);\n});\n\nwatch(activeRailIndex, (index) => {\n if (index >= 0)\n scrollActiveDescendantIntoView(get(railRef), `${baseId}-cat-${index}`);\n});\n\n/**\n * Opening takes focus where the user types next: desktop is a combobox, so\n * focus stays in the field and ArrowDown moves into the panes, while the\n * mobile sheet has no typing field and focuses the rail instead. Closing\n * resets the query, so the field shows the selection again next time, and\n * returns focus to the trigger, which the APG dialog contract asks for.\n */\nfunction onOpenChanged(value: boolean): void {\n if (value) {\n syncActiveToSelection();\n set(focusedPane, 'rail');\n set(drillStep, 'category');\n resetHighlightToSelection();\n nextTick(() => (get(canType) ? get(activatorRef) : get(railRef))?.focus({ preventScroll: true }));\n }\n else {\n set(searchInput, '');\n nextTick(() => get(activatorRef)?.focus({ preventScroll: true }));\n }\n}\n\nwatch(isOpen, onOpenChanged);\n</script>\n\n<template>\n <component\n :is=\"shell\"\n v-model=\"isOpen\"\n v-bind=\"{ ...$attrs, ...shellProps }\"\n >\n <template #activator=\"{ attrs }\">\n <slot\n name=\"activator\"\n v-bind=\"{ attrs, isOpen, item: selectedItem, open, text: selectedText }\"\n >\n <div :class=\"activatorUi.wrapper()\">\n <RuiFieldLabel\n v-if=\"label\"\n :id=\"`${baseId}-label`\"\n :text=\"label\"\n :hidden=\"placement === 'hidden'\"\n :required=\"required\"\n :disabled=\"disabled\"\n />\n <div\n ref=\"fieldRef\"\n role=\"combobox\"\n data-id=\"activator\"\n :class=\"activatorUi.activator()\"\n :aria-labelledby=\"label ? `${baseId}-label` : undefined\"\n aria-haspopup=\"dialog\"\n :aria-controls=\"isOpen ? `${baseId}-panel` : undefined\"\n :aria-expanded=\"isOpen\"\n :aria-disabled=\"disabled || undefined\"\n :aria-readonly=\"(readOnly || !canType) || undefined\"\n :aria-required=\"required || undefined\"\n :aria-invalid=\"hasError || undefined\"\n :aria-busy=\"loading || undefined\"\n v-bind=\"readOnly ? {} : activatorHandlers(attrs)\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n >\n <span\n v-if=\"selectedItem && !isTyping && $slots.selection\"\n data-id=\"selection\"\n :class=\"activatorUi.selection()\"\n >\n <slot\n name=\"selection.prepend\"\n v-bind=\"{ item: selectedItem }\"\n />\n <slot\n name=\"selection\"\n v-bind=\"{ item: selectedItem }\"\n />\n </span>\n <input\n ref=\"activatorRef\"\n data-id=\"search-input\"\n :value=\"isTyping ? searchInput : (($slots.selection && selectedItem) ? '' : (selectedText ?? ''))\"\n :placeholder=\"isTyping ? placeholder : ''\"\n :readonly=\"!canType\"\n :disabled=\"disabled\"\n :aria-invalid=\"hasError || undefined\"\n :aria-labelledby=\"label ? `${baseId}-label` : undefined\"\n class=\"bg-transparent outline-hidden\"\n :class=\"[activatorUi.value(), !canType && 'cursor-pointer']\"\n @input=\"onInput($event)\"\n @keydown.down.prevent=\"focusPane('rail')\"\n @keydown.enter.prevent=\"selectTopResult()\"\n @keydown.esc=\"close()\"\n />\n <span\n v-if=\"showClear\"\n data-id=\"clear\"\n :class=\"[activatorUi.clear(), focused && 'visible!', { 'mr-2': !dense }]\"\n @click.stop.prevent=\"clearSelection()\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"16\"\n />\n </span>\n <span\n data-id=\"chevron\"\n :class=\"activatorUi.iconWrapper()\"\n >\n <RuiIcon\n :class=\"activatorUi.icon()\"\n :size=\"dense ? 16 : 20\"\n name=\"lu-chevron-down\"\n />\n </span>\n <RuiProgress\n v-if=\"loading\"\n :class=\"activatorUi.progress()\"\n color=\"primary\"\n thickness=\"3\"\n variant=\"indeterminate\"\n />\n <fieldset :class=\"activatorUi.fieldset()\" />\n </div>\n <RuiFormTextDetail\n v-if=\"detailsMode !== 'hidden'\"\n :details=\"detailsMode\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n />\n </div>\n </slot>\n </template>\n\n <div\n :id=\"`${baseId}-panel`\"\n :class=\"ui.root({ class: cn(classNames?.root) })\"\n data-id=\"panel\"\n >\n <!-- On desktop the field itself is the search input, so the panel only\n needs its own search box in the mobile sheet. -->\n <div\n v-if=\"isMobile || $slots.header\"\n :class=\"ui.header()\"\n >\n <slot name=\"header\">\n <div :class=\"ui.title()\">\n {{ label }}\n </div>\n </slot>\n <RuiTextField\n v-if=\"searchable && isMobile\"\n v-model=\"searchInput\"\n :class=\"cn(classNames?.search)\"\n :placeholder=\"placeholder\"\n :dense=\"dense\"\n prepend-icon=\"lu-search\"\n details=\"hidden\"\n clearable\n data-id=\"search\"\n />\n </div>\n\n <div :class=\"ui.body()\">\n <!-- Category rail (master) -->\n <div\n v-show=\"showRail\"\n ref=\"railRef\"\n :class=\"ui.rail({ class: cn(classNames?.rail) })\"\n role=\"tablist\"\n aria-orientation=\"vertical\"\n :aria-label=\"label\"\n :aria-activedescendant=\"focusedPane === 'rail' && activeRailIndex >= 0 ? `${baseId}-cat-${activeRailIndex}` : undefined\"\n tabindex=\"0\"\n data-id=\"rail\"\n @focus=\"focusedPane = 'rail'\"\n @keydown.down.prevent=\"moveRail(1)\"\n @keydown.up.prevent=\"moveRail(-1)\"\n @keydown.right.prevent=\"enterDetail()\"\n @keydown.enter.prevent=\"enterDetail()\"\n @keydown.home.prevent=\"setActiveCategory(railEntries[0]?.category ?? null)\"\n @keydown.end.prevent=\"setActiveCategory(railEntries[railEntries.length - 1]?.category ?? null)\"\n >\n <RuiButton\n v-for=\"(entry, index) in railEntries\"\n :id=\"`${baseId}-cat-${index}`\"\n :key=\"entry.category ?? '__all__'\"\n role=\"tab\"\n variant=\"list\"\n :size=\"dense ? 'sm' : undefined\"\n :active=\"entry.category === activeCategory\"\n :aria-selected=\"entry.category === activeCategory\"\n tabindex=\"-1\"\n :data-highlighted=\"focusedPane === 'rail' && index === activeRailIndex\"\n :data-category=\"entry.category ?? '__all__'\"\n @click=\"onCategoryClick(entry.category)\"\n >\n <slot\n name=\"category\"\n v-bind=\"{ active: entry.category === activeCategory, category: entry.category, count: entry.count, label: entry.label }\"\n >\n <span class=\"truncate\">{{ entry.label }}</span>\n <span :class=\"ui.railCount()\">{{ entry.count }}</span>\n </slot>\n </RuiButton>\n </div>\n\n <!-- Detail pane -->\n <div\n v-show=\"showDetail\"\n ref=\"detailRef\"\n :class=\"ui.detail({ class: cn(classNames?.detail) })\"\n role=\"listbox\"\n :aria-label=\"activeCategory ?? allLabel\"\n :aria-activedescendant=\"focusedPane === 'detail' && !isEmpty ? `${baseId}-opt-${highlightedItemIndex}` : undefined\"\n tabindex=\"0\"\n data-id=\"detail\"\n @focus=\"focusedPane = 'detail'\"\n @keydown.down.prevent=\"moveItem(1)\"\n @keydown.up.prevent=\"moveItem(-1)\"\n @keydown.left.prevent=\"backToRail()\"\n @keydown.home.prevent=\"highlightedItemIndex = 0\"\n @keydown.end.prevent=\"highlightedItemIndex = detailItems.length - 1\"\n @keydown.enter.prevent=\"selectHighlighted()\"\n @keydown.space.prevent=\"selectHighlighted()\"\n >\n <template v-if=\"mode === 'drill' && showDetail\">\n <button\n type=\"button\"\n :class=\"ui.backButton()\"\n data-id=\"back\"\n @click=\"backToRail()\"\n >\n <RuiIcon\n name=\"lu-chevron-left\"\n size=\"18\"\n />\n <span>{{ activeCategory ?? allLabel }}</span>\n </button>\n </template>\n\n <slot\n v-if=\"isEmpty\"\n name=\"empty\"\n >\n <div :class=\"ui.empty()\">\n {{ emptyText }}\n </div>\n </slot>\n\n <template\n v-for=\"row in detailRows\"\n v-else\n :key=\"row.type === 'header' ? `h-${row.category}` : `i-${row.index}`\"\n >\n <div\n v-if=\"row.type === 'header'\"\n :class=\"ui.subheader()\"\n role=\"presentation\"\n >\n {{ row.category }}\n </div>\n <RuiButton\n v-else\n :id=\"`${baseId}-opt-${row.index}`\"\n role=\"option\"\n variant=\"list\"\n :size=\"dense ? 'sm' : undefined\"\n :active=\"isSelected(row.item)\"\n :aria-selected=\"isSelected(row.item)\"\n tabindex=\"-1\"\n :data-highlighted=\"focusedPane === 'detail' && row.index === highlightedItemIndex\"\n :class=\"{ [ui.highlighted()]: focusedPane === 'detail' && row.index === highlightedItemIndex && !isSelected(row.item) }\"\n @click=\"selectItem(row.item)\"\n >\n <template\n v-if=\"$slots['item.prepend']\"\n #prepend\n >\n <slot\n name=\"item.prepend\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n >\n {{ getText(row.item) }}\n </slot>\n <template\n v-if=\"$slots['item.append']\"\n #append\n >\n <slot\n name=\"item.append\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n />\n </template>\n </RuiButton>\n </template>\n </div>\n </div>\n\n <div\n v-if=\"$slots.footer\"\n :class=\"ui.footer()\"\n >\n <slot\n name=\"footer\"\n v-bind=\"{ close }\"\n />\n </div>\n </div>\n </component>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"RuiCategoryPicker.vue_vue_type_script_setup_true_lang.js","names":["$attrs","$slots","_useModel","RuiDialog","RuiMenu","_openBlock","_createBlock","_resolveDynamicComponent","_unref","_mergeProps","_isRef","_withCtx","_renderSlot","_normalizeProps","_guardReactiveProps","_createElementVNode","_normalizeClass","RuiFieldLabel","_createCommentVNode","_createElementBlock","_withModifiers","_createVNode","RuiIcon","RuiProgress","RuiFormTextDetail","_toDisplayString","RuiTextField","_withDirectives","_withKeys","_Fragment","_renderList","RuiButton","_vShow","_createSlots","_createTextVNode"],"sources":["../../../../src/components/forms/category-picker/RuiCategoryPicker.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue, TItem\">\nimport type { KeyOfType } from '@/composables/dropdown-menu';\nimport type { VueClassValue } from '@/types/class-value';\nimport { breakpointsTailwind, useBreakpoints } from '@vueuse/core';\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport { categoryPickerActivatorStyles, categoryPickerStyles } from '@/components/forms/category-picker/category-picker-styles';\nimport RuiFieldLabel from '@/components/forms/field-label/RuiFieldLabel.vue';\nimport RuiTextField from '@/components/forms/text-field/RuiTextField.vue';\nimport RuiFormTextDetail from '@/components/helpers/RuiFormTextDetail.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiDialog, { type DialogProps } from '@/components/overlays/dialog/RuiDialog.vue';\nimport { activatorHandlers } from '@/components/overlays/menu/activator-handlers';\nimport RuiMenu from '@/components/overlays/menu/RuiMenu.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { type LabelPlacement, useLabelPlacement } from '@/composables/defaults/field';\nimport { Placement } from '@/composables/floating';\nimport { useCategoryPicker } from '@/composables/forms/category-picker/use-category-picker';\nimport { type FormDetails, useFormDetails, useFormTextDetail } from '@/utils/form-text-detail';\nimport { cn } from '@/utils/tv';\n\nexport interface RuiCategoryPickerClassNames {\n root?: VueClassValue;\n rail?: VueClassValue;\n detail?: VueClassValue;\n search?: VueClassValue;\n}\n\nexport type CategoryPickerBreakpoint = 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\nexport interface RuiCategoryPickerProps<TValue, TItem> {\n /** The full catalogue of selectable items. */\n items: TItem[];\n /** Resolve an item's category label. Takes precedence over `categoryAttr`. */\n categoryOf?: (item: TItem) => string;\n /** Resolve an item's category from one of its own properties. */\n categoryAttr?: keyof TItem;\n /** Property whose value becomes the model value. Omit to use the item itself. */\n keyAttr?: KeyOfType<TItem, TValue extends (infer U)[] ? U : TValue>;\n /** Property that produces an item's display text. */\n textAttr?: keyof TItem;\n /** Field label, and the title of the mobile sheet. */\n label?: string;\n /** Placeholder shown in the field while it is empty or being searched. */\n placeholder?: string;\n /** Allow filtering: type in the field (desktop) or the sheet's search box (mobile). */\n searchable?: boolean;\n /**\n * Custom predicate. Receives the resolved category label as the third\n * argument, matching the `RuiAutoComplete` filter signature.\n */\n filter?: (item: TItem, query: string, category: string) => boolean;\n /** Show the synthetic \"All\" pseudo-category as the first rail entry. */\n showAll?: boolean;\n /** Label for the \"All\" pseudo-category. */\n allLabel?: string;\n /** Text shown when no item matches the current search. */\n emptyText?: string;\n disabled?: boolean;\n readOnly?: boolean;\n dense?: boolean;\n clearable?: boolean;\n loading?: boolean;\n /** Where the label shows; falls back to the nearest `RuiFieldDefaults`, then the app default, then `top`. */\n labelPlacement?: LabelPlacement;\n /** Marks the trigger field as required (renders the asterisk). */\n required?: boolean;\n /** Validation errors rendered under the trigger field. */\n errorMessages?: string | string[];\n /** Success messages rendered under the trigger field. */\n successMessages?: string | string[];\n /** Hint rendered under the trigger field. */\n hint?: string;\n /** The line under the trigger field for its hint and messages: `reserve`, `auto` or `hidden`; falls back to the nearest `RuiFieldDefaults`, then the app default, then `reserve`. */\n details?: FormDetails;\n /** @deprecated Use `details=\"hidden\"`. Removed in 4.0. */\n hideDetails?: boolean;\n /** Below this breakpoint the picker collapses to a single-pane drill-in. */\n mobileBreakpoint?: CategoryPickerBreakpoint;\n /** Forwarded to the mobile bottom-sheet (`RuiDialog`). */\n dialogOptions?: DialogProps;\n classNames?: RuiCategoryPickerClassNames;\n}\n\ndefineOptions({\n name: 'RuiCategoryPicker',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<TValue>();\nconst searchInput = defineModel<string>('search', { default: '' });\n\nconst {\n items,\n categoryOf = undefined,\n categoryAttr = undefined,\n keyAttr = undefined,\n textAttr = undefined,\n label = 'Select',\n placeholder = 'Search',\n searchable = true,\n filter = undefined,\n showAll = true,\n allLabel = 'All',\n emptyText = 'No results',\n disabled = false,\n readOnly = false,\n dense = false,\n clearable = false,\n loading = false,\n labelPlacement = undefined,\n required = false,\n errorMessages = [],\n successMessages = [],\n hint = '',\n details = undefined,\n hideDetails = false,\n mobileBreakpoint = 'md',\n dialogOptions = undefined,\n classNames = undefined,\n} = defineProps<RuiCategoryPickerProps<TValue, TItem>>();\n\nconst emit = defineEmits<{\n select: [item: TItem];\n}>();\n\ndefineSlots<{\n 'activator'?: (props: { attrs: { onClick?: () => void; onMouseover?: () => void; onMouseleave?: () => void }; isOpen: boolean; open: () => void; item: TItem | undefined; text: string | undefined }) => any;\n 'selection'?: (props: { item: TItem }) => any;\n 'selection.prepend'?: (props: { item: TItem }) => any;\n 'category'?: (props: { category: string | null; label: string; active: boolean; count: number }) => any;\n 'item'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'item.prepend'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'item.append'?: (props: { item: TItem; active: boolean; selected: boolean }) => any;\n 'empty'?: () => any;\n 'header'?: () => any;\n 'footer'?: (props: { close: () => void }) => any;\n}>();\n\nconst isOpen = ref<boolean>(false);\nconst focusedPane = ref<'rail' | 'detail'>('rail');\nconst highlightedItemIndex = ref<number>(0);\nconst drillStep = ref<'category' | 'detail'>('category');\n\nconst railRef = useTemplateRef<HTMLDivElement>('railRef');\nconst detailRef = useTemplateRef<HTMLDivElement>('detailRef');\nconst activatorRef = useTemplateRef<HTMLInputElement>('activatorRef');\nconst fieldRef = useTemplateRef<HTMLElement>('fieldRef');\nconst isHovered = ref<boolean>(false);\n\nconst { focused } = useFocus(activatorRef);\nconst { hasError, hasSuccess } = useFormTextDetail(() => errorMessages, () => successMessages);\nconst detailsMode = useFormDetails(() => details, () => hideDetails);\n\nconst baseId = useId();\nconst breakpoints = useBreakpoints(breakpointsTailwind);\n\nfunction resolveCategory(item: TItem): string {\n if (categoryOf)\n return categoryOf(item);\n if (categoryAttr !== undefined)\n return String(item[categoryAttr] ?? '');\n return '';\n}\n\nconst {\n activeCategory,\n detailItems,\n detailRows,\n getIdentifier,\n getText,\n isEmpty,\n isSelected,\n railEntries,\n setActiveCategory,\n syncActiveToSelection,\n} = useCategoryPicker<TValue, TItem>({\n allLabel: () => allLabel,\n categoryOf: resolveCategory,\n filter: () => filter,\n items: () => items,\n keyAttr,\n modelValue,\n search: searchInput,\n searchable: () => searchable,\n showAll: () => showAll,\n textAttr,\n});\n\nconst isMobile = computed<boolean>(() => get(breakpoints.smaller(mobileBreakpoint)));\n\nconst mode = computed<'twoPane' | 'drill'>(() => (get(isMobile) ? 'drill' : 'twoPane'));\n\n/**\n * Desktop anchors an outside-click popover under the field (RuiMenu); narrow\n * width shows a full-width bottom sheet (RuiDialog).\n */\nconst shell = computed<typeof RuiMenu | typeof RuiDialog>(() => (get(isMobile) ? RuiDialog : RuiMenu));\n\n/**\n * The bottom sheet's props. Each shell is given only its own props, so the\n * other component's never leak through as attributes.\n */\nfunction dialogShellProps(): Record<string, unknown> {\n return {\n ariaLabel: dialogOptions?.ariaLabel ?? label,\n bottomSheet: dialogOptions?.bottomSheet ?? true,\n maxWidth: dialogOptions?.maxWidth,\n persistent: dialogOptions?.persistent,\n width: dialogOptions?.width ?? '100%',\n };\n}\n\n/**\n * The popover's props. It anchors to the field box rather than the wrapper, so\n * it sits directly under the input instead of below the reserved details row,\n * and drops the menu's default vertical padding so the popover box equals the\n * panel: the size middleware caps the panel to the available height, and any\n * extra chrome padding would push the popover past the viewport edge. The\n * popover is a dialog, as the activator's `aria-haspopup` says: it holds a\n * tablist and a listbox, which a `menu` may not contain.\n */\nfunction menuShellProps(): Record<string, unknown> {\n return {\n anchorEl: get(fieldRef) ?? undefined,\n classNames: { content: 'py-0' },\n closeOnContentClick: false,\n disableAutoFocus: true,\n fullWidth: true,\n menuLabel: label,\n options: { placement: Placement.bottomStart, size: true },\n persistent: dialogOptions?.persistent,\n persistOnActivatorClick: true,\n role: 'dialog',\n };\n}\n\nconst shellProps = computed<Record<string, unknown>>(() =>\n get(isMobile) ? dialogShellProps() : menuShellProps());\n\nconst ui = computed<ReturnType<typeof categoryPickerStyles>>(() => categoryPickerStyles({\n dense,\n mode: get(mode),\n}));\n\nconst selectedItem = computed<TItem | undefined>(() => {\n const value = get(modelValue);\n if (value === undefined)\n return undefined;\n return items.find(item => getIdentifier(item) === value);\n});\n\nconst selectedText = computed<string | undefined>(() => {\n const selected = get(selectedItem);\n return selected ? getText(selected) : undefined;\n});\n\nconst placement = useLabelPlacement(() => labelPlacement);\n\n/**\n * Desktop lets the user type in the field itself (no separate search box);\n * narrow width keeps a read-only trigger that opens the sheet's own search.\n */\nconst canType = computed<boolean>(() => searchable && !get(isMobile) && !readOnly && !disabled);\n\nconst isTyping = computed<boolean>(() => get(isOpen) && get(canType));\n\n/**\n * Drives both the clear button itself and the room the selection overlay has\n * to leave for it, so the two can never disagree.\n */\nconst showClear = computed<boolean>(() =>\n clearable && get(selectedItem) !== undefined && !disabled && !readOnly);\n\nconst activatorUi = computed<ReturnType<typeof categoryPickerActivatorStyles>>(() => categoryPickerActivatorStyles({\n dense,\n disabled,\n hasError: get(hasError),\n hasSuccess: get(hasSuccess) && !get(hasError),\n hovered: get(isHovered),\n opened: get(isOpen),\n readonly: readOnly,\n withClear: get(showClear),\n}));\n\nconst activeRailIndex = computed<number>(() =>\n get(railEntries).findIndex(entry => entry.category === get(activeCategory)));\n\nconst showRail = computed<boolean>(() => get(mode) === 'twoPane' || get(drillStep) === 'category');\nconst showDetail = computed<boolean>(() => get(mode) === 'twoPane' || get(drillStep) === 'detail');\n\nfunction clamp(value: number, max: number): number {\n if (value < 0)\n return 0;\n if (value > max)\n return max;\n return value;\n}\n\nfunction open(): void {\n if (disabled)\n return;\n set(isOpen, true);\n}\n\nfunction close(): void {\n set(isOpen, false);\n}\n\nfunction onInput(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement))\n return;\n set(searchInput, target.value);\n if (!get(isOpen))\n open();\n}\n\nfunction selectItem(item: TItem): void {\n if (readOnly)\n return;\n set(modelValue, getIdentifier(item));\n emit('select', item);\n close();\n}\n\nfunction clearSelection(): void {\n set(modelValue, undefined);\n}\n\nfunction onCategoryClick(category: string | null): void {\n setActiveCategory(category);\n if (get(mode) === 'drill')\n set(drillStep, 'detail');\n else\n focusPane('detail');\n}\n\n/**\n * Moves focus to one of the panes. The focus call prevents scrolling because\n * the panes sit inside a teleported popover, and without it the browser\n * scrolls the page behind to reveal the pane.\n */\nfunction focusPane(pane: 'rail' | 'detail'): void {\n set(focusedPane, pane);\n nextTick(() => {\n (pane === 'rail' ? get(railRef) : get(detailRef))?.focus({ preventScroll: true });\n });\n}\n\nfunction moveRail(direction: 1 | -1): void {\n const entries = get(railEntries);\n if (entries.length === 0)\n return;\n const next = clamp(get(activeRailIndex) + direction, entries.length - 1);\n setActiveCategory(entries[next]?.category ?? null);\n}\n\nfunction moveItem(direction: 1 | -1): void {\n const total = get(detailItems).length;\n if (total === 0)\n return;\n set(highlightedItemIndex, clamp(get(highlightedItemIndex) + direction, total - 1));\n}\n\nfunction enterDetail(): void {\n if (get(detailItems).length === 0)\n return;\n resetHighlightToSelection();\n if (get(mode) === 'drill')\n set(drillStep, 'detail');\n focusPane('detail');\n}\n\nfunction selectHighlighted(): void {\n const item = get(detailItems)[get(highlightedItemIndex)];\n if (item !== undefined)\n selectItem(item);\n}\n\n/**\n * Enter from the field commits the best match for the current query, so a\n * type-to-pick flow (type a few letters, press Enter) works without leaving\n * the input. Does nothing when the field is empty.\n */\nfunction selectTopResult(): void {\n if (!get(searchInput).trim())\n return;\n const item = get(detailItems)[0];\n if (item !== undefined)\n selectItem(item);\n}\n\n/**\n * Keys in the search field: ArrowDown moves into the rail, Enter picks the top\n * match, Escape closes.\n *\n * An IME that is composing owns all three: the arrows walk its candidate list,\n * Enter confirms a candidate and Escape cancels it. While composing the field\n * leaves them alone, default action included, so neither a pick nor a close\n * lands mid-word.\n */\nfunction onSearchKeydown(event: KeyboardEvent): void {\n if (event.isComposing)\n return;\n\n if (event.key === 'ArrowDown') {\n event.preventDefault();\n focusPane('rail');\n }\n else if (event.key === 'Enter') {\n event.preventDefault();\n selectTopResult();\n }\n else if (event.key === 'Escape') {\n close();\n }\n}\n\nfunction resetHighlightToSelection(): void {\n const index = get(detailItems).findIndex(item => isSelected(item));\n set(highlightedItemIndex, index >= 0 ? index : 0);\n}\n\nfunction backToRail(): void {\n if (get(mode) === 'drill')\n set(drillStep, 'category');\n else\n focusPane('rail');\n}\n\nfunction scrollActiveDescendantIntoView(container: HTMLElement | null, id: string): void {\n if (!container)\n return;\n nextTick(() => {\n container.querySelector(`#${id}`)?.scrollIntoView({ block: 'nearest' });\n });\n}\n\n// Reset the item highlight whenever the active category changes.\nwatch(activeCategory, () => {\n resetHighlightToSelection();\n});\n\nwatch(highlightedItemIndex, (index) => {\n scrollActiveDescendantIntoView(get(detailRef), `${baseId}-opt-${index}`);\n});\n\nwatch(activeRailIndex, (index) => {\n if (index >= 0)\n scrollActiveDescendantIntoView(get(railRef), `${baseId}-cat-${index}`);\n});\n\n/**\n * Opening takes focus where the user types next: desktop is a combobox, so\n * focus stays in the field and ArrowDown moves into the panes, while the\n * mobile sheet has no typing field and focuses the rail instead. Closing\n * resets the query, so the field shows the selection again next time, and\n * returns focus to the trigger, which the APG dialog contract asks for.\n */\nfunction onOpenChanged(value: boolean): void {\n if (value) {\n syncActiveToSelection();\n set(focusedPane, 'rail');\n set(drillStep, 'category');\n resetHighlightToSelection();\n nextTick(() => (get(canType) ? get(activatorRef) : get(railRef))?.focus({ preventScroll: true }));\n }\n else {\n set(searchInput, '');\n nextTick(() => get(activatorRef)?.focus({ preventScroll: true }));\n }\n}\n\nwatch(isOpen, onOpenChanged);\n</script>\n\n<template>\n <component\n :is=\"shell\"\n v-model=\"isOpen\"\n v-bind=\"{ ...$attrs, ...shellProps }\"\n >\n <template #activator=\"{ attrs }\">\n <slot\n name=\"activator\"\n v-bind=\"{ attrs, isOpen, item: selectedItem, open, text: selectedText }\"\n >\n <div :class=\"activatorUi.wrapper()\">\n <RuiFieldLabel\n v-if=\"label\"\n :id=\"`${baseId}-label`\"\n :text=\"label\"\n :hidden=\"placement === 'hidden'\"\n :required=\"required\"\n :disabled=\"disabled\"\n />\n <div\n ref=\"fieldRef\"\n role=\"combobox\"\n data-id=\"activator\"\n :class=\"activatorUi.activator()\"\n :aria-labelledby=\"label ? `${baseId}-label` : undefined\"\n aria-haspopup=\"dialog\"\n :aria-controls=\"isOpen ? `${baseId}-panel` : undefined\"\n :aria-expanded=\"isOpen\"\n :aria-disabled=\"disabled || undefined\"\n :aria-readonly=\"(readOnly || !canType) || undefined\"\n :aria-required=\"required || undefined\"\n :aria-invalid=\"hasError || undefined\"\n :aria-busy=\"loading || undefined\"\n v-bind=\"readOnly ? {} : activatorHandlers(attrs)\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n >\n <span\n v-if=\"selectedItem && !isTyping && $slots.selection\"\n data-id=\"selection\"\n :class=\"activatorUi.selection()\"\n >\n <slot\n name=\"selection.prepend\"\n v-bind=\"{ item: selectedItem }\"\n />\n <slot\n name=\"selection\"\n v-bind=\"{ item: selectedItem }\"\n />\n </span>\n <input\n ref=\"activatorRef\"\n data-id=\"search-input\"\n :value=\"isTyping ? searchInput : (($slots.selection && selectedItem) ? '' : (selectedText ?? ''))\"\n :placeholder=\"isTyping ? placeholder : ''\"\n :readonly=\"!canType\"\n :disabled=\"disabled\"\n :aria-invalid=\"hasError || undefined\"\n :aria-labelledby=\"label ? `${baseId}-label` : undefined\"\n class=\"bg-transparent outline-hidden\"\n :class=\"[activatorUi.value(), !canType && 'cursor-pointer']\"\n @input=\"onInput($event)\"\n @keydown=\"onSearchKeydown($event)\"\n />\n <span\n v-if=\"showClear\"\n data-id=\"clear\"\n :class=\"[activatorUi.clear(), focused && 'visible!', { 'mr-2': !dense }]\"\n @click.stop.prevent=\"clearSelection()\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"16\"\n />\n </span>\n <span\n data-id=\"chevron\"\n :class=\"activatorUi.iconWrapper()\"\n >\n <RuiIcon\n :class=\"activatorUi.icon()\"\n :size=\"dense ? 16 : 20\"\n name=\"lu-chevron-down\"\n />\n </span>\n <RuiProgress\n v-if=\"loading\"\n :class=\"activatorUi.progress()\"\n color=\"primary\"\n thickness=\"3\"\n variant=\"indeterminate\"\n />\n <fieldset :class=\"activatorUi.fieldset()\" />\n </div>\n <RuiFormTextDetail\n v-if=\"detailsMode !== 'hidden'\"\n :details=\"detailsMode\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n />\n </div>\n </slot>\n </template>\n\n <div\n :id=\"`${baseId}-panel`\"\n :class=\"ui.root({ class: cn(classNames?.root) })\"\n data-id=\"panel\"\n >\n <!-- On desktop the field itself is the search input, so the panel only\n needs its own search box in the mobile sheet. -->\n <div\n v-if=\"isMobile || $slots.header\"\n :class=\"ui.header()\"\n >\n <slot name=\"header\">\n <div :class=\"ui.title()\">\n {{ label }}\n </div>\n </slot>\n <RuiTextField\n v-if=\"searchable && isMobile\"\n v-model=\"searchInput\"\n :class=\"cn(classNames?.search)\"\n :placeholder=\"placeholder\"\n :dense=\"dense\"\n prepend-icon=\"lu-search\"\n details=\"hidden\"\n clearable\n data-id=\"search\"\n />\n </div>\n\n <div :class=\"ui.body()\">\n <!-- Category rail (master) -->\n <div\n v-show=\"showRail\"\n ref=\"railRef\"\n :class=\"ui.rail({ class: cn(classNames?.rail) })\"\n role=\"tablist\"\n aria-orientation=\"vertical\"\n :aria-label=\"label\"\n :aria-activedescendant=\"focusedPane === 'rail' && activeRailIndex >= 0 ? `${baseId}-cat-${activeRailIndex}` : undefined\"\n tabindex=\"0\"\n data-id=\"rail\"\n @focus=\"focusedPane = 'rail'\"\n @keydown.down.prevent=\"moveRail(1)\"\n @keydown.up.prevent=\"moveRail(-1)\"\n @keydown.right.prevent=\"enterDetail()\"\n @keydown.enter.prevent=\"enterDetail()\"\n @keydown.home.prevent=\"setActiveCategory(railEntries[0]?.category ?? null)\"\n @keydown.end.prevent=\"setActiveCategory(railEntries[railEntries.length - 1]?.category ?? null)\"\n >\n <RuiButton\n v-for=\"(entry, index) in railEntries\"\n :id=\"`${baseId}-cat-${index}`\"\n :key=\"entry.category ?? '__all__'\"\n role=\"tab\"\n variant=\"list\"\n :size=\"dense ? 'sm' : undefined\"\n :active=\"entry.category === activeCategory\"\n :aria-selected=\"entry.category === activeCategory\"\n tabindex=\"-1\"\n :data-highlighted=\"focusedPane === 'rail' && index === activeRailIndex\"\n :data-category=\"entry.category ?? '__all__'\"\n @click=\"onCategoryClick(entry.category)\"\n >\n <slot\n name=\"category\"\n v-bind=\"{ active: entry.category === activeCategory, category: entry.category, count: entry.count, label: entry.label }\"\n >\n <span class=\"truncate\">{{ entry.label }}</span>\n <span :class=\"ui.railCount()\">{{ entry.count }}</span>\n </slot>\n </RuiButton>\n </div>\n\n <!-- Detail pane -->\n <div\n v-show=\"showDetail\"\n ref=\"detailRef\"\n :class=\"ui.detail({ class: cn(classNames?.detail) })\"\n role=\"listbox\"\n :aria-label=\"activeCategory ?? allLabel\"\n :aria-activedescendant=\"focusedPane === 'detail' && !isEmpty ? `${baseId}-opt-${highlightedItemIndex}` : undefined\"\n tabindex=\"0\"\n data-id=\"detail\"\n @focus=\"focusedPane = 'detail'\"\n @keydown.down.prevent=\"moveItem(1)\"\n @keydown.up.prevent=\"moveItem(-1)\"\n @keydown.left.prevent=\"backToRail()\"\n @keydown.home.prevent=\"highlightedItemIndex = 0\"\n @keydown.end.prevent=\"highlightedItemIndex = detailItems.length - 1\"\n @keydown.enter.prevent=\"selectHighlighted()\"\n @keydown.space.prevent=\"selectHighlighted()\"\n >\n <template v-if=\"mode === 'drill' && showDetail\">\n <button\n type=\"button\"\n :class=\"ui.backButton()\"\n data-id=\"back\"\n @click=\"backToRail()\"\n >\n <RuiIcon\n name=\"lu-chevron-left\"\n size=\"18\"\n />\n <span>{{ activeCategory ?? allLabel }}</span>\n </button>\n </template>\n\n <slot\n v-if=\"isEmpty\"\n name=\"empty\"\n >\n <div :class=\"ui.empty()\">\n {{ emptyText }}\n </div>\n </slot>\n\n <template\n v-for=\"row in detailRows\"\n v-else\n :key=\"row.type === 'header' ? `h-${row.category}` : `i-${row.index}`\"\n >\n <div\n v-if=\"row.type === 'header'\"\n :class=\"ui.subheader()\"\n role=\"presentation\"\n >\n {{ row.category }}\n </div>\n <RuiButton\n v-else\n :id=\"`${baseId}-opt-${row.index}`\"\n role=\"option\"\n variant=\"list\"\n :size=\"dense ? 'sm' : undefined\"\n :active=\"isSelected(row.item)\"\n :aria-selected=\"isSelected(row.item)\"\n tabindex=\"-1\"\n :data-highlighted=\"focusedPane === 'detail' && row.index === highlightedItemIndex\"\n :class=\"{ [ui.highlighted()]: focusedPane === 'detail' && row.index === highlightedItemIndex && !isSelected(row.item) }\"\n @click=\"selectItem(row.item)\"\n >\n <template\n v-if=\"$slots['item.prepend']\"\n #prepend\n >\n <slot\n name=\"item.prepend\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n >\n {{ getText(row.item) }}\n </slot>\n <template\n v-if=\"$slots['item.append']\"\n #append\n >\n <slot\n name=\"item.append\"\n v-bind=\"{ active: row.index === highlightedItemIndex, item: row.item, selected: isSelected(row.item) }\"\n />\n </template>\n </RuiButton>\n </template>\n </div>\n </div>\n\n <div\n v-if=\"$slots.footer\"\n :class=\"ui.footer()\"\n >\n <slot\n name=\"footer\"\n v-bind=\"{ close }\"\n />\n </div>\n </div>\n </component>\n</template>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"RuiMenuSelect.vue_vue_type_script_setup_true_lang.js","names":["$attrs","_useModel","_openBlock","_createBlock","RuiMenu","_mergeProps","_unref","_isRef","_createSlots","_withCtx","_renderSlot","_normalizeProps","_guardReactiveProps","_createElementVNode","_withKeys","_withModifiers","_createElementBlock","_normalizeClass","_createTextVNode","_toDisplayString","_createCommentVNode","_createVNode","RuiIcon","RuiProgress","_normalizeStyle","_Fragment","_renderList","RuiButton","RuiFieldLabel"],"sources":["../../../../src/components/forms/select/RuiMenuSelect.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue, TItem\">\nimport type { VueClassValue } from '@/types/class-value';\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport RuiFieldLabel from '@/components/forms/field-label/RuiFieldLabel.vue';\nimport { menuSelectStyles } from '@/components/forms/select/menu-select-styles';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiMenu, { type MenuProps } from '@/components/overlays/menu/RuiMenu.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { type LabelPlacement, useLabelPlacement } from '@/composables/defaults/field';\nimport { getOptionHeight, type KeyOfType, useDropdownMenu } from '@/composables/dropdown-menu';\nimport { type FloatingOptions, Placement } from '@/composables/floating';\nimport { type FormDetails, useFormDetails, useFormTextDetail } from '@/utils/form-text-detail';\nimport { getNonRootAttrs, getRootAttrs } from '@/utils/helpers';\nimport { cn } from '@/utils/tv';\n\nexport interface RuiMenuSelectClassNames {\n root?: VueClassValue;\n label?: VueClassValue;\n menu?: VueClassValue;\n option?: VueClassValue;\n}\n\nexport interface MenuSelectProps<TValue, TItem> {\n options: TItem[];\n keyAttr?: KeyOfType<TItem, TValue extends Array<infer U> ? U : TValue>;\n textAttr?: keyof TItem;\n disabled?: boolean;\n loading?: boolean;\n readOnly?: boolean;\n dense?: boolean;\n clearable?: boolean;\n label?: string;\n /** Where the label shows; falls back to the nearest `RuiFieldDefaults`, then the app default, then `top`. */\n labelPlacement?: LabelPlacement;\n menuOptions?: MenuProps;\n classNames?: RuiMenuSelectClassNames;\n prependWidth?: number;\n appendWidth?: number;\n itemHeight?: number;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n /** The line under the field for its hint and messages: `reserve`, `auto` or `hidden`; falls back to the nearest `RuiFieldDefaults`, then the app default, then `reserve`. */\n details?: FormDetails;\n /** @deprecated Use `details=\"hidden\"`. Removed in 4.0. */\n hideDetails?: boolean;\n autoSelectFirst?: boolean;\n hideNoData?: boolean;\n noDataText?: string;\n required?: boolean;\n}\n\ndefineOptions({\n name: 'RuiMenuSelect',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<TValue | undefined>({ required: true });\n\nconst {\n options,\n disabled = false,\n loading = false,\n readOnly = false,\n dense = false,\n clearable = false,\n details = undefined,\n hideDetails = false,\n label = 'Select',\n labelPlacement = undefined,\n menuOptions,\n classNames,\n hint,\n keyAttr,\n textAttr,\n itemHeight,\n prependWidth,\n appendWidth,\n errorMessages = [],\n successMessages = [],\n autoSelectFirst = false,\n hideNoData = false,\n noDataText = 'No data available',\n required = false,\n} = defineProps<MenuSelectProps<TValue, TItem>>();\n\ndefineSlots<{\n 'activator'?: (props: {\n disabled: boolean;\n value: TItem | undefined;\n readOnly: boolean;\n attrs: Record<string, unknown>;\n open: boolean;\n hasError: boolean;\n hasSuccess: boolean;\n }) => any;\n 'selection.prepend'?: (props: { item: TItem }) => any;\n 'selection'?: (props: { item: TItem }) => any;\n 'item.prepend'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'item'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'item.append'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'no-data'?: () => any;\n}>();\n\nconst menuRef = useTemplateRef<HTMLDivElement>('menuRef');\nconst activator = useTemplateRef<HTMLDivElement>('activator');\nconst { focused } = useFocus(activator);\nconst isHovered = ref<boolean>(false);\nconst labelId = useId();\n\nconst placement = useLabelPlacement(() => labelPlacement);\n\nconst { hasError, hasSuccess } = useFormTextDetail(\n () => errorMessages,\n () => successMessages,\n);\nconst detailsMode = useFormDetails(() => details, () => hideDetails);\n\nconst value = computed<TItem | undefined>({\n get: () => {\n const value = get(modelValue);\n if (keyAttr)\n return options.find(option => option[keyAttr] === value);\n // eslint-disable-next-line slop/no-chained-type-assertions -- without keyAttr the model holds the item itself, which the two generics cannot express\n return value as unknown as TItem;\n },\n set: (selected?: TItem) => {\n const selection = keyAttr && selected ? selected[keyAttr] : selected;\n set(modelValue, selection as TValue);\n },\n});\n\nfunction setValue(val: TItem): void {\n set(value, val);\n set(focused, true);\n}\n\nconst {\n containerProps,\n wrapperProps,\n renderedData,\n isOpen,\n menuWidth,\n getText,\n getIdentifier,\n isActiveItem,\n modelHighlightedIndex,\n moveHighlight,\n applyHighlighted,\n valueKey,\n} = useDropdownMenu<TValue, TItem>({\n itemHeight: itemHeight ?? getOptionHeight(dense),\n keyAttr,\n textAttr,\n options: () => options,\n dense: () => dense,\n value,\n menuRef,\n disabled: () => disabled,\n autoSelectFirst,\n setValue,\n prependWidth,\n appendWidth,\n});\n\nconst ui = computed<ReturnType<typeof menuSelectStyles>>(() => menuSelectStyles({\n opened: get(isOpen),\n hovered: get(isHovered),\n dense,\n disabled,\n readonly: readOnly,\n hasError: get(hasError),\n hasSuccess: get(hasSuccess) && !get(hasError),\n}));\n\nconst highlightedClass = menuSelectStyles({}).highlighted();\n\nfunction clear(): void {\n set(modelValue, undefined);\n}\n\nconst menuFloatingOptions = computed<FloatingOptions>(() => ({\n placement: Placement.bottomStart,\n ...menuOptions?.options,\n}));\n</script>\n\n<template>\n <RuiMenu\n v-model=\"isOpen\"\n v-bind=\"{ ...getRootAttrs($attrs, []), ...menuOptions }\"\n :class=\"ui.wrapper({ class: cn($attrs.class) })\"\n :class-names=\"{ ...menuOptions?.classNames, details: ['px-0', cn(menuOptions?.classNames?.details) ?? ''] }\"\n :options=\"menuFloatingOptions\"\n close-on-content-click\n full-width\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n :dense=\"dense\"\n :show-details=\"detailsMode !== 'hidden'\"\n :details=\"detailsMode === 'auto' ? 'auto' : 'reserve'\"\n :disabled=\"disabled\"\n disable-auto-focus\n role=\"listbox\"\n >\n <template\n v-if=\"label\"\n #label\n >\n <RuiFieldLabel\n :id=\"labelId\"\n :text=\"label\"\n :hidden=\"placement === 'hidden'\"\n :required=\"required\"\n :disabled=\"disabled\"\n />\n </template>\n <template #activator=\"{ attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\">\n <slot\n name=\"activator\"\n v-bind=\"{ disabled, value, readOnly, attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\"\n >\n <!-- a select-only combobox: the button opens a listbox of options, and carries the field's state -->\n <button\n ref=\"activator\"\n role=\"combobox\"\n :disabled=\"disabled\"\n :aria-disabled=\"disabled\"\n :aria-expanded=\"isOpen\"\n :aria-readonly=\"readOnly || undefined\"\n :aria-required=\"required || undefined\"\n :aria-busy=\"loading || undefined\"\n :aria-labelledby=\"label ? labelId : undefined\"\n type=\"button\"\n :tabindex=\"disabled || readOnly ? -1 : 0\"\n :class=\"ui.activator({ class: cn(classNames?.label) })\"\n v-bind=\"{\n ...getNonRootAttrs($attrs),\n ...(readOnly ? {} : attrs),\n }\"\n data-id=\"activator\"\n :aria-invalid=\"hasError\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n @keydown.enter.prevent=\"applyHighlighted()\"\n @keydown.space.prevent=\"applyHighlighted()\"\n @keydown.home.prevent=\"modelHighlightedIndex = 0\"\n @keydown.end.prevent=\"modelHighlightedIndex = options.length - 1\"\n >\n <span\n v-if=\"value\"\n :class=\"ui.value()\"\n >\n <slot\n name=\"selection.prepend\"\n v-bind=\"{ item: value }\"\n />\n <slot\n name=\"selection\"\n v-bind=\"{ item: value }\"\n >\n {{ getText(value) }}\n </slot>\n </span>\n\n <span\n v-if=\"clearable && value && !disabled && !readOnly\"\n data-id=\"clear\"\n :class=\"[ui.clear(), focused && 'visible!', { 'mr-2': !dense }]\"\n @click.stop.prevent=\"clear()\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"16\"\n />\n </span>\n\n <span :class=\"ui.iconWrapper()\">\n <RuiIcon\n :class=\"ui.icon()\"\n :size=\"dense ? 16 : 20\"\n name=\"lu-chevron-down\"\n />\n </span>\n\n <RuiProgress\n v-if=\"loading\"\n :class=\"ui.progress()\"\n color=\"primary\"\n thickness=\"2\"\n variant=\"indeterminate\"\n />\n </button>\n <fieldset :class=\"ui.fieldset()\" />\n </slot>\n <input\n :value=\"valueKey\"\n class=\"hidden\"\n type=\"hidden\"\n />\n </template>\n <template #default=\"{ width }\">\n <div\n v-if=\"options.length > 0\"\n :ref=\"containerProps.ref\"\n :class=\"ui.menu({ class: cn(classNames?.menu) })\"\n :style=\"[containerProps.style, { width: `${width}px`, minWidth: menuWidth }]\"\n @scroll=\"containerProps.onScroll\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n >\n <div\n v-bind=\"wrapperProps\"\n ref=\"menuRef\"\n >\n <RuiButton\n v-for=\"{ item, _index } in renderedData\"\n :key=\"getIdentifier(item)\"\n :active=\"isActiveItem(item)\"\n role=\"option\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n variant=\"list\"\n :data-highlighted=\"modelHighlightedIndex === _index\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && modelHighlightedIndex === _index,\n }\"\n @click=\"setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled, item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled, item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled, item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </div>\n </div>\n\n <div\n v-else-if=\"!hideNoData\"\n data-id=\"no-data\"\n :style=\"{ width: `${width}px`, minWidth: menuWidth }\"\n :class=\"classNames?.menu\"\n >\n <slot name=\"no-data\">\n <div class=\"p-4\">\n {{ noDataText }}\n </div>\n </slot>\n </div>\n </template>\n </RuiMenu>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"RuiMenuSelect.vue_vue_type_script_setup_true_lang.js","names":["$attrs","_useModel","_openBlock","_createBlock","RuiMenu","_mergeProps","_unref","_isRef","_createSlots","_withCtx","_renderSlot","_normalizeProps","_guardReactiveProps","_createElementVNode","_withKeys","_withModifiers","_createElementBlock","_normalizeClass","_createTextVNode","_toDisplayString","_createCommentVNode","_createVNode","RuiIcon","RuiProgress","_normalizeStyle","_Fragment","_renderList","RuiButton","RuiFieldLabel"],"sources":["../../../../src/components/forms/select/RuiMenuSelect.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"TValue, TItem\">\nimport type { VueClassValue } from '@/types/class-value';\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport RuiFieldLabel from '@/components/forms/field-label/RuiFieldLabel.vue';\nimport { menuSelectStyles } from '@/components/forms/select/menu-select-styles';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiMenu, { type MenuProps } from '@/components/overlays/menu/RuiMenu.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { type LabelPlacement, useLabelPlacement } from '@/composables/defaults/field';\nimport { getOptionHeight, type KeyOfType, useDropdownMenu } from '@/composables/dropdown-menu';\nimport { type FloatingOptions, Placement } from '@/composables/floating';\nimport { type FormDetails, useFormDetails, useFormTextDetail } from '@/utils/form-text-detail';\nimport { getNonRootAttrs, getRootAttrs } from '@/utils/helpers';\nimport { cn } from '@/utils/tv';\n\nexport interface RuiMenuSelectClassNames {\n root?: VueClassValue;\n label?: VueClassValue;\n menu?: VueClassValue;\n option?: VueClassValue;\n}\n\nexport interface MenuSelectProps<TValue, TItem> {\n options: TItem[];\n keyAttr?: KeyOfType<TItem, TValue extends Array<infer U> ? U : TValue>;\n textAttr?: keyof TItem;\n disabled?: boolean;\n loading?: boolean;\n readOnly?: boolean;\n dense?: boolean;\n clearable?: boolean;\n label?: string;\n /** Where the label shows; falls back to the nearest `RuiFieldDefaults`, then the app default, then `top`. */\n labelPlacement?: LabelPlacement;\n menuOptions?: MenuProps;\n classNames?: RuiMenuSelectClassNames;\n prependWidth?: number;\n appendWidth?: number;\n itemHeight?: number;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n /** The line under the field for its hint and messages: `reserve`, `auto` or `hidden`; falls back to the nearest `RuiFieldDefaults`, then the app default, then `reserve`. */\n details?: FormDetails;\n /** @deprecated Use `details=\"hidden\"`. Removed in 4.0. */\n hideDetails?: boolean;\n autoSelectFirst?: boolean;\n hideNoData?: boolean;\n noDataText?: string;\n required?: boolean;\n}\n\ndefineOptions({\n name: 'RuiMenuSelect',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<TValue | undefined>({ required: true });\n\nconst {\n options,\n disabled = false,\n loading = false,\n readOnly = false,\n dense = false,\n clearable = false,\n details = undefined,\n hideDetails = false,\n label = 'Select',\n labelPlacement = undefined,\n menuOptions,\n classNames,\n hint,\n keyAttr,\n textAttr,\n itemHeight,\n prependWidth,\n appendWidth,\n errorMessages = [],\n successMessages = [],\n autoSelectFirst = false,\n hideNoData = false,\n noDataText = 'No data available',\n required = false,\n} = defineProps<MenuSelectProps<TValue, TItem>>();\n\ndefineSlots<{\n 'activator'?: (props: {\n disabled: boolean;\n value: TItem | undefined;\n readOnly: boolean;\n attrs: Record<string, unknown>;\n open: boolean;\n hasError: boolean;\n hasSuccess: boolean;\n }) => any;\n 'selection.prepend'?: (props: { item: TItem }) => any;\n 'selection'?: (props: { item: TItem }) => any;\n 'item.prepend'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'item'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'item.append'?: (props: { disabled: boolean; item: TItem; active: boolean }) => any;\n 'no-data'?: () => any;\n}>();\n\nconst menuRef = useTemplateRef<HTMLDivElement>('menuRef');\nconst activator = useTemplateRef<HTMLDivElement>('activator');\nconst { focused } = useFocus(activator);\nconst isHovered = ref<boolean>(false);\nconst labelId = useId();\n\nconst placement = useLabelPlacement(() => labelPlacement);\n\nconst { hasError, hasSuccess } = useFormTextDetail(\n () => errorMessages,\n () => successMessages,\n);\nconst detailsMode = useFormDetails(() => details, () => hideDetails);\n\nconst value = computed<TItem | undefined>({\n get: () => {\n const value = get(modelValue);\n if (keyAttr)\n return options.find(option => option[keyAttr] === value);\n // eslint-disable-next-line slop/no-chained-type-assertions -- without keyAttr the model holds the item itself, which the two generics cannot express\n return value as unknown as TItem;\n },\n set: (selected?: TItem) => {\n const selection = keyAttr && selected ? selected[keyAttr] : selected;\n set(modelValue, selection as TValue);\n },\n});\n\nfunction setValue(val: TItem): void {\n set(value, val);\n set(focused, true);\n}\n\nconst {\n containerProps,\n wrapperProps,\n renderedData,\n isOpen,\n menuWidth,\n getText,\n getIdentifier,\n isActiveItem,\n modelHighlightedIndex,\n moveHighlight,\n applyHighlighted,\n valueKey,\n} = useDropdownMenu<TValue, TItem>({\n itemHeight: itemHeight ?? getOptionHeight(dense),\n keyAttr,\n textAttr,\n options: () => options,\n dense: () => dense,\n value,\n menuRef,\n disabled: () => disabled,\n autoSelectFirst,\n setValue,\n prependWidth,\n appendWidth,\n});\n\nconst ui = computed<ReturnType<typeof menuSelectStyles>>(() => menuSelectStyles({\n opened: get(isOpen),\n hovered: get(isHovered),\n dense,\n disabled,\n readonly: readOnly,\n hasError: get(hasError),\n hasSuccess: get(hasSuccess) && !get(hasError),\n}));\n\nconst highlightedClass = menuSelectStyles({}).highlighted();\n\nfunction clear(): void {\n set(modelValue, undefined);\n}\n\nconst menuFloatingOptions = computed<FloatingOptions>(() => ({\n placement: Placement.bottomStart,\n ...menuOptions?.options,\n}));\n</script>\n\n<template>\n <RuiMenu\n v-model=\"isOpen\"\n v-bind=\"{ ...getRootAttrs($attrs, []), ...menuOptions }\"\n :class=\"ui.wrapper({ class: cn($attrs.class) })\"\n :class-names=\"{ ...menuOptions?.classNames, details: ['px-0', cn(menuOptions?.classNames?.details) ?? ''] }\"\n :options=\"menuFloatingOptions\"\n close-on-content-click\n full-width\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n :dense=\"dense\"\n :show-details=\"detailsMode !== 'hidden'\"\n :details=\"detailsMode === 'auto' ? 'auto' : 'reserve'\"\n :disabled=\"disabled\"\n disable-auto-focus\n role=\"listbox\"\n >\n <template\n v-if=\"label\"\n #label\n >\n <RuiFieldLabel\n :id=\"labelId\"\n :text=\"label\"\n :hidden=\"placement === 'hidden'\"\n :required=\"required\"\n :disabled=\"disabled\"\n />\n </template>\n <template #activator=\"{ attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\">\n <slot\n name=\"activator\"\n v-bind=\"{ disabled, value, readOnly, attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\"\n >\n <!-- a select-only combobox: the button opens a listbox of options, and carries the field's state -->\n <button\n ref=\"activator\"\n role=\"combobox\"\n :disabled=\"disabled\"\n :aria-disabled=\"disabled\"\n :aria-expanded=\"isOpen\"\n :aria-readonly=\"readOnly || undefined\"\n :aria-required=\"required || undefined\"\n :aria-busy=\"loading || undefined\"\n :aria-labelledby=\"label ? labelId : undefined\"\n type=\"button\"\n :tabindex=\"disabled || readOnly ? -1 : 0\"\n :class=\"ui.activator({ class: cn(classNames?.label) })\"\n v-bind=\"{\n ...getNonRootAttrs($attrs),\n ...(readOnly ? {} : attrs),\n }\"\n data-id=\"activator\"\n :aria-invalid=\"hasError\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n @keydown.enter.prevent=\"applyHighlighted()\"\n @keydown.space.prevent=\"applyHighlighted()\"\n @keydown.home.prevent=\"modelHighlightedIndex = 0\"\n @keydown.end.prevent=\"modelHighlightedIndex = options.length - 1\"\n >\n <span\n v-if=\"value\"\n :class=\"ui.value()\"\n >\n <slot\n name=\"selection.prepend\"\n v-bind=\"{ item: value }\"\n />\n <slot\n name=\"selection\"\n v-bind=\"{ item: value }\"\n >\n {{ getText(value) }}\n </slot>\n </span>\n\n <span\n v-if=\"clearable && value && !disabled && !readOnly\"\n data-id=\"clear\"\n :class=\"[ui.clear(), focused && 'visible!', { 'mr-2': !dense }]\"\n @click.stop.prevent=\"clear()\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"16\"\n />\n </span>\n\n <span :class=\"ui.iconWrapper()\">\n <RuiIcon\n :class=\"ui.icon()\"\n :size=\"dense ? 16 : 20\"\n name=\"lu-chevron-down\"\n />\n </span>\n\n <RuiProgress\n v-if=\"loading\"\n :class=\"ui.progress()\"\n color=\"primary\"\n thickness=\"2\"\n variant=\"indeterminate\"\n />\n </button>\n <fieldset :class=\"ui.fieldset()\" />\n </slot>\n <input\n :value=\"valueKey\"\n class=\"hidden\"\n type=\"hidden\"\n />\n </template>\n <template #default=\"{ width }\">\n <div\n v-if=\"options.length > 0\"\n :ref=\"containerProps.ref\"\n :class=\"ui.menu({ class: cn(classNames?.menu) })\"\n :style=\"[containerProps.style, { width: `${width}px`, minWidth: menuWidth }]\"\n @scroll=\"containerProps.onScroll\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n >\n <div\n v-bind=\"wrapperProps\"\n ref=\"menuRef\"\n >\n <RuiButton\n v-for=\"{ item, _index } in renderedData\"\n :key=\"getIdentifier(item)\"\n :active=\"isActiveItem(item)\"\n role=\"option\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n variant=\"list\"\n :data-highlighted=\"modelHighlightedIndex === _index\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && modelHighlightedIndex === _index,\n }\"\n @click=\"setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled, item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled, item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled, item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </div>\n </div>\n\n <div\n v-else-if=\"!hideNoData\"\n data-id=\"no-data\"\n :style=\"{ width: `${width}px`, minWidth: menuWidth }\"\n :class=\"classNames?.menu\"\n >\n <slot name=\"no-data\">\n <div class=\"p-4\">\n {{ noDataText }}\n </div>\n </slot>\n </div>\n </template>\n </RuiMenu>\n</template>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"RuiDataTableBody.js","names":["_sfc_main"],"sources":["../../../../src/components/tables/data-table/RuiDataTableBody.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object\">\nimport type { TableColumn } from '@/components/tables/RuiTableHead.vue';\nimport { useDataTableColumns, useDataTableRowIdentity, useDataTableStyling } from '@/components/tables/data-table/context';\nimport RuiDataTableEmptyRow from '@/components/tables/data-table/RuiDataTableEmptyRow.vue';\nimport RuiDataTableErrorRow from '@/components/tables/data-table/RuiDataTableErrorRow.vue';\nimport RuiDataTableGroupRow from '@/components/tables/data-table/RuiDataTableGroupRow.vue';\nimport RuiDataTableLoadingRow from '@/components/tables/data-table/RuiDataTableLoadingRow.vue';\nimport RuiDataTableRow from '@/components/tables/data-table/RuiDataTableRow.vue';\nimport { type GroupHeader, isRow } from '@/composables/tables/data-table/types';\n\nconst { error, loading, noData } = defineProps<{\n filtered: (T | GroupHeader<T>)[];\n loading: boolean;\n noData: boolean;\n empty: { label?: string; description?: string };\n error: string;\n errorTitle: string;\n retryText: string;\n}>();\n\nconst emit = defineEmits<{\n retry: [];\n}>();\n\ndefineSlots<Partial<\n Record<`item.${string}`, (props: { column: TableColumn<T>; row: T; index: number }) => any> & {\n 'body.prepend'?: (props: { colspan: number }) => any;\n 'body.append'?: (props: { colspan: number }) => any;\n 'group.header'?: (props: {\n colspan: number;\n header: GroupHeader<T>;\n isOpen: boolean;\n toggle: () => void;\n }) => any;\n 'group.header.content'?: (props: { header: GroupHeader<T>; groupKey: string }) => any;\n 'expanded-item'?: (props: { row: T; index: number }) => any;\n 'row-append'?: (props: { row: T; index: number }) => any;\n 'no-data'?: () => any;\n 'empty-description'?: () => any;\n 'error'?: () => any;\n }\n>>();\n\nconst { classes, colspan } = useDataTableStyling();\nconst { itemSlotKeys } = useDataTableColumns<T>();\nconst { getRowId } = useDataTableRowIdentity<T>();\n\n/**\n * Loading outranks a failure, which outranks the empty state: rows that did\n * not arrive are not rows that are not there. Rows kept from an earlier read\n * stay below the error.\n */\nconst showError = computed<boolean>(() => !!error && !loading);\n\n/**\n * Rows shown while a read is in flight are the previous result. They stay at\n * full strength and usable, since a refresh can run for minutes; the header\n * bar marks the read, and `data-stale` lets a consumer style them.\n */\nconst stale = computed<boolean>(() => loading && !noData);\n</script>\n\n<template>\n <tbody\n :class=\"classes.tbody\"\n :data-stale=\"stale || undefined\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n v-if=\"$slots['body.prepend']\"\n :colspan=\"colspan\"\n name=\"body.prepend\"\n />\n <RuiDataTableErrorRow\n v-if=\"showError\"\n :message=\"error\"\n :title=\"errorTitle\"\n :retry-text=\"retryText\"\n @retry=\"emit('retry')\"\n >\n <template\n v-if=\"$slots.error\"\n #error\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"error\" />\n </template>\n </RuiDataTableErrorRow>\n <template v-for=\"(row, index) in filtered\">\n <RuiDataTableGroupRow\n v-if=\"!isRow(row)\"\n :key=\"`group-${row.identifier}`\"\n :row=\"row\"\n >\n <template\n v-if=\"$slots['group.header']\"\n #group.header=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"group.header\"\n v-bind=\"slotData\"\n />\n </template>\n <template\n v-if=\"$slots['group.header.content']\"\n #group.header.content=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"group.header.content\"\n v-bind=\"slotData\"\n />\n </template>\n </RuiDataTableGroupRow>\n <RuiDataTableRow\n v-else\n :key=\"`row-${getRowId(row)}`\"\n :row=\"row\"\n :index=\"index\"\n >\n <template\n v-for=\"key in itemSlotKeys\"\n #[`item.${key}`]=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n :name=\"`item.${key}`\"\n v-bind=\"slotData\"\n />\n </template>\n <template\n v-if=\"$slots['expanded-item']\"\n #expanded-item=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"expanded-item\"\n v-bind=\"slotData\"\n />\n </template>\n <template\n v-if=\"$slots['row-append']\"\n #row-append=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"row-append\"\n v-bind=\"slotData\"\n />\n </template>\n </RuiDataTableRow>\n </template>\n <RuiDataTableLoadingRow v-if=\"loading && noData\" />\n <RuiDataTableEmptyRow\n v-if=\"noData && empty && !loading && !showError\"\n :empty=\"empty\"\n >\n <template\n v-if=\"$slots['no-data']\"\n #no-data\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"no-data\" />\n </template>\n <template\n v-if=\"$slots['empty-description']\"\n #empty-description\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"empty-description\" />\n </template>\n </RuiDataTableEmptyRow>\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n v-if=\"$slots['body.append']\"\n :colspan=\"colspan\"\n name=\"body.append\"\n />\n </tbody>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"RuiDataTableBody.js","names":["_sfc_main"],"sources":["../../../../src/components/tables/data-table/RuiDataTableBody.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object\">\nimport type { TableColumn } from '@/components/tables/RuiTableHead.vue';\nimport { useDataTableColumns, useDataTableRowIdentity, useDataTableStyling } from '@/components/tables/data-table/context';\nimport RuiDataTableEmptyRow from '@/components/tables/data-table/RuiDataTableEmptyRow.vue';\nimport RuiDataTableErrorRow from '@/components/tables/data-table/RuiDataTableErrorRow.vue';\nimport RuiDataTableGroupRow from '@/components/tables/data-table/RuiDataTableGroupRow.vue';\nimport RuiDataTableLoadingRow from '@/components/tables/data-table/RuiDataTableLoadingRow.vue';\nimport RuiDataTableRow from '@/components/tables/data-table/RuiDataTableRow.vue';\nimport { type GroupHeader, isRow } from '@/composables/tables/data-table/types';\n\nconst { error, loading, noData } = defineProps<{\n filtered: (T | GroupHeader<T>)[];\n loading: boolean;\n noData: boolean;\n empty: { label?: string; description?: string };\n error: string;\n errorTitle: string;\n retryText: string;\n}>();\n\nconst emit = defineEmits<{\n retry: [];\n}>();\n\ndefineSlots<Partial<\n Record<`item.${string}`, (props: { column: TableColumn<T>; row: T; index: number }) => any> & {\n 'body.prepend'?: (props: { colspan: number }) => any;\n 'body.append'?: (props: { colspan: number }) => any;\n 'group.header'?: (props: {\n colspan: number;\n header: GroupHeader<T>;\n isOpen: boolean;\n toggle: () => void;\n }) => any;\n 'group.header.content'?: (props: { header: GroupHeader<T>; groupKey: string }) => any;\n 'expanded-item'?: (props: { row: T; index: number }) => any;\n 'row-append'?: (props: { row: T; index: number }) => any;\n 'no-data'?: () => any;\n 'empty-description'?: () => any;\n 'error'?: () => any;\n }\n>>();\n\nconst { classes, colspan } = useDataTableStyling();\nconst { itemSlotKeys } = useDataTableColumns<T>();\nconst { getRowId } = useDataTableRowIdentity<T>();\n\n/**\n * Loading outranks a failure, which outranks the empty state: rows that did\n * not arrive are not rows that are not there. Rows kept from an earlier read\n * stay below the error.\n */\nconst showError = computed<boolean>(() => !!error && !loading);\n\n/**\n * Rows shown while a read is in flight are the previous result. They stay at\n * full strength and usable, since a refresh can run for minutes; the header\n * bar marks the read, and `data-stale` lets a consumer style them.\n */\nconst stale = computed<boolean>(() => loading && !noData);\n\n/**\n * Keys each entry of the row loop, so a row keeps its DOM and component state while the rows\n * around it change. Without it the loop patches entries by position, and a list that shifts by one\n * row (a virtual window, a row added on top) rebuilds every row below the change.\n */\nfunction getEntryKey(row: T | GroupHeader<T>): string {\n return isRow(row) ? `row-${getRowId(row)}` : `group-${row.identifier}`;\n}\n</script>\n\n<template>\n <tbody\n :class=\"classes.tbody\"\n :data-stale=\"stale || undefined\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n v-if=\"$slots['body.prepend']\"\n :colspan=\"colspan\"\n name=\"body.prepend\"\n />\n <RuiDataTableErrorRow\n v-if=\"showError\"\n :message=\"error\"\n :title=\"errorTitle\"\n :retry-text=\"retryText\"\n @retry=\"emit('retry')\"\n >\n <template\n v-if=\"$slots.error\"\n #error\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"error\" />\n </template>\n </RuiDataTableErrorRow>\n <template\n v-for=\"(row, index) in filtered\"\n :key=\"getEntryKey(row)\"\n >\n <RuiDataTableGroupRow\n v-if=\"!isRow(row)\"\n :row=\"row\"\n >\n <template\n v-if=\"$slots['group.header']\"\n #group.header=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"group.header\"\n v-bind=\"slotData\"\n />\n </template>\n <template\n v-if=\"$slots['group.header.content']\"\n #group.header.content=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"group.header.content\"\n v-bind=\"slotData\"\n />\n </template>\n </RuiDataTableGroupRow>\n <RuiDataTableRow\n v-else\n :row=\"row\"\n :index=\"index\"\n >\n <template\n v-for=\"key in itemSlotKeys\"\n #[`item.${key}`]=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n :name=\"`item.${key}`\"\n v-bind=\"slotData\"\n />\n </template>\n <template\n v-if=\"$slots['expanded-item']\"\n #expanded-item=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"expanded-item\"\n v-bind=\"slotData\"\n />\n </template>\n <template\n v-if=\"$slots['row-append']\"\n #row-append=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"row-append\"\n v-bind=\"slotData\"\n />\n </template>\n </RuiDataTableRow>\n </template>\n <RuiDataTableLoadingRow v-if=\"loading && noData\" />\n <RuiDataTableEmptyRow\n v-if=\"noData && empty && !loading && !showError\"\n :empty=\"empty\"\n >\n <template\n v-if=\"$slots['no-data']\"\n #no-data\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"no-data\" />\n </template>\n <template\n v-if=\"$slots['empty-description']\"\n #empty-description\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"empty-description\" />\n </template>\n </RuiDataTableEmptyRow>\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n v-if=\"$slots['body.append']\"\n :colspan=\"colspan\"\n name=\"body.append\"\n />\n </tbody>\n</template>\n"],"mappings":""}
@@ -37,6 +37,14 @@ var RuiDataTableBody_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
37
37
  * bar marks the read, and `data-stale` lets a consumer style them.
38
38
  */
39
39
  const stale = computed(() => __props.loading && !__props.noData);
40
+ /**
41
+ * Keys each entry of the row loop, so a row keeps its DOM and component state while the rows
42
+ * around it change. Without it the loop patches entries by position, and a list that shifts by one
43
+ * row (a virtual window, a row added on top) rebuilds every row below the change.
44
+ */
45
+ function getEntryKey(row) {
46
+ return isRow(row) ? `row-${getRowId(row)}` : `group-${row.identifier}`;
47
+ }
40
48
  return (_ctx, _cache) => {
41
49
  return openBlock(), createElementBlock("tbody", {
42
50
  class: normalizeClass(unref(classes).tbody),
@@ -59,8 +67,8 @@ var RuiDataTableBody_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
59
67
  "retry-text"
60
68
  ])) : createCommentVNode("", true),
61
69
  (openBlock(true), createElementBlock(Fragment, null, renderList(__props.filtered, (row, index) => {
62
- return openBlock(), createElementBlock(Fragment, null, [!unref(isRow)(row) ? (openBlock(), createBlock(RuiDataTableGroupRow_default, {
63
- key: `group-${row.identifier}`,
70
+ return openBlock(), createElementBlock(Fragment, { key: getEntryKey(row) }, [!unref(isRow)(row) ? (openBlock(), createBlock(RuiDataTableGroupRow_default, {
71
+ key: 0,
64
72
  row
65
73
  }, createSlots({ _: 2 }, [_ctx.$slots["group.header"] ? {
66
74
  name: "group.header",
@@ -71,7 +79,7 @@ var RuiDataTableBody_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
71
79
  fn: withCtx((slotData) => [renderSlot(_ctx.$slots, "group.header.content", mergeProps({ ref_for: true }, slotData))]),
72
80
  key: "1"
73
81
  } : void 0]), 1032, ["row"])) : (openBlock(), createBlock(RuiDataTableRow_default, {
74
- key: `row-${unref(getRowId)(row)}`,
82
+ key: 1,
75
83
  row,
76
84
  index
77
85
  }, createSlots({ _: 2 }, [
@@ -92,7 +100,7 @@ var RuiDataTableBody_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
92
100
  key: "1"
93
101
  } : void 0
94
102
  ]), 1032, ["row", "index"]))], 64);
95
- }), 256)),
103
+ }), 128)),
96
104
  __props.loading && __props.noData ? (openBlock(), createBlock(RuiDataTableLoadingRow_default, { key: 2 })) : createCommentVNode("", true),
97
105
  __props.noData && __props.empty && !__props.loading && !unref(showError) ? (openBlock(), createBlock(RuiDataTableEmptyRow_default, {
98
106
  key: 3,
@@ -1 +1 @@
1
- {"version":3,"file":"RuiDataTableBody.vue_vue_type_script_setup_true_lang.js","names":["$slots","_openBlock","_createElementBlock","_normalizeClass","_unref","_renderSlot","_createCommentVNode","_createBlock","RuiDataTableErrorRow","_createSlots","_withCtx","_Fragment","_renderList","RuiDataTableGroupRow","_mergeProps","RuiDataTableRow","RuiDataTableLoadingRow","RuiDataTableEmptyRow"],"sources":["../../../../src/components/tables/data-table/RuiDataTableBody.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object\">\nimport type { TableColumn } from '@/components/tables/RuiTableHead.vue';\nimport { useDataTableColumns, useDataTableRowIdentity, useDataTableStyling } from '@/components/tables/data-table/context';\nimport RuiDataTableEmptyRow from '@/components/tables/data-table/RuiDataTableEmptyRow.vue';\nimport RuiDataTableErrorRow from '@/components/tables/data-table/RuiDataTableErrorRow.vue';\nimport RuiDataTableGroupRow from '@/components/tables/data-table/RuiDataTableGroupRow.vue';\nimport RuiDataTableLoadingRow from '@/components/tables/data-table/RuiDataTableLoadingRow.vue';\nimport RuiDataTableRow from '@/components/tables/data-table/RuiDataTableRow.vue';\nimport { type GroupHeader, isRow } from '@/composables/tables/data-table/types';\n\nconst { error, loading, noData } = defineProps<{\n filtered: (T | GroupHeader<T>)[];\n loading: boolean;\n noData: boolean;\n empty: { label?: string; description?: string };\n error: string;\n errorTitle: string;\n retryText: string;\n}>();\n\nconst emit = defineEmits<{\n retry: [];\n}>();\n\ndefineSlots<Partial<\n Record<`item.${string}`, (props: { column: TableColumn<T>; row: T; index: number }) => any> & {\n 'body.prepend'?: (props: { colspan: number }) => any;\n 'body.append'?: (props: { colspan: number }) => any;\n 'group.header'?: (props: {\n colspan: number;\n header: GroupHeader<T>;\n isOpen: boolean;\n toggle: () => void;\n }) => any;\n 'group.header.content'?: (props: { header: GroupHeader<T>; groupKey: string }) => any;\n 'expanded-item'?: (props: { row: T; index: number }) => any;\n 'row-append'?: (props: { row: T; index: number }) => any;\n 'no-data'?: () => any;\n 'empty-description'?: () => any;\n 'error'?: () => any;\n }\n>>();\n\nconst { classes, colspan } = useDataTableStyling();\nconst { itemSlotKeys } = useDataTableColumns<T>();\nconst { getRowId } = useDataTableRowIdentity<T>();\n\n/**\n * Loading outranks a failure, which outranks the empty state: rows that did\n * not arrive are not rows that are not there. Rows kept from an earlier read\n * stay below the error.\n */\nconst showError = computed<boolean>(() => !!error && !loading);\n\n/**\n * Rows shown while a read is in flight are the previous result. They stay at\n * full strength and usable, since a refresh can run for minutes; the header\n * bar marks the read, and `data-stale` lets a consumer style them.\n */\nconst stale = computed<boolean>(() => loading && !noData);\n</script>\n\n<template>\n <tbody\n :class=\"classes.tbody\"\n :data-stale=\"stale || undefined\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n v-if=\"$slots['body.prepend']\"\n :colspan=\"colspan\"\n name=\"body.prepend\"\n />\n <RuiDataTableErrorRow\n v-if=\"showError\"\n :message=\"error\"\n :title=\"errorTitle\"\n :retry-text=\"retryText\"\n @retry=\"emit('retry')\"\n >\n <template\n v-if=\"$slots.error\"\n #error\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"error\" />\n </template>\n </RuiDataTableErrorRow>\n <template v-for=\"(row, index) in filtered\">\n <RuiDataTableGroupRow\n v-if=\"!isRow(row)\"\n :key=\"`group-${row.identifier}`\"\n :row=\"row\"\n >\n <template\n v-if=\"$slots['group.header']\"\n #group.header=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"group.header\"\n v-bind=\"slotData\"\n />\n </template>\n <template\n v-if=\"$slots['group.header.content']\"\n #group.header.content=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"group.header.content\"\n v-bind=\"slotData\"\n />\n </template>\n </RuiDataTableGroupRow>\n <RuiDataTableRow\n v-else\n :key=\"`row-${getRowId(row)}`\"\n :row=\"row\"\n :index=\"index\"\n >\n <template\n v-for=\"key in itemSlotKeys\"\n #[`item.${key}`]=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n :name=\"`item.${key}`\"\n v-bind=\"slotData\"\n />\n </template>\n <template\n v-if=\"$slots['expanded-item']\"\n #expanded-item=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"expanded-item\"\n v-bind=\"slotData\"\n />\n </template>\n <template\n v-if=\"$slots['row-append']\"\n #row-append=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"row-append\"\n v-bind=\"slotData\"\n />\n </template>\n </RuiDataTableRow>\n </template>\n <RuiDataTableLoadingRow v-if=\"loading && noData\" />\n <RuiDataTableEmptyRow\n v-if=\"noData && empty && !loading && !showError\"\n :empty=\"empty\"\n >\n <template\n v-if=\"$slots['no-data']\"\n #no-data\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"no-data\" />\n </template>\n <template\n v-if=\"$slots['empty-description']\"\n #empty-description\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"empty-description\" />\n </template>\n </RuiDataTableEmptyRow>\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n v-if=\"$slots['body.append']\"\n :colspan=\"colspan\"\n name=\"body.append\"\n />\n </tbody>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"RuiDataTableBody.vue_vue_type_script_setup_true_lang.js","names":["$slots","_openBlock","_createElementBlock","_normalizeClass","_unref","_renderSlot","_createCommentVNode","_createBlock","RuiDataTableErrorRow","_createSlots","_withCtx","_Fragment","_renderList","RuiDataTableGroupRow","_mergeProps","RuiDataTableRow","RuiDataTableLoadingRow","RuiDataTableEmptyRow"],"sources":["../../../../src/components/tables/data-table/RuiDataTableBody.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object\">\nimport type { TableColumn } from '@/components/tables/RuiTableHead.vue';\nimport { useDataTableColumns, useDataTableRowIdentity, useDataTableStyling } from '@/components/tables/data-table/context';\nimport RuiDataTableEmptyRow from '@/components/tables/data-table/RuiDataTableEmptyRow.vue';\nimport RuiDataTableErrorRow from '@/components/tables/data-table/RuiDataTableErrorRow.vue';\nimport RuiDataTableGroupRow from '@/components/tables/data-table/RuiDataTableGroupRow.vue';\nimport RuiDataTableLoadingRow from '@/components/tables/data-table/RuiDataTableLoadingRow.vue';\nimport RuiDataTableRow from '@/components/tables/data-table/RuiDataTableRow.vue';\nimport { type GroupHeader, isRow } from '@/composables/tables/data-table/types';\n\nconst { error, loading, noData } = defineProps<{\n filtered: (T | GroupHeader<T>)[];\n loading: boolean;\n noData: boolean;\n empty: { label?: string; description?: string };\n error: string;\n errorTitle: string;\n retryText: string;\n}>();\n\nconst emit = defineEmits<{\n retry: [];\n}>();\n\ndefineSlots<Partial<\n Record<`item.${string}`, (props: { column: TableColumn<T>; row: T; index: number }) => any> & {\n 'body.prepend'?: (props: { colspan: number }) => any;\n 'body.append'?: (props: { colspan: number }) => any;\n 'group.header'?: (props: {\n colspan: number;\n header: GroupHeader<T>;\n isOpen: boolean;\n toggle: () => void;\n }) => any;\n 'group.header.content'?: (props: { header: GroupHeader<T>; groupKey: string }) => any;\n 'expanded-item'?: (props: { row: T; index: number }) => any;\n 'row-append'?: (props: { row: T; index: number }) => any;\n 'no-data'?: () => any;\n 'empty-description'?: () => any;\n 'error'?: () => any;\n }\n>>();\n\nconst { classes, colspan } = useDataTableStyling();\nconst { itemSlotKeys } = useDataTableColumns<T>();\nconst { getRowId } = useDataTableRowIdentity<T>();\n\n/**\n * Loading outranks a failure, which outranks the empty state: rows that did\n * not arrive are not rows that are not there. Rows kept from an earlier read\n * stay below the error.\n */\nconst showError = computed<boolean>(() => !!error && !loading);\n\n/**\n * Rows shown while a read is in flight are the previous result. They stay at\n * full strength and usable, since a refresh can run for minutes; the header\n * bar marks the read, and `data-stale` lets a consumer style them.\n */\nconst stale = computed<boolean>(() => loading && !noData);\n\n/**\n * Keys each entry of the row loop, so a row keeps its DOM and component state while the rows\n * around it change. Without it the loop patches entries by position, and a list that shifts by one\n * row (a virtual window, a row added on top) rebuilds every row below the change.\n */\nfunction getEntryKey(row: T | GroupHeader<T>): string {\n return isRow(row) ? `row-${getRowId(row)}` : `group-${row.identifier}`;\n}\n</script>\n\n<template>\n <tbody\n :class=\"classes.tbody\"\n :data-stale=\"stale || undefined\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n v-if=\"$slots['body.prepend']\"\n :colspan=\"colspan\"\n name=\"body.prepend\"\n />\n <RuiDataTableErrorRow\n v-if=\"showError\"\n :message=\"error\"\n :title=\"errorTitle\"\n :retry-text=\"retryText\"\n @retry=\"emit('retry')\"\n >\n <template\n v-if=\"$slots.error\"\n #error\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"error\" />\n </template>\n </RuiDataTableErrorRow>\n <template\n v-for=\"(row, index) in filtered\"\n :key=\"getEntryKey(row)\"\n >\n <RuiDataTableGroupRow\n v-if=\"!isRow(row)\"\n :row=\"row\"\n >\n <template\n v-if=\"$slots['group.header']\"\n #group.header=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"group.header\"\n v-bind=\"slotData\"\n />\n </template>\n <template\n v-if=\"$slots['group.header.content']\"\n #group.header.content=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"group.header.content\"\n v-bind=\"slotData\"\n />\n </template>\n </RuiDataTableGroupRow>\n <RuiDataTableRow\n v-else\n :row=\"row\"\n :index=\"index\"\n >\n <template\n v-for=\"key in itemSlotKeys\"\n #[`item.${key}`]=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n :name=\"`item.${key}`\"\n v-bind=\"slotData\"\n />\n </template>\n <template\n v-if=\"$slots['expanded-item']\"\n #expanded-item=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"expanded-item\"\n v-bind=\"slotData\"\n />\n </template>\n <template\n v-if=\"$slots['row-append']\"\n #row-append=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"row-append\"\n v-bind=\"slotData\"\n />\n </template>\n </RuiDataTableRow>\n </template>\n <RuiDataTableLoadingRow v-if=\"loading && noData\" />\n <RuiDataTableEmptyRow\n v-if=\"noData && empty && !loading && !showError\"\n :empty=\"empty\"\n >\n <template\n v-if=\"$slots['no-data']\"\n #no-data\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"no-data\" />\n </template>\n <template\n v-if=\"$slots['empty-description']\"\n #empty-description\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"empty-description\" />\n </template>\n </RuiDataTableEmptyRow>\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n v-if=\"$slots['body.append']\"\n :colspan=\"colspan\"\n name=\"body.append\"\n />\n </tbody>\n</template>\n"],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  "$schema": "http://json.schemastore.org/web-types",
3
3
  "framework": "vue",
4
4
  "name": "@rotki/ui-library",
5
- "version": "3.1.3",
5
+ "version": "3.1.4",
6
6
  "js-types-syntax": "typescript",
7
7
  "description-markup": "markdown",
8
8
  "contributions": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rotki/ui-library",
3
- "version": "3.1.3",
3
+ "version": "3.1.4",
4
4
  "description": "A vue design system and component library for rotki",
5
5
  "type": "module",
6
6
  "keywords": [