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,119 @@
1
+ import { Radios, RADIOS_SCALE } from './Radios';
2
+
3
+ const sizeOptions = RADIOS_SCALE.map((token) => token.key);
4
+
5
+ export default {
6
+ title: 'Atoms/Radios',
7
+ component: Radios,
8
+ tags: ['autodocs'],
9
+ argTypes: {
10
+ size: {
11
+ control: 'select',
12
+ options: sizeOptions,
13
+ description: 'Token de radio (px base transformado a rem)',
14
+ },
15
+ },
16
+ };
17
+
18
+ export const Playground = {
19
+ args: {
20
+ size: '16',
21
+ className: 'bg-emerald-500 w-32 h-32',
22
+ },
23
+ };
24
+
25
+ export const EscalaCompleta = () => (
26
+ <div className="space-y-4">
27
+ <h3 className="text-lg font-semibold text-gray-800 mb-4">Tamaños</h3>
28
+ {RADIOS_SCALE.map((token) => (
29
+ <div
30
+ key={token.key}
31
+ className="flex items-center gap-6"
32
+ >
33
+ <div className={`radius-token radius-token-${token.key}`} />
34
+ <div className="flex items-center gap-2 text-sm">
35
+ <span className={`text-gray-800 ${token.key === '16' ? 'font-bold' : ''}`}>
36
+ {token.px}
37
+ </span>
38
+ <span className={`${token.key === '16' ? 'text-emerald-600 font-bold' : 'text-emerald-600'}`}>
39
+ {token.rem}
40
+ </span>
41
+ </div>
42
+ </div>
43
+ ))}
44
+ </div>
45
+ );
46
+
47
+ export const EjemplosDeUso = () => (
48
+ <div className="space-y-6">
49
+ <div>
50
+ <p className="text-body-md font-medium text-gray-700 mb-4">
51
+ Botones con diferentes radios
52
+ </p>
53
+ <div className="flex items-center gap-4 flex-wrap">
54
+ <button className={`px-4 py-2 bg-emerald-500 text-white radius-4 hover:bg-emerald-600 transition-colors`}>
55
+ radius-4
56
+ </button>
57
+ <button className={`px-4 py-2 bg-emerald-500 text-white radius-8 hover:bg-emerald-600 transition-colors`}>
58
+ radius-8
59
+ </button>
60
+ <button className={`px-4 py-2 bg-emerald-500 text-white radius-12 hover:bg-emerald-600 transition-colors`}>
61
+ radius-12
62
+ </button>
63
+ <button className={`px-4 py-2 bg-emerald-500 text-white radius-16 hover:bg-emerald-600 transition-colors`}>
64
+ radius-16 (Base)
65
+ </button>
66
+ <button className={`px-4 py-2 bg-emerald-500 text-white radius-24 hover:bg-emerald-600 transition-colors`}>
67
+ radius-24
68
+ </button>
69
+ <button className={`px-4 py-2 bg-emerald-500 text-white radius-32 hover:bg-emerald-600 transition-colors`}>
70
+ radius-32
71
+ </button>
72
+ </div>
73
+ </div>
74
+
75
+ <div>
76
+ <p className="text-body-md font-medium text-gray-700 mb-4">
77
+ Cards con diferentes radios
78
+ </p>
79
+ <div className="grid grid-cols-3 gap-4">
80
+ <div className={`p-4 bg-white border border-gray-200 shadow-sm radius-4`}>
81
+ <h3 className="font-semibold mb-2">Card radius-4</h3>
82
+ <p className="text-sm text-gray-600">Bordes más cuadrados</p>
83
+ </div>
84
+ <div className={`p-4 bg-white border border-gray-200 shadow-sm radius-16`}>
85
+ <h3 className="font-semibold mb-2">Card radius-16</h3>
86
+ <p className="text-sm text-gray-600">Bordes estándar</p>
87
+ </div>
88
+ <div className={`p-4 bg-white border border-gray-200 shadow-sm radius-32`}>
89
+ <h3 className="font-semibold mb-2">Card radius-32</h3>
90
+ <p className="text-sm text-gray-600">Bordes muy redondeados</p>
91
+ </div>
92
+ </div>
93
+ </div>
94
+
95
+ <div>
96
+ <p className="text-body-md font-medium text-gray-700 mb-4">
97
+ Inputs con diferentes radios
98
+ </p>
99
+ <div className="flex flex-col gap-4 max-w-md">
100
+ <input
101
+ type="text"
102
+ placeholder="Input con radius-4"
103
+ className={`px-4 py-2 border border-gray-300 radius-4 focus:outline-none focus:ring-2 focus:ring-emerald-500`}
104
+ />
105
+ <input
106
+ type="text"
107
+ placeholder="Input con radius-12"
108
+ className={`px-4 py-2 border border-gray-300 radius-12 focus:outline-none focus:ring-2 focus:ring-emerald-500`}
109
+ />
110
+ <input
111
+ type="text"
112
+ placeholder="Input con radius-16 (Base)"
113
+ className={`px-4 py-2 border border-gray-300 radius-16 focus:outline-none focus:ring-2 focus:ring-emerald-500`}
114
+ />
115
+ </div>
116
+ </div>
117
+ </div>
118
+ );
119
+
@@ -0,0 +1,25 @@
1
+ const ShadowCard = ({ children = 'Default' }) => (
2
+ <div className="p-12 bg-gradient-to-br from-gray-50 to-white min-h-[240px] flex items-center justify-center">
3
+ <div className="shadow px-10 py-8 text-center">
4
+ <p className="text-body-lg text-gray-700">{children}</p>
5
+ </div>
6
+ </div>
7
+ );
8
+
9
+ export default {
10
+ title: 'Atoms/Shadows',
11
+ };
12
+
13
+ export const Default = {
14
+ render: () => (
15
+ <div className="max-w-lg mx-auto space-y-4">
16
+ <ShadowCard>Shadow Default</ShadowCard>
17
+ <div className="text-center">
18
+ <code className="text-emerald-600 text-sm">.shadow</code>
19
+ <p className="text-gray-500 text-sm mt-1">Box shadow oficial del sistema</p>
20
+ </div>
21
+ </div>
22
+ ),
23
+ };
24
+
25
+
@@ -0,0 +1,50 @@
1
+ export const Skeleton = ({
2
+ variant = 'text',
3
+ width,
4
+ height,
5
+ className = '',
6
+ ...props
7
+ }) => {
8
+ const baseClasses = 'animate-pulse bg-gray-200 rounded';
9
+
10
+ const variantClasses = {
11
+ text: 'h-4',
12
+ circular: 'rounded-full',
13
+ rectangular: 'rounded',
14
+ };
15
+
16
+ const style = {};
17
+ if (width) style.width = width;
18
+ if (height) style.height = height;
19
+
20
+ if (variant === 'circular') {
21
+ return (
22
+ <div
23
+ className={`${baseClasses} ${variantClasses.circular} ${className}`}
24
+ style={style}
25
+ {...props}
26
+ />
27
+ );
28
+ }
29
+
30
+ if (variant === 'rectangular') {
31
+ return (
32
+ <div
33
+ className={`${baseClasses} ${variantClasses.rectangular} ${className}`}
34
+ style={style}
35
+ {...props}
36
+ />
37
+ );
38
+ }
39
+
40
+ return (
41
+ <div
42
+ className={`${baseClasses} ${variantClasses.text} ${className}`}
43
+ style={style}
44
+ {...props}
45
+ />
46
+ );
47
+ };
48
+
49
+ export default Skeleton;
50
+
@@ -0,0 +1,55 @@
1
+ import { Skeleton } from './Skeleton';
2
+
3
+ export default {
4
+ title: 'Atoms/Skeleton',
5
+ component: Skeleton,
6
+ tags: ['autodocs'],
7
+ argTypes: {
8
+ variant: {
9
+ control: 'select',
10
+ options: ['text', 'circular', 'rectangular'],
11
+ },
12
+ },
13
+ };
14
+
15
+ export const Text = {
16
+ args: {
17
+ variant: 'text',
18
+ },
19
+ };
20
+
21
+ export const Circular = {
22
+ args: {
23
+ variant: 'circular',
24
+ width: 40,
25
+ height: 40,
26
+ },
27
+ };
28
+
29
+ export const Rectangular = {
30
+ args: {
31
+ variant: 'rectangular',
32
+ width: 200,
33
+ height: 100,
34
+ },
35
+ };
36
+
37
+ export const CustomSize = {
38
+ args: {
39
+ variant: 'text',
40
+ width: '50%',
41
+ height: 20,
42
+ },
43
+ };
44
+
45
+ export const CardSkeleton = {
46
+ render: () => (
47
+ <div className="border rounded-lg p-4 space-y-3">
48
+ <Skeleton variant="circular" width={40} height={40} />
49
+ <Skeleton variant="text" width="80%" />
50
+ <Skeleton variant="text" width="60%" />
51
+ <Skeleton variant="rectangular" width="100%" height={100} />
52
+ </div>
53
+ ),
54
+ };
55
+
@@ -0,0 +1,56 @@
1
+ const SCALE = [
2
+ { key: '160', px: '160px', rem: '10rem' },
3
+ { key: '96', px: '96px', rem: '6rem' },
4
+ { key: '80', px: '80px', rem: '5rem' },
5
+ { key: '64', px: '64px', rem: '4rem' },
6
+ { key: '48', px: '48px', rem: '3rem' },
7
+ { key: '40', px: '40px', rem: '2.5rem' },
8
+ { key: '32', px: '32px', rem: '2rem' },
9
+ { key: '24', px: '24px', rem: '1.5rem' },
10
+ { key: '16', px: '16px', rem: '1rem' },
11
+ { key: '12', px: '12px', rem: '0.75rem' },
12
+ { key: '8', px: '8px', rem: '0.5rem' },
13
+ { key: '4', px: '4px', rem: '0.25rem' },
14
+ { key: '2', px: '2px', rem: '0.125rem' },
15
+ ];
16
+
17
+ const scaleMap = SCALE.reduce((acc, token) => {
18
+ acc[token.key] = token;
19
+ acc[`space-${token.key}`] = token;
20
+ return acc;
21
+ }, {});
22
+
23
+ export const SPACING_SCALE = SCALE;
24
+
25
+ export const Spacing = ({
26
+ size = '16',
27
+ axis = 'vertical',
28
+ as: Component = 'div',
29
+ className = '',
30
+ style = {},
31
+ thickness = '1px',
32
+ ...props
33
+ }) => {
34
+ const normalized = typeof size === 'number' ? String(size) : size;
35
+ const token = scaleMap[normalized] || scaleMap['16'];
36
+ const cssVar = `var(--space-${token.key})`;
37
+
38
+ const dimensionStyle =
39
+ axis === 'horizontal'
40
+ ? { width: cssVar, height: thickness }
41
+ : axis === 'square'
42
+ ? { width: cssVar, height: cssVar }
43
+ : { height: cssVar, width: thickness };
44
+
45
+ return (
46
+ <Component
47
+ className={className}
48
+ style={{ display: 'inline-flex', ...dimensionStyle, ...style }}
49
+ aria-hidden="true"
50
+ {...props}
51
+ />
52
+ );
53
+ };
54
+
55
+ export default Spacing;
56
+
@@ -0,0 +1,78 @@
1
+ import { Spacing, SPACING_SCALE } from './Spacing';
2
+
3
+ const sizeOptions = SPACING_SCALE.map((token) => token.key);
4
+
5
+ export default {
6
+ title: 'Atoms/Spacing',
7
+ component: Spacing,
8
+ tags: ['autodocs'],
9
+ argTypes: {
10
+ size: {
11
+ control: 'select',
12
+ options: sizeOptions,
13
+ description: 'Token de espacio (px base transformado a rem)',
14
+ },
15
+ axis: {
16
+ control: 'inline-radio',
17
+ options: ['vertical', 'horizontal', 'square'],
18
+ },
19
+ thickness: {
20
+ control: 'text',
21
+ description: 'Grosor usado cuando axis es vertical u horizontal',
22
+ },
23
+ },
24
+ };
25
+
26
+ export const Playground = {
27
+ args: {
28
+ size: '32',
29
+ axis: 'square',
30
+ className: 'bg-emerald-500 rounded',
31
+ },
32
+ };
33
+
34
+ export const EscalaCompleta = () => (
35
+ <div className="space-y-4">
36
+ {SPACING_SCALE.map((token) => (
37
+ <div
38
+ key={token.key}
39
+ className="flex items-center gap-6 border border-gray-100 rounded-lg p-4 shadow-sm bg-white"
40
+ >
41
+ <div className={`space-token space-token-${token.key}`} />
42
+ <div className="flex flex-col text-sm text-gray-700">
43
+ <span className="font-semibold">{`space-${token.key}`}</span>
44
+ <span>{token.px} / {token.rem}</span>
45
+ </div>
46
+ </div>
47
+ ))}
48
+ </div>
49
+ );
50
+
51
+ export const UsosComunes = () => (
52
+ <div className="space-y-6">
53
+ <div>
54
+ <p className="text-body-md font-medium text-gray-700 mb-2">
55
+ Separadores verticales
56
+ </p>
57
+ <div className="flex flex-col border rounded-lg p-4 gap-0 bg-white">
58
+ <div className="py-2 text-gray-600">Bloque A</div>
59
+ <Spacing size="24" axis="vertical" className="bg-transparent" />
60
+ <div className="py-2 text-gray-600">Bloque B</div>
61
+ </div>
62
+ </div>
63
+
64
+ <div>
65
+ <p className="text-body-md font-medium text-gray-700 mb-2">
66
+ Separadores horizontales
67
+ </p>
68
+ <div className="flex items-center border rounded-lg p-4 bg-white">
69
+ <div className="text-gray-600">Etiqueta</div>
70
+ <Spacing size="16" axis="horizontal" thickness="1px" className="bg-transparent" />
71
+ <button className="px-3 py-1 text-sm rounded-md bg-emerald-50 text-emerald-700">
72
+ Acción
73
+ </button>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ );
78
+
@@ -0,0 +1,47 @@
1
+ export const Spinner = ({
2
+ size = 'medium',
3
+ variant = 'primary',
4
+ className = '',
5
+ ...props
6
+ }) => {
7
+ const sizeClasses = {
8
+ small: 'w-4 h-4',
9
+ medium: 'w-8 h-8',
10
+ large: 'w-12 h-12',
11
+ };
12
+
13
+ const variantClasses = {
14
+ primary: 'text-blue-600',
15
+ secondary: 'text-gray-600',
16
+ white: 'text-white',
17
+ };
18
+
19
+ const classes = `animate-spin ${sizeClasses[size]} ${variantClasses[variant]} ${className}`;
20
+
21
+ return (
22
+ <svg
23
+ className={classes}
24
+ xmlns="http://www.w3.org/2000/svg"
25
+ fill="none"
26
+ viewBox="0 0 24 24"
27
+ {...props}
28
+ >
29
+ <circle
30
+ className="opacity-25"
31
+ cx="12"
32
+ cy="12"
33
+ r="10"
34
+ stroke="currentColor"
35
+ strokeWidth="4"
36
+ />
37
+ <path
38
+ className="opacity-75"
39
+ fill="currentColor"
40
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
41
+ />
42
+ </svg>
43
+ );
44
+ };
45
+
46
+ export default Spinner;
47
+
@@ -0,0 +1,56 @@
1
+ import { Spinner } from './Spinner';
2
+
3
+ export default {
4
+ title: 'Atoms/Spinner',
5
+ component: Spinner,
6
+ tags: ['autodocs'],
7
+ argTypes: {
8
+ size: {
9
+ control: 'select',
10
+ options: ['small', 'medium', 'large'],
11
+ },
12
+ variant: {
13
+ control: 'select',
14
+ options: ['primary', 'secondary', 'white'],
15
+ },
16
+ },
17
+ };
18
+
19
+ export const Default = {
20
+ args: {
21
+ size: 'medium',
22
+ variant: 'primary',
23
+ },
24
+ };
25
+
26
+ export const Small = {
27
+ args: {
28
+ size: 'small',
29
+ },
30
+ };
31
+
32
+ export const Large = {
33
+ args: {
34
+ size: 'large',
35
+ },
36
+ };
37
+
38
+ export const Secondary = {
39
+ args: {
40
+ variant: 'secondary',
41
+ },
42
+ };
43
+
44
+ export const White = {
45
+ args: {
46
+ variant: 'white',
47
+ },
48
+ decorators: [
49
+ (Story) => (
50
+ <div className="bg-gray-800 p-4">
51
+ <Story />
52
+ </div>
53
+ ),
54
+ ],
55
+ };
56
+
@@ -0,0 +1,74 @@
1
+ import { useEffect } from 'react';
2
+ import { Icon } from '../Icon/Icon';
3
+
4
+ export const Toast = ({
5
+ message,
6
+ variant = 'info',
7
+ duration = 5000,
8
+ onClose,
9
+ position = 'top-right',
10
+ className = '',
11
+ ...props
12
+ }) => {
13
+ useEffect(() => {
14
+ if (duration > 0 && onClose) {
15
+ const timer = setTimeout(() => {
16
+ onClose();
17
+ }, duration);
18
+ return () => clearTimeout(timer);
19
+ }
20
+ }, [duration, onClose]);
21
+
22
+ const variantClasses = {
23
+ info: 'bg-blue-50 border-blue-200 text-blue-800',
24
+ success: 'bg-green-50 border-green-200 text-green-800',
25
+ warning: 'bg-yellow-50 border-yellow-200 text-yellow-800',
26
+ danger: 'bg-red-50 border-red-200 text-red-800',
27
+ };
28
+
29
+ const iconMap = {
30
+ info: 'InformationCircleIcon',
31
+ success: 'CheckCircleIcon',
32
+ warning: 'ExclamationTriangleIcon',
33
+ danger: 'XCircleIcon',
34
+ };
35
+
36
+ const positionClasses = {
37
+ 'top-right': 'top-4 right-4',
38
+ 'top-left': 'top-4 left-4',
39
+ 'top-center': 'top-4 left-1/2 transform -translate-x-1/2',
40
+ 'bottom-right': 'bottom-4 right-4',
41
+ 'bottom-left': 'bottom-4 left-4',
42
+ 'bottom-center': 'bottom-4 left-1/2 transform -translate-x-1/2',
43
+ };
44
+
45
+ return (
46
+ <div
47
+ className={`fixed ${positionClasses[position]} z-50 max-w-sm w-full ${className}`}
48
+ {...props}
49
+ >
50
+ <div
51
+ className={`border rounded-lg shadow-lg p-4 flex items-start ${variantClasses[variant]}`}
52
+ >
53
+ <Icon
54
+ name={iconMap[variant]}
55
+ className="mr-3 h-5 w-5 flex-shrink-0 mt-0.5"
56
+ />
57
+ <div className="flex-1">
58
+ <p className="text-sm font-medium">{message}</p>
59
+ </div>
60
+ {onClose && (
61
+ <button
62
+ onClick={onClose}
63
+ className="ml-4 text-current opacity-50 hover:opacity-75"
64
+ >
65
+ <Icon name="XMarkIcon" className="h-5 w-5" />
66
+ </button>
67
+ )}
68
+ </div>
69
+ </div>
70
+ );
71
+ };
72
+
73
+ export default Toast;
74
+
@@ -0,0 +1,101 @@
1
+ import { useState } from 'react';
2
+ import { Toast } from './Toast';
3
+ import { Button } from '../Button/Button';
4
+
5
+ export default {
6
+ title: 'Atoms/Toast',
7
+ component: Toast,
8
+ tags: ['autodocs'],
9
+ };
10
+
11
+ export const Info = {
12
+ render: () => {
13
+ const [show, setShow] = useState(false);
14
+ return (
15
+ <>
16
+ <Button onClick={() => setShow(true)}>Mostrar Toast Info</Button>
17
+ {show && (
18
+ <Toast
19
+ message="Esta es una notificación informativa"
20
+ variant="info"
21
+ onClose={() => setShow(false)}
22
+ />
23
+ )}
24
+ </>
25
+ );
26
+ },
27
+ };
28
+
29
+ export const Success = {
30
+ render: () => {
31
+ const [show, setShow] = useState(false);
32
+ return (
33
+ <>
34
+ <Button onClick={() => setShow(true)}>Mostrar Toast Success</Button>
35
+ {show && (
36
+ <Toast
37
+ message="Operación completada exitosamente"
38
+ variant="success"
39
+ onClose={() => setShow(false)}
40
+ />
41
+ )}
42
+ </>
43
+ );
44
+ },
45
+ };
46
+
47
+ export const Warning = {
48
+ render: () => {
49
+ const [show, setShow] = useState(false);
50
+ return (
51
+ <>
52
+ <Button onClick={() => setShow(true)}>Mostrar Toast Warning</Button>
53
+ {show && (
54
+ <Toast
55
+ message="Por favor revisa esta información"
56
+ variant="warning"
57
+ onClose={() => setShow(false)}
58
+ />
59
+ )}
60
+ </>
61
+ );
62
+ },
63
+ };
64
+
65
+ export const Danger = {
66
+ render: () => {
67
+ const [show, setShow] = useState(false);
68
+ return (
69
+ <>
70
+ <Button onClick={() => setShow(true)}>Mostrar Toast Error</Button>
71
+ {show && (
72
+ <Toast
73
+ message="Ocurrió un error al procesar la solicitud"
74
+ variant="danger"
75
+ onClose={() => setShow(false)}
76
+ />
77
+ )}
78
+ </>
79
+ );
80
+ },
81
+ };
82
+
83
+ export const AutoClose = {
84
+ render: () => {
85
+ const [show, setShow] = useState(false);
86
+ return (
87
+ <>
88
+ <Button onClick={() => setShow(true)}>Mostrar Toast (Auto-close 3s)</Button>
89
+ {show && (
90
+ <Toast
91
+ message="Este toast se cerrará automáticamente en 3 segundos"
92
+ variant="info"
93
+ duration={3000}
94
+ onClose={() => setShow(false)}
95
+ />
96
+ )}
97
+ </>
98
+ );
99
+ },
100
+ };
101
+