@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,159 @@
1
+ {% comment %}
2
+ Header Widget - HTML Content/Announcement Bar Only
3
+ This widget displays only the HTML content announcement bar.
4
+ The actual header menu (logo, navigation, search, cart) should be handled by HeaderMenuWidget.
5
+ {% endcomment %}
6
+
7
+ {% liquid
8
+ assign widget_settings = widget.settings
9
+ assign widget_data = widget.data
10
+
11
+ comment
12
+ Static Widget: Content comes from widget_data.content (header configuration)
13
+ Settings come from widget_settings
14
+ endcomment
15
+
16
+ assign content_data = widget_data.content | default: widget.content
17
+ if content_data.size > 0
18
+ assign header_config = content_data.first
19
+ else
20
+ assign header_config = content_data
21
+ endif
22
+
23
+ assign html_content_raw = header_config.HtmlContent | default: header_config.htmlContent
24
+ assign html_content = blank
25
+ if html_content_raw and html_content_raw != blank and html_content_raw != 'null'
26
+ assign html_content = html_content_raw
27
+ endif
28
+ assign hide_close_button = widget_settings.hideCloseButton
29
+ if hide_close_button == nil and header_config and header_config.HideCloseButton != nil
30
+ assign hide_close_button = header_config.HideCloseButton
31
+ endif
32
+ assign background_color = widget_settings.backgroundColor
33
+ if background_color == nil and header_config and header_config.BackgroundColor != nil
34
+ assign background_color = header_config.BackgroundColor
35
+ endif
36
+ assign text_color = widget_settings.textColor
37
+ if text_color == nil and header_config and header_config.TextColor != nil
38
+ assign text_color = header_config.TextColor
39
+ endif
40
+ %}
41
+
42
+ {% if html_content and html_content != blank and html_content != 'null' %}
43
+ <div class="widget-header-announcement" data-widget-id="{{ widget.id }}">
44
+ <div class="widget-header-announcement__bar"{% if background_color and background_color != 'null' and background_color != blank %} style="background-color: {{ background_color }};"{% endif %}>
45
+ <div class="widget-header-announcement__content"{% if text_color and text_color != 'null' and text_color != blank %} style="color: {{ text_color }};"{% endif %}>
46
+ {{ html_content }}
47
+ </div>
48
+ {% unless hide_close_button %}
49
+ <button class="widget-header-announcement__close" aria-label="Close announcement" data-announcement-close>
50
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
51
+ <line x1="18" y1="6" x2="6" y2="18"></line>
52
+ <line x1="6" y1="6" x2="18" y2="18"></line>
53
+ </svg>
54
+ </button>
55
+ {% endunless %}
56
+ </div>
57
+ </div>
58
+ {% endif %}
59
+
60
+ <style>
61
+ /* Announcement Bar Styles */
62
+ .widget-header-announcement__bar {
63
+ display: flex;
64
+ align-items: center;
65
+ justify-content: center;
66
+ position: relative;
67
+ padding: 12px 24px;
68
+ background: #111;
69
+ color: #fff;
70
+ text-align: center;
71
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
72
+ }
73
+
74
+ .widget-header-announcement__bar.hidden {
75
+ display: none;
76
+ }
77
+
78
+ .widget-header-announcement__content {
79
+ flex: 1;
80
+ font-size: 14px;
81
+ line-height: 1.5;
82
+ }
83
+
84
+ .widget-header-announcement__content p {
85
+ margin: 0;
86
+ }
87
+
88
+ .widget-header-announcement__content img {
89
+ max-width: 100%;
90
+ height: auto;
91
+ vertical-align: middle;
92
+ }
93
+
94
+ .widget-header-announcement__close {
95
+ position: absolute;
96
+ right: 12px;
97
+ top: 50%;
98
+ transform: translateY(-50%);
99
+ background: transparent;
100
+ border: none;
101
+ color: inherit;
102
+ cursor: pointer;
103
+ padding: 4px;
104
+ display: flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ opacity: 0.7;
108
+ transition: opacity 0.2s ease;
109
+ }
110
+
111
+ .widget-header-announcement__close:hover {
112
+ opacity: 1;
113
+ }
114
+
115
+ .widget-header-announcement__close svg {
116
+ width: 16px;
117
+ height: 16px;
118
+ }
119
+
120
+ @media (max-width: 768px) {
121
+ .widget-header-announcement__bar {
122
+ padding: 10px 40px 10px 16px;
123
+ font-size: 13px;
124
+ }
125
+
126
+ .widget-header-announcement__close {
127
+ right: 8px;
128
+ }
129
+ }
130
+ </style>
131
+
132
+ <script>
133
+ // Handle announcement bar close button
134
+ (function() {
135
+ const announcementCloseBtn = document.querySelector('[data-announcement-close]');
136
+ if (announcementCloseBtn) {
137
+ announcementCloseBtn.addEventListener('click', function() {
138
+ const announcement = this.closest('.widget-header-announcement__bar');
139
+ if (announcement) {
140
+ announcement.classList.add('hidden');
141
+ // Store in sessionStorage to remember it's closed for this session
142
+ const widgetId = announcement.closest('[data-widget-id]')?.getAttribute('data-widget-id');
143
+ if (widgetId) {
144
+ sessionStorage.setItem(`announcement-closed-${widgetId}`, 'true');
145
+ }
146
+ }
147
+ });
148
+
149
+ // Check if announcement was previously closed in this session
150
+ const widgetId = announcementCloseBtn.closest('[data-widget-id]')?.getAttribute('data-widget-id');
151
+ if (widgetId && sessionStorage.getItem(`announcement-closed-${widgetId}`) === 'true') {
152
+ const announcement = announcementCloseBtn.closest('.widget-header-announcement__bar');
153
+ if (announcement) {
154
+ announcement.classList.add('hidden');
155
+ }
156
+ }
157
+ }
158
+ })();
159
+ </script>
@@ -0,0 +1,214 @@
1
+ {% liquid
2
+ assign widget_settings = widget.settings
3
+ assign widget_content = widget.content
4
+ assign widget_data = widget.data
5
+
6
+ assign title = widget_content.Title | default: widget_content.title | default: widget_settings.title
7
+
8
+ comment
9
+ HTML content priority order:
10
+ 1. HtmlContent / htmlContent - direct field from widget (highest priority)
11
+ 2. widget.data.htmlContent - normalized HtmlContent
12
+ 3. widget_content.HtmlContent / htmlContent - from content JSON
13
+ 4. widget_content.Html / html - from content JSON
14
+ 5. Other fallbacks for backward compatibility
15
+ endcomment
16
+
17
+ if widget.htmlContent
18
+ assign html_content = widget.htmlContent
19
+ elsif widget.HtmlContent
20
+ assign html_content = widget.HtmlContent
21
+ elsif widget_data.htmlContent
22
+ assign html_content = widget_data.htmlContent
23
+ elsif widget_content.Html
24
+ assign html_content = widget_content.Html
25
+ elsif widget_content.html
26
+ assign html_content = widget_content.html
27
+ elsif widget_content.HtmlContent
28
+ assign html_content = widget_content.HtmlContent
29
+ elsif widget_content.htmlContent
30
+ assign html_content = widget_content.htmlContent
31
+ elsif widget_content.Body
32
+ assign html_content = widget_content.Body
33
+ elsif widget_content.body
34
+ assign html_content = widget_content.body
35
+ elsif widget_content.Content
36
+ assign html_content = widget_content.Content
37
+ elsif widget_content.content
38
+ assign html_content = widget_content.content
39
+ elsif widget_settings.html
40
+ assign html_content = widget_settings.html
41
+ elsif widget_data.html
42
+ assign html_content = widget_data.html
43
+ else
44
+ assign html_content = ''
45
+ endif
46
+
47
+ assign show_container = widget_content.ShowContainer | default: widget_settings.show_container | default: 'Yes'
48
+ assign show_widget_title_raw = widget_content.ShowWidgetTitle | default: widget_settings.show_widget_title | default: 'Yes'
49
+
50
+ if show_widget_title_raw == null
51
+ assign show_widget_title = true
52
+ elsif show_widget_title_raw == blank
53
+ assign show_widget_title = true
54
+ elsif show_widget_title_raw == 'null'
55
+ assign show_widget_title = true
56
+ elsif show_widget_title_raw == 'Yes'
57
+ assign show_widget_title = true
58
+ elsif show_widget_title_raw == true
59
+ assign show_widget_title = true
60
+ else
61
+ assign show_widget_title = false
62
+ endif
63
+ assign widget_title_alignment_raw = widget_content.WidgetTitleAlignment | default: widget_settings.widget_title_alignment | default: 'center'
64
+ assign widget_title_alignment = widget_title_alignment_raw | downcase
65
+ if widget_title_alignment != 'left' and widget_title_alignment != 'right' and widget_title_alignment != 'center'
66
+ assign widget_title_alignment = 'center'
67
+ endif
68
+ assign background_color = widget_content.BackgroundColor | default: widget_settings.background_color
69
+ assign text_color = widget_content.TextColor | default: widget_settings.text_color
70
+ assign padding_top = widget_content.PaddingTop | default: widget_settings.padding_top
71
+ assign padding_bottom = widget_content.PaddingBottom | default: widget_settings.padding_bottom
72
+ %}
73
+
74
+ <section class="widget widget-html" data-widget-id="{{ widget.id }}"{% if background_color and background_color != blank and background_color != 'null' %} style="background-color: {{ background_color }};"{% endif %}>
75
+ <div class="widget-html__inner{% if show_container == 'No' %} widget-html__inner--full{% endif %}">
76
+ {% if show_widget_title %}
77
+ {% if title and title != blank %}
78
+ <h2 class="widget-title" style="text-align: {{ widget_title_alignment }};{% if text_color and text_color != blank %} color: {{ text_color }};{% endif %}">{{ title }}</h2>
79
+ {% endif %}
80
+ {% endif %}
81
+
82
+ {% if html_content and html_content != blank %}
83
+ <div class="widget-html__content"{% if text_color and text_color != blank and text_color != 'null' %} style="color: {{ text_color }};"{% endif %}>
84
+ {{ html_content }}
85
+ </div>
86
+ {% endif %}
87
+ </div>
88
+
89
+ <style>
90
+ .widget-html {
91
+ padding: {{ padding_top | default: '40' }}px 0 {{ padding_bottom | default: '40' }}px;
92
+ }
93
+ .widget-html__inner {
94
+ max-width: 1400px;
95
+ margin: 0 auto;
96
+ padding: 0 24px;
97
+ }
98
+ .widget-html__inner--full {
99
+ max-width: none;
100
+ padding: 0;
101
+ }
102
+ .widget-html .widget-header {
103
+ position: static;
104
+ z-index: auto;
105
+ }
106
+ .widget-html .widget-title {
107
+ font-size: 1.4rem;
108
+ font-weight: 500;
109
+ line-height: 1.3;
110
+ color: #111;
111
+ margin: 0 0 8px 0;
112
+ letter-spacing: -0.01em;
113
+ }
114
+ .widget-html__content {
115
+ font-size: 16px;
116
+ line-height: 1.7;
117
+ color: #374151;
118
+ }
119
+ .widget-html__content :where(h1, h2, h3, h4, h5, h6) {
120
+ margin-top: 1.5em;
121
+ margin-bottom: 0.5em;
122
+ font-weight: 600;
123
+ line-height: 1.3;
124
+ color: #111;
125
+ }
126
+ .widget-html__content h1 { font-size: 2em; }
127
+ .widget-html__content h2 { font-size: 1.5em; }
128
+ .widget-html__content h3 { font-size: 1.25em; }
129
+ .widget-html__content h4 { font-size: 1.1em; }
130
+ .widget-html__content p {
131
+ margin-bottom: 1em;
132
+ }
133
+ .widget-html__content ul,
134
+ .widget-html__content ol {
135
+ margin: 1em 0;
136
+ padding-left: 1.5em;
137
+ }
138
+ .widget-html__content li {
139
+ margin-bottom: 0.5em;
140
+ }
141
+ .widget-html__content a {
142
+ color: #2563eb;
143
+ text-decoration: underline;
144
+ }
145
+ .widget-html__content a:hover {
146
+ color: #1d4ed8;
147
+ }
148
+ .widget-html__content img {
149
+ max-width: 100%;
150
+ height: auto;
151
+ border-radius: 8px;
152
+ }
153
+ .widget-html__content table {
154
+ width: 100%;
155
+ border-collapse: collapse;
156
+ margin: 1em 0;
157
+ }
158
+ .widget-html__content th,
159
+ .widget-html__content td {
160
+ padding: 12px;
161
+ border: 1px solid #e5e7eb;
162
+ text-align: left;
163
+ }
164
+ .widget-html__content th {
165
+ background: #f9fafb;
166
+ font-weight: 600;
167
+ }
168
+ .widget-html__content blockquote {
169
+ margin: 1.5em 0;
170
+ padding: 1em 1.5em;
171
+ border-left: 4px solid #e5e7eb;
172
+ background: #f9fafb;
173
+ font-style: italic;
174
+ }
175
+ .widget-html__content pre,
176
+ .widget-html__content code {
177
+ font-family: 'Monaco', 'Consolas', monospace;
178
+ font-size: 0.9em;
179
+ }
180
+ .widget-html__content pre {
181
+ background: #1f2937;
182
+ color: #f9fafb;
183
+ padding: 1em;
184
+ border-radius: 8px;
185
+ overflow-x: auto;
186
+ }
187
+ .widget-html__content code {
188
+ background: #f3f4f6;
189
+ padding: 0.2em 0.4em;
190
+ border-radius: 4px;
191
+ }
192
+ .widget-html__content pre code {
193
+ background: none;
194
+ padding: 0;
195
+ }
196
+
197
+ /* Mobile */
198
+ @media (max-width: 768px) {
199
+ .widget-html {
200
+ padding: 32px 0;
201
+ }
202
+ .widget-html__inner {
203
+ padding: 0 16px;
204
+ }
205
+ .widget-html .widget-title {
206
+ font-size: 1.3rem;
207
+ }
208
+ .widget-html__content {
209
+ font-size: 15px;
210
+ }
211
+ }
212
+ </style>
213
+ </section>
214
+
@@ -0,0 +1,217 @@
1
+ {% liquid
2
+ assign widget_settings = widget.settings
3
+ assign widget_content = widget.content
4
+ assign widget_data = widget.data
5
+
6
+ comment
7
+ Dynamic Widget: News items come from widget_data.products (enriched by WidgetService)
8
+ Settings come from widget_settings
9
+ endcomment
10
+ assign news_items = widget_data.products | default: widget_data.items | default: widget_data.newsItems
11
+ assign heading = widget_settings.title | default: widget.Title | default: widget_content.ProductName
12
+
13
+ assign show_container = widget_content.ShowContainer | default: widget_settings.showContainer | default: 'Default'
14
+ assign show_widget_title_raw = widget_content.ShowWidgetTitle | default: widget_settings.showWidgetTitle | default: 'Default'
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
+ elsif show_widget_title_raw == 'Yes' or show_widget_title_raw == 'Default'
18
+ assign show_widget_title = true
19
+ else
20
+ assign show_widget_title = false
21
+ endif
22
+
23
+ assign widget_title_alignment_raw = widget_content.WidgetTitleAlignment | default: widget_settings.widgetTitleAlignment | default: 'Default'
24
+ assign alignment = 'center'
25
+ if widget_title_alignment_raw == 'Left' or widget_title_alignment_raw == 'left'
26
+ assign alignment = 'left'
27
+ elsif widget_title_alignment_raw == 'Right' or widget_title_alignment_raw == 'right'
28
+ assign alignment = 'right'
29
+ elsif widget_title_alignment_raw == 'Center' or widget_title_alignment_raw == 'Center' or widget_title_alignment_raw == 'Default' or widget_title_alignment_raw == 'default'
30
+ assign alignment = 'center'
31
+ endif
32
+
33
+ assign background_color = widget_content.BackgroundColor | default: widget_settings.backgroundColor
34
+ assign text_color = widget_content.TextColor | default: widget_settings.textColor
35
+ assign show_grid_view = widget_content.ShowGridView | default: widget_settings.showGridView | default: true
36
+
37
+ comment
38
+ Container logic based on section and ShowContainer:
39
+ - section == "hero" + ShowContainer == "No" or ("Default"/null in hero) → no container
40
+ - section != "hero" + ShowContainer == ("Default"/null) or ShowContainer == "Yes" → with container
41
+ - ShowContainer == "No" → no container
42
+ - ShowContainer == "Yes" → with container
43
+ endcomment
44
+ assign widget_section = widget.section | default: 'content'
45
+ assign use_container = true
46
+
47
+ if show_container == 'No'
48
+ assign use_container = false
49
+ elsif show_container == 'Yes'
50
+ assign use_container = true
51
+ else
52
+ if widget_section == 'hero'
53
+ if show_container == 'Default' or show_container == null or show_container == 'null' or show_container == blank
54
+ assign use_container = false
55
+ else
56
+ assign use_container = true
57
+ endif
58
+ else
59
+ assign use_container = true
60
+ endif
61
+ endif
62
+ %}
63
+
64
+ {% if news_items and news_items.size > 0 %}
65
+ {% if use_container %}
66
+ <section class="widget widget-news" data-widget-id="{{ widget.id }}"{% if background_color and background_color != 'null' and background_color != blank %} style="background-color: {{ background_color }}; color: {{ text_color }};"{% endif %}>
67
+ <div class="container">
68
+ {% if show_widget_title and heading and heading != blank %}
69
+ <h2 class="widget-news__title" style="text-align: {{ alignment }}; color: {{ text_color }}; padding-top: 2%;">{{ heading }}</h2>
70
+ {% endif %}
71
+
72
+ <div class="row product-list widget-news__list">
73
+ {% for item in news_items %}
74
+ {% if show_grid_view %}
75
+ <div class="col-6 col-md-3 p-b-35 p-l-5 p-r-5">
76
+ {% render 'snippets/news-thumbnail', news_item: item %}
77
+ </div>
78
+ {% else %}
79
+ <div class="col-md-3 p-b-35 p-l-5 p-r-5">
80
+ {% render 'snippets/news-thumbnail', news_item: item %}
81
+ </div>
82
+ {% endif %}
83
+ {% endfor %}
84
+ </div>
85
+ </div>
86
+ </section>
87
+ {% else %}
88
+ <div class="widget widget-news" data-widget-id="{{ widget.id }}"{% if background_color and background_color != 'null' and background_color != blank %} style="background-color: {{ background_color }}; color: {{ text_color }};"{% endif %}>
89
+ {% if show_widget_title and heading and heading != blank %}
90
+ <h2 class="widget-news__title" style="text-align: {{ alignment }}; color: {{ text_color }}; padding-top: 2%;">{{ heading }}</h2>
91
+ {% endif %}
92
+
93
+ <div class="row product-list widget-news__list">
94
+ {% for item in news_items %}
95
+ {% if show_grid_view %}
96
+ <div class="col-6 col-md-3 p-b-35 p-l-5 p-r-5">
97
+ {% render 'snippets/news-thumbnail', news_item: item %}
98
+ </div>
99
+ {% else %}
100
+ <div class="col-md-3 p-b-35 p-l-5 p-r-5">
101
+ {% render 'snippets/news-thumbnail', news_item: item %}
102
+ </div>
103
+ {% endif %}
104
+ {% endfor %}
105
+ </div>
106
+ </div>
107
+ {% endif %}
108
+ {% else %}
109
+ <section class="widget widget-news widget-news--empty" data-widget-id="{{ widget.id }}">
110
+ <div class="widget-empty">
111
+ <p>No news items available.</p>
112
+ </div>
113
+ </section>
114
+ {% endif %}
115
+
116
+ <style>
117
+ .widget-news {
118
+ padding: 40px 0;
119
+ }
120
+
121
+ .widget-news__title {
122
+ font-size: 1.4rem;
123
+ font-weight: 500;
124
+ line-height: 1.3;
125
+ margin: 0 0 32px 0;
126
+ }
127
+
128
+ .widget-news__list {
129
+ display: flex;
130
+ flex-wrap: wrap;
131
+ margin: 0 -5px;
132
+ }
133
+
134
+ .widget-news .row {
135
+ display: flex;
136
+ flex-wrap: wrap;
137
+ margin: 0 -5px;
138
+ }
139
+
140
+ .widget-news .col-6 {
141
+ flex: 0 0 50%;
142
+ max-width: 50%;
143
+ padding: 0 5px;
144
+ }
145
+
146
+ .widget-news .col-md-3 {
147
+ flex: 0 0 100%;
148
+ max-width: 100%;
149
+ padding: 0 5px;
150
+ }
151
+
152
+ @media (min-width: 768px) {
153
+ .widget-news .col-md-3 {
154
+ flex: 0 0 25%;
155
+ max-width: 25%;
156
+ }
157
+ }
158
+
159
+ .widget-news .p-b-35 {
160
+ padding-bottom: 35px;
161
+ }
162
+
163
+ .widget-news .p-l-5 {
164
+ padding-left: 5px;
165
+ }
166
+
167
+ .widget-news .p-r-5 {
168
+ padding-right: 5px;
169
+ }
170
+
171
+ .widget-news .container {
172
+ max-width: 1400px;
173
+ margin: 0 auto;
174
+ padding: 0 24px;
175
+ }
176
+
177
+ .widget-empty {
178
+ padding: 60px 24px;
179
+ text-align: center;
180
+ color: #6b7280;
181
+ }
182
+
183
+ .widget-empty p {
184
+ margin: 0;
185
+ font-size: 14px;
186
+ }
187
+
188
+ @media (max-width: 768px) {
189
+ .widget-news {
190
+ padding: 32px 0;
191
+ }
192
+
193
+ .widget-news__title {
194
+ font-size: 1.3rem;
195
+ margin-bottom: 24px;
196
+ }
197
+
198
+ .widget-news .container {
199
+ padding: 0 16px;
200
+ }
201
+ }
202
+
203
+ @media (max-width: 480px) {
204
+ .widget-news {
205
+ padding: 24px 0;
206
+ }
207
+
208
+ .widget-news__title {
209
+ font-size: 1.2rem;
210
+ margin-bottom: 20px;
211
+ }
212
+
213
+ .widget-news .container {
214
+ padding: 0 12px;
215
+ }
216
+ }
217
+ </style>