diff --git a/docs/components/content/examples/SelectMenuExampleSearchQuery.vue b/docs/components/content/examples/SelectMenuExampleSearchQuery.vue new file mode 100644 index 0000000000..db05eafb21 --- /dev/null +++ b/docs/components/content/examples/SelectMenuExampleSearchQuery.vue @@ -0,0 +1,16 @@ + + + diff --git a/docs/content/3.forms/4.select-menu.md b/docs/content/3.forms/4.select-menu.md index c31451b918..62029af8fe 100644 --- a/docs/content/3.forms/4.select-menu.md +++ b/docs/content/3.forms/4.select-menu.md @@ -146,6 +146,18 @@ componentProps: --- :: +### Control the query :u-badge{label="New" class="align-middle ml-2 !rounded-full" variant="subtle"} + +Use a `v-model:query` to control the search query. + +::component-example +--- +component: 'select-menu-example-search-query' +componentProps: + class: 'w-full lg:w-48' +--- +:: + ## Creatable Use the `creatable` prop to enable the creation of new options when the search doesn't return any results (only works with `searchable`). diff --git a/src/runtime/components/forms/SelectMenu.vue b/src/runtime/components/forms/SelectMenu.vue index e87abcab96..f68a0465c5 100644 --- a/src/runtime/components/forms/SelectMenu.vue +++ b/src/runtime/components/forms/SelectMenu.vue @@ -58,14 +58,13 @@ import { ref, computed, toRef, watch, defineComponent } from 'vue' -import type { PropType, ComponentPublicInstance } from 'vue' +import type { PropType } from 'vue' import { Combobox as HCombobox, ComboboxButton as HComboboxButton, @@ -177,6 +176,10 @@ export default defineComponent({ type: [String, Number, Object, Array], default: '' }, + query: { + type: String, + default: null + }, by: { type: String, default: undefined @@ -326,7 +329,7 @@ export default defineComponent({ default: () => ({}) } }, - emits: ['update:modelValue', 'open', 'close', 'change'], + emits: ['update:modelValue', 'update:query', 'open', 'close', 'change'], setup (props, { emit, slots }) { const { ui, attrs } = useUI('select', toRef(props, 'ui'), config, toRef(props, 'class')) @@ -341,8 +344,16 @@ export default defineComponent({ const size = computed(() => sizeButtonGroup.value || sizeFormGroup.value) - const query = ref('') - const searchInput = ref>() + const internalQuery = ref('') + const query = computed({ + get () { + return props.query ?? internalQuery.value + }, + set (value) { + internalQuery.value = value + emit('update:query', value) + } + }) const selectClass = computed(() => { const variant = ui.value.color?.[color.value as string]?.[props.variant as string] || ui.value.variant[props.variant] @@ -476,17 +487,15 @@ export default defineComponent({ }) function onUpdate (event: any) { - if (query.value && searchInput.value?.$el) { - query.value = '' - // explicitly set input text because `ComboboxInput` `displayValue` is not reactive - searchInput.value.$el.value = '' - } - emit('update:modelValue', event) emit('change', event) emitFormChange() } + function onChange (event: any) { + query.value = event.target.value + } + return { // eslint-disable-next-line vue/no-dupe-keys ui, @@ -512,8 +521,10 @@ export default defineComponent({ trailingWrapperIconClass, filteredOptions, createOption, + // eslint-disable-next-line vue/no-dupe-keys query, - onUpdate + onUpdate, + onChange } } })