// ui.jsx — primitives partagées (icônes UI simples, boutons, pastilles, logo) const { useState, useEffect, useRef } = React; // Jeu d'icônes : traits géométriques simples (UI, pas de l'illustration) const ICONS = { check: "M4 11l4.5 4.5L18 5", arrowR: "M5 11h12M12 6l5 5-5 5", arrowL: "M17 11H5M10 6l-5 5 5 5", plus: "M11 4v14M4 11h14", doc: "M6 3h7l4 4v12H6zM13 3v4h4", scan: "M4 7V4h3M15 4h3v3M18 15v3h-3M7 18H4v-3M7 11h8", auto: "M11 3l1.6 4.4L17 9l-4.4 1.6L11 15l-1.6-4.4L5 9l4.4-1.6z", // étincelle ~ losange flag: "M5 3v15M5 4h9l-1.5 3L14 10H5", download: "M11 4v9m0 0l-4-4m4 4l4-4M4 17h14", shield: "M11 3l6 2v5c0 4-3 6.5-6 7.5C8 16.5 5 14 5 10V5z", info: "M11 7h.01M10 10h1v5h1", building: "M5 18V5h7v13M12 9h4v9M8 8h1M8 11h1M8 14h1", chevR: "M9 6l5 5-5 5", chevD: "M6 9l5 5 5-5", upload: "M11 16V7m0 0L7 11m4-4l4 4M4 4h14", edit: "M13 4l4 4M4 17l1-4 9-9 3 3-9 9z", user: "M11 11a3.2 3.2 0 100-6.4A3.2 3.2 0 0011 11zM4.5 18a6.5 6.5 0 0113 0", lock: "M6 10V7a5 5 0 0110 0v3M5 10h12v8H5z", clock: "M11 5v6l4 2M11 19a8 8 0 100-16 8 8 0 000 16z", search: "M10 16a6 6 0 100-12 6 6 0 000 12zM15 15l4 4", }; function Icon({ name, size = 22, className = "", strokeWidth = 1.6 }) { const d = ICONS[name]; return ( ); } // Marque : losange + feuille stylisée minimale (formes simples) function Logo({ size = 30 }) { return ( Grant4me ); } function Btn({ children, kind = "primary", icon, iconAfter, onClick, type = "button", full, disabled, size }) { return ( ); } // Pastille de statut (champs + demandes) const STATUS_META = { auto: { label: "Rempli pour vous", tone: "green", icon: "check" }, confirm: { label: "À confirmer", tone: "gold", icon: "flag" }, fourni: { label: "Fourni", tone: "green", icon: "check" }, a_fournir: { label: "À fournir", tone: "gold", icon: "flag" }, a_completer: { label: "À compléter", tone: "gold", icon: "edit" }, soumis: { label: "Soumis", tone: "ink", icon: "check" }, brouillon: { label: "Brouillon", tone: "muted", icon: "edit" }, }; function Pill({ status, label, tone, icon, small }) { const m = STATUS_META[status] || {}; const t = tone || m.tone || "muted"; return ( {(icon || m.icon) && } {label || (m.label && I18N.t(m.label))} ); } // Guide « où trouver cette information » — clé de l'accompagnement PME. // guide = { titre, t } OU children libres. function HelpNote({ children, guide, label }) { const [open, setOpen] = useState(false); return ( {open && ( {guide ? ( <> {guide.titre} {guide.t} ) : children} )} ); } // Autocomplete contre un référentiel (lookup_referentiel) — ex. CAE codes. // search(q) -> [{code, libelle}] ; onPick(match) remplit code+libellé. function Autocomplete({ value, onChange, onPick, search, placeholder, ariaLabel, footHint }) { const [open, setOpen] = useState(false); const [hl, setHl] = useState(0); const q = (value || "").trim(); const matches = open && q ? search(q) : []; const pick = (m) => { onPick(m); setOpen(false); }; const onKey = (e) => { if (!open || !matches.length) return; if (e.key === "ArrowDown") { e.preventDefault(); setHl((h) => Math.min(h + 1, matches.length - 1)); } else if (e.key === "ArrowUp") { e.preventDefault(); setHl((h) => Math.max(h - 1, 0)); } else if (e.key === "Enter") { e.preventDefault(); pick(matches[hl]); } else if (e.key === "Escape") { setOpen(false); } }; return ( 0} onChange={(e) => { onChange(e.target.value); setOpen(true); setHl(0); }} onFocus={() => setOpen(true)} onKeyDown={onKey} onBlur={() => setTimeout(() => setOpen(false), 130)} /> {open && q && ( {matches.length ? matches.map((m, i) => ( { e.preventDefault(); pick(m); }} onMouseEnter={() => setHl(i)}> {m.code} {m.libelle} )) : {I18N.t("Aucun résultat pour « {q} »", { q })}} {footHint && matches.length > 0 && {footHint}} )} ); } function FormPagePlaceholder({ label = "page du formulaire", filled = 0 }) { return (
{/* L'aperçu de page ne nomme plus un programme précis : le tampon reprend le mot du domaine actif. */}
{I18N.L("formulaire").toUpperCase()}
{Array.from({ length: 9 }).map((_, i) => (
))}
{label}
); } Object.assign(window, { Icon, Logo, Btn, Pill, HelpNote, Autocomplete, FormPagePlaceholder, STATUS_META });