zz-shopify-components 0.24.1-beta.2 → 0.25.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.
- package/package.json +1 -1
- package/snippets/zz-accessories-preview-swiper.liquid +0 -222
- package/snippets/zz-gallery-tab.liquid +0 -212
- package/snippets/zz-model-viewer.liquid +0 -124
- package/snippets/zz-product-model.liquid +0 -447
- package/snippets/zz-product-preview.liquid +0 -51
- package/snippets/zz-product-swiper.liquid +0 -248
- package/snippets/zz-product-video.liquid +0 -109
package/package.json
CHANGED
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
<div 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({{ 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
|
-
>
|
|
31
|
-
<rect width="36" height="36" rx="18" fill="#FC6C0F"/>
|
|
32
|
-
<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"/>
|
|
33
|
-
<circle cx="24.584" cy="26" r="0.6" fill="white" stroke="white" stroke-width="0.8"/>
|
|
34
|
-
<circle cx="14.916" cy="26" r="0.6" fill="white" stroke="white" stroke-width="0.8"/>
|
|
35
|
-
<path d="M17.4746 17.7998H21.4746" stroke="white" stroke-width="1.3" stroke-linecap="round"/>
|
|
36
|
-
<path d="M19.4746 19.7998V15.7998" stroke="white" stroke-width="1.3" stroke-linecap="round"/>
|
|
37
|
-
</svg>
|
|
38
|
-
<div class=" tw-flex tw-flex-col tw-gap-[4px] ">
|
|
39
|
-
<div class="lg:tw-text-[16px] tw-text-[12px] tw-leading-none tw-font-bold">
|
|
40
|
-
{{
|
|
41
|
-
block.settings.accessories_title
|
|
42
|
-
| default: block.settings.accessories_product.title
|
|
43
|
-
}}
|
|
44
|
-
</div>
|
|
45
|
-
<div class="lg:tw-text-[14px] tw-text-[12px] tw-leading-none tw-font-medium">
|
|
46
|
-
{{ block.settings.accessories_product.price | money_without_trailing_zeros }}
|
|
47
|
-
</div>
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
50
|
-
{% endif %}
|
|
51
|
-
{% endfor %}
|
|
52
|
-
</div>
|
|
53
|
-
{% endfor %}
|
|
54
|
-
</div>
|
|
55
|
-
<div class='swiper-pagination'></div>
|
|
56
|
-
<div class='product-accessories-swiper-button-prev'>
|
|
57
|
-
{% render 'zz-prev-next-btn', type: 'prev', color_type: 'dark' %}
|
|
58
|
-
</div>
|
|
59
|
-
<div class='product-accessories-swiper-button-next'>
|
|
60
|
-
{% render 'zz-prev-next-btn', type: 'next', color_type: 'dark' %}
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
|
|
64
|
-
<div class=' tw-absolute tw-bottom-[20px] tw-left-1/2 tw-transform tw-translate-x-[-50%] tw-z-10 tw-w-[80%] '>
|
|
65
|
-
<div class='swiper product-accessories-swiper-list-thumbs tw-opacity-0'>
|
|
66
|
-
<div class='swiper-wrapper tw-flex tw-justify-center'>
|
|
67
|
-
{% for image in product.metafields.custom.acc_images.value %}
|
|
68
|
-
<div class='swiper-slide !tw-w-auto tw-opacity-30 tw-relative'>
|
|
69
|
-
{{
|
|
70
|
-
image
|
|
71
|
-
| image_url: width: 200
|
|
72
|
-
| image_tag:
|
|
73
|
-
loading: 'lazy',
|
|
74
|
-
class: 'lg:tw-w-[50px] tw-w-[40px] tw-aspect-square tw-object-cover tw-rounded-[6px] ',
|
|
75
|
-
sizes: '(min-width: 750px) 50px, calc(100vw - 30px)',
|
|
76
|
-
widths: '50, 100, 150'
|
|
77
|
-
}}
|
|
78
|
-
</div>
|
|
79
|
-
{% endfor %}
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
82
|
-
</div>
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
</div>
|
|
86
|
-
|
|
87
|
-
<style>
|
|
88
|
-
.product-accessories-swiper-container .swiper-pagination {
|
|
89
|
-
--swiper-pagination-color: black;
|
|
90
|
-
--swiper-pagination-bottom: 10px;
|
|
91
|
-
}
|
|
92
|
-
.product-accessories-swiper-container .swiper-pagination,
|
|
93
|
-
.product-accessories-swiper-list-thumbs {
|
|
94
|
-
transition: opacity 0.2s ease-in-out;
|
|
95
|
-
}
|
|
96
|
-
.product-accessories-swiper-list-thumbs {
|
|
97
|
-
transform: translateY(12px) scale(0.96);
|
|
98
|
-
transform-origin: bottom center;
|
|
99
|
-
will-change: opacity, transform;
|
|
100
|
-
transition:
|
|
101
|
-
opacity 0.25s ease,
|
|
102
|
-
transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
103
|
-
}
|
|
104
|
-
.product-accessories-swiper-container:hover
|
|
105
|
-
.product-accessories-swiper-list-thumbs {
|
|
106
|
-
opacity: 1;
|
|
107
|
-
transform: translateY(0) scale(1);
|
|
108
|
-
}
|
|
109
|
-
.product-accessories-swiper-container:hover .swiper-pagination {
|
|
110
|
-
display: none;
|
|
111
|
-
transition: none;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
.product-accessories-swiper-list-thumbs .swiper-slide {
|
|
115
|
-
border: 1px solid black;
|
|
116
|
-
border-radius: 6px;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
.product-accessories-swiper-list-thumbs .swiper-slide-thumb-active {
|
|
120
|
-
opacity: 1;
|
|
121
|
-
border: 1px solid black;
|
|
122
|
-
border-radius: 6px;
|
|
123
|
-
}
|
|
124
|
-
.product-accessories-swiper-button-prev,
|
|
125
|
-
.product-accessories-swiper-button-next {
|
|
126
|
-
position: absolute;
|
|
127
|
-
top: 50%;
|
|
128
|
-
transform: translateY(-50%);
|
|
129
|
-
z-index: 10;
|
|
130
|
-
cursor: pointer;
|
|
131
|
-
pointer-events: auto;
|
|
132
|
-
}
|
|
133
|
-
@media screen and (max-width: 1024px) {
|
|
134
|
-
.product-accessories-swiper-button-prev {
|
|
135
|
-
display: none;
|
|
136
|
-
}
|
|
137
|
-
.product-accessories-swiper-button-next {
|
|
138
|
-
display: none;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
.product-accessories-swiper-button-prev {
|
|
142
|
-
left: 20px;
|
|
143
|
-
}
|
|
144
|
-
.product-accessories-swiper-button-next {
|
|
145
|
-
right: 20px;
|
|
146
|
-
}
|
|
147
|
-
</style>
|
|
148
|
-
<script>
|
|
149
|
-
document.addEventListener('DOMContentLoaded', function() {
|
|
150
|
-
const thumbs = new Swiper('.product-accessories-swiper-list-thumbs', {
|
|
151
|
-
slidesPerView: 5,
|
|
152
|
-
spaceBetween: 8,
|
|
153
|
-
watchSlidesProgress: true,
|
|
154
|
-
slideToClickedSlide: true,
|
|
155
|
-
breakpoints: {
|
|
156
|
-
0: { slidesPerView: 4, spaceBetween: 6 },
|
|
157
|
-
768: { slidesPerView: 5, spaceBetween: 8 },
|
|
158
|
-
1024:{ slidesPerView: 6, spaceBetween: 10 }
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
const swiper = new Swiper('.product-accessories-swiper-list', {
|
|
163
|
-
loop: true,
|
|
164
|
-
navigation: {
|
|
165
|
-
nextEl: '.product-accessories-swiper-button-next',
|
|
166
|
-
prevEl: '.product-accessories-swiper-button-prev',
|
|
167
|
-
},
|
|
168
|
-
effect: 'fade',
|
|
169
|
-
loopedSlides: {{ product.metafields.custom.acc_images.value.size | default: 0 }},
|
|
170
|
-
slidesPerView: 1,
|
|
171
|
-
spaceBetween: 10,
|
|
172
|
-
pagination: {
|
|
173
|
-
el: '.swiper-pagination',
|
|
174
|
-
clickable: true,
|
|
175
|
-
},
|
|
176
|
-
thumbs: { swiper: thumbs },
|
|
177
|
-
{% comment %} on: {
|
|
178
|
-
slideChange: function() {
|
|
179
|
-
const currentIndex = this.activeIndex;
|
|
180
|
-
console.log('currentIndex:', currentIndex);
|
|
181
|
-
const pointSwiperIndex = document.querySelectorAll('.product-accessories-point-item').forEach(item => {
|
|
182
|
-
if (item.dataset.pointSwiperIndex == currentIndex + 1) {
|
|
183
|
-
item.style.display = 'flex';
|
|
184
|
-
} else {
|
|
185
|
-
item.style.display = 'none';
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
} {% endcomment %}
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
window.accessoriesPointClickHandler = async function(variantId) {
|
|
194
|
-
try {
|
|
195
|
-
const res = await fetch(window.routes.cart_add_url, {
|
|
196
|
-
method: 'POST',
|
|
197
|
-
headers: {
|
|
198
|
-
'Content-Type': 'application/json',
|
|
199
|
-
'Accept': 'application/json'
|
|
200
|
-
},
|
|
201
|
-
body: JSON.stringify({
|
|
202
|
-
items: [{ id: variantId, quantity: 1 }]
|
|
203
|
-
})
|
|
204
|
-
});
|
|
205
|
-
const data = await res.json();
|
|
206
|
-
if (data && data.status) {
|
|
207
|
-
console.error('Add to cart error:', data);
|
|
208
|
-
alert(data.message || 'Failed to add to cart');
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
const cartDrawer = document.querySelector('cart-drawer');
|
|
212
|
-
if (cartDrawer && typeof cartDrawer.renderContents === 'function') {
|
|
213
|
-
cartDrawer.renderContents(data);
|
|
214
|
-
if (typeof cartDrawer.open === 'function') cartDrawer.open();
|
|
215
|
-
} else {
|
|
216
|
-
window.location.href = window.routes.cart_url;
|
|
217
|
-
}
|
|
218
|
-
} catch (e) {
|
|
219
|
-
console.error(e);
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
</script>
|
|
@@ -1,212 +0,0 @@
|
|
|
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 class='gallery-tab-item active' data-test-locator="galleryTabItemPhoto" data-test-value="photo">
|
|
4
|
-
<div class='gallery-tab-item-text'>
|
|
5
|
-
<svg
|
|
6
|
-
xmlns='http://www.w3.org/2000/svg'
|
|
7
|
-
width='16'
|
|
8
|
-
height='16'
|
|
9
|
-
viewBox='0 0 16 16'
|
|
10
|
-
fill='none'
|
|
11
|
-
>
|
|
12
|
-
<path d="M1 4.5V12.5C1 13.0523 1.44772 13.5 2 13.5H14" stroke="black" stroke-linecap="round" stroke-linejoin="round"/>
|
|
13
|
-
<rect x="3" y="3.5" width="12" height="8" rx="0.5" stroke="black"/>
|
|
14
|
-
<path d="M3 11L6 8L8 9.5L10.5 7L14.5 11" stroke="black" stroke-linejoin="round"/>
|
|
15
|
-
<circle cx="5.75" cy="5.75" r="0.75" fill="black"/>
|
|
16
|
-
</svg>
|
|
17
|
-
<span class='gallery-tab-item-text-span'> Photo </span>
|
|
18
|
-
</div>
|
|
19
|
-
</div>
|
|
20
|
-
{% if show_accessories_point %}
|
|
21
|
-
<div class='gallery-tab-item' data-test-locator="galleryTabItemAccessories" data-test-value="accessories">
|
|
22
|
-
<div class='gallery-tab-item-text'>
|
|
23
|
-
<svg
|
|
24
|
-
width='20'
|
|
25
|
-
height='20'
|
|
26
|
-
viewBox='0 0 20 20'
|
|
27
|
-
fill='none'
|
|
28
|
-
xmlns='http://www.w3.org/2000/svg'
|
|
29
|
-
>
|
|
30
|
-
<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"/>
|
|
31
|
-
<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"/>
|
|
32
|
-
</svg>
|
|
33
|
-
|
|
34
|
-
<span class='gallery-tab-item-text-span'> Accessories </span>
|
|
35
|
-
</div>
|
|
36
|
-
</div>
|
|
37
|
-
{% endif %}
|
|
38
|
-
{% if show_video %}
|
|
39
|
-
<div class='gallery-tab-item' data-test-locator="galleryTabItemVideo" data-test-value="video">
|
|
40
|
-
<div class='gallery-tab-item-text'>
|
|
41
|
-
<svg
|
|
42
|
-
width='16'
|
|
43
|
-
height='16'
|
|
44
|
-
viewBox='0 0 16 16'
|
|
45
|
-
fill='none'
|
|
46
|
-
xmlns='http://www.w3.org/2000/svg'
|
|
47
|
-
>
|
|
48
|
-
<rect x="0.5" y="2.5" width="15" height="11" rx="1.5" stroke="black"/>
|
|
49
|
-
<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"/>
|
|
50
|
-
</svg>
|
|
51
|
-
<span class='gallery-tab-item-text-span'> Video </span>
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
54
|
-
{% endif %}
|
|
55
|
-
{% if show_model %}
|
|
56
|
-
<div class='gallery-tab-item' data-test-locator="galleryTabItem3D" data-test-value="3d">
|
|
57
|
-
<div class='gallery-tab-item-text'>
|
|
58
|
-
<svg
|
|
59
|
-
width='16'
|
|
60
|
-
height='16'
|
|
61
|
-
viewBox='0 0 16 16'
|
|
62
|
-
fill='none'
|
|
63
|
-
xmlns='http://www.w3.org/2000/svg'
|
|
64
|
-
>
|
|
65
|
-
<path d="M12.6963 3.78809V8.71094L8 11.4229L3.30371 8.71094V3.78809L8 1.07617L12.6963 3.78809Z" stroke="black"/>
|
|
66
|
-
<path d="M12.6346 3.75198L8.03554 6.40723L3.43652 3.75198" stroke="black"/>
|
|
67
|
-
<path d="M8 11.375V5.875" stroke="black" stroke-linejoin="round"/>
|
|
68
|
-
<path d="M6.41406 12.5996L7.82828 14.0138L6.41406 15.428" stroke="black" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
69
|
-
<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"/>
|
|
70
|
-
</svg>
|
|
71
|
-
<span class='gallery-tab-item-text-span'> 3D </span>
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
{% endif %}
|
|
75
|
-
</div>
|
|
76
|
-
</gallery-tab>
|
|
77
|
-
|
|
78
|
-
<style>
|
|
79
|
-
.gallery-tab-item {
|
|
80
|
-
padding: 8px 16px;
|
|
81
|
-
cursor: pointer;
|
|
82
|
-
display: inline-block;
|
|
83
|
-
border-radius: 100px;
|
|
84
|
-
background: #fff;
|
|
85
|
-
color: #000;
|
|
86
|
-
font-size: 14px;
|
|
87
|
-
font-weight: 400;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
@media screen and (max-width: 1024px) {
|
|
91
|
-
.gallery-tab-item {
|
|
92
|
-
padding: 2px 8px;
|
|
93
|
-
font-size: 12px !important;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
.gallery-tab-item:hover {
|
|
97
|
-
background: #F5F5F5;
|
|
98
|
-
}
|
|
99
|
-
.gallery-tab-item.active {
|
|
100
|
-
color: #fff;
|
|
101
|
-
background: #000;
|
|
102
|
-
}
|
|
103
|
-
.gallery-tab-item-text {
|
|
104
|
-
display: flex;
|
|
105
|
-
align-items: center;
|
|
106
|
-
justify-content: center;
|
|
107
|
-
gap: 8px;
|
|
108
|
-
}
|
|
109
|
-
.gallery-tab-item svg {
|
|
110
|
-
transition:
|
|
111
|
-
filter 0.2s ease,
|
|
112
|
-
fill 0.2s ease,
|
|
113
|
-
stroke 0.2s ease;
|
|
114
|
-
}
|
|
115
|
-
.gallery-tab-item.active svg {
|
|
116
|
-
filter: invert(1);
|
|
117
|
-
}
|
|
118
|
-
.gallery-tab-item-text-span {
|
|
119
|
-
}
|
|
120
|
-
</style>
|
|
121
|
-
|
|
122
|
-
<script>
|
|
123
|
-
;(() => {
|
|
124
|
-
class GalleryTab extends HTMLElement {
|
|
125
|
-
static get observedAttributes() {
|
|
126
|
-
return ['value'];
|
|
127
|
-
}
|
|
128
|
-
constructor() {
|
|
129
|
-
super();
|
|
130
|
-
this._items = [];
|
|
131
|
-
this._suppressChange = false;
|
|
132
|
-
this._onItemClick = this._onItemClick.bind(this);
|
|
133
|
-
}
|
|
134
|
-
connectedCallback() {
|
|
135
|
-
this._items = Array.from(this.querySelectorAll('.gallery-tab-item'));
|
|
136
|
-
this._items.forEach((item) => {
|
|
137
|
-
item.addEventListener('click', this._onItemClick, false);
|
|
138
|
-
item.setAttribute('role', 'tab');
|
|
139
|
-
});
|
|
140
|
-
const activeFromDom = this.querySelector('.gallery-tab-item.active');
|
|
141
|
-
const activeFromDomValue = activeFromDom ? (activeFromDom.dataset.testValue || activeFromDom.getAttribute('data-test-value')) : null;
|
|
142
|
-
const firstItemValue = this._items[0] ? (this._items[0].dataset.testValue || this._items[0].getAttribute('data-test-value')) : null;
|
|
143
|
-
const initialValue = this.getAttribute('value') || activeFromDomValue || firstItemValue || '';
|
|
144
|
-
this._suppressChange = true;
|
|
145
|
-
this.setAttribute('value', String(initialValue));
|
|
146
|
-
this._syncActive(initialValue);
|
|
147
|
-
this._suppressChange = false;
|
|
148
|
-
}
|
|
149
|
-
disconnectedCallback() {
|
|
150
|
-
this._items.forEach((item) => {
|
|
151
|
-
item.removeEventListener('click', this._onItemClick, false);
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
attributeChangedCallback(name, oldValue, newValue) {
|
|
155
|
-
if (name !== 'value' || oldValue === newValue) return;
|
|
156
|
-
this._syncActive(newValue);
|
|
157
|
-
if (!this._suppressChange) {
|
|
158
|
-
this.dispatchEvent(
|
|
159
|
-
new CustomEvent('change', {
|
|
160
|
-
detail: { value: newValue },
|
|
161
|
-
bubbles: true
|
|
162
|
-
})
|
|
163
|
-
);
|
|
164
|
-
document.querySelectorAll('.product-preview-item').forEach((item) => {
|
|
165
|
-
if (item.dataset.galleryValue === newValue) {
|
|
166
|
-
item.classList.add('active');
|
|
167
|
-
} else {
|
|
168
|
-
item.classList.remove('active');
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
get value() {
|
|
174
|
-
return this.getAttribute('value');
|
|
175
|
-
}
|
|
176
|
-
set value(next) {
|
|
177
|
-
if (next == null) {
|
|
178
|
-
this.removeAttribute('value');
|
|
179
|
-
} else {
|
|
180
|
-
this.setAttribute('value', String(next));
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
_onItemClick(event) {
|
|
184
|
-
const item = event.currentTarget;
|
|
185
|
-
const nextValue = item ? (item.dataset.testValue || item.getAttribute('data-test-value')) : '';
|
|
186
|
-
if (!nextValue || nextValue === this.value) return;
|
|
187
|
-
this.value = nextValue;
|
|
188
|
-
}
|
|
189
|
-
_syncActive(value) {
|
|
190
|
-
const normalized = String(value || '');
|
|
191
|
-
let matched = false;
|
|
192
|
-
this._items.forEach((item) => {
|
|
193
|
-
const itemValue = item.dataset.testValue || item.getAttribute('data-test-value') || '';
|
|
194
|
-
const isActive = itemValue === normalized;
|
|
195
|
-
item.classList.toggle('active', isActive);
|
|
196
|
-
item.setAttribute('aria-selected', isActive ? 'true' : 'false');
|
|
197
|
-
if (isActive) matched = true;
|
|
198
|
-
});
|
|
199
|
-
if (!matched && this._items[0]) {
|
|
200
|
-
this._items.forEach((item, index) => {
|
|
201
|
-
const isActive = index === 0;
|
|
202
|
-
item.classList.toggle('active', isActive);
|
|
203
|
-
item.setAttribute('aria-selected', isActive ? 'true' : 'false');
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
if (!customElements.get('gallery-tab')) {
|
|
209
|
-
customElements.define('gallery-tab', GalleryTab);
|
|
210
|
-
}
|
|
211
|
-
})();
|
|
212
|
-
</script>
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
{% comment %}
|
|
2
|
-
id: 模型viewer id
|
|
3
|
-
src: 模型文件url
|
|
4
|
-
poster: 模型封面图片url
|
|
5
|
-
environment_image: 环境图片url
|
|
6
|
-
{% endcomment %}
|
|
7
|
-
<style>
|
|
8
|
-
#{{ id }} .mv-mask {
|
|
9
|
-
position: absolute;
|
|
10
|
-
display: flex;
|
|
11
|
-
flex-direction: column;
|
|
12
|
-
justify-content: center;
|
|
13
|
-
align-items: center;
|
|
14
|
-
width: 100%;
|
|
15
|
-
height: 100%;
|
|
16
|
-
background: #f5f5f6;
|
|
17
|
-
color: #000;
|
|
18
|
-
transition: opacity 0.4s ease;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
#{{ id }} .mv-percent {
|
|
23
|
-
font-size: 14px;
|
|
24
|
-
line-height: 1;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
#{{ id }} .mv-fs-btn {
|
|
28
|
-
position: absolute;
|
|
29
|
-
bottom: 20px;
|
|
30
|
-
right: 20px;
|
|
31
|
-
width: 48px;
|
|
32
|
-
height: 48px;
|
|
33
|
-
display: inline-flex;
|
|
34
|
-
align-items: center;
|
|
35
|
-
justify-content: center;
|
|
36
|
-
border-radius: 9999px;
|
|
37
|
-
background: rgba(0,0,0,0.3);
|
|
38
|
-
color: #fff;
|
|
39
|
-
border: 0;
|
|
40
|
-
cursor: pointer;
|
|
41
|
-
z-index: 5;
|
|
42
|
-
}
|
|
43
|
-
#{{ id }} .mv-fs-btn:focus { outline: 2px solid rgba(255,255,255,0.8); outline-offset: 2px; }
|
|
44
|
-
#{{ id }} .mv-fs-icon { width: 18px; height: 18px; }
|
|
45
|
-
</style>
|
|
46
|
-
<model-viewer
|
|
47
|
-
id='{{ id }}'
|
|
48
|
-
src='{{ src }}'
|
|
49
|
-
camera-controls='true'
|
|
50
|
-
style='--poster-color: transparent; background-color: #f5f5f6;'
|
|
51
|
-
poster='{{ poster }}'
|
|
52
|
-
{% comment %} data-shopify-feature='1.12' {% endcomment %}
|
|
53
|
-
loading='eager'
|
|
54
|
-
field-of-view='45deg'
|
|
55
|
-
max-field-of-view='40deg'
|
|
56
|
-
max-camera-orbit='auto auto 8m'
|
|
57
|
-
min-camera-orbit='auto auto 4m'
|
|
58
|
-
exposure='1'
|
|
59
|
-
camera-orbit='200deg 90deg 100%'
|
|
60
|
-
ar-status='not-presenting'
|
|
61
|
-
class='tw-w-full tw-h-full'
|
|
62
|
-
environment-image='{{ environment_image }}'
|
|
63
|
-
alt='product model'
|
|
64
|
-
>
|
|
65
|
-
<div slot='progress-bar' class='mv-mask'>
|
|
66
|
-
<div class='mv-loading tw-flex tw-items-center tw-justify-center tw-gap-6 tw-flex-col'>
|
|
67
|
-
<img
|
|
68
|
-
src='{{ 'loading.png' | asset_url }}'
|
|
69
|
-
height="40px"
|
|
70
|
-
width="40px"
|
|
71
|
-
alt='loading'
|
|
72
|
-
class='tw-animate-spin tw-h-[40px] tw-object-contain tw-w-[40px]'
|
|
73
|
-
>
|
|
74
|
-
|
|
75
|
-
<div class='mv-percent'>0%</div>
|
|
76
|
-
</div>
|
|
77
|
-
</div>
|
|
78
|
-
</model-viewer>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
<script>
|
|
82
|
-
(function () {
|
|
83
|
-
const mv = document.getElementById('{{ id }}');
|
|
84
|
-
if (!mv) return;
|
|
85
|
-
const mask = mv.querySelector('.mv-mask');
|
|
86
|
-
const percentEl = mv.querySelector('.mv-percent');
|
|
87
|
-
if (!mask || !percentEl) return;
|
|
88
|
-
|
|
89
|
-
const setPercent = (p) => {
|
|
90
|
-
const clamped = Math.max(0, Math.min(1, p));
|
|
91
|
-
percentEl.textContent = Math.round(clamped * 100) + '%';
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
mv.addEventListener('progress', (e) => {
|
|
95
|
-
const p =
|
|
96
|
-
e && e.detail && typeof e.detail.totalProgress === 'number'
|
|
97
|
-
? e.detail.totalProgress
|
|
98
|
-
: 0;
|
|
99
|
-
setPercent(p);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
mv.addEventListener('load', () => {
|
|
103
|
-
setPercent(1);
|
|
104
|
-
requestAnimationFrame(() => {
|
|
105
|
-
mask.style.opacity = '0';
|
|
106
|
-
});
|
|
107
|
-
setTimeout(() => {
|
|
108
|
-
mask.style.display = 'none';
|
|
109
|
-
}, 400);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
mv.addEventListener('error', () => {
|
|
113
|
-
mask.style.opacity = '0';
|
|
114
|
-
setTimeout(() => {
|
|
115
|
-
mask.style.display = 'none';
|
|
116
|
-
}, 400);
|
|
117
|
-
});
|
|
118
|
-
})();
|
|
119
|
-
</script>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|