flysoft-react-ui 0.4.0 → 0.5.2
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 +20 -4
- package/dist/components/form-controls/AutocompleteInput.d.ts +11 -3
- package/dist/components/form-controls/AutocompleteInput.d.ts.map +1 -1
- package/dist/components/form-controls/AutocompleteInput.js +410 -31
- package/dist/components/form-controls/Button.js +1 -1
- package/dist/components/form-controls/Checkbox.d.ts +14 -0
- package/dist/components/form-controls/Checkbox.d.ts.map +1 -0
- package/dist/components/form-controls/Checkbox.js +77 -0
- package/dist/components/form-controls/DateInput.d.ts +20 -4
- package/dist/components/form-controls/DateInput.d.ts.map +1 -1
- package/dist/components/form-controls/DateInput.js +425 -70
- package/dist/components/form-controls/DatePicker.d.ts +4 -3
- package/dist/components/form-controls/DatePicker.d.ts.map +1 -1
- package/dist/components/form-controls/DatePicker.js +26 -30
- package/dist/components/form-controls/Input.d.ts +10 -1
- package/dist/components/form-controls/Input.d.ts.map +1 -1
- package/dist/components/form-controls/Input.js +16 -10
- package/dist/components/form-controls/Pagination.d.ts +1 -0
- package/dist/components/form-controls/Pagination.d.ts.map +1 -1
- package/dist/components/form-controls/Pagination.js +3 -40
- package/dist/components/form-controls/RadioButtonGroup.d.ts +62 -0
- package/dist/components/form-controls/RadioButtonGroup.d.ts.map +1 -0
- package/dist/components/form-controls/RadioButtonGroup.js +220 -0
- package/dist/components/form-controls/SearchSelectInput-OLD.d.ts +68 -0
- package/dist/components/form-controls/SearchSelectInput-OLD.d.ts.map +1 -0
- package/dist/components/form-controls/SearchSelectInput-OLD.js +962 -0
- package/dist/components/form-controls/SearchSelectInput.d.ts +70 -0
- package/dist/components/form-controls/SearchSelectInput.d.ts.map +1 -0
- package/dist/components/form-controls/SearchSelectInput.js +335 -0
- package/dist/components/form-controls/index.d.ts +7 -1
- package/dist/components/form-controls/index.d.ts.map +1 -1
- package/dist/components/form-controls/index.js +3 -0
- package/dist/components/layout/AppLayout.d.ts +3 -2
- package/dist/components/layout/AppLayout.d.ts.map +1 -1
- package/dist/components/layout/AppLayout.js +104 -31
- package/dist/components/layout/Card.d.ts +4 -1
- package/dist/components/layout/Card.d.ts.map +1 -1
- package/dist/components/layout/Card.js +30 -1
- package/dist/components/layout/Collection.js +1 -1
- package/dist/components/layout/DataTable.d.ts +29 -0
- package/dist/components/layout/DataTable.d.ts.map +1 -0
- package/dist/components/layout/DataTable.js +165 -0
- package/dist/components/layout/index.d.ts +2 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/layout/index.js +1 -0
- package/dist/components/utils/Avatar.d.ts +49 -0
- package/dist/components/utils/Avatar.d.ts.map +1 -0
- package/dist/components/utils/Avatar.js +93 -0
- package/dist/components/utils/Badge.d.ts +3 -0
- package/dist/components/utils/Badge.d.ts.map +1 -1
- package/dist/components/utils/Badge.js +130 -26
- package/dist/components/utils/Dialog.d.ts.map +1 -1
- package/dist/components/utils/Dialog.js +5 -1
- package/dist/components/utils/DropdownMenu.d.ts +25 -0
- package/dist/components/utils/DropdownMenu.d.ts.map +1 -0
- package/dist/components/utils/DropdownMenu.js +145 -0
- package/dist/components/utils/Filter.d.ts +57 -0
- package/dist/components/utils/Filter.d.ts.map +1 -0
- package/dist/components/utils/Filter.js +580 -0
- package/dist/components/utils/FiltersDialog.d.ts +21 -0
- package/dist/components/utils/FiltersDialog.d.ts.map +1 -0
- package/dist/components/utils/FiltersDialog.js +104 -0
- package/dist/components/utils/Loader.js +1 -1
- package/dist/components/utils/RoadMap.d.ts +59 -0
- package/dist/components/utils/RoadMap.d.ts.map +1 -0
- package/dist/components/utils/RoadMap.js +138 -0
- package/dist/components/utils/Snackbar.d.ts +13 -0
- package/dist/components/utils/Snackbar.d.ts.map +1 -0
- package/dist/components/utils/Snackbar.js +121 -0
- package/dist/components/utils/SnackbarContainer.d.ts +7 -0
- package/dist/components/utils/SnackbarContainer.d.ts.map +1 -0
- package/dist/components/utils/SnackbarContainer.js +25 -0
- package/dist/components/utils/index.d.ts +12 -0
- package/dist/components/utils/index.d.ts.map +1 -1
- package/dist/components/utils/index.js +6 -0
- package/dist/contexts/AppLayoutContext.d.ts +40 -0
- package/dist/contexts/AppLayoutContext.d.ts.map +1 -0
- package/dist/contexts/AppLayoutContext.js +98 -0
- package/dist/contexts/ListCrudContext.d.ts +29 -0
- package/dist/contexts/ListCrudContext.d.ts.map +1 -0
- package/dist/contexts/ListCrudContext.js +209 -0
- package/dist/contexts/SnackbarContext.d.ts +26 -0
- package/dist/contexts/SnackbarContext.d.ts.map +1 -0
- package/dist/contexts/SnackbarContext.js +34 -0
- package/dist/contexts/index.d.ts +6 -0
- package/dist/contexts/index.d.ts.map +1 -1
- package/dist/contexts/index.js +6 -0
- package/dist/contexts/presets.js +6 -6
- package/dist/docs/AuthDocs.tsx/AuthDocsContent.js +3 -1
- package/dist/docs/AvatarDocs.d.ts +4 -0
- package/dist/docs/AvatarDocs.d.ts.map +1 -0
- package/dist/docs/AvatarDocs.js +7 -0
- package/dist/docs/BadgeDocs.d.ts.map +1 -1
- package/dist/docs/BadgeDocs.js +4 -2
- package/dist/docs/CardDocs.d.ts.map +1 -1
- package/dist/docs/CardDocs.js +7 -1
- package/dist/docs/CheckboxDocs.d.ts +4 -0
- package/dist/docs/CheckboxDocs.d.ts.map +1 -0
- package/dist/docs/CheckboxDocs.js +7 -0
- package/dist/docs/DataTableDocs.d.ts +4 -0
- package/dist/docs/DataTableDocs.d.ts.map +1 -0
- package/dist/docs/DataTableDocs.js +244 -0
- package/dist/docs/DateInputDocs.d.ts +1 -0
- package/dist/docs/DateInputDocs.d.ts.map +1 -1
- package/dist/docs/DateInputDocs.js +7 -9
- package/dist/docs/DatePickerDocs.d.ts +1 -0
- package/dist/docs/DatePickerDocs.d.ts.map +1 -1
- package/dist/docs/DatePickerDocs.js +6 -8
- package/dist/docs/DocAdmin.d.ts +4 -0
- package/dist/docs/DocAdmin.d.ts.map +1 -0
- package/dist/docs/DocAdmin.js +68 -0
- package/dist/docs/DocsMenu.d.ts.map +1 -1
- package/dist/docs/DocsMenu.js +1 -1
- package/dist/docs/DocsRouter.d.ts.map +1 -1
- package/dist/docs/DocsRouter.js +13 -1
- package/dist/docs/DropdownMenuDocs.d.ts +4 -0
- package/dist/docs/DropdownMenuDocs.d.ts.map +1 -0
- package/dist/docs/DropdownMenuDocs.js +66 -0
- package/dist/docs/ExampleFormDocs.d.ts +4 -0
- package/dist/docs/ExampleFormDocs.d.ts.map +1 -0
- package/dist/docs/ExampleFormDocs.js +148 -0
- package/dist/docs/FilterDocs.d.ts +4 -0
- package/dist/docs/FilterDocs.d.ts.map +1 -0
- package/dist/docs/FilterDocs.js +112 -0
- package/dist/docs/InputDocs.d.ts.map +1 -1
- package/dist/docs/InputDocs.js +11 -1
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.d.ts +11 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.d.ts.map +1 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.js +25 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.d.ts +2 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.d.ts.map +1 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.js +51 -0
- package/dist/docs/PaginationDocs.js +6 -6
- package/dist/docs/RadioButtonGroupDocs.d.ts +4 -0
- package/dist/docs/RadioButtonGroupDocs.d.ts.map +1 -0
- package/dist/docs/RadioButtonGroupDocs.js +46 -0
- package/dist/docs/RoadMapDocs.d.ts +4 -0
- package/dist/docs/RoadMapDocs.d.ts.map +1 -0
- package/dist/docs/RoadMapDocs.js +171 -0
- package/dist/docs/SearchSelectInputDocs.d.ts +4 -0
- package/dist/docs/SearchSelectInputDocs.d.ts.map +1 -0
- package/dist/docs/SearchSelectInputDocs.js +168 -0
- package/dist/docs/SnackbarDocs.d.ts +4 -0
- package/dist/docs/SnackbarDocs.d.ts.map +1 -0
- package/dist/docs/SnackbarDocs.js +50 -0
- package/dist/docs/TabsGroupDocs.d.ts.map +1 -1
- package/dist/docs/TabsGroupDocs.js +12 -1
- package/dist/docs/docMockServices/empresaService.d.ts +38 -0
- package/dist/docs/docMockServices/empresaService.d.ts.map +1 -0
- package/dist/docs/docMockServices/empresaService.js +116 -0
- package/dist/docs/docMockServices/index.d.ts +9 -0
- package/dist/docs/docMockServices/index.d.ts.map +1 -0
- package/dist/docs/docMockServices/index.js +8 -0
- package/dist/docs/docMockServices/initialData.d.ts +6 -0
- package/dist/docs/docMockServices/initialData.d.ts.map +1 -0
- package/dist/docs/docMockServices/initialData.js +132 -0
- package/dist/docs/docMockServices/interfaces.d.ts +26 -0
- package/dist/docs/docMockServices/interfaces.d.ts.map +1 -0
- package/dist/docs/docMockServices/interfaces.js +1 -0
- package/dist/docs/docMockServices/personaEmpresaService.d.ts +43 -0
- package/dist/docs/docMockServices/personaEmpresaService.d.ts.map +1 -0
- package/dist/docs/docMockServices/personaEmpresaService.js +113 -0
- package/dist/docs/docMockServices/personaService.d.ts +39 -0
- package/dist/docs/docMockServices/personaService.d.ts.map +1 -0
- package/dist/docs/docMockServices/personaService.js +180 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useAsyncRequest.d.ts +17 -0
- package/dist/hooks/useAsyncRequest.d.ts.map +1 -0
- package/dist/hooks/useAsyncRequest.js +70 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
|
@@ -1,9 +1,20 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import React, { useState, useRef } from "react";
|
|
3
3
|
import { useBreakpoint } from "../../hooks";
|
|
4
4
|
import { useElementScroll } from "../../hooks/useElementScroll";
|
|
5
5
|
import { Button } from "../form-controls";
|
|
6
|
-
export const AppLayout = ({
|
|
6
|
+
export const AppLayout = ({ navbar, leftDrawer, children, className = "", }) => {
|
|
7
|
+
// Extract values from interfaces
|
|
8
|
+
const navBarLeftNode = navbar?.navBarLeftNode;
|
|
9
|
+
const navBarRightNode = navbar?.navBarRightNode;
|
|
10
|
+
const fullWidthNavbar = navbar?.fullWidthNavbar ?? true;
|
|
11
|
+
const navbarHeight = navbar?.height ?? "64px";
|
|
12
|
+
const navbarClassName = navbar?.className || "";
|
|
13
|
+
const leftDrawerHeader = leftDrawer?.headerNode;
|
|
14
|
+
const leftDrawerContent = leftDrawer?.contentNode;
|
|
15
|
+
const leftDrawerFooter = leftDrawer?.footerNode;
|
|
16
|
+
const leftDrawerClassName = leftDrawer?.className || "";
|
|
17
|
+
const leftDrawerWidth = leftDrawer?.width;
|
|
7
18
|
const { isMobile, isTablet } = useBreakpoint();
|
|
8
19
|
const contentRef = useRef(null);
|
|
9
20
|
const { scrollY, scrollDirection } = useElementScroll(contentRef);
|
|
@@ -12,8 +23,15 @@ export const AppLayout = ({ navBarDrawer, leftDrawer, children, className = "",
|
|
|
12
23
|
const isNavbarVisibleRef = useRef(isNavbarVisible);
|
|
13
24
|
const isTransitioningRef = useRef(false);
|
|
14
25
|
const lastScrollYRef = useRef(0);
|
|
26
|
+
// Determinar si hay algún contenido en el drawer izquierdo
|
|
27
|
+
const hasLeftDrawerContent = leftDrawerHeader || leftDrawerContent || leftDrawerFooter;
|
|
15
28
|
const shouldShowMobileDrawer = isMobile || isTablet;
|
|
16
|
-
const shouldShowDesktopDrawer = !shouldShowMobileDrawer &&
|
|
29
|
+
const shouldShowDesktopDrawer = !shouldShowMobileDrawer && hasLeftDrawerContent;
|
|
30
|
+
// Determinar si debemos mostrar el navbar
|
|
31
|
+
// Se muestra si hay navBarLeftNode o navBarRightNode o si estamos en móvil/tablet con contenido en el drawer
|
|
32
|
+
const shouldShowNavbar = navBarLeftNode ||
|
|
33
|
+
navBarRightNode ||
|
|
34
|
+
(shouldShowMobileDrawer && hasLeftDrawerContent);
|
|
17
35
|
// Mantener el ref sincronizado con el estado
|
|
18
36
|
React.useEffect(() => {
|
|
19
37
|
isNavbarVisibleRef.current = isNavbarVisible;
|
|
@@ -48,7 +66,9 @@ export const AppLayout = ({ navBarDrawer, leftDrawer, children, className = "",
|
|
|
48
66
|
// Siempre mostrar navbar cerca del top
|
|
49
67
|
shouldBeVisible = true;
|
|
50
68
|
}
|
|
51
|
-
else if (scrollDirection === "down" &&
|
|
69
|
+
else if (scrollDirection === "down" &&
|
|
70
|
+
scrollY > HIDE_THRESHOLD &&
|
|
71
|
+
!isNearBottom) {
|
|
52
72
|
// Ocultar navbar al hacer scroll hacia abajo, excepto si estamos cerca del final
|
|
53
73
|
shouldBeVisible = false;
|
|
54
74
|
}
|
|
@@ -87,36 +107,84 @@ export const AppLayout = ({ navBarDrawer, leftDrawer, children, className = "",
|
|
|
87
107
|
${className}
|
|
88
108
|
`;
|
|
89
109
|
// Clases del navbar
|
|
90
|
-
const navbarClasses =
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
110
|
+
const navbarClasses = `${fullWidthNavbar
|
|
111
|
+
? `z-[1000] fixed top-0 left-0 right-0 overflow-hidden`
|
|
112
|
+
: `relative z-[1000] overflow-hidden`} ${navbarClassName}`.trim();
|
|
113
|
+
// Estilos inline para la transformación
|
|
114
|
+
// Cuando fullWidthNavbar es false, solo usamos height para ocultar (sin transform)
|
|
115
|
+
// Cuando fullWidthNavbar es true, usamos transform para ocultar (manteniendo height)
|
|
116
|
+
const navbarStyle = fullWidthNavbar
|
|
117
|
+
? {
|
|
118
|
+
transform: isNavbarVisible ? "translateY(0)" : "translateY(-100%)",
|
|
119
|
+
transition: "transform 300ms ease-in-out",
|
|
120
|
+
willChange: "transform",
|
|
121
|
+
height: navbarHeight, // Override any height classes in className
|
|
122
|
+
}
|
|
123
|
+
: {
|
|
124
|
+
height: isNavbarVisible ? navbarHeight : "0",
|
|
125
|
+
minHeight: isNavbarVisible ? navbarHeight : "0",
|
|
126
|
+
maxHeight: isNavbarVisible ? navbarHeight : "0",
|
|
127
|
+
transition: "height 300ms ease-in-out, min-height 300ms ease-in-out, max-height 300ms ease-in-out",
|
|
128
|
+
overflow: "hidden",
|
|
129
|
+
};
|
|
130
|
+
const navbarContentClasses = `flex items-center justify-between gap-2`;
|
|
131
|
+
// Style for navbar content with dynamic height
|
|
132
|
+
// When fullWidthNavbar is false and hidden, set height to 0 to not occupy space
|
|
133
|
+
// When fullWidthNavbar is true, always maintain height to prevent layout shifts
|
|
134
|
+
const navbarContentStyle = {
|
|
135
|
+
height: fullWidthNavbar || isNavbarVisible ? navbarHeight : "0",
|
|
136
|
+
maxHeight: fullWidthNavbar || isNavbarVisible ? navbarHeight : "0",
|
|
137
|
+
overflow: "hidden",
|
|
138
|
+
transition: "height 300ms ease-in-out, max-height 300ms ease-in-out",
|
|
139
|
+
opacity: isNavbarVisible || fullWidthNavbar ? 1 : 0,
|
|
100
140
|
};
|
|
101
|
-
const
|
|
102
|
-
flex items-center
|
|
103
|
-
md:px-3
|
|
104
|
-
`;
|
|
105
|
-
const navbarDrawerClasses = `flex-1`;
|
|
141
|
+
const navbarLeftClasses = `flex items-center gap-2`;
|
|
142
|
+
const navbarRightClasses = `flex items-center gap-2`;
|
|
106
143
|
// Clases del contenido principal
|
|
107
|
-
const mainClasses = `
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
144
|
+
const mainClasses = `flex flex-1 overflow-hidden transition-all duration-300 ease-in-out`;
|
|
145
|
+
// Style for main content with dynamic navbar height padding
|
|
146
|
+
const mainStyle = fullWidthNavbar && shouldShowNavbar && isNavbarVisible
|
|
147
|
+
? { paddingTop: navbarHeight }
|
|
148
|
+
: {};
|
|
149
|
+
// Clases del drawer izquierdo (contenedor principal)
|
|
150
|
+
// width se aplica como estilo inline para tener prioridad sobre className
|
|
112
151
|
const leftDrawerClasses = `
|
|
113
|
-
w-64 bg-[var(--color-bg-default)]
|
|
114
|
-
|
|
152
|
+
${leftDrawerWidth ? "" : "w-64"} bg-[var(--color-bg-default)]
|
|
153
|
+
flex-shrink-0 flex flex-col
|
|
115
154
|
transition-all duration-300 ease-in-out
|
|
116
|
-
${
|
|
155
|
+
${fullWidthNavbar && shouldShowNavbar && isNavbarVisible ? "pt-4" : "h-full"}
|
|
156
|
+
${leftDrawerClassName}
|
|
157
|
+
`
|
|
158
|
+
.trim()
|
|
159
|
+
.replace(/\s+/g, " ");
|
|
160
|
+
// Style for left drawer with dynamic width
|
|
161
|
+
const leftDrawerStyle = leftDrawerWidth ? { width: leftDrawerWidth } : {};
|
|
162
|
+
// Clases del contenedor que incluye drawer y contenido (cuando fullWidthNavbar es false)
|
|
163
|
+
const contentWrapperClasses = fullWidthNavbar
|
|
164
|
+
? ""
|
|
165
|
+
: `flex flex-row flex-1 overflow-hidden`;
|
|
166
|
+
// Clases del contenedor que incluye navbar y main (cuando fullWidthNavbar es false)
|
|
167
|
+
const drawerAndContentClasses = fullWidthNavbar
|
|
168
|
+
? ""
|
|
169
|
+
: `flex flex-col flex-1 overflow-hidden`;
|
|
170
|
+
// Style for drawer and content wrapper with dynamic navbar height padding
|
|
171
|
+
const drawerAndContentStyle = !fullWidthNavbar && shouldShowNavbar && isNavbarVisible
|
|
172
|
+
? { paddingTop: 0 }
|
|
173
|
+
: {};
|
|
174
|
+
// Clases del header del drawer (fijo arriba)
|
|
175
|
+
const leftDrawerHeaderClasses = `
|
|
176
|
+
flex-shrink-0
|
|
177
|
+
`;
|
|
178
|
+
// Clases del contenido del drawer (scrolleable sin scrollbar visible)
|
|
179
|
+
const leftDrawerContentClasses = `
|
|
180
|
+
flex-1 overflow-y-auto scrollbar-hide
|
|
181
|
+
`;
|
|
182
|
+
// Clases del footer del drawer (fijo abajo)
|
|
183
|
+
const leftDrawerFooterClasses = `
|
|
184
|
+
flex-shrink-0
|
|
117
185
|
`;
|
|
118
186
|
const contentClasses = `
|
|
119
|
-
flex-1 overflow-y-auto px-2
|
|
187
|
+
flex-1 overflow-y-auto px-2 pb-4 lg:px-6 pt-4
|
|
120
188
|
`;
|
|
121
189
|
// Clases del overlay móvil
|
|
122
190
|
const overlayClasses = `
|
|
@@ -128,10 +196,15 @@ export const AppLayout = ({ navBarDrawer, leftDrawer, children, className = "",
|
|
|
128
196
|
bg-[var(--color-bg-default)] shadow-[var(--shadow-xl)]
|
|
129
197
|
transform -translate-x-full transition-transform duration-300 ease-in-out
|
|
130
198
|
z-[1999] flex flex-col
|
|
131
|
-
|
|
199
|
+
${leftDrawerClassName}
|
|
200
|
+
`
|
|
201
|
+
.trim()
|
|
202
|
+
.replace(/\s+/g, " ");
|
|
203
|
+
// Style for mobile drawer with dynamic width
|
|
204
|
+
const mobileDrawerStyle = leftDrawerWidth ? { width: leftDrawerWidth } : {};
|
|
132
205
|
const mobileDrawerOpenClasses = `translate-x-0`;
|
|
133
206
|
const mobileDrawerContentClasses = `
|
|
134
|
-
flex-1 overflow-y-auto
|
|
207
|
+
flex-1 overflow-y-auto scrollbar-hide
|
|
135
208
|
`;
|
|
136
|
-
return (_jsxs("div", { className: layoutClasses, children: [
|
|
209
|
+
return (_jsxs("div", { className: layoutClasses, children: [fullWidthNavbar ? (_jsxs(_Fragment, { children: [shouldShowNavbar && (_jsx("nav", { className: navbarClasses, style: navbarStyle, children: _jsxs("div", { className: navbarContentClasses, style: navbarContentStyle, children: [_jsxs("div", { className: navbarLeftClasses, children: [shouldShowMobileDrawer && hasLeftDrawerContent && (_jsx("div", { className: "pr-4 lg:px-4 md:px-3", children: _jsx(Button, { variant: "ghost", icon: "fa-bars", onClick: handleMobileDrawerToggle, "aria-label": "Abrir men\u00FA" }) })), navBarLeftNode && (_jsx("div", { children: typeof navBarLeftNode === "string" ? (_jsx("span", { children: navBarLeftNode })) : (navBarLeftNode) }))] }), navBarRightNode && (_jsx("div", { className: navbarRightClasses, children: typeof navBarRightNode === "string" ? (_jsx("span", { children: navBarRightNode })) : (navBarRightNode) }))] }) })), _jsxs("div", { className: mainClasses, style: mainStyle, children: [shouldShowDesktopDrawer && (_jsxs("aside", { className: leftDrawerClasses, style: leftDrawerStyle, children: [leftDrawerHeader && (_jsx("div", { className: leftDrawerHeaderClasses, children: leftDrawerHeader })), leftDrawerContent && (_jsx("div", { className: leftDrawerContentClasses, children: leftDrawerContent })), leftDrawerFooter && (_jsx("div", { className: leftDrawerFooterClasses, children: leftDrawerFooter }))] })), _jsx("main", { ref: contentRef, className: contentClasses, children: children })] })] })) : (_jsx(_Fragment, { children: _jsxs("div", { className: contentWrapperClasses, children: [shouldShowDesktopDrawer && (_jsxs("aside", { className: leftDrawerClasses, style: leftDrawerStyle, children: [leftDrawerHeader && (_jsx("div", { className: leftDrawerHeaderClasses, children: leftDrawerHeader })), leftDrawerContent && (_jsx("div", { className: leftDrawerContentClasses, children: leftDrawerContent })), leftDrawerFooter && (_jsx("div", { className: leftDrawerFooterClasses, children: leftDrawerFooter }))] })), _jsxs("div", { className: drawerAndContentClasses, style: drawerAndContentStyle, children: [shouldShowNavbar && (_jsx("nav", { className: navbarClasses, style: navbarStyle, children: _jsxs("div", { className: navbarContentClasses, style: navbarContentStyle, children: [_jsxs("div", { className: navbarLeftClasses, children: [shouldShowMobileDrawer && hasLeftDrawerContent && (_jsx("div", { className: "pr-4 lg:px-4 md:px-3", children: _jsx(Button, { variant: "ghost", icon: "fa-bars", onClick: handleMobileDrawerToggle, "aria-label": "Abrir men\u00FA" }) })), navBarLeftNode && (_jsx("div", { children: typeof navBarLeftNode === "string" ? (_jsx("span", { children: navBarLeftNode })) : (navBarLeftNode) }))] }), navBarRightNode && (_jsx("div", { className: navbarRightClasses, children: typeof navBarRightNode === "string" ? (_jsx("span", { children: navBarRightNode })) : (navBarRightNode) }))] }) })), _jsx("main", { ref: contentRef, className: contentClasses, children: children })] })] }) })), shouldShowMobileDrawer && hasLeftDrawerContent && isMobileDrawerOpen && (_jsx("div", { className: overlayClasses, onClick: handleOverlayClick })), shouldShowMobileDrawer && hasLeftDrawerContent && (_jsxs("aside", { className: `${mobileDrawerBaseClasses} ${isMobileDrawerOpen ? mobileDrawerOpenClasses : ""}`, style: mobileDrawerStyle, children: [_jsxs("div", { className: "flex-shrink-0 flex items-center justify-between", children: [leftDrawerHeader ? (_jsx("div", { className: "flex-1", children: leftDrawerHeader })) : (_jsx("div", { className: "flex-1" })), _jsx("div", { className: "p-4", children: _jsx(Button, { variant: "ghost", icon: "fa-times", onClick: handleMobileDrawerToggle, "aria-label": "Cerrar men\u00FA" }) })] }), leftDrawerContent && (_jsx("div", { className: mobileDrawerContentClasses, children: leftDrawerContent })), leftDrawerFooter && (_jsx("div", { className: "flex-shrink-0", children: leftDrawerFooter }))] }))] }));
|
|
137
210
|
};
|
|
@@ -4,7 +4,10 @@ export interface CardProps {
|
|
|
4
4
|
subtitle?: string | React.ReactNode;
|
|
5
5
|
children: React.ReactNode;
|
|
6
6
|
className?: string;
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Acciones para el header de la tarjeta. Retorna un array de ReactNode que se mostrarán en un DropdownMenu.
|
|
9
|
+
*/
|
|
10
|
+
headerActions?: () => Array<React.ReactNode>;
|
|
8
11
|
footer?: React.ReactNode;
|
|
9
12
|
variant?: "default" | "elevated" | "outlined";
|
|
10
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../../src/components/layout/Card.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Card.d.ts","sourceRoot":"","sources":["../../../src/components/layout/Card.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IACpC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC7C,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB,OAAO,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;CAC/C;AAED,eAAO,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,SAAS,CA6HpC,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React from "react";
|
|
3
|
+
import { DropdownMenu } from "../utils/DropdownMenu";
|
|
3
4
|
export const Card = ({ title, subtitle, children, className = "", headerActions, footer, variant = "default", }) => {
|
|
4
5
|
// Separar clases de background del className
|
|
5
6
|
const classArray = className.trim().split(/\s+/).filter(Boolean);
|
|
@@ -25,5 +26,33 @@ export const Card = ({ title, subtitle, children, className = "", headerActions,
|
|
|
25
26
|
outlined: `border-[var(--color-gray-300)]`,
|
|
26
27
|
};
|
|
27
28
|
const classes = `${baseClasses} ${variantClasses[variant]} ${otherClasses.join(" ")}`;
|
|
28
|
-
|
|
29
|
+
// Convertir array de ReactNode a array de ActionItem para DropdownMenu
|
|
30
|
+
const convertActionsToOptions = (actions) => {
|
|
31
|
+
return actions.map((action, index) => ({
|
|
32
|
+
id: index,
|
|
33
|
+
content: (_jsx("div", { onClick: (e) => {
|
|
34
|
+
// Detener la propagación para que el onClick del DropdownMenu no interfiera
|
|
35
|
+
e.stopPropagation();
|
|
36
|
+
}, children: action })),
|
|
37
|
+
}));
|
|
38
|
+
};
|
|
39
|
+
const headerActionsArray = headerActions?.();
|
|
40
|
+
const hasHeaderActions = headerActionsArray && headerActionsArray.length > 0;
|
|
41
|
+
const [isHovered, setIsHovered] = React.useState(false);
|
|
42
|
+
const [isLargeScreen, setIsLargeScreen] = React.useState(false);
|
|
43
|
+
React.useEffect(() => {
|
|
44
|
+
const checkScreenSize = () => {
|
|
45
|
+
setIsLargeScreen(window.innerWidth >= 1024);
|
|
46
|
+
};
|
|
47
|
+
checkScreenSize();
|
|
48
|
+
window.addEventListener("resize", checkScreenSize);
|
|
49
|
+
return () => {
|
|
50
|
+
window.removeEventListener("resize", checkScreenSize);
|
|
51
|
+
};
|
|
52
|
+
}, []);
|
|
53
|
+
return (_jsxs("div", { className: `${classes} relative`, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), children: [(title || subtitle || hasHeaderActions) && (_jsx("div", { className: "px-6 pt-4", children: _jsx("div", { className: "flex items-center justify-between", children: _jsxs("div", { children: [title && (_jsx("h3", { className: "text-lg font-semibold text-[var(--color-text-primary)]", children: title })), subtitle && (_jsx("p", { className: "text-sm text-[var(--color-text-secondary)] mt-1", children: subtitle }))] }) }) })), hasHeaderActions && (_jsx("div", { className: "absolute top-2 right-2 transition-opacity", style: {
|
|
54
|
+
opacity: isLargeScreen ? (isHovered ? 1 : 0) : 1,
|
|
55
|
+
}, children: _jsx(DropdownMenu, { options: convertActionsToOptions(headerActionsArray), onOptionSelected: () => {
|
|
56
|
+
// Las acciones ya manejan sus propios eventos
|
|
57
|
+
}, renderOption: (item) => item.content, replaceOnSingleOption: true }) })), _jsx("div", { className: "px-6 py-4", children: children }), footer && _jsx("div", { className: "px-6 pb-4", children: footer })] }));
|
|
29
58
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import React from "react";
|
|
3
|
-
export const Collection = ({ children, gap = "
|
|
3
|
+
export const Collection = ({ children, gap = "1rem", direction = "column", wrap = false, className = "", }) => {
|
|
4
4
|
const baseClasses = `
|
|
5
5
|
flex
|
|
6
6
|
font-[var(--font-default)]
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
isLoading?: boolean;
|
|
26
|
+
loadingRows?: number;
|
|
27
|
+
}
|
|
28
|
+
export declare const DataTable: <T>({ columns, rows, className, maxRows, locale, isLoading, loadingRows, }: DataTableProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
//# 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;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,SAAS,GAAI,CAAC,EAAG,wEAQ3B,cAAc,CAAC,CAAC,CAAC,4CAgTnB,CAAC"}
|
|
@@ -0,0 +1,165 @@
|
|
|
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", isLoading = false, loadingRows = 5, }) => {
|
|
5
|
+
// Calcular si necesitamos scroll
|
|
6
|
+
const displayRows = isLoading ? loadingRows : rows.length;
|
|
7
|
+
const needsScroll = maxRows !== undefined && displayRows > maxRows;
|
|
8
|
+
// Altura aproximada de una fila (px-4 py-3 = ~48px por fila)
|
|
9
|
+
const rowHeight = 48;
|
|
10
|
+
const maxHeight = maxRows ? `${maxRows * rowHeight}px` : undefined;
|
|
11
|
+
// Verificar si alguna columna tiene footer
|
|
12
|
+
const hasFooter = columns.some((column) => column.footer !== undefined);
|
|
13
|
+
const getCellValue = (column, row) => {
|
|
14
|
+
if (!column.value)
|
|
15
|
+
return null;
|
|
16
|
+
if (typeof column.value === "function") {
|
|
17
|
+
return column.value(row);
|
|
18
|
+
}
|
|
19
|
+
// Si es string o number, puede ser un nombre de propiedad o un valor directo
|
|
20
|
+
if (typeof column.value === "string" || typeof column.value === "number") {
|
|
21
|
+
// Intentar obtener la propiedad del objeto si existe
|
|
22
|
+
if (typeof column.value === "string" &&
|
|
23
|
+
typeof row === "object" &&
|
|
24
|
+
row !== null) {
|
|
25
|
+
const value = row[column.value];
|
|
26
|
+
if (value !== undefined) {
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Si no es una propiedad, retornar el valor directo
|
|
31
|
+
return column.value;
|
|
32
|
+
}
|
|
33
|
+
return column.value;
|
|
34
|
+
};
|
|
35
|
+
const formatValue = (value, type) => {
|
|
36
|
+
if (React.isValidElement(value)) {
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
// Convertir string a número si es necesario para currency o numeric
|
|
40
|
+
let numericValue = null;
|
|
41
|
+
if (typeof value === "number") {
|
|
42
|
+
numericValue = value;
|
|
43
|
+
}
|
|
44
|
+
else if (typeof value === "string" &&
|
|
45
|
+
(type === "currency" || type === "numeric")) {
|
|
46
|
+
const parsed = parseFloat(value);
|
|
47
|
+
if (!isNaN(parsed)) {
|
|
48
|
+
numericValue = parsed;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (numericValue !== null) {
|
|
52
|
+
if (type === "currency") {
|
|
53
|
+
// Formatear usando el locale proporcionado sin símbolo de moneda
|
|
54
|
+
const parts = new Intl.NumberFormat(locale, {
|
|
55
|
+
style: "currency",
|
|
56
|
+
currency: "EUR",
|
|
57
|
+
minimumFractionDigits: 2,
|
|
58
|
+
maximumFractionDigits: 2,
|
|
59
|
+
}).formatToParts(numericValue);
|
|
60
|
+
// Construir el string sin el símbolo de moneda
|
|
61
|
+
return parts
|
|
62
|
+
.filter((part) => part.type !== "currency")
|
|
63
|
+
.map((part) => part.value)
|
|
64
|
+
.join("");
|
|
65
|
+
}
|
|
66
|
+
if (type === "numeric") {
|
|
67
|
+
// Formatear usando el locale proporcionado
|
|
68
|
+
const hasDecimals = numericValue % 1 !== 0;
|
|
69
|
+
return new Intl.NumberFormat(locale, {
|
|
70
|
+
minimumFractionDigits: hasDecimals ? 2 : 0,
|
|
71
|
+
maximumFractionDigits: hasDecimals ? 2 : 0,
|
|
72
|
+
}).format(numericValue);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (typeof value === "string" && type === "date") {
|
|
76
|
+
try {
|
|
77
|
+
const date = new Date(value);
|
|
78
|
+
return date.toLocaleDateString(locale);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return value;
|
|
85
|
+
};
|
|
86
|
+
const getAlignmentClass = (align, type) => {
|
|
87
|
+
// Las columnas de tipo 'date' siempre se alinean a la izquierda
|
|
88
|
+
// Las columnas de tipo 'currency' y 'numeric' siempre se alinean a la derecha
|
|
89
|
+
let effectiveAlign = align;
|
|
90
|
+
if (type === "date") {
|
|
91
|
+
effectiveAlign = "left";
|
|
92
|
+
}
|
|
93
|
+
else if (type === "currency" || type === "numeric") {
|
|
94
|
+
effectiveAlign = "right";
|
|
95
|
+
}
|
|
96
|
+
switch (effectiveAlign) {
|
|
97
|
+
case "right":
|
|
98
|
+
return "text-right";
|
|
99
|
+
case "center":
|
|
100
|
+
return "text-center";
|
|
101
|
+
case "left":
|
|
102
|
+
default:
|
|
103
|
+
return "text-left";
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
// Convertir array de ReactNode a array de ActionItem para DropdownMenu
|
|
107
|
+
const convertActionsToOptions = (actions) => {
|
|
108
|
+
return actions.map((action, index) => ({
|
|
109
|
+
id: index,
|
|
110
|
+
content: (_jsx("div", { onClick: (e) => {
|
|
111
|
+
// Detener la propagación para que el onClick del DropdownMenu no interfiera
|
|
112
|
+
e.stopPropagation();
|
|
113
|
+
}, children: action })),
|
|
114
|
+
}));
|
|
115
|
+
};
|
|
116
|
+
// Componente Skeleton para celdas de carga
|
|
117
|
+
const SkeletonCell = () => (_jsx("div", { className: "h-4 bg-[var(--color-border-default)]/40 rounded animate-pulse w-full" }));
|
|
118
|
+
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) => {
|
|
119
|
+
const headerActions = column.headerActions?.();
|
|
120
|
+
const hasHeaderActions = headerActions && headerActions.length > 0;
|
|
121
|
+
return (_jsx("th", { className: `
|
|
122
|
+
px-4 py-3 text-sm font-semibold text-[var(--color-text-primary)]
|
|
123
|
+
bg-[var(--color-bg-secondary)]
|
|
124
|
+
${getAlignmentClass(column.align, column.type)}
|
|
125
|
+
${hasHeaderActions ? "relative" : ""}
|
|
126
|
+
`, style: {
|
|
127
|
+
...(column.width ? { width: column.width } : {}),
|
|
128
|
+
}, children: isLoading ? (_jsx(SkeletonCell, {})) : hasHeaderActions ? (_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("span", { children: column.header || "" }), _jsx(DropdownMenu, { options: convertActionsToOptions(headerActions), onOptionSelected: () => {
|
|
129
|
+
// Las acciones ya manejan sus propios eventos
|
|
130
|
+
}, renderOption: (item) => item.content, replaceOnSingleOption: true })] })) : (column.header || "") }, index));
|
|
131
|
+
}) }) }), _jsx("tbody", { children: isLoading
|
|
132
|
+
? Array.from({ length: loadingRows }).map((_, rowIndex) => (_jsx("tr", { className: "border-b border-[var(--color-border-default)]", children: columns.map((column, colIndex) => (_jsx("td", { className: `
|
|
133
|
+
px-4 py-3 text-sm text-[var(--color-text-primary)]
|
|
134
|
+
${getAlignmentClass(column.align, column.type)}
|
|
135
|
+
`, style: {
|
|
136
|
+
...(column.width ? { width: column.width } : {}),
|
|
137
|
+
}, children: _jsx(SkeletonCell, {}) }, colIndex))) }, `skeleton-${rowIndex}`)))
|
|
138
|
+
: rows.map((row, rowIndex) => (_jsx("tr", { className: "group/row border-b border-[var(--color-border-default)] transition-colors hover:bg-[var(--color-bg-secondary)]", children: columns.map((column, colIndex) => {
|
|
139
|
+
const cellValue = getCellValue(column, row);
|
|
140
|
+
const formattedValue = formatValue(cellValue, column.type);
|
|
141
|
+
const tooltip = column.tooltip
|
|
142
|
+
? column.tooltip(row)
|
|
143
|
+
: undefined;
|
|
144
|
+
const rowActions = column.actions?.(row);
|
|
145
|
+
const hasRowActions = rowActions && rowActions.length > 0;
|
|
146
|
+
return (_jsx("td", { className: `
|
|
147
|
+
px-4 py-3 text-sm text-[var(--color-text-primary)]
|
|
148
|
+
${getAlignmentClass(column.align, column.type)}
|
|
149
|
+
`, style: {
|
|
150
|
+
...(column.width ? { width: column.width } : {}),
|
|
151
|
+
}, title: tooltip
|
|
152
|
+
? typeof tooltip === "string"
|
|
153
|
+
? tooltip
|
|
154
|
+
: undefined
|
|
155
|
+
: 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: () => {
|
|
156
|
+
// Las acciones ya manejan sus propios eventos
|
|
157
|
+
}, renderOption: (item) => item.content, replaceOnSingleOption: true }) })] })) : (formattedValue) }, colIndex));
|
|
158
|
+
}) }, 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: `
|
|
159
|
+
px-4 py-3 text-sm font-semibold text-[var(--color-text-primary)]
|
|
160
|
+
bg-[var(--color-bg-secondary)]
|
|
161
|
+
${getAlignmentClass(column.align, column.type)}
|
|
162
|
+
`, style: {
|
|
163
|
+
...(column.width ? { width: column.width } : {}),
|
|
164
|
+
}, children: isLoading ? _jsx(SkeletonCell, {}) : column.footer || "" }, index))) }) }))] }) }) }));
|
|
165
|
+
};
|
|
@@ -10,4 +10,6 @@ export { TabsGroup } from "./TabsGroup";
|
|
|
10
10
|
export type { TabsGroupProps, Tab } from "./TabsGroup";
|
|
11
11
|
export { TabPanel } from "./TabPanel";
|
|
12
12
|
export type { TabPanelProps } from "./TabPanel";
|
|
13
|
+
export { DataTable } from "./DataTable";
|
|
14
|
+
export type { DataTableProps, DataTableColumn } from "./DataTable";
|
|
13
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/layout/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,YAAY,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/layout/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,YAAY,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface AvatarProps {
|
|
3
|
+
/**
|
|
4
|
+
* Text to extract initials from
|
|
5
|
+
* The component will show the first letter of the first word
|
|
6
|
+
* and the first letter of the last word if there are multiple words
|
|
7
|
+
*/
|
|
8
|
+
text: string;
|
|
9
|
+
/**
|
|
10
|
+
* Optional image URL to display instead of initials
|
|
11
|
+
*/
|
|
12
|
+
image?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Optional background color (hexadecimal, rgb, or color name)
|
|
15
|
+
* Default: gray-600 (#4b5563)
|
|
16
|
+
*/
|
|
17
|
+
bgColor?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Optional text color (hexadecimal, rgb, or color name)
|
|
20
|
+
* Default: white (#ffffff)
|
|
21
|
+
*/
|
|
22
|
+
textColor?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Optional size variant
|
|
25
|
+
* Default: md
|
|
26
|
+
*/
|
|
27
|
+
size?: "sm" | "md" | "lg";
|
|
28
|
+
/**
|
|
29
|
+
* Optional additional CSS classes
|
|
30
|
+
*/
|
|
31
|
+
className?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Avatar component displays a circular avatar with initials or an image
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* // Basic usage with text
|
|
38
|
+
* <Avatar text="John Doe" />
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // With image
|
|
42
|
+
* <Avatar text="John Doe" image="https://example.com/avatar.jpg" />
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // Custom colors
|
|
46
|
+
* <Avatar text="Jane Smith" bgColor="#3b82f6" textColor="#ffffff" />
|
|
47
|
+
*/
|
|
48
|
+
export declare const Avatar: React.FC<AvatarProps>;
|
|
49
|
+
//# sourceMappingURL=Avatar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Avatar.d.ts","sourceRoot":"","sources":["../../../src/components/utils/Avatar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAuDxC,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC1B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CA2DxC,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React, { useState } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Helper function to convert color names to CSS values
|
|
5
|
+
*/
|
|
6
|
+
const getColorValue = (color) => {
|
|
7
|
+
if (!color)
|
|
8
|
+
return undefined;
|
|
9
|
+
// If already a valid CSS value (hex, rgb, rgba, hsl, etc.), return it
|
|
10
|
+
if (color.startsWith("#") ||
|
|
11
|
+
color.startsWith("rgb") ||
|
|
12
|
+
color.startsWith("hsl")) {
|
|
13
|
+
return color;
|
|
14
|
+
}
|
|
15
|
+
// Map common color names
|
|
16
|
+
const colorMap = {
|
|
17
|
+
white: "#ffffff",
|
|
18
|
+
black: "#000000",
|
|
19
|
+
"gray-800": "#1f2937",
|
|
20
|
+
"gray-700": "#374151",
|
|
21
|
+
"gray-600": "#4b5563",
|
|
22
|
+
"gray-500": "#6b7280",
|
|
23
|
+
"gray-400": "#9ca3af",
|
|
24
|
+
"gray-300": "#d1d5db",
|
|
25
|
+
"gray-200": "#e5e7eb",
|
|
26
|
+
"gray-100": "#f3f4f6",
|
|
27
|
+
"gray-50": "#f9fafb",
|
|
28
|
+
};
|
|
29
|
+
return colorMap[color.toLowerCase()] || color;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Helper function to extract initials from text
|
|
33
|
+
* Returns first letter of first word and first letter of last word (if multiple words)
|
|
34
|
+
*/
|
|
35
|
+
const getInitials = (text) => {
|
|
36
|
+
if (!text || text.trim().length === 0)
|
|
37
|
+
return "";
|
|
38
|
+
const words = text.trim().split(/\s+/);
|
|
39
|
+
if (words.length === 0)
|
|
40
|
+
return "";
|
|
41
|
+
const firstLetter = words[0].charAt(0).toUpperCase();
|
|
42
|
+
if (words.length === 1) {
|
|
43
|
+
return firstLetter;
|
|
44
|
+
}
|
|
45
|
+
const lastLetter = words[words.length - 1].charAt(0).toUpperCase();
|
|
46
|
+
return `${firstLetter}${lastLetter}`;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Avatar component displays a circular avatar with initials or an image
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* // Basic usage with text
|
|
53
|
+
* <Avatar text="John Doe" />
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* // With image
|
|
57
|
+
* <Avatar text="John Doe" image="https://example.com/avatar.jpg" />
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* // Custom colors
|
|
61
|
+
* <Avatar text="Jane Smith" bgColor="#3b82f6" textColor="#ffffff" />
|
|
62
|
+
*/
|
|
63
|
+
export const Avatar = ({ text, image, bgColor = "gray-600", textColor = "white", size = "md", className = "", }) => {
|
|
64
|
+
const [imageError, setImageError] = useState(false);
|
|
65
|
+
const initials = getInitials(text);
|
|
66
|
+
const showImage = image && !imageError;
|
|
67
|
+
const sizeClasses = {
|
|
68
|
+
sm: "w-8 h-8 text-xs",
|
|
69
|
+
md: "w-10 h-10 text-sm",
|
|
70
|
+
lg: "w-12 h-12 text-base",
|
|
71
|
+
};
|
|
72
|
+
const baseClasses = `
|
|
73
|
+
rounded-full
|
|
74
|
+
flex
|
|
75
|
+
items-center
|
|
76
|
+
justify-center
|
|
77
|
+
font-semibold
|
|
78
|
+
font-[var(--font-default)]
|
|
79
|
+
overflow-hidden
|
|
80
|
+
flex-shrink-0
|
|
81
|
+
${sizeClasses[size]}
|
|
82
|
+
${className}
|
|
83
|
+
`;
|
|
84
|
+
// Inline styles for colors (only if no image or image failed to load)
|
|
85
|
+
const inlineStyles = showImage
|
|
86
|
+
? {}
|
|
87
|
+
: {
|
|
88
|
+
backgroundColor: getColorValue(bgColor) || bgColor || "#4b5563",
|
|
89
|
+
color: getColorValue(textColor) || textColor || "#ffffff",
|
|
90
|
+
};
|
|
91
|
+
return (_jsx("div", { className: baseClasses, style: inlineStyles, title: text, role: "img", "aria-label": text, children: showImage ? (_jsx("img", { src: image, alt: text, className: "w-full h-full object-cover", onError: () => setImageError(true) })) : (_jsx("span", { children: initials })) }));
|
|
92
|
+
};
|
|
93
|
+
Avatar.displayName = "Avatar";
|
|
@@ -8,6 +8,9 @@ export interface BadgeProps {
|
|
|
8
8
|
icon?: string;
|
|
9
9
|
iconPosition?: "left" | "right";
|
|
10
10
|
iconLabel?: string;
|
|
11
|
+
bg?: string;
|
|
12
|
+
textColor?: string;
|
|
13
|
+
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
|
11
14
|
}
|
|
12
15
|
export declare const Badge: React.FC<BadgeProps>;
|
|
13
16
|
//# sourceMappingURL=Badge.d.ts.map
|