mirror of
https://github.com/StanGirard/quivr.git
synced 2024-12-15 05:31:33 +03:00
92494aa6c6
* fix: fixed clipRule typo * fix: fill-rule typo
133 lines
4.1 KiB
TypeScript
133 lines
4.1 KiB
TypeScript
/* eslint-disable max-lines */
|
|
import { BsCheckCircleFill } from "react-icons/bs";
|
|
|
|
import Popover from "@/lib/components/ui/Popover";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
export type SelectOptionProps<T> = {
|
|
label: string;
|
|
value: T;
|
|
};
|
|
|
|
type SelectProps<T> = {
|
|
options: SelectOptionProps<T>[];
|
|
value?: T;
|
|
onChange: (option: T) => void;
|
|
label?: string;
|
|
readOnly?: boolean;
|
|
className?: string;
|
|
emptyLabel?: string;
|
|
};
|
|
|
|
const selectedStyle = "rounded-lg bg-black text-white";
|
|
|
|
export const Select = <T extends string | number>({
|
|
onChange,
|
|
options,
|
|
value,
|
|
label,
|
|
readOnly = false,
|
|
className,
|
|
emptyLabel,
|
|
}: SelectProps<T>): JSX.Element => {
|
|
const selectedValueLabel = options.find(
|
|
(option) => option.value === value
|
|
)?.label;
|
|
|
|
if (readOnly) {
|
|
return (
|
|
<div className={cn("gap-2", className)}>
|
|
{label !== undefined && (
|
|
<label
|
|
id="listbox-label"
|
|
className="block text-sm font-medium leading-6 text-gray-900"
|
|
>
|
|
{label}
|
|
</label>
|
|
)}
|
|
<div className="relative">
|
|
<button
|
|
type="button"
|
|
className="relative w-full cursor-default rounded-md bg-white py-1.5 px-3 text-left text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 focus:outline-none focus:ring-2 focus:ring-indigo-500 sm:text-sm sm:leading-6"
|
|
aria-haspopup="listbox"
|
|
disabled
|
|
>
|
|
<span className="flex items-center">
|
|
<span className="mx-4 block truncate">
|
|
{selectedValueLabel ?? emptyLabel ?? "-"}
|
|
</span>
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className={cn("gap-2", className)}>
|
|
{label !== undefined && (
|
|
<label
|
|
id="listbox-label"
|
|
className="block text-sm font-medium leading-6 text-gray-900"
|
|
>
|
|
{label}
|
|
</label>
|
|
)}
|
|
<div className="relative">
|
|
<Popover
|
|
Trigger={
|
|
<button
|
|
type="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-500 sm:text-sm sm:leading-6"
|
|
aria-haspopup="listbox"
|
|
>
|
|
<span className="flex items-center">
|
|
<span className="ml-3 block truncate">
|
|
{selectedValueLabel ?? emptyLabel ?? "-"}
|
|
</span>
|
|
</span>
|
|
<span className="pointer-events-none absolute inset-y-0 right-0 ml-3 flex items-center pr-2">
|
|
<svg
|
|
className="h-5 w-5 text-gray-400"
|
|
viewBox="0 0 20 20"
|
|
fill="currentColor"
|
|
aria-hidden="true"
|
|
>
|
|
<path
|
|
fillRule="evenodd"
|
|
d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z"
|
|
clipRule="evenodd"
|
|
/>
|
|
</svg>
|
|
</span>
|
|
</button>
|
|
}
|
|
CloseTrigger={<div />}
|
|
>
|
|
<ul role="listbox">
|
|
{options.map((option) => (
|
|
<li
|
|
className="text-gray-900 relative cursor-pointer select-none py-2"
|
|
id="listbox-option-0"
|
|
key={option.value}
|
|
onClick={() => onChange(option.value)}
|
|
>
|
|
<div
|
|
className={`flex items-center px-3 py-2 ${
|
|
value === option.value && selectedStyle
|
|
}`}
|
|
>
|
|
<span className="font-bold block truncate mr-2">
|
|
{option.label}
|
|
</span>
|
|
{value === option.value && <BsCheckCircleFill />}
|
|
</div>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</Popover>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|