336 lines
6.5 KiB
TypeScript
336 lines
6.5 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useMemo } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Command,
|
|
CommandEmpty,
|
|
CommandGroup,
|
|
CommandInput,
|
|
CommandList,
|
|
} from "@/components/ui/command";
|
|
import {
|
|
Popover,
|
|
PopoverContent,
|
|
PopoverTrigger,
|
|
} from "@/components/ui/popover";
|
|
import { CategoryIcon } from "@/components/ui/category-icon";
|
|
import { ChevronsUpDown } from "lucide-react";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
// Group icons by category for better organization
|
|
const iconGroups: Record<string, string[]> = {
|
|
Alimentation: [
|
|
"shopping-cart",
|
|
"utensils",
|
|
"croissant",
|
|
"coffee",
|
|
"wine",
|
|
"beer",
|
|
"pizza",
|
|
"apple",
|
|
"cherry",
|
|
"salad",
|
|
"sandwich",
|
|
"ice-cream",
|
|
"cake",
|
|
"cup-soda",
|
|
"milk",
|
|
"egg",
|
|
"fish",
|
|
"beef",
|
|
],
|
|
Transport: [
|
|
"fuel",
|
|
"train",
|
|
"car",
|
|
"parking",
|
|
"bike",
|
|
"plane",
|
|
"bus",
|
|
"ship",
|
|
"sailboat",
|
|
"truck",
|
|
"car-front",
|
|
"circle-parking",
|
|
"train-front",
|
|
],
|
|
Logement: [
|
|
"home",
|
|
"zap",
|
|
"droplet",
|
|
"hammer",
|
|
"sofa",
|
|
"refrigerator",
|
|
"washing-machine",
|
|
"lamp",
|
|
"lamp-desk",
|
|
"armchair",
|
|
"bath",
|
|
"shower-head",
|
|
"door-open",
|
|
"fence",
|
|
"trees",
|
|
"flower",
|
|
"leaf",
|
|
"sun",
|
|
"snowflake",
|
|
"wind",
|
|
"thermometer",
|
|
],
|
|
Santé: [
|
|
"pill",
|
|
"stethoscope",
|
|
"hospital",
|
|
"glasses",
|
|
"dumbbell",
|
|
"sparkles",
|
|
"heart",
|
|
"heart-pulse",
|
|
"activity",
|
|
"syringe",
|
|
"bandage",
|
|
"brain",
|
|
"eye",
|
|
"ear",
|
|
"hand",
|
|
"footprints",
|
|
"person-standing",
|
|
],
|
|
Loisirs: [
|
|
"tv",
|
|
"music",
|
|
"film",
|
|
"gamepad",
|
|
"book",
|
|
"ticket",
|
|
"clapperboard",
|
|
"headphones",
|
|
"speaker",
|
|
"radio",
|
|
"camera",
|
|
"image",
|
|
"palette",
|
|
"brush",
|
|
"pen-tool",
|
|
"scissors",
|
|
"drama",
|
|
"party-popper",
|
|
],
|
|
Sport: ["trophy", "medal", "target", "volleyball"],
|
|
Shopping: [
|
|
"shirt",
|
|
"smartphone",
|
|
"package",
|
|
"shopping-bag",
|
|
"store",
|
|
"gem",
|
|
"watch",
|
|
"sunglasses",
|
|
"crown",
|
|
"laptop",
|
|
"monitor",
|
|
"keyboard",
|
|
"mouse",
|
|
"printer",
|
|
"tablet-smartphone",
|
|
"headset",
|
|
],
|
|
Services: [
|
|
"wifi",
|
|
"repeat",
|
|
"landmark",
|
|
"shield",
|
|
"receipt",
|
|
"file-text",
|
|
"mail",
|
|
"phone",
|
|
"message-square",
|
|
"send",
|
|
"globe",
|
|
"cloud",
|
|
"server",
|
|
"lock",
|
|
"unlock",
|
|
"settings",
|
|
"wrench",
|
|
],
|
|
Finance: [
|
|
"piggy-bank",
|
|
"banknote",
|
|
"wallet",
|
|
"hand-coins",
|
|
"undo",
|
|
"coins",
|
|
"credit-card",
|
|
"building",
|
|
"building2",
|
|
"trending-up",
|
|
"trending-down",
|
|
"bar-chart",
|
|
"pie-chart",
|
|
"line-chart",
|
|
"calculator",
|
|
"percent",
|
|
"dollar-sign",
|
|
"euro",
|
|
],
|
|
Voyage: [
|
|
"bed",
|
|
"luggage",
|
|
"map",
|
|
"map-pin",
|
|
"compass",
|
|
"mountain",
|
|
"tent",
|
|
"palmtree",
|
|
"umbrella",
|
|
"globe2",
|
|
"flag",
|
|
],
|
|
Famille: [
|
|
"graduation-cap",
|
|
"baby",
|
|
"paw-print",
|
|
"users",
|
|
"user",
|
|
"user-plus",
|
|
"dog",
|
|
"cat",
|
|
"bird",
|
|
"rabbit",
|
|
],
|
|
Autre: [
|
|
"heart-handshake",
|
|
"gift",
|
|
"cigarette",
|
|
"arrow-right-left",
|
|
"help-circle",
|
|
"tag",
|
|
"folder",
|
|
"key",
|
|
"star",
|
|
"bookmark",
|
|
"clock",
|
|
"calendar",
|
|
"bell",
|
|
"alert-triangle",
|
|
"info",
|
|
"check-circle",
|
|
"x-circle",
|
|
"plus",
|
|
"minus",
|
|
"search",
|
|
"trash",
|
|
"edit",
|
|
"download",
|
|
"upload",
|
|
"share",
|
|
"link",
|
|
"paperclip",
|
|
"archive",
|
|
"box",
|
|
"boxes",
|
|
"container",
|
|
"briefcase",
|
|
"education",
|
|
"award",
|
|
"lightbulb",
|
|
"flame",
|
|
"rocket",
|
|
"atom",
|
|
],
|
|
};
|
|
|
|
interface IconPickerProps {
|
|
value: string;
|
|
onChange: (icon: string) => void;
|
|
color?: string;
|
|
}
|
|
|
|
export function IconPicker({ value, onChange, color }: IconPickerProps) {
|
|
const [open, setOpen] = useState(false);
|
|
const [search, setSearch] = useState("");
|
|
|
|
// Filter icons based on search
|
|
const filteredGroups = useMemo(() => {
|
|
if (!search.trim()) return iconGroups;
|
|
|
|
const query = search.toLowerCase();
|
|
const result: Record<string, string[]> = {};
|
|
|
|
Object.entries(iconGroups).forEach(([group, icons]) => {
|
|
const filtered = icons.filter(
|
|
(icon) =>
|
|
icon.toLowerCase().includes(query) ||
|
|
group.toLowerCase().includes(query),
|
|
);
|
|
if (filtered.length > 0) {
|
|
result[group] = filtered;
|
|
}
|
|
});
|
|
|
|
return result;
|
|
}, [search]);
|
|
|
|
const handleSelect = (icon: string) => {
|
|
onChange(icon);
|
|
setOpen(false);
|
|
setSearch("");
|
|
};
|
|
|
|
return (
|
|
<Popover open={open} onOpenChange={setOpen} modal={true}>
|
|
<PopoverTrigger asChild>
|
|
<Button
|
|
variant="outline"
|
|
role="combobox"
|
|
aria-expanded={open}
|
|
className="w-full justify-between"
|
|
>
|
|
<div className="flex items-center gap-2">
|
|
<CategoryIcon icon={value} color={color} size={20} />
|
|
<span className="text-muted-foreground text-sm">{value}</span>
|
|
</div>
|
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent
|
|
className="w-[350px] p-0"
|
|
align="start"
|
|
onOpenAutoFocus={(e) => e.preventDefault()}
|
|
>
|
|
<Command>
|
|
<CommandInput
|
|
placeholder="Rechercher une icône..."
|
|
value={search}
|
|
onValueChange={setSearch}
|
|
/>
|
|
<CommandList className="max-h-[300px]">
|
|
<CommandEmpty>Aucune icône trouvée.</CommandEmpty>
|
|
{Object.entries(filteredGroups).map(([group, icons]) => (
|
|
<CommandGroup key={group} heading={group}>
|
|
<div className="grid grid-cols-6 gap-1 p-1">
|
|
{icons.map((icon) => (
|
|
<button
|
|
key={icon}
|
|
onClick={() => handleSelect(icon)}
|
|
className={cn(
|
|
"flex items-center justify-center p-2 rounded-md hover:bg-accent transition-colors",
|
|
value === icon && "bg-accent ring-2 ring-primary",
|
|
)}
|
|
title={icon}
|
|
>
|
|
<CategoryIcon icon={icon} color={color} size={20} />
|
|
</button>
|
|
))}
|
|
</div>
|
|
</CommandGroup>
|
|
))}
|
|
</CommandList>
|
|
</Command>
|
|
</PopoverContent>
|
|
</Popover>
|
|
);
|
|
}
|