@magic-spells/gift-with-purchase 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.
- package/README.md +123 -26
- package/dist/gift-with-purchase.cjs.css +3 -0
- package/dist/gift-with-purchase.cjs.css.map +1 -1
- package/dist/gift-with-purchase.cjs.js +276 -186
- package/dist/gift-with-purchase.cjs.js.map +1 -1
- package/dist/gift-with-purchase.esm.css +3 -0
- package/dist/gift-with-purchase.esm.css.map +1 -1
- package/dist/gift-with-purchase.esm.js +276 -186
- package/dist/gift-with-purchase.esm.js.map +1 -1
- package/dist/gift-with-purchase.min.css +1 -1
- package/dist/gift-with-purchase.min.css.map +1 -1
- package/dist/gift-with-purchase.min.js +1 -1
- package/dist/gift-with-purchase.min.js.map +1 -1
- package/dist/gift-with-purchase.scss +5 -0
- package/package.json +4 -4
- package/src/gift-with-purchase.js +276 -186
- package/src/gift-with-purchase.scss +5 -0
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ A powerful, e-commerce web component for automatic gift-with-purchase threshold
|
|
|
8
8
|
|
|
9
9
|
- 🎁 **Automatic Gift Management** - Adds/removes gifts based on cart thresholds using smart pricing logic
|
|
10
10
|
- 🛒 **Shopify Integration** - Built-in Cart API support with proper line item properties
|
|
11
|
-
- 📱 **Cart Panel Sync** - Automatically syncs with cart-
|
|
11
|
+
- 📱 **Cart Panel Sync** - Automatically syncs with cart-panel components using `calculated_subtotal`
|
|
12
12
|
- 🎨 **Highly Customizable** - CSS custom properties and swappable content elements
|
|
13
13
|
- ⚡ **Event-Driven** - Custom events for gift addition, removal, and errors
|
|
14
14
|
- 🔧 **Flexible Content** - Data attributes for dynamic image, title, and variant updates
|
|
@@ -27,8 +27,9 @@ npm install @magic-spells/gift-with-purchase
|
|
|
27
27
|
threshold="75.00"
|
|
28
28
|
current="45.00"
|
|
29
29
|
variant-id="12345678"
|
|
30
|
+
money-format="${{amount}}"
|
|
30
31
|
message-above="🎉 Congratulations! You've qualified for your FREE gift!"
|
|
31
|
-
message-below="Add
|
|
32
|
+
message-below="Add [amount] more to unlock your free gift! 🎁">
|
|
32
33
|
<div class="gwp-product">
|
|
33
34
|
<img src="gift-image.jpg" alt="Free Gift" />
|
|
34
35
|
<div class="gwp-content">
|
|
@@ -46,21 +47,22 @@ npm install @magic-spells/gift-with-purchase
|
|
|
46
47
|
|
|
47
48
|
## Cart Integration
|
|
48
49
|
|
|
49
|
-
The component automatically listens for cart data changes when placed inside a `<cart-
|
|
50
|
+
The component automatically listens for cart data changes when placed inside a `<cart-panel>` component from the `@magic-spells/cart-panel` package:
|
|
50
51
|
|
|
51
|
-
```
|
|
52
|
-
<cart-
|
|
52
|
+
```liquid
|
|
53
|
+
<cart-panel>
|
|
53
54
|
<gift-with-purchase
|
|
54
55
|
threshold="75.00"
|
|
55
56
|
variant-id="12345678"
|
|
57
|
+
money-format={{ shop.money_format | json }}
|
|
56
58
|
message-above="🎉 Congratulations! You've qualified for your FREE gift!"
|
|
57
|
-
message-below="Add
|
|
59
|
+
message-below="Add [amount] more to unlock your free gift! 🎁">
|
|
58
60
|
<!-- Gift content -->
|
|
59
61
|
</gift-with-purchase>
|
|
60
|
-
</cart-
|
|
62
|
+
</cart-panel>
|
|
61
63
|
```
|
|
62
64
|
|
|
63
|
-
When the cart-
|
|
65
|
+
When the cart-panel emits a `cart-panel:data-changed` event (typically from Shopify cart updates), the gift component will automatically:
|
|
64
66
|
|
|
65
67
|
- Update the current cart amount using `calculated_subtotal` for accurate threshold calculation
|
|
66
68
|
- Check if the threshold is met (excludes other gifts and honors pricing exclusions)
|
|
@@ -73,42 +75,74 @@ The component uses intelligent threshold calculation:
|
|
|
73
75
|
- **Uses `calculated_subtotal`** from cart-panel which properly handles item exclusions
|
|
74
76
|
- **Excludes gifts**: Other gifts with purchase won't count toward this threshold
|
|
75
77
|
- **Includes bundle items**: Hidden bundle components that should count are included
|
|
76
|
-
- **
|
|
78
|
+
- **Multi-currency support**: Automatically converts threshold using `Shopify.currency.rate` for stores with multiple currencies
|
|
79
|
+
- **Requires cart-panel**: The component requires `calculated_subtotal` in cart events to function
|
|
77
80
|
|
|
78
81
|
## JavaScript API
|
|
79
82
|
|
|
80
83
|
```javascript
|
|
81
84
|
const gwp = document.querySelector('gift-with-purchase');
|
|
82
85
|
|
|
83
|
-
//
|
|
86
|
+
// Setters - update state programmatically
|
|
84
87
|
gwp.setCurrentAmount(85.5);
|
|
85
|
-
|
|
86
|
-
// Change threshold
|
|
87
88
|
gwp.setThreshold(100.0);
|
|
88
|
-
|
|
89
|
-
// Update variant ID
|
|
90
89
|
gwp.setVariantId('87654321');
|
|
91
90
|
|
|
92
|
-
// Get
|
|
91
|
+
// Get full state object
|
|
93
92
|
const state = gwp.getState();
|
|
94
93
|
console.log(state.isActive, state.isAdded, state.remainingAmount);
|
|
95
94
|
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
// Readonly getters - access individual properties
|
|
96
|
+
console.log(gwp.currentAmount); // number - current cart amount
|
|
97
|
+
console.log(gwp.threshold); // number - threshold amount
|
|
98
|
+
console.log(gwp.variantId); // string - gift variant ID
|
|
99
|
+
console.log(gwp.isActive); // boolean - threshold met
|
|
100
|
+
console.log(gwp.isAdded); // boolean - gift in cart
|
|
101
|
+
console.log(gwp.promoEnded); // boolean - promo has ended
|
|
102
|
+
console.log(gwp.productAvailable); // boolean - gift product available
|
|
103
|
+
console.log(gwp.isDisabled); // boolean - promo ended OR product unavailable
|
|
101
104
|
```
|
|
102
105
|
|
|
103
106
|
## Attributes
|
|
104
107
|
|
|
105
108
|
| Attribute | Description | Example |
|
|
106
109
|
| --------------- | -------------------------------------------------------------------- | ------------------------------------------------------------ |
|
|
107
|
-
| `threshold` | Spending threshold to unlock the gift
|
|
108
|
-
| `current` | Current cart amount
|
|
110
|
+
| `threshold` | Spending threshold to unlock the gift (auto-converts for multi-currency) | `"75.00"` |
|
|
111
|
+
| `current` | Current cart amount (typically set automatically via cart-panel) | `"45.00"` |
|
|
109
112
|
| `variant-id` | Shopify variant ID for the gift product | `"12345678"` |
|
|
113
|
+
| `promo-ended` | Disables the promo and hides the component | `"true"` |
|
|
114
|
+
| `product-available` | Whether the gift product is available (disables if false) | `"true"` |
|
|
110
115
|
| `message-above` | Message shown when threshold is met | `"🎉 Congratulations! You've qualified for your FREE gift!"` |
|
|
111
|
-
| `message-below` | Message shown
|
|
116
|
+
| `message-below` | Message shown below threshold; use `[amount]` for remaining amount | `"Add [amount] more to unlock your free gift! 🎁"` |
|
|
117
|
+
| `money-format` | Shopify-style money format for currency display | `"${{amount}}"` |
|
|
118
|
+
|
|
119
|
+
### The `[amount]` Placeholder
|
|
120
|
+
|
|
121
|
+
In the `message-below` attribute, use `[amount]` (with square brackets) to insert the remaining amount needed to reach the threshold. The component automatically:
|
|
122
|
+
|
|
123
|
+
- Calculates the remaining amount (`threshold - currentAmount`)
|
|
124
|
+
- Converts to the customer's currency using `Shopify.currency.rate`
|
|
125
|
+
- Formats using your `money-format` attribute
|
|
126
|
+
|
|
127
|
+
```html
|
|
128
|
+
<!-- Example: If threshold is $75 and cart is $45, displays "Add $30 more..." -->
|
|
129
|
+
<gift-with-purchase
|
|
130
|
+
threshold="75.00"
|
|
131
|
+
money-format="${{amount}}"
|
|
132
|
+
message-below="Add [amount] more to unlock your free gift!">
|
|
133
|
+
</gift-with-purchase>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Why square brackets?** We use `[amount]` instead of `{{amount}}` to avoid conflicts with Shopify Liquid templates and JavaScript template literals.
|
|
137
|
+
|
|
138
|
+
### Multi-Currency Support
|
|
139
|
+
|
|
140
|
+
The component automatically handles currency conversion for multi-currency Shopify stores:
|
|
141
|
+
|
|
142
|
+
- Set `threshold` in your store's **base currency** (e.g., USD)
|
|
143
|
+
- The component reads `Shopify.currency.rate` from the browser
|
|
144
|
+
- Threshold is automatically converted to the customer's selected currency
|
|
145
|
+
- The `[amount]` placeholder displays in the converted currency
|
|
112
146
|
|
|
113
147
|
## Message Element
|
|
114
148
|
|
|
@@ -127,6 +161,62 @@ The component requires a message element to display threshold messages:
|
|
|
127
161
|
</gift-with-purchase>
|
|
128
162
|
```
|
|
129
163
|
|
|
164
|
+
## Currency Formatting
|
|
165
|
+
|
|
166
|
+
Use the `money-format` attribute to format amounts with the correct currency symbol and formatting:
|
|
167
|
+
|
|
168
|
+
```html
|
|
169
|
+
<gift-with-purchase
|
|
170
|
+
threshold="75.00"
|
|
171
|
+
variant-id="12345678"
|
|
172
|
+
money-format="${{amount}}"
|
|
173
|
+
message-below="Add [amount] more to unlock your free gift!">
|
|
174
|
+
<!-- Gift content -->
|
|
175
|
+
</gift-with-purchase>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
In Shopify themes, you can pass the shop's money format using the `json` filter:
|
|
179
|
+
|
|
180
|
+
```liquid
|
|
181
|
+
<gift-with-purchase
|
|
182
|
+
threshold="75.00"
|
|
183
|
+
variant-id="12345678"
|
|
184
|
+
money-format={{ shop.money_format | json }}
|
|
185
|
+
message-below="Add [amount] more to unlock your free gift!">
|
|
186
|
+
<!-- Gift content -->
|
|
187
|
+
</gift-with-purchase>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Available Shopify money formats:
|
|
191
|
+
- `shop.money_format` - Basic format (e.g., `${{amount}}`)
|
|
192
|
+
- `shop.money_with_currency_format` - With currency code (e.g., `${{amount}} USD`)
|
|
193
|
+
|
|
194
|
+
### Supported Format Placeholders
|
|
195
|
+
|
|
196
|
+
| Placeholder | Output | Example |
|
|
197
|
+
| ----------- | ------ | ------- |
|
|
198
|
+
| `{{amount}}` | Amount with decimals | `19.99` |
|
|
199
|
+
| `{{amount_no_decimals}}` | Rounded whole number | `20` |
|
|
200
|
+
| `{{amount_with_comma_separator}}` | Comma as decimal | `19,99` |
|
|
201
|
+
| `{{amount_no_decimals_with_comma_separator}}` | Whole with comma thousands | `1,000` |
|
|
202
|
+
|
|
203
|
+
### Examples by Currency
|
|
204
|
+
|
|
205
|
+
```html
|
|
206
|
+
<!-- USD -->
|
|
207
|
+
money-format="${{amount}}" <!-- $30.00 -->
|
|
208
|
+
|
|
209
|
+
<!-- EUR -->
|
|
210
|
+
money-format="€{{amount}}" <!-- €30.00 -->
|
|
211
|
+
money-format="{{amount}} €" <!-- 30.00 € -->
|
|
212
|
+
|
|
213
|
+
<!-- GBP -->
|
|
214
|
+
money-format="£{{amount}}" <!-- £30.00 -->
|
|
215
|
+
|
|
216
|
+
<!-- JPY (no decimals) -->
|
|
217
|
+
money-format="¥{{amount_no_decimals}}" <!-- ¥3000 -->
|
|
218
|
+
```
|
|
219
|
+
|
|
130
220
|
## Events
|
|
131
221
|
|
|
132
222
|
The component emits custom events for integration:
|
|
@@ -184,9 +274,11 @@ The component automatically applies a `state` attribute based on its current con
|
|
|
184
274
|
|
|
185
275
|
- `state="active"` - Threshold met, gift available to add
|
|
186
276
|
- `state="added"` - Gift successfully added to cart
|
|
277
|
+
- `state="inactive"` - Below threshold (component idle)
|
|
187
278
|
- `state="ended"` - Promo ended (component hidden)
|
|
279
|
+
- `state="disabled"` - Gift unavailable (component hidden)
|
|
188
280
|
|
|
189
|
-
Note:
|
|
281
|
+
Note: `ended` and `disabled` both hide the component while still removing any existing gift items.
|
|
190
282
|
|
|
191
283
|
## Shopify Integration Details
|
|
192
284
|
|
|
@@ -248,7 +340,7 @@ In your Shopify cart template, you can identify and handle gift items:
|
|
|
248
340
|
threshold="50.00"
|
|
249
341
|
variant-id="111111"
|
|
250
342
|
message-above="🎉 You've unlocked a free tote bag!"
|
|
251
|
-
message-below="Add
|
|
343
|
+
message-below="Add [amount] more for a free tote bag! 👜">
|
|
252
344
|
<div class="gwp-product">
|
|
253
345
|
<img src="tote-bag.jpg" alt="Free Tote Bag" />
|
|
254
346
|
<h4 class="gwp-title">Free Tote Bag</h4>
|
|
@@ -261,7 +353,7 @@ In your Shopify cart template, you can identify and handle gift items:
|
|
|
261
353
|
threshold="100.00"
|
|
262
354
|
variant-id="222222"
|
|
263
355
|
message-above="✨ Amazing! You've earned our premium gift set!"
|
|
264
|
-
message-below="Spend
|
|
356
|
+
message-below="Spend [amount] more for our exclusive premium gift set! ✨">
|
|
265
357
|
<div class="gwp-product">
|
|
266
358
|
<img src="gift-set.jpg" alt="Premium Gift Set" />
|
|
267
359
|
<h4 class="gwp-title">Premium Gift Set</h4>
|
|
@@ -313,10 +405,15 @@ Type definitions are included in the package:
|
|
|
313
405
|
interface GiftWithPurchaseState {
|
|
314
406
|
currentAmount: number;
|
|
315
407
|
threshold: number;
|
|
408
|
+
convertedThreshold: number;
|
|
316
409
|
variantId: string | null;
|
|
317
410
|
isActive: boolean;
|
|
318
411
|
isAdded: boolean;
|
|
412
|
+
promoEnded: boolean;
|
|
413
|
+
productAvailable: boolean;
|
|
414
|
+
isDisabled: boolean;
|
|
319
415
|
remainingAmount: number;
|
|
416
|
+
currencyRate: number;
|
|
320
417
|
}
|
|
321
418
|
|
|
322
419
|
// Component automatically handles message injection
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["gift-with-purchase.scss"],"names":[],"mappings":"AAAA;EACE,wBAAwB;EACxB,mBAAmB;EACnB,eAAe;EACf,wBAAwB;EACxB,uBAAuB;EACvB,4BAA4B;EAC5B,2BAA2B;EAC3B,0BAA0B;EAC1B,yBAAyB;EACzB,cAAc;EACd,0CAA0C;EAC1C,uCAAuC;EACvC,2BAA2B;EAC3B,sCAAsC;EACtC,6BAA6B;AAC/B;AACA;EACE,aAAa;EACb,uBAAuB;EACvB,mBAAmB;AACrB;AACA;EACE,cAAc;AAChB;AACA;EACE,OAAO;EACP,YAAY;AACd;AACA;EACE,sCAAsC;EACtC,sCAAsC;EACtC,6BAA6B;AAC/B;AACA;EACE,qCAAqC;EACrC,qCAAqC;EACrC,4BAA4B;AAC9B;AACA;EACE,aAAa;AACf","file":"gift-with-purchase.cjs.css","sourcesContent":["gift-with-purchase {\n --gwp-border-radius: 8px;\n --gwp-padding: 1rem;\n --gwp-gap: 1rem;\n --gwp-bg-active: #e8f5e8;\n --gwp-bg-added: #d4edda;\n --gwp-border-active: #28a745;\n --gwp-border-added: #155724;\n --gwp-text-active: #155724;\n --gwp-text-added: #155724;\n display: block;\n border: 2px solid var(--gwp-border-active);\n border-radius: var(--gwp-border-radius);\n padding: var(--gwp-padding);\n background-color: var(--gwp-bg-active);\n color: var(--gwp-text-active);\n}\ngift-with-purchase .gwp-product {\n display: flex;\n align-items: flex-start;\n gap: var(--gwp-gap);\n}\ngift-with-purchase [data-gwp-image] {\n flex-shrink: 0;\n}\ngift-with-purchase .gwp-content {\n flex: 1;\n min-width: 0;\n}\ngift-with-purchase[state=active] {\n background-color: var(--gwp-bg-active);\n border-color: var(--gwp-border-active);\n color: var(--gwp-text-active);\n}\ngift-with-purchase[state=added] {\n background-color: var(--gwp-bg-added);\n border-color: var(--gwp-border-added);\n color: var(--gwp-text-added);\n}\ngift-with-purchase[state=ended] {\n display: none;\n}"]}
|
|
1
|
+
{"version":3,"sources":["gift-with-purchase.scss"],"names":[],"mappings":"AAAA;EACE,wBAAwB;EACxB,mBAAmB;EACnB,eAAe;EACf,wBAAwB;EACxB,uBAAuB;EACvB,4BAA4B;EAC5B,2BAA2B;EAC3B,0BAA0B;EAC1B,yBAAyB;EACzB,cAAc;EACd,0CAA0C;EAC1C,uCAAuC;EACvC,2BAA2B;EAC3B,sCAAsC;EACtC,6BAA6B;AAC/B;AACA;EACE,aAAa;EACb,uBAAuB;EACvB,mBAAmB;AACrB;AACA;EACE,cAAc;AAChB;AACA;EACE,OAAO;EACP,YAAY;AACd;AACA;EACE,sCAAsC;EACtC,sCAAsC;EACtC,6BAA6B;AAC/B;AACA;EACE,qCAAqC;EACrC,qCAAqC;EACrC,4BAA4B;AAC9B;AACA;EACE,aAAa;AACf;AACA;EACE,aAAa;AACf","file":"gift-with-purchase.cjs.css","sourcesContent":["gift-with-purchase {\n --gwp-border-radius: 8px;\n --gwp-padding: 1rem;\n --gwp-gap: 1rem;\n --gwp-bg-active: #e8f5e8;\n --gwp-bg-added: #d4edda;\n --gwp-border-active: #28a745;\n --gwp-border-added: #155724;\n --gwp-text-active: #155724;\n --gwp-text-added: #155724;\n display: block;\n border: 2px solid var(--gwp-border-active);\n border-radius: var(--gwp-border-radius);\n padding: var(--gwp-padding);\n background-color: var(--gwp-bg-active);\n color: var(--gwp-text-active);\n}\ngift-with-purchase .gwp-product {\n display: flex;\n align-items: flex-start;\n gap: var(--gwp-gap);\n}\ngift-with-purchase [data-gwp-image] {\n flex-shrink: 0;\n}\ngift-with-purchase .gwp-content {\n flex: 1;\n min-width: 0;\n}\ngift-with-purchase[state=active] {\n background-color: var(--gwp-bg-active);\n border-color: var(--gwp-border-active);\n color: var(--gwp-text-active);\n}\ngift-with-purchase[state=added] {\n background-color: var(--gwp-bg-added);\n border-color: var(--gwp-border-added);\n color: var(--gwp-text-added);\n}\ngift-with-purchase[state=ended] {\n display: none;\n}\ngift-with-purchase[state=disabled] {\n display: none;\n}"]}
|