// ui.jsx — shared UI primitives for NOVA Résidence // Loaded BEFORE app.jsx so its components and styles are available. // ────────────────────────────────────────────────────────────────────────── // PLACEHOLDER (striped image stand-in) // ────────────────────────────────────────────────────────────────────────── function Placeholder({ label, ratio = "16/9", tone = "warm", radius = 0, style = {}, children, src }) { const theme = window.useTheme ? window.useTheme() : null; const showLabel = theme ? theme.t.showPlaceholderLabels : true; const accent = theme ? theme.palette.accent : "#C5A572"; const ink = theme ? theme.palette.ink : "#1F2A33"; const bg = tone === "dark" ? (theme ? theme.palette.deep : "#1F2A33") : tone === "ink" ? ink : (theme ? theme.palette.bg2 : "#EBE6DA"); const stripeColor = tone === "dark" || tone === "ink" ? "rgba(255,255,255,0.06)" : "rgba(31,42,51,0.06)"; // If a real image src is provided, render it; placeholder label only shown if no src OR theme.showPlaceholderLabels is true AND we want overlay. return (
{src && ( {label )} {!src && showLabel && (
{label || "image"}
)} {children}
); } // ────────────────────────────────────────────────────────────────────────── // LOGO — wordmark + monogram (text-based since no logo file provided yet) // ────────────────────────────────────────────────────────────────────────── function Logo({ size = 32, variant = "full", dark = false, onClick }) { // Use the horizontal brand mark when full, the round monogram when compact const src = variant === "mark" ? (dark ? "assets/logo-mono-dore.png" : "assets/logo-rond.png") : (dark ? "assets/logo-fond-sombre.png" : "assets/logo-horizontal.png"); const h = variant === "mark" ? size : Math.round(size * 1.05); return ( { if (onClick) { e.preventDefault(); onClick(); } }} style={{ display: "inline-flex", alignItems: "center", textDecoration: "none" }}> NOVA Résidence ); } // ────────────────────────────────────────────────────────────────────────── // BUTTONS // ────────────────────────────────────────────────────────────────────────── function Button({ children, variant = "primary", onClick, href, size = "md", icon, style = {} }) { const sizing = size === "lg" ? { padding: "16px 28px", fontSize: 14 } : size === "sm" ? { padding: "8px 14px", fontSize: 12 } : { padding: "12px 22px", fontSize: 13 }; const variantStyle = variant === "primary" ? { background: "var(--accent)", color: "#FAF6EC", border: "1px solid var(--accent)" } : variant === "dark" ? { background: "var(--ink)", color: "var(--bg)", border: "1px solid var(--ink)" } : variant === "ghost" ? { background: "transparent", color: "var(--ink)", border: "1px solid var(--line)" } : variant === "link" ? { background: "transparent", color: "var(--ink)", border: 0, padding: 0, textDecoration: "underline", textUnderlineOffset: 4 } : {}; const Tag = href ? "a" : "button"; return ( { if (variant === "primary") e.currentTarget.style.background = "var(--accent-dark)"; }} onMouseLeave={(e) => { if (variant === "primary") e.currentTarget.style.background = "var(--accent)"; }} > {children} {icon && {icon}} ); } // ────────────────────────────────────────────────────────────────────────── // SECTION SHELL // ────────────────────────────────────────────────────────────────────────── function Section({ children, bg = "bg", id, style = {}, fullBleed = false, label }) { return (
{label && {label}} {children}
); } function Eyebrow({ children, style = {} }) { return (
{children}
); } // ────────────────────────────────────────────────────────────────────────── // TOP BAR // ────────────────────────────────────────────────────────────────────────── function TopBar({ route, navigate }) { const [scrolled, setScrolled] = useState(false); useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 8); onScroll(); window.addEventListener("scroll", onScroll, { passive: true }); return () => window.removeEventListener("scroll", onScroll); }, []); const links = [ { id: "residence", label: "La Résidence" }, { id: "appartements", label: "Appartements" }, { id: "orange", label: "Orange" }, { id: "investir", label: "Investir" }, { id: "contact", label: "Contact" }, ]; return (
navigate("accueil")} />
07 66 46 18 65
); } // ────────────────────────────────────────────────────────────────────────── // FOOTER // ────────────────────────────────────────────────────────────────────────── function Footer({ navigate }) { return ( ); } function FooterCol({ title, links, navigate }) { return (

{title}

); } // ────────────────────────────────────────────────────────────────────────── // FORMAT HELPERS // ────────────────────────────────────────────────────────────────────────── const fmtPrice = (n) => new Intl.NumberFormat("fr-FR", { style: "currency", currency: "EUR", maximumFractionDigits: 0 }).format(n); const fmtN = (n) => new Intl.NumberFormat("fr-FR").format(Math.round(n)); // expose Object.assign(window, { Placeholder, Logo, Button, Section, Eyebrow, TopBar, Footer, FooterCol, fmtPrice, fmtN, });