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.
- 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/PageComponents/Visual-Components/TabComponent.tsx +526 -105
|
@@ -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
|
|
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
|
|
613
|
-
const
|
|
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
|
|
627
|
-
{
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
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=
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
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
|
-
|
|
659
|
-
</
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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
|
|
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
|
|
841
|
-
const
|
|
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
|
|
855
|
-
{
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
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
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
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
|
-
|
|
890
|
-
</
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
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
|
|
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
|
|
1037
|
-
const
|
|
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
|
|
1053
|
-
{
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
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
|
-
|
|
1069
|
-
|
|
1070
|
-
}
|
|
1071
|
-
break;
|
|
1490
|
+
);
|
|
1491
|
+
}
|
|
1492
|
+
break;
|
|
1072
1493
|
|
|
1073
1494
|
default:
|
|
1074
1495
|
return (
|