Created
July 30, 2023 18:09
-
-
Save RayyanNafees/c77343a7f8fb6372b4be95fead5a41e1 to your computer and use it in GitHub Desktop.
Solidjs UnoCSS Combobox from TailwindUI (Iconify for Icons)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* @unocss-include */ | |
import { | |
onMount, | |
createSignal, | |
Show, | |
For, | |
onCleanup, | |
createEffect, | |
createMemo, | |
} from 'solid-js' | |
import type { JSX } from 'solid-js' | |
import countryList, { CountryData } from 'country-codes-list' | |
interface Country { | |
name: string | |
code: string | |
icon: JSX.Element | |
} | |
const India: Country = { | |
name: 'India', | |
code: '91', | |
icon: <span class='i-twemoji-flag-for-flag-india'></span>, | |
} | |
const countryfy = (countryArr: CountryData[]): Country[] => | |
countryArr.map((c) => ({ | |
name: c.countryNameEn, | |
icon: ( | |
<span class={'i-twemoji-flag-' + c.countryNameEn.toLowerCase()}></span> | |
), | |
code: c.countryCallingCode, | |
})) | |
const countries: Country[] = countryfy(countryList.all()) | |
export default () => { | |
const [search, setSearch] = createSignal<string>('+' + India.code) | |
const [show, setShow] = createSignal<boolean>(false) | |
const [items, setItems] = createSignal<Country[]>(countries) | |
const [selectedItem, setSelectedItem] = createSignal<{ | |
item: Country | |
idx: number | |
}>({ | |
item: India, | |
idx: 0, | |
}) | |
const handleSearch = (e: InputEvent) => { | |
setShow(true) | |
setSearch((e.target as HTMLInputElement).value) | |
setItems( | |
countries.filter((item) => | |
item.name!.toLowerCase().includes(search().toLowerCase()) | |
) | |
) | |
} | |
const handleItemClick = (item: Country, idx: number) => { | |
setSelectedItem({ item, idx }) | |
setSearch(item.code) | |
setShow(false) | |
} | |
//* Click Outside | |
let divRef: HTMLDivElement | |
onMount(() => { | |
const handleClick = (event: MouseEvent) => { | |
if (!divRef.contains(event.target as Node)) { | |
// withuot `as Node` highlights in red | |
setShow(false) | |
} | |
} | |
document.addEventListener('click', handleClick) | |
onCleanup(() => { | |
document.removeEventListener('click', handleClick) | |
}) | |
}) | |
return ( | |
<div | |
class='relative max-w-xs px-4 text-base' | |
onClick={() => setShow(true)} | |
ref={divRef!} | |
> | |
<div class='label-button flex items-center gap-1 px-2 border rounded-lg shadow-sm'> | |
{selectedItem().item!.icon} | |
<input | |
type='text' | |
placeholder='Type to search' | |
class='w-full px-2 py-2 text-gray-500 bg-transparent rounded-md outline-none' | |
onFocus={() => setShow(true)} | |
value={search()} | |
onInput={handleSearch} | |
/> | |
<Show | |
when={Boolean(search())} | |
fallback={ | |
<button onClick={() => setShow((s) => s)}> | |
<div class='i-mdi:chevron-down'></div> | |
</button> | |
} | |
> | |
<button | |
onClick={() => { | |
setSearch('') | |
setSelectedItem({ item: India, idx: 0 }) | |
// setShow(false) | |
}} | |
> | |
<div class='i-mdi:close'></div> | |
</button> | |
</Show> | |
</div> | |
<Show when={show()}> | |
<div class='relative w-full'> | |
<ul | |
class='absolute w-full mt-3 overflow-y-auto bg-white border rounded-md shadow-sm max-h-64' | |
role='listbox' | |
> | |
<li | |
id='li-alert' | |
class='hidden px-3 py-2 text-center text-gray-600' | |
> | |
Not results available | |
</li> | |
<For | |
each={items()} | |
fallback={<div class='loading loading-dots loading-xs'></div>} | |
> | |
{({ name, code, icon }, idx) => ( | |
<li | |
onClick={() => handleItemClick({ name, code, icon }, idx())} | |
role='option' | |
aria-selected={selectedItem().idx == idx()} | |
class='menu-el-js flex items-center justify-between gap-2 px-3 py-2 cursor-default duration-150 text-gray-500 hover:text-gray-900 hover:bg-indigo-50' | |
classList={{ | |
'text-indigo-600 bg-indigo-50': selectedItem().idx == idx(), | |
}} | |
> | |
<div class={'w-6 h-6 rounded-full ' + icon} /> | |
<div class='flex-1 text-left flex items-center gap-x-1'> | |
{name} | |
</div> | |
<div class='text-gray-800'>+{code}</div> | |
</li> | |
)} | |
</For> | |
</ul> | |
</div> | |
</Show> | |
</div> | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment