@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.
- package/README.md +425 -0
- package/assets/Logo_o2vend.png +0 -0
- package/assets/favicon.png +0 -0
- package/assets/logo-white.png +0 -0
- package/bin/o2vend +42 -0
- package/config/widget-map.json +50 -0
- package/lib/commands/check.js +201 -0
- package/lib/commands/generate.js +33 -0
- package/lib/commands/init.js +214 -0
- package/lib/commands/optimize.js +216 -0
- package/lib/commands/package.js +208 -0
- package/lib/commands/serve.js +105 -0
- package/lib/commands/validate.js +191 -0
- package/lib/lib/api-client.js +357 -0
- package/lib/lib/dev-server.js +2618 -0
- package/lib/lib/file-watcher.js +80 -0
- package/lib/lib/hot-reload.js +106 -0
- package/lib/lib/liquid-engine.js +822 -0
- package/lib/lib/liquid-filters.js +671 -0
- package/lib/lib/mock-api-server.js +989 -0
- package/lib/lib/mock-data.js +1468 -0
- package/lib/lib/widget-service.js +321 -0
- package/package.json +70 -0
- package/test-theme/README.md +27 -0
- package/test-theme/assets/async-sections.js +446 -0
- package/test-theme/assets/cart-drawer.js +463 -0
- package/test-theme/assets/cart-manager.js +223 -0
- package/test-theme/assets/checkout-price-handler.js +368 -0
- package/test-theme/assets/components.css +4629 -0
- package/test-theme/assets/delivery-zone.css +299 -0
- package/test-theme/assets/delivery-zone.js +396 -0
- package/test-theme/assets/logo.png +0 -0
- package/test-theme/assets/sections.css +48 -0
- package/test-theme/assets/theme.css +3500 -0
- package/test-theme/assets/theme.js +3745 -0
- package/test-theme/config/settings_data.json +292 -0
- package/test-theme/config/settings_schema.json +1050 -0
- package/test-theme/layout/theme.liquid +195 -0
- package/test-theme/locales/en.default.json +260 -0
- package/test-theme/sections/content-fallback.liquid +53 -0
- package/test-theme/sections/content.liquid +57 -0
- package/test-theme/sections/footer-fallback.liquid +328 -0
- package/test-theme/sections/footer.liquid +278 -0
- package/test-theme/sections/header-fallback.liquid +1805 -0
- package/test-theme/sections/header.liquid +1145 -0
- package/test-theme/sections/hero-fallback.liquid +212 -0
- package/test-theme/sections/hero.liquid +136 -0
- package/test-theme/snippets/account-sidebar.liquid +200 -0
- package/test-theme/snippets/add-to-cart-modal.liquid +484 -0
- package/test-theme/snippets/breadcrumbs.liquid +134 -0
- package/test-theme/snippets/cart-drawer.liquid +467 -0
- package/test-theme/snippets/delivery-zone-city-selector.liquid +79 -0
- package/test-theme/snippets/delivery-zone-modal.liquid +337 -0
- package/test-theme/snippets/delivery-zone-search.liquid +78 -0
- package/test-theme/snippets/icon.liquid +105 -0
- package/test-theme/snippets/login-modal.liquid +346 -0
- package/test-theme/snippets/mega-menu.liquid +812 -0
- package/test-theme/snippets/news-thumbnail.liquid +187 -0
- package/test-theme/snippets/pagination.liquid +120 -0
- package/test-theme/snippets/price.liquid +92 -0
- package/test-theme/snippets/product-card-related.liquid +78 -0
- package/test-theme/snippets/product-card-simple.liquid +41 -0
- package/test-theme/snippets/product-card.liquid +697 -0
- package/test-theme/snippets/rating.liquid +85 -0
- package/test-theme/snippets/skeleton-collection-grid.liquid +114 -0
- package/test-theme/snippets/skeleton-product-card.liquid +124 -0
- package/test-theme/snippets/skeleton-product-grid.liquid +34 -0
- package/test-theme/snippets/social-sharing.liquid +185 -0
- package/test-theme/templates/account/dashboard.liquid +401 -0
- package/test-theme/templates/account/loyalty-redemption.liquid +405 -0
- package/test-theme/templates/account/loyalty.liquid +588 -0
- package/test-theme/templates/account/order-detail.liquid +230 -0
- package/test-theme/templates/account/orders.liquid +349 -0
- package/test-theme/templates/account/profile.liquid +758 -0
- package/test-theme/templates/account/register.liquid +232 -0
- package/test-theme/templates/account/return-orders.liquid +348 -0
- package/test-theme/templates/account/store-credit.liquid +464 -0
- package/test-theme/templates/account/subscriptions.liquid +601 -0
- package/test-theme/templates/account/wishlist.liquid +419 -0
- package/test-theme/templates/address-book.liquid +1092 -0
- package/test-theme/templates/categories.liquid +452 -0
- package/test-theme/templates/checkout.liquid +4511 -0
- package/test-theme/templates/error.liquid +384 -0
- package/test-theme/templates/index.liquid +11 -0
- package/test-theme/templates/login.liquid +185 -0
- package/test-theme/templates/order-confirmation.liquid +720 -0
- package/test-theme/templates/page.liquid +297 -0
- package/test-theme/templates/product-detail.liquid +4363 -0
- package/test-theme/templates/products.liquid +518 -0
- package/test-theme/templates/search.liquid +922 -0
- package/test-theme/theme.json.example +19 -0
- package/test-theme/widgets/brand-carousel.liquid +676 -0
- package/test-theme/widgets/brand.liquid +245 -0
- package/test-theme/widgets/carousel.liquid +843 -0
- package/test-theme/widgets/category-list-carousel.liquid +656 -0
- package/test-theme/widgets/category-list.liquid +340 -0
- package/test-theme/widgets/category.liquid +475 -0
- package/test-theme/widgets/discount-time.liquid +176 -0
- package/test-theme/widgets/footer-menu.liquid +695 -0
- package/test-theme/widgets/footer.liquid +179 -0
- package/test-theme/widgets/gallery.liquid +271 -0
- package/test-theme/widgets/header-menu.liquid +932 -0
- package/test-theme/widgets/header.liquid +159 -0
- package/test-theme/widgets/html.liquid +214 -0
- package/test-theme/widgets/news.liquid +217 -0
- package/test-theme/widgets/product-canvas.liquid +235 -0
- package/test-theme/widgets/product-carousel.liquid +502 -0
- package/test-theme/widgets/product.liquid +45 -0
- package/test-theme/widgets/recently-viewed.liquid +26 -0
- package/test-theme/widgets/shared/product-grid.liquid +339 -0
- package/test-theme/widgets/simple-product.liquid +42 -0
- package/test-theme/widgets/single-product.liquid +610 -0
- package/test-theme/widgets/spacebar-carousel.liquid +663 -0
- package/test-theme/widgets/spacebar.liquid +279 -0
- package/test-theme/widgets/splash.liquid +378 -0
- package/test-theme/widgets/testimonial-carousel.liquid +709 -0
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
{% liquid
|
|
2
|
+
assign widget_settings = widget.settings
|
|
3
|
+
assign widget_data = widget.data
|
|
4
|
+
|
|
5
|
+
comment
|
|
6
|
+
Static Widget: Content comes from widget_data.content (array of testimonials)
|
|
7
|
+
Settings come from widget_settings
|
|
8
|
+
endcomment
|
|
9
|
+
|
|
10
|
+
assign content_data = widget_data.content | default: widget.content
|
|
11
|
+
assign testimonials = content_data | default: widget_data.testimonials
|
|
12
|
+
|
|
13
|
+
if content_data.size > 0 and content_data.first.Title
|
|
14
|
+
assign testimonials = content_data
|
|
15
|
+
endif
|
|
16
|
+
|
|
17
|
+
assign heading = widget_settings.title | default: 'What Our Customers Say'
|
|
18
|
+
assign subtitle = widget_settings.subtitle
|
|
19
|
+
assign background_color = widget_settings.backgroundColor
|
|
20
|
+
assign text_color = widget_settings.textColor
|
|
21
|
+
assign hide_dots = widget_settings.hideDot | default: false
|
|
22
|
+
assign hide_arrows = widget_settings.hideArrow | default: false
|
|
23
|
+
assign show_widget_title_raw = widget_settings.showWidgetTitle | default: 'Yes'
|
|
24
|
+
if show_widget_title_raw == null or show_widget_title_raw == blank or show_widget_title_raw == 'null'
|
|
25
|
+
assign show_widget_title = true
|
|
26
|
+
else
|
|
27
|
+
if show_widget_title_raw == 'Yes'
|
|
28
|
+
assign show_widget_title = true
|
|
29
|
+
elsif show_widget_title_raw == true
|
|
30
|
+
assign show_widget_title = true
|
|
31
|
+
else
|
|
32
|
+
assign show_widget_title = false
|
|
33
|
+
endif
|
|
34
|
+
endif
|
|
35
|
+
assign widget_title_alignment_raw = widget_settings.widgetTitleAlignment | default: 'center'
|
|
36
|
+
assign widget_title_alignment = widget_title_alignment_raw | downcase
|
|
37
|
+
if widget_title_alignment != 'left' and widget_title_alignment != 'right' and widget_title_alignment != 'center'
|
|
38
|
+
assign widget_title_alignment = 'center'
|
|
39
|
+
endif
|
|
40
|
+
%}
|
|
41
|
+
|
|
42
|
+
<section class="widget widget-testimonial-carousel" data-widget-id="{{ widget.id }}" data-testimonial-carousel{% if background_color and background_color != blank and background_color != 'null' %} style="background-color: {{ background_color }};"{% endif %}>
|
|
43
|
+
<div class="testimonial-carousel-container">
|
|
44
|
+
{% if show_widget_title %}
|
|
45
|
+
<header class="testimonial-carousel-header">
|
|
46
|
+
<div class="testimonial-carousel-header__text" style="text-align: {{ widget_title_alignment }};">
|
|
47
|
+
{% if heading %}
|
|
48
|
+
<h2 class="testimonial-carousel-title"{% if text_color and text_color != 'null' %} style="color: {{ text_color }};"{% endif %}>{{ heading }}</h2>
|
|
49
|
+
{% endif %}
|
|
50
|
+
{% if subtitle %}
|
|
51
|
+
<p class="testimonial-carousel-subtitle"{% if text_color and text_color != 'null' %} style="color: {{ text_color }};"{% endif %}>{{ subtitle }}</p>
|
|
52
|
+
{% endif %}
|
|
53
|
+
</div>
|
|
54
|
+
{% if testimonials and testimonials.size > 0 and hide_arrows != true %}
|
|
55
|
+
<div class="testimonial-carousel-nav">
|
|
56
|
+
<button type="button" class="testimonial-carousel-arrow testimonial-carousel-arrow--prev" data-carousel-prev aria-label="Previous">
|
|
57
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
58
|
+
<polyline points="15 18 9 12 15 6"></polyline>
|
|
59
|
+
</svg>
|
|
60
|
+
</button>
|
|
61
|
+
<button type="button" class="testimonial-carousel-arrow testimonial-carousel-arrow--next" data-carousel-next aria-label="Next">
|
|
62
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
63
|
+
<polyline points="9 18 15 12 9 6"></polyline>
|
|
64
|
+
</svg>
|
|
65
|
+
</button>
|
|
66
|
+
</div>
|
|
67
|
+
{% endif %}
|
|
68
|
+
</header>
|
|
69
|
+
{% endif %}
|
|
70
|
+
|
|
71
|
+
{% if testimonials and testimonials.size > 0 %}
|
|
72
|
+
<div class="testimonial-carousel-wrapper">
|
|
73
|
+
{% unless hide_arrows == true %}
|
|
74
|
+
<button type="button" class="testimonial-carousel-side-arrow testimonial-carousel-side-arrow--prev" data-carousel-prev aria-label="Previous">
|
|
75
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
76
|
+
<polyline points="15 18 9 12 15 6"></polyline>
|
|
77
|
+
</svg>
|
|
78
|
+
</button>
|
|
79
|
+
{% endunless %}
|
|
80
|
+
|
|
81
|
+
<div class="testimonial-carousel-track-wrapper" data-carousel-viewport>
|
|
82
|
+
<div class="testimonial-carousel-track" data-carousel-track>
|
|
83
|
+
{% for item in testimonials %}
|
|
84
|
+
{% comment %} Extract testimonial data with fallbacks {% endcomment %}
|
|
85
|
+
{% assign item_title = item.Title | default: item.title %}
|
|
86
|
+
{% assign item_description = item.Description | default: item.description | default: item.quote | default: item.text | default: item.Quote | default: item.Text %}
|
|
87
|
+
{% assign person_name = item.PersonName | default: item.personName | default: item.author | default: item.Author | default: item.name | default: item.Name %}
|
|
88
|
+
{% assign company_name = item.CompanyName | default: item.companyName | default: item.company | default: item.Company | default: item.role | default: item.Role | default: item.position | default: item.Position %}
|
|
89
|
+
{% assign item_rating = item.Rating | default: item.rating | default: 5 %}
|
|
90
|
+
{% assign item_icon_html = item.IconHtml | default: item.iconHtml %}
|
|
91
|
+
|
|
92
|
+
{% assign item_image = nil %}
|
|
93
|
+
{% if item.ImageUrl and item.ImageUrl != 'null' and item.ImageUrl != blank %}
|
|
94
|
+
{% assign item_image = item.ImageUrl %}
|
|
95
|
+
{% elsif item.imageUrl and item.imageUrl != 'null' and item.imageUrl != blank %}
|
|
96
|
+
{% assign item_image = item.imageUrl %}
|
|
97
|
+
{% elsif item.Avatar and item.Avatar != 'null' and item.Avatar != blank %}
|
|
98
|
+
{% assign item_image = item.Avatar %}
|
|
99
|
+
{% elsif item.avatar and item.avatar != 'null' and item.avatar != blank %}
|
|
100
|
+
{% assign item_image = item.avatar %}
|
|
101
|
+
{% elsif item.Image and item.Image != 'null' and item.Image != blank %}
|
|
102
|
+
{% assign item_image = item.Image %}
|
|
103
|
+
{% elsif item.image and item.image != 'null' and item.image != blank %}
|
|
104
|
+
{% assign item_image = item.image %}
|
|
105
|
+
{% endif %}
|
|
106
|
+
|
|
107
|
+
<div class="testimonial-carousel-slide" data-slide-index="{{ forloop.index0 }}">
|
|
108
|
+
<blockquote class="testimonial-card">
|
|
109
|
+
<!-- Quote Icon -->
|
|
110
|
+
<div class="testimonial-card__quote-icon">
|
|
111
|
+
{% if item_icon_html and item_icon_html != 'null' and item_icon_html != blank %}
|
|
112
|
+
{{ item_icon_html }}
|
|
113
|
+
{% else %}
|
|
114
|
+
<svg width="32" height="32" viewBox="0 0 24 24" fill="currentColor" opacity="0.15">
|
|
115
|
+
<path d="M14.017 21v-7.391c0-5.704 3.731-9.57 8.983-10.609l.995 2.151c-2.432.917-3.995 3.638-3.995 5.849h4v10h-9.983zm-14.017 0v-7.391c0-5.704 3.748-9.57 9-10.609l.996 2.151c-2.433.917-3.996 3.638-3.996 5.849h3.983v10h-9.983z"/>
|
|
116
|
+
</svg>
|
|
117
|
+
{% endif %}
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<!-- Rating Stars -->
|
|
121
|
+
{% if item_rating and item_rating > 0 %}
|
|
122
|
+
<div class="testimonial-card__rating">
|
|
123
|
+
{% for i in (1..5) %}
|
|
124
|
+
{% if i <= item_rating %}
|
|
125
|
+
<svg class="star star--filled" width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
|
|
126
|
+
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
|
|
127
|
+
</svg>
|
|
128
|
+
{% else %}
|
|
129
|
+
<svg class="star star--empty" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
130
|
+
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
|
|
131
|
+
</svg>
|
|
132
|
+
{% endif %}
|
|
133
|
+
{% endfor %}
|
|
134
|
+
</div>
|
|
135
|
+
{% endif %}
|
|
136
|
+
|
|
137
|
+
<!-- Title -->
|
|
138
|
+
{% if item_title and item_title != blank %}
|
|
139
|
+
<h3 class="testimonial-card__title">{{ item_title }}</h3>
|
|
140
|
+
{% endif %}
|
|
141
|
+
|
|
142
|
+
<!-- Quote/Description -->
|
|
143
|
+
{% if item_description and item_description != blank %}
|
|
144
|
+
<p class="testimonial-card__text">"{{ item_description }}"</p>
|
|
145
|
+
{% endif %}
|
|
146
|
+
|
|
147
|
+
<!-- Author Info -->
|
|
148
|
+
<footer class="testimonial-card__footer">
|
|
149
|
+
{% if item_image and item_image != blank %}
|
|
150
|
+
<div class="testimonial-card__avatar">
|
|
151
|
+
<img src="{{ item_image }}" alt="{{ person_name }}" width="48" height="48" loading="lazy" onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
|
|
152
|
+
<span class="testimonial-card__avatar-fallback" style="display: none;">
|
|
153
|
+
{{ person_name | slice: 0 | upcase }}
|
|
154
|
+
</span>
|
|
155
|
+
</div>
|
|
156
|
+
{% elsif person_name and person_name != blank %}
|
|
157
|
+
<div class="testimonial-card__avatar testimonial-card__avatar--initials">
|
|
158
|
+
<span>{{ person_name | slice: 0 | upcase }}</span>
|
|
159
|
+
</div>
|
|
160
|
+
{% endif %}
|
|
161
|
+
|
|
162
|
+
<div class="testimonial-card__author">
|
|
163
|
+
{% if person_name and person_name != blank %}
|
|
164
|
+
<span class="testimonial-card__name">{{ person_name }}</span>
|
|
165
|
+
{% endif %}
|
|
166
|
+
{% if company_name and company_name != blank %}
|
|
167
|
+
<span class="testimonial-card__company">{{ company_name }}</span>
|
|
168
|
+
{% endif %}
|
|
169
|
+
</div>
|
|
170
|
+
</footer>
|
|
171
|
+
</blockquote>
|
|
172
|
+
</div>
|
|
173
|
+
{% endfor %}
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
{% unless hide_arrows == true %}
|
|
178
|
+
<button type="button" class="testimonial-carousel-side-arrow testimonial-carousel-side-arrow--next" data-carousel-next aria-label="Next">
|
|
179
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
180
|
+
<polyline points="9 18 15 12 9 6"></polyline>
|
|
181
|
+
</svg>
|
|
182
|
+
</button>
|
|
183
|
+
{% endunless %}
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
{% unless hide_dots == true %}
|
|
187
|
+
<div class="testimonial-carousel-dots" data-carousel-dots></div>
|
|
188
|
+
{% endunless %}
|
|
189
|
+
{% else %}
|
|
190
|
+
<div class="widget-empty">
|
|
191
|
+
<p>{{ widget_settings.empty_state | default: 'Testimonials will appear here.' }}</p>
|
|
192
|
+
</div>
|
|
193
|
+
{% endif %}
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<style>
|
|
197
|
+
/* Testimonial Carousel - Modern Design */
|
|
198
|
+
.widget-testimonial-carousel {
|
|
199
|
+
padding: 60px 0;
|
|
200
|
+
overflow: hidden;
|
|
201
|
+
background: linear-gradient(135deg, #fafafa 0%, #f5f5f5 100%);
|
|
202
|
+
border-radius: var(--border-radius-medium);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.widget-testimonial-carousel .testimonial-carousel-container {
|
|
206
|
+
max-width: 1400px;
|
|
207
|
+
margin: 0 auto;
|
|
208
|
+
padding: 0 24px;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/* Header */
|
|
212
|
+
.widget-testimonial-carousel .testimonial-carousel-header {
|
|
213
|
+
display: flex;
|
|
214
|
+
align-items: center;
|
|
215
|
+
justify-content: space-between;
|
|
216
|
+
margin-bottom: 40px;
|
|
217
|
+
gap: 16px;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.widget-testimonial-carousel .testimonial-carousel-header__text {
|
|
221
|
+
flex: 1;
|
|
222
|
+
text-align: center;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.widget-testimonial-carousel .testimonial-carousel-header {
|
|
226
|
+
position: static;
|
|
227
|
+
z-index: auto;
|
|
228
|
+
}
|
|
229
|
+
.widget-testimonial-carousel .testimonial-carousel-title {
|
|
230
|
+
font-size: 1.4rem;
|
|
231
|
+
font-weight: 500;
|
|
232
|
+
line-height: 1.3;
|
|
233
|
+
color: #111;
|
|
234
|
+
margin: 0;
|
|
235
|
+
letter-spacing: -0.01em;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.widget-testimonial-carousel .testimonial-carousel-subtitle {
|
|
239
|
+
font-size: 16px;
|
|
240
|
+
color: #6b7280;
|
|
241
|
+
margin: 8px 0 0 0;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* Header Nav Arrows */
|
|
245
|
+
.widget-testimonial-carousel .testimonial-carousel-nav {
|
|
246
|
+
display: flex;
|
|
247
|
+
gap: 8px;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.widget-testimonial-carousel .testimonial-carousel-arrow {
|
|
251
|
+
width: 44px;
|
|
252
|
+
height: 44px;
|
|
253
|
+
border-radius: 50%;
|
|
254
|
+
border: 1px solid #e0e0e0;
|
|
255
|
+
background: #fff;
|
|
256
|
+
display: flex;
|
|
257
|
+
align-items: center;
|
|
258
|
+
justify-content: center;
|
|
259
|
+
cursor: pointer;
|
|
260
|
+
color: #333;
|
|
261
|
+
transition: all 0.2s ease;
|
|
262
|
+
flex-shrink: 0;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.widget-testimonial-carousel .testimonial-carousel-arrow:hover:not(:disabled) {
|
|
266
|
+
background: #111;
|
|
267
|
+
color: #fff;
|
|
268
|
+
border-color: #111;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.widget-testimonial-carousel .testimonial-carousel-arrow:disabled {
|
|
272
|
+
opacity: 0.3;
|
|
273
|
+
cursor: not-allowed;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* Carousel Wrapper */
|
|
277
|
+
.widget-testimonial-carousel .testimonial-carousel-wrapper {
|
|
278
|
+
position: relative;
|
|
279
|
+
display: flex;
|
|
280
|
+
align-items: center;
|
|
281
|
+
gap: 16px;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/* Side Navigation Arrows */
|
|
285
|
+
.widget-testimonial-carousel .testimonial-carousel-side-arrow {
|
|
286
|
+
display: none;
|
|
287
|
+
width: 48px;
|
|
288
|
+
height: 48px;
|
|
289
|
+
border-radius: 50%;
|
|
290
|
+
border: none;
|
|
291
|
+
background: #fff;
|
|
292
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
|
|
293
|
+
align-items: center;
|
|
294
|
+
justify-content: center;
|
|
295
|
+
cursor: pointer;
|
|
296
|
+
color: #333;
|
|
297
|
+
transition: all 0.2s ease;
|
|
298
|
+
flex-shrink: 0;
|
|
299
|
+
z-index: 10;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.widget-testimonial-carousel .testimonial-carousel-side-arrow:hover:not(:disabled) {
|
|
303
|
+
background: #111;
|
|
304
|
+
color: #fff;
|
|
305
|
+
transform: scale(1.05);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.widget-testimonial-carousel .testimonial-carousel-side-arrow:disabled {
|
|
309
|
+
opacity: 0;
|
|
310
|
+
pointer-events: none;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/* Track */
|
|
314
|
+
.widget-testimonial-carousel .testimonial-carousel-track-wrapper {
|
|
315
|
+
flex: 1;
|
|
316
|
+
overflow: hidden;
|
|
317
|
+
position: relative;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.widget-testimonial-carousel .testimonial-carousel-track {
|
|
321
|
+
display: flex !important;
|
|
322
|
+
gap: 24px;
|
|
323
|
+
overflow-x: auto !important;
|
|
324
|
+
overflow-y: hidden !important;
|
|
325
|
+
scroll-snap-type: x mandatory;
|
|
326
|
+
scroll-behavior: smooth;
|
|
327
|
+
-webkit-overflow-scrolling: touch;
|
|
328
|
+
scrollbar-width: none;
|
|
329
|
+
-ms-overflow-style: none;
|
|
330
|
+
padding: 8px 0;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.widget-testimonial-carousel .testimonial-carousel-track::-webkit-scrollbar {
|
|
334
|
+
display: none;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/* Slides - Desktop: 3 items */
|
|
338
|
+
.widget-testimonial-carousel .testimonial-carousel-slide {
|
|
339
|
+
flex: 0 0 calc(33.333% - 16px);
|
|
340
|
+
min-width: 0;
|
|
341
|
+
max-width: calc(33.333% - 16px);
|
|
342
|
+
scroll-snap-align: start;
|
|
343
|
+
scroll-snap-stop: normal;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/* Testimonial Card */
|
|
347
|
+
.widget-testimonial-carousel .testimonial-card {
|
|
348
|
+
background: #fff;
|
|
349
|
+
border-radius: var(--border-radius-medium);
|
|
350
|
+
padding: 28px;
|
|
351
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
|
|
352
|
+
margin: 0;
|
|
353
|
+
height: 100%;
|
|
354
|
+
display: flex;
|
|
355
|
+
flex-direction: column;
|
|
356
|
+
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.widget-testimonial-carousel .testimonial-card:hover {
|
|
360
|
+
transform: translateY(-4px);
|
|
361
|
+
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.1);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.widget-testimonial-carousel .testimonial-card__quote-icon {
|
|
365
|
+
color: #111;
|
|
366
|
+
margin-bottom: 16px;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.widget-testimonial-carousel .testimonial-card__rating {
|
|
370
|
+
display: flex;
|
|
371
|
+
gap: 2px;
|
|
372
|
+
margin-bottom: 16px;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.widget-testimonial-carousel .testimonial-card__rating .star--filled {
|
|
376
|
+
color: #fbbf24;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.widget-testimonial-carousel .testimonial-card__rating .star--empty {
|
|
380
|
+
color: #d1d5db;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.widget-testimonial-carousel .testimonial-card__title {
|
|
384
|
+
font-size: 18px;
|
|
385
|
+
font-weight: 600;
|
|
386
|
+
color: #111;
|
|
387
|
+
margin: 0 0 12px;
|
|
388
|
+
line-height: 1.3;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.widget-testimonial-carousel .testimonial-card__text {
|
|
392
|
+
font-size: 15px;
|
|
393
|
+
line-height: 1.7;
|
|
394
|
+
color: #4b5563;
|
|
395
|
+
margin: 0;
|
|
396
|
+
flex: 1;
|
|
397
|
+
font-style: italic;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.widget-testimonial-carousel .testimonial-card__footer {
|
|
401
|
+
display: flex;
|
|
402
|
+
align-items: center;
|
|
403
|
+
gap: 12px;
|
|
404
|
+
margin-top: 20px;
|
|
405
|
+
padding-top: 20px;
|
|
406
|
+
border-top: 1px solid #f0f0f0;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.widget-testimonial-carousel .testimonial-card__avatar {
|
|
410
|
+
width: 48px;
|
|
411
|
+
height: 48px;
|
|
412
|
+
border-radius: 50%;
|
|
413
|
+
overflow: hidden;
|
|
414
|
+
flex-shrink: 0;
|
|
415
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.widget-testimonial-carousel .testimonial-card__avatar img {
|
|
419
|
+
width: 100%;
|
|
420
|
+
height: 100%;
|
|
421
|
+
object-fit: cover;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.widget-testimonial-carousel .testimonial-card__avatar--initials,
|
|
425
|
+
.widget-testimonial-carousel .testimonial-card__avatar-fallback {
|
|
426
|
+
display: flex;
|
|
427
|
+
align-items: center;
|
|
428
|
+
justify-content: center;
|
|
429
|
+
color: #fff;
|
|
430
|
+
font-weight: 600;
|
|
431
|
+
font-size: 18px;
|
|
432
|
+
width: 100%;
|
|
433
|
+
height: 100%;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.widget-testimonial-carousel .testimonial-card__author {
|
|
437
|
+
display: flex;
|
|
438
|
+
flex-direction: column;
|
|
439
|
+
gap: 2px;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.widget-testimonial-carousel .testimonial-card__name {
|
|
443
|
+
font-size: 15px;
|
|
444
|
+
font-weight: 600;
|
|
445
|
+
color: #111;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.widget-testimonial-carousel .testimonial-card__company {
|
|
449
|
+
font-size: 13px;
|
|
450
|
+
color: #6b7280;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/* Dots */
|
|
454
|
+
.widget-testimonial-carousel .testimonial-carousel-dots {
|
|
455
|
+
display: flex;
|
|
456
|
+
justify-content: center;
|
|
457
|
+
gap: 8px;
|
|
458
|
+
margin-top: 32px;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.widget-testimonial-carousel .testimonial-carousel-dot {
|
|
462
|
+
width: 8px;
|
|
463
|
+
height: 8px;
|
|
464
|
+
border-radius: 50%;
|
|
465
|
+
background: #d1d5db;
|
|
466
|
+
border: none;
|
|
467
|
+
padding: 0;
|
|
468
|
+
cursor: pointer;
|
|
469
|
+
transition: all 0.2s ease;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.widget-testimonial-carousel .testimonial-carousel-dot:hover {
|
|
473
|
+
background: #9ca3af;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.widget-testimonial-carousel .testimonial-carousel-dot.active {
|
|
477
|
+
background: #111;
|
|
478
|
+
width: 24px;
|
|
479
|
+
border-radius: 4px;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
.widget-testimonial-carousel .widget-empty {
|
|
483
|
+
text-align: center;
|
|
484
|
+
padding: 60px 24px;
|
|
485
|
+
color: #6b7280;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/* Desktop: Show side arrows, hide header arrows */
|
|
489
|
+
@media (min-width: 1025px) {
|
|
490
|
+
.widget-testimonial-carousel .testimonial-carousel-side-arrow {
|
|
491
|
+
display: flex;
|
|
492
|
+
}
|
|
493
|
+
.widget-testimonial-carousel .testimonial-carousel-nav {
|
|
494
|
+
display: none;
|
|
495
|
+
}
|
|
496
|
+
.widget-testimonial-carousel .testimonial-carousel-header__text {
|
|
497
|
+
text-align: left;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/* Tablet: 2 items */
|
|
502
|
+
@media (max-width: 1024px) {
|
|
503
|
+
.widget-testimonial-carousel .testimonial-carousel-slide {
|
|
504
|
+
flex: 0 0 calc(50% - 12px);
|
|
505
|
+
max-width: calc(50% - 12px);
|
|
506
|
+
}
|
|
507
|
+
.widget-testimonial-carousel .testimonial-carousel-track {
|
|
508
|
+
gap: 20px;
|
|
509
|
+
}
|
|
510
|
+
.widget-testimonial-carousel .testimonial-carousel-title {
|
|
511
|
+
font-size: 1.3rem;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/* Mobile: 1 item (with peek) */
|
|
516
|
+
@media (max-width: 768px) {
|
|
517
|
+
.widget-testimonial-carousel {
|
|
518
|
+
padding: 48px 0;
|
|
519
|
+
}
|
|
520
|
+
.widget-testimonial-carousel .testimonial-carousel-container {
|
|
521
|
+
padding: 0 16px;
|
|
522
|
+
}
|
|
523
|
+
.widget-testimonial-carousel .testimonial-carousel-title {
|
|
524
|
+
font-size: 1.3rem;
|
|
525
|
+
}
|
|
526
|
+
.widget-testimonial-carousel .testimonial-carousel-header {
|
|
527
|
+
margin-bottom: 28px;
|
|
528
|
+
flex-direction: column;
|
|
529
|
+
text-align: center;
|
|
530
|
+
}
|
|
531
|
+
.widget-testimonial-carousel .testimonial-carousel-header__text {
|
|
532
|
+
text-align: center;
|
|
533
|
+
}
|
|
534
|
+
.widget-testimonial-carousel .testimonial-carousel-track {
|
|
535
|
+
gap: 16px;
|
|
536
|
+
}
|
|
537
|
+
.widget-testimonial-carousel .testimonial-carousel-slide {
|
|
538
|
+
flex: 0 0 calc(85% - 8px);
|
|
539
|
+
max-width: calc(85% - 8px);
|
|
540
|
+
}
|
|
541
|
+
.widget-testimonial-carousel .testimonial-card {
|
|
542
|
+
padding: 24px;
|
|
543
|
+
}
|
|
544
|
+
.widget-testimonial-carousel .testimonial-carousel-arrow {
|
|
545
|
+
width: 40px;
|
|
546
|
+
height: 40px;
|
|
547
|
+
}
|
|
548
|
+
.widget-testimonial-carousel .testimonial-carousel-dots {
|
|
549
|
+
margin-top: 24px;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/* Small Mobile */
|
|
554
|
+
@media (max-width: 480px) {
|
|
555
|
+
.widget-testimonial-carousel .testimonial-carousel-container {
|
|
556
|
+
padding: 0 12px;
|
|
557
|
+
}
|
|
558
|
+
.widget-testimonial-carousel .testimonial-carousel-slide {
|
|
559
|
+
flex: 0 0 calc(90% - 6px);
|
|
560
|
+
max-width: calc(90% - 6px);
|
|
561
|
+
}
|
|
562
|
+
.widget-testimonial-carousel .testimonial-card {
|
|
563
|
+
padding: 20px;
|
|
564
|
+
}
|
|
565
|
+
.widget-testimonial-carousel .testimonial-card__title {
|
|
566
|
+
font-size: 16px;
|
|
567
|
+
}
|
|
568
|
+
.widget-testimonial-carousel .testimonial-card__text {
|
|
569
|
+
font-size: 14px;
|
|
570
|
+
}
|
|
571
|
+
.widget-testimonial-carousel .testimonial-carousel-title {
|
|
572
|
+
font-size: 1.2rem;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
</style>
|
|
576
|
+
|
|
577
|
+
<script>
|
|
578
|
+
(function() {
|
|
579
|
+
if (document.readyState === 'loading') {
|
|
580
|
+
document.addEventListener('DOMContentLoaded', initTestimonialCarousel);
|
|
581
|
+
} else {
|
|
582
|
+
initTestimonialCarousel();
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function initTestimonialCarousel() {
|
|
586
|
+
const widget = document.querySelector('[data-widget-id="{{ widget.id }}"][data-testimonial-carousel]');
|
|
587
|
+
if (!widget) return;
|
|
588
|
+
|
|
589
|
+
const track = widget.querySelector('[data-carousel-track]');
|
|
590
|
+
const viewport = widget.querySelector('[data-carousel-viewport]');
|
|
591
|
+
const prevBtns = widget.querySelectorAll('[data-carousel-prev]');
|
|
592
|
+
const nextBtns = widget.querySelectorAll('[data-carousel-next]');
|
|
593
|
+
const dotsContainer = widget.querySelector('[data-carousel-dots]');
|
|
594
|
+
const slides = widget.querySelectorAll('.testimonial-carousel-slide');
|
|
595
|
+
|
|
596
|
+
if (!track || slides.length === 0) return;
|
|
597
|
+
|
|
598
|
+
function getItemsPerView() {
|
|
599
|
+
const width = window.innerWidth;
|
|
600
|
+
if (width <= 480) return 1;
|
|
601
|
+
if (width <= 768) return 1;
|
|
602
|
+
if (width <= 1024) return 2;
|
|
603
|
+
return 3;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
function getPageCount() {
|
|
607
|
+
const itemsPerView = getItemsPerView();
|
|
608
|
+
return Math.ceil(slides.length / itemsPerView);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function getCurrentPage() {
|
|
612
|
+
if (!track.scrollWidth || track.scrollWidth <= viewport.clientWidth) return 0;
|
|
613
|
+
const scrollRatio = track.scrollLeft / (track.scrollWidth - viewport.clientWidth);
|
|
614
|
+
return Math.round(scrollRatio * (getPageCount() - 1));
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function createDots() {
|
|
618
|
+
if (!dotsContainer) return;
|
|
619
|
+
dotsContainer.innerHTML = '';
|
|
620
|
+
const pageCount = getPageCount();
|
|
621
|
+
if (pageCount <= 1) return;
|
|
622
|
+
|
|
623
|
+
for (let i = 0; i < pageCount; i++) {
|
|
624
|
+
const dot = document.createElement('button');
|
|
625
|
+
dot.className = 'testimonial-carousel-dot' + (i === 0 ? ' active' : '');
|
|
626
|
+
dot.setAttribute('aria-label', 'Go to page ' + (i + 1));
|
|
627
|
+
dot.addEventListener('click', () => goToPage(i));
|
|
628
|
+
dotsContainer.appendChild(dot);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function updateDots() {
|
|
633
|
+
if (!dotsContainer) return;
|
|
634
|
+
const dots = dotsContainer.querySelectorAll('.testimonial-carousel-dot');
|
|
635
|
+
const currentPage = getCurrentPage();
|
|
636
|
+
dots.forEach((dot, index) => {
|
|
637
|
+
dot.classList.toggle('active', index === currentPage);
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function updateArrows() {
|
|
642
|
+
const atStart = track.scrollLeft <= 5;
|
|
643
|
+
const atEnd = track.scrollLeft >= track.scrollWidth - viewport.clientWidth - 5;
|
|
644
|
+
|
|
645
|
+
prevBtns.forEach(btn => btn.disabled = atStart);
|
|
646
|
+
nextBtns.forEach(btn => btn.disabled = atEnd);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function goToPage(pageIndex) {
|
|
650
|
+
const itemsPerView = getItemsPerView();
|
|
651
|
+
const slide = slides[0];
|
|
652
|
+
if (!slide) return;
|
|
653
|
+
|
|
654
|
+
const gap = parseFloat(getComputedStyle(track).gap) || 24;
|
|
655
|
+
const slideWidth = slide.offsetWidth + gap;
|
|
656
|
+
const scrollTo = pageIndex * itemsPerView * slideWidth;
|
|
657
|
+
|
|
658
|
+
track.scrollTo({ left: scrollTo, behavior: 'smooth' });
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
function scrollByItems(direction) {
|
|
662
|
+
const itemsPerView = getItemsPerView();
|
|
663
|
+
const slide = slides[0];
|
|
664
|
+
if (!slide) return;
|
|
665
|
+
|
|
666
|
+
const gap = parseFloat(getComputedStyle(track).gap) || 24;
|
|
667
|
+
const scrollAmount = (slide.offsetWidth + gap) * Math.max(1, itemsPerView);
|
|
668
|
+
|
|
669
|
+
track.scrollBy({ left: direction * scrollAmount, behavior: 'smooth' });
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
prevBtns.forEach(btn => {
|
|
673
|
+
btn.addEventListener('click', (e) => {
|
|
674
|
+
e.preventDefault();
|
|
675
|
+
scrollByItems(-1);
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
nextBtns.forEach(btn => {
|
|
680
|
+
btn.addEventListener('click', (e) => {
|
|
681
|
+
e.preventDefault();
|
|
682
|
+
scrollByItems(1);
|
|
683
|
+
});
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
let scrollTimeout;
|
|
687
|
+
track.addEventListener('scroll', () => {
|
|
688
|
+
clearTimeout(scrollTimeout);
|
|
689
|
+
scrollTimeout = setTimeout(() => {
|
|
690
|
+
updateDots();
|
|
691
|
+
updateArrows();
|
|
692
|
+
}, 50);
|
|
693
|
+
}, { passive: true });
|
|
694
|
+
|
|
695
|
+
let resizeTimeout;
|
|
696
|
+
window.addEventListener('resize', () => {
|
|
697
|
+
clearTimeout(resizeTimeout);
|
|
698
|
+
resizeTimeout = setTimeout(() => {
|
|
699
|
+
createDots();
|
|
700
|
+
updateArrows();
|
|
701
|
+
}, 200);
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
createDots();
|
|
705
|
+
updateArrows();
|
|
706
|
+
}
|
|
707
|
+
})();
|
|
708
|
+
</script>
|
|
709
|
+
</section>
|