zz-shopify-components 0.25.0 → 0.25.1-beta.0

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.
Binary file
@@ -419,7 +419,8 @@ body.zz-dialog-open {
419
419
  position: fixed;
420
420
  inset: 0;
421
421
  display: grid;
422
- place-items: center;
422
+ place-items: start center;
423
+ padding-top: 20%;
423
424
  pointer-events: none;
424
425
  /* 不阻挡点击 */
425
426
  z-index: 9999;
@@ -0,0 +1,36 @@
1
+ {% schema %}
2
+ {
3
+ "name": "Compare Tab Item",
4
+ "settings": [
5
+ {
6
+ "type": "text",
7
+ "id": "title",
8
+ "label": "Tab标题"
9
+ },
10
+ {
11
+ "type": "image_picker",
12
+ "id": "image",
13
+ "label": "图片"
14
+ },
15
+ ],
16
+ "presets": [
17
+ {
18
+ "name": "Compare Tab Item",
19
+ "settings": {
20
+ }
21
+ }
22
+ ]
23
+ }
24
+ {% endschema %}
25
+
26
+ <input
27
+ type="radio"
28
+ name="zz-bundle-compare-item-{{ block.id }}"
29
+ role="tab"
30
+ class="tw-daisy-tab"
31
+ aria-label= {{ block.settings.title }}
32
+ checked="checked" />
33
+ <div role="tabpanel" class="tw-daisy-tab-content tw-w-full tw-p-10">
34
+
35
+ <img src="{{ block.settings.image | image_url }}" alt="{{ block.settings.title }}" class="tw-w-full tw-h-full" />
36
+ </div>
@@ -0,0 +1,33 @@
1
+ {% schema %}
2
+ {
3
+ "name": "Bundle Compare",
4
+ "class": "zz-bundle-compare",
5
+ "settings": [
6
+ {
7
+ "type": "image_picker",
8
+ "id": "pc_image",
9
+ "label": "PC端图片"
10
+ },
11
+ ],
12
+ "blocks": [
13
+ {
14
+ "type": "zz-bundle-compare-item",
15
+ }
16
+ ],
17
+ "presets": [
18
+ {
19
+ "name": "Bundle Compare",
20
+ "settings": {
21
+ }
22
+ }
23
+ ]
24
+ }
25
+ {% endschema %}
26
+ {% comment %} <div class="zz-bundle-compare-container">
27
+ {% content_for 'block', block %}
28
+ </div> {% endcomment %}
29
+
30
+ <div role="tablist" class="tw-daisy-tabs tw-daisy-tabs-bordered">
31
+ {% content_for 'blocks' %}
32
+ </div>
33
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zz-shopify-components",
3
- "version": "0.25.0",
3
+ "version": "0.25.1-beta.0",
4
4
  "description": "Reusable Shopify components for theme projects",
5
5
  "keywords": [
6
6
  "shopify",
@@ -0,0 +1,327 @@
1
+ <zz-accessories-preview-swiper class=' tw-w-full product-accessories-swiper-container tw-h-full tw-relative'>
2
+ <div class='swiper product-accessories-swiper-list tw-w-full tw-h-full tw-relative'>
3
+ <div class='swiper-wrapper'>
4
+ {% for image in product.metafields.custom.acc_images.value %}
5
+ {% assign accessories_points_index = forloop.index %}
6
+ <div class='swiper-slide tw-flex tw-justify-center tw-relative'>
7
+ {{
8
+ image
9
+ | image_url: width: 3000
10
+ | image_tag:
11
+ loading: 'lazy',
12
+ class: 'tw-w-auto tw-h-full tw-max-w-full tw-object-cover tw-m-auto',
13
+ sizes: '(min-width: 750px) 700px, calc(100vw - 30px)',
14
+ widths: '3000, 4000, 5000'
15
+ }}
16
+ {% for block in accessories_points %}
17
+ {% if block.settings.swiper_index == accessories_points_index %}
18
+ <div
19
+ onclick='window.accessoriesPointClickHandler(event, {{ block.settings.accessories_product.selected_or_first_available_variant.id }})'
20
+ data-point-swiper-index='{{ block.settings.swiper_index }}'
21
+ class='product-accessories-point-item tw-absolute tw-rounded-full tw-backdrop-blur-[20px] tw-gap-[12px] tw-flex tw-items-center tw-justify-center tw-bg-[rgba(255,255,255,0.8)] lg:tw-py-[6px] tw-py-[4px] lg:tw-pl-[8px] tw-pl-[4px] tw-pr-[20px] tw-z-50 tw-cursor-pointer'
22
+ style='left: {{ block.settings.point_x }}%; top: {{ block.settings.point_y }}%;'
23
+ >
24
+ <svg
25
+ width='36'
26
+ height='36'
27
+ viewBox='0 0 36 36'
28
+ fill='none'
29
+ xmlns='http://www.w3.org/2000/svg'
30
+ class="tw-block buy-icon tw-w-[36px] tw-h-[36px]"
31
+ >
32
+ <rect width="36" height="36" rx="18" fill="#FC6C0F"/>
33
+ <path d="M8 10H9.51521C10.2098 10 10.8135 10.4769 10.9744 11.1526L11.4143 13M11.4143 13L13.3341 21.0632C13.5486 21.9642 14.3536 22.6 15.2797 22.6H23.786C24.7278 22.6 25.542 21.943 25.7409 21.0225L26.9511 15.4225C27.2204 14.1765 26.2709 13 24.9962 13H11.4143Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
34
+ <circle cx="24.584" cy="26" r="0.6" fill="white" stroke="white" stroke-width="0.8"/>
35
+ <circle cx="14.916" cy="26" r="0.6" fill="white" stroke="white" stroke-width="0.8"/>
36
+ <path d="M17.4746 17.7998H21.4746" stroke="white" stroke-width="1.3" stroke-linecap="round"/>
37
+ <path d="M19.4746 19.7998V15.7998" stroke="white" stroke-width="1.3" stroke-linecap="round"/>
38
+ </svg>
39
+
40
+ <svg
41
+ width='30'
42
+ height='30'
43
+ viewBox='0 0 30 30'
44
+ fill='none'
45
+ xmlns='http://www.w3.org/2000/svg'
46
+ class="tw-hidden buy-done-icon tw-w-[36px] tw-h-[36px]"
47
+ >
48
+ <rect width="30" height="30" rx="15" fill="#FC6C0F"/>
49
+ <rect x="1" y="1" width="28" height="28" rx="14" stroke="white" stroke-opacity="0.3" stroke-width="2"/>
50
+ <path d="M10.4168 15.0586L13.9523 18.5941L19.8449 12.7016" stroke="white" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
51
+ </svg>
52
+
53
+ <div class=' tw-flex tw-flex-col '>
54
+ <div class='lg:tw-text-[16px] tw-text-[12px] tw-leading-[1.2] tw-font-bold'>
55
+ {{
56
+ block.settings.accessories_title
57
+ | default: block.settings.accessories_product.selected_or_first_available_variant.title
58
+ }}
59
+ </div>
60
+ <div class='lg:tw-text-[14px] tw-text-[12px] tw-leading-[1.5] tw-font-medium'>
61
+ {{
62
+ block.settings.accessories_product.selected_or_first_available_variant.price
63
+ | money_without_trailing_zeros
64
+ }}
65
+ </div>
66
+ </div>
67
+ </div>
68
+ {% endif %}
69
+ {% endfor %}
70
+ </div>
71
+ {% endfor %}
72
+ </div>
73
+ <div class='swiper-pagination'></div>
74
+ <div class='product-accessories-swiper-button-prev'>
75
+ {% render 'zz-prev-next-btn', type: 'prev', color_type: 'dark' %}
76
+ </div>
77
+ <div class='product-accessories-swiper-button-next'>
78
+ {% render 'zz-prev-next-btn', type: 'next', color_type: 'dark' %}
79
+ </div>
80
+ </div>
81
+
82
+ <div class=' tw-absolute tw-bottom-[20px] tw-left-1/2 tw-transform tw-translate-x-[-50%] tw-z-10 tw-w-[80%] '>
83
+ <div class='swiper product-accessories-swiper-list-thumbs tw-opacity-0'>
84
+ <div class='swiper-wrapper tw-flex tw-justify-center'>
85
+ {% for image in product.metafields.custom.acc_images.value %}
86
+ <div class='swiper-slide !tw-w-auto tw-opacity-30 tw-relative'>
87
+ {{
88
+ image
89
+ | image_url: width: 200
90
+ | image_tag:
91
+ loading: 'lazy',
92
+ class: 'lg:tw-w-[40px] tw-w-[30px] tw-aspect-square tw-object-cover tw-rounded-[6px] ',
93
+ sizes: '(min-width: 750px) 50px, calc(100vw - 30px)',
94
+ widths: '50, 100, 150'
95
+ }}
96
+ </div>
97
+ {% endfor %}
98
+ </div>
99
+ </div>
100
+ </div>
101
+ </zz-accessories-preview-swiper>
102
+
103
+ <style>
104
+ zz-accessories-preview-swiper {
105
+ display: block;
106
+ }
107
+ .product-accessories-swiper-container .swiper-pagination {
108
+ --swiper-pagination-color: black;
109
+ --swiper-pagination-bottom: 10px;
110
+ }
111
+ .product-accessories-swiper-container .swiper-pagination,
112
+ .product-accessories-swiper-list-thumbs {
113
+ transition: opacity 0.2s ease-in-out;
114
+ }
115
+ .product-accessories-swiper-list-thumbs {
116
+ transform: translateY(12px) scale(0.96);
117
+ transform-origin: bottom center;
118
+ will-change: opacity, transform;
119
+ transition:
120
+ opacity 0.25s ease,
121
+ transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
122
+ }
123
+ .product-accessories-swiper-container:hover
124
+ .product-accessories-swiper-list-thumbs {
125
+ opacity: 1;
126
+ transform: translateY(0) scale(1);
127
+ }
128
+ .product-accessories-swiper-container:hover .swiper-pagination {
129
+ display: none;
130
+ transition: none;
131
+ }
132
+
133
+ .product-accessories-swiper-list-thumbs .swiper-slide {
134
+ border: 1px solid black;
135
+ border-radius: 6px;
136
+ }
137
+
138
+ .product-accessories-swiper-list-thumbs .swiper-slide-thumb-active {
139
+ opacity: 1;
140
+ border: 1px solid black;
141
+ border-radius: 6px;
142
+ }
143
+ .product-accessories-swiper-button-prev,
144
+ .product-accessories-swiper-button-next {
145
+ position: absolute;
146
+ top: 50%;
147
+ transform: translateY(-50%);
148
+ z-index: 10;
149
+ cursor: pointer;
150
+ pointer-events: auto;
151
+ }
152
+ @media screen and (max-width: 1024px) {
153
+ .product-accessories-swiper-button-prev {
154
+ display: none;
155
+ }
156
+ .product-accessories-swiper-button-next {
157
+ display: none;
158
+ }
159
+ }
160
+ .product-accessories-swiper-button-prev {
161
+ left: 20px;
162
+ }
163
+ .product-accessories-swiper-button-next {
164
+ right: 20px;
165
+ }
166
+ </style>
167
+ <script>
168
+ ;(() => {
169
+ class ZZAccessoriesPreviewSwiper extends HTMLElement {
170
+ constructor() {
171
+ super();
172
+ this._thumbs = null;
173
+ this._swiper = null;
174
+ this._listEl = null;
175
+ this._thumbsEl = null;
176
+ this._paginationEl = null;
177
+ this._btnPrev = null;
178
+ this._btnNext = null;
179
+ this._initialized = false;
180
+ }
181
+
182
+ connectedCallback() {
183
+ const init = () => {
184
+ if (this._initialized) return;
185
+ if (typeof Swiper === 'undefined') return;
186
+ this._ensureElements();
187
+ this._initSwipers();
188
+ this._initialized = true;
189
+ };
190
+
191
+ if (document.readyState === 'loading') {
192
+ document.addEventListener('DOMContentLoaded', init, { once: true });
193
+ } else {
194
+ init();
195
+ }
196
+ }
197
+
198
+ disconnectedCallback() {
199
+ this._destroySwipers();
200
+ this._initialized = false;
201
+ }
202
+
203
+ _ensureElements() {
204
+ this._listEl = this.querySelector('.product-accessories-swiper-list');
205
+ this._thumbsEl = this.querySelector('.product-accessories-swiper-list-thumbs');
206
+ if (!this._listEl || !this._thumbsEl) return;
207
+ this._paginationEl = this._listEl.querySelector('.swiper-pagination');
208
+ this._btnPrev = this._listEl.querySelector('.product-accessories-swiper-button-prev');
209
+ this._btnNext = this._listEl.querySelector('.product-accessories-swiper-button-next');
210
+ }
211
+
212
+ _initSwipers() {
213
+ if (!this._listEl || !this._thumbsEl) return;
214
+
215
+ this._thumbs = new Swiper(this._thumbsEl, {
216
+ slidesPerView: 5,
217
+ spaceBetween: 8,
218
+ watchSlidesProgress: true,
219
+ slideToClickedSlide: true,
220
+ breakpoints: {
221
+ 0: { slidesPerView: 4, spaceBetween: 6 },
222
+ 768: { slidesPerView: 5, spaceBetween: 8 },
223
+ 1024: { slidesPerView: 6, spaceBetween: 10 },
224
+ },
225
+ });
226
+
227
+ const loopedSlides = {{ product.metafields.custom.acc_images.value.size | default: 0 }};
228
+ this._swiper = new Swiper(this._listEl, {
229
+ loop: true,
230
+ navigation: {
231
+ nextEl: this._btnNext,
232
+ prevEl: this._btnPrev,
233
+ },
234
+ effect: 'fade',
235
+ loopedSlides: loopedSlides,
236
+ slidesPerView: 1,
237
+ spaceBetween: 10,
238
+ pagination: {
239
+ el: this._paginationEl,
240
+ clickable: true,
241
+ },
242
+ thumbs: { swiper: this._thumbs },
243
+ });
244
+ }
245
+
246
+ _destroySwipers() {
247
+ try {
248
+ this._swiper && this._swiper.destroy(true, true);
249
+ } catch (e) {}
250
+ try {
251
+ this._thumbs && this._thumbs.destroy(true, true);
252
+ } catch (e) {}
253
+ this._swiper = null;
254
+ this._thumbs = null;
255
+ }
256
+ }
257
+
258
+ if (!customElements.get('zz-accessories-preview-swiper')) {
259
+ customElements.define('zz-accessories-preview-swiper', ZZAccessoriesPreviewSwiper);
260
+ }
261
+
262
+ window.accessoriesPointClickHandler = async function(eventOrElement, variantId) {
263
+ const element =
264
+ eventOrElement?.currentTarget ||
265
+ (eventOrElement instanceof Event ? eventOrElement.target : eventOrElement);
266
+
267
+ if (!(element instanceof Element)) {
268
+ console.warn('accessoriesPointClickHandler: expected an Element but got', eventOrElement);
269
+ return;
270
+ }
271
+
272
+ const buyIcon = element.querySelector('.buy-icon');
273
+ const buyDoneIcon = element.querySelector('.buy-done-icon');
274
+ if (buyIcon) buyIcon.style.display = 'none';
275
+ if (buyDoneIcon) buyDoneIcon.style.display = 'block';
276
+ const data = {
277
+ items: [
278
+ {
279
+ id: variantId,
280
+ quantity: 1,
281
+ },
282
+ ],
283
+ sections: 'cart-drawer,cart-icon-bubble',
284
+ };
285
+ const cart = document.querySelector('cart-drawer');
286
+
287
+ await fetch(`${routes.cart_add_url}`, {
288
+ method: 'POST',
289
+ headers: {
290
+ 'Content-Type': 'application/json',
291
+ Accept: 'application/javascript',
292
+ },
293
+ body: JSON.stringify(data),
294
+ })
295
+ .then((response) => response.json())
296
+ .then((response) => {
297
+ if (response.status) {
298
+ publish(PUB_SUB_EVENTS.cartError, {
299
+ source: 'product-form',
300
+ productVariantId: variantId,
301
+ errors: response.errors || response.description,
302
+ message: response.message,
303
+ });
304
+ }
305
+
306
+ publish(PUB_SUB_EVENTS.cartUpdate, {
307
+ source: 'product-form',
308
+ productVariantId: variantId,
309
+ cartData: response,
310
+ });
311
+ cart.renderContents(response, false);
312
+ zzShowToast('1 item added to Cart', { type: 'success' });
313
+ })
314
+ .catch((e) => {
315
+ console.error(e);
316
+ })
317
+ .finally(() => {
318
+ setTimeout(() => {
319
+ if (buyIcon) buyIcon.style.display = 'block';
320
+ if (buyDoneIcon) buyDoneIcon.style.display = 'none';
321
+ }, 3000);
322
+ if (cart && cart.classList.contains('is-empty'))
323
+ cart.classList.remove('is-empty');
324
+ });
325
+ };
326
+ })();
327
+ </script>
@@ -0,0 +1,257 @@
1
+ <gallery-tab>
2
+ <div class='gallery-tab-container lg:tw-mt-[20px] tw-mt-[12px] tw-absolute bottom-0 left-0 tw-w-full tw-z-10 tw-text-center tw-flex tw-items-center tw-justify-center lg:tw-gap-[12px] tw-gap-[4px]'>
3
+ <div
4
+ class='gallery-tab-item active'
5
+ data-test-locator='galleryTabItemPhoto'
6
+ data-test-value='photo'
7
+ >
8
+ <div class='gallery-tab-item-text'>
9
+ <svg
10
+ xmlns='http://www.w3.org/2000/svg'
11
+ width='16'
12
+ height='16'
13
+ viewBox='0 0 16 16'
14
+ fill='none'
15
+ >
16
+ <path d="M1 4.5V12.5C1 13.0523 1.44772 13.5 2 13.5H14" stroke="black" stroke-linecap="round" stroke-linejoin="round"/>
17
+ <rect x="3" y="3.5" width="12" height="8" rx="0.5" stroke="black"/>
18
+ <path d="M3 11L6 8L8 9.5L10.5 7L14.5 11" stroke="black" stroke-linejoin="round"/>
19
+ <circle cx="5.75" cy="5.75" r="0.75" fill="black"/>
20
+ </svg>
21
+ <span class='gallery-tab-item-text-span'> Photo </span>
22
+ </div>
23
+ </div>
24
+ {% if show_accessories_point %}
25
+ <div
26
+ class='gallery-tab-item'
27
+ data-test-locator='galleryTabItemAccessories'
28
+ data-test-value='accessories'
29
+ >
30
+ <div class='gallery-tab-item-text'>
31
+ <svg
32
+ width='20'
33
+ height='20'
34
+ viewBox='0 0 20 20'
35
+ fill='none'
36
+ xmlns='http://www.w3.org/2000/svg'
37
+ >
38
+ <path d="M6.88574 3.83398H13.1035C13.9019 3.83401 14.5757 4.42847 14.6748 5.2207L15.8027 14.249C15.9207 15.1938 15.1845 16.028 14.2324 16.0283H5.75781C4.80554 16.0283 4.06858 15.1939 4.18652 14.249L5.31445 5.2207C5.41357 4.4285 6.08736 3.83406 6.88574 3.83398Z" stroke="black"/>
39
+ <path d="M12.7724 7.15332C12.7724 8.68744 11.5287 9.9311 9.99457 9.9311C8.46045 9.9311 7.2168 8.68744 7.2168 7.15332" stroke="black" stroke-linecap="round"/>
40
+ </svg>
41
+
42
+ <span class='gallery-tab-item-text-span'> Accessories </span>
43
+ </div>
44
+ </div>
45
+ {% endif %}
46
+ {% if show_video %}
47
+ <div
48
+ class='gallery-tab-item'
49
+ data-test-locator='galleryTabItemVideo'
50
+ data-test-value='video'
51
+ >
52
+ <div class='gallery-tab-item-text'>
53
+ <svg
54
+ width='16'
55
+ height='16'
56
+ viewBox='0 0 16 16'
57
+ fill='none'
58
+ xmlns='http://www.w3.org/2000/svg'
59
+ >
60
+ <rect x="0.5" y="2.5" width="15" height="11" rx="1.5" stroke="black"/>
61
+ <path d="M6.26348 6.16606C6.26362 5.85833 6.59736 5.66564 6.86394 5.81938L10.1644 7.72531C10.4306 7.87943 10.4304 8.26404 10.1639 8.41793L6.86389 10.3232C6.59737 10.4771 6.2642 10.2849 6.26385 9.97726L6.26348 6.16606Z" stroke="black"/>
62
+ </svg>
63
+ <span class='gallery-tab-item-text-span'> Video </span>
64
+ </div>
65
+ </div>
66
+ {% endif %}
67
+ {% if show_model %}
68
+ <div
69
+ class='gallery-tab-item'
70
+ data-test-locator='galleryTabItem3D'
71
+ data-test-value='3d'
72
+ >
73
+ <div class='gallery-tab-item-text'>
74
+ <svg
75
+ width='16'
76
+ height='16'
77
+ viewBox='0 0 16 16'
78
+ fill='none'
79
+ xmlns='http://www.w3.org/2000/svg'
80
+ >
81
+ <path d="M12.6963 3.78809V8.71094L8 11.4229L3.30371 8.71094V3.78809L8 1.07617L12.6963 3.78809Z" stroke="black"/>
82
+ <path d="M12.6346 3.75198L8.03554 6.40723L3.43652 3.75198" stroke="black"/>
83
+ <path d="M8 11.375V5.875" stroke="black" stroke-linejoin="round"/>
84
+ <path d="M6.41406 12.5996L7.82828 14.0138L6.41406 15.428" stroke="black" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/>
85
+ <path d="M3.30431 7C1.59439 7.73314 0.5 8.858 0.5 10.1192C0.5 12.2387 3.59098 13.9732 7.5 14.1104M12.6957 7C14.4056 7.73314 15.5 8.858 15.5 10.1192C15.5 12.0543 12.9234 13.6685 9.5 14.0392" stroke="black"/>
86
+ </svg>
87
+ <span class='gallery-tab-item-text-span'> 3D </span>
88
+ </div>
89
+ </div>
90
+ {% endif %}
91
+ </div>
92
+ </gallery-tab>
93
+
94
+ <style>
95
+ .gallery-tab-item {
96
+ padding: 3px 16px;
97
+ cursor: pointer;
98
+ display: inline-block;
99
+ border-radius: 100px;
100
+ background: #fff;
101
+ color: #000;
102
+ font-size: 14px;
103
+ font-weight: 400;
104
+ }
105
+
106
+ @media screen and (max-width: 1024px) {
107
+ .gallery-tab-item {
108
+ padding: 8px 10px;
109
+ font-size: 12px !important;
110
+ line-height: 12px;
111
+ }
112
+ }
113
+ .gallery-tab-item:hover {
114
+ background: #f5f5f5;
115
+ }
116
+ .gallery-tab-item.active {
117
+ color: #fff;
118
+ background: #000;
119
+ }
120
+ .gallery-tab-item-text {
121
+ display: flex;
122
+ align-items: center;
123
+ justify-content: center;
124
+ gap: 8px;
125
+ }
126
+ .gallery-tab-item svg {
127
+ transition: filter 0.2s ease, fill 0.2s ease, stroke 0.2s ease;
128
+ }
129
+ .gallery-tab-item.active svg {
130
+ filter: invert(1);
131
+ }
132
+ .gallery-tab-item-text-span {
133
+ }
134
+ @media screen and (max-width: 1024px) {
135
+ .gallery-tab-item-text svg {
136
+ width: 12px;
137
+ height: 12px;
138
+ }
139
+ .gallery-tab-item-text {
140
+ font-size: 12px;
141
+ gap: 6px;
142
+ }
143
+ }
144
+ </style>
145
+
146
+ <script>
147
+ (() => {
148
+ class GalleryTab extends HTMLElement {
149
+ static get observedAttributes() {
150
+ return ['value'];
151
+ }
152
+ constructor() {
153
+ super();
154
+ this._items = [];
155
+ this._suppressChange = false;
156
+ this._onItemClick = this._onItemClick.bind(this);
157
+ }
158
+ connectedCallback() {
159
+ this._items = Array.from(this.querySelectorAll('.gallery-tab-item'));
160
+ this._items.forEach((item) => {
161
+ item.addEventListener('click', this._onItemClick, false);
162
+ item.setAttribute('role', 'tab');
163
+ });
164
+ const activeFromDom = this.querySelector('.gallery-tab-item.active');
165
+ const activeFromDomValue = activeFromDom
166
+ ? activeFromDom.dataset.testValue ||
167
+ activeFromDom.getAttribute('data-test-value')
168
+ : null;
169
+ const firstItemValue = this._items[0]
170
+ ? this._items[0].dataset.testValue ||
171
+ this._items[0].getAttribute('data-test-value')
172
+ : null;
173
+ const initialValue =
174
+ this.getAttribute('value') ||
175
+ activeFromDomValue ||
176
+ firstItemValue ||
177
+ '';
178
+ this._suppressChange = true;
179
+ this.setAttribute('value', String(initialValue));
180
+ this._syncActive(initialValue);
181
+ this._suppressChange = false;
182
+ }
183
+ disconnectedCallback() {
184
+ this._items.forEach((item) => {
185
+ item.removeEventListener('click', this._onItemClick, false);
186
+ });
187
+ }
188
+ attributeChangedCallback(name, oldValue, newValue) {
189
+ if (name !== 'value' || oldValue === newValue) return;
190
+ this._syncActive(newValue);
191
+ if (!this._suppressChange) {
192
+ this.dispatchEvent(
193
+ new CustomEvent('change', {
194
+ detail: { value: newValue },
195
+ bubbles: true,
196
+ })
197
+ );
198
+ document.querySelectorAll('.product-preview-item').forEach((item) => {
199
+ if (item.dataset.galleryValue === newValue) {
200
+ item.classList.add('active');
201
+ } else {
202
+ item.classList.remove('active');
203
+ }
204
+ });
205
+ }
206
+ document
207
+ .querySelectorAll('product-video-tab video')
208
+ .forEach((video) => {
209
+ video.pause();
210
+ video.currentTime = 0;
211
+ });
212
+ }
213
+ get value() {
214
+ return this.getAttribute('value');
215
+ }
216
+ set value(next) {
217
+ if (next == null) {
218
+ this.removeAttribute('value');
219
+ } else {
220
+ this.setAttribute('value', String(next));
221
+ }
222
+ }
223
+ _onItemClick(event) {
224
+ const item = event.currentTarget;
225
+ const nextValue = item
226
+ ? item.dataset.testValue || item.getAttribute('data-test-value')
227
+ : '';
228
+ if (!nextValue || nextValue === this.value) return;
229
+ this.value = nextValue;
230
+ }
231
+ _syncActive(value) {
232
+ const normalized = String(value || '');
233
+ let matched = false;
234
+ this._items.forEach((item) => {
235
+ const itemValue =
236
+ item.dataset.testValue ||
237
+ item.getAttribute('data-test-value') ||
238
+ '';
239
+ const isActive = itemValue === normalized;
240
+ item.classList.toggle('active', isActive);
241
+ item.setAttribute('aria-selected', isActive ? 'true' : 'false');
242
+ if (isActive) matched = true;
243
+ });
244
+ if (!matched && this._items[0]) {
245
+ this._items.forEach((item, index) => {
246
+ const isActive = index === 0;
247
+ item.classList.toggle('active', isActive);
248
+ item.setAttribute('aria-selected', isActive ? 'true' : 'false');
249
+ });
250
+ }
251
+ }
252
+ }
253
+ if (!customElements.get('gallery-tab')) {
254
+ customElements.define('gallery-tab', GalleryTab);
255
+ }
256
+ })();
257
+ </script>