flysoft-react-ui 0.5.0 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +19 -7
- package/dist/components/form-controls/AutocompleteInput.d.ts +11 -3
- package/dist/components/form-controls/AutocompleteInput.d.ts.map +1 -1
- package/dist/components/form-controls/AutocompleteInput.js +411 -31
- package/dist/components/form-controls/Button.d.ts +3 -0
- package/dist/components/form-controls/Button.d.ts.map +1 -1
- package/dist/components/form-controls/Button.js +160 -19
- package/dist/components/form-controls/Checkbox.d.ts +14 -0
- package/dist/components/form-controls/Checkbox.d.ts.map +1 -0
- package/dist/components/form-controls/Checkbox.js +79 -0
- package/dist/components/form-controls/DateInput.d.ts +24 -4
- package/dist/components/form-controls/DateInput.d.ts.map +1 -1
- package/dist/components/form-controls/DateInput.js +492 -70
- package/dist/components/form-controls/DatePicker.d.ts +4 -3
- package/dist/components/form-controls/DatePicker.d.ts.map +1 -1
- package/dist/components/form-controls/DatePicker.js +26 -30
- package/dist/components/form-controls/Input.d.ts +10 -1
- package/dist/components/form-controls/Input.d.ts.map +1 -1
- package/dist/components/form-controls/Input.js +17 -10
- package/dist/components/form-controls/LinkButton.d.ts +15 -0
- package/dist/components/form-controls/LinkButton.d.ts.map +1 -0
- package/dist/components/form-controls/LinkButton.js +248 -0
- package/dist/components/form-controls/Pagination.d.ts +1 -0
- package/dist/components/form-controls/Pagination.d.ts.map +1 -1
- package/dist/components/form-controls/Pagination.js +3 -40
- package/dist/components/form-controls/RadioButtonGroup.d.ts +62 -0
- package/dist/components/form-controls/RadioButtonGroup.d.ts.map +1 -0
- package/dist/components/form-controls/RadioButtonGroup.js +220 -0
- package/dist/components/form-controls/SearchSelectInput-OLD.d.ts +68 -0
- package/dist/components/form-controls/SearchSelectInput-OLD.d.ts.map +1 -0
- package/dist/components/form-controls/SearchSelectInput-OLD.js +963 -0
- package/dist/components/form-controls/SearchSelectInput.d.ts +70 -0
- package/dist/components/form-controls/SearchSelectInput.d.ts.map +1 -0
- package/dist/components/form-controls/SearchSelectInput.js +336 -0
- package/dist/components/form-controls/index.d.ts +9 -1
- package/dist/components/form-controls/index.d.ts.map +1 -1
- package/dist/components/form-controls/index.js +4 -0
- package/dist/components/layout/Accordion.d.ts +13 -0
- package/dist/components/layout/Accordion.d.ts.map +1 -0
- package/dist/components/layout/Accordion.js +67 -0
- package/dist/components/layout/AppLayout.d.ts +3 -2
- package/dist/components/layout/AppLayout.d.ts.map +1 -1
- package/dist/components/layout/AppLayout.js +104 -31
- package/dist/components/layout/Card.d.ts +8 -3
- package/dist/components/layout/Card.d.ts.map +1 -1
- package/dist/components/layout/Card.js +18 -19
- package/dist/components/layout/Collection.js +1 -1
- package/dist/components/layout/DataTable.d.ts +3 -1
- package/dist/components/layout/DataTable.d.ts.map +1 -1
- package/dist/components/layout/DataTable.js +34 -29
- package/dist/components/layout/index.d.ts +2 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/layout/index.js +1 -0
- package/dist/components/utils/Avatar.d.ts +49 -0
- package/dist/components/utils/Avatar.d.ts.map +1 -0
- package/dist/components/utils/Avatar.js +93 -0
- package/dist/components/utils/Badge.d.ts +3 -0
- package/dist/components/utils/Badge.d.ts.map +1 -1
- package/dist/components/utils/Badge.js +131 -26
- package/dist/components/utils/Dialog.d.ts.map +1 -1
- package/dist/components/utils/Dialog.js +6 -1
- package/dist/components/utils/Filter.d.ts +57 -0
- package/dist/components/utils/Filter.d.ts.map +1 -0
- package/dist/components/utils/Filter.js +581 -0
- package/dist/components/utils/FiltersDialog.d.ts +21 -0
- package/dist/components/utils/FiltersDialog.d.ts.map +1 -0
- package/dist/components/utils/FiltersDialog.js +104 -0
- package/dist/components/utils/Loader.js +2 -2
- package/dist/components/utils/RoadMap.d.ts +59 -0
- package/dist/components/utils/RoadMap.d.ts.map +1 -0
- package/dist/components/utils/RoadMap.js +139 -0
- package/dist/components/utils/Snackbar.d.ts +13 -0
- package/dist/components/utils/Snackbar.d.ts.map +1 -0
- package/dist/components/utils/Snackbar.js +122 -0
- package/dist/components/utils/SnackbarContainer.d.ts +7 -0
- package/dist/components/utils/SnackbarContainer.d.ts.map +1 -0
- package/dist/components/utils/SnackbarContainer.js +25 -0
- package/dist/components/utils/iconUtils.d.ts +16 -0
- package/dist/components/utils/iconUtils.d.ts.map +1 -0
- package/dist/components/utils/iconUtils.js +40 -0
- package/dist/components/utils/index.d.ts +12 -0
- package/dist/components/utils/index.d.ts.map +1 -1
- package/dist/components/utils/index.js +6 -0
- package/dist/contexts/AppLayoutContext.d.ts +40 -0
- package/dist/contexts/AppLayoutContext.d.ts.map +1 -0
- package/dist/contexts/AppLayoutContext.js +98 -0
- package/dist/contexts/ListCrudContext.d.ts +50 -0
- package/dist/contexts/ListCrudContext.d.ts.map +1 -0
- package/dist/contexts/ListCrudContext.js +253 -0
- package/dist/contexts/SnackbarContext.d.ts +26 -0
- package/dist/contexts/SnackbarContext.d.ts.map +1 -0
- package/dist/contexts/SnackbarContext.js +34 -0
- package/dist/contexts/index.d.ts +6 -0
- package/dist/contexts/index.d.ts.map +1 -1
- package/dist/contexts/index.js +6 -0
- package/dist/contexts/presets.js +6 -6
- package/dist/docs/AccordionDocs.d.ts +4 -0
- package/dist/docs/AccordionDocs.d.ts.map +1 -0
- package/dist/docs/AccordionDocs.js +21 -0
- package/dist/docs/AuthDocs.tsx/AuthDocsContent.js +3 -5
- package/dist/docs/AutocompleteInputDocs.js +1 -1
- package/dist/docs/AvatarDocs.d.ts +4 -0
- package/dist/docs/AvatarDocs.d.ts.map +1 -0
- package/dist/docs/AvatarDocs.js +7 -0
- package/dist/docs/BadgeDocs.d.ts.map +1 -1
- package/dist/docs/BadgeDocs.js +4 -2
- package/dist/docs/ButtonDocs.d.ts.map +1 -1
- package/dist/docs/ButtonDocs.js +1 -1
- package/dist/docs/CardDocs.d.ts.map +1 -1
- package/dist/docs/CardDocs.js +17 -8
- package/dist/docs/CheckboxDocs.d.ts +4 -0
- package/dist/docs/CheckboxDocs.d.ts.map +1 -0
- package/dist/docs/CheckboxDocs.js +7 -0
- package/dist/docs/DataTableDocs.d.ts.map +1 -1
- package/dist/docs/DataTableDocs.js +9 -5
- package/dist/docs/DateInputDocs.d.ts +1 -0
- package/dist/docs/DateInputDocs.d.ts.map +1 -1
- package/dist/docs/DateInputDocs.js +7 -9
- package/dist/docs/DatePickerDocs.d.ts +1 -0
- package/dist/docs/DatePickerDocs.d.ts.map +1 -1
- package/dist/docs/DatePickerDocs.js +6 -8
- package/dist/docs/DialogDocs.js +1 -1
- package/dist/docs/DocAdmin.d.ts +4 -0
- package/dist/docs/DocAdmin.d.ts.map +1 -0
- package/dist/docs/DocAdmin.js +68 -0
- package/dist/docs/DocsMenu.d.ts.map +1 -1
- package/dist/docs/DocsMenu.js +3 -3
- package/dist/docs/DocsRouter.d.ts.map +1 -1
- package/dist/docs/DocsRouter.js +13 -1
- package/dist/docs/DropdownMenuDocs.js +1 -1
- package/dist/docs/ExampleFormDocs.d.ts +4 -0
- package/dist/docs/ExampleFormDocs.d.ts.map +1 -0
- package/dist/docs/ExampleFormDocs.js +148 -0
- package/dist/docs/FilterDocs.d.ts +4 -0
- package/dist/docs/FilterDocs.d.ts.map +1 -0
- package/dist/docs/FilterDocs.js +112 -0
- package/dist/docs/InputDocs.d.ts.map +1 -1
- package/dist/docs/InputDocs.js +11 -1
- package/dist/docs/LinkButtonDocs.d.ts +4 -0
- package/dist/docs/LinkButtonDocs.d.ts.map +1 -0
- package/dist/docs/LinkButtonDocs.js +7 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.d.ts +2 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.d.ts.map +1 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.js +29 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresas.d.ts +2 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresas.d.ts.map +1 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresas.js +7 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.d.ts +2 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.d.ts.map +1 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.js +57 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsEditDialog.d.ts +9 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsEditDialog.d.ts.map +1 -0
- package/dist/docs/ListCrudDocs.tsx/ListCrudDocsEditDialog.js +30 -0
- package/dist/docs/PaginationDocs.js +6 -6
- package/dist/docs/RadioButtonGroupDocs.d.ts +4 -0
- package/dist/docs/RadioButtonGroupDocs.d.ts.map +1 -0
- package/dist/docs/RadioButtonGroupDocs.js +46 -0
- package/dist/docs/RoadMapDocs.d.ts +4 -0
- package/dist/docs/RoadMapDocs.d.ts.map +1 -0
- package/dist/docs/RoadMapDocs.js +171 -0
- package/dist/docs/SearchSelectInputDocs.d.ts +4 -0
- package/dist/docs/SearchSelectInputDocs.d.ts.map +1 -0
- package/dist/docs/SearchSelectInputDocs.js +168 -0
- package/dist/docs/SnackbarDocs.d.ts +4 -0
- package/dist/docs/SnackbarDocs.d.ts.map +1 -0
- package/dist/docs/SnackbarDocs.js +50 -0
- package/dist/docs/TabsGroupDocs.d.ts.map +1 -1
- package/dist/docs/TabsGroupDocs.js +12 -1
- package/dist/docs/docMockServices/empresaService.d.ts +38 -0
- package/dist/docs/docMockServices/empresaService.d.ts.map +1 -0
- package/dist/docs/docMockServices/empresaService.js +117 -0
- package/dist/docs/docMockServices/index.d.ts +9 -0
- package/dist/docs/docMockServices/index.d.ts.map +1 -0
- package/dist/docs/docMockServices/index.js +8 -0
- package/dist/docs/docMockServices/initialData.d.ts +6 -0
- package/dist/docs/docMockServices/initialData.d.ts.map +1 -0
- package/dist/docs/docMockServices/initialData.js +132 -0
- package/dist/docs/docMockServices/interfaces.d.ts +26 -0
- package/dist/docs/docMockServices/interfaces.d.ts.map +1 -0
- package/dist/docs/docMockServices/interfaces.js +1 -0
- package/dist/docs/docMockServices/personaEmpresaService.d.ts +43 -0
- package/dist/docs/docMockServices/personaEmpresaService.d.ts.map +1 -0
- package/dist/docs/docMockServices/personaEmpresaService.js +113 -0
- package/dist/docs/docMockServices/personaService.d.ts +39 -0
- package/dist/docs/docMockServices/personaService.d.ts.map +1 -0
- package/dist/docs/docMockServices/personaService.js +181 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useAsyncRequest.d.ts +17 -0
- package/dist/hooks/useAsyncRequest.d.ts.map +1 -0
- package/dist/hooks/useAsyncRequest.js +70 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/templates/forms/ContactForm.js +2 -2
- package/dist/templates/forms/LoginForm.js +1 -1
- package/dist/templates/forms/RegistrationForm.js +1 -1
- package/dist/templates/layouts/SidebarLayout.d.ts.map +1 -1
- package/dist/templates/layouts/SidebarLayout.js +3 -2
- package/dist/templates/patterns/FormPattern.d.ts.map +1 -1
- package/dist/templates/patterns/FormPattern.js +4 -3
- package/package.json +5 -2
|
@@ -1,14 +1,39 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import React from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
4
|
+
import dayjs, {} from "dayjs";
|
|
5
|
+
import { useFormContext } from "react-hook-form";
|
|
3
6
|
import { Input } from "./Input";
|
|
4
7
|
import { DatePicker } from "./DatePicker";
|
|
5
8
|
const pad = (value) => value.toString().padStart(2, "0");
|
|
9
|
+
const isDayjs = (value) => {
|
|
10
|
+
return (value !== null &&
|
|
11
|
+
value !== undefined &&
|
|
12
|
+
typeof value === "object" &&
|
|
13
|
+
"isValid" in value &&
|
|
14
|
+
typeof value.isValid === "function");
|
|
15
|
+
};
|
|
16
|
+
const normalizeToDayjs = (value) => {
|
|
17
|
+
if (value === null || value === undefined)
|
|
18
|
+
return null;
|
|
19
|
+
if (isDayjs(value))
|
|
20
|
+
return value;
|
|
21
|
+
// Si no es Dayjs, intentar convertirlo
|
|
22
|
+
if (typeof value === "string" ||
|
|
23
|
+
typeof value === "number" ||
|
|
24
|
+
value instanceof Date) {
|
|
25
|
+
const d = dayjs(value);
|
|
26
|
+
return d.isValid() ? d : null;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
};
|
|
6
30
|
const formatDateToString = (date, format) => {
|
|
7
|
-
|
|
31
|
+
const normalized = normalizeToDayjs(date);
|
|
32
|
+
if (!normalized || !normalized.isValid())
|
|
8
33
|
return "";
|
|
9
|
-
const day = pad(date
|
|
10
|
-
const month = pad(
|
|
11
|
-
const year =
|
|
34
|
+
const day = pad(normalized.date());
|
|
35
|
+
const month = pad(normalized.month() + 1);
|
|
36
|
+
const year = normalized.year().toString();
|
|
12
37
|
if (format === "mm/dd/yyyy") {
|
|
13
38
|
return `${month}/${day}/${year}`;
|
|
14
39
|
}
|
|
@@ -33,11 +58,15 @@ const parseDateFromString = (value, format) => {
|
|
|
33
58
|
month <= 12 &&
|
|
34
59
|
year >= 1000 &&
|
|
35
60
|
year <= 9999) {
|
|
36
|
-
const date =
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
date
|
|
40
|
-
|
|
61
|
+
const date = dayjs()
|
|
62
|
+
.year(year)
|
|
63
|
+
.month(month - 1)
|
|
64
|
+
.date(day);
|
|
65
|
+
if (date.isValid() &&
|
|
66
|
+
date.year() === year &&
|
|
67
|
+
date.month() === month - 1 &&
|
|
68
|
+
date.date() === day) {
|
|
69
|
+
return date.startOf("day");
|
|
41
70
|
}
|
|
42
71
|
}
|
|
43
72
|
}
|
|
@@ -57,100 +86,493 @@ const parseDateFromString = (value, format) => {
|
|
|
57
86
|
month > 12) {
|
|
58
87
|
return null;
|
|
59
88
|
}
|
|
60
|
-
const date =
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
date
|
|
89
|
+
const date = dayjs()
|
|
90
|
+
.year(year)
|
|
91
|
+
.month(month - 1)
|
|
92
|
+
.date(day);
|
|
93
|
+
if (!date.isValid() ||
|
|
94
|
+
date.year() !== year ||
|
|
95
|
+
date.month() !== month - 1 ||
|
|
96
|
+
date.date() !== day) {
|
|
64
97
|
return null;
|
|
65
98
|
}
|
|
66
|
-
return date;
|
|
99
|
+
return date.startOf("day");
|
|
67
100
|
};
|
|
68
|
-
export const DateInput = ({ value, onChange, format = "dd/mm/yyyy", datePickerProps, icon = "fa-calendar-alt", iconPosition = "right", className = "", ...inputProps }) => {
|
|
69
|
-
|
|
70
|
-
const
|
|
101
|
+
export const DateInput = React.forwardRef(({ value, onChange, format = "dd/mm/yyyy", datePickerProps, icon = "fa-calendar-alt", iconPosition = "right", className = "", readOnly = false, ...inputProps }, ref) => {
|
|
102
|
+
// Extraer onBlur de inputProps para manejarlo por separado
|
|
103
|
+
const { onBlur: registerOnBlur, ...restInputProps } = inputProps;
|
|
104
|
+
// Detectar si estamos en modo register: si viene 'name' de register, estamos en modo register
|
|
105
|
+
// register siempre pasa 'name', 'onChange', 'onBlur', y 'ref'
|
|
106
|
+
const isRegisterMode = React.useMemo(() => {
|
|
107
|
+
// Si viene 'name' en inputProps, es porque viene de register
|
|
108
|
+
return "name" in inputProps && inputProps.name !== undefined;
|
|
109
|
+
}, [inputProps]);
|
|
110
|
+
const fieldName = isRegisterMode && "name" in restInputProps
|
|
111
|
+
? restInputProps.name
|
|
112
|
+
: undefined;
|
|
113
|
+
// Obtener setValue del contexto del formulario
|
|
114
|
+
// useFormContext debe llamarse incondicionalmente (requisito de React Hooks)
|
|
115
|
+
// Si no hay FormProvider, esto lanzará un error
|
|
116
|
+
// Para usar sin FormProvider, el componente guardará el valor Dayjs serializado (ISO string)
|
|
117
|
+
// en el input, y se puede parsear de vuelta a Dayjs cuando se lee el valor del formulario
|
|
118
|
+
const formContext = useFormContext();
|
|
119
|
+
const setValue = formContext?.setValue;
|
|
120
|
+
const [internalDate, setInternalDate] = React.useState(null);
|
|
121
|
+
const [displayValue, setDisplayValue] = React.useState("");
|
|
71
122
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
123
|
+
const [pickerPosition, setPickerPosition] = React.useState(null);
|
|
72
124
|
const containerRef = React.useRef(null);
|
|
73
|
-
const
|
|
74
|
-
const
|
|
125
|
+
const pickerRef = React.useRef(null);
|
|
126
|
+
const inputRef = React.useRef(null);
|
|
127
|
+
const hiddenInputRef = React.useRef(null);
|
|
128
|
+
const isTypingRef = React.useRef(false);
|
|
129
|
+
const lastDayjsValueRef = React.useRef(null);
|
|
130
|
+
// Función helper para sincronizar displayValue con el valor del formulario en modo register
|
|
131
|
+
const syncDisplayValue = React.useCallback(() => {
|
|
132
|
+
if (isRegisterMode && inputRef.current) {
|
|
133
|
+
const formValue = inputRef.current.value;
|
|
134
|
+
if (formValue) {
|
|
135
|
+
// Parsear el string de fecha del formulario
|
|
136
|
+
const parsed = parseDateFromString(formValue, format);
|
|
137
|
+
if (parsed) {
|
|
138
|
+
setDisplayValue(formatDateToString(parsed, format));
|
|
139
|
+
setInternalDate(parsed);
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// Si hay un valor pero no se puede parsear, mostrarlo tal cual
|
|
144
|
+
setDisplayValue(formValue);
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
setDisplayValue("");
|
|
150
|
+
setInternalDate(null);
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}, [isRegisterMode, format]);
|
|
156
|
+
// Sincronizar displayValue con el valor del formulario en modo register
|
|
75
157
|
React.useEffect(() => {
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
158
|
+
if (isRegisterMode) {
|
|
159
|
+
let attempts = 0;
|
|
160
|
+
const maxAttempts = 50; // Intentar durante ~5 segundos (50 * 100ms)
|
|
161
|
+
const trySync = () => {
|
|
162
|
+
if (inputRef.current) {
|
|
163
|
+
const formValue = inputRef.current.value;
|
|
164
|
+
if (formValue) {
|
|
165
|
+
const parsed = parseDateFromString(formValue, format);
|
|
166
|
+
if (parsed) {
|
|
167
|
+
setDisplayValue(formatDateToString(parsed, format));
|
|
168
|
+
setInternalDate(parsed);
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
setDisplayValue(formValue);
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
setDisplayValue("");
|
|
178
|
+
setInternalDate(null);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return false;
|
|
182
|
+
};
|
|
183
|
+
// Intentar inmediatamente
|
|
184
|
+
if (trySync()) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
// Si no encontramos el valor, usar un intervalo
|
|
188
|
+
const intervalId = window.setInterval(() => {
|
|
189
|
+
attempts++;
|
|
190
|
+
if (trySync() || attempts >= maxAttempts) {
|
|
191
|
+
clearInterval(intervalId);
|
|
192
|
+
}
|
|
193
|
+
}, 100);
|
|
194
|
+
// También usar timeouts como fallback
|
|
195
|
+
const timeouts = [];
|
|
196
|
+
[0, 50, 100, 200, 500, 1000].forEach((delay) => {
|
|
197
|
+
const timeoutId = window.setTimeout(() => {
|
|
198
|
+
trySync();
|
|
199
|
+
}, delay);
|
|
200
|
+
timeouts.push(timeoutId);
|
|
201
|
+
});
|
|
202
|
+
return () => {
|
|
203
|
+
clearInterval(intervalId);
|
|
204
|
+
timeouts.forEach(clearTimeout);
|
|
205
|
+
};
|
|
79
206
|
}
|
|
80
|
-
}, [
|
|
81
|
-
//
|
|
207
|
+
}, [isRegisterMode, format]);
|
|
208
|
+
// También escuchar cambios en el input nativo para sincronizar cuando cambie
|
|
82
209
|
React.useEffect(() => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
210
|
+
if (isRegisterMode && inputRef.current) {
|
|
211
|
+
const input = inputRef.current;
|
|
212
|
+
const handleInputSync = () => {
|
|
213
|
+
syncDisplayValue();
|
|
214
|
+
};
|
|
215
|
+
input.addEventListener("input", handleInputSync);
|
|
216
|
+
input.addEventListener("change", handleInputSync);
|
|
217
|
+
const observer = new MutationObserver(() => {
|
|
218
|
+
syncDisplayValue();
|
|
219
|
+
});
|
|
220
|
+
observer.observe(input, {
|
|
221
|
+
attributes: true,
|
|
222
|
+
attributeFilter: ["value"],
|
|
223
|
+
});
|
|
224
|
+
return () => {
|
|
225
|
+
input.removeEventListener("input", handleInputSync);
|
|
226
|
+
input.removeEventListener("change", handleInputSync);
|
|
227
|
+
observer.disconnect();
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}, [isRegisterMode, syncDisplayValue]);
|
|
231
|
+
// Sincronizar con el valor del formulario
|
|
232
|
+
React.useEffect(() => {
|
|
233
|
+
if (!isTypingRef.current) {
|
|
234
|
+
if (isRegisterMode) {
|
|
235
|
+
// En modo register con setValue, leer del formulario
|
|
236
|
+
if (formContext && fieldName) {
|
|
237
|
+
const formValue = formContext.watch(fieldName);
|
|
238
|
+
const normalized = normalizeToDayjs(formValue);
|
|
239
|
+
setInternalDate(normalized);
|
|
240
|
+
if (normalized) {
|
|
241
|
+
setDisplayValue(formatDateToString(normalized, format));
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
setDisplayValue("");
|
|
245
|
+
}
|
|
94
246
|
}
|
|
247
|
+
// Si no hay setValue, syncDisplayValue se encarga de sincronizar desde el input nativo
|
|
95
248
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
249
|
+
else {
|
|
250
|
+
// Modo Controller, sincronizar con el valor Dayjs
|
|
251
|
+
const normalized = normalizeToDayjs(value);
|
|
252
|
+
setInternalDate(normalized);
|
|
253
|
+
if (normalized) {
|
|
254
|
+
setDisplayValue(formatDateToString(normalized, format));
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
setDisplayValue("");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}, [value, format, isRegisterMode, setValue, fieldName, formContext]);
|
|
262
|
+
// Determinar el valor a mostrar en el input
|
|
263
|
+
const inputValue = isRegisterMode ? displayValue : displayValue;
|
|
105
264
|
const handleDateChange = (date) => {
|
|
106
|
-
if (
|
|
265
|
+
if (readOnly)
|
|
266
|
+
return;
|
|
267
|
+
const dateString = formatDateToString(date, format);
|
|
268
|
+
if (isRegisterMode) {
|
|
269
|
+
// En modo register, usar setValue si está disponible para guardar el objeto Dayjs
|
|
270
|
+
// Si no está disponible, guardar como string (comportamiento por defecto)
|
|
271
|
+
if (setValue && fieldName) {
|
|
272
|
+
// Usar setValue para guardar el objeto Dayjs directamente
|
|
273
|
+
setValue(fieldName, date, {
|
|
274
|
+
shouldValidate: true,
|
|
275
|
+
shouldDirty: true,
|
|
276
|
+
shouldTouch: true,
|
|
277
|
+
});
|
|
278
|
+
// Actualizar el displayValue y el estado interno
|
|
279
|
+
setDisplayValue(dateString);
|
|
280
|
+
setInternalDate(date);
|
|
281
|
+
// Actualizar el input nativo con el valor visual (string) para mantener la sincronización
|
|
282
|
+
// Esto es solo para mostrar el valor correcto en el input
|
|
283
|
+
// El valor real (Dayjs) ya está guardado en el formulario vía setValue
|
|
284
|
+
if (inputRef.current) {
|
|
285
|
+
const nativeInput = inputRef.current;
|
|
286
|
+
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set;
|
|
287
|
+
if (nativeInputValueSetter) {
|
|
288
|
+
nativeInputValueSetter.call(nativeInput, dateString);
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
nativeInput.value = dateString;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
// Sin FormProvider: guardar el valor Dayjs serializado (ISO string) en el input
|
|
297
|
+
// react-hook-form leerá este valor y se puede parsear de vuelta a Dayjs cuando se lee
|
|
298
|
+
lastDayjsValueRef.current = date;
|
|
299
|
+
// Guardar el valor Dayjs serializado (como ISO string)
|
|
300
|
+
const dayjsIsoString = date ? date.toISOString() : "";
|
|
301
|
+
// Actualizar el input visible con el valor Dayjs serializado
|
|
302
|
+
// Esto es lo que react-hook-form leerá y guardará
|
|
303
|
+
if (inputRef.current) {
|
|
304
|
+
const nativeInput = inputRef.current;
|
|
305
|
+
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set;
|
|
306
|
+
if (nativeInputValueSetter) {
|
|
307
|
+
nativeInputValueSetter.call(nativeInput, dayjsIsoString);
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
nativeInput.value = dayjsIsoString;
|
|
311
|
+
}
|
|
312
|
+
// Llamar al onChange de register con el valor Dayjs serializado
|
|
313
|
+
if (onChange) {
|
|
314
|
+
const changeEvent = {
|
|
315
|
+
target: nativeInput,
|
|
316
|
+
currentTarget: nativeInput,
|
|
317
|
+
};
|
|
318
|
+
onChange(changeEvent);
|
|
319
|
+
}
|
|
320
|
+
// Disparar eventos nativos
|
|
321
|
+
const inputEvent = new Event("input", {
|
|
322
|
+
bubbles: true,
|
|
323
|
+
cancelable: true,
|
|
324
|
+
});
|
|
325
|
+
nativeInput.dispatchEvent(inputEvent);
|
|
326
|
+
const changeEventNative = new Event("change", {
|
|
327
|
+
bubbles: true,
|
|
328
|
+
cancelable: true,
|
|
329
|
+
});
|
|
330
|
+
nativeInput.dispatchEvent(changeEventNative);
|
|
331
|
+
}
|
|
332
|
+
// Actualizar el displayValue con el string formateado para mostrar al usuario
|
|
333
|
+
// Aunque el input tiene el ISO string, mostramos el formato legible
|
|
334
|
+
setDisplayValue(dateString);
|
|
335
|
+
setInternalDate(date);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
// Modo Controller - comportamiento original
|
|
107
340
|
setInternalDate(date);
|
|
108
|
-
|
|
341
|
+
setDisplayValue(dateString);
|
|
342
|
+
if (onChange) {
|
|
343
|
+
const dayjsHandler = onChange;
|
|
344
|
+
dayjsHandler(date);
|
|
345
|
+
}
|
|
109
346
|
}
|
|
110
|
-
onChange?.(date);
|
|
111
347
|
setIsOpen(false);
|
|
112
348
|
};
|
|
113
349
|
const handleInputChange = (event) => {
|
|
350
|
+
if (readOnly)
|
|
351
|
+
return;
|
|
114
352
|
const newValue = event.target.value;
|
|
115
|
-
|
|
116
|
-
|
|
353
|
+
isTypingRef.current = true;
|
|
354
|
+
if (isRegisterMode) {
|
|
355
|
+
// En modo register, actualizar el displayValue mientras el usuario escribe
|
|
356
|
+
setDisplayValue(newValue);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
// Modo Controller
|
|
360
|
+
setDisplayValue(newValue);
|
|
361
|
+
}
|
|
117
362
|
};
|
|
118
363
|
const handleInputBlur = (event) => {
|
|
364
|
+
isTypingRef.current = false;
|
|
119
365
|
const newValue = event.target.value.trim();
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
366
|
+
if (isRegisterMode) {
|
|
367
|
+
// En modo register, validar y actualizar el input nativo
|
|
368
|
+
if (!newValue) {
|
|
369
|
+
// Limpiar el valor
|
|
370
|
+
if (setValue && fieldName) {
|
|
371
|
+
// Si tenemos setValue, usar setValue para limpiar el valor como null
|
|
372
|
+
setValue(fieldName, null, {
|
|
373
|
+
shouldValidate: true,
|
|
374
|
+
shouldDirty: true,
|
|
375
|
+
shouldTouch: true,
|
|
376
|
+
});
|
|
377
|
+
setDisplayValue("");
|
|
378
|
+
setInternalDate(null);
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
// Fallback: actualizar el input nativo
|
|
382
|
+
if (inputRef.current) {
|
|
383
|
+
const nativeInput = inputRef.current;
|
|
384
|
+
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set;
|
|
385
|
+
setter?.call(nativeInput, "");
|
|
386
|
+
if (onChange) {
|
|
387
|
+
const changeEvent = {
|
|
388
|
+
target: nativeInput,
|
|
389
|
+
currentTarget: nativeInput,
|
|
390
|
+
};
|
|
391
|
+
onChange(changeEvent);
|
|
392
|
+
}
|
|
393
|
+
const inputEvent = new Event("input", { bubbles: true });
|
|
394
|
+
nativeInput.dispatchEvent(inputEvent);
|
|
395
|
+
const changeEventNative = new Event("change", { bubbles: true });
|
|
396
|
+
nativeInput.dispatchEvent(changeEventNative);
|
|
397
|
+
}
|
|
398
|
+
setDisplayValue("");
|
|
399
|
+
setInternalDate(null);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
const parsed = parseDateFromString(newValue, format);
|
|
404
|
+
if (parsed) {
|
|
405
|
+
// handleDateChange ya usa setValue si está disponible
|
|
406
|
+
handleDateChange(parsed);
|
|
407
|
+
// Si usamos setValue, asegurarse de que el valor Dayjs se mantenga
|
|
408
|
+
// después del blur, ya que registerOnBlur puede causar que react-hook-form
|
|
409
|
+
// lea el valor del input como string
|
|
410
|
+
if (setValue && fieldName && registerOnBlur) {
|
|
411
|
+
// Guardar el valor Dayjs antes de llamar a registerOnBlur
|
|
412
|
+
const dayjsValue = parsed;
|
|
413
|
+
// Llamar al onBlur de register
|
|
414
|
+
registerOnBlur(event);
|
|
415
|
+
// Usar setTimeout para asegurarse de que esto se ejecuta después de registerOnBlur
|
|
416
|
+
// y restaurar el valor Dayjs si react-hook-form lo sobrescribió con un string
|
|
417
|
+
setTimeout(() => {
|
|
418
|
+
const currentValue = formContext?.watch(fieldName);
|
|
419
|
+
// Si el valor actual es un string en lugar de Dayjs, restaurarlo
|
|
420
|
+
if (currentValue && typeof currentValue === "string") {
|
|
421
|
+
setValue(fieldName, dayjsValue, {
|
|
422
|
+
shouldValidate: true,
|
|
423
|
+
shouldDirty: true,
|
|
424
|
+
shouldTouch: true,
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
}, 0);
|
|
428
|
+
// No llamar a registerOnBlur más abajo ya que ya lo llamamos aquí
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
// Si no es válida, restaurar el valor anterior
|
|
434
|
+
const previousValue = inputRef.current?.value || "";
|
|
435
|
+
setDisplayValue(previousValue);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
// Llamar al onBlur de register si existe
|
|
439
|
+
// Solo se llama aquí si no usamos setValue o si no había un valor válido
|
|
440
|
+
if (registerOnBlur) {
|
|
441
|
+
registerOnBlur(event);
|
|
442
|
+
}
|
|
127
443
|
}
|
|
128
444
|
else {
|
|
129
|
-
//
|
|
130
|
-
|
|
445
|
+
// Modo Controller
|
|
446
|
+
if (!newValue) {
|
|
447
|
+
handleDateChange(null);
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
const parsed = parseDateFromString(newValue, format);
|
|
451
|
+
if (parsed) {
|
|
452
|
+
handleDateChange(parsed);
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
// Si no es válida, restaurar el valor anterior formateado
|
|
456
|
+
setDisplayValue(formatDateToString(internalDate, format));
|
|
457
|
+
}
|
|
458
|
+
}
|
|
131
459
|
}
|
|
132
460
|
};
|
|
133
461
|
const handleIconClick = (event) => {
|
|
462
|
+
if (readOnly)
|
|
463
|
+
return;
|
|
134
464
|
event.preventDefault();
|
|
135
465
|
setIsOpen((prev) => !prev);
|
|
136
466
|
};
|
|
137
467
|
React.useEffect(() => {
|
|
468
|
+
if (!isOpen)
|
|
469
|
+
return;
|
|
138
470
|
const handleClickOutside = (event) => {
|
|
139
|
-
|
|
140
|
-
|
|
471
|
+
const target = event.target;
|
|
472
|
+
const isClickInsideContainer = containerRef.current?.contains(target);
|
|
473
|
+
const isClickInsidePicker = pickerRef.current?.contains(target);
|
|
474
|
+
if (!isClickInsideContainer && !isClickInsidePicker) {
|
|
141
475
|
setIsOpen(false);
|
|
142
476
|
}
|
|
143
477
|
};
|
|
144
|
-
|
|
478
|
+
// Pequeño delay para asegurar que el portal esté montado
|
|
479
|
+
const timer = setTimeout(() => {
|
|
480
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
481
|
+
}, 0);
|
|
145
482
|
return () => {
|
|
483
|
+
clearTimeout(timer);
|
|
146
484
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
147
485
|
};
|
|
148
|
-
}, []);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
486
|
+
}, [isOpen]);
|
|
487
|
+
// Combinar refs: el ref del componente y el ref interno
|
|
488
|
+
const combinedRef = React.useCallback((node) => {
|
|
489
|
+
inputRef.current = node;
|
|
490
|
+
if (typeof ref === "function") {
|
|
491
|
+
ref(node);
|
|
492
|
+
}
|
|
493
|
+
else if (ref) {
|
|
494
|
+
ref.current = node;
|
|
495
|
+
}
|
|
496
|
+
// Cuando el ref se establece en modo register, sincronizar el displayValue
|
|
497
|
+
if (isRegisterMode && node) {
|
|
498
|
+
[0, 10, 50, 100, 200, 500].forEach((delay) => {
|
|
499
|
+
setTimeout(() => {
|
|
500
|
+
if (node && inputRef.current === node) {
|
|
501
|
+
const formValue = node.value;
|
|
502
|
+
if (formValue) {
|
|
503
|
+
const parsed = parseDateFromString(formValue, format);
|
|
504
|
+
if (parsed) {
|
|
505
|
+
setDisplayValue(formatDateToString(parsed, format));
|
|
506
|
+
setInternalDate(parsed);
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
setDisplayValue(formValue);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}, delay);
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
}, [ref, isRegisterMode, format]);
|
|
517
|
+
const datePickerInitialViewDate = internalDate ?? datePickerProps?.initialViewDate ?? dayjs();
|
|
518
|
+
// Ocultar el ícono cuando está en modo readOnly
|
|
519
|
+
const displayIcon = readOnly ? undefined : icon;
|
|
520
|
+
const displayIconPosition = readOnly ? undefined : iconPosition;
|
|
521
|
+
const displayOnIconClick = readOnly ? undefined : handleIconClick;
|
|
522
|
+
// Verificar que estamos en el navegador
|
|
523
|
+
// Inicializar isMounted de forma síncrona si es posible
|
|
524
|
+
const [isMounted, setIsMounted] = React.useState(() => {
|
|
525
|
+
return typeof document !== "undefined" && !!document.body;
|
|
526
|
+
});
|
|
527
|
+
React.useEffect(() => {
|
|
528
|
+
if (!isMounted && typeof document !== "undefined" && document.body) {
|
|
529
|
+
setIsMounted(true);
|
|
530
|
+
}
|
|
531
|
+
}, [isMounted]);
|
|
532
|
+
// Actualizar posición del picker cuando se abre
|
|
533
|
+
React.useEffect(() => {
|
|
534
|
+
if (isOpen && !readOnly && containerRef.current && isMounted) {
|
|
535
|
+
const updatePosition = () => {
|
|
536
|
+
const rect = containerRef.current?.getBoundingClientRect();
|
|
537
|
+
if (rect) {
|
|
538
|
+
setPickerPosition({
|
|
539
|
+
top: rect.bottom + window.scrollY + 4,
|
|
540
|
+
left: rect.right + window.scrollX - 280, // Alinear a la derecha
|
|
541
|
+
width: rect.width,
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
updatePosition();
|
|
546
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
547
|
+
window.addEventListener("resize", updatePosition);
|
|
548
|
+
return () => {
|
|
549
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
550
|
+
window.removeEventListener("resize", updatePosition);
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
setPickerPosition(null);
|
|
555
|
+
}
|
|
556
|
+
}, [isOpen, readOnly, isMounted]);
|
|
557
|
+
return (_jsxs("div", { ref: containerRef, className: "relative w-full", children: [isRegisterMode && !setValue && fieldName && (_jsx("input", { ref: hiddenInputRef, type: "hidden", name: fieldName, value: lastDayjsValueRef.current
|
|
558
|
+
? lastDayjsValueRef.current.toISOString()
|
|
559
|
+
: "" })), _jsx(Input, { ...restInputProps, ref: combinedRef, type: "text", value: inputValue, onChange: handleInputChange, onBlur: handleInputBlur, icon: displayIcon, iconPosition: displayIconPosition, onIconClick: displayOnIconClick, placeholder: restInputProps.placeholder ??
|
|
560
|
+
(format === "mm/dd/yyyy" ? "mm/dd/yyyy" : "dd/mm/yyyy"), className: className, readOnly: readOnly }), (() => {
|
|
561
|
+
// Verificar de forma segura que document.body existe y es válido
|
|
562
|
+
const bodyElement = typeof document !== "undefined" &&
|
|
563
|
+
document.body &&
|
|
564
|
+
document.body instanceof HTMLElement
|
|
565
|
+
? document.body
|
|
566
|
+
: null;
|
|
567
|
+
return (!readOnly &&
|
|
568
|
+
isOpen &&
|
|
569
|
+
pickerPosition &&
|
|
570
|
+
isMounted &&
|
|
571
|
+
bodyElement &&
|
|
572
|
+
createPortal(_jsx("div", { ref: pickerRef, className: "fixed z-[2001] min-w-[280px] w-max", style: {
|
|
573
|
+
top: `${pickerPosition.top}px`,
|
|
574
|
+
left: `${pickerPosition.left}px`,
|
|
575
|
+
}, children: _jsx(DatePicker, { ...datePickerProps, value: internalDate ?? datePickerInitialViewDate, onChange: (date) => handleDateChange(date) }) }), bodyElement));
|
|
576
|
+
})()] }));
|
|
577
|
+
});
|
|
578
|
+
DateInput.displayName = "DateInput";
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { type Dayjs } from "dayjs";
|
|
2
3
|
export type DatePickerView = {
|
|
3
4
|
month: number;
|
|
4
5
|
year: number;
|
|
5
6
|
};
|
|
6
7
|
export interface DatePickerProps {
|
|
7
|
-
value?:
|
|
8
|
-
onChange?: (date:
|
|
9
|
-
initialViewDate?:
|
|
8
|
+
value?: Dayjs | null;
|
|
9
|
+
onChange?: (date: Dayjs) => void;
|
|
10
|
+
initialViewDate?: Dayjs;
|
|
10
11
|
startWeekOn?: "monday" | "sunday";
|
|
11
12
|
className?: string;
|
|
12
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatePicker.d.ts","sourceRoot":"","sources":["../../../src/components/form-controls/DatePicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"DatePicker.d.ts","sourceRoot":"","sources":["../../../src/components/form-controls/DatePicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAc,EAAE,KAAK,KAAK,EAAE,MAAM,OAAO,CAAC;AAG1C,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,eAAe,CAAC,EAAE,KAAK,CAAC;IACxB,WAAW,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAuBD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA8OhD,CAAC"}
|