@magic-spells/cart-panel 0.1.2 → 0.2.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 +381 -190
- package/dist/cart-panel.cjs.css +2 -9
- package/dist/cart-panel.cjs.js +185 -77
- package/dist/cart-panel.cjs.js.map +1 -1
- package/dist/cart-panel.css +2 -9
- package/dist/cart-panel.esm.css +2 -9
- package/dist/cart-panel.esm.js +185 -77
- package/dist/cart-panel.esm.js.map +1 -1
- package/dist/cart-panel.js +1637 -1254
- package/dist/cart-panel.js.map +1 -1
- package/dist/cart-panel.min.css +1 -1
- package/dist/cart-panel.min.js +1 -1
- package/dist/cart-panel.scss +2 -12
- package/package.json +2 -2
- package/src/cart-panel.js +185 -78
- package/src/cart-panel.scss +2 -12
package/dist/cart-panel.cjs.css
CHANGED
|
@@ -189,8 +189,7 @@ cart-panel {
|
|
|
189
189
|
top: 0;
|
|
190
190
|
right: 0;
|
|
191
191
|
width: var(--cart-panel-width);
|
|
192
|
-
height:
|
|
193
|
-
opacity: 0;
|
|
192
|
+
height: 100dvh;
|
|
194
193
|
transform: translateX(100%);
|
|
195
194
|
pointer-events: none;
|
|
196
195
|
z-index: var(--cart-panel-z-index);
|
|
@@ -198,7 +197,7 @@ cart-panel {
|
|
|
198
197
|
box-shadow: var(--cart-panel-shadow);
|
|
199
198
|
border-radius: var(--cart-panel-border-radius);
|
|
200
199
|
overflow: hidden;
|
|
201
|
-
transition:
|
|
200
|
+
transition: transform var(--cart-transition-duration) var(--cart-transition-timing);
|
|
202
201
|
}
|
|
203
202
|
cart-panel.hidden {
|
|
204
203
|
display: none;
|
|
@@ -206,10 +205,4 @@ cart-panel.hidden {
|
|
|
206
205
|
|
|
207
206
|
body.overflow-hidden {
|
|
208
207
|
overflow: hidden;
|
|
209
|
-
position: fixed;
|
|
210
|
-
width: 100%;
|
|
211
|
-
height: 100%;
|
|
212
|
-
left: 0;
|
|
213
|
-
right: 0;
|
|
214
|
-
margin: 0;
|
|
215
208
|
}
|
package/dist/cart-panel.cjs.js
CHANGED
|
@@ -12,7 +12,6 @@ var EventEmitter = require('@magic-spells/event-emitter');
|
|
|
12
12
|
*/
|
|
13
13
|
class CartDialog extends HTMLElement {
|
|
14
14
|
#handleTransitionEnd;
|
|
15
|
-
#scrollPosition = 0;
|
|
16
15
|
#currentCart = null;
|
|
17
16
|
#eventEmitter;
|
|
18
17
|
#isInitialRender = true;
|
|
@@ -35,31 +34,21 @@ class CartDialog extends HTMLElement {
|
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
/**
|
|
38
|
-
*
|
|
37
|
+
* Locks body scrolling
|
|
39
38
|
* @private
|
|
40
39
|
*/
|
|
41
40
|
#lockScroll() {
|
|
42
|
-
|
|
43
|
-
// Save current scroll position
|
|
44
|
-
_.#scrollPosition = window.pageYOffset;
|
|
45
|
-
|
|
46
|
-
// Apply fixed position to body
|
|
41
|
+
// Apply overflow hidden to body
|
|
47
42
|
document.body.classList.add('overflow-hidden');
|
|
48
|
-
document.body.style.top = `-${_.#scrollPosition}px`;
|
|
49
43
|
}
|
|
50
44
|
|
|
51
45
|
/**
|
|
52
|
-
* Restores
|
|
46
|
+
* Restores body scrolling when cart dialog is closed
|
|
53
47
|
* @private
|
|
54
48
|
*/
|
|
55
49
|
#restoreScroll() {
|
|
56
|
-
|
|
57
|
-
// Remove fixed positioning
|
|
50
|
+
// Remove overflow hidden from body
|
|
58
51
|
document.body.classList.remove('overflow-hidden');
|
|
59
|
-
document.body.style.removeProperty('top');
|
|
60
|
-
|
|
61
|
-
// Restore scroll position
|
|
62
|
-
window.scrollTo(0, _.#scrollPosition);
|
|
63
52
|
}
|
|
64
53
|
|
|
65
54
|
/**
|
|
@@ -100,7 +89,18 @@ class CartDialog extends HTMLElement {
|
|
|
100
89
|
return;
|
|
101
90
|
}
|
|
102
91
|
|
|
103
|
-
|
|
92
|
+
// Check if focus-trap already exists, if not create one
|
|
93
|
+
_.focusTrap = _.contentPanel.querySelector('focus-trap');
|
|
94
|
+
if (!_.focusTrap) {
|
|
95
|
+
_.focusTrap = document.createElement('focus-trap');
|
|
96
|
+
|
|
97
|
+
// Move all existing cart-panel content into the focus trap
|
|
98
|
+
const existingContent = Array.from(_.contentPanel.childNodes);
|
|
99
|
+
existingContent.forEach((child) => _.focusTrap.appendChild(child));
|
|
100
|
+
|
|
101
|
+
// Insert focus trap inside the cart-panel
|
|
102
|
+
_.contentPanel.appendChild(_.focusTrap);
|
|
103
|
+
}
|
|
104
104
|
|
|
105
105
|
// Ensure we have labelledby and describedby references
|
|
106
106
|
if (!_.getAttribute('aria-labelledby')) {
|
|
@@ -113,20 +113,15 @@ class CartDialog extends HTMLElement {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
// Insert focus trap before the cart-panel
|
|
117
|
-
_.contentPanel.parentNode.insertBefore(_.focusTrap, _.contentPanel);
|
|
118
|
-
// Move cart-panel inside the focus trap
|
|
119
|
-
_.focusTrap.appendChild(_.contentPanel);
|
|
120
|
-
|
|
121
|
-
// Setup the trap - this will add focus-trap-start/end elements around the content
|
|
122
|
-
_.focusTrap.setupTrap();
|
|
123
|
-
|
|
124
116
|
// Add modal overlay if it doesn't already exist
|
|
125
117
|
if (!_.querySelector('cart-overlay')) {
|
|
126
118
|
_.prepend(document.createElement('cart-overlay'));
|
|
127
119
|
}
|
|
128
120
|
_.#attachListeners();
|
|
129
121
|
_.#bindKeyboard();
|
|
122
|
+
|
|
123
|
+
// Load cart data immediately after component initialization
|
|
124
|
+
_.refreshCart();
|
|
130
125
|
}
|
|
131
126
|
|
|
132
127
|
/**
|
|
@@ -159,6 +154,14 @@ class CartDialog extends HTMLElement {
|
|
|
159
154
|
*/
|
|
160
155
|
#emit(eventName, data = null) {
|
|
161
156
|
this.#eventEmitter.emit(eventName, data);
|
|
157
|
+
|
|
158
|
+
// Also emit as native DOM events for better compatibility
|
|
159
|
+
this.dispatchEvent(
|
|
160
|
+
new CustomEvent(eventName, {
|
|
161
|
+
detail: data,
|
|
162
|
+
bubbles: true,
|
|
163
|
+
})
|
|
164
|
+
);
|
|
162
165
|
}
|
|
163
166
|
|
|
164
167
|
/**
|
|
@@ -240,11 +243,12 @@ class CartDialog extends HTMLElement {
|
|
|
240
243
|
// Success - let smart comparison handle the removal animation
|
|
241
244
|
this.#currentCart = updatedCart;
|
|
242
245
|
this.#renderCartItems(updatedCart);
|
|
243
|
-
this.#
|
|
246
|
+
this.#renderCartPanel(updatedCart);
|
|
244
247
|
|
|
245
248
|
// Emit cart updated and data changed events
|
|
246
|
-
this.#
|
|
247
|
-
this.#emit('cart-dialog:
|
|
249
|
+
const cartWithCalculatedFields = this.#addCalculatedFields(updatedCart);
|
|
250
|
+
this.#emit('cart-dialog:updated', { cart: cartWithCalculatedFields });
|
|
251
|
+
this.#emit('cart-dialog:data-changed', cartWithCalculatedFields);
|
|
248
252
|
} else {
|
|
249
253
|
// Error - reset to ready state
|
|
250
254
|
element.setState('ready');
|
|
@@ -275,12 +279,13 @@ class CartDialog extends HTMLElement {
|
|
|
275
279
|
// Success - update cart data and refresh items
|
|
276
280
|
this.#currentCart = updatedCart;
|
|
277
281
|
this.#renderCartItems(updatedCart);
|
|
278
|
-
this.#
|
|
282
|
+
this.#renderCartPanel(updatedCart);
|
|
279
283
|
element.setState('ready');
|
|
280
284
|
|
|
281
285
|
// Emit cart updated and data changed events
|
|
282
|
-
this.#
|
|
283
|
-
this.#emit('cart-dialog:
|
|
286
|
+
const cartWithCalculatedFields = this.#addCalculatedFields(updatedCart);
|
|
287
|
+
this.#emit('cart-dialog:updated', { cart: cartWithCalculatedFields });
|
|
288
|
+
this.#emit('cart-dialog:data-changed', cartWithCalculatedFields);
|
|
284
289
|
} else {
|
|
285
290
|
// Error - reset to ready state
|
|
286
291
|
element.setState('ready');
|
|
@@ -294,11 +299,52 @@ class CartDialog extends HTMLElement {
|
|
|
294
299
|
});
|
|
295
300
|
}
|
|
296
301
|
|
|
302
|
+
/**
|
|
303
|
+
* Update cart count elements across the site
|
|
304
|
+
* @private
|
|
305
|
+
*/
|
|
306
|
+
#renderCartCount(cartData) {
|
|
307
|
+
if (!cartData) return;
|
|
308
|
+
|
|
309
|
+
// Calculate visible item count (excluding _hide_in_cart items)
|
|
310
|
+
const visibleItems = this.#getVisibleCartItems(cartData);
|
|
311
|
+
const visibleItemCount = visibleItems.reduce((total, item) => total + item.quantity, 0);
|
|
312
|
+
|
|
313
|
+
// Update all cart count elements across the site
|
|
314
|
+
const cartCountElements = document.querySelectorAll('[data-content-cart-count]');
|
|
315
|
+
cartCountElements.forEach((element) => {
|
|
316
|
+
element.textContent = visibleItemCount;
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Update cart subtotal elements across the site
|
|
322
|
+
* @private
|
|
323
|
+
*/
|
|
324
|
+
#renderCartSubtotal(cartData) {
|
|
325
|
+
if (!cartData) return;
|
|
326
|
+
|
|
327
|
+
// Calculate subtotal from all items except those marked to ignore pricing
|
|
328
|
+
const pricedItems = cartData.items.filter(item => {
|
|
329
|
+
const ignorePrice = item.properties?._ignore_price_in_subtotal;
|
|
330
|
+
return !ignorePrice;
|
|
331
|
+
});
|
|
332
|
+
const subtotal = pricedItems.reduce((total, item) => total + (item.line_price || 0), 0);
|
|
333
|
+
|
|
334
|
+
// Update all cart subtotal elements across the site
|
|
335
|
+
const cartSubtotalElements = document.querySelectorAll('[data-content-cart-subtotal]');
|
|
336
|
+
cartSubtotalElements.forEach((element) => {
|
|
337
|
+
// Format as currency (assuming cents, convert to dollars)
|
|
338
|
+
const formatted = (subtotal / 100).toFixed(2);
|
|
339
|
+
element.textContent = `$${formatted}`;
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
297
343
|
/**
|
|
298
344
|
* Update cart items display based on cart data
|
|
299
345
|
* @private
|
|
300
346
|
*/
|
|
301
|
-
#
|
|
347
|
+
#renderCartPanel(cart = null) {
|
|
302
348
|
const cartData = cart || this.#currentCart;
|
|
303
349
|
if (!cartData) return;
|
|
304
350
|
|
|
@@ -314,14 +360,22 @@ class CartDialog extends HTMLElement {
|
|
|
314
360
|
return;
|
|
315
361
|
}
|
|
316
362
|
|
|
317
|
-
//
|
|
318
|
-
|
|
319
|
-
|
|
363
|
+
// Check visible item count for showing/hiding sections
|
|
364
|
+
const visibleItems = this.#getVisibleCartItems(cartData);
|
|
365
|
+
const hasVisibleItems = visibleItems.length > 0;
|
|
366
|
+
|
|
367
|
+
// Show/hide sections based on visible item count
|
|
368
|
+
if (hasVisibleItems) {
|
|
369
|
+
hasItemsSection.style.display = '';
|
|
320
370
|
emptySection.style.display = 'none';
|
|
321
371
|
} else {
|
|
322
372
|
hasItemsSection.style.display = 'none';
|
|
323
|
-
emptySection.style.display = '
|
|
373
|
+
emptySection.style.display = '';
|
|
324
374
|
}
|
|
375
|
+
|
|
376
|
+
// Update cart count and subtotal across the site
|
|
377
|
+
this.#renderCartCount(cartData);
|
|
378
|
+
this.#renderCartSubtotal(cartData);
|
|
325
379
|
}
|
|
326
380
|
|
|
327
381
|
/**
|
|
@@ -373,20 +427,37 @@ class CartDialog extends HTMLElement {
|
|
|
373
427
|
|
|
374
428
|
/**
|
|
375
429
|
* Refresh cart data from server and update components
|
|
430
|
+
* @param {Object} [cartObj=null] - Optional cart object to use instead of fetching
|
|
376
431
|
* @returns {Promise<Object>} Cart data object
|
|
377
432
|
*/
|
|
378
|
-
refreshCart() {
|
|
379
|
-
|
|
433
|
+
refreshCart(cartObj = null) {
|
|
434
|
+
// If cart object is provided, use it directly
|
|
435
|
+
if (cartObj && !cartObj.error) {
|
|
436
|
+
// console.log('Using provided cart data:', cartObj);
|
|
437
|
+
this.#currentCart = cartObj;
|
|
438
|
+
this.#renderCartItems(cartObj);
|
|
439
|
+
this.#renderCartPanel(cartObj);
|
|
440
|
+
|
|
441
|
+
// Emit cart refreshed and data changed events
|
|
442
|
+
const cartWithCalculatedFields = this.#addCalculatedFields(cartObj);
|
|
443
|
+
this.#emit('cart-dialog:refreshed', { cart: cartWithCalculatedFields });
|
|
444
|
+
this.#emit('cart-dialog:data-changed', cartWithCalculatedFields);
|
|
445
|
+
|
|
446
|
+
return Promise.resolve(cartObj);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// Otherwise fetch from server
|
|
380
450
|
return this.getCart().then((cartData) => {
|
|
381
|
-
console.log('Cart data received:', cartData);
|
|
451
|
+
// console.log('Cart data received:', cartData);
|
|
382
452
|
if (cartData && !cartData.error) {
|
|
383
453
|
this.#currentCart = cartData;
|
|
384
454
|
this.#renderCartItems(cartData);
|
|
385
|
-
this.#
|
|
455
|
+
this.#renderCartPanel(cartData);
|
|
386
456
|
|
|
387
457
|
// Emit cart refreshed and data changed events
|
|
388
|
-
this.#
|
|
389
|
-
this.#emit('cart-dialog:
|
|
458
|
+
const cartWithCalculatedFields = this.#addCalculatedFields(cartData);
|
|
459
|
+
this.#emit('cart-dialog:refreshed', { cart: cartWithCalculatedFields });
|
|
460
|
+
this.#emit('cart-dialog:data-changed', cartWithCalculatedFields);
|
|
390
461
|
} else {
|
|
391
462
|
console.warn('Cart data has error or is null:', cartData);
|
|
392
463
|
}
|
|
@@ -400,14 +471,11 @@ class CartDialog extends HTMLElement {
|
|
|
400
471
|
*/
|
|
401
472
|
#removeItemsFromDOM(itemsContainer, newKeysSet) {
|
|
402
473
|
const currentItems = Array.from(itemsContainer.querySelectorAll('cart-item'));
|
|
403
|
-
const itemsToRemove = currentItems.filter((item) => !newKeysSet.has(item.getAttribute('key')));
|
|
404
474
|
|
|
405
|
-
|
|
406
|
-
`Removing ${itemsToRemove.length} items:`,
|
|
407
|
-
itemsToRemove.map((item) => item.getAttribute('key'))
|
|
408
|
-
);
|
|
475
|
+
const itemsToRemove = currentItems.filter((item) => !newKeysSet.has(item.getAttribute('key')));
|
|
409
476
|
|
|
410
477
|
itemsToRemove.forEach((item) => {
|
|
478
|
+
console.log('destroy yourself', item);
|
|
411
479
|
item.destroyYourself();
|
|
412
480
|
});
|
|
413
481
|
}
|
|
@@ -417,11 +485,6 @@ class CartDialog extends HTMLElement {
|
|
|
417
485
|
* @private
|
|
418
486
|
*/
|
|
419
487
|
#addItemsToDOM(itemsContainer, itemsToAdd, newKeys) {
|
|
420
|
-
console.log(
|
|
421
|
-
`Adding ${itemsToAdd.length} items:`,
|
|
422
|
-
itemsToAdd.map((item) => item.key || item.id)
|
|
423
|
-
);
|
|
424
|
-
|
|
425
488
|
// Delay adding new items by 300ms to let cart slide open first
|
|
426
489
|
setTimeout(() => {
|
|
427
490
|
itemsToAdd.forEach((itemData) => {
|
|
@@ -454,6 +517,41 @@ class CartDialog extends HTMLElement {
|
|
|
454
517
|
}, 100);
|
|
455
518
|
}
|
|
456
519
|
|
|
520
|
+
/**
|
|
521
|
+
* Filter cart items to exclude those with _hide_in_cart property
|
|
522
|
+
* @private
|
|
523
|
+
*/
|
|
524
|
+
#getVisibleCartItems(cartData) {
|
|
525
|
+
if (!cartData || !cartData.items) return [];
|
|
526
|
+
return cartData.items.filter((item) => {
|
|
527
|
+
// Check for _hide_in_cart in various possible locations
|
|
528
|
+
const hidden = item.properties?._hide_in_cart;
|
|
529
|
+
|
|
530
|
+
return !hidden;
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Add calculated fields to cart object for events
|
|
536
|
+
* @private
|
|
537
|
+
*/
|
|
538
|
+
#addCalculatedFields(cartData) {
|
|
539
|
+
if (!cartData) return cartData;
|
|
540
|
+
|
|
541
|
+
const visibleItems = this.#getVisibleCartItems(cartData);
|
|
542
|
+
const calculated_count = visibleItems.reduce((total, item) => total + item.quantity, 0);
|
|
543
|
+
const calculated_subtotal = visibleItems.reduce(
|
|
544
|
+
(total, item) => total + (item.line_price || 0),
|
|
545
|
+
0
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
return {
|
|
549
|
+
...cartData,
|
|
550
|
+
calculated_count,
|
|
551
|
+
calculated_subtotal,
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
|
|
457
555
|
/**
|
|
458
556
|
* Render cart items from Shopify cart data with smart comparison
|
|
459
557
|
* @private
|
|
@@ -470,53 +568,53 @@ class CartDialog extends HTMLElement {
|
|
|
470
568
|
return;
|
|
471
569
|
}
|
|
472
570
|
|
|
571
|
+
// Filter out items with _hide_in_cart property
|
|
572
|
+
const visibleItems = this.#getVisibleCartItems(cartData);
|
|
573
|
+
|
|
473
574
|
// Handle initial render - load all items without animation
|
|
474
575
|
if (this.#isInitialRender) {
|
|
475
|
-
console.log('Initial cart render:',
|
|
576
|
+
// console.log('Initial cart render:', visibleItems.length, 'visible items');
|
|
476
577
|
|
|
477
578
|
// Clear existing items
|
|
478
579
|
itemsContainer.innerHTML = '';
|
|
479
580
|
|
|
480
581
|
// Create cart-item elements without animation
|
|
481
|
-
|
|
582
|
+
visibleItems.forEach((itemData) => {
|
|
482
583
|
const cartItem$1 = new cartItem.CartItem(itemData); // No animation
|
|
584
|
+
// const cartItem = document.createElement('cart-item');
|
|
585
|
+
// cartItem.setData(itemData);
|
|
483
586
|
itemsContainer.appendChild(cartItem$1);
|
|
484
587
|
});
|
|
485
588
|
|
|
486
589
|
this.#isInitialRender = false;
|
|
487
|
-
|
|
590
|
+
|
|
488
591
|
return;
|
|
489
592
|
}
|
|
490
593
|
|
|
491
|
-
console.log('Smart rendering cart items:', cartData.items.length, 'items');
|
|
492
|
-
|
|
493
594
|
// Get current DOM items and their keys
|
|
494
595
|
const currentItems = Array.from(itemsContainer.querySelectorAll('cart-item'));
|
|
495
596
|
const currentKeys = new Set(currentItems.map((item) => item.getAttribute('key')));
|
|
496
597
|
|
|
497
|
-
// Get new cart data keys in order
|
|
498
|
-
const newKeys =
|
|
598
|
+
// Get new cart data keys in order (only visible items)
|
|
599
|
+
const newKeys = visibleItems.map((item) => item.key || item.id);
|
|
499
600
|
const newKeysSet = new Set(newKeys);
|
|
500
601
|
|
|
501
602
|
// Step 1: Remove items that are no longer in cart data
|
|
502
603
|
this.#removeItemsFromDOM(itemsContainer, newKeysSet);
|
|
503
604
|
|
|
504
605
|
// Step 2: Add new items that weren't in DOM (with animation delay)
|
|
505
|
-
const itemsToAdd =
|
|
606
|
+
const itemsToAdd = visibleItems.filter(
|
|
506
607
|
(itemData) => !currentKeys.has(itemData.key || itemData.id)
|
|
507
608
|
);
|
|
508
|
-
|
|
509
609
|
this.#addItemsToDOM(itemsContainer, itemsToAdd, newKeys);
|
|
510
|
-
|
|
511
|
-
console.log('Smart rendering complete, container children:', itemsContainer.children.length);
|
|
512
610
|
}
|
|
513
611
|
|
|
514
612
|
/**
|
|
515
613
|
* Set the template function for cart items
|
|
516
614
|
* @param {Function} templateFn - Function that takes item data and returns HTML string
|
|
517
615
|
*/
|
|
518
|
-
setCartItemTemplate(templateFn) {
|
|
519
|
-
cartItem.CartItem.setTemplate(templateFn);
|
|
616
|
+
setCartItemTemplate(templateName, templateFn) {
|
|
617
|
+
cartItem.CartItem.setTemplate(templateName, templateFn);
|
|
520
618
|
}
|
|
521
619
|
|
|
522
620
|
/**
|
|
@@ -532,10 +630,13 @@ class CartDialog extends HTMLElement {
|
|
|
532
630
|
* @param {HTMLElement} [triggerEl=null] - The element that triggered the cart dialog
|
|
533
631
|
* @fires CartDialog#show - Fired when the cart dialog has been shown
|
|
534
632
|
*/
|
|
535
|
-
show(triggerEl = null) {
|
|
633
|
+
show(triggerEl = null, cartObj) {
|
|
536
634
|
const _ = this;
|
|
537
635
|
_.triggerEl = triggerEl || false;
|
|
538
636
|
|
|
637
|
+
// Lock body scrolling
|
|
638
|
+
_.#lockScroll();
|
|
639
|
+
|
|
539
640
|
// Remove the hidden class first to ensure content is rendered
|
|
540
641
|
_.contentPanel.classList.remove('hidden');
|
|
541
642
|
|
|
@@ -543,17 +644,16 @@ class CartDialog extends HTMLElement {
|
|
|
543
644
|
requestAnimationFrame(() => {
|
|
544
645
|
// Update ARIA states
|
|
545
646
|
_.setAttribute('aria-hidden', 'false');
|
|
647
|
+
|
|
546
648
|
if (_.triggerEl) {
|
|
547
649
|
_.triggerEl.setAttribute('aria-expanded', 'true');
|
|
548
650
|
}
|
|
549
651
|
|
|
550
|
-
// Lock body scrolling and save scroll position
|
|
551
|
-
_.#lockScroll();
|
|
552
|
-
|
|
553
652
|
// Focus management
|
|
554
653
|
const firstFocusable = _.querySelector(
|
|
555
654
|
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
556
655
|
);
|
|
656
|
+
|
|
557
657
|
if (firstFocusable) {
|
|
558
658
|
requestAnimationFrame(() => {
|
|
559
659
|
firstFocusable.focus();
|
|
@@ -561,7 +661,7 @@ class CartDialog extends HTMLElement {
|
|
|
561
661
|
}
|
|
562
662
|
|
|
563
663
|
// Refresh cart data when showing
|
|
564
|
-
_.refreshCart();
|
|
664
|
+
_.refreshCart(cartObj);
|
|
565
665
|
|
|
566
666
|
// Emit show event - cart dialog is now visible
|
|
567
667
|
_.#emit('cart-dialog:show', { triggerElement: _.triggerEl });
|
|
@@ -576,23 +676,31 @@ class CartDialog extends HTMLElement {
|
|
|
576
676
|
hide() {
|
|
577
677
|
const _ = this;
|
|
578
678
|
|
|
579
|
-
// Restore body scroll and scroll position
|
|
580
|
-
_.#restoreScroll();
|
|
581
|
-
|
|
582
679
|
// Update ARIA states
|
|
583
680
|
if (_.triggerEl) {
|
|
584
681
|
// remove focus from modal panel first
|
|
585
682
|
_.triggerEl.focus();
|
|
586
683
|
// mark trigger as no longer expanded
|
|
587
684
|
_.triggerEl.setAttribute('aria-expanded', 'false');
|
|
685
|
+
} else {
|
|
686
|
+
// If no trigger element, blur any focused element inside the panel
|
|
687
|
+
const activeElement = document.activeElement;
|
|
688
|
+
if (activeElement && _.contains(activeElement)) {
|
|
689
|
+
activeElement.blur();
|
|
690
|
+
}
|
|
588
691
|
}
|
|
589
692
|
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
693
|
+
requestAnimationFrame(() => {
|
|
694
|
+
// Set aria-hidden to start transition
|
|
695
|
+
// The transitionend event handler will add display:none when complete
|
|
696
|
+
_.setAttribute('aria-hidden', 'true');
|
|
593
697
|
|
|
594
|
-
|
|
595
|
-
|
|
698
|
+
// Emit hide event - cart dialog is now starting to hide
|
|
699
|
+
_.#emit('cart-dialog:hide', { triggerElement: _.triggerEl });
|
|
700
|
+
|
|
701
|
+
// Restore body scroll
|
|
702
|
+
_.#restoreScroll();
|
|
703
|
+
});
|
|
596
704
|
}
|
|
597
705
|
}
|
|
598
706
|
|