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,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
+