@androbinco/library-cli 0.1.0 → 0.2.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/index.js +70 -4
- 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 +79 -0
- package/src/templates/in-view/in-view-hidden-text.tsx +45 -0
- package/src/templates/in-view/in-view-stroke-line.tsx +29 -0
- package/src/templates/scroll-tracker/examples/scroll-tracker-showcase.tsx +90 -0
- package/src/templates/scroll-tracker/scroll-tracker-provider.tsx +78 -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 +406 -55
- package/src/utils/files.js +88 -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
package/README.md
CHANGED
|
@@ -59,18 +59,20 @@ robin-library-cli
|
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
The CLI will:
|
|
62
|
-
1. Display a welcome message
|
|
62
|
+
1. Display a welcome message with a link to view examples online
|
|
63
63
|
2. Check for React dependency in your `package.json` (warns if not found)
|
|
64
64
|
3. Show an interactive menu of available components
|
|
65
|
-
4.
|
|
65
|
+
4. For components with variants, show a submenu to select specific configurations
|
|
66
|
+
5. Copy the selected component to `src/components/{component-name}/`
|
|
66
67
|
|
|
67
68
|
### Component Structure & Flow
|
|
68
69
|
|
|
69
70
|
Flow:
|
|
70
|
-
1. Main menu (
|
|
71
|
-
2. Components: multiselect prompt —
|
|
72
|
-
3.
|
|
73
|
-
4.
|
|
71
|
+
1. Main menu (Components or Exit)
|
|
72
|
+
2. Components: multiselect prompt — "Please select one or more components"
|
|
73
|
+
3. **Submenu Selection** (for components with variants): Some components offer submenus to select specific variants or configurations
|
|
74
|
+
4. Copy selected components (one run can copy many)
|
|
75
|
+
5. Post-action prompt: "Do something else?" (loop back to menu) or Exit
|
|
74
76
|
|
|
75
77
|
Components are copied to the following structure:
|
|
76
78
|
|
|
@@ -97,33 +99,77 @@ Additional safety:
|
|
|
97
99
|
|
|
98
100
|
## Available Components
|
|
99
101
|
|
|
100
|
-
###
|
|
101
|
-
|
|
102
|
-
- **Name**: `
|
|
103
|
-
- **Description**:
|
|
104
|
-
- **
|
|
105
|
-
- **
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
-
|
|
110
|
-
-
|
|
111
|
-
- **
|
|
112
|
-
- **
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
118
|
-
|
|
119
|
-
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
- **
|
|
124
|
-
- **
|
|
125
|
-
- **
|
|
126
|
-
- **
|
|
102
|
+
### Carousel Component
|
|
103
|
+
|
|
104
|
+
- **Name**: `carousel`
|
|
105
|
+
- **Description**: Carousel component with navigation and pagination
|
|
106
|
+
- **Dependencies**: `embla-carousel-react`, `embla-carousel`, `class-variance-authority`
|
|
107
|
+
- **Files**:
|
|
108
|
+
- `carousel.barrel.tsx` (main component)
|
|
109
|
+
- `components/` (navigation buttons, pagination, provider)
|
|
110
|
+
- `hooks/` (use-slide-active hook)
|
|
111
|
+
- `styles/` (embla.css styles)
|
|
112
|
+
- `examples/` (example implementations)
|
|
113
|
+
- **Location**: `src/components/carousel/`
|
|
114
|
+
- **Features**:
|
|
115
|
+
- Navigation buttons (prev/next)
|
|
116
|
+
- Multiple pagination types (dots, numbers, progress bar)
|
|
117
|
+
- Responsive breakpoints
|
|
118
|
+
- Loop support
|
|
119
|
+
- Slide active state tracking
|
|
120
|
+
|
|
121
|
+
### In-View Component
|
|
122
|
+
|
|
123
|
+
- **Name**: `in-view`
|
|
124
|
+
- **Description**: In view animations for scroll-triggered effects
|
|
125
|
+
- **Dependencies**: `motion` (Framer Motion)
|
|
126
|
+
- **Location**: `src/components/in-view/`
|
|
127
|
+
- **Variants** (selectable via submenu):
|
|
128
|
+
- **Grid**: Grid-based animations
|
|
129
|
+
- Files: `in-view-grid.tsx`, `in-view-animation.tsx`, `data.in-view.ts`
|
|
130
|
+
- Example: `in-view-grid-showcase.tsx`
|
|
131
|
+
- **Individual Elements**: Individual element animations
|
|
132
|
+
- Files: `in-view-hidden-text.tsx`, `in-view-stroke-line.tsx`, `in-view-animation.tsx`, `data.in-view.ts`
|
|
133
|
+
- Example: `in-view-examples.home.tsx`
|
|
134
|
+
- **Features**:
|
|
135
|
+
- Scroll-triggered animations
|
|
136
|
+
- Grid-based and individual element animations
|
|
137
|
+
- Reusable animation utilities
|
|
138
|
+
- Type-safe data attributes
|
|
139
|
+
|
|
140
|
+
### Ticker Component
|
|
141
|
+
|
|
142
|
+
- **Name**: `ticker`
|
|
143
|
+
- **Description**: Ticker component with hover and non-hover variants
|
|
144
|
+
- **Dependencies**: `motion` (Framer Motion)
|
|
145
|
+
- **Location**: `src/components/ticker/`
|
|
146
|
+
- **Variants** (selectable via submenu):
|
|
147
|
+
- **Stop on hover ticker (motion)**: MotionTicker with hover stop functionality
|
|
148
|
+
- Files: `motion-ticker.tsx`, `hooks/` (use-ticker-clones, use-ticker-incremental)
|
|
149
|
+
- Example: `ticker-hover-showcase.home.tsx`
|
|
150
|
+
- **Non-stop on hover ticker (css)**: TickerStatic with CSS animations
|
|
151
|
+
- Files: `css-ticker/css-ticker.tsx`, `css-ticker/ticker.keyframes.css`
|
|
152
|
+
- Example: `ticker-static-showcase.home.tsx`
|
|
153
|
+
- **Features**:
|
|
154
|
+
- Infinite scrolling ticker
|
|
155
|
+
- Hover to pause (motion variant)
|
|
156
|
+
- CSS-based animation option
|
|
157
|
+
- Customizable speed and direction
|
|
158
|
+
|
|
159
|
+
### Scroll Tracker Component
|
|
160
|
+
|
|
161
|
+
- **Name**: `scroll-tracker`
|
|
162
|
+
- **Description**: Scroll tracker provider for scroll-driven animations
|
|
163
|
+
- **Dependencies**: `motion` (Framer Motion)
|
|
164
|
+
- **Files**:
|
|
165
|
+
- `scroll-tracker-provider.tsx` (main provider component)
|
|
166
|
+
- `examples/scroll-tracker-showcase.tsx` (example implementation)
|
|
167
|
+
- **Location**: `src/components/scroll-tracker/`
|
|
168
|
+
- **Features**:
|
|
169
|
+
- Scroll progress tracking
|
|
170
|
+
- Context-based scroll state management
|
|
171
|
+
- Enables scroll-driven animations throughout your app
|
|
172
|
+
- Works seamlessly with Framer Motion
|
|
127
173
|
|
|
128
174
|
More components will be added in future updates.
|
|
129
175
|
|
|
@@ -132,6 +178,7 @@ More components will be added in future updates.
|
|
|
132
178
|
- ✨ Beautiful, modern UI with Astro-like aesthetics
|
|
133
179
|
- 🎯 Interactive component selection
|
|
134
180
|
- 🔍 Automatic React dependency detection
|
|
181
|
+
- 📦 Automatic dependency installation (detects and installs missing npm packages)
|
|
135
182
|
- 🛡️ Safe overwrite handling (checks for empty directories)
|
|
136
183
|
- 📁 Automatic directory creation
|
|
137
184
|
- ⚠️ Helpful warnings and error messages
|
|
@@ -141,13 +188,15 @@ More components will be added in future updates.
|
|
|
141
188
|
|
|
142
189
|
1. **Dependency Check**: The CLI checks your `package.json` for React. If not found, it displays a warning but still allows you to proceed (you might add React later).
|
|
143
190
|
|
|
144
|
-
2. **Component Selection**: An interactive menu displays all available components with descriptions.
|
|
191
|
+
2. **Component Selection**: An interactive menu displays all available components with descriptions. You can select multiple components at once. Some components offer submenus to select specific variants or configurations (e.g., Grid vs Individual Elements for in-view animations).
|
|
192
|
+
|
|
193
|
+
3. **Dependency Installation**: The CLI automatically detects missing dependencies required by selected components and offers to install them using your project's package manager (npm, pnpm, or yarn).
|
|
145
194
|
|
|
146
|
-
|
|
195
|
+
4. **Directory Check**: Before copying, the CLI checks if the destination directory exists and whether it's empty.
|
|
147
196
|
|
|
148
|
-
|
|
197
|
+
5. **Copy Operation**: All files from the component template are copied to `src/components/{component-name}/`, creating directories as needed.
|
|
149
198
|
|
|
150
|
-
|
|
199
|
+
6. **Completion**: A success message confirms the component has been added to your project.
|
|
151
200
|
|
|
152
201
|
## Common Use Cases
|
|
153
202
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@androbinco/library-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Interactive CLI tool to add Robin library components to your project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -11,13 +11,14 @@
|
|
|
11
11
|
"src/",
|
|
12
12
|
"README.md"
|
|
13
13
|
],
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"library",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
14
|
+
"scripts": {
|
|
15
|
+
"start": "node ./src/index.js",
|
|
16
|
+
"dev": "node ./src/index.js",
|
|
17
|
+
"test": "echo \"Error: no test specified for robin-library-cli\" && exit 1",
|
|
18
|
+
"build": "echo \"robin-library-cli: No build step required.\"",
|
|
19
|
+
"lint": "echo \"robin-library-cli: Lint not configured.\""
|
|
20
|
+
},
|
|
21
|
+
"keywords": ["cli", "robin", "library", "components", "react"],
|
|
21
22
|
"author": "androbinco",
|
|
22
23
|
"license": "MIT",
|
|
23
24
|
"dependencies": {
|
|
@@ -29,12 +30,6 @@
|
|
|
29
30
|
},
|
|
30
31
|
"publishConfig": {
|
|
31
32
|
"access": "public"
|
|
32
|
-
},
|
|
33
|
-
"scripts": {
|
|
34
|
-
"start": "node ./src/index.js",
|
|
35
|
-
"dev": "node ./src/index.js",
|
|
36
|
-
"test": "echo \"Error: no test specified for robin-library-cli\" && exit 1",
|
|
37
|
-
"build": "echo \"robin-library-cli: No build step required.\"",
|
|
38
|
-
"lint": "echo \"robin-library-cli: Lint not configured.\""
|
|
39
33
|
}
|
|
40
|
-
}
|
|
34
|
+
}
|
|
35
|
+
|
package/src/index.js
CHANGED
|
@@ -12,15 +12,73 @@ const TEMPLATES_DIR = path.join(__dirname, 'templates');
|
|
|
12
12
|
|
|
13
13
|
// Component registry
|
|
14
14
|
const COMPONENTS = [
|
|
15
|
-
{ name: 'example', description: 'Hello World example', sourceDir: 'example', dependencies: [] },
|
|
16
|
-
{ name: 'button', description: 'Minimal button component', sourceDir: 'button', dependencies: [] },
|
|
17
|
-
{ name: 'card', description: 'Minimal card wrapper', sourceDir: 'card', dependencies: [] },
|
|
18
|
-
{ name: 'hero', description: 'Minimal hero section', sourceDir: 'hero', dependencies: [] },
|
|
19
15
|
{
|
|
20
16
|
name: 'carousel',
|
|
21
17
|
description: 'Carousel component with navigation and pagination',
|
|
22
18
|
sourceDir: 'carousel',
|
|
23
19
|
dependencies: ['embla-carousel-react', 'embla-carousel', 'class-variance-authority']
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'in-view',
|
|
23
|
+
description: 'In view animations',
|
|
24
|
+
sourceDir: 'in-view',
|
|
25
|
+
hasSubmenu: true,
|
|
26
|
+
submenuOptions: [
|
|
27
|
+
{
|
|
28
|
+
value: 'grid',
|
|
29
|
+
label: 'Grid',
|
|
30
|
+
description: 'Grid-based animations',
|
|
31
|
+
hasNestedSubmenu: true,
|
|
32
|
+
nestedSubmenuOptions: [
|
|
33
|
+
{ value: 'in-view-grid', file: 'in-view-grid.tsx', required: true },
|
|
34
|
+
{ value: 'in-view-animation', file: 'in-view-animation.tsx', required: false, checkExists: true },
|
|
35
|
+
{ value: 'data.in-view', file: 'data.in-view.ts', required: true }
|
|
36
|
+
],
|
|
37
|
+
examples: ['in-view-grid-showcase.tsx']
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
value: 'individual',
|
|
41
|
+
label: 'Individual Elements',
|
|
42
|
+
description: 'Individual element animations',
|
|
43
|
+
hasNestedSubmenu: true,
|
|
44
|
+
nestedSubmenuOptions: [
|
|
45
|
+
{ value: 'in-view-hidden-text', file: 'in-view-hidden-text.tsx', required: true },
|
|
46
|
+
{ value: 'in-view-stroke-line', file: 'in-view-stroke-line.tsx', required: true },
|
|
47
|
+
{ value: 'in-view-animation', file: 'in-view-animation.tsx', required: false, checkExists: true },
|
|
48
|
+
{ value: 'data.in-view', file: 'data.in-view.ts', required: true }
|
|
49
|
+
],
|
|
50
|
+
examples: ['in-view-examples.home.tsx']
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
dependencies: ['motion']
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'ticker',
|
|
57
|
+
description: 'Ticker component with hover and non-hover variants',
|
|
58
|
+
sourceDir: 'ticker',
|
|
59
|
+
hasSubmenu: true,
|
|
60
|
+
submenuOptions: [
|
|
61
|
+
{
|
|
62
|
+
value: 'hover',
|
|
63
|
+
label: 'Stop on hover ticker (motion)',
|
|
64
|
+
description: 'MotionTicker with hover stop functionality',
|
|
65
|
+
examples: ['ticker-hover-showcase.home.tsx']
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
value: 'non-hover',
|
|
69
|
+
label: 'Non-stop on hover ticker (css)',
|
|
70
|
+
description: 'TickerStatic with CSS animations',
|
|
71
|
+
examples: ['ticker-static-showcase.home.tsx']
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
dependencies: ['motion']
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'scroll-tracker',
|
|
78
|
+
description: 'Scroll tracker provider for scroll-driven animations',
|
|
79
|
+
sourceDir: 'scroll-tracker',
|
|
80
|
+
dependencies: ['motion'],
|
|
81
|
+
examples: ['scroll-tracker-showcase.tsx']
|
|
24
82
|
}
|
|
25
83
|
];
|
|
26
84
|
|
|
@@ -40,6 +98,14 @@ async function main() {
|
|
|
40
98
|
// Beautiful intro
|
|
41
99
|
p.intro('✨ Welcome to Robin Library CLI');
|
|
42
100
|
|
|
101
|
+
// Add clickable link using ANSI escape codes for hyperlinks
|
|
102
|
+
const websiteUrl = 'https://robin-animation-library-620346143712.us-east1.run.app/';
|
|
103
|
+
const linkText = 'https://robin-animation-library-620346143712.us-east1.run.app/';
|
|
104
|
+
// ANSI escape code format: \u001b]8;;URL\u0007TEXT\u001b]8;;\u0007
|
|
105
|
+
const clickableLink = `\u001b]8;;${websiteUrl}\u0007${linkText}\u001b]8;;\u0007`;
|
|
106
|
+
p.log.info(`You can check examples in ${clickableLink}`);
|
|
107
|
+
|
|
108
|
+
|
|
43
109
|
// Check React dependency
|
|
44
110
|
await checkReactDependency();
|
|
45
111
|
|
|
@@ -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,
|
|
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;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Children, useEffect, useState } from "react";
|
|
3
|
+
|
|
4
|
+
import { motion, Transition } from "motion/react";
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/common/utils/classname-builder";
|
|
7
|
+
|
|
8
|
+
import IN_VIEW_ANIMATIONS from "./data.in-view";
|
|
9
|
+
|
|
10
|
+
type GridInViewProps = {
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
className?: string;
|
|
13
|
+
effect?: keyof typeof IN_VIEW_ANIMATIONS;
|
|
14
|
+
columns?: number | { desktop: number; mobile: number };
|
|
15
|
+
delayDelta?: number;
|
|
16
|
+
delayByColumn?: boolean;
|
|
17
|
+
transition?: Omit<Transition, "delay">;
|
|
18
|
+
columnsMobile?: number;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const GridInView = ({
|
|
22
|
+
children,
|
|
23
|
+
className,
|
|
24
|
+
effect = "fadeInUp",
|
|
25
|
+
delayDelta = 0.3,
|
|
26
|
+
delayByColumn = true,
|
|
27
|
+
transition = {
|
|
28
|
+
duration: 0.5,
|
|
29
|
+
ease: "easeOut",
|
|
30
|
+
},
|
|
31
|
+
columns = 5,
|
|
32
|
+
}: GridInViewProps) => {
|
|
33
|
+
const childrenArray = Children.toArray(children);
|
|
34
|
+
const isColumnsObject = typeof columns === "object";
|
|
35
|
+
const [columnsValue, setColumnsValue] = useState(
|
|
36
|
+
isColumnsObject ? columns.desktop : columns
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (!delayByColumn) return;
|
|
41
|
+
if (typeof window === "undefined" || !isColumnsObject) return;
|
|
42
|
+
if (window.innerWidth < 1024) setColumnsValue(columns.mobile);
|
|
43
|
+
else setColumnsValue(columns.desktop);
|
|
44
|
+
}, [columns, isColumnsObject, delayByColumn]);
|
|
45
|
+
const delayCount = (index: number) =>
|
|
46
|
+
delayByColumn ? index % columnsValue : index;
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div
|
|
50
|
+
className={cn(
|
|
51
|
+
"grid gap-4",
|
|
52
|
+
"[grid-template-columns:repeat(var(--columns),1fr)]",
|
|
53
|
+
"max-lg:[grid-template-columns:repeat(var(--columns-mobile),1fr)]",
|
|
54
|
+
className
|
|
55
|
+
)}
|
|
56
|
+
style={
|
|
57
|
+
{
|
|
58
|
+
"--columns": isColumnsObject ? columns.desktop : columns,
|
|
59
|
+
"--columns-mobile": isColumnsObject ? columns.mobile : columns,
|
|
60
|
+
} as React.CSSProperties
|
|
61
|
+
}
|
|
62
|
+
>
|
|
63
|
+
{childrenArray.map((item, index) => (
|
|
64
|
+
<motion.div
|
|
65
|
+
key={index}
|
|
66
|
+
initial={IN_VIEW_ANIMATIONS[effect].hidden}
|
|
67
|
+
transition={{
|
|
68
|
+
...transition,
|
|
69
|
+
delay: delayCount(index) * delayDelta,
|
|
70
|
+
}}
|
|
71
|
+
viewport={{ once: true, margin: "-10% 0%" }}
|
|
72
|
+
whileInView={IN_VIEW_ANIMATIONS[effect].visible}
|
|
73
|
+
>
|
|
74
|
+
{item}
|
|
75
|
+
</motion.div>
|
|
76
|
+
))}
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
};
|