@treely/strapi-slices 7.1.1 → 7.1.3
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 -29
- 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 -29
- package/dist/strapi-slices.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/slices/CarouselMarqueeBanner/CarouselMarqueeBanner.stories.tsx +20 -50
- package/src/slices/CarouselMarqueeBanner/CarouselMarqueeBanner.test.tsx +16 -22
- package/src/slices/CarouselMarqueeBanner/CarouselMarqueeBanner.tsx +51 -48
- package/src/slices/CarouselMarqueeBanner/styles.ts +4 -2
- package/src/utils/getClosestRatio.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@treely/strapi-slices",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Tree.ly FlexCo",
|
|
6
6
|
"description": "@treely/strapi-slices is a open source library maintained by Tree.ly.",
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
"adblock-detect-react": "^1.1.0",
|
|
135
135
|
"axios": "^1.7.2",
|
|
136
136
|
"axios-cache-interceptor": "^1.5.3",
|
|
137
|
-
"boemly": "^7.
|
|
137
|
+
"boemly": "^7.2.0",
|
|
138
138
|
"embla-carousel-auto-scroll": "^8.5.1",
|
|
139
139
|
"embla-carousel-autoplay": "^8.5.1",
|
|
140
140
|
"embla-carousel-react": "^8.5.1",
|
|
@@ -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,
|
|
@@ -22,38 +22,36 @@ import { CarouselInnerContainer, LogoGrid } from './styles';
|
|
|
22
22
|
export interface CarouselMarqueeBannerProps {
|
|
23
23
|
slice: {
|
|
24
24
|
title?: string;
|
|
25
|
-
logos:
|
|
26
|
-
id: number;
|
|
27
|
-
img: StrapiImage;
|
|
28
|
-
}[];
|
|
25
|
+
logos: StrapiImage[];
|
|
29
26
|
};
|
|
30
27
|
}
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
// Separate component containing the actual logic
|
|
30
|
+
const CarouselMarqueeBannerContent: React.FC<CarouselMarqueeBannerProps> = ({
|
|
33
31
|
slice,
|
|
34
|
-
}
|
|
32
|
+
}) => {
|
|
35
33
|
const [primary50] = useToken('colors', ['primary.50']);
|
|
36
34
|
const { width: windowWidth } = useWindowSize();
|
|
37
|
-
const
|
|
35
|
+
const hasEnoughLogosForLoop = slice.logos.length >= 5;
|
|
38
36
|
const LOOP_ARRAY_LENGTH = windowWidth > 2000 ? 5 : 4;
|
|
39
37
|
|
|
40
38
|
// Duplicate Logos to create a full loop
|
|
41
|
-
const logosToRender =
|
|
42
|
-
? slice.logos
|
|
43
|
-
:
|
|
39
|
+
const logosToRender = hasEnoughLogosForLoop
|
|
40
|
+
? Array.from({ length: LOOP_ARRAY_LENGTH }, () => slice.logos).flat()
|
|
41
|
+
: slice.logos;
|
|
44
42
|
|
|
45
43
|
const [isMobile] = useMediaQuery(BREAKPOINT_MD_QUERY);
|
|
46
44
|
|
|
45
|
+
// Carousel setup
|
|
47
46
|
const [emblaRef] = useEmblaCarousel(
|
|
48
47
|
{
|
|
49
|
-
loop:
|
|
48
|
+
loop: hasEnoughLogosForLoop,
|
|
50
49
|
align: 'start',
|
|
51
50
|
containScroll: 'trimSnaps',
|
|
52
51
|
dragFree: true,
|
|
53
52
|
},
|
|
54
|
-
|
|
55
|
-
? [
|
|
56
|
-
: [
|
|
53
|
+
hasEnoughLogosForLoop
|
|
54
|
+
? [
|
|
57
55
|
AutoScroll({
|
|
58
56
|
playOnInit: true,
|
|
59
57
|
speed: isMobile ? 0.5 : 1,
|
|
@@ -62,16 +60,16 @@ export const CarouselMarqueeBanner: React.FC<CarouselMarqueeBannerProps> = ({
|
|
|
62
60
|
stopOnFocusIn: false,
|
|
63
61
|
}),
|
|
64
62
|
]
|
|
63
|
+
: []
|
|
65
64
|
);
|
|
66
65
|
|
|
67
|
-
const renderLogos = () => {
|
|
68
|
-
if (
|
|
66
|
+
const renderLogos = (): React.ReactNode => {
|
|
67
|
+
if (!hasEnoughLogosForLoop) {
|
|
69
68
|
return (
|
|
70
69
|
<LogoGrid>
|
|
71
70
|
{slice.logos.map((logo, index) => (
|
|
72
71
|
<Box
|
|
73
72
|
key={`${logo.id}-${index}`}
|
|
74
|
-
width={isMobile ? '16' : '36'}
|
|
75
73
|
flexShrink={0}
|
|
76
74
|
transform="translate3d(0, 0, 0)"
|
|
77
75
|
>
|
|
@@ -83,25 +81,20 @@ export const CarouselMarqueeBanner: React.FC<CarouselMarqueeBannerProps> = ({
|
|
|
83
81
|
>
|
|
84
82
|
<Box
|
|
85
83
|
position="relative"
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
logo.img.img.data.attributes.width,
|
|
93
|
-
logo.img.img.data.attributes.height
|
|
94
|
-
)})`}
|
|
95
|
-
width={isMobile ? '16' : '36'}
|
|
96
|
-
borderRadius="xl"
|
|
84
|
+
height={isMobile ? '16' : '36'}
|
|
85
|
+
width={`calc(var(--boemly-sizes-10)
|
|
86
|
+
* ${getClosestRatio(
|
|
87
|
+
logo.img.data.attributes.width,
|
|
88
|
+
logo.img.data.attributes.height
|
|
89
|
+
)})`}
|
|
97
90
|
>
|
|
98
91
|
<Image
|
|
99
|
-
src={strapiMediaUrl(logo.img
|
|
100
|
-
alt={logo.
|
|
92
|
+
src={strapiMediaUrl(logo.img, 'large')}
|
|
93
|
+
alt={logo.alt}
|
|
101
94
|
fill
|
|
102
95
|
style={{
|
|
103
|
-
objectFit: logo.
|
|
104
|
-
|
|
96
|
+
objectFit: logo.objectFit || 'contain',
|
|
97
|
+
filter: 'grayscale(100%)',
|
|
105
98
|
}}
|
|
106
99
|
/>
|
|
107
100
|
</Box>
|
|
@@ -118,7 +111,6 @@ export const CarouselMarqueeBanner: React.FC<CarouselMarqueeBannerProps> = ({
|
|
|
118
111
|
{logosToRender.map((logo, index) => (
|
|
119
112
|
<Box
|
|
120
113
|
key={`${logo.id}-${index}`}
|
|
121
|
-
width={isMobile ? '16' : '36'}
|
|
122
114
|
flexShrink={0}
|
|
123
115
|
transform="translate3d(0, 0, 0)"
|
|
124
116
|
>
|
|
@@ -130,25 +122,19 @@ export const CarouselMarqueeBanner: React.FC<CarouselMarqueeBannerProps> = ({
|
|
|
130
122
|
>
|
|
131
123
|
<Box
|
|
132
124
|
position="relative"
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
: 'var(--boemly-sizes-36)'
|
|
138
|
-
} / ${getClosestRatio(
|
|
139
|
-
logo.img.img.data.attributes.width,
|
|
140
|
-
logo.img.img.data.attributes.height
|
|
125
|
+
height={isMobile ? '16' : '36'}
|
|
126
|
+
width={`calc(var(--boemly-sizes-10) * ${getClosestRatio(
|
|
127
|
+
logo.img.data.attributes.width,
|
|
128
|
+
logo.img.data.attributes.height
|
|
141
129
|
)})`}
|
|
142
|
-
width={isMobile ? '16' : '36'}
|
|
143
|
-
borderRadius="xl"
|
|
144
130
|
>
|
|
145
131
|
<Image
|
|
146
|
-
src={strapiMediaUrl(logo.img
|
|
147
|
-
alt={logo.
|
|
132
|
+
src={strapiMediaUrl(logo.img, 'large')}
|
|
133
|
+
alt={logo.alt}
|
|
148
134
|
fill
|
|
149
135
|
style={{
|
|
150
|
-
objectFit: logo.
|
|
151
|
-
|
|
136
|
+
objectFit: logo.objectFit || 'contain',
|
|
137
|
+
filter: 'grayscale(100%)',
|
|
152
138
|
}}
|
|
153
139
|
/>
|
|
154
140
|
</Box>
|
|
@@ -180,4 +166,21 @@ export const CarouselMarqueeBanner: React.FC<CarouselMarqueeBannerProps> = ({
|
|
|
180
166
|
);
|
|
181
167
|
};
|
|
182
168
|
|
|
169
|
+
// Lazy-rendering the child component after client-side hydration
|
|
170
|
+
export const CarouselMarqueeBanner: React.FC<CarouselMarqueeBannerProps> = ({
|
|
171
|
+
slice,
|
|
172
|
+
}: CarouselMarqueeBannerProps) => {
|
|
173
|
+
const [showChild, setShowChild] = useState(false);
|
|
174
|
+
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
setShowChild(true); // Hydrate the component after the client-side is ready
|
|
177
|
+
}, []);
|
|
178
|
+
|
|
179
|
+
if (!showChild) {
|
|
180
|
+
return <div />;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return <CarouselMarqueeBannerContent slice={slice} />;
|
|
184
|
+
};
|
|
185
|
+
|
|
183
186
|
export default CarouselMarqueeBanner;
|
|
@@ -12,7 +12,9 @@ export const CarouselInnerContainer = styled(
|
|
|
12
12
|
display: flex;
|
|
13
13
|
justify-content: ${(props) =>
|
|
14
14
|
props.logoCount < 5 ? 'center' : 'flex-start'};
|
|
15
|
-
gap: var(--boemly-space-
|
|
15
|
+
gap: var(--boemly-space-24);
|
|
16
|
+
padding-right: var(--boemly-space-24);
|
|
17
|
+
padding-left: var(--boemly-space-24);
|
|
16
18
|
|
|
17
19
|
@media screen and (max-width: ${BREAKPOINT_MD}) {
|
|
18
20
|
width: calc(
|
|
@@ -28,7 +30,7 @@ export const LogoGrid = styled(motion.div)`
|
|
|
28
30
|
display: flex;
|
|
29
31
|
flex-wrap: wrap;
|
|
30
32
|
justify-content: center;
|
|
31
|
-
gap: var(--boemly-space-
|
|
33
|
+
gap: var(--boemly-space-24);
|
|
32
34
|
|
|
33
35
|
@media screen and (max-width: ${BREAKPOINT_MD}) {
|
|
34
36
|
display: grid;
|