Files
aggios.app/front-end-agency/components/ui/CustomSelect.tsx
2025-12-29 17:23:59 -03:00

104 lines
4.9 KiB
TypeScript

"use client";
import { Fragment, useState } from "react";
import { Listbox, ListboxButton, ListboxOption, ListboxOptions, Transition } from "@headlessui/react";
import { CheckIcon, ChevronUpDownIcon } from "@heroicons/react/20/solid";
export interface SelectOption {
label: string;
value: string | number;
icon?: React.ReactNode;
color?: string; // Cor para badge/ponto
}
interface CustomSelectProps {
options: SelectOption[];
value: string | number;
onChange: (value: any) => void;
label?: string;
placeholder?: string;
className?: string;
buttonClassName?: string;
}
export default function CustomSelect({
options,
value,
onChange,
label,
placeholder = "Selecione...",
className = "",
buttonClassName = ""
}: CustomSelectProps) {
const selected = options.find((opt) => opt.value === value) || null;
return (
<div className={`w-full ${className}`}>
{label && (
<label className="block text-xs font-bold text-zinc-500 dark:text-zinc-400 uppercase tracking-wider mb-2">
{label}
</label>
)}
<Listbox value={value} onChange={onChange}>
<div className="relative">
<ListboxButton
className={`
relative w-full cursor-pointer rounded-xl bg-white dark:bg-zinc-900 py-2.5 pl-4 pr-10 text-left text-sm font-semibold transition-all border
${buttonClassName || 'border-zinc-200 dark:border-zinc-800 text-zinc-700 dark:text-zinc-300 hover:border-zinc-400'}
focus:outline-none focus:border-zinc-400 dark:focus:border-zinc-500
`}
>
<span className="flex items-center gap-2 truncate">
{selected?.color && (
<span className={`w-2 h-2 rounded-full ${selected.color}`} />
)}
{selected?.icon && <span>{selected.icon}</span>}
{selected ? selected.label : placeholder}
</span>
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<ChevronUpDownIcon className="h-5 w-5 text-zinc-400" aria-hidden="true" />
</span>
</ListboxButton>
<Transition
as={Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<ListboxOptions className="absolute z-50 mt-2 max-h-60 w-full overflow-auto rounded-xl bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 py-1 focus:outline-none sm:text-sm">
{options.map((option, idx) => (
<ListboxOption
key={idx}
className={({ active }) =>
`relative cursor-pointer select-none py-2.5 pl-10 pr-4 transition-colors ${active ? "bg-zinc-50 dark:bg-zinc-800 text-brand-600 dark:text-brand-400" : "text-zinc-700 dark:text-zinc-300"
}`
}
value={option.value}
>
{({ selected: isSelected }) => (
<>
<span className={`flex items-center gap-2 truncate ${isSelected ? "font-bold" : "font-medium"}`}>
{option.color && (
<span className={`w-2 h-2 rounded-full ${option.color}`} />
)}
{option.icon && <span>{option.icon}</span>}
{option.label}
</span>
{isSelected ? (
<span className="absolute inset-y-0 left-0 flex items-center pl-3 text-brand-600 dark:text-brand-400">
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
) : null}
</>
)}
</ListboxOption>
))}
</ListboxOptions>
</Transition>
</div>
</Listbox>
</div>
);
}