@magic-spells/gift-with-purchase 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.
@@ -1,8 +1,9 @@
1
+ //#region src/gift-with-purchase.js
1
2
  /**
2
- * Gift With Purchase Component - automatically adds/removes gift when cart threshold is met
3
- * Emits gwp:added/gwp:removed/gwp:error events and broadcasts cart updates
4
- */
5
- class GiftWithPurchase extends HTMLElement {
3
+ * Gift With Purchase Component - automatically adds/removes gift when cart threshold is met
4
+ * Emits gwp:added/gwp:removed/gwp:error events and broadcasts cart updates
5
+ */
6
+ var GiftWithPurchase = class extends HTMLElement {
6
7
  #threshold = 0;
7
8
  #currentAmount = 0;
8
9
  #variantId = null;
@@ -15,348 +16,376 @@ class GiftWithPurchase extends HTMLElement {
15
16
  #handlers = {};
16
17
  #debounceTimer = null;
17
18
  #attachRetryTimer = null;
18
- #isMutating = false; // prevents overlapping cart mutations
19
- #pendingCart = null; // stores cart snapshot during mutation for recheck
19
+ #isMutating = false;
20
+ #missedUpdate = false;
20
21
  #messageAbove = null;
21
22
  #messageBelow = null;
22
23
  #moneyFormat = null;
23
-
24
24
  static get observedAttributes() {
25
25
  return [
26
- 'threshold',
27
- 'current',
28
- 'variant-id',
29
- 'promo-ended',
30
- 'product-available',
31
- 'message-above',
32
- 'message-below',
33
- 'money-format',
26
+ "threshold",
27
+ "current",
28
+ "variant-id",
29
+ "promo-ended",
30
+ "product-available",
31
+ "message-above",
32
+ "message-below",
33
+ "money-format"
34
34
  ];
35
35
  }
36
-
37
36
  constructor() {
38
37
  super();
39
38
  const _ = this;
40
- _.#threshold = parseFloat(_.getAttribute('threshold')) || 0;
41
- _.#currentAmount = parseFloat(_.getAttribute('current')) || 0;
42
- _.#variantId = _.getAttribute('variant-id');
43
- _.#promoEnded = _.hasAttribute('promo-ended');
44
- _.#productAvailable = _.#parseBooleanValue(_.getAttribute('product-available'), true);
45
- _.#messageAbove = _.getAttribute('message-above');
46
- _.#messageBelow = _.getAttribute('message-below');
47
- _.#moneyFormat = _.getAttribute('money-format');
39
+ _.#threshold = parseFloat(_.getAttribute("threshold")) || 0;
40
+ _.#currentAmount = parseFloat(_.getAttribute("current")) || 0;
41
+ _.#variantId = _.getAttribute("variant-id");
42
+ _.#promoEnded = _.hasAttribute("promo-ended");
43
+ _.#productAvailable = _.#parseBooleanValue(_.getAttribute("product-available"), true);
44
+ _.#messageAbove = _.getAttribute("message-above");
45
+ _.#messageBelow = _.getAttribute("message-below");
46
+ _.#moneyFormat = _.getAttribute("money-format");
48
47
  _.#handlers = { cartDataChange: _.#handleCartDataChange.bind(_) };
49
48
  }
50
-
51
49
  connectedCallback() {
52
50
  const _ = this;
53
-
54
51
  _.#calculateInitialState();
55
52
  _.#render();
56
53
  _.#updateVisualState();
57
54
  _.#attachListeners();
55
+ if (_.#isDisabled && (!_.#cartPanel || _.#cartPanel.hasAttribute("manual"))) _.#updateState(null);
58
56
  }
59
-
60
57
  #calculateInitialState() {
61
58
  const _ = this;
62
- // Calculate initial active state based on attributes (before any cart events)
63
59
  const convertedThreshold = _.#getConvertedThreshold();
64
60
  _.#isDisabled = _.#promoEnded || !_.#productAvailable;
65
61
  _.#isActive = _.#currentAmount >= convertedThreshold && !_.#isDisabled;
66
62
  }
67
-
68
63
  disconnectedCallback() {
69
64
  const _ = this;
70
65
  if (_.#debounceTimer) clearTimeout(_.#debounceTimer);
71
66
  if (_.#attachRetryTimer) clearTimeout(_.#attachRetryTimer);
72
- if (_.#cartPanel) _.#cartPanel.removeEventListener('cart-panel:data-changed', _.#handlers.cartDataChange);
67
+ if (_.#cartPanel) _.#cartPanel.removeEventListener("cart-panel:data-changed", _.#handlers.cartDataChange);
73
68
  }
74
-
75
69
  attributeChangedCallback(name, oldValue, newValue) {
76
70
  const _ = this;
77
71
  if (oldValue === newValue) return;
78
-
79
72
  switch (name) {
80
- case 'threshold':
73
+ case "threshold":
81
74
  _.#threshold = parseFloat(newValue) || 0;
82
75
  break;
83
- case 'current':
76
+ case "current":
84
77
  _.#currentAmount = parseFloat(newValue) || 0;
85
78
  break;
86
- case 'variant-id':
79
+ case "variant-id":
87
80
  _.#variantId = newValue;
88
81
  break;
89
- case 'promo-ended':
82
+ case "promo-ended":
90
83
  _.#promoEnded = newValue !== null;
91
84
  break;
92
- case 'product-available':
85
+ case "product-available":
93
86
  _.#productAvailable = _.#parseBooleanValue(newValue, true);
94
87
  break;
95
- case 'message-above':
88
+ case "message-above":
96
89
  _.#messageAbove = newValue;
97
90
  break;
98
- case 'message-below':
91
+ case "message-below":
99
92
  _.#messageBelow = newValue;
100
93
  break;
101
- case 'money-format':
102
- _.#moneyFormat = newValue;
103
- break;
104
- }
105
-
106
- // Recalculate state and update UI if component is connected
107
- if (_.isConnected) {
108
- _.#calculateInitialState();
109
- _.#updateVisualState();
110
- _.#updateMessages();
94
+ case "money-format": _.#moneyFormat = newValue;
111
95
  }
96
+ if (!_.isConnected) return;
97
+ const wasDisabled = _.#isDisabled;
98
+ _.#calculateInitialState();
99
+ _.#updateVisualState();
100
+ _.#updateMessages();
101
+ if (_.#isDisabled && !wasDisabled) _.#updateState(null);
112
102
  }
113
-
114
103
  #render() {
115
- this.classList.add('gift-with-purchase');
104
+ this.classList.add("gift-with-purchase");
116
105
  this.#renderMessages();
117
106
  }
118
-
119
107
  #renderMessages() {
120
- // Look for existing message element with data-content-gwp-message
121
108
  this.#updateMessages();
122
109
  }
123
-
124
110
  #formatMoney(amount) {
125
- if (!this.#moneyFormat) return amount.toFixed(2).replace(/\.00$/, '');
126
-
111
+ if (!this.#moneyFormat) return amount.toFixed(2).replace(/\.00$/, "");
127
112
  const amountFixed = amount.toFixed(2);
128
113
  const amountNoDecimals = Math.round(amount).toString();
129
- const amountWithComma = amountFixed.replace('.', ',');
130
- const amountNoDecimalsWithComma = amountNoDecimals.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
131
-
132
- return this.#moneyFormat
133
- .replace(/\{\{\s*amount_no_decimals_with_comma_separator\s*\}\}/g, amountNoDecimalsWithComma)
134
- .replace(/\{\{\s*amount_with_comma_separator\s*\}\}/g, amountWithComma)
135
- .replace(/\{\{\s*amount_no_decimals\s*\}\}/g, amountNoDecimals)
136
- .replace(/\{\{\s*amount\s*\}\}/g, amountFixed);
137
- }
138
-
114
+ const amountWithComma = amountFixed.replace(".", ",");
115
+ const amountNoDecimalsWithComma = amountNoDecimals.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
116
+ return this.#moneyFormat.replace(/\{\{\s*amount_no_decimals_with_comma_separator\s*\}\}/g, amountNoDecimalsWithComma).replace(/\{\{\s*amount_with_comma_separator\s*\}\}/g, amountWithComma).replace(/\{\{\s*amount_no_decimals\s*\}\}/g, amountNoDecimals).replace(/\{\{\s*amount\s*\}\}/g, amountFixed);
117
+ }
139
118
  #getConvertedThreshold() {
140
- // Convert threshold using Shopify currency rate if available (for multi-currency stores)
141
119
  const rate = parseFloat(window.Shopify?.currency?.rate) || 1;
142
120
  return this.#threshold * rate;
143
121
  }
144
-
145
122
  #parseBooleanValue(value, defaultValue = true) {
146
- if (value === null || typeof value === 'undefined') return defaultValue;
147
- if (value === '') return true;
123
+ if (value === null || typeof value === "undefined") return defaultValue;
124
+ if (value === "") return true;
148
125
  const normalized = String(value).trim().toLowerCase();
149
- if (normalized === 'false' || normalized === '0') return false;
150
- if (normalized === 'true' || normalized === '1') return true;
126
+ if (normalized === "false" || normalized === "0") return false;
127
+ if (normalized === "true" || normalized === "1") return true;
151
128
  return defaultValue;
152
129
  }
153
-
154
130
  #updateMessages() {
155
131
  const _ = this;
156
- const messageEl = _.querySelector('[data-content-gwp-message]');
132
+ const messageEl = _.querySelector("[data-content-gwp-message]");
157
133
  if (!messageEl) return;
158
-
159
- let message = '';
160
- if (_.#isActive && _.#messageAbove) {
161
- message = _.#messageAbove;
162
- } else if (!_.#isActive && _.#messageBelow) {
134
+ let message = "";
135
+ if (_.#isActive && _.#messageAbove) message = _.#messageAbove;
136
+ else if (!_.#isActive && _.#messageBelow) {
163
137
  const remaining = _.#getConvertedThreshold() - _.#currentAmount;
164
138
  const formattedAmount = _.#formatMoney(remaining);
165
- message = _.#messageBelow
166
- .replace(/\[\s*amount\s*\]/g, formattedAmount)
167
- .replace(/\[amount\]/g, formattedAmount);
139
+ message = _.#messageBelow.replace(/\[\s*amount\s*\]/g, formattedAmount).replace(/\[amount\]/g, formattedAmount);
168
140
  }
169
-
170
141
  messageEl.textContent = message;
171
- messageEl.style.display = message ? 'block' : 'none';
142
+ messageEl.style.display = message ? "block" : "none";
172
143
  }
173
-
174
144
  #attachListeners() {
175
145
  const _ = this;
176
- _.#cartPanel = _.closest('cart-panel');
177
-
178
- if (_.#cartPanel) {
179
- _.#cartPanel.addEventListener('cart-panel:data-changed', _.#handlers.cartDataChange);
180
- } else {
181
- _.#attachRetryTimer = setTimeout(() => {
182
- _.#attachRetryTimer = null;
183
- _.#cartPanel = _.closest('cart-panel');
184
- if (_.#cartPanel) _.#cartPanel.addEventListener('cart-panel:data-changed', _.#handlers.cartDataChange);
185
- else console.error('GWP - cart-panel still not found after delay');
186
- }, 100);
187
- }
146
+ _.#cartPanel = _.closest("cart-panel");
147
+ if (_.#cartPanel) _.#cartPanel.addEventListener("cart-panel:data-changed", _.#handlers.cartDataChange);
148
+ else _.#attachRetryTimer = setTimeout(() => {
149
+ _.#attachRetryTimer = null;
150
+ _.#cartPanel = _.closest("cart-panel");
151
+ if (_.#cartPanel) _.#cartPanel.addEventListener("cart-panel:data-changed", _.#handlers.cartDataChange);
152
+ else console.error("GWP - cart-panel still not found after delay");
153
+ }, 100);
154
+ }
155
+ /**
156
+ * Ask the parent cart-panel to re-fetch and re-render the cart.
157
+ * refreshCart() is the public API across every published cart-panel (0.3.x - 2.x);
158
+ * getCartAndRefresh() is only tried as a fallback for custom panels written against
159
+ * the name earlier versions of this component mistakenly called. Missing methods are
160
+ * ignored rather than thrown so a successful add/remove never reports a spurious error.
161
+ */
162
+ #refreshCartPanel() {
163
+ const panel = this.#cartPanel;
164
+ if (!panel) return;
165
+ if (typeof panel.refreshCart === "function") panel.refreshCart();
166
+ else if (typeof panel.getCartAndRefresh === "function") panel.getCartAndRefresh();
188
167
  }
189
-
190
168
  #handleCartDataChange(event) {
191
169
  const _ = this;
192
170
  const cart = event.detail;
193
- if (!cart || typeof cart.calculated_subtotal === 'undefined') return;
171
+ if (!cart || typeof cart.calculated_subtotal === "undefined") return;
194
172
  if (_.#debounceTimer) clearTimeout(_.#debounceTimer);
195
-
196
173
  _.#debounceTimer = setTimeout(() => {
197
174
  _.#debounceTimer = null;
175
+ if (_.#isMutating) {
176
+ _.#missedUpdate = true;
177
+ return;
178
+ }
198
179
  _.#currentAmount = parseFloat(cart.calculated_subtotal / 100) || 0;
199
180
  _.#checkGiftInCart(cart);
200
181
  _.#updateState(cart);
201
182
  }, 300);
202
183
  }
203
-
204
184
  #checkGiftInCart(cart) {
205
185
  const _ = this;
206
- const giftLines = _.#getGiftLines(cart, true);
186
+ const giftLines = _.#getGiftLines(cart);
207
187
  _.#isAdded = giftLines.length > 0;
188
+ const duplicate = giftLines.find((item) => item.quantity > 1);
189
+ if (duplicate) _.#trimGiftQuantity(duplicate);
208
190
  }
209
-
210
- #getGiftLines(cart, matchVariantId = true) {
191
+ async #trimGiftQuantity(item) {
211
192
  const _ = this;
212
- if (!cart?.items) return [];
193
+ _.#isMutating = true;
194
+ try {
195
+ const res = await fetch("/cart/change.js", {
196
+ method: "POST",
197
+ credentials: "same-origin",
198
+ headers: {
199
+ "Content-Type": "application/json",
200
+ "X-Requested-With": "XMLHttpRequest"
201
+ },
202
+ body: JSON.stringify({
203
+ id: item.key,
204
+ quantity: 1
205
+ })
206
+ });
207
+ if (!res.ok) throw new Error(`http ${res.status}`);
208
+ _.#refreshCartPanel();
209
+ } catch (err) {
210
+ console.error("giftwithpurchase: quantity trim error", err);
211
+ _.dispatchEvent(new CustomEvent("gwp:error", {
212
+ detail: {
213
+ action: "trim",
214
+ error: err.message
215
+ },
216
+ bubbles: true
217
+ }));
218
+ } finally {
219
+ _.#isMutating = false;
220
+ _.#discardStaleCart();
221
+ }
222
+ }
223
+ #getGiftLines(cart) {
224
+ const _ = this;
225
+ if (!cart?.items || !_.#variantId) return [];
213
226
  return cart.items.filter((item) => {
214
- if (item.properties?._gwp_item !== 'true') return false;
215
- if (!matchVariantId) return true;
216
- if (!_.#variantId) return false;
227
+ if (item.properties?._gwp_item !== "true") return false;
217
228
  return item.variant_id?.toString() === _.#variantId.toString();
218
229
  });
219
230
  }
220
-
221
- #recheckIfPending() {
231
+ /**
232
+ * Cart snapshots held across a mutation predate it - drop them and ask for fresh truth.
233
+ */
234
+ #discardStaleCart() {
222
235
  const _ = this;
223
- if (_.#pendingCart) {
224
- const cart = _.#pendingCart;
225
- _.#pendingCart = null;
226
- _.#checkGiftInCart(cart);
227
- _.#updateState(cart);
236
+ if (_.#debounceTimer) {
237
+ clearTimeout(_.#debounceTimer);
238
+ _.#debounceTimer = null;
239
+ _.#missedUpdate = true;
228
240
  }
241
+ if (!_.#missedUpdate) return;
242
+ _.#missedUpdate = false;
243
+ if (_.#cartPanel) _.#refreshCartPanel();
244
+ else _.#updateState(null);
229
245
  }
230
-
231
246
  #updateState(cart) {
232
247
  const _ = this;
233
-
234
- // If mutation in progress, queue this cart for recheck later
235
248
  if (_.#isMutating) {
236
- _.#pendingCart = cart;
249
+ _.#missedUpdate = true;
237
250
  return;
238
251
  }
239
-
240
- const wasActive = _.#isActive;
241
252
  const convertedThreshold = _.#getConvertedThreshold();
242
253
  _.#isDisabled = _.#promoEnded || !_.#productAvailable;
243
254
  _.#isActive = _.#currentAmount >= convertedThreshold && !_.#isDisabled;
244
-
245
- if (_.#isDisabled) _.#removeAllGiftsFromCart(cart);
246
- else if (_.#isActive && !wasActive && !_.#isAdded && _.#variantId) _.#addGiftToCart();
255
+ if (_.#isDisabled) _.#removeGiftFromCart(cart);
256
+ else if (_.#isActive && !_.#isAdded && _.#variantId) _.#addGiftToCart();
247
257
  else if (!_.#isActive && _.#isAdded && _.#variantId) _.#removeGiftFromCart(cart);
248
-
249
258
  _.#updateVisualState();
250
259
  _.#updateMessages();
251
260
  }
252
-
253
261
  #updateVisualState() {
254
262
  const _ = this;
255
263
  if (_.#promoEnded) {
256
- _.setAttribute('state', 'ended');
257
- _.style.display = 'none';
264
+ _.setAttribute("state", "ended");
265
+ _.style.display = "none";
258
266
  return;
259
267
  }
260
-
261
268
  if (!_.#productAvailable) {
262
- _.setAttribute('state', 'disabled');
263
- _.style.display = 'none';
269
+ _.setAttribute("state", "disabled");
270
+ _.style.display = "none";
264
271
  return;
265
272
  }
266
-
267
- _.style.display = '';
268
- if (_.#isAdded) _.setAttribute('state', 'added');
269
- else if (_.#isActive) _.setAttribute('state', 'active');
270
- else _.setAttribute('state', 'inactive');
273
+ _.style.display = "";
274
+ if (_.#isAdded) _.setAttribute("state", "added");
275
+ else if (_.#isActive) _.setAttribute("state", "active");
276
+ else _.setAttribute("state", "inactive");
271
277
  }
272
-
273
278
  async #addGiftToCart() {
274
279
  const _ = this;
275
280
  _.#isMutating = true;
276
281
  try {
277
- const res = await fetch('/cart/add.js', {
278
- method: 'POST',
279
- credentials: 'same-origin',
280
- headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
281
- body: JSON.stringify({
282
- items: [{ id: _.#variantId, quantity: 1, properties: { _gwp_item: 'true', _hide_in_cart: 'true', _ignore_price_in_subtotal: 'true' } }],
283
- }),
282
+ const res = await fetch("/cart/add.js", {
283
+ method: "POST",
284
+ credentials: "same-origin",
285
+ headers: {
286
+ "Content-Type": "application/json",
287
+ "X-Requested-With": "XMLHttpRequest"
288
+ },
289
+ body: JSON.stringify({ items: [{
290
+ id: _.#variantId,
291
+ quantity: 1,
292
+ properties: {
293
+ _gwp_item: "true",
294
+ _hide_in_cart: "true",
295
+ _ignore_price_in_subtotal: "true"
296
+ }
297
+ }] })
284
298
  });
285
299
  if (!res.ok) throw new Error(`http ${res.status}`);
286
300
  await res.json();
287
301
  _.#isAdded = true;
288
- _.dispatchEvent(new CustomEvent('gwp:added', { detail: { variantId: _.#variantId }, bubbles: true }));
289
- _.#cartPanel?.getCartAndRefresh();
302
+ _.dispatchEvent(new CustomEvent("gwp:added", {
303
+ detail: { variantId: _.#variantId },
304
+ bubbles: true
305
+ }));
306
+ _.#refreshCartPanel();
290
307
  } catch (err) {
291
- console.error('giftwithpurchase: add error', err);
292
- _.dispatchEvent(new CustomEvent('gwp:error', { detail: { action: 'add', error: err.message }, bubbles: true }));
308
+ console.error("giftwithpurchase: add error", err);
309
+ _.dispatchEvent(new CustomEvent("gwp:error", {
310
+ detail: {
311
+ action: "add",
312
+ error: err.message
313
+ },
314
+ bubbles: true
315
+ }));
293
316
  } finally {
294
317
  _.#isMutating = false;
295
- _.#recheckIfPending();
318
+ _.#discardStaleCart();
296
319
  }
297
320
  }
298
-
299
321
  async #removeGiftFromCart(cart) {
300
322
  const _ = this;
301
- if (!cart?.items) return;
302
-
303
- const giftLines = _.#getGiftLines(cart, true);
304
- if (!giftLines.length) {
305
- _.#isAdded = false;
306
- return;
307
- }
308
-
309
323
  _.#isMutating = true;
310
324
  try {
325
+ if (!cart?.items) cart = await _.#fetchCart();
326
+ if (!cart?.items) return;
327
+ const giftLines = _.#getGiftLines(cart);
328
+ if (!giftLines.length) {
329
+ _.#isAdded = false;
330
+ return;
331
+ }
311
332
  await _.#removeAllGiftItems(giftLines);
312
333
  } finally {
313
334
  _.#isMutating = false;
314
- _.#recheckIfPending();
335
+ _.#discardStaleCart();
315
336
  }
316
337
  }
317
-
318
- async #removeAllGiftsFromCart(cart) {
338
+ async #fetchCart() {
319
339
  const _ = this;
320
- if (!cart?.items) return;
321
-
322
- const giftLines = _.#getGiftLines(cart, false);
323
- if (!giftLines.length) {
324
- _.#isAdded = false;
325
- return;
326
- }
327
-
328
- _.#isMutating = true;
329
340
  try {
330
- await _.#removeAllGiftItems(giftLines);
331
- } finally {
332
- _.#isMutating = false;
333
- _.#recheckIfPending();
341
+ let cart;
342
+ if (typeof _.#cartPanel?.getCart === "function") cart = await _.#cartPanel.getCart();
343
+ else {
344
+ const res = await fetch("/cart.js", { credentials: "same-origin" });
345
+ if (!res.ok) throw new Error(`http ${res.status}`);
346
+ cart = await res.json();
347
+ }
348
+ return cart?.error ? null : cart;
349
+ } catch (err) {
350
+ console.error("giftwithpurchase: cart fetch error", err);
351
+ return null;
334
352
  }
335
353
  }
336
-
337
354
  async #removeAllGiftItems(giftLines) {
338
355
  const _ = this;
339
356
  try {
340
- await Promise.all(
341
- giftLines.map(async (item) => {
342
- const res = await fetch('/cart/change.js', {
343
- method: 'POST',
344
- credentials: 'same-origin',
345
- headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
346
- body: JSON.stringify({ id: item.key, quantity: 0 }),
347
- });
348
- if (!res.ok) throw new Error(`http ${res.status}`);
349
- })
350
- );
357
+ await Promise.all(giftLines.map(async (item) => {
358
+ const res = await fetch("/cart/change.js", {
359
+ method: "POST",
360
+ credentials: "same-origin",
361
+ headers: {
362
+ "Content-Type": "application/json",
363
+ "X-Requested-With": "XMLHttpRequest"
364
+ },
365
+ body: JSON.stringify({
366
+ id: item.key,
367
+ quantity: 0
368
+ })
369
+ });
370
+ if (!res.ok) throw new Error(`http ${res.status}`);
371
+ }));
351
372
  _.#isAdded = false;
352
- _.dispatchEvent(new CustomEvent('gwp:removed', { detail: { variantId: _.#variantId }, bubbles: true }));
353
- _.#cartPanel?.getCartAndRefresh();
373
+ _.dispatchEvent(new CustomEvent("gwp:removed", {
374
+ detail: { variantId: _.#variantId },
375
+ bubbles: true
376
+ }));
377
+ _.#refreshCartPanel();
354
378
  } catch (err) {
355
- console.error('giftwithpurchase: bulk remove error', err);
356
- _.dispatchEvent(new CustomEvent('gwp:error', { detail: { action: 'remove', error: err.message }, bubbles: true }));
379
+ console.error("giftwithpurchase: bulk remove error", err);
380
+ _.dispatchEvent(new CustomEvent("gwp:error", {
381
+ detail: {
382
+ action: "remove",
383
+ error: err.message
384
+ },
385
+ bubbles: true
386
+ }));
357
387
  }
358
388
  }
359
-
360
389
  getState() {
361
390
  const _ = this;
362
391
  const convertedThreshold = _.#getConvertedThreshold();
@@ -371,10 +400,9 @@ class GiftWithPurchase extends HTMLElement {
371
400
  productAvailable: _.#productAvailable,
372
401
  isDisabled: _.#isDisabled,
373
402
  remainingAmount: Math.max(0, convertedThreshold - _.#currentAmount),
374
- currencyRate: parseFloat(window.Shopify?.currency?.rate) || 1,
403
+ currencyRate: parseFloat(window.Shopify?.currency?.rate) || 1
375
404
  };
376
405
  }
377
-
378
406
  get currentAmount() {
379
407
  return this.#currentAmount;
380
408
  }
@@ -399,29 +427,22 @@ class GiftWithPurchase extends HTMLElement {
399
427
  get isDisabled() {
400
428
  return this.#isDisabled;
401
429
  }
402
-
403
430
  setCurrentAmount(amount) {
404
431
  const _ = this;
405
432
  _.#currentAmount = parseFloat(amount) || 0;
406
- _.#updateState({ items: [] });
433
+ _.#updateState(null);
407
434
  _.#updateMessages();
408
435
  }
409
-
410
436
  setThreshold(threshold) {
411
437
  const _ = this;
412
438
  _.#threshold = parseFloat(threshold) || 0;
413
- _.#updateState({ items: [] });
439
+ _.#updateState(null);
414
440
  _.#updateMessages();
415
441
  }
416
-
417
442
  setVariantId(variantId) {
418
443
  this.#variantId = variantId;
419
444
  }
420
- }
421
-
422
- if (!customElements.get('gift-with-purchase')) {
423
- customElements.define('gift-with-purchase', GiftWithPurchase);
424
- }
425
-
445
+ };
446
+ if (!customElements.get("gift-with-purchase")) customElements.define("gift-with-purchase", GiftWithPurchase);
447
+ //#endregion
426
448
  export { GiftWithPurchase };
427
- //# sourceMappingURL=gift-with-purchase.esm.js.map
@@ -1,2 +1,2 @@
1
- gift-with-purchase{--gwp-border-radius:8px;--gwp-padding:1rem;--gwp-gap:1rem;--gwp-bg-active:#e8f5e8;--gwp-bg-added:#d4edda;--gwp-border-active:#28a745;--gwp-border-added:#155724;--gwp-text-active:#155724;--gwp-text-added:#155724;background-color:var(--gwp-bg-active);border:2px solid var(--gwp-border-active);border-radius:var(--gwp-border-radius);color:var(--gwp-text-active);display:block;padding:var(--gwp-padding)}gift-with-purchase .gwp-product{align-items:flex-start;display:flex;gap:var(--gwp-gap)}gift-with-purchase [data-gwp-image]{flex-shrink:0}gift-with-purchase .gwp-content{flex:1;min-width:0}gift-with-purchase[state=active]{background-color:var(--gwp-bg-active);border-color:var(--gwp-border-active);color:var(--gwp-text-active)}gift-with-purchase[state=added]{background-color:var(--gwp-bg-added);border-color:var(--gwp-border-added);color:var(--gwp-text-added)}gift-with-purchase[state=disabled],gift-with-purchase[state=ended]{display:none}
2
- /*# sourceMappingURL=gift-with-purchase.min.css.map */
1
+ gift-with-purchase{--gwp-border-radius:8px;--gwp-padding:1rem;--gwp-gap:1rem;--gwp-bg-active:#e8f5e8;--gwp-bg-added:#d4edda;--gwp-border-active:#28a745;--gwp-border-added:#155724;--gwp-text-active:#155724;--gwp-text-added:#155724;border:2px solid var(--gwp-border-active);border-radius:var(--gwp-border-radius);padding:var(--gwp-padding);background-color:var(--gwp-bg-active);color:var(--gwp-text-active);display:block}gift-with-purchase .gwp-product{align-items:flex-start;gap:var(--gwp-gap);display:flex}gift-with-purchase [data-gwp-image]{flex-shrink:0}gift-with-purchase .gwp-content{flex:1;min-width:0}gift-with-purchase[state=active]{background-color:var(--gwp-bg-active);border-color:var(--gwp-border-active);color:var(--gwp-text-active)}gift-with-purchase[state=added]{background-color:var(--gwp-bg-added);border-color:var(--gwp-border-added);color:var(--gwp-text-added)}gift-with-purchase[state=ended],gift-with-purchase[state=disabled]{display:none}
2
+ /*$vite$:1*/