// components.jsx — shared primitives for Butter Cream Doces
const { useState, useEffect, useRef } = React;
const WA_NUMBER = "5562994198503";
function waLink(msg) {
const text = encodeURIComponent(msg || "Olá! Gostaria de solicitar um orçamento de doces para meu evento.");
return `https://wa.me/${WA_NUMBER}?text=${text}`;
}
/* ---------- Icons (stroke, 24x24) ---------- */
const Icon = {
whatsapp: (p) => (
),
arrow: (p) => (),
plus: (p) => (),
star: (p) => (),
craft: (p) => (),
leaf: (p) => (),
heart: (p) => (),
box: (p) => (),
chat: (p) => (),
taste: (p) => (),
brush: (p) => (),
truck: (p) => (),
pin: (p) => (),
mail: (p) => (),
ig: (p) => (),
menu: (p) => (),
cake: (p) => (),
};
/* ---------- WhatsApp Button ---------- */
function WaButton({ msg, children, className = "btn-primary", size = "" }) {
return (
{children}
);
}
/* ---------- Image Placeholder ---------- */
function Ph({ label, dark = false, icon = true, style }) {
return (
{icon && }
{label}
);
}
/* ---------- Scroll reveal (survives React re-renders) ---------- */
function useReveal() {
// Re-assert revealed state synchronously before paint so a React re-render
// (which rewrites className from JSX) can't wipe the imperatively-added "in".
React.useLayoutEffect(() => {
if (window.__revealed) window.__revealed.forEach((el) => { if (el.isConnected) el.classList.add("in"); });
});
useEffect(() => {
if (!window.__revealed) window.__revealed = new Set();
let io = window.__bcIO;
if (!io) {
io = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting) { e.target.classList.add("in"); window.__revealed.add(e.target); }
});
}, { threshold: 0.08, rootMargin: "0px 0px -6% 0px" });
window.__bcIO = io;
}
document.querySelectorAll(".reveal").forEach((el) => io.observe(el));
// safety: if frames never run (throttled/background tab), snap content visible
// without a transition so it can never get stuck at opacity 0.
const safety = setTimeout(() => {
document.querySelectorAll(".reveal:not(.in)").forEach((el) => {
if (el.getBoundingClientRect().top < window.innerHeight * 1.05) {
el.style.transition = "none";
el.classList.add("in");
window.__revealed.add(el);
}
});
}, 600);
return () => clearTimeout(safety);
});
}
/* ---------- Section eyebrow + heading ---------- */
function SecHead({ eyebrow, title, lead, align = "center" }) {
return (
{eyebrow}
{title}
{lead &&
{lead}
}
);
}
Object.assign(window, { WA_NUMBER, waLink, Icon, WaButton, Ph, useReveal, SecHead });