@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
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.3.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
|
+
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import {
|
|
4
|
+
COMPONENTS,
|
|
5
|
+
getComponentByName,
|
|
6
|
+
getVariantByName,
|
|
7
|
+
getFilesForVariant
|
|
8
|
+
} from '../utils/components.js';
|
|
9
|
+
import {
|
|
10
|
+
detectPackageManager,
|
|
11
|
+
checkMissingDependencies,
|
|
12
|
+
installDependencies
|
|
13
|
+
} from '../utils/dependencies.js';
|
|
14
|
+
import { copyComponent } from '../utils/files.js';
|
|
15
|
+
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = path.dirname(__filename);
|
|
18
|
+
const TEMPLATES_DIR = path.join(__dirname, '..', 'templates');
|
|
19
|
+
|
|
20
|
+
export async function handleAdd(args) {
|
|
21
|
+
const [componentName, variantName] = args;
|
|
22
|
+
|
|
23
|
+
if (!componentName) {
|
|
24
|
+
console.error('Error: Component name is required');
|
|
25
|
+
console.log('Usage: add <component> [variant]');
|
|
26
|
+
console.log('Run "list" to see available components');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const component = getComponentByName(componentName);
|
|
31
|
+
|
|
32
|
+
if (!component) {
|
|
33
|
+
console.error(`Error: Component "${componentName}" not found`);
|
|
34
|
+
console.log('Available components:');
|
|
35
|
+
COMPONENTS.forEach(c => console.log(` - ${c.name}`));
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Check if component requires variant
|
|
40
|
+
if (component.hasSubmenu && !variantName) {
|
|
41
|
+
console.error(`Error: Component "${componentName}" requires a variant`);
|
|
42
|
+
console.log('Available variants:');
|
|
43
|
+
component.submenuOptions.forEach(v => console.log(` - ${v.value}: ${v.description}`));
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Get variant if specified
|
|
48
|
+
let variant = null;
|
|
49
|
+
let filesToCopy = null;
|
|
50
|
+
let examplesToCopy = [];
|
|
51
|
+
|
|
52
|
+
if (component.hasSubmenu && variantName) {
|
|
53
|
+
variant = getVariantByName(component, variantName);
|
|
54
|
+
|
|
55
|
+
if (!variant) {
|
|
56
|
+
console.error(`Error: Variant "${variantName}" not found for component "${componentName}"`);
|
|
57
|
+
console.log('Available variants:');
|
|
58
|
+
component.submenuOptions.forEach(v => console.log(` - ${v.value}: ${v.description}`));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
filesToCopy = getFilesForVariant(component, variant);
|
|
63
|
+
examplesToCopy = variant.examples || [];
|
|
64
|
+
} else {
|
|
65
|
+
// Simple component without variant
|
|
66
|
+
examplesToCopy = component.examples || [];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Determine dependencies
|
|
70
|
+
let dependencies = component.dependencies || [];
|
|
71
|
+
|
|
72
|
+
// For ticker non-hover, no motion needed
|
|
73
|
+
if (component.name === 'ticker' && variantName === 'non-hover') {
|
|
74
|
+
dependencies = [];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Check and install dependencies
|
|
78
|
+
if (dependencies.length > 0) {
|
|
79
|
+
const missingDependencies = await checkMissingDependencies(dependencies);
|
|
80
|
+
|
|
81
|
+
if (missingDependencies.length > 0) {
|
|
82
|
+
console.log(`Installing dependencies: ${missingDependencies.join(', ')}`);
|
|
83
|
+
const packageManager = await detectPackageManager();
|
|
84
|
+
const success = await installDependencies(missingDependencies, packageManager);
|
|
85
|
+
|
|
86
|
+
if (!success) {
|
|
87
|
+
console.warn('Warning: Failed to install some dependencies. You may need to install them manually.');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Copy component
|
|
93
|
+
console.log(`Copying ${componentName}${variantName ? ` (${variantName})` : ''}...`);
|
|
94
|
+
|
|
95
|
+
const success = await copyComponent(component, TEMPLATES_DIR, examplesToCopy, filesToCopy);
|
|
96
|
+
|
|
97
|
+
if (success) {
|
|
98
|
+
console.log(`✅ Component ${componentName} copied successfully!`);
|
|
99
|
+
console.log(`Files are available at: src/components/${componentName}/`);
|
|
100
|
+
if (examplesToCopy.length > 0) {
|
|
101
|
+
console.log(`Examples are available at: src/components/${componentName}/examples/`);
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
console.error(`❌ Failed to copy component ${componentName}`);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { COMPONENTS, getComponentByName } from '../utils/components.js';
|
|
2
|
+
|
|
3
|
+
export function handleList(args) {
|
|
4
|
+
const [componentName] = args;
|
|
5
|
+
|
|
6
|
+
if (!componentName) {
|
|
7
|
+
// List all components
|
|
8
|
+
console.log('Available components:\n');
|
|
9
|
+
|
|
10
|
+
for (const component of COMPONENTS) {
|
|
11
|
+
console.log(` ${component.name}`);
|
|
12
|
+
console.log(` ${component.description}`);
|
|
13
|
+
|
|
14
|
+
if (component.hasSubmenu) {
|
|
15
|
+
console.log(` Variants: ${component.submenuOptions.map(v => v.value).join(', ')}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
console.log('');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
console.log('Usage:');
|
|
22
|
+
console.log(' add <component> Copy a component');
|
|
23
|
+
console.log(' add <component> <variant> Copy a specific variant');
|
|
24
|
+
console.log(' list <component> List variants for a component');
|
|
25
|
+
} else {
|
|
26
|
+
// List variants for specific component
|
|
27
|
+
const component = getComponentByName(componentName);
|
|
28
|
+
|
|
29
|
+
if (!component) {
|
|
30
|
+
console.error(`Error: Component "${componentName}" not found`);
|
|
31
|
+
console.log('Run "list" to see all available components');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log(`Component: ${component.name}`);
|
|
36
|
+
console.log(`Description: ${component.description}`);
|
|
37
|
+
console.log(`Dependencies: ${component.dependencies?.join(', ') || 'none'}`);
|
|
38
|
+
|
|
39
|
+
if (component.hasSubmenu) {
|
|
40
|
+
console.log('\nVariants:');
|
|
41
|
+
for (const variant of component.submenuOptions) {
|
|
42
|
+
console.log(` ${variant.value}`);
|
|
43
|
+
console.log(` ${variant.description}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log(`\nUsage: add ${component.name} <variant>`);
|
|
47
|
+
} else {
|
|
48
|
+
console.log(`\nUsage: add ${component.name}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
package/src/index.js
CHANGED
|
@@ -4,25 +4,44 @@ import * as p from '@clack/prompts';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { checkReactDependency } from './utils/dependencies.js';
|
|
7
|
-
import { handleComponentsFlow } from './utils/components.js';
|
|
7
|
+
import { handleComponentsFlow, COMPONENTS } from './utils/components.js';
|
|
8
|
+
import { handleAdd } from './commands/add.js';
|
|
9
|
+
import { handleList } from './commands/list.js';
|
|
8
10
|
|
|
9
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
12
|
const __dirname = path.dirname(__filename);
|
|
11
13
|
const TEMPLATES_DIR = path.join(__dirname, 'templates');
|
|
12
14
|
|
|
13
|
-
//
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
15
|
+
// Parse command line arguments
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
const command = args[0];
|
|
18
|
+
|
|
19
|
+
// Handle direct commands (non-interactive mode)
|
|
20
|
+
if (command === 'add') {
|
|
21
|
+
await handleAdd(args.slice(1));
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (command === 'list') {
|
|
26
|
+
handleList(args.slice(1));
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (command === 'help' || command === '--help' || command === '-h') {
|
|
31
|
+
console.log('Robin Library CLI\n');
|
|
32
|
+
console.log('Usage:');
|
|
33
|
+
console.log(' robin-library-cli Interactive mode');
|
|
34
|
+
console.log(' robin-library-cli add <comp> Add a component');
|
|
35
|
+
console.log(' robin-library-cli list List all components');
|
|
36
|
+
console.log(' robin-library-cli list <comp> List variants for a component');
|
|
37
|
+
console.log(' robin-library-cli help Show this help\n');
|
|
38
|
+
console.log('Examples:');
|
|
39
|
+
console.log(' robin-library-cli add carousel');
|
|
40
|
+
console.log(' robin-library-cli add ticker hover');
|
|
41
|
+
console.log(' robin-library-cli add scroll-components parallax-image');
|
|
42
|
+
console.log(' robin-library-cli list ticker');
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
26
45
|
|
|
27
46
|
async function mainMenu() {
|
|
28
47
|
const action = await p.select({
|
|
@@ -40,6 +59,14 @@ async function main() {
|
|
|
40
59
|
// Beautiful intro
|
|
41
60
|
p.intro('✨ Welcome to Robin Library CLI');
|
|
42
61
|
|
|
62
|
+
// Add clickable link using ANSI escape codes for hyperlinks
|
|
63
|
+
const websiteUrl = 'https://robin-animation-library-620346143712.us-east1.run.app/';
|
|
64
|
+
const linkText = 'https://robin-animation-library-620346143712.us-east1.run.app/';
|
|
65
|
+
// ANSI escape code format: \u001b]8;;URL\u0007TEXT\u001b]8;;\u0007
|
|
66
|
+
const clickableLink = `\u001b]8;;${websiteUrl}\u0007${linkText}\u001b]8;;\u0007`;
|
|
67
|
+
p.log.info(`You can check examples in ${clickableLink}`);
|
|
68
|
+
|
|
69
|
+
|
|
43
70
|
// Check React dependency
|
|
44
71
|
await checkReactDependency();
|
|
45
72
|
|
|
@@ -52,7 +79,11 @@ async function main() {
|
|
|
52
79
|
}
|
|
53
80
|
|
|
54
81
|
if (action === 'components') {
|
|
55
|
-
await handleComponentsFlow(COMPONENTS, TEMPLATES_DIR);
|
|
82
|
+
const result = await handleComponentsFlow(COMPONENTS, TEMPLATES_DIR);
|
|
83
|
+
// If user selected go back, skip the "do more" prompt and show main menu again
|
|
84
|
+
if (result && result.goBack) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
56
87
|
}
|
|
57
88
|
|
|
58
89
|
const doMore = await p.select({
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { cva, VariantProps } from 'class-variance-authority';
|
|
2
|
+
|
|
3
|
+
import { cn } from '@/common/utils/classname-builder';
|
|
4
|
+
|
|
5
|
+
import { useCarouselContext } from '../provider/carousel.provider';
|
|
6
|
+
|
|
7
|
+
type DotButtonVariants = VariantProps<typeof dotButtonVariants>['variant'];
|
|
8
|
+
|
|
9
|
+
export type DotPaginationProps = {
|
|
10
|
+
scrollSnaps: number[];
|
|
11
|
+
variant?: 'primary';
|
|
12
|
+
className?: string;
|
|
13
|
+
};
|
|
14
|
+
const dotButtonVariants = cva(
|
|
15
|
+
'size-3 cursor-pointer rounded-full transition-all duration-300 ease-out',
|
|
16
|
+
{
|
|
17
|
+
variants: {
|
|
18
|
+
variant: {
|
|
19
|
+
primary: 'focus:outline-2',
|
|
20
|
+
},
|
|
21
|
+
selected: {
|
|
22
|
+
true: 'w-9',
|
|
23
|
+
false: '',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
compoundVariants: [
|
|
27
|
+
{
|
|
28
|
+
variant: 'primary',
|
|
29
|
+
selected: true,
|
|
30
|
+
className: 'bg-indigo-900',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
variant: 'primary',
|
|
34
|
+
selected: false,
|
|
35
|
+
className: 'bg-indigo-500',
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
export const DotButton: React.FC<{ index: number; variant: DotButtonVariants }> = ({
|
|
42
|
+
index,
|
|
43
|
+
variant = 'primary',
|
|
44
|
+
...restProps
|
|
45
|
+
}) => {
|
|
46
|
+
const { selectedIndex, onDotButtonClick } = useCarouselContext();
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<button
|
|
50
|
+
className={dotButtonVariants({ variant, selected: selectedIndex === index })}
|
|
51
|
+
disabled={selectedIndex === index}
|
|
52
|
+
onClick={() => onDotButtonClick(index)}
|
|
53
|
+
{...restProps}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const BulletPagination = ({
|
|
59
|
+
scrollSnaps,
|
|
60
|
+
className,
|
|
61
|
+
variant = 'primary',
|
|
62
|
+
...restProps
|
|
63
|
+
}: DotPaginationProps) => (
|
|
64
|
+
<div className={cn('mx-auto flex w-max gap-2', className)} {...restProps}>
|
|
65
|
+
{scrollSnaps.map((_, index) => (
|
|
66
|
+
<DotButton key={index} index={index} variant={variant} />
|
|
67
|
+
))}
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { VariantProps } from 'class-variance-authority';
|
|
2
|
+
|
|
3
|
+
import { cn } from '@/common/utils/classname-builder';
|
|
4
|
+
import { Text } from '@/common/ui/text/text';
|
|
5
|
+
import { textVariants } from '@/common/ui/text/text.styles';
|
|
6
|
+
|
|
7
|
+
export type NumberPaginationProps = {
|
|
8
|
+
className?: string;
|
|
9
|
+
variant?: VariantProps<typeof textVariants>['variant'];
|
|
10
|
+
textClassName?: string;
|
|
11
|
+
selectedIndex: number;
|
|
12
|
+
scrollSnaps: number[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const NumberPagination = ({
|
|
16
|
+
className,
|
|
17
|
+
variant,
|
|
18
|
+
textClassName,
|
|
19
|
+
selectedIndex,
|
|
20
|
+
scrollSnaps,
|
|
21
|
+
...props
|
|
22
|
+
}: NumberPaginationProps) => {
|
|
23
|
+
return (
|
|
24
|
+
<div className={cn('mx-auto flex w-max gap-2', className)} {...props}>
|
|
25
|
+
<Text className={textClassName} variant={variant}>
|
|
26
|
+
{selectedIndex + 1} / {scrollSnaps.length}
|
|
27
|
+
</Text>
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
};
|
package/src/templates/carousel/components/pagination/progress/progress.pagination.carousel.tsx
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { cva, VariantProps } from 'class-variance-authority';
|
|
2
|
+
|
|
3
|
+
import { cn } from '@/common/utils/classname-builder';
|
|
4
|
+
|
|
5
|
+
import { useSlideProgress } from './use-slide-progress';
|
|
6
|
+
|
|
7
|
+
const effects = {
|
|
8
|
+
scale: (selectedIndex: number, totalItems: number) => ({
|
|
9
|
+
transformOrigin: 'left',
|
|
10
|
+
transform: `scaleX(${(selectedIndex + 1) / totalItems})`,
|
|
11
|
+
}),
|
|
12
|
+
slider: (selectedIndex: number, totalItems: number) => {
|
|
13
|
+
if (totalItems === 0) return {};
|
|
14
|
+
const width = Math.ceil(100 / totalItems);
|
|
15
|
+
const left = Math.ceil((selectedIndex / totalItems) * 100);
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
width: `${width}%`,
|
|
19
|
+
cursor: 'grab',
|
|
20
|
+
transformOrigin: 'left',
|
|
21
|
+
left: `${left}%`,
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type ProgressPaginationProps = {
|
|
27
|
+
className?: string;
|
|
28
|
+
selectedIndex: number;
|
|
29
|
+
scrollSnaps: number[];
|
|
30
|
+
effect?: keyof typeof effects;
|
|
31
|
+
onDotButtonClick: (index: number) => void;
|
|
32
|
+
variant?: VariantProps<typeof backgroundVariants>['variant'];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const backgroundVariants = cva('relative mx-auto flex w-200 max-w-full overflow-hidden', {
|
|
36
|
+
variants: {
|
|
37
|
+
variant: {
|
|
38
|
+
primary: 'h-2 rounded-full bg-grey-50-30',
|
|
39
|
+
},
|
|
40
|
+
effect: {
|
|
41
|
+
slider: 'bg-grey-50-6',
|
|
42
|
+
scale: 'bg-grey-50-30',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
const progressVariants = cva('linear absolute bottom-0 left-0 transition-all duration-300', {
|
|
47
|
+
variants: {
|
|
48
|
+
variant: {
|
|
49
|
+
primary: 'h-full rounded-full bg-indigo-500',
|
|
50
|
+
},
|
|
51
|
+
effect: {
|
|
52
|
+
slider: '',
|
|
53
|
+
scale: 'pointer-events-none !w-full',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export const ProgressPagination = ({
|
|
59
|
+
className,
|
|
60
|
+
selectedIndex,
|
|
61
|
+
scrollSnaps,
|
|
62
|
+
onDotButtonClick,
|
|
63
|
+
variant = 'primary',
|
|
64
|
+
effect = 'scale',
|
|
65
|
+
...props
|
|
66
|
+
}: ProgressPaginationProps) => {
|
|
67
|
+
const isSlider = effect === 'slider';
|
|
68
|
+
const { isDragging, barRef, setIsDragging } = useSlideProgress({ isActive: isSlider });
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<div className={cn(backgroundVariants({ variant, effect }), className)} {...props}>
|
|
72
|
+
<button
|
|
73
|
+
ref={barRef}
|
|
74
|
+
className={cn(progressVariants({ variant, effect }))}
|
|
75
|
+
style={{ ...effects[effect](selectedIndex, scrollSnaps.length) }}
|
|
76
|
+
onMouseDown={() => {
|
|
77
|
+
if (!isSlider) return;
|
|
78
|
+
setIsDragging(true);
|
|
79
|
+
}}
|
|
80
|
+
onMouseUp={() => setIsDragging(false)}
|
|
81
|
+
/>
|
|
82
|
+
|
|
83
|
+
{scrollSnaps.map((_, index) => (
|
|
84
|
+
<div
|
|
85
|
+
key={index}
|
|
86
|
+
className={cn('h-full w-full', !isDragging && 'cursor-pointer')}
|
|
87
|
+
onMouseDown={() => {
|
|
88
|
+
onDotButtonClick(index);
|
|
89
|
+
if (!isSlider) return;
|
|
90
|
+
setIsDragging(true);
|
|
91
|
+
}}
|
|
92
|
+
onMouseEnter={() => {
|
|
93
|
+
if (isDragging) onDotButtonClick(index);
|
|
94
|
+
}}
|
|
95
|
+
/>
|
|
96
|
+
))}
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
};
|