@vue-interface/searchable-select-field 2.0.3 → 2.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"searchable-select-field.js","sources":["../../../node_modules/.pnpm/@heroicons+vue@2.2.0_vue@3.5.26_typescript@5.9.3_/node_modules/@heroicons/vue/24/outline/esm/ChevronDownIcon.js","../../../node_modules/.pnpm/@heroicons+vue@2.2.0_vue@3.5.26_typescript@5.9.3_/node_modules/@heroicons/vue/24/outline/esm/XMarkIcon.js","../src/SearchableSelectField.vue"],"sourcesContent":["import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nexport default function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"none\",\n viewBox: \"0 0 24 24\",\n \"stroke-width\": \"1.5\",\n stroke: \"currentColor\",\n \"aria-hidden\": \"true\",\n \"data-slot\": \"icon\"\n }, [\n _createElementVNode(\"path\", {\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\",\n d: \"m19.5 8.25-7.5 7.5-7.5-7.5\"\n })\n ]))\n}","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nexport default function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"none\",\n viewBox: \"0 0 24 24\",\n \"stroke-width\": \"1.5\",\n stroke: \"currentColor\",\n \"aria-hidden\": \"true\",\n \"data-slot\": \"icon\"\n }, [\n _createElementVNode(\"path\", {\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\",\n d: \"M6 18 18 6M6 6l12 12\"\n })\n ]))\n}","<script setup lang=\"ts\" generic=\"T, Value\">\nimport { ChevronDownIcon, XMarkIcon } from '@heroicons/vue/24/outline';\nimport { ActivityIndicator, Pulse } from '@vue-interface/activity-indicator';\nimport type { FormControlEvents, FormControlProps, FormControlSlots } from '@vue-interface/form-control';\nimport { useFormControl } from '@vue-interface/form-control';\nimport { InputField } from '@vue-interface/input-field';\nimport Fuse, { IFuseOptions } from 'fuse.js';\nimport { InputHTMLAttributes, computed, nextTick, ref, useTemplateRef, watch, watchEffect } from 'vue';\n\nconst props = withDefaults(defineProps<SearchableSelectFieldProps<T,Value>>(), {\n formControlClass: 'form-control',\n labelClass: 'form-label',\n size: 'form-control-md',\n clearable: true,\n options: () => []\n});\n\nconst model = defineModel<T>();\nconst isInteractive = computed(() => !props.disabled && !props.readonly);\n\ndefineSlots<FormControlSlots<SearchableSelectFieldSizePrefix,T> & {\n default(props: { option: T; display?: (option: T) => string }): any;\n}>();\n\nconst emit = defineEmits<FormControlEvents>();\n\nconst {\n controlAttributes,\n formGroupClasses,\n listeners\n} = useFormControl<InputHTMLAttributes, SearchableSelectFieldSizePrefix, T|undefined, T>({ model, props, emit });\n\nwatchEffect(() => {\n if(props.value !== undefined) {\n model.value = props.value;\n }\n});\n\nconst input = ref<string>();\nconst field = useTemplateRef<any>('field');\nconst showOptions = ref(false);\nconst active = ref<number>();\nconst buttons = useTemplateRef<HTMLButtonElement[]>('buttons');\nconst optionsEl = useTemplateRef<HTMLDivElement>('optionsEl');\n\nconst keys = computed(() => {\n return typeof props.options === 'object' && props.options?.[0]\n ? Object.keys(props.options?.[0])\n : ['$'];\n});\n\nlet fuse: Fuse<T> = createFuse();\n\nfunction createFuse() {\n return new Fuse(props.options ?? [], props.fuseOptions ?? {\n includeScore: true,\n threshold: .45,\n keys: keys.value\n });\n}\n\nconst filtered = computed<T[]>(() => {\n if(!input.value) {\n return props.options ?? [];\n }\n\n const matches = fuse.search(input.value).map(({ item }) => item);\n\n if(props.allowCustom && !matches.length) {\n return props.options;\n }\n\n return matches;\n});\n\nwatch(() => props.options, () => {\n fuse = createFuse();\n});\n\nfunction scrollIntoView(child?: HTMLElement) {\n const parent = optionsEl.value;\n\n if(!parent || !child) {\n return;\n }\n\n const parentRect = parent.getBoundingClientRect();\n const childRect = child.getBoundingClientRect();\n\n const childTop = childRect.top - parentRect.top + parent.scrollTop;\n\n parent.scrollTop = childTop;\n};\n\nwatch([input, active], ([input, active]) => {\n if(input) {\n buttons.value?.[0]?.scrollIntoView({\n block: 'nearest',\n inline: 'nearest'\n });\n }\n else if(active !== undefined) {\n buttons.value?.[active]?.scrollIntoView({\n block: 'nearest',\n inline: 'nearest'\n });\n }\n});\n\nwatch(optionsEl, (value) => {\n if(!value || active.value === undefined) {\n return;\n }\n\n nextTick(() => {\n scrollIntoView(buttons.value?.[active.value as number]);\n });\n});\n\nfunction select(option?: T) {\n model.value = option;\n active.value = option && props.options.includes(option)\n ? props.options.indexOf(option)\n : undefined;\n input.value = undefined;\n showOptions.value = false;\n}\n\nfunction onInput(e: Event) {\n if (!isInteractive.value) return;\n\n showOptions.value = true;\n active.value = undefined;\n input.value = (e.target as HTMLInputElement)?.value;\n\n if(!input.value && !props.allowCustom) {\n model.value = undefined;\n }\n\n if(props.allowCustom) {\n model.value = input.value as T;\n }\n}\n\nfunction onKeypressEnter() {\n if (!isInteractive.value) return;\n\n if(!showOptions.value) {\n showOptions.value = true;\n return;\n }\n\n if(props.allowCustom && active.value === undefined) {\n select(input.value as T ?? model.value);\n }\n else if(active.value === undefined) {\n select(filtered.value[0]);\n }\n else if(filtered.value[active.value]) {\n select(filtered.value[active.value]);\n }\n else {\n select(undefined);\n }\n}\n\nfunction onKeydownUp() {\n if (!isInteractive.value) return;\n \n showOptions.value = true;\n\n if(!active.value) {\n active.value = filtered.value.length - 1;\n }\n else {\n active.value--;\n }\n}\n\nfunction onKeydownDown() {\n if (!isInteractive.value) return;\n\n showOptions.value = true;\n\n if(active.value === undefined || active.value === filtered.value.length - 1) {\n active.value = 0;\n }\n else {\n active.value++;\n }\n}\n\nfunction onBlur() {\n showOptions.value = false;\n input.value = undefined;\n}\n\nfunction onClickOption(option: T) {\n if (!isInteractive.value) return;\n select(option);\n}\n\nfunction clear() {\n if (!isInteractive.value) return;\n input.value = undefined;\n model.value = undefined;\n}\n\nfunction toggle() {\n if (!isInteractive.value) return;\n \n if(showOptions.value ) {\n showOptions.value = false;\n }\n else {\n showOptions.value = true;\n field.value?.$el?.querySelector('input')?.focus();\n }\n}\n\nconst canClear = computed(() => {\n return props.clearable && (!!input.value || !!model.value) && isInteractive.value;\n});\n</script>\n<script lang=\"ts\">\nexport type SearchableSelectFieldSizePrefix = 'form-control';\n\nexport type SearchableSelectFieldProps<ModelValue, Value> = FormControlProps<\n InputHTMLAttributes, \n SearchableSelectFieldSizePrefix, \n ModelValue, \n Value\n> & {\n options?: ModelValue[];\n fuseOptions?: IFuseOptions<ModelValue>;\n display?: (option: ModelValue) => string;\n allowCustom?: boolean;\n clearable?: boolean;\n};\n</script>\n\n\n<template>\n <div class=\"relative [&_.form-control]:pr-8\">\n <InputField\n ref=\"field\"\n class=\"searchable-select-field-input\"\n :class=\"{ 'has-clear-button': canClear, formGroupClasses }\"\n :size=\"size\"\n v-bind=\"{ ...$attrs, controlAttributes, listeners }\"\n :name=\"name\"\n :label=\"label\"\n :model-value=\"input ?? (model && props?.display ? props?.display?.(model) : model)\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :color=\"color\"\n :error=\"error\"\n :errors=\"errors\"\n :feedback=\"feedback\"\n :valid=\"valid\"\n :invalid=\"invalid\"\n @click=\"isInteractive && (showOptions = true)\"\n @focus=\"isInteractive && (showOptions = true)\"\n @blur=\"onBlur\"\n @keypress.enter.prevent=\"onKeypressEnter\"\n @keydown.up.prevent=\"onKeydownUp\"\n @keydown.down.prevent=\"onKeydownDown\"\n @keyup.escape=\"showOptions = false\"\n @input=\"onInput\">\n <template #icon v-if=\"$slots.icon\">\n <slot name=\"icon\" />\n </template>\n <template #activity>\n <slot\n name=\"activity\"\n v-bind=\"{ disabled, options, invalid, valid }\">\n <ActivityIndicator\n v-if=\"!disabled && !options\"\n :type=\"Pulse\"\n size=\"activity-indicator-sm\" />\n <button\n v-else-if=\"canClear\"\n type=\"button\"\n class=\"searchable-select-field-clear-button\"\n @click.stop=\"clear\">\n <XMarkIcon class=\"size-[1.25em]\" />\n </button>\n <button\n v-else-if=\"!invalid && !valid\"\n type=\"button\"\n @click.stop=\"toggle\">\n <ChevronDownIcon class=\"size-[1em]\" />\n </button>\n </slot>\n </template>\n </InputField>\n <div\n v-if=\"showOptions && filtered.length\"\n ref=\"optionsEl\"\n tabindex=\"-1\"\n class=\"searchable-select-field-dropdown\"\n :class=\"size\"\n @mousedown.prevent.stop>\n <button\n v-for=\"(option, i) in filtered\"\n ref=\"buttons\"\n :key=\"i\"\n type=\"button\"\n tabindex=\"-1\"\n :title=\"display?.(option) ?? String(option)\"\n :class=\"{\n ['bg-neutral-200 dark:bg-neutral-700']: active === i\n }\"\n @mousedown.prevent\n @click=\"onClickOption(option)\">\n <slot v-bind=\"{ option, display }\">\n <div class=\"truncate\">\n {{ display?.(option) ?? option }}\n </div>\n </slot>\n </button>\n </div>\n </div>\n</template>"],"names":["render","_ctx","_cache","_openBlock","_createElementBlock","_createElementVNode","props","__props","model","_useModel","isInteractive","computed","emit","__emit","controlAttributes","formGroupClasses","listeners","useFormControl","watchEffect","input","ref","field","useTemplateRef","showOptions","active","buttons","optionsEl","keys","fuse","createFuse","Fuse","filtered","matches","item","watch","scrollIntoView","child","parent","parentRect","childTop","value","nextTick","select","option","onInput","e","onKeypressEnter","onKeydownUp","onKeydownDown","onBlur","onClickOption","clear","toggle","canClear","_hoisted_1","_createVNode","_unref","_mergeProps","$attrs","$event","_renderSlot","_normalizeProps","_guardReactiveProps","_createBlock","ActivityIndicator","Pulse","XMarkIcon","ChevronDownIcon","$slots","_normalizeClass","_Fragment","_renderList","i","_hoisted_3","_toDisplayString"],"mappings":"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","x_google_ignoreList":[0,1]}
1
+ {"version":3,"file":"searchable-select-field.js","sources":["../../../node_modules/.pnpm/@heroicons+vue@2.2.0_vue@3.5.27_typescript@5.9.3_/node_modules/@heroicons/vue/24/outline/esm/ChevronDownIcon.js","../../../node_modules/.pnpm/@heroicons+vue@2.2.0_vue@3.5.27_typescript@5.9.3_/node_modules/@heroicons/vue/24/outline/esm/XMarkIcon.js","../src/SearchableSelectField.vue"],"sourcesContent":["import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nexport default function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"none\",\n viewBox: \"0 0 24 24\",\n \"stroke-width\": \"1.5\",\n stroke: \"currentColor\",\n \"aria-hidden\": \"true\",\n \"data-slot\": \"icon\"\n }, [\n _createElementVNode(\"path\", {\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\",\n d: \"m19.5 8.25-7.5 7.5-7.5-7.5\"\n })\n ]))\n}","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nexport default function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"none\",\n viewBox: \"0 0 24 24\",\n \"stroke-width\": \"1.5\",\n stroke: \"currentColor\",\n \"aria-hidden\": \"true\",\n \"data-slot\": \"icon\"\n }, [\n _createElementVNode(\"path\", {\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\",\n d: \"M6 18 18 6M6 6l12 12\"\n })\n ]))\n}","<script setup lang=\"ts\" generic=\"ModelValue, Value extends ModelValue\">\nimport { ChevronDownIcon, XMarkIcon } from '@heroicons/vue/24/outline';\nimport { ActivityIndicator, Pulse } from '@vue-interface/activity-indicator';\nimport type { FormControlEvents, FormControlProps, FormControlSlots } from '@vue-interface/form-control';\nimport { useFormControl } from '@vue-interface/form-control';\nimport { InputField } from '@vue-interface/input-field';\nimport Fuse, { IFuseOptions } from 'fuse.js';\nimport { type HTMLAttributes, computed, nextTick, ref, useTemplateRef, watch, watchEffect } from 'vue';\n\nconst props = withDefaults(defineProps<SearchableSelectFieldProps<ModelValue,Value>>(), {\n formControlClass: 'form-control',\n labelClass: 'form-label',\n size: 'form-control-md',\n clearable: true,\n options: () => []\n});\n\nconst model = defineModel<ModelValue>();\nconst isInteractive = computed(() => !props.disabled && !props.readonly);\n\ndefineSlots<FormControlSlots<SearchableSelectFieldSizePrefix,ModelValue> & {\n default(props: { option: ModelValue; display?: (option: ModelValue) => string }): any;\n}>();\n\nconst emit = defineEmits<FormControlEvents>();\n\nconst {\n controlAttributes,\n formGroupClasses,\n listeners\n} = useFormControl<HTMLAttributes, SearchableSelectFieldSizePrefix, ModelValue|undefined, ModelValue>({ model, props, emit });\n\nwatchEffect(() => {\n if(props.value !== undefined) {\n model.value = props.value;\n }\n});\n\nconst input = ref<string>();\nconst field = useTemplateRef<any>('field');\nconst showOptions = ref(false);\nconst active = ref<number>();\nconst buttons = useTemplateRef<HTMLButtonElement[]>('buttons');\nconst optionsEl = useTemplateRef<HTMLDivElement>('optionsEl');\n\nconst keys = computed(() => {\n return typeof props.options === 'object' && props.options?.[0]\n ? Object.keys(props.options?.[0])\n : ['$'];\n});\n\nlet fuse: Fuse<ModelValue> = createFuse();\n\nfunction createFuse() {\n return new Fuse(props.options ?? [], props.fuseOptions ?? {\n includeScore: true,\n threshold: .45,\n keys: keys.value\n });\n}\n\nconst filtered = computed<ModelValue[]>(() => {\n if(!input.value) {\n return props.options ?? [];\n }\n\n const matches = fuse.search(input.value).map(({ item }) => item);\n\n if(props.allowCustom && !matches.length) {\n return props.options;\n }\n\n return matches;\n});\n\nwatch(() => props.options, () => {\n fuse = createFuse();\n});\n\nfunction scrollIntoView(child?: HTMLElement) {\n const parent = optionsEl.value;\n\n if(!parent || !child) {\n return;\n }\n\n const parentRect = parent.getBoundingClientRect();\n const childRect = child.getBoundingClientRect();\n\n const childTop = childRect.top - parentRect.top + parent.scrollTop;\n\n parent.scrollTop = childTop;\n};\n\nwatch([input, active], ([input, active]) => {\n if(input) {\n buttons.value?.[0]?.scrollIntoView({\n block: 'nearest',\n inline: 'nearest'\n });\n }\n else if(active !== undefined) {\n buttons.value?.[active]?.scrollIntoView({\n block: 'nearest',\n inline: 'nearest'\n });\n }\n});\n\nwatch(optionsEl, (value) => {\n if(!value || active.value === undefined) {\n return;\n }\n\n nextTick(() => {\n scrollIntoView(buttons.value?.[active.value as number]);\n });\n});\n\nfunction select(option?: ModelValue) {\n model.value = option;\n active.value = option && props.options.includes(option)\n ? props.options.indexOf(option)\n : undefined;\n input.value = undefined;\n showOptions.value = false;\n}\n\nfunction onInput(e: Event) {\n if (!isInteractive.value) return;\n\n showOptions.value = true;\n active.value = undefined;\n input.value = (e.target as HTMLInputElement)?.value;\n\n if(!input.value && !props.allowCustom) {\n model.value = undefined;\n }\n\n if(props.allowCustom) {\n model.value = input.value as ModelValue;\n }\n}\n\nfunction onKeypressEnter() {\n if (!isInteractive.value) return;\n\n if(!showOptions.value) {\n showOptions.value = true;\n return;\n }\n\n if(props.allowCustom && active.value === undefined) {\n select(input.value as ModelValue ?? model.value);\n }\n else if(active.value === undefined) {\n select(filtered.value[0]);\n }\n else if(filtered.value[active.value]) {\n select(filtered.value[active.value]);\n }\n else {\n select(undefined);\n }\n}\n\nfunction onKeydownUp() {\n if (!isInteractive.value) return;\n \n showOptions.value = true;\n\n if(!active.value) {\n active.value = filtered.value.length - 1;\n }\n else {\n active.value--;\n }\n}\n\nfunction onKeydownDown() {\n if (!isInteractive.value) return;\n\n showOptions.value = true;\n\n if(active.value === undefined || active.value === filtered.value.length - 1) {\n active.value = 0;\n }\n else {\n active.value++;\n }\n}\n\nfunction onBlur() {\n showOptions.value = false;\n input.value = undefined;\n}\n\nfunction onClickOption(option: ModelValue) {\n if (!isInteractive.value) return;\n select(option);\n}\n\nfunction clear() {\n if (!isInteractive.value) return;\n input.value = undefined;\n model.value = undefined;\n}\n\nfunction toggle() {\n if (!isInteractive.value) return;\n \n if(showOptions.value ) {\n showOptions.value = false;\n }\n else {\n showOptions.value = true;\n field.value?.$el?.querySelector('input')?.focus();\n }\n}\n\nconst canClear = computed(() => {\n return props.clearable && (!!input.value || !!model.value) && isInteractive.value;\n});\n</script>\n<script lang=\"ts\">\nexport type SearchableSelectFieldSizePrefix = 'form-control';\n\nexport type SearchableSelectFieldProps<ModelValue, Value> = FormControlProps<\n HTMLAttributes, \n SearchableSelectFieldSizePrefix, \n ModelValue, \n Value\n> & {\n options?: ModelValue[];\n fuseOptions?: IFuseOptions<ModelValue>;\n display?: (option: ModelValue) => string;\n allowCustom?: boolean;\n clearable?: boolean;\n};\n</script>\n\n\n<template>\n <div class=\"relative [&_.form-control]:pr-8\">\n <InputField\n ref=\"field\"\n class=\"searchable-select-field-input\"\n :class=\"{ 'has-clear-button': canClear, formGroupClasses }\"\n :size=\"size\"\n v-bind=\"{ ...$attrs, controlAttributes, listeners }\"\n :name=\"name\"\n :label=\"label\"\n :model-value=\"input ?? (model && props?.display ? props?.display?.(model) : model)\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :color=\"color\"\n :error=\"error\"\n :errors=\"errors\"\n :feedback=\"feedback\"\n :valid=\"valid\"\n :invalid=\"invalid\"\n @click=\"isInteractive && (showOptions = true)\"\n @focus=\"isInteractive && (showOptions = true)\"\n @blur=\"onBlur\"\n @keypress.enter.prevent=\"onKeypressEnter\"\n @keydown.up.prevent=\"onKeydownUp\"\n @keydown.down.prevent=\"onKeydownDown\"\n @keyup.escape=\"showOptions = false\"\n @input=\"onInput\">\n <template #icon v-if=\"$slots.icon\">\n <slot name=\"icon\" />\n </template>\n <template #activity>\n <slot\n name=\"activity\"\n v-bind=\"{ disabled, options, invalid, valid }\">\n <ActivityIndicator\n v-if=\"!disabled && !options\"\n :type=\"Pulse\"\n size=\"activity-indicator-sm\" />\n <button\n v-else-if=\"canClear\"\n type=\"button\"\n class=\"searchable-select-field-clear-button\"\n @click.stop=\"clear\">\n <XMarkIcon class=\"size-[1.25em]\" />\n </button>\n <button\n v-else-if=\"!invalid && !valid\"\n type=\"button\"\n @click.stop=\"toggle\">\n <ChevronDownIcon class=\"size-[1em]\" />\n </button>\n </slot>\n </template>\n </InputField>\n <div\n v-if=\"showOptions && filtered.length\"\n ref=\"optionsEl\"\n tabindex=\"-1\"\n class=\"searchable-select-field-dropdown\"\n :class=\"size\"\n @mousedown.prevent.stop>\n <button\n v-for=\"(option, i) in filtered\"\n ref=\"buttons\"\n :key=\"i\"\n type=\"button\"\n tabindex=\"-1\"\n :title=\"display?.(option) ?? String(option)\"\n :class=\"{\n ['bg-neutral-200 dark:bg-neutral-700']: active === i\n }\"\n @mousedown.prevent\n @click=\"onClickOption(option)\">\n <slot v-bind=\"{ option, display }\">\n <div class=\"truncate\">\n {{ display?.(option) ?? option }}\n </div>\n </slot>\n </button>\n </div>\n </div>\n</template>"],"names":["render","_ctx","_cache","_openBlock","_createElementBlock","_createElementVNode","props","__props","model","_useModel","isInteractive","computed","emit","__emit","controlAttributes","formGroupClasses","listeners","useFormControl","watchEffect","input","ref","field","useTemplateRef","showOptions","active","buttons","optionsEl","keys","fuse","createFuse","Fuse","filtered","matches","item","watch","scrollIntoView","child","parent","parentRect","childTop","value","nextTick","select","option","onInput","e","onKeypressEnter","onKeydownUp","onKeydownDown","onBlur","onClickOption","clear","toggle","canClear","_hoisted_1","_createVNode","_unref","_mergeProps","$attrs","$event","_renderSlot","_normalizeProps","_guardReactiveProps","_createBlock","ActivityIndicator","Pulse","XMarkIcon","ChevronDownIcon","$slots","_normalizeClass","_Fragment","_renderList","i","_hoisted_3","_toDisplayString"],"mappings":"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","x_google_ignoreList":[0,1]}
@@ -1 +1 @@
1
- {"version":3,"file":"searchable-select-field.umd.cjs","sources":["../../../node_modules/.pnpm/@heroicons+vue@2.2.0_vue@3.5.26_typescript@5.9.3_/node_modules/@heroicons/vue/24/outline/esm/ChevronDownIcon.js","../../../node_modules/.pnpm/@heroicons+vue@2.2.0_vue@3.5.26_typescript@5.9.3_/node_modules/@heroicons/vue/24/outline/esm/XMarkIcon.js","../src/SearchableSelectField.vue"],"sourcesContent":["import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nexport default function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"none\",\n viewBox: \"0 0 24 24\",\n \"stroke-width\": \"1.5\",\n stroke: \"currentColor\",\n \"aria-hidden\": \"true\",\n \"data-slot\": \"icon\"\n }, [\n _createElementVNode(\"path\", {\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\",\n d: \"m19.5 8.25-7.5 7.5-7.5-7.5\"\n })\n ]))\n}","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nexport default function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"none\",\n viewBox: \"0 0 24 24\",\n \"stroke-width\": \"1.5\",\n stroke: \"currentColor\",\n \"aria-hidden\": \"true\",\n \"data-slot\": \"icon\"\n }, [\n _createElementVNode(\"path\", {\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\",\n d: \"M6 18 18 6M6 6l12 12\"\n })\n ]))\n}","<script setup lang=\"ts\" generic=\"T, Value\">\nimport { ChevronDownIcon, XMarkIcon } from '@heroicons/vue/24/outline';\nimport { ActivityIndicator, Pulse } from '@vue-interface/activity-indicator';\nimport type { FormControlEvents, FormControlProps, FormControlSlots } from '@vue-interface/form-control';\nimport { useFormControl } from '@vue-interface/form-control';\nimport { InputField } from '@vue-interface/input-field';\nimport Fuse, { IFuseOptions } from 'fuse.js';\nimport { InputHTMLAttributes, computed, nextTick, ref, useTemplateRef, watch, watchEffect } from 'vue';\n\nconst props = withDefaults(defineProps<SearchableSelectFieldProps<T,Value>>(), {\n formControlClass: 'form-control',\n labelClass: 'form-label',\n size: 'form-control-md',\n clearable: true,\n options: () => []\n});\n\nconst model = defineModel<T>();\nconst isInteractive = computed(() => !props.disabled && !props.readonly);\n\ndefineSlots<FormControlSlots<SearchableSelectFieldSizePrefix,T> & {\n default(props: { option: T; display?: (option: T) => string }): any;\n}>();\n\nconst emit = defineEmits<FormControlEvents>();\n\nconst {\n controlAttributes,\n formGroupClasses,\n listeners\n} = useFormControl<InputHTMLAttributes, SearchableSelectFieldSizePrefix, T|undefined, T>({ model, props, emit });\n\nwatchEffect(() => {\n if(props.value !== undefined) {\n model.value = props.value;\n }\n});\n\nconst input = ref<string>();\nconst field = useTemplateRef<any>('field');\nconst showOptions = ref(false);\nconst active = ref<number>();\nconst buttons = useTemplateRef<HTMLButtonElement[]>('buttons');\nconst optionsEl = useTemplateRef<HTMLDivElement>('optionsEl');\n\nconst keys = computed(() => {\n return typeof props.options === 'object' && props.options?.[0]\n ? Object.keys(props.options?.[0])\n : ['$'];\n});\n\nlet fuse: Fuse<T> = createFuse();\n\nfunction createFuse() {\n return new Fuse(props.options ?? [], props.fuseOptions ?? {\n includeScore: true,\n threshold: .45,\n keys: keys.value\n });\n}\n\nconst filtered = computed<T[]>(() => {\n if(!input.value) {\n return props.options ?? [];\n }\n\n const matches = fuse.search(input.value).map(({ item }) => item);\n\n if(props.allowCustom && !matches.length) {\n return props.options;\n }\n\n return matches;\n});\n\nwatch(() => props.options, () => {\n fuse = createFuse();\n});\n\nfunction scrollIntoView(child?: HTMLElement) {\n const parent = optionsEl.value;\n\n if(!parent || !child) {\n return;\n }\n\n const parentRect = parent.getBoundingClientRect();\n const childRect = child.getBoundingClientRect();\n\n const childTop = childRect.top - parentRect.top + parent.scrollTop;\n\n parent.scrollTop = childTop;\n};\n\nwatch([input, active], ([input, active]) => {\n if(input) {\n buttons.value?.[0]?.scrollIntoView({\n block: 'nearest',\n inline: 'nearest'\n });\n }\n else if(active !== undefined) {\n buttons.value?.[active]?.scrollIntoView({\n block: 'nearest',\n inline: 'nearest'\n });\n }\n});\n\nwatch(optionsEl, (value) => {\n if(!value || active.value === undefined) {\n return;\n }\n\n nextTick(() => {\n scrollIntoView(buttons.value?.[active.value as number]);\n });\n});\n\nfunction select(option?: T) {\n model.value = option;\n active.value = option && props.options.includes(option)\n ? props.options.indexOf(option)\n : undefined;\n input.value = undefined;\n showOptions.value = false;\n}\n\nfunction onInput(e: Event) {\n if (!isInteractive.value) return;\n\n showOptions.value = true;\n active.value = undefined;\n input.value = (e.target as HTMLInputElement)?.value;\n\n if(!input.value && !props.allowCustom) {\n model.value = undefined;\n }\n\n if(props.allowCustom) {\n model.value = input.value as T;\n }\n}\n\nfunction onKeypressEnter() {\n if (!isInteractive.value) return;\n\n if(!showOptions.value) {\n showOptions.value = true;\n return;\n }\n\n if(props.allowCustom && active.value === undefined) {\n select(input.value as T ?? model.value);\n }\n else if(active.value === undefined) {\n select(filtered.value[0]);\n }\n else if(filtered.value[active.value]) {\n select(filtered.value[active.value]);\n }\n else {\n select(undefined);\n }\n}\n\nfunction onKeydownUp() {\n if (!isInteractive.value) return;\n \n showOptions.value = true;\n\n if(!active.value) {\n active.value = filtered.value.length - 1;\n }\n else {\n active.value--;\n }\n}\n\nfunction onKeydownDown() {\n if (!isInteractive.value) return;\n\n showOptions.value = true;\n\n if(active.value === undefined || active.value === filtered.value.length - 1) {\n active.value = 0;\n }\n else {\n active.value++;\n }\n}\n\nfunction onBlur() {\n showOptions.value = false;\n input.value = undefined;\n}\n\nfunction onClickOption(option: T) {\n if (!isInteractive.value) return;\n select(option);\n}\n\nfunction clear() {\n if (!isInteractive.value) return;\n input.value = undefined;\n model.value = undefined;\n}\n\nfunction toggle() {\n if (!isInteractive.value) return;\n \n if(showOptions.value ) {\n showOptions.value = false;\n }\n else {\n showOptions.value = true;\n field.value?.$el?.querySelector('input')?.focus();\n }\n}\n\nconst canClear = computed(() => {\n return props.clearable && (!!input.value || !!model.value) && isInteractive.value;\n});\n</script>\n<script lang=\"ts\">\nexport type SearchableSelectFieldSizePrefix = 'form-control';\n\nexport type SearchableSelectFieldProps<ModelValue, Value> = FormControlProps<\n InputHTMLAttributes, \n SearchableSelectFieldSizePrefix, \n ModelValue, \n Value\n> & {\n options?: ModelValue[];\n fuseOptions?: IFuseOptions<ModelValue>;\n display?: (option: ModelValue) => string;\n allowCustom?: boolean;\n clearable?: boolean;\n};\n</script>\n\n\n<template>\n <div class=\"relative [&_.form-control]:pr-8\">\n <InputField\n ref=\"field\"\n class=\"searchable-select-field-input\"\n :class=\"{ 'has-clear-button': canClear, formGroupClasses }\"\n :size=\"size\"\n v-bind=\"{ ...$attrs, controlAttributes, listeners }\"\n :name=\"name\"\n :label=\"label\"\n :model-value=\"input ?? (model && props?.display ? props?.display?.(model) : model)\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :color=\"color\"\n :error=\"error\"\n :errors=\"errors\"\n :feedback=\"feedback\"\n :valid=\"valid\"\n :invalid=\"invalid\"\n @click=\"isInteractive && (showOptions = true)\"\n @focus=\"isInteractive && (showOptions = true)\"\n @blur=\"onBlur\"\n @keypress.enter.prevent=\"onKeypressEnter\"\n @keydown.up.prevent=\"onKeydownUp\"\n @keydown.down.prevent=\"onKeydownDown\"\n @keyup.escape=\"showOptions = false\"\n @input=\"onInput\">\n <template #icon v-if=\"$slots.icon\">\n <slot name=\"icon\" />\n </template>\n <template #activity>\n <slot\n name=\"activity\"\n v-bind=\"{ disabled, options, invalid, valid }\">\n <ActivityIndicator\n v-if=\"!disabled && !options\"\n :type=\"Pulse\"\n size=\"activity-indicator-sm\" />\n <button\n v-else-if=\"canClear\"\n type=\"button\"\n class=\"searchable-select-field-clear-button\"\n @click.stop=\"clear\">\n <XMarkIcon class=\"size-[1.25em]\" />\n </button>\n <button\n v-else-if=\"!invalid && !valid\"\n type=\"button\"\n @click.stop=\"toggle\">\n <ChevronDownIcon class=\"size-[1em]\" />\n </button>\n </slot>\n </template>\n </InputField>\n <div\n v-if=\"showOptions && filtered.length\"\n ref=\"optionsEl\"\n tabindex=\"-1\"\n class=\"searchable-select-field-dropdown\"\n :class=\"size\"\n @mousedown.prevent.stop>\n <button\n v-for=\"(option, i) in filtered\"\n ref=\"buttons\"\n :key=\"i\"\n type=\"button\"\n tabindex=\"-1\"\n :title=\"display?.(option) ?? String(option)\"\n :class=\"{\n ['bg-neutral-200 dark:bg-neutral-700']: active === i\n }\"\n @mousedown.prevent\n @click=\"onClickOption(option)\">\n <slot v-bind=\"{ option, display }\">\n <div class=\"truncate\">\n {{ display?.(option) ?? option }}\n </div>\n </slot>\n </button>\n </div>\n </div>\n</template>"],"names":["render","_ctx","_cache","_openBlock","_createElementBlock","_createElementVNode","props","__props","model","_useModel","isInteractive","computed","emit","__emit","controlAttributes","formGroupClasses","listeners","useFormControl","watchEffect","input","ref","field","useTemplateRef","showOptions","active","buttons","optionsEl","keys","fuse","createFuse","Fuse","filtered","matches","item","watch","scrollIntoView","child","parent","parentRect","childTop","value","nextTick","select","option","onInput","e","onKeypressEnter","onKeydownUp","onKeydownDown","onBlur","onClickOption","clear","toggle","canClear","_hoisted_1","_createVNode","_unref","_mergeProps","$attrs","$event","_renderSlot","_normalizeProps","_guardReactiveProps","_createBlock","ActivityIndicator","Pulse","XMarkIcon","ChevronDownIcon","$slots","_normalizeClass","_Fragment","_renderList","i","_hoisted_3","_toDisplayString"],"mappings":"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","x_google_ignoreList":[0,1]}
1
+ {"version":3,"file":"searchable-select-field.umd.cjs","sources":["../../../node_modules/.pnpm/@heroicons+vue@2.2.0_vue@3.5.27_typescript@5.9.3_/node_modules/@heroicons/vue/24/outline/esm/ChevronDownIcon.js","../../../node_modules/.pnpm/@heroicons+vue@2.2.0_vue@3.5.27_typescript@5.9.3_/node_modules/@heroicons/vue/24/outline/esm/XMarkIcon.js","../src/SearchableSelectField.vue"],"sourcesContent":["import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nexport default function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"none\",\n viewBox: \"0 0 24 24\",\n \"stroke-width\": \"1.5\",\n stroke: \"currentColor\",\n \"aria-hidden\": \"true\",\n \"data-slot\": \"icon\"\n }, [\n _createElementVNode(\"path\", {\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\",\n d: \"m19.5 8.25-7.5 7.5-7.5-7.5\"\n })\n ]))\n}","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nexport default function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"none\",\n viewBox: \"0 0 24 24\",\n \"stroke-width\": \"1.5\",\n stroke: \"currentColor\",\n \"aria-hidden\": \"true\",\n \"data-slot\": \"icon\"\n }, [\n _createElementVNode(\"path\", {\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\",\n d: \"M6 18 18 6M6 6l12 12\"\n })\n ]))\n}","<script setup lang=\"ts\" generic=\"ModelValue, Value extends ModelValue\">\nimport { ChevronDownIcon, XMarkIcon } from '@heroicons/vue/24/outline';\nimport { ActivityIndicator, Pulse } from '@vue-interface/activity-indicator';\nimport type { FormControlEvents, FormControlProps, FormControlSlots } from '@vue-interface/form-control';\nimport { useFormControl } from '@vue-interface/form-control';\nimport { InputField } from '@vue-interface/input-field';\nimport Fuse, { IFuseOptions } from 'fuse.js';\nimport { type HTMLAttributes, computed, nextTick, ref, useTemplateRef, watch, watchEffect } from 'vue';\n\nconst props = withDefaults(defineProps<SearchableSelectFieldProps<ModelValue,Value>>(), {\n formControlClass: 'form-control',\n labelClass: 'form-label',\n size: 'form-control-md',\n clearable: true,\n options: () => []\n});\n\nconst model = defineModel<ModelValue>();\nconst isInteractive = computed(() => !props.disabled && !props.readonly);\n\ndefineSlots<FormControlSlots<SearchableSelectFieldSizePrefix,ModelValue> & {\n default(props: { option: ModelValue; display?: (option: ModelValue) => string }): any;\n}>();\n\nconst emit = defineEmits<FormControlEvents>();\n\nconst {\n controlAttributes,\n formGroupClasses,\n listeners\n} = useFormControl<HTMLAttributes, SearchableSelectFieldSizePrefix, ModelValue|undefined, ModelValue>({ model, props, emit });\n\nwatchEffect(() => {\n if(props.value !== undefined) {\n model.value = props.value;\n }\n});\n\nconst input = ref<string>();\nconst field = useTemplateRef<any>('field');\nconst showOptions = ref(false);\nconst active = ref<number>();\nconst buttons = useTemplateRef<HTMLButtonElement[]>('buttons');\nconst optionsEl = useTemplateRef<HTMLDivElement>('optionsEl');\n\nconst keys = computed(() => {\n return typeof props.options === 'object' && props.options?.[0]\n ? Object.keys(props.options?.[0])\n : ['$'];\n});\n\nlet fuse: Fuse<ModelValue> = createFuse();\n\nfunction createFuse() {\n return new Fuse(props.options ?? [], props.fuseOptions ?? {\n includeScore: true,\n threshold: .45,\n keys: keys.value\n });\n}\n\nconst filtered = computed<ModelValue[]>(() => {\n if(!input.value) {\n return props.options ?? [];\n }\n\n const matches = fuse.search(input.value).map(({ item }) => item);\n\n if(props.allowCustom && !matches.length) {\n return props.options;\n }\n\n return matches;\n});\n\nwatch(() => props.options, () => {\n fuse = createFuse();\n});\n\nfunction scrollIntoView(child?: HTMLElement) {\n const parent = optionsEl.value;\n\n if(!parent || !child) {\n return;\n }\n\n const parentRect = parent.getBoundingClientRect();\n const childRect = child.getBoundingClientRect();\n\n const childTop = childRect.top - parentRect.top + parent.scrollTop;\n\n parent.scrollTop = childTop;\n};\n\nwatch([input, active], ([input, active]) => {\n if(input) {\n buttons.value?.[0]?.scrollIntoView({\n block: 'nearest',\n inline: 'nearest'\n });\n }\n else if(active !== undefined) {\n buttons.value?.[active]?.scrollIntoView({\n block: 'nearest',\n inline: 'nearest'\n });\n }\n});\n\nwatch(optionsEl, (value) => {\n if(!value || active.value === undefined) {\n return;\n }\n\n nextTick(() => {\n scrollIntoView(buttons.value?.[active.value as number]);\n });\n});\n\nfunction select(option?: ModelValue) {\n model.value = option;\n active.value = option && props.options.includes(option)\n ? props.options.indexOf(option)\n : undefined;\n input.value = undefined;\n showOptions.value = false;\n}\n\nfunction onInput(e: Event) {\n if (!isInteractive.value) return;\n\n showOptions.value = true;\n active.value = undefined;\n input.value = (e.target as HTMLInputElement)?.value;\n\n if(!input.value && !props.allowCustom) {\n model.value = undefined;\n }\n\n if(props.allowCustom) {\n model.value = input.value as ModelValue;\n }\n}\n\nfunction onKeypressEnter() {\n if (!isInteractive.value) return;\n\n if(!showOptions.value) {\n showOptions.value = true;\n return;\n }\n\n if(props.allowCustom && active.value === undefined) {\n select(input.value as ModelValue ?? model.value);\n }\n else if(active.value === undefined) {\n select(filtered.value[0]);\n }\n else if(filtered.value[active.value]) {\n select(filtered.value[active.value]);\n }\n else {\n select(undefined);\n }\n}\n\nfunction onKeydownUp() {\n if (!isInteractive.value) return;\n \n showOptions.value = true;\n\n if(!active.value) {\n active.value = filtered.value.length - 1;\n }\n else {\n active.value--;\n }\n}\n\nfunction onKeydownDown() {\n if (!isInteractive.value) return;\n\n showOptions.value = true;\n\n if(active.value === undefined || active.value === filtered.value.length - 1) {\n active.value = 0;\n }\n else {\n active.value++;\n }\n}\n\nfunction onBlur() {\n showOptions.value = false;\n input.value = undefined;\n}\n\nfunction onClickOption(option: ModelValue) {\n if (!isInteractive.value) return;\n select(option);\n}\n\nfunction clear() {\n if (!isInteractive.value) return;\n input.value = undefined;\n model.value = undefined;\n}\n\nfunction toggle() {\n if (!isInteractive.value) return;\n \n if(showOptions.value ) {\n showOptions.value = false;\n }\n else {\n showOptions.value = true;\n field.value?.$el?.querySelector('input')?.focus();\n }\n}\n\nconst canClear = computed(() => {\n return props.clearable && (!!input.value || !!model.value) && isInteractive.value;\n});\n</script>\n<script lang=\"ts\">\nexport type SearchableSelectFieldSizePrefix = 'form-control';\n\nexport type SearchableSelectFieldProps<ModelValue, Value> = FormControlProps<\n HTMLAttributes, \n SearchableSelectFieldSizePrefix, \n ModelValue, \n Value\n> & {\n options?: ModelValue[];\n fuseOptions?: IFuseOptions<ModelValue>;\n display?: (option: ModelValue) => string;\n allowCustom?: boolean;\n clearable?: boolean;\n};\n</script>\n\n\n<template>\n <div class=\"relative [&_.form-control]:pr-8\">\n <InputField\n ref=\"field\"\n class=\"searchable-select-field-input\"\n :class=\"{ 'has-clear-button': canClear, formGroupClasses }\"\n :size=\"size\"\n v-bind=\"{ ...$attrs, controlAttributes, listeners }\"\n :name=\"name\"\n :label=\"label\"\n :model-value=\"input ?? (model && props?.display ? props?.display?.(model) : model)\"\n :disabled=\"disabled\"\n :readonly=\"readonly\"\n :color=\"color\"\n :error=\"error\"\n :errors=\"errors\"\n :feedback=\"feedback\"\n :valid=\"valid\"\n :invalid=\"invalid\"\n @click=\"isInteractive && (showOptions = true)\"\n @focus=\"isInteractive && (showOptions = true)\"\n @blur=\"onBlur\"\n @keypress.enter.prevent=\"onKeypressEnter\"\n @keydown.up.prevent=\"onKeydownUp\"\n @keydown.down.prevent=\"onKeydownDown\"\n @keyup.escape=\"showOptions = false\"\n @input=\"onInput\">\n <template #icon v-if=\"$slots.icon\">\n <slot name=\"icon\" />\n </template>\n <template #activity>\n <slot\n name=\"activity\"\n v-bind=\"{ disabled, options, invalid, valid }\">\n <ActivityIndicator\n v-if=\"!disabled && !options\"\n :type=\"Pulse\"\n size=\"activity-indicator-sm\" />\n <button\n v-else-if=\"canClear\"\n type=\"button\"\n class=\"searchable-select-field-clear-button\"\n @click.stop=\"clear\">\n <XMarkIcon class=\"size-[1.25em]\" />\n </button>\n <button\n v-else-if=\"!invalid && !valid\"\n type=\"button\"\n @click.stop=\"toggle\">\n <ChevronDownIcon class=\"size-[1em]\" />\n </button>\n </slot>\n </template>\n </InputField>\n <div\n v-if=\"showOptions && filtered.length\"\n ref=\"optionsEl\"\n tabindex=\"-1\"\n class=\"searchable-select-field-dropdown\"\n :class=\"size\"\n @mousedown.prevent.stop>\n <button\n v-for=\"(option, i) in filtered\"\n ref=\"buttons\"\n :key=\"i\"\n type=\"button\"\n tabindex=\"-1\"\n :title=\"display?.(option) ?? String(option)\"\n :class=\"{\n ['bg-neutral-200 dark:bg-neutral-700']: active === i\n }\"\n @mousedown.prevent\n @click=\"onClickOption(option)\">\n <slot v-bind=\"{ option, display }\">\n <div class=\"truncate\">\n {{ display?.(option) ?? option }}\n </div>\n </slot>\n </button>\n </div>\n </div>\n</template>"],"names":["render","_ctx","_cache","_openBlock","_createElementBlock","_createElementVNode","props","__props","model","_useModel","isInteractive","computed","emit","__emit","controlAttributes","formGroupClasses","listeners","useFormControl","watchEffect","input","ref","field","useTemplateRef","showOptions","active","buttons","optionsEl","keys","fuse","createFuse","Fuse","filtered","matches","item","watch","scrollIntoView","child","parent","parentRect","childTop","value","nextTick","select","option","onInput","e","onKeypressEnter","onKeydownUp","onKeydownDown","onBlur","onClickOption","clear","toggle","canClear","_hoisted_1","_createVNode","_unref","_mergeProps","$attrs","$event","_renderSlot","_normalizeProps","_guardReactiveProps","_createBlock","ActivityIndicator","Pulse","XMarkIcon","ChevronDownIcon","$slots","_normalizeClass","_Fragment","_renderList","i","_hoisted_3","_toDisplayString"],"mappings":"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","x_google_ignoreList":[0,1]}
@@ -1,15 +1,15 @@
1
1
  import { FormControlProps, FormControlSlots } from '@vue-interface/form-control';
2
2
  import { IFuseOptions } from 'fuse.js';
3
- import { InputHTMLAttributes } from 'vue';
3
+ import { HTMLAttributes } from 'vue';
4
4
  export type SearchableSelectFieldSizePrefix = 'form-control';
5
- export type SearchableSelectFieldProps<ModelValue, Value> = FormControlProps<InputHTMLAttributes, SearchableSelectFieldSizePrefix, ModelValue, Value> & {
5
+ export type SearchableSelectFieldProps<ModelValue, Value> = FormControlProps<HTMLAttributes, SearchableSelectFieldSizePrefix, ModelValue, Value> & {
6
6
  options?: ModelValue[];
7
7
  fuseOptions?: IFuseOptions<ModelValue>;
8
8
  display?: (option: ModelValue) => string;
9
9
  allowCustom?: boolean;
10
10
  clearable?: boolean;
11
11
  };
12
- declare const _default: <T, Value>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
12
+ declare const _default: <ModelValue, Value extends ModelValue>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
13
13
  props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{
14
14
  readonly onInput?: ((event: Event) => any) | undefined;
15
15
  readonly onSelect?: ((event: Event) => any) | undefined;
@@ -45,9 +45,9 @@ declare const _default: <T, Value>(__VLS_props: NonNullable<Awaited<typeof __VLS
45
45
  readonly onTouchcancel?: ((event: TouchEvent) => any) | undefined;
46
46
  readonly onChange?: ((event: Event) => any) | undefined;
47
47
  readonly onBeforeinput?: ((event: Event) => any) | undefined;
48
- readonly "onUpdate:modelValue"?: ((value: T) => any) | undefined;
48
+ readonly "onUpdate:modelValue"?: ((value: ModelValue) => any) | undefined;
49
49
  } & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps, never>, "onCopy" | "onCut" | "onPaste" | "onFocus" | "onFocusin" | "onFocusout" | "onBlur" | "onChange" | "onBeforeinput" | "onInput" | "onReset" | "onSubmit" | "onInvalid" | "onKeydown" | "onKeypress" | "onKeyup" | "onMousedown" | "onMouseenter" | "onMouseleave" | "onMousemove" | "onMouseout" | "onMouseover" | "onMouseup" | "onSelect" | "onScroll" | "onTouchcancel" | "onTouchend" | "onTouchmove" | "onTouchstart" | "onClick" | "onContextmenu" | "onWheel" | "onDoubleclick" | "onSelectionchange" | "onUpdate:modelValue"> & ({
50
- modelValue?: T;
50
+ modelValue?: ModelValue;
51
51
  } & {
52
52
  activity?: boolean;
53
53
  disabled?: boolean;
@@ -62,7 +62,7 @@ declare const _default: <T, Value>(__VLS_props: NonNullable<Awaited<typeof __VLS
62
62
  invalid?: boolean;
63
63
  label?: string;
64
64
  labelClass?: string;
65
- modelValue?: T | undefined;
65
+ modelValue?: ModelValue | undefined;
66
66
  name?: string;
67
67
  plaintext?: boolean;
68
68
  size?: import('@vue-interface/form-control').FormControlSize<"form-control"> | undefined;
@@ -70,27 +70,27 @@ declare const _default: <T, Value>(__VLS_props: NonNullable<Awaited<typeof __VLS
70
70
  readonly?: boolean;
71
71
  valid?: boolean;
72
72
  value?: Value | undefined;
73
- } & Omit<InputHTMLAttributes, "size"> & {
74
- options?: T[] | undefined;
75
- fuseOptions?: IFuseOptions<T> | undefined;
76
- display?: ((option: T) => string) | undefined;
73
+ } & Omit<HTMLAttributes, "size"> & {
74
+ options?: ModelValue[] | undefined;
75
+ fuseOptions?: IFuseOptions<ModelValue> | undefined;
76
+ display?: ((option: ModelValue) => string) | undefined;
77
77
  allowCustom?: boolean;
78
78
  clearable?: boolean;
79
79
  }) & Partial<{}>> & import('vue').PublicProps;
80
80
  expose(exposed: import('vue').ShallowUnwrapRef<{}>): void;
81
81
  attrs: any;
82
- slots: Readonly<FormControlSlots<"form-control", T> & {
82
+ slots: Readonly<FormControlSlots<"form-control", ModelValue> & {
83
83
  default(props: {
84
- option: T;
85
- display?: (option: T) => string;
84
+ option: ModelValue;
85
+ display?: (option: ModelValue) => string;
86
86
  }): any;
87
- }> & FormControlSlots<"form-control", T> & {
87
+ }> & FormControlSlots<"form-control", ModelValue> & {
88
88
  default(props: {
89
- option: T;
90
- display?: (option: T) => string;
89
+ option: ModelValue;
90
+ display?: (option: ModelValue) => string;
91
91
  }): any;
92
92
  };
93
- emit: (((evt: "input", event: Event) => void) & ((evt: "select", event: Event) => void) & ((evt: "blur", event: FocusEvent) => void) & ((evt: "focus", event: FocusEvent) => void) & ((evt: "focusin", event: FocusEvent) => void) & ((evt: "focusout", event: FocusEvent) => void) & ((evt: "click", event: MouseEvent) => void) & ((evt: "doubleclick", event: MouseEvent) => void) & ((evt: "contextmenu", event: MouseEvent) => void) & ((evt: "mousedown", event: MouseEvent) => void) & ((evt: "mouseup", event: MouseEvent) => void) & ((evt: "mouseover", event: MouseEvent) => void) & ((evt: "mouseout", event: MouseEvent) => void) & ((evt: "mouseenter", event: MouseEvent) => void) & ((evt: "mouseleave", event: MouseEvent) => void) & ((evt: "mousemove", event: MouseEvent) => void) & ((evt: "keydown", event: KeyboardEvent) => void) & ((evt: "keyup", event: KeyboardEvent) => void) & ((evt: "keypress", event: KeyboardEvent) => void) & ((evt: "selectionchange", event: Event) => void) & ((evt: "invalid", event: Event) => void) & ((evt: "submit", event: Event) => void) & ((evt: "reset", event: Event) => void) & ((evt: "scroll", event: Event) => void) & ((evt: "wheel", event: WheelEvent) => void) & ((evt: "copy", event: ClipboardEvent) => void) & ((evt: "cut", event: ClipboardEvent) => void) & ((evt: "paste", event: ClipboardEvent) => void) & ((evt: "touchstart", event: TouchEvent) => void) & ((evt: "touchend", event: TouchEvent) => void) & ((evt: "touchmove", event: TouchEvent) => void) & ((evt: "touchcancel", event: TouchEvent) => void) & ((evt: "change", event: Event) => void) & ((evt: "beforeinput", event: Event) => void)) & ((evt: "update:modelValue", value: T) => void);
93
+ emit: (((evt: "input", event: Event) => void) & ((evt: "select", event: Event) => void) & ((evt: "blur", event: FocusEvent) => void) & ((evt: "focus", event: FocusEvent) => void) & ((evt: "focusin", event: FocusEvent) => void) & ((evt: "focusout", event: FocusEvent) => void) & ((evt: "click", event: MouseEvent) => void) & ((evt: "doubleclick", event: MouseEvent) => void) & ((evt: "contextmenu", event: MouseEvent) => void) & ((evt: "mousedown", event: MouseEvent) => void) & ((evt: "mouseup", event: MouseEvent) => void) & ((evt: "mouseover", event: MouseEvent) => void) & ((evt: "mouseout", event: MouseEvent) => void) & ((evt: "mouseenter", event: MouseEvent) => void) & ((evt: "mouseleave", event: MouseEvent) => void) & ((evt: "mousemove", event: MouseEvent) => void) & ((evt: "keydown", event: KeyboardEvent) => void) & ((evt: "keyup", event: KeyboardEvent) => void) & ((evt: "keypress", event: KeyboardEvent) => void) & ((evt: "selectionchange", event: Event) => void) & ((evt: "invalid", event: Event) => void) & ((evt: "submit", event: Event) => void) & ((evt: "reset", event: Event) => void) & ((evt: "scroll", event: Event) => void) & ((evt: "wheel", event: WheelEvent) => void) & ((evt: "copy", event: ClipboardEvent) => void) & ((evt: "cut", event: ClipboardEvent) => void) & ((evt: "paste", event: ClipboardEvent) => void) & ((evt: "touchstart", event: TouchEvent) => void) & ((evt: "touchend", event: TouchEvent) => void) & ((evt: "touchmove", event: TouchEvent) => void) & ((evt: "touchcancel", event: TouchEvent) => void) & ((evt: "change", event: Event) => void) & ((evt: "beforeinput", event: Event) => void)) & ((evt: "update:modelValue", value: ModelValue) => void);
94
94
  }>) => import('vue').VNode & {
95
95
  __ctx?: Awaited<typeof __VLS_setup>;
96
96
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vue-interface/searchable-select-field",
3
- "version": "2.0.3",
3
+ "version": "2.0.4",
4
4
  "description": "A Vue searchable select field component.",
5
5
  "type": "module",
6
6
  "main": "./dist/searchable-select-field.umd.cjs",
@@ -48,8 +48,8 @@
48
48
  "fuse.js": "^7.1.0",
49
49
  "vue": "^3.3.4",
50
50
  "@vue-interface/activity-indicator": "3.0.8",
51
- "@vue-interface/form-control": "2.0.22",
52
- "@vue-interface/input-field": "3.0.3",
51
+ "@vue-interface/form-control": "2.0.23",
52
+ "@vue-interface/input-field": "3.0.4",
53
53
  "@vue-interface/sizeable": "2.0.0"
54
54
  },
55
55
  "scripts": {
@@ -1,13 +1,13 @@
1
- <script setup lang="ts" generic="T, Value">
1
+ <script setup lang="ts" generic="ModelValue, Value extends ModelValue">
2
2
  import { ChevronDownIcon, XMarkIcon } from '@heroicons/vue/24/outline';
3
3
  import { ActivityIndicator, Pulse } from '@vue-interface/activity-indicator';
4
4
  import type { FormControlEvents, FormControlProps, FormControlSlots } from '@vue-interface/form-control';
5
5
  import { useFormControl } from '@vue-interface/form-control';
6
6
  import { InputField } from '@vue-interface/input-field';
7
7
  import Fuse, { IFuseOptions } from 'fuse.js';
8
- import { InputHTMLAttributes, computed, nextTick, ref, useTemplateRef, watch, watchEffect } from 'vue';
8
+ import { type HTMLAttributes, computed, nextTick, ref, useTemplateRef, watch, watchEffect } from 'vue';
9
9
 
10
- const props = withDefaults(defineProps<SearchableSelectFieldProps<T,Value>>(), {
10
+ const props = withDefaults(defineProps<SearchableSelectFieldProps<ModelValue,Value>>(), {
11
11
  formControlClass: 'form-control',
12
12
  labelClass: 'form-label',
13
13
  size: 'form-control-md',
@@ -15,11 +15,11 @@ const props = withDefaults(defineProps<SearchableSelectFieldProps<T,Value>>(), {
15
15
  options: () => []
16
16
  });
17
17
 
18
- const model = defineModel<T>();
18
+ const model = defineModel<ModelValue>();
19
19
  const isInteractive = computed(() => !props.disabled && !props.readonly);
20
20
 
21
- defineSlots<FormControlSlots<SearchableSelectFieldSizePrefix,T> & {
22
- default(props: { option: T; display?: (option: T) => string }): any;
21
+ defineSlots<FormControlSlots<SearchableSelectFieldSizePrefix,ModelValue> & {
22
+ default(props: { option: ModelValue; display?: (option: ModelValue) => string }): any;
23
23
  }>();
24
24
 
25
25
  const emit = defineEmits<FormControlEvents>();
@@ -28,7 +28,7 @@ const {
28
28
  controlAttributes,
29
29
  formGroupClasses,
30
30
  listeners
31
- } = useFormControl<InputHTMLAttributes, SearchableSelectFieldSizePrefix, T|undefined, T>({ model, props, emit });
31
+ } = useFormControl<HTMLAttributes, SearchableSelectFieldSizePrefix, ModelValue|undefined, ModelValue>({ model, props, emit });
32
32
 
33
33
  watchEffect(() => {
34
34
  if(props.value !== undefined) {
@@ -49,7 +49,7 @@ const keys = computed(() => {
49
49
  : ['$'];
50
50
  });
51
51
 
52
- let fuse: Fuse<T> = createFuse();
52
+ let fuse: Fuse<ModelValue> = createFuse();
53
53
 
54
54
  function createFuse() {
55
55
  return new Fuse(props.options ?? [], props.fuseOptions ?? {
@@ -59,7 +59,7 @@ function createFuse() {
59
59
  });
60
60
  }
61
61
 
62
- const filtered = computed<T[]>(() => {
62
+ const filtered = computed<ModelValue[]>(() => {
63
63
  if(!input.value) {
64
64
  return props.options ?? [];
65
65
  }
@@ -117,7 +117,7 @@ watch(optionsEl, (value) => {
117
117
  });
118
118
  });
119
119
 
120
- function select(option?: T) {
120
+ function select(option?: ModelValue) {
121
121
  model.value = option;
122
122
  active.value = option && props.options.includes(option)
123
123
  ? props.options.indexOf(option)
@@ -138,7 +138,7 @@ function onInput(e: Event) {
138
138
  }
139
139
 
140
140
  if(props.allowCustom) {
141
- model.value = input.value as T;
141
+ model.value = input.value as ModelValue;
142
142
  }
143
143
  }
144
144
 
@@ -151,7 +151,7 @@ function onKeypressEnter() {
151
151
  }
152
152
 
153
153
  if(props.allowCustom && active.value === undefined) {
154
- select(input.value as T ?? model.value);
154
+ select(input.value as ModelValue ?? model.value);
155
155
  }
156
156
  else if(active.value === undefined) {
157
157
  select(filtered.value[0]);
@@ -195,7 +195,7 @@ function onBlur() {
195
195
  input.value = undefined;
196
196
  }
197
197
 
198
- function onClickOption(option: T) {
198
+ function onClickOption(option: ModelValue) {
199
199
  if (!isInteractive.value) return;
200
200
  select(option);
201
201
  }
@@ -226,7 +226,7 @@ const canClear = computed(() => {
226
226
  export type SearchableSelectFieldSizePrefix = 'form-control';
227
227
 
228
228
  export type SearchableSelectFieldProps<ModelValue, Value> = FormControlProps<
229
- InputHTMLAttributes,
229
+ HTMLAttributes,
230
230
  SearchableSelectFieldSizePrefix,
231
231
  ModelValue,
232
232
  Value