@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,245 @@
1
+ {% liquid
2
+ assign widget_settings = widget.settings
3
+ assign widget_data = widget.data
4
+
5
+ comment
6
+ Dynamic Widget: Brands come from widget_data.brands (enriched by WidgetService)
7
+ Settings come from widget_settings
8
+ endcomment
9
+ assign brands = widget_data.Brands | default: widget_data.brands
10
+ assign show_container = widget_settings.showContainer
11
+ assign show_bottom_margin = widget_settings.showWidgetBottomMargin
12
+ assign show_widget_title_raw = widget_settings.showWidgetTitle | default: 'Yes'
13
+ if show_widget_title_raw == null or show_widget_title_raw == blank or show_widget_title_raw == 'null'
14
+ assign show_widget_title = true
15
+ else
16
+ if show_widget_title_raw == 'Yes'
17
+ assign show_widget_title = true
18
+ elsif show_widget_title_raw == true
19
+ assign show_widget_title = true
20
+ else
21
+ assign show_widget_title = false
22
+ endif
23
+ endif
24
+ assign widget_title_alignment_raw = widget_settings.widgetTitleAlignment | default: 'center'
25
+ assign widget_title_alignment = widget_title_alignment_raw | downcase
26
+ if widget_title_alignment != 'left' and widget_title_alignment != 'right' and widget_title_alignment != 'center'
27
+ assign widget_title_alignment = 'center'
28
+ endif
29
+ assign background_color = widget_settings.backgroundColor
30
+ assign text_color = widget_settings.textColor
31
+ assign heading = widget_settings.title
32
+ assign subtitle = widget_settings.subtitle
33
+ %}
34
+
35
+ <section class="widget widget-brand" data-widget-id="{{ widget.id }}"{% if background_color and background_color != 'null' %} style="background-color: {{ background_color }};"{% endif %}>
36
+ <div class="widget-brand__inner">
37
+ {% if show_widget_title %}
38
+ {% if heading or subtitle %}
39
+ <header class="widget-header"{% if widget_title_alignment %} style="text-align: {{ widget_title_alignment }};"{% endif %}{% if text_color and text_color != 'null' %} style="color: {{ text_color }};"{% endif %}>
40
+ {% if heading %}<h2 class="widget-title">{{ heading }}</h2>{% endif %}
41
+ {% if subtitle %}<p class="widget-subtitle">{{ subtitle }}</p>{% endif %}
42
+ </header>
43
+ {% endif %}
44
+ {% endif %}
45
+
46
+ {% if brands and brands.size > 0 %}
47
+ <div class="widget-brand__grid">
48
+ {% for brand in brands %}
49
+ {% assign brand_id = brand.Id | default: brand.id | default: brand.brandId %}
50
+ {% assign brand_name = brand.Name | default: brand.name %}
51
+ {% assign brand_slug = brand.Slug | default: brand.slug %}
52
+ {% assign brand_url = brand.Url | default: brand.url | default: brand.link %}
53
+ {% if brand_url == blank %}
54
+ {% if brand_slug and brand_slug != blank %}
55
+ {% assign brand_url = brand_slug %}
56
+ {% else %}
57
+ {% assign brand_url = '#' %}
58
+ {% endif %}
59
+ {% endif %}
60
+
61
+ {% comment %} Image fallback: thumbnailImage.url > Logo > logo > image {% endcomment %}
62
+ {% assign brand_image = nil %}
63
+ {% if brand.thumbnailImage and brand.thumbnailImage.url %}
64
+ {% assign brand_image = brand.thumbnailImage.url %}
65
+ {% elsif brand.ThumbnailImage and brand.ThumbnailImage.Url %}
66
+ {% assign brand_image = brand.ThumbnailImage.Url %}
67
+ {% elsif brand.Logo %}
68
+ {% assign brand_image = brand.Logo %}
69
+ {% elsif brand.logo %}
70
+ {% assign brand_image = brand.logo %}
71
+ {% elsif brand.image %}
72
+ {% assign brand_image = brand.image %}
73
+ {% elsif brand.Image %}
74
+ {% assign brand_image = brand.Image %}
75
+ {% endif %}
76
+
77
+ <a href="{{ brand_url }}" class="widget-brand__item" aria-label="{{ brand_name }}">
78
+ {% if brand_image != blank and brand_image != '' and brand_image != 'null' %}
79
+ {% if brand_image contains 'http://' or brand_image contains 'https://' %}
80
+ {% assign brand_image_src = brand_image %}
81
+ {% else %}
82
+ {% assign brand_image_src = brand_image | asset_url %}
83
+ {% endif %}
84
+ <img src="{{ brand_image_src }}" alt="{{ brand_name }}" width="200" height="60" loading="lazy" onerror="this.style.display='none'; this.nextElementSibling.style.display='block';">
85
+ <span class="widget-brand__name widget-brand__name--fallback" style="display: none;">{{ brand_name }}</span>
86
+ {% else %}
87
+ <span class="widget-brand__name">{{ brand_name }}</span>
88
+ {% endif %}
89
+ </a>
90
+ {% endfor %}
91
+ </div>
92
+ {% else %}
93
+ <div class="widget-empty">
94
+ <p>{{ widget_settings.empty_state | default: 'No brands to display yet.' }}</p>
95
+ </div>
96
+ {% endif %}
97
+ </div>
98
+
99
+ <style>
100
+ .widget-brand {
101
+ {% if show_bottom_margin == 'Yes' %}
102
+ padding: 2.5rem 0;
103
+ {% elsif show_bottom_margin == 'No' %}
104
+ padding: 2.5rem 0 0;
105
+ {% else %}
106
+ padding: 2.5rem 0;
107
+ {% endif %}
108
+ background: {{ background_color | default: widget_settings.background_color | default: 'transparent' }};
109
+ }
110
+ .widget-brand__inner {
111
+ {% if show_container == 'Yes' %}
112
+ max-width: 1400px;
113
+ margin-left: auto;
114
+ margin-right: auto;
115
+ {% endif %}
116
+ padding-left: 24px;
117
+ padding-right: 24px;
118
+ }
119
+
120
+ .widget-brand .widget-header {
121
+ margin-bottom: 32px;
122
+ position: static;
123
+ z-index: auto;
124
+ }
125
+ .widget-brand .widget-title {
126
+ font-size: 1.4rem;
127
+ font-weight: 500;
128
+ line-height: 1.3;
129
+ color: #111;
130
+ margin: 0 0 8px 0;
131
+ letter-spacing: -0.01em;
132
+ }
133
+ .widget-brand .widget-subtitle {
134
+ font-size: 14px;
135
+ color: #6b7280;
136
+ margin: 0;
137
+ }
138
+ .widget-brand__grid {
139
+ display: grid;
140
+ gap: 24px;
141
+ grid-template-columns: repeat(6, 1fr);
142
+ align-items: stretch;
143
+ }
144
+ .widget-brand__item {
145
+ position: relative;
146
+ display: flex;
147
+ align-items: center;
148
+ justify-content: center;
149
+ padding: 12px;
150
+ border-radius: 12px;
151
+ background: #fff;
152
+ box-shadow: 0 4px 12px rgba(15, 23, 42, 0.08);
153
+ min-height: 100px;
154
+ text-decoration: none;
155
+ color: inherit;
156
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
157
+ overflow: hidden;
158
+ }
159
+ .widget-brand__item:hover {
160
+ transform: translateY(-4px);
161
+ box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
162
+ }
163
+ .widget-brand__item img {
164
+ max-width: 100%;
165
+ max-height: 60px;
166
+ width: auto;
167
+ height: auto;
168
+ object-fit: contain;
169
+ }
170
+ .widget-brand__name {
171
+ font-size: 14px;
172
+ font-weight: 600;
173
+ color: #111;
174
+ text-align: center;
175
+ line-height: 1.3;
176
+ word-break: break-word;
177
+ letter-spacing: -0.01em;
178
+ }
179
+ .widget-brand__name--fallback {
180
+ position: absolute;
181
+ top: 50%;
182
+ left: 50%;
183
+ transform: translate(-50%, -50%);
184
+ width: 100%;
185
+ padding: 0 8px;
186
+ }
187
+
188
+ /* Tablet: 4 columns */
189
+ @media (max-width: 1024px) {
190
+ .widget-brand__grid {
191
+ grid-template-columns: repeat(4, 1fr);
192
+ gap: 20px;
193
+ }
194
+ }
195
+
196
+ /* Mobile: 3 columns */
197
+ @media (max-width: 768px) {
198
+ .widget-brand {
199
+ padding: 32px 0;
200
+ }
201
+ .widget-brand__inner {
202
+ padding: 0 16px;
203
+ }
204
+ .widget-brand .widget-header {
205
+ margin-bottom: 24px;
206
+ }
207
+ .widget-brand .widget-title {
208
+ font-size: 1.3rem;
209
+ }
210
+ .widget-brand__grid {
211
+ grid-template-columns: repeat(3, 1fr);
212
+ gap: 16px;
213
+ }
214
+ .widget-brand__item {
215
+ padding: 16px;
216
+ min-height: 80px;
217
+ }
218
+ .widget-brand__item img {
219
+ max-height: 40px;
220
+ }
221
+ }
222
+
223
+ /* Small Mobile: 2 columns */
224
+ @media (max-width: 480px) {
225
+ .widget-brand__inner {
226
+ padding: 0 12px;
227
+ }
228
+ .widget-brand__grid {
229
+ grid-template-columns: repeat(2, 1fr);
230
+ gap: 12px;
231
+ }
232
+ .widget-brand__item {
233
+ padding: 12px;
234
+ min-height: 70px;
235
+ border-radius: 8px;
236
+ }
237
+ .widget-brand__item img {
238
+ max-height: 36px;
239
+ }
240
+ .widget-brand .widget-title {
241
+ font-size: 1.2rem;
242
+ }
243
+ }
244
+ </style>
245
+ </section>