@treely/strapi-slices 7.1.0 → 7.1.2
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/README.md +4 -1
- package/dist/slices/CarouselMarqueeBanner/CarouselMarqueeBanner.d.ts +1 -4
- package/dist/strapi-slices.cjs.development.js +41 -28
- package/dist/strapi-slices.cjs.development.js.map +1 -1
- package/dist/strapi-slices.cjs.production.min.js +1 -1
- package/dist/strapi-slices.cjs.production.min.js.map +1 -1
- package/dist/strapi-slices.esm.js +41 -28
- package/dist/strapi-slices.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/slices/CarouselMarqueeBanner/CarouselMarqueeBanner.stories.tsx +20 -50
- package/src/slices/CarouselMarqueeBanner/CarouselMarqueeBanner.test.tsx +16 -22
- package/src/slices/CarouselMarqueeBanner/CarouselMarqueeBanner.tsx +49 -41
package/package.json
CHANGED
|
@@ -22,43 +22,28 @@ Default.args = {
|
|
|
22
22
|
logos: [
|
|
23
23
|
{
|
|
24
24
|
id: 1,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
alt: 'Alt text 1',
|
|
28
|
-
img: { data: storybookStrapiAvatarMock },
|
|
29
|
-
},
|
|
25
|
+
alt: 'Alt text 1',
|
|
26
|
+
img: { data: storybookStrapiAvatarMock },
|
|
30
27
|
},
|
|
31
28
|
{
|
|
32
29
|
id: 2,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
alt: 'Alt text 1',
|
|
36
|
-
img: { data: storybookStrapiAvatarMock },
|
|
37
|
-
},
|
|
30
|
+
alt: 'Alt text 2',
|
|
31
|
+
img: { data: storybookStrapiAvatarMock },
|
|
38
32
|
},
|
|
39
33
|
{
|
|
40
34
|
id: 3,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
alt: 'Alt text 1',
|
|
44
|
-
img: { data: storybookStrapiAvatarMock },
|
|
45
|
-
},
|
|
35
|
+
alt: 'Alt text 3',
|
|
36
|
+
img: { data: storybookStrapiAvatarMock },
|
|
46
37
|
},
|
|
47
38
|
{
|
|
48
39
|
id: 4,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
alt: 'Alt text 1',
|
|
52
|
-
img: { data: storybookStrapiTreeIconMock },
|
|
53
|
-
},
|
|
40
|
+
alt: 'Alt text 4',
|
|
41
|
+
img: { data: storybookStrapiTreeIconMock },
|
|
54
42
|
},
|
|
55
43
|
{
|
|
56
44
|
id: 5,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
alt: 'Alt text 1',
|
|
60
|
-
img: { data: storybookStrapiCoverMock },
|
|
61
|
-
},
|
|
45
|
+
alt: 'Partner Logo 5',
|
|
46
|
+
img: { data: storybookStrapiAvatarMock },
|
|
62
47
|
},
|
|
63
48
|
],
|
|
64
49
|
},
|
|
@@ -71,43 +56,28 @@ WithTitle.args = {
|
|
|
71
56
|
logos: [
|
|
72
57
|
{
|
|
73
58
|
id: 1,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
alt: 'Partner Logo 1',
|
|
77
|
-
img: { data: storybookStrapiAvatarMock },
|
|
78
|
-
},
|
|
59
|
+
alt: 'Partner Logo 1',
|
|
60
|
+
img: { data: storybookStrapiAvatarMock },
|
|
79
61
|
},
|
|
80
62
|
{
|
|
81
63
|
id: 2,
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
alt: 'Partner Logo 2',
|
|
85
|
-
img: { data: storybookStrapiAvatarMock },
|
|
86
|
-
},
|
|
64
|
+
alt: 'Partner Logo 2',
|
|
65
|
+
img: { data: storybookStrapiAvatarMock },
|
|
87
66
|
},
|
|
88
67
|
{
|
|
89
68
|
id: 3,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
alt: 'Partner Logo 3',
|
|
93
|
-
img: { data: storybookStrapiAvatarMock },
|
|
94
|
-
},
|
|
69
|
+
alt: 'Partner Logo 3',
|
|
70
|
+
img: { data: storybookStrapiAvatarMock },
|
|
95
71
|
},
|
|
96
72
|
{
|
|
97
73
|
id: 4,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
alt: 'Partner Logo 4',
|
|
101
|
-
img: { data: storybookStrapiTreeIconMock },
|
|
102
|
-
},
|
|
74
|
+
alt: 'Partner Logo 4',
|
|
75
|
+
img: { data: storybookStrapiTreeIconMock },
|
|
103
76
|
},
|
|
104
77
|
{
|
|
105
78
|
id: 5,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
alt: 'Partner Logo 5',
|
|
109
|
-
img: { data: storybookStrapiCoverMock },
|
|
110
|
-
},
|
|
79
|
+
alt: 'Partner Logo 5',
|
|
80
|
+
img: { data: storybookStrapiCoverMock },
|
|
111
81
|
},
|
|
112
82
|
],
|
|
113
83
|
},
|
|
@@ -31,39 +31,33 @@ const defaultProps: CarouselMarqueeBannerProps = {
|
|
|
31
31
|
logos: [
|
|
32
32
|
{
|
|
33
33
|
id: 1,
|
|
34
|
+
alt: 'Logo 1',
|
|
34
35
|
img: {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
...strapiMediaMock.attributes,
|
|
42
|
-
width: 1000,
|
|
43
|
-
height: 600,
|
|
44
|
-
},
|
|
36
|
+
data: {
|
|
37
|
+
...strapiMediaMock,
|
|
38
|
+
attributes: {
|
|
39
|
+
...strapiMediaMock.attributes,
|
|
40
|
+
width: 1000,
|
|
41
|
+
height: 600,
|
|
45
42
|
},
|
|
46
43
|
},
|
|
47
|
-
objectFit: 'contain',
|
|
48
44
|
},
|
|
45
|
+
objectFit: 'contain',
|
|
49
46
|
},
|
|
50
47
|
{
|
|
51
48
|
id: 2,
|
|
49
|
+
alt: 'Logo 2',
|
|
52
50
|
img: {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
...strapiMediaMock.attributes,
|
|
60
|
-
width: 800,
|
|
61
|
-
height: 500,
|
|
62
|
-
},
|
|
51
|
+
data: {
|
|
52
|
+
...strapiMediaMock,
|
|
53
|
+
attributes: {
|
|
54
|
+
...strapiMediaMock.attributes,
|
|
55
|
+
width: 800,
|
|
56
|
+
height: 500,
|
|
63
57
|
},
|
|
64
58
|
},
|
|
65
|
-
objectFit: 'cover',
|
|
66
59
|
},
|
|
60
|
+
objectFit: 'cover',
|
|
67
61
|
},
|
|
68
62
|
],
|
|
69
63
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
DefaultSectionContainer,
|
|
4
4
|
Flex,
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
Spacer,
|
|
7
7
|
Box,
|
|
8
8
|
useMediaQuery,
|
|
9
|
+
useToken,
|
|
9
10
|
} from 'boemly';
|
|
10
11
|
import Image from 'next/image';
|
|
11
12
|
import useEmblaCarousel from 'embla-carousel-react';
|
|
@@ -21,37 +22,36 @@ import { CarouselInnerContainer, LogoGrid } from './styles';
|
|
|
21
22
|
export interface CarouselMarqueeBannerProps {
|
|
22
23
|
slice: {
|
|
23
24
|
title?: string;
|
|
24
|
-
logos:
|
|
25
|
-
id: number;
|
|
26
|
-
img: StrapiImage;
|
|
27
|
-
}[];
|
|
25
|
+
logos: StrapiImage[];
|
|
28
26
|
};
|
|
29
27
|
}
|
|
30
28
|
|
|
31
|
-
|
|
29
|
+
// Separate component containing the actual logic
|
|
30
|
+
const CarouselMarqueeBannerContent: React.FC<CarouselMarqueeBannerProps> = ({
|
|
32
31
|
slice,
|
|
33
|
-
}
|
|
32
|
+
}) => {
|
|
33
|
+
const [primary50] = useToken('colors', ['primary.50']);
|
|
34
34
|
const { width: windowWidth } = useWindowSize();
|
|
35
|
-
const
|
|
35
|
+
const hasEnoughLogosForLoop = slice.logos.length >= 5;
|
|
36
36
|
const LOOP_ARRAY_LENGTH = windowWidth > 2000 ? 5 : 4;
|
|
37
37
|
|
|
38
38
|
// Duplicate Logos to create a full loop
|
|
39
|
-
const logosToRender =
|
|
40
|
-
? slice.logos
|
|
41
|
-
:
|
|
39
|
+
const logosToRender = hasEnoughLogosForLoop
|
|
40
|
+
? Array.from({ length: LOOP_ARRAY_LENGTH }, () => slice.logos).flat()
|
|
41
|
+
: slice.logos;
|
|
42
42
|
|
|
43
43
|
const [isMobile] = useMediaQuery(BREAKPOINT_MD_QUERY);
|
|
44
44
|
|
|
45
|
+
// Carousel setup
|
|
45
46
|
const [emblaRef] = useEmblaCarousel(
|
|
46
47
|
{
|
|
47
|
-
loop:
|
|
48
|
+
loop: hasEnoughLogosForLoop,
|
|
48
49
|
align: 'start',
|
|
49
50
|
containScroll: 'trimSnaps',
|
|
50
51
|
dragFree: true,
|
|
51
52
|
},
|
|
52
|
-
|
|
53
|
-
? [
|
|
54
|
-
: [
|
|
53
|
+
hasEnoughLogosForLoop
|
|
54
|
+
? [
|
|
55
55
|
AutoScroll({
|
|
56
56
|
playOnInit: true,
|
|
57
57
|
speed: isMobile ? 0.5 : 1,
|
|
@@ -60,10 +60,11 @@ export const CarouselMarqueeBanner: React.FC<CarouselMarqueeBannerProps> = ({
|
|
|
60
60
|
stopOnFocusIn: false,
|
|
61
61
|
}),
|
|
62
62
|
]
|
|
63
|
+
: []
|
|
63
64
|
);
|
|
64
65
|
|
|
65
|
-
const renderLogos = () => {
|
|
66
|
-
if (
|
|
66
|
+
const renderLogos = (): React.ReactNode => {
|
|
67
|
+
if (!hasEnoughLogosForLoop) {
|
|
67
68
|
return (
|
|
68
69
|
<LogoGrid>
|
|
69
70
|
{slice.logos.map((logo, index) => (
|
|
@@ -87,18 +88,18 @@ export const CarouselMarqueeBanner: React.FC<CarouselMarqueeBannerProps> = ({
|
|
|
87
88
|
? 'var(--boemly-sizes-16)'
|
|
88
89
|
: 'var(--boemly-sizes-36)'
|
|
89
90
|
} / ${getClosestRatio(
|
|
90
|
-
logo.img.
|
|
91
|
-
logo.img.
|
|
91
|
+
logo.img.data.attributes.width,
|
|
92
|
+
logo.img.data.attributes.height
|
|
92
93
|
)})`}
|
|
93
94
|
width={isMobile ? '16' : '36'}
|
|
94
95
|
borderRadius="xl"
|
|
95
96
|
>
|
|
96
97
|
<Image
|
|
97
|
-
src={strapiMediaUrl(logo.img
|
|
98
|
-
alt={logo.
|
|
98
|
+
src={strapiMediaUrl(logo.img, 'large')}
|
|
99
|
+
alt={logo.alt}
|
|
99
100
|
fill
|
|
100
101
|
style={{
|
|
101
|
-
objectFit: logo.
|
|
102
|
+
objectFit: logo.objectFit || 'contain',
|
|
102
103
|
borderRadius: 'var(--boemly-radii-xl)',
|
|
103
104
|
}}
|
|
104
105
|
/>
|
|
@@ -134,18 +135,18 @@ export const CarouselMarqueeBanner: React.FC<CarouselMarqueeBannerProps> = ({
|
|
|
134
135
|
? 'var(--boemly-sizes-16)'
|
|
135
136
|
: 'var(--boemly-sizes-36)'
|
|
136
137
|
} / ${getClosestRatio(
|
|
137
|
-
logo.img.
|
|
138
|
-
logo.img.
|
|
138
|
+
logo.img.data.attributes.width,
|
|
139
|
+
logo.img.data.attributes.height
|
|
139
140
|
)})`}
|
|
140
141
|
width={isMobile ? '16' : '36'}
|
|
141
142
|
borderRadius="xl"
|
|
142
143
|
>
|
|
143
144
|
<Image
|
|
144
|
-
src={strapiMediaUrl(logo.img
|
|
145
|
-
alt={logo.
|
|
145
|
+
src={strapiMediaUrl(logo.img, 'large')}
|
|
146
|
+
alt={logo.alt}
|
|
146
147
|
fill
|
|
147
148
|
style={{
|
|
148
|
-
objectFit: logo.
|
|
149
|
+
objectFit: logo.objectFit || 'contain',
|
|
149
150
|
borderRadius: 'var(--boemly-radii-xl)',
|
|
150
151
|
}}
|
|
151
152
|
/>
|
|
@@ -159,18 +160,8 @@ export const CarouselMarqueeBanner: React.FC<CarouselMarqueeBannerProps> = ({
|
|
|
159
160
|
};
|
|
160
161
|
|
|
161
162
|
return (
|
|
162
|
-
<DefaultSectionContainer>
|
|
163
|
-
|
|
164
|
-
maxWidth="full"
|
|
165
|
-
margin="auto"
|
|
166
|
-
display="flex"
|
|
167
|
-
flexDirection="column"
|
|
168
|
-
justifyContent="center"
|
|
169
|
-
alignItems="center"
|
|
170
|
-
height="2xs"
|
|
171
|
-
backgroundColor="gray.50"
|
|
172
|
-
padding="42px 0 56px"
|
|
173
|
-
>
|
|
163
|
+
<DefaultSectionContainer backgroundColor={primary50}>
|
|
164
|
+
<>
|
|
174
165
|
{slice.title ? (
|
|
175
166
|
<>
|
|
176
167
|
<Flex alignItems="center" flexDirection="column">
|
|
@@ -178,14 +169,31 @@ export const CarouselMarqueeBanner: React.FC<CarouselMarqueeBannerProps> = ({
|
|
|
178
169
|
{slice.title}
|
|
179
170
|
</Heading>
|
|
180
171
|
</Flex>
|
|
181
|
-
<Spacer height="12" />
|
|
172
|
+
<Spacer height="12" minHeight="12" />
|
|
182
173
|
</>
|
|
183
174
|
) : null}
|
|
184
175
|
|
|
185
176
|
{renderLogos()}
|
|
186
|
-
|
|
177
|
+
</>
|
|
187
178
|
</DefaultSectionContainer>
|
|
188
179
|
);
|
|
189
180
|
};
|
|
190
181
|
|
|
182
|
+
// Lazy-rendering the child component after client-side hydration
|
|
183
|
+
export const CarouselMarqueeBanner: React.FC<CarouselMarqueeBannerProps> = ({
|
|
184
|
+
slice,
|
|
185
|
+
}: CarouselMarqueeBannerProps) => {
|
|
186
|
+
const [showChild, setShowChild] = useState(false);
|
|
187
|
+
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
setShowChild(true); // Hydrate the component after the client-side is ready
|
|
190
|
+
}, []);
|
|
191
|
+
|
|
192
|
+
if (!showChild) {
|
|
193
|
+
return <div />;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return <CarouselMarqueeBannerContent slice={slice} />;
|
|
197
|
+
};
|
|
198
|
+
|
|
191
199
|
export default CarouselMarqueeBanner;
|