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.
@@ -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
- height: props.height,
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
- height: '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
- height: props.vertical ? `${100 / slidesToShow}%` : '100%',
229
- overflow: 'hidden',
230
- display: 'flex',
231
- alignItems: 'center',
232
- justifyContent: 'center',
233
- cursor: clickable ? 'pointer' : 'default'
234
- }}
235
- onClick={clickable ? () => handleSlideClick(slide) : undefined}
236
- >
237
- {slide.sl_type === 'video' ? (
238
- <video
239
- src={resolveAssetUrl(slide.video?.url)}
240
- style={{ width: '100%', height: '100%', objectFit: props.objectFit as any }}
241
- autoPlay={props.autoplay}
242
- muted
243
- loop
244
- controls={!props.autoplay}
245
- />
246
- ) : (
247
- <img
248
- src={resolveAssetUrl(slide.image?.url)}
249
- alt={slide.image?.alt || ''}
250
- style={{ width: '100%', height: '100%', objectFit: props.objectFit as any }}
251
- />
252
- )}
253
- </div>
254
- );
255
- })}
256
-
257
- </div>
258
-
259
- {/* Navigation arrows */}
260
- {props.sliderArrowVisible && totalSlides > slidesToShow && (
261
- <>
262
- <button
263
- className="carousel-arrow carousel-arrow-prev"
264
- onClick={prevSlide}
265
- style={{
266
- position: 'absolute',
267
- ...(props.vertical
268
- ? { top: '10px', left: '50%', transform: 'translateX(-50%)' }
269
- : { top: '50%', left: '1px', transform: 'translateY(-50%)' }),
270
- zIndex: 10,
271
- background: 'white',
272
- color: 'black',
273
- border: '2px solid white',
274
- boxShadow: '0 2px 6px rgba(0,0,0,0.5)',
275
- borderRadius: '15%',
276
- width: '40px',
277
- height: '60px',
278
- cursor: 'pointer',
279
- display: 'flex',
280
- alignItems: 'center',
281
- justifyContent: 'center',
282
- fontSize: '20px',
283
- fontWeight: 'bold',
284
- transition: 'transform 0.2s ease-in-out',
285
-
286
- }}
287
- >
288
- {props.vertical ? '˄' : '‹'}
289
- </button>
290
-
291
- <button
292
- className="carousel-arrow carousel-arrow-next"
293
- onClick={nextSlide}
294
- style={{
295
- position: 'absolute',
296
- ...(props.vertical
297
- ? { bottom: '10px', left: '50%', transform: 'translateX(-50%)' }
298
- : { top: '50%', right: '1px', transform: 'translateY(-50%)' }),
299
- zIndex: 10,
300
- background: 'white',
301
- color: 'black',
302
- border: '2px solid white',
303
- boxShadow: '0 2px 6px rgba(0,0,0,0.5)',
304
- borderRadius: '15%',
305
- width: '40px',
306
- height: '60px',
307
- cursor: 'pointer',
308
- display: 'flex',
309
- alignItems: 'center',
310
- justifyContent: 'center',
311
- fontSize: '20px',
312
- fontWeight: 'bold',
313
- transition: 'transform 0.2s ease-in-out',
314
- }}
315
- >
316
- {props.vertical ? '˅' : '›'}
317
- </button>
318
-
319
- </>
320
- )}
321
-
322
- {/* Pagination dots */}
323
- {props.pagingDotVisible && totalSlides > 1 && (
324
- <div
325
- className="carousel-dots"
326
- style={{
327
- position: 'absolute',
328
- ...(props.vertical
329
- ? { top: '50%', right: '10px', transform: 'translateY(-50%)', bottom: 'auto', left: 'auto' }
330
- : { bottom: '15px', left: '0', right: '0' }
331
- ),
332
- display: 'flex',
333
- justifyContent: 'center',
334
- alignItems: 'center',
335
- flexDirection: props.vertical ? 'column' : 'row',
336
- gap: '8px',
337
- zIndex: 100,
338
- padding: props.vertical ? '8px 6px' : '6px 10px',
339
-
340
- borderRadius: props.vertical ? '12px' : '16px'
341
- }}
342
- >
343
- {Array.from({ length: totalSlides }).map((_, index) => (
344
- <button
345
- key={index}
346
- aria-label={`Go to slide ${index + 1}`}
347
- className={`carousel-dot ${index === currentSlide ? 'active' : ''}`}
348
- onClick={() => goToSlide(index)}
349
- style={{
350
- width: index === currentSlide ? '12px' : '10px',
351
- height: index === currentSlide ? '12px' : '10px',
352
- borderRadius: '50%',
353
- border: 'none',
354
- background: index === currentSlide ? '#000' : '#ccc',
355
- cursor: 'pointer',
356
- transition: 'all 200ms ease'
357
- }}
358
- />
359
- ))}
360
- </div>
361
- )}
362
- </div>
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;