@growth-angels/ds-core 1.12.10 → 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 +33 -149
- 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 +57 -232
- 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,146 +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 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
14
|
const displayNavigation = hasNavigation;
|
|
20
15
|
const displayPagination = hasPagination;
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
if (loop) {
|
|
23
|
-
setIsAtStart(false);
|
|
24
|
-
setIsAtEnd(false);
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
setIsAtStart(activeSlideIndex === 0);
|
|
28
|
-
setIsAtEnd(activeSlideIndex === totalPages - 1);
|
|
29
|
-
}
|
|
30
|
-
}, [activeSlideIndex, totalPages, loop]);
|
|
31
|
-
const breakpoint = useBreakpointObserver({ sm: 768, md: 992, lg: 1200, xl: 1400 });
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
setTotalPages(calculatePages(slides.length, slidesPerView[breakpoint], 1));
|
|
34
|
-
}, [breakpoint, slidesPerView]);
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
const track = trackRef.current;
|
|
37
|
-
if (!track)
|
|
38
|
-
return;
|
|
39
|
-
const handleScroll = () => {
|
|
40
|
-
if (loop) {
|
|
41
|
-
const slideWidth = track.scrollWidth / (slides.length * 3);
|
|
42
|
-
const scrollLeft = track.scrollLeft;
|
|
43
|
-
// Repositionnement infini (uniquement si pas en navigation)
|
|
44
|
-
if (!isNavigatingRef.current) {
|
|
45
|
-
if (scrollLeft <= slideWidth * slides.length) {
|
|
46
|
-
track.scrollLeft = scrollLeft + slideWidth * slides.length;
|
|
47
|
-
}
|
|
48
|
-
else if (scrollLeft >= slideWidth * slides.length * 2) {
|
|
49
|
-
track.scrollLeft = scrollLeft - slideWidth * slides.length;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
const scrollPosition = track.scrollLeft + slideWidth / 2;
|
|
53
|
-
const domIndex = Math.floor(scrollPosition / slideWidth);
|
|
54
|
-
const index = domIndex % slides.length;
|
|
55
|
-
setActiveDOMIndex(domIndex);
|
|
56
|
-
setActiveSlideIndex(index);
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
const slideWidth = track.scrollWidth / slides.length;
|
|
60
|
-
const scrollPosition = track.scrollLeft + slideWidth / 2;
|
|
61
|
-
const index = Math.floor(scrollPosition / slideWidth);
|
|
62
|
-
setActiveSlideIndex(Math.min(index, slides.length - 1));
|
|
63
|
-
setActiveDOMIndex(index);
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
track.addEventListener("scroll", handleScroll);
|
|
67
|
-
// Position initiale au milieu en mode loop
|
|
68
|
-
if (loop) {
|
|
69
|
-
setTimeout(() => {
|
|
70
|
-
const slideWidth = track.scrollWidth / (slides.length * 3);
|
|
71
|
-
track.scrollLeft = slideWidth * slides.length;
|
|
72
|
-
}, 0);
|
|
73
|
-
}
|
|
74
|
-
return () => track.removeEventListener("scroll", handleScroll);
|
|
75
|
-
}, [slides.length, loop]);
|
|
76
|
-
const style = Object.fromEntries(Object.entries(slidesPerView).map(([key, value]) => [`--ga-ds-slides-per-view-${key}`, `${value}`]));
|
|
77
|
-
const goPrev = () => {
|
|
78
|
-
if (!trackRef.current)
|
|
79
|
-
return;
|
|
80
|
-
const allSlides = trackRef.current.querySelectorAll(".ga-ds-carousel__slide");
|
|
81
|
-
if (activeSlideIndex === 0 && !loop)
|
|
82
|
-
return;
|
|
83
|
-
isNavigatingRef.current = true;
|
|
84
|
-
if (loop) {
|
|
85
|
-
// En mode loop, on utilise l'index DOM actuel
|
|
86
|
-
allSlides[activeDOMIndex - 1]?.scrollIntoView({
|
|
87
|
-
behavior: "smooth",
|
|
88
|
-
block: "nearest",
|
|
89
|
-
inline: "start",
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
const nextIndex = activeSlideIndex - 1;
|
|
94
|
-
allSlides[nextIndex]?.scrollIntoView({
|
|
95
|
-
behavior: "smooth",
|
|
96
|
-
block: "nearest",
|
|
97
|
-
inline: "start",
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
setTimeout(() => {
|
|
101
|
-
isNavigatingRef.current = false;
|
|
102
|
-
}, 600);
|
|
103
|
-
};
|
|
104
|
-
const goNext = () => {
|
|
105
|
-
if (!trackRef.current)
|
|
106
|
-
return;
|
|
107
|
-
const allSlides = trackRef.current.querySelectorAll(".ga-ds-carousel__slide");
|
|
108
|
-
isNavigatingRef.current = true;
|
|
109
|
-
if (loop) {
|
|
110
|
-
// En mode loop, on utilise l'index DOM actuel
|
|
111
|
-
allSlides[activeDOMIndex + 1]?.scrollIntoView({
|
|
112
|
-
behavior: "smooth",
|
|
113
|
-
block: "nearest",
|
|
114
|
-
inline: "start",
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
const totalSlides = allSlides.length;
|
|
119
|
-
if (activeSlideIndex >= totalSlides - 1)
|
|
120
|
-
return;
|
|
121
|
-
if (isAtEnd)
|
|
122
|
-
return;
|
|
123
|
-
const nextIndex = activeSlideIndex + 1;
|
|
124
|
-
allSlides[nextIndex]?.scrollIntoView({
|
|
125
|
-
behavior: "smooth",
|
|
126
|
-
block: "nearest",
|
|
127
|
-
inline: "start",
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
setTimeout(() => {
|
|
131
|
-
isNavigatingRef.current = false;
|
|
132
|
-
}, 600);
|
|
133
|
-
};
|
|
134
|
-
const goTo = (nextIndex) => {
|
|
135
|
-
if (!trackRef.current)
|
|
136
|
-
return;
|
|
137
|
-
const slides = trackRef.current.querySelectorAll(".ga-ds-carousel__slide");
|
|
138
|
-
slides[nextIndex]?.scrollIntoView({
|
|
139
|
-
behavior: "smooth",
|
|
140
|
-
block: "nearest",
|
|
141
|
-
inline: "start",
|
|
142
|
-
});
|
|
143
|
-
};
|
|
144
16
|
const classes = ["ga-ds-carousel"];
|
|
145
17
|
if (props.extraClassNames) {
|
|
146
18
|
if (typeof props.extraClassNames === "string") {
|
|
@@ -150,19 +22,31 @@ export const Carousel = (props) => {
|
|
|
150
22
|
classes.push(...props.extraClassNames);
|
|
151
23
|
}
|
|
152
24
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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"
|
|
156
47
|
? children
|
|
157
|
-
: slides.map((child, index) => {
|
|
158
|
-
return (_jsx("div", { className: `ga-ds-carousel__slide ${index === activeDOMIndex ? "ga-ds-carousel__slide--active" : ""}`, children: child }, index));
|
|
159
|
-
}) }), _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 }))] })] }));
|
|
160
|
-
};
|
|
161
|
-
const CarouselNavigation = ({ goPrev, goNext, isAtStart, isAtEnd, }) => {
|
|
162
|
-
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, {})] })] }));
|
|
163
49
|
};
|
|
164
|
-
const
|
|
165
|
-
return (
|
|
166
|
-
cursor: clickable ? "pointer" : "default",
|
|
167
|
-
} }, 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" })] }));
|
|
168
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,158 +20,13 @@ 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 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
|
-
|
|
33
27
|
const displayNavigation = hasNavigation
|
|
34
28
|
const displayPagination = hasPagination
|
|
35
29
|
|
|
36
|
-
useEffect(() => {
|
|
37
|
-
if (loop) {
|
|
38
|
-
setIsAtStart(false)
|
|
39
|
-
setIsAtEnd(false)
|
|
40
|
-
} else {
|
|
41
|
-
setIsAtStart(activeSlideIndex === 0)
|
|
42
|
-
setIsAtEnd(activeSlideIndex === totalPages - 1)
|
|
43
|
-
}
|
|
44
|
-
}, [activeSlideIndex, totalPages, loop])
|
|
45
|
-
|
|
46
|
-
const breakpoint = useBreakpointObserver({ sm: 768, md: 992, lg: 1200, xl: 1400 })
|
|
47
|
-
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
setTotalPages(calculatePages(slides.length, slidesPerView[breakpoint], 1))
|
|
50
|
-
}, [breakpoint, slidesPerView])
|
|
51
|
-
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
const track = trackRef.current
|
|
54
|
-
if (!track) return
|
|
55
|
-
|
|
56
|
-
const handleScroll = () => {
|
|
57
|
-
if (loop) {
|
|
58
|
-
const slideWidth = track.scrollWidth / (slides.length * 3)
|
|
59
|
-
const scrollLeft = track.scrollLeft
|
|
60
|
-
|
|
61
|
-
// Repositionnement infini (uniquement si pas en navigation)
|
|
62
|
-
if (!isNavigatingRef.current) {
|
|
63
|
-
if (scrollLeft <= slideWidth * slides.length) {
|
|
64
|
-
track.scrollLeft = scrollLeft + slideWidth * slides.length
|
|
65
|
-
} else if (scrollLeft >= slideWidth * slides.length * 2) {
|
|
66
|
-
track.scrollLeft = scrollLeft - slideWidth * slides.length
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const scrollPosition = track.scrollLeft + slideWidth / 2
|
|
71
|
-
const domIndex = Math.floor(scrollPosition / slideWidth)
|
|
72
|
-
const index = domIndex % slides.length
|
|
73
|
-
setActiveDOMIndex(domIndex)
|
|
74
|
-
setActiveSlideIndex(index)
|
|
75
|
-
} else {
|
|
76
|
-
const slideWidth = track.scrollWidth / slides.length
|
|
77
|
-
const scrollPosition = track.scrollLeft + slideWidth / 2
|
|
78
|
-
const index = Math.floor(scrollPosition / slideWidth)
|
|
79
|
-
setActiveSlideIndex(Math.min(index, slides.length - 1))
|
|
80
|
-
setActiveDOMIndex(index)
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
track.addEventListener("scroll", handleScroll)
|
|
85
|
-
|
|
86
|
-
// Position initiale au milieu en mode loop
|
|
87
|
-
if (loop) {
|
|
88
|
-
setTimeout(() => {
|
|
89
|
-
const slideWidth = track.scrollWidth / (slides.length * 3)
|
|
90
|
-
track.scrollLeft = slideWidth * slides.length
|
|
91
|
-
}, 0)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return () => track.removeEventListener("scroll", handleScroll)
|
|
95
|
-
}, [slides.length, loop])
|
|
96
|
-
|
|
97
|
-
const style = Object.fromEntries(
|
|
98
|
-
Object.entries(slidesPerView).map(([key, value]) => [`--ga-ds-slides-per-view-${key}`, `${value}`])
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
const goPrev = () => {
|
|
102
|
-
if (!trackRef.current) return
|
|
103
|
-
const allSlides = trackRef.current.querySelectorAll(".ga-ds-carousel__slide")
|
|
104
|
-
if (activeSlideIndex === 0 && !loop) return
|
|
105
|
-
|
|
106
|
-
isNavigatingRef.current = true
|
|
107
|
-
|
|
108
|
-
if (loop) {
|
|
109
|
-
// En mode loop, on utilise l'index DOM actuel
|
|
110
|
-
allSlides[activeDOMIndex - 1]?.scrollIntoView({
|
|
111
|
-
behavior: "smooth",
|
|
112
|
-
block: "nearest",
|
|
113
|
-
inline: "start",
|
|
114
|
-
})
|
|
115
|
-
} else {
|
|
116
|
-
const nextIndex = activeSlideIndex - 1
|
|
117
|
-
allSlides[nextIndex]?.scrollIntoView({
|
|
118
|
-
behavior: "smooth",
|
|
119
|
-
block: "nearest",
|
|
120
|
-
inline: "start",
|
|
121
|
-
})
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
setTimeout(() => {
|
|
125
|
-
isNavigatingRef.current = false
|
|
126
|
-
}, 600)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const goNext = () => {
|
|
130
|
-
if (!trackRef.current) return
|
|
131
|
-
const allSlides = trackRef.current.querySelectorAll(".ga-ds-carousel__slide")
|
|
132
|
-
|
|
133
|
-
isNavigatingRef.current = true
|
|
134
|
-
|
|
135
|
-
if (loop) {
|
|
136
|
-
// En mode loop, on utilise l'index DOM actuel
|
|
137
|
-
allSlides[activeDOMIndex + 1]?.scrollIntoView({
|
|
138
|
-
behavior: "smooth",
|
|
139
|
-
block: "nearest",
|
|
140
|
-
inline: "start",
|
|
141
|
-
})
|
|
142
|
-
} else {
|
|
143
|
-
const totalSlides = allSlides.length
|
|
144
|
-
if (activeSlideIndex >= totalSlides - 1) return
|
|
145
|
-
if (isAtEnd) return
|
|
146
|
-
|
|
147
|
-
const nextIndex = activeSlideIndex + 1
|
|
148
|
-
allSlides[nextIndex]?.scrollIntoView({
|
|
149
|
-
behavior: "smooth",
|
|
150
|
-
block: "nearest",
|
|
151
|
-
inline: "start",
|
|
152
|
-
})
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
setTimeout(() => {
|
|
156
|
-
isNavigatingRef.current = false
|
|
157
|
-
}, 600)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const goTo = (nextIndex: number) => {
|
|
161
|
-
if (!trackRef.current) return
|
|
162
|
-
const slides = trackRef.current.querySelectorAll(".ga-ds-carousel__slide")
|
|
163
|
-
slides[nextIndex]?.scrollIntoView({
|
|
164
|
-
behavior: "smooth",
|
|
165
|
-
block: "nearest",
|
|
166
|
-
inline: "start",
|
|
167
|
-
})
|
|
168
|
-
}
|
|
169
|
-
|
|
170
30
|
const classes = ["ga-ds-carousel"]
|
|
171
31
|
|
|
172
32
|
if (props.extraClassNames) {
|
|
@@ -177,103 +37,68 @@ export const Carousel = (props: CarouselProps) => {
|
|
|
177
37
|
}
|
|
178
38
|
}
|
|
179
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
|
+
|
|
180
48
|
return (
|
|
181
|
-
<div className={classes.join(" ")}
|
|
182
|
-
{navigation?.positionY === "top" && displayNavigation &&
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
|
192
74
|
}
|
|
75
|
+
onSwiper={(swiper: SwiperType) => {
|
|
76
|
+
swiperRef.current = swiper
|
|
77
|
+
}}
|
|
78
|
+
className="ga-ds-carousel__track"
|
|
193
79
|
>
|
|
194
80
|
{context === "wp-editor"
|
|
195
81
|
? children
|
|
196
|
-
: slides.map((child, index) =>
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
{child}
|
|
203
|
-
</div>
|
|
204
|
-
)
|
|
205
|
-
})}
|
|
206
|
-
</div>
|
|
82
|
+
: slides.map((child, index) => (
|
|
83
|
+
<SwiperSlide key={index} className="ga-ds-carousel__slide">
|
|
84
|
+
{child}
|
|
85
|
+
</SwiperSlide>
|
|
86
|
+
))}
|
|
87
|
+
</Swiper>
|
|
207
88
|
|
|
208
89
|
<div className="ga-ds-carousel__navigation">
|
|
209
|
-
{
|
|
210
|
-
|
|
211
|
-
totalPages={totalPages}
|
|
212
|
-
activeSlideIndex={activeSlideIndex}
|
|
213
|
-
goTo={goTo}
|
|
214
|
-
clickable={pagination.clickable}
|
|
215
|
-
/>
|
|
216
|
-
)}
|
|
217
|
-
{displayNavigation && navigation?.positionY === "bottom" && (
|
|
218
|
-
<CarouselNavigation goPrev={goPrev} goNext={goNext} isAtStart={isAtStart} isAtEnd={isAtEnd} />
|
|
219
|
-
)}
|
|
90
|
+
{displayPagination && <div className="ga-ds-carousel__dots"></div>}
|
|
91
|
+
{displayNavigation && navigation?.positionY === "bottom" && <CarouselNavigation />}
|
|
220
92
|
</div>
|
|
221
93
|
</div>
|
|
222
94
|
)
|
|
223
95
|
}
|
|
224
96
|
|
|
225
|
-
const CarouselNavigation = ({
|
|
226
|
-
goPrev,
|
|
227
|
-
goNext,
|
|
228
|
-
isAtStart,
|
|
229
|
-
isAtEnd,
|
|
230
|
-
}: {
|
|
231
|
-
goPrev: () => void
|
|
232
|
-
goNext: () => void
|
|
233
|
-
isAtStart: boolean
|
|
234
|
-
isAtEnd: boolean
|
|
235
|
-
}) => {
|
|
97
|
+
const CarouselNavigation = () => {
|
|
236
98
|
return (
|
|
237
99
|
<div className={`ga-ds-carousel__arrows`}>
|
|
238
|
-
<Button
|
|
239
|
-
|
|
240
|
-
icon="chevron-left"
|
|
241
|
-
onClick={goPrev}
|
|
242
|
-
disabled={isAtStart}
|
|
243
|
-
/>
|
|
244
|
-
<Button
|
|
245
|
-
extraClassNames={["ga-ds-carousel__button", "ga-ds-carousel__button--next"]}
|
|
246
|
-
icon="chevron-right"
|
|
247
|
-
onClick={goNext}
|
|
248
|
-
disabled={isAtEnd}
|
|
249
|
-
/>
|
|
250
|
-
</div>
|
|
251
|
-
)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const CarouselPagination = ({
|
|
255
|
-
totalPages,
|
|
256
|
-
activeSlideIndex,
|
|
257
|
-
goTo,
|
|
258
|
-
clickable,
|
|
259
|
-
}: {
|
|
260
|
-
totalPages: number
|
|
261
|
-
activeSlideIndex: number
|
|
262
|
-
goTo: (nextIndex: number) => void
|
|
263
|
-
clickable: boolean
|
|
264
|
-
}) => {
|
|
265
|
-
return (
|
|
266
|
-
<div className="ga-ds-carousel__dots">
|
|
267
|
-
{Array.from({ length: totalPages }, (_, index) => index).map((_, index) => (
|
|
268
|
-
<span
|
|
269
|
-
key={index}
|
|
270
|
-
className={`ga-ds-carousel__dot ${index === activeSlideIndex ? "ga-ds-carousel__dot--active" : ""}`}
|
|
271
|
-
onClick={() => clickable && goTo(index)}
|
|
272
|
-
style={{
|
|
273
|
-
cursor: clickable ? "pointer" : "default",
|
|
274
|
-
}}
|
|
275
|
-
></span>
|
|
276
|
-
))}
|
|
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" />
|
|
277
102
|
</div>
|
|
278
103
|
)
|
|
279
104
|
}
|