/* =====================================================================
ui.jsx — primitivas visuales del Cotizador (Sell-U Design System).
OptionCard (single), CheckCard (multi), CompactChoice, Tooltip, Modal,
Icon, Eyebrow. Todas atan a window al final.
===================================================================== */
const { useState: useStateUI, useEffect: useEffectUI, useRef: useRefUI, useCallback: useCbUI } = React;
/* Icono Lucide por nombre — usa la fuente UMD cargada en la página. */
function Icon({ name, size = 20, color = "currentColor", strokeWidth = 1.5, style }) {
const ref = useRefUI(null);
useEffectUI(() => {
if (ref.current && window.lucide) {
ref.current.innerHTML = "";
const el = document.createElement("i");
el.setAttribute("data-lucide", name);
ref.current.appendChild(el);
try {
window.lucide.createIcons({
attrs: { width: size, height: size, "stroke-width": strokeWidth, stroke: color },
nameAttr: "data-lucide",
});
} catch (e) {}
}
}, [name, size, color, strokeWidth]);
return ;
}
/* Eyebrow label */
function Eyebrow({ children, style }) {
return (
{children}
);
}
/* Tooltip informativo accesible (para términos técnicos) */
function Tooltip({ text, label }) {
const [open, setOpen] = useStateUI(false);
return (
{open && (
{text}
)}
);
}
/* Tarjeta de opción single-select (grande). */
function OptionCard({ selected, onClick, icon, title, desc, badge, tone, density, disabled }) {
const [hover, setHover] = useStateUI(false);
const pad = density === "compact" ? "16px 16px" : density === "comfy" ? "24px 22px" : "20px 20px";
const accent = tone === "accent";
const borderColor = selected ? (accent ? "var(--accent-500)" : "var(--navy-700)") : (hover ? "var(--border-strong)" : "var(--border-default)");
return (
);
}
/* Tarjeta multi-select (checkbox). */
function CheckCard({ checked, onClick, icon, title, desc, density }) {
const [hover, setHover] = useStateUI(false);
const pad = density === "compact" ? "14px 16px" : "18px 18px";
return (
);
}
/* Grupo de opciones compactas tipo "segmented" / pills (para sub-preguntas). */
function CompactChoice({ value, options, onChange, name }) {
return (
{options.map(opt => {
const sel = value === opt.value;
return (
);
})}
);
}
/* Modal accesible con overlay. */
function Modal({ open, onClose, children, maxWidth = 520, labelledBy }) {
useEffectUI(() => {
if (!open) return;
const onKey = (e) => { if (e.key === "Escape") onClose && onClose(); };
document.addEventListener("keydown", onKey);
document.body.style.overflow = "hidden";
return () => { document.removeEventListener("keydown", onKey); document.body.style.overflow = ""; };
}, [open, onClose]);
if (!open) return null;
return (
{ if (e.target === e.currentTarget) onClose && onClose(); }}
style={{
position: "fixed", inset: 0, zIndex: 200, background: "rgba(20, 24, 42, 0.36)",
display: "flex", alignItems: "center", justifyContent: "center", padding: 20,
animation: "cotz-fade 200ms var(--ease-out)",
}}
>
{children}
);
}
Object.assign(window, { Icon, Eyebrow, Tooltip, OptionCard, CheckCard, CompactChoice, Modal });