flysoft-react-ui 0.4.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/dist/App.d.ts.map +1 -1
  2. package/dist/App.js +20 -4
  3. package/dist/components/form-controls/AutocompleteInput.d.ts +11 -3
  4. package/dist/components/form-controls/AutocompleteInput.d.ts.map +1 -1
  5. package/dist/components/form-controls/AutocompleteInput.js +410 -31
  6. package/dist/components/form-controls/Button.js +1 -1
  7. package/dist/components/form-controls/Checkbox.d.ts +14 -0
  8. package/dist/components/form-controls/Checkbox.d.ts.map +1 -0
  9. package/dist/components/form-controls/Checkbox.js +77 -0
  10. package/dist/components/form-controls/DateInput.d.ts +20 -4
  11. package/dist/components/form-controls/DateInput.d.ts.map +1 -1
  12. package/dist/components/form-controls/DateInput.js +425 -70
  13. package/dist/components/form-controls/DatePicker.d.ts +4 -3
  14. package/dist/components/form-controls/DatePicker.d.ts.map +1 -1
  15. package/dist/components/form-controls/DatePicker.js +26 -30
  16. package/dist/components/form-controls/Input.d.ts +10 -1
  17. package/dist/components/form-controls/Input.d.ts.map +1 -1
  18. package/dist/components/form-controls/Input.js +16 -10
  19. package/dist/components/form-controls/Pagination.d.ts +1 -0
  20. package/dist/components/form-controls/Pagination.d.ts.map +1 -1
  21. package/dist/components/form-controls/Pagination.js +3 -40
  22. package/dist/components/form-controls/RadioButtonGroup.d.ts +62 -0
  23. package/dist/components/form-controls/RadioButtonGroup.d.ts.map +1 -0
  24. package/dist/components/form-controls/RadioButtonGroup.js +220 -0
  25. package/dist/components/form-controls/SearchSelectInput-OLD.d.ts +68 -0
  26. package/dist/components/form-controls/SearchSelectInput-OLD.d.ts.map +1 -0
  27. package/dist/components/form-controls/SearchSelectInput-OLD.js +962 -0
  28. package/dist/components/form-controls/SearchSelectInput.d.ts +70 -0
  29. package/dist/components/form-controls/SearchSelectInput.d.ts.map +1 -0
  30. package/dist/components/form-controls/SearchSelectInput.js +335 -0
  31. package/dist/components/form-controls/index.d.ts +7 -1
  32. package/dist/components/form-controls/index.d.ts.map +1 -1
  33. package/dist/components/form-controls/index.js +3 -0
  34. package/dist/components/layout/AppLayout.d.ts +3 -2
  35. package/dist/components/layout/AppLayout.d.ts.map +1 -1
  36. package/dist/components/layout/AppLayout.js +104 -31
  37. package/dist/components/layout/Card.d.ts +4 -1
  38. package/dist/components/layout/Card.d.ts.map +1 -1
  39. package/dist/components/layout/Card.js +30 -1
  40. package/dist/components/layout/Collection.js +1 -1
  41. package/dist/components/layout/DataTable.d.ts +29 -0
  42. package/dist/components/layout/DataTable.d.ts.map +1 -0
  43. package/dist/components/layout/DataTable.js +165 -0
  44. package/dist/components/layout/index.d.ts +2 -0
  45. package/dist/components/layout/index.d.ts.map +1 -1
  46. package/dist/components/layout/index.js +1 -0
  47. package/dist/components/utils/Avatar.d.ts +49 -0
  48. package/dist/components/utils/Avatar.d.ts.map +1 -0
  49. package/dist/components/utils/Avatar.js +93 -0
  50. package/dist/components/utils/Badge.d.ts +3 -0
  51. package/dist/components/utils/Badge.d.ts.map +1 -1
  52. package/dist/components/utils/Badge.js +130 -26
  53. package/dist/components/utils/Dialog.d.ts.map +1 -1
  54. package/dist/components/utils/Dialog.js +5 -1
  55. package/dist/components/utils/DropdownMenu.d.ts +25 -0
  56. package/dist/components/utils/DropdownMenu.d.ts.map +1 -0
  57. package/dist/components/utils/DropdownMenu.js +145 -0
  58. package/dist/components/utils/Filter.d.ts +57 -0
  59. package/dist/components/utils/Filter.d.ts.map +1 -0
  60. package/dist/components/utils/Filter.js +580 -0
  61. package/dist/components/utils/FiltersDialog.d.ts +21 -0
  62. package/dist/components/utils/FiltersDialog.d.ts.map +1 -0
  63. package/dist/components/utils/FiltersDialog.js +104 -0
  64. package/dist/components/utils/Loader.js +1 -1
  65. package/dist/components/utils/RoadMap.d.ts +59 -0
  66. package/dist/components/utils/RoadMap.d.ts.map +1 -0
  67. package/dist/components/utils/RoadMap.js +138 -0
  68. package/dist/components/utils/Snackbar.d.ts +13 -0
  69. package/dist/components/utils/Snackbar.d.ts.map +1 -0
  70. package/dist/components/utils/Snackbar.js +121 -0
  71. package/dist/components/utils/SnackbarContainer.d.ts +7 -0
  72. package/dist/components/utils/SnackbarContainer.d.ts.map +1 -0
  73. package/dist/components/utils/SnackbarContainer.js +25 -0
  74. package/dist/components/utils/index.d.ts +12 -0
  75. package/dist/components/utils/index.d.ts.map +1 -1
  76. package/dist/components/utils/index.js +6 -0
  77. package/dist/contexts/AppLayoutContext.d.ts +40 -0
  78. package/dist/contexts/AppLayoutContext.d.ts.map +1 -0
  79. package/dist/contexts/AppLayoutContext.js +98 -0
  80. package/dist/contexts/ListCrudContext.d.ts +29 -0
  81. package/dist/contexts/ListCrudContext.d.ts.map +1 -0
  82. package/dist/contexts/ListCrudContext.js +209 -0
  83. package/dist/contexts/SnackbarContext.d.ts +26 -0
  84. package/dist/contexts/SnackbarContext.d.ts.map +1 -0
  85. package/dist/contexts/SnackbarContext.js +34 -0
  86. package/dist/contexts/index.d.ts +6 -0
  87. package/dist/contexts/index.d.ts.map +1 -1
  88. package/dist/contexts/index.js +6 -0
  89. package/dist/contexts/presets.js +6 -6
  90. package/dist/docs/AuthDocs.tsx/AuthDocsContent.js +3 -1
  91. package/dist/docs/AvatarDocs.d.ts +4 -0
  92. package/dist/docs/AvatarDocs.d.ts.map +1 -0
  93. package/dist/docs/AvatarDocs.js +7 -0
  94. package/dist/docs/BadgeDocs.d.ts.map +1 -1
  95. package/dist/docs/BadgeDocs.js +4 -2
  96. package/dist/docs/CardDocs.d.ts.map +1 -1
  97. package/dist/docs/CardDocs.js +7 -1
  98. package/dist/docs/CheckboxDocs.d.ts +4 -0
  99. package/dist/docs/CheckboxDocs.d.ts.map +1 -0
  100. package/dist/docs/CheckboxDocs.js +7 -0
  101. package/dist/docs/DataTableDocs.d.ts +4 -0
  102. package/dist/docs/DataTableDocs.d.ts.map +1 -0
  103. package/dist/docs/DataTableDocs.js +244 -0
  104. package/dist/docs/DateInputDocs.d.ts +1 -0
  105. package/dist/docs/DateInputDocs.d.ts.map +1 -1
  106. package/dist/docs/DateInputDocs.js +7 -9
  107. package/dist/docs/DatePickerDocs.d.ts +1 -0
  108. package/dist/docs/DatePickerDocs.d.ts.map +1 -1
  109. package/dist/docs/DatePickerDocs.js +6 -8
  110. package/dist/docs/DocAdmin.d.ts +4 -0
  111. package/dist/docs/DocAdmin.d.ts.map +1 -0
  112. package/dist/docs/DocAdmin.js +68 -0
  113. package/dist/docs/DocsMenu.d.ts.map +1 -1
  114. package/dist/docs/DocsMenu.js +1 -1
  115. package/dist/docs/DocsRouter.d.ts.map +1 -1
  116. package/dist/docs/DocsRouter.js +13 -1
  117. package/dist/docs/DropdownMenuDocs.d.ts +4 -0
  118. package/dist/docs/DropdownMenuDocs.d.ts.map +1 -0
  119. package/dist/docs/DropdownMenuDocs.js +66 -0
  120. package/dist/docs/ExampleFormDocs.d.ts +4 -0
  121. package/dist/docs/ExampleFormDocs.d.ts.map +1 -0
  122. package/dist/docs/ExampleFormDocs.js +148 -0
  123. package/dist/docs/FilterDocs.d.ts +4 -0
  124. package/dist/docs/FilterDocs.d.ts.map +1 -0
  125. package/dist/docs/FilterDocs.js +112 -0
  126. package/dist/docs/InputDocs.d.ts.map +1 -1
  127. package/dist/docs/InputDocs.js +11 -1
  128. package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.d.ts +11 -0
  129. package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.d.ts.map +1 -0
  130. package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.js +25 -0
  131. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.d.ts +2 -0
  132. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.d.ts.map +1 -0
  133. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.js +51 -0
  134. package/dist/docs/PaginationDocs.js +6 -6
  135. package/dist/docs/RadioButtonGroupDocs.d.ts +4 -0
  136. package/dist/docs/RadioButtonGroupDocs.d.ts.map +1 -0
  137. package/dist/docs/RadioButtonGroupDocs.js +46 -0
  138. package/dist/docs/RoadMapDocs.d.ts +4 -0
  139. package/dist/docs/RoadMapDocs.d.ts.map +1 -0
  140. package/dist/docs/RoadMapDocs.js +171 -0
  141. package/dist/docs/SearchSelectInputDocs.d.ts +4 -0
  142. package/dist/docs/SearchSelectInputDocs.d.ts.map +1 -0
  143. package/dist/docs/SearchSelectInputDocs.js +168 -0
  144. package/dist/docs/SnackbarDocs.d.ts +4 -0
  145. package/dist/docs/SnackbarDocs.d.ts.map +1 -0
  146. package/dist/docs/SnackbarDocs.js +50 -0
  147. package/dist/docs/TabsGroupDocs.d.ts.map +1 -1
  148. package/dist/docs/TabsGroupDocs.js +12 -1
  149. package/dist/docs/docMockServices/empresaService.d.ts +38 -0
  150. package/dist/docs/docMockServices/empresaService.d.ts.map +1 -0
  151. package/dist/docs/docMockServices/empresaService.js +116 -0
  152. package/dist/docs/docMockServices/index.d.ts +9 -0
  153. package/dist/docs/docMockServices/index.d.ts.map +1 -0
  154. package/dist/docs/docMockServices/index.js +8 -0
  155. package/dist/docs/docMockServices/initialData.d.ts +6 -0
  156. package/dist/docs/docMockServices/initialData.d.ts.map +1 -0
  157. package/dist/docs/docMockServices/initialData.js +132 -0
  158. package/dist/docs/docMockServices/interfaces.d.ts +26 -0
  159. package/dist/docs/docMockServices/interfaces.d.ts.map +1 -0
  160. package/dist/docs/docMockServices/interfaces.js +1 -0
  161. package/dist/docs/docMockServices/personaEmpresaService.d.ts +43 -0
  162. package/dist/docs/docMockServices/personaEmpresaService.d.ts.map +1 -0
  163. package/dist/docs/docMockServices/personaEmpresaService.js +113 -0
  164. package/dist/docs/docMockServices/personaService.d.ts +39 -0
  165. package/dist/docs/docMockServices/personaService.d.ts.map +1 -0
  166. package/dist/docs/docMockServices/personaService.js +180 -0
  167. package/dist/hooks/index.d.ts +2 -0
  168. package/dist/hooks/index.d.ts.map +1 -1
  169. package/dist/hooks/index.js +1 -0
  170. package/dist/hooks/useAsyncRequest.d.ts +17 -0
  171. package/dist/hooks/useAsyncRequest.d.ts.map +1 -0
  172. package/dist/hooks/useAsyncRequest.js +70 -0
  173. package/dist/index.css +1 -1
  174. package/dist/index.d.ts +22 -0
  175. package/dist/index.d.ts.map +1 -1
  176. package/dist/index.js +11 -0
  177. package/dist/index.js.map +1 -1
  178. 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
- if (!date)
31
+ const normalized = normalizeToDayjs(date);
32
+ if (!normalized || !normalized.isValid())
8
33
  return "";
9
- const day = pad(date.getDate());
10
- const month = pad(date.getMonth() + 1);
11
- const year = date.getFullYear().toString();
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 = new Date(year, month - 1, day);
37
- if (date.getFullYear() === year &&
38
- date.getMonth() === month - 1 &&
39
- date.getDate() === day) {
40
- return date;
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,426 @@ const parseDateFromString = (value, format) => {
57
86
  month > 12) {
58
87
  return null;
59
88
  }
60
- const date = new Date(year, month - 1, day);
61
- if (date.getFullYear() !== year ||
62
- date.getMonth() !== month - 1 ||
63
- date.getDate() !== day) {
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
- const [internalDate, setInternalDate] = React.useState(value ?? null);
70
- const [inputValue, setInputValue] = React.useState(formatDateToString(value ?? null, format));
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
+ // Para usar objetos Dayjs con register, el formulario debe estar dentro de FormProvider
115
+ // useFormContext debe llamarse incondicionalmente (requisito de React Hooks)
116
+ // Si no hay FormProvider y se usa en modo register, useFormContext lanzará un error
117
+ // Para usar sin FormProvider, usar Controller en lugar de register
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 inputWrapperRef = React.useRef(null);
74
- const iconRef = React.useRef(null);
125
+ const pickerRef = React.useRef(null);
126
+ const inputRef = React.useRef(null);
127
+ const isTypingRef = React.useRef(false);
128
+ // Función helper para sincronizar displayValue con el valor del formulario en modo register
129
+ const syncDisplayValue = React.useCallback(() => {
130
+ if (isRegisterMode && inputRef.current) {
131
+ const formValue = inputRef.current.value;
132
+ if (formValue) {
133
+ // Parsear el string de fecha del formulario
134
+ const parsed = parseDateFromString(formValue, format);
135
+ if (parsed) {
136
+ setDisplayValue(formatDateToString(parsed, format));
137
+ setInternalDate(parsed);
138
+ return true;
139
+ }
140
+ else {
141
+ // Si hay un valor pero no se puede parsear, mostrarlo tal cual
142
+ setDisplayValue(formValue);
143
+ return true;
144
+ }
145
+ }
146
+ else {
147
+ setDisplayValue("");
148
+ setInternalDate(null);
149
+ return false;
150
+ }
151
+ }
152
+ return false;
153
+ }, [isRegisterMode, format]);
154
+ // Sincronizar displayValue con el valor del formulario en modo register
75
155
  React.useEffect(() => {
76
- if (value !== undefined) {
77
- setInternalDate(value);
78
- setInputValue(formatDateToString(value, format));
156
+ if (isRegisterMode) {
157
+ let attempts = 0;
158
+ const maxAttempts = 50; // Intentar durante ~5 segundos (50 * 100ms)
159
+ const trySync = () => {
160
+ if (inputRef.current) {
161
+ const formValue = inputRef.current.value;
162
+ if (formValue) {
163
+ const parsed = parseDateFromString(formValue, format);
164
+ if (parsed) {
165
+ setDisplayValue(formatDateToString(parsed, format));
166
+ setInternalDate(parsed);
167
+ return true;
168
+ }
169
+ else {
170
+ setDisplayValue(formValue);
171
+ return true;
172
+ }
173
+ }
174
+ else {
175
+ setDisplayValue("");
176
+ setInternalDate(null);
177
+ }
178
+ }
179
+ return false;
180
+ };
181
+ // Intentar inmediatamente
182
+ if (trySync()) {
183
+ return;
184
+ }
185
+ // Si no encontramos el valor, usar un intervalo
186
+ const intervalId = window.setInterval(() => {
187
+ attempts++;
188
+ if (trySync() || attempts >= maxAttempts) {
189
+ clearInterval(intervalId);
190
+ }
191
+ }, 100);
192
+ // También usar timeouts como fallback
193
+ const timeouts = [];
194
+ [0, 50, 100, 200, 500, 1000].forEach((delay) => {
195
+ const timeoutId = window.setTimeout(() => {
196
+ trySync();
197
+ }, delay);
198
+ timeouts.push(timeoutId);
199
+ });
200
+ return () => {
201
+ clearInterval(intervalId);
202
+ timeouts.forEach(clearTimeout);
203
+ };
79
204
  }
80
- }, [value, format]);
81
- // Centrar el ícono verticalmente respecto al input real
205
+ }, [isRegisterMode, format]);
206
+ // También escuchar cambios en el input nativo para sincronizar cuando cambie
82
207
  React.useEffect(() => {
83
- const updateIconPosition = () => {
84
- if (iconPosition === "right" &&
85
- inputWrapperRef.current &&
86
- iconRef.current) {
87
- const inputElement = inputWrapperRef.current.querySelector("input");
88
- if (inputElement) {
89
- const inputRect = inputElement.getBoundingClientRect();
90
- const wrapperRect = inputWrapperRef.current.getBoundingClientRect();
91
- const topOffset = inputRect.top - wrapperRect.top + inputRect.height / 2;
92
- iconRef.current.style.top = `${topOffset}px`;
93
- iconRef.current.style.transform = "translateY(-50%)";
208
+ if (isRegisterMode && inputRef.current) {
209
+ const input = inputRef.current;
210
+ const handleInputSync = () => {
211
+ syncDisplayValue();
212
+ };
213
+ input.addEventListener("input", handleInputSync);
214
+ input.addEventListener("change", handleInputSync);
215
+ const observer = new MutationObserver(() => {
216
+ syncDisplayValue();
217
+ });
218
+ observer.observe(input, {
219
+ attributes: true,
220
+ attributeFilter: ["value"],
221
+ });
222
+ return () => {
223
+ input.removeEventListener("input", handleInputSync);
224
+ input.removeEventListener("change", handleInputSync);
225
+ observer.disconnect();
226
+ };
227
+ }
228
+ }, [isRegisterMode, syncDisplayValue]);
229
+ // Sincronizar con el valor del formulario
230
+ React.useEffect(() => {
231
+ if (!isTypingRef.current) {
232
+ if (isRegisterMode) {
233
+ // En modo register con setValue, leer del formulario
234
+ if (formContext && fieldName) {
235
+ const formValue = formContext.watch(fieldName);
236
+ const normalized = normalizeToDayjs(formValue);
237
+ setInternalDate(normalized);
238
+ if (normalized) {
239
+ setDisplayValue(formatDateToString(normalized, format));
240
+ }
241
+ else {
242
+ setDisplayValue("");
243
+ }
94
244
  }
245
+ // Si no hay setValue, syncDisplayValue se encarga de sincronizar desde el input nativo
95
246
  }
96
- };
97
- // Ejecutar inmediatamente
98
- updateIconPosition();
99
- // Ejecutar cuando cambie el tamaño de la ventana
100
- window.addEventListener("resize", updateIconPosition);
101
- return () => {
102
- window.removeEventListener("resize", updateIconPosition);
103
- };
104
- }, [iconPosition, inputValue, inputProps.label, inputProps.size]);
247
+ else {
248
+ // Modo Controller, sincronizar con el valor Dayjs
249
+ const normalized = normalizeToDayjs(value);
250
+ setInternalDate(normalized);
251
+ if (normalized) {
252
+ setDisplayValue(formatDateToString(normalized, format));
253
+ }
254
+ else {
255
+ setDisplayValue("");
256
+ }
257
+ }
258
+ }
259
+ }, [value, format, isRegisterMode, setValue, fieldName, formContext]);
260
+ // Determinar el valor a mostrar en el input
261
+ const inputValue = isRegisterMode ? displayValue : displayValue;
105
262
  const handleDateChange = (date) => {
106
- if (value === undefined) {
263
+ if (readOnly)
264
+ return;
265
+ const dateString = formatDateToString(date, format);
266
+ if (isRegisterMode) {
267
+ // En modo register, usar setValue si está disponible para guardar el objeto Dayjs
268
+ // Si no está disponible, guardar como string (comportamiento por defecto)
269
+ if (setValue && fieldName) {
270
+ // Usar setValue para guardar el objeto Dayjs directamente
271
+ setValue(fieldName, date, {
272
+ shouldValidate: true,
273
+ shouldDirty: true,
274
+ });
275
+ }
276
+ else {
277
+ // Fallback: actualizar el input nativo con el string de fecha
278
+ if (inputRef.current) {
279
+ const nativeInput = inputRef.current;
280
+ const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set;
281
+ if (nativeInputValueSetter) {
282
+ nativeInputValueSetter.call(nativeInput, dateString);
283
+ }
284
+ else {
285
+ nativeInput.value = dateString;
286
+ }
287
+ // Llamar al onChange de register
288
+ if (onChange) {
289
+ const changeEvent = {
290
+ target: nativeInput,
291
+ currentTarget: nativeInput,
292
+ };
293
+ onChange(changeEvent);
294
+ }
295
+ // Disparar eventos nativos
296
+ const inputEvent = new Event("input", {
297
+ bubbles: true,
298
+ cancelable: true,
299
+ });
300
+ nativeInput.dispatchEvent(inputEvent);
301
+ const changeEventNative = new Event("change", {
302
+ bubbles: true,
303
+ cancelable: true,
304
+ });
305
+ nativeInput.dispatchEvent(changeEventNative);
306
+ }
307
+ }
308
+ // Actualizar el displayValue
309
+ setDisplayValue(dateString);
107
310
  setInternalDate(date);
108
- setInputValue(formatDateToString(date, format));
109
311
  }
110
- onChange?.(date);
312
+ else {
313
+ // Modo Controller - comportamiento original
314
+ setInternalDate(date);
315
+ setDisplayValue(dateString);
316
+ if (onChange) {
317
+ const dayjsHandler = onChange;
318
+ dayjsHandler(date);
319
+ }
320
+ }
111
321
  setIsOpen(false);
112
322
  };
113
323
  const handleInputChange = (event) => {
324
+ if (readOnly)
325
+ return;
114
326
  const newValue = event.target.value;
115
- setInputValue(newValue);
116
- // No intentamos parsear en cada pulsación, solo actualizamos el texto.
327
+ isTypingRef.current = true;
328
+ if (isRegisterMode) {
329
+ // En modo register, actualizar el displayValue mientras el usuario escribe
330
+ setDisplayValue(newValue);
331
+ }
332
+ else {
333
+ // Modo Controller
334
+ setDisplayValue(newValue);
335
+ }
117
336
  };
118
337
  const handleInputBlur = (event) => {
338
+ isTypingRef.current = false;
119
339
  const newValue = event.target.value.trim();
120
- if (!newValue) {
121
- handleDateChange(null);
122
- return;
123
- }
124
- const parsed = parseDateFromString(newValue, format);
125
- if (parsed) {
126
- handleDateChange(parsed);
340
+ if (isRegisterMode) {
341
+ // En modo register, validar y actualizar el input nativo
342
+ if (!newValue) {
343
+ // Limpiar el valor
344
+ if (inputRef.current) {
345
+ const nativeInput = inputRef.current;
346
+ const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set;
347
+ setter?.call(nativeInput, "");
348
+ if (onChange) {
349
+ const changeEvent = {
350
+ target: nativeInput,
351
+ currentTarget: nativeInput,
352
+ };
353
+ onChange(changeEvent);
354
+ }
355
+ const inputEvent = new Event("input", { bubbles: true });
356
+ nativeInput.dispatchEvent(inputEvent);
357
+ const changeEventNative = new Event("change", { bubbles: true });
358
+ nativeInput.dispatchEvent(changeEventNative);
359
+ }
360
+ setDisplayValue("");
361
+ setInternalDate(null);
362
+ }
363
+ else {
364
+ const parsed = parseDateFromString(newValue, format);
365
+ if (parsed) {
366
+ handleDateChange(parsed);
367
+ }
368
+ else {
369
+ // Si no es válida, restaurar el valor anterior
370
+ const previousValue = inputRef.current?.value || "";
371
+ setDisplayValue(previousValue);
372
+ }
373
+ }
374
+ // Llamar al onBlur de register si existe
375
+ if (registerOnBlur) {
376
+ registerOnBlur(event);
377
+ }
127
378
  }
128
379
  else {
129
- // Si no es válida, restauramos el valor anterior formateado.
130
- setInputValue(formatDateToString(internalDate, format));
380
+ // Modo Controller
381
+ if (!newValue) {
382
+ handleDateChange(null);
383
+ }
384
+ else {
385
+ const parsed = parseDateFromString(newValue, format);
386
+ if (parsed) {
387
+ handleDateChange(parsed);
388
+ }
389
+ else {
390
+ // Si no es válida, restaurar el valor anterior formateado
391
+ setDisplayValue(formatDateToString(internalDate, format));
392
+ }
393
+ }
131
394
  }
132
395
  };
133
396
  const handleIconClick = (event) => {
397
+ if (readOnly)
398
+ return;
134
399
  event.preventDefault();
135
400
  setIsOpen((prev) => !prev);
136
401
  };
137
402
  React.useEffect(() => {
403
+ if (!isOpen)
404
+ return;
138
405
  const handleClickOutside = (event) => {
139
- if (containerRef.current &&
140
- !containerRef.current.contains(event.target)) {
406
+ const target = event.target;
407
+ const isClickInsideContainer = containerRef.current?.contains(target);
408
+ const isClickInsidePicker = pickerRef.current?.contains(target);
409
+ if (!isClickInsideContainer && !isClickInsidePicker) {
141
410
  setIsOpen(false);
142
411
  }
143
412
  };
144
- document.addEventListener("mousedown", handleClickOutside);
413
+ // Pequeño delay para asegurar que el portal esté montado
414
+ const timer = setTimeout(() => {
415
+ document.addEventListener("mousedown", handleClickOutside);
416
+ }, 0);
145
417
  return () => {
418
+ clearTimeout(timer);
146
419
  document.removeEventListener("mousedown", handleClickOutside);
147
420
  };
148
- }, []);
149
- const datePickerInitialViewDate = internalDate ?? datePickerProps?.initialViewDate ?? new Date();
150
- return (_jsxs("div", { ref: containerRef, className: "relative w-full", children: [_jsxs("div", { ref: inputWrapperRef, className: "relative", children: [_jsx(Input, { ...inputProps, type: "text", value: inputValue, onChange: handleInputChange, onBlur: handleInputBlur, icon: iconPosition === "right" ? undefined : icon, iconPosition: iconPosition, placeholder: inputProps.placeholder ??
151
- (format === "mm/dd/yyyy" ? "mm/dd/yyyy" : "dd/mm/yyyy"), className: `${className} ${iconPosition === "right" ? "pr-10" : ""}` }), iconPosition === "right" && (_jsx("div", { ref: iconRef, className: "absolute right-3 cursor-pointer", onMouseDown: handleIconClick, children: _jsx("i", { className: `fa ${icon} ${inputProps.size === "sm"
152
- ? "w-4 h-4"
153
- : inputProps.size === "lg"
154
- ? "w-6 h-6"
155
- : "w-5 h-5"} text-[var(--color-text-muted)]` }) }))] }), isOpen && (_jsx("div", { className: "absolute z-20 mt-1 right-0", children: _jsx(DatePicker, { ...datePickerProps, value: internalDate ?? datePickerInitialViewDate, onChange: (date) => handleDateChange(date) }) }))] }));
156
- };
421
+ }, [isOpen]);
422
+ // Combinar refs: el ref del componente y el ref interno
423
+ const combinedRef = React.useCallback((node) => {
424
+ inputRef.current = node;
425
+ if (typeof ref === "function") {
426
+ ref(node);
427
+ }
428
+ else if (ref) {
429
+ ref.current = node;
430
+ }
431
+ // Cuando el ref se establece en modo register, sincronizar el displayValue
432
+ if (isRegisterMode && node) {
433
+ [0, 10, 50, 100, 200, 500].forEach((delay) => {
434
+ setTimeout(() => {
435
+ if (node && inputRef.current === node) {
436
+ const formValue = node.value;
437
+ if (formValue) {
438
+ const parsed = parseDateFromString(formValue, format);
439
+ if (parsed) {
440
+ setDisplayValue(formatDateToString(parsed, format));
441
+ setInternalDate(parsed);
442
+ }
443
+ else {
444
+ setDisplayValue(formValue);
445
+ }
446
+ }
447
+ }
448
+ }, delay);
449
+ });
450
+ }
451
+ }, [ref, isRegisterMode, format]);
452
+ const datePickerInitialViewDate = internalDate ?? datePickerProps?.initialViewDate ?? dayjs();
453
+ // Ocultar el ícono cuando está en modo readOnly
454
+ const displayIcon = readOnly ? undefined : icon;
455
+ const displayIconPosition = readOnly ? undefined : iconPosition;
456
+ const displayOnIconClick = readOnly ? undefined : handleIconClick;
457
+ // Verificar que estamos en el navegador
458
+ // Inicializar isMounted de forma síncrona si es posible
459
+ const [isMounted, setIsMounted] = React.useState(() => {
460
+ return typeof document !== "undefined" && !!document.body;
461
+ });
462
+ React.useEffect(() => {
463
+ if (!isMounted && typeof document !== "undefined" && document.body) {
464
+ setIsMounted(true);
465
+ }
466
+ }, [isMounted]);
467
+ // Actualizar posición del picker cuando se abre
468
+ React.useEffect(() => {
469
+ if (isOpen && !readOnly && containerRef.current && isMounted) {
470
+ const updatePosition = () => {
471
+ const rect = containerRef.current?.getBoundingClientRect();
472
+ if (rect) {
473
+ setPickerPosition({
474
+ top: rect.bottom + window.scrollY + 4,
475
+ left: rect.right + window.scrollX - 280, // Alinear a la derecha
476
+ width: rect.width,
477
+ });
478
+ }
479
+ };
480
+ updatePosition();
481
+ window.addEventListener("scroll", updatePosition, true);
482
+ window.addEventListener("resize", updatePosition);
483
+ return () => {
484
+ window.removeEventListener("scroll", updatePosition, true);
485
+ window.removeEventListener("resize", updatePosition);
486
+ };
487
+ }
488
+ else {
489
+ setPickerPosition(null);
490
+ }
491
+ }, [isOpen, readOnly, isMounted]);
492
+ return (_jsxs("div", { ref: containerRef, className: "relative w-full", children: [_jsx(Input, { ...restInputProps, ref: combinedRef, type: "text", value: inputValue, onChange: handleInputChange, onBlur: handleInputBlur, icon: displayIcon, iconPosition: displayIconPosition, onIconClick: displayOnIconClick, placeholder: restInputProps.placeholder ??
493
+ (format === "mm/dd/yyyy" ? "mm/dd/yyyy" : "dd/mm/yyyy"), className: className, readOnly: readOnly }), (() => {
494
+ // Verificar de forma segura que document.body existe y es válido
495
+ const bodyElement = typeof document !== "undefined" &&
496
+ document.body &&
497
+ document.body instanceof HTMLElement
498
+ ? document.body
499
+ : null;
500
+ return (!readOnly &&
501
+ isOpen &&
502
+ pickerPosition &&
503
+ isMounted &&
504
+ bodyElement &&
505
+ createPortal(_jsx("div", { ref: pickerRef, className: "fixed z-[2001] min-w-[280px] w-max", style: {
506
+ top: `${pickerPosition.top}px`,
507
+ left: `${pickerPosition.left}px`,
508
+ }, children: _jsx(DatePicker, { ...datePickerProps, value: internalDate ?? datePickerInitialViewDate, onChange: (date) => handleDateChange(date) }) }), bodyElement));
509
+ })()] }));
510
+ });
511
+ 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?: Date | null;
8
- onChange?: (date: Date) => void;
9
- initialViewDate?: Date;
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;AAG1B,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAChC,eAAe,CAAC,EAAE,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA4BD,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA2OhD,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"}