@jvsoft/utils 0.0.13-alpha.6 → 1.0.0-alpha.11

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 (72) hide show
  1. package/README.md +5 -0
  2. package/directives/auto-select-first.directive.d.ts +13 -0
  3. package/directives/autocomplete-match-validator.directive.d.ts +10 -0
  4. package/directives/autocomplete.directive.d.ts +23 -0
  5. package/{src/functions → directives}/index.d.ts +1 -1
  6. package/directives/popover/popover-listener.directive.d.ts +12 -0
  7. package/directives/popover/popover-panel.component.d.ts +13 -0
  8. package/directives/popover/popover.directive.d.ts +23 -0
  9. package/directives/popover/popover.interface.d.ts +8 -0
  10. package/directives/popover/popover.service.d.ts +17 -0
  11. package/directives/public-api.d.ts +8 -0
  12. package/fesm2022/jvsoft-utils-directives.mjs +459 -0
  13. package/fesm2022/jvsoft-utils-directives.mjs.map +1 -0
  14. package/fesm2022/{jvsoft-utils-src-functions.mjs → jvsoft-utils-functions.mjs} +485 -191
  15. package/fesm2022/jvsoft-utils-functions.mjs.map +1 -0
  16. package/fesm2022/{jvsoft-utils-src-interfaces.mjs → jvsoft-utils-interfaces.mjs} +1 -1
  17. package/fesm2022/jvsoft-utils-interfaces.mjs.map +1 -0
  18. package/fesm2022/{jvsoft-utils-src-pipes.mjs → jvsoft-utils-pipes.mjs} +57 -2
  19. package/fesm2022/jvsoft-utils-pipes.mjs.map +1 -0
  20. package/fesm2022/jvsoft-utils-services.mjs +105 -0
  21. package/fesm2022/jvsoft-utils-services.mjs.map +1 -0
  22. package/fesm2022/jvsoft-utils.mjs +1084 -197
  23. package/fesm2022/jvsoft-utils.mjs.map +1 -1
  24. package/functions/base64.d.ts +63 -0
  25. package/functions/dev-log.d.ts +97 -0
  26. package/functions/index.d.ts +4 -0
  27. package/functions/mat-form-controls/autocomplete.d.ts +43 -27
  28. package/functions/objects-arrays.d.ts +13 -97
  29. package/functions/public-api.d.ts +1 -0
  30. package/interfaces/index.d.ts +4 -0
  31. package/package.json +32 -15
  32. package/pipes/display-with.pipe.d.ts +14 -0
  33. package/pipes/index.d.ts +4 -0
  34. package/pipes/public-api.d.ts +1 -0
  35. package/public-api.d.ts +6 -4
  36. package/{src/pipes → services}/index.d.ts +1 -1
  37. package/services/public-api.d.ts +1 -0
  38. package/services/reloj.service.d.ts +31 -0
  39. package/fesm2022/jvsoft-utils-src-functions.mjs.map +0 -1
  40. package/fesm2022/jvsoft-utils-src-interfaces.mjs.map +0 -1
  41. package/fesm2022/jvsoft-utils-src-pipes.mjs.map +0 -1
  42. package/src/functions/base64.d.ts +0 -26
  43. package/src/functions/browser.d.ts +0 -1
  44. package/src/functions/crypto-js.d.ts +0 -2
  45. package/src/functions/date.d.ts +0 -3
  46. package/src/functions/email.d.ts +0 -2
  47. package/src/functions/file.d.ts +0 -10
  48. package/src/functions/forms.d.ts +0 -23
  49. package/src/functions/http-client.d.ts +0 -2
  50. package/src/functions/local-storage.d.ts +0 -29
  51. package/src/functions/mat-form-controls/autocomplete.d.ts +0 -51
  52. package/src/functions/mat-form-controls/index.d.ts +0 -2
  53. package/src/functions/number.d.ts +0 -2
  54. package/src/functions/object-transformation.d.ts +0 -2
  55. package/src/functions/objects-arrays.d.ts +0 -147
  56. package/src/functions/public-api.d.ts +0 -16
  57. package/src/functions/string.d.ts +0 -23
  58. package/src/functions/sweetalert.d.ts +0 -5
  59. package/src/functions/utiles.d.ts +0 -1
  60. package/src/interfaces/datos.d.ts +0 -4
  61. package/src/interfaces/index.d.ts +0 -5
  62. package/src/interfaces/public-api.d.ts +0 -1
  63. package/src/pipes/data-en-lista.pipe.d.ts +0 -8
  64. package/src/pipes/date-diff-string.pipe.d.ts +0 -17
  65. package/src/pipes/filtro.pipe.d.ts +0 -18
  66. package/src/pipes/form-control-is-required.pipe.d.ts +0 -9
  67. package/src/pipes/json-parse.pipe.d.ts +0 -7
  68. package/src/pipes/no-sanitize.pipe.d.ts +0 -10
  69. package/src/pipes/public-api.d.ts +0 -8
  70. package/src/pipes/tipo-valor-funcion.pipe.d.ts +0 -9
  71. package/src/pipes/zero-fill.pipe.d.ts +0 -8
  72. /package/{classes → src/classes}/data-model.d.ts +0 -0
@@ -1,5 +1,6 @@
1
- import { untilDestroyed } from '@ngneat/until-destroy';
2
- import { startWith, debounceTime, tap, isObservable, switchMap, of, finalize } from 'rxjs';
1
+ import { inject, DestroyRef } from '@angular/core';
2
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
3
+ import { tap, startWith, debounceTime, isObservable, switchMap, of, finalize } from 'rxjs';
3
4
  import { map } from 'rxjs/operators';
4
5
  import { Buffer } from 'buffer';
5
6
  import CryptoJS from 'crypto-js';
@@ -11,14 +12,6 @@ import moment from 'moment';
11
12
  import swal from 'sweetalert2';
12
13
  import { jwtDecode } from 'jwt-decode';
13
14
 
14
- /**
15
- * Realiza una fusión profunda entre dos objetos, sin modificar el original.
16
- * Si el valor en target no es un objeto, lo reemplaza directamente.
17
- *
18
- * @param source - Objeto fuente que se clonará y fusionará.
19
- * @param target - Objeto destino cuyos valores se fusionarán.
20
- * @returns Un nuevo objeto con la fusión profunda.
21
- */
22
15
  function deepMerge(source, target) {
23
16
  // Crea un clon profundo sin usar JSON.parse(JSON.stringify)
24
17
  let cloneSource = deepClone(source);
@@ -40,12 +33,7 @@ function deepMerge(source, target) {
40
33
  }
41
34
  return cloneSource; // Retorna el clon y no modifica el original
42
35
  }
43
- /**
44
- * Realiza una clonación profunda de un objeto, manejando funciones, fechas y arrays.
45
- *
46
- * @param obj - Objeto a clonar.
47
- * @returns Una copia profunda del objeto.
48
- */
36
+ // Función de clonación profunda que maneja funciones, fechas y otros tipos especiales
49
37
  function deepClone(obj) {
50
38
  if (obj === null || typeof obj !== 'object') {
51
39
  return obj; // Devuelve el valor si no es un objeto
@@ -71,7 +59,6 @@ function deepClone(obj) {
71
59
  /**
72
60
  * Busca un elemento en un array o jerarquía de objetos según un campo y valor especificado.
73
61
  *
74
- * @param datosFn - Objeto con los parámetros de búsqueda: items, campo, valor y opcionalmente campoHijo.
75
62
  * @returns El elemento encontrado o undefined si no existe.
76
63
  */
77
64
  function buscarPorCampo(datosFn) {
@@ -98,33 +85,13 @@ function buscarPorCampo(datosFn) {
98
85
  // Si no se encuentra nada, retorna undefined
99
86
  return undefined;
100
87
  }
101
- /**
102
- * Suma los valores de las propiedades especificadas de un objeto.
103
- *
104
- * @param item - Objeto con las propiedades a sumar.
105
- * @param campos - Array de nombres de las propiedades a sumar.
106
- * @returns La suma de los valores de las propiedades.
107
- */
108
88
  function sumarPropiedades(item, campos) {
109
89
  const datosSumar = campos.map(campo => (item[campo] * 1));
110
90
  return datosSumar.reduce((a, b) => a + b, 0);
111
91
  }
112
- /**
113
- * Verifica si el valor proporcionado es un número válido.
114
- *
115
- * @param value - Valor a verificar.
116
- * @returns true si es un número, false en caso contrario.
117
- */
118
92
  function esNumero(value) {
119
93
  return !isNaN(Number(value));
120
94
  }
121
- /**
122
- * Suma los valores de las propiedades especificadas en un array de objetos.
123
- *
124
- * @param arrayObjetos - Array de objetos a procesar.
125
- * @param campos - Array de nombres de las propiedades a sumar.
126
- * @returns Objeto con la suma de cada propiedad.
127
- */
128
95
  function sumarObjetos(arrayObjetos, campos) {
129
96
  return arrayObjetos.reduce((accumulator, item) => {
130
97
  campos.forEach(campo => {
@@ -136,22 +103,9 @@ function sumarObjetos(arrayObjetos, campos) {
136
103
  return accumulator;
137
104
  }, {});
138
105
  }
139
- /**
140
- * Obtiene los valores únicos de un array.
141
- *
142
- * @param array - Array de valores.
143
- * @returns Array con los valores únicos.
144
- */
145
106
  function getUniqueValues(array) {
146
107
  return array.filter((currentValue, index, arr) => (arr.indexOf(currentValue) === index));
147
108
  }
148
- /**
149
- * Obtiene los objetos únicos de un array según una propiedad específica.
150
- *
151
- * @param objetos - Array de objetos.
152
- * @param campo - Nombre de la propiedad para determinar unicidad.
153
- * @returns Array de objetos únicos por la propiedad.
154
- */
155
109
  function getUniqueValuesByProperty(objetos, campo) {
156
110
  const objetosUnicos = {};
157
111
  objetos.forEach(objeto => {
@@ -167,14 +121,6 @@ function getUniqueValuesByProperty(objetos, campo) {
167
121
  // Convertir el objeto de claves únicas de vuelta a una lista de objetos
168
122
  return Object.values(objetosUnicos);
169
123
  }
170
- /**
171
- * Ordena un array de valores numéricos o alfabéticos.
172
- *
173
- * @param array - Array a ordenar.
174
- * @param numeros - Si es true, ordena como números.
175
- * @param sentido - 'ASC' para ascendente, 'DESC' para descendente.
176
- * @returns Array ordenado.
177
- */
178
124
  function ordenarArray(array, numeros = false, sentido = 'ASC') {
179
125
  if (numeros) {
180
126
  if (sentido != 'ASC') {
@@ -184,24 +130,9 @@ function ordenarArray(array, numeros = false, sentido = 'ASC') {
184
130
  }
185
131
  return array.sort((a, b) => (a > b) ? 1 : ((b > a) ? -1 : 0));
186
132
  }
187
- /**
188
- * Ordena un array de objetos por una propiedad específica.
189
- *
190
- * @param objData - Array de objetos a ordenar.
191
- * @param propiedad - Propiedad por la que se ordena.
192
- * @param numeros - (Obsoleto) Si es true, ordena como números.
193
- * @returns Array ordenado.
194
- */
195
133
  function ordenarPorPropiedad(objData, propiedad, /**@deprecated*/ numeros = false) {
196
134
  return ordenarPorPropiedades(objData, { propiedades: [propiedad], direcciones: ['asc'] });
197
135
  }
198
- /**
199
- * Ordena un array de objetos por varias propiedades y direcciones.
200
- *
201
- * @param arr - Array de objetos a ordenar.
202
- * @param options - Opciones con propiedades y direcciones de orden.
203
- * @returns Array ordenado.
204
- */
205
136
  function ordenarPorPropiedades(arr, options) {
206
137
  const { propiedades, direcciones = [] } = options;
207
138
  const orden = direcciones.map(d => d === 'desc' ? -1 : 1);
@@ -221,13 +152,6 @@ function ordenarPorPropiedades(arr, options) {
221
152
  }, 0);
222
153
  });
223
154
  }
224
- /**
225
- * Agrupa los elementos de un array según una clave o función de clave.
226
- *
227
- * @param array - Array de objetos a agrupar.
228
- * @param key - Propiedad o función para agrupar.
229
- * @returns Objeto con los grupos por clave.
230
- */
231
155
  function groupBy(array, key) {
232
156
  const keyFn = key instanceof Function ? key : (obj) => obj[key];
233
157
  return array.reduce((objectsByKeyValue, obj) => {
@@ -236,13 +160,6 @@ function groupBy(array, key) {
236
160
  return objectsByKeyValue;
237
161
  }, {});
238
162
  }
239
- /**
240
- * Agrupa y anida los elementos de un array según una lista de propiedades.
241
- *
242
- * @param arr - Array de objetos a agrupar.
243
- * @param properties - Propiedades para agrupar de forma anidada.
244
- * @returns Objeto anidado por los grupos de propiedades.
245
- */
246
163
  function nestGroupsBy(arr, properties) {
247
164
  const fnGroupBy = (conversions, property2) => {
248
165
  return conversions.reduce((acc, obj) => {
@@ -316,13 +233,6 @@ function eliminarColumnaPorIndex(data, columnIndex) {
316
233
  return newRow;
317
234
  });
318
235
  }
319
- /**
320
- * Elimina elementos duplicados de un array de objetos según claves específicas.
321
- *
322
- * @param array - Array de objetos.
323
- * @param claves - Claves para determinar unicidad. Si no se especifica, compara todo el objeto.
324
- * @returns Array sin duplicados.
325
- */
326
236
  function eliminarDuplicados(array, claves) {
327
237
  const unicos = new Map();
328
238
  for (const item of array) {
@@ -335,14 +245,6 @@ function eliminarDuplicados(array, claves) {
335
245
  }
336
246
  return Array.from(unicos.values());
337
247
  }
338
- /**
339
- * Elimina elementos de un array origen que estén presentes en otro array, según claves específicas.
340
- *
341
- * @param origen - Array original.
342
- * @param elementosAEliminar - Elementos a eliminar del array origen.
343
- * @param claves - Claves para comparar los objetos. Si no se especifica, compara todo el objeto.
344
- * @returns Array filtrado sin los elementos eliminados.
345
- */
346
248
  function eliminarElementos(origen, elementosAEliminar, claves) {
347
249
  const clavesSet = new Set();
348
250
  for (const item of elementosAEliminar) {
@@ -358,41 +260,12 @@ function eliminarElementos(origen, elementosAEliminar, claves) {
358
260
  return !clavesSet.has(key);
359
261
  });
360
262
  }
361
-
362
- /**
363
- * Devuelve un valor de visualización para un elemento buscado.
364
- * Compatible con listas simples, objetos y campos múltiples.
365
- */
366
- function mostrarValorEnBusqueda(campos, idxSel) {
367
- const buscarEnLista = (lista) => {
368
- if (!lista)
369
- return null;
370
- if (campos.campoId === '*object*' || campos.campoId === '*objeto*') {
371
- return lista.find((x) => JSON.stringify(x).trim() === JSON.stringify(idxSel).trim());
372
- }
373
- return lista.find((x) => x[campos.campoId] === idxSel);
374
- };
375
- const impDataMostrar = () => {
376
- let vD = buscarEnLista(campos.lista) || (campos.opcExtra && buscarEnLista(campos.opcExtra));
377
- if (!vD)
378
- return '';
379
- if (Array.isArray(campos.campoValue)) {
380
- return campos.campoValue.map((vCampo) => vD[vCampo] ?? '').join(' - ').trim();
381
- }
382
- return (vD[campos.campoValue] ?? '').trim();
383
- };
384
- if (esNumero(idxSel)) {
385
- if ((idxSel > 0 && campos.lista?.length) || (idxSel && typeof idxSel === 'object')) {
386
- return impDataMostrar();
387
- }
388
- }
389
- else if (campos.lista?.length) {
390
- return impDataMostrar();
391
- }
392
- return '';
393
- }
394
263
  /**
395
- * Filtra datos locales de un array basado en un valor de búsqueda.
264
+ * Filtra datos localmente según un valor de búsqueda
265
+ * @param data - Array de datos a filtrar
266
+ * @param value - Valor de búsqueda
267
+ * @param campoBuscar - Campo(s) por el cual buscar
268
+ * @returns Array filtrado
396
269
  */
397
270
  function filtrarDatosLocal(data, value, campoBuscar) {
398
271
  if (!value)
@@ -411,32 +284,286 @@ function filtrarDatosLocal(data, value, campoBuscar) {
411
284
  });
412
285
  });
413
286
  }
287
+
414
288
  /**
415
- * Vincula un FormControl a datos locales para autocompletado.
289
+ * Intenta obtener el DestroyRef desde el llamador (objThis/control)
290
+ * o inyectándolo si estamos en un contexto de inyección.
416
291
  */
417
- function changeSelectData(objThis, { formControl, data, campoBuscar, variableResultado }) {
418
- objThis.filtrados[variableResultado] = formControl.valueChanges.pipe(untilDestroyed(objThis)).pipe(startWith(''), map(value => data ? filtrarDatosLocal(data, value, campoBuscar) : []));
292
+ function obtenerDestroyRef(caller, manualDRef, fnName = 'N/A') {
293
+ let dRef = manualDRef || caller?.['destroyRef'] || caller?.['_destroyRef'];
294
+ if (!dRef) {
295
+ try {
296
+ dRef = inject(DestroyRef, { optional: true });
297
+ }
298
+ catch (e) {
299
+ console.warn(`${fnName}: Falló inject(DestroyRef) (fuera del contexto de inyección). Pasa el destroyRef explícitamente o decláralo en el componente para habilitar la limpieza.`);
300
+ }
301
+ }
302
+ return dRef ?? undefined;
303
+ }
304
+ function mostrarValorEnBusqueda(campos, idxSel) {
305
+ const impDataMostrar = () => {
306
+ let vD;
307
+ if (campos.campoId == '*object*' || campos.campoId == '*objeto*') {
308
+ console.log(campos);
309
+ vD = campos.lista.find((x) => JSON.stringify(x).trim() == JSON.stringify(idxSel).trim());
310
+ console.log(vD);
311
+ }
312
+ else {
313
+ vD = campos.lista.find((x) => x[campos.campoId] == idxSel);
314
+ }
315
+ if (!vD && campos.opcExtra) {
316
+ console.log('eval ', campos.opcExtra);
317
+ if (campos.campoId == '*object*' || campos.campoId == '*objeto*') {
318
+ console.log(campos);
319
+ vD = campos.opcExtra.find((x) => JSON.stringify(x).trim() == JSON.stringify(idxSel).trim());
320
+ console.log(vD);
321
+ }
322
+ else {
323
+ vD = campos.opcExtra.find((x) => x[campos.campoId] == idxSel);
324
+ }
325
+ }
326
+ if (vD) {
327
+ let txtFinal = '';
328
+ if (Array.isArray(campos.campoValue)) {
329
+ campos.campoValue.forEach((vCampo, idx) => {
330
+ txtFinal += (vD[vCampo] ?? '');
331
+ if (idx < campos.campoValue.length - 1) {
332
+ txtFinal += ' - ';
333
+ }
334
+ });
335
+ }
336
+ else {
337
+ txtFinal = vD[campos.campoValue] ?? '';
338
+ }
339
+ return txtFinal.trim();
340
+ }
341
+ else {
342
+ console.log('ASSSSS ----- SSSS ');
343
+ }
344
+ return '';
345
+ };
346
+ if (esNumero(idxSel)) {
347
+ if (idxSel > 0 && campos.lista?.length > 0) {
348
+ return impDataMostrar();
349
+ }
350
+ else if (idxSel && typeof idxSel == 'object') {
351
+ return impDataMostrar();
352
+ }
353
+ }
354
+ else {
355
+ if (campos.lista?.length > 0) {
356
+ return impDataMostrar();
357
+ }
358
+ }
359
+ return '';
360
+ }
361
+ /**
362
+ * @deprecated Use JvsAutocompleteDirective en su lugar para un enfoque más declarativo y robusto.
363
+ */
364
+ function changeSelectData(objThis, dataFiltro) {
365
+ const dRef = obtenerDestroyRef(objThis, dataFiltro.destroyRef, 'changeSelectData');
366
+ objThis['filtrados'][dataFiltro.variableResultado] = dataFiltro.formControl.valueChanges.pipe(dRef ? takeUntilDestroyed(dRef) : tap(() => { })).pipe(startWith(''), map(value => {
367
+ const varN = dataFiltro.data;
368
+ if (varN) {
369
+ if (value) {
370
+ return varN.map(x => x).filter(dat => {
371
+ if (Array.isArray(dataFiltro.campoBuscar)) {
372
+ let encontrado = false;
373
+ for (const vCampo of dataFiltro.campoBuscar) {
374
+ // console.log(vCampo, value, dat[vCampo]);
375
+ if (isNaN(Number(value))) {
376
+ // NO ES NUMERO
377
+ if (value && dat[vCampo] && dat[vCampo].toLowerCase().includes(value?.toString().toLowerCase())) {
378
+ encontrado = true;
379
+ break;
380
+ }
381
+ }
382
+ else {
383
+ if (value && dat[vCampo] && dat[vCampo].toString().includes(value?.toString())) {
384
+ encontrado = true;
385
+ break;
386
+ }
387
+ }
388
+ }
389
+ return encontrado;
390
+ }
391
+ else {
392
+ if (isNaN(Number(value))) {
393
+ return dat[dataFiltro.campoBuscar].toLowerCase().includes(value?.toString().toLowerCase());
394
+ }
395
+ else {
396
+ return dat[dataFiltro.campoBuscar].toString().includes(value?.toString());
397
+ }
398
+ }
399
+ });
400
+ }
401
+ return varN;
402
+ }
403
+ return false;
404
+ }));
419
405
  }
420
406
  /**
421
- * Vincula un FormControl a datos locales obtenidos de dataServidor o dataServidorSuscripcion.
407
+ * @deprecated Use JvsAutocompleteDirective en su lugar para un enfoque más declarativo y robusto.
422
408
  */
423
- function changeSelect(control, formControl, tipo, campoBuscar, campoFiltro = null) {
424
- const filtro = campoFiltro ?? tipo;
425
- control.filtrados[filtro] = formControl.valueChanges.pipe(untilDestroyed(control)).pipe(startWith(''), map(value => {
409
+ function changeSelect(control, formControl, tipo, campoBuscar, campoFiltro = null, destroyRef) {
410
+ // console.log(formControl);
411
+ // const formGroup = formControl.parent.controls;
412
+ // console.warn( Object.keys(formGroup).find(name => formControl === formGroup[name]) || null );
413
+ if (!campoFiltro) {
414
+ campoFiltro = tipo;
415
+ }
416
+ const dRef = obtenerDestroyRef(control, destroyRef, 'changeSelect');
417
+ control['filtrados'][campoFiltro ?? '__'] = formControl.valueChanges.pipe(dRef ? takeUntilDestroyed(dRef) : tap(() => { })).pipe(startWith(''), map(value => {
418
+ // console.warn(value);
426
419
  const partes = tipo.split('.');
427
- const varN = control.dataServidor?.[partes[0]]?.[partes[1]] ??
428
- control.dataServidor?.[tipo] ??
429
- control.dataServidorSuscripcion?.[tipo]?.getValue();
430
- return varN ? filtrarDatosLocal(varN, value, campoBuscar) : [];
420
+ let varN;
421
+ if (control['dataServidor']) {
422
+ varN = (partes.length > 1) ? control['dataServidor'][partes[0]][partes[1]] : control['dataServidor'][tipo];
423
+ }
424
+ else if (control['dataServidorSuscripcion']) {
425
+ varN = control['dataServidorSuscripcion'][tipo].getValue();
426
+ }
427
+ if (varN) {
428
+ if (value) {
429
+ return varN.map((x) => x).filter((dat) => {
430
+ if (Array.isArray(campoBuscar)) {
431
+ let encontrado = false;
432
+ for (const vCampo of campoBuscar) {
433
+ // console.log(vCampo, value, dat[vCampo]);
434
+ if (isNaN(Number(value))) {
435
+ // NO ES NUMERO
436
+ if (value && dat[vCampo] && dat[vCampo].toLowerCase().includes(value?.toString().toLowerCase())) {
437
+ encontrado = true;
438
+ break;
439
+ }
440
+ }
441
+ else {
442
+ if (value && dat[vCampo] && dat[vCampo].toString().includes(value?.toString())) {
443
+ encontrado = true;
444
+ break;
445
+ }
446
+ }
447
+ }
448
+ return encontrado;
449
+ }
450
+ else {
451
+ if (isNaN(Number(value))) {
452
+ return dat[campoBuscar].toLowerCase().includes(value?.toString().toLowerCase());
453
+ }
454
+ else {
455
+ return dat[campoBuscar].toString().includes(value?.toString());
456
+ }
457
+ }
458
+ });
459
+ }
460
+ return varN;
461
+ }
462
+ return false;
431
463
  }));
432
464
  }
465
+ /**
466
+ * @deprecated Use JvsAutocompleteDirective en su lugar para un enfoque más declarativo y robusto.
467
+ */
468
+ function changeSelectDataApi(objThis, dataFiltro) {
469
+ if (!dataFiltro.variableResultado) {
470
+ dataFiltro.variableResultado = dataFiltro.tipoReq;
471
+ }
472
+ const idFiltrado = dataFiltro.variableResultado;
473
+ const dRef = obtenerDestroyRef(objThis, dataFiltro.destroyRef, 'changeSelectDataApi');
474
+ dataFiltro.formControl.valueChanges.pipe(dRef ? takeUntilDestroyed(dRef) : tap(() => { }), debounceTime(500), tap(() => {
475
+ objThis.filtrados[dataFiltro.variableResultado + 'tmp'] = isObservable(objThis.filtrados[idFiltrado]) ? [] : objThis.filtrados[idFiltrado] || [];
476
+ if (objThis.filtrados[idFiltrado] !== objThis.filtrados[idFiltrado + 'tmp']) {
477
+ objThis.filtrados[idFiltrado] = [];
478
+ }
479
+ objThis.isLoading = true;
480
+ }), switchMap(value => {
481
+ if (dataFiltro.campoId) {
482
+ const busquedaActual2 = objThis.filtrados[idFiltrado + 'tmp'].findIndex((item) => item[dataFiltro.campoId ?? '--'] === value);
483
+ if (busquedaActual2 >= 0) {
484
+ return of({ [dataFiltro.tipoReq]: objThis.filtrados[idFiltrado + 'tmp'] });
485
+ }
486
+ }
487
+ return !value || value.length < (dataFiltro.minLength ?? 3) ? [] : (dataFiltro.queryService.getDataMethod('GET', dataFiltro.tipoReq, {
488
+ ...(dataFiltro.dataExtra ?? {}),
489
+ ...(dataFiltro.dataExtraVariable ? Object.fromEntries(dataFiltro.dataExtraVariable.map((objData) => [objData.campo, objData.ctrlValue.value])) : {}),
490
+ txtBuscar: value,
491
+ }, dataFiltro.anonimo).pipe(finalize(() => objThis.isLoading = false)));
492
+ })).subscribe((data) => {
493
+ objThis.filtrados[idFiltrado] = data[dataFiltro.tipoReq] ?? [];
494
+ });
495
+ }
496
+ /**
497
+ * @deprecated Use JvsAutocompleteDirective en su lugar para un enfoque más declarativo y robusto.
498
+ */
499
+ function changeSelectApi(control, queryService, formControl, tipo, dataExtra = {}, dataExtraVariable = null, minLength = 1, anonimo = false, destroyRef) {
500
+ const dRef = obtenerDestroyRef(control, destroyRef, 'changeSelectApi');
501
+ formControl.valueChanges.pipe(dRef ? takeUntilDestroyed(dRef) : tap(() => { }), debounceTime(500), tap((value) => {
502
+ control['filtrados'][tipo + 'tmp'] = isObservable(control['filtrados'][tipo]) ? [] : control['filtrados'][tipo];
503
+ if (control['filtrados'][tipo] != control['filtrados'][tipo + 'tmp']) {
504
+ control['filtrados'][tipo] = [];
505
+ }
506
+ control['isLoading'] = true;
507
+ }), switchMap(value => {
508
+ const formGroup = formControl.parent?.controls;
509
+ const nombreControl = Object.keys(formGroup).find(name => formControl === formGroup[name]) || null;
510
+ if (nombreControl && control['filtrados'][tipo + 'tmp'] && control['filtrados'][tipo + 'tmp'].length > 0) {
511
+ const busquedaActual = control['filtrados'][tipo + 'tmp'].findIndex((item) => item[nombreControl] == value);
512
+ if (busquedaActual >= 0) {
513
+ const vRet = {};
514
+ vRet[tipo] = control['filtrados'][tipo + 'tmp'];
515
+ control['isLoading'] = false;
516
+ return of(vRet);
517
+ }
518
+ }
519
+ if (!value || value.length < minLength) {
520
+ return [];
521
+ }
522
+ const dataExtraVariableData = {};
523
+ if (dataExtraVariable) {
524
+ // @ts-ignore
525
+ for (const objData of dataExtraVariable) {
526
+ dataExtraVariableData[objData.campo] = objData.ctrlValue.value;
527
+ }
528
+ }
529
+ return queryService.getDataMethod('GET', tipo, { ...dataExtra, ...dataExtraVariableData, ...{ txtBuscar: value } }, anonimo).pipe(finalize(() => {
530
+ control['isLoading'] = false;
531
+ }));
532
+ })).subscribe((data) => {
533
+ if (data[tipo] == undefined) {
534
+ control['filtrados'][tipo] = [];
535
+ }
536
+ else {
537
+ control['filtrados'][tipo] = data[tipo];
538
+ }
539
+ });
540
+ }
541
+ /**
542
+ * Comprueba si un valor es una Promesa
543
+ * @param valor - Valor a verificar
544
+ * @returns true si es una Promise
545
+ */
546
+ function esPromise(valor) {
547
+ return !!valor && typeof valor.then === 'function';
548
+ }
549
+ /**
550
+ * Selecciona todo el texto de un input
551
+ * @param event - Evento del input
552
+ */
553
+ function seleccionarTextoInput$1(event) {
554
+ event.target.select();
555
+ }
433
556
  /**
434
557
  * Función genérica para vincular un FormControl con datos desde API o Promise.
435
558
  * Preparada para migrar a signals en el futuro.
436
559
  */
560
+ /**
561
+ * @deprecated Use JvsAutocompleteDirective en su lugar para un enfoque más declarativo y robusto.
562
+ */
437
563
  function changeSelectReformateado(config) {
438
- const { objThis, tipoReq, formControl, queryService, campoId, minLength = 3, dataExtra = {}, dataExtraVariable = [], anonimo = false, variableResultado = tipoReq, } = config;
439
- formControl.valueChanges.pipe(debounceTime(500), tap(() => {
564
+ const { objThis, tipoReq, formControl, queryService, campoId, minLength = 3, dataExtra = {}, dataExtraVariable = [], anonimo = false, variableResultado = tipoReq, destroyRef } = config;
565
+ const dRef = obtenerDestroyRef(objThis, destroyRef, 'changeSelectReformateado');
566
+ formControl.valueChanges.pipe(dRef ? takeUntilDestroyed(dRef) : tap(() => { }), debounceTime(500), tap(() => {
440
567
  objThis.filtrados[variableResultado + 'tmp'] = isObservable(objThis.filtrados[variableResultado])
441
568
  ? []
442
569
  : objThis.filtrados[variableResultado] || [];
@@ -456,10 +583,11 @@ function changeSelectReformateado(config) {
456
583
  objThis.isLoading = false;
457
584
  return of({ [tipoReq]: [] });
458
585
  }
459
- const extraVars = Object.fromEntries(dataExtraVariable.map(v => [v.campo, v.ctrlValue.value]));
586
+ const extraVars = Object.fromEntries(dataExtraVariable.map((v) => [v.campo, v.ctrlValue.value]));
460
587
  const query = queryService.getDataMethod('GET', tipoReq, { ...dataExtra, ...extraVars, txtBuscar: value }, anonimo);
461
588
  if (esPromise(query)) {
462
- return query.then((data) => ({ [tipoReq]: data[tipoReq] ?? [] }))
589
+ return query
590
+ .then((data) => ({ [tipoReq]: data[tipoReq] ?? [] }))
463
591
  .finally(() => { objThis.isLoading = false; });
464
592
  }
465
593
  return query.pipe(finalize(() => { objThis.isLoading = false; }));
@@ -467,57 +595,39 @@ function changeSelectReformateado(config) {
467
595
  objThis.filtrados[variableResultado] = data[tipoReq] ?? [];
468
596
  });
469
597
  }
470
- /**
471
- * Alias para compatibilidad.
472
- */
473
- function changeSelectDataApi(objThis, dataFiltro) {
474
- return changeSelectReformateado({
475
- objThis,
476
- tipoReq: dataFiltro.tipoReq,
477
- formControl: dataFiltro.formControl,
478
- queryService: dataFiltro.queryService,
479
- campoId: dataFiltro.campoId,
480
- minLength: dataFiltro.minLength,
481
- dataExtra: dataFiltro.dataExtra,
482
- dataExtraVariable: dataFiltro.dataExtraVariable,
483
- anonimo: dataFiltro.anonimo,
484
- variableResultado: dataFiltro.variableResultado,
485
- });
486
- }
487
- /**
488
- * Alias para compatibilidad.
489
- */
490
- function changeSelectApi(control, queryService, formControl, tipo, dataExtra = {}, dataExtraVariable = null, minLength = 1, anonimo = false) {
491
- return changeSelectReformateado({
492
- objThis: control,
493
- tipoReq: tipo,
494
- formControl,
495
- queryService,
496
- minLength,
497
- dataExtra,
498
- dataExtraVariable: dataExtraVariable ?? [],
499
- anonimo,
500
- });
501
- }
502
- /**
503
- * Comprueba si un valor es una Promesa.
504
- */
505
- function esPromise(valor) {
506
- return !!valor && typeof valor.then === 'function';
507
- }
508
598
 
509
599
  function seleccionarTextoInput(event) {
510
600
  event.target.select();
511
601
  }
512
602
 
603
+ /**
604
+ * Codifica un string a base64 (método legacy)
605
+ * @param val - String a codificar
606
+ * @returns String codificado en base64
607
+ * @deprecated Use encodeBase64String() en su lugar
608
+ */
513
609
  function b64Encode(val) {
514
610
  return Buffer.from(val, 'binary').toString('base64');
515
611
  }
612
+ /**
613
+ * Decodifica un string desde base64 (método legacy)
614
+ * @param val - String en base64 a decodificar
615
+ * @returns String decodificado
616
+ * @deprecated Use decodeBase64String() en su lugar
617
+ */
516
618
  function b64Decode(val) {
517
619
  return Buffer.from(val, 'base64').toString('binary');
518
620
  }
519
621
  /**
520
622
  * Codificar string a Base64 (UTF-8 seguro)
623
+ * @param str - String a codificar
624
+ * @returns String codificado en base64
625
+ *
626
+ * @example
627
+ * ```typescript
628
+ * const encoded = encodeBase64String('Hola Mundo ñ');
629
+ * // encoded: "SG9sYSBNdW5kbyDDsQ=="
630
+ * ```
521
631
  */
522
632
  function encodeBase64String(str) {
523
633
  const encoder = new TextEncoder();
@@ -528,6 +638,14 @@ function encodeBase64String(str) {
528
638
  }
529
639
  /**
530
640
  * Decodificar Base64 a string (UTF-8 seguro)
641
+ * @param b64 - String en base64 a decodificar
642
+ * @returns String decodificado
643
+ *
644
+ * @example
645
+ * ```typescript
646
+ * const decoded = decodeBase64String('SG9sYSBNdW5kbyDDsQ==');
647
+ * // decoded: "Hola Mundo ñ"
648
+ * ```
531
649
  */
532
650
  function decodeBase64String(b64) {
533
651
  const binary = atob(b64);
@@ -540,18 +658,43 @@ function decodeBase64String(b64) {
540
658
  }
541
659
  /**
542
660
  * Codificar un objeto a base64 (UTF-8 seguro)
661
+ * @param obj - Objeto a codificar
662
+ * @returns String codificado en base64
663
+ *
664
+ * @example
665
+ * ```typescript
666
+ * const obj = { nombre: 'Juan', edad: 25 };
667
+ * const encoded = encodeBase64Object(obj);
668
+ * ```
543
669
  */
544
670
  function encodeBase64Object(obj) {
545
671
  return encodeBase64String(JSON.stringify(obj));
546
672
  }
547
673
  /**
548
674
  * Decodificar un base64 y obtener el objeto original
675
+ * @param b64 - String en base64 a decodificar
676
+ * @returns Objeto decodificado
677
+ *
678
+ * @example
679
+ * ```typescript
680
+ * const obj = decodeBase64Object<{ nombre: string, edad: number }>(encoded);
681
+ * // obj: { nombre: 'Juan', edad: 25 }
682
+ * ```
549
683
  */
550
684
  function decodeBase64Object(b64) {
551
685
  return JSON.parse(decodeBase64String(b64));
552
686
  }
553
687
  /**
554
688
  * Codificar archivo a Base64 (retorna solo el contenido, sin "data:...")
689
+ * @param file - Archivo o Blob a codificar
690
+ * @returns Promise con el string en base64
691
+ *
692
+ * @example
693
+ * ```typescript
694
+ * const file = event.target.files[0];
695
+ * const base64 = await encodeBase64File(file);
696
+ * // base64: "iVBORw0KGgoAAAANSUhEUgAA..."
697
+ * ```
555
698
  */
556
699
  function encodeBase64File(file) {
557
700
  return new Promise((resolve, reject) => {
@@ -566,6 +709,16 @@ function encodeBase64File(file) {
566
709
  }
567
710
  /**
568
711
  * Decodificar Base64 a Blob (para reconstruir archivos en Angular)
712
+ * @param b64 - String en base64
713
+ * @param mimeType - Tipo MIME del archivo (opcional)
714
+ * @returns Blob con el contenido decodificado
715
+ *
716
+ * @example
717
+ * ```typescript
718
+ * const blob = decodeBase64ToBlob(base64String, 'image/png');
719
+ * const url = URL.createObjectURL(blob);
720
+ * // Usar url para mostrar imagen o descargar
721
+ * ```
569
722
  */
570
723
  function decodeBase64ToBlob(b64, mimeType = 'application/octet-stream') {
571
724
  const byteChars = atob(b64);
@@ -639,6 +792,147 @@ function formatearFechaCadena(fecha) {
639
792
  return new Date(year, month, day);
640
793
  }
641
794
 
795
+ /**
796
+ * Configuración global para las utilidades de logging
797
+ */
798
+ let isProductionMode = false;
799
+ /**
800
+ * Configura el modo de producción para las utilidades de logging
801
+ * @param production - true si está en modo producción, false para desarrollo
802
+ *
803
+ * @example
804
+ * ```typescript
805
+ * import { setProductionMode } from '@jvsoft/utils';
806
+ * import { environment } from './environments/environment';
807
+ *
808
+ * setProductionMode(environment.production);
809
+ * ```
810
+ */
811
+ function setProductionMode(production) {
812
+ isProductionMode = production;
813
+ }
814
+ /**
815
+ * Obtiene el estado actual del modo de producción
816
+ * @returns true si está en modo producción, false en desarrollo
817
+ */
818
+ function isProduction() {
819
+ return isProductionMode;
820
+ }
821
+ /**
822
+ * Muestra mensajes de log solo en modo desarrollo
823
+ * @param args - Argumentos a mostrar en consola
824
+ *
825
+ * @example
826
+ * ```typescript
827
+ * devLog('Usuario cargado:', usuario);
828
+ * devLog('Estado:', { activo: true, rol: 'admin' });
829
+ * ```
830
+ */
831
+ function devLog(...args) {
832
+ if (!isProductionMode) {
833
+ console.log(...args);
834
+ }
835
+ }
836
+ /**
837
+ * Muestra advertencias solo en modo desarrollo
838
+ * @param args - Argumentos a mostrar como advertencia
839
+ *
840
+ * @example
841
+ * ```typescript
842
+ * devWarn('Función deprecada, usar nuevaFuncion() en su lugar');
843
+ * ```
844
+ */
845
+ function devWarn(...args) {
846
+ if (!isProductionMode) {
847
+ console.warn(...args);
848
+ }
849
+ }
850
+ /**
851
+ * Muestra errores en consola (siempre, incluso en producción)
852
+ * @param args - Argumentos a mostrar como error
853
+ *
854
+ * @example
855
+ * ```typescript
856
+ * devError('Error al cargar datos:', error);
857
+ * ```
858
+ */
859
+ function devError(...args) {
860
+ console.error(...args);
861
+ }
862
+ /**
863
+ * Crea un grupo de logs solo en modo desarrollo
864
+ * @param label - Etiqueta del grupo
865
+ * @param collapsed - Si el grupo debe estar colapsado por defecto
866
+ *
867
+ * @example
868
+ * ```typescript
869
+ * devGroup('Datos del usuario');
870
+ * devLog('Nombre:', usuario.nombre);
871
+ * devLog('Email:', usuario.email);
872
+ * devGroupEnd();
873
+ * ```
874
+ */
875
+ function devGroup(label, collapsed = false) {
876
+ if (!isProductionMode) {
877
+ if (collapsed) {
878
+ console.groupCollapsed(label);
879
+ }
880
+ else {
881
+ console.group(label);
882
+ }
883
+ }
884
+ }
885
+ /**
886
+ * Cierra el grupo de logs actual
887
+ */
888
+ function devGroupEnd() {
889
+ if (!isProductionMode) {
890
+ console.groupEnd();
891
+ }
892
+ }
893
+ /**
894
+ * Muestra una tabla en consola solo en modo desarrollo
895
+ * @param data - Datos a mostrar en formato tabla
896
+ *
897
+ * @example
898
+ * ```typescript
899
+ * devTable([
900
+ * { nombre: 'Juan', edad: 25 },
901
+ * { nombre: 'María', edad: 30 }
902
+ * ]);
903
+ * ```
904
+ */
905
+ function devTable(data) {
906
+ if (!isProductionMode) {
907
+ console.table(data);
908
+ }
909
+ }
910
+ /**
911
+ * Inicia un temporizador solo en modo desarrollo
912
+ * @param label - Etiqueta del temporizador
913
+ *
914
+ * @example
915
+ * ```typescript
916
+ * devTime('carga-datos');
917
+ * // ... código a medir
918
+ * devTimeEnd('carga-datos'); // Muestra: carga-datos: 123.45ms
919
+ * ```
920
+ */
921
+ function devTime(label) {
922
+ if (!isProductionMode) {
923
+ console.time(label);
924
+ }
925
+ }
926
+ /**
927
+ * Finaliza un temporizador y muestra el tiempo transcurrido
928
+ * @param label - Etiqueta del temporizador
929
+ */
930
+ function devTimeEnd(label) {
931
+ if (!isProductionMode) {
932
+ console.timeEnd(label);
933
+ }
934
+ }
935
+
642
936
  function maskEmail(email) {
643
937
  const [user, domain] = email.split("@");
644
938
  if (user.length <= 2) {
@@ -1446,5 +1740,5 @@ function verificarRUC(ruc) {
1446
1740
  * Generated bundle index. Do not edit.
1447
1741
  */
1448
1742
 
1449
- export { b64Decode, b64Encode, buscarPorCampo, changeSelect, changeSelectApi, changeSelectData, changeSelectDataApi, changeSelectReformateado, convertirBytes, decodeBase64Object, decodeBase64String, decodeBase64ToBlob, deepClone, deepMerge, delLocalStorage, desencriptar, downLoadFileStream, eliminarColumnaPorIndex, eliminarDuplicados, eliminarElementos, encodeBase64File, encodeBase64Object, encodeBase64String, encriptar, esNumero, establecerQuitarRequired, extraerDominio, formatearFecha, formatearFechaCadena, formatearFechaFormato, generateRandomString, getBrowserName, getCambiarPwd, getDataArchivoFromPath, getFormValidationErrors, getLocalStorage, getUniqueValues, getUniqueValuesByProperty, groupBy, inicializarVariablesSessionStorage, isEmail, jwtToken, jwtTokenData, jwtTokenExpiracion, jwtTokenExpiracionFaltante, localStorageKeys, markAsTouchedWithoutEmitEvent, maskEmail, mensajeAlerta, mensajeConfirmacion, mensajeTimer, mensajeToast, mensajesDeError, mensajesErrorFormControl, mostrarValorEnBusqueda, nestGroupsBy, numberToWords, objectPropertiesBoolean, objectPropertiesToType, obtenerHostDesdeUrl, obtenerMimeType, obtenerUltimoOrden, ordenarArray, ordenarPorPropiedad, ordenarPorPropiedades, roundToDecimal, sanitizarNombreArchivo, seleccionarTextoInput, setCambiarPwd, setControlDesdeLista, setJwtTokenData, setLocalStorage, sumarObjetos, sumarPropiedades, toFormData, transformarFechasPorNombreDeCampo, verificarRUC };
1450
- //# sourceMappingURL=jvsoft-utils-src-functions.mjs.map
1743
+ export { b64Decode, b64Encode, buscarPorCampo, changeSelect, changeSelectApi, changeSelectData, changeSelectDataApi, changeSelectReformateado, convertirBytes, decodeBase64Object, decodeBase64String, decodeBase64ToBlob, deepClone, deepMerge, delLocalStorage, desencriptar, devError, devGroup, devGroupEnd, devLog, devTable, devTime, devTimeEnd, devWarn, downLoadFileStream, eliminarColumnaPorIndex, eliminarDuplicados, eliminarElementos, encodeBase64File, encodeBase64Object, encodeBase64String, encriptar, esNumero, esPromise, establecerQuitarRequired, extraerDominio, filtrarDatosLocal, formatearFecha, formatearFechaCadena, formatearFechaFormato, generateRandomString, getBrowserName, getCambiarPwd, getDataArchivoFromPath, getFormValidationErrors, getLocalStorage, getUniqueValues, getUniqueValuesByProperty, getValueByPath, groupBy, inicializarVariablesSessionStorage, isEmail, isProduction, jwtToken, jwtTokenData, jwtTokenExpiracion, jwtTokenExpiracionFaltante, localStorageKeys, markAsTouchedWithoutEmitEvent, maskEmail, mensajeAlerta, mensajeConfirmacion, mensajeTimer, mensajeToast, mensajesDeError, mensajesErrorFormControl, mostrarValorEnBusqueda, nestGroupsBy, numberToWords, objectPropertiesBoolean, objectPropertiesToType, obtenerHostDesdeUrl, obtenerMimeType, obtenerUltimoOrden, ordenarArray, ordenarPorPropiedad, ordenarPorPropiedades, roundToDecimal, sanitizarNombreArchivo, seleccionarTextoInput, setCambiarPwd, setControlDesdeLista, setJwtTokenData, setLocalStorage, setProductionMode, sumarObjetos, sumarPropiedades, toFormData, transformarFechasPorNombreDeCampo, verificarRUC };
1744
+ //# sourceMappingURL=jvsoft-utils-functions.mjs.map