tee3apps-cms-sdk-react 0.0.13 → 0.0.15

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.
@@ -326,6 +326,33 @@ const TabComponent: React.FC<TabComponentMainProps> = ({ props, deviceMode = 'we
326
326
  setCarouselIndex(0);
327
327
  };
328
328
 
329
+ // Helper function to get sliding window indices for grid layouts
330
+ const getSlidingWindowIndices = (totalItems: number, itemsPerView: number, currentIndex: number): number[] => {
331
+ if (totalItems <= itemsPerView) {
332
+ // If total items <= items per view, show all items
333
+ return Array.from({ length: totalItems }, (_, i) => i);
334
+ }
335
+
336
+ // Calculate max possible index for sliding window
337
+ // For sliding window, we can slide until the last item is shown
338
+ // Max index is when we show the last itemsPerView items
339
+ const maxIndex = totalItems - itemsPerView;
340
+ const clampedIndex = Math.min(Math.max(0, currentIndex), maxIndex);
341
+
342
+ // Sliding window: start from clampedIndex, show itemsPerView items
343
+ return Array.from({ length: itemsPerView }, (_, i) => clampedIndex + i);
344
+ };
345
+
346
+ // Helper function to calculate max carousel index for grid layouts
347
+ const getMaxCarouselIndex = (totalItems: number, itemsPerView: number): number => {
348
+ if (totalItems <= itemsPerView) {
349
+ return 0; // No carousel needed
350
+ }
351
+ // For sliding window, max index is when we show the last itemsPerView items
352
+ // This allows us to slide through all items with overlapping windows
353
+ return Math.max(0, totalItems - itemsPerView);
354
+ };
355
+
329
356
  const getCurrentLayout = (tab: TabItem): string => {
330
357
  switch (deviceMode) {
331
358
  case 'mobileweb': return tab.mode.mobileweb.layout;
@@ -599,7 +626,7 @@ const TabComponent: React.FC<TabComponentMainProps> = ({ props, deviceMode = 'we
599
626
  );
600
627
  }
601
628
 
602
- // Other layouts: grid display with limited images
629
+ // Other layouts: grid display with sliding window carousel
603
630
  const getMaxImages = () => {
604
631
  switch (layout) {
605
632
  case '1x2': return 2;
@@ -609,8 +636,24 @@ const TabComponent: React.FC<TabComponentMainProps> = ({ props, deviceMode = 'we
609
636
  }
610
637
  };
611
638
 
612
- const maxImages = getMaxImages();
613
- const displayImages = images.slice(0, maxImages);
639
+ const itemsPerView = getMaxImages();
640
+ const maxCarouselIndex = getMaxCarouselIndex(images.length, itemsPerView);
641
+ const displayIndices = getSlidingWindowIndices(images.length, itemsPerView, carouselIndex);
642
+ const displayImages = displayIndices.map(idx => images[idx]);
643
+ const needsCarousel = images.length > itemsPerView;
644
+
645
+ // Navigation handlers specific to this layout
646
+ const handleNextImage = () => {
647
+ if (carouselIndex < maxCarouselIndex) {
648
+ setCarouselIndex((prev) => Math.min(prev + 1, maxCarouselIndex));
649
+ }
650
+ };
651
+
652
+ const handlePrevImage = () => {
653
+ if (carouselIndex > 0) {
654
+ setCarouselIndex((prev) => Math.max(prev - 1, 0));
655
+ }
656
+ };
614
657
 
615
658
  // Create dynamic content style for GROUPIMAGE
616
659
  const groupImageContentStyle = {
@@ -623,46 +666,147 @@ const TabComponent: React.FC<TabComponentMainProps> = ({ props, deviceMode = 'we
623
666
  };
624
667
 
625
668
  return (
626
- <div style={groupImageContentStyle} className='media-box'>
627
- {displayImages.map((image, index) => {
628
- const linkUrl = buildLinkForSlide(image);
629
- const imageElement = (
630
- <div key={index} className="media-box">
631
- <img
632
- src={getImageUrl(
633
- tab.tabContentGroupImage.type === 'DYNAMIC'
634
- ? image.image.url
635
- : image.attr?.url
636
- )}
637
- alt={
638
- tab.tabContentGroupImage.type === 'DYNAMIC'
639
- ? image.image.alt
640
- : image.attr?.alt
669
+ <div
670
+ style={{
671
+ position: 'relative',
672
+ width: '100%',
673
+ height: 'auto',
674
+ overflow: 'hidden',
675
+ display: 'flex',
676
+ alignItems: 'center',
677
+ justifyContent: 'center',
678
+ }}
679
+ >
680
+ <div style={groupImageContentStyle} className='media-box'>
681
+ {displayImages.map((image, displayIndex) => {
682
+ const originalIndex = displayIndices[displayIndex];
683
+ const linkUrl = buildLinkForSlide(image);
684
+ const imageElement = (
685
+ <div key={originalIndex} className="media-box">
686
+ <img
687
+ src={getImageUrl(
688
+ tab.tabContentGroupImage.type === 'DYNAMIC'
689
+ ? image.image.url
690
+ : image.attr?.url
691
+ )}
692
+ alt={
693
+ tab.tabContentGroupImage.type === 'DYNAMIC'
694
+ ? image.image.alt
695
+ : image.attr?.alt
696
+ }
697
+ className="media-content"
698
+ />
699
+ </div>
700
+ );
701
+
702
+ return linkUrl ? (
703
+ <a
704
+ key={originalIndex}
705
+ href={linkUrl}
706
+ target={
707
+ image.link_type === 'EXTERNALLINK'
708
+ ? image.external_link?.target || '_blank'
709
+ : '_self'
641
710
  }
642
- className="media-content"
643
- />
644
- </div>
645
- );
646
-
647
- return linkUrl ? (
648
- <a
649
- key={index}
650
- href={linkUrl}
651
- target={
652
- image.link_type === 'EXTERNALLINK'
653
- ? image.external_link?.target || '_blank'
654
- : '_self'
655
- }
656
- className='media-box'
711
+ className='media-box'
712
+ >
713
+ {imageElement}
714
+ </a>
715
+ ) : (
716
+ <div key={originalIndex} style={{ width: '100%', height: '100%' }}>
717
+ {imageElement}
718
+ </div>
719
+ );
720
+ })}
721
+ </div>
722
+
723
+ {/* Carousel Navigation for Grid Layouts */}
724
+ {needsCarousel && (
725
+ <>
726
+ {/* Previous Button */}
727
+ <button
728
+ onClick={handlePrevImage}
729
+ disabled={carouselIndex === 0}
730
+ style={{
731
+ position: 'absolute',
732
+ left: '10px',
733
+ top: '50%',
734
+ transform: 'translateY(-50%)',
735
+ background: 'rgba(0,0,0,0.5)',
736
+ color: 'white',
737
+ border: 'none',
738
+ borderRadius: '50%',
739
+ width: '40px',
740
+ height: '40px',
741
+ cursor: carouselIndex === 0 ? 'not-allowed' : 'pointer',
742
+ opacity: carouselIndex === 0 ? 0.5 : 1,
743
+ display: 'flex',
744
+ alignItems: 'center',
745
+ justifyContent: 'center',
746
+ fontSize: '18px',
747
+ zIndex: 10,
748
+ }}
657
749
  >
658
- {imageElement}
659
- </a>
660
- ) : (
661
- <div key={index} style={{ width: '100%', height: '100%' }}>
662
- {imageElement}
750
+
751
+ </button>
752
+
753
+ {/* Next Button */}
754
+ <button
755
+ onClick={handleNextImage}
756
+ disabled={carouselIndex >= maxCarouselIndex}
757
+ style={{
758
+ position: 'absolute',
759
+ right: '10px',
760
+ top: '50%',
761
+ transform: 'translateY(-50%)',
762
+ background: 'rgba(0,0,0,0.5)',
763
+ color: 'white',
764
+ border: 'none',
765
+ borderRadius: '50%',
766
+ width: '40px',
767
+ height: '40px',
768
+ cursor: carouselIndex >= maxCarouselIndex ? 'not-allowed' : 'pointer',
769
+ opacity: carouselIndex >= maxCarouselIndex ? 0.5 : 1,
770
+ display: 'flex',
771
+ alignItems: 'center',
772
+ justifyContent: 'center',
773
+ fontSize: '18px',
774
+ zIndex: 10,
775
+ }}
776
+ >
777
+
778
+ </button>
779
+
780
+ {/* Dots Indicator */}
781
+ <div
782
+ style={{
783
+ position: 'absolute',
784
+ bottom: '10px',
785
+ left: '50%',
786
+ transform: 'translateX(-50%)',
787
+ display: 'flex',
788
+ gap: '8px',
789
+ zIndex: 10,
790
+ }}
791
+ >
792
+ {Array.from({ length: maxCarouselIndex + 1 }, (_, index) => (
793
+ <button
794
+ key={index}
795
+ onClick={() => goToCarouselItem(index)}
796
+ style={{
797
+ width: index === carouselIndex ? '12px' : '8px',
798
+ height: index === carouselIndex ? '12px' : '8px',
799
+ borderRadius: '50%',
800
+ border: 'none',
801
+ background: index === carouselIndex ? 'white' : 'rgba(255,255,255,0.5)',
802
+ cursor: 'pointer',
803
+ transition: 'all 0.3s ease',
804
+ }}
805
+ />
806
+ ))}
663
807
  </div>
664
- );
665
- })}
808
+ </>
809
+ )}
666
810
  </div>
667
811
  );
668
812
 
@@ -827,7 +971,7 @@ const TabComponent: React.FC<TabComponentMainProps> = ({ props, deviceMode = 'we
827
971
  );
828
972
  }
829
973
 
830
- // Other layouts: grid display with limited videos
974
+ // Other layouts: grid display with sliding window carousel
831
975
  const getMaxVideos = () => {
832
976
  switch (layout) {
833
977
  case '1x2': return 2;
@@ -837,8 +981,24 @@ const TabComponent: React.FC<TabComponentMainProps> = ({ props, deviceMode = 'we
837
981
  }
838
982
  };
839
983
 
840
- const maxVideos = getMaxVideos();
841
- const displayVideos = videos.slice(0, maxVideos);
984
+ const itemsPerView = getMaxVideos();
985
+ const maxCarouselIndex = getMaxCarouselIndex(videos.length, itemsPerView);
986
+ const displayIndices = getSlidingWindowIndices(videos.length, itemsPerView, carouselIndex);
987
+ const displayVideos = displayIndices.map(idx => videos[idx]);
988
+ const needsCarousel = videos.length > itemsPerView;
989
+
990
+ // Navigation handlers specific to this layout
991
+ const handleNextVideo = () => {
992
+ if (carouselIndex < maxCarouselIndex) {
993
+ setCarouselIndex((prev) => Math.min(prev + 1, maxCarouselIndex));
994
+ }
995
+ };
996
+
997
+ const handlePrevVideo = () => {
998
+ if (carouselIndex > 0) {
999
+ setCarouselIndex((prev) => Math.max(prev - 1, 0));
1000
+ }
1001
+ };
842
1002
 
843
1003
  // Create dynamic content style for GROUPVIDEO
844
1004
  const groupVideoContentStyle = {
@@ -851,49 +1011,150 @@ const TabComponent: React.FC<TabComponentMainProps> = ({ props, deviceMode = 'we
851
1011
  };
852
1012
 
853
1013
  return (
854
- <div style={groupVideoContentStyle} className='media-box'>
855
- {displayVideos.map((video, index) => {
856
- const linkUrl = buildLinkForSlide(video);
857
-
858
- const videoElement = (
859
- <div className="media-box">
860
- <video
861
- src={getImageUrl(
862
- tab.tabContentGroupVideo.type === 'DYNAMIC'
863
- ? video.video?.url
864
- : video.attr?.url
865
- )}
866
- title={
867
- tab.tabContentGroupVideo.type === 'DYNAMIC'
868
- ? video.video?.alt || video.name
869
- : video.attr?.alt
870
- }
871
- className="media-content"
872
- controls
873
- style={{ width: '100%', height: 'auto', objectFit: 'cover' }}
874
- />
875
- </div>
876
- );
1014
+ <div
1015
+ style={{
1016
+ position: 'relative',
1017
+ width: '100%',
1018
+ height: 'auto',
1019
+ overflow: 'hidden',
1020
+ display: 'flex',
1021
+ alignItems: 'center',
1022
+ justifyContent: 'center',
1023
+ }}
1024
+ >
1025
+ <div style={groupVideoContentStyle} className='media-box'>
1026
+ {displayVideos.map((video, displayIndex) => {
1027
+ const originalIndex = displayIndices[displayIndex];
1028
+ const linkUrl = buildLinkForSlide(video);
1029
+
1030
+ const videoElement = (
1031
+ <div key={originalIndex} className="media-box">
1032
+ <video
1033
+ src={getImageUrl(
1034
+ tab.tabContentGroupVideo.type === 'DYNAMIC'
1035
+ ? video.video?.url
1036
+ : video.attr?.url
1037
+ )}
1038
+ title={
1039
+ tab.tabContentGroupVideo.type === 'DYNAMIC'
1040
+ ? video.video?.alt || video.name
1041
+ : video.attr?.alt
1042
+ }
1043
+ className="media-content"
1044
+ controls
1045
+ style={{ width: '100%', height: 'auto', objectFit: 'cover' }}
1046
+ />
1047
+ </div>
1048
+ );
877
1049
 
878
- return linkUrl ? (
879
- <a
880
- key={index}
881
- href={linkUrl}
882
- target={
883
- video.link_type === 'EXTERNALLINK'
884
- ? video.external_link?.target || '_blank'
885
- : '_self'
886
- }
887
- className='media-box'
1050
+ return linkUrl ? (
1051
+ <a
1052
+ key={originalIndex}
1053
+ href={linkUrl}
1054
+ target={
1055
+ video.link_type === 'EXTERNALLINK'
1056
+ ? video.external_link?.target || '_blank'
1057
+ : '_self'
1058
+ }
1059
+ className='media-box'
1060
+ >
1061
+ {videoElement}
1062
+ </a>
1063
+ ) : (
1064
+ <div key={originalIndex} style={{ width: '100%', height: '100%' }}>
1065
+ {videoElement}
1066
+ </div>
1067
+ );
1068
+ })}
1069
+ </div>
1070
+
1071
+ {/* Carousel Navigation for Grid Layouts */}
1072
+ {needsCarousel && (
1073
+ <>
1074
+ {/* Previous Button */}
1075
+ <button
1076
+ onClick={handlePrevVideo}
1077
+ disabled={carouselIndex === 0}
1078
+ style={{
1079
+ position: 'absolute',
1080
+ left: '10px',
1081
+ top: '50%',
1082
+ transform: 'translateY(-50%)',
1083
+ background: 'rgba(0,0,0,0.5)',
1084
+ color: 'white',
1085
+ border: 'none',
1086
+ borderRadius: '50%',
1087
+ width: '40px',
1088
+ height: '40px',
1089
+ cursor: carouselIndex === 0 ? 'not-allowed' : 'pointer',
1090
+ opacity: carouselIndex === 0 ? 0.5 : 1,
1091
+ display: 'flex',
1092
+ alignItems: 'center',
1093
+ justifyContent: 'center',
1094
+ fontSize: '18px',
1095
+ zIndex: 10,
1096
+ }}
888
1097
  >
889
- {videoElement}
890
- </a>
891
- ) : (
892
- <div key={index} style={{ width: '100%', height: '100%' }}>
893
- {videoElement}
1098
+
1099
+ </button>
1100
+
1101
+ {/* Next Button */}
1102
+ <button
1103
+ onClick={handleNextVideo}
1104
+ disabled={carouselIndex >= maxCarouselIndex}
1105
+ style={{
1106
+ position: 'absolute',
1107
+ right: '10px',
1108
+ top: '50%',
1109
+ transform: 'translateY(-50%)',
1110
+ background: 'rgba(0,0,0,0.5)',
1111
+ color: 'white',
1112
+ border: 'none',
1113
+ borderRadius: '50%',
1114
+ width: '40px',
1115
+ height: '40px',
1116
+ cursor: carouselIndex >= maxCarouselIndex ? 'not-allowed' : 'pointer',
1117
+ opacity: carouselIndex >= maxCarouselIndex ? 0.5 : 1,
1118
+ display: 'flex',
1119
+ alignItems: 'center',
1120
+ justifyContent: 'center',
1121
+ fontSize: '18px',
1122
+ zIndex: 10,
1123
+ }}
1124
+ >
1125
+
1126
+ </button>
1127
+
1128
+ {/* Dots Indicator */}
1129
+ <div
1130
+ style={{
1131
+ position: 'absolute',
1132
+ bottom: '10px',
1133
+ left: '50%',
1134
+ transform: 'translateX(-50%)',
1135
+ display: 'flex',
1136
+ gap: '8px',
1137
+ zIndex: 10,
1138
+ }}
1139
+ >
1140
+ {Array.from({ length: maxCarouselIndex + 1 }, (_, index) => (
1141
+ <button
1142
+ key={index}
1143
+ onClick={() => goToCarouselItem(index)}
1144
+ style={{
1145
+ width: index === carouselIndex ? '12px' : '8px',
1146
+ height: index === carouselIndex ? '12px' : '8px',
1147
+ borderRadius: '50%',
1148
+ border: 'none',
1149
+ background: index === carouselIndex ? 'white' : 'rgba(255,255,255,0.5)',
1150
+ cursor: 'pointer',
1151
+ transition: 'all 0.3s ease',
1152
+ }}
1153
+ />
1154
+ ))}
894
1155
  </div>
895
- );
896
- })}
1156
+ </>
1157
+ )}
897
1158
  </div>
898
1159
  );
899
1160
 
@@ -1023,7 +1284,7 @@ const TabComponent: React.FC<TabComponentMainProps> = ({ props, deviceMode = 'we
1023
1284
  );
1024
1285
  }
1025
1286
 
1026
- // Other layouts: Grid view with limited products
1287
+ // Other layouts: Grid view with sliding window carousel
1027
1288
  const getMaxProducts = () => {
1028
1289
  switch (layout) {
1029
1290
  case '1x2': return 2;
@@ -1033,8 +1294,45 @@ const TabComponent: React.FC<TabComponentMainProps> = ({ props, deviceMode = 'we
1033
1294
  }
1034
1295
  };
1035
1296
 
1036
- const maxProducts = getMaxProducts();
1037
- const displayProducts = products.slice(0, maxProducts);
1297
+ const itemsPerView = getMaxProducts();
1298
+ const maxCarouselIndex = getMaxCarouselIndex(products.length, itemsPerView);
1299
+ const displayIndices = getSlidingWindowIndices(products.length, itemsPerView, carouselIndex);
1300
+ const displayProducts = displayIndices.map(idx => products[idx]);
1301
+ const needsCarousel = products.length > itemsPerView;
1302
+
1303
+ // Debug logging
1304
+ console.log('GROUPPRODUCT Grid Layout:', {
1305
+ layout,
1306
+ totalProducts: products.length,
1307
+ itemsPerView,
1308
+ carouselIndex,
1309
+ maxCarouselIndex,
1310
+ displayIndices,
1311
+ needsCarousel
1312
+ });
1313
+
1314
+ // Navigation handlers specific to this layout
1315
+ const handleNextProduct = () => {
1316
+ console.log('Next clicked, current:', carouselIndex, 'max:', maxCarouselIndex);
1317
+ if (carouselIndex < maxCarouselIndex) {
1318
+ setCarouselIndex((prev) => {
1319
+ const next = Math.min(prev + 1, maxCarouselIndex);
1320
+ console.log('Setting carousel index to:', next);
1321
+ return next;
1322
+ });
1323
+ }
1324
+ };
1325
+
1326
+ const handlePrevProduct = () => {
1327
+ console.log('Prev clicked, current:', carouselIndex);
1328
+ if (carouselIndex > 0) {
1329
+ setCarouselIndex((prev) => {
1330
+ const next = Math.max(prev - 1, 0);
1331
+ console.log('Setting carousel index to:', next);
1332
+ return next;
1333
+ });
1334
+ }
1335
+ };
1038
1336
 
1039
1337
  // Create dynamic content style for GROUPPRODUCT
1040
1338
  const groupProductContentStyle = {
@@ -1049,26 +1347,149 @@ const TabComponent: React.FC<TabComponentMainProps> = ({ props, deviceMode = 'we
1049
1347
  };
1050
1348
 
1051
1349
  return (
1052
- <div style={groupProductContentStyle}>
1053
- {displayProducts.map((product, index) => (
1054
- <div
1055
- key={index}
1056
- style={{
1057
- width: '100%',
1058
- height: '100%',
1059
- display: 'flex',
1060
- alignItems: 'stretch',
1061
- justifyContent: 'center',
1062
- minHeight: '200px',
1063
- }}
1064
- >
1065
- <ProductCard product={product} layout={layout} />
1350
+ <div
1351
+ style={{
1352
+ position: 'relative',
1353
+ width: '100%',
1354
+ height: 'auto',
1355
+ overflow: 'hidden',
1356
+ display: 'flex',
1357
+ alignItems: 'center',
1358
+ justifyContent: 'center',
1359
+ }}
1360
+ >
1361
+ <div style={groupProductContentStyle}>
1362
+ {displayProducts.map((product, displayIndex) => {
1363
+ const originalIndex = displayIndices[displayIndex];
1364
+ return (
1365
+ <div
1366
+ key={originalIndex}
1367
+ style={{
1368
+ width: '100%',
1369
+ height: '100%',
1370
+ display: 'flex',
1371
+ alignItems: 'stretch',
1372
+ justifyContent: 'center',
1373
+ minHeight: '200px',
1374
+ }}
1375
+ >
1376
+ <ProductCard product={product} layout={layout} />
1377
+ </div>
1378
+ );
1379
+ })}
1380
+ </div>
1381
+
1382
+ {/* Carousel Navigation for Grid Layouts */}
1383
+ {needsCarousel && (
1384
+ <>
1385
+ {/* Previous Button */}
1386
+ <button
1387
+ onClick={(e) => {
1388
+ e.preventDefault();
1389
+ e.stopPropagation();
1390
+ handlePrevProduct();
1391
+ }}
1392
+ disabled={carouselIndex === 0}
1393
+ style={{
1394
+ position: 'absolute',
1395
+ left: '10px',
1396
+ top: '50%',
1397
+ transform: 'translateY(-50%)',
1398
+ background: carouselIndex === 0 ? 'rgba(0,0,0,0.3)' : 'rgba(0,0,0,0.7)',
1399
+ color: 'white',
1400
+ border: 'none',
1401
+ borderRadius: '50%',
1402
+ width: '40px',
1403
+ height: '40px',
1404
+ cursor: carouselIndex === 0 ? 'not-allowed' : 'pointer',
1405
+ opacity: carouselIndex === 0 ? 0.5 : 1,
1406
+ display: 'flex',
1407
+ alignItems: 'center',
1408
+ justifyContent: 'center',
1409
+ fontSize: '24px',
1410
+ fontWeight: 'bold',
1411
+ zIndex: 100,
1412
+ pointerEvents: 'auto',
1413
+ transition: 'all 0.3s ease',
1414
+ }}
1415
+ >
1416
+
1417
+ </button>
1418
+
1419
+ {/* Next Button */}
1420
+ <button
1421
+ onClick={(e) => {
1422
+ e.preventDefault();
1423
+ e.stopPropagation();
1424
+ handleNextProduct();
1425
+ }}
1426
+ disabled={carouselIndex >= maxCarouselIndex}
1427
+ style={{
1428
+ position: 'absolute',
1429
+ right: '10px',
1430
+ top: '50%',
1431
+ transform: 'translateY(-50%)',
1432
+ background: carouselIndex >= maxCarouselIndex ? 'rgba(0,0,0,0.3)' : 'rgba(0,0,0,0.7)',
1433
+ color: 'white',
1434
+ border: 'none',
1435
+ borderRadius: '50%',
1436
+ width: '40px',
1437
+ height: '40px',
1438
+ cursor: carouselIndex >= maxCarouselIndex ? 'not-allowed' : 'pointer',
1439
+ opacity: carouselIndex >= maxCarouselIndex ? 0.5 : 1,
1440
+ display: 'flex',
1441
+ alignItems: 'center',
1442
+ justifyContent: 'center',
1443
+ fontSize: '24px',
1444
+ fontWeight: 'bold',
1445
+ zIndex: 100,
1446
+ pointerEvents: 'auto',
1447
+ transition: 'all 0.3s ease',
1448
+ }}
1449
+ >
1450
+
1451
+ </button>
1452
+
1453
+ {/* Dots Indicator */}
1454
+ <div
1455
+ style={{
1456
+ position: 'absolute',
1457
+ bottom: '10px',
1458
+ left: '50%',
1459
+ transform: 'translateX(-50%)',
1460
+ display: 'flex',
1461
+ gap: '8px',
1462
+ zIndex: 100,
1463
+ pointerEvents: 'auto',
1464
+ }}
1465
+ >
1466
+ {Array.from({ length: maxCarouselIndex + 1 }, (_, index) => (
1467
+ <button
1468
+ key={index}
1469
+ onClick={(e) => {
1470
+ e.preventDefault();
1471
+ e.stopPropagation();
1472
+ goToCarouselItem(index);
1473
+ }}
1474
+ style={{
1475
+ width: index === carouselIndex ? '12px' : '8px',
1476
+ height: index === carouselIndex ? '12px' : '8px',
1477
+ borderRadius: '50%',
1478
+ border: '2px solid white',
1479
+ background: index === carouselIndex ? 'white' : 'rgba(255,255,255,0.6)',
1480
+ cursor: 'pointer',
1481
+ transition: 'all 0.3s ease',
1482
+ pointerEvents: 'auto',
1483
+ }}
1484
+ />
1485
+ ))}
1486
+ </div>
1487
+ </>
1488
+ )}
1066
1489
  </div>
1067
- ))}
1068
- </div>
1069
- );
1070
- }
1071
- break;
1490
+ );
1491
+ }
1492
+ break;
1072
1493
 
1073
1494
  default:
1074
1495
  return (