v3/src/components/form/icon_picker.tsx

82 lines
3.3 KiB
TypeScript

import Icon from "@mdi/react";
import { useState } from "react";
import { Button } from "~/components/ui/button";
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "~/components/ui/command";
import { Popover, PopoverContent, PopoverTrigger } from "~/components/ui/popover";
import { type Icon as IconType, type IconName, icons } from "~/lib/icon";
type IconPickerProps = ({
onChange?: (icon: IconName) => void;
withNone?: false;
} | {
onChange?: (icon?: IconName) => void;
withNone: true;
}) & {
initialValue?: IconName;
className?: string;
}
// TODO: Make this faster by not showing all icons at once
export function IconPicker(props: IconPickerProps) {
const [value, setValue] = useState<IconName | undefined>(props.initialValue);
const [open, setOpen] = useState(false);
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className="h-full aspect-square"
>
{value && <Entry icon={icons[value] as IconType} />}
</Button>
</PopoverTrigger>
<PopoverContent className="w-[200px] p-0">
<Command>
<CommandInput placeholder="Search types..." />
<CommandList>
<CommandEmpty>No icon found.</CommandEmpty>
<CommandGroup>
{props.withNone && <CommandItem
value={"none"}
onSelect={() => {
setValue(undefined)
setOpen(false)
props.onChange && props.onChange(undefined)
}}>
<Entry withLabel />
</CommandItem>}
{Object.entries(icons).map(([key, value]) => (
<CommandItem
key={key}
value={key}
onSelect={(currentValue) => {
setValue(currentValue as IconName)
setOpen(false)
props.onChange && props.onChange(currentValue as IconName)
}}
>
<Entry icon={value} withLabel />
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}
function Entry(props: { icon?: IconType, withLabel?: boolean }) {
const withLabel = props.withLabel ?? false;
return (
<div className="flex flex-row w-full gap-2 items-center">
<div className="h-6 w-6">
{props.icon && <Icon path={props.icon.path} size={1} />}
</div>
{withLabel && <span>{props.icon?.name ?? "None"}</span>}
</div>
);
}