"use client" import { useLayoutEffect, useRef, type ComponentType, type ReactNode } from "react" import { Check, Minus, Plus } from "lucide-react" import { Icon } from "@iconify/react" import { Input } from "@/components/ui/input" import { cn } from "@/lib/utils" import type { LabelPickerVisual } from "@/lib/label-picker-visual" export type CatalogLabelPresence = "none" | "some" | "all" export type LabelPickerItemComponent = ComponentType<{ children: ReactNode onSelect?: (event: Event) => void className?: string }> export function LabelPickerLeadingVisual({ visual, }: { visual: LabelPickerVisual }) { if (visual.kind === "iconify") { return ( ) } return ( ) } function LabelPickerCheckboxVisual({ checked, }: { checked: boolean | "indeterminate" }) { return ( {checked === true ? ( ) : checked === "indeterminate" ? ( ) : null} ) } export function EmailLabelPickerBlock({ query, onQueryChange, catalogLabels, resolveLabelVisual, Item, getLabelPresence, onToggleCatalogLabel, onCreateLabel, listClassName, searchAutoFocus = true, }: { query: string onQueryChange: (v: string) => void catalogLabels: string[] resolveLabelVisual: (label: string) => LabelPickerVisual Item: LabelPickerItemComponent getLabelPresence: (label: string) => CatalogLabelPresence onToggleCatalogLabel: (label: string) => void onCreateLabel: (label: string) => void listClassName?: string /** Focus search field when the picker mounts (submenu / sheet open). */ searchAutoFocus?: boolean }) { const searchInputRef = useRef(null) useLayoutEffect(() => { if (!searchAutoFocus) return let inner = 0 const outer = requestAnimationFrame(() => { inner = requestAnimationFrame(() => { searchInputRef.current?.focus({ preventScroll: true }) }) }) return () => { cancelAnimationFrame(outer) if (inner) cancelAnimationFrame(inner) } }, [searchAutoFocus]) const q = query.trim().toLowerCase() const filtered = catalogLabels.filter( (l) => q.length === 0 || l.toLowerCase().includes(q) ) const trimmed = query.trim() const hasExact = catalogLabels.some( (l) => l.toLowerCase() === trimmed.toLowerCase() ) const canCreate = trimmed.length > 0 && !hasExact return ( <>
e.stopPropagation()} > onQueryChange(e.target.value)} placeholder="Rechercher ou créer un libellé…" aria-label="Rechercher ou créer un libellé" className="h-8 border-[#dadce0] text-sm shadow-none" autoComplete="off" onPointerDown={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()} onKeyDown={(e) => e.stopPropagation()} />
{canCreate ? ( { e.preventDefault() onCreateLabel(trimmed) }} > Créer le libellé « {trimmed} » ) : null} {filtered.map((label) => { const presence = getLabelPresence(label) const boxChecked: boolean | "indeterminate" = presence === "all" ? true : presence === "some" ? "indeterminate" : false return ( { e.preventDefault() onToggleCatalogLabel(label) }} > {label} ) })} {filtered.length === 0 && !canCreate ? (
Aucun libellé correspondant
) : null}
) }