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,64 @@
|
|
|
1
|
+
import { Timeline } from './Timeline';
|
|
2
|
+
|
|
3
|
+
const timelineItems = [
|
|
4
|
+
{
|
|
5
|
+
date: '15 de Enero 2024',
|
|
6
|
+
title: 'Pedido creado',
|
|
7
|
+
description: 'El pedido #12345 ha sido creado exitosamente.',
|
|
8
|
+
icon: 'CheckCircleIcon',
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
date: '16 de Enero 2024',
|
|
12
|
+
title: 'Pedido confirmado',
|
|
13
|
+
description: 'El pedido ha sido confirmado y está en proceso.',
|
|
14
|
+
icon: 'CheckCircleIcon',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
date: '17 de Enero 2024',
|
|
18
|
+
title: 'En tránsito',
|
|
19
|
+
description: 'El pedido ha sido enviado y está en camino.',
|
|
20
|
+
icon: 'CheckCircleIcon',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
date: '18 de Enero 2024',
|
|
24
|
+
title: 'Entregado',
|
|
25
|
+
description: 'El pedido ha sido entregado exitosamente.',
|
|
26
|
+
icon: 'CheckCircleIcon',
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
export default {
|
|
31
|
+
title: 'DataDisplay/Timeline',
|
|
32
|
+
component: Timeline,
|
|
33
|
+
tags: ['autodocs'],
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const Default = {
|
|
37
|
+
args: {
|
|
38
|
+
items: timelineItems,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const Simple = {
|
|
43
|
+
args: {
|
|
44
|
+
items: [
|
|
45
|
+
{
|
|
46
|
+
date: 'Hoy',
|
|
47
|
+
title: 'Evento 1',
|
|
48
|
+
description: 'Descripción del primer evento',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
date: 'Ayer',
|
|
52
|
+
title: 'Evento 2',
|
|
53
|
+
description: 'Descripción del segundo evento',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const WithoutIcons = {
|
|
60
|
+
args: {
|
|
61
|
+
items: timelineItems.map(({ icon, ...item }) => item),
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const Checkbox = ({
|
|
2
|
+
label,
|
|
3
|
+
checked = false,
|
|
4
|
+
disabled = false,
|
|
5
|
+
onChange,
|
|
6
|
+
className = '',
|
|
7
|
+
...props
|
|
8
|
+
}) => {
|
|
9
|
+
return (
|
|
10
|
+
<label className={`flex items-center ${disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'} ${className}`}>
|
|
11
|
+
<input
|
|
12
|
+
type="checkbox"
|
|
13
|
+
checked={checked}
|
|
14
|
+
disabled={disabled}
|
|
15
|
+
onChange={onChange}
|
|
16
|
+
className="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
{label && (
|
|
20
|
+
<span className="ml-2 text-sm text-gray-700">{label}</span>
|
|
21
|
+
)}
|
|
22
|
+
</label>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default Checkbox;
|
|
27
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Checkbox } from './Checkbox';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Inputs/Checkbox',
|
|
6
|
+
component: Checkbox,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const Default = {
|
|
11
|
+
render: () => {
|
|
12
|
+
const [checked, setChecked] = useState(false);
|
|
13
|
+
return (
|
|
14
|
+
<Checkbox
|
|
15
|
+
label="Accept terms and conditions"
|
|
16
|
+
checked={checked}
|
|
17
|
+
onChange={(e) => setChecked(e.target.checked)}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const Checked = {
|
|
24
|
+
args: {
|
|
25
|
+
label: 'Checked by default',
|
|
26
|
+
checked: true,
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const Disabled = {
|
|
31
|
+
args: {
|
|
32
|
+
label: 'Disabled checkbox',
|
|
33
|
+
checked: false,
|
|
34
|
+
disabled: true,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const DisabledChecked = {
|
|
39
|
+
args: {
|
|
40
|
+
label: 'Disabled and checked',
|
|
41
|
+
checked: true,
|
|
42
|
+
disabled: true,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const WithoutLabel = {
|
|
47
|
+
args: {
|
|
48
|
+
checked: false,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useTranslation } from 'react-i18next';
|
|
2
|
+
import { Icon } from '../../Atoms/Icon/Icon';
|
|
3
|
+
|
|
4
|
+
export const DatePicker = ({
|
|
5
|
+
label,
|
|
6
|
+
value,
|
|
7
|
+
error,
|
|
8
|
+
helperText,
|
|
9
|
+
disabled = false,
|
|
10
|
+
required = false,
|
|
11
|
+
className = '',
|
|
12
|
+
...props
|
|
13
|
+
}) => {
|
|
14
|
+
const { t } = useTranslation();
|
|
15
|
+
|
|
16
|
+
const inputClasses = `
|
|
17
|
+
w-full px-3 py-2 pl-10 border rounded-lg
|
|
18
|
+
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
|
|
19
|
+
disabled:bg-gray-100 disabled:cursor-not-allowed
|
|
20
|
+
${error ? 'border-red-500' : 'border-gray-300'}
|
|
21
|
+
${className}
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className="w-full">
|
|
26
|
+
{label && (
|
|
27
|
+
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
28
|
+
{label}
|
|
29
|
+
{required && <span className="text-red-500 ml-1">*</span>}
|
|
30
|
+
</label>
|
|
31
|
+
)}
|
|
32
|
+
<div className="relative">
|
|
33
|
+
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
34
|
+
<Icon name="CalendarIcon" className="h-5 w-5 text-gray-400" />
|
|
35
|
+
</div>
|
|
36
|
+
<input
|
|
37
|
+
type="date"
|
|
38
|
+
value={value}
|
|
39
|
+
disabled={disabled}
|
|
40
|
+
className={inputClasses}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
44
|
+
{error && (
|
|
45
|
+
<p className="mt-1 text-sm text-red-600">{error}</p>
|
|
46
|
+
)}
|
|
47
|
+
{helperText && !error && (
|
|
48
|
+
<p className="mt-1 text-sm text-gray-500">{helperText}</p>
|
|
49
|
+
)}
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default DatePicker;
|
|
55
|
+
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { DatePicker } from './DatePicker';
|
|
3
|
+
import { I18nProvider } from '../../../providers/I18nProvider';
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
title: 'Inputs/DatePicker',
|
|
7
|
+
component: DatePicker,
|
|
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 [value, setValue] = useState('');
|
|
21
|
+
return (
|
|
22
|
+
<DatePicker
|
|
23
|
+
label="Fecha de nacimiento"
|
|
24
|
+
value={value}
|
|
25
|
+
onChange={(e) => setValue(e.target.value)}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const WithError = {
|
|
32
|
+
args: {
|
|
33
|
+
label: 'Fecha',
|
|
34
|
+
error: 'Por favor selecciona una fecha válida',
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const Disabled = {
|
|
39
|
+
args: {
|
|
40
|
+
label: 'Fecha deshabilitada',
|
|
41
|
+
value: '2024-01-01',
|
|
42
|
+
disabled: true,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const Required = {
|
|
47
|
+
args: {
|
|
48
|
+
label: 'Fecha requerida',
|
|
49
|
+
required: true,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Icon } from '../../Atoms/Icon/Icon';
|
|
4
|
+
|
|
5
|
+
export const FileUpload = ({
|
|
6
|
+
label,
|
|
7
|
+
accept,
|
|
8
|
+
multiple = false,
|
|
9
|
+
error,
|
|
10
|
+
helperText,
|
|
11
|
+
disabled = false,
|
|
12
|
+
required = false,
|
|
13
|
+
onFileChange,
|
|
14
|
+
className = '',
|
|
15
|
+
...props
|
|
16
|
+
}) => {
|
|
17
|
+
const { t } = useTranslation();
|
|
18
|
+
const [files, setFiles] = useState([]);
|
|
19
|
+
|
|
20
|
+
const handleChange = (e) => {
|
|
21
|
+
const selectedFiles = Array.from(e.target.files);
|
|
22
|
+
setFiles(selectedFiles);
|
|
23
|
+
if (onFileChange) {
|
|
24
|
+
onFileChange(selectedFiles);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const removeFile = (index) => {
|
|
29
|
+
const newFiles = files.filter((_, i) => i !== index);
|
|
30
|
+
setFiles(newFiles);
|
|
31
|
+
if (onFileChange) {
|
|
32
|
+
onFileChange(newFiles);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div className={`w-full ${className}`}>
|
|
38
|
+
{label && (
|
|
39
|
+
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
40
|
+
{label}
|
|
41
|
+
{required && <span className="text-red-500 ml-1">*</span>}
|
|
42
|
+
</label>
|
|
43
|
+
)}
|
|
44
|
+
<div className="mt-1 flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-lg hover:border-gray-400 transition-colors">
|
|
45
|
+
<div className="space-y-1 text-center">
|
|
46
|
+
<Icon name="CloudArrowUpIcon" className="mx-auto h-12 w-12 text-gray-400" />
|
|
47
|
+
<div className="flex text-sm text-gray-600">
|
|
48
|
+
<label
|
|
49
|
+
htmlFor="file-upload"
|
|
50
|
+
className={`relative cursor-pointer rounded-md font-medium ${
|
|
51
|
+
disabled ? 'text-gray-400 cursor-not-allowed' : 'text-blue-600 hover:text-blue-500'
|
|
52
|
+
} focus-within:outline-none focus-within:ring-2 focus-within:ring-blue-500`}
|
|
53
|
+
>
|
|
54
|
+
<span>{multiple ? 'Sube archivos' : 'Sube un archivo'}</span>
|
|
55
|
+
<input
|
|
56
|
+
id="file-upload"
|
|
57
|
+
name="file-upload"
|
|
58
|
+
type="file"
|
|
59
|
+
className="sr-only"
|
|
60
|
+
accept={accept}
|
|
61
|
+
multiple={multiple}
|
|
62
|
+
disabled={disabled}
|
|
63
|
+
onChange={handleChange}
|
|
64
|
+
{...props}
|
|
65
|
+
/>
|
|
66
|
+
</label>
|
|
67
|
+
<p className="pl-1">o arrastra y suelta</p>
|
|
68
|
+
</div>
|
|
69
|
+
<p className="text-xs text-gray-500">PNG, JPG, GIF hasta 10MB</p>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
{files.length > 0 && (
|
|
73
|
+
<div className="mt-4 space-y-2">
|
|
74
|
+
{files.map((file, index) => (
|
|
75
|
+
<div
|
|
76
|
+
key={index}
|
|
77
|
+
className="flex items-center justify-between p-2 bg-gray-50 rounded-lg"
|
|
78
|
+
>
|
|
79
|
+
<div className="flex items-center space-x-2">
|
|
80
|
+
<Icon name="DocumentIcon" className="h-5 w-5 text-gray-400" />
|
|
81
|
+
<span className="text-sm text-gray-700">{file.name}</span>
|
|
82
|
+
<span className="text-xs text-gray-500">
|
|
83
|
+
({(file.size / 1024).toFixed(2)} KB)
|
|
84
|
+
</span>
|
|
85
|
+
</div>
|
|
86
|
+
<button
|
|
87
|
+
type="button"
|
|
88
|
+
onClick={() => removeFile(index)}
|
|
89
|
+
className="text-red-600 hover:text-red-800"
|
|
90
|
+
>
|
|
91
|
+
<Icon name="XMarkIcon" className="h-5 w-5" />
|
|
92
|
+
</button>
|
|
93
|
+
</div>
|
|
94
|
+
))}
|
|
95
|
+
</div>
|
|
96
|
+
)}
|
|
97
|
+
{error && (
|
|
98
|
+
<p className="mt-1 text-sm text-red-600">{error}</p>
|
|
99
|
+
)}
|
|
100
|
+
{helperText && !error && (
|
|
101
|
+
<p className="mt-1 text-sm text-gray-500">{helperText}</p>
|
|
102
|
+
)}
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export default FileUpload;
|
|
108
|
+
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { FileUpload } from './FileUpload';
|
|
2
|
+
import { I18nProvider } from '../../../providers/I18nProvider';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Inputs/FileUpload',
|
|
6
|
+
component: FileUpload,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
decorators: [
|
|
9
|
+
(Story) => (
|
|
10
|
+
<I18nProvider>
|
|
11
|
+
<Story />
|
|
12
|
+
</I18nProvider>
|
|
13
|
+
),
|
|
14
|
+
],
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const Default = {
|
|
18
|
+
args: {
|
|
19
|
+
label: 'Subir archivo',
|
|
20
|
+
accept: 'image/*',
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const Multiple = {
|
|
25
|
+
args: {
|
|
26
|
+
label: 'Subir múltiples archivos',
|
|
27
|
+
accept: 'image/*',
|
|
28
|
+
multiple: true,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const WithError = {
|
|
33
|
+
args: {
|
|
34
|
+
label: 'Subir archivo',
|
|
35
|
+
error: 'El archivo es demasiado grande',
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const Disabled = {
|
|
40
|
+
args: {
|
|
41
|
+
label: 'Subir archivo',
|
|
42
|
+
disabled: true,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const Required = {
|
|
47
|
+
args: {
|
|
48
|
+
label: 'Subir archivo requerido',
|
|
49
|
+
required: true,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useTranslation } from 'react-i18next';
|
|
2
|
+
|
|
3
|
+
export const Input = ({
|
|
4
|
+
label,
|
|
5
|
+
placeholder,
|
|
6
|
+
type = 'text',
|
|
7
|
+
error,
|
|
8
|
+
helperText,
|
|
9
|
+
disabled = false,
|
|
10
|
+
required = false,
|
|
11
|
+
className = '',
|
|
12
|
+
...props
|
|
13
|
+
}) => {
|
|
14
|
+
const { t } = useTranslation();
|
|
15
|
+
|
|
16
|
+
const inputClasses = `
|
|
17
|
+
w-full px-3 py-2 border rounded-lg
|
|
18
|
+
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
|
|
19
|
+
disabled:bg-gray-100 disabled:cursor-not-allowed
|
|
20
|
+
${error ? 'border-red-500' : 'border-gray-300'}
|
|
21
|
+
${className}
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className="w-full">
|
|
26
|
+
{label && (
|
|
27
|
+
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
28
|
+
{label}
|
|
29
|
+
{required && <span className="text-red-500 ml-1">*</span>}
|
|
30
|
+
</label>
|
|
31
|
+
)}
|
|
32
|
+
<input
|
|
33
|
+
type={type}
|
|
34
|
+
placeholder={placeholder || t('placeholder.search')}
|
|
35
|
+
disabled={disabled}
|
|
36
|
+
className={inputClasses}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
{error && (
|
|
40
|
+
<p className="mt-1 text-sm text-red-600">{error}</p>
|
|
41
|
+
)}
|
|
42
|
+
{helperText && !error && (
|
|
43
|
+
<p className="mt-1 text-sm text-gray-500">{helperText}</p>
|
|
44
|
+
)}
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default Input;
|
|
50
|
+
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Input } from './Input';
|
|
2
|
+
import { I18nProvider } from '../../../providers/I18nProvider';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Inputs/Input',
|
|
6
|
+
component: Input,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
decorators: [
|
|
9
|
+
(Story) => (
|
|
10
|
+
<I18nProvider>
|
|
11
|
+
<Story />
|
|
12
|
+
</I18nProvider>
|
|
13
|
+
),
|
|
14
|
+
],
|
|
15
|
+
argTypes: {
|
|
16
|
+
type: {
|
|
17
|
+
control: 'select',
|
|
18
|
+
options: ['text', 'email', 'password', 'number', 'tel', 'url'],
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const Default = {
|
|
24
|
+
args: {
|
|
25
|
+
label: 'Label',
|
|
26
|
+
placeholder: 'Enter text...',
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const WithError = {
|
|
31
|
+
args: {
|
|
32
|
+
label: 'Email',
|
|
33
|
+
type: 'email',
|
|
34
|
+
placeholder: 'Enter your email',
|
|
35
|
+
error: 'Please enter a valid email address',
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const WithHelperText = {
|
|
40
|
+
args: {
|
|
41
|
+
label: 'Password',
|
|
42
|
+
type: 'password',
|
|
43
|
+
placeholder: 'Enter password',
|
|
44
|
+
helperText: 'Must be at least 8 characters',
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const Disabled = {
|
|
49
|
+
args: {
|
|
50
|
+
label: 'Disabled Input',
|
|
51
|
+
placeholder: 'This input is disabled',
|
|
52
|
+
disabled: true,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const Required = {
|
|
57
|
+
args: {
|
|
58
|
+
label: 'Required Field',
|
|
59
|
+
placeholder: 'This field is required',
|
|
60
|
+
required: true,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const RadioButton = ({
|
|
2
|
+
label,
|
|
3
|
+
name,
|
|
4
|
+
value,
|
|
5
|
+
checked = false,
|
|
6
|
+
disabled = false,
|
|
7
|
+
onChange,
|
|
8
|
+
className = '',
|
|
9
|
+
...props
|
|
10
|
+
}) => {
|
|
11
|
+
return (
|
|
12
|
+
<label className={`flex items-center ${disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'} ${className}`}>
|
|
13
|
+
<input
|
|
14
|
+
type="radio"
|
|
15
|
+
name={name}
|
|
16
|
+
value={value}
|
|
17
|
+
checked={checked}
|
|
18
|
+
disabled={disabled}
|
|
19
|
+
onChange={onChange}
|
|
20
|
+
className="w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500"
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
{label && (
|
|
24
|
+
<span className="ml-2 text-sm text-gray-700">{label}</span>
|
|
25
|
+
)}
|
|
26
|
+
</label>
|
|
27
|
+
);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default RadioButton;
|
|
31
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { RadioButton } from './RadioButton';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Inputs/RadioButton',
|
|
6
|
+
component: RadioButton,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const Default = {
|
|
11
|
+
render: () => {
|
|
12
|
+
const [selected, setSelected] = useState('option1');
|
|
13
|
+
return (
|
|
14
|
+
<div className="space-y-2">
|
|
15
|
+
<RadioButton
|
|
16
|
+
name="options"
|
|
17
|
+
value="option1"
|
|
18
|
+
label="Option 1"
|
|
19
|
+
checked={selected === 'option1'}
|
|
20
|
+
onChange={(e) => setSelected(e.target.value)}
|
|
21
|
+
/>
|
|
22
|
+
<RadioButton
|
|
23
|
+
name="options"
|
|
24
|
+
value="option2"
|
|
25
|
+
label="Option 2"
|
|
26
|
+
checked={selected === 'option2'}
|
|
27
|
+
onChange={(e) => setSelected(e.target.value)}
|
|
28
|
+
/>
|
|
29
|
+
<RadioButton
|
|
30
|
+
name="options"
|
|
31
|
+
value="option3"
|
|
32
|
+
label="Option 3"
|
|
33
|
+
checked={selected === 'option3'}
|
|
34
|
+
onChange={(e) => setSelected(e.target.value)}
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const Single = {
|
|
42
|
+
args: {
|
|
43
|
+
name: 'single',
|
|
44
|
+
value: 'value1',
|
|
45
|
+
label: 'Single Radio Button',
|
|
46
|
+
checked: true,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const Disabled = {
|
|
51
|
+
args: {
|
|
52
|
+
name: 'disabled',
|
|
53
|
+
value: 'value1',
|
|
54
|
+
label: 'Disabled Radio Button',
|
|
55
|
+
checked: false,
|
|
56
|
+
disabled: true,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useTranslation } from 'react-i18next';
|
|
2
|
+
|
|
3
|
+
export const Select = ({
|
|
4
|
+
label,
|
|
5
|
+
options = [],
|
|
6
|
+
value,
|
|
7
|
+
placeholder,
|
|
8
|
+
error,
|
|
9
|
+
helperText,
|
|
10
|
+
disabled = false,
|
|
11
|
+
required = false,
|
|
12
|
+
className = '',
|
|
13
|
+
...props
|
|
14
|
+
}) => {
|
|
15
|
+
const { t } = useTranslation();
|
|
16
|
+
|
|
17
|
+
const selectClasses = `
|
|
18
|
+
w-full px-3 py-2 border rounded-lg
|
|
19
|
+
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
|
|
20
|
+
disabled:bg-gray-100 disabled:cursor-not-allowed
|
|
21
|
+
${error ? 'border-red-500' : 'border-gray-300'}
|
|
22
|
+
${className}
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className="w-full">
|
|
27
|
+
{label && (
|
|
28
|
+
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
29
|
+
{label}
|
|
30
|
+
{required && <span className="text-red-500 ml-1">*</span>}
|
|
31
|
+
</label>
|
|
32
|
+
)}
|
|
33
|
+
<select
|
|
34
|
+
value={value}
|
|
35
|
+
disabled={disabled}
|
|
36
|
+
className={selectClasses}
|
|
37
|
+
{...props}
|
|
38
|
+
>
|
|
39
|
+
{placeholder && (
|
|
40
|
+
<option value="">{placeholder || t('placeholder.select')}</option>
|
|
41
|
+
)}
|
|
42
|
+
{options.map((option) => (
|
|
43
|
+
<option key={option.value} value={option.value}>
|
|
44
|
+
{option.label}
|
|
45
|
+
</option>
|
|
46
|
+
))}
|
|
47
|
+
</select>
|
|
48
|
+
{error && (
|
|
49
|
+
<p className="mt-1 text-sm text-red-600">{error}</p>
|
|
50
|
+
)}
|
|
51
|
+
{helperText && !error && (
|
|
52
|
+
<p className="mt-1 text-sm text-gray-500">{helperText}</p>
|
|
53
|
+
)}
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default Select;
|
|
59
|
+
|