@sparklingopinions/sparkling-ui 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/aliases.d.ts +47 -10
- package/dist/components/Avatar/Avatar.vue.d.ts +1 -1
- package/dist/components/Calendar/Calendar.js.map +1 -1
- package/dist/components/Calendar/Calendar.vue.d.ts +2 -2
- package/dist/components/Calendar/Calendar.vue_vue_type_script_setup_true_lang.js +6 -14
- package/dist/components/Calendar/Calendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Dialog/Dialog.js.map +1 -1
- package/dist/components/Dialog/Dialog.vue_vue_type_script_setup_true_lang.js +26 -29
- package/dist/components/Dialog/Dialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Filters/FilterChip.js.map +1 -1
- package/dist/components/Filters/FilterChip.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/Filters/FilterChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Filters/FilterMenu.js.map +1 -1
- package/dist/components/Filters/FilterMenu.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/Filters/FilterMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Filters/SortMenu.js.map +1 -1
- package/dist/components/Filters/SortMenu.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/Filters/SortMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Icon/iconSet.js +92 -2
- package/dist/components/Icon/iconSet.js.map +1 -1
- package/dist/components/Input/Input.js.map +1 -1
- package/dist/components/Input/Input.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/Input/Input.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Popover/Popover.js.map +1 -1
- package/dist/components/Popover/Popover.vue.d.ts +3 -0
- package/dist/components/Popover/Popover.vue_vue_type_script_setup_true_lang.js +38 -30
- package/dist/components/Popover/Popover.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/PropertyList/Editable.js.map +1 -1
- package/dist/components/PropertyList/Editable.vue_vue_type_script_setup_true_lang.js +6 -4
- package/dist/components/PropertyList/Editable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/PropertyList/PropertyList.js.map +1 -1
- package/dist/components/PropertyList/PropertyList.vue.d.ts +17 -2
- package/dist/components/PropertyList/PropertyList.vue_vue_type_script_setup_true_lang.js +19 -14
- package/dist/components/PropertyList/PropertyList.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/PropertyRow/Collapsible.js.map +1 -1
- package/dist/components/PropertyRow/Collapsible.vue.d.ts +1 -1
- package/dist/components/PropertyRow/Collapsible.vue_vue_type_script_setup_true_lang.js +12 -12
- package/dist/components/PropertyRow/Collapsible.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Radio/Radio.js.map +1 -1
- package/dist/components/Radio/Radio.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/Radio/Radio.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/Select.vue.d.ts +10 -1
- package/dist/components/Select/Select.vue_vue_type_script_setup_true_lang.js +125 -101
- package/dist/components/Select/Select.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Textarea/Textarea.js.map +1 -1
- package/dist/components/Textarea/Textarea.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/Textarea/Textarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/package.json +1 -1
- package/src/styles/tailwind.css +4 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Select.js","names":["_sfc_main","Select_vue_vue_type_script_setup_true_lang_default","Select_default"],"sources":["../../../src/components/Select/Select.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T extends SelectValue\">\nimport { computed, nextTick, ref, useAttrs } from 'vue'\n\nimport {\n ListboxContent,\n ListboxFilter,\n ListboxItem,\n ListboxRoot,\n ListboxVirtualizer,\n PopoverAnchor,\n PopoverContent,\n PopoverPortal,\n PopoverRoot\n} from 'reka-ui'\n\nimport type { Hue } from '../../utils/hues'\nimport { useFieldControl } from '../FormField/context'\nimport Spinner from '../Spinner/Spinner.vue'\nimport Tag from '../Tag/Tag.vue'\nimport CheckIcon from '../internal/CheckIcon.vue'\nimport type { SelectOption, SelectValue } from './types'\n\nexport interface SelectProps<T extends SelectValue> {\n options: SelectOption<T>[]\n multiple?: boolean\n /** A filter row at the top of the list; on by default for `multiple`, `canCreate` or more than 8 options. */\n searchable?: boolean\n /** Offers to create what was typed, through `createOption`. */\n canCreate?: boolean\n /** Makes the created option; by default its value is its label. */\n createOption?: (draft: { label: string; color?: Hue }) => SelectOption<T> | Promise<SelectOption<T>>\n /** Creates with a hue, shown as a tag. */\n createColored?: boolean\n loading?: boolean\n /** 36px for forms, 28px for table feet and toolbars. */\n size?: 'md' | 'sm'\n disabled?: boolean\n invalid?: boolean\n placeholder?: string\n searchPlaceholder?: string\n emptyLabel?: string\n createLabel?: string\n removeLabel?: string\n /** Where the list opens; it flips when there's no room. */\n side?: 'bottom' | 'top'\n align?: 'start' | 'end'\n /** The list's width, when it should differ from the trigger's, e.g. `20rem`. */\n panelWidth?: string\n}\n\n// Attributes (aria-label, class for its width) go to the trigger, the one element in the page's flow.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<SelectProps<T>>(), {\n searchable: undefined,\n createOption: undefined,\n createColored: true,\n size: 'md',\n placeholder: 'Select an option',\n searchPlaceholder: 'Type to filter',\n emptyLabel: 'No results found',\n createLabel: 'Create',\n removeLabel: 'Remove',\n side: 'bottom',\n align: 'start',\n panelWidth: undefined\n})\n\nconst emit = defineEmits<{ 'query-change': [query: string] }>()\n\n// An aria-label would hide the trigger's visible value from its name; it's said before the value instead.\nconst attrs = useAttrs()\nconst label = computed(() => attrs['aria-label'] as string | undefined)\nconst triggerAttrs = computed(() => Object.fromEntries(Object.entries(attrs).filter(([key]) => key !== 'aria-label')))\n\nconst model = defineModel<T | T[] | null>({ default: null })\n\nconst open = ref(false)\nconst query = ref('')\nconst keyboard = ref(false)\n\nconst values = computed<T[]>(() =>\n Array.isArray(model.value) ? model.value : model.value === null || model.value === undefined ? [] : [model.value]\n)\nconst selected = computed(() =>\n values.value.map((value) => props.options.find((option) => option.value === value)).filter((option) => !!option)\n)\nconst search = computed(() => props.searchable ?? (props.multiple || props.canCreate || props.options.length > 8))\nconst listed = computed(() => {\n const text = query.value.trim().toLowerCase()\n return props.options\n .filter((option) => !(props.multiple && search.value && values.value.includes(option.value)))\n .filter((option) => !text || option.label.toLowerCase().includes(text))\n})\nconst showCreate = computed(() => {\n const text = query.value.trim().toLowerCase()\n return props.canCreate && !!text && !props.options.some((option) => option.label.toLowerCase() === text)\n})\n// More than this and the list renders only the rows in view.\nconst virtual = computed(() => listed.value.length > 20)\n\nconst createHues: Hue[] = ['gray', 'purple', 'red', 'green', 'orange', 'pink', 'blue', 'slate']\nconst newHue = computed(() => createHues[query.value.length % createHues.length])\nconst CREATE = { create: true } as const\n\nconst { attrs: fieldAttrs, invalid: isInvalid } = useFieldControl(\n () => undefined,\n () => props.invalid\n)\n\nfunction setQuery(text: string) {\n query.value = text\n emit('query-change', text)\n}\n\nfunction show(viaKeyboard: boolean) {\n if (props.disabled) return\n keyboard.value = viaKeyboard\n open.value = true\n}\n\nfunction close() {\n open.value = false\n if (query.value) setQuery('')\n}\n\nfunction toggle(event?: Event) {\n if (open.value) close()\n // A click from the keyboard (Enter, Space) has no pointer position.\n else show(!!event && (event as MouseEvent).detail === 0)\n}\n\nfunction choose(option: SelectOption<T>) {\n if (props.multiple) {\n model.value = values.value.includes(option.value)\n ? values.value.filter((value) => value !== option.value)\n : [...values.value, option.value]\n setQuery('')\n } else {\n model.value = option.value\n close()\n }\n}\n\nasync function create() {\n const label = query.value.trim()\n const draft = props.createColored ? { label, color: newHue.value } : { label }\n const option = props.createOption\n ? await props.createOption(draft)\n : ({ value: label as T, label, color: draft.color } as SelectOption<T>)\n choose(option)\n}\n\nfunction onSelect(event: CustomEvent, row: SelectOption<T> | typeof CREATE) {\n // What's chosen lives in the model; the listbox keeps no selection of its own.\n event.preventDefault()\n if ('create' in row) create()\n else if (!row.disabled) choose(row)\n}\n\nfunction remove(value: T) {\n model.value = values.value.filter((chosen) => chosen !== value)\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (['ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) keyboard.value = true\n if (event.key === 'Backspace' && props.multiple && !query.value && values.value.length)\n remove(values.value[values.value.length - 1]!)\n}\n\nfunction onTriggerKeydown(event: KeyboardEvent) {\n if (!open.value && ['ArrowDown', 'ArrowUp'].includes(event.key)) {\n event.preventDefault()\n show(true)\n }\n}\n\n// The filter takes focus when there is one, or else the list, so the arrow keys work at once.\nfunction onOpenAutoFocus(event: Event) {\n event.preventDefault()\n nextTick(() =>\n (event.target as HTMLElement | null)?.querySelector<HTMLElement>('[data-select-filter], [role=\"listbox\"]')?.focus()\n )\n}\n\n// Back to the trigger, which Reka can't find: it's our anchor, not its trigger.\nconst anchor = ref<{ $el: HTMLElement }>()\nfunction returnFocus(event: Event) {\n event.preventDefault()\n anchor.value?.$el?.focus()\n}\n\n// Reka highlights the first option as the list mounts; only the chosen one, or the keyboard, should be.\nconst listbox = ref()\nfunction onHighlight(payload: { value: unknown } | undefined) {\n if (keyboard.value || !payload) return\n if (values.value.includes(payload.value as T)) return\n nextTick(() => listbox.value && !keyboard.value && (listbox.value.highlightedElement = null))\n}\n\nconst triggerSizes = { md: 'min-h-9 pr-2.5 pl-3 text-sm', sm: 'h-7 pr-1.5 pl-2 text-13' }\nconst optionClass =\n 'flex min-h-8 cursor-pointer items-center justify-between gap-3 rounded-lg py-1.5 pr-2 pl-2.5 text-13 whitespace-nowrap text-sui-ink outline-hidden select-none data-disabled:cursor-default data-disabled:opacity-50 data-highlighted:bg-sui-sunken data-[state=checked]:font-medium data-[state=checked]:text-sui-accent group-data-keyboard/list:data-highlighted:sui-ring-inset'\n</script>\n\n<!-- One select for every choice: a short fixed list, a searchable one, several values as tags (`multiple`), or creating\n what was typed (`canCreate`). The `trigger` slot draws your own trigger and gets `open`, `toggle` and `selected`. -->\n<template>\n <PopoverRoot :open=\"open\" @update:open=\"(value) => (value ? show(false) : close())\">\n <PopoverAnchor ref=\"anchor\" as-child>\n <slot name=\"trigger\" :open=\"open\" :selected=\"selected\" :toggle=\"toggle\">\n <button\n v-bind=\"{ ...fieldAttrs, ...triggerAttrs }\"\n :aria-expanded=\"open\"\n aria-haspopup=\"listbox\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n class=\"group inline-flex items-center justify-between gap-1.5 rounded-(--sui-radius-button) border bg-sui-surface text-left text-sui-ink transition-colors duration-150 focus-visible:sui-ring-field disabled:pointer-events-none disabled:opacity-60\"\n :class=\"[\n triggerSizes[size],\n isInvalid ? 'border-sui-danger-mid' : 'border-sui-line-control hover:border-sui-ink-muted'\n ]\"\n :disabled=\"disabled\"\n type=\"button\"\n @click=\"toggle\"\n @keydown=\"onTriggerKeydown\"\n >\n <span v-if=\"label\" class=\"sr-only\">{{ label }}: </span>\n <span v-if=\"!selected.length\" class=\"text-sui-ink-faint\">{{ placeholder }}</span>\n <span v-else-if=\"multiple\" class=\"flex flex-wrap gap-1 py-1.25\">\n <Tag\n v-for=\"option in selected\"\n :key=\"option.value\"\n :color=\"option.color ?? undefined\"\n :name=\"option.label\"\n />\n </span>\n <Tag v-else-if=\"selected[0]!.color\" :color=\"selected[0]!.color\" :name=\"selected[0]!.label\" />\n <span v-else class=\"min-w-0 truncate whitespace-nowrap\">{{ selected[0]!.label }}</span>\n <svg\n aria-hidden=\"true\"\n class=\"size-4 shrink-0 fill-none stroke-sui-ink-muted transition-transform duration-150 group-aria-expanded:rotate-180\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.5\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M4.5 6.5 8 10l3.5-3.5\" />\n </svg>\n </button>\n </slot>\n </PopoverAnchor>\n <PopoverPortal>\n <PopoverContent\n :align=\"align\"\n :aria-label=\"label ?? placeholder\"\n class=\"z-[1000] overflow-hidden rounded-xl border border-sui-line-subtle bg-sui-surface text-sui-ink shadow-lg data-[state=open]:animate-sui-menu-in\"\n :class=\"\n search ? 'min-w-[max(var(--reka-popover-trigger-width),15rem)]' : 'min-w-(--reka-popover-trigger-width)'\n \"\n :side=\"side\"\n :side-offset=\"4\"\n :style=\"panelWidth ? { width: panelWidth } : undefined\"\n @close-auto-focus=\"returnFocus\"\n @open-auto-focus=\"onOpenAutoFocus\"\n >\n <ListboxRoot\n ref=\"listbox\"\n :highlight-on-hover=\"true\"\n :model-value=\"multiple ? values : (values[0] ?? undefined)\"\n :multiple=\"multiple\"\n @highlight=\"onHighlight\"\n @keydown=\"onKeydown\"\n @pointermove=\"keyboard = false\"\n >\n <div\n v-if=\"search\"\n class=\"flex min-h-11 flex-wrap items-center gap-1 border-b border-sui-line-subtle px-3 py-2.5\"\n >\n <template v-if=\"multiple\">\n <Tag\n v-for=\"option in selected\"\n :key=\"option.value\"\n :color=\"option.color ?? undefined\"\n :name=\"option.label\"\n removable\n :remove-label=\"removeLabel\"\n @remove=\"remove(option.value)\"\n />\n </template>\n <!-- focus: classes too: an app's forms plugin rings focused text fields, and outranks plain utilities. -->\n <ListboxFilter\n :aria-label=\"label ?? placeholder\"\n class=\"h-5 min-w-16 flex-1 rounded-none border-none bg-transparent p-0 text-13 text-sui-ink shadow-none outline-hidden placeholder:text-sui-ink-faint focus:border-none focus:shadow-none focus:ring-0 focus:outline-hidden\"\n data-select-filter\n :model-value=\"query\"\n :placeholder=\"multiple && selected.length ? '' : searchPlaceholder\"\n @update:model-value=\"setQuery\"\n />\n </div>\n <div v-if=\"loading\" class=\"flex justify-center py-6\"><Spinner size=\"sm\" /></div>\n <ListboxContent\n v-else-if=\"listed.length || showCreate\"\n :aria-label=\"label ?? placeholder\"\n class=\"group/list max-h-60 overflow-auto p-1 outline-hidden\"\n :data-keyboard=\"keyboard || undefined\"\n >\n <ListboxVirtualizer\n v-if=\"virtual\"\n v-slot=\"{ option }\"\n :estimate-size=\"32\"\n :options=\"listed\"\n :text-content=\"(option) => option.label\"\n >\n <ListboxItem\n :class=\"optionClass\"\n :disabled=\"option.disabled\"\n :value=\"option.value\"\n @select=\"onSelect($event, option as SelectOption<T>)\"\n >\n <span class=\"inline-flex min-w-0 items-center gap-1.5 truncate\">\n <slot name=\"option\" :option=\"option as SelectOption<T>\">\n <Tag v-if=\"option.color\" :color=\"option.color\" :name=\"option.label\" />\n <template v-else>{{ option.label }}</template>\n </slot>\n </span>\n <CheckIcon v-if=\"values.includes(option.value)\" />\n </ListboxItem>\n </ListboxVirtualizer>\n <template v-else>\n <ListboxItem\n v-for=\"option in listed\"\n :key=\"option.value\"\n :class=\"optionClass\"\n :disabled=\"option.disabled\"\n :value=\"option.value\"\n @select=\"onSelect($event, option)\"\n >\n <span class=\"inline-flex min-w-0 items-center gap-1.5 truncate\">\n <slot name=\"option\" :option=\"option\">\n <Tag v-if=\"option.color\" :color=\"option.color\" :name=\"option.label\" />\n <template v-else>{{ option.label }}</template>\n </slot>\n </span>\n <CheckIcon v-if=\"values.includes(option.value)\" />\n </ListboxItem>\n </template>\n <ListboxItem v-if=\"showCreate\" :class=\"optionClass\" :value=\"CREATE\" @select=\"onSelect($event, CREATE)\">\n <span class=\"inline-flex min-w-0 items-center gap-1.5\">\n {{ createLabel }}\n <Tag v-if=\"createColored\" :color=\"newHue\" :name=\"query.trim()\" />\n <strong v-else class=\"font-medium\">“{{ query.trim() }}”</strong>\n </span>\n </ListboxItem>\n </ListboxContent>\n <div v-else class=\"px-3.5 py-3 text-13 text-sui-ink-muted\">{{ emptyLabel }}</div>\n </ListboxRoot>\n </PopoverContent>\n </PopoverPortal>\n </PopoverRoot>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Select.js","names":["_sfc_main","Select_vue_vue_type_script_setup_true_lang_default","Select_default"],"sources":["../../../src/components/Select/Select.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T extends SelectValue\">\nimport { computed, nextTick, ref, shallowRef, useAttrs } from 'vue'\n\nimport {\n ListboxContent,\n ListboxFilter,\n ListboxItem,\n ListboxRoot,\n ListboxVirtualizer,\n PopoverAnchor,\n PopoverContent,\n PopoverPortal,\n PopoverRoot\n} from 'reka-ui'\n\nimport type { Hue } from '../../utils/hues'\nimport { useFieldControl } from '../FormField/context'\nimport Spinner from '../Spinner/Spinner.vue'\nimport Tag from '../Tag/Tag.vue'\nimport CheckIcon from '../internal/CheckIcon.vue'\nimport type { SelectOption, SelectValue } from './types'\n\nexport interface SelectProps<T extends SelectValue> {\n options: SelectOption<T>[]\n /** The options for chosen values that aren't in `options`, such as a saved choice while `options` holds search\n * results. Options picked from the list are remembered without it. */\n selectedOptions?: SelectOption<T>[]\n multiple?: boolean\n /** Lets a single choice be removed, as a tag in the filter row or with Backspace there. */\n clearable?: boolean\n /** A filter row at the top of the list; on by default for `multiple`, `canCreate` or more than 8 options. */\n searchable?: boolean\n /** Narrows `options` to what's typed: by label, or by your function. `false` when `options` are already the results\n * for the query, such as from a server search through `query-change`. */\n filter?: boolean | ((option: SelectOption<T>, query: string) => boolean)\n /** Offers to create what was typed, through `createOption`. */\n canCreate?: boolean\n /** Makes the created option; by default its value is its label. */\n createOption?: (draft: { label: string; color?: Hue }) => SelectOption<T> | Promise<SelectOption<T>>\n /** Creates with a hue, shown as a tag. */\n createColored?: boolean\n loading?: boolean\n /** 36px for forms, 28px for table feet and toolbars. */\n size?: 'md' | 'sm'\n disabled?: boolean\n invalid?: boolean\n placeholder?: string\n searchPlaceholder?: string\n emptyLabel?: string\n createLabel?: string\n removeLabel?: string\n /** Where the list opens; it flips when there's no room. */\n side?: 'bottom' | 'top'\n align?: 'start' | 'end'\n /** The list's width, when it should differ from the trigger's, e.g. `20rem`. */\n panelWidth?: string\n}\n\n// Attributes (aria-label, class for its width) go to the trigger, the one element in the page's flow.\ndefineOptions({ inheritAttrs: false })\n\nconst props = withDefaults(defineProps<SelectProps<T>>(), {\n selectedOptions: () => [],\n searchable: undefined,\n filter: true,\n createOption: undefined,\n createColored: true,\n size: 'md',\n placeholder: 'Select an option',\n searchPlaceholder: 'Type to filter',\n emptyLabel: 'No results found',\n createLabel: 'Create',\n removeLabel: 'Remove',\n side: 'bottom',\n align: 'start',\n panelWidth: undefined\n})\n\nconst emit = defineEmits<{ 'query-change': [query: string]; 'create-error': [error: unknown] }>()\n\n// An aria-label would hide the trigger's visible value from its name; it's said before the value instead.\nconst attrs = useAttrs()\nconst label = computed(() => attrs['aria-label'] as string | undefined)\nconst triggerAttrs = computed(() => Object.fromEntries(Object.entries(attrs).filter(([key]) => key !== 'aria-label')))\n\nconst model = defineModel<T | T[] | null>({ default: null })\n\nconst open = ref(false)\nconst query = ref('')\nconst keyboard = ref(false)\n\nconst values = computed<T[]>(() =>\n Array.isArray(model.value) ? model.value : model.value === null || model.value === undefined ? [] : [model.value]\n)\n// Options picked from earlier results, which `options` may no longer hold.\nconst picked = shallowRef<SelectOption<T>[]>([])\nconst selected = computed(() =>\n values.value\n .map((value) =>\n [props.options, props.selectedOptions, picked.value]\n .map((list) => list.find((option) => option.value === value))\n .find((option) => !!option)\n )\n .filter((option) => !!option)\n)\nconst search = computed(\n () =>\n props.searchable ??\n (props.multiple || props.canCreate || props.clearable || props.filter === false || props.options.length > 8)\n)\nconst removable = computed(() => props.multiple || props.clearable)\nconst matches = (option: SelectOption<T>, text: string) =>\n typeof props.filter === 'function'\n ? props.filter(option, text)\n : option.label.toLowerCase().includes(text.toLowerCase())\nconst listed = computed(() => {\n const text = query.value.trim()\n return props.options\n .filter((option) => !(props.multiple && search.value && values.value.includes(option.value)))\n .filter((option) => !text || props.filter === false || matches(option, text))\n})\nconst showCreate = computed(() => {\n const text = query.value.trim().toLowerCase()\n return props.canCreate && !!text && !props.options.some((option) => option.label.toLowerCase() === text)\n})\n// More than this and the list renders only the rows in view.\nconst virtual = computed(() => listed.value.length > 20)\n\nconst createHues: Hue[] = ['gray', 'purple', 'red', 'green', 'orange', 'pink', 'blue', 'slate']\nconst newHue = computed(() => createHues[query.value.length % createHues.length])\nconst CREATE = { create: true } as const\n\nconst { attrs: fieldAttrs, invalid: isInvalid } = useFieldControl(\n () => undefined,\n () => props.invalid\n)\n\nfunction setQuery(text: string) {\n query.value = text\n emit('query-change', text)\n}\n\nfunction show(viaKeyboard: boolean) {\n if (props.disabled) return\n keyboard.value = viaKeyboard\n open.value = true\n}\n\nfunction close() {\n open.value = false\n if (query.value) setQuery('')\n}\n\nfunction toggle(event?: Event) {\n if (open.value) close()\n // A click from the keyboard (Enter, Space) has no pointer position.\n else show(!!event && (event as MouseEvent).detail === 0)\n}\n\nfunction choose(option: SelectOption<T>) {\n picked.value = [option, ...picked.value.filter((known) => known.value !== option.value)]\n if (props.multiple) {\n model.value = values.value.includes(option.value)\n ? values.value.filter((value) => value !== option.value)\n : [...values.value, option.value]\n setQuery('')\n } else {\n model.value = option.value\n close()\n }\n}\n\nconst creating = ref(false)\n\n// A failed createOption keeps what was typed, so it can be tried again.\nasync function create() {\n if (creating.value) return\n const label = query.value.trim()\n const draft = props.createColored ? { label, color: newHue.value } : { label }\n creating.value = true\n try {\n const option = props.createOption\n ? await props.createOption(draft)\n : ({ value: label as T, label, color: draft.color } as SelectOption<T>)\n choose(option)\n } catch (error) {\n emit('create-error', error)\n } finally {\n creating.value = false\n }\n}\n\nfunction onSelect(event: CustomEvent, row: SelectOption<T> | typeof CREATE) {\n // What's chosen lives in the model; the listbox keeps no selection of its own.\n event.preventDefault()\n if ('create' in row) create()\n else if (!row.disabled) choose(row)\n}\n\nfunction remove(value: T) {\n model.value = props.multiple ? values.value.filter((chosen) => chosen !== value) : null\n}\n\nfunction onKeydown(event: KeyboardEvent) {\n if (['ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) keyboard.value = true\n if (event.key === 'Backspace' && removable.value && !query.value && values.value.length)\n remove(values.value[values.value.length - 1]!)\n}\n\nfunction onTriggerKeydown(event: KeyboardEvent) {\n if (!open.value && ['ArrowDown', 'ArrowUp'].includes(event.key)) {\n event.preventDefault()\n show(true)\n }\n}\n\n// The filter takes focus when there is one, or else the list, so the arrow keys work at once.\nfunction onOpenAutoFocus(event: Event) {\n event.preventDefault()\n nextTick(() =>\n (event.target as HTMLElement | null)?.querySelector<HTMLElement>('[data-select-filter], [role=\"listbox\"]')?.focus()\n )\n}\n\n// Back to the trigger, which Reka can't find: it's our anchor, not its trigger.\nconst anchor = ref<{ $el: HTMLElement }>()\n\n// Nor does Reka know a press on it isn't outside: closing there would let the click reopen the list.\nfunction onPointerDownOutside(event: CustomEvent<{ originalEvent: PointerEvent }>) {\n const target = event.detail.originalEvent.target\n if (target instanceof Node && anchor.value?.$el?.contains(target)) event.preventDefault()\n}\nfunction returnFocus(event: Event) {\n event.preventDefault()\n anchor.value?.$el?.focus()\n}\n\n// Reka highlights the first option as the list mounts; only the chosen one, or the keyboard, should be.\nconst listbox = ref()\nfunction onHighlight(payload: { value: unknown } | undefined) {\n if (keyboard.value || !payload) return\n if (values.value.includes(payload.value as T)) return\n nextTick(() => listbox.value && !keyboard.value && (listbox.value.highlightedElement = null))\n}\n\nconst triggerSizes = { md: 'min-h-9 pr-2.5 pl-3 text-sm', sm: 'h-7 pr-1.5 pl-2 text-13' }\nconst optionClass =\n 'flex min-h-8 cursor-pointer items-center justify-between gap-3 rounded-lg py-1.5 pr-2 pl-2.5 text-13 whitespace-nowrap text-sui-ink outline-hidden select-none data-disabled:cursor-default data-disabled:opacity-50 data-highlighted:bg-sui-sunken data-[state=checked]:font-medium data-[state=checked]:text-sui-accent group-data-keyboard/list:data-highlighted:sui-ring-inset'\n</script>\n\n<!-- One select for every choice: a short fixed list, a searchable one, several values as tags (`multiple`), or creating\n what was typed (`canCreate`). For a server search, set `:filter=\"false\"` and replace `options` on `query-change`. The `trigger` slot draws your own trigger and gets `open`, `toggle` and `selected`. -->\n<template>\n <PopoverRoot :open=\"open\" @update:open=\"(value) => (value ? show(false) : close())\">\n <PopoverAnchor ref=\"anchor\" as-child>\n <slot name=\"trigger\" :open=\"open\" :selected=\"selected\" :toggle=\"toggle\">\n <button\n v-bind=\"{ ...fieldAttrs, ...triggerAttrs }\"\n :aria-expanded=\"open\"\n aria-haspopup=\"listbox\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n class=\"group inline-flex items-center justify-between gap-1.5 rounded-(--sui-radius-button) border bg-sui-surface text-left text-sui-ink transition-[border-color,box-shadow] duration-150 focus-visible:border-sui-accent focus-visible:sui-ring-field disabled:pointer-events-none disabled:opacity-60\"\n :class=\"[\n triggerSizes[size],\n isInvalid ? 'border-sui-danger-mid' : 'border-sui-line-strong hover:border-sui-ink-muted'\n ]\"\n :disabled=\"disabled\"\n type=\"button\"\n @click=\"toggle\"\n @keydown=\"onTriggerKeydown\"\n >\n <span v-if=\"label\" class=\"sr-only\">{{ label }}: </span>\n <span v-if=\"!selected.length\" class=\"text-sui-ink-faint\">{{ placeholder }}</span>\n <span v-else-if=\"multiple\" class=\"flex flex-wrap gap-1 py-1.25\">\n <Tag\n v-for=\"option in selected\"\n :key=\"option.value\"\n :color=\"option.color ?? undefined\"\n :name=\"option.label\"\n />\n </span>\n <Tag v-else-if=\"selected[0]!.color\" :color=\"selected[0]!.color\" :name=\"selected[0]!.label\" />\n <span v-else class=\"min-w-0 truncate whitespace-nowrap\">{{ selected[0]!.label }}</span>\n <svg\n aria-hidden=\"true\"\n class=\"size-4 shrink-0 fill-none stroke-sui-ink-muted transition-transform duration-150 group-aria-expanded:rotate-180\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.5\"\n viewBox=\"0 0 16 16\"\n >\n <path d=\"M4.5 6.5 8 10l3.5-3.5\" />\n </svg>\n </button>\n </slot>\n </PopoverAnchor>\n <PopoverPortal>\n <PopoverContent\n :align=\"align\"\n :aria-label=\"label ?? placeholder\"\n class=\"z-[1100] overflow-hidden rounded-xl border border-sui-line-subtle bg-sui-surface text-sui-ink shadow-lg data-[state=open]:animate-sui-menu-in\"\n :class=\"\n search ? 'min-w-[max(var(--reka-popover-trigger-width),15rem)]' : 'min-w-(--reka-popover-trigger-width)'\n \"\n :side=\"side\"\n :side-offset=\"4\"\n :style=\"panelWidth ? { width: panelWidth } : undefined\"\n @close-auto-focus=\"returnFocus\"\n @open-auto-focus=\"onOpenAutoFocus\"\n @pointer-down-outside=\"onPointerDownOutside\"\n >\n <ListboxRoot\n ref=\"listbox\"\n :highlight-on-hover=\"true\"\n :model-value=\"multiple ? values : (values[0] ?? undefined)\"\n :multiple=\"multiple\"\n @highlight=\"onHighlight\"\n @keydown=\"onKeydown\"\n @pointermove=\"keyboard = false\"\n >\n <div\n v-if=\"search\"\n class=\"flex min-h-11 flex-wrap items-center gap-1 border-b border-sui-line-subtle px-3 py-2.5\"\n >\n <template v-if=\"removable\">\n <Tag\n v-for=\"option in selected\"\n :key=\"option.value\"\n :color=\"option.color ?? undefined\"\n :name=\"option.label\"\n removable\n :remove-label=\"removeLabel\"\n @remove=\"remove(option.value)\"\n />\n </template>\n <!-- focus: classes too: an app's forms plugin rings focused text fields, and outranks plain utilities. -->\n <ListboxFilter\n :aria-label=\"label ?? placeholder\"\n class=\"h-5 min-w-16 flex-1 rounded-none border-none bg-transparent p-0 text-13 text-sui-ink shadow-none outline-hidden placeholder:text-sui-ink-faint focus:border-none focus:shadow-none focus:ring-0 focus:outline-hidden\"\n data-select-filter\n :model-value=\"query\"\n :placeholder=\"removable && selected.length ? '' : searchPlaceholder\"\n @update:model-value=\"setQuery\"\n />\n </div>\n <div v-if=\"loading\" class=\"flex justify-center py-6\"><Spinner size=\"sm\" /></div>\n <ListboxContent\n v-else-if=\"listed.length || showCreate\"\n :aria-label=\"label ?? placeholder\"\n class=\"group/list max-h-60 overflow-auto p-1 outline-hidden\"\n :data-keyboard=\"keyboard || undefined\"\n >\n <ListboxVirtualizer\n v-if=\"virtual\"\n v-slot=\"{ option }\"\n :estimate-size=\"32\"\n :options=\"listed\"\n :text-content=\"(option) => option.label\"\n >\n <ListboxItem\n :class=\"optionClass\"\n :disabled=\"option.disabled\"\n :value=\"option.value\"\n @select=\"onSelect($event, option as SelectOption<T>)\"\n >\n <span class=\"inline-flex min-w-0 items-center gap-1.5 truncate\">\n <slot name=\"option\" :option=\"option as SelectOption<T>\">\n <Tag v-if=\"option.color\" :color=\"option.color\" :name=\"option.label\" />\n <template v-else>{{ option.label }}</template>\n </slot>\n </span>\n <CheckIcon v-if=\"values.includes(option.value)\" />\n </ListboxItem>\n </ListboxVirtualizer>\n <template v-else>\n <ListboxItem\n v-for=\"option in listed\"\n :key=\"option.value\"\n :class=\"optionClass\"\n :disabled=\"option.disabled\"\n :value=\"option.value\"\n @select=\"onSelect($event, option)\"\n >\n <span class=\"inline-flex min-w-0 items-center gap-1.5 truncate\">\n <slot name=\"option\" :option=\"option\">\n <Tag v-if=\"option.color\" :color=\"option.color\" :name=\"option.label\" />\n <template v-else>{{ option.label }}</template>\n </slot>\n </span>\n <CheckIcon v-if=\"values.includes(option.value)\" />\n </ListboxItem>\n </template>\n <ListboxItem v-if=\"showCreate\" :class=\"optionClass\" :value=\"CREATE\" @select=\"onSelect($event, CREATE)\">\n <span class=\"inline-flex min-w-0 items-center gap-1.5\">\n {{ createLabel }}\n <Tag v-if=\"createColored\" :color=\"newHue\" :name=\"query.trim()\" />\n <strong v-else class=\"font-medium\">“{{ query.trim() }}”</strong>\n </span>\n </ListboxItem>\n </ListboxContent>\n <div v-else class=\"px-3.5 py-3 text-13 text-sui-ink-muted\">{{ emptyLabel }}</div>\n </ListboxRoot>\n </PopoverContent>\n </PopoverPortal>\n </PopoverRoot>\n</template>\n"],"mappings":""}
|
|
@@ -2,9 +2,17 @@ import type { Hue } from '../../utils/hues';
|
|
|
2
2
|
import type { SelectOption, SelectValue } from './types';
|
|
3
3
|
export interface SelectProps<T extends SelectValue> {
|
|
4
4
|
options: SelectOption<T>[];
|
|
5
|
+
/** The options for chosen values that aren't in `options`, such as a saved choice while `options` holds search
|
|
6
|
+
* results. Options picked from the list are remembered without it. */
|
|
7
|
+
selectedOptions?: SelectOption<T>[];
|
|
5
8
|
multiple?: boolean;
|
|
9
|
+
/** Lets a single choice be removed, as a tag in the filter row or with Backspace there. */
|
|
10
|
+
clearable?: boolean;
|
|
6
11
|
/** A filter row at the top of the list; on by default for `multiple`, `canCreate` or more than 8 options. */
|
|
7
12
|
searchable?: boolean;
|
|
13
|
+
/** Narrows `options` to what's typed: by label, or by your function. `false` when `options` are already the results
|
|
14
|
+
* for the query, such as from a server search through `query-change`. */
|
|
15
|
+
filter?: boolean | ((option: SelectOption<T>, query: string) => boolean);
|
|
8
16
|
/** Offers to create what was typed, through `createOption`. */
|
|
9
17
|
canCreate?: boolean;
|
|
10
18
|
/** Makes the created option; by default its value is its label. */
|
|
@@ -36,6 +44,7 @@ declare const __VLS_export: <T extends SelectValue>(__VLS_props: NonNullable<Awa
|
|
|
36
44
|
}) & {
|
|
37
45
|
"onUpdate:modelValue"?: ((value: T | T[] | null) => any) | undefined;
|
|
38
46
|
"onQuery-change"?: ((query: string) => any) | undefined;
|
|
47
|
+
"onCreate-error"?: ((error: unknown) => any) | undefined;
|
|
39
48
|
}> & (typeof globalThis extends {
|
|
40
49
|
__VLS_PROPS_FALLBACK: infer P;
|
|
41
50
|
} ? P : {});
|
|
@@ -56,7 +65,7 @@ declare const __VLS_export: <T extends SelectValue>(__VLS_props: NonNullable<Awa
|
|
|
56
65
|
option: SelectOption<T>;
|
|
57
66
|
}) => any;
|
|
58
67
|
};
|
|
59
|
-
emit: ((evt: "query-change", query: string) => void) & ((event: "update:modelValue", value: T | T[] | null) => void);
|
|
68
|
+
emit: (((evt: "query-change", query: string) => void) & ((evt: "create-error", error: unknown) => void)) & ((event: "update:modelValue", value: T | T[] | null) => void);
|
|
60
69
|
}>) => import("vue").VNode & {
|
|
61
70
|
__ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
|
|
62
71
|
};
|
|
@@ -2,47 +2,53 @@ import { useFieldControl as e } from "../FormField/context.js";
|
|
|
2
2
|
import t from "../internal/CheckIcon.js";
|
|
3
3
|
import n from "../Spinner/Spinner.js";
|
|
4
4
|
import r from "../Tag/Tag.js";
|
|
5
|
-
import { Fragment as i, computed as a, createBlock as o, createCommentVNode as s, createElementBlock as c, createElementVNode as l, createTextVNode as u, createVNode as d, defineComponent as f, mergeModels as p, mergeProps as ee, nextTick as m, normalizeClass as h, normalizeStyle as te, openBlock as g, ref as _, renderList as v, renderSlot as y, toDisplayString as b, unref as x, useAttrs as
|
|
6
|
-
import { ListboxContent as
|
|
5
|
+
import { Fragment as i, computed as a, createBlock as o, createCommentVNode as s, createElementBlock as c, createElementVNode as l, createTextVNode as u, createVNode as d, defineComponent as f, mergeModels as p, mergeProps as ee, nextTick as m, normalizeClass as h, normalizeStyle as te, openBlock as g, ref as _, renderList as v, renderSlot as y, shallowRef as ne, toDisplayString as b, unref as x, useAttrs as re, useModel as ie, withCtx as S } from "vue";
|
|
6
|
+
import { ListboxContent as ae, ListboxFilter as oe, ListboxItem as C, ListboxRoot as se, ListboxVirtualizer as ce, PopoverAnchor as le, PopoverContent as ue, PopoverPortal as de, PopoverRoot as fe } from "reka-ui";
|
|
7
7
|
//#region src/components/Select/Select.vue?vue&type=script&setup=true&lang.ts
|
|
8
|
-
var
|
|
8
|
+
var pe = [
|
|
9
9
|
"aria-expanded",
|
|
10
10
|
"aria-invalid",
|
|
11
11
|
"disabled"
|
|
12
|
-
],
|
|
12
|
+
], me = {
|
|
13
13
|
key: 0,
|
|
14
14
|
class: "sr-only"
|
|
15
|
-
},
|
|
15
|
+
}, he = {
|
|
16
16
|
key: 1,
|
|
17
17
|
class: "text-sui-ink-faint"
|
|
18
|
-
},
|
|
18
|
+
}, ge = {
|
|
19
19
|
key: 2,
|
|
20
20
|
class: "flex flex-wrap gap-1 py-1.25"
|
|
21
|
-
},
|
|
21
|
+
}, _e = {
|
|
22
22
|
key: 4,
|
|
23
23
|
class: "min-w-0 truncate whitespace-nowrap"
|
|
24
|
-
},
|
|
24
|
+
}, ve = {
|
|
25
25
|
key: 0,
|
|
26
26
|
class: "flex min-h-11 flex-wrap items-center gap-1 border-b border-sui-line-subtle px-3 py-2.5"
|
|
27
|
-
},
|
|
27
|
+
}, ye = {
|
|
28
28
|
key: 1,
|
|
29
29
|
class: "flex justify-center py-6"
|
|
30
|
-
},
|
|
30
|
+
}, be = { class: "inline-flex min-w-0 items-center gap-1.5 truncate" }, xe = { class: "inline-flex min-w-0 items-center gap-1.5 truncate" }, Se = { class: "inline-flex min-w-0 items-center gap-1.5" }, Ce = {
|
|
31
31
|
key: 1,
|
|
32
32
|
class: "font-medium"
|
|
33
|
-
},
|
|
33
|
+
}, we = {
|
|
34
34
|
key: 3,
|
|
35
35
|
class: "px-3.5 py-3 text-13 text-sui-ink-muted"
|
|
36
|
-
},
|
|
36
|
+
}, w = "flex min-h-8 cursor-pointer items-center justify-between gap-3 rounded-lg py-1.5 pr-2 pl-2.5 text-13 whitespace-nowrap text-sui-ink outline-hidden select-none data-disabled:cursor-default data-disabled:opacity-50 data-highlighted:bg-sui-sunken data-[state=checked]:font-medium data-[state=checked]:text-sui-accent group-data-keyboard/list:data-highlighted:sui-ring-inset", T = /*@__PURE__*/ f({
|
|
37
37
|
inheritAttrs: !1,
|
|
38
38
|
__name: "Select",
|
|
39
39
|
props: /*@__PURE__*/ p({
|
|
40
40
|
options: {},
|
|
41
|
+
selectedOptions: { default: () => [] },
|
|
41
42
|
multiple: { type: Boolean },
|
|
43
|
+
clearable: { type: Boolean },
|
|
42
44
|
searchable: {
|
|
43
45
|
type: Boolean,
|
|
44
46
|
default: void 0
|
|
45
47
|
},
|
|
48
|
+
filter: {
|
|
49
|
+
type: [Boolean, Function],
|
|
50
|
+
default: !0
|
|
51
|
+
},
|
|
46
52
|
canCreate: { type: Boolean },
|
|
47
53
|
createOption: {
|
|
48
54
|
type: Function,
|
|
@@ -68,15 +74,19 @@ var ue = [
|
|
|
68
74
|
modelValue: { default: null },
|
|
69
75
|
modelModifiers: {}
|
|
70
76
|
}),
|
|
71
|
-
emits: /*@__PURE__*/ p(["query-change"], ["update:modelValue"]),
|
|
77
|
+
emits: /*@__PURE__*/ p(["query-change", "create-error"], ["update:modelValue"]),
|
|
72
78
|
setup(f, { emit: p }) {
|
|
73
|
-
let
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
let T = f, E = p, D = re(), O = a(() => D["aria-label"]), Te = a(() => Object.fromEntries(Object.entries(D).filter(([e]) => e !== "aria-label"))), k = ie(f, "modelValue"), A = _(!1), j = _(""), M = _(!1), N = a(() => Array.isArray(k.value) ? k.value : k.value === null || k.value === void 0 ? [] : [k.value]), P = ne([]), F = a(() => N.value.map((e) => [
|
|
80
|
+
T.options,
|
|
81
|
+
T.selectedOptions,
|
|
82
|
+
P.value
|
|
83
|
+
].map((t) => t.find((t) => t.value === e)).find((e) => !!e)).filter((e) => !!e)), I = a(() => T.searchable ?? (T.multiple || T.canCreate || T.clearable || T.filter === !1 || T.options.length > 8)), L = a(() => T.multiple || T.clearable), Ee = (e, t) => typeof T.filter == "function" ? T.filter(e, t) : e.label.toLowerCase().includes(t.toLowerCase()), R = a(() => {
|
|
84
|
+
let e = j.value.trim();
|
|
85
|
+
return T.options.filter((e) => !(T.multiple && I.value && N.value.includes(e.value))).filter((t) => !e || T.filter === !1 || Ee(t, e));
|
|
76
86
|
}), z = a(() => {
|
|
77
|
-
let e =
|
|
78
|
-
return
|
|
79
|
-
}),
|
|
87
|
+
let e = j.value.trim().toLowerCase();
|
|
88
|
+
return T.canCreate && !!e && !T.options.some((t) => t.label.toLowerCase() === e);
|
|
89
|
+
}), De = a(() => R.value.length > 20), B = [
|
|
80
90
|
"gray",
|
|
81
91
|
"purple",
|
|
82
92
|
"red",
|
|
@@ -85,101 +95,114 @@ var ue = [
|
|
|
85
95
|
"pink",
|
|
86
96
|
"blue",
|
|
87
97
|
"slate"
|
|
88
|
-
], V = a(() => B[
|
|
98
|
+
], V = a(() => B[j.value.length % B.length]), H = { create: !0 }, { attrs: Oe, invalid: U } = e(() => void 0, () => T.invalid);
|
|
89
99
|
function W(e) {
|
|
90
|
-
|
|
100
|
+
j.value = e, E("query-change", e);
|
|
91
101
|
}
|
|
92
102
|
function G(e) {
|
|
93
|
-
|
|
103
|
+
T.disabled || (M.value = e, A.value = !0);
|
|
94
104
|
}
|
|
95
105
|
function K() {
|
|
96
|
-
|
|
106
|
+
A.value = !1, j.value && W("");
|
|
97
107
|
}
|
|
98
108
|
function q(e) {
|
|
99
|
-
|
|
109
|
+
A.value ? K() : G(!!e && e.detail === 0);
|
|
100
110
|
}
|
|
101
111
|
function J(e) {
|
|
102
|
-
|
|
112
|
+
P.value = [e, ...P.value.filter((t) => t.value !== e.value)], T.multiple ? (k.value = N.value.includes(e.value) ? N.value.filter((t) => t !== e.value) : [...N.value, e.value], W("")) : (k.value = e.value, K());
|
|
103
113
|
}
|
|
104
|
-
|
|
105
|
-
|
|
114
|
+
let Y = _(!1);
|
|
115
|
+
async function ke() {
|
|
116
|
+
if (Y.value) return;
|
|
117
|
+
let e = j.value.trim(), t = T.createColored ? {
|
|
106
118
|
label: e,
|
|
107
119
|
color: V.value
|
|
108
120
|
} : { label: e };
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
Y.value = !0;
|
|
122
|
+
try {
|
|
123
|
+
J(T.createOption ? await T.createOption(t) : {
|
|
124
|
+
value: e,
|
|
125
|
+
label: e,
|
|
126
|
+
color: t.color
|
|
127
|
+
});
|
|
128
|
+
} catch (e) {
|
|
129
|
+
E("create-error", e);
|
|
130
|
+
} finally {
|
|
131
|
+
Y.value = !1;
|
|
132
|
+
}
|
|
117
133
|
}
|
|
118
|
-
function X(e) {
|
|
119
|
-
|
|
134
|
+
function X(e, t) {
|
|
135
|
+
e.preventDefault(), "create" in t ? ke() : t.disabled || J(t);
|
|
120
136
|
}
|
|
121
137
|
function Z(e) {
|
|
138
|
+
k.value = T.multiple ? N.value.filter((t) => t !== e) : null;
|
|
139
|
+
}
|
|
140
|
+
function Ae(e) {
|
|
122
141
|
[
|
|
123
142
|
"ArrowUp",
|
|
124
143
|
"ArrowDown",
|
|
125
144
|
"Home",
|
|
126
145
|
"End"
|
|
127
|
-
].includes(e.key) && (
|
|
146
|
+
].includes(e.key) && (M.value = !0), e.key === "Backspace" && L.value && !j.value && N.value.length && Z(N.value[N.value.length - 1]);
|
|
128
147
|
}
|
|
129
|
-
function
|
|
130
|
-
!
|
|
148
|
+
function je(e) {
|
|
149
|
+
!A.value && ["ArrowDown", "ArrowUp"].includes(e.key) && (e.preventDefault(), G(!0));
|
|
131
150
|
}
|
|
132
|
-
function
|
|
151
|
+
function Me(e) {
|
|
133
152
|
e.preventDefault(), m(() => e.target?.querySelector("[data-select-filter], [role=\"listbox\"]")?.focus());
|
|
134
153
|
}
|
|
135
154
|
let Q = _();
|
|
136
|
-
function
|
|
155
|
+
function Ne(e) {
|
|
156
|
+
let t = e.detail.originalEvent.target;
|
|
157
|
+
t instanceof Node && Q.value?.$el?.contains(t) && e.preventDefault();
|
|
158
|
+
}
|
|
159
|
+
function Pe(e) {
|
|
137
160
|
e.preventDefault(), Q.value?.$el?.focus();
|
|
138
161
|
}
|
|
139
162
|
let $ = _();
|
|
140
|
-
function
|
|
141
|
-
!
|
|
163
|
+
function Fe(e) {
|
|
164
|
+
!M.value && e && (N.value.includes(e.value) || m(() => $.value && !M.value && ($.value.highlightedElement = null)));
|
|
142
165
|
}
|
|
143
|
-
let
|
|
166
|
+
let Ie = {
|
|
144
167
|
md: "min-h-9 pr-2.5 pl-3 text-sm",
|
|
145
168
|
sm: "h-7 pr-1.5 pl-2 text-13"
|
|
146
169
|
};
|
|
147
|
-
return (e, a) => (g(), o(x(
|
|
148
|
-
open:
|
|
170
|
+
return (e, a) => (g(), o(x(fe), {
|
|
171
|
+
open: A.value,
|
|
149
172
|
"onUpdate:open": a[2] ||= (e) => e ? G(!1) : K()
|
|
150
173
|
}, {
|
|
151
|
-
default: S(() => [d(x(
|
|
174
|
+
default: S(() => [d(x(le), {
|
|
152
175
|
ref_key: "anchor",
|
|
153
176
|
ref: Q,
|
|
154
177
|
"as-child": ""
|
|
155
178
|
}, {
|
|
156
179
|
default: S(() => [y(e.$slots, "trigger", {
|
|
157
|
-
open:
|
|
158
|
-
selected:
|
|
180
|
+
open: A.value,
|
|
181
|
+
selected: F.value,
|
|
159
182
|
toggle: q
|
|
160
183
|
}, () => [l("button", ee({
|
|
161
|
-
...x(
|
|
162
|
-
...
|
|
184
|
+
...x(Oe),
|
|
185
|
+
...Te.value
|
|
163
186
|
}, {
|
|
164
|
-
"aria-expanded":
|
|
187
|
+
"aria-expanded": A.value,
|
|
165
188
|
"aria-haspopup": "listbox",
|
|
166
189
|
"aria-invalid": x(U) ? "true" : void 0,
|
|
167
|
-
class: ["group inline-flex items-center justify-between gap-1.5 rounded-(--sui-radius-button) border bg-sui-surface text-left text-sui-ink transition-
|
|
190
|
+
class: ["group inline-flex items-center justify-between gap-1.5 rounded-(--sui-radius-button) border bg-sui-surface text-left text-sui-ink transition-[border-color,box-shadow] duration-150 focus-visible:border-sui-accent focus-visible:sui-ring-field disabled:pointer-events-none disabled:opacity-60", [Ie[f.size], x(U) ? "border-sui-danger-mid" : "border-sui-line-strong hover:border-sui-ink-muted"]],
|
|
168
191
|
disabled: f.disabled,
|
|
169
192
|
type: "button",
|
|
170
193
|
onClick: q,
|
|
171
|
-
onKeydown:
|
|
194
|
+
onKeydown: je
|
|
172
195
|
}), [
|
|
173
|
-
|
|
174
|
-
|
|
196
|
+
O.value ? (g(), c("span", me, b(O.value) + ": ", 1)) : s("", !0),
|
|
197
|
+
F.value.length ? f.multiple ? (g(), c("span", ge, [(g(!0), c(i, null, v(F.value, (e) => (g(), o(r, {
|
|
175
198
|
key: e.value,
|
|
176
199
|
color: e.color ?? void 0,
|
|
177
200
|
name: e.label
|
|
178
|
-
}, null, 8, ["color", "name"]))), 128))])) :
|
|
201
|
+
}, null, 8, ["color", "name"]))), 128))])) : F.value[0].color ? (g(), o(r, {
|
|
179
202
|
key: 3,
|
|
180
|
-
color:
|
|
181
|
-
name:
|
|
182
|
-
}, null, 8, ["color", "name"])) : (g(), c("span",
|
|
203
|
+
color: F.value[0].color,
|
|
204
|
+
name: F.value[0].label
|
|
205
|
+
}, null, 8, ["color", "name"])) : (g(), c("span", _e, b(F.value[0].label), 1)) : (g(), c("span", he, b(f.placeholder), 1)),
|
|
183
206
|
a[3] ||= l("svg", {
|
|
184
207
|
"aria-hidden": "true",
|
|
185
208
|
class: "size-4 shrink-0 fill-none stroke-sui-ink-muted transition-transform duration-150 group-aria-expanded:rotate-180",
|
|
@@ -188,75 +211,76 @@ var ue = [
|
|
|
188
211
|
"stroke-width": "1.5",
|
|
189
212
|
viewBox: "0 0 16 16"
|
|
190
213
|
}, [l("path", { d: "M4.5 6.5 8 10l3.5-3.5" })], -1)
|
|
191
|
-
], 16,
|
|
214
|
+
], 16, pe)])]),
|
|
192
215
|
_: 3
|
|
193
|
-
}, 512), d(x(
|
|
194
|
-
default: S(() => [d(x(
|
|
216
|
+
}, 512), d(x(de), null, {
|
|
217
|
+
default: S(() => [d(x(ue), {
|
|
195
218
|
align: f.align,
|
|
196
|
-
"aria-label":
|
|
197
|
-
class: h(["z-[
|
|
219
|
+
"aria-label": O.value ?? f.placeholder,
|
|
220
|
+
class: h(["z-[1100] overflow-hidden rounded-xl border border-sui-line-subtle bg-sui-surface text-sui-ink shadow-lg data-[state=open]:animate-sui-menu-in", I.value ? "min-w-[max(var(--reka-popover-trigger-width),15rem)]" : "min-w-(--reka-popover-trigger-width)"]),
|
|
198
221
|
side: f.side,
|
|
199
222
|
"side-offset": 4,
|
|
200
223
|
style: te(f.panelWidth ? { width: f.panelWidth } : void 0),
|
|
201
|
-
onCloseAutoFocus:
|
|
202
|
-
onOpenAutoFocus:
|
|
224
|
+
onCloseAutoFocus: Pe,
|
|
225
|
+
onOpenAutoFocus: Me,
|
|
226
|
+
onPointerDownOutside: Ne
|
|
203
227
|
}, {
|
|
204
|
-
default: S(() => [d(x(
|
|
228
|
+
default: S(() => [d(x(se), {
|
|
205
229
|
ref_key: "listbox",
|
|
206
230
|
ref: $,
|
|
207
231
|
"highlight-on-hover": !0,
|
|
208
|
-
"model-value": f.multiple ?
|
|
232
|
+
"model-value": f.multiple ? N.value : N.value[0] ?? void 0,
|
|
209
233
|
multiple: f.multiple,
|
|
210
|
-
onHighlight:
|
|
211
|
-
onKeydown:
|
|
212
|
-
onPointermove: a[1] ||= (e) =>
|
|
234
|
+
onHighlight: Fe,
|
|
235
|
+
onKeydown: Ae,
|
|
236
|
+
onPointermove: a[1] ||= (e) => M.value = !1
|
|
213
237
|
}, {
|
|
214
|
-
default: S(() => [
|
|
238
|
+
default: S(() => [I.value ? (g(), c("div", ve, [L.value ? (g(!0), c(i, { key: 0 }, v(F.value, (e) => (g(), o(r, {
|
|
215
239
|
key: e.value,
|
|
216
240
|
color: e.color ?? void 0,
|
|
217
241
|
name: e.label,
|
|
218
242
|
removable: "",
|
|
219
243
|
"remove-label": f.removeLabel,
|
|
220
|
-
onRemove: (t) =>
|
|
244
|
+
onRemove: (t) => Z(e.value)
|
|
221
245
|
}, null, 8, [
|
|
222
246
|
"color",
|
|
223
247
|
"name",
|
|
224
248
|
"remove-label",
|
|
225
249
|
"onRemove"
|
|
226
|
-
]))), 128)) : s("", !0), d(x(
|
|
227
|
-
"aria-label":
|
|
250
|
+
]))), 128)) : s("", !0), d(x(oe), {
|
|
251
|
+
"aria-label": O.value ?? f.placeholder,
|
|
228
252
|
class: "h-5 min-w-16 flex-1 rounded-none border-none bg-transparent p-0 text-13 text-sui-ink shadow-none outline-hidden placeholder:text-sui-ink-faint focus:border-none focus:shadow-none focus:ring-0 focus:outline-hidden",
|
|
229
253
|
"data-select-filter": "",
|
|
230
|
-
"model-value":
|
|
231
|
-
placeholder:
|
|
254
|
+
"model-value": j.value,
|
|
255
|
+
placeholder: L.value && F.value.length ? "" : f.searchPlaceholder,
|
|
232
256
|
"onUpdate:modelValue": W
|
|
233
257
|
}, null, 8, [
|
|
234
258
|
"aria-label",
|
|
235
259
|
"model-value",
|
|
236
260
|
"placeholder"
|
|
237
|
-
])])) : s("", !0), f.loading ? (g(), c("div",
|
|
261
|
+
])])) : s("", !0), f.loading ? (g(), c("div", ye, [d(n, { size: "sm" })])) : R.value.length || z.value ? (g(), o(x(ae), {
|
|
238
262
|
key: 2,
|
|
239
|
-
"aria-label":
|
|
263
|
+
"aria-label": O.value ?? f.placeholder,
|
|
240
264
|
class: "group/list max-h-60 overflow-auto p-1 outline-hidden",
|
|
241
|
-
"data-keyboard":
|
|
265
|
+
"data-keyboard": M.value || void 0
|
|
242
266
|
}, {
|
|
243
|
-
default: S(() => [
|
|
267
|
+
default: S(() => [De.value ? (g(), o(x(ce), {
|
|
244
268
|
key: 0,
|
|
245
269
|
"estimate-size": 32,
|
|
246
270
|
options: R.value,
|
|
247
271
|
"text-content": (e) => e.label
|
|
248
272
|
}, {
|
|
249
|
-
default: S(({ option: n }) => [d(x(
|
|
250
|
-
class: h(
|
|
273
|
+
default: S(({ option: n }) => [d(x(C), {
|
|
274
|
+
class: h(w),
|
|
251
275
|
disabled: n.disabled,
|
|
252
276
|
value: n.value,
|
|
253
|
-
onSelect: (e) =>
|
|
277
|
+
onSelect: (e) => X(e, n)
|
|
254
278
|
}, {
|
|
255
|
-
default: S(() => [l("span",
|
|
279
|
+
default: S(() => [l("span", be, [y(e.$slots, "option", { option: n }, () => [n.color ? (g(), o(r, {
|
|
256
280
|
key: 0,
|
|
257
281
|
color: n.color,
|
|
258
282
|
name: n.label
|
|
259
|
-
}, null, 8, ["color", "name"])) : (g(), c(i, { key: 1 }, [u(b(n.label), 1)], 64))])]),
|
|
283
|
+
}, null, 8, ["color", "name"])) : (g(), c(i, { key: 1 }, [u(b(n.label), 1)], 64))])]), N.value.includes(n.value) ? (g(), o(t, { key: 0 })) : s("", !0)]),
|
|
260
284
|
_: 2
|
|
261
285
|
}, 1032, [
|
|
262
286
|
"disabled",
|
|
@@ -264,38 +288,38 @@ var ue = [
|
|
|
264
288
|
"onSelect"
|
|
265
289
|
])]),
|
|
266
290
|
_: 3
|
|
267
|
-
}, 8, ["options", "text-content"])) : (g(!0), c(i, { key: 1 }, v(R.value, (n) => (g(), o(x(
|
|
291
|
+
}, 8, ["options", "text-content"])) : (g(!0), c(i, { key: 1 }, v(R.value, (n) => (g(), o(x(C), {
|
|
268
292
|
key: n.value,
|
|
269
|
-
class: h(
|
|
293
|
+
class: h(w),
|
|
270
294
|
disabled: n.disabled,
|
|
271
295
|
value: n.value,
|
|
272
|
-
onSelect: (e) =>
|
|
296
|
+
onSelect: (e) => X(e, n)
|
|
273
297
|
}, {
|
|
274
|
-
default: S(() => [l("span",
|
|
298
|
+
default: S(() => [l("span", xe, [y(e.$slots, "option", { option: n }, () => [n.color ? (g(), o(r, {
|
|
275
299
|
key: 0,
|
|
276
300
|
color: n.color,
|
|
277
301
|
name: n.label
|
|
278
|
-
}, null, 8, ["color", "name"])) : (g(), c(i, { key: 1 }, [u(b(n.label), 1)], 64))])]),
|
|
302
|
+
}, null, 8, ["color", "name"])) : (g(), c(i, { key: 1 }, [u(b(n.label), 1)], 64))])]), N.value.includes(n.value) ? (g(), o(t, { key: 0 })) : s("", !0)]),
|
|
279
303
|
_: 2
|
|
280
304
|
}, 1032, [
|
|
281
305
|
"disabled",
|
|
282
306
|
"value",
|
|
283
307
|
"onSelect"
|
|
284
|
-
]))), 128)), z.value ? (g(), o(x(
|
|
308
|
+
]))), 128)), z.value ? (g(), o(x(C), {
|
|
285
309
|
key: 2,
|
|
286
|
-
class: h(
|
|
310
|
+
class: h(w),
|
|
287
311
|
value: H,
|
|
288
|
-
onSelect: a[0] ||= (e) =>
|
|
312
|
+
onSelect: a[0] ||= (e) => X(e, H)
|
|
289
313
|
}, {
|
|
290
|
-
default: S(() => [l("span",
|
|
314
|
+
default: S(() => [l("span", Se, [u(b(f.createLabel) + " ", 1), f.createColored ? (g(), o(r, {
|
|
291
315
|
key: 0,
|
|
292
316
|
color: V.value,
|
|
293
|
-
name:
|
|
294
|
-
}, null, 8, ["color", "name"])) : (g(), c("strong",
|
|
317
|
+
name: j.value.trim()
|
|
318
|
+
}, null, 8, ["color", "name"])) : (g(), c("strong", Ce, "“" + b(j.value.trim()) + "”", 1))])]),
|
|
295
319
|
_: 1
|
|
296
320
|
})) : s("", !0)]),
|
|
297
321
|
_: 3
|
|
298
|
-
}, 8, ["aria-label", "data-keyboard"])) : (g(), c("div",
|
|
322
|
+
}, 8, ["aria-label", "data-keyboard"])) : (g(), c("div", we, b(f.emptyLabel), 1))]),
|
|
299
323
|
_: 3
|
|
300
324
|
}, 8, ["model-value", "multiple"])]),
|
|
301
325
|
_: 3
|
|
@@ -313,6 +337,6 @@ var ue = [
|
|
|
313
337
|
}
|
|
314
338
|
});
|
|
315
339
|
//#endregion
|
|
316
|
-
export {
|
|
340
|
+
export { T as default };
|
|
317
341
|
|
|
318
342
|
//# sourceMappingURL=Select.vue_vue_type_script_setup_true_lang.js.map
|