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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/.dockerignore +8 -0
  2. package/.github/workflows/publish.yml +107 -0
  3. package/.prettierrc +3 -0
  4. package/.storybook/main.ts +19 -0
  5. package/.storybook/preview.ts +14 -0
  6. package/.storybook/vitest.setup.ts +9 -0
  7. package/Dockerfile +37 -0
  8. package/build.js +102 -0
  9. package/chromatic.config.json +5 -0
  10. package/cleanDirectories.js +40 -0
  11. package/dist/README.md +243 -0
  12. package/dist/components/Icon/Icon.js +1 -1
  13. package/dist/components/Table/Table.js +1 -1
  14. package/dist/index.cjs +24 -0
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.js +7 -1
  17. package/dist/mui.d.ts +1 -0
  18. package/dist/package.json +197 -0
  19. package/package.json +4 -32
  20. package/rollup.config.cjs +87 -0
  21. package/src/components/ActionMenu/ActionMenu.stories.tsx +230 -0
  22. package/src/components/ActionMenu/ActionMenu.tsx +174 -0
  23. package/src/components/ActionMenu/index.ts +2 -0
  24. package/src/components/AppBar/AppBar.stories.tsx +272 -0
  25. package/src/components/AppBar/AppBar.sx.ts +32 -0
  26. package/src/components/AppBar/AppBar.tsx +123 -0
  27. package/src/components/AppBar/AppBarBrand.tsx +120 -0
  28. package/src/components/AppBar/AppBarContext.ts +25 -0
  29. package/src/components/AppBar/AppBarMenuToggle.tsx +90 -0
  30. package/src/components/AppBar/AppBarUserMenu.tsx +217 -0
  31. package/src/components/AppBar/index.ts +25 -0
  32. package/src/components/Autocomplete/Autocomplete.definitions.ts +477 -0
  33. package/src/components/Autocomplete/Autocomplete.helpers.ts +60 -0
  34. package/src/components/Autocomplete/Autocomplete.stories.tsx +748 -0
  35. package/src/components/Autocomplete/Autocomplete.sx.ts +30 -0
  36. package/src/components/Autocomplete/Autocomplete.tsx +361 -0
  37. package/src/components/Autocomplete/Autocomplete.types.ts +13 -0
  38. package/src/components/Autocomplete/_parts/AutocompleteChips.tsx +55 -0
  39. package/src/components/Autocomplete/_parts/AutocompleteLoader.tsx +17 -0
  40. package/src/components/Autocomplete/_parts/AutocompleteOption.tsx +31 -0
  41. package/src/components/Autocomplete/index.ts +12 -0
  42. package/src/components/Avatar/Avatar.definitions.ts +162 -0
  43. package/src/components/Avatar/Avatar.stories.tsx +258 -0
  44. package/src/components/Avatar/Avatar.tsx +206 -0
  45. package/src/components/Avatar/index.ts +1 -0
  46. package/src/components/Button/Button.definition.ts +97 -0
  47. package/src/components/Button/Button.stories.tsx +285 -0
  48. package/src/components/Button/Button.tsx +67 -0
  49. package/src/components/Button/index.ts +1 -0
  50. package/src/components/Card/Card.definition.ts +5 -0
  51. package/src/components/Card/Card.stories.tsx +221 -0
  52. package/src/components/Card/Card.sx.ts +104 -0
  53. package/src/components/Card/Card.tsx +200 -0
  54. package/src/components/Card/index.ts +9 -0
  55. package/src/components/Chip/Chip.definitions.ts +167 -0
  56. package/src/components/Chip/Chip.stories.tsx +265 -0
  57. package/src/components/Chip/Chip.tsx +61 -0
  58. package/src/components/Chip/index.ts +1 -0
  59. package/src/components/Column/Column.tsx +29 -0
  60. package/src/components/Column/index.ts +1 -0
  61. package/src/components/DatePicker/DatePicker.definitions.ts +228 -0
  62. package/src/components/DatePicker/DatePicker.helpers.ts +24 -0
  63. package/src/components/DatePicker/DatePicker.stories.tsx +309 -0
  64. package/src/components/DatePicker/DatePicker.sx.ts +33 -0
  65. package/src/components/DatePicker/DatePicker.tsx +189 -0
  66. package/src/components/DatePicker/DatePicker.types.ts +10 -0
  67. package/src/components/DatePicker/index.ts +9 -0
  68. package/src/components/DateRangePicker/DateRangePicker.definitions.ts +191 -0
  69. package/src/components/DateRangePicker/DateRangePicker.stories.tsx +252 -0
  70. package/src/components/DateRangePicker/DateRangePicker.tsx +56 -0
  71. package/src/components/DateRangePicker/index.ts +1 -0
  72. package/src/components/DateTimePicker/DateTimePicker.definitions.ts +256 -0
  73. package/src/components/DateTimePicker/DateTimePicker.helpers.ts +38 -0
  74. package/src/components/DateTimePicker/DateTimePicker.stories.tsx +418 -0
  75. package/src/components/DateTimePicker/DateTimePicker.sx.ts +30 -0
  76. package/src/components/DateTimePicker/DateTimePicker.tsx +225 -0
  77. package/src/components/DateTimePicker/DateTimePicker.types.ts +10 -0
  78. package/src/components/DateTimePicker/index.ts +9 -0
  79. package/src/components/Drawer/Drawer.stories.tsx +270 -0
  80. package/src/components/Drawer/Drawer.sx.ts +106 -0
  81. package/src/components/Drawer/Drawer.tsx +214 -0
  82. package/src/components/Drawer/DrawerContext.ts +26 -0
  83. package/src/components/Drawer/DrawerItem.tsx +110 -0
  84. package/src/components/Drawer/index.ts +10 -0
  85. package/src/components/Flyout/Flyout.stories.tsx +282 -0
  86. package/src/components/Flyout/Flyout.tsx +122 -0
  87. package/src/components/Flyout/index.ts +1 -0
  88. package/src/components/Gallery/Gallery.definition.tsx +37 -0
  89. package/src/components/Gallery/Gallery.stories.tsx +82 -0
  90. package/src/components/Gallery/Gallery.tsx +118 -0
  91. package/src/components/Gallery/GalleryLightbox.tsx +170 -0
  92. package/src/components/Gallery/GalleryMain.tsx +84 -0
  93. package/src/components/Gallery/GalleryThumbnails.tsx +106 -0
  94. package/src/components/Gallery/index.ts +1 -0
  95. package/src/components/Icon/Icon.stories.tsx +121 -0
  96. package/src/components/Icon/Icon.tsx +175 -0
  97. package/src/components/Icon/index.ts +2 -0
  98. package/src/components/Input/Input.definitions.ts +324 -0
  99. package/src/components/Input/Input.helpers.ts +49 -0
  100. package/src/components/Input/Input.stories.tsx +499 -0
  101. package/src/components/Input/Input.sx.ts +42 -0
  102. package/src/components/Input/Input.tsx +141 -0
  103. package/src/components/Input/Input.types.ts +10 -0
  104. package/src/components/Input/index.ts +9 -0
  105. package/src/components/InputGroup/InputGroup.definitions.ts +158 -0
  106. package/src/components/InputGroup/InputGroup.stories.tsx +267 -0
  107. package/src/components/InputGroup/InputGroup.tsx +179 -0
  108. package/src/components/InputGroup/index.ts +1 -0
  109. package/src/components/MenuButton/MenuButton.stories.tsx +197 -0
  110. package/src/components/MenuButton/MenuButton.tsx +100 -0
  111. package/src/components/MenuButton/index.ts +1 -0
  112. package/src/components/Modal/Modal.stories.tsx +721 -0
  113. package/src/components/Modal/Modal.tsx +355 -0
  114. package/src/components/Modal/ModalBody.tsx +16 -0
  115. package/src/components/Modal/ModalFooter.tsx +71 -0
  116. package/src/components/Modal/ModalHeader.tsx +18 -0
  117. package/src/components/Modal/index.ts +6 -0
  118. package/src/components/PageLoader/PageLoader.stories.tsx +217 -0
  119. package/src/components/PageLoader/PageLoader.tsx +96 -0
  120. package/src/components/PageLoader/index.ts +2 -0
  121. package/src/components/ScrollTopButton/ScrollTopButton.stories.tsx +158 -0
  122. package/src/components/ScrollTopButton/ScrollTopButton.tsx +135 -0
  123. package/src/components/ScrollTopButton/index.ts +8 -0
  124. package/src/components/ScrollTopButton/scrollToTop.ts +37 -0
  125. package/src/components/Select/Select.definitions.ts +602 -0
  126. package/src/components/Select/Select.helpers.ts +71 -0
  127. package/src/components/Select/Select.stories.tsx +687 -0
  128. package/src/components/Select/Select.sx.ts +14 -0
  129. package/src/components/Select/Select.tsx +429 -0
  130. package/src/components/Select/Select.types.ts +15 -0
  131. package/src/components/Select/_parts/SelectMenuItem.tsx +40 -0
  132. package/src/components/Select/_parts/SelectSearchHeader.tsx +51 -0
  133. package/src/components/Select/_parts/SelectValue.tsx +96 -0
  134. package/src/components/Select/index.ts +14 -0
  135. package/src/components/Stat/Stat.stories.tsx +85 -0
  136. package/src/components/Stat/Stat.tsx +117 -0
  137. package/src/components/Stat/index.ts +2 -0
  138. package/src/components/StatusMessage/StatusMessage.stories.tsx +130 -0
  139. package/src/components/StatusMessage/StatusMessage.tsx +162 -0
  140. package/src/components/StatusMessage/index.ts +2 -0
  141. package/src/components/Stepper/Step.tsx +21 -0
  142. package/src/components/Stepper/Stepper.definition.ts +75 -0
  143. package/src/components/Stepper/Stepper.stories.tsx +122 -0
  144. package/src/components/Stepper/Stepper.tsx +75 -0
  145. package/src/components/Stepper/index.ts +2 -0
  146. package/src/components/Table/EmptyTable.png +0 -0
  147. package/src/components/Table/Table.definition.ts +580 -0
  148. package/src/components/Table/Table.stories.tsx +853 -0
  149. package/src/components/Table/Table.tsx +495 -0
  150. package/src/components/Table/data.ts +134 -0
  151. package/src/components/Table/exportsUtils.ts +195 -0
  152. package/src/components/Table/index.ts +3 -0
  153. package/src/components/Table/types.ts +34 -0
  154. package/src/components/Tabs/Tab.definition.ts +53 -0
  155. package/src/components/Tabs/Tab.tsx +19 -0
  156. package/src/components/Tabs/Tabs.stories.tsx +118 -0
  157. package/src/components/Tabs/Tabs.tsx +99 -0
  158. package/src/components/Tabs/_tabUtils.tsx +4 -0
  159. package/src/components/Tabs/index.ts +2 -0
  160. package/src/components/Timeline/Timeline.definition.ts +43 -0
  161. package/src/components/Timeline/Timeline.stories.tsx +108 -0
  162. package/src/components/Timeline/Timeline.tsx +49 -0
  163. package/src/components/Timeline/TimelineItem.tsx +31 -0
  164. package/src/components/Timeline/index.ts +2 -0
  165. package/src/components/Tooltip/Tooltip.stories.tsx +129 -0
  166. package/src/components/Tooltip/Tooltip.tsx +58 -0
  167. package/src/components/Tooltip/index.ts +1 -0
  168. package/src/components/_shared/formField.sx.ts +118 -0
  169. package/src/components/_shared/resolvePreset.ts +35 -0
  170. package/src/hooks/ClipBoard/ClipBoard.stories.tsx +168 -0
  171. package/src/hooks/ClipBoard/ClipBoard.tsx +131 -0
  172. package/src/hooks/ClipBoard/ClipboardUnifiedDemo.tsx +111 -0
  173. package/src/hooks/ClipBoard/index.ts +1 -0
  174. package/src/hooks/Wizard/Wizard.stories.tsx +301 -0
  175. package/src/hooks/Wizard/WizardContext.tsx +166 -0
  176. package/src/hooks/Wizard/index.ts +6 -0
  177. package/src/hooks/Wizard/useWizard.ts +13 -0
  178. package/src/index.ts +17 -0
  179. package/src/mui.ts +54 -0
  180. package/src/styles.css +3 -0
  181. package/src/theme/componentStyles.ts +47 -0
  182. package/src/theme/tokens.ts +43 -0
  183. package/tailwind.config.js +10 -0
  184. package/tsconfig.json +48 -0
  185. package/tsup.config.js +41 -0
  186. package/vite.config.js +132 -0
  187. package/vitest.config.ts +35 -0
@@ -0,0 +1,285 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Button } from "./Button";
3
+ import NotificationsIcon from "@mui/icons-material/Notifications";
4
+ import { expect, within } from "storybook/test";
5
+ import {
6
+ ContainedButtonDefinition,
7
+ DefaultButtonDefinition,
8
+ DisabledStateButtonDefinition,
9
+ LoadingButtonDefinition,
10
+ LoadingIndicatorEndButtonDefinition,
11
+ LoadingIndicatorStartButtonDefinition,
12
+ OutlinedButtonDefinition,
13
+ WithEndIconButtonDefinition,
14
+ WithLargeSizeButtonDefinition,
15
+ WithSmallSizeButtonDefinition,
16
+ WithStartIconButtonDefinition
17
+ } from "./Button.definition";
18
+
19
+
20
+ const meta: Meta<typeof Button> = {
21
+ title: "Components/Button",
22
+ component: Button,
23
+ parameters: {
24
+ layout: "centered"
25
+ },
26
+ tags: ["autodocs"]
27
+ };
28
+
29
+ export default meta;
30
+
31
+ type Story = StoryObj<typeof Button>;
32
+
33
+ export const Default: Story = {
34
+ args: {
35
+ children: "Button",
36
+ variant: "text"
37
+ },
38
+ parameters: {
39
+ docs: {
40
+ description: {
41
+ story: "Boton con el texto \"Button\" y el estilo \"default\"."
42
+ },
43
+ source: {
44
+ code: DefaultButtonDefinition.trim()
45
+ }
46
+ }
47
+ }
48
+ };
49
+
50
+ export const Outlined: Story = {
51
+ args: {
52
+ children: "Button",
53
+ variant: "outlined"
54
+ },
55
+ parameters: {
56
+ docs: {
57
+ description: {
58
+ story: "Boton con el texto \"Button\" y el estilo \"outlined\"."
59
+ },
60
+ source: {
61
+ code: OutlinedButtonDefinition.trim()
62
+ }
63
+ }
64
+ }
65
+ };
66
+
67
+ export const Contained: Story = {
68
+ args: {
69
+ children: "Button",
70
+ variant: "contained"
71
+ },
72
+ parameters: {
73
+ docs: {
74
+ description: {
75
+ story: "Boton con el texto \"Button\" y el estilo \"contained\"."
76
+ },
77
+ source: {
78
+ code: ContainedButtonDefinition.trim()
79
+ }
80
+ }
81
+ }
82
+ };
83
+
84
+
85
+ export const WithStartIcon: Story = {
86
+ args: {
87
+ children: "Button",
88
+ variant: "contained",
89
+ startIcon: <NotificationsIcon />,
90
+ "data-testid": "button"
91
+ },
92
+ parameters: {
93
+ docs: {
94
+ description: {
95
+ story: "Boton con el texto \"Button\", el estilo \"contained\" y un icono al inicio."
96
+ },
97
+ source: {
98
+ code: WithStartIconButtonDefinition.trim()
99
+ }
100
+ }
101
+ },
102
+ play: async ({ canvasElement }) => {
103
+ const canvas = within(canvasElement);
104
+ const button = canvas.getByTestId("button");
105
+ expect(within(button).getByText("Button")).toBeInTheDocument();
106
+ }
107
+ };
108
+
109
+
110
+ export const WithEndIcon: Story = {
111
+ args: {
112
+ children: "Button",
113
+ variant: "contained",
114
+ endIcon: <NotificationsIcon />,
115
+ "data-testid": "button"
116
+ },
117
+ parameters: {
118
+ docs: {
119
+ description: {
120
+ story: "Boton con el texto \"Button\", el estilo \"contained\" y un icono al final."
121
+ },
122
+ source: {
123
+ code: WithEndIconButtonDefinition.trim()
124
+ }
125
+ }
126
+ },
127
+ play: async ({ canvasElement }) => {
128
+ const canvas = within(canvasElement);
129
+ const button = canvas.getByTestId("button");
130
+ expect(within(button).getByText("Button")).toBeInTheDocument();
131
+ }
132
+ };
133
+
134
+ export const WithSmallSize: Story = {
135
+ args: {
136
+ children: "Button",
137
+ variant: "contained",
138
+ size: "small",
139
+ "data-testid": "button"
140
+ },
141
+ parameters: {
142
+ docs: {
143
+ description: {
144
+ story: "Boton tamaño \"small\"."
145
+ },
146
+ source: {
147
+ code: WithSmallSizeButtonDefinition.trim()
148
+ }
149
+ }
150
+ },
151
+ play: async ({ canvasElement }) => {
152
+ const canvas = within(canvasElement);
153
+ const button = canvas.getByTestId("button");
154
+ expect(within(button).getByText("Button")).toBeInTheDocument();
155
+ }
156
+ };
157
+
158
+
159
+ export const WithLargeSize: Story = {
160
+ args: {
161
+ children: "Button",
162
+ variant: "contained",
163
+ size: "large",
164
+ "data-testid": "button"
165
+ },
166
+ parameters: {
167
+ docs: {
168
+ description: {
169
+ story: "Boton tamaño \"large\"."
170
+ },
171
+ source: {
172
+ code: WithLargeSizeButtonDefinition.trim()
173
+ }
174
+ }
175
+ },
176
+ play: async ({ canvasElement }) => {
177
+ const canvas = within(canvasElement);
178
+ const button = canvas.getByTestId("button");
179
+ expect(within(button).getByText("Button")).toBeInTheDocument();
180
+ }
181
+ };
182
+
183
+ export const Disabled: Story = {
184
+ args: {
185
+ children: "Button",
186
+ variant: "contained",
187
+ "data-testid": "button",
188
+ disabled: true
189
+ },
190
+ parameters: {
191
+ docs: {
192
+ description: {
193
+ story: "Boton deshabilitado."
194
+ },
195
+ source: {
196
+ code: DisabledStateButtonDefinition.trim()
197
+ }
198
+ }
199
+ },
200
+ play: async ({ canvasElement }) => {
201
+ const canvas = within(canvasElement);
202
+ const button = canvas.getByTestId("button");
203
+ expect(within(button).getByText("Button")).toBeInTheDocument();
204
+ expect(button).toBeDisabled();
205
+ }
206
+ };
207
+
208
+
209
+ export const Loading: Story = {
210
+ args: {
211
+ children: "Button",
212
+ variant: "contained",
213
+ loading: true,
214
+ "data-testid": "button"
215
+ },
216
+ parameters: {
217
+ docs: {
218
+ description: {
219
+ story: "Boton con loader."
220
+ },
221
+ source: {
222
+ code: LoadingButtonDefinition.trim()
223
+ }
224
+ }
225
+ },
226
+ play: async ({ canvasElement }) => {
227
+ const canvas = within(canvasElement);
228
+ const button = canvas.getByTestId("button");
229
+ expect(within(button).getByText("Button")).toBeInTheDocument();
230
+ expect(button).toBeDisabled();
231
+ }
232
+ };
233
+
234
+ export const LoadingStart: Story = {
235
+ args: {
236
+ children: "Button",
237
+ variant: "contained",
238
+ loading: true,
239
+ loadingPosition: "start",
240
+ "data-testid": "button"
241
+ },
242
+ parameters: {
243
+ docs: {
244
+ description: {
245
+ story: "Boton con al inicio loader."
246
+ },
247
+ source: {
248
+ code: LoadingIndicatorStartButtonDefinition.trim()
249
+ }
250
+ }
251
+ },
252
+ play: async ({ canvasElement }) => {
253
+ const canvas = within(canvasElement);
254
+ const button = canvas.getByTestId("button");
255
+ expect(within(button).getByText("Button")).toBeInTheDocument();
256
+ expect(button).toBeDisabled();
257
+ }
258
+ };
259
+
260
+
261
+ export const LoadingEnd: Story = {
262
+ args: {
263
+ children: "Button",
264
+ variant: "contained",
265
+ loading: true,
266
+ loadingPosition: "end",
267
+ "data-testid": "button"
268
+ },
269
+ parameters: {
270
+ docs: {
271
+ description: {
272
+ story: "Boton con al inicio loader."
273
+ },
274
+ source: {
275
+ code: LoadingIndicatorEndButtonDefinition.trim()
276
+ }
277
+ }
278
+ },
279
+ play: async ({ canvasElement }) => {
280
+ const canvas = within(canvasElement);
281
+ const button = canvas.getByTestId("button");
282
+ expect(within(button).getByText("Button")).toBeInTheDocument();
283
+ expect(button).toBeDisabled();
284
+ }
285
+ };
@@ -0,0 +1,67 @@
1
+ import { Button as MuiButton } from "@mui/material";
2
+ import type { ButtonProps } from "@mui/material/Button";
3
+ import React from "react";
4
+
5
+ type PickButtonProps = Pick<ButtonProps, "href" | "children" | "onClick" | "color" | "disabled" | "className" | "endIcon" | "startIcon" | "loading" | "loadingIndicator" | "variant" | "size" | "loadingPosition" | "type" | "sx">;
6
+
7
+ export interface MyButtonProps extends PickButtonProps {
8
+ children?: React.ReactNode;
9
+ variant?: "text" | "outlined" | "contained";
10
+ disabled?: boolean;
11
+ endIcon?: React.ReactNode;
12
+ startIcon?: React.ReactNode;
13
+ size?: "small" | "medium" | "large";
14
+ className?: string;
15
+ onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
16
+ loading?: boolean;
17
+ loadingIndicator?: React.ReactNode;
18
+ loadingPosition?: "start" | "end" | "center";
19
+ color?: "primary" | "success" | "error" | "info" | "warning"; //change for tailwind class colors
20
+ href?: string;
21
+ "data-testid"?: string;
22
+ type?: "button" | "submit" ;
23
+ }
24
+
25
+ export const Button: React.FC<MyButtonProps> = ({
26
+ children,
27
+ variant,
28
+ disabled,
29
+ startIcon,
30
+ endIcon,
31
+ size,
32
+ className,
33
+ onClick,
34
+ loading,
35
+ loadingPosition,
36
+ loadingIndicator,
37
+ color,
38
+ href,
39
+ "data-testid": dataTestId,
40
+ type,
41
+ sx
42
+ }) => {
43
+ return (
44
+ <MuiButton
45
+ disabled={disabled}
46
+ variant={variant}
47
+ startIcon={startIcon}
48
+ endIcon={endIcon}
49
+ size={size}
50
+ className={className}
51
+ onClick={onClick}
52
+ loading={loading}
53
+ loadingPosition={loadingPosition}
54
+ loadingIndicator={loadingIndicator}
55
+ sx={{ textTransform: "none", ...sx }}
56
+ disableRipple={true}
57
+ disableElevation={true}
58
+ color={color}
59
+ data-testid={dataTestId}
60
+ href={href}
61
+ type={type}
62
+ >
63
+ {children}
64
+ </MuiButton>);
65
+ };
66
+
67
+ export default Button;
@@ -0,0 +1 @@
1
+ export { default as Button } from './Button';
@@ -0,0 +1,5 @@
1
+ export const DefaultCardDefinition = `
2
+ <Card>
3
+ Card
4
+ </Card>
5
+ `
@@ -0,0 +1,221 @@
1
+ import React from 'react';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+ import { Box, Button, Chip, Stack, Typography } from '@mui/material';
4
+ import EditIcon from '@mui/icons-material/Edit';
5
+ import DeleteIcon from '@mui/icons-material/Delete';
6
+ import PersonIcon from '@mui/icons-material/Person';
7
+
8
+ import { Card } from './Card';
9
+ import { ActionMenu } from '../ActionMenu';
10
+
11
+ const meta: Meta<typeof Card> = {
12
+ title: 'Components/Card',
13
+ component: Card,
14
+ tags: ['autodocs'],
15
+ parameters: {
16
+ layout: 'padded',
17
+ docs: {
18
+ description: {
19
+ component:
20
+ 'Contenedor semántico con slots `title` / `subtitle` / `actions` / `footer`. Reemplaza el patrón `.card` + `.card-header` + `.card-body` de Metronic con composición declarativa. Backward-compat: si solo se pasa `children`, se comporta como el Card original.',
21
+ },
22
+ },
23
+ },
24
+ argTypes: {
25
+ variant: {
26
+ control: 'select',
27
+ options: ['elevated', 'outlined', 'plain'],
28
+ },
29
+ padding: {
30
+ control: 'select',
31
+ options: ['none', 'dense', 'normal', 'loose'],
32
+ },
33
+ clickable: { control: 'boolean' },
34
+ },
35
+ };
36
+
37
+ export default meta;
38
+ type Story = StoryObj<typeof Card>;
39
+
40
+ // ── Stories ──────────────────────────────────────────────────────────────
41
+
42
+ export const LegacyBodyOnly: Story = {
43
+ args: {
44
+ children: 'Contenido simple del card — sin header ni footer.',
45
+ },
46
+ parameters: {
47
+ docs: {
48
+ description: {
49
+ story:
50
+ 'Modo backward-compat: pasando solo `children` el Card se comporta como el original (body con padding, sombra sutil).',
51
+ },
52
+ },
53
+ },
54
+ };
55
+
56
+ export const WithTitleAndSubtitle: Story = {
57
+ args: {
58
+ title: 'Datos del cliente',
59
+ subtitle: 'Última actualización: hace 2 horas',
60
+ children: (
61
+ <Typography variant="body2" color="text.secondary">
62
+ Aquí va el contenido del card, por ejemplo un formulario o una tabla.
63
+ </Typography>
64
+ ),
65
+ },
66
+ };
67
+
68
+ export const WithActions: Story = {
69
+ args: {
70
+ title: 'Afiliación #12458',
71
+ subtitle: 'Aprobada el 10 abril 2026',
72
+ actions: (
73
+ <ActionMenu
74
+ items={[
75
+ { label: 'Editar', icon: <EditIcon fontSize="small" />, onClick: () => {} },
76
+ {
77
+ label: 'Eliminar',
78
+ icon: <DeleteIcon fontSize="small" />,
79
+ onClick: () => {},
80
+ danger: true,
81
+ dividerBefore: true,
82
+ },
83
+ ]}
84
+ />
85
+ ),
86
+ children: (
87
+ <Stack direction="row" spacing={2} alignItems="center">
88
+ <PersonIcon color="action" />
89
+ <Box>
90
+ <Typography variant="body2" sx={{ fontWeight: 600 }}>
91
+ Andrea Pineda
92
+ </Typography>
93
+ <Typography variant="caption" color="text.secondary">
94
+ andrea@soyfri.com
95
+ </Typography>
96
+ </Box>
97
+ <Chip label="Activo" color="success" size="small" sx={{ ml: 'auto' }} />
98
+ </Stack>
99
+ ),
100
+ },
101
+ };
102
+
103
+ export const WithFooter: Story = {
104
+ args: {
105
+ title: 'Nueva afiliación',
106
+ children: (
107
+ <Stack spacing={1}>
108
+ <Typography variant="body2">
109
+ Confirma los datos antes de enviar la solicitud.
110
+ </Typography>
111
+ <Typography variant="caption" color="text.secondary">
112
+ Campos marcados con * son obligatorios.
113
+ </Typography>
114
+ </Stack>
115
+ ),
116
+ footer: (
117
+ <>
118
+ <Button variant="outlined" color="secondary">
119
+ Cancelar
120
+ </Button>
121
+ <Button variant="contained">Enviar</Button>
122
+ </>
123
+ ),
124
+ },
125
+ };
126
+
127
+ export const OutlinedVariant: Story = {
128
+ args: {
129
+ variant: 'outlined',
130
+ title: 'Card outlined',
131
+ subtitle: 'Sin sombra, solo borde',
132
+ children: (
133
+ <Typography variant="body2">
134
+ Útil cuando el card vive sobre un fondo con elevation mayor, o para
135
+ evitar stacked shadows en listas densas.
136
+ </Typography>
137
+ ),
138
+ },
139
+ };
140
+
141
+ export const PlainVariant: Story = {
142
+ args: {
143
+ variant: 'plain',
144
+ title: 'Card plain',
145
+ subtitle: 'Sin borde ni sombra',
146
+ children: (
147
+ <Typography variant="body2">
148
+ Perfecto para composición interna — por ejemplo, cuando un card va
149
+ dentro de otro y quieres evitar doble sombra.
150
+ </Typography>
151
+ ),
152
+ },
153
+ };
154
+
155
+ export const Clickable: Story = {
156
+ args: {
157
+ clickable: true,
158
+ title: 'Haz click en este card',
159
+ subtitle: 'Fíjate en el hover',
160
+ children: (
161
+ <Typography variant="body2" color="text.secondary">
162
+ El cursor cambia y aparece un lift sutil en hover. Se dispara `onClick`.
163
+ </Typography>
164
+ ),
165
+ onClick: () => console.log('card clicked'),
166
+ },
167
+ };
168
+
169
+ export const DensePadding: Story = {
170
+ args: {
171
+ padding: 'dense',
172
+ title: 'Card denso',
173
+ subtitle: 'Padding reducido',
174
+ children: (
175
+ <Stack spacing={0.5}>
176
+ <Typography variant="body2">Línea 1</Typography>
177
+ <Typography variant="body2">Línea 2</Typography>
178
+ <Typography variant="body2">Línea 3</Typography>
179
+ </Stack>
180
+ ),
181
+ },
182
+ };
183
+
184
+ export const CustomHeader: Story = {
185
+ args: {
186
+ header: (
187
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, width: '100%' }}>
188
+ <Box
189
+ sx={{
190
+ width: 40,
191
+ height: 40,
192
+ borderRadius: 2,
193
+ bgcolor: 'primary.main',
194
+ color: 'primary.contrastText',
195
+ display: 'flex',
196
+ alignItems: 'center',
197
+ justifyContent: 'center',
198
+ fontWeight: 700,
199
+ }}
200
+ >
201
+ 42
202
+ </Box>
203
+ <Box sx={{ flex: 1 }}>
204
+ <Typography variant="subtitle1" sx={{ fontWeight: 700 }}>
205
+ Header totalmente custom
206
+ </Typography>
207
+ <Typography variant="caption" color="text.secondary">
208
+ Tomas control del slot entero
209
+ </Typography>
210
+ </Box>
211
+ <Chip label="v2" size="small" variant="outlined" />
212
+ </Box>
213
+ ),
214
+ children: (
215
+ <Typography variant="body2">
216
+ Cuando `header` se usa, los props `title`/`subtitle`/`actions` son
217
+ ignorados.
218
+ </Typography>
219
+ ),
220
+ },
221
+ };
@@ -0,0 +1,104 @@
1
+ import type { SxProps, Theme } from '@mui/material/styles';
2
+
3
+ export type CardVariant = 'elevated' | 'outlined' | 'plain';
4
+ export type CardPadding = 'none' | 'dense' | 'normal' | 'loose';
5
+
6
+ export interface BuildCardSxArgs {
7
+ variant: CardVariant;
8
+ padding: CardPadding;
9
+ clickable: boolean;
10
+ }
11
+
12
+ const paddingMap: Record<CardPadding, number> = {
13
+ none: 0,
14
+ dense: 1.5,
15
+ normal: 2.5,
16
+ loose: 4,
17
+ };
18
+
19
+ /**
20
+ * Estilo base del Card (root). La props `sx` del consumer se compone encima
21
+ * junto al preset resuelto (en Card.tsx). Este builder solo se ocupa del
22
+ * variant + padding, para que siga siendo predecible.
23
+ */
24
+ export function buildCardSx({
25
+ variant,
26
+ padding,
27
+ clickable,
28
+ }: BuildCardSxArgs): SxProps<Theme> {
29
+ return (theme) => ({
30
+ borderRadius: 2,
31
+ backgroundColor: 'background.paper',
32
+ boxShadow:
33
+ variant === 'elevated'
34
+ ? theme.shadows[1]
35
+ : 'none',
36
+ border:
37
+ variant === 'outlined'
38
+ ? `1px solid ${theme.palette.divider}`
39
+ : 'none',
40
+ transition: theme.transitions.create(
41
+ ['box-shadow', 'transform', 'border-color'],
42
+ { duration: theme.transitions.duration.shorter },
43
+ ),
44
+ cursor: clickable ? 'pointer' : 'default',
45
+ ...(clickable && {
46
+ '&:hover': {
47
+ boxShadow:
48
+ variant === 'elevated'
49
+ ? theme.shadows[3]
50
+ : variant === 'outlined'
51
+ ? theme.shadows[1]
52
+ : 'none',
53
+ transform: variant !== 'plain' ? 'translateY(-1px)' : 'none',
54
+ },
55
+ '&:active': {
56
+ transform: 'translateY(0)',
57
+ },
58
+ }),
59
+ // Body padding (cuando no hay header/footer envolventes). Para el caso
60
+ // compuesto los slots manejan su propio padding, dejamos el root en 0.
61
+ p: paddingMap[padding],
62
+ });
63
+ }
64
+
65
+ export function buildCardHeaderSx(): SxProps<Theme> {
66
+ return (theme) => ({
67
+ display: 'flex',
68
+ alignItems: 'center',
69
+ gap: 1.5,
70
+ px: 2.5,
71
+ py: 1.75,
72
+ borderBottom: `1px solid ${theme.palette.divider}`,
73
+ '& .card-header-text': {
74
+ flex: 1,
75
+ minWidth: 0,
76
+ },
77
+ '& .card-header-actions': {
78
+ display: 'flex',
79
+ alignItems: 'center',
80
+ gap: 0.5,
81
+ flexShrink: 0,
82
+ },
83
+ });
84
+ }
85
+
86
+ export function buildCardBodySx(padding: CardPadding): SxProps<Theme> {
87
+ return {
88
+ px: padding === 'none' ? 0 : padding === 'dense' ? 1.5 : padding === 'loose' ? 4 : 2.5,
89
+ py: padding === 'none' ? 0 : padding === 'dense' ? 1.5 : padding === 'loose' ? 4 : 2.5,
90
+ };
91
+ }
92
+
93
+ export function buildCardFooterSx(): SxProps<Theme> {
94
+ return (theme) => ({
95
+ display: 'flex',
96
+ alignItems: 'center',
97
+ justifyContent: 'flex-end',
98
+ gap: 1,
99
+ px: 2.5,
100
+ py: 1.5,
101
+ borderTop: `1px solid ${theme.palette.divider}`,
102
+ backgroundColor: theme.palette.action.hover,
103
+ });
104
+ }