import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { cn } from "../lib/cn"; import { ChevronDownIcon, CheckIcon, TemplateIcon } from "./icons"; import { getTemplate, groupedTemplates } from "../templates"; import { CUSTOM_TEMPLATE_ID } from "../hooks/useEditor"; import type { Template } from "../types"; interface TemplatePickerProps { currentId: string; onSelect: (template: Template) => void; } /** A small aspect-ratio swatch (max 30px on the long edge). */ function swatch(aspect: number | null) { const a = aspect ?? 1; const long = 30; const width = a >= 1 ? long : long * a; const height = a >= 1 ? long / a : long; return { width: `${width}px`, height: `${height}px` }; } export function TemplatePicker({ currentId, onSelect }: TemplatePickerProps) { const [open, setOpen] = useState(false); const [pos, setPos] = useState<{ top: number; left: number }>(); const btnRef = useRef(null); const isCustom = currentId === CUSTOM_TEMPLATE_ID; const label = isCustom ? "Custom crop" : getTemplate(currentId).name; function toggle() { const r = btnRef.current?.getBoundingClientRect(); if (r) setPos({ top: r.bottom + 8, left: r.left }); setOpen((o) => !o); } useEffect(() => { if (!open) return; const onKey = (e: KeyboardEvent) => e.key === "Escape" && setOpen(false); window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [open]); return ( <> {open && pos && createPortal( <>
setOpen(false)} aria-hidden="true" />
{groupedTemplates().map((g) => (

{g.group}

{g.items.map((t) => { const active = t.id === currentId; return ( ); })}
))}
, document.body, )} ); }