/* global React, ReactDOM */
const { useState, useEffect, useRef } = React;
/* ---------- Cart Drawer ---------- */
function CartDrawer({ open, onClose, items, navigate, setItems }) {
const subtotal = items.reduce((s, it) => s + it.price * it.qty, 0);
const [gift, setGift] = useState(false);
const total = subtotal + (gift ? 30 : 0);
return (
<>
>
);
}
/* ---------- Search overlay ---------- */
function SearchOverlay({ open, onClose, navigate }) {
const [q, setQ] = useState("");
const inputRef = useRef(null);
useEffect(() => { if (open && inputRef.current) inputRef.current.focus(); }, [open]);
const results = q.length < 2 ? [] : window.PEARLA_DATA.products
.filter(p => (p.name + " " + p.category + " " + p.collection).toLowerCase().includes(q.toLowerCase()))
.slice(0, 6);
const popular = ["Midnight Muse", "Ramadan & Eid", "S'élever", "Caftans under 1000"];
return (
{open && (
e.stopPropagation()}>
setQ(e.target.value)}
style={{ flex: 1, border: 0, background: "transparent", fontFamily: "var(--serif)", fontSize: 28, color: "var(--ink)" }} />
{q.length < 2 ? (
Popular searches
{popular.map(t => (
))}
) : (
{results.length === 0 ? (
No matches. Try "abaya", "lace" or "scent".
) : (
)}
)}
)}
);
}
/* ---------- App ---------- */
const DEFAULTS = /*EDITMODE-BEGIN*/{
"palette": "modest-luxe",
"displayFont": "Cormorant Garamond",
"showPedigree": true,
"showFragrance": true,
"showSocial": true,
"showDrop": true,
"denser": false
}/*EDITMODE-END*/;
const PALETTES = {
"modest-luxe": {
name: "Modest luxe",
swatch: ["#FBF7F1", "#1B1814", "#B59A66"],
vars: { "--bg": "#FBF7F1", "--ink": "#1B1814", "--noir": "#100D0A", "--gold": "#B59A66", "--gold-deep": "#8A6F3F", "--bone": "#EEE7DB", "--paper": "#F5EFE5", "--line": "#E6DCCD", "--line-strong": "#D5C8B0", "--muted": "#6B5F4F" }
},
"stone-noir": {
name: "Stone noir",
swatch: ["#EFECE4", "#1A1A1C", "#8E8276"],
vars: { "--bg": "#EFECE4", "--ink": "#1A1A1C", "--noir": "#0E0E10", "--gold": "#8E8276", "--gold-deep": "#5E544A", "--bone": "#E0DCD2", "--paper": "#E6E2D8", "--line": "#D1CCBF", "--line-strong": "#B7B1A1", "--muted": "#6A645B" }
},
"blush-ink": {
name: "Blush & ink",
swatch: ["#FAEEEA", "#22171A", "#C28A8A"],
vars: { "--bg": "#FAEEEA", "--ink": "#22171A", "--noir": "#140C10", "--gold": "#C28A8A", "--gold-deep": "#8E5A5A", "--bone": "#F1DEDA", "--paper": "#F5E5E1", "--line": "#E8D2CC", "--line-strong": "#CFAAA3", "--muted": "#7A5A5A" }
},
"midnight-pearl": {
name: "Midnight pearl",
swatch: ["#11131A", "#EDE6D8", "#B3A06B"],
vars: { "--bg": "#11131A", "--ink": "#EDE6D8", "--noir": "#0A0B10", "--gold": "#B3A06B", "--gold-deep": "#D6C28A", "--bone": "#1E202A", "--paper": "#181B25", "--line": "#2A2D38", "--line-strong": "#3A3E4A", "--muted": "#9A9486" }
}
};
function App() {
const [route, setRoute] = useState(window.location.hash.replace("#", "") || "/");
const [bag, setBag] = useState([]);
const [bagOpen, setBagOpen] = useState(false);
const [searchOpen, setSearchOpen] = useState(false);
const [favs, setFavs] = useState(new Set());
const [toast, setToast] = useState("");
const [tweaks, setTweaks] = useState(DEFAULTS);
const navigate = (r) => {
setRoute(r);
window.location.hash = r;
window.scrollTo({ top: 0, behavior: "instant" });
};
useEffect(() => {
const onHash = () => setRoute(window.location.hash.replace("#", "") || "/");
window.addEventListener("hashchange", onHash);
return () => window.removeEventListener("hashchange", onHash);
}, []);
/* apply tweaks (palette + font) */
useEffect(() => {
const root = document.documentElement;
const pal = PALETTES[tweaks.palette] || PALETTES["modest-luxe"];
Object.entries(pal.vars).forEach(([k, v]) => root.style.setProperty(k, v));
root.style.setProperty("--serif", `"${tweaks.displayFont}", Georgia, serif`);
document.body.dataset.denser = tweaks.denser ? "1" : "0";
}, [tweaks]);
const setTweak = (key, val) => {
const next = typeof key === "object" ? { ...tweaks, ...key } : { ...tweaks, [key]: val };
setTweaks(next);
window.parent?.postMessage({ type: "__edit_mode_set_keys", edits: typeof key === "object" ? key : { [key]: val } }, "*");
};
const onAdd = (p, size) => {
setBag(prev => {
const existing = prev.findIndex(it => it.id === p.id && it.size === size);
if (existing >= 0) {
const c = prev.slice();
c[existing] = { ...c[existing], qty: c[existing].qty + 1 };
return c;
}
return [...prev, { id: p.id, name: p.name, price: p.price, image: p.image, size, qty: 1 }];
});
setToast(`Added — ${p.name}`);
setTimeout(() => setToast(""), 1800);
};
const toggleFav = (id) => {
setFavs(prev => {
const next = new Set(prev);
next.has(id) ? next.delete(id) : next.add(id);
return next;
});
};
const bagCount = bag.reduce((s, it) => s + it.qty, 0);
/* route resolver */
let page;
if (route === "/" || route === "") {
page = (
<>
{tweaks.showPedigree && }
{tweaks.showDrop && }
{tweaks.showFragrance && }
{tweaks.showSocial && }
>
);
} else if (route === "/shop") {
page = ;
} else if (route === "/collections") {
page = ;
} else if (route.startsWith("/collections/")) {
page = ;
} else if (route.startsWith("/product/")) {
page = ;
} else if (route === "/fragrance") {
page = ;
} else if (route === "/gifting") {
page = ;
} else if (route === "/about") {
page = ;
} else if (route === "/contact") {
page = ;
} else {
page = ;
}
return (
setBagOpen(true)} openSearch={() => setSearchOpen(true)} />
{page}
setBagOpen(false)} items={bag} setItems={setBag} navigate={navigate} />
setSearchOpen(false)} navigate={navigate} />
{toast}
PALETTES[k].swatch)}
onChange={(swatch) => {
const key = Object.keys(PALETTES).find(k => JSON.stringify(PALETTES[k].swatch) === JSON.stringify(swatch));
if (key) setTweak("palette", key);
}} />
{(PALETTES[tweaks.palette] || PALETTES["modest-luxe"]).name}
setTweak("displayFont", v)} />
setTweak("showPedigree", v)} />
setTweak("showDrop", v)} />
setTweak("showFragrance", v)} />
setTweak("showSocial", v)} />
{[
["Home", "/"], ["Shop", "/shop"], ["Collections", "/collections"],
["A Collection — Princess", "/collections/princess-and-the-pearl"],
["Product — Celestial Bloom", "/product/celestial-bloom-abaya"],
["Fragrance", "/fragrance"], ["Gifting", "/gifting"],
["About", "/about"], ["Contact", "/contact"]
].map(([label, path]) => (
navigate(path)} />
))}
);
}
ReactDOM.createRoot(document.getElementById("root")).render();