mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-05 17:58:55 +00:00
111 lines
4.3 KiB
TypeScript
111 lines
4.3 KiB
TypeScript
import { Fragment, useState, useEffect } from 'react'
|
|
import { Combobox, Transition } from '@headlessui/react'
|
|
import { faCheck, faChevronDown } from '@fortawesome/pro-solid-svg-icons'
|
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|
|
|
interface ListDropdownProps {
|
|
list: {[key: string] : string}[];
|
|
listDisplayKey?: string;
|
|
onSelect: (val:string)=>void
|
|
}
|
|
export const ListSearchDropdown = ({
|
|
list, listDisplayKey, onSelect
|
|
}:ListDropdownProps) => {
|
|
const [selected, setSelected] = useState(list[0])
|
|
const [query, setQuery] = useState('')
|
|
|
|
const filteredList =
|
|
query === ''
|
|
? list
|
|
: list.filter((item) =>{
|
|
if(listDisplayKey)
|
|
return item[listDisplayKey]
|
|
.toLowerCase()
|
|
.replace(/\s+/g, '')
|
|
.includes(query.toLowerCase().replace(/\s+/g, ''));
|
|
|
|
return Object.values(item)[0]
|
|
.toLowerCase()
|
|
.replace(/\s+/g, '')
|
|
.includes(query.toLowerCase().replace(/\s+/g, ''));
|
|
})
|
|
|
|
useEffect(()=>{
|
|
if (listDisplayKey) onSelect(selected[listDisplayKey]);
|
|
else onSelect(Object.values(selected)[0]);
|
|
}, [selected]);
|
|
|
|
return (
|
|
<Combobox value={selected} onChange={setSelected}>
|
|
<div className="relative mt-1">
|
|
<div className="relative w-full cursor-default rounded-md text-left shadow-md sm:text-sm">
|
|
<Combobox.Input
|
|
className="w-full border-none rounded-md py-2 pl-3 pr-10 text-sm leading-5 bg-brand-secondary text-white outline-none outline-offset-0 transition-all duration-150 ease-in-out focus:outline-brand-primary"
|
|
displayValue={(item: {[key: string] : string}) => {
|
|
if(listDisplayKey) return item[listDisplayKey];
|
|
else return Object.values(item)[0]
|
|
}}
|
|
onChange={(event) => setQuery(event.target.value)}
|
|
/>
|
|
<Combobox.Button className="absolute inset-y-0 right-0 flex items-center pr-2">
|
|
<FontAwesomeIcon icon={faChevronDown} aria-hidden="true"/>
|
|
</Combobox.Button>
|
|
</div>
|
|
<Transition
|
|
as={Fragment}
|
|
leave="transition ease-in duration-100"
|
|
leaveFrom="opacity-100"
|
|
leaveTo="opacity-0"
|
|
afterLeave={() => setQuery('')}
|
|
>
|
|
<Combobox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-brand-secondary py-1 text-base shadow-lg focus:outline-none sm:text-sm z-10">
|
|
{filteredList.length === 0 && query !== '' ? (
|
|
<div className="relative cursor-default select-none px-4 py-2 text-white">
|
|
Nothing found.
|
|
</div>
|
|
) : (
|
|
filteredList.map((item, itemIdx) => {
|
|
if(itemIdx <= 10)
|
|
return (
|
|
<Combobox.Option
|
|
key={itemIdx}
|
|
className={({ active }) =>
|
|
`relative cursor-pointer select-none py-2 pl-10 pr-4 ${
|
|
active ? 'text-white' : 'text-gray-400'
|
|
}`
|
|
}
|
|
value={item}
|
|
>
|
|
{({ selected, active }) => (
|
|
<>
|
|
<span
|
|
className={`block truncate ${
|
|
selected ? 'font-medium' : 'font-normal'
|
|
}`}
|
|
>
|
|
{!listDisplayKey && Object.values(item)[0]}
|
|
{listDisplayKey && item[listDisplayKey]}
|
|
</span>
|
|
{selected ? (
|
|
<span
|
|
className={`absolute inset-y-0 left-0 flex items-center pl-3 ${
|
|
active ? 'text-white' : 'text-teal-600'
|
|
}`}
|
|
>
|
|
<FontAwesomeIcon icon={faCheck} aria-hidden="true"/>
|
|
</span>
|
|
) : null}
|
|
</>
|
|
)}
|
|
</Combobox.Option>
|
|
);
|
|
return null;
|
|
})
|
|
)}
|
|
</Combobox.Options>
|
|
</Transition>
|
|
</div>
|
|
</Combobox>
|
|
)
|
|
}
|