@lime-bundles/widget 2.4.1 → 2.5.1
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/dist/index.cjs +834 -106
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +753 -25
- package/dist/index.js.map +1 -1
- package/dist/lime-bundle.global.js +354 -26
- package/dist/lime-bundle.global.js.map +1 -1
- package/docs/css-variables.md +3 -0
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -26,16 +26,393 @@ __export(index_exports, {
|
|
|
26
26
|
module.exports = __toCommonJS(index_exports);
|
|
27
27
|
|
|
28
28
|
// src/lime-bundle.ts
|
|
29
|
-
var
|
|
29
|
+
var import_core7 = require("@lime-bundles/core");
|
|
30
30
|
|
|
31
31
|
// src/renderers/fixed.ts
|
|
32
|
-
var
|
|
32
|
+
var import_core5 = require("@lime-bundles/core");
|
|
33
33
|
|
|
34
|
-
// src/
|
|
34
|
+
// src/dropdown/bind-dropdown.ts
|
|
35
35
|
var import_core = require("@lime-bundles/core");
|
|
36
|
+
var { computePosition, emptyTypeAheadState, handleKey, pushTypeAheadChar } = import_core.dropdown;
|
|
37
|
+
var ITEM_HEIGHT_PX = 32;
|
|
38
|
+
var LIST_PAD_Y = 8;
|
|
39
|
+
var MAX_VISIBLE_ITEMS = 8;
|
|
40
|
+
var openInstances = [];
|
|
41
|
+
function closeOutsideEvent(event) {
|
|
42
|
+
const path = event.composedPath();
|
|
43
|
+
for (let i = openInstances.length - 1; i >= 0; i--) {
|
|
44
|
+
const inst = openInstances[i];
|
|
45
|
+
if (!path.includes(inst.shell) && !path.includes(inst.listbox)) {
|
|
46
|
+
inst.close();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function onDocResize() {
|
|
51
|
+
for (let i = openInstances.length - 1; i >= 0; i--) openInstances[i].close();
|
|
52
|
+
}
|
|
53
|
+
var docListenersAttached = false;
|
|
54
|
+
function attachDocumentListeners() {
|
|
55
|
+
if (docListenersAttached) return;
|
|
56
|
+
document.addEventListener("pointerdown", closeOutsideEvent, true);
|
|
57
|
+
window.addEventListener("scroll", closeOutsideEvent, true);
|
|
58
|
+
window.addEventListener("resize", onDocResize);
|
|
59
|
+
docListenersAttached = true;
|
|
60
|
+
}
|
|
61
|
+
function detachDocumentListeners() {
|
|
62
|
+
if (!docListenersAttached || openInstances.length > 0) return;
|
|
63
|
+
document.removeEventListener("pointerdown", closeOutsideEvent, true);
|
|
64
|
+
window.removeEventListener("scroll", closeOutsideEvent, true);
|
|
65
|
+
window.removeEventListener("resize", onDocResize);
|
|
66
|
+
docListenersAttached = false;
|
|
67
|
+
}
|
|
68
|
+
function readOptions(select) {
|
|
69
|
+
const out = [];
|
|
70
|
+
for (let i = 0; i < select.options.length; i++) {
|
|
71
|
+
const o = select.options[i];
|
|
72
|
+
out.push({ disabled: o.disabled, label: o.textContent || o.value });
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
function firstEnabled(opts) {
|
|
77
|
+
for (let i = 0; i < opts.length; i++) if (!opts[i].disabled) return i;
|
|
78
|
+
return -1;
|
|
79
|
+
}
|
|
80
|
+
var VARIANT_SELECT_CLASSES = [
|
|
81
|
+
"lb-bundle-variant-select",
|
|
82
|
+
"lb-mix-match__variant-select"
|
|
83
|
+
];
|
|
84
|
+
var BIND_SELECTOR = VARIANT_SELECT_CLASSES.map(
|
|
85
|
+
(c) => `select.${c}:not(.lb-dropdown-state)`
|
|
86
|
+
).join(", ");
|
|
87
|
+
function bindDropdown(select) {
|
|
88
|
+
const slot = select;
|
|
89
|
+
if (select.classList.contains("lb-dropdown-state")) {
|
|
90
|
+
return slot.__lbDropdownInstance ?? null;
|
|
91
|
+
}
|
|
92
|
+
const doc = select.ownerDocument;
|
|
93
|
+
const rootNode = select.getRootNode();
|
|
94
|
+
const labelText = select.getAttribute("aria-label") ?? "";
|
|
95
|
+
const idBase = `lb-dd-${Math.random().toString(36).slice(2, 9)}`;
|
|
96
|
+
select.classList.add("lb-dropdown-state");
|
|
97
|
+
select.setAttribute("aria-hidden", "true");
|
|
98
|
+
select.setAttribute("tabindex", "-1");
|
|
99
|
+
const shell = doc.createElement("div");
|
|
100
|
+
shell.className = "lb-dropdown";
|
|
101
|
+
shell.setAttribute("data-lb-dropdown", "");
|
|
102
|
+
const trigger = doc.createElement("button");
|
|
103
|
+
trigger.type = "button";
|
|
104
|
+
trigger.className = "lb-dropdown-trigger";
|
|
105
|
+
trigger.setAttribute("role", "combobox");
|
|
106
|
+
trigger.setAttribute("aria-haspopup", "listbox");
|
|
107
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
108
|
+
const listboxId = `${idBase}-listbox`;
|
|
109
|
+
trigger.setAttribute("aria-controls", listboxId);
|
|
110
|
+
if (labelText) trigger.setAttribute("aria-label", labelText);
|
|
111
|
+
const triggerLabel = doc.createElement("span");
|
|
112
|
+
triggerLabel.className = "lb-dropdown-trigger-value";
|
|
113
|
+
const chevron = doc.createElement("span");
|
|
114
|
+
chevron.className = "lb-dropdown-chevron";
|
|
115
|
+
chevron.setAttribute("aria-hidden", "true");
|
|
116
|
+
trigger.appendChild(triggerLabel);
|
|
117
|
+
trigger.appendChild(chevron);
|
|
118
|
+
const listbox = doc.createElement("ul");
|
|
119
|
+
listbox.id = listboxId;
|
|
120
|
+
listbox.className = "lb-dropdown-listbox";
|
|
121
|
+
listbox.setAttribute("role", "listbox");
|
|
122
|
+
if (labelText) listbox.setAttribute("aria-label", labelText);
|
|
123
|
+
listbox.hidden = true;
|
|
124
|
+
shell.appendChild(trigger);
|
|
125
|
+
select.parentNode?.insertBefore(shell, select.nextSibling);
|
|
126
|
+
const modalOverlay = select.closest("[data-modal-overlay]");
|
|
127
|
+
if (modalOverlay) {
|
|
128
|
+
modalOverlay.appendChild(listbox);
|
|
129
|
+
listbox.setAttribute("data-lb-dropdown-portal", "");
|
|
130
|
+
} else {
|
|
131
|
+
shell.appendChild(listbox);
|
|
132
|
+
}
|
|
133
|
+
let isOpen = false;
|
|
134
|
+
let activeIndex = -1;
|
|
135
|
+
let typeAhead = emptyTypeAheadState();
|
|
136
|
+
let optionEls = [];
|
|
137
|
+
let instance;
|
|
138
|
+
function syncFromSelect() {
|
|
139
|
+
const opts = readOptions(select);
|
|
140
|
+
const idx = select.selectedIndex;
|
|
141
|
+
triggerLabel.textContent = idx >= 0 && opts[idx] ? opts[idx].label : "";
|
|
142
|
+
while (listbox.firstChild) listbox.removeChild(listbox.firstChild);
|
|
143
|
+
optionEls = [];
|
|
144
|
+
for (let i = 0; i < opts.length; i++) {
|
|
145
|
+
const li = doc.createElement("li");
|
|
146
|
+
li.id = `${idBase}-opt-${i}`;
|
|
147
|
+
li.className = "lb-dropdown-option";
|
|
148
|
+
li.setAttribute("role", "option");
|
|
149
|
+
li.setAttribute("aria-selected", i === idx ? "true" : "false");
|
|
150
|
+
if (opts[i].disabled) li.setAttribute("aria-disabled", "true");
|
|
151
|
+
li.setAttribute("data-value", select.options[i].value);
|
|
152
|
+
li.setAttribute("data-index", String(i));
|
|
153
|
+
li.textContent = opts[i].label;
|
|
154
|
+
listbox.appendChild(li);
|
|
155
|
+
optionEls.push(li);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function setActive(newIndex) {
|
|
159
|
+
if (activeIndex >= 0 && optionEls[activeIndex]) {
|
|
160
|
+
optionEls[activeIndex].classList.remove("is-active");
|
|
161
|
+
}
|
|
162
|
+
activeIndex = newIndex;
|
|
163
|
+
if (newIndex >= 0 && optionEls[newIndex]) {
|
|
164
|
+
const li = optionEls[newIndex];
|
|
165
|
+
li.classList.add("is-active");
|
|
166
|
+
trigger.setAttribute("aria-activedescendant", li.id);
|
|
167
|
+
const liTop = li.offsetTop;
|
|
168
|
+
const liBottom = liTop + li.offsetHeight;
|
|
169
|
+
const visTop = listbox.scrollTop;
|
|
170
|
+
const visBottom = visTop + listbox.clientHeight;
|
|
171
|
+
if (liTop < visTop) {
|
|
172
|
+
listbox.scrollTop = liTop;
|
|
173
|
+
} else if (liBottom > visBottom) {
|
|
174
|
+
listbox.scrollTop = liBottom - listbox.clientHeight;
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
trigger.setAttribute("aria-activedescendant", "");
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function position() {
|
|
181
|
+
const rect = trigger.getBoundingClientRect();
|
|
182
|
+
if (rect.width === 0) return false;
|
|
183
|
+
const visibleCount = Math.min(optionEls.length || 1, MAX_VISIBLE_ITEMS);
|
|
184
|
+
const desiredHeight = visibleCount * ITEM_HEIGHT_PX + LIST_PAD_Y;
|
|
185
|
+
const result = computePosition({
|
|
186
|
+
trigger: {
|
|
187
|
+
top: rect.top,
|
|
188
|
+
bottom: rect.bottom,
|
|
189
|
+
left: rect.left,
|
|
190
|
+
width: rect.width
|
|
191
|
+
},
|
|
192
|
+
viewportHeight: window.innerHeight,
|
|
193
|
+
desiredHeight
|
|
194
|
+
});
|
|
195
|
+
listbox.setAttribute("data-placement", result.placement);
|
|
196
|
+
listbox.style.maxHeight = `${result.maxHeight}px`;
|
|
197
|
+
if (listbox.hasAttribute("data-lb-dropdown-portal")) {
|
|
198
|
+
listbox.style.top = `${result.offsetTop}px`;
|
|
199
|
+
listbox.style.left = `${result.offsetLeft}px`;
|
|
200
|
+
listbox.style.width = `${result.width}px`;
|
|
201
|
+
}
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
function open() {
|
|
205
|
+
if (isOpen) return;
|
|
206
|
+
for (let i = openInstances.length - 1; i >= 0; i--) {
|
|
207
|
+
if (openInstances[i] !== instance) openInstances[i].close();
|
|
208
|
+
}
|
|
209
|
+
isOpen = true;
|
|
210
|
+
listbox.hidden = false;
|
|
211
|
+
trigger.setAttribute("aria-expanded", "true");
|
|
212
|
+
if (!position()) {
|
|
213
|
+
requestAnimationFrame(() => position());
|
|
214
|
+
}
|
|
215
|
+
const opts = readOptions(select);
|
|
216
|
+
const selIdx = select.selectedIndex;
|
|
217
|
+
if (selIdx >= 0 && opts[selIdx] && !opts[selIdx].disabled) {
|
|
218
|
+
setActive(selIdx);
|
|
219
|
+
} else {
|
|
220
|
+
setActive(firstEnabled(opts));
|
|
221
|
+
}
|
|
222
|
+
openInstances.push(instance);
|
|
223
|
+
if (openInstances.length === 1) attachDocumentListeners();
|
|
224
|
+
}
|
|
225
|
+
function close(restoreFocus) {
|
|
226
|
+
if (!isOpen) return;
|
|
227
|
+
isOpen = false;
|
|
228
|
+
listbox.hidden = true;
|
|
229
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
230
|
+
trigger.setAttribute("aria-activedescendant", "");
|
|
231
|
+
if (activeIndex >= 0 && optionEls[activeIndex]) {
|
|
232
|
+
optionEls[activeIndex].classList.remove("is-active");
|
|
233
|
+
}
|
|
234
|
+
activeIndex = -1;
|
|
235
|
+
const idx = openInstances.indexOf(instance);
|
|
236
|
+
if (idx >= 0) openInstances.splice(idx, 1);
|
|
237
|
+
if (openInstances.length === 0) detachDocumentListeners();
|
|
238
|
+
if (restoreFocus) trigger.focus();
|
|
239
|
+
}
|
|
240
|
+
function commit(index) {
|
|
241
|
+
const opt = select.options[index];
|
|
242
|
+
if (!opt || opt.disabled) return;
|
|
243
|
+
if (select.value !== opt.value) {
|
|
244
|
+
select.value = opt.value;
|
|
245
|
+
const event = new Event("change", { bubbles: true });
|
|
246
|
+
select.dispatchEvent(event);
|
|
247
|
+
}
|
|
248
|
+
syncFromSelect();
|
|
249
|
+
close(true);
|
|
250
|
+
}
|
|
251
|
+
function applyAction(action) {
|
|
252
|
+
switch (action.type) {
|
|
253
|
+
case "open":
|
|
254
|
+
open();
|
|
255
|
+
if (action.activeIndex >= 0) setActive(action.activeIndex);
|
|
256
|
+
return;
|
|
257
|
+
case "close":
|
|
258
|
+
close(action.restoreFocus);
|
|
259
|
+
return;
|
|
260
|
+
case "move-active":
|
|
261
|
+
setActive(action.activeIndex);
|
|
262
|
+
return;
|
|
263
|
+
case "commit":
|
|
264
|
+
commit(action.index);
|
|
265
|
+
return;
|
|
266
|
+
case "type-ahead": {
|
|
267
|
+
const opts = readOptions(select);
|
|
268
|
+
const result = pushTypeAheadChar(
|
|
269
|
+
typeAhead,
|
|
270
|
+
action.char,
|
|
271
|
+
Date.now(),
|
|
272
|
+
opts
|
|
273
|
+
);
|
|
274
|
+
typeAhead = result.newState;
|
|
275
|
+
if (result.matchedIndex !== null) {
|
|
276
|
+
if (!isOpen) open();
|
|
277
|
+
setActive(result.matchedIndex);
|
|
278
|
+
}
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
case "passthrough":
|
|
282
|
+
return;
|
|
283
|
+
default: {
|
|
284
|
+
const _exhaustive = action;
|
|
285
|
+
void _exhaustive;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function onKeydown(event) {
|
|
290
|
+
const opts = readOptions(select);
|
|
291
|
+
const action = handleKey(
|
|
292
|
+
{
|
|
293
|
+
key: event.key,
|
|
294
|
+
ctrlKey: event.ctrlKey,
|
|
295
|
+
metaKey: event.metaKey,
|
|
296
|
+
altKey: event.altKey,
|
|
297
|
+
shiftKey: event.shiftKey
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
isOpen,
|
|
301
|
+
activeIndex,
|
|
302
|
+
selectedIndex: select.selectedIndex,
|
|
303
|
+
options: opts
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
if (action.preventDefault) event.preventDefault();
|
|
307
|
+
applyAction(action);
|
|
308
|
+
}
|
|
309
|
+
function onTriggerClick(event) {
|
|
310
|
+
event.preventDefault();
|
|
311
|
+
if (isOpen) close(false);
|
|
312
|
+
else open();
|
|
313
|
+
}
|
|
314
|
+
function onListboxClick(event) {
|
|
315
|
+
let target = event.target;
|
|
316
|
+
while (target && target !== listbox) {
|
|
317
|
+
if (target.classList?.contains("lb-dropdown-option")) {
|
|
318
|
+
const idx = parseInt(target.getAttribute("data-index") ?? "", 10);
|
|
319
|
+
if (!Number.isNaN(idx)) {
|
|
320
|
+
commit(idx);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
target = target.parentElement;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
function onListboxMousemove(event) {
|
|
328
|
+
let target = event.target;
|
|
329
|
+
while (target && target !== listbox) {
|
|
330
|
+
if (target.classList?.contains("lb-dropdown-option")) {
|
|
331
|
+
if (target.getAttribute("aria-disabled") === "true") return;
|
|
332
|
+
const idx = parseInt(target.getAttribute("data-index") ?? "", 10);
|
|
333
|
+
if (!Number.isNaN(idx) && idx !== activeIndex) setActive(idx);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
target = target.parentElement;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function onShellFocusout() {
|
|
340
|
+
setTimeout(() => {
|
|
341
|
+
if (!isOpen) return;
|
|
342
|
+
const active = rootNode.activeElement ?? doc.activeElement;
|
|
343
|
+
if (!shell.contains(active)) close(false);
|
|
344
|
+
}, 0);
|
|
345
|
+
}
|
|
346
|
+
function onSelectChange() {
|
|
347
|
+
syncFromSelect();
|
|
348
|
+
}
|
|
349
|
+
const observer = new MutationObserver(() => {
|
|
350
|
+
syncFromSelect();
|
|
351
|
+
});
|
|
352
|
+
observer.observe(select, {
|
|
353
|
+
childList: true,
|
|
354
|
+
subtree: true,
|
|
355
|
+
attributes: true,
|
|
356
|
+
attributeFilter: ["disabled", "value", "selected"]
|
|
357
|
+
});
|
|
358
|
+
const onListboxMousedown = (event) => event.preventDefault();
|
|
359
|
+
trigger.addEventListener("click", onTriggerClick);
|
|
360
|
+
trigger.addEventListener("keydown", onKeydown);
|
|
361
|
+
shell.addEventListener("focusout", onShellFocusout);
|
|
362
|
+
listbox.addEventListener("mousedown", onListboxMousedown);
|
|
363
|
+
listbox.addEventListener("click", onListboxClick);
|
|
364
|
+
listbox.addEventListener("mousemove", onListboxMousemove);
|
|
365
|
+
select.addEventListener("change", onSelectChange);
|
|
366
|
+
function destroy() {
|
|
367
|
+
if (isOpen) close(false);
|
|
368
|
+
observer.disconnect();
|
|
369
|
+
trigger.removeEventListener("click", onTriggerClick);
|
|
370
|
+
trigger.removeEventListener("keydown", onKeydown);
|
|
371
|
+
shell.removeEventListener("focusout", onShellFocusout);
|
|
372
|
+
listbox.removeEventListener("mousedown", onListboxMousedown);
|
|
373
|
+
listbox.removeEventListener("click", onListboxClick);
|
|
374
|
+
listbox.removeEventListener("mousemove", onListboxMousemove);
|
|
375
|
+
select.removeEventListener("change", onSelectChange);
|
|
376
|
+
if (shell.parentNode) shell.parentNode.removeChild(shell);
|
|
377
|
+
if (listbox.parentNode) listbox.parentNode.removeChild(listbox);
|
|
378
|
+
select.classList.remove("lb-dropdown-state");
|
|
379
|
+
select.removeAttribute("aria-hidden");
|
|
380
|
+
select.removeAttribute("tabindex");
|
|
381
|
+
delete slot.__lbDropdownInstance;
|
|
382
|
+
}
|
|
383
|
+
instance = {
|
|
384
|
+
shell,
|
|
385
|
+
listbox,
|
|
386
|
+
select,
|
|
387
|
+
close: () => close(false),
|
|
388
|
+
destroy
|
|
389
|
+
};
|
|
390
|
+
slot.__lbDropdownInstance = instance;
|
|
391
|
+
syncFromSelect();
|
|
392
|
+
return instance;
|
|
393
|
+
}
|
|
394
|
+
function bindAllDropdowns(root) {
|
|
395
|
+
const selects = root.querySelectorAll(BIND_SELECTOR);
|
|
396
|
+
const instances = [];
|
|
397
|
+
selects.forEach((sel) => {
|
|
398
|
+
const inst = bindDropdown(sel);
|
|
399
|
+
if (inst) instances.push(inst);
|
|
400
|
+
});
|
|
401
|
+
return instances;
|
|
402
|
+
}
|
|
403
|
+
function unbindAllDropdowns(root) {
|
|
404
|
+
const bound = root.querySelectorAll("select.lb-dropdown-state");
|
|
405
|
+
bound.forEach((sel) => {
|
|
406
|
+
const inst = sel.__lbDropdownInstance;
|
|
407
|
+
if (inst) inst.destroy();
|
|
408
|
+
});
|
|
409
|
+
}
|
|
36
410
|
|
|
37
|
-
// src/renderers/
|
|
411
|
+
// src/renderers/pricing.ts
|
|
38
412
|
var import_core2 = require("@lime-bundles/core");
|
|
413
|
+
|
|
414
|
+
// src/renderers/countdown.ts
|
|
415
|
+
var import_core3 = require("@lime-bundles/core");
|
|
39
416
|
function renderCountdown(endsAtIso) {
|
|
40
417
|
const parsed = parseIso(endsAtIso);
|
|
41
418
|
if (parsed === null) return null;
|
|
@@ -62,7 +439,7 @@ function renderCountdown(endsAtIso) {
|
|
|
62
439
|
stop();
|
|
63
440
|
return;
|
|
64
441
|
}
|
|
65
|
-
timer.textContent = (0,
|
|
442
|
+
timer.textContent = (0, import_core3.formatCountdown)(msLeft);
|
|
66
443
|
}
|
|
67
444
|
function stop() {
|
|
68
445
|
if (intervalId !== null) {
|
|
@@ -116,7 +493,7 @@ function el(tag, className, attrs = {}) {
|
|
|
116
493
|
}
|
|
117
494
|
|
|
118
495
|
// src/renderers/image.ts
|
|
119
|
-
var
|
|
496
|
+
var import_core4 = require("@lime-bundles/core");
|
|
120
497
|
|
|
121
498
|
// src/renderers/fixed.ts
|
|
122
499
|
var PLACEHOLDER_THUMB_SVG = `
|
|
@@ -127,7 +504,7 @@ var PLACEHOLDER_THUMB_SVG = `
|
|
|
127
504
|
</svg>`;
|
|
128
505
|
function renderFixedBundle(container, bundle, onAddToCart, onCleanup) {
|
|
129
506
|
const wc = bundle.widgetConfig;
|
|
130
|
-
const qtyFor = (productId, variantId) => (0,
|
|
507
|
+
const qtyFor = (productId, variantId) => (0, import_core5.resolveBundleQty)(bundle, productId, variantId);
|
|
131
508
|
const rows = [];
|
|
132
509
|
let oosCount = 0;
|
|
133
510
|
bundle.products.forEach((product, idx) => {
|
|
@@ -192,11 +569,13 @@ function renderFixedBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
192
569
|
})
|
|
193
570
|
);
|
|
194
571
|
container.appendChild(root);
|
|
572
|
+
bindAllDropdowns(root);
|
|
573
|
+
onCleanup?.(() => unbindAllDropdowns(root));
|
|
195
574
|
updatePricing();
|
|
196
575
|
function updatePricing() {
|
|
197
576
|
const totalCents = rows.reduce((sum, r) => {
|
|
198
577
|
if (!r.selected) return sum;
|
|
199
|
-
const unit = (0,
|
|
578
|
+
const unit = (0, import_core2.parseCents)(r.selected.price.amount);
|
|
200
579
|
return sum + unit * r.qty;
|
|
201
580
|
}, 0);
|
|
202
581
|
const saleCents = computeSale(totalCents, bundle.discountConfig, rows);
|
|
@@ -217,7 +596,7 @@ function buildRowState(bundle, product, productIndex) {
|
|
|
217
596
|
const eligibleVariants = merchantScoped;
|
|
218
597
|
const isOos = !firstInStock;
|
|
219
598
|
const selected = firstInStock ?? merchantScoped[0] ?? null;
|
|
220
|
-
const qty = selected ? (0,
|
|
599
|
+
const qty = selected ? (0, import_core5.resolveBundleQty)(bundle, product.id, selected.id) : 1;
|
|
221
600
|
return { product, eligibleVariants, selected, qty, isOos };
|
|
222
601
|
}
|
|
223
602
|
function renderHeader(bundle, currency) {
|
|
@@ -237,7 +616,7 @@ function renderHeader(bundle, currency) {
|
|
|
237
616
|
"data-header-badge": ""
|
|
238
617
|
});
|
|
239
618
|
header.appendChild(badgeEl);
|
|
240
|
-
const initialPricing = (0,
|
|
619
|
+
const initialPricing = (0, import_core2.computeFixedPricing)(
|
|
241
620
|
bundle,
|
|
242
621
|
bundle.productQuantities,
|
|
243
622
|
wc.pricing.showSaveBadge
|
|
@@ -273,9 +652,9 @@ function deriveHeaderBadge(bundle, totalCents, saleCents, currency) {
|
|
|
273
652
|
return `-${Math.round(dc.discountValue)}%`;
|
|
274
653
|
}
|
|
275
654
|
if (dc.discountType === "fixed_amount" && dc.discountValue > 0) {
|
|
276
|
-
return `-${(0,
|
|
655
|
+
return `-${(0, import_core2.formatCents)(Math.round(dc.discountValue * 100), currency)}`;
|
|
277
656
|
}
|
|
278
|
-
return `-${(0,
|
|
657
|
+
return `-${(0, import_core2.formatCents)(savings, currency)}`;
|
|
279
658
|
}
|
|
280
659
|
function renderProductRow(state, currency, qtyFor, onVariantChange) {
|
|
281
660
|
const rowEl = el(
|
|
@@ -287,18 +666,20 @@ function renderProductRow(state, currency, qtyFor, onVariantChange) {
|
|
|
287
666
|
}
|
|
288
667
|
);
|
|
289
668
|
const thumb = el("div", "lb-bundle-thumbnail", { "data-thumbnail": "" });
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
669
|
+
const initialThumbImage = state.selected?.image ?? state.product.featuredImage ?? null;
|
|
670
|
+
let thumbImg = null;
|
|
671
|
+
if (initialThumbImage) {
|
|
672
|
+
thumbImg = document.createElement("img");
|
|
673
|
+
thumbImg.src = (0, import_core4.transformImageUrl)(initialThumbImage.url, {
|
|
674
|
+
width: import_core4.THUMB_PX,
|
|
675
|
+
height: import_core4.THUMB_PX,
|
|
295
676
|
crop: "center"
|
|
296
677
|
});
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
thumb.appendChild(
|
|
678
|
+
thumbImg.alt = initialThumbImage.altText ?? state.product.title;
|
|
679
|
+
thumbImg.width = import_core4.THUMB_PX;
|
|
680
|
+
thumbImg.height = import_core4.THUMB_PX;
|
|
681
|
+
thumbImg.loading = "lazy";
|
|
682
|
+
thumb.appendChild(thumbImg);
|
|
302
683
|
} else {
|
|
303
684
|
thumb.insertAdjacentHTML("beforeend", PLACEHOLDER_THUMB_SVG);
|
|
304
685
|
}
|
|
@@ -338,12 +719,12 @@ function renderProductRow(state, currency, qtyFor, onVariantChange) {
|
|
|
338
719
|
unitPriceEl.setAttribute("hidden", "");
|
|
339
720
|
info.appendChild(unitPriceEl);
|
|
340
721
|
const applyVariantToRow = (variant) => {
|
|
341
|
-
const unit = (0,
|
|
342
|
-
priceEl.textContent = (0,
|
|
722
|
+
const unit = (0, import_core2.parseCents)(variant.price.amount);
|
|
723
|
+
priceEl.textContent = (0, import_core2.formatCents)(unit, currency);
|
|
343
724
|
if (variant.compareAtPrice) {
|
|
344
|
-
const cmp = (0,
|
|
725
|
+
const cmp = (0, import_core2.parseCents)(variant.compareAtPrice.amount);
|
|
345
726
|
if (cmp > unit) {
|
|
346
|
-
compare.textContent = (0,
|
|
727
|
+
compare.textContent = (0, import_core2.formatCents)(cmp, currency);
|
|
347
728
|
compare.removeAttribute("hidden");
|
|
348
729
|
} else {
|
|
349
730
|
compare.setAttribute("hidden", "");
|
|
@@ -351,7 +732,7 @@ function renderProductRow(state, currency, qtyFor, onVariantChange) {
|
|
|
351
732
|
} else {
|
|
352
733
|
compare.setAttribute("hidden", "");
|
|
353
734
|
}
|
|
354
|
-
const unitText = (0,
|
|
735
|
+
const unitText = (0, import_core2.formatUnitPrice)(
|
|
355
736
|
variant.unitPrice,
|
|
356
737
|
variant.unitPriceMeasurement,
|
|
357
738
|
currency
|
|
@@ -362,6 +743,15 @@ function renderProductRow(state, currency, qtyFor, onVariantChange) {
|
|
|
362
743
|
} else {
|
|
363
744
|
unitPriceEl.setAttribute("hidden", "");
|
|
364
745
|
}
|
|
746
|
+
const nextImage = variant.image ?? state.product.featuredImage;
|
|
747
|
+
if (thumbImg && nextImage) {
|
|
748
|
+
thumbImg.src = (0, import_core4.transformImageUrl)(nextImage.url, {
|
|
749
|
+
width: import_core4.THUMB_PX,
|
|
750
|
+
height: import_core4.THUMB_PX,
|
|
751
|
+
crop: "center"
|
|
752
|
+
});
|
|
753
|
+
thumbImg.alt = nextImage.altText ?? state.product.title;
|
|
754
|
+
}
|
|
365
755
|
};
|
|
366
756
|
applyVariantToRow(state.selected);
|
|
367
757
|
if (state.eligibleVariants.length > 1) {
|
|
@@ -472,9 +862,9 @@ function renderPricingRow(bundle) {
|
|
|
472
862
|
return {
|
|
473
863
|
el: row,
|
|
474
864
|
update({ totalCents, saleCents, savingsCents, currency }) {
|
|
475
|
-
sale.textContent = (0,
|
|
865
|
+
sale.textContent = (0, import_core2.formatCents)(saleCents, currency);
|
|
476
866
|
if (bundle.widgetConfig.pricing.showCompareAtPrice && savingsCents > 0) {
|
|
477
|
-
compare.textContent = (0,
|
|
867
|
+
compare.textContent = (0, import_core2.formatCents)(totalCents, currency);
|
|
478
868
|
compare.style.display = "";
|
|
479
869
|
} else {
|
|
480
870
|
compare.style.display = "none";
|
|
@@ -499,7 +889,7 @@ function renderSavingsBar() {
|
|
|
499
889
|
return;
|
|
500
890
|
}
|
|
501
891
|
bar.style.display = "";
|
|
502
|
-
amount.textContent = (0,
|
|
892
|
+
amount.textContent = (0, import_core2.formatCents)(savingsCents, currency);
|
|
503
893
|
}
|
|
504
894
|
};
|
|
505
895
|
}
|
|
@@ -521,7 +911,7 @@ function computeSale(totalCents, discount, rows) {
|
|
|
521
911
|
let saleCents = 0;
|
|
522
912
|
for (const r of rows) {
|
|
523
913
|
if (!r.selected) continue;
|
|
524
|
-
const unit = (0,
|
|
914
|
+
const unit = (0, import_core2.parseCents)(r.selected.price.amount);
|
|
525
915
|
const off = Math.floor(unit * discount.discountValue / 100);
|
|
526
916
|
const perUnit = Math.max(0, unit - off);
|
|
527
917
|
saleCents += perUnit * r.qty;
|
|
@@ -532,7 +922,7 @@ function computeSale(totalCents, discount, rows) {
|
|
|
532
922
|
}
|
|
533
923
|
|
|
534
924
|
// src/renderers/mix-match.ts
|
|
535
|
-
var
|
|
925
|
+
var import_core6 = require("@lime-bundles/core");
|
|
536
926
|
var PLACEHOLDER_THUMB_SVG2 = `
|
|
537
927
|
<svg class="lb-bundle-placeholder-icon" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
|
538
928
|
<rect x="4" y="4" width="20" height="20" rx="3"></rect>
|
|
@@ -628,6 +1018,7 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
628
1018
|
})
|
|
629
1019
|
);
|
|
630
1020
|
container.appendChild(root);
|
|
1021
|
+
onCleanup?.(() => unbindAllDropdowns(root));
|
|
631
1022
|
const firstEligible = eligible.find((ep) => !ep.isOos);
|
|
632
1023
|
const firstVariant = firstEligible?.firstAvailableVariant ?? firstEligible?.variants[0];
|
|
633
1024
|
if (firstEligible && firstVariant) {
|
|
@@ -636,15 +1027,15 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
636
1027
|
productTitle: firstEligible.product.title,
|
|
637
1028
|
variantId: firstVariant.id,
|
|
638
1029
|
variantTitle: firstVariant.title,
|
|
639
|
-
imageUrl: firstEligible.product.featuredImage?.url ?? null,
|
|
640
|
-
priceCents: (0,
|
|
641
|
-
compareCents: firstVariant.compareAtPrice ? (0,
|
|
642
|
-
unitPriceLabel: (0,
|
|
1030
|
+
imageUrl: firstVariant.image?.url ?? firstEligible.product.featuredImage?.url ?? null,
|
|
1031
|
+
priceCents: (0, import_core2.parseCents)(firstVariant.price.amount),
|
|
1032
|
+
compareCents: firstVariant.compareAtPrice ? (0, import_core2.parseCents)(firstVariant.compareAtPrice.amount) : null,
|
|
1033
|
+
unitPriceLabel: (0, import_core2.formatUnitPrice)(
|
|
643
1034
|
firstVariant.unitPrice,
|
|
644
1035
|
firstVariant.unitPriceMeasurement,
|
|
645
1036
|
currency
|
|
646
1037
|
),
|
|
647
|
-
quantity: (0,
|
|
1038
|
+
quantity: (0, import_core6.resolveBundleQty)(bundle, firstEligible.product.id, firstVariant.id)
|
|
648
1039
|
});
|
|
649
1040
|
}
|
|
650
1041
|
afterMutation();
|
|
@@ -655,15 +1046,15 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
655
1046
|
productTitle: product.title,
|
|
656
1047
|
variantId: variant.id,
|
|
657
1048
|
variantTitle: variant.title,
|
|
658
|
-
imageUrl: product.featuredImage?.url ?? null,
|
|
659
|
-
priceCents: (0,
|
|
660
|
-
compareCents: variant.compareAtPrice ? (0,
|
|
661
|
-
unitPriceLabel: (0,
|
|
1049
|
+
imageUrl: variant.image?.url ?? product.featuredImage?.url ?? null,
|
|
1050
|
+
priceCents: (0, import_core2.parseCents)(variant.price.amount),
|
|
1051
|
+
compareCents: variant.compareAtPrice ? (0, import_core2.parseCents)(variant.compareAtPrice.amount) : null,
|
|
1052
|
+
unitPriceLabel: (0, import_core2.formatUnitPrice)(
|
|
662
1053
|
variant.unitPrice,
|
|
663
1054
|
variant.unitPriceMeasurement,
|
|
664
1055
|
currency
|
|
665
1056
|
),
|
|
666
|
-
quantity: (0,
|
|
1057
|
+
quantity: (0, import_core6.resolveBundleQty)(bundle, product.id, variant.id)
|
|
667
1058
|
});
|
|
668
1059
|
afterMutation();
|
|
669
1060
|
}
|
|
@@ -730,7 +1121,7 @@ function renderHeader2(bundle) {
|
|
|
730
1121
|
if (discountType === "percentage" && discountValue > 0) {
|
|
731
1122
|
label = `-${Math.round(discountValue)}%`;
|
|
732
1123
|
} else if (discountType === "fixed_amount" && discountValue > 0) {
|
|
733
|
-
label = `-${(0,
|
|
1124
|
+
label = `-${(0, import_core2.formatCents)(
|
|
734
1125
|
Math.round(discountValue * 100),
|
|
735
1126
|
bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? "USD"
|
|
736
1127
|
)}`;
|
|
@@ -817,14 +1208,14 @@ function renderFilledSlot(selection, index, currency, onRemove) {
|
|
|
817
1208
|
const thumb = el("div", "lb-bundle-thumbnail", { "data-thumbnail": "" });
|
|
818
1209
|
if (selection.imageUrl) {
|
|
819
1210
|
const img = document.createElement("img");
|
|
820
|
-
img.src = (0,
|
|
821
|
-
width:
|
|
822
|
-
height:
|
|
1211
|
+
img.src = (0, import_core4.transformImageUrl)(selection.imageUrl, {
|
|
1212
|
+
width: import_core4.THUMB_PX,
|
|
1213
|
+
height: import_core4.THUMB_PX,
|
|
823
1214
|
crop: "center"
|
|
824
1215
|
});
|
|
825
1216
|
img.alt = selection.productTitle;
|
|
826
|
-
img.width =
|
|
827
|
-
img.height =
|
|
1217
|
+
img.width = import_core4.THUMB_PX;
|
|
1218
|
+
img.height = import_core4.THUMB_PX;
|
|
828
1219
|
img.loading = "lazy";
|
|
829
1220
|
thumb.appendChild(img);
|
|
830
1221
|
} else {
|
|
@@ -848,11 +1239,11 @@ function renderFilledSlot(selection, index, currency, onRemove) {
|
|
|
848
1239
|
const priceWrap = el("span", "lb-mix-match__filled-price");
|
|
849
1240
|
if (lineCompare !== null && lineCompare > linePrice) {
|
|
850
1241
|
const compare = el("span", "lb-mix-match__filled-compare");
|
|
851
|
-
compare.textContent = (0,
|
|
1242
|
+
compare.textContent = (0, import_core2.formatCents)(lineCompare, currency);
|
|
852
1243
|
priceWrap.appendChild(compare);
|
|
853
1244
|
}
|
|
854
1245
|
const priceEl = document.createElement("span");
|
|
855
|
-
priceEl.textContent = (0,
|
|
1246
|
+
priceEl.textContent = (0, import_core2.formatCents)(linePrice, currency);
|
|
856
1247
|
priceWrap.appendChild(priceEl);
|
|
857
1248
|
info.appendChild(priceWrap);
|
|
858
1249
|
if (selection.unitPriceLabel) {
|
|
@@ -897,14 +1288,14 @@ function renderPricingSection(showCompareAtPrice) {
|
|
|
897
1288
|
(s, sel) => s + sel.priceCents * sel.quantity,
|
|
898
1289
|
0
|
|
899
1290
|
);
|
|
900
|
-
const saleCents = (0,
|
|
1291
|
+
const saleCents = (0, import_core2.computeBundleSaleCents)(totalCents, bundle.discountConfig);
|
|
901
1292
|
if (showCompareAtPrice && totalCents > saleCents) {
|
|
902
|
-
compare.textContent = (0,
|
|
1293
|
+
compare.textContent = (0, import_core2.formatCents)(totalCents, currency);
|
|
903
1294
|
compare.style.display = "";
|
|
904
1295
|
} else {
|
|
905
1296
|
compare.style.display = "none";
|
|
906
1297
|
}
|
|
907
|
-
sale.textContent = (0,
|
|
1298
|
+
sale.textContent = (0, import_core2.formatCents)(saleCents, currency);
|
|
908
1299
|
}
|
|
909
1300
|
return { el: wrap, update };
|
|
910
1301
|
}
|
|
@@ -927,14 +1318,14 @@ function renderSavingsBar2() {
|
|
|
927
1318
|
(s, sel) => s + sel.priceCents * sel.quantity,
|
|
928
1319
|
0
|
|
929
1320
|
);
|
|
930
|
-
const saleCents = (0,
|
|
1321
|
+
const saleCents = (0, import_core2.computeBundleSaleCents)(totalCents, bundle.discountConfig);
|
|
931
1322
|
const savings = Math.max(0, totalCents - saleCents);
|
|
932
1323
|
if (savings <= 0) {
|
|
933
1324
|
wrap.style.display = "none";
|
|
934
1325
|
return;
|
|
935
1326
|
}
|
|
936
1327
|
wrap.style.display = "";
|
|
937
|
-
amount.textContent = (0,
|
|
1328
|
+
amount.textContent = (0, import_core2.formatCents)(savings, currency);
|
|
938
1329
|
}
|
|
939
1330
|
return { el: wrap, update };
|
|
940
1331
|
}
|
|
@@ -1030,24 +1421,27 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1030
1421
|
{ "data-product-id": ep.product.id.replace(/^.*\//, "") }
|
|
1031
1422
|
);
|
|
1032
1423
|
const thumb = el("div", "lb-mix-match__modal-product-thumb");
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1424
|
+
const initialThumbVariant = ep.variants.find((v) => v.availableForSale) ?? ep.variants[0] ?? null;
|
|
1425
|
+
const initialThumbImage = initialThumbVariant?.image ?? ep.product.featuredImage ?? null;
|
|
1426
|
+
let thumbImg = null;
|
|
1427
|
+
if (initialThumbImage) {
|
|
1428
|
+
thumbImg = document.createElement("img");
|
|
1429
|
+
thumbImg.src = (0, import_core4.transformImageUrl)(initialThumbImage.url, {
|
|
1430
|
+
width: import_core4.THUMB_PX,
|
|
1431
|
+
height: import_core4.THUMB_PX,
|
|
1038
1432
|
crop: "center"
|
|
1039
1433
|
});
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
thumb.appendChild(
|
|
1434
|
+
thumbImg.alt = initialThumbImage.altText ?? ep.product.title;
|
|
1435
|
+
thumbImg.width = import_core4.THUMB_PX;
|
|
1436
|
+
thumbImg.height = import_core4.THUMB_PX;
|
|
1437
|
+
thumbImg.loading = "lazy";
|
|
1438
|
+
thumb.appendChild(thumbImg);
|
|
1045
1439
|
} else {
|
|
1046
1440
|
thumb.insertAdjacentHTML("beforeend", PLACEHOLDER_THUMB_SVG2);
|
|
1047
1441
|
}
|
|
1048
1442
|
const countBadge = el("span", "lb-bundle-qty-badge");
|
|
1049
1443
|
countBadge.textContent = String(
|
|
1050
|
-
(0,
|
|
1444
|
+
(0, import_core6.resolveBundleQty)(bundle, ep.product.id, currentVariant.id)
|
|
1051
1445
|
);
|
|
1052
1446
|
thumb.appendChild(countBadge);
|
|
1053
1447
|
productEl.appendChild(thumb);
|
|
@@ -1056,8 +1450,8 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1056
1450
|
title.textContent = ep.product.title;
|
|
1057
1451
|
info.appendChild(title);
|
|
1058
1452
|
const price = el("p", "lb-mix-match__modal-product-price");
|
|
1059
|
-
price.textContent = (0,
|
|
1060
|
-
(0,
|
|
1453
|
+
price.textContent = (0, import_core2.formatCents)(
|
|
1454
|
+
(0, import_core2.parseCents)(currentVariant.price.amount) * (0, import_core6.resolveBundleQty)(bundle, ep.product.id, currentVariant.id),
|
|
1061
1455
|
currency
|
|
1062
1456
|
);
|
|
1063
1457
|
info.appendChild(price);
|
|
@@ -1065,7 +1459,7 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1065
1459
|
"p",
|
|
1066
1460
|
"lb-mix-match__modal-product-unit-price lb-bundle-product-unit-price"
|
|
1067
1461
|
);
|
|
1068
|
-
const initialUnitText = (0,
|
|
1462
|
+
const initialUnitText = (0, import_core2.formatUnitPrice)(
|
|
1069
1463
|
currentVariant.unitPrice,
|
|
1070
1464
|
currentVariant.unitPriceMeasurement,
|
|
1071
1465
|
currency
|
|
@@ -1124,16 +1518,16 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1124
1518
|
}
|
|
1125
1519
|
currentVariant = next;
|
|
1126
1520
|
row.variant = next;
|
|
1127
|
-
const nextQty = (0,
|
|
1521
|
+
const nextQty = (0, import_core6.resolveBundleQty)(
|
|
1128
1522
|
bundle,
|
|
1129
1523
|
ep.product.id,
|
|
1130
1524
|
currentVariant.id
|
|
1131
1525
|
);
|
|
1132
|
-
price.textContent = (0,
|
|
1133
|
-
(0,
|
|
1526
|
+
price.textContent = (0, import_core2.formatCents)(
|
|
1527
|
+
(0, import_core2.parseCents)(currentVariant.price.amount) * nextQty,
|
|
1134
1528
|
currency
|
|
1135
1529
|
);
|
|
1136
|
-
const nextUnitText = (0,
|
|
1530
|
+
const nextUnitText = (0, import_core2.formatUnitPrice)(
|
|
1137
1531
|
currentVariant.unitPrice,
|
|
1138
1532
|
currentVariant.unitPriceMeasurement,
|
|
1139
1533
|
currency
|
|
@@ -1145,6 +1539,15 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1145
1539
|
unitPrice.textContent = "";
|
|
1146
1540
|
unitPrice.hidden = true;
|
|
1147
1541
|
}
|
|
1542
|
+
const nextImage = currentVariant.image ?? ep.product.featuredImage;
|
|
1543
|
+
if (thumbImg && nextImage) {
|
|
1544
|
+
thumbImg.src = (0, import_core4.transformImageUrl)(nextImage.url, {
|
|
1545
|
+
width: import_core4.THUMB_PX,
|
|
1546
|
+
height: import_core4.THUMB_PX,
|
|
1547
|
+
crop: "center"
|
|
1548
|
+
});
|
|
1549
|
+
thumbImg.alt = nextImage.altText ?? ep.product.title;
|
|
1550
|
+
}
|
|
1148
1551
|
recomputeDisabled(next.selectedOptions.map((o) => o.value));
|
|
1149
1552
|
rowUpdateCount();
|
|
1150
1553
|
};
|
|
@@ -1195,7 +1598,7 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1195
1598
|
productEl.appendChild(info);
|
|
1196
1599
|
const rowUpdateCount = () => {
|
|
1197
1600
|
countBadge.textContent = String(
|
|
1198
|
-
(0,
|
|
1601
|
+
(0, import_core6.resolveBundleQty)(bundle, ep.product.id, currentVariant.id)
|
|
1199
1602
|
);
|
|
1200
1603
|
};
|
|
1201
1604
|
if (!ep.isOos) {
|
|
@@ -1215,6 +1618,7 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1215
1618
|
productRows.push(row);
|
|
1216
1619
|
list.appendChild(productEl);
|
|
1217
1620
|
});
|
|
1621
|
+
bindAllDropdowns(list);
|
|
1218
1622
|
refreshCounts();
|
|
1219
1623
|
}
|
|
1220
1624
|
function applySearch() {
|
|
@@ -1318,7 +1722,7 @@ function renderVolumeBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
1318
1722
|
const product = bundle.products[0];
|
|
1319
1723
|
const variant = product?.variants.nodes.find((v) => v.availableForSale);
|
|
1320
1724
|
if (!variant && wc.outOfStockBehavior === "hide") return;
|
|
1321
|
-
const basePriceCents = variant ? (0,
|
|
1725
|
+
const basePriceCents = variant ? (0, import_core2.parseCents)(variant.price.amount) : 0;
|
|
1322
1726
|
const currency = variant?.price.currencyCode ?? "USD";
|
|
1323
1727
|
const discountType = bundle.discountConfig.discountType;
|
|
1324
1728
|
const resolved = bundle.volumeTiers.map((tier, index) => {
|
|
@@ -1497,13 +1901,13 @@ function renderTierCard(r, isSelected, currency, popularLabel, showComparePrice,
|
|
|
1497
1901
|
const price = el("span", "lb-volume__tier-price");
|
|
1498
1902
|
if (showComparePrice && r.pricePerUnitCents < r.basePricePerUnitCents) {
|
|
1499
1903
|
const compare = el("span", "lb-volume__tier-compare");
|
|
1500
|
-
compare.textContent = (0,
|
|
1904
|
+
compare.textContent = (0, import_core2.formatCents)(r.basePricePerUnitCents, currency);
|
|
1501
1905
|
price.appendChild(compare);
|
|
1502
1906
|
}
|
|
1503
1907
|
if (showPerUnitPrice) {
|
|
1504
1908
|
const each = document.createElement("span");
|
|
1505
1909
|
each.setAttribute("data-tier-price-each", "");
|
|
1506
|
-
each.textContent = (0,
|
|
1910
|
+
each.textContent = (0, import_core2.formatCents)(r.pricePerUnitCents, currency);
|
|
1507
1911
|
price.appendChild(each);
|
|
1508
1912
|
const unit = el("span", "lb-volume__tier-unit");
|
|
1509
1913
|
unit.textContent = " each";
|
|
@@ -1542,11 +1946,11 @@ function renderPricingRow2(resolved, selectedIndex, currency, showItemCount, sho
|
|
|
1542
1946
|
const compare = el("span", "lb-bundle-compare-price", {
|
|
1543
1947
|
"data-compare-price": ""
|
|
1544
1948
|
});
|
|
1545
|
-
compare.textContent = (0,
|
|
1949
|
+
compare.textContent = (0, import_core2.formatCents)(undiscountedCents, currency);
|
|
1546
1950
|
prices.appendChild(compare);
|
|
1547
1951
|
}
|
|
1548
1952
|
const sale = el("span", "lb-bundle-sale-price", { "data-total-price": "" });
|
|
1549
|
-
sale.textContent = (0,
|
|
1953
|
+
sale.textContent = (0, import_core2.formatCents)(totalCents, currency);
|
|
1550
1954
|
prices.appendChild(sale);
|
|
1551
1955
|
row.appendChild(prices);
|
|
1552
1956
|
return row;
|
|
@@ -1562,7 +1966,7 @@ function renderSavingsBar3(resolved, selectedIndex, currency) {
|
|
|
1562
1966
|
labelEl.textContent = "You save";
|
|
1563
1967
|
bar.appendChild(labelEl);
|
|
1564
1968
|
const amount = el("span", "", { "data-savings-amount": "" });
|
|
1565
|
-
amount.textContent = (0,
|
|
1969
|
+
amount.textContent = (0, import_core2.formatCents)(savings, currency);
|
|
1566
1970
|
bar.appendChild(amount);
|
|
1567
1971
|
return bar;
|
|
1568
1972
|
}
|
|
@@ -1583,7 +1987,7 @@ function badgeFor(resolved, currency, discountType) {
|
|
|
1583
1987
|
const { tier } = resolved;
|
|
1584
1988
|
if (discountType === "fixed_amount") {
|
|
1585
1989
|
const amount = tier.amount ?? 0;
|
|
1586
|
-
if (amount > 0) return `-${(0,
|
|
1990
|
+
if (amount > 0) return `-${(0, import_core2.formatCents)(Math.round(amount * 100), currency)}`;
|
|
1587
1991
|
return null;
|
|
1588
1992
|
}
|
|
1589
1993
|
if (discountType === "percentage") {
|
|
@@ -1610,7 +2014,7 @@ function clamp(n, min, max) {
|
|
|
1610
2014
|
}
|
|
1611
2015
|
|
|
1612
2016
|
// src/lime-bundle.ts
|
|
1613
|
-
var
|
|
2017
|
+
var import_core8 = require("@lime-bundles/core");
|
|
1614
2018
|
|
|
1615
2019
|
// src/utils/input-mode.ts
|
|
1616
2020
|
var NAV_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -1680,6 +2084,11 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
|
|
|
1680
2084
|
--lb-thumbnail-bg: #F0F0F0;
|
|
1681
2085
|
--lb-widget-pad: 20px;
|
|
1682
2086
|
--lb-progress-color: var(--lb-primary-color);
|
|
2087
|
+
/* Cap on the per-bundle product/slot/tier list height \u2014 keeps long
|
|
2088
|
+
bundles from pushing the CTA off-screen. The list scrolls
|
|
2089
|
+
internally with the same custom 4px scrollbar as the variant
|
|
2090
|
+
dropdown when content exceeds this. */
|
|
2091
|
+
--lb-list-max-height: 360px;
|
|
1683
2092
|
|
|
1684
2093
|
font-family: inherit;
|
|
1685
2094
|
font-size: 16px;
|
|
@@ -1992,10 +2401,10 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
|
|
|
1992
2401
|
click-to-focus doesn't leave a keyboard-style ring. The modal overlay
|
|
1993
2402
|
gets its own selector because the Liquid path reparents it to body,
|
|
1994
2403
|
outside the widget root. */
|
|
1995
|
-
.using-mouse .lb-bundle-widget :focus,
|
|
1996
|
-
.using-mouse .lb-bundle-widget :focus-visible,
|
|
1997
|
-
.using-mouse .lb-mix-match__modal-overlay :focus,
|
|
1998
|
-
.using-mouse .lb-mix-match__modal-overlay :focus-visible {
|
|
2404
|
+
.using-mouse .lb-bundle-widget :focus:not([aria-checked="true"]),
|
|
2405
|
+
.using-mouse .lb-bundle-widget :focus-visible:not([aria-checked="true"]),
|
|
2406
|
+
.using-mouse .lb-mix-match__modal-overlay :focus:not([aria-checked="true"]),
|
|
2407
|
+
.using-mouse .lb-mix-match__modal-overlay :focus-visible:not([aria-checked="true"]) {
|
|
1999
2408
|
outline: none;
|
|
2000
2409
|
outline-offset: 0;
|
|
2001
2410
|
box-shadow: none;
|
|
@@ -2234,6 +2643,22 @@ var BUNDLE_FIXED_CSS = `/* Lime Bundles \u2014 Fixed bundle styles */
|
|
|
2234
2643
|
flex-direction: column;
|
|
2235
2644
|
gap: 0;
|
|
2236
2645
|
margin: 0;
|
|
2646
|
+
max-height: var(--lb-list-max-height);
|
|
2647
|
+
overflow-y: auto;
|
|
2648
|
+
/* Custom scrollbar \u2014 text colour at 15% opacity (thumb) and 5% (track). */
|
|
2649
|
+
scrollbar-width: thin;
|
|
2650
|
+
scrollbar-color: color-mix(in srgb, var(--lb-text) 15%, transparent)
|
|
2651
|
+
color-mix(in srgb, var(--lb-text) 2%, transparent);
|
|
2652
|
+
}
|
|
2653
|
+
|
|
2654
|
+
.lb-fixed__products::-webkit-scrollbar { width: 4px; }
|
|
2655
|
+
.lb-fixed__products::-webkit-scrollbar-track {
|
|
2656
|
+
background: color-mix(in srgb, var(--lb-text) 2%, transparent);
|
|
2657
|
+
border-radius: 2px;
|
|
2658
|
+
}
|
|
2659
|
+
.lb-fixed__products::-webkit-scrollbar-thumb {
|
|
2660
|
+
background: color-mix(in srgb, var(--lb-text) 15%, transparent);
|
|
2661
|
+
border-radius: 2px;
|
|
2237
2662
|
}
|
|
2238
2663
|
|
|
2239
2664
|
/* Fixed bundles: product rows */
|
|
@@ -2289,6 +2714,28 @@ var BUNDLE_FIXED_CSS = `/* Lime Bundles \u2014 Fixed bundle styles */
|
|
|
2289
2714
|
`;
|
|
2290
2715
|
var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
|
|
2291
2716
|
|
|
2717
|
+
/* === Slot list ============================================================
|
|
2718
|
+
Caps the height of the slot stack so long bundles don't push the CTA off
|
|
2719
|
+
the page. Internal scroll with the same custom 4px scrollbar as the
|
|
2720
|
+
variant dropdown panel. */
|
|
2721
|
+
.lb-mix-match__slots {
|
|
2722
|
+
max-height: var(--lb-list-max-height);
|
|
2723
|
+
overflow-y: auto;
|
|
2724
|
+
scrollbar-width: thin;
|
|
2725
|
+
scrollbar-color: color-mix(in srgb, var(--lb-text) 15%, transparent)
|
|
2726
|
+
color-mix(in srgb, var(--lb-text) 2%, transparent);
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
.lb-mix-match__slots::-webkit-scrollbar { width: 4px; }
|
|
2730
|
+
.lb-mix-match__slots::-webkit-scrollbar-track {
|
|
2731
|
+
background: color-mix(in srgb, var(--lb-text) 2%, transparent);
|
|
2732
|
+
border-radius: 2px;
|
|
2733
|
+
}
|
|
2734
|
+
.lb-mix-match__slots::-webkit-scrollbar-thumb {
|
|
2735
|
+
background: color-mix(in srgb, var(--lb-text) 15%, transparent);
|
|
2736
|
+
border-radius: 2px;
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2292
2739
|
/* === Progress Bar === */
|
|
2293
2740
|
.lb-mix-match__progress {
|
|
2294
2741
|
margin-bottom: 16px;
|
|
@@ -2805,6 +3252,22 @@ var BUNDLE_VOLUME_CSS = `/* Lime Bundles \u2014 Volume / Quantity Breaks styles
|
|
|
2805
3252
|
display: flex;
|
|
2806
3253
|
flex-direction: column;
|
|
2807
3254
|
gap: 12px;
|
|
3255
|
+
max-height: var(--lb-list-max-height);
|
|
3256
|
+
overflow-y: auto;
|
|
3257
|
+
/* Custom scrollbar \u2014 text colour at 15% opacity (thumb) and 5% (track). */
|
|
3258
|
+
scrollbar-width: thin;
|
|
3259
|
+
scrollbar-color: color-mix(in srgb, var(--lb-text) 15%, transparent)
|
|
3260
|
+
color-mix(in srgb, var(--lb-text) 2%, transparent);
|
|
3261
|
+
}
|
|
3262
|
+
|
|
3263
|
+
.lb-volume__tiers::-webkit-scrollbar { width: 4px; }
|
|
3264
|
+
.lb-volume__tiers::-webkit-scrollbar-track {
|
|
3265
|
+
background: color-mix(in srgb, var(--lb-text) 2%, transparent);
|
|
3266
|
+
border-radius: 2px;
|
|
3267
|
+
}
|
|
3268
|
+
.lb-volume__tiers::-webkit-scrollbar-thumb {
|
|
3269
|
+
background: color-mix(in srgb, var(--lb-text) 15%, transparent);
|
|
3270
|
+
border-radius: 2px;
|
|
2808
3271
|
}
|
|
2809
3272
|
|
|
2810
3273
|
.lb-volume__tier {
|
|
@@ -2820,10 +3283,21 @@ var BUNDLE_VOLUME_CSS = `/* Lime Bundles \u2014 Volume / Quantity Breaks styles
|
|
|
2820
3283
|
}
|
|
2821
3284
|
|
|
2822
3285
|
.lb-volume__tier:hover {
|
|
2823
|
-
border-color:
|
|
3286
|
+
border-color: var(--lb-tier-selected-border-color);
|
|
2824
3287
|
}
|
|
2825
3288
|
|
|
2826
|
-
|
|
3289
|
+
/* Suppress the UA default focus outline so a freshly-clicked selected
|
|
3290
|
+
tier doesn't briefly show the 1px focus ring on top of (or in place of)
|
|
3291
|
+
the custom selected-state outline below. Keyboard focus is still
|
|
3292
|
+
indicated by the :focus-visible rule. */
|
|
3293
|
+
.lb-volume__tier:focus {
|
|
3294
|
+
outline: none;
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
/* Keyboard focus indicator \u2014 explicitly excludes the selected tier so
|
|
3298
|
+
the selected-state outline rule below has full ownership of the
|
|
3299
|
+
outline property when both states apply at once. */
|
|
3300
|
+
.lb-volume__tier:focus-visible:not([aria-checked="true"]) {
|
|
2827
3301
|
outline: 2px solid var(--lb-primary-color);
|
|
2828
3302
|
outline-offset: 2px;
|
|
2829
3303
|
}
|
|
@@ -2909,6 +3383,259 @@ var BUNDLE_VOLUME_CSS = `/* Lime Bundles \u2014 Volume / Quantity Breaks styles
|
|
|
2909
3383
|
}
|
|
2910
3384
|
|
|
2911
3385
|
|
|
3386
|
+
`;
|
|
3387
|
+
var BUNDLE_DROPDOWN_CSS = `/**
|
|
3388
|
+
* Lime Bundles \u2014 Custom variant-picker dropdown styling.
|
|
3389
|
+
*
|
|
3390
|
+
* Reuses existing CSS variables: no new merchant-configurable surface.
|
|
3391
|
+
* --lb-variant-border-{width,color}, --lb-variant-radius, --lb-variant-chevron
|
|
3392
|
+
* --lb-bg, --lb-text, --lb-primary-color
|
|
3393
|
+
*
|
|
3394
|
+
* Mix-match modal context overrides via .lb-mix-match__modal scope to use
|
|
3395
|
+
* --lb-picker-variant-* and --lb-picker-bg.
|
|
3396
|
+
*/
|
|
3397
|
+
|
|
3398
|
+
/* Hide the native <select> while keeping it form-serializable and focusable
|
|
3399
|
+
programmatically. The .lb-dropdown-state marker is added by JS at bind
|
|
3400
|
+
time, so this rule matches every variant-select class (main widget,
|
|
3401
|
+
mix-match modal, future bundle types). aria-hidden + tabindex=-1
|
|
3402
|
+
(also set in JS) remove it from the accessibility tree. */
|
|
3403
|
+
.lb-dropdown-state {
|
|
3404
|
+
position: absolute !important;
|
|
3405
|
+
width: 1px !important;
|
|
3406
|
+
height: 1px !important;
|
|
3407
|
+
padding: 0 !important;
|
|
3408
|
+
margin: -1px !important;
|
|
3409
|
+
overflow: hidden !important;
|
|
3410
|
+
clip: rect(0 0 0 0) !important;
|
|
3411
|
+
white-space: nowrap !important;
|
|
3412
|
+
border: 0 !important;
|
|
3413
|
+
pointer-events: none !important;
|
|
3414
|
+
}
|
|
3415
|
+
|
|
3416
|
+
/* Shell fills its parent column. */
|
|
3417
|
+
.lb-dropdown {
|
|
3418
|
+
position: relative;
|
|
3419
|
+
display: inline-block;
|
|
3420
|
+
width: 100%;
|
|
3421
|
+
max-width: 100%;
|
|
3422
|
+
font-family: inherit;
|
|
3423
|
+
}
|
|
3424
|
+
|
|
3425
|
+
/* Trigger styled identically to the closed-state native select */
|
|
3426
|
+
.lb-dropdown-trigger {
|
|
3427
|
+
display: inline-flex;
|
|
3428
|
+
align-items: center;
|
|
3429
|
+
justify-content: space-between;
|
|
3430
|
+
gap: 8px;
|
|
3431
|
+
width: 100%;
|
|
3432
|
+
border: var(--lb-variant-border-width) solid var(--lb-variant-border-color);
|
|
3433
|
+
border-radius: var(--lb-variant-radius);
|
|
3434
|
+
padding: 8px 12px;
|
|
3435
|
+
font-size: 12px;
|
|
3436
|
+
line-height: 16px;
|
|
3437
|
+
color: var(--lb-text);
|
|
3438
|
+
background: var(--lb-bg);
|
|
3439
|
+
font-family: inherit;
|
|
3440
|
+
cursor: pointer;
|
|
3441
|
+
appearance: none;
|
|
3442
|
+
-webkit-appearance: none;
|
|
3443
|
+
text-align: start;
|
|
3444
|
+
transition: border-color 120ms ease;
|
|
3445
|
+
}
|
|
3446
|
+
|
|
3447
|
+
.lb-dropdown-trigger:focus-visible {
|
|
3448
|
+
outline: 2px solid var(--lb-primary-color);
|
|
3449
|
+
outline-offset: 2px;
|
|
3450
|
+
}
|
|
3451
|
+
|
|
3452
|
+
.lb-dropdown-trigger[aria-expanded="true"] {
|
|
3453
|
+
border-color: var(--lb-text);
|
|
3454
|
+
}
|
|
3455
|
+
|
|
3456
|
+
.lb-dropdown-trigger-value {
|
|
3457
|
+
flex: 1 1 auto;
|
|
3458
|
+
white-space: nowrap;
|
|
3459
|
+
overflow: hidden;
|
|
3460
|
+
text-overflow: ellipsis;
|
|
3461
|
+
text-align: start;
|
|
3462
|
+
}
|
|
3463
|
+
|
|
3464
|
+
.lb-dropdown-chevron {
|
|
3465
|
+
flex: 0 0 auto;
|
|
3466
|
+
width: 12px;
|
|
3467
|
+
height: 12px;
|
|
3468
|
+
background: var(--lb-variant-chevron) center / contain no-repeat;
|
|
3469
|
+
transition: transform 120ms ease;
|
|
3470
|
+
}
|
|
3471
|
+
|
|
3472
|
+
.lb-dropdown-trigger[aria-expanded="true"] .lb-dropdown-chevron {
|
|
3473
|
+
transform: rotate(180deg);
|
|
3474
|
+
}
|
|
3475
|
+
|
|
3476
|
+
/* Popover panel \u2014 position: absolute against the .lb-dropdown shell
|
|
3477
|
+
(already position: relative). Top/left/width come from CSS so we
|
|
3478
|
+
never depend on JS having set inline coords by the time the panel
|
|
3479
|
+
becomes visible. JS only sets max-height. */
|
|
3480
|
+
.lb-dropdown-listbox {
|
|
3481
|
+
position: absolute;
|
|
3482
|
+
left: 0;
|
|
3483
|
+
/* Default to below-trigger placement so the panel doesn't overlap the
|
|
3484
|
+
trigger if data-placement is missing for any reason. The explicit
|
|
3485
|
+
[data-placement="down"|"up"] rules below override this. */
|
|
3486
|
+
top: calc(100% + 4px);
|
|
3487
|
+
width: 100%;
|
|
3488
|
+
z-index: 9999;
|
|
3489
|
+
margin: 0;
|
|
3490
|
+
padding: 4px 0;
|
|
3491
|
+
list-style: none;
|
|
3492
|
+
background: var(--lb-bg);
|
|
3493
|
+
color: var(--lb-text);
|
|
3494
|
+
border: var(--lb-variant-border-width) solid var(--lb-variant-border-color);
|
|
3495
|
+
border-radius: var(--lb-variant-radius);
|
|
3496
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
|
3497
|
+
overflow-y: auto;
|
|
3498
|
+
overflow-x: hidden;
|
|
3499
|
+
/* Custom scrollbar \u2014 text colour at 15% opacity (thumb) and 5% (track). */
|
|
3500
|
+
scrollbar-width: thin;
|
|
3501
|
+
scrollbar-color: color-mix(in srgb, var(--lb-text) 15%, transparent)
|
|
3502
|
+
color-mix(in srgb, var(--lb-text) 2%, transparent);
|
|
3503
|
+
animation: lb-dropdown-in-down 120ms ease-out;
|
|
3504
|
+
transform-origin: top center;
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
.lb-dropdown-listbox[data-placement="down"] {
|
|
3508
|
+
top: calc(100% + 4px);
|
|
3509
|
+
}
|
|
3510
|
+
|
|
3511
|
+
.lb-dropdown-listbox[data-placement="up"] {
|
|
3512
|
+
top: auto;
|
|
3513
|
+
bottom: calc(100% + 4px);
|
|
3514
|
+
animation-name: lb-dropdown-in-up;
|
|
3515
|
+
transform-origin: bottom center;
|
|
3516
|
+
}
|
|
3517
|
+
|
|
3518
|
+
/* When portaled out of the .lb-dropdown shell (mix-match modal context:
|
|
3519
|
+
.lb-mix-match__modal applies translateY which would otherwise trap
|
|
3520
|
+
position:fixed), switch to fixed and let JS set viewport coords. */
|
|
3521
|
+
.lb-dropdown-listbox[data-lb-dropdown-portal] {
|
|
3522
|
+
position: fixed;
|
|
3523
|
+
top: auto;
|
|
3524
|
+
left: auto;
|
|
3525
|
+
bottom: auto;
|
|
3526
|
+
width: auto;
|
|
3527
|
+
}
|
|
3528
|
+
|
|
3529
|
+
/* Custom scrollbar \u2014 Webkit/Blink: exact 4px width */
|
|
3530
|
+
.lb-dropdown-listbox::-webkit-scrollbar {
|
|
3531
|
+
width: 4px;
|
|
3532
|
+
}
|
|
3533
|
+
.lb-dropdown-listbox::-webkit-scrollbar-track {
|
|
3534
|
+
background: color-mix(in srgb, var(--lb-text) 2%, transparent);
|
|
3535
|
+
border-radius: 2px;
|
|
3536
|
+
}
|
|
3537
|
+
.lb-dropdown-listbox::-webkit-scrollbar-thumb {
|
|
3538
|
+
background: color-mix(in srgb, var(--lb-text) 15%, transparent);
|
|
3539
|
+
border-radius: 2px;
|
|
3540
|
+
}
|
|
3541
|
+
|
|
3542
|
+
/* Options */
|
|
3543
|
+
.lb-dropdown-option {
|
|
3544
|
+
padding: 8px 12px;
|
|
3545
|
+
font-size: 12px;
|
|
3546
|
+
line-height: 16px;
|
|
3547
|
+
cursor: pointer;
|
|
3548
|
+
white-space: nowrap;
|
|
3549
|
+
overflow: hidden;
|
|
3550
|
+
text-overflow: ellipsis;
|
|
3551
|
+
color: var(--lb-text);
|
|
3552
|
+
}
|
|
3553
|
+
|
|
3554
|
+
.lb-dropdown-option[aria-selected="true"] {
|
|
3555
|
+
font-weight: 600;
|
|
3556
|
+
}
|
|
3557
|
+
|
|
3558
|
+
.lb-dropdown-option.is-active,
|
|
3559
|
+
.lb-dropdown-option:hover:not([aria-disabled="true"]) {
|
|
3560
|
+
background: color-mix(in srgb, var(--lb-text) 2%, transparent);
|
|
3561
|
+
}
|
|
3562
|
+
|
|
3563
|
+
.lb-dropdown-option[aria-disabled="true"] {
|
|
3564
|
+
opacity: 0.4;
|
|
3565
|
+
cursor: not-allowed;
|
|
3566
|
+
}
|
|
3567
|
+
|
|
3568
|
+
/* Animations */
|
|
3569
|
+
@keyframes lb-dropdown-in-down {
|
|
3570
|
+
from { opacity: 0; transform: translateY(-4px) scale(0.98); }
|
|
3571
|
+
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
3572
|
+
}
|
|
3573
|
+
|
|
3574
|
+
@keyframes lb-dropdown-in-up {
|
|
3575
|
+
from { opacity: 0; transform: translateY(4px) scale(0.98); }
|
|
3576
|
+
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
3577
|
+
}
|
|
3578
|
+
|
|
3579
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3580
|
+
.lb-dropdown-listbox { animation: none; }
|
|
3581
|
+
.lb-dropdown-chevron { transition: none; }
|
|
3582
|
+
.lb-dropdown-trigger { transition: none; }
|
|
3583
|
+
}
|
|
3584
|
+
|
|
3585
|
+
/* Forced-colors mode (Windows high-contrast) */
|
|
3586
|
+
@media (forced-colors: active) {
|
|
3587
|
+
.lb-dropdown-trigger {
|
|
3588
|
+
border-color: ButtonBorder;
|
|
3589
|
+
color: ButtonText;
|
|
3590
|
+
background: ButtonFace;
|
|
3591
|
+
}
|
|
3592
|
+
.lb-dropdown-listbox {
|
|
3593
|
+
border-color: ButtonBorder;
|
|
3594
|
+
background: Canvas;
|
|
3595
|
+
color: CanvasText;
|
|
3596
|
+
}
|
|
3597
|
+
.lb-dropdown-option.is-active {
|
|
3598
|
+
background: Highlight;
|
|
3599
|
+
color: HighlightText;
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3603
|
+
/* Mix-match modal context \u2014 use picker-scoped variables.
|
|
3604
|
+
No CSS fallbacks: --lb-picker-* are always emitted by bundle-widget.liquid
|
|
3605
|
+
because WidgetConfig.parse() fully hydrates the merchant config.
|
|
3606
|
+
See docs/solutions/ui-bugs/widget-css-single-source-defaults.md. */
|
|
3607
|
+
.lb-mix-match__modal .lb-dropdown-trigger {
|
|
3608
|
+
border-color: var(--lb-picker-variant-border-color);
|
|
3609
|
+
border-width: var(--lb-picker-variant-border-width);
|
|
3610
|
+
border-radius: var(--lb-picker-variant-radius);
|
|
3611
|
+
background: var(--lb-picker-bg);
|
|
3612
|
+
color: var(--lb-picker-text);
|
|
3613
|
+
}
|
|
3614
|
+
|
|
3615
|
+
.lb-mix-match__modal .lb-dropdown-chevron {
|
|
3616
|
+
background-image: var(--lb-picker-variant-chevron);
|
|
3617
|
+
}
|
|
3618
|
+
|
|
3619
|
+
/* Listbox is portaled out of the transformed .lb-mix-match__modal up to
|
|
3620
|
+
its [data-modal-overlay] parent, so picker-scoped rules anchor on the
|
|
3621
|
+
overlay attribute, not the modal class. */
|
|
3622
|
+
[data-modal-overlay] > .lb-dropdown-listbox {
|
|
3623
|
+
border-color: var(--lb-picker-variant-border-color);
|
|
3624
|
+
border-width: var(--lb-picker-variant-border-width);
|
|
3625
|
+
border-radius: var(--lb-picker-variant-radius);
|
|
3626
|
+
background: var(--lb-picker-bg);
|
|
3627
|
+
color: var(--lb-picker-text);
|
|
3628
|
+
scrollbar-color: color-mix(in srgb, var(--lb-picker-text) 15%, transparent)
|
|
3629
|
+
color-mix(in srgb, var(--lb-picker-text) 2%, transparent);
|
|
3630
|
+
}
|
|
3631
|
+
|
|
3632
|
+
[data-modal-overlay] > .lb-dropdown-listbox::-webkit-scrollbar-track {
|
|
3633
|
+
background: color-mix(in srgb, var(--lb-picker-text) 2%, transparent);
|
|
3634
|
+
border-radius: 2px;
|
|
3635
|
+
}
|
|
3636
|
+
[data-modal-overlay] > .lb-dropdown-listbox::-webkit-scrollbar-thumb {
|
|
3637
|
+
background: color-mix(in srgb, var(--lb-picker-text) 15%, transparent);
|
|
3638
|
+
}
|
|
2912
3639
|
`;
|
|
2913
3640
|
var BUNDLE_SKELETON_CSS = `/* Lime Bundles \u2014 web-component loading skeleton (not mirrored to theme assets) */
|
|
2914
3641
|
|
|
@@ -3107,7 +3834,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3107
3834
|
const controller = new AbortController();
|
|
3108
3835
|
this.abortController = controller;
|
|
3109
3836
|
this.renderLoading();
|
|
3110
|
-
const client = (0,
|
|
3837
|
+
const client = (0, import_core7.createStorefrontClient)({
|
|
3111
3838
|
shopDomain: this.shopDomain,
|
|
3112
3839
|
accessToken: this.storefrontToken
|
|
3113
3840
|
});
|
|
@@ -3132,7 +3859,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3132
3859
|
handle
|
|
3133
3860
|
);
|
|
3134
3861
|
}
|
|
3135
|
-
const cssPromise = client.query(
|
|
3862
|
+
const cssPromise = client.query(import_core7.SHOP_CUSTOM_CSS_QUERY, void 0, {
|
|
3136
3863
|
signal: controller.signal
|
|
3137
3864
|
}).catch(() => null);
|
|
3138
3865
|
await bundlePromise;
|
|
@@ -3144,8 +3871,8 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3144
3871
|
}
|
|
3145
3872
|
const css = await cssPromise;
|
|
3146
3873
|
if (css?.shop?.metafield?.value) {
|
|
3147
|
-
(0,
|
|
3148
|
-
const sanitized = (0,
|
|
3874
|
+
(0, import_core7.injectCustomCss)(this.shopDomain, css.shop.metafield.value);
|
|
3875
|
+
const sanitized = (0, import_core7.sanitizeCustomCss)(css.shop.metafield.value);
|
|
3149
3876
|
if (sanitized.ok) this.shopCustomCss = sanitized.css;
|
|
3150
3877
|
}
|
|
3151
3878
|
await this.applyABVariants();
|
|
@@ -3173,14 +3900,14 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3173
3900
|
this.bundles.map(async (bundle) => {
|
|
3174
3901
|
if (!bundle.abTestId || !bundle.abVariantB) return bundle;
|
|
3175
3902
|
try {
|
|
3176
|
-
const assignment = await (0,
|
|
3903
|
+
const assignment = await (0, import_core7.getABTestAssignment)(
|
|
3177
3904
|
this.appUrl,
|
|
3178
3905
|
this.shopDomain,
|
|
3179
3906
|
bundle.abTestId,
|
|
3180
3907
|
bundle.id
|
|
3181
3908
|
);
|
|
3182
3909
|
if (assignment?.variant === "B") {
|
|
3183
|
-
return (0,
|
|
3910
|
+
return (0, import_core7.applyABVariantB)(bundle);
|
|
3184
3911
|
}
|
|
3185
3912
|
} catch (err) {
|
|
3186
3913
|
console.warn(
|
|
@@ -3195,7 +3922,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3195
3922
|
}
|
|
3196
3923
|
async fetchSingleBundle(client, signal) {
|
|
3197
3924
|
const data = await client.query(
|
|
3198
|
-
|
|
3925
|
+
import_core7.BUNDLE_METAOBJECT_QUERY,
|
|
3199
3926
|
{ id: this.bundleGid },
|
|
3200
3927
|
{ signal }
|
|
3201
3928
|
);
|
|
@@ -3203,7 +3930,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3203
3930
|
this.bundles = [];
|
|
3204
3931
|
return;
|
|
3205
3932
|
}
|
|
3206
|
-
const parsed = (0,
|
|
3933
|
+
const parsed = (0, import_core7.parseMetaobjectBundle)(
|
|
3207
3934
|
data.metaobject.id,
|
|
3208
3935
|
data.metaobject.fields
|
|
3209
3936
|
);
|
|
@@ -3211,7 +3938,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3211
3938
|
}
|
|
3212
3939
|
async fetchProductBundles(client, signal, productHandle) {
|
|
3213
3940
|
const data = await client.query(
|
|
3214
|
-
|
|
3941
|
+
import_core7.BUNDLES_FOR_PRODUCT_QUERY,
|
|
3215
3942
|
{ handle: productHandle },
|
|
3216
3943
|
{ signal }
|
|
3217
3944
|
);
|
|
@@ -3222,7 +3949,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3222
3949
|
const refs = data.product.metafield?.references?.nodes ?? [];
|
|
3223
3950
|
const bundles = [];
|
|
3224
3951
|
for (const ref of refs) {
|
|
3225
|
-
const parsed = (0,
|
|
3952
|
+
const parsed = (0, import_core7.parseMetaobjectBundle)(ref.id, ref.fields);
|
|
3226
3953
|
if (parsed) bundles.push(parsed);
|
|
3227
3954
|
}
|
|
3228
3955
|
this.bundles = bundles;
|
|
@@ -3255,7 +3982,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3255
3982
|
*/
|
|
3256
3983
|
async defaultAddToCart(lines) {
|
|
3257
3984
|
if (typeof window === "undefined") return;
|
|
3258
|
-
const client = (0,
|
|
3985
|
+
const client = (0, import_core7.createStorefrontClient)({
|
|
3259
3986
|
shopDomain: this.shopDomain,
|
|
3260
3987
|
accessToken: this.storefrontToken
|
|
3261
3988
|
});
|
|
@@ -3266,7 +3993,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3266
3993
|
let checkoutUrl = null;
|
|
3267
3994
|
if (existingCartId) {
|
|
3268
3995
|
const res = await client.query(
|
|
3269
|
-
|
|
3996
|
+
import_core7.CART_LINES_ADD_MUTATION,
|
|
3270
3997
|
{ cartId: existingCartId, lines }
|
|
3271
3998
|
);
|
|
3272
3999
|
const payload = res.cartLinesAdd;
|
|
@@ -3278,7 +4005,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3278
4005
|
}
|
|
3279
4006
|
if (!checkoutUrl) {
|
|
3280
4007
|
const res = await client.query(
|
|
3281
|
-
|
|
4008
|
+
import_core7.CART_CREATE_MUTATION,
|
|
3282
4009
|
{ input: { lines } }
|
|
3283
4010
|
);
|
|
3284
4011
|
const payload = res.cartCreate;
|
|
@@ -3324,7 +4051,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3324
4051
|
const price = variant ? parseFloat(variant.price.amount) : 0;
|
|
3325
4052
|
return sum + price * line.quantity;
|
|
3326
4053
|
}, 0);
|
|
3327
|
-
(0,
|
|
4054
|
+
(0, import_core7.reportAddToCart)(
|
|
3328
4055
|
{ shopDomain: this.shopDomain, appUrl: this.appUrl },
|
|
3329
4056
|
{
|
|
3330
4057
|
bundleGid: bundle.id,
|
|
@@ -3344,7 +4071,8 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3344
4071
|
BUNDLE_BASE_CSS,
|
|
3345
4072
|
BUNDLE_FIXED_CSS,
|
|
3346
4073
|
BUNDLE_MIX_MATCH_CSS,
|
|
3347
|
-
BUNDLE_VOLUME_CSS
|
|
4074
|
+
BUNDLE_VOLUME_CSS,
|
|
4075
|
+
BUNDLE_DROPDOWN_CSS
|
|
3348
4076
|
].join("\n");
|
|
3349
4077
|
this.shadow.appendChild(style);
|
|
3350
4078
|
if (this.shopCustomCss) {
|
|
@@ -3360,7 +4088,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3360
4088
|
container.setAttribute("aria-label", bundle.title);
|
|
3361
4089
|
container.setAttribute("data-bundle-type", bundle.bundleType);
|
|
3362
4090
|
container.setAttribute("data-bundle-gid", bundle.id);
|
|
3363
|
-
(0,
|
|
4091
|
+
(0, import_core8.applyWidgetConfigVars)(container, bundle.widgetConfig);
|
|
3364
4092
|
this.renderCleanups.push(trackInputMode(container));
|
|
3365
4093
|
const dispatch = (lines) => this.handleAddToCart(bundle, lines);
|
|
3366
4094
|
const registerCleanup = (fn) => this.renderCleanups.push(fn);
|
|
@@ -3396,8 +4124,8 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3396
4124
|
}
|
|
3397
4125
|
setupImpressionFor(bundle, element) {
|
|
3398
4126
|
if (!this.analyticsEnabled || !this.appUrl) return;
|
|
3399
|
-
const cleanup = (0,
|
|
3400
|
-
(0,
|
|
4127
|
+
const cleanup = (0, import_core7.observeImpression)(element, () => {
|
|
4128
|
+
(0, import_core7.reportImpression)(
|
|
3401
4129
|
{ shopDomain: this.shopDomain, appUrl: this.appUrl },
|
|
3402
4130
|
{
|
|
3403
4131
|
bundleGid: bundle.id,
|