
* Added search on most pages * Changed material icons to symbols and added wikipage search * Updated deps * Changed color of the play button on previewcard overlay * Updated search params * Updated deps * Audit fix * Removed unused graphql files
102 lines
3.5 KiB
TypeScript
102 lines
3.5 KiB
TypeScript
import { Fragment, useCallback, useRef } from "react";
|
|
import { useBoolean, useOnClickOutside } from "usehooks-ts";
|
|
import { Ico } from "components/Ico";
|
|
import { cIf, cJoin } from "helpers/className";
|
|
|
|
/*
|
|
* ╭─────────────╮
|
|
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
|
*/
|
|
|
|
interface Props {
|
|
value: number;
|
|
options: string[];
|
|
selected?: number;
|
|
allowEmpty?: boolean;
|
|
className?: string;
|
|
onChange: (value: number) => void;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
|
|
|
export const Select = ({
|
|
className,
|
|
value,
|
|
options,
|
|
allowEmpty,
|
|
disabled = false,
|
|
onChange,
|
|
}: Props): JSX.Element => {
|
|
const { value: isOpened, setFalse: setClosed, toggle: toggleOpened } = useBoolean(false);
|
|
|
|
const tryToggling = useCallback(() => {
|
|
if (disabled) return;
|
|
const optionCount = options.length + (value === -1 ? 1 : 0);
|
|
if (optionCount > 1) toggleOpened();
|
|
}, [disabled, options.length, value, toggleOpened]);
|
|
|
|
const onSelectionChanged = useCallback(
|
|
(newIndex: number) => {
|
|
setClosed();
|
|
onChange(newIndex);
|
|
},
|
|
[onChange, setClosed]
|
|
);
|
|
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
useOnClickOutside(ref, setClosed);
|
|
|
|
return (
|
|
<div
|
|
ref={ref}
|
|
className={cJoin(
|
|
"relative text-center transition-filter",
|
|
cIf(isOpened, "z-10 drop-shadow-lg shadow-shade"),
|
|
className
|
|
)}>
|
|
<div
|
|
className={cJoin(
|
|
`grid cursor-pointer select-none grid-flow-col grid-cols-[1fr_auto_auto]
|
|
place-items-center rounded-3xl p-1 outline outline-1 -outline-offset-1
|
|
outline-mid`,
|
|
cIf(isOpened, "rounded-b-none bg-highlight outline-transparent"),
|
|
cIf(
|
|
disabled,
|
|
"cursor-not-allowed text-dark opacity-50 outline-dark/60 grayscale",
|
|
"transition-all hover:bg-mid hover:outline-transparent"
|
|
)
|
|
)}>
|
|
<p onClick={tryToggling} className="w-full px-4 py-1">
|
|
{value === -1 ? "—" : options[value]}
|
|
</p>
|
|
{value >= 0 && allowEmpty && (
|
|
<Ico
|
|
icon="close"
|
|
className="!text-xs"
|
|
onClick={() => !disabled && onSelectionChanged(-1)}
|
|
/>
|
|
)}
|
|
<Ico onClick={tryToggling} icon={isOpened ? "arrow_drop_up" : "arrow_drop_down"} />
|
|
</div>
|
|
<div className={cJoin("left-0 right-0 rounded-b-[1em]", cIf(isOpened, "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(isOpened, "bg-highlight", "bg-light")
|
|
)}
|
|
id={option}
|
|
onClick={() => onSelectionChanged(index)}>
|
|
{option}
|
|
</div>
|
|
)}
|
|
</Fragment>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|