@vio-live/web-sdk 0.19.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +144 -0
- package/dist/chunk-25QECQDS.js +3 -0
- package/dist/{chunk-PZUOMOLF.js.map → chunk-25QECQDS.js.map} +1 -1
- package/dist/{chunk-CMNTHF66.js → chunk-AMDI7KH3.js} +918 -83
- package/dist/chunk-AMDI7KH3.js.map +1 -0
- package/dist/{chunk-NUNGU6LL.js → chunk-COCHGJ2K.js} +699 -69
- package/dist/chunk-COCHGJ2K.js.map +1 -0
- package/dist/core/index.d.ts +467 -11
- package/dist/core/index.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +3 -3
- package/dist/react/index.js.map +1 -1
- package/dist/{tokens-IrVSMVcA.d.ts → tokens-DSYvrBsB.d.ts} +117 -0
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-CMNTHF66.js.map +0 -1
- package/dist/chunk-NUNGU6LL.js.map +0 -1
- package/dist/chunk-PZUOMOLF.js +0 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Vio, __decorateClass, primaryImageUrl, displayPrice, getGlobalCurrency, ensureVippsButton, formatPrice, nexiReturnPaymentId, readNexiPending, kustomReturnOutcome, KUSTOM_RETURN_QUERY_KEYS, ADYEN_RETURN_METHOD, adyenReturnParams, ADYEN_RETURN_QUERY_KEYS, adyenReturnOutcome, AdyenOriginError } from './chunk-
|
|
1
|
+
import { Vio, __decorateClass, primaryImageUrl, displayPrice, getGlobalCurrency, ensureVippsButton, formatPrice, nexiReturnPaymentId, readNexiPending, kustomReturnOutcome, KUSTOM_RETURN_QUERY_KEYS, ADYEN_RETURN_METHOD, adyenReturnParams, ADYEN_RETURN_QUERY_KEYS, adyenReturnOutcome, AdyenOriginError, autoSlotDisplayName, autoSlotId, normalizeScope } from './chunk-AMDI7KH3.js';
|
|
2
2
|
import { LitElement, css, html } from 'lit';
|
|
3
3
|
import { property, state, query } from 'lit/decorators.js';
|
|
4
4
|
|
|
@@ -165,6 +165,124 @@ function applyVioTheme(overrides, target = typeof document !== "undefined" ? doc
|
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
|
+
|
|
169
|
+
// src/ui/slot-registration.ts
|
|
170
|
+
var SCOPE_ATTR = "vio-scope";
|
|
171
|
+
var SCOPE_NAME_ATTR = "vio-scope-name";
|
|
172
|
+
var SCOPE_DATA_ATTR = "data-vio-scope";
|
|
173
|
+
var SCOPE_NAME_DATA_ATTR = "data-vio-scope-name";
|
|
174
|
+
var SlotRegistration = class {
|
|
175
|
+
constructor(host, kind) {
|
|
176
|
+
this.host = host;
|
|
177
|
+
this.kind = kind;
|
|
178
|
+
this.identity = null;
|
|
179
|
+
this.release = null;
|
|
180
|
+
/** The element's position-based identity, fixed at connect time. */
|
|
181
|
+
this.position = null;
|
|
182
|
+
}
|
|
183
|
+
/** The effective slot id ('' before connect / after disconnect). */
|
|
184
|
+
get effective() {
|
|
185
|
+
return this.identity?.id ?? "";
|
|
186
|
+
}
|
|
187
|
+
/** The identity in effect, or null. */
|
|
188
|
+
get current() {
|
|
189
|
+
return this.identity;
|
|
190
|
+
}
|
|
191
|
+
/** On connect: fix scope + ordinal from the element's position, then register. */
|
|
192
|
+
connect(attrs) {
|
|
193
|
+
this.position = positionOf(this.host, attrs);
|
|
194
|
+
this.sync(attrs);
|
|
195
|
+
}
|
|
196
|
+
/** (Re)register when the attributes changed the identity. */
|
|
197
|
+
sync(attrs) {
|
|
198
|
+
if (this.position && scopeOf(this.host, attrs) !== this.position.scope) {
|
|
199
|
+
this.position = positionOf(this.host, attrs);
|
|
200
|
+
}
|
|
201
|
+
const next = this.resolve(attrs);
|
|
202
|
+
if (sameIdentity(next, this.identity)) return;
|
|
203
|
+
this.clear();
|
|
204
|
+
if (!next) return;
|
|
205
|
+
try {
|
|
206
|
+
this.release = Vio.registerSlot(next.id, next.displayName);
|
|
207
|
+
this.identity = next;
|
|
208
|
+
} catch {
|
|
209
|
+
this.release = null;
|
|
210
|
+
this.identity = null;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/** What the identity would be for these attributes (null before connect without a host id). */
|
|
214
|
+
resolve(attrs) {
|
|
215
|
+
const explicit = typeof attrs.vioSlot === "string" ? attrs.vioSlot.trim() : "";
|
|
216
|
+
const name = typeof attrs.vioSlotName === "string" && attrs.vioSlotName.trim() ? attrs.vioSlotName.trim() : void 0;
|
|
217
|
+
if (explicit) return { id: explicit, displayName: name, auto: false, scope: this.position?.scope ?? "" };
|
|
218
|
+
if (!this.position) return null;
|
|
219
|
+
const { scope, scopeName, ordinal } = this.position;
|
|
220
|
+
return {
|
|
221
|
+
id: autoSlotId(this.kind, ordinal, scope),
|
|
222
|
+
displayName: name ?? autoSlotDisplayName(this.kind, ordinal, scopeName),
|
|
223
|
+
auto: true,
|
|
224
|
+
scope
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
/** Unregister whatever is registered. */
|
|
228
|
+
clear() {
|
|
229
|
+
try {
|
|
230
|
+
this.release?.();
|
|
231
|
+
} catch {
|
|
232
|
+
}
|
|
233
|
+
this.release = null;
|
|
234
|
+
this.identity = null;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
function sameIdentity(a, b) {
|
|
238
|
+
if (!a || !b) return a === b;
|
|
239
|
+
return a.id === b.id && a.displayName === b.displayName;
|
|
240
|
+
}
|
|
241
|
+
function scopeOf(el, attrs) {
|
|
242
|
+
const own = attrs && typeof attrs.vioScope === "string" ? attrs.vioScope : typeof el.vioScope === "string" ? el.vioScope ?? "" : el.getAttribute(SCOPE_ATTR) ?? "";
|
|
243
|
+
const value = normalizeScope(own) || normalizeScope(closestAcrossShadow(el, `[${SCOPE_DATA_ATTR}]`)?.getAttribute(SCOPE_DATA_ATTR));
|
|
244
|
+
return value;
|
|
245
|
+
}
|
|
246
|
+
function scopeNameOf(el, attrs) {
|
|
247
|
+
const own = attrs && typeof attrs.vioScopeName === "string" ? attrs.vioScopeName : typeof el.vioScopeName === "string" ? el.vioScopeName ?? "" : el.getAttribute(SCOPE_NAME_ATTR) ?? "";
|
|
248
|
+
const value = own.trim() || (closestAcrossShadow(el, `[${SCOPE_NAME_DATA_ATTR}]`)?.getAttribute(SCOPE_NAME_DATA_ATTR) ?? "").trim();
|
|
249
|
+
return value;
|
|
250
|
+
}
|
|
251
|
+
function positionOf(host, attrs) {
|
|
252
|
+
const scope = scopeOf(host, attrs);
|
|
253
|
+
const scopeName = scopeNameOf(host, attrs);
|
|
254
|
+
let ordinal = 0;
|
|
255
|
+
try {
|
|
256
|
+
const tag = host.tagName.toLowerCase();
|
|
257
|
+
const root = host.getRootNode();
|
|
258
|
+
const scopeRoot = root && typeof root.querySelectorAll === "function" ? root : typeof document !== "undefined" ? document : null;
|
|
259
|
+
if (scopeRoot) {
|
|
260
|
+
const peers = Array.from(scopeRoot.querySelectorAll(tag)).filter(
|
|
261
|
+
(el) => el === host || scopeOf(el) === scope
|
|
262
|
+
);
|
|
263
|
+
const index = peers.indexOf(host);
|
|
264
|
+
ordinal = index >= 0 ? index : 0;
|
|
265
|
+
}
|
|
266
|
+
} catch {
|
|
267
|
+
ordinal = 0;
|
|
268
|
+
}
|
|
269
|
+
return { scope, scopeName, ordinal };
|
|
270
|
+
}
|
|
271
|
+
function closestAcrossShadow(start, selector) {
|
|
272
|
+
try {
|
|
273
|
+
let node = start;
|
|
274
|
+
while (node) {
|
|
275
|
+
const found = typeof node.closest === "function" ? node.closest(selector) : null;
|
|
276
|
+
if (found) return found;
|
|
277
|
+
const root = node.getRootNode();
|
|
278
|
+
node = root && root !== node && typeof root.host === "object" ? root.host ?? null : null;
|
|
279
|
+
}
|
|
280
|
+
} catch {
|
|
281
|
+
}
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// src/ui/components/vio-product.ts
|
|
168
286
|
var VioProduct = class extends LitElement {
|
|
169
287
|
constructor() {
|
|
170
288
|
super(...arguments);
|
|
@@ -175,6 +293,13 @@ var VioProduct = class extends LitElement {
|
|
|
175
293
|
this.imageUrl = "";
|
|
176
294
|
this.productId = "";
|
|
177
295
|
this.sponsorId = "";
|
|
296
|
+
this.vioSlot = "";
|
|
297
|
+
this.vioSlotName = "";
|
|
298
|
+
this.vioScope = "";
|
|
299
|
+
this.vioScopeName = "";
|
|
300
|
+
this.vioContentId = "";
|
|
301
|
+
this.vioContentTitle = "";
|
|
302
|
+
this.slotRegistration = new SlotRegistration(this, "card");
|
|
178
303
|
this.hasVariants = false;
|
|
179
304
|
this.variants = void 0;
|
|
180
305
|
this.options = void 0;
|
|
@@ -184,6 +309,36 @@ var VioProduct = class extends LitElement {
|
|
|
184
309
|
this.justAdded = false;
|
|
185
310
|
this.boundCartChange = () => this.updateInCart();
|
|
186
311
|
}
|
|
312
|
+
/** The slot id in effect: `vio-slot`, else the auto id (`card-N`). Read-only. */
|
|
313
|
+
get effectiveSlot() {
|
|
314
|
+
return this.slotRegistration.effective;
|
|
315
|
+
}
|
|
316
|
+
/** The slot campaign's sponsor as the string the attributes use ('' when unknown). */
|
|
317
|
+
sponsorFromAttribution() {
|
|
318
|
+
const id = Vio.attribution(this.effectiveSlot).sponsorId;
|
|
319
|
+
return id !== void 0 ? String(id) : "";
|
|
320
|
+
}
|
|
321
|
+
slotAttributes() {
|
|
322
|
+
return {
|
|
323
|
+
vioSlot: this.vioSlot,
|
|
324
|
+
vioSlotName: this.vioSlotName,
|
|
325
|
+
vioScope: this.vioScope,
|
|
326
|
+
vioScopeName: this.vioScopeName
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* What every event carries for attribution (`Vio.attribution`): the place,
|
|
331
|
+
* its campaign and sponsor, and the content — overridden here when the host
|
|
332
|
+
* set `vio-content-id` / `vio-content-title` on this card.
|
|
333
|
+
*/
|
|
334
|
+
attribution() {
|
|
335
|
+
const base = Vio.attribution(this.effectiveSlot, { sponsorId: this.sponsorId || void 0 });
|
|
336
|
+
return {
|
|
337
|
+
...base,
|
|
338
|
+
...this.vioContentId ? { contentId: this.vioContentId } : {},
|
|
339
|
+
...this.vioContentTitle ? { contentTitle: this.vioContentTitle } : {}
|
|
340
|
+
};
|
|
341
|
+
}
|
|
187
342
|
static {
|
|
188
343
|
this.styles = css`
|
|
189
344
|
:host {
|
|
@@ -297,14 +452,19 @@ var VioProduct = class extends LitElement {
|
|
|
297
452
|
connectedCallback() {
|
|
298
453
|
super.connectedCallback();
|
|
299
454
|
this.updateInCart();
|
|
455
|
+
this.slotRegistration.connect(this.slotAttributes());
|
|
300
456
|
Vio.cart.addEventListener("change", this.boundCartChange);
|
|
301
457
|
}
|
|
302
458
|
disconnectedCallback() {
|
|
303
459
|
Vio.cart.removeEventListener("change", this.boundCartChange);
|
|
460
|
+
this.slotRegistration.clear();
|
|
304
461
|
super.disconnectedCallback();
|
|
305
462
|
}
|
|
306
463
|
updated(changed) {
|
|
307
464
|
if (changed.has("productId") || changed.has("sponsorId")) this.updateInCart();
|
|
465
|
+
if (changed.has("vioSlot") || changed.has("vioSlotName") || changed.has("vioScope") || changed.has("vioScopeName")) {
|
|
466
|
+
this.slotRegistration.sync(this.slotAttributes());
|
|
467
|
+
}
|
|
308
468
|
}
|
|
309
469
|
/** Reflect whether (and how many units of) this product sit in the cart. */
|
|
310
470
|
updateInCart() {
|
|
@@ -330,12 +490,14 @@ var VioProduct = class extends LitElement {
|
|
|
330
490
|
composed: true,
|
|
331
491
|
detail: {
|
|
332
492
|
productId: this.productId,
|
|
333
|
-
sponsorId: this.sponsorId,
|
|
334
493
|
brand: this.brand,
|
|
335
494
|
name: this.name,
|
|
336
495
|
price: this.price,
|
|
337
496
|
retailer: this.retailer,
|
|
338
|
-
imageUrl: this.imageUrl
|
|
497
|
+
imageUrl: this.imageUrl,
|
|
498
|
+
...this.attribution(),
|
|
499
|
+
// The host's sponsor-id wins; without one, the slot's campaign sponsor.
|
|
500
|
+
sponsorId: this.sponsorId || this.sponsorFromAttribution()
|
|
339
501
|
}
|
|
340
502
|
})
|
|
341
503
|
);
|
|
@@ -355,18 +517,28 @@ var VioProduct = class extends LitElement {
|
|
|
355
517
|
this.onClick();
|
|
356
518
|
return;
|
|
357
519
|
}
|
|
520
|
+
const attribution = this.attribution();
|
|
358
521
|
this.dispatchEvent(
|
|
359
522
|
new CustomEvent("vio:product-add", {
|
|
360
523
|
bubbles: true,
|
|
361
524
|
composed: true,
|
|
362
525
|
detail: {
|
|
363
526
|
productId: this.productId,
|
|
364
|
-
sponsorId: this.sponsorId,
|
|
365
527
|
brand: this.brand,
|
|
366
528
|
name: this.name,
|
|
367
529
|
price: this.price,
|
|
368
530
|
retailer: this.retailer,
|
|
369
|
-
imageUrl: this.imageUrl
|
|
531
|
+
imageUrl: this.imageUrl,
|
|
532
|
+
...attribution,
|
|
533
|
+
// The host's sponsor-id wins; without one, the slot's campaign sponsor.
|
|
534
|
+
sponsorId: this.sponsorId || this.sponsorFromAttribution(),
|
|
535
|
+
// The host adds to the cart: pass this through as `origin` so the
|
|
536
|
+
// purchase is attributed to this place and campaign.
|
|
537
|
+
origin: {
|
|
538
|
+
campaignId: attribution.campaignId,
|
|
539
|
+
slot: attribution.slot,
|
|
540
|
+
contentId: attribution.contentId
|
|
541
|
+
}
|
|
370
542
|
}
|
|
371
543
|
})
|
|
372
544
|
);
|
|
@@ -434,6 +606,24 @@ __decorateClass([
|
|
|
434
606
|
__decorateClass([
|
|
435
607
|
property({ type: String, attribute: "sponsor-id" })
|
|
436
608
|
], VioProduct.prototype, "sponsorId", 2);
|
|
609
|
+
__decorateClass([
|
|
610
|
+
property({ type: String, attribute: "vio-slot" })
|
|
611
|
+
], VioProduct.prototype, "vioSlot", 2);
|
|
612
|
+
__decorateClass([
|
|
613
|
+
property({ type: String, attribute: "vio-slot-name" })
|
|
614
|
+
], VioProduct.prototype, "vioSlotName", 2);
|
|
615
|
+
__decorateClass([
|
|
616
|
+
property({ type: String, attribute: "vio-scope" })
|
|
617
|
+
], VioProduct.prototype, "vioScope", 2);
|
|
618
|
+
__decorateClass([
|
|
619
|
+
property({ type: String, attribute: "vio-scope-name" })
|
|
620
|
+
], VioProduct.prototype, "vioScopeName", 2);
|
|
621
|
+
__decorateClass([
|
|
622
|
+
property({ type: String, attribute: "vio-content-id" })
|
|
623
|
+
], VioProduct.prototype, "vioContentId", 2);
|
|
624
|
+
__decorateClass([
|
|
625
|
+
property({ type: String, attribute: "vio-content-title" })
|
|
626
|
+
], VioProduct.prototype, "vioContentTitle", 2);
|
|
437
627
|
__decorateClass([
|
|
438
628
|
property({ type: Boolean, attribute: "has-variants" })
|
|
439
629
|
], VioProduct.prototype, "hasVariants", 2);
|
|
@@ -468,6 +658,13 @@ var VioProductCarousel = class extends LitElement {
|
|
|
468
658
|
this.sponsorId = 0;
|
|
469
659
|
this.productRefs = "";
|
|
470
660
|
this.currency = "NOK";
|
|
661
|
+
this.vioSlot = "";
|
|
662
|
+
this.vioSlotName = "";
|
|
663
|
+
this.vioScope = "";
|
|
664
|
+
this.vioScopeName = "";
|
|
665
|
+
this.vioContentId = "";
|
|
666
|
+
this.vioContentTitle = "";
|
|
667
|
+
this.slotRegistration = new SlotRegistration(this, "carousel");
|
|
471
668
|
this.fetched = null;
|
|
472
669
|
this.isLoading = false;
|
|
473
670
|
this.fetchError = "";
|
|
@@ -479,6 +676,27 @@ var VioProductCarousel = class extends LitElement {
|
|
|
479
676
|
this.impressed = false;
|
|
480
677
|
this.listTracked = false;
|
|
481
678
|
}
|
|
679
|
+
/** The slot id in effect: `vio-slot`, else the auto id (`carousel-N`). Read-only. */
|
|
680
|
+
get effectiveSlot() {
|
|
681
|
+
return this.slotRegistration.effective;
|
|
682
|
+
}
|
|
683
|
+
slotAttributes() {
|
|
684
|
+
return {
|
|
685
|
+
vioSlot: this.vioSlot,
|
|
686
|
+
vioSlotName: this.vioSlotName,
|
|
687
|
+
vioScope: this.vioScope,
|
|
688
|
+
vioScopeName: this.vioScopeName
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
/** Attribution every analytics event of this carousel carries (`Vio.attribution` + the content overrides). */
|
|
692
|
+
attribution() {
|
|
693
|
+
const base = Vio.attribution(this.effectiveSlot, { sponsorId: this.sponsorId > 0 ? this.sponsorId : void 0 });
|
|
694
|
+
return {
|
|
695
|
+
...base,
|
|
696
|
+
...this.vioContentId ? { contentId: this.vioContentId } : {},
|
|
697
|
+
...this.vioContentTitle ? { contentTitle: this.vioContentTitle } : {}
|
|
698
|
+
};
|
|
699
|
+
}
|
|
482
700
|
static {
|
|
483
701
|
this.styles = css`
|
|
484
702
|
:host {
|
|
@@ -626,6 +844,7 @@ var VioProductCarousel = class extends LitElement {
|
|
|
626
844
|
}
|
|
627
845
|
connectedCallback() {
|
|
628
846
|
super.connectedCallback();
|
|
847
|
+
this.slotRegistration.connect(this.slotAttributes());
|
|
629
848
|
this.observeForAnalytics();
|
|
630
849
|
if (this.productRefs.trim() || this.productIds.trim()) {
|
|
631
850
|
void this.autoFetch();
|
|
@@ -634,14 +853,28 @@ var VioProductCarousel = class extends LitElement {
|
|
|
634
853
|
disconnectedCallback() {
|
|
635
854
|
this.unobserveImpression?.();
|
|
636
855
|
this.unobserveImpression = null;
|
|
856
|
+
this.slotRegistration.clear();
|
|
637
857
|
super.disconnectedCallback();
|
|
638
858
|
}
|
|
639
859
|
updated(changed) {
|
|
640
|
-
|
|
860
|
+
if (changed.has("vioSlot") || changed.has("vioSlotName") || changed.has("vioScope") || changed.has("vioScopeName")) {
|
|
861
|
+
this.slotRegistration.sync(this.slotAttributes());
|
|
862
|
+
}
|
|
863
|
+
const propChanged = changed.has("productRefs") || changed.has("productIds") || changed.has("commerceKey") || changed.has("sponsorId") || changed.has("vioSlot");
|
|
641
864
|
if (propChanged && (this.productRefs.trim() || this.productIds.trim())) {
|
|
642
865
|
void this.autoFetch();
|
|
643
866
|
}
|
|
644
867
|
}
|
|
868
|
+
/**
|
|
869
|
+
* The sponsor of this carousel: the host's `sponsor-id`, else the primary
|
|
870
|
+
* sponsor of the campaign the slot shows (ADR-0034 — "the campaign of the
|
|
871
|
+
* block is its sponsor"), else 0 (unknown).
|
|
872
|
+
*/
|
|
873
|
+
effectiveSponsorId() {
|
|
874
|
+
if (this.sponsorId > 0) return this.sponsorId;
|
|
875
|
+
const fromSlot = Number(Vio.campaignForSlot(this.effectiveSlot || null)?.primarySponsor?.id);
|
|
876
|
+
return Number.isFinite(fromSlot) && fromSlot > 0 ? fromSlot : 0;
|
|
877
|
+
}
|
|
645
878
|
async autoFetch() {
|
|
646
879
|
if (!Vio.isInitialized) {
|
|
647
880
|
this.fetchError = "Vio not initialized \u2014 call Vio.init({ apiKey }) first";
|
|
@@ -650,7 +883,14 @@ var VioProductCarousel = class extends LitElement {
|
|
|
650
883
|
this.isLoading = true;
|
|
651
884
|
this.fetchError = "";
|
|
652
885
|
try {
|
|
653
|
-
|
|
886
|
+
if (this.productRefs.trim() === "" && !this.commerceKey && this.sponsorId <= 0 && !Vio.bootstrapCache) {
|
|
887
|
+
try {
|
|
888
|
+
await Vio.bootstrap();
|
|
889
|
+
} catch {
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
const sponsorId = this.effectiveSponsorId();
|
|
893
|
+
const refs = this.productRefs.trim() ? this.parseRefs(this.productRefs) : this.productIds.split(",").map((s) => parseInt(s.trim(), 10)).filter((n) => !Number.isNaN(n)).map((id) => ({ sponsorId, productId: id }));
|
|
654
894
|
if (refs.length === 0) {
|
|
655
895
|
this.fetched = [];
|
|
656
896
|
return;
|
|
@@ -662,23 +902,23 @@ var VioProductCarousel = class extends LitElement {
|
|
|
662
902
|
bySponsor.set(r.sponsorId, arr);
|
|
663
903
|
}
|
|
664
904
|
const fetched = await Promise.all(
|
|
665
|
-
Array.from(bySponsor.entries()).map(async ([
|
|
666
|
-
const commerce = this.commerceKey && this.productRefs.trim() === "" ? Vio.commerceWithKey(this.commerceKey) : Vio.commerceFor(
|
|
905
|
+
Array.from(bySponsor.entries()).map(async ([sponsorId2, ids]) => {
|
|
906
|
+
const commerce = this.commerceKey && this.productRefs.trim() === "" ? Vio.commerceWithKey(this.commerceKey) : Vio.commerceFor(sponsorId2);
|
|
667
907
|
const products = await commerce.channel.product.getByIds({
|
|
668
908
|
product_ids: ids,
|
|
669
909
|
currency: this.currency,
|
|
670
910
|
image_size: "large"
|
|
671
911
|
});
|
|
672
|
-
return { sponsorId, products };
|
|
912
|
+
return { sponsorId: sponsorId2, products };
|
|
673
913
|
})
|
|
674
914
|
);
|
|
675
915
|
const lookup = /* @__PURE__ */ new Map();
|
|
676
|
-
for (const { sponsorId, products } of fetched) {
|
|
677
|
-
for (const p of products) lookup.set(`${
|
|
916
|
+
for (const { sponsorId: sponsorId2, products } of fetched) {
|
|
917
|
+
for (const p of products) lookup.set(`${sponsorId2}:${p.id}`, p);
|
|
678
918
|
}
|
|
679
|
-
this.fetched = refs.map(({ sponsorId, productId }) => {
|
|
680
|
-
const product = lookup.get(`${
|
|
681
|
-
return product ? { product, sponsorId } : null;
|
|
919
|
+
this.fetched = refs.map(({ sponsorId: sponsorId2, productId }) => {
|
|
920
|
+
const product = lookup.get(`${sponsorId2}:${productId}`);
|
|
921
|
+
return product ? { product, sponsorId: sponsorId2 } : null;
|
|
682
922
|
}).filter((x) => x !== null);
|
|
683
923
|
this.maybeTrackList();
|
|
684
924
|
} catch (err) {
|
|
@@ -695,10 +935,12 @@ var VioProductCarousel = class extends LitElement {
|
|
|
695
935
|
{
|
|
696
936
|
sponsorId: this.sponsorId > 0 ? this.sponsorId : void 0,
|
|
697
937
|
componentTemplateId: "product-carousel",
|
|
698
|
-
|
|
938
|
+
// The slot is the real location id; the label only stands in for it.
|
|
939
|
+
locationId: this.effectiveSlot || this.label || void 0,
|
|
940
|
+
...this.attribution()
|
|
699
941
|
},
|
|
700
942
|
{
|
|
701
|
-
key: `carousel:${this.label || this.productIds || this.productRefs || "default"}`,
|
|
943
|
+
key: `carousel:${this.effectiveSlot || this.label || this.productIds || this.productRefs || "default"}`,
|
|
702
944
|
onImpression: () => {
|
|
703
945
|
this.impressed = true;
|
|
704
946
|
this.maybeTrackList();
|
|
@@ -716,14 +958,16 @@ var VioProductCarousel = class extends LitElement {
|
|
|
716
958
|
if (entries.length === 0) return;
|
|
717
959
|
this.listTracked = true;
|
|
718
960
|
try {
|
|
961
|
+
const sponsorId = this.effectiveSponsorId();
|
|
719
962
|
Vio.analytics.track("view_item_list", {
|
|
720
963
|
context: {
|
|
721
|
-
sponsorId:
|
|
964
|
+
sponsorId: sponsorId > 0 ? sponsorId : void 0,
|
|
722
965
|
componentTemplateId: "product-carousel",
|
|
723
|
-
locationId: this.label || void 0
|
|
966
|
+
locationId: this.effectiveSlot || this.label || void 0,
|
|
967
|
+
...this.attribution()
|
|
724
968
|
},
|
|
725
969
|
commerce: {
|
|
726
|
-
items: entries.map(({ product, sponsorId }) => ({
|
|
970
|
+
items: entries.map(({ product, sponsorId: sponsorId2 }) => ({
|
|
727
971
|
productId: product.id,
|
|
728
972
|
name: product.title,
|
|
729
973
|
price: Number(product.price?.amount_incl_taxes ?? product.price?.amount) || void 0
|
|
@@ -776,6 +1020,9 @@ var VioProductCarousel = class extends LitElement {
|
|
|
776
1020
|
retailer=${retailer}
|
|
777
1021
|
image-url=${img}
|
|
778
1022
|
?has-variants=${(p.variants?.length ?? 0) > 1}
|
|
1023
|
+
.vioSlot=${this.effectiveSlot}
|
|
1024
|
+
.vioContentId=${this.vioContentId}
|
|
1025
|
+
.vioContentTitle=${this.vioContentTitle}
|
|
779
1026
|
></vio-product>
|
|
780
1027
|
`;
|
|
781
1028
|
})}`;
|
|
@@ -845,6 +1092,24 @@ __decorateClass([
|
|
|
845
1092
|
__decorateClass([
|
|
846
1093
|
property({ type: String })
|
|
847
1094
|
], VioProductCarousel.prototype, "currency", 2);
|
|
1095
|
+
__decorateClass([
|
|
1096
|
+
property({ type: String, attribute: "vio-slot" })
|
|
1097
|
+
], VioProductCarousel.prototype, "vioSlot", 2);
|
|
1098
|
+
__decorateClass([
|
|
1099
|
+
property({ type: String, attribute: "vio-slot-name" })
|
|
1100
|
+
], VioProductCarousel.prototype, "vioSlotName", 2);
|
|
1101
|
+
__decorateClass([
|
|
1102
|
+
property({ type: String, attribute: "vio-scope" })
|
|
1103
|
+
], VioProductCarousel.prototype, "vioScope", 2);
|
|
1104
|
+
__decorateClass([
|
|
1105
|
+
property({ type: String, attribute: "vio-scope-name" })
|
|
1106
|
+
], VioProductCarousel.prototype, "vioScopeName", 2);
|
|
1107
|
+
__decorateClass([
|
|
1108
|
+
property({ type: String, attribute: "vio-content-id" })
|
|
1109
|
+
], VioProductCarousel.prototype, "vioContentId", 2);
|
|
1110
|
+
__decorateClass([
|
|
1111
|
+
property({ type: String, attribute: "vio-content-title" })
|
|
1112
|
+
], VioProductCarousel.prototype, "vioContentTitle", 2);
|
|
848
1113
|
__decorateClass([
|
|
849
1114
|
query(".scroller")
|
|
850
1115
|
], VioProductCarousel.prototype, "scroller", 2);
|
|
@@ -885,6 +1150,192 @@ function isMethodEnabled(available, ...names) {
|
|
|
885
1150
|
);
|
|
886
1151
|
}
|
|
887
1152
|
|
|
1153
|
+
// src/ui/dialog-focus.ts
|
|
1154
|
+
var DialogFocus = class {
|
|
1155
|
+
constructor(opts) {
|
|
1156
|
+
this.opts = opts;
|
|
1157
|
+
this.opener = null;
|
|
1158
|
+
this.active = false;
|
|
1159
|
+
/** The element inside the dialog that had focus last — where a pull-back continues from. */
|
|
1160
|
+
this.lastInside = null;
|
|
1161
|
+
this.onKeydown = (e) => {
|
|
1162
|
+
if (e.defaultPrevented) return;
|
|
1163
|
+
if (e.key === "Escape" || e.key === "Esc") {
|
|
1164
|
+
if (this.opts.canClose && !this.opts.canClose()) return;
|
|
1165
|
+
e.preventDefault();
|
|
1166
|
+
e.stopPropagation();
|
|
1167
|
+
this.opts.close();
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
if (e.key !== "Tab" || e.altKey || e.ctrlKey || e.metaKey) return;
|
|
1171
|
+
const container = this.opts.container();
|
|
1172
|
+
if (!container) return;
|
|
1173
|
+
const items = tabbableIn(container);
|
|
1174
|
+
if (items.length === 0) {
|
|
1175
|
+
e.preventDefault();
|
|
1176
|
+
focusElement(container);
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
const active = deepActiveElement();
|
|
1180
|
+
const index = active ? items.indexOf(active) : -1;
|
|
1181
|
+
e.preventDefault();
|
|
1182
|
+
const count = items.length;
|
|
1183
|
+
const next = index === -1 ? e.shiftKey ? count - 1 : 0 : (index + (e.shiftKey ? -1 : 1) + count) % count;
|
|
1184
|
+
focusElement(items[next]);
|
|
1185
|
+
};
|
|
1186
|
+
/**
|
|
1187
|
+
* Focus moved somewhere. Inside the dialog: remember where. Into another
|
|
1188
|
+
* open Vio dialog: theirs. Onto the page: the browser walked out of the
|
|
1189
|
+
* dialog on its own (Safari leaving a widget iframe) — continue from the
|
|
1190
|
+
* element that had focus, backwards when the page element sits before
|
|
1191
|
+
* the host in document order.
|
|
1192
|
+
*/
|
|
1193
|
+
this.onFocusIn = (e) => {
|
|
1194
|
+
if (!this.opts.host.isConnected) return;
|
|
1195
|
+
const path = e.composedPath();
|
|
1196
|
+
if (path.includes(this.opts.host)) {
|
|
1197
|
+
const active = deepActiveElement();
|
|
1198
|
+
this.lastInside = active instanceof HTMLElement ? active : null;
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
if (path.some((n) => isOpenVioDialog(n, this.opts.host))) return;
|
|
1202
|
+
const container = this.opts.container();
|
|
1203
|
+
if (!container) return;
|
|
1204
|
+
const items = tabbableIn(container);
|
|
1205
|
+
if (items.length === 0) {
|
|
1206
|
+
focusElement(container);
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
const target = e.target instanceof Node ? e.target : null;
|
|
1210
|
+
const backwards = !!target && precedes(target, this.opts.host);
|
|
1211
|
+
const index = this.lastInside ? items.indexOf(this.lastInside) : -1;
|
|
1212
|
+
const count = items.length;
|
|
1213
|
+
const next = backwards ? index <= 0 ? count - 1 : index - 1 : index === -1 || index === count - 1 ? 0 : index + 1;
|
|
1214
|
+
focusElement(items[next]);
|
|
1215
|
+
};
|
|
1216
|
+
/** Tab pressed while focus is on the page (or the body): enter the dialog. */
|
|
1217
|
+
this.onDocumentKeydown = (e) => {
|
|
1218
|
+
if (!this.opts.host.isConnected) return;
|
|
1219
|
+
if (e.defaultPrevented || e.key !== "Tab" || e.altKey || e.ctrlKey || e.metaKey) return;
|
|
1220
|
+
const active = deepActiveElement();
|
|
1221
|
+
if (active && isWithin(active, this.opts.host)) return;
|
|
1222
|
+
if (active && hasOpenVioDialogAbove(active, this.opts.host)) return;
|
|
1223
|
+
const container = this.opts.container();
|
|
1224
|
+
if (!container) return;
|
|
1225
|
+
e.preventDefault();
|
|
1226
|
+
e.stopPropagation();
|
|
1227
|
+
const items = tabbableIn(container);
|
|
1228
|
+
focusElement(items.length === 0 ? container : e.shiftKey ? items[items.length - 1] : items[0]);
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Call from `updated()` when `open` changed. Lit reports `open` as changed
|
|
1233
|
+
* on the first render too (from undefined to its initial value): with the
|
|
1234
|
+
* same value twice this is a no-op, so that first call does nothing.
|
|
1235
|
+
*/
|
|
1236
|
+
update(open) {
|
|
1237
|
+
if (open === this.active) return;
|
|
1238
|
+
this.active = open;
|
|
1239
|
+
if (open) this.opened();
|
|
1240
|
+
else this.closed();
|
|
1241
|
+
}
|
|
1242
|
+
opened() {
|
|
1243
|
+
this.opener = deepActiveElement();
|
|
1244
|
+
this.lastInside = null;
|
|
1245
|
+
this.opts.host.addEventListener("keydown", this.onKeydown);
|
|
1246
|
+
document.addEventListener("focusin", this.onFocusIn, true);
|
|
1247
|
+
document.addEventListener("keydown", this.onDocumentKeydown, true);
|
|
1248
|
+
const container = this.opts.container();
|
|
1249
|
+
if (container) focusInto(container);
|
|
1250
|
+
}
|
|
1251
|
+
closed() {
|
|
1252
|
+
this.opts.host.removeEventListener("keydown", this.onKeydown);
|
|
1253
|
+
document.removeEventListener("focusin", this.onFocusIn, true);
|
|
1254
|
+
document.removeEventListener("keydown", this.onDocumentKeydown, true);
|
|
1255
|
+
const opener = this.opener;
|
|
1256
|
+
this.opener = null;
|
|
1257
|
+
const active = deepActiveElement();
|
|
1258
|
+
if (!active || !isWithin(active, this.opts.host)) return;
|
|
1259
|
+
if (opener && opener.isConnected && isRestorable(opener)) {
|
|
1260
|
+
focusElement(opener);
|
|
1261
|
+
} else if (active instanceof HTMLElement) {
|
|
1262
|
+
active.blur();
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
};
|
|
1266
|
+
function isOpenVioDialog(node, host) {
|
|
1267
|
+
return node instanceof Element && node !== host && node.tagName.startsWith("VIO-") && node.hasAttribute("open");
|
|
1268
|
+
}
|
|
1269
|
+
function hasOpenVioDialogAbove(node, host) {
|
|
1270
|
+
let n = node;
|
|
1271
|
+
while (n) {
|
|
1272
|
+
if (isOpenVioDialog(n, host)) return true;
|
|
1273
|
+
n = n.parentNode ?? (n instanceof ShadowRoot ? n.host : null);
|
|
1274
|
+
}
|
|
1275
|
+
return false;
|
|
1276
|
+
}
|
|
1277
|
+
function precedes(a, b) {
|
|
1278
|
+
return (a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0;
|
|
1279
|
+
}
|
|
1280
|
+
function deepActiveElement() {
|
|
1281
|
+
if (typeof document === "undefined") return null;
|
|
1282
|
+
let el = document.activeElement;
|
|
1283
|
+
while (el?.shadowRoot?.activeElement) el = el.shadowRoot.activeElement;
|
|
1284
|
+
return el;
|
|
1285
|
+
}
|
|
1286
|
+
var TABBABLE_CANDIDATES = "a[href], area[href], button, input, select, textarea, iframe, summary, [tabindex], [contenteditable]";
|
|
1287
|
+
function tabbableIn(container) {
|
|
1288
|
+
const out = [];
|
|
1289
|
+
walkChildren(container, out);
|
|
1290
|
+
return out;
|
|
1291
|
+
}
|
|
1292
|
+
function walkChildren(parent, out) {
|
|
1293
|
+
for (const child of Array.from(parent.children)) {
|
|
1294
|
+
if (child instanceof HTMLSlotElement) {
|
|
1295
|
+
const assigned = child.assignedElements({ flatten: true });
|
|
1296
|
+
if (assigned.length > 0) {
|
|
1297
|
+
for (const el of assigned) visit(el, out);
|
|
1298
|
+
continue;
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
visit(child, out);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
function visit(el, out) {
|
|
1305
|
+
if (isTabbable(el)) out.push(el);
|
|
1306
|
+
if (el.shadowRoot) walkChildren(el.shadowRoot, out);
|
|
1307
|
+
else walkChildren(el, out);
|
|
1308
|
+
}
|
|
1309
|
+
function isTabbable(el) {
|
|
1310
|
+
if (!(el instanceof HTMLElement) || !el.matches(TABBABLE_CANDIDATES)) return false;
|
|
1311
|
+
const tabindex = el.getAttribute("tabindex");
|
|
1312
|
+
if (tabindex !== null && Number(tabindex) < 0) return false;
|
|
1313
|
+
if (el.hasAttribute("disabled")) return false;
|
|
1314
|
+
if (el instanceof HTMLInputElement && el.type === "hidden") return false;
|
|
1315
|
+
if (el.getAttribute("contenteditable") === "false" && !el.matches("a[href], button, input, select, textarea, iframe, [tabindex]")) return false;
|
|
1316
|
+
if (el.hidden || el.getAttribute("aria-hidden") === "true") return false;
|
|
1317
|
+
if (typeof el.checkVisibility === "function" && !el.checkVisibility()) return false;
|
|
1318
|
+
return true;
|
|
1319
|
+
}
|
|
1320
|
+
function focusInto(container) {
|
|
1321
|
+
const first = tabbableIn(container)[0];
|
|
1322
|
+
focusElement(first ?? container);
|
|
1323
|
+
}
|
|
1324
|
+
function focusElement(el) {
|
|
1325
|
+
el.focus({ preventScroll: true });
|
|
1326
|
+
}
|
|
1327
|
+
function isRestorable(el) {
|
|
1328
|
+
return el instanceof HTMLElement && el !== document.body && el !== document.documentElement;
|
|
1329
|
+
}
|
|
1330
|
+
function isWithin(node, ancestor) {
|
|
1331
|
+
let n = node;
|
|
1332
|
+
while (n) {
|
|
1333
|
+
if (n === ancestor) return true;
|
|
1334
|
+
n = n.parentNode ?? (n instanceof ShadowRoot ? n.host : null);
|
|
1335
|
+
}
|
|
1336
|
+
return false;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
888
1339
|
// src/ui/components/vio-product-detail.ts
|
|
889
1340
|
var STRIPE_LOGO_SRC = "data:image/svg+xml;utf8," + encodeURIComponent(
|
|
890
1341
|
'<svg width="360" height="150" viewBox="0 0 360 150" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M360 77.4001C360 51.8001 347.6 31.6001 323.9 31.6001C300.1 31.6001 285.7 51.8001 285.7 77.2001C285.7 107.3 302.7 122.5 327.1 122.5C339 122.5 348 119.8 354.8 116V96.0001C348 99.4001 340.2 101.5 330.3 101.5C320.6 101.5 312 98.1001 310.9 86.3001H359.8C359.8 85.0001 360 79.8001 360 77.4001ZM310.6 67.9001C310.6 56.6001 317.5 51.9001 323.8 51.9001C329.9 51.9001 336.4 56.6001 336.4 67.9001H310.6Z" fill="#533AFD"/><path fill-rule="evenodd" clip-rule="evenodd" d="M247.1 31.6001C237.3 31.6001 231 36.2001 227.5 39.4001L226.2 33.2001H204.2V149.8L229.2 144.5L229.3 116.2C232.9 118.8 238.2 122.5 247 122.5C264.9 122.5 281.2 108.1 281.2 76.4001C281.1 47.4001 264.6 31.6001 247.1 31.6001ZM241.1 100.5C235.2 100.5 231.7 98.4001 229.3 95.8001L229.2 58.7001C231.8 55.8001 235.4 53.8001 241.1 53.8001C250.2 53.8001 256.5 64.0001 256.5 77.1001C256.5 90.5001 250.3 100.5 241.1 100.5Z" fill="#533AFD"/><path fill-rule="evenodd" clip-rule="evenodd" d="M169.8 25.7L194.9 20.3V0L169.8 5.3V25.7Z" fill="#533AFD"/><path d="M194.9 33.3H169.8V120.8H194.9V33.3Z" fill="#533AFD"/><path fill-rule="evenodd" clip-rule="evenodd" d="M142.9 40.7L141.3 33.3H119.7V120.8H144.7V61.5C150.6 53.8 160.6 55.2 163.7 56.3V33.3C160.5 32.1 148.8 29.9 142.9 40.7Z" fill="#533AFD"/><path fill-rule="evenodd" clip-rule="evenodd" d="M92.8999 11.6001L68.4999 16.8001L68.3999 96.9001C68.3999 111.7 79.4999 122.6 94.2999 122.6C102.5 122.6 108.5 121.1 111.8 119.3V99.0001C108.6 100.3 92.7999 104.9 92.7999 90.1001V54.6001H111.8V33.3001H92.7999L92.8999 11.6001Z" fill="#533AFD"/><path fill-rule="evenodd" clip-rule="evenodd" d="M25.3 58.7001C25.3 54.8001 28.5 53.3001 33.8 53.3001C41.4 53.3001 51 55.6001 58.6 59.7001V36.2001C50.3 32.9001 42.1 31.6001 33.8 31.6001C13.5 31.6001 0 42.2001 0 59.9001C0 87.5001 38 83.1001 38 95.0001C38 99.6001 34 101.1 28.4 101.1C20.1 101.1 9.5 97.7001 1.1 93.1001V116.9C10.4 120.9 19.8 122.6 28.4 122.6C49.2 122.6 63.5 112.3 63.5 94.4001C63.4 64.6001 25.3 69.9001 25.3 58.7001Z" fill="#533AFD"/></svg>'
|
|
@@ -897,6 +1348,17 @@ var VioProductDetail = class extends LitElement {
|
|
|
897
1348
|
this.sponsorId = 0;
|
|
898
1349
|
this.autoOpen = true;
|
|
899
1350
|
this.currency = "NOK";
|
|
1351
|
+
this.vioSlot = "";
|
|
1352
|
+
this.vioSlotName = "";
|
|
1353
|
+
this.vioScope = "";
|
|
1354
|
+
this.vioScopeName = "";
|
|
1355
|
+
this.vioContentId = "";
|
|
1356
|
+
this.vioContentTitle = "";
|
|
1357
|
+
this.slotRegistration = new SlotRegistration(this, "detail");
|
|
1358
|
+
/** What the click that opened the detail carried ('' when nothing). */
|
|
1359
|
+
this.eventSlot = "";
|
|
1360
|
+
this.eventContentId = "";
|
|
1361
|
+
this.eventContentTitle = "";
|
|
900
1362
|
this.product = null;
|
|
901
1363
|
this.sellerName = null;
|
|
902
1364
|
this.sellerSettled = false;
|
|
@@ -916,13 +1378,27 @@ var VioProductDetail = class extends LitElement {
|
|
|
916
1378
|
this.optMapCache = /* @__PURE__ */ new WeakMap();
|
|
917
1379
|
/** Pre-prepared Apple Pay handle — show() fires synchronously on tap. */
|
|
918
1380
|
this.applePayHandle = null;
|
|
1381
|
+
/** Keyboard and focus while open: Escape closes, Tab stays inside, focus returns to the opener. */
|
|
1382
|
+
this.dialogFocus = new DialogFocus({
|
|
1383
|
+
host: this,
|
|
1384
|
+
container: () => this.renderRoot.querySelector(".modal"),
|
|
1385
|
+
close: () => this.close()
|
|
1386
|
+
});
|
|
919
1387
|
this.boundProductClick = (e) => {
|
|
920
1388
|
if (!this.autoOpen) return;
|
|
921
1389
|
const ev = e;
|
|
922
1390
|
const productIdNum = parseInt(ev.detail.productId, 10);
|
|
923
1391
|
if (Number.isNaN(productIdNum)) return;
|
|
924
|
-
const
|
|
925
|
-
|
|
1392
|
+
const slot = typeof ev.detail.slot === "string" ? ev.detail.slot.trim() : "";
|
|
1393
|
+
let sponsorIdNum = parseInt(ev.detail.sponsorId, 10);
|
|
1394
|
+
if (Number.isNaN(sponsorIdNum) || sponsorIdNum <= 0) {
|
|
1395
|
+
const fromSlot = Number(Vio.campaignForSlot(slot || this.effectiveSlot || null)?.primarySponsor?.id);
|
|
1396
|
+
if (!Number.isFinite(fromSlot) || fromSlot <= 0) return;
|
|
1397
|
+
sponsorIdNum = fromSlot;
|
|
1398
|
+
}
|
|
1399
|
+
this.eventSlot = slot;
|
|
1400
|
+
this.eventContentId = ev.detail.contentId !== void 0 && ev.detail.contentId !== null ? String(ev.detail.contentId).trim() : "";
|
|
1401
|
+
this.eventContentTitle = typeof ev.detail.contentTitle === "string" ? ev.detail.contentTitle.trim() : "";
|
|
926
1402
|
this.productId = ev.detail.productId;
|
|
927
1403
|
this.sponsorId = sponsorIdNum;
|
|
928
1404
|
this.currency = ev.detail.currency || getGlobalCurrency();
|
|
@@ -930,6 +1406,33 @@ var VioProductDetail = class extends LitElement {
|
|
|
930
1406
|
void this.fetchProduct();
|
|
931
1407
|
};
|
|
932
1408
|
}
|
|
1409
|
+
/** The slot id in effect for this element: `vio-slot`, else the auto id (`detail-N`). Read-only. */
|
|
1410
|
+
get effectiveSlot() {
|
|
1411
|
+
return this.slotRegistration.effective;
|
|
1412
|
+
}
|
|
1413
|
+
/** The slot the open product is attributed to: the opening card's, else this element's. */
|
|
1414
|
+
get activeSlot() {
|
|
1415
|
+
return this.eventSlot || this.effectiveSlot;
|
|
1416
|
+
}
|
|
1417
|
+
slotAttributes() {
|
|
1418
|
+
return {
|
|
1419
|
+
vioSlot: this.vioSlot,
|
|
1420
|
+
vioSlotName: this.vioSlotName,
|
|
1421
|
+
vioScope: this.vioScope,
|
|
1422
|
+
vioScopeName: this.vioScopeName
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
/** Attribution every event of the open product carries (`Vio.attribution` of the opening card's slot, else this element's). */
|
|
1426
|
+
attribution() {
|
|
1427
|
+
const base = Vio.attribution(this.activeSlot, { sponsorId: this.sponsorId > 0 ? this.sponsorId : void 0 });
|
|
1428
|
+
const contentId = this.eventContentId || this.vioContentId;
|
|
1429
|
+
const contentTitle = this.eventContentTitle || this.vioContentTitle;
|
|
1430
|
+
return {
|
|
1431
|
+
...base,
|
|
1432
|
+
...contentId ? { contentId } : {},
|
|
1433
|
+
...contentTitle ? { contentTitle } : {}
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
933
1436
|
static {
|
|
934
1437
|
this.styles = css`
|
|
935
1438
|
:host { display: contents; }
|
|
@@ -959,6 +1462,8 @@ var VioProductDetail = class extends LitElement {
|
|
|
959
1462
|
overflow-y: auto;
|
|
960
1463
|
}
|
|
961
1464
|
:host([open]) .modal { transform: translateY(0); }
|
|
1465
|
+
/* Focus lands on the panel itself only as a fallback — no ring for it. */
|
|
1466
|
+
.modal:focus { outline: none; }
|
|
962
1467
|
|
|
963
1468
|
.handle { display: none; }
|
|
964
1469
|
|
|
@@ -967,7 +1472,7 @@ var VioProductDetail = class extends LitElement {
|
|
|
967
1472
|
.modal {
|
|
968
1473
|
top: auto;
|
|
969
1474
|
height: 92vh;
|
|
970
|
-
border-radius: 16px 16px 0 0;
|
|
1475
|
+
border-radius: var(--vio-radius-xl, 16px) var(--vio-radius-xl, 16px) 0 0;
|
|
971
1476
|
box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.08);
|
|
972
1477
|
}
|
|
973
1478
|
.handle {
|
|
@@ -981,7 +1486,7 @@ var VioProductDetail = class extends LitElement {
|
|
|
981
1486
|
width: 40px;
|
|
982
1487
|
height: 4px;
|
|
983
1488
|
background: var(--vio-color-border, #e5e5e5);
|
|
984
|
-
border-radius: 2px;
|
|
1489
|
+
border-radius: var(--vio-radius-sm, 2px);
|
|
985
1490
|
}
|
|
986
1491
|
}
|
|
987
1492
|
|
|
@@ -1346,9 +1851,9 @@ var VioProductDetail = class extends LitElement {
|
|
|
1346
1851
|
);
|
|
1347
1852
|
background-size: 200% 100%;
|
|
1348
1853
|
animation: vio-skeleton-shimmer 1.4s ease-in-out infinite;
|
|
1349
|
-
border-radius: 8px;
|
|
1854
|
+
border-radius: var(--vio-radius-lg, 8px);
|
|
1350
1855
|
}
|
|
1351
|
-
.sk-image { width: 100%; aspect-ratio: 1; border-radius: 12px; }
|
|
1856
|
+
.sk-image { width: 100%; aspect-ratio: 1; border-radius: var(--vio-radius-lg, 12px); }
|
|
1352
1857
|
.sk-line { height: 14px; }
|
|
1353
1858
|
.sk-brand { width: 38%; height: 10px; margin-top: 6px; }
|
|
1354
1859
|
.sk-title { width: 85%; height: 26px; }
|
|
@@ -1375,13 +1880,21 @@ var VioProductDetail = class extends LitElement {
|
|
|
1375
1880
|
}
|
|
1376
1881
|
connectedCallback() {
|
|
1377
1882
|
super.connectedCallback();
|
|
1883
|
+
this.slotRegistration.connect(this.slotAttributes());
|
|
1378
1884
|
void this.loadVippsButton();
|
|
1379
1885
|
document.addEventListener("vio:product-click", this.boundProductClick);
|
|
1380
1886
|
}
|
|
1381
1887
|
disconnectedCallback() {
|
|
1382
1888
|
document.removeEventListener("vio:product-click", this.boundProductClick);
|
|
1889
|
+
this.slotRegistration.clear();
|
|
1383
1890
|
super.disconnectedCallback();
|
|
1384
1891
|
}
|
|
1892
|
+
updated(changed) {
|
|
1893
|
+
if (changed.has("open")) this.dialogFocus.update(this.open);
|
|
1894
|
+
if (changed.has("vioSlot") || changed.has("vioSlotName") || changed.has("vioScope") || changed.has("vioScopeName")) {
|
|
1895
|
+
this.slotRegistration.sync(this.slotAttributes());
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1385
1898
|
show() {
|
|
1386
1899
|
this.open = true;
|
|
1387
1900
|
}
|
|
@@ -1447,7 +1960,8 @@ var VioProductDetail = class extends LitElement {
|
|
|
1447
1960
|
Vio.analytics.track("view_item", {
|
|
1448
1961
|
context: {
|
|
1449
1962
|
sponsorId: this.sponsorId > 0 ? Number(this.sponsorId) : void 0,
|
|
1450
|
-
componentTemplateId: "product-detail"
|
|
1963
|
+
componentTemplateId: "product-detail",
|
|
1964
|
+
...this.attribution()
|
|
1451
1965
|
},
|
|
1452
1966
|
commerce: {
|
|
1453
1967
|
items: [
|
|
@@ -1487,7 +2001,7 @@ var VioProductDetail = class extends LitElement {
|
|
|
1487
2001
|
}
|
|
1488
2002
|
/** Which buy buttons to render — backend-configured per sponsor. */
|
|
1489
2003
|
async loadAvailablePaymentMethods() {
|
|
1490
|
-
const spId = this.sponsorId || globalThis.__VIO_SPONSOR_ID__;
|
|
2004
|
+
const spId = this.sponsorId || Number(Vio.campaignForSlot(this.activeSlot || null)?.primarySponsor?.id) || globalThis.__VIO_SPONSOR_ID__;
|
|
1491
2005
|
if (!spId) return;
|
|
1492
2006
|
try {
|
|
1493
2007
|
const methods = await Vio.checkout.getAvailablePaymentMethods(spId);
|
|
@@ -1769,12 +2283,14 @@ var VioProductDetail = class extends LitElement {
|
|
|
1769
2283
|
const sponsor = Vio.bootstrapCache?.primarySponsor;
|
|
1770
2284
|
const secondaries = Vio.bootstrapCache?.secondarySponsors ?? [];
|
|
1771
2285
|
const sponsorName = sponsor?.id === this.sponsorId ? sponsor.name : secondaries.find((s) => s.id === this.sponsorId)?.name;
|
|
2286
|
+
const { campaignId, slot, contentId } = this.attribution();
|
|
1772
2287
|
Vio.cart.addProduct({
|
|
1773
2288
|
product: this.product,
|
|
1774
2289
|
sponsorId: this.sponsorId,
|
|
1775
2290
|
sponsorName,
|
|
1776
2291
|
quantity: this.quantity,
|
|
1777
|
-
variantId: variant?.id
|
|
2292
|
+
variantId: variant?.id,
|
|
2293
|
+
origin: { campaignId, slot, contentId }
|
|
1778
2294
|
});
|
|
1779
2295
|
return variant?.id;
|
|
1780
2296
|
}
|
|
@@ -1791,7 +2307,8 @@ var VioProductDetail = class extends LitElement {
|
|
|
1791
2307
|
productId: this.product.id,
|
|
1792
2308
|
sponsorId: this.sponsorId,
|
|
1793
2309
|
quantity: this.quantity,
|
|
1794
|
-
variantId
|
|
2310
|
+
variantId,
|
|
2311
|
+
...this.attribution()
|
|
1795
2312
|
}
|
|
1796
2313
|
})
|
|
1797
2314
|
);
|
|
@@ -1816,7 +2333,7 @@ var VioProductDetail = class extends LitElement {
|
|
|
1816
2333
|
new CustomEvent("vio:checkout-open", {
|
|
1817
2334
|
bubbles: true,
|
|
1818
2335
|
composed: true,
|
|
1819
|
-
detail: { sponsorId: this.sponsorId, paymentMethod: "klarna", express: true }
|
|
2336
|
+
detail: { sponsorId: this.sponsorId, paymentMethod: "klarna", express: true, ...this.attribution() }
|
|
1820
2337
|
})
|
|
1821
2338
|
);
|
|
1822
2339
|
} finally {
|
|
@@ -1846,7 +2363,7 @@ var VioProductDetail = class extends LitElement {
|
|
|
1846
2363
|
new CustomEvent("vio:checkout-open", {
|
|
1847
2364
|
bubbles: true,
|
|
1848
2365
|
composed: true,
|
|
1849
|
-
detail: { sponsorId: this.sponsorId, express: false }
|
|
2366
|
+
detail: { sponsorId: this.sponsorId, express: false, ...this.attribution() }
|
|
1850
2367
|
})
|
|
1851
2368
|
);
|
|
1852
2369
|
}
|
|
@@ -1882,7 +2399,7 @@ var VioProductDetail = class extends LitElement {
|
|
|
1882
2399
|
new CustomEvent("vio:checkout-open", {
|
|
1883
2400
|
bubbles: true,
|
|
1884
2401
|
composed: true,
|
|
1885
|
-
detail: { sponsorId: this.sponsorId, paymentMethod: "vipps", express: false }
|
|
2402
|
+
detail: { sponsorId: this.sponsorId, paymentMethod: "vipps", express: false, ...this.attribution() }
|
|
1886
2403
|
})
|
|
1887
2404
|
);
|
|
1888
2405
|
}
|
|
@@ -1895,7 +2412,7 @@ var VioProductDetail = class extends LitElement {
|
|
|
1895
2412
|
new CustomEvent("vio:checkout-open", {
|
|
1896
2413
|
bubbles: true,
|
|
1897
2414
|
composed: true,
|
|
1898
|
-
detail: { sponsorId: this.sponsorId, paymentMethod: "stripe", express: false }
|
|
2415
|
+
detail: { sponsorId: this.sponsorId, paymentMethod: "stripe", express: false, ...this.attribution() }
|
|
1899
2416
|
})
|
|
1900
2417
|
);
|
|
1901
2418
|
}
|
|
@@ -1938,7 +2455,7 @@ var VioProductDetail = class extends LitElement {
|
|
|
1938
2455
|
render() {
|
|
1939
2456
|
return html`
|
|
1940
2457
|
<div class="backdrop" @click=${this.close}></div>
|
|
1941
|
-
<div class="modal" role="dialog" aria-label="Produkt">
|
|
2458
|
+
<div class="modal" role="dialog" aria-modal="true" aria-label="Produkt" tabindex="-1">
|
|
1942
2459
|
<div class="handle" @click=${this.close} aria-hidden="true"></div>
|
|
1943
2460
|
<div class="topbar">
|
|
1944
2461
|
<span class="topbar-brand">${this.headerName()}</span>
|
|
@@ -2178,6 +2695,24 @@ __decorateClass([
|
|
|
2178
2695
|
__decorateClass([
|
|
2179
2696
|
property({ type: String })
|
|
2180
2697
|
], VioProductDetail.prototype, "currency", 2);
|
|
2698
|
+
__decorateClass([
|
|
2699
|
+
property({ type: String, attribute: "vio-slot" })
|
|
2700
|
+
], VioProductDetail.prototype, "vioSlot", 2);
|
|
2701
|
+
__decorateClass([
|
|
2702
|
+
property({ type: String, attribute: "vio-slot-name" })
|
|
2703
|
+
], VioProductDetail.prototype, "vioSlotName", 2);
|
|
2704
|
+
__decorateClass([
|
|
2705
|
+
property({ type: String, attribute: "vio-scope" })
|
|
2706
|
+
], VioProductDetail.prototype, "vioScope", 2);
|
|
2707
|
+
__decorateClass([
|
|
2708
|
+
property({ type: String, attribute: "vio-scope-name" })
|
|
2709
|
+
], VioProductDetail.prototype, "vioScopeName", 2);
|
|
2710
|
+
__decorateClass([
|
|
2711
|
+
property({ type: String, attribute: "vio-content-id" })
|
|
2712
|
+
], VioProductDetail.prototype, "vioContentId", 2);
|
|
2713
|
+
__decorateClass([
|
|
2714
|
+
property({ type: String, attribute: "vio-content-title" })
|
|
2715
|
+
], VioProductDetail.prototype, "vioContentTitle", 2);
|
|
2181
2716
|
__decorateClass([
|
|
2182
2717
|
state()
|
|
2183
2718
|
], VioProductDetail.prototype, "product", 2);
|
|
@@ -2247,6 +2782,12 @@ var VioCart = class extends LitElement {
|
|
|
2247
2782
|
this.preparedTotal = -1;
|
|
2248
2783
|
/** In-flight guard for loadAvailablePaymentMethods. */
|
|
2249
2784
|
this.loadingPaymentMethods = false;
|
|
2785
|
+
/** Keyboard and focus while open: Escape closes, Tab stays inside, focus returns to the opener. */
|
|
2786
|
+
this.dialogFocus = new DialogFocus({
|
|
2787
|
+
host: this,
|
|
2788
|
+
container: () => this.renderRoot.querySelector(".drawer"),
|
|
2789
|
+
close: () => this.close()
|
|
2790
|
+
});
|
|
2250
2791
|
this.boundOnCartChange = (e) => {
|
|
2251
2792
|
const detail = e.detail;
|
|
2252
2793
|
this.carts = detail.cartsBySponsor;
|
|
@@ -2289,6 +2830,8 @@ var VioCart = class extends LitElement {
|
|
|
2289
2830
|
box-shadow: -8px 0 24px rgba(0, 0, 0, 0.06);
|
|
2290
2831
|
}
|
|
2291
2832
|
:host([open]) .drawer { transform: translateX(0); }
|
|
2833
|
+
/* Focus lands on the drawer itself only as a fallback — no ring for it. */
|
|
2834
|
+
.drawer:focus { outline: none; }
|
|
2292
2835
|
|
|
2293
2836
|
.handle { display: none; }
|
|
2294
2837
|
|
|
@@ -2301,7 +2844,7 @@ var VioCart = class extends LitElement {
|
|
|
2301
2844
|
bottom: 0;
|
|
2302
2845
|
width: 100%;
|
|
2303
2846
|
height: 88vh;
|
|
2304
|
-
border-radius: 16px 16px 0 0;
|
|
2847
|
+
border-radius: var(--vio-radius-xl, 16px) var(--vio-radius-xl, 16px) 0 0;
|
|
2305
2848
|
transform: translateY(100%);
|
|
2306
2849
|
box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.08);
|
|
2307
2850
|
}
|
|
@@ -2316,7 +2859,7 @@ var VioCart = class extends LitElement {
|
|
|
2316
2859
|
width: 40px;
|
|
2317
2860
|
height: 4px;
|
|
2318
2861
|
background: var(--vio-color-border, #e5e5e5);
|
|
2319
|
-
border-radius: 2px;
|
|
2862
|
+
border-radius: var(--vio-radius-sm, 2px);
|
|
2320
2863
|
}
|
|
2321
2864
|
}
|
|
2322
2865
|
|
|
@@ -2409,7 +2952,7 @@ var VioCart = class extends LitElement {
|
|
|
2409
2952
|
height: 24px;
|
|
2410
2953
|
border: 1px solid var(--vio-color-border, #e5e5e5);
|
|
2411
2954
|
background: #fff;
|
|
2412
|
-
border-radius: 2px;
|
|
2955
|
+
border-radius: var(--vio-radius-sm, 2px);
|
|
2413
2956
|
cursor: pointer;
|
|
2414
2957
|
font-size: 12px;
|
|
2415
2958
|
color: var(--vio-color-text, #0a0a0a);
|
|
@@ -2466,7 +3009,7 @@ var VioCart = class extends LitElement {
|
|
|
2466
3009
|
background: #000;
|
|
2467
3010
|
color: #fff;
|
|
2468
3011
|
border: none;
|
|
2469
|
-
border-radius: 8px;
|
|
3012
|
+
border-radius: var(--vio-radius-lg, 8px);
|
|
2470
3013
|
font-size: 19px;
|
|
2471
3014
|
font-weight: 500;
|
|
2472
3015
|
line-height: 1;
|
|
@@ -2488,7 +3031,7 @@ var VioCart = class extends LitElement {
|
|
|
2488
3031
|
background: #ffb3c7;
|
|
2489
3032
|
color: #0a0a0a;
|
|
2490
3033
|
border: none;
|
|
2491
|
-
border-radius: 8px;
|
|
3034
|
+
border-radius: var(--vio-radius-lg, 8px);
|
|
2492
3035
|
font-size: 16px;
|
|
2493
3036
|
font-weight: 600;
|
|
2494
3037
|
cursor: pointer;
|
|
@@ -2509,7 +3052,7 @@ var VioCart = class extends LitElement {
|
|
|
2509
3052
|
background: #ff5b24;
|
|
2510
3053
|
color: #fff;
|
|
2511
3054
|
border: none;
|
|
2512
|
-
border-radius: 8px;
|
|
3055
|
+
border-radius: var(--vio-radius-lg, 8px);
|
|
2513
3056
|
cursor: pointer;
|
|
2514
3057
|
display: flex;
|
|
2515
3058
|
align-items: center;
|
|
@@ -2533,7 +3076,7 @@ var VioCart = class extends LitElement {
|
|
|
2533
3076
|
background: #635bff;
|
|
2534
3077
|
color: #fff;
|
|
2535
3078
|
border: none;
|
|
2536
|
-
border-radius: 8px;
|
|
3079
|
+
border-radius: var(--vio-radius-lg, 8px);
|
|
2537
3080
|
font-size: 16px;
|
|
2538
3081
|
font-weight: 600;
|
|
2539
3082
|
cursor: pointer;
|
|
@@ -2671,6 +3214,7 @@ var VioCart = class extends LitElement {
|
|
|
2671
3214
|
return isMethodEnabled(this.availableMethods, ...names);
|
|
2672
3215
|
}
|
|
2673
3216
|
updated(changed) {
|
|
3217
|
+
if (changed.has("open")) this.dialogFocus.update(this.open);
|
|
2674
3218
|
if (changed.has("open") && this.open) {
|
|
2675
3219
|
this.confirmedOrder = null;
|
|
2676
3220
|
this.preparedTotal = -1;
|
|
@@ -2795,6 +3339,7 @@ var VioCart = class extends LitElement {
|
|
|
2795
3339
|
async startVippsExpressOrCheckout(sponsorId) {
|
|
2796
3340
|
let fallback = true;
|
|
2797
3341
|
try {
|
|
3342
|
+
await Vio.cart.waitForCartMutations(sponsorId);
|
|
2798
3343
|
if (!await Vio.checkout.getVippsExpressEnabled(sponsorId)) throw new Error("express off");
|
|
2799
3344
|
const res = await Vio.checkout.startVippsExpress(sponsorId);
|
|
2800
3345
|
fallback = !res?.payment_url;
|
|
@@ -2836,11 +3381,14 @@ var VioCart = class extends LitElement {
|
|
|
2836
3381
|
for (const cart of this.carts.values()) return cart.currency;
|
|
2837
3382
|
return "NOK";
|
|
2838
3383
|
}
|
|
3384
|
+
// A delta on the manager's live quantity, not a target computed from this
|
|
3385
|
+
// render's snapshot: two clicks before Lit re-renders used to ask for the
|
|
3386
|
+
// same quantity twice (Alan, 2026-10-15).
|
|
2839
3387
|
onIncrement(item) {
|
|
2840
|
-
Vio.cart.
|
|
3388
|
+
Vio.cart.adjustQuantity(item.id, item.sponsorId, 1);
|
|
2841
3389
|
}
|
|
2842
3390
|
onDecrement(item) {
|
|
2843
|
-
Vio.cart.
|
|
3391
|
+
Vio.cart.adjustQuantity(item.id, item.sponsorId, -1);
|
|
2844
3392
|
}
|
|
2845
3393
|
onRemove(item) {
|
|
2846
3394
|
Vio.cart.removeItem(item.id, item.sponsorId);
|
|
@@ -2850,7 +3398,7 @@ var VioCart = class extends LitElement {
|
|
|
2850
3398
|
const sponsorCarts = [...this.carts.values()].filter((c) => c.items.length > 0);
|
|
2851
3399
|
return html`
|
|
2852
3400
|
<div class="backdrop" @click=${this.close}></div>
|
|
2853
|
-
<aside class="drawer" role="dialog" aria-label=${this.heading}>
|
|
3401
|
+
<aside class="drawer" role="dialog" aria-modal="true" aria-label=${this.heading} tabindex="-1">
|
|
2854
3402
|
<div class="handle" @click=${this.close} aria-hidden="true"></div>
|
|
2855
3403
|
<div class="header">
|
|
2856
3404
|
<h2 class="heading">${this.confirmedOrder ? "Takk!" : this.heading}</h2>
|
|
@@ -3044,6 +3592,38 @@ __decorateClass([
|
|
|
3044
3592
|
__decorateClass([
|
|
3045
3593
|
state()
|
|
3046
3594
|
], VioCart.prototype, "confirmedOrder", 2);
|
|
3595
|
+
|
|
3596
|
+
// src/ui/checkout-errors.ts
|
|
3597
|
+
var EMPTY_CART_MESSAGE = "Handlekurven er tom. Legg til et produkt og pr\xF8v igjen.";
|
|
3598
|
+
var CART_CHANGED_MESSAGE = "Handlekurven ble endret i en annen fane. Last siden p\xE5 nytt og pr\xF8v igjen.";
|
|
3599
|
+
var CART_PAID_MESSAGE = "Denne handlekurven er allerede betalt. Start en ny handlekurv.";
|
|
3600
|
+
var PAYMENT_START_FAILED_MESSAGE = "Betalingen kunne ikke startes. Pr\xF8v igjen, eller velg en annen betalingsm\xE5te.";
|
|
3601
|
+
var SHIPS_APART = "kan ikke sendes sammen";
|
|
3602
|
+
function friendlyPaymentError(raw, method) {
|
|
3603
|
+
const text = String(raw ?? "");
|
|
3604
|
+
const lower = text.toLowerCase();
|
|
3605
|
+
if (lower.includes("got 0") || lower.includes("amount must be a positive")) {
|
|
3606
|
+
return EMPTY_CART_MESSAGE;
|
|
3607
|
+
}
|
|
3608
|
+
if (lower.includes("replaced by a newer") || text.includes("CHECKOUT_SUPERSEDED")) {
|
|
3609
|
+
return CART_CHANGED_MESSAGE;
|
|
3610
|
+
}
|
|
3611
|
+
if (lower.includes("already been paid") || text.includes("CART_ALREADY_PAID")) {
|
|
3612
|
+
return CART_PAID_MESSAGE;
|
|
3613
|
+
}
|
|
3614
|
+
if (text.includes(SHIPS_APART)) return norwegianSentence(text);
|
|
3615
|
+
return PAYMENT_START_FAILED_MESSAGE;
|
|
3616
|
+
}
|
|
3617
|
+
function norwegianSentence(text) {
|
|
3618
|
+
const at = text.indexOf(SHIPS_APART);
|
|
3619
|
+
const prefixEnd = text.lastIndexOf(": ", at);
|
|
3620
|
+
return (prefixEnd === -1 ? text : text.slice(prefixEnd + 2)).trim();
|
|
3621
|
+
}
|
|
3622
|
+
function errorText(err) {
|
|
3623
|
+
return err instanceof Error ? err.message : String(err);
|
|
3624
|
+
}
|
|
3625
|
+
|
|
3626
|
+
// src/ui/components/vio-checkout.ts
|
|
3047
3627
|
var STRIPE_LOGO_SRC3 = "data:image/svg+xml;utf8," + encodeURIComponent(
|
|
3048
3628
|
'<svg width="360" height="150" viewBox="0 0 360 150" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M360 77.4001C360 51.8001 347.6 31.6001 323.9 31.6001C300.1 31.6001 285.7 51.8001 285.7 77.2001C285.7 107.3 302.7 122.5 327.1 122.5C339 122.5 348 119.8 354.8 116V96.0001C348 99.4001 340.2 101.5 330.3 101.5C320.6 101.5 312 98.1001 310.9 86.3001H359.8C359.8 85.0001 360 79.8001 360 77.4001ZM310.6 67.9001C310.6 56.6001 317.5 51.9001 323.8 51.9001C329.9 51.9001 336.4 56.6001 336.4 67.9001H310.6Z" fill="#533AFD"/><path fill-rule="evenodd" clip-rule="evenodd" d="M247.1 31.6001C237.3 31.6001 231 36.2001 227.5 39.4001L226.2 33.2001H204.2V149.8L229.2 144.5L229.3 116.2C232.9 118.8 238.2 122.5 247 122.5C264.9 122.5 281.2 108.1 281.2 76.4001C281.1 47.4001 264.6 31.6001 247.1 31.6001ZM241.1 100.5C235.2 100.5 231.7 98.4001 229.3 95.8001L229.2 58.7001C231.8 55.8001 235.4 53.8001 241.1 53.8001C250.2 53.8001 256.5 64.0001 256.5 77.1001C256.5 90.5001 250.3 100.5 241.1 100.5Z" fill="#533AFD"/><path fill-rule="evenodd" clip-rule="evenodd" d="M169.8 25.7L194.9 20.3V0L169.8 5.3V25.7Z" fill="#533AFD"/><path d="M194.9 33.3H169.8V120.8H194.9V33.3Z" fill="#533AFD"/><path fill-rule="evenodd" clip-rule="evenodd" d="M142.9 40.7L141.3 33.3H119.7V120.8H144.7V61.5C150.6 53.8 160.6 55.2 163.7 56.3V33.3C160.5 32.1 148.8 29.9 142.9 40.7Z" fill="#533AFD"/><path fill-rule="evenodd" clip-rule="evenodd" d="M92.8999 11.6001L68.4999 16.8001L68.3999 96.9001C68.3999 111.7 79.4999 122.6 94.2999 122.6C102.5 122.6 108.5 121.1 111.8 119.3V99.0001C108.6 100.3 92.7999 104.9 92.7999 90.1001V54.6001H111.8V33.3001H92.7999L92.8999 11.6001Z" fill="#533AFD"/><path fill-rule="evenodd" clip-rule="evenodd" d="M25.3 58.7001C25.3 54.8001 28.5 53.3001 33.8 53.3001C41.4 53.3001 51 55.6001 58.6 59.7001V36.2001C50.3 32.9001 42.1 31.6001 33.8 31.6001C13.5 31.6001 0 42.2001 0 59.9001C0 87.5001 38 83.1001 38 95.0001C38 99.6001 34 101.1 28.4 101.1C20.1 101.1 9.5 97.7001 1.1 93.1001V116.9C10.4 120.9 19.8 122.6 28.4 122.6C49.2 122.6 63.5 112.3 63.5 94.4001C63.4 64.6001 25.3 69.9001 25.3 58.7001Z" fill="#533AFD"/></svg>'
|
|
3049
3629
|
);
|
|
@@ -3155,6 +3735,16 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
3155
3735
|
};
|
|
3156
3736
|
/** In-flight guard for loadAvailablePaymentMethods. */
|
|
3157
3737
|
this.loadingPaymentMethods = false;
|
|
3738
|
+
/**
|
|
3739
|
+
* Keyboard and focus while open: Escape closes — not while a payment is
|
|
3740
|
+
* in flight — Tab stays inside, focus returns to the opener.
|
|
3741
|
+
*/
|
|
3742
|
+
this.dialogFocus = new DialogFocus({
|
|
3743
|
+
host: this,
|
|
3744
|
+
container: () => this.renderRoot.querySelector(".modal"),
|
|
3745
|
+
close: () => this.close(),
|
|
3746
|
+
canClose: () => !this.paymentInFlight
|
|
3747
|
+
});
|
|
3158
3748
|
this.paymentMethodsResolved = false;
|
|
3159
3749
|
/** Guards `autoSelectSoleMethod` to ONE attempt per opening — see there. */
|
|
3160
3750
|
this.autoSelectAttempted = false;
|
|
@@ -3233,10 +3823,17 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
3233
3823
|
};
|
|
3234
3824
|
this.boundOnPaymentError = (e) => {
|
|
3235
3825
|
const detail = e.detail;
|
|
3236
|
-
|
|
3826
|
+
if (typeof console !== "undefined") {
|
|
3827
|
+
console.warn("[VioCheckout] payment-error:", detail.method, detail.error);
|
|
3828
|
+
}
|
|
3829
|
+
this.paymentError = detail.method === "klarna" ? `Klarna-betalingen kunne ikke fullf\xF8res: ${detail.error}` : friendlyPaymentError(detail.error, detail.method);
|
|
3237
3830
|
this.open = true;
|
|
3238
3831
|
};
|
|
3239
3832
|
}
|
|
3833
|
+
/** A payment is being started or confirmed right now: the overlay must not close under it. */
|
|
3834
|
+
get paymentInFlight() {
|
|
3835
|
+
return this.stripePaying || this.stripeLoading || this.vippsLoading || this.klarnaAuthorizing || this.applePayInProgress;
|
|
3836
|
+
}
|
|
3240
3837
|
static {
|
|
3241
3838
|
this.styles = css`
|
|
3242
3839
|
:host { display: contents; }
|
|
@@ -3265,6 +3862,8 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
3265
3862
|
overflow-y: auto;
|
|
3266
3863
|
}
|
|
3267
3864
|
:host([open]) .modal { transform: translateY(0); }
|
|
3865
|
+
/* Focus lands on the panel itself only as a fallback — no ring for it. */
|
|
3866
|
+
.modal:focus { outline: none; }
|
|
3268
3867
|
|
|
3269
3868
|
/* Confirmation drawer — compact bottom sheet, not the full-screen overlay. */
|
|
3270
3869
|
.modal.as-drawer {
|
|
@@ -3894,6 +4493,7 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
3894
4493
|
const vippsStatus = await Vio.checkout.getVippsStatus(checkoutId, sponsorId || void 0);
|
|
3895
4494
|
const state6 = String(vippsStatus?.state ?? "").toUpperCase();
|
|
3896
4495
|
sawStatus = true;
|
|
4496
|
+
if (vippsStatus?.duplicate_of) return { outcome: "duplicate", sawStatus };
|
|
3897
4497
|
if (state6 === "AUTHORIZED") return { outcome: "paid", sawStatus };
|
|
3898
4498
|
if (state6 === "CREATED") continue;
|
|
3899
4499
|
return { outcome: "failed", sawStatus };
|
|
@@ -3926,6 +4526,9 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
3926
4526
|
detail: { method: vioMethod, sponsorId, orderId: checkoutId }
|
|
3927
4527
|
})
|
|
3928
4528
|
);
|
|
4529
|
+
} else if (outcome === "duplicate") {
|
|
4530
|
+
this.paymentError = CART_PAID_MESSAGE;
|
|
4531
|
+
this.open = true;
|
|
3929
4532
|
} else if (outcome === "failed") {
|
|
3930
4533
|
this.paymentError = "Betalingen ble avbrutt eller feilet. Vennligst pr\xF8v igjen.";
|
|
3931
4534
|
this.open = true;
|
|
@@ -3996,7 +4599,7 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
3996
4599
|
this.verifyingReturn = null;
|
|
3997
4600
|
if (order?.status === "checkout_complete" && order.html_snippet) {
|
|
3998
4601
|
const spId = this.checkoutState?.sponsorId ?? 0;
|
|
3999
|
-
this.showProviderReceipt("kustom", spId, order);
|
|
4602
|
+
this.showProviderReceipt("kustom", spId, order, kustomReturn.checkoutId);
|
|
4000
4603
|
await this.updateComplete;
|
|
4001
4604
|
const container = this.lightContainer("vio-kustom-checkout-container", "vio-kustom");
|
|
4002
4605
|
Vio.checkout.renderKustomSnippet(container, order.html_snippet);
|
|
@@ -4042,12 +4645,12 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
4042
4645
|
const result = { order: { orderId: order.order_id }, chargedTotal: order.total_price };
|
|
4043
4646
|
this.qliroMountedOrderId = order.order_id;
|
|
4044
4647
|
if (order.html_snippet) {
|
|
4045
|
-
this.showProviderReceipt("qliro", spId, result);
|
|
4648
|
+
this.showProviderReceipt("qliro", spId, result, checkoutId);
|
|
4046
4649
|
await this.updateComplete;
|
|
4047
4650
|
const container = this.lightContainer("vio-qliro-checkout-container", "vio-qliro");
|
|
4048
4651
|
Vio.checkout.renderKustomSnippet(container, order.html_snippet);
|
|
4049
4652
|
} else {
|
|
4050
|
-
this.confirmOrder("qliro", spId, result);
|
|
4653
|
+
this.confirmOrder("qliro", spId, result, checkoutId);
|
|
4051
4654
|
}
|
|
4052
4655
|
} else if (order?.html_snippet) {
|
|
4053
4656
|
this.qliroMountedOrderId = order.order_id;
|
|
@@ -4098,9 +4701,12 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
4098
4701
|
Vio.checkout.renderKustomSnippet(container, order.html_snippet);
|
|
4099
4702
|
}
|
|
4100
4703
|
if (order.status === "PurchaseCompleted") {
|
|
4101
|
-
this.showProviderReceipt(
|
|
4102
|
-
|
|
4103
|
-
|
|
4704
|
+
this.showProviderReceipt(
|
|
4705
|
+
"walley",
|
|
4706
|
+
this.checkoutState?.sponsorId ?? 0,
|
|
4707
|
+
{ orderId: order.order_id },
|
|
4708
|
+
checkoutId
|
|
4709
|
+
);
|
|
4104
4710
|
}
|
|
4105
4711
|
}
|
|
4106
4712
|
} catch (err) {
|
|
@@ -4196,6 +4802,10 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
4196
4802
|
const checkout = await Vio.checkout.getCheckout(checkoutId, sponsorId || void 0);
|
|
4197
4803
|
const status = String(checkout?.status ?? "").toLowerCase();
|
|
4198
4804
|
sawStatus = true;
|
|
4805
|
+
if (checkout?.duplicate_of) {
|
|
4806
|
+
outcome = "duplicate";
|
|
4807
|
+
break;
|
|
4808
|
+
}
|
|
4199
4809
|
if (_VioCheckout.FAILED_STATUSES.has(status)) {
|
|
4200
4810
|
outcome = "failed";
|
|
4201
4811
|
break;
|
|
@@ -4260,6 +4870,7 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
4260
4870
|
super.disconnectedCallback();
|
|
4261
4871
|
}
|
|
4262
4872
|
updated(changed) {
|
|
4873
|
+
if (changed.has("open")) this.dialogFocus.update(this.open);
|
|
4263
4874
|
if (changed.has("open") && this.open && this.items.length > 0 && this.availableShippingsList.length === 0) {
|
|
4264
4875
|
void this.loadAvailableShippings();
|
|
4265
4876
|
}
|
|
@@ -4518,7 +5129,7 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
4518
5129
|
if (typeof console !== "undefined") {
|
|
4519
5130
|
console.warn("[VioCheckout] Klarna Payments mount failed:", err);
|
|
4520
5131
|
}
|
|
4521
|
-
this.paymentError =
|
|
5132
|
+
this.paymentError = friendlyPaymentError(errorText(err));
|
|
4522
5133
|
} finally {
|
|
4523
5134
|
this.klarnaMounting = false;
|
|
4524
5135
|
}
|
|
@@ -4701,7 +5312,10 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
4701
5312
|
}
|
|
4702
5313
|
this.stripeLoading = true;
|
|
4703
5314
|
void Vio.checkout.startStripePayment(this.checkoutState?.sponsorId, this.form).catch((err) => {
|
|
4704
|
-
|
|
5315
|
+
if (typeof console !== "undefined") {
|
|
5316
|
+
console.warn("[VioCheckout] Stripe payment could not start:", err);
|
|
5317
|
+
}
|
|
5318
|
+
this.paymentError = friendlyPaymentError(errorText(err));
|
|
4705
5319
|
}).finally(() => {
|
|
4706
5320
|
this.stripeLoading = false;
|
|
4707
5321
|
});
|
|
@@ -4709,7 +5323,10 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
4709
5323
|
if (method === "vipps") {
|
|
4710
5324
|
this.vippsLoading = true;
|
|
4711
5325
|
void this.startVippsFromCheckout().catch((err) => {
|
|
4712
|
-
|
|
5326
|
+
if (typeof console !== "undefined") {
|
|
5327
|
+
console.warn("[VioCheckout] Vipps payment could not start:", err);
|
|
5328
|
+
}
|
|
5329
|
+
this.paymentError = friendlyPaymentError(errorText(err));
|
|
4713
5330
|
}).finally(() => {
|
|
4714
5331
|
this.vippsLoading = false;
|
|
4715
5332
|
});
|
|
@@ -4744,11 +5361,11 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
4744
5361
|
* the confirmation screen, emits `vio:payment-success`, and clears the
|
|
4745
5362
|
* paid sponsor's cart so the badge + drawer reflect the purchase.
|
|
4746
5363
|
*/
|
|
4747
|
-
confirmOrder(method, sponsorId, result) {
|
|
5364
|
+
confirmOrder(method, sponsorId, result, returnCheckoutId) {
|
|
4748
5365
|
const r = result ?? {};
|
|
4749
5366
|
const shippingPrice = this.shippingMajor();
|
|
4750
5367
|
const email = String(this.form.email ?? "").trim();
|
|
4751
|
-
const checkoutId = this.checkoutState?.checkoutId;
|
|
5368
|
+
const checkoutId = returnCheckoutId || this.checkoutState?.checkoutId;
|
|
4752
5369
|
const providerOrderId = r.order?.orderId != null ? String(r.order.orderId) : "";
|
|
4753
5370
|
this.confirmedOrder = {
|
|
4754
5371
|
items: [...this.items],
|
|
@@ -4765,7 +5382,10 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
4765
5382
|
new CustomEvent("vio:payment-success", {
|
|
4766
5383
|
bubbles: true,
|
|
4767
5384
|
composed: true,
|
|
4768
|
-
|
|
5385
|
+
// `checkoutId` is Vio's own reference for the purchase — what the
|
|
5386
|
+
// redirect path already sends as `orderId`; analytics falls back to it
|
|
5387
|
+
// when the provider's result carries no order id.
|
|
5388
|
+
detail: { method, sponsorId, result, checkoutId }
|
|
4769
5389
|
})
|
|
4770
5390
|
);
|
|
4771
5391
|
Vio.cart.clearSponsorCart(sponsorId);
|
|
@@ -4877,7 +5497,9 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
4877
5497
|
<div
|
|
4878
5498
|
class="modal ${this.express ? "as-side" : this.orderConfirmed ? "as-confirm" : ""}"
|
|
4879
5499
|
role="dialog"
|
|
5500
|
+
aria-modal="true"
|
|
4880
5501
|
aria-label=${this.heading}
|
|
5502
|
+
tabindex="-1"
|
|
4881
5503
|
>
|
|
4882
5504
|
<div class="handle" @click=${this.close} aria-hidden="true"></div>
|
|
4883
5505
|
<div class="topbar">
|
|
@@ -4913,15 +5535,16 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
4913
5535
|
* sponsor's cart cleared — but the screen is the provider's receipt, not our
|
|
4914
5536
|
* "Takk!": two receipts for one purchase is one too many.
|
|
4915
5537
|
*/
|
|
4916
|
-
showProviderReceipt(method, sponsorId, result) {
|
|
5538
|
+
showProviderReceipt(method, sponsorId, result, returnCheckoutId) {
|
|
4917
5539
|
this.providerReceipt = method;
|
|
4918
5540
|
this.confirmedMethod = method;
|
|
4919
5541
|
this.open = true;
|
|
5542
|
+
const checkoutId = returnCheckoutId || this.checkoutState?.checkoutId;
|
|
4920
5543
|
this.dispatchEvent(
|
|
4921
5544
|
new CustomEvent("vio:payment-success", {
|
|
4922
5545
|
bubbles: true,
|
|
4923
5546
|
composed: true,
|
|
4924
|
-
detail: { method, sponsorId, result }
|
|
5547
|
+
detail: { method, sponsorId, result, checkoutId }
|
|
4925
5548
|
})
|
|
4926
5549
|
);
|
|
4927
5550
|
if (sponsorId) {
|
|
@@ -5090,7 +5713,7 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
5090
5713
|
if (typeof console !== "undefined") {
|
|
5091
5714
|
console.warn("[VioCheckout] Kustom mount failed:", err);
|
|
5092
5715
|
}
|
|
5093
|
-
this.paymentError =
|
|
5716
|
+
this.paymentError = friendlyPaymentError(errorText(err));
|
|
5094
5717
|
Vio.checkout.selectPaymentMethod("");
|
|
5095
5718
|
} finally {
|
|
5096
5719
|
this.kustomMounting = false;
|
|
@@ -5210,7 +5833,7 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
5210
5833
|
if (typeof console !== "undefined") {
|
|
5211
5834
|
console.warn("[VioCheckout] Qliro mount failed:", err);
|
|
5212
5835
|
}
|
|
5213
|
-
this.paymentError =
|
|
5836
|
+
this.paymentError = friendlyPaymentError(errorText(err));
|
|
5214
5837
|
Vio.checkout.selectPaymentMethod("");
|
|
5215
5838
|
} finally {
|
|
5216
5839
|
this.qliroMounting = false;
|
|
@@ -5267,7 +5890,7 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
5267
5890
|
if (typeof console !== "undefined") {
|
|
5268
5891
|
console.warn("[VioCheckout] Walley mount failed:", err);
|
|
5269
5892
|
}
|
|
5270
|
-
this.paymentError =
|
|
5893
|
+
this.paymentError = friendlyPaymentError(errorText(err));
|
|
5271
5894
|
Vio.checkout.selectPaymentMethod("");
|
|
5272
5895
|
} finally {
|
|
5273
5896
|
this.walleyMounting = false;
|
|
@@ -5342,7 +5965,7 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
5342
5965
|
if (typeof console !== "undefined") {
|
|
5343
5966
|
console.warn("[VioCheckout] Nexi mount failed:", err);
|
|
5344
5967
|
}
|
|
5345
|
-
this.paymentError =
|
|
5968
|
+
this.paymentError = friendlyPaymentError(errorText(err));
|
|
5346
5969
|
Vio.checkout.selectPaymentMethod("");
|
|
5347
5970
|
} finally {
|
|
5348
5971
|
this.nexiMounting = false;
|
|
@@ -5529,7 +6152,7 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
5529
6152
|
this.adyenMountedKey = this.embedPurchaseKey();
|
|
5530
6153
|
} catch (err) {
|
|
5531
6154
|
this.unmountAdyen();
|
|
5532
|
-
this.paymentError = err instanceof AdyenOriginError ? "Betaling er ikke aktivert for denne siden enn\xE5. Ta kontakt med butikken." :
|
|
6155
|
+
this.paymentError = err instanceof AdyenOriginError ? "Betaling er ikke aktivert for denne siden enn\xE5. Ta kontakt med butikken." : friendlyPaymentError(errorText(err));
|
|
5533
6156
|
if (typeof console !== "undefined") console.warn("[VioCheckout] Adyen mount failed:", err);
|
|
5534
6157
|
} finally {
|
|
5535
6158
|
this.adyenMounting = false;
|
|
@@ -5639,7 +6262,10 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
5639
6262
|
},
|
|
5640
6263
|
onLoadError: (message) => {
|
|
5641
6264
|
this.unmountStripe();
|
|
5642
|
-
|
|
6265
|
+
if (typeof console !== "undefined") {
|
|
6266
|
+
console.warn("[VioCheckout] Stripe Payment Element failed to load:", message);
|
|
6267
|
+
}
|
|
6268
|
+
this.paymentError = friendlyPaymentError(message, "stripe");
|
|
5643
6269
|
}
|
|
5644
6270
|
});
|
|
5645
6271
|
if (this.embedSession() !== mountedFor || this.checkoutState?.paymentMethod !== "stripe" || !this.open) {
|
|
@@ -5658,7 +6284,7 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
5658
6284
|
this.stripeMountedKey = this.embedPurchaseKey();
|
|
5659
6285
|
} catch (err) {
|
|
5660
6286
|
this.unmountStripe();
|
|
5661
|
-
this.paymentError =
|
|
6287
|
+
this.paymentError = friendlyPaymentError(errorText(err));
|
|
5662
6288
|
if (typeof console !== "undefined") console.warn("[VioCheckout] Stripe mount failed:", err);
|
|
5663
6289
|
} finally {
|
|
5664
6290
|
this.stripeMounting = false;
|
|
@@ -5687,6 +6313,9 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
5687
6313
|
const settled = checkoutId ? await this.waitForStripeOrder(checkoutId, spId) : "pending";
|
|
5688
6314
|
if (settled === "paid") {
|
|
5689
6315
|
this.confirmOrder("stripe", spId, { chargedTotal: charged });
|
|
6316
|
+
} else if (settled === "duplicate") {
|
|
6317
|
+
this.stripeSettled = false;
|
|
6318
|
+
this.paymentError = CART_PAID_MESSAGE;
|
|
5690
6319
|
} else if (settled === "failed") {
|
|
5691
6320
|
this.stripeSettled = false;
|
|
5692
6321
|
this.paymentError = "Betalingen ble avbrutt eller feilet. Vennligst pr\xF8v igjen.";
|
|
@@ -5712,6 +6341,7 @@ var _VioCheckout = class _VioCheckout extends LitElement {
|
|
|
5712
6341
|
try {
|
|
5713
6342
|
const checkout = await Vio.checkout.getCheckout(checkoutId, sponsorId || void 0);
|
|
5714
6343
|
const status = String(checkout?.status ?? "").toLowerCase();
|
|
6344
|
+
if (checkout?.duplicate_of) return "duplicate";
|
|
5715
6345
|
if (_VioCheckout.FAILED_STATUSES.has(status)) return "failed";
|
|
5716
6346
|
if (_VioCheckout.PAID_STATUSES.has(status)) return "paid";
|
|
5717
6347
|
} catch (err) {
|
|
@@ -6377,5 +7007,5 @@ function registerVioElements() {
|
|
|
6377
7007
|
}
|
|
6378
7008
|
|
|
6379
7009
|
export { VioCart, VioCheckout, VioProduct, VioProductCarousel, VioProductDetail, VioTokens, applyVioTheme, registerVioElements };
|
|
6380
|
-
//# sourceMappingURL=chunk-
|
|
6381
|
-
//# sourceMappingURL=chunk-
|
|
7010
|
+
//# sourceMappingURL=chunk-COCHGJ2K.js.map
|
|
7011
|
+
//# sourceMappingURL=chunk-COCHGJ2K.js.map
|