@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
|
@@ -3,8 +3,53 @@
|
|
|
3
3
|
O2VEND Default Theme - Products Template
|
|
4
4
|
{% endcomment %}
|
|
5
5
|
|
|
6
|
+
<style>
|
|
7
|
+
:root {
|
|
8
|
+
--color-primary: {{ settings.color_primary | default: '#000000' }};
|
|
9
|
+
--color-primary-hover: {{ settings.color_primary_dark | default: '#333333' }};
|
|
10
|
+
--color-primary-light: {{ settings.color_primary_light | default: '#666666' }};
|
|
11
|
+
--color-success: {{ settings.color_success | default: '#22c55e' }};
|
|
12
|
+
--color-success-light: {{ settings.color_success | default: '#22c55e' }};
|
|
13
|
+
--color-danger: {{ settings.color_error | default: '#ef4444' }};
|
|
14
|
+
--color-danger-light: {{ settings.color_error | default: '#ef4444' }};
|
|
15
|
+
--color-text: {{ settings.color_text | default: '#000000' }};
|
|
16
|
+
--color-text-light: {{ settings.color_text_muted | default: '#666666' }};
|
|
17
|
+
--color-background: {{ settings.color_background | default: '#ffffff' }};
|
|
18
|
+
--color-card-bg: {{ settings.color_surface | default: '#f5f5f5' }};
|
|
19
|
+
--color-border: {{ settings.color_border | default: '#cccccc' }};
|
|
20
|
+
--color-border-light: {{ settings.color_surface | default: '#f5f5f5' }};
|
|
21
|
+
--shadow-sm: var(--shadow-sm);
|
|
22
|
+
--shadow-md: var(--shadow-md);
|
|
23
|
+
--shadow-lg: var(--shadow-lg);
|
|
24
|
+
--radius-md: var(--radius-md);
|
|
25
|
+
--radius-lg: var(--radius-lg);
|
|
26
|
+
--spacing-xs: var(--space-3);
|
|
27
|
+
--spacing-sm: var(--space-4);
|
|
28
|
+
--spacing-md: var(--space-6);
|
|
29
|
+
--spacing-lg: var(--space-8);
|
|
30
|
+
--spacing-xl: var(--space-12);
|
|
31
|
+
}
|
|
32
|
+
</style>
|
|
33
|
+
|
|
6
34
|
<!-- Breadcrumb Navigation Removed for Default Style -->
|
|
7
35
|
|
|
36
|
+
<!-- Hero Widgets (if any) -->
|
|
37
|
+
{% assign hero_widgets = widgets.hero %}
|
|
38
|
+
{% if hero_widgets and hero_widgets.size > 0 %}
|
|
39
|
+
<section class="theme-section theme-section--hero" data-section="hero">
|
|
40
|
+
{% for widget in hero_widgets %}
|
|
41
|
+
<div class="theme-widget-wrapper"
|
|
42
|
+
data-widget-id="{{ widget.id }}"
|
|
43
|
+
data-widget-type="{{ widget.type }}"
|
|
44
|
+
data-widget-position="{{ widget.Position | default: widget.position | default: forloop.index }}">
|
|
45
|
+
{% if widget and widget.template_path %}
|
|
46
|
+
{% render widget.template_path, widget: widget, settings: settings, shop: shop, is_hero_first: forloop.first %}
|
|
47
|
+
{% endif %}
|
|
48
|
+
</div>
|
|
49
|
+
{% endfor %}
|
|
50
|
+
</section>
|
|
51
|
+
{% endif %}
|
|
52
|
+
|
|
8
53
|
<!-- Collection Header -->
|
|
9
54
|
<section class="collection-header">
|
|
10
55
|
<div class="collection-header-wrapper">
|
|
@@ -110,7 +155,7 @@
|
|
|
110
155
|
<div class="products-grid" id="products-grid">
|
|
111
156
|
{% for product in collection.products %}
|
|
112
157
|
{% hook 'product_card_before' %}
|
|
113
|
-
{%
|
|
158
|
+
{% render 'snippets/product-card', product: product %}
|
|
114
159
|
{% hook 'product_card_after' %}
|
|
115
160
|
{% endfor %}
|
|
116
161
|
</div>
|
|
@@ -123,7 +168,7 @@
|
|
|
123
168
|
<p class="no-products-description">Try adjusting your filters or browse our other collections.</p>
|
|
124
169
|
<div class="no-products-actions">
|
|
125
170
|
<button class="btn btn-primary" id="clear-all-filters">Clear All Filters</button>
|
|
126
|
-
<a href="/
|
|
171
|
+
<a href="/categories" class="btn btn-outline">Browse Categories</a>
|
|
127
172
|
</div>
|
|
128
173
|
</div>
|
|
129
174
|
</div>
|
|
@@ -131,14 +176,31 @@
|
|
|
131
176
|
</div>
|
|
132
177
|
|
|
133
178
|
<!-- Pagination -->
|
|
134
|
-
{%
|
|
179
|
+
{% render 'snippets/pagination', pagination: pagination %}
|
|
135
180
|
</div>
|
|
136
181
|
</section>
|
|
137
182
|
{% hook 'product_list_after' %}
|
|
138
183
|
|
|
184
|
+
<!-- Content Widgets (if any) -->
|
|
185
|
+
{% assign content_widgets = widgets.content %}
|
|
186
|
+
{% if content_widgets and content_widgets.size > 0 %}
|
|
187
|
+
<section class="theme-section theme-section--content" data-section="content">
|
|
188
|
+
{% for widget in content_widgets %}
|
|
189
|
+
<div class="theme-widget-wrapper"
|
|
190
|
+
data-widget-id="{{ widget.id }}"
|
|
191
|
+
data-widget-type="{{ widget.type }}"
|
|
192
|
+
data-widget-position="{{ widget.Position | default: widget.position | default: forloop.index }}">
|
|
193
|
+
{% if widget and widget.template_path %}
|
|
194
|
+
{% render widget.template_path, widget: widget, settings: settings, shop: shop %}
|
|
195
|
+
{% endif %}
|
|
196
|
+
</div>
|
|
197
|
+
{% endfor %}
|
|
198
|
+
</section>
|
|
199
|
+
{% endif %}
|
|
200
|
+
|
|
139
201
|
<script>
|
|
140
202
|
// Collection page specific JavaScript - Server-side filtering & sorting
|
|
141
|
-
document.addEventListener('DOMContentLoaded',
|
|
203
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
142
204
|
// Parse URL parameters
|
|
143
205
|
const urlParams = new URLSearchParams(window.location.search);
|
|
144
206
|
|
|
@@ -321,27 +383,90 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
321
383
|
const resetFiltersBtn = document.getElementById('reset-filters');
|
|
322
384
|
const clearAllFiltersBtn = document.getElementById('clear-all-filters');
|
|
323
385
|
|
|
386
|
+
// Calculate scrollbar width to prevent layout shift
|
|
387
|
+
function getScrollbarWidth() {
|
|
388
|
+
// Create a temporary div to measure scrollbar width
|
|
389
|
+
const outer = document.createElement('div');
|
|
390
|
+
outer.style.visibility = 'hidden';
|
|
391
|
+
outer.style.overflow = 'scroll';
|
|
392
|
+
outer.style.msOverflowStyle = 'scrollbar';
|
|
393
|
+
outer.style.width = '100px';
|
|
394
|
+
outer.style.position = 'absolute';
|
|
395
|
+
outer.style.top = '-9999px';
|
|
396
|
+
document.body.appendChild(outer);
|
|
397
|
+
|
|
398
|
+
const inner = document.createElement('div');
|
|
399
|
+
inner.style.width = '100%';
|
|
400
|
+
outer.appendChild(inner);
|
|
401
|
+
|
|
402
|
+
const scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
|
|
403
|
+
|
|
404
|
+
outer.parentNode.removeChild(outer);
|
|
405
|
+
|
|
406
|
+
return scrollbarWidth;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Store scrollbar width in CSS variable (only calculate once)
|
|
410
|
+
let scrollbarWidth = null;
|
|
411
|
+
function getScrollbarWidthCached() {
|
|
412
|
+
if (scrollbarWidth === null) {
|
|
413
|
+
scrollbarWidth = getScrollbarWidth();
|
|
414
|
+
document.documentElement.style.setProperty('--scrollbar-width', scrollbarWidth + 'px');
|
|
415
|
+
}
|
|
416
|
+
return scrollbarWidth;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Initialize scrollbar width on page load
|
|
420
|
+
getScrollbarWidthCached();
|
|
421
|
+
|
|
422
|
+
// Function to open filter with scroll lock
|
|
423
|
+
function openFilter() {
|
|
424
|
+
const scrollY = window.scrollY;
|
|
425
|
+
// Store scroll position before applying fixed position
|
|
426
|
+
document.body.dataset.scrollY = scrollY;
|
|
427
|
+
document.body.style.top = `-${scrollY}px`;
|
|
428
|
+
document.body.classList.add('filter-open');
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Function to close filter and restore scroll
|
|
432
|
+
function closeFilter() {
|
|
433
|
+
const scrollY = parseInt(document.body.dataset.scrollY || '0');
|
|
434
|
+
document.body.classList.remove('filter-open');
|
|
435
|
+
document.body.style.top = '';
|
|
436
|
+
delete document.body.dataset.scrollY;
|
|
437
|
+
// Restore scroll position
|
|
438
|
+
window.scrollTo(0, scrollY);
|
|
439
|
+
}
|
|
440
|
+
|
|
324
441
|
// Toggle filter dropdown
|
|
325
442
|
if (filterToggle && filterDropdown) {
|
|
326
|
-
filterToggle.addEventListener('click',
|
|
443
|
+
filterToggle.addEventListener('click', () => {
|
|
444
|
+
const isOpening = !filterDropdown.classList.contains('active');
|
|
327
445
|
filterDropdown.classList.toggle('active');
|
|
328
|
-
|
|
446
|
+
|
|
447
|
+
if (isOpening) {
|
|
448
|
+
openFilter();
|
|
449
|
+
} else {
|
|
450
|
+
closeFilter();
|
|
451
|
+
}
|
|
329
452
|
});
|
|
330
453
|
}
|
|
331
454
|
|
|
332
455
|
// Close filter dropdown
|
|
333
456
|
if (filterClose) {
|
|
334
|
-
filterClose.addEventListener('click',
|
|
457
|
+
filterClose.addEventListener('click', () => {
|
|
335
458
|
filterDropdown.classList.remove('active');
|
|
336
|
-
|
|
459
|
+
closeFilter();
|
|
337
460
|
});
|
|
338
461
|
}
|
|
339
462
|
|
|
340
463
|
// Close filter dropdown when clicking outside
|
|
341
|
-
document.addEventListener('click',
|
|
342
|
-
if (filterDropdown &&
|
|
464
|
+
document.addEventListener('click', (e) => {
|
|
465
|
+
if (filterDropdown && filterDropdown.classList.contains('active') &&
|
|
466
|
+
!filterDropdown.contains(e.target) &&
|
|
467
|
+
!filterToggle.contains(e.target)) {
|
|
343
468
|
filterDropdown.classList.remove('active');
|
|
344
|
-
|
|
469
|
+
closeFilter();
|
|
345
470
|
}
|
|
346
471
|
});
|
|
347
472
|
|
|
@@ -374,7 +499,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
374
499
|
|
|
375
500
|
// Apply filters - Submit to server
|
|
376
501
|
if (applyFiltersBtn) {
|
|
377
|
-
applyFiltersBtn.addEventListener('click',
|
|
502
|
+
applyFiltersBtn.addEventListener('click', () => {
|
|
378
503
|
applyFiltersToURL();
|
|
379
504
|
});
|
|
380
505
|
}
|
|
@@ -405,16 +530,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
405
530
|
}
|
|
406
531
|
|
|
407
532
|
if (sortToggle && sortMenu) {
|
|
408
|
-
sortToggle.addEventListener('click',
|
|
533
|
+
sortToggle.addEventListener('click', () => {
|
|
409
534
|
sortDropdown.classList.toggle('open');
|
|
410
535
|
const expanded = sortToggle.getAttribute('aria-expanded') === 'true';
|
|
411
536
|
sortToggle.setAttribute('aria-expanded', (!expanded).toString());
|
|
412
537
|
});
|
|
413
538
|
|
|
414
539
|
sortMenu.querySelectorAll('.sort-option').forEach(option => {
|
|
415
|
-
option.addEventListener('click',
|
|
416
|
-
const value =
|
|
417
|
-
const label =
|
|
540
|
+
option.addEventListener('click', () => {
|
|
541
|
+
const value = option.getAttribute('data-value');
|
|
542
|
+
const label = option.getAttribute('data-label') || option.textContent;
|
|
418
543
|
|
|
419
544
|
// Update sort toggle label
|
|
420
545
|
if (sortLabel) {
|
|
@@ -432,8 +557,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
432
557
|
opt.classList.remove('active', 'selected');
|
|
433
558
|
opt.setAttribute('aria-selected', 'false');
|
|
434
559
|
});
|
|
435
|
-
|
|
436
|
-
|
|
560
|
+
option.classList.add('active', 'selected');
|
|
561
|
+
option.setAttribute('aria-selected', 'true');
|
|
437
562
|
|
|
438
563
|
sortDropdown.classList.remove('open');
|
|
439
564
|
applySort(value);
|
|
@@ -441,7 +566,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
441
566
|
});
|
|
442
567
|
|
|
443
568
|
// Close on outside click
|
|
444
|
-
document.addEventListener('click',
|
|
569
|
+
document.addEventListener('click', (e) => {
|
|
445
570
|
if (!sortDropdown.contains(e.target)) {
|
|
446
571
|
sortDropdown.classList.remove('open');
|
|
447
572
|
sortToggle.setAttribute('aria-expanded', 'false');
|
|
@@ -454,12 +579,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
454
579
|
const productsGrid = document.getElementById('products-grid');
|
|
455
580
|
|
|
456
581
|
viewBtns.forEach(btn => {
|
|
457
|
-
btn.addEventListener('click',
|
|
458
|
-
const view =
|
|
582
|
+
btn.addEventListener('click', () => {
|
|
583
|
+
const view = btn.dataset.view;
|
|
459
584
|
|
|
460
585
|
// Update active button
|
|
461
586
|
viewBtns.forEach(b => b.classList.remove('active'));
|
|
462
|
-
|
|
587
|
+
btn.classList.add('active');
|
|
463
588
|
|
|
464
589
|
// Update grid class
|
|
465
590
|
if (productsGrid) {
|
|
@@ -491,26 +616,26 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
491
616
|
const priceRangeMax = document.getElementById('price-range-max');
|
|
492
617
|
|
|
493
618
|
if (priceMin && priceRangeMin) {
|
|
494
|
-
priceMin.addEventListener('input',
|
|
495
|
-
priceRangeMin.value =
|
|
619
|
+
priceMin.addEventListener('input', () => {
|
|
620
|
+
priceRangeMin.value = priceMin.value;
|
|
496
621
|
});
|
|
497
622
|
}
|
|
498
623
|
|
|
499
624
|
if (priceMax && priceRangeMax) {
|
|
500
|
-
priceMax.addEventListener('input',
|
|
501
|
-
priceRangeMax.value =
|
|
625
|
+
priceMax.addEventListener('input', () => {
|
|
626
|
+
priceRangeMax.value = priceMax.value;
|
|
502
627
|
});
|
|
503
628
|
}
|
|
504
629
|
|
|
505
630
|
if (priceRangeMin && priceMin) {
|
|
506
|
-
priceRangeMin.addEventListener('input',
|
|
507
|
-
priceMin.value =
|
|
631
|
+
priceRangeMin.addEventListener('input', () => {
|
|
632
|
+
priceMin.value = priceRangeMin.value;
|
|
508
633
|
});
|
|
509
634
|
}
|
|
510
635
|
|
|
511
636
|
if (priceRangeMax && priceMax) {
|
|
512
|
-
priceRangeMax.addEventListener('input',
|
|
513
|
-
priceMax.value =
|
|
637
|
+
priceRangeMax.addEventListener('input', () => {
|
|
638
|
+
priceMax.value = priceRangeMax.value;
|
|
514
639
|
});
|
|
515
640
|
}
|
|
516
641
|
});
|