@growth-angels/ds-core 1.12.9 → 1.13.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.
- package/dist/hooks/useReactAdaptater.d.ts +1 -0
- package/dist/hooks/useReactAdaptater.js +2 -1
- package/dist/organisms/Carousel/Carousel.d.ts +3 -0
- package/dist/organisms/Carousel/Carousel.js +35 -164
- package/dist/organisms/Carousel/Carousel.types.d.ts +1 -0
- package/package.json +3 -2
- package/src/hooks/useReactAdaptater.ts +2 -1
- package/src/organisms/Carousel/Carousel.tsx +59 -250
- package/src/organisms/Carousel/Carousel.types.ts +1 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
export const useReactAdapter = () => {
|
|
2
3
|
// Check if WordPress element is available (WordPress context)
|
|
3
4
|
if (typeof window !== 'undefined' && window.wp?.element) {
|
|
@@ -5,5 +6,5 @@ export const useReactAdapter = () => {
|
|
|
5
6
|
}
|
|
6
7
|
// Fallback for non-WordPress environments (Storybook, tests, SSR)
|
|
7
8
|
// This will bundle React only in non-WordPress builds
|
|
8
|
-
return
|
|
9
|
+
return React;
|
|
9
10
|
};
|
|
@@ -1,159 +1,18 @@
|
|
|
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";
|
|
4
3
|
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";
|
|
5
9
|
export const Carousel = (props) => {
|
|
6
|
-
const { children, slidesPerView = { sm: 1, md: 2, lg: 3, xl: 4 }, spaceBetween = 20, navigation, pagination, context, hasPagination, hasNavigation, loop = false, } = props;
|
|
7
|
-
const {
|
|
8
|
-
const
|
|
9
|
-
const isNavigatingRef = useRef(false);
|
|
10
|
+
const { children, slidesPerView = { xs: 1, sm: 1, md: 2, lg: 3, xl: 4 }, spaceBetween = 20, navigation, pagination, context, hasPagination, hasNavigation, loop = false, } = props;
|
|
11
|
+
const { useRef, Children } = useReactAdapter();
|
|
12
|
+
const swiperRef = useRef(null);
|
|
10
13
|
const slides = Children.toArray(children);
|
|
11
|
-
const
|
|
12
|
-
|
|
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
|
-
};
|
|
14
|
+
const displayNavigation = hasNavigation;
|
|
15
|
+
const displayPagination = hasPagination;
|
|
157
16
|
const classes = ["ga-ds-carousel"];
|
|
158
17
|
if (props.extraClassNames) {
|
|
159
18
|
if (typeof props.extraClassNames === "string") {
|
|
@@ -163,19 +22,31 @@ export const Carousel = (props) => {
|
|
|
163
22
|
classes.push(...props.extraClassNames);
|
|
164
23
|
}
|
|
165
24
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
25
|
+
const breakpoints = {
|
|
26
|
+
0: { slidesPerView: slidesPerView.xs || slidesPerView.sm || 1 },
|
|
27
|
+
576: { slidesPerView: slidesPerView.sm || 1 },
|
|
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"
|
|
169
47
|
? children
|
|
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 })] }));
|
|
48
|
+
: slides.map((child, index) => (_jsx(SwiperSlide, { className: "ga-ds-carousel__slide", children: child }, index))) }), _jsxs("div", { className: "ga-ds-carousel__navigation", children: [displayPagination && _jsx("div", { className: "ga-ds-carousel__dots" }), displayNavigation && navigation?.positionY === "bottom" && _jsx(CarouselNavigation, {})] })] }));
|
|
176
49
|
};
|
|
177
|
-
const
|
|
178
|
-
return (
|
|
179
|
-
cursor: clickable ? "pointer" : "default",
|
|
180
|
-
} }, index))) }));
|
|
50
|
+
const CarouselNavigation = () => {
|
|
51
|
+
return (_jsxs("div", { className: `ga-ds-carousel__arrows`, children: [_jsx(Button, { extraClassNames: ["ga-ds-carousel__button", "ga-ds-carousel__button--prev"], icon: "chevron-left" }), _jsx(Button, { extraClassNames: ["ga-ds-carousel__button", "ga-ds-carousel__button--next"], icon: "chevron-right" })] }));
|
|
181
52
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@growth-angels/ds-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "Design system by Growth Angels",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
"build-storybook": "storybook build"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@growth-angels/foundation": "^1.4.1"
|
|
40
|
+
"@growth-angels/foundation": "^1.4.1",
|
|
41
|
+
"swiper": "^11.1.15"
|
|
41
42
|
},
|
|
42
43
|
"peerDependencies": {
|
|
43
44
|
"react": ">=18"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from 'react'
|
|
1
2
|
// @ts-ignore - React types from window.wp.element
|
|
2
3
|
type ReactType = typeof import('react')
|
|
3
4
|
|
|
@@ -27,6 +28,6 @@ export const useReactAdapter = (): ExtendedReactType => {
|
|
|
27
28
|
|
|
28
29
|
// Fallback for non-WordPress environments (Storybook, tests, SSR)
|
|
29
30
|
// This will bundle React only in non-WordPress builds
|
|
30
|
-
return
|
|
31
|
+
return React as ExtendedReactType
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { Button } from "../../atoms/atoms"
|
|
2
|
-
import { useBreakpointObserver } from "../../hooks/useBreakPointObserver"
|
|
3
2
|
import { useReactAdapter } from "../../hooks/useReactAdaptater"
|
|
4
3
|
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"
|
|
5
10
|
|
|
6
11
|
export const Carousel = (props: CarouselProps) => {
|
|
7
12
|
const {
|
|
8
13
|
children,
|
|
9
|
-
slidesPerView = { sm: 1, md: 2, lg: 3, xl: 4 },
|
|
14
|
+
slidesPerView = { xs: 1, sm: 1, md: 2, lg: 3, xl: 4 },
|
|
10
15
|
spaceBetween = 20,
|
|
11
16
|
navigation,
|
|
12
17
|
pagination,
|
|
@@ -15,173 +20,12 @@ export const Carousel = (props: CarouselProps) => {
|
|
|
15
20
|
hasNavigation,
|
|
16
21
|
loop = false,
|
|
17
22
|
} = props
|
|
18
|
-
const {
|
|
19
|
-
const
|
|
20
|
-
const isNavigatingRef = useRef(false)
|
|
23
|
+
const { useRef, Children } = useReactAdapter()
|
|
24
|
+
const swiperRef = useRef<SwiperType | null>(null)
|
|
21
25
|
const slides = Children.toArray(children)
|
|
22
26
|
|
|
23
|
-
const
|
|
24
|
-
|
|
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
|
-
}
|
|
27
|
+
const displayNavigation = hasNavigation
|
|
28
|
+
const displayPagination = hasPagination
|
|
185
29
|
|
|
186
30
|
const classes = ["ga-ds-carousel"]
|
|
187
31
|
|
|
@@ -193,103 +37,68 @@ export const Carousel = (props: CarouselProps) => {
|
|
|
193
37
|
}
|
|
194
38
|
}
|
|
195
39
|
|
|
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
|
+
|
|
196
48
|
return (
|
|
197
|
-
<div className={classes.join(" ")}
|
|
198
|
-
{navigation?.positionY === "top" && displayNavigation &&
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
49
|
+
<div className={classes.join(" ")}>
|
|
50
|
+
{navigation?.positionY === "top" && displayNavigation && <CarouselNavigation />}
|
|
51
|
+
|
|
52
|
+
<Swiper
|
|
53
|
+
modules={[Navigation, Pagination, A11y]}
|
|
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
|
|
208
74
|
}
|
|
75
|
+
onSwiper={(swiper: SwiperType) => {
|
|
76
|
+
swiperRef.current = swiper
|
|
77
|
+
}}
|
|
78
|
+
className="ga-ds-carousel__track"
|
|
209
79
|
>
|
|
210
80
|
{context === "wp-editor"
|
|
211
81
|
? children
|
|
212
|
-
: slides.map((child, index) =>
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
{child}
|
|
219
|
-
</div>
|
|
220
|
-
)
|
|
221
|
-
})}
|
|
222
|
-
</div>
|
|
82
|
+
: slides.map((child, index) => (
|
|
83
|
+
<SwiperSlide key={index} className="ga-ds-carousel__slide">
|
|
84
|
+
{child}
|
|
85
|
+
</SwiperSlide>
|
|
86
|
+
))}
|
|
87
|
+
</Swiper>
|
|
223
88
|
|
|
224
89
|
<div className="ga-ds-carousel__navigation">
|
|
225
|
-
{
|
|
226
|
-
|
|
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
|
-
)}
|
|
90
|
+
{displayPagination && <div className="ga-ds-carousel__dots"></div>}
|
|
91
|
+
{displayNavigation && navigation?.positionY === "bottom" && <CarouselNavigation />}
|
|
236
92
|
</div>
|
|
237
93
|
</div>
|
|
238
94
|
)
|
|
239
95
|
}
|
|
240
96
|
|
|
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
|
-
}) => {
|
|
97
|
+
const CarouselNavigation = () => {
|
|
252
98
|
return (
|
|
253
99
|
<div className={`ga-ds-carousel__arrows`}>
|
|
254
|
-
<Button
|
|
255
|
-
|
|
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
|
-
))}
|
|
100
|
+
<Button extraClassNames={["ga-ds-carousel__button", "ga-ds-carousel__button--prev"]} icon="chevron-left" />
|
|
101
|
+
<Button extraClassNames={["ga-ds-carousel__button", "ga-ds-carousel__button--next"]} icon="chevron-right" />
|
|
293
102
|
</div>
|
|
294
103
|
)
|
|
295
104
|
}
|