@soyfri/shared-library 1.5.0 → 2.0.0-beta.1

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 (284) hide show
  1. package/build.js +75 -38
  2. package/dist/components/ActionMenu/ActionMenu.cjs +107 -0
  3. package/dist/components/ActionMenu/ActionMenu.cjs.map +1 -0
  4. package/dist/components/ActionMenu/ActionMenu.d.ts +60 -0
  5. package/dist/components/ActionMenu/ActionMenu.js +107 -0
  6. package/dist/components/ActionMenu/ActionMenu.js.map +1 -0
  7. package/dist/components/ActionMenu/index.d.ts +2 -0
  8. package/dist/components/ActionMenu.d.ts +6 -0
  9. package/dist/components/AppBar/AppBar.cjs +346 -0
  10. package/dist/components/AppBar/AppBar.cjs.map +1 -0
  11. package/dist/components/AppBar/AppBar.d.ts +55 -0
  12. package/dist/components/AppBar/AppBar.js +346 -0
  13. package/dist/components/AppBar/AppBar.js.map +1 -0
  14. package/dist/components/AppBar/AppBar.sx.d.ts +12 -0
  15. package/dist/components/AppBar/AppBarBrand.d.ts +31 -0
  16. package/dist/components/AppBar/AppBarContext.d.ts +18 -0
  17. package/dist/components/AppBar/AppBarMenuToggle.d.ts +39 -0
  18. package/dist/components/AppBar/AppBarUserMenu.d.ts +65 -0
  19. package/dist/components/AppBar/index.d.ts +12 -0
  20. package/dist/components/AppBar.d.ts +6 -0
  21. package/dist/components/Autocomplete/Autocomplete.cjs +259 -54
  22. package/dist/components/Autocomplete/Autocomplete.cjs.map +1 -1
  23. package/dist/components/Autocomplete/Autocomplete.d.ts +64 -9
  24. package/dist/components/Autocomplete/Autocomplete.definitions.d.ts +6 -0
  25. package/dist/components/Autocomplete/Autocomplete.helpers.d.ts +18 -0
  26. package/dist/components/Autocomplete/Autocomplete.js +261 -56
  27. package/dist/components/Autocomplete/Autocomplete.js.map +1 -1
  28. package/dist/components/Autocomplete/Autocomplete.sx.d.ts +7 -0
  29. package/dist/components/Autocomplete/Autocomplete.types.d.ts +1 -0
  30. package/dist/components/Autocomplete/_parts/AutocompleteChips.d.ts +19 -0
  31. package/dist/components/Autocomplete/_parts/AutocompleteLoader.d.ts +9 -0
  32. package/dist/components/Autocomplete/_parts/AutocompleteOption.d.ts +19 -0
  33. package/dist/components/Autocomplete/index.d.ts +2 -1
  34. package/dist/components/Autocomplete.d.ts +4 -0
  35. package/dist/components/Avatar/Avatar.cjs +116 -79
  36. package/dist/components/Avatar/Avatar.cjs.map +1 -1
  37. package/dist/components/Avatar/Avatar.d.ts +16 -2
  38. package/dist/components/Avatar/Avatar.definitions.d.ts +11 -0
  39. package/dist/components/Avatar/Avatar.js +117 -80
  40. package/dist/components/Avatar/Avatar.js.map +1 -1
  41. package/dist/components/Card/Card.cjs +168 -9
  42. package/dist/components/Card/Card.cjs.map +1 -1
  43. package/dist/components/Card/Card.d.ts +78 -8
  44. package/dist/components/Card/Card.js +170 -11
  45. package/dist/components/Card/Card.js.map +1 -1
  46. package/dist/components/Card/Card.sx.d.ts +17 -0
  47. package/dist/components/Card/index.d.ts +4 -1
  48. package/dist/components/Card.d.ts +4 -0
  49. package/dist/components/DatePicker/DatePicker.cjs +201 -3
  50. package/dist/components/DatePicker/DatePicker.cjs.map +1 -1
  51. package/dist/components/DatePicker/DatePicker.d.ts +47 -9
  52. package/dist/components/DatePicker/DatePicker.definitions.d.ts +1 -0
  53. package/dist/components/DatePicker/DatePicker.helpers.d.ts +7 -0
  54. package/dist/components/DatePicker/DatePicker.js +200 -2
  55. package/dist/components/DatePicker/DatePicker.js.map +1 -1
  56. package/dist/components/DatePicker/DatePicker.sx.d.ts +9 -0
  57. package/dist/components/DatePicker/DatePicker.types.d.ts +1 -0
  58. package/dist/components/DatePicker/index.d.ts +2 -1
  59. package/dist/components/DatePicker.d.ts +4 -0
  60. package/dist/components/DateTimePicker/DateTimePicker.cjs +152 -138
  61. package/dist/components/DateTimePicker/DateTimePicker.cjs.map +1 -1
  62. package/dist/components/DateTimePicker/DateTimePicker.d.ts +46 -9
  63. package/dist/components/DateTimePicker/DateTimePicker.definitions.d.ts +1 -0
  64. package/dist/components/DateTimePicker/DateTimePicker.helpers.d.ts +11 -0
  65. package/dist/components/DateTimePicker/DateTimePicker.js +152 -138
  66. package/dist/components/DateTimePicker/DateTimePicker.js.map +1 -1
  67. package/dist/components/DateTimePicker/DateTimePicker.sx.d.ts +7 -0
  68. package/dist/components/DateTimePicker/DateTimePicker.types.d.ts +1 -0
  69. package/dist/components/DateTimePicker/index.d.ts +2 -1
  70. package/dist/components/DateTimePicker.d.ts +4 -0
  71. package/dist/components/Drawer/Drawer.cjs +271 -0
  72. package/dist/components/Drawer/Drawer.cjs.map +1 -0
  73. package/dist/components/Drawer/Drawer.d.ts +51 -0
  74. package/dist/components/Drawer/Drawer.js +271 -0
  75. package/dist/components/Drawer/Drawer.js.map +1 -0
  76. package/dist/components/Drawer/Drawer.sx.d.ts +23 -0
  77. package/dist/components/Drawer/DrawerContext.d.ts +18 -0
  78. package/dist/components/Drawer/DrawerItem.d.ts +35 -0
  79. package/dist/components/Drawer/index.d.ts +6 -0
  80. package/dist/components/Drawer.d.ts +6 -0
  81. package/dist/components/Icon/Icon.cjs +44 -3
  82. package/dist/components/Icon/Icon.cjs.map +1 -1
  83. package/dist/components/Icon/Icon.d.ts +34 -1
  84. package/dist/components/Icon/Icon.js +44 -3
  85. package/dist/components/Icon/Icon.js.map +1 -1
  86. package/dist/components/Input/Input.cjs +173 -3
  87. package/dist/components/Input/Input.cjs.map +1 -1
  88. package/dist/components/Input/Input.d.ts +20 -15
  89. package/dist/components/Input/Input.definitions.d.ts +5 -2
  90. package/dist/components/Input/Input.helpers.d.ts +14 -0
  91. package/dist/components/Input/Input.js +172 -2
  92. package/dist/components/Input/Input.js.map +1 -1
  93. package/dist/components/Input/Input.sx.d.ts +8 -0
  94. package/dist/components/Input/Input.types.d.ts +1 -0
  95. package/dist/components/Input/index.d.ts +2 -1
  96. package/dist/components/Input.d.ts +4 -0
  97. package/dist/components/InputGroup/InputGroup.cjs +104 -91
  98. package/dist/components/InputGroup/InputGroup.cjs.map +1 -1
  99. package/dist/components/InputGroup/InputGroup.d.ts +37 -1
  100. package/dist/components/InputGroup/InputGroup.definitions.d.ts +6 -0
  101. package/dist/components/InputGroup/InputGroup.js +106 -93
  102. package/dist/components/InputGroup/InputGroup.js.map +1 -1
  103. package/dist/components/Modal/Modal.cjs +226 -116
  104. package/dist/components/Modal/Modal.cjs.map +1 -1
  105. package/dist/components/Modal/Modal.d.ts +38 -2
  106. package/dist/components/Modal/Modal.js +227 -117
  107. package/dist/components/Modal/Modal.js.map +1 -1
  108. package/dist/components/Modal/ModalFooter.d.ts +9 -1
  109. package/dist/components/Modal/index.d.ts +5 -0
  110. package/dist/components/PageLoader/PageLoader.cjs +61 -0
  111. package/dist/components/PageLoader/PageLoader.cjs.map +1 -0
  112. package/dist/components/PageLoader/PageLoader.d.ts +38 -0
  113. package/dist/components/PageLoader/PageLoader.js +61 -0
  114. package/dist/components/PageLoader/PageLoader.js.map +1 -0
  115. package/dist/components/PageLoader/index.d.ts +2 -0
  116. package/dist/components/PageLoader.d.ts +6 -0
  117. package/dist/components/ScrollTopButton/ScrollTopButton.cjs +79 -0
  118. package/dist/components/ScrollTopButton/ScrollTopButton.cjs.map +1 -0
  119. package/dist/components/ScrollTopButton/ScrollTopButton.d.ts +48 -0
  120. package/dist/components/ScrollTopButton/ScrollTopButton.js +79 -0
  121. package/dist/components/ScrollTopButton/ScrollTopButton.js.map +1 -0
  122. package/dist/components/ScrollTopButton/index.d.ts +4 -0
  123. package/dist/components/ScrollTopButton/scrollToTop.d.ts +29 -0
  124. package/dist/components/ScrollTopButton.d.ts +6 -0
  125. package/dist/components/Select/Select.cjs +446 -4
  126. package/dist/components/Select/Select.cjs.map +1 -1
  127. package/dist/components/Select/Select.d.ts +33 -13
  128. package/dist/components/Select/Select.definitions.d.ts +3 -0
  129. package/dist/components/Select/Select.helpers.d.ts +28 -0
  130. package/dist/components/Select/Select.js +445 -3
  131. package/dist/components/Select/Select.js.map +1 -1
  132. package/dist/components/Select/Select.sx.d.ts +7 -0
  133. package/dist/components/Select/Select.types.d.ts +1 -0
  134. package/dist/components/Select/_parts/SelectMenuItem.d.ts +20 -0
  135. package/dist/components/Select/_parts/SelectSearchHeader.d.ts +15 -0
  136. package/dist/components/Select/_parts/SelectValue.d.ts +22 -0
  137. package/dist/components/Select/index.d.ts +2 -1
  138. package/dist/components/Select.d.ts +4 -0
  139. package/dist/components/Stat/Stat.cjs +1 -1
  140. package/dist/components/Stat/Stat.js +1 -1
  141. package/dist/components/Stepper/Stepper.cjs +4 -1
  142. package/dist/components/Stepper/Stepper.cjs.map +1 -1
  143. package/dist/components/Stepper/Stepper.d.ts +5 -0
  144. package/dist/components/Stepper/Stepper.js +4 -1
  145. package/dist/components/Stepper/Stepper.js.map +1 -1
  146. package/dist/components/_shared/formField.sx.d.ts +33 -0
  147. package/dist/components/_shared/resolvePreset.d.ts +18 -0
  148. package/dist/formField.sx-CQ1mbk9M.cjs +76 -0
  149. package/dist/formField.sx-CQ1mbk9M.cjs.map +1 -0
  150. package/dist/formField.sx-DfVbMe0V.js +77 -0
  151. package/dist/formField.sx-DfVbMe0V.js.map +1 -0
  152. package/dist/hooks/Wizard/Wizard.cjs +7 -0
  153. package/dist/hooks/Wizard/Wizard.cjs.map +1 -0
  154. package/dist/hooks/Wizard/Wizard.js +7 -0
  155. package/dist/hooks/Wizard/Wizard.js.map +1 -0
  156. package/dist/hooks/Wizard/WizardContext.d.ts +67 -0
  157. package/dist/hooks/Wizard/index.d.ts +3 -0
  158. package/dist/hooks/Wizard/useWizard.d.ts +9 -0
  159. package/dist/hooks/Wizard.d.ts +2 -0
  160. package/dist/index.cjs +99 -1
  161. package/dist/index.cjs.map +1 -1
  162. package/dist/index.d.ts +3 -0
  163. package/dist/index.js +31 -2
  164. package/dist/index.js.map +1 -1
  165. package/dist/mui.d.ts +5 -0
  166. package/dist/resolvePreset-B-IB0ehH.js +15 -0
  167. package/dist/resolvePreset-B-IB0ehH.js.map +1 -0
  168. package/dist/resolvePreset-CT3kU-K2.cjs +14 -0
  169. package/dist/resolvePreset-CT3kU-K2.cjs.map +1 -0
  170. package/dist/styles.css +3 -112
  171. package/dist/theme/componentStyles.d.ts +32 -0
  172. package/dist/theme/tokens.d.ts +28 -0
  173. package/dist/useWizard-CWdIxZzX.cjs +94 -0
  174. package/dist/useWizard-CWdIxZzX.cjs.map +1 -0
  175. package/dist/useWizard-CWq--C3o.js +95 -0
  176. package/dist/useWizard-CWq--C3o.js.map +1 -0
  177. package/package.json +1 -1
  178. package/src/components/ActionMenu/ActionMenu.stories.tsx +230 -0
  179. package/src/components/ActionMenu/ActionMenu.tsx +174 -0
  180. package/src/components/ActionMenu/index.ts +2 -0
  181. package/src/components/AppBar/AppBar.stories.tsx +272 -0
  182. package/src/components/AppBar/AppBar.sx.ts +32 -0
  183. package/src/components/AppBar/AppBar.tsx +123 -0
  184. package/src/components/AppBar/AppBarBrand.tsx +120 -0
  185. package/src/components/AppBar/AppBarContext.ts +25 -0
  186. package/src/components/AppBar/AppBarMenuToggle.tsx +90 -0
  187. package/src/components/AppBar/AppBarUserMenu.tsx +217 -0
  188. package/src/components/AppBar/index.ts +25 -0
  189. package/src/components/Autocomplete/Autocomplete.definitions.ts +223 -0
  190. package/src/components/Autocomplete/Autocomplete.helpers.ts +60 -0
  191. package/src/components/Autocomplete/Autocomplete.stories.tsx +363 -2
  192. package/src/components/Autocomplete/Autocomplete.sx.ts +30 -0
  193. package/src/components/Autocomplete/Autocomplete.tsx +312 -90
  194. package/src/components/Autocomplete/Autocomplete.types.ts +13 -0
  195. package/src/components/Autocomplete/_parts/AutocompleteChips.tsx +55 -0
  196. package/src/components/Autocomplete/_parts/AutocompleteLoader.tsx +17 -0
  197. package/src/components/Autocomplete/_parts/AutocompleteOption.tsx +31 -0
  198. package/src/components/Autocomplete/index.ts +12 -1
  199. package/src/components/Avatar/Avatar.definitions.ts +162 -0
  200. package/src/components/Avatar/Avatar.stories.tsx +205 -1
  201. package/src/components/Avatar/Avatar.tsx +166 -103
  202. package/src/components/Card/Card.stories.tsx +205 -16
  203. package/src/components/Card/Card.sx.ts +104 -0
  204. package/src/components/Card/Card.tsx +191 -35
  205. package/src/components/Card/index.ts +9 -1
  206. package/src/components/DatePicker/DatePicker.definitions.ts +24 -1
  207. package/src/components/DatePicker/DatePicker.helpers.ts +24 -0
  208. package/src/components/DatePicker/DatePicker.stories.tsx +29 -2
  209. package/src/components/DatePicker/DatePicker.sx.ts +33 -0
  210. package/src/components/DatePicker/DatePicker.tsx +163 -139
  211. package/src/components/DatePicker/DatePicker.types.ts +10 -0
  212. package/src/components/DatePicker/index.ts +9 -1
  213. package/src/components/DateTimePicker/DateTimePicker.definitions.ts +24 -0
  214. package/src/components/DateTimePicker/DateTimePicker.helpers.ts +38 -0
  215. package/src/components/DateTimePicker/DateTimePicker.stories.tsx +29 -1
  216. package/src/components/DateTimePicker/DateTimePicker.sx.ts +30 -0
  217. package/src/components/DateTimePicker/DateTimePicker.tsx +200 -166
  218. package/src/components/DateTimePicker/DateTimePicker.types.ts +10 -0
  219. package/src/components/DateTimePicker/index.ts +9 -1
  220. package/src/components/Drawer/Drawer.stories.tsx +270 -0
  221. package/src/components/Drawer/Drawer.sx.ts +106 -0
  222. package/src/components/Drawer/Drawer.tsx +214 -0
  223. package/src/components/Drawer/DrawerContext.ts +26 -0
  224. package/src/components/Drawer/DrawerItem.tsx +110 -0
  225. package/src/components/Drawer/index.ts +10 -0
  226. package/src/components/Flyout/Flyout.stories.tsx +26 -18
  227. package/src/components/Icon/Icon.stories.tsx +68 -1
  228. package/src/components/Icon/Icon.tsx +87 -6
  229. package/src/components/Input/Input.definitions.ts +74 -2
  230. package/src/components/Input/Input.helpers.ts +49 -0
  231. package/src/components/Input/Input.stories.tsx +116 -4
  232. package/src/components/Input/Input.sx.ts +42 -0
  233. package/src/components/Input/Input.tsx +117 -162
  234. package/src/components/Input/Input.types.ts +10 -0
  235. package/src/components/Input/index.ts +9 -1
  236. package/src/components/InputGroup/InputGroup.definitions.ts +158 -0
  237. package/src/components/InputGroup/InputGroup.stories.tsx +159 -28
  238. package/src/components/InputGroup/InputGroup.tsx +159 -116
  239. package/src/components/Modal/Modal.stories.tsx +434 -6
  240. package/src/components/Modal/Modal.tsx +303 -121
  241. package/src/components/Modal/ModalFooter.tsx +22 -12
  242. package/src/components/Modal/index.ts +6 -1
  243. package/src/components/PageLoader/PageLoader.stories.tsx +217 -0
  244. package/src/components/PageLoader/PageLoader.tsx +96 -0
  245. package/src/components/PageLoader/index.ts +2 -0
  246. package/src/components/ScrollTopButton/ScrollTopButton.stories.tsx +158 -0
  247. package/src/components/ScrollTopButton/ScrollTopButton.tsx +135 -0
  248. package/src/components/ScrollTopButton/index.ts +8 -0
  249. package/src/components/ScrollTopButton/scrollToTop.ts +37 -0
  250. package/src/components/Select/Select.definitions.ts +114 -0
  251. package/src/components/Select/Select.helpers.ts +71 -0
  252. package/src/components/Select/Select.stories.tsx +126 -8
  253. package/src/components/Select/Select.sx.ts +14 -0
  254. package/src/components/Select/Select.tsx +246 -285
  255. package/src/components/Select/Select.types.ts +15 -0
  256. package/src/components/Select/_parts/SelectMenuItem.tsx +40 -0
  257. package/src/components/Select/_parts/SelectSearchHeader.tsx +51 -0
  258. package/src/components/Select/_parts/SelectValue.tsx +96 -0
  259. package/src/components/Select/index.ts +14 -1
  260. package/src/components/Stepper/Stepper.tsx +17 -1
  261. package/src/components/Tooltip/Tooltip.stories.tsx +15 -3
  262. package/src/components/_shared/formField.sx.ts +118 -0
  263. package/src/components/_shared/resolvePreset.ts +35 -0
  264. package/src/hooks/Wizard/Wizard.stories.tsx +301 -0
  265. package/src/hooks/Wizard/WizardContext.tsx +166 -0
  266. package/src/hooks/Wizard/index.ts +6 -0
  267. package/src/hooks/Wizard/useWizard.ts +13 -0
  268. package/src/index.ts +17 -1
  269. package/src/mui.ts +44 -0
  270. package/src/theme/componentStyles.ts +47 -0
  271. package/src/theme/tokens.ts +43 -0
  272. package/dist/DatePicker-BSNboVhN.js +0 -201
  273. package/dist/DatePicker-BSNboVhN.js.map +0 -1
  274. package/dist/DatePicker-BoqxWAhj.cjs +0 -200
  275. package/dist/DatePicker-BoqxWAhj.cjs.map +0 -1
  276. package/dist/Input-DFHs7cJ_.js +0 -171
  277. package/dist/Input-DFHs7cJ_.js.map +0 -1
  278. package/dist/Input-c8MwNNPg.cjs +0 -170
  279. package/dist/Input-c8MwNNPg.cjs.map +0 -1
  280. package/dist/Select-BO2N56sm.cjs +0 -411
  281. package/dist/Select-BO2N56sm.cjs.map +0 -1
  282. package/dist/Select-BcLkyHSE.js +0 -412
  283. package/dist/Select-BcLkyHSE.js.map +0 -1
  284. package/dist/index.css +0 -3
@@ -1,15 +1,31 @@
1
- import React from "react";
1
+ import React, {
2
+ useMemo,
3
+ useRef,
4
+ useState,
5
+ type ReactNode,
6
+ type SyntheticEvent,
7
+ } from 'react';
2
8
  import {
3
- Autocomplete,
9
+ Autocomplete as MuiAutocomplete,
4
10
  TextField,
5
- Chip,
6
- Avatar,
7
- Box,
8
- Typography,
9
- CircularProgress,
10
- } from "@mui/material";
11
- import ClearIcon from "@mui/icons-material/Clear";
11
+ type TextFieldProps,
12
+ } from '@mui/material';
13
+ import { useTheme, type SxProps, type Theme } from '@mui/material/styles';
14
+ import { Controller, type Control, type RegisterOptions } from 'react-hook-form';
12
15
 
16
+ import { buildAutocompleteSx } from './Autocomplete.sx';
17
+ import {
18
+ areResolvedValuesEqual,
19
+ isResolvedValueEmpty,
20
+ resolveMultipleValue,
21
+ resolveSingleValue,
22
+ } from './Autocomplete.helpers';
23
+ import { resolvePreset } from '../_shared/resolvePreset';
24
+ import { AutocompleteOption } from './_parts/AutocompleteOption';
25
+ import { AutocompleteChips } from './_parts/AutocompleteChips';
26
+ import { AutocompleteLoader } from './_parts/AutocompleteLoader';
27
+
28
+ // ── Tipos de dominio ─────────────────────────────────────────────────────
13
29
  export interface SelectOption {
14
30
  value: string | number;
15
31
  label: string;
@@ -19,121 +35,327 @@ export interface SelectOption {
19
35
  [key: string]: any;
20
36
  }
21
37
 
22
- type RenderOptionItem = (item: SelectOption) => React.ReactNode;
23
- type RenderChipLabel = (item: SelectOption) => React.ReactNode;
38
+ export type LabelPosition = 'outside' | 'floating';
39
+ export type AutocompleteSize = 'small' | 'medium';
40
+
41
+ // ── Render slots ─────────────────────────────────────────────────────────
42
+ export type RenderOptionItem = (item: SelectOption) => ReactNode;
43
+ export type RenderChipLabel = (item: SelectOption) => ReactNode;
24
44
 
25
- interface AutocompleteProps<
26
- TValue extends SelectOption["value"] = SelectOption["value"],
27
- > {
45
+ // ── Props base ───────────────────────────────────────────────────────────
46
+ export type BaseAutocompleteProps<
47
+ _TValue extends SelectOption['value'] = SelectOption['value'],
48
+ > = {
28
49
  label?: string;
29
- value: TValue | TValue[];
30
- onChange: (val: TValue | TValue[]) => void;
31
- options?: SelectOption[];
50
+ options: SelectOption[];
32
51
  placeholder?: string;
33
52
  multiple?: boolean;
34
53
  disabled?: boolean;
54
+ readOnly?: boolean;
35
55
  loading?: boolean;
36
56
  error?: boolean;
37
57
  helperText?: string;
38
58
  maxChipsToShow?: number;
39
59
  renderOptionItem?: RenderOptionItem;
40
60
  renderChipLabel?: RenderChipLabel;
61
+ /** Border radius del input. Default: 10. */
62
+ borderRadius?: number | string;
63
+ /** "outside" (default) = label arriba del input; "floating" = comportamiento nativo MUI. */
64
+ labelPosition?: LabelPosition;
65
+ /** Tamaño del TextField. Default: 'small'. */
66
+ size?: AutocompleteSize;
67
+ /** Texto cuando no hay coincidencias. */
68
+ noOptionsText?: ReactNode;
69
+ /** Texto cuando está cargando. */
70
+ loadingText?: ReactNode;
71
+ sx?: SxProps<Theme>;
72
+ className?: string;
73
+ /**
74
+ * Nombre del preset de estilo registrado en `theme.styles.Autocomplete`.
75
+ * - `"default"` (o ausente) = estilo built-in del paquete.
76
+ * - Cualquier otro string = mergea el preset encima del estilo built-in.
77
+ */
78
+ preset?: string;
79
+ /** Props para el TextField subyacente. */
80
+ textFieldProps?: Partial<TextFieldProps>;
81
+
82
+ // ── Búsqueda asíncrona / remota ──────────────────────────────────────
83
+ /** Se dispara cada vez que el usuario escribe. Útil para llamadas a servicios
84
+ * (debounce recomendado en el consumer). */
85
+ onInputChange?: (
86
+ event: SyntheticEvent,
87
+ value: string,
88
+ reason: 'input' | 'reset' | 'clear' | 'blur' | 'selectOption' | 'removeOption',
89
+ ) => void;
90
+ /** Valor controlado del texto de búsqueda. */
91
+ inputValue?: string;
92
+ /** Filtro custom. Para búsqueda remota pasar `(x) => x` y confiar sólo en el servicio. */
93
+ filterOptions?: (options: SelectOption[], state: any) => SelectOption[];
94
+ /** Permite valores arbitrarios (no solo de la lista). */
95
+ freeSolo?: boolean;
96
+ open?: boolean;
97
+ onOpen?: (event: SyntheticEvent) => void;
98
+ onClose?: (event: SyntheticEvent, reason: string) => void;
99
+ };
100
+
101
+ // ── Variantes discriminadas (RHF vs controlado) ──────────────────────────
102
+ export interface ControlledAutocompleteProps<
103
+ TValue extends SelectOption['value'] = SelectOption['value'],
104
+ > extends BaseAutocompleteProps<TValue> {
105
+ value: TValue | TValue[] | null;
106
+ onChange: (val: TValue | TValue[] | null) => void;
107
+ name?: string;
108
+ control?: never;
109
+ validation?: never;
41
110
  }
42
111
 
43
- export function AutocompleteSelect<
44
- TValue extends SelectOption["value"] = SelectOption["value"],
112
+ export interface RHFAutocompleteProps<
113
+ TValue extends SelectOption['value'] = SelectOption['value'],
114
+ > extends BaseAutocompleteProps<TValue> {
115
+ name: string;
116
+ control: Control<any>;
117
+ validation?: RegisterOptions;
118
+ value?: never;
119
+ onChange?: never;
120
+ }
121
+
122
+ // ── API pública final ────────────────────────────────────────────────────
123
+ export type AutocompleteProps<
124
+ TValue extends SelectOption['value'] = SelectOption['value'],
125
+ > = ControlledAutocompleteProps<TValue> | RHFAutocompleteProps<TValue>;
126
+
127
+ export function Autocomplete<
128
+ TValue extends SelectOption['value'] = SelectOption['value'],
45
129
  >(props: AutocompleteProps<TValue>) {
46
130
  const {
47
131
  label,
48
- options = [],
49
- value,
50
- onChange,
132
+ options,
51
133
  placeholder,
52
134
  multiple = false,
53
135
  disabled = false,
136
+ readOnly = false,
54
137
  loading = false,
55
- error = false,
138
+ error: errorProp = false,
56
139
  helperText,
140
+ maxChipsToShow = 3,
57
141
  renderOptionItem,
58
142
  renderChipLabel,
59
- maxChipsToShow = 3,
60
- } = props;
143
+ borderRadius = 10,
144
+ labelPosition = 'outside',
145
+ size = 'small',
146
+ noOptionsText = 'No hay opciones',
147
+ loadingText,
148
+ sx,
149
+ className,
150
+ preset,
151
+ textFieldProps,
152
+ onInputChange,
153
+ inputValue,
154
+ filterOptions,
155
+ freeSolo,
156
+ open,
157
+ onOpen,
158
+ onClose,
159
+ } = props as BaseAutocompleteProps<TValue>;
61
160
 
62
- const renderTags = (value: SelectOption[], getTagProps: any) => {
63
- const displayed = value.slice(0, maxChipsToShow);
64
- const hidden = value.length - maxChipsToShow;
161
+ // Focus tracking para mostrar placeholder solo cuando el label sube.
162
+ const [isFocused, setIsFocused] = useState(false);
65
163
 
66
- return (
67
- <>
68
- {displayed.map((option, index) => (
69
- <Chip
70
- {...getTagProps({ index })}
71
- key={option.value}
72
- label={renderChipLabel ? renderChipLabel(option) : option.label}
73
- avatar={option.img ? <Avatar src={option.img} /> : undefined}
74
- deleteIcon={<ClearIcon />}
75
- />
76
- ))}
164
+ // Cache interno de opciones vistas (indexado por value). Crítico para async
165
+ // multiple: cuando `options` cambian por un nuevo search, los items ya
166
+ // seleccionados no desaparecen de los chips porque los recordamos acá.
167
+ const seenOptionsRef = useRef<Map<SelectOption['value'], SelectOption>>(
168
+ new Map(),
169
+ );
170
+ for (const opt of options) {
171
+ seenOptionsRef.current.set(opt.value, opt);
172
+ }
77
173
 
78
- {hidden > 0 && <Chip label={`+${hidden} más`} />}
79
- </>
80
- );
81
- };
174
+ // Pool de opciones para resolver values (merge de options + vistas).
175
+ const resolvePool = useMemo<SelectOption[]>(() => {
176
+ const merged = new Map(seenOptionsRef.current);
177
+ for (const opt of options) merged.set(opt.value, opt);
178
+ return Array.from(merged.values());
179
+ // eslint-disable-next-line react-hooks/exhaustive-deps
180
+ }, [options]);
82
181
 
83
- const renderOption = (propsLi: any, option: SelectOption) => (
84
- <li {...propsLi} key={option.value}>
85
- {renderOptionItem ? renderOptionItem(option) : option.label}
86
- </li>
182
+ // Estabiliza la referencia del resolvedValue que pasamos a MUI Autocomplete.
183
+ // MUI compara value con `!==`: una referencia nueva con mismo contenido
184
+ // dispara resetInputValue() y borra el texto que el usuario está escribiendo.
185
+ const prevResolvedRef = useRef<SelectOption[] | SelectOption | null>(
186
+ multiple ? [] : null,
87
187
  );
88
188
 
89
- const renderAutocomplete = (value: any, onChange: any) => (
90
- <Autocomplete
91
- multiple={multiple}
92
- options={options}
93
- value={
94
- multiple
95
- ? (options.filter((o) =>
96
- (value as string[] | undefined)?.includes(o.value as string),
97
- ) ?? [])
98
- : (options.find((o) => o.value === value) ?? null)
99
- }
100
- disabled={disabled}
101
- loading={loading}
102
- getOptionLabel={(opt) => opt?.label ?? ""}
103
- isOptionEqualToValue={(a, b) => a.value === b.value}
104
- onChange={onChange}
105
- renderOption={renderOption}
106
- renderTags={multiple ? renderTags : undefined}
107
- noOptionsText="No hay opciones"
108
- loadingText={
109
- <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
110
- <CircularProgress size={20} />
111
- <Typography>Cargando...</Typography>
112
- </Box>
113
- }
114
- renderInput={(params) => (
115
- <TextField
116
- {...params}
117
- label={label}
118
- placeholder={placeholder}
119
- error={error}
120
- helperText={helperText}
121
- />
122
- )}
123
- />
124
- );
189
+ const stabilizeResolved = (
190
+ newVal: SelectOption[] | SelectOption | null,
191
+ ): any => {
192
+ if (areResolvedValuesEqual(prevResolvedRef.current, newVal, multiple)) {
193
+ return prevResolvedRef.current;
194
+ }
195
+ prevResolvedRef.current = newVal;
196
+ return newVal;
197
+ };
125
198
 
126
- const handleChangeInternal = (event: any, newValue: any) => {
199
+ const theme = useTheme();
200
+ const presetSx = resolvePreset('Autocomplete', preset, theme);
201
+
202
+ const mergedSx = [
203
+ buildAutocompleteSx(borderRadius, labelPosition),
204
+ ...(presetSx ? [presetSx] : []),
205
+ ...(Array.isArray(sx) ? sx : sx ? [sx] : []),
206
+ ];
207
+
208
+ const renderAutocomplete = (
209
+ resolvedValue: any,
210
+ handleChange: (event: any, newValue: any) => void,
211
+ onBlur?: () => void,
212
+ inputRef?: React.Ref<any>,
213
+ rhfError?: boolean,
214
+ rhfHelperText?: string,
215
+ ) => {
216
+ const finalError = !!rhfError || !!errorProp;
217
+ const finalHelperText = rhfHelperText || helperText;
218
+
219
+ const isEmpty = isResolvedValueEmpty(resolvedValue, multiple);
220
+ const showPlaceholder = isEmpty && isFocused && !!placeholder;
221
+
222
+ return (
223
+ <MuiAutocomplete
224
+ multiple={multiple}
225
+ options={options}
226
+ value={resolvedValue}
227
+ disabled={disabled}
228
+ readOnly={readOnly}
229
+ loading={loading}
230
+ className={className}
231
+ sx={mergedSx}
232
+ freeSolo={freeSolo as any}
233
+ open={open}
234
+ onOpen={onOpen}
235
+ onClose={onClose}
236
+ inputValue={inputValue}
237
+ onInputChange={onInputChange}
238
+ filterOptions={filterOptions as any}
239
+ getOptionLabel={(opt) =>
240
+ typeof opt === 'string' ? opt : (opt?.label ?? '')
241
+ }
242
+ isOptionEqualToValue={(a, b) => a?.value === b?.value}
243
+ getOptionDisabled={(opt) => !!opt?.disabled}
244
+ onChange={handleChange}
245
+ onFocus={() => setIsFocused(true)}
246
+ onBlur={() => {
247
+ setIsFocused(false);
248
+ onBlur?.();
249
+ }}
250
+ renderOption={(liProps, option) => (
251
+ <AutocompleteOption
252
+ liProps={liProps as any}
253
+ option={option}
254
+ customRender={renderOptionItem}
255
+ />
256
+ )}
257
+ renderTags={
258
+ multiple
259
+ ? (value, getTagProps) => (
260
+ <AutocompleteChips
261
+ value={value}
262
+ getTagProps={getTagProps}
263
+ size={size}
264
+ maxChipsToShow={maxChipsToShow}
265
+ renderChipLabel={renderChipLabel}
266
+ />
267
+ )
268
+ : undefined
269
+ }
270
+ noOptionsText={noOptionsText}
271
+ loadingText={loadingText ?? <AutocompleteLoader />}
272
+ renderInput={(params) => (
273
+ <TextField
274
+ {...params}
275
+ label={label}
276
+ size={size}
277
+ variant="outlined"
278
+ placeholder={showPlaceholder ? placeholder : undefined}
279
+ error={finalError}
280
+ helperText={finalHelperText}
281
+ inputRef={inputRef}
282
+ {...textFieldProps}
283
+ InputLabelProps={{
284
+ ...(params.InputLabelProps as Record<string, any>),
285
+ ...(textFieldProps?.InputLabelProps as Record<string, any> | undefined),
286
+ shrink:
287
+ labelPosition === 'outside'
288
+ ? !isEmpty || isFocused
289
+ : (params.InputLabelProps as any)?.shrink,
290
+ }}
291
+ />
292
+ )}
293
+ />
294
+ );
295
+ };
296
+
297
+ const handleControlledChange = (_event: any, newValue: any) => {
298
+ const onChange = (props as ControlledAutocompleteProps<TValue>).onChange;
127
299
  if (multiple) {
128
- const values = (newValue ?? []).map((opt: any) => opt.value);
129
- (onChange as (val: TValue[]) => void)?.(values);
300
+ const values = (newValue ?? []).map((opt: SelectOption) => opt.value as TValue);
301
+ onChange(values);
130
302
  } else {
131
- const value = newValue?.value ?? "";
132
- (onChange as (val: TValue) => void)?.(value);
303
+ onChange((newValue?.value ?? null) as TValue | null);
133
304
  }
134
305
  };
135
306
 
136
- return renderAutocomplete(value, handleChangeInternal);
307
+ // --- RHF mode ---
308
+ if ('control' in props && props.control) {
309
+ const { name, control, validation } = props as RHFAutocompleteProps<TValue>;
310
+ return (
311
+ <Controller
312
+ name={name}
313
+ control={control}
314
+ rules={validation}
315
+ render={({ field, fieldState: { error: fieldError } }) => {
316
+ const resolvedValue = stabilizeResolved(
317
+ multiple
318
+ ? resolveMultipleValue(resolvePool, field.value)
319
+ : resolveSingleValue(resolvePool, field.value),
320
+ );
321
+
322
+ const handleChange = (_event: any, newValue: any) => {
323
+ if (multiple) {
324
+ field.onChange(
325
+ (newValue ?? []).map((opt: SelectOption) => opt.value),
326
+ );
327
+ } else {
328
+ field.onChange(newValue?.value ?? null);
329
+ }
330
+ };
331
+
332
+ return renderAutocomplete(
333
+ resolvedValue,
334
+ handleChange,
335
+ field.onBlur,
336
+ field.ref,
337
+ !!fieldError,
338
+ fieldError?.message,
339
+ );
340
+ }}
341
+ />
342
+ );
343
+ }
344
+
345
+ // --- Controlled mode ---
346
+ const controlledValue = (props as ControlledAutocompleteProps<TValue>).value;
347
+ const resolvedValue = stabilizeResolved(
348
+ multiple
349
+ ? resolveMultipleValue(resolvePool, controlledValue as SelectOption['value'][])
350
+ : resolveSingleValue(resolvePool, controlledValue as SelectOption['value']),
351
+ );
352
+
353
+ return renderAutocomplete(resolvedValue, handleControlledChange);
137
354
  }
138
355
 
139
- export default AutocompleteSelect;
356
+ /**
357
+ * @deprecated Usar `Autocomplete` en su lugar. Alias mantenido para retro-compatibilidad.
358
+ */
359
+ export const AutocompleteSelect = Autocomplete;
360
+
361
+ export default Autocomplete;
@@ -0,0 +1,13 @@
1
+ // Re-export barrel para compatibilidad con imports antiguos.
2
+ // Los tipos ahora viven dentro de Autocomplete.tsx.
3
+ export type {
4
+ SelectOption,
5
+ LabelPosition,
6
+ AutocompleteSize,
7
+ RenderOptionItem,
8
+ RenderChipLabel,
9
+ BaseAutocompleteProps,
10
+ ControlledAutocompleteProps,
11
+ RHFAutocompleteProps,
12
+ AutocompleteProps,
13
+ } from './Autocomplete';
@@ -0,0 +1,55 @@
1
+ import React from 'react';
2
+ import { Avatar, Chip } from '@mui/material';
3
+ import ClearIcon from '@mui/icons-material/Clear';
4
+
5
+ import type {
6
+ SelectOption,
7
+ RenderChipLabel,
8
+ AutocompleteSize,
9
+ } from '../Autocomplete';
10
+
11
+ interface AutocompleteChipsProps {
12
+ value: SelectOption[];
13
+ getTagProps: (args: { index: number }) => Record<string, any>;
14
+ size: AutocompleteSize;
15
+ maxChipsToShow: number;
16
+ renderChipLabel?: RenderChipLabel;
17
+ }
18
+
19
+ /**
20
+ * Render de los chips del Autocomplete en modo multiple.
21
+ * Trunca después de `maxChipsToShow` con un chip "+N más".
22
+ * El `getTagProps({ index })` viene de MUI y contiene el handler de delete,
23
+ * la key, etc. — se spreadea tal cual al Chip.
24
+ */
25
+ export const AutocompleteChips: React.FC<AutocompleteChipsProps> = ({
26
+ value,
27
+ getTagProps,
28
+ size,
29
+ maxChipsToShow,
30
+ renderChipLabel,
31
+ }) => {
32
+ const displayed = value.slice(0, maxChipsToShow);
33
+ const hidden = value.length - maxChipsToShow;
34
+
35
+ return (
36
+ <>
37
+ {displayed.map((option, index) => {
38
+ const tagProps = getTagProps({ index });
39
+ return (
40
+ <Chip
41
+ {...tagProps}
42
+ key={option.value}
43
+ size={size}
44
+ label={renderChipLabel ? renderChipLabel(option) : option.label}
45
+ avatar={option.img ? <Avatar src={option.img} /> : undefined}
46
+ deleteIcon={<ClearIcon />}
47
+ />
48
+ );
49
+ })}
50
+ {hidden > 0 && <Chip size={size} label={`+${hidden} más`} />}
51
+ </>
52
+ );
53
+ };
54
+
55
+ export default AutocompleteChips;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { Box, CircularProgress, Typography } from '@mui/material';
3
+
4
+ /**
5
+ * Loader por defecto para el `loadingText` del Autocomplete.
6
+ * El consumer puede sobrescribirlo pasando `loadingText` al componente padre.
7
+ */
8
+ export const AutocompleteLoader: React.FC<{ text?: string }> = ({
9
+ text = 'Cargando...',
10
+ }) => (
11
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
12
+ <CircularProgress size={16} />
13
+ <Typography variant="body2">{text}</Typography>
14
+ </Box>
15
+ );
16
+
17
+ export default AutocompleteLoader;
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import type { SelectOption, RenderOptionItem } from '../Autocomplete';
3
+
4
+ interface AutocompleteOptionProps {
5
+ liProps: React.HTMLAttributes<HTMLLIElement> & { key?: React.Key };
6
+ option: SelectOption;
7
+ customRender?: RenderOptionItem;
8
+ }
9
+
10
+ /**
11
+ * Render por defecto de un <li> del dropdown del Autocomplete.
12
+ * Se expone como componente para testear y extender fácilmente, pero MUI
13
+ * lo llama desde `renderOption={(props, option) => ...}` en el padre.
14
+ *
15
+ * El `key` lo manejamos nosotros (no spreadeamos junto al resto) porque MUI
16
+ * lo inyecta como parte de `propsLi` y React exige que sea prop directo.
17
+ */
18
+ export const AutocompleteOption: React.FC<AutocompleteOptionProps> = ({
19
+ liProps,
20
+ option,
21
+ customRender,
22
+ }) => {
23
+ const { key: _ignored, ...liRest } = liProps;
24
+ return (
25
+ <li key={option.value} {...liRest}>
26
+ {customRender ? customRender(option) : option.label}
27
+ </li>
28
+ );
29
+ };
30
+
31
+ export default AutocompleteOption;
@@ -1 +1,12 @@
1
- export { default as Autocomplete} from './Autocomplete'
1
+ export { Autocomplete, AutocompleteSelect, default } from './Autocomplete';
2
+ export type {
3
+ AutocompleteProps,
4
+ SelectOption,
5
+ BaseAutocompleteProps,
6
+ ControlledAutocompleteProps,
7
+ RHFAutocompleteProps,
8
+ RenderOptionItem,
9
+ RenderChipLabel,
10
+ LabelPosition,
11
+ AutocompleteSize,
12
+ } from './Autocomplete.types';