@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.
- package/README.md +56 -16
- package/dist/{gift-with-purchase.cjs.css → gift-with-purchase.css} +12 -9
- package/dist/gift-with-purchase.esm.js +226 -205
- package/dist/gift-with-purchase.min.css +2 -2
- package/dist/gift-with-purchase.min.js +1 -2
- package/package.json +16 -23
- package/dist/gift-with-purchase.cjs.css.map +0 -1
- package/dist/gift-with-purchase.cjs.js +0 -429
- package/dist/gift-with-purchase.cjs.js.map +0 -1
- package/dist/gift-with-purchase.esm.css +0 -46
- package/dist/gift-with-purchase.esm.css.map +0 -1
- package/dist/gift-with-purchase.esm.js.map +0 -1
- package/dist/gift-with-purchase.min.css.map +0 -1
- package/dist/gift-with-purchase.min.js.map +0 -1
- package/dist/gift-with-purchase.scss +0 -75
- package/src/gift-with-purchase.js +0 -428
- package/src/gift-with-purchase.scss +0 -75
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
//#region src/gift-with-purchase.js
|
|
1
2
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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;
|
|
19
|
-
#
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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(
|
|
41
|
-
_.#currentAmount = parseFloat(_.getAttribute(
|
|
42
|
-
_.#variantId = _.getAttribute(
|
|
43
|
-
_.#promoEnded = _.hasAttribute(
|
|
44
|
-
_.#productAvailable = _.#parseBooleanValue(_.getAttribute(
|
|
45
|
-
_.#messageAbove = _.getAttribute(
|
|
46
|
-
_.#messageBelow = _.getAttribute(
|
|
47
|
-
_.#moneyFormat = _.getAttribute(
|
|
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(
|
|
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
|
|
73
|
+
case "threshold":
|
|
81
74
|
_.#threshold = parseFloat(newValue) || 0;
|
|
82
75
|
break;
|
|
83
|
-
case
|
|
76
|
+
case "current":
|
|
84
77
|
_.#currentAmount = parseFloat(newValue) || 0;
|
|
85
78
|
break;
|
|
86
|
-
case
|
|
79
|
+
case "variant-id":
|
|
87
80
|
_.#variantId = newValue;
|
|
88
81
|
break;
|
|
89
|
-
case
|
|
82
|
+
case "promo-ended":
|
|
90
83
|
_.#promoEnded = newValue !== null;
|
|
91
84
|
break;
|
|
92
|
-
case
|
|
85
|
+
case "product-available":
|
|
93
86
|
_.#productAvailable = _.#parseBooleanValue(newValue, true);
|
|
94
87
|
break;
|
|
95
|
-
case
|
|
88
|
+
case "message-above":
|
|
96
89
|
_.#messageAbove = newValue;
|
|
97
90
|
break;
|
|
98
|
-
case
|
|
91
|
+
case "message-below":
|
|
99
92
|
_.#messageBelow = newValue;
|
|
100
93
|
break;
|
|
101
|
-
case
|
|
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(
|
|
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
|
-
|
|
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 ===
|
|
147
|
-
if (value ===
|
|
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 ===
|
|
150
|
-
if (normalized ===
|
|
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(
|
|
132
|
+
const messageEl = _.querySelector("[data-content-gwp-message]");
|
|
157
133
|
if (!messageEl) return;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (_.#isActive && _.#
|
|
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 ?
|
|
142
|
+
messageEl.style.display = message ? "block" : "none";
|
|
172
143
|
}
|
|
173
|
-
|
|
174
144
|
#attachListeners() {
|
|
175
145
|
const _ = this;
|
|
176
|
-
_.#cartPanel = _.closest(
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
_.#
|
|
180
|
-
|
|
181
|
-
_.#
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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 ===
|
|
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
|
|
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
|
-
|
|
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 !==
|
|
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
|
-
|
|
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 (_.#
|
|
224
|
-
|
|
225
|
-
_.#
|
|
226
|
-
_.#
|
|
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
|
-
_.#
|
|
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 (_.#
|
|
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(
|
|
257
|
-
_.style.display =
|
|
264
|
+
_.setAttribute("state", "ended");
|
|
265
|
+
_.style.display = "none";
|
|
258
266
|
return;
|
|
259
267
|
}
|
|
260
|
-
|
|
261
268
|
if (!_.#productAvailable) {
|
|
262
|
-
_.setAttribute(
|
|
263
|
-
_.style.display =
|
|
269
|
+
_.setAttribute("state", "disabled");
|
|
270
|
+
_.style.display = "none";
|
|
264
271
|
return;
|
|
265
272
|
}
|
|
266
|
-
|
|
267
|
-
_.
|
|
268
|
-
if (_.#
|
|
269
|
-
else
|
|
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(
|
|
278
|
-
method:
|
|
279
|
-
credentials:
|
|
280
|
-
headers: {
|
|
281
|
-
|
|
282
|
-
|
|
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(
|
|
289
|
-
|
|
302
|
+
_.dispatchEvent(new CustomEvent("gwp:added", {
|
|
303
|
+
detail: { variantId: _.#variantId },
|
|
304
|
+
bubbles: true
|
|
305
|
+
}));
|
|
306
|
+
_.#refreshCartPanel();
|
|
290
307
|
} catch (err) {
|
|
291
|
-
console.error(
|
|
292
|
-
_.dispatchEvent(new CustomEvent(
|
|
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
|
-
_.#
|
|
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
|
-
_.#
|
|
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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
|
|
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(
|
|
353
|
-
|
|
373
|
+
_.dispatchEvent(new CustomEvent("gwp:removed", {
|
|
374
|
+
detail: { variantId: _.#variantId },
|
|
375
|
+
bubbles: true
|
|
376
|
+
}));
|
|
377
|
+
_.#refreshCartPanel();
|
|
354
378
|
} catch (err) {
|
|
355
|
-
console.error(
|
|
356
|
-
_.dispatchEvent(new CustomEvent(
|
|
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(
|
|
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(
|
|
439
|
+
_.#updateState(null);
|
|
414
440
|
_.#updateMessages();
|
|
415
441
|
}
|
|
416
|
-
|
|
417
442
|
setVariantId(variantId) {
|
|
418
443
|
this.#variantId = variantId;
|
|
419
444
|
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
|
|
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;
|
|
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;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*/
|