// Shared UI primitives for Unique Furniture.
// Exposes Header, Footer, ProductCard, Placeholder, Icon to window so other
// Babel scripts can pull them off the global scope (Babel scripts don't share
// module scope by default).
const { useState, useEffect, useRef, useMemo } = React;
// ─────────────────────────────────────────────────────────────────────────
// Inline SVG icons. Kept minimal — no decorative iconography on cards.
// ─────────────────────────────────────────────────────────────────────────
function Icon({ name, size = 18, stroke = 1.4 }) {
const s = { width: size, height: size, fill: "none", stroke: "currentColor", strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round" };
switch (name) {
case "search":
return ;
case "user":
return ;
case "cart":
return ;
case "heart":
return ;
case "arrow-right":
return ;
case "arrow-left":
return ;
case "menu":
return ;
case "x":
return ;
case "check":
return ;
case "truck":
return ;
case "shield":
return ;
case "return":
return ;
case "instagram":
return ;
case "facebook":
return ;
case "whatsapp":
return ;
case "plus":
return ;
case "minus":
return ;
case "stamp":
return ;
default: return null;
}
}
// ─────────────────────────────────────────────────────────────────────────
// Placeholder: warm gradient block with product name, used in lieu of photos.
// ─────────────────────────────────────────────────────────────────────────
function Placeholder({ name, tone = 1, corner, dark = false, className = "" }) {
const toneClass = dark ? "ph-dark" : `ph-t${tone}`;
return (
{corner &&
{corner}
}
{name}
);
}
// ─────────────────────────────────────────────────────────────────────────
// Product card. Hover reveals quick-view bar. Click → PDP via nav callback.
// ─────────────────────────────────────────────────────────────────────────
function ProductCard({ p, onOpen, financingMode, t }) {
return (
onOpen(p.id)}>
{p.badge &&
{p.badge}
}
{t.shop.quickView}
{p.sub}
{p.name}
${p.price.toLocaleString()}
{p.was && ${p.was.toLocaleString()}}
{p.finance > 0 && financingMode !== "off" && (
{financingMode === "loud"
? ${p.finance}/mo
: <>or ${p.finance}/mo · 12 mo 0% APR>}
)}
);
}
// ─────────────────────────────────────────────────────────────────────────
// Header.
// ─────────────────────────────────────────────────────────────────────────
function Header({ route, navigate, cartCount, lang, setLang, t }) {
const navItems = [
{ key: "shop", label: t.nav.shop, to: "shop" },
{ key: "collections", label: t.nav.collections, to: "collections" },
{ key: "workshop", label: t.nav.workshop, to: "workshop" },
{ key: "about", label: t.nav.about, to: "about" },
{ key: "contact", label: t.nav.showroom, to: "contact" },
];
return (
);
}
// ─────────────────────────────────────────────────────────────────────────
// Footer.
// ─────────────────────────────────────────────────────────────────────────
function Footer({ t, navigate }) {
const f = t.footer;
return (
);
}
// ─────────────────────────────────────────────────────────────────────────
// Section header — eyebrow + display headline + optional sub + optional CTA.
// ─────────────────────────────────────────────────────────────────────────
function SectionHead({ eyebrow, headline, sub, cta, onCta }) {
return (
{eyebrow &&
{eyebrow}
}
{headline}
{(sub || cta) && (
)}
);
}
// ─────────────────────────────────────────────────────────────────────────
// Marquee strip used between sections.
// ─────────────────────────────────────────────────────────────────────────
function Marquee({ items }) {
const doubled = [...items, ...items];
return (
{doubled.map((it, i) => {it})}
);
}
Object.assign(window, { Icon, Placeholder, ProductCard, Header, Footer, SectionHead, Marquee });