ngx-dsxlibrary 2.21.70 → 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.
|
@@ -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
|
/**
|
|
@@ -4102,181 +4277,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
4102
4277
|
}]
|
|
4103
4278
|
}] });
|
|
4104
4279
|
|
|
4105
|
-
/**
|
|
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)
|
|
4142
|
-
*
|
|
4143
|
-
* **Navegación / Utilidades**
|
|
4144
|
-
* - `home` → Ir al inicio (gris)
|
|
4145
|
-
* - `refresh` → Recargar datos desde el servidor (morado)
|
|
4146
|
-
*
|
|
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
4280
|
/**
|
|
4281
4281
|
* Paleta corporativa de colores para `dsx-button`.
|
|
4282
4282
|
*
|
|
@@ -4303,22 +4303,22 @@ const DSX_PALETTE = {
|
|
|
4303
4303
|
danger: '#EF5350',
|
|
4304
4304
|
warning: '#FF7043',
|
|
4305
4305
|
caution: '#FFB300',
|
|
4306
|
-
success: '#0ba055',
|
|
4307
|
-
primary: '#0066cc',
|
|
4306
|
+
success: '#0ba055',
|
|
4307
|
+
primary: '#0066cc',
|
|
4308
4308
|
info: '#26C6DA',
|
|
4309
4309
|
neutral: '#90A4AE',
|
|
4310
|
-
restore: '#B0BEC5',
|
|
4311
|
-
return: '#11438d',
|
|
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',
|
|
4318
|
-
cyan: '#00BCD4',
|
|
4319
|
-
olive: '#808000',
|
|
4320
|
-
brown: '#8D6E63',
|
|
4321
|
-
slate: '#607D8B',
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
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
|
-
|
|
4413
|
-
`
|
|
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
|
-
|
|
4420
|
-
`
|
|
4421
|
-
|
|
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
|
-
|
|
4452
|
-
`
|
|
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
|
-
|
|
4460
|
-
`
|
|
4461
|
-
|
|
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.
|
|
4610
|
-
|
|
4611
|
-
|
|
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(
|
|
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
|
-
|
|
4625
|
-
|
|
4626
|
-
navigated
|
|
4627
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
4754
|
-
|
|
4705
|
+
// ============ MÉTODOS DE DEBUG ============
|
|
4706
|
+
logFullState() {
|
|
4707
|
+
if (!this._isDev || !this.debug())
|
|
4755
4708
|
return;
|
|
4756
|
-
|
|
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
|
-
|
|
4759
|
-
if (!this.debug())
|
|
4798
|
+
logValidationIssues(validations) {
|
|
4799
|
+
if (!this._isDev || !this.debug())
|
|
4760
4800
|
return;
|
|
4761
|
-
|
|
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
|
-
|
|
4764
|
-
|
|
4834
|
+
// Métodos de log existentes mejorados
|
|
4835
|
+
log(message, data) {
|
|
4836
|
+
if (!this._isDev || !this.debug())
|
|
4765
4837
|
return;
|
|
4766
|
-
console.
|
|
4838
|
+
console.log(`%c[dsx-button:${this.type()}] ${message}`, 'color:#1976d2;font-weight:bold', data ?? '');
|
|
4767
4839
|
}
|
|
4768
|
-
|
|
4769
|
-
if (!this.debug())
|
|
4840
|
+
warn(message, data) {
|
|
4841
|
+
if (!this._isDev || !this.debug())
|
|
4770
4842
|
return;
|
|
4771
|
-
console.
|
|
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: '
|
|
4948
|
+
primeIcon: 'fa-solid fa-eye',
|
|
4877
4949
|
colorToken: 'view',
|
|
4878
4950
|
tooltip: 'Vista previa',
|
|
4879
4951
|
},
|
|
@@ -8702,6 +8774,10 @@ class DsxAutocomplete {
|
|
|
8702
8774
|
ngControl = null;
|
|
8703
8775
|
isDevModeInternal = isDevMode();
|
|
8704
8776
|
controlSignal = signal(null, ...(ngDevMode ? [{ debugName: "controlSignal" }] : /* istanbul ignore next */ []));
|
|
8777
|
+
// ✅ Almacenar IDs pendientes (privado)
|
|
8778
|
+
pendingIdsInternal = [];
|
|
8779
|
+
pendingValue = null;
|
|
8780
|
+
pendingLogs = [];
|
|
8705
8781
|
datasource = input.required(...(ngDevMode ? [{ debugName: "datasource" }] : /* istanbul ignore next */ []));
|
|
8706
8782
|
optionLabel = input.required(...(ngDevMode ? [{ debugName: "optionLabel" }] : /* istanbul ignore next */ []));
|
|
8707
8783
|
label = input('Seleccionar', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
@@ -8723,6 +8799,14 @@ class DsxAutocomplete {
|
|
|
8723
8799
|
get isDevMode() {
|
|
8724
8800
|
return this.isDevModeInternal;
|
|
8725
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
|
+
}
|
|
8726
8810
|
get formValue() {
|
|
8727
8811
|
return this.ngControl?.control?.value;
|
|
8728
8812
|
}
|
|
@@ -8773,11 +8857,19 @@ class DsxAutocomplete {
|
|
|
8773
8857
|
});
|
|
8774
8858
|
}
|
|
8775
8859
|
});
|
|
8860
|
+
// ✅ Effect mejorado para recargar cuando cambia el datasource
|
|
8776
8861
|
effect(() => {
|
|
8777
8862
|
const ds = this.datasource();
|
|
8778
|
-
if (ds.length > 0
|
|
8863
|
+
if (ds.length > 0) {
|
|
8779
8864
|
this.logDebug('📚 Datasource actualizado:', ds.length);
|
|
8780
|
-
|
|
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) {
|
|
8781
8873
|
this.logDebug('🔄 Recargando valor con nuevo datasource');
|
|
8782
8874
|
this.writeValue(this.ngControl.control.value);
|
|
8783
8875
|
}
|
|
@@ -8824,6 +8916,70 @@ class DsxAutocomplete {
|
|
|
8824
8916
|
this.lastLogTime = now;
|
|
8825
8917
|
console.log(`[DsxAutocomplete] ${message}`, data || '');
|
|
8826
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
|
+
}
|
|
8827
8983
|
searchRequisitos(event) {
|
|
8828
8984
|
const inicio = performance.now();
|
|
8829
8985
|
const label = this.optionLabel();
|
|
@@ -8862,13 +9018,10 @@ class DsxAutocomplete {
|
|
|
8862
9018
|
.replace(/\s+/g, ' ')
|
|
8863
9019
|
.trim();
|
|
8864
9020
|
}
|
|
8865
|
-
// ✨ CORREGIDO: evaluarYAgregar para manejar valores no encontrados
|
|
8866
9021
|
evaluarYAgregar(event) {
|
|
8867
9022
|
const valorInput = event.target.value?.trim();
|
|
8868
9023
|
if (!valorInput) {
|
|
8869
9024
|
this.dataFiltrada.set([]);
|
|
8870
|
-
// Si el input está vacío y hay valor seleccionado, mantenerlo
|
|
8871
|
-
// Si no hay valor, asegurar que el formulario tenga null o []
|
|
8872
9025
|
if (this.obtenerArrayValue().length === 0) {
|
|
8873
9026
|
this.actualizarFormulario([]);
|
|
8874
9027
|
}
|
|
@@ -8929,11 +9082,8 @@ class DsxAutocomplete {
|
|
|
8929
9082
|
this.logDebug('🆕 Nuevo elemento creado', valorInput);
|
|
8930
9083
|
}
|
|
8931
9084
|
else {
|
|
8932
|
-
// ✅ NO permite crear: limpiar el valor a null o []
|
|
8933
9085
|
this.logDebug('⚠️ No se encontraron coincidencias y no se permite crear', valorInput);
|
|
8934
|
-
// ✨ IMPORTANTE: Limpiar la selección a null o []
|
|
8935
9086
|
this.actualizarFormulario([]);
|
|
8936
|
-
// Marcar como touched para mostrar validación
|
|
8937
9087
|
if (this.ngControl?.control) {
|
|
8938
9088
|
this.ngControl.control.markAsTouched();
|
|
8939
9089
|
this.onTouched();
|
|
@@ -9059,9 +9209,7 @@ class DsxAutocomplete {
|
|
|
9059
9209
|
this.timeoutLimpieza = null;
|
|
9060
9210
|
}
|
|
9061
9211
|
}
|
|
9062
|
-
// ✨ MEJORADO: actualizarFormulario para manejar correctamente valores vacíos
|
|
9063
9212
|
actualizarFormulario(nuevaLista) {
|
|
9064
|
-
// Si la lista está vacía, siempre enviar null o [] según el modo
|
|
9065
9213
|
if (nuevaLista.length === 0) {
|
|
9066
9214
|
if (this.isMultiple()) {
|
|
9067
9215
|
this.value = [];
|
|
@@ -9077,7 +9225,6 @@ class DsxAutocomplete {
|
|
|
9077
9225
|
});
|
|
9078
9226
|
return;
|
|
9079
9227
|
}
|
|
9080
|
-
// Si hay elementos, procesar normalmente
|
|
9081
9228
|
if (this.isMultiple()) {
|
|
9082
9229
|
this.value = nuevaLista;
|
|
9083
9230
|
}
|
|
@@ -9096,6 +9243,7 @@ class DsxAutocomplete {
|
|
|
9096
9243
|
getFormControlName() {
|
|
9097
9244
|
return this.ngControl?.name || null;
|
|
9098
9245
|
}
|
|
9246
|
+
// ✨ MEJORADO: writeValue con manejo de IDs pendientes y logs
|
|
9099
9247
|
writeValue(value) {
|
|
9100
9248
|
const timestamp = new Date().toISOString();
|
|
9101
9249
|
this.logDebug(`🔄 writeValue INVOCADO ${timestamp}`, {
|
|
@@ -9113,10 +9261,16 @@ class DsxAutocomplete {
|
|
|
9113
9261
|
isMultiple: this.isMultiple(),
|
|
9114
9262
|
hasDatasource: this.datasource().length > 0,
|
|
9115
9263
|
datasourceSize: this.datasource().length,
|
|
9264
|
+
pendingIdsActuales: this.pendingIdsInternal,
|
|
9116
9265
|
});
|
|
9117
9266
|
if (value === null || value === undefined) {
|
|
9118
9267
|
this.logDebug(`⚠️ Valor null/undefined, limpiando selección`);
|
|
9119
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;
|
|
9120
9274
|
this.initialValueSet = true;
|
|
9121
9275
|
this.cdr.markForCheck();
|
|
9122
9276
|
this.cdr.detectChanges();
|
|
@@ -9127,6 +9281,11 @@ class DsxAutocomplete {
|
|
|
9127
9281
|
if (Array.isArray(value)) {
|
|
9128
9282
|
if (value.length === 0) {
|
|
9129
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;
|
|
9130
9289
|
this.initialValueSet = true;
|
|
9131
9290
|
this.cdr.markForCheck();
|
|
9132
9291
|
this.cdr.detectChanges();
|
|
@@ -9134,41 +9293,73 @@ class DsxAutocomplete {
|
|
|
9134
9293
|
}
|
|
9135
9294
|
if (typeof value[0] === 'number') {
|
|
9136
9295
|
ids = value;
|
|
9296
|
+
this.logDebug(`📊 Recibidos ${ids.length} IDs:`, ids);
|
|
9137
9297
|
}
|
|
9138
9298
|
else if (typeof value[0] === 'object' && value[0] !== null) {
|
|
9139
9299
|
items = value;
|
|
9300
|
+
this.logDebug(`📊 Recibidos ${items.length} objetos:`, items.map((i) => i[this.optionLabel()]));
|
|
9140
9301
|
}
|
|
9141
9302
|
}
|
|
9142
9303
|
else if (typeof value === 'number') {
|
|
9143
9304
|
ids = [value];
|
|
9305
|
+
this.logDebug(`📊 Recibido 1 ID:`, [value]);
|
|
9144
9306
|
}
|
|
9145
9307
|
else if (typeof value === 'object' && value !== null) {
|
|
9146
9308
|
items = [value];
|
|
9309
|
+
this.logDebug(`📊 Recibido 1 objeto:`, value[this.optionLabel()]);
|
|
9147
9310
|
}
|
|
9148
9311
|
else {
|
|
9149
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;
|
|
9150
9318
|
this.initialValueSet = true;
|
|
9151
9319
|
this.cdr.markForCheck();
|
|
9152
9320
|
this.cdr.detectChanges();
|
|
9153
9321
|
return;
|
|
9154
9322
|
}
|
|
9155
9323
|
const sourceData = this.datasource();
|
|
9156
|
-
|
|
9157
|
-
|
|
9158
|
-
|
|
9159
|
-
|
|
9160
|
-
|
|
9161
|
-
|
|
9162
|
-
|
|
9163
|
-
|
|
9164
|
-
|
|
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`);
|
|
9165
9340
|
const objetos = sourceData.filter((item) => ids.includes(Number(item[this.idKey()])));
|
|
9166
9341
|
const objetosOrdenados = ids
|
|
9167
9342
|
.map((id) => objetos.find((item) => Number(item[this.idKey()]) === id))
|
|
9168
9343
|
.filter((item) => item !== undefined);
|
|
9169
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
|
+
}
|
|
9170
9360
|
this.logDebug(`🔍 Encontrados ${items.length} de ${ids.length} IDs`);
|
|
9171
9361
|
}
|
|
9362
|
+
// ✅ Verificar que los items existan en el datasource
|
|
9172
9363
|
if (items.length > 0 && sourceData.length > 0) {
|
|
9173
9364
|
const itemsValidos = items.filter((item) => sourceData.some((s) => s[this.idKey()] === item[this.idKey()]));
|
|
9174
9365
|
if (itemsValidos.length !== items.length) {
|
|
@@ -9179,6 +9370,7 @@ class DsxAutocomplete {
|
|
|
9179
9370
|
items = itemsValidos;
|
|
9180
9371
|
}
|
|
9181
9372
|
}
|
|
9373
|
+
// ✅ Asignar el valor
|
|
9182
9374
|
if (this.isMultiple()) {
|
|
9183
9375
|
this.value = items;
|
|
9184
9376
|
}
|
|
@@ -9188,6 +9380,9 @@ class DsxAutocomplete {
|
|
|
9188
9380
|
this.initialValueSet = true;
|
|
9189
9381
|
this.cdr.detectChanges();
|
|
9190
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
|
+
}
|
|
9191
9386
|
}
|
|
9192
9387
|
forceReload() {
|
|
9193
9388
|
this.logDebug('🔄 Forzando recarga');
|
|
@@ -9214,7 +9409,7 @@ class DsxAutocomplete {
|
|
|
9214
9409
|
useExisting: forwardRef(() => DsxAutocomplete),
|
|
9215
9410
|
multi: true,
|
|
9216
9411
|
},
|
|
9217
|
-
], ngImport: i0, template: "<p-floatlabel variant=\"on\">\r\n <p-autoComplete\r\n fluid\r\n
|
|
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" }] });
|
|
9218
9413
|
}
|
|
9219
9414
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxAutocomplete, decorators: [{
|
|
9220
9415
|
type: Component,
|
|
@@ -9230,7 +9425,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
9230
9425
|
useExisting: forwardRef(() => DsxAutocomplete),
|
|
9231
9426
|
multi: true,
|
|
9232
9427
|
},
|
|
9233
|
-
], template: "<p-floatlabel variant=\"on\">\r\n <p-autoComplete\r\n fluid\r\n
|
|
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" }]
|
|
9234
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 }] }] } });
|
|
9235
9430
|
|
|
9236
9431
|
// dsx-select.component.ts
|