/* global React */
const { useState, useEffect, useRef } = React;
/* ============================ Íconos (line) ============================ */
function Icon({ name, size = 22, stroke = 2 }) {
const p = {
width: size, height: size, viewBox: "0 0 24 24", fill: "none",
stroke: "currentColor", strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round",
};
const paths = {
home: <> >,
grid: <> >,
cart: <> >,
search: <> >,
user: <> >,
chat: <> >,
filter: <> >,
chevR: ,
chevL: ,
chevD: ,
x: <> >,
check: ,
plus: <> >,
minus: ,
truck: <> >,
store: <> >,
spark: ,
drop: ,
ruler: <> >,
info: <> >,
star: ,
phone: ,
brush: <> >,
shield: <> >,
arrowL: <> >,
pin: <> >,
card: <> >,
clock: <> >,
layers: <> >,
send: ,
};
return {paths[name] || null} ;
}
/* ============================ Botón ============================ */
function Button({ variant = "primary", size, block, icon, iconRight, children, ...rest }) {
const cls = ["btn", `btn--${variant}`, size && `btn--${size}`, block && "btn--block"].filter(Boolean).join(" ");
return (
{icon && }
{children}
{iconRight && }
);
}
/* ============================ Badge / Tag ============================ */
function Badge({ tone = "neutral", icon, children }) {
return {icon && }{children} ;
}
/* ============================ Stepper de cantidad ============================ */
function QtyStepper({ value, onChange, min = 1 }) {
return (
onChange(Math.max(min, value - 1))} aria-label="Quitar">
{value}
onChange(value + 1)} aria-label="Agregar">
);
}
const btnQty = { width: 38, height: 38, display: "grid", placeItems: "center", background: "transparent", border: "none" };
/* ============================ Swatch + Grid ============================ */
function Swatch({ color, active, onClick }) {
return (
{active && }
{color.name}
);
}
function SwatchGrid({ colors, activeHex, onSelect }) {
return (
{colors.map((c) => (
onSelect(c)} />
))}
);
}
/* ============================ Selector de presentación ============================ */
function PresentationSelector({ options, value, onChange, product }) {
return (
{options.map((p) => (
onChange(p.id)}
aria-pressed={value === p.id}
>
{p.label}
{p.unit}
))}
);
}
/* ============================ Lata "renderizada" (color real) ============================ */
function CanVisual({ hex = "#0077b6", label = "Nogopaint", sub = "" }) {
// contraste de la etiqueta segun el color
return (
);
}
function ProductMedia({ product, color, compact = false }) {
const hex = color?.hex || "#0077b6";
const sub = product.use === "exterior" ? "Exterior" : "Interior";
if (!product.image) return ;
return (
{sub}
);
}
/* ============================ Stock label ============================ */
function StockLabel({ qty }) {
if (qty === 0) return Sin stock ;
if (qty <= 6) return Últimas {qty} unidades ;
return En stock ;
}
/* ============================ Card de producto ============================ */
function ProductCard({ product, onOpen }) {
const NOGO = window.NOGO;
const firstFam = product.colors[0];
const previewColors = NOGO.palette[firstFam].slice(0, 4);
const defaultPres = product.pres[Math.min(1, product.pres.length - 1)];
const price = NOGO.priceFor(product, defaultPres);
const stock = NOGO.stockFor(product, defaultPres);
const colorCount = product.colors.reduce((n, f) => n + NOGO.palette[f].length, 0);
return (
onOpen(product)}>
{product.featured &&
Más vendido }
{product.use === "exterior" ? "Exterior" : "Interior"}
{product.name}
{product.tagline}
{previewColors.map((c) => )}
+{colorCount}
);
}
/* ============================ Toast host ============================ */
function ToastHost({ toasts }) {
return (
{toasts.map((t) => (
{t.msg}
))}
);
}
/* ============================ Sheet (bottom drawer) ============================ */
function Sheet({ title, onClose, children, footer, full }) {
return (
<>
{title && (
{title}
)}
{children}
{footer &&
{footer}
}
>
);
}
/* ============================ Field wrapper ============================ */
function Field({ label, hint, error, children }) {
return (
{label && {label} }
{children}
{error ? {error} : hint ? {hint} : null}
);
}
/* exportar a window para los demás scripts babel */
Object.assign(window, {
Icon, Button, Badge, QtyStepper, Swatch, SwatchGrid, PresentationSelector,
CanVisual, ProductMedia, StockLabel, ProductCard, ToastHost, Sheet, Field,
useState, useEffect, useRef,
});