@magic-spells/cart-progress-bar 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -40
- package/dist/cart-progress-bar.cjs.js +23 -39
- package/dist/cart-progress-bar.cjs.js.map +1 -1
- package/dist/cart-progress-bar.css +17 -79
- package/dist/cart-progress-bar.esm.js +23 -39
- package/dist/cart-progress-bar.esm.js.map +1 -1
- package/dist/cart-progress-bar.js +23 -39
- package/dist/cart-progress-bar.js.map +1 -1
- package/dist/cart-progress-bar.min.css +1 -1
- package/dist/cart-progress-bar.min.js +1 -1
- package/dist/cart-progress-bar.scss +27 -105
- package/package.json +1 -1
- package/src/cart-progress-bar.js +23 -39
- package/src/cart-progress-bar.scss +27 -105
package/README.md
CHANGED
|
@@ -6,8 +6,8 @@ A beautiful, accessible cart progress bar web component for free shipping thresh
|
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
- 🎯 **Smart Messaging** - Template-based messages with
|
|
10
|
-
- 🎨 **
|
|
9
|
+
- 🎯 **Smart Messaging** - Template-based messages with flexible placeholder formats
|
|
10
|
+
- 🎨 **Simplified Theming** - Just 5 CSS variables to control all colors and appearance
|
|
11
11
|
- 📱 **Responsive** - Mobile-optimized with responsive breakpoints
|
|
12
12
|
- ⚡ **Smooth Animations** - Buttery smooth transitions and completion effects
|
|
13
13
|
- 🔧 **Easy Integration** - Drop-in web component that works with any framework
|
|
@@ -25,14 +25,14 @@ npm install @magic-spells/cart-progress-bar
|
|
|
25
25
|
threshold="75.00"
|
|
26
26
|
current="25.50"
|
|
27
27
|
message-above="🎉 Congratulations! You've qualified for FREE shipping!"
|
|
28
|
-
message-below="Add ${
|
|
29
|
-
<p data-content-cart-progress-message>Add ${
|
|
28
|
+
message-below="Add ${ amount } more for FREE shipping!">
|
|
29
|
+
<p data-content-cart-progress-message>Add ${ amount } more for FREE shipping!</p>
|
|
30
30
|
<progress-bar></progress-bar>
|
|
31
31
|
</cart-progress-bar>
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
```css
|
|
35
|
-
@import '@magic-spells/cart-progress-bar/css';
|
|
35
|
+
@import '@magic-spells/cart-progress-bar/css/min';
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
## JavaScript API
|
|
@@ -54,21 +54,30 @@ const info = progressBar.getProgress();
|
|
|
54
54
|
console.log(info.percent, info.isComplete, info.thresholdAmount);
|
|
55
55
|
|
|
56
56
|
// Update message templates
|
|
57
|
-
progressBar.setMessages('Almost there!', 'Only ${
|
|
57
|
+
progressBar.setMessages('Almost there!', 'Only ${ amount } more to go!');
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
## Cart Integration
|
|
61
61
|
|
|
62
|
-
The component automatically listens for cart data changes when placed inside a `<cart-
|
|
62
|
+
The component automatically listens for cart data changes when placed inside a `<cart-dialog>` component from the `@magic-spells/cart-panel` package:
|
|
63
63
|
|
|
64
64
|
```html
|
|
65
|
-
<cart-
|
|
66
|
-
<cart-progress-bar threshold="75.00" message-below="Add ${
|
|
65
|
+
<cart-dialog>
|
|
66
|
+
<cart-progress-bar threshold="75.00" message-below="Add ${ amount } more for FREE shipping!">
|
|
67
67
|
</cart-progress-bar>
|
|
68
|
-
</cart-
|
|
68
|
+
</cart-dialog>
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
When the cart-
|
|
71
|
+
When the cart-dialog emits a `cart-dialog:data-changed` event (typically from Shopify cart API updates), the progress bar will automatically update with the calculated cart subtotal.
|
|
72
|
+
|
|
73
|
+
### Smart Pricing Logic
|
|
74
|
+
|
|
75
|
+
The progress bar uses intelligent pricing calculation:
|
|
76
|
+
|
|
77
|
+
- **Preferred**: Uses `calculated_subtotal` from cart-panel (respects `_ignore_price_in_subtotal` property)
|
|
78
|
+
- **Fallback**: Uses `total_price` for backwards compatibility
|
|
79
|
+
- **Includes**: Bundle items that are hidden in cart but should count toward shipping threshold
|
|
80
|
+
- **Excludes**: Gift items or promotions marked with `_ignore_price_in_subtotal` property
|
|
72
81
|
|
|
73
82
|
## Attributes
|
|
74
83
|
|
|
@@ -77,7 +86,7 @@ When the cart-panel emits a `cart-dialog:data-changed` event (typically from Sho
|
|
|
77
86
|
| `threshold` | Threshold amount for free shipping | `"75.00"` |
|
|
78
87
|
| `current` | Current cart amount | `"25.50"` |
|
|
79
88
|
| `message-above` | Success message when threshold is reached | `"🎉 FREE shipping unlocked!"` |
|
|
80
|
-
| `message-below` | Message template shown below the bar | `"Add ${
|
|
89
|
+
| `message-below` | Message template shown below the bar | `"Add ${ amount } more!"` |
|
|
81
90
|
|
|
82
91
|
## Customization
|
|
83
92
|
|
|
@@ -85,58 +94,87 @@ Use CSS custom properties to customize the appearance:
|
|
|
85
94
|
|
|
86
95
|
```css
|
|
87
96
|
cart-progress-bar {
|
|
97
|
+
/* Core color customization */
|
|
98
|
+
--cart-progress-section-bg: #f8f9fa;
|
|
99
|
+
--cart-progress-section-color: #333;
|
|
100
|
+
--cart-progress-bar-bg: #e9ecef;
|
|
101
|
+
--cart-progress-bar-fill-before: #ff6b6b;
|
|
102
|
+
--cart-progress-bar-fill-after: #28a745;
|
|
103
|
+
|
|
104
|
+
/* Structure (optional) */
|
|
88
105
|
--cart-progress-bar-height: 16px;
|
|
89
|
-
--cart-progress-bar-fill-bg: #ff6b6b;
|
|
90
106
|
--cart-progress-bar-border-radius: 8px;
|
|
91
|
-
--cart-progress-message-font-size: 1rem;
|
|
92
|
-
--cart-progress-message-color: #333;
|
|
93
107
|
}
|
|
94
108
|
```
|
|
95
109
|
|
|
96
110
|
## Available CSS Custom Properties
|
|
97
111
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
|
101
|
-
|
|
|
102
|
-
| `--cart-progress-
|
|
103
|
-
| `--cart-progress-
|
|
104
|
-
| `--cart-progress-bar-
|
|
105
|
-
| `--cart-progress-
|
|
106
|
-
| `--cart-progress-
|
|
112
|
+
### Core Color Variables
|
|
113
|
+
|
|
114
|
+
| Property | Description | Default |
|
|
115
|
+
| --------------------------------- | ------------------------------------ | ------------- |
|
|
116
|
+
| `--cart-progress-section-bg` | Background color of entire component | `transparent` |
|
|
117
|
+
| `--cart-progress-section-color` | Text/foreground color | `#495057` |
|
|
118
|
+
| `--cart-progress-bar-bg` | Background of progress bar track | `#e9ecef` |
|
|
119
|
+
| `--cart-progress-bar-fill-before` | Fill color before reaching threshold | `#28a745` |
|
|
120
|
+
| `--cart-progress-bar-fill-after` | Fill color after reaching threshold | `#007bff` |
|
|
121
|
+
|
|
122
|
+
### Structure Variables (Optional)
|
|
123
|
+
|
|
124
|
+
| Property | Description | Default |
|
|
125
|
+
| --------------------------------------- | --------------------------------- | ------- |
|
|
126
|
+
| `--cart-progress-bar-height` | Height of the progress bar | `12px` |
|
|
127
|
+
| `--cart-progress-bar-border-radius` | Border radius | `6px` |
|
|
128
|
+
| `--cart-progress-bar-shadow` | Box shadow for progress bar | `inset 0 1px 2px rgba(0, 0, 0, 0.1)` |
|
|
129
|
+
| `--cart-progress-bar-transition-duration` | Animation transition duration | `0.3s` |
|
|
107
130
|
|
|
108
131
|
## Message Templates
|
|
109
132
|
|
|
110
|
-
Use
|
|
133
|
+
Use placeholder formats in your message templates and include the currency symbol:
|
|
111
134
|
|
|
112
135
|
```html
|
|
113
136
|
<cart-progress-bar
|
|
114
137
|
message-above="🎉 FREE shipping unlocked!"
|
|
115
|
-
message-below="You need ${
|
|
116
|
-
<p data-content-cart-progress-message>You need ${
|
|
138
|
+
message-below="You need ${ amount } more for free shipping!">
|
|
139
|
+
<p data-content-cart-progress-message>You need ${ amount } more for free shipping!</p>
|
|
117
140
|
</cart-progress-bar>
|
|
118
141
|
```
|
|
119
142
|
|
|
143
|
+
### Supported Placeholder Formats
|
|
144
|
+
|
|
145
|
+
- `{ amount }` - spaces around amount
|
|
146
|
+
- `{amount}` - no spaces
|
|
147
|
+
- `[amount]` - square brackets (with or without spaces)
|
|
148
|
+
|
|
149
|
+
### Examples
|
|
150
|
+
|
|
151
|
+
```html
|
|
152
|
+
<!-- Dollar amounts -->
|
|
153
|
+
<cart-progress-bar message-below="Add ${ amount } more for free shipping!">
|
|
154
|
+
|
|
155
|
+
<!-- Euro amounts -->
|
|
156
|
+
<cart-progress-bar message-below="Only €{amount} left to unlock free delivery!">
|
|
157
|
+
|
|
158
|
+
<!-- With square brackets -->
|
|
159
|
+
<cart-progress-bar message-below="Just £[amount] more to go!">
|
|
160
|
+
```
|
|
161
|
+
|
|
120
162
|
The component automatically:
|
|
121
163
|
|
|
122
|
-
- Shows `message-below` when incomplete (with
|
|
123
|
-
- Shows `message-above` when threshold is reached (success message)
|
|
124
|
-
- Formats
|
|
164
|
+
- Shows `message-below` when incomplete (with placeholder replaced with remaining amount)
|
|
165
|
+
- Shows `message-above` when threshold is reached (success message)
|
|
166
|
+
- Formats amounts as minimal decimal numbers (removes .00 for whole amounts)
|
|
125
167
|
- Updates messages when amounts change
|
|
168
|
+
- Switches between before/after progress bar colors based on completion status
|
|
126
169
|
|
|
127
|
-
## States and
|
|
128
|
-
|
|
129
|
-
The component automatically adds CSS classes based on progress:
|
|
170
|
+
## States and Attributes
|
|
130
171
|
|
|
131
|
-
|
|
132
|
-
- `.progress-medium` - 50-74% progress
|
|
133
|
-
- `.progress-high` - 75-99% progress
|
|
134
|
-
- `.progress-complete` - 100% progress
|
|
172
|
+
The component automatically sets attributes based on completion status:
|
|
135
173
|
|
|
136
|
-
|
|
174
|
+
- `complete="true"` - When threshold is reached
|
|
175
|
+
- `complete="false"` - When threshold is not reached
|
|
137
176
|
|
|
138
|
-
|
|
139
|
-
- `data-incomplete="true"` - When threshold is not reached
|
|
177
|
+
These attributes are used internally to switch between the "before" and "after" progress bar colors.
|
|
140
178
|
|
|
141
179
|
## Browser Support
|
|
142
180
|
|
|
@@ -156,11 +156,11 @@ class CartProgressBar extends HTMLElement {
|
|
|
156
156
|
|
|
157
157
|
#attachListeners() {
|
|
158
158
|
// Find the nearest cart-panel component
|
|
159
|
-
const
|
|
159
|
+
const cartDialog = this.closest('cart-dialog');
|
|
160
160
|
|
|
161
|
-
if (
|
|
161
|
+
if (cartDialog) {
|
|
162
162
|
// Listen for cart data changes
|
|
163
|
-
|
|
163
|
+
cartDialog.addEventListener('cart-dialog:data-changed', (event) => {
|
|
164
164
|
this.#handleCartDataChange(event);
|
|
165
165
|
});
|
|
166
166
|
}
|
|
@@ -169,9 +169,19 @@ class CartProgressBar extends HTMLElement {
|
|
|
169
169
|
#handleCartDataChange(event) {
|
|
170
170
|
const updatedCart = event.detail;
|
|
171
171
|
|
|
172
|
-
if (updatedCart
|
|
173
|
-
//
|
|
174
|
-
|
|
172
|
+
if (updatedCart) {
|
|
173
|
+
// Use calculated_subtotal if available (handles _ignore_price_in_subtotal logic)
|
|
174
|
+
// Otherwise fall back to total_price for backwards compatibility
|
|
175
|
+
let currentAmount = 0;
|
|
176
|
+
|
|
177
|
+
if (typeof updatedCart.calculated_subtotal !== 'undefined') {
|
|
178
|
+
// calculated_subtotal is already in dollars, no conversion needed
|
|
179
|
+
currentAmount = updatedCart.calculated_subtotal / 100;
|
|
180
|
+
} else if (typeof updatedCart.total_price !== 'undefined') {
|
|
181
|
+
// Convert from cents to dollars if needed (Shopify typically returns cents)
|
|
182
|
+
currentAmount = updatedCart.total_price / 100;
|
|
183
|
+
}
|
|
184
|
+
|
|
175
185
|
this.setCurrentAmount(currentAmount);
|
|
176
186
|
}
|
|
177
187
|
}
|
|
@@ -180,8 +190,8 @@ class CartProgressBar extends HTMLElement {
|
|
|
180
190
|
const isComplete = this.#currentAmount >= this.#minAmount;
|
|
181
191
|
const remainingAmount = Math.max(0, this.#minAmount - this.#currentAmount);
|
|
182
192
|
|
|
183
|
-
// Format
|
|
184
|
-
const formattedAmount =
|
|
193
|
+
// Format amount with minimal formatting
|
|
194
|
+
const formattedAmount = remainingAmount.toFixed(2).replace('.00', '');
|
|
185
195
|
|
|
186
196
|
// Update the single message element
|
|
187
197
|
if (this.#messageElement) {
|
|
@@ -196,7 +206,10 @@ class CartProgressBar extends HTMLElement {
|
|
|
196
206
|
}
|
|
197
207
|
|
|
198
208
|
if (messageTemplate) {
|
|
199
|
-
|
|
209
|
+
// Support multiple placeholder formats: {amount}, { amount }, [amount]
|
|
210
|
+
const message = messageTemplate
|
|
211
|
+
.replace(/\{\s*amount\s*\}/g, formattedAmount)
|
|
212
|
+
.replace(/\[\s*amount\s*\]/g, formattedAmount);
|
|
200
213
|
this.#messageElement.textContent = message;
|
|
201
214
|
this.#messageElement.style.display = 'block';
|
|
202
215
|
} else {
|
|
@@ -208,38 +221,9 @@ class CartProgressBar extends HTMLElement {
|
|
|
208
221
|
|
|
209
222
|
#updateComponentState() {
|
|
210
223
|
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
|
-
}
|
|
224
|
+
this.setAttribute('complete', isComplete.toString());
|
|
232
225
|
}
|
|
233
226
|
|
|
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
227
|
|
|
244
228
|
/**
|
|
245
229
|
* Public API: Set the progress percentage directly
|
|
@@ -1 +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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"}
|
|
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 cartDialog = this.closest('cart-dialog');\n\n\t\tif (cartDialog) {\n\t\t\t// Listen for cart data changes\n\t\t\tcartDialog.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) {\n\t\t\t// Use calculated_subtotal if available (handles _ignore_price_in_subtotal logic)\n\t\t\t// Otherwise fall back to total_price for backwards compatibility\n\t\t\tlet currentAmount = 0;\n\n\t\t\tif (typeof updatedCart.calculated_subtotal !== 'undefined') {\n\t\t\t\t// calculated_subtotal is already in dollars, no conversion needed\n\t\t\t\tcurrentAmount = updatedCart.calculated_subtotal / 100;\n\t\t\t} else if (typeof updatedCart.total_price !== 'undefined') {\n\t\t\t\t// Convert from cents to dollars if needed (Shopify typically returns cents)\n\t\t\t\tcurrentAmount = updatedCart.total_price / 100;\n\t\t\t}\n\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 amount with minimal formatting\n\t\tconst formattedAmount = remainingAmount.toFixed(2).replace('.00', '');\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\t// Support multiple placeholder formats: {amount}, { amount }, [amount]\n\t\t\t\tconst message = messageTemplate\n\t\t\t\t\t.replace(/\\{\\s*amount\\s*\\}/g, formattedAmount)\n\t\t\t\t\t.replace(/\\[\\s*amount\\s*\\]/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\t\tthis.setAttribute('complete', isComplete.toString());\n\t}\n\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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"}
|
|
@@ -1,53 +1,33 @@
|
|
|
1
1
|
cart-progress-bar {
|
|
2
2
|
--cart-progress-bar-height: 12px;
|
|
3
3
|
--cart-progress-bar-border-radius: 6px;
|
|
4
|
-
--cart-progress-bar-bg: #e9ecef;
|
|
5
|
-
--cart-progress-bar-fill-bg: #28a745;
|
|
6
|
-
--cart-progress-bar-complete-bg: #007bff;
|
|
7
4
|
--cart-progress-bar-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
8
5
|
--cart-progress-bar-transition-duration: 0.3s;
|
|
9
|
-
--cart-progress-
|
|
10
|
-
--cart-progress-
|
|
11
|
-
--cart-progress-
|
|
12
|
-
--cart-progress-
|
|
13
|
-
--cart-progress-
|
|
6
|
+
--cart-progress-section-bg: transparent;
|
|
7
|
+
--cart-progress-section-color: #495057;
|
|
8
|
+
--cart-progress-bar-bg: #e9ecef;
|
|
9
|
+
--cart-progress-bar-fill-before: #28a745;
|
|
10
|
+
--cart-progress-bar-fill-after: #007bff;
|
|
14
11
|
--cart-progress-percent: 0%;
|
|
12
|
+
--cart-progress-bar-fill-current: var(--cart-progress-bar-fill-before);
|
|
15
13
|
display: block;
|
|
16
14
|
width: 100%;
|
|
17
15
|
max-width: 100%;
|
|
16
|
+
background-color: var(--cart-progress-section-bg);
|
|
17
|
+
color: var(--cart-progress-section-color);
|
|
18
18
|
}
|
|
19
19
|
cart-progress-bar p[data-content-cart-progress-message] {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
color: var(--cart-progress-message-color);
|
|
23
|
-
margin: var(--cart-progress-message-margin) 0;
|
|
24
|
-
text-align: center;
|
|
25
|
-
transition: color var(--cart-progress-bar-transition-duration) ease;
|
|
26
|
-
}
|
|
27
|
-
@media (max-width: 768px) {
|
|
28
|
-
cart-progress-bar p[data-content-cart-progress-message] {
|
|
29
|
-
font-size: calc(var(--cart-progress-message-font-size) * 0.9);
|
|
30
|
-
margin: calc(var(--cart-progress-message-margin) * 0.75) 0;
|
|
31
|
-
}
|
|
20
|
+
color: inherit;
|
|
21
|
+
transition: opacity var(--cart-progress-bar-transition-duration) ease;
|
|
32
22
|
}
|
|
33
|
-
cart-progress-bar[
|
|
34
|
-
color: var(--cart-progress-message-complete-color);
|
|
23
|
+
cart-progress-bar[complete=true] p[data-content-cart-progress-message] {
|
|
35
24
|
font-weight: 600;
|
|
36
25
|
}
|
|
37
|
-
cart-progress-bar
|
|
38
|
-
--cart-progress-bar-fill-
|
|
39
|
-
}
|
|
40
|
-
cart-progress-bar.progress-medium {
|
|
41
|
-
--cart-progress-bar-fill-bg: #ffc107;
|
|
42
|
-
}
|
|
43
|
-
cart-progress-bar.progress-high {
|
|
44
|
-
--cart-progress-bar-fill-bg: #fd7e14;
|
|
45
|
-
}
|
|
46
|
-
cart-progress-bar.progress-complete {
|
|
47
|
-
--cart-progress-bar-fill-bg: var(--cart-progress-bar-complete-bg);
|
|
26
|
+
cart-progress-bar[complete=false] {
|
|
27
|
+
--cart-progress-bar-fill-current: var(--cart-progress-bar-fill-before);
|
|
48
28
|
}
|
|
49
|
-
cart-progress-bar
|
|
50
|
-
|
|
29
|
+
cart-progress-bar[complete=true] {
|
|
30
|
+
--cart-progress-bar-fill-current: var(--cart-progress-bar-fill-after);
|
|
51
31
|
}
|
|
52
32
|
|
|
53
33
|
progress-bar {
|
|
@@ -63,7 +43,7 @@ progress-bar {
|
|
|
63
43
|
progress-bar .progress-bar-fill {
|
|
64
44
|
height: 100%;
|
|
65
45
|
width: var(--cart-progress-percent);
|
|
66
|
-
background-color: var(--cart-progress-bar-fill-
|
|
46
|
+
background-color: var(--cart-progress-bar-fill-current);
|
|
67
47
|
border-radius: var(--cart-progress-bar-border-radius);
|
|
68
48
|
transition: width var(--cart-progress-bar-transition-duration) ease, background-color var(--cart-progress-bar-transition-duration) ease;
|
|
69
49
|
position: relative;
|
|
@@ -80,52 +60,10 @@ progress-bar .progress-bar-fill::after {
|
|
|
80
60
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
|
|
81
61
|
transition: left 0.5s ease;
|
|
82
62
|
}
|
|
83
|
-
cart-progress-bar[
|
|
63
|
+
cart-progress-bar[complete=true] progress-bar .progress-bar-fill::after {
|
|
84
64
|
left: 100%;
|
|
85
65
|
}
|
|
86
66
|
progress-bar:focus-visible {
|
|
87
67
|
outline: 2px solid #007bff;
|
|
88
68
|
outline-offset: 2px;
|
|
89
|
-
}
|
|
90
|
-
@media (prefers-contrast: high) {
|
|
91
|
-
progress-bar {
|
|
92
|
-
--cart-progress-bar-bg: #000000;
|
|
93
|
-
--cart-progress-bar-fill-bg: #ffffff;
|
|
94
|
-
border: 1px solid #ffffff;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
@media (prefers-reduced-motion: reduce) {
|
|
98
|
-
progress-bar .progress-bar-fill {
|
|
99
|
-
transition: none;
|
|
100
|
-
}
|
|
101
|
-
progress-bar .progress-bar-fill::after {
|
|
102
|
-
transition: none;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
@media (max-width: 768px) {
|
|
107
|
-
cart-progress-bar {
|
|
108
|
-
--cart-progress-bar-height: 10px;
|
|
109
|
-
--cart-progress-message-margin: 0.375rem;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
@media (prefers-color-scheme: dark) {
|
|
113
|
-
cart-progress-bar {
|
|
114
|
-
--cart-progress-bar-bg: #343a40;
|
|
115
|
-
--cart-progress-message-color: #e9ecef;
|
|
116
|
-
--cart-progress-message-complete-color: #28a745;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
@media print {
|
|
120
|
-
cart-progress-bar .above-message,
|
|
121
|
-
cart-progress-bar .below-message {
|
|
122
|
-
color: #000000 !important;
|
|
123
|
-
}
|
|
124
|
-
cart-progress-bar progress-bar {
|
|
125
|
-
border: 1px solid #000000;
|
|
126
|
-
}
|
|
127
|
-
cart-progress-bar progress-bar .progress-bar-fill {
|
|
128
|
-
background-color: #000000 !important;
|
|
129
|
-
background-image: none !important;
|
|
130
|
-
}
|
|
131
69
|
}
|
|
@@ -154,11 +154,11 @@ class CartProgressBar extends HTMLElement {
|
|
|
154
154
|
|
|
155
155
|
#attachListeners() {
|
|
156
156
|
// Find the nearest cart-panel component
|
|
157
|
-
const
|
|
157
|
+
const cartDialog = this.closest('cart-dialog');
|
|
158
158
|
|
|
159
|
-
if (
|
|
159
|
+
if (cartDialog) {
|
|
160
160
|
// Listen for cart data changes
|
|
161
|
-
|
|
161
|
+
cartDialog.addEventListener('cart-dialog:data-changed', (event) => {
|
|
162
162
|
this.#handleCartDataChange(event);
|
|
163
163
|
});
|
|
164
164
|
}
|
|
@@ -167,9 +167,19 @@ class CartProgressBar extends HTMLElement {
|
|
|
167
167
|
#handleCartDataChange(event) {
|
|
168
168
|
const updatedCart = event.detail;
|
|
169
169
|
|
|
170
|
-
if (updatedCart
|
|
171
|
-
//
|
|
172
|
-
|
|
170
|
+
if (updatedCart) {
|
|
171
|
+
// Use calculated_subtotal if available (handles _ignore_price_in_subtotal logic)
|
|
172
|
+
// Otherwise fall back to total_price for backwards compatibility
|
|
173
|
+
let currentAmount = 0;
|
|
174
|
+
|
|
175
|
+
if (typeof updatedCart.calculated_subtotal !== 'undefined') {
|
|
176
|
+
// calculated_subtotal is already in dollars, no conversion needed
|
|
177
|
+
currentAmount = updatedCart.calculated_subtotal / 100;
|
|
178
|
+
} else if (typeof updatedCart.total_price !== 'undefined') {
|
|
179
|
+
// Convert from cents to dollars if needed (Shopify typically returns cents)
|
|
180
|
+
currentAmount = updatedCart.total_price / 100;
|
|
181
|
+
}
|
|
182
|
+
|
|
173
183
|
this.setCurrentAmount(currentAmount);
|
|
174
184
|
}
|
|
175
185
|
}
|
|
@@ -178,8 +188,8 @@ class CartProgressBar extends HTMLElement {
|
|
|
178
188
|
const isComplete = this.#currentAmount >= this.#minAmount;
|
|
179
189
|
const remainingAmount = Math.max(0, this.#minAmount - this.#currentAmount);
|
|
180
190
|
|
|
181
|
-
// Format
|
|
182
|
-
const formattedAmount =
|
|
191
|
+
// Format amount with minimal formatting
|
|
192
|
+
const formattedAmount = remainingAmount.toFixed(2).replace('.00', '');
|
|
183
193
|
|
|
184
194
|
// Update the single message element
|
|
185
195
|
if (this.#messageElement) {
|
|
@@ -194,7 +204,10 @@ class CartProgressBar extends HTMLElement {
|
|
|
194
204
|
}
|
|
195
205
|
|
|
196
206
|
if (messageTemplate) {
|
|
197
|
-
|
|
207
|
+
// Support multiple placeholder formats: {amount}, { amount }, [amount]
|
|
208
|
+
const message = messageTemplate
|
|
209
|
+
.replace(/\{\s*amount\s*\}/g, formattedAmount)
|
|
210
|
+
.replace(/\[\s*amount\s*\]/g, formattedAmount);
|
|
198
211
|
this.#messageElement.textContent = message;
|
|
199
212
|
this.#messageElement.style.display = 'block';
|
|
200
213
|
} else {
|
|
@@ -206,38 +219,9 @@ class CartProgressBar extends HTMLElement {
|
|
|
206
219
|
|
|
207
220
|
#updateComponentState() {
|
|
208
221
|
const isComplete = this.#currentAmount >= this.#minAmount;
|
|
209
|
-
|
|
210
|
-
if (isComplete) {
|
|
211
|
-
this.setAttribute('data-complete', 'true');
|
|
212
|
-
this.removeAttribute('data-incomplete');
|
|
213
|
-
} else {
|
|
214
|
-
this.setAttribute('data-incomplete', 'true');
|
|
215
|
-
this.removeAttribute('data-complete');
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Set progress level classes for styling
|
|
219
|
-
this.classList.remove('progress-low', 'progress-medium', 'progress-high', 'progress-complete');
|
|
220
|
-
|
|
221
|
-
if (isComplete) {
|
|
222
|
-
this.classList.add('progress-complete');
|
|
223
|
-
} else if (this.#progressPercent >= 75) {
|
|
224
|
-
this.classList.add('progress-high');
|
|
225
|
-
} else if (this.#progressPercent >= 50) {
|
|
226
|
-
this.classList.add('progress-medium');
|
|
227
|
-
} else {
|
|
228
|
-
this.classList.add('progress-low');
|
|
229
|
-
}
|
|
222
|
+
this.setAttribute('complete', isComplete.toString());
|
|
230
223
|
}
|
|
231
224
|
|
|
232
|
-
#formatCurrency(amount) {
|
|
233
|
-
// Basic currency formatting - could be enhanced with locale/currency options
|
|
234
|
-
return new Intl.NumberFormat('en-US', {
|
|
235
|
-
style: 'currency',
|
|
236
|
-
currency: 'USD',
|
|
237
|
-
minimumFractionDigits: 2,
|
|
238
|
-
maximumFractionDigits: 2,
|
|
239
|
-
}).format(amount);
|
|
240
|
-
}
|
|
241
225
|
|
|
242
226
|
/**
|
|
243
227
|
* Public API: Set the progress percentage directly
|