105 lines
3.7 KiB
TypeScript
105 lines
3.7 KiB
TypeScript
import { Fragment, useEffect, useState } from 'react'
|
|
|
|
import { Listbox, Transition } from '@headlessui/react'
|
|
import { CheckIcon, ChevronUpDownIcon } from '@heroicons/react/20/solid'
|
|
|
|
import { Model } from '@janhq/core'
|
|
import { atom, useSetAtom } from 'jotai'
|
|
import { twMerge } from 'tailwind-merge'
|
|
|
|
import { getDownloadedModels } from '@/hooks/useGetDownloadedModels'
|
|
|
|
export const selectedModelAtom = atom<Model | undefined>(undefined)
|
|
|
|
export default function DropdownListSidebar() {
|
|
const [downloadedModels, setDownloadedModels] = useState<Model[]>([])
|
|
const [selected, setSelected] = useState<Model | undefined>()
|
|
const setSelectedModel = useSetAtom(selectedModelAtom)
|
|
|
|
useEffect(() => {
|
|
getDownloadedModels().then((downloadedModels) => {
|
|
setDownloadedModels(downloadedModels)
|
|
|
|
if (downloadedModels.length > 0) {
|
|
setSelected(downloadedModels[0])
|
|
setSelectedModel(downloadedModels[0])
|
|
}
|
|
})
|
|
}, [])
|
|
|
|
if (!selected) return null
|
|
|
|
return (
|
|
<Listbox
|
|
value={selected}
|
|
onChange={(model) => {
|
|
setSelected(model)
|
|
setSelectedModel(model)
|
|
}}
|
|
>
|
|
{({ open }) => (
|
|
<>
|
|
<div className="relative mt-2">
|
|
<Listbox.Button className="relative w-full cursor-default rounded-md bg-white py-1.5 pl-3 pr-10 text-left text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-600 sm:text-sm sm:leading-6">
|
|
<span className="block truncate">{selected.name}</span>
|
|
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
|
|
<ChevronUpDownIcon
|
|
className="h-5 w-5 text-gray-400"
|
|
aria-hidden="true"
|
|
/>
|
|
</span>
|
|
</Listbox.Button>
|
|
|
|
<Transition
|
|
show={open}
|
|
as={Fragment}
|
|
leave="transition ease-in duration-100"
|
|
leaveFrom="opacity-100"
|
|
leaveTo="opacity-0"
|
|
>
|
|
<Listbox.Options className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
|
|
{downloadedModels.map((model) => (
|
|
<Listbox.Option
|
|
key={model.id}
|
|
className={({ active }) =>
|
|
twMerge(
|
|
active ? 'bg-indigo-600 text-white' : 'text-gray-900',
|
|
'relative cursor-default select-none py-2 pl-3 pr-9'
|
|
)
|
|
}
|
|
value={model}
|
|
>
|
|
{({ selected, active }) => (
|
|
<>
|
|
<span
|
|
className={twMerge(
|
|
selected ? 'font-semibold' : 'font-normal',
|
|
'block truncate'
|
|
)}
|
|
>
|
|
{model.name}
|
|
</span>
|
|
|
|
{selected ? (
|
|
<span
|
|
className={twMerge(
|
|
active ? 'text-white' : 'text-indigo-600',
|
|
'absolute inset-y-0 right-0 flex items-center pr-4'
|
|
)}
|
|
>
|
|
<CheckIcon className="h-5 w-5" aria-hidden="true" />
|
|
</span>
|
|
) : null}
|
|
</>
|
|
)}
|
|
</Listbox.Option>
|
|
))}
|
|
</Listbox.Options>
|
|
</Transition>
|
|
</div>
|
|
</>
|
|
)}
|
|
</Listbox>
|
|
)
|
|
}
|