// ui.jsx — small shared UI pieces. Exports: Logo, TagMockup, TwoTone, Field, Icon function Logo({ size = 32, eyeColor }) { // Qlé monogram: the Q forms the keyring; its centre holds the product (tag + QR). // Holes use --surface so they read as cut-outs on the light UI. eyeColor tints the tag. const ec = eyeColor || "var(--ink)"; const hole = "var(--surface)"; return React.createElement( "svg", { width: size, height: size, viewBox: "0 0 100 100", fill: "none", style: { display: "block" } }, React.createElement("circle", { cx: 47, cy: 48, r: 33, stroke: "var(--ink)", strokeWidth: 11, fill: "none" }), React.createElement("line", { x1: 64, y1: 65, x2: 84, y2: 85, stroke: "var(--ink)", strokeWidth: 11, strokeLinecap: "round" }), React.createElement("rect", { x: 44.2, y: 36.2, width: 5.6, height: 7.4, rx: 2.1, fill: ec }), React.createElement("rect", { x: 37, y: 41.5, width: 20, height: 20, rx: 3.6, fill: ec }), React.createElement("circle", { cx: 47, cy: 38.9, r: 1.65, fill: hole }), React.createElement("rect", { x: 42.4, y: 46.9, width: 9.2, height: 9.2, rx: 2.2, fill: hole }), React.createElement("rect", { x: 45.4, y: 49.9, width: 3.2, height: 3.2, rx: 0.7, fill: ec }), ); } // A tangible 3D "bag tag" mockup that reproduces the real product silhouette // (rounded plaque + flared shoulders + ring with a punched hole), colorable to // any filament color, with the QR inset on the plaque face. function TagMockup({ text, plate, code, size = 300, big }) { const plateHex = plate || "#F4F5F7"; const codeHex = code || "#15161A"; const uid = React.useMemo(() => "tg" + Math.random().toString(36).slice(2, 8), []); const VBH = 94.79; // viewBox height (aspect from the source photo) const H = size * (VBH / 100); const qr = 0.47 * size; const qrLeft = 0.2635 * size; const qrTop = 0.3095 * size; const isLight = /^#?(f|e)/i.test(plateHex.replace("#", "")) || plateHex.indexOf("F4F5F7") >= 0; return React.createElement( "div", { style: { width: size, height: H, position: "relative" } }, // colored tag silhouette React.createElement( "svg", { width: size, height: H, viewBox: "0 0 100 " + VBH, style: { position: "absolute", inset: 0, display: "block", filter: "drop-shadow(0 10px 18px rgba(20,22,30,.14))", overflow: "visible" }, }, React.createElement("defs", null, React.createElement("mask", { id: uid, maskUnits: "userSpaceOnUse" }, React.createElement("rect", { x: 0, y: 0, width: 100, height: VBH, fill: "white" }), React.createElement("circle", { cx: 47.2, cy: 18.6, r: 4.3, fill: "black" }), ) ), React.createElement("g", { fill: plateHex, mask: "url(#" + uid + ")" }, React.createElement("rect", { x: 19.5, y: 26.3, width: 60.7, height: 56.3, rx: 5 }), React.createElement("path", { d: "M38.5,27.6 L38.5,24 Q39.2,19.6 43.8,18.9 L50.6,18.9 Q55.2,19.6 55.9,24 L55.9,27.6 Z" }), React.createElement("circle", { cx: 47.2, cy: 18.6, r: 9.1 }), ), // faint edge so a white tag reads on a light background isLight ? React.createElement("g", { fill: "none", stroke: "rgba(20,22,30,.10)", strokeWidth: 0.5, mask: "url(#" + uid + ")" }, React.createElement("rect", { x: 19.5, y: 26.3, width: 60.7, height: 56.3, rx: 5 }), React.createElement("circle", { cx: 47.2, cy: 18.6, r: 9.1 }), ) : null, ), // QR on the plaque face React.createElement( "div", { style: { position: "absolute", left: qrLeft, top: qrTop, width: qr, height: qr } }, React.createElement(window.QRPreview, { text, plate: plateHex, code: codeHex, size: qr, showPlate: false, rounded: true, }) ) ); } function TwoTone({ plate, code, size = 26 }) { return React.createElement("span", { className: "twotone", style: { background: `linear-gradient(135deg, ${plate} 50%, ${code} 50%)`, width: size, height: size }, }); } // Generic labelled field wrapper function Field({ label, opt, error, children }) { return React.createElement( "div", { className: "field" }, label ? React.createElement("label", { className: "field__label" }, label, opt ? React.createElement("span", { className: "opt" }, " " + opt) : null) : null, children, error ? React.createElement("div", { className: "field__err" }, error) : null ); } // Photo-based tag preview: real product photo for the chosen color + live QR overlay. const PHOTO_COLORS = { green: 1, red: 1, blue: 1, teal: 1, yellow: 1, orange: 1, black: 1, white: 1, clear: 1, pink: 1, gold: 1, applegreen: 1 }; function PhotoTagPreview({ colorId, code, text, size = 220 }) { const plateHex = window.findFilament(colorId).hex; if (!PHOTO_COLORS[colorId]) { return React.createElement(window.TagMockup, { text, plate: plateHex, code, size }); } const H = size * (320 / 260); return React.createElement( "div", { style: { width: size, height: H, position: "relative" } }, React.createElement("img", { src: "assets/tag-" + colorId + ".png", alt: "", style: { width: "100%", height: "100%", display: "block" } }), React.createElement("div", { style: { position: "absolute", left: "11.9%", top: "27.95%", width: "76.19%", height: "61.91%" } }, React.createElement(window.QRPreview, { text, plate: plateHex, code, size: 120, pad: 3, showPlate: false, rounded: true, style: { width: "100%", height: "100%", borderRadius: 0 } }) ) ); } Object.assign(window, { Logo, TagMockup, TwoTone, Field, PhotoTagPreview });