flysoft-react-ui 1.2.4 → 1.2.6
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/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 +11190 -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 +8 -4
- 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.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,680 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import React from "react";
|
|
3
|
-
import { createPortal } from "react-dom";
|
|
4
|
-
import { useFormContext } from "react-hook-form";
|
|
5
|
-
import { Input } from "./Input";
|
|
6
|
-
import { normalizeIconClass } from "../utils/iconUtils";
|
|
7
|
-
import { Checkbox } from "./Checkbox";
|
|
8
|
-
const AutocompleteInputInner = React.forwardRef(({ options, value, onChange, onSelectOption, noResultsText = "Sin resultados", className = "", getOptionLabel, getOptionValue, getOptionDescription, renderOption, readOnly = false, multiple = false, ...inputProps }, ref) => {
|
|
9
|
-
const [internalValue, setInternalValue] = React.useState(value || (multiple ? [] : ""));
|
|
10
|
-
const [displayValue, setDisplayValue] = React.useState("");
|
|
11
|
-
const [isOpen, setIsOpen] = React.useState(false);
|
|
12
|
-
const [highlightedIndex, setHighlightedIndex] = React.useState(-1);
|
|
13
|
-
const [dropdownPosition, setDropdownPosition] = React.useState(null);
|
|
14
|
-
const containerRef = React.useRef(null);
|
|
15
|
-
const dropdownRef = React.useRef(null);
|
|
16
|
-
const inputRef = React.useRef(null);
|
|
17
|
-
const hiddenInputRef = React.useRef(null);
|
|
18
|
-
const justClearedRef = React.useRef(false);
|
|
19
|
-
// Detectar si estamos en modo register: si viene 'name' de register, estamos en modo register
|
|
20
|
-
// register siempre pasa 'name', 'onChange', 'onBlur', y 'ref'
|
|
21
|
-
const isRegisterMode = React.useMemo(() => {
|
|
22
|
-
// Si viene 'name' en inputProps, es porque viene de register
|
|
23
|
-
return "name" in inputProps && inputProps.name !== undefined;
|
|
24
|
-
}, [inputProps]);
|
|
25
|
-
const fieldName = isRegisterMode && "name" in inputProps
|
|
26
|
-
? inputProps.name
|
|
27
|
-
: undefined;
|
|
28
|
-
// Obtener setValue del contexto del formulario
|
|
29
|
-
// Para usar con register, el formulario debe estar dentro de FormProvider
|
|
30
|
-
// useFormContext debe llamarse incondicionalmente (requisito de React Hooks)
|
|
31
|
-
// Si no hay FormProvider y se usa en modo register, useFormContext lanzará un error
|
|
32
|
-
// Para usar sin FormProvider, usar Controller en lugar de register
|
|
33
|
-
const formContext = useFormContext();
|
|
34
|
-
const setValue = formContext?.setValue;
|
|
35
|
-
const inputValue = isRegisterMode
|
|
36
|
-
? displayValue
|
|
37
|
-
: (multiple ? displayValue : (typeof value === "string" ? value : (typeof internalValue === "string" ? internalValue : "")));
|
|
38
|
-
const selectedValuesArray = React.useMemo(() => {
|
|
39
|
-
const currentVal = isRegisterMode && hiddenInputRef.current
|
|
40
|
-
? hiddenInputRef.current.value
|
|
41
|
-
: (value !== undefined ? value : internalValue);
|
|
42
|
-
if (Array.isArray(currentVal))
|
|
43
|
-
return currentVal;
|
|
44
|
-
// En modo register, los valores múltiples podrían estar como un string JSON o separados por comas.
|
|
45
|
-
// Si el hiddenInput recibe un array en react-hook-form, su valor puede volverse "[object Object]" o concatenado.
|
|
46
|
-
// Asumiremos que react-hook-form maneja correctamente el array si el value prop es un array.
|
|
47
|
-
// Si recibimos un string:
|
|
48
|
-
if (typeof currentVal === "string") {
|
|
49
|
-
if (!currentVal)
|
|
50
|
-
return [];
|
|
51
|
-
try {
|
|
52
|
-
// Intentar parsear si es JSON
|
|
53
|
-
const parsed = JSON.parse(currentVal);
|
|
54
|
-
if (Array.isArray(parsed))
|
|
55
|
-
return parsed.map(String);
|
|
56
|
-
return [currentVal];
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
// Si no es JSON, asumir es un solo string o separado por comas (no recomendado por conflicto con comas en valores)
|
|
60
|
-
return [currentVal];
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return [];
|
|
64
|
-
}, [isRegisterMode, value, internalValue, multiple]);
|
|
65
|
-
const labelGetter = React.useCallback((item) => {
|
|
66
|
-
if (getOptionLabel)
|
|
67
|
-
return getOptionLabel(item);
|
|
68
|
-
const anyItem = item;
|
|
69
|
-
return (anyItem.label ?? "").toString();
|
|
70
|
-
}, [getOptionLabel]);
|
|
71
|
-
const valueGetter = React.useCallback((item) => {
|
|
72
|
-
if (getOptionValue)
|
|
73
|
-
return getOptionValue(item);
|
|
74
|
-
const anyItem = item;
|
|
75
|
-
return anyItem.value ?? undefined;
|
|
76
|
-
}, [getOptionValue]);
|
|
77
|
-
// Calcular el label unificado para multiple
|
|
78
|
-
const getMultipleDisplayValue = React.useCallback((values) => {
|
|
79
|
-
if (!values || values.length === 0)
|
|
80
|
-
return "";
|
|
81
|
-
const labels = values.map((val) => {
|
|
82
|
-
const matchingOption = options.find((option) => String(valueGetter(option)) === String(val));
|
|
83
|
-
return matchingOption ? labelGetter(matchingOption) : String(val);
|
|
84
|
-
});
|
|
85
|
-
return labels.join(", ");
|
|
86
|
-
}, [options, valueGetter, labelGetter]);
|
|
87
|
-
const descriptionGetter = React.useCallback((item) => {
|
|
88
|
-
if (getOptionDescription)
|
|
89
|
-
return getOptionDescription(item);
|
|
90
|
-
const anyItem = item;
|
|
91
|
-
return anyItem.description;
|
|
92
|
-
}, [getOptionDescription]);
|
|
93
|
-
const filteredOptions = React.useMemo(() => {
|
|
94
|
-
let search = displayValue.trim().toLowerCase();
|
|
95
|
-
// Si es múltiple y no estamos enfocando el input para buscar, no filtramos por displayValue
|
|
96
|
-
// (ya que displayValue contiene los labels de los items seleccionados separados por coma)
|
|
97
|
-
if (multiple) {
|
|
98
|
-
if (document.activeElement !== inputRef.current || displayValue === getMultipleDisplayValue(selectedValuesArray)) {
|
|
99
|
-
search = "";
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
search = inputValue.trim().toLowerCase();
|
|
104
|
-
}
|
|
105
|
-
if (!search)
|
|
106
|
-
return options;
|
|
107
|
-
return options.filter((option) => {
|
|
108
|
-
const label = labelGetter(option).toLowerCase();
|
|
109
|
-
const optionValue = String(valueGetter(option) ?? "").toLowerCase();
|
|
110
|
-
return label.includes(search) || optionValue.includes(search);
|
|
111
|
-
});
|
|
112
|
-
}, [inputValue, options, labelGetter, valueGetter]);
|
|
113
|
-
// Función helper para sincronizar displayValue con el valor del formulario
|
|
114
|
-
const syncDisplayValue = React.useCallback(() => {
|
|
115
|
-
if (isRegisterMode) {
|
|
116
|
-
// En modo register, usamos el hiddenInputRef si existe, sino el inputRef (legacy/fallback)
|
|
117
|
-
const targetInput = hiddenInputRef.current || inputRef.current;
|
|
118
|
-
if (targetInput) {
|
|
119
|
-
const formValue = targetInput.value;
|
|
120
|
-
if (multiple) {
|
|
121
|
-
let parsedValues = [];
|
|
122
|
-
try {
|
|
123
|
-
parsedValues = JSON.parse(formValue);
|
|
124
|
-
}
|
|
125
|
-
catch {
|
|
126
|
-
parsedValues = formValue ? [formValue] : [];
|
|
127
|
-
}
|
|
128
|
-
if (Array.isArray(parsedValues)) {
|
|
129
|
-
setDisplayValue(getMultipleDisplayValue(parsedValues));
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
// Si el valor del formulario coincide con algún getOptionValue, mostrar su label
|
|
135
|
-
const matchingOption = options.find((option) => String(valueGetter(option)) === String(formValue));
|
|
136
|
-
if (matchingOption) {
|
|
137
|
-
const label = labelGetter(matchingOption);
|
|
138
|
-
setDisplayValue(label);
|
|
139
|
-
return true; // Indica que se encontró y sincronizó un valor
|
|
140
|
-
}
|
|
141
|
-
else if (formValue) {
|
|
142
|
-
// Si hay un valor pero no coincide, mostrarlo tal cual (o buscar por label si fuera el caso)
|
|
143
|
-
setDisplayValue(formValue);
|
|
144
|
-
return true;
|
|
145
|
-
}
|
|
146
|
-
else {
|
|
147
|
-
setDisplayValue("");
|
|
148
|
-
return false; // No hay valor aún
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
return false;
|
|
154
|
-
}, [isRegisterMode, options, valueGetter, labelGetter, multiple, getMultipleDisplayValue]);
|
|
155
|
-
// Sincronizar displayValue con el valor del formulario en modo register
|
|
156
|
-
// Usar un intervalo que se ejecute hasta que encuentre el valor o hasta un máximo de intentos
|
|
157
|
-
React.useEffect(() => {
|
|
158
|
-
if (isRegisterMode) {
|
|
159
|
-
let attempts = 0;
|
|
160
|
-
const maxAttempts = 50; // Intentar durante ~5 segundos (50 * 100ms)
|
|
161
|
-
// Función que intenta sincronizar y retorna true si encontró un valor
|
|
162
|
-
const trySync = () => {
|
|
163
|
-
const targetInput = hiddenInputRef.current || inputRef.current;
|
|
164
|
-
if (targetInput) {
|
|
165
|
-
const formValue = targetInput.value;
|
|
166
|
-
if (formValue) {
|
|
167
|
-
if (multiple) {
|
|
168
|
-
let parsedValues = [];
|
|
169
|
-
try {
|
|
170
|
-
parsedValues = JSON.parse(formValue);
|
|
171
|
-
}
|
|
172
|
-
catch {
|
|
173
|
-
parsedValues = [formValue];
|
|
174
|
-
}
|
|
175
|
-
setDisplayValue(getMultipleDisplayValue(parsedValues));
|
|
176
|
-
return true;
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
// Hay un valor, intentar sincronizar
|
|
180
|
-
const matchingOption = options.find((option) => String(valueGetter(option)) === String(formValue));
|
|
181
|
-
if (matchingOption) {
|
|
182
|
-
const label = labelGetter(matchingOption);
|
|
183
|
-
setDisplayValue(label);
|
|
184
|
-
return true; // Valor encontrado y sincronizado
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
setDisplayValue(formValue);
|
|
188
|
-
return true; // Valor encontrado pero no coincide con opciones
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return false; // No hay valor aún
|
|
194
|
-
};
|
|
195
|
-
// Intentar inmediatamente
|
|
196
|
-
if (trySync()) {
|
|
197
|
-
return; // Ya encontramos el valor
|
|
198
|
-
}
|
|
199
|
-
// Si no encontramos el valor, usar un intervalo para seguir intentando
|
|
200
|
-
const intervalId = window.setInterval(() => {
|
|
201
|
-
attempts++;
|
|
202
|
-
if (trySync() || attempts >= maxAttempts) {
|
|
203
|
-
clearInterval(intervalId);
|
|
204
|
-
}
|
|
205
|
-
}, 100); // Intentar cada 100ms
|
|
206
|
-
// También usar timeouts como fallback
|
|
207
|
-
const timeouts = [];
|
|
208
|
-
[0, 50, 100, 200, 500, 1000].forEach((delay) => {
|
|
209
|
-
const timeoutId = window.setTimeout(() => {
|
|
210
|
-
trySync();
|
|
211
|
-
}, delay);
|
|
212
|
-
timeouts.push(timeoutId);
|
|
213
|
-
});
|
|
214
|
-
return () => {
|
|
215
|
-
clearInterval(intervalId);
|
|
216
|
-
timeouts.forEach(clearTimeout);
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
}, [isRegisterMode, options, valueGetter, labelGetter]);
|
|
220
|
-
// También escuchar cambios en el input (hidden o visible) para sincronizar cuando cambie
|
|
221
|
-
React.useEffect(() => {
|
|
222
|
-
if (isRegisterMode) {
|
|
223
|
-
// Observamos el hiddenInput si estamos en register mode y existe, o el input normal
|
|
224
|
-
const targetElement = hiddenInputRef.current || inputRef.current;
|
|
225
|
-
if (targetElement) {
|
|
226
|
-
// Función para sincronizar cuando el input cambia
|
|
227
|
-
const handleInputSync = () => {
|
|
228
|
-
// Solo sincronizar si es el hidden input o si no tenemos hidden input
|
|
229
|
-
if (targetElement === hiddenInputRef.current) {
|
|
230
|
-
syncDisplayValue();
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
// Escuchar eventos de input y change (aunque en hidden input no suelen dispararse eventos de usuario)
|
|
234
|
-
targetElement.addEventListener("input", handleInputSync);
|
|
235
|
-
targetElement.addEventListener("change", handleInputSync);
|
|
236
|
-
// También usar MutationObserver para detectar cambios en el atributo value (más fiable para hidden inputs cambiados por JS)
|
|
237
|
-
const observer = new MutationObserver(() => {
|
|
238
|
-
syncDisplayValue();
|
|
239
|
-
});
|
|
240
|
-
observer.observe(targetElement, {
|
|
241
|
-
attributes: true,
|
|
242
|
-
attributeFilter: ["value"],
|
|
243
|
-
});
|
|
244
|
-
return () => {
|
|
245
|
-
targetElement.removeEventListener("input", handleInputSync);
|
|
246
|
-
targetElement.removeEventListener("change", handleInputSync);
|
|
247
|
-
observer.disconnect();
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}, [isRegisterMode, syncDisplayValue]);
|
|
252
|
-
const handleChange = (event) => {
|
|
253
|
-
if (readOnly)
|
|
254
|
-
return;
|
|
255
|
-
const newValue = event.target.value;
|
|
256
|
-
if (isRegisterMode) {
|
|
257
|
-
setDisplayValue(newValue);
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
if (multiple) {
|
|
261
|
-
setDisplayValue(newValue);
|
|
262
|
-
}
|
|
263
|
-
else {
|
|
264
|
-
// Modo API personalizada para input simple
|
|
265
|
-
if (value === undefined) {
|
|
266
|
-
setInternalValue(newValue);
|
|
267
|
-
}
|
|
268
|
-
if (onChange) {
|
|
269
|
-
onChange(newValue);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
setIsOpen(true);
|
|
274
|
-
setHighlightedIndex(-1);
|
|
275
|
-
};
|
|
276
|
-
const handleSelect = (option) => {
|
|
277
|
-
if (readOnly)
|
|
278
|
-
return;
|
|
279
|
-
const label = labelGetter(option);
|
|
280
|
-
const selectedValue = valueGetter(option);
|
|
281
|
-
const valueString = String(selectedValue ?? "");
|
|
282
|
-
let newValuesArray = [];
|
|
283
|
-
let newValueToSet = valueString;
|
|
284
|
-
let newLabelToSet = label;
|
|
285
|
-
if (multiple) {
|
|
286
|
-
// Toggle selection
|
|
287
|
-
if (selectedValuesArray.includes(valueString)) {
|
|
288
|
-
newValuesArray = selectedValuesArray.filter(v => v !== valueString);
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
newValuesArray = [...selectedValuesArray, valueString];
|
|
292
|
-
}
|
|
293
|
-
if (!isRegisterMode && value === undefined) {
|
|
294
|
-
setInternalValue(newValuesArray);
|
|
295
|
-
}
|
|
296
|
-
newLabelToSet = getMultipleDisplayValue(newValuesArray);
|
|
297
|
-
setDisplayValue(newLabelToSet);
|
|
298
|
-
// Pass the array to the onChange/register hooks
|
|
299
|
-
newValueToSet = newValuesArray;
|
|
300
|
-
}
|
|
301
|
-
if (isRegisterMode) {
|
|
302
|
-
// En modo register, setear el valor usando setValue o actualizando el input nativo (hidden)
|
|
303
|
-
if (setValue && fieldName) {
|
|
304
|
-
setValue(fieldName, newValueToSet, {
|
|
305
|
-
shouldValidate: true,
|
|
306
|
-
shouldDirty: true,
|
|
307
|
-
});
|
|
308
|
-
// Actualizar el input hidden
|
|
309
|
-
if (hiddenInputRef.current) {
|
|
310
|
-
hiddenInputRef.current.value = multiple ? JSON.stringify(newValuesArray) : valueString;
|
|
311
|
-
}
|
|
312
|
-
// Actualizar displayValue con el label
|
|
313
|
-
setDisplayValue(newLabelToSet);
|
|
314
|
-
}
|
|
315
|
-
else {
|
|
316
|
-
// Fallback
|
|
317
|
-
const targetInput = hiddenInputRef.current || inputRef.current;
|
|
318
|
-
if (targetInput) {
|
|
319
|
-
const nativeInput = targetInput;
|
|
320
|
-
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set;
|
|
321
|
-
const stringObjToSet = multiple ? JSON.stringify(newValuesArray) : valueString;
|
|
322
|
-
if (nativeInputValueSetter) {
|
|
323
|
-
nativeInputValueSetter.call(nativeInput, stringObjToSet);
|
|
324
|
-
}
|
|
325
|
-
else {
|
|
326
|
-
nativeInput.value = stringObjToSet;
|
|
327
|
-
}
|
|
328
|
-
// Disparar eventos
|
|
329
|
-
if (onChange) {
|
|
330
|
-
const changeEvent = {
|
|
331
|
-
target: nativeInput,
|
|
332
|
-
currentTarget: nativeInput,
|
|
333
|
-
};
|
|
334
|
-
onChange(changeEvent);
|
|
335
|
-
}
|
|
336
|
-
const inputEvent = new Event("input", {
|
|
337
|
-
bubbles: true,
|
|
338
|
-
cancelable: true,
|
|
339
|
-
});
|
|
340
|
-
nativeInput.dispatchEvent(inputEvent);
|
|
341
|
-
const changeEventNative = new Event("change", {
|
|
342
|
-
bubbles: true,
|
|
343
|
-
cancelable: true,
|
|
344
|
-
});
|
|
345
|
-
nativeInput.dispatchEvent(changeEventNative);
|
|
346
|
-
}
|
|
347
|
-
setDisplayValue(newLabelToSet);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
else {
|
|
351
|
-
// Modo API personalizada - comportamiento original
|
|
352
|
-
if (onChange) {
|
|
353
|
-
if (multiple) {
|
|
354
|
-
onChange(newValuesArray);
|
|
355
|
-
}
|
|
356
|
-
else {
|
|
357
|
-
onChange(valueString);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
onSelectOption?.(option, selectedValue);
|
|
362
|
-
if (!multiple) {
|
|
363
|
-
setIsOpen(false);
|
|
364
|
-
}
|
|
365
|
-
};
|
|
366
|
-
const handleKeyDown = (event) => {
|
|
367
|
-
if (readOnly)
|
|
368
|
-
return;
|
|
369
|
-
if (!isOpen && (event.key === "ArrowDown" || event.key === "ArrowUp")) {
|
|
370
|
-
setIsOpen(true);
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
if (!filteredOptions.length)
|
|
374
|
-
return;
|
|
375
|
-
if (event.key === "ArrowDown") {
|
|
376
|
-
event.preventDefault();
|
|
377
|
-
setHighlightedIndex((prev) => prev < filteredOptions.length - 1 ? prev + 1 : 0);
|
|
378
|
-
}
|
|
379
|
-
else if (event.key === "ArrowUp") {
|
|
380
|
-
event.preventDefault();
|
|
381
|
-
setHighlightedIndex((prev) => prev > 0 ? prev - 1 : filteredOptions.length - 1);
|
|
382
|
-
}
|
|
383
|
-
else if (event.key === "Enter") {
|
|
384
|
-
// Si hay una opción resaltada, seleccionarla
|
|
385
|
-
if (highlightedIndex >= 0 &&
|
|
386
|
-
highlightedIndex < filteredOptions.length) {
|
|
387
|
-
event.preventDefault();
|
|
388
|
-
handleSelect(filteredOptions[highlightedIndex]);
|
|
389
|
-
}
|
|
390
|
-
else if (isOpen && filteredOptions.length > 0) {
|
|
391
|
-
// Si no hay opción resaltada pero hay opciones disponibles, seleccionar la primera
|
|
392
|
-
event.preventDefault();
|
|
393
|
-
handleSelect(filteredOptions[0]);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
else if (event.key === "Escape") {
|
|
397
|
-
setIsOpen(false);
|
|
398
|
-
}
|
|
399
|
-
};
|
|
400
|
-
React.useEffect(() => {
|
|
401
|
-
if (!isOpen)
|
|
402
|
-
return;
|
|
403
|
-
const handleClickOutside = (event) => {
|
|
404
|
-
const target = event.target;
|
|
405
|
-
const isClickInsideContainer = containerRef.current?.contains(target);
|
|
406
|
-
const isClickInsideDropdown = dropdownRef.current?.contains(target);
|
|
407
|
-
if (!isClickInsideContainer && !isClickInsideDropdown) {
|
|
408
|
-
setIsOpen(false);
|
|
409
|
-
}
|
|
410
|
-
};
|
|
411
|
-
// Pequeño delay para asegurar que el portal esté montado
|
|
412
|
-
const timer = setTimeout(() => {
|
|
413
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
414
|
-
}, 0);
|
|
415
|
-
return () => {
|
|
416
|
-
clearTimeout(timer);
|
|
417
|
-
document.removeEventListener("mousedown", handleClickOutside);
|
|
418
|
-
};
|
|
419
|
-
}, [isOpen]);
|
|
420
|
-
React.useEffect(() => {
|
|
421
|
-
if (!isRegisterMode) {
|
|
422
|
-
// Solo para modo API personalizada
|
|
423
|
-
if (value !== undefined) {
|
|
424
|
-
// Si el value es el resultado de getOptionValue, buscar la opción correspondiente
|
|
425
|
-
// y mostrar su label. Si no se encuentra, mostrar el value tal cual.
|
|
426
|
-
if (multiple) {
|
|
427
|
-
const arrayVals = Array.isArray(value) ? value : [value];
|
|
428
|
-
setDisplayValue(getMultipleDisplayValue(arrayVals));
|
|
429
|
-
}
|
|
430
|
-
else {
|
|
431
|
-
const matchingOption = options.find((option) => String(valueGetter(option)) === String(value));
|
|
432
|
-
if (matchingOption) {
|
|
433
|
-
setInternalValue(labelGetter(matchingOption));
|
|
434
|
-
}
|
|
435
|
-
else {
|
|
436
|
-
setInternalValue(value);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
else {
|
|
441
|
-
// Resetear el estado interno cuando value es undefined (por ejemplo, después de un reset)
|
|
442
|
-
if (multiple) {
|
|
443
|
-
setDisplayValue("");
|
|
444
|
-
setInternalValue([]);
|
|
445
|
-
}
|
|
446
|
-
else {
|
|
447
|
-
setInternalValue("");
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
}, [value, options, valueGetter, labelGetter, isRegisterMode, multiple, getMultipleDisplayValue]);
|
|
452
|
-
const showDropdown = !readOnly && isOpen && (filteredOptions.length > 0 || noResultsText);
|
|
453
|
-
// Verificar que estamos en el navegador
|
|
454
|
-
// Inicializar isMounted de forma síncrona si es posible
|
|
455
|
-
const [isMounted, setIsMounted] = React.useState(() => {
|
|
456
|
-
return typeof document !== "undefined" && !!document.body;
|
|
457
|
-
});
|
|
458
|
-
React.useEffect(() => {
|
|
459
|
-
if (!isMounted && typeof document !== "undefined" && document.body) {
|
|
460
|
-
setIsMounted(true);
|
|
461
|
-
}
|
|
462
|
-
}, [isMounted]);
|
|
463
|
-
// Actualizar posición del dropdown cuando se abre
|
|
464
|
-
React.useEffect(() => {
|
|
465
|
-
if (showDropdown && containerRef.current && isMounted) {
|
|
466
|
-
const updatePosition = () => {
|
|
467
|
-
const rect = containerRef.current?.getBoundingClientRect();
|
|
468
|
-
if (rect) {
|
|
469
|
-
setDropdownPosition({
|
|
470
|
-
top: rect.bottom + window.scrollY + 4,
|
|
471
|
-
left: rect.left + window.scrollX,
|
|
472
|
-
width: rect.width,
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
};
|
|
476
|
-
updatePosition();
|
|
477
|
-
window.addEventListener("scroll", updatePosition, true);
|
|
478
|
-
window.addEventListener("resize", updatePosition);
|
|
479
|
-
return () => {
|
|
480
|
-
window.removeEventListener("scroll", updatePosition, true);
|
|
481
|
-
window.removeEventListener("resize", updatePosition);
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
else {
|
|
485
|
-
setDropdownPosition(null);
|
|
486
|
-
}
|
|
487
|
-
}, [showDropdown, isMounted]);
|
|
488
|
-
// Detectar si hay un valor seleccionado
|
|
489
|
-
// Un valor está seleccionado si el value coincide con el getOptionValue de alguna opción
|
|
490
|
-
const hasSelectedValue = React.useMemo(() => {
|
|
491
|
-
if (multiple) {
|
|
492
|
-
return selectedValuesArray.length > 0;
|
|
493
|
-
}
|
|
494
|
-
if (isRegisterMode) {
|
|
495
|
-
const targetInput = hiddenInputRef.current || inputRef.current;
|
|
496
|
-
if (targetInput) {
|
|
497
|
-
const formValue = targetInput.value;
|
|
498
|
-
if (!formValue)
|
|
499
|
-
return false;
|
|
500
|
-
return options.some((option) => String(valueGetter(option)) === String(formValue));
|
|
501
|
-
}
|
|
502
|
-
return false;
|
|
503
|
-
}
|
|
504
|
-
if (value === undefined || value === null || value === "")
|
|
505
|
-
return false;
|
|
506
|
-
// Verificar si el value coincide con el getOptionValue de alguna opción
|
|
507
|
-
return options.some((option) => String(valueGetter(option)) === String(value));
|
|
508
|
-
}, [value, options, valueGetter, isRegisterMode, multiple, selectedValuesArray]);
|
|
509
|
-
// Función para limpiar el valor
|
|
510
|
-
const handleClear = React.useCallback((event) => {
|
|
511
|
-
if (readOnly)
|
|
512
|
-
return;
|
|
513
|
-
event.preventDefault();
|
|
514
|
-
event.stopPropagation();
|
|
515
|
-
// Marcar que acabamos de limpiar para prevenir que onFocus abra el diálogo
|
|
516
|
-
justClearedRef.current = true;
|
|
517
|
-
if (isRegisterMode) {
|
|
518
|
-
// En modo register, limpiar el input nativo (hidden) y disparar eventos
|
|
519
|
-
const targetInput = hiddenInputRef.current || inputRef.current;
|
|
520
|
-
if (targetInput) {
|
|
521
|
-
const nativeInput = targetInput;
|
|
522
|
-
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set;
|
|
523
|
-
setter?.call(nativeInput, "");
|
|
524
|
-
// Disparar eventos para que react-hook-form lo capture
|
|
525
|
-
const inputEvent = new Event("input", { bubbles: true });
|
|
526
|
-
nativeInput.dispatchEvent(inputEvent);
|
|
527
|
-
const changeEvent = new Event("change", { bubbles: true });
|
|
528
|
-
nativeInput.dispatchEvent(changeEvent);
|
|
529
|
-
// Llamar al onChange de register
|
|
530
|
-
if (onChange) {
|
|
531
|
-
const changeEventReact = {
|
|
532
|
-
target: nativeInput,
|
|
533
|
-
currentTarget: nativeInput,
|
|
534
|
-
};
|
|
535
|
-
onChange(changeEventReact);
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
// Limpiar el displayValue
|
|
539
|
-
setDisplayValue("");
|
|
540
|
-
}
|
|
541
|
-
else {
|
|
542
|
-
// Modo API personalizada
|
|
543
|
-
if (value === undefined) {
|
|
544
|
-
if (multiple) {
|
|
545
|
-
setInternalValue([]);
|
|
546
|
-
}
|
|
547
|
-
else {
|
|
548
|
-
setInternalValue("");
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
if (onChange) {
|
|
552
|
-
if (multiple) {
|
|
553
|
-
onChange([]);
|
|
554
|
-
}
|
|
555
|
-
else {
|
|
556
|
-
onChange("");
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
setDisplayValue("");
|
|
560
|
-
}
|
|
561
|
-
setIsOpen(false);
|
|
562
|
-
// Resetear el flag después de un pequeño delay para permitir que otros eventos se procesen
|
|
563
|
-
setTimeout(() => {
|
|
564
|
-
justClearedRef.current = false;
|
|
565
|
-
}, 100);
|
|
566
|
-
}, [value, onChange, isRegisterMode, readOnly]);
|
|
567
|
-
// Determinar qué ícono mostrar: si hay valor seleccionado, mostrar "X", sino usar el ícono original
|
|
568
|
-
// Si está en readOnly, no mostrar el ícono de limpiar ni permitir clicks
|
|
569
|
-
const displayIcon = readOnly
|
|
570
|
-
? inputProps.icon
|
|
571
|
-
: hasSelectedValue
|
|
572
|
-
? "fa-times"
|
|
573
|
-
: inputProps.icon;
|
|
574
|
-
const displayIconPosition = readOnly
|
|
575
|
-
? inputProps.iconPosition || "left"
|
|
576
|
-
: hasSelectedValue
|
|
577
|
-
? "right"
|
|
578
|
-
: inputProps.iconPosition || "left";
|
|
579
|
-
const displayOnIconClick = readOnly
|
|
580
|
-
? undefined
|
|
581
|
-
: hasSelectedValue
|
|
582
|
-
? handleClear
|
|
583
|
-
: inputProps.onIconClick;
|
|
584
|
-
// Refs separados: uno para el visible y otro para el hidden (registrado)
|
|
585
|
-
const setHiddenRef = React.useCallback((node) => {
|
|
586
|
-
hiddenInputRef.current = node;
|
|
587
|
-
// Solo pasar el ref externo al hidden input en modo register
|
|
588
|
-
if (isRegisterMode) {
|
|
589
|
-
if (typeof ref === "function") {
|
|
590
|
-
ref(node);
|
|
591
|
-
}
|
|
592
|
-
else if (ref) {
|
|
593
|
-
ref.current = node;
|
|
594
|
-
}
|
|
595
|
-
// Sincronización inicial cuando el ref se monta
|
|
596
|
-
if (node) {
|
|
597
|
-
[0, 10, 50, 100, 200, 500].forEach((delay) => {
|
|
598
|
-
setTimeout(() => {
|
|
599
|
-
if (node && hiddenInputRef.current === node) {
|
|
600
|
-
const formValue = node.value;
|
|
601
|
-
if (formValue) {
|
|
602
|
-
const matchingOption = options.find((option) => String(valueGetter(option)) === String(formValue));
|
|
603
|
-
if (multiple) {
|
|
604
|
-
try {
|
|
605
|
-
const parsed = JSON.parse(formValue);
|
|
606
|
-
if (Array.isArray(parsed)) {
|
|
607
|
-
setDisplayValue(getMultipleDisplayValue(parsed));
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
catch {
|
|
611
|
-
setDisplayValue(getMultipleDisplayValue([formValue]));
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
else {
|
|
615
|
-
if (matchingOption) {
|
|
616
|
-
setDisplayValue(labelGetter(matchingOption));
|
|
617
|
-
}
|
|
618
|
-
else {
|
|
619
|
-
setDisplayValue(formValue);
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
}, delay);
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}, [ref, isRegisterMode, options, valueGetter, labelGetter, multiple, getMultipleDisplayValue]);
|
|
629
|
-
const setVisibleRef = React.useCallback((node) => {
|
|
630
|
-
inputRef.current = node;
|
|
631
|
-
// En modo NO register, pasamos el ref al input visible
|
|
632
|
-
if (!isRegisterMode) {
|
|
633
|
-
if (typeof ref === "function") {
|
|
634
|
-
ref(node);
|
|
635
|
-
}
|
|
636
|
-
else if (ref) {
|
|
637
|
-
ref.current = node;
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
}, [ref, isRegisterMode]);
|
|
641
|
-
// Separar propiedades para input visible y hidden
|
|
642
|
-
const { name: nameProp, ...visibleInputProps } = inputProps;
|
|
643
|
-
return (_jsxs("div", { ref: containerRef, className: "relative w-full", children: [isRegisterMode && (_jsx("input", { type: "hidden", name: nameProp, ref: setHiddenRef, defaultValue: value })), _jsx(Input, { ...visibleInputProps, name: isRegisterMode ? undefined : nameProp, ref: setVisibleRef, value: multiple ? displayValue : inputValue, onChange: handleChange, onFocus: () => {
|
|
644
|
-
if (!readOnly && !justClearedRef.current) {
|
|
645
|
-
setIsOpen(true);
|
|
646
|
-
}
|
|
647
|
-
}, onKeyDown: handleKeyDown, className: className, icon: displayIcon, iconPosition: displayIconPosition, onIconClick: displayOnIconClick, readOnly: readOnly }), (() => {
|
|
648
|
-
// Verificar de forma segura que document.body existe y es válido
|
|
649
|
-
const bodyElement = typeof document !== "undefined" &&
|
|
650
|
-
document.body &&
|
|
651
|
-
document.body instanceof HTMLElement
|
|
652
|
-
? document.body
|
|
653
|
-
: null;
|
|
654
|
-
return (showDropdown &&
|
|
655
|
-
dropdownPosition &&
|
|
656
|
-
isMounted &&
|
|
657
|
-
bodyElement &&
|
|
658
|
-
createPortal(_jsx("div", { ref: dropdownRef, className: "fixed z-[2001] min-w-full w-max rounded-md border border-[var(--color-border-default)] \n bg-[var(--color-bg-default)] shadow-[var(--shadow-lg)] max-h-60 overflow-auto", style: {
|
|
659
|
-
top: `${dropdownPosition.top}px`,
|
|
660
|
-
left: `${dropdownPosition.left}px`,
|
|
661
|
-
minWidth: `${dropdownPosition.width}px`,
|
|
662
|
-
}, children: filteredOptions.length > 0 ? (_jsx("ul", { className: "py-1 list-none pl-0 m-0", children: filteredOptions.map((option, index) => {
|
|
663
|
-
const label = labelGetter(option);
|
|
664
|
-
const description = descriptionGetter(option);
|
|
665
|
-
const anyOption = option;
|
|
666
|
-
return (_jsx("li", { className: `px-3 py-2 cursor-pointer flex items-start gap-2 text-sm
|
|
667
|
-
${index === highlightedIndex
|
|
668
|
-
? "bg-[var(--color-primary-soft)] text-[var(--color-primary)]"
|
|
669
|
-
: "text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)]"}`, onMouseDown: (event) => {
|
|
670
|
-
event.preventDefault();
|
|
671
|
-
handleSelect(option);
|
|
672
|
-
}, onMouseEnter: () => setHighlightedIndex(index), children: renderOption ? (renderOption(option)) : (_jsxs(_Fragment, { children: [multiple && (_jsx(Checkbox, { readOnly: true, checked: selectedValuesArray.includes(String(valueGetter(option) ?? "")), className: "mr-2 pointer-events-none" })), anyOption.icon && (_jsx("i", { className: `${normalizeIconClass(anyOption.icon)} mt-0.5 text-[var(--color-text-muted)] flex-shrink-0 mr-2` })), _jsxs("div", { className: "flex flex-col min-w-0", children: [_jsx("span", { className: "font-[var(--font-default)] whitespace-nowrap", children: label }), description !== undefined &&
|
|
673
|
-
description !== null && (_jsx("span", { className: "text-xs text-[var(--color-text-secondary)] break-words", children: description }))] })] })) }, String(valueGetter(option) ?? label ?? index)));
|
|
674
|
-
}) })) : (_jsx("div", { className: "px-3 py-2 text-sm text-[var(--color-text-secondary)]", children: noResultsText })) }), bodyElement));
|
|
675
|
-
})()] }));
|
|
676
|
-
});
|
|
677
|
-
// Asignar displayName antes del cast genérico
|
|
678
|
-
AutocompleteInputInner.displayName = "AutocompleteInput";
|
|
679
|
-
// Exportar con el cast genérico
|
|
680
|
-
export const AutocompleteInput = AutocompleteInputInner;
|