@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.
Files changed (90) hide show
  1. package/README.md +4 -0
  2. package/lib/lib/dev-server.js +344 -48
  3. package/lib/lib/liquid-engine.js +3 -1
  4. package/lib/lib/mock-data.js +473 -119
  5. package/lib/lib/widget-service.js +12 -4
  6. package/package.json +2 -2
  7. package/test-theme/assets/async-sections.js +32 -24
  8. package/test-theme/assets/cart-drawer.js +20 -22
  9. package/test-theme/assets/cart-manager.js +1 -15
  10. package/test-theme/assets/checkout-price-handler.js +12 -11
  11. package/test-theme/assets/checkout.css +1415 -0
  12. package/test-theme/assets/checkout.js +3174 -0
  13. package/test-theme/assets/components.css +178 -29
  14. package/test-theme/assets/delivery-zone.js +1 -1
  15. package/test-theme/assets/product-detail.css +1050 -0
  16. package/test-theme/assets/product-detail.js +2940 -0
  17. package/test-theme/assets/theme.css +95 -120
  18. package/test-theme/assets/theme.js +781 -186
  19. package/test-theme/layout/theme.liquid +91 -17
  20. package/test-theme/sections/content.liquid +64 -57
  21. package/test-theme/sections/footer-fallback.liquid +57 -7
  22. package/test-theme/sections/footer.liquid +63 -12
  23. package/test-theme/sections/header-fallback.liquid +41 -41
  24. package/test-theme/sections/header.liquid +41 -51
  25. package/test-theme/sections/hero-fallback.liquid +1 -1
  26. package/test-theme/sections/hero.liquid +159 -136
  27. package/test-theme/snippets/account-sidebar.liquid +121 -29
  28. package/test-theme/snippets/add-to-cart-modal.liquid +258 -206
  29. package/test-theme/snippets/breadcrumbs.liquid +98 -11
  30. package/test-theme/snippets/cart-drawer.liquid +93 -0
  31. package/test-theme/snippets/delivery-zone-city-selector.liquid +101 -15
  32. package/test-theme/snippets/delivery-zone-modal.liquid +529 -84
  33. package/test-theme/snippets/delivery-zone-search.liquid +104 -18
  34. package/test-theme/snippets/login-modal.liquid +269 -82
  35. package/test-theme/snippets/mega-menu.liquid +130 -43
  36. package/test-theme/snippets/news-thumbnail.liquid +120 -28
  37. package/test-theme/snippets/pagination.liquid +1 -1
  38. package/test-theme/snippets/price.liquid +100 -9
  39. package/test-theme/snippets/product-card-related.liquid +22 -4
  40. package/test-theme/snippets/product-card-simple.liquid +521 -25
  41. package/test-theme/snippets/product-card.liquid +145 -232
  42. package/test-theme/snippets/rating.liquid +100 -9
  43. package/test-theme/snippets/skeleton-collection-grid.liquid +94 -8
  44. package/test-theme/snippets/skeleton-product-card.liquid +102 -16
  45. package/test-theme/snippets/skeleton-product-grid.liquid +87 -1
  46. package/test-theme/snippets/social-sharing.liquid +133 -32
  47. package/test-theme/templates/account/dashboard.liquid +30 -0
  48. package/test-theme/templates/account/loyalty-redemption.liquid +29 -28
  49. package/test-theme/templates/account/loyalty.liquid +45 -43
  50. package/test-theme/templates/account/order-detail.liquid +15 -8
  51. package/test-theme/templates/account/orders.liquid +189 -35
  52. package/test-theme/templates/account/profile.liquid +509 -114
  53. package/test-theme/templates/account/register.liquid +18 -8
  54. package/test-theme/templates/account/return-orders.liquid +31 -30
  55. package/test-theme/templates/account/store-credit.liquid +27 -26
  56. package/test-theme/templates/account/subscriptions.liquid +22 -5
  57. package/test-theme/templates/account/wishlist.liquid +88 -19
  58. package/test-theme/templates/address-book.liquid +166 -69
  59. package/test-theme/templates/categories.liquid +90 -30
  60. package/test-theme/templates/checkout.liquid +137 -3834
  61. package/test-theme/templates/error.liquid +23 -21
  62. package/test-theme/templates/index.liquid +29 -0
  63. package/test-theme/templates/login.liquid +33 -6
  64. package/test-theme/templates/order-confirmation.liquid +67 -9
  65. package/test-theme/templates/page.liquid +418 -206
  66. package/test-theme/templates/product-detail.liquid +124 -3878
  67. package/test-theme/templates/products.liquid +155 -30
  68. package/test-theme/templates/search.liquid +739 -225
  69. package/test-theme/widgets/brand-carousel.liquid +102 -82
  70. package/test-theme/widgets/brand.liquid +78 -50
  71. package/test-theme/widgets/carousel.liquid +253 -121
  72. package/test-theme/widgets/category-list-carousel.liquid +32 -8
  73. package/test-theme/widgets/category-list.liquid +21 -6
  74. package/test-theme/widgets/category.liquid +104 -37
  75. package/test-theme/widgets/discount-time.liquid +326 -119
  76. package/test-theme/widgets/footer-menu.liquid +115 -23
  77. package/test-theme/widgets/footer.liquid +118 -5
  78. package/test-theme/widgets/gallery.liquid +29 -5
  79. package/test-theme/widgets/header-menu.liquid +25 -13
  80. package/test-theme/widgets/header.liquid +64 -26
  81. package/test-theme/widgets/html.liquid +29 -6
  82. package/test-theme/widgets/news.liquid +6 -0
  83. package/test-theme/widgets/product-canvas.liquid +20 -12
  84. package/test-theme/widgets/product-carousel.liquid +118 -56
  85. package/test-theme/widgets/shared/product-grid.liquid +12 -0
  86. package/test-theme/widgets/single-product.liquid +688 -250
  87. package/test-theme/widgets/spacebar-carousel.liquid +39 -10
  88. package/test-theme/widgets/spacebar.liquid +77 -6
  89. package/test-theme/widgets/splash.liquid +40 -30
  90. 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
- {% include 'snippets/product-card', product: product %}
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="/collections" class="btn btn-outline">Browse Collections</a>
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
- {% include 'snippets/pagination', pagination: pagination %}
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', function() {
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', function() {
443
+ filterToggle.addEventListener('click', () => {
444
+ const isOpening = !filterDropdown.classList.contains('active');
327
445
  filterDropdown.classList.toggle('active');
328
- document.body.classList.toggle('filter-open');
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', function() {
457
+ filterClose.addEventListener('click', () => {
335
458
  filterDropdown.classList.remove('active');
336
- document.body.classList.remove('filter-open');
459
+ closeFilter();
337
460
  });
338
461
  }
339
462
 
340
463
  // Close filter dropdown when clicking outside
341
- document.addEventListener('click', function(e) {
342
- if (filterDropdown && !filterDropdown.contains(e.target) && !filterToggle.contains(e.target)) {
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
- document.body.classList.remove('filter-open');
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', function() {
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', function() {
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', function() {
416
- const value = this.getAttribute('data-value');
417
- const label = this.getAttribute('data-label') || this.textContent;
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
- this.classList.add('active', 'selected');
436
- this.setAttribute('aria-selected', 'true');
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', function(e) {
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', function() {
458
- const view = this.dataset.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
- this.classList.add('active');
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', function() {
495
- priceRangeMin.value = this.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', function() {
501
- priceRangeMax.value = this.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', function() {
507
- priceMin.value = this.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', function() {
513
- priceMax.value = this.value;
637
+ priceRangeMax.addEventListener('input', () => {
638
+ priceMax.value = priceRangeMax.value;
514
639
  });
515
640
  }
516
641
  });