flysoft-react-ui 0.3.3 → 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 +1 -1
- package/dist/App.js +5 -2
- package/dist/components/form-controls/Pagination.d.ts +16 -0
- package/dist/components/form-controls/Pagination.d.ts.map +1 -0
- package/dist/components/form-controls/Pagination.js +60 -0
- package/dist/components/form-controls/index.d.ts +2 -0
- package/dist/components/form-controls/index.d.ts.map +1 -1
- package/dist/components/form-controls/index.js +1 -0
- package/dist/components/layout/Card.d.ts +4 -1
- package/dist/components/layout/Card.d.ts.map +1 -1
- package/dist/components/layout/Card.js +30 -1
- package/dist/components/layout/DataTable.d.ts +27 -0
- package/dist/components/layout/DataTable.d.ts.map +1 -0
- package/dist/components/layout/DataTable.js +160 -0
- package/dist/components/layout/TabPanel.d.ts +7 -0
- package/dist/components/layout/TabPanel.d.ts.map +1 -0
- package/dist/components/layout/TabPanel.js +11 -0
- package/dist/components/layout/TabsGroup.d.ts +20 -0
- package/dist/components/layout/TabsGroup.d.ts.map +1 -0
- package/dist/components/layout/TabsGroup.js +52 -0
- package/dist/components/layout/index.d.ts +6 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/layout/index.js +3 -0
- package/dist/components/utils/Dialog.d.ts +11 -0
- package/dist/components/utils/Dialog.d.ts.map +1 -0
- package/dist/components/utils/Dialog.js +39 -0
- package/dist/components/utils/DropdownMenu.d.ts +25 -0
- package/dist/components/utils/DropdownMenu.d.ts.map +1 -0
- package/dist/components/utils/DropdownMenu.js +145 -0
- package/dist/components/utils/Loader.d.ts +11 -0
- package/dist/components/utils/Loader.d.ts.map +1 -0
- package/dist/components/utils/Loader.js +44 -0
- package/dist/components/utils/index.d.ts +4 -0
- package/dist/components/utils/index.d.ts.map +1 -1
- package/dist/components/utils/index.js +2 -0
- package/dist/docs/AuthDocs.tsx/AuthDocsContent.js +3 -1
- package/dist/docs/CardDocs.d.ts.map +1 -1
- package/dist/docs/CardDocs.js +7 -1
- package/dist/docs/DataTableDocs.d.ts +4 -0
- package/dist/docs/DataTableDocs.d.ts.map +1 -0
- package/dist/docs/DataTableDocs.js +240 -0
- package/dist/docs/DialogDocs.d.ts +4 -0
- package/dist/docs/DialogDocs.d.ts.map +1 -0
- package/dist/docs/DialogDocs.js +12 -0
- package/dist/docs/DocsMenu.d.ts.map +1 -1
- package/dist/docs/DocsMenu.js +1 -1
- package/dist/docs/DocsRouter.d.ts.map +1 -1
- package/dist/docs/DocsRouter.js +7 -1
- package/dist/docs/DropdownMenuDocs.d.ts +4 -0
- package/dist/docs/DropdownMenuDocs.d.ts.map +1 -0
- package/dist/docs/DropdownMenuDocs.js +66 -0
- package/dist/docs/LoaderDocs.d.ts +4 -0
- package/dist/docs/LoaderDocs.d.ts.map +1 -0
- package/dist/docs/LoaderDocs.js +33 -0
- package/dist/docs/PaginationDocs.d.ts +4 -0
- package/dist/docs/PaginationDocs.d.ts.map +1 -0
- package/dist/docs/PaginationDocs.js +38 -0
- package/dist/docs/TabsGroupDocs.d.ts +4 -0
- package/dist/docs/TabsGroupDocs.d.ts.map +1 -0
- package/dist/docs/TabsGroupDocs.js +27 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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;
|
|
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:
|
|
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;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface PaginationInterface<T> {
|
|
3
|
+
list: Array<T>;
|
|
4
|
+
limit: number;
|
|
5
|
+
page: number;
|
|
6
|
+
pages: number;
|
|
7
|
+
total: number;
|
|
8
|
+
}
|
|
9
|
+
export interface PaginationProps {
|
|
10
|
+
fieldName?: string;
|
|
11
|
+
page?: number;
|
|
12
|
+
pages?: number;
|
|
13
|
+
total?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare const Pagination: React.FC<PaginationProps>;
|
|
16
|
+
//# sourceMappingURL=Pagination.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pagination.d.ts","sourceRoot":"","sources":["../../../src/components/form-controls/Pagination.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAsHhD,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { useSearchParams } from "react-router-dom";
|
|
4
|
+
import { Button } from "./Button";
|
|
5
|
+
export const Pagination = ({ fieldName = "pagina", page = 1, pages = 1, total = 0, }) => {
|
|
6
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
7
|
+
const navigateToPage = (newPage) => {
|
|
8
|
+
if (newPage < 1 || newPage > pages || newPage === page) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const newSearchParams = new URLSearchParams(searchParams);
|
|
12
|
+
newSearchParams.set(fieldName, newPage.toString());
|
|
13
|
+
setSearchParams(newSearchParams, { replace: true });
|
|
14
|
+
};
|
|
15
|
+
const goToFirstPage = () => navigateToPage(1);
|
|
16
|
+
const goToPreviousPage = () => navigateToPage(page - 1);
|
|
17
|
+
const goToNextPage = () => navigateToPage(page + 1);
|
|
18
|
+
const goToLastPage = () => navigateToPage(pages);
|
|
19
|
+
const isFirstPage = page <= 1;
|
|
20
|
+
const isLastPage = page >= pages;
|
|
21
|
+
const hasPages = pages > 1;
|
|
22
|
+
return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { children: _jsx(Button, { variant: "ghost", size: "sm", icon: "fa-angle-double-left", onClick: goToFirstPage, disabled: isFirstPage || !hasPages, "aria-label": "Primera p\u00E1gina" }) }), _jsx("div", { children: _jsx(Button, { variant: "ghost", size: "sm", icon: "fa-angle-left", onClick: goToPreviousPage, disabled: isFirstPage || !hasPages, "aria-label": "P\u00E1gina anterior" }) }), _jsxs("div", { className: "text-xs", children: [_jsxs("span", { className: "block", children: ["P\u00E1gina ", page, " de ", pages] }), _jsxs("span", { className: "block", children: [total, " elemento", total !== 1 ? "s" : ""] })] }), _jsx("div", { children: _jsx(Button, { variant: "ghost", size: "sm", icon: "fa-angle-right", onClick: goToNextPage, disabled: isLastPage || !hasPages, "aria-label": "P\u00E1gina siguiente" }) }), _jsx("div", { children: _jsx(Button, { variant: "ghost", size: "sm", icon: "fa-angle-double-right", onClick: goToLastPage, disabled: isLastPage || !hasPages, "aria-label": "\u00DAltima p\u00E1gina" }) })] })
|
|
23
|
+
// <div className="flex flex-col items-center gap-0 font-[var(--font-default)]">
|
|
24
|
+
// {/* Botones de navegación */}
|
|
25
|
+
// <div className="flex items-center gap-2">
|
|
26
|
+
//
|
|
27
|
+
// {/* Texto de página */}
|
|
28
|
+
// <span
|
|
29
|
+
// className="text-xs px-3 leading-none"
|
|
30
|
+
// style={{ color: "var(--color-text-primary)" }}
|
|
31
|
+
// >
|
|
32
|
+
// Página {page} de {pages}
|
|
33
|
+
// </span>
|
|
34
|
+
// <Button
|
|
35
|
+
// variant="ghost"
|
|
36
|
+
// size="sm"
|
|
37
|
+
// icon="fa-angle-right"
|
|
38
|
+
// onClick={goToNextPage}
|
|
39
|
+
// disabled={isLastPage || !hasPages}
|
|
40
|
+
// aria-label="Página siguiente"
|
|
41
|
+
// />
|
|
42
|
+
// <Button
|
|
43
|
+
// variant="ghost"
|
|
44
|
+
// size="sm"
|
|
45
|
+
// icon="fa-angle-double-right"
|
|
46
|
+
// onClick={goToLastPage}
|
|
47
|
+
// disabled={isLastPage || !hasPages}
|
|
48
|
+
// aria-label="Última página"
|
|
49
|
+
// />
|
|
50
|
+
// </div>
|
|
51
|
+
// {/* Texto de elementos */}
|
|
52
|
+
// <span
|
|
53
|
+
// className="text-xs leading-none"
|
|
54
|
+
// style={{ color: "var(--color-text-secondary)" }}
|
|
55
|
+
// >
|
|
56
|
+
// {total} elemento{total !== 1 ? "s" : ""}
|
|
57
|
+
// </span>
|
|
58
|
+
// </div>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
@@ -3,9 +3,11 @@ export { Input } from "./Input";
|
|
|
3
3
|
export { AutocompleteInput } from "./AutocompleteInput";
|
|
4
4
|
export { DatePicker } from "./DatePicker";
|
|
5
5
|
export { DateInput } from "./DateInput";
|
|
6
|
+
export { Pagination } from "./Pagination";
|
|
6
7
|
export type { ButtonProps } from "./Button";
|
|
7
8
|
export type { InputProps } from "./Input";
|
|
8
9
|
export type { AutocompleteInputProps, AutocompleteOption, } from "./AutocompleteInput";
|
|
9
10
|
export type { DatePickerProps } from "./DatePicker";
|
|
10
11
|
export type { DateInputProps, DateInputFormat } from "./DateInput";
|
|
12
|
+
export type { PaginationProps, PaginationInterface, } from "./Pagination";
|
|
11
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/form-controls/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,YAAY,EACV,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/form-controls/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,YAAY,EACV,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnE,YAAY,EACV,eAAe,EACf,mBAAmB,GACpB,MAAM,cAAc,CAAC"}
|
|
@@ -4,7 +4,10 @@ export interface CardProps {
|
|
|
4
4
|
subtitle?: string | React.ReactNode;
|
|
5
5
|
children: React.ReactNode;
|
|
6
6
|
className?: string;
|
|
7
|
-
|
|
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;
|
|
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
|
-
|
|
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
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TabPanel.d.ts","sourceRoot":"","sources":["../../../src/components/layout/TabPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAgB5C,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { useTabsContext } from "./TabsGroup";
|
|
4
|
+
export const TabPanel = ({ children, tabId }) => {
|
|
5
|
+
const { activeTab } = useTabsContext();
|
|
6
|
+
const isActive = activeTab.toString() === tabId.toString();
|
|
7
|
+
if (!isActive) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
return (_jsx("div", { role: "tabpanel", className: "w-full font-[var(--font-default)]", children: children }));
|
|
11
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface Tab {
|
|
3
|
+
id: string | number;
|
|
4
|
+
label: string;
|
|
5
|
+
}
|
|
6
|
+
export interface TabsGroupProps {
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
tabs: Tab[];
|
|
9
|
+
paramName?: string;
|
|
10
|
+
headerNode?: React.ReactNode;
|
|
11
|
+
onChangeTab?: (selectedTab: string) => void;
|
|
12
|
+
}
|
|
13
|
+
interface TabsContextType {
|
|
14
|
+
activeTab: string | number;
|
|
15
|
+
setActiveTab: (tab: string | number) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare const useTabsContext: () => TabsContextType;
|
|
18
|
+
export declare const TabsGroup: React.FC<TabsGroupProps>;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=TabsGroup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TabsGroup.d.ts","sourceRoot":"","sources":["../../../src/components/layout/TabsGroup.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkE,MAAM,OAAO,CAAC;AAGvF,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,WAAW,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AAED,UAAU,eAAe;IACvB,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;CAC9C;AAID,eAAO,MAAM,cAAc,uBAM1B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CA8F9C,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useState, useEffect, useMemo, createContext, useContext } from "react";
|
|
3
|
+
import { useSearchParams } from "react-router-dom";
|
|
4
|
+
const TabsContext = createContext(undefined);
|
|
5
|
+
export const useTabsContext = () => {
|
|
6
|
+
const context = useContext(TabsContext);
|
|
7
|
+
if (!context) {
|
|
8
|
+
throw new Error("TabPanel must be used within TabsGroup");
|
|
9
|
+
}
|
|
10
|
+
return context;
|
|
11
|
+
};
|
|
12
|
+
export const TabsGroup = ({ children, tabs, paramName, headerNode, onChangeTab, }) => {
|
|
13
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
14
|
+
const defaultTab = tabs[0]?.id?.toString() || "";
|
|
15
|
+
// Obtener el tab activo desde URL params si paramName está definido, sino desde estado local
|
|
16
|
+
const urlTab = paramName ? searchParams.get(paramName) : null;
|
|
17
|
+
const initialTab = urlTab || defaultTab;
|
|
18
|
+
const [activeTab, setActiveTabState] = useState(initialTab);
|
|
19
|
+
// Sincronizar con URL params cuando cambian
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (paramName && urlTab) {
|
|
22
|
+
setActiveTabState(urlTab);
|
|
23
|
+
}
|
|
24
|
+
}, [paramName, urlTab]);
|
|
25
|
+
const setActiveTab = (tab) => {
|
|
26
|
+
setActiveTabState(tab);
|
|
27
|
+
// Actualizar URL params si paramName está definido
|
|
28
|
+
if (paramName) {
|
|
29
|
+
const newSearchParams = new URLSearchParams(searchParams);
|
|
30
|
+
newSearchParams.set(paramName, tab.toString());
|
|
31
|
+
setSearchParams(newSearchParams, { replace: true });
|
|
32
|
+
}
|
|
33
|
+
// Llamar al callback si está definido
|
|
34
|
+
if (onChangeTab) {
|
|
35
|
+
onChangeTab(tab.toString());
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const contextValue = useMemo(() => ({ activeTab, setActiveTab }), [activeTab]);
|
|
39
|
+
const handleTabClick = (tabId) => {
|
|
40
|
+
setActiveTab(tabId);
|
|
41
|
+
};
|
|
42
|
+
return (_jsx(TabsContext.Provider, { value: contextValue, children: _jsxs("div", { className: "w-full font-[var(--font-default)]", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-[var(--color-border-default)]", children: [_jsx("div", { className: "flex items-center gap-1", children: tabs.map((tab) => {
|
|
43
|
+
const isActive = activeTab.toString() === tab.id.toString();
|
|
44
|
+
return (_jsx("button", { onClick: () => handleTabClick(tab.id), className: `
|
|
45
|
+
px-4 py-2 text-sm font-medium transition-colors cursor-pointer
|
|
46
|
+
border-b-2 -mb-[1px]
|
|
47
|
+
${isActive
|
|
48
|
+
? "text-[var(--color-primary)] border-[var(--color-primary)]"
|
|
49
|
+
: "text-[var(--color-text-secondary)] border-transparent hover:text-[var(--color-text-primary)] hover:border-[var(--color-border-default)]"}
|
|
50
|
+
`, "aria-selected": isActive, role: "tab", children: tab.label }, tab.id));
|
|
51
|
+
}) }), headerNode && (_jsx("div", { className: "flex items-center", children: headerNode }))] }), _jsx("div", { className: "mt-4", children: children })] }) }));
|
|
52
|
+
};
|
|
@@ -6,4 +6,10 @@ export { Collection } from "./Collection";
|
|
|
6
6
|
export type { CollectionProps } from "./Collection";
|
|
7
7
|
export { DataField } from "./DataField";
|
|
8
8
|
export type { DataFieldProps } from "./DataField";
|
|
9
|
+
export { TabsGroup } from "./TabsGroup";
|
|
10
|
+
export type { TabsGroupProps, Tab } from "./TabsGroup";
|
|
11
|
+
export { TabPanel } from "./TabPanel";
|
|
12
|
+
export type { TabPanelProps } from "./TabPanel";
|
|
13
|
+
export { DataTable } from "./DataTable";
|
|
14
|
+
export type { DataTableProps, DataTableColumn } from "./DataTable";
|
|
9
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"}
|
|
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"}
|
|
@@ -2,3 +2,6 @@ export { Card } from "./Card";
|
|
|
2
2
|
export { AppLayout } from "./AppLayout";
|
|
3
3
|
export { Collection } from "./Collection";
|
|
4
4
|
export { DataField } from "./DataField";
|
|
5
|
+
export { TabsGroup } from "./TabsGroup";
|
|
6
|
+
export { TabPanel } from "./TabPanel";
|
|
7
|
+
export { DataTable } from "./DataTable";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface DialogProps {
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
title: React.ReactNode;
|
|
5
|
+
dialogBody: React.ReactNode;
|
|
6
|
+
dialogActions: React.ReactNode;
|
|
7
|
+
onClose?: () => void;
|
|
8
|
+
closeOnOverlayClick?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare const Dialog: React.FC<DialogProps>;
|
|
11
|
+
//# sourceMappingURL=Dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../../src/components/utils/Dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AAEzC,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC;IAC5B,aAAa,EAAE,KAAK,CAAC,SAAS,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAiGxC,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useEffect } from "react";
|
|
3
|
+
export const Dialog = ({ isOpen, title, dialogBody, dialogActions, onClose, closeOnOverlayClick = false, }) => {
|
|
4
|
+
// Prevenir scroll del body cuando el dialog está abierto
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
if (isOpen) {
|
|
7
|
+
document.body.style.overflow = "hidden";
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
document.body.style.overflow = "";
|
|
11
|
+
}
|
|
12
|
+
return () => {
|
|
13
|
+
document.body.style.overflow = "";
|
|
14
|
+
};
|
|
15
|
+
}, [isOpen]);
|
|
16
|
+
// Manejar tecla Escape para cerrar
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const handleEscape = (e) => {
|
|
19
|
+
if (e.key === "Escape" && isOpen && onClose) {
|
|
20
|
+
onClose();
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
if (isOpen) {
|
|
24
|
+
document.addEventListener("keydown", handleEscape);
|
|
25
|
+
}
|
|
26
|
+
return () => {
|
|
27
|
+
document.removeEventListener("keydown", handleEscape);
|
|
28
|
+
};
|
|
29
|
+
}, [isOpen, onClose]);
|
|
30
|
+
if (!isOpen) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const handleOverlayClick = () => {
|
|
34
|
+
if (closeOnOverlayClick && onClose) {
|
|
35
|
+
onClose();
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
return (_jsxs("div", { className: "fixed inset-0 z-[2000] flex items-center justify-center p-4", role: "dialog", "aria-modal": "true", "aria-labelledby": "dialog-title", children: [_jsx("div", { className: "absolute inset-0 bg-black/50 backdrop-blur-sm", onClick: handleOverlayClick }), _jsxs("div", { className: "relative w-auto max-w-lg min-w-[400px] bg-[var(--color-bg-default)] rounded-lg shadow-[var(--shadow-xl)] border border-[var(--color-border-default)] font-[var(--font-default)] max-h-[90vh] flex flex-col", onClick: (e) => e.stopPropagation(), children: [_jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-[var(--color-border-default)]", children: [_jsx("h2", { id: "dialog-title", className: "text-lg font-semibold text-[var(--color-text-primary)]", children: title }), onClose && (_jsx("button", { onClick: onClose, className: "ml-4 p-1 rounded-md text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-hover)] transition-colors cursor-pointer", "aria-label": "Cerrar dialog", children: _jsx("i", { className: "fa fa-times" }) }))] }), _jsx("div", { className: "px-6 py-4 overflow-y-auto flex-1 text-[var(--color-text-primary)] min-w-0", children: dialogBody }), _jsx("div", { className: "px-6 py-4 border-t border-[var(--color-border-default)] flex items-center justify-end gap-2 flex-shrink-0 flex-wrap", children: dialogActions })] })] }));
|
|
39
|
+
};
|
|
@@ -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"}
|