@soyfri/shared-library 2.0.0-beta.2 → 2.0.0-beta.4

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 (187) hide show
  1. package/.dockerignore +8 -0
  2. package/.github/workflows/publish.yml +107 -0
  3. package/.prettierrc +3 -0
  4. package/.storybook/main.ts +19 -0
  5. package/.storybook/preview.ts +14 -0
  6. package/.storybook/vitest.setup.ts +9 -0
  7. package/Dockerfile +37 -0
  8. package/build.js +102 -0
  9. package/chromatic.config.json +5 -0
  10. package/cleanDirectories.js +40 -0
  11. package/dist/README.md +243 -0
  12. package/dist/components/Icon/Icon.js +1 -1
  13. package/dist/components/Table/Table.js +1 -1
  14. package/dist/index.cjs +24 -0
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.js +7 -1
  17. package/dist/mui.d.ts +1 -0
  18. package/dist/package.json +197 -0
  19. package/package.json +4 -32
  20. package/rollup.config.cjs +87 -0
  21. package/src/components/ActionMenu/ActionMenu.stories.tsx +230 -0
  22. package/src/components/ActionMenu/ActionMenu.tsx +174 -0
  23. package/src/components/ActionMenu/index.ts +2 -0
  24. package/src/components/AppBar/AppBar.stories.tsx +272 -0
  25. package/src/components/AppBar/AppBar.sx.ts +32 -0
  26. package/src/components/AppBar/AppBar.tsx +123 -0
  27. package/src/components/AppBar/AppBarBrand.tsx +120 -0
  28. package/src/components/AppBar/AppBarContext.ts +25 -0
  29. package/src/components/AppBar/AppBarMenuToggle.tsx +90 -0
  30. package/src/components/AppBar/AppBarUserMenu.tsx +217 -0
  31. package/src/components/AppBar/index.ts +25 -0
  32. package/src/components/Autocomplete/Autocomplete.definitions.ts +477 -0
  33. package/src/components/Autocomplete/Autocomplete.helpers.ts +60 -0
  34. package/src/components/Autocomplete/Autocomplete.stories.tsx +748 -0
  35. package/src/components/Autocomplete/Autocomplete.sx.ts +30 -0
  36. package/src/components/Autocomplete/Autocomplete.tsx +361 -0
  37. package/src/components/Autocomplete/Autocomplete.types.ts +13 -0
  38. package/src/components/Autocomplete/_parts/AutocompleteChips.tsx +55 -0
  39. package/src/components/Autocomplete/_parts/AutocompleteLoader.tsx +17 -0
  40. package/src/components/Autocomplete/_parts/AutocompleteOption.tsx +31 -0
  41. package/src/components/Autocomplete/index.ts +12 -0
  42. package/src/components/Avatar/Avatar.definitions.ts +162 -0
  43. package/src/components/Avatar/Avatar.stories.tsx +258 -0
  44. package/src/components/Avatar/Avatar.tsx +206 -0
  45. package/src/components/Avatar/index.ts +1 -0
  46. package/src/components/Button/Button.definition.ts +97 -0
  47. package/src/components/Button/Button.stories.tsx +285 -0
  48. package/src/components/Button/Button.tsx +67 -0
  49. package/src/components/Button/index.ts +1 -0
  50. package/src/components/Card/Card.definition.ts +5 -0
  51. package/src/components/Card/Card.stories.tsx +221 -0
  52. package/src/components/Card/Card.sx.ts +104 -0
  53. package/src/components/Card/Card.tsx +200 -0
  54. package/src/components/Card/index.ts +9 -0
  55. package/src/components/Chip/Chip.definitions.ts +167 -0
  56. package/src/components/Chip/Chip.stories.tsx +265 -0
  57. package/src/components/Chip/Chip.tsx +61 -0
  58. package/src/components/Chip/index.ts +1 -0
  59. package/src/components/Column/Column.tsx +29 -0
  60. package/src/components/Column/index.ts +1 -0
  61. package/src/components/DatePicker/DatePicker.definitions.ts +228 -0
  62. package/src/components/DatePicker/DatePicker.helpers.ts +24 -0
  63. package/src/components/DatePicker/DatePicker.stories.tsx +309 -0
  64. package/src/components/DatePicker/DatePicker.sx.ts +33 -0
  65. package/src/components/DatePicker/DatePicker.tsx +189 -0
  66. package/src/components/DatePicker/DatePicker.types.ts +10 -0
  67. package/src/components/DatePicker/index.ts +9 -0
  68. package/src/components/DateRangePicker/DateRangePicker.definitions.ts +191 -0
  69. package/src/components/DateRangePicker/DateRangePicker.stories.tsx +252 -0
  70. package/src/components/DateRangePicker/DateRangePicker.tsx +56 -0
  71. package/src/components/DateRangePicker/index.ts +1 -0
  72. package/src/components/DateTimePicker/DateTimePicker.definitions.ts +256 -0
  73. package/src/components/DateTimePicker/DateTimePicker.helpers.ts +38 -0
  74. package/src/components/DateTimePicker/DateTimePicker.stories.tsx +418 -0
  75. package/src/components/DateTimePicker/DateTimePicker.sx.ts +30 -0
  76. package/src/components/DateTimePicker/DateTimePicker.tsx +225 -0
  77. package/src/components/DateTimePicker/DateTimePicker.types.ts +10 -0
  78. package/src/components/DateTimePicker/index.ts +9 -0
  79. package/src/components/Drawer/Drawer.stories.tsx +270 -0
  80. package/src/components/Drawer/Drawer.sx.ts +106 -0
  81. package/src/components/Drawer/Drawer.tsx +214 -0
  82. package/src/components/Drawer/DrawerContext.ts +26 -0
  83. package/src/components/Drawer/DrawerItem.tsx +110 -0
  84. package/src/components/Drawer/index.ts +10 -0
  85. package/src/components/Flyout/Flyout.stories.tsx +282 -0
  86. package/src/components/Flyout/Flyout.tsx +122 -0
  87. package/src/components/Flyout/index.ts +1 -0
  88. package/src/components/Gallery/Gallery.definition.tsx +37 -0
  89. package/src/components/Gallery/Gallery.stories.tsx +82 -0
  90. package/src/components/Gallery/Gallery.tsx +118 -0
  91. package/src/components/Gallery/GalleryLightbox.tsx +170 -0
  92. package/src/components/Gallery/GalleryMain.tsx +84 -0
  93. package/src/components/Gallery/GalleryThumbnails.tsx +106 -0
  94. package/src/components/Gallery/index.ts +1 -0
  95. package/src/components/Icon/Icon.stories.tsx +121 -0
  96. package/src/components/Icon/Icon.tsx +175 -0
  97. package/src/components/Icon/index.ts +2 -0
  98. package/src/components/Input/Input.definitions.ts +324 -0
  99. package/src/components/Input/Input.helpers.ts +49 -0
  100. package/src/components/Input/Input.stories.tsx +499 -0
  101. package/src/components/Input/Input.sx.ts +42 -0
  102. package/src/components/Input/Input.tsx +141 -0
  103. package/src/components/Input/Input.types.ts +10 -0
  104. package/src/components/Input/index.ts +9 -0
  105. package/src/components/InputGroup/InputGroup.definitions.ts +158 -0
  106. package/src/components/InputGroup/InputGroup.stories.tsx +267 -0
  107. package/src/components/InputGroup/InputGroup.tsx +179 -0
  108. package/src/components/InputGroup/index.ts +1 -0
  109. package/src/components/MenuButton/MenuButton.stories.tsx +197 -0
  110. package/src/components/MenuButton/MenuButton.tsx +100 -0
  111. package/src/components/MenuButton/index.ts +1 -0
  112. package/src/components/Modal/Modal.stories.tsx +721 -0
  113. package/src/components/Modal/Modal.tsx +355 -0
  114. package/src/components/Modal/ModalBody.tsx +16 -0
  115. package/src/components/Modal/ModalFooter.tsx +71 -0
  116. package/src/components/Modal/ModalHeader.tsx +18 -0
  117. package/src/components/Modal/index.ts +6 -0
  118. package/src/components/PageLoader/PageLoader.stories.tsx +217 -0
  119. package/src/components/PageLoader/PageLoader.tsx +96 -0
  120. package/src/components/PageLoader/index.ts +2 -0
  121. package/src/components/ScrollTopButton/ScrollTopButton.stories.tsx +158 -0
  122. package/src/components/ScrollTopButton/ScrollTopButton.tsx +135 -0
  123. package/src/components/ScrollTopButton/index.ts +8 -0
  124. package/src/components/ScrollTopButton/scrollToTop.ts +37 -0
  125. package/src/components/Select/Select.definitions.ts +602 -0
  126. package/src/components/Select/Select.helpers.ts +71 -0
  127. package/src/components/Select/Select.stories.tsx +687 -0
  128. package/src/components/Select/Select.sx.ts +14 -0
  129. package/src/components/Select/Select.tsx +429 -0
  130. package/src/components/Select/Select.types.ts +15 -0
  131. package/src/components/Select/_parts/SelectMenuItem.tsx +40 -0
  132. package/src/components/Select/_parts/SelectSearchHeader.tsx +51 -0
  133. package/src/components/Select/_parts/SelectValue.tsx +96 -0
  134. package/src/components/Select/index.ts +14 -0
  135. package/src/components/Stat/Stat.stories.tsx +85 -0
  136. package/src/components/Stat/Stat.tsx +117 -0
  137. package/src/components/Stat/index.ts +2 -0
  138. package/src/components/StatusMessage/StatusMessage.stories.tsx +130 -0
  139. package/src/components/StatusMessage/StatusMessage.tsx +162 -0
  140. package/src/components/StatusMessage/index.ts +2 -0
  141. package/src/components/Stepper/Step.tsx +21 -0
  142. package/src/components/Stepper/Stepper.definition.ts +75 -0
  143. package/src/components/Stepper/Stepper.stories.tsx +122 -0
  144. package/src/components/Stepper/Stepper.tsx +75 -0
  145. package/src/components/Stepper/index.ts +2 -0
  146. package/src/components/Table/EmptyTable.png +0 -0
  147. package/src/components/Table/Table.definition.ts +580 -0
  148. package/src/components/Table/Table.stories.tsx +853 -0
  149. package/src/components/Table/Table.tsx +495 -0
  150. package/src/components/Table/data.ts +134 -0
  151. package/src/components/Table/exportsUtils.ts +195 -0
  152. package/src/components/Table/index.ts +3 -0
  153. package/src/components/Table/types.ts +34 -0
  154. package/src/components/Tabs/Tab.definition.ts +53 -0
  155. package/src/components/Tabs/Tab.tsx +19 -0
  156. package/src/components/Tabs/Tabs.stories.tsx +118 -0
  157. package/src/components/Tabs/Tabs.tsx +99 -0
  158. package/src/components/Tabs/_tabUtils.tsx +4 -0
  159. package/src/components/Tabs/index.ts +2 -0
  160. package/src/components/Timeline/Timeline.definition.ts +43 -0
  161. package/src/components/Timeline/Timeline.stories.tsx +108 -0
  162. package/src/components/Timeline/Timeline.tsx +49 -0
  163. package/src/components/Timeline/TimelineItem.tsx +31 -0
  164. package/src/components/Timeline/index.ts +2 -0
  165. package/src/components/Tooltip/Tooltip.stories.tsx +129 -0
  166. package/src/components/Tooltip/Tooltip.tsx +58 -0
  167. package/src/components/Tooltip/index.ts +1 -0
  168. package/src/components/_shared/formField.sx.ts +118 -0
  169. package/src/components/_shared/resolvePreset.ts +35 -0
  170. package/src/hooks/ClipBoard/ClipBoard.stories.tsx +168 -0
  171. package/src/hooks/ClipBoard/ClipBoard.tsx +131 -0
  172. package/src/hooks/ClipBoard/ClipboardUnifiedDemo.tsx +111 -0
  173. package/src/hooks/ClipBoard/index.ts +1 -0
  174. package/src/hooks/Wizard/Wizard.stories.tsx +301 -0
  175. package/src/hooks/Wizard/WizardContext.tsx +166 -0
  176. package/src/hooks/Wizard/index.ts +6 -0
  177. package/src/hooks/Wizard/useWizard.ts +13 -0
  178. package/src/index.ts +17 -0
  179. package/src/mui.ts +54 -0
  180. package/src/styles.css +3 -0
  181. package/src/theme/componentStyles.ts +47 -0
  182. package/src/theme/tokens.ts +43 -0
  183. package/tailwind.config.js +10 -0
  184. package/tsconfig.json +48 -0
  185. package/tsup.config.js +41 -0
  186. package/vite.config.js +132 -0
  187. package/vitest.config.ts +35 -0
@@ -0,0 +1,175 @@
1
+ import React, { type ReactNode, useMemo } from 'react';
2
+ import { Badge, styled } from '@mui/material';
3
+
4
+ export type IconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
5
+
6
+ export interface IconProps {
7
+ /**
8
+ * Contenido icono. Modo legacy: acepta un elemento MUI o SVG ya armado.
9
+ * Si se usa junto con `path` o `url`, `children` tiene prioridad.
10
+ */
11
+ children?: ReactNode;
12
+ /**
13
+ * Ruta SVG inline (el contenido del atributo `d` de un `<path>`). Útil para
14
+ * los iconos custom de Metronic que vienen como strings de path.
15
+ *
16
+ * ```tsx
17
+ * <Icon path="M12 2L2 22h20L12 2z" />
18
+ * ```
19
+ *
20
+ * Si el string contiene el tag `<svg>` completo (no solo el `d`), también
21
+ * se soporta y se inyecta tal cual via `dangerouslySetInnerHTML`.
22
+ */
23
+ path?: string;
24
+ /**
25
+ * URL a un archivo `.svg` (asset local o remoto). Se renderiza vía `<img>`.
26
+ *
27
+ * ```tsx
28
+ * <Icon url="/assets/icons/custom-warning.svg" />
29
+ * ```
30
+ */
31
+ url?: string;
32
+ /**
33
+ * Alt text cuando se usa `url`. Default: `"icon"`.
34
+ */
35
+ alt?: string;
36
+ /**
37
+ * ViewBox cuando se usa `path`. Default: `"0 0 24 24"`. Ignorado si `path`
38
+ * ya viene como `<svg>` completo.
39
+ */
40
+ viewBox?: string;
41
+ color?: string;
42
+ outlined?: boolean;
43
+ spin?: boolean;
44
+ badge?: string | number;
45
+ badgeColor?: 'primary' | 'secondary' | 'error';
46
+ badgePosition?: {
47
+ vertical: 'top' | 'bottom';
48
+ horizontal: 'right' | 'left';
49
+ };
50
+ badgeSize?: 'small' | 'medium' | 'large';
51
+ size?: IconSize;
52
+ black?: boolean;
53
+ white?: boolean;
54
+ className?: string;
55
+ }
56
+
57
+ const sizeMap: Record<IconSize, number> = {
58
+ xs: 18,
59
+ sm: 24,
60
+ md: 36,
61
+ lg: 48,
62
+ xl: 64,
63
+ };
64
+
65
+ const StyledIcon = styled('span')<{
66
+ color?: string;
67
+ fontSize?: number;
68
+ spin?: boolean;
69
+ outlined?: boolean;
70
+ }>(({ color, fontSize, spin, outlined }) => ({
71
+ display: 'inline-flex',
72
+ alignItems: 'center',
73
+ justifyContent: 'center',
74
+ color: color,
75
+ fontSize: fontSize,
76
+ width: fontSize,
77
+ height: fontSize,
78
+ animation: spin ? 'spin 1s linear infinite' : undefined,
79
+ border: outlined ? '1px solid currentColor' : undefined,
80
+ borderRadius: '50%',
81
+ padding: outlined ? 4 : 0,
82
+ '@keyframes spin': {
83
+ '0%': { transform: 'rotate(0deg)' },
84
+ '100%': { transform: 'rotate(360deg)' },
85
+ },
86
+ '& svg': {
87
+ width: '100%',
88
+ height: '100%',
89
+ fill: 'currentColor',
90
+ display: 'block',
91
+ },
92
+ '& img': {
93
+ width: '100%',
94
+ height: '100%',
95
+ display: 'block',
96
+ },
97
+ }));
98
+
99
+ export const Icon: React.FC<IconProps> = ({
100
+ children,
101
+ path,
102
+ url,
103
+ alt = 'icon',
104
+ viewBox = '0 0 24 24',
105
+ color = 'var(--primary-color)',
106
+ outlined = false,
107
+ spin = false,
108
+ badge,
109
+ badgeColor = 'primary',
110
+ badgePosition = { vertical: 'bottom', horizontal: 'right' },
111
+ size = 'sm',
112
+ black,
113
+ white,
114
+ className,
115
+ }) => {
116
+ const finalColor = useMemo(() => {
117
+ if (white) return 'white';
118
+ if (black) return 'black';
119
+ return color;
120
+ }, [color, white, black]);
121
+
122
+ const iconSize = sizeMap[size];
123
+
124
+ // Resolver el contenido según la prop activa.
125
+ const content = useMemo(() => {
126
+ if (children) return children;
127
+
128
+ if (path) {
129
+ const trimmed = path.trim();
130
+ // Si es un SVG completo (empieza con <svg), lo inyectamos tal cual.
131
+ if (trimmed.startsWith('<svg')) {
132
+ return <span dangerouslySetInnerHTML={{ __html: trimmed }} />;
133
+ }
134
+ // Si no, asumimos que es el atributo `d` de un <path>.
135
+ return (
136
+ <svg
137
+ xmlns="http://www.w3.org/2000/svg"
138
+ viewBox={viewBox}
139
+ fill="currentColor"
140
+ aria-hidden="true"
141
+ >
142
+ <path d={trimmed} />
143
+ </svg>
144
+ );
145
+ }
146
+
147
+ if (url) {
148
+ return <img src={url} alt={alt} />;
149
+ }
150
+
151
+ return null;
152
+ }, [children, path, url, alt, viewBox]);
153
+
154
+ return (
155
+ <Badge
156
+ badgeContent={badge}
157
+ color={badgeColor}
158
+ anchorOrigin={badgePosition}
159
+ invisible={badge === undefined}
160
+ sx={{ '.MuiBadge-badge': { transform: 'scale(1)' } }}
161
+ >
162
+ <StyledIcon
163
+ color={finalColor}
164
+ fontSize={iconSize}
165
+ spin={spin}
166
+ outlined={outlined}
167
+ className={`hsi-icon ${className || ''}`}
168
+ >
169
+ {content}
170
+ </StyledIcon>
171
+ </Badge>
172
+ );
173
+ };
174
+
175
+ export default Icon;
@@ -0,0 +1,2 @@
1
+ import '../../styles.css';
2
+ export { default as Icon } from './Icon'
@@ -0,0 +1,324 @@
1
+ // Input.definitions.ts
2
+
3
+ export const BasicTextInputDefinition = `
4
+ import React, { useState } from 'react';
5
+ import { Input } from './Input'; // Ajusta la ruta si es necesario
6
+ import { Box, Typography } from '@mui/material';
7
+
8
+ export const BasicTextInputExample = () => {
9
+ const [value, setValue] = useState('Texto de ejemplo');
10
+ return (
11
+ <Box sx={{ width: 300 }}>
12
+ <Input
13
+ label="Nombre"
14
+ value={value}
15
+ onChange={setValue}
16
+ />
17
+ <Typography sx={{ mt: 2 }}>Valor actual: {value}</Typography>
18
+ </Box>
19
+ );
20
+ };
21
+ `;
22
+
23
+ export const NumberInputDefinition = `
24
+ import React, { useState } from 'react';
25
+ import { Input } from './Input'; // Ajusta la ruta si es necesario
26
+ import { Box, Typography } from '@mui/material';
27
+
28
+ export const NumberInputExample = () => {
29
+ const [value, setValue] = useState(123);
30
+ return (
31
+ <Box sx={{ width: 300 }}>
32
+ <Input
33
+ label="Cantidad"
34
+ type="number"
35
+ value={value}
36
+ onChange={setValue}
37
+ />
38
+ <Typography sx={{ mt: 2 }}>Valor actual: {value}</Typography>
39
+ </Box>
40
+ );
41
+ };
42
+ `;
43
+
44
+ export const EmailInputDefinition = `
45
+ import React, { useState } from 'react';
46
+ import { Input } from './Input'; // Ajusta la ruta si es necesario
47
+ import { Box, Typography } from '@mui/material';
48
+
49
+ export const EmailInputExample = () => {
50
+ const [value, setValue] = useState('ejemplo@dominio.com');
51
+ return (
52
+ <Box sx={{ width: 300 }}>
53
+ <Input
54
+ label="Correo Electrónico"
55
+ type="email"
56
+ value={value}
57
+ onChange={setValue}
58
+ />
59
+ <Typography sx={{ mt: 2 }}>Valor actual: {value}</Typography>
60
+ </Box>
61
+ );
62
+ };
63
+ `;
64
+
65
+ export const PasswordInputDefinition = `
66
+ import React, { useState } from 'react';
67
+ import { Input } from './Input'; // Ajusta la ruta si es necesario
68
+ import { Box, Typography } from '@mui/material';
69
+
70
+ export const PasswordInputExample = () => {
71
+ const [value, setValue] = useState('micontraseña');
72
+ return (
73
+ <Box sx={{ width: 300 }}>
74
+ <Input
75
+ label="Contraseña"
76
+ type="password"
77
+ value={value}
78
+ onChange={setValue}
79
+ />
80
+ <Typography sx={{ mt: 2 }}>Valor actual: {value}</Typography>
81
+ </Box>
82
+ );
83
+ };
84
+ `;
85
+
86
+ export const InputWithPlaceholderDefinition = `
87
+ import React, { useState } from 'react';
88
+ import { Input } from './Input'; // Ajusta la ruta si es necesario
89
+ import { Box, Typography } from '@mui/material';
90
+
91
+ export const InputWithPlaceholderExample = () => {
92
+ const [value, setValue] = useState('');
93
+ return (
94
+ <Box sx={{ width: 300 }}>
95
+ <Input
96
+ label="Búsqueda"
97
+ placeholder="Escribe tu término de búsqueda..."
98
+ value={value}
99
+ onChange={setValue}
100
+ />
101
+ <Typography sx={{ mt: 2 }}>Valor actual: {value || 'Vacío'}</Typography>
102
+ </Box>
103
+ );
104
+ };
105
+ `;
106
+
107
+ export const InputWithErrorDefinition = `
108
+ import React, { useState } from 'react';
109
+ import { Input } from './Input'; // Ajusta la ruta si es necesario
110
+ import { Box, Typography } from '@mui/material';
111
+
112
+ export const InputWithErrorExample = () => {
113
+ const [value, setValue] = useState('invalido');
114
+ const hasError = value.length < 5 && value.length > 0;
115
+ return (
116
+ <Box sx={{ width: 300 }}>
117
+ <Input
118
+ label="Nombre de usuario"
119
+ value={value}
120
+ onChange={setValue}
121
+ error={hasError}
122
+ helperText={hasError ? 'Mínimo 5 caracteres' : ''}
123
+ />
124
+ <Typography sx={{ mt: 2 }}>Valor actual: {value}</Typography>
125
+ </Box>
126
+ );
127
+ };
128
+ `;
129
+
130
+ export const DisabledInputDefinition = `
131
+ import React, { useState } from 'react';
132
+ import { Input } from './Input'; // Ajusta la ruta si es necesario
133
+ import { Box, Typography } from '@mui/material';
134
+
135
+ export const DisabledInputExample = () => {
136
+ const [value, setValue] = useState('Campo deshabilitado');
137
+ return (
138
+ <Box sx={{ width: 300 }}>
139
+ <Input
140
+ label="Estado"
141
+ value={value}
142
+ onChange={setValue}
143
+ disabled
144
+ />
145
+ <Typography sx={{ mt: 2 }}>Valor actual: {value}</Typography>
146
+ </Box>
147
+ );
148
+ };
149
+ `;
150
+
151
+ export const ReadOnlyInputDefinition = `
152
+ import React, { useState } from 'react';
153
+ import { Input } from './Input'; // Ajusta la ruta si es necesario
154
+ import { Box, Typography } from '@mui/material';
155
+
156
+ export const ReadOnlyInputExample = () => {
157
+ const [value, setValue] = useState('Valor de solo lectura');
158
+ return (
159
+ <Box sx={{ width: 300 }}>
160
+ <Input
161
+ label="Información"
162
+ value={value}
163
+ onChange={setValue}
164
+ readOnly
165
+ />
166
+ <Typography sx={{ mt: 2 }}>Valor actual: {value}</Typography>
167
+ </Box>
168
+ );
169
+ };
170
+ `;
171
+
172
+ export const InputVariantsDefinition = `
173
+ import React, { useState } from 'react';
174
+ import { Input } from './Input'; // Ajusta la ruta si es necesario
175
+ import { Box, Typography } from '@mui/material';
176
+
177
+ export const InputVariantsExample = () => {
178
+ const [valueOutlined, setValueOutlined] = useState('Outlined');
179
+ const [valueFilled, setValueFilled] = useState('Filled');
180
+ const [valueStandard, setValueStandard] = useState('Standard');
181
+ return (
182
+ <Box sx={{ display: 'flex', flexDirection: 'column', gap: 3, width: 300 }}>
183
+ <Input
184
+ label="Esquema Outlined"
185
+ value={valueOutlined}
186
+ onChange={setValueOutlined}
187
+ variant="outlined"
188
+ />
189
+ <Input
190
+ label="Esquema Filled"
191
+ value={valueFilled}
192
+ onChange={setValueFilled}
193
+ variant="filled"
194
+ />
195
+ <Input
196
+ label="Esquema Standard"
197
+ value={valueStandard}
198
+ onChange={setValueStandard}
199
+ variant="standard"
200
+ />
201
+ </Box>
202
+ );
203
+ };
204
+ `;
205
+
206
+ export const InputSizesDefinition = `
207
+ import React, { useState } from 'react';
208
+ import { Input } from './Input'; // Ajusta la ruta si es necesario
209
+ import { Box, Typography } from '@mui/material';
210
+
211
+ export const InputSizesExample = () => {
212
+ const [valueMedium, setValueMedium] = useState('Tamaño Mediano');
213
+ const [valueSmall, setValueSmall] = useState('Tamaño Pequeño');
214
+ return (
215
+ <Box sx={{ display: 'flex', flexDirection: 'column', gap: 3, width: 300 }}>
216
+ <Input
217
+ label="Input Mediano"
218
+ value={valueMedium}
219
+ onChange={setValueMedium}
220
+ size="medium"
221
+ />
222
+ <Input
223
+ label="Input Pequeño"
224
+ value={valueSmall}
225
+ onChange={setValueSmall}
226
+ size="small"
227
+ />
228
+ </Box>
229
+ );
230
+ };
231
+ `;
232
+
233
+ export const FullWidthInputDefinition = `
234
+ import React, { useState } from 'react';
235
+ import { Input } from './Input'; // Ajusta la ruta si es necesario
236
+ import { Box, Typography } from '@mui/material';
237
+
238
+ export const FullWidthInputExample = () => {
239
+ const [value, setValue] = useState('Ocupa todo el ancho');
240
+ return (
241
+ <Box sx={{ width: '100%' }}>
242
+ <Input
243
+ label="Input de Ancho Completo"
244
+ value={value}
245
+ onChange={setValue}
246
+ fullWidth
247
+ />
248
+ <Typography sx={{ mt: 2 }}>Valor actual: {value}</Typography>
249
+ </Box>
250
+ );
251
+ };
252
+ `;
253
+
254
+ export const LabelPositionFloatingDefinition = `
255
+ import React, { useState } from 'react';
256
+ import { Input } from './Input'; // Ajusta la ruta si es necesario
257
+ import { Box } from '@mui/material';
258
+
259
+ export const LabelPositionExample = () => {
260
+ const [outside, setOutside] = useState('Outside (default)');
261
+ const [floating, setFloating] = useState('Floating');
262
+ return (
263
+ <Box sx={{ display: 'flex', flexDirection: 'column', gap: 3, width: 300 }}>
264
+ <Input
265
+ label="Label outside"
266
+ value={outside}
267
+ onChange={setOutside}
268
+ labelPosition="outside"
269
+ />
270
+ <Input
271
+ label="Label floating (MUI clásico)"
272
+ value={floating}
273
+ onChange={setFloating}
274
+ labelPosition="floating"
275
+ />
276
+ </Box>
277
+ );
278
+ };
279
+ `;
280
+
281
+ export const CustomBorderRadiusDefinition = `
282
+ import React, { useState } from 'react';
283
+ import { Input } from './Input'; // Ajusta la ruta si es necesario
284
+ import { Box } from '@mui/material';
285
+
286
+ export const CustomBorderRadiusExample = () => {
287
+ const [v, setV] = useState('Texto');
288
+ return (
289
+ <Box sx={{ display: 'flex', flexDirection: 'column', gap: 3, width: 300 }}>
290
+ <Input label="Sin radius" value={v} onChange={setV} borderRadius={0} />
291
+ <Input label="Radius 4px" value={v} onChange={setV} borderRadius={4} />
292
+ <Input label="Default (10px)" value={v} onChange={setV} />
293
+ <Input label="Pill (24px)" value={v} onChange={setV} borderRadius={24} />
294
+ </Box>
295
+ );
296
+ };
297
+ `;
298
+
299
+ export const CustomStylingDefinition = `
300
+ import React, { useState } from 'react';
301
+ import { Input } from './Input'; // Ajusta la ruta si es necesario
302
+ import { Box } from '@mui/material';
303
+
304
+ export const CustomStylingExample = () => {
305
+ const [v, setV] = useState('Custom completo');
306
+ return (
307
+ <Box sx={{ width: 300 }}>
308
+ <Input
309
+ label="Combinado: bg + borderColor + label color"
310
+ value={v}
311
+ onChange={setV}
312
+ sx={{
313
+ '& .MuiInputLabel-root': { color: 'primary.main' },
314
+ '& .MuiOutlinedInput-root': { bgcolor: '#fff7e6' },
315
+ '& .MuiOutlinedInput-notchedOutline': {
316
+ borderColor: 'warning.main',
317
+ borderWidth: 2,
318
+ },
319
+ }}
320
+ />
321
+ </Box>
322
+ );
323
+ };
324
+ `;
@@ -0,0 +1,49 @@
1
+ import type { InputHTMLAttributes } from 'react';
2
+ import type { TextFieldProps } from '@mui/material';
3
+ import type { InputType, LabelPosition } from './Input';
4
+
5
+ /**
6
+ * Convierte el valor raw del evento al tipo apropiado.
7
+ * Para type="number" intenta parsear a número; si falla, devuelve el raw
8
+ * (permite estados intermedios como "" o "-" mientras el usuario tipea).
9
+ */
10
+ export const parseValue = (raw: string, type: InputType): string | number => {
11
+ if (type !== 'number') return raw;
12
+ if (raw === '' || raw === '-') return raw;
13
+ const n = parseFloat(raw);
14
+ return isNaN(n) ? raw : n;
15
+ };
16
+
17
+ /**
18
+ * Construye los slotProps del TextField consolidando htmlInput + inputLabel.
19
+ * Maneja los atributos min/max cuando type="number" y readOnly.
20
+ */
21
+ export const buildSlotProps = (
22
+ type: InputType,
23
+ min: number | undefined,
24
+ max: number | undefined,
25
+ customSlotProps: TextFieldProps['slotProps'],
26
+ customInputProps: InputHTMLAttributes<HTMLInputElement> | undefined,
27
+ _labelPosition: LabelPosition,
28
+ readOnly?: boolean,
29
+ ): TextFieldProps['slotProps'] => {
30
+ const numberAttrs =
31
+ type === 'number'
32
+ ? { min: min ?? Number.NEGATIVE_INFINITY, ...(max !== undefined && { max }) }
33
+ : {};
34
+
35
+ return {
36
+ ...customSlotProps,
37
+ htmlInput: {
38
+ ...customInputProps,
39
+ ...(customSlotProps as any)?.htmlInput,
40
+ ...numberAttrs,
41
+ ...(readOnly && { readOnly: true }),
42
+ },
43
+ // Dejamos que MUI maneje el shrink nativamente (animación inside → up).
44
+ // El consumidor puede forzar shrink con slotProps.inputLabel.shrink=true si lo necesita.
45
+ inputLabel: {
46
+ ...(customSlotProps as any)?.inputLabel,
47
+ },
48
+ };
49
+ };