@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.
Files changed (53) hide show
  1. package/README.md +86 -37
  2. package/package.json +11 -16
  3. package/src/commands/add.js +107 -0
  4. package/src/commands/list.js +51 -0
  5. package/src/index.js +46 -15
  6. package/src/templates/carousel/components/navigation-buttons.tsx +1 -0
  7. package/src/templates/carousel/components/pagination/bullet.pagination.carousel.tsx +69 -0
  8. package/src/templates/carousel/components/pagination/number.pagination.carousel.tsx +30 -0
  9. package/src/templates/carousel/components/pagination/progress/progress.pagination.carousel.tsx +99 -0
  10. package/src/templates/carousel/components/pagination/progress/use-slide-progress.tsx +31 -0
  11. package/src/templates/carousel/components/pagination.tsx +47 -82
  12. package/src/templates/faqs-accordion/examples/faqs-showcase.tsx +42 -0
  13. package/src/templates/faqs-accordion/faqs-accordion.tsx +70 -0
  14. package/src/templates/faqs-accordion/mock-data.ts +38 -0
  15. package/src/templates/faqs-accordion/types.ts +18 -0
  16. package/src/templates/in-view/data.in-view.ts +89 -0
  17. package/src/templates/in-view/examples/in-view-examples.home.tsx +101 -0
  18. package/src/templates/in-view/examples/in-view-grid-showcase.tsx +41 -0
  19. package/src/templates/in-view/in-view-animation.tsx +72 -0
  20. package/src/templates/in-view/in-view-grid.tsx +81 -0
  21. package/src/templates/in-view/in-view-hidden-text.tsx +45 -0
  22. package/src/templates/in-view/in-view-stroke-line.tsx +30 -0
  23. package/src/templates/lenis/examples/providers.tsx +23 -0
  24. package/src/templates/lenis/lenis-provider.tsx +46 -0
  25. package/src/templates/scroll-components/hooks/use-client-dimensions.ts +21 -0
  26. package/src/templates/scroll-components/parallax/examples/parallax-showcase.tsx +87 -0
  27. package/src/templates/scroll-components/parallax/parallax.css +36 -0
  28. package/src/templates/scroll-components/parallax/parallax.tsx +67 -0
  29. package/src/templates/scroll-components/scale-gallery/components/expanding-element.tsx +40 -0
  30. package/src/templates/scroll-components/scale-gallery/examples/scale-gallery-showcase.tsx +68 -0
  31. package/src/templates/scroll-components/scale-gallery/scale-gallery.tsx +57 -0
  32. package/src/templates/scroll-components/scroll-tracker-provider.tsx +78 -0
  33. package/src/templates/scroll-components/scroll-tracker-showcase.tsx +44 -0
  34. package/src/templates/strapi-dynamic-zone/README.md +157 -0
  35. package/src/templates/strapi-dynamic-zone/dynamic-zone.tsx +113 -0
  36. package/src/templates/strapi-dynamic-zone/examples/page.tsx +53 -0
  37. package/src/templates/strapi-dynamic-zone/examples/renderers.tsx +74 -0
  38. package/src/templates/strapi-dynamic-zone/examples/types.ts +41 -0
  39. package/src/templates/strapi-dynamic-zone/index.ts +11 -0
  40. package/src/templates/strapi-dynamic-zone/types.ts +73 -0
  41. package/src/templates/ticker/css-ticker/css-ticker.tsx +61 -0
  42. package/src/templates/ticker/css-ticker/ticker.keyframes.css +86 -0
  43. package/src/templates/ticker/examples/ticker-hover-showcase.home.tsx +57 -0
  44. package/src/templates/ticker/examples/ticker-static-showcase.home.tsx +56 -0
  45. package/src/templates/ticker/hooks/use-ticker-clones.tsx +70 -0
  46. package/src/templates/ticker/hooks/use-ticker-incremental.tsx +72 -0
  47. package/src/templates/ticker/motion-ticker.tsx +93 -0
  48. package/src/utils/components.js +587 -54
  49. package/src/utils/files.js +89 -5
  50. package/src/templates/button/button.tsx +0 -5
  51. package/src/templates/card/card.tsx +0 -5
  52. package/src/templates/example/example.tsx +0 -5
  53. package/src/templates/hero/hero.tsx +0 -5
@@ -0,0 +1,31 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+
3
+ export const useSlideProgress = ({ isActive }: { isActive: boolean }) => {
4
+ const [isDragging, setIsDragging] = useState(false);
5
+ const barRef = useRef<HTMLButtonElement>(null);
6
+
7
+ useEffect(() => {
8
+ if (!isActive) return;
9
+ const cancelDrag = () => setIsDragging(false);
10
+
11
+ if (isDragging) {
12
+ document.body.addEventListener('mouseup', cancelDrag);
13
+ document.body.addEventListener('touchend', cancelDrag);
14
+ barRef.current?.style.setProperty('cursor', 'grabbing');
15
+ document.body.style.setProperty('cursor', 'grabbing');
16
+ }
17
+ if (!isDragging) {
18
+ document.body.removeEventListener('mouseup', cancelDrag);
19
+ document.body.removeEventListener('touchend', cancelDrag);
20
+ document.body.style.setProperty('cursor', 'default');
21
+ barRef.current?.style.setProperty('cursor', 'grab');
22
+ }
23
+
24
+ return () => {
25
+ document.body.removeEventListener('mouseup', cancelDrag);
26
+ document.body.removeEventListener('touchend', cancelDrag);
27
+ };
28
+ }, [isDragging, isActive]);
29
+
30
+ return { isDragging, barRef, setIsDragging };
31
+ };
@@ -1,89 +1,54 @@
1
+ 'use client';
1
2
  import React from 'react';
2
3
 
3
- import { cva, VariantProps } from 'class-variance-authority';
4
-
5
- import { cn } from '@/common/utils/classname-builder';
6
-
7
4
  import { useCarouselContext } from './provider/carousel.provider';
8
5
 
9
- export const Pagination: React.FC<
10
- React.HTMLAttributes<HTMLDivElement> & { type?: 'dot' | 'number' | 'progress' }
11
- > = ({ className, type = 'dot', ...props }) => {
6
+ import { BulletPagination, DotPaginationProps } from './pagination/bullet.pagination.carousel';
7
+ import { NumberPagination, NumberPaginationProps } from './pagination/number.pagination.carousel';
8
+ import {
9
+ ProgressPagination,
10
+ ProgressPaginationProps,
11
+ } from './pagination/progress/progress.pagination.carousel';
12
+
13
+ type OmitContext<T> = Omit<T, 'selectedIndex' | 'scrollSnaps' | 'onDotButtonClick'>;
14
+ type PaginationProps =
15
+ | ({ type: 'number' } & OmitContext<NumberPaginationProps>)
16
+ | ({ type: 'progress' } & OmitContext<ProgressPaginationProps>)
17
+ | ({ type: 'bullet' } & OmitContext<DotPaginationProps>);
18
+
19
+ export const Pagination = (props: PaginationProps) => {
20
+ const { type, variant, className, ...restProps } = props;
12
21
  const { scrollSnaps, selectedIndex, onDotButtonClick } = useCarouselContext();
13
22
 
14
- if (type === 'number')
15
- return (
16
- <div className={cn('mx-auto flex w-max gap-2', className)} {...props}>
17
- <p className="text-body-2 text-grey-50-15">
18
- {selectedIndex + 1} / {scrollSnaps.length}
19
- </p>
20
- </div>
21
- );
22
- if (type === 'progress')
23
- return (
24
- <div
25
- className={cn(
26
- 'relative mx-auto flex h-2 w-200 max-w-full gap-2 overflow-hidden rounded-full bg-bg-primary',
27
- className,
28
- )}
29
- {...props}
30
- >
31
- <div
32
- className="pointer-events-none absolute bottom-0 left-0 h-2 w-full bg-fill-brand-primary transition-all duration-300 ease-out"
33
- style={{
34
- transformOrigin: 'left',
35
- transform: `scaleX(${(selectedIndex + 1) / scrollSnaps.length})`,
36
- }}
37
- />
38
- {scrollSnaps.map((_, index) => (
39
- <div
40
- key={index}
41
- className="h-2 w-full cursor-pointer"
42
- onClick={() => onDotButtonClick(index)}
43
- />
44
- ))}
45
- </div>
46
- );
47
-
48
- if (type === 'dot')
49
- return (
50
- <div className={cn('mx-auto flex w-max gap-2', className)} {...props}>
51
- {scrollSnaps.map((_, index) => (
52
- <DotButton key={index} index={index} variant="primary" />
53
- ))}
54
- </div>
55
- );
56
- };
57
-
58
- type DotButtonVariants = VariantProps<typeof dotButtonVariants>['variant'];
59
- const dotButtonVariants = cva(
60
- 'size-3 cursor-pointer rounded-full transition-all duration-300 ease-out focus:outline-2',
61
- {
62
- variants: {
63
- variant: {
64
- primary: 'bg-fill-brand-primary',
65
- },
66
- selected: {
67
- true: 'w-9 !bg-amber-950',
68
- false: '',
69
- },
70
- },
71
- },
72
- );
73
-
74
- export const DotButton: React.FC<{ index: number; variant: DotButtonVariants }> = ({
75
- index,
76
- variant = 'primary',
77
- ...restProps
78
- }) => {
79
- const { selectedIndex, onDotButtonClick } = useCarouselContext();
80
-
81
- return (
82
- <button
83
- className={dotButtonVariants({ variant, selected: selectedIndex === index })}
84
- disabled={selectedIndex === index}
85
- onClick={() => onDotButtonClick(index)}
86
- {...restProps}
87
- />
88
- );
23
+ const paginationComponents = {
24
+ number: () => (
25
+ <NumberPagination
26
+ className={className}
27
+ scrollSnaps={scrollSnaps}
28
+ selectedIndex={selectedIndex}
29
+ variant={variant as NumberPaginationProps['variant']}
30
+ {...restProps}
31
+ />
32
+ ),
33
+ progress: () => (
34
+ <ProgressPagination
35
+ className={className}
36
+ scrollSnaps={scrollSnaps}
37
+ selectedIndex={selectedIndex}
38
+ variant={variant as ProgressPaginationProps['variant']}
39
+ onDotButtonClick={onDotButtonClick}
40
+ {...restProps}
41
+ />
42
+ ),
43
+ bullet: () => (
44
+ <BulletPagination
45
+ className={className}
46
+ scrollSnaps={scrollSnaps}
47
+ variant={variant as DotPaginationProps['variant']}
48
+ {...restProps}
49
+ />
50
+ ),
51
+ } as const;
52
+
53
+ return paginationComponents[type]();
89
54
  };
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+
3
+ import { FaqsAccordion, MOCK_FAQS_DATA } from '../faqs-accordion';
4
+
5
+ /**
6
+ * Example showcase for FaqsAccordion component
7
+ *
8
+ * This simulates a Strapi API call with mock data.
9
+ * In production, replace MOCK_FAQS_DATA with:
10
+ *
11
+ * const response = await fetch('your-strapi-url/api/faqs?populate=questions');
12
+ * const faqs = await response.json();
13
+ */
14
+ export default function FaqsShowcase() {
15
+ // Simulating Strapi response with mock data
16
+ const faqsData = MOCK_FAQS_DATA;
17
+
18
+ return (
19
+ <div className="min-h-screen bg-gray-50 py-12">
20
+ <div className="container mx-auto px-4">
21
+ <h1 className="mb-12 text-center text-4xl font-bold">
22
+ FAQs Accordion Example
23
+ </h1>
24
+
25
+ <FaqsAccordion faqs={faqsData} />
26
+
27
+ {/* Example with custom styling */}
28
+ <div className="mt-16">
29
+ <h2 className="mb-8 text-center text-3xl font-bold">
30
+ Custom Styled Version
31
+ </h2>
32
+ <FaqsAccordion
33
+ faqs={faqsData}
34
+ className="rounded-lg bg-white p-8 shadow-lg"
35
+ questionClassName="text-blue-600"
36
+ answerClassName="text-gray-700"
37
+ />
38
+ </div>
39
+ </div>
40
+ </div>
41
+ );
42
+ }
@@ -0,0 +1,70 @@
1
+ 'use client';
2
+
3
+ import * as Accordion from '@radix-ui/react-accordion';
4
+ import { FaqsAccordionProps } from './types';
5
+
6
+ const ChevronDownIcon = ({ className }: { className?: string }) => (
7
+ <svg
8
+ width="24"
9
+ height="24"
10
+ viewBox="0 0 24 24"
11
+ fill="none"
12
+ xmlns="http://www.w3.org/2000/svg"
13
+ className={className}
14
+ >
15
+ <path
16
+ d="M6 9L12 15L18 9"
17
+ stroke="currentColor"
18
+ strokeWidth="2"
19
+ strokeLinecap="round"
20
+ strokeLinejoin="round"
21
+ />
22
+ </svg>
23
+ );
24
+
25
+ export const FaqsAccordion = ({
26
+ faqs,
27
+ className = '',
28
+ questionClassName = '',
29
+ answerClassName = '',
30
+ }: FaqsAccordionProps) => {
31
+ return (
32
+ <div className={`mx-auto w-full max-w-[910px] ${className}`}>
33
+ <h2 className="mb-8 text-3xl font-bold">{faqs.title}</h2>
34
+
35
+ <Accordion.Root type="single" collapsible className="w-full">
36
+ {faqs.questions.map((faq, index) => (
37
+ <div key={faq.id}>
38
+ <Accordion.Item value={`item-${faq.id}`} className="border-b">
39
+ <Accordion.Header>
40
+ <Accordion.Trigger
41
+ className={`group flex w-full items-center justify-between py-6 text-left transition-all hover:opacity-80 ${questionClassName}`}
42
+ >
43
+ <span className="text-lg font-semibold">{faq.question}</span>
44
+ <ChevronDownIcon className="transition-transform duration-300 group-data-[state=open]:rotate-180" />
45
+ </Accordion.Trigger>
46
+ </Accordion.Header>
47
+
48
+ <Accordion.Content
49
+ className={`overflow-hidden transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down ${answerClassName}`}
50
+ >
51
+ <div className="pb-6 text-base text-gray-600">
52
+ {faq.answer}
53
+ </div>
54
+ </Accordion.Content>
55
+ </Accordion.Item>
56
+ </div>
57
+ ))}
58
+ </Accordion.Root>
59
+ </div>
60
+ );
61
+ };
62
+
63
+ export default FaqsAccordion;
64
+
65
+ export type {
66
+ FaqsAccordionProps,
67
+ FaqsResponse,
68
+ FaqQuestion,
69
+ } from './types';
70
+ export { MOCK_FAQS_DATA } from './mock-data';
@@ -0,0 +1,38 @@
1
+ import { FaqsResponse } from './types';
2
+
3
+ export const MOCK_FAQS_DATA: FaqsResponse = {
4
+ id: 1,
5
+ title: 'Frequently Asked Questions',
6
+ questions: [
7
+ {
8
+ id: 1,
9
+ question: 'What is the return policy?',
10
+ answer:
11
+ 'You can return any item within 30 days of purchase for a full refund. Items must be in original condition with tags attached.',
12
+ },
13
+ {
14
+ id: 2,
15
+ question: 'How long does shipping take?',
16
+ answer:
17
+ 'Standard shipping takes 5-7 business days. Express shipping is available and takes 2-3 business days.',
18
+ },
19
+ {
20
+ id: 3,
21
+ question: 'Do you ship internationally?',
22
+ answer:
23
+ 'Yes, we ship to over 50 countries worldwide. International shipping times vary by location.',
24
+ },
25
+ {
26
+ id: 4,
27
+ question: 'How do I track my order?',
28
+ answer:
29
+ 'Once your order ships, you\'ll receive a tracking number via email. You can use this to track your package on our website.',
30
+ },
31
+ {
32
+ id: 5,
33
+ question: 'What payment methods do you accept?',
34
+ answer:
35
+ 'We accept all major credit cards, PayPal, Apple Pay, and Google Pay for your convenience.',
36
+ },
37
+ ],
38
+ };
@@ -0,0 +1,18 @@
1
+ export type FaqQuestion = {
2
+ id: number;
3
+ question: string;
4
+ answer: string;
5
+ };
6
+
7
+ export type FaqsResponse = {
8
+ id: number;
9
+ title: string;
10
+ questions: FaqQuestion[];
11
+ };
12
+
13
+ export type FaqsAccordionProps = {
14
+ faqs: FaqsResponse;
15
+ className?: string;
16
+ questionClassName?: string;
17
+ answerClassName?: string;
18
+ };
@@ -0,0 +1,89 @@
1
+ const IN_VIEW_ANIMATIONS = {
2
+ fadeInUp: {
3
+ hidden: {
4
+ y: '100px',
5
+ opacity: 0,
6
+ },
7
+ visible: {
8
+ opacity: 1,
9
+ y: '0%',
10
+ },
11
+ },
12
+ fadeInLeft: {
13
+ hidden: { opacity: 0, transform: `translateX(-100px)` },
14
+ visible: { opacity: 1, transform: `translateX(0px)` },
15
+ },
16
+ fadeInDown: {
17
+ hidden: { opacity: 0, transform: `translateY(-50px)` },
18
+ visible: { opacity: 1, transform: `translateY(0px)` },
19
+ },
20
+ fadeInRight: {
21
+ hidden: { opacity: 0, transform: `translateX(100px)` },
22
+ visible: { opacity: 1, transform: `translateX(0px)` },
23
+ },
24
+ opacity: {
25
+ hidden: { opacity: 0 },
26
+ visible: { opacity: 1 },
27
+ },
28
+ imgBlur: {
29
+ hidden: {
30
+ filter: 'blur(10px)',
31
+ backdropFilter: 'blur(10px)',
32
+ WebkitBackdropFilter: 'blur(10px)',
33
+ overflow: 'hidden',
34
+ },
35
+ visible: {
36
+ overflow: 'hidden',
37
+ filter: 'blur(0px)',
38
+ backdropFilter: 'blur(0px)',
39
+ WebkitBackdropFilter: 'blur(0)',
40
+ },
41
+ },
42
+ scaleX: {
43
+ hidden: {
44
+ scaleX: 0,
45
+ },
46
+ visible: {
47
+ scaleX: 1,
48
+ },
49
+ },
50
+ strokeLine: {
51
+ hidden: {
52
+ scaleX: 0.001,
53
+ transformOrigin: 'left center',
54
+ },
55
+ visible: {
56
+ scaleX: 1,
57
+ transformOrigin: 'left center',
58
+ },
59
+ },
60
+ slideInUp: {
61
+ hidden: {
62
+ y: '85%',
63
+ },
64
+ visible: {
65
+ y: '0%',
66
+ },
67
+ },
68
+ slideInLeftFull: {
69
+ hidden: {
70
+ x: '-100%',
71
+ },
72
+ visible: {
73
+ x: '0%',
74
+ },
75
+ },
76
+ fadeInRightFull: {
77
+ hidden: {
78
+ opacity: 0,
79
+ x: '100%',
80
+ },
81
+ visible: {
82
+ opacity: 1,
83
+ x: '0%',
84
+ },
85
+ },
86
+ };
87
+
88
+ export default IN_VIEW_ANIMATIONS;
89
+
@@ -0,0 +1,101 @@
1
+ import { Fragment } from "react";
2
+
3
+ import InViewAnimation from "../in-view-animation";
4
+ import { InViewHiddenText } from "../in-view/in-view-hidden-text";
5
+ import { InViewStrokeLine } from "../in-view/in-view-stroke-line";
6
+ import { Text } from "@/common/ui/text/text";
7
+
8
+ const InViewExampleCard = ({ text }: { text?: string }) => {
9
+ return (
10
+ <div className="flex flex-col items-center justify-center h-50 w-full rounded-2xl border-2 border-fill-brand-primary bg-bg-primary-inverse p-2">
11
+ <Text
12
+ className="text-text-interactive-neutral-primary-inverse"
13
+ variant="title.6"
14
+ >
15
+ {text}
16
+ </Text>
17
+ </div>
18
+ );
19
+ };
20
+
21
+ export const InViewExamplesHome = () => {
22
+ return (
23
+ <div className="container mx-auto max-w-480">
24
+ <Text className="pb-15" variant="title.6">
25
+ In View Examples {"(Individual Elements)"}
26
+ </Text>
27
+ <div className="grid w-full gap-2 gap-y-2 overflow-hidden max-lg:grid-cols-1 lg:grid-cols-3 lg:gap-y-10">
28
+ <InViewAnimation effect="fadeInLeft">
29
+ <InViewExampleCard text="Fade Left Effect" />
30
+ </InViewAnimation>
31
+ <InViewAnimation
32
+ effect="opacity"
33
+ transition={{
34
+ delay: 0.2,
35
+ }}
36
+ >
37
+ <InViewExampleCard text="Opacity Effect" />
38
+ </InViewAnimation>
39
+ <InViewAnimation effect="fadeInRight">
40
+ <InViewExampleCard text="Fade Right Effect" />
41
+ </InViewAnimation>
42
+ <div className="flex w-full flex-col flex-wrap justify-end gap-5 lg:col-span-2">
43
+ <InViewStrokeLine
44
+ className="bg-fill-brand-primary opacity-40"
45
+ transition={{
46
+ duration: 0.8,
47
+ }}
48
+ />
49
+ <InViewHiddenText>
50
+ <Text className="py-1 text-grey-100-80">
51
+ Hidden text effect and stroke line
52
+ </Text>
53
+ </InViewHiddenText>
54
+
55
+ {Array.from({ length: 4 }).map((_, index) => (
56
+ <Fragment key={index}>
57
+ <InViewStrokeLine
58
+ className="bg-fill-brand-primary opacity-40"
59
+ transition={{
60
+ duration: 0.8,
61
+ delay: 0.1 * (index + 1),
62
+ }}
63
+ />
64
+ <InViewHiddenText
65
+ transition={{
66
+ delay: 0.2 * (index + 1),
67
+ }}
68
+ >
69
+ <Text className="text-grey-100-80">
70
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
71
+ </Text>
72
+ </InViewHiddenText>
73
+ </Fragment>
74
+ ))}
75
+ <InViewStrokeLine
76
+ className="bg-fill-brand-primary opacity-40"
77
+ transition={{
78
+ duration: 0.8,
79
+ delay: 0.4,
80
+ }}
81
+ />
82
+ </div>
83
+ <InViewAnimation
84
+ className="lg:col-start-3"
85
+ effect="imgBlur"
86
+ once={false}
87
+ >
88
+ <Center className="relative h-102 w-full overflow-hidden rounded-2xl bg-fill-brand-primary">
89
+ <img alt="Placeholder" src="/img/racti-sireno.jpeg" />
90
+ <Text
91
+ className="absolute bottom-1/4 rounded-2xl bg-grey-50-30 p-2 text-text-primary"
92
+ variant="title.6"
93
+ >
94
+ Blur Effect
95
+ </Text>
96
+ </Center>
97
+ </InViewAnimation>
98
+ </div>
99
+ </div>
100
+ );
101
+ };
@@ -0,0 +1,41 @@
1
+ import { GridInView } from "@/common/animations/in-view/in-view-grid";
2
+ import { Center } from "@/common/ui/containers/center";
3
+ import { Text } from "@/common/ui/text/text";
4
+ import { cn } from "@/common/utils/classname-builder";
5
+
6
+ const CardComponent = ({
7
+ pepito,
8
+ className,
9
+ }: {
10
+ pepito: number;
11
+ className?: string;
12
+ }) => {
13
+ return (
14
+ <div className="flex flex-col items-center justify-center h-50 w-full rounded-2xl border-2 border-fill-brand-primary bg-bg-primary-inverse p-2">
15
+ <Text>{pepito}</Text>
16
+ </div>
17
+ );
18
+ };
19
+
20
+ export const InViewGridShowcase = () => {
21
+ return (
22
+ <>
23
+ <div>
24
+ <h1>In View Grid:</h1>
25
+ <GridInView columns={5} delayByColumn={false}>
26
+ {Array.from({ length: 10 }, (_, index) => index).map((item) => (
27
+ <CardComponent key={item} pepito={item + 1} />
28
+ ))}
29
+ </GridInView>
30
+ </div>
31
+ <div>
32
+ <h1>In View Grid with delay by column:</h1>
33
+ <GridInView columns={{ desktop: 4, mobile: 2 }}>
34
+ {Array.from({ length: 12 }, (_, index) => index).map((item) => (
35
+ <CardComponent key={item} className="h-100" pepito={item + 1} />
36
+ ))}
37
+ </GridInView>
38
+ </div>
39
+ </>
40
+ );
41
+ };
@@ -0,0 +1,72 @@
1
+ 'use client';
2
+ import { FC, ReactNode } from 'react';
3
+
4
+ import {
5
+ motion,
6
+ MotionProps,
7
+ TargetAndTransition,
8
+ Transition,
9
+ } from 'motion/react';
10
+
11
+ import IN_VIEW_ANIMATIONS from './data.in-view';
12
+
13
+ const transitionsDefault: Transition = {
14
+ duration: 0.6,
15
+ delay: 0,
16
+ ease: 'easeInOut',
17
+ };
18
+
19
+ export type InViewAnimationProps = {
20
+ margin?: string;
21
+ transition?: Transition;
22
+ effect?: keyof typeof IN_VIEW_ANIMATIONS;
23
+ customEffect?: {
24
+ hidden: TargetAndTransition;
25
+ visible: TargetAndTransition;
26
+ };
27
+ execute?: boolean;
28
+ once?: boolean;
29
+ style?: React.CSSProperties;
30
+ className?: string;
31
+ children?: ReactNode;
32
+ } & MotionProps &
33
+ React.HTMLAttributes<HTMLDivElement>;
34
+
35
+ const InViewAnimation: FC<InViewAnimationProps> = ({
36
+ margin = '-10% 0%',
37
+ execute = true,
38
+ transition = {},
39
+ customEffect,
40
+ effect = 'fadeInUp',
41
+ once = true,
42
+ style,
43
+ className,
44
+ children,
45
+ ...props
46
+ }) => (
47
+ <motion.div
48
+ className={className}
49
+ initial={customEffect?.hidden || IN_VIEW_ANIMATIONS[effect].hidden}
50
+ style={style}
51
+ transition={{
52
+ ...transitionsDefault,
53
+ ...transition,
54
+ }}
55
+ viewport={
56
+ execute
57
+ ? {
58
+ once,
59
+ margin,
60
+ }
61
+ : {}
62
+ }
63
+ whileInView={
64
+ execute ? customEffect?.visible || IN_VIEW_ANIMATIONS[effect].visible : {}
65
+ }
66
+ {...props}
67
+ >
68
+ {children}
69
+ </motion.div>
70
+ );
71
+
72
+ export default InViewAnimation;