@jvsoft/utils 0.0.13-alpha.5 → 1.0.0-alpha.10

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} +543 -190
  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 +1142 -196
  23. package/fesm2022/jvsoft-utils.mjs.map +1 -1
  24. package/functions/base64.d.ts +87 -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 +27 -17
  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 -2
  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
263
  /**
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
- /**
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
+
288
+ /**
289
+ * Intenta obtener el DestroyRef desde el llamador (objThis/control)
290
+ * o inyectándolo si estamos en un contexto de inyección.
291
+ */
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
+ }
414
361
  /**
415
- * Vincula un FormControl a datos locales para autocompletado.
362
+ * @deprecated Use JvsAutocompleteDirective en su lugar para un enfoque más declarativo y robusto.
416
363
  */
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) : []));
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,54 +595,138 @@ function changeSelectReformateado(config) {
467
595
  objThis.filtrados[variableResultado] = data[tipoReq] ?? [];
468
596
  });
469
597
  }
598
+
599
+ function seleccionarTextoInput(event) {
600
+ event.target.select();
601
+ }
602
+
470
603
  /**
471
- * Alias para compatibilidad.
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
472
608
  */
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
- });
609
+ function b64Encode(val) {
610
+ return Buffer.from(val, 'binary').toString('base64');
486
611
  }
487
612
  /**
488
- * Alias para compatibilidad.
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
489
617
  */
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
- });
618
+ function b64Decode(val) {
619
+ return Buffer.from(val, 'base64').toString('binary');
501
620
  }
502
621
  /**
503
- * Comprueba si un valor es una Promesa.
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
+ * ```
504
631
  */
505
- function esPromise(valor) {
506
- return !!valor && typeof valor.then === 'function';
632
+ function encodeBase64String(str) {
633
+ const encoder = new TextEncoder();
634
+ const bytes = encoder.encode(str);
635
+ let binary = '';
636
+ bytes.forEach(b => binary += String.fromCharCode(b));
637
+ return btoa(binary);
507
638
  }
508
-
509
- function seleccionarTextoInput(event) {
510
- event.target.select();
639
+ /**
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
+ * ```
649
+ */
650
+ function decodeBase64String(b64) {
651
+ const binary = atob(b64);
652
+ const bytes = new Uint8Array(binary.length);
653
+ for (let i = 0; i < binary.length; i++) {
654
+ bytes[i] = binary.charCodeAt(i);
655
+ }
656
+ const decoder = new TextDecoder();
657
+ return decoder.decode(bytes);
511
658
  }
512
-
513
- function b64Encode(val) {
514
- return Buffer.from(val, 'binary').toString('base64');
659
+ /**
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
+ * ```
669
+ */
670
+ function encodeBase64Object(obj) {
671
+ return encodeBase64String(JSON.stringify(obj));
515
672
  }
516
- function b64Decode(val) {
517
- return Buffer.from(val, 'base64').toString('binary');
673
+ /**
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
+ * ```
683
+ */
684
+ function decodeBase64Object(b64) {
685
+ return JSON.parse(decodeBase64String(b64));
686
+ }
687
+ /**
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
+ * ```
698
+ */
699
+ function encodeBase64File(file) {
700
+ return new Promise((resolve, reject) => {
701
+ const reader = new FileReader();
702
+ reader.onload = () => {
703
+ const result = reader.result;
704
+ resolve(result.split(',')[1]); // quita el prefijo "data:...;base64,"
705
+ };
706
+ reader.onerror = reject;
707
+ reader.readAsDataURL(file);
708
+ });
709
+ }
710
+ /**
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
+ * ```
722
+ */
723
+ function decodeBase64ToBlob(b64, mimeType = 'application/octet-stream') {
724
+ const byteChars = atob(b64);
725
+ const byteNumbers = new Array(byteChars.length);
726
+ for (let i = 0; i < byteChars.length; i++) {
727
+ byteNumbers[i] = byteChars.charCodeAt(i);
728
+ }
729
+ return new Blob([new Uint8Array(byteNumbers)], { type: mimeType });
518
730
  }
519
731
 
520
732
  function getBrowserName() {
@@ -580,6 +792,147 @@ function formatearFechaCadena(fecha) {
580
792
  return new Date(year, month, day);
581
793
  }
582
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
+
583
936
  function maskEmail(email) {
584
937
  const [user, domain] = email.split("@");
585
938
  if (user.length <= 2) {
@@ -1387,5 +1740,5 @@ function verificarRUC(ruc) {
1387
1740
  * Generated bundle index. Do not edit.
1388
1741
  */
1389
1742
 
1390
- export { b64Decode, b64Encode, buscarPorCampo, changeSelect, changeSelectApi, changeSelectData, changeSelectDataApi, changeSelectReformateado, convertirBytes, deepClone, deepMerge, delLocalStorage, desencriptar, downLoadFileStream, eliminarColumnaPorIndex, eliminarDuplicados, eliminarElementos, 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 };
1391
- //# 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