@magic-spells/cart-progress-bar 0.2.0 → 1.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.
@@ -1 +1 @@
1
- {"version":3,"file":"cart-progress-bar.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 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('{ amount }', 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\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})\n\t\t\t.format(amount)\n\t\t\t.replace('.00', '');\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.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\tconst _ = this;\n\t\t_.setAttribute('role', 'progressbar');\n\t\t_.setAttribute('aria-valuemin', '0');\n\t\t_.setAttribute('aria-valuemax', '100');\n\t\t_.setAttribute('aria-valuenow', '0');\n\t\t_.innerHTML = '<div class=\"progress-bar-fill\"></div>';\n\t}\n\n\tsetPercent(percent) {\n\t\tconst p = Math.max(0, Math.min(100, percent));\n\t\tthis.style.setProperty('--cart-progress-percent', `${p}%`);\n\t\tthis.setAttribute('aria-valuenow', p);\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#threshold = 0;\n\t#current = 0;\n\t#percent = 0;\n\t#msgAbove = '';\n\t#msgBelow = '';\n\t#bar = null;\n\t#msgEl = null;\n\t#moneyFmt = null;\n\t#debounce = null;\n\n\tstatic get observedAttributes() {\n\t\treturn ['threshold', 'current', 'message-above', 'message-below', 'money-format'];\n\t}\n\n\tconstructor() {\n\t\tsuper();\n\t\tconst _ = this;\n\t\t_.#threshold = parseFloat(_.getAttribute('threshold')) || 0;\n\t\t_.#current = parseFloat(_.getAttribute('current')) || 0;\n\t\t_.#msgAbove = _.getAttribute('message-above') || '';\n\t\t_.#msgBelow = _.getAttribute('message-below') || '';\n\t\t_.#moneyFmt = _.getAttribute('money-format');\n\t}\n\n\tasync connectedCallback() {\n\t\tawait customElements.whenDefined('progress-bar');\n\t\tthis.#render();\n\t\tthis.#updateProgress();\n\t\tthis.#attachListeners();\n\t}\n\n\tdisconnectedCallback() {\n\t\tif (this.#debounce) clearTimeout(this.#debounce);\n\t}\n\n\tattributeChangedCallback(name, oldVal, newVal) {\n\t\tif (oldVal === newVal) return;\n\t\tconst _ = this;\n\n\t\tswitch (name) {\n\t\t\tcase 'threshold':\n\t\t\t\t_.#threshold = parseFloat(newVal) || 0;\n\t\t\t\t_.#updateProgress();\n\t\t\t\tbreak;\n\t\t\tcase 'current':\n\t\t\t\t_.#current = parseFloat(newVal) || 0;\n\t\t\t\t_.#updateProgress();\n\t\t\t\tbreak;\n\t\t\tcase 'message-above':\n\t\t\t\t_.#msgAbove = newVal || '';\n\t\t\t\t_.#updateMessages();\n\t\t\t\tbreak;\n\t\t\tcase 'message-below':\n\t\t\t\t_.#msgBelow = newVal || '';\n\t\t\t\t_.#updateMessages();\n\t\t\t\tbreak;\n\t\t\tcase 'money-format':\n\t\t\t\t_.#moneyFmt = newVal;\n\t\t\t\t_.#updateMessages();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t#render() {\n\t\tconst _ = this;\n\t\t// Find existing elements or create them\n\t\t_.#msgEl = _.querySelector('[data-content-cart-progress-message]');\n\t\t_.#bar = _.querySelector('progress-bar');\n\n\t\t// Create message element if needed\n\t\tif (!_.#msgEl && (_.#msgAbove || _.#msgBelow)) {\n\t\t\t_.#msgEl = document.createElement('p');\n\t\t\t_.#msgEl.setAttribute('data-content-cart-progress-message', '');\n\t\t\t_.appendChild(_.#msgEl);\n\t\t}\n\n\t\t// Create progress bar if needed\n\t\tif (!_.#bar) {\n\t\t\t_.#bar = document.createElement('progress-bar');\n\t\t\t_.appendChild(_.#bar);\n\t\t}\n\t}\n\n\t#updateProgress() {\n\t\tconst _ = this;\n\t\tconst converted = _.#converted();\n\t\t_.#percent = converted === 0 ? 100 : Math.min(100, (_.#current / converted) * 100);\n\t\tif (_.#bar) _.#bar.setPercent(_.#percent);\n\t\t_.#updateState(converted);\n\t}\n\n\t#attachListeners() {\n\t\tconst _ = this;\n\t\tconst panel = _.closest('cart-panel');\n\t\tif (panel) {\n\t\t\tpanel.addEventListener('cart-panel:data-changed', (e) => _.#onCartChange(e));\n\t\t}\n\t}\n\n\t#onCartChange(event) {\n\t\tconst _ = this;\n\t\tconst cart = event.detail;\n\t\tif (!cart) return;\n\n\t\tif (_.#debounce) clearTimeout(_.#debounce);\n\t\t_.#debounce = setTimeout(() => {\n\t\t\t_.#debounce = null;\n\t\t\t// Use calculated_subtotal if available, else total_price (both in cents)\n\t\t\tconst amt = (cart.calculated_subtotal ?? cart.total_price ?? 0) / 100;\n\t\t\t_.setCurrentAmount(amt);\n\t\t}, 100);\n\t}\n\n\t#updateState(converted) {\n\t\tconst _ = this;\n\t\tconst complete = _.#current >= converted;\n\t\tconst remaining = Math.max(0, converted - _.#current);\n\t\tconst formatted = _.#fmtMoney(remaining);\n\n\t\t// Update message\n\t\tif (_.#msgEl) {\n\t\t\tlet tpl = complete ? _.#msgAbove : (_.#msgBelow || _.#msgAbove);\n\t\t\tif (tpl) {\n\t\t\t\t_.#msgEl.textContent = tpl.replace(/\\[\\s*amount\\s*\\]/g, formatted);\n\t\t\t\t_.#msgEl.style.display = 'block';\n\t\t\t} else {\n\t\t\t\t_.#msgEl.style.display = 'none';\n\t\t\t}\n\t\t}\n\n\t\t// Update complete attribute\n\t\t_.setAttribute('complete', complete.toString());\n\t}\n\n\t#fmtMoney(amt) {\n\t\tconst fmt = this.#moneyFmt;\n\t\tif (!fmt) return amt.toFixed(2).replace(/\\.00$/, '');\n\n\t\tconst fixed = amt.toFixed(2);\n\t\tconst noDecimals = Math.round(amt).toString();\n\t\tconst withComma = fixed.replace('.', ',');\n\t\tconst noDecWithComma = noDecimals.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\n\n\t\treturn fmt\n\t\t\t.replace(/\\{\\{\\s*amount_no_decimals_with_comma_separator\\s*\\}\\}/g, noDecWithComma)\n\t\t\t.replace(/\\{\\{\\s*amount_with_comma_separator\\s*\\}\\}/g, withComma)\n\t\t\t.replace(/\\{\\{\\s*amount_no_decimals\\s*\\}\\}/g, noDecimals)\n\t\t\t.replace(/\\{\\{\\s*amount\\s*\\}\\}/g, fixed);\n\t}\n\n\t#converted() {\n\t\tconst rate = parseFloat(window.Shopify?.currency?.rate) || 1;\n\t\treturn this.#threshold * rate;\n\t}\n\n\t#updateMessages() {\n\t\tthis.#updateState(this.#converted());\n\t}\n\n\t// Public API\n\tsetPercent(pct) {\n\t\tconst _ = this;\n\t\tconst p = Math.max(0, Math.min(100, pct));\n\t\t_.#percent = p;\n\t\tif (_.#bar) _.#bar.setPercent(p);\n\t\t// Use converted threshold for multi-currency consistency\n\t\t_.#current = (p / 100) * _.#converted();\n\t\t_.setAttribute('current', _.#current.toString());\n\t\t_.#updateState(_.#converted());\n\t}\n\n\tsetCurrentAmount(amt) {\n\t\tconst _ = this;\n\t\t_.#current = parseFloat(amt) || 0;\n\t\t_.setAttribute('current', _.#current.toString());\n\t\t_.#updateProgress();\n\t}\n\n\tsetThresholdAmount(amt) {\n\t\tconst _ = this;\n\t\t_.#threshold = parseFloat(amt) || 0;\n\t\t_.setAttribute('threshold', _.#threshold.toString());\n\t\t_.#updateProgress();\n\t}\n\n\t/** @deprecated Use setThresholdAmount */\n\tsetMinAmount(amt) {\n\t\tthis.setThresholdAmount(amt);\n\t}\n\n\tgetProgress() {\n\t\tconst _ = this;\n\t\tconst converted = _.#converted();\n\t\treturn {\n\t\t\tcurrentAmount: _.#current,\n\t\t\tthresholdAmount: _.#threshold,\n\t\t\tconvertedThreshold: converted,\n\t\t\tminAmount: _.#threshold,\n\t\t\tremainingAmount: Math.max(0, converted - _.#current),\n\t\t\tpercent: _.#percent,\n\t\t\tisComplete: _.#current >= converted,\n\t\t\tcurrencyRate: parseFloat(window.Shopify?.currency?.rate) || 1,\n\t\t};\n\t}\n\n\tsetMessages(above = null, below = null) {\n\t\tconst _ = this;\n\t\tif (above !== null) {\n\t\t\t_.#msgAbove = above;\n\t\t\t_.setAttribute('message-above', above);\n\t\t}\n\t\tif (below !== null) {\n\t\t\t_.#msgBelow = below;\n\t\t\t_.setAttribute('message-below', below);\n\t\t}\n\t\t_.#updateMessages();\n\t}\n\n\t// Getters (with backwards compatibility)\n\tget currentAmount() { return this.#current; }\n\tget thresholdAmount() { return this.#threshold; }\n\tget minAmount() { return this.#threshold; }\n\tget percent() { return this.#percent; }\n\tget isComplete() { return this.#current >= this.#converted(); }\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 +1 @@
1
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).CartProgressBar={})}(this,function(t){"use strict";class ProgressBar extends HTMLElement{constructor(){super(),this.#t()}#t(){this.setAttribute("role","progressbar"),this.setAttribute("aria-valuemin","0"),this.setAttribute("aria-valuemax","100"),this.setAttribute("aria-valuenow","0"),this.innerHTML='<div class="progress-bar-fill"></div>'}setPercent(t){const e=Math.max(0,Math.min(100,t));this.style.setProperty("--cart-progress-percent",`${e}%`),this.setAttribute("aria-valuenow",e)}}customElements.define("progress-bar",ProgressBar);class CartProgressBar extends HTMLElement{#e=0;#s=0;#r=0;#i="";#n="";#a=null;#o=null;static get observedAttributes(){return["threshold","current","message-above","message-below"]}constructor(){super(),this.#u()}#u(){this.#e=parseFloat(this.getAttribute("threshold"))||0,this.#s=parseFloat(this.getAttribute("current"))||0,this.#i=this.getAttribute("message-above")||"",this.#n=this.getAttribute("message-below")||""}async connectedCallback(){if(await customElements.whenDefined("progress-bar"),!customElements.get("progress-bar"))throw new Error("<progress-bar> must be registered before <cart-progress-bar> runs");this.#h(),this.#m(),this.#g()}attributeChangedCallback(t,e,s){if(e!==s)switch(t){case"threshold":this.#e=parseFloat(s)||0,this.#m();break;case"current":this.#s=parseFloat(s)||0,this.#m();break;case"message-above":this.#i=s||"",this.#l();break;case"message-below":this.#n=s||"",this.#l()}}#h(){this.#o=this.querySelector("[data-content-cart-progress-message]")||this.querySelector("p[data-content-cart-progress-message]"),this.#a=this.querySelector("progress-bar"),this.#o||!this.#i&&!this.#n||(this.#o=document.createElement("p"),this.#o.setAttribute("data-content-cart-progress-message",""),this.appendChild(this.#o)),this.#a||(customElements.get("progress-bar")?(this.#a=document.createElement("progress-bar"),this.appendChild(this.#a)):customElements.whenDefined("progress-bar").then(()=>{this.#a||(this.#a=document.createElement("progress-bar"),this.appendChild(this.#a),this.#m())}))}#m(){0===this.#e?this.#r=100:this.#r=Math.min(100,this.#s/this.#e*100),this.#a&&this.#a.setPercent(this.#r),this.#l(),this.#c()}#g(){const t=this.closest("cart-dialog");t&&t.addEventListener("cart-dialog:data-changed",t=>{this.#p(t)})}#p(t){const e=t.detail;if(e){let t=0;void 0!==e.calculated_subtotal?t=e.calculated_subtotal/100:void 0!==e.total_price&&(t=e.total_price/100),this.setCurrentAmount(t)}}#l(){const t=this.#s>=this.#e,e=Math.max(0,this.#e-this.#s),s=this.#d(e);if(this.#o){let e;if(t&&this.#i?e=this.#i:t||(e=this.#n||this.#i),e){const t=e.replace("{ amount }",s);this.#o.textContent=t,this.#o.style.display="block"}else this.#o.style.display="none"}}#c(){const t=this.#s>=this.#e;this.setAttribute("complete",t.toString())}#d(t){return new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",minimumFractionDigits:2,maximumFractionDigits:2}).format(t).replace(".00","")}setPercent(t){const e=Math.max(0,Math.min(100,t));this.#r=e,this.#a&&this.#a.setPercent(e),this.#s=e/100*this.#e,this.setAttribute("current",this.#s.toString()),this.#l(),this.#c()}setCurrentAmount(t){this.#s=parseFloat(t)||0,this.setAttribute("current",this.#s.toString()),this.#m()}setThresholdAmount(t){this.#e=parseFloat(t)||0,this.setAttribute("threshold",this.#e.toString()),this.#m()}setMinAmount(t){this.setThresholdAmount(t)}getProgress(){return{currentAmount:this.#s,thresholdAmount:this.#e,minAmount:this.#e,remainingAmount:Math.max(0,this.#e-this.#s),percent:this.#r,isComplete:this.#s>=this.#e}}setMessages(t=null,e=null){null!==t&&(this.#i=t,this.setAttribute("message-above",t)),null!==e&&(this.#n=e,this.setAttribute("message-below",e)),this.#l()}get currentAmount(){return this.#s}get thresholdAmount(){return this.#e}get minAmount(){return this.#e}get percent(){return this.#r}get isComplete(){return this.#s>=this.#e}}customElements.define("cart-progress-bar",CartProgressBar),t.CartProgressBar=CartProgressBar,t.ProgressBar=ProgressBar});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).CartProgressBar={})}(this,function(e){"use strict";class ProgressBar extends HTMLElement{constructor(){super();const e=this;e.setAttribute("role","progressbar"),e.setAttribute("aria-valuemin","0"),e.setAttribute("aria-valuemax","100"),e.setAttribute("aria-valuenow","0"),e.innerHTML='<div class="progress-bar-fill"></div>'}setPercent(e){const t=Math.max(0,Math.min(100,e));this.style.setProperty("--cart-progress-percent",`${t}%`),this.setAttribute("aria-valuenow",t)}}customElements.define("progress-bar",ProgressBar);class CartProgressBar extends HTMLElement{#e=0;#t=0;#r=0;#s="";#a="";#o=null;#n=null;#c=null;#u=null;static get observedAttributes(){return["threshold","current","message-above","message-below","money-format"]}constructor(){super();const e=this;e.#e=parseFloat(e.getAttribute("threshold"))||0,e.#t=parseFloat(e.getAttribute("current"))||0,e.#s=e.getAttribute("message-above")||"",e.#a=e.getAttribute("message-below")||"",e.#c=e.getAttribute("money-format")}async connectedCallback(){await customElements.whenDefined("progress-bar"),this.#l(),this.#i(),this.#m()}disconnectedCallback(){this.#u&&clearTimeout(this.#u)}attributeChangedCallback(e,t,r){if(t===r)return;const s=this;switch(e){case"threshold":s.#e=parseFloat(r)||0,s.#i();break;case"current":s.#t=parseFloat(r)||0,s.#i();break;case"message-above":s.#s=r||"",s.#d();break;case"message-below":s.#a=r||"",s.#d();break;case"money-format":s.#c=r,s.#d()}}#l(){const e=this;e.#n=e.querySelector("[data-content-cart-progress-message]"),e.#o=e.querySelector("progress-bar"),e.#n||!e.#s&&!e.#a||(e.#n=document.createElement("p"),e.#n.setAttribute("data-content-cart-progress-message",""),e.appendChild(e.#n)),e.#o||(e.#o=document.createElement("progress-bar"),e.appendChild(e.#o))}#i(){const e=this,t=e.#g();e.#r=0===t?100:Math.min(100,e.#t/t*100),e.#o&&e.#o.setPercent(e.#r),e.#h(t)}#m(){const e=this,t=e.closest("cart-panel");t&&t.addEventListener("cart-panel:data-changed",t=>e.#p(t))}#p(e){const t=this,r=e.detail;r&&(t.#u&&clearTimeout(t.#u),t.#u=setTimeout(()=>{t.#u=null;const e=(r.calculated_subtotal??r.total_price??0)/100;t.setCurrentAmount(e)},100))}#h(e){const t=this,r=t.#t>=e,s=Math.max(0,e-t.#t),a=t.#b(s);if(t.#n){let e=r?t.#s:t.#a||t.#s;e?(t.#n.textContent=e.replace(/\[\s*amount\s*\]/g,a),t.#n.style.display="block"):t.#n.style.display="none"}t.setAttribute("complete",r.toString())}#b(e){const t=this.#c;if(!t)return e.toFixed(2).replace(/\.00$/,"");const r=e.toFixed(2),s=Math.round(e).toString(),a=r.replace(".",","),o=s.replace(/\B(?=(\d{3})+(?!\d))/g,",");return t.replace(/\{\{\s*amount_no_decimals_with_comma_separator\s*\}\}/g,o).replace(/\{\{\s*amount_with_comma_separator\s*\}\}/g,a).replace(/\{\{\s*amount_no_decimals\s*\}\}/g,s).replace(/\{\{\s*amount\s*\}\}/g,r)}#g(){const e=parseFloat(window.Shopify?.currency?.rate)||1;return this.#e*e}#d(){this.#h(this.#g())}setPercent(e){const t=this,r=Math.max(0,Math.min(100,e));t.#r=r,t.#o&&t.#o.setPercent(r),t.#t=r/100*t.#g(),t.setAttribute("current",t.#t.toString()),t.#h(t.#g())}setCurrentAmount(e){const t=this;t.#t=parseFloat(e)||0,t.setAttribute("current",t.#t.toString()),t.#i()}setThresholdAmount(e){const t=this;t.#e=parseFloat(e)||0,t.setAttribute("threshold",t.#e.toString()),t.#i()}setMinAmount(e){this.setThresholdAmount(e)}getProgress(){const e=this,t=e.#g();return{currentAmount:e.#t,thresholdAmount:e.#e,convertedThreshold:t,minAmount:e.#e,remainingAmount:Math.max(0,t-e.#t),percent:e.#r,isComplete:e.#t>=t,currencyRate:parseFloat(window.Shopify?.currency?.rate)||1}}setMessages(e=null,t=null){const r=this;null!==e&&(r.#s=e,r.setAttribute("message-above",e)),null!==t&&(r.#a=t,r.setAttribute("message-below",t)),r.#d()}get currentAmount(){return this.#t}get thresholdAmount(){return this.#e}get minAmount(){return this.#e}get percent(){return this.#r}get isComplete(){return this.#t>=this.#g()}}customElements.define("cart-progress-bar",CartProgressBar),e.CartProgressBar=CartProgressBar,e.ProgressBar=ProgressBar});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magic-spells/cart-progress-bar",
3
- "version": "0.2.0",
3
+ "version": "1.0.0",
4
4
  "description": "Cart progress bar web component for free shipping thresholds.",
5
5
  "author": "Cory Schulz",
6
6
  "license": "MIT",
@@ -60,11 +60,11 @@
60
60
  "not ie <= 11"
61
61
  ],
62
62
  "devDependencies": {
63
- "@eslint/js": "^8.57.0",
63
+ "@eslint/js": "^9.38.0",
64
64
  "@rollup/plugin-node-resolve": "^15.2.3",
65
65
  "@rollup/plugin-terser": "^0.4.4",
66
- "eslint": "^8.0.0",
67
- "globals": "^13.24.0",
66
+ "eslint": "^9.38.0",
67
+ "globals": "^15.15.0",
68
68
  "prettier": "^3.3.3",
69
69
  "rollup": "^3.0.0",
70
70
  "rollup-plugin-copy": "^3.5.0",
@@ -6,23 +6,18 @@ import './cart-progress-bar.scss';
6
6
  class ProgressBar extends HTMLElement {
7
7
  constructor() {
8
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>';
9
+ const _ = this;
10
+ _.setAttribute('role', 'progressbar');
11
+ _.setAttribute('aria-valuemin', '0');
12
+ _.setAttribute('aria-valuemax', '100');
13
+ _.setAttribute('aria-valuenow', '0');
14
+ _.innerHTML = '<div class="progress-bar-fill"></div>';
20
15
  }
21
16
 
22
17
  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);
18
+ const p = Math.max(0, Math.min(100, percent));
19
+ this.style.setProperty('--cart-progress-percent', `${p}%`);
20
+ this.setAttribute('aria-valuenow', p);
26
21
  }
27
22
  }
28
23
 
@@ -34,303 +29,230 @@ customElements.define('progress-bar', ProgressBar);
34
29
  */
35
30
  class CartProgressBar extends HTMLElement {
36
31
  // 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
- */
32
+ #threshold = 0;
33
+ #current = 0;
34
+ #percent = 0;
35
+ #msgAbove = '';
36
+ #msgBelow = '';
37
+ #bar = null;
38
+ #msgEl = null;
39
+ #moneyFmt = null;
40
+ #debounce = null;
41
+
48
42
  static get observedAttributes() {
49
- return ['threshold', 'current', 'message-above', 'message-below'];
43
+ return ['threshold', 'current', 'message-above', 'message-below', 'money-format'];
50
44
  }
51
45
 
52
46
  constructor() {
53
47
  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') || '';
48
+ const _ = this;
49
+ _.#threshold = parseFloat(_.getAttribute('threshold')) || 0;
50
+ _.#current = parseFloat(_.getAttribute('current')) || 0;
51
+ _.#msgAbove = _.getAttribute('message-above') || '';
52
+ _.#msgBelow = _.getAttribute('message-below') || '';
53
+ _.#moneyFmt = _.getAttribute('money-format');
65
54
  }
66
55
 
67
56
  async connectedCallback() {
68
- // ensure the child custom element has been registered
69
57
  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
58
  this.#render();
76
59
  this.#updateProgress();
77
60
  this.#attachListeners();
78
61
  }
79
62
 
80
- attributeChangedCallback(name, oldValue, newValue) {
81
- if (oldValue === newValue) return;
63
+ disconnectedCallback() {
64
+ if (this.#debounce) clearTimeout(this.#debounce);
65
+ }
66
+
67
+ attributeChangedCallback(name, oldVal, newVal) {
68
+ if (oldVal === newVal) return;
69
+ const _ = this;
82
70
 
83
71
  switch (name) {
84
72
  case 'threshold':
85
- this.#minAmount = parseFloat(newValue) || 0;
86
- this.#updateProgress();
73
+ _.#threshold = parseFloat(newVal) || 0;
74
+ _.#updateProgress();
87
75
  break;
88
76
  case 'current':
89
- this.#currentAmount = parseFloat(newValue) || 0;
90
- this.#updateProgress();
77
+ _.#current = parseFloat(newVal) || 0;
78
+ _.#updateProgress();
91
79
  break;
92
80
  case 'message-above':
93
- this.#originalAboveMessage = newValue || '';
94
- this.#updateMessages();
81
+ _.#msgAbove = newVal || '';
82
+ _.#updateMessages();
95
83
  break;
96
84
  case 'message-below':
97
- this.#originalBelowMessage = newValue || '';
98
- this.#updateMessages();
85
+ _.#msgBelow = newVal || '';
86
+ _.#updateMessages();
87
+ break;
88
+ case 'money-format':
89
+ _.#moneyFmt = newVal;
90
+ _.#updateMessages();
99
91
  break;
100
92
  }
101
93
  }
102
94
 
103
95
  #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);
96
+ const _ = this;
97
+ // Find existing elements or create them
98
+ _.#msgEl = _.querySelector('[data-content-cart-progress-message]');
99
+ _.#bar = _.querySelector('progress-bar');
100
+
101
+ // Create message element if needed
102
+ if (!_.#msgEl && (_.#msgAbove || _.#msgBelow)) {
103
+ _.#msgEl = document.createElement('p');
104
+ _.#msgEl.setAttribute('data-content-cart-progress-message', '');
105
+ _.appendChild(_.#msgEl);
117
106
  }
118
107
 
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
- }
108
+ // Create progress bar if needed
109
+ if (!_.#bar) {
110
+ _.#bar = document.createElement('progress-bar');
111
+ _.appendChild(_.#bar);
135
112
  }
136
113
  }
137
114
 
138
115
  #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();
116
+ const _ = this;
117
+ const converted = _.#converted();
118
+ _.#percent = converted === 0 ? 100 : Math.min(100, (_.#current / converted) * 100);
119
+ if (_.#bar) _.#bar.setPercent(_.#percent);
120
+ _.#updateState(converted);
155
121
  }
156
122
 
157
123
  #attachListeners() {
158
- // Find the nearest cart-panel component
159
- const cartDialog = this.closest('cart-dialog');
160
-
161
- if (cartDialog) {
162
- // Listen for cart data changes
163
- cartDialog.addEventListener('cart-dialog:data-changed', (event) => {
164
- this.#handleCartDataChange(event);
165
- });
124
+ const _ = this;
125
+ const panel = _.closest('cart-panel');
126
+ if (panel) {
127
+ panel.addEventListener('cart-panel:data-changed', (e) => _.#onCartChange(e));
166
128
  }
167
129
  }
168
130
 
169
- #handleCartDataChange(event) {
170
- const updatedCart = event.detail;
171
-
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
-
185
- this.setCurrentAmount(currentAmount);
186
- }
131
+ #onCartChange(event) {
132
+ const _ = this;
133
+ const cart = event.detail;
134
+ if (!cart) return;
135
+
136
+ if (_.#debounce) clearTimeout(_.#debounce);
137
+ _.#debounce = setTimeout(() => {
138
+ _.#debounce = null;
139
+ // Use calculated_subtotal if available, else total_price (both in cents)
140
+ const amt = (cart.calculated_subtotal ?? cart.total_price ?? 0) / 100;
141
+ _.setCurrentAmount(amt);
142
+ }, 100);
187
143
  }
188
144
 
189
- #updateMessages() {
190
- const isComplete = this.#currentAmount >= this.#minAmount;
191
- const remainingAmount = Math.max(0, this.#minAmount - this.#currentAmount);
192
-
193
- // Format remaining amount as currency (assuming USD for now)
194
- const formattedAmount = this.#formatCurrency(remainingAmount);
195
-
196
- // Update the single message element
197
- if (this.#messageElement) {
198
- let messageTemplate;
199
-
200
- if (isComplete && this.#originalAboveMessage) {
201
- // Show success message when complete
202
- messageTemplate = this.#originalAboveMessage;
203
- } else if (!isComplete) {
204
- // Show progress message when incomplete
205
- messageTemplate = this.#originalBelowMessage || this.#originalAboveMessage;
206
- }
207
-
208
- if (messageTemplate) {
209
- const message = messageTemplate.replace('{ amount }', formattedAmount);
210
- this.#messageElement.textContent = message;
211
- this.#messageElement.style.display = 'block';
145
+ #updateState(converted) {
146
+ const _ = this;
147
+ const complete = _.#current >= converted;
148
+ const remaining = Math.max(0, converted - _.#current);
149
+ const formatted = _.#fmtMoney(remaining);
150
+
151
+ // Update message
152
+ if (_.#msgEl) {
153
+ let tpl = complete ? _.#msgAbove : (_.#msgBelow || _.#msgAbove);
154
+ if (tpl) {
155
+ _.#msgEl.textContent = tpl.replace(/\[\s*amount\s*\]/g, formatted);
156
+ _.#msgEl.style.display = 'block';
212
157
  } else {
213
- // Hide message if no template available for current state
214
- this.#messageElement.style.display = 'none';
158
+ _.#msgEl.style.display = 'none';
215
159
  }
216
160
  }
217
- }
218
161
 
219
- #updateComponentState() {
220
- const isComplete = this.#currentAmount >= this.#minAmount;
221
- this.setAttribute('complete', isComplete.toString());
162
+ // Update complete attribute
163
+ _.setAttribute('complete', complete.toString());
222
164
  }
223
165
 
224
- #formatCurrency(amount) {
225
- // Basic currency formatting - could be enhanced with locale/currency options
226
- return new Intl.NumberFormat('en-US', {
227
- style: 'currency',
228
- currency: 'USD',
229
- minimumFractionDigits: 2,
230
- maximumFractionDigits: 2,
231
- })
232
- .format(amount)
233
- .replace('.00', '');
234
- }
166
+ #fmtMoney(amt) {
167
+ const fmt = this.#moneyFmt;
168
+ if (!fmt) return amt.toFixed(2).replace(/\.00$/, '');
235
169
 
236
- /**
237
- * Public API: Set the progress percentage directly
238
- * @param {number} percent - Progress percentage (0-100)
239
- */
240
- setPercent(percent) {
241
- const clampedPercent = Math.max(0, Math.min(100, percent));
242
- this.#progressPercent = clampedPercent;
170
+ const fixed = amt.toFixed(2);
171
+ const noDecimals = Math.round(amt).toString();
172
+ const withComma = fixed.replace('.', ',');
173
+ const noDecWithComma = noDecimals.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
243
174
 
244
- if (this.#progressBar) {
245
- this.#progressBar.setPercent(clampedPercent);
246
- }
175
+ return fmt
176
+ .replace(/\{\{\s*amount_no_decimals_with_comma_separator\s*\}\}/g, noDecWithComma)
177
+ .replace(/\{\{\s*amount_with_comma_separator\s*\}\}/g, withComma)
178
+ .replace(/\{\{\s*amount_no_decimals\s*\}\}/g, noDecimals)
179
+ .replace(/\{\{\s*amount\s*\}\}/g, fixed);
180
+ }
181
+
182
+ #converted() {
183
+ const rate = parseFloat(window.Shopify?.currency?.rate) || 1;
184
+ return this.#threshold * rate;
185
+ }
247
186
 
248
- // Calculate current amount based on percentage
249
- this.#currentAmount = (clampedPercent / 100) * this.#minAmount;
250
- this.setAttribute('current', this.#currentAmount.toString());
187
+ #updateMessages() {
188
+ this.#updateState(this.#converted());
189
+ }
251
190
 
252
- this.#updateMessages();
253
- this.#updateComponentState();
191
+ // Public API
192
+ setPercent(pct) {
193
+ const _ = this;
194
+ const p = Math.max(0, Math.min(100, pct));
195
+ _.#percent = p;
196
+ if (_.#bar) _.#bar.setPercent(p);
197
+ // Use converted threshold for multi-currency consistency
198
+ _.#current = (p / 100) * _.#converted();
199
+ _.setAttribute('current', _.#current.toString());
200
+ _.#updateState(_.#converted());
254
201
  }
255
202
 
256
- /**
257
- * Public API: Set the current cart amount
258
- * @param {number} amount - Current cart amount
259
- */
260
- setCurrentAmount(amount) {
261
- this.#currentAmount = parseFloat(amount) || 0;
262
- this.setAttribute('current', this.#currentAmount.toString());
263
- this.#updateProgress();
203
+ setCurrentAmount(amt) {
204
+ const _ = this;
205
+ _.#current = parseFloat(amt) || 0;
206
+ _.setAttribute('current', _.#current.toString());
207
+ _.#updateProgress();
264
208
  }
265
209
 
266
- /**
267
- * Public API: Set the threshold amount for free shipping
268
- * @param {number} amount - Threshold amount for free shipping
269
- */
270
- setThresholdAmount(amount) {
271
- this.#minAmount = parseFloat(amount) || 0;
272
- this.setAttribute('threshold', this.#minAmount.toString());
273
- this.#updateProgress();
210
+ setThresholdAmount(amt) {
211
+ const _ = this;
212
+ _.#threshold = parseFloat(amt) || 0;
213
+ _.setAttribute('threshold', _.#threshold.toString());
214
+ _.#updateProgress();
274
215
  }
275
216
 
276
- /**
277
- * Public API: Set the minimum amount for free shipping (deprecated - use setThresholdAmount)
278
- * @param {number} amount - Minimum amount threshold
279
- * @deprecated Use setThresholdAmount instead
280
- */
281
- setMinAmount(amount) {
282
- this.setThresholdAmount(amount);
217
+ /** @deprecated Use setThresholdAmount */
218
+ setMinAmount(amt) {
219
+ this.setThresholdAmount(amt);
283
220
  }
284
221
 
285
- /**
286
- * Public API: Get current progress information
287
- */
288
222
  getProgress() {
223
+ const _ = this;
224
+ const converted = _.#converted();
289
225
  return {
290
- currentAmount: this.#currentAmount,
291
- thresholdAmount: this.#minAmount,
292
- minAmount: this.#minAmount, // backwards compatibility
293
- remainingAmount: Math.max(0, this.#minAmount - this.#currentAmount),
294
- percent: this.#progressPercent,
295
- isComplete: this.#currentAmount >= this.#minAmount,
226
+ currentAmount: _.#current,
227
+ thresholdAmount: _.#threshold,
228
+ convertedThreshold: converted,
229
+ minAmount: _.#threshold,
230
+ remainingAmount: Math.max(0, converted - _.#current),
231
+ percent: _.#percent,
232
+ isComplete: _.#current >= converted,
233
+ currencyRate: parseFloat(window.Shopify?.currency?.rate) || 1,
296
234
  };
297
235
  }
298
236
 
299
- /**
300
- * Public API: Update message templates
301
- * @param {string} aboveMessage - Message template for above the bar
302
- * @param {string} belowMessage - Message template for below the bar
303
- */
304
- setMessages(aboveMessage = null, belowMessage = null) {
305
- if (aboveMessage !== null) {
306
- this.#originalAboveMessage = aboveMessage;
307
- this.setAttribute('message-above', aboveMessage);
237
+ setMessages(above = null, below = null) {
238
+ const _ = this;
239
+ if (above !== null) {
240
+ _.#msgAbove = above;
241
+ _.setAttribute('message-above', above);
308
242
  }
309
-
310
- if (belowMessage !== null) {
311
- this.#originalBelowMessage = belowMessage;
312
- this.setAttribute('message-below', belowMessage);
243
+ if (below !== null) {
244
+ _.#msgBelow = below;
245
+ _.setAttribute('message-below', below);
313
246
  }
314
-
315
- this.#updateMessages();
247
+ _.#updateMessages();
316
248
  }
317
249
 
318
- // Getters
319
- get currentAmount() {
320
- return this.#currentAmount;
321
- }
322
- get thresholdAmount() {
323
- return this.#minAmount;
324
- }
325
- get minAmount() {
326
- return this.#minAmount;
327
- } // backwards compatibility
328
- get percent() {
329
- return this.#progressPercent;
330
- }
331
- get isComplete() {
332
- return this.#currentAmount >= this.#minAmount;
333
- }
250
+ // Getters (with backwards compatibility)
251
+ get currentAmount() { return this.#current; }
252
+ get thresholdAmount() { return this.#threshold; }
253
+ get minAmount() { return this.#threshold; }
254
+ get percent() { return this.#percent; }
255
+ get isComplete() { return this.#current >= this.#converted(); }
334
256
  }
335
257
 
336
258
  // Define CartProgressBar custom element