@crystaldesign/widget-library 25.8.0-beta.5 → 25.8.0-beta.7
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/build/esm/index.js +409 -109
- package/build/types/widget-library/src/hooks/useGlobalCache.d.ts +62 -0
- package/build/types/widget-library/src/hooks/useGlobalCache.d.ts.map +1 -0
- package/build/types/widget-library/src/hooks/useGlobalSelectedProduct.d.ts +29 -0
- package/build/types/widget-library/src/hooks/useGlobalSelectedProduct.d.ts.map +1 -0
- package/build/types/widget-library/src/hooks/useProductData.d.ts +8 -3
- package/build/types/widget-library/src/hooks/useProductData.d.ts.map +1 -1
- package/package.json +2 -2
package/build/esm/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { useEffect, useMemo,
|
|
1
|
+
import { useEffect, useMemo, useCallback, useState, useRef, forwardRef } from 'react';
|
|
2
2
|
import { useDivaCore, getLogger, DivaError, useTranslation } from '@crystaldesign/diva-core';
|
|
3
|
+
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
|
|
3
4
|
import _asyncToGenerator from '@babel/runtime/helpers/asyncToGenerator';
|
|
4
5
|
import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
|
|
5
6
|
import _regeneratorRuntime from '@babel/runtime/regenerator';
|
|
6
7
|
import _defineProperty from '@babel/runtime/helpers/defineProperty';
|
|
7
8
|
import { Navigation, Thumbs, Pagination } from 'swiper/modules';
|
|
8
9
|
import { Swiper, SwiperSlide } from 'swiper/react';
|
|
9
|
-
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
|
|
10
10
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
11
11
|
import classNames from 'classnames';
|
|
12
12
|
import { useLayer, Arrow } from 'react-laag';
|
|
@@ -80,20 +80,234 @@ function useConfiguration(type, componentSettings) {
|
|
|
80
80
|
return settings;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
var LOG$2 = getLogger('DivaWidget', 'useGlobalCache');
|
|
84
|
+
|
|
85
|
+
// Global cache for shared data between widgets
|
|
86
|
+
|
|
87
|
+
// Initialize global cache if not exists
|
|
88
|
+
if (typeof window !== 'undefined' && !window.__divaWidgetCache) {
|
|
89
|
+
window.__divaWidgetCache = {
|
|
90
|
+
cache: new Map()
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Global cache for shared data between widgets of a website
|
|
96
|
+
* Simple implementation with request deduplication
|
|
97
|
+
*/
|
|
98
|
+
function useGlobalCache(_ref) {
|
|
99
|
+
var uniqueWidgetId = _ref.uniqueWidgetId,
|
|
100
|
+
widgetType = _ref.widgetType;
|
|
101
|
+
var getOrFetch = useCallback(function (key, fetcher) {
|
|
102
|
+
LOG$2.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] getOrFetch called for key: ").concat(key));
|
|
103
|
+
if (typeof window === 'undefined' || !window.__divaWidgetCache) {
|
|
104
|
+
LOG$2.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] No global cache available, fetching directly for key: ").concat(key));
|
|
105
|
+
return fetcher();
|
|
106
|
+
}
|
|
107
|
+
var cache = window.__divaWidgetCache.cache;
|
|
108
|
+
var entry = cache.get(key);
|
|
109
|
+
|
|
110
|
+
// If we have cached data, return it immediately
|
|
111
|
+
if (entry !== null && entry !== void 0 && entry.data) {
|
|
112
|
+
LOG$2.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Cache hit for key: ").concat(key, ", returning cached data"));
|
|
113
|
+
return Promise.resolve(entry.data);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// If there's already a pending request, return that promise (deduplication)
|
|
117
|
+
if (entry !== null && entry !== void 0 && entry.promise) {
|
|
118
|
+
LOG$2.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Cache hit for pending request, returning existing promise for key: ").concat(key));
|
|
119
|
+
return entry.promise;
|
|
120
|
+
}
|
|
121
|
+
LOG$2.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Cache miss for key: ").concat(key, ", creating new request"));
|
|
122
|
+
|
|
123
|
+
// Create new entry or reset existing one
|
|
124
|
+
var newEntry = {
|
|
125
|
+
data: null,
|
|
126
|
+
promise: null,
|
|
127
|
+
error: null,
|
|
128
|
+
timestamp: Date.now()
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Create new request
|
|
132
|
+
var promise = fetcher().then(function (data) {
|
|
133
|
+
LOG$2.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Request successful for key: ").concat(key, ", updating cache with ").concat(data.length, " items"));
|
|
134
|
+
var currentEntry = cache.get(key);
|
|
135
|
+
if (currentEntry) {
|
|
136
|
+
currentEntry.data = data;
|
|
137
|
+
currentEntry.promise = null;
|
|
138
|
+
currentEntry.error = null;
|
|
139
|
+
currentEntry.timestamp = Date.now();
|
|
140
|
+
}
|
|
141
|
+
return data;
|
|
142
|
+
})["catch"](function (error) {
|
|
143
|
+
LOG$2.error(new DivaError("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Request failed for key: ").concat(key), {
|
|
144
|
+
cause: error
|
|
145
|
+
}));
|
|
146
|
+
var currentEntry = cache.get(key);
|
|
147
|
+
if (currentEntry) {
|
|
148
|
+
currentEntry.promise = null;
|
|
149
|
+
currentEntry.error = error;
|
|
150
|
+
currentEntry.timestamp = Date.now();
|
|
151
|
+
}
|
|
152
|
+
throw error;
|
|
153
|
+
});
|
|
154
|
+
newEntry.promise = promise;
|
|
155
|
+
cache.set(key, newEntry);
|
|
156
|
+
return promise;
|
|
157
|
+
}, [uniqueWidgetId, widgetType]);
|
|
158
|
+
var clear = useCallback(function (key) {
|
|
159
|
+
LOG$2.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Clearing cache entry for key: ").concat(key));
|
|
160
|
+
if (typeof window !== 'undefined' && window.__divaWidgetCache) {
|
|
161
|
+
var deleted = window.__divaWidgetCache.cache["delete"](key);
|
|
162
|
+
LOG$2.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Cache entry ").concat(deleted ? 'deleted' : 'not found', " for key: ").concat(key));
|
|
163
|
+
}
|
|
164
|
+
}, [uniqueWidgetId, widgetType]);
|
|
165
|
+
var clearAll = useCallback(function () {
|
|
166
|
+
LOG$2.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Clearing all cache entries"));
|
|
167
|
+
if (typeof window !== 'undefined' && window.__divaWidgetCache) {
|
|
168
|
+
var size = window.__divaWidgetCache.cache.size;
|
|
169
|
+
window.__divaWidgetCache.cache.clear();
|
|
170
|
+
LOG$2.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Cleared ").concat(size, " cache entries"));
|
|
171
|
+
}
|
|
172
|
+
}, [uniqueWidgetId, widgetType]);
|
|
173
|
+
return useMemo(function () {
|
|
174
|
+
return {
|
|
175
|
+
getOrFetch: getOrFetch,
|
|
176
|
+
clear: clear,
|
|
177
|
+
clearAll: clearAll
|
|
178
|
+
};
|
|
179
|
+
}, [getOrFetch, clear, clearAll]);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
var LOG$1 = getLogger('DivaWidget', 'useGlobalSelectedProduct');
|
|
183
|
+
var EVENT_NAME_PRODUCT_SELECTED = 'diva:widget:product-selected';
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Hook to get the globally selected product
|
|
187
|
+
*
|
|
188
|
+
* Initially and when the product changes, it gets selected.
|
|
189
|
+
* When ever the selected product is changed, an event is dispatched to notify other widgets, so they can also set the new product as selected.
|
|
190
|
+
* The hook also listens for product selection events from other widgets and updates the selected product accordingly.
|
|
191
|
+
*
|
|
192
|
+
* @param product - The product to select initially
|
|
193
|
+
* @returns The selected product and a function to set the selected product
|
|
194
|
+
*/
|
|
195
|
+
function useGlobalSelectedProduct(_ref) {
|
|
196
|
+
var product = _ref.product,
|
|
197
|
+
uniqueWidgetId = _ref.uniqueWidgetId,
|
|
198
|
+
widgetType = _ref.widgetType;
|
|
199
|
+
var _useState = useState(product),
|
|
200
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
201
|
+
selectedProduct = _useState2[0],
|
|
202
|
+
setInternalSelectedProduct = _useState2[1];
|
|
203
|
+
var lastEventTimestamp = useRef(0);
|
|
204
|
+
|
|
205
|
+
// Update internal state when prop changes - prop always takes precedence for this widget
|
|
206
|
+
useEffect(function () {
|
|
207
|
+
if (product && product._id !== (selectedProduct === null || selectedProduct === void 0 ? void 0 : selectedProduct._id)) {
|
|
208
|
+
var timestamp = Date.now();
|
|
209
|
+
lastEventTimestamp.current = timestamp;
|
|
210
|
+
LOG$1.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Product prop changed, updating selected product from ").concat((selectedProduct === null || selectedProduct === void 0 ? void 0 : selectedProduct._id) || 'none', " to ").concat(product._id));
|
|
211
|
+
setInternalSelectedProduct(product);
|
|
212
|
+
}
|
|
213
|
+
}, [product]);
|
|
214
|
+
|
|
215
|
+
// Listen to product selection events from other widgets
|
|
216
|
+
var handleProductSelection = useCallback(function (event) {
|
|
217
|
+
var customEvent = event;
|
|
218
|
+
var _customEvent$detail = customEvent.detail,
|
|
219
|
+
product = _customEvent$detail.product,
|
|
220
|
+
sourceId = _customEvent$detail.sourceId,
|
|
221
|
+
timestamp = _customEvent$detail.timestamp;
|
|
222
|
+
LOG$1.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Received product selection event from ").concat(sourceId, ", product: ").concat(product._id, ", timestamp: ").concat(timestamp));
|
|
223
|
+
|
|
224
|
+
// Ignore our own events
|
|
225
|
+
if (sourceId === uniqueWidgetId) {
|
|
226
|
+
LOG$1.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Ignoring own event"));
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Ignore older events (handle out-of-order events)
|
|
231
|
+
if (timestamp <= lastEventTimestamp.current) {
|
|
232
|
+
LOG$1.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Ignoring older event (timestamp: ").concat(timestamp, " <= ").concat(lastEventTimestamp.current, ")"));
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Ignore if product hasn't actually changed
|
|
237
|
+
if ((selectedProduct === null || selectedProduct === void 0 ? void 0 : selectedProduct._id) === product._id) {
|
|
238
|
+
LOG$1.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Ignoring event - product already selected: ").concat(product._id));
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
LOG$1.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Updating selected product from ").concat((selectedProduct === null || selectedProduct === void 0 ? void 0 : selectedProduct._id) || 'none', " to ").concat(product._id));
|
|
242
|
+
lastEventTimestamp.current = timestamp;
|
|
243
|
+
setInternalSelectedProduct(product);
|
|
244
|
+
}, [uniqueWidgetId, selectedProduct, widgetType]);
|
|
245
|
+
|
|
246
|
+
// Stable event listener setup
|
|
247
|
+
useEffect(function () {
|
|
248
|
+
window.addEventListener(EVENT_NAME_PRODUCT_SELECTED, handleProductSelection);
|
|
249
|
+
return function () {
|
|
250
|
+
window.removeEventListener(EVENT_NAME_PRODUCT_SELECTED, handleProductSelection);
|
|
251
|
+
};
|
|
252
|
+
}, [handleProductSelection]);
|
|
253
|
+
var setSelectedProduct = useCallback(function (product) {
|
|
254
|
+
// Avoid unnecessary updates
|
|
255
|
+
if ((selectedProduct === null || selectedProduct === void 0 ? void 0 : selectedProduct._id) === product._id) {
|
|
256
|
+
LOG$1.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Ignoring setSelectedProduct - product already selected: ").concat(product._id));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
var timestamp = Date.now();
|
|
260
|
+
lastEventTimestamp.current = timestamp;
|
|
261
|
+
LOG$1.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Setting selected product from ").concat((selectedProduct === null || selectedProduct === void 0 ? void 0 : selectedProduct._id) || 'none', " to ").concat(product._id));
|
|
262
|
+
setInternalSelectedProduct(product);
|
|
263
|
+
|
|
264
|
+
// Dispatch event to notify other widgets
|
|
265
|
+
LOG$1.debug("[".concat(widgetType, ":").concat(uniqueWidgetId, "] Dispatching product selection event to notify other widgets"));
|
|
266
|
+
window.dispatchEvent(new CustomEvent(EVENT_NAME_PRODUCT_SELECTED, {
|
|
267
|
+
detail: {
|
|
268
|
+
product: product,
|
|
269
|
+
sourceId: uniqueWidgetId,
|
|
270
|
+
timestamp: timestamp
|
|
271
|
+
}
|
|
272
|
+
}));
|
|
273
|
+
}, [uniqueWidgetId, selectedProduct, widgetType]);
|
|
274
|
+
return {
|
|
275
|
+
selectedProduct: selectedProduct,
|
|
276
|
+
setSelectedProduct: setSelectedProduct
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
86
280
|
var LOG = getLogger('DivaWidget', 'useProductData');
|
|
281
|
+
// Generate unique widget instance ID
|
|
282
|
+
var generateWidgetId = function generateWidgetId() {
|
|
283
|
+
return "widget-".concat(Date.now(), "-").concat(Math.random().toString(36).substr(2, 9));
|
|
284
|
+
};
|
|
87
285
|
function useProductData(_ref) {
|
|
88
286
|
var productIds = _ref.productIds,
|
|
89
287
|
variants = _ref.variants,
|
|
90
|
-
|
|
288
|
+
widgetType = _ref.widgetType;
|
|
289
|
+
// Generate stable widget ID that persists for the lifetime of this hook instance
|
|
290
|
+
var widgetId = useMemo(function () {
|
|
291
|
+
return generateWidgetId();
|
|
292
|
+
}, []);
|
|
91
293
|
var _useDivaCore = useDivaCore(),
|
|
92
294
|
handler = _useDivaCore.handler;
|
|
93
|
-
var
|
|
295
|
+
var _useGlobalCache = useGlobalCache({
|
|
296
|
+
uniqueWidgetId: widgetId,
|
|
297
|
+
widgetType: widgetType
|
|
298
|
+
}),
|
|
299
|
+
getOrFetch = _useGlobalCache.getOrFetch;
|
|
300
|
+
var _useState = useState([]),
|
|
94
301
|
_useState2 = _slicedToArray(_useState, 2),
|
|
95
302
|
productVariants = _useState2[0],
|
|
96
303
|
setProductVariants = _useState2[1];
|
|
304
|
+
var _useGlobalSelectedPro = useGlobalSelectedProduct({
|
|
305
|
+
product: productVariants[0],
|
|
306
|
+
uniqueWidgetId: widgetId,
|
|
307
|
+
widgetType: widgetType
|
|
308
|
+
}),
|
|
309
|
+
selectedProduct = _useGlobalSelectedPro.selectedProduct,
|
|
310
|
+
setSelectedProduct = _useGlobalSelectedPro.setSelectedProduct;
|
|
97
311
|
var _useState3 = useState(false),
|
|
98
312
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
99
313
|
error = _useState4[0],
|
|
@@ -102,120 +316,206 @@ function useProductData(_ref) {
|
|
|
102
316
|
_useState6 = _slicedToArray(_useState5, 2),
|
|
103
317
|
loading = _useState6[0],
|
|
104
318
|
setLoading = _useState6[1];
|
|
319
|
+
var normalizedProductIds = useMemo(function () {
|
|
320
|
+
if (!productIds) return [];
|
|
321
|
+
return Array.isArray(productIds) ? productIds : [productIds];
|
|
322
|
+
}, [productIds]);
|
|
323
|
+
var fetchProducts = useCallback(/*#__PURE__*/function () {
|
|
324
|
+
var _ref2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee2(ids) {
|
|
325
|
+
var productPromises, results, flattenedResults;
|
|
326
|
+
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
|
|
327
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
328
|
+
case 0:
|
|
329
|
+
LOG.debug("[".concat(widgetType, ":").concat(widgetId, "] fetchProducts called with ids: ").concat(ids.join(', ')));
|
|
330
|
+
if (!variants) {
|
|
331
|
+
_context2.next = 4;
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
LOG.debug("[".concat(widgetType, ":").concat(widgetId, "] Using provided variants instead of fetching"));
|
|
335
|
+
return _context2.abrupt("return", variants);
|
|
336
|
+
case 4:
|
|
337
|
+
// Create promises for all products (cached or to be fetched) to preserve order
|
|
338
|
+
productPromises = ids.map(/*#__PURE__*/function () {
|
|
339
|
+
var _ref3 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee(id) {
|
|
340
|
+
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
341
|
+
while (1) switch (_context.prev = _context.next) {
|
|
342
|
+
case 0:
|
|
343
|
+
LOG.debug("[".concat(widgetType, ":").concat(widgetId, "] Fetching product variants for id: ").concat(id));
|
|
344
|
+
return _context.abrupt("return", getOrFetch(id, function () {
|
|
345
|
+
return handler.productHandler.apiGetProductVariants(id, true);
|
|
346
|
+
}));
|
|
347
|
+
case 2:
|
|
348
|
+
case "end":
|
|
349
|
+
return _context.stop();
|
|
350
|
+
}
|
|
351
|
+
}, _callee);
|
|
352
|
+
}));
|
|
353
|
+
return function (_x2) {
|
|
354
|
+
return _ref3.apply(this, arguments);
|
|
355
|
+
};
|
|
356
|
+
}());
|
|
357
|
+
_context2.next = 7;
|
|
358
|
+
return Promise.all(productPromises);
|
|
359
|
+
case 7:
|
|
360
|
+
results = _context2.sent;
|
|
361
|
+
flattenedResults = results.flat();
|
|
362
|
+
LOG.debug("[".concat(widgetType, ":").concat(widgetId, "] Fetched ").concat(flattenedResults.length, " total product variants"));
|
|
363
|
+
return _context2.abrupt("return", flattenedResults);
|
|
364
|
+
case 11:
|
|
365
|
+
case "end":
|
|
366
|
+
return _context2.stop();
|
|
367
|
+
}
|
|
368
|
+
}, _callee2);
|
|
369
|
+
}));
|
|
370
|
+
return function (_x) {
|
|
371
|
+
return _ref2.apply(this, arguments);
|
|
372
|
+
};
|
|
373
|
+
}(), [handler.productHandler, getOrFetch, variants, widgetId, widgetType]);
|
|
374
|
+
|
|
375
|
+
// Main effect for loading products when productIds or variants change
|
|
105
376
|
useEffect(function () {
|
|
377
|
+
LOG.debug("[".concat(widgetType, ":").concat(widgetId, "] Main effect triggered with normalizedProductIds: ").concat(normalizedProductIds.join(', ')));
|
|
378
|
+
if (!normalizedProductIds.length) {
|
|
379
|
+
LOG.error(new DivaError("[".concat(widgetType, ":").concat(widgetId, "] ProductIds array is required and cannot be empty")));
|
|
380
|
+
setError(true);
|
|
381
|
+
setLoading(false);
|
|
382
|
+
setProductVariants([]);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
106
385
|
setError(false);
|
|
107
386
|
setLoading(true);
|
|
108
|
-
|
|
109
|
-
LOG.
|
|
387
|
+
fetchProducts(normalizedProductIds).then(function (variants) {
|
|
388
|
+
LOG.debug("[".concat(widgetType, ":").concat(widgetId, "] Successfully loaded ").concat(variants.length, " product variants"));
|
|
389
|
+
setProductVariants(variants);
|
|
390
|
+
setLoading(false);
|
|
391
|
+
})["catch"](function (error) {
|
|
392
|
+
LOG.error(new DivaError("[".concat(widgetType, ":").concat(widgetId, "] Error loading products"), {
|
|
393
|
+
cause: error
|
|
394
|
+
}));
|
|
110
395
|
setError(true);
|
|
111
396
|
setLoading(false);
|
|
397
|
+
setProductVariants([]);
|
|
398
|
+
});
|
|
399
|
+
}, [normalizedProductIds]);
|
|
400
|
+
|
|
401
|
+
// Effect for handling selectedProduct changes
|
|
402
|
+
useEffect(function () {
|
|
403
|
+
// If a product is selected that is not in the productVariants list,
|
|
404
|
+
// fetch the variants for that product and append them to the productVariants list
|
|
405
|
+
if (!selectedProduct || productVariants.find(function (p) {
|
|
406
|
+
return p._id === selectedProduct._id;
|
|
407
|
+
})) {
|
|
112
408
|
return;
|
|
113
409
|
}
|
|
114
|
-
(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
410
|
+
LOG.debug("[".concat(widgetType, ":").concat(widgetId, "] Selected product ").concat(selectedProduct._id, " not in current variants, fetching variants for it"));
|
|
411
|
+
setLoading(true);
|
|
412
|
+
setError(false);
|
|
413
|
+
fetchProducts([selectedProduct._id]).then(function (variants) {
|
|
414
|
+
setProductVariants(function (old) {
|
|
415
|
+
// Prevent duplicate products
|
|
416
|
+
var existingIds = new Set(old.map(function (p) {
|
|
417
|
+
return p._id;
|
|
418
|
+
}));
|
|
419
|
+
var newVariants = variants.filter(function (v) {
|
|
420
|
+
return !existingIds.has(v._id);
|
|
421
|
+
});
|
|
422
|
+
var updatedVariants = newVariants.length > 0 ? [].concat(_toConsumableArray(old), _toConsumableArray(newVariants)) : old;
|
|
423
|
+
LOG.debug("[".concat(widgetType, ":").concat(widgetId, "] Added ").concat(newVariants.length, " new variants to productVariants, total: ").concat(updatedVariants.length));
|
|
424
|
+
return updatedVariants;
|
|
425
|
+
});
|
|
426
|
+
setLoading(false);
|
|
427
|
+
})["catch"](function (error) {
|
|
428
|
+
LOG.error(new DivaError("[".concat(widgetType, ":").concat(widgetId, "] Error loading product"), {
|
|
429
|
+
cause: error
|
|
430
|
+
}));
|
|
431
|
+
setError(true);
|
|
432
|
+
setLoading(false);
|
|
433
|
+
});
|
|
434
|
+
}, [selectedProduct, productVariants, fetchProducts, widgetId, widgetType]);
|
|
435
|
+
var setSelectedProductId = useCallback(/*#__PURE__*/function () {
|
|
436
|
+
var _ref4 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee3(productId) {
|
|
437
|
+
var product, _variants, _product;
|
|
438
|
+
return _regeneratorRuntime.wrap(function _callee3$(_context3) {
|
|
439
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
440
|
+
case 0:
|
|
441
|
+
LOG.debug("[".concat(widgetType, ":").concat(widgetId, "] setSelectedProductId called with productId: ").concat(productId));
|
|
442
|
+
|
|
443
|
+
// If a product is selected that is not in the productVariants list,
|
|
444
|
+
// fetch the variants for that product and append them to the productVariants list
|
|
445
|
+
product = productVariants.find(function (p) {
|
|
446
|
+
return p._id === productId;
|
|
447
|
+
});
|
|
448
|
+
if (!product) {
|
|
449
|
+
_context3.next = 6;
|
|
128
450
|
break;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return
|
|
149
|
-
while (1) switch (_context.prev = _context.next) {
|
|
150
|
-
case 0:
|
|
151
|
-
product = _step.value;
|
|
152
|
-
matchingProductId = ids.find(function (id) {
|
|
153
|
-
return product._id === id;
|
|
154
|
-
});
|
|
155
|
-
if (matchingProductId) {
|
|
156
|
-
LOG.businessEvent('onLoadProductPDP', 'ProductDetailPage', 'Neues Product in PDP geladen.', {
|
|
157
|
-
productId: matchingProductId,
|
|
158
|
-
catalogName: product.modelName,
|
|
159
|
-
catalogCodex: product.catCodex
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
case 3:
|
|
163
|
-
case "end":
|
|
164
|
-
return _context.stop();
|
|
165
|
-
}
|
|
166
|
-
}, _loop);
|
|
451
|
+
}
|
|
452
|
+
LOG.debug("[".concat(widgetType, ":").concat(widgetId, "] Product ").concat(productId, " found in current variants, setting as selected"));
|
|
453
|
+
setSelectedProduct(product);
|
|
454
|
+
return _context3.abrupt("return");
|
|
455
|
+
case 6:
|
|
456
|
+
LOG.debug("[".concat(widgetType, ":").concat(widgetId, "] Product ").concat(productId, " not found in current variants, fetching variants for it"));
|
|
457
|
+
_context3.prev = 7;
|
|
458
|
+
setError(false);
|
|
459
|
+
setLoading(true);
|
|
460
|
+
_context3.next = 12;
|
|
461
|
+
return fetchProducts([productId]);
|
|
462
|
+
case 12:
|
|
463
|
+
_variants = _context3.sent;
|
|
464
|
+
setProductVariants(function (old) {
|
|
465
|
+
// Prevent duplicate products
|
|
466
|
+
var existingIds = new Set(old.map(function (p) {
|
|
467
|
+
return p._id;
|
|
468
|
+
}));
|
|
469
|
+
var newVariants = _variants.filter(function (v) {
|
|
470
|
+
return !existingIds.has(v._id);
|
|
167
471
|
});
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
break;
|
|
178
|
-
case 21:
|
|
179
|
-
_context2.next = 26;
|
|
180
|
-
break;
|
|
181
|
-
case 23:
|
|
182
|
-
_context2.prev = 23;
|
|
183
|
-
_context2.t1 = _context2["catch"](14);
|
|
184
|
-
_iterator.e(_context2.t1);
|
|
185
|
-
case 26:
|
|
186
|
-
_context2.prev = 26;
|
|
187
|
-
_iterator.f();
|
|
188
|
-
return _context2.finish(26);
|
|
189
|
-
case 29:
|
|
190
|
-
onProductLoaded === null || onProductLoaded === void 0 || onProductLoaded(newVariants);
|
|
191
|
-
_context2.next = 36;
|
|
472
|
+
var updatedVariants = [].concat(_toConsumableArray(old), _toConsumableArray(newVariants));
|
|
473
|
+
LOG.debug("[".concat(widgetType, ":").concat(widgetId, "] Added ").concat(newVariants.length, " new variants to productVariants, total: ").concat(updatedVariants.length));
|
|
474
|
+
return updatedVariants;
|
|
475
|
+
});
|
|
476
|
+
_product = _variants.find(function (p) {
|
|
477
|
+
return p._id === productId;
|
|
478
|
+
});
|
|
479
|
+
if (!_product) {
|
|
480
|
+
_context3.next = 20;
|
|
192
481
|
break;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
482
|
+
}
|
|
483
|
+
LOG.debug("[".concat(widgetType, ":").concat(widgetId, "] Setting product ").concat(productId, " as selected after fetching"));
|
|
484
|
+
setSelectedProduct(_product);
|
|
485
|
+
_context3.next = 21;
|
|
486
|
+
break;
|
|
487
|
+
case 20:
|
|
488
|
+
throw new DivaError("[".concat(widgetType, ":").concat(widgetId, "] Product ").concat(productId, " not found"));
|
|
489
|
+
case 21:
|
|
490
|
+
_context3.next = 27;
|
|
491
|
+
break;
|
|
492
|
+
case 23:
|
|
493
|
+
_context3.prev = 23;
|
|
494
|
+
_context3.t0 = _context3["catch"](7);
|
|
495
|
+
LOG.error(new DivaError("[".concat(widgetType, ":").concat(widgetId, "] Error loading product"), {
|
|
496
|
+
cause: _context3.t0
|
|
497
|
+
}));
|
|
498
|
+
setError(true);
|
|
499
|
+
case 27:
|
|
500
|
+
_context3.prev = 27;
|
|
501
|
+
setLoading(false);
|
|
502
|
+
return _context3.finish(27);
|
|
503
|
+
case 30:
|
|
504
|
+
case "end":
|
|
505
|
+
return _context3.stop();
|
|
506
|
+
}
|
|
507
|
+
}, _callee3, null, [[7, 23, 27, 30]]);
|
|
508
|
+
}));
|
|
509
|
+
return function (_x3) {
|
|
510
|
+
return _ref4.apply(this, arguments);
|
|
511
|
+
};
|
|
512
|
+
}(), [productVariants, fetchProducts, setSelectedProduct, widgetId, widgetType]);
|
|
215
513
|
return {
|
|
216
514
|
productVariants: productVariants,
|
|
217
515
|
error: error,
|
|
218
|
-
loading: loading
|
|
516
|
+
loading: loading,
|
|
517
|
+
selectedProduct: selectedProduct,
|
|
518
|
+
setSelectedProductId: setSelectedProductId
|
|
219
519
|
};
|
|
220
520
|
}
|
|
221
521
|
|
|
@@ -596,7 +896,7 @@ var css_248z$1 = ".gallery-XR09O .gallery-thumb .swiper-slide {\n opacity: 0.4;
|
|
|
596
896
|
styleInject(css_248z$1);
|
|
597
897
|
|
|
598
898
|
var galleryAdditional = "gallery-additional-oIgEY";
|
|
599
|
-
var css_248z = ".gallery-additional-oIgEY.diva-widget-gallery {\n width: 100%;\n height: 100%;\n --swiper-navigation-color: #bbb;\n --swiper-navigation-sides-offset: 4px;\n}\n\n.gallery-additional-oIgEY .gallery-main {\n height: 85%;\n}\n\n.gallery-additional-oIgEY .gallery-thumb {\n height: 15%;\n}\n\n.gallery-additional-oIgEY .
|
|
899
|
+
var css_248z = ".gallery-additional-oIgEY.diva-widget-gallery {\n width: 100%;\n height: 100%;\n --swiper-navigation-color: #bbb;\n --swiper-navigation-sides-offset: 4px;\n}\n\n.gallery-additional-oIgEY .gallery-main {\n height: 85%;\n}\n\n.gallery-additional-oIgEY .gallery-thumb {\n height: 15%;\n}\n\n.gallery-additional-oIgEY .diva-widget-mediaitem,\n.gallery-additional-oIgEY .gallery-image-thumb {\n width: 100%;\n height: 100%;\n -o-object-fit: scale-down;\n object-fit: scale-down;\n}\n\n.gallery-additional-oIgEY .gallery-image-thumb {\n cursor: pointer;\n}\n\n.gallery-additional-oIgEY .mobile .gallery-main {\n height: 100%;\n}\n\n.gallery-additional-oIgEY .mobile .gallery-thumb {\n display: none;\n}\n\n.gallery-additional-oIgEY .gallery-page-number {\n position: absolute;\n bottom: 16px;\n left: 16px;\n border-radius: 15px;\n z-index: 1000;\n padding: 0 16px;\n}\n";
|
|
600
900
|
styleInject(css_248z);
|
|
601
901
|
|
|
602
902
|
function ownKeys$1(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
interface CacheEntry<T> {
|
|
2
|
+
/**
|
|
3
|
+
* The actual data after the request is resolved
|
|
4
|
+
*/
|
|
5
|
+
data: T[] | null;
|
|
6
|
+
/**
|
|
7
|
+
* The promise for the request
|
|
8
|
+
*/
|
|
9
|
+
promise: Promise<T[]> | null;
|
|
10
|
+
/**
|
|
11
|
+
* The error if the request failed
|
|
12
|
+
*/
|
|
13
|
+
error: Error | null;
|
|
14
|
+
/**
|
|
15
|
+
* Timestamp when the entry was last updated
|
|
16
|
+
*/
|
|
17
|
+
timestamp: number;
|
|
18
|
+
}
|
|
19
|
+
interface DivaWidgetCache {
|
|
20
|
+
/**
|
|
21
|
+
* Get data from cache or fetch if not available
|
|
22
|
+
* Implements request deduplication - multiple components requesting the same data
|
|
23
|
+
* will share the same promise
|
|
24
|
+
* @param key - Cache key (product ID)
|
|
25
|
+
* @param fetcher - Function to fetch data
|
|
26
|
+
* @returns Promise that resolves to the data
|
|
27
|
+
*/
|
|
28
|
+
getOrFetch: <T>(key: string, fetcher: () => Promise<T[]>) => Promise<T[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Clear a specific cache entry
|
|
31
|
+
* @param key - Cache key to clear
|
|
32
|
+
*/
|
|
33
|
+
clear: (key: string) => void;
|
|
34
|
+
/**
|
|
35
|
+
* Clear all cache entries
|
|
36
|
+
*/
|
|
37
|
+
clearAll: () => void;
|
|
38
|
+
}
|
|
39
|
+
interface UseGlobalCacheProps {
|
|
40
|
+
/**
|
|
41
|
+
* Unique widget instance ID, used for events and logging
|
|
42
|
+
*/
|
|
43
|
+
uniqueWidgetId: string;
|
|
44
|
+
/**
|
|
45
|
+
* Widget type, used for events and logging
|
|
46
|
+
*/
|
|
47
|
+
widgetType: string;
|
|
48
|
+
}
|
|
49
|
+
declare global {
|
|
50
|
+
interface Window {
|
|
51
|
+
__divaWidgetCache: {
|
|
52
|
+
cache: Map<string, CacheEntry<any>>;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Global cache for shared data between widgets of a website
|
|
58
|
+
* Simple implementation with request deduplication
|
|
59
|
+
*/
|
|
60
|
+
export declare function useGlobalCache({ uniqueWidgetId, widgetType }: UseGlobalCacheProps): DivaWidgetCache;
|
|
61
|
+
export {};
|
|
62
|
+
//# sourceMappingURL=useGlobalCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useGlobalCache.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/useGlobalCache.ts"],"names":[],"mappings":"AAKA,UAAU,UAAU,CAAC,CAAC;IACpB;;OAEG;IACH,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC;IACjB;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IAC7B;;OAEG;IACH,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,eAAe;IACvB;;;;;;;OAOG;IACH,UAAU,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAC1E;;;OAGG;IACH,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B;;OAEG;IACH,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,UAAU,mBAAmB;IAC3B;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAGD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,iBAAiB,EAAE;YACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;SACrC,CAAC;KACH;CACF;AASD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAAE,cAAc,EAAE,UAAU,EAAE,EAAE,mBAAmB,GAAG,eAAe,CAwFnG"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ProductData } from '@crystaldesign/diva-core';
|
|
2
|
+
interface UseGlobalSelectedProductProps {
|
|
3
|
+
product?: ProductData;
|
|
4
|
+
/**
|
|
5
|
+
* Unique widget instance ID, used for events and logging
|
|
6
|
+
*/
|
|
7
|
+
uniqueWidgetId: string;
|
|
8
|
+
/**
|
|
9
|
+
* Widget type, used for events and logging
|
|
10
|
+
*/
|
|
11
|
+
widgetType: string;
|
|
12
|
+
}
|
|
13
|
+
interface UseGlobalSelectedProductReturn {
|
|
14
|
+
selectedProduct: ProductData | undefined;
|
|
15
|
+
setSelectedProduct: (product: ProductData) => void;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Hook to get the globally selected product
|
|
19
|
+
*
|
|
20
|
+
* Initially and when the product changes, it gets selected.
|
|
21
|
+
* When ever the selected product is changed, an event is dispatched to notify other widgets, so they can also set the new product as selected.
|
|
22
|
+
* The hook also listens for product selection events from other widgets and updates the selected product accordingly.
|
|
23
|
+
*
|
|
24
|
+
* @param product - The product to select initially
|
|
25
|
+
* @returns The selected product and a function to set the selected product
|
|
26
|
+
*/
|
|
27
|
+
export declare function useGlobalSelectedProduct({ product, uniqueWidgetId, widgetType }: UseGlobalSelectedProductProps): UseGlobalSelectedProductReturn;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=useGlobalSelectedProduct.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useGlobalSelectedProduct.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/useGlobalSelectedProduct.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAIlE,UAAU,6BAA6B;IACrC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,8BAA8B;IACtC,eAAe,EAAE,WAAW,GAAG,SAAS,CAAC;IACzC,kBAAkB,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;CACpD;AAYD;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,EAAE,6BAA6B,GAAG,8BAA8B,CA6F/I"}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { ProductData } from '@crystaldesign/diva-core';
|
|
2
2
|
export interface UseProductDataProps {
|
|
3
|
-
productIds?: string[];
|
|
3
|
+
productIds?: string[] | string;
|
|
4
4
|
variants?: ProductData[];
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Widget type, used for logging
|
|
7
|
+
*/
|
|
8
|
+
widgetType: string;
|
|
6
9
|
}
|
|
7
|
-
export declare function useProductData({ productIds, variants,
|
|
10
|
+
export declare function useProductData({ productIds, variants, widgetType }: UseProductDataProps): {
|
|
8
11
|
productVariants: ProductData[];
|
|
9
12
|
error: boolean;
|
|
10
13
|
loading: boolean;
|
|
14
|
+
selectedProduct: ProductData | undefined;
|
|
15
|
+
setSelectedProductId: (productId: string) => Promise<void>;
|
|
11
16
|
};
|
|
12
17
|
//# sourceMappingURL=useProductData.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useProductData.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/useProductData.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,WAAW,EAA0B,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"useProductData.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/useProductData.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,WAAW,EAA0B,MAAM,0BAA0B,CAAC;AAM1F,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAC/B,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAKD,wBAAgB,cAAc,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,mBAAmB;;;;;sCAoGlE,MAAM;EA6C3B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crystaldesign/widget-library",
|
|
3
|
-
"version": "25.8.0-beta.
|
|
3
|
+
"version": "25.8.0-beta.7",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"storybook": "storybook dev -p 6006",
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
"suiteName": "widget-library",
|
|
37
37
|
"outputDirectory": "./test-reports"
|
|
38
38
|
},
|
|
39
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "d160f39c475acc47e32f9641b14a25de2c27cfd4"
|
|
40
40
|
}
|