82 lines
3.3 KiB
TypeScript
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>
|
|
);
|
|
}
|