@magic-spells/cart-progress-bar 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  A beautiful, accessible cart progress bar web component for free shipping thresholds and e-commerce sites.
4
4
 
5
- [**Live Demo**](https://magic-spells.github.io/cart-progress-bar/demo/)
5
+ [**Live Demo**](https://magic-spells.github.io/cart/)
6
6
 
7
7
  ## Features
8
8
 
9
- - 🎯 **Smart Messaging** - Template-based messages with flexible placeholder formats
10
- - 🎨 **Simplified Theming** - Just 5 CSS variables to control all colors and appearance
9
+ - 🎯 **Smart Messaging** - Template-based messages with an `[amount]` placeholder
10
+ - 🎨 **Simplified Theming** - Just 6 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
@@ -18,6 +18,32 @@ A beautiful, accessible cart progress bar web component for free shipping thresh
18
18
  npm install @magic-spells/cart-progress-bar
19
19
  ```
20
20
 
21
+ The package is **ESM only** — there is no CommonJS build and `exports` declares no
22
+ `require` condition. Import it once, anywhere in your module graph; it registers
23
+ `<cart-progress-bar>` and `<progress-bar>` as a side effect.
24
+
25
+ ```javascript
26
+ import '@magic-spells/cart-progress-bar';
27
+ // or, if you need the classes:
28
+ import { CartProgressBar, ProgressBar } from '@magic-spells/cart-progress-bar';
29
+ ```
30
+
31
+ The stylesheet is extracted into its own file rather than injected by the JS, so
32
+ load it yourself:
33
+
34
+ ```css
35
+ @import '@magic-spells/cart-progress-bar/css'; /* unminified */
36
+ @import '@magic-spells/cart-progress-bar/css/min'; /* minified */
37
+ ```
38
+
39
+ For a plain `<script>` tag with no build step, use the minified UMD bundle, which
40
+ exposes the global `CartProgressBar`:
41
+
42
+ ```html
43
+ <link rel="stylesheet" href="node_modules/@magic-spells/cart-progress-bar/dist/cart-progress-bar.min.css" />
44
+ <script src="node_modules/@magic-spells/cart-progress-bar/dist/cart-progress-bar.min.js"></script>
45
+ ```
46
+
21
47
  ## Basic Usage
22
48
 
23
49
  ```html
@@ -25,16 +51,12 @@ npm install @magic-spells/cart-progress-bar
25
51
  threshold="75.00"
26
52
  current="25.50"
27
53
  message-above="🎉 Congratulations! You've qualified for FREE shipping!"
28
- message-below="Add ${ amount } more for FREE shipping!">
29
- <p data-content-cart-progress-message>Add ${ amount } more for FREE shipping!</p>
54
+ message-below="Add $[amount] more for FREE shipping!">
55
+ <p data-content-cart-progress-message>Add $[amount] more for FREE shipping!</p>
30
56
  <progress-bar></progress-bar>
31
57
  </cart-progress-bar>
32
58
  ```
33
59
 
34
- ```css
35
- @import '@magic-spells/cart-progress-bar/css/min';
36
- ```
37
-
38
60
  ## JavaScript API
39
61
 
40
62
  ```javascript
@@ -54,21 +76,21 @@ const info = progressBar.getProgress();
54
76
  console.log(info.percent, info.isComplete, info.thresholdAmount);
55
77
 
56
78
  // Update message templates
57
- progressBar.setMessages('Almost there!', 'Only ${ amount } more to go!');
79
+ progressBar.setMessages('Almost there!', 'Only $[amount] more to go!');
58
80
  ```
59
81
 
60
82
  ## Cart Integration
61
83
 
62
- The component automatically listens for cart data changes when placed inside a `<cart-dialog>` component from the `@magic-spells/cart-panel` package:
84
+ The component automatically listens for cart data changes when placed inside a `<cart-panel>` component from the `@magic-spells/cart-panel` package:
63
85
 
64
86
  ```html
65
- <cart-dialog>
66
- <cart-progress-bar threshold="75.00" message-below="Add ${ amount } more for FREE shipping!">
87
+ <cart-panel>
88
+ <cart-progress-bar threshold="75.00" message-below="Add $[amount] more for FREE shipping!">
67
89
  </cart-progress-bar>
68
- </cart-dialog>
90
+ </cart-panel>
69
91
  ```
70
92
 
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.
93
+ When the cart-panel emits a `cart-panel:data-changed` event (typically from Shopify cart API updates), the progress bar will automatically update with the calculated cart subtotal. Point it at a different ancestor with `listen-selector` and `listen-event`.
72
94
 
73
95
  ### Smart Pricing Logic
74
96
 
@@ -86,7 +108,10 @@ The progress bar uses intelligent pricing calculation:
86
108
  | `threshold` | Threshold amount for free shipping | `"75.00"` |
87
109
  | `current` | Current cart amount | `"25.50"` |
88
110
  | `message-above` | Success message when threshold is reached | `"🎉 FREE shipping unlocked!"` |
89
- | `message-below` | Message template shown below the bar | `"Add ${ amount } more!"` |
111
+ | `message-below` | Message template shown below the bar | `"Add $[amount] more!"` |
112
+ | `money-format` | Shopify money format string; uses `{{amount}}` placeholders and formats the amount that fills `[amount]` in the messages | `"${{amount}}"` |
113
+ | `listen-selector` | `closest()` selector for the element to listen on | `"cart-panel"` (default) |
114
+ | `listen-event` | Event name to listen for on that element | `"cart-panel:data-changed"` (default) |
90
115
 
91
116
  ## Customization
92
117
 
@@ -95,8 +120,6 @@ Use CSS custom properties to customize the appearance:
95
120
  ```css
96
121
  cart-progress-bar {
97
122
  /* Core color customization */
98
- --cart-progress-section-bg: #f8f9fa;
99
- --cart-progress-section-color: #333;
100
123
  --cart-progress-bar-bg: #e9ecef;
101
124
  --cart-progress-bar-fill-before: #ff6b6b;
102
125
  --cart-progress-bar-fill-after: #28a745;
@@ -104,6 +127,7 @@ cart-progress-bar {
104
127
  /* Structure (optional) */
105
128
  --cart-progress-bar-height: 16px;
106
129
  --cart-progress-bar-border-radius: 8px;
130
+ --cart-progress-bar-transition-duration: 0.3s;
107
131
  }
108
132
  ```
109
133
 
@@ -113,8 +137,6 @@ cart-progress-bar {
113
137
 
114
138
  | Property | Description | Default |
115
139
  | --------------------------------- | ------------------------------------ | ------------- |
116
- | `--cart-progress-section-bg` | Background color of entire component | `transparent` |
117
- | `--cart-progress-section-color` | Text/foreground color | `#495057` |
118
140
  | `--cart-progress-bar-bg` | Background of progress bar track | `#e9ecef` |
119
141
  | `--cart-progress-bar-fill-before` | Fill color before reaching threshold | `#28a745` |
120
142
  | `--cart-progress-bar-fill-after` | Fill color after reaching threshold | `#007bff` |
@@ -125,38 +147,43 @@ cart-progress-bar {
125
147
  | --------------------------------------- | --------------------------------- | ------- |
126
148
  | `--cart-progress-bar-height` | Height of the progress bar | `12px` |
127
149
  | `--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
150
  | `--cart-progress-bar-transition-duration` | Animation transition duration | `0.3s` |
130
151
 
152
+ Two more are set for you and are not meant to be overridden:
153
+ `--cart-progress-percent` (the fill width, written by JavaScript) and
154
+ `--cart-progress-bar-fill-current` (switched between the before/after colors by the
155
+ `complete` attribute).
156
+
131
157
  ## Message Templates
132
158
 
133
- Use placeholder formats in your message templates and include the currency symbol:
159
+ Use the `[amount]` placeholder in your message templates and include the currency symbol:
134
160
 
135
161
  ```html
136
162
  <cart-progress-bar
137
163
  message-above="🎉 FREE shipping unlocked!"
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>
164
+ message-below="You need $[amount] more for free shipping!">
165
+ <p data-content-cart-progress-message>You need $[amount] more for free shipping!</p>
140
166
  </cart-progress-bar>
141
167
  ```
142
168
 
143
- ### Supported Placeholder Formats
169
+ ### Supported Placeholder Format
144
170
 
145
- - `{ amount }` - spaces around amount
146
- - `{amount}` - no spaces
147
- - `[amount]` - square brackets (with or without spaces)
171
+ `[amount]` square brackets, with or without spaces inside (`[amount]`, `[ amount ]`). Square
172
+ brackets avoid clashing with Liquid's `{{ }}` and JavaScript template literals, so curly-brace
173
+ forms are **not** substituted in messages. (`money-format` is the one place `{{amount}}` is used,
174
+ and it formats the number that fills `[amount]`.)
148
175
 
149
176
  ### Examples
150
177
 
151
178
  ```html
152
179
  <!-- Dollar amounts -->
153
- <cart-progress-bar message-below="Add ${ amount } more for free shipping!">
180
+ <cart-progress-bar message-below="Add $[amount] more for free shipping!">
154
181
 
155
182
  <!-- Euro amounts -->
156
- <cart-progress-bar message-below="Only €{amount} left to unlock free delivery!">
183
+ <cart-progress-bar message-below="Only €[amount] left to unlock free delivery!">
157
184
 
158
- <!-- With square brackets -->
159
- <cart-progress-bar message-below="Just £[amount] more to go!">
185
+ <!-- Spaces inside the brackets work too -->
186
+ <cart-progress-bar message-below="Just £[ amount ] more to go!">
160
187
  ```
161
188
 
162
189
  The component automatically:
@@ -183,6 +210,36 @@ These attributes are used internally to switch between the "before" and "after"
183
210
  - Safari 14+
184
211
  - All modern browsers with Custom Elements support
185
212
 
213
+ ## Development
214
+
215
+ - `npm run build` — production build to `dist/` via `scripts/build.mjs` (Vite +
216
+ Rolldown, Lightning CSS, Terser). Emits exactly four files: the unminified ESM
217
+ bundle, the minified UMD bundle, and the plain and minified stylesheets.
218
+ - `npm run dev` — watch build into `demo/dist/`, plus a Vite dev server for
219
+ `demo/` at [http://localhost:3001](http://localhost:3001) with live reload.
220
+ `host: true`, so the demo also answers on the LAN address Vite prints.
221
+ - `npm run lint` / `npm run format` — ESLint / Prettier.
222
+
223
+ `scripts/build.mjs` is the whole build configuration (`configFile: false`) —
224
+ there is no `vite.config.js`. Sourcemaps are emitted for the demo only; the
225
+ published `dist/` carries none, because Rolldown inlines `sourcesContent` and
226
+ would ship `src/` a second time inside them.
227
+
228
+ ### Build output
229
+
230
+ | File | Format |
231
+ | ----------------------------- | ----------------------------------------- |
232
+ | `cart-progress-bar.esm.js` | Unminified ESM |
233
+ | `cart-progress-bar.min.js` | Minified UMD, global `CartProgressBar` |
234
+ | `cart-progress-bar.css` | Plain stylesheet |
235
+ | `cart-progress-bar.min.css` | Minified stylesheet |
236
+
186
237
  ## License
187
238
 
188
239
  MIT License - see LICENSE file for details.
240
+
241
+ ---
242
+
243
+ <p align="center">
244
+ Made by <a href="https://github.com/coryschulz">Cory Schulz</a>
245
+ </p>
@@ -1,69 +1,48 @@
1
1
  cart-progress-bar {
2
2
  --cart-progress-bar-height: 12px;
3
3
  --cart-progress-bar-border-radius: 6px;
4
- --cart-progress-bar-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
5
- --cart-progress-bar-transition-duration: 0.3s;
6
- --cart-progress-section-bg: transparent;
7
- --cart-progress-section-color: #495057;
4
+ --cart-progress-bar-transition-duration: .3s;
8
5
  --cart-progress-bar-bg: #e9ecef;
9
6
  --cart-progress-bar-fill-before: #28a745;
10
7
  --cart-progress-bar-fill-after: #007bff;
11
8
  --cart-progress-percent: 0%;
12
9
  --cart-progress-bar-fill-current: var(--cart-progress-bar-fill-before);
13
- display: block;
14
10
  width: 100%;
15
- max-width: 100%;
16
- background-color: var(--cart-progress-section-bg);
17
- color: var(--cart-progress-section-color);
11
+ display: block;
18
12
  }
13
+
19
14
  cart-progress-bar p[data-content-cart-progress-message] {
20
- color: inherit;
21
15
  transition: opacity var(--cart-progress-bar-transition-duration) ease;
22
16
  }
23
- cart-progress-bar[complete=true] p[data-content-cart-progress-message] {
17
+
18
+ cart-progress-bar[complete="true"] p[data-content-cart-progress-message] {
24
19
  font-weight: 600;
25
20
  }
26
- cart-progress-bar[complete=false] {
21
+
22
+ cart-progress-bar[complete="false"] {
27
23
  --cart-progress-bar-fill-current: var(--cart-progress-bar-fill-before);
28
24
  }
29
- cart-progress-bar[complete=true] {
25
+
26
+ cart-progress-bar[complete="true"] {
30
27
  --cart-progress-bar-fill-current: var(--cart-progress-bar-fill-after);
31
28
  }
32
29
 
33
30
  progress-bar {
34
- display: block;
35
31
  width: 100%;
36
32
  height: var(--cart-progress-bar-height);
37
33
  background-color: var(--cart-progress-bar-bg);
38
34
  border-radius: var(--cart-progress-bar-border-radius);
39
- box-shadow: var(--cart-progress-bar-shadow);
40
- overflow: hidden;
35
+ display: block;
41
36
  position: relative;
37
+ overflow: hidden;
42
38
  }
39
+
43
40
  progress-bar .progress-bar-fill {
44
41
  height: 100%;
45
42
  width: var(--cart-progress-percent);
46
43
  background-color: var(--cart-progress-bar-fill-current);
47
44
  border-radius: var(--cart-progress-bar-border-radius);
48
- transition: width var(--cart-progress-bar-transition-duration) ease, background-color var(--cart-progress-bar-transition-duration) ease;
49
- position: relative;
50
- background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.1) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0.1) 75%, transparent 75%, transparent);
51
- background-size: 12px 12px;
52
- }
53
- progress-bar .progress-bar-fill::after {
54
- content: "";
55
- position: absolute;
56
- top: 0;
57
- left: -100%;
58
- width: 100%;
59
- height: 100%;
60
- background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
61
- transition: left 0.5s ease;
62
- }
63
- cart-progress-bar[complete=true] progress-bar .progress-bar-fill::after {
64
- left: 100%;
45
+ transition: width var(--cart-progress-bar-transition-duration) ease,
46
+ background-color var(--cart-progress-bar-transition-duration) ease;
65
47
  }
66
- progress-bar:focus-visible {
67
- outline: 2px solid #007bff;
68
- outline-offset: 2px;
69
- }
48
+ /*$vite$:1*/
@@ -1,222 +1,212 @@
1
+ //#region src/cart-progress-bar.js
1
2
  /**
2
- * ProgressBar helper class for the visual progress bar element
3
- */
4
- class ProgressBar extends HTMLElement {
3
+ * ProgressBar helper class for the visual progress bar element
4
+ */
5
+ var ProgressBar = class extends HTMLElement {
5
6
  constructor() {
6
7
  super();
7
8
  const _ = this;
8
- _.setAttribute('role', 'progressbar');
9
- _.setAttribute('aria-valuemin', '0');
10
- _.setAttribute('aria-valuemax', '100');
11
- _.setAttribute('aria-valuenow', '0');
12
- _.innerHTML = '<div class="progress-bar-fill"></div>';
9
+ _.setAttribute("role", "progressbar");
10
+ _.setAttribute("aria-valuemin", "0");
11
+ _.setAttribute("aria-valuemax", "100");
12
+ _.setAttribute("aria-valuenow", "0");
13
+ _.innerHTML = "<div class=\"progress-bar-fill\"></div>";
13
14
  }
14
-
15
15
  setPercent(percent) {
16
16
  const p = Math.max(0, Math.min(100, percent));
17
- this.style.setProperty('--cart-progress-percent', `${p}%`);
18
- this.setAttribute('aria-valuenow', p);
17
+ this.style.setProperty("--cart-progress-percent", `${p}%`);
18
+ this.setAttribute("aria-valuenow", p);
19
19
  }
20
- }
21
-
22
- // Define ProgressBar custom element immediately so it's available for CartProgressBar
23
- customElements.define('progress-bar', ProgressBar);
24
-
20
+ };
21
+ if (!customElements.get("progress-bar")) customElements.define("progress-bar", ProgressBar);
25
22
  /**
26
- * CartProgressBar main component
27
- */
28
- class CartProgressBar extends HTMLElement {
29
- // Private fields
23
+ * CartProgressBar main component
24
+ */
25
+ var CartProgressBar = class extends HTMLElement {
30
26
  #threshold = 0;
31
27
  #current = 0;
32
28
  #percent = 0;
33
- #msgAbove = '';
34
- #msgBelow = '';
29
+ #msgAbove = "";
30
+ #msgBelow = "";
35
31
  #bar = null;
36
32
  #msgEl = null;
37
33
  #moneyFmt = null;
38
34
  #debounce = null;
39
-
35
+ #listenSelector = "cart-panel";
36
+ #listenEvent = "cart-panel:data-changed";
37
+ #listenTarget = null;
38
+ #listenHandler = null;
40
39
  static get observedAttributes() {
41
- return ['threshold', 'current', 'message-above', 'message-below', 'money-format'];
40
+ return [
41
+ "threshold",
42
+ "current",
43
+ "message-above",
44
+ "message-below",
45
+ "money-format",
46
+ "listen-selector",
47
+ "listen-event"
48
+ ];
42
49
  }
43
-
44
50
  constructor() {
45
51
  super();
46
52
  const _ = this;
47
- _.#threshold = parseFloat(_.getAttribute('threshold')) || 0;
48
- _.#current = parseFloat(_.getAttribute('current')) || 0;
49
- _.#msgAbove = _.getAttribute('message-above') || '';
50
- _.#msgBelow = _.getAttribute('message-below') || '';
51
- _.#moneyFmt = _.getAttribute('money-format');
53
+ _.#threshold = parseFloat(_.getAttribute("threshold")) || 0;
54
+ _.#current = parseFloat(_.getAttribute("current")) || 0;
55
+ _.#msgAbove = _.getAttribute("message-above") || "";
56
+ _.#msgBelow = _.getAttribute("message-below") || "";
57
+ _.#moneyFmt = _.getAttribute("money-format");
58
+ _.#listenSelector = _.getAttribute("listen-selector") || "cart-panel";
59
+ _.#listenEvent = _.getAttribute("listen-event") || "cart-panel:data-changed";
52
60
  }
53
-
54
61
  async connectedCallback() {
55
- await customElements.whenDefined('progress-bar');
62
+ await customElements.whenDefined("progress-bar");
56
63
  this.#render();
57
64
  this.#updateProgress();
58
65
  this.#attachListeners();
59
66
  }
60
-
61
67
  disconnectedCallback() {
62
68
  if (this.#debounce) clearTimeout(this.#debounce);
69
+ this.#detachListeners();
63
70
  }
64
-
65
71
  attributeChangedCallback(name, oldVal, newVal) {
66
72
  if (oldVal === newVal) return;
67
73
  const _ = this;
68
-
69
74
  switch (name) {
70
- case 'threshold':
75
+ case "threshold":
71
76
  _.#threshold = parseFloat(newVal) || 0;
72
77
  _.#updateProgress();
73
78
  break;
74
- case 'current':
79
+ case "current":
75
80
  _.#current = parseFloat(newVal) || 0;
76
81
  _.#updateProgress();
77
82
  break;
78
- case 'message-above':
79
- _.#msgAbove = newVal || '';
83
+ case "message-above":
84
+ _.#msgAbove = newVal || "";
80
85
  _.#updateMessages();
81
86
  break;
82
- case 'message-below':
83
- _.#msgBelow = newVal || '';
87
+ case "message-below":
88
+ _.#msgBelow = newVal || "";
84
89
  _.#updateMessages();
85
90
  break;
86
- case 'money-format':
91
+ case "money-format":
87
92
  _.#moneyFmt = newVal;
88
93
  _.#updateMessages();
89
94
  break;
95
+ case "listen-selector":
96
+ _.#listenSelector = newVal || "cart-panel";
97
+ _.#detachListeners();
98
+ _.#attachListeners();
99
+ break;
100
+ case "listen-event":
101
+ _.#detachListeners();
102
+ _.#listenEvent = newVal || "cart-panel:data-changed";
103
+ _.#attachListeners();
90
104
  }
91
105
  }
92
-
93
106
  #render() {
94
107
  const _ = this;
95
- // Find existing elements or create them
96
- _.#msgEl = _.querySelector('[data-content-cart-progress-message]');
97
- _.#bar = _.querySelector('progress-bar');
98
-
99
- // Create message element if needed
108
+ _.#msgEl = _.querySelector("[data-content-cart-progress-message]");
109
+ _.#bar = _.querySelector("progress-bar");
100
110
  if (!_.#msgEl && (_.#msgAbove || _.#msgBelow)) {
101
- _.#msgEl = document.createElement('p');
102
- _.#msgEl.setAttribute('data-content-cart-progress-message', '');
111
+ _.#msgEl = document.createElement("p");
112
+ _.#msgEl.setAttribute("data-content-cart-progress-message", "");
103
113
  _.appendChild(_.#msgEl);
104
114
  }
105
-
106
- // Create progress bar if needed
107
115
  if (!_.#bar) {
108
- _.#bar = document.createElement('progress-bar');
116
+ _.#bar = document.createElement("progress-bar");
109
117
  _.appendChild(_.#bar);
110
118
  }
111
119
  }
112
-
113
120
  #updateProgress() {
114
121
  const _ = this;
115
122
  const converted = _.#converted();
116
- _.#percent = converted === 0 ? 100 : Math.min(100, (_.#current / converted) * 100);
123
+ _.#percent = converted === 0 ? 100 : Math.min(100, _.#current / converted * 100);
117
124
  if (_.#bar) _.#bar.setPercent(_.#percent);
118
125
  _.#updateState(converted);
119
126
  }
120
-
121
127
  #attachListeners() {
122
128
  const _ = this;
123
- const panel = _.closest('cart-panel');
124
- if (panel) {
125
- panel.addEventListener('cart-panel:data-changed', (e) => _.#onCartChange(e));
129
+ const target = _.closest(_.#listenSelector);
130
+ if (target) {
131
+ _.#listenHandler = (e) => _.#onCartChange(e);
132
+ _.#listenTarget = target;
133
+ target.addEventListener(_.#listenEvent, _.#listenHandler);
134
+ }
135
+ }
136
+ #detachListeners() {
137
+ const _ = this;
138
+ if (_.#listenTarget && _.#listenHandler) {
139
+ _.#listenTarget.removeEventListener(_.#listenEvent, _.#listenHandler);
140
+ _.#listenTarget = null;
141
+ _.#listenHandler = null;
126
142
  }
127
143
  }
128
-
129
144
  #onCartChange(event) {
130
145
  const _ = this;
131
146
  const cart = event.detail;
132
147
  if (!cart) return;
133
-
134
148
  if (_.#debounce) clearTimeout(_.#debounce);
135
149
  _.#debounce = setTimeout(() => {
136
150
  _.#debounce = null;
137
- // Use calculated_subtotal if available, else total_price (both in cents)
138
151
  const amt = (cart.calculated_subtotal ?? cart.total_price ?? 0) / 100;
139
152
  _.setCurrentAmount(amt);
140
153
  }, 100);
141
154
  }
142
-
143
155
  #updateState(converted) {
144
156
  const _ = this;
145
157
  const complete = _.#current >= converted;
146
158
  const remaining = Math.max(0, converted - _.#current);
147
159
  const formatted = _.#fmtMoney(remaining);
148
-
149
- // Update message
150
160
  if (_.#msgEl) {
151
- let tpl = complete ? _.#msgAbove : (_.#msgBelow || _.#msgAbove);
161
+ let tpl = complete ? _.#msgAbove : _.#msgBelow || _.#msgAbove;
152
162
  if (tpl) {
153
163
  _.#msgEl.textContent = tpl.replace(/\[\s*amount\s*\]/g, formatted);
154
- _.#msgEl.style.display = 'block';
155
- } else {
156
- _.#msgEl.style.display = 'none';
157
- }
164
+ _.#msgEl.style.display = "block";
165
+ } else _.#msgEl.style.display = "none";
158
166
  }
159
-
160
- // Update complete attribute
161
- _.setAttribute('complete', complete.toString());
167
+ _.setAttribute("complete", complete.toString());
162
168
  }
163
-
164
169
  #fmtMoney(amt) {
165
170
  const fmt = this.#moneyFmt;
166
- if (!fmt) return amt.toFixed(2).replace(/\.00$/, '');
167
-
171
+ if (!fmt) return amt.toFixed(2).replace(/\.00$/, "");
168
172
  const fixed = amt.toFixed(2);
169
173
  const noDecimals = Math.round(amt).toString();
170
- const withComma = fixed.replace('.', ',');
171
- const noDecWithComma = noDecimals.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
172
-
173
- return fmt
174
- .replace(/\{\{\s*amount_no_decimals_with_comma_separator\s*\}\}/g, noDecWithComma)
175
- .replace(/\{\{\s*amount_with_comma_separator\s*\}\}/g, withComma)
176
- .replace(/\{\{\s*amount_no_decimals\s*\}\}/g, noDecimals)
177
- .replace(/\{\{\s*amount\s*\}\}/g, fixed);
174
+ const withComma = fixed.replace(".", ",");
175
+ const noDecWithComma = noDecimals.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
176
+ return fmt.replace(/\{\{\s*amount_no_decimals_with_comma_separator\s*\}\}/g, noDecWithComma).replace(/\{\{\s*amount_with_comma_separator\s*\}\}/g, withComma).replace(/\{\{\s*amount_no_decimals\s*\}\}/g, noDecimals).replace(/\{\{\s*amount\s*\}\}/g, fixed);
178
177
  }
179
-
180
178
  #converted() {
181
179
  const rate = parseFloat(window.Shopify?.currency?.rate) || 1;
182
180
  return this.#threshold * rate;
183
181
  }
184
-
185
182
  #updateMessages() {
186
183
  this.#updateState(this.#converted());
187
184
  }
188
-
189
- // Public API
190
185
  setPercent(pct) {
191
186
  const _ = this;
192
187
  const p = Math.max(0, Math.min(100, pct));
193
188
  _.#percent = p;
194
189
  if (_.#bar) _.#bar.setPercent(p);
195
- // Use converted threshold for multi-currency consistency
196
- _.#current = (p / 100) * _.#converted();
197
- _.setAttribute('current', _.#current.toString());
190
+ _.#current = p / 100 * _.#converted();
191
+ _.setAttribute("current", _.#current.toString());
198
192
  _.#updateState(_.#converted());
199
193
  }
200
-
201
194
  setCurrentAmount(amt) {
202
195
  const _ = this;
203
196
  _.#current = parseFloat(amt) || 0;
204
- _.setAttribute('current', _.#current.toString());
197
+ _.setAttribute("current", _.#current.toString());
205
198
  _.#updateProgress();
206
199
  }
207
-
208
200
  setThresholdAmount(amt) {
209
201
  const _ = this;
210
202
  _.#threshold = parseFloat(amt) || 0;
211
- _.setAttribute('threshold', _.#threshold.toString());
203
+ _.setAttribute("threshold", _.#threshold.toString());
212
204
  _.#updateProgress();
213
205
  }
214
-
215
206
  /** @deprecated Use setThresholdAmount */
216
207
  setMinAmount(amt) {
217
208
  this.setThresholdAmount(amt);
218
209
  }
219
-
220
210
  getProgress() {
221
211
  const _ = this;
222
212
  const converted = _.#converted();
@@ -228,33 +218,37 @@ class CartProgressBar extends HTMLElement {
228
218
  remainingAmount: Math.max(0, converted - _.#current),
229
219
  percent: _.#percent,
230
220
  isComplete: _.#current >= converted,
231
- currencyRate: parseFloat(window.Shopify?.currency?.rate) || 1,
221
+ currencyRate: parseFloat(window.Shopify?.currency?.rate) || 1
232
222
  };
233
223
  }
234
-
235
224
  setMessages(above = null, below = null) {
236
225
  const _ = this;
237
226
  if (above !== null) {
238
227
  _.#msgAbove = above;
239
- _.setAttribute('message-above', above);
228
+ _.setAttribute("message-above", above);
240
229
  }
241
230
  if (below !== null) {
242
231
  _.#msgBelow = below;
243
- _.setAttribute('message-below', below);
232
+ _.setAttribute("message-below", below);
244
233
  }
245
234
  _.#updateMessages();
246
235
  }
247
-
248
- // Getters (with backwards compatibility)
249
- get currentAmount() { return this.#current; }
250
- get thresholdAmount() { return this.#threshold; }
251
- get minAmount() { return this.#threshold; }
252
- get percent() { return this.#percent; }
253
- get isComplete() { return this.#current >= this.#converted(); }
254
- }
255
-
256
- // Define CartProgressBar custom element
257
- customElements.define('cart-progress-bar', CartProgressBar);
258
-
236
+ get currentAmount() {
237
+ return this.#current;
238
+ }
239
+ get thresholdAmount() {
240
+ return this.#threshold;
241
+ }
242
+ get minAmount() {
243
+ return this.#threshold;
244
+ }
245
+ get percent() {
246
+ return this.#percent;
247
+ }
248
+ get isComplete() {
249
+ return this.#current >= this.#converted();
250
+ }
251
+ };
252
+ if (!customElements.get("cart-progress-bar")) customElements.define("cart-progress-bar", CartProgressBar);
253
+ //#endregion
259
254
  export { CartProgressBar, ProgressBar };
260
- //# sourceMappingURL=cart-progress-bar.esm.js.map