@androbinco/library-cli 0.1.0 → 0.3.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/README.md +86 -37
- package/package.json +11 -16
- package/src/commands/add.js +107 -0
- package/src/commands/list.js +51 -0
- package/src/index.js +46 -15
- package/src/templates/carousel/components/navigation-buttons.tsx +1 -0
- package/src/templates/carousel/components/pagination/bullet.pagination.carousel.tsx +69 -0
- package/src/templates/carousel/components/pagination/number.pagination.carousel.tsx +30 -0
- package/src/templates/carousel/components/pagination/progress/progress.pagination.carousel.tsx +99 -0
- package/src/templates/carousel/components/pagination/progress/use-slide-progress.tsx +31 -0
- package/src/templates/carousel/components/pagination.tsx +47 -82
- package/src/templates/faqs-accordion/examples/faqs-showcase.tsx +42 -0
- package/src/templates/faqs-accordion/faqs-accordion.tsx +70 -0
- package/src/templates/faqs-accordion/mock-data.ts +38 -0
- package/src/templates/faqs-accordion/types.ts +18 -0
- package/src/templates/in-view/data.in-view.ts +89 -0
- package/src/templates/in-view/examples/in-view-examples.home.tsx +101 -0
- package/src/templates/in-view/examples/in-view-grid-showcase.tsx +41 -0
- package/src/templates/in-view/in-view-animation.tsx +72 -0
- package/src/templates/in-view/in-view-grid.tsx +81 -0
- package/src/templates/in-view/in-view-hidden-text.tsx +45 -0
- package/src/templates/in-view/in-view-stroke-line.tsx +30 -0
- package/src/templates/lenis/examples/providers.tsx +23 -0
- package/src/templates/lenis/lenis-provider.tsx +46 -0
- package/src/templates/scroll-components/hooks/use-client-dimensions.ts +21 -0
- package/src/templates/scroll-components/parallax/examples/parallax-showcase.tsx +87 -0
- package/src/templates/scroll-components/parallax/parallax.css +36 -0
- package/src/templates/scroll-components/parallax/parallax.tsx +67 -0
- package/src/templates/scroll-components/scale-gallery/components/expanding-element.tsx +40 -0
- package/src/templates/scroll-components/scale-gallery/examples/scale-gallery-showcase.tsx +68 -0
- package/src/templates/scroll-components/scale-gallery/scale-gallery.tsx +57 -0
- package/src/templates/scroll-components/scroll-tracker-provider.tsx +78 -0
- package/src/templates/scroll-components/scroll-tracker-showcase.tsx +44 -0
- package/src/templates/strapi-dynamic-zone/README.md +157 -0
- package/src/templates/strapi-dynamic-zone/dynamic-zone.tsx +113 -0
- package/src/templates/strapi-dynamic-zone/examples/page.tsx +53 -0
- package/src/templates/strapi-dynamic-zone/examples/renderers.tsx +74 -0
- package/src/templates/strapi-dynamic-zone/examples/types.ts +41 -0
- package/src/templates/strapi-dynamic-zone/index.ts +11 -0
- package/src/templates/strapi-dynamic-zone/types.ts +73 -0
- package/src/templates/ticker/css-ticker/css-ticker.tsx +61 -0
- package/src/templates/ticker/css-ticker/ticker.keyframes.css +86 -0
- package/src/templates/ticker/examples/ticker-hover-showcase.home.tsx +57 -0
- package/src/templates/ticker/examples/ticker-static-showcase.home.tsx +56 -0
- package/src/templates/ticker/hooks/use-ticker-clones.tsx +70 -0
- package/src/templates/ticker/hooks/use-ticker-incremental.tsx +72 -0
- package/src/templates/ticker/motion-ticker.tsx +93 -0
- package/src/utils/components.js +587 -54
- package/src/utils/files.js +89 -5
- package/src/templates/button/button.tsx +0 -5
- package/src/templates/card/card.tsx +0 -5
- package/src/templates/example/example.tsx +0 -5
- package/src/templates/hero/hero.tsx +0 -5
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
.ticker {
|
|
2
|
+
--ticker-speed: 20s;
|
|
3
|
+
}
|
|
4
|
+
.ticker-slow {
|
|
5
|
+
--ticker-speed: 100s;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
@keyframes base-ticker-right {
|
|
9
|
+
0% {
|
|
10
|
+
transform: translateX(0%);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
50% {
|
|
14
|
+
transform: translateX(100%);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
50.001% {
|
|
18
|
+
transform: translateX(-100%);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
100% {
|
|
22
|
+
transform: translateX(0%);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
@keyframes cloned-ticker-right {
|
|
26
|
+
0% {
|
|
27
|
+
transform: translateX(0%);
|
|
28
|
+
}
|
|
29
|
+
100% {
|
|
30
|
+
transform: translateX(200%);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@keyframes base-ticker-left {
|
|
35
|
+
0% {
|
|
36
|
+
transform: translateX(0%);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
50% {
|
|
40
|
+
transform: translateX(-100%);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
50.001% {
|
|
44
|
+
transform: translateX(100%);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
100% {
|
|
48
|
+
transform: translateX(0%);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
@keyframes cloned-ticker-left {
|
|
52
|
+
0% {
|
|
53
|
+
transform: translateX(0%);
|
|
54
|
+
}
|
|
55
|
+
100% {
|
|
56
|
+
transform: translateX(-200%);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
.animate-base-ticker {
|
|
60
|
+
animation-name: base-ticker-right;
|
|
61
|
+
animation-duration: var(--ticker-speed);
|
|
62
|
+
animation-timing-function: linear;
|
|
63
|
+
animation-iteration-count: infinite;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.animate-cloned-ticker {
|
|
67
|
+
animation-name: cloned-ticker-right;
|
|
68
|
+
animation-duration: var(--ticker-speed);
|
|
69
|
+
animation-timing-function: linear;
|
|
70
|
+
animation-iteration-count: infinite;
|
|
71
|
+
}
|
|
72
|
+
.animate-base-ticker-left {
|
|
73
|
+
animation-name: base-ticker-left;
|
|
74
|
+
animation-duration: var(--ticker-speed);
|
|
75
|
+
animation-timing-function: linear;
|
|
76
|
+
animation-iteration-count: infinite;
|
|
77
|
+
}
|
|
78
|
+
.animate-cloned-ticker-left {
|
|
79
|
+
animation-name: cloned-ticker-left;
|
|
80
|
+
animation-duration: var(--ticker-speed);
|
|
81
|
+
animation-timing-function: linear;
|
|
82
|
+
animation-iteration-count: infinite;
|
|
83
|
+
}
|
|
84
|
+
.animate-pause {
|
|
85
|
+
animation-play-state: paused;
|
|
86
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { MotionTicker } from "../motion-ticker";
|
|
2
|
+
import { Text } from "@/common/ui/text/text";
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
This is a ticker component that is used to display a list of items in a ticker.
|
|
6
|
+
It is a wrapper around the motion library and uses the useMotionValue hook to create the ticker.
|
|
7
|
+
It is a client component that is used to display a list of items in a ticker.
|
|
8
|
+
|
|
9
|
+
To test it you can just use it like this in any page:
|
|
10
|
+
<TickerHoverShowcaseHome />
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const TickerExampleCard = ({
|
|
14
|
+
text = "BASIC TICKER EXAMPLE",
|
|
15
|
+
}: {
|
|
16
|
+
text?: string;
|
|
17
|
+
}) => {
|
|
18
|
+
return (
|
|
19
|
+
<div className="border-2 border-x-0 border-fill-brand-primary bg-bg-primary-inverse px-5 py-2">
|
|
20
|
+
<Text className="capitalize" variant="body.2">
|
|
21
|
+
{text}
|
|
22
|
+
</Text>
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
const CardWithSpacingExample = ({
|
|
27
|
+
text = "CARD WITH SPACING EXAMPLE",
|
|
28
|
+
}: {
|
|
29
|
+
text?: string;
|
|
30
|
+
}) => {
|
|
31
|
+
return (
|
|
32
|
+
<div className="px-1">
|
|
33
|
+
<div className="border-2 border-fill-brand-primary bg-bg-primary-inverse px-5 py-2">
|
|
34
|
+
<Text className="capitalize" variant="body.2">
|
|
35
|
+
{text}
|
|
36
|
+
</Text>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const TickerHoverShowcaseHome = () => {
|
|
43
|
+
return (
|
|
44
|
+
<div className="flex flex-col gap-12">
|
|
45
|
+
<Text className="px-4 pb-15" variant="title.6">
|
|
46
|
+
Ticker with hover stop
|
|
47
|
+
</Text>
|
|
48
|
+
<MotionTicker>
|
|
49
|
+
<CardWithSpacingExample text="TICKER EXAMPLE" />
|
|
50
|
+
<CardWithSpacingExample text="HOVER TICKER" />
|
|
51
|
+
</MotionTicker>
|
|
52
|
+
<MotionTicker direction="left">
|
|
53
|
+
<TickerExampleCard text="TICKER EXAMPLE" />
|
|
54
|
+
</MotionTicker>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { TickerStatic } from "../css-ticker/css-ticker";
|
|
2
|
+
import { Text } from "@/common/ui/text/text";
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
This is a ticker component that is used to display a list of items in a ticker.
|
|
6
|
+
It is a wrapper around the css library and uses the css animations to create the ticker.
|
|
7
|
+
It is a client component that is used to display a list of items in a ticker.
|
|
8
|
+
|
|
9
|
+
To test it you can just use it like this in any page:
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const TickerExampleCard = ({
|
|
13
|
+
text = "BASIC TICKER EXAMPLE",
|
|
14
|
+
}: {
|
|
15
|
+
text?: string;
|
|
16
|
+
}) => {
|
|
17
|
+
return (
|
|
18
|
+
<div className="border-2 border-x-0 border-fill-brand-primary bg-bg-primary-inverse px-5 py-2">
|
|
19
|
+
<Text className="capitalize" variant="body.2">
|
|
20
|
+
{text}
|
|
21
|
+
</Text>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
const CardWithSpacingExample = ({
|
|
26
|
+
text = "CARD WITH SPACING EXAMPLE",
|
|
27
|
+
}: {
|
|
28
|
+
text?: string;
|
|
29
|
+
}) => {
|
|
30
|
+
return (
|
|
31
|
+
<div className="px-1">
|
|
32
|
+
<div className="border-2 border-fill-brand-primary bg-bg-primary-inverse px-5 py-2">
|
|
33
|
+
<Text className="capitalize" variant="body.2">
|
|
34
|
+
{text}
|
|
35
|
+
</Text>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const TickerStaticShowcaseHome = () => {
|
|
42
|
+
return (
|
|
43
|
+
<div className="flex flex-col gap-12">
|
|
44
|
+
<Text className="px-4 pb-15" variant="title.6">
|
|
45
|
+
Ticker static
|
|
46
|
+
</Text>
|
|
47
|
+
<TickerStatic>
|
|
48
|
+
<TickerExampleCard text="STATIC TICKER EXAMPLE" />
|
|
49
|
+
<TickerExampleCard text="TICKER" />
|
|
50
|
+
</TickerStatic>
|
|
51
|
+
<TickerStatic direction="left">
|
|
52
|
+
<CardWithSpacingExample text="TICKER EXAMPLE" />
|
|
53
|
+
</TickerStatic>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export const useTickerClones = ({
|
|
4
|
+
tickerCard,
|
|
5
|
+
tickerClone,
|
|
6
|
+
onCloneWrapper,
|
|
7
|
+
direction,
|
|
8
|
+
}: {
|
|
9
|
+
tickerCard: React.RefObject<HTMLDivElement | null>;
|
|
10
|
+
tickerClone: React.RefObject<HTMLDivElement | null>;
|
|
11
|
+
onCloneWrapper?: (clone: HTMLElement) => void;
|
|
12
|
+
direction: 'left' | 'right';
|
|
13
|
+
}) => {
|
|
14
|
+
const calculatedWidth = useRef(0);
|
|
15
|
+
const [clonesNeeded, setClonesNeeded] = useState(1);
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (typeof window === 'undefined') return;
|
|
19
|
+
const sizeCloneNeeded = () => {
|
|
20
|
+
const tickerCardContent = Array.from(tickerCard.current?.childNodes ?? []).reduce(
|
|
21
|
+
(acc, child) => {
|
|
22
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
23
|
+
return acc;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return acc + (child as HTMLElement)?.offsetWidth;
|
|
27
|
+
},
|
|
28
|
+
0,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const tickerCardWidth = tickerCardContent;
|
|
32
|
+
|
|
33
|
+
if (!tickerCardWidth) return;
|
|
34
|
+
calculatedWidth.current = tickerCardWidth;
|
|
35
|
+
const clonesCalc = Math.ceil(window.outerWidth / tickerCardWidth);
|
|
36
|
+
|
|
37
|
+
if (clonesCalc > 1) setClonesNeeded(clonesCalc);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
sizeCloneNeeded();
|
|
41
|
+
window.addEventListener('resize', sizeCloneNeeded);
|
|
42
|
+
|
|
43
|
+
return () => {
|
|
44
|
+
window.removeEventListener('resize', sizeCloneNeeded);
|
|
45
|
+
};
|
|
46
|
+
}, [tickerCard]);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (typeof window === 'undefined') return;
|
|
49
|
+
const clonedTickerContent = tickerCard.current?.cloneNode(true) as HTMLElement;
|
|
50
|
+
|
|
51
|
+
if (!clonedTickerContent) return;
|
|
52
|
+
if (tickerClone?.current?.childNodes.length) {
|
|
53
|
+
tickerClone.current.removeChild(tickerClone.current.childNodes[0]);
|
|
54
|
+
}
|
|
55
|
+
clonedTickerContent.id = 'ticker-content-clone';
|
|
56
|
+
|
|
57
|
+
if (onCloneWrapper) onCloneWrapper(clonedTickerContent);
|
|
58
|
+
|
|
59
|
+
const tickerCardWidth = tickerCard.current?.offsetWidth;
|
|
60
|
+
|
|
61
|
+
tickerClone.current?.style.setProperty(
|
|
62
|
+
'left',
|
|
63
|
+
direction === 'right' ? `-${tickerCardWidth}px` : `${tickerCardWidth}px`,
|
|
64
|
+
'important',
|
|
65
|
+
);
|
|
66
|
+
tickerClone.current?.appendChild(clonedTickerContent);
|
|
67
|
+
}, [clonesNeeded, tickerCard, tickerClone, direction, onCloneWrapper]);
|
|
68
|
+
|
|
69
|
+
return { clonesNeeded, calculatedWidth };
|
|
70
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
|
|
4
|
+
import { MotionValue, useMotionValue, useMotionValueEvent } from 'motion/react';
|
|
5
|
+
|
|
6
|
+
const directionModifier = {
|
|
7
|
+
left: false,
|
|
8
|
+
right: true,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const useTickerIncremental = ({
|
|
12
|
+
direction,
|
|
13
|
+
speed,
|
|
14
|
+
delta,
|
|
15
|
+
stopIncrement,
|
|
16
|
+
onStop,
|
|
17
|
+
}: {
|
|
18
|
+
speed: number;
|
|
19
|
+
delta: number;
|
|
20
|
+
direction: 'left' | 'right';
|
|
21
|
+
stopIncrement?: boolean;
|
|
22
|
+
onStop?: (x: MotionValue<number>) => void;
|
|
23
|
+
}) => {
|
|
24
|
+
const x = useMotionValue(0);
|
|
25
|
+
const lastTimeRef = useRef<number>(0);
|
|
26
|
+
const fpsRef = useRef(60);
|
|
27
|
+
const translateX = useMotionValue('0%');
|
|
28
|
+
const clonedTranslateX = useMotionValue('0%');
|
|
29
|
+
const requestRef = useRef<number>(0);
|
|
30
|
+
|
|
31
|
+
useMotionValueEvent(x, 'change', (value) => {
|
|
32
|
+
const modifier = directionModifier[direction] ? 1 : -1;
|
|
33
|
+
|
|
34
|
+
translateX.set(`${modifier * (value > 100 ? value - 200 : value)}%`);
|
|
35
|
+
clonedTranslateX.set(`${modifier * value}%`);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (stopIncrement) {
|
|
40
|
+
cancelAnimationFrame(requestRef.current);
|
|
41
|
+
onStop?.(x);
|
|
42
|
+
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const animate = (now: number) => {
|
|
46
|
+
const currentX = x.get();
|
|
47
|
+
const deltaMs = now - lastTimeRef.current;
|
|
48
|
+
|
|
49
|
+
lastTimeRef.current = now;
|
|
50
|
+
|
|
51
|
+
fpsRef.current = 1000 / deltaMs;
|
|
52
|
+
|
|
53
|
+
if (currentX >= 200) {
|
|
54
|
+
x.set(0);
|
|
55
|
+
} else {
|
|
56
|
+
const newX = Number((currentX + speed * delta).toFixed(4));
|
|
57
|
+
|
|
58
|
+
x.set(newX);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
requestRef.current = requestAnimationFrame(animate);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
requestRef.current = requestAnimationFrame(animate);
|
|
65
|
+
|
|
66
|
+
return () => {
|
|
67
|
+
cancelAnimationFrame(requestRef.current);
|
|
68
|
+
};
|
|
69
|
+
}, [speed, delta, x, stopIncrement, onStop]);
|
|
70
|
+
|
|
71
|
+
return { x, x1: translateX, x2: clonedTranslateX, fps: fpsRef };
|
|
72
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { Fragment, useRef, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { animate, motion, useInView } from 'motion/react';
|
|
5
|
+
|
|
6
|
+
import { cn } from '@/common/utils/classname-builder';
|
|
7
|
+
|
|
8
|
+
import { useTickerClones } from './hooks/use-ticker-clones';
|
|
9
|
+
import { useTickerIncremental } from './hooks/use-ticker-incremental';
|
|
10
|
+
type MotionTickerProps = {
|
|
11
|
+
direction?: 'left' | 'right';
|
|
12
|
+
speed?: number;
|
|
13
|
+
delta?: number;
|
|
14
|
+
className?: string;
|
|
15
|
+
wrapperClassName?: string;
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
hoverStop?: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const MotionTicker = ({
|
|
21
|
+
direction = 'right',
|
|
22
|
+
speed = 1,
|
|
23
|
+
delta = 0.05,
|
|
24
|
+
className,
|
|
25
|
+
children,
|
|
26
|
+
wrapperClassName,
|
|
27
|
+
hoverStop = true,
|
|
28
|
+
}: MotionTickerProps) => {
|
|
29
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
30
|
+
const tickerClone = useRef<HTMLDivElement>(null);
|
|
31
|
+
const cardWrapper = useRef<HTMLDivElement>(null);
|
|
32
|
+
const [isHovering, setIsHovering] = useState(false);
|
|
33
|
+
const isInView = useInView(containerRef, {
|
|
34
|
+
once: false,
|
|
35
|
+
margin: '50% 0%',
|
|
36
|
+
});
|
|
37
|
+
const { clonesNeeded } = useTickerClones({ tickerCard: cardWrapper, tickerClone, direction });
|
|
38
|
+
const calculateProportionalDuration = (distance: number, fps?: number) => {
|
|
39
|
+
const distancePerFrame = speed * delta;
|
|
40
|
+
const framesNeeded = distance / distancePerFrame;
|
|
41
|
+
|
|
42
|
+
return framesNeeded / (fps ?? 60); // calc is based on 60fps
|
|
43
|
+
};
|
|
44
|
+
const { x1, x2, fps } = useTickerIncremental({
|
|
45
|
+
speed,
|
|
46
|
+
direction,
|
|
47
|
+
delta,
|
|
48
|
+
stopIncrement: isHovering || !isInView,
|
|
49
|
+
onStop: async (motionValue) => {
|
|
50
|
+
if (!isInView) return;
|
|
51
|
+
const distance = 3;
|
|
52
|
+
const EASE_OUT_COMPENSATION = 1.15;
|
|
53
|
+
const duration =
|
|
54
|
+
calculateProportionalDuration(distance, fps?.current) * EASE_OUT_COMPENSATION;
|
|
55
|
+
|
|
56
|
+
return await animate(motionValue, motionValue.get() + distance, {
|
|
57
|
+
duration,
|
|
58
|
+
ease: 'easeOut',
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div
|
|
65
|
+
ref={containerRef}
|
|
66
|
+
className={cn('group relative w-full overflow-hidden', wrapperClassName)}
|
|
67
|
+
role="group"
|
|
68
|
+
onMouseEnter={() => {
|
|
69
|
+
if (!hoverStop) return;
|
|
70
|
+
setIsHovering(true);
|
|
71
|
+
}}
|
|
72
|
+
onMouseLeave={() => {
|
|
73
|
+
if (!hoverStop) return;
|
|
74
|
+
setIsHovering(false);
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
<motion.div
|
|
78
|
+
ref={cardWrapper}
|
|
79
|
+
className={cn('flex w-max flex-row', className)}
|
|
80
|
+
style={{ x: x1 }}
|
|
81
|
+
>
|
|
82
|
+
{Array.from({ length: clonesNeeded }).map((_, index) => {
|
|
83
|
+
return <Fragment key={index}>{children}</Fragment>;
|
|
84
|
+
})}
|
|
85
|
+
</motion.div>
|
|
86
|
+
<motion.div
|
|
87
|
+
ref={tickerClone}
|
|
88
|
+
className={cn('absolute top-0 h-full w-max', className)}
|
|
89
|
+
style={{ x: x2 }}
|
|
90
|
+
/>
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
};
|