@runwell/shopify-toolkit 0.1.0 → 0.3.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/modules/_shared/css-tokens/assets/runwell-tokens.css +14 -0
- package/modules/_shared/css-tokens/module.json +13 -0
- package/modules/bundle-builder/README.md +40 -0
- package/modules/bundle-builder/assets/runwell-bundle-builder.css +383 -0
- package/modules/bundle-builder/module.json +26 -0
- package/modules/bundle-builder/sections/runwell-bundle-builder.liquid +370 -0
- package/modules/cart-cross-sell/README.md +32 -0
- package/modules/cart-cross-sell/module.json +15 -0
- package/modules/cart-cross-sell/snippets/runwell-cart-xsell.liquid +40 -0
- package/modules/cart-freeship-progress/README.md +29 -0
- package/modules/cart-freeship-progress/module.json +16 -0
- package/modules/cart-freeship-progress/snippets/runwell-cart-freeship.liquid +27 -0
- package/modules/cart-usps/README.md +22 -0
- package/modules/cart-usps/module.json +17 -0
- package/modules/cart-usps/snippets/runwell-cart-usps.liquid +11 -0
- package/modules/editorial-hero/sections/runwell-video-hero.liquid +9 -3
- package/modules/gift-with-purchase/README.md +36 -0
- package/modules/gift-with-purchase/assets/runwell-gwp.js +42 -0
- package/modules/gift-with-purchase/module.json +32 -0
- package/modules/gift-with-purchase/snippets/runwell-gwp.liquid +30 -0
- package/modules/loyalty-tiers/README.md +45 -0
- package/modules/loyalty-tiers/module.json +40 -0
- package/modules/loyalty-tiers/sections/runwell-tier-card.liquid +86 -0
- package/modules/product-badges/README.md +35 -0
- package/modules/product-badges/module.json +16 -0
- package/modules/product-badges/snippets/runwell-product-badges.liquid +19 -0
- package/modules/quantity-breaks/README.md +33 -0
- package/modules/quantity-breaks/module.json +35 -0
- package/modules/quantity-breaks/snippets/runwell-quantity-breaks.liquid +28 -0
- package/modules/quick-view/README.md +36 -0
- package/modules/quick-view/assets/runwell-quickview.js +153 -0
- package/modules/quick-view/module.json +14 -0
- package/modules/quick-view/snippets/runwell-quickview-modal.liquid +14 -0
- package/modules/quick-view/snippets/runwell-quickview-trigger.liquid +19 -0
- package/modules/subscriptions/README.md +37 -0
- package/modules/subscriptions/module.json +36 -0
- package/modules/subscriptions/snippets/runwell-subscription-picker.liquid +35 -0
- package/modules/wishlist/README.md +48 -0
- package/modules/wishlist/assets/runwell-wishlist.js +112 -0
- package/modules/wishlist/module.json +25 -0
- package/modules/wishlist/sections/runwell-wishlist-page.liquid +35 -0
- package/modules/wishlist/snippets/runwell-wishlist-icon.liquid +17 -0
- package/modules/wishlist/templates/page.wishlist.json +13 -0
- package/package.json +1 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
Runwell quick-view modal shell. Render once at end of layout/theme.liquid:
|
|
3
|
+
{% render 'runwell-quickview-modal' %}
|
|
4
|
+
{%- endcomment -%}
|
|
5
|
+
|
|
6
|
+
<div class="runwell-quickview" data-runwell-quickview-modal aria-hidden="true" role="dialog" aria-labelledby="runwell-qv-title">
|
|
7
|
+
<div class="runwell-quickview__backdrop" data-runwell-quickview-close></div>
|
|
8
|
+
<div class="runwell-quickview__panel">
|
|
9
|
+
<button class="runwell-quickview__close" type="button" data-runwell-quickview-close aria-label="Close">×</button>
|
|
10
|
+
<div class="runwell-quickview__body" data-runwell-quickview-body></div>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<script src="{{ 'runwell-quickview.js' | asset_url }}" defer="defer"></script>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
Runwell quick-view trigger button. Render inside card-product.liquid
|
|
3
|
+
near the product image:
|
|
4
|
+
|
|
5
|
+
{% render 'runwell-quickview-trigger', handle: card_product.handle %}
|
|
6
|
+
{%- endcomment -%}
|
|
7
|
+
|
|
8
|
+
<button
|
|
9
|
+
type="button"
|
|
10
|
+
class="runwell-quickview-trigger"
|
|
11
|
+
data-runwell-quickview
|
|
12
|
+
data-handle="{{ handle }}"
|
|
13
|
+
aria-label="Quick view"
|
|
14
|
+
>
|
|
15
|
+
<svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
|
|
16
|
+
<path d="M12 5C7 5 3 12 3 12s4 7 9 7 9-7 9-7-4-7-9-7zm0 11a4 4 0 110-8 4 4 0 010 8z" fill="currentColor"/>
|
|
17
|
+
<circle cx="12" cy="12" r="2" fill="#FFFFFF"/>
|
|
18
|
+
</svg>
|
|
19
|
+
</button>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# subscriptions
|
|
2
|
+
|
|
3
|
+
Brand-styled subscribe-and-save picker on top of Shopify's native Selling Plan API. Replaces Recharge / Bold Subscriptions / Appstle / Loop for the storefront display layer. Customer subscription management uses Shopify's native customer account.
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
- `snippets/runwell-subscription-picker.liquid`. Radio picker between one-time and subscribe-and-save.
|
|
8
|
+
|
|
9
|
+
## How to use
|
|
10
|
+
|
|
11
|
+
1. Run the merchant admin steps in `module.json` (install Shopify's free Subscriptions app, create a Selling Plan Group)
|
|
12
|
+
2. Render the picker in `sections/main-product.liquid` BEFORE the `buy_buttons` block:
|
|
13
|
+
```liquid
|
|
14
|
+
{% render 'runwell-subscription-picker', product: product %}
|
|
15
|
+
```
|
|
16
|
+
3. The picker emits a `selling_plan` form input that Dawn's buy-buttons snippet picks up automatically when present
|
|
17
|
+
4. Style `.runwell-sub` selectors in your brand stylesheet
|
|
18
|
+
|
|
19
|
+
## Config
|
|
20
|
+
|
|
21
|
+
| Key | Default | Notes |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| `one_time_label` | `One-time purchase` | Label for the no-subscription option |
|
|
24
|
+
| `subscribe_label` | `Subscribe and save` | Label for the subscription option |
|
|
25
|
+
| `fineprint` | `Cancel anytime. Skip a delivery from your account.` | Microcopy under the picker |
|
|
26
|
+
|
|
27
|
+
## Why no app
|
|
28
|
+
|
|
29
|
+
Shopify ships native subscription primitives (Selling Plan API, native customer-account subscription tab) since 2021. The free Shopify Subscriptions app is the canonical UI for managing Selling Plan Groups. We use that for management; this module handles the storefront PDP picker.
|
|
30
|
+
|
|
31
|
+
## Replaces
|
|
32
|
+
|
|
33
|
+
Recharge, Bold Subscriptions, Appstle, Loop (display layer only; the management UX is Shopify's native account tab).
|
|
34
|
+
|
|
35
|
+
## Plan requirement
|
|
36
|
+
|
|
37
|
+
Works on **Shopify Basic and up**. The free Shopify Subscriptions app is required.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "subscriptions",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"category": "catalog",
|
|
5
|
+
"description": "Brand-styled subscribe-and-save picker that wraps Shopify's native Selling Plan API. Replaces Recharge / Bold Subscriptions / Appstle for the storefront display layer; subscription management still uses Shopify's native customer account.",
|
|
6
|
+
"files": {
|
|
7
|
+
"snippets": ["snippets/runwell-subscription-picker.liquid"]
|
|
8
|
+
},
|
|
9
|
+
"config": {
|
|
10
|
+
"schema": {
|
|
11
|
+
"one_time_label": { "type": "string", "default": "One-time purchase" },
|
|
12
|
+
"subscribe_label": { "type": "string", "default": "Subscribe and save" },
|
|
13
|
+
"fineprint": { "type": "string", "default": "Cancel anytime. Skip a delivery from your account." }
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"admin_steps": [
|
|
17
|
+
{
|
|
18
|
+
"id": "install-shopify-subscriptions",
|
|
19
|
+
"label": "Install Shopify's free Subscriptions app",
|
|
20
|
+
"url": "https://apps.shopify.com/shopify-subscriptions",
|
|
21
|
+
"summary": "Install the Subscriptions app made by Shopify (free, first-party). Required to expose Selling Plan Groups."
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "create-selling-plan-group",
|
|
25
|
+
"label": "Create a Selling Plan Group",
|
|
26
|
+
"url": "https://admin.shopify.com/store/{store_handle}/apps",
|
|
27
|
+
"summary": "Apps > Subscriptions > Create plan. Name 'Subscribe and save'. Add delivery options every 30/60/90 days. Apply 15% discount on subscription orders. Apply to selected products."
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"id": "enable-customer-account-tab",
|
|
31
|
+
"label": "Verify Subscriptions tab on customer account",
|
|
32
|
+
"url": "https://admin.shopify.com/store/{store_handle}/settings/customer_accounts",
|
|
33
|
+
"summary": "Settings > Customer accounts > Customize. Confirm the Subscriptions section is enabled. Customers will see skip/cancel/change frequency from /account."
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
Runwell subscription picker. Brand-styled radio between one-time and
|
|
3
|
+
subscribe-and-save when a product has selling_plan_groups configured.
|
|
4
|
+
Render inside main-product.liquid where the buy buttons live, BEFORE
|
|
5
|
+
the buy_buttons block:
|
|
6
|
+
|
|
7
|
+
{% render 'runwell-subscription-picker', product: product %}
|
|
8
|
+
|
|
9
|
+
Requires:
|
|
10
|
+
- Shopify Subscriptions app installed (free, made by Shopify)
|
|
11
|
+
- At least one Selling Plan Group attached to the product
|
|
12
|
+
{%- endcomment -%}
|
|
13
|
+
|
|
14
|
+
{%- if product != blank and product.selling_plan_groups.size > 0 -%}
|
|
15
|
+
{%- assign group = product.selling_plan_groups[0] -%}
|
|
16
|
+
<fieldset class="runwell-sub" data-runwell-subscription-picker>
|
|
17
|
+
<legend class="visually-hidden">Purchase type</legend>
|
|
18
|
+
<label class="runwell-sub__option">
|
|
19
|
+
<input type="radio" name="selling_plan" value="" checked>
|
|
20
|
+
<span class="runwell-sub__title">{{config.one_time_label}}</span>
|
|
21
|
+
<span class="runwell-sub__price">{{ product.price | money }}</span>
|
|
22
|
+
</label>
|
|
23
|
+
{%- for plan in group.selling_plans -%}
|
|
24
|
+
<label class="runwell-sub__option runwell-sub__option--featured">
|
|
25
|
+
<input type="radio" name="selling_plan" value="{{ plan.id }}">
|
|
26
|
+
<span class="runwell-sub__title">
|
|
27
|
+
{{config.subscribe_label}}
|
|
28
|
+
<span class="runwell-sub__discount">{{ plan.price_adjustments[0].value }}% off</span>
|
|
29
|
+
</span>
|
|
30
|
+
<span class="runwell-sub__detail">{{ plan.name }}</span>
|
|
31
|
+
</label>
|
|
32
|
+
{%- endfor -%}
|
|
33
|
+
<p class="runwell-sub__fineprint">{{config.fineprint}}</p>
|
|
34
|
+
</fieldset>
|
|
35
|
+
{%- endif -%}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# wishlist
|
|
2
|
+
|
|
3
|
+
Native wishlist using localStorage. Replaces Wishlist Plus, Wishlist King, Globo Wishlist. App-free.
|
|
4
|
+
|
|
5
|
+
## Files
|
|
6
|
+
|
|
7
|
+
- `assets/runwell-wishlist.js`. Core storage + UI logic. ~120 LOC, no dependencies.
|
|
8
|
+
- `snippets/runwell-wishlist-icon.liquid`. Heart button to drop into card-product or main-product.
|
|
9
|
+
- `sections/runwell-wishlist-page.liquid`. Section that hydrates the dedicated wishlist page.
|
|
10
|
+
- `templates/page.wishlist.json`. Page template wiring the section to the `/pages/wishlist` URL.
|
|
11
|
+
|
|
12
|
+
## How to use
|
|
13
|
+
|
|
14
|
+
1. Sync the module: `runwell-shopify sync`
|
|
15
|
+
2. Add the heart icon wherever a product is listed:
|
|
16
|
+
```liquid
|
|
17
|
+
{% render 'runwell-wishlist-icon', handle: card_product.handle %}
|
|
18
|
+
```
|
|
19
|
+
3. Create the Wishlist page in admin (see admin_steps in module.json):
|
|
20
|
+
- Pages > Add page > Title "Wishlist" > Visible > Theme template "page.wishlist" > Save
|
|
21
|
+
4. Visit `/pages/wishlist` to see the saved items hydrate from localStorage.
|
|
22
|
+
|
|
23
|
+
## Config
|
|
24
|
+
|
|
25
|
+
| Key | Default | Notes |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| `max_items` | `100` | Max items kept in localStorage. Excess trimmed FIFO. |
|
|
28
|
+
|
|
29
|
+
## Storage
|
|
30
|
+
|
|
31
|
+
| Surface | Mechanism |
|
|
32
|
+
|---|---|
|
|
33
|
+
| Guest user | `localStorage["runwell_wishlist"] = ["handle-1", ...]` |
|
|
34
|
+
| Logged-in customer | localStorage only in v0.1; metafield sync is v0.2 (requires App Proxy) |
|
|
35
|
+
|
|
36
|
+
## Events
|
|
37
|
+
|
|
38
|
+
The module dispatches `runwell:wishlist:change` on `document` whenever an item is added or removed. Useful if you want to update a header counter:
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
document.addEventListener('runwell:wishlist:change', function (e) {
|
|
42
|
+
console.log('count:', e.detail.count);
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Replaces
|
|
47
|
+
|
|
48
|
+
Wishlist Plus, Wishlist King, Globo Wishlist (display layer; the metafield sync option in those apps is the only feature not yet built).
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/* Runwell wishlist. localStorage-backed for guests; reads/writes
|
|
2
|
+
customer metafield lushi.wishlist when logged in (writes require an
|
|
3
|
+
App Proxy; v1 ships localStorage only, sync to metafield is v0.2).
|
|
4
|
+
Replaces Wishlist Plus, Wishlist King, Globo Wishlist. */
|
|
5
|
+
(function () {
|
|
6
|
+
if (typeof window === 'undefined') return;
|
|
7
|
+
var KEY = 'runwell_wishlist';
|
|
8
|
+
var MAX = 100;
|
|
9
|
+
|
|
10
|
+
function read() {
|
|
11
|
+
try { return JSON.parse(localStorage.getItem(KEY) || '[]'); } catch (e) { return []; }
|
|
12
|
+
}
|
|
13
|
+
function write(list) {
|
|
14
|
+
try { localStorage.setItem(KEY, JSON.stringify(list.slice(0, MAX))); } catch (e) {}
|
|
15
|
+
}
|
|
16
|
+
function has(handle) { return read().indexOf(handle) !== -1; }
|
|
17
|
+
function add(handle) {
|
|
18
|
+
var list = read().filter(function (h) { return h !== handle; });
|
|
19
|
+
list.unshift(handle);
|
|
20
|
+
write(list);
|
|
21
|
+
fire('add', handle);
|
|
22
|
+
}
|
|
23
|
+
function remove(handle) {
|
|
24
|
+
var list = read().filter(function (h) { return h !== handle; });
|
|
25
|
+
write(list);
|
|
26
|
+
fire('remove', handle);
|
|
27
|
+
}
|
|
28
|
+
function toggle(handle) {
|
|
29
|
+
if (has(handle)) { remove(handle); return false; }
|
|
30
|
+
add(handle); return true;
|
|
31
|
+
}
|
|
32
|
+
function fire(action, handle) {
|
|
33
|
+
var event = new CustomEvent('runwell:wishlist:change', { detail: { action: action, handle: handle, count: read().length } });
|
|
34
|
+
document.dispatchEvent(event);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function bindButtons() {
|
|
38
|
+
document.querySelectorAll('[data-runwell-wishlist]').forEach(function (btn) {
|
|
39
|
+
if (btn.dataset.runwellWishlistBound === '1') return;
|
|
40
|
+
btn.dataset.runwellWishlistBound = '1';
|
|
41
|
+
var handle = btn.getAttribute('data-handle');
|
|
42
|
+
if (!handle) return;
|
|
43
|
+
function refresh() {
|
|
44
|
+
if (has(handle)) {
|
|
45
|
+
btn.setAttribute('aria-pressed', 'true');
|
|
46
|
+
btn.classList.add('is-saved');
|
|
47
|
+
} else {
|
|
48
|
+
btn.setAttribute('aria-pressed', 'false');
|
|
49
|
+
btn.classList.remove('is-saved');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
btn.addEventListener('click', function (e) {
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
toggle(handle);
|
|
55
|
+
refresh();
|
|
56
|
+
});
|
|
57
|
+
refresh();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function renderPage() {
|
|
62
|
+
var host = document.querySelector('[data-runwell-wishlist-page]');
|
|
63
|
+
if (!host) return;
|
|
64
|
+
var handles = read();
|
|
65
|
+
var grid = host.querySelector('[data-runwell-wishlist-grid]');
|
|
66
|
+
var emptyState = host.querySelector('[data-runwell-wishlist-empty]');
|
|
67
|
+
if (!handles.length) {
|
|
68
|
+
if (emptyState) emptyState.style.display = '';
|
|
69
|
+
if (grid) grid.style.display = 'none';
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (emptyState) emptyState.style.display = 'none';
|
|
73
|
+
if (grid) grid.style.display = '';
|
|
74
|
+
|
|
75
|
+
Promise.all(handles.map(function (h) {
|
|
76
|
+
return fetch('/products/' + h + '.js').then(function (r) { return r.ok ? r.json() : null; }).catch(function () { return null; });
|
|
77
|
+
})).then(function (products) {
|
|
78
|
+
if (!grid) return;
|
|
79
|
+
grid.innerHTML = products.filter(Boolean).map(function (p) {
|
|
80
|
+
var img = p.featured_image ? '<img src="' + p.featured_image + '" alt="' + p.title + '" loading="lazy">' : '';
|
|
81
|
+
var price = (p.price / 100).toFixed(2);
|
|
82
|
+
return '<a class="runwell-wishlist__card" href="/products/' + p.handle + '">' +
|
|
83
|
+
'<div class="runwell-wishlist__media">' + img + '</div>' +
|
|
84
|
+
'<div class="runwell-wishlist__title">' + p.title + '</div>' +
|
|
85
|
+
'<div class="runwell-wishlist__price">$' + price + '</div>' +
|
|
86
|
+
'<button type="button" class="runwell-wishlist__remove" data-handle="' + p.handle + '" aria-label="Remove from wishlist">Remove</button>' +
|
|
87
|
+
'</a>';
|
|
88
|
+
}).join('');
|
|
89
|
+
|
|
90
|
+
grid.querySelectorAll('.runwell-wishlist__remove').forEach(function (btn) {
|
|
91
|
+
btn.addEventListener('click', function (e) {
|
|
92
|
+
e.preventDefault();
|
|
93
|
+
remove(btn.getAttribute('data-handle'));
|
|
94
|
+
renderPage();
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
window.runwellWishlist = { has: has, add: add, remove: remove, toggle: toggle, list: read };
|
|
101
|
+
|
|
102
|
+
function init() {
|
|
103
|
+
bindButtons();
|
|
104
|
+
renderPage();
|
|
105
|
+
}
|
|
106
|
+
document.addEventListener('runwell:wishlist:change', bindButtons);
|
|
107
|
+
if (document.readyState === 'loading') {
|
|
108
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
109
|
+
} else {
|
|
110
|
+
init();
|
|
111
|
+
}
|
|
112
|
+
})();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wishlist",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"category": "customer",
|
|
5
|
+
"description": "Native wishlist using localStorage. Heart icon on cards + dedicated /pages/wishlist page. v1 ships localStorage; metafield sync via App Proxy is v0.2. Replaces Wishlist Plus, Wishlist King, Globo Wishlist.",
|
|
6
|
+
"files": {
|
|
7
|
+
"assets": ["assets/runwell-wishlist.js"],
|
|
8
|
+
"snippets": ["snippets/runwell-wishlist-icon.liquid"],
|
|
9
|
+
"sections": ["sections/runwell-wishlist-page.liquid"],
|
|
10
|
+
"templates": ["templates/page.wishlist.json"]
|
|
11
|
+
},
|
|
12
|
+
"config": {
|
|
13
|
+
"schema": {
|
|
14
|
+
"max_items": { "type": "number", "default": 100 }
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"admin_steps": [
|
|
18
|
+
{
|
|
19
|
+
"id": "create-wishlist-page",
|
|
20
|
+
"label": "Create the wishlist page in Shopify admin",
|
|
21
|
+
"url": "https://admin.shopify.com/store/{store_handle}/pages/new",
|
|
22
|
+
"summary": "Create a Page with title 'Wishlist'. Set visibility Visible. In the Theme template dropdown, select 'page.wishlist'. Save."
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
Runwell wishlist page section. Drop into a Shopify Page template
|
|
3
|
+
(e.g. templates/page.wishlist.json).
|
|
4
|
+
{%- endcomment -%}
|
|
5
|
+
|
|
6
|
+
<section class="runwell-wishlist" data-runwell-wishlist-page>
|
|
7
|
+
<div class="runwell-wishlist__inner">
|
|
8
|
+
{%- if section.settings.heading != blank -%}
|
|
9
|
+
<h1 class="runwell-wishlist__heading">{{ section.settings.heading }}</h1>
|
|
10
|
+
{%- endif -%}
|
|
11
|
+
<div class="runwell-wishlist__empty" data-runwell-wishlist-empty>
|
|
12
|
+
<p>{{ section.settings.empty_text }}</p>
|
|
13
|
+
<a href="{{ routes.collections_url }}/all" class="runwell-wishlist__cta">{{ section.settings.empty_cta }}</a>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="runwell-wishlist__grid" data-runwell-wishlist-grid></div>
|
|
16
|
+
</div>
|
|
17
|
+
</section>
|
|
18
|
+
|
|
19
|
+
<script src="{{ 'runwell-wishlist.js' | asset_url }}" defer="defer"></script>
|
|
20
|
+
|
|
21
|
+
{% schema %}
|
|
22
|
+
{
|
|
23
|
+
"name": "Runwell wishlist page",
|
|
24
|
+
"tag": "section",
|
|
25
|
+
"class": "section-runwell-wishlist",
|
|
26
|
+
"settings": [
|
|
27
|
+
{ "type": "text", "id": "heading", "label": "Heading", "default": "Saved for later" },
|
|
28
|
+
{ "type": "text", "id": "empty_text", "label": "Empty state text", "default": "Save items as you browse and they will show up here." },
|
|
29
|
+
{ "type": "text", "id": "empty_cta", "label": "Empty state CTA", "default": "Start browsing" }
|
|
30
|
+
],
|
|
31
|
+
"presets": [
|
|
32
|
+
{ "name": "Runwell wishlist page" }
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
{% endschema %}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{%- comment -%}
|
|
2
|
+
Runwell wishlist heart button. Drop into card-product or main-product.
|
|
3
|
+
Caller sets handle via {% render 'runwell-wishlist-icon', handle: card_product.handle %}
|
|
4
|
+
{%- endcomment -%}
|
|
5
|
+
|
|
6
|
+
<button
|
|
7
|
+
type="button"
|
|
8
|
+
class="runwell-wishlist-icon"
|
|
9
|
+
data-runwell-wishlist
|
|
10
|
+
data-handle="{{ handle }}"
|
|
11
|
+
aria-pressed="false"
|
|
12
|
+
aria-label="Save for later"
|
|
13
|
+
>
|
|
14
|
+
<svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
|
|
15
|
+
<path d="M12 21s-7-4.35-9.5-9C.5 7 4 3 7.5 3c2 0 3.5 1 4.5 2.5C13 4 14.5 3 16.5 3 20 3 23.5 7 21.5 12 19 16.65 12 21 12 21z" fill="none" stroke="currentColor" stroke-width="1.5"/>
|
|
16
|
+
</svg>
|
|
17
|
+
</button>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sections": {
|
|
3
|
+
"wishlist": {
|
|
4
|
+
"type": "runwell-wishlist-page",
|
|
5
|
+
"settings": {
|
|
6
|
+
"heading": "Saved for later",
|
|
7
|
+
"empty_text": "Save items as you browse and they will show up here.",
|
|
8
|
+
"empty_cta": "Start browsing"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"order": ["wishlist"]
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runwell/shopify-toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Reusable Shopify theme modules from Runwell. Replaces typically app-driven features (reviews, wishlist, urgency, FAQ, post-purchase upsell, exit popups, free-ship progress, sticky ATC, testimonials, badges, bundles) with native Liquid + JS + CSS that ship across multiple client themes via a config-driven sync CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|