@growth-angels/ds-core 1.13.0 → 1.14.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.
|
@@ -1,18 +1,159 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Button } from "../../atoms/atoms";
|
|
3
|
+
import { useBreakpointObserver } from "../../hooks/useBreakPointObserver";
|
|
3
4
|
import { useReactAdapter } from "../../hooks/useReactAdaptater";
|
|
4
|
-
import { Swiper, SwiperSlide } from "swiper/react";
|
|
5
|
-
import { Navigation, Pagination, A11y } from "swiper/modules";
|
|
6
|
-
import "swiper/css";
|
|
7
|
-
import "swiper/css/navigation";
|
|
8
|
-
import "swiper/css/pagination";
|
|
9
5
|
export const Carousel = (props) => {
|
|
10
|
-
const { children, slidesPerView = {
|
|
11
|
-
const { useRef, Children } = useReactAdapter();
|
|
12
|
-
const
|
|
6
|
+
const { children, slidesPerView = { sm: 1, md: 2, lg: 3, xl: 4 }, spaceBetween = 20, navigation, pagination, context, hasPagination, hasNavigation, loop = false, } = props;
|
|
7
|
+
const { useEffect, useState, useRef, Children } = useReactAdapter();
|
|
8
|
+
const trackRef = useRef(null);
|
|
9
|
+
const isNavigatingRef = useRef(false);
|
|
13
10
|
const slides = Children.toArray(children);
|
|
14
|
-
const
|
|
15
|
-
|
|
11
|
+
const calculatePages = (totalSlides = 0, slidesPerView = 0, step = 1) => {
|
|
12
|
+
return Math.max(1, Math.ceil((totalSlides - slidesPerView) / step) + 1);
|
|
13
|
+
};
|
|
14
|
+
const [activeSlideIndex, setActiveSlideIndex] = useState(0);
|
|
15
|
+
const [activeDOMIndex, setActiveDOMIndex] = useState(0);
|
|
16
|
+
const [isAtEnd, setIsAtEnd] = useState(false);
|
|
17
|
+
const [isAtStart, setIsAtStart] = useState(true);
|
|
18
|
+
const [totalPages, setTotalPages] = useState(0);
|
|
19
|
+
const [isOverflowing, setIsOverflowing] = useState(false);
|
|
20
|
+
const displayNavigation = hasNavigation && (loop || isOverflowing);
|
|
21
|
+
const displayPagination = hasPagination && (loop || isOverflowing);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (loop) {
|
|
24
|
+
setIsAtStart(false);
|
|
25
|
+
setIsAtEnd(false);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
setIsAtStart(activeSlideIndex === 0);
|
|
29
|
+
setIsAtEnd(activeSlideIndex === totalPages - 1);
|
|
30
|
+
}
|
|
31
|
+
}, [activeSlideIndex, totalPages, loop]);
|
|
32
|
+
const breakpoint = useBreakpointObserver({ sm: 768, md: 992, lg: 1200, xl: 1400 });
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
setTotalPages(calculatePages(slides.length, slidesPerView[breakpoint], 1));
|
|
35
|
+
}, [breakpoint, slidesPerView]);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const track = trackRef.current;
|
|
38
|
+
if (!track)
|
|
39
|
+
return;
|
|
40
|
+
const handleScroll = () => {
|
|
41
|
+
if (loop) {
|
|
42
|
+
const slideWidth = track.scrollWidth / (slides.length * 3);
|
|
43
|
+
const scrollLeft = track.scrollLeft;
|
|
44
|
+
// Repositionnement infini (uniquement si pas en navigation)
|
|
45
|
+
if (!isNavigatingRef.current) {
|
|
46
|
+
if (scrollLeft <= slideWidth * slides.length) {
|
|
47
|
+
track.scrollLeft = scrollLeft + slideWidth * slides.length;
|
|
48
|
+
}
|
|
49
|
+
else if (scrollLeft >= slideWidth * slides.length * 2) {
|
|
50
|
+
track.scrollLeft = scrollLeft - slideWidth * slides.length;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const scrollPosition = track.scrollLeft + slideWidth / 2;
|
|
54
|
+
const domIndex = Math.floor(scrollPosition / slideWidth);
|
|
55
|
+
const index = domIndex % slides.length;
|
|
56
|
+
setActiveDOMIndex(domIndex);
|
|
57
|
+
setActiveSlideIndex(index);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const slideWidth = track.scrollWidth / slides.length;
|
|
61
|
+
const scrollPosition = track.scrollLeft + slideWidth / 2;
|
|
62
|
+
const index = Math.floor(scrollPosition / slideWidth);
|
|
63
|
+
setActiveSlideIndex(Math.min(index, slides.length - 1));
|
|
64
|
+
setActiveDOMIndex(index);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
track.addEventListener("scroll", handleScroll);
|
|
68
|
+
// Position initiale au milieu en mode loop
|
|
69
|
+
if (loop) {
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
const slideWidth = track.scrollWidth / (slides.length * 3);
|
|
72
|
+
track.scrollLeft = slideWidth * slides.length;
|
|
73
|
+
}, 0);
|
|
74
|
+
}
|
|
75
|
+
return () => track.removeEventListener("scroll", handleScroll);
|
|
76
|
+
}, [slides.length, loop]);
|
|
77
|
+
// Check if the slides overflow the container
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
const track = trackRef.current;
|
|
80
|
+
if (!track)
|
|
81
|
+
return;
|
|
82
|
+
const handleResize = () => {
|
|
83
|
+
setIsOverflowing(track.scrollWidth > track.clientWidth);
|
|
84
|
+
};
|
|
85
|
+
handleResize(); // Initial check
|
|
86
|
+
window.addEventListener("resize", handleResize);
|
|
87
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
88
|
+
}, [loop]);
|
|
89
|
+
const style = Object.fromEntries(Object.entries(slidesPerView).map(([key, value]) => [`--ga-ds-slides-per-view-${key}`, `${value}`]));
|
|
90
|
+
const goPrev = () => {
|
|
91
|
+
if (!trackRef.current)
|
|
92
|
+
return;
|
|
93
|
+
const allSlides = trackRef.current.querySelectorAll(".ga-ds-carousel__slide");
|
|
94
|
+
if (activeSlideIndex === 0 && !loop)
|
|
95
|
+
return;
|
|
96
|
+
isNavigatingRef.current = true;
|
|
97
|
+
if (loop) {
|
|
98
|
+
// En mode loop, on utilise l'index DOM actuel
|
|
99
|
+
allSlides[activeDOMIndex - 1]?.scrollIntoView({
|
|
100
|
+
behavior: "smooth",
|
|
101
|
+
block: "nearest",
|
|
102
|
+
inline: "start",
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
const nextIndex = activeSlideIndex - 1;
|
|
107
|
+
allSlides[nextIndex]?.scrollIntoView({
|
|
108
|
+
behavior: "smooth",
|
|
109
|
+
block: "nearest",
|
|
110
|
+
inline: "start",
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
setTimeout(() => {
|
|
114
|
+
isNavigatingRef.current = false;
|
|
115
|
+
}, 600);
|
|
116
|
+
};
|
|
117
|
+
const goNext = () => {
|
|
118
|
+
if (!trackRef.current)
|
|
119
|
+
return;
|
|
120
|
+
const allSlides = trackRef.current.querySelectorAll(".ga-ds-carousel__slide");
|
|
121
|
+
isNavigatingRef.current = true;
|
|
122
|
+
if (loop) {
|
|
123
|
+
// En mode loop, on utilise l'index DOM actuel
|
|
124
|
+
allSlides[activeDOMIndex + 1]?.scrollIntoView({
|
|
125
|
+
behavior: "smooth",
|
|
126
|
+
block: "nearest",
|
|
127
|
+
inline: "start",
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
const totalSlides = allSlides.length;
|
|
132
|
+
if (activeSlideIndex >= totalSlides - 1)
|
|
133
|
+
return;
|
|
134
|
+
if (isAtEnd)
|
|
135
|
+
return;
|
|
136
|
+
const nextIndex = activeSlideIndex + 1;
|
|
137
|
+
allSlides[nextIndex]?.scrollIntoView({
|
|
138
|
+
behavior: "smooth",
|
|
139
|
+
block: "nearest",
|
|
140
|
+
inline: "start",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
setTimeout(() => {
|
|
144
|
+
isNavigatingRef.current = false;
|
|
145
|
+
}, 600);
|
|
146
|
+
};
|
|
147
|
+
const goTo = (nextIndex) => {
|
|
148
|
+
if (!trackRef.current)
|
|
149
|
+
return;
|
|
150
|
+
const slides = trackRef.current.querySelectorAll(".ga-ds-carousel__slide");
|
|
151
|
+
slides[nextIndex]?.scrollIntoView({
|
|
152
|
+
behavior: "smooth",
|
|
153
|
+
block: "nearest",
|
|
154
|
+
inline: "start",
|
|
155
|
+
});
|
|
156
|
+
};
|
|
16
157
|
const classes = ["ga-ds-carousel"];
|
|
17
158
|
if (props.extraClassNames) {
|
|
18
159
|
if (typeof props.extraClassNames === "string") {
|
|
@@ -22,31 +163,19 @@ export const Carousel = (props) => {
|
|
|
22
163
|
classes.push(...props.extraClassNames);
|
|
23
164
|
}
|
|
24
165
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
992: { slidesPerView: slidesPerView.md || 2 },
|
|
29
|
-
1200: { slidesPerView: slidesPerView.lg || 3 },
|
|
30
|
-
1440: { slidesPerView: slidesPerView.xl || 4 },
|
|
31
|
-
};
|
|
32
|
-
return (_jsxs("div", { className: classes.join(" "), children: [navigation?.positionY === "top" && displayNavigation && _jsx(CarouselNavigation, {}), _jsx(Swiper, { modules: [Navigation, Pagination, A11y], spaceBetween: spaceBetween, breakpoints: breakpoints, loop: loop, navigation: displayNavigation
|
|
33
|
-
? {
|
|
34
|
-
prevEl: ".ga-ds-carousel__button--prev",
|
|
35
|
-
nextEl: ".ga-ds-carousel__button--next",
|
|
36
|
-
}
|
|
37
|
-
: false, pagination: displayPagination && pagination
|
|
38
|
-
? {
|
|
39
|
-
clickable: pagination.clickable,
|
|
40
|
-
el: ".ga-ds-carousel__dots",
|
|
41
|
-
bulletClass: "ga-ds-carousel__dot",
|
|
42
|
-
bulletActiveClass: "ga-ds-carousel__dot--active",
|
|
43
|
-
}
|
|
44
|
-
: false, onSwiper: (swiper) => {
|
|
45
|
-
swiperRef.current = swiper;
|
|
46
|
-
}, className: "ga-ds-carousel__track", children: context === "wp-editor"
|
|
166
|
+
return (_jsxs("div", { className: classes.join(" "), style: style, children: [navigation?.positionY === "top" && displayNavigation && (_jsx(CarouselNavigation, { goPrev: goPrev, goNext: goNext, isAtStart: isAtStart, isAtEnd: isAtEnd })), _jsx("div", { className: "ga-ds-carousel__track", ref: trackRef, style: {
|
|
167
|
+
"--ga-ds-space-between": `${spaceBetween / 10}rem`,
|
|
168
|
+
}, children: context === "wp-editor"
|
|
47
169
|
? children
|
|
48
|
-
: slides.map((child, index) =>
|
|
170
|
+
: slides.map((child, index) => {
|
|
171
|
+
return (_jsx("div", { className: `ga-ds-carousel__slide ${index === activeDOMIndex ? "ga-ds-carousel__slide--active" : ""}`, children: child }, index));
|
|
172
|
+
}) }), _jsxs("div", { className: "ga-ds-carousel__navigation", children: [pagination && displayPagination && (_jsx(CarouselPagination, { totalPages: totalPages, activeSlideIndex: activeSlideIndex, goTo: goTo, clickable: pagination.clickable })), displayNavigation && navigation?.positionY === "bottom" && (_jsx(CarouselNavigation, { goPrev: goPrev, goNext: goNext, isAtStart: isAtStart, isAtEnd: isAtEnd }))] })] }));
|
|
173
|
+
};
|
|
174
|
+
const CarouselNavigation = ({ goPrev, goNext, isAtStart, isAtEnd, }) => {
|
|
175
|
+
return (_jsxs("div", { className: `ga-ds-carousel__arrows`, children: [_jsx(Button, { extraClassNames: ["ga-ds-carousel__button", "ga-ds-carousel__button--prev"], icon: "chevron-left", onClick: goPrev, disabled: isAtStart }), _jsx(Button, { extraClassNames: ["ga-ds-carousel__button", "ga-ds-carousel__button--next"], icon: "chevron-right", onClick: goNext, disabled: isAtEnd })] }));
|
|
49
176
|
};
|
|
50
|
-
const
|
|
51
|
-
return (
|
|
177
|
+
const CarouselPagination = ({ totalPages, activeSlideIndex, goTo, clickable, }) => {
|
|
178
|
+
return (_jsx("div", { className: "ga-ds-carousel__dots", children: Array.from({ length: totalPages }, (_, index) => index).map((_, index) => (_jsx("span", { className: `ga-ds-carousel__dot ${index === activeSlideIndex ? "ga-ds-carousel__dot--active" : ""}`, onClick: () => clickable && goTo(index), style: {
|
|
179
|
+
cursor: clickable ? "pointer" : "default",
|
|
180
|
+
} }, index))) }));
|
|
52
181
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@growth-angels/ds-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"description": "Design system by Growth Angels",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -37,8 +37,7 @@
|
|
|
37
37
|
"build-storybook": "storybook build"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@growth-angels/foundation": "^1.4.1"
|
|
41
|
-
"swiper": "^11.1.15"
|
|
40
|
+
"@growth-angels/foundation": "^1.4.1"
|
|
42
41
|
},
|
|
43
42
|
"peerDependencies": {
|
|
44
43
|
"react": ">=18"
|
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import { Button } from "../../atoms/atoms"
|
|
2
|
+
import { useBreakpointObserver } from "../../hooks/useBreakPointObserver"
|
|
2
3
|
import { useReactAdapter } from "../../hooks/useReactAdaptater"
|
|
3
4
|
import { CarouselProps } from "./Carousel.types"
|
|
4
|
-
import { Swiper, SwiperSlide } from "swiper/react"
|
|
5
|
-
import { Navigation, Pagination, A11y } from "swiper/modules"
|
|
6
|
-
import type { Swiper as SwiperType } from "swiper"
|
|
7
|
-
import "swiper/css"
|
|
8
|
-
import "swiper/css/navigation"
|
|
9
|
-
import "swiper/css/pagination"
|
|
10
5
|
|
|
11
6
|
export const Carousel = (props: CarouselProps) => {
|
|
12
7
|
const {
|
|
13
8
|
children,
|
|
14
|
-
slidesPerView = {
|
|
9
|
+
slidesPerView = { sm: 1, md: 2, lg: 3, xl: 4 },
|
|
15
10
|
spaceBetween = 20,
|
|
16
11
|
navigation,
|
|
17
12
|
pagination,
|
|
@@ -20,12 +15,173 @@ export const Carousel = (props: CarouselProps) => {
|
|
|
20
15
|
hasNavigation,
|
|
21
16
|
loop = false,
|
|
22
17
|
} = props
|
|
23
|
-
const { useRef, Children } = useReactAdapter()
|
|
24
|
-
const
|
|
18
|
+
const { useEffect, useState, useRef, Children } = useReactAdapter()
|
|
19
|
+
const trackRef = useRef<HTMLDivElement>(null)
|
|
20
|
+
const isNavigatingRef = useRef(false)
|
|
25
21
|
const slides = Children.toArray(children)
|
|
26
22
|
|
|
27
|
-
const
|
|
28
|
-
|
|
23
|
+
const calculatePages = (totalSlides = 0, slidesPerView = 0, step = 1) => {
|
|
24
|
+
return Math.max(1, Math.ceil((totalSlides - slidesPerView) / step) + 1)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const [activeSlideIndex, setActiveSlideIndex] = useState(0)
|
|
28
|
+
const [activeDOMIndex, setActiveDOMIndex] = useState(0)
|
|
29
|
+
const [isAtEnd, setIsAtEnd] = useState(false)
|
|
30
|
+
const [isAtStart, setIsAtStart] = useState(true)
|
|
31
|
+
const [totalPages, setTotalPages] = useState(0)
|
|
32
|
+
const [isOverflowing, setIsOverflowing] = useState(false)
|
|
33
|
+
|
|
34
|
+
const displayNavigation = hasNavigation && (loop || isOverflowing)
|
|
35
|
+
const displayPagination = hasPagination && (loop || isOverflowing)
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (loop) {
|
|
39
|
+
setIsAtStart(false)
|
|
40
|
+
setIsAtEnd(false)
|
|
41
|
+
} else {
|
|
42
|
+
setIsAtStart(activeSlideIndex === 0)
|
|
43
|
+
setIsAtEnd(activeSlideIndex === totalPages - 1)
|
|
44
|
+
}
|
|
45
|
+
}, [activeSlideIndex, totalPages, loop])
|
|
46
|
+
|
|
47
|
+
const breakpoint = useBreakpointObserver({ sm: 768, md: 992, lg: 1200, xl: 1400 })
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
setTotalPages(calculatePages(slides.length, slidesPerView[breakpoint], 1))
|
|
51
|
+
}, [breakpoint, slidesPerView])
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
const track = trackRef.current
|
|
55
|
+
if (!track) return
|
|
56
|
+
|
|
57
|
+
const handleScroll = () => {
|
|
58
|
+
if (loop) {
|
|
59
|
+
const slideWidth = track.scrollWidth / (slides.length * 3)
|
|
60
|
+
const scrollLeft = track.scrollLeft
|
|
61
|
+
|
|
62
|
+
// Repositionnement infini (uniquement si pas en navigation)
|
|
63
|
+
if (!isNavigatingRef.current) {
|
|
64
|
+
if (scrollLeft <= slideWidth * slides.length) {
|
|
65
|
+
track.scrollLeft = scrollLeft + slideWidth * slides.length
|
|
66
|
+
} else if (scrollLeft >= slideWidth * slides.length * 2) {
|
|
67
|
+
track.scrollLeft = scrollLeft - slideWidth * slides.length
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const scrollPosition = track.scrollLeft + slideWidth / 2
|
|
72
|
+
const domIndex = Math.floor(scrollPosition / slideWidth)
|
|
73
|
+
const index = domIndex % slides.length
|
|
74
|
+
setActiveDOMIndex(domIndex)
|
|
75
|
+
setActiveSlideIndex(index)
|
|
76
|
+
} else {
|
|
77
|
+
const slideWidth = track.scrollWidth / slides.length
|
|
78
|
+
const scrollPosition = track.scrollLeft + slideWidth / 2
|
|
79
|
+
const index = Math.floor(scrollPosition / slideWidth)
|
|
80
|
+
setActiveSlideIndex(Math.min(index, slides.length - 1))
|
|
81
|
+
setActiveDOMIndex(index)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
track.addEventListener("scroll", handleScroll)
|
|
86
|
+
|
|
87
|
+
// Position initiale au milieu en mode loop
|
|
88
|
+
if (loop) {
|
|
89
|
+
setTimeout(() => {
|
|
90
|
+
const slideWidth = track.scrollWidth / (slides.length * 3)
|
|
91
|
+
track.scrollLeft = slideWidth * slides.length
|
|
92
|
+
}, 0)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return () => track.removeEventListener("scroll", handleScroll)
|
|
96
|
+
}, [slides.length, loop])
|
|
97
|
+
|
|
98
|
+
// Check if the slides overflow the container
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
const track = trackRef.current
|
|
101
|
+
if (!track) return
|
|
102
|
+
|
|
103
|
+
const handleResize = () => {
|
|
104
|
+
setIsOverflowing(track.scrollWidth > track.clientWidth)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
handleResize() // Initial check
|
|
108
|
+
|
|
109
|
+
window.addEventListener("resize", handleResize)
|
|
110
|
+
return () => window.removeEventListener("resize", handleResize)
|
|
111
|
+
}, [loop])
|
|
112
|
+
|
|
113
|
+
const style = Object.fromEntries(
|
|
114
|
+
Object.entries(slidesPerView).map(([key, value]) => [`--ga-ds-slides-per-view-${key}`, `${value}`])
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
const goPrev = () => {
|
|
118
|
+
if (!trackRef.current) return
|
|
119
|
+
const allSlides = trackRef.current.querySelectorAll(".ga-ds-carousel__slide")
|
|
120
|
+
if (activeSlideIndex === 0 && !loop) return
|
|
121
|
+
|
|
122
|
+
isNavigatingRef.current = true
|
|
123
|
+
|
|
124
|
+
if (loop) {
|
|
125
|
+
// En mode loop, on utilise l'index DOM actuel
|
|
126
|
+
allSlides[activeDOMIndex - 1]?.scrollIntoView({
|
|
127
|
+
behavior: "smooth",
|
|
128
|
+
block: "nearest",
|
|
129
|
+
inline: "start",
|
|
130
|
+
})
|
|
131
|
+
} else {
|
|
132
|
+
const nextIndex = activeSlideIndex - 1
|
|
133
|
+
allSlides[nextIndex]?.scrollIntoView({
|
|
134
|
+
behavior: "smooth",
|
|
135
|
+
block: "nearest",
|
|
136
|
+
inline: "start",
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
setTimeout(() => {
|
|
141
|
+
isNavigatingRef.current = false
|
|
142
|
+
}, 600)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const goNext = () => {
|
|
146
|
+
if (!trackRef.current) return
|
|
147
|
+
const allSlides = trackRef.current.querySelectorAll(".ga-ds-carousel__slide")
|
|
148
|
+
|
|
149
|
+
isNavigatingRef.current = true
|
|
150
|
+
|
|
151
|
+
if (loop) {
|
|
152
|
+
// En mode loop, on utilise l'index DOM actuel
|
|
153
|
+
allSlides[activeDOMIndex + 1]?.scrollIntoView({
|
|
154
|
+
behavior: "smooth",
|
|
155
|
+
block: "nearest",
|
|
156
|
+
inline: "start",
|
|
157
|
+
})
|
|
158
|
+
} else {
|
|
159
|
+
const totalSlides = allSlides.length
|
|
160
|
+
if (activeSlideIndex >= totalSlides - 1) return
|
|
161
|
+
if (isAtEnd) return
|
|
162
|
+
|
|
163
|
+
const nextIndex = activeSlideIndex + 1
|
|
164
|
+
allSlides[nextIndex]?.scrollIntoView({
|
|
165
|
+
behavior: "smooth",
|
|
166
|
+
block: "nearest",
|
|
167
|
+
inline: "start",
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
setTimeout(() => {
|
|
172
|
+
isNavigatingRef.current = false
|
|
173
|
+
}, 600)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const goTo = (nextIndex: number) => {
|
|
177
|
+
if (!trackRef.current) return
|
|
178
|
+
const slides = trackRef.current.querySelectorAll(".ga-ds-carousel__slide")
|
|
179
|
+
slides[nextIndex]?.scrollIntoView({
|
|
180
|
+
behavior: "smooth",
|
|
181
|
+
block: "nearest",
|
|
182
|
+
inline: "start",
|
|
183
|
+
})
|
|
184
|
+
}
|
|
29
185
|
|
|
30
186
|
const classes = ["ga-ds-carousel"]
|
|
31
187
|
|
|
@@ -37,68 +193,103 @@ export const Carousel = (props: CarouselProps) => {
|
|
|
37
193
|
}
|
|
38
194
|
}
|
|
39
195
|
|
|
40
|
-
const breakpoints = {
|
|
41
|
-
0: { slidesPerView: slidesPerView.xs || slidesPerView.sm || 1 },
|
|
42
|
-
576: { slidesPerView: slidesPerView.sm || 1 },
|
|
43
|
-
992: { slidesPerView: slidesPerView.md || 2 },
|
|
44
|
-
1200: { slidesPerView: slidesPerView.lg || 3 },
|
|
45
|
-
1440: { slidesPerView: slidesPerView.xl || 4 },
|
|
46
|
-
}
|
|
47
|
-
|
|
48
196
|
return (
|
|
49
|
-
<div className={classes.join(" ")}>
|
|
50
|
-
{navigation?.positionY === "top" && displayNavigation &&
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
spaceBetween={spaceBetween}
|
|
55
|
-
breakpoints={breakpoints}
|
|
56
|
-
loop={loop}
|
|
57
|
-
navigation={
|
|
58
|
-
displayNavigation
|
|
59
|
-
? {
|
|
60
|
-
prevEl: ".ga-ds-carousel__button--prev",
|
|
61
|
-
nextEl: ".ga-ds-carousel__button--next",
|
|
62
|
-
}
|
|
63
|
-
: false
|
|
64
|
-
}
|
|
65
|
-
pagination={
|
|
66
|
-
displayPagination && pagination
|
|
67
|
-
? {
|
|
68
|
-
clickable: pagination.clickable,
|
|
69
|
-
el: ".ga-ds-carousel__dots",
|
|
70
|
-
bulletClass: "ga-ds-carousel__dot",
|
|
71
|
-
bulletActiveClass: "ga-ds-carousel__dot--active",
|
|
72
|
-
}
|
|
73
|
-
: false
|
|
74
|
-
}
|
|
75
|
-
onSwiper={(swiper: SwiperType) => {
|
|
76
|
-
swiperRef.current = swiper
|
|
77
|
-
}}
|
|
197
|
+
<div className={classes.join(" ")} style={style}>
|
|
198
|
+
{navigation?.positionY === "top" && displayNavigation && (
|
|
199
|
+
<CarouselNavigation goPrev={goPrev} goNext={goNext} isAtStart={isAtStart} isAtEnd={isAtEnd} />
|
|
200
|
+
)}
|
|
201
|
+
<div
|
|
78
202
|
className="ga-ds-carousel__track"
|
|
203
|
+
ref={trackRef}
|
|
204
|
+
style={
|
|
205
|
+
{
|
|
206
|
+
"--ga-ds-space-between": `${spaceBetween / 10}rem`,
|
|
207
|
+
} as React.CSSProperties
|
|
208
|
+
}
|
|
79
209
|
>
|
|
80
210
|
{context === "wp-editor"
|
|
81
211
|
? children
|
|
82
|
-
: slides.map((child, index) =>
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
212
|
+
: slides.map((child, index) => {
|
|
213
|
+
return (
|
|
214
|
+
<div
|
|
215
|
+
key={index}
|
|
216
|
+
className={`ga-ds-carousel__slide ${index === activeDOMIndex ? "ga-ds-carousel__slide--active" : ""}`}
|
|
217
|
+
>
|
|
218
|
+
{child}
|
|
219
|
+
</div>
|
|
220
|
+
)
|
|
221
|
+
})}
|
|
222
|
+
</div>
|
|
88
223
|
|
|
89
224
|
<div className="ga-ds-carousel__navigation">
|
|
90
|
-
{displayPagination &&
|
|
91
|
-
|
|
225
|
+
{pagination && displayPagination && (
|
|
226
|
+
<CarouselPagination
|
|
227
|
+
totalPages={totalPages}
|
|
228
|
+
activeSlideIndex={activeSlideIndex}
|
|
229
|
+
goTo={goTo}
|
|
230
|
+
clickable={pagination.clickable}
|
|
231
|
+
/>
|
|
232
|
+
)}
|
|
233
|
+
{displayNavigation && navigation?.positionY === "bottom" && (
|
|
234
|
+
<CarouselNavigation goPrev={goPrev} goNext={goNext} isAtStart={isAtStart} isAtEnd={isAtEnd} />
|
|
235
|
+
)}
|
|
92
236
|
</div>
|
|
93
237
|
</div>
|
|
94
238
|
)
|
|
95
239
|
}
|
|
96
240
|
|
|
97
|
-
const CarouselNavigation = (
|
|
241
|
+
const CarouselNavigation = ({
|
|
242
|
+
goPrev,
|
|
243
|
+
goNext,
|
|
244
|
+
isAtStart,
|
|
245
|
+
isAtEnd,
|
|
246
|
+
}: {
|
|
247
|
+
goPrev: () => void
|
|
248
|
+
goNext: () => void
|
|
249
|
+
isAtStart: boolean
|
|
250
|
+
isAtEnd: boolean
|
|
251
|
+
}) => {
|
|
98
252
|
return (
|
|
99
253
|
<div className={`ga-ds-carousel__arrows`}>
|
|
100
|
-
<Button
|
|
101
|
-
|
|
254
|
+
<Button
|
|
255
|
+
extraClassNames={["ga-ds-carousel__button", "ga-ds-carousel__button--prev"]}
|
|
256
|
+
icon="chevron-left"
|
|
257
|
+
onClick={goPrev}
|
|
258
|
+
disabled={isAtStart}
|
|
259
|
+
/>
|
|
260
|
+
<Button
|
|
261
|
+
extraClassNames={["ga-ds-carousel__button", "ga-ds-carousel__button--next"]}
|
|
262
|
+
icon="chevron-right"
|
|
263
|
+
onClick={goNext}
|
|
264
|
+
disabled={isAtEnd}
|
|
265
|
+
/>
|
|
266
|
+
</div>
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const CarouselPagination = ({
|
|
271
|
+
totalPages,
|
|
272
|
+
activeSlideIndex,
|
|
273
|
+
goTo,
|
|
274
|
+
clickable,
|
|
275
|
+
}: {
|
|
276
|
+
totalPages: number
|
|
277
|
+
activeSlideIndex: number
|
|
278
|
+
goTo: (nextIndex: number) => void
|
|
279
|
+
clickable: boolean
|
|
280
|
+
}) => {
|
|
281
|
+
return (
|
|
282
|
+
<div className="ga-ds-carousel__dots">
|
|
283
|
+
{Array.from({ length: totalPages }, (_, index) => index).map((_, index) => (
|
|
284
|
+
<span
|
|
285
|
+
key={index}
|
|
286
|
+
className={`ga-ds-carousel__dot ${index === activeSlideIndex ? "ga-ds-carousel__dot--active" : ""}`}
|
|
287
|
+
onClick={() => clickable && goTo(index)}
|
|
288
|
+
style={{
|
|
289
|
+
cursor: clickable ? "pointer" : "default",
|
|
290
|
+
}}
|
|
291
|
+
></span>
|
|
292
|
+
))}
|
|
102
293
|
</div>
|
|
103
294
|
)
|
|
104
295
|
}
|