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
@@ -0,0 +1,580 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useState, useEffect, useRef } from "react";
3
+ import { createPortal } from "react-dom";
4
+ import { useSearchParams } from "react-router-dom";
5
+ import dayjs, {} from "dayjs";
6
+ import { Button } from "../form-controls/Button";
7
+ import { Input } from "../form-controls/Input";
8
+ import { DateInput } from "../form-controls/DateInput";
9
+ import { AutocompleteInput } from "../form-controls/AutocompleteInput";
10
+ import { SearchSelectInput } from "../form-controls/SearchSelectInput";
11
+ import { DataField } from "../layout/DataField";
12
+ export const Filter = (props) => {
13
+ const { paramName, label, staticOptions, inputWidth, value: propValue, onChange, hideEmpty = false, } = props;
14
+ const filterType = props.filterType || "text";
15
+ // Calcular el ancho por defecto según el tipo de filtro
16
+ const defaultInputWidth = filterType === "date" || filterType === "autocomplete" ? "160px" : "200px";
17
+ const finalInputWidth = inputWidth || defaultInputWidth;
18
+ const [searchParams, setSearchParams] = useSearchParams();
19
+ const urlValue = paramName
20
+ ? searchParams.get(paramName) || undefined
21
+ : undefined;
22
+ // Usar propValue si está presente, sino usar urlValue
23
+ const currentValue = propValue !== undefined ? propValue : urlValue;
24
+ const [inputValue, setInputValue] = useState(currentValue || "");
25
+ const [searchValue, setSearchValue] = useState(currentValue || "");
26
+ const [dateValue, setDateValue] = useState(null);
27
+ const [autocompleteValue, setAutocompleteValue] = useState(currentValue || "");
28
+ const [searchSelectValue, setSearchSelectValue] = useState(currentValue || undefined);
29
+ const [searchSelectLabel, setSearchSelectLabel] = useState("");
30
+ const [isUserTyping, setIsUserTyping] = useState(false);
31
+ const [isOpen, setIsOpen] = useState(false);
32
+ const [panelPosition, setPanelPosition] = useState(null);
33
+ const containerRef = useRef(null);
34
+ const panelRef = useRef(null);
35
+ const inputRef = useRef(null);
36
+ const searchInputRef = useRef(null);
37
+ const searchSelectInputRef = useRef(null);
38
+ const dateInputRef = useRef(null);
39
+ const autocompleteInputRef = useRef(null);
40
+ // Sincronizar el input con el valor actual (propValue o urlValue)
41
+ useEffect(() => {
42
+ if (filterType !== "autocomplete" &&
43
+ filterType !== "search" &&
44
+ filterType !== "searchSelect") {
45
+ setInputValue(currentValue || "");
46
+ }
47
+ }, [currentValue, filterType]);
48
+ // Sincronizar el searchValue con el valor actual (propValue o urlValue)
49
+ useEffect(() => {
50
+ if (filterType === "search") {
51
+ setSearchValue(currentValue || "");
52
+ }
53
+ }, [currentValue, filterType]);
54
+ // Sincronizar el searchSelectValue con el valor actual (propValue o urlValue)
55
+ // y buscar el label si no está disponible
56
+ useEffect(() => {
57
+ if (filterType === "searchSelect" && currentValue) {
58
+ const searchSelectProps = props;
59
+ const getOptionLabel = searchSelectProps.getOptionLabel || ((item) => item.label || "");
60
+ const getOptionValue = searchSelectProps.getOptionValue || ((item) => item.value || "");
61
+ const onSingleSearchPromiseFn = searchSelectProps.onSingleSearchPromiseFn;
62
+ // Si searchSelectValue es un objeto, extraer el label directamente
63
+ if (searchSelectValue && typeof searchSelectValue === "object") {
64
+ const label = getOptionLabel(searchSelectValue);
65
+ const value = String(getOptionValue(searchSelectValue));
66
+ if (value === String(currentValue)) {
67
+ setSearchSelectLabel(label);
68
+ return;
69
+ }
70
+ }
71
+ // Si no tenemos el objeto completo, usar onSingleSearchPromiseFn para obtenerlo
72
+ if (onSingleSearchPromiseFn) {
73
+ onSingleSearchPromiseFn(currentValue)
74
+ .then((option) => {
75
+ if (option) {
76
+ setSearchSelectValue(option);
77
+ setSearchSelectLabel(getOptionLabel(option));
78
+ }
79
+ else {
80
+ setSearchSelectLabel(String(currentValue));
81
+ }
82
+ })
83
+ .catch(() => {
84
+ setSearchSelectLabel(String(currentValue));
85
+ });
86
+ }
87
+ else {
88
+ setSearchSelectLabel(String(currentValue));
89
+ }
90
+ }
91
+ else if (filterType === "searchSelect" && !currentValue) {
92
+ setSearchSelectValue(undefined);
93
+ setSearchSelectLabel("");
94
+ }
95
+ // eslint-disable-next-line react-hooks/exhaustive-deps
96
+ }, [currentValue, filterType]);
97
+ // Sincronizar el dateValue con el valor actual (propValue o urlValue)
98
+ useEffect(() => {
99
+ if (filterType === "date") {
100
+ if (currentValue) {
101
+ const date = dayjs(currentValue, "YYYY-MM-DD");
102
+ setDateValue(date.isValid() ? date : null);
103
+ }
104
+ else {
105
+ setDateValue(null);
106
+ }
107
+ }
108
+ }, [currentValue, filterType]);
109
+ // Sincronizar el autocompleteValue con el valor actual (propValue o urlValue)
110
+ // Para autocomplete, mantener el value (no el label) para que AutocompleteInput pueda encontrar la opción
111
+ useEffect(() => {
112
+ if (filterType === "autocomplete") {
113
+ // Mantener el value, el AutocompleteInput se encargará de mostrar el label
114
+ setAutocompleteValue(currentValue || "");
115
+ setIsUserTyping(false);
116
+ }
117
+ }, [currentValue, filterType]);
118
+ // Cerrar el panel al hacer clic fuera
119
+ useEffect(() => {
120
+ if (!isOpen)
121
+ return;
122
+ const handleClickOutside = (event) => {
123
+ const target = event.target;
124
+ const isClickInsideContainer = containerRef.current?.contains(target);
125
+ const isClickInsidePanel = panelRef.current?.contains(target);
126
+ // Verificar si el click está dentro de un elemento con portal (DatePicker, AutocompleteInput, etc.)
127
+ // Estos elementos tienen z-[2001] y están posicionados fixed
128
+ let isClickInsidePortal = false;
129
+ if (target instanceof Element) {
130
+ let element = target;
131
+ // Subir por el árbol DOM buscando elementos con z-index alto que sean portales
132
+ while (element && element !== document.body) {
133
+ const computedStyle = window.getComputedStyle(element);
134
+ const zIndex = computedStyle.zIndex;
135
+ const position = computedStyle.position;
136
+ // Si encontramos un elemento fixed con z-index alto, probablemente es un portal
137
+ if (position === "fixed" &&
138
+ (zIndex === "2001" || parseInt(zIndex) >= 2001)) {
139
+ isClickInsidePortal = true;
140
+ break;
141
+ }
142
+ element = element.parentElement;
143
+ }
144
+ }
145
+ if (!isClickInsideContainer &&
146
+ !isClickInsidePanel &&
147
+ !isClickInsidePortal) {
148
+ setIsOpen(false);
149
+ }
150
+ };
151
+ // Pequeño delay para asegurar que el portal esté montado
152
+ const timer = setTimeout(() => {
153
+ document.addEventListener("mousedown", handleClickOutside);
154
+ }, 0);
155
+ return () => {
156
+ clearTimeout(timer);
157
+ document.removeEventListener("mousedown", handleClickOutside);
158
+ };
159
+ }, [isOpen]);
160
+ const handleSetFilter = () => {
161
+ const newValue = inputValue.trim() || undefined;
162
+ // Si hay onChange, llamarlo con el nuevo valor
163
+ if (onChange) {
164
+ onChange(newValue);
165
+ }
166
+ // Si hay paramName, actualizar el query param
167
+ if (paramName) {
168
+ const newSearchParams = new URLSearchParams(searchParams);
169
+ if (newValue) {
170
+ newSearchParams.set(paramName, newValue);
171
+ }
172
+ else {
173
+ newSearchParams.delete(paramName);
174
+ }
175
+ // Usar replace: true para reemplazar la URL sin agregar una nueva entrada al historial
176
+ setSearchParams(newSearchParams, { replace: true });
177
+ }
178
+ setIsOpen(false);
179
+ };
180
+ const handleSearchChange = (e) => {
181
+ setSearchValue(e.target.value);
182
+ };
183
+ const handleSearchKeyDown = (e) => {
184
+ if (e.key === "Enter") {
185
+ handleSearchSubmit();
186
+ }
187
+ };
188
+ const handleSearchSubmit = () => {
189
+ const newValue = searchValue.trim() || undefined;
190
+ // Si hay onChange, llamarlo con el nuevo valor
191
+ if (onChange) {
192
+ onChange(newValue);
193
+ }
194
+ // Si hay paramName, actualizar el query param
195
+ if (paramName) {
196
+ const newSearchParams = new URLSearchParams(searchParams);
197
+ if (newValue) {
198
+ newSearchParams.set(paramName, newValue);
199
+ }
200
+ else {
201
+ newSearchParams.delete(paramName);
202
+ }
203
+ setSearchParams(newSearchParams, { replace: true });
204
+ }
205
+ };
206
+ const handleSearchIconClick = () => {
207
+ // Verificar si el texto actual coincide con el valor actual
208
+ const valueMatches = currentValue && searchValue.trim() === currentValue;
209
+ if (valueMatches) {
210
+ // Si coincide, limpiar
211
+ const newValue = undefined;
212
+ // Si hay onChange, llamarlo con undefined
213
+ if (onChange) {
214
+ onChange(newValue);
215
+ }
216
+ // Si hay paramName, actualizar el query param
217
+ if (paramName) {
218
+ const newSearchParams = new URLSearchParams(searchParams);
219
+ newSearchParams.delete(paramName);
220
+ setSearchParams(newSearchParams, { replace: true });
221
+ }
222
+ setSearchValue("");
223
+ }
224
+ else {
225
+ // Si no coincide, hacer submit
226
+ handleSearchSubmit();
227
+ }
228
+ };
229
+ const handleAutocompleteChange = () => {
230
+ // Marcar que el usuario está escribiendo para que el AutocompleteInput pueda manejar el texto libremente
231
+ setIsUserTyping(true);
232
+ };
233
+ const handleAutocompleteSelect = () => {
234
+ // Obtener el valor actual del input del AutocompleteInput
235
+ const currentInputValue = autocompleteInputRef.current?.value || "";
236
+ // Buscar si el texto actual coincide con alguna opción
237
+ if (filterType === "autocomplete") {
238
+ const autocompleteProps = props;
239
+ const getOptionLabel = autocompleteProps.getOptionLabel || ((item) => item.label || "");
240
+ const getOptionValue = autocompleteProps.getOptionValue || ((item) => item.value || "");
241
+ // Buscar opción por label
242
+ const matchingOption = autocompleteProps.options.find((opt) => getOptionLabel(opt).toLowerCase() === currentInputValue.toLowerCase());
243
+ let newValue;
244
+ if (matchingOption) {
245
+ newValue = String(getOptionValue(matchingOption));
246
+ setAutocompleteValue(newValue);
247
+ setIsUserTyping(false);
248
+ }
249
+ else if (currentInputValue.trim()) {
250
+ // Si no coincide con ninguna opción, guardar el texto tal cual
251
+ newValue = currentInputValue.trim();
252
+ setAutocompleteValue(newValue);
253
+ setIsUserTyping(false);
254
+ }
255
+ else {
256
+ newValue = undefined;
257
+ setAutocompleteValue("");
258
+ setIsUserTyping(false);
259
+ }
260
+ // Si hay onChange, llamarlo con el nuevo valor
261
+ if (onChange) {
262
+ onChange(newValue);
263
+ }
264
+ // Si hay paramName, actualizar el query param
265
+ if (paramName) {
266
+ const newSearchParams = new URLSearchParams(searchParams);
267
+ if (newValue) {
268
+ newSearchParams.set(paramName, newValue);
269
+ }
270
+ else {
271
+ newSearchParams.delete(paramName);
272
+ }
273
+ setSearchParams(newSearchParams, { replace: true });
274
+ }
275
+ setIsOpen(false);
276
+ }
277
+ };
278
+ const handleAutocompleteOptionSelect = (option) => {
279
+ if (filterType === "autocomplete") {
280
+ const autocompleteProps = props;
281
+ const getOptionValue = autocompleteProps.getOptionValue || ((item) => item.value || "");
282
+ const newValue = String(getOptionValue(option));
283
+ setAutocompleteValue(newValue);
284
+ setIsUserTyping(false);
285
+ // Si hay onChange, llamarlo con el nuevo valor
286
+ if (onChange) {
287
+ onChange(newValue);
288
+ }
289
+ // Si hay paramName, actualizar el query param
290
+ if (paramName) {
291
+ const newSearchParams = new URLSearchParams(searchParams);
292
+ newSearchParams.set(paramName, newValue);
293
+ setSearchParams(newSearchParams, { replace: true });
294
+ }
295
+ setIsOpen(false);
296
+ }
297
+ };
298
+ const handleSearchSelectChange = (value) => {
299
+ if (filterType === "searchSelect") {
300
+ const searchSelectProps = props;
301
+ const getOptionValue = searchSelectProps.getOptionValue || ((item) => item.value || "");
302
+ const getOptionLabel = searchSelectProps.getOptionLabel || ((item) => item.label || "");
303
+ // Si value es un objeto, extraer el valor usando getOptionValue
304
+ // Si value es un string/number, usarlo directamente
305
+ let newValue;
306
+ if (value === undefined || value === null) {
307
+ newValue = undefined;
308
+ setSearchSelectValue(undefined);
309
+ setSearchSelectLabel("");
310
+ }
311
+ else if (typeof value === "object") {
312
+ newValue = String(getOptionValue(value));
313
+ setSearchSelectValue(value);
314
+ setSearchSelectLabel(getOptionLabel(value));
315
+ }
316
+ else {
317
+ newValue = String(value);
318
+ setSearchSelectValue(value);
319
+ // Si solo tenemos el valor, el useEffect se encargará de buscar el label
320
+ setSearchSelectLabel("");
321
+ }
322
+ // Si hay onChange, llamarlo con el nuevo valor
323
+ if (onChange) {
324
+ onChange(newValue);
325
+ }
326
+ // Si hay paramName, actualizar el query param
327
+ if (paramName) {
328
+ const newSearchParams = new URLSearchParams(searchParams);
329
+ if (newValue) {
330
+ newSearchParams.set(paramName, newValue);
331
+ }
332
+ else {
333
+ newSearchParams.delete(paramName);
334
+ }
335
+ setSearchParams(newSearchParams, { replace: true });
336
+ }
337
+ setIsOpen(false);
338
+ }
339
+ };
340
+ const handleDateChange = (date) => {
341
+ // Solo actualizar el estado temporal, no setear en la URL
342
+ setDateValue(date);
343
+ };
344
+ const handleSetDateFilter = () => {
345
+ let newValue;
346
+ if (dateValue && dateValue.isValid()) {
347
+ // Guardar como yyyy-mm-dd
348
+ newValue = dateValue.format("YYYY-MM-DD");
349
+ }
350
+ else {
351
+ newValue = undefined;
352
+ }
353
+ // Si hay onChange, llamarlo con el nuevo valor
354
+ if (onChange) {
355
+ onChange(newValue);
356
+ }
357
+ // Si hay paramName, actualizar el query param
358
+ if (paramName) {
359
+ const newSearchParams = new URLSearchParams(searchParams);
360
+ if (newValue) {
361
+ newSearchParams.set(paramName, newValue);
362
+ }
363
+ else {
364
+ newSearchParams.delete(paramName);
365
+ }
366
+ setSearchParams(newSearchParams, { replace: true });
367
+ }
368
+ setIsOpen(false);
369
+ };
370
+ const handleStaticOptionSelect = (option) => {
371
+ const newValue = option.value;
372
+ // Si hay onChange, llamarlo con el nuevo valor
373
+ if (onChange) {
374
+ onChange(newValue);
375
+ }
376
+ // Si hay paramName, actualizar el query param
377
+ if (paramName) {
378
+ const newSearchParams = new URLSearchParams(searchParams);
379
+ newSearchParams.set(paramName, newValue);
380
+ setSearchParams(newSearchParams, { replace: true });
381
+ }
382
+ setIsOpen(false);
383
+ };
384
+ // Para staticOptions: mostrar el texto de la opción si el valor coincide (prioridad)
385
+ // Para date: convertir yyyy-mm-dd a Dayjs y formatear para mostrar como dd/mm/yyyy
386
+ // Para autocomplete: mostrar el label de la opción si el valor coincide
387
+ const getDisplayValue = () => {
388
+ // Primero verificar si el valor coincide con alguna opción estática
389
+ if (staticOptions && currentValue) {
390
+ const option = staticOptions.find((opt) => opt.value === currentValue);
391
+ if (option) {
392
+ return option.text;
393
+ }
394
+ }
395
+ // Para autocomplete, buscar el label de la opción
396
+ if (filterType === "autocomplete" && currentValue) {
397
+ const autocompleteProps = props;
398
+ if (autocompleteProps.options) {
399
+ const getOptionLabel = autocompleteProps.getOptionLabel || ((item) => item.label || "");
400
+ const getOptionValue = autocompleteProps.getOptionValue || ((item) => item.value || "");
401
+ const option = autocompleteProps.options.find((opt) => String(getOptionValue(opt)) === String(currentValue));
402
+ if (option) {
403
+ return getOptionLabel(option);
404
+ }
405
+ }
406
+ }
407
+ // Para searchSelect, mostrar el label guardado o buscar el label del objeto
408
+ if (filterType === "searchSelect" && currentValue) {
409
+ if (searchSelectLabel) {
410
+ return searchSelectLabel;
411
+ }
412
+ // Si tenemos el objeto completo, extraer el label
413
+ if (searchSelectValue && typeof searchSelectValue === "object") {
414
+ const searchSelectProps = props;
415
+ const getOptionLabel = searchSelectProps.getOptionLabel || ((item) => item.label || "");
416
+ return getOptionLabel(searchSelectValue);
417
+ }
418
+ }
419
+ // Si no hay opción estática que coincida, formatear según el tipo
420
+ if (filterType === "date" && currentValue) {
421
+ const date = dayjs(currentValue, "YYYY-MM-DD");
422
+ if (date.isValid()) {
423
+ return date.format("DD/MM/YYYY");
424
+ }
425
+ }
426
+ return currentValue || "";
427
+ };
428
+ const handleClearFilter = (e) => {
429
+ e.stopPropagation();
430
+ // Si hay onChange, llamarlo con undefined para limpiar
431
+ if (onChange) {
432
+ onChange(undefined);
433
+ }
434
+ // Si hay paramName, actualizar el query param
435
+ if (paramName) {
436
+ const newSearchParams = new URLSearchParams(searchParams);
437
+ newSearchParams.delete(paramName);
438
+ setSearchParams(newSearchParams, { replace: true });
439
+ }
440
+ };
441
+ const handleTogglePanel = () => {
442
+ if (!isOpen) {
443
+ // Calcular posición antes de abrir
444
+ if (containerRef.current) {
445
+ const rect = containerRef.current.getBoundingClientRect();
446
+ setPanelPosition({
447
+ top: rect.bottom + window.scrollY + 4,
448
+ left: rect.left + window.scrollX,
449
+ width: rect.width,
450
+ });
451
+ }
452
+ }
453
+ setIsOpen(!isOpen);
454
+ };
455
+ // Actualizar posición cuando se abre o cuando cambia el scroll
456
+ useEffect(() => {
457
+ if (isOpen && containerRef.current) {
458
+ const updatePosition = () => {
459
+ const rect = containerRef.current?.getBoundingClientRect();
460
+ if (rect) {
461
+ setPanelPosition({
462
+ top: rect.bottom + window.scrollY + 4,
463
+ left: rect.left + window.scrollX,
464
+ width: rect.width,
465
+ });
466
+ }
467
+ };
468
+ updatePosition();
469
+ window.addEventListener("scroll", updatePosition, true);
470
+ window.addEventListener("resize", updatePosition);
471
+ return () => {
472
+ window.removeEventListener("scroll", updatePosition, true);
473
+ window.removeEventListener("resize", updatePosition);
474
+ };
475
+ }
476
+ }, [isOpen]);
477
+ // Hacer foco en el input cuando se abre el panel
478
+ useEffect(() => {
479
+ if (isOpen) {
480
+ // Pequeño delay para asegurar que el DOM esté actualizado
481
+ const timer = setTimeout(() => {
482
+ if (filterType === "text" || filterType === "number") {
483
+ inputRef.current?.focus();
484
+ }
485
+ else if (filterType === "date") {
486
+ dateInputRef.current?.focus();
487
+ }
488
+ else if (filterType === "autocomplete") {
489
+ autocompleteInputRef.current?.focus();
490
+ }
491
+ // searchSelect no necesita focus porque usa un Dialog interno
492
+ }, 50);
493
+ return () => clearTimeout(timer);
494
+ }
495
+ }, [isOpen, filterType]);
496
+ // Si hideEmpty es true y no hay valor, no renderizar el componente
497
+ if (hideEmpty && !currentValue) {
498
+ return null;
499
+ }
500
+ // Contenedor tipo badge con diseño similar al Input
501
+ // Altura ajustada para coincidir con input sm: py-1.5 (6px arriba y abajo) + text-sm (14px línea) = ~26px total
502
+ const badgeContainer = (_jsxs("div", { className: "inline-flex items-center gap-2 px-3 py-1.5 h-[2.1rem] rounded-lg border border-[var(--color-border-default)] bg-[var(--color-bg-default)] text-[var(--color-text-primary)] font-[var(--font-default)] cursor-pointer text-sm transition-colors", onClick: handleTogglePanel, children: [_jsx("span", { className: "text-sm min-w-[1rem]", children: getDisplayValue() || "\u00A0" }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("span", { className: "p-0.5 hover:bg-[var(--color-bg-secondary)] rounded transition-colors flex items-center justify-center", children: _jsx("i", { className: `fa fa-chevron-down text-xs text-[var(--color-text-muted)] hover:text-[var(--color-primary)] transition-all ${isOpen ? "rotate-180" : ""}` }) }), currentValue && (_jsx("button", { onClick: handleClearFilter, className: "p-0.5 hover:bg-[var(--color-bg-secondary)] rounded transition-colors flex items-center justify-center", "aria-label": "Borrar filtro", type: "button", children: _jsx("i", { className: "fa fa-times text-xs text-[var(--color-text-muted)] hover:text-[var(--color-primary)] transition-colors" }) }))] })] }));
503
+ // Renderizar según el tipo de filtro
504
+ if (filterType === "autocomplete") {
505
+ return (_jsxs("div", { ref: containerRef, className: "relative inline-block", children: [_jsx(DataField, { label: label, value: badgeContainer, className: "inline-block" }), isOpen &&
506
+ panelPosition &&
507
+ typeof document !== "undefined" &&
508
+ document.body &&
509
+ createPortal(_jsx("div", { ref: panelRef, className: "fixed z-[2001] w-fit rounded-md border border-[var(--color-border-default)] bg-[var(--color-bg-default)] shadow-[var(--shadow-lg)] p-4", style: {
510
+ top: `${panelPosition.top}px`,
511
+ left: `${panelPosition.left}px`,
512
+ }, children: _jsxs("div", { className: "space-y-3", children: [staticOptions && staticOptions.length > 0 && (_jsx("ul", { className: "py-1 max-h-60 overflow-auto", children: staticOptions.map((option) => (_jsx("li", { className: `px-3 py-2 cursor-pointer flex items-center gap-2 text-sm rounded transition-colors ${currentValue === option.value
513
+ ? "bg-[var(--color-primary-soft)] text-[var(--color-primary)]"
514
+ : "text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)]"}`, onMouseDown: (e) => {
515
+ e.preventDefault();
516
+ handleStaticOptionSelect(option);
517
+ }, children: _jsx("span", { className: "font-[var(--font-default)]", children: option.text }) }, option.value))) })), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { style: { width: finalInputWidth }, children: _jsx(AutocompleteInput, { ref: autocompleteInputRef, options: props.options, value: isUserTyping ? undefined : autocompleteValue, onChange: handleAutocompleteChange, getOptionLabel: props.getOptionLabel, getOptionValue: props.getOptionValue, renderOption: props.renderOption, noResultsText: props.noResultsText, onSelectOption: handleAutocompleteOptionSelect }) }), _jsx(Button, { onClick: handleAutocompleteSelect, icon: "fa-arrow-right", variant: "ghost" })] })] }) }), document.body)] }));
518
+ }
519
+ if (filterType === "date") {
520
+ return (_jsxs("div", { ref: containerRef, className: "relative inline-block", children: [_jsx(DataField, { label: label, value: badgeContainer, className: "inline-block" }), isOpen &&
521
+ panelPosition &&
522
+ typeof document !== "undefined" &&
523
+ document.body &&
524
+ createPortal(_jsx("div", { ref: panelRef, className: "fixed z-[2001] w-fit rounded-md border border-[var(--color-border-default)] bg-[var(--color-bg-default)] shadow-[var(--shadow-lg)] p-4", style: {
525
+ top: `${panelPosition.top}px`,
526
+ left: `${panelPosition.left}px`,
527
+ }, children: _jsxs("div", { className: "space-y-3", children: [staticOptions && staticOptions.length > 0 && (_jsx("ul", { className: "py-1 max-h-60 overflow-auto", children: staticOptions.map((option) => (_jsx("li", { className: `px-3 py-2 cursor-pointer flex items-center gap-2 text-sm rounded transition-colors ${currentValue === option.value
528
+ ? "bg-[var(--color-primary-soft)] text-[var(--color-primary)]"
529
+ : "text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)]"}`, onMouseDown: (e) => {
530
+ e.preventDefault();
531
+ handleStaticOptionSelect(option);
532
+ }, children: _jsx("span", { className: "font-[var(--font-default)]", children: option.text }) }, option.value))) })), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { style: { width: finalInputWidth }, children: _jsx(DateInput, { ref: dateInputRef, value: dateValue, onChange: handleDateChange, format: "dd/mm/yyyy" }) }), _jsx(Button, { onClick: handleSetDateFilter, icon: "fa-arrow-right", variant: "ghost" })] })] }) }), document.body)] }));
533
+ }
534
+ // Para searchSelect
535
+ if (filterType === "searchSelect") {
536
+ const searchSelectProps = props;
537
+ return (_jsxs("div", { ref: containerRef, className: "relative inline-block", children: [_jsx(DataField, { label: label, value: badgeContainer, className: "inline-block" }), isOpen &&
538
+ panelPosition &&
539
+ typeof document !== "undefined" &&
540
+ document.body &&
541
+ createPortal(_jsx("div", { ref: panelRef, className: "fixed z-[2001] w-fit rounded-md border border-[var(--color-border-default)] bg-[var(--color-bg-default)] shadow-[var(--shadow-lg)] p-4", style: {
542
+ top: `${panelPosition.top}px`,
543
+ left: `${panelPosition.left}px`,
544
+ }, children: _jsxs("div", { className: "space-y-3", children: [staticOptions && staticOptions.length > 0 && (_jsx("ul", { className: "py-1 max-h-60 overflow-auto", children: staticOptions.map((option) => (_jsx("li", { className: `px-3 py-2 cursor-pointer flex items-center gap-2 text-sm rounded transition-colors ${currentValue === option.value
545
+ ? "bg-[var(--color-primary-soft)] text-[var(--color-primary)]"
546
+ : "text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)]"}`, onMouseDown: (e) => {
547
+ e.preventDefault();
548
+ handleStaticOptionSelect(option);
549
+ }, children: _jsx("span", { className: "font-[var(--font-default)]", children: option.text }) }, option.value))) })), _jsx("div", { style: { width: finalInputWidth }, children: _jsx(SearchSelectInput, { ref: searchSelectInputRef, value: searchSelectValue, onChange: handleSearchSelectChange, onSearchPromiseFn: searchSelectProps.onSearchPromiseFn, onSingleSearchPromiseFn: searchSelectProps.onSingleSearchPromiseFn, getOptionLabel: searchSelectProps.getOptionLabel, getOptionValue: searchSelectProps.getOptionValue, renderOption: searchSelectProps.renderOption, dialogTitle: searchSelectProps.dialogTitle, noResultsText: searchSelectProps.noResultsText }) })] }) }), document.body)] }));
550
+ }
551
+ // Para search
552
+ if (filterType === "search") {
553
+ // Mostrar X solo si hay un valor actual Y el texto del input coincide con ese valor
554
+ const hasValue = !!currentValue;
555
+ const valueMatches = hasValue && searchValue.trim() === currentValue;
556
+ return (_jsx("div", { ref: containerRef, className: "relative inline-block", children: _jsx(DataField, { label: label, value: _jsx("div", { style: { width: finalInputWidth }, children: _jsx(Input, { ref: searchInputRef, type: "text", value: searchValue, onChange: handleSearchChange, onKeyDown: handleSearchKeyDown, icon: valueMatches ? "fa-times" : "fa-search", iconPosition: "right", onIconClick: handleSearchIconClick, placeholder: "Buscar...", size: "sm" }) }), className: "inline-block" }) }));
557
+ }
558
+ // Para text y number
559
+ return (_jsxs("div", { ref: containerRef, className: "relative inline-block", children: [_jsx(DataField, { label: label, value: badgeContainer, className: "inline-block" }), isOpen &&
560
+ panelPosition &&
561
+ typeof document !== "undefined" &&
562
+ document.body &&
563
+ createPortal(_jsx("div", { ref: panelRef, className: "fixed z-[2001] w-fit rounded-md border border-[var(--color-border-default)] bg-[var(--color-bg-default)] shadow-[var(--shadow-lg)] p-4", style: {
564
+ top: `${panelPosition.top}px`,
565
+ left: `${panelPosition.left}px`,
566
+ }, children: _jsxs("div", { className: "space-y-3", children: [staticOptions && staticOptions.length > 0 && (_jsx("ul", { className: "py-1 max-h-60 overflow-auto", children: staticOptions.map((option) => (_jsx("li", { className: `px-3 py-2 cursor-pointer flex items-center gap-2 text-sm rounded transition-colors ${urlValue === option.value
567
+ ? "bg-[var(--color-primary-soft)] text-[var(--color-primary)]"
568
+ : "text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)]"}`, onMouseDown: (e) => {
569
+ e.preventDefault();
570
+ handleStaticOptionSelect(option);
571
+ }, children: _jsx("span", { className: "font-[var(--font-default)]", children: option.text }) }, option.value))) })), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { style: { width: finalInputWidth }, children: _jsx(Input, { ref: inputRef, type: filterType === "number" ? "number" : "text", value: inputValue, onChange: (e) => setInputValue(e.target.value), placeholder: "Ingresa un valor", min: filterType === "number"
572
+ ? props.min
573
+ : undefined, max: filterType === "number"
574
+ ? props.max
575
+ : undefined, onKeyDown: (e) => {
576
+ if (e.key === "Enter") {
577
+ handleSetFilter();
578
+ }
579
+ } }) }), _jsx(Button, { onClick: handleSetFilter, icon: "fa-arrow-right", variant: "ghost" })] })] }) }), document.body)] }));
580
+ };
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ import { type FilterProps } from "./Filter";
3
+ export interface FilterConfig {
4
+ filterType: FilterProps["filterType"];
5
+ paramName: string;
6
+ label?: string;
7
+ staticOptions?: FilterProps["staticOptions"];
8
+ inputWidth?: string;
9
+ min?: number;
10
+ max?: number;
11
+ options?: any[];
12
+ getOptionLabel?: (item: any) => string;
13
+ getOptionValue?: (item: any) => any;
14
+ renderOption?: (item: any) => React.ReactNode;
15
+ noResultsText?: string;
16
+ }
17
+ export interface FiltersDialogProps {
18
+ filters: FilterConfig[];
19
+ }
20
+ export declare const FiltersDialog: React.FC<FiltersDialogProps>;
21
+ //# sourceMappingURL=FiltersDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FiltersDialog.d.ts","sourceRoot":"","sources":["../../../src/components/utils/FiltersDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAGpD,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,WAAW,CAAC,YAAY,CAAC,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC;IAChB,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,MAAM,CAAC;IACvC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,GAAG,CAAC;IACpC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,KAAK,CAAC,SAAS,CAAC;IAC9C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAgJtD,CAAC"}