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.
Files changed (66) hide show
  1. package/dist/App.d.ts.map +1 -1
  2. package/dist/App.js +5 -2
  3. package/dist/components/form-controls/Pagination.d.ts +16 -0
  4. package/dist/components/form-controls/Pagination.d.ts.map +1 -0
  5. package/dist/components/form-controls/Pagination.js +60 -0
  6. package/dist/components/form-controls/index.d.ts +2 -0
  7. package/dist/components/form-controls/index.d.ts.map +1 -1
  8. package/dist/components/form-controls/index.js +1 -0
  9. package/dist/components/layout/Card.d.ts +4 -1
  10. package/dist/components/layout/Card.d.ts.map +1 -1
  11. package/dist/components/layout/Card.js +30 -1
  12. package/dist/components/layout/DataTable.d.ts +27 -0
  13. package/dist/components/layout/DataTable.d.ts.map +1 -0
  14. package/dist/components/layout/DataTable.js +160 -0
  15. package/dist/components/layout/TabPanel.d.ts +7 -0
  16. package/dist/components/layout/TabPanel.d.ts.map +1 -0
  17. package/dist/components/layout/TabPanel.js +11 -0
  18. package/dist/components/layout/TabsGroup.d.ts +20 -0
  19. package/dist/components/layout/TabsGroup.d.ts.map +1 -0
  20. package/dist/components/layout/TabsGroup.js +52 -0
  21. package/dist/components/layout/index.d.ts +6 -0
  22. package/dist/components/layout/index.d.ts.map +1 -1
  23. package/dist/components/layout/index.js +3 -0
  24. package/dist/components/utils/Dialog.d.ts +11 -0
  25. package/dist/components/utils/Dialog.d.ts.map +1 -0
  26. package/dist/components/utils/Dialog.js +39 -0
  27. package/dist/components/utils/DropdownMenu.d.ts +25 -0
  28. package/dist/components/utils/DropdownMenu.d.ts.map +1 -0
  29. package/dist/components/utils/DropdownMenu.js +145 -0
  30. package/dist/components/utils/Loader.d.ts +11 -0
  31. package/dist/components/utils/Loader.d.ts.map +1 -0
  32. package/dist/components/utils/Loader.js +44 -0
  33. package/dist/components/utils/index.d.ts +4 -0
  34. package/dist/components/utils/index.d.ts.map +1 -1
  35. package/dist/components/utils/index.js +2 -0
  36. package/dist/docs/AuthDocs.tsx/AuthDocsContent.js +3 -1
  37. package/dist/docs/CardDocs.d.ts.map +1 -1
  38. package/dist/docs/CardDocs.js +7 -1
  39. package/dist/docs/DataTableDocs.d.ts +4 -0
  40. package/dist/docs/DataTableDocs.d.ts.map +1 -0
  41. package/dist/docs/DataTableDocs.js +240 -0
  42. package/dist/docs/DialogDocs.d.ts +4 -0
  43. package/dist/docs/DialogDocs.d.ts.map +1 -0
  44. package/dist/docs/DialogDocs.js +12 -0
  45. package/dist/docs/DocsMenu.d.ts.map +1 -1
  46. package/dist/docs/DocsMenu.js +1 -1
  47. package/dist/docs/DocsRouter.d.ts.map +1 -1
  48. package/dist/docs/DocsRouter.js +7 -1
  49. package/dist/docs/DropdownMenuDocs.d.ts +4 -0
  50. package/dist/docs/DropdownMenuDocs.d.ts.map +1 -0
  51. package/dist/docs/DropdownMenuDocs.js +66 -0
  52. package/dist/docs/LoaderDocs.d.ts +4 -0
  53. package/dist/docs/LoaderDocs.d.ts.map +1 -0
  54. package/dist/docs/LoaderDocs.js +33 -0
  55. package/dist/docs/PaginationDocs.d.ts +4 -0
  56. package/dist/docs/PaginationDocs.d.ts.map +1 -0
  57. package/dist/docs/PaginationDocs.js +38 -0
  58. package/dist/docs/TabsGroupDocs.d.ts +4 -0
  59. package/dist/docs/TabsGroupDocs.d.ts.map +1 -0
  60. package/dist/docs/TabsGroupDocs.js +27 -0
  61. package/dist/index.css +1 -1
  62. package/dist/index.d.ts +14 -0
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +7 -0
  65. package/dist/index.js.map +1 -1
  66. 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;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;
@@ -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"}
@@ -3,3 +3,4 @@ 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";
@@ -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
+ };
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ export interface TabPanelProps {
3
+ children?: React.ReactNode;
4
+ tabId: string | number;
5
+ }
6
+ export declare const TabPanel: React.FC<TabPanelProps>;
7
+ //# sourceMappingURL=TabPanel.d.ts.map
@@ -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"}