@c4h/chuci 0.1.0 → 0.2.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/CHANGELOG.md +32 -32
- package/LICENSE +20 -20
- package/README.ja.md +143 -143
- package/README.md +224 -224
- package/dist/chuci.js +9067 -6595
- package/dist/chuci.umd.js +164 -165
- package/dist/index.d.ts +1 -0
- package/package.json +36 -33
- package/src/components/swiper/cc-swiper-slide.ts +49 -49
- package/src/components/swiper/cc-swiper-styles.ts +28 -28
- package/src/components/swiper/cc-swiper.ts +379 -361
- package/src/components/swiper/swiper-styles.css +4 -4
- package/src/components/viewer/cc-viewer-3dmodel.ts +491 -491
- package/src/components/viewer/cc-viewer-base.ts +278 -278
- package/src/components/viewer/cc-viewer-gaussian.ts +380 -380
- package/src/components/viewer/cc-viewer-image.ts +189 -189
- package/src/components/viewer/cc-viewer-panorama.ts +85 -85
- package/src/components/viewer/cc-viewer-styles.ts +55 -55
- package/src/components/viewer/cc-viewer-video.ts +109 -109
- package/src/components/viewer/cc-viewer-youtube.ts +75 -75
- package/src/components/viewer/cc-viewer.ts +290 -290
- package/src/index.ts +24 -24
- package/src/types/css-modules.d.ts +1 -1
- package/src/types/global.d.ts +10 -10
- package/src/utils/base-element.ts +76 -76
- package/dist/assets/azumaya_panorama1.png +0 -0
- package/dist/chuci.cjs +0 -4483
- package/dist/index-8VMexD2a.cjs +0 -255
- package/dist/index-kvsisbKS.js +0 -2125
- package/dist/index.html +0 -241
- package/dist/test-image.html +0 -63
|
@@ -1,362 +1,380 @@
|
|
|
1
|
-
import { ChuciElement } from '@/utils/base-element'
|
|
2
|
-
import Swiper from 'swiper'
|
|
3
|
-
import { Navigation, Pagination, Scrollbar, Autoplay, Thumbs, Keyboard } from 'swiper/modules'
|
|
4
|
-
|
|
5
|
-
// Import Swiper styles as strings to inject into shadow DOM
|
|
6
|
-
import swiperStyles from './swiper-styles.css?inline'
|
|
7
|
-
|
|
8
|
-
export class CcSwiper extends ChuciElement {
|
|
9
|
-
private slider?: Swiper
|
|
10
|
-
private divContainer?: HTMLDivElement
|
|
11
|
-
private divSlides?: HTMLDivElement
|
|
12
|
-
private divGallery?: HTMLDivElement
|
|
13
|
-
private divPagination?: HTMLDivElement
|
|
14
|
-
private divPrevious?: HTMLDivElement
|
|
15
|
-
private divNext?: HTMLDivElement
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
...Array.from(this.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
document.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
--swiper-
|
|
109
|
-
--swiper-
|
|
110
|
-
--swiper-
|
|
111
|
-
--swiper-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
--swiper-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
background-
|
|
146
|
-
background-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
font-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
.swiper-button-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
background-
|
|
212
|
-
background-
|
|
213
|
-
background-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
background-
|
|
223
|
-
background-
|
|
224
|
-
background-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
const
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
${caption !== "" ?
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
${
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
<div id='
|
|
259
|
-
<div id='
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
} :
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
1
|
+
import { ChuciElement } from '@/utils/base-element'
|
|
2
|
+
import Swiper from 'swiper'
|
|
3
|
+
import { Navigation, Pagination, Scrollbar, Autoplay, Thumbs, Keyboard } from 'swiper/modules'
|
|
4
|
+
|
|
5
|
+
// Import Swiper styles as strings to inject into shadow DOM
|
|
6
|
+
import swiperStyles from './swiper-styles.css?inline'
|
|
7
|
+
|
|
8
|
+
export class CcSwiper extends ChuciElement {
|
|
9
|
+
private slider?: Swiper
|
|
10
|
+
private divContainer?: HTMLDivElement
|
|
11
|
+
private divSlides?: HTMLDivElement
|
|
12
|
+
private divGallery?: HTMLDivElement
|
|
13
|
+
private divPagination?: HTMLDivElement
|
|
14
|
+
private divPrevious?: HTMLDivElement
|
|
15
|
+
private divNext?: HTMLDivElement
|
|
16
|
+
private isDragging = false
|
|
17
|
+
|
|
18
|
+
static get observedAttributes() {
|
|
19
|
+
return ['has-thumb', 'autoplay']
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get hasThumb() {
|
|
23
|
+
return this.hasAttribute('has-thumb')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get autoplay() {
|
|
27
|
+
return this.hasAttribute('autoplay')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get slides() {
|
|
31
|
+
return [
|
|
32
|
+
...Array.from(this.querySelectorAll('cc-swiper-slide')),
|
|
33
|
+
...Array.from(this.divSlides?.querySelectorAll('cc-swiper-slide') ?? [])
|
|
34
|
+
]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async openViewer(imageUrl: string, imageType: string, slideIndex?: number) {
|
|
38
|
+
let ccView = document.querySelector("cc-viewer")
|
|
39
|
+
if (!ccView) {
|
|
40
|
+
const viewerElement = document.createElement("cc-viewer")
|
|
41
|
+
document.body.appendChild(viewerElement)
|
|
42
|
+
|
|
43
|
+
// Wait for custom element to be defined and connected
|
|
44
|
+
await customElements.whenDefined('cc-viewer')
|
|
45
|
+
|
|
46
|
+
// Use a small timeout to ensure the element is fully initialized
|
|
47
|
+
ccView = await new Promise((res) => {
|
|
48
|
+
setTimeout(() => {
|
|
49
|
+
res(document.querySelector("cc-viewer"))
|
|
50
|
+
}, 100)
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Store current swiper reference and slide index in viewer
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
56
|
+
(ccView as any).setSwiper(this);
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
+
(ccView as any).setCurrentSlideIndex(slideIndex ?? this.slider?.activeIndex ?? 0);
|
|
59
|
+
|
|
60
|
+
// Get the slide element to extract attributes
|
|
61
|
+
const slide = this.slides[slideIndex ?? this.slider?.activeIndex ?? 0];
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
63
|
+
const attributes: Record<string, any> = {};
|
|
64
|
+
|
|
65
|
+
// Check for viewer-specific attributes
|
|
66
|
+
if (slide?.hasAttribute('fit-to-container')) {
|
|
67
|
+
attributes.fitToContainer = true;
|
|
68
|
+
}
|
|
69
|
+
if (slide?.hasAttribute('debug-mode')) {
|
|
70
|
+
attributes.debugMode = true;
|
|
71
|
+
}
|
|
72
|
+
if (slide?.hasAttribute('camera-position')) {
|
|
73
|
+
attributes.cameraPosition = slide.getAttribute('camera-position');
|
|
74
|
+
}
|
|
75
|
+
if (slide?.hasAttribute('camera-target')) {
|
|
76
|
+
attributes.cameraTarget = slide.getAttribute('camera-target');
|
|
77
|
+
}
|
|
78
|
+
if (slide?.hasAttribute('show-texture')) {
|
|
79
|
+
attributes.showTexture = slide.getAttribute('show-texture') === 'true';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// For 3D models, pass material-url as attribute
|
|
83
|
+
if (imageType === '3dmodel' && slide?.hasAttribute('material-url')) {
|
|
84
|
+
attributes.materialUrl = slide.getAttribute('material-url');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
88
|
+
;(ccView as any).open(imageUrl, imageType, attributes)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
protected firstUpdated() {
|
|
92
|
+
// Initialization is now done in render method after DOM update
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
protected render() {
|
|
96
|
+
// Inject Swiper styles
|
|
97
|
+
const swiperStyleTag = `
|
|
98
|
+
<style>
|
|
99
|
+
${swiperStyles}
|
|
100
|
+
</style>
|
|
101
|
+
`
|
|
102
|
+
|
|
103
|
+
const styles = this.css`
|
|
104
|
+
:host {
|
|
105
|
+
display: block;
|
|
106
|
+
height: 100%;
|
|
107
|
+
width: 100%;
|
|
108
|
+
--swiper-theme-color: var(--cc-slider-theme-color, #007aff);
|
|
109
|
+
--swiper-navigation-color: var(--cc-slider-navigation-color, #007aff);
|
|
110
|
+
--swiper-gallery-height: 0px;
|
|
111
|
+
--swiper-slider-margin-bottom: 0px;
|
|
112
|
+
--swiper-navigation-size: 44px;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
:host([has-thumb]) {
|
|
116
|
+
--swiper-slider-margin-bottom: 10px;
|
|
117
|
+
--swiper-gallery-height: calc(100px - var(--swiper-slider-margin-bottom));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
#divContainer {
|
|
121
|
+
height: calc(100% - var(--swiper-gallery-height) - var(--swiper-slider-margin-bottom));
|
|
122
|
+
margin-bottom: var(--swiper-slider-margin-bottom);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.swiper {
|
|
126
|
+
height: 100%;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
#divGallery {
|
|
130
|
+
height: var(--swiper-gallery-height);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.gallery-thumbs .swiper-slide {
|
|
134
|
+
height: 100%;
|
|
135
|
+
opacity: 0.25;
|
|
136
|
+
transition: 200ms;
|
|
137
|
+
cursor: pointer;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.gallery-thumbs .swiper-slide-thumb-active {
|
|
141
|
+
opacity: 1;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.gallery-thumb {
|
|
145
|
+
background-position: center !important;
|
|
146
|
+
background-repeat: no-repeat !important;
|
|
147
|
+
background-size: cover !important;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.swiper-wrapper {
|
|
151
|
+
text-align: center;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.swiper-slide {
|
|
155
|
+
background-color: white;
|
|
156
|
+
height: 100%;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
img.viewer {
|
|
160
|
+
object-fit: contain;
|
|
161
|
+
height: 100%;
|
|
162
|
+
width: 100%;
|
|
163
|
+
cursor: pointer;
|
|
164
|
+
pointer-events: auto !important;
|
|
165
|
+
user-select: none;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
img.viewer.w-caption {
|
|
169
|
+
height: calc(100% - 10px - 1.5rem);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.slider-caption {
|
|
173
|
+
padding: 5px;
|
|
174
|
+
margin: 0;
|
|
175
|
+
line-height: 1.5em;
|
|
176
|
+
background: #000000;
|
|
177
|
+
color: #ffffff;
|
|
178
|
+
font-size: 0.6rem;
|
|
179
|
+
font-weight: 700;
|
|
180
|
+
position: absolute;
|
|
181
|
+
bottom: 0;
|
|
182
|
+
left: 0;
|
|
183
|
+
right: 0;
|
|
184
|
+
z-index: 10;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* Adjust pagination position when caption exists */
|
|
188
|
+
.swiper-pagination {
|
|
189
|
+
bottom: 10px !important;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* When captions exist, move pagination up */
|
|
193
|
+
#divContainer.has-captions .swiper-pagination {
|
|
194
|
+
bottom: calc(1.5rem + 20px) !important;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* Navigation button styles with SVG icons */
|
|
198
|
+
.swiper-button-prev,
|
|
199
|
+
.swiper-button-next {
|
|
200
|
+
color: var(--swiper-navigation-color);
|
|
201
|
+
font-size: 0; /* Hide text */
|
|
202
|
+
width: var(--swiper-navigation-size);
|
|
203
|
+
height: var(--swiper-navigation-size);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.swiper-button-prev:after {
|
|
207
|
+
content: '';
|
|
208
|
+
display: block;
|
|
209
|
+
width: var(--swiper-navigation-size);
|
|
210
|
+
height: var(--swiper-navigation-size);
|
|
211
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23007aff'%3E%3Cpath d='M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z'/%3E%3C/svg%3E");
|
|
212
|
+
background-size: contain;
|
|
213
|
+
background-repeat: no-repeat;
|
|
214
|
+
background-position: center;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.swiper-button-next:after {
|
|
218
|
+
content: '';
|
|
219
|
+
display: block;
|
|
220
|
+
width: var(--swiper-navigation-size);
|
|
221
|
+
height: var(--swiper-navigation-size);
|
|
222
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23007aff'%3E%3Cpath d='M8.59 16.59L10 18l6-6-6-6-1.41 1.41L13.17 12z'/%3E%3C/svg%3E");
|
|
223
|
+
background-size: contain;
|
|
224
|
+
background-repeat: no-repeat;
|
|
225
|
+
background-position: center;
|
|
226
|
+
}
|
|
227
|
+
`
|
|
228
|
+
|
|
229
|
+
const slidesHtml = this.slides.map((slide, index) => {
|
|
230
|
+
const thumbnailUrl = slide.getAttribute('thumbnail-url') || ''
|
|
231
|
+
const imageUrl = slide.getAttribute('image-url') || ''
|
|
232
|
+
const imageType = slide.getAttribute('image-type') || 'image'
|
|
233
|
+
const caption = slide.getAttribute('caption') || ''
|
|
234
|
+
|
|
235
|
+
return `
|
|
236
|
+
<div class='swiper-slide'>
|
|
237
|
+
<img src="${thumbnailUrl}" data-image-url="${imageUrl}" data-image-type="${imageType}" data-index="${index}" class="viewer${caption !== "" ? ` w-caption` : ""}">
|
|
238
|
+
${caption !== "" ? `<p class="slider-caption">${caption}</p>` : ""}
|
|
239
|
+
</div>
|
|
240
|
+
`
|
|
241
|
+
}).join('')
|
|
242
|
+
|
|
243
|
+
const galleryHtml = this.slides.map((slide, index) => {
|
|
244
|
+
const thumbnailUrl = slide.getAttribute('thumbnail-url') || ''
|
|
245
|
+
return `
|
|
246
|
+
<div class='swiper-slide gallery-thumb' data-index="${index}" style="background-image: url('${thumbnailUrl}')"></div>
|
|
247
|
+
`
|
|
248
|
+
}).join('')
|
|
249
|
+
|
|
250
|
+
const html = `
|
|
251
|
+
${swiperStyleTag}
|
|
252
|
+
${styles}
|
|
253
|
+
<div id='divContainer' class='swiper gallery-top'>
|
|
254
|
+
<div id='divSlides' class='swiper-wrapper'>
|
|
255
|
+
${slidesHtml}
|
|
256
|
+
</div>
|
|
257
|
+
|
|
258
|
+
<div id='divPagination' class='swiper-pagination'></div>
|
|
259
|
+
<div id='divPrevious' class='swiper-button-prev'></div>
|
|
260
|
+
<div id='divNext' class='swiper-button-next'></div>
|
|
261
|
+
</div>
|
|
262
|
+
<div id='divGallery' class='swiper gallery-thumbs'>
|
|
263
|
+
<div class='swiper-wrapper'>
|
|
264
|
+
${galleryHtml}
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
`
|
|
268
|
+
|
|
269
|
+
this.updateShadowRoot(html)
|
|
270
|
+
|
|
271
|
+
// Initialize Swiper after DOM update
|
|
272
|
+
setTimeout(() => {
|
|
273
|
+
this.initializeSwiper()
|
|
274
|
+
|
|
275
|
+
// Add click handlers for gallery thumbs
|
|
276
|
+
this.queryAll('.gallery-thumb').forEach((thumb, index) => {
|
|
277
|
+
thumb.addEventListener('click', () => this.slider?.slideTo(index))
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
// Add click handlers for viewer images
|
|
281
|
+
this.queryAll('img.viewer').forEach((img) => {
|
|
282
|
+
// Prevent default image behavior
|
|
283
|
+
img.addEventListener('dragstart', (e) => e.preventDefault())
|
|
284
|
+
|
|
285
|
+
img.addEventListener('click', (e) => {
|
|
286
|
+
// Ignore click if user was dragging
|
|
287
|
+
if (this.isDragging) {
|
|
288
|
+
this.isDragging = false
|
|
289
|
+
return
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
e.preventDefault()
|
|
293
|
+
e.stopPropagation()
|
|
294
|
+
e.stopImmediatePropagation()
|
|
295
|
+
const target = e.target as HTMLImageElement
|
|
296
|
+
const imageUrl = target.getAttribute('data-image-url') || ''
|
|
297
|
+
const imageType = target.getAttribute('data-image-type') || 'image'
|
|
298
|
+
const index = parseInt(target.getAttribute('data-index') || '0', 10)
|
|
299
|
+
this.openViewer(imageUrl, imageType, index)
|
|
300
|
+
return false
|
|
301
|
+
}, true)
|
|
302
|
+
})
|
|
303
|
+
}, 0)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private initializeSwiper() {
|
|
307
|
+
this.divContainer = this.query('#divContainer') ?? undefined
|
|
308
|
+
this.divSlides = this.query('#divSlides') ?? undefined
|
|
309
|
+
this.divGallery = this.query('#divGallery') ?? undefined
|
|
310
|
+
this.divPagination = this.query('#divPagination') ?? undefined
|
|
311
|
+
this.divPrevious = this.query('#divPrevious') ?? undefined
|
|
312
|
+
this.divNext = this.query('#divNext') ?? undefined
|
|
313
|
+
|
|
314
|
+
// Check if any slides have captions
|
|
315
|
+
const hasCaptions = this.slides.some(slide => slide.getAttribute('caption'))
|
|
316
|
+
if (hasCaptions && this.divContainer) {
|
|
317
|
+
this.divContainer.classList.add('has-captions')
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Core library features at https://swiperjs.com/api/#custom-build
|
|
321
|
+
const slidesLoop = this.slides.length >= 2
|
|
322
|
+
if (!this.divContainer) return
|
|
323
|
+
|
|
324
|
+
// Destroy existing slider if any
|
|
325
|
+
if (this.slider) {
|
|
326
|
+
this.slider.destroy()
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
this.slider = new Swiper(this.divContainer, {
|
|
330
|
+
modules: [Navigation, Pagination, Scrollbar, Autoplay, Thumbs, Keyboard],
|
|
331
|
+
navigation: {
|
|
332
|
+
prevEl: this.divPrevious,
|
|
333
|
+
nextEl: this.divNext,
|
|
334
|
+
},
|
|
335
|
+
pagination: this.hasThumb ? {} : {
|
|
336
|
+
el: this.divPagination
|
|
337
|
+
},
|
|
338
|
+
autoplay: this.autoplay ? {
|
|
339
|
+
delay: 5000,
|
|
340
|
+
disableOnInteraction: false,
|
|
341
|
+
reverseDirection: false,
|
|
342
|
+
stopOnLastSlide: false,
|
|
343
|
+
waitForTransition: true,
|
|
344
|
+
} : false,
|
|
345
|
+
thumbs: this.hasThumb && this.divGallery ? {
|
|
346
|
+
swiper: new Swiper(this.divGallery, {
|
|
347
|
+
spaceBetween: 10,
|
|
348
|
+
slidesPerView: Math.min(Math.max(4, this.slides.length), 8),
|
|
349
|
+
watchSlidesProgress: true,
|
|
350
|
+
}),
|
|
351
|
+
} : {},
|
|
352
|
+
preventClicks: false,
|
|
353
|
+
preventClicksPropagation: false,
|
|
354
|
+
simulateTouch: true,
|
|
355
|
+
allowTouchMove: true,
|
|
356
|
+
loop: slidesLoop,
|
|
357
|
+
on: {
|
|
358
|
+
sliderMove: () => {
|
|
359
|
+
this.isDragging = true
|
|
360
|
+
},
|
|
361
|
+
touchEnd: () => {
|
|
362
|
+
// Reset dragging flag after a short delay to allow click event to check it
|
|
363
|
+
setTimeout(() => {
|
|
364
|
+
this.isDragging = false
|
|
365
|
+
}, 50)
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
})
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (!customElements.get('cc-swiper')) {
|
|
373
|
+
customElements.define('cc-swiper', CcSwiper)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
declare global {
|
|
377
|
+
interface HTMLElementTagNameMap {
|
|
378
|
+
'cc-swiper': CcSwiper
|
|
379
|
+
}
|
|
362
380
|
}
|