/* ============================================
H-Arch — Shared UI primitives
Nav · Footer · Buttons · Drawer · Forms · Toast · Icons
============================================ */
const { useState, useEffect, useRef, useCallback, useMemo } = React;
/* ---------- Icons (inline SVG only — no third-party) ---------- */
const Icon = {
arrow: (p) => (
),
close: (p) => (
),
download: (p) => (
),
whatsapp: (p) => (
),
ig: (p) => (
),
li: (p) => (
),
yt: (p) => (
),
pin: (p) => (
),
search: (p) => (
),
check: (p) => (
),
};
/* ---------- Brand mark ---------- */
function BrandMark({ size = 40 }) {
return (
);
}
/* ---------- Buttons ---------- */
function Button({ variant = 'primary', size, onLight, children, className = '', ...rest }) {
const cls = [
'btn',
`btn--${variant}`,
size === 'lg' && 'btn--lg',
size === 'sm' && 'btn--small',
onLight && 'on-light',
className,
].filter(Boolean).join(' ');
return ;
}
/* ---------- Nav ---------- */
function Nav({ route, setRoute, theme = 'light' }) {
const [open, setOpen] = useState(false);
const links = [
['home', 'Index'],
['finishes', 'Finishes'],
['projects', 'Projects'],
['capabilities', 'Capabilities'],
['showroom', 'The d3 showroom'],
['contact', 'Contact'],
];
return (
);
}
/* ---------- Heritage ticker ---------- */
function Ticker() {
const items = [
'Backed by Hidayath Group',
'Metalwork since 1976',
"UAE's largest integrated stainless-steel service centre",
'20+ countries · five continents',
'In-house fabrication & finishing',
'Specifier atelier · Dubai Design District',
];
const doubled = [...items, ...items];
return (
{doubled.map((item, i) => (
{item}
))}
);
}
/* ---------- Toast ---------- */
function Toast({ msg, onDone }) {
useEffect(() => {
if (!msg) return;
const t = setTimeout(onDone, 2800);
return () => clearTimeout(t);
}, [msg, onDone]);
if (!msg) return null;
return (
{msg}
);
}
/* ---------- WhatsApp FAB ---------- */
function WhatsAppFab() {
const href = 'https://wa.me/971586332122?text=' +
encodeURIComponent("Hello H-Arch — I'd like to enquire about a project at d3.");
return (
);
}
/* ---------- Footer ---------- */
function Footer({ setRoute, onSample }) {
return (
);
}
/* ---------- Sample request drawer ---------- */
function FinishDrawer({ finish, onClose, onSubmit }) {
const [form, setForm] = useState({ name: '', firm: '', project: '', message: '' });
const [sent, setSent] = useState(false);
const handle = (k) => (e) => setForm({ ...form, [k]: e.target.value });
useEffect(() => {
if (!finish) return;
const onKey = (e) => { if (e.key === 'Escape') onClose(); };
document.addEventListener('keydown', onKey);
document.body.style.overflow = 'hidden';
return () => {
document.removeEventListener('keydown', onKey);
document.body.style.overflow = '';
};
}, [finish, onClose]);
// Reset local form when finish changes
useEffect(() => {
setForm({ name: '', firm: '', project: '', message: '' });
setSent(false);
}, [finish && finish.slug]);
if (!finish) return null;
const submit = (e) => {
e.preventDefault();
if (!form.name || !form.firm) return;
setSent(true);
onSubmit(`Sample request logged for ${finish.name} — we'll be in touch within one business day.`);
};
return (
<>
>
);
}
/* ---------- Scroll-reveal helper ---------- */
function useReveal() {
useEffect(() => {
const els = document.querySelectorAll('.reveal:not(.is-visible)');
if (!('IntersectionObserver' in window)) {
els.forEach((el) => el.classList.add('is-visible'));
return;
}
const io = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting) {
e.target.classList.add('is-visible');
io.unobserve(e.target);
}
});
}, { rootMargin: '0px 0px -8% 0px', threshold: 0.05 });
els.forEach((el) => io.observe(el));
return () => io.disconnect();
});
}
Object.assign(window, {
Icon, BrandMark, Button, Nav, Ticker, Toast, WhatsAppFab, Footer, FinishDrawer, useReveal,
});