@androbinco/library-cli 0.2.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 (40) hide show
  1. package/package.json +1 -1
  2. package/src/commands/add.js +107 -0
  3. package/src/commands/list.js +51 -0
  4. package/src/index.js +38 -73
  5. package/src/templates/carousel/components/navigation-buttons.tsx +1 -0
  6. package/src/templates/carousel/components/pagination/bullet.pagination.carousel.tsx +69 -0
  7. package/src/templates/carousel/components/pagination/number.pagination.carousel.tsx +30 -0
  8. package/src/templates/carousel/components/pagination/progress/progress.pagination.carousel.tsx +99 -0
  9. package/src/templates/carousel/components/pagination/progress/use-slide-progress.tsx +31 -0
  10. package/src/templates/carousel/components/pagination.tsx +47 -82
  11. package/src/templates/faqs-accordion/examples/faqs-showcase.tsx +42 -0
  12. package/src/templates/faqs-accordion/faqs-accordion.tsx +70 -0
  13. package/src/templates/faqs-accordion/mock-data.ts +38 -0
  14. package/src/templates/faqs-accordion/types.ts +18 -0
  15. package/src/templates/in-view/data.in-view.ts +1 -1
  16. package/src/templates/in-view/in-view-animation.tsx +7 -7
  17. package/src/templates/in-view/in-view-grid.tsx +22 -20
  18. package/src/templates/in-view/in-view-hidden-text.tsx +7 -7
  19. package/src/templates/in-view/in-view-stroke-line.tsx +6 -5
  20. package/src/templates/lenis/examples/providers.tsx +23 -0
  21. package/src/templates/lenis/lenis-provider.tsx +46 -0
  22. package/src/templates/scroll-components/hooks/use-client-dimensions.ts +21 -0
  23. package/src/templates/scroll-components/parallax/examples/parallax-showcase.tsx +87 -0
  24. package/src/templates/scroll-components/parallax/parallax.css +36 -0
  25. package/src/templates/scroll-components/parallax/parallax.tsx +67 -0
  26. package/src/templates/scroll-components/scale-gallery/components/expanding-element.tsx +40 -0
  27. package/src/templates/scroll-components/scale-gallery/examples/scale-gallery-showcase.tsx +68 -0
  28. package/src/templates/scroll-components/scale-gallery/scale-gallery.tsx +57 -0
  29. package/src/templates/scroll-components/scroll-tracker-showcase.tsx +44 -0
  30. package/src/templates/strapi-dynamic-zone/README.md +157 -0
  31. package/src/templates/strapi-dynamic-zone/dynamic-zone.tsx +113 -0
  32. package/src/templates/strapi-dynamic-zone/examples/page.tsx +53 -0
  33. package/src/templates/strapi-dynamic-zone/examples/renderers.tsx +74 -0
  34. package/src/templates/strapi-dynamic-zone/examples/types.ts +41 -0
  35. package/src/templates/strapi-dynamic-zone/index.ts +11 -0
  36. package/src/templates/strapi-dynamic-zone/types.ts +73 -0
  37. package/src/utils/components.js +187 -5
  38. package/src/utils/files.js +13 -12
  39. package/src/templates/scroll-tracker/examples/scroll-tracker-showcase.tsx +0 -90
  40. /package/src/templates/{scroll-tracker → scroll-components}/scroll-tracker-provider.tsx +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@androbinco/library-cli",
3
- "version": "0.2.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",
@@ -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,83 +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
- // Component registry
14
- const COMPONENTS = [
15
- {
16
- name: 'carousel',
17
- description: 'Carousel component with navigation and pagination',
18
- sourceDir: 'carousel',
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']
82
- }
83
- ];
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
+ }
84
45
 
85
46
  async function mainMenu() {
86
47
  const action = await p.select({
@@ -118,7 +79,11 @@ async function main() {
118
79
  }
119
80
 
120
81
  if (action === 'components') {
121
- 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
+ }
122
87
  }
123
88
 
124
89
  const doMore = await p.select({
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  import React, { ComponentPropsWithRef } from "react";
2
3
 
3
4
  import { cva, VariantProps } from "class-variance-authority";
@@ -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
+ };
@@ -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
+ };
@@ -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
  };