@magic-spells/gift-with-purchase 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Magic Spells
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,332 @@
1
+ # Gift with Purchase
2
+
3
+ A powerful, e-commerce web component for automatic gift-with-purchase threshold promotions. Seamlessly integrates with Shopify and automatically manages gift items in the cart based on spending thresholds.
4
+
5
+ [**Live Demo**](https://magic-spells.github.io/gift-with-purchase/demo/)
6
+
7
+ ## Features
8
+
9
+ - 🎁 **Automatic Gift Management** - Adds/removes gifts based on cart thresholds using smart pricing logic
10
+ - 🛒 **Shopify Integration** - Built-in Cart API support with proper line item properties
11
+ - 📱 **Cart Panel Sync** - Automatically syncs with cart-dialog components using `calculated_subtotal`
12
+ - 🎨 **Highly Customizable** - CSS custom properties and swappable content elements
13
+ - ⚡ **Event-Driven** - Custom events for gift addition, removal, and errors
14
+ - 🔧 **Flexible Content** - Data attributes for dynamic image, title, and variant updates
15
+ - 📱 **Responsive** - Mobile-optimized with responsive design
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @magic-spells/gift-with-purchase
21
+ ```
22
+
23
+ ## Basic Usage
24
+
25
+ ```html
26
+ <gift-with-purchase
27
+ threshold="75.00"
28
+ current="45.00"
29
+ variant-id="12345678"
30
+ message-above="🎉 Congratulations! You've qualified for your FREE gift!"
31
+ message-below="Add ${ amount } more to unlock your free gift! 🎁">
32
+ <div class="gwp-product">
33
+ <img src="gift-image.jpg" alt="Free Gift" />
34
+ <div class="gwp-content">
35
+ <h4 class="gwp-title">Free Sample Set</h4>
36
+ <p class="gwp-variant">Travel Size Collection</p>
37
+ </div>
38
+ </div>
39
+ <p data-content-gwp-message class="text-sm font-medium"></p>
40
+ </gift-with-purchase>
41
+ ```
42
+
43
+ ```css
44
+ @import '@magic-spells/gift-with-purchase/css';
45
+ ```
46
+
47
+ ## Cart Integration
48
+
49
+ The component automatically listens for cart data changes when placed inside a `<cart-dialog>` component from the `@magic-spells/cart-panel` package:
50
+
51
+ ```html
52
+ <cart-dialog>
53
+ <gift-with-purchase
54
+ threshold="75.00"
55
+ variant-id="12345678"
56
+ message-above="🎉 Congratulations! You've qualified for your FREE gift!"
57
+ message-below="Add ${ amount } more to unlock your free gift! 🎁">
58
+ <!-- Gift content -->
59
+ </gift-with-purchase>
60
+ </cart-dialog>
61
+ ```
62
+
63
+ When the cart-dialog emits a `cart-dialog:data-changed` event (typically from Shopify cart updates), the gift component will automatically:
64
+
65
+ - Update the current cart amount using `calculated_subtotal` for accurate threshold calculation
66
+ - Check if the threshold is met (excludes other gifts and honors pricing exclusions)
67
+ - Add the gift to cart if threshold is reached
68
+ - Remove the gift if cart falls below threshold
69
+
70
+ ### Smart Pricing Logic
71
+
72
+ The component uses intelligent threshold calculation:
73
+ - **Uses `calculated_subtotal`** from cart-panel which properly handles item exclusions
74
+ - **Excludes gifts**: Other gifts with purchase won't count toward this threshold
75
+ - **Includes bundle items**: Hidden bundle components that should count are included
76
+ - **Backwards compatible**: Falls back to `total_price` if `calculated_subtotal` unavailable
77
+
78
+ ## JavaScript API
79
+
80
+ ```javascript
81
+ const gwp = document.querySelector('gift-with-purchase');
82
+
83
+ // Update cart amount
84
+ gwp.setCurrentAmount(85.5);
85
+
86
+ // Change threshold
87
+ gwp.setThreshold(100.0);
88
+
89
+ // Update variant ID
90
+ gwp.setVariantId('87654321');
91
+
92
+ // Get current state
93
+ const state = gwp.getState();
94
+ console.log(state.isActive, state.isAdded, state.remainingAmount);
95
+
96
+ // Manual message updates (component handles this automatically)
97
+ const messageEl = gwp.querySelector('[data-content-gwp-message]');
98
+ if (messageEl) {
99
+ messageEl.textContent = 'Custom message';
100
+ }
101
+ ```
102
+
103
+ ## Attributes
104
+
105
+ | Attribute | Description | Example |
106
+ | --------------- | -------------------------------------------------------------------- | ------------------------------------------------------------ |
107
+ | `threshold` | Spending threshold to unlock the gift | `"75.00"` |
108
+ | `current` | Current cart amount | `"45.00"` |
109
+ | `variant-id` | Shopify variant ID for the gift product | `"12345678"` |
110
+ | `message-above` | Message shown when threshold is met | `"🎉 Congratulations! You've qualified for your FREE gift!"` |
111
+ | `message-below` | Message shown when below threshold (uses `{ amount }` placeholder) | `"Add ${ amount } more to unlock your free gift! 🎁"` |
112
+
113
+ ## Message Element
114
+
115
+ The component requires a message element to display threshold messages:
116
+
117
+ ```html
118
+ <gift-with-purchase threshold="75.00" variant-id="12345678">
119
+ <!-- Your gift content with any styling -->
120
+ <div class="gift-content">
121
+ <h4 class="font-bold">Free Gift</h4>
122
+ <p class="text-gray-600">Sample Description</p>
123
+ </div>
124
+
125
+ <!-- Required: Element where messages will be injected -->
126
+ <p data-content-gwp-message class="bg-green-100 p-2 rounded"></p>
127
+ </gift-with-purchase>
128
+ ```
129
+
130
+ ## Events
131
+
132
+ The component emits custom events for integration:
133
+
134
+ ```javascript
135
+ // Gift successfully added to cart
136
+ gwp.addEventListener('gwp:added', (event) => {
137
+ console.log('Gift added:', event.detail.variantId);
138
+ });
139
+
140
+ // Gift successfully removed from cart
141
+ gwp.addEventListener('gwp:removed', (event) => {
142
+ console.log('Gift removed:', event.detail.variantId);
143
+ });
144
+
145
+ // Error occurred during add/remove
146
+ gwp.addEventListener('gwp:error', (event) => {
147
+ console.error('Error:', event.detail.error);
148
+ console.log('Action:', event.detail.action); // 'add' or 'remove'
149
+ });
150
+ ```
151
+
152
+ ## Customization
153
+
154
+ Use CSS custom properties to customize the appearance:
155
+
156
+ ```css
157
+ gift-with-purchase {
158
+ --gwp-border-radius: 12px;
159
+ --gwp-padding: 1.5rem;
160
+ --gwp-bg-active: #e8f5e8;
161
+ --gwp-border-active: #28a745;
162
+ --gwp-text-active: #155724;
163
+ --gwp-image-size: 80px;
164
+ }
165
+ ```
166
+
167
+ ## Available CSS Custom Properties
168
+
169
+ | Property | Description | Default |
170
+ | --------------------- | ---------------------- | --------- |
171
+ | `--gwp-border-radius` | Border radius | `8px` |
172
+ | `--gwp-padding` | Internal padding | `1rem` |
173
+ | `--gwp-bg-active` | Background when active | `#e8f5e8` |
174
+ | `--gwp-bg-added` | Background when added | `#d4edda` |
175
+ | `--gwp-border-active` | Border when active | `#28a745` |
176
+ | `--gwp-border-added` | Border when added | `#155724` |
177
+ | `--gwp-text-active` | Text color when active | `#155724` |
178
+ | `--gwp-text-added` | Text color when added | `#155724` |
179
+ | `--gwp-gap` | Gap between elements | `1rem` |
180
+
181
+ ## Component States
182
+
183
+ The component automatically applies a `state` attribute based on its current condition:
184
+
185
+ - `state="active"` - Threshold met, gift available to add
186
+ - `state="added"` - Gift successfully added to cart
187
+ - `state="ended"` - Promo ended (component hidden)
188
+
189
+ Note: There is no `inactive` state since the component would typically not be loaded at all when below threshold in most Shopify implementations.
190
+
191
+ ## Shopify Integration Details
192
+
193
+ ### Cart API Calls
194
+
195
+ The component uses Shopify's Cart API endpoints:
196
+
197
+ - `POST /cart/add.js` - Adds the gift to cart
198
+ - `GET /cart.js` - Gets current cart state
199
+ - `POST /cart/change.js` - Removes gift from cart
200
+
201
+ ### Line Item Properties
202
+
203
+ When adding gifts to the cart, the component automatically sets these properties for proper cart integration:
204
+
205
+ ```javascript
206
+ {
207
+ id: variantId,
208
+ quantity: 1,
209
+ properties: {
210
+ _gwp_item: "true",
211
+ _hide_in_cart: "true",
212
+ _ignore_price_in_subtotal: "true"
213
+ }
214
+ }
215
+ ```
216
+
217
+ **Property Functions:**
218
+ - **`_gwp_item: "true"`** - Identifies gift items in cart templates and for removal logic
219
+ - **`_hide_in_cart: "true"`** - Hides gifts from cart display (handled by `@magic-spells/cart-panel`)
220
+ - **`_ignore_price_in_subtotal: "true"`** - Excludes gift price from subtotal calculations and threshold checks
221
+ - **Automatic management** - These properties ensure gifts don't interfere with other promotions or cart totals
222
+
223
+ ### Cart Template Integration
224
+
225
+ In your Shopify cart template, you can identify and handle gift items:
226
+
227
+ ```liquid
228
+ {% for item in cart.items %}
229
+ {% if item.properties._gwp_item == 'true' %}
230
+ <!-- This is a gift item -->
231
+ <div class="cart-gift-item">
232
+ <span class="gift-badge">FREE GIFT</span>
233
+ {{ item.product.title }}
234
+ </div>
235
+ {% else %}
236
+ <!-- Regular cart item -->
237
+ {% endif %}
238
+ {% endfor %}
239
+ ```
240
+
241
+ ## Advanced Usage
242
+
243
+ ### Multiple Thresholds
244
+
245
+ ```html
246
+ <!-- Low tier gift -->
247
+ <gift-with-purchase
248
+ threshold="50.00"
249
+ variant-id="111111"
250
+ message-above="🎉 You've unlocked a free tote bag!"
251
+ message-below="Add ${ amount } more for a free tote bag! 👜">
252
+ <div class="gwp-product">
253
+ <img src="tote-bag.jpg" alt="Free Tote Bag" />
254
+ <h4 class="gwp-title">Free Tote Bag</h4>
255
+ </div>
256
+ <p data-content-gwp-message class="message-low-tier"></p>
257
+ </gift-with-purchase>
258
+
259
+ <!-- High tier gift -->
260
+ <gift-with-purchase
261
+ threshold="100.00"
262
+ variant-id="222222"
263
+ message-above="✨ Amazing! You've earned our premium gift set!"
264
+ message-below="Spend ${ amount } more for our exclusive premium gift set! ✨">
265
+ <div class="gwp-product">
266
+ <img src="gift-set.jpg" alt="Premium Gift Set" />
267
+ <h4 class="gwp-title">Premium Gift Set</h4>
268
+ </div>
269
+ <p data-content-gwp-message class="message-premium"></p>
270
+ </gift-with-purchase>
271
+ ```
272
+
273
+ ### Custom Styling Examples
274
+
275
+ ```css
276
+ /* Minimal style */
277
+ .gwp-minimal {
278
+ --gwp-padding: 0.75rem;
279
+ --gwp-border-radius: 4px;
280
+ --gwp-bg-active: #f0f8f0;
281
+ }
282
+
283
+ /* Bold style */
284
+ .gwp-bold {
285
+ --gwp-padding: 1.5rem;
286
+ --gwp-border-radius: 12px;
287
+ --gwp-bg-active: #28a745;
288
+ --gwp-text-active: white;
289
+ }
290
+
291
+ /* Compact mobile style */
292
+ @media (max-width: 768px) {
293
+ gift-with-purchase {
294
+ --gwp-image-size: 40px;
295
+ --gwp-padding: 0.5rem;
296
+ --gwp-gap: 0.5rem;
297
+ }
298
+ }
299
+ ```
300
+
301
+ ## Browser Support
302
+
303
+ - Chrome/Edge 88+
304
+ - Firefox 85+
305
+ - Safari 14+
306
+ - All modern browsers with Custom Elements support
307
+
308
+ ## TypeScript Support
309
+
310
+ Type definitions are included in the package:
311
+
312
+ ```typescript
313
+ interface GiftWithPurchaseState {
314
+ currentAmount: number;
315
+ threshold: number;
316
+ variantId: string | null;
317
+ isActive: boolean;
318
+ isAdded: boolean;
319
+ remainingAmount: number;
320
+ }
321
+
322
+ // Component automatically handles message injection
323
+ // Style your content and message elements with any CSS classes
324
+ ```
325
+
326
+ ## Contributing
327
+
328
+ Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.
329
+
330
+ ## License
331
+
332
+ MIT License - see LICENSE file for details.
@@ -0,0 +1,43 @@
1
+ gift-with-purchase {
2
+ --gwp-border-radius: 8px;
3
+ --gwp-padding: 1rem;
4
+ --gwp-gap: 1rem;
5
+ --gwp-bg-active: #e8f5e8;
6
+ --gwp-bg-added: #d4edda;
7
+ --gwp-border-active: #28a745;
8
+ --gwp-border-added: #155724;
9
+ --gwp-text-active: #155724;
10
+ --gwp-text-added: #155724;
11
+ display: block;
12
+ border: 2px solid var(--gwp-border-active);
13
+ border-radius: var(--gwp-border-radius);
14
+ padding: var(--gwp-padding);
15
+ background-color: var(--gwp-bg-active);
16
+ color: var(--gwp-text-active);
17
+ }
18
+ gift-with-purchase .gwp-product {
19
+ display: flex;
20
+ align-items: flex-start;
21
+ gap: var(--gwp-gap);
22
+ }
23
+ gift-with-purchase [data-gwp-image] {
24
+ flex-shrink: 0;
25
+ }
26
+ gift-with-purchase .gwp-content {
27
+ flex: 1;
28
+ min-width: 0;
29
+ }
30
+ gift-with-purchase[state=active] {
31
+ background-color: var(--gwp-bg-active);
32
+ border-color: var(--gwp-border-active);
33
+ color: var(--gwp-text-active);
34
+ }
35
+ gift-with-purchase[state=added] {
36
+ background-color: var(--gwp-bg-added);
37
+ border-color: var(--gwp-border-added);
38
+ color: var(--gwp-text-added);
39
+ }
40
+ gift-with-purchase[state=ended] {
41
+ display: none;
42
+ }
43
+ /*# sourceMappingURL=gift-with-purchase.cjs.css.map */
@@ -0,0 +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}"]}