@rotki/ui-library 2.23.4 → 2.23.5

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":"RuiAutoComplete.js","names":[],"sources":["../../../../src/components/forms/auto-complete/RuiAutoComplete.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 RuiChip from '@/components/chips/RuiChip.vue';\nimport { autoCompleteStyles, type AutoCompleteVariant } from '@/components/forms/auto-complete/auto-complete-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 {\n type GroupBy,\n type ItemDisabled,\n type KeyOfType,\n useDropdownMenu,\n useDropdownOptionProperty,\n} from '@/composables/dropdown-menu';\nimport { type FloatingOptions, Placement } from '@/composables/floating';\nimport {\n useAutoCompleteFocus,\n useAutoCompleteKeyboardNavigation,\n useAutoCompleteSearch,\n useAutoCompleteValue,\n} from '@/composables/forms/auto-complete';\nimport { useFormTextDetail } from '@/utils/form-text-detail';\nimport { getNonRootAttrs, getRootAttrs, getTextToken } from '@/utils/helpers';\nimport { isEqual } from '@/utils/is-equal';\nimport { cn } from '@/utils/tv';\n\nexport type AutoCompleteModelValue<TValue> =\n TValue extends Array<infer U> ? U[] : TValue | undefined;\n\nexport interface RuiAutoCompleteClassNames {\n root?: VueClassValue;\n label?: VueClassValue;\n menu?: VueClassValue;\n}\n\nexport interface AutoCompleteProps<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 menuOptions?: MenuProps;\n classNames?: RuiAutoCompleteClassNames;\n /** @deprecated Use `classNames.label` instead */\n labelClass?: string;\n /** @deprecated Use `classNames.menu` instead */\n menuClass?: string;\n prependWidth?: number;\n appendWidth?: number;\n itemHeight?: number;\n variant?: AutoCompleteVariant;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n hideDetails?: boolean;\n autoSelectFirst?: boolean;\n chips?: boolean;\n noFilter?: boolean;\n hideNoData?: boolean;\n noDataText?: string;\n /**\n * Custom search predicate. Receives the resolved group label as a third\n * argument when `groupBy` is set, so callers don't have to re-run the\n * `groupBy` resolver themselves.\n */\n filter?: (item: TItem, queryText: string, group?: string) => boolean;\n hideSelected?: boolean;\n placeholder?: string;\n returnObject?: boolean;\n customValue?: boolean;\n hideCustomValue?: boolean;\n required?: boolean;\n hideSearchInput?: boolean;\n hideSelectionWrapper?: boolean;\n groupBy?: GroupBy<TItem>;\n itemDisabled?: ItemDisabled<TItem>;\n /**\n * When true and `groupBy` is set, the default search predicate also matches\n * against the resolved group label. Items belonging to a group whose label\n * matches the query stay visible (group header included), even when the\n * items themselves don't match. No effect when a custom `filter` is supplied\n * or when `groupBy` is undefined.\n */\n searchIncludesGroupLabel?: boolean;\n}\n\ndefineOptions({\n name: 'RuiAutoComplete',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<AutoCompleteModelValue<TValue>>({ required: true });\n\nconst searchInputModel = defineModel<string>('searchInput', { default: '' });\n\nconst {\n options = [],\n disabled = false,\n loading = false,\n readOnly = false,\n dense = false,\n clearable = false,\n hideDetails = false,\n chips = false,\n label = 'Select',\n menuOptions,\n classNames,\n labelClass,\n menuClass,\n variant = 'default',\n hint,\n keyAttr,\n textAttr,\n itemHeight,\n errorMessages = [],\n successMessages = [],\n autoSelectFirst = false,\n noFilter = false,\n hideNoData = false,\n noDataText = 'No data available',\n filter,\n hideSelected = false,\n placeholder = '',\n returnObject = false,\n customValue = false,\n hideCustomValue = false,\n required = false,\n hideSearchInput = false,\n hideSelectionWrapper = false,\n groupBy,\n itemDisabled,\n searchIncludesGroupLabel = false,\n} = defineProps<AutoCompleteProps<TValue, TItem>>();\n\nconst slots = defineSlots<{\n 'activator'?: (props: {\n disabled: boolean;\n value: TItem[];\n variant: string;\n readOnly: boolean;\n attrs: Record<string, unknown>;\n open: boolean;\n hasError: boolean;\n hasSuccess: boolean;\n }) => any;\n 'activator.label'?: (props: { value: TItem[] }) => any;\n 'selection.prepend'?: (props: { index: number; item: TItem }) => any;\n 'selection'?: (props: { index: number; item: TItem; chipAttrs: Record<string, unknown> }) => 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 'group-header'?: (props: { group: string; items: TItem[] }) => any;\n 'no-data'?: () => any;\n 'placeholder'?: (props: { disabled: boolean; readOnly: boolean }) => any;\n 'footer'?: () => any;\n}>();\n\nconst { getText, getIdentifier } = useDropdownOptionProperty<TValue, TItem>({\n keyAttr,\n textAttr,\n});\n\nconst textInput = useTemplateRef<HTMLInputElement>('textInput');\nconst activator = useTemplateRef<HTMLDivElement>('activator');\nconst menuRef = useTemplateRef<HTMLDivElement>('menuRef');\nconst menuWrapperRef = useTemplateRef<HTMLDivElement>('menuWrapperRef');\n\nconst { focused: activatorFocusedWithin } = useFocusWithin(activator);\nconst { focused: menuWrapperFocusedWithin } = useFocusWithin(menuWrapperRef);\nconst { focused: searchInputFocused } = useFocus(textInput);\nconst { focused: activatorFocused } = useFocus(activator);\n\nconst {\n internalSearch,\n filteredOptions,\n justOpened,\n updateInternalSearch,\n textValueToProperValue,\n} = useAutoCompleteSearch<TItem>(\n () => options,\n searchInputModel,\n {\n keyAttr: () => keyAttr,\n textAttr: () => textAttr,\n noFilter: () => noFilter,\n filter: () => filter,\n customValue: () => customValue,\n hideCustomValue: () => hideCustomValue,\n returnObject: () => returnObject,\n groupBy: () => groupBy,\n searchIncludesGroupLabel: () => searchIncludesGroupLabel,\n },\n);\n\nconst isOpen = ref<boolean>(false);\nconst isHovered = ref<boolean>(false);\n\n// Calculate multiple from modelValue directly to avoid circular dependency\nconst multiple = computed<boolean>(() => Array.isArray(get(modelValue)));\n\nconst shouldApplyValueAsSearch = computed<boolean>(\n () => !(slots.selection || get(multiple) || chips),\n);\n\nconst { value, setSelected } = useAutoCompleteValue<AutoCompleteModelValue<TValue>, TItem>(\n modelValue,\n () => options,\n {\n keyAttr: () => keyAttr,\n returnObject: () => returnObject,\n customValue: () => customValue,\n },\n {\n getIdentifier,\n getText,\n textValueToProperValue,\n shouldApplyValueAsSearch,\n isOpen,\n multiple,\n updateInternalSearch,\n },\n);\n\nconst resolvedItemHeight = itemHeight ?? (dense ? 30 : 48);\n\nconst {\n containerProps,\n wrapperProps,\n renderedData,\n menuWidth,\n isActiveItem,\n itemIndexInValue,\n highlightedIndex,\n moveHighlight,\n applyHighlighted,\n optionsWithSelectedHidden,\n userNavigated,\n groupedOptions,\n isGrouped,\n isItemDisabled,\n} = useDropdownMenu<TValue, TItem>({\n itemHeight: resolvedItemHeight,\n keyAttr,\n textAttr,\n options: filteredOptions,\n dense: () => dense,\n value,\n menuRef,\n setValue,\n autoSelectFirst,\n hideSelected,\n isOpen,\n getText,\n getIdentifier,\n groupBy: () => groupBy,\n itemDisabled: () => itemDisabled,\n});\n\nconst {\n focusedValueIndex,\n moveSelectedValueHighlight,\n onEnter,\n onInputDeletePressed,\n onTab,\n setValueFocus,\n} = useAutoCompleteKeyboardNavigation<TItem>(\n {\n chips: () => chips,\n customValue: () => customValue,\n multiple,\n },\n {\n activator,\n applyHighlighted,\n clear,\n filteredOptions,\n getText,\n highlightedIndex,\n internalSearch,\n isOpen,\n removeValue: (item: TItem): void => { setValue(item); },\n searchInputFocused,\n setSearchAsValue,\n userNavigated,\n value,\n },\n);\n\nconst {\n anyFocused: focusAnyFocused,\n inputClass: focusInputClass,\n onActivatorFocused: focusOnActivatorFocused,\n onInputFocused: focusOnInputFocused,\n setInputFocus: focusSetInputFocus,\n} = useAutoCompleteFocus(\n {\n customValue: () => customValue,\n disabled: () => disabled,\n shouldApplyValueAsSearch,\n },\n {\n activatorFocused,\n activatorFocusedWithin,\n focusedValueIndex,\n internalSearch,\n isOpen,\n justOpened,\n menuWrapperFocusedWithin,\n searchInputFocused,\n setSearchAsValue,\n textInput,\n updateInternalSearch,\n },\n);\n\nconst menuMinHeight = computed<number>(\n () => Math.min(5, get(optionsWithSelectedHidden).length) * resolvedItemHeight,\n);\n\nconst { hasError, hasSuccess } = useFormTextDetail(\n () => errorMessages,\n () => successMessages,\n);\n\nconst valueSet = computed<boolean>(() => get(value).length > 0);\n\n// True when the consumer's #placeholder slot is taking over the resting\n// content area, so the outlined/resting label must hide to avoid overlapping\n// the slot content.\nconst placeholderSlotActive = computed<boolean>(() => Boolean(slots.placeholder) && !get(valueSet) && !get(searchInputFocused));\n\nconst usedPlaceholder = computed<string>(() => {\n if (get(searchInputFocused))\n return placeholder;\n return '';\n});\n\nconst outlined = computed<boolean>(() => variant === 'outlined');\nconst float = computed<boolean>(() => (get(isOpen) || get(valueSet) || get(searchInputFocused)) && get(outlined));\n\nconst legendText = computed<string>(() => {\n if (!get(float) || !label)\n return '';\n return required ? `${label} ﹡` : label;\n});\n\nconst ui = computed<ReturnType<typeof autoCompleteStyles>>(() => autoCompleteStyles({\n filled: variant === 'filled',\n outlined: get(outlined),\n float: get(float),\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 = autoCompleteStyles({}).highlighted();\n\nfunction updateSearchInput(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement))\n return;\n\n const value = target.value;\n set(isOpen, true);\n updateInternalSearch(value);\n set(justOpened, false);\n}\n\nasync function setValue(val: TItem, skipRefocused = false): Promise<void> {\n const isMultiple = get(multiple);\n\n if (isMultiple) {\n const newValue = [...get(value)];\n const indexInValue = itemIndexInValue(val);\n if (indexInValue === -1) {\n updateInternalSearch();\n newValue.push(val);\n }\n else {\n newValue.splice(indexInValue, 1);\n }\n set(value, newValue);\n }\n else {\n if (get(shouldApplyValueAsSearch))\n updateInternalSearch(getText(val));\n else updateInternalSearch();\n\n set(value, [val]);\n }\n\n if (!isMultiple) {\n if (!skipRefocused) {\n set(activatorFocused, true);\n get(activator)?.focus();\n await nextTick(() => {\n set(isOpen, false);\n });\n }\n else {\n set(isOpen, false);\n }\n }\n else if (!skipRefocused) {\n set(searchInputFocused, true);\n }\n}\n\nfunction setSearchAsValue(): void {\n const searchToBeValue = get(internalSearch);\n if (!searchToBeValue)\n return;\n\n const newValue: TItem = textValueToProperValue(searchToBeValue);\n setValue(newValue, true);\n}\n\nfunction clear(): void {\n updateInternalSearch();\n set(modelValue, (Array.isArray(get(modelValue)) ? [] : undefined) as AutoCompleteModelValue<TValue>);\n}\n\nfunction chipAttrs(item: TItem, index: number): Record<string, unknown> {\n return {\n 'data-index': index,\n 'data-value': getIdentifier(item),\n 'onKeydown': (event: KeyboardEvent): void => {\n const { key } = event;\n if (['Backspace', 'Delete'].includes(key)) {\n event.stopPropagation();\n event.preventDefault();\n // Alt/Option + delete converts the chip back into editable search text\n // instead of removing it outright. Only meaningful when custom values\n // are accepted, since the restored text has to be re-selectable.\n if (event.altKey && customValue) {\n const text = getText(item) ?? '';\n setValue(item);\n updateInternalSearch(text);\n }\n else {\n setValue(item);\n }\n }\n },\n 'onClick': (e: MouseEvent): void => {\n e.stopPropagation();\n setValueFocus(index);\n },\n 'onClick:close': (): void => {\n setValue(item);\n },\n };\n}\n\nfunction setSelectionRange(start: number, end: number): void {\n set(searchInputFocused, true);\n get(textInput)?.setSelectionRange?.(start, end);\n}\n\nfunction arrowClicked(event: MouseEvent): void {\n if (get(isOpen)) {\n set(isOpen, false);\n event.stopPropagation();\n }\n}\n\nfunction openMenu(): void {\n set(isOpen, true);\n}\n\nfunction closeMenu(): void {\n set(isOpen, false);\n}\n\n// Optimize options watcher with shallow comparison first\nwatch(() => options, (curr, old) => {\n if (curr === old || customValue)\n return;\n\n // Only do deep comparison if reference changed\n if (isEqual(curr, old))\n return;\n\n setSelected(get(value));\n});\n\nconst menuFloatingOptions = computed<FloatingOptions>(() => ({\n placement: Placement.bottomStart,\n ...menuOptions?.options,\n}));\n\ndefineExpose({\n closeMenu,\n focus: focusSetInputFocus,\n openMenu,\n setSelectionRange,\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 :options=\"menuFloatingOptions\"\n :close-on-content-click=\"false\"\n :full-width=\"true\"\n :persist-on-activator-click=\"true\"\n :menu-class=\"[\n { hidden: optionsWithSelectedHidden.length === 0 && customValue && !slots['no-data'] },\n menuOptions?.menuClass,\n ]\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n :dense=\"dense\"\n :show-details=\"!hideDetails\"\n :disabled=\"disabled\"\n disable-auto-focus\n >\n <template #activator=\"{ attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\">\n <slot\n name=\"activator\"\n v-bind=\"{ disabled, value, variant, readOnly, attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\"\n >\n <div\n ref=\"activator\"\n :class=\"ui.activator({ class: cn(classNames?.label) ?? labelClass })\"\n v-bind=\"{\n ...getNonRootAttrs($attrs, ['onClick', 'class']),\n ...(readOnly ? {} : attrs),\n }\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n :aria-disabled=\"disabled || undefined\"\n :aria-readonly=\"readOnly || undefined\"\n :aria-required=\"required || undefined\"\n :aria-busy=\"loading || undefined\"\n data-id=\"activator\"\n :aria-invalid=\"hasError\"\n :tabindex=\"disabled || readOnly ? -1 : 0\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n @click=\"focusSetInputFocus()\"\n @focus=\"focusOnActivatorFocused()\"\n @keydown.enter=\"onEnter($event)\"\n @keydown.tab=\"onTab($event)\"\n @keydown.left=\"moveSelectedValueHighlight($event, false)\"\n @keydown.right=\"moveSelectedValueHighlight($event, true)\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n @keydown.home.prevent=\"highlightedIndex = 0\"\n @keydown.end.prevent=\"highlightedIndex = optionsWithSelectedHidden.length - 1\"\n >\n <span\n v-if=\"(outlined || (!valueSet && !searchInputFocused)) && !placeholderSlotActive\"\n :class=\"[\n ui.label(),\n { 'pr-2': !valueSet && !open && outlined },\n ]\"\n >\n <slot\n name=\"activator.label\"\n v-bind=\"{ value }\"\n >\n {{ label }}\n </slot>\n <span\n v-if=\"required\"\n :class=\"ui.required()\"\n >\n ﹡\n </span>\n </span>\n <div\n data-id=\"value\"\n :class=\"ui.value()\"\n >\n <!--\n Placeholder slot occupies the same flex space the input takes\n when focused, so transitioning into the focused state doesn't\n shift the activator's content. pointer-events-none is required\n so clicks pass through to the activator (otherwise the slot\n swallows the first click and the menu needs two taps to open).\n -->\n <div\n v-if=\"!valueSet && !searchInputFocused && slots.placeholder\"\n data-id=\"placeholder\"\n class=\"flex-1 min-w-0 pointer-events-none\"\n >\n <slot\n name=\"placeholder\"\n v-bind=\"{ disabled, readOnly }\"\n />\n </div>\n <template\n v-for=\"(item, i) in value\"\n :key=\"getIdentifier(item)\"\n >\n <RuiChip\n v-if=\"chips\"\n :key=\"getTextToken(getIdentifier(item))\"\n tabindex=\"-1\"\n :size=\"dense ? 'sm' : 'md'\"\n closeable\n :class=\"{ 'leading-3': dense }\"\n clickable\n v-bind=\"chipAttrs(item, i)\"\n >\n <div class=\"flex\">\n <slot\n name=\"selection.prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n :index=\"i\"\n name=\"selection\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </RuiChip>\n <div\n v-else-if=\"\n multiple\n || (!searchInputFocused && (slots['selection.prepend'] || slots.selection))\n \"\n :class=\"hideSelectionWrapper ? 'contents' : 'flex'\"\n >\n <slot\n name=\"selection.prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n v-if=\"multiple || slots.selection\"\n :index=\"i\"\n name=\"selection\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </template>\n <input\n ref=\"textInput\"\n :disabled=\"disabled\"\n :value=\"internalSearch\"\n class=\"bg-transparent outline-none\"\n type=\"text\"\n :placeholder=\"usedPlaceholder\"\n :class=\"[focusInputClass, { hidden: hideSearchInput }]\"\n :aria-invalid=\"hasError\"\n aria-autocomplete=\"list\"\n @keydown.delete=\"onInputDeletePressed()\"\n @input.stop=\"updateSearchInput($event)\"\n @focus=\"focusOnInputFocused()\"\n />\n </div>\n\n <RuiButton\n v-if=\"clearable && valueSet && !disabled\"\n variant=\"text\"\n icon\n size=\"sm\"\n tabindex=\"-1\"\n color=\"error\"\n data-id=\"clear\"\n :class=\"[\n ui.clear(),\n focusAnyFocused && '!visible',\n { 'mr-2': !dense },\n ]\"\n @click.stop.prevent=\"clear()\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"18\"\n />\n </RuiButton>\n\n <span\n :class=\"ui.iconWrapper()\"\n @click=\"arrowClicked($event)\"\n >\n <RuiIcon\n :class=\"ui.icon()\"\n :size=\"dense ? 16 : 24\"\n name=\"lu-chevron-down\"\n />\n </span>\n\n <RuiProgress\n v-if=\"loading\"\n :class=\"ui.progress()\"\n color=\"primary\"\n thickness=\"3\"\n variant=\"indeterminate\"\n />\n </div>\n <fieldset\n v-if=\"outlined\"\n :class=\"ui.fieldset()\"\n >\n <legend :class=\"ui.legend()\">\n {{ legendText }}\n </legend>\n </fieldset>\n </slot>\n </template>\n <template #default=\"{ width }\">\n <div ref=\"menuWrapperRef\">\n <div\n v-if=\"optionsWithSelectedHidden.length > 0\"\n :ref=\"containerProps.ref\"\n :class=\"ui.menu({ class: cn(classNames?.menu) ?? menuClass })\"\n :style=\"[containerProps.style, { width: `${width}px`, minWidth: menuWidth, minHeight: `${menuMinHeight}px` }]\"\n @scroll=\"containerProps.onScroll\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n >\n <div\n v-if=\"isGrouped\"\n ref=\"menuRef\"\n >\n <template\n v-for=\"(bucket, bucketIndex) in groupedOptions\"\n :key=\"bucket.group || `group-${bucketIndex}`\"\n >\n <div\n class=\"sticky top-0 z-10 bg-white dark:bg-rui-grey-900\"\n data-id=\"group-header\"\n >\n <slot\n name=\"group-header\"\n v-bind=\"{ group: bucket.group, items: bucket.items }\"\n >\n <div class=\"px-3 py-1 text-xs uppercase tracking-wide text-rui-text-secondary\">\n {{ bucket.group }}\n </div>\n </slot>\n </div>\n <RuiButton\n v-for=\"item in bucket.items\"\n :key=\"getIdentifier(item)?.toString()\"\n :active=\"isActiveItem(item)\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"isItemDisabled(item)\"\n tabindex=\"0\"\n variant=\"list\"\n :data-highlighted=\"optionsWithSelectedHidden.indexOf(item) === highlightedIndex\"\n :data-disabled=\"isItemDisabled(item) || undefined\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && optionsWithSelectedHidden.indexOf(item) === highlightedIndex,\n }\"\n @click=\"!isItemDisabled(item) && setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </template>\n </div>\n <div\n v-else\n v-bind=\"wrapperProps\"\n ref=\"menuRef\"\n >\n <RuiButton\n v-for=\"{ item, _index } in renderedData\"\n :key=\"getIdentifier(item)?.toString()\"\n :active=\"isActiveItem(item)\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"isItemDisabled(item)\"\n tabindex=\"0\"\n variant=\"list\"\n :data-highlighted=\"highlightedIndex === _index\"\n :data-disabled=\"isItemDisabled(item) || undefined\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && highlightedIndex === _index,\n }\"\n @click=\"!isItemDisabled(item) && setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </div>\n </div>\n\n <div\n v-else-if=\"!hideNoData\"\n :style=\"{ width: `${width}px`, minWidth: menuWidth }\"\n :class=\"classNames?.menu ?? menuClass\"\n >\n <slot name=\"no-data\">\n <div\n v-if=\"!customValue\"\n class=\"p-4\"\n data-id=\"no-data\"\n >\n {{ noDataText }}\n </div>\n </slot>\n </div>\n <!--\n The scroll container above reserves ~15px on the right for the\n scrollbar gutter. We render an equivalent right-padding here so the\n footer's content aligns with the option rows' content rather than\n the menu's outer edge. `pr-[var(--rui-scrollbar-gutter,15px)]`\n lets consumers override if their theme reserves a different width.\n -->\n <div\n v-if=\"slots.footer\"\n class=\"bg-white dark:bg-rui-grey-900 border-t border-black/[0.12] dark:border-white/[0.12] pr-[var(--rui-scrollbar-gutter,15px)] -mb-2\"\n data-id=\"footer\"\n >\n <slot name=\"footer\" />\n </div>\n </div>\n </template>\n </RuiMenu>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"RuiAutoComplete.js","names":[],"sources":["../../../../src/components/forms/auto-complete/RuiAutoComplete.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 RuiChip from '@/components/chips/RuiChip.vue';\nimport { autoCompleteStyles, type AutoCompleteVariant } from '@/components/forms/auto-complete/auto-complete-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 {\n type GroupBy,\n type ItemDisabled,\n type KeyOfType,\n useDropdownMenu,\n useDropdownOptionProperty,\n} from '@/composables/dropdown-menu';\nimport { type FloatingOptions, Placement } from '@/composables/floating';\nimport {\n useAutoCompleteFocus,\n useAutoCompleteKeyboardNavigation,\n useAutoCompleteSearch,\n useAutoCompleteValue,\n} from '@/composables/forms/auto-complete';\nimport { useFormTextDetail } from '@/utils/form-text-detail';\nimport { getNonRootAttrs, getRootAttrs, getTextToken } from '@/utils/helpers';\nimport { isEqual } from '@/utils/is-equal';\nimport { cn } from '@/utils/tv';\n\nexport type AutoCompleteModelValue<TValue> =\n TValue extends Array<infer U> ? U[] : TValue | undefined;\n\nexport interface RuiAutoCompleteClassNames {\n root?: VueClassValue;\n label?: VueClassValue;\n menu?: VueClassValue;\n}\n\nexport interface AutoCompleteProps<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 menuOptions?: MenuProps;\n classNames?: RuiAutoCompleteClassNames;\n /** @deprecated Use `classNames.label` instead */\n labelClass?: string;\n /** @deprecated Use `classNames.menu` instead */\n menuClass?: string;\n prependWidth?: number;\n appendWidth?: number;\n itemHeight?: number;\n variant?: AutoCompleteVariant;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n hideDetails?: boolean;\n autoSelectFirst?: boolean;\n chips?: boolean;\n noFilter?: boolean;\n hideNoData?: boolean;\n noDataText?: string;\n /**\n * Custom search predicate. Receives the resolved group label as a third\n * argument when `groupBy` is set, so callers don't have to re-run the\n * `groupBy` resolver themselves.\n */\n filter?: (item: TItem, queryText: string, group?: string) => boolean;\n hideSelected?: boolean;\n placeholder?: string;\n returnObject?: boolean;\n customValue?: boolean;\n hideCustomValue?: boolean;\n required?: boolean;\n hideSearchInput?: boolean;\n hideSelectionWrapper?: boolean;\n groupBy?: GroupBy<TItem>;\n itemDisabled?: ItemDisabled<TItem>;\n /**\n * When true and `groupBy` is set, the default search predicate also matches\n * against the resolved group label. Items belonging to a group whose label\n * matches the query stay visible (group header included), even when the\n * items themselves don't match. No effect when a custom `filter` is supplied\n * or when `groupBy` is undefined.\n */\n searchIncludesGroupLabel?: boolean;\n}\n\ndefineOptions({\n name: 'RuiAutoComplete',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<AutoCompleteModelValue<TValue>>({ required: true });\n\nconst searchInputModel = defineModel<string>('searchInput', { default: '' });\n\nconst {\n options = [],\n disabled = false,\n loading = false,\n readOnly = false,\n dense = false,\n clearable = false,\n hideDetails = false,\n chips = false,\n label = 'Select',\n menuOptions,\n classNames,\n labelClass,\n menuClass,\n variant = 'default',\n hint,\n keyAttr,\n textAttr,\n itemHeight,\n errorMessages = [],\n successMessages = [],\n autoSelectFirst = false,\n noFilter = false,\n hideNoData = false,\n noDataText = 'No data available',\n filter,\n hideSelected = false,\n placeholder = '',\n returnObject = false,\n customValue = false,\n hideCustomValue = false,\n required = false,\n hideSearchInput = false,\n hideSelectionWrapper = false,\n groupBy,\n itemDisabled,\n searchIncludesGroupLabel = false,\n} = defineProps<AutoCompleteProps<TValue, TItem>>();\n\nconst slots = defineSlots<{\n 'activator'?: (props: {\n disabled: boolean;\n value: TItem[];\n variant: string;\n readOnly: boolean;\n attrs: Record<string, unknown>;\n open: boolean;\n hasError: boolean;\n hasSuccess: boolean;\n }) => any;\n 'activator.label'?: (props: { value: TItem[] }) => any;\n 'selection.prepend'?: (props: { index: number; item: TItem }) => any;\n 'selection'?: (props: { index: number; item: TItem; chipAttrs: Record<string, unknown> }) => 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 'group-header'?: (props: { group: string; items: TItem[] }) => any;\n 'no-data'?: () => any;\n 'placeholder'?: (props: { disabled: boolean; readOnly: boolean }) => any;\n 'footer'?: () => any;\n}>();\n\nconst { getText, getIdentifier } = useDropdownOptionProperty<TValue, TItem>({\n keyAttr,\n textAttr,\n});\n\nconst textInput = useTemplateRef<HTMLInputElement>('textInput');\nconst activator = useTemplateRef<HTMLDivElement>('activator');\nconst menuRef = useTemplateRef<HTMLDivElement>('menuRef');\nconst menuWrapperRef = useTemplateRef<HTMLDivElement>('menuWrapperRef');\n\nconst { focused: activatorFocusedWithin } = useFocusWithin(activator);\nconst { focused: menuWrapperFocusedWithin } = useFocusWithin(menuWrapperRef);\nconst { focused: searchInputFocused } = useFocus(textInput);\nconst { focused: activatorFocused } = useFocus(activator);\n\nconst {\n internalSearch,\n filteredOptions,\n justOpened,\n updateInternalSearch,\n textValueToProperValue,\n} = useAutoCompleteSearch<TItem>(\n () => options,\n searchInputModel,\n {\n keyAttr: () => keyAttr,\n textAttr: () => textAttr,\n noFilter: () => noFilter,\n filter: () => filter,\n customValue: () => customValue,\n hideCustomValue: () => hideCustomValue,\n returnObject: () => returnObject,\n groupBy: () => groupBy,\n searchIncludesGroupLabel: () => searchIncludesGroupLabel,\n },\n);\n\nconst isOpen = ref<boolean>(false);\nconst isHovered = ref<boolean>(false);\n\n// Calculate multiple from modelValue directly to avoid circular dependency\nconst multiple = computed<boolean>(() => Array.isArray(get(modelValue)));\n\nconst shouldApplyValueAsSearch = computed<boolean>(\n () => !(slots.selection || get(multiple) || chips),\n);\n\nconst { resolveIn, value, setSelected } = useAutoCompleteValue<AutoCompleteModelValue<TValue>, TItem>(\n modelValue,\n () => options,\n {\n keyAttr: () => keyAttr,\n returnObject: () => returnObject,\n customValue: () => customValue,\n },\n {\n getIdentifier,\n getText,\n textValueToProperValue,\n shouldApplyValueAsSearch,\n isOpen,\n multiple,\n updateInternalSearch,\n },\n);\n\nconst resolvedItemHeight = itemHeight ?? (dense ? 30 : 48);\n\nconst {\n containerProps,\n wrapperProps,\n renderedData,\n menuWidth,\n isActiveItem,\n itemIndexInValue,\n highlightedIndex,\n moveHighlight,\n applyHighlighted,\n optionsWithSelectedHidden,\n userNavigated,\n groupedOptions,\n isGrouped,\n isItemDisabled,\n} = useDropdownMenu<TValue, TItem>({\n itemHeight: resolvedItemHeight,\n keyAttr,\n textAttr,\n options: filteredOptions,\n dense: () => dense,\n value,\n menuRef,\n setValue,\n autoSelectFirst,\n hideSelected,\n isOpen,\n getText,\n getIdentifier,\n groupBy: () => groupBy,\n itemDisabled: () => itemDisabled,\n});\n\nconst {\n focusedValueIndex,\n moveSelectedValueHighlight,\n onEnter,\n onInputDeletePressed,\n onTab,\n setValueFocus,\n} = useAutoCompleteKeyboardNavigation<TItem>(\n {\n chips: () => chips,\n customValue: () => customValue,\n multiple,\n },\n {\n activator,\n applyHighlighted,\n clear,\n filteredOptions,\n getText,\n highlightedIndex,\n internalSearch,\n isOpen,\n removeValue: (item: TItem): void => { setValue(item); },\n searchInputFocused,\n setSearchAsValue,\n userNavigated,\n value,\n },\n);\n\nconst {\n anyFocused: focusAnyFocused,\n inputClass: focusInputClass,\n onActivatorFocused: focusOnActivatorFocused,\n onInputFocused: focusOnInputFocused,\n setInputFocus: focusSetInputFocus,\n} = useAutoCompleteFocus(\n {\n customValue: () => customValue,\n disabled: () => disabled,\n shouldApplyValueAsSearch,\n },\n {\n activatorFocused,\n activatorFocusedWithin,\n focusedValueIndex,\n internalSearch,\n isOpen,\n justOpened,\n menuWrapperFocusedWithin,\n searchInputFocused,\n setSearchAsValue,\n textInput,\n updateInternalSearch,\n },\n);\n\nconst menuMinHeight = computed<number>(\n () => Math.min(5, get(optionsWithSelectedHidden).length) * resolvedItemHeight,\n);\n\nconst { hasError, hasSuccess } = useFormTextDetail(\n () => errorMessages,\n () => successMessages,\n);\n\nconst valueSet = computed<boolean>(() => get(value).length > 0);\n\n// True when the consumer's #placeholder slot is taking over the resting\n// content area, so the outlined/resting label must hide to avoid overlapping\n// the slot content.\nconst placeholderSlotActive = computed<boolean>(() => Boolean(slots.placeholder) && !get(valueSet) && !get(searchInputFocused));\n\nconst usedPlaceholder = computed<string>(() => {\n if (get(searchInputFocused))\n return placeholder;\n return '';\n});\n\nconst outlined = computed<boolean>(() => variant === 'outlined');\nconst float = computed<boolean>(() => (get(isOpen) || get(valueSet) || get(searchInputFocused)) && get(outlined));\n\nconst legendText = computed<string>(() => {\n if (!get(float) || !label)\n return '';\n return required ? `${label} ﹡` : label;\n});\n\nconst ui = computed<ReturnType<typeof autoCompleteStyles>>(() => autoCompleteStyles({\n filled: variant === 'filled',\n outlined: get(outlined),\n float: get(float),\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 = autoCompleteStyles({}).highlighted();\n\nfunction updateSearchInput(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement))\n return;\n\n const value = target.value;\n set(isOpen, true);\n updateInternalSearch(value);\n set(justOpened, false);\n}\n\nasync function setValue(val: TItem, skipRefocused = false): Promise<void> {\n const isMultiple = get(multiple);\n\n if (isMultiple) {\n const newValue = [...get(value)];\n const indexInValue = itemIndexInValue(val);\n if (indexInValue === -1) {\n updateInternalSearch();\n newValue.push(val);\n }\n else {\n newValue.splice(indexInValue, 1);\n }\n set(value, newValue);\n }\n else {\n if (get(shouldApplyValueAsSearch))\n updateInternalSearch(getText(val));\n else updateInternalSearch();\n\n set(value, [val]);\n }\n\n if (!isMultiple) {\n if (!skipRefocused) {\n set(activatorFocused, true);\n get(activator)?.focus();\n await nextTick(() => {\n set(isOpen, false);\n });\n }\n else {\n set(isOpen, false);\n }\n }\n else if (!skipRefocused) {\n set(searchInputFocused, true);\n }\n}\n\nfunction setSearchAsValue(): void {\n const searchToBeValue = get(internalSearch);\n if (!searchToBeValue)\n return;\n\n const newValue: TItem = textValueToProperValue(searchToBeValue);\n setValue(newValue, true);\n}\n\nfunction clear(): void {\n updateInternalSearch();\n set(modelValue, (Array.isArray(get(modelValue)) ? [] : undefined) as AutoCompleteModelValue<TValue>);\n}\n\nfunction chipAttrs(item: TItem, index: number): Record<string, unknown> {\n return {\n 'data-index': index,\n 'data-value': getIdentifier(item),\n 'onKeydown': (event: KeyboardEvent): void => {\n const { key } = event;\n if (['Backspace', 'Delete'].includes(key)) {\n event.stopPropagation();\n event.preventDefault();\n // Alt/Option + delete converts the chip back into editable search text\n // instead of removing it outright. Only meaningful when custom values\n // are accepted, since the restored text has to be re-selectable.\n if (event.altKey && customValue) {\n const text = getText(item) ?? '';\n setValue(item);\n updateInternalSearch(text);\n }\n else {\n setValue(item);\n }\n }\n },\n 'onClick': (e: MouseEvent): void => {\n e.stopPropagation();\n setValueFocus(index);\n },\n 'onClick:close': (): void => {\n setValue(item);\n },\n };\n}\n\nfunction setSelectionRange(start: number, end: number): void {\n set(searchInputFocused, true);\n get(textInput)?.setSelectionRange?.(start, end);\n}\n\nfunction arrowClicked(event: MouseEvent): void {\n if (get(isOpen)) {\n set(isOpen, false);\n event.stopPropagation();\n }\n}\n\nfunction openMenu(): void {\n set(isOpen, true);\n}\n\nfunction closeMenu(): void {\n set(isOpen, false);\n}\n\n// Optimize options watcher with shallow comparison first\nwatch(() => options, (curr, old) => {\n if (curr === old || customValue)\n return;\n\n // Only do deep comparison if reference changed\n if (isEqual(curr, old))\n return;\n\n // Only reconcile a selection the previous options could actually resolve. A value\n // that was already unresolvable is one the consumer set before its options arrived\n // (async lists start empty), so clearing it here would discard a legitimate value.\n if (!get(multiple) && resolveIn(old).length === 0)\n return;\n\n setSelected(get(value));\n});\n\nconst menuFloatingOptions = computed<FloatingOptions>(() => ({\n placement: Placement.bottomStart,\n ...menuOptions?.options,\n}));\n\ndefineExpose({\n closeMenu,\n focus: focusSetInputFocus,\n openMenu,\n setSelectionRange,\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 :options=\"menuFloatingOptions\"\n :close-on-content-click=\"false\"\n :full-width=\"true\"\n :persist-on-activator-click=\"true\"\n :menu-class=\"[\n { hidden: optionsWithSelectedHidden.length === 0 && customValue && !slots['no-data'] },\n menuOptions?.menuClass,\n ]\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n :dense=\"dense\"\n :show-details=\"!hideDetails\"\n :disabled=\"disabled\"\n disable-auto-focus\n >\n <template #activator=\"{ attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\">\n <slot\n name=\"activator\"\n v-bind=\"{ disabled, value, variant, readOnly, attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\"\n >\n <div\n ref=\"activator\"\n :class=\"ui.activator({ class: cn(classNames?.label) ?? labelClass })\"\n v-bind=\"{\n ...getNonRootAttrs($attrs, ['onClick', 'class']),\n ...(readOnly ? {} : attrs),\n }\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n :aria-disabled=\"disabled || undefined\"\n :aria-readonly=\"readOnly || undefined\"\n :aria-required=\"required || undefined\"\n :aria-busy=\"loading || undefined\"\n data-id=\"activator\"\n :aria-invalid=\"hasError\"\n :tabindex=\"disabled || readOnly ? -1 : 0\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n @click=\"focusSetInputFocus()\"\n @focus=\"focusOnActivatorFocused()\"\n @keydown.enter=\"onEnter($event)\"\n @keydown.tab=\"onTab($event)\"\n @keydown.left=\"moveSelectedValueHighlight($event, false)\"\n @keydown.right=\"moveSelectedValueHighlight($event, true)\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n @keydown.home.prevent=\"highlightedIndex = 0\"\n @keydown.end.prevent=\"highlightedIndex = optionsWithSelectedHidden.length - 1\"\n >\n <span\n v-if=\"(outlined || (!valueSet && !searchInputFocused)) && !placeholderSlotActive\"\n :class=\"[\n ui.label(),\n { 'pr-2': !valueSet && !open && outlined },\n ]\"\n >\n <slot\n name=\"activator.label\"\n v-bind=\"{ value }\"\n >\n {{ label }}\n </slot>\n <span\n v-if=\"required\"\n :class=\"ui.required()\"\n >\n ﹡\n </span>\n </span>\n <div\n data-id=\"value\"\n :class=\"ui.value()\"\n >\n <!--\n Placeholder slot occupies the same flex space the input takes\n when focused, so transitioning into the focused state doesn't\n shift the activator's content. pointer-events-none is required\n so clicks pass through to the activator (otherwise the slot\n swallows the first click and the menu needs two taps to open).\n -->\n <div\n v-if=\"!valueSet && !searchInputFocused && slots.placeholder\"\n data-id=\"placeholder\"\n class=\"flex-1 min-w-0 pointer-events-none\"\n >\n <slot\n name=\"placeholder\"\n v-bind=\"{ disabled, readOnly }\"\n />\n </div>\n <template\n v-for=\"(item, i) in value\"\n :key=\"getIdentifier(item)\"\n >\n <RuiChip\n v-if=\"chips\"\n :key=\"getTextToken(getIdentifier(item))\"\n tabindex=\"-1\"\n :size=\"dense ? 'sm' : 'md'\"\n closeable\n :class=\"{ 'leading-3': dense }\"\n clickable\n v-bind=\"chipAttrs(item, i)\"\n >\n <div class=\"flex\">\n <slot\n name=\"selection.prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n :index=\"i\"\n name=\"selection\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </RuiChip>\n <div\n v-else-if=\"\n multiple\n || (!searchInputFocused && (slots['selection.prepend'] || slots.selection))\n \"\n :class=\"hideSelectionWrapper ? 'contents' : 'flex'\"\n >\n <slot\n name=\"selection.prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n v-if=\"multiple || slots.selection\"\n :index=\"i\"\n name=\"selection\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </template>\n <input\n ref=\"textInput\"\n :disabled=\"disabled\"\n :value=\"internalSearch\"\n class=\"bg-transparent outline-none\"\n type=\"text\"\n :placeholder=\"usedPlaceholder\"\n :class=\"[focusInputClass, { hidden: hideSearchInput }]\"\n :aria-invalid=\"hasError\"\n aria-autocomplete=\"list\"\n @keydown.delete=\"onInputDeletePressed()\"\n @input.stop=\"updateSearchInput($event)\"\n @focus=\"focusOnInputFocused()\"\n />\n </div>\n\n <RuiButton\n v-if=\"clearable && valueSet && !disabled\"\n variant=\"text\"\n icon\n size=\"sm\"\n tabindex=\"-1\"\n color=\"error\"\n data-id=\"clear\"\n :class=\"[\n ui.clear(),\n focusAnyFocused && '!visible',\n { 'mr-2': !dense },\n ]\"\n @click.stop.prevent=\"clear()\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"18\"\n />\n </RuiButton>\n\n <span\n :class=\"ui.iconWrapper()\"\n @click=\"arrowClicked($event)\"\n >\n <RuiIcon\n :class=\"ui.icon()\"\n :size=\"dense ? 16 : 24\"\n name=\"lu-chevron-down\"\n />\n </span>\n\n <RuiProgress\n v-if=\"loading\"\n :class=\"ui.progress()\"\n color=\"primary\"\n thickness=\"3\"\n variant=\"indeterminate\"\n />\n </div>\n <fieldset\n v-if=\"outlined\"\n :class=\"ui.fieldset()\"\n >\n <legend :class=\"ui.legend()\">\n {{ legendText }}\n </legend>\n </fieldset>\n </slot>\n </template>\n <template #default=\"{ width }\">\n <div ref=\"menuWrapperRef\">\n <div\n v-if=\"optionsWithSelectedHidden.length > 0\"\n :ref=\"containerProps.ref\"\n :class=\"ui.menu({ class: cn(classNames?.menu) ?? menuClass })\"\n :style=\"[containerProps.style, { width: `${width}px`, minWidth: menuWidth, minHeight: `${menuMinHeight}px` }]\"\n @scroll=\"containerProps.onScroll\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n >\n <div\n v-if=\"isGrouped\"\n ref=\"menuRef\"\n >\n <template\n v-for=\"(bucket, bucketIndex) in groupedOptions\"\n :key=\"bucket.group || `group-${bucketIndex}`\"\n >\n <div\n class=\"sticky top-0 z-10 bg-white dark:bg-rui-grey-900\"\n data-id=\"group-header\"\n >\n <slot\n name=\"group-header\"\n v-bind=\"{ group: bucket.group, items: bucket.items }\"\n >\n <div class=\"px-3 py-1 text-xs uppercase tracking-wide text-rui-text-secondary\">\n {{ bucket.group }}\n </div>\n </slot>\n </div>\n <RuiButton\n v-for=\"item in bucket.items\"\n :key=\"getIdentifier(item)?.toString()\"\n :active=\"isActiveItem(item)\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"isItemDisabled(item)\"\n tabindex=\"0\"\n variant=\"list\"\n :data-highlighted=\"optionsWithSelectedHidden.indexOf(item) === highlightedIndex\"\n :data-disabled=\"isItemDisabled(item) || undefined\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && optionsWithSelectedHidden.indexOf(item) === highlightedIndex,\n }\"\n @click=\"!isItemDisabled(item) && setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </template>\n </div>\n <div\n v-else\n v-bind=\"wrapperProps\"\n ref=\"menuRef\"\n >\n <RuiButton\n v-for=\"{ item, _index } in renderedData\"\n :key=\"getIdentifier(item)?.toString()\"\n :active=\"isActiveItem(item)\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"isItemDisabled(item)\"\n tabindex=\"0\"\n variant=\"list\"\n :data-highlighted=\"highlightedIndex === _index\"\n :data-disabled=\"isItemDisabled(item) || undefined\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && highlightedIndex === _index,\n }\"\n @click=\"!isItemDisabled(item) && setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </div>\n </div>\n\n <div\n v-else-if=\"!hideNoData\"\n :style=\"{ width: `${width}px`, minWidth: menuWidth }\"\n :class=\"classNames?.menu ?? menuClass\"\n >\n <slot name=\"no-data\">\n <div\n v-if=\"!customValue\"\n class=\"p-4\"\n data-id=\"no-data\"\n >\n {{ noDataText }}\n </div>\n </slot>\n </div>\n <!--\n The scroll container above reserves ~15px on the right for the\n scrollbar gutter. We render an equivalent right-padding here so the\n footer's content aligns with the option rows' content rather than\n the menu's outer edge. `pr-[var(--rui-scrollbar-gutter,15px)]`\n lets consumers override if their theme reserves a different width.\n -->\n <div\n v-if=\"slots.footer\"\n class=\"bg-white dark:bg-rui-grey-900 border-t border-black/[0.12] dark:border-white/[0.12] pr-[var(--rui-scrollbar-gutter,15px)] -mb-2\"\n data-id=\"footer\"\n >\n <slot name=\"footer\" />\n </div>\n </div>\n </template>\n </RuiMenu>\n</template>\n"],"mappings":""}
@@ -190,7 +190,7 @@ var RuiAutoComplete_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
190
190
  const isHovered = ref(false);
191
191
  const multiple = computed(() => Array.isArray(get$1(modelValue)));
192
192
  const shouldApplyValueAsSearch = computed(() => !(slots.selection || get$1(multiple) || __props.chips));
193
- const { value, setSelected } = useAutoCompleteValue(modelValue, () => __props.options, {
193
+ const { resolveIn, value, setSelected } = useAutoCompleteValue(modelValue, () => __props.options, {
194
194
  keyAttr: () => __props.keyAttr,
195
195
  returnObject: () => __props.returnObject,
196
196
  customValue: () => __props.customValue
@@ -371,6 +371,7 @@ var RuiAutoComplete_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
371
371
  watch(() => __props.options, (curr, old) => {
372
372
  if (curr === old || __props.customValue) return;
373
373
  if (isEqual(curr, old)) return;
374
+ if (!get$1(multiple) && resolveIn(old).length === 0) return;
374
375
  setSelected(get$1(value));
375
376
  });
376
377
  const menuFloatingOptions = computed(() => ({
@@ -1 +1 @@
1
- {"version":3,"file":"RuiAutoComplete.vue_vue_type_script_setup_true_lang.js","names":["$attrs"],"sources":["../../../../src/components/forms/auto-complete/RuiAutoComplete.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 RuiChip from '@/components/chips/RuiChip.vue';\nimport { autoCompleteStyles, type AutoCompleteVariant } from '@/components/forms/auto-complete/auto-complete-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 {\n type GroupBy,\n type ItemDisabled,\n type KeyOfType,\n useDropdownMenu,\n useDropdownOptionProperty,\n} from '@/composables/dropdown-menu';\nimport { type FloatingOptions, Placement } from '@/composables/floating';\nimport {\n useAutoCompleteFocus,\n useAutoCompleteKeyboardNavigation,\n useAutoCompleteSearch,\n useAutoCompleteValue,\n} from '@/composables/forms/auto-complete';\nimport { useFormTextDetail } from '@/utils/form-text-detail';\nimport { getNonRootAttrs, getRootAttrs, getTextToken } from '@/utils/helpers';\nimport { isEqual } from '@/utils/is-equal';\nimport { cn } from '@/utils/tv';\n\nexport type AutoCompleteModelValue<TValue> =\n TValue extends Array<infer U> ? U[] : TValue | undefined;\n\nexport interface RuiAutoCompleteClassNames {\n root?: VueClassValue;\n label?: VueClassValue;\n menu?: VueClassValue;\n}\n\nexport interface AutoCompleteProps<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 menuOptions?: MenuProps;\n classNames?: RuiAutoCompleteClassNames;\n /** @deprecated Use `classNames.label` instead */\n labelClass?: string;\n /** @deprecated Use `classNames.menu` instead */\n menuClass?: string;\n prependWidth?: number;\n appendWidth?: number;\n itemHeight?: number;\n variant?: AutoCompleteVariant;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n hideDetails?: boolean;\n autoSelectFirst?: boolean;\n chips?: boolean;\n noFilter?: boolean;\n hideNoData?: boolean;\n noDataText?: string;\n /**\n * Custom search predicate. Receives the resolved group label as a third\n * argument when `groupBy` is set, so callers don't have to re-run the\n * `groupBy` resolver themselves.\n */\n filter?: (item: TItem, queryText: string, group?: string) => boolean;\n hideSelected?: boolean;\n placeholder?: string;\n returnObject?: boolean;\n customValue?: boolean;\n hideCustomValue?: boolean;\n required?: boolean;\n hideSearchInput?: boolean;\n hideSelectionWrapper?: boolean;\n groupBy?: GroupBy<TItem>;\n itemDisabled?: ItemDisabled<TItem>;\n /**\n * When true and `groupBy` is set, the default search predicate also matches\n * against the resolved group label. Items belonging to a group whose label\n * matches the query stay visible (group header included), even when the\n * items themselves don't match. No effect when a custom `filter` is supplied\n * or when `groupBy` is undefined.\n */\n searchIncludesGroupLabel?: boolean;\n}\n\ndefineOptions({\n name: 'RuiAutoComplete',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<AutoCompleteModelValue<TValue>>({ required: true });\n\nconst searchInputModel = defineModel<string>('searchInput', { default: '' });\n\nconst {\n options = [],\n disabled = false,\n loading = false,\n readOnly = false,\n dense = false,\n clearable = false,\n hideDetails = false,\n chips = false,\n label = 'Select',\n menuOptions,\n classNames,\n labelClass,\n menuClass,\n variant = 'default',\n hint,\n keyAttr,\n textAttr,\n itemHeight,\n errorMessages = [],\n successMessages = [],\n autoSelectFirst = false,\n noFilter = false,\n hideNoData = false,\n noDataText = 'No data available',\n filter,\n hideSelected = false,\n placeholder = '',\n returnObject = false,\n customValue = false,\n hideCustomValue = false,\n required = false,\n hideSearchInput = false,\n hideSelectionWrapper = false,\n groupBy,\n itemDisabled,\n searchIncludesGroupLabel = false,\n} = defineProps<AutoCompleteProps<TValue, TItem>>();\n\nconst slots = defineSlots<{\n 'activator'?: (props: {\n disabled: boolean;\n value: TItem[];\n variant: string;\n readOnly: boolean;\n attrs: Record<string, unknown>;\n open: boolean;\n hasError: boolean;\n hasSuccess: boolean;\n }) => any;\n 'activator.label'?: (props: { value: TItem[] }) => any;\n 'selection.prepend'?: (props: { index: number; item: TItem }) => any;\n 'selection'?: (props: { index: number; item: TItem; chipAttrs: Record<string, unknown> }) => 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 'group-header'?: (props: { group: string; items: TItem[] }) => any;\n 'no-data'?: () => any;\n 'placeholder'?: (props: { disabled: boolean; readOnly: boolean }) => any;\n 'footer'?: () => any;\n}>();\n\nconst { getText, getIdentifier } = useDropdownOptionProperty<TValue, TItem>({\n keyAttr,\n textAttr,\n});\n\nconst textInput = useTemplateRef<HTMLInputElement>('textInput');\nconst activator = useTemplateRef<HTMLDivElement>('activator');\nconst menuRef = useTemplateRef<HTMLDivElement>('menuRef');\nconst menuWrapperRef = useTemplateRef<HTMLDivElement>('menuWrapperRef');\n\nconst { focused: activatorFocusedWithin } = useFocusWithin(activator);\nconst { focused: menuWrapperFocusedWithin } = useFocusWithin(menuWrapperRef);\nconst { focused: searchInputFocused } = useFocus(textInput);\nconst { focused: activatorFocused } = useFocus(activator);\n\nconst {\n internalSearch,\n filteredOptions,\n justOpened,\n updateInternalSearch,\n textValueToProperValue,\n} = useAutoCompleteSearch<TItem>(\n () => options,\n searchInputModel,\n {\n keyAttr: () => keyAttr,\n textAttr: () => textAttr,\n noFilter: () => noFilter,\n filter: () => filter,\n customValue: () => customValue,\n hideCustomValue: () => hideCustomValue,\n returnObject: () => returnObject,\n groupBy: () => groupBy,\n searchIncludesGroupLabel: () => searchIncludesGroupLabel,\n },\n);\n\nconst isOpen = ref<boolean>(false);\nconst isHovered = ref<boolean>(false);\n\n// Calculate multiple from modelValue directly to avoid circular dependency\nconst multiple = computed<boolean>(() => Array.isArray(get(modelValue)));\n\nconst shouldApplyValueAsSearch = computed<boolean>(\n () => !(slots.selection || get(multiple) || chips),\n);\n\nconst { value, setSelected } = useAutoCompleteValue<AutoCompleteModelValue<TValue>, TItem>(\n modelValue,\n () => options,\n {\n keyAttr: () => keyAttr,\n returnObject: () => returnObject,\n customValue: () => customValue,\n },\n {\n getIdentifier,\n getText,\n textValueToProperValue,\n shouldApplyValueAsSearch,\n isOpen,\n multiple,\n updateInternalSearch,\n },\n);\n\nconst resolvedItemHeight = itemHeight ?? (dense ? 30 : 48);\n\nconst {\n containerProps,\n wrapperProps,\n renderedData,\n menuWidth,\n isActiveItem,\n itemIndexInValue,\n highlightedIndex,\n moveHighlight,\n applyHighlighted,\n optionsWithSelectedHidden,\n userNavigated,\n groupedOptions,\n isGrouped,\n isItemDisabled,\n} = useDropdownMenu<TValue, TItem>({\n itemHeight: resolvedItemHeight,\n keyAttr,\n textAttr,\n options: filteredOptions,\n dense: () => dense,\n value,\n menuRef,\n setValue,\n autoSelectFirst,\n hideSelected,\n isOpen,\n getText,\n getIdentifier,\n groupBy: () => groupBy,\n itemDisabled: () => itemDisabled,\n});\n\nconst {\n focusedValueIndex,\n moveSelectedValueHighlight,\n onEnter,\n onInputDeletePressed,\n onTab,\n setValueFocus,\n} = useAutoCompleteKeyboardNavigation<TItem>(\n {\n chips: () => chips,\n customValue: () => customValue,\n multiple,\n },\n {\n activator,\n applyHighlighted,\n clear,\n filteredOptions,\n getText,\n highlightedIndex,\n internalSearch,\n isOpen,\n removeValue: (item: TItem): void => { setValue(item); },\n searchInputFocused,\n setSearchAsValue,\n userNavigated,\n value,\n },\n);\n\nconst {\n anyFocused: focusAnyFocused,\n inputClass: focusInputClass,\n onActivatorFocused: focusOnActivatorFocused,\n onInputFocused: focusOnInputFocused,\n setInputFocus: focusSetInputFocus,\n} = useAutoCompleteFocus(\n {\n customValue: () => customValue,\n disabled: () => disabled,\n shouldApplyValueAsSearch,\n },\n {\n activatorFocused,\n activatorFocusedWithin,\n focusedValueIndex,\n internalSearch,\n isOpen,\n justOpened,\n menuWrapperFocusedWithin,\n searchInputFocused,\n setSearchAsValue,\n textInput,\n updateInternalSearch,\n },\n);\n\nconst menuMinHeight = computed<number>(\n () => Math.min(5, get(optionsWithSelectedHidden).length) * resolvedItemHeight,\n);\n\nconst { hasError, hasSuccess } = useFormTextDetail(\n () => errorMessages,\n () => successMessages,\n);\n\nconst valueSet = computed<boolean>(() => get(value).length > 0);\n\n// True when the consumer's #placeholder slot is taking over the resting\n// content area, so the outlined/resting label must hide to avoid overlapping\n// the slot content.\nconst placeholderSlotActive = computed<boolean>(() => Boolean(slots.placeholder) && !get(valueSet) && !get(searchInputFocused));\n\nconst usedPlaceholder = computed<string>(() => {\n if (get(searchInputFocused))\n return placeholder;\n return '';\n});\n\nconst outlined = computed<boolean>(() => variant === 'outlined');\nconst float = computed<boolean>(() => (get(isOpen) || get(valueSet) || get(searchInputFocused)) && get(outlined));\n\nconst legendText = computed<string>(() => {\n if (!get(float) || !label)\n return '';\n return required ? `${label} ﹡` : label;\n});\n\nconst ui = computed<ReturnType<typeof autoCompleteStyles>>(() => autoCompleteStyles({\n filled: variant === 'filled',\n outlined: get(outlined),\n float: get(float),\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 = autoCompleteStyles({}).highlighted();\n\nfunction updateSearchInput(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement))\n return;\n\n const value = target.value;\n set(isOpen, true);\n updateInternalSearch(value);\n set(justOpened, false);\n}\n\nasync function setValue(val: TItem, skipRefocused = false): Promise<void> {\n const isMultiple = get(multiple);\n\n if (isMultiple) {\n const newValue = [...get(value)];\n const indexInValue = itemIndexInValue(val);\n if (indexInValue === -1) {\n updateInternalSearch();\n newValue.push(val);\n }\n else {\n newValue.splice(indexInValue, 1);\n }\n set(value, newValue);\n }\n else {\n if (get(shouldApplyValueAsSearch))\n updateInternalSearch(getText(val));\n else updateInternalSearch();\n\n set(value, [val]);\n }\n\n if (!isMultiple) {\n if (!skipRefocused) {\n set(activatorFocused, true);\n get(activator)?.focus();\n await nextTick(() => {\n set(isOpen, false);\n });\n }\n else {\n set(isOpen, false);\n }\n }\n else if (!skipRefocused) {\n set(searchInputFocused, true);\n }\n}\n\nfunction setSearchAsValue(): void {\n const searchToBeValue = get(internalSearch);\n if (!searchToBeValue)\n return;\n\n const newValue: TItem = textValueToProperValue(searchToBeValue);\n setValue(newValue, true);\n}\n\nfunction clear(): void {\n updateInternalSearch();\n set(modelValue, (Array.isArray(get(modelValue)) ? [] : undefined) as AutoCompleteModelValue<TValue>);\n}\n\nfunction chipAttrs(item: TItem, index: number): Record<string, unknown> {\n return {\n 'data-index': index,\n 'data-value': getIdentifier(item),\n 'onKeydown': (event: KeyboardEvent): void => {\n const { key } = event;\n if (['Backspace', 'Delete'].includes(key)) {\n event.stopPropagation();\n event.preventDefault();\n // Alt/Option + delete converts the chip back into editable search text\n // instead of removing it outright. Only meaningful when custom values\n // are accepted, since the restored text has to be re-selectable.\n if (event.altKey && customValue) {\n const text = getText(item) ?? '';\n setValue(item);\n updateInternalSearch(text);\n }\n else {\n setValue(item);\n }\n }\n },\n 'onClick': (e: MouseEvent): void => {\n e.stopPropagation();\n setValueFocus(index);\n },\n 'onClick:close': (): void => {\n setValue(item);\n },\n };\n}\n\nfunction setSelectionRange(start: number, end: number): void {\n set(searchInputFocused, true);\n get(textInput)?.setSelectionRange?.(start, end);\n}\n\nfunction arrowClicked(event: MouseEvent): void {\n if (get(isOpen)) {\n set(isOpen, false);\n event.stopPropagation();\n }\n}\n\nfunction openMenu(): void {\n set(isOpen, true);\n}\n\nfunction closeMenu(): void {\n set(isOpen, false);\n}\n\n// Optimize options watcher with shallow comparison first\nwatch(() => options, (curr, old) => {\n if (curr === old || customValue)\n return;\n\n // Only do deep comparison if reference changed\n if (isEqual(curr, old))\n return;\n\n setSelected(get(value));\n});\n\nconst menuFloatingOptions = computed<FloatingOptions>(() => ({\n placement: Placement.bottomStart,\n ...menuOptions?.options,\n}));\n\ndefineExpose({\n closeMenu,\n focus: focusSetInputFocus,\n openMenu,\n setSelectionRange,\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 :options=\"menuFloatingOptions\"\n :close-on-content-click=\"false\"\n :full-width=\"true\"\n :persist-on-activator-click=\"true\"\n :menu-class=\"[\n { hidden: optionsWithSelectedHidden.length === 0 && customValue && !slots['no-data'] },\n menuOptions?.menuClass,\n ]\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n :dense=\"dense\"\n :show-details=\"!hideDetails\"\n :disabled=\"disabled\"\n disable-auto-focus\n >\n <template #activator=\"{ attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\">\n <slot\n name=\"activator\"\n v-bind=\"{ disabled, value, variant, readOnly, attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\"\n >\n <div\n ref=\"activator\"\n :class=\"ui.activator({ class: cn(classNames?.label) ?? labelClass })\"\n v-bind=\"{\n ...getNonRootAttrs($attrs, ['onClick', 'class']),\n ...(readOnly ? {} : attrs),\n }\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n :aria-disabled=\"disabled || undefined\"\n :aria-readonly=\"readOnly || undefined\"\n :aria-required=\"required || undefined\"\n :aria-busy=\"loading || undefined\"\n data-id=\"activator\"\n :aria-invalid=\"hasError\"\n :tabindex=\"disabled || readOnly ? -1 : 0\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n @click=\"focusSetInputFocus()\"\n @focus=\"focusOnActivatorFocused()\"\n @keydown.enter=\"onEnter($event)\"\n @keydown.tab=\"onTab($event)\"\n @keydown.left=\"moveSelectedValueHighlight($event, false)\"\n @keydown.right=\"moveSelectedValueHighlight($event, true)\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n @keydown.home.prevent=\"highlightedIndex = 0\"\n @keydown.end.prevent=\"highlightedIndex = optionsWithSelectedHidden.length - 1\"\n >\n <span\n v-if=\"(outlined || (!valueSet && !searchInputFocused)) && !placeholderSlotActive\"\n :class=\"[\n ui.label(),\n { 'pr-2': !valueSet && !open && outlined },\n ]\"\n >\n <slot\n name=\"activator.label\"\n v-bind=\"{ value }\"\n >\n {{ label }}\n </slot>\n <span\n v-if=\"required\"\n :class=\"ui.required()\"\n >\n ﹡\n </span>\n </span>\n <div\n data-id=\"value\"\n :class=\"ui.value()\"\n >\n <!--\n Placeholder slot occupies the same flex space the input takes\n when focused, so transitioning into the focused state doesn't\n shift the activator's content. pointer-events-none is required\n so clicks pass through to the activator (otherwise the slot\n swallows the first click and the menu needs two taps to open).\n -->\n <div\n v-if=\"!valueSet && !searchInputFocused && slots.placeholder\"\n data-id=\"placeholder\"\n class=\"flex-1 min-w-0 pointer-events-none\"\n >\n <slot\n name=\"placeholder\"\n v-bind=\"{ disabled, readOnly }\"\n />\n </div>\n <template\n v-for=\"(item, i) in value\"\n :key=\"getIdentifier(item)\"\n >\n <RuiChip\n v-if=\"chips\"\n :key=\"getTextToken(getIdentifier(item))\"\n tabindex=\"-1\"\n :size=\"dense ? 'sm' : 'md'\"\n closeable\n :class=\"{ 'leading-3': dense }\"\n clickable\n v-bind=\"chipAttrs(item, i)\"\n >\n <div class=\"flex\">\n <slot\n name=\"selection.prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n :index=\"i\"\n name=\"selection\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </RuiChip>\n <div\n v-else-if=\"\n multiple\n || (!searchInputFocused && (slots['selection.prepend'] || slots.selection))\n \"\n :class=\"hideSelectionWrapper ? 'contents' : 'flex'\"\n >\n <slot\n name=\"selection.prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n v-if=\"multiple || slots.selection\"\n :index=\"i\"\n name=\"selection\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </template>\n <input\n ref=\"textInput\"\n :disabled=\"disabled\"\n :value=\"internalSearch\"\n class=\"bg-transparent outline-none\"\n type=\"text\"\n :placeholder=\"usedPlaceholder\"\n :class=\"[focusInputClass, { hidden: hideSearchInput }]\"\n :aria-invalid=\"hasError\"\n aria-autocomplete=\"list\"\n @keydown.delete=\"onInputDeletePressed()\"\n @input.stop=\"updateSearchInput($event)\"\n @focus=\"focusOnInputFocused()\"\n />\n </div>\n\n <RuiButton\n v-if=\"clearable && valueSet && !disabled\"\n variant=\"text\"\n icon\n size=\"sm\"\n tabindex=\"-1\"\n color=\"error\"\n data-id=\"clear\"\n :class=\"[\n ui.clear(),\n focusAnyFocused && '!visible',\n { 'mr-2': !dense },\n ]\"\n @click.stop.prevent=\"clear()\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"18\"\n />\n </RuiButton>\n\n <span\n :class=\"ui.iconWrapper()\"\n @click=\"arrowClicked($event)\"\n >\n <RuiIcon\n :class=\"ui.icon()\"\n :size=\"dense ? 16 : 24\"\n name=\"lu-chevron-down\"\n />\n </span>\n\n <RuiProgress\n v-if=\"loading\"\n :class=\"ui.progress()\"\n color=\"primary\"\n thickness=\"3\"\n variant=\"indeterminate\"\n />\n </div>\n <fieldset\n v-if=\"outlined\"\n :class=\"ui.fieldset()\"\n >\n <legend :class=\"ui.legend()\">\n {{ legendText }}\n </legend>\n </fieldset>\n </slot>\n </template>\n <template #default=\"{ width }\">\n <div ref=\"menuWrapperRef\">\n <div\n v-if=\"optionsWithSelectedHidden.length > 0\"\n :ref=\"containerProps.ref\"\n :class=\"ui.menu({ class: cn(classNames?.menu) ?? menuClass })\"\n :style=\"[containerProps.style, { width: `${width}px`, minWidth: menuWidth, minHeight: `${menuMinHeight}px` }]\"\n @scroll=\"containerProps.onScroll\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n >\n <div\n v-if=\"isGrouped\"\n ref=\"menuRef\"\n >\n <template\n v-for=\"(bucket, bucketIndex) in groupedOptions\"\n :key=\"bucket.group || `group-${bucketIndex}`\"\n >\n <div\n class=\"sticky top-0 z-10 bg-white dark:bg-rui-grey-900\"\n data-id=\"group-header\"\n >\n <slot\n name=\"group-header\"\n v-bind=\"{ group: bucket.group, items: bucket.items }\"\n >\n <div class=\"px-3 py-1 text-xs uppercase tracking-wide text-rui-text-secondary\">\n {{ bucket.group }}\n </div>\n </slot>\n </div>\n <RuiButton\n v-for=\"item in bucket.items\"\n :key=\"getIdentifier(item)?.toString()\"\n :active=\"isActiveItem(item)\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"isItemDisabled(item)\"\n tabindex=\"0\"\n variant=\"list\"\n :data-highlighted=\"optionsWithSelectedHidden.indexOf(item) === highlightedIndex\"\n :data-disabled=\"isItemDisabled(item) || undefined\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && optionsWithSelectedHidden.indexOf(item) === highlightedIndex,\n }\"\n @click=\"!isItemDisabled(item) && setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </template>\n </div>\n <div\n v-else\n v-bind=\"wrapperProps\"\n ref=\"menuRef\"\n >\n <RuiButton\n v-for=\"{ item, _index } in renderedData\"\n :key=\"getIdentifier(item)?.toString()\"\n :active=\"isActiveItem(item)\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"isItemDisabled(item)\"\n tabindex=\"0\"\n variant=\"list\"\n :data-highlighted=\"highlightedIndex === _index\"\n :data-disabled=\"isItemDisabled(item) || undefined\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && highlightedIndex === _index,\n }\"\n @click=\"!isItemDisabled(item) && setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </div>\n </div>\n\n <div\n v-else-if=\"!hideNoData\"\n :style=\"{ width: `${width}px`, minWidth: menuWidth }\"\n :class=\"classNames?.menu ?? menuClass\"\n >\n <slot name=\"no-data\">\n <div\n v-if=\"!customValue\"\n class=\"p-4\"\n data-id=\"no-data\"\n >\n {{ noDataText }}\n </div>\n </slot>\n </div>\n <!--\n The scroll container above reserves ~15px on the right for the\n scrollbar gutter. We render an equivalent right-padding here so the\n footer's content aligns with the option rows' content rather than\n the menu's outer edge. `pr-[var(--rui-scrollbar-gutter,15px)]`\n lets consumers override if their theme reserves a different width.\n -->\n <div\n v-if=\"slots.footer\"\n class=\"bg-white dark:bg-rui-grey-900 border-t border-black/[0.12] dark:border-white/[0.12] pr-[var(--rui-scrollbar-gutter,15px)] -mb-2\"\n data-id=\"footer\"\n >\n <slot name=\"footer\" />\n </div>\n </div>\n </template>\n </RuiMenu>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"RuiAutoComplete.vue_vue_type_script_setup_true_lang.js","names":["$attrs"],"sources":["../../../../src/components/forms/auto-complete/RuiAutoComplete.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 RuiChip from '@/components/chips/RuiChip.vue';\nimport { autoCompleteStyles, type AutoCompleteVariant } from '@/components/forms/auto-complete/auto-complete-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 {\n type GroupBy,\n type ItemDisabled,\n type KeyOfType,\n useDropdownMenu,\n useDropdownOptionProperty,\n} from '@/composables/dropdown-menu';\nimport { type FloatingOptions, Placement } from '@/composables/floating';\nimport {\n useAutoCompleteFocus,\n useAutoCompleteKeyboardNavigation,\n useAutoCompleteSearch,\n useAutoCompleteValue,\n} from '@/composables/forms/auto-complete';\nimport { useFormTextDetail } from '@/utils/form-text-detail';\nimport { getNonRootAttrs, getRootAttrs, getTextToken } from '@/utils/helpers';\nimport { isEqual } from '@/utils/is-equal';\nimport { cn } from '@/utils/tv';\n\nexport type AutoCompleteModelValue<TValue> =\n TValue extends Array<infer U> ? U[] : TValue | undefined;\n\nexport interface RuiAutoCompleteClassNames {\n root?: VueClassValue;\n label?: VueClassValue;\n menu?: VueClassValue;\n}\n\nexport interface AutoCompleteProps<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 menuOptions?: MenuProps;\n classNames?: RuiAutoCompleteClassNames;\n /** @deprecated Use `classNames.label` instead */\n labelClass?: string;\n /** @deprecated Use `classNames.menu` instead */\n menuClass?: string;\n prependWidth?: number;\n appendWidth?: number;\n itemHeight?: number;\n variant?: AutoCompleteVariant;\n hint?: string;\n errorMessages?: string | string[];\n successMessages?: string | string[];\n hideDetails?: boolean;\n autoSelectFirst?: boolean;\n chips?: boolean;\n noFilter?: boolean;\n hideNoData?: boolean;\n noDataText?: string;\n /**\n * Custom search predicate. Receives the resolved group label as a third\n * argument when `groupBy` is set, so callers don't have to re-run the\n * `groupBy` resolver themselves.\n */\n filter?: (item: TItem, queryText: string, group?: string) => boolean;\n hideSelected?: boolean;\n placeholder?: string;\n returnObject?: boolean;\n customValue?: boolean;\n hideCustomValue?: boolean;\n required?: boolean;\n hideSearchInput?: boolean;\n hideSelectionWrapper?: boolean;\n groupBy?: GroupBy<TItem>;\n itemDisabled?: ItemDisabled<TItem>;\n /**\n * When true and `groupBy` is set, the default search predicate also matches\n * against the resolved group label. Items belonging to a group whose label\n * matches the query stay visible (group header included), even when the\n * items themselves don't match. No effect when a custom `filter` is supplied\n * or when `groupBy` is undefined.\n */\n searchIncludesGroupLabel?: boolean;\n}\n\ndefineOptions({\n name: 'RuiAutoComplete',\n inheritAttrs: false,\n});\n\nconst modelValue = defineModel<AutoCompleteModelValue<TValue>>({ required: true });\n\nconst searchInputModel = defineModel<string>('searchInput', { default: '' });\n\nconst {\n options = [],\n disabled = false,\n loading = false,\n readOnly = false,\n dense = false,\n clearable = false,\n hideDetails = false,\n chips = false,\n label = 'Select',\n menuOptions,\n classNames,\n labelClass,\n menuClass,\n variant = 'default',\n hint,\n keyAttr,\n textAttr,\n itemHeight,\n errorMessages = [],\n successMessages = [],\n autoSelectFirst = false,\n noFilter = false,\n hideNoData = false,\n noDataText = 'No data available',\n filter,\n hideSelected = false,\n placeholder = '',\n returnObject = false,\n customValue = false,\n hideCustomValue = false,\n required = false,\n hideSearchInput = false,\n hideSelectionWrapper = false,\n groupBy,\n itemDisabled,\n searchIncludesGroupLabel = false,\n} = defineProps<AutoCompleteProps<TValue, TItem>>();\n\nconst slots = defineSlots<{\n 'activator'?: (props: {\n disabled: boolean;\n value: TItem[];\n variant: string;\n readOnly: boolean;\n attrs: Record<string, unknown>;\n open: boolean;\n hasError: boolean;\n hasSuccess: boolean;\n }) => any;\n 'activator.label'?: (props: { value: TItem[] }) => any;\n 'selection.prepend'?: (props: { index: number; item: TItem }) => any;\n 'selection'?: (props: { index: number; item: TItem; chipAttrs: Record<string, unknown> }) => 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 'group-header'?: (props: { group: string; items: TItem[] }) => any;\n 'no-data'?: () => any;\n 'placeholder'?: (props: { disabled: boolean; readOnly: boolean }) => any;\n 'footer'?: () => any;\n}>();\n\nconst { getText, getIdentifier } = useDropdownOptionProperty<TValue, TItem>({\n keyAttr,\n textAttr,\n});\n\nconst textInput = useTemplateRef<HTMLInputElement>('textInput');\nconst activator = useTemplateRef<HTMLDivElement>('activator');\nconst menuRef = useTemplateRef<HTMLDivElement>('menuRef');\nconst menuWrapperRef = useTemplateRef<HTMLDivElement>('menuWrapperRef');\n\nconst { focused: activatorFocusedWithin } = useFocusWithin(activator);\nconst { focused: menuWrapperFocusedWithin } = useFocusWithin(menuWrapperRef);\nconst { focused: searchInputFocused } = useFocus(textInput);\nconst { focused: activatorFocused } = useFocus(activator);\n\nconst {\n internalSearch,\n filteredOptions,\n justOpened,\n updateInternalSearch,\n textValueToProperValue,\n} = useAutoCompleteSearch<TItem>(\n () => options,\n searchInputModel,\n {\n keyAttr: () => keyAttr,\n textAttr: () => textAttr,\n noFilter: () => noFilter,\n filter: () => filter,\n customValue: () => customValue,\n hideCustomValue: () => hideCustomValue,\n returnObject: () => returnObject,\n groupBy: () => groupBy,\n searchIncludesGroupLabel: () => searchIncludesGroupLabel,\n },\n);\n\nconst isOpen = ref<boolean>(false);\nconst isHovered = ref<boolean>(false);\n\n// Calculate multiple from modelValue directly to avoid circular dependency\nconst multiple = computed<boolean>(() => Array.isArray(get(modelValue)));\n\nconst shouldApplyValueAsSearch = computed<boolean>(\n () => !(slots.selection || get(multiple) || chips),\n);\n\nconst { resolveIn, value, setSelected } = useAutoCompleteValue<AutoCompleteModelValue<TValue>, TItem>(\n modelValue,\n () => options,\n {\n keyAttr: () => keyAttr,\n returnObject: () => returnObject,\n customValue: () => customValue,\n },\n {\n getIdentifier,\n getText,\n textValueToProperValue,\n shouldApplyValueAsSearch,\n isOpen,\n multiple,\n updateInternalSearch,\n },\n);\n\nconst resolvedItemHeight = itemHeight ?? (dense ? 30 : 48);\n\nconst {\n containerProps,\n wrapperProps,\n renderedData,\n menuWidth,\n isActiveItem,\n itemIndexInValue,\n highlightedIndex,\n moveHighlight,\n applyHighlighted,\n optionsWithSelectedHidden,\n userNavigated,\n groupedOptions,\n isGrouped,\n isItemDisabled,\n} = useDropdownMenu<TValue, TItem>({\n itemHeight: resolvedItemHeight,\n keyAttr,\n textAttr,\n options: filteredOptions,\n dense: () => dense,\n value,\n menuRef,\n setValue,\n autoSelectFirst,\n hideSelected,\n isOpen,\n getText,\n getIdentifier,\n groupBy: () => groupBy,\n itemDisabled: () => itemDisabled,\n});\n\nconst {\n focusedValueIndex,\n moveSelectedValueHighlight,\n onEnter,\n onInputDeletePressed,\n onTab,\n setValueFocus,\n} = useAutoCompleteKeyboardNavigation<TItem>(\n {\n chips: () => chips,\n customValue: () => customValue,\n multiple,\n },\n {\n activator,\n applyHighlighted,\n clear,\n filteredOptions,\n getText,\n highlightedIndex,\n internalSearch,\n isOpen,\n removeValue: (item: TItem): void => { setValue(item); },\n searchInputFocused,\n setSearchAsValue,\n userNavigated,\n value,\n },\n);\n\nconst {\n anyFocused: focusAnyFocused,\n inputClass: focusInputClass,\n onActivatorFocused: focusOnActivatorFocused,\n onInputFocused: focusOnInputFocused,\n setInputFocus: focusSetInputFocus,\n} = useAutoCompleteFocus(\n {\n customValue: () => customValue,\n disabled: () => disabled,\n shouldApplyValueAsSearch,\n },\n {\n activatorFocused,\n activatorFocusedWithin,\n focusedValueIndex,\n internalSearch,\n isOpen,\n justOpened,\n menuWrapperFocusedWithin,\n searchInputFocused,\n setSearchAsValue,\n textInput,\n updateInternalSearch,\n },\n);\n\nconst menuMinHeight = computed<number>(\n () => Math.min(5, get(optionsWithSelectedHidden).length) * resolvedItemHeight,\n);\n\nconst { hasError, hasSuccess } = useFormTextDetail(\n () => errorMessages,\n () => successMessages,\n);\n\nconst valueSet = computed<boolean>(() => get(value).length > 0);\n\n// True when the consumer's #placeholder slot is taking over the resting\n// content area, so the outlined/resting label must hide to avoid overlapping\n// the slot content.\nconst placeholderSlotActive = computed<boolean>(() => Boolean(slots.placeholder) && !get(valueSet) && !get(searchInputFocused));\n\nconst usedPlaceholder = computed<string>(() => {\n if (get(searchInputFocused))\n return placeholder;\n return '';\n});\n\nconst outlined = computed<boolean>(() => variant === 'outlined');\nconst float = computed<boolean>(() => (get(isOpen) || get(valueSet) || get(searchInputFocused)) && get(outlined));\n\nconst legendText = computed<string>(() => {\n if (!get(float) || !label)\n return '';\n return required ? `${label} ﹡` : label;\n});\n\nconst ui = computed<ReturnType<typeof autoCompleteStyles>>(() => autoCompleteStyles({\n filled: variant === 'filled',\n outlined: get(outlined),\n float: get(float),\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 = autoCompleteStyles({}).highlighted();\n\nfunction updateSearchInput(event: Event): void {\n const target = event.target;\n if (!(target instanceof HTMLInputElement))\n return;\n\n const value = target.value;\n set(isOpen, true);\n updateInternalSearch(value);\n set(justOpened, false);\n}\n\nasync function setValue(val: TItem, skipRefocused = false): Promise<void> {\n const isMultiple = get(multiple);\n\n if (isMultiple) {\n const newValue = [...get(value)];\n const indexInValue = itemIndexInValue(val);\n if (indexInValue === -1) {\n updateInternalSearch();\n newValue.push(val);\n }\n else {\n newValue.splice(indexInValue, 1);\n }\n set(value, newValue);\n }\n else {\n if (get(shouldApplyValueAsSearch))\n updateInternalSearch(getText(val));\n else updateInternalSearch();\n\n set(value, [val]);\n }\n\n if (!isMultiple) {\n if (!skipRefocused) {\n set(activatorFocused, true);\n get(activator)?.focus();\n await nextTick(() => {\n set(isOpen, false);\n });\n }\n else {\n set(isOpen, false);\n }\n }\n else if (!skipRefocused) {\n set(searchInputFocused, true);\n }\n}\n\nfunction setSearchAsValue(): void {\n const searchToBeValue = get(internalSearch);\n if (!searchToBeValue)\n return;\n\n const newValue: TItem = textValueToProperValue(searchToBeValue);\n setValue(newValue, true);\n}\n\nfunction clear(): void {\n updateInternalSearch();\n set(modelValue, (Array.isArray(get(modelValue)) ? [] : undefined) as AutoCompleteModelValue<TValue>);\n}\n\nfunction chipAttrs(item: TItem, index: number): Record<string, unknown> {\n return {\n 'data-index': index,\n 'data-value': getIdentifier(item),\n 'onKeydown': (event: KeyboardEvent): void => {\n const { key } = event;\n if (['Backspace', 'Delete'].includes(key)) {\n event.stopPropagation();\n event.preventDefault();\n // Alt/Option + delete converts the chip back into editable search text\n // instead of removing it outright. Only meaningful when custom values\n // are accepted, since the restored text has to be re-selectable.\n if (event.altKey && customValue) {\n const text = getText(item) ?? '';\n setValue(item);\n updateInternalSearch(text);\n }\n else {\n setValue(item);\n }\n }\n },\n 'onClick': (e: MouseEvent): void => {\n e.stopPropagation();\n setValueFocus(index);\n },\n 'onClick:close': (): void => {\n setValue(item);\n },\n };\n}\n\nfunction setSelectionRange(start: number, end: number): void {\n set(searchInputFocused, true);\n get(textInput)?.setSelectionRange?.(start, end);\n}\n\nfunction arrowClicked(event: MouseEvent): void {\n if (get(isOpen)) {\n set(isOpen, false);\n event.stopPropagation();\n }\n}\n\nfunction openMenu(): void {\n set(isOpen, true);\n}\n\nfunction closeMenu(): void {\n set(isOpen, false);\n}\n\n// Optimize options watcher with shallow comparison first\nwatch(() => options, (curr, old) => {\n if (curr === old || customValue)\n return;\n\n // Only do deep comparison if reference changed\n if (isEqual(curr, old))\n return;\n\n // Only reconcile a selection the previous options could actually resolve. A value\n // that was already unresolvable is one the consumer set before its options arrived\n // (async lists start empty), so clearing it here would discard a legitimate value.\n if (!get(multiple) && resolveIn(old).length === 0)\n return;\n\n setSelected(get(value));\n});\n\nconst menuFloatingOptions = computed<FloatingOptions>(() => ({\n placement: Placement.bottomStart,\n ...menuOptions?.options,\n}));\n\ndefineExpose({\n closeMenu,\n focus: focusSetInputFocus,\n openMenu,\n setSelectionRange,\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 :options=\"menuFloatingOptions\"\n :close-on-content-click=\"false\"\n :full-width=\"true\"\n :persist-on-activator-click=\"true\"\n :menu-class=\"[\n { hidden: optionsWithSelectedHidden.length === 0 && customValue && !slots['no-data'] },\n menuOptions?.menuClass,\n ]\"\n :error-messages=\"errorMessages\"\n :success-messages=\"successMessages\"\n :hint=\"hint\"\n :dense=\"dense\"\n :show-details=\"!hideDetails\"\n :disabled=\"disabled\"\n disable-auto-focus\n >\n <template #activator=\"{ attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\">\n <slot\n name=\"activator\"\n v-bind=\"{ disabled, value, variant, readOnly, attrs, open, hasError: slotHasError, hasSuccess: slotHasSuccess }\"\n >\n <div\n ref=\"activator\"\n :class=\"ui.activator({ class: cn(classNames?.label) ?? labelClass })\"\n v-bind=\"{\n ...getNonRootAttrs($attrs, ['onClick', 'class']),\n ...(readOnly ? {} : attrs),\n }\"\n role=\"combobox\"\n :aria-expanded=\"open\"\n :aria-disabled=\"disabled || undefined\"\n :aria-readonly=\"readOnly || undefined\"\n :aria-required=\"required || undefined\"\n :aria-busy=\"loading || undefined\"\n data-id=\"activator\"\n :aria-invalid=\"hasError\"\n :tabindex=\"disabled || readOnly ? -1 : 0\"\n @mouseenter=\"isHovered = true\"\n @mouseleave=\"isHovered = false\"\n @click=\"focusSetInputFocus()\"\n @focus=\"focusOnActivatorFocused()\"\n @keydown.enter=\"onEnter($event)\"\n @keydown.tab=\"onTab($event)\"\n @keydown.left=\"moveSelectedValueHighlight($event, false)\"\n @keydown.right=\"moveSelectedValueHighlight($event, true)\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n @keydown.home.prevent=\"highlightedIndex = 0\"\n @keydown.end.prevent=\"highlightedIndex = optionsWithSelectedHidden.length - 1\"\n >\n <span\n v-if=\"(outlined || (!valueSet && !searchInputFocused)) && !placeholderSlotActive\"\n :class=\"[\n ui.label(),\n { 'pr-2': !valueSet && !open && outlined },\n ]\"\n >\n <slot\n name=\"activator.label\"\n v-bind=\"{ value }\"\n >\n {{ label }}\n </slot>\n <span\n v-if=\"required\"\n :class=\"ui.required()\"\n >\n ﹡\n </span>\n </span>\n <div\n data-id=\"value\"\n :class=\"ui.value()\"\n >\n <!--\n Placeholder slot occupies the same flex space the input takes\n when focused, so transitioning into the focused state doesn't\n shift the activator's content. pointer-events-none is required\n so clicks pass through to the activator (otherwise the slot\n swallows the first click and the menu needs two taps to open).\n -->\n <div\n v-if=\"!valueSet && !searchInputFocused && slots.placeholder\"\n data-id=\"placeholder\"\n class=\"flex-1 min-w-0 pointer-events-none\"\n >\n <slot\n name=\"placeholder\"\n v-bind=\"{ disabled, readOnly }\"\n />\n </div>\n <template\n v-for=\"(item, i) in value\"\n :key=\"getIdentifier(item)\"\n >\n <RuiChip\n v-if=\"chips\"\n :key=\"getTextToken(getIdentifier(item))\"\n tabindex=\"-1\"\n :size=\"dense ? 'sm' : 'md'\"\n closeable\n :class=\"{ 'leading-3': dense }\"\n clickable\n v-bind=\"chipAttrs(item, i)\"\n >\n <div class=\"flex\">\n <slot\n name=\"selection.prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n :index=\"i\"\n name=\"selection\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </RuiChip>\n <div\n v-else-if=\"\n multiple\n || (!searchInputFocused && (slots['selection.prepend'] || slots.selection))\n \"\n :class=\"hideSelectionWrapper ? 'contents' : 'flex'\"\n >\n <slot\n name=\"selection.prepend\"\n :index=\"i\"\n v-bind=\"{ item }\"\n />\n <slot\n v-if=\"multiple || slots.selection\"\n :index=\"i\"\n name=\"selection\"\n v-bind=\"{ item, chipAttrs: chipAttrs(item, i) }\"\n >\n {{ getText(item) }}\n </slot>\n </div>\n </template>\n <input\n ref=\"textInput\"\n :disabled=\"disabled\"\n :value=\"internalSearch\"\n class=\"bg-transparent outline-none\"\n type=\"text\"\n :placeholder=\"usedPlaceholder\"\n :class=\"[focusInputClass, { hidden: hideSearchInput }]\"\n :aria-invalid=\"hasError\"\n aria-autocomplete=\"list\"\n @keydown.delete=\"onInputDeletePressed()\"\n @input.stop=\"updateSearchInput($event)\"\n @focus=\"focusOnInputFocused()\"\n />\n </div>\n\n <RuiButton\n v-if=\"clearable && valueSet && !disabled\"\n variant=\"text\"\n icon\n size=\"sm\"\n tabindex=\"-1\"\n color=\"error\"\n data-id=\"clear\"\n :class=\"[\n ui.clear(),\n focusAnyFocused && '!visible',\n { 'mr-2': !dense },\n ]\"\n @click.stop.prevent=\"clear()\"\n >\n <RuiIcon\n name=\"lu-x\"\n size=\"18\"\n />\n </RuiButton>\n\n <span\n :class=\"ui.iconWrapper()\"\n @click=\"arrowClicked($event)\"\n >\n <RuiIcon\n :class=\"ui.icon()\"\n :size=\"dense ? 16 : 24\"\n name=\"lu-chevron-down\"\n />\n </span>\n\n <RuiProgress\n v-if=\"loading\"\n :class=\"ui.progress()\"\n color=\"primary\"\n thickness=\"3\"\n variant=\"indeterminate\"\n />\n </div>\n <fieldset\n v-if=\"outlined\"\n :class=\"ui.fieldset()\"\n >\n <legend :class=\"ui.legend()\">\n {{ legendText }}\n </legend>\n </fieldset>\n </slot>\n </template>\n <template #default=\"{ width }\">\n <div ref=\"menuWrapperRef\">\n <div\n v-if=\"optionsWithSelectedHidden.length > 0\"\n :ref=\"containerProps.ref\"\n :class=\"ui.menu({ class: cn(classNames?.menu) ?? menuClass })\"\n :style=\"[containerProps.style, { width: `${width}px`, minWidth: menuWidth, minHeight: `${menuMinHeight}px` }]\"\n @scroll=\"containerProps.onScroll\"\n @keydown.up.prevent=\"moveHighlight(true)\"\n @keydown.down.prevent=\"moveHighlight(false)\"\n >\n <div\n v-if=\"isGrouped\"\n ref=\"menuRef\"\n >\n <template\n v-for=\"(bucket, bucketIndex) in groupedOptions\"\n :key=\"bucket.group || `group-${bucketIndex}`\"\n >\n <div\n class=\"sticky top-0 z-10 bg-white dark:bg-rui-grey-900\"\n data-id=\"group-header\"\n >\n <slot\n name=\"group-header\"\n v-bind=\"{ group: bucket.group, items: bucket.items }\"\n >\n <div class=\"px-3 py-1 text-xs uppercase tracking-wide text-rui-text-secondary\">\n {{ bucket.group }}\n </div>\n </slot>\n </div>\n <RuiButton\n v-for=\"item in bucket.items\"\n :key=\"getIdentifier(item)?.toString()\"\n :active=\"isActiveItem(item)\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"isItemDisabled(item)\"\n tabindex=\"0\"\n variant=\"list\"\n :data-highlighted=\"optionsWithSelectedHidden.indexOf(item) === highlightedIndex\"\n :data-disabled=\"isItemDisabled(item) || undefined\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && optionsWithSelectedHidden.indexOf(item) === highlightedIndex,\n }\"\n @click=\"!isItemDisabled(item) && setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </template>\n </div>\n <div\n v-else\n v-bind=\"wrapperProps\"\n ref=\"menuRef\"\n >\n <RuiButton\n v-for=\"{ item, _index } in renderedData\"\n :key=\"getIdentifier(item)?.toString()\"\n :active=\"isActiveItem(item)\"\n :aria-selected=\"isActiveItem(item)\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"isItemDisabled(item)\"\n tabindex=\"0\"\n variant=\"list\"\n :data-highlighted=\"highlightedIndex === _index\"\n :data-disabled=\"isItemDisabled(item) || undefined\"\n :class=\"{\n [highlightedClass]: !isActiveItem(item) && highlightedIndex === _index,\n }\"\n @click=\"!isItemDisabled(item) && setValue(item)\"\n >\n <template #prepend>\n <slot\n name=\"item.prepend\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n <slot\n name=\"item\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n >\n {{ getText(item) }}\n </slot>\n <template #append>\n <slot\n name=\"item.append\"\n v-bind=\"{ disabled: isItemDisabled(item), item, active: isActiveItem(item) }\"\n />\n </template>\n </RuiButton>\n </div>\n </div>\n\n <div\n v-else-if=\"!hideNoData\"\n :style=\"{ width: `${width}px`, minWidth: menuWidth }\"\n :class=\"classNames?.menu ?? menuClass\"\n >\n <slot name=\"no-data\">\n <div\n v-if=\"!customValue\"\n class=\"p-4\"\n data-id=\"no-data\"\n >\n {{ noDataText }}\n </div>\n </slot>\n </div>\n <!--\n The scroll container above reserves ~15px on the right for the\n scrollbar gutter. We render an equivalent right-padding here so the\n footer's content aligns with the option rows' content rather than\n the menu's outer edge. `pr-[var(--rui-scrollbar-gutter,15px)]`\n lets consumers override if their theme reserves a different width.\n -->\n <div\n v-if=\"slots.footer\"\n class=\"bg-white dark:bg-rui-grey-900 border-t border-black/[0.12] dark:border-white/[0.12] pr-[var(--rui-scrollbar-gutter,15px)] -mb-2\"\n data-id=\"footer\"\n >\n <slot name=\"footer\" />\n </div>\n </div>\n </template>\n </RuiMenu>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgGA,MAAM,aAAa,SAA2C,SAAA,YAAmB;EAEjF,MAAM,mBAAmB,SAAmB,SAAC,aAA8B;EAyC3E,MAAM,QAAQ,SAAA;EAuBd,MAAM,EAAE,SAAS,kBAAkB,0BAAyC;GAC1E,SAAM,QAAA;GACN,UAAO,QAAA;EACT,CAAC;EAED,MAAM,YAAY,eAAiC,WAAW;EAC9D,MAAM,YAAY,eAA+B,WAAW;EAC5D,MAAM,UAAU,eAA+B,SAAS;EACxD,MAAM,iBAAiB,eAA+B,gBAAgB;EAEtE,MAAM,EAAE,SAAS,2BAA2B,eAAe,SAAS;EACpE,MAAM,EAAE,SAAS,6BAA6B,eAAe,cAAc;EAC3E,MAAM,EAAE,SAAS,uBAAuB,SAAS,SAAS;EAC1D,MAAM,EAAE,SAAS,qBAAqB,SAAS,SAAS;EAExD,MAAM,EACJ,gBACA,iBACA,YACA,sBACA,2BACE,4BACI,QAAA,SACN,kBACA;GACE,eAAe,QAAA;GACf,gBAAgB,QAAA;GAChB,gBAAgB,QAAA;GAChB,cAAc,QAAA;GACd,mBAAmB,QAAA;GACnB,uBAAuB,QAAA;GACvB,oBAAoB,QAAA;GACpB,eAAe,QAAA;GACf,gCAAgC,QAAA;EAClC,CACF;EAEA,MAAM,SAAS,IAAa,KAAK;EACjC,MAAM,YAAY,IAAa,KAAK;EAGpC,MAAM,WAAW,eAAwB,MAAM,QAAQ,MAAI,UAAU,CAAC,CAAC;EAEvE,MAAM,2BAA2B,eACzB,EAAE,MAAM,aAAa,MAAI,QAAQ,KAAK,QAAA,MAC9C;EAEA,MAAM,EAAE,WAAW,OAAO,gBAAgB,qBACxC,kBACM,QAAA,SACN;GACE,eAAe,QAAA;GACf,oBAAoB,QAAA;GACpB,mBAAmB,QAAA;EACrB,GACA;GACE;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CACF;EAEA,MAAM,qBAAqB,QAAA,eAAe,QAAA,QAAQ,KAAK;EAEvD,MAAM,EACJ,gBACA,cACA,cACA,WACA,cACA,kBACA,kBACA,eACA,kBACA,2BACA,eACA,gBACA,WACA,mBACE,gBAA+B;GACjC,YAAY;GACZ,SAAM,QAAA;GACN,UAAO,QAAA;GACP,SAAS;GACT,aAAa,QAAA;GACb;GACA;GACA;GACA,iBAAc,QAAA;GACd,cAAW,QAAA;GACX;GACA;GACA;GACA,eAAe,QAAA;GACf,oBAAoB,QAAA;EACtB,CAAC;EAED,MAAM,EACJ,mBACA,4BACA,SACA,sBACA,OACA,kBACE,kCACF;GACE,aAAa,QAAA;GACb,mBAAmB,QAAA;GACnB;EACF,GACA;GACE;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA,cAAc,SAAsB;IAAE,SAAS,IAAI;GAAG;GACtD;GACA;GACA;GACA;EACF,CACF;EAEA,MAAM,EACJ,YAAY,iBACZ,YAAY,iBACZ,oBAAoB,yBACpB,gBAAgB,qBAChB,eAAe,uBACb,qBACF;GACE,mBAAmB,QAAA;GACnB,gBAAgB,QAAA;GAChB;EACF,GACA;GACE;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF,CACF;EAEA,MAAM,gBAAgB,eACd,KAAK,IAAI,GAAG,MAAI,yBAAyB,CAAC,CAAC,MAAM,IAAI,kBAC7D;EAEA,MAAM,EAAE,UAAU,eAAe,wBACzB,QAAA,qBACA,QAAA,eACR;EAEA,MAAM,WAAW,eAAwB,MAAI,KAAK,CAAC,CAAC,SAAS,CAAC;EAK9D,MAAM,wBAAwB,eAAwB,QAAQ,MAAM,WAAW,KAAK,CAAC,MAAI,QAAQ,KAAK,CAAC,MAAI,kBAAkB,CAAC;EAE9H,MAAM,kBAAkB,eAAuB;GAC7C,IAAI,MAAI,kBAAkB,GACxB,OAAO,QAAA;GACT,OAAO;EACT,CAAC;EAED,MAAM,WAAW,eAAwB,QAAA,YAAY,UAAU;EAC/D,MAAM,QAAQ,gBAAyB,MAAI,MAAM,KAAK,MAAI,QAAQ,KAAK,MAAI,kBAAkB,MAAM,MAAI,QAAQ,CAAC;EAEhH,MAAM,aAAa,eAAuB;GACxC,IAAI,CAAC,MAAI,KAAK,KAAK,CAAC,QAAA,OAClB,OAAO;GACT,OAAO,QAAA,WAAW,GAAG,QAAA,MAAM,MAAM,QAAA;EACnC,CAAC;EAED,MAAM,KAAK,eAAsD,mBAAmB;GAClF,QAAQ,QAAA,YAAY;GACpB,UAAU,MAAI,QAAQ;GACtB,OAAO,MAAI,KAAK;GAChB,QAAQ,MAAI,MAAM;GAClB,SAAS,MAAI,SAAS;GACtB,OAAI,QAAA;GACJ,UAAO,QAAA;GACP,UAAU,QAAA;GACV,UAAU,MAAI,QAAQ;GACtB,YAAY,MAAI,UAAU,KAAK,CAAC,MAAI,QAAQ;EAC9C,CAAC,CAAC;EAEF,MAAM,mBAAmB,mBAAmB,CAAC,CAAC,CAAC,CAAC,YAAY;EAE5D,SAAS,kBAAkB,OAAoB;GAC7C,MAAM,SAAS,MAAM;GACrB,IAAI,EAAE,kBAAkB,mBACtB;GAEF,MAAM,QAAQ,OAAO;GACrB,MAAI,QAAQ,IAAI;GAChB,qBAAqB,KAAK;GAC1B,MAAI,YAAY,KAAK;EACvB;EAEA,eAAe,SAAS,KAAY,gBAAgB,OAAsB;GACxE,MAAM,aAAa,MAAI,QAAQ;GAE/B,IAAI,YAAY;IACd,MAAM,WAAW,CAAC,GAAG,MAAI,KAAK,CAAC;IAC/B,MAAM,eAAe,iBAAiB,GAAG;IACzC,IAAI,iBAAiB,IAAI;KACvB,qBAAqB;KACrB,SAAS,KAAK,GAAG;IACnB,OAEE,SAAS,OAAO,cAAc,CAAC;IAEjC,MAAI,OAAO,QAAQ;GACrB,OACK;IACH,IAAI,MAAI,wBAAwB,GAC9B,qBAAqB,QAAQ,GAAG,CAAC;SAC9B,qBAAqB;IAE1B,MAAI,OAAO,CAAC,GAAG,CAAC;GAClB;GAEA,IAAI,CAAC,YACH,IAAI,CAAC,eAAe;IAClB,MAAI,kBAAkB,IAAI;IAC1B,MAAI,SAAS,CAAC,EAAE,MAAM;IACtB,MAAM,eAAe;KACnB,MAAI,QAAQ,KAAK;IACnB,CAAC;GACH,OAEE,MAAI,QAAQ,KAAK;QAGhB,IAAI,CAAC,eACR,MAAI,oBAAoB,IAAI;EAEhC;EAEA,SAAS,mBAAyB;GAChC,MAAM,kBAAkB,MAAI,cAAc;GAC1C,IAAI,CAAC,iBACH;GAGF,SADwB,uBAAuB,eACtC,GAAU,IAAI;EACzB;EAEA,SAAS,QAAc;GACrB,qBAAqB;GACrB,MAAI,YAAa,MAAM,QAAQ,MAAI,UAAU,CAAC,IAAI,CAAC,IAAI,KAAA,CAA4C;EACrG;EAEA,SAAS,UAAU,MAAa,OAAwC;GACtE,OAAO;IACL,cAAc;IACd,cAAc,cAAc,IAAI;IAChC,cAAc,UAA+B;KAC3C,MAAM,EAAE,QAAQ;KAChB,IAAI,CAAC,aAAa,QAAQ,CAAC,CAAC,SAAS,GAAG,GAAG;MACzC,MAAM,gBAAgB;MACtB,MAAM,eAAe;MAIrB,IAAI,MAAM,UAAU,QAAA,aAAa;OAC/B,MAAM,OAAO,QAAQ,IAAI,KAAK;OAC9B,SAAS,IAAI;OACb,qBAAqB,IAAI;MAC3B,OAEE,SAAS,IAAI;KAEjB;IACF;IACA,YAAY,MAAwB;KAClC,EAAE,gBAAgB;KAClB,cAAc,KAAK;IACrB;IACA,uBAA6B;KAC3B,SAAS,IAAI;IACf;GACF;EACF;EAEA,SAAS,kBAAkB,OAAe,KAAmB;GAC3D,MAAI,oBAAoB,IAAI;GAC5B,MAAI,SAAS,CAAC,EAAE,oBAAoB,OAAO,GAAG;EAChD;EAEA,SAAS,aAAa,OAAyB;GAC7C,IAAI,MAAI,MAAM,GAAG;IACf,MAAI,QAAQ,KAAK;IACjB,MAAM,gBAAgB;GACxB;EACF;EAEA,SAAS,WAAiB;GACxB,MAAI,QAAQ,IAAI;EAClB;EAEA,SAAS,YAAkB;GACzB,MAAI,QAAQ,KAAK;EACnB;EAGA,YAAY,QAAA,UAAU,MAAM,QAAQ;GAClC,IAAI,SAAS,OAAO,QAAA,aAClB;GAGF,IAAI,QAAQ,MAAM,GAAG,GACnB;GAKF,IAAI,CAAC,MAAI,QAAQ,KAAK,UAAU,GAAG,CAAC,CAAC,WAAW,GAC9C;GAEF,YAAY,MAAI,KAAK,CAAC;EACxB,CAAC;EAED,MAAM,sBAAsB,gBAAiC;GAC3D,WAAW,UAAU;GACrB,GAAG,QAAA,aAAa;EAClB,EAAE;EAEF,SAAa;GACX;GACA,OAAO;GACP;GACA;EACF,CAAC;;uBAIC,YAmWU,iBAnWV,WAmWU;gBAlWC,MAAA,MAAA;0FAAM,QAAA,SAAA;;OACF,MAAA,YAAA,CAAY,CAACA,KAAAA,QAAM,CAAA,CAAA;IAAA,GAAU,QAAA;GAAW,GAAA;IACpD,OAAO,MAAA,EAAA,CAAE,CAAC,QAAO,EAAA,OAAU,MAAA,EAAA,CAAE,CAACA,KAAAA,OAAO,KAAK,EAAA,CAAA;IAC1C,SAAS,MAAA,mBAAA;IACT,0BAAwB;IACxB,cAAY;IACZ,8BAA4B;IAC5B,cAAU,CAAA,EAAA,QAAoB,MAAA,yBAAA,CAAyB,CAAC,WAAM,KAAU,QAAA,eAAW,CAAK,MAAK,WAAA,GAAqB,QAAA,aAAa,SAAA;IAI/H,kBAAgB,QAAA;IAChB,oBAAkB,QAAA;IAClB,MAAM,QAAA;IACN,OAAO,QAAA;IACP,gBAAY,CAAG,QAAA;IACf,UAAU,QAAA;IACX,sBAAA;;IAEW,WAAS,SA8LX,EA9Le,OAAO,MAAI,UAAY,cAAY,YAAc,qBAAc,CACrF,WA6LO,KAAA,QAAA,aAAA,eAAA,mBAAA;KAAA,UA3LK,QAAA;KAAQ,OAAE,MAAA,KAAA;KAAK,SAAE,QAAA;KAAO,UAAE,QAAA;KAAU;KAAO;KAAI,UAAY;KAAY,YAAc;IAAc,CAAA,CAAA,SA2LxG,CAzLL,mBAgLM,OAhLN,WAgLM;cA/KA;KAAJ,KAAI;KACH,OAAO,MAAA,EAAA,CAAE,CAAC,UAAS,EAAA,OAAU,MAAA,EAAA,CAAE,CAAC,QAAA,YAAY,KAAK,KAAK,QAAA,WAAU,CAAA;;QACxC,MAAA,eAAA,CAAe,CAACA,KAAAA,QAAM,CAAA,WAAA,OAAA,CAAA;QAAyC,QAAA,WAAQ,CAAA,IAAQ;;KAIxG,MAAK;KACJ,iBAAe;KACf,iBAAe,QAAA,YAAY,KAAA;KAC3B,iBAAe,QAAA,YAAY,KAAA;KAC3B,iBAAe,QAAA,YAAY,KAAA;KAC3B,aAAW,QAAA,WAAW,KAAA;KACvB,WAAQ;KACP,gBAAc,MAAA,QAAA;KACd,UAAU,QAAA,YAAY,QAAA,WAAQ,KAAA;KAC9B,cAAU,OAAA,OAAA,OAAA,MAAA,WAAE,UAAA,QAAS;KACrB,cAAU,OAAA,OAAA,OAAA,MAAA,WAAE,UAAA,QAAS;KACrB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,kBAAA,CAAkB,CAAA;KACzB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,uBAAA,CAAuB,CAAA;KAC9B,WAAO;qDAAQ,MAAA,OAAA,CAAO,CAAC,MAAM,GAAA,CAAA,OAAA,CAAA;uDAChB,MAAA,KAAA,CAAK,CAAC,MAAM,GAAA,CAAA,KAAA,CAAA;uDACX,MAAA,0BAAA,CAA0B,CAAC,QAAM,KAAA,GAAA,CAAA,MAAA,CAAA;uDAChC,MAAA,0BAAA,CAA0B,CAAC,QAAM,IAAA,GAAA,CAAA,OAAA,CAAA;qEAC5B,MAAA,aAAA,CAAa,CAAA,IAAA,GAAA,CAAA,SAAA,CAAA,GAAA,CAAA,IAAA,CAAA;qEACX,MAAA,aAAA,CAAa,CAAA,KAAA,GAAA,CAAA,SAAA,CAAA,GAAA,CAAA,MAAA,CAAA;qEACb,iBAAA,QAAgB,GAAA,CAAA,SAAA,CAAA,GAAA,CAAA,MAAA,CAAA;qEACjB,iBAAA,QAAmB,MAAA,yBAAA,CAAyB,CAAC,SAAM,GAAA,CAAA,SAAA,CAAA,GAAA,CAAA,KAAA,CAAA;;;MAGhE,MAAA,QAAA,KAAQ,CAAM,MAAA,QAAA,KAAQ,CAAK,MAAA,kBAAA,MAAkB,CAAO,MAAA,qBAAA,KAAA,UAAA,GAD7D,mBAmBO,QAAA;;MAjBJ,OAAK,eAAA,CAAkB,MAAA,EAAA,CAAE,CAAC,MAAK,GAAA,EAAA,QAAA,CAA6B,MAAA,QAAA,KAAQ,CAAK,QAAQ,MAAA,QAAA,EAAQ,CAAA,CAAA;SAK1F,WAKO,KAAA,QAAA,mBAAA,eAAA,mBAAA,EAAA,OAHK,MAAA,KAAA,EAAK,CAAA,CAAA,SAGV,CAAA,gBAAA,gBADF,QAAA,KAAK,GAAA,CAAA,CAAA,CAAA,GAGF,QAAA,YAAA,UAAA,GADR,mBAKO,QAAA;;MAHJ,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,SAAQ,CAAA;QACpB,OAED,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;KAEF,mBAsFM,OAAA;MArFJ,WAAQ;MACP,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,MAAK,CAAA;;OAUP,MAAA,QAAA,KAAQ,CAAK,MAAA,kBAAA,KAAsB,MAAM,eAAA,UAAA,GADlD,mBASM,OATN,YASM,CAJJ,WAGE,KAAA,QAAA,eAAA,eAAA,mBAAA;OAAA,UADU,QAAA;OAAQ,UAAE,QAAA;MAAQ,CAAA,CAAA,CAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;wBAGhC,mBAkDW,UAAA,MAAA,WAjDW,MAAA,KAAA,IAAZ,MAAM,MAAC;+DACT,MAAA,aAAA,CAAa,CAAC,IAAI,EAAA,GAAA,CAGhB,QAAA,SAAA,UAAA,GADR,YAwBU,iBAxBV,WAwBU;QAtBP,KAAK,MAAA,YAAA,CAAY,CAAC,MAAA,aAAA,CAAa,CAAC,IAAI,CAAA;QACrC,UAAS;QACR,MAAM,QAAA,QAAK,OAAA;QACZ,WAAA;QACC,OAAK,EAAA,aAAiB,QAAA,MAAK;QAC5B,WAAA;6BACQ,UAAU,MAAM,CAAC,CAAA,GAAA;+BAenB,CAbN,mBAaM,OAbN,YAaM,CAZJ,WAIE,KAAA,QAAA,qBAJF,WAIE,EAFC,OAAO,EAAC,GAAA,EAAA,SAAA,KAAA,GAAA,EACC,KAAI,CAAA,CAAA,GAEhB,WAMO,KAAA,QAAA,aANP,WAMO,EALJ,OAAO,EAAC,GAAA,EAAA,SAAA,KAAA,GAAA;SAEC;SAAI,WAAa,UAAU,MAAM,CAAC;QAAA,CAAA,SAGvC,CAAA,gBAAA,gBADF,MAAA,OAAA,CAAO,CAAC,IAAI,CAAA,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;;sCAKW,MAAA,QAAA,KAAA,CAAkC,MAAA,kBAAA,MAAuB,MAAK,wBAAyB,MAAM,cAAA,UAAA,GAD7H,mBAoBM,OAAA;;QAfH,OAAK,eAAE,QAAA,uBAAoB,aAAA,MAAA;WAE5B,WAIE,KAAA,QAAA,qBAJF,WAIE,EAFC,OAAO,EAAC,GAAA,EAAA,SAAA,KAAA,GAAA,EACC,KAAI,CAAA,CAAA,GAGR,MAAA,QAAA,KAAY,MAAM,YAD1B,WAOO,KAAA,QAAA,aAPP,WAOO,EALJ,OAAO,EAAC,GAAA,EAAA,SAAA,KAAA,GAAA;QAEC;QAAI,WAAa,UAAU,MAAM,CAAC;OAAA,CAAA,SAGvC,CAAA,gBAAA,gBADF,MAAA,OAAA,CAAO,CAAC,IAAI,CAAA,GAAA,CAAA,CAAA,GAAA,KAAA,GAAA,CAAA,IAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,EAAA;;MAIrB,mBAaE,SAAA;gBAZI;OAAJ,KAAI;OACH,UAAU,QAAA;OACV,OAAO,MAAA,cAAA;OACR,OAAK,eAAA,CAAC,+BAA6B,CAG1B,MAAA,eAAA,GAAe,EAAA,QAAY,QAAA,gBAAe,CAAA,CAAA,CAAA;OAFnD,MAAK;OACJ,aAAa,MAAA,eAAA;OAEb,gBAAc,MAAA,QAAA;OACf,qBAAkB;OACjB,WAAO,OAAA,OAAA,OAAA,KAAA,UAAA,WAAS,MAAA,oBAAA,CAAoB,CAAA,GAAA,CAAA,QAAA,CAAA;OACpC,SAAK,OAAA,OAAA,OAAA,KAAA,eAAA,WAAO,kBAAkB,MAAM,GAAA,CAAA,MAAA,CAAA;OACpC,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,mBAAA,CAAmB,CAAA;;;KAKvB,QAAA,aAAa,MAAA,QAAA,KAAQ,CAAK,QAAA,YAAA,UAAA,GADlC,YAmBY,mBAAA;;MAjBV,SAAQ;MACR,MAAA;MACA,MAAK;MACL,UAAS;MACT,OAAM;MACN,WAAQ;MACP,OAAK,eAAA;OAAkB,MAAA,EAAA,CAAE,CAAC,MAAK;OAAkB,MAAA,eAAA,KAAe;kBAAyC,QAAA,MAAK;;MAK9G,SAAK,OAAA,OAAA,OAAA,KAAA,eAAA,WAAe,MAAK,GAAA,CAAA,QAAA,SAAA,CAAA;;6BAKxB,CAHF,YAGE,iBAAA;OAFA,MAAK;OACL,MAAK;;;;KAIT,mBASO,QAAA;MARJ,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,YAAW,CAAA;MACrB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,aAAa,MAAM;SAE3B,YAIE,iBAAA;MAHC,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,KAAI,CAAA;MACd,MAAM,QAAA,QAAK,KAAA;MACZ,MAAK;;KAKD,QAAA,WAAA,UAAA,GADR,YAME,qBAAA;;MAJC,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,SAAQ,CAAA;MACnB,OAAM;MACN,WAAU;MACV,SAAQ;;wBAIJ,MAAA,QAAA,KAAA,UAAA,GADR,mBAOW,YAAA;;KALR,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,SAAQ,CAAA;QAEnB,mBAES,UAAA,EAFA,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,OAAM,CAAA,EAAA,GAAA,gBACpB,MAAA,UAAA,CAAU,GAAA,CAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,CAAA,CAAA;IAKV,SAAO,SA6IV,EA7Ic,YAAK,CACzB,mBA4IM,OAAA;cA5IG;KAAJ,KAAI;QAEC,MAAA,yBAAA,CAAyB,CAAC,SAAM,KAAA,UAAA,GADxC,mBA4GM,OAAA;;KA1GH,KAAK,MAAA,cAAA,CAAc,CAAC;KACpB,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,KAAI,EAAA,OAAU,MAAA,EAAA,CAAE,CAAC,QAAA,YAAY,IAAI,KAAK,QAAA,UAAS,CAAA,CAAA;KACzD,OAAK,eAAA,CAAG,MAAA,cAAA,CAAc,CAAC,OAAK;MAAA,OAAA,GAAc,MAAK;MAAA,UAAgB,MAAA,SAAA;MAAS,WAAA,GAAgB,MAAA,aAAA,EAAa;KAAA,CAAA,CAAA;KACrG,UAAM,OAAA,QAAA,OAAA,OAAA,GAAA,SAAE,MAAA,cAAA,CAAc,CAAC,YAAf,MAAA,cAAA,CAAc,CAAC,SAAQ,GAAA,IAAA;KAC/B,WAAO,CAAA,OAAA,QAAA,OAAA,MAAA,SAAA,eAAA,WAAa,MAAA,aAAA,CAAa,CAAA,IAAA,GAAA,CAAA,SAAA,CAAA,GAAA,CAAA,IAAA,CAAA,IAAA,OAAA,QAAA,OAAA,MAAA,SAAA,eAAA,WACX,MAAA,aAAA,CAAa,CAAA,KAAA,GAAA,CAAA,SAAA,CAAA,GAAA,CAAA,MAAA,CAAA,EAAA;QAG5B,MAAA,SAAA,KAAA,UAAA,GADR,mBAyDM,OAAA;;cAvDA;KAAJ,KAAI;0BAEJ,mBAoDW,UAAA,MAAA,WAnDuB,MAAA,cAAA,IAAxB,QAAQ,gBAAW;6DACrB,OAAO,SAAK,SAAa,cAAA,GAAA,CAE/B,mBAYM,OAZN,YAYM,CARJ,WAOO,KAAA,QAAA,gBAPP,WAOO,EAAA,SAAA,KAAA,GAAA;MAAA,OALY,OAAO;MAAK,OAAS,OAAO;KAAK,CAAA,SAK7C,CAHL,mBAEM,OAFN,YAEM,gBADD,OAAO,KAAK,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,IAAA,UAAA,IAAA,GAIrB,mBAkCY,UAAA,MAAA,WAjCK,OAAO,QAAf,SAAI;0BADb,YAkCY,mBAAA;OAhCT,KAAK,MAAA,aAAA,CAAa,CAAC,IAAI,CAAA,EAAG,SAAQ;OAClC,QAAQ,MAAA,YAAA,CAAY,CAAC,IAAI;OACzB,iBAAe,MAAA,YAAA,CAAY,CAAC,IAAI;OAChC,MAAM,QAAA,QAAK,OAAU,KAAA;OACrB,UAAU,MAAA,cAAA,CAAc,CAAC,IAAI;OAC9B,UAAS;OACT,SAAQ;OACP,oBAAkB,MAAA,yBAAA,CAAyB,CAAC,QAAQ,IAAI,MAAM,MAAA,gBAAA;OAC9D,iBAAe,MAAA,cAAA,CAAc,CAAC,IAAI,KAAK,KAAA;OACvC,OAAK,eAAA,GAAuB,MAAA,gBAAA,IAAgB,CAAI,MAAA,YAAA,CAAY,CAAC,IAAI,KAAK,MAAA,yBAAA,CAAyB,CAAC,QAAQ,IAAI,MAAM,MAAA,gBAAA,EAAA,CAAA;OAGlH,UAAK,WAAA,CAAG,MAAA,cAAA,CAAc,CAAC,IAAI,KAAK,SAAS,IAAI;;OAEnC,SAAO,cAId,CAHF,WAGE,KAAA,QAAA,gBAHF,WAGE,EAAA,SAAA,KAAA,GAAA;QAAA,UADoB,MAAA,cAAA,CAAc,CAAC,IAAI;QAAG;QAAI,QAAU,MAAA,YAAA,CAAY,CAAC,IAAI;OAAA,CAAA,CAAA,CAAA,CAAA;OASlE,QAAM,cAIb,CAHF,WAGE,KAAA,QAAA,eAHF,WAGE,EAAA,SAAA,KAAA,GAAA;QAAA,UADoB,MAAA,cAAA,CAAc,CAAC,IAAI;QAAG;QAAI,QAAU,MAAA,YAAA,CAAY,CAAC,IAAI;OAAA,CAAA,CAAA,CAAA,CAAA;8BAJtE,CALP,WAKO,KAAA,QAAA,QALP,WAKO,EAAA,SAAA,KAAA,GAAA;QAAA,UAHe,MAAA,cAAA,CAAc,CAAC,IAAI;QAAG;QAAI,QAAU,MAAA,YAAA,CAAY,CAAC,IAAI;OAAA,CAAA,SAGpE,CAAA,gBAAA,gBADF,MAAA,OAAA,CAAO,CAAC,IAAI,CAAA,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA;;;;;;;;;;;;;sCAWvB,mBAwCM,OAxCN,WAwCM,EAAA,KAAA,EAAA,GAtCI,MAAA,YAAA,GAAY;cAChB;KAAJ,KAAI;2BAEJ,mBAkCY,UAAA,MAAA,WAjCiB,MAAA,YAAA,IAAY,EAA9B,MAAM,aAAM;yBADvB,YAkCY,mBAAA;MAhCT,KAAK,MAAA,aAAA,CAAa,CAAC,IAAI,CAAA,EAAG,SAAQ;MAClC,QAAQ,MAAA,YAAA,CAAY,CAAC,IAAI;MACzB,iBAAe,MAAA,YAAA,CAAY,CAAC,IAAI;MAChC,MAAM,QAAA,QAAK,OAAU,KAAA;MACrB,UAAU,MAAA,cAAA,CAAc,CAAC,IAAI;MAC9B,UAAS;MACT,SAAQ;MACP,oBAAkB,MAAA,gBAAA,MAAqB;MACvC,iBAAe,MAAA,cAAA,CAAc,CAAC,IAAI,KAAK,KAAA;MACvC,OAAK,eAAA,GAAqB,MAAA,gBAAA,IAAgB,CAAI,MAAA,YAAA,CAAY,CAAC,IAAI,KAAK,MAAA,gBAAA,MAAqB,OAAA,CAAA;MAGzF,UAAK,WAAA,CAAG,MAAA,cAAA,CAAc,CAAC,IAAI,KAAK,SAAS,IAAI;;MAEnC,SAAO,cAId,CAHF,WAGE,KAAA,QAAA,gBAHF,WAGE,EAAA,SAAA,KAAA,GAAA;OAAA,UADoB,MAAA,cAAA,CAAc,CAAC,IAAI;OAAG;OAAI,QAAU,MAAA,YAAA,CAAY,CAAC,IAAI;MAAA,CAAA,CAAA,CAAA,CAAA;MASlE,QAAM,cAIb,CAHF,WAGE,KAAA,QAAA,eAHF,WAGE,EAAA,SAAA,KAAA,GAAA;OAAA,UADoB,MAAA,cAAA,CAAc,CAAC,IAAI;OAAG;OAAI,QAAU,MAAA,YAAA,CAAY,CAAC,IAAI;MAAA,CAAA,CAAA,CAAA,CAAA;6BAJtE,CALP,WAKO,KAAA,QAAA,QALP,WAKO,EAAA,SAAA,KAAA,GAAA;OAAA,UAHe,MAAA,cAAA,CAAc,CAAC,IAAI;OAAG;OAAI,QAAU,MAAA,YAAA,CAAY,CAAC,IAAI;MAAA,CAAA,SAGpE,CAAA,gBAAA,gBADF,MAAA,OAAA,CAAO,CAAC,IAAI,CAAA,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA;;;;;;;;;;;;+BAaT,QAAA,cAAA,UAAA,GADd,mBAcM,OAAA;;KAZH,OAAK,eAAA;MAAA,OAAA,GAAc,MAAK;MAAA,UAAgB,MAAA,SAAA;KAAS,CAAA;KACjD,OAAK,eAAE,QAAA,YAAY,QAAQ,QAAA,SAAS;QAErC,WAQO,KAAA,QAAA,WAAA,CAAA,SAAA,CAAA,CANI,QAAA,eAAA,UAAA,GADT,mBAMM,OANN,YAMM,gBADD,QAAA,UAAU,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,GAYX,MAAM,UAAA,UAAA,GADd,mBAMM,OANN,YAMM,CADJ,WAAsB,KAAA,QAAA,QAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,GAAA,CAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"RuiDataTableCell.js","names":[],"sources":["../../../../src/components/tables/data-table/RuiDataTableCell.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object\">\nimport type { TableColumn } from '@/components/tables/RuiTableHead.vue';\nimport { useDataTableColumns, useDataTableExpansion, useDataTableStyling } from '@/components/tables/data-table/context';\nimport RuiExpandButton from '@/components/tables/RuiExpandButton.vue';\n\nconst { column } = defineProps<{\n column: TableColumn<T>;\n row: T;\n index: number;\n rowId?: T[keyof T];\n}>();\n\ndefineSlots<{\n default?: (props: { column: TableColumn<T>; row: T; index: number }) => any;\n}>();\n\nconst { cellValue, columnAttr } = useDataTableColumns<T>();\nconst { isExpanded, onToggleExpand } = useDataTableExpansion<T>();\nconst { isMobile } = useDataTableStyling();\n\nconst mobileLabel = computed<string>(() => {\n const label = column[columnAttr];\n return label === undefined || label === null ? '' : String(label);\n});\nconst showMobileLabel = computed<boolean>(() => get(isMobile) && column.key !== 'expand' && get(mobileLabel).length > 0);\n</script>\n\n<template>\n <td\n :class=\"[\n column.tdClass,\n column.cellClass,\n isMobile ? 'flex items-start justify-between gap-4 text-right' : '',\n ]\"\n :colspan=\"column.colspan ?? 1\"\n :rowspan=\"column.rowspan ?? 1\"\n >\n <span\n v-if=\"showMobileLabel\"\n aria-hidden=\"true\"\n class=\"shrink-0 text-left font-medium text-rui-text-secondary\"\n data-id=\"cell-label\"\n >\n {{ mobileLabel }}\n </span>\n <div :class=\"isMobile ? 'min-w-0 ml-auto' : 'contents'\">\n <slot\n :column=\"column\"\n :row=\"row\"\n :index=\"index\"\n >\n <RuiExpandButton\n v-if=\"column.key === 'expand'\"\n :expanded=\"rowId !== undefined && isExpanded(rowId)\"\n @click=\"onToggleExpand(row)\"\n />\n <template v-else>\n {{ cellValue(row, column.key) }}\n </template>\n </slot>\n </div>\n </td>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"RuiDataTableCell.js","names":[],"sources":["../../../../src/components/tables/data-table/RuiDataTableCell.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object\">\nimport type { TableColumn } from '@/components/tables/RuiTableHead.vue';\nimport { useDataTableColumns, useDataTableExpansion, useDataTableStyling } from '@/components/tables/data-table/context';\nimport RuiExpandButton from '@/components/tables/RuiExpandButton.vue';\n\nconst { column } = defineProps<{\n column: TableColumn<T>;\n row: T;\n index: number;\n rowId?: T[keyof T];\n}>();\n\ndefineSlots<{\n default?: (props: { column: TableColumn<T>; row: T; index: number }) => any;\n}>();\n\nconst { cellValue, columnAttr, itemSlotKeys } = useDataTableColumns<T>();\nconst { isExpanded, onToggleExpand } = useDataTableExpansion<T>();\nconst { isMobile } = useDataTableStyling();\n\n// The built-in toggle is only a fallback for consumers that do not drive the\n// expand column themselves. A consumer that provides `#item.expand` owns the\n// decision per row, including rendering nothing for a row that cannot expand,\n// so slot emptiness must not fall back to a toggle they deliberately withheld.\nconst ownsExpandColumn = computed<boolean>(() => itemSlotKeys.has('expand'));\n\nconst mobileLabel = computed<string>(() => {\n const label = column[columnAttr];\n return label === undefined || label === null ? '' : String(label);\n});\nconst showMobileLabel = computed<boolean>(() => get(isMobile) && column.key !== 'expand' && get(mobileLabel).length > 0);\n</script>\n\n<template>\n <td\n :class=\"[\n column.tdClass,\n column.cellClass,\n isMobile ? 'flex items-start justify-between gap-4 text-right' : '',\n ]\"\n :colspan=\"column.colspan ?? 1\"\n :rowspan=\"column.rowspan ?? 1\"\n >\n <span\n v-if=\"showMobileLabel\"\n aria-hidden=\"true\"\n class=\"shrink-0 text-left font-medium text-rui-text-secondary\"\n data-id=\"cell-label\"\n >\n {{ mobileLabel }}\n </span>\n <div :class=\"isMobile ? 'min-w-0 ml-auto' : 'contents'\">\n <slot\n :column=\"column\"\n :row=\"row\"\n :index=\"index\"\n >\n <RuiExpandButton\n v-if=\"column.key === 'expand' && !ownsExpandColumn\"\n :expanded=\"rowId !== undefined && isExpanded(rowId)\"\n @click=\"onToggleExpand(row)\"\n />\n <template v-else-if=\"column.key !== 'expand'\">\n {{ cellValue(row, column.key) }}\n </template>\n </slot>\n </div>\n </td>\n</template>\n"],"mappings":""}
@@ -19,9 +19,10 @@ var RuiDataTableCell_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
19
19
  rowId: {}
20
20
  },
21
21
  setup(__props) {
22
- const { cellValue, columnAttr } = useDataTableColumns();
22
+ const { cellValue, columnAttr, itemSlotKeys } = useDataTableColumns();
23
23
  const { isExpanded, onToggleExpand } = useDataTableExpansion();
24
24
  const { isMobile } = useDataTableStyling();
25
+ const ownsExpandColumn = computed(() => itemSlotKeys.has("expand"));
25
26
  const mobileLabel = computed(() => {
26
27
  const label = __props.column[columnAttr];
27
28
  return label === void 0 || label === null ? "" : String(label);
@@ -40,11 +41,11 @@ var RuiDataTableCell_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
40
41
  column: __props.column,
41
42
  row: __props.row,
42
43
  index: __props.index
43
- }, () => [__props.column.key === "expand" ? (openBlock(), createBlock(RuiExpandButton_default, {
44
+ }, () => [__props.column.key === "expand" && !unref(ownsExpandColumn) ? (openBlock(), createBlock(RuiExpandButton_default, {
44
45
  key: 0,
45
46
  expanded: __props.rowId !== void 0 && unref(isExpanded)(__props.rowId),
46
47
  onClick: _cache[0] || (_cache[0] = ($event) => unref(onToggleExpand)(__props.row))
47
- }, null, 8, ["expanded"])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(unref(cellValue)(__props.row, __props.column.key)), 1)], 64))])], 2)], 10, _hoisted_1);
48
+ }, null, 8, ["expanded"])) : __props.column.key !== "expand" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createTextVNode(toDisplayString(unref(cellValue)(__props.row, __props.column.key)), 1)], 64)) : createCommentVNode("", true)])], 2)], 10, _hoisted_1);
48
49
  };
49
50
  }
50
51
  });
@@ -1 +1 @@
1
- {"version":3,"file":"RuiDataTableCell.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../../src/components/tables/data-table/RuiDataTableCell.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object\">\nimport type { TableColumn } from '@/components/tables/RuiTableHead.vue';\nimport { useDataTableColumns, useDataTableExpansion, useDataTableStyling } from '@/components/tables/data-table/context';\nimport RuiExpandButton from '@/components/tables/RuiExpandButton.vue';\n\nconst { column } = defineProps<{\n column: TableColumn<T>;\n row: T;\n index: number;\n rowId?: T[keyof T];\n}>();\n\ndefineSlots<{\n default?: (props: { column: TableColumn<T>; row: T; index: number }) => any;\n}>();\n\nconst { cellValue, columnAttr } = useDataTableColumns<T>();\nconst { isExpanded, onToggleExpand } = useDataTableExpansion<T>();\nconst { isMobile } = useDataTableStyling();\n\nconst mobileLabel = computed<string>(() => {\n const label = column[columnAttr];\n return label === undefined || label === null ? '' : String(label);\n});\nconst showMobileLabel = computed<boolean>(() => get(isMobile) && column.key !== 'expand' && get(mobileLabel).length > 0);\n</script>\n\n<template>\n <td\n :class=\"[\n column.tdClass,\n column.cellClass,\n isMobile ? 'flex items-start justify-between gap-4 text-right' : '',\n ]\"\n :colspan=\"column.colspan ?? 1\"\n :rowspan=\"column.rowspan ?? 1\"\n >\n <span\n v-if=\"showMobileLabel\"\n aria-hidden=\"true\"\n class=\"shrink-0 text-left font-medium text-rui-text-secondary\"\n data-id=\"cell-label\"\n >\n {{ mobileLabel }}\n </span>\n <div :class=\"isMobile ? 'min-w-0 ml-auto' : 'contents'\">\n <slot\n :column=\"column\"\n :row=\"row\"\n :index=\"index\"\n >\n <RuiExpandButton\n v-if=\"column.key === 'expand'\"\n :expanded=\"rowId !== undefined && isExpanded(rowId)\"\n @click=\"onToggleExpand(row)\"\n />\n <template v-else>\n {{ cellValue(row, column.key) }}\n </template>\n </slot>\n </div>\n </td>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAgBA,MAAM,EAAE,WAAW,eAAe,oBAAuB;EACzD,MAAM,EAAE,YAAY,mBAAmB,sBAAyB;EAChE,MAAM,EAAE,aAAa,oBAAoB;EAEzC,MAAM,cAAc,eAAuB;GACzC,MAAM,QAAQ,QAAA,OAAO;GACrB,OAAO,UAAU,KAAA,KAAa,UAAU,OAAO,KAAK,OAAO,KAAK;EAClE,CAAC;EACD,MAAM,kBAAkB,eAAwB,IAAI,QAAQ,KAAK,QAAA,OAAO,QAAQ,YAAY,IAAI,WAAW,CAAC,CAAC,SAAS,CAAC;;uBAIrH,mBAiCK,MAAA;IAhCF,OAAK,eAAA;KAAU,QAAA,OAAO;KAAe,QAAA,OAAO;KAAiB,MAAA,QAAA,IAAQ,sDAAA;;IAKrE,SAAS,QAAA,OAAO,WAAO;IACvB,SAAS,QAAA,OAAO,WAAO;OAGhB,MAAA,eAAA,KAAA,UAAA,GADR,mBAOO,QAPP,YAOO,gBADF,MAAA,WAAA,CAAW,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,GAEhB,mBAeM,OAAA,EAfA,OAAK,eAAE,MAAA,QAAA,IAAQ,oBAAA,UAAA,EAAA,GAAA,CACnB,WAaO,KAAA,QAAA,WAAA;IAZJ,QAAQ,QAAA;IACR,KAAK,QAAA;IACL,OAAO,QAAA;YAUH,CAPG,QAAA,OAAO,QAAG,YAAA,UAAA,GADlB,YAIE,yBAAA;;IAFC,UAAU,QAAA,UAAU,KAAA,KAAa,MAAA,UAAA,CAAU,CAAC,QAAA,KAAK;IACjD,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,cAAA,CAAc,CAAC,QAAA,GAAG;8CAE5B,mBAEW,UAAA,EAAA,KAAA,EAAA,GAAA,CAAA,gBAAA,gBADN,MAAA,SAAA,CAAS,CAAC,QAAA,KAAK,QAAA,OAAO,GAAG,CAAA,GAAA,CAAA,CAAA,GAAA,EAAA,EAAA,CAAA,CAAA,GAAA,CAAA,CAAA,GAAA,IAAA,UAAA"}
1
+ {"version":3,"file":"RuiDataTableCell.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../../src/components/tables/data-table/RuiDataTableCell.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object\">\nimport type { TableColumn } from '@/components/tables/RuiTableHead.vue';\nimport { useDataTableColumns, useDataTableExpansion, useDataTableStyling } from '@/components/tables/data-table/context';\nimport RuiExpandButton from '@/components/tables/RuiExpandButton.vue';\n\nconst { column } = defineProps<{\n column: TableColumn<T>;\n row: T;\n index: number;\n rowId?: T[keyof T];\n}>();\n\ndefineSlots<{\n default?: (props: { column: TableColumn<T>; row: T; index: number }) => any;\n}>();\n\nconst { cellValue, columnAttr, itemSlotKeys } = useDataTableColumns<T>();\nconst { isExpanded, onToggleExpand } = useDataTableExpansion<T>();\nconst { isMobile } = useDataTableStyling();\n\n// The built-in toggle is only a fallback for consumers that do not drive the\n// expand column themselves. A consumer that provides `#item.expand` owns the\n// decision per row, including rendering nothing for a row that cannot expand,\n// so slot emptiness must not fall back to a toggle they deliberately withheld.\nconst ownsExpandColumn = computed<boolean>(() => itemSlotKeys.has('expand'));\n\nconst mobileLabel = computed<string>(() => {\n const label = column[columnAttr];\n return label === undefined || label === null ? '' : String(label);\n});\nconst showMobileLabel = computed<boolean>(() => get(isMobile) && column.key !== 'expand' && get(mobileLabel).length > 0);\n</script>\n\n<template>\n <td\n :class=\"[\n column.tdClass,\n column.cellClass,\n isMobile ? 'flex items-start justify-between gap-4 text-right' : '',\n ]\"\n :colspan=\"column.colspan ?? 1\"\n :rowspan=\"column.rowspan ?? 1\"\n >\n <span\n v-if=\"showMobileLabel\"\n aria-hidden=\"true\"\n class=\"shrink-0 text-left font-medium text-rui-text-secondary\"\n data-id=\"cell-label\"\n >\n {{ mobileLabel }}\n </span>\n <div :class=\"isMobile ? 'min-w-0 ml-auto' : 'contents'\">\n <slot\n :column=\"column\"\n :row=\"row\"\n :index=\"index\"\n >\n <RuiExpandButton\n v-if=\"column.key === 'expand' && !ownsExpandColumn\"\n :expanded=\"rowId !== undefined && isExpanded(rowId)\"\n @click=\"onToggleExpand(row)\"\n />\n <template v-else-if=\"column.key !== 'expand'\">\n {{ cellValue(row, column.key) }}\n </template>\n </slot>\n </div>\n </td>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAgBA,MAAM,EAAE,WAAW,YAAY,iBAAiB,oBAAuB;EACvE,MAAM,EAAE,YAAY,mBAAmB,sBAAyB;EAChE,MAAM,EAAE,aAAa,oBAAoB;EAMzC,MAAM,mBAAmB,eAAwB,aAAa,IAAI,QAAQ,CAAC;EAE3E,MAAM,cAAc,eAAuB;GACzC,MAAM,QAAQ,QAAA,OAAO;GACrB,OAAO,UAAU,KAAA,KAAa,UAAU,OAAO,KAAK,OAAO,KAAK;EAClE,CAAC;EACD,MAAM,kBAAkB,eAAwB,IAAI,QAAQ,KAAK,QAAA,OAAO,QAAQ,YAAY,IAAI,WAAW,CAAC,CAAC,SAAS,CAAC;;uBAIrH,mBAiCK,MAAA;IAhCF,OAAK,eAAA;KAAU,QAAA,OAAO;KAAe,QAAA,OAAO;KAAiB,MAAA,QAAA,IAAQ,sDAAA;;IAKrE,SAAS,QAAA,OAAO,WAAO;IACvB,SAAS,QAAA,OAAO,WAAO;OAGhB,MAAA,eAAA,KAAA,UAAA,GADR,mBAOO,QAPP,YAOO,gBADF,MAAA,WAAA,CAAW,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,GAEhB,mBAeM,OAAA,EAfA,OAAK,eAAE,MAAA,QAAA,IAAQ,oBAAA,UAAA,EAAA,GAAA,CACnB,WAaO,KAAA,QAAA,WAAA;IAZJ,QAAQ,QAAA;IACR,KAAK,QAAA;IACL,OAAO,QAAA;YAUH,CAPG,QAAA,OAAO,QAAG,YAAA,CAAkB,MAAA,gBAAA,KAAA,UAAA,GADpC,YAIE,yBAAA;;IAFC,UAAU,QAAA,UAAU,KAAA,KAAa,MAAA,UAAA,CAAU,CAAC,QAAA,KAAK;IACjD,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,cAAA,CAAc,CAAC,QAAA,GAAG;gCAEP,QAAA,OAAO,QAAG,YAAA,UAAA,GAA/B,mBAEW,UAAA,EAAA,KAAA,EAAA,GAAA,CAAA,gBAAA,gBADN,MAAA,SAAA,CAAS,CAAC,QAAA,KAAK,QAAA,OAAO,GAAG,CAAA,GAAA,CAAA,CAAA,GAAA,EAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,CAAA,GAAA,CAAA,CAAA,GAAA,IAAA,UAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"RuiDataTableRow.js","names":[],"sources":["../../../../src/components/tables/data-table/RuiDataTableRow.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object\">\nimport type { TableColumn } from '@/components/tables/RuiTableHead.vue';\nimport RuiCheckbox from '@/components/forms/checkbox/RuiCheckbox.vue';\nimport { useDataTableColumns, useDataTableExpansion, useDataTableRowIdentity, useDataTableSelection, useDataTableStyling } from '@/components/tables/data-table/context';\nimport RuiDataTableCell from '@/components/tables/data-table/RuiDataTableCell.vue';\nimport RuiDataTableExpandedRow from '@/components/tables/data-table/RuiDataTableExpandedRow.vue';\nimport RuiExpandButton from '@/components/tables/RuiExpandButton.vue';\n\nconst {\n row,\n index,\n} = defineProps<{\n row: T;\n index: number;\n}>();\n\nconst slots = defineSlots<Partial<\n Record<`item.${string}`, (props: { column: TableColumn<T>; row: T; index: number }) => any> & {\n 'expanded-item'?: (props: { row: T; index: number }) => any;\n }\n>>();\n\nconst { classes, dense, isMobile } = useDataTableStyling();\nconst { cellValue, columns, itemSlotKeys } = useDataTableColumns<T>();\nconst { isDisabledRow, isSelected, onCheckboxClick, onSelect, selectedData } = useDataTableSelection<T>();\nconst { expandable, isExpanded, onToggleExpand } = useDataTableExpansion<T>();\nconst { getRowId, itemClass } = useDataTableRowIdentity<T>();\n\nconst rowId = computed<T[keyof T]>(() => getRowId(row));\nconst selected = computed<boolean>(() => isSelected(get(rowId)));\nconst disabled = computed<boolean>(() => isDisabledRow(get(rowId)));\nconst expanded = computed<boolean>(() => get(expandable) && !!slots['expanded-item'] && isExpanded(get(rowId)));\nconst rowClass = computed<string>(() => typeof itemClass === 'string' ? itemClass : itemClass(row));\n\n// A column is pinned to the mobile card header when it is flagged `mobileHeader`\n// (typically an action column) or when it is the auto-generated expand column.\nfunction isMobileHeaderColumn(column: TableColumn<T>): boolean {\n return !!column.mobileHeader || column.key === 'expand';\n}\n\n// A pinned column only earns a spot in the header if it actually renders\n// something: the expand toggle, a provided item slot, or a non-empty cell value.\n// Without this, a pinned column with no content (e.g. an action column whose\n// `#item.action` slot is not provided in a nested table) would leave an empty\n// header bar with just its divider line.\nfunction mobileHeaderColumnHasContent(column: TableColumn<T>): boolean {\n if (column.key === 'expand')\n return true;\n if (itemSlotKeys.has(column.key.toString()))\n return true;\n const value = cellValue(row, column.key);\n return value !== undefined && value !== null && value !== '';\n}\n\n// In the stacked mobile layout, header columns render in the card header row\n// instead of as a label/value pair. Everything else stacks below in the body.\nconst mobileHeaderColumns = computed<TableColumn<T>[]>(() =>\n get(isMobile) ? get(columns).filter(column => isMobileHeaderColumn(column) && mobileHeaderColumnHasContent(column)) : [],\n);\nconst bodyColumns = computed<TableColumn<T>[]>(() =>\n get(isMobile) ? get(columns).filter(column => !isMobileHeaderColumn(column)) : get(columns),\n);\nconst showMobileHeader = computed<boolean>(() =>\n get(isMobile) && (!!get(selectedData) || get(mobileHeaderColumns).length > 0),\n);\n\n// `!border-y` defeats the `divide-y-0` on the mobile tbody, which otherwise\n// zeroes the top/bottom border on every card except the first one. When the\n// card is expanded, its bottom edge flattens so the expanded panel attaches\n// flush beneath it.\nconst mobileCardClass = computed<string>(() => {\n const shared = 'relative flex flex-col border !border-y border-black/[0.12] dark:border-white/[0.12] overflow-hidden';\n // These bindings are not run through tailwind-merge, so avoid emitting\n // conflicting utilities (mb-0 vs mb-3, rounded-b-none vs rounded-lg): pick the\n // right one per state instead.\n return get(expanded)\n ? `${shared} rounded-t-lg mb-0`\n : `${shared} rounded-lg mb-3 last:mb-0`;\n});\n</script>\n\n<template>\n <tr\n :class=\"[selected ? classes.trSelected : classes.tr, rowClass, isMobile ? mobileCardClass : '']\"\n :aria-selected=\"selectedData ? selected : undefined\"\n data-id=\"row\"\n >\n <!-- Mobile card header: checkbox on the left, pinned action columns on the right -->\n <td\n v-if=\"showMobileHeader\"\n class=\"flex items-center justify-between gap-2 px-4 py-2 border-b border-black/[0.12] dark:border-white/[0.12]\"\n data-id=\"mobile-card-header\"\n >\n <RuiCheckbox\n v-if=\"selectedData\"\n :data-id=\"`table-toggle-check-${index}`\"\n :model-value=\"selected\"\n :disabled=\"disabled\"\n :size=\"dense ? 'sm' : undefined\"\n color=\"primary\"\n class=\"select-none\"\n hide-details\n @update:model-value=\"onSelect($event, rowId, true)\"\n @click=\"onCheckboxClick($event, rowId, index)\"\n />\n <span v-else />\n\n <div\n v-if=\"mobileHeaderColumns.length > 0\"\n class=\"flex items-center gap-1 min-w-0\"\n >\n <template\n v-for=\"(column, headerIndex) in mobileHeaderColumns\"\n :key=\"`header-${headerIndex}`\"\n >\n <RuiExpandButton\n v-if=\"column.key === 'expand'\"\n :expanded=\"isExpanded(rowId)\"\n @click=\"onToggleExpand(row)\"\n />\n <slot\n v-else-if=\"itemSlotKeys.has(column.key.toString())\"\n :name=\"`item.${column.key.toString()}`\"\n :column=\"column\"\n :row=\"row\"\n :index=\"index\"\n />\n <template v-else>\n {{ cellValue(row, column.key) }}\n </template>\n </template>\n </div>\n </td>\n\n <!-- Desktop checkbox cell -->\n <td\n v-else-if=\"selectedData && !isMobile\"\n :class=\"classes.checkbox\"\n colspan=\"1\"\n rowspan=\"1\"\n >\n <RuiCheckbox\n :data-id=\"`table-toggle-check-${index}`\"\n :model-value=\"selected\"\n :disabled=\"disabled\"\n :size=\"dense ? 'sm' : undefined\"\n color=\"primary\"\n class=\"select-none\"\n hide-details\n @update:model-value=\"onSelect($event, rowId, true)\"\n @click=\"onCheckboxClick($event, rowId, index)\"\n />\n </td>\n\n <RuiDataTableCell\n v-for=\"(column, subIndex) in bodyColumns\"\n :key=\"subIndex\"\n :column=\"column\"\n :row=\"row\"\n :index=\"index\"\n :row-id=\"rowId\"\n >\n <template\n v-if=\"itemSlotKeys.has(column.key.toString())\"\n #default=\"slotData\"\n >\n <slot\n :name=\"`item.${column.key.toString()}`\"\n v-bind=\"slotData\"\n />\n </template>\n </RuiDataTableCell>\n </tr>\n\n <RuiDataTableExpandedRow\n v-if=\"expanded\"\n :row=\"row\"\n :index=\"index\"\n >\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 </RuiDataTableExpandedRow>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"RuiDataTableRow.js","names":[],"sources":["../../../../src/components/tables/data-table/RuiDataTableRow.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object\">\nimport type { TableColumn } from '@/components/tables/RuiTableHead.vue';\nimport RuiCheckbox from '@/components/forms/checkbox/RuiCheckbox.vue';\nimport { useDataTableColumns, useDataTableExpansion, useDataTableRowIdentity, useDataTableSelection, useDataTableStyling } from '@/components/tables/data-table/context';\nimport RuiDataTableCell from '@/components/tables/data-table/RuiDataTableCell.vue';\nimport RuiDataTableExpandedRow from '@/components/tables/data-table/RuiDataTableExpandedRow.vue';\nimport RuiExpandButton from '@/components/tables/RuiExpandButton.vue';\n\nconst {\n row,\n index,\n} = defineProps<{\n row: T;\n index: number;\n}>();\n\nconst slots = defineSlots<Partial<\n Record<`item.${string}`, (props: { column: TableColumn<T>; row: T; index: number }) => any> & {\n 'expanded-item'?: (props: { row: T; index: number }) => any;\n }\n>>();\n\nconst { classes, dense, isMobile } = useDataTableStyling();\nconst { cellValue, columns, itemSlotKeys } = useDataTableColumns<T>();\nconst { isDisabledRow, isSelected, onCheckboxClick, onSelect, selectedData } = useDataTableSelection<T>();\nconst { expandable, isExpanded, onToggleExpand } = useDataTableExpansion<T>();\nconst { getRowId, itemClass } = useDataTableRowIdentity<T>();\n\nconst rowId = computed<T[keyof T]>(() => getRowId(row));\nconst selected = computed<boolean>(() => isSelected(get(rowId)));\nconst disabled = computed<boolean>(() => isDisabledRow(get(rowId)));\nconst expanded = computed<boolean>(() => get(expandable) && !!slots['expanded-item'] && isExpanded(get(rowId)));\nconst rowClass = computed<string>(() => typeof itemClass === 'string' ? itemClass : itemClass(row));\n\n// A column is pinned to the mobile card header when it is flagged `mobileHeader`\n// (typically an action column) or when it is the auto-generated expand column.\nfunction isMobileHeaderColumn(column: TableColumn<T>): boolean {\n return !!column.mobileHeader || column.key === 'expand';\n}\n\n// A pinned column only earns a spot in the header if it actually renders\n// something: the expand toggle, a provided item slot, or a non-empty cell value.\n// Without this, a pinned column with no content (e.g. an action column whose\n// `#item.action` slot is not provided in a nested table) would leave an empty\n// header bar with just its divider line.\nfunction mobileHeaderColumnHasContent(column: TableColumn<T>): boolean {\n if (column.key === 'expand')\n return true;\n if (itemSlotKeys.has(column.key.toString()))\n return true;\n const value = cellValue(row, column.key);\n return value !== undefined && value !== null && value !== '';\n}\n\n// In the stacked mobile layout, header columns render in the card header row\n// instead of as a label/value pair. Everything else stacks below in the body.\nconst mobileHeaderColumns = computed<TableColumn<T>[]>(() =>\n get(isMobile) ? get(columns).filter(column => isMobileHeaderColumn(column) && mobileHeaderColumnHasContent(column)) : [],\n);\nconst bodyColumns = computed<TableColumn<T>[]>(() =>\n get(isMobile) ? get(columns).filter(column => !isMobileHeaderColumn(column)) : get(columns),\n);\nconst showMobileHeader = computed<boolean>(() =>\n get(isMobile) && (!!get(selectedData) || get(mobileHeaderColumns).length > 0),\n);\n\n// `!border-y` defeats the `divide-y-0` on the mobile tbody, which otherwise\n// zeroes the top/bottom border on every card except the first one. When the\n// card is expanded, its bottom edge flattens so the expanded panel attaches\n// flush beneath it.\nconst mobileCardClass = computed<string>(() => {\n const shared = 'relative flex flex-col border !border-y border-black/[0.12] dark:border-white/[0.12] overflow-hidden';\n // These bindings are not run through tailwind-merge, so avoid emitting\n // conflicting utilities (mb-0 vs mb-3, rounded-b-none vs rounded-lg): pick the\n // right one per state instead.\n return get(expanded)\n ? `${shared} rounded-t-lg mb-0`\n : `${shared} rounded-lg mb-3 last:mb-0`;\n});\n</script>\n\n<template>\n <tr\n :class=\"[selected ? classes.trSelected : classes.tr, rowClass, isMobile ? mobileCardClass : '']\"\n :aria-selected=\"selectedData ? selected : undefined\"\n data-id=\"row\"\n >\n <!-- Mobile card header: checkbox on the left, pinned action columns on the right -->\n <td\n v-if=\"showMobileHeader\"\n class=\"flex items-center justify-between gap-2 px-4 py-2 border-b border-black/[0.12] dark:border-white/[0.12]\"\n data-id=\"mobile-card-header\"\n >\n <RuiCheckbox\n v-if=\"selectedData\"\n :data-id=\"`table-toggle-check-${index}`\"\n :model-value=\"selected\"\n :disabled=\"disabled\"\n :size=\"dense ? 'sm' : undefined\"\n color=\"primary\"\n class=\"select-none\"\n hide-details\n @update:model-value=\"onSelect($event, rowId, true)\"\n @click=\"onCheckboxClick($event, rowId, index)\"\n />\n <span v-else />\n\n <div\n v-if=\"mobileHeaderColumns.length > 0\"\n class=\"flex items-center gap-1 min-w-0\"\n >\n <template\n v-for=\"(column, headerIndex) in mobileHeaderColumns\"\n :key=\"`header-${headerIndex}`\"\n >\n <slot\n v-if=\"itemSlotKeys.has(column.key.toString())\"\n :name=\"`item.${column.key.toString()}`\"\n :column=\"column\"\n :row=\"row\"\n :index=\"index\"\n />\n <RuiExpandButton\n v-else-if=\"column.key === 'expand'\"\n :expanded=\"isExpanded(rowId)\"\n @click=\"onToggleExpand(row)\"\n />\n <template v-else>\n {{ cellValue(row, column.key) }}\n </template>\n </template>\n </div>\n </td>\n\n <!-- Desktop checkbox cell -->\n <td\n v-else-if=\"selectedData && !isMobile\"\n :class=\"classes.checkbox\"\n colspan=\"1\"\n rowspan=\"1\"\n >\n <RuiCheckbox\n :data-id=\"`table-toggle-check-${index}`\"\n :model-value=\"selected\"\n :disabled=\"disabled\"\n :size=\"dense ? 'sm' : undefined\"\n color=\"primary\"\n class=\"select-none\"\n hide-details\n @update:model-value=\"onSelect($event, rowId, true)\"\n @click=\"onCheckboxClick($event, rowId, index)\"\n />\n </td>\n\n <RuiDataTableCell\n v-for=\"(column, subIndex) in bodyColumns\"\n :key=\"subIndex\"\n :column=\"column\"\n :row=\"row\"\n :index=\"index\"\n :row-id=\"rowId\"\n >\n <template\n v-if=\"itemSlotKeys.has(column.key.toString())\"\n #default=\"slotData\"\n >\n <slot\n :name=\"`item.${column.key.toString()}`\"\n v-bind=\"slotData\"\n />\n </template>\n </RuiDataTableCell>\n </tr>\n\n <RuiDataTableExpandedRow\n v-if=\"expanded\"\n :row=\"row\"\n :index=\"index\"\n >\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 </RuiDataTableExpandedRow>\n</template>\n"],"mappings":""}
@@ -77,15 +77,15 @@ var RuiDataTableRow_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
77
77
  "disabled",
78
78
  "size"
79
79
  ])) : (openBlock(), createElementBlock("span", _hoisted_3)), unref(mobileHeaderColumns).length > 0 ? (openBlock(), createElementBlock("div", _hoisted_4, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(mobileHeaderColumns), (column, headerIndex) => {
80
- return openBlock(), createElementBlock(Fragment, { key: `header-${headerIndex}` }, [column.key === "expand" ? (openBlock(), createBlock(RuiExpandButton_default, {
81
- key: 0,
82
- expanded: unref(isExpanded)(unref(rowId)),
83
- onClick: _cache[2] || (_cache[2] = ($event) => unref(onToggleExpand)(__props.row))
84
- }, null, 8, ["expanded"])) : unref(itemSlotKeys).has(column.key.toString()) ? renderSlot(_ctx.$slots, `item.${column.key.toString()}`, {
80
+ return openBlock(), createElementBlock(Fragment, { key: `header-${headerIndex}` }, [unref(itemSlotKeys).has(column.key.toString()) ? renderSlot(_ctx.$slots, `item.${column.key.toString()}`, {
85
81
  column,
86
82
  row: __props.row,
87
83
  index: __props.index
88
- }, void 0, void 0, 1) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [createTextVNode(toDisplayString(unref(cellValue)(__props.row, column.key)), 1)], 64))], 64);
84
+ }, void 0, void 0, 0) : column.key === "expand" ? (openBlock(), createBlock(RuiExpandButton_default, {
85
+ key: 1,
86
+ expanded: unref(isExpanded)(unref(rowId)),
87
+ onClick: _cache[2] || (_cache[2] = ($event) => unref(onToggleExpand)(__props.row))
88
+ }, null, 8, ["expanded"])) : (openBlock(), createElementBlock(Fragment, { key: 2 }, [createTextVNode(toDisplayString(unref(cellValue)(__props.row, column.key)), 1)], 64))], 64);
89
89
  }), 128))])) : createCommentVNode("", true)])) : unref(selectedData) && !unref(isMobile) ? (openBlock(), createElementBlock("td", {
90
90
  key: 1,
91
91
  class: normalizeClass(unref(classes).checkbox),
@@ -1 +1 @@
1
- {"version":3,"file":"RuiDataTableRow.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../../src/components/tables/data-table/RuiDataTableRow.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object\">\nimport type { TableColumn } from '@/components/tables/RuiTableHead.vue';\nimport RuiCheckbox from '@/components/forms/checkbox/RuiCheckbox.vue';\nimport { useDataTableColumns, useDataTableExpansion, useDataTableRowIdentity, useDataTableSelection, useDataTableStyling } from '@/components/tables/data-table/context';\nimport RuiDataTableCell from '@/components/tables/data-table/RuiDataTableCell.vue';\nimport RuiDataTableExpandedRow from '@/components/tables/data-table/RuiDataTableExpandedRow.vue';\nimport RuiExpandButton from '@/components/tables/RuiExpandButton.vue';\n\nconst {\n row,\n index,\n} = defineProps<{\n row: T;\n index: number;\n}>();\n\nconst slots = defineSlots<Partial<\n Record<`item.${string}`, (props: { column: TableColumn<T>; row: T; index: number }) => any> & {\n 'expanded-item'?: (props: { row: T; index: number }) => any;\n }\n>>();\n\nconst { classes, dense, isMobile } = useDataTableStyling();\nconst { cellValue, columns, itemSlotKeys } = useDataTableColumns<T>();\nconst { isDisabledRow, isSelected, onCheckboxClick, onSelect, selectedData } = useDataTableSelection<T>();\nconst { expandable, isExpanded, onToggleExpand } = useDataTableExpansion<T>();\nconst { getRowId, itemClass } = useDataTableRowIdentity<T>();\n\nconst rowId = computed<T[keyof T]>(() => getRowId(row));\nconst selected = computed<boolean>(() => isSelected(get(rowId)));\nconst disabled = computed<boolean>(() => isDisabledRow(get(rowId)));\nconst expanded = computed<boolean>(() => get(expandable) && !!slots['expanded-item'] && isExpanded(get(rowId)));\nconst rowClass = computed<string>(() => typeof itemClass === 'string' ? itemClass : itemClass(row));\n\n// A column is pinned to the mobile card header when it is flagged `mobileHeader`\n// (typically an action column) or when it is the auto-generated expand column.\nfunction isMobileHeaderColumn(column: TableColumn<T>): boolean {\n return !!column.mobileHeader || column.key === 'expand';\n}\n\n// A pinned column only earns a spot in the header if it actually renders\n// something: the expand toggle, a provided item slot, or a non-empty cell value.\n// Without this, a pinned column with no content (e.g. an action column whose\n// `#item.action` slot is not provided in a nested table) would leave an empty\n// header bar with just its divider line.\nfunction mobileHeaderColumnHasContent(column: TableColumn<T>): boolean {\n if (column.key === 'expand')\n return true;\n if (itemSlotKeys.has(column.key.toString()))\n return true;\n const value = cellValue(row, column.key);\n return value !== undefined && value !== null && value !== '';\n}\n\n// In the stacked mobile layout, header columns render in the card header row\n// instead of as a label/value pair. Everything else stacks below in the body.\nconst mobileHeaderColumns = computed<TableColumn<T>[]>(() =>\n get(isMobile) ? get(columns).filter(column => isMobileHeaderColumn(column) && mobileHeaderColumnHasContent(column)) : [],\n);\nconst bodyColumns = computed<TableColumn<T>[]>(() =>\n get(isMobile) ? get(columns).filter(column => !isMobileHeaderColumn(column)) : get(columns),\n);\nconst showMobileHeader = computed<boolean>(() =>\n get(isMobile) && (!!get(selectedData) || get(mobileHeaderColumns).length > 0),\n);\n\n// `!border-y` defeats the `divide-y-0` on the mobile tbody, which otherwise\n// zeroes the top/bottom border on every card except the first one. When the\n// card is expanded, its bottom edge flattens so the expanded panel attaches\n// flush beneath it.\nconst mobileCardClass = computed<string>(() => {\n const shared = 'relative flex flex-col border !border-y border-black/[0.12] dark:border-white/[0.12] overflow-hidden';\n // These bindings are not run through tailwind-merge, so avoid emitting\n // conflicting utilities (mb-0 vs mb-3, rounded-b-none vs rounded-lg): pick the\n // right one per state instead.\n return get(expanded)\n ? `${shared} rounded-t-lg mb-0`\n : `${shared} rounded-lg mb-3 last:mb-0`;\n});\n</script>\n\n<template>\n <tr\n :class=\"[selected ? classes.trSelected : classes.tr, rowClass, isMobile ? mobileCardClass : '']\"\n :aria-selected=\"selectedData ? selected : undefined\"\n data-id=\"row\"\n >\n <!-- Mobile card header: checkbox on the left, pinned action columns on the right -->\n <td\n v-if=\"showMobileHeader\"\n class=\"flex items-center justify-between gap-2 px-4 py-2 border-b border-black/[0.12] dark:border-white/[0.12]\"\n data-id=\"mobile-card-header\"\n >\n <RuiCheckbox\n v-if=\"selectedData\"\n :data-id=\"`table-toggle-check-${index}`\"\n :model-value=\"selected\"\n :disabled=\"disabled\"\n :size=\"dense ? 'sm' : undefined\"\n color=\"primary\"\n class=\"select-none\"\n hide-details\n @update:model-value=\"onSelect($event, rowId, true)\"\n @click=\"onCheckboxClick($event, rowId, index)\"\n />\n <span v-else />\n\n <div\n v-if=\"mobileHeaderColumns.length > 0\"\n class=\"flex items-center gap-1 min-w-0\"\n >\n <template\n v-for=\"(column, headerIndex) in mobileHeaderColumns\"\n :key=\"`header-${headerIndex}`\"\n >\n <RuiExpandButton\n v-if=\"column.key === 'expand'\"\n :expanded=\"isExpanded(rowId)\"\n @click=\"onToggleExpand(row)\"\n />\n <slot\n v-else-if=\"itemSlotKeys.has(column.key.toString())\"\n :name=\"`item.${column.key.toString()}`\"\n :column=\"column\"\n :row=\"row\"\n :index=\"index\"\n />\n <template v-else>\n {{ cellValue(row, column.key) }}\n </template>\n </template>\n </div>\n </td>\n\n <!-- Desktop checkbox cell -->\n <td\n v-else-if=\"selectedData && !isMobile\"\n :class=\"classes.checkbox\"\n colspan=\"1\"\n rowspan=\"1\"\n >\n <RuiCheckbox\n :data-id=\"`table-toggle-check-${index}`\"\n :model-value=\"selected\"\n :disabled=\"disabled\"\n :size=\"dense ? 'sm' : undefined\"\n color=\"primary\"\n class=\"select-none\"\n hide-details\n @update:model-value=\"onSelect($event, rowId, true)\"\n @click=\"onCheckboxClick($event, rowId, index)\"\n />\n </td>\n\n <RuiDataTableCell\n v-for=\"(column, subIndex) in bodyColumns\"\n :key=\"subIndex\"\n :column=\"column\"\n :row=\"row\"\n :index=\"index\"\n :row-id=\"rowId\"\n >\n <template\n v-if=\"itemSlotKeys.has(column.key.toString())\"\n #default=\"slotData\"\n >\n <slot\n :name=\"`item.${column.key.toString()}`\"\n v-bind=\"slotData\"\n />\n </template>\n </RuiDataTableCell>\n </tr>\n\n <RuiDataTableExpandedRow\n v-if=\"expanded\"\n :row=\"row\"\n :index=\"index\"\n >\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 </RuiDataTableExpandedRow>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"RuiDataTableRow.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../../src/components/tables/data-table/RuiDataTableRow.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object\">\nimport type { TableColumn } from '@/components/tables/RuiTableHead.vue';\nimport RuiCheckbox from '@/components/forms/checkbox/RuiCheckbox.vue';\nimport { useDataTableColumns, useDataTableExpansion, useDataTableRowIdentity, useDataTableSelection, useDataTableStyling } from '@/components/tables/data-table/context';\nimport RuiDataTableCell from '@/components/tables/data-table/RuiDataTableCell.vue';\nimport RuiDataTableExpandedRow from '@/components/tables/data-table/RuiDataTableExpandedRow.vue';\nimport RuiExpandButton from '@/components/tables/RuiExpandButton.vue';\n\nconst {\n row,\n index,\n} = defineProps<{\n row: T;\n index: number;\n}>();\n\nconst slots = defineSlots<Partial<\n Record<`item.${string}`, (props: { column: TableColumn<T>; row: T; index: number }) => any> & {\n 'expanded-item'?: (props: { row: T; index: number }) => any;\n }\n>>();\n\nconst { classes, dense, isMobile } = useDataTableStyling();\nconst { cellValue, columns, itemSlotKeys } = useDataTableColumns<T>();\nconst { isDisabledRow, isSelected, onCheckboxClick, onSelect, selectedData } = useDataTableSelection<T>();\nconst { expandable, isExpanded, onToggleExpand } = useDataTableExpansion<T>();\nconst { getRowId, itemClass } = useDataTableRowIdentity<T>();\n\nconst rowId = computed<T[keyof T]>(() => getRowId(row));\nconst selected = computed<boolean>(() => isSelected(get(rowId)));\nconst disabled = computed<boolean>(() => isDisabledRow(get(rowId)));\nconst expanded = computed<boolean>(() => get(expandable) && !!slots['expanded-item'] && isExpanded(get(rowId)));\nconst rowClass = computed<string>(() => typeof itemClass === 'string' ? itemClass : itemClass(row));\n\n// A column is pinned to the mobile card header when it is flagged `mobileHeader`\n// (typically an action column) or when it is the auto-generated expand column.\nfunction isMobileHeaderColumn(column: TableColumn<T>): boolean {\n return !!column.mobileHeader || column.key === 'expand';\n}\n\n// A pinned column only earns a spot in the header if it actually renders\n// something: the expand toggle, a provided item slot, or a non-empty cell value.\n// Without this, a pinned column with no content (e.g. an action column whose\n// `#item.action` slot is not provided in a nested table) would leave an empty\n// header bar with just its divider line.\nfunction mobileHeaderColumnHasContent(column: TableColumn<T>): boolean {\n if (column.key === 'expand')\n return true;\n if (itemSlotKeys.has(column.key.toString()))\n return true;\n const value = cellValue(row, column.key);\n return value !== undefined && value !== null && value !== '';\n}\n\n// In the stacked mobile layout, header columns render in the card header row\n// instead of as a label/value pair. Everything else stacks below in the body.\nconst mobileHeaderColumns = computed<TableColumn<T>[]>(() =>\n get(isMobile) ? get(columns).filter(column => isMobileHeaderColumn(column) && mobileHeaderColumnHasContent(column)) : [],\n);\nconst bodyColumns = computed<TableColumn<T>[]>(() =>\n get(isMobile) ? get(columns).filter(column => !isMobileHeaderColumn(column)) : get(columns),\n);\nconst showMobileHeader = computed<boolean>(() =>\n get(isMobile) && (!!get(selectedData) || get(mobileHeaderColumns).length > 0),\n);\n\n// `!border-y` defeats the `divide-y-0` on the mobile tbody, which otherwise\n// zeroes the top/bottom border on every card except the first one. When the\n// card is expanded, its bottom edge flattens so the expanded panel attaches\n// flush beneath it.\nconst mobileCardClass = computed<string>(() => {\n const shared = 'relative flex flex-col border !border-y border-black/[0.12] dark:border-white/[0.12] overflow-hidden';\n // These bindings are not run through tailwind-merge, so avoid emitting\n // conflicting utilities (mb-0 vs mb-3, rounded-b-none vs rounded-lg): pick the\n // right one per state instead.\n return get(expanded)\n ? `${shared} rounded-t-lg mb-0`\n : `${shared} rounded-lg mb-3 last:mb-0`;\n});\n</script>\n\n<template>\n <tr\n :class=\"[selected ? classes.trSelected : classes.tr, rowClass, isMobile ? mobileCardClass : '']\"\n :aria-selected=\"selectedData ? selected : undefined\"\n data-id=\"row\"\n >\n <!-- Mobile card header: checkbox on the left, pinned action columns on the right -->\n <td\n v-if=\"showMobileHeader\"\n class=\"flex items-center justify-between gap-2 px-4 py-2 border-b border-black/[0.12] dark:border-white/[0.12]\"\n data-id=\"mobile-card-header\"\n >\n <RuiCheckbox\n v-if=\"selectedData\"\n :data-id=\"`table-toggle-check-${index}`\"\n :model-value=\"selected\"\n :disabled=\"disabled\"\n :size=\"dense ? 'sm' : undefined\"\n color=\"primary\"\n class=\"select-none\"\n hide-details\n @update:model-value=\"onSelect($event, rowId, true)\"\n @click=\"onCheckboxClick($event, rowId, index)\"\n />\n <span v-else />\n\n <div\n v-if=\"mobileHeaderColumns.length > 0\"\n class=\"flex items-center gap-1 min-w-0\"\n >\n <template\n v-for=\"(column, headerIndex) in mobileHeaderColumns\"\n :key=\"`header-${headerIndex}`\"\n >\n <slot\n v-if=\"itemSlotKeys.has(column.key.toString())\"\n :name=\"`item.${column.key.toString()}`\"\n :column=\"column\"\n :row=\"row\"\n :index=\"index\"\n />\n <RuiExpandButton\n v-else-if=\"column.key === 'expand'\"\n :expanded=\"isExpanded(rowId)\"\n @click=\"onToggleExpand(row)\"\n />\n <template v-else>\n {{ cellValue(row, column.key) }}\n </template>\n </template>\n </div>\n </td>\n\n <!-- Desktop checkbox cell -->\n <td\n v-else-if=\"selectedData && !isMobile\"\n :class=\"classes.checkbox\"\n colspan=\"1\"\n rowspan=\"1\"\n >\n <RuiCheckbox\n :data-id=\"`table-toggle-check-${index}`\"\n :model-value=\"selected\"\n :disabled=\"disabled\"\n :size=\"dense ? 'sm' : undefined\"\n color=\"primary\"\n class=\"select-none\"\n hide-details\n @update:model-value=\"onSelect($event, rowId, true)\"\n @click=\"onCheckboxClick($event, rowId, index)\"\n />\n </td>\n\n <RuiDataTableCell\n v-for=\"(column, subIndex) in bodyColumns\"\n :key=\"subIndex\"\n :column=\"column\"\n :row=\"row\"\n :index=\"index\"\n :row-id=\"rowId\"\n >\n <template\n v-if=\"itemSlotKeys.has(column.key.toString())\"\n #default=\"slotData\"\n >\n <slot\n :name=\"`item.${column.key.toString()}`\"\n v-bind=\"slotData\"\n />\n </template>\n </RuiDataTableCell>\n </tr>\n\n <RuiDataTableExpandedRow\n v-if=\"expanded\"\n :row=\"row\"\n :index=\"index\"\n >\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 </RuiDataTableExpandedRow>\n</template>\n"],"mappings":"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"}
@@ -10,6 +10,8 @@ export interface UseAutoCompleteValueOptions<TItem> {
10
10
  export interface UseAutoCompleteValueReturn<TItem> {
11
11
  value: ComputedRef<TItem[]>;
12
12
  setSelected: (selected: TItem[]) => void;
13
+ /** Resolves the current model value against an arbitrary options list. */
14
+ resolveIn: (options: TItem[]) => TItem[];
13
15
  }
14
16
  export interface UseAutoCompleteValueDeps<TItem> {
15
17
  getIdentifier: (item: TItem) => any;
@@ -3,12 +3,12 @@ import { computed, toValue, watch } from "vue";
3
3
  import { get, set } from "@vueuse/shared";
4
4
  //#region src/composables/forms/auto-complete/value.ts
5
5
  function useAutoCompleteValue(modelValue, options, opts, deps) {
6
- const optionsMap = computed(() => {
6
+ function buildOptionsMap(optionsData) {
7
7
  const map = /* @__PURE__ */ new Map();
8
- const optionsData = toValue(options);
9
8
  for (const item of optionsData) map.set(deps.getIdentifier(item), item);
10
9
  return map;
11
- });
10
+ }
11
+ const optionsMap = computed(() => buildOptionsMap(toValue(options)));
12
12
  function convertModelValueToArray(modelValueData) {
13
13
  if (modelValueData === null || modelValueData === void 0) return [];
14
14
  return Array.isArray(modelValueData) ? modelValueData : [modelValueData];
@@ -44,16 +44,19 @@ function useAutoCompleteValue(modelValue, options, opts, deps) {
44
44
  if (selection.length === 0) return onUpdateModelValue(void 0);
45
45
  return onUpdateModelValue(selection[0]);
46
46
  }
47
+ function resolveAgainst(optionsMapData) {
48
+ const modelValueData = get(modelValue);
49
+ const keyAttr = toValue(opts.keyAttr);
50
+ const returnObject = toValue(opts.returnObject) ?? false;
51
+ const customValue = toValue(opts.customValue) ?? false;
52
+ if (keyAttr && returnObject) return processReturnObjectValue(modelValueData, optionsMapData, returnObject, customValue);
53
+ return processStandardValue(modelValueData, optionsMapData, returnObject, customValue);
54
+ }
55
+ function resolveIn(optionsData) {
56
+ return resolveAgainst(buildOptionsMap(optionsData));
57
+ }
47
58
  const value = computed({
48
- get: () => {
49
- const modelValueData = get(modelValue);
50
- const keyAttr = toValue(opts.keyAttr);
51
- const returnObject = toValue(opts.returnObject) ?? false;
52
- const customValue = toValue(opts.customValue) ?? false;
53
- const optionsMapData = get(optionsMap);
54
- if (keyAttr && returnObject) return processReturnObjectValue(modelValueData, optionsMapData, returnObject, customValue);
55
- return processStandardValue(modelValueData, optionsMapData, returnObject, customValue);
56
- },
59
+ get: () => resolveAgainst(get(optionsMap)),
57
60
  set: (selected) => {
58
61
  setSelected(selected);
59
62
  }
@@ -69,6 +72,7 @@ function useAutoCompleteValue(modelValue, options, opts, deps) {
69
72
  }
70
73
  }, { immediate: true });
71
74
  return {
75
+ resolveIn,
72
76
  setSelected,
73
77
  value
74
78
  };
@@ -1 +1 @@
1
- {"version":3,"file":"value.js","names":[],"sources":["../../../../src/composables/forms/auto-complete/value.ts"],"sourcesContent":["import type { ComputedRef, MaybeRefOrGetter, Ref } from 'vue';\nimport { get, set } from '@vueuse/shared';\nimport { assert } from '@/utils/assert';\n\nexport interface UseAutoCompleteValueOptions<TItem> {\n /** The property used as the unique identifier for each item. */\n keyAttr?: MaybeRefOrGetter<keyof TItem | undefined>;\n /** Whether to return the full item object instead of just the key. */\n returnObject?: MaybeRefOrGetter<boolean>;\n /** Whether custom (free-text) values are allowed. */\n customValue?: MaybeRefOrGetter<boolean>;\n}\n\nexport interface UseAutoCompleteValueReturn<TItem> {\n value: ComputedRef<TItem[]>;\n setSelected: (selected: TItem[]) => void;\n}\n\nexport interface UseAutoCompleteValueDeps<TItem> {\n getIdentifier: (item: TItem) => any;\n getText: (item: TItem) => string | undefined;\n textValueToProperValue: (val: any, returnObject?: boolean) => TItem;\n shouldApplyValueAsSearch: MaybeRefOrGetter<boolean>;\n isOpen: MaybeRefOrGetter<boolean>;\n multiple: MaybeRefOrGetter<boolean>;\n updateInternalSearch: (value?: string) => void;\n}\n\nexport function useAutoCompleteValue<TValue, TItem>(\n modelValue: Ref<TValue | undefined>,\n options: MaybeRefOrGetter<TItem[]>,\n opts: UseAutoCompleteValueOptions<TItem>,\n deps: UseAutoCompleteValueDeps<TItem>,\n): UseAutoCompleteValueReturn<TItem> {\n // Create maps for O(1) lookup performance\n const optionsMap = computed<Map<any, TItem>>(() => {\n const map = new Map<any, TItem>();\n const optionsData = toValue(options);\n for (const item of optionsData) {\n map.set(deps.getIdentifier(item), item);\n }\n return map;\n });\n\n function convertModelValueToArray(modelValueData: TValue | undefined): any[] {\n if (modelValueData === null || modelValueData === undefined)\n return [];\n return Array.isArray(modelValueData) ? modelValueData : [modelValueData];\n }\n\n function filterValues(\n valueArray: any[],\n optionsMapData: Map<any, TItem>,\n customValue: boolean,\n returnObject: boolean,\n getKey: (val: any) => any,\n ): TItem[] {\n const filtered: TItem[] = [];\n for (const val of valueArray) {\n const inOptions = optionsMapData.get(getKey(val));\n if (inOptions !== undefined)\n filtered.push(inOptions);\n else if (customValue)\n filtered.push(deps.textValueToProperValue(val, returnObject));\n }\n return filtered;\n }\n\n function processReturnObjectValue(\n modelValueData: TValue | undefined,\n optionsMapData: Map<any, TItem>,\n returnObject: boolean,\n customValue: boolean,\n ): TItem[] {\n const multipleValue = Array.isArray(modelValueData);\n const valueArray = convertModelValueToArray(modelValueData);\n const filtered = filterValues(valueArray, optionsMapData, customValue, returnObject, val => deps.getIdentifier(val));\n\n if (multipleValue || filtered.length === 0)\n return filtered;\n\n const val = filtered[0];\n assert(val !== undefined && val !== null);\n return [val];\n }\n\n function processStandardValue(\n modelValueData: TValue | undefined,\n optionsMapData: Map<any, TItem>,\n returnObject: boolean,\n customValue: boolean,\n ): TItem[] {\n const valueArray = convertModelValueToArray(modelValueData);\n return filterValues(valueArray, optionsMapData, customValue, returnObject, val => val);\n }\n\n function onUpdateModelValue(value: TValue | undefined): void {\n set(modelValue, value);\n }\n\n function setSelected(selected: TItem[]): void {\n const keyAttr = toValue(opts.keyAttr);\n const returnObject = toValue(opts.returnObject);\n const selection = keyAttr && !returnObject ? selected.map(item => item[keyAttr]) : selected;\n\n if (toValue(deps.multiple))\n return onUpdateModelValue(selection as TValue);\n\n if (selection.length === 0)\n return onUpdateModelValue(undefined);\n\n return onUpdateModelValue(selection[0] as TValue);\n }\n\n const value = computed<TItem[]>({\n get: () => {\n const modelValueData = get(modelValue);\n const keyAttr = toValue(opts.keyAttr);\n const returnObject = toValue(opts.returnObject) ?? false;\n const customValue = toValue(opts.customValue) ?? false;\n const optionsMapData = get(optionsMap);\n\n if (keyAttr && returnObject)\n return processReturnObjectValue(modelValueData, optionsMapData, returnObject, customValue);\n\n return processStandardValue(modelValueData, optionsMapData, returnObject, customValue);\n },\n set: (selected: TItem[]): void => {\n setSelected(selected);\n },\n });\n\n // Watching `value` (not `modelValue`) so the search text resyncs once\n // `options` arrives later and the lookup can finally resolve.\n watch([value, () => toValue(deps.isOpen)], () => {\n if (toValue(deps.isOpen) || !toValue(deps.shouldApplyValueAsSearch))\n return;\n\n const currentValue = get(value);\n if (toValue(deps.multiple) || currentValue.length === 0) {\n deps.updateInternalSearch();\n }\n else {\n const firstItem = currentValue[0];\n assert(firstItem !== undefined && firstItem !== null);\n deps.updateInternalSearch(deps.getText(firstItem));\n }\n }, { immediate: true });\n\n return {\n setSelected,\n value,\n };\n}\n"],"mappings":";;;;AA4BA,SAAgB,qBACd,YACA,SACA,MACA,MACmC;CAEnC,MAAM,aAAa,eAAgC;EACjD,MAAM,sBAAM,IAAI,IAAgB;EAChC,MAAM,cAAc,QAAQ,OAAO;EACnC,KAAK,MAAM,QAAQ,aACjB,IAAI,IAAI,KAAK,cAAc,IAAI,GAAG,IAAI;EAExC,OAAO;CACT,CAAC;CAED,SAAS,yBAAyB,gBAA2C;EAC3E,IAAI,mBAAmB,QAAQ,mBAAmB,KAAA,GAChD,OAAO,CAAC;EACV,OAAO,MAAM,QAAQ,cAAc,IAAI,iBAAiB,CAAC,cAAc;CACzE;CAEA,SAAS,aACP,YACA,gBACA,aACA,cACA,QACS;EACT,MAAM,WAAoB,CAAC;EAC3B,KAAK,MAAM,OAAO,YAAY;GAC5B,MAAM,YAAY,eAAe,IAAI,OAAO,GAAG,CAAC;GAChD,IAAI,cAAc,KAAA,GAChB,SAAS,KAAK,SAAS;QACpB,IAAI,aACP,SAAS,KAAK,KAAK,uBAAuB,KAAK,YAAY,CAAC;EAChE;EACA,OAAO;CACT;CAEA,SAAS,yBACP,gBACA,gBACA,cACA,aACS;EACT,MAAM,gBAAgB,MAAM,QAAQ,cAAc;EAElD,MAAM,WAAW,aADE,yBAAyB,cACd,GAAY,gBAAgB,aAAa,eAAc,QAAO,KAAK,cAAc,GAAG,CAAC;EAEnH,IAAI,iBAAiB,SAAS,WAAW,GACvC,OAAO;EAET,MAAM,MAAM,SAAS;EACrB,OAAO,QAAQ,KAAA,KAAa,QAAQ,IAAI;EACxC,OAAO,CAAC,GAAG;CACb;CAEA,SAAS,qBACP,gBACA,gBACA,cACA,aACS;EAET,OAAO,aADY,yBAAyB,cACxB,GAAY,gBAAgB,aAAa,eAAc,QAAO,GAAG;CACvF;CAEA,SAAS,mBAAmB,OAAiC;EAC3D,IAAI,YAAY,KAAK;CACvB;CAEA,SAAS,YAAY,UAAyB;EAC5C,MAAM,UAAU,QAAQ,KAAK,OAAO;EACpC,MAAM,eAAe,QAAQ,KAAK,YAAY;EAC9C,MAAM,YAAY,WAAW,CAAC,eAAe,SAAS,KAAI,SAAQ,KAAK,QAAQ,IAAI;EAEnF,IAAI,QAAQ,KAAK,QAAQ,GACvB,OAAO,mBAAmB,SAAmB;EAE/C,IAAI,UAAU,WAAW,GACvB,OAAO,mBAAmB,KAAA,CAAS;EAErC,OAAO,mBAAmB,UAAU,EAAY;CAClD;CAEA,MAAM,QAAQ,SAAkB;EAC9B,WAAW;GACT,MAAM,iBAAiB,IAAI,UAAU;GACrC,MAAM,UAAU,QAAQ,KAAK,OAAO;GACpC,MAAM,eAAe,QAAQ,KAAK,YAAY,KAAK;GACnD,MAAM,cAAc,QAAQ,KAAK,WAAW,KAAK;GACjD,MAAM,iBAAiB,IAAI,UAAU;GAErC,IAAI,WAAW,cACb,OAAO,yBAAyB,gBAAgB,gBAAgB,cAAc,WAAW;GAE3F,OAAO,qBAAqB,gBAAgB,gBAAgB,cAAc,WAAW;EACvF;EACA,MAAM,aAA4B;GAChC,YAAY,QAAQ;EACtB;CACF,CAAC;CAID,MAAM,CAAC,aAAa,QAAQ,KAAK,MAAM,CAAC,SAAS;EAC/C,IAAI,QAAQ,KAAK,MAAM,KAAK,CAAC,QAAQ,KAAK,wBAAwB,GAChE;EAEF,MAAM,eAAe,IAAI,KAAK;EAC9B,IAAI,QAAQ,KAAK,QAAQ,KAAK,aAAa,WAAW,GACpD,KAAK,qBAAqB;OAEvB;GACH,MAAM,YAAY,aAAa;GAC/B,OAAO,cAAc,KAAA,KAAa,cAAc,IAAI;GACpD,KAAK,qBAAqB,KAAK,QAAQ,SAAS,CAAC;EACnD;CACF,GAAG,EAAE,WAAW,KAAK,CAAC;CAEtB,OAAO;EACL;EACA;CACF;AACF"}
1
+ {"version":3,"file":"value.js","names":[],"sources":["../../../../src/composables/forms/auto-complete/value.ts"],"sourcesContent":["import type { ComputedRef, MaybeRefOrGetter, Ref } from 'vue';\nimport { get, set } from '@vueuse/shared';\nimport { assert } from '@/utils/assert';\n\nexport interface UseAutoCompleteValueOptions<TItem> {\n /** The property used as the unique identifier for each item. */\n keyAttr?: MaybeRefOrGetter<keyof TItem | undefined>;\n /** Whether to return the full item object instead of just the key. */\n returnObject?: MaybeRefOrGetter<boolean>;\n /** Whether custom (free-text) values are allowed. */\n customValue?: MaybeRefOrGetter<boolean>;\n}\n\nexport interface UseAutoCompleteValueReturn<TItem> {\n value: ComputedRef<TItem[]>;\n setSelected: (selected: TItem[]) => void;\n /** Resolves the current model value against an arbitrary options list. */\n resolveIn: (options: TItem[]) => TItem[];\n}\n\nexport interface UseAutoCompleteValueDeps<TItem> {\n getIdentifier: (item: TItem) => any;\n getText: (item: TItem) => string | undefined;\n textValueToProperValue: (val: any, returnObject?: boolean) => TItem;\n shouldApplyValueAsSearch: MaybeRefOrGetter<boolean>;\n isOpen: MaybeRefOrGetter<boolean>;\n multiple: MaybeRefOrGetter<boolean>;\n updateInternalSearch: (value?: string) => void;\n}\n\nexport function useAutoCompleteValue<TValue, TItem>(\n modelValue: Ref<TValue | undefined>,\n options: MaybeRefOrGetter<TItem[]>,\n opts: UseAutoCompleteValueOptions<TItem>,\n deps: UseAutoCompleteValueDeps<TItem>,\n): UseAutoCompleteValueReturn<TItem> {\n function buildOptionsMap(optionsData: TItem[]): Map<any, TItem> {\n const map = new Map<any, TItem>();\n for (const item of optionsData) {\n map.set(deps.getIdentifier(item), item);\n }\n return map;\n }\n\n // Create maps for O(1) lookup performance\n const optionsMap = computed<Map<any, TItem>>(() => buildOptionsMap(toValue(options)));\n\n function convertModelValueToArray(modelValueData: TValue | undefined): any[] {\n if (modelValueData === null || modelValueData === undefined)\n return [];\n return Array.isArray(modelValueData) ? modelValueData : [modelValueData];\n }\n\n function filterValues(\n valueArray: any[],\n optionsMapData: Map<any, TItem>,\n customValue: boolean,\n returnObject: boolean,\n getKey: (val: any) => any,\n ): TItem[] {\n const filtered: TItem[] = [];\n for (const val of valueArray) {\n const inOptions = optionsMapData.get(getKey(val));\n if (inOptions !== undefined)\n filtered.push(inOptions);\n else if (customValue)\n filtered.push(deps.textValueToProperValue(val, returnObject));\n }\n return filtered;\n }\n\n function processReturnObjectValue(\n modelValueData: TValue | undefined,\n optionsMapData: Map<any, TItem>,\n returnObject: boolean,\n customValue: boolean,\n ): TItem[] {\n const multipleValue = Array.isArray(modelValueData);\n const valueArray = convertModelValueToArray(modelValueData);\n const filtered = filterValues(valueArray, optionsMapData, customValue, returnObject, val => deps.getIdentifier(val));\n\n if (multipleValue || filtered.length === 0)\n return filtered;\n\n const val = filtered[0];\n assert(val !== undefined && val !== null);\n return [val];\n }\n\n function processStandardValue(\n modelValueData: TValue | undefined,\n optionsMapData: Map<any, TItem>,\n returnObject: boolean,\n customValue: boolean,\n ): TItem[] {\n const valueArray = convertModelValueToArray(modelValueData);\n return filterValues(valueArray, optionsMapData, customValue, returnObject, val => val);\n }\n\n function onUpdateModelValue(value: TValue | undefined): void {\n set(modelValue, value);\n }\n\n function setSelected(selected: TItem[]): void {\n const keyAttr = toValue(opts.keyAttr);\n const returnObject = toValue(opts.returnObject);\n const selection = keyAttr && !returnObject ? selected.map(item => item[keyAttr]) : selected;\n\n if (toValue(deps.multiple))\n return onUpdateModelValue(selection as TValue);\n\n if (selection.length === 0)\n return onUpdateModelValue(undefined);\n\n return onUpdateModelValue(selection[0] as TValue);\n }\n\n function resolveAgainst(optionsMapData: Map<any, TItem>): TItem[] {\n const modelValueData = get(modelValue);\n const keyAttr = toValue(opts.keyAttr);\n const returnObject = toValue(opts.returnObject) ?? false;\n const customValue = toValue(opts.customValue) ?? false;\n\n if (keyAttr && returnObject)\n return processReturnObjectValue(modelValueData, optionsMapData, returnObject, customValue);\n\n return processStandardValue(modelValueData, optionsMapData, returnObject, customValue);\n }\n\n function resolveIn(optionsData: TItem[]): TItem[] {\n return resolveAgainst(buildOptionsMap(optionsData));\n }\n\n const value = computed<TItem[]>({\n get: () => resolveAgainst(get(optionsMap)),\n set: (selected: TItem[]): void => {\n setSelected(selected);\n },\n });\n\n // Watching `value` (not `modelValue`) so the search text resyncs once\n // `options` arrives later and the lookup can finally resolve.\n watch([value, () => toValue(deps.isOpen)], () => {\n if (toValue(deps.isOpen) || !toValue(deps.shouldApplyValueAsSearch))\n return;\n\n const currentValue = get(value);\n if (toValue(deps.multiple) || currentValue.length === 0) {\n deps.updateInternalSearch();\n }\n else {\n const firstItem = currentValue[0];\n assert(firstItem !== undefined && firstItem !== null);\n deps.updateInternalSearch(deps.getText(firstItem));\n }\n }, { immediate: true });\n\n return {\n resolveIn,\n setSelected,\n value,\n };\n}\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": "2.23.4",
5
+ "version": "2.23.5",
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": "2.23.4",
3
+ "version": "2.23.5",
4
4
  "description": "A vue design system and component library for rotki",
5
5
  "type": "module",
6
6
  "keywords": [