@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
@@ -0,0 +1,120 @@
1
+ import React, { type ReactNode, type MouseEvent } from 'react';
2
+ import { Box, Typography } from '@mui/material';
3
+ import type { SxProps, Theme } from '@mui/material/styles';
4
+
5
+ export interface AppBarBrandProps {
6
+ /** Logo (img, svg, componente). Se muestra a la izquierda del título. */
7
+ logo?: ReactNode;
8
+ /** Título del sistema. */
9
+ title?: ReactNode;
10
+ /** Subtítulo opcional (p.ej. nombre de módulo actual). */
11
+ subtitle?: ReactNode;
12
+ /** Handler de click sobre todo el brand (típicamente navega al home). */
13
+ onClick?: (event: MouseEvent<HTMLDivElement>) => void;
14
+ /** Oculta solo el título en viewports pequeños (mantiene el logo). Default: true. */
15
+ hideTitleOnMobile?: boolean;
16
+ sx?: SxProps<Theme>;
17
+ className?: string;
18
+ }
19
+
20
+ /**
21
+ * Brand del AppBar: combina logo + título + subtítulo opcional con un
22
+ * click handler global (típicamente para navegar al home).
23
+ *
24
+ * ```tsx
25
+ * <AppBarBrand
26
+ * logo={<img src={logo} alt="" height={32} />}
27
+ * title="Afiliaciones"
28
+ * subtitle="Panel administrativo"
29
+ * onClick={() => navigate('/')}
30
+ * />
31
+ * ```
32
+ */
33
+ export function AppBarBrand({
34
+ logo,
35
+ title,
36
+ subtitle,
37
+ onClick,
38
+ hideTitleOnMobile = true,
39
+ sx,
40
+ className,
41
+ }: AppBarBrandProps) {
42
+ const clickable = !!onClick;
43
+
44
+ const rootSx: SxProps<Theme> = [
45
+ {
46
+ display: 'flex',
47
+ alignItems: 'center',
48
+ gap: 1.25,
49
+ cursor: clickable ? 'pointer' : 'default',
50
+ userSelect: 'none',
51
+ minWidth: 0,
52
+ },
53
+ ...(Array.isArray(sx) ? sx : sx ? [sx] : []),
54
+ ];
55
+
56
+ return (
57
+ <Box
58
+ role={clickable ? 'button' : undefined}
59
+ tabIndex={clickable ? 0 : undefined}
60
+ onClick={onClick}
61
+ onKeyDown={(event) => {
62
+ if (!clickable) return;
63
+ if (event.key === 'Enter' || event.key === ' ') {
64
+ event.preventDefault();
65
+ onClick?.(event as unknown as MouseEvent<HTMLDivElement>);
66
+ }
67
+ }}
68
+ className={className}
69
+ sx={rootSx}
70
+ >
71
+ {logo && (
72
+ <Box
73
+ component="span"
74
+ sx={{
75
+ display: 'flex',
76
+ alignItems: 'center',
77
+ flexShrink: 0,
78
+ }}
79
+ >
80
+ {logo}
81
+ </Box>
82
+ )}
83
+ {(title || subtitle) && (
84
+ <Box
85
+ sx={{
86
+ display: hideTitleOnMobile
87
+ ? { xs: 'none', sm: 'flex' }
88
+ : 'flex',
89
+ flexDirection: 'column',
90
+ minWidth: 0,
91
+ lineHeight: 1.2,
92
+ }}
93
+ >
94
+ {title && (
95
+ <Typography
96
+ component="span"
97
+ variant="subtitle1"
98
+ noWrap
99
+ sx={{ fontWeight: 700 }}
100
+ >
101
+ {title}
102
+ </Typography>
103
+ )}
104
+ {subtitle && (
105
+ <Typography
106
+ component="span"
107
+ variant="caption"
108
+ color="text.secondary"
109
+ noWrap
110
+ >
111
+ {subtitle}
112
+ </Typography>
113
+ )}
114
+ </Box>
115
+ )}
116
+ </Box>
117
+ );
118
+ }
119
+
120
+ export default AppBarBrand;
@@ -0,0 +1,25 @@
1
+ import { createContext, useContext } from 'react';
2
+
3
+ /**
4
+ * Context interno del AppBar. Permite que sub-componentes (MenuToggle, etc.)
5
+ * consuman callbacks registrados en el AppBar padre sin tener que pasarlos
6
+ * por props explícitas.
7
+ */
8
+ export interface AppBarContextValue {
9
+ /** Handler del botón hamburguesa. Si es undefined, el toggle se oculta. */
10
+ onMenuToggle?: () => void;
11
+ /** Estado actual del drawer asociado (usado por el icono del toggle). */
12
+ menuOpen?: boolean;
13
+ }
14
+
15
+ export const AppBarContext = createContext<AppBarContextValue | null>(null);
16
+
17
+ /**
18
+ * Hook para leer el contexto del AppBar. Si se llama fuera de un `<AppBar>`,
19
+ * devuelve un objeto vacío — los sub-componentes caen en modo "standalone"
20
+ * con sus defaults en lugar de romper.
21
+ */
22
+ export function useAppBarContext(): AppBarContextValue {
23
+ const ctx = useContext(AppBarContext);
24
+ return ctx ?? {};
25
+ }
@@ -0,0 +1,90 @@
1
+ import React, { type ReactNode } from 'react';
2
+ import { IconButton, Tooltip } from '@mui/material';
3
+ import MenuIcon from '@mui/icons-material/Menu';
4
+ import MenuOpenIcon from '@mui/icons-material/MenuOpen';
5
+ import type { SxProps, Theme } from '@mui/material/styles';
6
+
7
+ import { useAppBarContext } from './AppBarContext';
8
+
9
+ export interface AppBarMenuToggleProps {
10
+ /**
11
+ * Callback explícito. Si se omite, se consume del `AppBarContext`. Útil para
12
+ * usar el toggle fuera de un `<AppBar>` (p.ej. en un header custom).
13
+ */
14
+ onClick?: () => void;
15
+ /** Estado del drawer asociado. Si se omite, se lee del `AppBarContext`. */
16
+ menuOpen?: boolean;
17
+ /** Icono custom cuando el drawer está cerrado. Default: `<MenuIcon />`. */
18
+ closedIcon?: ReactNode;
19
+ /** Icono custom cuando el drawer está abierto. Default: `<MenuOpenIcon />`. */
20
+ openIcon?: ReactNode;
21
+ /** Texto de tooltip. Default: "Menú". */
22
+ tooltip?: string;
23
+ /** aria-label del botón. Default: el mismo tooltip. */
24
+ ariaLabel?: string;
25
+ /** Tamaño del IconButton. Default: `'medium'`. */
26
+ size?: 'small' | 'medium' | 'large';
27
+ /** Oculta el botón cuando no hay handler disponible. Default: true. */
28
+ hideIfNoHandler?: boolean;
29
+ sx?: SxProps<Theme>;
30
+ className?: string;
31
+ }
32
+
33
+ /**
34
+ * Botón hamburguesa del AppBar. Por default consume `onMenuToggle` y
35
+ * `menuOpen` del `AppBarContext` (pattern composicional), pero permite
36
+ * overrides explícitos si se usa fuera de un `<AppBar>`.
37
+ *
38
+ * ```tsx
39
+ * <AppBar onMenuToggle={toggleDrawer} menuOpen={open}>
40
+ * <AppBarMenuToggle />
41
+ * ...
42
+ * </AppBar>
43
+ * ```
44
+ */
45
+ export function AppBarMenuToggle({
46
+ onClick,
47
+ menuOpen,
48
+ closedIcon,
49
+ openIcon,
50
+ tooltip = 'Menú',
51
+ ariaLabel,
52
+ size = 'medium',
53
+ hideIfNoHandler = true,
54
+ sx,
55
+ className,
56
+ }: AppBarMenuToggleProps) {
57
+ const ctx = useAppBarContext();
58
+
59
+ const handler = onClick ?? ctx.onMenuToggle;
60
+ const open = menuOpen ?? ctx.menuOpen ?? false;
61
+
62
+ if (!handler && hideIfNoHandler) {
63
+ return null;
64
+ }
65
+
66
+ const icon = open
67
+ ? (openIcon ?? <MenuOpenIcon />)
68
+ : (closedIcon ?? <MenuIcon />);
69
+
70
+ return (
71
+ <Tooltip title={tooltip} arrow>
72
+ <span>
73
+ <IconButton
74
+ size={size}
75
+ edge="start"
76
+ color="inherit"
77
+ aria-label={ariaLabel ?? tooltip}
78
+ onClick={handler}
79
+ disabled={!handler}
80
+ className={className}
81
+ sx={sx}
82
+ >
83
+ {icon}
84
+ </IconButton>
85
+ </span>
86
+ </Tooltip>
87
+ );
88
+ }
89
+
90
+ export default AppBarMenuToggle;
@@ -0,0 +1,217 @@
1
+ import React, { useState, type ReactNode, type MouseEvent } from 'react';
2
+ import {
3
+ Avatar,
4
+ Box,
5
+ Divider,
6
+ IconButton,
7
+ ListItemIcon,
8
+ ListItemText,
9
+ Menu,
10
+ MenuItem,
11
+ Tooltip,
12
+ Typography,
13
+ } from '@mui/material';
14
+ import type { SxProps, Theme } from '@mui/material/styles';
15
+
16
+ export interface AppBarUserMenuItem {
17
+ /** Key única. Si no se provee, se usa el label. */
18
+ key?: string;
19
+ /** Texto del item. */
20
+ label: ReactNode;
21
+ /** Icono opcional a la izquierda. */
22
+ icon?: ReactNode;
23
+ /** Handler de click. */
24
+ onClick?: (event: MouseEvent<HTMLLIElement>) => void;
25
+ /** Deshabilita el item. */
26
+ disabled?: boolean;
27
+ /** Pinta el item en color error (típico para "Cerrar sesión"). */
28
+ danger?: boolean;
29
+ /** Inserta un `<Divider />` ANTES de este item. */
30
+ dividerBefore?: boolean;
31
+ }
32
+
33
+ export interface AppBarUserMenuUser {
34
+ /** Nombre a mostrar en el header del menú. */
35
+ name?: ReactNode;
36
+ /** Email / subtítulo secundario. */
37
+ email?: ReactNode;
38
+ /** URL del avatar. Si se omite, se muestran iniciales del `name`. */
39
+ avatarUrl?: string;
40
+ /** Iniciales custom. Si se omite, se calculan del `name`. */
41
+ initials?: string;
42
+ }
43
+
44
+ export interface AppBarUserMenuProps {
45
+ /** Datos del usuario a mostrar. */
46
+ user?: AppBarUserMenuUser;
47
+ /** Items del menú. */
48
+ items: AppBarUserMenuItem[];
49
+ /**
50
+ * Si `true`, oculta la fila con nombre + email en el tope del menú.
51
+ * Default: `false`.
52
+ */
53
+ hideUserHeader?: boolean;
54
+ /** Tooltip del trigger. Default: "Cuenta". */
55
+ triggerTooltip?: string;
56
+ /** Tamaño del Avatar en px. Default: 36. */
57
+ avatarSize?: number;
58
+ /** sx del Paper del Menu. */
59
+ menuSx?: SxProps<Theme>;
60
+ /** sx del trigger (IconButton). */
61
+ triggerSx?: SxProps<Theme>;
62
+ className?: string;
63
+ }
64
+
65
+ function computeInitials(name?: ReactNode): string {
66
+ if (typeof name !== 'string') return '?';
67
+ const parts = name.trim().split(/\s+/).filter(Boolean);
68
+ if (parts.length === 0) return '?';
69
+ const first = parts[0]?.charAt(0) ?? '';
70
+ const last = parts.length > 1 ? (parts[parts.length - 1]?.charAt(0) ?? '') : '';
71
+ return (first + last).toUpperCase();
72
+ }
73
+
74
+ /**
75
+ * Menú de usuario del AppBar: avatar clickable que despliega un menú con la
76
+ * info del usuario (nombre + email) y una lista de acciones (perfil, cerrar
77
+ * sesión, etc).
78
+ *
79
+ * ```tsx
80
+ * <AppBarUserMenu
81
+ * user={{ name: 'Andrea', email: 'a@soyfri.com' }}
82
+ * items={[
83
+ * { label: 'Perfil', icon: <PersonIcon />, onClick: goToProfile },
84
+ * { label: 'Cerrar sesión', icon: <LogoutIcon />, onClick: logout, danger: true, dividerBefore: true },
85
+ * ]}
86
+ * />
87
+ * ```
88
+ */
89
+ export function AppBarUserMenu({
90
+ user,
91
+ items,
92
+ hideUserHeader = false,
93
+ triggerTooltip = 'Cuenta',
94
+ avatarSize = 36,
95
+ menuSx,
96
+ triggerSx,
97
+ className,
98
+ }: AppBarUserMenuProps) {
99
+ const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
100
+ const open = Boolean(anchorEl);
101
+
102
+ const handleOpen = (event: MouseEvent<HTMLElement>) => {
103
+ setAnchorEl(event.currentTarget);
104
+ };
105
+ const handleClose = () => setAnchorEl(null);
106
+
107
+ const handleItemClick = (
108
+ event: MouseEvent<HTMLLIElement>,
109
+ item: AppBarUserMenuItem,
110
+ ) => {
111
+ event.stopPropagation();
112
+ item.onClick?.(event);
113
+ handleClose();
114
+ };
115
+
116
+ const initials = user?.initials ?? computeInitials(user?.name);
117
+ const showUserHeader = !hideUserHeader && (user?.name || user?.email);
118
+
119
+ return (
120
+ <Box component="span" className={className}>
121
+ <Tooltip title={triggerTooltip} arrow>
122
+ <IconButton
123
+ onClick={handleOpen}
124
+ size="small"
125
+ aria-label={triggerTooltip}
126
+ aria-haspopup="menu"
127
+ aria-expanded={open || undefined}
128
+ sx={[{ p: 0.5 }, ...(Array.isArray(triggerSx) ? triggerSx : triggerSx ? [triggerSx] : [])]}
129
+ >
130
+ <Avatar
131
+ src={user?.avatarUrl}
132
+ sx={{
133
+ width: avatarSize,
134
+ height: avatarSize,
135
+ fontSize: avatarSize * 0.4,
136
+ fontWeight: 600,
137
+ }}
138
+ >
139
+ {!user?.avatarUrl && initials}
140
+ </Avatar>
141
+ </IconButton>
142
+ </Tooltip>
143
+
144
+ <Menu
145
+ anchorEl={anchorEl}
146
+ open={open}
147
+ onClose={handleClose}
148
+ anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
149
+ transformOrigin={{ vertical: 'top', horizontal: 'right' }}
150
+ slotProps={{
151
+ paper: {
152
+ sx: { minWidth: 240, ...(menuSx as any) },
153
+ },
154
+ }}
155
+ >
156
+ {showUserHeader && (
157
+ <Box
158
+ sx={{
159
+ px: 2,
160
+ py: 1.25,
161
+ display: 'flex',
162
+ flexDirection: 'column',
163
+ gap: 0.25,
164
+ }}
165
+ >
166
+ {user?.name && (
167
+ <Typography variant="subtitle2" noWrap sx={{ fontWeight: 700 }}>
168
+ {user.name}
169
+ </Typography>
170
+ )}
171
+ {user?.email && (
172
+ <Typography variant="caption" color="text.secondary" noWrap>
173
+ {user.email}
174
+ </Typography>
175
+ )}
176
+ </Box>
177
+ )}
178
+ {showUserHeader && <Divider />}
179
+
180
+ {items.map((item, idx) => {
181
+ const key = item.key ?? `${String(item.label)}-${idx}`;
182
+ const node = (
183
+ <MenuItem
184
+ key={key}
185
+ disabled={item.disabled}
186
+ onClick={(event) => handleItemClick(event, item)}
187
+ sx={(theme) => ({
188
+ color: item.danger ? theme.palette.error.main : 'inherit',
189
+ '& .MuiListItemIcon-root': {
190
+ color: item.danger
191
+ ? theme.palette.error.main
192
+ : 'inherit',
193
+ minWidth: 32,
194
+ },
195
+ })}
196
+ >
197
+ {item.icon && <ListItemIcon>{item.icon}</ListItemIcon>}
198
+ <ListItemText primary={item.label} />
199
+ </MenuItem>
200
+ );
201
+
202
+ if (item.dividerBefore && idx > 0) {
203
+ return (
204
+ <React.Fragment key={`${key}-frag`}>
205
+ <Divider />
206
+ {node}
207
+ </React.Fragment>
208
+ );
209
+ }
210
+ return node;
211
+ })}
212
+ </Menu>
213
+ </Box>
214
+ );
215
+ }
216
+
217
+ export default AppBarUserMenu;
@@ -0,0 +1,25 @@
1
+ export { AppBar, default } from './AppBar';
2
+ export type {
3
+ AppBarProps,
4
+ AppBarPosition,
5
+ AppBarColor,
6
+ } from './AppBar';
7
+
8
+ export { AppBarBrand } from './AppBarBrand';
9
+ export type { AppBarBrandProps } from './AppBarBrand';
10
+
11
+ export { AppBarMenuToggle } from './AppBarMenuToggle';
12
+ export type { AppBarMenuToggleProps } from './AppBarMenuToggle';
13
+
14
+ export { AppBarUserMenu } from './AppBarUserMenu';
15
+ export type {
16
+ AppBarUserMenuProps,
17
+ AppBarUserMenuItem,
18
+ AppBarUserMenuUser,
19
+ } from './AppBarUserMenu';
20
+
21
+ export { AppBarContext, useAppBarContext } from './AppBarContext';
22
+ export type { AppBarContextValue } from './AppBarContext';
23
+
24
+ export { buildAppBarSx } from './AppBar.sx';
25
+ export type { BuildAppBarSxArgs } from './AppBar.sx';
@@ -252,3 +252,226 @@ export const ManyOptionsExample = () => {
252
252
  );
253
253
  };
254
254
  `;
255
+
256
+ // =============================================================================
257
+ // WITH ERROR / HELPER TEXT
258
+ // =============================================================================
259
+ export const WithErrorAutocompleteDefinition = `
260
+ import React, { useState } from 'react';
261
+ import Autocomplete from './Autocomplete';
262
+ import { Box } from '@mui/material';
263
+
264
+ const options = [
265
+ { value: '10', label: '10' },
266
+ { value: '25', label: '25' },
267
+ ];
268
+
269
+ export const WithErrorExample = () => {
270
+ const [value, setValue] = useState<string | null>(null);
271
+ return (
272
+ <Box sx={{ width: 300 }}>
273
+ <Autocomplete
274
+ label="Cantidad"
275
+ options={options}
276
+ value={value}
277
+ onChange={(val) => setValue(val as string | null)}
278
+ error={\!value}
279
+ helperText={\!value ? 'Debes seleccionar una cantidad' : ' '}
280
+ />
281
+ </Box>
282
+ );
283
+ };
284
+ `;
285
+
286
+ // =============================================================================
287
+ // LABEL POSITION
288
+ // =============================================================================
289
+ export const LabelPositionAutocompleteDefinition = `
290
+ import React, { useState } from 'react';
291
+ import Autocomplete from './Autocomplete';
292
+ import { Box, Stack } from '@mui/material';
293
+
294
+ const options = [
295
+ { value: 'a', label: 'Opción A' },
296
+ { value: 'b', label: 'Opción B' },
297
+ ];
298
+
299
+ export const LabelPositionExample = () => {
300
+ const [a, setA] = useState<string | null>(null);
301
+ const [b, setB] = useState<string | null>(null);
302
+ return (
303
+ <Stack spacing={3} sx={{ width: 320 }}>
304
+ <Autocomplete
305
+ label="Outside (default)"
306
+ labelPosition="outside"
307
+ options={options}
308
+ value={a}
309
+ onChange={(val) => setA(val as string | null)}
310
+ />
311
+ <Autocomplete
312
+ label="Floating"
313
+ labelPosition="floating"
314
+ options={options}
315
+ value={b}
316
+ onChange={(val) => setB(val as string | null)}
317
+ />
318
+ </Stack>
319
+ );
320
+ };
321
+ `;
322
+
323
+ // =============================================================================
324
+ // CUSTOM BORDER RADIUS
325
+ // =============================================================================
326
+ export const CustomBorderRadiusAutocompleteDefinition = `
327
+ import React, { useState } from 'react';
328
+ import Autocomplete from './Autocomplete';
329
+ import { Box, Stack } from '@mui/material';
330
+
331
+ const options = [
332
+ { value: '10', label: '10' },
333
+ { value: '25', label: '25' },
334
+ ];
335
+
336
+ export const CustomBorderRadiusExample = () => {
337
+ const [a, setA] = useState<string | null>(null);
338
+ const [b, setB] = useState<string | null>(null);
339
+ const [c, setC] = useState<string | null>(null);
340
+ return (
341
+ <Stack spacing={2} sx={{ width: 300 }}>
342
+ <Autocomplete label="0" borderRadius={0} options={options} value={a} onChange={(v) => setA(v as string | null)} />
343
+ <Autocomplete label="10 (default)" borderRadius={10} options={options} value={b} onChange={(v) => setB(v as string | null)} />
344
+ <Autocomplete label="24 (pill)" borderRadius={24} options={options} value={c} onChange={(v) => setC(v as string | null)} />
345
+ </Stack>
346
+ );
347
+ };
348
+ `;
349
+
350
+ // =============================================================================
351
+ // EMPTY WITH PLACEHOLDER
352
+ // =============================================================================
353
+ export const EmptyWithPlaceholderAutocompleteDefinition = `
354
+ import React, { useState } from 'react';
355
+ import Autocomplete from './Autocomplete';
356
+ import { Box } from '@mui/material';
357
+
358
+ const options = [
359
+ { value: 'mx', label: 'México' },
360
+ { value: 'co', label: 'Colombia' },
361
+ { value: 'ar', label: 'Argentina' },
362
+ ];
363
+
364
+ export const EmptyWithPlaceholderExample = () => {
365
+ const [value, setValue] = useState<string | null>(null);
366
+ return (
367
+ <Box sx={{ width: 300 }}>
368
+ <Autocomplete
369
+ label="País"
370
+ placeholder="Buscar país..."
371
+ options={options}
372
+ value={value}
373
+ onChange={(val) => setValue(val as string | null)}
374
+ />
375
+ </Box>
376
+ );
377
+ };
378
+ `;
379
+
380
+ // =============================================================================
381
+ // REACT HOOK FORM
382
+ // =============================================================================
383
+ export const RHFAutocompleteDefinition = `
384
+ import React from 'react';
385
+ import { useForm } from 'react-hook-form';
386
+ import Autocomplete from './Autocomplete';
387
+ import { Box, Button, Stack } from '@mui/material';
388
+
389
+ const options = [
390
+ { value: 'mx', label: 'México' },
391
+ { value: 'co', label: 'Colombia' },
392
+ { value: 'ar', label: 'Argentina' },
393
+ ];
394
+
395
+ type FormValues = { country: string | null };
396
+
397
+ export const RHFExample = () => {
398
+ const { control, handleSubmit, watch } = useForm<FormValues>({
399
+ defaultValues: { country: null },
400
+ });
401
+
402
+ return (
403
+ <Box component="form" onSubmit={handleSubmit((data) => console.log(data))}>
404
+ <Stack spacing={2} sx={{ width: 320 }}>
405
+ <Autocomplete
406
+ name="country"
407
+ control={control}
408
+ validation={{ required: 'Campo requerido' }}
409
+ label="País"
410
+ placeholder="Seleccione..."
411
+ options={options}
412
+ />
413
+ <Button type="submit" variant="contained">Guardar</Button>
414
+ <Box>Valor actual: {JSON.stringify(watch('country'))}</Box>
415
+ </Stack>
416
+ </Box>
417
+ );
418
+ };
419
+ `;
420
+
421
+ // =============================================================================
422
+ // ASYNC — búsqueda remota contra un servicio
423
+ // =============================================================================
424
+ export const AsyncServiceAutocompleteDefinition = [
425
+ 'import React, { useState, useEffect, useMemo } from ', "'react'", ';',
426
+ 'import Autocomplete from ', "'./Autocomplete'", ';',
427
+ 'import { Box } from ', "'@mui/material'", ';',
428
+ 'import debounce from ', "'lodash/debounce'", ';',
429
+ '',
430
+ '// Supón que tienes un servicio:',
431
+ '// await fetch(`/api/users?q=${query}`).then(r => r.json())',
432
+ 'async function fetchUsers(query: string) {',
433
+ ' const res = await fetch(`/api/users?q=${encodeURIComponent(query)}`);',
434
+ ' const data = await res.json();',
435
+ ' return data.map((u: any) => ({ value: u.id, label: u.name }));',
436
+ '}',
437
+ '',
438
+ 'export const AsyncServiceExample = () => {',
439
+ ' const [value, setValue] = useState<string | null>(null);',
440
+ ' const [input, setInput] = useState("");',
441
+ ' const [options, setOptions] = useState<any[]>([]);',
442
+ ' const [loading, setLoading] = useState(false);',
443
+ '',
444
+ ' const search = useMemo(',
445
+ ' () =>',
446
+ ' debounce(async (q: string) => {',
447
+ ' if (\!q) { setOptions([]); return; }',
448
+ ' setLoading(true);',
449
+ ' try {',
450
+ ' setOptions(await fetchUsers(q));',
451
+ ' } finally {',
452
+ ' setLoading(false);',
453
+ ' }',
454
+ ' }, 300),',
455
+ ' [],',
456
+ ' );',
457
+ '',
458
+ ' useEffect(() => { search(input); }, [input, search]);',
459
+ '',
460
+ ' return (',
461
+ ' <Box sx={{ width: 320 }}>',
462
+ ' <Autocomplete',
463
+ ' label="Usuario"',
464
+ ' placeholder="Buscar usuario..."',
465
+ ' options={options}',
466
+ ' value={value}',
467
+ ' onChange={(v) => setValue(v as string | null)}',
468
+ ' inputValue={input}',
469
+ ' onInputChange={(_, v) => setInput(v)}',
470
+ ' loading={loading}',
471
+ ' // Desactiva el filtro cliente: el servicio ya filtra.',
472
+ ' filterOptions={(x) => x}',
473
+ ' />',
474
+ ' </Box>',
475
+ ' );',
476
+ '};',
477
+ ].join('\n');