@magic-spells/cart-panel 0.1.0 → 0.1.2

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 CHANGED
@@ -42,44 +42,44 @@ Or include directly in your HTML:
42
42
  ```html
43
43
  <!-- Trigger button -->
44
44
  <button aria-haspopup="dialog" aria-controls="my-cart" aria-expanded="false">
45
- Open Cart (3 items)
45
+ Open Cart (3 items)
46
46
  </button>
47
47
 
48
48
  <!-- Cart modal dialog -->
49
49
  <cart-dialog id="my-cart" aria-labelledby="cart-title">
50
- <cart-panel>
51
- <div class="cart-header">
52
- <h2 id="cart-title">Shopping Cart</h2>
53
- <button data-action="hide-cart" aria-label="Close cart">&times;</button>
54
- </div>
55
-
56
- <div class="cart-body">
57
- <!-- Cart items using @magic-spells/cart-item -->
58
- <cart-item data-key="shopify-line-item-123">
59
- <cart-item-content>
60
- <div class="product-info">
61
- <img src="product.jpg" alt="Product" />
62
- <div>
63
- <h4>Awesome T-Shirt</h4>
64
- <div class="price">$29.99</div>
65
- </div>
66
- </div>
67
- <div class="quantity-controls">
68
- <input type="number" data-cart-quantity value="1" min="1" />
69
- <button data-action="remove">Remove</button>
70
- </div>
71
- </cart-item-content>
72
- <cart-item-processing>
73
- <div>Processing...</div>
74
- </cart-item-processing>
75
- </cart-item>
76
- </div>
77
-
78
- <div class="cart-footer">
79
- <div class="cart-total">Total: $29.99</div>
80
- <button class="checkout-btn">Checkout</button>
81
- </div>
82
- </cart-panel>
50
+ <cart-panel>
51
+ <div class="cart-header">
52
+ <h2 id="cart-title">Shopping Cart</h2>
53
+ <button data-action="hide-cart" aria-label="Close cart">&times;</button>
54
+ </div>
55
+
56
+ <div class="cart-body">
57
+ <!-- Cart items using @magic-spells/cart-item -->
58
+ <cart-item data-key="shopify-line-item-123">
59
+ <cart-item-content>
60
+ <div class="product-info">
61
+ <img src="product.jpg" alt="Product" />
62
+ <div>
63
+ <h4>Awesome T-Shirt</h4>
64
+ <div class="price">$29.99</div>
65
+ </div>
66
+ </div>
67
+ <div class="quantity-controls">
68
+ <input type="number" data-cart-quantity value="1" min="1" />
69
+ <button data-action="remove">Remove</button>
70
+ </div>
71
+ </cart-item-content>
72
+ <cart-item-processing>
73
+ <div>Processing...</div>
74
+ </cart-item-processing>
75
+ </cart-item>
76
+ </div>
77
+
78
+ <div class="cart-footer">
79
+ <div class="cart-total">Total: $29.99</div>
80
+ <button class="checkout-btn">Checkout</button>
81
+ </div>
82
+ </cart-panel>
83
83
  </cart-dialog>
84
84
  ```
85
85
 
@@ -132,28 +132,28 @@ Example:
132
132
  ```html
133
133
  <!-- Minimal cart modal -->
134
134
  <cart-dialog id="simple-cart">
135
- <cart-panel>
136
- <h2>Cart</h2>
137
- <button data-action="hide-cart">Close</button>
138
- <!-- Cart content here -->
139
- </cart-panel>
135
+ <cart-panel>
136
+ <h2>Cart</h2>
137
+ <button data-action="hide-cart">Close</button>
138
+ <!-- Cart content here -->
139
+ </cart-panel>
140
140
  </cart-dialog>
141
141
 
142
142
  <!-- Complete cart with all features -->
143
143
  <cart-dialog id="full-cart" aria-modal="true" aria-labelledby="cart-heading">
144
- <cart-overlay></cart-overlay>
145
- <cart-panel>
146
- <header class="cart-header">
147
- <h2 id="cart-heading">Shopping Cart</h2>
148
- <button data-action="hide-cart" aria-label="Close cart">×</button>
149
- </header>
150
- <div class="cart-content">
151
- <!-- Cart items will be rendered here -->
152
- </div>
153
- <footer class="cart-footer">
154
- <button class="checkout-btn">Checkout</button>
155
- </footer>
156
- </cart-panel>
144
+ <cart-overlay></cart-overlay>
145
+ <cart-panel>
146
+ <header class="cart-header">
147
+ <h2 id="cart-heading">Shopping Cart</h2>
148
+ <button data-action="hide-cart" aria-label="Close cart">×</button>
149
+ </header>
150
+ <div class="cart-content">
151
+ <!-- Cart items will be rendered here -->
152
+ </div>
153
+ <footer class="cart-footer">
154
+ <button class="checkout-btn">Checkout</button>
155
+ </footer>
156
+ </cart-panel>
157
157
  </cart-dialog>
158
158
  ```
159
159
 
@@ -166,52 +166,52 @@ The component provides complete styling control through CSS custom properties an
166
166
  ```css
167
167
  /* Customize modal positioning and sizing */
168
168
  cart-dialog {
169
- --cart-panel-width: min(500px, 95vw);
170
- --cart-panel-z-index: 9999;
171
- --cart-overlay-z-index: 9998;
169
+ --cart-panel-width: min(500px, 95vw);
170
+ --cart-panel-z-index: 9999;
171
+ --cart-overlay-z-index: 9998;
172
172
  }
173
173
 
174
174
  /* Customize overlay appearance */
175
175
  cart-overlay {
176
- --cart-overlay-background: rgba(0, 0, 0, 0.3);
177
- --cart-overlay-backdrop-filter: blur(8px);
176
+ --cart-overlay-background: rgba(0, 0, 0, 0.3);
177
+ --cart-overlay-backdrop-filter: blur(8px);
178
178
  }
179
179
 
180
180
  /* Customize panel styling */
181
181
  cart-panel {
182
- --cart-panel-background: #ffffff;
183
- --cart-panel-shadow: -10px 0 30px rgba(0, 0, 0, 0.2);
184
- --cart-panel-border-radius: 12px 0 0 12px;
182
+ --cart-panel-background: #ffffff;
183
+ --cart-panel-shadow: -10px 0 30px rgba(0, 0, 0, 0.2);
184
+ --cart-panel-border-radius: 12px 0 0 12px;
185
185
  }
186
186
 
187
187
  /* Customize animations */
188
188
  cart-dialog {
189
- --cart-transition-duration: 400ms;
190
- --cart-transition-timing: cubic-bezier(0.25, 0.8, 0.25, 1);
189
+ --cart-transition-duration: 400ms;
190
+ --cart-transition-timing: cubic-bezier(0.25, 0.8, 0.25, 1);
191
191
  }
192
192
 
193
193
  /* Style your cart content layout */
194
194
  cart-panel {
195
- display: flex;
196
- flex-direction: column;
195
+ display: flex;
196
+ flex-direction: column;
197
197
  }
198
198
 
199
199
  .cart-header {
200
- padding: 1.5rem;
201
- border-bottom: 1px solid #eee;
202
- background: #f8f9fa;
200
+ padding: 1.5rem;
201
+ border-bottom: 1px solid #eee;
202
+ background: #f8f9fa;
203
203
  }
204
204
 
205
205
  .cart-content {
206
- flex: 1;
207
- overflow-y: auto;
208
- padding: 1rem;
206
+ flex: 1;
207
+ overflow-y: auto;
208
+ padding: 1rem;
209
209
  }
210
210
 
211
211
  .cart-footer {
212
- padding: 1.5rem;
213
- border-top: 1px solid #eee;
214
- background: #f8f9fa;
212
+ padding: 1.5rem;
213
+ border-top: 1px solid #eee;
214
+ background: #f8f9fa;
215
215
  }
216
216
  ```
217
217
 
@@ -238,26 +238,26 @@ The component supports both CSS custom properties and SCSS variables for maximum
238
238
  ```css
239
239
  /* Dramatic slide-in effect */
240
240
  .dramatic-cart {
241
- --cart-transition-duration: 600ms;
242
- --cart-transition-timing: cubic-bezier(0.68, -0.55, 0.265, 1.55);
243
- --cart-overlay-background: rgba(0, 0, 0, 0.4);
244
- --cart-overlay-backdrop-filter: blur(10px);
241
+ --cart-transition-duration: 600ms;
242
+ --cart-transition-timing: cubic-bezier(0.68, -0.55, 0.265, 1.55);
243
+ --cart-overlay-background: rgba(0, 0, 0, 0.4);
244
+ --cart-overlay-backdrop-filter: blur(10px);
245
245
  }
246
246
 
247
247
  /* Subtle minimal styling */
248
248
  .minimal-cart {
249
- --cart-panel-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
250
- --cart-panel-border-radius: 8px;
251
- --cart-transition-duration: 200ms;
252
- --cart-overlay-background: rgba(0, 0, 0, 0.05);
249
+ --cart-panel-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
250
+ --cart-panel-border-radius: 8px;
251
+ --cart-transition-duration: 200ms;
252
+ --cart-overlay-background: rgba(0, 0, 0, 0.05);
253
253
  }
254
254
 
255
255
  /* Mobile-optimized full-width */
256
256
  @media (max-width: 768px) {
257
- .mobile-cart {
258
- --cart-panel-width: 100vw;
259
- --cart-panel-border-radius: 0;
260
- }
257
+ .mobile-cart {
258
+ --cart-panel-width: 100vw;
259
+ --cart-panel-border-radius: 0;
260
+ }
261
261
  }
262
262
  ```
263
263
 
@@ -276,8 +276,8 @@ $cart-overlay-background: rgba(0, 0, 0, 0.25);
276
276
  @import '@magic-spells/cart-panel/css';
277
277
 
278
278
  .my-store cart-dialog {
279
- --cart-transition-duration: 400ms;
280
- --cart-panel-background: #f8f9fa;
279
+ --cart-transition-duration: 400ms;
280
+ --cart-panel-background: #f8f9fa;
281
281
  }
282
282
  ```
283
283
 
@@ -330,32 +330,32 @@ await cartDialog.refreshCart();
330
330
 
331
331
  // Event emitter pattern (recommended)
332
332
  cartDialog
333
- .on('cart-dialog:show', (e) => {
334
- console.log('Cart opened by:', e.detail.triggerElement);
335
- })
336
- .on('cart-dialog:data-changed', (cartData) => {
337
- console.log('Cart updated:', cartData);
338
- // Update header cart count, etc.
339
- });
333
+ .on('cart-dialog:show', (e) => {
334
+ console.log('Cart opened by:', e.detail.triggerElement);
335
+ })
336
+ .on('cart-dialog:data-changed', (cartData) => {
337
+ console.log('Cart updated:', cartData);
338
+ // Update header cart count, etc.
339
+ });
340
340
 
341
341
  // Traditional event listeners (also supported)
342
342
  cartDialog.addEventListener('cart-item:remove', (e) => {
343
- console.log('Remove requested:', e.detail.cartKey);
343
+ console.log('Remove requested:', e.detail.cartKey);
344
344
 
345
- // The component handles the API calls automatically
346
- // Just listen for the data changes
345
+ // The component handles the API calls automatically
346
+ // Just listen for the data changes
347
347
  });
348
348
 
349
349
  cartDialog.addEventListener('cart-item:quantity-change', (e) => {
350
- console.log('Quantity changed:', e.detail.quantity);
351
- // Component automatically syncs with Shopify
350
+ console.log('Quantity changed:', e.detail.quantity);
351
+ // Component automatically syncs with Shopify
352
352
  });
353
353
 
354
354
  // Listen for all cart changes
355
355
  cartDialog.on('cart-dialog:data-changed', (cartData) => {
356
- // Update your UI when cart changes
357
- updateCartBadge(cartData.item_count);
358
- updateCartTotal(cartData.total_price);
356
+ // Update your UI when cart changes
357
+ updateCartBadge(cartData.item_count);
358
+ updateCartTotal(cartData.total_price);
359
359
  });
360
360
  ```
361
361
 
@@ -377,81 +377,46 @@ The component is optimized for:
377
377
 
378
378
  The cart panel automatically integrates with Shopify's AJAX Cart API. Simply add the component to your theme and it handles all cart operations:
379
379
 
380
- ```liquid
380
+ ```html
381
381
  <!-- In your Shopify theme layout -->
382
382
  <button
383
- aria-haspopup="dialog"
384
- aria-controls="shopify-cart"
385
- aria-expanded="false"
386
- class="cart-trigger">
387
- Cart ({{ cart.item_count }})
383
+ aria-haspopup="dialog"
384
+ aria-controls="shopify-cart"
385
+ aria-expanded="false"
386
+ class="cart-trigger">
387
+ Cart ({{ cart.item_count }})
388
388
  </button>
389
389
 
390
390
  <cart-dialog id="shopify-cart" aria-labelledby="cart-heading">
391
- <cart-panel>
392
- <header class="cart-header">
393
- <h2 id="cart-heading">{{ 'cart.general.title' | t }}</h2>
394
- <button data-action="hide-cart" aria-label="{{ 'cart.general.close' | t }}">
395
- {% render 'icon-close' %}
396
- </button>
397
- </header>
398
-
399
- <div class="cart-content">
400
- <!-- Cart items will be populated automatically -->
401
- {% for item in cart.items %}
402
- <cart-item data-key="{{ item.key }}">
403
- <cart-item-content>
404
- <div class="cart-item-layout">
405
- <img src="{{ item.image | img_url: '100x100' }}" alt="{{ item.title | escape }}">
406
- <div class="item-details">
407
- <h4>{{ item.product.title }}</h4>
408
- {% unless item.variant.title == 'Default Title' %}
409
- <p class="variant">{{ item.variant.title }}</p>
410
- {% endunless %}
411
- <p class="price">{{ item.final_price | money }}</p>
412
- </div>
413
- <div class="item-controls">
414
- <input
415
- type="number"
416
- data-cart-quantity
417
- value="{{ item.quantity }}"
418
- min="0">
419
- <button data-action="remove">{{ 'cart.general.remove' | t }}</button>
420
- </div>
421
- </div>
422
- </cart-item-content>
423
- <cart-item-processing>
424
- <div class="spinner"></div>
425
- <span>{{ 'cart.general.updating' | t }}</span>
426
- </cart-item-processing>
427
- </cart-item>
428
- {% endfor %}
429
- </div>
430
-
431
- <footer class="cart-footer">
432
- <div class="cart-total">
433
- {{ 'cart.general.total' | t }}: <span data-cart-total>{{ cart.total_price | money }}</span>
434
- </div>
435
- <a href="/checkout" class="checkout-btn">
436
- {{ 'cart.general.checkout' | t }}
437
- </a>
438
- </footer>
439
- </cart-panel>
391
+ <cart-panel>
392
+ <header class="cart-header">
393
+ <h2 id="cart-heading">Your Cart</h2>
394
+ <button data-action="hide-cart" aria-label="hide cart">X</button>
395
+ </header>
396
+
397
+ <div class="cart-content">
398
+ <!-- Cart items will be populated automatically in javascript -->
399
+ </div>
400
+
401
+ <footer class="cart-footer">
402
+ <div class="cart-total"></div>
403
+ <a href="/checkout" class="button"> Checkout </a>
404
+ </footer>
405
+ </cart-panel>
440
406
  </cart-dialog>
441
407
 
442
408
  <script>
443
- // Optional: Listen for cart updates to sync with other UI elements
444
- document.querySelector('cart-dialog').on('cart-dialog:data-changed', (cartData) => {
445
- // Update cart count in header
446
- document.querySelector('.cart-trigger').textContent = `Cart (${cartData.item_count})`;
447
-
448
- // Update cart total
449
- document.querySelector('[data-cart-total]').textContent =
450
- new Intl.NumberFormat('en-US', {
451
- style: 'currency',
452
- currency: 'USD'
453
- }).format(cartData.total_price / 100);
454
- });
409
+ // Optional: Listen for cart updates to sync with other UI elements
410
+ document.querySelector('cart-dialog').on('cart-dialog:data-changed', (cartData) => {
411
+ // Update cart count in header
412
+ document.querySelector('.cart-trigger').textContent = `Cart (${cartData.item_count})`;
413
+
414
+ // Update cart total
415
+ document.querySelector('[data-cart-total]').textContent = new Intl.NumberFormat('en-US', {
416
+ style: 'currency',
417
+ currency: 'USD',
418
+ }).format(cartData.total_price / 100);
419
+ });
455
420
  </script>
456
421
  ```
457
422
 
@@ -460,63 +425,63 @@ document.querySelector('cart-dialog').on('cart-dialog:data-changed', (cartData)
460
425
  ```javascript
461
426
  // Example for non-Shopify platforms
462
427
  class CustomCartManager {
463
- constructor() {
464
- this.cartDialog = document.querySelector('cart-dialog');
465
- this.setupEventListeners();
466
- }
467
-
468
- setupEventListeners() {
469
- // Listen for cart data changes
470
- this.cartDialog.on('cart-dialog:data-changed', (cartData) => {
471
- this.updateCartUI(cartData);
472
- });
473
-
474
- // Override default cart operations for custom API
475
- this.cartDialog.getCart = this.customGetCart.bind(this);
476
- this.cartDialog.updateCartItem = this.customUpdateCartItem.bind(this);
477
- }
478
-
479
- async customGetCart() {
480
- try {
481
- const response = await fetch('/api/cart');
482
- return await response.json();
483
- } catch (error) {
484
- console.error('Failed to fetch cart:', error);
485
- return { error: true, message: error.message };
486
- }
487
- }
488
-
489
- async customUpdateCartItem(itemId, quantity) {
490
- try {
491
- const response = await fetch('/api/cart/update', {
492
- method: 'POST',
493
- headers: { 'Content-Type': 'application/json' },
494
- body: JSON.stringify({ itemId, quantity }),
495
- });
496
-
497
- if (!response.ok) throw new Error(response.statusText);
498
-
499
- // Return updated cart data
500
- return this.customGetCart();
501
- } catch (error) {
502
- console.error('Failed to update cart:', error);
503
- return { error: true, message: error.message };
504
- }
505
- }
506
-
507
- updateCartUI(cartData) {
508
- // Update cart count in navigation
509
- const cartCount = document.querySelector('.cart-count');
510
- if (cartCount) {
511
- cartCount.textContent = cartData.items?.length || 0;
512
- }
513
-
514
- // Update cart total display
515
- const cartTotal = document.querySelector('.cart-total-display');
516
- if (cartTotal && cartData.total) {
517
- cartTotal.textContent = cartData.total;
518
- }
519
- }
428
+ constructor() {
429
+ this.cartDialog = document.querySelector('cart-dialog');
430
+ this.setupEventListeners();
431
+ }
432
+
433
+ setupEventListeners() {
434
+ // Listen for cart data changes
435
+ this.cartDialog.on('cart-dialog:data-changed', (cartData) => {
436
+ this.updateCartUI(cartData);
437
+ });
438
+
439
+ // Override default cart operations for custom API
440
+ this.cartDialog.getCart = this.customGetCart.bind(this);
441
+ this.cartDialog.updateCartItem = this.customUpdateCartItem.bind(this);
442
+ }
443
+
444
+ async customGetCart() {
445
+ try {
446
+ const response = await fetch('/api/cart');
447
+ return await response.json();
448
+ } catch (error) {
449
+ console.error('Failed to fetch cart:', error);
450
+ return { error: true, message: error.message };
451
+ }
452
+ }
453
+
454
+ async customUpdateCartItem(itemId, quantity) {
455
+ try {
456
+ const response = await fetch('/api/cart/update', {
457
+ method: 'POST',
458
+ headers: { 'Content-Type': 'application/json' },
459
+ body: JSON.stringify({ itemId, quantity }),
460
+ });
461
+
462
+ if (!response.ok) throw new Error(response.statusText);
463
+
464
+ // Return updated cart data
465
+ return this.customGetCart();
466
+ } catch (error) {
467
+ console.error('Failed to update cart:', error);
468
+ return { error: true, message: error.message };
469
+ }
470
+ }
471
+
472
+ updateCartUI(cartData) {
473
+ // Update cart count in navigation
474
+ const cartCount = document.querySelector('.cart-count');
475
+ if (cartCount) {
476
+ cartCount.textContent = cartData.items?.length || 0;
477
+ }
478
+
479
+ // Update cart total display
480
+ const cartTotal = document.querySelector('.cart-total-display');
481
+ if (cartTotal && cartData.total) {
482
+ cartTotal.textContent = cartData.total;
483
+ }
484
+ }
520
485
  }
521
486
 
522
487
  // Initialize
@@ -1,15 +1,18 @@
1
1
  cart-item {
2
2
  --cart-item-processing-duration: 250ms;
3
3
  --cart-item-destroying-duration: 600ms;
4
+ --cart-item-appearing-duration: 400ms;
4
5
  --cart-item-shadow-color: rgba(0, 0, 0, 0.15);
5
6
  --cart-item-shadow-color-strong: rgba(0, 0, 0, 0.5);
6
- --cart-item-processing-bg: rgba(100, 100, 100, 0.2);
7
7
  --cart-item-destroying-bg: rgba(0, 0, 0, 0.1);
8
8
  --cart-item-processing-scale: 0.98;
9
9
  --cart-item-destroying-scale: 0.85;
10
+ --cart-item-appearing-scale: 0.9;
10
11
  --cart-item-processing-blur: 1px;
11
12
  --cart-item-destroying-blur: 10px;
13
+ --cart-item-appearing-blur: 2px;
12
14
  --cart-item-destroying-opacity: 0.2;
15
+ --cart-item-appearing-opacity: 0.5;
13
16
  --cart-item-destroying-brightness: 0.6;
14
17
  --cart-item-destroying-saturate: 0.3;
15
18
  display: block;
@@ -31,57 +34,76 @@ cart-item::after {
31
34
  left: 0px;
32
35
  transition: background-color var(--cart-item-processing-duration) ease;
33
36
  }
34
- cart-item[data-state=ready] cart-item-content {
37
+ cart-item[state=ready] {
38
+ transition: filter var(--cart-item-processing-duration) ease-out, background-color var(--cart-item-processing-duration) ease-out, box-shadow var(--cart-item-processing-duration) ease-out, height var(--cart-item-appearing-duration) ease-out;
39
+ }
40
+ cart-item[state=ready] cart-item-content {
35
41
  transform: scale(1);
36
42
  filter: blur(0px);
37
43
  opacity: 1;
44
+ transition: transform var(--cart-item-appearing-duration) ease-out, filter var(--cart-item-appearing-duration) ease-out, opacity var(--cart-item-appearing-duration) ease-out;
38
45
  }
39
- cart-item[data-state=ready] cart-item-processing {
46
+ cart-item[state=ready] cart-item-processing {
40
47
  opacity: 0;
41
48
  visibility: hidden;
42
49
  }
43
- cart-item[data-state=processing] {
50
+ cart-item[state=processing] {
44
51
  box-shadow: inset 0px 2px 10px var(--cart-item-shadow-color);
45
52
  }
46
- cart-item[data-state=processing]::after {
53
+ cart-item[state=processing]::after {
47
54
  background: rgba(0, 0, 0, 0.15);
48
55
  }
49
- cart-item[data-state=processing] cart-item-content {
56
+ cart-item[state=processing] cart-item-content {
50
57
  transform: scale(var(--cart-item-processing-scale));
51
58
  filter: blur(var(--cart-item-processing-blur));
52
59
  opacity: 0.9;
53
60
  pointer-events: none;
61
+ transition: transform var(--cart-item-processing-duration) ease-out, filter var(--cart-item-processing-duration) ease-out, opacity var(--cart-item-processing-duration) ease-out;
54
62
  }
55
- cart-item[data-state=processing] cart-item-processing {
63
+ cart-item[state=processing] cart-item-processing {
56
64
  opacity: 1;
57
65
  visibility: visible;
58
66
  }
59
- cart-item[data-state=destroying] {
67
+ cart-item[state=destroying] {
60
68
  background-color: var(--cart-item-destroying-bg);
61
- box-shadow: inset 0px 4px 20px var(--cart-item-shadow-color-strong);
69
+ box-shadow: inset 0px 2px 20px var(--cart-item-shadow-color-strong);
62
70
  margin-top: 0px;
63
71
  margin-bottom: 0px;
64
72
  transition: filter var(--cart-item-destroying-duration) ease, background-color var(--cart-item-destroying-duration) ease, box-shadow var(--cart-item-destroying-duration) ease, margin var(--cart-item-destroying-duration) ease;
65
73
  }
66
- cart-item[data-state=destroying]::after {
74
+ cart-item[state=destroying]::after {
67
75
  background: rgba(0, 0, 0, 0.9);
68
76
  transition: background-color var(--cart-item-destroying-duration) ease;
69
77
  }
70
- cart-item[data-state=destroying] cart-item-content {
78
+ cart-item[state=destroying] cart-item-content {
71
79
  transition: transform var(--cart-item-destroying-duration) ease, filter var(--cart-item-destroying-duration) ease, opacity var(--cart-item-destroying-duration) ease;
72
80
  transform: scale(var(--cart-item-destroying-scale));
73
81
  filter: blur(var(--cart-item-destroying-blur)) saturate(var(--cart-item-destroying-saturate));
74
82
  opacity: var(--cart-item-destroying-opacity);
75
83
  pointer-events: none;
76
84
  }
77
- cart-item[data-state=destroying] cart-item-processing {
85
+ cart-item[state=destroying] cart-item-processing {
78
86
  opacity: 0;
79
87
  transition: opacity var(--cart-item-processing-duration) ease;
80
88
  }
89
+ cart-item[state=appearing] {
90
+ height: 0px;
91
+ overflow: hidden;
92
+ transition: height var(--cart-item-appearing-duration) ease-out, filter var(--cart-item-appearing-duration) ease-out, opacity var(--cart-item-appearing-duration) ease-out;
93
+ }
94
+ cart-item[state=appearing] cart-item-content {
95
+ transform: scale(var(--cart-item-appearing-scale));
96
+ filter: blur(var(--cart-item-appearing-blur));
97
+ opacity: var(--cart-item-appearing-opacity);
98
+ transition: transform var(--cart-item-appearing-duration) ease-out, filter var(--cart-item-appearing-duration) ease-out, opacity var(--cart-item-appearing-duration) ease-out;
99
+ }
100
+ cart-item[state=appearing] cart-item-processing {
101
+ opacity: 0;
102
+ visibility: hidden;
103
+ }
81
104
 
82
105
  cart-item-content {
83
106
  display: block;
84
- transition: transform var(--cart-item-processing-duration) ease-out, filter var(--cart-item-processing-duration) ease-out, opacity var(--cart-item-processing-duration) ease-out;
85
107
  }
86
108
 
87
109
  cart-item-processing {
@@ -99,7 +121,29 @@ cart-item-processing {
99
121
  transition: opacity var(--cart-item-processing-duration) ease-out, visibility var(--cart-item-processing-duration) ease-out;
100
122
  z-index: 10;
101
123
  }
124
+ cart-item-processing .cart-item-loader {
125
+ width: 60px;
126
+ aspect-ratio: 2;
127
+ --_g: no-repeat radial-gradient(circle closest-side, #000 90%, #0000);
128
+ background: var(--_g) 0% 50%, var(--_g) 50% 50%, var(--_g) 100% 50%;
129
+ background-size: 33.3333333333% 50%;
130
+ animation: cart-item-loader 1s infinite linear;
131
+ }
102
132
 
133
+ @keyframes cart-item-loader {
134
+ 20% {
135
+ background-position: 0% 0%, 50% 50%, 100% 50%;
136
+ }
137
+ 40% {
138
+ background-position: 0% 100%, 50% 0%, 100% 50%;
139
+ }
140
+ 60% {
141
+ background-position: 0% 50%, 50% 100%, 100% 0%;
142
+ }
143
+ 80% {
144
+ background-position: 0% 50%, 50% 50%, 100% 100%;
145
+ }
146
+ }
103
147
  :root {
104
148
  --cart-dialog-z-index: 1000;
105
149
  --cart-overlay-z-index: 1000;