@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 +21 -0
- package/README.md +150 -0
- package/dist/cart-progress-bar.cjs.js +349 -0
- package/dist/cart-progress-bar.cjs.js.map +1 -0
- package/dist/cart-progress-bar.css +131 -0
- package/dist/cart-progress-bar.esm.js +346 -0
- package/dist/cart-progress-bar.esm.js.map +1 -0
- package/dist/cart-progress-bar.js +355 -0
- package/dist/cart-progress-bar.js.map +1 -0
- package/dist/cart-progress-bar.min.css +1 -0
- package/dist/cart-progress-bar.min.js +1 -0
- package/dist/cart-progress-bar.scss +202 -0
- package/package.json +75 -0
- package/src/cart-progress-bar.js +348 -0
- package/src/cart-progress-bar.scss +202 -0
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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"}
|