tee3apps-cms-sdk-react 0.0.18 → 0.0.20
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/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/App.css +5 -7
- package/src/PageComponents/RowComponent.tsx +55 -2
- package/src/PageComponents/Visual-Components/CarouselComponent.tsx +363 -365
- package/src/PageComponents/Visual-Components/TabComponent.tsx +53 -47
- package/src/PageComponents/Visual-Components/tab.css +3 -8
- package/src/index.css +9 -9
|
@@ -1,366 +1,364 @@
|
|
|
1
|
-
import React, { useState, useEffect, useCallback } from 'react';
|
|
2
|
-
import { Linodeurl } from '../../const';
|
|
3
|
-
|
|
4
|
-
// Types for the carousel props
|
|
5
|
-
interface Slide {
|
|
6
|
-
sl_id: string;
|
|
7
|
-
name: string;
|
|
8
|
-
sl_type: string;
|
|
9
|
-
image: {
|
|
10
|
-
isDynamic: boolean;
|
|
11
|
-
url: string;
|
|
12
|
-
alt: string;
|
|
13
|
-
width: number | null;
|
|
14
|
-
height: number | null;
|
|
15
|
-
};
|
|
16
|
-
video: {
|
|
17
|
-
url?: string;
|
|
18
|
-
alt?: string;
|
|
19
|
-
};
|
|
20
|
-
link_type: string; // NONE | EXTERNALLINK | PRODUCT | TAG | PAGE
|
|
21
|
-
product: any;
|
|
22
|
-
tag: any;
|
|
23
|
-
page: any;
|
|
24
|
-
external_link: {
|
|
25
|
-
url: string;
|
|
26
|
-
target: string;
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface SlideMode {
|
|
31
|
-
web: {
|
|
32
|
-
slidesToShow: number;
|
|
33
|
-
};
|
|
34
|
-
mobileweb: {
|
|
35
|
-
slidesToShow: number;
|
|
36
|
-
};
|
|
37
|
-
mobileapp: {
|
|
38
|
-
slidesToShow: number;
|
|
39
|
-
};
|
|
40
|
-
tablet: {
|
|
41
|
-
slidesToShow: number;
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface SlidesData {
|
|
46
|
-
all: Slide[];
|
|
47
|
-
mode: SlideMode;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface CarouselProps {
|
|
51
|
-
name: string;
|
|
52
|
-
code: string;
|
|
53
|
-
scrollMode: string;
|
|
54
|
-
autoplay: boolean;
|
|
55
|
-
autoplayInterval: number;
|
|
56
|
-
pauseOnHover: boolean;
|
|
57
|
-
speed: number;
|
|
58
|
-
height: string;
|
|
59
|
-
vertical: boolean;
|
|
60
|
-
sliderArrowVisible: boolean;
|
|
61
|
-
pagingDotVisible: boolean;
|
|
62
|
-
objectFit: string;
|
|
63
|
-
slides: SlidesData[];
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
interface CarouselComponentMainProps {
|
|
67
|
-
props: CarouselProps;
|
|
68
|
-
deviceMode?: string;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const CarouselComponent: React.FC<CarouselComponentMainProps> = ({
|
|
72
|
-
props,
|
|
73
|
-
deviceMode = 'web'
|
|
74
|
-
}) => {
|
|
75
|
-
const [currentSlide, setCurrentSlide] = useState(0);
|
|
76
|
-
const [isPlaying, setIsPlaying] = useState(props.autoplay);
|
|
77
|
-
const [isHovered, setIsHovered] = useState(false);
|
|
78
|
-
|
|
79
|
-
const slides = props.slides[0].all;
|
|
80
|
-
const totalSlides = slides.length;
|
|
81
|
-
|
|
82
|
-
// Function to go to next slide (advance by 1 to support wrap pairs)
|
|
83
|
-
const nextSlide = useCallback(() => {
|
|
84
|
-
setCurrentSlide((prev) => (prev + 1) % totalSlides);
|
|
85
|
-
}, [totalSlides]);
|
|
86
|
-
|
|
87
|
-
// Function to go to previous slide
|
|
88
|
-
const prevSlide = useCallback(() => {
|
|
89
|
-
setCurrentSlide((prev) => (prev - 1 + totalSlides) % totalSlides);
|
|
90
|
-
}, [totalSlides]);
|
|
91
|
-
|
|
92
|
-
// Function to go to specific slide start index
|
|
93
|
-
const goToSlide = (index: number) => {
|
|
94
|
-
setCurrentSlide(index % totalSlides);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
// Handle autoplay
|
|
98
|
-
useEffect(() => {
|
|
99
|
-
if (!props.autoplay || !isPlaying || isHovered) return;
|
|
100
|
-
|
|
101
|
-
const interval = setInterval(() => {
|
|
102
|
-
nextSlide();
|
|
103
|
-
}, props.autoplayInterval);
|
|
104
|
-
|
|
105
|
-
return () => clearInterval(interval);
|
|
106
|
-
}, [props.autoplay, props.autoplayInterval, isPlaying, isHovered, nextSlide]);
|
|
107
|
-
|
|
108
|
-
// Handle pause on hover
|
|
109
|
-
const handleMouseEnter = () => {
|
|
110
|
-
setIsHovered(true);
|
|
111
|
-
if (props.pauseOnHover) {
|
|
112
|
-
setIsPlaying(false);
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const handleMouseLeave = () => {
|
|
117
|
-
setIsHovered(false);
|
|
118
|
-
if (props.pauseOnHover) {
|
|
119
|
-
setIsPlaying(true);
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
// Get current mode settings
|
|
124
|
-
const getCurrentMode = () => {
|
|
125
|
-
switch(deviceMode) {
|
|
126
|
-
case 'mobileweb':
|
|
127
|
-
return props.slides[0].mode.mobileweb;
|
|
128
|
-
case 'mobileapp':
|
|
129
|
-
return props.slides[0].mode.mobileapp;
|
|
130
|
-
case 'tablet':
|
|
131
|
-
return props.slides[0].mode.tablet;
|
|
132
|
-
case 'web':
|
|
133
|
-
default:
|
|
134
|
-
return props.slides[0].mode.web;
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const currentMode = getCurrentMode();
|
|
139
|
-
const slidesToShow = currentMode.slidesToShow;
|
|
140
|
-
|
|
141
|
-
const resolveAssetUrl = (url?: string) => {
|
|
142
|
-
if (!url) return '';
|
|
143
|
-
if (url.startsWith('http://') || url.startsWith('https://')) return url;
|
|
144
|
-
return `${Linodeurl}${url}`;
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
const buildLinkForSlide = (slide: Slide) => {
|
|
148
|
-
console.warn(slide)
|
|
149
|
-
switch (slide.link_type) {
|
|
150
|
-
case 'NONE':
|
|
151
|
-
return null;
|
|
152
|
-
case 'EXTERNALLINK':
|
|
153
|
-
return `${slide.external_link?.url || ''}`;
|
|
154
|
-
case 'PRODUCT': {
|
|
155
|
-
const product: any = slide.product || {};
|
|
156
|
-
const pdType = product?.pd_type || product?.product_type;
|
|
157
|
-
const code = product?.code || '';
|
|
158
|
-
if (!code) return null;
|
|
159
|
-
|
|
160
|
-
return pdType === 'VARIANT'
|
|
161
|
-
? `/variant/${code}`
|
|
162
|
-
: `model/${code}`;
|
|
163
|
-
}
|
|
164
|
-
case 'TAG': {
|
|
165
|
-
const tag: any = slide.tag || {};
|
|
166
|
-
if (!tag.code) return null;
|
|
167
|
-
return `/tag/${tag?.code}`;
|
|
168
|
-
}
|
|
169
|
-
case 'PAGE': {
|
|
170
|
-
const page: any = slide.page || {};
|
|
171
|
-
const pageId = page._id
|
|
172
|
-
const pgType=page.page_type
|
|
173
|
-
return `/page/${pageId}?type=${pgType}`;
|
|
174
|
-
}
|
|
175
|
-
default:
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
const handleSlideClick = (slide: Slide) => {
|
|
181
|
-
const link = buildLinkForSlide(slide);
|
|
182
|
-
if (link === null) return;
|
|
183
|
-
if (typeof link === 'string') {
|
|
184
|
-
if (link.startsWith('http')) {
|
|
185
|
-
window.open(link, '_blank');
|
|
186
|
-
} else {
|
|
187
|
-
window.location.href = link;
|
|
188
|
-
}
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
const queryString = Object.entries((link as any).query || {})
|
|
192
|
-
.map(([key, value]) => `${key}=${encodeURIComponent(value as string)}`)
|
|
193
|
-
.join('&');
|
|
194
|
-
const fullPath = `${(link as any).pathname}${queryString ? `?${queryString}` : ''}`;
|
|
195
|
-
window.location.href = fullPath;
|
|
196
|
-
};
|
|
197
|
-
|
|
198
|
-
return (
|
|
199
|
-
<div
|
|
200
|
-
className="carousel-container"
|
|
201
|
-
style={{
|
|
202
|
-
position: 'relative',
|
|
203
|
-
|
|
204
|
-
overflow: 'hidden'
|
|
205
|
-
}}
|
|
206
|
-
onMouseEnter={handleMouseEnter}
|
|
207
|
-
onMouseLeave={handleMouseLeave}
|
|
208
|
-
>
|
|
209
|
-
{/* Slides container */}
|
|
210
|
-
<div
|
|
211
|
-
className="carousel-slides"
|
|
212
|
-
style={{
|
|
213
|
-
display: 'flex',
|
|
214
|
-
|
|
215
|
-
transition: `opacity ${props.speed}ms ease`,
|
|
216
|
-
flexDirection: props.vertical ? 'column' : 'row'
|
|
217
|
-
}}
|
|
218
|
-
>
|
|
219
|
-
{Array.from({ length: slidesToShow }).map((_, offset) => {
|
|
220
|
-
const slide = slides[(currentSlide + offset) % totalSlides];
|
|
221
|
-
const clickable = buildLinkForSlide(slide) !== null;
|
|
222
|
-
return (
|
|
223
|
-
<div
|
|
224
|
-
key={`${slide.sl_id}-${offset}`}
|
|
225
|
-
className="carousel-slide"
|
|
226
|
-
style={{
|
|
227
|
-
flex: `0 0 ${100 / slidesToShow}%`,
|
|
228
|
-
|
|
229
|
-
overflow: 'hidden',
|
|
230
|
-
display: '
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
};
|
|
365
|
-
|
|
1
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import { Linodeurl } from '../../const';
|
|
3
|
+
|
|
4
|
+
// Types for the carousel props
|
|
5
|
+
interface Slide {
|
|
6
|
+
sl_id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
sl_type: string;
|
|
9
|
+
image: {
|
|
10
|
+
isDynamic: boolean;
|
|
11
|
+
url: string;
|
|
12
|
+
alt: string;
|
|
13
|
+
width: number | null;
|
|
14
|
+
height: number | null;
|
|
15
|
+
};
|
|
16
|
+
video: {
|
|
17
|
+
url?: string;
|
|
18
|
+
alt?: string;
|
|
19
|
+
};
|
|
20
|
+
link_type: string; // NONE | EXTERNALLINK | PRODUCT | TAG | PAGE
|
|
21
|
+
product: any;
|
|
22
|
+
tag: any;
|
|
23
|
+
page: any;
|
|
24
|
+
external_link: {
|
|
25
|
+
url: string;
|
|
26
|
+
target: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface SlideMode {
|
|
31
|
+
web: {
|
|
32
|
+
slidesToShow: number;
|
|
33
|
+
};
|
|
34
|
+
mobileweb: {
|
|
35
|
+
slidesToShow: number;
|
|
36
|
+
};
|
|
37
|
+
mobileapp: {
|
|
38
|
+
slidesToShow: number;
|
|
39
|
+
};
|
|
40
|
+
tablet: {
|
|
41
|
+
slidesToShow: number;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
interface SlidesData {
|
|
46
|
+
all: Slide[];
|
|
47
|
+
mode: SlideMode;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface CarouselProps {
|
|
51
|
+
name: string;
|
|
52
|
+
code: string;
|
|
53
|
+
scrollMode: string;
|
|
54
|
+
autoplay: boolean;
|
|
55
|
+
autoplayInterval: number;
|
|
56
|
+
pauseOnHover: boolean;
|
|
57
|
+
speed: number;
|
|
58
|
+
height: string;
|
|
59
|
+
vertical: boolean;
|
|
60
|
+
sliderArrowVisible: boolean;
|
|
61
|
+
pagingDotVisible: boolean;
|
|
62
|
+
objectFit: string;
|
|
63
|
+
slides: SlidesData[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface CarouselComponentMainProps {
|
|
67
|
+
props: CarouselProps;
|
|
68
|
+
deviceMode?: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const CarouselComponent: React.FC<CarouselComponentMainProps> = ({
|
|
72
|
+
props,
|
|
73
|
+
deviceMode = 'web'
|
|
74
|
+
}) => {
|
|
75
|
+
const [currentSlide, setCurrentSlide] = useState(0);
|
|
76
|
+
const [isPlaying, setIsPlaying] = useState(props.autoplay);
|
|
77
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
78
|
+
|
|
79
|
+
const slides = props.slides[0].all;
|
|
80
|
+
const totalSlides = slides.length;
|
|
81
|
+
|
|
82
|
+
// Function to go to next slide (advance by 1 to support wrap pairs)
|
|
83
|
+
const nextSlide = useCallback(() => {
|
|
84
|
+
setCurrentSlide((prev) => (prev + 1) % totalSlides);
|
|
85
|
+
}, [totalSlides]);
|
|
86
|
+
|
|
87
|
+
// Function to go to previous slide
|
|
88
|
+
const prevSlide = useCallback(() => {
|
|
89
|
+
setCurrentSlide((prev) => (prev - 1 + totalSlides) % totalSlides);
|
|
90
|
+
}, [totalSlides]);
|
|
91
|
+
|
|
92
|
+
// Function to go to specific slide start index
|
|
93
|
+
const goToSlide = (index: number) => {
|
|
94
|
+
setCurrentSlide(index % totalSlides);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Handle autoplay
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
if (!props.autoplay || !isPlaying || isHovered) return;
|
|
100
|
+
|
|
101
|
+
const interval = setInterval(() => {
|
|
102
|
+
nextSlide();
|
|
103
|
+
}, props.autoplayInterval);
|
|
104
|
+
|
|
105
|
+
return () => clearInterval(interval);
|
|
106
|
+
}, [props.autoplay, props.autoplayInterval, isPlaying, isHovered, nextSlide]);
|
|
107
|
+
|
|
108
|
+
// Handle pause on hover
|
|
109
|
+
const handleMouseEnter = () => {
|
|
110
|
+
setIsHovered(true);
|
|
111
|
+
if (props.pauseOnHover) {
|
|
112
|
+
setIsPlaying(false);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const handleMouseLeave = () => {
|
|
117
|
+
setIsHovered(false);
|
|
118
|
+
if (props.pauseOnHover) {
|
|
119
|
+
setIsPlaying(true);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Get current mode settings
|
|
124
|
+
const getCurrentMode = () => {
|
|
125
|
+
switch(deviceMode) {
|
|
126
|
+
case 'mobileweb':
|
|
127
|
+
return props.slides[0].mode.mobileweb;
|
|
128
|
+
case 'mobileapp':
|
|
129
|
+
return props.slides[0].mode.mobileapp;
|
|
130
|
+
case 'tablet':
|
|
131
|
+
return props.slides[0].mode.tablet;
|
|
132
|
+
case 'web':
|
|
133
|
+
default:
|
|
134
|
+
return props.slides[0].mode.web;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const currentMode = getCurrentMode();
|
|
139
|
+
const slidesToShow = currentMode.slidesToShow;
|
|
140
|
+
|
|
141
|
+
const resolveAssetUrl = (url?: string) => {
|
|
142
|
+
if (!url) return '';
|
|
143
|
+
if (url.startsWith('http://') || url.startsWith('https://')) return url;
|
|
144
|
+
return `${Linodeurl}${url}`;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const buildLinkForSlide = (slide: Slide) => {
|
|
148
|
+
console.warn(slide)
|
|
149
|
+
switch (slide.link_type) {
|
|
150
|
+
case 'NONE':
|
|
151
|
+
return null;
|
|
152
|
+
case 'EXTERNALLINK':
|
|
153
|
+
return `${slide.external_link?.url || ''}`;
|
|
154
|
+
case 'PRODUCT': {
|
|
155
|
+
const product: any = slide.product || {};
|
|
156
|
+
const pdType = product?.pd_type || product?.product_type;
|
|
157
|
+
const code = product?.code || '';
|
|
158
|
+
if (!code) return null;
|
|
159
|
+
|
|
160
|
+
return pdType === 'VARIANT'
|
|
161
|
+
? `/variant/${code}`
|
|
162
|
+
: `model/${code}`;
|
|
163
|
+
}
|
|
164
|
+
case 'TAG': {
|
|
165
|
+
const tag: any = slide.tag || {};
|
|
166
|
+
if (!tag.code) return null;
|
|
167
|
+
return `/tag/${tag?.code}`;
|
|
168
|
+
}
|
|
169
|
+
case 'PAGE': {
|
|
170
|
+
const page: any = slide.page || {};
|
|
171
|
+
const pageId = page._id
|
|
172
|
+
const pgType=page.page_type
|
|
173
|
+
return `/page/${pageId}?type=${pgType}`;
|
|
174
|
+
}
|
|
175
|
+
default:
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const handleSlideClick = (slide: Slide) => {
|
|
181
|
+
const link = buildLinkForSlide(slide);
|
|
182
|
+
if (link === null) return;
|
|
183
|
+
if (typeof link === 'string') {
|
|
184
|
+
if (link.startsWith('http')) {
|
|
185
|
+
window.open(link, '_blank');
|
|
186
|
+
} else {
|
|
187
|
+
window.location.href = link;
|
|
188
|
+
}
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const queryString = Object.entries((link as any).query || {})
|
|
192
|
+
.map(([key, value]) => `${key}=${encodeURIComponent(value as string)}`)
|
|
193
|
+
.join('&');
|
|
194
|
+
const fullPath = `${(link as any).pathname}${queryString ? `?${queryString}` : ''}`;
|
|
195
|
+
window.location.href = fullPath;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
<div
|
|
200
|
+
className="carousel-container"
|
|
201
|
+
style={{
|
|
202
|
+
position: 'relative',
|
|
203
|
+
width: '100%',
|
|
204
|
+
overflow: 'hidden'
|
|
205
|
+
}}
|
|
206
|
+
onMouseEnter={handleMouseEnter}
|
|
207
|
+
onMouseLeave={handleMouseLeave}
|
|
208
|
+
>
|
|
209
|
+
{/* Slides container */}
|
|
210
|
+
<div
|
|
211
|
+
className="carousel-slides"
|
|
212
|
+
style={{
|
|
213
|
+
display: 'flex',
|
|
214
|
+
width: '100%',
|
|
215
|
+
transition: `opacity ${props.speed}ms ease`,
|
|
216
|
+
flexDirection: props.vertical ? 'column' : 'row'
|
|
217
|
+
}}
|
|
218
|
+
>
|
|
219
|
+
{Array.from({ length: slidesToShow }).map((_, offset) => {
|
|
220
|
+
const slide = slides[(currentSlide + offset) % totalSlides];
|
|
221
|
+
const clickable = buildLinkForSlide(slide) !== null;
|
|
222
|
+
return (
|
|
223
|
+
<div
|
|
224
|
+
key={`${slide.sl_id}-${offset}`}
|
|
225
|
+
className="carousel-slide"
|
|
226
|
+
style={{
|
|
227
|
+
flex: `0 0 ${100 / slidesToShow}%`,
|
|
228
|
+
width: props.vertical ? '100%' : `${100 / slidesToShow}%`,
|
|
229
|
+
overflow: 'hidden',
|
|
230
|
+
display: 'block',
|
|
231
|
+
cursor: clickable ? 'pointer' : 'default'
|
|
232
|
+
}}
|
|
233
|
+
onClick={clickable ? () => handleSlideClick(slide) : undefined}
|
|
234
|
+
>
|
|
235
|
+
{slide.sl_type === 'video' ? (
|
|
236
|
+
<video
|
|
237
|
+
src={resolveAssetUrl(slide.video?.url)}
|
|
238
|
+
style={{ width: '100%', height: 'auto', display: 'block' }}
|
|
239
|
+
autoPlay={props.autoplay}
|
|
240
|
+
muted
|
|
241
|
+
loop
|
|
242
|
+
controls={!props.autoplay}
|
|
243
|
+
/>
|
|
244
|
+
) : (
|
|
245
|
+
<img
|
|
246
|
+
src={resolveAssetUrl(slide.image?.url)}
|
|
247
|
+
alt={slide.image?.alt || ''}
|
|
248
|
+
style={{ width: '100%', height: 'auto', display: 'block' }}
|
|
249
|
+
/>
|
|
250
|
+
)}
|
|
251
|
+
</div>
|
|
252
|
+
);
|
|
253
|
+
})}
|
|
254
|
+
|
|
255
|
+
</div>
|
|
256
|
+
|
|
257
|
+
{/* Navigation arrows */}
|
|
258
|
+
{props.sliderArrowVisible && totalSlides > slidesToShow && (
|
|
259
|
+
<>
|
|
260
|
+
<button
|
|
261
|
+
className="carousel-arrow carousel-arrow-prev"
|
|
262
|
+
onClick={prevSlide}
|
|
263
|
+
style={{
|
|
264
|
+
position: 'absolute',
|
|
265
|
+
...(props.vertical
|
|
266
|
+
? { top: '10px', left: '50%', transform: 'translateX(-50%)' }
|
|
267
|
+
: { top: '50%', left: '1px', transform: 'translateY(-50%)' }),
|
|
268
|
+
zIndex: 10,
|
|
269
|
+
background: 'white',
|
|
270
|
+
color: 'black',
|
|
271
|
+
border: '2px solid white',
|
|
272
|
+
boxShadow: '0 2px 6px rgba(0,0,0,0.5)',
|
|
273
|
+
borderRadius: '15%',
|
|
274
|
+
width: '40px',
|
|
275
|
+
height: '60px',
|
|
276
|
+
cursor: 'pointer',
|
|
277
|
+
display: 'flex',
|
|
278
|
+
alignItems: 'center',
|
|
279
|
+
justifyContent: 'center',
|
|
280
|
+
fontSize: '20px',
|
|
281
|
+
fontWeight: 'bold',
|
|
282
|
+
transition: 'transform 0.2s ease-in-out',
|
|
283
|
+
|
|
284
|
+
}}
|
|
285
|
+
>
|
|
286
|
+
{props.vertical ? '˄' : '‹'}
|
|
287
|
+
</button>
|
|
288
|
+
|
|
289
|
+
<button
|
|
290
|
+
className="carousel-arrow carousel-arrow-next"
|
|
291
|
+
onClick={nextSlide}
|
|
292
|
+
style={{
|
|
293
|
+
position: 'absolute',
|
|
294
|
+
...(props.vertical
|
|
295
|
+
? { bottom: '10px', left: '50%', transform: 'translateX(-50%)' }
|
|
296
|
+
: { top: '50%', right: '1px', transform: 'translateY(-50%)' }),
|
|
297
|
+
zIndex: 10,
|
|
298
|
+
background: 'white',
|
|
299
|
+
color: 'black',
|
|
300
|
+
border: '2px solid white',
|
|
301
|
+
boxShadow: '0 2px 6px rgba(0,0,0,0.5)',
|
|
302
|
+
borderRadius: '15%',
|
|
303
|
+
width: '40px',
|
|
304
|
+
height: '60px',
|
|
305
|
+
cursor: 'pointer',
|
|
306
|
+
display: 'flex',
|
|
307
|
+
alignItems: 'center',
|
|
308
|
+
justifyContent: 'center',
|
|
309
|
+
fontSize: '20px',
|
|
310
|
+
fontWeight: 'bold',
|
|
311
|
+
transition: 'transform 0.2s ease-in-out',
|
|
312
|
+
}}
|
|
313
|
+
>
|
|
314
|
+
{props.vertical ? '˅' : '›'}
|
|
315
|
+
</button>
|
|
316
|
+
|
|
317
|
+
</>
|
|
318
|
+
)}
|
|
319
|
+
|
|
320
|
+
{/* Pagination dots */}
|
|
321
|
+
{props.pagingDotVisible && totalSlides > 1 && (
|
|
322
|
+
<div
|
|
323
|
+
className="carousel-dots"
|
|
324
|
+
style={{
|
|
325
|
+
position: 'absolute',
|
|
326
|
+
...(props.vertical
|
|
327
|
+
? { top: '50%', right: '10px', transform: 'translateY(-50%)', bottom: 'auto', left: 'auto' }
|
|
328
|
+
: { bottom: '15px', left: '0', right: '0' }
|
|
329
|
+
),
|
|
330
|
+
display: 'flex',
|
|
331
|
+
justifyContent: 'center',
|
|
332
|
+
alignItems: 'center',
|
|
333
|
+
flexDirection: props.vertical ? 'column' : 'row',
|
|
334
|
+
gap: '8px',
|
|
335
|
+
zIndex: 100,
|
|
336
|
+
padding: props.vertical ? '8px 6px' : '6px 10px',
|
|
337
|
+
|
|
338
|
+
borderRadius: props.vertical ? '12px' : '16px'
|
|
339
|
+
}}
|
|
340
|
+
>
|
|
341
|
+
{Array.from({ length: totalSlides }).map((_, index) => (
|
|
342
|
+
<button
|
|
343
|
+
key={index}
|
|
344
|
+
aria-label={`Go to slide ${index + 1}`}
|
|
345
|
+
className={`carousel-dot ${index === currentSlide ? 'active' : ''}`}
|
|
346
|
+
onClick={() => goToSlide(index)}
|
|
347
|
+
style={{
|
|
348
|
+
width: index === currentSlide ? '12px' : '10px',
|
|
349
|
+
height: index === currentSlide ? '12px' : '10px',
|
|
350
|
+
borderRadius: '50%',
|
|
351
|
+
border: 'none',
|
|
352
|
+
background: index === currentSlide ? '#000' : '#ccc',
|
|
353
|
+
cursor: 'pointer',
|
|
354
|
+
transition: 'all 200ms ease'
|
|
355
|
+
}}
|
|
356
|
+
/>
|
|
357
|
+
))}
|
|
358
|
+
</div>
|
|
359
|
+
)}
|
|
360
|
+
</div>
|
|
361
|
+
);
|
|
362
|
+
};
|
|
363
|
+
|
|
366
364
|
export default CarouselComponent;
|