ngx-dsxlibrary 2.21.69 → 2.21.71

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.
@@ -32,9 +32,6 @@ import { Button, ButtonModule } from 'primeng/button';
32
32
  import moment from 'moment-timezone';
33
33
  import { ToastrService } from 'ngx-toastr';
34
34
  import Swal from 'sweetalert2';
35
- import { AutoComplete, AutoCompleteModule } from 'primeng/autocomplete';
36
- import * as i6 from 'primeng/floatlabel';
37
- import { FloatLabel, FloatLabelModule } from 'primeng/floatlabel';
38
35
  import { FileUpload, FileUploadModule } from 'primeng/fileupload';
39
36
  import { LottieComponent } from 'ngx-lottie';
40
37
  import { createTimeline } from 'animejs';
@@ -44,9 +41,12 @@ import * as i4 from 'primeng/menubar';
44
41
  import { MenubarModule } from 'primeng/menubar';
45
42
  import { JwtHelperService } from '@auth0/angular-jwt';
46
43
  import { CookieService } from 'ngx-cookie-service';
44
+ import * as i6 from 'primeng/floatlabel';
45
+ import { FloatLabelModule, FloatLabel } from 'primeng/floatlabel';
47
46
  import * as i7 from 'primeng/password';
48
47
  import { PasswordModule } from 'primeng/password';
49
48
  import { AccordionModule } from 'primeng/accordion';
49
+ import { AutoCompleteModule, AutoComplete } from 'primeng/autocomplete';
50
50
  import { AutoFocusModule } from 'primeng/autofocus';
51
51
  import { AvatarGroupModule } from 'primeng/avatargroup';
52
52
  import { BadgeModule } from 'primeng/badge';
@@ -2184,6 +2184,181 @@ function provideEnvironment(environment) {
2184
2184
  };
2185
2185
  }
2186
2186
 
2187
+ /**
2188
+ * action-message.model.ts
2189
+ *
2190
+ * Centraliza los tipos de acción y la configuración de mensajes para acciones de botones y operaciones comunes en la UI.
2191
+ *
2192
+ * - Define los tipos de acción estándar usados en dsx-button y otros componentes.
2193
+ * - Permite mantener uniformidad y evitar errores por typos en los tipos de acción.
2194
+ * - Provee la función getActionMessageConfig para obtener los textos, títulos e íconos de confirmación según la acción y el estado.
2195
+ * - Si se usa una acción válida pero sin mensaje configurado, en modo desarrollo muestra un warning para facilitar el mantenimiento.
2196
+ *
2197
+ * Uso recomendado:
2198
+ * - Importa ACTION_TYPES y ActionType donde se requiera validar o tipar acciones de botones.
2199
+ * - Usa getActionMessageConfig para obtener la configuración de confirmación antes de ejecutar acciones destructivas o de restauración.
2200
+ *
2201
+ * Ejemplo:
2202
+ * import { ACTION_TYPES, ActionType, getActionMessageConfig } from '.../action-message.model';
2203
+ * const config = getActionMessageConfig('softDelete', true);
2204
+ * // config: { title, message, icon }
2205
+ *
2206
+ * Si agregas un nuevo tipo de acción, recuerda añadir su mensaje en getActionMessageConfig.
2207
+ */
2208
+ /**
2209
+ * Tipos de acción disponibles para el componente `dsx-button`.
2210
+ *
2211
+ * Usa `ACTION_TYPES` en el consumidor para evitar typos.
2212
+ *
2213
+ * **Persistencia de datos (formulario)**
2214
+ * - `create` → Navega a la ruta de nuevo registro (azul)
2215
+ * - `saveOrUpdate` → Muestra "Guardar" si `id` es 0, "Actualizar" si `id` > 0 (amarillo)
2216
+ *
2217
+ * **Eliminar**
2218
+ * - `delete` → Eliminar registro permanentemente (rojo)
2219
+ * - `softDelete` → Borrar registro con borrado lógico (amarillo)
2220
+ *
2221
+ * **Formulario**
2222
+ * - `validate` → Validar formulario sin guardar (gris)
2223
+ * - `restore` → Restaurar datos originales (gris)
2224
+ *
2225
+ * **Navegación / Utilidades**
2226
+ * - `home` → Ir al inicio (gris)
2227
+ * - `refresh` → Recargar datos desde el servidor (morado)
2228
+ *
2229
+ * **Consulta / Descarga**
2230
+ * - `details` → Ver el detalle de un registro (azul índigo)
2231
+ * - `view` → Previsualizar o abrir detalle (azul cielo)
2232
+ * - `export` → Exportar información a Excel (verde Excel)
2233
+ * - `download` → Descargar archivo o recurso (ámbar cálido)
2234
+ * - `import` → Importar datos desde archivo (azul acero)
2235
+ */
2236
+ const ACTION_TYPES = {
2237
+ hardDelete: 'hardDelete',
2238
+ softDelete: 'softDelete',
2239
+ return: 'return',
2240
+ home: 'home',
2241
+ validate: 'validate',
2242
+ restore: 'restore',
2243
+ edit: 'edit',
2244
+ create: 'create',
2245
+ save: 'save',
2246
+ update: 'update',
2247
+ refresh: 'refresh',
2248
+ details: 'details',
2249
+ view: 'view',
2250
+ export: 'export',
2251
+ download: 'download',
2252
+ import: 'import',
2253
+ };
2254
+ function getActionMessageConfig(action, secondArg, thirdArg) {
2255
+ const isActive = action === 'softDelete' || action === 'hardDelete'
2256
+ ? typeof secondArg === 'boolean'
2257
+ ? secondArg
2258
+ : true
2259
+ : true;
2260
+ const dataPreview = action === 'softDelete' || action === 'hardDelete'
2261
+ ? thirdArg
2262
+ : typeof secondArg === 'string'
2263
+ ? secondArg
2264
+ : undefined;
2265
+ // Valores por defecto para todas las propiedades
2266
+ const defaults = {
2267
+ title: '',
2268
+ message: '',
2269
+ icon: undefined,
2270
+ confirmButtonText: undefined,
2271
+ cancelButtonText: undefined,
2272
+ showConfirmButton: undefined,
2273
+ showCancelButton: undefined,
2274
+ };
2275
+ if (action === 'softDelete') {
2276
+ if (isActive) {
2277
+ return {
2278
+ ...defaults,
2279
+ title: '¿Borrar registro?',
2280
+ message: 'El registro dejará de estar disponible en las listas principales.' +
2281
+ (dataPreview ? `<br>${dataPreview}` : ''),
2282
+ icono: 'icon/trash-bin.png',
2283
+ confirmButtonText: 'Eliminar',
2284
+ cancelButtonText: 'Cancelar',
2285
+ showConfirmButton: true,
2286
+ showCancelButton: true,
2287
+ };
2288
+ }
2289
+ else {
2290
+ return {
2291
+ ...defaults,
2292
+ title: '¿Restaurar registro?',
2293
+ message: 'El registro volverá a estar <strong>activo</strong> y visible en todas las listas principales del sistema.' +
2294
+ (dataPreview ? `<br>${dataPreview}` : ''),
2295
+ icono: 'icon/documentation.png',
2296
+ confirmButtonText: 'Sí, restaurar',
2297
+ cancelButtonText: 'Dejar archivado',
2298
+ showConfirmButton: true,
2299
+ showCancelButton: true,
2300
+ };
2301
+ }
2302
+ }
2303
+ if (action === 'hardDelete') {
2304
+ return {
2305
+ ...defaults,
2306
+ title: '¿Eliminar permanentemente?',
2307
+ message: 'Esta acción <strong>no se puede deshacer</strong>. Se perderán todos los datos asociados a este registro de forma inmediata.' +
2308
+ (dataPreview ? `<br>${dataPreview}` : ''),
2309
+ icono: 'icon/delete.png',
2310
+ confirmButtonText: 'Eliminar permanentemente',
2311
+ cancelButtonText: 'Cancelar',
2312
+ showConfirmButton: true,
2313
+ showCancelButton: true,
2314
+ };
2315
+ }
2316
+ if (action === 'save') {
2317
+ return {
2318
+ ...defaults,
2319
+ title: '¿Guardar registro?',
2320
+ message: 'El nuevo registro se guardará en el sistema.' +
2321
+ (dataPreview ? `<br>${dataPreview}` : ''),
2322
+ icono: 'icon2/save_2029637.png',
2323
+ confirmButtonText: 'Guardar',
2324
+ cancelButtonText: 'Cancelar',
2325
+ showConfirmButton: true,
2326
+ showCancelButton: true,
2327
+ };
2328
+ }
2329
+ if (action === 'update') {
2330
+ return {
2331
+ ...defaults,
2332
+ title: '¿Actualizar registro?',
2333
+ message: 'Los cambios realizados se actualizarán en el sistema.' +
2334
+ (dataPreview ? `<br>${dataPreview}` : ''),
2335
+ icono: 'icon2/save_2029637.png',
2336
+ confirmButtonText: 'Actualizar',
2337
+ cancelButtonText: 'Cancelar',
2338
+ showConfirmButton: true,
2339
+ showCancelButton: true,
2340
+ };
2341
+ }
2342
+ if (action === 'restore') {
2343
+ return {
2344
+ ...defaults,
2345
+ title: '¿Revertir cambios?',
2346
+ message: 'Se perderán todos los cambios que hayas realizado en el formulario y se volverán a cargar los datos originales.',
2347
+ icono: 'icon/data-recovery.png',
2348
+ confirmButtonText: 'Restaurar',
2349
+ cancelButtonText: 'Cancelar',
2350
+ showConfirmButton: true,
2351
+ showCancelButton: true,
2352
+ };
2353
+ }
2354
+ if (typeof ngDevMode !== 'undefined' && ngDevMode) {
2355
+ console.warn(`[getActionMessageConfig] Acción "${action}" existe pero no tiene mensaje configurado. ` +
2356
+ 'Agrega la configuración correspondiente para evitar este warning.');
2357
+ }
2358
+ // Retorna los valores por defecto si la acción no es válida
2359
+ return { ...defaults };
2360
+ }
2361
+
2187
2362
  const INITIAL_PARAMETERS = new InjectionToken('InitialParameters');
2188
2363
 
2189
2364
  /**
@@ -4103,222 +4278,47 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
4103
4278
  }] });
4104
4279
 
4105
4280
  /**
4106
- * action-message.model.ts
4107
- *
4108
- * Centraliza los tipos de acción y la configuración de mensajes para acciones de botones y operaciones comunes en la UI.
4109
- *
4110
- * - Define los tipos de acción estándar usados en dsx-button y otros componentes.
4111
- * - Permite mantener uniformidad y evitar errores por typos en los tipos de acción.
4112
- * - Provee la función getActionMessageConfig para obtener los textos, títulos e íconos de confirmación según la acción y el estado.
4113
- * - Si se usa una acción válida pero sin mensaje configurado, en modo desarrollo muestra un warning para facilitar el mantenimiento.
4114
- *
4115
- * Uso recomendado:
4116
- * - Importa ACTION_TYPES y ActionType donde se requiera validar o tipar acciones de botones.
4117
- * - Usa getActionMessageConfig para obtener la configuración de confirmación antes de ejecutar acciones destructivas o de restauración.
4118
- *
4119
- * Ejemplo:
4120
- * import { ACTION_TYPES, ActionType, getActionMessageConfig } from '.../action-message.model';
4121
- * const config = getActionMessageConfig('softDelete', true);
4122
- * // config: { title, message, icon }
4123
- *
4124
- * Si agregas un nuevo tipo de acción, recuerda añadir su mensaje en getActionMessageConfig.
4125
- */
4126
- /**
4127
- * Tipos de acción disponibles para el componente `dsx-button`.
4128
- *
4129
- * Usa `ACTION_TYPES` en el consumidor para evitar typos.
4130
- *
4131
- * **Persistencia de datos (formulario)**
4132
- * - `create` → Navega a la ruta de nuevo registro (azul)
4133
- * - `saveOrUpdate` → Muestra "Guardar" si `id` es 0, "Actualizar" si `id` > 0 (amarillo)
4134
- *
4135
- * **Eliminar**
4136
- * - `delete` → Eliminar registro permanentemente (rojo)
4137
- * - `softDelete` → Borrar registro con borrado lógico (amarillo)
4138
- *
4139
- * **Formulario**
4140
- * - `validate` → Validar formulario sin guardar (gris)
4141
- * - `restore` → Restaurar datos originales (gris)
4281
+ * Paleta corporativa de colores para `dsx-button`.
4142
4282
  *
4143
- * **Navegación / Utilidades**
4144
- * - `home` Ir al inicio (gris)
4145
- * - `refresh` → Recargar datos desde el servidor (morado)
4283
+ * Cada token tiene un significado semántico fijo. Úsala como referencia
4284
+ * para asignar `colorToken` al añadir nuevos tipos de botón.
4146
4285
  *
4147
- * **Consulta / Descarga**
4148
- * - `details` → Ver el detalle de un registro (azul índigo)
4149
- * - `view` Previsualizar o abrir detalle (azul cielo)
4150
- * - `export` Exportar información a Excel (verde Excel)
4151
- * - `download` Descargar archivo o recurso (ámbar cálido)
4152
- * - `import` Importar datos desde archivo (azul acero)
4153
- */
4154
- const ACTION_TYPES = {
4155
- hardDelete: 'hardDelete',
4156
- softDelete: 'softDelete',
4157
- return: 'return',
4158
- home: 'home',
4159
- validate: 'validate',
4160
- restore: 'restore',
4161
- edit: 'edit',
4162
- create: 'create',
4163
- save: 'save',
4164
- update: 'update',
4165
- refresh: 'refresh',
4166
- details: 'details',
4167
- view: 'view',
4168
- export: 'export',
4169
- download: 'download',
4170
- import: 'import',
4171
- };
4172
- function getActionMessageConfig(action, secondArg, thirdArg) {
4173
- const isActive = action === 'softDelete' || action === 'hardDelete'
4174
- ? typeof secondArg === 'boolean'
4175
- ? secondArg
4176
- : true
4177
- : true;
4178
- const dataPreview = action === 'softDelete' || action === 'hardDelete'
4179
- ? thirdArg
4180
- : typeof secondArg === 'string'
4181
- ? secondArg
4182
- : undefined;
4183
- // Valores por defecto para todas las propiedades
4184
- const defaults = {
4185
- title: '',
4186
- message: '',
4187
- icon: undefined,
4188
- confirmButtonText: undefined,
4189
- cancelButtonText: undefined,
4190
- showConfirmButton: undefined,
4191
- showCancelButton: undefined,
4192
- };
4193
- if (action === 'softDelete') {
4194
- if (isActive) {
4195
- return {
4196
- ...defaults,
4197
- title: '¿Borrar registro?',
4198
- message: 'El registro dejará de estar disponible en las listas principales.' +
4199
- (dataPreview ? `<br>${dataPreview}` : ''),
4200
- icono: 'icon/trash-bin.png',
4201
- confirmButtonText: 'Eliminar',
4202
- cancelButtonText: 'Cancelar',
4203
- showConfirmButton: true,
4204
- showCancelButton: true,
4205
- };
4206
- }
4207
- else {
4208
- return {
4209
- ...defaults,
4210
- title: '¿Restaurar registro?',
4211
- message: 'El registro volverá a estar <strong>activo</strong> y visible en todas las listas principales del sistema.' +
4212
- (dataPreview ? `<br>${dataPreview}` : ''),
4213
- icono: 'icon/documentation.png',
4214
- confirmButtonText: 'Sí, restaurar',
4215
- cancelButtonText: 'Dejar archivado',
4216
- showConfirmButton: true,
4217
- showCancelButton: true,
4218
- };
4219
- }
4220
- }
4221
- if (action === 'hardDelete') {
4222
- return {
4223
- ...defaults,
4224
- title: '¿Eliminar permanentemente?',
4225
- message: 'Esta acción <strong>no se puede deshacer</strong>. Se perderán todos los datos asociados a este registro de forma inmediata.' +
4226
- (dataPreview ? `<br>${dataPreview}` : ''),
4227
- icono: 'icon/delete.png',
4228
- confirmButtonText: 'Eliminar permanentemente',
4229
- cancelButtonText: 'Cancelar',
4230
- showConfirmButton: true,
4231
- showCancelButton: true,
4232
- };
4233
- }
4234
- if (action === 'save') {
4235
- return {
4236
- ...defaults,
4237
- title: '¿Guardar registro?',
4238
- message: 'El nuevo registro se guardará en el sistema.' +
4239
- (dataPreview ? `<br>${dataPreview}` : ''),
4240
- icono: 'icon2/save_2029637.png',
4241
- confirmButtonText: 'Guardar',
4242
- cancelButtonText: 'Cancelar',
4243
- showConfirmButton: true,
4244
- showCancelButton: true,
4245
- };
4246
- }
4247
- if (action === 'update') {
4248
- return {
4249
- ...defaults,
4250
- title: '¿Actualizar registro?',
4251
- message: 'Los cambios realizados se actualizarán en el sistema.' +
4252
- (dataPreview ? `<br>${dataPreview}` : ''),
4253
- icono: 'icon2/save_2029637.png',
4254
- confirmButtonText: 'Actualizar',
4255
- cancelButtonText: 'Cancelar',
4256
- showConfirmButton: true,
4257
- showCancelButton: true,
4258
- };
4259
- }
4260
- if (action === 'restore') {
4261
- return {
4262
- ...defaults,
4263
- title: '¿Revertir cambios?',
4264
- message: 'Se perderán todos los cambios que hayas realizado en el formulario y se volverán a cargar los datos originales.',
4265
- icono: 'icon/data-recovery.png',
4266
- confirmButtonText: 'Restaurar',
4267
- cancelButtonText: 'Cancelar',
4268
- showConfirmButton: true,
4269
- showCancelButton: true,
4270
- };
4271
- }
4272
- if (typeof ngDevMode !== 'undefined' && ngDevMode) {
4273
- console.warn(`[getActionMessageConfig] Acción "${action}" existe pero no tiene mensaje configurado. ` +
4274
- 'Agrega la configuración correspondiente para evitar este warning.');
4275
- }
4276
- // Retorna los valores por defecto si la acción no es válida
4277
- return { ...defaults };
4278
- }
4279
-
4280
- /**
4281
- * Paleta corporativa de colores para `dsx-button`.
4282
- *
4283
- * Cada token tiene un significado semántico fijo. Úsala como referencia
4284
- * para asignar `colorToken` al añadir nuevos tipos de botón.
4285
- *
4286
- * | Token | Color | Uso semántico |
4287
- * |-----------|----------------|---------------------------------------------------|
4288
- * | danger | Coral rojo | Acción destructiva e irreversible (eliminar) |
4289
- * | warning | Naranja cálido | Acción con riesgo moderado / borrado lógico |
4290
- * | caution | Ámbar dorado | Requiere verificación del usuario (actualizar) |
4291
- * | success | Verde menta | Guardar nuevo registro / agregar |
4292
- * | primary | Azul claro | Editar / navegar con contexto de registro |
4293
- * | info | Cian suave | Recargar datos / refrescar vista |
4294
- * | neutral | Gris azulado | Acción secundaria / navegación sin contexto |
4295
- * | restore | Violeta suave | Deshacer / restaurar estado anterior |
4296
- * | view | Teal profundo | Ver / previsualizar detalle |
4297
- * | excel | Verde Excel | Exportar información a hoja de cálculo |
4298
- * | download | Naranja quemado| Descargar archivo o recurso |
4299
- * | import | Azul acero | Importar información desde archivo |
4300
- * | details | Azul índigo | Abrir detalle de registro |
4286
+ * | Token | Color | Uso semántico |
4287
+ * |-----------|----------------|---------------------------------------------------|
4288
+ * | danger | Coral rojo | Acción destructiva e irreversible (eliminar) |
4289
+ * | warning | Naranja cálido | Acción con riesgo moderado / borrado lógico |
4290
+ * | caution | Ámbar dorado | Requiere verificación del usuario (actualizar) |
4291
+ * | success | Verde menta | Guardar nuevo registro / agregar |
4292
+ * | primary | Azul claro | Editar / navegar con contexto de registro |
4293
+ * | info | Cian suave | Recargar datos / refrescar vista |
4294
+ * | neutral | Gris azulado | Acción secundaria / navegación sin contexto |
4295
+ * | restore | Violeta suave | Deshacer / restaurar estado anterior |
4296
+ * | view | Teal profundo | Ver / previsualizar detalle |
4297
+ * | excel | Verde Excel | Exportar información a hoja de cálculo |
4298
+ * | download | Naranja quemado| Descargar archivo o recurso |
4299
+ * | import | Azul acero | Importar información desde archivo |
4300
+ * | details | Azul índigo | Abrir detalle de registro |
4301
4301
  */
4302
4302
  const DSX_PALETTE = {
4303
4303
  danger: '#EF5350',
4304
4304
  warning: '#FF7043',
4305
4305
  caution: '#FFB300',
4306
- success: '#0ba055', // Verde para Guardar
4307
- primary: '#0066cc', // Azul para Actualizar
4306
+ success: '#0ba055',
4307
+ primary: '#0066cc',
4308
4308
  info: '#26C6DA',
4309
4309
  neutral: '#90A4AE',
4310
- restore: '#B0BEC5', // Gris suave, menos resaltado
4311
- return: '#11438d', // Azul violeta para "Regresar registro archivado"
4310
+ restore: '#B0BEC5',
4311
+ return: '#11438d',
4312
4312
  view: '#00838F',
4313
4313
  excel: '#217346',
4314
4314
  download: '#E65100',
4315
4315
  import: '#546E7A',
4316
4316
  details: '#3949AB',
4317
- magenta: '#D81B60', // Magenta vibrante
4318
- cyan: '#00BCD4', // Cian brillante
4319
- olive: '#808000', // Verde oliva
4320
- brown: '#8D6E63', // Marrón suave
4321
- slate: '#607D8B', // Azul pizarra
4317
+ magenta: '#D81B60',
4318
+ cyan: '#00BCD4',
4319
+ olive: '#808000',
4320
+ brown: '#8D6E63',
4321
+ slate: '#607D8B',
4322
4322
  };
4323
4323
  /** Tipos que necesitan un `[id]` mayor a 0 para poder mostrarse. */
4324
4324
  const TYPES_REQUIRING_ID = [
@@ -4331,23 +4331,56 @@ class DsxButtonComponent {
4331
4331
  renderCount = 0;
4332
4332
  _paramService = inject((ParameterValuesService));
4333
4333
  _router = inject(Router);
4334
- // Agrega un input para el tamaño
4334
+ _isDev = isDevMode();
4335
+ lastValidationState = null;
4335
4336
  size = input(undefined, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
4336
4337
  _environment = inject(ENVIRONMENT, {
4337
4338
  optional: true,
4338
4339
  });
4339
4340
  constructor() {
4340
- if (isDevMode()) {
4341
+ // Solo activar efectos de debug en desarrollo
4342
+ if (this._isDev) {
4343
+ // Efecto principal que captura todo el estado
4341
4344
  effect(() => {
4342
4345
  if (!this.debug())
4343
4346
  return;
4344
4347
  this.renderCount++;
4345
- console.log(`[dsx-button:${this.type()}] render #${this.renderCount}`, {
4346
- id: this.id(),
4347
- type: this.type(),
4348
- disabled: this.disabled(),
4349
- routerLink: this.routerLink(),
4350
- });
4348
+ this.logFullState();
4349
+ });
4350
+ // Efecto específico para validaciones
4351
+ effect(() => {
4352
+ if (!this.debug())
4353
+ return;
4354
+ const visible = this.visible();
4355
+ const validations = {
4356
+ global: this.passesGlobalVisibilityPolicy(),
4357
+ parameter: this.passesParameterValidation(),
4358
+ id: this.passesIdValidation(),
4359
+ visible,
4360
+ };
4361
+ const stateKey = JSON.stringify(validations);
4362
+ if (stateKey !== this.lastValidationState) {
4363
+ this.lastValidationState = stateKey;
4364
+ this.logValidationIssues(validations);
4365
+ }
4366
+ });
4367
+ // Efecto para detectar problemas con IDs
4368
+ effect(() => {
4369
+ if (!this.debug())
4370
+ return;
4371
+ const type = this.type();
4372
+ if (TYPES_REQUIRING_ID.includes(type)) {
4373
+ const id = this.getNormalizedId();
4374
+ const routeId = this.getIdFromNavigationTarget(this.effectiveRouterLink());
4375
+ if (!id && !routeId) {
4376
+ console.warn(`%c⚠️ [dsx-button:${type}] ID no disponible para acción que lo requiere`, 'color:#ed6c02;font-weight:bold', {
4377
+ type,
4378
+ explicitId: this.id(),
4379
+ routeId,
4380
+ requireIdInput: this.requireIdInput(),
4381
+ });
4382
+ }
4383
+ }
4351
4384
  });
4352
4385
  }
4353
4386
  }
@@ -4357,17 +4390,7 @@ class DsxButtonComponent {
4357
4390
  type = input(ACTION_TYPES.hardDelete, ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
4358
4391
  disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
4359
4392
  routerLink = input(undefined, ...(ngDevMode ? [{ debugName: "routerLink" }] : /* istanbul ignore next */ []));
4360
- /**
4361
- * Ruta de navegación simplificada. La `/` inicial se agrega automáticamente.
4362
- * Cada segmento solo admite: letras, números, `-`, `_`, `.` y `:` (params de ruta).
4363
- * Ejemplos válidos: `'home'`, `'container/nuevo'`, `'item/123'`
4364
- * Ejemplos inválidos: `'item/*'`, `'item?foo=bar'`, `'item/../other'`
4365
- * Para rutas dinámicas complejas usa `[routerLink]` directamente.
4366
- */
4367
4393
  routerPath = input(undefined, ...(ngDevMode ? [{ debugName: "routerPath" }] : /* istanbul ignore next */ []));
4368
- /** Ancho del botón. Usa `rem` para que escale con la preferencia de fuente del usuario.
4369
- * Ejemplo: `'8rem'`, `'12rem'`. Valor por defecto: `'9rem'`.
4370
- */
4371
4394
  width = input('9rem', ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
4372
4395
  iconOnly = input(false, ...(ngDevMode ? [{ debugName: "iconOnly" }] : /* istanbul ignore next */ []));
4373
4396
  iconOnlyWidth = input('2.5rem', ...(ngDevMode ? [{ debugName: "iconOnlyWidth" }] : /* istanbul ignore next */ []));
@@ -4383,24 +4406,12 @@ class DsxButtonComponent {
4383
4406
  tooltipOverride = input(undefined, ...(ngDevMode ? [{ debugName: "tooltipOverride" }] : /* istanbul ignore next */ []));
4384
4407
  iconOverride = input(undefined, ...(ngDevMode ? [{ debugName: "iconOverride" }] : /* istanbul ignore next */ []));
4385
4408
  primeIconOverride = input(undefined, ...(ngDevMode ? [{ debugName: "primeIconOverride" }] : /* istanbul ignore next */ []));
4386
- /** Permite personalizar el color usando únicamente tokens de la paleta DSX. */
4387
4409
  colorOverride = input(undefined, ...(ngDevMode ? [{ debugName: "colorOverride" }] : /* istanbul ignore next */ []));
4388
- /** Permite forzar estilo outlined o sólido por instancia. */
4389
4410
  outlinedOverride = input(undefined, ...(ngDevMode ? [{ debugName: "outlinedOverride" }] : /* istanbul ignore next */ []));
4390
- /**
4391
- * Override por instancia para el botón `validate`.
4392
- * Tiene prioridad sobre `environment.showValidateButton`.
4393
- */
4394
4411
  showValidateButtonOverride = input(undefined, ...(ngDevMode ? [{ debugName: "showValidateButtonOverride" }] : /* istanbul ignore next */ []));
4395
- /**
4396
- * Override por instancia para el botón `restore`.
4397
- * Tiene prioridad sobre `environment.showRestoreButton`.
4398
- */
4399
4412
  showRestoreButtonOverride = input(undefined, ...(ngDevMode ? [{ debugName: "showRestoreButtonOverride" }] : /* istanbul ignore next */ []));
4400
4413
  action = output();
4401
- /** Valida y normaliza `routerPath`: agrega `/` inicial y rechaza caracteres inválidos. */
4402
4414
  resolvedRouterLink = computed(() => {
4403
- this.log('computed resolvedRouterLink');
4404
4415
  const raw = this.routerPath();
4405
4416
  if (!raw)
4406
4417
  return undefined;
@@ -4409,23 +4420,22 @@ class DsxButtonComponent {
4409
4420
  const rawSegments = normalizedRaw.split('/');
4410
4421
  const hasEmptySegments = normalizedRaw.length > 0 && rawSegments.some((s) => s.length === 0);
4411
4422
  if (hasEmptySegments) {
4412
- console.warn(`[dsx-button] routerPath contiene segmentos vacíos ("//" o "/" al final). ` +
4413
- `Ruta recibida: "${raw}"`);
4423
+ if (this._isDev && this.debug()) {
4424
+ console.warn(`[dsx-button] routerPath contiene segmentos vacíos: "${raw}"`);
4425
+ }
4414
4426
  return undefined;
4415
4427
  }
4416
4428
  const segments = rawSegments.filter(Boolean);
4417
4429
  const invalid = segments.filter((s) => !VALID_SEGMENT.test(s));
4418
4430
  if (invalid.length > 0) {
4419
- console.warn(`[dsx-button] routerPath contiene segmentos inválidos: "${invalid.join('", "')}". ` +
4420
- `Solo se permiten letras, números, guion (-), guion bajo (_), punto (.) y dos puntos (:). ` +
4421
- `Ruta recibida: "${raw}"`);
4431
+ if (this._isDev && this.debug()) {
4432
+ console.warn(`[dsx-button] routerPath contiene segmentos inválidos: "${invalid.join('", "')}"`);
4433
+ }
4422
4434
  return undefined;
4423
4435
  }
4424
4436
  return `/${segments.join('/')}`;
4425
4437
  }, ...(ngDevMode ? [{ debugName: "resolvedRouterLink" }] : /* istanbul ignore next */ []));
4426
- /** Prioriza routerPath (validado) sobre routerLink (libre). */
4427
4438
  effectiveRouterLink = computed(() => {
4428
- this.log('computed effectiveRouterLink');
4429
4439
  const resolvedPath = this.resolvedRouterLink();
4430
4440
  if (resolvedPath) {
4431
4441
  return resolvedPath;
@@ -4438,7 +4448,6 @@ class DsxButtonComponent {
4438
4448
  if (!trimmed) {
4439
4449
  return link;
4440
4450
  }
4441
- // URLs externas no se normalizan ni validan como rutas internas.
4442
4451
  if (/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(trimmed)) {
4443
4452
  return link;
4444
4453
  }
@@ -4448,20 +4457,20 @@ class DsxButtonComponent {
4448
4457
  const hasEmptySegments = pathForValidation.length > 0 &&
4449
4458
  rawSegments.some((segment) => segment.length === 0);
4450
4459
  if (hasEmptySegments) {
4451
- console.warn(`[dsx-button] routerLink contiene segmentos vacíos ("//" o "/" al final). ` +
4452
- `Ruta recibida: "${link}"`);
4460
+ if (this._isDev && this.debug()) {
4461
+ console.warn(`[dsx-button] routerLink contiene segmentos vacíos: "${link}"`);
4462
+ }
4453
4463
  return link;
4454
4464
  }
4455
4465
  const invalid = rawSegments
4456
4466
  .filter(Boolean)
4457
4467
  .filter((segment) => !VALID_SEGMENT.test(segment));
4458
4468
  if (invalid.length > 0) {
4459
- console.warn(`[dsx-button] routerLink contiene segmentos inválidos: "${invalid.join('", "')}". ` +
4460
- `Solo se permiten letras, números, guion (-), guion bajo (_), punto (.) y dos puntos (:). ` +
4461
- `Ruta recibida: "${link}"`);
4469
+ if (this._isDev && this.debug()) {
4470
+ console.warn(`[dsx-button] routerLink contiene segmentos inválidos: "${invalid.join('", "')}"`);
4471
+ }
4462
4472
  return link;
4463
4473
  }
4464
- // Si llega una ruta simple sin prefijo, se normaliza a absoluta para evitar navegación relativa inesperada.
4465
4474
  if (link.startsWith('/') ||
4466
4475
  link.startsWith('./') ||
4467
4476
  link.startsWith('../')) {
@@ -4472,23 +4481,6 @@ class DsxButtonComponent {
4472
4481
  }
4473
4482
  return `/${trimmed.split('/').filter(Boolean).join('/')}`;
4474
4483
  }, ...(ngDevMode ? [{ debugName: "effectiveRouterLink" }] : /* istanbul ignore next */ []));
4475
- // get buttonStyle(): Record<string, string> {
4476
- // this.log('getter buttonStyle');
4477
- // const color = DSX_PALETTE[this.config.colorToken];
4478
- // const width = this.iconOnly() ? this.iconOnlyWidth() : this.width();
4479
- // if (this.isCompactIconButton) {
4480
- // return {
4481
- // width,
4482
- // background: 'transparent',
4483
- // borderColor: 'transparent',
4484
- // color,
4485
- // };
4486
- // }
4487
- // if (this.config.outlined) {
4488
- // return { width, background: 'transparent', borderColor: color, color };
4489
- // }
4490
- // return { width, background: color, borderColor: color, color: '#ffffff' };
4491
- // }
4492
4484
  buttonStyleComputed = computed(() => {
4493
4485
  const color = DSX_PALETTE[this.config.colorToken];
4494
4486
  const width = this.iconOnly() ? this.iconOnlyWidth() : this.width();
@@ -4518,17 +4510,13 @@ class DsxButtonComponent {
4518
4510
  get buttonLabel() {
4519
4511
  return this.iconOnly() ? undefined : this.label;
4520
4512
  }
4521
- // Y modifica el getter buttonSize
4522
4513
  get buttonSize() {
4523
- // Si el usuario especificó un tamaño, úsalo
4524
4514
  if (this.size()) {
4525
4515
  return this.size();
4526
4516
  }
4527
- // Si es compacto, siempre small
4528
4517
  if (this.isCompactIconButton) {
4529
4518
  return 'small';
4530
4519
  }
4531
- // Por defecto, small para todos los botones
4532
4520
  return 'small';
4533
4521
  }
4534
4522
  get buttonStyleClass() {
@@ -4541,7 +4529,6 @@ class DsxButtonComponent {
4541
4529
  const cfg = ACTION_CONFIG[this.type()];
4542
4530
  const colorOverride = this.colorOverride();
4543
4531
  const outlinedOverride = this.outlinedOverride();
4544
- // Ya no se gestiona softDelete/return como modo, cada acción es independiente
4545
4532
  return {
4546
4533
  ...cfg,
4547
4534
  colorToken: colorOverride ?? cfg.colorToken,
@@ -4551,10 +4538,6 @@ class DsxButtonComponent {
4551
4538
  get label() {
4552
4539
  return this.labelOverride() ?? this.config.label;
4553
4540
  }
4554
- // get tooltip(): string {
4555
- // this.log('getter tooltip');
4556
- // return this.tooltipOverride() ?? this.config.tooltip;
4557
- // }
4558
4541
  tooltip = computed(() => {
4559
4542
  return this.tooltipOverride() ?? this.config.tooltip;
4560
4543
  }, ...(ngDevMode ? [{ debugName: "tooltip" }] : /* istanbul ignore next */ []));
@@ -4570,18 +4553,6 @@ class DsxButtonComponent {
4570
4553
  }
4571
4554
  return 'primeIcon' in this.config ? this.config.primeIcon : undefined;
4572
4555
  }
4573
- // showButton(): boolean {
4574
- // this.time('showButton');
4575
- // const result =
4576
- // this.passesGlobalVisibilityPolicy() &&
4577
- // this.passesParameterValidation() &&
4578
- // this.passesIdValidation();
4579
- // this.log('showButton()', {
4580
- // result,
4581
- // });
4582
- // this.timeEnd('showButton');
4583
- // return result;
4584
- // }
4585
4556
  visible = computed(() => {
4586
4557
  return (this.passesGlobalVisibilityPolicy() &&
4587
4558
  this.passesParameterValidation() &&
@@ -4606,45 +4577,44 @@ class DsxButtonComponent {
4606
4577
  return true;
4607
4578
  }
4608
4579
  async onButtonClick() {
4609
- this.log('click', {
4610
- id: this.id(),
4611
- target: this.effectiveRouterLink(),
4612
- });
4580
+ if (this._isDev && this.debug()) {
4581
+ console.group(`%c🖱️ [dsx-button:${this.type()}] Click`, 'color:#1976d2;font-weight:bold;font-size:13px');
4582
+ console.log('ID:', this.id());
4583
+ console.log('Target:', this.effectiveRouterLink());
4584
+ console.log('Disabled:', this.disabled());
4585
+ console.groupEnd();
4586
+ }
4613
4587
  this.action.emit();
4614
4588
  const target = this.effectiveRouterLink();
4615
4589
  if (target == null) {
4616
- if (this.type() === 'home') {
4617
- console.warn('[dsx-button] El botón tipo "home" no tiene routerLink ni routerPath configurado.');
4590
+ if (this.type() === 'home' && this._isDev && this.debug()) {
4591
+ console.warn(`[dsx-button:${this.type()}] No tiene routerLink ni routerPath configurado.`);
4618
4592
  }
4619
4593
  return;
4620
4594
  }
4621
4595
  try {
4622
4596
  const start = performance.now();
4623
4597
  const navigated = await this.navigateToTarget(target);
4624
- this.log('navigation', {
4625
- target,
4626
- navigated,
4627
- durationMs: Math.round(performance.now() - start),
4628
- });
4598
+ const duration = Math.round(performance.now() - start);
4599
+ if (this._isDev && this.debug()) {
4600
+ const emoji = navigated ? '✅' : '⚠️';
4601
+ const color = navigated ? '#2e7d32' : '#ed6c02';
4602
+ console.log(`%c${emoji} Navegación ${navigated ? 'exitosa' : 'rechazada'} (${duration}ms)`, `color:${color};font-weight:bold`, target);
4603
+ }
4629
4604
  if (!navigated) {
4630
- // El router rechazó la navegación, lo más común durante hot-reload
4631
- // es que un guard de autenticación aún no tiene el estado listo.
4632
- // Se reintenta una vez tras un tick para darle tiempo al guard.
4633
4605
  const retried = await new Promise((resolve) => setTimeout(async () => resolve(await this.navigateToTarget(target)), 0));
4634
- if (!retried) {
4635
- console.warn('[dsx-button] Navegación rechazada (posiblemente por un route guard). ' +
4636
- 'Si ocurre solo tras hot-reload, es normal que el guard rechace ' +
4637
- 'la primera navegación mientras los servicios de autenticación ' +
4638
- 'no han terminado de restaurar su estado.', target);
4606
+ if (!retried && this._isDev && this.debug()) {
4607
+ console.warn(`[dsx-button:${this.type()}] Navegación rechazada por route guard.`, target);
4639
4608
  }
4640
4609
  }
4641
4610
  }
4642
4611
  catch (error) {
4643
- console.warn('[dsx-button] Error al navegar desde el botón.', target, error);
4612
+ if (this._isDev && this.debug()) {
4613
+ console.error(`[dsx-button:${this.type()}] Error al navegar:`, target, error);
4614
+ }
4644
4615
  }
4645
4616
  }
4646
4617
  passesIdValidation() {
4647
- this.log('passesIdValidation');
4648
4618
  const explicitId = this.getNormalizedId();
4649
4619
  const routeId = this.getIdFromNavigationTarget(this.effectiveRouterLink());
4650
4620
  const id = explicitId ?? routeId;
@@ -4653,7 +4623,6 @@ class DsxButtonComponent {
4653
4623
  this.type() === 'softDelete' ||
4654
4624
  this.type() === 'restore' ||
4655
4625
  this.type() === 'return';
4656
- // Permite exigir que venga [id] cuando el caso de uso lo requiera.
4657
4626
  if (this.requireIdInput() && typeof id !== 'number') {
4658
4627
  return false;
4659
4628
  }
@@ -4713,16 +4682,6 @@ class DsxButtonComponent {
4713
4682
  }
4714
4683
  return undefined;
4715
4684
  }
4716
- hasExplicitIdInput() {
4717
- const value = this.id();
4718
- if (value === null || value === undefined) {
4719
- return false;
4720
- }
4721
- if (typeof value === 'string') {
4722
- return value.trim() !== '';
4723
- }
4724
- return true;
4725
- }
4726
4685
  navigateToTarget(target) {
4727
4686
  if (Array.isArray(target)) {
4728
4687
  return this._router.navigate(target);
@@ -4734,41 +4693,154 @@ class DsxButtonComponent {
4734
4693
  return this._router.navigateByUrl(target);
4735
4694
  }
4736
4695
  passesParameterValidation() {
4737
- this.log('passesParameterValidation');
4738
4696
  const parameterName = this.parameterName();
4739
- // Si no se define un parámetro de seguridad, el botón se muestra sin restricción.
4740
4697
  if (!parameterName) {
4741
4698
  return true;
4742
4699
  }
4743
- // Mantiene una UX estable durante refresh de parámetros: evita parpadeo
4744
- // y no evalúa permisos hasta que ParameterValuesService termine de cargar.
4745
4700
  if (!this._paramService.loaded) {
4746
4701
  return false;
4747
4702
  }
4748
- // Conserva el comportamiento original del servicio:
4749
- // - retorna false si no cumple el valor esperado
4750
- // - alerta si el parámetro no existe (ayuda a detectar typos)
4751
4703
  return this._paramService.isParameterValue(parameterName, this.parameterExpectedValue(), this.parameterIndex());
4752
4704
  }
4753
- log(message, data) {
4754
- if (!this.debug())
4705
+ // ============ MÉTODOS DE DEBUG ============
4706
+ logFullState() {
4707
+ if (!this._isDev || !this.debug())
4755
4708
  return;
4756
- console.log(`%c[dsx-button:${this.type()}] ${message}`, 'color:#1976d2;font-weight:bold', data ?? '');
4709
+ const id = this.getNormalizedId();
4710
+ const routeId = this.getIdFromNavigationTarget(this.effectiveRouterLink());
4711
+ const paramName = this.parameterName();
4712
+ const paramLoaded = this._paramService.loaded;
4713
+ const paramValue = paramName && paramLoaded
4714
+ ? this._paramService.getValue(paramName, this.parameterIndex())
4715
+ : undefined;
4716
+ console.group(`%c📊 [dsx-button:${this.type()}] Render #${this.renderCount}`, 'color:#1976d2;font-weight:bold;font-size:14px');
4717
+ // 1. Estado general
4718
+ console.log('🎯 Estado:', {
4719
+ visible: this.visible(),
4720
+ disabled: this.disabled(),
4721
+ type: this.type(),
4722
+ size: this.size(),
4723
+ iconOnly: this.iconOnly(),
4724
+ rounded: this.rounded(),
4725
+ isCompact: this.isCompactIconButton,
4726
+ });
4727
+ // 2. Configuración
4728
+ console.log('⚙️ Configuración:', {
4729
+ label: this.label,
4730
+ icon: this.icon,
4731
+ primeIcon: this.primeIcon,
4732
+ colorToken: this.config.colorToken,
4733
+ outlined: this.config.outlined,
4734
+ tooltip: this.tooltip(),
4735
+ });
4736
+ // 3. Router
4737
+ console.log('🔗 Router:', {
4738
+ routerLink: this.routerLink(),
4739
+ routerPath: this.routerPath(),
4740
+ resolvedRouterLink: this.resolvedRouterLink(),
4741
+ effectiveRouterLink: this.effectiveRouterLink(),
4742
+ });
4743
+ // 4. IDs
4744
+ console.log('🆔 IDs:', {
4745
+ inputId: this.id(),
4746
+ normalizedId: id,
4747
+ routeId: routeId,
4748
+ requireIdInput: this.requireIdInput(),
4749
+ requiresExistingRecord: this.requiresIdGreaterThanZero() ||
4750
+ ['hardDelete', 'softDelete', 'restore', 'return'].includes(this.type()),
4751
+ });
4752
+ // 5. Parámetros
4753
+ if (paramName) {
4754
+ console.log('🔐 Parámetros:', {
4755
+ name: paramName,
4756
+ expected: this.parameterExpectedValue(),
4757
+ index: this.parameterIndex(),
4758
+ loaded: paramLoaded,
4759
+ actual: paramValue,
4760
+ matches: paramLoaded
4761
+ ? paramValue === this.parameterExpectedValue()
4762
+ : 'No cargado',
4763
+ });
4764
+ }
4765
+ // 6. Overrides
4766
+ const overrides = {
4767
+ label: this.labelOverride(),
4768
+ tooltip: this.tooltipOverride(),
4769
+ icon: this.iconOverride(),
4770
+ primeIcon: this.primeIconOverride(),
4771
+ color: this.colorOverride(),
4772
+ outlined: this.outlinedOverride(),
4773
+ showValidate: this.showValidateButtonOverride(),
4774
+ showRestore: this.showRestoreButtonOverride(),
4775
+ };
4776
+ const activeOverrides = Object.entries(overrides)
4777
+ .filter(([_, value]) => value !== undefined)
4778
+ .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {});
4779
+ if (Object.keys(activeOverrides).length > 0) {
4780
+ console.log('🔄 Overrides activos:', activeOverrides);
4781
+ }
4782
+ // 7. Validaciones
4783
+ console.log('✅ Validaciones:', {
4784
+ globalVisibility: this.passesGlobalVisibilityPolicy(),
4785
+ parameterValidation: this.passesParameterValidation(),
4786
+ idValidation: this.passesIdValidation(),
4787
+ visible: this.visible(),
4788
+ });
4789
+ // 8. Estilos
4790
+ console.log('🎨 Estilos:', {
4791
+ width: this.iconOnly() ? this.iconOnlyWidth() : this.width(),
4792
+ style: this.buttonStyleComputed(),
4793
+ styleClass: this.buttonStyleClass,
4794
+ size: this.buttonSize,
4795
+ });
4796
+ console.groupEnd();
4757
4797
  }
4758
- warn(message, data) {
4759
- if (!this.debug())
4798
+ logValidationIssues(validations) {
4799
+ if (!this._isDev || !this.debug())
4760
4800
  return;
4761
- console.warn(`[dsx-button:${this.type()}] ${message}`, data ?? '');
4801
+ if (!validations.visible) {
4802
+ console.group(`%c❌ [dsx-button:${this.type()}] NO VISIBLE`, 'color:#d32f2f;font-weight:bold;font-size:13px');
4803
+ console.log('Validaciones fallidas:', {
4804
+ globalVisibility: validations.global,
4805
+ parameterValidation: validations.parameter,
4806
+ idValidation: validations.id,
4807
+ });
4808
+ if (!validations.id) {
4809
+ const id = this.getNormalizedId();
4810
+ const routeId = this.getIdFromNavigationTarget(this.effectiveRouterLink());
4811
+ console.warn('⚠️ Problema con ID:', {
4812
+ explicitId: this.id(),
4813
+ normalizedId: id,
4814
+ routeId: routeId,
4815
+ requireIdInput: this.requireIdInput(),
4816
+ requiresExistingRecord: this.requiresIdGreaterThanZero() ||
4817
+ ['hardDelete', 'softDelete', 'restore', 'return'].includes(this.type()),
4818
+ });
4819
+ }
4820
+ if (!validations.parameter && this.parameterName()) {
4821
+ console.warn('⚠️ Problema con parámetro:', {
4822
+ name: this.parameterName(),
4823
+ expected: this.parameterExpectedValue(),
4824
+ index: this.parameterIndex(),
4825
+ loaded: this._paramService.loaded,
4826
+ actualValue: this._paramService.loaded
4827
+ ? this._paramService.getValue(this.parameterName(), this.parameterIndex())
4828
+ : 'No cargado',
4829
+ });
4830
+ }
4831
+ console.groupEnd();
4832
+ }
4762
4833
  }
4763
- time(label) {
4764
- if (!this.debug())
4834
+ // Métodos de log existentes mejorados
4835
+ log(message, data) {
4836
+ if (!this._isDev || !this.debug())
4765
4837
  return;
4766
- console.time(`[dsx-button:${this.type()}] ${label}`);
4838
+ console.log(`%c[dsx-button:${this.type()}] ${message}`, 'color:#1976d2;font-weight:bold', data ?? '');
4767
4839
  }
4768
- timeEnd(label) {
4769
- if (!this.debug())
4840
+ warn(message, data) {
4841
+ if (!this._isDev || !this.debug())
4770
4842
  return;
4771
- console.timeEnd(`[dsx-button:${this.type()}] ${label}`);
4843
+ console.warn(`[dsx-button:${this.type()}] ${message}`, data ?? '');
4772
4844
  }
4773
4845
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4774
4846
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: DsxButtonComponent, isStandalone: true, selector: "dsx-button", inputs: { debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, routerPath: { classPropertyName: "routerPath", publicName: "routerPath", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, iconOnly: { classPropertyName: "iconOnly", publicName: "iconOnly", isSignal: true, isRequired: false, transformFunction: null }, iconOnlyWidth: { classPropertyName: "iconOnlyWidth", publicName: "iconOnlyWidth", isSignal: true, isRequired: false, transformFunction: null }, raised: { classPropertyName: "raised", publicName: "raised", isSignal: true, isRequired: false, transformFunction: null }, rounded: { classPropertyName: "rounded", publicName: "rounded", isSignal: true, isRequired: false, transformFunction: null }, parameterName: { classPropertyName: "parameterName", publicName: "parameterName", isSignal: true, isRequired: false, transformFunction: null }, parameterExpectedValue: { classPropertyName: "parameterExpectedValue", publicName: "parameterExpectedValue", isSignal: true, isRequired: false, transformFunction: null }, parameterIndex: { classPropertyName: "parameterIndex", publicName: "parameterIndex", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, requireIdInput: { classPropertyName: "requireIdInput", publicName: "requireIdInput", isSignal: true, isRequired: false, transformFunction: null }, requiresIdGreaterThanZero: { classPropertyName: "requiresIdGreaterThanZero", publicName: "requiresIdGreaterThanZero", isSignal: true, isRequired: false, transformFunction: null }, labelOverride: { classPropertyName: "labelOverride", publicName: "labelOverride", isSignal: true, isRequired: false, transformFunction: null }, tooltipOverride: { classPropertyName: "tooltipOverride", publicName: "tooltipOverride", isSignal: true, isRequired: false, transformFunction: null }, iconOverride: { classPropertyName: "iconOverride", publicName: "iconOverride", isSignal: true, isRequired: false, transformFunction: null }, primeIconOverride: { classPropertyName: "primeIconOverride", publicName: "primeIconOverride", isSignal: true, isRequired: false, transformFunction: null }, colorOverride: { classPropertyName: "colorOverride", publicName: "colorOverride", isSignal: true, isRequired: false, transformFunction: null }, outlinedOverride: { classPropertyName: "outlinedOverride", publicName: "outlinedOverride", isSignal: true, isRequired: false, transformFunction: null }, showValidateButtonOverride: { classPropertyName: "showValidateButtonOverride", publicName: "showValidateButtonOverride", isSignal: true, isRequired: false, transformFunction: null }, showRestoreButtonOverride: { classPropertyName: "showRestoreButtonOverride", publicName: "showRestoreButtonOverride", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { action: "action" }, host: { properties: { "class.dsx-button-compact-host": "this.compactHostClass" } }, ngImport: i0, template: "<!--Bot\u00F3n con las configuraciones din\u00E1micas para CRUD -->\r\n@if (visible()) {\r\n<p-button\r\n [label]=\"buttonLabel\"\r\n [style]=\"buttonStyleComputed()\"\r\n [styleClass]=\"buttonStyleClass\"\r\n [pTooltip]=\"tooltip()\"\r\n [tooltipDisabled]=\"disabled()\"\r\n tooltipPosition=\"top\"\r\n [rounded]=\"rounded()\"\r\n [text]=\"isCompactIconButton\"\r\n [size]=\"buttonSize\"\r\n severity=\"secondary\"\r\n [disabled]=\"disabled()\"\r\n [icon]=\"iconOnly() ? primeIcon : undefined\"\r\n (click)=\"onButtonClick()\"\r\n>\r\n @if (!iconOnly()) {\r\n <span class=\"material-symbols-outlined dsx-button-icon-small\"\r\n >{{ materialIcon }}</span\r\n >\r\n }\r\n</p-button>\r\n}\r\n", styles: [".dsx-button-icon-small{font-size:1.25rem;vertical-align:middle}:host{display:inline-flex;margin-bottom:.25rem}:host.dsx-button-compact-host{margin-bottom:0}:host ::ng-deep .p-button.dsx-button-compact{min-height:2rem;padding-block:.2rem}:host ::ng-deep .p-button .material-symbols-outlined.p-button-icon{line-height:1;vertical-align:middle}:host ::ng-deep .p-button.dsx-button-compact .p-button-icon{margin:0;font-size:1.2rem;line-height:1}:host ::ng-deep .p-button.dsx-button-compact .material-symbols-outlined.p-button-icon{font-variation-settings:\"FILL\" 1,\"wght\" 500,\"GRAD\" 0,\"opsz\" 24}:host ::ng-deep .p-button:disabled,:host ::ng-deep .p-button.p-disabled{cursor:not-allowed!important;opacity:.45!important;filter:grayscale(.2) saturate(.75)}:host ::ng-deep .p-button:disabled *,:host ::ng-deep .p-button.p-disabled *{cursor:not-allowed!important}.p-button.dsx-button-outlined-neutral,.p-button.dsx-button-outlined-info{background:#f5f7fa!important;border:1.5px solid #0066cc!important;color:#06c!important}.p-button.dsx-button-outlined-neutral:disabled,.p-button.dsx-button-outlined-info:disabled{background:#f5f7fa!important;border:1.5px solid #b0bec5!important;color:#b0bec5!important}\n"], dependencies: [{ kind: "component", type: Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i1$3.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }] });
@@ -4873,7 +4945,7 @@ const ACTION_CONFIG = {
4873
4945
  view: {
4874
4946
  label: 'Visualizar',
4875
4947
  icon: 'visibility',
4876
- primeIcon: 'pi pi-eye',
4948
+ primeIcon: 'fa-solid fa-eye',
4877
4949
  colorToken: 'view',
4878
4950
  tooltip: 'Vista previa',
4879
4951
  },
@@ -4900,167 +4972,11 @@ const ACTION_CONFIG = {
4900
4972
  },
4901
4973
  };
4902
4974
 
4903
- class DsxAutocomplete {
4904
- // Inputs usando Señales
4905
- datasource = input.required(...(ngDevMode ? [{ debugName: "datasource" }] : /* istanbul ignore next */ []));
4906
- optionLabel = input.required(...(ngDevMode ? [{ debugName: "optionLabel" }] : /* istanbul ignore next */ []));
4907
- label = input('Seleccionar', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
4908
- delay = input(100, ...(ngDevMode ? [{ debugName: "delay" }] : /* istanbul ignore next */ []));
4909
- idKey = input.required(...(ngDevMode ? [{ debugName: "idKey" }] : /* istanbul ignore next */ []));
4910
- dataKey = input('', ...(ngDevMode ? [{ debugName: "dataKey" }] : /* istanbul ignore next */ []));
4911
- permitirCrear = input(false, ...(ngDevMode ? [{ debugName: "permitirCrear" }] : /* istanbul ignore next */ []));
4912
- debug = input(false, ...(ngDevMode ? [{ debugName: "debug" }] : /* istanbul ignore next */ []));
4913
- factoryNuevoRegistro = input(...(ngDevMode ? [undefined, { debugName: "factoryNuevoRegistro" }] : /* istanbul ignore next */ []));
4914
- // Estado Interno
4915
- dataFiltrada = signal([], ...(ngDevMode ? [{ debugName: "dataFiltrada" }] : /* istanbul ignore next */ []));
4916
- value = [];
4917
- onChange = () => { };
4918
- onTouched = () => { };
4919
- disabled = false;
4920
- lastLogTime = 0;
4921
- timeoutLimpieza = null;
4922
- logDebug(message, data) {
4923
- if (!this.debug())
4924
- return;
4925
- const now = Date.now();
4926
- if (now - this.lastLogTime < 50)
4927
- return;
4928
- this.lastLogTime = now;
4929
- console.log(`[DsxAutocomplete] ${message}`, data || '');
4930
- }
4931
- searchRequisitos(event) {
4932
- const inicio = performance.now();
4933
- const label = this.optionLabel();
4934
- // FLUJO A: Dropdown (sin texto)
4935
- if (!event.query || event.query.trim() === '') {
4936
- const todoElUniverso = this.datasource();
4937
- this.dataFiltrada.set(todoElUniverso);
4938
- this.logDebug('Modo dropdown activo', {
4939
- registros: todoElUniverso.length,
4940
- });
4941
- return;
4942
- }
4943
- // FLUJO B: Búsqueda
4944
- const palabrasBuscadas = event.query
4945
- .toLowerCase()
4946
- .trim()
4947
- .split(/\s+/)
4948
- .filter((palabra) => palabra.length > 0);
4949
- const filtrados = this.datasource()
4950
- .filter((item) => {
4951
- const textoRegistro = String(item[label] || '').toLowerCase();
4952
- return palabrasBuscadas.every((palabra) => textoRegistro.includes(palabra));
4953
- })
4954
- .slice(0, 10);
4955
- this.dataFiltrada.set(filtrados);
4956
- this.logDebug(`Búsqueda "${event.query}" (${(performance.now() - inicio).toFixed(2)}ms)`, { encontrados: filtrados.length });
4957
- }
4958
- evaluarYAgregar(event) {
4959
- const valorInput = event.target.value?.trim();
4960
- if (!valorInput) {
4961
- this.dataFiltrada.set([]);
4962
- return;
4963
- }
4964
- const sugerencias = this.dataFiltrada();
4965
- const actuales = this.value || [];
4966
- const campoId = this.idKey();
4967
- if (sugerencias.length > 0) {
4968
- const primeraMatch = sugerencias[0];
4969
- const yaExiste = actuales.some((item) => item[campoId] === primeraMatch[campoId]);
4970
- if (!yaExiste) {
4971
- this.actualizarFormulario([...actuales, primeraMatch]);
4972
- this.logDebug('✅ Elemento agregado');
4973
- }
4974
- }
4975
- else if (this.permitirCrear()) {
4976
- let nuevo;
4977
- if (this.factoryNuevoRegistro()) {
4978
- nuevo = this.factoryNuevoRegistro()(valorInput);
4979
- }
4980
- else {
4981
- nuevo = {
4982
- [campoId]: 0,
4983
- [this.optionLabel()]: valorInput.toUpperCase(),
4984
- activo: true,
4985
- };
4986
- }
4987
- this.actualizarFormulario([...actuales, nuevo]);
4988
- this.logDebug('🆕 Nuevo elemento creado', valorInput);
4989
- }
4990
- event.target.value = '';
4991
- this.dataFiltrada.set([]);
4992
- }
4993
- onHide() {
4994
- this.logDebug('Dropdown ocultado - Limpiando estado');
4995
- // Limpiar timeout anterior si existe
4996
- if (this.timeoutLimpieza) {
4997
- clearTimeout(this.timeoutLimpieza);
4998
- this.timeoutLimpieza = null;
4999
- }
5000
- // Limpiar después de un pequeño delay
5001
- this.timeoutLimpieza = setTimeout(() => {
5002
- this.dataFiltrada.set([]);
5003
- this.timeoutLimpieza = null;
5004
- }, 100);
5005
- }
5006
- onShow() {
5007
- this.logDebug('Dropdown mostrado');
5008
- // Limpiar timeout pendiente
5009
- if (this.timeoutLimpieza) {
5010
- clearTimeout(this.timeoutLimpieza);
5011
- this.timeoutLimpieza = null;
5012
- }
5013
- }
5014
- actualizarFormulario(nuevaLista) {
5015
- this.value = nuevaLista;
5016
- this.onChange(this.value);
5017
- this.logDebug('Formulario actualizado', {
5018
- total: nuevaLista.length,
5019
- });
5020
- }
5021
- // --- ControlValueAccessor ---
5022
- writeValue(value) {
5023
- this.value = value || [];
5024
- this.dataFiltrada.set([]);
5025
- if (this.timeoutLimpieza) {
5026
- clearTimeout(this.timeoutLimpieza);
5027
- this.timeoutLimpieza = null;
5028
- }
5029
- }
5030
- registerOnChange(fn) {
5031
- this.onChange = fn;
5032
- }
5033
- registerOnTouched(fn) {
5034
- this.onTouched = fn;
5035
- }
5036
- setDisabledState(isDisabled) {
5037
- this.disabled = isDisabled;
5038
- }
5039
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxAutocomplete, deps: [], target: i0.ɵɵFactoryTarget.Component });
5040
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.17", type: DsxAutocomplete, isStandalone: true, selector: "dsx-autocomplete", inputs: { datasource: { classPropertyName: "datasource", publicName: "datasource", isSignal: true, isRequired: true, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, delay: { classPropertyName: "delay", publicName: "delay", isSignal: true, isRequired: false, transformFunction: null }, idKey: { classPropertyName: "idKey", publicName: "idKey", isSignal: true, isRequired: true, transformFunction: null }, dataKey: { classPropertyName: "dataKey", publicName: "dataKey", isSignal: true, isRequired: false, transformFunction: null }, permitirCrear: { classPropertyName: "permitirCrear", publicName: "permitirCrear", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, factoryNuevoRegistro: { classPropertyName: "factoryNuevoRegistro", publicName: "factoryNuevoRegistro", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
5041
- {
5042
- provide: NG_VALUE_ACCESSOR,
5043
- useExisting: forwardRef(() => DsxAutocomplete),
5044
- multi: true,
5045
- },
5046
- ], ngImport: i0, template: "<p-floatlabel variant=\"on\">\r\n <p-autoComplete\r\n fluid\r\n multiple\r\n [dropdown]=\"true\"\r\n [suggestions]=\"dataFiltrada()\"\r\n (completeMethod)=\"searchRequisitos($event)\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"onChange(value)\"\r\n (onBlur)=\"onTouched()\"\r\n (onShow)=\"onShow()\"\r\n (onHide)=\"onHide()\"\r\n [disabled]=\"disabled\"\r\n [optionLabel]=\"optionLabel()\"\r\n [dataKey]=\"dataKey() || idKey()\"\r\n (keyup.enter)=\"evaluarYAgregar($event)\"\r\n [delay]=\"delay()\"\r\n placeholder=\"Ingresa un texto....\"\r\n >\r\n </p-autoComplete>\r\n <label>{{ label() }}</label>\r\n</p-floatlabel>\r\n", styles: [""], dependencies: [{ kind: "component", type: AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: FloatLabel, selector: "p-floatlabel, p-floatLabel, p-float-label", inputs: ["variant"] }] });
5047
- }
5048
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxAutocomplete, decorators: [{
5049
- type: Component,
5050
- args: [{ selector: 'dsx-autocomplete', imports: [AutoComplete, FormsModule, FloatLabel], providers: [
5051
- {
5052
- provide: NG_VALUE_ACCESSOR,
5053
- useExisting: forwardRef(() => DsxAutocomplete),
5054
- multi: true,
5055
- },
5056
- ], template: "<p-floatlabel variant=\"on\">\r\n <p-autoComplete\r\n fluid\r\n multiple\r\n [dropdown]=\"true\"\r\n [suggestions]=\"dataFiltrada()\"\r\n (completeMethod)=\"searchRequisitos($event)\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"onChange(value)\"\r\n (onBlur)=\"onTouched()\"\r\n (onShow)=\"onShow()\"\r\n (onHide)=\"onHide()\"\r\n [disabled]=\"disabled\"\r\n [optionLabel]=\"optionLabel()\"\r\n [dataKey]=\"dataKey() || idKey()\"\r\n (keyup.enter)=\"evaluarYAgregar($event)\"\r\n [delay]=\"delay()\"\r\n placeholder=\"Ingresa un texto....\"\r\n >\r\n </p-autoComplete>\r\n <label>{{ label() }}</label>\r\n</p-floatlabel>\r\n" }]
5057
- }], propDecorators: { datasource: [{ type: i0.Input, args: [{ isSignal: true, alias: "datasource", required: true }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], delay: [{ type: i0.Input, args: [{ isSignal: true, alias: "delay", required: false }] }], idKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "idKey", required: true }] }], dataKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataKey", required: false }] }], permitirCrear: [{ type: i0.Input, args: [{ isSignal: true, alias: "permitirCrear", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }], factoryNuevoRegistro: [{ type: i0.Input, args: [{ isSignal: true, alias: "factoryNuevoRegistro", required: false }] }] } });
5058
-
5059
- class FileComponent {
5060
- // Inputs
5061
- existingFile = input(null, ...(ngDevMode ? [{ debugName: "existingFile" }] : /* istanbul ignore next */ []));
5062
- existingFileName = input(null, ...(ngDevMode ? [{ debugName: "existingFileName" }] : /* istanbul ignore next */ []));
5063
- overrideFileName = input(null, ...(ngDevMode ? [{ debugName: "overrideFileName" }] : /* istanbul ignore next */ []));
4975
+ class FileComponent {
4976
+ // Inputs
4977
+ existingFile = input(null, ...(ngDevMode ? [{ debugName: "existingFile" }] : /* istanbul ignore next */ []));
4978
+ existingFileName = input(null, ...(ngDevMode ? [{ debugName: "existingFileName" }] : /* istanbul ignore next */ []));
4979
+ overrideFileName = input(null, ...(ngDevMode ? [{ debugName: "overrideFileName" }] : /* istanbul ignore next */ []));
5064
4980
  debug = input(false, ...(ngDevMode ? [{ debugName: "debug" }] : /* istanbul ignore next */ []));
5065
4981
  required = input(true, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
5066
4982
  accept = input('.xls,.xlsx', ...(ngDevMode ? [{ debugName: "accept" }] : /* istanbul ignore next */ []));
@@ -8597,7 +8513,7 @@ class DsxdateShort {
8597
8513
  useExisting: forwardRef(() => DsxdateShort),
8598
8514
  multi: true,
8599
8515
  },
8600
- ], ngImport: i0, template: "<!-- dsxdate-short.html -->\r\n<p-floatLabel variant=\"on\">\r\n <p-datepicker\r\n [formControl]=\"fechaControl\"\r\n [fluid]=\"true\"\r\n [showIcon]=\"showIcon()\"\r\n [showOnFocus]=\"showOnFocus()\"\r\n [showButtonBar]=\"showButtonBar()\"\r\n [dateFormat]=\"dateFormat()\"\r\n [placeholder]=\"placeholder()\"\r\n pInputMask=\"99/99/9999\"\r\n />\r\n <label>{{ label() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 SIN CONDICI\u00D3N - IGUAL QUE EL ORIGINAL -->\r\n<dsx-message-error [control]=\"fechaControl\" />\r\n\r\n<!-- Debug info -->\r\n@if (debugEnabled() && isDevMode) {\r\n<div\r\n class=\"debug-info\"\r\n style=\"\r\n font-size: 12px;\r\n color: #666;\r\n margin-top: 4px;\r\n border-top: 1px dashed #ccc;\r\n padding-top: 4px;\r\n \"\r\n>\r\n <small>\r\n <strong>Debug:</strong>\r\n Value: {{ fechaControl.value | json }} | Valid: {{ fechaControl.valid }} |\r\n Invalid: {{ fechaControl.invalid }} | Errors: {{ fechaControl.errors | json\r\n }} | Dirty: {{ fechaControl.dirty }} | Touched: {{ fechaControl.touched }} |\r\n Status: {{ fechaControl.status }}\r\n </small>\r\n</div>\r\n}\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: FloatLabel, selector: "p-floatlabel, p-floatLabel, p-float-label", inputs: ["variant"] }, { kind: "component", type: DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: AppMessageErrorComponent, selector: "dsx-message-error", inputs: ["control", "form", "debugMode"] }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }] });
8516
+ ], ngImport: i0, template: "<!-- dsxdate-short.html -->\r\n<p-floatLabel variant=\"on\">\r\n <p-datepicker\r\n appendTo=\"body\"\r\n [formControl]=\"fechaControl\"\r\n [fluid]=\"true\"\r\n [showIcon]=\"showIcon()\"\r\n [showOnFocus]=\"showOnFocus()\"\r\n [showButtonBar]=\"showButtonBar()\"\r\n [dateFormat]=\"dateFormat()\"\r\n [placeholder]=\"placeholder()\"\r\n pInputMask=\"99/99/9999\"\r\n />\r\n <label>{{ label() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 SIN CONDICI\u00D3N - IGUAL QUE EL ORIGINAL -->\r\n<dsx-message-error [control]=\"fechaControl\" />\r\n\r\n<!-- Debug info -->\r\n@if (debugEnabled() && isDevMode) {\r\n<div\r\n class=\"debug-info\"\r\n style=\"\r\n font-size: 12px;\r\n color: #666;\r\n margin-top: 4px;\r\n border-top: 1px dashed #ccc;\r\n padding-top: 4px;\r\n \"\r\n>\r\n <small>\r\n <strong>Debug:</strong>\r\n Value: {{ fechaControl.value | json }} | Valid: {{ fechaControl.valid }} |\r\n Invalid: {{ fechaControl.invalid }} | Errors: {{ fechaControl.errors | json\r\n }} | Dirty: {{ fechaControl.dirty }} | Touched: {{ fechaControl.touched }} |\r\n Status: {{ fechaControl.status }}\r\n </small>\r\n</div>\r\n}\r\n", styles: [""], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: FloatLabel, selector: "p-floatlabel, p-floatLabel, p-float-label", inputs: ["variant"] }, { kind: "component", type: DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: AppMessageErrorComponent, selector: "dsx-message-error", inputs: ["control", "form", "debugMode"] }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }] });
8601
8517
  }
8602
8518
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxdateShort, decorators: [{
8603
8519
  type: Component,
@@ -8614,7 +8530,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
8614
8530
  useExisting: forwardRef(() => DsxdateShort),
8615
8531
  multi: true,
8616
8532
  },
8617
- ], template: "<!-- dsxdate-short.html -->\r\n<p-floatLabel variant=\"on\">\r\n <p-datepicker\r\n [formControl]=\"fechaControl\"\r\n [fluid]=\"true\"\r\n [showIcon]=\"showIcon()\"\r\n [showOnFocus]=\"showOnFocus()\"\r\n [showButtonBar]=\"showButtonBar()\"\r\n [dateFormat]=\"dateFormat()\"\r\n [placeholder]=\"placeholder()\"\r\n pInputMask=\"99/99/9999\"\r\n />\r\n <label>{{ label() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 SIN CONDICI\u00D3N - IGUAL QUE EL ORIGINAL -->\r\n<dsx-message-error [control]=\"fechaControl\" />\r\n\r\n<!-- Debug info -->\r\n@if (debugEnabled() && isDevMode) {\r\n<div\r\n class=\"debug-info\"\r\n style=\"\r\n font-size: 12px;\r\n color: #666;\r\n margin-top: 4px;\r\n border-top: 1px dashed #ccc;\r\n padding-top: 4px;\r\n \"\r\n>\r\n <small>\r\n <strong>Debug:</strong>\r\n Value: {{ fechaControl.value | json }} | Valid: {{ fechaControl.valid }} |\r\n Invalid: {{ fechaControl.invalid }} | Errors: {{ fechaControl.errors | json\r\n }} | Dirty: {{ fechaControl.dirty }} | Touched: {{ fechaControl.touched }} |\r\n Status: {{ fechaControl.status }}\r\n </small>\r\n</div>\r\n}\r\n" }]
8533
+ ], template: "<!-- dsxdate-short.html -->\r\n<p-floatLabel variant=\"on\">\r\n <p-datepicker\r\n appendTo=\"body\"\r\n [formControl]=\"fechaControl\"\r\n [fluid]=\"true\"\r\n [showIcon]=\"showIcon()\"\r\n [showOnFocus]=\"showOnFocus()\"\r\n [showButtonBar]=\"showButtonBar()\"\r\n [dateFormat]=\"dateFormat()\"\r\n [placeholder]=\"placeholder()\"\r\n pInputMask=\"99/99/9999\"\r\n />\r\n <label>{{ label() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 SIN CONDICI\u00D3N - IGUAL QUE EL ORIGINAL -->\r\n<dsx-message-error [control]=\"fechaControl\" />\r\n\r\n<!-- Debug info -->\r\n@if (debugEnabled() && isDevMode) {\r\n<div\r\n class=\"debug-info\"\r\n style=\"\r\n font-size: 12px;\r\n color: #666;\r\n margin-top: 4px;\r\n border-top: 1px dashed #ccc;\r\n padding-top: 4px;\r\n \"\r\n>\r\n <small>\r\n <strong>Debug:</strong>\r\n Value: {{ fechaControl.value | json }} | Valid: {{ fechaControl.valid }} |\r\n Invalid: {{ fechaControl.invalid }} | Errors: {{ fechaControl.errors | json\r\n }} | Dirty: {{ fechaControl.dirty }} | Touched: {{ fechaControl.touched }} |\r\n Status: {{ fechaControl.status }}\r\n </small>\r\n</div>\r\n}\r\n" }]
8618
8534
  }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], dateFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFormat", required: false }] }], showIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIcon", required: false }] }], showOnFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOnFocus", required: false }] }], showButtonBar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showButtonBar", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }] } });
8619
8535
 
8620
8536
  // dsx-input-currency.ts
@@ -8852,6 +8768,666 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
8852
8768
  args: [InputNumber]
8853
8769
  }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], currency: [{ type: i0.Input, args: [{ isSignal: true, alias: "currency", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], minFractionDigits: [{ type: i0.Input, args: [{ isSignal: true, alias: "minFractionDigits", required: false }] }], maxFractionDigits: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFractionDigits", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], selectAllOnFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectAllOnFocus", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }], showError: [{ type: i0.Input, args: [{ isSignal: true, alias: "showError", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], focus: [{ type: i0.Output, args: ["focus"] }], blur: [{ type: i0.Output, args: ["blur"] }] } });
8854
8770
 
8771
+ class DsxAutocomplete {
8772
+ injector = inject(Injector);
8773
+ cdr = inject(ChangeDetectorRef);
8774
+ ngControl = null;
8775
+ isDevModeInternal = isDevMode();
8776
+ controlSignal = signal(null, ...(ngDevMode ? [{ debugName: "controlSignal" }] : /* istanbul ignore next */ []));
8777
+ // ✅ Almacenar IDs pendientes (privado)
8778
+ pendingIdsInternal = [];
8779
+ pendingValue = null;
8780
+ pendingLogs = [];
8781
+ datasource = input.required(...(ngDevMode ? [{ debugName: "datasource" }] : /* istanbul ignore next */ []));
8782
+ optionLabel = input.required(...(ngDevMode ? [{ debugName: "optionLabel" }] : /* istanbul ignore next */ []));
8783
+ label = input('Seleccionar', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
8784
+ delay = input(100, ...(ngDevMode ? [{ debugName: "delay" }] : /* istanbul ignore next */ []));
8785
+ idKey = input.required(...(ngDevMode ? [{ debugName: "idKey" }] : /* istanbul ignore next */ []));
8786
+ dataKey = input('', ...(ngDevMode ? [{ debugName: "dataKey" }] : /* istanbul ignore next */ []));
8787
+ permitirCrear = input(false, ...(ngDevMode ? [{ debugName: "permitirCrear" }] : /* istanbul ignore next */ []));
8788
+ isMultiple = input(true, ...(ngDevMode ? [{ debugName: "isMultiple" }] : /* istanbul ignore next */ []));
8789
+ debug = input(false, ...(ngDevMode ? [{ debugName: "debug" }] : /* istanbul ignore next */ []));
8790
+ factoryNuevoRegistro = input(...(ngDevMode ? [undefined, { debugName: "factoryNuevoRegistro" }] : /* istanbul ignore next */ []));
8791
+ returnID = input(false, ...(ngDevMode ? [{ debugName: "returnID" }] : /* istanbul ignore next */ []));
8792
+ showError = input(true, ...(ngDevMode ? [{ debugName: "showError" }] : /* istanbul ignore next */ []));
8793
+ errorControl = computed(() => {
8794
+ const control = this.controlSignal();
8795
+ if (!this.showError())
8796
+ return null;
8797
+ return control?.control ?? null;
8798
+ }, ...(ngDevMode ? [{ debugName: "errorControl" }] : /* istanbul ignore next */ []));
8799
+ get isDevMode() {
8800
+ return this.isDevModeInternal;
8801
+ }
8802
+ // ✅ Getter público para IDs pendientes (solo para debug)
8803
+ get pendingIds() {
8804
+ return this.pendingIdsInternal;
8805
+ }
8806
+ // ✅ Getter para logs de pendientes
8807
+ get pendingLogsInfo() {
8808
+ return this.pendingLogs.join(' | ');
8809
+ }
8810
+ get formValue() {
8811
+ return this.ngControl?.control?.value;
8812
+ }
8813
+ get controlName() {
8814
+ return this.ngControl?.name || null;
8815
+ }
8816
+ get controlValid() {
8817
+ return this.ngControl?.control?.valid ?? true;
8818
+ }
8819
+ get controlTouched() {
8820
+ return this.ngControl?.control?.touched ?? false;
8821
+ }
8822
+ get controlDirty() {
8823
+ return this.ngControl?.control?.dirty ?? false;
8824
+ }
8825
+ get controlErrors() {
8826
+ return this.ngControl?.control?.errors ?? null;
8827
+ }
8828
+ get controlValue() {
8829
+ return this.ngControl?.control?.value;
8830
+ }
8831
+ get controlStatus() {
8832
+ if (!this.ngControl?.control)
8833
+ return 'sin control';
8834
+ const control = this.ngControl.control;
8835
+ return `valid: ${control.valid}, touched: ${control.touched}, dirty: ${control.dirty}, errors: ${JSON.stringify(control.errors)}`;
8836
+ }
8837
+ dataFiltrada = signal([], ...(ngDevMode ? [{ debugName: "dataFiltrada" }] : /* istanbul ignore next */ []));
8838
+ value = this.isMultiple() ? [] : null;
8839
+ onChange = () => { };
8840
+ onTouched = () => { };
8841
+ disabled = false;
8842
+ lastLogTime = 0;
8843
+ timeoutLimpieza = null;
8844
+ initialValueSet = false;
8845
+ constructor() {
8846
+ effect(() => {
8847
+ this.ngControl = this.injector.get(NgControl, null);
8848
+ this.controlSignal.set(this.ngControl);
8849
+ if (this.shouldDebug() && this.ngControl) {
8850
+ this.logDebug('Inferred from formControlName:', {
8851
+ controlName: this.getFormControlName(),
8852
+ optionLabel: this.optionLabel(),
8853
+ label: this.label(),
8854
+ showError: this.showError(),
8855
+ returnID: this.returnID(),
8856
+ isMultiple: this.isMultiple(),
8857
+ });
8858
+ }
8859
+ });
8860
+ // ✅ Effect mejorado para recargar cuando cambia el datasource
8861
+ effect(() => {
8862
+ const ds = this.datasource();
8863
+ if (ds.length > 0) {
8864
+ this.logDebug('📚 Datasource actualizado:', ds.length);
8865
+ // ✅ Si hay IDs pendientes, intentar cargarlos
8866
+ if (this.pendingIdsInternal.length > 0) {
8867
+ this.logDebug('🔄 Procesando IDs pendientes:', this.pendingIdsInternal);
8868
+ this.procesarIdsPendientes(this.pendingIdsInternal);
8869
+ // ✅ No limpiamos inmediatamente para poder ver el estado en debug
8870
+ }
8871
+ // ✅ Si ya se inicializó y hay valor en el formulario, recargar
8872
+ if (this.initialValueSet && this.ngControl?.control?.value) {
8873
+ this.logDebug('🔄 Recargando valor con nuevo datasource');
8874
+ this.writeValue(this.ngControl.control.value);
8875
+ }
8876
+ }
8877
+ });
8878
+ effect(() => {
8879
+ const control = this.ngControl?.control;
8880
+ if (control && this.shouldDebug()) {
8881
+ const subscription = control.statusChanges?.subscribe(() => {
8882
+ this.logDebug('📊 Estado del control cambiado:', {
8883
+ controlName: this.getFormControlName(),
8884
+ valid: control.valid,
8885
+ touched: control.touched,
8886
+ dirty: control.dirty,
8887
+ errors: control.errors,
8888
+ value: control.value,
8889
+ });
8890
+ });
8891
+ return () => {
8892
+ if (subscription) {
8893
+ subscription.unsubscribe();
8894
+ }
8895
+ };
8896
+ }
8897
+ return () => { };
8898
+ });
8899
+ }
8900
+ ngOnInit() {
8901
+ this.logDebug('🚀 Componente inicializado');
8902
+ this.value = this.isMultiple() ? [] : null;
8903
+ }
8904
+ ngAfterViewInit() {
8905
+ this.cdr.detectChanges();
8906
+ }
8907
+ shouldDebug() {
8908
+ return this.isDevModeInternal && this.debug();
8909
+ }
8910
+ logDebug(message, data) {
8911
+ if (!this.shouldDebug())
8912
+ return;
8913
+ const now = Date.now();
8914
+ if (now - this.lastLogTime < 50)
8915
+ return;
8916
+ this.lastLogTime = now;
8917
+ console.log(`[DsxAutocomplete] ${message}`, data || '');
8918
+ }
8919
+ // ✅ Método para registrar eventos de pendientes
8920
+ logPending(message, ids) {
8921
+ if (!this.shouldDebug())
8922
+ return;
8923
+ const timestamp = new Date().toISOString().substring(11, 19);
8924
+ const logEntry = `[${timestamp}] ${message} ${ids ? `IDs: [${ids.join(', ')}]` : ''}`;
8925
+ this.pendingLogs.push(logEntry);
8926
+ // Mantener solo los últimos 10 logs
8927
+ if (this.pendingLogs.length > 10) {
8928
+ this.pendingLogs.shift();
8929
+ }
8930
+ console.log(`[DsxAutocomplete] ⏳ PENDING: ${logEntry}`, {
8931
+ pendingIds: this.pendingIdsInternal,
8932
+ totalPending: this.pendingIdsInternal.length,
8933
+ });
8934
+ }
8935
+ // ✅ Nuevo método: Procesar IDs pendientes con logs mejorados
8936
+ procesarIdsPendientes(ids) {
8937
+ if (ids.length === 0) {
8938
+ this.logDebug('✅ No hay IDs pendientes para procesar');
8939
+ return;
8940
+ }
8941
+ const sourceData = this.datasource();
8942
+ if (sourceData.length === 0) {
8943
+ this.logPending('⚠️ Datasource vacío, no se pueden procesar IDs pendientes', ids);
8944
+ return;
8945
+ }
8946
+ this.logPending(`🔍 Procesando ${ids.length} IDs pendientes`, ids);
8947
+ const objetos = sourceData.filter((item) => ids.includes(Number(item[this.idKey()])));
8948
+ const objetosOrdenados = ids
8949
+ .map((id) => objetos.find((item) => Number(item[this.idKey()]) === id))
8950
+ .filter((item) => item !== undefined);
8951
+ const encontrados = objetosOrdenados.length;
8952
+ const noEncontrados = ids.length - encontrados;
8953
+ this.logPending(`✅ Procesados: ${encontrados} encontrados, ${noEncontrados} no encontrados`, ids);
8954
+ if (noEncontrados > 0) {
8955
+ const idsNoEncontrados = ids.filter((id) => !objetos.some((item) => Number(item[this.idKey()]) === id));
8956
+ this.logPending(`⚠️ IDs no encontrados en datasource:`, idsNoEncontrados);
8957
+ }
8958
+ // Si se encontraron todos los objetos, actualizar la vista
8959
+ if (encontrados > 0) {
8960
+ if (this.isMultiple()) {
8961
+ this.value = objetosOrdenados;
8962
+ }
8963
+ else {
8964
+ this.value = objetosOrdenados.length > 0 ? objetosOrdenados[0] : null;
8965
+ }
8966
+ this.cdr.detectChanges();
8967
+ // ✅ Limpiar pendientes solo si se encontraron todos
8968
+ if (noEncontrados === 0) {
8969
+ this.pendingIdsInternal = [];
8970
+ this.logPending('✅ Todos los IDs pendientes fueron resueltos', ids);
8971
+ }
8972
+ else {
8973
+ // Mantener solo los no encontrados
8974
+ const idsNoEncontrados = ids.filter((id) => !objetos.some((item) => Number(item[this.idKey()]) === id));
8975
+ this.pendingIdsInternal = idsNoEncontrados;
8976
+ this.logPending(`⏳ ${idsNoEncontrados.length} IDs pendientes aún no resueltos`, idsNoEncontrados);
8977
+ }
8978
+ }
8979
+ else {
8980
+ this.logPending('❌ Ningún ID pendiente pudo ser resuelto', ids);
8981
+ }
8982
+ }
8983
+ searchRequisitos(event) {
8984
+ const inicio = performance.now();
8985
+ const label = this.optionLabel();
8986
+ if (!event.query || event.query.trim() === '') {
8987
+ const todoElUniverso = this.datasource();
8988
+ this.dataFiltrada.set(todoElUniverso);
8989
+ this.logDebug('Modo dropdown activo', {
8990
+ registros: todoElUniverso.length,
8991
+ });
8992
+ return;
8993
+ }
8994
+ const queryNormalizado = this.normalizarTexto(event.query.trim());
8995
+ if (!queryNormalizado) {
8996
+ this.dataFiltrada.set(this.datasource());
8997
+ return;
8998
+ }
8999
+ const palabrasBuscadas = queryNormalizado
9000
+ .toLowerCase()
9001
+ .split(/\s+/)
9002
+ .filter((palabra) => palabra.length > 0);
9003
+ const filtrados = this.datasource()
9004
+ .filter((item) => {
9005
+ const textoRegistro = this.normalizarTexto(String(item[label] || '')).toLowerCase();
9006
+ return palabrasBuscadas.every((palabra) => textoRegistro.includes(palabra));
9007
+ })
9008
+ .slice(0, 10);
9009
+ this.dataFiltrada.set(filtrados);
9010
+ this.logDebug(`Búsqueda "${event.query}" (${(performance.now() - inicio).toFixed(2)}ms)`, { encontrados: filtrados.length });
9011
+ }
9012
+ normalizarTexto(texto) {
9013
+ if (!texto)
9014
+ return '';
9015
+ return texto
9016
+ .normalize('NFD')
9017
+ .replace(/[\u0300-\u036f]/g, '')
9018
+ .replace(/\s+/g, ' ')
9019
+ .trim();
9020
+ }
9021
+ evaluarYAgregar(event) {
9022
+ const valorInput = event.target.value?.trim();
9023
+ if (!valorInput) {
9024
+ this.dataFiltrada.set([]);
9025
+ if (this.obtenerArrayValue().length === 0) {
9026
+ this.actualizarFormulario([]);
9027
+ }
9028
+ return;
9029
+ }
9030
+ const sugerencias = this.dataFiltrada();
9031
+ const actuales = this.obtenerArrayValue();
9032
+ const campoId = this.idKey();
9033
+ if (sugerencias.length > 0) {
9034
+ const valorNormalizado = this.normalizarTexto(valorInput).toLowerCase();
9035
+ const primeraMatch = sugerencias.find((item) => {
9036
+ const itemLabel = this.normalizarTexto(String(item[this.optionLabel()] || '')).toLowerCase();
9037
+ return itemLabel === valorNormalizado;
9038
+ }) || sugerencias[0];
9039
+ if (!this.isMultiple()) {
9040
+ this.actualizarFormulario([primeraMatch]);
9041
+ this.logDebug('✅ Elemento reemplazado (modo single)', primeraMatch[this.optionLabel()]);
9042
+ event.target.value = '';
9043
+ this.dataFiltrada.set([]);
9044
+ return;
9045
+ }
9046
+ const yaExiste = actuales.some((item) => item[campoId] === primeraMatch[campoId]);
9047
+ if (!yaExiste) {
9048
+ this.actualizarFormulario([...actuales, primeraMatch]);
9049
+ this.logDebug('✅ Elemento agregado');
9050
+ }
9051
+ else {
9052
+ this.logDebug('⚠️ Elemento ya existe en la selección');
9053
+ event.target.value = '';
9054
+ this.dataFiltrada.set([]);
9055
+ return;
9056
+ }
9057
+ }
9058
+ else if (this.permitirCrear()) {
9059
+ const valorNormalizado = this.normalizarTexto(valorInput).toLowerCase();
9060
+ const yaExisteEnActuales = actuales.some((item) => {
9061
+ const itemLabel = this.normalizarTexto(String(item[this.optionLabel()] || '')).toLowerCase();
9062
+ return itemLabel === valorNormalizado;
9063
+ });
9064
+ if (yaExisteEnActuales) {
9065
+ this.logDebug('⚠️ Elemento ya existe en la selección');
9066
+ event.target.value = '';
9067
+ this.dataFiltrada.set([]);
9068
+ return;
9069
+ }
9070
+ let nuevo;
9071
+ if (this.factoryNuevoRegistro()) {
9072
+ nuevo = this.factoryNuevoRegistro()(valorInput);
9073
+ }
9074
+ else {
9075
+ nuevo = {
9076
+ [campoId]: 0,
9077
+ [this.optionLabel()]: valorInput.toUpperCase(),
9078
+ activo: true,
9079
+ };
9080
+ }
9081
+ this.actualizarFormulario(this.isMultiple() ? [...actuales, nuevo] : [nuevo]);
9082
+ this.logDebug('🆕 Nuevo elemento creado', valorInput);
9083
+ }
9084
+ else {
9085
+ this.logDebug('⚠️ No se encontraron coincidencias y no se permite crear', valorInput);
9086
+ this.actualizarFormulario([]);
9087
+ if (this.ngControl?.control) {
9088
+ this.ngControl.control.markAsTouched();
9089
+ this.onTouched();
9090
+ }
9091
+ }
9092
+ event.target.value = '';
9093
+ this.dataFiltrada.set([]);
9094
+ }
9095
+ clearSelection() {
9096
+ this.actualizarFormulario([]);
9097
+ this.logDebug('🧹 Selección limpiada');
9098
+ }
9099
+ onNgModelChange(selectedItems) {
9100
+ let items = [];
9101
+ if (selectedItems === null || selectedItems === undefined) {
9102
+ items = [];
9103
+ }
9104
+ else if (Array.isArray(selectedItems)) {
9105
+ items = selectedItems;
9106
+ }
9107
+ else {
9108
+ items = [selectedItems];
9109
+ }
9110
+ this.logDebug('📝 onNgModelChange:', {
9111
+ items: items.length,
9112
+ isMultiple: this.isMultiple(),
9113
+ isArray: Array.isArray(selectedItems),
9114
+ isNull: selectedItems === null,
9115
+ returnID: this.returnID(),
9116
+ });
9117
+ if (this.isMultiple()) {
9118
+ this.value = items;
9119
+ }
9120
+ else {
9121
+ this.value = items.length > 0 ? items[0] : null;
9122
+ }
9123
+ const outputValue = this.getOutputValue(items);
9124
+ this.onChange(outputValue);
9125
+ this.logDebug('📊 Estado del control después de onChange:', {
9126
+ controlName: this.getFormControlName(),
9127
+ status: this.controlStatus,
9128
+ value: outputValue,
9129
+ });
9130
+ if (items.length === 0 && this.ngControl?.control) {
9131
+ this.ngControl.control.markAsTouched();
9132
+ this.onTouched();
9133
+ this.logDebug('📝 Control marcado como touched (selección vacía)', {
9134
+ newStatus: this.controlStatus,
9135
+ });
9136
+ }
9137
+ this.logDebug('📝 Selección actualizada', {
9138
+ returnID: this.returnID(),
9139
+ items: items.length,
9140
+ output: outputValue,
9141
+ });
9142
+ }
9143
+ getOutputValue(items) {
9144
+ if (items.length === 0) {
9145
+ return this.isMultiple() ? [] : null;
9146
+ }
9147
+ if (this.returnID()) {
9148
+ const ids = items.map((item) => Number(item[this.idKey()]));
9149
+ return this.isMultiple() ? ids : ids[0];
9150
+ }
9151
+ return this.isMultiple() ? items : items[0];
9152
+ }
9153
+ obtenerArrayValue() {
9154
+ if (this.isMultiple()) {
9155
+ return Array.isArray(this.value) ? this.value : [];
9156
+ }
9157
+ return this.value ? [this.value] : [];
9158
+ }
9159
+ getValueLength() {
9160
+ if (this.isMultiple()) {
9161
+ return Array.isArray(this.value) ? this.value.length : 0;
9162
+ }
9163
+ return this.value ? 1 : 0;
9164
+ }
9165
+ getValueDisplay(type) {
9166
+ if (!this.value || (Array.isArray(this.value) && this.value.length === 0)) {
9167
+ return 'Ninguno';
9168
+ }
9169
+ const items = Array.isArray(this.value) ? this.value : [this.value];
9170
+ if (type === 'id') {
9171
+ return items.map((item) => item[this.idKey()]).join(', ') || 'Ninguno';
9172
+ }
9173
+ else {
9174
+ return (items.map((item) => item[this.optionLabel()]).join(', ') || 'Ninguno');
9175
+ }
9176
+ }
9177
+ onHide() {
9178
+ this.logDebug('Dropdown ocultado - Limpiando estado');
9179
+ if (this.timeoutLimpieza) {
9180
+ clearTimeout(this.timeoutLimpieza);
9181
+ this.timeoutLimpieza = null;
9182
+ }
9183
+ this.timeoutLimpieza = setTimeout(() => {
9184
+ this.dataFiltrada.set([]);
9185
+ this.timeoutLimpieza = null;
9186
+ }, 100);
9187
+ if (this.ngControl?.control) {
9188
+ const estabaTouched = this.ngControl.control.touched;
9189
+ if (!estabaTouched) {
9190
+ this.ngControl.control.markAsTouched();
9191
+ this.onTouched();
9192
+ this.logDebug('Control marked as touched (onHide)', {
9193
+ controlName: this.getFormControlName(),
9194
+ newStatus: this.controlStatus,
9195
+ });
9196
+ }
9197
+ else {
9198
+ this.logDebug('Control ya estaba touched (onHide)', {
9199
+ controlName: this.getFormControlName(),
9200
+ status: this.controlStatus,
9201
+ });
9202
+ }
9203
+ }
9204
+ }
9205
+ onShow() {
9206
+ this.logDebug('Dropdown mostrado');
9207
+ if (this.timeoutLimpieza) {
9208
+ clearTimeout(this.timeoutLimpieza);
9209
+ this.timeoutLimpieza = null;
9210
+ }
9211
+ }
9212
+ actualizarFormulario(nuevaLista) {
9213
+ if (nuevaLista.length === 0) {
9214
+ if (this.isMultiple()) {
9215
+ this.value = [];
9216
+ }
9217
+ else {
9218
+ this.value = null;
9219
+ }
9220
+ const outputValue = this.isMultiple() ? [] : null;
9221
+ this.onChange(outputValue);
9222
+ this.logDebug('Formulario actualizado (vacío)', {
9223
+ isMultiple: this.isMultiple(),
9224
+ output: outputValue,
9225
+ });
9226
+ return;
9227
+ }
9228
+ if (this.isMultiple()) {
9229
+ this.value = nuevaLista;
9230
+ }
9231
+ else {
9232
+ this.value = nuevaLista.length > 0 ? nuevaLista[0] : null;
9233
+ }
9234
+ const outputValue = this.getOutputValue(nuevaLista);
9235
+ this.onChange(outputValue);
9236
+ this.logDebug('Formulario actualizado', {
9237
+ total: nuevaLista.length,
9238
+ isMultiple: this.isMultiple(),
9239
+ returnID: this.returnID(),
9240
+ output: outputValue,
9241
+ });
9242
+ }
9243
+ getFormControlName() {
9244
+ return this.ngControl?.name || null;
9245
+ }
9246
+ // ✨ MEJORADO: writeValue con manejo de IDs pendientes y logs
9247
+ writeValue(value) {
9248
+ const timestamp = new Date().toISOString();
9249
+ this.logDebug(`🔄 writeValue INVOCADO ${timestamp}`, {
9250
+ value,
9251
+ type: value === null
9252
+ ? 'null'
9253
+ : value === undefined
9254
+ ? 'undefined'
9255
+ : Array.isArray(value)
9256
+ ? 'array'
9257
+ : typeof value === 'object'
9258
+ ? 'object'
9259
+ : typeof value,
9260
+ returnID: this.returnID(),
9261
+ isMultiple: this.isMultiple(),
9262
+ hasDatasource: this.datasource().length > 0,
9263
+ datasourceSize: this.datasource().length,
9264
+ pendingIdsActuales: this.pendingIdsInternal,
9265
+ });
9266
+ if (value === null || value === undefined) {
9267
+ this.logDebug(`⚠️ Valor null/undefined, limpiando selección`);
9268
+ this.value = this.isMultiple() ? [] : null;
9269
+ if (this.pendingIdsInternal.length > 0) {
9270
+ this.logPending('🧹 Limpiando IDs pendientes por null/undefined', this.pendingIdsInternal);
9271
+ this.pendingIdsInternal = [];
9272
+ }
9273
+ this.pendingValue = null;
9274
+ this.initialValueSet = true;
9275
+ this.cdr.markForCheck();
9276
+ this.cdr.detectChanges();
9277
+ return;
9278
+ }
9279
+ let items = [];
9280
+ let ids = [];
9281
+ if (Array.isArray(value)) {
9282
+ if (value.length === 0) {
9283
+ this.value = this.isMultiple() ? [] : null;
9284
+ if (this.pendingIdsInternal.length > 0) {
9285
+ this.logPending('🧹 Limpiando IDs pendientes por array vacío', this.pendingIdsInternal);
9286
+ this.pendingIdsInternal = [];
9287
+ }
9288
+ this.pendingValue = null;
9289
+ this.initialValueSet = true;
9290
+ this.cdr.markForCheck();
9291
+ this.cdr.detectChanges();
9292
+ return;
9293
+ }
9294
+ if (typeof value[0] === 'number') {
9295
+ ids = value;
9296
+ this.logDebug(`📊 Recibidos ${ids.length} IDs:`, ids);
9297
+ }
9298
+ else if (typeof value[0] === 'object' && value[0] !== null) {
9299
+ items = value;
9300
+ this.logDebug(`📊 Recibidos ${items.length} objetos:`, items.map((i) => i[this.optionLabel()]));
9301
+ }
9302
+ }
9303
+ else if (typeof value === 'number') {
9304
+ ids = [value];
9305
+ this.logDebug(`📊 Recibido 1 ID:`, [value]);
9306
+ }
9307
+ else if (typeof value === 'object' && value !== null) {
9308
+ items = [value];
9309
+ this.logDebug(`📊 Recibido 1 objeto:`, value[this.optionLabel()]);
9310
+ }
9311
+ else {
9312
+ this.value = this.isMultiple() ? [] : null;
9313
+ if (this.pendingIdsInternal.length > 0) {
9314
+ this.logPending('🧹 Limpiando IDs pendientes por tipo no soportado', this.pendingIdsInternal);
9315
+ this.pendingIdsInternal = [];
9316
+ }
9317
+ this.pendingValue = null;
9318
+ this.initialValueSet = true;
9319
+ this.cdr.markForCheck();
9320
+ this.cdr.detectChanges();
9321
+ return;
9322
+ }
9323
+ const sourceData = this.datasource();
9324
+ // ✅ Si tenemos IDs y el datasource está vacío, guardarlos para después
9325
+ if (ids.length > 0 && sourceData.length === 0) {
9326
+ this.logPending(`⏳ Datasource vacío (${sourceData.length}), guardando ${ids.length} IDs para procesar después`, ids);
9327
+ this.pendingIdsInternal = ids;
9328
+ this.pendingValue = value;
9329
+ this.initialValueSet = true;
9330
+ // ✅ Mostrar un placeholder visual mientras se cargan los datos
9331
+ this.value = this.isMultiple() ? [] : null;
9332
+ this.cdr.markForCheck();
9333
+ this.cdr.detectChanges();
9334
+ this.logDebug(`⏳ IDs guardados como pendientes:`, this.pendingIdsInternal);
9335
+ return;
9336
+ }
9337
+ // ✅ Si tenemos IDs y el datasource existe, buscar los objetos
9338
+ if (ids.length > 0 && sourceData.length > 0) {
9339
+ this.logDebug(`🔍 Buscando ${ids.length} IDs en datasource de ${sourceData.length} elementos`);
9340
+ const objetos = sourceData.filter((item) => ids.includes(Number(item[this.idKey()])));
9341
+ const objetosOrdenados = ids
9342
+ .map((id) => objetos.find((item) => Number(item[this.idKey()]) === id))
9343
+ .filter((item) => item !== undefined);
9344
+ items = objetosOrdenados;
9345
+ // ✅ Si no se encontraron algunos IDs, guardarlos como pendientes
9346
+ const idsNoEncontrados = ids.filter((id) => !objetos.some((item) => Number(item[this.idKey()]) === id));
9347
+ if (idsNoEncontrados.length > 0) {
9348
+ this.logPending(`⚠️ ${idsNoEncontrados.length} IDs no encontrados en datasource`, idsNoEncontrados);
9349
+ this.pendingIdsInternal = idsNoEncontrados;
9350
+ this.pendingValue = value;
9351
+ }
9352
+ else {
9353
+ // ✅ Todos los IDs fueron encontrados
9354
+ if (this.pendingIdsInternal.length > 0) {
9355
+ this.logPending('✅ Todos los IDs pendientes fueron resueltos', this.pendingIdsInternal);
9356
+ this.pendingIdsInternal = [];
9357
+ this.pendingValue = null;
9358
+ }
9359
+ }
9360
+ this.logDebug(`🔍 Encontrados ${items.length} de ${ids.length} IDs`);
9361
+ }
9362
+ // ✅ Verificar que los items existan en el datasource
9363
+ if (items.length > 0 && sourceData.length > 0) {
9364
+ const itemsValidos = items.filter((item) => sourceData.some((s) => s[this.idKey()] === item[this.idKey()]));
9365
+ if (itemsValidos.length !== items.length) {
9366
+ this.logDebug(`⚠️ Algunos objetos no existen en datasource`, {
9367
+ original: items.length,
9368
+ validos: itemsValidos.length,
9369
+ });
9370
+ items = itemsValidos;
9371
+ }
9372
+ }
9373
+ // ✅ Asignar el valor
9374
+ if (this.isMultiple()) {
9375
+ this.value = items;
9376
+ }
9377
+ else {
9378
+ this.value = items.length > 0 ? items[0] : null;
9379
+ }
9380
+ this.initialValueSet = true;
9381
+ this.cdr.detectChanges();
9382
+ this.logDebug(`✅ Vista actualizada con ${items.length} elementos (${this.isMultiple() ? 'multiple' : 'single'})`);
9383
+ if (this.pendingIdsInternal.length > 0) {
9384
+ this.logDebug(`⏳ IDs pendientes actuales:`, this.pendingIdsInternal);
9385
+ }
9386
+ }
9387
+ forceReload() {
9388
+ this.logDebug('🔄 Forzando recarga');
9389
+ if (this.ngControl?.control) {
9390
+ const currentValue = this.ngControl.control.value;
9391
+ this.logDebug('🔄 Valor actual del formulario:', currentValue);
9392
+ this.writeValue(currentValue);
9393
+ }
9394
+ }
9395
+ registerOnChange(fn) {
9396
+ this.onChange = fn;
9397
+ }
9398
+ registerOnTouched(fn) {
9399
+ this.onTouched = fn;
9400
+ }
9401
+ setDisabledState(isDisabled) {
9402
+ this.disabled = isDisabled;
9403
+ this.cdr.markForCheck();
9404
+ }
9405
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxAutocomplete, deps: [], target: i0.ɵɵFactoryTarget.Component });
9406
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: DsxAutocomplete, isStandalone: true, selector: "dsx-autocomplete", inputs: { datasource: { classPropertyName: "datasource", publicName: "datasource", isSignal: true, isRequired: true, transformFunction: null }, optionLabel: { classPropertyName: "optionLabel", publicName: "optionLabel", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, delay: { classPropertyName: "delay", publicName: "delay", isSignal: true, isRequired: false, transformFunction: null }, idKey: { classPropertyName: "idKey", publicName: "idKey", isSignal: true, isRequired: true, transformFunction: null }, dataKey: { classPropertyName: "dataKey", publicName: "dataKey", isSignal: true, isRequired: false, transformFunction: null }, permitirCrear: { classPropertyName: "permitirCrear", publicName: "permitirCrear", isSignal: true, isRequired: false, transformFunction: null }, isMultiple: { classPropertyName: "isMultiple", publicName: "isMultiple", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, factoryNuevoRegistro: { classPropertyName: "factoryNuevoRegistro", publicName: "factoryNuevoRegistro", isSignal: true, isRequired: false, transformFunction: null }, returnID: { classPropertyName: "returnID", publicName: "returnID", isSignal: true, isRequired: false, transformFunction: null }, showError: { classPropertyName: "showError", publicName: "showError", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
9407
+ {
9408
+ provide: NG_VALUE_ACCESSOR,
9409
+ useExisting: forwardRef(() => DsxAutocomplete),
9410
+ multi: true,
9411
+ },
9412
+ ], ngImport: i0, template: "<p-floatlabel variant=\"on\">\r\n <p-autoComplete\r\n fluid\r\n [multiple]=\"isMultiple()\"\r\n [dropdown]=\"true\"\r\n [showClear]=\"true\"\r\n [suggestions]=\"dataFiltrada()\"\r\n (completeMethod)=\"searchRequisitos($event)\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"onNgModelChange($event)\"\r\n (onBlur)=\"onTouched()\"\r\n (onShow)=\"onShow()\"\r\n (onHide)=\"onHide()\"\r\n [disabled]=\"disabled\"\r\n [optionLabel]=\"optionLabel()\"\r\n [dataKey]=\"dataKey() || idKey()\"\r\n (keyup.enter)=\"evaluarYAgregar($event)\"\r\n [delay]=\"delay()\"\r\n placeholder=\"Ingresa un texto....\"\r\n [inputStyle]=\"{'text-transform': 'uppercase'}\"\r\n >\r\n <ng-template let-item pTemplate=\"selectedItem\">\r\n {{ item[optionLabel()] }}\r\n </ng-template>\r\n\r\n <ng-template let-item pTemplate=\"item\">\r\n {{ item[optionLabel()] }}\r\n </ng-template>\r\n </p-autoComplete>\r\n <label>{{ label() }}</label>\r\n</p-floatlabel>\r\n\r\n<!-- \u2728 Indicador de carga (cuando hay IDs pendientes) -->\r\n@if (pendingIds.length > 0 && debug() && isDevMode) {\r\n<div\r\n style=\"\r\n font-size: 10px;\r\n color: #f59e0b;\r\n margin-top: 2px;\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n \"\r\n>\r\n <span>\u23F3</span>\r\n <span\r\n >Cargando datos pendientes ({{ pendingIds.length }} IDs: [{{\r\n pendingIds.join(', ') }}])</span\r\n >\r\n</div>\r\n}\r\n\r\n<!-- \u2728 Bot\u00F3n de limpiar para modo single -->\r\n@if (!isMultiple() && value) {\r\n<button\r\n type=\"button\"\r\n (click)=\"clearSelection()\"\r\n style=\"\r\n position: absolute;\r\n right: 10px;\r\n top: 50%;\r\n transform: translateY(-50%);\r\n background: transparent;\r\n border: none;\r\n cursor: pointer;\r\n color: #999;\r\n font-size: 14px;\r\n z-index: 1;\r\n \"\r\n aria-label=\"Limpiar selecci\u00F3n\"\r\n>\r\n \u2715\r\n</button>\r\n}\r\n\r\n<!-- Mensaje de error -->\r\n@if (showError() && errorControl()) {\r\n<dsx-message-error [control]=\"errorControl()\" />\r\n}\r\n\r\n<!-- Panel de Debug -->\r\n@if (debug() && isDevMode) {\r\n<div\r\n style=\"\r\n margin-top: 8px;\r\n font-size: 11px;\r\n color: #666;\r\n background: #f5f5f5;\r\n padding: 6px 10px;\r\n border-radius: 4px;\r\n font-family: monospace;\r\n border: 1px solid #ddd;\r\n \"\r\n>\r\n <div\r\n style=\"\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n gap: 8px;\r\n flex-wrap: wrap;\r\n \"\r\n >\r\n <div>\r\n <strong>\uD83D\uDD0D Debug:</strong>\r\n <span style=\"margin-left: 8px\">Items: {{ getValueLength() }}</span>\r\n <span style=\"margin-left: 8px\">IDs: {{ getValueDisplay('id') }}</span>\r\n <span style=\"margin-left: 8px\"\r\n >Nombres: {{ getValueDisplay('nombre') }}</span\r\n >\r\n <span style=\"margin-left: 8px; color: #888; font-size: 10px\">\r\n | returnID: {{ returnID() ? '\u2705 IDs' : '\u274C Objetos' }} | Multiple: {{\r\n isMultiple() ? '\u2705' : '\u274C' }} | Control: {{ controlName || 'sin nombre'\r\n }}\r\n </span>\r\n @if (pendingIds.length > 0) {\r\n <span style=\"margin-left: 8px; color: #f59e0b; font-weight: bold\">\r\n | \u23F3 Pendientes: {{ pendingIds.length }} ({{ pendingIds.join(', ') }})\r\n </span>\r\n } @else {\r\n <span style=\"margin-left: 8px; color: #10b981; font-weight: bold\">\r\n | \u2705 Sin pendientes\r\n </span>\r\n }\r\n <span style=\"margin-left: 8px; color: #888; font-size: 9px\">\r\n | Logs: {{ pendingLogsInfo }}\r\n </span>\r\n </div>\r\n <div\r\n style=\"\r\n font-size: 10px;\r\n color: #333;\r\n background: #e8e8e8;\r\n padding: 2px 8px;\r\n border-radius: 4px;\r\n \"\r\n >\r\n <strong>\uD83D\uDCCA Validaci\u00F3n:</strong>\r\n <span [style.color]=\"controlValid ? 'green' : 'red'\">\r\n {{ controlValid ? '\u2705 V\u00E1lido' : '\u274C Inv\u00E1lido' }}\r\n </span>\r\n <span style=\"margin-left: 4px\">\r\n | Touched: {{ controlTouched ? '\u2705' : '\u274C' }}\r\n </span>\r\n <span style=\"margin-left: 4px\">\r\n | Dirty: {{ controlDirty ? '\u2705' : '\u274C' }}\r\n </span>\r\n @if (controlErrors) {\r\n <span style=\"margin-left: 4px; color: red\">\r\n | Errors: {{ controlErrors | json }}\r\n </span>\r\n }\r\n </div>\r\n <button\r\n (click)=\"forceReload()\"\r\n style=\"\r\n font-size: 10px;\r\n padding: 2px 8px;\r\n border-radius: 4px;\r\n border: 1px solid #ccc;\r\n background: white;\r\n cursor: pointer;\r\n \"\r\n type=\"button\"\r\n >\r\n \uD83D\uDD04 Recargar\r\n </button>\r\n </div>\r\n</div>\r\n}\r\n", styles: [""], dependencies: [{ kind: "component", type: AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: FloatLabel, selector: "p-floatlabel, p-floatLabel, p-float-label", inputs: ["variant"] }, { kind: "component", type: AppMessageErrorComponent, selector: "dsx-message-error", inputs: ["control", "form", "debugMode"] }, { kind: "pipe", type: JsonPipe, name: "json" }] });
9413
+ }
9414
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxAutocomplete, decorators: [{
9415
+ type: Component,
9416
+ args: [{ selector: 'dsx-autocomplete', imports: [
9417
+ AutoComplete,
9418
+ FormsModule,
9419
+ FloatLabel,
9420
+ JsonPipe,
9421
+ AppMessageErrorComponent,
9422
+ ], providers: [
9423
+ {
9424
+ provide: NG_VALUE_ACCESSOR,
9425
+ useExisting: forwardRef(() => DsxAutocomplete),
9426
+ multi: true,
9427
+ },
9428
+ ], template: "<p-floatlabel variant=\"on\">\r\n <p-autoComplete\r\n fluid\r\n [multiple]=\"isMultiple()\"\r\n [dropdown]=\"true\"\r\n [showClear]=\"true\"\r\n [suggestions]=\"dataFiltrada()\"\r\n (completeMethod)=\"searchRequisitos($event)\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"onNgModelChange($event)\"\r\n (onBlur)=\"onTouched()\"\r\n (onShow)=\"onShow()\"\r\n (onHide)=\"onHide()\"\r\n [disabled]=\"disabled\"\r\n [optionLabel]=\"optionLabel()\"\r\n [dataKey]=\"dataKey() || idKey()\"\r\n (keyup.enter)=\"evaluarYAgregar($event)\"\r\n [delay]=\"delay()\"\r\n placeholder=\"Ingresa un texto....\"\r\n [inputStyle]=\"{'text-transform': 'uppercase'}\"\r\n >\r\n <ng-template let-item pTemplate=\"selectedItem\">\r\n {{ item[optionLabel()] }}\r\n </ng-template>\r\n\r\n <ng-template let-item pTemplate=\"item\">\r\n {{ item[optionLabel()] }}\r\n </ng-template>\r\n </p-autoComplete>\r\n <label>{{ label() }}</label>\r\n</p-floatlabel>\r\n\r\n<!-- \u2728 Indicador de carga (cuando hay IDs pendientes) -->\r\n@if (pendingIds.length > 0 && debug() && isDevMode) {\r\n<div\r\n style=\"\r\n font-size: 10px;\r\n color: #f59e0b;\r\n margin-top: 2px;\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n \"\r\n>\r\n <span>\u23F3</span>\r\n <span\r\n >Cargando datos pendientes ({{ pendingIds.length }} IDs: [{{\r\n pendingIds.join(', ') }}])</span\r\n >\r\n</div>\r\n}\r\n\r\n<!-- \u2728 Bot\u00F3n de limpiar para modo single -->\r\n@if (!isMultiple() && value) {\r\n<button\r\n type=\"button\"\r\n (click)=\"clearSelection()\"\r\n style=\"\r\n position: absolute;\r\n right: 10px;\r\n top: 50%;\r\n transform: translateY(-50%);\r\n background: transparent;\r\n border: none;\r\n cursor: pointer;\r\n color: #999;\r\n font-size: 14px;\r\n z-index: 1;\r\n \"\r\n aria-label=\"Limpiar selecci\u00F3n\"\r\n>\r\n \u2715\r\n</button>\r\n}\r\n\r\n<!-- Mensaje de error -->\r\n@if (showError() && errorControl()) {\r\n<dsx-message-error [control]=\"errorControl()\" />\r\n}\r\n\r\n<!-- Panel de Debug -->\r\n@if (debug() && isDevMode) {\r\n<div\r\n style=\"\r\n margin-top: 8px;\r\n font-size: 11px;\r\n color: #666;\r\n background: #f5f5f5;\r\n padding: 6px 10px;\r\n border-radius: 4px;\r\n font-family: monospace;\r\n border: 1px solid #ddd;\r\n \"\r\n>\r\n <div\r\n style=\"\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n gap: 8px;\r\n flex-wrap: wrap;\r\n \"\r\n >\r\n <div>\r\n <strong>\uD83D\uDD0D Debug:</strong>\r\n <span style=\"margin-left: 8px\">Items: {{ getValueLength() }}</span>\r\n <span style=\"margin-left: 8px\">IDs: {{ getValueDisplay('id') }}</span>\r\n <span style=\"margin-left: 8px\"\r\n >Nombres: {{ getValueDisplay('nombre') }}</span\r\n >\r\n <span style=\"margin-left: 8px; color: #888; font-size: 10px\">\r\n | returnID: {{ returnID() ? '\u2705 IDs' : '\u274C Objetos' }} | Multiple: {{\r\n isMultiple() ? '\u2705' : '\u274C' }} | Control: {{ controlName || 'sin nombre'\r\n }}\r\n </span>\r\n @if (pendingIds.length > 0) {\r\n <span style=\"margin-left: 8px; color: #f59e0b; font-weight: bold\">\r\n | \u23F3 Pendientes: {{ pendingIds.length }} ({{ pendingIds.join(', ') }})\r\n </span>\r\n } @else {\r\n <span style=\"margin-left: 8px; color: #10b981; font-weight: bold\">\r\n | \u2705 Sin pendientes\r\n </span>\r\n }\r\n <span style=\"margin-left: 8px; color: #888; font-size: 9px\">\r\n | Logs: {{ pendingLogsInfo }}\r\n </span>\r\n </div>\r\n <div\r\n style=\"\r\n font-size: 10px;\r\n color: #333;\r\n background: #e8e8e8;\r\n padding: 2px 8px;\r\n border-radius: 4px;\r\n \"\r\n >\r\n <strong>\uD83D\uDCCA Validaci\u00F3n:</strong>\r\n <span [style.color]=\"controlValid ? 'green' : 'red'\">\r\n {{ controlValid ? '\u2705 V\u00E1lido' : '\u274C Inv\u00E1lido' }}\r\n </span>\r\n <span style=\"margin-left: 4px\">\r\n | Touched: {{ controlTouched ? '\u2705' : '\u274C' }}\r\n </span>\r\n <span style=\"margin-left: 4px\">\r\n | Dirty: {{ controlDirty ? '\u2705' : '\u274C' }}\r\n </span>\r\n @if (controlErrors) {\r\n <span style=\"margin-left: 4px; color: red\">\r\n | Errors: {{ controlErrors | json }}\r\n </span>\r\n }\r\n </div>\r\n <button\r\n (click)=\"forceReload()\"\r\n style=\"\r\n font-size: 10px;\r\n padding: 2px 8px;\r\n border-radius: 4px;\r\n border: 1px solid #ccc;\r\n background: white;\r\n cursor: pointer;\r\n \"\r\n type=\"button\"\r\n >\r\n \uD83D\uDD04 Recargar\r\n </button>\r\n </div>\r\n</div>\r\n}\r\n" }]
9429
+ }], ctorParameters: () => [], propDecorators: { datasource: [{ type: i0.Input, args: [{ isSignal: true, alias: "datasource", required: true }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], delay: [{ type: i0.Input, args: [{ isSignal: true, alias: "delay", required: false }] }], idKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "idKey", required: true }] }], dataKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataKey", required: false }] }], permitirCrear: [{ type: i0.Input, args: [{ isSignal: true, alias: "permitirCrear", required: false }] }], isMultiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "isMultiple", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }], factoryNuevoRegistro: [{ type: i0.Input, args: [{ isSignal: true, alias: "factoryNuevoRegistro", required: false }] }], returnID: [{ type: i0.Input, args: [{ isSignal: true, alias: "returnID", required: false }] }], showError: [{ type: i0.Input, args: [{ isSignal: true, alias: "showError", required: false }] }] } });
9430
+
8855
9431
  // dsx-select.component.ts
8856
9432
  class DsxSelect {
8857
9433
  injector = inject(Injector);