@soyfri/shared-library 1.5.0 → 2.0.0-beta.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 (283) hide show
  1. package/dist/components/ActionMenu/ActionMenu.cjs +107 -0
  2. package/dist/components/ActionMenu/ActionMenu.cjs.map +1 -0
  3. package/dist/components/ActionMenu/ActionMenu.d.ts +60 -0
  4. package/dist/components/ActionMenu/ActionMenu.js +107 -0
  5. package/dist/components/ActionMenu/ActionMenu.js.map +1 -0
  6. package/dist/components/ActionMenu/index.d.ts +2 -0
  7. package/dist/components/ActionMenu.d.ts +6 -0
  8. package/dist/components/AppBar/AppBar.cjs +346 -0
  9. package/dist/components/AppBar/AppBar.cjs.map +1 -0
  10. package/dist/components/AppBar/AppBar.d.ts +55 -0
  11. package/dist/components/AppBar/AppBar.js +346 -0
  12. package/dist/components/AppBar/AppBar.js.map +1 -0
  13. package/dist/components/AppBar/AppBar.sx.d.ts +12 -0
  14. package/dist/components/AppBar/AppBarBrand.d.ts +31 -0
  15. package/dist/components/AppBar/AppBarContext.d.ts +18 -0
  16. package/dist/components/AppBar/AppBarMenuToggle.d.ts +39 -0
  17. package/dist/components/AppBar/AppBarUserMenu.d.ts +65 -0
  18. package/dist/components/AppBar/index.d.ts +12 -0
  19. package/dist/components/AppBar.d.ts +6 -0
  20. package/dist/components/Autocomplete/Autocomplete.cjs +259 -54
  21. package/dist/components/Autocomplete/Autocomplete.cjs.map +1 -1
  22. package/dist/components/Autocomplete/Autocomplete.d.ts +64 -9
  23. package/dist/components/Autocomplete/Autocomplete.definitions.d.ts +6 -0
  24. package/dist/components/Autocomplete/Autocomplete.helpers.d.ts +18 -0
  25. package/dist/components/Autocomplete/Autocomplete.js +261 -56
  26. package/dist/components/Autocomplete/Autocomplete.js.map +1 -1
  27. package/dist/components/Autocomplete/Autocomplete.sx.d.ts +7 -0
  28. package/dist/components/Autocomplete/Autocomplete.types.d.ts +1 -0
  29. package/dist/components/Autocomplete/_parts/AutocompleteChips.d.ts +19 -0
  30. package/dist/components/Autocomplete/_parts/AutocompleteLoader.d.ts +9 -0
  31. package/dist/components/Autocomplete/_parts/AutocompleteOption.d.ts +19 -0
  32. package/dist/components/Autocomplete/index.d.ts +2 -1
  33. package/dist/components/Autocomplete.d.ts +4 -0
  34. package/dist/components/Avatar/Avatar.cjs +116 -79
  35. package/dist/components/Avatar/Avatar.cjs.map +1 -1
  36. package/dist/components/Avatar/Avatar.d.ts +16 -2
  37. package/dist/components/Avatar/Avatar.definitions.d.ts +11 -0
  38. package/dist/components/Avatar/Avatar.js +117 -80
  39. package/dist/components/Avatar/Avatar.js.map +1 -1
  40. package/dist/components/Card/Card.cjs +168 -9
  41. package/dist/components/Card/Card.cjs.map +1 -1
  42. package/dist/components/Card/Card.d.ts +78 -8
  43. package/dist/components/Card/Card.js +170 -11
  44. package/dist/components/Card/Card.js.map +1 -1
  45. package/dist/components/Card/Card.sx.d.ts +17 -0
  46. package/dist/components/Card/index.d.ts +4 -1
  47. package/dist/components/Card.d.ts +4 -0
  48. package/dist/components/DatePicker/DatePicker.cjs +201 -3
  49. package/dist/components/DatePicker/DatePicker.cjs.map +1 -1
  50. package/dist/components/DatePicker/DatePicker.d.ts +47 -9
  51. package/dist/components/DatePicker/DatePicker.definitions.d.ts +1 -0
  52. package/dist/components/DatePicker/DatePicker.helpers.d.ts +7 -0
  53. package/dist/components/DatePicker/DatePicker.js +200 -2
  54. package/dist/components/DatePicker/DatePicker.js.map +1 -1
  55. package/dist/components/DatePicker/DatePicker.sx.d.ts +9 -0
  56. package/dist/components/DatePicker/DatePicker.types.d.ts +1 -0
  57. package/dist/components/DatePicker/index.d.ts +2 -1
  58. package/dist/components/DatePicker.d.ts +4 -0
  59. package/dist/components/DateTimePicker/DateTimePicker.cjs +152 -138
  60. package/dist/components/DateTimePicker/DateTimePicker.cjs.map +1 -1
  61. package/dist/components/DateTimePicker/DateTimePicker.d.ts +46 -9
  62. package/dist/components/DateTimePicker/DateTimePicker.definitions.d.ts +1 -0
  63. package/dist/components/DateTimePicker/DateTimePicker.helpers.d.ts +11 -0
  64. package/dist/components/DateTimePicker/DateTimePicker.js +152 -138
  65. package/dist/components/DateTimePicker/DateTimePicker.js.map +1 -1
  66. package/dist/components/DateTimePicker/DateTimePicker.sx.d.ts +7 -0
  67. package/dist/components/DateTimePicker/DateTimePicker.types.d.ts +1 -0
  68. package/dist/components/DateTimePicker/index.d.ts +2 -1
  69. package/dist/components/DateTimePicker.d.ts +4 -0
  70. package/dist/components/Drawer/Drawer.cjs +271 -0
  71. package/dist/components/Drawer/Drawer.cjs.map +1 -0
  72. package/dist/components/Drawer/Drawer.d.ts +51 -0
  73. package/dist/components/Drawer/Drawer.js +271 -0
  74. package/dist/components/Drawer/Drawer.js.map +1 -0
  75. package/dist/components/Drawer/Drawer.sx.d.ts +23 -0
  76. package/dist/components/Drawer/DrawerContext.d.ts +18 -0
  77. package/dist/components/Drawer/DrawerItem.d.ts +35 -0
  78. package/dist/components/Drawer/index.d.ts +6 -0
  79. package/dist/components/Drawer.d.ts +6 -0
  80. package/dist/components/Icon/Icon.cjs +44 -3
  81. package/dist/components/Icon/Icon.cjs.map +1 -1
  82. package/dist/components/Icon/Icon.d.ts +34 -1
  83. package/dist/components/Icon/Icon.js +44 -3
  84. package/dist/components/Icon/Icon.js.map +1 -1
  85. package/dist/components/Input/Input.cjs +173 -3
  86. package/dist/components/Input/Input.cjs.map +1 -1
  87. package/dist/components/Input/Input.d.ts +20 -15
  88. package/dist/components/Input/Input.definitions.d.ts +5 -2
  89. package/dist/components/Input/Input.helpers.d.ts +14 -0
  90. package/dist/components/Input/Input.js +172 -2
  91. package/dist/components/Input/Input.js.map +1 -1
  92. package/dist/components/Input/Input.sx.d.ts +8 -0
  93. package/dist/components/Input/Input.types.d.ts +1 -0
  94. package/dist/components/Input/index.d.ts +2 -1
  95. package/dist/components/Input.d.ts +4 -0
  96. package/dist/components/InputGroup/InputGroup.cjs +104 -91
  97. package/dist/components/InputGroup/InputGroup.cjs.map +1 -1
  98. package/dist/components/InputGroup/InputGroup.d.ts +37 -1
  99. package/dist/components/InputGroup/InputGroup.definitions.d.ts +6 -0
  100. package/dist/components/InputGroup/InputGroup.js +106 -93
  101. package/dist/components/InputGroup/InputGroup.js.map +1 -1
  102. package/dist/components/Modal/Modal.cjs +226 -116
  103. package/dist/components/Modal/Modal.cjs.map +1 -1
  104. package/dist/components/Modal/Modal.d.ts +38 -2
  105. package/dist/components/Modal/Modal.js +227 -117
  106. package/dist/components/Modal/Modal.js.map +1 -1
  107. package/dist/components/Modal/ModalFooter.d.ts +9 -1
  108. package/dist/components/Modal/index.d.ts +5 -0
  109. package/dist/components/PageLoader/PageLoader.cjs +61 -0
  110. package/dist/components/PageLoader/PageLoader.cjs.map +1 -0
  111. package/dist/components/PageLoader/PageLoader.d.ts +38 -0
  112. package/dist/components/PageLoader/PageLoader.js +61 -0
  113. package/dist/components/PageLoader/PageLoader.js.map +1 -0
  114. package/dist/components/PageLoader/index.d.ts +2 -0
  115. package/dist/components/PageLoader.d.ts +6 -0
  116. package/dist/components/ScrollTopButton/ScrollTopButton.cjs +79 -0
  117. package/dist/components/ScrollTopButton/ScrollTopButton.cjs.map +1 -0
  118. package/dist/components/ScrollTopButton/ScrollTopButton.d.ts +48 -0
  119. package/dist/components/ScrollTopButton/ScrollTopButton.js +79 -0
  120. package/dist/components/ScrollTopButton/ScrollTopButton.js.map +1 -0
  121. package/dist/components/ScrollTopButton/index.d.ts +4 -0
  122. package/dist/components/ScrollTopButton/scrollToTop.d.ts +29 -0
  123. package/dist/components/ScrollTopButton.d.ts +6 -0
  124. package/dist/components/Select/Select.cjs +446 -4
  125. package/dist/components/Select/Select.cjs.map +1 -1
  126. package/dist/components/Select/Select.d.ts +33 -13
  127. package/dist/components/Select/Select.definitions.d.ts +3 -0
  128. package/dist/components/Select/Select.helpers.d.ts +28 -0
  129. package/dist/components/Select/Select.js +445 -3
  130. package/dist/components/Select/Select.js.map +1 -1
  131. package/dist/components/Select/Select.sx.d.ts +7 -0
  132. package/dist/components/Select/Select.types.d.ts +1 -0
  133. package/dist/components/Select/_parts/SelectMenuItem.d.ts +20 -0
  134. package/dist/components/Select/_parts/SelectSearchHeader.d.ts +15 -0
  135. package/dist/components/Select/_parts/SelectValue.d.ts +22 -0
  136. package/dist/components/Select/index.d.ts +2 -1
  137. package/dist/components/Select.d.ts +4 -0
  138. package/dist/components/Stat/Stat.cjs +1 -1
  139. package/dist/components/Stat/Stat.js +1 -1
  140. package/dist/components/Stepper/Stepper.cjs +4 -1
  141. package/dist/components/Stepper/Stepper.cjs.map +1 -1
  142. package/dist/components/Stepper/Stepper.d.ts +5 -0
  143. package/dist/components/Stepper/Stepper.js +4 -1
  144. package/dist/components/Stepper/Stepper.js.map +1 -1
  145. package/dist/components/_shared/formField.sx.d.ts +33 -0
  146. package/dist/components/_shared/resolvePreset.d.ts +18 -0
  147. package/dist/formField.sx-CQ1mbk9M.cjs +76 -0
  148. package/dist/formField.sx-CQ1mbk9M.cjs.map +1 -0
  149. package/dist/formField.sx-DfVbMe0V.js +77 -0
  150. package/dist/formField.sx-DfVbMe0V.js.map +1 -0
  151. package/dist/hooks/Wizard/Wizard.cjs +7 -0
  152. package/dist/hooks/Wizard/Wizard.cjs.map +1 -0
  153. package/dist/hooks/Wizard/Wizard.js +7 -0
  154. package/dist/hooks/Wizard/Wizard.js.map +1 -0
  155. package/dist/hooks/Wizard/WizardContext.d.ts +67 -0
  156. package/dist/hooks/Wizard/index.d.ts +3 -0
  157. package/dist/hooks/Wizard/useWizard.d.ts +9 -0
  158. package/dist/hooks/Wizard.d.ts +2 -0
  159. package/dist/index.cjs +99 -1
  160. package/dist/index.cjs.map +1 -1
  161. package/dist/index.d.ts +3 -0
  162. package/dist/index.js +31 -2
  163. package/dist/index.js.map +1 -1
  164. package/dist/mui.d.ts +5 -0
  165. package/dist/resolvePreset-B-IB0ehH.js +15 -0
  166. package/dist/resolvePreset-B-IB0ehH.js.map +1 -0
  167. package/dist/resolvePreset-CT3kU-K2.cjs +14 -0
  168. package/dist/resolvePreset-CT3kU-K2.cjs.map +1 -0
  169. package/dist/styles.css +3 -112
  170. package/dist/theme/componentStyles.d.ts +32 -0
  171. package/dist/theme/tokens.d.ts +28 -0
  172. package/dist/useWizard-CWdIxZzX.cjs +94 -0
  173. package/dist/useWizard-CWdIxZzX.cjs.map +1 -0
  174. package/dist/useWizard-CWq--C3o.js +95 -0
  175. package/dist/useWizard-CWq--C3o.js.map +1 -0
  176. package/package.json +1 -1
  177. package/src/components/ActionMenu/ActionMenu.stories.tsx +230 -0
  178. package/src/components/ActionMenu/ActionMenu.tsx +174 -0
  179. package/src/components/ActionMenu/index.ts +2 -0
  180. package/src/components/AppBar/AppBar.stories.tsx +272 -0
  181. package/src/components/AppBar/AppBar.sx.ts +32 -0
  182. package/src/components/AppBar/AppBar.tsx +123 -0
  183. package/src/components/AppBar/AppBarBrand.tsx +120 -0
  184. package/src/components/AppBar/AppBarContext.ts +25 -0
  185. package/src/components/AppBar/AppBarMenuToggle.tsx +90 -0
  186. package/src/components/AppBar/AppBarUserMenu.tsx +217 -0
  187. package/src/components/AppBar/index.ts +25 -0
  188. package/src/components/Autocomplete/Autocomplete.definitions.ts +223 -0
  189. package/src/components/Autocomplete/Autocomplete.helpers.ts +60 -0
  190. package/src/components/Autocomplete/Autocomplete.stories.tsx +363 -2
  191. package/src/components/Autocomplete/Autocomplete.sx.ts +30 -0
  192. package/src/components/Autocomplete/Autocomplete.tsx +312 -90
  193. package/src/components/Autocomplete/Autocomplete.types.ts +13 -0
  194. package/src/components/Autocomplete/_parts/AutocompleteChips.tsx +55 -0
  195. package/src/components/Autocomplete/_parts/AutocompleteLoader.tsx +17 -0
  196. package/src/components/Autocomplete/_parts/AutocompleteOption.tsx +31 -0
  197. package/src/components/Autocomplete/index.ts +12 -1
  198. package/src/components/Avatar/Avatar.definitions.ts +162 -0
  199. package/src/components/Avatar/Avatar.stories.tsx +205 -1
  200. package/src/components/Avatar/Avatar.tsx +166 -103
  201. package/src/components/Card/Card.stories.tsx +205 -16
  202. package/src/components/Card/Card.sx.ts +104 -0
  203. package/src/components/Card/Card.tsx +191 -35
  204. package/src/components/Card/index.ts +9 -1
  205. package/src/components/DatePicker/DatePicker.definitions.ts +24 -1
  206. package/src/components/DatePicker/DatePicker.helpers.ts +24 -0
  207. package/src/components/DatePicker/DatePicker.stories.tsx +29 -2
  208. package/src/components/DatePicker/DatePicker.sx.ts +33 -0
  209. package/src/components/DatePicker/DatePicker.tsx +163 -139
  210. package/src/components/DatePicker/DatePicker.types.ts +10 -0
  211. package/src/components/DatePicker/index.ts +9 -1
  212. package/src/components/DateTimePicker/DateTimePicker.definitions.ts +24 -0
  213. package/src/components/DateTimePicker/DateTimePicker.helpers.ts +38 -0
  214. package/src/components/DateTimePicker/DateTimePicker.stories.tsx +29 -1
  215. package/src/components/DateTimePicker/DateTimePicker.sx.ts +30 -0
  216. package/src/components/DateTimePicker/DateTimePicker.tsx +200 -166
  217. package/src/components/DateTimePicker/DateTimePicker.types.ts +10 -0
  218. package/src/components/DateTimePicker/index.ts +9 -1
  219. package/src/components/Drawer/Drawer.stories.tsx +270 -0
  220. package/src/components/Drawer/Drawer.sx.ts +106 -0
  221. package/src/components/Drawer/Drawer.tsx +214 -0
  222. package/src/components/Drawer/DrawerContext.ts +26 -0
  223. package/src/components/Drawer/DrawerItem.tsx +110 -0
  224. package/src/components/Drawer/index.ts +10 -0
  225. package/src/components/Flyout/Flyout.stories.tsx +26 -18
  226. package/src/components/Icon/Icon.stories.tsx +68 -1
  227. package/src/components/Icon/Icon.tsx +87 -6
  228. package/src/components/Input/Input.definitions.ts +74 -2
  229. package/src/components/Input/Input.helpers.ts +49 -0
  230. package/src/components/Input/Input.stories.tsx +116 -4
  231. package/src/components/Input/Input.sx.ts +42 -0
  232. package/src/components/Input/Input.tsx +117 -162
  233. package/src/components/Input/Input.types.ts +10 -0
  234. package/src/components/Input/index.ts +9 -1
  235. package/src/components/InputGroup/InputGroup.definitions.ts +158 -0
  236. package/src/components/InputGroup/InputGroup.stories.tsx +159 -28
  237. package/src/components/InputGroup/InputGroup.tsx +159 -116
  238. package/src/components/Modal/Modal.stories.tsx +434 -6
  239. package/src/components/Modal/Modal.tsx +303 -121
  240. package/src/components/Modal/ModalFooter.tsx +22 -12
  241. package/src/components/Modal/index.ts +6 -1
  242. package/src/components/PageLoader/PageLoader.stories.tsx +217 -0
  243. package/src/components/PageLoader/PageLoader.tsx +96 -0
  244. package/src/components/PageLoader/index.ts +2 -0
  245. package/src/components/ScrollTopButton/ScrollTopButton.stories.tsx +158 -0
  246. package/src/components/ScrollTopButton/ScrollTopButton.tsx +135 -0
  247. package/src/components/ScrollTopButton/index.ts +8 -0
  248. package/src/components/ScrollTopButton/scrollToTop.ts +37 -0
  249. package/src/components/Select/Select.definitions.ts +114 -0
  250. package/src/components/Select/Select.helpers.ts +71 -0
  251. package/src/components/Select/Select.stories.tsx +126 -8
  252. package/src/components/Select/Select.sx.ts +14 -0
  253. package/src/components/Select/Select.tsx +246 -285
  254. package/src/components/Select/Select.types.ts +15 -0
  255. package/src/components/Select/_parts/SelectMenuItem.tsx +40 -0
  256. package/src/components/Select/_parts/SelectSearchHeader.tsx +51 -0
  257. package/src/components/Select/_parts/SelectValue.tsx +96 -0
  258. package/src/components/Select/index.ts +14 -1
  259. package/src/components/Stepper/Stepper.tsx +17 -1
  260. package/src/components/Tooltip/Tooltip.stories.tsx +15 -3
  261. package/src/components/_shared/formField.sx.ts +118 -0
  262. package/src/components/_shared/resolvePreset.ts +35 -0
  263. package/src/hooks/Wizard/Wizard.stories.tsx +301 -0
  264. package/src/hooks/Wizard/WizardContext.tsx +166 -0
  265. package/src/hooks/Wizard/index.ts +6 -0
  266. package/src/hooks/Wizard/useWizard.ts +13 -0
  267. package/src/index.ts +17 -1
  268. package/src/mui.ts +44 -0
  269. package/src/theme/componentStyles.ts +47 -0
  270. package/src/theme/tokens.ts +43 -0
  271. package/dist/DatePicker-BSNboVhN.js +0 -201
  272. package/dist/DatePicker-BSNboVhN.js.map +0 -1
  273. package/dist/DatePicker-BoqxWAhj.cjs +0 -200
  274. package/dist/DatePicker-BoqxWAhj.cjs.map +0 -1
  275. package/dist/Input-DFHs7cJ_.js +0 -171
  276. package/dist/Input-DFHs7cJ_.js.map +0 -1
  277. package/dist/Input-c8MwNNPg.cjs +0 -170
  278. package/dist/Input-c8MwNNPg.cjs.map +0 -1
  279. package/dist/Select-BO2N56sm.cjs +0 -411
  280. package/dist/Select-BO2N56sm.cjs.map +0 -1
  281. package/dist/Select-BcLkyHSE.js +0 -412
  282. package/dist/Select-BcLkyHSE.js.map +0 -1
  283. package/dist/index.css +0 -3
@@ -0,0 +1,60 @@
1
+ import type { SxProps, Theme } from '@mui/material/styles';
2
+ import type { SelectOption } from './Autocomplete';
3
+
4
+ /** Resuelve un value único a su SelectOption dentro del pool. */
5
+ export const resolveSingleValue = (
6
+ options: SelectOption[],
7
+ value: SelectOption['value'] | null | undefined,
8
+ ): SelectOption | null => options.find((o) => o.value === value) ?? null;
9
+
10
+ /** Resuelve un array de values a sus SelectOption dentro del pool. */
11
+ export const resolveMultipleValue = (
12
+ options: SelectOption[],
13
+ value: SelectOption['value'][] | null | undefined,
14
+ ): SelectOption[] => {
15
+ if (!Array.isArray(value)) return [];
16
+ return options.filter((o) => value.includes(o.value));
17
+ };
18
+
19
+ /** Merge de sx base + extra del consumer, respetando el contrato de MUI. */
20
+ export const mergeSx = (
21
+ base: SxProps<Theme>,
22
+ extra?: SxProps<Theme>,
23
+ ): SxProps<Theme> => {
24
+ if (!extra) return base;
25
+ return [base, ...(Array.isArray(extra) ? extra : [extra])] as SxProps<Theme>;
26
+ };
27
+
28
+ /**
29
+ * Compara dos valores resueltos (single o multiple) por value para decidir
30
+ * si son estructuralmente iguales. Se usa para estabilizar la referencia que
31
+ * se pasa a MUI Autocomplete (MUI compara value por referencia con `!==`,
32
+ * y una nueva referencia — aunque el contenido sea igual — dispara
33
+ * `resetInputValue` y borra el texto mientras el usuario tipea).
34
+ */
35
+ export const areResolvedValuesEqual = (
36
+ prev: SelectOption | SelectOption[] | null,
37
+ next: SelectOption | SelectOption[] | null,
38
+ multiple: boolean,
39
+ ): boolean => {
40
+ if (multiple) {
41
+ if (!Array.isArray(prev) || !Array.isArray(next)) return false;
42
+ if (prev.length !== next.length) return false;
43
+ return prev.every((o, i) => o.value === next[i]?.value);
44
+ }
45
+ return (
46
+ (prev as SelectOption | null)?.value ===
47
+ (next as SelectOption | null)?.value
48
+ );
49
+ };
50
+
51
+ /** Determina si un valor resuelto está vacío (para placeholder/shrink). */
52
+ export const isResolvedValueEmpty = (
53
+ resolvedValue: unknown,
54
+ multiple: boolean,
55
+ ): boolean => {
56
+ if (multiple) {
57
+ return !Array.isArray(resolvedValue) || resolvedValue.length === 0;
58
+ }
59
+ return !resolvedValue;
60
+ };
@@ -1,6 +1,7 @@
1
+ import { useState, useEffect, useMemo } from "react";
1
2
  import type { Meta, StoryObj } from "@storybook/react";
2
- import React, { useState } from "react";
3
- import { Box, Typography, Avatar } from "@mui/material";
3
+ import { Box, Typography, Avatar, Stack, Button } from "@mui/material";
4
+ import { useForm } from "react-hook-form";
4
5
 
5
6
  import Autocomplete, { SelectOption } from "./Autocomplete";
6
7
  import {
@@ -12,6 +13,12 @@ import {
12
13
  MultipleWithLimitAutocompleteDefinition,
13
14
  SimpleAutocompleteDefinition,
14
15
  WithPlaceholderAutocompleteDefinition,
16
+ WithErrorAutocompleteDefinition,
17
+ LabelPositionAutocompleteDefinition,
18
+ CustomBorderRadiusAutocompleteDefinition,
19
+ EmptyWithPlaceholderAutocompleteDefinition,
20
+ RHFAutocompleteDefinition,
21
+ AsyncServiceAutocompleteDefinition,
15
22
  } from "./Autocomplete.definitions";
16
23
 
17
24
  // =============================================================================
@@ -385,3 +392,357 @@ export const ManyOptions: Story = {
385
392
  },
386
393
  },
387
394
  };
395
+
396
+ // =============================================================================
397
+ // NUEVAS STORIES — API refactorizada
398
+ // =============================================================================
399
+
400
+ export const WithError: Story = {
401
+ render: () => {
402
+ const [value, setValue] = useState<string | null>(null);
403
+ return (
404
+ <Box sx={{ width: 300 }}>
405
+ <Autocomplete
406
+ label="Cantidad"
407
+ options={basicOptions}
408
+ value={value}
409
+ onChange={(val) => setValue(val as string | null)}
410
+ error={!value}
411
+ helperText={!value ? "Debes seleccionar una cantidad" : " "}
412
+ />
413
+ </Box>
414
+ );
415
+ },
416
+ parameters: {
417
+ docs: {
418
+ description: {
419
+ story:
420
+ "Autocomplete en estado de error con `helperText`. Muestra la validación visual (borde + label + texto en rojo).",
421
+ },
422
+ source: { code: WithErrorAutocompleteDefinition.trim() },
423
+ },
424
+ },
425
+ };
426
+
427
+ export const LabelPosition: Story = {
428
+ render: () => {
429
+ const [a, setA] = useState<string | null>(null);
430
+ const [b, setB] = useState<string | null>(null);
431
+ return (
432
+ <Stack spacing={3} sx={{ width: 320 }}>
433
+ <Autocomplete
434
+ label="Outside (default)"
435
+ labelPosition="outside"
436
+ options={basicOptions}
437
+ value={a}
438
+ onChange={(val) => setA(val as string | null)}
439
+ />
440
+ <Autocomplete
441
+ label="Floating"
442
+ labelPosition="floating"
443
+ options={basicOptions}
444
+ value={b}
445
+ onChange={(val) => setB(val as string | null)}
446
+ />
447
+ </Stack>
448
+ );
449
+ },
450
+ parameters: {
451
+ docs: {
452
+ description: {
453
+ story:
454
+ "Comparación entre `labelPosition='outside'` (label arriba del input, consistente con Input/Select/DatePicker) y `labelPosition='floating'` (comportamiento nativo MUI dentro del notch).",
455
+ },
456
+ source: { code: LabelPositionAutocompleteDefinition.trim() },
457
+ },
458
+ },
459
+ };
460
+
461
+ export const CustomBorderRadius: Story = {
462
+ render: () => {
463
+ const [a, setA] = useState<string | null>(null);
464
+ const [b, setB] = useState<string | null>(null);
465
+ const [c, setC] = useState<string | null>(null);
466
+ return (
467
+ <Stack spacing={2} sx={{ width: 300 }}>
468
+ <Autocomplete
469
+ label="0"
470
+ borderRadius={0}
471
+ options={basicOptions}
472
+ value={a}
473
+ onChange={(v) => setA(v as string | null)}
474
+ />
475
+ <Autocomplete
476
+ label="10 (default)"
477
+ borderRadius={10}
478
+ options={basicOptions}
479
+ value={b}
480
+ onChange={(v) => setB(v as string | null)}
481
+ />
482
+ <Autocomplete
483
+ label="24 (pill)"
484
+ borderRadius={24}
485
+ options={basicOptions}
486
+ value={c}
487
+ onChange={(v) => setC(v as string | null)}
488
+ />
489
+ </Stack>
490
+ );
491
+ },
492
+ parameters: {
493
+ docs: {
494
+ description: {
495
+ story:
496
+ "La prop `borderRadius` permite personalizar el radio del borde sin usar `sx` (acepta number en px o string).",
497
+ },
498
+ source: { code: CustomBorderRadiusAutocompleteDefinition.trim() },
499
+ },
500
+ },
501
+ };
502
+
503
+ export const EmptyWithPlaceholder: Story = {
504
+ render: () => {
505
+ const [value, setValue] = useState<string | null>(null);
506
+ const countries: SelectOption[] = [
507
+ { value: "mx", label: "México" },
508
+ { value: "co", label: "Colombia" },
509
+ { value: "ar", label: "Argentina" },
510
+ ];
511
+ return (
512
+ <Box sx={{ width: 300 }}>
513
+ <Autocomplete
514
+ label="País"
515
+ placeholder="Buscar país..."
516
+ options={countries}
517
+ value={value}
518
+ onChange={(val) => setValue(val as string | null)}
519
+ />
520
+ <Typography sx={{ mt: 2 }} variant="caption" color="text.secondary">
521
+ Al estar vacío y sin foco, solo se ve el label como placeholder. Al
522
+ enfocar, el label sube y aparece el placeholder real.
523
+ </Typography>
524
+ </Box>
525
+ );
526
+ },
527
+ parameters: {
528
+ docs: {
529
+ description: {
530
+ story:
531
+ "Autocomplete vacío con placeholder. El placeholder solo se muestra cuando el campo está enfocado (mismo patrón que Select/Input).",
532
+ },
533
+ source: { code: EmptyWithPlaceholderAutocompleteDefinition.trim() },
534
+ },
535
+ },
536
+ };
537
+
538
+ export const WithReactHookForm: Story = {
539
+ render: () => {
540
+ type FormValues = { country: string | null };
541
+ const { control, handleSubmit, watch } = useForm<FormValues>({
542
+ defaultValues: { country: null },
543
+ });
544
+ const countries: SelectOption[] = [
545
+ { value: "mx", label: "México" },
546
+ { value: "co", label: "Colombia" },
547
+ { value: "ar", label: "Argentina" },
548
+ ];
549
+
550
+ const [submitted, setSubmitted] = useState<FormValues | null>(null);
551
+
552
+ return (
553
+ <Box
554
+ component="form"
555
+ onSubmit={handleSubmit((data) => setSubmitted(data))}
556
+ >
557
+ <Stack spacing={2} sx={{ width: 320 }}>
558
+ <Autocomplete
559
+ name="country"
560
+ control={control}
561
+ validation={{ required: "Campo requerido" }}
562
+ label="País"
563
+ placeholder="Seleccione..."
564
+ options={countries}
565
+ />
566
+ <Button type="submit" variant="contained">
567
+ Guardar
568
+ </Button>
569
+ <Typography variant="caption" color="text.secondary">
570
+ Valor actual (watch): {JSON.stringify(watch("country"))}
571
+ </Typography>
572
+ {submitted && (
573
+ <Typography variant="caption" color="success.main">
574
+ Submit: {JSON.stringify(submitted)}
575
+ </Typography>
576
+ )}
577
+ </Stack>
578
+ </Box>
579
+ );
580
+ },
581
+ parameters: {
582
+ docs: {
583
+ description: {
584
+ story:
585
+ "Integración con React Hook Form usando `name`/`control`/`validation`. El error de validación se renderiza automáticamente como helperText.",
586
+ },
587
+ source: { code: RHFAutocompleteDefinition.trim() },
588
+ },
589
+ },
590
+ };
591
+
592
+ // =============================================================================
593
+ // ASYNC — búsqueda remota contra un servicio
594
+ // =============================================================================
595
+ // Simulador de servicio: hace una "llamada" a una API fake con latencia.
596
+ const mockUsersDb: SelectOption[] = [
597
+ { value: 1, label: "Andrea García" },
598
+ { value: 2, label: "Andrés Pérez" },
599
+ { value: 3, label: "Beatriz López" },
600
+ { value: 4, label: "Carlos Ruiz" },
601
+ { value: 5, label: "Camila Torres" },
602
+ { value: 6, label: "Diego Fernández" },
603
+ { value: 7, label: "Elena Morales" },
604
+ { value: 8, label: "Fabián Núñez" },
605
+ ];
606
+
607
+ const fakeFetchUsers = (query: string): Promise<SelectOption[]> =>
608
+ new Promise((resolve) => {
609
+ setTimeout(() => {
610
+ if (!query) return resolve([]);
611
+ const q = query.toLowerCase();
612
+ resolve(mockUsersDb.filter((u) => u.label.toLowerCase().includes(q)));
613
+ }, 400);
614
+ });
615
+
616
+ // Debounce casero (para no agregar lodash como dep de la story).
617
+ function debounce<T extends (...args: any[]) => void>(fn: T, ms: number) {
618
+ let t: ReturnType<typeof setTimeout>;
619
+ return (...args: Parameters<T>) => {
620
+ clearTimeout(t);
621
+ t = setTimeout(() => fn(...args), ms);
622
+ };
623
+ }
624
+
625
+ export const AsyncService: Story = {
626
+ render: () => {
627
+ const [value, setValue] = useState<number | null>(null);
628
+ const [input, setInput] = useState("");
629
+ const [options, setOptions] = useState<SelectOption[]>([]);
630
+ const [loading, setLoading] = useState(false);
631
+
632
+ const search = useMemo(
633
+ () =>
634
+ debounce(async (q: string) => {
635
+ if (!q) {
636
+ setOptions([]);
637
+ return;
638
+ }
639
+ setLoading(true);
640
+ try {
641
+ const res = await fakeFetchUsers(q);
642
+ setOptions(res);
643
+ } finally {
644
+ setLoading(false);
645
+ }
646
+ }, 300),
647
+ [],
648
+ );
649
+
650
+ useEffect(() => {
651
+ search(input);
652
+ }, [input, search]);
653
+
654
+ return (
655
+ <Box sx={{ width: 320 }}>
656
+ <Autocomplete<number>
657
+ label="Usuario"
658
+ placeholder="Buscar usuario..."
659
+ options={options}
660
+ value={value}
661
+ onChange={(v) => setValue(v as number | null)}
662
+ inputValue={input}
663
+ onInputChange={(_, v) => setInput(v)}
664
+ loading={loading}
665
+ // Desactiva filtro cliente: confiamos en el servicio.
666
+ filterOptions={(x) => x}
667
+ />
668
+ <Typography sx={{ mt: 2 }} variant="caption" color="text.secondary">
669
+ {"Escribe para buscar. El servicio se consulta con debounce (300ms). filterOptions={(x) => x} desactiva el filtro cliente."}
670
+ </Typography>
671
+ </Box>
672
+ );
673
+ },
674
+ parameters: {
675
+ docs: {
676
+ description: {
677
+ story:
678
+ "Búsqueda asíncrona contra un servicio. El consumer maneja el estado de `options`/`loading` y reacciona a `onInputChange` para llamar al servicio. Usar `filterOptions={(x) => x}` para desactivar el filtro cliente.",
679
+ },
680
+ source: { code: AsyncServiceAutocompleteDefinition.trim() },
681
+ },
682
+ },
683
+ };
684
+
685
+ export const AsyncServiceMultiple: Story = {
686
+ render: () => {
687
+ const [value, setValue] = useState<number[]>([]);
688
+ const [input, setInput] = useState("");
689
+ const [options, setOptions] = useState<SelectOption[]>([]);
690
+ const [loading, setLoading] = useState(false);
691
+
692
+ const search = useMemo(
693
+ () =>
694
+ debounce(async (q: string) => {
695
+ if (!q) {
696
+ setOptions([]);
697
+ return;
698
+ }
699
+ setLoading(true);
700
+ try {
701
+ const res = await fakeFetchUsers(q);
702
+ setOptions(res);
703
+ } finally {
704
+ setLoading(false);
705
+ }
706
+ }, 300),
707
+ [],
708
+ );
709
+
710
+ useEffect(() => {
711
+ search(input);
712
+ }, [input, search]);
713
+
714
+ return (
715
+ <Box sx={{ width: 420 }}>
716
+ <Autocomplete<number>
717
+ multiple
718
+ maxChipsToShow={4}
719
+ label="Usuarios"
720
+ placeholder="Buscar y seleccionar varios..."
721
+ options={options}
722
+ value={value}
723
+ onChange={(v) => setValue(v as number[])}
724
+ inputValue={input}
725
+ onInputChange={(_, v) => setInput(v)}
726
+ loading={loading}
727
+ filterOptions={(x) => x}
728
+ />
729
+ <Typography sx={{ mt: 2 }} variant="caption" color="text.secondary">
730
+ Selecciona varios usuarios buscando con diferentes queries. Los chips
731
+ persisten aunque el search cambie las `options` (cache interno).
732
+ </Typography>
733
+ <Typography sx={{ mt: 1 }} variant="caption" display="block">
734
+ Seleccionados: {JSON.stringify(value)}
735
+ </Typography>
736
+ </Box>
737
+ );
738
+ },
739
+ parameters: {
740
+ docs: {
741
+ description: {
742
+ story:
743
+ "Búsqueda asíncrona **con selección múltiple**. El componente mantiene un cache interno de opciones ya vistas, así los chips de los items seleccionados NO desaparecen cuando el usuario busca otros términos. Flujo: buscar 'an' → seleccionar Andrea → buscar 'be' → seleccionar Beatriz → los dos chips siguen visibles.",
744
+ },
745
+ source: { code: AsyncServiceAutocompleteDefinition.trim() },
746
+ },
747
+ },
748
+ };
@@ -0,0 +1,30 @@
1
+ import type { SxProps, Theme } from '@mui/material/styles';
2
+
3
+ import { buildFormFieldSx } from '../_shared/formField.sx';
4
+ import type { LabelPosition } from './Autocomplete';
5
+
6
+ /**
7
+ * Builder del sx del Autocomplete. Reutiliza `buildFormFieldSx` y añade
8
+ * los paddings propios del Autocomplete (chips + input interno).
9
+ */
10
+ export const buildAutocompleteSx = (
11
+ borderRadius: number | string,
12
+ labelPosition: LabelPosition,
13
+ ): SxProps<Theme> =>
14
+ buildFormFieldSx({
15
+ borderRadius,
16
+ labelPosition,
17
+ extraOutsideSx: {
18
+ // El input interno del Autocomplete.
19
+ '& .MuiAutocomplete-input': {
20
+ paddingTop: '4px',
21
+ paddingBottom: '4px',
22
+ },
23
+
24
+ // Contenedor del input (donde viven los chips en modo multiple).
25
+ '& .MuiAutocomplete-inputRoot': {
26
+ paddingTop: '4px',
27
+ paddingBottom: '4px',
28
+ },
29
+ },
30
+ });