@o2vend/theme-cli 1.0.36 → 1.0.38
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 +4 -0
- package/lib/lib/dev-server.js +344 -48
- package/lib/lib/liquid-engine.js +3 -1
- package/lib/lib/mock-data.js +473 -119
- package/lib/lib/widget-service.js +12 -4
- package/package.json +2 -2
- package/test-theme/assets/async-sections.js +32 -24
- package/test-theme/assets/cart-drawer.js +20 -22
- package/test-theme/assets/cart-manager.js +1 -15
- package/test-theme/assets/checkout-price-handler.js +12 -11
- package/test-theme/assets/checkout.css +1415 -0
- package/test-theme/assets/checkout.js +3174 -0
- package/test-theme/assets/components.css +178 -29
- package/test-theme/assets/delivery-zone.js +1 -1
- package/test-theme/assets/product-detail.css +1050 -0
- package/test-theme/assets/product-detail.js +2940 -0
- package/test-theme/assets/theme.css +95 -120
- package/test-theme/assets/theme.js +781 -186
- package/test-theme/layout/theme.liquid +91 -17
- package/test-theme/sections/content.liquid +64 -57
- package/test-theme/sections/footer-fallback.liquid +57 -7
- package/test-theme/sections/footer.liquid +63 -12
- package/test-theme/sections/header-fallback.liquid +41 -41
- package/test-theme/sections/header.liquid +41 -51
- package/test-theme/sections/hero-fallback.liquid +1 -1
- package/test-theme/sections/hero.liquid +159 -136
- package/test-theme/snippets/account-sidebar.liquid +121 -29
- package/test-theme/snippets/add-to-cart-modal.liquid +258 -206
- package/test-theme/snippets/breadcrumbs.liquid +98 -11
- package/test-theme/snippets/cart-drawer.liquid +93 -0
- package/test-theme/snippets/delivery-zone-city-selector.liquid +101 -15
- package/test-theme/snippets/delivery-zone-modal.liquid +529 -84
- package/test-theme/snippets/delivery-zone-search.liquid +104 -18
- package/test-theme/snippets/login-modal.liquid +269 -82
- package/test-theme/snippets/mega-menu.liquid +130 -43
- package/test-theme/snippets/news-thumbnail.liquid +120 -28
- package/test-theme/snippets/pagination.liquid +1 -1
- package/test-theme/snippets/price.liquid +100 -9
- package/test-theme/snippets/product-card-related.liquid +22 -4
- package/test-theme/snippets/product-card-simple.liquid +521 -25
- package/test-theme/snippets/product-card.liquid +145 -232
- package/test-theme/snippets/rating.liquid +100 -9
- package/test-theme/snippets/skeleton-collection-grid.liquid +94 -8
- package/test-theme/snippets/skeleton-product-card.liquid +102 -16
- package/test-theme/snippets/skeleton-product-grid.liquid +87 -1
- package/test-theme/snippets/social-sharing.liquid +133 -32
- package/test-theme/templates/account/dashboard.liquid +30 -0
- package/test-theme/templates/account/loyalty-redemption.liquid +29 -28
- package/test-theme/templates/account/loyalty.liquid +45 -43
- package/test-theme/templates/account/order-detail.liquid +15 -8
- package/test-theme/templates/account/orders.liquid +189 -35
- package/test-theme/templates/account/profile.liquid +509 -114
- package/test-theme/templates/account/register.liquid +18 -8
- package/test-theme/templates/account/return-orders.liquid +31 -30
- package/test-theme/templates/account/store-credit.liquid +27 -26
- package/test-theme/templates/account/subscriptions.liquid +22 -5
- package/test-theme/templates/account/wishlist.liquid +88 -19
- package/test-theme/templates/address-book.liquid +166 -69
- package/test-theme/templates/categories.liquid +90 -30
- package/test-theme/templates/checkout.liquid +137 -3834
- package/test-theme/templates/error.liquid +23 -21
- package/test-theme/templates/index.liquid +29 -0
- package/test-theme/templates/login.liquid +33 -6
- package/test-theme/templates/order-confirmation.liquid +67 -9
- package/test-theme/templates/page.liquid +418 -206
- package/test-theme/templates/product-detail.liquid +124 -3878
- package/test-theme/templates/products.liquid +155 -30
- package/test-theme/templates/search.liquid +739 -225
- package/test-theme/widgets/brand-carousel.liquid +102 -82
- package/test-theme/widgets/brand.liquid +78 -50
- package/test-theme/widgets/carousel.liquid +253 -121
- package/test-theme/widgets/category-list-carousel.liquid +32 -8
- package/test-theme/widgets/category-list.liquid +21 -6
- package/test-theme/widgets/category.liquid +104 -37
- package/test-theme/widgets/discount-time.liquid +326 -119
- package/test-theme/widgets/footer-menu.liquid +115 -23
- package/test-theme/widgets/footer.liquid +118 -5
- package/test-theme/widgets/gallery.liquid +29 -5
- package/test-theme/widgets/header-menu.liquid +25 -13
- package/test-theme/widgets/header.liquid +64 -26
- package/test-theme/widgets/html.liquid +29 -6
- package/test-theme/widgets/news.liquid +6 -0
- package/test-theme/widgets/product-canvas.liquid +20 -12
- package/test-theme/widgets/product-carousel.liquid +118 -56
- package/test-theme/widgets/shared/product-grid.liquid +12 -0
- package/test-theme/widgets/single-product.liquid +688 -250
- package/test-theme/widgets/spacebar-carousel.liquid +39 -10
- package/test-theme/widgets/spacebar.liquid +77 -6
- package/test-theme/widgets/splash.liquid +40 -30
- package/test-theme/widgets/testimonial-carousel.liquid +111 -67
|
@@ -40,16 +40,22 @@
|
|
|
40
40
|
assign background_color = widget_settings.backgroundColor | default: widget_settings.BackgroundColor | default: widget_content.BackgroundColor | default: widget_content.backgroundColor
|
|
41
41
|
assign text_color = widget_settings.textColor | default: widget_settings.TextColor | default: widget_content.TextColor | default: widget_content.textColor
|
|
42
42
|
assign show_add_to_cart = widget_settings.showAddToCartButton | default: widget_content.ShowAddToCartButton | default: true
|
|
43
|
+
assign product_call_for_pricing_raw = product.showCallForPricing | default: product.ShowCallForPricing | default: product.isCallForPricing | default: product.IsCallForPricing | default: false
|
|
44
|
+
assign product_show_call_for_pricing = false
|
|
45
|
+
if product_call_for_pricing_raw == true or product_call_for_pricing_raw == 'true' or product_call_for_pricing_raw == 1 or product_call_for_pricing_raw == '1'
|
|
46
|
+
assign product_show_call_for_pricing = true
|
|
47
|
+
endif
|
|
43
48
|
|
|
44
49
|
comment
|
|
45
50
|
Extract short description (first 200 chars or until first paragraph)
|
|
46
51
|
endcomment
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
assign short_description = full_description | truncate: 200
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
assign
|
|
52
|
+
assign full_description = product.shortDescription | default: product.ShortDescription | default: product.description | default: product.Description | default: product.htmlContent | default: product.HtmlContent
|
|
53
|
+
if full_description and full_description != blank
|
|
54
|
+
assign short_description = full_description | strip_html | strip | truncate: 200
|
|
55
|
+
assign normalized_description = full_description | replace: '<P>', '<p>' | replace: '</P>', '</p>'
|
|
56
|
+
if normalized_description contains '<p>'
|
|
57
|
+
assign first_para = normalized_description | split: '<p>' | last | split: '</p>' | first
|
|
58
|
+
assign short_description = first_para | strip_html | strip | truncate: 200
|
|
53
59
|
endif
|
|
54
60
|
endif
|
|
55
61
|
%}
|
|
@@ -68,34 +74,51 @@
|
|
|
68
74
|
{% if product %}
|
|
69
75
|
<div class="single-product-pdp">
|
|
70
76
|
<div class="single-product-pdp__layout">
|
|
71
|
-
<!-- Product Gallery -->
|
|
72
|
-
<div class="single-product-
|
|
73
|
-
<div class="
|
|
77
|
+
<!-- Product Gallery with Carousel -->
|
|
78
|
+
<div class="single-product-pdp__media{% if product.images and product.images.size > 1 %} has-thumbnails{% endif %}">
|
|
79
|
+
<div class="single-product-media-main">
|
|
74
80
|
{% if product.images and product.images.size > 0 %}
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
<!-- Carousel Images -->
|
|
82
|
+
<div class="carousel-track-container">
|
|
83
|
+
<div class="carousel-track">
|
|
84
|
+
{% for image in product.images %}
|
|
85
|
+
{% assign image_url = image.url | default: image %}
|
|
86
|
+
<div class="carousel-slide {% if forloop.first %}active{% endif %}" data-index="{{ forloop.index0 }}">
|
|
87
|
+
<img
|
|
88
|
+
src="{{ image_url }}"
|
|
89
|
+
alt="{{ product.name | default: product.title }} - {{ forloop.index }}"
|
|
90
|
+
class="single-product-media-main-image"
|
|
91
|
+
loading="{% if forloop.first %}eager{% else %}lazy{% endif %}"
|
|
92
|
+
width="600"
|
|
93
|
+
height="600"
|
|
94
|
+
>
|
|
95
|
+
</div>
|
|
96
|
+
{% endfor %}
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<!-- Carousel Dots Indicator -->
|
|
101
|
+
{% if product.images.size > 1 %}
|
|
102
|
+
<div class="carousel-dots">
|
|
103
|
+
{% for image in product.images %}
|
|
104
|
+
<button class="carousel-dot {% if forloop.first %}active{% endif %}" data-index="{{ forloop.index0 }}" aria-label="Go to image {{ forloop.index }}" type="button"></button>
|
|
105
|
+
{% endfor %}
|
|
106
|
+
</div>
|
|
107
|
+
{% endif %}
|
|
108
|
+
{% elsif product.thumbnailImage %}
|
|
109
|
+
{% assign thumbImage = product.thumbnailImage %}
|
|
110
|
+
<div class="carousel-slide active" data-index="0">
|
|
77
111
|
<img
|
|
78
|
-
src="{{
|
|
79
|
-
alt="{{ product.name | default: product.title }}
|
|
80
|
-
class="
|
|
81
|
-
|
|
82
|
-
loading="{% if forloop.first %}eager{% else %}lazy{% endif %}"
|
|
112
|
+
src="{{ thumbImage.url | default: thumbImage }}"
|
|
113
|
+
alt="{{ product.name | default: product.title }}"
|
|
114
|
+
class="single-product-media-main-image"
|
|
115
|
+
loading="eager"
|
|
83
116
|
width="600"
|
|
84
117
|
height="600"
|
|
85
118
|
>
|
|
86
|
-
|
|
87
|
-
{% elsif product.thumbnailImage %}
|
|
88
|
-
{% assign thumbImage = product.thumbnailImage %}
|
|
89
|
-
<img
|
|
90
|
-
src="{{ thumbImage.url | default: thumbImage }}"
|
|
91
|
-
alt="{{ product.name | default: product.title }}"
|
|
92
|
-
class="gallery-main-image active"
|
|
93
|
-
loading="eager"
|
|
94
|
-
width="600"
|
|
95
|
-
height="600"
|
|
96
|
-
>
|
|
119
|
+
</div>
|
|
97
120
|
{% else %}
|
|
98
|
-
<div class="
|
|
121
|
+
<div class="single-product-media-placeholder">
|
|
99
122
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
100
123
|
<rect x="16" y="16" width="32" height="32" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
101
124
|
<path d="M16 24L24 32L32 24L40 32L48 24" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
@@ -106,11 +129,11 @@
|
|
|
106
129
|
|
|
107
130
|
<!-- Thumbnails -->
|
|
108
131
|
{% if product.images and product.images.size > 1 %}
|
|
109
|
-
<div class="
|
|
132
|
+
<div class="single-product-media-thumbnails">
|
|
110
133
|
{% for image in product.images limit: 8 %}
|
|
111
134
|
{% assign image_url = image.url | default: image %}
|
|
112
135
|
<button
|
|
113
|
-
class="
|
|
136
|
+
class="single-product-media-thumbnail {% if forloop.first %}active{% endif %}"
|
|
114
137
|
data-image="{{ image_url }}"
|
|
115
138
|
data-index="{{ forloop.index0 }}"
|
|
116
139
|
aria-label="View image {{ forloop.index }}"
|
|
@@ -137,10 +160,14 @@
|
|
|
137
160
|
|
|
138
161
|
<!-- Price -->
|
|
139
162
|
<div class="product-price-wrapper">
|
|
140
|
-
<span class="price-current" id="singleProductPrice">
|
|
141
|
-
{
|
|
163
|
+
<span class="price-current" id="singleProductPrice-{{ widget.id }}">
|
|
164
|
+
{% if product_show_call_for_pricing %}
|
|
165
|
+
Call for pricing
|
|
166
|
+
{% else %}
|
|
167
|
+
{{ product.prices.price | money_with_settings: shop.settings }}
|
|
168
|
+
{% endif %}
|
|
142
169
|
</span>
|
|
143
|
-
{% if product.prices.mrp and product.prices.mrp > product.prices.price %}
|
|
170
|
+
{% if product_show_call_for_pricing == false and product.prices.mrp and product.prices.mrp > product.prices.price %}
|
|
144
171
|
<span class="price-compare">
|
|
145
172
|
{{ product.prices.mrp | money_with_settings: shop.settings }}
|
|
146
173
|
</span>
|
|
@@ -155,7 +182,7 @@
|
|
|
155
182
|
{% endif %}
|
|
156
183
|
|
|
157
184
|
<!-- Product Form -->
|
|
158
|
-
<form class="product-form" id="singleProductForm" data-product-id="{{ product.productId }}">
|
|
185
|
+
<form class="product-form" id="singleProductForm-{{ widget.id }}" data-product-id="{{ product.productId }}">
|
|
159
186
|
<!-- Variations -->
|
|
160
187
|
{% if product.variations and product.variations.size > 0 %}
|
|
161
188
|
{% assign default_variant = null %}
|
|
@@ -170,8 +197,8 @@
|
|
|
170
197
|
{% endif %}
|
|
171
198
|
|
|
172
199
|
<div class="product-variation-selector">
|
|
173
|
-
<label for="singleProductVariant" class="variation-label">Select Variant:</label>
|
|
174
|
-
<select id="singleProductVariant" class="variation-select" name="variantId" aria-label="Select product variant">
|
|
200
|
+
<label for="singleProductVariant-{{ widget.id }}" class="variation-label">Select Variant:</label>
|
|
201
|
+
<select id="singleProductVariant-{{ widget.id }}" class="variation-select" name="variantId" aria-label="Select product variant">
|
|
175
202
|
{% for variant in product.variations %}
|
|
176
203
|
{% assign isAvailable = variant.inStock | default: variant.available | default: true %}
|
|
177
204
|
{% assign variant_name = variant.name | default: '' %}
|
|
@@ -192,6 +219,7 @@
|
|
|
192
219
|
value="{{ variant.productId }}"
|
|
193
220
|
data-variant-price="{{ variant_price }}"
|
|
194
221
|
data-variant-mrp="{{ variant.prices.mrp | default: 0 }}"
|
|
222
|
+
data-show-call-for-pricing="{{ variant.showCallForPricing | default: variant.ShowCallForPricing | default: variant.isCallForPricing | default: variant.IsCallForPricing | default: false }}"
|
|
195
223
|
data-in-stock="{{ isAvailable }}"
|
|
196
224
|
{% if variant.productId == default_variant.productId %}selected{% endif %}>
|
|
197
225
|
{{ variant_name }}{% unless isAvailable %} — Out of Stock{% endunless %}
|
|
@@ -203,8 +231,22 @@
|
|
|
203
231
|
|
|
204
232
|
<!-- Quantity (optional) -->
|
|
205
233
|
<div class="product-quantity">
|
|
206
|
-
<label for="singleProductQuantity">Quantity
|
|
207
|
-
<
|
|
234
|
+
<label for="singleProductQuantity-{{ widget.id }}" class="quantity-label">Quantity</label>
|
|
235
|
+
<div class="quantity-wrapper">
|
|
236
|
+
<button type="button" class="quantity-btn quantity-decrease" aria-label="Decrease quantity">−</button>
|
|
237
|
+
<input
|
|
238
|
+
type="number"
|
|
239
|
+
id="singleProductQuantity-{{ widget.id }}"
|
|
240
|
+
name="quantity"
|
|
241
|
+
value="1"
|
|
242
|
+
min="1"
|
|
243
|
+
max="{{ product.stockQuantity | default: 99 }}"
|
|
244
|
+
class="quantity-input"
|
|
245
|
+
aria-label="Quantity"
|
|
246
|
+
readonly
|
|
247
|
+
>
|
|
248
|
+
<button type="button" class="quantity-btn quantity-increase" aria-label="Increase quantity">+</button>
|
|
249
|
+
</div>
|
|
208
250
|
</div>
|
|
209
251
|
|
|
210
252
|
<!-- Add to Cart Button -->
|
|
@@ -219,7 +261,8 @@
|
|
|
219
261
|
class="btn-add-to-cart"
|
|
220
262
|
data-product-id="{{ default_variant_id }}"
|
|
221
263
|
data-base-product-id="{{ product.productId }}">
|
|
222
|
-
|
|
264
|
+
<span class="loading-spinner" aria-hidden="true"></span>
|
|
265
|
+
<span class="btn-add-to-cart__text">Add to Cart</span>
|
|
223
266
|
</button>
|
|
224
267
|
{% endif %}
|
|
225
268
|
</form>
|
|
@@ -234,377 +277,772 @@
|
|
|
234
277
|
</div>
|
|
235
278
|
|
|
236
279
|
<style>
|
|
237
|
-
|
|
238
|
-
|
|
280
|
+
:root {
|
|
281
|
+
--single-product-primary: {{ settings.color_primary | default: 'var(--color-primary, #111827)' }};
|
|
282
|
+
--single-product-primary-hover: {{ settings.color_primary_dark | default: 'var(--color-primary-dark, #0f172a)' }};
|
|
283
|
+
--single-product-text: {{ settings.color_text | default: 'var(--color-text, #111827)' }};
|
|
284
|
+
--single-product-text-muted: {{ settings.color_text_muted | default: 'var(--color-text-muted, #6b7280)' }};
|
|
285
|
+
--single-product-background: {{ settings.color_background | default: 'var(--color-background, #ffffff)' }};
|
|
286
|
+
--single-product-surface: {{ settings.color_surface | default: 'var(--color-surface, #f8fafc)' }};
|
|
287
|
+
--single-product-border: {{ settings.color_border | default: 'var(--color-border, #e5e7eb)' }};
|
|
288
|
+
--single-product-shadow: 0 10px 30px color-mix(in srgb, #0f172a 8%, transparent);
|
|
239
289
|
}
|
|
240
290
|
|
|
291
|
+
.widget-single-product {
|
|
292
|
+
padding: 4rem 0;
|
|
293
|
+
}
|
|
294
|
+
|
|
241
295
|
.widget-single-product__inner {
|
|
242
|
-
max-width:
|
|
296
|
+
max-width: 1240px;
|
|
243
297
|
margin: 0 auto;
|
|
244
|
-
padding: 0
|
|
298
|
+
padding: 0 1.25rem;
|
|
245
299
|
}
|
|
246
300
|
|
|
247
301
|
.widget-single-product .widget-header {
|
|
248
|
-
margin
|
|
302
|
+
margin: 0 0 2.5rem;
|
|
249
303
|
text-align: center;
|
|
250
304
|
}
|
|
251
305
|
|
|
252
306
|
.widget-single-product .widget-title {
|
|
253
|
-
font-size:
|
|
254
|
-
font-weight:
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
307
|
+
font-size: 2.35rem;
|
|
308
|
+
font-weight: 700;
|
|
309
|
+
letter-spacing: -0.02em;
|
|
310
|
+
line-height: 1.12;
|
|
311
|
+
margin: 0 0 0.5rem;
|
|
312
|
+
color: var(--single-product-text);
|
|
258
313
|
}
|
|
259
314
|
|
|
260
315
|
.widget-single-product .widget-subtitle {
|
|
261
|
-
font-size: 1.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
316
|
+
font-size: 1.15rem;
|
|
317
|
+
margin: 0 auto;
|
|
318
|
+
max-width: 760px;
|
|
319
|
+
color: var(--single-product-text-muted);
|
|
320
|
+
line-height: 1.65;
|
|
266
321
|
}
|
|
267
322
|
|
|
268
|
-
/* PDP Layout */
|
|
269
323
|
.single-product-pdp__layout {
|
|
270
324
|
display: grid;
|
|
271
|
-
grid-template-columns: 1fr
|
|
272
|
-
gap:
|
|
325
|
+
grid-template-columns: 1fr;
|
|
326
|
+
gap: 1.5rem;
|
|
273
327
|
align-items: start;
|
|
274
328
|
}
|
|
275
329
|
|
|
276
|
-
|
|
277
|
-
|
|
330
|
+
@media (min-width: 960px) {
|
|
331
|
+
.single-product-pdp__layout {
|
|
332
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
333
|
+
gap: 2rem;
|
|
334
|
+
align-items: stretch;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.single-product-pdp__media {
|
|
278
339
|
display: flex;
|
|
279
340
|
flex-direction: column;
|
|
280
|
-
gap:
|
|
341
|
+
gap: 0.75rem;
|
|
342
|
+
min-width: 0;
|
|
281
343
|
}
|
|
282
344
|
|
|
283
|
-
.
|
|
345
|
+
.single-product-media-main {
|
|
284
346
|
position: relative;
|
|
285
|
-
aspect-ratio: 1;
|
|
286
347
|
width: 100%;
|
|
287
|
-
|
|
288
|
-
|
|
348
|
+
aspect-ratio: 4 / 3;
|
|
349
|
+
max-height: 520px;
|
|
350
|
+
background: linear-gradient(145deg, var(--single-product-surface), color-mix(in srgb, var(--single-product-surface) 80%, #ffffff));
|
|
351
|
+
border-radius: 1.25rem;
|
|
289
352
|
overflow: hidden;
|
|
353
|
+
box-shadow: var(--single-product-shadow);
|
|
354
|
+
border: 1px solid color-mix(in srgb, var(--single-product-border) 85%, transparent);
|
|
290
355
|
}
|
|
291
356
|
|
|
292
|
-
.
|
|
293
|
-
|
|
357
|
+
.carousel-track-container {
|
|
358
|
+
width: 100%;
|
|
359
|
+
height: 100%;
|
|
360
|
+
overflow: hidden;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.carousel-track {
|
|
364
|
+
display: flex;
|
|
365
|
+
width: 100%;
|
|
366
|
+
height: 100%;
|
|
367
|
+
transition: transform 0.45s cubic-bezier(0.2, 0.65, 0.2, 1);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.carousel-slide {
|
|
371
|
+
flex: 0 0 100%;
|
|
372
|
+
width: 100%;
|
|
373
|
+
height: 100%;
|
|
374
|
+
position: relative;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.single-product-media-main-image {
|
|
294
378
|
width: 100%;
|
|
295
379
|
height: 100%;
|
|
296
380
|
object-fit: cover;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.carousel-dots {
|
|
297
384
|
position: absolute;
|
|
298
|
-
|
|
299
|
-
left:
|
|
385
|
+
bottom: 1rem;
|
|
386
|
+
left: 50%;
|
|
387
|
+
transform: translateX(-50%);
|
|
388
|
+
display: flex;
|
|
389
|
+
gap: 0.45rem;
|
|
390
|
+
z-index: 2;
|
|
391
|
+
padding: 0.5rem 0.75rem;
|
|
392
|
+
background: color-mix(in srgb, var(--single-product-text) 18%, transparent);
|
|
393
|
+
border-radius: 999px;
|
|
394
|
+
backdrop-filter: blur(8px);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.carousel-dot {
|
|
398
|
+
width: 0.45rem;
|
|
399
|
+
height: 0.45rem;
|
|
400
|
+
border: 0;
|
|
401
|
+
padding: 0;
|
|
402
|
+
border-radius: 999px;
|
|
403
|
+
cursor: pointer;
|
|
404
|
+
background: color-mix(in srgb, var(--single-product-background) 68%, transparent);
|
|
405
|
+
transition: width 0.25s ease, background 0.25s ease;
|
|
300
406
|
}
|
|
301
407
|
|
|
302
|
-
.
|
|
303
|
-
|
|
408
|
+
.carousel-dot.active {
|
|
409
|
+
width: 1.2rem;
|
|
410
|
+
background: var(--single-product-background);
|
|
304
411
|
}
|
|
305
412
|
|
|
306
|
-
.
|
|
413
|
+
.single-product-media-placeholder {
|
|
307
414
|
width: 100%;
|
|
308
415
|
height: 100%;
|
|
309
416
|
display: flex;
|
|
310
417
|
align-items: center;
|
|
311
418
|
justify-content: center;
|
|
312
|
-
color:
|
|
419
|
+
color: var(--single-product-text-muted);
|
|
313
420
|
}
|
|
314
421
|
|
|
315
|
-
.
|
|
316
|
-
display:
|
|
317
|
-
|
|
422
|
+
.single-product-media-thumbnails {
|
|
423
|
+
display: grid;
|
|
424
|
+
grid-auto-flow: column;
|
|
425
|
+
grid-auto-columns: 3.5rem;
|
|
426
|
+
gap: 0.55rem;
|
|
318
427
|
overflow-x: auto;
|
|
319
|
-
padding
|
|
428
|
+
padding: 0.15rem 0 0.4rem;
|
|
429
|
+
scrollbar-width: thin;
|
|
430
|
+
scrollbar-color: var(--single-product-border) transparent;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.single-product-media-thumbnails::-webkit-scrollbar {
|
|
434
|
+
height: 0.35rem;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.single-product-media-thumbnails::-webkit-scrollbar-thumb {
|
|
438
|
+
background: var(--single-product-border);
|
|
439
|
+
border-radius: 999px;
|
|
320
440
|
}
|
|
321
441
|
|
|
322
|
-
.
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
border:
|
|
327
|
-
border-radius: 6px;
|
|
442
|
+
.single-product-media-thumbnail {
|
|
443
|
+
width: 3.5rem;
|
|
444
|
+
height: 3.5rem;
|
|
445
|
+
border: 1px solid color-mix(in srgb, var(--single-product-border) 90%, transparent);
|
|
446
|
+
border-radius: 0.65rem;
|
|
328
447
|
overflow: hidden;
|
|
329
|
-
background:
|
|
330
|
-
cursor: pointer;
|
|
448
|
+
background: var(--single-product-surface);
|
|
331
449
|
padding: 0;
|
|
332
|
-
|
|
450
|
+
cursor: pointer;
|
|
451
|
+
transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.single-product-media-thumbnail:hover {
|
|
455
|
+
transform: translateY(-2px);
|
|
333
456
|
}
|
|
334
457
|
|
|
335
|
-
.
|
|
336
|
-
border-color:
|
|
458
|
+
.single-product-media-thumbnail.active {
|
|
459
|
+
border-color: var(--single-product-primary);
|
|
460
|
+
box-shadow: 0 0 0 2px color-mix(in srgb, var(--single-product-primary) 20%, transparent);
|
|
337
461
|
}
|
|
338
462
|
|
|
339
|
-
.
|
|
463
|
+
.single-product-media-thumbnail img {
|
|
340
464
|
width: 100%;
|
|
341
465
|
height: 100%;
|
|
342
466
|
object-fit: cover;
|
|
343
467
|
}
|
|
344
468
|
|
|
345
|
-
/* Product Info */
|
|
346
469
|
.single-product-pdp__info {
|
|
347
470
|
display: flex;
|
|
348
471
|
flex-direction: column;
|
|
349
|
-
gap:
|
|
472
|
+
gap: 2.5rem;
|
|
473
|
+
background: color-mix(in srgb, var(--single-product-background) 95%, #ffffff);
|
|
474
|
+
border: 1px solid color-mix(in srgb, var(--single-product-border) 85%, transparent);
|
|
475
|
+
border-radius: 1.25rem;
|
|
476
|
+
padding: 1.25rem;
|
|
477
|
+
height:100%
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
@media (min-width: 960px) {
|
|
481
|
+
.single-product-pdp__media {
|
|
482
|
+
height: 100%;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.single-product-pdp__media.has-thumbnails {
|
|
486
|
+
display: grid;
|
|
487
|
+
grid-template-columns: 4.1rem minmax(0, 1fr);
|
|
488
|
+
grid-template-rows: minmax(0, 1fr);
|
|
489
|
+
gap: 0.85rem;
|
|
490
|
+
align-items: stretch;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.single-product-media-main {
|
|
494
|
+
flex: 1;
|
|
495
|
+
height: 100%;
|
|
496
|
+
max-height: none;
|
|
497
|
+
aspect-ratio: auto;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.single-product-pdp__media.has-thumbnails .single-product-media-main {
|
|
501
|
+
grid-column: 2;
|
|
502
|
+
grid-row: 1;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.single-product-pdp__media.has-thumbnails .single-product-media-thumbnails {
|
|
506
|
+
grid-column: 1;
|
|
507
|
+
grid-row: 1;
|
|
508
|
+
display: grid;
|
|
509
|
+
grid-auto-flow: row;
|
|
510
|
+
grid-auto-rows: 3.5rem;
|
|
511
|
+
grid-auto-columns: auto;
|
|
512
|
+
align-content: start;
|
|
513
|
+
overflow-y: auto;
|
|
514
|
+
overflow-x: hidden;
|
|
515
|
+
height: 100%;
|
|
516
|
+
max-height: 100%;
|
|
517
|
+
padding: 0 0.2rem 0 0;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
.single-product-pdp__media.has-thumbnails .single-product-media-thumbnails::-webkit-scrollbar {
|
|
521
|
+
width: 0.35rem;
|
|
522
|
+
height: auto;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.single-product-pdp__info {
|
|
526
|
+
height: 100%;
|
|
527
|
+
}
|
|
350
528
|
}
|
|
351
529
|
|
|
352
530
|
.product-vendor {
|
|
353
|
-
|
|
531
|
+
display: inline-flex;
|
|
532
|
+
align-self: flex-start;
|
|
533
|
+
font-size: 0.8rem;
|
|
354
534
|
text-transform: uppercase;
|
|
355
|
-
letter-spacing: 0.
|
|
356
|
-
color: #
|
|
357
|
-
|
|
535
|
+
letter-spacing: 0.12em;
|
|
536
|
+
color: color-mix(in srgb, var(--single-product-primary) 75%, #334155);
|
|
537
|
+
background: color-mix(in srgb, var(--single-product-primary) 9%, transparent);
|
|
538
|
+
border: 1px solid color-mix(in srgb, var(--single-product-primary) 20%, transparent);
|
|
539
|
+
padding: 0.45rem 0.75rem;
|
|
540
|
+
border-radius: 999px;
|
|
541
|
+
font-weight: 700;
|
|
358
542
|
}
|
|
359
543
|
|
|
360
544
|
.product-title {
|
|
361
|
-
font-size: 32px;
|
|
362
|
-
font-weight: 600;
|
|
363
|
-
line-height: 1.2;
|
|
364
545
|
margin: 0;
|
|
365
|
-
color:
|
|
546
|
+
color: var(--single-product-text);
|
|
547
|
+
font-size: 1.8rem;
|
|
548
|
+
line-height: 1.16;
|
|
549
|
+
letter-spacing: -0.02em;
|
|
550
|
+
font-weight: 700;
|
|
366
551
|
}
|
|
367
552
|
|
|
368
553
|
.product-price-wrapper {
|
|
369
554
|
display: flex;
|
|
370
555
|
align-items: baseline;
|
|
371
|
-
gap:
|
|
556
|
+
gap: 0.7rem;
|
|
557
|
+
flex-wrap: wrap;
|
|
372
558
|
}
|
|
373
559
|
|
|
374
560
|
.price-current {
|
|
375
|
-
font-size:
|
|
376
|
-
font-weight:
|
|
377
|
-
|
|
561
|
+
font-size: 1.65rem;
|
|
562
|
+
font-weight: 700;
|
|
563
|
+
line-height: 1;
|
|
564
|
+
color: var(--single-product-text);
|
|
378
565
|
}
|
|
379
566
|
|
|
380
567
|
.price-compare {
|
|
381
|
-
font-size:
|
|
382
|
-
color:
|
|
568
|
+
font-size: 1rem;
|
|
569
|
+
color: var(--single-product-text-muted);
|
|
383
570
|
text-decoration: line-through;
|
|
571
|
+
text-decoration-thickness: 1px;
|
|
384
572
|
}
|
|
385
573
|
|
|
386
574
|
.product-short-description {
|
|
387
|
-
font-size:
|
|
575
|
+
font-size: 1.5rem;
|
|
388
576
|
line-height: 1.6;
|
|
389
|
-
color:
|
|
390
|
-
|
|
577
|
+
color: var(--single-product-text-muted);
|
|
578
|
+
padding-top: 0.25rem;
|
|
579
|
+
border-top: 1px solid color-mix(in srgb, var(--single-product-border) 80%, transparent);
|
|
391
580
|
}
|
|
392
581
|
|
|
393
|
-
/* Form */
|
|
394
582
|
.product-form {
|
|
395
|
-
display:
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
margin-top: 24px;
|
|
583
|
+
display: grid;
|
|
584
|
+
gap: 1.8rem;
|
|
585
|
+
margin-top: 0.15rem;
|
|
399
586
|
}
|
|
400
587
|
|
|
401
|
-
.product-variation-selector
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
gap:
|
|
588
|
+
.product-variation-selector,
|
|
589
|
+
.product-quantity {
|
|
590
|
+
display: flex;
|
|
591
|
+
gap: 3.5rem;
|
|
592
|
+
align-items: center;
|
|
405
593
|
}
|
|
406
594
|
|
|
407
|
-
.variation-label
|
|
408
|
-
|
|
409
|
-
font-
|
|
410
|
-
|
|
595
|
+
.variation-label,
|
|
596
|
+
.quantity-label {
|
|
597
|
+
font-size: 1.3rem;
|
|
598
|
+
|
|
599
|
+
color: var(--single-product-text);
|
|
600
|
+
letter-spacing: 0.08em;
|
|
601
|
+
|
|
411
602
|
}
|
|
412
603
|
|
|
413
604
|
.variation-select {
|
|
414
605
|
width: 100%;
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
border:
|
|
418
|
-
border
|
|
419
|
-
background:
|
|
420
|
-
|
|
421
|
-
|
|
606
|
+
min-height: 2.5rem;
|
|
607
|
+
padding: 0.5rem 0.8rem;
|
|
608
|
+
border-radius: 0.6rem;
|
|
609
|
+
border: 1px solid var(--single-product-border);
|
|
610
|
+
background: var(--single-product-background);
|
|
611
|
+
color: var(--single-product-text);
|
|
612
|
+
font-size: 0.92rem;
|
|
613
|
+
outline: none;
|
|
614
|
+
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
|
422
615
|
}
|
|
423
616
|
|
|
424
|
-
.variation-select:
|
|
425
|
-
border-color: #
|
|
617
|
+
.variation-select:focus {
|
|
618
|
+
border-color: color-mix(in srgb, var(--single-product-primary) 60%, #93c5fd);
|
|
619
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--single-product-primary) 14%, transparent);
|
|
426
620
|
}
|
|
427
621
|
|
|
428
|
-
.
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
622
|
+
.quantity-wrapper {
|
|
623
|
+
display: flex;
|
|
624
|
+
border: 1px solid var(--single-product-border);
|
|
625
|
+
border-radius: 0.6rem;
|
|
626
|
+
overflow: hidden;
|
|
627
|
+
background: var(--single-product-background);
|
|
628
|
+
align-items: center;
|
|
629
|
+
width: fit-content;
|
|
432
630
|
}
|
|
433
631
|
|
|
434
|
-
.
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
632
|
+
.quantity-btn {
|
|
633
|
+
width: 44px;
|
|
634
|
+
height: 44px;
|
|
635
|
+
border: none;
|
|
636
|
+
background: transparent;
|
|
637
|
+
cursor: pointer;
|
|
638
|
+
display: flex;
|
|
639
|
+
align-items: center;
|
|
640
|
+
justify-content: center;
|
|
641
|
+
transition: background 0.2s ease;
|
|
642
|
+
color: #111;
|
|
643
|
+
font-size: 20px;
|
|
644
|
+
font-weight: 300;
|
|
645
|
+
line-height: 1;
|
|
438
646
|
}
|
|
439
647
|
|
|
440
|
-
.
|
|
441
|
-
|
|
442
|
-
font-weight: 600;
|
|
443
|
-
color: #111;
|
|
648
|
+
.quantity-btn:hover:not(:disabled) {
|
|
649
|
+
background: color-mix(in srgb, var(--single-product-surface) 88%, #ffffff);
|
|
444
650
|
}
|
|
445
651
|
|
|
446
652
|
.quantity-input {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
653
|
+
width: 60px;
|
|
654
|
+
height: 44px;
|
|
655
|
+
border: none;
|
|
656
|
+
border-left: 1px solid #e5e7eb;
|
|
657
|
+
border-right: 1px solid #e5e7eb;
|
|
658
|
+
text-align: center;
|
|
659
|
+
font-weight: 400;
|
|
660
|
+
font-size: 14px;
|
|
661
|
+
background: #ffffff;
|
|
662
|
+
color: #111;
|
|
452
663
|
}
|
|
453
664
|
|
|
454
|
-
.
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
font-weight: 600;
|
|
459
|
-
color: #fff;
|
|
460
|
-
background: #111;
|
|
461
|
-
border: none;
|
|
462
|
-
border-radius: 8px;
|
|
463
|
-
cursor: pointer;
|
|
464
|
-
transition: background 0.2s ease, transform 0.1s ease;
|
|
465
|
-
margin-top: 8px;
|
|
665
|
+
.quantity-input::-webkit-inner-spin-button,
|
|
666
|
+
.quantity-input::-webkit-outer-spin-button {
|
|
667
|
+
-webkit-appearance: none;
|
|
668
|
+
margin: 0;
|
|
466
669
|
}
|
|
467
670
|
|
|
671
|
+
.btn-add-to-cart {
|
|
672
|
+
padding: 16px 32px;
|
|
673
|
+
border-radius: var(--border-radius-medium);
|
|
674
|
+
font-weight: 500;
|
|
675
|
+
font-size: 14px;
|
|
676
|
+
cursor: pointer;
|
|
677
|
+
transition: all 0.2s ease;
|
|
678
|
+
display: inline-flex;
|
|
679
|
+
align-items: center;
|
|
680
|
+
justify-content: center;
|
|
681
|
+
gap: 8px;
|
|
682
|
+
border: 1px solid transparent;
|
|
683
|
+
text-transform: uppercase;
|
|
684
|
+
letter-spacing: 0.05em;
|
|
685
|
+
white-space: nowrap;
|
|
686
|
+
width: 100%;
|
|
687
|
+
background: #111;
|
|
688
|
+
color: #ffffff;
|
|
689
|
+
border-color: #111;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
|
|
468
693
|
.btn-add-to-cart:hover:not(:disabled) {
|
|
469
|
-
|
|
694
|
+
transform: translateY(-1px);
|
|
695
|
+
box-shadow: 0 16px 30px color-mix(in srgb, var(--single-product-primary) 22%, transparent);
|
|
696
|
+
filter: brightness(1.03);
|
|
470
697
|
}
|
|
471
698
|
|
|
472
699
|
.btn-add-to-cart:active:not(:disabled) {
|
|
473
|
-
transform:
|
|
700
|
+
transform: translateY(0);
|
|
474
701
|
}
|
|
475
702
|
|
|
476
703
|
.btn-add-to-cart:disabled {
|
|
477
|
-
background: #d1d5db;
|
|
478
704
|
cursor: not-allowed;
|
|
705
|
+
background: color-mix(in srgb, var(--single-product-border) 88%, transparent);
|
|
706
|
+
box-shadow: none;
|
|
707
|
+
color: color-mix(in srgb, var(--single-product-text-muted) 85%, #9ca3af);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
.btn-add-to-cart .loading-spinner {
|
|
711
|
+
display: none;
|
|
712
|
+
width: 0.9rem;
|
|
713
|
+
height: 0.9rem;
|
|
714
|
+
border: 2px solid color-mix(in srgb, var(--single-product-background) 35%, transparent);
|
|
715
|
+
border-top-color: var(--single-product-background);
|
|
716
|
+
border-radius: 50%;
|
|
717
|
+
animation: spin 0.8s linear infinite;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
@keyframes spin {
|
|
721
|
+
to {
|
|
722
|
+
transform: rotate(360deg);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.btn-add-to-cart.loading .loading-spinner {
|
|
727
|
+
display: inline-block;
|
|
479
728
|
}
|
|
480
729
|
|
|
481
730
|
.widget-empty {
|
|
482
|
-
padding:
|
|
731
|
+
padding: 3rem 1.25rem;
|
|
483
732
|
text-align: center;
|
|
484
|
-
color:
|
|
733
|
+
color: var(--single-product-text-muted);
|
|
485
734
|
}
|
|
486
735
|
|
|
487
736
|
.widget-empty p {
|
|
488
737
|
margin: 0;
|
|
489
|
-
font-size:
|
|
738
|
+
font-size: 1rem;
|
|
490
739
|
}
|
|
491
740
|
|
|
492
|
-
|
|
493
|
-
@media (max-width: 768px) {
|
|
741
|
+
@media (max-width: 1024px) {
|
|
494
742
|
.widget-single-product {
|
|
495
|
-
padding:
|
|
743
|
+
padding: 3rem 0;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.widget-single-product .widget-title {
|
|
747
|
+
font-size: 2rem;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
.product-title {
|
|
751
|
+
font-size: 1.8rem;
|
|
496
752
|
}
|
|
753
|
+
}
|
|
497
754
|
|
|
755
|
+
@media (max-width: 640px) {
|
|
498
756
|
.widget-single-product__inner {
|
|
499
|
-
padding: 0
|
|
757
|
+
padding: 0 0.9rem;
|
|
500
758
|
}
|
|
501
759
|
|
|
502
|
-
.single-product-
|
|
503
|
-
|
|
504
|
-
|
|
760
|
+
.single-product-pdp__info {
|
|
761
|
+
padding: 1.1rem;
|
|
762
|
+
border-radius: 1rem;
|
|
505
763
|
}
|
|
506
764
|
|
|
507
|
-
.product-title {
|
|
508
|
-
font-size:
|
|
765
|
+
.widget-single-product .widget-title {
|
|
766
|
+
font-size: 1.7rem;
|
|
509
767
|
}
|
|
510
768
|
|
|
511
|
-
.
|
|
512
|
-
font-size:
|
|
769
|
+
.widget-single-product .widget-subtitle {
|
|
770
|
+
font-size: 1rem;
|
|
513
771
|
}
|
|
514
772
|
|
|
515
|
-
.price-
|
|
516
|
-
font-size:
|
|
773
|
+
.price-current {
|
|
774
|
+
font-size: 1.65rem;
|
|
517
775
|
}
|
|
518
|
-
}
|
|
519
776
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
padding: 24px 0;
|
|
777
|
+
.single-product-media-thumbnails {
|
|
778
|
+
grid-auto-columns: 4rem;
|
|
523
779
|
}
|
|
524
780
|
|
|
525
|
-
.
|
|
526
|
-
|
|
781
|
+
.single-product-media-thumbnail {
|
|
782
|
+
width: 4rem;
|
|
783
|
+
height: 4rem;
|
|
527
784
|
}
|
|
528
785
|
|
|
529
|
-
.
|
|
530
|
-
|
|
786
|
+
.single-product-media-main {
|
|
787
|
+
aspect-ratio: 1 / 1;
|
|
788
|
+
max-height: none;
|
|
531
789
|
}
|
|
790
|
+
}
|
|
791
|
+
</style>
|
|
532
792
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
793
|
+
{% if product %}<script>
|
|
794
|
+
(() => {
|
|
795
|
+
const widgetRoot = (document.currentScript && document.currentScript.closest('.widget-single-product'))
|
|
796
|
+
|| document.querySelector('.widget-single-product[data-widget-id="{{ widget.id }}"]');
|
|
797
|
+
if (!widgetRoot) return;
|
|
798
|
+
if (widgetRoot.dataset.singleProductBound === 'true') return;
|
|
799
|
+
widgetRoot.dataset.singleProductBound = 'true';
|
|
800
|
+
|
|
801
|
+
const currencySymbol = '{{ shop.settings.currencySymbol | default: shop.currency | default: "$" }}';
|
|
802
|
+
let touchStartX = 0;
|
|
803
|
+
let touchEndX = 0;
|
|
804
|
+
|
|
805
|
+
function formatMoney(value) {
|
|
806
|
+
const amount = Number(value || 0);
|
|
807
|
+
return currencySymbol + amount.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
function getCarouselState() {
|
|
811
|
+
return {
|
|
812
|
+
track: widgetRoot.querySelector('.carousel-track'),
|
|
813
|
+
slides: Array.from(widgetRoot.querySelectorAll('.carousel-slide')),
|
|
814
|
+
dots: Array.from(widgetRoot.querySelectorAll('.carousel-dot')),
|
|
815
|
+
thumbnails: Array.from(widgetRoot.querySelectorAll('.single-product-media-thumbnail'))
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
function setCarouselIndex(index) {
|
|
820
|
+
const state = getCarouselState();
|
|
821
|
+
if (!state.track || state.slides.length === 0) return;
|
|
822
|
+
|
|
823
|
+
const totalSlides = state.slides.length;
|
|
824
|
+
let safeIndex = index;
|
|
825
|
+
if (safeIndex < 0) safeIndex = totalSlides - 1;
|
|
826
|
+
if (safeIndex >= totalSlides) safeIndex = 0;
|
|
827
|
+
widgetRoot.dataset.currentSlideIndex = String(safeIndex);
|
|
828
|
+
|
|
829
|
+
state.track.style.transform = `translateX(-${safeIndex * 100}%)`;
|
|
830
|
+
state.dots.forEach((dot, i) => dot.classList.toggle('active', i === safeIndex));
|
|
831
|
+
state.thumbnails.forEach((thumb, i) => thumb.classList.toggle('active', i === safeIndex));
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
function getCurrentSlideIndex() {
|
|
835
|
+
return parseInt(widgetRoot.dataset.currentSlideIndex || '0', 10) || 0;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
function openLoginModal() {
|
|
839
|
+
if (window.Theme && typeof window.Theme.openLoginModal === 'function') {
|
|
840
|
+
window.Theme.openLoginModal();
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
if (window.CartManager && typeof window.CartManager.openLoginModal === 'function') {
|
|
844
|
+
window.CartManager.openLoginModal();
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
const loginTrigger = document.querySelector('[data-login-modal-trigger]');
|
|
848
|
+
if (loginTrigger) loginTrigger.click();
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function isUserLoggedIn() {
|
|
852
|
+
return document.cookie.includes('O2VENDIsUserLoggedin=true') || document.cookie.includes('O2VENDUserToken=');
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
async function addToCartAPI(productId, quantity) {
|
|
856
|
+
if (window.Theme && typeof window.Theme.addToCart === 'function') {
|
|
857
|
+
const wasLoggedIn = isUserLoggedIn();
|
|
858
|
+
const themeResult = await window.Theme.addToCart(productId, quantity, true);
|
|
859
|
+
const isLoggedInNow = isUserLoggedIn();
|
|
860
|
+
|
|
861
|
+
if (!wasLoggedIn || !isLoggedInNow) {
|
|
862
|
+
throw new Error('Authentication required');
|
|
536
863
|
}
|
|
537
864
|
|
|
538
|
-
.
|
|
539
|
-
|
|
865
|
+
if (themeResult === false || (themeResult && themeResult.success === false)) {
|
|
866
|
+
if (themeResult && themeResult.requiresAuth) {
|
|
867
|
+
openLoginModal();
|
|
868
|
+
throw new Error('Authentication required');
|
|
869
|
+
}
|
|
870
|
+
throw new Error((themeResult && (themeResult.error || themeResult.message)) || 'Failed to add to cart');
|
|
540
871
|
}
|
|
872
|
+
return { success: true, viaTheme: true, data: themeResult };
|
|
873
|
+
}
|
|
541
874
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
875
|
+
const response = await fetch('/webstoreapi/carts/add', {
|
|
876
|
+
method: 'POST',
|
|
877
|
+
headers: {
|
|
878
|
+
'Content-Type': 'application/json',
|
|
879
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
880
|
+
'Accept': 'application/json'
|
|
881
|
+
},
|
|
882
|
+
body: JSON.stringify({ productId: productId, quantity: quantity })
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
let data = {};
|
|
886
|
+
try {
|
|
887
|
+
data = await response.json();
|
|
888
|
+
} catch (error) {
|
|
889
|
+
data = {};
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
if (!response.ok || !data.success) {
|
|
893
|
+
if (data.requiresAuth || response.status === 401 || response.status === 403 || response.status === 404) {
|
|
894
|
+
openLoginModal();
|
|
895
|
+
throw new Error('Authentication required');
|
|
545
896
|
}
|
|
897
|
+
throw new Error(data.error || data.message || 'Failed to add to cart');
|
|
546
898
|
}
|
|
547
|
-
</style>
|
|
548
899
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
const
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
900
|
+
return { success: true, viaTheme: false, data: data };
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
widgetRoot.addEventListener('click', (event) => {
|
|
904
|
+
const dot = event.target.closest('.carousel-dot');
|
|
905
|
+
if (dot && widgetRoot.contains(dot)) {
|
|
906
|
+
const index = parseInt(dot.dataset.index || '0', 10);
|
|
907
|
+
if (!Number.isNaN(index)) setCarouselIndex(index);
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
const thumbnail = event.target.closest('.single-product-media-thumbnail');
|
|
912
|
+
if (thumbnail && widgetRoot.contains(thumbnail)) {
|
|
913
|
+
const index = parseInt(thumbnail.dataset.index || '0', 10);
|
|
914
|
+
if (!Number.isNaN(index)) setCarouselIndex(index);
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const decreaseBtn = event.target.closest('.quantity-decrease');
|
|
919
|
+
if (decreaseBtn && widgetRoot.contains(decreaseBtn)) {
|
|
920
|
+
const quantityInput = widgetRoot.querySelector('.quantity-input');
|
|
921
|
+
if (!quantityInput) return;
|
|
922
|
+
const value = parseInt(quantityInput.value || '1', 10);
|
|
923
|
+
if (value > 1) quantityInput.value = String(value - 1);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
const increaseBtn = event.target.closest('.quantity-increase');
|
|
928
|
+
if (increaseBtn && widgetRoot.contains(increaseBtn)) {
|
|
929
|
+
const quantityInput = widgetRoot.querySelector('.quantity-input');
|
|
930
|
+
if (!quantityInput) return;
|
|
931
|
+
const value = parseInt(quantityInput.value || '1', 10);
|
|
932
|
+
const max = parseInt(quantityInput.max || '99', 10);
|
|
933
|
+
if (value < max) quantityInput.value = String(value + 1);
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
widgetRoot.addEventListener('change', (event) => {
|
|
938
|
+
const variantSelect = event.target.closest('.variation-select');
|
|
939
|
+
if (!variantSelect || !widgetRoot.contains(variantSelect)) return;
|
|
940
|
+
|
|
941
|
+
const option = variantSelect.options[variantSelect.selectedIndex];
|
|
942
|
+
if (!option) return;
|
|
943
|
+
|
|
944
|
+
const addToCartBtn = widgetRoot.querySelector('.btn-add-to-cart');
|
|
945
|
+
const priceCurrent = widgetRoot.querySelector('.price-current');
|
|
946
|
+
const priceCompare = widgetRoot.querySelector('.price-compare');
|
|
947
|
+
const price = Number(option.dataset.variantPrice || 0);
|
|
948
|
+
const mrp = Number(option.dataset.variantMrp || 0);
|
|
949
|
+
const inStock = option.dataset.inStock === 'true';
|
|
950
|
+
const callForPricing = option.dataset.showCallForPricing === 'true' || option.dataset.showCallForPricing === '1';
|
|
951
|
+
|
|
952
|
+
if (priceCurrent) {
|
|
953
|
+
priceCurrent.textContent = callForPricing ? 'Call for pricing' : formatMoney(price);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
if (priceCompare) {
|
|
957
|
+
if (!callForPricing && mrp > price) {
|
|
958
|
+
priceCompare.textContent = formatMoney(mrp);
|
|
959
|
+
priceCompare.style.display = '';
|
|
960
|
+
} else {
|
|
961
|
+
priceCompare.style.display = 'none';
|
|
580
962
|
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
if (addToCartBtn) {
|
|
966
|
+
addToCartBtn.setAttribute('data-product-id', option.value);
|
|
967
|
+
addToCartBtn.disabled = !inStock;
|
|
968
|
+
const textEl = addToCartBtn.querySelector('.btn-add-to-cart__text');
|
|
969
|
+
if (textEl) textEl.textContent = inStock ? 'Add to Cart' : 'Out of Stock';
|
|
970
|
+
}
|
|
971
|
+
});
|
|
972
|
+
|
|
973
|
+
widgetRoot.addEventListener('submit', async (event) => {
|
|
974
|
+
const form = event.target.closest('.product-form');
|
|
975
|
+
if (!form || !widgetRoot.contains(form)) return;
|
|
976
|
+
|
|
977
|
+
event.preventDefault();
|
|
978
|
+
|
|
979
|
+
const addToCartBtn = form.querySelector('.btn-add-to-cart');
|
|
980
|
+
if (!addToCartBtn || addToCartBtn.disabled || addToCartBtn.classList.contains('loading')) return;
|
|
981
|
+
|
|
982
|
+
const quantityInput = form.querySelector('.quantity-input');
|
|
983
|
+
const productId = addToCartBtn.getAttribute('data-product-id') || form.getAttribute('data-product-id');
|
|
984
|
+
const quantity = parseInt((quantityInput && quantityInput.value) ? quantityInput.value : '1', 10);
|
|
985
|
+
if (!productId) return;
|
|
986
|
+
|
|
987
|
+
const textEl = addToCartBtn.querySelector('.btn-add-to-cart__text');
|
|
988
|
+
const originalText = textEl ? textEl.textContent : 'Add to Cart';
|
|
989
|
+
addToCartBtn.classList.add('loading');
|
|
990
|
+
addToCartBtn.disabled = true;
|
|
991
|
+
if (textEl) textEl.textContent = 'Adding...';
|
|
581
992
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
if (window.Theme && typeof window.Theme.
|
|
591
|
-
window.Theme.
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
});
|
|
993
|
+
try {
|
|
994
|
+
const addResult = await addToCartAPI(productId, quantity);
|
|
995
|
+
if (textEl) textEl.textContent = 'Added';
|
|
996
|
+
if (addResult && addResult.viaTheme !== true && window.Theme && typeof window.Theme.showNotification === 'function') {
|
|
997
|
+
window.Theme.showNotification('Product added to cart!', 'success', 3000);
|
|
998
|
+
}
|
|
999
|
+
} catch (error) {
|
|
1000
|
+
if (!String((error && error.message) || '').toLowerCase().includes('auth')) {
|
|
1001
|
+
if (window.Theme && typeof window.Theme.showNotification === 'function') {
|
|
1002
|
+
window.Theme.showNotification('Failed to add product to cart. Please try again.', 'error', 3000);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
} finally {
|
|
1006
|
+
setTimeout(() => {
|
|
1007
|
+
addToCartBtn.classList.remove('loading');
|
|
1008
|
+
const variantSelect = form.querySelector('.variation-select');
|
|
1009
|
+
if (variantSelect) {
|
|
1010
|
+
const selectedOption = variantSelect.options[variantSelect.selectedIndex];
|
|
1011
|
+
addToCartBtn.disabled = selectedOption ? selectedOption.dataset.inStock !== 'true' : false;
|
|
602
1012
|
} else {
|
|
603
|
-
|
|
604
|
-
alert('Cart functionality not available. Please try again later.');
|
|
1013
|
+
addToCartBtn.disabled = false;
|
|
605
1014
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
1015
|
+
if (textEl) textEl.textContent = originalText;
|
|
1016
|
+
}, 1200);
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
widgetRoot.addEventListener('keydown', (event) => {
|
|
1021
|
+
if (event.key === 'ArrowLeft') {
|
|
1022
|
+
setCarouselIndex(getCurrentSlideIndex() - 1);
|
|
1023
|
+
}
|
|
1024
|
+
if (event.key === 'ArrowRight') {
|
|
1025
|
+
setCarouselIndex(getCurrentSlideIndex() + 1);
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
const carouselContainer = widgetRoot.querySelector('.single-product-media-main');
|
|
1030
|
+
if (carouselContainer) {
|
|
1031
|
+
carouselContainer.addEventListener('touchstart', (event) => {
|
|
1032
|
+
touchStartX = event.changedTouches[0].screenX;
|
|
1033
|
+
}, { passive: true });
|
|
1034
|
+
carouselContainer.addEventListener('touchend', (event) => {
|
|
1035
|
+
touchEndX = event.changedTouches[0].screenX;
|
|
1036
|
+
const diff = touchStartX - touchEndX;
|
|
1037
|
+
if (Math.abs(diff) > 50) {
|
|
1038
|
+
setCarouselIndex(diff > 0 ? getCurrentSlideIndex() + 1 : getCurrentSlideIndex() - 1);
|
|
1039
|
+
}
|
|
1040
|
+
}, { passive: true });
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
setCarouselIndex(0);
|
|
1044
|
+
})();
|
|
1045
|
+
</script>
|
|
1046
|
+
|
|
609
1047
|
{% endif %}
|
|
610
1048
|
</section>
|