penguinui is now in the root dir in penguinui-components/

This commit is contained in:
Priec
2026-06-18 10:21:29 +02:00
parent 0a619517b6
commit ee944ed5ce
181 changed files with 7699 additions and 1 deletions

View File

@@ -0,0 +1,129 @@
<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="w-full max-w-xs flex flex-col gap-1" x-on:keydown="highlightFirstMatchingOption($event.key)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false">
<label for="skills" class="w-fit pl-0.5 text-sm text-on-surface dark:text-on-surface-dark">Skills(s)</label>
<div class="relative">
<!-- trigger button -->
<button type="button" role="combobox" class="inline-flex w-full items-center justify-between gap-2 whitespace-nowrap border-outline bg-surface-alt px-4 py-2 text-sm font-medium capitalize tracking-wide text-on-surface transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:text-on-surface-dark dark:focus-visible:outline-primary-dark border rounded-radius" 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="text-sm w-full font-normal text-start overflow-hidden text-ellipsis whitespace-nowrap" x-text="setLabelText()"></span>
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5">
<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="absolute z-10 left-0 top-11 flex max-h-44 w-full flex-col overflow-hidden overflow-y-auto border-outline bg-surface-alt py-1.5 dark:border-outline-dark dark:bg-surface-dark-alt border rounded-radius" 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="flex items-center gap-2 px-4 py-3 text-sm font-medium text-on-surface hover:bg-surface-dark/5 has-focus:bg-surface-dark/5 dark:text-on-surface-dark dark:hover:bg-surface/5 dark:has-focus:bg-surface/5 has-checked:text-on-surface-strong dark:has-checked:text-on-surface-dark-strong has-disabled:cursor-not-allowed has-disabled:opacity-75" x-bind:for="'checkboxOption' + index">
<div class="relative flex items-center">
<input type="checkbox" class="combobox-option before:content[''] peer relative size-4 appearance-none overflow-hidden border border-outline bg-surface-alt before:absolute before:inset-0 checked:border-primary checked:before:bg-primary focus:outline-2 focus:outline-offset-2 focus:outline-outline-strong checked:focus:outline-primary active:outline-offset-0 disabled:cursor-not-allowed dark:border-outline-dark rounded-sm dark:bg-surface-dark-alt dark:checked:border-primary-dark dark:checked:before:bg-primary-dark dark:focus:outline-outline-dark-strong dark:checked:focus:outline-primary-dark" 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="pointer-events-none invisible absolute left-1/2 top-1/2 size-3 -translate-x-1/2 -translate-y-1/2 text-on-primary peer-checked:visible dark:text-on-primary-dark" 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>

View File

@@ -0,0 +1,115 @@
<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="w-full max-w-xs flex flex-col gap-1" x-on:keydown="highlightFirstMatchingOption($event.key)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false">
<label for="user" class="w-fit pl-0.5 text-sm text-on-surface dark:text-on-surface-dark">Share with</label>
<div class="relative">
<!-- trigger button -->
<button type="button" role="combobox" class="inline-flex w-full items-center justify-between gap-2 whitespace-nowrap border-outline bg-surface-alt px-4 py-2 text-sm font-medium capitalize tracking-wide text-on-surface transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:text-on-surface-dark dark:focus-visible:outline-primary-dark rounded-radius border" 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="text-sm font-normal" 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="size-5">
<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="absolute z-10 left-0 top-11 flex max-h-44 w-full flex-col overflow-hidden overflow-y-auto border-outline bg-surface-alt py-1.5 dark:border-outline-dark dark:bg-surface-dark-alt rounded-radius border" 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="combobox-option inline-flex justify-between items-center gap-6 bg-surface-alt px-4 py-2 text-sm text-on-surface hover:bg-surface-dark-alt/5 hover:text-on-surface-strong focus-visible:bg-surface-dark-alt/5 focus-visible:text-on-surface-strong focus-visible:outline-hidden dark:bg-surface-dark-alt dark:text-on-surface-dark dark:hover:bg-surface-alt/5 dark:hover:text-on-surface-dark-strong dark:focus-visible:bg-surface-alt/10 dark:focus-visible:text-on-surface-dark-strong" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0" >
<div class="flex items-center gap-2">
<img class="size-8 rounded-full" x-bind:src="item.img" alt="" aria-hidden="true"/>
<!-- Label -->
<div class="flex flex-col">
<span x-bind:class="selectedOption == item ? 'font-bold' : null" x-text="item.label"></span>
<span class="text-xs"x-text="item.email"></span>
<!-- Screen reader 'selected' indicator -->
<span class="sr-only" 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="size-4" 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>

View File

@@ -0,0 +1,259 @@
<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="flex w-full max-w-xs flex-col gap-1" x-on:keydown="handleKeydownOnOptions($event)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false" x-init="options = allOptions">
<label for="make" class="w-fit pl-0.5 text-sm text-on-surface dark:text-on-surface-dark">Make</label>
<div class="relative">
<!-- trigger button -->
<button type="button" class="inline-flex w-full items-center justify-between gap-2 border border-outline rounded-radius bg-surface-alt px-4 py-2 text-sm font-medium tracking-wide text-on-surface transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:text-on-surface-dark dark:focus-visible:outline-primary-dark" 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="text-sm font-normal" 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="size-5" 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="absolute left-0 top-11 z-10 w-full overflow-hidden rounded-radius border border-outline bg-surface-alt dark:border-outline-dark dark:bg-surface-dark-alt" 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="relative">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="1.5" class="absolute left-4 top-1/2 size-5 -translate-y-1/2 text-on-surface/50 dark:text-on-surface-dark/50" 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="w-full border-b border-outline bg-surface-alt py-2.5 pl-11 pr-4 text-sm text-on-surface focus:outline-hidden focus-visible:border-primary disabled:cursor-not-allowed disabled:opacity-75 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark dark:focus-visible:border-primary-dark" name="searchField" aria-label="Search" x-on:input="getFilteredOptions($el.value)" x-ref="searchField" placeholder="Search" />
</div>
<!-- Options -->
<ul class="flex max-h-44 flex-col overflow-y-auto">
<li class="hidden px-4 py-2 text-sm text-on-surface dark:text-on-surface-dark" x-ref="noResultsMessage">
<span>No matches found</span>
</li>
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="combobox-option inline-flex justify-between gap-6 bg-surface-alt px-4 py-2 text-sm text-on-surface hover:bg-surface-dark-alt/5 hover:text-on-surface-strong focus-visible:bg-surface-dark-alt/5 focus-visible:text-on-surface-strong focus-visible:outline-hidden dark:bg-surface-dark-alt dark:text-on-surface-dark dark:hover:bg-surface-alt/5 dark:hover:text-on-surface-dark-strong dark:focus-visible:bg-surface-alt/10 dark:focus-visible:text-on-surface-dark-strong" 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="sr-only" 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="size-4" 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>

View File

@@ -0,0 +1,318 @@
<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="flex w-full max-w-xs flex-col gap-1" x-on:keydown="handleKeydownOnOptions($event)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false" x-init="options = allOptions">
<label for="country" class="w-fit pl-0.5 text-sm text-on-surface dark:text-on-surface-dark">Country</label>
<div class="relative">
<!-- trigger button -->
<button type="button" class="inline-flex w-full items-center justify-between gap-2 border border-outline rounded-radius bg-surface-alt px-4 py-2 text-sm font-medium tracking-wide text-on-surface transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:text-on-surface-dark dark:focus-visible:outline-primary-dark" 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="text-sm font-normal" 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="size-5" 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="absolute left-0 top-11 z-10 w-full overflow-hidden rounded-radius border border-outline bg-surface-alt dark:border-outline-dark dark:bg-surface-dark-alt" 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="relative">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="1.5" class="absolute left-4 top-1/2 size-5 -translate-y-1/2 text-on-surface/50 dark:text-on-surface-dark/50" 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="w-full border-b border-outline bg-surface-alt py-2.5 pl-11 pr-4 text-sm text-on-surface focus:outline-hidden focus-visible:border-primary disabled:cursor-not-allowed disabled:opacity-75 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark dark:focus-visible:border-primary-dark" name="searchField" aria-label="Search" x-on:input="getFilteredOptions($el.value)" x-ref="searchField" placeholder="Search" />
</div>
<!-- Options -->
<ul class="flex max-h-44 flex-col overflow-y-auto">
<li class="hidden px-4 py-2 text-sm text-on-surface dark:text-on-surface-dark" x-ref="noResultsMessage">
<span>No matches found</span>
</li>
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="combobox-option inline-flex justify-between gap-6 bg-surface-alt px-4 py-2 text-sm text-on-surface hover:bg-surface-dark-alt/5 hover:text-on-surface-strong focus-visible:bg-surface-dark-alt/5 focus-visible:text-on-surface-strong focus-visible:outline-hidden dark:bg-surface-dark-alt dark:text-on-surface-dark dark:hover:bg-surface-alt/5 dark:hover:text-on-surface-dark-strong dark:focus-visible:bg-surface-alt/10 dark:focus-visible:text-on-surface-dark-strong" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0">
<div class="flex items-center gap-2">
<!-- 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="sr-only" 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="size-4" 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>

View File

@@ -0,0 +1,317 @@
<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="flex w-full max-w-xs flex-col gap-1">
<label for="phoneNumber" class="w-fit pl-0.5 text-sm text-on-surface dark:text-on-surface-dark">Phone Number</label>
<div class="relative flex w-full" 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="inline-flex w-max items-center justify-between gap-2 whitespace-nowrap rounded-l-radius border border-outline border-r-0 bg-surface-alt px-4 py-2 text-sm font-medium tracking-wide text-on-surface transition hover:opacity-75 focus-visible:outline-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:text-on-surface-dark dark:focus-visible:outline-primary-dark" 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="h-3.5 w-5" x-bind:alt="selectedOption.value" x-bind:src="'https://flagcdn.com/' + selectedOption.iso + '.svg'" />
<!-- Chevron -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5 shrink-0" 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="absolute left-0 top-11 z-10 w-full overflow-hidden rounded-radius border border-outline bg-surface-alt dark:border-outline-dark dark:bg-surface-dark-alt" 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="relative">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="1.5" class="absolute left-4 top-1/2 size-5 -translate-y-1/2 text-on-surface/50 dark:text-on-surface-dark/50" 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="w-full border-b border-outline bg-surface-alt py-2.5 pl-11 pr-4 text-sm text-on-surface focus:outline-hidden focus-visible:border-primary disabled:cursor-not-allowed disabled:opacity-75 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark dark:focus-visible:border-primary-dark" name="searchField" aria-label="Search" x-on:input="getFilteredOptions($el.value)" x-ref="searchField" placeholder="Search"/>
</div>
<!-- Options -->
<ul class="flex max-h-44 flex-col overflow-y-auto">
<li class="hidden px-4 py-2 text-sm text-on-surface dark:text-on-surface-dark" x-ref="noResultsMessage">
<span>No matches found</span>
</li>
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="inline-flex justify-between gap-6 bg-surface-alt px-4 py-2 text-sm text-on-surface hover:bg-surface-dark-alt/5 hover:text-on-surface-strong focus-visible:bg-surface-dark-alt/5 focus-visible:text-on-surface-strong focus-visible:outline-hidden dark:bg-surface-dark-alt dark:text-on-surface-dark dark:hover:bg-surface-alt/5 dark:hover:text-on-surface-dark-strong dark:focus-visible:bg-surface-alt/10 dark:focus-visible:text-on-surface-dark-strong" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0">
<div class="flex items-center gap-2">
<!-- Flag Image -->
<img class="h-3.5 w-5" 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="font-bold" x-text="'(' + item.phoneCode + ')'"></span>
<!-- Screen reader 'selected' indicator -->
<span class="sr-only" 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="size-4" 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="w-full border-outline rounded-r-radius border rounded-l-none bg-surface-alt px-2.5 py-2 text-sm text-on-surface focus:outline-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:cursor-not-allowed disabled:opacity-75 dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:text-on-surface-dark dark:focus-visible:outline-primary-dark" x-ref="phoneNumber" autocomplete="phone" placeholder="+00 000-000-0000"/>
</div>
</div>

View File

@@ -0,0 +1,107 @@
<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="w-full max-w-xs flex flex-col gap-1" x-on:keydown="highlightFirstMatchingOption($event.key)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false">
<label for="industry" class="w-fit pl-0.5 text-sm text-on-surface dark:text-on-surface-dark">Industry</label>
<div class="relative">
<!-- trigger button -->
<button type="button" role="combobox" class="inline-flex w-full items-center justify-between gap-2 whitespace-nowrap border-outline bg-surface-alt px-4 py-2 text-sm font-medium capitalize tracking-wide text-on-surface transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:text-on-surface-dark dark:focus-visible:outline-primary-dark rounded-radius border" 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="text-sm font-normal" 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="size-5">
<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="absolute z-10 left-0 top-11 flex max-h-44 w-full flex-col overflow-hidden overflow-y-auto border-outline bg-surface-alt py-1.5 dark:border-outline-dark dark:bg-surface-dark-alt rounded-radius border" 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="combobox-option inline-flex justify-between gap-6 bg-surface-alt px-4 py-2 text-sm text-on-surface hover:bg-surface-dark-alt/5 hover:text-on-surface-strong focus-visible:bg-surface-dark-alt/5 focus-visible:text-on-surface-strong focus-visible:outline-hidden dark:bg-surface-dark-alt dark:text-on-surface-dark dark:hover:bg-surface-alt/5 dark:hover:text-on-surface-dark-strong dark:focus-visible:bg-surface-alt/10 dark:focus-visible:text-on-surface-dark-strong" 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="sr-only" 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="size-4" 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>

View File

@@ -0,0 +1,330 @@
<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="flex w-full max-w-xs flex-col gap-1" x-on:keydown="handleKeydownOnOptions($event)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false" x-init="options = allOptions">
<label for="state" class="w-fit pl-0.5 text-sm text-on-surface dark:text-on-surface-dark">State</label>
<div class="relative">
<!-- trigger button -->
<button type="button" class="inline-flex w-full items-center justify-between gap-2 border border-outline rounded-radius bg-surface-alt px-4 py-2 text-sm font-medium tracking-wide text-on-surface transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary dark:border-outline-dark dark:bg-surface-dark-alt/50 dark:text-on-surface-dark dark:focus-visible:outline-primary-dark" 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="text-sm font-normal" 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="size-5" 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="absolute left-0 top-11 z-10 w-full overflow-hidden rounded-radius border border-outline bg-surface-alt dark:border-outline-dark dark:bg-surface-dark-alt" 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="relative">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="1.5" class="absolute left-4 top-1/2 size-5 -translate-y-1/2 text-on-surface dark:text-on-surface-dark" 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="w-full border-b border-outline bg-surface-alt py-2.5 pl-11 pr-4 text-sm text-on-surface focus:outline-hidden focus-visible:border-primary disabled:cursor-not-allowed disabled:opacity-75 dark:border-outline-dark dark:bg-surface-dark-alt dark:text-on-surface-dark dark:focus-visible:border-primary-dark" name="searchField" aria-label="Search" x-on:input="getFilteredOptions($el.value)" x-ref="searchField" placeholder="Search" />
</div>
<!-- Options -->
<ul class="flex max-h-44 flex-col overflow-y-auto">
<li class="hidden px-4 py-2 text-sm text-on-surface dark:text-on-surface-dark" x-ref="noResultsMessage">
<span>No matches found</span>
</li>
<template x-for="(item, index) in options" x-bind:key="item.value">
<li class="combobox-option inline-flex justify-between gap-6 bg-surface-alt px-4 py-2 text-sm text-on-surface hover:bg-surface-dark-alt/5 hover:text-on-surface-strong focus-visible:bg-surface-dark-alt/5 focus-visible:text-on-surface-strong focus-visible:outline-hidden dark:bg-surface-dark-alt dark:text-on-surface-dark dark:hover:bg-surface-alt/5 dark:hover:text-on-surface-dark-strong dark:focus-visible:bg-surface-alt/10 dark:focus-visible:text-on-surface-dark-strong" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0">
<div class="flex items-center gap-2">
<!-- 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="sr-only" 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="size-4" 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>