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.
- package/LICENSE.md +20 -0
- package/README.md +109 -0
- package/dist/anima-ds.cjs.js +1332 -0
- package/dist/anima-ds.esm.js +88297 -0
- package/dist/vite.svg +1 -0
- package/package.json +76 -0
- package/src/assets/charly.png +0 -0
- package/src/components/Atoms/Alert/Alert.jsx +52 -0
- package/src/components/Atoms/Alert/Alert.stories.jsx +62 -0
- package/src/components/Atoms/Avatar/Avatar.jsx +60 -0
- package/src/components/Atoms/Avatar/Avatar.stories.jsx +61 -0
- package/src/components/Atoms/Badge/Badge.jsx +34 -0
- package/src/components/Atoms/Badge/Badge.stories.jsx +55 -0
- package/src/components/Atoms/Button/Button.jsx +281 -0
- package/src/components/Atoms/Button/Button.stories.jsx +365 -0
- package/src/components/Atoms/Divider/Divider.jsx +49 -0
- package/src/components/Atoms/Divider/Divider.stories.jsx +62 -0
- package/src/components/Atoms/Icon/Icon.jsx +361 -0
- package/src/components/Atoms/Icon/Icon.stories.jsx +115 -0
- package/src/components/Atoms/Label/Label.jsx +22 -0
- package/src/components/Atoms/Progress/Progress.jsx +49 -0
- package/src/components/Atoms/Progress/Progress.stories.jsx +88 -0
- package/src/components/Atoms/Radios/Radios.jsx +39 -0
- package/src/components/Atoms/Radios/Radios.stories.jsx +119 -0
- package/src/components/Atoms/Shadow/Shadow.stories.jsx +25 -0
- package/src/components/Atoms/Skeleton/Skeleton.jsx +50 -0
- package/src/components/Atoms/Skeleton/Skeleton.stories.jsx +55 -0
- package/src/components/Atoms/Spacing/Spacing.jsx +56 -0
- package/src/components/Atoms/Spacing/Spacing.stories.jsx +78 -0
- package/src/components/Atoms/Spinner/Spinner.jsx +47 -0
- package/src/components/Atoms/Spinner/Spinner.stories.jsx +56 -0
- package/src/components/Atoms/Toast/Toast.jsx +74 -0
- package/src/components/Atoms/Toast/Toast.stories.jsx +101 -0
- package/src/components/Atoms/Tooltip/Tooltip.jsx +49 -0
- package/src/components/Atoms/Tooltip/Tooltip.stories.jsx +58 -0
- package/src/components/Atoms/Typography/Typography.jsx +52 -0
- package/src/components/Atoms/Typography/Typography.stories.jsx +267 -0
- package/src/components/DataDisplay/AreaChart/AreaChart.jsx +95 -0
- package/src/components/DataDisplay/AreaChart/AreaChart.stories.jsx +51 -0
- package/src/components/DataDisplay/BarChart/BarChart.jsx +90 -0
- package/src/components/DataDisplay/BarChart/BarChart.stories.jsx +60 -0
- package/src/components/DataDisplay/Card/Card.jsx +31 -0
- package/src/components/DataDisplay/Card/Card.stories.jsx +50 -0
- package/src/components/DataDisplay/ColumnChart/ColumnChart.jsx +92 -0
- package/src/components/DataDisplay/ColumnChart/ColumnChart.stories.jsx +65 -0
- package/src/components/DataDisplay/DBGrid/DBGrid.jsx +138 -0
- package/src/components/DataDisplay/DBGrid/DBGrid.stories.jsx +126 -0
- package/src/components/DataDisplay/DonutChart/DonutChart.jsx +83 -0
- package/src/components/DataDisplay/DonutChart/DonutChart.stories.jsx +48 -0
- package/src/components/DataDisplay/EmptyState/EmptyState.jsx +30 -0
- package/src/components/DataDisplay/EmptyState/EmptyState.stories.jsx +42 -0
- package/src/components/DataDisplay/LineChart/LineChart.jsx +86 -0
- package/src/components/DataDisplay/LineChart/LineChart.stories.jsx +67 -0
- package/src/components/DataDisplay/List/List.jsx +30 -0
- package/src/components/DataDisplay/List/List.stories.jsx +59 -0
- package/src/components/DataDisplay/PieChart/PieChart.jsx +64 -0
- package/src/components/DataDisplay/PieChart/PieChart.stories.jsx +47 -0
- package/src/components/DataDisplay/StatCard/StatCard.jsx +55 -0
- package/src/components/DataDisplay/StatCard/StatCard.stories.jsx +59 -0
- package/src/components/DataDisplay/TagList/TagList.jsx +37 -0
- package/src/components/DataDisplay/TagList/TagList.stories.jsx +48 -0
- package/src/components/DataDisplay/Timeline/Timeline.jsx +41 -0
- package/src/components/DataDisplay/Timeline/Timeline.stories.jsx +64 -0
- package/src/components/Inputs/Checkbox/Checkbox.jsx +27 -0
- package/src/components/Inputs/Checkbox/Checkbox.stories.jsx +51 -0
- package/src/components/Inputs/DatePicker/DatePicker.jsx +55 -0
- package/src/components/Inputs/DatePicker/DatePicker.stories.jsx +52 -0
- package/src/components/Inputs/FileUpload/FileUpload.jsx +108 -0
- package/src/components/Inputs/FileUpload/FileUpload.stories.jsx +52 -0
- package/src/components/Inputs/Input/Input.jsx +50 -0
- package/src/components/Inputs/Input/Input.stories.jsx +63 -0
- package/src/components/Inputs/RadioButton/RadioButton.jsx +31 -0
- package/src/components/Inputs/RadioButton/RadioButton.stories.jsx +59 -0
- package/src/components/Inputs/Select/Select.jsx +59 -0
- package/src/components/Inputs/Select/Select.stories.jsx +66 -0
- package/src/components/Inputs/Switch/Switch.jsx +44 -0
- package/src/components/Inputs/Switch/Switch.stories.jsx +51 -0
- package/src/components/Inputs/Textarea/Textarea.jsx +51 -0
- package/src/components/Inputs/Textarea/Textarea.stories.jsx +65 -0
- package/src/components/Layout/Accordion/Accordion.jsx +58 -0
- package/src/components/Layout/Accordion/Accordion.stories.jsx +55 -0
- package/src/components/Layout/Breadcrumbs/Breadcrumbs.jsx +49 -0
- package/src/components/Layout/Breadcrumbs/Breadcrumbs.stories.jsx +44 -0
- package/src/components/Layout/Breakpoint/Breakpoint.jsx +35 -0
- package/src/components/Layout/Breakpoint/Breakpoint.stories.jsx +348 -0
- package/src/components/Layout/Drawer/Drawer.jsx +75 -0
- package/src/components/Layout/Drawer/Drawer.stories.jsx +77 -0
- package/src/components/Layout/Dropdown/Dropdown.jsx +83 -0
- package/src/components/Layout/Dropdown/Dropdown.stories.jsx +53 -0
- package/src/components/Layout/Grid/Grid.jsx +39 -0
- package/src/components/Layout/Grid/Grid.stories.jsx +546 -0
- package/src/components/Layout/Header/Header.jsx +50 -0
- package/src/components/Layout/Header/Header.stories.jsx +36 -0
- package/src/components/Layout/Layout/Layout.jsx +14 -0
- package/src/components/Layout/Layout/Layout.stories.jsx +34 -0
- package/src/components/Layout/Modal/Modal.jsx +78 -0
- package/src/components/Layout/Modal/Modal.stories.jsx +98 -0
- package/src/components/Layout/Pagination/Pagination.jsx +109 -0
- package/src/components/Layout/Pagination/Pagination.stories.jsx +62 -0
- package/src/components/Layout/Sidebar/Sidebar.jsx +57 -0
- package/src/components/Layout/Sidebar/Sidebar.stories.jsx +51 -0
- package/src/components/Layout/Stepper/Stepper.jsx +132 -0
- package/src/components/Layout/Stepper/Stepper.stories.jsx +78 -0
- package/src/components/Layout/Tabs/Tabs.jsx +63 -0
- package/src/components/Layout/Tabs/Tabs.stories.jsx +62 -0
- package/src/components/Views/ChangePasswordForm/ChangePasswordForm.jsx +125 -0
- package/src/components/Views/ChangePasswordForm/ChangePasswordForm.stories.jsx +55 -0
- package/src/components/Views/Chat/Chat.jsx +134 -0
- package/src/components/Views/Chat/Chat.stories.jsx +143 -0
- package/src/components/Views/LoginForm/LoginForm.jsx +98 -0
- package/src/components/Views/LoginForm/LoginForm.stories.jsx +55 -0
- package/src/i18n/config.js +209 -0
- package/src/index.js +60 -0
- package/src/main.jsx +510 -0
- package/src/providers/I18nProvider.jsx +13 -0
- package/src/style.css +721 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Breakpoint } from './Breakpoint';
|
|
3
|
+
import { Typography } from '../../Atoms/Typography/Typography';
|
|
4
|
+
|
|
5
|
+
const BREAKPOINTS = [
|
|
6
|
+
{
|
|
7
|
+
name: 'Mobile',
|
|
8
|
+
key: 'breakpoint-sm',
|
|
9
|
+
range: '0 - 480px',
|
|
10
|
+
example: '412px',
|
|
11
|
+
selected: true,
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: 'Tablet',
|
|
15
|
+
key: 'breakpoint-md',
|
|
16
|
+
range: '481 - 768px',
|
|
17
|
+
example: '768px',
|
|
18
|
+
selected: false,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'Small/Medium Desktop',
|
|
22
|
+
key: 'breakpoint-base',
|
|
23
|
+
range: '769 - 1440px',
|
|
24
|
+
example: '1440px',
|
|
25
|
+
selected: true,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'Large Desktop',
|
|
29
|
+
key: 'breakpoint-lg',
|
|
30
|
+
range: '1441px +',
|
|
31
|
+
example: '1920px',
|
|
32
|
+
selected: false,
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
export default {
|
|
37
|
+
title: 'Layout/Breakpoint',
|
|
38
|
+
component: Breakpoint,
|
|
39
|
+
tags: ['autodocs'],
|
|
40
|
+
parameters: {
|
|
41
|
+
viewport: {
|
|
42
|
+
viewports: {
|
|
43
|
+
mobile: {
|
|
44
|
+
name: 'Mobile',
|
|
45
|
+
styles: {
|
|
46
|
+
width: '412px',
|
|
47
|
+
height: '915px',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
tablet: {
|
|
51
|
+
name: 'Tablet',
|
|
52
|
+
styles: {
|
|
53
|
+
width: '768px',
|
|
54
|
+
height: '1024px',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
desktop: {
|
|
58
|
+
name: 'Small/Medium Desktop',
|
|
59
|
+
styles: {
|
|
60
|
+
width: '1440px',
|
|
61
|
+
height: '900px',
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
desktopLarge: {
|
|
65
|
+
name: 'Large Desktop',
|
|
66
|
+
styles: {
|
|
67
|
+
width: '1920px',
|
|
68
|
+
height: '1080px',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const BreakpointsOverview = {
|
|
77
|
+
render: () => (
|
|
78
|
+
<div className="space-y-8 p-6">
|
|
79
|
+
<div>
|
|
80
|
+
<Typography variant="h3" className="color-gray-900 mb-4">
|
|
81
|
+
Breakpoints del Sistema
|
|
82
|
+
</Typography>
|
|
83
|
+
<Typography variant="body-md" className="color-gray-600 mb-6">
|
|
84
|
+
Variables CSS disponibles para usar en media queries y componentes responsivos.
|
|
85
|
+
</Typography>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<div className="flex flex-wrap gap-6">
|
|
89
|
+
{BREAKPOINTS.map((bp) => (
|
|
90
|
+
<div
|
|
91
|
+
key={bp.key}
|
|
92
|
+
className={`flex flex-col border-2 rounded-lg p-6 bg-white shadow ${
|
|
93
|
+
bp.selected ? 'border-green-600' : 'border-gray-200'
|
|
94
|
+
}`}
|
|
95
|
+
style={{ minWidth: '280px' }}
|
|
96
|
+
>
|
|
97
|
+
<div className="mb-4">
|
|
98
|
+
<Typography variant="h5" className="color-gray-900 mb-2">
|
|
99
|
+
{bp.name}
|
|
100
|
+
</Typography>
|
|
101
|
+
<Typography variant="body-sm" className="color-gray-600 mb-1">
|
|
102
|
+
Rango: {bp.range}
|
|
103
|
+
</Typography>
|
|
104
|
+
<Typography variant="body-sm" className="color-gray-600 mb-3">
|
|
105
|
+
Ejemplo: {bp.example}
|
|
106
|
+
</Typography>
|
|
107
|
+
<code className="text-body-sm bg-gray-100 px-2 py-1 rounded color-gray-900">
|
|
108
|
+
{bp.key}
|
|
109
|
+
</code>
|
|
110
|
+
</div>
|
|
111
|
+
{bp.selected && (
|
|
112
|
+
<div className="mt-2">
|
|
113
|
+
<span className="text-body-sm color-green-600 font-medium">
|
|
114
|
+
✓ Pantalla seleccionada para trabajar
|
|
115
|
+
</span>
|
|
116
|
+
</div>
|
|
117
|
+
)}
|
|
118
|
+
</div>
|
|
119
|
+
))}
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
),
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const CSSVariables = {
|
|
126
|
+
render: () => (
|
|
127
|
+
<div className="space-y-6 p-6">
|
|
128
|
+
<div>
|
|
129
|
+
<Typography variant="h4" className="color-gray-900 mb-4">
|
|
130
|
+
Variables CSS Disponibles
|
|
131
|
+
</Typography>
|
|
132
|
+
<Typography variant="body-md" className="color-gray-600 mb-6">
|
|
133
|
+
Usa estas variables en tus media queries o componentes:
|
|
134
|
+
</Typography>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div className="bg-gray-50 rounded-lg p-6 space-y-4">
|
|
138
|
+
<div>
|
|
139
|
+
<Typography variant="h6" className="color-gray-900 mb-2">
|
|
140
|
+
Mobile (breakpoint-sm)
|
|
141
|
+
</Typography>
|
|
142
|
+
<code className="block bg-white p-3 rounded border border-gray-200 text-body-sm">
|
|
143
|
+
--breakpoint-sm: 0px<br />
|
|
144
|
+
--breakpoint-sm-max: 480px
|
|
145
|
+
</code>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<div>
|
|
149
|
+
<Typography variant="h6" className="color-gray-900 mb-2">
|
|
150
|
+
Tablet (breakpoint-md)
|
|
151
|
+
</Typography>
|
|
152
|
+
<code className="block bg-white p-3 rounded border border-gray-200 text-body-sm">
|
|
153
|
+
--breakpoint-md: 481px<br />
|
|
154
|
+
--breakpoint-md-max: 768px
|
|
155
|
+
</code>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<div>
|
|
159
|
+
<Typography variant="h6" className="color-gray-900 mb-2">
|
|
160
|
+
Small/Medium Desktop (breakpoint-base)
|
|
161
|
+
</Typography>
|
|
162
|
+
<code className="block bg-white p-3 rounded border border-gray-200 text-body-sm">
|
|
163
|
+
--breakpoint-base: 769px<br />
|
|
164
|
+
--breakpoint-base-max: 1440px
|
|
165
|
+
</code>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<div>
|
|
169
|
+
<Typography variant="h6" className="color-gray-900 mb-2">
|
|
170
|
+
Large Desktop (breakpoint-lg)
|
|
171
|
+
</Typography>
|
|
172
|
+
<code className="block bg-white p-3 rounded border border-gray-200 text-body-sm">
|
|
173
|
+
--breakpoint-lg: 1441px
|
|
174
|
+
</code>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
<div className="bg-blue-50 rounded-lg p-6">
|
|
179
|
+
<Typography variant="h6" className="color-blue-900 mb-2">
|
|
180
|
+
Ejemplo de uso en CSS:
|
|
181
|
+
</Typography>
|
|
182
|
+
<code className="block bg-white p-4 rounded border border-blue-200 text-body-sm color-gray-900 whitespace-pre">
|
|
183
|
+
{`@media (min-width: var(--breakpoint-md)) {
|
|
184
|
+
.my-component {
|
|
185
|
+
padding: var(--space-24);
|
|
186
|
+
}
|
|
187
|
+
}`}
|
|
188
|
+
</code>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
),
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export const VisualBreakpoints = {
|
|
195
|
+
render: () => (
|
|
196
|
+
<div className="space-y-6 p-6">
|
|
197
|
+
<div>
|
|
198
|
+
<Typography variant="h4" className="color-gray-900 mb-4">
|
|
199
|
+
Visualización de Breakpoints
|
|
200
|
+
</Typography>
|
|
201
|
+
<Typography variant="body-md" className="color-gray-600 mb-6">
|
|
202
|
+
Ajusta el tamaño de la ventana para ver cómo cambian los breakpoints.
|
|
203
|
+
</Typography>
|
|
204
|
+
</div>
|
|
205
|
+
|
|
206
|
+
<div className="flex flex-wrap gap-4">
|
|
207
|
+
{BREAKPOINTS.map((bp) => {
|
|
208
|
+
const widthMap = {
|
|
209
|
+
'breakpoint-sm': 'w-20',
|
|
210
|
+
'breakpoint-md': 'w-32',
|
|
211
|
+
'breakpoint-base': 'w-48',
|
|
212
|
+
'breakpoint-lg': 'w-64',
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<div
|
|
217
|
+
key={bp.key}
|
|
218
|
+
className={`${widthMap[bp.key] || 'w-32'} h-32 border-2 rounded-lg p-4 bg-white shadow ${
|
|
219
|
+
bp.selected ? 'border-green-600' : 'border-gray-200'
|
|
220
|
+
}`}
|
|
221
|
+
>
|
|
222
|
+
<Typography variant="body-sm" className="color-gray-900 font-medium mb-1">
|
|
223
|
+
{bp.name}
|
|
224
|
+
</Typography>
|
|
225
|
+
<Typography variant="caption" className="color-gray-600">
|
|
226
|
+
{bp.range}
|
|
227
|
+
</Typography>
|
|
228
|
+
<div className="mt-2">
|
|
229
|
+
<code className="text-[10px] bg-gray-100 px-1.5 py-0.5 rounded color-gray-700">
|
|
230
|
+
{bp.key}
|
|
231
|
+
</code>
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
);
|
|
235
|
+
})}
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
),
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
export const InteractiveBreakpoint = {
|
|
242
|
+
render: () => {
|
|
243
|
+
const getActiveBreakpoint = () => {
|
|
244
|
+
if (typeof window === 'undefined') return 'breakpoint-base';
|
|
245
|
+
const width = window.innerWidth;
|
|
246
|
+
if (width <= 480) return 'breakpoint-sm';
|
|
247
|
+
if (width <= 768) return 'breakpoint-md';
|
|
248
|
+
if (width <= 1440) return 'breakpoint-base';
|
|
249
|
+
return 'breakpoint-lg';
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const [activeBreakpoint, setActiveBreakpoint] = React.useState(getActiveBreakpoint());
|
|
253
|
+
|
|
254
|
+
React.useEffect(() => {
|
|
255
|
+
const handleResize = () => {
|
|
256
|
+
setActiveBreakpoint(getActiveBreakpoint());
|
|
257
|
+
};
|
|
258
|
+
window.addEventListener('resize', handleResize);
|
|
259
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
260
|
+
}, []);
|
|
261
|
+
|
|
262
|
+
const activeBP = BREAKPOINTS.find((bp) => bp.key === activeBreakpoint);
|
|
263
|
+
|
|
264
|
+
return (
|
|
265
|
+
<div className="space-y-6 p-6">
|
|
266
|
+
<div>
|
|
267
|
+
<Typography variant="h4" className="color-gray-900 mb-2">
|
|
268
|
+
Breakpoint Activo
|
|
269
|
+
</Typography>
|
|
270
|
+
<Typography variant="body-md" className="color-gray-600 mb-4">
|
|
271
|
+
Cambia el tamaño del viewport desde el panel de controles de Storybook (icono de dispositivo móvil en la barra superior) o redimensiona la ventana del navegador.
|
|
272
|
+
</Typography>
|
|
273
|
+
</div>
|
|
274
|
+
|
|
275
|
+
<div className="bg-gradient-to-r from-green-50 to-blue-50 rounded-lg p-6 border-2 border-green-200">
|
|
276
|
+
<div className="flex items-center gap-4 mb-4">
|
|
277
|
+
<div className="w-16 h-16 bg-green-600 rounded-lg flex items-center justify-center">
|
|
278
|
+
<Typography variant="h5" className="color-white font-bold">
|
|
279
|
+
{activeBP?.name.charAt(0)}
|
|
280
|
+
</Typography>
|
|
281
|
+
</div>
|
|
282
|
+
<div>
|
|
283
|
+
<Typography variant="h5" className="color-gray-900 mb-1">
|
|
284
|
+
{activeBP?.name}
|
|
285
|
+
</Typography>
|
|
286
|
+
<Typography variant="body-sm" className="color-gray-600">
|
|
287
|
+
Ancho actual: <strong>{typeof window !== 'undefined' ? window.innerWidth : 0}px</strong>
|
|
288
|
+
</Typography>
|
|
289
|
+
</div>
|
|
290
|
+
</div>
|
|
291
|
+
<div className="bg-white rounded p-4 border border-gray-200">
|
|
292
|
+
<Typography variant="body-sm" className="color-gray-700 mb-2">
|
|
293
|
+
<strong>Variable CSS:</strong>
|
|
294
|
+
</Typography>
|
|
295
|
+
<code className="text-body-sm bg-gray-100 px-3 py-2 rounded block color-gray-900">
|
|
296
|
+
{activeBP?.key}
|
|
297
|
+
</code>
|
|
298
|
+
<Typography variant="body-sm" className="color-gray-700 mt-3 mb-2">
|
|
299
|
+
<strong>Rango:</strong> {activeBP?.range}
|
|
300
|
+
</Typography>
|
|
301
|
+
</div>
|
|
302
|
+
</div>
|
|
303
|
+
|
|
304
|
+
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
305
|
+
{BREAKPOINTS.map((bp) => (
|
|
306
|
+
<div
|
|
307
|
+
key={bp.key}
|
|
308
|
+
className={`p-4 rounded-lg border-2 ${
|
|
309
|
+
bp.key === activeBreakpoint
|
|
310
|
+
? 'border-green-600 bg-green-50'
|
|
311
|
+
: 'border-gray-200 bg-white'
|
|
312
|
+
}`}
|
|
313
|
+
>
|
|
314
|
+
<Typography
|
|
315
|
+
variant="body-sm"
|
|
316
|
+
className={`font-medium mb-1 ${
|
|
317
|
+
bp.key === activeBreakpoint ? 'color-green-900' : 'color-gray-900'
|
|
318
|
+
}`}
|
|
319
|
+
>
|
|
320
|
+
{bp.name}
|
|
321
|
+
</Typography>
|
|
322
|
+
<Typography variant="caption" className="color-gray-600">
|
|
323
|
+
{bp.range}
|
|
324
|
+
</Typography>
|
|
325
|
+
{bp.key === activeBreakpoint && (
|
|
326
|
+
<div className="mt-2">
|
|
327
|
+
<span className="text-body-sm color-green-600 font-semibold">✓ Activo</span>
|
|
328
|
+
</div>
|
|
329
|
+
)}
|
|
330
|
+
</div>
|
|
331
|
+
))}
|
|
332
|
+
</div>
|
|
333
|
+
|
|
334
|
+
<div className="bg-blue-50 rounded-lg p-6 border border-blue-200">
|
|
335
|
+
<Typography variant="h6" className="color-blue-900 mb-3">
|
|
336
|
+
💡 Cómo probar:
|
|
337
|
+
</Typography>
|
|
338
|
+
<ul className="space-y-2 text-body-sm color-gray-700">
|
|
339
|
+
<li>1. Usa el selector de viewport en la barra superior de Storybook</li>
|
|
340
|
+
<li>2. O redimensiona manualmente la ventana del navegador</li>
|
|
341
|
+
<li>3. Observa cómo cambia el breakpoint activo arriba</li>
|
|
342
|
+
</ul>
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
);
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { Icon } from '../../Atoms/Icon/Icon';
|
|
3
|
+
import { Button } from '../../Atoms/Button/Button';
|
|
4
|
+
|
|
5
|
+
export const Drawer = ({
|
|
6
|
+
isOpen = false,
|
|
7
|
+
onClose,
|
|
8
|
+
title,
|
|
9
|
+
children,
|
|
10
|
+
position = 'right',
|
|
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 positionClasses = {
|
|
30
|
+
left: 'left-0',
|
|
31
|
+
right: 'right-0',
|
|
32
|
+
top: 'top-0 left-0 right-0',
|
|
33
|
+
bottom: 'bottom-0 left-0 right-0',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const sizeClasses = {
|
|
37
|
+
small: position === 'left' || position === 'right' ? 'w-80' : 'h-64',
|
|
38
|
+
medium: position === 'left' || position === 'right' ? 'w-96' : 'h-96',
|
|
39
|
+
large: position === 'left' || position === 'right' ? 'w-[32rem]' : 'h-[32rem]',
|
|
40
|
+
full: position === 'left' || position === 'right' ? 'w-screen' : 'h-screen',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<>
|
|
45
|
+
<div
|
|
46
|
+
className="fixed inset-0 z-40 bg-gray-500 bg-opacity-75 transition-opacity"
|
|
47
|
+
onClick={onClose}
|
|
48
|
+
/>
|
|
49
|
+
<div
|
|
50
|
+
className={`fixed z-50 bg-white shadow-xl ${positionClasses[position]} ${sizeClasses[size]} ${className}`}
|
|
51
|
+
{...props}
|
|
52
|
+
>
|
|
53
|
+
{title && (
|
|
54
|
+
<div className="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
|
|
55
|
+
<h3 className="text-lg font-semibold text-gray-900">{title}</h3>
|
|
56
|
+
{showCloseButton && (
|
|
57
|
+
<button
|
|
58
|
+
onClick={onClose}
|
|
59
|
+
className="text-gray-400 hover:text-gray-500"
|
|
60
|
+
>
|
|
61
|
+
<Icon name="XMarkIcon" className="h-6 w-6" />
|
|
62
|
+
</button>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
)}
|
|
66
|
+
<div className="p-6 overflow-y-auto" style={{ maxHeight: 'calc(100vh - 80px)' }}>
|
|
67
|
+
{children}
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export default Drawer;
|
|
75
|
+
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Drawer } from './Drawer';
|
|
3
|
+
import { Button } from '../../Atoms/Button/Button';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: 'Layout/Drawer',
|
|
7
|
+
component: Drawer,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
argTypes: {
|
|
10
|
+
position: {
|
|
11
|
+
control: 'select',
|
|
12
|
+
options: ['left', 'right', 'top', 'bottom'],
|
|
13
|
+
},
|
|
14
|
+
size: {
|
|
15
|
+
control: 'select',
|
|
16
|
+
options: ['small', 'medium', 'large', 'full'],
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const Right = {
|
|
22
|
+
render: () => {
|
|
23
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
24
|
+
return (
|
|
25
|
+
<>
|
|
26
|
+
<Button onClick={() => setIsOpen(true)}>Abrir Drawer Derecha</Button>
|
|
27
|
+
<Drawer
|
|
28
|
+
isOpen={isOpen}
|
|
29
|
+
onClose={() => setIsOpen(false)}
|
|
30
|
+
title="Drawer desde la Derecha"
|
|
31
|
+
position="right"
|
|
32
|
+
>
|
|
33
|
+
<p>Este es el contenido del drawer que se abre desde la derecha.</p>
|
|
34
|
+
</Drawer>
|
|
35
|
+
</>
|
|
36
|
+
);
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const Left = {
|
|
41
|
+
render: () => {
|
|
42
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
43
|
+
return (
|
|
44
|
+
<>
|
|
45
|
+
<Button onClick={() => setIsOpen(true)}>Abrir Drawer Izquierda</Button>
|
|
46
|
+
<Drawer
|
|
47
|
+
isOpen={isOpen}
|
|
48
|
+
onClose={() => setIsOpen(false)}
|
|
49
|
+
title="Drawer desde la Izquierda"
|
|
50
|
+
position="left"
|
|
51
|
+
>
|
|
52
|
+
<p>Este es el contenido del drawer que se abre desde la izquierda.</p>
|
|
53
|
+
</Drawer>
|
|
54
|
+
</>
|
|
55
|
+
);
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const Large = {
|
|
60
|
+
render: () => {
|
|
61
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
62
|
+
return (
|
|
63
|
+
<>
|
|
64
|
+
<Button onClick={() => setIsOpen(true)}>Abrir Drawer Grande</Button>
|
|
65
|
+
<Drawer
|
|
66
|
+
isOpen={isOpen}
|
|
67
|
+
onClose={() => setIsOpen(false)}
|
|
68
|
+
title="Drawer Grande"
|
|
69
|
+
size="large"
|
|
70
|
+
>
|
|
71
|
+
<p>Este es un drawer de tamaño grande con más espacio.</p>
|
|
72
|
+
</Drawer>
|
|
73
|
+
</>
|
|
74
|
+
);
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { useState, useRef, useEffect } from 'react';
|
|
2
|
+
import { Icon } from '../../Atoms/Icon/Icon';
|
|
3
|
+
|
|
4
|
+
export const Dropdown = ({
|
|
5
|
+
trigger,
|
|
6
|
+
items = [],
|
|
7
|
+
position = 'bottom-left',
|
|
8
|
+
className = '',
|
|
9
|
+
...props
|
|
10
|
+
}) => {
|
|
11
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
12
|
+
const dropdownRef = useRef(null);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const handleClickOutside = (event) => {
|
|
16
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
17
|
+
setIsOpen(false);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
if (isOpen) {
|
|
22
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return () => {
|
|
26
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
27
|
+
};
|
|
28
|
+
}, [isOpen]);
|
|
29
|
+
|
|
30
|
+
const positionClasses = {
|
|
31
|
+
'bottom-left': 'top-full left-0 mt-1',
|
|
32
|
+
'bottom-right': 'top-full right-0 mt-1',
|
|
33
|
+
'top-left': 'bottom-full left-0 mb-1',
|
|
34
|
+
'top-right': 'bottom-full right-0 mb-1',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div className={`relative inline-block ${className}`} ref={dropdownRef} {...props}>
|
|
39
|
+
<div onClick={() => setIsOpen(!isOpen)}>
|
|
40
|
+
{trigger || (
|
|
41
|
+
<button className="p-2 rounded-lg hover:bg-gray-100">
|
|
42
|
+
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
43
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 12.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 18.75a.75.75 0 110-1.5.75.75 0 010 1.5z" />
|
|
44
|
+
</svg>
|
|
45
|
+
</button>
|
|
46
|
+
)}
|
|
47
|
+
</div>
|
|
48
|
+
{isOpen && (
|
|
49
|
+
<div
|
|
50
|
+
className={`absolute z-50 w-56 bg-white rounded-lg shadow-lg border border-gray-200 py-1 ${positionClasses[position]}`}
|
|
51
|
+
>
|
|
52
|
+
{items.map((item, index) => (
|
|
53
|
+
<button
|
|
54
|
+
key={index}
|
|
55
|
+
onClick={() => {
|
|
56
|
+
if (item.onClick) {
|
|
57
|
+
item.onClick();
|
|
58
|
+
}
|
|
59
|
+
setIsOpen(false);
|
|
60
|
+
}}
|
|
61
|
+
className={`
|
|
62
|
+
w-full text-left px-4 py-2 text-sm
|
|
63
|
+
${item.danger ? 'text-red-600 hover:bg-red-50' : 'text-gray-700 hover:bg-gray-100'}
|
|
64
|
+
${item.disabled ? 'opacity-50 cursor-not-allowed' : ''}
|
|
65
|
+
`}
|
|
66
|
+
disabled={item.disabled}
|
|
67
|
+
>
|
|
68
|
+
<div className="flex items-center">
|
|
69
|
+
{item.icon && (
|
|
70
|
+
<Icon name={item.icon} className="mr-3 h-5 w-5" />
|
|
71
|
+
)}
|
|
72
|
+
{item.label}
|
|
73
|
+
</div>
|
|
74
|
+
</button>
|
|
75
|
+
))}
|
|
76
|
+
</div>
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export default Dropdown;
|
|
83
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Dropdown } from './Dropdown';
|
|
2
|
+
import { Button } from '../../Atoms/Button/Button';
|
|
3
|
+
import { Icon } from '../../Atoms/Icon/Icon';
|
|
4
|
+
|
|
5
|
+
const dropdownItems = [
|
|
6
|
+
{ label: 'Editar', icon: 'Cog6ToothIcon', onClick: () => alert('Editar') },
|
|
7
|
+
{ label: 'Duplicar', icon: 'DocumentIcon', onClick: () => alert('Duplicar') },
|
|
8
|
+
{ label: 'Compartir', icon: 'UserIcon', onClick: () => alert('Compartir') },
|
|
9
|
+
{ label: 'Eliminar', icon: 'XMarkIcon', danger: true, onClick: () => alert('Eliminar') },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
title: 'Layout/Dropdown',
|
|
14
|
+
component: Dropdown,
|
|
15
|
+
tags: ['autodocs'],
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const Default = {
|
|
19
|
+
args: {
|
|
20
|
+
items: dropdownItems,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const WithCustomTrigger = {
|
|
25
|
+
args: {
|
|
26
|
+
trigger: (
|
|
27
|
+
<Button variant="outline">
|
|
28
|
+
Opciones <Icon name="ChevronDownIcon" className="ml-2 h-4 w-4" />
|
|
29
|
+
</Button>
|
|
30
|
+
),
|
|
31
|
+
items: dropdownItems,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const WithDisabledItem = {
|
|
36
|
+
args: {
|
|
37
|
+
items: [
|
|
38
|
+
...dropdownItems,
|
|
39
|
+
{ label: 'Deshabilitado', disabled: true },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const Simple = {
|
|
45
|
+
args: {
|
|
46
|
+
items: [
|
|
47
|
+
{ label: 'Opción 1', onClick: () => alert('Opción 1') },
|
|
48
|
+
{ label: 'Opción 2', onClick: () => alert('Opción 2') },
|
|
49
|
+
{ label: 'Opción 3', onClick: () => alert('Opción 3') },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const Grid = ({
|
|
2
|
+
columns = 2,
|
|
3
|
+
variant = 'mobile',
|
|
4
|
+
children,
|
|
5
|
+
className = '',
|
|
6
|
+
...props
|
|
7
|
+
}) => {
|
|
8
|
+
const gridClasses = {
|
|
9
|
+
mobile: {
|
|
10
|
+
2: 'grid-mobile-2',
|
|
11
|
+
3: 'grid-mobile-3',
|
|
12
|
+
4: 'grid-mobile-4',
|
|
13
|
+
8: 'grid-mobile-8',
|
|
14
|
+
},
|
|
15
|
+
desktop: {
|
|
16
|
+
2: 'grid-desktop-2',
|
|
17
|
+
3: 'grid-desktop-3',
|
|
18
|
+
4: 'grid-desktop-4',
|
|
19
|
+
8: 'grid-desktop-8',
|
|
20
|
+
},
|
|
21
|
+
'desktop-lg': {
|
|
22
|
+
12: 'grid-desktop-lg-12',
|
|
23
|
+
16: 'grid-desktop-lg-16',
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const variantClasses = gridClasses[variant] || gridClasses.mobile;
|
|
28
|
+
const gridClass = variantClasses[columns] || variantClasses[2] || variantClasses[12];
|
|
29
|
+
const classes = `${gridClass} ${className}`.trim();
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className={classes} {...props}>
|
|
33
|
+
{children}
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default Grid;
|
|
39
|
+
|