Tailwind CSS and Alpine JS Combobox
Comboboxes, also known as listboxes, dropdowns, or select, allow users to choose an option from a customized list. Unlike default select components, comboboxes offer customization options, including the ability to incorporate elements like checkboxes.
This component requires Alpine JS v3 to function properly. Some advanced features may require additional Alpine plugins (such as focus).
Tell Me MoreSimple combobox
A simple dropdown with a list of text options, pressing a key will focus on the first option that starts with that key. The selected value will be stored in a hidden input field for easy retrieval.
x-data: Minimal vs Full
Sometimes the content of x-data can become verbose. Alpine offers a way to extract the logic from x-data into a separate object . We provide both styles and you can switch between them. Please note that even with minimal x-data , we still retain essential data (for example, slides for a carousel) and only extract the logic.
<div x-data="{
options: [
{
value: 'Agriculture',
label: 'Agriculture',
},
{
value: 'Construction',
label: 'Construction',
},
{
value: 'Education',
label: 'Education',
},
{
value: 'Entertainment',
label: 'Entertainment',
},
{
value: 'Finance',
label: 'Finance',
},
{
value: 'Healthcare',
label: 'Healthcare',
},
{
value: 'Hospitality',
label: 'Hospitality',
},
{
value: 'IT',
label: 'IT',
},
{
value: 'Manufacturing',
label: 'Manufacturing',
},
{
value: 'Marketing',
label: 'Marketing',
},
{
value: 'Real Estate',
label: 'Real Estate',
},
{
value: 'Retail',
label: 'Retail',
},
{
value: 'Transportation',
label: 'Transportation',
},
],
isOpen: false,
openedWithKeyboard: false,
selectedOption: null,
setSelectedOption(option) {
this.selectedOption = option
this.isOpen = false
this.openedWithKeyboard = false
this.$refs.hiddenTextField.value = option.value
},
highlightFirstMatchingOption(pressedKey) {
const option = this.options.find((item) =>
item.label.toLowerCase().startsWith(pressedKey.toLowerCase()),
)
if (option) {
const index = this.options.indexOf(option)
const allOptions = document.querySelectorAll('.combobox-option')
if (allOptions[index]) {
allOptions[index].focus()
}
}
},
}" class="" x-on:keydown="highlightFirstMatchingOption($event.key)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false">
<label for="industry" class="">Industry</label>
<div class="">
<!-- trigger button -->
<button type="button" role="combobox" class="" aria-haspopup="listbox" aria-controls="industriesList" x-on:click="isOpen = ! isOpen" x-on:keydown.down.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-bind:aria-label="selectedOption ? selectedOption.value : 'Please Select'" x-bind:aria-expanded="isOpen || openedWithKeyboard">
<span class="" x-text="selectedOption ? selectedOption.value : 'Please Select'"></span>
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
</svg>
</button>
<!-- hidden input to grab the selected value -->
<input id="industry" name="industry" type="text" x-ref="hiddenTextField" hidden/>
<ul x-cloak x-show="isOpen || openedWithKeyboard" id="industriesList" class="" role="listbox" aria-label="industries list" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" x-transition x-trap="openedWithKeyboard">
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0" >
<!-- Label -->
<span x-bind:class="selectedOption == item ? 'font-bold' : null" x-text="item.label"></span>
<!-- Screen reader 'selected' indicator -->
<span class="" x-text="selectedOption == item ? 'selected' : null"></span>
<!-- Checkmark -->
<svg x-cloak x-show="selectedOption == item" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"/>
</svg>
</li>
</template>
</ul>
</div>
</div>
<div x-data="combobox({
options: [
{
value: 'Agriculture',
label: 'Agriculture',
},
{
value: 'Construction',
label: 'Construction',
},
{
value: 'Education',
label: 'Education',
},
{
value: 'Entertainment',
label: 'Entertainment',
},
{
value: 'Finance',
label: 'Finance',
},
{
value: 'Healthcare',
label: 'Healthcare',
},
{
value: 'Hospitality',
label: 'Hospitality',
},
{
value: 'IT',
label: 'IT',
},
{
value: 'Manufacturing',
label: 'Manufacturing',
},
{
value: 'Marketing',
label: 'Marketing',
},
{
value: 'Real Estate',
label: 'Real Estate',
},
{
value: 'Retail',
label: 'Retail',
},
{
value: 'Transportation',
label: 'Transportation',
},
],
})" class="" x-on:keydown="highlightFirstMatchingOption($event.key)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false">
<label for="industry" class="">Industry</label>
<div class="">
<!-- trigger button -->
<button type="button" role="combobox" class="" aria-haspopup="listbox" aria-controls="industriesList" x-on:click="isOpen = ! isOpen" x-on:keydown.down.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-bind:aria-label="selectedOption ? selectedOption.value : 'Please Select'" x-bind:aria-expanded="isOpen || openedWithKeyboard">
<span class="" x-text="selectedOption ? selectedOption.value : 'Please Select'"></span>
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
</svg>
</button>
<!-- hidden input to grab the selected value -->
<input id="industry" name="industry" type="text" x-ref="hiddenTextField" hidden/>
<ul x-cloak x-show="isOpen || openedWithKeyboard" id="industriesList" class="" role="listbox" aria-label="industries list" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" x-transition x-trap="openedWithKeyboard">
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0" >
<!-- Label -->
<span x-bind:class="selectedOption == item ? 'font-bold' : null" x-text="item.label"></span>
<!-- Screen reader 'selected' indicator -->
<span class="" x-text="selectedOption == item ? 'selected' : null"></span>
<!-- Checkmark -->
<svg x-cloak x-show="selectedOption == item" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"/>
</svg>
</li>
</template>
</ul>
</div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('combobox', (comboboxData = {
options: [],
},) => ({
options: comboboxData.options,
isOpen: false,
openedWithKeyboard: false,
selectedOption: null,
setSelectedOption(option) {
this.selectedOption = option
this.isOpen = false
this.openedWithKeyboard = false
this.$refs.hiddenTextField.value = option.value
},
highlightFirstMatchingOption(pressedKey) {
const option = this.options.find((item) =>
item.label.toLowerCase().startsWith(pressedKey.toLowerCase()),
)
if (option) {
const index = this.options.indexOf(option)
const allOptions = document.querySelectorAll('.combobox-option')
if (allOptions[index]) {
allOptions[index].focus()
}
}
},
}))
})
</script>
Combobox with images
A dropdown with options that include images, pressing a key will focus on the first option that starts with that key.
x-data: Minimal vs Full
Sometimes the content of x-data can become verbose. Alpine offers a way to extract the logic from x-data into a separate object . We provide both styles and you can switch between them. Please note that even with minimal x-data , we still retain essential data (for example, slides for a carousel) and only extract the logic.
<div x-data="{
options: [
{
value: 'Aiden Walker',
label: 'Aiden Walker',
email: 'aiden.walker@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-1.webp',
},
{
value: 'Alex Martinez',
label: 'Alex Martinez',
email: 'alex.martinez@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-6.webp',
},
{
value: 'Ava Collins',
label: 'Ava Collins',
email: 'ava.collins@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-8.webp',
},
{
value: 'Bob Johnson',
label: 'Bob Johnson',
email: 'bob.johnson@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-2.webp',
},
{
value: 'Emily Rodriguez',
label: 'Emily Rodriguez',
email: 'emily.rodriguez@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-5.webp',
},
{
value: 'Emma Thompson',
label: 'Emma Thompson',
email: 'emma.thompson@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-4.webp',
},
{
value: 'Ethan Brown',
label: 'Ethan Brown',
email: 'ethan.brown@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-9.webp',
},
{
value: 'Isabella Davis',
label: 'Isabella Davis',
email: 'isabellea.davis@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-3.webp',
},
{
value: 'Noah Brooks',
label: 'Noah Brooks',
email: 'noah.brooks@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-7.webp',
},
],
isOpen: false,
openedWithKeyboard: false,
selectedOption: null,
setSelectedOption(option) {
this.selectedOption = option
this.isOpen = false
this.openedWithKeyboard = false
this.$refs.hiddenTextField.value = option.value
},
highlightFirstMatchingOption(pressedKey) {
const option = this.options.find((item) =>
item.label.toLowerCase().startsWith(pressedKey.toLowerCase()),
)
if (option) {
const index = this.options.indexOf(option)
const allOptions = document.querySelectorAll('.combobox-option')
if (allOptions[index]) {
allOptions[index].focus()
}
}
},
}" class="" x-on:keydown="highlightFirstMatchingOption($event.key)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false">
<label for="user" class="">Share with</label>
<div class="">
<!-- trigger button -->
<button type="button" role="combobox" class="" aria-haspopup="listbox" aria-controls="usersList" x-on:click="isOpen = ! isOpen" x-on:keydown.down.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-bind:aria-label="selectedOption ? selectedOption.value : 'Please Select'" x-bind:aria-expanded="isOpen || openedWithKeyboard">
<span class="" x-text="selectedOption ? selectedOption.value : 'Please Select'"></span>
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
</svg>
</button>
<!-- hidden input to grab the selected value -->
<input id="user" name="user" type="text" x-ref="hiddenTextField" hidden/>
<ul x-cloak x-show="isOpen || openedWithKeyboard" id="usersList" class="" role="listbox" aria-label="users list" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" x-transition x-trap="openedWithKeyboard">
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0" >
<div class="">
<img class="" x-bind:src="item.img" alt="" aria-hidden="true"/>
<!-- Label -->
<div class="">
<span x-bind:class="selectedOption == item ? 'font-bold' : null" x-text="item.label"></span>
<span class=""x-text="item.email"></span>
<!-- Screen reader 'selected' indicator -->
<span class="" x-text="selectedOption == item ? 'selected' : null"></span>
</div>
</div>
<!-- Checkmark -->
<svg x-cloak x-show="selectedOption == item" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"/>
</svg>
</li>
</template>
</ul>
</div>
</div>
<div x-data="combobox({
options: [
{
value: 'Aiden Walker',
label: 'Aiden Walker',
email: 'aiden.walker@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-1.webp',
},
{
value: 'Alex Martinez',
label: 'Alex Martinez',
email: 'alex.martinez@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-6.webp',
},
{
value: 'Ava Collins',
label: 'Ava Collins',
email: 'ava.collins@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-8.webp',
},
{
value: 'Bob Johnson',
label: 'Bob Johnson',
email: 'bob.johnson@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-2.webp',
},
{
value: 'Emily Rodriguez',
label: 'Emily Rodriguez',
email: 'emily.rodriguez@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-5.webp',
},
{
value: 'Emma Thompson',
label: 'Emma Thompson',
email: 'emma.thompson@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-4.webp',
},
{
value: 'Ethan Brown',
label: 'Ethan Brown',
email: 'ethan.brown@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-9.webp',
},
{
value: 'Isabella Davis',
label: 'Isabella Davis',
email: 'isabellea.davis@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-3.webp',
},
{
value: 'Noah Brooks',
label: 'Noah Brooks',
email: 'noah.brooks@example.com',
img: 'https://res.cloudinary.com/ds8pgw1pf/image/upload/penguinui/component-assets/avatars/avatar-7.webp',
},
],
})" class="" x-on:keydown="highlightFirstMatchingOption($event.key)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false">
<label for="user" class="">Share with</label>
<div class="">
<!-- trigger button -->
<button type="button" role="combobox" class="" aria-haspopup="listbox" aria-controls="usersList" x-on:click="isOpen = ! isOpen" x-on:keydown.down.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-bind:aria-label="selectedOption ? selectedOption.value : 'Please Select'" x-bind:aria-expanded="isOpen || openedWithKeyboard">
<span class="" x-text="selectedOption ? selectedOption.value : 'Please Select'"></span>
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
</svg>
</button>
<!-- hidden input to grab the selected value -->
<input id="user" name="user" type="text" x-ref="hiddenTextField" hidden/>
<ul x-cloak x-show="isOpen || openedWithKeyboard" id="usersList" class="" role="listbox" aria-label="users list" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" x-transition x-trap="openedWithKeyboard">
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0" >
<div class="">
<img class="" x-bind:src="item.img" alt="" aria-hidden="true"/>
<!-- Label -->
<div class="">
<span x-bind:class="selectedOption == item ? 'font-bold' : null" x-text="item.label"></span>
<span class=""x-text="item.email"></span>
<!-- Screen reader 'selected' indicator -->
<span class="" x-text="selectedOption == item ? 'selected' : null"></span>
</div>
</div>
<!-- Checkmark -->
<svg x-cloak x-show="selectedOption == item" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"/>
</svg>
</li>
</template>
</ul>
</div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('combobox', (comboboxData = {
options: [],
},) => ({
options: comboboxData.options,
isOpen: false,
openedWithKeyboard: false,
selectedOption: null,
setSelectedOption(option) {
this.selectedOption = option
this.isOpen = false
this.openedWithKeyboard = false
this.$refs.hiddenTextField.value = option.value
},
highlightFirstMatchingOption(pressedKey) {
const option = this.options.find((item) =>
item.label.toLowerCase().startsWith(pressedKey.toLowerCase()),
)
if (option) {
const index = this.options.indexOf(option)
const allOptions = document.querySelectorAll('.combobox-option')
if (allOptions[index]) {
allOptions[index].focus()
}
}
},
}))
})
</script>
Combobox with checkboxes
A multiselect combobox with checkboxes that allow users to select multiple options from a list of options. All the selected value will be stored in a hidden input field for easy retrieval.
x-data: Minimal vs Full
Sometimes the content of x-data can become verbose. Alpine offers a way to extract the logic from x-data into a separate object . We provide both styles and you can switch between them. Please note that even with minimal x-data , we still retain essential data (for example, slides for a carousel) and only extract the logic.
<div x-data="{
options: [
{
value: 'C++',
label: 'C++',
},
{
value: 'CSS',
label: 'CSS',
},
{
value: 'Golang',
label: 'Golang',
},
{
value: 'HTML',
label: 'HTML',
},
{
value: 'Java',
label: 'Java',
},
{
value: 'Javascript',
label: 'Javascript',
},
{
value: 'Kotlin',
label: 'Kotlin',
},
{
value: 'Perl',
label: 'Perl',
},
{
value: 'PHP',
label: 'PHP',
},
{
value: 'Python',
label: 'Python',
},
{
value: 'Ruby',
label: 'Ruby',
},
{
value: 'Rust',
label: 'Rust',
},
{
value: 'TypeScript',
label: 'TypeScript',
},
],
isOpen: false,
openedWithKeyboard: false,
selectedOptions: [],
setLabelText() {
const count = this.selectedOptions.length;
// if there are no selected options
if (count === 0) return 'Please Select';
// join the selected options with a comma
return this.selectedOptions.join(', ');
},
highlightFirstMatchingOption(pressedKey) {
// if Enter pressed, do nothing
if (pressedKey === 'Enter') return
// find and focus the option that starts with the pressed key
const option = this.options.find((item) =>
item.label.toLowerCase().startsWith(pressedKey.toLowerCase()),
)
if (option) {
const index = this.options.indexOf(option)
const allOptions = document.querySelectorAll('.combobox-option')
if (allOptions[index]) {
allOptions[index].focus()
}
}
},
handleOptionToggle(option) {
if (option.checked) {
this.selectedOptions.push(option.value)
} else {
// remove the unchecked option from the selectedOptions array
this.selectedOptions = this.selectedOptions.filter(
(opt) => opt !== option.value,
)
}
// set the value of the hidden field to the selectedOptions array
this.$refs.hiddenTextField.value = this.selectedOptions
},
}" class="" x-on:keydown="highlightFirstMatchingOption($event.key)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false">
<label for="skills" class="">Skills(s)</label>
<div class="">
<!-- trigger button -->
<button type="button" role="combobox" class="" aria-haspopup="listbox" aria-controls="skillsList" x-on:click="isOpen = ! isOpen" x-on:keydown.down.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-bind:aria-label="setLabelText()" x-bind:aria-expanded="isOpen || openedWithKeyboard">
<span class="" x-text="setLabelText()"></span>
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
</svg>
</button>
<!-- hidden input to grab the selected value -->
<input id="skills" name="skills" type="text" x-ref="hiddenTextField" hidden />
<ul x-cloak x-show="isOpen || openedWithKeyboard" id="skillsList" class="" role="listbox" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" x-transition x-trap="openedWithKeyboard">
<template x-for="(item, index) in options" x-bind:key="item.value">
<!-- option -->
<li role="option">
<label class="" x-bind:for="'checkboxOption' + index">
<div class="">
<input type="checkbox" class="" x-on:change="handleOptionToggle($el)" x-on:keydown.enter.prevent="$el.checked = ! $el.checked; handleOptionToggle($el)" x-bind:value="item.value" x-bind:id="'checkboxOption' + index" />
<!-- Checkmark -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="4" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/>
</svg>
</div>
<span x-text="item.label"></span>
</label>
</li>
</template>
</ul>
</div>
</div>
<div x-data="combobox({
options: [
{
value: 'C++',
label: 'C++',
},
{
value: 'CSS',
label: 'CSS',
},
{
value: 'Golang',
label: 'Golang',
},
{
value: 'HTML',
label: 'HTML',
},
{
value: 'Java',
label: 'Java',
},
{
value: 'Javascript',
label: 'Javascript',
},
{
value: 'Kotlin',
label: 'Kotlin',
},
{
value: 'Perl',
label: 'Perl',
},
{
value: 'PHP',
label: 'PHP',
},
{
value: 'Python',
label: 'Python',
},
{
value: 'Ruby',
label: 'Ruby',
},
{
value: 'Rust',
label: 'Rust',
},
{
value: 'TypeScript',
label: 'TypeScript',
},
]
})" class="" x-on:keydown="highlightFirstMatchingOption($event.key)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false">
<label for="skills" class="">Skills(s)</label>
<div class="">
<!-- trigger button -->
<button type="button" role="combobox" class="" aria-haspopup="listbox" aria-controls="skillsList" x-on:click="isOpen = ! isOpen" x-on:keydown.down.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-bind:aria-label="setLabelText()" x-bind:aria-expanded="isOpen || openedWithKeyboard">
<span class="" x-text="setLabelText()"></span>
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
</svg>
</button>
<!-- hidden input to grab the selected value -->
<input id="skills" name="skills" type="text" x-ref="hiddenTextField" hidden />
<ul x-cloak x-show="isOpen || openedWithKeyboard" id="skillsList" class="" role="listbox" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" x-transition x-trap="openedWithKeyboard">
<template x-for="(item, index) in options" x-bind:key="item.value">
<!-- option -->
<li role="option">
<label class="" x-bind:for="'checkboxOption' + index">
<div class="">
<input type="checkbox" class="" x-on:change="handleOptionToggle($el)" x-on:keydown.enter.prevent="$el.checked = ! $el.checked; handleOptionToggle($el)" x-bind:value="item.value" x-bind:id="'checkboxOption' + index" />
<!-- Checkmark -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="4" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"/>
</svg>
</div>
<span x-text="item.label"></span>
</label>
</li>
</template>
</ul>
</div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('combobox', (comboboxData = {
options: [],
},) => ({
options: comboboxData.options,
isOpen: false,
openedWithKeyboard: false,
selectedOptions: [],
setLabelText() {
const count = this.selectedOptions.length;
// if there are no selected options
if (count === 0) return 'Please Select';
// if there is only one selected option
return this.selectedOptions.join(', ');
},
highlightFirstMatchingOption(pressedKey) {
// if Enter pressed, do nothing
if (pressedKey === 'Enter') return
// find and focus the option that starts with the pressed key
const option = this.options.find((item) =>
item.label.toLowerCase().startsWith(pressedKey.toLowerCase()),
)
if (option) {
const index = this.options.indexOf(option)
const allOptions = document.querySelectorAll('.combobox-option')
if (allOptions[index]) {
allOptions[index].focus()
}
}
},
handleOptionToggle(option) {
if (option.checked) {
this.selectedOptions.push(option.value)
} else {
// remove the unchecked option from the selectedOptions array
this.selectedOptions = this.selectedOptions.filter(
(opt) => opt !== option.value
)
}
// set the value of the hidden field to the selectedOptions array
this.$refs.hiddenTextField.value = this.selectedOptions
},
}))
})
</script>
Combobox with search
A dropdown with a text input that allows users to filter the list of options.
- No matches found
x-data: Minimal vs Full
Sometimes the content of x-data can become verbose. Alpine offers a way to extract the logic from x-data into a separate object . We provide both styles and you can switch between them. Please note that even with minimal x-data , we still retain essential data (for example, slides for a carousel) and only extract the logic.
<div x-data="{
allOptions: [
{
label: 'Acura',
value: 'Acura'
},
{
label: 'Alfa Romeo',
value: 'Alfa Romeo'
},
{
label: 'Aston Martin',
value: 'Aston Martin'
},
{
label: 'Audi',
value: 'Audi'
},
{
label: 'Bentley',
value: 'Bentley'
},
{
label: 'BMW',
value: 'BMW'
},
{
label: 'Bugatti',
value: 'Bugatti'
},
{
label: 'Buick',
value: 'Buick'
},
{
label: 'Cadillac',
value: 'Cadillac'
},
{
label: 'Chevrolet',
value: 'Chevrolet'
},
{
label: 'Chrysler',
value: 'Chrysler'
},
{
label: 'Citroën',
value: 'Citroën'
},
{
label: 'Dodge',
value: 'Dodge'
},
{
label: 'Ferrari',
value: 'Ferrari'
},
{
label: 'Fiat',
value: 'Fiat'
},
{
label: 'Ford',
value: 'Ford'
},
{
label: 'Genesis',
value: 'Genesis'
},
{
label: 'GMC',
value: 'GMC'
},
{
label: 'Honda',
value: 'Honda'
},
{
label: 'Hyundai',
value: 'Hyundai'
},
{
label: 'Infiniti',
value: 'Infiniti'
},
{
label: 'Jaguar',
value: 'Jaguar'
},
{
label: 'Jeep',
value: 'Jeep'
},
{
label: 'Kia',
value: 'Kia'
},
{
label: 'Lamborghini',
value: 'Lamborghini'
},
{
label: 'Land Rover',
value: 'Land Rover'
},
{
label: 'Lexus',
value: 'Lexus'
},
{
label: 'Lincoln',
value: 'Lincoln'
},
{
label: 'Maserati',
value: 'Maserati'
},
{
label: 'Mazda',
value: 'Mazda'
},
{
label: 'McLaren',
value: 'McLaren'
},
{
label: 'Mercedes-Benz',
value: 'Mercedes-Benz'
},
{
label: 'Mini',
value: 'Mini'
},
{
label: 'Mitsubishi',
value: 'Mitsubishi'
},
{
label: 'Nissan',
value: 'Nissan'
},
{
label: 'Peugeot',
value: 'Peugeot'
},
{
label: 'Porsche',
value: 'Porsche'
},
{
label: 'Ram',
value: 'Ram'
},
{
label: 'Renault',
value: 'Renault'
},
{
label: 'Rolls-Royce',
value: 'Rolls-Royce'
},
{
label: 'Subaru',
value: 'Subaru'
},
{
label: 'Suzuki',
value: 'Suzuki'
},
{
label: 'Tesla',
value: 'Tesla'
},
{
label: 'Toyota',
value: 'Toyota'
},
{
label: 'Volkswagen',
value: 'Volkswagen'
},
{
label: 'Volvo',
value: 'Volvo'
},
],
options: [],
isOpen: false,
openedWithKeyboard: false,
selectedOption: null,
setSelectedOption(option) {
this.selectedOption = option
this.isOpen = false
this.openedWithKeyboard = false
this.$refs.hiddenTextField.value = option.value
},
getFilteredOptions(query) {
this.options = this.allOptions.filter((option) =>
option.label.toLowerCase().includes(query.toLowerCase()),
)
if (this.options.length === 0) {
this.$refs.noResultsMessage.classList.remove('hidden')
} else {
this.$refs.noResultsMessage.classList.add('hidden')
}
},
handleKeydownOnOptions(event) {
// if the user presses backspace or the alpha-numeric keys, focus on the search field
if ((event.keyCode >= 65 && event.keyCode <= 90) || (event.keyCode >= 48 && event.keyCode <= 57) || event.keyCode === 8) {
this.$refs.searchField.focus()
}
},
}" class="" x-on:keydown="handleKeydownOnOptions($event)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false" x-init="options = allOptions">
<label for="make" class="">Make</label>
<div class="">
<!-- trigger button -->
<button type="button" class="" role="combobox" aria-controls="makesList" aria-haspopup="listbox" x-on:click="isOpen = ! isOpen" x-on:keydown.down.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-bind:aria-expanded="isOpen || openedWithKeyboard" x-bind:aria-label="selectedOption ? selectedOption.value : 'Please Select'" >
<span class="" x-text="selectedOption ? selectedOption.value : 'Please Select'"></span>
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"class="" aria-hidden="true">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
</svg>
</button>
<!-- Hidden Input To Grab The Selected Value -->
<input id="make" name="make" x-ref="hiddenTextField" hidden=""/>
<div x-show="isOpen || openedWithKeyboard" id="makesList" class="" role="listbox" aria-label="industries list" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" x-transition x-trap="openedWithKeyboard">
<!-- Search -->
<div class="">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="1.5" class="" aria-hidden="true" >
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"/>
</svg>
<input type="text" class="" name="searchField" aria-label="Search" x-on:input="getFilteredOptions($el.value)" x-ref="searchField" placeholder="Search" />
</div>
<!-- Options -->
<ul class="">
<li class="" x-ref="noResultsMessage">
<span>No matches found</span>
</li>
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0">
<!-- Label -->
<span x-bind:class="selectedOption == item ? 'font-bold' : null" x-text="item.label"></span>
<!-- Screen reader 'selected' indicator -->
<span class="" x-text="selectedOption == item ? 'selected' : null"></span>
<!-- Checkmark -->
<svg x-cloak x-show="selectedOption == item" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5">
</svg>
</li>
</template>
</ul>
</div>
</div>
</div>
<div x-data="combobox({
allOptions: [
{
label: 'Acura',
value: 'Acura'
},
{
label: 'Alfa Romeo',
value: 'Alfa Romeo'
},
{
label: 'Aston Martin',
value: 'Aston Martin'
},
{
label: 'Audi',
value: 'Audi'
},
{
label: 'Bentley',
value: 'Bentley'
},
{
label: 'BMW',
value: 'BMW'
},
{
label: 'Bugatti',
value: 'Bugatti'
},
{
label: 'Buick',
value: 'Buick'
},
{
label: 'Cadillac',
value: 'Cadillac'
},
{
label: 'Chevrolet',
value: 'Chevrolet'
},
{
label: 'Chrysler',
value: 'Chrysler'
},
{
label: 'Citroën',
value: 'Citroën'
},
{
label: 'Dodge',
value: 'Dodge'
},
{
label: 'Ferrari',
value: 'Ferrari'
},
{
label: 'Fiat',
value: 'Fiat'
},
{
label: 'Ford',
value: 'Ford'
},
{
label: 'Genesis',
value: 'Genesis'
},
{
label: 'GMC',
value: 'GMC'
},
{
label: 'Honda',
value: 'Honda'
},
{
label: 'Hyundai',
value: 'Hyundai'
},
{
label: 'Infiniti',
value: 'Infiniti'
},
{
label: 'Jaguar',
value: 'Jaguar'
},
{
label: 'Jeep',
value: 'Jeep'
},
{
label: 'Kia',
value: 'Kia'
},
{
label: 'Lamborghini',
value: 'Lamborghini'
},
{
label: 'Land Rover',
value: 'Land Rover'
},
{
label: 'Lexus',
value: 'Lexus'
},
{
label: 'Lincoln',
value: 'Lincoln'
},
{
label: 'Maserati',
value: 'Maserati'
},
{
label: 'Mazda',
value: 'Mazda'
},
{
label: 'McLaren',
value: 'McLaren'
},
{
label: 'Mercedes-Benz',
value: 'Mercedes-Benz'
},
{
label: 'Mini',
value: 'Mini'
},
{
label: 'Mitsubishi',
value: 'Mitsubishi'
},
{
label: 'Nissan',
value: 'Nissan'
},
{
label: 'Peugeot',
value: 'Peugeot'
},
{
label: 'Porsche',
value: 'Porsche'
},
{
label: 'Ram',
value: 'Ram'
},
{
label: 'Renault',
value: 'Renault'
},
{
label: 'Rolls-Royce',
value: 'Rolls-Royce'
},
{
label: 'Subaru',
value: 'Subaru'
},
{
label: 'Suzuki',
value: 'Suzuki'
},
{
label: 'Tesla',
value: 'Tesla'
},
{
label: 'Toyota',
value: 'Toyota'
},
{
label: 'Volkswagen',
value: 'Volkswagen'
},
{
label: 'Volvo',
value: 'Volvo'
},
],
})" class="" x-on:keydown="handleKeydownOnOptions($event)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false">
<label for="make" class="">Make</label>
<div class="">
<!-- trigger button -->
<button type="button" class="" role="combobox" aria-controls="makesList" aria-haspopup="listbox" x-on:click="isOpen = ! isOpen" x-on:keydown.down.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-bind:aria-expanded="isOpen || openedWithKeyboard" x-bind:aria-label="selectedOption ? selectedOption.value : 'Please Select'" >
<span class="" x-text="selectedOption ? selectedOption.value : 'Please Select'"></span>
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"class="" aria-hidden="true">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
</svg>
</button>
<!-- Hidden Input To Grab The Selected Value -->
<input id="make" name="make" x-ref="hiddenTextField" hidden=""/>
<div x-show="isOpen || openedWithKeyboard" id="makesList" class="" role="listbox" aria-label="industries list" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" x-transition x-trap="openedWithKeyboard">
<!-- Search -->
<div class="">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="1.5" class="" aria-hidden="true" >
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"/>
</svg>
<input type="text" class="" name="searchField" x-ref="searchField" aria-label="Search" x-on:input="getFilteredOptions($el.value)" placeholder="Search" />
</div>
<!-- Options -->
<ul class="">
<li class="" x-ref="noResultsMessage">
<span>No matches found</span>
</li>
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0">
<!-- Label -->
<span x-bind:class="selectedOption == item ? 'font-bold' : null" x-text="item.label"></span>
<!-- Screen reader 'selected' indicator -->
<span class="" x-text="selectedOption == item ? 'selected' : null"></span>
<!-- Checkmark -->
<svg x-cloak x-show="selectedOption == item" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5">
</svg>
</li>
</template>
</ul>
</div>
</div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('combobox', (comboboxData = {
allOptions: [],
},) => ({
options: comboboxData.allOptions,
isOpen: false,
openedWithKeyboard: false,
selectedOption: null,
setSelectedOption(option) {
this.selectedOption = option
this.isOpen = false
this.openedWithKeyboard = false
this.$refs.hiddenTextField.value = option.value
},
getFilteredOptions(query) {
this.options = comboboxData.allOptions.filter((option) =>
option.label.toLowerCase().includes(query.toLowerCase()),
)
if (this.options.length === 0) {
this.$refs.noResultsMessage.classList.remove('hidden')
} else {
this.$refs.noResultsMessage.classList.add('hidden')
}
},
// if the user presses backspace or the alpha-numeric keys, focus on the search field
handleKeydownOnOptions(event) {
if ((event.keyCode >= 65 && event.keyCode <= 90) || (event.keyCode >= 48 && event.keyCode <= 57) || event.keyCode === 8) {
this.$refs.searchField.focus()
}
},
}))
})
</script>
|| event.keyCode === 8 Country selector with flags and search
A country selector dropdown with flags and a search field. This component uses flagpedia.net for the flags. The selected value will be stored in a hidden input field for easy retrieval.
- No matches found
-
x-data: Minimal vs Full
Sometimes the content of x-data can become verbose. Alpine offers a way to extract the logic from x-data into a separate object . We provide both styles and you can switch between them. Please note that even with minimal x-data , we still retain essential data (for example, slides for a carousel) and only extract the logic.
<div x-data="{
allOptions: [
{ label: 'Afghanistan', value: 'Afghanistan', iso: 'af' },
{ label: 'Albania', value: 'Albania', iso: 'al' },
{ label: 'Algeria', value: 'Algeria', iso: 'dz' },
{ label: 'Andorra', value: 'Andorra', iso: 'ad' },
{ label: 'Angola', value: 'Angola', iso: 'ao' },
{ label: 'Anguilla', value: 'Anguilla', iso: 'ai' },
{ label: 'Antigua and Barbuda', value: 'Antigua and Barbuda', iso: 'ag' },
{ label: 'Argentina', value: 'Argentina', iso: 'ar' },
{ label: 'Armenia', value: 'Armenia', iso: 'am' },
{ label: 'Aruba', value: 'Aruba', iso: 'aw' },
{ label: 'Australia', value: 'Australia', iso: 'au' },
{ label: 'Austria', value: 'Austria', iso: 'at' },
{ label: 'Azerbaijan', value: 'Azerbaijan', iso: 'az' },
{ label: 'Bahamas', value: 'Bahamas', iso: 'bs' },
{ label: 'Bahrain', value: 'Bahrain', iso: 'bh' },
{ label: 'Bangladesh', value: 'Bangladesh', iso: 'bd' },
{ label: 'Barbados', value: 'Barbados', iso: 'bb' },
{ label: 'Belarus', value: 'Belarus', iso: 'by' },
{ label: 'Belgium', value: 'Belgium', iso: 'be' },
{ label: 'Belize', value: 'Belize', iso: 'bz' },
{ label: 'Benin', value: 'Benin', iso: 'bj' },
{ label: 'Bermuda', value: 'Bermuda', iso: 'bm' },
{ label: 'Bhutan', value: 'Bhutan', iso: 'bt' },
{ label: 'Bolivia', value: 'Bolivia', iso: 'bo' },
{ label: 'Bosnia and Herzegovina', value: 'Bosnia and Herzegovina', iso: 'ba' },
{ label: 'Botswana', value: 'Botswana', iso: 'bw' },
{ label: 'Brazil', value: 'Brazil', iso: 'br' },
{ label: 'British Indian Ocean Territory', value: 'British Indian Ocean Territory', iso: 'io' },
{ label: 'British Virgin Islands', value: 'British Virgin Islands', iso: 'vg' },
{ label: 'Brunei', value: 'Brunei', iso: 'bn' },
{ label: 'Bulgaria', value: 'Bulgaria', iso: 'bg' },
{ label: 'Burkina Faso', value: 'Burkina Faso', iso: 'bf' },
{ label: 'Burundi', value: 'Burundi', iso: 'bi' },
{ label: 'Cambodia', value: 'Cambodia', iso: 'kh' },
{ label: 'Cameroon', value: 'Cameroon', iso: 'cm' },
{ label: 'Canada', value: 'Canada', iso: 'ca' },
{ label: 'Cape Verde', value: 'Cape Verde', iso: 'cv' },
{ label: 'Cayman Islands', value: 'Cayman Islands', iso: 'ky' },
{ label: 'Central African Republic', value: 'Central African Republic', iso: 'cf' },
{ label: 'Chad', value: 'Chad', iso: 'td' },
{ label: 'Chile', value: 'Chile', iso: 'cl' },
{ label: 'China', value: 'China', iso: 'cn' },
{ label: 'Colombia', value: 'Colombia', iso: 'co' },
{ label: 'Comoros', value: 'Comoros', iso: 'km' },
{ label: 'Congo (Brazzaville)', value: 'Congo (Brazzaville)', iso: 'cg' },
{ label: 'Congo (Kinshasa)', value: 'Congo (Kinshasa)', iso: 'cd' },
{ label: 'Cook Islands', value: 'Cook Islands', iso: 'ck' },
{ label: 'Costa Rica', value: 'Costa Rica', iso: 'cr' },
{ label: 'Croatia', value: 'Croatia', iso: 'hr' },
{ label: 'Cuba', value: 'Cuba', iso: 'cu' },
{ label: 'Curaçao', value: 'Curaçao', iso: 'cw' },
{ label: 'Cyprus', value: 'Cyprus', iso: 'cy' },
{ label: 'Czechia', value: 'Czechia', iso: 'cz' },
{ label: 'Denmark', value: 'Denmark', iso: 'dk' },
{ label: 'Djibouti', value: 'Djibouti', iso: 'dj' },
{ label: 'Dominica', value: 'Dominica', iso: 'dm' },
{ label: 'Dominican Republic', value: 'Dominican Republic', iso: 'do' },
{ label: 'Ecuador', value: 'Ecuador', iso: 'ec' },
{ label: 'Egypt', value: 'Egypt', iso: 'eg' },
{ label: 'El Salvador', value: 'El Salvador', iso: 'sv' },
{ label: 'England', value: 'England', iso: 'gb-eng' },
{ label: 'Equatorial Guinea', value: 'Equatorial Guinea', iso: 'gq' },
{ label: 'Eritrea', value: 'Eritrea', iso: 'er' },
{ label: 'Estonia', value: 'Estonia', iso: 'ee' },
{ label: 'Eswatini (Swaziland)', value: 'Eswatini (Swaziland)', iso: 'sz' },
{ label: 'Ethiopia', value: 'Ethiopia', iso: 'et' },
{ label: 'Falkland Islands (Islas Malvinas)', value: 'Falkland Islands (Islas Malvinas)', iso: 'fk' },
{ label: 'Faroe Islands', value: 'Faroe Islands', iso: 'fo' },
{ label: 'Fiji', value: 'Fiji', iso: 'fj' },
{ label: 'Finland', value: 'Finland', iso: 'fi' },
{ label: 'France', value: 'France', iso: 'fr' },
{ label: 'French Guiana', value: 'French Guiana', iso: 'gf' },
{ label: 'French Polynesia', value: 'French Polynesia', iso: 'pf' },
{ label: 'French Southern and Antarctic Lands', value: 'French Southern and Antarctic Lands', iso: 'tf' },
{ label: 'Gabon', value: 'Gabon', iso: 'ga' },
{ label: 'Gambia', value: 'Gambia', iso: 'gm' },
{ label: 'Georgia', value: 'Georgia', iso: 'ge' },
{ label: 'Germany', value: 'Germany', iso: 'de' },
{ label: 'Ghana', value: 'Ghana', iso: 'gh' },
{ label: 'Gibraltar', value: 'Gibraltar', iso: 'gi' },
{ label: 'Greece', value: 'Greece', iso: 'gr' },
{ label: 'Greenland', value: 'Greenland', iso: 'gl' },
{ label: 'Grenada', value: 'Grenada', iso: 'gd' },
{ label: 'Guadeloupe', value: 'Guadeloupe', iso: 'gp' },
{ label: 'Guam', value: 'Guam', iso: 'gu' },
{ label: 'Guatemala', value: 'Guatemala', iso: 'gt' },
{ label: 'Guernsey', value: 'Guernsey', iso: 'gg' },
{ label: 'Guinea', value: 'Guinea', iso: 'gn' },
{ label: 'Guinea-Bissau', value: 'Guinea-Bissau', iso: 'gw' },
{ label: 'Guyana', value: 'Guyana', iso: 'gy' },
{ label: 'Haiti', value: 'Haiti', iso: 'ht' },
{ label: 'Honduras', value: 'Honduras', iso: 'hn' },
{ label: 'Hong Kong', value: 'Hong Kong', iso: 'hk' },
{ label: 'Hungary', value: 'Hungary', iso: 'hu' },
{ label: 'Iceland', value: 'Iceland', iso: 'is' },
{ label: 'India', value: 'India', iso: 'in' },
{ label: 'Indonesia', value: 'Indonesia', iso: 'id' },
{ label: 'Iran', value: 'Iran', iso: 'ir' },
{ label: 'Iraq', value: 'Iraq', iso: 'iq' },
{ label: 'Ireland', value: 'Ireland', iso: 'ie' },
{ label: 'Isle of Man', value: 'Isle of Man', iso: 'im' },
{ label: 'Israel', value: 'Israel', iso: 'il' },
{ label: 'Italy', value: 'Italy', iso: 'it' },
{ label: 'Ivory Coast', value: 'Ivory Coast', iso: 'ci' },
{ label: 'Jamaica', value: 'Jamaica', iso: 'jm' },
{ label: 'Japan', value: 'Japan', iso: 'jp' },
{ label: 'Jersey', value: 'Jersey', iso: 'je' },
{ label: 'Jordan', value: 'Jordan', iso: 'jo' },
{ label: 'Kazakhstan', value: 'Kazakhstan', iso: 'kz' },
{ label: 'Kenya', value: 'Kenya', iso: 'ke' },
{ label: 'Kiribati', value: 'Kiribati', iso: 'ki' },
{ label: 'Kosovo', value: 'Kosovo', iso: 'xk' },
{ label: 'Kuwait', value: 'Kuwait', iso: 'kw' },
{ label: 'Kyrgyzstan', value: 'Kyrgyzstan', iso: 'kg' },
{ label: 'Laos', value: 'Laos', iso: 'la' },
{ label: 'Latvia', value: 'Latvia', iso: 'lv' },
{ label: 'Lebanon', value: 'Lebanon', iso: 'lb' },
{ label: 'Lesotho', value: 'Lesotho', iso: 'ls' },
{ label: 'Liberia', value: 'Liberia', iso: 'lr' },
{ label: 'Libya', value: 'Libya', iso: 'ly' },
{ label: 'Liechtenstein', value: 'Liechtenstein', iso: 'li' },
{ label: 'Lithuania', value: 'Lithuania', iso: 'lt' },
{ label: 'Luxembourg', value: 'Luxembourg', iso: 'lu' },
{ label: 'Macao', value: 'Macao', iso: 'mo' },
{ label: 'Madagascar', value: 'Madagascar', iso: 'mg' },
{ label: 'Malawi', value: 'Malawi', iso: 'mw' },
{ label: 'Malaysia', value: 'Malaysia', iso: 'my' },
{ label: 'Maldives', value: 'Maldives', iso: 'mv' },
{ label: 'Mali', value: 'Mali', iso: 'ml' },
{ label: 'Malta', value: 'Malta', iso: 'mt' },
{ label: 'Marshall Islands', value: 'Marshall Islands', iso: 'mh' },
{ label: 'Martinique', value: 'Martinique', iso: 'mq' },
{ label: 'Mauritania', value: 'Mauritania', iso: 'mr' },
{ label: 'Mauritius', value: 'Mauritius', iso: 'mu' },
{ label: 'Mayotte', value: 'Mayotte', iso: 'yt' },
{ label: 'Mexico', value: 'Mexico', iso: 'mx' },
{ label: 'Micronesia', value: 'Micronesia', iso: 'fm' },
{ label: 'Moldova', value: 'Moldova', iso: 'md' },
{ label: 'Monaco', value: 'Monaco', iso: 'mc' },
{ label: 'Mongolia', value: 'Mongolia', iso: 'mn' },
{ label: 'Montenegro', value: 'Montenegro', iso: 'me' },
{ label: 'Montserrat', value: 'Montserrat', iso: 'ms' },
{ label: 'Morocco', value: 'Morocco', iso: 'ma' },
{ label: 'Mozambique', value: 'Mozambique', iso: 'mz' },
{ label: 'Myanmar (Burma)', value: 'Myanmar (Burma)', iso: 'mm' },
{ label: 'Namibia', value: 'Namibia', iso: 'na' },
{ label: 'Nauru', value: 'Nauru', iso: 'nr' },
{ label: 'Nepal', value: 'Nepal', iso: 'np' },
{ label: 'Netherlands', value: 'Netherlands', iso: 'nl' },
{ label: 'New Caledonia', value: 'New Caledonia', iso: 'nc' },
{ label: 'New Zealand', value: 'New Zealand', iso: 'nz' },
{ label: 'Nicaragua', value: 'Nicaragua', iso: 'ni' },
{ label: 'Niger', value: 'Niger', iso: 'ne' },
{ label: 'Nigeria', value: 'Nigeria', iso: 'ng' },
{ label: 'Niue', value: 'Niue', iso: 'nu' },
{ label: 'Norfolk Island', value: 'Norfolk Island', iso: 'nf' },
{ label: 'North Korea', value: 'North Korea', iso: 'kp' },
{ label: 'North Macedonia', value: 'North Macedonia', iso: 'mk' },
{ label: 'Northern Ireland', value: 'Northern Ireland', iso: 'gb-nir' },
{ label: 'Northern Mariana Islands', value: 'Northern Mariana Islands', iso: 'mp' },
{ label: 'Norway', value: 'Norway', iso: 'no' },
{ label: 'Oman', value: 'Oman', iso: 'om' },
{ label: 'Pakistan', value: 'Pakistan', iso: 'pk' },
{ label: 'Palau', value: 'Palau', iso: 'pw' },
{ label: 'Palestine', value: 'Palestine', iso: 'ps' },
{ label: 'Panama', value: 'Panama', iso: 'pa' },
{ label: 'Papua New Guinea', value: 'Papua New Guinea', iso: 'pg' },
{ label: 'Paraguay', value: 'Paraguay', iso: 'py' },
{ label: 'Peru', value: 'Peru', iso: 'pe' },
{ label: 'Philippines', value: 'Philippines', iso: 'ph' },
{ label: 'Pitcairn Islands', value: 'Pitcairn Islands', iso: 'pn' },
{ label: 'Poland', value: 'Poland', iso: 'pl' },
{ label: 'Portugal', value: 'Portugal', iso: 'pt' },
{ label: 'Puerto Rico', value: 'Puerto Rico', iso: 'pr' },
{ label: 'Qatar', value: 'Qatar', iso: 'qa' },
{ label: 'Romania', value: 'Romania', iso: 'ro' },
{ label: 'Russia', value: 'Russia', iso: 'ru' },
{ label: 'Rwanda', value: 'Rwanda', iso: 'rw' },
{ label: 'Saint Helena, Ascension and Tristan da Cunha', value: 'Saint Helena, Ascension and Tristan da Cunha', iso: 'sh' },
{ label: 'Saint Kitts and Nevis', value: 'Saint Kitts and Nevis', iso: 'kn' },
{ label: 'Saint Lucia', value: 'Saint Lucia', iso: 'lc' },
{ label: 'Saint Pierre and Miquelon', value: 'Saint Pierre and Miquelon', iso: 'pm' },
{ label: 'Saint Vincent and the Grenadines', value: 'Saint Vincent and the Grenadines', iso: 'vc' },
{ label: 'Samoa', value: 'Samoa', iso: 'ws' },
{ label: 'San Marino', value: 'San Marino', iso: 'sm' },
{ label: 'Sao Tome and Principe', value: 'Sao Tome and Principe', iso: 'st' },
{ label: 'Saudi Arabia', value: 'Saudi Arabia', iso: 'sa' },
{ label: 'Scotland', value: 'Scotland', iso: 'gb-sct' },
{ label: 'Senegal', value: 'Senegal', iso: 'sn' },
{ label: 'Serbia', value: 'Serbia', iso: 'rs' },
{ label: 'Seychelles', value: 'Seychelles', iso: 'sc' },
{ label: 'Sierra Leone', value: 'Sierra Leone', iso: 'sl' },
{ label: 'Singapore', value: 'Singapore', iso: 'sg' },
{ label: 'Sint Maarten', value: 'Sint Maarten', iso: 'sx' },
{ label: 'Slovakia', value: 'Slovakia', iso: 'sk' },
{ label: 'Slovenia', value: 'Slovenia', iso: 'si' },
{ label: 'Solomon Islands', value: 'Solomon Islands', iso: 'sb' },
{ label: 'Somalia', value: 'Somalia', iso: 'so' },
{ label: 'South Africa', value: 'South Africa', iso: 'za' },
{ label: 'South Korea', value: 'South Korea', iso: 'kr' },
{ label: 'South Sudan', value: 'South Sudan', iso: 'ss' },
{ label: 'Spain', value: 'Spain', iso: 'es' },
{ label: 'Sri Lanka', value: 'Sri Lanka', iso: 'lk' },
{ label: 'Sudan', value: 'Sudan', iso: 'sd' },
{ label: 'Suriname', value: 'Suriname', iso: 'sr' },
{ label: 'Svalbard and Jan Mayen', value: 'Svalbard and Jan Mayen', iso: 'sj' },
{ label: 'Sweden', value: 'Sweden', iso: 'se' },
{ label: 'Switzerland', value: 'Switzerland', iso: 'ch' },
{ label: 'Syria', value: 'Syria', iso: 'sy' },
{ label: 'Taiwan', value: 'Taiwan', iso: 'tw' },
{ label: 'Tajikistan', value: 'Tajikistan', iso: 'tj' },
{ label: 'Tanzania', value: 'Tanzania', iso: 'tz' },
{ label: 'Thailand', value: 'Thailand', iso: 'th' },
{ label: 'Timor-Leste', value: 'Timor-Leste', iso: 'tl' },
{ label: 'Togo', value: 'Togo', iso: 'tg' },
{ label: 'Tokelau', value: 'Tokelau', iso: 'tk' },
{ label: 'Tonga', value: 'Tonga', iso: 'to' },
{ label: 'Trinidad and Tobago', value: 'Trinidad and Tobago', iso: 'tt' },
{ label: 'Tunisia', value: 'Tunisia', iso: 'tn' },
{ label: 'Turkey', value: 'Turkey', iso: 'tr' },
{ label: 'Turkmenistan', value: 'Turkmenistan', iso: 'tm' },
{ label: 'Turks and Caicos Islands', value: 'Turks and Caicos Islands', iso: 'tc' },
{ label: 'Tuvalu', value: 'Tuvalu', iso: 'tv' },
{ label: 'Uganda', value: 'Uganda', iso: 'ug' },
{ label: 'Ukraine', value: 'Ukraine', iso: 'ua' },
{ label: 'United Arab Emirates', value: 'United Arab Emirates', iso: 'ae' },
{ label: 'United Kingdom', value: 'United Kingdom', iso: 'gb' },
{ label: 'United States of America', value: 'United States of America', iso: 'us' },
{ label: 'Uruguay', value: 'Uruguay', iso: 'uy' },
{ label: 'Uzbekistan', value: 'Uzbekistan', iso: 'uz' },
{ label: 'Vanuatu', value: 'Vanuatu', iso: 'vu' },
{ label: 'Vatican City (Holy See)', value: 'Vatican City (Holy See)', iso: 'va' },
{ label: 'Venezuela', value: 'Venezuela', iso: 've' },
{ label: 'Vietnam', value: 'Vietnam', iso: 'vn' },
{ label: 'Wales', value: 'Wales', iso: 'gb-wls' },
{ label: 'Western Sahara', value: 'Western Sahara', iso: 'eh' },
{ label: 'Yemen', value: 'Yemen', iso: 'ye' },
{ label: 'Zambia', value: 'Zambia', iso: 'zm' },
{ label: 'Zimbabwe', value: 'Zimbabwe', iso: 'zw' },
],
options: [],
isOpen: false,
openedWithKeyboard: false,
selectedOption: null,
setSelectedOption(option) {
this.selectedOption = option
this.isOpen = false
this.openedWithKeyboard = false
this.$refs.hiddenTextField.value = option.value
},
getFilteredOptions(query) {
this.options = this.allOptions.filter((option) =>
option.label.toLowerCase().includes(query.toLowerCase()),
)
if (this.options.length === 0) {
this.$refs.noResultsMessage.classList.remove('hidden')
} else {
this.$refs.noResultsMessage.classList.add('hidden')
}
},
handleKeydownOnOptions(event) {
// if the user presses backspace or the alpha-numeric keys, focus on the search field
if ((event.keyCode >= 65 && event.keyCode <= 90) || (event.keyCode >= 48 && event.keyCode <= 57) || event.keyCode === 8) {
this.$refs.searchField.focus()
}
},
}" class="" x-on:keydown="handleKeydownOnOptions($event)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false" x-init="options = allOptions">
<label for="country" class="">Country</label>
<div class="">
<!-- trigger button -->
<button type="button" class="" role="combobox" aria-controls="countriesList" aria-haspopup="listbox" x-on:click="isOpen = ! isOpen" x-on:keydown.down.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-bind:aria-expanded="isOpen || openedWithKeyboard" x-bind:aria-label="selectedOption ? selectedOption.value : 'Please Select'" >
<span class="" x-text="selectedOption ? selectedOption.value : 'Please Select'"></span>
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"class="" aria-hidden="true">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
</svg>
</button>
<!-- Hidden Input To Grab The Selected Value -->
<input id="country" name="country" autocomplete="off" x-ref="hiddenTextField" hidden=""/>
<div x-show="isOpen || openedWithKeyboard" id="countriesList" class="" role="listbox" aria-label="countries list" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" x-transition x-trap="openedWithKeyboard">
<!-- Search -->
<div class="">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="1.5" class="" aria-hidden="true" >
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"/>
</svg>
<input type="text" class="" name="searchField" aria-label="Search" x-on:input="getFilteredOptions($el.value)" x-ref="searchField" placeholder="Search" />
</div>
<!-- Options -->
<ul class="">
<li class="" x-ref="noResultsMessage">
<span>No matches found</span>
</li>
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0">
<div class="">
<!-- Flag image -->
<img class="w-5 h-3.5" x-bind:src="'https://flagcdn.com/' + item.iso + '.svg'" alt="" aria-hidden="true"/>
<!-- Country name -->
<span x-bind:class="selectedOption == item ? 'font-bold' : null" x-text="item.label"></span>
<!-- Screen reader 'selected' indicator -->
<span class="" x-text="selectedOption == item ? 'selected' : null"></span>
</div>
<!-- Checkmark -->
<svg x-cloak x-show="selectedOption == item" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5">
</svg>
</li>
</template>
</ul>
</div>
</div>
</div>
<div x-data="combobox" class="" x-on:keydown="handleKeydownOnOptions($event)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false">
<label for="country" class="">Country</label>
<div class="">
<!-- trigger button -->
<button type="button" class="" role="combobox" aria-controls="countriesList" aria-haspopup="listbox" x-on:click="isOpen = ! isOpen" x-on:keydown.down.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-bind:aria-expanded="isOpen || openedWithKeyboard" x-bind:aria-label="selectedOption ? selectedOption.value : 'Please Select'" >
<span class="" x-text="selectedOption ? selectedOption.value : 'Please Select'"></span>
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"class="" aria-hidden="true">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
</svg>
</button>
<!-- Hidden Input To Grab The Selected Value -->
<input id="country" name="country" autocomplete="off" x-ref="hiddenTextField" hidden=""/>
<div x-show="isOpen || openedWithKeyboard" id="countriesList" class="" role="listbox" aria-label="countries list" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" x-transition x-trap="openedWithKeyboard">
<!-- Search -->
<div class="">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="1.5" class="" aria-hidden="true" >
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"/>
</svg>
<input type="text" class="" name="searchField" aria-label="Search" x-on:input="getFilteredOptions($el.value)" x-ref="searchField" placeholder="Search" />
</div>
<!-- Options -->
<ul class="">
<li class="" x-ref="noResultsMessage">
<span>No matches found</span>
</li>
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0">
<div class="">
<!-- Flag image -->
<img class="w-5 h-3.5" x-bind:src="'https://flagcdn.com/' + item.iso + '.svg'" alt="" aria-hidden="true"/>
<!-- Country name -->
<span x-bind:class="selectedOption == item ? 'font-bold' : null" x-text="item.label"></span>
<!-- Screen reader 'selected' indicator -->
<span class="" x-text="selectedOption == item ? 'selected' : null"></span>
</div>
<!-- Checkmark -->
<svg x-cloak x-show="selectedOption == item" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5">
</svg>
</li>
</template>
</ul>
</div>
</div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('combobox', (comboboxData = {
allOptions: [
{ label: 'Afghanistan', value: 'Afghanistan', iso: 'af' },
{ label: 'Albania', value: 'Albania', iso: 'al' },
{ label: 'Algeria', value: 'Algeria', iso: 'dz' },
{ label: 'Andorra', value: 'Andorra', iso: 'ad' },
{ label: 'Angola', value: 'Angola', iso: 'ao' },
{ label: 'Anguilla', value: 'Anguilla', iso: 'ai' },
{ label: 'Antigua and Barbuda', value: 'Antigua and Barbuda', iso: 'ag' },
{ label: 'Argentina', value: 'Argentina', iso: 'ar' },
{ label: 'Armenia', value: 'Armenia', iso: 'am' },
{ label: 'Aruba', value: 'Aruba', iso: 'aw' },
{ label: 'Australia', value: 'Australia', iso: 'au' },
{ label: 'Austria', value: 'Austria', iso: 'at' },
{ label: 'Azerbaijan', value: 'Azerbaijan', iso: 'az' },
{ label: 'Bahamas', value: 'Bahamas', iso: 'bs' },
{ label: 'Bahrain', value: 'Bahrain', iso: 'bh' },
{ label: 'Bangladesh', value: 'Bangladesh', iso: 'bd' },
{ label: 'Barbados', value: 'Barbados', iso: 'bb' },
{ label: 'Belarus', value: 'Belarus', iso: 'by' },
{ label: 'Belgium', value: 'Belgium', iso: 'be' },
{ label: 'Belize', value: 'Belize', iso: 'bz' },
{ label: 'Benin', value: 'Benin', iso: 'bj' },
{ label: 'Bermuda', value: 'Bermuda', iso: 'bm' },
{ label: 'Bhutan', value: 'Bhutan', iso: 'bt' },
{ label: 'Bolivia', value: 'Bolivia', iso: 'bo' },
{ label: 'Bosnia and Herzegovina', value: 'Bosnia and Herzegovina', iso: 'ba' },
{ label: 'Botswana', value: 'Botswana', iso: 'bw' },
{ label: 'Brazil', value: 'Brazil', iso: 'br' },
{ label: 'British Indian Ocean Territory', value: 'British Indian Ocean Territory', iso: 'io' },
{ label: 'British Virgin Islands', value: 'British Virgin Islands', iso: 'vg' },
{ label: 'Brunei', value: 'Brunei', iso: 'bn' },
{ label: 'Bulgaria', value: 'Bulgaria', iso: 'bg' },
{ label: 'Burkina Faso', value: 'Burkina Faso', iso: 'bf' },
{ label: 'Burundi', value: 'Burundi', iso: 'bi' },
{ label: 'Cambodia', value: 'Cambodia', iso: 'kh' },
{ label: 'Cameroon', value: 'Cameroon', iso: 'cm' },
{ label: 'Canada', value: 'Canada', iso: 'ca' },
{ label: 'Cape Verde', value: 'Cape Verde', iso: 'cv' },
{ label: 'Cayman Islands', value: 'Cayman Islands', iso: 'ky' },
{ label: 'Central African Republic', value: 'Central African Republic', iso: 'cf' },
{ label: 'Chad', value: 'Chad', iso: 'td' },
{ label: 'Chile', value: 'Chile', iso: 'cl' },
{ label: 'China', value: 'China', iso: 'cn' },
{ label: 'Colombia', value: 'Colombia', iso: 'co' },
{ label: 'Comoros', value: 'Comoros', iso: 'km' },
{ label: 'Congo (Brazzaville)', value: 'Congo (Brazzaville)', iso: 'cg' },
{ label: 'Congo (Kinshasa)', value: 'Congo (Kinshasa)', iso: 'cd' },
{ label: 'Cook Islands', value: 'Cook Islands', iso: 'ck' },
{ label: 'Costa Rica', value: 'Costa Rica', iso: 'cr' },
{ label: 'Croatia', value: 'Croatia', iso: 'hr' },
{ label: 'Cuba', value: 'Cuba', iso: 'cu' },
{ label: 'Curaçao', value: 'Curaçao', iso: 'cw' },
{ label: 'Cyprus', value: 'Cyprus', iso: 'cy' },
{ label: 'Czechia', value: 'Czechia', iso: 'cz' },
{ label: 'Denmark', value: 'Denmark', iso: 'dk' },
{ label: 'Djibouti', value: 'Djibouti', iso: 'dj' },
{ label: 'Dominica', value: 'Dominica', iso: 'dm' },
{ label: 'Dominican Republic', value: 'Dominican Republic', iso: 'do' },
{ label: 'Ecuador', value: 'Ecuador', iso: 'ec' },
{ label: 'Egypt', value: 'Egypt', iso: 'eg' },
{ label: 'El Salvador', value: 'El Salvador', iso: 'sv' },
{ label: 'England', value: 'England', iso: 'gb-eng' },
{ label: 'Equatorial Guinea', value: 'Equatorial Guinea', iso: 'gq' },
{ label: 'Eritrea', value: 'Eritrea', iso: 'er' },
{ label: 'Estonia', value: 'Estonia', iso: 'ee' },
{ label: 'Eswatini (Swaziland)', value: 'Eswatini (Swaziland)', iso: 'sz' },
{ label: 'Ethiopia', value: 'Ethiopia', iso: 'et' },
{ label: 'Falkland Islands (Islas Malvinas)', value: 'Falkland Islands (Islas Malvinas)', iso: 'fk' },
{ label: 'Faroe Islands', value: 'Faroe Islands', iso: 'fo' },
{ label: 'Fiji', value: 'Fiji', iso: 'fj' },
{ label: 'Finland', value: 'Finland', iso: 'fi' },
{ label: 'France', value: 'France', iso: 'fr' },
{ label: 'French Guiana', value: 'French Guiana', iso: 'gf' },
{ label: 'French Polynesia', value: 'French Polynesia', iso: 'pf' },
{ label: 'French Southern and Antarctic Lands', value: 'French Southern and Antarctic Lands', iso: 'tf' },
{ label: 'Gabon', value: 'Gabon', iso: 'ga' },
{ label: 'Gambia', value: 'Gambia', iso: 'gm' },
{ label: 'Georgia', value: 'Georgia', iso: 'ge' },
{ label: 'Germany', value: 'Germany', iso: 'de' },
{ label: 'Ghana', value: 'Ghana', iso: 'gh' },
{ label: 'Gibraltar', value: 'Gibraltar', iso: 'gi' },
{ label: 'Greece', value: 'Greece', iso: 'gr' },
{ label: 'Greenland', value: 'Greenland', iso: 'gl' },
{ label: 'Grenada', value: 'Grenada', iso: 'gd' },
{ label: 'Guadeloupe', value: 'Guadeloupe', iso: 'gp' },
{ label: 'Guam', value: 'Guam', iso: 'gu' },
{ label: 'Guatemala', value: 'Guatemala', iso: 'gt' },
{ label: 'Guernsey', value: 'Guernsey', iso: 'gg' },
{ label: 'Guinea', value: 'Guinea', iso: 'gn' },
{ label: 'Guinea-Bissau', value: 'Guinea-Bissau', iso: 'gw' },
{ label: 'Guyana', value: 'Guyana', iso: 'gy' },
{ label: 'Haiti', value: 'Haiti', iso: 'ht' },
{ label: 'Honduras', value: 'Honduras', iso: 'hn' },
{ label: 'Hong Kong', value: 'Hong Kong', iso: 'hk' },
{ label: 'Hungary', value: 'Hungary', iso: 'hu' },
{ label: 'Iceland', value: 'Iceland', iso: 'is' },
{ label: 'India', value: 'India', iso: 'in' },
{ label: 'Indonesia', value: 'Indonesia', iso: 'id' },
{ label: 'Iran', value: 'Iran', iso: 'ir' },
{ label: 'Iraq', value: 'Iraq', iso: 'iq' },
{ label: 'Ireland', value: 'Ireland', iso: 'ie' },
{ label: 'Isle of Man', value: 'Isle of Man', iso: 'im' },
{ label: 'Israel', value: 'Israel', iso: 'il' },
{ label: 'Italy', value: 'Italy', iso: 'it' },
{ label: 'Ivory Coast', value: 'Ivory Coast', iso: 'ci' },
{ label: 'Jamaica', value: 'Jamaica', iso: 'jm' },
{ label: 'Japan', value: 'Japan', iso: 'jp' },
{ label: 'Jersey', value: 'Jersey', iso: 'je' },
{ label: 'Jordan', value: 'Jordan', iso: 'jo' },
{ label: 'Kazakhstan', value: 'Kazakhstan', iso: 'kz' },
{ label: 'Kenya', value: 'Kenya', iso: 'ke' },
{ label: 'Kiribati', value: 'Kiribati', iso: 'ki' },
{ label: 'Kosovo', value: 'Kosovo', iso: 'xk' },
{ label: 'Kuwait', value: 'Kuwait', iso: 'kw' },
{ label: 'Kyrgyzstan', value: 'Kyrgyzstan', iso: 'kg' },
{ label: 'Laos', value: 'Laos', iso: 'la' },
{ label: 'Latvia', value: 'Latvia', iso: 'lv' },
{ label: 'Lebanon', value: 'Lebanon', iso: 'lb' },
{ label: 'Lesotho', value: 'Lesotho', iso: 'ls' },
{ label: 'Liberia', value: 'Liberia', iso: 'lr' },
{ label: 'Libya', value: 'Libya', iso: 'ly' },
{ label: 'Liechtenstein', value: 'Liechtenstein', iso: 'li' },
{ label: 'Lithuania', value: 'Lithuania', iso: 'lt' },
{ label: 'Luxembourg', value: 'Luxembourg', iso: 'lu' },
{ label: 'Macao', value: 'Macao', iso: 'mo' },
{ label: 'Madagascar', value: 'Madagascar', iso: 'mg' },
{ label: 'Malawi', value: 'Malawi', iso: 'mw' },
{ label: 'Malaysia', value: 'Malaysia', iso: 'my' },
{ label: 'Maldives', value: 'Maldives', iso: 'mv' },
{ label: 'Mali', value: 'Mali', iso: 'ml' },
{ label: 'Malta', value: 'Malta', iso: 'mt' },
{ label: 'Marshall Islands', value: 'Marshall Islands', iso: 'mh' },
{ label: 'Martinique', value: 'Martinique', iso: 'mq' },
{ label: 'Mauritania', value: 'Mauritania', iso: 'mr' },
{ label: 'Mauritius', value: 'Mauritius', iso: 'mu' },
{ label: 'Mayotte', value: 'Mayotte', iso: 'yt' },
{ label: 'Mexico', value: 'Mexico', iso: 'mx' },
{ label: 'Micronesia', value: 'Micronesia', iso: 'fm' },
{ label: 'Moldova', value: 'Moldova', iso: 'md' },
{ label: 'Monaco', value: 'Monaco', iso: 'mc' },
{ label: 'Mongolia', value: 'Mongolia', iso: 'mn' },
{ label: 'Montenegro', value: 'Montenegro', iso: 'me' },
{ label: 'Montserrat', value: 'Montserrat', iso: 'ms' },
{ label: 'Morocco', value: 'Morocco', iso: 'ma' },
{ label: 'Mozambique', value: 'Mozambique', iso: 'mz' },
{ label: 'Myanmar (Burma)', value: 'Myanmar (Burma)', iso: 'mm' },
{ label: 'Namibia', value: 'Namibia', iso: 'na' },
{ label: 'Nauru', value: 'Nauru', iso: 'nr' },
{ label: 'Nepal', value: 'Nepal', iso: 'np' },
{ label: 'Netherlands', value: 'Netherlands', iso: 'nl' },
{ label: 'New Caledonia', value: 'New Caledonia', iso: 'nc' },
{ label: 'New Zealand', value: 'New Zealand', iso: 'nz' },
{ label: 'Nicaragua', value: 'Nicaragua', iso: 'ni' },
{ label: 'Niger', value: 'Niger', iso: 'ne' },
{ label: 'Nigeria', value: 'Nigeria', iso: 'ng' },
{ label: 'Niue', value: 'Niue', iso: 'nu' },
{ label: 'Norfolk Island', value: 'Norfolk Island', iso: 'nf' },
{ label: 'North Korea', value: 'North Korea', iso: 'kp' },
{ label: 'North Macedonia', value: 'North Macedonia', iso: 'mk' },
{ label: 'Northern Ireland', value: 'Northern Ireland', iso: 'gb-nir' },
{ label: 'Northern Mariana Islands', value: 'Northern Mariana Islands', iso: 'mp' },
{ label: 'Norway', value: 'Norway', iso: 'no' },
{ label: 'Oman', value: 'Oman', iso: 'om' },
{ label: 'Pakistan', value: 'Pakistan', iso: 'pk' },
{ label: 'Palau', value: 'Palau', iso: 'pw' },
{ label: 'Palestine', value: 'Palestine', iso: 'ps' },
{ label: 'Panama', value: 'Panama', iso: 'pa' },
{ label: 'Papua New Guinea', value: 'Papua New Guinea', iso: 'pg' },
{ label: 'Paraguay', value: 'Paraguay', iso: 'py' },
{ label: 'Peru', value: 'Peru', iso: 'pe' },
{ label: 'Philippines', value: 'Philippines', iso: 'ph' },
{ label: 'Pitcairn Islands', value: 'Pitcairn Islands', iso: 'pn' },
{ label: 'Poland', value: 'Poland', iso: 'pl' },
{ label: 'Portugal', value: 'Portugal', iso: 'pt' },
{ label: 'Puerto Rico', value: 'Puerto Rico', iso: 'pr' },
{ label: 'Qatar', value: 'Qatar', iso: 'qa' },
{ label: 'Romania', value: 'Romania', iso: 'ro' },
{ label: 'Russia', value: 'Russia', iso: 'ru' },
{ label: 'Rwanda', value: 'Rwanda', iso: 'rw' },
{ label: 'Saint Helena, Ascension and Tristan da Cunha', value: 'Saint Helena, Ascension and Tristan da Cunha', iso: 'sh' },
{ label: 'Saint Kitts and Nevis', value: 'Saint Kitts and Nevis', iso: 'kn' },
{ label: 'Saint Lucia', value: 'Saint Lucia', iso: 'lc' },
{ label: 'Saint Pierre and Miquelon', value: 'Saint Pierre and Miquelon', iso: 'pm' },
{ label: 'Saint Vincent and the Grenadines', value: 'Saint Vincent and the Grenadines', iso: 'vc' },
{ label: 'Samoa', value: 'Samoa', iso: 'ws' },
{ label: 'San Marino', value: 'San Marino', iso: 'sm' },
{ label: 'Sao Tome and Principe', value: 'Sao Tome and Principe', iso: 'st' },
{ label: 'Saudi Arabia', value: 'Saudi Arabia', iso: 'sa' },
{ label: 'Scotland', value: 'Scotland', iso: 'gb-sct' },
{ label: 'Senegal', value: 'Senegal', iso: 'sn' },
{ label: 'Serbia', value: 'Serbia', iso: 'rs' },
{ label: 'Seychelles', value: 'Seychelles', iso: 'sc' },
{ label: 'Sierra Leone', value: 'Sierra Leone', iso: 'sl' },
{ label: 'Singapore', value: 'Singapore', iso: 'sg' },
{ label: 'Sint Maarten', value: 'Sint Maarten', iso: 'sx' },
{ label: 'Slovakia', value: 'Slovakia', iso: 'sk' },
{ label: 'Slovenia', value: 'Slovenia', iso: 'si' },
{ label: 'Solomon Islands', value: 'Solomon Islands', iso: 'sb' },
{ label: 'Somalia', value: 'Somalia', iso: 'so' },
{ label: 'South Africa', value: 'South Africa', iso: 'za' },
{ label: 'South Korea', value: 'South Korea', iso: 'kr' },
{ label: 'South Sudan', value: 'South Sudan', iso: 'ss' },
{ label: 'Spain', value: 'Spain', iso: 'es' },
{ label: 'Sri Lanka', value: 'Sri Lanka', iso: 'lk' },
{ label: 'Sudan', value: 'Sudan', iso: 'sd' },
{ label: 'Suriname', value: 'Suriname', iso: 'sr' },
{ label: 'Svalbard and Jan Mayen', value: 'Svalbard and Jan Mayen', iso: 'sj' },
{ label: 'Sweden', value: 'Sweden', iso: 'se' },
{ label: 'Switzerland', value: 'Switzerland', iso: 'ch' },
{ label: 'Syria', value: 'Syria', iso: 'sy' },
{ label: 'Taiwan', value: 'Taiwan', iso: 'tw' },
{ label: 'Tajikistan', value: 'Tajikistan', iso: 'tj' },
{ label: 'Tanzania', value: 'Tanzania', iso: 'tz' },
{ label: 'Thailand', value: 'Thailand', iso: 'th' },
{ label: 'Timor-Leste', value: 'Timor-Leste', iso: 'tl' },
{ label: 'Togo', value: 'Togo', iso: 'tg' },
{ label: 'Tokelau', value: 'Tokelau', iso: 'tk' },
{ label: 'Tonga', value: 'Tonga', iso: 'to' },
{ label: 'Trinidad and Tobago', value: 'Trinidad and Tobago', iso: 'tt' },
{ label: 'Tunisia', value: 'Tunisia', iso: 'tn' },
{ label: 'Turkey', value: 'Turkey', iso: 'tr' },
{ label: 'Turkmenistan', value: 'Turkmenistan', iso: 'tm' },
{ label: 'Turks and Caicos Islands', value: 'Turks and Caicos Islands', iso: 'tc' },
{ label: 'Tuvalu', value: 'Tuvalu', iso: 'tv' },
{ label: 'Uganda', value: 'Uganda', iso: 'ug' },
{ label: 'Ukraine', value: 'Ukraine', iso: 'ua' },
{ label: 'United Arab Emirates', value: 'United Arab Emirates', iso: 'ae' },
{ label: 'United Kingdom', value: 'United Kingdom', iso: 'gb' },
{ label: 'United States of America', value: 'United States of America', iso: 'us' },
{ label: 'Uruguay', value: 'Uruguay', iso: 'uy' },
{ label: 'Uzbekistan', value: 'Uzbekistan', iso: 'uz' },
{ label: 'Vanuatu', value: 'Vanuatu', iso: 'vu' },
{ label: 'Vatican City (Holy See)', value: 'Vatican City (Holy See)', iso: 'va' },
{ label: 'Venezuela', value: 'Venezuela', iso: 've' },
{ label: 'Vietnam', value: 'Vietnam', iso: 'vn' },
{ label: 'Wales', value: 'Wales', iso: 'gb-wls' },
{ label: 'Western Sahara', value: 'Western Sahara', iso: 'eh' },
{ label: 'Yemen', value: 'Yemen', iso: 'ye' },
{ label: 'Zambia', value: 'Zambia', iso: 'zm' },
{ label: 'Zimbabwe', value: 'Zimbabwe', iso: 'zw' },
],
},) => ({
options: comboboxData.allOptions,
isOpen: false,
openedWithKeyboard: false,
selectedOption: null,
setSelectedOption(option) {
this.selectedOption = option
this.isOpen = false
this.openedWithKeyboard = false
this.$refs.hiddenTextField.value = option.value
},
getFilteredOptions(query) {
this.options = comboboxData.allOptions.filter((option) =>
option.label.toLowerCase().includes(query.toLowerCase()),
)
if (this.options.length === 0) {
this.$refs.noResultsMessage.classList.remove('hidden')
} else {
this.$refs.noResultsMessage.classList.add('hidden')
}
},
// if the user presses backspace or the alpha-numeric keys, focus on the search field
handleKeydownOnOptions(event) {
if ((event.keyCode >= 65 && event.keyCode <= 90) || (event.keyCode >= 48 && event.keyCode <= 57) || event.keyCode === 8) {
this.$refs.searchField.focus()
}
},
}))
})
</script>
US state selector with flags and search
A U.S. state selector dropdown with state flags and a search field. This component uses flagpedia.net for the flags. The selected value will be stored in a hidden input field for easy retrieval.
- No matches found
-
x-data: Minimal vs Full
Sometimes the content of x-data can become verbose. Alpine offers a way to extract the logic from x-data into a separate object . We provide both styles and you can switch between them. Please note that even with minimal x-data , we still retain essential data (for example, slides for a carousel) and only extract the logic.
<div x-data="{
allOptions: [
{
label: 'Alabama',
value: 'Alabama',
iso: 'AL'
},
{
label: 'Alaska',
value: 'Alaska',
iso: 'AK'
},
{
label: 'Arizona',
value: 'Arizona',
iso: 'AZ'
},
{
label: 'Arkansas',
value: 'Arkansas',
iso: 'AR'
},
{
label: 'California',
value: 'California',
iso: 'CA'
},
{
label: 'Colorado',
value: 'Colorado',
iso: 'CO'
},
{
label: 'Connecticut',
value: 'Connecticut',
iso: 'CT'
},
{
label: 'Delaware',
value: 'Delaware',
iso: 'DE'
},
{
label: 'Florida',
value: 'Florida',
iso: 'FL'
},
{
label: 'Georgia',
value: 'Georgia',
iso: 'GA'
},
{
label: 'Hawaii',
value: 'Hawaii',
iso: 'HI'
},
{
label: 'Idaho',
value: 'Idaho',
iso: 'ID'
},
{
label: 'Illinois',
value: 'Illinois',
iso: 'IL'
},
{
label: 'Indiana',
value: 'Indiana',
iso: 'IN'
},
{
label: 'Iowa',
value: 'Iowa',
iso: 'IA'
},
{
label: 'Kansas',
value: 'Kansas',
iso: 'KS'
},
{
label: 'Kentucky',
value: 'Kentucky',
iso: 'KY'
},
{
label: 'Louisiana',
value: 'Louisiana',
iso: 'LA'
},
{
label: 'Maine',
value: 'Maine',
iso: 'ME'
},
{
label: 'Maryland',
value: 'Maryland',
iso: 'MD'
},
{
label: 'Massachusetts',
value: 'Massachusetts',
iso: 'MA'
},
{
label: 'Michigan',
value: 'Michigan',
iso: 'MI'
},
{
label: 'Minnesota',
value: 'Minnesota',
iso: 'MN'
},
{
label: 'Mississippi',
value: 'Mississippi',
iso: 'MS'
},
{
label: 'Missouri',
value: 'Missouri',
iso: 'MO'
},
{
label: 'Montana',
value: 'Montana',
iso: 'MT'
},
{
label: 'Nebraska',
value: 'Nebraska',
iso: 'NE'
},
{
label: 'Nevada',
value: 'Nevada',
iso: 'NV'
},
{
label: 'New Hampshire',
value: 'New Hampshire',
iso: 'NH'
},
{
label: 'New Jersey',
value: 'New Jersey',
iso: 'NJ'
},
{
label: 'New Mexico',
value: 'New Mexico',
iso: 'NM'
},
{
label: 'New York',
value: 'New York',
iso: 'NY'
},
{
label: 'North Carolina',
value: 'North Carolina',
iso: 'NC'
},
{
label: 'North Dakota',
value: 'North Dakota',
iso: 'ND'
},
{
label: 'Ohio',
value: 'Ohio',
iso: 'OH'
},
{
label: 'Oklahoma',
value: 'Oklahoma',
iso: 'OK'
},
{
label: 'Oregon',
value: 'Oregon',
iso: 'OR'
},
{
label: 'Pennsylvania',
value: 'Pennsylvania',
iso: 'PA'
},
{
label: 'Rhode Island',
value: 'Rhode Island',
iso: 'RI'
},
{
label: 'South Carolina',
value: 'South Carolina',
iso: 'SC'
},
{
label: 'South Dakota',
value: 'South Dakota',
iso: 'SD'
},
{
label: 'Tennessee',
value: 'Tennessee',
iso: 'TN'
},
{
label: 'Texas',
value: 'Texas',
iso: 'TX'
},
{
label: 'Utah',
value: 'Utah',
iso: 'UT'
},
{
label: 'Vermont',
value: 'Vermont',
iso: 'VT'
},
{
label: 'Virginia',
value: 'Virginia',
iso: 'VA'
},
{
label: 'Washington',
value: 'Washington',
iso: 'WA'
},
{
label: 'West Virginia',
value: 'West Virginia',
iso: 'WV'
},
{
label: 'Wisconsin',
value: 'Wisconsin',
iso: 'WI'
},
{
label: 'Wyoming',
value: 'Wyoming',
iso: 'WY'
}
],
options: [],
isOpen: false,
openedWithKeyboard: false,
selectedOption: null,
setSelectedOption(option) {
this.selectedOption = option
this.isOpen = false
this.openedWithKeyboard = false
this.$refs.hiddenTextField.value = option.value
},
getFilteredOptions(query) {
this.options = this.allOptions.filter((option) =>
option.label.toLowerCase().includes(query.toLowerCase()) ||
option.iso.toLowerCase().includes(query.toLowerCase())
)
if (this.options.length === 0) {
this.$refs.noResultsMessage.classList.remove('hidden')
} else {
this.$refs.noResultsMessage.classList.add('hidden')
}
},
handleKeydownOnOptions(event) {
// if the user presses backspace or the alpha-numeric keys, focus on the search field
if ((event.keyCode >= 65 && event.keyCode <= 90) || (event.keyCode >= 48 && event.keyCode <= 57) || event.keyCode === 8) {
this.$refs.searchField.focus()
}
},
}" class="" x-on:keydown="handleKeydownOnOptions($event)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false" x-init="options = allOptions">
<label for="state" class="">State</label>
<div class="">
<!-- trigger button -->
<button type="button" class="" role="combobox" aria-controls="statesList" aria-haspopup="listbox" x-on:click="isOpen = ! isOpen" x-on:keydown.down.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-bind:aria-expanded="isOpen || openedWithKeyboard" x-bind:aria-label="selectedOption ? selectedOption.value : 'Please Select'" >
<span class="" x-text="selectedOption ? selectedOption.value : 'Please Select'"></span>
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"class="" aria-hidden="true">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
</svg>
</button>
<!-- Hidden Input To Grab The Selected Value -->
<input id="state" name="state" autocomplete="off" x-ref="hiddenTextField" hidden=""/>
<div x-show="isOpen || openedWithKeyboard" id="statesList" class="" role="listbox" aria-label="states list" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" x-transition x-trap="openedWithKeyboard">
<!-- Search -->
<div class="">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="1.5" class="" aria-hidden="true" >
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"/>
</svg>
<input type="text" class="" name="searchField" aria-label="Search" x-on:input="getFilteredOptions($el.value)" x-ref="searchField" placeholder="Search" />
</div>
<!-- Options -->
<ul class="">
<li class="" x-ref="noResultsMessage">
<span>No matches found</span>
</li>
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0">
<div class="">
<!-- Flag image -->
<img class="w-5 h-3.5" x-bind:src="'https://flagcdn.com/us-' + item.iso.toLowerCase() + '.svg'" alt="" aria-hidden="true"/>
<!-- State name -->
<span x-bind:class="selectedOption == item ? 'font-bold' : null" x-text="item.label"></span>
<!-- Screen reader 'selected' indicator -->
<span class="" x-text="selectedOption == item ? 'selected' : null"></span>
</div>
<!-- Checkmark -->
<svg x-cloak x-show="selectedOption == item" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5">
</svg>
</li>
</template>
</ul>
</div>
</div>
</div>
<div x-data="combobox" class="" x-on:keydown="handleKeydownOnOptions($event)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false">
<label for="state" class="">State</label>
<div class="">
<!-- trigger button -->
<button type="button" class="" role="combobox" aria-controls="statesList" aria-haspopup="listbox" x-on:click="isOpen = ! isOpen" x-on:keydown.down.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-bind:aria-expanded="isOpen || openedWithKeyboard" x-bind:aria-label="selectedOption ? selectedOption.value : 'Please Select'" >
<span class="" x-text="selectedOption ? selectedOption.value : 'Please Select'"></span>
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor"class="" aria-hidden="true">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd"/>
</svg>
</button>
<!-- Hidden Input To Grab The Selected Value -->
<input id="state" name="state" autocomplete="off" x-ref="hiddenTextField" hidden=""/>
<div x-show="isOpen || openedWithKeyboard" id="statesList" class="" role="listbox" aria-label="states list" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" x-transition x-trap="openedWithKeyboard">
<!-- Search -->
<div class="">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="1.5" class="" aria-hidden="true" >
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"/>
</svg>
<input type="text" class="" name="searchField" aria-label="Search" x-on:input="getFilteredOptions($el.value)" x-ref="searchField" placeholder="Search" />
</div>
<!-- Options -->
<ul class="">
<li class="" x-ref="noResultsMessage">
<span>No matches found</span>
</li>
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0">
<div class="">
<!-- Flag image -->
<img class="w-5 h-3.5" x-bind:src="'https://flagcdn.com/us-' + item.iso.toLowerCase() + '.svg'" alt="" aria-hidden="true"/>
<!-- State name -->
<span x-bind:class="selectedOption == item ? 'font-bold' : null" x-text="item.label"></span>
<!-- Screen reader 'selected' indicator -->
<span class="" x-text="selectedOption == item ? 'selected' : null"></span>
</div>
<!-- Checkmark -->
<svg x-cloak x-show="selectedOption == item" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5">
</svg>
</li>
</template>
</ul>
</div>
</div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('combobox', (comboboxData = {
allOptions: [
{
label: 'Alabama',
value: 'Alabama',
iso: 'AL'
},
{
label: 'Alaska',
value: 'Alaska',
iso: 'AK'
},
{
label: 'Arizona',
value: 'Arizona',
iso: 'AZ'
},
{
label: 'Arkansas',
value: 'Arkansas',
iso: 'AR'
},
{
label: 'California',
value: 'California',
iso: 'CA'
},
{
label: 'Colorado',
value: 'Colorado',
iso: 'CO'
},
{
label: 'Connecticut',
value: 'Connecticut',
iso: 'CT'
},
{
label: 'Delaware',
value: 'Delaware',
iso: 'DE'
},
{
label: 'Florida',
value: 'Florida',
iso: 'FL'
},
{
label: 'Georgia',
value: 'Georgia',
iso: 'GA'
},
{
label: 'Hawaii',
value: 'Hawaii',
iso: 'HI'
},
{
label: 'Idaho',
value: 'Idaho',
iso: 'ID'
},
{
label: 'Illinois',
value: 'Illinois',
iso: 'IL'
},
{
label: 'Indiana',
value: 'Indiana',
iso: 'IN'
},
{
label: 'Iowa',
value: 'Iowa',
iso: 'IA'
},
{
label: 'Kansas',
value: 'Kansas',
iso: 'KS'
},
{
label: 'Kentucky',
value: 'Kentucky',
iso: 'KY'
},
{
label: 'Louisiana',
value: 'Louisiana',
iso: 'LA'
},
{
label: 'Maine',
value: 'Maine',
iso: 'ME'
},
{
label: 'Maryland',
value: 'Maryland',
iso: 'MD'
},
{
label: 'Massachusetts',
value: 'Massachusetts',
iso: 'MA'
},
{
label: 'Michigan',
value: 'Michigan',
iso: 'MI'
},
{
label: 'Minnesota',
value: 'Minnesota',
iso: 'MN'
},
{
label: 'Mississippi',
value: 'Mississippi',
iso: 'MS'
},
{
label: 'Missouri',
value: 'Missouri',
iso: 'MO'
},
{
label: 'Montana',
value: 'Montana',
iso: 'MT'
},
{
label: 'Nebraska',
value: 'Nebraska',
iso: 'NE'
},
{
label: 'Nevada',
value: 'Nevada',
iso: 'NV'
},
{
label: 'New Hampshire',
value: 'New Hampshire',
iso: 'NH'
},
{
label: 'New Jersey',
value: 'New Jersey',
iso: 'NJ'
},
{
label: 'New Mexico',
value: 'New Mexico',
iso: 'NM'
},
{
label: 'New York',
value: 'New York',
iso: 'NY'
},
{
label: 'North Carolina',
value: 'North Carolina',
iso: 'NC'
},
{
label: 'North Dakota',
value: 'North Dakota',
iso: 'ND'
},
{
label: 'Ohio',
value: 'Ohio',
iso: 'OH'
},
{
label: 'Oklahoma',
value: 'Oklahoma',
iso: 'OK'
},
{
label: 'Oregon',
value: 'Oregon',
iso: 'OR'
},
{
label: 'Pennsylvania',
value: 'Pennsylvania',
iso: 'PA'
},
{
label: 'Rhode Island',
value: 'Rhode Island',
iso: 'RI'
},
{
label: 'South Carolina',
value: 'South Carolina',
iso: 'SC'
},
{
label: 'South Dakota',
value: 'South Dakota',
iso: 'SD'
},
{
label: 'Tennessee',
value: 'Tennessee',
iso: 'TN'
},
{
label: 'Texas',
value: 'Texas',
iso: 'TX'
},
{
label: 'Utah',
value: 'Utah',
iso: 'UT'
},
{
label: 'Vermont',
value: 'Vermont',
iso: 'VT'
},
{
label: 'Virginia',
value: 'Virginia',
iso: 'VA'
},
{
label: 'Washington',
value: 'Washington',
iso: 'WA'
},
{
label: 'West Virginia',
value: 'West Virginia',
iso: 'WV'
},
{
label: 'Wisconsin',
value: 'Wisconsin',
iso: 'WI'
},
{
label: 'Wyoming',
value: 'Wyoming',
iso: 'WY'
}
],
},) => ({
options: comboboxData.allOptions,
isOpen: false,
openedWithKeyboard: false,
selectedOption: null,
setSelectedOption(option) {
this.selectedOption = option
this.isOpen = false
this.openedWithKeyboard = false
this.$refs.hiddenTextField.value = option.value
},
getFilteredOptions(query) {
this.options = comboboxData.allOptions.filter((option) =>
option.label.toLowerCase().includes(query.toLowerCase()) ||
option.iso.toLowerCase().includes(query.toLowerCase())
)
if (this.options.length === 0) {
this.$refs.noResultsMessage.classList.remove('hidden')
} else {
this.$refs.noResultsMessage.classList.add('hidden')
}
},
// if the user presses backspace or the alpha-numeric keys, focus on the search field
handleKeydownOnOptions(event) {
if ((event.keyCode >= 65 && event.keyCode <= 90) || (event.keyCode >= 48 && event.keyCode <= 57) || event.keyCode === 8) {
this.$refs.searchField.focus()
}
},
}))
})
</script>
Phone number input with country code dropdown
A country code selector dropdown paired with a phone input field. This component uses flagpedia.net for the flags.
- No matches found
-
x-data: Minimal vs Full
Sometimes the content of x-data can become verbose. Alpine offers a way to extract the logic from x-data into a separate object . We provide both styles and you can switch between them. Please note that even with minimal x-data , we still retain essential data (for example, slides for a carousel) and only extract the logic.
<div x-data="{
allOptions: [
{ label: 'Afghanistan', value: 'Afghanistan', iso: 'af', phoneCode: '+93' },
{ label: 'Albania', value: 'Albania', iso: 'al', phoneCode: '+355' },
{ label: 'Algeria', value: 'Algeria', iso: 'dz', phoneCode: '+213' },
{ label: 'Andorra', value: 'Andorra', iso: 'ad', phoneCode: '+376' },
{ label: 'Angola', value: 'Angola', iso: 'ao', phoneCode: '+244' },
{ label: 'Anguilla', value: 'Anguilla', iso: 'ai', phoneCode: '+1-264' },
{ label: 'Antigua and Barbuda', value: 'Antigua and Barbuda', iso: 'ag', phoneCode: '+1-268' },
{ label: 'Argentina', value: 'Argentina', iso: 'ar', phoneCode: '+54' },
{ label: 'Armenia', value: 'Armenia', iso: 'am', phoneCode: '+374' },
{ label: 'Aruba', value: 'Aruba', iso: 'aw', phoneCode: '+297' },
{ label: 'Australia', value: 'Australia', iso: 'au', phoneCode: '+61' },
{ label: 'Austria', value: 'Austria', iso: 'at', phoneCode: '+43' },
{ label: 'Azerbaijan', value: 'Azerbaijan', iso: 'az', phoneCode: '+994' },
{ label: 'Bahamas', value: 'Bahamas', iso: 'bs', phoneCode: '+1-242' },
{ label: 'Bahrain', value: 'Bahrain', iso: 'bh', phoneCode: '+973' },
{ label: 'Bangladesh', value: 'Bangladesh', iso: 'bd', phoneCode: '+880' },
{ label: 'Barbados', value: 'Barbados', iso: 'bb', phoneCode: '+1-246' },
{ label: 'Belarus', value: 'Belarus', iso: 'by', phoneCode: '+375' },
{ label: 'Belgium', value: 'Belgium', iso: 'be', phoneCode: '+32' },
{ label: 'Belize', value: 'Belize', iso: 'bz', phoneCode: '+501' },
{ label: 'Benin', value: 'Benin', iso: 'bj', phoneCode: '+229' },
{ label: 'Bermuda', value: 'Bermuda', iso: 'bm', phoneCode: '+1-441' },
{ label: 'Bhutan', value: 'Bhutan', iso: 'bt', phoneCode: '+975' },
{ label: 'Bolivia', value: 'Bolivia', iso: 'bo', phoneCode: '+591' },
{ label: 'Bosnia and Herzegovina', value: 'Bosnia and Herzegovina', iso: 'ba', phoneCode: '+387' },
{ label: 'Botswana', value: 'Botswana', iso: 'bw', phoneCode: '+267' },
{ label: 'Brazil', value: 'Brazil', iso: 'br', phoneCode: '+55' },
{ label: 'British Indian Ocean Territory', value: 'British Indian Ocean Territory', iso: 'io', phoneCode: '+246' },
{ label: 'British Virgin Islands', value: 'British Virgin Islands', iso: 'vg', phoneCode: '+1-284' },
{ label: 'Brunei', value: 'Brunei', iso: 'bn', phoneCode: '+673' },
{ label: 'Bulgaria', value: 'Bulgaria', iso: 'bg', phoneCode: '+359' },
{ label: 'Burkina Faso', value: 'Burkina Faso', iso: 'bf', phoneCode: '+226' },
{ label: 'Burundi', value: 'Burundi', iso: 'bi', phoneCode: '+257' },
{ label: 'Cambodia', value: 'Cambodia', iso: 'kh', phoneCode: '+855' },
{ label: 'Cameroon', value: 'Cameroon', iso: 'cm', phoneCode: '+237' },
{ label: 'Canada', value: 'Canada', iso: 'ca', phoneCode: '+1' },
{ label: 'Cape Verde', value: 'Cape Verde', iso: 'cv', phoneCode: '+238' },
{ label: 'Cayman Islands', value: 'Cayman Islands', iso: 'ky', phoneCode: '+1-345' },
{ label: 'Central African Republic', value: 'Central African Republic', iso: 'cf', phoneCode: '+236' },
{ label: 'Chad', value: 'Chad', iso: 'td', phoneCode: '+235' },
{ label: 'Chile', value: 'Chile', iso: 'cl', phoneCode: '+56' },
{ label: 'China', value: 'China', iso: 'cn', phoneCode: '+86' },
{ label: 'Colombia', value: 'Colombia', iso: 'co', phoneCode: '+57' },
{ label: 'Comoros', value: 'Comoros', iso: 'km', phoneCode: '+269' },
{ label: 'Congo (Brazzaville)', value: 'Congo (Brazzaville)', iso: 'cg', phoneCode: '+242' },
{ label: 'Congo (Kinshasa)', value: 'Congo (Kinshasa)', iso: 'cd', phoneCode: '+243' },
{ label: 'Cook Islands', value: 'Cook Islands', iso: 'ck', phoneCode: '+682' },
{ label: 'Costa Rica', value: 'Costa Rica', iso: 'cr', phoneCode: '+506' },
{ label: 'Croatia', value: 'Croatia', iso: 'hr', phoneCode: '+385' },
{ label: 'Cuba', value: 'Cuba', iso: 'cu', phoneCode: '+53' },
{ label: 'Curaçao', value: 'Curaçao', iso: 'cw', phoneCode: '+599' },
{ label: 'Cyprus', value: 'Cyprus', iso: 'cy', phoneCode: '+357' },
{ label: 'Czechia', value: 'Czechia', iso: 'cz', phoneCode: '+420' },
{ label: 'Denmark', value: 'Denmark', iso: 'dk', phoneCode: '+45' },
{ label: 'Djibouti', value: 'Djibouti', iso: 'dj', phoneCode: '+253' },
{ label: 'Dominica', value: 'Dominica', iso: 'dm', phoneCode: '+1-767' },
{ label: 'Dominican Republic', value: 'Dominican Republic', iso: 'do', phoneCode: '+1-809' },
{ label: 'Ecuador', value: 'Ecuador', iso: 'ec', phoneCode: '+593' },
{ label: 'Egypt', value: 'Egypt', iso: 'eg', phoneCode: '+20' },
{ label: 'El Salvador', value: 'El Salvador', iso: 'sv', phoneCode: '+503' },
{ label: 'England', value: 'England', iso: 'gb-eng', phoneCode: '+44' },
{ label: 'Equatorial Guinea', value: 'Equatorial Guinea', iso: 'gq', phoneCode: '+240' },
{ label: 'Eritrea', value: 'Eritrea', iso: 'er', phoneCode: '+291' },
{ label: 'Estonia', value: 'Estonia', iso: 'ee', phoneCode: '+372' },
{ label: 'Eswatini (Swaziland)', value: 'Eswatini (Swaziland)', iso: 'sz', phoneCode: '+268' },
{ label: 'Ethiopia', value: 'Ethiopia', iso: 'et', phoneCode: '+251' },
{ label: 'Falkland Islands (Islas Malvinas)', value: 'Falkland Islands (Islas Malvinas)', iso: 'fk', phoneCode: '+500' },
{ label: 'Faroe Islands', value: 'Faroe Islands', iso: 'fo', phoneCode: '+298' },
{ label: 'Fiji', value: 'Fiji', iso: 'fj', phoneCode: '+679' },
{ label: 'Finland', value: 'Finland', iso: 'fi', phoneCode: '+358' },
{ label: 'France', value: 'France', iso: 'fr', phoneCode: '+33' },
{ label: 'French Guiana', value: 'French Guiana', iso: 'gf', phoneCode: '+594' },
{ label: 'French Polynesia', value: 'French Polynesia', iso: 'pf', phoneCode: '+689' },
{ label: 'French Southern and Antarctic Lands', value: 'French Southern and Antarctic Lands', iso: 'tf', phoneCode: '+262' },
{ label: 'Gabon', value: 'Gabon', iso: 'ga', phoneCode: '+241' },
{ label: 'Gambia', value: 'Gambia', iso: 'gm', phoneCode: '+220' },
{ label: 'Georgia', value: 'Georgia', iso: 'ge', phoneCode: '+995' },
{ label: 'Germany', value: 'Germany', iso: 'de', phoneCode: '+49' },
{ label: 'Ghana', value: 'Ghana', iso: 'gh', phoneCode: '+233' },
{ label: 'Gibraltar', value: 'Gibraltar', iso: 'gi', phoneCode: '+350' },
{ label: 'Greece', value: 'Greece', iso: 'gr', phoneCode: '+30' },
{ label: 'Greenland', value: 'Greenland', iso: 'gl', phoneCode: '+299' },
{ label: 'Grenada', value: 'Grenada', iso: 'gd', phoneCode: '+1-473' },
{ label: 'Guadeloupe', value: 'Guadeloupe', iso: 'gp', phoneCode: '+590' },
{ label: 'Guam', value: 'Guam', iso: 'gu', phoneCode: '+1-671' },
{ label: 'Guatemala', value: 'Guatemala', iso: 'gt', phoneCode: '+502' },
{ label: 'Guinea', value: 'Guinea', iso: 'gn', phoneCode: '+224' },
{ label: 'Guinea-Bissau', value: 'Guinea-Bissau', iso: 'gw', phoneCode: '+245' },
{ label: 'Guyana', value: 'Guyana', iso: 'gy', phoneCode: '+592' },
{ label: 'Haiti', value: 'Haiti', iso: 'ht', phoneCode: '+509' },
{ label: 'Honduras', value: 'Honduras', iso: 'hn', phoneCode: '+504' },
{ label: 'Hong Kong', value: 'Hong Kong', iso: 'hk', phoneCode: '+852' },
{ label: 'Hungary', value: 'Hungary', iso: 'hu', phoneCode: '+36' },
{ label: 'Iceland', value: 'Iceland', iso: 'is', phoneCode: '+354' },
{ label: 'India', value: 'India', iso: 'in', phoneCode: '+91' },
{ label: 'Indonesia', value: 'Indonesia', iso: 'id', phoneCode: '+62' },
{ label: 'Iran', value: 'Iran', iso: 'ir', phoneCode: '+98' },
{ label: 'Iraq', value: 'Iraq', iso: 'iq', phoneCode: '+964' },
{ label: 'Ireland', value: 'Ireland', iso: 'ie', phoneCode: '+353' },
{ label: 'Israel', value: 'Israel', iso: 'il', phoneCode: '+972' },
{ label: 'Italy', value: 'Italy', iso: 'it', phoneCode: '+39' },
{ label: 'Ivory Coast', value: 'Ivory Coast', iso: 'ci', phoneCode: '+225' },
{ label: 'Jamaica', value: 'Jamaica', iso: 'jm', phoneCode: '+1-876' },
{ label: 'Japan', value: 'Japan', iso: 'jp', phoneCode: '+81' },
{ label: 'Jordan', value: 'Jordan', iso: 'jo', phoneCode: '+962' },
{ label: 'Kazakhstan', value: 'Kazakhstan', iso: 'kz', phoneCode: '+7' },
{ label: 'Kenya', value: 'Kenya', iso: 'ke', phoneCode: '+254' },
{ label: 'Kiribati', value: 'Kiribati', iso: 'ki', phoneCode: '+686' },
{ label: 'Kosovo', value: 'Kosovo', iso: 'xk', phoneCode: '+383' },
{ label: 'Kuwait', value: 'Kuwait', iso: 'kw', phoneCode: '+965' },
{ label: 'Kyrgyzstan', value: 'Kyrgyzstan', iso: 'kg', phoneCode: '+996' },
{ label: 'Laos', value: 'Laos', iso: 'la', phoneCode: '+856' },
{ label: 'Latvia', value: 'Latvia', iso: 'lv', phoneCode: '+371' },
{ label: 'Lebanon', value: 'Lebanon', iso: 'lb', phoneCode: '+961' },
{ label: 'Lesotho', value: 'Lesotho', iso: 'ls', phoneCode: '+266' },
{ label: 'Liberia', value: 'Liberia', iso: 'lr', phoneCode: '+231' },
{ label: 'Libya', value: 'Libya', iso: 'ly', phoneCode: '+218' },
{ label: 'Liechtenstein', value: 'Liechtenstein', iso: 'li', phoneCode: '+423' },
{ label: 'Lithuania', value: 'Lithuania', iso: 'lt', phoneCode: '+370' },
{ label: 'Luxembourg', value: 'Luxembourg', iso: 'lu', phoneCode: '+352' },
{ label: 'Macau', value: 'Macau', iso: 'mo', phoneCode: '+853' },
{ label: 'Madagascar', value: 'Madagascar', iso: 'mg', phoneCode: '+261' },
{ label: 'Malawi', value: 'Malawi', iso: 'mw', phoneCode: '+265' },
{ label: 'Malaysia', value: 'Malaysia', iso: 'my', phoneCode: '+60' },
{ label: 'Maldives', value: 'Maldives', iso: 'mv', phoneCode: '+960' },
{ label: 'Mali', value: 'Mali', iso: 'ml', phoneCode: '+223' },
{ label: 'Malta', value: 'Malta', iso: 'mt', phoneCode: '+356' },
{ label: 'Marshall Islands', value: 'Marshall Islands', iso: 'mh', phoneCode: '+692' },
{ label: 'Martinique', value: 'Martinique', iso: 'mq', phoneCode: '+596' },
{ label: 'Mauritania', value: 'Mauritania', iso: 'mr', phoneCode: '+222' },
{ label: 'Mauritius', value: 'Mauritius', iso: 'mu', phoneCode: '+230' },
{ label: 'Mexico', value: 'Mexico', iso: 'mx', phoneCode: '+52' },
{ label: 'Micronesia', value: 'Micronesia', iso: 'fm', phoneCode: '+691' },
{ label: 'Moldova', value: 'Moldova', iso: 'md', phoneCode: '+373' },
{ label: 'Monaco', value: 'Monaco', iso: 'mc', phoneCode: '+377' },
{ label: 'Mongolia', value: 'Mongolia', iso: 'mn', phoneCode: '+976' },
{ label: 'Montenegro', value: 'Montenegro', iso: 'me', phoneCode: '+382' },
{ label: 'Montserrat', value: 'Montserrat', iso: 'ms', phoneCode: '+1-664' },
{ label: 'Morocco', value: 'Morocco', iso: 'ma', phoneCode: '+212' },
{ label: 'Mozambique', value: 'Mozambique', iso: 'mz', phoneCode: '+258' },
{ label: 'Myanmar (Burma)', value: 'Myanmar (Burma)', iso: 'mm', phoneCode: '+95' },
{ label: 'Namibia', value: 'Namibia', iso: 'na', phoneCode: '+264' },
{ label: 'Nauru', value: 'Nauru', iso: 'nr', phoneCode: '+674' },
{ label: 'Nepal', value: 'Nepal', iso: 'np', phoneCode: '+977' },
{ label: 'Netherlands', value: 'Netherlands', iso: 'nl', phoneCode: '+31' },
{ label: 'New Caledonia', value: 'New Caledonia', iso: 'nc', phoneCode: '+687' },
{ label: 'New Zealand', value: 'New Zealand', iso: 'nz', phoneCode: '+64' },
{ label: 'Nicaragua', value: 'Nicaragua', iso: 'ni', phoneCode: '+505' },
{ label: 'Niger', value: 'Niger', iso: 'ne', phoneCode: '+227' },
{ label: 'Nigeria', value: 'Nigeria', iso: 'ng', phoneCode: '+234' },
{ label: 'Niue', value: 'Niue', iso: 'nu', phoneCode: '+683' },
{ label: 'Norfolk Island', value: 'Norfolk Island', iso: 'nf', phoneCode: '+672' },
{ label: 'North Korea', value: 'North Korea', iso: 'kp', phoneCode: '+850' },
{ label: 'North Macedonia', value: 'North Macedonia', iso: 'mk', phoneCode: '+389' },
{ label: 'Northern Ireland', value: 'Northern Ireland', iso: 'gb-nir', phoneCode: '+44' },
{ label: 'Northern Mariana Islands', value: 'Northern Mariana Islands', iso: 'mp', phoneCode: '+1-670' },
{ label: 'Norway', value: 'Norway', iso: 'no', phoneCode: '+47' },
{ label: 'Oman', value: 'Oman', iso: 'om', phoneCode: '+968' },
{ label: 'Pakistan', value: 'Pakistan', iso: 'pk', phoneCode: '+92' },
{ label: 'Palau', value: 'Palau', iso: 'pw', phoneCode: '+680' },
{ label: 'Palestine', value: 'Palestine', iso: 'ps', phoneCode: '+970' },
{ label: 'Panama', value: 'Panama', iso: 'pa', phoneCode: '+507' },
{ label: 'Papua New Guinea', value: 'Papua New Guinea', iso: 'pg', phoneCode: '+675' },
{ label: 'Paraguay', value: 'Paraguay', iso: 'py', phoneCode: '+595' },
{ label: 'Peru', value: 'Peru', iso: 'pe', phoneCode: '+51' },
{ label: 'Philippines', value: 'Philippines', iso: 'ph', phoneCode: '+63' },
{ label: 'Poland', value: 'Poland', iso: 'pl', phoneCode: '+48' },
{ label: 'Portugal', value: 'Portugal', iso: 'pt', phoneCode: '+351' },
{ label: 'Puerto Rico', value: 'Puerto Rico', iso: 'pr', phoneCode: '+1-787' },
{ label: 'Qatar', value: 'Qatar', iso: 'qa', phoneCode: '+974' },
{ label: 'Réunion', value: 'Réunion', iso: 're', phoneCode: '+262' },
{ label: 'Romania', value: 'Romania', iso: 'ro', phoneCode: '+40' },
{ label: 'Russia', value: 'Russia', iso: 'ru', phoneCode: '+7' },
{ label: 'Rwanda', value: 'Rwanda', iso: 'rw', phoneCode: '+250' },
{ label: 'Saint Barthélemy', value: 'Saint Barthélemy', iso: 'bl', phoneCode: '+590' },
{ label: 'Saint Helena', value: 'Saint Helena', iso: 'sh', phoneCode: '+290' },
{ label: 'Saint Kitts & Nevis', value: 'Saint Kitts & Nevis', iso: 'kn', phoneCode: '+1-869' },
{ label: 'Saint Lucia', value: 'Saint Lucia', iso: 'lc', phoneCode: '+1-758' },
{ label: 'Saint Martin', value: 'Saint Martin', iso: 'mf', phoneCode: '+590' },
{ label: 'Saint Pierre & Miquelon', value: 'Saint Pierre & Miquelon', iso: 'pm', phoneCode: '+508' },
{ label: 'Saint Vincent & Grenadines', value: 'Saint Vincent & Grenadines', iso: 'vc', phoneCode: '+1-784' },
{ label: 'Samoa', value: 'Samoa', iso: 'ws', phoneCode: '+685' },
{ label: 'San Marino', value: 'San Marino', iso: 'sm', phoneCode: '+378' },
{ label: 'São Tomé & Príncipe', value: 'São Tomé & Príncipe', iso: 'st', phoneCode: '+239' },
{ label: 'Saudi Arabia', value: 'Saudi Arabia', iso: 'sa', phoneCode: '+966' },
{ label: 'Senegal', value: 'Senegal', iso: 'sn', phoneCode: '+221' },
{ label: 'Serbia', value: 'Serbia', iso: 'rs', phoneCode: '+381' },
{ label: 'Seychelles', value: 'Seychelles', iso: 'sc', phoneCode: '+248' },
{ label: 'Sierra Leone', value: 'Sierra Leone', iso: 'sl', phoneCode: '+232' },
{ label: 'Singapore', value: 'Singapore', iso: 'sg', phoneCode: '+65' },
{ label: 'Sint Maarten', value: 'Sint Maarten', iso: 'sx', phoneCode: '+1-721' },
{ label: 'Slovakia', value: 'Slovakia', iso: 'sk', phoneCode: '+421' },
{ label: 'Slovenia', value: 'Slovenia', iso: 'si', phoneCode: '+386' },
{ label: 'Solomon Islands', value: 'Solomon Islands', iso: 'sb', phoneCode: '+677' },
{ label: 'Somalia', value: 'Somalia', iso: 'so', phoneCode: '+252' },
{ label: 'South Africa', value: 'South Africa', iso: 'za', phoneCode: '+27' },
{ label: 'South Korea', value: 'South Korea', iso: 'kr', phoneCode: '+82' },
{ label: 'South Sudan', value: 'South Sudan', iso: 'ss', phoneCode: '+211' },
{ label: 'Spain', value: 'Spain', iso: 'es', phoneCode: '+34' },
{ label: 'Sri Lanka', value: 'Sri Lanka', iso: 'lk', phoneCode: '+94' },
{ label: 'Sudan', value: 'Sudan', iso: 'sd', phoneCode: '+249' },
{ label: 'Suriname', value: 'Suriname', iso: 'sr', phoneCode: '+597' },
{ label: 'Sweden', value: 'Sweden', iso: 'se', phoneCode: '+46' },
{ label: 'Switzerland', value: 'Switzerland', iso: 'ch', phoneCode: '+41' },
{ label: 'Syria', value: 'Syria', iso: 'sy', phoneCode: '+963' },
{ label: 'Taiwan', value: 'Taiwan', iso: 'tw', phoneCode: '+886' },
{ label: 'Tajikistan', value: 'Tajikistan', iso: 'tj', phoneCode: '+992' },
{ label: 'Tanzania', value: 'Tanzania', iso: 'tz', phoneCode: '+255' },
{ label: 'Thailand', value: 'Thailand', iso: 'th', phoneCode: '+66' },
{ label: 'Timor-Leste', value: 'Timor-Leste', iso: 'tl', phoneCode: '+670' },
{ label: 'Togo', value: 'Togo', iso: 'tg', phoneCode: '+228' },
{ label: 'Tonga', value: 'Tonga', iso: 'to', phoneCode: '+676' },
{ label: 'Trinidad & Tobago', value: 'Trinidad & Tobago', iso: 'tt', phoneCode: '+1-868' },
{ label: 'Tunisia', value: 'Tunisia', iso: 'tn', phoneCode: '+216' },
{ label: 'Turkey', value: 'Turkey', iso: 'tr', phoneCode: '+90' },
{ label: 'Turkmenistan', value: 'Turkmenistan', iso: 'tm', phoneCode: '+993' },
{ label: 'Turks & Caicos Islands', value: 'Turks & Caicos Islands', iso: 'tc', phoneCode: '+1-649' },
{ label: 'Tuvalu', value: 'Tuvalu', iso: 'tv', phoneCode: '+688' },
{ label: 'Uganda', value: 'Uganda', iso: 'ug', phoneCode: '+256' },
{ label: 'Ukraine', value: 'Ukraine', iso: 'ua', phoneCode: '+380' },
{ label: 'United Arab Emirates', value: 'United Arab Emirates', iso: 'ae', phoneCode: '+971' },
{ label: 'United Kingdom', value: 'United Kingdom', iso: 'gb', phoneCode: '+44' },
{ label: 'United States', value: 'United States', iso: 'us', phoneCode: '+1' },
{ label: 'Uruguay', value: 'Uruguay', iso: 'uy', phoneCode: '+598' },
{ label: 'Uzbekistan', value: 'Uzbekistan', iso: 'uz', phoneCode: '+998' },
{ label: 'Vanuatu', value: 'Vanuatu', iso: 'vu', phoneCode: '+678' },
{ label: 'Vatican City', value: 'Vatican City', iso: 'va', phoneCode: '+379' },
{ label: 'Venezuela', value: 'Venezuela', iso: 've', phoneCode: '+58' },
{ label: 'Vietnam', value: 'Vietnam', iso: 'vn', phoneCode: '+84' },
{ label: 'Wallis & Futuna', value: 'Wallis & Futuna', iso: 'wf', phoneCode: '+681' },
{ label: 'Yemen', value: 'Yemen', iso: 'ye', phoneCode: '+967' },
{ label: 'Zambia', value: 'Zambia', iso: 'zm', phoneCode: '+260' },
{ label: 'Zimbabwe', value: 'Zimbabwe', iso: 'zw', phoneCode: '+263' }
],
options: [],
isOpen: true,
openedWithKeyboard: false,
selectedOption: null,
setSelectedOption(option) {
this.selectedOption = option
this.isOpen = false
this.openedWithKeyboard = false
this.$refs.phoneNumber.value = option.phoneCode + ' '
},
getFilteredOptions(query) {
this.options = this.allOptions.filter(
(option) =>
option.label.toLowerCase().includes(query.toLowerCase()) ||
option.phoneCode.includes(query.toLowerCase()),
)
if (this.options.length === 0) {
this.$refs.noResultsMessage.classList.remove('hidden')
} else {
this.$refs.noResultsMessage.classList.add('hidden')
}
},
handleKeydownOnOptions(event) {
// if the user presses backspace or the alpha-numeric keys, focus on the search field
if ((event.keyCode >= 65 && event.keyCode <= 90) || (event.keyCode >= 48 && event.keyCode <= 57) || event.keyCode === 8) {
this.$refs.searchField.focus()
}
},
}" class="">
<label for="phoneNumber" class="">Phone Number</label>
<div class="" x-on:keydown="handleKeydownOnOptions($event)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false" x-init="(options = allOptions), setSelectedOption(options[222])">
<!-- Country Selector -->
<div>
<!-- Trigger Button -->
<button type="button" class="" role="combobox" aria-controls="countriesList" aria-haspopup="listbox" x-on:click="isOpen = ! isOpen" x-on:keydown.down.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-bind:aria-expanded="isOpen || openedWithKeyboard" x-bind:aria-label="selectedOption ? selectedOption.value : 'Please Select'">
<!-- Selected Country Flag -->
<img class="" x-bind:alt="selectedOption.value" x-bind:src="'https://flagcdn.com/' + selectedOption.iso + '.svg'" />
<span class="" x-text="selectedOption.iso"></span>
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="" aria-hidden="true">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd">
</svg>
</button>
<!-- Dropdown -->
<div x-show="isOpen || openedWithKeyboard" id="countriesList" class="" role="listbox" aria-label="countries list" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" x-transition x-trap="openedWithKeyboard">
<!-- Search -->
<div class="">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="1.5" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"/>
</svg>
<input type="text" class="" name="searchField" aria-label="Search" x-on:input="getFilteredOptions($el.value)" x-ref="searchField" placeholder="Search"/>
</div>
<!-- Options -->
<ul class="">
<li class="" x-ref="noResultsMessage">
<span>No matches found</span>
</li>
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0">
<div class="">
<!-- Flag Image -->
<img class="" alt="" aria-hidden="true" x-bind:src="'https://flagcdn.com/' + item.iso + '.svg'" />
<!-- Label -->
<span x-bind:class="selectedOption == item ? 'font-bold' : null" x-text="item.value"></span>
<span class="" x-text="'(' + item.phoneCode + ')'"></span>
<!-- Screen reader 'selected' indicator -->
<span class="" x-text="selectedOption == item ? 'selected' : null"></span>
</div>
<!-- Checkmark -->
<svg x-show="selectedOption == item" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"/>
</svg>
</li>
</template>
</ul>
</div>
</div>
<input id="phoneNumber" type="tel" class="" x-ref="phoneNumber" autocomplete="phone" placeholder="+00 000-000-0000"/>
</div>
</div>
<div x-data="combobox" class="">
<label for="phoneNumber" class="">Phone Number</label>
<div class="" x-on:keydown="handleKeydownOnOptions($event)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false" x-init="setSelectedOption(options[222])">
<!-- Country Selector -->
<div>
<!-- Trigger Button -->
<button type="button" class="" role="combobox" aria-controls="countriesList" aria-haspopup="listbox" x-on:click="isOpen = ! isOpen" x-on:keydown.down.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-bind:aria-expanded="isOpen || openedWithKeyboard" x-bind:aria-label="selectedOption ? selectedOption.value : 'Please Select'">
<!-- Selected Country Flag -->
<img class="" x-bind:alt="selectedOption.value" x-bind:src="'https://flagcdn.com/' + selectedOption.iso + '.svg'" />
<span class="" x-text="selectedOption.iso"></span>
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="" aria-hidden="true">
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd">
</svg>
</button>
<!-- Dropdown -->
<div x-show="isOpen || openedWithKeyboard" id="countriesList" class="" role="listbox" aria-label="countries list" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" x-transition x-trap="openedWithKeyboard">
<!-- Search -->
<div class="">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="1.5" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"/>
</svg>
<input type="text" class="" name="searchField" aria-label="Search" x-on:input="getFilteredOptions($el.value)" x-ref="searchField" placeholder="Search"/>
</div>
<!-- Options -->
<ul class="">
<li class="" x-ref="noResultsMessage">
<span>No matches found</span>
</li>
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0">
<div class="">
<!-- Flag Image -->
<img class="" alt="" aria-hidden="true" x-bind:src="'https://flagcdn.com/' + item.iso + '.svg'" />
<!-- Label -->
<span x-bind:class="selectedOption == item ? 'font-bold' : null" x-text="item.value"></span>
<span class="" x-text="'(' + item.phoneCode + ')'"></span>
<!-- Screen reader 'selected' indicator -->
<span class="" x-text="selectedOption == item ? 'selected' : null"></span>
</div>
<!-- Checkmark -->
<svg x-show="selectedOption == item" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"/>
</svg>
</li>
</template>
</ul>
</div>
</div>
<input id="phoneNumber" type="tel" class="" x-ref="phoneNumber" autocomplete="phone" placeholder="+00 000-000-0000"/>
</div>
</div>
<script>
document.addEventListener('alpine:init', () => {
Alpine.data('combobox', (comboboxData = {
allOptions: [
{ label: 'Afghanistan', value: 'Afghanistan', iso: 'af', phoneCode: '+93' },
{ label: 'Albania', value: 'Albania', iso: 'al', phoneCode: '+355' },
{ label: 'Algeria', value: 'Algeria', iso: 'dz', phoneCode: '+213' },
{ label: 'Andorra', value: 'Andorra', iso: 'ad', phoneCode: '+376' },
{ label: 'Angola', value: 'Angola', iso: 'ao', phoneCode: '+244' },
{ label: 'Anguilla', value: 'Anguilla', iso: 'ai', phoneCode: '+1-264' },
{ label: 'Antigua and Barbuda', value: 'Antigua and Barbuda', iso: 'ag', phoneCode: '+1-268' },
{ label: 'Argentina', value: 'Argentina', iso: 'ar', phoneCode: '+54' },
{ label: 'Armenia', value: 'Armenia', iso: 'am', phoneCode: '+374' },
{ label: 'Aruba', value: 'Aruba', iso: 'aw', phoneCode: '+297' },
{ label: 'Australia', value: 'Australia', iso: 'au', phoneCode: '+61' },
{ label: 'Austria', value: 'Austria', iso: 'at', phoneCode: '+43' },
{ label: 'Azerbaijan', value: 'Azerbaijan', iso: 'az', phoneCode: '+994' },
{ label: 'Bahamas', value: 'Bahamas', iso: 'bs', phoneCode: '+1-242' },
{ label: 'Bahrain', value: 'Bahrain', iso: 'bh', phoneCode: '+973' },
{ label: 'Bangladesh', value: 'Bangladesh', iso: 'bd', phoneCode: '+880' },
{ label: 'Barbados', value: 'Barbados', iso: 'bb', phoneCode: '+1-246' },
{ label: 'Belarus', value: 'Belarus', iso: 'by', phoneCode: '+375' },
{ label: 'Belgium', value: 'Belgium', iso: 'be', phoneCode: '+32' },
{ label: 'Belize', value: 'Belize', iso: 'bz', phoneCode: '+501' },
{ label: 'Benin', value: 'Benin', iso: 'bj', phoneCode: '+229' },
{ label: 'Bermuda', value: 'Bermuda', iso: 'bm', phoneCode: '+1-441' },
{ label: 'Bhutan', value: 'Bhutan', iso: 'bt', phoneCode: '+975' },
{ label: 'Bolivia', value: 'Bolivia', iso: 'bo', phoneCode: '+591' },
{ label: 'Bosnia and Herzegovina', value: 'Bosnia and Herzegovina', iso: 'ba', phoneCode: '+387' },
{ label: 'Botswana', value: 'Botswana', iso: 'bw', phoneCode: '+267' },
{ label: 'Brazil', value: 'Brazil', iso: 'br', phoneCode: '+55' },
{ label: 'British Indian Ocean Territory', value: 'British Indian Ocean Territory', iso: 'io', phoneCode: '+246' },
{ label: 'British Virgin Islands', value: 'British Virgin Islands', iso: 'vg', phoneCode: '+1-284' },
{ label: 'Brunei', value: 'Brunei', iso: 'bn', phoneCode: '+673' },
{ label: 'Bulgaria', value: 'Bulgaria', iso: 'bg', phoneCode: '+359' },
{ label: 'Burkina Faso', value: 'Burkina Faso', iso: 'bf', phoneCode: '+226' },
{ label: 'Burundi', value: 'Burundi', iso: 'bi', phoneCode: '+257' },
{ label: 'Cambodia', value: 'Cambodia', iso: 'kh', phoneCode: '+855' },
{ label: 'Cameroon', value: 'Cameroon', iso: 'cm', phoneCode: '+237' },
{ label: 'Canada', value: 'Canada', iso: 'ca', phoneCode: '+1' },
{ label: 'Cape Verde', value: 'Cape Verde', iso: 'cv', phoneCode: '+238' },
{ label: 'Cayman Islands', value: 'Cayman Islands', iso: 'ky', phoneCode: '+1-345' },
{ label: 'Central African Republic', value: 'Central African Republic', iso: 'cf', phoneCode: '+236' },
{ label: 'Chad', value: 'Chad', iso: 'td', phoneCode: '+235' },
{ label: 'Chile', value: 'Chile', iso: 'cl', phoneCode: '+56' },
{ label: 'China', value: 'China', iso: 'cn', phoneCode: '+86' },
{ label: 'Colombia', value: 'Colombia', iso: 'co', phoneCode: '+57' },
{ label: 'Comoros', value: 'Comoros', iso: 'km', phoneCode: '+269' },
{ label: 'Congo (Brazzaville)', value: 'Congo (Brazzaville)', iso: 'cg', phoneCode: '+242' },
{ label: 'Congo (Kinshasa)', value: 'Congo (Kinshasa)', iso: 'cd', phoneCode: '+243' },
{ label: 'Cook Islands', value: 'Cook Islands', iso: 'ck', phoneCode: '+682' },
{ label: 'Costa Rica', value: 'Costa Rica', iso: 'cr', phoneCode: '+506' },
{ label: 'Croatia', value: 'Croatia', iso: 'hr', phoneCode: '+385' },
{ label: 'Cuba', value: 'Cuba', iso: 'cu', phoneCode: '+53' },
{ label: 'Curaçao', value: 'Curaçao', iso: 'cw', phoneCode: '+599' },
{ label: 'Cyprus', value: 'Cyprus', iso: 'cy', phoneCode: '+357' },
{ label: 'Czechia', value: 'Czechia', iso: 'cz', phoneCode: '+420' },
{ label: 'Denmark', value: 'Denmark', iso: 'dk', phoneCode: '+45' },
{ label: 'Djibouti', value: 'Djibouti', iso: 'dj', phoneCode: '+253' },
{ label: 'Dominica', value: 'Dominica', iso: 'dm', phoneCode: '+1-767' },
{ label: 'Dominican Republic', value: 'Dominican Republic', iso: 'do', phoneCode: '+1-809' },
{ label: 'Ecuador', value: 'Ecuador', iso: 'ec', phoneCode: '+593' },
{ label: 'Egypt', value: 'Egypt', iso: 'eg', phoneCode: '+20' },
{ label: 'El Salvador', value: 'El Salvador', iso: 'sv', phoneCode: '+503' },
{ label: 'England', value: 'England', iso: 'gb-eng', phoneCode: '+44' },
{ label: 'Equatorial Guinea', value: 'Equatorial Guinea', iso: 'gq', phoneCode: '+240' },
{ label: 'Eritrea', value: 'Eritrea', iso: 'er', phoneCode: '+291' },
{ label: 'Estonia', value: 'Estonia', iso: 'ee', phoneCode: '+372' },
{ label: 'Eswatini (Swaziland)', value: 'Eswatini (Swaziland)', iso: 'sz', phoneCode: '+268' },
{ label: 'Ethiopia', value: 'Ethiopia', iso: 'et', phoneCode: '+251' },
{ label: 'Falkland Islands (Islas Malvinas)', value: 'Falkland Islands (Islas Malvinas)', iso: 'fk', phoneCode: '+500' },
{ label: 'Faroe Islands', value: 'Faroe Islands', iso: 'fo', phoneCode: '+298' },
{ label: 'Fiji', value: 'Fiji', iso: 'fj', phoneCode: '+679' },
{ label: 'Finland', value: 'Finland', iso: 'fi', phoneCode: '+358' },
{ label: 'France', value: 'France', iso: 'fr', phoneCode: '+33' },
{ label: 'French Guiana', value: 'French Guiana', iso: 'gf', phoneCode: '+594' },
{ label: 'French Polynesia', value: 'French Polynesia', iso: 'pf', phoneCode: '+689' },
{ label: 'French Southern and Antarctic Lands', value: 'French Southern and Antarctic Lands', iso: 'tf', phoneCode: '+262' },
{ label: 'Gabon', value: 'Gabon', iso: 'ga', phoneCode: '+241' },
{ label: 'Gambia', value: 'Gambia', iso: 'gm', phoneCode: '+220' },
{ label: 'Georgia', value: 'Georgia', iso: 'ge', phoneCode: '+995' },
{ label: 'Germany', value: 'Germany', iso: 'de', phoneCode: '+49' },
{ label: 'Ghana', value: 'Ghana', iso: 'gh', phoneCode: '+233' },
{ label: 'Gibraltar', value: 'Gibraltar', iso: 'gi', phoneCode: '+350' },
{ label: 'Greece', value: 'Greece', iso: 'gr', phoneCode: '+30' },
{ label: 'Greenland', value: 'Greenland', iso: 'gl', phoneCode: '+299' },
{ label: 'Grenada', value: 'Grenada', iso: 'gd', phoneCode: '+1-473' },
{ label: 'Guadeloupe', value: 'Guadeloupe', iso: 'gp', phoneCode: '+590' },
{ label: 'Guam', value: 'Guam', iso: 'gu', phoneCode: '+1-671' },
{ label: 'Guatemala', value: 'Guatemala', iso: 'gt', phoneCode: '+502' },
{ label: 'Guinea', value: 'Guinea', iso: 'gn', phoneCode: '+224' },
{ label: 'Guinea-Bissau', value: 'Guinea-Bissau', iso: 'gw', phoneCode: '+245' },
{ label: 'Guyana', value: 'Guyana', iso: 'gy', phoneCode: '+592' },
{ label: 'Haiti', value: 'Haiti', iso: 'ht', phoneCode: '+509' },
{ label: 'Honduras', value: 'Honduras', iso: 'hn', phoneCode: '+504' },
{ label: 'Hong Kong', value: 'Hong Kong', iso: 'hk', phoneCode: '+852' },
{ label: 'Hungary', value: 'Hungary', iso: 'hu', phoneCode: '+36' },
{ label: 'Iceland', value: 'Iceland', iso: 'is', phoneCode: '+354' },
{ label: 'India', value: 'India', iso: 'in', phoneCode: '+91' },
{ label: 'Indonesia', value: 'Indonesia', iso: 'id', phoneCode: '+62' },
{ label: 'Iran', value: 'Iran', iso: 'ir', phoneCode: '+98' },
{ label: 'Iraq', value: 'Iraq', iso: 'iq', phoneCode: '+964' },
{ label: 'Ireland', value: 'Ireland', iso: 'ie', phoneCode: '+353' },
{ label: 'Israel', value: 'Israel', iso: 'il', phoneCode: '+972' },
{ label: 'Italy', value: 'Italy', iso: 'it', phoneCode: '+39' },
{ label: 'Ivory Coast', value: 'Ivory Coast', iso: 'ci', phoneCode: '+225' },
{ label: 'Jamaica', value: 'Jamaica', iso: 'jm', phoneCode: '+1-876' },
{ label: 'Japan', value: 'Japan', iso: 'jp', phoneCode: '+81' },
{ label: 'Jordan', value: 'Jordan', iso: 'jo', phoneCode: '+962' },
{ label: 'Kazakhstan', value: 'Kazakhstan', iso: 'kz', phoneCode: '+7' },
{ label: 'Kenya', value: 'Kenya', iso: 'ke', phoneCode: '+254' },
{ label: 'Kiribati', value: 'Kiribati', iso: 'ki', phoneCode: '+686' },
{ label: 'Kosovo', value: 'Kosovo', iso: 'xk', phoneCode: '+383' },
{ label: 'Kuwait', value: 'Kuwait', iso: 'kw', phoneCode: '+965' },
{ label: 'Kyrgyzstan', value: 'Kyrgyzstan', iso: 'kg', phoneCode: '+996' },
{ label: 'Laos', value: 'Laos', iso: 'la', phoneCode: '+856' },
{ label: 'Latvia', value: 'Latvia', iso: 'lv', phoneCode: '+371' },
{ label: 'Lebanon', value: 'Lebanon', iso: 'lb', phoneCode: '+961' },
{ label: 'Lesotho', value: 'Lesotho', iso: 'ls', phoneCode: '+266' },
{ label: 'Liberia', value: 'Liberia', iso: 'lr', phoneCode: '+231' },
{ label: 'Libya', value: 'Libya', iso: 'ly', phoneCode: '+218' },
{ label: 'Liechtenstein', value: 'Liechtenstein', iso: 'li', phoneCode: '+423' },
{ label: 'Lithuania', value: 'Lithuania', iso: 'lt', phoneCode: '+370' },
{ label: 'Luxembourg', value: 'Luxembourg', iso: 'lu', phoneCode: '+352' },
{ label: 'Macau', value: 'Macau', iso: 'mo', phoneCode: '+853' },
{ label: 'Madagascar', value: 'Madagascar', iso: 'mg', phoneCode: '+261' },
{ label: 'Malawi', value: 'Malawi', iso: 'mw', phoneCode: '+265' },
{ label: 'Malaysia', value: 'Malaysia', iso: 'my', phoneCode: '+60' },
{ label: 'Maldives', value: 'Maldives', iso: 'mv', phoneCode: '+960' },
{ label: 'Mali', value: 'Mali', iso: 'ml', phoneCode: '+223' },
{ label: 'Malta', value: 'Malta', iso: 'mt', phoneCode: '+356' },
{ label: 'Marshall Islands', value: 'Marshall Islands', iso: 'mh', phoneCode: '+692' },
{ label: 'Martinique', value: 'Martinique', iso: 'mq', phoneCode: '+596' },
{ label: 'Mauritania', value: 'Mauritania', iso: 'mr', phoneCode: '+222' },
{ label: 'Mauritius', value: 'Mauritius', iso: 'mu', phoneCode: '+230' },
{ label: 'Mexico', value: 'Mexico', iso: 'mx', phoneCode: '+52' },
{ label: 'Micronesia', value: 'Micronesia', iso: 'fm', phoneCode: '+691' },
{ label: 'Moldova', value: 'Moldova', iso: 'md', phoneCode: '+373' },
{ label: 'Monaco', value: 'Monaco', iso: 'mc', phoneCode: '+377' },
{ label: 'Mongolia', value: 'Mongolia', iso: 'mn', phoneCode: '+976' },
{ label: 'Montenegro', value: 'Montenegro', iso: 'me', phoneCode: '+382' },
{ label: 'Montserrat', value: 'Montserrat', iso: 'ms', phoneCode: '+1-664' },
{ label: 'Morocco', value: 'Morocco', iso: 'ma', phoneCode: '+212' },
{ label: 'Mozambique', value: 'Mozambique', iso: 'mz', phoneCode: '+258' },
{ label: 'Myanmar (Burma)', value: 'Myanmar (Burma)', iso: 'mm', phoneCode: '+95' },
{ label: 'Namibia', value: 'Namibia', iso: 'na', phoneCode: '+264' },
{ label: 'Nauru', value: 'Nauru', iso: 'nr', phoneCode: '+674' },
{ label: 'Nepal', value: 'Nepal', iso: 'np', phoneCode: '+977' },
{ label: 'Netherlands', value: 'Netherlands', iso: 'nl', phoneCode: '+31' },
{ label: 'New Caledonia', value: 'New Caledonia', iso: 'nc', phoneCode: '+687' },
{ label: 'New Zealand', value: 'New Zealand', iso: 'nz', phoneCode: '+64' },
{ label: 'Nicaragua', value: 'Nicaragua', iso: 'ni', phoneCode: '+505' },
{ label: 'Niger', value: 'Niger', iso: 'ne', phoneCode: '+227' },
{ label: 'Nigeria', value: 'Nigeria', iso: 'ng', phoneCode: '+234' },
{ label: 'Niue', value: 'Niue', iso: 'nu', phoneCode: '+683' },
{ label: 'Norfolk Island', value: 'Norfolk Island', iso: 'nf', phoneCode: '+672' },
{ label: 'North Korea', value: 'North Korea', iso: 'kp', phoneCode: '+850' },
{ label: 'North Macedonia', value: 'North Macedonia', iso: 'mk', phoneCode: '+389' },
{ label: 'Northern Ireland', value: 'Northern Ireland', iso: 'gb-nir', phoneCode: '+44' },
{ label: 'Northern Mariana Islands', value: 'Northern Mariana Islands', iso: 'mp', phoneCode: '+1-670' },
{ label: 'Norway', value: 'Norway', iso: 'no', phoneCode: '+47' },
{ label: 'Oman', value: 'Oman', iso: 'om', phoneCode: '+968' },
{ label: 'Pakistan', value: 'Pakistan', iso: 'pk', phoneCode: '+92' },
{ label: 'Palau', value: 'Palau', iso: 'pw', phoneCode: '+680' },
{ label: 'Palestine', value: 'Palestine', iso: 'ps', phoneCode: '+970' },
{ label: 'Panama', value: 'Panama', iso: 'pa', phoneCode: '+507' },
{ label: 'Papua New Guinea', value: 'Papua New Guinea', iso: 'pg', phoneCode: '+675' },
{ label: 'Paraguay', value: 'Paraguay', iso: 'py', phoneCode: '+595' },
{ label: 'Peru', value: 'Peru', iso: 'pe', phoneCode: '+51' },
{ label: 'Philippines', value: 'Philippines', iso: 'ph', phoneCode: '+63' },
{ label: 'Poland', value: 'Poland', iso: 'pl', phoneCode: '+48' },
{ label: 'Portugal', value: 'Portugal', iso: 'pt', phoneCode: '+351' },
{ label: 'Puerto Rico', value: 'Puerto Rico', iso: 'pr', phoneCode: '+1-787' },
{ label: 'Qatar', value: 'Qatar', iso: 'qa', phoneCode: '+974' },
{ label: 'Réunion', value: 'Réunion', iso: 're', phoneCode: '+262' },
{ label: 'Romania', value: 'Romania', iso: 'ro', phoneCode: '+40' },
{ label: 'Russia', value: 'Russia', iso: 'ru', phoneCode: '+7' },
{ label: 'Rwanda', value: 'Rwanda', iso: 'rw', phoneCode: '+250' },
{ label: 'Saint Barthélemy', value: 'Saint Barthélemy', iso: 'bl', phoneCode: '+590' },
{ label: 'Saint Helena', value: 'Saint Helena', iso: 'sh', phoneCode: '+290' },
{ label: 'Saint Kitts & Nevis', value: 'Saint Kitts & Nevis', iso: 'kn', phoneCode: '+1-869' },
{ label: 'Saint Lucia', value: 'Saint Lucia', iso: 'lc', phoneCode: '+1-758' },
{ label: 'Saint Martin', value: 'Saint Martin', iso: 'mf', phoneCode: '+590' },
{ label: 'Saint Pierre & Miquelon', value: 'Saint Pierre & Miquelon', iso: 'pm', phoneCode: '+508' },
{ label: 'Saint Vincent & Grenadines', value: 'Saint Vincent & Grenadines', iso: 'vc', phoneCode: '+1-784' },
{ label: 'Samoa', value: 'Samoa', iso: 'ws', phoneCode: '+685' },
{ label: 'San Marino', value: 'San Marino', iso: 'sm', phoneCode: '+378' },
{ label: 'São Tomé & Príncipe', value: 'São Tomé & Príncipe', iso: 'st', phoneCode: '+239' },
{ label: 'Saudi Arabia', value: 'Saudi Arabia', iso: 'sa', phoneCode: '+966' },
{ label: 'Senegal', value: 'Senegal', iso: 'sn', phoneCode: '+221' },
{ label: 'Serbia', value: 'Serbia', iso: 'rs', phoneCode: '+381' },
{ label: 'Seychelles', value: 'Seychelles', iso: 'sc', phoneCode: '+248' },
{ label: 'Sierra Leone', value: 'Sierra Leone', iso: 'sl', phoneCode: '+232' },
{ label: 'Singapore', value: 'Singapore', iso: 'sg', phoneCode: '+65' },
{ label: 'Sint Maarten', value: 'Sint Maarten', iso: 'sx', phoneCode: '+1-721' },
{ label: 'Slovakia', value: 'Slovakia', iso: 'sk', phoneCode: '+421' },
{ label: 'Slovenia', value: 'Slovenia', iso: 'si', phoneCode: '+386' },
{ label: 'Solomon Islands', value: 'Solomon Islands', iso: 'sb', phoneCode: '+677' },
{ label: 'Somalia', value: 'Somalia', iso: 'so', phoneCode: '+252' },
{ label: 'South Africa', value: 'South Africa', iso: 'za', phoneCode: '+27' },
{ label: 'South Korea', value: 'South Korea', iso: 'kr', phoneCode: '+82' },
{ label: 'South Sudan', value: 'South Sudan', iso: 'ss', phoneCode: '+211' },
{ label: 'Spain', value: 'Spain', iso: 'es', phoneCode: '+34' },
{ label: 'Sri Lanka', value: 'Sri Lanka', iso: 'lk', phoneCode: '+94' },
{ label: 'Sudan', value: 'Sudan', iso: 'sd', phoneCode: '+249' },
{ label: 'Suriname', value: 'Suriname', iso: 'sr', phoneCode: '+597' },
{ label: 'Sweden', value: 'Sweden', iso: 'se', phoneCode: '+46' },
{ label: 'Switzerland', value: 'Switzerland', iso: 'ch', phoneCode: '+41' },
{ label: 'Syria', value: 'Syria', iso: 'sy', phoneCode: '+963' },
{ label: 'Taiwan', value: 'Taiwan', iso: 'tw', phoneCode: '+886' },
{ label: 'Tajikistan', value: 'Tajikistan', iso: 'tj', phoneCode: '+992' },
{ label: 'Tanzania', value: 'Tanzania', iso: 'tz', phoneCode: '+255' },
{ label: 'Thailand', value: 'Thailand', iso: 'th', phoneCode: '+66' },
{ label: 'Timor-Leste', value: 'Timor-Leste', iso: 'tl', phoneCode: '+670' },
{ label: 'Togo', value: 'Togo', iso: 'tg', phoneCode: '+228' },
{ label: 'Tonga', value: 'Tonga', iso: 'to', phoneCode: '+676' },
{ label: 'Trinidad & Tobago', value: 'Trinidad & Tobago', iso: 'tt', phoneCode: '+1-868' },
{ label: 'Tunisia', value: 'Tunisia', iso: 'tn', phoneCode: '+216' },
{ label: 'Turkey', value: 'Turkey', iso: 'tr', phoneCode: '+90' },
{ label: 'Turkmenistan', value: 'Turkmenistan', iso: 'tm', phoneCode: '+993' },
{ label: 'Turks & Caicos Islands', value: 'Turks & Caicos Islands', iso: 'tc', phoneCode: '+1-649' },
{ label: 'Tuvalu', value: 'Tuvalu', iso: 'tv', phoneCode: '+688' },
{ label: 'Uganda', value: 'Uganda', iso: 'ug', phoneCode: '+256' },
{ label: 'Ukraine', value: 'Ukraine', iso: 'ua', phoneCode: '+380' },
{ label: 'United Arab Emirates', value: 'United Arab Emirates', iso: 'ae', phoneCode: '+971' },
{ label: 'United Kingdom', value: 'United Kingdom', iso: 'gb', phoneCode: '+44' },
{ label: 'United States', value: 'United States', iso: 'us', phoneCode: '+1' },
{ label: 'Uruguay', value: 'Uruguay', iso: 'uy', phoneCode: '+598' },
{ label: 'Uzbekistan', value: 'Uzbekistan', iso: 'uz', phoneCode: '+998' },
{ label: 'Vanuatu', value: 'Vanuatu', iso: 'vu', phoneCode: '+678' },
{ label: 'Vatican City', value: 'Vatican City', iso: 'va', phoneCode: '+379' },
{ label: 'Venezuela', value: 'Venezuela', iso: 've', phoneCode: '+58' },
{ label: 'Vietnam', value: 'Vietnam', iso: 'vn', phoneCode: '+84' },
{ label: 'Wallis & Futuna', value: 'Wallis & Futuna', iso: 'wf', phoneCode: '+681' },
{ label: 'Yemen', value: 'Yemen', iso: 'ye', phoneCode: '+967' },
{ label: 'Zambia', value: 'Zambia', iso: 'zm', phoneCode: '+260' },
{ label: 'Zimbabwe', value: 'Zimbabwe', iso: 'zw', phoneCode: '+263' }
],
},) => ({
options: comboboxData.allOptions,
isOpen: false,
openedWithKeyboard: false,
selectedOption: null,
setSelectedOption(option) {
this.selectedOption = option
this.isOpen = false
this.openedWithKeyboard = false
this.$refs.phoneNumber.value = option.phoneCode + ' '
},
getFilteredOptions(query) {
this.options = comboboxData.allOptions.filter((option) =>
option.label.toLowerCase().includes(query.toLowerCase()),
)
if (this.options.length === 0) {
this.$refs.noResultsMessage.classList.remove('hidden')
} else {
this.$refs.noResultsMessage.classList.add('hidden')
}
},
init() {
//Set initial value to US(+1)
this.setSelectedOption(comboboxData.allOptions[222])
},
// if the user presses backspace or the alpha-numeric keys, focus on the search field
handleKeydownOnOptions(event) {
if ((event.keyCode >= 65 && event.keyCode <= 90) || (event.keyCode >= 48 && event.keyCode <= 57) || event.keyCode === 8) {
this.$refs.searchField.focus()
}
},
}))
})
</script>
Data
List of all Alpine JS data used in this component.
Property | Description |
---|---|
options |
Array - List of all available options Array - List of all filtered options (Combobox with search) |
isOpen |
Boolean - Dropdown is open/closed
|
openedWithKeyboard |
Boolean - Dropdown has opened with keyboard
|
selectedOption |
Object - Currently selected option
|
setSelectedOption()
param:option |
Function - Sets selected option
|
highlightOptionOnKeydown()
param:firstLetter |
Function - Highlights(focus) the first option that matches the pressed keyboard key
|
For combobox with checkboxes | |
selectedOptions |
Array - Currently checked options
|
setLabelText() |
Function - Sets label text based on currently checked options
|
handleOptionToggle()
param:option |
Function - Updates the checked options list when an option is checked or unchecked
|
For combobox with search | |
allOptions |
Array - List of all available options |
getfilteredOptions(query)
param:query |
Function - Filters options based on the search query
|
handleKeydownOnOptions()
param:event |
Function - Checks pressed key and shifts focus to the search input on alphanumeric values
|
Keyboard Navigation
Key | Action |
---|---|
Tab |
Next focusable element gets the focus
|
Space |
Dropdown closed: Dropdown opens Dropdown opened: Option gets selected/(un)checked |
Dropdown closed: Dropdown opens Dropdown opened: Next option gets focus |
|
Previous option gets focus | |
Esc |
Dropdown closes
|