@soyfri/shared-library 2.0.0-beta.23 → 2.0.0-beta.25

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.
@@ -47,6 +47,9 @@ const mergeSx = (base, extra) => {
47
47
  };
48
48
  const Avatar = ({
49
49
  items,
50
+ src,
51
+ alt,
52
+ fallback,
50
53
  type,
51
54
  displayedAvatars = 4,
52
55
  size = "sm",
@@ -58,6 +61,8 @@ const Avatar = ({
58
61
  overlap
59
62
  }) => {
60
63
  var _a;
64
+ const resolvedItems = items && items.length > 0 ? items : src || fallback ? [{ imageUrl: src, text: alt, content: fallback }] : [];
65
+ const showCaption = items && items.length > 0 ? showText : false;
61
66
  const [failedUrls, setFailedUrls] = React.useState(/* @__PURE__ */ new Set());
62
67
  const handleImageError = (url) => {
63
68
  setFailedUrls((prev) => {
@@ -67,12 +72,12 @@ const Avatar = ({
67
72
  return next;
68
73
  });
69
74
  };
70
- if (!items || items.length === 0) {
75
+ if (resolvedItems.length === 0) {
71
76
  return null;
72
77
  }
73
78
  const s = resolveSize(size);
74
79
  const effectiveOverlap = overlap != null ? overlap : s.overlap;
75
- const visibleItems = items.slice(0, displayedAvatars);
80
+ const visibleItems = resolvedItems.slice(0, displayedAvatars);
76
81
  const baseAvatarSx = {
77
82
  width: s.px,
78
83
  height: s.px,
@@ -104,7 +109,7 @@ const Avatar = ({
104
109
  }
105
110
  },
106
111
  sx: finalSx,
107
- children: !hasImage && showBadgeFallback ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-label": item.text, children: item.badge }) : !hasImage ? /* @__PURE__ */ jsxRuntime.jsx(
112
+ children: !hasImage && item.content != null ? item.content : !hasImage && showBadgeFallback ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-label": item.text, children: item.badge }) : !hasImage ? /* @__PURE__ */ jsxRuntime.jsx(
108
113
  AccountCircleIcon,
109
114
  {
110
115
  "aria-label": item.text,
@@ -133,7 +138,7 @@ const Avatar = ({
133
138
  ),
134
139
  children: [
135
140
  /* @__PURE__ */ jsxRuntime.jsx(material.Box, { sx: { display: "flex", alignItems: "center" }, children: visibleItems.map((item, i) => renderSingleAvatar(item, i)) }),
136
- showText && ((_a = items[0]) == null ? void 0 : _a.text) && /* @__PURE__ */ jsxRuntime.jsxs(
141
+ showCaption && ((_a = resolvedItems[0]) == null ? void 0 : _a.text) && /* @__PURE__ */ jsxRuntime.jsxs(
137
142
  material.Typography,
138
143
  {
139
144
  variant: "caption",
@@ -144,12 +149,12 @@ const Avatar = ({
144
149
  color: "text.primary"
145
150
  },
146
151
  children: [
147
- type && items.length === 1 && /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { component: "span", sx: { mr: 0.5 }, children: [
152
+ type && resolvedItems.length === 1 && /* @__PURE__ */ jsxRuntime.jsxs(material.Box, { component: "span", sx: { mr: 0.5 }, children: [
148
153
  type,
149
154
  ":"
150
155
  ] }),
151
- items[0].text,
152
- items.length > 1 && ` +${items.length - 1}`
156
+ resolvedItems[0].text,
157
+ resolvedItems.length > 1 && ` +${resolvedItems.length - 1}`
153
158
  ]
154
159
  }
155
160
  )
@@ -158,4 +163,4 @@ const Avatar = ({
158
163
  );
159
164
  };
160
165
  exports.Avatar = Avatar;
161
- //# sourceMappingURL=Avatar-CuSrK8Wn.cjs.map
166
+ //# sourceMappingURL=Avatar-Dw5rzayR.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Avatar-Dw5rzayR.cjs","sources":["../src/components/Avatar/Avatar.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport {\n Avatar as MuiAvatar,\n Box,\n Tooltip,\n Typography,\n type SxProps,\n type Theme,\n} from '@mui/material';\nimport AccountCircleIcon from '@mui/icons-material/AccountCircle';\n\nexport interface AvatarItem {\n text?: string;\n imageUrl?: string;\n badge?: string;\n color?: string;\n backgroundColor?: string;\n /** Contenido a renderizar como fallback cuando no hay imagen (ej. iniciales). */\n content?: React.ReactNode;\n}\n\nexport type AvatarSize = 'sm' | 'md' | 'lg' | 'xl' | number;\n\nexport interface AvatarProps {\n type?: string;\n /**\n * Lista de avatares (grupo apilado). Para un solo avatar puedes usar\n * `src`/`alt` en su lugar.\n */\n items?: AvatarItem[];\n /**\n * Atajo para un único avatar por URL de imagen. Equivale a\n * `items={[{ imageUrl: src, text: alt }]}`. Ignorado si `items` tiene elementos.\n */\n src?: string;\n /** Texto alternativo de la imagen cuando se usa `src` (modo single). */\n alt?: string;\n /** Fallback (ej. iniciales) cuando no hay imagen, en modo single (`src`). */\n fallback?: React.ReactNode;\n displayedAvatars?: number;\n size?: AvatarSize;\n showText?: boolean;\n showTooltip?: boolean;\n /**\n * sx aplicado al contenedor raíz.\n */\n sx?: SxProps<Theme>;\n /**\n * sx aplicado a cada MuiAvatar individual (se mergea sobre los defaults).\n */\n avatarSx?: SxProps<Theme>;\n className?: string;\n /**\n * Overlap (px) entre avatares cuando hay varios. Default depende del tamaño.\n */\n overlap?: number;\n}\n\n// Escala alineada con la escala de MUI (sm=32, md=40, lg=56, xl=96) con borde\n// proporcional para el efecto stacked.\nconst sizeMap: Record<\n Exclude<AvatarSize, number>,\n { px: number; border: number; font: number; overlap: number }\n> = {\n sm: { px: 32, border: 2, font: 14, overlap: 8 },\n md: { px: 40, border: 2, font: 16, overlap: 10 },\n lg: { px: 56, border: 3, font: 22, overlap: 14 },\n xl: { px: 96, border: 4, font: 36, overlap: 20 },\n};\n\nconst resolveSize = (size: AvatarSize) => {\n if (typeof size === 'number') {\n return {\n px: size,\n border: Math.max(2, Math.round(size * 0.05)),\n font: Math.round(size * 0.4),\n overlap: Math.round(size * 0.25),\n };\n }\n return sizeMap[size];\n};\n\nconst mergeSx = (base: SxProps<Theme>, extra?: SxProps<Theme>): SxProps<Theme> => {\n if (!extra) return base;\n const baseArr = Array.isArray(base) ? base : [base];\n const extraArr = Array.isArray(extra) ? extra : [extra];\n return [...baseArr, ...extraArr] as SxProps<Theme>;\n};\n\nexport const Avatar: React.FC<AvatarProps> = ({\n items,\n src,\n alt,\n fallback,\n type,\n displayedAvatars = 4,\n size = 'sm',\n showText = true,\n showTooltip = false,\n sx,\n avatarSx,\n className,\n overlap,\n}) => {\n // Modo single (`src`) vs grupo (`items`). En single no mostramos caption.\n const resolvedItems: AvatarItem[] =\n items && items.length > 0\n ? items\n : src || fallback\n ? [{ imageUrl: src, text: alt, content: fallback }]\n : [];\n const showCaption = items && items.length > 0 ? showText : false;\n // Indexamos por URL (no por posición) para que cambios en `items` — reorder,\n // filtrado, paginación — no hagan que un avatar válido \"herede\" el estado de\n // imagen rota de un item anterior que estaba en la misma posición.\n const [failedUrls, setFailedUrls] = useState<Set<string>>(new Set());\n\n const handleImageError = (url: string) => {\n setFailedUrls((prev) => {\n if (prev.has(url)) return prev;\n const next = new Set(prev);\n next.add(url);\n return next;\n });\n };\n\n if (resolvedItems.length === 0) {\n return null;\n }\n\n const s = resolveSize(size);\n const effectiveOverlap = overlap ?? s.overlap;\n const visibleItems = resolvedItems.slice(0, displayedAvatars);\n\n const baseAvatarSx: SxProps<Theme> = {\n width: s.px,\n height: s.px,\n fontSize: s.font,\n fontWeight: 700,\n border: (theme) => `${s.border}px solid ${theme.palette.background.paper}`,\n boxSizing: 'content-box',\n };\n\n const renderSingleAvatar = (item: AvatarItem, i: number) => {\n const hasImage = !!item.imageUrl && !failedUrls.has(item.imageUrl);\n const showBadgeFallback = !!item.badge;\n const itemKey = item.imageUrl ?? `${item.text ?? ''}-${item.badge ?? ''}-${i}`;\n\n // Defaults tirando al theme; item.color / item.backgroundColor tienen prioridad.\n const itemSx: SxProps<Theme> = {\n bgcolor: item.backgroundColor ?? 'action.selected',\n color: item.color ?? 'text.secondary',\n // Stacking manual: margen negativo al segundo avatar en adelante.\n ...(i > 0 && { marginLeft: `-${effectiveOverlap}px` }),\n zIndex: visibleItems.length - i,\n };\n\n const finalSx = mergeSx(mergeSx(baseAvatarSx, itemSx), avatarSx);\n\n const avatarEl = (\n <MuiAvatar\n alt={item.text || 'User avatar'}\n src={hasImage ? item.imageUrl : undefined}\n imgProps={{\n onError: () => {\n if (item.imageUrl) handleImageError(item.imageUrl);\n },\n }}\n sx={finalSx}\n >\n {!hasImage && item.content != null ? (\n item.content\n ) : !hasImage && showBadgeFallback ? (\n <span aria-label={item.text}>{item.badge}</span>\n ) : !hasImage ? (\n <AccountCircleIcon\n aria-label={item.text}\n sx={{ width: '100%', height: '100%' }}\n />\n ) : null}\n </MuiAvatar>\n );\n\n if (showTooltip && item.text) {\n return (\n <Tooltip key={itemKey} title={item.text}>\n {avatarEl}\n </Tooltip>\n );\n }\n return <React.Fragment key={itemKey}>{avatarEl}</React.Fragment>;\n };\n\n return (\n <Box\n className={className}\n sx={mergeSx(\n {\n display: 'flex',\n alignItems: 'center',\n lineHeight: 1,\n width: 'fit-content',\n },\n sx,\n )}\n >\n <Box sx={{ display: 'flex', alignItems: 'center' }}>\n {visibleItems.map((item, i) => renderSingleAvatar(item, i))}\n </Box>\n\n {showCaption && resolvedItems[0]?.text && (\n <Typography\n variant=\"caption\"\n sx={{\n ml: 1,\n fontSize: '0.75rem',\n fontWeight: 400,\n color: 'text.primary',\n }}\n >\n {type && resolvedItems.length === 1 && (\n <Box component=\"span\" sx={{ mr: 0.5 }}>\n {type}:\n </Box>\n )}\n {resolvedItems[0].text}\n {resolvedItems.length > 1 && ` +${resolvedItems.length - 1}`}\n </Typography>\n )}\n </Box>\n );\n};\n\nexport default Avatar;\n"],"names":["useState","_a","jsx","MuiAvatar","Tooltip","jsxs","Box","Typography"],"mappings":"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"}
@@ -46,6 +46,9 @@ const mergeSx = (base, extra) => {
46
46
  };
47
47
  const Avatar = ({
48
48
  items,
49
+ src,
50
+ alt,
51
+ fallback,
49
52
  type,
50
53
  displayedAvatars = 4,
51
54
  size = "sm",
@@ -57,6 +60,8 @@ const Avatar = ({
57
60
  overlap
58
61
  }) => {
59
62
  var _a;
63
+ const resolvedItems = items && items.length > 0 ? items : src || fallback ? [{ imageUrl: src, text: alt, content: fallback }] : [];
64
+ const showCaption = items && items.length > 0 ? showText : false;
60
65
  const [failedUrls, setFailedUrls] = useState(/* @__PURE__ */ new Set());
61
66
  const handleImageError = (url) => {
62
67
  setFailedUrls((prev) => {
@@ -66,12 +71,12 @@ const Avatar = ({
66
71
  return next;
67
72
  });
68
73
  };
69
- if (!items || items.length === 0) {
74
+ if (resolvedItems.length === 0) {
70
75
  return null;
71
76
  }
72
77
  const s = resolveSize(size);
73
78
  const effectiveOverlap = overlap != null ? overlap : s.overlap;
74
- const visibleItems = items.slice(0, displayedAvatars);
79
+ const visibleItems = resolvedItems.slice(0, displayedAvatars);
75
80
  const baseAvatarSx = {
76
81
  width: s.px,
77
82
  height: s.px,
@@ -103,7 +108,7 @@ const Avatar = ({
103
108
  }
104
109
  },
105
110
  sx: finalSx,
106
- children: !hasImage && showBadgeFallback ? /* @__PURE__ */ jsx("span", { "aria-label": item.text, children: item.badge }) : !hasImage ? /* @__PURE__ */ jsx(
111
+ children: !hasImage && item.content != null ? item.content : !hasImage && showBadgeFallback ? /* @__PURE__ */ jsx("span", { "aria-label": item.text, children: item.badge }) : !hasImage ? /* @__PURE__ */ jsx(
107
112
  AccountCircleIcon,
108
113
  {
109
114
  "aria-label": item.text,
@@ -132,7 +137,7 @@ const Avatar = ({
132
137
  ),
133
138
  children: [
134
139
  /* @__PURE__ */ jsx(Box, { sx: { display: "flex", alignItems: "center" }, children: visibleItems.map((item, i) => renderSingleAvatar(item, i)) }),
135
- showText && ((_a = items[0]) == null ? void 0 : _a.text) && /* @__PURE__ */ jsxs(
140
+ showCaption && ((_a = resolvedItems[0]) == null ? void 0 : _a.text) && /* @__PURE__ */ jsxs(
136
141
  Typography,
137
142
  {
138
143
  variant: "caption",
@@ -143,12 +148,12 @@ const Avatar = ({
143
148
  color: "text.primary"
144
149
  },
145
150
  children: [
146
- type && items.length === 1 && /* @__PURE__ */ jsxs(Box, { component: "span", sx: { mr: 0.5 }, children: [
151
+ type && resolvedItems.length === 1 && /* @__PURE__ */ jsxs(Box, { component: "span", sx: { mr: 0.5 }, children: [
147
152
  type,
148
153
  ":"
149
154
  ] }),
150
- items[0].text,
151
- items.length > 1 && ` +${items.length - 1}`
155
+ resolvedItems[0].text,
156
+ resolvedItems.length > 1 && ` +${resolvedItems.length - 1}`
152
157
  ]
153
158
  }
154
159
  )
@@ -159,4 +164,4 @@ const Avatar = ({
159
164
  export {
160
165
  Avatar as A
161
166
  };
162
- //# sourceMappingURL=Avatar-CgT7955R.js.map
167
+ //# sourceMappingURL=Avatar-H8akSege.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Avatar-H8akSege.js","sources":["../src/components/Avatar/Avatar.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport {\n Avatar as MuiAvatar,\n Box,\n Tooltip,\n Typography,\n type SxProps,\n type Theme,\n} from '@mui/material';\nimport AccountCircleIcon from '@mui/icons-material/AccountCircle';\n\nexport interface AvatarItem {\n text?: string;\n imageUrl?: string;\n badge?: string;\n color?: string;\n backgroundColor?: string;\n /** Contenido a renderizar como fallback cuando no hay imagen (ej. iniciales). */\n content?: React.ReactNode;\n}\n\nexport type AvatarSize = 'sm' | 'md' | 'lg' | 'xl' | number;\n\nexport interface AvatarProps {\n type?: string;\n /**\n * Lista de avatares (grupo apilado). Para un solo avatar puedes usar\n * `src`/`alt` en su lugar.\n */\n items?: AvatarItem[];\n /**\n * Atajo para un único avatar por URL de imagen. Equivale a\n * `items={[{ imageUrl: src, text: alt }]}`. Ignorado si `items` tiene elementos.\n */\n src?: string;\n /** Texto alternativo de la imagen cuando se usa `src` (modo single). */\n alt?: string;\n /** Fallback (ej. iniciales) cuando no hay imagen, en modo single (`src`). */\n fallback?: React.ReactNode;\n displayedAvatars?: number;\n size?: AvatarSize;\n showText?: boolean;\n showTooltip?: boolean;\n /**\n * sx aplicado al contenedor raíz.\n */\n sx?: SxProps<Theme>;\n /**\n * sx aplicado a cada MuiAvatar individual (se mergea sobre los defaults).\n */\n avatarSx?: SxProps<Theme>;\n className?: string;\n /**\n * Overlap (px) entre avatares cuando hay varios. Default depende del tamaño.\n */\n overlap?: number;\n}\n\n// Escala alineada con la escala de MUI (sm=32, md=40, lg=56, xl=96) con borde\n// proporcional para el efecto stacked.\nconst sizeMap: Record<\n Exclude<AvatarSize, number>,\n { px: number; border: number; font: number; overlap: number }\n> = {\n sm: { px: 32, border: 2, font: 14, overlap: 8 },\n md: { px: 40, border: 2, font: 16, overlap: 10 },\n lg: { px: 56, border: 3, font: 22, overlap: 14 },\n xl: { px: 96, border: 4, font: 36, overlap: 20 },\n};\n\nconst resolveSize = (size: AvatarSize) => {\n if (typeof size === 'number') {\n return {\n px: size,\n border: Math.max(2, Math.round(size * 0.05)),\n font: Math.round(size * 0.4),\n overlap: Math.round(size * 0.25),\n };\n }\n return sizeMap[size];\n};\n\nconst mergeSx = (base: SxProps<Theme>, extra?: SxProps<Theme>): SxProps<Theme> => {\n if (!extra) return base;\n const baseArr = Array.isArray(base) ? base : [base];\n const extraArr = Array.isArray(extra) ? extra : [extra];\n return [...baseArr, ...extraArr] as SxProps<Theme>;\n};\n\nexport const Avatar: React.FC<AvatarProps> = ({\n items,\n src,\n alt,\n fallback,\n type,\n displayedAvatars = 4,\n size = 'sm',\n showText = true,\n showTooltip = false,\n sx,\n avatarSx,\n className,\n overlap,\n}) => {\n // Modo single (`src`) vs grupo (`items`). En single no mostramos caption.\n const resolvedItems: AvatarItem[] =\n items && items.length > 0\n ? items\n : src || fallback\n ? [{ imageUrl: src, text: alt, content: fallback }]\n : [];\n const showCaption = items && items.length > 0 ? showText : false;\n // Indexamos por URL (no por posición) para que cambios en `items` — reorder,\n // filtrado, paginación — no hagan que un avatar válido \"herede\" el estado de\n // imagen rota de un item anterior que estaba en la misma posición.\n const [failedUrls, setFailedUrls] = useState<Set<string>>(new Set());\n\n const handleImageError = (url: string) => {\n setFailedUrls((prev) => {\n if (prev.has(url)) return prev;\n const next = new Set(prev);\n next.add(url);\n return next;\n });\n };\n\n if (resolvedItems.length === 0) {\n return null;\n }\n\n const s = resolveSize(size);\n const effectiveOverlap = overlap ?? s.overlap;\n const visibleItems = resolvedItems.slice(0, displayedAvatars);\n\n const baseAvatarSx: SxProps<Theme> = {\n width: s.px,\n height: s.px,\n fontSize: s.font,\n fontWeight: 700,\n border: (theme) => `${s.border}px solid ${theme.palette.background.paper}`,\n boxSizing: 'content-box',\n };\n\n const renderSingleAvatar = (item: AvatarItem, i: number) => {\n const hasImage = !!item.imageUrl && !failedUrls.has(item.imageUrl);\n const showBadgeFallback = !!item.badge;\n const itemKey = item.imageUrl ?? `${item.text ?? ''}-${item.badge ?? ''}-${i}`;\n\n // Defaults tirando al theme; item.color / item.backgroundColor tienen prioridad.\n const itemSx: SxProps<Theme> = {\n bgcolor: item.backgroundColor ?? 'action.selected',\n color: item.color ?? 'text.secondary',\n // Stacking manual: margen negativo al segundo avatar en adelante.\n ...(i > 0 && { marginLeft: `-${effectiveOverlap}px` }),\n zIndex: visibleItems.length - i,\n };\n\n const finalSx = mergeSx(mergeSx(baseAvatarSx, itemSx), avatarSx);\n\n const avatarEl = (\n <MuiAvatar\n alt={item.text || 'User avatar'}\n src={hasImage ? item.imageUrl : undefined}\n imgProps={{\n onError: () => {\n if (item.imageUrl) handleImageError(item.imageUrl);\n },\n }}\n sx={finalSx}\n >\n {!hasImage && item.content != null ? (\n item.content\n ) : !hasImage && showBadgeFallback ? (\n <span aria-label={item.text}>{item.badge}</span>\n ) : !hasImage ? (\n <AccountCircleIcon\n aria-label={item.text}\n sx={{ width: '100%', height: '100%' }}\n />\n ) : null}\n </MuiAvatar>\n );\n\n if (showTooltip && item.text) {\n return (\n <Tooltip key={itemKey} title={item.text}>\n {avatarEl}\n </Tooltip>\n );\n }\n return <React.Fragment key={itemKey}>{avatarEl}</React.Fragment>;\n };\n\n return (\n <Box\n className={className}\n sx={mergeSx(\n {\n display: 'flex',\n alignItems: 'center',\n lineHeight: 1,\n width: 'fit-content',\n },\n sx,\n )}\n >\n <Box sx={{ display: 'flex', alignItems: 'center' }}>\n {visibleItems.map((item, i) => renderSingleAvatar(item, i))}\n </Box>\n\n {showCaption && resolvedItems[0]?.text && (\n <Typography\n variant=\"caption\"\n sx={{\n ml: 1,\n fontSize: '0.75rem',\n fontWeight: 400,\n color: 'text.primary',\n }}\n >\n {type && resolvedItems.length === 1 && (\n <Box component=\"span\" sx={{ mr: 0.5 }}>\n {type}:\n </Box>\n )}\n {resolvedItems[0].text}\n {resolvedItems.length > 1 && ` +${resolvedItems.length - 1}`}\n </Typography>\n )}\n </Box>\n );\n};\n\nexport default Avatar;\n"],"names":["_a","MuiAvatar","React"],"mappings":"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"}
@@ -17,8 +17,8 @@ var __spreadValues = (a, b) => {
17
17
  return a;
18
18
  };
19
19
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
- import { jsx } from "react/jsx-runtime";
21
- import { useTheme, Card as Card$1 } from "@mui/material";
20
+ import { jsxs, jsx } from "react/jsx-runtime";
21
+ import { useTheme, Card as Card$1, CardHeader } from "@mui/material";
22
22
  import { r as resolvePreset } from "./resolvePreset-K6_BfWHD.js";
23
23
  const paddingMap = {
24
24
  none: 0,
@@ -60,6 +60,10 @@ function Card({
60
60
  clickable = false,
61
61
  preset,
62
62
  raised,
63
+ title,
64
+ subheader,
65
+ action,
66
+ headerSx,
63
67
  sx,
64
68
  onClick,
65
69
  onBlur,
@@ -78,7 +82,8 @@ function Card({
78
82
  ...presetSx ? [presetSx] : [],
79
83
  ...Array.isArray(sx) ? sx : sx ? [sx] : []
80
84
  ];
81
- return /* @__PURE__ */ jsx(
85
+ const hasHeader = title != null || subheader != null || action != null;
86
+ return /* @__PURE__ */ jsxs(
82
87
  Card$1,
83
88
  {
84
89
  sx: rootSx,
@@ -87,7 +92,18 @@ function Card({
87
92
  onClick,
88
93
  onBlur,
89
94
  elevation: 0,
90
- children
95
+ children: [
96
+ hasHeader && /* @__PURE__ */ jsx(
97
+ CardHeader,
98
+ {
99
+ title,
100
+ subheader,
101
+ action,
102
+ sx: headerSx
103
+ }
104
+ ),
105
+ children
106
+ ]
91
107
  }
92
108
  );
93
109
  }
@@ -95,4 +111,4 @@ export {
95
111
  Card as C,
96
112
  buildCardSx as b
97
113
  };
98
- //# sourceMappingURL=Card-B1wtavyl.js.map
114
+ //# sourceMappingURL=Card-C4dabH4V.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Card-C4dabH4V.js","sources":["../src/components/Card/Card.sx.ts","../src/components/Card/Card.tsx"],"sourcesContent":["import type { SxProps, Theme } from '@mui/material/styles';\n\nexport type CardVariant = 'elevated' | 'outlined' | 'plain';\nexport type CardPadding = 'none' | 'dense' | 'normal' | 'loose';\n\nexport interface BuildCardSxArgs {\n variant: CardVariant;\n padding: CardPadding;\n clickable: boolean;\n}\n\nconst paddingMap: Record<CardPadding, number> = {\n none: 0,\n dense: 1.5,\n normal: 2.5,\n loose: 4,\n};\n\n/**\n * Estilo base del Card. La prop `sx` del consumer se compone encima junto al\n * preset resuelto (en Card.tsx). Este builder solo se ocupa de variant +\n * padding para que siga siendo predecible.\n */\nexport function buildCardSx({\n variant,\n padding,\n clickable,\n}: BuildCardSxArgs): SxProps<Theme> {\n return (theme) => ({\n borderRadius: 2,\n backgroundColor: 'background.paper',\n boxShadow: variant === 'elevated' ? theme.shadows[1] : 'none',\n border:\n variant === 'outlined'\n ? `1px solid ${theme.palette.divider}`\n : 'none',\n transition: theme.transitions.create(\n ['box-shadow', 'transform', 'border-color'],\n { duration: theme.transitions.duration.shorter },\n ),\n cursor: clickable ? 'pointer' : 'default',\n ...(clickable && {\n '&:hover': {\n boxShadow:\n variant === 'elevated'\n ? theme.shadows[3]\n : variant === 'outlined'\n ? theme.shadows[1]\n : 'none',\n transform: variant !== 'plain' ? 'translateY(-1px)' : 'none',\n },\n '&:active': {\n transform: 'translateY(0)',\n },\n }),\n p: paddingMap[padding],\n });\n}\n","import { type ReactNode } from 'react';\nimport { Card as MuiCard, CardHeader, useTheme } from '@mui/material';\nimport type { SxProps, Theme } from '@mui/material/styles';\n\nimport { resolvePreset } from '../_shared/resolvePreset';\nimport { buildCardSx, type CardVariant, type CardPadding } from './Card.sx';\n\nexport interface CardProps {\n /** Contenido del Card. */\n children?: ReactNode;\n /**\n * Variante visual.\n * - `elevated` (default): sombra sutil.\n * - `outlined`: borde sin sombra.\n * - `plain`: sin borde ni sombra, útil para layouts con nesting.\n */\n variant?: CardVariant;\n /** Densidad del padding interno. */\n padding?: CardPadding;\n /** Si `true`, muestra feedback de hover/active (cursor, sombra). */\n clickable?: boolean;\n /**\n * Preset registrado en `theme.styles.Card`. `\"default\"` usa el estilo\n * built-in sin preset extra.\n */\n preset?: string;\n /**\n * MUI `raised` legacy — mantenido por backward-compat. Equivale a\n * `variant=\"elevated\"` con sombra mayor. Ignora `variant` si se usa.\n */\n raised?: boolean;\n /**\n * Título de cabecera. Si se provee (o `subheader`/`action`), se renderiza un\n * `CardHeader` antes del contenido. Reemplaza el patrón\n * `<Card><CardHeader/>...</Card>` de MUI.\n */\n title?: ReactNode;\n /** Subtítulo de la cabecera. */\n subheader?: ReactNode;\n /** Acción a la derecha de la cabecera (ej. botón/menú). */\n action?: ReactNode;\n /** sx aplicado al `CardHeader`. */\n headerSx?: SxProps<Theme>;\n /** sx del root. Se compone sobre el base + preset. */\n sx?: SxProps<Theme>;\n onClick?: () => void;\n onBlur?: () => void;\n className?: string;\n 'data-testid'?: string;\n}\n\nexport function Card({\n children,\n variant,\n padding = 'normal',\n clickable = false,\n preset,\n raised,\n title,\n subheader,\n action,\n headerSx,\n sx,\n onClick,\n onBlur,\n className,\n 'data-testid': dataTestId,\n}: CardProps) {\n const theme = useTheme();\n\n const resolvedVariant: CardVariant = raised\n ? 'elevated'\n : (variant ?? 'elevated');\n\n const presetSx = resolvePreset('Card', preset, theme);\n\n const rootSx: SxProps<Theme> = [\n buildCardSx({\n variant: resolvedVariant,\n padding,\n clickable: clickable || Boolean(onClick),\n }),\n ...(presetSx ? [presetSx] : []),\n ...(Array.isArray(sx) ? sx : sx ? [sx] : []),\n ];\n\n const hasHeader =\n title != null || subheader != null || action != null;\n\n return (\n <MuiCard\n sx={rootSx}\n className={className}\n data-testid={dataTestId}\n onClick={onClick}\n onBlur={onBlur}\n elevation={0}\n >\n {hasHeader && (\n <CardHeader\n title={title}\n subheader={subheader}\n action={action}\n sx={headerSx}\n />\n )}\n {children}\n </MuiCard>\n );\n}\n\nexport default Card;\n"],"names":["MuiCard"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAWA,MAAM,aAA0C;AAAA,EAC9C,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AACT;AAOO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACF,GAAoC;AAClC,SAAO,CAAC,UAAW;AAAA,IACjB,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,WAAW,YAAY,aAAa,MAAM,QAAQ,CAAC,IAAI;AAAA,IACvD,QACE,YAAY,aACR,aAAa,MAAM,QAAQ,OAAO,KAClC;AAAA,IACN,YAAY,MAAM,YAAY;AAAA,MAC5B,CAAC,cAAc,aAAa,cAAc;AAAA,MAC1C,EAAE,UAAU,MAAM,YAAY,SAAS,QAAA;AAAA,IAAQ;AAAA,IAEjD,QAAQ,YAAY,YAAY;AAAA,KAC5B,aAAa;AAAA,IACf,WAAW;AAAA,MACT,WACE,YAAY,aACR,MAAM,QAAQ,CAAC,IACf,YAAY,aACV,MAAM,QAAQ,CAAC,IACf;AAAA,MACR,WAAW,YAAY,UAAU,qBAAqB;AAAA,IAAA;AAAA,IAExD,YAAY;AAAA,MACV,WAAW;AAAA,IAAA;AAAA,EACb,IAzBe;AAAA,IA2BjB,GAAG,WAAW,OAAO;AAAA,EAAA;AAEzB;ACNO,SAAS,KAAK;AAAA,EACnB;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AACjB,GAAc;AACZ,QAAM,QAAQ,SAAA;AAEd,QAAM,kBAA+B,SACjC,aACC,4BAAW;AAEhB,QAAM,WAAW,cAAc,QAAQ,QAAQ,KAAK;AAEpD,QAAM,SAAyB;AAAA,IAC7B,YAAY;AAAA,MACV,SAAS;AAAA,MACT;AAAA,MACA,WAAW,aAAa,QAAQ,OAAO;AAAA,IAAA,CACxC;AAAA,IACD,GAAI,WAAW,CAAC,QAAQ,IAAI,CAAA;AAAA,IAC5B,GAAI,MAAM,QAAQ,EAAE,IAAI,KAAK,KAAK,CAAC,EAAE,IAAI,CAAA;AAAA,EAAC;AAG5C,QAAM,YACJ,SAAS,QAAQ,aAAa,QAAQ,UAAU;AAElD,SACE;AAAA,IAACA;AAAAA,IAAA;AAAA,MACC,IAAI;AAAA,MACJ;AAAA,MACA,eAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MAEV,UAAA;AAAA,QAAA,aACC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA,YACA,IAAI;AAAA,UAAA;AAAA,QAAA;AAAA,QAGP;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGP;"}
@@ -61,6 +61,10 @@ function Card({
61
61
  clickable = false,
62
62
  preset,
63
63
  raised,
64
+ title,
65
+ subheader,
66
+ action,
67
+ headerSx,
64
68
  sx,
65
69
  onClick,
66
70
  onBlur,
@@ -79,7 +83,8 @@ function Card({
79
83
  ...presetSx ? [presetSx] : [],
80
84
  ...Array.isArray(sx) ? sx : sx ? [sx] : []
81
85
  ];
82
- return /* @__PURE__ */ jsxRuntime.jsx(
86
+ const hasHeader = title != null || subheader != null || action != null;
87
+ return /* @__PURE__ */ jsxRuntime.jsxs(
83
88
  material.Card,
84
89
  {
85
90
  sx: rootSx,
@@ -88,10 +93,21 @@ function Card({
88
93
  onClick,
89
94
  onBlur,
90
95
  elevation: 0,
91
- children
96
+ children: [
97
+ hasHeader && /* @__PURE__ */ jsxRuntime.jsx(
98
+ material.CardHeader,
99
+ {
100
+ title,
101
+ subheader,
102
+ action,
103
+ sx: headerSx
104
+ }
105
+ ),
106
+ children
107
+ ]
92
108
  }
93
109
  );
94
110
  }
95
111
  exports.Card = Card;
96
112
  exports.buildCardSx = buildCardSx;
97
- //# sourceMappingURL=Card-DfdU610V.cjs.map
113
+ //# sourceMappingURL=Card-DIZLchyP.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Card-DIZLchyP.cjs","sources":["../src/components/Card/Card.sx.ts","../src/components/Card/Card.tsx"],"sourcesContent":["import type { SxProps, Theme } from '@mui/material/styles';\n\nexport type CardVariant = 'elevated' | 'outlined' | 'plain';\nexport type CardPadding = 'none' | 'dense' | 'normal' | 'loose';\n\nexport interface BuildCardSxArgs {\n variant: CardVariant;\n padding: CardPadding;\n clickable: boolean;\n}\n\nconst paddingMap: Record<CardPadding, number> = {\n none: 0,\n dense: 1.5,\n normal: 2.5,\n loose: 4,\n};\n\n/**\n * Estilo base del Card. La prop `sx` del consumer se compone encima junto al\n * preset resuelto (en Card.tsx). Este builder solo se ocupa de variant +\n * padding para que siga siendo predecible.\n */\nexport function buildCardSx({\n variant,\n padding,\n clickable,\n}: BuildCardSxArgs): SxProps<Theme> {\n return (theme) => ({\n borderRadius: 2,\n backgroundColor: 'background.paper',\n boxShadow: variant === 'elevated' ? theme.shadows[1] : 'none',\n border:\n variant === 'outlined'\n ? `1px solid ${theme.palette.divider}`\n : 'none',\n transition: theme.transitions.create(\n ['box-shadow', 'transform', 'border-color'],\n { duration: theme.transitions.duration.shorter },\n ),\n cursor: clickable ? 'pointer' : 'default',\n ...(clickable && {\n '&:hover': {\n boxShadow:\n variant === 'elevated'\n ? theme.shadows[3]\n : variant === 'outlined'\n ? theme.shadows[1]\n : 'none',\n transform: variant !== 'plain' ? 'translateY(-1px)' : 'none',\n },\n '&:active': {\n transform: 'translateY(0)',\n },\n }),\n p: paddingMap[padding],\n });\n}\n","import { type ReactNode } from 'react';\nimport { Card as MuiCard, CardHeader, useTheme } from '@mui/material';\nimport type { SxProps, Theme } from '@mui/material/styles';\n\nimport { resolvePreset } from '../_shared/resolvePreset';\nimport { buildCardSx, type CardVariant, type CardPadding } from './Card.sx';\n\nexport interface CardProps {\n /** Contenido del Card. */\n children?: ReactNode;\n /**\n * Variante visual.\n * - `elevated` (default): sombra sutil.\n * - `outlined`: borde sin sombra.\n * - `plain`: sin borde ni sombra, útil para layouts con nesting.\n */\n variant?: CardVariant;\n /** Densidad del padding interno. */\n padding?: CardPadding;\n /** Si `true`, muestra feedback de hover/active (cursor, sombra). */\n clickable?: boolean;\n /**\n * Preset registrado en `theme.styles.Card`. `\"default\"` usa el estilo\n * built-in sin preset extra.\n */\n preset?: string;\n /**\n * MUI `raised` legacy — mantenido por backward-compat. Equivale a\n * `variant=\"elevated\"` con sombra mayor. Ignora `variant` si se usa.\n */\n raised?: boolean;\n /**\n * Título de cabecera. Si se provee (o `subheader`/`action`), se renderiza un\n * `CardHeader` antes del contenido. Reemplaza el patrón\n * `<Card><CardHeader/>...</Card>` de MUI.\n */\n title?: ReactNode;\n /** Subtítulo de la cabecera. */\n subheader?: ReactNode;\n /** Acción a la derecha de la cabecera (ej. botón/menú). */\n action?: ReactNode;\n /** sx aplicado al `CardHeader`. */\n headerSx?: SxProps<Theme>;\n /** sx del root. Se compone sobre el base + preset. */\n sx?: SxProps<Theme>;\n onClick?: () => void;\n onBlur?: () => void;\n className?: string;\n 'data-testid'?: string;\n}\n\nexport function Card({\n children,\n variant,\n padding = 'normal',\n clickable = false,\n preset,\n raised,\n title,\n subheader,\n action,\n headerSx,\n sx,\n onClick,\n onBlur,\n className,\n 'data-testid': dataTestId,\n}: CardProps) {\n const theme = useTheme();\n\n const resolvedVariant: CardVariant = raised\n ? 'elevated'\n : (variant ?? 'elevated');\n\n const presetSx = resolvePreset('Card', preset, theme);\n\n const rootSx: SxProps<Theme> = [\n buildCardSx({\n variant: resolvedVariant,\n padding,\n clickable: clickable || Boolean(onClick),\n }),\n ...(presetSx ? [presetSx] : []),\n ...(Array.isArray(sx) ? sx : sx ? [sx] : []),\n ];\n\n const hasHeader =\n title != null || subheader != null || action != null;\n\n return (\n <MuiCard\n sx={rootSx}\n className={className}\n data-testid={dataTestId}\n onClick={onClick}\n onBlur={onBlur}\n elevation={0}\n >\n {hasHeader && (\n <CardHeader\n title={title}\n subheader={subheader}\n action={action}\n sx={headerSx}\n />\n )}\n {children}\n </MuiCard>\n );\n}\n\nexport default Card;\n"],"names":["useTheme","resolvePreset","jsxs","MuiCard","jsx","CardHeader"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAWA,MAAM,aAA0C;AAAA,EAC9C,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AACT;AAOO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACF,GAAoC;AAClC,SAAO,CAAC,UAAW;AAAA,IACjB,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,WAAW,YAAY,aAAa,MAAM,QAAQ,CAAC,IAAI;AAAA,IACvD,QACE,YAAY,aACR,aAAa,MAAM,QAAQ,OAAO,KAClC;AAAA,IACN,YAAY,MAAM,YAAY;AAAA,MAC5B,CAAC,cAAc,aAAa,cAAc;AAAA,MAC1C,EAAE,UAAU,MAAM,YAAY,SAAS,QAAA;AAAA,IAAQ;AAAA,IAEjD,QAAQ,YAAY,YAAY;AAAA,KAC5B,aAAa;AAAA,IACf,WAAW;AAAA,MACT,WACE,YAAY,aACR,MAAM,QAAQ,CAAC,IACf,YAAY,aACV,MAAM,QAAQ,CAAC,IACf;AAAA,MACR,WAAW,YAAY,UAAU,qBAAqB;AAAA,IAAA;AAAA,IAExD,YAAY;AAAA,MACV,WAAW;AAAA,IAAA;AAAA,EACb,IAzBe;AAAA,IA2BjB,GAAG,WAAW,OAAO;AAAA,EAAA;AAEzB;ACNO,SAAS,KAAK;AAAA,EACnB;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AACjB,GAAc;AACZ,QAAM,QAAQA,SAAAA,SAAA;AAEd,QAAM,kBAA+B,SACjC,aACC,4BAAW;AAEhB,QAAM,WAAWC,cAAAA,cAAc,QAAQ,QAAQ,KAAK;AAEpD,QAAM,SAAyB;AAAA,IAC7B,YAAY;AAAA,MACV,SAAS;AAAA,MACT;AAAA,MACA,WAAW,aAAa,QAAQ,OAAO;AAAA,IAAA,CACxC;AAAA,IACD,GAAI,WAAW,CAAC,QAAQ,IAAI,CAAA;AAAA,IAC5B,GAAI,MAAM,QAAQ,EAAE,IAAI,KAAK,KAAK,CAAC,EAAE,IAAI,CAAA;AAAA,EAAC;AAG5C,QAAM,YACJ,SAAS,QAAQ,aAAa,QAAQ,UAAU;AAElD,SACEC,2BAAAA;AAAAA,IAACC,SAAAA;AAAAA,IAAA;AAAA,MACC,IAAI;AAAA,MACJ;AAAA,MACA,eAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MAEV,UAAA;AAAA,QAAA,aACCC,2BAAAA;AAAAA,UAACC,SAAAA;AAAAA,UAAA;AAAA,YACC;AAAA,YACA;AAAA,YACA;AAAA,YACA,IAAI;AAAA,UAAA;AAAA,QAAA;AAAA,QAGP;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGP;;;"}
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const Avatar = require("../../Avatar-CuSrK8Wn.cjs");
3
+ const Avatar = require("../../Avatar-Dw5rzayR.cjs");
4
4
  exports.Avatar = Avatar.Avatar;
5
5
  //# sourceMappingURL=Avatar.cjs.map
@@ -6,11 +6,26 @@ export interface AvatarItem {
6
6
  badge?: string;
7
7
  color?: string;
8
8
  backgroundColor?: string;
9
+ /** Contenido a renderizar como fallback cuando no hay imagen (ej. iniciales). */
10
+ content?: React.ReactNode;
9
11
  }
10
12
  export type AvatarSize = 'sm' | 'md' | 'lg' | 'xl' | number;
11
13
  export interface AvatarProps {
12
14
  type?: string;
13
- items: AvatarItem[];
15
+ /**
16
+ * Lista de avatares (grupo apilado). Para un solo avatar puedes usar
17
+ * `src`/`alt` en su lugar.
18
+ */
19
+ items?: AvatarItem[];
20
+ /**
21
+ * Atajo para un único avatar por URL de imagen. Equivale a
22
+ * `items={[{ imageUrl: src, text: alt }]}`. Ignorado si `items` tiene elementos.
23
+ */
24
+ src?: string;
25
+ /** Texto alternativo de la imagen cuando se usa `src` (modo single). */
26
+ alt?: string;
27
+ /** Fallback (ej. iniciales) cuando no hay imagen, en modo single (`src`). */
28
+ fallback?: React.ReactNode;
14
29
  displayedAvatars?: number;
15
30
  size?: AvatarSize;
16
31
  showText?: boolean;
@@ -1,4 +1,4 @@
1
- import { A } from "../../Avatar-CgT7955R.js";
1
+ import { A } from "../../Avatar-H8akSege.js";
2
2
  export {
3
3
  A as Avatar
4
4
  };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
- const Card = require("../../Card-DfdU610V.cjs");
3
+ const Card = require("../../Card-DIZLchyP.cjs");
4
4
  exports.Card = Card.Card;
5
5
  exports.buildCardSx = Card.buildCardSx;
6
6
  exports.default = Card.Card;
@@ -25,6 +25,18 @@ export interface CardProps {
25
25
  * `variant="elevated"` con sombra mayor. Ignora `variant` si se usa.
26
26
  */
27
27
  raised?: boolean;
28
+ /**
29
+ * Título de cabecera. Si se provee (o `subheader`/`action`), se renderiza un
30
+ * `CardHeader` antes del contenido. Reemplaza el patrón
31
+ * `<Card><CardHeader/>...</Card>` de MUI.
32
+ */
33
+ title?: ReactNode;
34
+ /** Subtítulo de la cabecera. */
35
+ subheader?: ReactNode;
36
+ /** Acción a la derecha de la cabecera (ej. botón/menú). */
37
+ action?: ReactNode;
38
+ /** sx aplicado al `CardHeader`. */
39
+ headerSx?: SxProps<Theme>;
28
40
  /** sx del root. Se compone sobre el base + preset. */
29
41
  sx?: SxProps<Theme>;
30
42
  onClick?: () => void;
@@ -32,5 +44,5 @@ export interface CardProps {
32
44
  className?: string;
33
45
  'data-testid'?: string;
34
46
  }
35
- export declare function Card({ children, variant, padding, clickable, preset, raised, sx, onClick, onBlur, className, 'data-testid': dataTestId, }: CardProps): import("react/jsx-runtime").JSX.Element;
47
+ export declare function Card({ children, variant, padding, clickable, preset, raised, title, subheader, action, headerSx, sx, onClick, onBlur, className, 'data-testid': dataTestId, }: CardProps): import("react/jsx-runtime").JSX.Element;
36
48
  export default Card;
@@ -1,4 +1,4 @@
1
- import { C, b, C as C2 } from "../../Card-B1wtavyl.js";
1
+ import { C, b, C as C2 } from "../../Card-C4dabH4V.js";
2
2
  export {
3
3
  C as Card,
4
4
  b as buildCardSx,
package/index.cjs CHANGED
@@ -4,10 +4,10 @@ require('./index.css');const resolvePreset = require("./resolvePreset-CxTI6_Ln.c
4
4
  const material = require("@mui/material");
5
5
  const timeViewRenderers = require("@mui/x-date-pickers/timeViewRenderers");
6
6
  const Button = require("./Button-C17mExpd.cjs");
7
- const Card = require("./Card-DfdU610V.cjs");
7
+ const Card = require("./Card-DIZLchyP.cjs");
8
8
  const Chip = require("./Chip-qoJLDiva.cjs");
9
9
  const components_Column = require("./components/Column/Column.cjs");
10
- const Avatar = require("./Avatar-CuSrK8Wn.cjs");
10
+ const Avatar = require("./Avatar-Dw5rzayR.cjs");
11
11
  const Stat = require("./Stat-BUcFCGrz.cjs");
12
12
  const Step = require("./Step-Nd7SJbRZ.cjs");
13
13
  const Tab = require("./Tab-BbP8jBcK.cjs");
package/index.js CHANGED
@@ -2,10 +2,10 @@ import { D, r } from "./resolvePreset-K6_BfWHD.js";
2
2
  import { Alert, AlertTitle, Backdrop, Box, CircularProgress, Collapse, Container, Divider, Fade, Grid, Grow, LinearProgress, Link, List, ListItem, ListItemButton, ListItemIcon, ListItemText, ListSubheader, Paper, Skeleton, Snackbar, Stack, ThemeProvider, Typography, Zoom, alpha, createTheme, styled, useMediaQuery, useTheme } from "@mui/material";
3
3
  import { renderMultiSectionDigitalClockTimeView } from "@mui/x-date-pickers/timeViewRenderers";
4
4
  import { B } from "./Button-UkkP-bNw.js";
5
- import { C } from "./Card-B1wtavyl.js";
5
+ import { C } from "./Card-C4dabH4V.js";
6
6
  import { C as C2 } from "./Chip-OPYQ1uQ_.js";
7
7
  import { Column } from "./components/Column/Column.js";
8
- import { A } from "./Avatar-CgT7955R.js";
8
+ import { A } from "./Avatar-H8akSege.js";
9
9
  import { S } from "./Stat-C06A_izS.js";
10
10
  import { a, S as S2 } from "./Step-BArsou1V.js";
11
11
  import { a as a2, T } from "./Tab-BxSxKJsP.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soyfri/shared-library",
3
- "version": "2.0.0-beta.23",
3
+ "version": "2.0.0-beta.25",
4
4
  "main": "./index.cjs",
5
5
  "module": "./index.js",
6
6
  "types": "./index.d.ts",
@@ -1 +0,0 @@
1
- {"version":3,"file":"Avatar-CgT7955R.js","sources":["../src/components/Avatar/Avatar.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport {\n Avatar as MuiAvatar,\n Box,\n Tooltip,\n Typography,\n type SxProps,\n type Theme,\n} from '@mui/material';\nimport AccountCircleIcon from '@mui/icons-material/AccountCircle';\n\nexport interface AvatarItem {\n text?: string;\n imageUrl?: string;\n badge?: string;\n color?: string;\n backgroundColor?: string;\n}\n\nexport type AvatarSize = 'sm' | 'md' | 'lg' | 'xl' | number;\n\nexport interface AvatarProps {\n type?: string;\n items: AvatarItem[];\n displayedAvatars?: number;\n size?: AvatarSize;\n showText?: boolean;\n showTooltip?: boolean;\n /**\n * sx aplicado al contenedor raíz.\n */\n sx?: SxProps<Theme>;\n /**\n * sx aplicado a cada MuiAvatar individual (se mergea sobre los defaults).\n */\n avatarSx?: SxProps<Theme>;\n className?: string;\n /**\n * Overlap (px) entre avatares cuando hay varios. Default depende del tamaño.\n */\n overlap?: number;\n}\n\n// Escala alineada con la escala de MUI (sm=32, md=40, lg=56, xl=96) con borde\n// proporcional para el efecto stacked.\nconst sizeMap: Record<\n Exclude<AvatarSize, number>,\n { px: number; border: number; font: number; overlap: number }\n> = {\n sm: { px: 32, border: 2, font: 14, overlap: 8 },\n md: { px: 40, border: 2, font: 16, overlap: 10 },\n lg: { px: 56, border: 3, font: 22, overlap: 14 },\n xl: { px: 96, border: 4, font: 36, overlap: 20 },\n};\n\nconst resolveSize = (size: AvatarSize) => {\n if (typeof size === 'number') {\n return {\n px: size,\n border: Math.max(2, Math.round(size * 0.05)),\n font: Math.round(size * 0.4),\n overlap: Math.round(size * 0.25),\n };\n }\n return sizeMap[size];\n};\n\nconst mergeSx = (base: SxProps<Theme>, extra?: SxProps<Theme>): SxProps<Theme> => {\n if (!extra) return base;\n const baseArr = Array.isArray(base) ? base : [base];\n const extraArr = Array.isArray(extra) ? extra : [extra];\n return [...baseArr, ...extraArr] as SxProps<Theme>;\n};\n\nexport const Avatar: React.FC<AvatarProps> = ({\n items,\n type,\n displayedAvatars = 4,\n size = 'sm',\n showText = true,\n showTooltip = false,\n sx,\n avatarSx,\n className,\n overlap,\n}) => {\n // Indexamos por URL (no por posición) para que cambios en `items` — reorder,\n // filtrado, paginación — no hagan que un avatar válido \"herede\" el estado de\n // imagen rota de un item anterior que estaba en la misma posición.\n const [failedUrls, setFailedUrls] = useState<Set<string>>(new Set());\n\n const handleImageError = (url: string) => {\n setFailedUrls((prev) => {\n if (prev.has(url)) return prev;\n const next = new Set(prev);\n next.add(url);\n return next;\n });\n };\n\n if (!items || items.length === 0) {\n return null;\n }\n\n const s = resolveSize(size);\n const effectiveOverlap = overlap ?? s.overlap;\n const visibleItems = items.slice(0, displayedAvatars);\n\n const baseAvatarSx: SxProps<Theme> = {\n width: s.px,\n height: s.px,\n fontSize: s.font,\n fontWeight: 700,\n border: (theme) => `${s.border}px solid ${theme.palette.background.paper}`,\n boxSizing: 'content-box',\n };\n\n const renderSingleAvatar = (item: AvatarItem, i: number) => {\n const hasImage = !!item.imageUrl && !failedUrls.has(item.imageUrl);\n const showBadgeFallback = !!item.badge;\n const itemKey = item.imageUrl ?? `${item.text ?? ''}-${item.badge ?? ''}-${i}`;\n\n // Defaults tirando al theme; item.color / item.backgroundColor tienen prioridad.\n const itemSx: SxProps<Theme> = {\n bgcolor: item.backgroundColor ?? 'action.selected',\n color: item.color ?? 'text.secondary',\n // Stacking manual: margen negativo al segundo avatar en adelante.\n ...(i > 0 && { marginLeft: `-${effectiveOverlap}px` }),\n zIndex: visibleItems.length - i,\n };\n\n const finalSx = mergeSx(mergeSx(baseAvatarSx, itemSx), avatarSx);\n\n const avatarEl = (\n <MuiAvatar\n alt={item.text || 'User avatar'}\n src={hasImage ? item.imageUrl : undefined}\n imgProps={{\n onError: () => {\n if (item.imageUrl) handleImageError(item.imageUrl);\n },\n }}\n sx={finalSx}\n >\n {!hasImage && showBadgeFallback ? (\n <span aria-label={item.text}>{item.badge}</span>\n ) : !hasImage ? (\n <AccountCircleIcon\n aria-label={item.text}\n sx={{ width: '100%', height: '100%' }}\n />\n ) : null}\n </MuiAvatar>\n );\n\n if (showTooltip && item.text) {\n return (\n <Tooltip key={itemKey} title={item.text}>\n {avatarEl}\n </Tooltip>\n );\n }\n return <React.Fragment key={itemKey}>{avatarEl}</React.Fragment>;\n };\n\n return (\n <Box\n className={className}\n sx={mergeSx(\n {\n display: 'flex',\n alignItems: 'center',\n lineHeight: 1,\n width: 'fit-content',\n },\n sx,\n )}\n >\n <Box sx={{ display: 'flex', alignItems: 'center' }}>\n {visibleItems.map((item, i) => renderSingleAvatar(item, i))}\n </Box>\n\n {showText && items[0]?.text && (\n <Typography\n variant=\"caption\"\n sx={{\n ml: 1,\n fontSize: '0.75rem',\n fontWeight: 400,\n color: 'text.primary',\n }}\n >\n {type && items.length === 1 && (\n <Box component=\"span\" sx={{ mr: 0.5 }}>\n {type}:\n </Box>\n )}\n {items[0].text}\n {items.length > 1 && ` +${items.length - 1}`}\n </Typography>\n )}\n </Box>\n );\n};\n\nexport default Avatar;\n"],"names":["_a","MuiAvatar","React"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Avatar-CuSrK8Wn.cjs","sources":["../src/components/Avatar/Avatar.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport {\n Avatar as MuiAvatar,\n Box,\n Tooltip,\n Typography,\n type SxProps,\n type Theme,\n} from '@mui/material';\nimport AccountCircleIcon from '@mui/icons-material/AccountCircle';\n\nexport interface AvatarItem {\n text?: string;\n imageUrl?: string;\n badge?: string;\n color?: string;\n backgroundColor?: string;\n}\n\nexport type AvatarSize = 'sm' | 'md' | 'lg' | 'xl' | number;\n\nexport interface AvatarProps {\n type?: string;\n items: AvatarItem[];\n displayedAvatars?: number;\n size?: AvatarSize;\n showText?: boolean;\n showTooltip?: boolean;\n /**\n * sx aplicado al contenedor raíz.\n */\n sx?: SxProps<Theme>;\n /**\n * sx aplicado a cada MuiAvatar individual (se mergea sobre los defaults).\n */\n avatarSx?: SxProps<Theme>;\n className?: string;\n /**\n * Overlap (px) entre avatares cuando hay varios. Default depende del tamaño.\n */\n overlap?: number;\n}\n\n// Escala alineada con la escala de MUI (sm=32, md=40, lg=56, xl=96) con borde\n// proporcional para el efecto stacked.\nconst sizeMap: Record<\n Exclude<AvatarSize, number>,\n { px: number; border: number; font: number; overlap: number }\n> = {\n sm: { px: 32, border: 2, font: 14, overlap: 8 },\n md: { px: 40, border: 2, font: 16, overlap: 10 },\n lg: { px: 56, border: 3, font: 22, overlap: 14 },\n xl: { px: 96, border: 4, font: 36, overlap: 20 },\n};\n\nconst resolveSize = (size: AvatarSize) => {\n if (typeof size === 'number') {\n return {\n px: size,\n border: Math.max(2, Math.round(size * 0.05)),\n font: Math.round(size * 0.4),\n overlap: Math.round(size * 0.25),\n };\n }\n return sizeMap[size];\n};\n\nconst mergeSx = (base: SxProps<Theme>, extra?: SxProps<Theme>): SxProps<Theme> => {\n if (!extra) return base;\n const baseArr = Array.isArray(base) ? base : [base];\n const extraArr = Array.isArray(extra) ? extra : [extra];\n return [...baseArr, ...extraArr] as SxProps<Theme>;\n};\n\nexport const Avatar: React.FC<AvatarProps> = ({\n items,\n type,\n displayedAvatars = 4,\n size = 'sm',\n showText = true,\n showTooltip = false,\n sx,\n avatarSx,\n className,\n overlap,\n}) => {\n // Indexamos por URL (no por posición) para que cambios en `items` — reorder,\n // filtrado, paginación — no hagan que un avatar válido \"herede\" el estado de\n // imagen rota de un item anterior que estaba en la misma posición.\n const [failedUrls, setFailedUrls] = useState<Set<string>>(new Set());\n\n const handleImageError = (url: string) => {\n setFailedUrls((prev) => {\n if (prev.has(url)) return prev;\n const next = new Set(prev);\n next.add(url);\n return next;\n });\n };\n\n if (!items || items.length === 0) {\n return null;\n }\n\n const s = resolveSize(size);\n const effectiveOverlap = overlap ?? s.overlap;\n const visibleItems = items.slice(0, displayedAvatars);\n\n const baseAvatarSx: SxProps<Theme> = {\n width: s.px,\n height: s.px,\n fontSize: s.font,\n fontWeight: 700,\n border: (theme) => `${s.border}px solid ${theme.palette.background.paper}`,\n boxSizing: 'content-box',\n };\n\n const renderSingleAvatar = (item: AvatarItem, i: number) => {\n const hasImage = !!item.imageUrl && !failedUrls.has(item.imageUrl);\n const showBadgeFallback = !!item.badge;\n const itemKey = item.imageUrl ?? `${item.text ?? ''}-${item.badge ?? ''}-${i}`;\n\n // Defaults tirando al theme; item.color / item.backgroundColor tienen prioridad.\n const itemSx: SxProps<Theme> = {\n bgcolor: item.backgroundColor ?? 'action.selected',\n color: item.color ?? 'text.secondary',\n // Stacking manual: margen negativo al segundo avatar en adelante.\n ...(i > 0 && { marginLeft: `-${effectiveOverlap}px` }),\n zIndex: visibleItems.length - i,\n };\n\n const finalSx = mergeSx(mergeSx(baseAvatarSx, itemSx), avatarSx);\n\n const avatarEl = (\n <MuiAvatar\n alt={item.text || 'User avatar'}\n src={hasImage ? item.imageUrl : undefined}\n imgProps={{\n onError: () => {\n if (item.imageUrl) handleImageError(item.imageUrl);\n },\n }}\n sx={finalSx}\n >\n {!hasImage && showBadgeFallback ? (\n <span aria-label={item.text}>{item.badge}</span>\n ) : !hasImage ? (\n <AccountCircleIcon\n aria-label={item.text}\n sx={{ width: '100%', height: '100%' }}\n />\n ) : null}\n </MuiAvatar>\n );\n\n if (showTooltip && item.text) {\n return (\n <Tooltip key={itemKey} title={item.text}>\n {avatarEl}\n </Tooltip>\n );\n }\n return <React.Fragment key={itemKey}>{avatarEl}</React.Fragment>;\n };\n\n return (\n <Box\n className={className}\n sx={mergeSx(\n {\n display: 'flex',\n alignItems: 'center',\n lineHeight: 1,\n width: 'fit-content',\n },\n sx,\n )}\n >\n <Box sx={{ display: 'flex', alignItems: 'center' }}>\n {visibleItems.map((item, i) => renderSingleAvatar(item, i))}\n </Box>\n\n {showText && items[0]?.text && (\n <Typography\n variant=\"caption\"\n sx={{\n ml: 1,\n fontSize: '0.75rem',\n fontWeight: 400,\n color: 'text.primary',\n }}\n >\n {type && items.length === 1 && (\n <Box component=\"span\" sx={{ mr: 0.5 }}>\n {type}:\n </Box>\n )}\n {items[0].text}\n {items.length > 1 && ` +${items.length - 1}`}\n </Typography>\n )}\n </Box>\n );\n};\n\nexport default Avatar;\n"],"names":["useState","_a","jsx","MuiAvatar","Tooltip","jsxs","Box","Typography"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Card-B1wtavyl.js","sources":["../src/components/Card/Card.sx.ts","../src/components/Card/Card.tsx"],"sourcesContent":["import type { SxProps, Theme } from '@mui/material/styles';\n\nexport type CardVariant = 'elevated' | 'outlined' | 'plain';\nexport type CardPadding = 'none' | 'dense' | 'normal' | 'loose';\n\nexport interface BuildCardSxArgs {\n variant: CardVariant;\n padding: CardPadding;\n clickable: boolean;\n}\n\nconst paddingMap: Record<CardPadding, number> = {\n none: 0,\n dense: 1.5,\n normal: 2.5,\n loose: 4,\n};\n\n/**\n * Estilo base del Card. La prop `sx` del consumer se compone encima junto al\n * preset resuelto (en Card.tsx). Este builder solo se ocupa de variant +\n * padding para que siga siendo predecible.\n */\nexport function buildCardSx({\n variant,\n padding,\n clickable,\n}: BuildCardSxArgs): SxProps<Theme> {\n return (theme) => ({\n borderRadius: 2,\n backgroundColor: 'background.paper',\n boxShadow: variant === 'elevated' ? theme.shadows[1] : 'none',\n border:\n variant === 'outlined'\n ? `1px solid ${theme.palette.divider}`\n : 'none',\n transition: theme.transitions.create(\n ['box-shadow', 'transform', 'border-color'],\n { duration: theme.transitions.duration.shorter },\n ),\n cursor: clickable ? 'pointer' : 'default',\n ...(clickable && {\n '&:hover': {\n boxShadow:\n variant === 'elevated'\n ? theme.shadows[3]\n : variant === 'outlined'\n ? theme.shadows[1]\n : 'none',\n transform: variant !== 'plain' ? 'translateY(-1px)' : 'none',\n },\n '&:active': {\n transform: 'translateY(0)',\n },\n }),\n p: paddingMap[padding],\n });\n}\n","import { type ReactNode } from 'react';\nimport { Card as MuiCard, useTheme } from '@mui/material';\nimport type { SxProps, Theme } from '@mui/material/styles';\n\nimport { resolvePreset } from '../_shared/resolvePreset';\nimport { buildCardSx, type CardVariant, type CardPadding } from './Card.sx';\n\nexport interface CardProps {\n /** Contenido del Card. */\n children?: ReactNode;\n /**\n * Variante visual.\n * - `elevated` (default): sombra sutil.\n * - `outlined`: borde sin sombra.\n * - `plain`: sin borde ni sombra, útil para layouts con nesting.\n */\n variant?: CardVariant;\n /** Densidad del padding interno. */\n padding?: CardPadding;\n /** Si `true`, muestra feedback de hover/active (cursor, sombra). */\n clickable?: boolean;\n /**\n * Preset registrado en `theme.styles.Card`. `\"default\"` usa el estilo\n * built-in sin preset extra.\n */\n preset?: string;\n /**\n * MUI `raised` legacy — mantenido por backward-compat. Equivale a\n * `variant=\"elevated\"` con sombra mayor. Ignora `variant` si se usa.\n */\n raised?: boolean;\n /** sx del root. Se compone sobre el base + preset. */\n sx?: SxProps<Theme>;\n onClick?: () => void;\n onBlur?: () => void;\n className?: string;\n 'data-testid'?: string;\n}\n\nexport function Card({\n children,\n variant,\n padding = 'normal',\n clickable = false,\n preset,\n raised,\n sx,\n onClick,\n onBlur,\n className,\n 'data-testid': dataTestId,\n}: CardProps) {\n const theme = useTheme();\n\n const resolvedVariant: CardVariant = raised\n ? 'elevated'\n : (variant ?? 'elevated');\n\n const presetSx = resolvePreset('Card', preset, theme);\n\n const rootSx: SxProps<Theme> = [\n buildCardSx({\n variant: resolvedVariant,\n padding,\n clickable: clickable || Boolean(onClick),\n }),\n ...(presetSx ? [presetSx] : []),\n ...(Array.isArray(sx) ? sx : sx ? [sx] : []),\n ];\n\n return (\n <MuiCard\n sx={rootSx}\n className={className}\n data-testid={dataTestId}\n onClick={onClick}\n onBlur={onBlur}\n elevation={0}\n >\n {children}\n </MuiCard>\n );\n}\n\nexport default Card;\n"],"names":["MuiCard"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAWA,MAAM,aAA0C;AAAA,EAC9C,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AACT;AAOO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACF,GAAoC;AAClC,SAAO,CAAC,UAAW;AAAA,IACjB,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,WAAW,YAAY,aAAa,MAAM,QAAQ,CAAC,IAAI;AAAA,IACvD,QACE,YAAY,aACR,aAAa,MAAM,QAAQ,OAAO,KAClC;AAAA,IACN,YAAY,MAAM,YAAY;AAAA,MAC5B,CAAC,cAAc,aAAa,cAAc;AAAA,MAC1C,EAAE,UAAU,MAAM,YAAY,SAAS,QAAA;AAAA,IAAQ;AAAA,IAEjD,QAAQ,YAAY,YAAY;AAAA,KAC5B,aAAa;AAAA,IACf,WAAW;AAAA,MACT,WACE,YAAY,aACR,MAAM,QAAQ,CAAC,IACf,YAAY,aACV,MAAM,QAAQ,CAAC,IACf;AAAA,MACR,WAAW,YAAY,UAAU,qBAAqB;AAAA,IAAA;AAAA,IAExD,YAAY;AAAA,MACV,WAAW;AAAA,IAAA;AAAA,EACb,IAzBe;AAAA,IA2BjB,GAAG,WAAW,OAAO;AAAA,EAAA;AAEzB;AClBO,SAAS,KAAK;AAAA,EACnB;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AACjB,GAAc;AACZ,QAAM,QAAQ,SAAA;AAEd,QAAM,kBAA+B,SACjC,aACC,4BAAW;AAEhB,QAAM,WAAW,cAAc,QAAQ,QAAQ,KAAK;AAEpD,QAAM,SAAyB;AAAA,IAC7B,YAAY;AAAA,MACV,SAAS;AAAA,MACT;AAAA,MACA,WAAW,aAAa,QAAQ,OAAO;AAAA,IAAA,CACxC;AAAA,IACD,GAAI,WAAW,CAAC,QAAQ,IAAI,CAAA;AAAA,IAC5B,GAAI,MAAM,QAAQ,EAAE,IAAI,KAAK,KAAK,CAAC,EAAE,IAAI,CAAA;AAAA,EAAC;AAG5C,SACE;AAAA,IAACA;AAAAA,IAAA;AAAA,MACC,IAAI;AAAA,MACJ;AAAA,MACA,eAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MAEV;AAAA,IAAA;AAAA,EAAA;AAGP;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"Card-DfdU610V.cjs","sources":["../src/components/Card/Card.sx.ts","../src/components/Card/Card.tsx"],"sourcesContent":["import type { SxProps, Theme } from '@mui/material/styles';\n\nexport type CardVariant = 'elevated' | 'outlined' | 'plain';\nexport type CardPadding = 'none' | 'dense' | 'normal' | 'loose';\n\nexport interface BuildCardSxArgs {\n variant: CardVariant;\n padding: CardPadding;\n clickable: boolean;\n}\n\nconst paddingMap: Record<CardPadding, number> = {\n none: 0,\n dense: 1.5,\n normal: 2.5,\n loose: 4,\n};\n\n/**\n * Estilo base del Card. La prop `sx` del consumer se compone encima junto al\n * preset resuelto (en Card.tsx). Este builder solo se ocupa de variant +\n * padding para que siga siendo predecible.\n */\nexport function buildCardSx({\n variant,\n padding,\n clickable,\n}: BuildCardSxArgs): SxProps<Theme> {\n return (theme) => ({\n borderRadius: 2,\n backgroundColor: 'background.paper',\n boxShadow: variant === 'elevated' ? theme.shadows[1] : 'none',\n border:\n variant === 'outlined'\n ? `1px solid ${theme.palette.divider}`\n : 'none',\n transition: theme.transitions.create(\n ['box-shadow', 'transform', 'border-color'],\n { duration: theme.transitions.duration.shorter },\n ),\n cursor: clickable ? 'pointer' : 'default',\n ...(clickable && {\n '&:hover': {\n boxShadow:\n variant === 'elevated'\n ? theme.shadows[3]\n : variant === 'outlined'\n ? theme.shadows[1]\n : 'none',\n transform: variant !== 'plain' ? 'translateY(-1px)' : 'none',\n },\n '&:active': {\n transform: 'translateY(0)',\n },\n }),\n p: paddingMap[padding],\n });\n}\n","import { type ReactNode } from 'react';\nimport { Card as MuiCard, useTheme } from '@mui/material';\nimport type { SxProps, Theme } from '@mui/material/styles';\n\nimport { resolvePreset } from '../_shared/resolvePreset';\nimport { buildCardSx, type CardVariant, type CardPadding } from './Card.sx';\n\nexport interface CardProps {\n /** Contenido del Card. */\n children?: ReactNode;\n /**\n * Variante visual.\n * - `elevated` (default): sombra sutil.\n * - `outlined`: borde sin sombra.\n * - `plain`: sin borde ni sombra, útil para layouts con nesting.\n */\n variant?: CardVariant;\n /** Densidad del padding interno. */\n padding?: CardPadding;\n /** Si `true`, muestra feedback de hover/active (cursor, sombra). */\n clickable?: boolean;\n /**\n * Preset registrado en `theme.styles.Card`. `\"default\"` usa el estilo\n * built-in sin preset extra.\n */\n preset?: string;\n /**\n * MUI `raised` legacy — mantenido por backward-compat. Equivale a\n * `variant=\"elevated\"` con sombra mayor. Ignora `variant` si se usa.\n */\n raised?: boolean;\n /** sx del root. Se compone sobre el base + preset. */\n sx?: SxProps<Theme>;\n onClick?: () => void;\n onBlur?: () => void;\n className?: string;\n 'data-testid'?: string;\n}\n\nexport function Card({\n children,\n variant,\n padding = 'normal',\n clickable = false,\n preset,\n raised,\n sx,\n onClick,\n onBlur,\n className,\n 'data-testid': dataTestId,\n}: CardProps) {\n const theme = useTheme();\n\n const resolvedVariant: CardVariant = raised\n ? 'elevated'\n : (variant ?? 'elevated');\n\n const presetSx = resolvePreset('Card', preset, theme);\n\n const rootSx: SxProps<Theme> = [\n buildCardSx({\n variant: resolvedVariant,\n padding,\n clickable: clickable || Boolean(onClick),\n }),\n ...(presetSx ? [presetSx] : []),\n ...(Array.isArray(sx) ? sx : sx ? [sx] : []),\n ];\n\n return (\n <MuiCard\n sx={rootSx}\n className={className}\n data-testid={dataTestId}\n onClick={onClick}\n onBlur={onBlur}\n elevation={0}\n >\n {children}\n </MuiCard>\n );\n}\n\nexport default Card;\n"],"names":["useTheme","resolvePreset","jsx","MuiCard"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAWA,MAAM,aAA0C;AAAA,EAC9C,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AACT;AAOO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACF,GAAoC;AAClC,SAAO,CAAC,UAAW;AAAA,IACjB,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,WAAW,YAAY,aAAa,MAAM,QAAQ,CAAC,IAAI;AAAA,IACvD,QACE,YAAY,aACR,aAAa,MAAM,QAAQ,OAAO,KAClC;AAAA,IACN,YAAY,MAAM,YAAY;AAAA,MAC5B,CAAC,cAAc,aAAa,cAAc;AAAA,MAC1C,EAAE,UAAU,MAAM,YAAY,SAAS,QAAA;AAAA,IAAQ;AAAA,IAEjD,QAAQ,YAAY,YAAY;AAAA,KAC5B,aAAa;AAAA,IACf,WAAW;AAAA,MACT,WACE,YAAY,aACR,MAAM,QAAQ,CAAC,IACf,YAAY,aACV,MAAM,QAAQ,CAAC,IACf;AAAA,MACR,WAAW,YAAY,UAAU,qBAAqB;AAAA,IAAA;AAAA,IAExD,YAAY;AAAA,MACV,WAAW;AAAA,IAAA;AAAA,EACb,IAzBe;AAAA,IA2BjB,GAAG,WAAW,OAAO;AAAA,EAAA;AAEzB;AClBO,SAAS,KAAK;AAAA,EACnB;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AACjB,GAAc;AACZ,QAAM,QAAQA,SAAAA,SAAA;AAEd,QAAM,kBAA+B,SACjC,aACC,4BAAW;AAEhB,QAAM,WAAWC,cAAAA,cAAc,QAAQ,QAAQ,KAAK;AAEpD,QAAM,SAAyB;AAAA,IAC7B,YAAY;AAAA,MACV,SAAS;AAAA,MACT;AAAA,MACA,WAAW,aAAa,QAAQ,OAAO;AAAA,IAAA,CACxC;AAAA,IACD,GAAI,WAAW,CAAC,QAAQ,IAAI,CAAA;AAAA,IAC5B,GAAI,MAAM,QAAQ,EAAE,IAAI,KAAK,KAAK,CAAC,EAAE,IAAI,CAAA;AAAA,EAAC;AAG5C,SACEC,2BAAAA;AAAAA,IAACC,SAAAA;AAAAA,IAAA;AAAA,MACC,IAAI;AAAA,MACJ;AAAA,MACA,eAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MAEV;AAAA,IAAA;AAAA,EAAA;AAGP;;;"}