@magic-spells/cart-progress-bar 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Magic Spells - Cory Schulz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # Cart Progress Bar
2
+
3
+ A beautiful, accessible cart progress bar web component for free shipping thresholds and e-commerce sites.
4
+
5
+ [**Live Demo**](https://magic-spells.github.io/cart-progress-bar/demo/)
6
+
7
+ ## Features
8
+
9
+ - 🎯 **Smart Messaging** - Template-based messages with automatic currency formatting
10
+ - 🎨 **Highly Customizable** - CSS custom properties for easy theming
11
+ - 📱 **Responsive** - Mobile-optimized with responsive breakpoints
12
+ - âš¡ **Smooth Animations** - Buttery smooth transitions and completion effects
13
+ - 🔧 **Easy Integration** - Drop-in web component that works with any framework
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @magic-spells/cart-progress-bar
19
+ ```
20
+
21
+ ## Basic Usage
22
+
23
+ ```html
24
+ <cart-progress-bar
25
+ threshold="75.00"
26
+ current="25.50"
27
+ message-above="🎉 Congratulations! You've qualified for FREE shipping!"
28
+ message-below="Add ${left} more for FREE shipping!">
29
+ <p data-content-cart-progress-message>Add ${left} more for FREE shipping!</p>
30
+ <progress-bar></progress-bar>
31
+ </cart-progress-bar>
32
+ ```
33
+
34
+ ```css
35
+ @import '@magic-spells/cart-progress-bar/css';
36
+ ```
37
+
38
+ ## JavaScript API
39
+
40
+ ```javascript
41
+ const progressBar = document.querySelector('cart-progress-bar');
42
+
43
+ // Set progress percentage directly
44
+ progressBar.setPercent(67);
45
+
46
+ // Update cart amount
47
+ progressBar.setCurrentAmount(45.5);
48
+
49
+ // Change threshold
50
+ progressBar.setThresholdAmount(100.0);
51
+
52
+ // Get current progress info
53
+ const info = progressBar.getProgress();
54
+ console.log(info.percent, info.isComplete, info.thresholdAmount);
55
+
56
+ // Update message templates
57
+ progressBar.setMessages('Almost there!', 'Only ${left} more to go!');
58
+ ```
59
+
60
+ ## Cart Integration
61
+
62
+ The component automatically listens for cart data changes when placed inside a `<cart-panel>` component:
63
+
64
+ ```html
65
+ <cart-panel>
66
+ <cart-progress-bar threshold="75.00" message-below="Add ${left} more for FREE shipping!">
67
+ </cart-progress-bar>
68
+ </cart-panel>
69
+ ```
70
+
71
+ When the cart-panel emits a `cart-dialog:data-changed` event (typically from Shopify cart updates), the progress bar will automatically update with the new cart total.
72
+
73
+ ## Attributes
74
+
75
+ | Attribute | Description | Example |
76
+ | --------------- | ----------------------------------------- | ------------------------------ |
77
+ | `threshold` | Threshold amount for free shipping | `"75.00"` |
78
+ | `current` | Current cart amount | `"25.50"` |
79
+ | `message-above` | Success message when threshold is reached | `"🎉 FREE shipping unlocked!"` |
80
+ | `message-below` | Message template shown below the bar | `"Add ${left} more!"` |
81
+
82
+ ## Customization
83
+
84
+ Use CSS custom properties to customize the appearance:
85
+
86
+ ```css
87
+ cart-progress-bar {
88
+ --cart-progress-bar-height: 16px;
89
+ --cart-progress-bar-fill-bg: #ff6b6b;
90
+ --cart-progress-bar-border-radius: 8px;
91
+ --cart-progress-message-font-size: 1rem;
92
+ --cart-progress-message-color: #333;
93
+ }
94
+ ```
95
+
96
+ ## Available CSS Custom Properties
97
+
98
+ | Property | Description | Default |
99
+ | ----------------------------------- | -------------------------- | ---------- |
100
+ | `--cart-progress-bar-height` | Height of the progress bar | `12px` |
101
+ | `--cart-progress-bar-border-radius` | Border radius | `6px` |
102
+ | `--cart-progress-bar-bg` | Background color | `#e9ecef` |
103
+ | `--cart-progress-bar-fill-bg` | Fill color | `#28a745` |
104
+ | `--cart-progress-bar-complete-bg` | Color when complete | `#007bff` |
105
+ | `--cart-progress-message-font-size` | Message font size | `0.875rem` |
106
+ | `--cart-progress-message-color` | Message text color | `#495057` |
107
+
108
+ ## Message Templates
109
+
110
+ Use `${left}` in your message templates for automatic currency formatting:
111
+
112
+ ```html
113
+ <cart-progress-bar
114
+ message-above="🎉 FREE shipping unlocked!"
115
+ message-below="You need ${left} more for free shipping!">
116
+ <p data-content-cart-progress-message>You need ${left} more for free shipping!</p>
117
+ </cart-progress-bar>
118
+ ```
119
+
120
+ The component automatically:
121
+
122
+ - Shows `message-below` when incomplete (with `${left}` replaced)
123
+ - Shows `message-above` when threshold is reached (success message)
124
+ - Formats the amount as currency (USD by default)
125
+ - Updates messages when amounts change
126
+
127
+ ## States and Classes
128
+
129
+ The component automatically adds CSS classes based on progress:
130
+
131
+ - `.progress-low` - 0-49% progress
132
+ - `.progress-medium` - 50-74% progress
133
+ - `.progress-high` - 75-99% progress
134
+ - `.progress-complete` - 100% progress
135
+
136
+ And data attributes:
137
+
138
+ - `data-complete="true"` - When threshold is reached
139
+ - `data-incomplete="true"` - When threshold is not reached
140
+
141
+ ## Browser Support
142
+
143
+ - Chrome/Edge 88+
144
+ - Firefox 85+
145
+ - Safari 14+
146
+ - All modern browsers with Custom Elements support
147
+
148
+ ## License
149
+
150
+ MIT License - see LICENSE file for details.
@@ -0,0 +1,349 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * ProgressBar helper class for the visual progress bar element
5
+ */
6
+ class ProgressBar extends HTMLElement {
7
+ constructor() {
8
+ super();
9
+ this.#setupProgressBar();
10
+ }
11
+
12
+ #setupProgressBar() {
13
+ this.setAttribute('role', 'progressbar');
14
+ this.setAttribute('aria-valuemin', '0');
15
+ this.setAttribute('aria-valuemax', '100');
16
+ this.setAttribute('aria-valuenow', '0');
17
+
18
+ // Create the visual progress bar
19
+ this.innerHTML = '<div class="progress-bar-fill"></div>';
20
+ }
21
+
22
+ setPercent(percent) {
23
+ const clampedPercent = Math.max(0, Math.min(100, percent));
24
+ this.style.setProperty('--cart-progress-percent', `${clampedPercent}%`);
25
+ this.setAttribute('aria-valuenow', clampedPercent);
26
+ }
27
+ }
28
+
29
+ // Define ProgressBar custom element immediately so it's available for CartProgressBar
30
+ customElements.define('progress-bar', ProgressBar);
31
+
32
+ /**
33
+ * CartProgressBar main component
34
+ */
35
+ class CartProgressBar extends HTMLElement {
36
+ // Private fields
37
+ #minAmount = 0;
38
+ #currentAmount = 0;
39
+ #progressPercent = 0;
40
+ #originalAboveMessage = '';
41
+ #originalBelowMessage = '';
42
+ #progressBar = null;
43
+ #messageElement = null;
44
+
45
+ /**
46
+ * Define which attributes should be observed for changes
47
+ */
48
+ static get observedAttributes() {
49
+ return ['threshold', 'current', 'message-above', 'message-below'];
50
+ }
51
+
52
+ constructor() {
53
+ super();
54
+ this.#init();
55
+ }
56
+
57
+ #init() {
58
+ // Read initial attributes
59
+ this.#minAmount = parseFloat(this.getAttribute('threshold')) || 0;
60
+ this.#currentAmount = parseFloat(this.getAttribute('current')) || 0;
61
+
62
+ // Store original message templates
63
+ this.#originalAboveMessage = this.getAttribute('message-above') || '';
64
+ this.#originalBelowMessage = this.getAttribute('message-below') || '';
65
+ }
66
+
67
+ async connectedCallback() {
68
+ // ensure the child custom element has been registered
69
+ await customElements.whenDefined('progress-bar');
70
+
71
+ if (!customElements.get('progress-bar')) {
72
+ throw new Error('<progress-bar> must be registered before <cart-progress-bar> runs');
73
+ }
74
+
75
+ this.#render();
76
+ this.#updateProgress();
77
+ this.#attachListeners();
78
+ }
79
+
80
+ attributeChangedCallback(name, oldValue, newValue) {
81
+ if (oldValue === newValue) return;
82
+
83
+ switch (name) {
84
+ case 'threshold':
85
+ this.#minAmount = parseFloat(newValue) || 0;
86
+ this.#updateProgress();
87
+ break;
88
+ case 'current':
89
+ this.#currentAmount = parseFloat(newValue) || 0;
90
+ this.#updateProgress();
91
+ break;
92
+ case 'message-above':
93
+ this.#originalAboveMessage = newValue || '';
94
+ this.#updateMessages();
95
+ break;
96
+ case 'message-below':
97
+ this.#originalBelowMessage = newValue || '';
98
+ this.#updateMessages();
99
+ break;
100
+ }
101
+ }
102
+
103
+ #render() {
104
+ // Find or create the message element
105
+ this.#messageElement =
106
+ this.querySelector('[data-content-cart-progress-message]') ||
107
+ this.querySelector('p[data-content-cart-progress-message]');
108
+
109
+ // Find existing progress bar (user can add their own)
110
+ this.#progressBar = this.querySelector('progress-bar');
111
+
112
+ // Create message element if it doesn't exist but we have message templates
113
+ if (!this.#messageElement && (this.#originalAboveMessage || this.#originalBelowMessage)) {
114
+ this.#messageElement = document.createElement('p');
115
+ this.#messageElement.setAttribute('data-content-cart-progress-message', '');
116
+ this.appendChild(this.#messageElement);
117
+ }
118
+
119
+ // Create progress bar if it doesn't exist - add it at the end (below text message)
120
+ if (!this.#progressBar) {
121
+ // Ensure progress-bar is defined before creating
122
+ if (customElements.get('progress-bar')) {
123
+ this.#progressBar = document.createElement('progress-bar');
124
+ this.appendChild(this.#progressBar);
125
+ } else {
126
+ // Fallback: wait for definition
127
+ customElements.whenDefined('progress-bar').then(() => {
128
+ if (!this.#progressBar) {
129
+ this.#progressBar = document.createElement('progress-bar');
130
+ this.appendChild(this.#progressBar);
131
+ this.#updateProgress(); // Update progress after creating
132
+ }
133
+ });
134
+ }
135
+ }
136
+ }
137
+
138
+ #updateProgress() {
139
+ if (this.#minAmount === 0) {
140
+ this.#progressPercent = 100;
141
+ } else {
142
+ this.#progressPercent = Math.min(100, (this.#currentAmount / this.#minAmount) * 100);
143
+ }
144
+
145
+ // Update progress bar
146
+ if (this.#progressBar) {
147
+ this.#progressBar.setPercent(this.#progressPercent);
148
+ }
149
+
150
+ // Update messages
151
+ this.#updateMessages();
152
+
153
+ // Update component state
154
+ this.#updateComponentState();
155
+ }
156
+
157
+ #attachListeners() {
158
+ // Find the nearest cart-panel component
159
+ const cartPanel = this.closest('cart-panel');
160
+
161
+ if (cartPanel) {
162
+ // Listen for cart data changes
163
+ cartPanel.addEventListener('cart-dialog:data-changed', (event) => {
164
+ this.#handleCartDataChange(event);
165
+ });
166
+ }
167
+ }
168
+
169
+ #handleCartDataChange(event) {
170
+ const updatedCart = event.detail;
171
+
172
+ if (updatedCart && typeof updatedCart.total_price !== 'undefined') {
173
+ // Convert from cents to dollars if needed (Shopify typically returns cents)
174
+ const currentAmount = updatedCart.total_price / 100;
175
+ this.setCurrentAmount(currentAmount);
176
+ }
177
+ }
178
+
179
+ #updateMessages() {
180
+ const isComplete = this.#currentAmount >= this.#minAmount;
181
+ const remainingAmount = Math.max(0, this.#minAmount - this.#currentAmount);
182
+
183
+ // Format remaining amount as currency (assuming USD for now)
184
+ const formattedAmount = this.#formatCurrency(remainingAmount);
185
+
186
+ // Update the single message element
187
+ if (this.#messageElement) {
188
+ let messageTemplate;
189
+
190
+ if (isComplete && this.#originalAboveMessage) {
191
+ // Show success message when complete
192
+ messageTemplate = this.#originalAboveMessage;
193
+ } else if (!isComplete) {
194
+ // Show progress message when incomplete
195
+ messageTemplate = this.#originalBelowMessage || this.#originalAboveMessage;
196
+ }
197
+
198
+ if (messageTemplate) {
199
+ const message = messageTemplate.replace(/\$\{left\}/g, formattedAmount);
200
+ this.#messageElement.textContent = message;
201
+ this.#messageElement.style.display = 'block';
202
+ } else {
203
+ // Hide message if no template available for current state
204
+ this.#messageElement.style.display = 'none';
205
+ }
206
+ }
207
+ }
208
+
209
+ #updateComponentState() {
210
+ const isComplete = this.#currentAmount >= this.#minAmount;
211
+
212
+ if (isComplete) {
213
+ this.setAttribute('data-complete', 'true');
214
+ this.removeAttribute('data-incomplete');
215
+ } else {
216
+ this.setAttribute('data-incomplete', 'true');
217
+ this.removeAttribute('data-complete');
218
+ }
219
+
220
+ // Set progress level classes for styling
221
+ this.classList.remove('progress-low', 'progress-medium', 'progress-high', 'progress-complete');
222
+
223
+ if (isComplete) {
224
+ this.classList.add('progress-complete');
225
+ } else if (this.#progressPercent >= 75) {
226
+ this.classList.add('progress-high');
227
+ } else if (this.#progressPercent >= 50) {
228
+ this.classList.add('progress-medium');
229
+ } else {
230
+ this.classList.add('progress-low');
231
+ }
232
+ }
233
+
234
+ #formatCurrency(amount) {
235
+ // Basic currency formatting - could be enhanced with locale/currency options
236
+ return new Intl.NumberFormat('en-US', {
237
+ style: 'currency',
238
+ currency: 'USD',
239
+ minimumFractionDigits: 2,
240
+ maximumFractionDigits: 2,
241
+ }).format(amount);
242
+ }
243
+
244
+ /**
245
+ * Public API: Set the progress percentage directly
246
+ * @param {number} percent - Progress percentage (0-100)
247
+ */
248
+ setPercent(percent) {
249
+ const clampedPercent = Math.max(0, Math.min(100, percent));
250
+ this.#progressPercent = clampedPercent;
251
+
252
+ if (this.#progressBar) {
253
+ this.#progressBar.setPercent(clampedPercent);
254
+ }
255
+
256
+ // Calculate current amount based on percentage
257
+ this.#currentAmount = (clampedPercent / 100) * this.#minAmount;
258
+ this.setAttribute('current', this.#currentAmount.toString());
259
+
260
+ this.#updateMessages();
261
+ this.#updateComponentState();
262
+ }
263
+
264
+ /**
265
+ * Public API: Set the current cart amount
266
+ * @param {number} amount - Current cart amount
267
+ */
268
+ setCurrentAmount(amount) {
269
+ this.#currentAmount = parseFloat(amount) || 0;
270
+ this.setAttribute('current', this.#currentAmount.toString());
271
+ this.#updateProgress();
272
+ }
273
+
274
+ /**
275
+ * Public API: Set the threshold amount for free shipping
276
+ * @param {number} amount - Threshold amount for free shipping
277
+ */
278
+ setThresholdAmount(amount) {
279
+ this.#minAmount = parseFloat(amount) || 0;
280
+ this.setAttribute('threshold', this.#minAmount.toString());
281
+ this.#updateProgress();
282
+ }
283
+
284
+ /**
285
+ * Public API: Set the minimum amount for free shipping (deprecated - use setThresholdAmount)
286
+ * @param {number} amount - Minimum amount threshold
287
+ * @deprecated Use setThresholdAmount instead
288
+ */
289
+ setMinAmount(amount) {
290
+ this.setThresholdAmount(amount);
291
+ }
292
+
293
+ /**
294
+ * Public API: Get current progress information
295
+ */
296
+ getProgress() {
297
+ return {
298
+ currentAmount: this.#currentAmount,
299
+ thresholdAmount: this.#minAmount,
300
+ minAmount: this.#minAmount, // backwards compatibility
301
+ remainingAmount: Math.max(0, this.#minAmount - this.#currentAmount),
302
+ percent: this.#progressPercent,
303
+ isComplete: this.#currentAmount >= this.#minAmount,
304
+ };
305
+ }
306
+
307
+ /**
308
+ * Public API: Update message templates
309
+ * @param {string} aboveMessage - Message template for above the bar
310
+ * @param {string} belowMessage - Message template for below the bar
311
+ */
312
+ setMessages(aboveMessage = null, belowMessage = null) {
313
+ if (aboveMessage !== null) {
314
+ this.#originalAboveMessage = aboveMessage;
315
+ this.setAttribute('message-above', aboveMessage);
316
+ }
317
+
318
+ if (belowMessage !== null) {
319
+ this.#originalBelowMessage = belowMessage;
320
+ this.setAttribute('message-below', belowMessage);
321
+ }
322
+
323
+ this.#updateMessages();
324
+ }
325
+
326
+ // Getters
327
+ get currentAmount() {
328
+ return this.#currentAmount;
329
+ }
330
+ get thresholdAmount() {
331
+ return this.#minAmount;
332
+ }
333
+ get minAmount() {
334
+ return this.#minAmount;
335
+ } // backwards compatibility
336
+ get percent() {
337
+ return this.#progressPercent;
338
+ }
339
+ get isComplete() {
340
+ return this.#currentAmount >= this.#minAmount;
341
+ }
342
+ }
343
+
344
+ // Define CartProgressBar custom element
345
+ customElements.define('cart-progress-bar', CartProgressBar);
346
+
347
+ exports.CartProgressBar = CartProgressBar;
348
+ exports.ProgressBar = ProgressBar;
349
+ //# sourceMappingURL=cart-progress-bar.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart-progress-bar.cjs.js","sources":["../src/cart-progress-bar.js"],"sourcesContent":["import './cart-progress-bar.scss';\n\n/**\n * ProgressBar helper class for the visual progress bar element\n */\nclass ProgressBar extends HTMLElement {\n\tconstructor() {\n\t\tsuper();\n\t\tthis.#setupProgressBar();\n\t}\n\n\t#setupProgressBar() {\n\t\tthis.setAttribute('role', 'progressbar');\n\t\tthis.setAttribute('aria-valuemin', '0');\n\t\tthis.setAttribute('aria-valuemax', '100');\n\t\tthis.setAttribute('aria-valuenow', '0');\n\n\t\t// Create the visual progress bar\n\t\tthis.innerHTML = '<div class=\"progress-bar-fill\"></div>';\n\t}\n\n\tsetPercent(percent) {\n\t\tconst clampedPercent = Math.max(0, Math.min(100, percent));\n\t\tthis.style.setProperty('--cart-progress-percent', `${clampedPercent}%`);\n\t\tthis.setAttribute('aria-valuenow', clampedPercent);\n\t}\n}\n\n// Define ProgressBar custom element immediately so it's available for CartProgressBar\ncustomElements.define('progress-bar', ProgressBar);\n\n/**\n * CartProgressBar main component\n */\nclass CartProgressBar extends HTMLElement {\n\t// Private fields\n\t#minAmount = 0;\n\t#currentAmount = 0;\n\t#progressPercent = 0;\n\t#originalAboveMessage = '';\n\t#originalBelowMessage = '';\n\t#progressBar = null;\n\t#messageElement = null;\n\n\t/**\n\t * Define which attributes should be observed for changes\n\t */\n\tstatic get observedAttributes() {\n\t\treturn ['threshold', 'current', 'message-above', 'message-below'];\n\t}\n\n\tconstructor() {\n\t\tsuper();\n\t\tthis.#init();\n\t}\n\n\t#init() {\n\t\t// Read initial attributes\n\t\tthis.#minAmount = parseFloat(this.getAttribute('threshold')) || 0;\n\t\tthis.#currentAmount = parseFloat(this.getAttribute('current')) || 0;\n\n\t\t// Store original message templates\n\t\tthis.#originalAboveMessage = this.getAttribute('message-above') || '';\n\t\tthis.#originalBelowMessage = this.getAttribute('message-below') || '';\n\t}\n\n\tasync connectedCallback() {\n\t\t// ensure the child custom element has been registered\n\t\tawait customElements.whenDefined('progress-bar');\n\n\t\tif (!customElements.get('progress-bar')) {\n\t\t\tthrow new Error('<progress-bar> must be registered before <cart-progress-bar> runs');\n\t\t}\n\n\t\tthis.#render();\n\t\tthis.#updateProgress();\n\t\tthis.#attachListeners();\n\t}\n\n\tattributeChangedCallback(name, oldValue, newValue) {\n\t\tif (oldValue === newValue) return;\n\n\t\tswitch (name) {\n\t\t\tcase 'threshold':\n\t\t\t\tthis.#minAmount = parseFloat(newValue) || 0;\n\t\t\t\tthis.#updateProgress();\n\t\t\t\tbreak;\n\t\t\tcase 'current':\n\t\t\t\tthis.#currentAmount = parseFloat(newValue) || 0;\n\t\t\t\tthis.#updateProgress();\n\t\t\t\tbreak;\n\t\t\tcase 'message-above':\n\t\t\t\tthis.#originalAboveMessage = newValue || '';\n\t\t\t\tthis.#updateMessages();\n\t\t\t\tbreak;\n\t\t\tcase 'message-below':\n\t\t\t\tthis.#originalBelowMessage = newValue || '';\n\t\t\t\tthis.#updateMessages();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t#render() {\n\t\t// Find or create the message element\n\t\tthis.#messageElement =\n\t\t\tthis.querySelector('[data-content-cart-progress-message]') ||\n\t\t\tthis.querySelector('p[data-content-cart-progress-message]');\n\n\t\t// Find existing progress bar (user can add their own)\n\t\tthis.#progressBar = this.querySelector('progress-bar');\n\n\t\t// Create message element if it doesn't exist but we have message templates\n\t\tif (!this.#messageElement && (this.#originalAboveMessage || this.#originalBelowMessage)) {\n\t\t\tthis.#messageElement = document.createElement('p');\n\t\t\tthis.#messageElement.setAttribute('data-content-cart-progress-message', '');\n\t\t\tthis.appendChild(this.#messageElement);\n\t\t}\n\n\t\t// Create progress bar if it doesn't exist - add it at the end (below text message)\n\t\tif (!this.#progressBar) {\n\t\t\t// Ensure progress-bar is defined before creating\n\t\t\tif (customElements.get('progress-bar')) {\n\t\t\t\tthis.#progressBar = document.createElement('progress-bar');\n\t\t\t\tthis.appendChild(this.#progressBar);\n\t\t\t} else {\n\t\t\t\t// Fallback: wait for definition\n\t\t\t\tcustomElements.whenDefined('progress-bar').then(() => {\n\t\t\t\t\tif (!this.#progressBar) {\n\t\t\t\t\t\tthis.#progressBar = document.createElement('progress-bar');\n\t\t\t\t\t\tthis.appendChild(this.#progressBar);\n\t\t\t\t\t\tthis.#updateProgress(); // Update progress after creating\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t#updateProgress() {\n\t\tif (this.#minAmount === 0) {\n\t\t\tthis.#progressPercent = 100;\n\t\t} else {\n\t\t\tthis.#progressPercent = Math.min(100, (this.#currentAmount / this.#minAmount) * 100);\n\t\t}\n\n\t\t// Update progress bar\n\t\tif (this.#progressBar) {\n\t\t\tthis.#progressBar.setPercent(this.#progressPercent);\n\t\t}\n\n\t\t// Update messages\n\t\tthis.#updateMessages();\n\n\t\t// Update component state\n\t\tthis.#updateComponentState();\n\t}\n\n\t#attachListeners() {\n\t\t// Find the nearest cart-panel component\n\t\tconst cartPanel = this.closest('cart-panel');\n\n\t\tif (cartPanel) {\n\t\t\t// Listen for cart data changes\n\t\t\tcartPanel.addEventListener('cart-dialog:data-changed', (event) => {\n\t\t\t\tthis.#handleCartDataChange(event);\n\t\t\t});\n\t\t}\n\t}\n\n\t#handleCartDataChange(event) {\n\t\tconst updatedCart = event.detail;\n\n\t\tif (updatedCart && typeof updatedCart.total_price !== 'undefined') {\n\t\t\t// Convert from cents to dollars if needed (Shopify typically returns cents)\n\t\t\tconst currentAmount = updatedCart.total_price / 100;\n\t\t\tthis.setCurrentAmount(currentAmount);\n\t\t}\n\t}\n\n\t#updateMessages() {\n\t\tconst isComplete = this.#currentAmount >= this.#minAmount;\n\t\tconst remainingAmount = Math.max(0, this.#minAmount - this.#currentAmount);\n\n\t\t// Format remaining amount as currency (assuming USD for now)\n\t\tconst formattedAmount = this.#formatCurrency(remainingAmount);\n\n\t\t// Update the single message element\n\t\tif (this.#messageElement) {\n\t\t\tlet messageTemplate;\n\n\t\t\tif (isComplete && this.#originalAboveMessage) {\n\t\t\t\t// Show success message when complete\n\t\t\t\tmessageTemplate = this.#originalAboveMessage;\n\t\t\t} else if (!isComplete) {\n\t\t\t\t// Show progress message when incomplete\n\t\t\t\tmessageTemplate = this.#originalBelowMessage || this.#originalAboveMessage;\n\t\t\t}\n\n\t\t\tif (messageTemplate) {\n\t\t\t\tconst message = messageTemplate.replace(/\\$\\{left\\}/g, formattedAmount);\n\t\t\t\tthis.#messageElement.textContent = message;\n\t\t\t\tthis.#messageElement.style.display = 'block';\n\t\t\t} else {\n\t\t\t\t// Hide message if no template available for current state\n\t\t\t\tthis.#messageElement.style.display = 'none';\n\t\t\t}\n\t\t}\n\t}\n\n\t#updateComponentState() {\n\t\tconst isComplete = this.#currentAmount >= this.#minAmount;\n\n\t\tif (isComplete) {\n\t\t\tthis.setAttribute('data-complete', 'true');\n\t\t\tthis.removeAttribute('data-incomplete');\n\t\t} else {\n\t\t\tthis.setAttribute('data-incomplete', 'true');\n\t\t\tthis.removeAttribute('data-complete');\n\t\t}\n\n\t\t// Set progress level classes for styling\n\t\tthis.classList.remove('progress-low', 'progress-medium', 'progress-high', 'progress-complete');\n\n\t\tif (isComplete) {\n\t\t\tthis.classList.add('progress-complete');\n\t\t} else if (this.#progressPercent >= 75) {\n\t\t\tthis.classList.add('progress-high');\n\t\t} else if (this.#progressPercent >= 50) {\n\t\t\tthis.classList.add('progress-medium');\n\t\t} else {\n\t\t\tthis.classList.add('progress-low');\n\t\t}\n\t}\n\n\t#formatCurrency(amount) {\n\t\t// Basic currency formatting - could be enhanced with locale/currency options\n\t\treturn new Intl.NumberFormat('en-US', {\n\t\t\tstyle: 'currency',\n\t\t\tcurrency: 'USD',\n\t\t\tminimumFractionDigits: 2,\n\t\t\tmaximumFractionDigits: 2,\n\t\t}).format(amount);\n\t}\n\n\t/**\n\t * Public API: Set the progress percentage directly\n\t * @param {number} percent - Progress percentage (0-100)\n\t */\n\tsetPercent(percent) {\n\t\tconst clampedPercent = Math.max(0, Math.min(100, percent));\n\t\tthis.#progressPercent = clampedPercent;\n\n\t\tif (this.#progressBar) {\n\t\t\tthis.#progressBar.setPercent(clampedPercent);\n\t\t}\n\n\t\t// Calculate current amount based on percentage\n\t\tthis.#currentAmount = (clampedPercent / 100) * this.#minAmount;\n\t\tthis.setAttribute('current', this.#currentAmount.toString());\n\n\t\tthis.#updateMessages();\n\t\tthis.#updateComponentState();\n\t}\n\n\t/**\n\t * Public API: Set the current cart amount\n\t * @param {number} amount - Current cart amount\n\t */\n\tsetCurrentAmount(amount) {\n\t\tthis.#currentAmount = parseFloat(amount) || 0;\n\t\tthis.setAttribute('current', this.#currentAmount.toString());\n\t\tthis.#updateProgress();\n\t}\n\n\t/**\n\t * Public API: Set the threshold amount for free shipping\n\t * @param {number} amount - Threshold amount for free shipping\n\t */\n\tsetThresholdAmount(amount) {\n\t\tthis.#minAmount = parseFloat(amount) || 0;\n\t\tthis.setAttribute('threshold', this.#minAmount.toString());\n\t\tthis.#updateProgress();\n\t}\n\n\t/**\n\t * Public API: Set the minimum amount for free shipping (deprecated - use setThresholdAmount)\n\t * @param {number} amount - Minimum amount threshold\n\t * @deprecated Use setThresholdAmount instead\n\t */\n\tsetMinAmount(amount) {\n\t\tthis.setThresholdAmount(amount);\n\t}\n\n\t/**\n\t * Public API: Get current progress information\n\t */\n\tgetProgress() {\n\t\treturn {\n\t\t\tcurrentAmount: this.#currentAmount,\n\t\t\tthresholdAmount: this.#minAmount,\n\t\t\tminAmount: this.#minAmount, // backwards compatibility\n\t\t\tremainingAmount: Math.max(0, this.#minAmount - this.#currentAmount),\n\t\t\tpercent: this.#progressPercent,\n\t\t\tisComplete: this.#currentAmount >= this.#minAmount,\n\t\t};\n\t}\n\n\t/**\n\t * Public API: Update message templates\n\t * @param {string} aboveMessage - Message template for above the bar\n\t * @param {string} belowMessage - Message template for below the bar\n\t */\n\tsetMessages(aboveMessage = null, belowMessage = null) {\n\t\tif (aboveMessage !== null) {\n\t\t\tthis.#originalAboveMessage = aboveMessage;\n\t\t\tthis.setAttribute('message-above', aboveMessage);\n\t\t}\n\n\t\tif (belowMessage !== null) {\n\t\t\tthis.#originalBelowMessage = belowMessage;\n\t\t\tthis.setAttribute('message-below', belowMessage);\n\t\t}\n\n\t\tthis.#updateMessages();\n\t}\n\n\t// Getters\n\tget currentAmount() {\n\t\treturn this.#currentAmount;\n\t}\n\tget thresholdAmount() {\n\t\treturn this.#minAmount;\n\t}\n\tget minAmount() {\n\t\treturn this.#minAmount;\n\t} // backwards compatibility\n\tget percent() {\n\t\treturn this.#progressPercent;\n\t}\n\tget isComplete() {\n\t\treturn this.#currentAmount >= this.#minAmount;\n\t}\n}\n\n// Define CartProgressBar custom element\ncustomElements.define('cart-progress-bar', CartProgressBar);\n\n// Export components for external use\nexport { CartProgressBar, ProgressBar };\n"],"names":[],"mappings":"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"}