@lime-bundles/widget 2.4.0 → 2.5.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/dist/index.cjs +1114 -162
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.js +1041 -90
- package/dist/index.js.map +1 -1
- package/dist/lime-bundle.global.js +399 -25
- 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
|
@@ -20,21 +20,399 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
LimeBundleElement: () => LimeBundleElement
|
|
23
|
+
LimeBundleElement: () => LimeBundleElement,
|
|
24
|
+
trackInputMode: () => trackInputMode
|
|
24
25
|
});
|
|
25
26
|
module.exports = __toCommonJS(index_exports);
|
|
26
27
|
|
|
27
28
|
// src/lime-bundle.ts
|
|
28
|
-
var
|
|
29
|
+
var import_core7 = require("@lime-bundles/core");
|
|
29
30
|
|
|
30
31
|
// src/renderers/fixed.ts
|
|
31
|
-
var
|
|
32
|
+
var import_core5 = require("@lime-bundles/core");
|
|
32
33
|
|
|
33
|
-
// src/
|
|
34
|
+
// src/dropdown/bind-dropdown.ts
|
|
34
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
|
+
}
|
|
35
410
|
|
|
36
|
-
// src/renderers/
|
|
411
|
+
// src/renderers/pricing.ts
|
|
37
412
|
var import_core2 = require("@lime-bundles/core");
|
|
413
|
+
|
|
414
|
+
// src/renderers/countdown.ts
|
|
415
|
+
var import_core3 = require("@lime-bundles/core");
|
|
38
416
|
function renderCountdown(endsAtIso) {
|
|
39
417
|
const parsed = parseIso(endsAtIso);
|
|
40
418
|
if (parsed === null) return null;
|
|
@@ -61,7 +439,7 @@ function renderCountdown(endsAtIso) {
|
|
|
61
439
|
stop();
|
|
62
440
|
return;
|
|
63
441
|
}
|
|
64
|
-
timer.textContent = (0,
|
|
442
|
+
timer.textContent = (0, import_core3.formatCountdown)(msLeft);
|
|
65
443
|
}
|
|
66
444
|
function stop() {
|
|
67
445
|
if (intervalId !== null) {
|
|
@@ -115,7 +493,7 @@ function el(tag, className, attrs = {}) {
|
|
|
115
493
|
}
|
|
116
494
|
|
|
117
495
|
// src/renderers/image.ts
|
|
118
|
-
var
|
|
496
|
+
var import_core4 = require("@lime-bundles/core");
|
|
119
497
|
|
|
120
498
|
// src/renderers/fixed.ts
|
|
121
499
|
var PLACEHOLDER_THUMB_SVG = `
|
|
@@ -126,7 +504,7 @@ var PLACEHOLDER_THUMB_SVG = `
|
|
|
126
504
|
</svg>`;
|
|
127
505
|
function renderFixedBundle(container, bundle, onAddToCart, onCleanup) {
|
|
128
506
|
const wc = bundle.widgetConfig;
|
|
129
|
-
const qtyFor = (productId, variantId) => (0,
|
|
507
|
+
const qtyFor = (productId, variantId) => (0, import_core5.resolveBundleQty)(bundle, productId, variantId);
|
|
130
508
|
const rows = [];
|
|
131
509
|
let oosCount = 0;
|
|
132
510
|
bundle.products.forEach((product, idx) => {
|
|
@@ -191,11 +569,13 @@ function renderFixedBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
191
569
|
})
|
|
192
570
|
);
|
|
193
571
|
container.appendChild(root);
|
|
572
|
+
bindAllDropdowns(root);
|
|
573
|
+
onCleanup?.(() => unbindAllDropdowns(root));
|
|
194
574
|
updatePricing();
|
|
195
575
|
function updatePricing() {
|
|
196
576
|
const totalCents = rows.reduce((sum, r) => {
|
|
197
577
|
if (!r.selected) return sum;
|
|
198
|
-
const unit = (0,
|
|
578
|
+
const unit = (0, import_core2.parseCents)(r.selected.price.amount);
|
|
199
579
|
return sum + unit * r.qty;
|
|
200
580
|
}, 0);
|
|
201
581
|
const saleCents = computeSale(totalCents, bundle.discountConfig, rows);
|
|
@@ -211,13 +591,12 @@ function renderFixedBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
211
591
|
}
|
|
212
592
|
function buildRowState(bundle, product, productIndex) {
|
|
213
593
|
const selectedVariantIds = bundle.selectedVariantIds?.[productIndex] ?? null;
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
const
|
|
219
|
-
const
|
|
220
|
-
const qty = selected ? (0, import_core4.resolveBundleQty)(bundle, product.id, selected.id) : 1;
|
|
594
|
+
const merchantScoped = selectedVariantIds && selectedVariantIds.length > 0 ? product.variants.nodes.filter((v) => selectedVariantIds.includes(v.id)) : product.variants.nodes;
|
|
595
|
+
const firstInStock = merchantScoped.find((v) => v.availableForSale) ?? null;
|
|
596
|
+
const eligibleVariants = merchantScoped;
|
|
597
|
+
const isOos = !firstInStock;
|
|
598
|
+
const selected = firstInStock ?? merchantScoped[0] ?? null;
|
|
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,36 +743,97 @@ 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) {
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
select.setAttribute("data-variant-select", "");
|
|
371
|
-
select.name = `lb-variant-${state.product.id.replace(/^.*\//, "")}`;
|
|
372
|
-
select.setAttribute(
|
|
373
|
-
"aria-label",
|
|
374
|
-
`Select variant for ${state.product.title}`
|
|
758
|
+
const optionNames = state.eligibleVariants[0].selectedOptions.map(
|
|
759
|
+
(o) => o.name
|
|
375
760
|
);
|
|
376
|
-
state.
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
761
|
+
const productIdTail = state.product.id.replace(/^.*\//, "");
|
|
762
|
+
const optionSelects = [];
|
|
763
|
+
const resolveVariant = (values) => state.eligibleVariants.find(
|
|
764
|
+
(v) => v.selectedOptions.every((o, i) => o.value === values[i]) && v.selectedOptions.length === values.length
|
|
765
|
+
) ?? null;
|
|
766
|
+
const syncSelectsToVariant = (variant) => {
|
|
767
|
+
variant.selectedOptions.forEach((o, i) => {
|
|
768
|
+
const sel = optionSelects[i];
|
|
769
|
+
if (sel && sel.value !== o.value) sel.value = o.value;
|
|
770
|
+
});
|
|
771
|
+
};
|
|
772
|
+
const isValueAvailable = (optionIndex, value, selected) => state.eligibleVariants.some((v) => {
|
|
773
|
+
if (!v.availableForSale) return false;
|
|
774
|
+
if (v.selectedOptions[optionIndex]?.value !== value) return false;
|
|
775
|
+
return v.selectedOptions.every(
|
|
776
|
+
(o, i) => i === optionIndex || o.value === selected[i]
|
|
386
777
|
);
|
|
387
|
-
|
|
778
|
+
});
|
|
779
|
+
const recomputeDisabled = (selected) => {
|
|
780
|
+
optionSelects.forEach((sel, i) => {
|
|
781
|
+
Array.from(sel.options).forEach((opt) => {
|
|
782
|
+
opt.disabled = !isValueAvailable(i, opt.value, selected);
|
|
783
|
+
});
|
|
784
|
+
});
|
|
785
|
+
};
|
|
786
|
+
const handleChange = () => {
|
|
787
|
+
const values = optionSelects.map((s) => s.value);
|
|
788
|
+
const variant = resolveVariant(values);
|
|
789
|
+
if (!variant) {
|
|
790
|
+
if (state.selected) {
|
|
791
|
+
syncSelectsToVariant(state.selected);
|
|
792
|
+
recomputeDisabled(state.selected.selectedOptions.map((o) => o.value));
|
|
793
|
+
}
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
388
796
|
state.selected = variant;
|
|
389
797
|
state.qty = qtyFor(state.product.id, variant.id);
|
|
390
798
|
if (qtyBadgeRef) qtyBadgeRef.textContent = String(state.qty);
|
|
391
799
|
applyVariantToRow(variant);
|
|
800
|
+
recomputeDisabled(variant.selectedOptions.map((o) => o.value));
|
|
392
801
|
onVariantChange();
|
|
802
|
+
};
|
|
803
|
+
const groupsContainer = el("div", "lb-bundle-variant-option-groups");
|
|
804
|
+
optionNames.forEach((name2, position) => {
|
|
805
|
+
const group = el("div", "lb-bundle-variant-option-group");
|
|
806
|
+
const label = el("span", "lb-bundle-variant-option-label");
|
|
807
|
+
label.textContent = name2;
|
|
808
|
+
group.appendChild(label);
|
|
809
|
+
const select = document.createElement("select");
|
|
810
|
+
select.className = "lb-bundle-variant-select";
|
|
811
|
+
select.setAttribute("data-variant-option", "");
|
|
812
|
+
select.setAttribute("data-option-position", String(position + 1));
|
|
813
|
+
select.name = `lb-variant-${productIdTail}-${position + 1}`;
|
|
814
|
+
select.setAttribute("aria-label", name2);
|
|
815
|
+
const seen = /* @__PURE__ */ new Set();
|
|
816
|
+
state.eligibleVariants.forEach((v) => {
|
|
817
|
+
const value = v.selectedOptions[position]?.value;
|
|
818
|
+
if (!value || seen.has(value)) return;
|
|
819
|
+
seen.add(value);
|
|
820
|
+
const opt = document.createElement("option");
|
|
821
|
+
opt.value = value;
|
|
822
|
+
opt.textContent = value;
|
|
823
|
+
if (state.selected?.selectedOptions[position]?.value === value) {
|
|
824
|
+
opt.selected = true;
|
|
825
|
+
}
|
|
826
|
+
select.appendChild(opt);
|
|
827
|
+
});
|
|
828
|
+
select.addEventListener("change", handleChange);
|
|
829
|
+
optionSelects.push(select);
|
|
830
|
+
group.appendChild(select);
|
|
831
|
+
groupsContainer.appendChild(group);
|
|
393
832
|
});
|
|
394
|
-
info.appendChild(
|
|
833
|
+
info.appendChild(groupsContainer);
|
|
834
|
+
if (state.selected) {
|
|
835
|
+
recomputeDisabled(state.selected.selectedOptions.map((o) => o.value));
|
|
836
|
+
}
|
|
395
837
|
} else if (state.eligibleVariants.length === 1 && state.product.variants.nodes.length > 1) {
|
|
396
838
|
const badge = el("span", "lb-bundle-variant-badge");
|
|
397
839
|
badge.textContent = state.eligibleVariants[0].title;
|
|
@@ -420,9 +862,9 @@ function renderPricingRow(bundle) {
|
|
|
420
862
|
return {
|
|
421
863
|
el: row,
|
|
422
864
|
update({ totalCents, saleCents, savingsCents, currency }) {
|
|
423
|
-
sale.textContent = (0,
|
|
865
|
+
sale.textContent = (0, import_core2.formatCents)(saleCents, currency);
|
|
424
866
|
if (bundle.widgetConfig.pricing.showCompareAtPrice && savingsCents > 0) {
|
|
425
|
-
compare.textContent = (0,
|
|
867
|
+
compare.textContent = (0, import_core2.formatCents)(totalCents, currency);
|
|
426
868
|
compare.style.display = "";
|
|
427
869
|
} else {
|
|
428
870
|
compare.style.display = "none";
|
|
@@ -447,7 +889,7 @@ function renderSavingsBar() {
|
|
|
447
889
|
return;
|
|
448
890
|
}
|
|
449
891
|
bar.style.display = "";
|
|
450
|
-
amount.textContent = (0,
|
|
892
|
+
amount.textContent = (0, import_core2.formatCents)(savingsCents, currency);
|
|
451
893
|
}
|
|
452
894
|
};
|
|
453
895
|
}
|
|
@@ -469,7 +911,7 @@ function computeSale(totalCents, discount, rows) {
|
|
|
469
911
|
let saleCents = 0;
|
|
470
912
|
for (const r of rows) {
|
|
471
913
|
if (!r.selected) continue;
|
|
472
|
-
const unit = (0,
|
|
914
|
+
const unit = (0, import_core2.parseCents)(r.selected.price.amount);
|
|
473
915
|
const off = Math.floor(unit * discount.discountValue / 100);
|
|
474
916
|
const perUnit = Math.max(0, unit - off);
|
|
475
917
|
saleCents += perUnit * r.qty;
|
|
@@ -480,6 +922,7 @@ function computeSale(totalCents, discount, rows) {
|
|
|
480
922
|
}
|
|
481
923
|
|
|
482
924
|
// src/renderers/mix-match.ts
|
|
925
|
+
var import_core6 = require("@lime-bundles/core");
|
|
483
926
|
var PLACEHOLDER_THUMB_SVG2 = `
|
|
484
927
|
<svg class="lb-bundle-placeholder-icon" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
|
|
485
928
|
<rect x="4" y="4" width="20" height="20" rx="3"></rect>
|
|
@@ -547,9 +990,6 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
547
990
|
showSearch: wc.showSearch,
|
|
548
991
|
onAdd: (product, variant) => addSelection(product, variant),
|
|
549
992
|
onRemove: (productId, variantId) => removeSelection(productId, variantId),
|
|
550
|
-
countFor: (productId, variantId) => selections.filter(
|
|
551
|
-
(s) => s.productId === productId && s.variantId === variantId
|
|
552
|
-
).length,
|
|
553
993
|
isOverMax: () => selections.length >= maxQty
|
|
554
994
|
});
|
|
555
995
|
root.appendChild(modal.el);
|
|
@@ -559,7 +999,7 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
559
999
|
if (cta.disabled) return;
|
|
560
1000
|
const lines = selections.map((s) => ({
|
|
561
1001
|
merchandiseId: s.variantId,
|
|
562
|
-
quantity:
|
|
1002
|
+
quantity: s.quantity,
|
|
563
1003
|
attributes: [
|
|
564
1004
|
{ key: "_lime_bundle_gid", value: bundle.id },
|
|
565
1005
|
{ key: "_lime_bundle_type", value: bundle.bundleType }
|
|
@@ -578,6 +1018,7 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
578
1018
|
})
|
|
579
1019
|
);
|
|
580
1020
|
container.appendChild(root);
|
|
1021
|
+
onCleanup?.(() => unbindAllDropdowns(root));
|
|
581
1022
|
const firstEligible = eligible.find((ep) => !ep.isOos);
|
|
582
1023
|
const firstVariant = firstEligible?.firstAvailableVariant ?? firstEligible?.variants[0];
|
|
583
1024
|
if (firstEligible && firstVariant) {
|
|
@@ -586,14 +1027,15 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
586
1027
|
productTitle: firstEligible.product.title,
|
|
587
1028
|
variantId: firstVariant.id,
|
|
588
1029
|
variantTitle: firstVariant.title,
|
|
589
|
-
imageUrl: firstEligible.product.featuredImage?.url ?? null,
|
|
590
|
-
priceCents: (0,
|
|
591
|
-
compareCents: firstVariant.compareAtPrice ? (0,
|
|
592
|
-
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)(
|
|
593
1034
|
firstVariant.unitPrice,
|
|
594
1035
|
firstVariant.unitPriceMeasurement,
|
|
595
1036
|
currency
|
|
596
|
-
)
|
|
1037
|
+
),
|
|
1038
|
+
quantity: (0, import_core6.resolveBundleQty)(bundle, firstEligible.product.id, firstVariant.id)
|
|
597
1039
|
});
|
|
598
1040
|
}
|
|
599
1041
|
afterMutation();
|
|
@@ -604,14 +1046,15 @@ function renderMixMatchBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
604
1046
|
productTitle: product.title,
|
|
605
1047
|
variantId: variant.id,
|
|
606
1048
|
variantTitle: variant.title,
|
|
607
|
-
imageUrl: product.featuredImage?.url ?? null,
|
|
608
|
-
priceCents: (0,
|
|
609
|
-
compareCents: variant.compareAtPrice ? (0,
|
|
610
|
-
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)(
|
|
611
1053
|
variant.unitPrice,
|
|
612
1054
|
variant.unitPriceMeasurement,
|
|
613
1055
|
currency
|
|
614
|
-
)
|
|
1056
|
+
),
|
|
1057
|
+
quantity: (0, import_core6.resolveBundleQty)(bundle, product.id, variant.id)
|
|
615
1058
|
});
|
|
616
1059
|
afterMutation();
|
|
617
1060
|
}
|
|
@@ -678,7 +1121,7 @@ function renderHeader2(bundle) {
|
|
|
678
1121
|
if (discountType === "percentage" && discountValue > 0) {
|
|
679
1122
|
label = `-${Math.round(discountValue)}%`;
|
|
680
1123
|
} else if (discountType === "fixed_amount" && discountValue > 0) {
|
|
681
|
-
label = `-${(0,
|
|
1124
|
+
label = `-${(0, import_core2.formatCents)(
|
|
682
1125
|
Math.round(discountValue * 100),
|
|
683
1126
|
bundle.products[0]?.priceRange.minVariantPrice.currencyCode ?? "USD"
|
|
684
1127
|
)}`;
|
|
@@ -765,19 +1208,22 @@ function renderFilledSlot(selection, index, currency, onRemove) {
|
|
|
765
1208
|
const thumb = el("div", "lb-bundle-thumbnail", { "data-thumbnail": "" });
|
|
766
1209
|
if (selection.imageUrl) {
|
|
767
1210
|
const img = document.createElement("img");
|
|
768
|
-
img.src = (0,
|
|
769
|
-
width:
|
|
770
|
-
height:
|
|
1211
|
+
img.src = (0, import_core4.transformImageUrl)(selection.imageUrl, {
|
|
1212
|
+
width: import_core4.THUMB_PX,
|
|
1213
|
+
height: import_core4.THUMB_PX,
|
|
771
1214
|
crop: "center"
|
|
772
1215
|
});
|
|
773
1216
|
img.alt = selection.productTitle;
|
|
774
|
-
img.width =
|
|
775
|
-
img.height =
|
|
1217
|
+
img.width = import_core4.THUMB_PX;
|
|
1218
|
+
img.height = import_core4.THUMB_PX;
|
|
776
1219
|
img.loading = "lazy";
|
|
777
1220
|
thumb.appendChild(img);
|
|
778
1221
|
} else {
|
|
779
1222
|
thumb.insertAdjacentHTML("beforeend", PLACEHOLDER_THUMB_SVG2);
|
|
780
1223
|
}
|
|
1224
|
+
const qtyBadge = el("span", "lb-bundle-qty-badge");
|
|
1225
|
+
qtyBadge.textContent = String(selection.quantity);
|
|
1226
|
+
thumb.appendChild(qtyBadge);
|
|
781
1227
|
slot.appendChild(thumb);
|
|
782
1228
|
const info = el("div", "lb-mix-match__filled-info");
|
|
783
1229
|
const title = el("span", "lb-mix-match__filled-title");
|
|
@@ -788,14 +1234,16 @@ function renderFilledSlot(selection, index, currency, onRemove) {
|
|
|
788
1234
|
variant.textContent = selection.variantTitle;
|
|
789
1235
|
info.appendChild(variant);
|
|
790
1236
|
}
|
|
1237
|
+
const linePrice = selection.priceCents * selection.quantity;
|
|
1238
|
+
const lineCompare = selection.compareCents !== null ? selection.compareCents * selection.quantity : null;
|
|
791
1239
|
const priceWrap = el("span", "lb-mix-match__filled-price");
|
|
792
|
-
if (
|
|
1240
|
+
if (lineCompare !== null && lineCompare > linePrice) {
|
|
793
1241
|
const compare = el("span", "lb-mix-match__filled-compare");
|
|
794
|
-
compare.textContent = (0,
|
|
1242
|
+
compare.textContent = (0, import_core2.formatCents)(lineCompare, currency);
|
|
795
1243
|
priceWrap.appendChild(compare);
|
|
796
1244
|
}
|
|
797
1245
|
const priceEl = document.createElement("span");
|
|
798
|
-
priceEl.textContent = (0,
|
|
1246
|
+
priceEl.textContent = (0, import_core2.formatCents)(linePrice, currency);
|
|
799
1247
|
priceWrap.appendChild(priceEl);
|
|
800
1248
|
info.appendChild(priceWrap);
|
|
801
1249
|
if (selection.unitPriceLabel) {
|
|
@@ -836,15 +1284,18 @@ function renderPricingSection(showCompareAtPrice) {
|
|
|
836
1284
|
return;
|
|
837
1285
|
}
|
|
838
1286
|
wrap.style.display = "";
|
|
839
|
-
const totalCents = selections.reduce(
|
|
840
|
-
|
|
1287
|
+
const totalCents = selections.reduce(
|
|
1288
|
+
(s, sel) => s + sel.priceCents * sel.quantity,
|
|
1289
|
+
0
|
|
1290
|
+
);
|
|
1291
|
+
const saleCents = (0, import_core2.computeBundleSaleCents)(totalCents, bundle.discountConfig);
|
|
841
1292
|
if (showCompareAtPrice && totalCents > saleCents) {
|
|
842
|
-
compare.textContent = (0,
|
|
1293
|
+
compare.textContent = (0, import_core2.formatCents)(totalCents, currency);
|
|
843
1294
|
compare.style.display = "";
|
|
844
1295
|
} else {
|
|
845
1296
|
compare.style.display = "none";
|
|
846
1297
|
}
|
|
847
|
-
sale.textContent = (0,
|
|
1298
|
+
sale.textContent = (0, import_core2.formatCents)(saleCents, currency);
|
|
848
1299
|
}
|
|
849
1300
|
return { el: wrap, update };
|
|
850
1301
|
}
|
|
@@ -863,15 +1314,18 @@ function renderSavingsBar2() {
|
|
|
863
1314
|
wrap.style.display = "none";
|
|
864
1315
|
return;
|
|
865
1316
|
}
|
|
866
|
-
const totalCents = selections.reduce(
|
|
867
|
-
|
|
1317
|
+
const totalCents = selections.reduce(
|
|
1318
|
+
(s, sel) => s + sel.priceCents * sel.quantity,
|
|
1319
|
+
0
|
|
1320
|
+
);
|
|
1321
|
+
const saleCents = (0, import_core2.computeBundleSaleCents)(totalCents, bundle.discountConfig);
|
|
868
1322
|
const savings = Math.max(0, totalCents - saleCents);
|
|
869
1323
|
if (savings <= 0) {
|
|
870
1324
|
wrap.style.display = "none";
|
|
871
1325
|
return;
|
|
872
1326
|
}
|
|
873
1327
|
wrap.style.display = "";
|
|
874
|
-
amount.textContent = (0,
|
|
1328
|
+
amount.textContent = (0, import_core2.formatCents)(savings, currency);
|
|
875
1329
|
}
|
|
876
1330
|
return { el: wrap, update };
|
|
877
1331
|
}
|
|
@@ -957,8 +1411,8 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
957
1411
|
rowsBuilt = true;
|
|
958
1412
|
list.innerHTML = "";
|
|
959
1413
|
eligible.forEach((ep) => {
|
|
960
|
-
const availableVariants = ep.variants
|
|
961
|
-
const firstAvailVariant =
|
|
1414
|
+
const availableVariants = ep.variants;
|
|
1415
|
+
const firstAvailVariant = ep.variants.find((v) => v.availableForSale) ?? ep.firstAvailableVariant ?? ep.variants[0];
|
|
962
1416
|
if (!firstAvailVariant) return;
|
|
963
1417
|
let currentVariant = firstAvailVariant;
|
|
964
1418
|
const productEl = el(
|
|
@@ -967,23 +1421,28 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
967
1421
|
{ "data-product-id": ep.product.id.replace(/^.*\//, "") }
|
|
968
1422
|
);
|
|
969
1423
|
const thumb = el("div", "lb-mix-match__modal-product-thumb");
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
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,
|
|
975
1432
|
crop: "center"
|
|
976
1433
|
});
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
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);
|
|
982
1439
|
} else {
|
|
983
1440
|
thumb.insertAdjacentHTML("beforeend", PLACEHOLDER_THUMB_SVG2);
|
|
984
1441
|
}
|
|
985
1442
|
const countBadge = el("span", "lb-bundle-qty-badge");
|
|
986
|
-
countBadge.
|
|
1443
|
+
countBadge.textContent = String(
|
|
1444
|
+
(0, import_core6.resolveBundleQty)(bundle, ep.product.id, currentVariant.id)
|
|
1445
|
+
);
|
|
987
1446
|
thumb.appendChild(countBadge);
|
|
988
1447
|
productEl.appendChild(thumb);
|
|
989
1448
|
const info = el("div", "lb-mix-match__modal-product-info");
|
|
@@ -991,8 +1450,8 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
991
1450
|
title.textContent = ep.product.title;
|
|
992
1451
|
info.appendChild(title);
|
|
993
1452
|
const price = el("p", "lb-mix-match__modal-product-price");
|
|
994
|
-
price.textContent = (0,
|
|
995
|
-
(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),
|
|
996
1455
|
currency
|
|
997
1456
|
);
|
|
998
1457
|
info.appendChild(price);
|
|
@@ -1000,7 +1459,7 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1000
1459
|
"p",
|
|
1001
1460
|
"lb-mix-match__modal-product-unit-price lb-bundle-product-unit-price"
|
|
1002
1461
|
);
|
|
1003
|
-
const initialUnitText = (0,
|
|
1462
|
+
const initialUnitText = (0, import_core2.formatUnitPrice)(
|
|
1004
1463
|
currentVariant.unitPrice,
|
|
1005
1464
|
currentVariant.unitPriceMeasurement,
|
|
1006
1465
|
currency
|
|
@@ -1019,31 +1478,56 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1019
1478
|
}
|
|
1020
1479
|
};
|
|
1021
1480
|
if (availableVariants.length > 1) {
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
select.setAttribute("data-variant-select", "");
|
|
1025
|
-
select.name = `lb-variant-${ep.product.id.replace(/^.*\//, "")}`;
|
|
1026
|
-
select.setAttribute(
|
|
1027
|
-
"aria-label",
|
|
1028
|
-
`Select variant for ${ep.product.title}`
|
|
1481
|
+
const optionNames = availableVariants[0].selectedOptions.map(
|
|
1482
|
+
(o) => o.name
|
|
1029
1483
|
);
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1484
|
+
const productIdTail = ep.product.id.replace(/^.*\//, "");
|
|
1485
|
+
const optionSelects = [];
|
|
1486
|
+
const resolveVariant = (values) => availableVariants.find(
|
|
1487
|
+
(v) => v.selectedOptions.length === values.length && v.selectedOptions.every((o, i) => o.value === values[i])
|
|
1488
|
+
) ?? null;
|
|
1489
|
+
const syncSelectsToVariant = (v) => {
|
|
1490
|
+
v.selectedOptions.forEach((o, i) => {
|
|
1491
|
+
const sel = optionSelects[i];
|
|
1492
|
+
if (sel && sel.value !== o.value) sel.value = o.value;
|
|
1493
|
+
});
|
|
1494
|
+
};
|
|
1495
|
+
const isValueAvailable = (optionIndex, value, selected) => availableVariants.some((v) => {
|
|
1496
|
+
if (!v.availableForSale) return false;
|
|
1497
|
+
if (v.selectedOptions[optionIndex]?.value !== value) return false;
|
|
1498
|
+
return v.selectedOptions.every(
|
|
1499
|
+
(o, i) => i === optionIndex || o.value === selected[i]
|
|
1500
|
+
);
|
|
1036
1501
|
});
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1502
|
+
const recomputeDisabled = (selected) => {
|
|
1503
|
+
optionSelects.forEach((sel, i) => {
|
|
1504
|
+
Array.from(sel.options).forEach((opt) => {
|
|
1505
|
+
opt.disabled = !isValueAvailable(i, opt.value, selected);
|
|
1506
|
+
});
|
|
1507
|
+
});
|
|
1508
|
+
};
|
|
1509
|
+
const handleChange = () => {
|
|
1510
|
+
const values = optionSelects.map((s) => s.value);
|
|
1511
|
+
const next = resolveVariant(values);
|
|
1512
|
+
if (!next) {
|
|
1513
|
+
syncSelectsToVariant(currentVariant);
|
|
1514
|
+
recomputeDisabled(
|
|
1515
|
+
currentVariant.selectedOptions.map((o) => o.value)
|
|
1516
|
+
);
|
|
1517
|
+
return;
|
|
1518
|
+
}
|
|
1040
1519
|
currentVariant = next;
|
|
1041
1520
|
row.variant = next;
|
|
1042
|
-
|
|
1043
|
-
|
|
1521
|
+
const nextQty = (0, import_core6.resolveBundleQty)(
|
|
1522
|
+
bundle,
|
|
1523
|
+
ep.product.id,
|
|
1524
|
+
currentVariant.id
|
|
1525
|
+
);
|
|
1526
|
+
price.textContent = (0, import_core2.formatCents)(
|
|
1527
|
+
(0, import_core2.parseCents)(currentVariant.price.amount) * nextQty,
|
|
1044
1528
|
currency
|
|
1045
1529
|
);
|
|
1046
|
-
const nextUnitText = (0,
|
|
1530
|
+
const nextUnitText = (0, import_core2.formatUnitPrice)(
|
|
1047
1531
|
currentVariant.unitPrice,
|
|
1048
1532
|
currentVariant.unitPriceMeasurement,
|
|
1049
1533
|
currency
|
|
@@ -1055,9 +1539,52 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1055
1539
|
unitPrice.textContent = "";
|
|
1056
1540
|
unitPrice.hidden = true;
|
|
1057
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
|
+
}
|
|
1551
|
+
recomputeDisabled(next.selectedOptions.map((o) => o.value));
|
|
1058
1552
|
rowUpdateCount();
|
|
1553
|
+
};
|
|
1554
|
+
const groupsContainer = el("div", "lb-bundle-variant-option-groups");
|
|
1555
|
+
optionNames.forEach((name, position) => {
|
|
1556
|
+
const group = el("div", "lb-bundle-variant-option-group");
|
|
1557
|
+
const label = el("span", "lb-bundle-variant-option-label");
|
|
1558
|
+
label.textContent = name;
|
|
1559
|
+
group.appendChild(label);
|
|
1560
|
+
const select = document.createElement("select");
|
|
1561
|
+
select.className = "lb-mix-match__variant-select";
|
|
1562
|
+
select.setAttribute("data-variant-option", "");
|
|
1563
|
+
select.setAttribute("data-option-position", String(position + 1));
|
|
1564
|
+
select.name = `lb-variant-${productIdTail}-${position + 1}`;
|
|
1565
|
+
select.setAttribute("aria-label", name);
|
|
1566
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1567
|
+
availableVariants.forEach((v) => {
|
|
1568
|
+
const value = v.selectedOptions[position]?.value;
|
|
1569
|
+
if (!value || seen.has(value)) return;
|
|
1570
|
+
seen.add(value);
|
|
1571
|
+
const opt = document.createElement("option");
|
|
1572
|
+
opt.value = value;
|
|
1573
|
+
opt.textContent = value;
|
|
1574
|
+
if (firstAvailVariant.selectedOptions[position]?.value === value) {
|
|
1575
|
+
opt.selected = true;
|
|
1576
|
+
}
|
|
1577
|
+
select.appendChild(opt);
|
|
1578
|
+
});
|
|
1579
|
+
select.addEventListener("change", handleChange);
|
|
1580
|
+
optionSelects.push(select);
|
|
1581
|
+
group.appendChild(select);
|
|
1582
|
+
groupsContainer.appendChild(group);
|
|
1059
1583
|
});
|
|
1060
|
-
info.appendChild(
|
|
1584
|
+
info.appendChild(groupsContainer);
|
|
1585
|
+
recomputeDisabled(
|
|
1586
|
+
firstAvailVariant.selectedOptions.map((o) => o.value)
|
|
1587
|
+
);
|
|
1061
1588
|
} else if (availableVariants.length === 1 && firstAvailVariant.title !== "Default Title") {
|
|
1062
1589
|
const variantLabel = el("span", "lb-mix-match__filled-variant");
|
|
1063
1590
|
variantLabel.textContent = firstAvailVariant.title;
|
|
@@ -1070,13 +1597,9 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1070
1597
|
}
|
|
1071
1598
|
productEl.appendChild(info);
|
|
1072
1599
|
const rowUpdateCount = () => {
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
countBadge.style.display = "";
|
|
1077
|
-
} else {
|
|
1078
|
-
countBadge.style.display = "none";
|
|
1079
|
-
}
|
|
1600
|
+
countBadge.textContent = String(
|
|
1601
|
+
(0, import_core6.resolveBundleQty)(bundle, ep.product.id, currentVariant.id)
|
|
1602
|
+
);
|
|
1080
1603
|
};
|
|
1081
1604
|
if (!ep.isOos) {
|
|
1082
1605
|
const addBtn = document.createElement("button");
|
|
@@ -1086,6 +1609,7 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1086
1609
|
addBtn.addEventListener("click", () => {
|
|
1087
1610
|
if (handlers.isOverMax()) return;
|
|
1088
1611
|
handlers.onAdd(ep.product, currentVariant);
|
|
1612
|
+
close();
|
|
1089
1613
|
});
|
|
1090
1614
|
productEl.appendChild(addBtn);
|
|
1091
1615
|
}
|
|
@@ -1094,6 +1618,7 @@ function renderModal(bundle, eligible, currency, handlers) {
|
|
|
1094
1618
|
productRows.push(row);
|
|
1095
1619
|
list.appendChild(productEl);
|
|
1096
1620
|
});
|
|
1621
|
+
bindAllDropdowns(list);
|
|
1097
1622
|
refreshCounts();
|
|
1098
1623
|
}
|
|
1099
1624
|
function applySearch() {
|
|
@@ -1197,7 +1722,7 @@ function renderVolumeBundle(container, bundle, onAddToCart, onCleanup) {
|
|
|
1197
1722
|
const product = bundle.products[0];
|
|
1198
1723
|
const variant = product?.variants.nodes.find((v) => v.availableForSale);
|
|
1199
1724
|
if (!variant && wc.outOfStockBehavior === "hide") return;
|
|
1200
|
-
const basePriceCents = variant ? (0,
|
|
1725
|
+
const basePriceCents = variant ? (0, import_core2.parseCents)(variant.price.amount) : 0;
|
|
1201
1726
|
const currency = variant?.price.currencyCode ?? "USD";
|
|
1202
1727
|
const discountType = bundle.discountConfig.discountType;
|
|
1203
1728
|
const resolved = bundle.volumeTiers.map((tier, index) => {
|
|
@@ -1376,13 +1901,13 @@ function renderTierCard(r, isSelected, currency, popularLabel, showComparePrice,
|
|
|
1376
1901
|
const price = el("span", "lb-volume__tier-price");
|
|
1377
1902
|
if (showComparePrice && r.pricePerUnitCents < r.basePricePerUnitCents) {
|
|
1378
1903
|
const compare = el("span", "lb-volume__tier-compare");
|
|
1379
|
-
compare.textContent = (0,
|
|
1904
|
+
compare.textContent = (0, import_core2.formatCents)(r.basePricePerUnitCents, currency);
|
|
1380
1905
|
price.appendChild(compare);
|
|
1381
1906
|
}
|
|
1382
1907
|
if (showPerUnitPrice) {
|
|
1383
1908
|
const each = document.createElement("span");
|
|
1384
1909
|
each.setAttribute("data-tier-price-each", "");
|
|
1385
|
-
each.textContent = (0,
|
|
1910
|
+
each.textContent = (0, import_core2.formatCents)(r.pricePerUnitCents, currency);
|
|
1386
1911
|
price.appendChild(each);
|
|
1387
1912
|
const unit = el("span", "lb-volume__tier-unit");
|
|
1388
1913
|
unit.textContent = " each";
|
|
@@ -1421,11 +1946,11 @@ function renderPricingRow2(resolved, selectedIndex, currency, showItemCount, sho
|
|
|
1421
1946
|
const compare = el("span", "lb-bundle-compare-price", {
|
|
1422
1947
|
"data-compare-price": ""
|
|
1423
1948
|
});
|
|
1424
|
-
compare.textContent = (0,
|
|
1949
|
+
compare.textContent = (0, import_core2.formatCents)(undiscountedCents, currency);
|
|
1425
1950
|
prices.appendChild(compare);
|
|
1426
1951
|
}
|
|
1427
1952
|
const sale = el("span", "lb-bundle-sale-price", { "data-total-price": "" });
|
|
1428
|
-
sale.textContent = (0,
|
|
1953
|
+
sale.textContent = (0, import_core2.formatCents)(totalCents, currency);
|
|
1429
1954
|
prices.appendChild(sale);
|
|
1430
1955
|
row.appendChild(prices);
|
|
1431
1956
|
return row;
|
|
@@ -1441,7 +1966,7 @@ function renderSavingsBar3(resolved, selectedIndex, currency) {
|
|
|
1441
1966
|
labelEl.textContent = "You save";
|
|
1442
1967
|
bar.appendChild(labelEl);
|
|
1443
1968
|
const amount = el("span", "", { "data-savings-amount": "" });
|
|
1444
|
-
amount.textContent = (0,
|
|
1969
|
+
amount.textContent = (0, import_core2.formatCents)(savings, currency);
|
|
1445
1970
|
bar.appendChild(amount);
|
|
1446
1971
|
return bar;
|
|
1447
1972
|
}
|
|
@@ -1462,7 +1987,7 @@ function badgeFor(resolved, currency, discountType) {
|
|
|
1462
1987
|
const { tier } = resolved;
|
|
1463
1988
|
if (discountType === "fixed_amount") {
|
|
1464
1989
|
const amount = tier.amount ?? 0;
|
|
1465
|
-
if (amount > 0) return `-${(0,
|
|
1990
|
+
if (amount > 0) return `-${(0, import_core2.formatCents)(Math.round(amount * 100), currency)}`;
|
|
1466
1991
|
return null;
|
|
1467
1992
|
}
|
|
1468
1993
|
if (discountType === "percentage") {
|
|
@@ -1489,7 +2014,62 @@ function clamp(n, min, max) {
|
|
|
1489
2014
|
}
|
|
1490
2015
|
|
|
1491
2016
|
// src/lime-bundle.ts
|
|
1492
|
-
var
|
|
2017
|
+
var import_core8 = require("@lime-bundles/core");
|
|
2018
|
+
|
|
2019
|
+
// src/utils/input-mode.ts
|
|
2020
|
+
var NAV_KEYS = /* @__PURE__ */ new Set([
|
|
2021
|
+
"Tab",
|
|
2022
|
+
"ArrowUp",
|
|
2023
|
+
"ArrowDown",
|
|
2024
|
+
"ArrowLeft",
|
|
2025
|
+
"ArrowRight",
|
|
2026
|
+
"Home",
|
|
2027
|
+
"End",
|
|
2028
|
+
"PageUp",
|
|
2029
|
+
"PageDown",
|
|
2030
|
+
"Enter",
|
|
2031
|
+
" ",
|
|
2032
|
+
"Escape"
|
|
2033
|
+
]);
|
|
2034
|
+
var targets = /* @__PURE__ */ new Set();
|
|
2035
|
+
var listenersAttached = false;
|
|
2036
|
+
function setAll(on) {
|
|
2037
|
+
const off = on === "using-mouse" ? "using-keyboard" : "using-mouse";
|
|
2038
|
+
for (const el2 of targets) {
|
|
2039
|
+
el2.classList.add(on);
|
|
2040
|
+
el2.classList.remove(off);
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
function onKeyDown(e) {
|
|
2044
|
+
if (NAV_KEYS.has(e.key)) setAll("using-keyboard");
|
|
2045
|
+
}
|
|
2046
|
+
function onPointerDown() {
|
|
2047
|
+
setAll("using-mouse");
|
|
2048
|
+
}
|
|
2049
|
+
function attachListeners() {
|
|
2050
|
+
if (listenersAttached) return;
|
|
2051
|
+
listenersAttached = true;
|
|
2052
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
2053
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
2054
|
+
}
|
|
2055
|
+
function detachListeners() {
|
|
2056
|
+
if (!listenersAttached) return;
|
|
2057
|
+
listenersAttached = false;
|
|
2058
|
+
document.removeEventListener("keydown", onKeyDown, true);
|
|
2059
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
2060
|
+
}
|
|
2061
|
+
function trackInputMode(target) {
|
|
2062
|
+
target.classList.add("using-mouse");
|
|
2063
|
+
target.classList.remove("using-keyboard");
|
|
2064
|
+
targets.add(target);
|
|
2065
|
+
attachListeners();
|
|
2066
|
+
return () => {
|
|
2067
|
+
targets.delete(target);
|
|
2068
|
+
target.classList.remove("using-mouse");
|
|
2069
|
+
target.classList.remove("using-keyboard");
|
|
2070
|
+
if (targets.size === 0) detachListeners();
|
|
2071
|
+
};
|
|
2072
|
+
}
|
|
1493
2073
|
|
|
1494
2074
|
// src/styles/bundle-css.ts
|
|
1495
2075
|
var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle widget types */
|
|
@@ -1504,6 +2084,11 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
|
|
|
1504
2084
|
--lb-thumbnail-bg: #F0F0F0;
|
|
1505
2085
|
--lb-widget-pad: 20px;
|
|
1506
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;
|
|
1507
2092
|
|
|
1508
2093
|
font-family: inherit;
|
|
1509
2094
|
font-size: 16px;
|
|
@@ -1775,6 +2360,56 @@ var BUNDLE_BASE_CSS = `/* Lime Bundles \u2014 shared base styles for all bundle
|
|
|
1775
2360
|
margin-top: 8px;
|
|
1776
2361
|
}
|
|
1777
2362
|
|
|
2363
|
+
/* Per-option variant pickers. Each option's label + select sit inside a
|
|
2364
|
+
.lb-bundle-variant-option-group (flex column, 2px gap between label and
|
|
2365
|
+
select); the groups stack inside a .lb-bundle-variant-option-groups
|
|
2366
|
+
parent (flex column, 12px gap between groups). The parent owns the top
|
|
2367
|
+
offset from the preceding unit-price line, so individual labels and
|
|
2368
|
+
selects don't carry their own vertical margins. */
|
|
2369
|
+
.lb-bundle-variant-option-groups {
|
|
2370
|
+
display: flex;
|
|
2371
|
+
flex-direction: column;
|
|
2372
|
+
gap: 12px;
|
|
2373
|
+
margin-top: 8px;
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
.lb-bundle-variant-option-group {
|
|
2377
|
+
display: flex;
|
|
2378
|
+
flex-direction: column;
|
|
2379
|
+
gap: 2px;
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
.lb-bundle-variant-option-label {
|
|
2383
|
+
display: block;
|
|
2384
|
+
margin: 0;
|
|
2385
|
+
font-size: 12px;
|
|
2386
|
+
line-height: 16px;
|
|
2387
|
+
font-weight: 600;
|
|
2388
|
+
letter-spacing: 0.05em;
|
|
2389
|
+
text-transform: uppercase;
|
|
2390
|
+
color: color-mix(in srgb, var(--lb-text) 60%, transparent);
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
.lb-mix-match__modal-product-info .lb-bundle-variant-option-groups {
|
|
2394
|
+
margin-top: 4px;
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
/* Focus-ring suppression for mouse users. A small JS helper \u2014 see
|
|
2398
|
+
packages/widget/src/utils/input-mode.ts and bundle-widget.js \u2014 toggles
|
|
2399
|
+
.using-mouse / .using-keyboard on the widget root (or html in the Liquid
|
|
2400
|
+
path) based on the customer's current input device. Default is mouse, so
|
|
2401
|
+
click-to-focus doesn't leave a keyboard-style ring. The modal overlay
|
|
2402
|
+
gets its own selector because the Liquid path reparents it to body,
|
|
2403
|
+
outside the widget root. */
|
|
2404
|
+
.using-mouse .lb-bundle-widget :focus,
|
|
2405
|
+
.using-mouse .lb-bundle-widget :focus-visible,
|
|
2406
|
+
.using-mouse .lb-mix-match__modal-overlay :focus,
|
|
2407
|
+
.using-mouse .lb-mix-match__modal-overlay :focus-visible {
|
|
2408
|
+
outline: none;
|
|
2409
|
+
outline-offset: 0;
|
|
2410
|
+
box-shadow: none;
|
|
2411
|
+
}
|
|
2412
|
+
|
|
1778
2413
|
.lb-bundle-quantity {
|
|
1779
2414
|
font-size: 12px;
|
|
1780
2415
|
line-height: 16px;
|
|
@@ -2008,6 +2643,22 @@ var BUNDLE_FIXED_CSS = `/* Lime Bundles \u2014 Fixed bundle styles */
|
|
|
2008
2643
|
flex-direction: column;
|
|
2009
2644
|
gap: 0;
|
|
2010
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;
|
|
2011
2662
|
}
|
|
2012
2663
|
|
|
2013
2664
|
/* Fixed bundles: product rows */
|
|
@@ -2032,9 +2683,10 @@ var BUNDLE_FIXED_CSS = `/* Lime Bundles \u2014 Fixed bundle styles */
|
|
|
2032
2683
|
border: none;
|
|
2033
2684
|
}
|
|
2034
2685
|
|
|
2035
|
-
/* Variant picker select \u2014 styled to match the variant badge aesthetic
|
|
2686
|
+
/* Variant picker select \u2014 styled to match the variant badge aesthetic.
|
|
2687
|
+
Sits inside .lb-bundle-variant-option-group so vertical spacing is owned
|
|
2688
|
+
by the group/groups flex gap, not the select itself. */
|
|
2036
2689
|
.lb-bundle-variant-select {
|
|
2037
|
-
margin-top: 8px;
|
|
2038
2690
|
display: inline-block;
|
|
2039
2691
|
border: var(--lb-variant-border-width) solid var(--lb-variant-border-color);
|
|
2040
2692
|
border-radius: var(--lb-variant-radius);
|
|
@@ -2051,7 +2703,8 @@ var BUNDLE_FIXED_CSS = `/* Lime Bundles \u2014 Fixed bundle styles */
|
|
|
2051
2703
|
background-repeat: no-repeat;
|
|
2052
2704
|
background-position: right 8px center;
|
|
2053
2705
|
background-size: 12px;
|
|
2054
|
-
|
|
2706
|
+
width: 50%;
|
|
2707
|
+
max-width: 50%;
|
|
2055
2708
|
}
|
|
2056
2709
|
|
|
2057
2710
|
.lb-bundle-variant-select:focus-visible {
|
|
@@ -2061,6 +2714,28 @@ var BUNDLE_FIXED_CSS = `/* Lime Bundles \u2014 Fixed bundle styles */
|
|
|
2061
2714
|
`;
|
|
2062
2715
|
var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
|
|
2063
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
|
+
|
|
2064
2739
|
/* === Progress Bar === */
|
|
2065
2740
|
.lb-mix-match__progress {
|
|
2066
2741
|
margin-bottom: 16px;
|
|
@@ -2458,7 +3133,6 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
|
|
|
2458
3133
|
|
|
2459
3134
|
.lb-mix-match__variant-select {
|
|
2460
3135
|
font-size: 12px;
|
|
2461
|
-
margin: 4px 0 0;
|
|
2462
3136
|
padding: 4px 24px 4px 8px;
|
|
2463
3137
|
border: var(--lb-picker-variant-border-width) solid var(--lb-picker-variant-border-color);
|
|
2464
3138
|
border-radius: var(--lb-picker-variant-radius);
|
|
@@ -2471,7 +3145,8 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
|
|
|
2471
3145
|
font-family: inherit;
|
|
2472
3146
|
min-height: 32px;
|
|
2473
3147
|
cursor: pointer;
|
|
2474
|
-
|
|
3148
|
+
width: 50%;
|
|
3149
|
+
max-width: 50%;
|
|
2475
3150
|
appearance: none;
|
|
2476
3151
|
-webkit-appearance: none;
|
|
2477
3152
|
}
|
|
@@ -2555,6 +3230,11 @@ var BUNDLE_MIX_MATCH_CSS = `/* Lime Bundles \u2014 Mix & Match styles */
|
|
|
2555
3230
|
.lb-mix-match__modal-overlay--open .lb-mix-match__modal {
|
|
2556
3231
|
transform: translateY(0);
|
|
2557
3232
|
}
|
|
3233
|
+
|
|
3234
|
+
.lb-mix-match__variant-select {
|
|
3235
|
+
width: 80%;
|
|
3236
|
+
max-width: 80%;
|
|
3237
|
+
}
|
|
2558
3238
|
}
|
|
2559
3239
|
|
|
2560
3240
|
/* === Reduced Motion === */
|
|
@@ -2572,6 +3252,22 @@ var BUNDLE_VOLUME_CSS = `/* Lime Bundles \u2014 Volume / Quantity Breaks styles
|
|
|
2572
3252
|
display: flex;
|
|
2573
3253
|
flex-direction: column;
|
|
2574
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;
|
|
2575
3271
|
}
|
|
2576
3272
|
|
|
2577
3273
|
.lb-volume__tier {
|
|
@@ -2676,6 +3372,259 @@ var BUNDLE_VOLUME_CSS = `/* Lime Bundles \u2014 Volume / Quantity Breaks styles
|
|
|
2676
3372
|
}
|
|
2677
3373
|
|
|
2678
3374
|
|
|
3375
|
+
`;
|
|
3376
|
+
var BUNDLE_DROPDOWN_CSS = `/**
|
|
3377
|
+
* Lime Bundles \u2014 Custom variant-picker dropdown styling.
|
|
3378
|
+
*
|
|
3379
|
+
* Reuses existing CSS variables: no new merchant-configurable surface.
|
|
3380
|
+
* --lb-variant-border-{width,color}, --lb-variant-radius, --lb-variant-chevron
|
|
3381
|
+
* --lb-bg, --lb-text, --lb-primary-color
|
|
3382
|
+
*
|
|
3383
|
+
* Mix-match modal context overrides via .lb-mix-match__modal scope to use
|
|
3384
|
+
* --lb-picker-variant-* and --lb-picker-bg.
|
|
3385
|
+
*/
|
|
3386
|
+
|
|
3387
|
+
/* Hide the native <select> while keeping it form-serializable and focusable
|
|
3388
|
+
programmatically. The .lb-dropdown-state marker is added by JS at bind
|
|
3389
|
+
time, so this rule matches every variant-select class (main widget,
|
|
3390
|
+
mix-match modal, future bundle types). aria-hidden + tabindex=-1
|
|
3391
|
+
(also set in JS) remove it from the accessibility tree. */
|
|
3392
|
+
.lb-dropdown-state {
|
|
3393
|
+
position: absolute !important;
|
|
3394
|
+
width: 1px !important;
|
|
3395
|
+
height: 1px !important;
|
|
3396
|
+
padding: 0 !important;
|
|
3397
|
+
margin: -1px !important;
|
|
3398
|
+
overflow: hidden !important;
|
|
3399
|
+
clip: rect(0 0 0 0) !important;
|
|
3400
|
+
white-space: nowrap !important;
|
|
3401
|
+
border: 0 !important;
|
|
3402
|
+
pointer-events: none !important;
|
|
3403
|
+
}
|
|
3404
|
+
|
|
3405
|
+
/* Shell fills its parent column. */
|
|
3406
|
+
.lb-dropdown {
|
|
3407
|
+
position: relative;
|
|
3408
|
+
display: inline-block;
|
|
3409
|
+
width: 100%;
|
|
3410
|
+
max-width: 100%;
|
|
3411
|
+
font-family: inherit;
|
|
3412
|
+
}
|
|
3413
|
+
|
|
3414
|
+
/* Trigger styled identically to the closed-state native select */
|
|
3415
|
+
.lb-dropdown-trigger {
|
|
3416
|
+
display: inline-flex;
|
|
3417
|
+
align-items: center;
|
|
3418
|
+
justify-content: space-between;
|
|
3419
|
+
gap: 8px;
|
|
3420
|
+
width: 100%;
|
|
3421
|
+
border: var(--lb-variant-border-width) solid var(--lb-variant-border-color);
|
|
3422
|
+
border-radius: var(--lb-variant-radius);
|
|
3423
|
+
padding: 8px 12px;
|
|
3424
|
+
font-size: 12px;
|
|
3425
|
+
line-height: 16px;
|
|
3426
|
+
color: var(--lb-text);
|
|
3427
|
+
background: var(--lb-bg);
|
|
3428
|
+
font-family: inherit;
|
|
3429
|
+
cursor: pointer;
|
|
3430
|
+
appearance: none;
|
|
3431
|
+
-webkit-appearance: none;
|
|
3432
|
+
text-align: start;
|
|
3433
|
+
transition: border-color 120ms ease;
|
|
3434
|
+
}
|
|
3435
|
+
|
|
3436
|
+
.lb-dropdown-trigger:focus-visible {
|
|
3437
|
+
outline: 2px solid var(--lb-primary-color);
|
|
3438
|
+
outline-offset: 2px;
|
|
3439
|
+
}
|
|
3440
|
+
|
|
3441
|
+
.lb-dropdown-trigger[aria-expanded="true"] {
|
|
3442
|
+
border-color: var(--lb-text);
|
|
3443
|
+
}
|
|
3444
|
+
|
|
3445
|
+
.lb-dropdown-trigger-value {
|
|
3446
|
+
flex: 1 1 auto;
|
|
3447
|
+
white-space: nowrap;
|
|
3448
|
+
overflow: hidden;
|
|
3449
|
+
text-overflow: ellipsis;
|
|
3450
|
+
text-align: start;
|
|
3451
|
+
}
|
|
3452
|
+
|
|
3453
|
+
.lb-dropdown-chevron {
|
|
3454
|
+
flex: 0 0 auto;
|
|
3455
|
+
width: 12px;
|
|
3456
|
+
height: 12px;
|
|
3457
|
+
background: var(--lb-variant-chevron) center / contain no-repeat;
|
|
3458
|
+
transition: transform 120ms ease;
|
|
3459
|
+
}
|
|
3460
|
+
|
|
3461
|
+
.lb-dropdown-trigger[aria-expanded="true"] .lb-dropdown-chevron {
|
|
3462
|
+
transform: rotate(180deg);
|
|
3463
|
+
}
|
|
3464
|
+
|
|
3465
|
+
/* Popover panel \u2014 position: absolute against the .lb-dropdown shell
|
|
3466
|
+
(already position: relative). Top/left/width come from CSS so we
|
|
3467
|
+
never depend on JS having set inline coords by the time the panel
|
|
3468
|
+
becomes visible. JS only sets max-height. */
|
|
3469
|
+
.lb-dropdown-listbox {
|
|
3470
|
+
position: absolute;
|
|
3471
|
+
left: 0;
|
|
3472
|
+
/* Default to below-trigger placement so the panel doesn't overlap the
|
|
3473
|
+
trigger if data-placement is missing for any reason. The explicit
|
|
3474
|
+
[data-placement="down"|"up"] rules below override this. */
|
|
3475
|
+
top: calc(100% + 4px);
|
|
3476
|
+
width: 100%;
|
|
3477
|
+
z-index: 9999;
|
|
3478
|
+
margin: 0;
|
|
3479
|
+
padding: 4px 0;
|
|
3480
|
+
list-style: none;
|
|
3481
|
+
background: var(--lb-bg);
|
|
3482
|
+
color: var(--lb-text);
|
|
3483
|
+
border: var(--lb-variant-border-width) solid var(--lb-variant-border-color);
|
|
3484
|
+
border-radius: var(--lb-variant-radius);
|
|
3485
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
|
|
3486
|
+
overflow-y: auto;
|
|
3487
|
+
overflow-x: hidden;
|
|
3488
|
+
/* Custom scrollbar \u2014 text colour at 15% opacity (thumb) and 5% (track). */
|
|
3489
|
+
scrollbar-width: thin;
|
|
3490
|
+
scrollbar-color: color-mix(in srgb, var(--lb-text) 15%, transparent)
|
|
3491
|
+
color-mix(in srgb, var(--lb-text) 2%, transparent);
|
|
3492
|
+
animation: lb-dropdown-in-down 120ms ease-out;
|
|
3493
|
+
transform-origin: top center;
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3496
|
+
.lb-dropdown-listbox[data-placement="down"] {
|
|
3497
|
+
top: calc(100% + 4px);
|
|
3498
|
+
}
|
|
3499
|
+
|
|
3500
|
+
.lb-dropdown-listbox[data-placement="up"] {
|
|
3501
|
+
top: auto;
|
|
3502
|
+
bottom: calc(100% + 4px);
|
|
3503
|
+
animation-name: lb-dropdown-in-up;
|
|
3504
|
+
transform-origin: bottom center;
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
/* When portaled out of the .lb-dropdown shell (mix-match modal context:
|
|
3508
|
+
.lb-mix-match__modal applies translateY which would otherwise trap
|
|
3509
|
+
position:fixed), switch to fixed and let JS set viewport coords. */
|
|
3510
|
+
.lb-dropdown-listbox[data-lb-dropdown-portal] {
|
|
3511
|
+
position: fixed;
|
|
3512
|
+
top: auto;
|
|
3513
|
+
left: auto;
|
|
3514
|
+
bottom: auto;
|
|
3515
|
+
width: auto;
|
|
3516
|
+
}
|
|
3517
|
+
|
|
3518
|
+
/* Custom scrollbar \u2014 Webkit/Blink: exact 4px width */
|
|
3519
|
+
.lb-dropdown-listbox::-webkit-scrollbar {
|
|
3520
|
+
width: 4px;
|
|
3521
|
+
}
|
|
3522
|
+
.lb-dropdown-listbox::-webkit-scrollbar-track {
|
|
3523
|
+
background: color-mix(in srgb, var(--lb-text) 2%, transparent);
|
|
3524
|
+
border-radius: 2px;
|
|
3525
|
+
}
|
|
3526
|
+
.lb-dropdown-listbox::-webkit-scrollbar-thumb {
|
|
3527
|
+
background: color-mix(in srgb, var(--lb-text) 15%, transparent);
|
|
3528
|
+
border-radius: 2px;
|
|
3529
|
+
}
|
|
3530
|
+
|
|
3531
|
+
/* Options */
|
|
3532
|
+
.lb-dropdown-option {
|
|
3533
|
+
padding: 8px 12px;
|
|
3534
|
+
font-size: 12px;
|
|
3535
|
+
line-height: 16px;
|
|
3536
|
+
cursor: pointer;
|
|
3537
|
+
white-space: nowrap;
|
|
3538
|
+
overflow: hidden;
|
|
3539
|
+
text-overflow: ellipsis;
|
|
3540
|
+
color: var(--lb-text);
|
|
3541
|
+
}
|
|
3542
|
+
|
|
3543
|
+
.lb-dropdown-option[aria-selected="true"] {
|
|
3544
|
+
font-weight: 600;
|
|
3545
|
+
}
|
|
3546
|
+
|
|
3547
|
+
.lb-dropdown-option.is-active,
|
|
3548
|
+
.lb-dropdown-option:hover:not([aria-disabled="true"]) {
|
|
3549
|
+
background: color-mix(in srgb, var(--lb-text) 2%, transparent);
|
|
3550
|
+
}
|
|
3551
|
+
|
|
3552
|
+
.lb-dropdown-option[aria-disabled="true"] {
|
|
3553
|
+
opacity: 0.4;
|
|
3554
|
+
cursor: not-allowed;
|
|
3555
|
+
}
|
|
3556
|
+
|
|
3557
|
+
/* Animations */
|
|
3558
|
+
@keyframes lb-dropdown-in-down {
|
|
3559
|
+
from { opacity: 0; transform: translateY(-4px) scale(0.98); }
|
|
3560
|
+
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
3561
|
+
}
|
|
3562
|
+
|
|
3563
|
+
@keyframes lb-dropdown-in-up {
|
|
3564
|
+
from { opacity: 0; transform: translateY(4px) scale(0.98); }
|
|
3565
|
+
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
3566
|
+
}
|
|
3567
|
+
|
|
3568
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3569
|
+
.lb-dropdown-listbox { animation: none; }
|
|
3570
|
+
.lb-dropdown-chevron { transition: none; }
|
|
3571
|
+
.lb-dropdown-trigger { transition: none; }
|
|
3572
|
+
}
|
|
3573
|
+
|
|
3574
|
+
/* Forced-colors mode (Windows high-contrast) */
|
|
3575
|
+
@media (forced-colors: active) {
|
|
3576
|
+
.lb-dropdown-trigger {
|
|
3577
|
+
border-color: ButtonBorder;
|
|
3578
|
+
color: ButtonText;
|
|
3579
|
+
background: ButtonFace;
|
|
3580
|
+
}
|
|
3581
|
+
.lb-dropdown-listbox {
|
|
3582
|
+
border-color: ButtonBorder;
|
|
3583
|
+
background: Canvas;
|
|
3584
|
+
color: CanvasText;
|
|
3585
|
+
}
|
|
3586
|
+
.lb-dropdown-option.is-active {
|
|
3587
|
+
background: Highlight;
|
|
3588
|
+
color: HighlightText;
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
|
|
3592
|
+
/* Mix-match modal context \u2014 use picker-scoped variables.
|
|
3593
|
+
No CSS fallbacks: --lb-picker-* are always emitted by bundle-widget.liquid
|
|
3594
|
+
because WidgetConfig.parse() fully hydrates the merchant config.
|
|
3595
|
+
See docs/solutions/ui-bugs/widget-css-single-source-defaults.md. */
|
|
3596
|
+
.lb-mix-match__modal .lb-dropdown-trigger {
|
|
3597
|
+
border-color: var(--lb-picker-variant-border-color);
|
|
3598
|
+
border-width: var(--lb-picker-variant-border-width);
|
|
3599
|
+
border-radius: var(--lb-picker-variant-radius);
|
|
3600
|
+
background: var(--lb-picker-bg);
|
|
3601
|
+
color: var(--lb-picker-text);
|
|
3602
|
+
}
|
|
3603
|
+
|
|
3604
|
+
.lb-mix-match__modal .lb-dropdown-chevron {
|
|
3605
|
+
background-image: var(--lb-picker-variant-chevron);
|
|
3606
|
+
}
|
|
3607
|
+
|
|
3608
|
+
/* Listbox is portaled out of the transformed .lb-mix-match__modal up to
|
|
3609
|
+
its [data-modal-overlay] parent, so picker-scoped rules anchor on the
|
|
3610
|
+
overlay attribute, not the modal class. */
|
|
3611
|
+
[data-modal-overlay] > .lb-dropdown-listbox {
|
|
3612
|
+
border-color: var(--lb-picker-variant-border-color);
|
|
3613
|
+
border-width: var(--lb-picker-variant-border-width);
|
|
3614
|
+
border-radius: var(--lb-picker-variant-radius);
|
|
3615
|
+
background: var(--lb-picker-bg);
|
|
3616
|
+
color: var(--lb-picker-text);
|
|
3617
|
+
scrollbar-color: color-mix(in srgb, var(--lb-picker-text) 15%, transparent)
|
|
3618
|
+
color-mix(in srgb, var(--lb-picker-text) 2%, transparent);
|
|
3619
|
+
}
|
|
3620
|
+
|
|
3621
|
+
[data-modal-overlay] > .lb-dropdown-listbox::-webkit-scrollbar-track {
|
|
3622
|
+
background: color-mix(in srgb, var(--lb-picker-text) 2%, transparent);
|
|
3623
|
+
border-radius: 2px;
|
|
3624
|
+
}
|
|
3625
|
+
[data-modal-overlay] > .lb-dropdown-listbox::-webkit-scrollbar-thumb {
|
|
3626
|
+
background: color-mix(in srgb, var(--lb-picker-text) 15%, transparent);
|
|
3627
|
+
}
|
|
2679
3628
|
`;
|
|
2680
3629
|
var BUNDLE_SKELETON_CSS = `/* Lime Bundles \u2014 web-component loading skeleton (not mirrored to theme assets) */
|
|
2681
3630
|
|
|
@@ -2874,7 +3823,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
2874
3823
|
const controller = new AbortController();
|
|
2875
3824
|
this.abortController = controller;
|
|
2876
3825
|
this.renderLoading();
|
|
2877
|
-
const client = (0,
|
|
3826
|
+
const client = (0, import_core7.createStorefrontClient)({
|
|
2878
3827
|
shopDomain: this.shopDomain,
|
|
2879
3828
|
accessToken: this.storefrontToken
|
|
2880
3829
|
});
|
|
@@ -2899,7 +3848,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
2899
3848
|
handle
|
|
2900
3849
|
);
|
|
2901
3850
|
}
|
|
2902
|
-
const cssPromise = client.query(
|
|
3851
|
+
const cssPromise = client.query(import_core7.SHOP_CUSTOM_CSS_QUERY, void 0, {
|
|
2903
3852
|
signal: controller.signal
|
|
2904
3853
|
}).catch(() => null);
|
|
2905
3854
|
await bundlePromise;
|
|
@@ -2911,8 +3860,8 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
2911
3860
|
}
|
|
2912
3861
|
const css = await cssPromise;
|
|
2913
3862
|
if (css?.shop?.metafield?.value) {
|
|
2914
|
-
(0,
|
|
2915
|
-
const sanitized = (0,
|
|
3863
|
+
(0, import_core7.injectCustomCss)(this.shopDomain, css.shop.metafield.value);
|
|
3864
|
+
const sanitized = (0, import_core7.sanitizeCustomCss)(css.shop.metafield.value);
|
|
2916
3865
|
if (sanitized.ok) this.shopCustomCss = sanitized.css;
|
|
2917
3866
|
}
|
|
2918
3867
|
await this.applyABVariants();
|
|
@@ -2940,14 +3889,14 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
2940
3889
|
this.bundles.map(async (bundle) => {
|
|
2941
3890
|
if (!bundle.abTestId || !bundle.abVariantB) return bundle;
|
|
2942
3891
|
try {
|
|
2943
|
-
const assignment = await (0,
|
|
3892
|
+
const assignment = await (0, import_core7.getABTestAssignment)(
|
|
2944
3893
|
this.appUrl,
|
|
2945
3894
|
this.shopDomain,
|
|
2946
3895
|
bundle.abTestId,
|
|
2947
3896
|
bundle.id
|
|
2948
3897
|
);
|
|
2949
3898
|
if (assignment?.variant === "B") {
|
|
2950
|
-
return (0,
|
|
3899
|
+
return (0, import_core7.applyABVariantB)(bundle);
|
|
2951
3900
|
}
|
|
2952
3901
|
} catch (err) {
|
|
2953
3902
|
console.warn(
|
|
@@ -2962,7 +3911,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
2962
3911
|
}
|
|
2963
3912
|
async fetchSingleBundle(client, signal) {
|
|
2964
3913
|
const data = await client.query(
|
|
2965
|
-
|
|
3914
|
+
import_core7.BUNDLE_METAOBJECT_QUERY,
|
|
2966
3915
|
{ id: this.bundleGid },
|
|
2967
3916
|
{ signal }
|
|
2968
3917
|
);
|
|
@@ -2970,7 +3919,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
2970
3919
|
this.bundles = [];
|
|
2971
3920
|
return;
|
|
2972
3921
|
}
|
|
2973
|
-
const parsed = (0,
|
|
3922
|
+
const parsed = (0, import_core7.parseMetaobjectBundle)(
|
|
2974
3923
|
data.metaobject.id,
|
|
2975
3924
|
data.metaobject.fields
|
|
2976
3925
|
);
|
|
@@ -2978,7 +3927,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
2978
3927
|
}
|
|
2979
3928
|
async fetchProductBundles(client, signal, productHandle) {
|
|
2980
3929
|
const data = await client.query(
|
|
2981
|
-
|
|
3930
|
+
import_core7.BUNDLES_FOR_PRODUCT_QUERY,
|
|
2982
3931
|
{ handle: productHandle },
|
|
2983
3932
|
{ signal }
|
|
2984
3933
|
);
|
|
@@ -2989,7 +3938,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
2989
3938
|
const refs = data.product.metafield?.references?.nodes ?? [];
|
|
2990
3939
|
const bundles = [];
|
|
2991
3940
|
for (const ref of refs) {
|
|
2992
|
-
const parsed = (0,
|
|
3941
|
+
const parsed = (0, import_core7.parseMetaobjectBundle)(ref.id, ref.fields);
|
|
2993
3942
|
if (parsed) bundles.push(parsed);
|
|
2994
3943
|
}
|
|
2995
3944
|
this.bundles = bundles;
|
|
@@ -3022,7 +3971,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3022
3971
|
*/
|
|
3023
3972
|
async defaultAddToCart(lines) {
|
|
3024
3973
|
if (typeof window === "undefined") return;
|
|
3025
|
-
const client = (0,
|
|
3974
|
+
const client = (0, import_core7.createStorefrontClient)({
|
|
3026
3975
|
shopDomain: this.shopDomain,
|
|
3027
3976
|
accessToken: this.storefrontToken
|
|
3028
3977
|
});
|
|
@@ -3033,7 +3982,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3033
3982
|
let checkoutUrl = null;
|
|
3034
3983
|
if (existingCartId) {
|
|
3035
3984
|
const res = await client.query(
|
|
3036
|
-
|
|
3985
|
+
import_core7.CART_LINES_ADD_MUTATION,
|
|
3037
3986
|
{ cartId: existingCartId, lines }
|
|
3038
3987
|
);
|
|
3039
3988
|
const payload = res.cartLinesAdd;
|
|
@@ -3045,7 +3994,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3045
3994
|
}
|
|
3046
3995
|
if (!checkoutUrl) {
|
|
3047
3996
|
const res = await client.query(
|
|
3048
|
-
|
|
3997
|
+
import_core7.CART_CREATE_MUTATION,
|
|
3049
3998
|
{ input: { lines } }
|
|
3050
3999
|
);
|
|
3051
4000
|
const payload = res.cartCreate;
|
|
@@ -3091,7 +4040,7 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3091
4040
|
const price = variant ? parseFloat(variant.price.amount) : 0;
|
|
3092
4041
|
return sum + price * line.quantity;
|
|
3093
4042
|
}, 0);
|
|
3094
|
-
(0,
|
|
4043
|
+
(0, import_core7.reportAddToCart)(
|
|
3095
4044
|
{ shopDomain: this.shopDomain, appUrl: this.appUrl },
|
|
3096
4045
|
{
|
|
3097
4046
|
bundleGid: bundle.id,
|
|
@@ -3111,7 +4060,8 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3111
4060
|
BUNDLE_BASE_CSS,
|
|
3112
4061
|
BUNDLE_FIXED_CSS,
|
|
3113
4062
|
BUNDLE_MIX_MATCH_CSS,
|
|
3114
|
-
BUNDLE_VOLUME_CSS
|
|
4063
|
+
BUNDLE_VOLUME_CSS,
|
|
4064
|
+
BUNDLE_DROPDOWN_CSS
|
|
3115
4065
|
].join("\n");
|
|
3116
4066
|
this.shadow.appendChild(style);
|
|
3117
4067
|
if (this.shopCustomCss) {
|
|
@@ -3127,7 +4077,8 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3127
4077
|
container.setAttribute("aria-label", bundle.title);
|
|
3128
4078
|
container.setAttribute("data-bundle-type", bundle.bundleType);
|
|
3129
4079
|
container.setAttribute("data-bundle-gid", bundle.id);
|
|
3130
|
-
(0,
|
|
4080
|
+
(0, import_core8.applyWidgetConfigVars)(container, bundle.widgetConfig);
|
|
4081
|
+
this.renderCleanups.push(trackInputMode(container));
|
|
3131
4082
|
const dispatch = (lines) => this.handleAddToCart(bundle, lines);
|
|
3132
4083
|
const registerCleanup = (fn) => this.renderCleanups.push(fn);
|
|
3133
4084
|
switch (bundle.bundleType) {
|
|
@@ -3162,8 +4113,8 @@ var LimeBundleElement = class extends HTMLElement {
|
|
|
3162
4113
|
}
|
|
3163
4114
|
setupImpressionFor(bundle, element) {
|
|
3164
4115
|
if (!this.analyticsEnabled || !this.appUrl) return;
|
|
3165
|
-
const cleanup = (0,
|
|
3166
|
-
(0,
|
|
4116
|
+
const cleanup = (0, import_core7.observeImpression)(element, () => {
|
|
4117
|
+
(0, import_core7.reportImpression)(
|
|
3167
4118
|
{ shopDomain: this.shopDomain, appUrl: this.appUrl },
|
|
3168
4119
|
{
|
|
3169
4120
|
bundleGid: bundle.id,
|
|
@@ -3230,6 +4181,7 @@ if (typeof customElements !== "undefined" && !customElements.get("lime-bundle"))
|
|
|
3230
4181
|
}
|
|
3231
4182
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3232
4183
|
0 && (module.exports = {
|
|
3233
|
-
LimeBundleElement
|
|
4184
|
+
LimeBundleElement,
|
|
4185
|
+
trackInputMode
|
|
3234
4186
|
});
|
|
3235
4187
|
//# sourceMappingURL=index.cjs.map
|