flysoft-react-ui 0.5.2 → 1.0.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 -6
- package/dist/components/form-controls/AutocompleteInput.d.ts.map +1 -1
- package/dist/components/form-controls/AutocompleteInput.js +177 -131
- package/dist/components/form-controls/Button.d.ts +3 -0
- package/dist/components/form-controls/Button.d.ts.map +1 -1
- package/dist/components/form-controls/Button.js +160 -19
- package/dist/components/form-controls/Checkbox.d.ts.map +1 -1
- package/dist/components/form-controls/Checkbox.js +3 -1
- package/dist/components/form-controls/DateInput.d.ts +5 -1
- package/dist/components/form-controls/DateInput.d.ts.map +1 -1
- package/dist/components/form-controls/DateInput.js +94 -27
- package/dist/components/form-controls/Input.d.ts.map +1 -1
- package/dist/components/form-controls/Input.js +2 -1
- package/dist/components/form-controls/LinkButton.d.ts +15 -0
- package/dist/components/form-controls/LinkButton.d.ts.map +1 -0
- package/dist/components/form-controls/LinkButton.js +248 -0
- package/dist/components/form-controls/SearchSelectInput-OLD.d.ts.map +1 -1
- package/dist/components/form-controls/SearchSelectInput-OLD.js +5 -4
- package/dist/components/form-controls/SearchSelectInput.d.ts.map +1 -1
- package/dist/components/form-controls/SearchSelectInput.js +3 -2
- 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/Accordion.d.ts +13 -0
- package/dist/components/layout/Accordion.d.ts.map +1 -0
- package/dist/components/layout/Accordion.js +67 -0
- package/dist/components/layout/AppLayout.d.ts.map +1 -1
- package/dist/components/layout/AppLayout.js +7 -7
- package/dist/components/layout/Card.d.ts +8 -3
- package/dist/components/layout/Card.d.ts.map +1 -1
- package/dist/components/layout/Card.js +21 -22
- package/dist/components/layout/DataTable.js +1 -1
- package/dist/components/layout/DropdownMenu.d.ts.map +1 -0
- package/dist/components/{utils → layout}/DropdownMenu.js +12 -6
- package/dist/components/layout/DropdownPanel.d.ts +7 -0
- package/dist/components/layout/DropdownPanel.d.ts.map +1 -0
- package/dist/components/layout/DropdownPanel.js +137 -0
- package/dist/components/{utils → layout}/Filter.d.ts +5 -0
- package/dist/components/layout/Filter.d.ts.map +1 -0
- package/dist/components/{utils → layout}/Filter.js +18 -9
- package/dist/components/layout/Menu.d.ts +31 -0
- package/dist/components/layout/Menu.d.ts.map +1 -0
- package/dist/components/layout/Menu.js +21 -0
- package/dist/components/layout/index.d.ts +10 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/layout/index.js +5 -0
- package/dist/components/utils/Badge.d.ts.map +1 -1
- package/dist/components/utils/Badge.js +3 -2
- package/dist/components/utils/Dialog.d.ts +2 -2
- package/dist/components/utils/Dialog.d.ts.map +1 -1
- package/dist/components/utils/Dialog.js +4 -3
- package/dist/components/utils/FiltersDialog.d.ts +1 -1
- package/dist/components/utils/FiltersDialog.d.ts.map +1 -1
- package/dist/components/utils/FiltersDialog.js +2 -2
- package/dist/components/utils/Loader.js +1 -1
- package/dist/components/utils/RoadMap.d.ts.map +1 -1
- package/dist/components/utils/RoadMap.js +2 -1
- package/dist/components/utils/Snackbar.d.ts.map +1 -1
- package/dist/components/utils/Snackbar.js +2 -1
- package/dist/components/utils/iconUtils.d.ts +16 -0
- package/dist/components/utils/iconUtils.d.ts.map +1 -0
- package/dist/components/utils/iconUtils.js +40 -0
- package/dist/components/utils/index.d.ts +0 -2
- package/dist/components/utils/index.d.ts.map +1 -1
- package/dist/components/utils/index.js +0 -1
- package/dist/contexts/CrudContext.d.ts +62 -0
- package/dist/contexts/CrudContext.d.ts.map +1 -0
- package/dist/contexts/CrudContext.js +333 -0
- package/dist/contexts/index.d.ts +2 -2
- package/dist/contexts/index.d.ts.map +1 -1
- package/dist/contexts/index.js +2 -2
- package/dist/docs/AccordionDocs.d.ts +4 -0
- package/dist/docs/AccordionDocs.d.ts.map +1 -0
- package/dist/docs/AccordionDocs.js +21 -0
- package/dist/docs/AuthDocs.tsx/AuthDocsContent.js +3 -5
- package/dist/docs/AutocompleteInputDocs.js +1 -1
- package/dist/docs/ButtonDocs.d.ts.map +1 -1
- package/dist/docs/ButtonDocs.js +1 -1
- package/dist/docs/CardDocs.d.ts.map +1 -1
- package/dist/docs/CardDocs.js +17 -8
- package/dist/docs/DataTableDocs.js +3 -3
- package/dist/docs/DialogDocs.d.ts.map +1 -1
- package/dist/docs/DialogDocs.js +1 -1
- package/dist/docs/DocAdmin.js +1 -1
- package/dist/docs/DocsMenu.d.ts.map +1 -1
- package/dist/docs/DocsMenu.js +3 -3
- package/dist/docs/DocsRouter.d.ts.map +1 -1
- package/dist/docs/DocsRouter.js +5 -1
- package/dist/docs/DropdownMenuDocs.js +1 -1
- package/dist/docs/DropdownPanelDocs.d.ts +4 -0
- package/dist/docs/DropdownPanelDocs.d.ts.map +1 -0
- package/dist/docs/DropdownPanelDocs.js +7 -0
- package/dist/docs/FilterDocs.d.ts.map +1 -1
- package/dist/docs/FilterDocs.js +19 -1
- package/dist/docs/LinkButtonDocs.d.ts +4 -0
- package/dist/docs/LinkButtonDocs.d.ts.map +1 -0
- package/dist/docs/LinkButtonDocs.js +7 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.d.ts +0 -9
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.d.ts.map +1 -1
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.js +32 -16
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaPersonas.d.ts +2 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaPersonas.d.ts.map +1 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaPersonas.js +34 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaSingle.d.ts +2 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaSingle.d.ts.map +1 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaSingle.js +66 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresas.d.ts +2 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresas.d.ts.map +1 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresas.js +7 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresasPersonasEditDialog.d.ts +10 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresasPersonasEditDialog.d.ts.map +1 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresasPersonasEditDialog.js +39 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.d.ts.map +1 -1
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.js +33 -27
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsEditDialog.d.ts +9 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsEditDialog.d.ts.map +1 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsEditDialog.js +30 -0
- package/dist/docs/MenuDocs.d.ts +4 -0
- package/dist/docs/MenuDocs.d.ts.map +1 -0
- package/dist/docs/MenuDocs.js +26 -0
- package/dist/docs/SearchSelectInputDocs.js +1 -1
- package/dist/docs/docMockServices/empresaService.d.ts +5 -5
- package/dist/docs/docMockServices/empresaService.d.ts.map +1 -1
- package/dist/docs/docMockServices/empresaService.js +20 -11
- package/dist/docs/docMockServices/interfaces.d.ts +12 -0
- package/dist/docs/docMockServices/interfaces.d.ts.map +1 -1
- package/dist/docs/docMockServices/personaEmpresaService.d.ts +6 -6
- package/dist/docs/docMockServices/personaEmpresaService.d.ts.map +1 -1
- package/dist/docs/docMockServices/personaEmpresaService.js +52 -14
- package/dist/docs/docMockServices/personaService.d.ts +2 -2
- package/dist/docs/docMockServices/personaService.d.ts.map +1 -1
- package/dist/docs/docMockServices/personaService.js +17 -7
- package/dist/index.css +1 -1
- package/dist/index.d.ts +12 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/templates/forms/ContactForm.js +2 -2
- package/dist/templates/forms/LoginForm.js +1 -1
- package/dist/templates/forms/RegistrationForm.js +1 -1
- package/dist/templates/layouts/SidebarLayout.d.ts.map +1 -1
- package/dist/templates/layouts/SidebarLayout.js +3 -2
- package/dist/templates/patterns/FormPattern.d.ts.map +1 -1
- package/dist/templates/patterns/FormPattern.js +4 -3
- package/package.json +4 -3
- package/dist/components/utils/DropdownMenu.d.ts.map +0 -1
- package/dist/components/utils/Filter.d.ts.map +0 -1
- package/dist/contexts/ListCrudContext.d.ts +0 -29
- package/dist/contexts/ListCrudContext.d.ts.map +0 -1
- package/dist/contexts/ListCrudContext.js +0 -209
- /package/dist/components/{utils → layout}/DropdownMenu.d.ts +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React from "react";
|
|
3
|
-
|
|
4
|
-
export const Card = ({ title, subtitle, children, className = "", headerActions, footer, variant = "default", }) => {
|
|
3
|
+
export const Card = ({ title, subtitle, children, className = "", headerActions, footer, variant = "default", alwaysDisplayHeaderActions = false, }) => {
|
|
5
4
|
// Separar clases de background del className
|
|
6
5
|
const classArray = className.trim().split(/\s+/).filter(Boolean);
|
|
7
6
|
const bgClasses = [];
|
|
@@ -16,9 +15,9 @@ export const Card = ({ title, subtitle, children, className = "", headerActions,
|
|
|
16
15
|
}
|
|
17
16
|
});
|
|
18
17
|
const backgroundClass = bgClasses.length > 0 ? bgClasses.join(" ") : "bg-[var(--color-bg-default)]";
|
|
19
|
-
const baseClasses = `
|
|
20
|
-
${backgroundClass} rounded-lg border
|
|
21
|
-
font-[var(--font-default)]
|
|
18
|
+
const baseClasses = `
|
|
19
|
+
${backgroundClass} rounded-lg border
|
|
20
|
+
font-[var(--font-default)]
|
|
22
21
|
`;
|
|
23
22
|
const variantClasses = {
|
|
24
23
|
default: `border-[var(--color-border-default)]`,
|
|
@@ -26,22 +25,11 @@ export const Card = ({ title, subtitle, children, className = "", headerActions,
|
|
|
26
25
|
outlined: `border-[var(--color-gray-300)]`,
|
|
27
26
|
};
|
|
28
27
|
const classes = `${baseClasses} ${variantClasses[variant]} ${otherClasses.join(" ")}`;
|
|
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
28
|
const [isHovered, setIsHovered] = React.useState(false);
|
|
42
29
|
const [isLargeScreen, setIsLargeScreen] = React.useState(false);
|
|
43
30
|
React.useEffect(() => {
|
|
44
31
|
const checkScreenSize = () => {
|
|
32
|
+
// md breakpoint en Tailwind es 768px, así que lg es 1024px
|
|
45
33
|
setIsLargeScreen(window.innerWidth >= 1024);
|
|
46
34
|
};
|
|
47
35
|
checkScreenSize();
|
|
@@ -50,9 +38,20 @@ export const Card = ({ title, subtitle, children, className = "", headerActions,
|
|
|
50
38
|
window.removeEventListener("resize", checkScreenSize);
|
|
51
39
|
};
|
|
52
40
|
}, []);
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
41
|
+
// Determinar la opacidad de las headerActions
|
|
42
|
+
const getHeaderActionsOpacity = () => {
|
|
43
|
+
if (!headerActions)
|
|
44
|
+
return 0;
|
|
45
|
+
// En pantallas pequeñas (md e inferiores) siempre se muestran
|
|
46
|
+
if (!isLargeScreen)
|
|
47
|
+
return 1;
|
|
48
|
+
// Si alwaysDisplayHeaderActions es true, siempre se muestran
|
|
49
|
+
if (alwaysDisplayHeaderActions)
|
|
50
|
+
return 1;
|
|
51
|
+
// Si es false y pantalla grande, solo al hacer hover
|
|
52
|
+
return isHovered ? 1 : 0;
|
|
53
|
+
};
|
|
54
|
+
return (_jsxs("div", { className: `${classes} relative`, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), 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("div", { className: "text-sm text-[var(--color-text-secondary)] mt-1", children: subtitle }))] }), headerActions && (_jsx("div", { className: "flex items-center transition-opacity", style: {
|
|
55
|
+
opacity: getHeaderActionsOpacity(),
|
|
56
|
+
}, children: headerActions }))] }) })), children && _jsx("div", { className: "px-6 py-4", children: children }), footer && _jsx("div", { className: "px-6 pb-4 flex items-center justify-end", children: footer })] }));
|
|
58
57
|
};
|
|
@@ -1,6 +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 "
|
|
3
|
+
import { DropdownMenu } from "./DropdownMenu";
|
|
4
4
|
export const DataTable = ({ columns, rows, className = "", maxRows, locale = "es-AR", isLoading = false, loadingRows = 5, }) => {
|
|
5
5
|
// Calcular si necesitamos scroll
|
|
6
6
|
const displayRows = isLoading ? loadingRows : rows.length;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DropdownMenu.d.ts","sourceRoot":"","sources":["../../../src/components/layout/DropdownMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAMN,MAAM,OAAO,CAAC;AAIf,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,4CAuNtB,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React, { useState, useRef, useEffect, useCallback, useMemo, } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
3
4
|
import { Button } from "../form-controls/Button";
|
|
4
5
|
export const DropdownMenu = ({ options, onOptionSelected, renderNode, getOptionLabel, renderOption, replaceOnSingleOption = false, }) => {
|
|
5
6
|
const [isOpen, setIsOpen] = useState(false);
|
|
@@ -136,10 +137,15 @@ export const DropdownMenu = ({ options, onOptionSelected, renderNode, getOptionL
|
|
|
136
137
|
if (shouldReplace && singleOption) {
|
|
137
138
|
return (_jsx("div", { onClick: () => handleOptionClick(singleOption), className: "cursor-pointer", children: renderOption ? renderOption(singleOption) : labelGetter(singleOption) }));
|
|
138
139
|
}
|
|
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 &&
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
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 &&
|
|
141
|
+
(typeof document !== "undefined" && document.body
|
|
142
|
+
? createPortal(_jsx("div", { ref: menuRef, className: "fixed z-[2000] 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) => {
|
|
143
|
+
const key = String(option?.id ??
|
|
144
|
+
labelGetter(option) ??
|
|
145
|
+
index);
|
|
146
|
+
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
|
|
147
|
+
? renderOption(option)
|
|
148
|
+
: labelGetter(option) }, key));
|
|
149
|
+
}) }), document.body)
|
|
150
|
+
: null)] }));
|
|
145
151
|
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface DropdownPanelProps {
|
|
3
|
+
renderNode?: React.ReactNode;
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
}
|
|
6
|
+
export declare const DropdownPanel: ({ renderNode, children, }: DropdownPanelProps) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
//# sourceMappingURL=DropdownPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DropdownPanel.d.ts","sourceRoot":"","sources":["../../../src/components/layout/DropdownPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAMN,MAAM,OAAO,CAAC;AAIf,MAAM,WAAW,kBAAkB;IAC/B,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC7B;AAED,eAAO,MAAM,aAAa,GAAI,2BAG3B,kBAAkB,4CAmLpB,CAAC"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useState, useRef, useEffect, useCallback, useMemo, } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
4
|
+
import { Button } from "../form-controls/Button";
|
|
5
|
+
export const DropdownPanel = ({ renderNode, children, }) => {
|
|
6
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
7
|
+
const [menuPosition, setMenuPosition] = useState("bottom");
|
|
8
|
+
const [scrollUpdate, setScrollUpdate] = useState(0);
|
|
9
|
+
const triggerRef = useRef(null);
|
|
10
|
+
const menuRef = useRef(null);
|
|
11
|
+
// Calcular posición del menú
|
|
12
|
+
const calculatePosition = useCallback(() => {
|
|
13
|
+
if (isOpen && triggerRef.current) {
|
|
14
|
+
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
15
|
+
const viewportHeight = window.innerHeight;
|
|
16
|
+
// Intentar obtener la altura real del menú, o usar una estimación si no está montado aún
|
|
17
|
+
let menuHeight = 200; // valor por defecto
|
|
18
|
+
if (menuRef.current) {
|
|
19
|
+
menuHeight = menuRef.current.getBoundingClientRect().height;
|
|
20
|
+
}
|
|
21
|
+
const menuMargin = 4;
|
|
22
|
+
// Calcular espacio disponible arriba y abajo
|
|
23
|
+
const spaceBelow = viewportHeight - triggerRect.bottom - menuMargin;
|
|
24
|
+
const spaceAbove = triggerRect.top - menuMargin;
|
|
25
|
+
// Si no hay suficiente espacio abajo pero sí arriba, mostrar arriba
|
|
26
|
+
if (spaceBelow < menuHeight && spaceAbove > spaceBelow) {
|
|
27
|
+
setMenuPosition("top");
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
setMenuPosition("bottom"); // Preferir abajo si cabe o si es el que más espacio tiene, o por defecto
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}, [isOpen]);
|
|
34
|
+
// Recalcular posición cuando cambia isOpen (y cuando el contenido podría haber cambiado el tamaño)
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
calculatePosition();
|
|
37
|
+
// Podríamos necesitar un ResizeObserver para ser más robustos si el contenido cambia
|
|
38
|
+
}, [calculatePosition]);
|
|
39
|
+
// Recalcular posición al hacer scroll o redimensionar
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (isOpen) {
|
|
42
|
+
const handleScroll = () => {
|
|
43
|
+
calculatePosition();
|
|
44
|
+
// Forzar actualización del estilo del menú
|
|
45
|
+
setScrollUpdate((prev) => prev + 1);
|
|
46
|
+
};
|
|
47
|
+
window.addEventListener("scroll", handleScroll, true);
|
|
48
|
+
window.addEventListener("resize", handleScroll);
|
|
49
|
+
return () => {
|
|
50
|
+
window.removeEventListener("scroll", handleScroll, true);
|
|
51
|
+
window.removeEventListener("resize", handleScroll);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}, [isOpen, calculatePosition]);
|
|
55
|
+
// Cerrar menú al hacer click fuera
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const handleClickOutside = (event) => {
|
|
58
|
+
if (isOpen &&
|
|
59
|
+
triggerRef.current &&
|
|
60
|
+
menuRef.current &&
|
|
61
|
+
!triggerRef.current.contains(event.target) &&
|
|
62
|
+
!menuRef.current.contains(event.target)) {
|
|
63
|
+
setIsOpen(false);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
if (isOpen) {
|
|
67
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
68
|
+
}
|
|
69
|
+
return () => {
|
|
70
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
71
|
+
};
|
|
72
|
+
}, [isOpen]);
|
|
73
|
+
// Cerrar menú al presionar Escape
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
const handleEscape = (event) => {
|
|
76
|
+
if (event.key === "Escape" && isOpen) {
|
|
77
|
+
setIsOpen(false);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
if (isOpen) {
|
|
81
|
+
document.addEventListener("keydown", handleEscape);
|
|
82
|
+
}
|
|
83
|
+
return () => {
|
|
84
|
+
document.removeEventListener("keydown", handleEscape);
|
|
85
|
+
};
|
|
86
|
+
}, [isOpen]);
|
|
87
|
+
const handleToggle = () => {
|
|
88
|
+
setIsOpen(!isOpen);
|
|
89
|
+
};
|
|
90
|
+
const menuStyles = useMemo(() => {
|
|
91
|
+
if (!isOpen || !triggerRef.current) {
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
95
|
+
const menuMargin = 4;
|
|
96
|
+
// Asegurar que el menú no se salga de la pantalla horizontalmente
|
|
97
|
+
let leftPosition = triggerRect.left;
|
|
98
|
+
const menuMinWidth = 160;
|
|
99
|
+
const viewportWidth = window.innerWidth;
|
|
100
|
+
// Si el menú se sale por la derecha, ajustar la posición
|
|
101
|
+
// Nota: Como el ancho es dinámico (basado en children), idealmente deberíamos medirlo.
|
|
102
|
+
// Usaremos menuRef si está disponible o un estimado.
|
|
103
|
+
let currentMenuWidth = menuMinWidth;
|
|
104
|
+
if (menuRef.current) {
|
|
105
|
+
currentMenuWidth = menuRef.current.getBoundingClientRect().width;
|
|
106
|
+
}
|
|
107
|
+
if (leftPosition + currentMenuWidth > viewportWidth) {
|
|
108
|
+
leftPosition = viewportWidth - currentMenuWidth - 8; // 8px de margen
|
|
109
|
+
}
|
|
110
|
+
// Asegurar que no se salga por la izquierda
|
|
111
|
+
if (leftPosition < 8) {
|
|
112
|
+
leftPosition = 8;
|
|
113
|
+
}
|
|
114
|
+
if (menuPosition === "top") {
|
|
115
|
+
return {
|
|
116
|
+
position: "fixed",
|
|
117
|
+
bottom: window.innerHeight - triggerRect.top + menuMargin,
|
|
118
|
+
left: leftPosition,
|
|
119
|
+
minWidth: Math.max(triggerRect.width, menuMinWidth), // Mantener el minWidth del trigger o 160
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
return {
|
|
124
|
+
position: "fixed",
|
|
125
|
+
top: triggerRect.bottom + menuMargin,
|
|
126
|
+
left: leftPosition,
|
|
127
|
+
minWidth: Math.max(triggerRect.width, menuMinWidth),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
// scrollUpdate se usa intencionalmente para forzar el recálculo en scroll
|
|
131
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
132
|
+
}, [isOpen, menuPosition, scrollUpdate]);
|
|
133
|
+
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 &&
|
|
134
|
+
(typeof document !== "undefined" && document.body
|
|
135
|
+
? createPortal(_jsx("div", { ref: menuRef, className: "fixed z-[2000] 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: children }), document.body)
|
|
136
|
+
: null)] }));
|
|
137
|
+
};
|
|
@@ -18,6 +18,11 @@ interface BaseFilterProps {
|
|
|
18
18
|
* Por defecto es false.
|
|
19
19
|
*/
|
|
20
20
|
hideEmpty?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Si es true, el componente se muestra con opacidad y no permite interacciones.
|
|
23
|
+
* Si el panel está abierto, se cerrará automáticamente.
|
|
24
|
+
*/
|
|
25
|
+
disabled?: boolean;
|
|
21
26
|
}
|
|
22
27
|
export interface TextFilterProps extends BaseFilterProps {
|
|
23
28
|
filterType?: "text";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Filter.d.ts","sourceRoot":"","sources":["../../../src/components/layout/Filter.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAQ3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAE7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAIvE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAGD,UAAU,eAAe;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAC/C;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAGD,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,UAAU,EAAE,QAAQ,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,uBAAuB,CAAC,CAAC,GAAG,kBAAkB,EAAE,CAAC,GAAG,MAAM,CACzE,SAAQ,eAAe;IACvB,UAAU,EAAE,cAAc,CAAC;IAC3B,OAAO,EAAE,CAAC,EAAE,CAAC;IACb,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IACrC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC;IAChC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,UAAU,EAAE,QAAQ,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB,CAAC,CAAC,GAAG,kBAAkB,EAAE,CAAC,GAAG,MAAM,CACzE,SAAQ,eAAe;IACvB,UAAU,EAAE,cAAc,CAAC;IAC3B,iBAAiB,EAAE,CACjB,IAAI,EAAE,MAAM,KACT,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,uBAAuB,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IAC9D,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IACrC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC;IAChC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAGD,MAAM,MAAM,WAAW,GACnB,eAAe,GACf,iBAAiB,GACjB,eAAe,GACf,uBAAuB,GACvB,iBAAiB,GACjB,uBAAuB,CAAC;AAE5B,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAy+BxC,CAAC"}
|
|
@@ -8,9 +8,10 @@ import { Input } from "../form-controls/Input";
|
|
|
8
8
|
import { DateInput } from "../form-controls/DateInput";
|
|
9
9
|
import { AutocompleteInput } from "../form-controls/AutocompleteInput";
|
|
10
10
|
import { SearchSelectInput } from "../form-controls/SearchSelectInput";
|
|
11
|
-
import { DataField } from "
|
|
11
|
+
import { DataField } from "./DataField";
|
|
12
|
+
import { normalizeIconClass } from "../utils/iconUtils";
|
|
12
13
|
export const Filter = (props) => {
|
|
13
|
-
const { paramName, label, staticOptions, inputWidth, value: propValue, onChange, hideEmpty = false, } = props;
|
|
14
|
+
const { paramName, label, staticOptions, inputWidth, value: propValue, onChange, hideEmpty = false, disabled = false, } = props;
|
|
14
15
|
const filterType = props.filterType || "text";
|
|
15
16
|
// Calcular el ancho por defecto según el tipo de filtro
|
|
16
17
|
const defaultInputWidth = filterType === "date" || filterType === "autocomplete" ? "160px" : "200px";
|
|
@@ -157,6 +158,12 @@ export const Filter = (props) => {
|
|
|
157
158
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
158
159
|
};
|
|
159
160
|
}, [isOpen]);
|
|
161
|
+
// Cerrar el panel si el componente se deshabilita
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
if (disabled) {
|
|
164
|
+
setIsOpen(false);
|
|
165
|
+
}
|
|
166
|
+
}, [disabled]);
|
|
160
167
|
const handleSetFilter = () => {
|
|
161
168
|
const newValue = inputValue.trim() || undefined;
|
|
162
169
|
// Si hay onChange, llamarlo con el nuevo valor
|
|
@@ -439,6 +446,8 @@ export const Filter = (props) => {
|
|
|
439
446
|
}
|
|
440
447
|
};
|
|
441
448
|
const handleTogglePanel = () => {
|
|
449
|
+
if (disabled)
|
|
450
|
+
return;
|
|
442
451
|
if (!isOpen) {
|
|
443
452
|
// Calcular posición antes de abrir
|
|
444
453
|
if (containerRef.current) {
|
|
@@ -499,10 +508,10 @@ export const Filter = (props) => {
|
|
|
499
508
|
}
|
|
500
509
|
// Contenedor tipo badge con diseño similar al Input
|
|
501
510
|
// Altura ajustada para coincidir con input sm: py-1.5 (6px arriba y abajo) + text-sm (14px línea) = ~26px total
|
|
502
|
-
const badgeContainer = (_jsxs("div", { className: "inline-flex items-center gap-2 px-3 py-1.5 h-[2.1rem] rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-default)] text-[var(--color-text-primary)] font-[var(--font-default)] cursor-pointer text-sm transition-colors", onClick: handleTogglePanel, children: [_jsx("span", { className: "text-sm min-w-[1rem]", children: getDisplayValue() || "\u00A0" }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("span", { className: "p-0.5 hover:bg-[var(--color-bg-secondary)] rounded transition-colors flex items-center justify-center", children: _jsx("i", { className:
|
|
511
|
+
const badgeContainer = (_jsxs("div", { className: "inline-flex items-center gap-2 px-3 py-1.5 h-[2.1rem] rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-default)] text-[var(--color-text-primary)] font-[var(--font-default)] cursor-pointer text-sm transition-colors", onClick: handleTogglePanel, children: [_jsx("span", { className: "text-sm min-w-[1rem]", children: getDisplayValue() || "\u00A0" }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("span", { className: "p-0.5 hover:bg-[var(--color-bg-secondary)] rounded transition-colors flex items-center justify-center", children: _jsx("i", { className: `${normalizeIconClass("fa-chevron-down")} text-xs text-[var(--color-text-muted)] hover:text-[var(--color-primary)] transition-all ${isOpen ? "rotate-180" : ""}` }) }), currentValue && (_jsx("button", { onClick: handleClearFilter, className: "p-0.5 hover:bg-[var(--color-bg-secondary)] rounded transition-colors flex items-center justify-center", "aria-label": "Borrar filtro", type: "button", children: _jsx("i", { className: `${normalizeIconClass("fa-times")} text-xs text-[var(--color-text-muted)] hover:text-[var(--color-primary)] transition-colors` }) }))] })] }));
|
|
503
512
|
// Renderizar según el tipo de filtro
|
|
504
513
|
if (filterType === "autocomplete") {
|
|
505
|
-
return (_jsxs("div", { ref: containerRef, className:
|
|
514
|
+
return (_jsxs("div", { ref: containerRef, className: `relative inline-block ${disabled ? "opacity-50 pointer-events-none" : ""}`, children: [_jsx(DataField, { label: label, value: badgeContainer, className: "inline-block" }), isOpen &&
|
|
506
515
|
panelPosition &&
|
|
507
516
|
typeof document !== "undefined" &&
|
|
508
517
|
document.body &&
|
|
@@ -514,10 +523,10 @@ export const Filter = (props) => {
|
|
|
514
523
|
: "text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)]"}`, onMouseDown: (e) => {
|
|
515
524
|
e.preventDefault();
|
|
516
525
|
handleStaticOptionSelect(option);
|
|
517
|
-
}, children: _jsx("span", { className: "font-[var(--font-default)]", children: option.text }) }, option.value))) })), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { style: { width: finalInputWidth }, children: _jsx(AutocompleteInput, { ref: autocompleteInputRef, options: props.options, value: isUserTyping ? undefined : autocompleteValue, onChange: handleAutocompleteChange, getOptionLabel: props.getOptionLabel, getOptionValue: props.getOptionValue, renderOption: props.renderOption, noResultsText: props.noResultsText, onSelectOption: handleAutocompleteOptionSelect }) }), _jsx(Button, { onClick: handleAutocompleteSelect, icon: "fa-arrow-right", variant: "ghost" })] })] }) }), document.body)] }));
|
|
526
|
+
}, children: _jsx("span", { className: "font-[var(--font-default)]", children: option.text }) }, option.value))) })), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { style: { width: finalInputWidth }, children: _jsx(AutocompleteInput, { ref: autocompleteInputRef, options: props.options, value: isUserTyping ? undefined : autocompleteValue, onChange: handleAutocompleteChange, getOptionLabel: props.getOptionLabel, getOptionValue: props.getOptionValue, renderOption: props.renderOption, noResultsText: props.noResultsText, onSelectOption: handleAutocompleteOptionSelect, disabled: disabled }) }), _jsx(Button, { onClick: handleAutocompleteSelect, icon: "fa-arrow-right", variant: "ghost" })] })] }) }), document.body)] }));
|
|
518
527
|
}
|
|
519
528
|
if (filterType === "date") {
|
|
520
|
-
return (_jsxs("div", { ref: containerRef, className:
|
|
529
|
+
return (_jsxs("div", { ref: containerRef, className: `relative inline-block ${disabled ? "opacity-50 pointer-events-none" : ""}`, children: [_jsx(DataField, { label: label, value: badgeContainer, className: "inline-block" }), isOpen &&
|
|
521
530
|
panelPosition &&
|
|
522
531
|
typeof document !== "undefined" &&
|
|
523
532
|
document.body &&
|
|
@@ -534,7 +543,7 @@ export const Filter = (props) => {
|
|
|
534
543
|
// Para searchSelect
|
|
535
544
|
if (filterType === "searchSelect") {
|
|
536
545
|
const searchSelectProps = props;
|
|
537
|
-
return (_jsxs("div", { ref: containerRef, className:
|
|
546
|
+
return (_jsxs("div", { ref: containerRef, className: `relative inline-block ${disabled ? "opacity-50 pointer-events-none" : ""}`, children: [_jsx(DataField, { label: label, value: badgeContainer, className: "inline-block" }), isOpen &&
|
|
538
547
|
panelPosition &&
|
|
539
548
|
typeof document !== "undefined" &&
|
|
540
549
|
document.body &&
|
|
@@ -553,10 +562,10 @@ export const Filter = (props) => {
|
|
|
553
562
|
// Mostrar X solo si hay un valor actual Y el texto del input coincide con ese valor
|
|
554
563
|
const hasValue = !!currentValue;
|
|
555
564
|
const valueMatches = hasValue && searchValue.trim() === currentValue;
|
|
556
|
-
return (_jsx("div", { ref: containerRef, className:
|
|
565
|
+
return (_jsx("div", { ref: containerRef, className: `relative inline-block ${disabled ? "opacity-50 pointer-events-none" : ""}`, children: _jsx(DataField, { label: label, value: _jsx("div", { style: { width: finalInputWidth }, children: _jsx(Input, { ref: searchInputRef, type: "text", value: searchValue, onChange: handleSearchChange, onKeyDown: handleSearchKeyDown, icon: valueMatches ? "fa-times" : "fa-search", iconPosition: "right", onIconClick: handleSearchIconClick, placeholder: "Buscar...", size: "sm", disabled: disabled }) }), className: "inline-block" }) }));
|
|
557
566
|
}
|
|
558
567
|
// Para text y number
|
|
559
|
-
return (_jsxs("div", { ref: containerRef, className:
|
|
568
|
+
return (_jsxs("div", { ref: containerRef, className: `relative inline-block ${disabled ? "opacity-50 pointer-events-none" : ""}`, children: [_jsx(DataField, { label: label, value: badgeContainer, className: "inline-block" }), isOpen &&
|
|
560
569
|
panelPosition &&
|
|
561
570
|
typeof document !== "undefined" &&
|
|
562
571
|
document.body &&
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface MenuProps<T = {
|
|
3
|
+
label: string;
|
|
4
|
+
}> {
|
|
5
|
+
options: T[];
|
|
6
|
+
onOptionSelected: (item: T) => void;
|
|
7
|
+
/**
|
|
8
|
+
* Obtiene el label que se muestra para cada opción. Por defecto usa la propiedad "label".
|
|
9
|
+
*/
|
|
10
|
+
getOptionLabel?: (item: T) => string;
|
|
11
|
+
/**
|
|
12
|
+
* Renderizado personalizado de cada opción. Si se define, se ignora el render por defecto.
|
|
13
|
+
*/
|
|
14
|
+
renderOption?: (item: T) => React.ReactNode;
|
|
15
|
+
/**
|
|
16
|
+
* Clases adicionales para el contenedor del menú. Permite sobreescribir los estilos por defecto.
|
|
17
|
+
*/
|
|
18
|
+
className?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Estilos adicionales para el contenedor del menú.
|
|
21
|
+
*/
|
|
22
|
+
style?: React.CSSProperties;
|
|
23
|
+
/**
|
|
24
|
+
* Clases adicionales para cada opción del menú.
|
|
25
|
+
*/
|
|
26
|
+
itemClassName?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare const Menu: <T = {
|
|
29
|
+
label: string;
|
|
30
|
+
}>({ options, onOptionSelected, getOptionLabel, renderOption, className, style, itemClassName, }: MenuProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
31
|
+
//# sourceMappingURL=Menu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Menu.d.ts","sourceRoot":"","sources":["../../../src/components/layout/Menu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsB,MAAM,OAAO,CAAC;AAG3C,MAAM,WAAW,SAAS,CAAC,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE;IAC5C,OAAO,EAAE,CAAC,EAAE,CAAC;IACb,gBAAgB,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IACpC;;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;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAC5B;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,eAAO,MAAM,IAAI,GAAI,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,EAAG,+FAQ1C,SAAS,CAAC,CAAC,CAAC,4CA8Cd,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React, { useCallback } from "react";
|
|
3
|
+
import { twMerge } from "tailwind-merge";
|
|
4
|
+
export const Menu = ({ options, onOptionSelected, getOptionLabel, renderOption, className, style, itemClassName, }) => {
|
|
5
|
+
const labelGetter = useCallback((item) => {
|
|
6
|
+
if (getOptionLabel)
|
|
7
|
+
return getOptionLabel(item);
|
|
8
|
+
const anyItem = item;
|
|
9
|
+
return (anyItem.label ?? "").toString();
|
|
10
|
+
}, [getOptionLabel]);
|
|
11
|
+
const handleOptionClick = (item) => {
|
|
12
|
+
onOptionSelected(item);
|
|
13
|
+
};
|
|
14
|
+
const mergedClassName = twMerge("bg-[var(--color-bg-default)] border border-[var(--color-border-default)] rounded-md shadow-[var(--shadow-sm)] py-1 min-w-[160px] font-[var(--font-default)] inline-block", className);
|
|
15
|
+
return (_jsx("div", { className: mergedClassName, style: style, children: options.map((option, index) => {
|
|
16
|
+
const key = String(option?.id ??
|
|
17
|
+
labelGetter(option) ??
|
|
18
|
+
index);
|
|
19
|
+
return (_jsx("div", { onClick: () => handleOptionClick(option), className: twMerge("px-4 py-2 text-sm text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] cursor-pointer transition-colors flex items-center", itemClassName), children: renderOption ? renderOption(option) : labelGetter(option) }, key));
|
|
20
|
+
}) }));
|
|
21
|
+
};
|
|
@@ -12,4 +12,14 @@ export { TabPanel } from "./TabPanel";
|
|
|
12
12
|
export type { TabPanelProps } from "./TabPanel";
|
|
13
13
|
export { DataTable } from "./DataTable";
|
|
14
14
|
export type { DataTableProps, DataTableColumn } from "./DataTable";
|
|
15
|
+
export { Accordion } from "./Accordion";
|
|
16
|
+
export type { AccordionProps } from "./Accordion";
|
|
17
|
+
export { Menu } from "./Menu";
|
|
18
|
+
export type { MenuProps } from "./Menu";
|
|
19
|
+
export { DropdownMenu } from "./DropdownMenu";
|
|
20
|
+
export type { DropdownMenuProps } from "./DropdownMenu";
|
|
21
|
+
export { DropdownPanel } from "./DropdownPanel";
|
|
22
|
+
export type { DropdownPanelProps } from "./DropdownPanel";
|
|
23
|
+
export { Filter } from "./Filter";
|
|
24
|
+
export type { FilterProps } from "./Filter";
|
|
15
25
|
//# 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;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,eAAe,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;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,YAAY,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -5,3 +5,8 @@ export { DataField } from "./DataField";
|
|
|
5
5
|
export { TabsGroup } from "./TabsGroup";
|
|
6
6
|
export { TabPanel } from "./TabPanel";
|
|
7
7
|
export { DataTable } from "./DataTable";
|
|
8
|
+
export { Accordion } from "./Accordion";
|
|
9
|
+
export { Menu } from "./Menu";
|
|
10
|
+
export { DropdownMenu } from "./DropdownMenu";
|
|
11
|
+
export { DropdownPanel } from "./DropdownPanel";
|
|
12
|
+
export { Filter } from "./Filter";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Badge.d.ts","sourceRoot":"","sources":["../../../src/components/utils/Badge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Badge.d.ts","sourceRoot":"","sources":["../../../src/components/utils/Badge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAkC1B,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,OAAO,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC9E,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;CAC1D;AAED,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,UAAU,CA2KtC,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 { normalizeIconClass } from "./iconUtils";
|
|
3
4
|
// Función helper para convertir nombres de colores comunes a valores CSS válidos
|
|
4
5
|
const getColorValue = (color) => {
|
|
5
6
|
if (!color)
|
|
@@ -109,8 +110,8 @@ export const Badge = ({ children, variant = "primary", size = "md", rounded = fa
|
|
|
109
110
|
return null;
|
|
110
111
|
// Si hay onClick y hay iconos, el onClick se aplica solo a los iconos
|
|
111
112
|
const iconClasses = onClick
|
|
112
|
-
?
|
|
113
|
-
:
|
|
113
|
+
? `${normalizeIconClass(icon)} ${iconSizeClasses} cursor-pointer`
|
|
114
|
+
: `${normalizeIconClass(icon)} ${iconSizeClasses}`;
|
|
114
115
|
return (_jsx("span", { className: iconPaddingClasses, children: _jsx("i", { className: iconClasses, "aria-hidden": !iconLabel, "aria-label": iconLabel, onClick: onClick, role: onClick ? "button" : undefined, tabIndex: onClick ? 0 : undefined, onKeyDown: onClick
|
|
115
116
|
? (e) => {
|
|
116
117
|
if (e.key === "Enter" || e.key === " ") {
|
|
@@ -2,8 +2,8 @@ import React from "react";
|
|
|
2
2
|
export interface DialogProps {
|
|
3
3
|
isOpen: boolean;
|
|
4
4
|
title: React.ReactNode;
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
footer?: React.ReactNode;
|
|
7
7
|
onClose?: () => void;
|
|
8
8
|
closeOnOverlayClick?: boolean;
|
|
9
9
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../../src/components/utils/Dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../../src/components/utils/Dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AAGzC,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CA2GxC,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React, { useEffect } from "react";
|
|
3
|
-
|
|
3
|
+
import { normalizeIconClass } from "./iconUtils";
|
|
4
|
+
export const Dialog = ({ isOpen, title, children, footer, onClose, closeOnOverlayClick = false, }) => {
|
|
4
5
|
// Prevenir scroll del body cuando el dialog está abierto
|
|
5
6
|
useEffect(() => {
|
|
6
7
|
if (isOpen) {
|
|
@@ -35,9 +36,9 @@ export const Dialog = ({ isOpen, title, dialogBody, dialogActions, onClose, clos
|
|
|
35
36
|
onClose();
|
|
36
37
|
}
|
|
37
38
|
};
|
|
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(), style: { overflow: "visible" }, children: [_jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-[var(--color-border-default)] flex-shrink-0", 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
|
|
39
|
+
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(), style: { overflow: "visible" }, children: [_jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-[var(--color-border-default)] flex-shrink-0", 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: normalizeIconClass("fa-times") }) }))] }), _jsx("div", { className: "px-6 py-4 flex-1 text-[var(--color-text-primary)] min-w-0", style: {
|
|
39
40
|
overflowY: "auto",
|
|
40
41
|
overflowX: "visible",
|
|
41
42
|
maxHeight: "calc(90vh - 200px)"
|
|
42
|
-
}, children:
|
|
43
|
+
}, children: children }), footer && (_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: footer }))] })] }));
|
|
43
44
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FiltersDialog.d.ts","sourceRoot":"","sources":["../../../src/components/utils/FiltersDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"FiltersDialog.d.ts","sourceRoot":"","sources":["../../../src/components/utils/FiltersDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG5D,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,WAAW,CAAC,YAAY,CAAC,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC;IAChB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,MAAM,CAAC;IACvC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,GAAG,CAAC;IACpC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,KAAK,CAAC,SAAS,CAAC;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA+ItD,CAAC"}
|
|
@@ -3,7 +3,7 @@ import React, { useState, useEffect } from "react";
|
|
|
3
3
|
import { useSearchParams } from "react-router-dom";
|
|
4
4
|
import { Button } from "../form-controls/Button";
|
|
5
5
|
import { Dialog } from "./Dialog";
|
|
6
|
-
import { Filter } from "
|
|
6
|
+
import { Filter } from "../layout/Filter";
|
|
7
7
|
import { Collection } from "../layout/Collection";
|
|
8
8
|
export const FiltersDialog = ({ filters }) => {
|
|
9
9
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
@@ -100,5 +100,5 @@ export const FiltersDialog = ({ filters }) => {
|
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
|
-
return (_jsxs(_Fragment, { children: [_jsx(Button, { icon: "fa-filter", variant: "outline", size: "sm", onClick: handleOpen, "aria-label": "Abrir filtros", children: "Filtrar" }), _jsx(Dialog, { isOpen: isOpen, title: "Filtros",
|
|
103
|
+
return (_jsxs(_Fragment, { children: [_jsx(Button, { icon: "fa-filter", variant: "outline", size: "sm", onClick: handleOpen, "aria-label": "Abrir filtros", children: "Filtrar" }), _jsx(Dialog, { isOpen: isOpen, title: "Filtros", footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", onClick: handleClose, children: "Cerrar" }), _jsx(Button, { variant: "primary", onClick: handleApplyFilters, children: "Aplicar filtros" })] }), onClose: handleClose, closeOnOverlayClick: false, children: _jsx("div", { className: "space-y-4", children: _jsx(Collection, { direction: "column", gap: "1rem", children: filters.map((filter) => (_jsx(Filter, { ...getFilterProps(filter) }, filter.paramName))) }) }) })] }));
|
|
104
104
|
};
|
|
@@ -3,7 +3,7 @@ import React from "react";
|
|
|
3
3
|
import { useTheme } from "../../contexts/ThemeContext";
|
|
4
4
|
export const Loader = ({ isLoading = false, text, children, keepContentWhileLoading, contentLoadingNode, overlayClassName, }) => {
|
|
5
5
|
const { theme } = useTheme();
|
|
6
|
-
const displayText = text
|
|
6
|
+
const displayText = text;
|
|
7
7
|
// Clases por defecto del overlay (negro semitransparente con blur)
|
|
8
8
|
const defaultOverlayClasses = "bg-black/50 backdrop-blur-sm";
|
|
9
9
|
// Combinar clases por defecto con las personalizadas
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RoadMap.d.ts","sourceRoot":"","sources":["../../../src/components/utils/RoadMap.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"RoadMap.d.ts","sourceRoot":"","sources":["../../../src/components/utils/RoadMap.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AA6E1B,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC9E;;;OAGG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAgH1C,CAAC"}
|