@nuskin/product-components 3.20.1 → 3.20.2-mdtanl-307.1.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/.releaserc CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "branches": [
3
- "master"
3
+ "master", {"name":"MDTANL-307.1", "channel":"prerelease", "prerelease":"mdtanl-307.1"}
4
4
  ],
5
5
  "plugins": [
6
6
  "@semantic-release/release-notes-generator",
@@ -129,10 +129,11 @@ export default {
129
129
  currencyCode: "",
130
130
  runConfig: null,
131
131
  cardPrices: {},
132
- impressionTracked: false,
133
132
  productsWithPrices: new Set(),
134
133
  loadedProductsMap: {},
135
- trackingTimer: null
134
+ componentId: `NsProductList-${Math.random()
135
+ .toString(36)
136
+ .substr(2, 9)}`
136
137
  };
137
138
  },
138
139
  computed: {
@@ -201,14 +202,32 @@ export default {
201
202
  if (!this.$NsProductDataService.check(skus)) {
202
203
  this.$NsProductDataService.queue(skus);
203
204
  }
204
- if (this.trackingTimer) {
205
- clearTimeout(this.trackingTimer);
206
- this.trackingTimer = null;
207
- }
208
- this.impressionTracked = false;
209
205
  this.productsWithPrices.clear();
210
206
  this.loadedProductsMap = {};
211
207
  }
208
+ },
209
+ showSpinner(isLoading, wasLoading) {
210
+ if (wasLoading && !isLoading) {
211
+ const collector = window.__nsProductImpressionCollector;
212
+ if (!collector) return;
213
+
214
+ if (!collector.componentsReadyTime) {
215
+ collector.componentsReadyTime = {};
216
+ }
217
+ collector.componentsReadyTime[this.componentId] = Date.now();
218
+
219
+ const totalComponents = collector.instances.size;
220
+ const readyComponents = Object.keys(collector.componentsReadyTime)
221
+ .length;
222
+
223
+ if (readyComponents === totalComponents && !collector.eventFired) {
224
+ requestAnimationFrame(() => {
225
+ if (!collector.eventFired) {
226
+ this.fireConsolidatedImpressionEvent();
227
+ }
228
+ });
229
+ }
230
+ }
212
231
  }
213
232
  },
214
233
  created() {
@@ -217,13 +236,30 @@ export default {
217
236
  runConfig.country
218
237
  ).currencyCode;
219
238
  this.runConfig = runConfig;
239
+
240
+ if (!window.__nsProductImpressionCollector) {
241
+ window.__nsProductImpressionCollector = {
242
+ instances: new Set(),
243
+ impressions: [],
244
+ currencyCode: this.currencyCode,
245
+ eventFired: false,
246
+ productDataMap: {}
247
+ };
248
+ }
249
+
250
+ const collector = window.__nsProductImpressionCollector;
251
+ collector.instances.add(this.componentId);
220
252
  },
221
253
  async mounted() {
222
254
  await this.setCommonStrings();
223
255
  },
224
256
  beforeDestroy() {
225
- if (this.trackingTimer) {
226
- clearTimeout(this.trackingTimer);
257
+ if (window.__nsProductImpressionCollector) {
258
+ const collector = window.__nsProductImpressionCollector;
259
+ collector.instances.delete(this.componentId);
260
+ if (collector.componentsReadyTime) {
261
+ delete collector.componentsReadyTime[this.componentId];
262
+ }
227
263
  }
228
264
  },
229
265
  methods: {
@@ -305,18 +341,18 @@ export default {
305
341
  displayedTitle: safeTitleCopy
306
342
  };
307
343
 
308
- if (this.trackingTimer) {
309
- clearTimeout(this.trackingTimer);
310
- }
311
- this.trackingTimer = setTimeout(() => {
312
- if (
313
- !this.impressionTracked &&
314
- Object.keys(this.loadedProductsMap).length > 0
315
- ) {
316
- this.trackProductImpressions();
317
- this.impressionTracked = true;
318
- }
319
- }, 300);
344
+ const collector = window.__nsProductImpressionCollector;
345
+
346
+ collector.productDataMap[safeDisplayedSku] = {
347
+ product: product,
348
+ displayedTitle: safeTitleCopy,
349
+ prices: {
350
+ price: price,
351
+ unitPrice: unitPrice,
352
+ salesPrice: salesPrice
353
+ },
354
+ section: this.title || "No Title"
355
+ };
320
356
  }
321
357
  this.$emit("product-update", product);
322
358
  },
@@ -324,21 +360,29 @@ export default {
324
360
  this.limitedItemsFrozen = this.limitedItems;
325
361
 
326
362
  event.target.blur();
363
+
364
+ const collector = window.__nsProductImpressionCollector;
365
+ if (collector) {
366
+ collector.eventFired = false;
367
+ collector.componentsReadyTime = {};
368
+ }
369
+
327
370
  this.skusLimit += this.skusPerLoad;
328
371
  },
329
372
 
330
- trackProductImpressions() {
331
- const displayedSkus = this.displayedItems
332
- .map(item => this.getItemSku(item))
333
- .filter(sku => sku && this.loadedProductsMap[sku]);
373
+ fireConsolidatedImpressionEvent() {
374
+ const collector = window.__nsProductImpressionCollector;
334
375
 
335
- const impressions = displayedSkus
336
- .map((displayedSku, index) => {
337
- const productData = this.loadedProductsMap[displayedSku];
338
- const prices = this.cardPrices[displayedSku] || {};
376
+ if (collector.eventFired) {
377
+ return;
378
+ }
339
379
 
340
- const product = productData.product;
341
- const displayedTitle = productData.displayedTitle;
380
+ const impressions = Object.keys(collector.productDataMap)
381
+ .map((sku, index) => {
382
+ const data = collector.productDataMap[sku];
383
+ const product = data.product;
384
+ const displayedTitle = data.displayedTitle;
385
+ const prices = data.prices;
342
386
 
343
387
  const productImages =
344
388
  (product.getProductCarouselImages &&
@@ -351,7 +395,7 @@ export default {
351
395
  "";
352
396
 
353
397
  return {
354
- id: displayedSku,
398
+ id: sku,
355
399
  name: displayedTitle || product.title || "",
356
400
  price:
357
401
  typeof prices.price !== "undefined" && prices.price !== null
@@ -384,13 +428,20 @@ export default {
384
428
  })
385
429
  .filter(Boolean);
386
430
 
387
- events.publish(events.shop.PRODUCT_IMPRESSION, {
431
+ if (impressions.length === 0) {
432
+ return;
433
+ }
434
+
435
+ const eventData = {
388
436
  ecommerce: {
389
- currencyCode: this.currencyCode,
437
+ currencyCode: collector.currencyCode,
390
438
  revenue: "",
391
- impressions
439
+ impressions: impressions
392
440
  }
393
- });
441
+ };
442
+
443
+ collector.eventFired = true;
444
+ events.publish(events.shop.PRODUCT_IMPRESSION, eventData);
394
445
  },
395
446
  async setCommonStrings() {
396
447
  if (!this.translations || !this.translations.loadMore) {
package/docs/CHANGELOG.md CHANGED
@@ -1 +1 @@
1
- ## [3.20.1](https://code.tls.nuskin.io/ns-am/ux/product-components/compare/v3.20.0...v3.20.1) (2026-04-22)
1
+ ## [3.20.2-mdtanl-307.1.1](https://code.tls.nuskin.io/ns-am/ux/product-components/compare/v3.20.1...v3.20.2-mdtanl-307.1.1) (2026-04-28)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuskin/product-components",
3
- "version": "3.20.1",
3
+ "version": "3.20.2-mdtanl-307.1.1",
4
4
  "description": "Nu Skin Product Components",
5
5
  "main": "index.js",
6
6
  "scripts": {