Searchable Select
Searchable Select allows users to select one or multiple items from a list of options. It provides a search input field to filter the options.
<script setup lang="ts">import { VSearchableSelect, VOption, VIcon, VFlag } from '@vonage/vivid-api-vue';</script>
<template> <VSearchableSelect label="Countries" clearable multiple style="width: 400px"> <template #icon> <VIcon name="globe-line" /> </template> <VOption value="afghanistan" text="Afghanistan"> <template #icon><VFlag code="AF" /></template> </VOption> <VOption value="albania" text="Albania"> <template #icon><VFlag code="AL" /></template> </VOption> <VOption value="algeria" text="Algeria"> <template #icon><VFlag code="DZ" /></template> </VOption> <VOption value="argentina" text="Argentina"> <template #icon><VFlag code="AR" /></template> </VOption> <VOption value="australia" text="Australia"> <template #icon><VFlag code="AU" /></template> </VOption> <VOption value="brazil" text="Brazil"> <template #icon><VFlag code="BR" /></template> </VOption> <VOption value="canada" text="Canada"> <template #icon><VFlag code="CA" /></template> </VOption> <VOption value="france" text="France"> <template #icon><VFlag code="FR" /></template> </VOption> <VOption value="germany" text="Germany"> <template #icon><VFlag code="DE" /></template> </VOption> <VOption value="japan" text="Japan"> <template #icon><VFlag code="JP" /></template> </VOption> <VOption value="united-kingdom" text="United Kingdom"> <template #icon><VFlag code="UK" /></template> </VOption> <VOption value="united-states" text="United States"> <template #icon><VFlag code="US" /></template> </VOption> </VSearchableSelect></template><vwc-searchable-select label="Countries" clearable multiple> <vwc-icon slot="icon" name="globe-line"></vwc-icon> <vwc-option value="afghanistan" text="Afghanistan"> <vwc-flag code="AF" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="albania" text="Albania"> <vwc-flag code="AL" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="algeria" text="Algeria"> <vwc-flag code="DZ" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="argentina" text="Argentina"> <vwc-flag code="AR" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="armenia" text="Armenia"> <vwc-flag code="AM" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="aruba" text="Aruba"> <vwc-flag code="AW" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="australia" text="Australia"> <vwc-flag code="AU" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="austria" text="Austria"> <vwc-flag code="AT" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="bahamas" text="Bahamas"> <vwc-flag code="BS" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="bahrain" text="Bahrain"> <vwc-flag code="BH" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="belarus" text="Belarus"> <vwc-flag code="BY" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="belgium" text="Belgium"> <vwc-flag code="BE" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="benin" text="Benin"> <vwc-flag code="BJ" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="bolivia" text="Bolivia"> <vwc-flag code="BO" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="botswana" text="Botswana"> <vwc-flag code="BW" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="brazil" text="Brazil"> <vwc-flag code="BR" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="bulgaria" text="Bulgaria"> <vwc-flag code="BG" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="burkina-faso" text="Burkina Faso"> <vwc-flag code="BF" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="cambodia" text="Cambodia"> <vwc-flag code="KH" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="canada" text="Canada"> <vwc-flag code="CA" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="cayman-islands" text="Cayman Islands"> <vwc-flag code="KY" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="central-african-republic" text="Central African Republic"> <vwc-flag code="CF" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="chad" text="Chad"> <vwc-flag code="TD" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="chile" text="Chile"> <vwc-flag code="CL" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="china" text="China"> <vwc-flag code="CN" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="colombia" text="Colombia"> <vwc-flag code="CO" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="costa-rica" text="Costa Rica"> <vwc-flag code="CR" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="croatia" text="Croatia"> <vwc-flag code="HR" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="cyprus" text="Cyprus"> <vwc-flag code="CY" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="denmark" text="Denmark"> <vwc-flag code="DK" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="dominican-republic" text="Dominican Republic"> <vwc-flag code="DO" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="egypt" text="Egypt"> <vwc-flag code="EG" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="el-salvador" text="El Salvador"> <vwc-flag code="SV" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="estonia" text="Estonia"> <vwc-flag code="EE" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="european-union" text="European Union"> <vwc-flag code="EU" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="finland" text="Finland"> <vwc-flag code="FI" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="france" text="France"> <vwc-flag code="FR" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="georgia" text="Georgia"> <vwc-flag code="GE" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="germany" text="Germany"> <vwc-flag code="DE" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="ghana" text="Ghana"> <vwc-flag code="GH" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="grenada" text="Grenada"> <vwc-flag code="GD" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="guatemala" text="Guatemala"> <vwc-flag code="GT" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="honduras" text="Honduras"> <vwc-flag code="HN" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="hong-kong" text="Hong Kong"> <vwc-flag code="HK" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="hungary" text="Hungary"> <vwc-flag code="HU" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="iceland" text="Iceland"> <vwc-flag code="IS" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="india" text="India"> <vwc-flag code="IN" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="indonesia" text="Indonesia"> <vwc-flag code="ID" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="ireland" text="Ireland"> <vwc-flag code="IE" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="israel" text="Israel"> <vwc-flag code="IL" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="italy" text="Italy"> <vwc-flag code="IT" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="jamaica" text="Jamaica"> <vwc-flag code="JM" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="japan" text="Japan"> <vwc-flag code="JP" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="latvia" text="Latvia"> <vwc-flag code="LV" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="liechtenstein" text="Liechtenstein"> <vwc-flag code="LI" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="lithuania" text="Lithuania"> <vwc-flag code="LT" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="luxembourg" text="Luxembourg"> <vwc-flag code="LU" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="macao" text="Macao"> <vwc-flag code="MO" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="malaysia" text="Malaysia"> <vwc-flag code="MY" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="malta" text="Malta"> <vwc-flag code="MT" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="mauritius" text="Mauritius"> <vwc-flag code="MU" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="mexico" text="Mexico"> <vwc-flag code="MX" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="moldova" text="Moldova"> <vwc-flag code="MD" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="netherlands" text="Netherlands"> <vwc-flag code="NL" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="new-zealand" text="New Zealand"> <vwc-flag code="NZ" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="nigeria" text="Nigeria"> <vwc-flag code="NG" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="norway" text="Norway"> <vwc-flag code="NO" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="pakistan" text="Pakistan"> <vwc-flag code="PK" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="panama" text="Panama"> <vwc-flag code="PA" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="peru" text="Peru"> <vwc-flag code="PE" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="philippines" text="Philippines"> <vwc-flag code="PH" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="poland" text="Poland"> <vwc-flag code="PL" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="portugal" text="Portugal"> <vwc-flag code="PT" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="puertorico" text="Puerto Rico"> <vwc-flag code="PR" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="romania" text="Romania"> <vwc-flag code="RO" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="russia" text="Russia"> <vwc-flag code="RU" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="rwanda" text="Rwanda"> <vwc-flag code="RW" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="saudi-arabia" text="Saudi Arabia"> <vwc-flag code="SA" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="serbia" text="Serbia"> <vwc-flag code="RS" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="singapore" text="Singapore"> <vwc-flag code="SG" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="slovakia" text="Slovakia"> <vwc-flag code="SK" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="slovenia" text="Slovenia"> <vwc-flag code="SI" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="south-africa" text="South Africa"> <vwc-flag code="ZA" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="south-korea" text="South Korea"> <vwc-flag code="KR" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="spain" text="Spain"> <vwc-flag code="ES" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="sri-lanka" text="Sri Lanka"> <vwc-flag code="LK" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="sweden" text="Sweden"> <vwc-flag code="SE" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="switzerland" text="Switzerland"> <vwc-flag code="CH" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="taiwan" text="Taiwan"> <vwc-flag code="TW" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="tajikistan" text="Tajikistan"> <vwc-flag code="TJ" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="tanzania" text="Tanzania"> <vwc-flag code="TZ" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="thailand" text="Thailand"> <vwc-flag code="TH" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="trinidad-and-tobago" text="Trinidad and Tobago"> <vwc-flag code="TT" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="turkey" text="Turkey"> <vwc-flag code="TR" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="ukraine" text="Ukraine"> <vwc-flag code="UA" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="united-arab-emirates" text="United Arab Emirates"> <vwc-flag code="AE" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="united-kingdom" text="United Kingdom"> <vwc-flag code="UK" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="united-states" text="United States"> <vwc-flag code="US" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="uruguay" text="Uruguay"> <vwc-flag code="UY" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="uzbekistan" text="Uzbekistan"> <vwc-flag code="UZ" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="venezuela" text="Venezuela"> <vwc-flag code="VE" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="vietnam" text="Vietnam"> <vwc-flag code="VN" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="zambia" text="Zambia"> <vwc-flag code="ZM" slot="icon"></vwc-flag> </vwc-option></vwc-searchable-select>
<style> vwc-searchable-select { width: 400px; }</style><script setup lang="ts">import { VSearchableSelect, VOption, VLayout, VButton, VFlag } from '@vonage/vivid-api-vue';</script>
<template> <form> <VLayout column-spacing="small" column-basis="block"> <div> <VSearchableSelect name="country" multiple required> <VOption value="AF" text="Afghanistan"> <template #icon><VFlag code="AF" /></template> </VOption> <VOption value="AL" text="Albania"> <template #icon><VFlag code="AL" /></template> </VOption> <VOption value="DZ" text="Algeria"> <template #icon><VFlag code="DZ" /></template> </VOption> </VSearchableSelect> </div> <div class="buttons"> <VButton label="Reset" type="reset" /> <VButton label="Submit" appearance="filled" type="submit" /> </div> </VLayout> </form></template>
<style scoped>.buttons { display: flex; gap: 12px;}</style><style> .buttons { display: flex; gap: 12px; }</style><form> <vwc-layout column-spacing="small" column-basis="block"> <div> <vwc-searchable-select name="country" multiple required> <vwc-option value="AF" text="Afghanistan"> <vwc-flag code="AF" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="AL" text="Albania"> <vwc-flag code="AL" slot="icon"></vwc-flag> </vwc-option> <vwc-option value="DZ" text="Algeria"> <vwc-flag code="DZ" slot="icon"></vwc-flag> </vwc-option> </vwc-searchable-select> </div> <div class="buttons"> <vwc-button label="Reset" type="reset"></vwc-button> <vwc-button label="Submit" appearance="filled" type="submit"></vwc-button> </div> </vwc-layout></form>To fetch options for the search text asynchronously you will need combine several features:
Use searchText and the search-text-change event to fetch options for the search text. Set loading while options are being fetched.
Use the no-options slot to indicate that options will become available when the user starts typing.
As you are now handling the filtering of options yourself, you need to disable the built-in filtering logic:
- Set
optionFilterto always returntrueto show all options. - Set
matchedTexton the options to the search text they were fetched with.
Already selected options need to stay present, even if they are no longer in the current result set. Use hidden to hide them in the dropdown.
<template> <div class="container"> <VSearchableSelect v-model="value" label="Favorite fruit" :option-filter="() => true" :loading="isLoading" @input="onInput($event.target?.values ?? [])" @search-text-change="onSearchTextChanged($event.target?.searchText ?? '')"> <VOption v-for="option in retainedOptions" :key="option.value" :value="option.value" :text="option.text" hidden /> <VOption v-for="option in currentSearchResults" :key="option.value" :value="option.value" :text="option.text" :matched-text="currentSearchText" /> <template #no-options><span>Start typing to search...</span></template> <template #loading-options><span>Loading results...</span></template> </VSearchableSelect>
<VSearchableSelect v-model:values="values" label="Favorite fruits" multiple :option-filter="() => true" :loading="isLoading2" @input="onInput2($event.target?.values ?? [])" @search-text-change="onSearchTextChanged2($event.target?.searchText ?? '')"> <VOption v-for="option in retainedOptions2" :key="option.value" :value="option.value" :text="option.text" hidden /> <VOption v-for="option in currentSearchResults2" :key="option.value" :value="option.value" :text="option.text" :matched-text="currentSearchText2" /> <template #no-options><span>Start typing to search...</span></template> <template #loading-options><span>Loading results...</span></template> </VSearchableSelect> </div>
</template>
<script setup lang="ts">import { VSearchableSelect, VOption } from '@vonage/vivid-api-vue';import { computed, ref } from 'vue';
const value = ref('');const values = ref<string[]>([]);
const isLoading = ref(false);const selectedOptions = ref<{ value: string; text: string }[]>([]);const currentSearchResults = ref<{ value: string; text: string }[]>([]);const currentSearchText = ref('');
const isLoading2 = ref(false);const selectedOptions2 = ref<{ value: string; text: string }[]>([]);const currentSearchResults2 = ref<{ value: string; text: string }[]>([]);const currentSearchText2 = ref('');
const retainedOptions = computed(() => selectedOptions.value.filter((s) => !currentSearchResults.value.some((o) => o.value === s.value)));const retainedOptions2 = computed(() => selectedOptions2.value.filter((s) => !currentSearchResults2.value.some((o) => o.value === s.value)));
const fruitsDatabase = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape', 'Jackfruit', 'Kiwi', 'Lemon', 'Mango', 'Nectarine', 'Orange', 'Passion fruit', 'Quince', 'Raspberry', 'Strawberry', 'Watermelon'];
async function fetchOptions(searchText: string) { return new Promise<{ value: string; text: string }[]>((resolve) => { setTimeout( () => resolve( fruitsDatabase .filter((fruit) => fruit.toLowerCase().includes(searchText.toLowerCase())) .map((fruit) => ({ value: fruit, text: fruit, })) ), 1000 ); });}
function debounce<T extends (...args: unknown[]) => void>(func: T, timeout: number) { let timer: ReturnType<typeof setTimeout>; return (...args: Parameters<T>) => { clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args); }, timeout); };}
let latestSearchText = '';const debouncedSearch = debounce(async (searchText: string) => { const newResults = await fetchOptions(searchText); if (latestSearchText !== searchText) return; isLoading.value = false; currentSearchResults.value = newResults; currentSearchText.value = searchText;}, 1000);
const onSearchTextChanged = (newSearchText: string) => { if (newSearchText === '') { latestSearchText = ''; isLoading.value = false; currentSearchResults.value = []; currentSearchText.value = ''; } else { latestSearchText = newSearchText; isLoading.value = true; debouncedSearch(newSearchText); }};
function onInput(valuesArr: string[]) { selectedOptions.value = valuesArr.map((v) => { const fromResults = currentSearchResults.value.find((o) => o.value === v); const fromSelected = selectedOptions.value.find((o) => o.value === v); return fromResults ?? fromSelected ?? { value: v, text: v }; });}
let latestSearchText2 = '';const debouncedSearch2 = debounce(async (searchText: string) => { const newResults = await fetchOptions(searchText); if (latestSearchText2 !== searchText) return; isLoading2.value = false; currentSearchResults2.value = newResults; currentSearchText2.value = searchText;}, 1000);
const onSearchTextChanged2 = (newSearchText: string) => { if (newSearchText === '') { latestSearchText2 = ''; isLoading2.value = false; currentSearchResults2.value = []; currentSearchText2.value = ''; } else { latestSearchText2 = newSearchText; isLoading2.value = true; debouncedSearch2(newSearchText); }};
function onInput2(valuesArr: string[]) { selectedOptions2.value = valuesArr.map((v) => { const fromResults = currentSearchResults2.value.find((o) => o.value === v); const fromSelected = selectedOptions2.value.find((o) => o.value === v); return fromResults ?? fromSelected ?? { value: v, text: v }; });}</script>
<style scoped>.container { block-size: 460px;}</style><vwc-searchable-select label="Favorite fruit"> <span slot="no-options">Start typing to search...</span></vwc-searchable-select>
<vwc-searchable-select label="Favorite fruits" multiple> <span slot="no-options">Start typing to search...</span></vwc-searchable-select>
<script> const fruitsDatabase = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape', 'Jackfruit', 'Kiwi', 'Lemon', 'Mango', 'Nectarine', 'Orange', 'Passion fruit', 'Quince', 'Raspberry', 'Strawberry', 'Watermelon'];
async function fetchOptions(searchText) { return new Promise((resolve) => { setTimeout( () => resolve( fruitsDatabase .filter((fruit) => fruit.toLowerCase().includes(searchText.toLowerCase())) .map((fruit) => ({ value: fruit, text: fruit, })) ), 1000 ); }); }
window.customElements.whenDefined('vwc-searchable-select').then(() => { document.querySelectorAll('vwc-searchable-select').forEach((select) => { // Disable built-in filtering select.optionFilter = () => true;
// State let componentState = { selectedOptions: [], currentSearchResults: [], currentSearchText: '', };
function MyComponent(state) { const retainedOptions = state.selectedOptions.filter((s) => !state.currentSearchResults.some((o) => o.value === s.value)); return [ // Render options that are selected but no longer in the current search results ...retainedOptions.map((option) => { return { key: option.value, props: { value: option.value, text: option.text, matchedText: '', hidden: true, // But hide them from the dropdown }, }; }), ...state.currentSearchResults.map((option) => { return { key: option.value, props: { value: option.value, text: option.text, matchedText: state.currentQueryText, // Use the search text that the search was performed with hidden: false, }, }; }), ]; }
let previousRenderedOptions = []; const updateState = (newState) => { componentState = newState; const newRenderedOptions = MyComponent(componentState); updateDOM(select, previousRenderedOptions, newRenderedOptions); previousRenderedOptions = newRenderedOptions; };
let latestSearchText = ''; const debouncedSearch = debounce(async (searchText) => { const newResults = await fetchOptions(searchText); if (latestSearchText !== searchText) { return; // Results are no longer relevant } select.loading = false; updateState({ ...componentState, currentSearchResults: newResults, currentQueryText: searchText, }); }, 1000);
const onSearchTextChanged = (newSearchText) => { if (newSearchText === '') { latestSearchText = ''; select.loading = false; updateState({ ...componentState, currentSearchResults: [], currentQueryText: '', }); } else { latestSearchText = newSearchText; select.loading = true; debouncedSearch(newSearchText); } };
select.addEventListener('search-text-change', (e) => { onSearchTextChanged(e.currentTarget.searchText); });
select.addEventListener('input', (e) => { updateState({ ...componentState, selectedOptions: e.currentTarget.values.map((v) => componentState.currentSearchResults.find((o) => o.value === v) || componentState.selectedOptions.find((o) => o.value === v)), }); }); }); });
function debounce(func, timeout) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args); }, timeout); }; }
function updateDOM(select, oldRenderedOptions, newRenderedOptions) { const insertAfter = (afterEl, el) => { if (afterEl) { afterEl.after(el); } else { select.appendChild(el); } };
let lastEl = null; for (let i = 0; i < newRenderedOptions.length; i++) { const newNode = newRenderedOptions[i]; const oldNode = oldRenderedOptions.find((o) => o.key === newNode.key); const el = select.querySelector(`[data-key="${newNode.key}"]`) || document.createElement('vwc-option'); el.dataset.key = newNode.key; for (const key of Object.keys(newNode.props)) { if (!oldNode || newNode.props[key] !== oldNode.props[key]) { el[key] = newNode.props[key]; } } insertAfter(lastEl, el); lastEl = el; } for (const oldNode of oldRenderedOptions) { if (!newRenderedOptions.find((n) => n.key === oldNode.key)) { select.querySelector(`[data-key="${oldNode.key}"]`).remove(); } } }</script>