flysoft-react-ui 1.2.3 → 1.2.5
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/AI_CONTEXT.md +1400 -217
- package/AI_INTEGRATION_GUIDE.md +343 -0
- package/INTEGRATION_GUIDE.md +60 -0
- package/README.md +5 -3
- package/dist/components/form-controls/Input.d.ts.map +1 -1
- package/dist/components/form-controls/index.d.ts +2 -2
- package/dist/components/form-controls/index.d.ts.map +1 -1
- package/dist/components/layout/Accordion.d.ts +1 -0
- package/dist/components/layout/Accordion.d.ts.map +1 -1
- package/dist/components/layout/DataTable.d.ts.map +1 -1
- package/dist/components/layout/DropdownMenu.d.ts +2 -1
- package/dist/components/layout/DropdownMenu.d.ts.map +1 -1
- package/dist/components/layout/DropdownPanel.d.ts +2 -1
- package/dist/components/layout/DropdownPanel.d.ts.map +1 -1
- package/dist/components/layout/Filter.d.ts +1 -0
- package/dist/components/layout/Filter.d.ts.map +1 -1
- package/dist/components/layout/Menu.d.ts +2 -1
- package/dist/components/layout/Menu.d.ts.map +1 -1
- package/dist/components/layout/TabsGroup.d.ts +1 -0
- package/dist/components/layout/TabsGroup.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11889 -24
- package/dist/index.js.map +1 -1
- package/dist/templates/forms/ContactForm.d.ts +1 -0
- package/dist/templates/forms/ContactForm.d.ts.map +1 -1
- package/dist/templates/forms/LoginForm.d.ts +1 -0
- package/dist/templates/forms/LoginForm.d.ts.map +1 -1
- package/dist/templates/forms/RegistrationForm.d.ts +1 -0
- package/dist/templates/forms/RegistrationForm.d.ts.map +1 -1
- package/dist/templates/layouts/DashboardLayout.d.ts +1 -0
- package/dist/templates/layouts/DashboardLayout.d.ts.map +1 -1
- package/dist/templates/layouts/SidebarLayout.d.ts +1 -0
- package/dist/templates/layouts/SidebarLayout.d.ts.map +1 -1
- package/dist/templates/patterns/FormPattern.d.ts +1 -0
- package/dist/templates/patterns/FormPattern.d.ts.map +1 -1
- package/dist/templates/patterns/ListPattern.d.ts +77 -0
- package/dist/templates/patterns/ListPattern.d.ts.map +1 -0
- package/package.json +6 -3
- package/dist/App.d.ts +0 -4
- package/dist/App.d.ts.map +0 -1
- package/dist/App.js +0 -30
- package/dist/components/ThemeSwitcher.js +0 -12
- package/dist/components/form-controls/AutocompleteInput.js +0 -680
- package/dist/components/form-controls/Button.js +0 -211
- package/dist/components/form-controls/Checkbox.js +0 -79
- package/dist/components/form-controls/CurrencyInput.js +0 -106
- package/dist/components/form-controls/DateInput.js +0 -578
- package/dist/components/form-controls/DatePicker.js +0 -144
- package/dist/components/form-controls/Input.js +0 -35
- package/dist/components/form-controls/LinkButton.js +0 -248
- package/dist/components/form-controls/Pagination.js +0 -23
- package/dist/components/form-controls/RadioButtonGroup.js +0 -220
- package/dist/components/form-controls/SearchSelectInput-OLD.d.ts +0 -68
- package/dist/components/form-controls/SearchSelectInput-OLD.d.ts.map +0 -1
- package/dist/components/form-controls/SearchSelectInput-OLD.js +0 -962
- package/dist/components/form-controls/SearchSelectInput.js +0 -336
- package/dist/components/form-controls/index.js +0 -11
- package/dist/components/index.js +0 -7
- package/dist/components/layout/Accordion.js +0 -67
- package/dist/components/layout/AppLayout.js +0 -230
- package/dist/components/layout/Card.js +0 -54
- package/dist/components/layout/Collection.js +0 -18
- package/dist/components/layout/DataField.js +0 -38
- package/dist/components/layout/DataTable.js +0 -164
- package/dist/components/layout/DropdownMenu.js +0 -176
- package/dist/components/layout/DropdownPanel.js +0 -162
- package/dist/components/layout/Filter.js +0 -629
- package/dist/components/layout/Menu.js +0 -21
- package/dist/components/layout/TabPanel.js +0 -11
- package/dist/components/layout/TabsGroup.js +0 -52
- package/dist/components/layout/index.js +0 -12
- package/dist/components/utils/Avatar.js +0 -77
- package/dist/components/utils/Badge.js +0 -151
- package/dist/components/utils/Dialog.js +0 -44
- package/dist/components/utils/FiltersDialog.js +0 -104
- package/dist/components/utils/Loader.js +0 -44
- package/dist/components/utils/RoadMap.js +0 -139
- package/dist/components/utils/Skeleton.js +0 -10
- package/dist/components/utils/Snackbar.js +0 -136
- package/dist/components/utils/SnackbarContainer.js +0 -26
- package/dist/components/utils/iconUtils.js +0 -40
- package/dist/components/utils/index.js +0 -9
- package/dist/contexts/AppLayoutContext.js +0 -104
- package/dist/contexts/AuthContext.js +0 -224
- package/dist/contexts/CrudContext.js +0 -333
- package/dist/contexts/SnackbarContext.js +0 -41
- package/dist/contexts/ThemeContext.js +0 -197
- package/dist/contexts/index.js +0 -13
- package/dist/contexts/presets.js +0 -311
- package/dist/contexts/types.js +0 -1
- package/dist/docs/AccordionDocs.d.ts +0 -4
- package/dist/docs/AccordionDocs.d.ts.map +0 -1
- package/dist/docs/AccordionDocs.js +0 -21
- package/dist/docs/AuthDocs.tsx/AuthDocs.d.ts +0 -13
- package/dist/docs/AuthDocs.tsx/AuthDocs.d.ts.map +0 -1
- package/dist/docs/AuthDocs.tsx/AuthDocs.js +0 -18
- package/dist/docs/AuthDocs.tsx/AuthDocsContent.d.ts +0 -2
- package/dist/docs/AuthDocs.tsx/AuthDocsContent.d.ts.map +0 -1
- package/dist/docs/AuthDocs.tsx/AuthDocsContent.js +0 -22
- package/dist/docs/AuthDocs.tsx/mockAuthService.d.ts +0 -24
- package/dist/docs/AuthDocs.tsx/mockAuthService.d.ts.map +0 -1
- package/dist/docs/AuthDocs.tsx/mockAuthService.js +0 -78
- package/dist/docs/AutocompleteInputDocs.d.ts +0 -4
- package/dist/docs/AutocompleteInputDocs.d.ts.map +0 -1
- package/dist/docs/AutocompleteInputDocs.js +0 -84
- package/dist/docs/AvatarDocs.d.ts +0 -4
- package/dist/docs/AvatarDocs.d.ts.map +0 -1
- package/dist/docs/AvatarDocs.js +0 -7
- package/dist/docs/BadgeDocs.d.ts +0 -4
- package/dist/docs/BadgeDocs.d.ts.map +0 -1
- package/dist/docs/BadgeDocs.js +0 -9
- package/dist/docs/ButtonDocs.d.ts +0 -4
- package/dist/docs/ButtonDocs.d.ts.map +0 -1
- package/dist/docs/ButtonDocs.js +0 -7
- package/dist/docs/CardDocs.d.ts +0 -4
- package/dist/docs/CardDocs.d.ts.map +0 -1
- package/dist/docs/CardDocs.js +0 -22
- package/dist/docs/CheckboxDocs.d.ts +0 -4
- package/dist/docs/CheckboxDocs.d.ts.map +0 -1
- package/dist/docs/CheckboxDocs.js +0 -7
- package/dist/docs/CurrencyInputDocs.d.ts +0 -4
- package/dist/docs/CurrencyInputDocs.d.ts.map +0 -1
- package/dist/docs/CurrencyInputDocs.js +0 -22
- package/dist/docs/DataFieldDocs.d.ts +0 -4
- package/dist/docs/DataFieldDocs.d.ts.map +0 -1
- package/dist/docs/DataFieldDocs.js +0 -7
- package/dist/docs/DataTableDocs.d.ts +0 -4
- package/dist/docs/DataTableDocs.d.ts.map +0 -1
- package/dist/docs/DataTableDocs.js +0 -244
- package/dist/docs/DateInputDocs.d.ts +0 -5
- package/dist/docs/DateInputDocs.d.ts.map +0 -1
- package/dist/docs/DateInputDocs.js +0 -19
- package/dist/docs/DatePickerDocs.d.ts +0 -5
- package/dist/docs/DatePickerDocs.d.ts.map +0 -1
- package/dist/docs/DatePickerDocs.js +0 -16
- package/dist/docs/DialogDocs.d.ts +0 -4
- package/dist/docs/DialogDocs.d.ts.map +0 -1
- package/dist/docs/DialogDocs.js +0 -13
- package/dist/docs/DocAdmin.d.ts +0 -4
- package/dist/docs/DocAdmin.d.ts.map +0 -1
- package/dist/docs/DocAdmin.js +0 -68
- package/dist/docs/DocsMenu.d.ts +0 -2
- package/dist/docs/DocsMenu.d.ts.map +0 -1
- package/dist/docs/DocsMenu.js +0 -5
- package/dist/docs/DocsRouter.d.ts +0 -4
- package/dist/docs/DocsRouter.d.ts.map +0 -1
- package/dist/docs/DocsRouter.js +0 -39
- package/dist/docs/DropdownMenuDocs.d.ts +0 -4
- package/dist/docs/DropdownMenuDocs.d.ts.map +0 -1
- package/dist/docs/DropdownMenuDocs.js +0 -66
- package/dist/docs/DropdownPanelDocs.d.ts +0 -4
- package/dist/docs/DropdownPanelDocs.d.ts.map +0 -1
- package/dist/docs/DropdownPanelDocs.js +0 -7
- package/dist/docs/ExampleFormDocs.d.ts +0 -4
- package/dist/docs/ExampleFormDocs.d.ts.map +0 -1
- package/dist/docs/ExampleFormDocs.js +0 -153
- package/dist/docs/FilterDocs.d.ts +0 -4
- package/dist/docs/FilterDocs.d.ts.map +0 -1
- package/dist/docs/FilterDocs.js +0 -130
- package/dist/docs/InputDocs.d.ts +0 -4
- package/dist/docs/InputDocs.d.ts.map +0 -1
- package/dist/docs/InputDocs.js +0 -17
- package/dist/docs/LinkButtonDocs.d.ts +0 -4
- package/dist/docs/LinkButtonDocs.d.ts.map +0 -1
- package/dist/docs/LinkButtonDocs.js +0 -7
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.d.ts +0 -2
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.d.ts.map +0 -1
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.js +0 -47
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaPersonas.d.ts +0 -2
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaPersonas.d.ts.map +0 -1
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaPersonas.js +0 -34
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaSingle.d.ts +0 -2
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaSingle.d.ts.map +0 -1
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresaSingle.js +0 -66
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresas.d.ts +0 -2
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresas.d.ts.map +0 -1
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresas.js +0 -7
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresasPersonasEditDialog.d.ts +0 -10
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresasPersonasEditDialog.d.ts.map +0 -1
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresasPersonasEditDialog.js +0 -39
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.d.ts +0 -2
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.d.ts.map +0 -1
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.js +0 -57
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsEditDialog.d.ts +0 -9
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsEditDialog.d.ts.map +0 -1
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsEditDialog.js +0 -30
- package/dist/docs/LoaderDocs.d.ts +0 -4
- package/dist/docs/LoaderDocs.d.ts.map +0 -1
- package/dist/docs/LoaderDocs.js +0 -33
- package/dist/docs/MenuDocs.d.ts +0 -4
- package/dist/docs/MenuDocs.d.ts.map +0 -1
- package/dist/docs/MenuDocs.js +0 -26
- package/dist/docs/PaginationDocs.d.ts +0 -4
- package/dist/docs/PaginationDocs.d.ts.map +0 -1
- package/dist/docs/PaginationDocs.js +0 -38
- package/dist/docs/RadioButtonGroupDocs.d.ts +0 -4
- package/dist/docs/RadioButtonGroupDocs.d.ts.map +0 -1
- package/dist/docs/RadioButtonGroupDocs.js +0 -46
- package/dist/docs/RoadMapDocs.d.ts +0 -4
- package/dist/docs/RoadMapDocs.d.ts.map +0 -1
- package/dist/docs/RoadMapDocs.js +0 -171
- package/dist/docs/SearchSelectInputDocs.d.ts +0 -4
- package/dist/docs/SearchSelectInputDocs.d.ts.map +0 -1
- package/dist/docs/SearchSelectInputDocs.js +0 -168
- package/dist/docs/SkeletonDocs.d.ts +0 -4
- package/dist/docs/SkeletonDocs.d.ts.map +0 -1
- package/dist/docs/SkeletonDocs.js +0 -7
- package/dist/docs/SnackbarDocs.d.ts +0 -4
- package/dist/docs/SnackbarDocs.d.ts.map +0 -1
- package/dist/docs/SnackbarDocs.js +0 -69
- package/dist/docs/TabsGroupDocs.d.ts +0 -4
- package/dist/docs/TabsGroupDocs.d.ts.map +0 -1
- package/dist/docs/TabsGroupDocs.js +0 -38
- package/dist/docs/ThemeSwitcherDocs.d.ts +0 -4
- package/dist/docs/ThemeSwitcherDocs.d.ts.map +0 -1
- package/dist/docs/ThemeSwitcherDocs.js +0 -11
- package/dist/docs/docMockServices/empresaService.d.ts +0 -38
- package/dist/docs/docMockServices/empresaService.d.ts.map +0 -1
- package/dist/docs/docMockServices/empresaService.js +0 -125
- package/dist/docs/docMockServices/index.d.ts +0 -9
- package/dist/docs/docMockServices/index.d.ts.map +0 -1
- package/dist/docs/docMockServices/index.js +0 -8
- package/dist/docs/docMockServices/initialData.d.ts +0 -6
- package/dist/docs/docMockServices/initialData.d.ts.map +0 -1
- package/dist/docs/docMockServices/initialData.js +0 -132
- package/dist/docs/docMockServices/interfaces.d.ts +0 -38
- package/dist/docs/docMockServices/interfaces.d.ts.map +0 -1
- package/dist/docs/docMockServices/interfaces.js +0 -1
- package/dist/docs/docMockServices/personaEmpresaService.d.ts +0 -43
- package/dist/docs/docMockServices/personaEmpresaService.d.ts.map +0 -1
- package/dist/docs/docMockServices/personaEmpresaService.js +0 -151
- package/dist/docs/docMockServices/personaService.d.ts +0 -39
- package/dist/docs/docMockServices/personaService.d.ts.map +0 -1
- package/dist/docs/docMockServices/personaService.js +0 -190
- package/dist/helpers/currencyFormat.js +0 -3
- package/dist/helpers/getErrorMessage.js +0 -13
- package/dist/helpers/getInitialLetters.js +0 -5
- package/dist/helpers/getQueryString.js +0 -13
- package/dist/helpers/index.js +0 -9
- package/dist/helpers/mappers.js +0 -27
- package/dist/helpers/nameValueArrayToObject.js +0 -3
- package/dist/helpers/objectToQueryString.js +0 -3
- package/dist/helpers/queryStringToObject.js +0 -13
- package/dist/helpers/regularExpressions.js +0 -5
- package/dist/hooks/index.js +0 -6
- package/dist/hooks/useAsyncRequest.js +0 -53
- package/dist/hooks/useBreakpoint.js +0 -59
- package/dist/hooks/useElementScroll.js +0 -58
- package/dist/hooks/useEnum.js +0 -21
- package/dist/hooks/useGlobalThemeStyles.js +0 -40
- package/dist/hooks/useThemeOverride.js +0 -99
- package/dist/interfaces/index.js +0 -1
- package/dist/interfaces/name-value.interface.js +0 -1
- package/dist/interfaces/pagination.interface.js +0 -1
- package/dist/main.d.ts +0 -2
- package/dist/main.d.ts.map +0 -1
- package/dist/main.js +0 -6
- package/dist/services/apiClient.js +0 -216
- package/dist/services/index.js +0 -1
- package/dist/styles.d.ts +0 -2
- package/dist/styles.d.ts.map +0 -1
- package/dist/styles.js +0 -3
- package/dist/templates/forms/ContactForm.js +0 -58
- package/dist/templates/forms/LoginForm.js +0 -36
- package/dist/templates/forms/RegistrationForm.js +0 -54
- package/dist/templates/layouts/DashboardLayout.js +0 -26
- package/dist/templates/layouts/SidebarLayout.js +0 -28
- package/dist/templates/patterns/FormPattern.js +0 -68
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import React, { useState, useMemo, useRef, useCallback, useEffect, } from "react";
|
|
3
|
-
import { useFormContext } from "react-hook-form";
|
|
4
|
-
import { Input } from "./Input";
|
|
5
|
-
import { Button } from "./Button";
|
|
6
|
-
import { Dialog, Loader } from "../utils";
|
|
7
|
-
import { normalizeIconClass } from "../utils/iconUtils";
|
|
8
|
-
const SearchSelectInputInner = React.forwardRef(function SearchSelectInput({ value, onChange, onSearchPromiseFn, onSingleSearchPromiseFn, onSelectOption, dialogTitle = "Seleccione una opción", icon = "fa-search", iconPosition = "right", noResultsText = "Sin resultados", getOptionLabel, getOptionValue, getOptionDescription, renderOption, label, readOnly = false, ...inputProps }, ref) {
|
|
9
|
-
const [inputText, setInputText] = useState("");
|
|
10
|
-
const [dialogInputText, setDialogInputText] = useState("");
|
|
11
|
-
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
|
12
|
-
const [options, setOptions] = useState([]);
|
|
13
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
14
|
-
const [hasSearched, setHasSearched] = useState(false);
|
|
15
|
-
const inputRef = useRef(null);
|
|
16
|
-
const dialogInputRef = useRef(null);
|
|
17
|
-
const justClearedRef = useRef(false);
|
|
18
|
-
// Detectar modo register
|
|
19
|
-
const isRegisterMode = useMemo(() => {
|
|
20
|
-
return "name" in inputProps && inputProps.name !== undefined;
|
|
21
|
-
}, [inputProps]);
|
|
22
|
-
const fieldName = isRegisterMode && "name" in inputProps
|
|
23
|
-
? inputProps.name
|
|
24
|
-
: undefined;
|
|
25
|
-
// Obtener setValue del contexto del formulario
|
|
26
|
-
// Para usar con register, el formulario debe estar dentro de FormProvider
|
|
27
|
-
// useFormContext debe llamarse incondicionalmente (requisito de React Hooks)
|
|
28
|
-
// Si no hay FormProvider y se usa en modo register, useFormContext lanzará un error
|
|
29
|
-
// Para usar sin FormProvider, usar Controller en lugar de register
|
|
30
|
-
const formContext = useFormContext();
|
|
31
|
-
const setValue = formContext?.setValue;
|
|
32
|
-
// Combinar refs
|
|
33
|
-
const combinedRef = useCallback((node) => {
|
|
34
|
-
inputRef.current = node;
|
|
35
|
-
if (typeof ref === "function") {
|
|
36
|
-
ref(node);
|
|
37
|
-
}
|
|
38
|
-
else if (ref) {
|
|
39
|
-
ref.current = node;
|
|
40
|
-
}
|
|
41
|
-
}, [ref]);
|
|
42
|
-
const valueGetter = useCallback((item) => {
|
|
43
|
-
if (getOptionValue)
|
|
44
|
-
return getOptionValue(item);
|
|
45
|
-
return item["value"];
|
|
46
|
-
}, [getOptionValue]);
|
|
47
|
-
const labelGetter = useCallback((item) => {
|
|
48
|
-
if (getOptionLabel)
|
|
49
|
-
return getOptionLabel(item);
|
|
50
|
-
return item["label"];
|
|
51
|
-
}, [getOptionLabel]);
|
|
52
|
-
const descriptionGetter = useCallback((item) => {
|
|
53
|
-
if (getOptionDescription)
|
|
54
|
-
return getOptionDescription(item);
|
|
55
|
-
return item["description"];
|
|
56
|
-
}, [getOptionDescription]);
|
|
57
|
-
const handleSearch = async (text) => {
|
|
58
|
-
setIsLoading(true);
|
|
59
|
-
setHasSearched(true);
|
|
60
|
-
const options = await onSearchPromiseFn(text);
|
|
61
|
-
if (options instanceof Array) {
|
|
62
|
-
setOptions(options);
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
setOptions(options.list);
|
|
66
|
-
}
|
|
67
|
-
setIsLoading(false);
|
|
68
|
-
};
|
|
69
|
-
const handleSelect = (option) => {
|
|
70
|
-
const selectedValue = valueGetter(option);
|
|
71
|
-
setIsDialogOpen(false);
|
|
72
|
-
// En modo register, setear el valor usando setValue o actualizando el input nativo
|
|
73
|
-
if (isRegisterMode) {
|
|
74
|
-
if (setValue && fieldName) {
|
|
75
|
-
setValue(fieldName, selectedValue, {
|
|
76
|
-
shouldValidate: true,
|
|
77
|
-
shouldDirty: true,
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
else if (inputRef.current) {
|
|
81
|
-
const nativeInput = inputRef.current;
|
|
82
|
-
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set;
|
|
83
|
-
if (nativeInputValueSetter) {
|
|
84
|
-
nativeInputValueSetter.call(nativeInput, String(selectedValue ?? ""));
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
nativeInput.value = String(selectedValue ?? "");
|
|
88
|
-
}
|
|
89
|
-
if (onChange) {
|
|
90
|
-
const changeEvent = {
|
|
91
|
-
target: nativeInput,
|
|
92
|
-
currentTarget: nativeInput,
|
|
93
|
-
};
|
|
94
|
-
onChange(changeEvent);
|
|
95
|
-
}
|
|
96
|
-
const inputEvent = new Event("input", {
|
|
97
|
-
bubbles: true,
|
|
98
|
-
cancelable: true,
|
|
99
|
-
});
|
|
100
|
-
nativeInput.dispatchEvent(inputEvent);
|
|
101
|
-
const changeEventNative = new Event("change", {
|
|
102
|
-
bubbles: true,
|
|
103
|
-
cancelable: true,
|
|
104
|
-
});
|
|
105
|
-
nativeInput.dispatchEvent(changeEventNative);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
onChange?.(selectedValue);
|
|
110
|
-
}
|
|
111
|
-
onSelectOption?.(option, selectedValue);
|
|
112
|
-
setInputText(labelGetter(option));
|
|
113
|
-
setDialogInputText("");
|
|
114
|
-
setOptions([]);
|
|
115
|
-
setHasSearched(false);
|
|
116
|
-
};
|
|
117
|
-
// Función para sincronizar inputText con un valor
|
|
118
|
-
const syncInputText = useCallback((currentValue) => {
|
|
119
|
-
if (currentValue === undefined ||
|
|
120
|
-
currentValue === null ||
|
|
121
|
-
currentValue === "") {
|
|
122
|
-
setInputText("");
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
// Si currentValue es un objeto (T) y tenemos getOptionLabel, usar directamente
|
|
126
|
-
if (typeof currentValue === "object" &&
|
|
127
|
-
getOptionLabel &&
|
|
128
|
-
!getOptionValue) {
|
|
129
|
-
try {
|
|
130
|
-
const label = getOptionLabel(currentValue);
|
|
131
|
-
setInputText(label);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
catch {
|
|
135
|
-
// Si falla, continuar con la búsqueda normal
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
// Normalizar el value: si es un objeto (T), extraer el valor usando valueGetter
|
|
139
|
-
let valueToSearch = currentValue;
|
|
140
|
-
if (typeof currentValue === "object" && getOptionValue) {
|
|
141
|
-
// Si currentValue es un objeto y tenemos getOptionValue, extraer el valor
|
|
142
|
-
try {
|
|
143
|
-
valueToSearch = getOptionValue(currentValue);
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
// Si falla, no podemos usar onSingleSearchPromiseFn con un objeto
|
|
147
|
-
// Intentar mostrar el label directamente si está disponible
|
|
148
|
-
if (getOptionLabel) {
|
|
149
|
-
try {
|
|
150
|
-
setInputText(getOptionLabel(currentValue));
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
catch {
|
|
154
|
-
setInputText(String(currentValue));
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
setInputText(String(currentValue));
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
// Buscar en las opciones actuales
|
|
163
|
-
const matchingOption = options.find((opt) => valueGetter(opt) === valueToSearch ||
|
|
164
|
-
valueGetter(opt) === currentValue);
|
|
165
|
-
if (matchingOption) {
|
|
166
|
-
setInputText(labelGetter(matchingOption));
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
// Si no se encuentra en las opciones actuales, usar onSingleSearchPromiseFn
|
|
170
|
-
if (onSingleSearchPromiseFn &&
|
|
171
|
-
valueToSearch !== undefined &&
|
|
172
|
-
valueToSearch !== null) {
|
|
173
|
-
onSingleSearchPromiseFn(valueToSearch)
|
|
174
|
-
.then((foundOption) => {
|
|
175
|
-
if (foundOption) {
|
|
176
|
-
setInputText(labelGetter(foundOption));
|
|
177
|
-
// Agregar la opción a las opciones disponibles si no está ya
|
|
178
|
-
setOptions((prev) => {
|
|
179
|
-
if (!prev.find((opt) => valueGetter(opt) === valueGetter(foundOption))) {
|
|
180
|
-
return [...prev, foundOption];
|
|
181
|
-
}
|
|
182
|
-
return prev;
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
// Si no se encuentra, mostrar el valor como string
|
|
187
|
-
setInputText(String(valueToSearch));
|
|
188
|
-
}
|
|
189
|
-
})
|
|
190
|
-
.catch((error) => {
|
|
191
|
-
console.error("Error al buscar opción individual:", error);
|
|
192
|
-
setInputText(String(valueToSearch));
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
// Si no hay onSingleSearchPromiseFn, mostrar el valor como string
|
|
197
|
-
setInputText(String(valueToSearch));
|
|
198
|
-
}
|
|
199
|
-
}, [
|
|
200
|
-
options,
|
|
201
|
-
getOptionValue,
|
|
202
|
-
getOptionLabel,
|
|
203
|
-
onSingleSearchPromiseFn,
|
|
204
|
-
valueGetter,
|
|
205
|
-
labelGetter,
|
|
206
|
-
]);
|
|
207
|
-
// Sincronizar inputText cuando cambia el value (modo controlado)
|
|
208
|
-
useEffect(() => {
|
|
209
|
-
if (!isRegisterMode) {
|
|
210
|
-
syncInputText(value);
|
|
211
|
-
}
|
|
212
|
-
}, [value, isRegisterMode, syncInputText]);
|
|
213
|
-
// Sincronizar inputText cuando cambia el valor del formulario (modo register)
|
|
214
|
-
useEffect(() => {
|
|
215
|
-
if (isRegisterMode && formContext && fieldName) {
|
|
216
|
-
// Sincronizar inicialmente
|
|
217
|
-
const formValue = formContext.watch(fieldName);
|
|
218
|
-
syncInputText(formValue);
|
|
219
|
-
// Suscribirse a cambios del formulario
|
|
220
|
-
const subscription = formContext.watch((_data, { name }) => {
|
|
221
|
-
// Solo sincronizar cuando cambia el campo específico
|
|
222
|
-
if (name === fieldName) {
|
|
223
|
-
const currentFormValue = formContext.watch(fieldName);
|
|
224
|
-
syncInputText(currentFormValue);
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
return () => subscription.unsubscribe();
|
|
228
|
-
}
|
|
229
|
-
}, [isRegisterMode, formContext, fieldName, syncInputText]);
|
|
230
|
-
// Hacer blur en el input del dialog cuando se abre
|
|
231
|
-
useEffect(() => {
|
|
232
|
-
if (isDialogOpen) {
|
|
233
|
-
// El Dialog renderiza condicionalmente, así que necesitamos esperar a que el input esté montado
|
|
234
|
-
// Usar requestAnimationFrame doble para asegurar que el DOM esté completamente renderizado
|
|
235
|
-
const timeoutId = setTimeout(() => {
|
|
236
|
-
requestAnimationFrame(() => {
|
|
237
|
-
dialogInputRef.current?.focus();
|
|
238
|
-
});
|
|
239
|
-
}, 50);
|
|
240
|
-
return () => clearTimeout(timeoutId);
|
|
241
|
-
}
|
|
242
|
-
}, [isDialogOpen]);
|
|
243
|
-
const getDialogBody = () => {
|
|
244
|
-
return (_jsxs("div", { children: [_jsx("div", { className: "mb-2", children: _jsx(Input, { ref: dialogInputRef, value: dialogInputText, onChange: (e) => setDialogInputText(e.target.value), icon: icon, iconPosition: iconPosition, onIconClick: () => handleSearch(dialogInputText), onKeyDown: (e) => {
|
|
245
|
-
if (e.key === "Enter") {
|
|
246
|
-
e.preventDefault();
|
|
247
|
-
handleSearch(dialogInputText);
|
|
248
|
-
}
|
|
249
|
-
} }) }), _jsx("div", { children: _jsx(Loader, { isLoading: isLoading, children: !hasSearched || options.length > 0 ? (_jsx("ul", { className: "space-y-1 max-h-96 overflow-y-auto list-none pl-0 m-0", children: options.map((option, index) => {
|
|
250
|
-
const label = labelGetter(option);
|
|
251
|
-
const description = descriptionGetter(option);
|
|
252
|
-
const anyOption = option;
|
|
253
|
-
return (_jsx("li", { className: "px-3 py-2 cursor-pointer rounded-md flex items-start gap-2 text-sm\r\n text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] transition-colors", onClick: () => handleSelect(option), children: renderOption ? (renderOption(option)) : (_jsxs(_Fragment, { children: [anyOption.icon && (_jsx("i", { className: `${normalizeIconClass(anyOption.icon)} mt-0.5 text-[var(--color-text-muted)]` })), _jsxs("div", { className: "flex flex-col flex-1", children: [_jsx("span", { className: "font-[var(--font-default)]", children: label }), description !== undefined &&
|
|
254
|
-
description !== null && (_jsx("span", { className: "text-xs text-[var(--color-text-secondary)]", children: description }))] })] })) }, String(valueGetter(option) ?? label ?? index)));
|
|
255
|
-
}) })) : (_jsx("div", { className: "px-3 py-8 text-center text-sm text-[var(--color-text-secondary)]", children: noResultsText })) }) })] }));
|
|
256
|
-
};
|
|
257
|
-
// Detectar si hay un valor seleccionado
|
|
258
|
-
const hasValue = inputText !== "" && inputText !== undefined && inputText !== null;
|
|
259
|
-
// Función para limpiar el valor y abrir el dialog
|
|
260
|
-
const handleIconClick = useCallback((event) => {
|
|
261
|
-
if (readOnly)
|
|
262
|
-
return;
|
|
263
|
-
event.preventDefault();
|
|
264
|
-
event.stopPropagation();
|
|
265
|
-
if (hasValue) {
|
|
266
|
-
// Si hay valor, limpiarlo
|
|
267
|
-
// Marcar que acabamos de limpiar para prevenir que onFocus abra el diálogo
|
|
268
|
-
justClearedRef.current = true;
|
|
269
|
-
if (isRegisterMode) {
|
|
270
|
-
if (setValue && fieldName) {
|
|
271
|
-
setValue(fieldName, undefined, {
|
|
272
|
-
shouldValidate: true,
|
|
273
|
-
shouldDirty: true,
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
else if (inputRef.current) {
|
|
277
|
-
const nativeInput = inputRef.current;
|
|
278
|
-
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set;
|
|
279
|
-
if (nativeInputValueSetter) {
|
|
280
|
-
nativeInputValueSetter.call(nativeInput, "");
|
|
281
|
-
}
|
|
282
|
-
else {
|
|
283
|
-
nativeInput.value = "";
|
|
284
|
-
}
|
|
285
|
-
if (onChange) {
|
|
286
|
-
const changeEvent = {
|
|
287
|
-
target: nativeInput,
|
|
288
|
-
currentTarget: nativeInput,
|
|
289
|
-
};
|
|
290
|
-
onChange(changeEvent);
|
|
291
|
-
}
|
|
292
|
-
const inputEvent = new Event("input", {
|
|
293
|
-
bubbles: true,
|
|
294
|
-
cancelable: true,
|
|
295
|
-
});
|
|
296
|
-
nativeInput.dispatchEvent(inputEvent);
|
|
297
|
-
const changeEventNative = new Event("change", {
|
|
298
|
-
bubbles: true,
|
|
299
|
-
cancelable: true,
|
|
300
|
-
});
|
|
301
|
-
nativeInput.dispatchEvent(changeEventNative);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
else {
|
|
305
|
-
onChange?.(undefined);
|
|
306
|
-
}
|
|
307
|
-
setInputText("");
|
|
308
|
-
setIsDialogOpen(false);
|
|
309
|
-
// Resetear el flag después de un pequeño delay para permitir que otros eventos se procesen
|
|
310
|
-
setTimeout(() => {
|
|
311
|
-
justClearedRef.current = false;
|
|
312
|
-
}, 100);
|
|
313
|
-
}
|
|
314
|
-
else {
|
|
315
|
-
// Si no hay valor, abrir el dialog
|
|
316
|
-
setIsDialogOpen(true);
|
|
317
|
-
}
|
|
318
|
-
}, [hasValue, isRegisterMode, setValue, fieldName, onChange, readOnly]);
|
|
319
|
-
// Determinar qué ícono mostrar: si hay valor, mostrar "fa-times", sino el ícono original
|
|
320
|
-
// Si está en readOnly, no mostrar ningún ícono
|
|
321
|
-
const displayIcon = readOnly ? undefined : hasValue ? "fa-times" : icon;
|
|
322
|
-
const displayIconPosition = readOnly ? undefined : iconPosition;
|
|
323
|
-
const displayOnIconClick = readOnly ? undefined : handleIconClick;
|
|
324
|
-
return (_jsxs(_Fragment, { children: [_jsx(Input, { ...inputProps, ref: combinedRef, label: label, value: inputText, onChange: (e) => {
|
|
325
|
-
if (readOnly)
|
|
326
|
-
return;
|
|
327
|
-
setInputText(e.target.value);
|
|
328
|
-
}, onFocus: () => {
|
|
329
|
-
if (!readOnly && !justClearedRef.current) {
|
|
330
|
-
setIsDialogOpen(true);
|
|
331
|
-
}
|
|
332
|
-
}, icon: displayIcon, iconPosition: displayIconPosition, onIconClick: displayOnIconClick, readOnly: readOnly }), !readOnly && (_jsx(Dialog, { isOpen: isDialogOpen, title: dialogTitle, footer: _jsx(Button, { variant: "outline", onClick: () => setIsDialogOpen(false), children: "Cerrar" }), onClose: () => setIsDialogOpen(false), children: getDialogBody() }))] }));
|
|
333
|
-
});
|
|
334
|
-
SearchSelectInputInner.displayName = "SearchSelectInput";
|
|
335
|
-
// Exportar con el cast genérico para permitir uso como <SearchSelectInput<T, K>>
|
|
336
|
-
export const SearchSelectInput = SearchSelectInputInner;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export { Button } from "./Button";
|
|
2
|
-
export { LinkButton } from "./LinkButton";
|
|
3
|
-
export { Input } from "./Input";
|
|
4
|
-
export { AutocompleteInput } from "./AutocompleteInput";
|
|
5
|
-
export { SearchSelectInput } from "./SearchSelectInput-OLD";
|
|
6
|
-
export { DatePicker } from "./DatePicker";
|
|
7
|
-
export { DateInput } from "./DateInput";
|
|
8
|
-
export { Pagination } from "./Pagination";
|
|
9
|
-
export { Checkbox } from "./Checkbox";
|
|
10
|
-
export { RadioButtonGroup } from "./RadioButtonGroup";
|
|
11
|
-
export { CurrencyInput } from "./CurrencyInput";
|
package/dist/components/index.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import React, { useState, useRef, useEffect } from "react";
|
|
3
|
-
import { normalizeIconClass } from "../utils/iconUtils";
|
|
4
|
-
export const Accordion = ({ title, children, icon, rightNode, defaultOpen = false, className = "", variant = "default", onToggle, }) => {
|
|
5
|
-
const [isOpen, setIsOpen] = useState(defaultOpen);
|
|
6
|
-
const [contentHeight, setContentHeight] = useState(0);
|
|
7
|
-
const contentRef = useRef(null);
|
|
8
|
-
// Separar clases de background del className
|
|
9
|
-
const classArray = className.trim().split(/\s+/).filter(Boolean);
|
|
10
|
-
const bgClasses = [];
|
|
11
|
-
const otherClasses = [];
|
|
12
|
-
classArray.forEach((cls) => {
|
|
13
|
-
// Detectar clases de background (bg-*, bg-gradient-*, bg-[...])
|
|
14
|
-
if (cls.startsWith("bg-") || cls.startsWith("bg-gradient-")) {
|
|
15
|
-
bgClasses.push(cls);
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
otherClasses.push(cls);
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
const backgroundClass = bgClasses.length > 0 ? bgClasses.join(" ") : "bg-[var(--color-bg-default)]";
|
|
22
|
-
const baseClasses = `
|
|
23
|
-
${backgroundClass} rounded-lg border
|
|
24
|
-
font-[var(--font-default)]
|
|
25
|
-
`;
|
|
26
|
-
const variantClasses = {
|
|
27
|
-
default: `border-[var(--color-border-default)]`,
|
|
28
|
-
elevated: `border-[var(--color-border-default)] shadow-[var(--shadow-lg)]`,
|
|
29
|
-
outlined: `border-[var(--color-gray-300)]`,
|
|
30
|
-
};
|
|
31
|
-
const classes = `${baseClasses} ${variantClasses[variant]} ${otherClasses.join(" ")}`;
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
if (contentRef.current) {
|
|
34
|
-
if (isOpen) {
|
|
35
|
-
// Usar requestAnimationFrame para asegurar que el DOM esté actualizado
|
|
36
|
-
requestAnimationFrame(() => {
|
|
37
|
-
if (contentRef.current) {
|
|
38
|
-
setContentHeight(contentRef.current.scrollHeight);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
setContentHeight(0);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}, [isOpen, children]);
|
|
47
|
-
// Inicializar altura si está abierto por defecto
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
if (defaultOpen && contentRef.current) {
|
|
50
|
-
requestAnimationFrame(() => {
|
|
51
|
-
if (contentRef.current) {
|
|
52
|
-
setContentHeight(contentRef.current.scrollHeight);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
}, [defaultOpen]);
|
|
57
|
-
const handleToggle = () => {
|
|
58
|
-
const newIsOpen = !isOpen;
|
|
59
|
-
setIsOpen(newIsOpen);
|
|
60
|
-
if (onToggle) {
|
|
61
|
-
onToggle(newIsOpen);
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
return (_jsxs("div", { className: `${classes} overflow-hidden`, children: [_jsxs("button", { onClick: handleToggle, className: "w-full flex items-center justify-between px-4 py-3 flysoft-button-reset bg-transparent border-none hover:bg-black/5 dark:hover:bg-white/5 transition-colors cursor-pointer", "aria-expanded": isOpen, children: [_jsxs("div", { className: "flex items-center gap-3 flex-1 min-w-0", children: [icon && (_jsx("i", { className: `${normalizeIconClass(icon)} text-[var(--color-text-secondary)] flex-shrink-0` })), _jsx("span", { className: "text-left font-medium text-[var(--color-text-primary)] truncate", children: title })] }), _jsxs("div", { className: "flex items-center gap-2 flex-shrink-0", children: [rightNode && (_jsx("div", { className: "flex items-center", onClick: (e) => e.stopPropagation(), children: rightNode })), _jsx("i", { className: `${normalizeIconClass(`fa-chevron-${isOpen ? "up" : "down"}`)} text-[var(--color-text-secondary)] transition-all duration-200 flex-shrink-0` })] })] }), _jsx("div", { ref: contentRef, className: "overflow-hidden transition-all duration-300 ease-in-out", style: {
|
|
65
|
-
maxHeight: `${contentHeight}px`,
|
|
66
|
-
}, children: _jsx("div", { className: "px-4 py-3 text-[var(--color-text-primary)]", children: children }) })] }));
|
|
67
|
-
};
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import React, { useState, useRef } from "react";
|
|
3
|
-
import { useBreakpoint } from "../../hooks";
|
|
4
|
-
import { useElementScroll } from "../../hooks/useElementScroll";
|
|
5
|
-
import { Button } from "../form-controls";
|
|
6
|
-
export const AppLayout = ({ navbar, leftDrawer, contentFooter, 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;
|
|
18
|
-
const { isMobile, isTablet } = useBreakpoint();
|
|
19
|
-
const contentRef = useRef(null);
|
|
20
|
-
const { scrollY, scrollDirection } = useElementScroll(contentRef);
|
|
21
|
-
const [isMobileDrawerOpen, setIsMobileDrawerOpen] = useState(false);
|
|
22
|
-
const [isNavbarVisible, setIsNavbarVisible] = useState(true);
|
|
23
|
-
const isNavbarVisibleRef = useRef(isNavbarVisible);
|
|
24
|
-
const isTransitioningRef = useRef(false);
|
|
25
|
-
const lastScrollYRef = useRef(0);
|
|
26
|
-
// Determinar si hay algún contenido en el drawer izquierdo
|
|
27
|
-
const hasLeftDrawerContent = leftDrawerHeader || leftDrawerContent || leftDrawerFooter;
|
|
28
|
-
const shouldShowMobileDrawer = isMobile || isTablet;
|
|
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);
|
|
35
|
-
// Mantener el ref sincronizado con el estado
|
|
36
|
-
React.useEffect(() => {
|
|
37
|
-
isNavbarVisibleRef.current = isNavbarVisible;
|
|
38
|
-
// Marcar que estamos en transición por 350ms (duración de la transición + margen)
|
|
39
|
-
isTransitioningRef.current = true;
|
|
40
|
-
const timer = setTimeout(() => {
|
|
41
|
-
isTransitioningRef.current = false;
|
|
42
|
-
}, 350);
|
|
43
|
-
return () => clearTimeout(timer);
|
|
44
|
-
}, [isNavbarVisible]);
|
|
45
|
-
// Controlar visibilidad del navbar basado en scroll con histeresis mejorada
|
|
46
|
-
React.useEffect(() => {
|
|
47
|
-
// Ignorar cambios durante transiciones o cambios muy pequeños de scroll
|
|
48
|
-
if (isTransitioningRef.current) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
const SCROLL_DELTA_THRESHOLD = 5; // Mínimo cambio de scroll para considerar
|
|
52
|
-
const scrollDelta = Math.abs(scrollY - lastScrollYRef.current);
|
|
53
|
-
// Ignorar cambios muy pequeños que pueden ser causados por el cambio de padding
|
|
54
|
-
if (scrollDelta < SCROLL_DELTA_THRESHOLD && lastScrollYRef.current > 0) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
const SHOW_THRESHOLD = 80;
|
|
58
|
-
const HIDE_THRESHOLD = 120;
|
|
59
|
-
// Verificar si estamos cerca del final del scroll (margen de error de 10px)
|
|
60
|
-
const element = contentRef.current;
|
|
61
|
-
const isNearBottom = element
|
|
62
|
-
? Math.abs(element.scrollHeight - element.clientHeight - element.scrollTop) < 10
|
|
63
|
-
: false;
|
|
64
|
-
let shouldBeVisible;
|
|
65
|
-
if (scrollY < SHOW_THRESHOLD) {
|
|
66
|
-
// Siempre mostrar navbar cerca del top
|
|
67
|
-
shouldBeVisible = true;
|
|
68
|
-
}
|
|
69
|
-
else if (scrollDirection === "down" &&
|
|
70
|
-
scrollY > HIDE_THRESHOLD &&
|
|
71
|
-
!isNearBottom) {
|
|
72
|
-
// Ocultar navbar al hacer scroll hacia abajo, excepto si estamos cerca del final
|
|
73
|
-
shouldBeVisible = false;
|
|
74
|
-
}
|
|
75
|
-
else if (scrollDirection === "up" && scrollY > SHOW_THRESHOLD) {
|
|
76
|
-
// Mostrar navbar al hacer scroll hacia arriba
|
|
77
|
-
shouldBeVisible = true;
|
|
78
|
-
}
|
|
79
|
-
else if (isNearBottom && scrollDirection === "down") {
|
|
80
|
-
// Si estamos en el final y scrolleamos hacia abajo, mantener el estado actual
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
// No cambiar el estado si scrollDirection es null o no se cumple ninguna condición
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
// Solo actualizar el estado si hay un cambio real
|
|
88
|
-
if (shouldBeVisible !== isNavbarVisibleRef.current) {
|
|
89
|
-
lastScrollYRef.current = scrollY;
|
|
90
|
-
setIsNavbarVisible(shouldBeVisible);
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
// Actualizar la referencia del scroll incluso si no cambiamos la visibilidad
|
|
94
|
-
lastScrollYRef.current = scrollY;
|
|
95
|
-
}
|
|
96
|
-
}, [scrollDirection, scrollY]);
|
|
97
|
-
const handleMobileDrawerToggle = () => {
|
|
98
|
-
setIsMobileDrawerOpen(!isMobileDrawerOpen);
|
|
99
|
-
};
|
|
100
|
-
const handleOverlayClick = () => {
|
|
101
|
-
setIsMobileDrawerOpen(false);
|
|
102
|
-
};
|
|
103
|
-
// Clases base del layout
|
|
104
|
-
const layoutClasses = `
|
|
105
|
-
flex flex-col w-full ${navbar?.navBarLeftNode || navbar?.navBarRightNode || leftDrawer?.contentNode || leftDrawer?.headerNode || leftDrawer?.footerNode ? "h-screen overflow-hidden" : "h-auto"}
|
|
106
|
-
font-[var(--font-default)]
|
|
107
|
-
${className}
|
|
108
|
-
`;
|
|
109
|
-
// Clases del navbar
|
|
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 100ms ease-in",
|
|
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 100ms ease-in, min-height 100ms ease-in, max-height 100ms ease-in",
|
|
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 100ms ease-in, max-height 100ms ease-in",
|
|
139
|
-
opacity: isNavbarVisible || fullWidthNavbar ? 1 : 0,
|
|
140
|
-
};
|
|
141
|
-
const navbarLeftClasses = `flex items-center gap-2`;
|
|
142
|
-
const navbarRightClasses = `flex items-center gap-2`;
|
|
143
|
-
// Clases del contenido principal
|
|
144
|
-
const mainClasses = `flex flex-1 overflow-hidden transition-all duration-100 ease-in`;
|
|
145
|
-
// Style for main content with dynamic navbar height padding
|
|
146
|
-
// No padding here anymore, it's handled by individual elements
|
|
147
|
-
const mainStyle = {};
|
|
148
|
-
// Style for the main content area (actual scrollable area)
|
|
149
|
-
// When fullWidthNavbar is true, we need top padding to avoid being covered by fixed navbar
|
|
150
|
-
const contentStyle = fullWidthNavbar && shouldShowNavbar && isNavbarVisible
|
|
151
|
-
? { paddingTop: navbarHeight }
|
|
152
|
-
: {};
|
|
153
|
-
// Clases del drawer izquierdo (contenedor principal)
|
|
154
|
-
// width se aplica como estilo inline para tener prioridad sobre className
|
|
155
|
-
const leftDrawerClasses = `
|
|
156
|
-
${leftDrawerWidth ? "" : "w-64"} bg-[var(--color-bg-default)]
|
|
157
|
-
flex-shrink-0 flex flex-col
|
|
158
|
-
transition-all duration-100 ease-in
|
|
159
|
-
${leftDrawerClassName}
|
|
160
|
-
`
|
|
161
|
-
.trim()
|
|
162
|
-
.replace(/\s+/g, " ");
|
|
163
|
-
// Style for left drawer with dynamic height and positioning
|
|
164
|
-
const leftDrawerStyle = {
|
|
165
|
-
...(leftDrawerWidth ? { width: leftDrawerWidth } : {}),
|
|
166
|
-
height: "100%", // Siempre 100% para evitar huecos en la parte superior
|
|
167
|
-
};
|
|
168
|
-
if (fullWidthNavbar) {
|
|
169
|
-
// Si el navbar es fullwidth, el drawer mide 100% y usamos padding para que el contenido
|
|
170
|
-
// no quede debajo del navbar fixed.
|
|
171
|
-
leftDrawerStyle.paddingTop =
|
|
172
|
-
shouldShowNavbar && isNavbarVisible ? navbarHeight : "0px";
|
|
173
|
-
leftDrawerStyle.transition = "padding-top 100ms ease-in";
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
// Si no es fullwidth, el drawer está al lado del navbar.
|
|
177
|
-
// No necesitamos marginTop ni height dinámico porque ya está en un flex-row
|
|
178
|
-
// y queremos que ocupe siempre todo el alto desde el borde superior.
|
|
179
|
-
leftDrawerStyle.marginTop = "0px";
|
|
180
|
-
leftDrawerStyle.transition = "padding-top 100ms ease-in";
|
|
181
|
-
}
|
|
182
|
-
// Clases del contenedor que incluye drawer y contenido (cuando fullWidthNavbar es false)
|
|
183
|
-
const contentWrapperClasses = fullWidthNavbar
|
|
184
|
-
? ""
|
|
185
|
-
: `flex flex-row flex-1 overflow-hidden`;
|
|
186
|
-
// Clases del contenedor que incluye navbar y main (cuando fullWidthNavbar es false)
|
|
187
|
-
const drawerAndContentClasses = fullWidthNavbar
|
|
188
|
-
? ""
|
|
189
|
-
: `flex flex-col flex-1 overflow-hidden`;
|
|
190
|
-
// Style for drawer and content wrapper with dynamic navbar height padding
|
|
191
|
-
const drawerAndContentStyle = !fullWidthNavbar && shouldShowNavbar && isNavbarVisible
|
|
192
|
-
? { paddingTop: 0 }
|
|
193
|
-
: {};
|
|
194
|
-
// Clases del header del drawer (fijo arriba)
|
|
195
|
-
const leftDrawerHeaderClasses = `
|
|
196
|
-
flex-shrink-0
|
|
197
|
-
`;
|
|
198
|
-
// Clases del contenido del drawer (scrolleable sin scrollbar visible)
|
|
199
|
-
const leftDrawerContentClasses = `
|
|
200
|
-
flex-1 overflow-y-auto scrollbar-hide
|
|
201
|
-
`;
|
|
202
|
-
// Clases del footer del drawer (fijo abajo)
|
|
203
|
-
const leftDrawerFooterClasses = `
|
|
204
|
-
flex-shrink-0
|
|
205
|
-
`;
|
|
206
|
-
const contentClasses = `
|
|
207
|
-
flex-1 overflow-y-auto flex flex-col
|
|
208
|
-
`;
|
|
209
|
-
// Clases del overlay móvil
|
|
210
|
-
const overlayClasses = `
|
|
211
|
-
fixed inset-0 bg-black/50 backdrop-blur-sm z-[1998]
|
|
212
|
-
`;
|
|
213
|
-
// Clases del drawer móvil
|
|
214
|
-
const mobileDrawerBaseClasses = `
|
|
215
|
-
fixed top-0 left-0 h-screen w-64 max-w-[80vw]
|
|
216
|
-
bg-[var(--color-bg-default)] shadow-[var(--shadow-xl)]
|
|
217
|
-
transform -translate-x-full transition-transform duration-100 ease-in
|
|
218
|
-
z-[1999] flex flex-col
|
|
219
|
-
${leftDrawerClassName}
|
|
220
|
-
`
|
|
221
|
-
.trim()
|
|
222
|
-
.replace(/\s+/g, " ");
|
|
223
|
-
// Style for mobile drawer with dynamic width
|
|
224
|
-
const mobileDrawerStyle = leftDrawerWidth ? { width: leftDrawerWidth } : {};
|
|
225
|
-
const mobileDrawerOpenClasses = `translate-x-0`;
|
|
226
|
-
const mobileDrawerContentClasses = `
|
|
227
|
-
flex-1 overflow-y-auto scrollbar-hide
|
|
228
|
-
`;
|
|
229
|
-
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 }))] })), _jsxs("main", { ref: contentRef, className: contentClasses, style: contentStyle, children: [_jsx("div", { className: "flex-1", children: children }), contentFooter && _jsx("div", { role: "contentinfo", children: contentFooter })] })] })] })) : (_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 px-2", 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("main", { ref: contentRef, className: contentClasses, style: contentStyle, children: [_jsx("div", { className: "flex-1", children: children }), contentFooter && _jsx("div", { role: "contentinfo", children: contentFooter })] })] })] }) })), 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: "absolute top-3 right-2", 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 }))] }))] }));
|
|
230
|
-
};
|