100 lines
3.2 KiB
TypeScript
100 lines
3.2 KiB
TypeScript
import { Fragment, useCallback, useState } from "react";
|
|
import { Ico, Icon } from "components/Ico";
|
|
import { cIf, cJoin } from "helpers/className";
|
|
import { useToggle } from "hooks/useToggle";
|
|
|
|
/*
|
|
* ╭─────────────╮
|
|
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
|
*/
|
|
|
|
interface Props {
|
|
value: number;
|
|
options: string[];
|
|
selected?: number;
|
|
allowEmpty?: boolean;
|
|
className?: string;
|
|
onChange: (value: number) => void;
|
|
}
|
|
|
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
|
|
|
export const Select = ({
|
|
className,
|
|
value,
|
|
options,
|
|
allowEmpty,
|
|
onChange,
|
|
}: Props): JSX.Element => {
|
|
const [opened, setOpened] = useState(false);
|
|
const toggleOpened = useToggle(setOpened);
|
|
|
|
const tryToggling = useCallback(() => {
|
|
const optionCount = options.length + (value === -1 ? 1 : 0);
|
|
if (optionCount > 1) toggleOpened();
|
|
}, [options.length, value, toggleOpened]);
|
|
|
|
return (
|
|
<div
|
|
className={cJoin(
|
|
"relative text-center transition-[filter]",
|
|
cIf(opened, "z-10 drop-shadow-shade-lg"),
|
|
className
|
|
)}
|
|
>
|
|
<div
|
|
className={cJoin(
|
|
`grid cursor-pointer grid-flow-col grid-cols-[1fr_auto_auto] place-items-center
|
|
rounded-[1em] bg-light p-1 outline outline-2 outline-offset-[-2px] outline-mid
|
|
transition-all hover:bg-mid hover:outline-[transparent]`,
|
|
cIf(opened, "rounded-b-none bg-highlight outline-[transparent]")
|
|
)}
|
|
>
|
|
<p onClick={tryToggling} className="w-full">
|
|
{value === -1 ? "—" : options[value]}
|
|
</p>
|
|
{value >= 0 && allowEmpty && (
|
|
<Ico
|
|
icon={Icon.Close}
|
|
className="!text-xs"
|
|
onClick={() => {
|
|
onChange(-1);
|
|
setOpened(false);
|
|
}}
|
|
/>
|
|
)}
|
|
<Ico
|
|
onClick={tryToggling}
|
|
icon={opened ? Icon.ArrowDropUp : Icon.ArrowDropDown}
|
|
/>
|
|
</div>
|
|
<div
|
|
className={cJoin(
|
|
"left-0 right-0 rounded-b-[1em]",
|
|
cIf(opened, "absolute", "hidden")
|
|
)}
|
|
>
|
|
{options.map((option, index) => (
|
|
<Fragment key={index}>
|
|
{index !== value && (
|
|
<div
|
|
className={cJoin(
|
|
"cursor-pointer p-1 transition-colors last-of-type:rounded-b-[1em] hover:bg-mid",
|
|
cIf(opened, "bg-highlight", "bg-light")
|
|
)}
|
|
id={option}
|
|
onClick={() => {
|
|
setOpened(false);
|
|
onChange(index);
|
|
}}
|
|
>
|
|
{option}
|
|
</div>
|
|
)}
|
|
</Fragment>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|