flysoft-react-ui 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/App.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.tsx"],"names":[],"mappings":"AAUA,OAAO,aAAa,CAAC;AAoCrB,iBAAS,GAAG,4CAkBX;AAED,eAAe,GAAG,CAAC"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.tsx"],"names":[],"mappings":"AAUA,OAAO,aAAa,CAAC;AAwCrB,iBAAS,GAAG,4CAoBX;AAED,eAAe,GAAG,CAAC"}
package/dist/App.js CHANGED
@@ -7,9 +7,12 @@ import { DocsMenu } from "./docs/DocsMenu";
7
7
  import DocsRouter from "./docs/DocsRouter";
8
8
  import { AuthDocs } from "./docs/AuthDocs.tsx/AuthDocs";
9
9
  function HomeContent() {
10
- return (_jsxs(_Fragment, { children: [_jsx(Card, { title: "Empleados", className: "bg-gray-50", children: _jsx(Card, { className: "w-1/2", title: "FALCONE WALTER ALDO", subtitle: "3558-03 - Titular", footer: _jsx(Button, { children: "Ver" }), headerActions: _jsxs(_Fragment, { children: [_jsx(Badge, { variant: "secondary", children: "OSOCNA" }), _jsx(Badge, { variant: "secondary", children: "B\u00C1SICO" })] }), children: _jsxs(Collection, { direction: "row", gap: "1rem", wrap: true, children: [_jsx(DataField, { label: "Cuil", value: "20179902711" }), _jsx(DataField, { label: "Doc", value: "17990271" }), _jsx(DataField, { label: "F Nac", value: "23/06/1966" }), _jsx(DataField, { label: "F Alta", value: "01/03/2012" })] }) }) }), _jsx("div", { children: _jsx(AuthDocs, {}) })] }));
10
+ return (_jsxs(_Fragment, { children: [_jsx(Card, { title: "Empleados", className: "bg-gray-50", children: _jsx(Card, { className: "w-1/2", title: "FALCONE WALTER ALDO", subtitle: "3558-03 - Titular", footer: _jsx(Button, { children: "Ver" }), headerActions: () => [
11
+ _jsx(Badge, { variant: "secondary", children: "OSOCNA" }),
12
+ _jsx(Badge, { variant: "secondary", children: "B\u00C1SICO" }),
13
+ ], children: _jsxs(Collection, { direction: "row", gap: "1rem", wrap: true, children: [_jsx(DataField, { label: "Cuil", value: "20179902711" }), _jsx(DataField, { label: "Doc", value: "17990271" }), _jsx(DataField, { label: "F Nac", value: "23/06/1966" }), _jsx(DataField, { label: "F Alta", value: "01/03/2012" })] }) }) }), _jsx("div", { children: _jsx(AuthDocs, {}) })] }));
11
14
  }
12
15
  function App() {
13
- return (_jsx(ThemeProvider, { initialTheme: "light", forceInitialTheme: false, children: _jsx(AppLayout, { navBarDrawer: _jsx("h2", { children: _jsx(Link, { to: "/", children: "Flysoft React UI" }) }), leftDrawer: _jsx(DocsMenu, {}), children: _jsxs(Routes, { children: [_jsx(Route, { path: "/", element: _jsx(HomeContent, {}) }), _jsx(Route, { path: "/docs/*", element: _jsx(DocsRouter, {}) })] }) }) }));
16
+ return (_jsx(ThemeProvider, { initialTheme: "light", forceInitialTheme: false, children: _jsx(AppLayout, { navBarDrawer: _jsx("div", { children: _jsx("h2", { children: _jsx(Link, { to: "/", children: "Flysoft React UI" }) }) }), leftDrawer: _jsx(DocsMenu, {}), children: _jsxs(Routes, { children: [_jsx(Route, { path: "/", element: _jsx(HomeContent, {}) }), _jsx(Route, { path: "/docs/*", element: _jsx(DocsRouter, {}) })] }) }) }));
14
17
  }
15
18
  export default App;
@@ -4,7 +4,10 @@ export interface CardProps {
4
4
  subtitle?: string | React.ReactNode;
5
5
  children: React.ReactNode;
6
6
  className?: string;
7
- headerActions?: React.ReactNode;
7
+ /**
8
+ * Acciones para el header de la tarjeta. Retorna un array de ReactNode que se mostrarán en un DropdownMenu.
9
+ */
10
+ headerActions?: () => Array<React.ReactNode>;
8
11
  footer?: React.ReactNode;
9
12
  variant?: "default" | "elevated" | "outlined";
10
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../../src/components/layout/Card.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IACpC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAChC,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,OAAO,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;CAC/C;AAED,eAAO,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,SAAS,CAsEpC,CAAC"}
1
+ {"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../../src/components/layout/Card.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IACpC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC7C,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,OAAO,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;CAC/C;AAED,eAAO,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,SAAS,CA6HpC,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from "react";
3
+ import { DropdownMenu } from "../utils/DropdownMenu";
3
4
  export const Card = ({ title, subtitle, children, className = "", headerActions, footer, variant = "default", }) => {
4
5
  // Separar clases de background del className
5
6
  const classArray = className.trim().split(/\s+/).filter(Boolean);
@@ -25,5 +26,33 @@ export const Card = ({ title, subtitle, children, className = "", headerActions,
25
26
  outlined: `border-[var(--color-gray-300)]`,
26
27
  };
27
28
  const classes = `${baseClasses} ${variantClasses[variant]} ${otherClasses.join(" ")}`;
28
- return (_jsxs("div", { className: classes, children: [(title || subtitle || headerActions) && (_jsx("div", { className: "px-6 pt-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [title && (_jsx("h3", { className: "text-lg font-semibold text-[var(--color-text-primary)]", children: title })), subtitle && (_jsx("p", { className: "text-sm text-[var(--color-text-secondary)] mt-1", children: subtitle }))] }), headerActions && (_jsx("div", { className: "flex items-center space-x-2", children: headerActions }))] }) })), _jsx("div", { className: "px-6 py-4", children: children }), footer && _jsx("div", { className: "px-6 pb-4", children: footer })] }));
29
+ // Convertir array de ReactNode a array de ActionItem para DropdownMenu
30
+ const convertActionsToOptions = (actions) => {
31
+ return actions.map((action, index) => ({
32
+ id: index,
33
+ content: (_jsx("div", { onClick: (e) => {
34
+ // Detener la propagación para que el onClick del DropdownMenu no interfiera
35
+ e.stopPropagation();
36
+ }, children: action })),
37
+ }));
38
+ };
39
+ const headerActionsArray = headerActions?.();
40
+ const hasHeaderActions = headerActionsArray && headerActionsArray.length > 0;
41
+ const [isHovered, setIsHovered] = React.useState(false);
42
+ const [isLargeScreen, setIsLargeScreen] = React.useState(false);
43
+ React.useEffect(() => {
44
+ const checkScreenSize = () => {
45
+ setIsLargeScreen(window.innerWidth >= 1024);
46
+ };
47
+ checkScreenSize();
48
+ window.addEventListener("resize", checkScreenSize);
49
+ return () => {
50
+ window.removeEventListener("resize", checkScreenSize);
51
+ };
52
+ }, []);
53
+ return (_jsxs("div", { className: `${classes} relative`, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: [(title || subtitle || hasHeaderActions) && (_jsx("div", { className: "px-6 pt-4", children: _jsx("div", { className: "flex items-center justify-between", children: _jsxs("div", { children: [title && (_jsx("h3", { className: "text-lg font-semibold text-[var(--color-text-primary)]", children: title })), subtitle && (_jsx("p", { className: "text-sm text-[var(--color-text-secondary)] mt-1", children: subtitle }))] }) }) })), hasHeaderActions && (_jsx("div", { className: "absolute top-2 right-2 transition-opacity", style: {
54
+ opacity: isLargeScreen ? (isHovered ? 1 : 0) : 1,
55
+ }, children: _jsx(DropdownMenu, { options: convertActionsToOptions(headerActionsArray), onOptionSelected: () => {
56
+ // Las acciones ya manejan sus propios eventos
57
+ }, renderOption: (item) => item.content, replaceOnSingleOption: true }) })), _jsx("div", { className: "px-6 py-4", children: children }), footer && _jsx("div", { className: "px-6 pb-4", children: footer })] }));
29
58
  };
@@ -0,0 +1,27 @@
1
+ import React from "react";
2
+ export interface DataTableColumn<T> {
3
+ align?: "left" | "right" | "center";
4
+ width?: string;
5
+ header?: string | React.ReactNode;
6
+ footer?: string | React.ReactNode;
7
+ value?: string | number | ((row: T) => string | React.ReactNode);
8
+ tooltip?: (row: T) => string | React.ReactNode;
9
+ type?: "text" | "numeric" | "currency" | "date";
10
+ /**
11
+ * Acciones para cada fila. Retorna un array de ReactNode que se mostrarán en un DropdownMenu.
12
+ */
13
+ actions?: (row: T) => Array<React.ReactNode>;
14
+ /**
15
+ * Acciones para el header de la columna. Retorna un array de ReactNode que se mostrarán en un DropdownMenu.
16
+ */
17
+ headerActions?: () => Array<React.ReactNode>;
18
+ }
19
+ export interface DataTableProps<T> {
20
+ columns: DataTableColumn<T>[];
21
+ rows: T[];
22
+ className?: string;
23
+ maxRows?: number;
24
+ locale?: string;
25
+ }
26
+ export declare const DataTable: <T>({ columns, rows, className, maxRows, locale, }: DataTableProps<T>) => import("react/jsx-runtime").JSX.Element;
27
+ //# sourceMappingURL=DataTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DataTable.d.ts","sourceRoot":"","sources":["../../../src/components/layout/DataTable.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;IACjE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IAC/C,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;IAChD;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC7C;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9B,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,SAAS,GAAI,CAAC,EAAG,gDAM3B,cAAc,CAAC,CAAC,CAAC,4CAwRnB,CAAC"}
@@ -0,0 +1,160 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { DropdownMenu } from "../utils/DropdownMenu";
4
+ export const DataTable = ({ columns, rows, className = "", maxRows, locale = "es-AR", }) => {
5
+ // Calcular si necesitamos scroll
6
+ const needsScroll = maxRows !== undefined && rows.length > maxRows;
7
+ // Altura aproximada de una fila (px-4 py-3 = ~48px por fila)
8
+ const rowHeight = 48;
9
+ const maxHeight = maxRows ? `${maxRows * rowHeight}px` : undefined;
10
+ // Verificar si alguna columna tiene footer
11
+ const hasFooter = columns.some((column) => column.footer !== undefined);
12
+ const getCellValue = (column, row) => {
13
+ if (!column.value)
14
+ return null;
15
+ if (typeof column.value === "function") {
16
+ return column.value(row);
17
+ }
18
+ // Si es string o number, puede ser un nombre de propiedad o un valor directo
19
+ if (typeof column.value === "string" || typeof column.value === "number") {
20
+ // Intentar obtener la propiedad del objeto si existe
21
+ if (typeof column.value === "string" &&
22
+ typeof row === "object" &&
23
+ row !== null) {
24
+ const value = row[column.value];
25
+ if (value !== undefined) {
26
+ return value;
27
+ }
28
+ }
29
+ // Si no es una propiedad, retornar el valor directo
30
+ return column.value;
31
+ }
32
+ return column.value;
33
+ };
34
+ const formatValue = (value, type) => {
35
+ if (React.isValidElement(value)) {
36
+ return value;
37
+ }
38
+ // Convertir string a número si es necesario para currency o numeric
39
+ let numericValue = null;
40
+ if (typeof value === "number") {
41
+ numericValue = value;
42
+ }
43
+ else if (typeof value === "string" &&
44
+ (type === "currency" || type === "numeric")) {
45
+ const parsed = parseFloat(value);
46
+ if (!isNaN(parsed)) {
47
+ numericValue = parsed;
48
+ }
49
+ }
50
+ if (numericValue !== null) {
51
+ if (type === "currency") {
52
+ // Formatear usando el locale proporcionado sin símbolo de moneda
53
+ const parts = new Intl.NumberFormat(locale, {
54
+ style: "currency",
55
+ currency: "EUR",
56
+ minimumFractionDigits: 2,
57
+ maximumFractionDigits: 2,
58
+ }).formatToParts(numericValue);
59
+ // Construir el string sin el símbolo de moneda
60
+ return parts
61
+ .filter((part) => part.type !== "currency")
62
+ .map((part) => part.value)
63
+ .join("");
64
+ }
65
+ if (type === "numeric") {
66
+ // Formatear usando el locale proporcionado
67
+ const hasDecimals = numericValue % 1 !== 0;
68
+ return new Intl.NumberFormat(locale, {
69
+ minimumFractionDigits: hasDecimals ? 2 : 0,
70
+ maximumFractionDigits: hasDecimals ? 2 : 0,
71
+ }).format(numericValue);
72
+ }
73
+ }
74
+ if (typeof value === "string" && type === "date") {
75
+ try {
76
+ const date = new Date(value);
77
+ return date.toLocaleDateString(locale);
78
+ }
79
+ catch {
80
+ return value;
81
+ }
82
+ }
83
+ return value;
84
+ };
85
+ const getAlignmentClass = (align, type) => {
86
+ // Las columnas de tipo 'date' siempre se alinean a la izquierda
87
+ // Las columnas de tipo 'currency' y 'numeric' siempre se alinean a la derecha
88
+ let effectiveAlign = align;
89
+ if (type === "date") {
90
+ effectiveAlign = "left";
91
+ }
92
+ else if (type === "currency" || type === "numeric") {
93
+ effectiveAlign = "right";
94
+ }
95
+ switch (effectiveAlign) {
96
+ case "right":
97
+ return "text-right";
98
+ case "center":
99
+ return "text-center";
100
+ case "left":
101
+ default:
102
+ return "text-left";
103
+ }
104
+ };
105
+ // Convertir array de ReactNode a array de ActionItem para DropdownMenu
106
+ const convertActionsToOptions = (actions) => {
107
+ return actions.map((action, index) => ({
108
+ id: index,
109
+ content: (_jsx("div", { onClick: (e) => {
110
+ // Detener la propagación para que el onClick del DropdownMenu no interfiera
111
+ e.stopPropagation();
112
+ }, children: action })),
113
+ }));
114
+ };
115
+ return (_jsx("div", { className: `overflow-x-auto ${className}`, children: _jsx("div", { className: needsScroll ? "relative overflow-y-auto" : "", style: needsScroll && maxHeight ? { maxHeight: maxHeight } : undefined, children: _jsxs("table", { className: "w-full border-collapse font-[var(--font-default)]", children: [_jsx("thead", { className: needsScroll ? "sticky top-0 z-10" : "", children: _jsx("tr", { className: "border-b border-[var(--color-border-default)]", children: columns.map((column, index) => {
116
+ const headerActions = column.headerActions?.();
117
+ const hasHeaderActions = headerActions && headerActions.length > 0;
118
+ return (_jsx("th", { className: `
119
+ px-4 py-3 text-sm font-semibold text-[var(--color-text-primary)]
120
+ bg-[var(--color-bg-secondary)]
121
+ ${getAlignmentClass(column.align, column.type)}
122
+ ${hasHeaderActions ? "relative" : ""}
123
+ `, style: {
124
+ ...(column.width ? { width: column.width } : {}),
125
+ fontStretch: "75%",
126
+ }, children: hasHeaderActions ? (_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("span", { children: column.header || "" }), _jsx(DropdownMenu, { options: convertActionsToOptions(headerActions), onOptionSelected: () => {
127
+ // Las acciones ya manejan sus propios eventos
128
+ }, renderOption: (item) => item.content, replaceOnSingleOption: true })] })) : (column.header || "") }, index));
129
+ }) }) }), _jsx("tbody", { children: rows.map((row, rowIndex) => (_jsx("tr", { className: "group/row border-b border-[var(--color-border-default)] transition-colors hover:bg-[var(--color-bg-secondary)]", style: {
130
+ fontStretch: "75%",
131
+ }, children: columns.map((column, colIndex) => {
132
+ const cellValue = getCellValue(column, row);
133
+ const formattedValue = formatValue(cellValue, column.type);
134
+ const tooltip = column.tooltip
135
+ ? column.tooltip(row)
136
+ : undefined;
137
+ const rowActions = column.actions?.(row);
138
+ const hasRowActions = rowActions && rowActions.length > 0;
139
+ return (_jsx("td", { className: `
140
+ px-4 py-3 text-sm text-[var(--color-text-primary)]
141
+ ${getAlignmentClass(column.align, column.type)}
142
+ `, style: {
143
+ ...(column.width ? { width: column.width } : {}),
144
+ fontVariationSettings: '"wdth" 75',
145
+ }, title: tooltip
146
+ ? typeof tooltip === "string"
147
+ ? tooltip
148
+ : undefined
149
+ : undefined, children: hasRowActions ? (_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("span", { children: formattedValue }), _jsx("div", { className: "lg:opacity-0 lg:group-hover/row:opacity-100 transition-opacity", children: _jsx(DropdownMenu, { options: convertActionsToOptions(rowActions), onOptionSelected: () => {
150
+ // Las acciones ya manejan sus propios eventos
151
+ }, renderOption: (item) => item.content, replaceOnSingleOption: true }) })] })) : (formattedValue) }, colIndex));
152
+ }) }, rowIndex))) }), hasFooter && (_jsx("tfoot", { className: needsScroll ? "sticky bottom-0 z-10" : "", children: _jsx("tr", { className: "border-t border-[var(--color-border-default)]", children: columns.map((column, index) => (_jsx("td", { className: `
153
+ px-4 py-3 text-sm font-semibold text-[var(--color-text-primary)]
154
+ bg-[var(--color-bg-secondary)]
155
+ ${getAlignmentClass(column.align, column.type)}
156
+ `, style: {
157
+ ...(column.width ? { width: column.width } : {}),
158
+ fontStretch: "75%",
159
+ }, children: column.footer || "" }, index))) }) }))] }) }) }));
160
+ };
@@ -10,4 +10,6 @@ export { TabsGroup } from "./TabsGroup";
10
10
  export type { TabsGroupProps, Tab } from "./TabsGroup";
11
11
  export { TabPanel } from "./TabPanel";
12
12
  export type { TabPanelProps } from "./TabPanel";
13
+ export { DataTable } from "./DataTable";
14
+ export type { DataTableProps, DataTableColumn } from "./DataTable";
13
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/layout/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,YAAY,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/layout/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,YAAY,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
@@ -4,3 +4,4 @@ export { Collection } from "./Collection";
4
4
  export { DataField } from "./DataField";
5
5
  export { TabsGroup } from "./TabsGroup";
6
6
  export { TabPanel } from "./TabPanel";
7
+ export { DataTable } from "./DataTable";
@@ -0,0 +1,25 @@
1
+ import React from "react";
2
+ export interface DropdownMenuProps<T = {
3
+ label: string;
4
+ }> {
5
+ options: T[];
6
+ onOptionSelected: (item: T) => void;
7
+ renderNode?: React.ReactNode;
8
+ /**
9
+ * Obtiene el label que se muestra para cada opción. Por defecto usa la propiedad "label".
10
+ */
11
+ getOptionLabel?: (item: T) => string;
12
+ /**
13
+ * Renderizado personalizado de cada opción. Si se define, se ignora el render por defecto.
14
+ */
15
+ renderOption?: (item: T) => React.ReactNode;
16
+ /**
17
+ * Si es true y hay una sola opción, muestra directamente la opción en lugar del trigger.
18
+ * Por defecto es false.
19
+ */
20
+ replaceOnSingleOption?: boolean;
21
+ }
22
+ export declare const DropdownMenu: <T = {
23
+ label: string;
24
+ }>({ options, onOptionSelected, renderNode, getOptionLabel, renderOption, replaceOnSingleOption, }: DropdownMenuProps<T>) => import("react/jsx-runtime").JSX.Element;
25
+ //# sourceMappingURL=DropdownMenu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DropdownMenu.d.ts","sourceRoot":"","sources":["../../../src/components/utils/DropdownMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAMN,MAAM,OAAO,CAAC;AAGf,MAAM,WAAW,iBAAiB,CAAC,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE;IACtD,OAAO,EAAE,CAAC,EAAE,CAAC;IACb,gBAAgB,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IACrC;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;IAC5C;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,eAAO,MAAM,YAAY,GAAI,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EAAG,iGAOlD,iBAAiB,CAAC,CAAC,CAAC,4CAiNtB,CAAC"}
@@ -0,0 +1,145 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useState, useRef, useEffect, useCallback, useMemo, } from "react";
3
+ import { Button } from "../form-controls/Button";
4
+ export const DropdownMenu = ({ options, onOptionSelected, renderNode, getOptionLabel, renderOption, replaceOnSingleOption = false, }) => {
5
+ const [isOpen, setIsOpen] = useState(false);
6
+ const [menuPosition, setMenuPosition] = useState("bottom");
7
+ const [scrollUpdate, setScrollUpdate] = useState(0);
8
+ const triggerRef = useRef(null);
9
+ const menuRef = useRef(null);
10
+ // Calcular posición del menú
11
+ const calculatePosition = useCallback(() => {
12
+ if (isOpen && triggerRef.current) {
13
+ const triggerRect = triggerRef.current.getBoundingClientRect();
14
+ const viewportHeight = window.innerHeight;
15
+ // Estimar altura del menú (aproximadamente 40px por opción + padding)
16
+ const estimatedMenuHeight = options.length * 40 + 16;
17
+ const menuMargin = 4;
18
+ // Calcular espacio disponible arriba y abajo
19
+ const spaceBelow = viewportHeight - triggerRect.bottom - menuMargin;
20
+ const spaceAbove = triggerRect.top - menuMargin;
21
+ // Si no hay suficiente espacio abajo pero sí arriba, mostrar arriba
22
+ // Usamos un margen de seguridad para asegurar que el menú quepa
23
+ if (spaceBelow < estimatedMenuHeight && spaceAbove > spaceBelow) {
24
+ setMenuPosition("top");
25
+ }
26
+ else {
27
+ setMenuPosition("bottom");
28
+ }
29
+ }
30
+ }, [isOpen, options.length]);
31
+ useEffect(() => {
32
+ calculatePosition();
33
+ }, [calculatePosition]);
34
+ // Recalcular posición al hacer scroll o redimensionar
35
+ useEffect(() => {
36
+ if (isOpen) {
37
+ const handleScroll = () => {
38
+ calculatePosition();
39
+ // Forzar actualización del estilo del menú
40
+ setScrollUpdate((prev) => prev + 1);
41
+ };
42
+ window.addEventListener("scroll", handleScroll, true);
43
+ window.addEventListener("resize", handleScroll);
44
+ return () => {
45
+ window.removeEventListener("scroll", handleScroll, true);
46
+ window.removeEventListener("resize", handleScroll);
47
+ };
48
+ }
49
+ }, [isOpen, calculatePosition]);
50
+ // Cerrar menú al hacer click fuera
51
+ useEffect(() => {
52
+ const handleClickOutside = (event) => {
53
+ if (isOpen &&
54
+ triggerRef.current &&
55
+ menuRef.current &&
56
+ !triggerRef.current.contains(event.target) &&
57
+ !menuRef.current.contains(event.target)) {
58
+ setIsOpen(false);
59
+ }
60
+ };
61
+ if (isOpen) {
62
+ document.addEventListener("mousedown", handleClickOutside);
63
+ }
64
+ return () => {
65
+ document.removeEventListener("mousedown", handleClickOutside);
66
+ };
67
+ }, [isOpen]);
68
+ // Cerrar menú al presionar Escape
69
+ useEffect(() => {
70
+ const handleEscape = (event) => {
71
+ if (event.key === "Escape" && isOpen) {
72
+ setIsOpen(false);
73
+ }
74
+ };
75
+ if (isOpen) {
76
+ document.addEventListener("keydown", handleEscape);
77
+ }
78
+ return () => {
79
+ document.removeEventListener("keydown", handleEscape);
80
+ };
81
+ }, [isOpen]);
82
+ const labelGetter = useCallback((item) => {
83
+ if (getOptionLabel)
84
+ return getOptionLabel(item);
85
+ const anyItem = item;
86
+ return (anyItem.label ?? "").toString();
87
+ }, [getOptionLabel]);
88
+ const handleToggle = () => {
89
+ setIsOpen(!isOpen);
90
+ };
91
+ const handleOptionClick = (item) => {
92
+ onOptionSelected(item);
93
+ setIsOpen(false);
94
+ };
95
+ // Si replaceOnSingleOption es true y hay una sola opción, mostrar directamente la opción
96
+ const shouldReplace = replaceOnSingleOption && options.length === 1;
97
+ const singleOption = shouldReplace ? options[0] : null;
98
+ const menuStyles = useMemo(() => {
99
+ if (!isOpen || !triggerRef.current) {
100
+ return {};
101
+ }
102
+ const triggerRect = triggerRef.current.getBoundingClientRect();
103
+ const menuMargin = 4;
104
+ // Asegurar que el menú no se salga de la pantalla horizontalmente
105
+ let leftPosition = triggerRect.left;
106
+ const menuMinWidth = 160;
107
+ const viewportWidth = window.innerWidth;
108
+ // Si el menú se sale por la derecha, ajustar la posición
109
+ if (leftPosition + menuMinWidth > viewportWidth) {
110
+ leftPosition = viewportWidth - menuMinWidth - 8; // 8px de margen
111
+ }
112
+ // Asegurar que no se salga por la izquierda
113
+ if (leftPosition < 8) {
114
+ leftPosition = 8;
115
+ }
116
+ if (menuPosition === "top") {
117
+ return {
118
+ position: "fixed",
119
+ bottom: window.innerHeight - triggerRect.top + menuMargin,
120
+ left: leftPosition,
121
+ minWidth: Math.max(triggerRect.width, menuMinWidth),
122
+ };
123
+ }
124
+ else {
125
+ return {
126
+ position: "fixed",
127
+ top: triggerRect.bottom + menuMargin,
128
+ left: leftPosition,
129
+ minWidth: Math.max(triggerRect.width, menuMinWidth),
130
+ };
131
+ }
132
+ // scrollUpdate se usa intencionalmente para forzar el recálculo en scroll
133
+ // eslint-disable-next-line react-hooks/exhaustive-deps
134
+ }, [isOpen, menuPosition, scrollUpdate]);
135
+ // Si debe reemplazar con la opción única, mostrar directamente la opción
136
+ if (shouldReplace && singleOption) {
137
+ return (_jsx("div", { onClick: () => handleOptionClick(singleOption), className: "cursor-pointer", children: renderOption ? renderOption(singleOption) : labelGetter(singleOption) }));
138
+ }
139
+ return (_jsxs("div", { className: "relative inline-block", ref: triggerRef, children: [_jsx("div", { onClick: handleToggle, className: "cursor-pointer", children: renderNode ? (renderNode) : (_jsx(Button, { variant: "ghost", icon: "fa-ellipsis-h" })) }), isOpen && (_jsx("div", { ref: menuRef, className: "absolute z-[1000] bg-[var(--color-bg-default)] border border-[var(--color-border-default)] rounded-md shadow-[var(--shadow-lg)] py-1 min-w-[160px] font-[var(--font-default)]", style: menuStyles, children: options.map((option, index) => {
140
+ const key = String(option?.id ??
141
+ labelGetter(option) ??
142
+ index);
143
+ return (_jsx("div", { onClick: () => handleOptionClick(option), className: "px-4 py-2 text-sm text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] cursor-pointer transition-colors flex items-center", children: renderOption ? renderOption(option) : labelGetter(option) }, key));
144
+ }) }))] }));
145
+ };
@@ -16,7 +16,9 @@ export const AuthDocsContent = () => {
16
16
  return (_jsxs("div", { className: "space-y-4", children: [_jsx(InterfacesDocumentation, {}), _jsx(Card, { children: _jsx("div", { className: "flex items-center justify-center py-8", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("i", { className: "fa fa-spinner fa-spin text-[var(--color-primary)] text-xl" }), _jsx("span", { className: "text-[var(--color-text-secondary)]", children: "Iniciando sesi\u00F3n..." })] }) }) })] }));
17
17
  }
18
18
  if (isAuthenticated && user) {
19
- return (_jsxs("div", { className: "space-y-4", children: [_jsx(InterfacesDocumentation, {}), _jsx(Card, { title: "Sesi\u00F3n Activa", subtitle: "Informaci\u00F3n del usuario autenticado", headerActions: _jsx(Badge, { variant: "success", icon: "fa-check-circle", iconPosition: "left", children: "Autenticado" }), children: _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsx(DataField, { label: "ID de Usuario", value: user.id }), _jsx(DataField, { label: "Nombre", value: user.name || "No disponible" })] }), user.aditionalData && (_jsxs("div", { className: "mt-4 pt-4 border-t border-[var(--color-border-default)]", children: [_jsx("h4", { className: "text-sm font-semibold text-[var(--color-text-primary)] mb-3", children: "Datos Adicionales" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [user.aditionalData.role && (_jsx(DataField, { label: "Rol", value: _jsx(Badge, { variant: "primary", icon: "fa-user-shield", children: user.aditionalData.role }) })), user.aditionalData.email && (_jsx(DataField, { label: "Email", value: user.aditionalData.email })), user.aditionalData.createdAt && (_jsx(DataField, { label: "Fecha de Creaci\u00F3n", value: new Date(user.aditionalData.createdAt).toLocaleString() })), user.aditionalData.permissions && (_jsx(DataField, { label: "Permisos", value: _jsx("div", { className: "flex flex-wrap gap-2", children: user.aditionalData.permissions.map((permission, index) => (_jsx(Badge, { variant: "info", size: "sm", icon: "fa-key", children: permission }, index))) }) }))] })] })), user.token && (_jsxs("div", { className: "mt-4 pt-4 border-t border-[var(--color-border-default)]", children: [_jsx("h4", { className: "text-sm font-semibold text-[var(--color-text-primary)] mb-3", children: "Informaci\u00F3n del Token" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsx(DataField, { label: "Tipo de Token", value: _jsx(Badge, { variant: "secondary", icon: "fa-tag", children: user.token.tokenType || "Bearer" }) }), user.token.expires && (_jsx(DataField, { label: "Expira", value: new Date(user.token.expires).toLocaleString() })), user.token.accessToken && (_jsx(DataField, { label: "Access Token", value: _jsxs("code", { className: "text-xs bg-[var(--color-bg-secondary)] px-2 py-1 rounded break-all", children: [user.token.accessToken.substring(0, 30), "..."] }) }))] })] })), _jsx("div", { className: "mt-6 pt-4 border-t border-[var(--color-border-default)]", children: _jsx(Button, { variant: "outline", icon: "fa-sign-out-alt", iconPosition: "left", onClick: handleLogout, className: "w-full md:w-auto", children: "Cerrar Sesi\u00F3n" }) })] }) })] }));
19
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(InterfacesDocumentation, {}), _jsx(Card, { title: "Sesi\u00F3n Activa", subtitle: "Informaci\u00F3n del usuario autenticado", headerActions: () => [
20
+ _jsx(Badge, { variant: "success", icon: "fa-check-circle", iconPosition: "left", children: "Autenticado" }),
21
+ ], children: _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsx(DataField, { label: "ID de Usuario", value: user.id }), _jsx(DataField, { label: "Nombre", value: user.name || "No disponible" })] }), user.aditionalData && (_jsxs("div", { className: "mt-4 pt-4 border-t border-[var(--color-border-default)]", children: [_jsx("h4", { className: "text-sm font-semibold text-[var(--color-text-primary)] mb-3", children: "Datos Adicionales" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [user.aditionalData.role && (_jsx(DataField, { label: "Rol", value: _jsx(Badge, { variant: "primary", icon: "fa-user-shield", children: user.aditionalData.role }) })), user.aditionalData.email && (_jsx(DataField, { label: "Email", value: user.aditionalData.email })), user.aditionalData.createdAt && (_jsx(DataField, { label: "Fecha de Creaci\u00F3n", value: new Date(user.aditionalData.createdAt).toLocaleString() })), user.aditionalData.permissions && (_jsx(DataField, { label: "Permisos", value: _jsx("div", { className: "flex flex-wrap gap-2", children: user.aditionalData.permissions.map((permission, index) => (_jsx(Badge, { variant: "info", size: "sm", icon: "fa-key", children: permission }, index))) }) }))] })] })), user.token && (_jsxs("div", { className: "mt-4 pt-4 border-t border-[var(--color-border-default)]", children: [_jsx("h4", { className: "text-sm font-semibold text-[var(--color-text-primary)] mb-3", children: "Informaci\u00F3n del Token" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [_jsx(DataField, { label: "Tipo de Token", value: _jsx(Badge, { variant: "secondary", icon: "fa-tag", children: user.token.tokenType || "Bearer" }) }), user.token.expires && (_jsx(DataField, { label: "Expira", value: new Date(user.token.expires).toLocaleString() })), user.token.accessToken && (_jsx(DataField, { label: "Access Token", value: _jsxs("code", { className: "text-xs bg-[var(--color-bg-secondary)] px-2 py-1 rounded break-all", children: [user.token.accessToken.substring(0, 30), "..."] }) }))] })] })), _jsx("div", { className: "mt-6 pt-4 border-t border-[var(--color-border-default)]", children: _jsx(Button, { variant: "outline", icon: "fa-sign-out-alt", iconPosition: "left", onClick: handleLogout, className: "w-full md:w-auto", children: "Cerrar Sesi\u00F3n" }) })] }) })] }));
20
22
  }
21
23
  return (_jsxs("div", { className: "space-y-4", children: [_jsx(InterfacesDocumentation, {}), _jsx(Card, { title: "Autenticaci\u00F3n", subtitle: "Inicia sesi\u00F3n para ver el AuthContext en acci\u00F3n", children: _jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: "bg-[var(--color-info-light)] border border-[var(--color-info)] rounded-lg p-4", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("i", { className: "fa fa-info-circle text-[var(--color-info)] mt-1" }), _jsx("div", { className: "flex-1", children: _jsxs("p", { className: "text-sm text-[var(--color-text-primary)]", children: ["Esta es una demostraci\u00F3n del", " ", _jsx("code", { className: "text-xs bg-white/50 px-1 rounded", children: "AuthContext" }), " ", "usando un servicio mock. Haz clic en el bot\u00F3n para simular un inicio de sesi\u00F3n."] }) })] }) }), _jsx("div", { className: "flex justify-center pt-4", children: _jsx(Button, { variant: "primary", size: "lg", icon: "fa-sign-in-alt", iconPosition: "left", onClick: handleLogin, children: "Iniciar Sesi\u00F3n" }) })] }) })] }));
22
24
  };
@@ -1 +1 @@
1
- {"version":3,"file":"CardDocs.d.ts","sourceRoot":"","sources":["../../src/docs/CardDocs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,QAAA,MAAM,QAAQ,EAAE,KAAK,CAAC,EA+IrB,CAAC;AAEF,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"CardDocs.d.ts","sourceRoot":"","sources":["../../src/docs/CardDocs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,QAAA,MAAM,QAAQ,EAAE,KAAK,CAAC,EA2drB,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -2,6 +2,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from "react";
3
3
  import { Card, Button } from "../index";
4
4
  const CardDocs = () => {
5
- return (_jsx("div", { className: "max-w-5xl mx-auto space-y-8", children: _jsx(Card, { title: "Card - Variantes y Ejemplos", children: _jsxs("div", { className: "space-y-10", children: [_jsxs("section", { children: [_jsx("h3", { className: "text-lg font-semibold mb-4", style: { color: "var(--flysoft-text-primary)" }, children: "Variantes" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-6", children: [_jsx(Card, { variant: "default", title: "Default", children: _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: "card por defecto" }) }), _jsx(Card, { variant: "elevated", title: "Elevated", children: _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: "card con sombra elevada" }) }), _jsx(Card, { variant: "outlined", title: "Outlined", children: _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: "card con borde destacado" }) })] })] }), _jsxs("section", { children: [_jsx("h3", { className: "text-lg font-semibold mb-4", style: { color: "var(--flysoft-text-primary)" }, children: "Header y Footer" }), _jsx(Card, { title: "Card con Acciones", subtitle: "Ejemplo de header y footer", headerActions: _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", icon: "fa-edit", children: "Editar" }), _jsx(Button, { size: "sm", variant: "primary", icon: "fa-save", children: "Guardar" })] }), footer: _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", children: "Cancelar" }), _jsx(Button, { size: "sm", variant: "primary", icon: "fa-check", children: "Aceptar" })] }), children: _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: "card con acciones en header y contenido descriptivo" }) })] }), _jsxs("section", { children: [_jsx("h3", { className: "text-lg font-semibold mb-4", style: { color: "var(--flysoft-text-primary)" }, children: "Title y Subtitle como ReactNode" }), _jsx(Card, { title: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("i", { className: "fa fa-user-circle" }), _jsx("span", { children: "Usuario Personalizado" })] }), subtitle: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("i", { className: "fa fa-envelope" }), _jsx("span", { children: "usuario@ejemplo.com" })] }), children: _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: "El t\u00EDtulo y subt\u00EDtulo pueden ser ReactNode, permitiendo incluir iconos, badges, o cualquier componente personalizado." }) })] }), _jsxs("section", { children: [_jsx("h3", { className: "text-lg font-semibold mb-4", style: { color: "var(--flysoft-text-primary)" }, children: "Background Personalizado" }), _jsx("p", { className: "mb-4 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "El componente detecta autom\u00E1ticamente las clases de background (bg-*) desde la prop className y las aplica al background de la card. Las dem\u00E1s clases se aplican normalmente al contenedor." }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6", children: [_jsx(Card, { title: "Card con bg-blue-50", className: "bg-blue-50", children: _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: "Card con background personalizado usando clases de Tailwind" }) }), _jsx(Card, { title: "Card con bg-gradient", className: "bg-gradient-to-br from-purple-100 to-pink-100", children: _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: "Card con gradiente personalizado" }) }), _jsx(Card, { title: "Card con bg y otras clases", className: "bg-green-50 p-8", children: _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: "Puedes combinar clases de background con otras clases de Tailwind" }) })] })] })] }) }) }));
5
+ return (_jsx("div", { className: "max-w-5xl mx-auto space-y-8", children: _jsx(Card, { title: "Card - Variantes y Ejemplos", children: _jsxs("div", { className: "space-y-10", children: [_jsxs("section", { children: [_jsx("h3", { className: "text-lg font-semibold mb-4", style: { color: "var(--flysoft-text-primary)" }, children: "Variantes" }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-6", children: [_jsx(Card, { variant: "default", title: "Default", children: _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: "card por defecto" }) }), _jsx(Card, { variant: "elevated", title: "Elevated", children: _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: "card con sombra elevada" }) }), _jsx(Card, { variant: "outlined", title: "Outlined", children: _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: "card con borde destacado" }) })] })] }), _jsxs("section", { children: [_jsx("h3", { className: "text-lg font-semibold mb-4", style: { color: "var(--flysoft-text-primary)" }, children: "Header y Footer" }), _jsxs("p", { className: "mb-4 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: ["El componente Card soporta acciones en el header usando la propiedad ", _jsx("code", { children: "headerActions" }), ". Las acciones se muestran en un DropdownMenu. En pantallas grandes (lg+), las acciones solo se muestran al hacer hover sobre el Card. En pantallas peque\u00F1as, siempre son visibles."] }), _jsxs("div", { className: "space-y-4", children: [_jsx(Card, { title: "Card con m\u00FAltiples acciones", subtitle: "Ejemplo con DropdownMenu", headerActions: () => [
6
+ _jsx(Button, { size: "sm", variant: "ghost", icon: "fa-edit", onClick: () => console.log("Editar"), children: "Editar" }, "edit"),
7
+ _jsx(Button, { size: "sm", variant: "ghost", icon: "fa-trash", onClick: () => console.log("Eliminar"), children: "Eliminar" }, "delete"),
8
+ _jsx(Button, { size: "sm", variant: "ghost", icon: "fa-share", onClick: () => console.log("Compartir"), children: "Compartir" }, "share"),
9
+ ], footer: _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", children: "Cancelar" }), _jsx(Button, { size: "sm", variant: "primary", icon: "fa-check", children: "Aceptar" })] }), children: _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: "Card con m\u00FAltiples acciones en el header. En pantallas grandes, pasa el mouse sobre el Card para ver las acciones." }) }), _jsx(Card, { title: "Card con una sola acci\u00F3n", subtitle: "Se muestra directamente sin men\u00FA", headerActions: () => [
10
+ _jsx(Button, { size: "sm", variant: "ghost", icon: "fa-search", onClick: () => console.log("Ver detalles") }, "view"),
11
+ ], children: _jsxs("p", { style: { color: "var(--flysoft-text-secondary)" }, children: ["Cuando hay una sola acci\u00F3n, se muestra directamente gracias a", " ", _jsx("code", { children: "replaceOnSingleOption" }), ", sin necesidad de abrir un men\u00FA."] }) })] })] }), _jsxs("section", { children: [_jsx("h3", { className: "text-lg font-semibold mb-4", style: { color: "var(--flysoft-text-primary)" }, children: "Title y Subtitle como ReactNode" }), _jsx(Card, { title: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("i", { className: "fa fa-user-circle" }), _jsx("span", { children: "Usuario Personalizado" })] }), subtitle: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("i", { className: "fa fa-envelope" }), _jsx("span", { children: "usuario@ejemplo.com" })] }), children: _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: "El t\u00EDtulo y subt\u00EDtulo pueden ser ReactNode, permitiendo incluir iconos, badges, o cualquier componente personalizado." }) })] }), _jsxs("section", { children: [_jsx("h3", { className: "text-lg font-semibold mb-4", style: { color: "var(--flysoft-text-primary)" }, children: "Background Personalizado" }), _jsx("p", { className: "mb-4 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "El componente detecta autom\u00E1ticamente las clases de background (bg-*) desde la prop className y las aplica al background de la card. Las dem\u00E1s clases se aplican normalmente al contenedor." }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6", children: [_jsx(Card, { title: "Card con bg-blue-50", className: "bg-blue-50", children: _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: "Card con background personalizado usando clases de Tailwind" }) }), _jsx(Card, { title: "Card con bg-gradient", className: "bg-gradient-to-br from-purple-100 to-pink-100", children: _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: "Card con gradiente personalizado" }) }), _jsx(Card, { title: "Card con bg y otras clases", className: "bg-green-50 p-8", children: _jsx("p", { style: { color: "var(--flysoft-text-secondary)" }, children: "Puedes combinar clases de background con otras clases de Tailwind" }) })] })] }), _jsxs("section", { children: [_jsx("h3", { className: "text-lg font-semibold mb-4", style: { color: "var(--flysoft-text-primary)" }, children: "Caracter\u00EDsticas" }), _jsx("div", { className: "space-y-3", children: _jsx("div", { className: "p-3 bg-[var(--color-bg-default)] border border-[var(--color-border-default)] rounded", children: _jsxs("ul", { className: "list-disc list-inside space-y-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: [_jsxs("li", { children: [_jsx("strong", { children: "Variantes:" }), " Soporta tres variantes: default, elevated (con sombra) y outlined (con borde destacado)"] }), _jsxs("li", { children: [_jsx("strong", { children: "HeaderActions:" }), " Usa la propiedad", " ", _jsx("code", { children: "headerActions" }), " para mostrar un DropdownMenu con acciones en el header. Las acciones se muestran en un men\u00FA desplegable."] }), _jsxs("li", { children: [_jsx("strong", { children: "Comportamiento responsive:" }), " En pantallas grandes (lg+), las acciones del header solo se muestran al hacer hover sobre el Card. En pantallas peque\u00F1as, siempre son visibles."] }), _jsxs("li", { children: [_jsx("strong", { children: "Opci\u00F3n \u00FAnica:" }), " Cuando hay una sola acci\u00F3n, se muestra directamente gracias a", " ", _jsx("code", { children: "replaceOnSingleOption" }), ", sin necesidad de abrir un men\u00FA."] }), _jsxs("li", { children: [_jsx("strong", { children: "Title y Subtitle flexibles:" }), " Pueden ser strings o ReactNode, permitiendo incluir iconos, badges u otros componentes."] }), _jsxs("li", { children: [_jsx("strong", { children: "Background personalizado:" }), " El componente detecta autom\u00E1ticamente las clases de background (bg-*) y las aplica correctamente."] }), _jsxs("li", { children: [_jsx("strong", { children: "Footer opcional:" }), " Puedes agregar contenido en el footer de la card."] })] }) }) })] }), _jsxs("section", { children: [_jsx("h3", { className: "text-lg font-semibold mb-4", style: { color: "var(--flysoft-text-primary)" }, children: "Props" }), _jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full border-collapse", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b border-[var(--color-border-default)]", children: [_jsx("th", { className: "px-4 py-2 text-left text-sm font-semibold", style: { color: "var(--flysoft-text-primary)" }, children: "Prop" }), _jsx("th", { className: "px-4 py-2 text-left text-sm font-semibold", style: { color: "var(--flysoft-text-primary)" }, children: "Tipo" }), _jsx("th", { className: "px-4 py-2 text-left text-sm font-semibold", style: { color: "var(--flysoft-text-primary)" }, children: "Requerido" }), _jsx("th", { className: "px-4 py-2 text-left text-sm font-semibold", style: { color: "var(--flysoft-text-primary)" }, children: "Descripci\u00F3n" })] }) }), _jsxs("tbody", { children: [_jsxs("tr", { className: "border-b border-[var(--color-border-default)]", children: [_jsx("td", { className: "px-4 py-2 text-sm font-mono", style: { color: "var(--flysoft-text-primary)" }, children: "children" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "ReactNode" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "S\u00ED" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "Contenido principal de la card." })] }), _jsxs("tr", { className: "border-b border-[var(--color-border-default)]", children: [_jsx("td", { className: "px-4 py-2 text-sm font-mono", style: { color: "var(--flysoft-text-primary)" }, children: "title" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "string | ReactNode" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "No" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "T\u00EDtulo de la card. Puede ser un string o un ReactNode." })] }), _jsxs("tr", { className: "border-b border-[var(--color-border-default)]", children: [_jsx("td", { className: "px-4 py-2 text-sm font-mono", style: { color: "var(--flysoft-text-primary)" }, children: "subtitle" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "string | ReactNode" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "No" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "Subt\u00EDtulo de la card. Se muestra debajo del t\u00EDtulo." })] }), _jsxs("tr", { className: "border-b border-[var(--color-border-default)]", children: [_jsx("td", { className: "px-4 py-2 text-sm font-mono", style: { color: "var(--flysoft-text-primary)" }, children: "headerActions" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "() => Array<ReactNode>" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "No" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "Funci\u00F3n que retorna un array de ReactNode que se mostrar\u00E1n en un DropdownMenu en el header. En pantallas grandes (lg+), solo se muestran al hacer hover. Las acciones deben manejar sus propios eventos onClick." })] }), _jsxs("tr", { className: "border-b border-[var(--color-border-default)]", children: [_jsx("td", { className: "px-4 py-2 text-sm font-mono", style: { color: "var(--flysoft-text-primary)" }, children: "footer" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "ReactNode" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "No" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "Contenido del footer de la card." })] }), _jsxs("tr", { className: "border-b border-[var(--color-border-default)]", children: [_jsx("td", { className: "px-4 py-2 text-sm font-mono", style: { color: "var(--flysoft-text-primary)" }, children: "variant" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "\"default\" | \"elevated\" | \"outlined\"" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "No" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "Variante visual de la card. Por defecto es \"default\"." })] }), _jsxs("tr", { className: "border-b border-[var(--color-border-default)]", children: [_jsx("td", { className: "px-4 py-2 text-sm font-mono", style: { color: "var(--flysoft-text-primary)" }, children: "className" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "string" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "No" }), _jsx("td", { className: "px-4 py-2 text-sm", style: { color: "var(--flysoft-text-secondary)" }, children: "Clases CSS adicionales. Las clases de background (bg-*) se aplican autom\u00E1ticamente al background de la card." })] })] })] }) })] })] }) }) }));
6
12
  };
7
13
  export default CardDocs;
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ declare const DataTableDocs: React.FC;
3
+ export default DataTableDocs;
4
+ //# sourceMappingURL=DataTableDocs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DataTableDocs.d.ts","sourceRoot":"","sources":["../../src/docs/DataTableDocs.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAa1B,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAguB1B,CAAC;AAEF,eAAe,aAAa,CAAC"}