anima-ds-nucleus 1.0.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 (116) hide show
  1. package/LICENSE.md +20 -0
  2. package/README.md +109 -0
  3. package/dist/anima-ds.cjs.js +1332 -0
  4. package/dist/anima-ds.esm.js +88297 -0
  5. package/dist/vite.svg +1 -0
  6. package/package.json +76 -0
  7. package/src/assets/charly.png +0 -0
  8. package/src/components/Atoms/Alert/Alert.jsx +52 -0
  9. package/src/components/Atoms/Alert/Alert.stories.jsx +62 -0
  10. package/src/components/Atoms/Avatar/Avatar.jsx +60 -0
  11. package/src/components/Atoms/Avatar/Avatar.stories.jsx +61 -0
  12. package/src/components/Atoms/Badge/Badge.jsx +34 -0
  13. package/src/components/Atoms/Badge/Badge.stories.jsx +55 -0
  14. package/src/components/Atoms/Button/Button.jsx +281 -0
  15. package/src/components/Atoms/Button/Button.stories.jsx +365 -0
  16. package/src/components/Atoms/Divider/Divider.jsx +49 -0
  17. package/src/components/Atoms/Divider/Divider.stories.jsx +62 -0
  18. package/src/components/Atoms/Icon/Icon.jsx +361 -0
  19. package/src/components/Atoms/Icon/Icon.stories.jsx +115 -0
  20. package/src/components/Atoms/Label/Label.jsx +22 -0
  21. package/src/components/Atoms/Progress/Progress.jsx +49 -0
  22. package/src/components/Atoms/Progress/Progress.stories.jsx +88 -0
  23. package/src/components/Atoms/Radios/Radios.jsx +39 -0
  24. package/src/components/Atoms/Radios/Radios.stories.jsx +119 -0
  25. package/src/components/Atoms/Shadow/Shadow.stories.jsx +25 -0
  26. package/src/components/Atoms/Skeleton/Skeleton.jsx +50 -0
  27. package/src/components/Atoms/Skeleton/Skeleton.stories.jsx +55 -0
  28. package/src/components/Atoms/Spacing/Spacing.jsx +56 -0
  29. package/src/components/Atoms/Spacing/Spacing.stories.jsx +78 -0
  30. package/src/components/Atoms/Spinner/Spinner.jsx +47 -0
  31. package/src/components/Atoms/Spinner/Spinner.stories.jsx +56 -0
  32. package/src/components/Atoms/Toast/Toast.jsx +74 -0
  33. package/src/components/Atoms/Toast/Toast.stories.jsx +101 -0
  34. package/src/components/Atoms/Tooltip/Tooltip.jsx +49 -0
  35. package/src/components/Atoms/Tooltip/Tooltip.stories.jsx +58 -0
  36. package/src/components/Atoms/Typography/Typography.jsx +52 -0
  37. package/src/components/Atoms/Typography/Typography.stories.jsx +267 -0
  38. package/src/components/DataDisplay/AreaChart/AreaChart.jsx +95 -0
  39. package/src/components/DataDisplay/AreaChart/AreaChart.stories.jsx +51 -0
  40. package/src/components/DataDisplay/BarChart/BarChart.jsx +90 -0
  41. package/src/components/DataDisplay/BarChart/BarChart.stories.jsx +60 -0
  42. package/src/components/DataDisplay/Card/Card.jsx +31 -0
  43. package/src/components/DataDisplay/Card/Card.stories.jsx +50 -0
  44. package/src/components/DataDisplay/ColumnChart/ColumnChart.jsx +92 -0
  45. package/src/components/DataDisplay/ColumnChart/ColumnChart.stories.jsx +65 -0
  46. package/src/components/DataDisplay/DBGrid/DBGrid.jsx +138 -0
  47. package/src/components/DataDisplay/DBGrid/DBGrid.stories.jsx +126 -0
  48. package/src/components/DataDisplay/DonutChart/DonutChart.jsx +83 -0
  49. package/src/components/DataDisplay/DonutChart/DonutChart.stories.jsx +48 -0
  50. package/src/components/DataDisplay/EmptyState/EmptyState.jsx +30 -0
  51. package/src/components/DataDisplay/EmptyState/EmptyState.stories.jsx +42 -0
  52. package/src/components/DataDisplay/LineChart/LineChart.jsx +86 -0
  53. package/src/components/DataDisplay/LineChart/LineChart.stories.jsx +67 -0
  54. package/src/components/DataDisplay/List/List.jsx +30 -0
  55. package/src/components/DataDisplay/List/List.stories.jsx +59 -0
  56. package/src/components/DataDisplay/PieChart/PieChart.jsx +64 -0
  57. package/src/components/DataDisplay/PieChart/PieChart.stories.jsx +47 -0
  58. package/src/components/DataDisplay/StatCard/StatCard.jsx +55 -0
  59. package/src/components/DataDisplay/StatCard/StatCard.stories.jsx +59 -0
  60. package/src/components/DataDisplay/TagList/TagList.jsx +37 -0
  61. package/src/components/DataDisplay/TagList/TagList.stories.jsx +48 -0
  62. package/src/components/DataDisplay/Timeline/Timeline.jsx +41 -0
  63. package/src/components/DataDisplay/Timeline/Timeline.stories.jsx +64 -0
  64. package/src/components/Inputs/Checkbox/Checkbox.jsx +27 -0
  65. package/src/components/Inputs/Checkbox/Checkbox.stories.jsx +51 -0
  66. package/src/components/Inputs/DatePicker/DatePicker.jsx +55 -0
  67. package/src/components/Inputs/DatePicker/DatePicker.stories.jsx +52 -0
  68. package/src/components/Inputs/FileUpload/FileUpload.jsx +108 -0
  69. package/src/components/Inputs/FileUpload/FileUpload.stories.jsx +52 -0
  70. package/src/components/Inputs/Input/Input.jsx +50 -0
  71. package/src/components/Inputs/Input/Input.stories.jsx +63 -0
  72. package/src/components/Inputs/RadioButton/RadioButton.jsx +31 -0
  73. package/src/components/Inputs/RadioButton/RadioButton.stories.jsx +59 -0
  74. package/src/components/Inputs/Select/Select.jsx +59 -0
  75. package/src/components/Inputs/Select/Select.stories.jsx +66 -0
  76. package/src/components/Inputs/Switch/Switch.jsx +44 -0
  77. package/src/components/Inputs/Switch/Switch.stories.jsx +51 -0
  78. package/src/components/Inputs/Textarea/Textarea.jsx +51 -0
  79. package/src/components/Inputs/Textarea/Textarea.stories.jsx +65 -0
  80. package/src/components/Layout/Accordion/Accordion.jsx +58 -0
  81. package/src/components/Layout/Accordion/Accordion.stories.jsx +55 -0
  82. package/src/components/Layout/Breadcrumbs/Breadcrumbs.jsx +49 -0
  83. package/src/components/Layout/Breadcrumbs/Breadcrumbs.stories.jsx +44 -0
  84. package/src/components/Layout/Breakpoint/Breakpoint.jsx +35 -0
  85. package/src/components/Layout/Breakpoint/Breakpoint.stories.jsx +348 -0
  86. package/src/components/Layout/Drawer/Drawer.jsx +75 -0
  87. package/src/components/Layout/Drawer/Drawer.stories.jsx +77 -0
  88. package/src/components/Layout/Dropdown/Dropdown.jsx +83 -0
  89. package/src/components/Layout/Dropdown/Dropdown.stories.jsx +53 -0
  90. package/src/components/Layout/Grid/Grid.jsx +39 -0
  91. package/src/components/Layout/Grid/Grid.stories.jsx +546 -0
  92. package/src/components/Layout/Header/Header.jsx +50 -0
  93. package/src/components/Layout/Header/Header.stories.jsx +36 -0
  94. package/src/components/Layout/Layout/Layout.jsx +14 -0
  95. package/src/components/Layout/Layout/Layout.stories.jsx +34 -0
  96. package/src/components/Layout/Modal/Modal.jsx +78 -0
  97. package/src/components/Layout/Modal/Modal.stories.jsx +98 -0
  98. package/src/components/Layout/Pagination/Pagination.jsx +109 -0
  99. package/src/components/Layout/Pagination/Pagination.stories.jsx +62 -0
  100. package/src/components/Layout/Sidebar/Sidebar.jsx +57 -0
  101. package/src/components/Layout/Sidebar/Sidebar.stories.jsx +51 -0
  102. package/src/components/Layout/Stepper/Stepper.jsx +132 -0
  103. package/src/components/Layout/Stepper/Stepper.stories.jsx +78 -0
  104. package/src/components/Layout/Tabs/Tabs.jsx +63 -0
  105. package/src/components/Layout/Tabs/Tabs.stories.jsx +62 -0
  106. package/src/components/Views/ChangePasswordForm/ChangePasswordForm.jsx +125 -0
  107. package/src/components/Views/ChangePasswordForm/ChangePasswordForm.stories.jsx +55 -0
  108. package/src/components/Views/Chat/Chat.jsx +134 -0
  109. package/src/components/Views/Chat/Chat.stories.jsx +143 -0
  110. package/src/components/Views/LoginForm/LoginForm.jsx +98 -0
  111. package/src/components/Views/LoginForm/LoginForm.stories.jsx +55 -0
  112. package/src/i18n/config.js +209 -0
  113. package/src/index.js +60 -0
  114. package/src/main.jsx +510 -0
  115. package/src/providers/I18nProvider.jsx +13 -0
  116. package/src/style.css +721 -0
@@ -0,0 +1,78 @@
1
+ import { useEffect } from 'react';
2
+ import { Icon } from '../../Atoms/Icon/Icon';
3
+ import { Button } from '../../Atoms/Button/Button';
4
+
5
+ export const Modal = ({
6
+ isOpen = false,
7
+ onClose,
8
+ title,
9
+ children,
10
+ footer,
11
+ size = 'medium',
12
+ showCloseButton = true,
13
+ className = '',
14
+ ...props
15
+ }) => {
16
+ useEffect(() => {
17
+ if (isOpen) {
18
+ document.body.style.overflow = 'hidden';
19
+ } else {
20
+ document.body.style.overflow = 'unset';
21
+ }
22
+ return () => {
23
+ document.body.style.overflow = 'unset';
24
+ };
25
+ }, [isOpen]);
26
+
27
+ if (!isOpen) return null;
28
+
29
+ const sizeClasses = {
30
+ small: 'max-w-md',
31
+ medium: 'max-w-lg',
32
+ large: 'max-w-2xl',
33
+ xl: 'max-w-4xl',
34
+ full: 'max-w-full mx-4',
35
+ };
36
+
37
+ return (
38
+ <div
39
+ className="fixed inset-0 z-50 overflow-y-auto"
40
+ onClick={onClose}
41
+ {...props}
42
+ >
43
+ <div className="flex items-center justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:p-0">
44
+ <div
45
+ className="fixed inset-0 transition-opacity bg-gray-500 bg-opacity-75"
46
+ onClick={onClose}
47
+ />
48
+ <div
49
+ className={`inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle w-full ${sizeClasses[size]} ${className}`}
50
+ onClick={(e) => e.stopPropagation()}
51
+ >
52
+ {title && (
53
+ <div className="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
54
+ <h3 className="text-lg font-semibold text-gray-900">{title}</h3>
55
+ {showCloseButton && (
56
+ <button
57
+ onClick={onClose}
58
+ className="text-gray-400 hover:text-gray-500"
59
+ >
60
+ <Icon name="XMarkIcon" className="h-6 w-6" />
61
+ </button>
62
+ )}
63
+ </div>
64
+ )}
65
+ <div className="px-6 py-4">{children}</div>
66
+ {footer && (
67
+ <div className="px-6 py-4 border-t border-gray-200 bg-gray-50">
68
+ {footer}
69
+ </div>
70
+ )}
71
+ </div>
72
+ </div>
73
+ </div>
74
+ );
75
+ };
76
+
77
+ export default Modal;
78
+
@@ -0,0 +1,98 @@
1
+ import { useState } from 'react';
2
+ import { Modal } from './Modal';
3
+ import { Button } from '../../Atoms/Button/Button';
4
+
5
+ export default {
6
+ title: 'Layout/Modal',
7
+ component: Modal,
8
+ tags: ['autodocs'],
9
+ argTypes: {
10
+ size: {
11
+ control: 'select',
12
+ options: ['small', 'medium', 'large', 'xl', 'full'],
13
+ },
14
+ },
15
+ };
16
+
17
+ export const Default = {
18
+ render: () => {
19
+ const [isOpen, setIsOpen] = useState(false);
20
+ return (
21
+ <>
22
+ <Button onClick={() => setIsOpen(true)}>Abrir Modal</Button>
23
+ <Modal
24
+ isOpen={isOpen}
25
+ onClose={() => setIsOpen(false)}
26
+ title="Título del Modal"
27
+ >
28
+ <p>Este es el contenido del modal. Puedes agregar cualquier contenido aquí.</p>
29
+ </Modal>
30
+ </>
31
+ );
32
+ },
33
+ };
34
+
35
+ export const WithFooter = {
36
+ render: () => {
37
+ const [isOpen, setIsOpen] = useState(false);
38
+ return (
39
+ <>
40
+ <Button onClick={() => setIsOpen(true)}>Abrir Modal con Footer</Button>
41
+ <Modal
42
+ isOpen={isOpen}
43
+ onClose={() => setIsOpen(false)}
44
+ title="Modal con Footer"
45
+ footer={
46
+ <div className="flex justify-end space-x-2">
47
+ <Button variant="outline" onClick={() => setIsOpen(false)}>
48
+ Cancelar
49
+ </Button>
50
+ <Button variant="primary" onClick={() => setIsOpen(false)}>
51
+ Guardar
52
+ </Button>
53
+ </div>
54
+ }
55
+ >
56
+ <p>Este modal tiene un footer con botones de acción.</p>
57
+ </Modal>
58
+ </>
59
+ );
60
+ },
61
+ };
62
+
63
+ export const Large = {
64
+ render: () => {
65
+ const [isOpen, setIsOpen] = useState(false);
66
+ return (
67
+ <>
68
+ <Button onClick={() => setIsOpen(true)}>Abrir Modal Grande</Button>
69
+ <Modal
70
+ isOpen={isOpen}
71
+ onClose={() => setIsOpen(false)}
72
+ title="Modal Grande"
73
+ size="large"
74
+ >
75
+ <p>Este es un modal de tamaño grande con más espacio para contenido.</p>
76
+ </Modal>
77
+ </>
78
+ );
79
+ },
80
+ };
81
+
82
+ export const WithoutTitle = {
83
+ render: () => {
84
+ const [isOpen, setIsOpen] = useState(false);
85
+ return (
86
+ <>
87
+ <Button onClick={() => setIsOpen(true)}>Abrir Modal sin Título</Button>
88
+ <Modal
89
+ isOpen={isOpen}
90
+ onClose={() => setIsOpen(false)}
91
+ >
92
+ <p>Este modal no tiene título.</p>
93
+ </Modal>
94
+ </>
95
+ );
96
+ },
97
+ };
98
+
@@ -0,0 +1,109 @@
1
+ import { Icon } from '../../Atoms/Icon/Icon';
2
+ import { Button } from '../../Atoms/Button/Button';
3
+
4
+ export const Pagination = ({
5
+ currentPage = 1,
6
+ totalPages = 1,
7
+ onPageChange,
8
+ showFirstLast = true,
9
+ className = '',
10
+ ...props
11
+ }) => {
12
+ const handlePageChange = (page) => {
13
+ if (page >= 1 && page <= totalPages && page !== currentPage) {
14
+ if (onPageChange) {
15
+ onPageChange(page);
16
+ }
17
+ }
18
+ };
19
+
20
+ const getPageNumbers = () => {
21
+ const pages = [];
22
+ const maxVisible = 5;
23
+
24
+ if (totalPages <= maxVisible) {
25
+ for (let i = 1; i <= totalPages; i++) {
26
+ pages.push(i);
27
+ }
28
+ } else {
29
+ if (currentPage <= 3) {
30
+ for (let i = 1; i <= 5; i++) {
31
+ pages.push(i);
32
+ }
33
+ } else if (currentPage >= totalPages - 2) {
34
+ for (let i = totalPages - 4; i <= totalPages; i++) {
35
+ pages.push(i);
36
+ }
37
+ } else {
38
+ for (let i = currentPage - 2; i <= currentPage + 2; i++) {
39
+ pages.push(i);
40
+ }
41
+ }
42
+ }
43
+ return pages;
44
+ };
45
+
46
+ if (totalPages <= 1) return null;
47
+
48
+ return (
49
+ <nav className={className} aria-label="Pagination" {...props}>
50
+ <div className="flex items-center justify-center space-x-1">
51
+ {showFirstLast && (
52
+ <Button
53
+ variant="outline"
54
+ size="small"
55
+ onClick={() => handlePageChange(1)}
56
+ disabled={currentPage === 1}
57
+ >
58
+ <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
59
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M18.75 19.5l-7.5-7.5 7.5-7.5m-6 15L5.25 12l7.5-7.5" />
60
+ </svg>
61
+ </Button>
62
+ )}
63
+ <Button
64
+ variant="outline"
65
+ size="small"
66
+ onClick={() => handlePageChange(currentPage - 1)}
67
+ disabled={currentPage === 1}
68
+ >
69
+ <Icon name="ChevronLeftIcon" className="h-4 w-4" />
70
+ </Button>
71
+
72
+ {getPageNumbers().map((page) => (
73
+ <Button
74
+ key={page}
75
+ variant={currentPage === page ? 'primary' : 'outline'}
76
+ size="small"
77
+ onClick={() => handlePageChange(page)}
78
+ >
79
+ {page}
80
+ </Button>
81
+ ))}
82
+
83
+ <Button
84
+ variant="outline"
85
+ size="small"
86
+ onClick={() => handlePageChange(currentPage + 1)}
87
+ disabled={currentPage === totalPages}
88
+ >
89
+ <Icon name="ChevronRightIcon" className="h-4 w-4" />
90
+ </Button>
91
+ {showFirstLast && (
92
+ <Button
93
+ variant="outline"
94
+ size="small"
95
+ onClick={() => handlePageChange(totalPages)}
96
+ disabled={currentPage === totalPages}
97
+ >
98
+ <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
99
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5.25 4.5l7.5 7.5-7.5 7.5m6-15l7.5 7.5-7.5 7.5" />
100
+ </svg>
101
+ </Button>
102
+ )}
103
+ </div>
104
+ </nav>
105
+ );
106
+ };
107
+
108
+ export default Pagination;
109
+
@@ -0,0 +1,62 @@
1
+ import { useState } from 'react';
2
+ import { Pagination } from './Pagination';
3
+
4
+ export default {
5
+ title: 'Layout/Pagination',
6
+ component: Pagination,
7
+ tags: ['autodocs'],
8
+ };
9
+
10
+ export const Default = {
11
+ render: () => {
12
+ const [page, setPage] = useState(1);
13
+ return (
14
+ <Pagination
15
+ currentPage={page}
16
+ totalPages={10}
17
+ onPageChange={setPage}
18
+ />
19
+ );
20
+ },
21
+ };
22
+
23
+ export const WithoutFirstLast = {
24
+ render: () => {
25
+ const [page, setPage] = useState(1);
26
+ return (
27
+ <Pagination
28
+ currentPage={page}
29
+ totalPages={10}
30
+ onPageChange={setPage}
31
+ showFirstLast={false}
32
+ />
33
+ );
34
+ },
35
+ };
36
+
37
+ export const FewPages = {
38
+ render: () => {
39
+ const [page, setPage] = useState(1);
40
+ return (
41
+ <Pagination
42
+ currentPage={page}
43
+ totalPages={3}
44
+ onPageChange={setPage}
45
+ />
46
+ );
47
+ },
48
+ };
49
+
50
+ export const ManyPages = {
51
+ render: () => {
52
+ const [page, setPage] = useState(5);
53
+ return (
54
+ <Pagination
55
+ currentPage={page}
56
+ totalPages={20}
57
+ onPageChange={setPage}
58
+ />
59
+ );
60
+ },
61
+ };
62
+
@@ -0,0 +1,57 @@
1
+ import { useTranslation } from 'react-i18next';
2
+ import { Icon } from '../../Atoms/Icon/Icon';
3
+
4
+ export const Sidebar = ({
5
+ items = [],
6
+ activeItem,
7
+ onItemClick,
8
+ className = '',
9
+ ...props
10
+ }) => {
11
+ const { t } = useTranslation();
12
+
13
+ const defaultItems = [
14
+ { id: 'home', label: t('nav.home'), icon: 'HomeIcon' },
15
+ { id: 'dashboard', label: t('nav.dashboard'), icon: 'ChartBarIcon' },
16
+ { id: 'profile', label: t('nav.profile'), icon: 'UserIcon' },
17
+ { id: 'settings', label: t('nav.settings'), icon: 'Cog6ToothIcon' },
18
+ ];
19
+
20
+ const menuItems = items.length > 0 ? items : defaultItems;
21
+
22
+ return (
23
+ <aside className={`bg-white shadow-sm border-r border-gray-200 ${className}`} {...props}>
24
+ <nav className="p-4">
25
+ <ul className="space-y-2">
26
+ {menuItems.map((item) => (
27
+ <li key={item.id}>
28
+ <button
29
+ onClick={() => onItemClick && onItemClick(item.id)}
30
+ className={`
31
+ w-full flex items-center px-4 py-2 rounded-lg
32
+ transition-colors
33
+ ${
34
+ activeItem === item.id
35
+ ? 'bg-blue-100 text-blue-700 font-medium'
36
+ : 'text-gray-700 hover:bg-gray-100'
37
+ }
38
+ `}
39
+ >
40
+ {item.icon && (
41
+ <Icon
42
+ name={item.icon}
43
+ className="mr-3 h-5 w-5"
44
+ />
45
+ )}
46
+ <span>{item.label}</span>
47
+ </button>
48
+ </li>
49
+ ))}
50
+ </ul>
51
+ </nav>
52
+ </aside>
53
+ );
54
+ };
55
+
56
+ export default Sidebar;
57
+
@@ -0,0 +1,51 @@
1
+ import { useState } from 'react';
2
+ import { Sidebar } from './Sidebar';
3
+ import { I18nProvider } from '../../../providers/I18nProvider';
4
+
5
+ export default {
6
+ title: 'Layout/Sidebar',
7
+ component: Sidebar,
8
+ tags: ['autodocs'],
9
+ decorators: [
10
+ (Story) => (
11
+ <I18nProvider>
12
+ <Story />
13
+ </I18nProvider>
14
+ ),
15
+ ],
16
+ };
17
+
18
+ export const Default = {
19
+ render: () => {
20
+ const [activeItem, setActiveItem] = useState('home');
21
+ return (
22
+ <div className="w-64">
23
+ <Sidebar
24
+ activeItem={activeItem}
25
+ onItemClick={setActiveItem}
26
+ />
27
+ </div>
28
+ );
29
+ },
30
+ };
31
+
32
+ export const WithCustomItems = {
33
+ render: () => {
34
+ const [activeItem, setActiveItem] = useState('item1');
35
+ const customItems = [
36
+ { id: 'item1', label: 'Custom Item 1', icon: 'HomeIcon' },
37
+ { id: 'item2', label: 'Custom Item 2', icon: 'UserIcon' },
38
+ { id: 'item3', label: 'Custom Item 3', icon: 'SettingsIcon' },
39
+ ];
40
+ return (
41
+ <div className="w-64">
42
+ <Sidebar
43
+ items={customItems}
44
+ activeItem={activeItem}
45
+ onItemClick={setActiveItem}
46
+ />
47
+ </div>
48
+ );
49
+ },
50
+ };
51
+
@@ -0,0 +1,132 @@
1
+ import { Icon } from '../../Atoms/Icon/Icon';
2
+
3
+ export const Stepper = ({
4
+ steps = [],
5
+ currentStep = 0,
6
+ orientation = 'horizontal',
7
+ className = '',
8
+ ...props
9
+ }) => {
10
+ if (steps.length === 0) return null;
11
+
12
+ if (orientation === 'vertical') {
13
+ return (
14
+ <div className={className} {...props}>
15
+ <ol className="space-y-4">
16
+ {steps.map((step, index) => {
17
+ const isActive = index === currentStep;
18
+ const isCompleted = index < currentStep;
19
+ const isUpcoming = index > currentStep;
20
+
21
+ return (
22
+ <li key={index} className="flex items-start">
23
+ <div className="flex items-center">
24
+ <div
25
+ className={`
26
+ flex items-center justify-center w-10 h-10 rounded-full border-2
27
+ ${
28
+ isCompleted
29
+ ? 'bg-blue-600 border-blue-600 text-white'
30
+ : isActive
31
+ ? 'bg-blue-100 border-blue-600 text-blue-600'
32
+ : 'bg-white border-gray-300 text-gray-400'
33
+ }
34
+ `}
35
+ >
36
+ {isCompleted ? (
37
+ <Icon name="CheckCircleIcon" className="h-6 w-6" />
38
+ ) : (
39
+ <span className="font-medium">{index + 1}</span>
40
+ )}
41
+ </div>
42
+ {index < steps.length - 1 && (
43
+ <div
44
+ className={`
45
+ w-0.5 h-12 ml-5
46
+ ${isCompleted ? 'bg-blue-600' : 'bg-gray-300'}
47
+ `}
48
+ />
49
+ )}
50
+ </div>
51
+ <div className="ml-4 pb-8">
52
+ <h3
53
+ className={`
54
+ text-sm font-medium
55
+ ${isActive || isCompleted ? 'text-gray-900' : 'text-gray-400'}
56
+ `}
57
+ >
58
+ {step.label}
59
+ </h3>
60
+ {step.description && (
61
+ <p className="text-sm text-gray-500 mt-1">{step.description}</p>
62
+ )}
63
+ </div>
64
+ </li>
65
+ );
66
+ })}
67
+ </ol>
68
+ </div>
69
+ );
70
+ }
71
+
72
+ return (
73
+ <div className={className} {...props}>
74
+ <ol className="flex items-center w-full">
75
+ {steps.map((step, index) => {
76
+ const isActive = index === currentStep;
77
+ const isCompleted = index < currentStep;
78
+ const isUpcoming = index > currentStep;
79
+ const isLast = index === steps.length - 1;
80
+
81
+ return (
82
+ <li key={index} className="flex items-center w-full">
83
+ <div className="flex items-center">
84
+ <div
85
+ className={`
86
+ flex items-center justify-center w-10 h-10 rounded-full border-2
87
+ ${
88
+ isCompleted
89
+ ? 'bg-blue-600 border-blue-600 text-white'
90
+ : isActive
91
+ ? 'bg-blue-100 border-blue-600 text-blue-600'
92
+ : 'bg-white border-gray-300 text-gray-400'
93
+ }
94
+ `}
95
+ >
96
+ {isCompleted ? (
97
+ <Icon name="CheckIcon" className="h-6 w-6" />
98
+ ) : (
99
+ <span className="font-medium">{index + 1}</span>
100
+ )}
101
+ </div>
102
+ {!isLast && (
103
+ <div
104
+ className={`
105
+ w-full h-0.5 mx-2
106
+ ${isCompleted ? 'bg-blue-600' : 'bg-gray-300'}
107
+ `}
108
+ />
109
+ )}
110
+ </div>
111
+ </li>
112
+ );
113
+ })}
114
+ </ol>
115
+ {steps[currentStep] && (
116
+ <div className="mt-6">
117
+ <h3 className="text-lg font-semibold text-gray-900">
118
+ {steps[currentStep].label}
119
+ </h3>
120
+ {steps[currentStep].description && (
121
+ <p className="text-sm text-gray-500 mt-1">
122
+ {steps[currentStep].description}
123
+ </p>
124
+ )}
125
+ </div>
126
+ )}
127
+ </div>
128
+ );
129
+ };
130
+
131
+ export default Stepper;
132
+
@@ -0,0 +1,78 @@
1
+ import { useState } from 'react';
2
+ import { Stepper } from './Stepper';
3
+ import { Button } from '../../Atoms/Button/Button';
4
+
5
+ const steps = [
6
+ { label: 'Información Personal', description: 'Completa tus datos básicos' },
7
+ { label: 'Verificación', description: 'Verifica tu identidad' },
8
+ { label: 'Configuración', description: 'Configura tus preferencias' },
9
+ { label: 'Confirmación', description: 'Revisa y confirma' },
10
+ ];
11
+
12
+ export default {
13
+ title: 'Layout/Stepper',
14
+ component: Stepper,
15
+ tags: ['autodocs'],
16
+ };
17
+
18
+ export const Horizontal = {
19
+ render: () => {
20
+ const [currentStep, setCurrentStep] = useState(0);
21
+ return (
22
+ <div>
23
+ <Stepper steps={steps} currentStep={currentStep} />
24
+ <div className="mt-6 flex space-x-2">
25
+ <Button
26
+ variant="outline"
27
+ onClick={() => setCurrentStep(Math.max(0, currentStep - 1))}
28
+ disabled={currentStep === 0}
29
+ >
30
+ Anterior
31
+ </Button>
32
+ <Button
33
+ variant="primary"
34
+ onClick={() => setCurrentStep(Math.min(steps.length - 1, currentStep + 1))}
35
+ disabled={currentStep === steps.length - 1}
36
+ >
37
+ Siguiente
38
+ </Button>
39
+ </div>
40
+ </div>
41
+ );
42
+ },
43
+ };
44
+
45
+ export const Vertical = {
46
+ render: () => {
47
+ const [currentStep, setCurrentStep] = useState(0);
48
+ return (
49
+ <div>
50
+ <Stepper steps={steps} currentStep={currentStep} orientation="vertical" />
51
+ <div className="mt-6 flex space-x-2">
52
+ <Button
53
+ variant="outline"
54
+ onClick={() => setCurrentStep(Math.max(0, currentStep - 1))}
55
+ disabled={currentStep === 0}
56
+ >
57
+ Anterior
58
+ </Button>
59
+ <Button
60
+ variant="primary"
61
+ onClick={() => setCurrentStep(Math.min(steps.length - 1, currentStep + 1))}
62
+ disabled={currentStep === steps.length - 1}
63
+ >
64
+ Siguiente
65
+ </Button>
66
+ </div>
67
+ </div>
68
+ );
69
+ },
70
+ };
71
+
72
+ export const Completed = {
73
+ args: {
74
+ steps: steps,
75
+ currentStep: 3,
76
+ },
77
+ };
78
+