@gemboss/gem-framework 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -1
- package/dist/SOURCE.json +3 -3
- package/dist/gem-runtime.js +4 -1
- package/dist/index.js +855 -13
- package/dist/plugins/delivery-estimate.d.ts +9 -0
- package/dist/plugins/delivery-estimate.js +1 -0
- package/dist/plugins/personalize.d.ts +9 -0
- package/dist/plugins/personalize.js +1 -0
- package/dist/plugins/print-preview.d.ts +9 -0
- package/dist/plugins/print-preview.js +1 -0
- package/dist/pod/core.d.ts +26 -0
- package/dist/pod/core.js +1 -0
- package/dist/pod/delivery-estimate.d.ts +28 -0
- package/dist/pod/delivery-estimate.js +1 -0
- package/dist/pod/personalize.d.ts +25 -0
- package/dist/pod/personalize.js +1 -0
- package/dist/pod/print-preview.d.ts +8 -0
- package/dist/pod/print-preview.js +1 -0
- package/dist/pod-buybox.css +55 -0
- package/dist/section-gate.js +161 -7
- package/package.json +10 -1
package/dist/index.js
CHANGED
|
@@ -435,7 +435,7 @@ function gemButtonGradientEndCss(accent = "var(--gem-color-accent)", accentText
|
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
// ../../packages/gem-framework/src/tokens.ts
|
|
438
|
-
var gemFrameworkVersion = "0.
|
|
438
|
+
var gemFrameworkVersion = "0.20.0";
|
|
439
439
|
var GEM_FOCUS_RING_CSS_VAR = "--gem-focus-ring: 2px solid var(--gem-color-accent);";
|
|
440
440
|
var GEM_TAP_MIN_CSS_VAR = "--gem-tap-min: 44px;";
|
|
441
441
|
var GEM_FOCUS_OFFSET_CSS_VAR = "--gem-focus-offset: 3px;";
|
|
@@ -2092,10 +2092,10 @@ function createPanel(parent, ctx, shared) {
|
|
|
2092
2092
|
ctx.on(prev, "click", () => step(-1));
|
|
2093
2093
|
ctx.on(next, "click", () => step(1));
|
|
2094
2094
|
ctx.on(add, "click", addToCart);
|
|
2095
|
-
for (const [group,
|
|
2095
|
+
for (const [group, attr5] of [[swatches, "data-gem-quick-view-color"], [sizes, "data-gem-quick-view-size"]]) {
|
|
2096
2096
|
ctx.on(group, "click", (event) => {
|
|
2097
|
-
const b = event.target.closest && event.target.closest(`[${
|
|
2098
|
-
if (b && !b.disabled) pick(group, Number(b.getAttribute(
|
|
2097
|
+
const b = event.target.closest && event.target.closest(`[${attr5}]`);
|
|
2098
|
+
if (b && !b.disabled) pick(group, Number(b.getAttribute(attr5)));
|
|
2099
2099
|
});
|
|
2100
2100
|
}
|
|
2101
2101
|
ctx.on(dialog, "cancel", (event) => {
|
|
@@ -2456,6 +2456,787 @@ function gemMotionChunks(legacyQuickView) {
|
|
|
2456
2456
|
};
|
|
2457
2457
|
}
|
|
2458
2458
|
|
|
2459
|
+
// ../../packages/gem-framework/src/pod/core.js
|
|
2460
|
+
var FIELD = "[data-gem-personalize]";
|
|
2461
|
+
var SCOPES = ["[data-gem-personalize-scope]", '[data-gem-element="product"]', 'form[action*="/cart/add"]', "[data-gem-section-id]"];
|
|
2462
|
+
function scopeOf(el) {
|
|
2463
|
+
for (const selector of SCOPES) {
|
|
2464
|
+
const found = el && el.closest ? el.closest(selector) : null;
|
|
2465
|
+
if (found) return found;
|
|
2466
|
+
}
|
|
2467
|
+
return el && el.ownerDocument ? el.ownerDocument.documentElement : null;
|
|
2468
|
+
}
|
|
2469
|
+
var uploads = /* @__PURE__ */ new WeakMap();
|
|
2470
|
+
var attr = (el, name) => el && el.getAttribute ? el.getAttribute(name) : null;
|
|
2471
|
+
var trim = (value) => typeof value === "string" ? value.trim() : "";
|
|
2472
|
+
function fieldName(field) {
|
|
2473
|
+
const own = trim(attr(field, "data-gem-personalize-name"));
|
|
2474
|
+
if (own) return own;
|
|
2475
|
+
const control = field.querySelector && field.querySelector("input, select, textarea");
|
|
2476
|
+
return control ? trim(control.getAttribute("name")) : "";
|
|
2477
|
+
}
|
|
2478
|
+
function fieldType(field) {
|
|
2479
|
+
const own = trim(attr(field, "data-gem-personalize-type"));
|
|
2480
|
+
if (own) return own;
|
|
2481
|
+
const control = field.querySelector && field.querySelector("input, select, textarea");
|
|
2482
|
+
if (!control) return "text";
|
|
2483
|
+
if (control.tagName === "SELECT") return "select";
|
|
2484
|
+
if (control.tagName === "TEXTAREA") return "textarea";
|
|
2485
|
+
return control.type === "file" ? "upload" : control.type === "checkbox" ? "checkbox" : control.type === "radio" ? "swatch" : "text";
|
|
2486
|
+
}
|
|
2487
|
+
var controlsOf = (field) => Array.prototype.slice.call(field.querySelectorAll ? field.querySelectorAll("input, select, textarea") : []).filter((c) => c.type !== "hidden");
|
|
2488
|
+
var HEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
2489
|
+
var hexColor = (value) => HEX.test(trim(value)) ? trim(value) : "";
|
|
2490
|
+
function readField(field) {
|
|
2491
|
+
const name = fieldName(field);
|
|
2492
|
+
const type = fieldType(field);
|
|
2493
|
+
const out = { name, type, value: "", color: "", preview: "", key: "" };
|
|
2494
|
+
const controls = controlsOf(field);
|
|
2495
|
+
if (type === "upload") {
|
|
2496
|
+
const state = uploads.get(field);
|
|
2497
|
+
if (state) Object.assign(out, { value: state.url || "", preview: state.preview || state.url || "", key: state.key || "" });
|
|
2498
|
+
return out;
|
|
2499
|
+
}
|
|
2500
|
+
if (type === "checkbox") {
|
|
2501
|
+
const box2 = controls.find((c) => c.type === "checkbox" && c.checked);
|
|
2502
|
+
if (box2) out.value = box2.value && box2.value !== "on" ? box2.value : "Yes";
|
|
2503
|
+
return out;
|
|
2504
|
+
}
|
|
2505
|
+
if (type === "select") {
|
|
2506
|
+
const select = controls.find((c) => c.tagName === "SELECT");
|
|
2507
|
+
const option = select && select.selectedIndex > -1 ? select.options[select.selectedIndex] : null;
|
|
2508
|
+
out.value = trim(select ? select.value : "");
|
|
2509
|
+
out.color = hexColor(attr(option, "data-gem-personalize-color"));
|
|
2510
|
+
return out;
|
|
2511
|
+
}
|
|
2512
|
+
const radios = controls.filter((c) => c.type === "radio");
|
|
2513
|
+
if (radios.length) {
|
|
2514
|
+
const picked = radios.find((c) => c.checked);
|
|
2515
|
+
if (picked) {
|
|
2516
|
+
out.value = trim(picked.value);
|
|
2517
|
+
const label = picked.closest ? picked.closest("label") : null;
|
|
2518
|
+
out.color = hexColor(attr(picked, "data-gem-personalize-color") || attr(label, "data-gem-personalize-color"));
|
|
2519
|
+
}
|
|
2520
|
+
return out;
|
|
2521
|
+
}
|
|
2522
|
+
const box = controls[0];
|
|
2523
|
+
out.value = trim(box ? box.value : "");
|
|
2524
|
+
return out;
|
|
2525
|
+
}
|
|
2526
|
+
function readScope(scope) {
|
|
2527
|
+
const values = {};
|
|
2528
|
+
const fields = scope && scope.querySelectorAll ? scope.querySelectorAll(FIELD) : [];
|
|
2529
|
+
Array.prototype.forEach.call(fields, (field) => {
|
|
2530
|
+
const read = readField(field);
|
|
2531
|
+
if (read.name) values[read.name] = field.hidden ? { ...read, value: "", color: "", preview: "" } : read;
|
|
2532
|
+
});
|
|
2533
|
+
return values;
|
|
2534
|
+
}
|
|
2535
|
+
var adopted = /* @__PURE__ */ new WeakMap();
|
|
2536
|
+
function adoptCss(el, key, sentinel, css) {
|
|
2537
|
+
const doc = el.ownerDocument;
|
|
2538
|
+
const view = doc && doc.defaultView;
|
|
2539
|
+
if (!css || !view) return false;
|
|
2540
|
+
let keys = adopted.get(doc);
|
|
2541
|
+
if (!keys) adopted.set(doc, keys = /* @__PURE__ */ new Set());
|
|
2542
|
+
if (keys.has(key)) return false;
|
|
2543
|
+
keys.add(key);
|
|
2544
|
+
try {
|
|
2545
|
+
if (view.getComputedStyle(el).getPropertyValue(sentinel).trim()) return false;
|
|
2546
|
+
const sheet = new view.CSSStyleSheet();
|
|
2547
|
+
sheet.replaceSync(css);
|
|
2548
|
+
doc.adoptedStyleSheets = [...doc.adoptedStyleSheets, sheet];
|
|
2549
|
+
return true;
|
|
2550
|
+
} catch {
|
|
2551
|
+
return false;
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2555
|
+
// ../../packages/gem-framework/src/pod/personalize.js
|
|
2556
|
+
var PERSONALIZE_MESSAGES = {
|
|
2557
|
+
required: "Fill this in to add to cart.",
|
|
2558
|
+
type: "This file type is not accepted.",
|
|
2559
|
+
size: "This file is over {mb} MB.",
|
|
2560
|
+
failed: "The upload failed. Try again.",
|
|
2561
|
+
uploading: "Wait for the upload to finish.",
|
|
2562
|
+
unavailable: "Photo upload is not available."
|
|
2563
|
+
};
|
|
2564
|
+
var WHEN = /^\s*(!)?\s*([^!=|]+?)\s*(?:(!=|==|=)\s*(.+?))?\s*$/;
|
|
2565
|
+
function parsePersonalizeWhen(text2) {
|
|
2566
|
+
if (typeof text2 !== "string" || !text2.trim()) return null;
|
|
2567
|
+
const m = WHEN.exec(text2);
|
|
2568
|
+
if (!m) return null;
|
|
2569
|
+
const [, not, name, op, rhs] = m;
|
|
2570
|
+
if (!op) return { name: name.trim(), op: not ? "unset" : "set", values: [] };
|
|
2571
|
+
const values = rhs.split("|").map((v) => v.trim().toLowerCase()).filter(Boolean);
|
|
2572
|
+
if (not || !values.length) return null;
|
|
2573
|
+
return { name: name.trim(), op: op === "!=" ? "not" : "is", values };
|
|
2574
|
+
}
|
|
2575
|
+
function personalizeWhenHolds(cond, values) {
|
|
2576
|
+
const key = Object.keys(values).find((k) => k.toLowerCase() === cond.name.toLowerCase());
|
|
2577
|
+
const value = key ? String(values[key].value || "").trim().toLowerCase() : "";
|
|
2578
|
+
if (cond.op === "set") return value !== "";
|
|
2579
|
+
if (cond.op === "unset") return value === "";
|
|
2580
|
+
return cond.op === "is" ? cond.values.includes(value) : !cond.values.includes(value);
|
|
2581
|
+
}
|
|
2582
|
+
function fileAccepted(file, accept) {
|
|
2583
|
+
const tokens = String(accept || "").split(",").map((t) => t.trim().toLowerCase()).filter(Boolean);
|
|
2584
|
+
if (!tokens.length) return true;
|
|
2585
|
+
const type = String(file.type || "").toLowerCase();
|
|
2586
|
+
const name = String(file.name || "").toLowerCase();
|
|
2587
|
+
return tokens.some((t) => t.startsWith(".") ? name.endsWith(t) : t.endsWith("/*") ? type.startsWith(t.slice(0, -1)) : type === t);
|
|
2588
|
+
}
|
|
2589
|
+
var attr2 = (el, name) => el && el.getAttribute ? el.getAttribute(name) : null;
|
|
2590
|
+
var tail = (id) => String(id).split("/").pop();
|
|
2591
|
+
var LISTS = '[data-gem-element="product-related"], [data-gem-element="product-list"]';
|
|
2592
|
+
function mountPersonalize(env, onUnmount) {
|
|
2593
|
+
const { window: win, document: doc, on, mark, api } = env;
|
|
2594
|
+
const owned = /* @__PURE__ */ new Set();
|
|
2595
|
+
const objectUrls = /* @__PURE__ */ new Set();
|
|
2596
|
+
const aborts = /* @__PURE__ */ new Set();
|
|
2597
|
+
let styled = false;
|
|
2598
|
+
let alive = true;
|
|
2599
|
+
const scopeFields = (scope) => [...owned].filter((f) => f.isConnected && scopeOf(f) === scope);
|
|
2600
|
+
const isRequired = (field) => attr2(field, "data-gem-personalize-required") === "true" || controlsOf(field).some((c) => c.required);
|
|
2601
|
+
const message = (field, reason, fill = {}) => {
|
|
2602
|
+
const own = attr2(field, `data-gem-personalize-message-${reason}`) || attr2(scopeOf(field), `data-gem-personalize-message-${reason}`);
|
|
2603
|
+
return (own || PERSONALIZE_MESSAGES[reason] || "").replace(/\{(\w+)\}/g, (_, k) => k in fill ? String(fill[k]) : "");
|
|
2604
|
+
};
|
|
2605
|
+
const errorBox = (field) => {
|
|
2606
|
+
let box = field.querySelector("[data-gem-personalize-error]");
|
|
2607
|
+
if (!box) {
|
|
2608
|
+
box = doc.createElement("p");
|
|
2609
|
+
box.className = "gem-el-product-personalization__error";
|
|
2610
|
+
box.setAttribute("data-gem-personalize-error", "");
|
|
2611
|
+
box.setAttribute("role", "alert");
|
|
2612
|
+
field.appendChild(box);
|
|
2613
|
+
}
|
|
2614
|
+
if (!box.id) box.id = `gem-personalize-error-${Math.random().toString(36).slice(2, 9)}`;
|
|
2615
|
+
return box;
|
|
2616
|
+
};
|
|
2617
|
+
const showProblem = (field, reason, fill) => {
|
|
2618
|
+
const box = errorBox(field);
|
|
2619
|
+
box.textContent = message(field, reason, fill);
|
|
2620
|
+
box.hidden = false;
|
|
2621
|
+
field.setAttribute("data-gem-personalize-invalid", reason);
|
|
2622
|
+
controlsOf(field).forEach((c) => {
|
|
2623
|
+
c.setAttribute("aria-invalid", "true");
|
|
2624
|
+
const ids = (attr2(c, "aria-describedby") || "").split(/\s+/).filter(Boolean);
|
|
2625
|
+
if (!ids.includes(box.id)) c.setAttribute("aria-describedby", [...ids, box.id].join(" "));
|
|
2626
|
+
});
|
|
2627
|
+
};
|
|
2628
|
+
const clearProblem = (field) => {
|
|
2629
|
+
if (!field.hasAttribute("data-gem-personalize-invalid")) return;
|
|
2630
|
+
field.removeAttribute("data-gem-personalize-invalid");
|
|
2631
|
+
const box = field.querySelector("[data-gem-personalize-error]");
|
|
2632
|
+
if (box) {
|
|
2633
|
+
box.textContent = "";
|
|
2634
|
+
box.hidden = true;
|
|
2635
|
+
}
|
|
2636
|
+
controlsOf(field).forEach((c) => c.removeAttribute("aria-invalid"));
|
|
2637
|
+
};
|
|
2638
|
+
const emitChange = (field) => {
|
|
2639
|
+
const { name, value, type, color: color2, preview } = readField(field);
|
|
2640
|
+
field.dispatchEvent(new win.CustomEvent("gem:personalize:change", { bubbles: true, detail: { name, value, type, color: color2, preview } }));
|
|
2641
|
+
};
|
|
2642
|
+
const count = (field) => {
|
|
2643
|
+
const out = field.querySelector("[data-gem-personalize-count]");
|
|
2644
|
+
const box = controlsOf(field).find((c) => c.tagName === "TEXTAREA" || c.type === "text" || c.type === "");
|
|
2645
|
+
const max = Number(attr2(field, "data-gem-personalize-max")) || (box && box.maxLength > 0 ? box.maxLength : 0);
|
|
2646
|
+
if (!out || !box || !max) return;
|
|
2647
|
+
out.textContent = `${box.value.length}/${max}`;
|
|
2648
|
+
if (box.value.length >= max) out.setAttribute("data-gem-personalize-full", "");
|
|
2649
|
+
else out.removeAttribute("data-gem-personalize-full");
|
|
2650
|
+
};
|
|
2651
|
+
const applyConditions = (scope) => {
|
|
2652
|
+
const fields = scopeFields(scope).filter((f) => attr2(f, "data-gem-personalize-when"));
|
|
2653
|
+
for (let pass = 0; pass < 4; pass += 1) {
|
|
2654
|
+
const values = readScope(scope);
|
|
2655
|
+
let changed = false;
|
|
2656
|
+
for (const field of fields) {
|
|
2657
|
+
if (attr2(field, "data-gem-personalize-state") === "unavailable") continue;
|
|
2658
|
+
const cond = parsePersonalizeWhen(attr2(field, "data-gem-personalize-when"));
|
|
2659
|
+
if (!cond) {
|
|
2660
|
+
field.setAttribute("data-gem-personalize-when-invalid", "");
|
|
2661
|
+
continue;
|
|
2662
|
+
}
|
|
2663
|
+
const show = personalizeWhenHolds(cond, values);
|
|
2664
|
+
if (field.hidden === !show) continue;
|
|
2665
|
+
field.hidden = !show;
|
|
2666
|
+
controlsOf(field).forEach((c) => {
|
|
2667
|
+
c.disabled = !show;
|
|
2668
|
+
});
|
|
2669
|
+
if (!show) clearProblem(field);
|
|
2670
|
+
changed = true;
|
|
2671
|
+
}
|
|
2672
|
+
if (!changed) break;
|
|
2673
|
+
}
|
|
2674
|
+
};
|
|
2675
|
+
const claim = (field) => {
|
|
2676
|
+
if (!mark(field, "personalize")) return false;
|
|
2677
|
+
owned.add(field);
|
|
2678
|
+
if (!styled && env.css) {
|
|
2679
|
+
adoptCss(field, "personalize", "--gem-personalize-css", env.css);
|
|
2680
|
+
styled = true;
|
|
2681
|
+
}
|
|
2682
|
+
const max = Number(attr2(field, "data-gem-personalize-max"));
|
|
2683
|
+
controlsOf(field).forEach((c) => {
|
|
2684
|
+
if (max > 0 && (c.tagName === "TEXTAREA" || c.type === "text") && !(c.maxLength > 0)) c.maxLength = max;
|
|
2685
|
+
});
|
|
2686
|
+
count(field);
|
|
2687
|
+
if (fieldType(field) === "upload" && !endpointOf(field)) unavailable(field);
|
|
2688
|
+
return true;
|
|
2689
|
+
};
|
|
2690
|
+
const claimAll = () => {
|
|
2691
|
+
const fresh = Array.prototype.filter.call(doc.querySelectorAll(FIELD), claim);
|
|
2692
|
+
new Set(fresh.map(scopeOf)).forEach(applyConditions);
|
|
2693
|
+
return fresh.length;
|
|
2694
|
+
};
|
|
2695
|
+
const forget = (field) => {
|
|
2696
|
+
const state = uploads.get(field);
|
|
2697
|
+
if (state && state.preview && objectUrls.delete(state.preview) && win.URL && win.URL.revokeObjectURL) win.URL.revokeObjectURL(state.preview);
|
|
2698
|
+
uploads.delete(field);
|
|
2699
|
+
field.removeAttribute("data-gem-personalize-state");
|
|
2700
|
+
const thumb = field.querySelector("[data-gem-personalize-thumb]");
|
|
2701
|
+
if (thumb) {
|
|
2702
|
+
thumb.removeAttribute("src");
|
|
2703
|
+
thumb.hidden = true;
|
|
2704
|
+
}
|
|
2705
|
+
};
|
|
2706
|
+
const endpointOf = (field) => {
|
|
2707
|
+
const gem = win.GemTheme || {};
|
|
2708
|
+
const url = attr2(field, "data-gem-personalize-endpoint") || attr2(scopeOf(field), "data-gem-personalize-endpoint") || api && api.uploadEndpoint || gem.uploadEndpoint;
|
|
2709
|
+
return typeof url === "string" && url.trim() ? url.trim() : "";
|
|
2710
|
+
};
|
|
2711
|
+
const unavailable = (field) => {
|
|
2712
|
+
forget(field);
|
|
2713
|
+
clearProblem(field);
|
|
2714
|
+
field.setAttribute("data-gem-personalize-state", "unavailable");
|
|
2715
|
+
field.hidden = true;
|
|
2716
|
+
controlsOf(field).forEach((c) => {
|
|
2717
|
+
c.disabled = true;
|
|
2718
|
+
});
|
|
2719
|
+
emitChange(field);
|
|
2720
|
+
};
|
|
2721
|
+
const json = (res) => res && typeof res.json === "function" ? res.json().catch(() => ({})) : Promise.resolve({});
|
|
2722
|
+
const failure = (reason, extra = {}) => Object.assign(new Error(reason), { reason, ...extra });
|
|
2723
|
+
const send = (endpoint, field, file, mb, signal) => {
|
|
2724
|
+
const fetch = (url, init) => win.fetch(url, { ...init, ...signal ? { signal } : {} });
|
|
2725
|
+
return fetch(endpoint, { method: "POST", credentials: "same-origin", headers: { "content-type": "application/json", accept: "application/json" }, body: JSON.stringify({ type: file.type, size: file.size, max_mb: mb }) }).then((res) => json(res).then((answer) => {
|
|
2726
|
+
if (res.status === 404) throw failure("unavailable");
|
|
2727
|
+
if (res.status === 415) throw failure("type");
|
|
2728
|
+
if (res.status === 413) throw failure("size", { mb: Number(answer.max_mb) || mb });
|
|
2729
|
+
if (!res.ok) throw failure("failed");
|
|
2730
|
+
const put = answer && answer.upload;
|
|
2731
|
+
if (put && typeof put.url === "string") {
|
|
2732
|
+
const headers = put.headers && typeof put.headers === "object" ? put.headers : {};
|
|
2733
|
+
return fetch(put.url, { method: typeof put.method === "string" ? put.method : "PUT", headers, body: file, credentials: "omit" }).then((done) => {
|
|
2734
|
+
if (!done || !done.ok) throw failure("failed");
|
|
2735
|
+
return answer;
|
|
2736
|
+
});
|
|
2737
|
+
}
|
|
2738
|
+
const body = new win.FormData();
|
|
2739
|
+
body.append("file", file, file.name);
|
|
2740
|
+
body.append("name", fieldName(field));
|
|
2741
|
+
return fetch(endpoint, { method: "POST", body, credentials: "same-origin" }).then((res2) => {
|
|
2742
|
+
if (!res2 || !res2.ok) throw failure(res2 && res2.status === 404 ? "unavailable" : "failed");
|
|
2743
|
+
return json(res2);
|
|
2744
|
+
});
|
|
2745
|
+
}));
|
|
2746
|
+
};
|
|
2747
|
+
const upload = (field, input) => {
|
|
2748
|
+
forget(field);
|
|
2749
|
+
clearProblem(field);
|
|
2750
|
+
const file = input.files && input.files[0];
|
|
2751
|
+
if (!file) {
|
|
2752
|
+
emitChange(field);
|
|
2753
|
+
return;
|
|
2754
|
+
}
|
|
2755
|
+
const accept = attr2(field, "data-gem-personalize-accept") || attr2(input, "accept") || "image/*";
|
|
2756
|
+
const mb = Number(attr2(field, "data-gem-personalize-max-mb")) || 10;
|
|
2757
|
+
const endpoint = endpointOf(field);
|
|
2758
|
+
const refuse = (reason, fill = { mb }) => {
|
|
2759
|
+
input.value = "";
|
|
2760
|
+
showProblem(field, reason, fill);
|
|
2761
|
+
emitChange(field);
|
|
2762
|
+
};
|
|
2763
|
+
if (!fileAccepted(file, accept)) return refuse("type");
|
|
2764
|
+
if (file.size > mb * 1048576) return refuse("size");
|
|
2765
|
+
if (!endpoint || typeof win.fetch !== "function") return unavailable(field);
|
|
2766
|
+
const preview = win.URL && win.URL.createObjectURL ? win.URL.createObjectURL(file) : "";
|
|
2767
|
+
if (preview) objectUrls.add(preview);
|
|
2768
|
+
const state = { uploading: true, preview, url: "", key: "" };
|
|
2769
|
+
uploads.set(field, state);
|
|
2770
|
+
field.setAttribute("data-gem-personalize-state", "uploading");
|
|
2771
|
+
const thumb = field.querySelector("[data-gem-personalize-thumb]");
|
|
2772
|
+
if (thumb && preview) {
|
|
2773
|
+
thumb.src = preview;
|
|
2774
|
+
thumb.hidden = false;
|
|
2775
|
+
}
|
|
2776
|
+
emitChange(field);
|
|
2777
|
+
const abort = win.AbortController ? new win.AbortController() : null;
|
|
2778
|
+
if (abort) aborts.add(abort);
|
|
2779
|
+
send(endpoint, field, file, mb, abort && abort.signal).then((answer) => {
|
|
2780
|
+
const url = answer && typeof answer.url === "string" && /^https?:\/\//i.test(answer.url) ? answer.url : "";
|
|
2781
|
+
if (!url) throw failure("failed");
|
|
2782
|
+
if (uploads.get(field) !== state) return;
|
|
2783
|
+
Object.assign(state, { uploading: false, url, key: typeof answer.key === "string" ? answer.key : "" });
|
|
2784
|
+
field.setAttribute("data-gem-personalize-state", "done");
|
|
2785
|
+
clearProblem(field);
|
|
2786
|
+
emitChange(field);
|
|
2787
|
+
}).catch((error) => {
|
|
2788
|
+
if (uploads.get(field) !== state || abort && abort.signal.aborted) return;
|
|
2789
|
+
const reason = error && error.reason;
|
|
2790
|
+
if (reason === "unavailable") return unavailable(field);
|
|
2791
|
+
forget(field);
|
|
2792
|
+
refuse(reason === "type" || reason === "size" ? reason : "failed", { mb: error && error.mb || mb });
|
|
2793
|
+
}).finally(() => {
|
|
2794
|
+
if (abort) aborts.delete(abort);
|
|
2795
|
+
});
|
|
2796
|
+
};
|
|
2797
|
+
const onEdit = (event) => {
|
|
2798
|
+
const field = event.target && event.target.closest ? event.target.closest(FIELD) : null;
|
|
2799
|
+
if (!field || !owned.has(field)) return;
|
|
2800
|
+
const type = fieldType(field);
|
|
2801
|
+
const typing = type === "text" || type === "textarea";
|
|
2802
|
+
if (event.type === "input" ? !typing : typing) return;
|
|
2803
|
+
if (type === "upload") {
|
|
2804
|
+
if (event.target.type === "file") upload(field, event.target);
|
|
2805
|
+
return;
|
|
2806
|
+
}
|
|
2807
|
+
count(field);
|
|
2808
|
+
if (readField(field).value) clearProblem(field);
|
|
2809
|
+
applyConditions(scopeOf(field));
|
|
2810
|
+
emitChange(field);
|
|
2811
|
+
};
|
|
2812
|
+
const problems = (scope) => scopeFields(scope).filter((f) => !f.hidden).flatMap((field) => {
|
|
2813
|
+
const state = uploads.get(field);
|
|
2814
|
+
if (state && state.uploading) return [[field, "uploading"]];
|
|
2815
|
+
return isRequired(field) && !readField(field).value ? [[field, "required"]] : [];
|
|
2816
|
+
});
|
|
2817
|
+
const allowed = (scope) => {
|
|
2818
|
+
applyConditions(scope);
|
|
2819
|
+
const found = problems(scope);
|
|
2820
|
+
scopeFields(scope).forEach((f) => {
|
|
2821
|
+
if (!found.some(([p]) => p === f)) clearProblem(f);
|
|
2822
|
+
});
|
|
2823
|
+
if (!found.length) return true;
|
|
2824
|
+
found.forEach(([field, reason]) => showProblem(field, reason));
|
|
2825
|
+
const [first] = found[0];
|
|
2826
|
+
const control = controlsOf(first)[0];
|
|
2827
|
+
if (control && control.focus) control.focus({ preventScroll: true });
|
|
2828
|
+
if (first.scrollIntoView) first.scrollIntoView({ block: "center" });
|
|
2829
|
+
scope.dispatchEvent(new win.CustomEvent("gem:personalize:blocked", { bubbles: true, detail: { names: found.map(([f]) => fieldName(f)), reasons: found.map(([, r]) => r) } }));
|
|
2830
|
+
return false;
|
|
2831
|
+
};
|
|
2832
|
+
const properties = (scope) => scopeFields(scope).filter((f) => !f.hidden).map(readField).filter((r) => r.name && r.value).flatMap((r) => [
|
|
2833
|
+
{ key: r.name, value: r.value },
|
|
2834
|
+
...r.type === "upload" && r.key ? [{ key: `_${r.name}_upload`, value: r.key }] : []
|
|
2835
|
+
]);
|
|
2836
|
+
const merge = (list, add) => [...Array.isArray(list) ? list.filter((p) => !add.some((q) => p && q.key === p.key)) : [], ...add];
|
|
2837
|
+
const variantOf = (scope) => [scope, ...scope.querySelectorAll("[data-gem-variant-id]")].filter((el) => el === scope || scopeOf(el) === scope && !el.closest(LISTS)).map((el) => attr2(el, "data-gem-variant-id")).filter(Boolean).map(tail);
|
|
2838
|
+
const scopeOfAdd = (event) => {
|
|
2839
|
+
const t = event.target;
|
|
2840
|
+
if (t && t.nodeType === 1) {
|
|
2841
|
+
const scope = scopeOf(t);
|
|
2842
|
+
if (scopeFields(scope).length) return scope;
|
|
2843
|
+
}
|
|
2844
|
+
const detail = event.detail || {};
|
|
2845
|
+
const ids = [detail.variantId, ...Array.isArray(detail.lines) ? detail.lines.map((l) => l && l.variantId) : []].filter(Boolean).map(tail);
|
|
2846
|
+
if (!ids.length) return null;
|
|
2847
|
+
const scopes = [...new Set([...owned].filter((f) => f.isConnected).map(scopeOf))];
|
|
2848
|
+
return scopes.find((scope) => variantOf(scope).some((id) => ids.includes(id))) || null;
|
|
2849
|
+
};
|
|
2850
|
+
const onAdd = (event) => {
|
|
2851
|
+
const scope = scopeOfAdd(event);
|
|
2852
|
+
if (!scope) return;
|
|
2853
|
+
const detail = event.detail && typeof event.detail === "object" ? event.detail : null;
|
|
2854
|
+
if (!allowed(scope)) {
|
|
2855
|
+
event.stopImmediatePropagation();
|
|
2856
|
+
if (event.cancelable) event.preventDefault();
|
|
2857
|
+
win.setTimeout(() => win.dispatchEvent(new win.CustomEvent("gem:cart:add-failed", { detail: { variantId: detail && detail.variantId, reason: "personalization" } })), 0);
|
|
2858
|
+
return;
|
|
2859
|
+
}
|
|
2860
|
+
const add = properties(scope);
|
|
2861
|
+
if (!detail || !add.length) return;
|
|
2862
|
+
if (event.type === "gem:cart:add") {
|
|
2863
|
+
detail.properties = merge(detail.properties, add);
|
|
2864
|
+
return;
|
|
2865
|
+
}
|
|
2866
|
+
const lines = Array.isArray(detail.lines) ? detail.lines.filter((l) => l && typeof l === "object") : [];
|
|
2867
|
+
const ids = variantOf(scope);
|
|
2868
|
+
const mine = lines.filter((l) => ids.includes(tail(l.variantId)));
|
|
2869
|
+
(mine.length ? mine : lines).forEach((line) => {
|
|
2870
|
+
line.properties = merge(line.properties, add);
|
|
2871
|
+
});
|
|
2872
|
+
};
|
|
2873
|
+
const scopeOfForm = (form) => [...new Set([...owned].filter((f) => f.isConnected).map(scopeOf))].filter((s) => s.contains(form)).sort((a, b) => a.contains(b) ? 1 : -1)[0] || null;
|
|
2874
|
+
const onSubmit = (event) => {
|
|
2875
|
+
const form = event.target;
|
|
2876
|
+
if (!form || form.tagName !== "FORM") return;
|
|
2877
|
+
const scope = scopeOfForm(form);
|
|
2878
|
+
if (!scope) return;
|
|
2879
|
+
if (!allowed(scope)) {
|
|
2880
|
+
event.preventDefault();
|
|
2881
|
+
event.stopImmediatePropagation();
|
|
2882
|
+
return;
|
|
2883
|
+
}
|
|
2884
|
+
if (!form.hasAttribute("action")) return;
|
|
2885
|
+
form.querySelectorAll("[data-gem-personalize-hidden]").forEach((el) => el.remove());
|
|
2886
|
+
properties(scope).forEach(({ key, value }) => {
|
|
2887
|
+
const input = doc.createElement("input");
|
|
2888
|
+
Object.assign(input, { type: "hidden", name: `properties[${key}]`, value });
|
|
2889
|
+
input.setAttribute("data-gem-personalize-hidden", "");
|
|
2890
|
+
form.appendChild(input);
|
|
2891
|
+
});
|
|
2892
|
+
};
|
|
2893
|
+
const bound = claimAll();
|
|
2894
|
+
on(doc, "input", onEdit);
|
|
2895
|
+
on(doc, "change", onEdit);
|
|
2896
|
+
on(doc, "submit", onSubmit, true);
|
|
2897
|
+
on(win, "gem:cart:add", onAdd, true);
|
|
2898
|
+
on(win, "gem:cart:add-many", onAdd, true);
|
|
2899
|
+
if (typeof win.MutationObserver === "function") {
|
|
2900
|
+
let queued = false;
|
|
2901
|
+
const watch = new win.MutationObserver((records) => {
|
|
2902
|
+
if (queued || !records.some((r) => Array.prototype.some.call(r.addedNodes, (n) => n.nodeType === 1 && (n.matches(FIELD) || n.querySelector(FIELD))))) return;
|
|
2903
|
+
queued = true;
|
|
2904
|
+
Promise.resolve().then(() => {
|
|
2905
|
+
queued = false;
|
|
2906
|
+
if (alive) claimAll();
|
|
2907
|
+
});
|
|
2908
|
+
});
|
|
2909
|
+
watch.observe(doc.documentElement, { childList: true, subtree: true });
|
|
2910
|
+
onUnmount(() => watch.disconnect());
|
|
2911
|
+
}
|
|
2912
|
+
onUnmount(() => {
|
|
2913
|
+
alive = false;
|
|
2914
|
+
aborts.forEach((a) => a.abort());
|
|
2915
|
+
owned.forEach((field) => {
|
|
2916
|
+
if (field.hidden && (attr2(field, "data-gem-personalize-when") || attr2(field, "data-gem-personalize-state") === "unavailable")) {
|
|
2917
|
+
field.hidden = false;
|
|
2918
|
+
controlsOf(field).forEach((c) => {
|
|
2919
|
+
c.disabled = false;
|
|
2920
|
+
});
|
|
2921
|
+
}
|
|
2922
|
+
forget(field);
|
|
2923
|
+
clearProblem(field);
|
|
2924
|
+
});
|
|
2925
|
+
owned.clear();
|
|
2926
|
+
});
|
|
2927
|
+
return bound;
|
|
2928
|
+
}
|
|
2929
|
+
|
|
2930
|
+
// ../../packages/gem-framework/src/pod/print-preview.js
|
|
2931
|
+
var HOOK = "[data-gem-print-preview]";
|
|
2932
|
+
var attr3 = (el, name) => el && el.getAttribute ? el.getAttribute(name) || "" : "";
|
|
2933
|
+
function printPreviewContent(root, values) {
|
|
2934
|
+
const pick = (name) => {
|
|
2935
|
+
const key = name && Object.keys(values).find((k) => k.toLowerCase() === name.trim().toLowerCase());
|
|
2936
|
+
return key ? values[key] : null;
|
|
2937
|
+
};
|
|
2938
|
+
const text2 = pick(attr3(root, "data-gem-print-preview-text"));
|
|
2939
|
+
const photo = pick(attr3(root, "data-gem-print-preview-photo"));
|
|
2940
|
+
const spec = attr3(root, "data-gem-print-preview-color").trim();
|
|
2941
|
+
const from = /^from:/i.test(spec) ? pick(spec.slice(5)) : null;
|
|
2942
|
+
const photoUrl = photo ? photo.preview || photo.value : "";
|
|
2943
|
+
return {
|
|
2944
|
+
text: text2 ? String(text2.value || "") : "",
|
|
2945
|
+
photo: /^(?:https?:|blob:|data:image\/)/i.test(photoUrl) ? photoUrl : "",
|
|
2946
|
+
color: from ? from.color || hexColor(from.value) : hexColor(spec)
|
|
2947
|
+
};
|
|
2948
|
+
}
|
|
2949
|
+
function mountPrintPreview(env, onUnmount) {
|
|
2950
|
+
const { window: win, document: doc, on, mark } = env;
|
|
2951
|
+
let count = 0;
|
|
2952
|
+
doc.querySelectorAll(HOOK).forEach((root) => {
|
|
2953
|
+
if (!mark(root, "print-preview")) return;
|
|
2954
|
+
count += 1;
|
|
2955
|
+
if (env.css) adoptCss(root, "print-preview", "--gem-print-preview-css", env.css);
|
|
2956
|
+
const area = root.querySelector("[data-gem-print-preview-area]");
|
|
2957
|
+
if (!area) return;
|
|
2958
|
+
const scope = scopeOf(root);
|
|
2959
|
+
let textEl = null;
|
|
2960
|
+
let photoEl = null;
|
|
2961
|
+
const fit = () => {
|
|
2962
|
+
if (!textEl) return;
|
|
2963
|
+
textEl.style.transform = "";
|
|
2964
|
+
const room = area.clientWidth;
|
|
2965
|
+
const need = textEl.scrollWidth;
|
|
2966
|
+
if (room > 0 && need > room) textEl.style.transform = `scale(${Math.max(0.3, room / need).toFixed(3)})`;
|
|
2967
|
+
};
|
|
2968
|
+
const draw = () => {
|
|
2969
|
+
const { text: text2, photo, color: color2 } = printPreviewContent(root, readScope(scope));
|
|
2970
|
+
if (photo) {
|
|
2971
|
+
if (!photoEl) {
|
|
2972
|
+
photoEl = doc.createElement("img");
|
|
2973
|
+
photoEl.className = "gem-el-product-print-preview__photo";
|
|
2974
|
+
photoEl.alt = "";
|
|
2975
|
+
area.prepend(photoEl);
|
|
2976
|
+
}
|
|
2977
|
+
if (photoEl.getAttribute("src") !== photo) photoEl.src = photo;
|
|
2978
|
+
} else if (photoEl) {
|
|
2979
|
+
photoEl.remove();
|
|
2980
|
+
photoEl = null;
|
|
2981
|
+
}
|
|
2982
|
+
if (text2) {
|
|
2983
|
+
if (!textEl) {
|
|
2984
|
+
textEl = doc.createElement("p");
|
|
2985
|
+
textEl.className = "gem-el-product-print-preview__text";
|
|
2986
|
+
area.append(textEl);
|
|
2987
|
+
}
|
|
2988
|
+
textEl.textContent = text2;
|
|
2989
|
+
fit();
|
|
2990
|
+
} else if (textEl) {
|
|
2991
|
+
textEl.remove();
|
|
2992
|
+
textEl = null;
|
|
2993
|
+
}
|
|
2994
|
+
if (color2) root.style.setProperty("--gem-print-preview-color", color2);
|
|
2995
|
+
else root.style.removeProperty("--gem-print-preview-color");
|
|
2996
|
+
if (text2 || photo) root.setAttribute("data-gem-print-preview-state", "drawn");
|
|
2997
|
+
else root.removeAttribute("data-gem-print-preview-state");
|
|
2998
|
+
};
|
|
2999
|
+
on(scope, "gem:personalize:change", draw);
|
|
3000
|
+
if (typeof win.ResizeObserver === "function") new win.ResizeObserver(fit).observe(area);
|
|
3001
|
+
draw();
|
|
3002
|
+
onUnmount(() => {
|
|
3003
|
+
if (photoEl) photoEl.remove();
|
|
3004
|
+
if (textEl) textEl.remove();
|
|
3005
|
+
root.removeAttribute("data-gem-print-preview-state");
|
|
3006
|
+
root.style.removeProperty("--gem-print-preview-color");
|
|
3007
|
+
});
|
|
3008
|
+
});
|
|
3009
|
+
return count;
|
|
3010
|
+
}
|
|
3011
|
+
|
|
3012
|
+
// ../../packages/gem-framework/src/pod/delivery-estimate.js
|
|
3013
|
+
var DAY = 864e5;
|
|
3014
|
+
function parseDayRange(value) {
|
|
3015
|
+
if (typeof value === "number") return Number.isFinite(value) && value >= 0 ? [Math.floor(value), Math.floor(value)] : null;
|
|
3016
|
+
const m = /^\s*(\d{1,3})\s*(?:[-–—]\s*(\d{1,3}))?\s*$/.exec(String(value == null ? "" : value));
|
|
3017
|
+
if (!m) return null;
|
|
3018
|
+
const min = Number(m[1]);
|
|
3019
|
+
const max = m[2] === void 0 ? min : Number(m[2]);
|
|
3020
|
+
return max >= min ? [min, max] : null;
|
|
3021
|
+
}
|
|
3022
|
+
var localDay = (now) => Date.UTC(now.getFullYear(), now.getMonth(), now.getDate());
|
|
3023
|
+
var weekend = (day) => {
|
|
3024
|
+
const d = new Date(day).getUTCDay();
|
|
3025
|
+
return d === 0 || d === 6;
|
|
3026
|
+
};
|
|
3027
|
+
var iso = (day) => new Date(day).toISOString().slice(0, 10);
|
|
3028
|
+
function addDays(day, n, business) {
|
|
3029
|
+
let at = day;
|
|
3030
|
+
let left = n;
|
|
3031
|
+
while (left > 0) {
|
|
3032
|
+
at += DAY;
|
|
3033
|
+
if (!business || !weekend(at)) left -= 1;
|
|
3034
|
+
}
|
|
3035
|
+
return at;
|
|
3036
|
+
}
|
|
3037
|
+
function storeHour(now, timeZone) {
|
|
3038
|
+
if (timeZone) {
|
|
3039
|
+
try {
|
|
3040
|
+
const part = new Intl.DateTimeFormat("en-US", { timeZone, hour: "numeric", hourCycle: "h23" }).formatToParts(now).find((p) => p.type === "hour");
|
|
3041
|
+
if (part) return Number(part.value) % 24;
|
|
3042
|
+
} catch {
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
return now.getHours();
|
|
3046
|
+
}
|
|
3047
|
+
function deliveryEstimate(now, spec) {
|
|
3048
|
+
const production = parseDayRange(spec.production);
|
|
3049
|
+
const shipping = parseDayRange(spec.shipping);
|
|
3050
|
+
if (!production || !shipping || !(now instanceof Date) || Number.isNaN(now.getTime())) return null;
|
|
3051
|
+
const business = spec.businessDays !== false;
|
|
3052
|
+
const today = localDay(now);
|
|
3053
|
+
let start = today;
|
|
3054
|
+
const cutoff = Number(spec.cutoffHour);
|
|
3055
|
+
if (spec.cutoffHour !== void 0 && spec.cutoffHour !== null && spec.cutoffHour !== "" && Number.isInteger(cutoff) && cutoff >= 0 && cutoff <= 23 && storeHour(now, spec.timeZone) >= cutoff) start += DAY;
|
|
3056
|
+
while (business && weekend(start)) start += DAY;
|
|
3057
|
+
const ships = [addDays(start, production[0], business), addDays(start, production[1], business)];
|
|
3058
|
+
const arrives = [addDays(ships[0], shipping[0], business), addDays(ships[1], shipping[1], business)];
|
|
3059
|
+
return { ordered: iso(today), start: iso(start), ships: ships.map(iso), arrives: arrives.map(iso) };
|
|
3060
|
+
}
|
|
3061
|
+
function formatDeliveryRange(from, to, locale) {
|
|
3062
|
+
const at = (d) => /* @__PURE__ */ new Date(`${d}T00:00:00Z`);
|
|
3063
|
+
let fmt;
|
|
3064
|
+
try {
|
|
3065
|
+
fmt = new Intl.DateTimeFormat(locale || void 0, { month: "short", day: "numeric", timeZone: "UTC" });
|
|
3066
|
+
} catch {
|
|
3067
|
+
fmt = new Intl.DateTimeFormat(void 0, { month: "short", day: "numeric", timeZone: "UTC" });
|
|
3068
|
+
}
|
|
3069
|
+
if (!to || to === from) return fmt.format(at(from));
|
|
3070
|
+
return typeof fmt.formatRange === "function" ? fmt.formatRange(at(from), at(to)) : `${fmt.format(at(from))} \u2013 ${fmt.format(at(to))}`;
|
|
3071
|
+
}
|
|
3072
|
+
var attr4 = (el, name) => (el.getAttribute(name) || "").trim();
|
|
3073
|
+
function mountDeliveryEstimate(env) {
|
|
3074
|
+
const { document: doc, mark } = env;
|
|
3075
|
+
let count = 0;
|
|
3076
|
+
doc.querySelectorAll("[data-gem-delivery-estimate]").forEach((root) => {
|
|
3077
|
+
if (!mark(root, "delivery-estimate")) return;
|
|
3078
|
+
const estimate = deliveryEstimate(/* @__PURE__ */ new Date(), {
|
|
3079
|
+
production: attr4(root, "data-gem-delivery-production"),
|
|
3080
|
+
shipping: attr4(root, "data-gem-delivery-shipping"),
|
|
3081
|
+
businessDays: attr4(root, "data-gem-delivery-business-days") !== "false",
|
|
3082
|
+
cutoffHour: attr4(root, "data-gem-delivery-cutoff-hour"),
|
|
3083
|
+
timeZone: attr4(root, "data-gem-delivery-timezone")
|
|
3084
|
+
});
|
|
3085
|
+
if (!estimate) return;
|
|
3086
|
+
const locale = attr4(root, "data-gem-delivery-locale") || doc.documentElement && doc.documentElement.lang || void 0;
|
|
3087
|
+
const ranges = { ordered: [estimate.ordered, estimate.ordered], ships: estimate.ships, arrives: estimate.arrives };
|
|
3088
|
+
root.querySelectorAll("[data-gem-delivery-date]").forEach((out) => {
|
|
3089
|
+
const range = ranges[attr4(out, "data-gem-delivery-date")];
|
|
3090
|
+
if (!range) return;
|
|
3091
|
+
out.textContent = formatDeliveryRange(range[0], range[1], locale);
|
|
3092
|
+
out.setAttribute("data-gem-delivery-from", range[0]);
|
|
3093
|
+
out.setAttribute("data-gem-delivery-to", range[1]);
|
|
3094
|
+
});
|
|
3095
|
+
root.setAttribute("data-gem-delivery-state", "local");
|
|
3096
|
+
count += 1;
|
|
3097
|
+
});
|
|
3098
|
+
return count;
|
|
3099
|
+
}
|
|
3100
|
+
|
|
3101
|
+
// ../../packages/gem-framework/src/pod-engine.ts
|
|
3102
|
+
var P = ".gem-el-product-personalization";
|
|
3103
|
+
var V = ".gem-el-product-print-preview";
|
|
3104
|
+
var D = ".gem-el-product-delivery-estimate";
|
|
3105
|
+
var S = ".gem-el-product-design-switcher";
|
|
3106
|
+
var GEM_POD_BUYBOX_CSS_LINES = [
|
|
3107
|
+
// product-personalization: one field per element. The wrapper is the hook; a field whose condition fails is [hidden].
|
|
3108
|
+
`${P} { display: grid; gap: var(--gem-space-2xs); min-width: 0; }`,
|
|
3109
|
+
`${P}[hidden] { display: none; }`,
|
|
3110
|
+
`${P}__label { display: grid; gap: var(--gem-space-2xs); }`,
|
|
3111
|
+
`${P}__title { font-size: var(--gem-text-sm); font-weight: 600; }`,
|
|
3112
|
+
`${P}__required { color: var(--gem-color-danger); }`,
|
|
3113
|
+
`${P}__input { box-sizing: border-box; width: 100%; min-height: var(--gem-tap-min); padding: var(--gem-space-xs) var(--gem-space-sm); font: inherit; color: var(--gem-color-text); background: var(--gem-color-background); border: var(--gem-stroke, 1px) solid var(--gem-color-border); border-radius: var(--gem-radius-md); }`,
|
|
3114
|
+
`textarea${P}__input { min-height: calc(var(--gem-tap-min) * 2); resize: vertical; }`,
|
|
3115
|
+
`${P}__meta { display: flex; gap: var(--gem-space-xs); align-items: baseline; }`,
|
|
3116
|
+
`${P}__help { margin: 0; color: var(--gem-color-muted); font-size: var(--gem-text-xs); }`,
|
|
3117
|
+
`${P}__count { margin-left: auto; color: var(--gem-color-muted); font-size: var(--gem-text-xs); font-variant-numeric: tabular-nums; }`,
|
|
3118
|
+
// swatch and image-choice: radios inside labels; the radio stays focusable, its picture shows the state.
|
|
3119
|
+
`${P}__options { display: flex; flex-wrap: wrap; gap: var(--gem-space-xs); margin: 0; padding: 0; border: 0; min-width: 0; }`,
|
|
3120
|
+
`${P}__option { position: relative; display: inline-grid; justify-items: center; gap: var(--gem-space-2xs); font-size: var(--gem-text-xs); cursor: pointer; }`,
|
|
3121
|
+
`${P}__option input { position: absolute; inset: 0; margin: 0; opacity: 0; cursor: pointer; }`,
|
|
3122
|
+
`${P}__swatch { display: block; width: var(--gem-tap-min); height: var(--gem-tap-min); border-radius: var(--gem-radius-pill); background: var(--gem-personalize-swatch, var(--gem-color-surface)); border: var(--gem-stroke, 1px) solid var(--gem-color-border); }`,
|
|
3123
|
+
`${P}__choice-image { display: block; width: calc(var(--gem-tap-min) * 1.75); aspect-ratio: 1; object-fit: cover; border-radius: var(--gem-radius-md); border: var(--gem-stroke, 1px) solid var(--gem-color-border); }`,
|
|
3124
|
+
`${P}__option input:checked ~ ${P}__swatch, ${P}__option input:checked ~ ${P}__choice-image { box-shadow: 0 0 0 var(--gem-stroke-thick, 2px) var(--gem-color-background), 0 0 0 calc(var(--gem-stroke-thick, 2px) * 2) var(--gem-color-text); }`,
|
|
3125
|
+
`${P}__option input:focus-visible ~ ${P}__swatch, ${P}__option input:focus-visible ~ ${P}__choice-image { outline: var(--gem-focus-ring); outline-offset: calc(var(--gem-focus-offset) + var(--gem-stroke-thick, 2px) * 2); }`,
|
|
3126
|
+
`${P}__checkbox { display: inline-flex; align-items: center; gap: var(--gem-space-xs); min-height: var(--gem-tap-min); cursor: pointer; }`,
|
|
3127
|
+
// upload: a dashed drop box around a visually hidden (still focusable) file input, a thumbnail once a photo is picked.
|
|
3128
|
+
`${P}__upload { position: relative; display: grid; justify-items: center; gap: var(--gem-space-2xs); padding: var(--gem-space-md); text-align: center; background: var(--gem-color-surface); border: var(--gem-stroke, 1px) dashed var(--gem-color-border); border-radius: var(--gem-radius-md); cursor: pointer; }`,
|
|
3129
|
+
`${P}__upload:focus-within { outline: var(--gem-focus-ring); outline-offset: var(--gem-focus-offset); }`,
|
|
3130
|
+
`${P}__file { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }`,
|
|
3131
|
+
`${P}__thumb { display: block; width: calc(var(--gem-tap-min) * 2); aspect-ratio: 1; object-fit: cover; border-radius: var(--gem-radius-sm); }`,
|
|
3132
|
+
`${P}__thumb[hidden], ${P}__thumb:not([src]) { display: none; }`,
|
|
3133
|
+
`${P}__privacy { margin: 0; color: var(--gem-color-muted); font-size: var(--gem-text-xs); }`,
|
|
3134
|
+
`${P}__error { margin: 0; color: var(--gem-color-danger); font-size: var(--gem-text-xs); }`,
|
|
3135
|
+
`${P}__error:empty, ${P}__error[hidden] { display: none; }`,
|
|
3136
|
+
// product-print-preview: the product's current image, and the print area on it (percent of the image, from the element's
|
|
3137
|
+
// area_* props or the gemboss.print_area metafield). Empty until the shopper types or uploads.
|
|
3138
|
+
`${V} { position: relative; display: block; overflow: hidden; border-radius: var(--gem-media-radius, var(--gem-radius-md)); }`,
|
|
3139
|
+
`${V}__media { display: block; width: 100%; height: auto; }`,
|
|
3140
|
+
`${V}__area { position: absolute; left: calc(var(--gem-print-preview-x, 20) * 1%); top: calc(var(--gem-print-preview-y, 35) * 1%); width: calc(var(--gem-print-preview-w, 60) * 1%); height: calc(var(--gem-print-preview-h, 20) * 1%); display: grid; place-items: center; overflow: hidden; pointer-events: none; container-type: size; }`,
|
|
3141
|
+
`${V}[data-gem-print-preview-shape="circle"] ${V}__area { border-radius: 50%; }`,
|
|
3142
|
+
`${V}[data-gem-print-preview-font="body"] { --gem-print-preview-font: var(--gem-font-body); }`,
|
|
3143
|
+
`${V}[data-gem-print-preview-font="accent"] { --gem-print-preview-font: var(--gem-font-accent, var(--gem-font-heading)); }`,
|
|
3144
|
+
// product-delivery-estimate: three steps on a line (timeline), or one sentence (line). Dates are the server's for its today
|
|
3145
|
+
// until the plugin rewrites them in the shopper's.
|
|
3146
|
+
`${D} { display: grid; gap: var(--gem-space-xs); font-size: var(--gem-text-sm); }`,
|
|
3147
|
+
`${D}__steps { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--gem-space-sm); margin: 0; padding: 0; list-style: none; }`,
|
|
3148
|
+
`${D}__step { position: relative; display: grid; justify-items: center; gap: var(--gem-space-2xs); text-align: center; }`,
|
|
3149
|
+
`${D}__step::before { content: ""; width: 0.75em; height: 0.75em; border-radius: var(--gem-radius-pill); background: var(--gem-color-accent); }`,
|
|
3150
|
+
`${D}__step + ${D}__step::after { content: ""; position: absolute; top: 0.375em; right: calc(50% + 0.75em); width: calc(100% - 1.5em + var(--gem-space-sm)); height: var(--gem-stroke, 1px); background: var(--gem-color-border); }`,
|
|
3151
|
+
`${D}__label { color: var(--gem-color-muted); font-size: var(--gem-text-xs); }`,
|
|
3152
|
+
`${D}__date { font-weight: 600; font-variant-numeric: tabular-nums; }`,
|
|
3153
|
+
`${D}__note { margin: 0; color: var(--gem-color-muted); font-size: var(--gem-text-xs); }`,
|
|
3154
|
+
`${D}[data-gem-delivery-style="line"] ${D}__steps { display: flex; flex-wrap: wrap; gap: var(--gem-space-2xs) var(--gem-space-sm); }`,
|
|
3155
|
+
`${D}[data-gem-delivery-style="line"] ${D}__step { display: inline-flex; gap: var(--gem-space-2xs); text-align: start; }`,
|
|
3156
|
+
`${D}[data-gem-delivery-style="line"] ${D}__step::before, ${D}[data-gem-delivery-style="line"] ${D}__step::after { content: none; }`,
|
|
3157
|
+
// product-design-switcher: one link per product that shares the design (pills, or cards with the sibling's photo).
|
|
3158
|
+
`${S} { display: grid; gap: var(--gem-space-xs); }`,
|
|
3159
|
+
`${S}__list { display: flex; flex-wrap: wrap; gap: var(--gem-space-xs); margin: 0; padding: 0; list-style: none; }`,
|
|
3160
|
+
`${S}__item { display: inline-flex; align-items: center; gap: var(--gem-space-2xs); min-height: var(--gem-tap-min); padding: var(--gem-space-2xs) var(--gem-space-sm); font-size: var(--gem-text-sm); color: var(--gem-color-text); text-decoration: none; background: var(--gem-color-background); border: var(--gem-stroke, 1px) solid var(--gem-color-border); border-radius: var(--gem-radius-pill); }`,
|
|
3161
|
+
`${S}__item:hover { border-color: var(--gem-color-text); }`,
|
|
3162
|
+
`${S}__item:focus-visible { outline: var(--gem-focus-ring); outline-offset: var(--gem-focus-offset); }`,
|
|
3163
|
+
`${S}__item[aria-current="page"] { border-color: var(--gem-color-text); box-shadow: inset 0 0 0 var(--gem-stroke, 1px) var(--gem-color-text); }`,
|
|
3164
|
+
`${S}__price { color: var(--gem-color-muted); font-variant-numeric: tabular-nums; }`,
|
|
3165
|
+
`${S}__image { display: none; }`,
|
|
3166
|
+
`${S}[data-gem-design-switcher-style="cards"] ${S}__list { display: grid; grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr)); }`,
|
|
3167
|
+
`${S}[data-gem-design-switcher-style="cards"] ${S}__item { flex-direction: column; align-items: stretch; padding: var(--gem-space-xs); border-radius: var(--gem-radius-md); }`,
|
|
3168
|
+
`${S}[data-gem-design-switcher-style="cards"] ${S}__image { display: block; width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: var(--gem-radius-sm); }`,
|
|
3169
|
+
`${S}[data-gem-design-switcher-style="cards"] ${S}__label { font-weight: 600; }`
|
|
3170
|
+
];
|
|
3171
|
+
var GEM_PERSONALIZE_RUNTIME_CSS_LINES = [
|
|
3172
|
+
`${P}[data-gem-personalize-invalid] ${P}__input, ${P}[data-gem-personalize-invalid] ${P}__upload, ${P}[data-gem-personalize-invalid] ${P}__swatch, ${P}[data-gem-personalize-invalid] ${P}__choice-image { border-color: var(--gem-color-danger); }`,
|
|
3173
|
+
`${P}[data-gem-personalize-state="uploading"] ${P}__upload { cursor: progress; opacity: 0.7; }`,
|
|
3174
|
+
`${P}__count[data-gem-personalize-full] { color: var(--gem-color-text); font-weight: 600; }`
|
|
3175
|
+
];
|
|
3176
|
+
var GEM_PRINT_PREVIEW_RUNTIME_CSS_LINES = [
|
|
3177
|
+
// The words: the theme font the element names, the colour it follows, as many lines as it allows, sized by the area's
|
|
3178
|
+
// height (container units); a word wider than the area is scaled down by the plugin with a transform, never re-laid out.
|
|
3179
|
+
`${V}__text { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: var(--gem-print-preview-lines, 2); max-width: 100%; margin: 0; overflow: hidden; overflow-wrap: anywhere; font-family: var(--gem-print-preview-font, var(--gem-font-heading)); font-size: calc(82cqh / var(--gem-print-preview-lines, 2)); line-height: 1.1; color: var(--gem-print-preview-color, var(--gem-color-text)); text-align: center; transform-origin: center; }`,
|
|
3180
|
+
`${V}[data-gem-print-preview-align="left"] ${V}__area { place-items: center start; }`,
|
|
3181
|
+
`${V}[data-gem-print-preview-align="left"] ${V}__text { text-align: start; transform-origin: left center; }`,
|
|
3182
|
+
`${V}[data-gem-print-preview-align="right"] ${V}__area { place-items: center end; }`,
|
|
3183
|
+
`${V}[data-gem-print-preview-align="right"] ${V}__text { text-align: end; transform-origin: right center; }`,
|
|
3184
|
+
`${V}__photo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }`
|
|
3185
|
+
];
|
|
3186
|
+
var GEM_POD_BUYBOX_CSS = GEM_POD_BUYBOX_CSS_LINES.join("\n") + "\n";
|
|
3187
|
+
var GEM_PERSONALIZE_RUNTIME_CSS = GEM_PERSONALIZE_RUNTIME_CSS_LINES.join("\n") + "\n";
|
|
3188
|
+
var GEM_PRINT_PREVIEW_RUNTIME_CSS = GEM_PRINT_PREVIEW_RUNTIME_CSS_LINES.join("\n") + "\n";
|
|
3189
|
+
var GEM_POD_CLASSES = [
|
|
3190
|
+
...["", "__label", "__title", "__required", "__input", "__meta", "__help", "__count", "__options", "__option", "__swatch", "__choice-image", "__checkbox", "__upload", "__file", "__thumb", "__privacy", "__error"].map((part) => `gem-el-product-personalization${part}`),
|
|
3191
|
+
...["", "__media", "__area", "__text", "__photo"].map((part) => `gem-el-product-print-preview${part}`),
|
|
3192
|
+
...["", "__steps", "__step", "__label", "__date", "__note"].map((part) => `gem-el-product-delivery-estimate${part}`),
|
|
3193
|
+
...["", "__list", "__item", "__label", "__price", "__image"].map((part) => `gem-el-product-design-switcher${part}`)
|
|
3194
|
+
];
|
|
3195
|
+
var GEM_POD_CSS_VARIABLES = [
|
|
3196
|
+
"--gem-personalize-css",
|
|
3197
|
+
"--gem-personalize-swatch",
|
|
3198
|
+
"--gem-print-preview-css",
|
|
3199
|
+
"--gem-print-preview-x",
|
|
3200
|
+
"--gem-print-preview-y",
|
|
3201
|
+
"--gem-print-preview-w",
|
|
3202
|
+
"--gem-print-preview-h",
|
|
3203
|
+
"--gem-print-preview-lines",
|
|
3204
|
+
"--gem-print-preview-color",
|
|
3205
|
+
"--gem-print-preview-font",
|
|
3206
|
+
// No accent font token in gem-framework yet: a skin may set it, the preview falls back to the heading font.
|
|
3207
|
+
"--gem-font-accent"
|
|
3208
|
+
];
|
|
3209
|
+
var GEM_POD_RUNTIME_FILES = ["pod/core.js", "pod/personalize.js", "pod/print-preview.js", "pod/delivery-estimate.js"];
|
|
3210
|
+
var GEM_POD_PLUGINS = [
|
|
3211
|
+
{ key: "personalize", hook: "[data-gem-personalize]", when: "viewport", budget: { gzip: 6144 } },
|
|
3212
|
+
{ key: "print-preview", hook: "[data-gem-print-preview]", when: "interaction", budget: { gzip: 6144 } },
|
|
3213
|
+
{ key: "delivery-estimate", hook: "[data-gem-delivery-estimate]", when: "viewport", budget: { gzip: 6144 } }
|
|
3214
|
+
];
|
|
3215
|
+
var MOUNTS = {
|
|
3216
|
+
personalize: { name: "mountPersonalize", file: "personalize", css: GEM_PERSONALIZE_RUNTIME_CSS },
|
|
3217
|
+
"print-preview": { name: "mountPrintPreview", file: "print-preview", css: GEM_PRINT_PREVIEW_RUNTIME_CSS },
|
|
3218
|
+
"delivery-estimate": { name: "mountDeliveryEstimate", file: "delivery-estimate", css: "" }
|
|
3219
|
+
};
|
|
3220
|
+
function gemPodChunks() {
|
|
3221
|
+
const chunks = {};
|
|
3222
|
+
for (const { key, hook, when } of GEM_POD_PLUGINS) {
|
|
3223
|
+
const { name, file, css } = MOUNTS[key];
|
|
3224
|
+
chunks[key] = {
|
|
3225
|
+
hook,
|
|
3226
|
+
when,
|
|
3227
|
+
imports: [`import { ${name} } from "../pod/${file}.js";`],
|
|
3228
|
+
body: [
|
|
3229
|
+
` registerPlugin('${key}', ({ api }) => {`,
|
|
3230
|
+
" const on = (el, type, fn, opt) => { if (canListen(el)) el.addEventListener(type, fn, opt); };",
|
|
3231
|
+
` const count = ${name}({ window, document, on, mark, api${css ? `, css: ${JSON.stringify(compactCss(css))}` : ""} }, onUnmount);`,
|
|
3232
|
+
` api.emit('gem:${key}:ready', { source: 'gem-runtime', count });`,
|
|
3233
|
+
" });"
|
|
3234
|
+
]
|
|
3235
|
+
};
|
|
3236
|
+
}
|
|
3237
|
+
return chunks;
|
|
3238
|
+
}
|
|
3239
|
+
|
|
2459
3240
|
// ../../packages/gem-framework/src/manifest.ts
|
|
2460
3241
|
var gemFrameworkPrimitiveGroups = [
|
|
2461
3242
|
{
|
|
@@ -2657,6 +3438,13 @@ var gemFrameworkPrimitiveGroups = [
|
|
|
2657
3438
|
group: "motion",
|
|
2658
3439
|
classes: [...GEM_MOTION_CLASSES]
|
|
2659
3440
|
},
|
|
3441
|
+
{
|
|
3442
|
+
// The Boss Theme POD buy box (pod-engine.ts, 0.20.0): the four elements gemsite renders (personalization v2, print
|
|
3443
|
+
// preview, delivery estimate, design switcher) and what their plugins draw. Styled by dist/pod-buybox.css and the
|
|
3444
|
+
// personalize / print-preview chunks' own sheets, never by gem-framework.css.
|
|
3445
|
+
group: "pod",
|
|
3446
|
+
classes: [...GEM_POD_CLASSES]
|
|
3447
|
+
},
|
|
2660
3448
|
{
|
|
2661
3449
|
group: "sections",
|
|
2662
3450
|
classes: [
|
|
@@ -2899,6 +3687,25 @@ var gemFrameworkRuntimePlugins = [
|
|
|
2899
3687
|
key: "rail",
|
|
2900
3688
|
load_strategy: "idle",
|
|
2901
3689
|
events: ["gem:rail:ready", "gem:motion:front", "gem:motion:fallback"]
|
|
3690
|
+
},
|
|
3691
|
+
// The Boss Theme POD buy box (0.20.0, pod-engine.ts): lazy chunks like the motion effects. idle schedules gem-runtime.js's
|
|
3692
|
+
// stub, which imports the chunk when its hook comes near (personalize, delivery-estimate) or is first touched (print-preview),
|
|
3693
|
+
// never on a page without one. personalize holds the product's add (gem:cart:add / add-many, a form submit) until every
|
|
3694
|
+
// required field is filled, and answers a held add with gem:cart:add-failed.
|
|
3695
|
+
{
|
|
3696
|
+
key: "personalize",
|
|
3697
|
+
load_strategy: "idle",
|
|
3698
|
+
events: ["gem:personalize:ready", "gem:personalize:change", "gem:personalize:blocked", "gem:cart:add-failed"]
|
|
3699
|
+
},
|
|
3700
|
+
{
|
|
3701
|
+
key: "print-preview",
|
|
3702
|
+
load_strategy: "idle",
|
|
3703
|
+
events: ["gem:print-preview:ready"]
|
|
3704
|
+
},
|
|
3705
|
+
{
|
|
3706
|
+
key: "delivery-estimate",
|
|
3707
|
+
load_strategy: "idle",
|
|
3708
|
+
events: ["gem:delivery-estimate:ready"]
|
|
2902
3709
|
}
|
|
2903
3710
|
];
|
|
2904
3711
|
var gemFrameworkValidationRules = [
|
|
@@ -3072,7 +3879,7 @@ var GEM_POPUP_SHELL_CSS = GEM_POPUP_SHELL_CSS_LINES.join("\n");
|
|
|
3072
3879
|
|
|
3073
3880
|
// ../../packages/gem-framework/src/primitives.ts
|
|
3074
3881
|
var dividerMask = (path) => `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 10' preserveAspectRatio='none'%3E%3Cpath fill='%23fff' d='${path}'/%3E%3C/svg%3E")`;
|
|
3075
|
-
var knownValues = (
|
|
3882
|
+
var knownValues = (attr5, values) => `:where(${values.map((v) => `[${attr5}='${v}']`).join(", ")})`;
|
|
3076
3883
|
var DIVIDER_SHAPES = Object.keys(GEM_SECTION_DIVIDER_PATHS);
|
|
3077
3884
|
var GEM_DIVIDER_CSS_LINES = [
|
|
3078
3885
|
// The clip is WIDENED, not removed. `.gem-section` sets `overflow: clip` and the wedge is drawn
|
|
@@ -7444,7 +8251,10 @@ var runtimePluginReadyEvents = {
|
|
|
7444
8251
|
bundle: "gem:bundle:ready",
|
|
7445
8252
|
"size-guide": "gem:size-guide:ready",
|
|
7446
8253
|
orbit: "gem:orbit:ready",
|
|
7447
|
-
rail: "gem:rail:ready"
|
|
8254
|
+
rail: "gem:rail:ready",
|
|
8255
|
+
personalize: "gem:personalize:ready",
|
|
8256
|
+
"print-preview": "gem:print-preview:ready",
|
|
8257
|
+
"delivery-estimate": "gem:delivery-estimate:ready"
|
|
7448
8258
|
};
|
|
7449
8259
|
var cartDrawerPluginRegistrationLines = [
|
|
7450
8260
|
" registerPlugin('cart-drawer', ({ api }) => {",
|
|
@@ -7992,11 +8802,14 @@ var legacyQuickViewLines = [
|
|
|
7992
8802
|
// page's api.quickView.
|
|
7993
8803
|
" if (surface || !api.quickView) api.quickView = { open: (detail = {}) => openSurface({ ...detail }), close };"
|
|
7994
8804
|
];
|
|
7995
|
-
var runtimeChunks =
|
|
7996
|
-
|
|
7997
|
-
|
|
7998
|
-
|
|
7999
|
-
|
|
8805
|
+
var runtimeChunks = {
|
|
8806
|
+
...gemMotionChunks({
|
|
8807
|
+
hook: '[data-gem-quick-view], [data-gem-action="quick-view"]',
|
|
8808
|
+
lines: legacyQuickViewLines,
|
|
8809
|
+
readyDetail: "count: bound + count, hasSurface: Boolean(surface)"
|
|
8810
|
+
}),
|
|
8811
|
+
...gemPodChunks()
|
|
8812
|
+
};
|
|
8000
8813
|
var promoPopupPluginRegistrationLines = [
|
|
8001
8814
|
" registerPlugin('promo-popup', ({ api }) => {",
|
|
8002
8815
|
// Only the explicit hook opts in. Every promo-popup section carries the archetype class .gem-promo-popup, and
|
|
@@ -8586,7 +9399,10 @@ var runtimePluginSourceLines = [
|
|
|
8586
9399
|
["bundle", bundlePluginRegistrationLines],
|
|
8587
9400
|
["size-guide", sizeGuidePluginRegistrationLines],
|
|
8588
9401
|
["orbit", runtimeChunks.orbit.body],
|
|
8589
|
-
["rail", runtimeChunks.rail.body]
|
|
9402
|
+
["rail", runtimeChunks.rail.body],
|
|
9403
|
+
["personalize", runtimeChunks.personalize.body],
|
|
9404
|
+
["print-preview", runtimeChunks["print-preview"].body],
|
|
9405
|
+
["delivery-estimate", runtimeChunks["delivery-estimate"].body]
|
|
8590
9406
|
];
|
|
8591
9407
|
var noopPluginRegistrationLine = (key, readyEvent) => ` registerNoopPlugin(${JSON.stringify(key)}, ${JSON.stringify(readyEvent)});`;
|
|
8592
9408
|
var chunkStubLine = (key, chunk) => ` registerPlugin(${JSON.stringify(key)}, ({ api }) => loadChunk(${JSON.stringify(key)}, ${JSON.stringify(chunk.hook)}, ${JSON.stringify(chunk.when)}, api));`;
|
|
@@ -8981,7 +9797,10 @@ var canonicalCssVariableNames = /* @__PURE__ */ new Set([
|
|
|
8981
9797
|
// internals (--gem-orbit-a, --gem-rail-t) included, and sets --gem-orbit-i / -n, the settings and --gem-motion-tint.
|
|
8982
9798
|
...new Set([GEM_ORBIT_BASE_CSS, GEM_RAIL_BASE_CSS, GEM_ORBIT_RUNTIME_CSS, GEM_RAIL_RUNTIME_CSS, GEM_QUICK_VIEW_CSS].join("\n").match(/--gem-[a-z0-9-]+/g) ?? []),
|
|
8983
9799
|
// …and every setting an effect reads from a CSS variable (`css: true`): a section sets it in its stylesheet, per breakpoint.
|
|
8984
|
-
...gemMotionEffectList.flatMap((effect) => Object.entries(effect.settings).filter(([, setting]) => setting.css).map(([name]) => `--gem-${effect.key}-${name}`))
|
|
9800
|
+
...gemMotionEffectList.flatMap((effect) => Object.entries(effect.settings).filter(([, setting]) => setting.css).map(([name]) => `--gem-${effect.key}-${name}`)),
|
|
9801
|
+
// The POD buy box's (pod-engine.ts, 0.20.0): the print area and lines gemsite sets on a preview, the swatch colour of an
|
|
9802
|
+
// option, the chunks' sentinels, and --gem-font-accent, the accent font a skin may set (the preview falls back to heading).
|
|
9803
|
+
...GEM_POD_CSS_VARIABLES
|
|
8985
9804
|
]);
|
|
8986
9805
|
var TOKENIZED_PROPERTIES = [
|
|
8987
9806
|
"background",
|
|
@@ -10353,7 +11172,14 @@ export {
|
|
|
10353
11172
|
GEM_ORBIT_BASE_CSS,
|
|
10354
11173
|
GEM_ORBIT_RUNTIME_CSS,
|
|
10355
11174
|
GEM_PART_CLASS,
|
|
11175
|
+
GEM_PERSONALIZE_RUNTIME_CSS,
|
|
11176
|
+
GEM_POD_BUYBOX_CSS,
|
|
11177
|
+
GEM_POD_CLASSES,
|
|
11178
|
+
GEM_POD_CSS_VARIABLES,
|
|
11179
|
+
GEM_POD_PLUGINS,
|
|
11180
|
+
GEM_POD_RUNTIME_FILES,
|
|
10356
11181
|
GEM_POPUP_SHELL_CSS,
|
|
11182
|
+
GEM_PRINT_PREVIEW_RUNTIME_CSS,
|
|
10357
11183
|
GEM_QUICK_VIEW_CSS,
|
|
10358
11184
|
GEM_RAIL_BASE_CSS,
|
|
10359
11185
|
GEM_RAIL_RUNTIME_CSS,
|
|
@@ -10370,6 +11196,7 @@ export {
|
|
|
10370
11196
|
MODERN_MINERAL_HOMEPAGE_LINEAGE_SECTIONS,
|
|
10371
11197
|
MODERN_MINERAL_HOMEPAGE_PACK_ID,
|
|
10372
11198
|
MODERN_MINERAL_STYLE_PACK_ID,
|
|
11199
|
+
PERSONALIZE_MESSAGES,
|
|
10373
11200
|
aaLabelColor,
|
|
10374
11201
|
aaTextColor,
|
|
10375
11202
|
auditGemFrameworkClassName,
|
|
@@ -10397,8 +11224,11 @@ export {
|
|
|
10397
11224
|
defaultGemTokens,
|
|
10398
11225
|
defaultSelection,
|
|
10399
11226
|
defineEffect,
|
|
11227
|
+
deliveryEstimate,
|
|
10400
11228
|
deriveFormSetId,
|
|
11229
|
+
fileAccepted,
|
|
10401
11230
|
follow,
|
|
11231
|
+
formatDeliveryRange,
|
|
10402
11232
|
gemBandSchemeColors,
|
|
10403
11233
|
gemBandSchemeCssVars,
|
|
10404
11234
|
gemBandSchemeDeclarations,
|
|
@@ -10428,6 +11258,7 @@ export {
|
|
|
10428
11258
|
gemPhoneTokenTarget,
|
|
10429
11259
|
gemPlanFormSet,
|
|
10430
11260
|
gemPlanShapeKnobs,
|
|
11261
|
+
gemPodChunks,
|
|
10431
11262
|
gemRailBaseCss,
|
|
10432
11263
|
gemRuntimePluginModuleKeys,
|
|
10433
11264
|
gemRuntimePluginSources,
|
|
@@ -10444,14 +11275,23 @@ export {
|
|
|
10444
11275
|
modernMineralHomepageLineageSections,
|
|
10445
11276
|
motionCssVarAliases,
|
|
10446
11277
|
motionCssVarName,
|
|
11278
|
+
mountDeliveryEstimate,
|
|
11279
|
+
mountPersonalize,
|
|
11280
|
+
mountPrintPreview,
|
|
10447
11281
|
normalizeGemTokens,
|
|
10448
11282
|
normalizeHex,
|
|
10449
11283
|
offerEngineRuntimeScript,
|
|
10450
11284
|
optionalGemTokenKeys,
|
|
10451
11285
|
orbitEffect,
|
|
11286
|
+
parseDayRange,
|
|
11287
|
+
parsePersonalizeWhen,
|
|
11288
|
+
personalizeWhenHolds,
|
|
10452
11289
|
planToRuntimeBootstrapConfig,
|
|
11290
|
+
printPreviewContent,
|
|
10453
11291
|
quickViewEffect,
|
|
10454
11292
|
railEffect,
|
|
11293
|
+
readField,
|
|
11294
|
+
readScope,
|
|
10455
11295
|
renderGemFrameworkManifest,
|
|
10456
11296
|
renderGemPreviewDocument,
|
|
10457
11297
|
renderGemSectionMarkup,
|
|
@@ -10466,9 +11306,11 @@ export {
|
|
|
10466
11306
|
ringDelta,
|
|
10467
11307
|
ringPose,
|
|
10468
11308
|
scopeMotionCss,
|
|
11309
|
+
scopeOf,
|
|
10469
11310
|
scrollProgress,
|
|
10470
11311
|
slugify,
|
|
10471
11312
|
spring,
|
|
11313
|
+
storeHour,
|
|
10472
11314
|
throwTarget,
|
|
10473
11315
|
toCssVars,
|
|
10474
11316
|
toMotionCssVars,
|