@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,7 +1,24 @@
1
- import React, { useState, useImperativeHandle, forwardRef } from 'react'; // 👈 Importa useImperativeHandle y forwardRef
2
- import { Modal as MuiModal, Box, Paper, useTheme, useMediaQuery } from '@mui/material';
1
+ import React, {
2
+ useState,
3
+ useImperativeHandle,
4
+ forwardRef,
5
+ type ReactNode,
6
+ } from 'react';
7
+ import {
8
+ Modal as MuiModal,
9
+ Paper,
10
+ useTheme,
11
+ useMediaQuery,
12
+ Box,
13
+ Stack,
14
+ Typography,
15
+ } from '@mui/material';
16
+ import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
17
+ import WarningAmberIcon from '@mui/icons-material/WarningAmber';
18
+ import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
19
+ import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
3
20
 
4
- import { DialogProps } from '@mui/material/Dialog';
21
+ import type { DialogProps } from '@mui/material/Dialog';
5
22
  import { ModalAction, ModalFooter, ModalFooterProps } from './ModalFooter';
6
23
  import { ModalHeader } from './ModalHeader';
7
24
  import { ModalBody } from './ModalBody';
@@ -12,153 +29,318 @@ export interface ModalRef {
12
29
  close: () => void;
13
30
  }
14
31
 
32
+ export type ModalMode = 'default' | 'confirm';
33
+ export type ModalSeverity = 'info' | 'warning' | 'error' | 'success';
34
+
15
35
  interface ModalProps {
16
- // La prop 'open' ahora es opcional si se usa con ref, pero aún puede ser controlada externamente
36
+ /**
37
+ * Modo del modal.
38
+ * - `default` (default): modal genérico con slots `Header`/`Body`/`Footer` y
39
+ * `actions` custom.
40
+ * - `confirm`: reemplaza al `ConfirmModal` legacy. Renderiza un layout
41
+ * focalizado con icono por severidad, mensaje y botones "Cancelar" /
42
+ * "Confirmar".
43
+ */
44
+ mode?: ModalMode;
45
+
46
+ // ── Props comunes ────────────────────────────────────────────────────
47
+ /** Controlado externamente. Omitir si se usa vía ref. */
17
48
  open?: boolean;
18
- onClose?: () => void; // 'onClose' ahora es opcional ya que el modal puede cerrarse internamente
49
+ /** Callback al cerrar. También se dispara al confirmar/cancelar. */
50
+ onClose?: () => void;
19
51
  title?: string;
20
- children: React.ReactNode;
52
+ children?: ReactNode;
21
53
  showCloseButton?: boolean;
22
54
  closeButtonText?: string;
23
55
  closeButtonDisabled?: boolean;
24
56
  actions?: ModalAction[];
25
57
  maxWidth?: DialogProps['maxWidth'];
26
- hiddenFooter?: boolean
58
+ hiddenFooter?: boolean;
59
+
60
+ // ── Props del modo confirm ───────────────────────────────────────────
61
+ /**
62
+ * Callback de confirmación. Soporta promesa — si devuelve `Promise`, el
63
+ * botón muestra estado `disabled` mientras resuelve.
64
+ */
65
+ onConfirm?: () => void | Promise<void>;
66
+ /** Texto del botón primario en modo `confirm`. Default: "Confirmar". */
67
+ confirmText?: string;
68
+ /** Deshabilita el botón de confirmar. */
69
+ confirmDisabled?: boolean;
70
+ /**
71
+ * Severidad visual en modo `confirm`. Controla el icono y el color del
72
+ * botón de confirmación.
73
+ * - `info` (default): primary
74
+ * - `warning`: warning (amarillo)
75
+ * - `error`: error (rojo) — típico para "Eliminar"
76
+ * - `success`: success (verde) — típico para "Aprobar"
77
+ */
78
+ severity?: ModalSeverity;
79
+ /**
80
+ * Mensaje del confirm. Puede ser string o cualquier ReactNode. Si se omite,
81
+ * se usa `children`.
82
+ */
83
+ confirmMessage?: ReactNode;
27
84
  }
28
85
 
29
86
  const modalStyle = {
30
- position: 'absolute' as 'absolute',
87
+ position: 'absolute' as const,
31
88
  top: '50%',
32
89
  left: '50%',
33
90
  transform: 'translate(-50%, -50%)',
34
91
  width: '90%',
35
92
  maxHeight: '90vh',
36
93
  display: 'flex',
37
- flexDirection: 'column',
94
+ flexDirection: 'column' as const,
38
95
  outline: 'none',
39
96
  };
40
97
 
41
- // Envuelve el componente en forwardRef
42
- export const Modal = forwardRef<ModalRef, ModalProps>(({
43
- open: controlledOpen, // Renombra la prop 'open' si se pasa externamente
44
- onClose: controlledOnClose, // Renombra la prop 'onClose' si se pasa externamente
45
- title,
46
- children,
47
- showCloseButton = true,
48
- closeButtonText = "Cerrar",
49
- closeButtonDisabled = false,
50
- actions = [],
51
- maxWidth = 'sm',
52
- hiddenFooter = false,
53
- }, ref) => { // Recibe la ref
54
- // Estado interno para gestionar la visibilidad si no es controlado externamente
55
- const [internalOpen, setInternalOpen] = useState(false);
56
-
57
- // Determina si el modal está abierto, priorizando la prop externa
58
- const isOpen = controlledOpen !== undefined ? controlledOpen : internalOpen;
59
-
60
- // Expone métodos 'open' y 'close' a través de la ref
61
- useImperativeHandle(ref, () => ({
62
- open: () => setInternalOpen(true),
63
- close: () => {
64
- setInternalOpen(false);
65
- controlledOnClose?.(); // Llama al onClose externo si existe
98
+ const severityConfig: Record<
99
+ ModalSeverity,
100
+ { color: ModalAction['color']; icon: ReactNode; bg: string }
101
+ > = {
102
+ info: {
103
+ color: 'primary',
104
+ icon: <InfoOutlinedIcon sx={{ fontSize: 48 }} />,
105
+ bg: 'primary.light',
106
+ },
107
+ warning: {
108
+ color: 'warning',
109
+ icon: <WarningAmberIcon sx={{ fontSize: 48 }} />,
110
+ bg: 'warning.light',
111
+ },
112
+ error: {
113
+ color: 'error',
114
+ icon: <ErrorOutlineIcon sx={{ fontSize: 48 }} />,
115
+ bg: 'error.light',
116
+ },
117
+ success: {
118
+ color: 'success',
119
+ icon: <CheckCircleOutlineIcon sx={{ fontSize: 48 }} />,
120
+ bg: 'success.light',
121
+ },
122
+ };
123
+
124
+ export const Modal = forwardRef<ModalRef, ModalProps>(
125
+ (
126
+ {
127
+ mode = 'default',
128
+ open: controlledOpen,
129
+ onClose: controlledOnClose,
130
+ title,
131
+ children,
132
+ showCloseButton = true,
133
+ closeButtonText = 'Cerrar',
134
+ closeButtonDisabled = false,
135
+ actions = [],
136
+ maxWidth = 'sm',
137
+ hiddenFooter = false,
138
+
139
+ // Props del modo confirm
140
+ onConfirm,
141
+ confirmText = 'Confirmar',
142
+ confirmDisabled = false,
143
+ severity = 'info',
144
+ confirmMessage,
66
145
  },
67
- }));
68
-
69
- const handleCloseInternal = () => {
70
- setInternalOpen(false);
71
- controlledOnClose?.(); // Llama al onClose externo si existe
72
- };
73
-
74
- const theme = useTheme();
75
- const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
76
-
77
- const getWidth = () => {
78
- if (isMobile) return '95%';
79
- switch (maxWidth) {
80
- case 'xs': return 300;
81
- case 'sm': return 500;
82
- case 'md': return 700;
83
- case 'lg': return 900;
84
- case 'xl': return 1100;
85
- case false: return 'auto';
86
- default: return 500;
87
- }
88
- };
89
-
90
- const renderChildren = () => {
91
- let header: React.ReactNode | null = null;
92
- let body: React.ReactNode | null = null;
93
- let footer: React.ReactNode | null = null;
94
-
95
- React.Children.forEach(children, (child) => {
96
- if (React.isValidElement(child)) {
97
- if (child.type === ModalHeader) {
98
- header = child;
99
- } else if (child.type === ModalBody) {
100
- body = child;
101
- } else if (child.type === ModalFooter) {
102
- const footerChild = child as React.ReactElement<ModalFooterProps>;
103
- const {
104
- showCloseButton: childShowCloseButton,
105
- closeButtonText: childCloseButtonText,
106
- closeButtonDisabled: childCloseButtonDisabled,
107
- onClose: childOnClose,
108
- actions: childActions,
109
- ...restOfFooterProps
110
- } = footerChild.props;
111
-
112
- footer = React.cloneElement(footerChild, {
113
- showCloseButton,
114
- closeButtonText,
115
- closeButtonDisabled,
116
- onClose: handleCloseInternal, // Usa la función de cierre interna
117
- actions,
118
- ...restOfFooterProps,
119
- });
146
+ ref,
147
+ ) => {
148
+ const [internalOpen, setInternalOpen] = useState(false);
149
+ const [confirmLoading, setConfirmLoading] = useState(false);
150
+
151
+ const isOpen = controlledOpen !== undefined ? controlledOpen : internalOpen;
152
+
153
+ useImperativeHandle(ref, () => ({
154
+ open: () => setInternalOpen(true),
155
+ close: () => {
156
+ setInternalOpen(false);
157
+ controlledOnClose?.();
158
+ },
159
+ }));
160
+
161
+ const handleCloseInternal = () => {
162
+ setInternalOpen(false);
163
+ controlledOnClose?.();
164
+ };
165
+
166
+ const handleConfirm = async () => {
167
+ if (!onConfirm) {
168
+ handleCloseInternal();
169
+ return;
170
+ }
171
+ try {
172
+ const result = onConfirm();
173
+ if (result instanceof Promise) {
174
+ setConfirmLoading(true);
175
+ await result;
120
176
  }
177
+ } finally {
178
+ setConfirmLoading(false);
179
+ handleCloseInternal();
121
180
  }
122
- });
123
-
124
- if (!footer && !hiddenFooter) {
125
- footer = (
126
- <ModalFooter
127
- showCloseButton={showCloseButton}
128
- closeButtonText={closeButtonText}
129
- closeButtonDisabled={closeButtonDisabled}
130
- onClose={handleCloseInternal} // Usa la función de cierre interna
131
- actions={actions}
132
- />
181
+ };
182
+
183
+ const theme = useTheme();
184
+ const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
185
+
186
+ const getWidth = () => {
187
+ if (isMobile) return '95%';
188
+ switch (maxWidth) {
189
+ case 'xs':
190
+ return 300;
191
+ case 'sm':
192
+ return 500;
193
+ case 'md':
194
+ return 700;
195
+ case 'lg':
196
+ return 900;
197
+ case 'xl':
198
+ return 1100;
199
+ case false:
200
+ return 'auto';
201
+ default:
202
+ return 500;
203
+ }
204
+ };
205
+
206
+ // ── Render modo CONFIRM ─────────────────────────────────────────────
207
+ if (mode === 'confirm') {
208
+ const config = severityConfig[severity];
209
+ const message = confirmMessage ?? children;
210
+
211
+ return (
212
+ <MuiModal
213
+ open={isOpen}
214
+ onClose={handleCloseInternal}
215
+ aria-labelledby="modal-title"
216
+ closeAfterTransition
217
+ >
218
+ <Paper sx={{ ...modalStyle, width: getWidth() }}>
219
+ <Stack spacing={2.5} sx={{ p: 3, alignItems: 'center', textAlign: 'center' }}>
220
+ <Box
221
+ sx={{
222
+ width: 72,
223
+ height: 72,
224
+ borderRadius: '50%',
225
+ backgroundColor: (t) =>
226
+ t.palette[severity === 'info' ? 'primary' : severity].light,
227
+ color: (t) =>
228
+ t.palette[severity === 'info' ? 'primary' : severity].dark,
229
+ display: 'flex',
230
+ alignItems: 'center',
231
+ justifyContent: 'center',
232
+ opacity: 0.9,
233
+ }}
234
+ >
235
+ {config.icon}
236
+ </Box>
237
+ {title && (
238
+ <Typography variant="h6" component="h2" sx={{ fontWeight: 700 }}>
239
+ {title}
240
+ </Typography>
241
+ )}
242
+ {message && (
243
+ <Typography variant="body2" color="text.secondary">
244
+ {message}
245
+ </Typography>
246
+ )}
247
+ </Stack>
248
+ <ModalFooter
249
+ showCloseButton={showCloseButton}
250
+ closeButtonText={closeButtonText}
251
+ closeButtonDisabled={closeButtonDisabled || confirmLoading}
252
+ onClose={handleCloseInternal}
253
+ actions={[
254
+ {
255
+ text: confirmText,
256
+ onClick: handleConfirm,
257
+ disabled: confirmDisabled || confirmLoading,
258
+ variant: 'contained',
259
+ color: config.color,
260
+ },
261
+ ]}
262
+ />
263
+ </Paper>
264
+ </MuiModal>
133
265
  );
134
266
  }
135
267
 
268
+ // ── Render modo DEFAULT (legacy) ────────────────────────────────────
269
+ const renderChildren = () => {
270
+ let header: ReactNode | null = null;
271
+ let body: ReactNode | null = null;
272
+ let footer: ReactNode | null = null;
273
+
274
+ React.Children.forEach(children, (child) => {
275
+ if (React.isValidElement(child)) {
276
+ if (child.type === ModalHeader) {
277
+ header = child;
278
+ } else if (child.type === ModalBody) {
279
+ body = child;
280
+ } else if (child.type === ModalFooter) {
281
+ const footerChild = child as React.ReactElement<ModalFooterProps>;
282
+ const {
283
+ showCloseButton: childShowCloseButton,
284
+ closeButtonText: childCloseButtonText,
285
+ closeButtonDisabled: childCloseButtonDisabled,
286
+ onClose: childOnClose,
287
+ actions: childActions,
288
+ ...restOfFooterProps
289
+ } = footerChild.props;
290
+
291
+ footer = React.cloneElement(footerChild, {
292
+ showCloseButton,
293
+ closeButtonText,
294
+ closeButtonDisabled,
295
+ onClose: handleCloseInternal,
296
+ actions,
297
+ ...restOfFooterProps,
298
+ });
299
+ }
300
+ }
301
+ });
302
+
303
+ if (!footer && !hiddenFooter) {
304
+ footer = (
305
+ <ModalFooter
306
+ showCloseButton={showCloseButton}
307
+ closeButtonText={closeButtonText}
308
+ closeButtonDisabled={closeButtonDisabled}
309
+ onClose={handleCloseInternal}
310
+ actions={actions}
311
+ />
312
+ );
313
+ }
314
+
315
+ return (
316
+ <>
317
+ {header || (title && <ModalHeader>{title}</ModalHeader>)}
318
+ {body}
319
+ {footer}
320
+ </>
321
+ );
322
+ };
323
+
136
324
  return (
137
- <>
138
- {header || (title && <ModalHeader>{title}</ModalHeader>)}
139
- {body}
140
- {footer}
141
- </>
325
+ <MuiModal
326
+ open={isOpen}
327
+ onClose={handleCloseInternal}
328
+ aria-labelledby="modal-title"
329
+ aria-describedby="modal-description"
330
+ closeAfterTransition
331
+ >
332
+ <Paper sx={{ ...modalStyle, width: getWidth() }}>
333
+ {renderChildren()}
334
+ </Paper>
335
+ </MuiModal>
142
336
  );
143
- };
144
-
145
- return (
146
- <MuiModal
147
- open={isOpen} // Usa el estado de visibilidad determinado
148
- onClose={handleCloseInternal} // Usa la función de cierre interna
149
- aria-labelledby="modal-title"
150
- aria-describedby="modal-description"
151
- closeAfterTransition
152
- >
153
- <Paper sx={{ ...modalStyle, width: getWidth() }}>
154
- {renderChildren()}
155
- </Paper>
156
- </MuiModal>
157
- );
158
- });
337
+ },
338
+ );
159
339
 
160
340
  // Define los sub-componentes como propiedades estáticas con tipos explícitos
161
- type ModalComponent = React.ForwardRefExoticComponent<ModalProps & React.RefAttributes<ModalRef>> & {
341
+ type ModalComponent = React.ForwardRefExoticComponent<
342
+ ModalProps & React.RefAttributes<ModalRef>
343
+ > & {
162
344
  Header: typeof ModalHeader;
163
345
  Body: typeof ModalBody;
164
346
  Footer: typeof ModalFooter;
@@ -170,4 +352,4 @@ ModalWithStatics.Header = ModalHeader;
170
352
  ModalWithStatics.Body = ModalBody;
171
353
  ModalWithStatics.Footer = ModalFooter;
172
354
 
173
- export default ModalWithStatics;
355
+ export default ModalWithStatics;
@@ -5,10 +5,16 @@ import { ButtonProps } from '@mui/material/Button';
5
5
  // Interfaz para acciones personalizadas (se mantiene aquí)
6
6
  export interface ModalAction {
7
7
  text: string;
8
- onClick: () => void;
8
+ onClick?: () => void;
9
9
  disabled?: boolean;
10
10
  variant?: ButtonProps['variant'];
11
11
  color?: ButtonProps['color'];
12
+ /**
13
+ * Props adicionales que se forwardean al `<Button>` interno. Útil para casos
14
+ * avanzados como conectar el botón con un `<form id="...">` externo usando
15
+ * `buttonProps={{ type: 'submit', form: 'my-form-id' }}`.
16
+ */
17
+ buttonProps?: Partial<ButtonProps> & { form?: string };
12
18
  }
13
19
 
14
20
  export interface ModalFooterProps { // Renombrado
@@ -42,17 +48,21 @@ export const ModalFooter: React.FC<ModalFooterProps> = ({ // Renombrado
42
48
  {closeButtonText}
43
49
  </Button>
44
50
  )}
45
- {actions.map((action, index) => (
46
- <Button
47
- key={index}
48
- onClick={action.onClick}
49
- disabled={action.disabled}
50
- variant={action.variant || 'contained'}
51
- color={action.color || 'primary'}
52
- >
53
- {action.text}
54
- </Button>
55
- ))}
51
+ {actions.map((action, index) => {
52
+ const { buttonProps } = action;
53
+ return (
54
+ <Button
55
+ key={index}
56
+ onClick={action.onClick}
57
+ disabled={action.disabled}
58
+ variant={action.variant || 'contained'}
59
+ color={action.color || 'primary'}
60
+ {...(buttonProps as any)}
61
+ >
62
+ {action.text}
63
+ </Button>
64
+ );
65
+ })}
56
66
  </Stack>
57
67
  </Box>
58
68
  );
@@ -1 +1,6 @@
1
- export {default as Modal } from './Modal';
1
+ export { default as Modal } from './Modal';
2
+ export type { ModalRef, ModalMode, ModalSeverity } from './Modal';
3
+ export { ModalHeader } from './ModalHeader';
4
+ export { ModalBody } from './ModalBody';
5
+ export { ModalFooter } from './ModalFooter';
6
+ export type { ModalAction, ModalFooterProps } from './ModalFooter';