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.
Files changed (206) hide show
  1. package/dist/App.d.ts.map +1 -1
  2. package/dist/App.js +19 -7
  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 +411 -31
  6. package/dist/components/form-controls/Button.d.ts +3 -0
  7. package/dist/components/form-controls/Button.d.ts.map +1 -1
  8. package/dist/components/form-controls/Button.js +160 -19
  9. package/dist/components/form-controls/Checkbox.d.ts +14 -0
  10. package/dist/components/form-controls/Checkbox.d.ts.map +1 -0
  11. package/dist/components/form-controls/Checkbox.js +79 -0
  12. package/dist/components/form-controls/DateInput.d.ts +24 -4
  13. package/dist/components/form-controls/DateInput.d.ts.map +1 -1
  14. package/dist/components/form-controls/DateInput.js +492 -70
  15. package/dist/components/form-controls/DatePicker.d.ts +4 -3
  16. package/dist/components/form-controls/DatePicker.d.ts.map +1 -1
  17. package/dist/components/form-controls/DatePicker.js +26 -30
  18. package/dist/components/form-controls/Input.d.ts +10 -1
  19. package/dist/components/form-controls/Input.d.ts.map +1 -1
  20. package/dist/components/form-controls/Input.js +17 -10
  21. package/dist/components/form-controls/LinkButton.d.ts +15 -0
  22. package/dist/components/form-controls/LinkButton.d.ts.map +1 -0
  23. package/dist/components/form-controls/LinkButton.js +248 -0
  24. package/dist/components/form-controls/Pagination.d.ts +1 -0
  25. package/dist/components/form-controls/Pagination.d.ts.map +1 -1
  26. package/dist/components/form-controls/Pagination.js +3 -40
  27. package/dist/components/form-controls/RadioButtonGroup.d.ts +62 -0
  28. package/dist/components/form-controls/RadioButtonGroup.d.ts.map +1 -0
  29. package/dist/components/form-controls/RadioButtonGroup.js +220 -0
  30. package/dist/components/form-controls/SearchSelectInput-OLD.d.ts +68 -0
  31. package/dist/components/form-controls/SearchSelectInput-OLD.d.ts.map +1 -0
  32. package/dist/components/form-controls/SearchSelectInput-OLD.js +963 -0
  33. package/dist/components/form-controls/SearchSelectInput.d.ts +70 -0
  34. package/dist/components/form-controls/SearchSelectInput.d.ts.map +1 -0
  35. package/dist/components/form-controls/SearchSelectInput.js +336 -0
  36. package/dist/components/form-controls/index.d.ts +9 -1
  37. package/dist/components/form-controls/index.d.ts.map +1 -1
  38. package/dist/components/form-controls/index.js +4 -0
  39. package/dist/components/layout/Accordion.d.ts +13 -0
  40. package/dist/components/layout/Accordion.d.ts.map +1 -0
  41. package/dist/components/layout/Accordion.js +67 -0
  42. package/dist/components/layout/AppLayout.d.ts +3 -2
  43. package/dist/components/layout/AppLayout.d.ts.map +1 -1
  44. package/dist/components/layout/AppLayout.js +104 -31
  45. package/dist/components/layout/Card.d.ts +8 -3
  46. package/dist/components/layout/Card.d.ts.map +1 -1
  47. package/dist/components/layout/Card.js +18 -19
  48. package/dist/components/layout/Collection.js +1 -1
  49. package/dist/components/layout/DataTable.d.ts +3 -1
  50. package/dist/components/layout/DataTable.d.ts.map +1 -1
  51. package/dist/components/layout/DataTable.js +34 -29
  52. package/dist/components/layout/index.d.ts +2 -0
  53. package/dist/components/layout/index.d.ts.map +1 -1
  54. package/dist/components/layout/index.js +1 -0
  55. package/dist/components/utils/Avatar.d.ts +49 -0
  56. package/dist/components/utils/Avatar.d.ts.map +1 -0
  57. package/dist/components/utils/Avatar.js +93 -0
  58. package/dist/components/utils/Badge.d.ts +3 -0
  59. package/dist/components/utils/Badge.d.ts.map +1 -1
  60. package/dist/components/utils/Badge.js +131 -26
  61. package/dist/components/utils/Dialog.d.ts.map +1 -1
  62. package/dist/components/utils/Dialog.js +6 -1
  63. package/dist/components/utils/Filter.d.ts +57 -0
  64. package/dist/components/utils/Filter.d.ts.map +1 -0
  65. package/dist/components/utils/Filter.js +581 -0
  66. package/dist/components/utils/FiltersDialog.d.ts +21 -0
  67. package/dist/components/utils/FiltersDialog.d.ts.map +1 -0
  68. package/dist/components/utils/FiltersDialog.js +104 -0
  69. package/dist/components/utils/Loader.js +2 -2
  70. package/dist/components/utils/RoadMap.d.ts +59 -0
  71. package/dist/components/utils/RoadMap.d.ts.map +1 -0
  72. package/dist/components/utils/RoadMap.js +139 -0
  73. package/dist/components/utils/Snackbar.d.ts +13 -0
  74. package/dist/components/utils/Snackbar.d.ts.map +1 -0
  75. package/dist/components/utils/Snackbar.js +122 -0
  76. package/dist/components/utils/SnackbarContainer.d.ts +7 -0
  77. package/dist/components/utils/SnackbarContainer.d.ts.map +1 -0
  78. package/dist/components/utils/SnackbarContainer.js +25 -0
  79. package/dist/components/utils/iconUtils.d.ts +16 -0
  80. package/dist/components/utils/iconUtils.d.ts.map +1 -0
  81. package/dist/components/utils/iconUtils.js +40 -0
  82. package/dist/components/utils/index.d.ts +12 -0
  83. package/dist/components/utils/index.d.ts.map +1 -1
  84. package/dist/components/utils/index.js +6 -0
  85. package/dist/contexts/AppLayoutContext.d.ts +40 -0
  86. package/dist/contexts/AppLayoutContext.d.ts.map +1 -0
  87. package/dist/contexts/AppLayoutContext.js +98 -0
  88. package/dist/contexts/ListCrudContext.d.ts +50 -0
  89. package/dist/contexts/ListCrudContext.d.ts.map +1 -0
  90. package/dist/contexts/ListCrudContext.js +253 -0
  91. package/dist/contexts/SnackbarContext.d.ts +26 -0
  92. package/dist/contexts/SnackbarContext.d.ts.map +1 -0
  93. package/dist/contexts/SnackbarContext.js +34 -0
  94. package/dist/contexts/index.d.ts +6 -0
  95. package/dist/contexts/index.d.ts.map +1 -1
  96. package/dist/contexts/index.js +6 -0
  97. package/dist/contexts/presets.js +6 -6
  98. package/dist/docs/AccordionDocs.d.ts +4 -0
  99. package/dist/docs/AccordionDocs.d.ts.map +1 -0
  100. package/dist/docs/AccordionDocs.js +21 -0
  101. package/dist/docs/AuthDocs.tsx/AuthDocsContent.js +3 -5
  102. package/dist/docs/AutocompleteInputDocs.js +1 -1
  103. package/dist/docs/AvatarDocs.d.ts +4 -0
  104. package/dist/docs/AvatarDocs.d.ts.map +1 -0
  105. package/dist/docs/AvatarDocs.js +7 -0
  106. package/dist/docs/BadgeDocs.d.ts.map +1 -1
  107. package/dist/docs/BadgeDocs.js +4 -2
  108. package/dist/docs/ButtonDocs.d.ts.map +1 -1
  109. package/dist/docs/ButtonDocs.js +1 -1
  110. package/dist/docs/CardDocs.d.ts.map +1 -1
  111. package/dist/docs/CardDocs.js +17 -8
  112. package/dist/docs/CheckboxDocs.d.ts +4 -0
  113. package/dist/docs/CheckboxDocs.d.ts.map +1 -0
  114. package/dist/docs/CheckboxDocs.js +7 -0
  115. package/dist/docs/DataTableDocs.d.ts.map +1 -1
  116. package/dist/docs/DataTableDocs.js +9 -5
  117. package/dist/docs/DateInputDocs.d.ts +1 -0
  118. package/dist/docs/DateInputDocs.d.ts.map +1 -1
  119. package/dist/docs/DateInputDocs.js +7 -9
  120. package/dist/docs/DatePickerDocs.d.ts +1 -0
  121. package/dist/docs/DatePickerDocs.d.ts.map +1 -1
  122. package/dist/docs/DatePickerDocs.js +6 -8
  123. package/dist/docs/DialogDocs.js +1 -1
  124. package/dist/docs/DocAdmin.d.ts +4 -0
  125. package/dist/docs/DocAdmin.d.ts.map +1 -0
  126. package/dist/docs/DocAdmin.js +68 -0
  127. package/dist/docs/DocsMenu.d.ts.map +1 -1
  128. package/dist/docs/DocsMenu.js +3 -3
  129. package/dist/docs/DocsRouter.d.ts.map +1 -1
  130. package/dist/docs/DocsRouter.js +13 -1
  131. package/dist/docs/DropdownMenuDocs.js +1 -1
  132. package/dist/docs/ExampleFormDocs.d.ts +4 -0
  133. package/dist/docs/ExampleFormDocs.d.ts.map +1 -0
  134. package/dist/docs/ExampleFormDocs.js +148 -0
  135. package/dist/docs/FilterDocs.d.ts +4 -0
  136. package/dist/docs/FilterDocs.d.ts.map +1 -0
  137. package/dist/docs/FilterDocs.js +112 -0
  138. package/dist/docs/InputDocs.d.ts.map +1 -1
  139. package/dist/docs/InputDocs.js +11 -1
  140. package/dist/docs/LinkButtonDocs.d.ts +4 -0
  141. package/dist/docs/LinkButtonDocs.d.ts.map +1 -0
  142. package/dist/docs/LinkButtonDocs.js +7 -0
  143. package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.d.ts +2 -0
  144. package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.d.ts.map +1 -0
  145. package/dist/docs/ListCrudDocs.tsx/ListCrudDocs.js +29 -0
  146. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresas.d.ts +2 -0
  147. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresas.d.ts.map +1 -0
  148. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentEmpresas.js +7 -0
  149. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.d.ts +2 -0
  150. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.d.ts.map +1 -0
  151. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsContentPersonas.js +57 -0
  152. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsEditDialog.d.ts +9 -0
  153. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsEditDialog.d.ts.map +1 -0
  154. package/dist/docs/ListCrudDocs.tsx/ListCrudDocsEditDialog.js +30 -0
  155. package/dist/docs/PaginationDocs.js +6 -6
  156. package/dist/docs/RadioButtonGroupDocs.d.ts +4 -0
  157. package/dist/docs/RadioButtonGroupDocs.d.ts.map +1 -0
  158. package/dist/docs/RadioButtonGroupDocs.js +46 -0
  159. package/dist/docs/RoadMapDocs.d.ts +4 -0
  160. package/dist/docs/RoadMapDocs.d.ts.map +1 -0
  161. package/dist/docs/RoadMapDocs.js +171 -0
  162. package/dist/docs/SearchSelectInputDocs.d.ts +4 -0
  163. package/dist/docs/SearchSelectInputDocs.d.ts.map +1 -0
  164. package/dist/docs/SearchSelectInputDocs.js +168 -0
  165. package/dist/docs/SnackbarDocs.d.ts +4 -0
  166. package/dist/docs/SnackbarDocs.d.ts.map +1 -0
  167. package/dist/docs/SnackbarDocs.js +50 -0
  168. package/dist/docs/TabsGroupDocs.d.ts.map +1 -1
  169. package/dist/docs/TabsGroupDocs.js +12 -1
  170. package/dist/docs/docMockServices/empresaService.d.ts +38 -0
  171. package/dist/docs/docMockServices/empresaService.d.ts.map +1 -0
  172. package/dist/docs/docMockServices/empresaService.js +117 -0
  173. package/dist/docs/docMockServices/index.d.ts +9 -0
  174. package/dist/docs/docMockServices/index.d.ts.map +1 -0
  175. package/dist/docs/docMockServices/index.js +8 -0
  176. package/dist/docs/docMockServices/initialData.d.ts +6 -0
  177. package/dist/docs/docMockServices/initialData.d.ts.map +1 -0
  178. package/dist/docs/docMockServices/initialData.js +132 -0
  179. package/dist/docs/docMockServices/interfaces.d.ts +26 -0
  180. package/dist/docs/docMockServices/interfaces.d.ts.map +1 -0
  181. package/dist/docs/docMockServices/interfaces.js +1 -0
  182. package/dist/docs/docMockServices/personaEmpresaService.d.ts +43 -0
  183. package/dist/docs/docMockServices/personaEmpresaService.d.ts.map +1 -0
  184. package/dist/docs/docMockServices/personaEmpresaService.js +113 -0
  185. package/dist/docs/docMockServices/personaService.d.ts +39 -0
  186. package/dist/docs/docMockServices/personaService.d.ts.map +1 -0
  187. package/dist/docs/docMockServices/personaService.js +181 -0
  188. package/dist/hooks/index.d.ts +2 -0
  189. package/dist/hooks/index.d.ts.map +1 -1
  190. package/dist/hooks/index.js +1 -0
  191. package/dist/hooks/useAsyncRequest.d.ts +17 -0
  192. package/dist/hooks/useAsyncRequest.d.ts.map +1 -0
  193. package/dist/hooks/useAsyncRequest.js +70 -0
  194. package/dist/index.css +1 -1
  195. package/dist/index.d.ts +23 -1
  196. package/dist/index.d.ts.map +1 -1
  197. package/dist/index.js +11 -0
  198. package/dist/index.js.map +1 -1
  199. package/dist/templates/forms/ContactForm.js +2 -2
  200. package/dist/templates/forms/LoginForm.js +1 -1
  201. package/dist/templates/forms/RegistrationForm.js +1 -1
  202. package/dist/templates/layouts/SidebarLayout.d.ts.map +1 -1
  203. package/dist/templates/layouts/SidebarLayout.js +3 -2
  204. package/dist/templates/patterns/FormPattern.d.ts.map +1 -1
  205. package/dist/templates/patterns/FormPattern.js +4 -3
  206. 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,493 @@ 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
+ // 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 inputWrapperRef = React.useRef(null);
74
- const iconRef = React.useRef(null);
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 (value !== undefined) {
77
- setInternalDate(value);
78
- setInputValue(formatDateToString(value, format));
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
- }, [value, format]);
81
- // Centrar el ícono verticalmente respecto al input real
207
+ }, [isRegisterMode, format]);
208
+ // También escuchar cambios en el input nativo para sincronizar cuando cambie
82
209
  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%)";
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
- // 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]);
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 (value === undefined) {
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
- setInputValue(formatDateToString(date, format));
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
- setInputValue(newValue);
116
- // No intentamos parsear en cada pulsación, solo actualizamos el texto.
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 (!newValue) {
121
- handleDateChange(null);
122
- return;
123
- }
124
- const parsed = parseDateFromString(newValue, format);
125
- if (parsed) {
126
- handleDateChange(parsed);
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
- // Si no es válida, restauramos el valor anterior formateado.
130
- setInputValue(formatDateToString(internalDate, format));
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
- if (containerRef.current &&
140
- !containerRef.current.contains(event.target)) {
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
- document.addEventListener("mousedown", handleClickOutside);
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
- 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
- };
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?: 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"}