@vio-live/web-sdk 0.21.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +177 -0
- package/dist/chunk-45YKS5UG.js +3 -0
- package/dist/{chunk-PZUOMOLF.js.map → chunk-45YKS5UG.js.map} +1 -1
- package/dist/{chunk-WTVGVDZX.js → chunk-BRJMT7P4.js} +1053 -80
- package/dist/chunk-BRJMT7P4.js.map +1 -0
- package/dist/{chunk-MRJR6DG5.js → chunk-UVDOKFGD.js} +403 -34
- package/dist/chunk-UVDOKFGD.js.map +1 -0
- package/dist/core/index.d.ts +431 -16
- 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-Dkbfcd1L.d.ts → tokens-DDfZuHF6.d.ts} +108 -0
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-MRJR6DG5.js.map +0 -1
- package/dist/chunk-PZUOMOLF.js +0 -3
- package/dist/chunk-WTVGVDZX.js.map +0 -1
|
@@ -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, normalizeSlotId, autoSlotDisplayName, autoSlotId, normalizeScope } from './chunk-BRJMT7P4.js';
|
|
2
2
|
import { LitElement, css, html } from 'lit';
|
|
3
3
|
import { property, state, query } from 'lit/decorators.js';
|
|
4
4
|
|
|
@@ -165,6 +165,141 @@ 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 liveAutoIds = (() => {
|
|
175
|
+
const g = globalThis;
|
|
176
|
+
return g.__VIO_LIVE_SLOT_IDS__ ?? (g.__VIO_LIVE_SLOT_IDS__ = /* @__PURE__ */ new Map());
|
|
177
|
+
})();
|
|
178
|
+
var SlotRegistration = class {
|
|
179
|
+
constructor(host, kind) {
|
|
180
|
+
this.host = host;
|
|
181
|
+
this.kind = kind;
|
|
182
|
+
this.identity = null;
|
|
183
|
+
this.release = null;
|
|
184
|
+
/** The auto identity held (its number is kept while the element lives). */
|
|
185
|
+
this.auto = null;
|
|
186
|
+
/** Set by connect(); the scope label is read then too. */
|
|
187
|
+
this.connected = false;
|
|
188
|
+
this.scope = "";
|
|
189
|
+
this.scopeName = "";
|
|
190
|
+
}
|
|
191
|
+
/** The effective slot id ('' before connect / after disconnect). */
|
|
192
|
+
get effective() {
|
|
193
|
+
return this.identity?.id ?? "";
|
|
194
|
+
}
|
|
195
|
+
/** On connect: read the scope from the DOM, then register. */
|
|
196
|
+
connect(attrs) {
|
|
197
|
+
this.connected = true;
|
|
198
|
+
this.scope = scopeOf(this.host, attrs);
|
|
199
|
+
this.scopeName = scopeNameOf(this.host, attrs);
|
|
200
|
+
this.sync(attrs);
|
|
201
|
+
}
|
|
202
|
+
/** (Re)register when the attributes changed the identity. */
|
|
203
|
+
sync(attrs) {
|
|
204
|
+
if (!this.connected) return;
|
|
205
|
+
const scope = scopeOf(this.host, attrs);
|
|
206
|
+
if (scope !== this.scope) {
|
|
207
|
+
this.scope = scope;
|
|
208
|
+
this.scopeName = scopeNameOf(this.host, attrs);
|
|
209
|
+
this.releaseAuto();
|
|
210
|
+
} else {
|
|
211
|
+
const scopeName = scopeNameOf(this.host, attrs);
|
|
212
|
+
if (scopeName !== this.scopeName) this.scopeName = scopeName;
|
|
213
|
+
}
|
|
214
|
+
const next = this.resolve(attrs);
|
|
215
|
+
if (sameIdentity(next, this.identity)) return;
|
|
216
|
+
this.unregister();
|
|
217
|
+
if (!next) return;
|
|
218
|
+
try {
|
|
219
|
+
this.release = typeof Vio.registerSlot === "function" ? Vio.registerSlot(next.id, next.displayName) : null;
|
|
220
|
+
this.identity = next;
|
|
221
|
+
} catch {
|
|
222
|
+
this.release = null;
|
|
223
|
+
this.identity = null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/** What the identity is for these attributes (null before connect without a host id). */
|
|
227
|
+
resolve(attrs) {
|
|
228
|
+
const explicit = normalizeSlotId(attrs.vioSlot);
|
|
229
|
+
const name = typeof attrs.vioSlotName === "string" && attrs.vioSlotName.trim() ? attrs.vioSlotName.trim() : void 0;
|
|
230
|
+
if (explicit) {
|
|
231
|
+
this.releaseAuto();
|
|
232
|
+
return { id: explicit, displayName: name };
|
|
233
|
+
}
|
|
234
|
+
if (!this.connected) return null;
|
|
235
|
+
const auto = this.claimAuto();
|
|
236
|
+
return { id: auto.id, displayName: name ?? autoSlotDisplayName(this.kind, auto.number - 1, this.scopeName) };
|
|
237
|
+
}
|
|
238
|
+
/** Unregister whatever is registered and free the auto number. */
|
|
239
|
+
clear() {
|
|
240
|
+
this.unregister();
|
|
241
|
+
this.releaseAuto();
|
|
242
|
+
this.connected = false;
|
|
243
|
+
}
|
|
244
|
+
unregister() {
|
|
245
|
+
try {
|
|
246
|
+
this.release?.();
|
|
247
|
+
} catch {
|
|
248
|
+
}
|
|
249
|
+
this.release = null;
|
|
250
|
+
this.identity = null;
|
|
251
|
+
}
|
|
252
|
+
/** The element's auto id: the one it already holds, else the first free number. */
|
|
253
|
+
claimAuto() {
|
|
254
|
+
if (this.auto && this.auto.scope === this.scope) return this.auto;
|
|
255
|
+
this.releaseAuto();
|
|
256
|
+
for (let number = 1; ; number++) {
|
|
257
|
+
const id = autoSlotId(this.kind, number - 1, this.scope);
|
|
258
|
+
const holders = liveAutoIds.get(id);
|
|
259
|
+
if (!holders || holders.size === 0 || holders.size === 1 && holders.has(this.host)) {
|
|
260
|
+
const set = holders ?? /* @__PURE__ */ new Set();
|
|
261
|
+
set.add(this.host);
|
|
262
|
+
liveAutoIds.set(id, set);
|
|
263
|
+
this.auto = { id, scope: this.scope, number };
|
|
264
|
+
return this.auto;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
releaseAuto() {
|
|
269
|
+
if (!this.auto) return;
|
|
270
|
+
const holders = liveAutoIds.get(this.auto.id);
|
|
271
|
+
holders?.delete(this.host);
|
|
272
|
+
if (holders && holders.size === 0) liveAutoIds.delete(this.auto.id);
|
|
273
|
+
this.auto = null;
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
function sameIdentity(a, b) {
|
|
277
|
+
if (!a || !b) return a === b;
|
|
278
|
+
return a.id === b.id && a.displayName === b.displayName;
|
|
279
|
+
}
|
|
280
|
+
function scopeOf(el, attrs) {
|
|
281
|
+
const own = attrs && typeof attrs.vioScope === "string" ? attrs.vioScope : typeof el.vioScope === "string" ? el.vioScope ?? "" : el.getAttribute(SCOPE_ATTR) ?? "";
|
|
282
|
+
return normalizeScope(own) || normalizeScope(closestAcrossShadow(el, `[${SCOPE_DATA_ATTR}]`)?.getAttribute(SCOPE_DATA_ATTR));
|
|
283
|
+
}
|
|
284
|
+
function scopeNameOf(el, attrs) {
|
|
285
|
+
const own = attrs && typeof attrs.vioScopeName === "string" ? attrs.vioScopeName : typeof el.vioScopeName === "string" ? el.vioScopeName ?? "" : el.getAttribute(SCOPE_NAME_ATTR) ?? "";
|
|
286
|
+
return own.trim() || (closestAcrossShadow(el, `[${SCOPE_NAME_DATA_ATTR}]`)?.getAttribute(SCOPE_NAME_DATA_ATTR) ?? "").trim();
|
|
287
|
+
}
|
|
288
|
+
function closestAcrossShadow(start, selector) {
|
|
289
|
+
try {
|
|
290
|
+
let node = start;
|
|
291
|
+
while (node) {
|
|
292
|
+
const found = typeof node.closest === "function" ? node.closest(selector) : null;
|
|
293
|
+
if (found) return found;
|
|
294
|
+
const root = node.getRootNode();
|
|
295
|
+
node = root && root !== node && typeof root.host === "object" ? root.host ?? null : null;
|
|
296
|
+
}
|
|
297
|
+
} catch {
|
|
298
|
+
}
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/ui/components/vio-product.ts
|
|
168
303
|
var VioProduct = class extends LitElement {
|
|
169
304
|
constructor() {
|
|
170
305
|
super(...arguments);
|
|
@@ -175,6 +310,13 @@ var VioProduct = class extends LitElement {
|
|
|
175
310
|
this.imageUrl = "";
|
|
176
311
|
this.productId = "";
|
|
177
312
|
this.sponsorId = "";
|
|
313
|
+
this.vioSlot = "";
|
|
314
|
+
this.vioSlotName = "";
|
|
315
|
+
this.vioScope = "";
|
|
316
|
+
this.vioScopeName = "";
|
|
317
|
+
this.vioContentId = "";
|
|
318
|
+
this.vioContentTitle = "";
|
|
319
|
+
this.slotRegistration = new SlotRegistration(this, "card");
|
|
178
320
|
this.hasVariants = false;
|
|
179
321
|
this.variants = void 0;
|
|
180
322
|
this.options = void 0;
|
|
@@ -184,6 +326,36 @@ var VioProduct = class extends LitElement {
|
|
|
184
326
|
this.justAdded = false;
|
|
185
327
|
this.boundCartChange = () => this.updateInCart();
|
|
186
328
|
}
|
|
329
|
+
/** The slot id in effect: `vio-slot`, else the auto id (`card-N`). Read-only. */
|
|
330
|
+
get effectiveSlot() {
|
|
331
|
+
return this.slotRegistration.effective;
|
|
332
|
+
}
|
|
333
|
+
/** The slot campaign's sponsor as the string the attributes use ('' when unknown). */
|
|
334
|
+
sponsorFromAttribution() {
|
|
335
|
+
const id = typeof Vio.attribution === "function" ? Vio.attribution(this.effectiveSlot).sponsorId : void 0;
|
|
336
|
+
return id !== void 0 ? String(id) : "";
|
|
337
|
+
}
|
|
338
|
+
slotAttributes() {
|
|
339
|
+
return {
|
|
340
|
+
vioSlot: this.vioSlot,
|
|
341
|
+
vioSlotName: this.vioSlotName,
|
|
342
|
+
vioScope: this.vioScope,
|
|
343
|
+
vioScopeName: this.vioScopeName
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* What every event carries for attribution (`Vio.attribution`): the place,
|
|
348
|
+
* its campaign and sponsor, and the content — overridden here when the host
|
|
349
|
+
* set `vio-content-id` / `vio-content-title` on this card.
|
|
350
|
+
*/
|
|
351
|
+
attribution() {
|
|
352
|
+
const base = typeof Vio.attribution === "function" ? Vio.attribution(this.effectiveSlot, { sponsorId: this.sponsorId || void 0 }) : { slot: this.effectiveSlot || void 0 };
|
|
353
|
+
return {
|
|
354
|
+
...base,
|
|
355
|
+
...this.vioContentId ? { contentId: this.vioContentId } : {},
|
|
356
|
+
...this.vioContentTitle ? { contentTitle: this.vioContentTitle } : {}
|
|
357
|
+
};
|
|
358
|
+
}
|
|
187
359
|
static {
|
|
188
360
|
this.styles = css`
|
|
189
361
|
:host {
|
|
@@ -297,14 +469,19 @@ var VioProduct = class extends LitElement {
|
|
|
297
469
|
connectedCallback() {
|
|
298
470
|
super.connectedCallback();
|
|
299
471
|
this.updateInCart();
|
|
472
|
+
this.slotRegistration.connect(this.slotAttributes());
|
|
300
473
|
Vio.cart.addEventListener("change", this.boundCartChange);
|
|
301
474
|
}
|
|
302
475
|
disconnectedCallback() {
|
|
303
476
|
Vio.cart.removeEventListener("change", this.boundCartChange);
|
|
477
|
+
this.slotRegistration.clear();
|
|
304
478
|
super.disconnectedCallback();
|
|
305
479
|
}
|
|
306
480
|
updated(changed) {
|
|
307
481
|
if (changed.has("productId") || changed.has("sponsorId")) this.updateInCart();
|
|
482
|
+
if (changed.has("vioSlot") || changed.has("vioSlotName") || changed.has("vioScope") || changed.has("vioScopeName")) {
|
|
483
|
+
this.slotRegistration.sync(this.slotAttributes());
|
|
484
|
+
}
|
|
308
485
|
}
|
|
309
486
|
/** Reflect whether (and how many units of) this product sit in the cart. */
|
|
310
487
|
updateInCart() {
|
|
@@ -330,12 +507,14 @@ var VioProduct = class extends LitElement {
|
|
|
330
507
|
composed: true,
|
|
331
508
|
detail: {
|
|
332
509
|
productId: this.productId,
|
|
333
|
-
sponsorId: this.sponsorId,
|
|
334
510
|
brand: this.brand,
|
|
335
511
|
name: this.name,
|
|
336
512
|
price: this.price,
|
|
337
513
|
retailer: this.retailer,
|
|
338
|
-
imageUrl: this.imageUrl
|
|
514
|
+
imageUrl: this.imageUrl,
|
|
515
|
+
...this.attribution(),
|
|
516
|
+
// The host's sponsor-id wins; without one, the slot's campaign sponsor.
|
|
517
|
+
sponsorId: this.sponsorId || this.sponsorFromAttribution()
|
|
339
518
|
}
|
|
340
519
|
})
|
|
341
520
|
);
|
|
@@ -355,18 +534,28 @@ var VioProduct = class extends LitElement {
|
|
|
355
534
|
this.onClick();
|
|
356
535
|
return;
|
|
357
536
|
}
|
|
537
|
+
const attribution = this.attribution();
|
|
358
538
|
this.dispatchEvent(
|
|
359
539
|
new CustomEvent("vio:product-add", {
|
|
360
540
|
bubbles: true,
|
|
361
541
|
composed: true,
|
|
362
542
|
detail: {
|
|
363
543
|
productId: this.productId,
|
|
364
|
-
sponsorId: this.sponsorId,
|
|
365
544
|
brand: this.brand,
|
|
366
545
|
name: this.name,
|
|
367
546
|
price: this.price,
|
|
368
547
|
retailer: this.retailer,
|
|
369
|
-
imageUrl: this.imageUrl
|
|
548
|
+
imageUrl: this.imageUrl,
|
|
549
|
+
...attribution,
|
|
550
|
+
// The host's sponsor-id wins; without one, the slot's campaign sponsor.
|
|
551
|
+
sponsorId: this.sponsorId || this.sponsorFromAttribution(),
|
|
552
|
+
// The host adds to the cart: pass this through as `origin` so the
|
|
553
|
+
// purchase is attributed to this place and campaign.
|
|
554
|
+
origin: {
|
|
555
|
+
campaignId: attribution.campaignId,
|
|
556
|
+
slot: attribution.slot,
|
|
557
|
+
contentId: attribution.contentId
|
|
558
|
+
}
|
|
370
559
|
}
|
|
371
560
|
})
|
|
372
561
|
);
|
|
@@ -434,6 +623,24 @@ __decorateClass([
|
|
|
434
623
|
__decorateClass([
|
|
435
624
|
property({ type: String, attribute: "sponsor-id" })
|
|
436
625
|
], VioProduct.prototype, "sponsorId", 2);
|
|
626
|
+
__decorateClass([
|
|
627
|
+
property({ type: String, attribute: "vio-slot" })
|
|
628
|
+
], VioProduct.prototype, "vioSlot", 2);
|
|
629
|
+
__decorateClass([
|
|
630
|
+
property({ type: String, attribute: "vio-slot-name" })
|
|
631
|
+
], VioProduct.prototype, "vioSlotName", 2);
|
|
632
|
+
__decorateClass([
|
|
633
|
+
property({ type: String, attribute: "vio-scope" })
|
|
634
|
+
], VioProduct.prototype, "vioScope", 2);
|
|
635
|
+
__decorateClass([
|
|
636
|
+
property({ type: String, attribute: "vio-scope-name" })
|
|
637
|
+
], VioProduct.prototype, "vioScopeName", 2);
|
|
638
|
+
__decorateClass([
|
|
639
|
+
property({ type: String, attribute: "vio-content-id" })
|
|
640
|
+
], VioProduct.prototype, "vioContentId", 2);
|
|
641
|
+
__decorateClass([
|
|
642
|
+
property({ type: String, attribute: "vio-content-title" })
|
|
643
|
+
], VioProduct.prototype, "vioContentTitle", 2);
|
|
437
644
|
__decorateClass([
|
|
438
645
|
property({ type: Boolean, attribute: "has-variants" })
|
|
439
646
|
], VioProduct.prototype, "hasVariants", 2);
|
|
@@ -468,6 +675,13 @@ var VioProductCarousel = class extends LitElement {
|
|
|
468
675
|
this.sponsorId = 0;
|
|
469
676
|
this.productRefs = "";
|
|
470
677
|
this.currency = "NOK";
|
|
678
|
+
this.vioSlot = "";
|
|
679
|
+
this.vioSlotName = "";
|
|
680
|
+
this.vioScope = "";
|
|
681
|
+
this.vioScopeName = "";
|
|
682
|
+
this.vioContentId = "";
|
|
683
|
+
this.vioContentTitle = "";
|
|
684
|
+
this.slotRegistration = new SlotRegistration(this, "carousel");
|
|
471
685
|
this.fetched = null;
|
|
472
686
|
this.isLoading = false;
|
|
473
687
|
this.fetchError = "";
|
|
@@ -479,6 +693,27 @@ var VioProductCarousel = class extends LitElement {
|
|
|
479
693
|
this.impressed = false;
|
|
480
694
|
this.listTracked = false;
|
|
481
695
|
}
|
|
696
|
+
/** The slot id in effect: `vio-slot`, else the auto id (`carousel-N`). Read-only. */
|
|
697
|
+
get effectiveSlot() {
|
|
698
|
+
return this.slotRegistration.effective;
|
|
699
|
+
}
|
|
700
|
+
slotAttributes() {
|
|
701
|
+
return {
|
|
702
|
+
vioSlot: this.vioSlot,
|
|
703
|
+
vioSlotName: this.vioSlotName,
|
|
704
|
+
vioScope: this.vioScope,
|
|
705
|
+
vioScopeName: this.vioScopeName
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
/** Attribution every analytics event of this carousel carries (`Vio.attribution` + the content overrides). */
|
|
709
|
+
attribution() {
|
|
710
|
+
const base = typeof Vio.attribution === "function" ? Vio.attribution(this.effectiveSlot, { sponsorId: this.sponsorId > 0 ? this.sponsorId : void 0 }) : { slot: this.effectiveSlot || void 0 };
|
|
711
|
+
return {
|
|
712
|
+
...base,
|
|
713
|
+
...this.vioContentId ? { contentId: this.vioContentId } : {},
|
|
714
|
+
...this.vioContentTitle ? { contentTitle: this.vioContentTitle } : {}
|
|
715
|
+
};
|
|
716
|
+
}
|
|
482
717
|
static {
|
|
483
718
|
this.styles = css`
|
|
484
719
|
:host {
|
|
@@ -626,6 +861,7 @@ var VioProductCarousel = class extends LitElement {
|
|
|
626
861
|
}
|
|
627
862
|
connectedCallback() {
|
|
628
863
|
super.connectedCallback();
|
|
864
|
+
this.slotRegistration.connect(this.slotAttributes());
|
|
629
865
|
this.observeForAnalytics();
|
|
630
866
|
if (this.productRefs.trim() || this.productIds.trim()) {
|
|
631
867
|
void this.autoFetch();
|
|
@@ -634,14 +870,28 @@ var VioProductCarousel = class extends LitElement {
|
|
|
634
870
|
disconnectedCallback() {
|
|
635
871
|
this.unobserveImpression?.();
|
|
636
872
|
this.unobserveImpression = null;
|
|
873
|
+
this.slotRegistration.clear();
|
|
637
874
|
super.disconnectedCallback();
|
|
638
875
|
}
|
|
639
876
|
updated(changed) {
|
|
640
|
-
|
|
877
|
+
if (changed.has("vioSlot") || changed.has("vioSlotName") || changed.has("vioScope") || changed.has("vioScopeName")) {
|
|
878
|
+
this.slotRegistration.sync(this.slotAttributes());
|
|
879
|
+
}
|
|
880
|
+
const propChanged = changed.has("productRefs") || changed.has("productIds") || changed.has("commerceKey") || changed.has("sponsorId") || changed.has("vioSlot");
|
|
641
881
|
if (propChanged && (this.productRefs.trim() || this.productIds.trim())) {
|
|
642
882
|
void this.autoFetch();
|
|
643
883
|
}
|
|
644
884
|
}
|
|
885
|
+
/**
|
|
886
|
+
* The sponsor of this carousel: the host's `sponsor-id`, else the primary
|
|
887
|
+
* sponsor of the campaign the slot shows (ADR-0034 — "the campaign of the
|
|
888
|
+
* block is its sponsor"), else 0 (unknown).
|
|
889
|
+
*/
|
|
890
|
+
effectiveSponsorId() {
|
|
891
|
+
if (this.sponsorId > 0) return this.sponsorId;
|
|
892
|
+
const fromSlot = typeof Vio.campaignForSlot === "function" ? Number(Vio.campaignForSlot(this.effectiveSlot || null)?.primarySponsor?.id) : NaN;
|
|
893
|
+
return Number.isFinite(fromSlot) && fromSlot > 0 ? fromSlot : 0;
|
|
894
|
+
}
|
|
645
895
|
async autoFetch() {
|
|
646
896
|
if (!Vio.isInitialized) {
|
|
647
897
|
this.fetchError = "Vio not initialized \u2014 call Vio.init({ apiKey }) first";
|
|
@@ -650,7 +900,14 @@ var VioProductCarousel = class extends LitElement {
|
|
|
650
900
|
this.isLoading = true;
|
|
651
901
|
this.fetchError = "";
|
|
652
902
|
try {
|
|
653
|
-
|
|
903
|
+
if (this.productRefs.trim() === "" && !this.commerceKey && this.sponsorId <= 0 && !Vio.bootstrapCache) {
|
|
904
|
+
try {
|
|
905
|
+
await Vio.bootstrap();
|
|
906
|
+
} catch {
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
const sponsorId = this.effectiveSponsorId();
|
|
910
|
+
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
911
|
if (refs.length === 0) {
|
|
655
912
|
this.fetched = [];
|
|
656
913
|
return;
|
|
@@ -662,23 +919,23 @@ var VioProductCarousel = class extends LitElement {
|
|
|
662
919
|
bySponsor.set(r.sponsorId, arr);
|
|
663
920
|
}
|
|
664
921
|
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(
|
|
922
|
+
Array.from(bySponsor.entries()).map(async ([sponsorId2, ids]) => {
|
|
923
|
+
const commerce = this.commerceKey && this.productRefs.trim() === "" ? Vio.commerceWithKey(this.commerceKey) : Vio.commerceFor(sponsorId2);
|
|
667
924
|
const products = await commerce.channel.product.getByIds({
|
|
668
925
|
product_ids: ids,
|
|
669
926
|
currency: this.currency,
|
|
670
927
|
image_size: "large"
|
|
671
928
|
});
|
|
672
|
-
return { sponsorId, products };
|
|
929
|
+
return { sponsorId: sponsorId2, products };
|
|
673
930
|
})
|
|
674
931
|
);
|
|
675
932
|
const lookup = /* @__PURE__ */ new Map();
|
|
676
|
-
for (const { sponsorId, products } of fetched) {
|
|
677
|
-
for (const p of products) lookup.set(`${
|
|
933
|
+
for (const { sponsorId: sponsorId2, products } of fetched) {
|
|
934
|
+
for (const p of products) lookup.set(`${sponsorId2}:${p.id}`, p);
|
|
678
935
|
}
|
|
679
|
-
this.fetched = refs.map(({ sponsorId, productId }) => {
|
|
680
|
-
const product = lookup.get(`${
|
|
681
|
-
return product ? { product, sponsorId } : null;
|
|
936
|
+
this.fetched = refs.map(({ sponsorId: sponsorId2, productId }) => {
|
|
937
|
+
const product = lookup.get(`${sponsorId2}:${productId}`);
|
|
938
|
+
return product ? { product, sponsorId: sponsorId2 } : null;
|
|
682
939
|
}).filter((x) => x !== null);
|
|
683
940
|
this.maybeTrackList();
|
|
684
941
|
} catch (err) {
|
|
@@ -695,10 +952,12 @@ var VioProductCarousel = class extends LitElement {
|
|
|
695
952
|
{
|
|
696
953
|
sponsorId: this.sponsorId > 0 ? this.sponsorId : void 0,
|
|
697
954
|
componentTemplateId: "product-carousel",
|
|
698
|
-
|
|
955
|
+
// The slot is the real location id; the label only stands in for it.
|
|
956
|
+
locationId: this.effectiveSlot || this.label || void 0,
|
|
957
|
+
...this.attribution()
|
|
699
958
|
},
|
|
700
959
|
{
|
|
701
|
-
key: `carousel:${this.label || this.productIds || this.productRefs || "default"}`,
|
|
960
|
+
key: `carousel:${this.effectiveSlot || this.label || this.productIds || this.productRefs || "default"}`,
|
|
702
961
|
onImpression: () => {
|
|
703
962
|
this.impressed = true;
|
|
704
963
|
this.maybeTrackList();
|
|
@@ -716,14 +975,16 @@ var VioProductCarousel = class extends LitElement {
|
|
|
716
975
|
if (entries.length === 0) return;
|
|
717
976
|
this.listTracked = true;
|
|
718
977
|
try {
|
|
978
|
+
const sponsorId = this.effectiveSponsorId();
|
|
719
979
|
Vio.analytics.track("view_item_list", {
|
|
720
980
|
context: {
|
|
721
|
-
sponsorId:
|
|
981
|
+
sponsorId: sponsorId > 0 ? sponsorId : void 0,
|
|
722
982
|
componentTemplateId: "product-carousel",
|
|
723
|
-
locationId: this.label || void 0
|
|
983
|
+
locationId: this.effectiveSlot || this.label || void 0,
|
|
984
|
+
...this.attribution()
|
|
724
985
|
},
|
|
725
986
|
commerce: {
|
|
726
|
-
items: entries.map(({ product, sponsorId }) => ({
|
|
987
|
+
items: entries.map(({ product, sponsorId: sponsorId2 }) => ({
|
|
727
988
|
productId: product.id,
|
|
728
989
|
name: product.title,
|
|
729
990
|
price: Number(product.price?.amount_incl_taxes ?? product.price?.amount) || void 0
|
|
@@ -776,6 +1037,9 @@ var VioProductCarousel = class extends LitElement {
|
|
|
776
1037
|
retailer=${retailer}
|
|
777
1038
|
image-url=${img}
|
|
778
1039
|
?has-variants=${(p.variants?.length ?? 0) > 1}
|
|
1040
|
+
.vioSlot=${this.effectiveSlot}
|
|
1041
|
+
.vioContentId=${this.vioContentId}
|
|
1042
|
+
.vioContentTitle=${this.vioContentTitle}
|
|
779
1043
|
></vio-product>
|
|
780
1044
|
`;
|
|
781
1045
|
})}`;
|
|
@@ -845,6 +1109,24 @@ __decorateClass([
|
|
|
845
1109
|
__decorateClass([
|
|
846
1110
|
property({ type: String })
|
|
847
1111
|
], VioProductCarousel.prototype, "currency", 2);
|
|
1112
|
+
__decorateClass([
|
|
1113
|
+
property({ type: String, attribute: "vio-slot" })
|
|
1114
|
+
], VioProductCarousel.prototype, "vioSlot", 2);
|
|
1115
|
+
__decorateClass([
|
|
1116
|
+
property({ type: String, attribute: "vio-slot-name" })
|
|
1117
|
+
], VioProductCarousel.prototype, "vioSlotName", 2);
|
|
1118
|
+
__decorateClass([
|
|
1119
|
+
property({ type: String, attribute: "vio-scope" })
|
|
1120
|
+
], VioProductCarousel.prototype, "vioScope", 2);
|
|
1121
|
+
__decorateClass([
|
|
1122
|
+
property({ type: String, attribute: "vio-scope-name" })
|
|
1123
|
+
], VioProductCarousel.prototype, "vioScopeName", 2);
|
|
1124
|
+
__decorateClass([
|
|
1125
|
+
property({ type: String, attribute: "vio-content-id" })
|
|
1126
|
+
], VioProductCarousel.prototype, "vioContentId", 2);
|
|
1127
|
+
__decorateClass([
|
|
1128
|
+
property({ type: String, attribute: "vio-content-title" })
|
|
1129
|
+
], VioProductCarousel.prototype, "vioContentTitle", 2);
|
|
848
1130
|
__decorateClass([
|
|
849
1131
|
query(".scroller")
|
|
850
1132
|
], VioProductCarousel.prototype, "scroller", 2);
|
|
@@ -1083,6 +1365,18 @@ var VioProductDetail = class extends LitElement {
|
|
|
1083
1365
|
this.sponsorId = 0;
|
|
1084
1366
|
this.autoOpen = true;
|
|
1085
1367
|
this.currency = "NOK";
|
|
1368
|
+
this.vioSlot = "";
|
|
1369
|
+
this.vioSlotName = "";
|
|
1370
|
+
this.vioScope = "";
|
|
1371
|
+
this.vioScopeName = "";
|
|
1372
|
+
this.vioContentId = "";
|
|
1373
|
+
this.vioContentTitle = "";
|
|
1374
|
+
this.slotRegistration = new SlotRegistration(this, "detail");
|
|
1375
|
+
/** What the click that opened the detail carried ('' when nothing), and for which product. */
|
|
1376
|
+
this.eventSlot = "";
|
|
1377
|
+
this.eventContentId = "";
|
|
1378
|
+
this.eventContentTitle = "";
|
|
1379
|
+
this.eventProductId = "";
|
|
1086
1380
|
this.product = null;
|
|
1087
1381
|
this.sellerName = null;
|
|
1088
1382
|
this.sellerSettled = false;
|
|
@@ -1113,8 +1407,17 @@ var VioProductDetail = class extends LitElement {
|
|
|
1113
1407
|
const ev = e;
|
|
1114
1408
|
const productIdNum = parseInt(ev.detail.productId, 10);
|
|
1115
1409
|
if (Number.isNaN(productIdNum)) return;
|
|
1116
|
-
const
|
|
1117
|
-
|
|
1410
|
+
const slot = typeof ev.detail.slot === "string" ? ev.detail.slot.trim() : "";
|
|
1411
|
+
let sponsorIdNum = parseInt(ev.detail.sponsorId, 10);
|
|
1412
|
+
if (Number.isNaN(sponsorIdNum) || sponsorIdNum <= 0) {
|
|
1413
|
+
const fromSlot = typeof Vio.campaignForSlot === "function" ? Number(Vio.campaignForSlot(slot || this.effectiveSlot || null)?.primarySponsor?.id) : NaN;
|
|
1414
|
+
if (!Number.isFinite(fromSlot) || fromSlot <= 0) return;
|
|
1415
|
+
sponsorIdNum = fromSlot;
|
|
1416
|
+
}
|
|
1417
|
+
this.eventSlot = slot;
|
|
1418
|
+
this.eventProductId = String(ev.detail.productId);
|
|
1419
|
+
this.eventContentId = ev.detail.contentId !== void 0 && ev.detail.contentId !== null ? String(ev.detail.contentId).trim() : "";
|
|
1420
|
+
this.eventContentTitle = typeof ev.detail.contentTitle === "string" ? ev.detail.contentTitle.trim() : "";
|
|
1118
1421
|
this.productId = ev.detail.productId;
|
|
1119
1422
|
this.sponsorId = sponsorIdNum;
|
|
1120
1423
|
this.currency = ev.detail.currency || getGlobalCurrency();
|
|
@@ -1122,6 +1425,37 @@ var VioProductDetail = class extends LitElement {
|
|
|
1122
1425
|
void this.fetchProduct();
|
|
1123
1426
|
};
|
|
1124
1427
|
}
|
|
1428
|
+
/** The card's context applies to the product it opened — not to one the host opened by hand later. */
|
|
1429
|
+
get fromCard() {
|
|
1430
|
+
return this.eventProductId !== "" && this.eventProductId === String(this.productId);
|
|
1431
|
+
}
|
|
1432
|
+
/** The slot id in effect for this element: `vio-slot`, else the auto id (`detail-N`). Read-only. */
|
|
1433
|
+
get effectiveSlot() {
|
|
1434
|
+
return this.slotRegistration.effective;
|
|
1435
|
+
}
|
|
1436
|
+
/** The slot the open product is attributed to: the opening card's, else this element's. */
|
|
1437
|
+
get activeSlot() {
|
|
1438
|
+
return this.fromCard && this.eventSlot || this.effectiveSlot;
|
|
1439
|
+
}
|
|
1440
|
+
slotAttributes() {
|
|
1441
|
+
return {
|
|
1442
|
+
vioSlot: this.vioSlot,
|
|
1443
|
+
vioSlotName: this.vioSlotName,
|
|
1444
|
+
vioScope: this.vioScope,
|
|
1445
|
+
vioScopeName: this.vioScopeName
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
/** Attribution every event of the open product carries (`Vio.attribution` of the opening card's slot, else this element's). */
|
|
1449
|
+
attribution() {
|
|
1450
|
+
const base = typeof Vio.attribution === "function" ? Vio.attribution(this.activeSlot, { sponsorId: this.sponsorId > 0 ? this.sponsorId : void 0 }) : { slot: this.activeSlot || void 0 };
|
|
1451
|
+
const contentId = this.fromCard && this.eventContentId || this.vioContentId;
|
|
1452
|
+
const contentTitle = this.fromCard && this.eventContentTitle || this.vioContentTitle;
|
|
1453
|
+
return {
|
|
1454
|
+
...base,
|
|
1455
|
+
...contentId ? { contentId } : {},
|
|
1456
|
+
...contentTitle ? { contentTitle } : {}
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1125
1459
|
static {
|
|
1126
1460
|
this.styles = css`
|
|
1127
1461
|
:host { display: contents; }
|
|
@@ -1569,15 +1903,20 @@ var VioProductDetail = class extends LitElement {
|
|
|
1569
1903
|
}
|
|
1570
1904
|
connectedCallback() {
|
|
1571
1905
|
super.connectedCallback();
|
|
1906
|
+
this.slotRegistration.connect(this.slotAttributes());
|
|
1572
1907
|
void this.loadVippsButton();
|
|
1573
1908
|
document.addEventListener("vio:product-click", this.boundProductClick);
|
|
1574
1909
|
}
|
|
1575
1910
|
disconnectedCallback() {
|
|
1576
1911
|
document.removeEventListener("vio:product-click", this.boundProductClick);
|
|
1912
|
+
this.slotRegistration.clear();
|
|
1577
1913
|
super.disconnectedCallback();
|
|
1578
1914
|
}
|
|
1579
1915
|
updated(changed) {
|
|
1580
1916
|
if (changed.has("open")) this.dialogFocus.update(this.open);
|
|
1917
|
+
if (changed.has("vioSlot") || changed.has("vioSlotName") || changed.has("vioScope") || changed.has("vioScopeName")) {
|
|
1918
|
+
this.slotRegistration.sync(this.slotAttributes());
|
|
1919
|
+
}
|
|
1581
1920
|
}
|
|
1582
1921
|
show() {
|
|
1583
1922
|
this.open = true;
|
|
@@ -1586,6 +1925,10 @@ var VioProductDetail = class extends LitElement {
|
|
|
1586
1925
|
this.open = false;
|
|
1587
1926
|
setTimeout(() => {
|
|
1588
1927
|
if (!this.open) {
|
|
1928
|
+
this.eventSlot = "";
|
|
1929
|
+
this.eventContentId = "";
|
|
1930
|
+
this.eventContentTitle = "";
|
|
1931
|
+
this.eventProductId = "";
|
|
1589
1932
|
this.product = null;
|
|
1590
1933
|
this.sellerName = null;
|
|
1591
1934
|
this.sellerSettled = false;
|
|
@@ -1644,7 +1987,8 @@ var VioProductDetail = class extends LitElement {
|
|
|
1644
1987
|
Vio.analytics.track("view_item", {
|
|
1645
1988
|
context: {
|
|
1646
1989
|
sponsorId: this.sponsorId > 0 ? Number(this.sponsorId) : void 0,
|
|
1647
|
-
componentTemplateId: "product-detail"
|
|
1990
|
+
componentTemplateId: "product-detail",
|
|
1991
|
+
...this.attribution()
|
|
1648
1992
|
},
|
|
1649
1993
|
commerce: {
|
|
1650
1994
|
items: [
|
|
@@ -1684,7 +2028,7 @@ var VioProductDetail = class extends LitElement {
|
|
|
1684
2028
|
}
|
|
1685
2029
|
/** Which buy buttons to render — backend-configured per sponsor. */
|
|
1686
2030
|
async loadAvailablePaymentMethods() {
|
|
1687
|
-
const spId = this.sponsorId || globalThis.__VIO_SPONSOR_ID__;
|
|
2031
|
+
const spId = this.sponsorId || (typeof Vio.campaignForSlot === "function" ? Number(Vio.campaignForSlot(this.activeSlot || null)?.primarySponsor?.id) : 0) || globalThis.__VIO_SPONSOR_ID__;
|
|
1688
2032
|
if (!spId) return;
|
|
1689
2033
|
try {
|
|
1690
2034
|
const methods = await Vio.checkout.getAvailablePaymentMethods(spId);
|
|
@@ -1966,12 +2310,14 @@ var VioProductDetail = class extends LitElement {
|
|
|
1966
2310
|
const sponsor = Vio.bootstrapCache?.primarySponsor;
|
|
1967
2311
|
const secondaries = Vio.bootstrapCache?.secondarySponsors ?? [];
|
|
1968
2312
|
const sponsorName = sponsor?.id === this.sponsorId ? sponsor.name : secondaries.find((s) => s.id === this.sponsorId)?.name;
|
|
2313
|
+
const { campaignId, slot, contentId } = this.attribution();
|
|
1969
2314
|
Vio.cart.addProduct({
|
|
1970
2315
|
product: this.product,
|
|
1971
2316
|
sponsorId: this.sponsorId,
|
|
1972
2317
|
sponsorName,
|
|
1973
2318
|
quantity: this.quantity,
|
|
1974
|
-
variantId: variant?.id
|
|
2319
|
+
variantId: variant?.id,
|
|
2320
|
+
origin: { campaignId, slot, contentId }
|
|
1975
2321
|
});
|
|
1976
2322
|
return variant?.id;
|
|
1977
2323
|
}
|
|
@@ -1988,7 +2334,8 @@ var VioProductDetail = class extends LitElement {
|
|
|
1988
2334
|
productId: this.product.id,
|
|
1989
2335
|
sponsorId: this.sponsorId,
|
|
1990
2336
|
quantity: this.quantity,
|
|
1991
|
-
variantId
|
|
2337
|
+
variantId,
|
|
2338
|
+
...this.attribution()
|
|
1992
2339
|
}
|
|
1993
2340
|
})
|
|
1994
2341
|
);
|
|
@@ -2013,7 +2360,7 @@ var VioProductDetail = class extends LitElement {
|
|
|
2013
2360
|
new CustomEvent("vio:checkout-open", {
|
|
2014
2361
|
bubbles: true,
|
|
2015
2362
|
composed: true,
|
|
2016
|
-
detail: { sponsorId: this.sponsorId, paymentMethod: "klarna", express: true }
|
|
2363
|
+
detail: { sponsorId: this.sponsorId, paymentMethod: "klarna", express: true, ...this.attribution() }
|
|
2017
2364
|
})
|
|
2018
2365
|
);
|
|
2019
2366
|
} finally {
|
|
@@ -2043,7 +2390,7 @@ var VioProductDetail = class extends LitElement {
|
|
|
2043
2390
|
new CustomEvent("vio:checkout-open", {
|
|
2044
2391
|
bubbles: true,
|
|
2045
2392
|
composed: true,
|
|
2046
|
-
detail: { sponsorId: this.sponsorId, express: false }
|
|
2393
|
+
detail: { sponsorId: this.sponsorId, express: false, ...this.attribution() }
|
|
2047
2394
|
})
|
|
2048
2395
|
);
|
|
2049
2396
|
}
|
|
@@ -2079,7 +2426,7 @@ var VioProductDetail = class extends LitElement {
|
|
|
2079
2426
|
new CustomEvent("vio:checkout-open", {
|
|
2080
2427
|
bubbles: true,
|
|
2081
2428
|
composed: true,
|
|
2082
|
-
detail: { sponsorId: this.sponsorId, paymentMethod: "vipps", express: false }
|
|
2429
|
+
detail: { sponsorId: this.sponsorId, paymentMethod: "vipps", express: false, ...this.attribution() }
|
|
2083
2430
|
})
|
|
2084
2431
|
);
|
|
2085
2432
|
}
|
|
@@ -2092,7 +2439,7 @@ var VioProductDetail = class extends LitElement {
|
|
|
2092
2439
|
new CustomEvent("vio:checkout-open", {
|
|
2093
2440
|
bubbles: true,
|
|
2094
2441
|
composed: true,
|
|
2095
|
-
detail: { sponsorId: this.sponsorId, paymentMethod: "stripe", express: false }
|
|
2442
|
+
detail: { sponsorId: this.sponsorId, paymentMethod: "stripe", express: false, ...this.attribution() }
|
|
2096
2443
|
})
|
|
2097
2444
|
);
|
|
2098
2445
|
}
|
|
@@ -2375,6 +2722,24 @@ __decorateClass([
|
|
|
2375
2722
|
__decorateClass([
|
|
2376
2723
|
property({ type: String })
|
|
2377
2724
|
], VioProductDetail.prototype, "currency", 2);
|
|
2725
|
+
__decorateClass([
|
|
2726
|
+
property({ type: String, attribute: "vio-slot" })
|
|
2727
|
+
], VioProductDetail.prototype, "vioSlot", 2);
|
|
2728
|
+
__decorateClass([
|
|
2729
|
+
property({ type: String, attribute: "vio-slot-name" })
|
|
2730
|
+
], VioProductDetail.prototype, "vioSlotName", 2);
|
|
2731
|
+
__decorateClass([
|
|
2732
|
+
property({ type: String, attribute: "vio-scope" })
|
|
2733
|
+
], VioProductDetail.prototype, "vioScope", 2);
|
|
2734
|
+
__decorateClass([
|
|
2735
|
+
property({ type: String, attribute: "vio-scope-name" })
|
|
2736
|
+
], VioProductDetail.prototype, "vioScopeName", 2);
|
|
2737
|
+
__decorateClass([
|
|
2738
|
+
property({ type: String, attribute: "vio-content-id" })
|
|
2739
|
+
], VioProductDetail.prototype, "vioContentId", 2);
|
|
2740
|
+
__decorateClass([
|
|
2741
|
+
property({ type: String, attribute: "vio-content-title" })
|
|
2742
|
+
], VioProductDetail.prototype, "vioContentTitle", 2);
|
|
2378
2743
|
__decorateClass([
|
|
2379
2744
|
state()
|
|
2380
2745
|
], VioProductDetail.prototype, "product", 2);
|
|
@@ -3001,6 +3366,7 @@ var VioCart = class extends LitElement {
|
|
|
3001
3366
|
async startVippsExpressOrCheckout(sponsorId) {
|
|
3002
3367
|
let fallback = true;
|
|
3003
3368
|
try {
|
|
3369
|
+
await Vio.cart.waitForCartMutations(sponsorId);
|
|
3004
3370
|
if (!await Vio.checkout.getVippsExpressEnabled(sponsorId)) throw new Error("express off");
|
|
3005
3371
|
const res = await Vio.checkout.startVippsExpress(sponsorId);
|
|
3006
3372
|
fallback = !res?.payment_url;
|
|
@@ -3042,11 +3408,14 @@ var VioCart = class extends LitElement {
|
|
|
3042
3408
|
for (const cart of this.carts.values()) return cart.currency;
|
|
3043
3409
|
return "NOK";
|
|
3044
3410
|
}
|
|
3411
|
+
// A delta on the manager's live quantity, not a target computed from this
|
|
3412
|
+
// render's snapshot: two clicks before Lit re-renders used to ask for the
|
|
3413
|
+
// same quantity twice (Alan, 2026-10-15).
|
|
3045
3414
|
onIncrement(item) {
|
|
3046
|
-
Vio.cart.
|
|
3415
|
+
Vio.cart.adjustQuantity(item.id, item.sponsorId, 1);
|
|
3047
3416
|
}
|
|
3048
3417
|
onDecrement(item) {
|
|
3049
|
-
Vio.cart.
|
|
3418
|
+
Vio.cart.adjustQuantity(item.id, item.sponsorId, -1);
|
|
3050
3419
|
}
|
|
3051
3420
|
onRemove(item) {
|
|
3052
3421
|
Vio.cart.removeItem(item.id, item.sponsorId);
|
|
@@ -6665,5 +7034,5 @@ function registerVioElements() {
|
|
|
6665
7034
|
}
|
|
6666
7035
|
|
|
6667
7036
|
export { VioCart, VioCheckout, VioProduct, VioProductCarousel, VioProductDetail, VioTokens, applyVioTheme, registerVioElements };
|
|
6668
|
-
//# sourceMappingURL=chunk-
|
|
6669
|
-
//# sourceMappingURL=chunk-
|
|
7037
|
+
//# sourceMappingURL=chunk-UVDOKFGD.js.map
|
|
7038
|
+
//# sourceMappingURL=chunk-UVDOKFGD.js.map
|