@o2vend/theme-cli 1.0.32

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 (116) hide show
  1. package/README.md +425 -0
  2. package/assets/Logo_o2vend.png +0 -0
  3. package/assets/favicon.png +0 -0
  4. package/assets/logo-white.png +0 -0
  5. package/bin/o2vend +42 -0
  6. package/config/widget-map.json +50 -0
  7. package/lib/commands/check.js +201 -0
  8. package/lib/commands/generate.js +33 -0
  9. package/lib/commands/init.js +214 -0
  10. package/lib/commands/optimize.js +216 -0
  11. package/lib/commands/package.js +208 -0
  12. package/lib/commands/serve.js +105 -0
  13. package/lib/commands/validate.js +191 -0
  14. package/lib/lib/api-client.js +357 -0
  15. package/lib/lib/dev-server.js +2618 -0
  16. package/lib/lib/file-watcher.js +80 -0
  17. package/lib/lib/hot-reload.js +106 -0
  18. package/lib/lib/liquid-engine.js +822 -0
  19. package/lib/lib/liquid-filters.js +671 -0
  20. package/lib/lib/mock-api-server.js +989 -0
  21. package/lib/lib/mock-data.js +1468 -0
  22. package/lib/lib/widget-service.js +321 -0
  23. package/package.json +70 -0
  24. package/test-theme/README.md +27 -0
  25. package/test-theme/assets/async-sections.js +446 -0
  26. package/test-theme/assets/cart-drawer.js +463 -0
  27. package/test-theme/assets/cart-manager.js +223 -0
  28. package/test-theme/assets/checkout-price-handler.js +368 -0
  29. package/test-theme/assets/components.css +4629 -0
  30. package/test-theme/assets/delivery-zone.css +299 -0
  31. package/test-theme/assets/delivery-zone.js +396 -0
  32. package/test-theme/assets/logo.png +0 -0
  33. package/test-theme/assets/sections.css +48 -0
  34. package/test-theme/assets/theme.css +3500 -0
  35. package/test-theme/assets/theme.js +3745 -0
  36. package/test-theme/config/settings_data.json +292 -0
  37. package/test-theme/config/settings_schema.json +1050 -0
  38. package/test-theme/layout/theme.liquid +195 -0
  39. package/test-theme/locales/en.default.json +260 -0
  40. package/test-theme/sections/content-fallback.liquid +53 -0
  41. package/test-theme/sections/content.liquid +57 -0
  42. package/test-theme/sections/footer-fallback.liquid +328 -0
  43. package/test-theme/sections/footer.liquid +278 -0
  44. package/test-theme/sections/header-fallback.liquid +1805 -0
  45. package/test-theme/sections/header.liquid +1145 -0
  46. package/test-theme/sections/hero-fallback.liquid +212 -0
  47. package/test-theme/sections/hero.liquid +136 -0
  48. package/test-theme/snippets/account-sidebar.liquid +200 -0
  49. package/test-theme/snippets/add-to-cart-modal.liquid +484 -0
  50. package/test-theme/snippets/breadcrumbs.liquid +134 -0
  51. package/test-theme/snippets/cart-drawer.liquid +467 -0
  52. package/test-theme/snippets/delivery-zone-city-selector.liquid +79 -0
  53. package/test-theme/snippets/delivery-zone-modal.liquid +337 -0
  54. package/test-theme/snippets/delivery-zone-search.liquid +78 -0
  55. package/test-theme/snippets/icon.liquid +105 -0
  56. package/test-theme/snippets/login-modal.liquid +346 -0
  57. package/test-theme/snippets/mega-menu.liquid +812 -0
  58. package/test-theme/snippets/news-thumbnail.liquid +187 -0
  59. package/test-theme/snippets/pagination.liquid +120 -0
  60. package/test-theme/snippets/price.liquid +92 -0
  61. package/test-theme/snippets/product-card-related.liquid +78 -0
  62. package/test-theme/snippets/product-card-simple.liquid +41 -0
  63. package/test-theme/snippets/product-card.liquid +697 -0
  64. package/test-theme/snippets/rating.liquid +85 -0
  65. package/test-theme/snippets/skeleton-collection-grid.liquid +114 -0
  66. package/test-theme/snippets/skeleton-product-card.liquid +124 -0
  67. package/test-theme/snippets/skeleton-product-grid.liquid +34 -0
  68. package/test-theme/snippets/social-sharing.liquid +185 -0
  69. package/test-theme/templates/account/dashboard.liquid +401 -0
  70. package/test-theme/templates/account/loyalty-redemption.liquid +405 -0
  71. package/test-theme/templates/account/loyalty.liquid +588 -0
  72. package/test-theme/templates/account/order-detail.liquid +230 -0
  73. package/test-theme/templates/account/orders.liquid +349 -0
  74. package/test-theme/templates/account/profile.liquid +758 -0
  75. package/test-theme/templates/account/register.liquid +232 -0
  76. package/test-theme/templates/account/return-orders.liquid +348 -0
  77. package/test-theme/templates/account/store-credit.liquid +464 -0
  78. package/test-theme/templates/account/subscriptions.liquid +601 -0
  79. package/test-theme/templates/account/wishlist.liquid +419 -0
  80. package/test-theme/templates/address-book.liquid +1092 -0
  81. package/test-theme/templates/categories.liquid +452 -0
  82. package/test-theme/templates/checkout.liquid +4511 -0
  83. package/test-theme/templates/error.liquid +384 -0
  84. package/test-theme/templates/index.liquid +11 -0
  85. package/test-theme/templates/login.liquid +185 -0
  86. package/test-theme/templates/order-confirmation.liquid +720 -0
  87. package/test-theme/templates/page.liquid +297 -0
  88. package/test-theme/templates/product-detail.liquid +4363 -0
  89. package/test-theme/templates/products.liquid +518 -0
  90. package/test-theme/templates/search.liquid +922 -0
  91. package/test-theme/theme.json.example +19 -0
  92. package/test-theme/widgets/brand-carousel.liquid +676 -0
  93. package/test-theme/widgets/brand.liquid +245 -0
  94. package/test-theme/widgets/carousel.liquid +843 -0
  95. package/test-theme/widgets/category-list-carousel.liquid +656 -0
  96. package/test-theme/widgets/category-list.liquid +340 -0
  97. package/test-theme/widgets/category.liquid +475 -0
  98. package/test-theme/widgets/discount-time.liquid +176 -0
  99. package/test-theme/widgets/footer-menu.liquid +695 -0
  100. package/test-theme/widgets/footer.liquid +179 -0
  101. package/test-theme/widgets/gallery.liquid +271 -0
  102. package/test-theme/widgets/header-menu.liquid +932 -0
  103. package/test-theme/widgets/header.liquid +159 -0
  104. package/test-theme/widgets/html.liquid +214 -0
  105. package/test-theme/widgets/news.liquid +217 -0
  106. package/test-theme/widgets/product-canvas.liquid +235 -0
  107. package/test-theme/widgets/product-carousel.liquid +502 -0
  108. package/test-theme/widgets/product.liquid +45 -0
  109. package/test-theme/widgets/recently-viewed.liquid +26 -0
  110. package/test-theme/widgets/shared/product-grid.liquid +339 -0
  111. package/test-theme/widgets/simple-product.liquid +42 -0
  112. package/test-theme/widgets/single-product.liquid +610 -0
  113. package/test-theme/widgets/spacebar-carousel.liquid +663 -0
  114. package/test-theme/widgets/spacebar.liquid +279 -0
  115. package/test-theme/widgets/splash.liquid +378 -0
  116. package/test-theme/widgets/testimonial-carousel.liquid +709 -0
@@ -0,0 +1,339 @@
1
+ {% liquid
2
+ assign widget_settings = widget.settings
3
+ assign widget_content = widget.content
4
+ assign widget_data = widget.data
5
+
6
+ assign widget_products = products
7
+ if widget_products == nil and widget_data and widget_data.products
8
+ assign widget_products = widget_data.products
9
+ endif
10
+ if widget_products == nil and widget_content and widget_content.products
11
+ assign widget_products = widget_content.products
12
+ endif
13
+ if widget_products == nil and widget_settings and widget_settings.products
14
+ assign widget_products = widget_settings.products
15
+ endif
16
+
17
+ assign heading = heading
18
+ if heading == nil and widget_settings and widget_settings.title
19
+ assign heading = widget_settings.title
20
+ endif
21
+ if heading == nil and widget_content and widget_content.title
22
+ assign heading = widget_content.title
23
+ endif
24
+
25
+ assign description = description
26
+ if description == nil and widget_settings and widget_settings.subtitle
27
+ assign description = widget_settings.subtitle
28
+ endif
29
+ if description == nil and widget_content and widget_content.subtitle
30
+ assign description = widget_content.subtitle
31
+ endif
32
+
33
+ assign columns = columns
34
+ if columns == nil and widget_settings and widget_settings.columns
35
+ assign columns = widget_settings.columns
36
+ endif
37
+ if columns == nil and widget_content and widget_content.columns
38
+ assign columns = widget_content.columns
39
+ endif
40
+ if columns == nil
41
+ assign columns = 4
42
+ endif
43
+
44
+ assign show_price = show_price
45
+ if show_price == nil and widget_settings and widget_settings.show_price != nil
46
+ assign show_price = widget_settings.show_price
47
+ endif
48
+ if show_price == nil and widget_content and widget_content.show_price != nil
49
+ assign show_price = widget_content.show_price
50
+ endif
51
+ if show_price == nil
52
+ assign show_price = true
53
+ endif
54
+
55
+ assign show_add_to_cart = show_add_to_cart
56
+ if show_add_to_cart == nil and widget_settings and widget_settings.show_add_to_cart != nil
57
+ assign show_add_to_cart = widget_settings.show_add_to_cart
58
+ endif
59
+ if show_add_to_cart == nil and widget_content and widget_content.show_add_to_cart != nil
60
+ assign show_add_to_cart = widget_content.show_add_to_cart
61
+ endif
62
+ if show_add_to_cart == nil
63
+ assign show_add_to_cart = true
64
+ endif
65
+ %}
66
+
67
+ {% assign show_title_raw = show_widget_title | default: 'Yes' %}
68
+ {% if show_title_raw == null or show_title_raw == blank or show_title_raw == 'null' %}
69
+ {% assign show_title = true %}
70
+ {% else %}
71
+ {% assign show_title = show_title_raw == 'Yes' or show_title_raw == true %}
72
+ {% endif %}
73
+ {% assign title_alignment_raw = widget_title_alignment | default: 'center' %}
74
+ {% assign title_alignment = title_alignment_raw | downcase %}
75
+ {% if title_alignment != 'left' and title_alignment != 'right' and title_alignment != 'center' %}
76
+ {% assign title_alignment = 'center' %}
77
+ {% endif %}
78
+
79
+ {% if widget_products and widget_products.size > 0 %}
80
+ <section class="widget widget-products" data-widget-id="{{ widget.id }}">
81
+ {% if show_title %}
82
+ {% if heading %}
83
+ <header class="widget-header" style="text-align: {{ title_alignment }};">
84
+ <h2 class="widget-title">{{ heading }}</h2>
85
+ {% if description %}
86
+ <p class="widget-subtitle">{{ description }}</p>
87
+ {% endif %}
88
+ </header>
89
+ {% endif %}
90
+ {% endif %}
91
+
92
+ <div class="widget-products-grid widget-products-grid--{{ columns }}-col">
93
+ {% for product in widget_products %}
94
+ <div class="widget-product-card">
95
+ {% if render_snippet %}
96
+ {% render render_snippet, product: product, widget: widget %}
97
+ {% elsif widget_settings and widget_settings.card_snippet %}
98
+ {% render widget_settings.card_snippet, product: product, widget: widget %}
99
+ {% else %}
100
+ {% render 'snippets/product-card', product: product, widget: widget, show_price: show_price, show_add_to_cart: show_add_to_cart %}
101
+ {% endif %}
102
+ </div>
103
+ {% endfor %}
104
+ </div>
105
+ </section>
106
+ {% else %}
107
+ <section class="widget widget-products widget-products--empty" data-widget-id="{{ widget.id }}">
108
+ <div class="widget-empty">
109
+ <h3>{{ heading | default: 'Products coming soon' }}</h3>
110
+ {% if description %}
111
+ <p>{{ description }}</p>
112
+ {% else %}
113
+ <p>{{ 'We could not find any products for this widget.' | t }}</p>
114
+ {% endif %}
115
+ </div>
116
+ </section>
117
+ {% endif %}
118
+
119
+ <style>
120
+ /* Widget Products Grid - Responsive Layout */
121
+ /* CRITICAL: All product cards MUST be identical size */
122
+
123
+ .widget.widget-products {
124
+ padding: 40px 0;
125
+ width: 100%;
126
+ box-sizing: border-box;
127
+ position: relative;
128
+ }
129
+
130
+ .widget.widget-products .widget-header {
131
+ text-align: center;
132
+ margin-bottom: 32px;
133
+ padding: 0 16px;
134
+ position: static;
135
+ z-index: auto;
136
+ }
137
+
138
+ .widget-title {
139
+ font-size: 1.4rem;
140
+ font-weight: 500;
141
+ line-height: 1.3;
142
+ color: #111;
143
+ margin: 0 0 8px 0;
144
+ letter-spacing: -0.01em;
145
+ }
146
+
147
+ .widget-subtitle {
148
+ font-size: 14px;
149
+ color: #6b7280;
150
+ margin: 0;
151
+ }
152
+
153
+ /* Base grid - Desktop: 4 columns */
154
+ .widget-products-grid {
155
+ display: grid;
156
+ grid-template-columns: repeat(4, 1fr);
157
+ grid-auto-rows: auto;
158
+ gap: 24px;
159
+ max-width: 1400px;
160
+ margin: 0 auto;
161
+ padding: 0 24px;
162
+ box-sizing: border-box;
163
+ }
164
+
165
+ /* Column variations - Desktop */
166
+ .widget-products-grid--2-col {
167
+ grid-template-columns: repeat(2, 1fr);
168
+ }
169
+
170
+ .widget-products-grid--3-col {
171
+ grid-template-columns: repeat(3, 1fr);
172
+ }
173
+
174
+ .widget-products-grid--4-col {
175
+ grid-template-columns: repeat(4, 1fr);
176
+ }
177
+
178
+ .widget-products-grid--5-col {
179
+ grid-template-columns: repeat(5, 1fr);
180
+ }
181
+
182
+ .widget-products-grid--6-col {
183
+ grid-template-columns: repeat(6, 1fr);
184
+ }
185
+
186
+ /* FORCE all widget product cards to be identical */
187
+ .widget-product-card {
188
+ width: 100%;
189
+ max-width: 100%;
190
+ min-width: 0;
191
+ grid-column: span 1;
192
+ grid-row: span 1;
193
+ overflow: hidden;
194
+ box-sizing: border-box;
195
+ }
196
+
197
+ /* FORCE all product cards inside widget to be identical */
198
+ .widget-products-grid .product-card {
199
+ width: 100%;
200
+ max-width: 100%;
201
+ grid-column: span 1;
202
+ grid-row: span 1;
203
+ box-sizing: border-box;
204
+ }
205
+
206
+ /* FORCE all image containers to have same aspect ratio */
207
+ .widget-products-grid .product-image-container {
208
+ aspect-ratio: 1;
209
+ width: 100%;
210
+ max-width: 100%;
211
+ overflow: hidden;
212
+ position: relative;
213
+ }
214
+
215
+ /* FORCE all images to cover container */
216
+ .widget-products-grid .product-image {
217
+ width: 100%;
218
+ height: 100%;
219
+ max-width: 100%;
220
+ max-height: 100%;
221
+ object-fit: cover;
222
+ position: absolute;
223
+ top: 0;
224
+ left: 0;
225
+ }
226
+
227
+ /* Override asymmetric grid for ALL nth-child variations */
228
+ .widget-products-grid .widget-product-card:nth-child(n),
229
+ .widget-products-grid .widget-product-card:first-child,
230
+ .widget-products-grid .widget-product-card:nth-child(3n),
231
+ .widget-products-grid .widget-product-card:nth-child(5n),
232
+ .widget-products-grid .product-card:nth-child(n),
233
+ .widget-products-grid .product-card:first-child,
234
+ .widget-products-grid .product-card:nth-child(3n),
235
+ .widget-products-grid .product-card:nth-child(5n) {
236
+ grid-column: span 1;
237
+ grid-row: span 1;
238
+ width: 100%;
239
+ max-width: 100%;
240
+ }
241
+
242
+ .widget-products--empty {
243
+ text-align: center;
244
+ padding: 60px 24px;
245
+ }
246
+
247
+ .widget-empty h3 {
248
+ font-size: 18px;
249
+ font-weight: 500;
250
+ color: #374151;
251
+ margin: 0 0 8px 0;
252
+ }
253
+
254
+ .widget-empty p {
255
+ font-size: 14px;
256
+ color: #6b7280;
257
+ margin: 0;
258
+ }
259
+
260
+ /* Tablet: 3 columns - max-width 1024px */
261
+ @media screen and (max-width: 1024px) {
262
+ .widget-products-grid,
263
+ .widget-products-grid--4-col,
264
+ .widget-products-grid--5-col,
265
+ .widget-products-grid--6-col {
266
+ grid-template-columns: repeat(3, 1fr) !important;
267
+ gap: 20px;
268
+ }
269
+ }
270
+
271
+ /* Small tablet / Large mobile: 2 columns - max-width 768px */
272
+ @media screen and (max-width: 768px) {
273
+ .widget.widget-products {
274
+ padding: 32px 0;
275
+ }
276
+
277
+ .widget.widget-products .widget-header {
278
+ margin-bottom: 24px;
279
+ position: static;
280
+ }
281
+
282
+ .widget-title {
283
+ font-size: 1.3rem;
284
+ }
285
+
286
+ .widget-products-grid,
287
+ .widget-products-grid--2-col,
288
+ .widget-products-grid--3-col,
289
+ .widget-products-grid--4-col,
290
+ .widget-products-grid--5-col,
291
+ .widget-products-grid--6-col {
292
+ grid-template-columns: repeat(2, 1fr) !important;
293
+ gap: 16px;
294
+ padding: 0 16px;
295
+ }
296
+ }
297
+
298
+ /* Mobile: 2 columns - max-width 480px */
299
+ @media screen and (max-width: 480px) {
300
+ .widget.widget-products {
301
+ padding: 24px 0;
302
+ }
303
+
304
+ .widget-products-grid,
305
+ .widget-products-grid--2-col,
306
+ .widget-products-grid--3-col,
307
+ .widget-products-grid--4-col,
308
+ .widget-products-grid--5-col,
309
+ .widget-products-grid--6-col {
310
+ grid-template-columns: repeat(2, 1fr) !important;
311
+ gap: 12px;
312
+ padding: 0 12px;
313
+ }
314
+
315
+ .widget-title {
316
+ font-size: 1.2rem;
317
+ }
318
+
319
+ .widget.widget-products .widget-header {
320
+ margin-bottom: 16px;
321
+ position: static;
322
+ }
323
+ }
324
+
325
+ /* Extra small mobile: still 2 columns but tighter spacing */
326
+ @media screen and (max-width: 360px) {
327
+ .widget-products-grid,
328
+ .widget-products-grid--2-col,
329
+ .widget-products-grid--3-col,
330
+ .widget-products-grid--4-col,
331
+ .widget-products-grid--5-col,
332
+ .widget-products-grid--6-col {
333
+ grid-template-columns: repeat(2, 1fr) !important;
334
+ gap: 8px;
335
+ padding: 0 8px;
336
+ }
337
+ }
338
+ </style>
339
+
@@ -0,0 +1,42 @@
1
+ {% liquid
2
+ assign widget_settings = widget.settings
3
+ assign widget_data = widget.data
4
+
5
+ comment
6
+ Dynamic Widget: Products come from widget_data.products (enriched by WidgetService)
7
+ Settings come from widget_settings
8
+ endcomment
9
+ assign products = widget_data.products
10
+ assign heading = widget_settings.title
11
+ assign description = widget_settings.subtitle
12
+ assign columns = widget_settings.columns | default: 4
13
+ assign show_add_to_cart = widget_settings.show_add_to_cart | default: true
14
+ assign show_widget_title_raw = widget_settings.showWidgetTitle | default: 'Yes'
15
+ if show_widget_title_raw == null or show_widget_title_raw == blank or show_widget_title_raw == 'null'
16
+ assign show_widget_title = true
17
+ else
18
+ if show_widget_title_raw == 'Yes'
19
+ assign show_widget_title = true
20
+ elsif show_widget_title_raw == true
21
+ assign show_widget_title = true
22
+ else
23
+ assign show_widget_title = false
24
+ endif
25
+ endif
26
+ assign widget_title_alignment_raw = widget_settings.widgetTitleAlignment | default: 'center'
27
+ assign widget_title_alignment = widget_title_alignment_raw | downcase
28
+ if widget_title_alignment != 'left' and widget_title_alignment != 'right' and widget_title_alignment != 'center'
29
+ assign widget_title_alignment = 'center'
30
+ endif
31
+ %}
32
+
33
+ {% render 'widgets/shared/product-grid',
34
+ widget: widget,
35
+ heading: heading,
36
+ description: description,
37
+ products: products,
38
+ columns: columns,
39
+ show_add_to_cart: show_add_to_cart,
40
+ show_widget_title: show_widget_title,
41
+ widget_title_alignment: widget_title_alignment %}
42
+