@jcbautistam/custom-table
Version:
Librería de tabla reutilizable con acciones, filtros, exportación y más.
671 lines • 230 kB
JavaScript
import { Component, Input, Output, EventEmitter, ViewChild, ElementRef, QueryList, ViewChildren, ViewEncapsulation } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { MatDialog } from '@angular/material/dialog';
import { fromEvent } from 'rxjs';
import { debounceTime, takeUntil } from 'rxjs/operators';
import { ConfiguracionParametros, ParametrosGenerales } from '../../Models/ParametrosGenerales';
import { ExportTableExcelService } from '../../Services/ExportTableExcel.service';
import { LoadingService } from '../../Services/loading.service';
import { ExportNotificationService } from '../../Services/exportNotification.service';
import { ColumnVisibilityModal2Component } from '../ColumnVisibility/column-visibility-modal2.component';
import * as i0 from "@angular/core";
import * as i1 from "../../Services/ExportTableExcel.service";
import * as i2 from "@angular/material/dialog";
import * as i3 from "../../Models/ParametrosGenerales";
import * as i4 from "../../Services/loading.service";
import * as i5 from "../../Services/exportNotification.service";
import * as i6 from "@angular/common";
import * as i7 from "@angular/forms";
import * as i8 from "@angular/router";
import * as i9 from "@angular/material/paginator";
import * as i10 from "@angular/material/table";
import * as i11 from "@angular/material/button";
import * as i12 from "@angular/material/icon";
import * as i13 from "ngx-bootstrap/datepicker";
import * as i14 from "@angular/material/checkbox";
import * as i15 from "@angular/material/slide-toggle";
import * as i16 from "@angular/material/tooltip";
import * as i17 from "@angular/material/progress-spinner";
import * as i18 from "../../Directives/ToUpperCase.directive";
import * as i19 from "../../Directives/OnlyDigit.directive";
import * as i20 from "../../pipes/onlyDigit.pipe";
import * as i21 from "../../pipes/string-truncate.pipe";
import * as i22 from "../../pipes/image.pipe";
export class CustomTableComponent {
get columnKeys() {
return Object.keys(this.columnConfigs);
}
constructor(excelService, dialog, configParams, loadingService, exportNotificationService) {
this.excelService = excelService;
this.dialog = dialog;
this.configParams = configParams;
this.loadingService = loadingService;
this.exportNotificationService = exportNotificationService;
// Objeto para manejar la cancelación de suscripciones de Observable
this.destroy$ = new EventEmitter();
// Variables para el control de la exportación
this.exportSubscription = null;
this.isExportInProgress = false;
this.maxDate = new Date(); // Fecha máxima hoy
this.minDate = new Date(); // Fecha mínima 30 días atrás
this.totalRecords = 0;
this.formularioModificado = false;
this.isSelectAll = false;
this.originalData = []; // Una copia de los datos originales para restablecer después del filtrado
this.dataActives = [];
// Esto almacenará el filtro actual para cada columna.
this.filters = {};
this.dataSource = new MatTableDataSource();
this.displayedColumns = [];
this.styleRow = {};
this.isToggleChecked = true;
//Se reciben los datos que mostrará la tabla
this.data = [];
//Se reciben los datos que mostrará en campo editable select
// @Input() dataSelect: any[] = [];
this.dataSelect = [];
this.withHeader = true;
//Se reciben configuraciones generales de la tabla como datos de paginacion y fontsize
this.tableConfigs = {};
//Se recibe el nombre del menu para aplicar el correcto permiso por usuario
this.nombreMenu = '';
//Se recibe el titulo de la tabla
this.nombreTabla = '';
//Se recibe la ruta a la que navegara cuando se use el boton [crear] propio de la tabla
this.crearRoute = '';
//Se recibe la indicacion sobre si se debe mostrar el boton [crear] normal
this.showCreateButton = false;
//Se recibe la indicacion sobre si se debe mostrar el boton [crear] normal
this.showCreateButtonModal = false;
//Se recibe la indicacion sobre si se debe mostrar el boton [exportar a excel] que descarga un archivo excel
this.showExportarButton = true;
//Se recibe el nombre con el que se debe guardar el archivo excel al exportar la tabla desde el boton [exportar a excel]
this.excelFileName = '';
//Indica si esta "cargando" datos, y asi manejar la visualizacion del indicador loader
this.isLoading = false;
//Se recibe la indicacion sobre si se debe mostrar el boton [crear] cuando es un catalogoGeneral
this.showCreateButtonDetGral = false;
//Se recibe la indicacion sobre si se debe mostrar el boton [refrescar datos] que refresca los datos de la tabla
this.showRefreshButton = true;
this.showAddButton = false;
//Se recibe la indicacion sobre si se debe mostrar el boton [configuracion de columnas] que abre el modal para elegir que columnas visualizar
this.showConfColumnsButton = true;
//Se recibe la indicacion sobre si se debe mostrar el boton [incluir inactivos] que incluye los registros que estan en estatus de inactivo
this.showFilterInactivos = true;
//Se recibe un arreglo de configuracion de boton de accion, permite saber que botons de acciones debe renderizar
this.actions = [];
this.resaltarSeleccion = false;
this.mostrarFiltroGeneralFechas = false;
this.showSelectAll = false;
// Aquí defines el mínimo de columnas requeridas
this.columnsReqMin = 3;
this.isReport = false;
this.parametros = new ParametrosGenerales();
this.extraParams = {}; // Objeto con parámetros dinámicos
//Nueva propiedad para centrar la tabla automáticamente
this.centerTable = true;
//Nueva propiedad para controlar la altura de la tabla
this.tableHeight = 'auto'; // Opción para forzar una altura específica si se necesita
//Propiedad para controlar la visibilidad del gran total
this.showGranTotal = false;
this.widthColumn = '20%';
//Outputs para emitir eventos al padre
this.onSelectAll = new EventEmitter();
this.cambiarEstatusEvent = new EventEmitter();
this.createEvent = new EventEmitter();
this.refreshEvent = new EventEmitter();
this.AddEvent = new EventEmitter();
this.onCreate = new EventEmitter();
this.enviarItemEvent = new EventEmitter();
this.filtroFechaGeneralEvent = new EventEmitter();
this.changeData = new EventEmitter();
this.filtroFechaReportEvent = new EventEmitter();
// Definir los rangos de fechas
this.predefinedRanges = [
{ label: 'Hoy', value: [new Date(), new Date()] },
{
label: 'Últimos 7 Días',
value: [
new Date(new Date().setDate(new Date().getDate() - 6)),
new Date(),
],
},
];
//Definir configuracion inicial para DateRangePicker
this.bsConfig = {
containerClass: 'theme-orange',
dateInputFormat: 'DD/MM/YYYY',
isAnimated: true,
};
this.applyFilter = (event, column) => {
this.parametros.filtrosPorColumna = {
...this.parametros.filtrosPorColumna,
[column]: event.target.value,
};
this.parametros['noPagina'] = 1;
this.parametros['tamanoPagina'] = 10;
this.loadData();
// Asegurar que el paginador se sincroniza
this.paginator.pageIndex = 0;
this.paginator.length = this.totalRecords;
};
this.applyFilterActives = (value) => {
this.parametros.activos = !value;
this.parametros['noPagina'] = 1;
this.parametros['tamanoPagina'] = 10;
this.loadData();
// Asegurar que el paginador se sincroniza
this.paginator.pageIndex = 0;
this.paginator.length = this.totalRecords;
};
this.updateData = () => {
this.isLoading = true;
this.loadData();
};
this.refreshTable = () => {
this.filterInputs.forEach((input) => {
input.nativeElement.value = '';
});
this.parametros.filtrosPorColumna = {};
this.parametros['noPagina'] = 1;
this.parametros['tamanoPagina'] = 10;
this.loadData();
// Asegurar que el paginador se sincroniza
this.paginator.pageIndex = 0;
this.paginator.length = this.totalRecords;
// Verificar overflow después de que los datos se actualizan
setTimeout(() => this.checkForHorizontalOverflow(), 300);
};
this.loadData = () => {
if (this.formularioModificado) {
this.originalData = [];
this.dataSource.data = [];
this.isLoading = true;
const params = this.configParams.configurar(this.parametros);
this.fetchDataFunction(params, this.extraParams).subscribe((data) => {
const items = data.items;
this.data = items;
this.totalRecords = data.totalRecords;
this.dataSource.data = this.data;
this.isLoading = false;
// Estilos predeterminados o configuraciones
this.styleRow = {
height: this.tableConfigs.heightRow,
};
// Inicializa originalData de forma segura
if (Array.isArray(this.data)) {
this.originalData = [...this.data];
}
else {
this.originalData = []; // O maneja como veas conveniente
}
if (this.isReport) {
this.bsConfigGeneral = {
containerClass: 'theme-orange',
dateInputFormat: 'DD/MM/YYYY',
isAnimated: true,
maxDate: this.maxDate,
};
}
// Recalcular dimensiones después de cargar datos
setTimeout(() => {
this.checkForHorizontalOverflow();
this.adjustTableHeight();
}, 100);
});
}
};
this.minDate.setDate(this.minDate.getDate() - 30);
this.bsConfigGeneral = {
minDate: this.minDate,
maxDate: this.maxDate,
containerClass: 'theme-orange',
dateInputFormat: 'DD/MM/YYYY',
isAnimated: true,
};
}
// En tu componente o servicio
getColumnConfigWithDefault(columnKey) {
const defaultConfig = {
displayName: '',
type: 'default',
showFilter: true,
visible: true,
};
const columnConfig = this.columnConfigs[columnKey];
if (columnConfig) {
// Combinar la configuración de columna existente con la configuración predeterminada
return { ...defaultConfig, ...columnConfig };
}
else {
// Si la configuración de columna no está definida, devolver la configuración predeterminada
return defaultConfig;
}
}
// Función para determinar si se debe bloquear el renglón
shouldBlockRow(element) {
for (const columnKey of Object.keys(this.columnConfigs)) {
const columnConfig = this.getColumnConfigWithDefault(columnKey);
if (columnConfig.bloquearSeleccion &&
columnConfig.bloquearSeleccion(element)) {
return true; // Se bloquea el renglón si se cumple la condición en alguna columna
}
}
return false; // No se bloquea el renglón si no se encuentra ninguna condición de bloqueo
}
//Abre modal para configurar que columnas configure para renderizar
openColumnVisibilityDialog() {
const columnsArray = Object.keys(this.columnConfigs)
.filter((key) => key !== 'activo')
.map((key) => ({
key: key,
...this.columnConfigs[key],
}));
const dialogRef = this.dialog.open(ColumnVisibilityModal2Component, {
width: '250px',
data: { columns: columnsArray, columnsReqMin: this.columnsReqMin }, // Pasamos el mínimo de columnas
});
dialogRef.afterClosed().subscribe((result) => {
if (result) {
// Actualizar columnConfigs con los resultados
result.forEach((column) => {
this.columnConfigs[column.key].visible = column.visible;
});
this.updateDisplayedColumns();
}
});
}
//Actualiza el arreglo de columnas a renderizar
updateDisplayedColumns() {
/* if (this.columnConfigs.activo !== undefined)
this.columnConfigs.activo.visible = false; */
const visibleColumns = Object.keys(this.columnConfigs).filter((key) => this.columnConfigs[key].visible);
if (this.actions.length === 0) {
this.displayedColumns = [...visibleColumns];
}
else {
this.displayedColumns = [...visibleColumns, 'acciones'];
}
}
onValueChangeDateGeneral(value) {
if (value &&
value.length === 2 &&
value[0] instanceof Date &&
value[1] instanceof Date) {
this.filtroFechaGeneralEvent.emit(value);
}
if (value && value[0] instanceof Date && value[1] instanceof Date) {
this.parametros.rangoFechas =
this.fechaToString(value[0]) + '-' + this.fechaToString(value[1]);
}
this.parametros['noPagina'] = 1;
this.parametros['tamanoPagina'] = 10;
this.loadData();
// Asegurar que el paginador se sincroniza
this.paginator.pageIndex = 0;
this.paginator.length = this.totalRecords;
}
//Ejecuta al cambiar el valor del DataRangePicker
onValueChangeDate(value, column = 'fechaCreacion') {
if (value == undefined) {
this.resetDateFilter(column);
}
console.log('fecha ingresasa: ', value, column);
if (value &&
value.length === 2 &&
value[0] instanceof Date &&
value[1] instanceof Date) {
this.applyDateFilter(value[0], value[1], column);
}
else {
this.resetDateFilter(column);
}
}
ngAfterViewInit() {
this.formularioModificado = true;
// Cargar datos iniciales
this.loadData();
// Ajustar el tamaño de la tabla al espacio disponible
this.adjustTableHeight();
// Observar cambios en el tamaño de la ventana
fromEvent(window, 'resize')
.pipe(debounceTime(200), takeUntil(this.destroy$))
.subscribe(() => {
this.checkForHorizontalOverflow();
this.adjustTableHeight();
});
// Verificar si hay overflow horizontal
this.checkForHorizontalOverflow();
// Detectar cuando hay espacio vacío después del último registro
this.checkEmptySpace();
// Observar cambios en los datos para actualizar el estado de espacio vacío
this.dataSource.connect().subscribe(() => {
this.checkEmptySpace();
});
}
ngOnInit() {
this.updateDisplayedColumns();
// Inicialización de filtros
this.filters = {};
for (const column of Object.keys(this.columnConfigs)) {
this.filters[column] = '';
}
// Suscribirse al estado de exportación
this.exportSubscription = this.exportNotificationService.exportStatus$
.pipe(takeUntil(this.destroy$))
.subscribe(status => {
this.isExportInProgress = status.isExporting;
});
}
ngOnChanges(changes) {
if ((changes['extraParams'] && this.extraParams) ||
(changes['parametros'] && this.parametros)) {
// Cuando itemDefault cambia, asignamos sus valores al form
this.loadData();
}
}
columnMatches(row) {
for (const column of Object.keys(this.columnConfigs)) {
const columnName = column;
let filter = this.filters[columnName];
if (filter === null) {
continue;
}
if (filter !== null && filter !== undefined) {
if (typeof filter === 'boolean') {
if (row[columnName] !== filter) {
return false;
}
}
else {
let value = String(row[columnName]).toLowerCase();
if (!value.includes(String(filter).toLowerCase())) {
return false;
}
}
}
}
return true;
}
applyDateFilter(startDate, endDate, column) {
this.parametros.filtrosPorColumna = {
...this.parametros.filtrosPorColumna,
[column]: this.fechaToString(startDate) + '-' + this.fechaToString(endDate),
};
this.parametros['noPagina'] = 1;
this.parametros['tamanoPagina'] = 10;
this.loadData();
// Asegurar que el paginador se sincroniza
this.paginator.pageIndex = 0;
this.paginator.length = this.totalRecords;
}
resetDateFilter(column) {
this.parametros.filtrosPorColumna = {
...this.parametros.filtrosPorColumna,
[column]: '',
};
this.parametros['noPagina'] = 1;
this.parametros['tamanoPagina'] = 10;
this.loadData();
// Asegurar que el paginador se sincroniza
this.paginator.pageIndex = 0;
this.paginator.length = this.totalRecords;
}
fechaToString(fecha) {
const day = String(fecha.getDate()).padStart(2, '0');
const month = String(fecha.getMonth() + 1).padStart(2, '0'); // Los meses en JS son base 0
const year = fecha.getFullYear();
// Formatear la fecha en dd/mm/yyyy
const formattedDate = `${day}/${month}/${year}`;
return formattedDate;
}
applyBooleanFilter(value, column) {
value
? (this.parametros.filtrosPorColumna = {
...this.parametros.filtrosPorColumna,
[column]: 'true',
})
: delete this.parametros.filtrosPorColumna[column];
this.parametros['noPagina'] = 1;
this.parametros['tamanoPagina'] = 10;
this.loadData();
// Asegurar que el paginador se sincroniza
this.paginator.pageIndex = 0;
this.paginator.length = this.totalRecords;
}
enviarItem(item) {
this.rowSeleccionada = item;
this.enviarItemEvent.emit(item);
}
agregarRegistro() {
this.AddEvent.emit();
}
cambiarEstatus(item) {
this.cambiarEstatusEvent.emit(item);
}
exportTable() {
// Iniciar la notificación de exportación y obtener el ID del trabajo
const jobId = this.exportNotificationService.startExport(this.excelFileName);
// Preparar parámetros para la consulta completa
const exportParams = { ...this.parametros };
// En lugar de usar 0, usamos el número total de registros
exportParams['noPagina'] = 1;
exportParams['tamanoPagina'] = this.totalRecords > 0 ? this.totalRecords : 10000;
// Configurar los parámetros de la consulta
const params = this.configParams.configurar(exportParams);
this.exportNotificationService.updateProgress(jobId, 25);
// Obtener las columnas a exportar (excluyendo 'acciones')
let displayColumns = this.actions.length > 0
? this.displayedColumns.filter(d => d != 'acciones')
: this.displayedColumns;
// Realizar la consulta al endpoint
this.fetchDataFunction(params, this.extraParams).subscribe({
next: (data) => {
this.exportNotificationService.updateProgress(jobId, 50);
// Transformar datos para usar nombres de visualización como cabeceras
const transformedData = data.items.map((item) => {
const transformedItem = {};
displayColumns.forEach((title) => {
if (this.columnConfigs[title].type != 'image') {
transformedItem[this.columnConfigs[title].displayName] = item[title];
}
});
return transformedItem;
});
this.exportNotificationService.updateProgress(jobId, 75);
// Exportar los datos completos
this.excelService.exportToExcel('catalogoPrincipal', this.excelFileName, transformedData);
// Completar la exportación
this.exportNotificationService.completeExport(jobId);
},
error: (error) => {
console.error('Error al exportar datos:', error);
this.exportNotificationService.errorExport(jobId, error);
}
});
}
onValueChangeDateReport(value) {
this.filtroFechaReportEvent.emit(value);
}
create() {
this.onCreate.emit();
this.createEvent.emit();
}
get isDataEmpty() {
return (this.dataSource && this.dataSource.data.length === 0 && !this.isLoading);
}
//Funcion para agregar estilos a los registros eliminados
shouldHideText(row) {
// Lógica para decidir si ocultar el texto
return row.activo === false; // Por ejemplo, oculta el texto para los registros no activos
}
onPageChange(event) {
this.parametros['noPagina'] = event.pageIndex + 1;
this.parametros['tamanoPagina'] = event.pageSize;
this.loadData();
// Scroll al inicio de la tabla al cambiar de página
if (this.matTableWrapper && this.matTableWrapper.nativeElement) {
const tableContent = this.matTableWrapper.nativeElement.querySelector('.table-v2-content');
if (tableContent) {
tableContent.scrollTop = 0;
}
}
}
onCheckSelectAll(event) {
this.isSelectAll = event.checked;
this.onSelectAll.emit(event);
}
/** Opciones de tamaño de página transformadas */
transformedPageSizeOptions(options) {
return options.map((size) => (size === 5 ? 10 : size));
}
/**
* Verifica si hay overflow horizontal en la tabla y aplica clase correspondiente
*/
checkForHorizontalOverflow() {
if (!this.matTableWrapper)
return;
setTimeout(() => {
const tableContent = this.matTableWrapper.nativeElement.querySelector('.table-v2-content');
if (!tableContent)
return;
const hasOverflow = tableContent.scrollWidth > tableContent.clientWidth;
if (hasOverflow) {
tableContent.classList.add('has-overflow');
}
else {
tableContent.classList.remove('has-overflow');
}
}, 300);
}
// Método para ajustar la altura de la tabla dinámicamente
adjustTableHeight() {
if (!this.matTableWrapper)
return;
setTimeout(() => {
try {
// Reiniciar overflow en el body
document.body.style.overflowX = 'hidden';
document.documentElement.style.overflowX = 'hidden';
// Referencias a los elementos relevantes
const tableContainer = this.matTableWrapper.nativeElement.closest('.table-v2-container');
if (!tableContainer)
return;
// Obtener el elemento del footer global de la aplicación
const appFooter = document.querySelector('footer') ||
document.querySelector('.app-footer') ||
document.querySelector('.main-footer');
// Dimensiones del viewport
const viewportHeight = window.innerHeight;
const viewportWidth = window.innerWidth;
// Posición del contenedor de la tabla
const tableRect = tableContainer.getBoundingClientRect();
const tableTop = tableRect.top;
// Calcular altura disponible según la posición del footer o el viewport
let availableHeight;
if (appFooter) {
// Si existe un footer, calcular basado en su posición
const footerRect = appFooter.getBoundingClientRect();
const footerTop = footerRect.top;
// Calcular la distancia entre la parte superior de la tabla y el footer
// y restar un margen de seguridad
availableHeight = footerTop - tableTop - 40;
}
else {
// Si no hay footer, usar el viewport menos un margen para la parte inferior
availableHeight = viewportHeight - tableTop - 40;
}
// Garantizar una altura mínima para la tabla
const minHeight = 400;
const finalHeight = Math.max(availableHeight, minHeight);
// Aplicar la altura calculada al contenedor de la tabla
tableContainer.style.height = `${finalHeight}px`;
// Ajustar el contenido de la tabla según headers y footers
const tableHeader = tableContainer.querySelector('.toolbar');
const tableFooter = tableContainer.querySelector('.sticky-footer');
// Calcular las alturas de los elementos
const headerHeight = tableHeader ? tableHeader.offsetHeight : 0;
const footerHeight = tableFooter ? tableFooter.offsetHeight : 0;
// Obtener referencia al contenido de la tabla
const tableContent = tableContainer.querySelector('.table-v2-content');
if (tableContent) {
// Calcular el espacio disponible para el contenido
const contentHeight = finalHeight - headerHeight - footerHeight;
tableContent.style.height = `${contentHeight}px`;
// Ajustar la tabla interior para manejar scroll horizontal
setTimeout(() => {
const table = tableContent.querySelector('table.mat-table');
if (table) {
// Verificar si se necesita scroll horizontal
const tableWidth = table.scrollWidth;
const contentWidth = tableContent.clientWidth;
if (tableWidth > contentWidth) {
// La tabla es más ancha que el contenedor, activar indicador visual de scroll
tableContent.classList.add('has-overflow');
tableContent.style.overflowX = 'scroll';
// Asegurar que la tabla sea lo suficientemente ancha
table.style.width = 'max-content';
table.style.minWidth = '100%';
}
else {
// No se necesita scroll horizontal
tableContent.classList.remove('has-overflow');
// La tabla cabe en el contenedor, ajustar al 100%
table.style.width = '100%';
}
}
}, 100);
}
// Asegurar que el footer sea visible
if (tableFooter) {
tableFooter.style.position = 'sticky';
tableFooter.style.bottom = '0';
tableFooter.style.zIndex = '100';
}
}
catch (error) {
console.error('Error al ajustar dimensiones de tabla:', error);
}
}, 200);
}
/**
* Verifica si hay espacio vacío después del último registro y aplica la clase correspondiente
*/
checkEmptySpace() {
setTimeout(() => {
if (!this.matTableWrapper)
return;
const tableElement = this.matTableWrapper.nativeElement;
const tableHeight = tableElement.offsetHeight;
const tableContentHeight = this.calculateContentHeight();
const tableContainer = tableElement.closest('.table-v2-content');
if (tableContainer && tableHeight < tableContainer.offsetHeight * 0.8) {
// Si el contenido de la tabla ocupa menos del 80% del contenedor, hay espacio vacío
tableElement.classList.add('has-empty-space');
}
else {
tableElement.classList.remove('has-empty-space');
}
});
}
/**
* Calcula la altura del contenido de la tabla (filas)
*/
calculateContentHeight() {
if (!this.matTableWrapper)
return 0;
const rows = this.matTableWrapper.nativeElement.querySelectorAll('tr.mat-row');
let totalHeight = 0;
rows.forEach((row) => {
totalHeight += row.offsetHeight;
});
return totalHeight;
}
ngOnDestroy() {
// Emitir evento para cancelar todas las suscripciones
this.destroy$.emit();
this.destroy$.complete();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: CustomTableComponent, deps: [{ token: i1.ExportTableExcelService }, { token: i2.MatDialog }, { token: i3.ConfiguracionParametros }, { token: i4.LoadingService }, { token: i5.ExportNotificationService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.2", type: CustomTableComponent, selector: "custom-table", inputs: { data: "data", dataSelect: "dataSelect", columnConfigs: "columnConfigs", withHeader: "withHeader", tableConfigs: "tableConfigs", nombreMenu: "nombreMenu", nombreTabla: "nombreTabla", crearRoute: "crearRoute", showCreateButton: "showCreateButton", showCreateButtonModal: "showCreateButtonModal", showExportarButton: "showExportarButton", excelFileName: "excelFileName", showCreateButtonDetGral: "showCreateButtonDetGral", showRefreshButton: "showRefreshButton", showAddButton: "showAddButton", showConfColumnsButton: "showConfColumnsButton", showFilterInactivos: "showFilterInactivos", actions: "actions", resaltarSeleccion: "resaltarSeleccion", mostrarFiltroGeneralFechas: "mostrarFiltroGeneralFechas", showSelectAll: "showSelectAll", columnsReqMin: "columnsReqMin", isReport: "isReport", fetchDataFunction: "fetchDataFunction", parametros: "parametros", extraParams: "extraParams", centerTable: "centerTable", tableHeight: "tableHeight", showGranTotal: "showGranTotal", widthColumn: "widthColumn" }, outputs: { onSelectAll: "onSelectAll", cambiarEstatusEvent: "cambiarEstatusEvent", createEvent: "createEvent", refreshEvent: "refreshEvent", AddEvent: "AddEvent", onCreate: "onCreate", enviarItemEvent: "enviarItemEvent", filtroFechaGeneralEvent: "filtroFechaGeneralEvent", changeData: "changeData", filtroFechaReportEvent: "filtroFechaReportEvent" }, viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "matTableWrapper", first: true, predicate: ["matTableWrapper"], descendants: true }, { propertyName: "filterInputs", predicate: ["filterInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"table-v2-container\" [ngClass]=\"{ 'table-v2-centered': centerTable }\">\r\n <div *ngIf=\"withHeader\" class=\"toolbar\">\r\n <div class=\"left-buttons\">\r\n <button *ngIf=\"showCreateButton\" mat-raised-button [routerLink]=\"crearRoute\" class=\"btn-tabla-centrado\"\r\n matTooltip=\"Nuevo\" matTooltipPosition=\"above\"\r\n matTooltipShowDelay=\"150\">\r\n Crear\r\n </button>\r\n <button *ngIf=\"showCreateButtonDetGral\" mat-raised-button (click)=\"create()\" class=\"btn-tabla-centrado\"\r\n matTooltip=\"Nuevo\" matTooltipPosition=\"above\"\r\n matTooltipShowDelay=\"150\">\r\n Crear\r\n </button>\r\n\r\n <button *ngIf=\"showCreateButtonModal\" mat-raised-button (click)=\"tableConfigs.createCallback()\"\r\n class=\"btn-tabla-centrado\" matTooltip=\"Nuevo\"\r\n matTooltipPosition=\"above\" matTooltipShowDelay=\"150\">\r\n Crear\r\n </button>\r\n\r\n <input *ngIf=\"mostrarFiltroGeneralFechas\" #filterInput class=\"form-control-date\" bsDaterangepicker\r\n placeholder=\"Filtrar por fecha\" type=\"text\" [bsConfig]=\"bsConfigGeneral\"\r\n (bsValueChange)=\"onValueChangeDateGeneral($event)\" />\r\n\r\n <input *ngIf=\"isReport\" #filterInput class=\"form-control-date\" bsDatepicker placeholder=\"Filtrar por fecha\"\r\n type=\"text\" [bsConfig]=\"bsConfigGeneral\" (bsValueChange)=\"onValueChangeDateReport($event)\" />\r\n\r\n <div *ngIf=\"showSelectAll\" class=\"checkbox-container\">\r\n <mat-checkbox [disabled]=\"false\" (change)=\"onCheckSelectAll($event)\">Todos</mat-checkbox>\r\n </div>\r\n </div>\r\n\r\n <div class=\"table-title\">\r\n <span>{{ nombreTabla }}</span>\r\n </div>\r\n\r\n <div class=\"right-buttons\">\r\n <style>\r\n .toggle-color-amarillo.mat-mdc-slide-toggle.mat-mdc-slide-toggle-checked .mdc-switch__thumb {\r\n background-color: var(--color-select-toggle) !important;\r\n }\r\n\r\n .toggle-color-amarillo.mat-mdc-slide-toggle.mat-mdc-slide-toggle-checked .mdc-switch__track {\r\n background-color: var(--color-select-toggle-transparente) !important;\r\n }\r\n\r\n .toggle-color-amarillo.mat-slide-toggle.mat-checked .mat-slide-toggle-thumb {\r\n background-color: var(--color-select-toggle) !important;\r\n }\r\n\r\n .toggle-color-amarillo.mat-slide-toggle.mat-checked .mat-slide-toggle-bar {\r\n background-color: var(--color-select-toggle-transparente) !important;\r\n }\r\n </style>\r\n <mat-slide-toggle *ngIf=\"showFilterInactivos\" (change)=\"applyFilterActives($event.checked)\"\r\n matTooltip=\"Incluir eliminados\" matTooltipPosition=\"above\" matTooltipShowDelay=\"150\"\r\n class=\"toggle-color-amarillo\">\r\n </mat-slide-toggle>\r\n <button *ngIf=\"showExportarButton\" mat-raised-button class=\"btn btn-secondary btn-tabla-centrado\"\r\n (click)=\"exportTable()\" [disabled]=\"isExportInProgress\"\r\n matTooltip=\"{{isExportInProgress ? 'Exportaci\u00F3n en progreso...' : 'Exportar a excel'}}\"\r\n matTooltipPosition=\"above\" matTooltipShowDelay=\"150\">\r\n <mat-icon>{{isExportInProgress ? 'hourglass_empty' : 'file_download'}}</mat-icon>\r\n </button>\r\n\r\n <button *ngIf=\"showRefreshButton\" mat-raised-button class=\"refresh-button btn-tabla-centrado\"\r\n (click)=\"refreshTable()\" matTooltip=\"Refrescar tabla\" matTooltipPosition=\"above\"\r\n matTooltipShowDelay=\"150\">\r\n <mat-icon>refresh</mat-icon>\r\n </button>\r\n\r\n <button *ngIf=\"showAddButton\" mat-raised-button class=\"refresh-button btn-tabla-centrado\"\r\n (click)=\"agregarRegistro()\" matTooltip=\"Agregar registro\" matTooltipPosition=\"above\"\r\n matTooltipShowDelay=\"150\">\r\n <mat-icon>add</mat-icon>\r\n </button>\r\n\r\n <button *ngIf=\"showConfColumnsButton\" (click)=\"openColumnVisibilityDialog()\" mat-raised-button\r\n class=\"btn btn-secondary settings-button btn-tabla-centrado\" matTooltip=\"Configurar columnas\"\r\n matTooltipPosition=\"above\" matTooltipShowDelay=\"150\">\r\n <mat-icon>settings</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div class=\"table-v2-wrapper\" #matTableWrapper>\r\n <div class=\"table-v2-content\">\r\n <mat-table id=\"catalogoPrincipal\" mat-table [dataSource]=\"dataSource\" class=\"mat-elevation-z0\">\r\n <!-- Columnas Din\u00E1micas -->\r\n <ng-container *ngFor=\"let column of columnKeys\" [matColumnDef]=\"column\">\r\n <th mat-header-cell *matHeaderCellDef sticky\r\n [ngClass]=\"{'columna-id': column === 'id' || column === 'Id'}\"\r\n [ngStyle]=\"{ width: widthColumn }\">\r\n <div style=\"display: flex; flex-direction: column;\">\r\n <div>{{ columnConfigs[column]?.displayName || column }}</div>\r\n <ng-container *ngIf=\"columnConfigs[column]?.showFilter\">\r\n <ng-container *ngIf=\"\r\n columnConfigs[column]?.type == 'default' ||\r\n columnConfigs[column]?.type == 'default-truncate'\r\n \">\r\n <input #filterInput class=\"form-control form-control-sm\"\r\n [ngClass]=\"{'filtro-width': column === 'id' || column === 'Id'}\" type=\"text\"\r\n (change)=\"applyFilter($event, column)\" placeholder=\"Filtrar\" />\r\n </ng-container>\r\n <ng-container *ngIf=\"columnConfigs[column]?.type == 'number'\">\r\n <input #filterInput class=\"form-control form-control-sm\"\r\n [ngClass]=\"{'filtro-width': column === 'id' || column === 'Id'}\" type=\"text\"\r\n (change)=\"applyFilter($event, column)\" placeholder=\"Filtrar\" />\r\n </ng-container>\r\n <ng-container *ngIf=\"columnConfigs[column]?.type == 'date'\">\r\n <input #filterInput style=\"width: 100%\" bsDaterangepicker placeholder=\"Filtrar\"\r\n class=\"form-control form-control-sm\" type=\"text\" [bsConfig]=\"bsConfig\"\r\n (bsValueChange)=\"onValueChangeDate($event, column)\" />\r\n </ng-container>\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'boolean'\">\r\n <mat-slide-toggle [checked]=\"columnConfigs[column]?.iniFilterBool\"\r\n (change)=\"applyBooleanFilter($event.checked ? true : null, column)\">\r\n </mat-slide-toggle>\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n </th>\r\n\r\n <td mat-cell *matCellDef=\"let element\" [ngClass]=\"{\r\n 'disabled-record': shouldBlockRow(element),\r\n registroEliminado: shouldHideText(element)\r\n }\" (click)=\"!shouldBlockRow(element) ? enviarItem(element) : null\">\r\n <div *ngIf=\"!isLoading && !isDataEmpty\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'boolean'\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.editable; else elseBlock\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.event; else elseEvent\">\r\n <mat-checkbox [(ngModel)]=\"element[column]\"\r\n (change)=\"columnConfigs[column]?.functionEvent(element)\">\r\n </mat-checkbox>\r\n </ng-container>\r\n\r\n <ng-template #elseEvent>\r\n <mat-checkbox [(ngModel)]=\"element[column]\"> </mat-checkbox>\r\n </ng-template>\r\n </ng-container>\r\n <ng-template #elseBlock>\r\n {{\r\n element[column]\r\n ? columnConfigs[column].trueValue\r\n : columnConfigs[column].falseValue\r\n }}\r\n </ng-template>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'date'\">\r\n <span class=\"text-uppercase\">{{ element[column] | date : \"dd/MMM/yyyy\" }}</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'date-time'\">\r\n <span class=\"text-uppercase\">{{ element[column] | date : \"dd/MMM/yyyy HH:MM\" }}</span>\r\n </ng-container>\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'default'\">\r\n <ng-container *ngIf=\"\r\n columnConfigs[column]?.customRender;\r\n else elseBlockDefault\r\n \">\r\n <span\r\n [ngClass]=\"{'text-uppercase': column === 'fechaCita' || columnConfigs[column]?.type === 'date'}\">{{\r\n columnConfigs[column]?.customRender(element)\r\n }}</span>\r\n </ng-container>\r\n <ng-template #elseBlockDefault>\r\n <ng-container\r\n *ngIf=\"columnConfigs[column]?.editable; else elseBlockDefaultEditable\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.event; else elseEventDefault\">\r\n <input type=\"text\" [(ngModel)]=\"element[column]\" style=\"width: 100%\"\r\n toUpperCase (blur)=\"columnConfigs[column]?.functionEvent(element)\" />\r\n </ng-container>\r\n <ng-template #elseEventDefault>\r\n <input type=\"text\" [(ngModel)]=\"element[column]\" style=\"width: 100%\"\r\n toUpperCase />\r\n </ng-template>\r\n </ng-container>\r\n <ng-template #elseBlockDefaultEditable>\r\n {{ element[column] }}\r\n </ng-template>\r\n </ng-template>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'number'\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.editable; else elseBlock\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.event; else elseEvent\">\r\n <input type=\"text\" [(ngModel)]=\"element[column]\"\r\n style=\"width: 100%; text-align: right\" (keydown.enter)=\"\r\n columnConfigs[column]?.functionEvent(element)\r\n \" onlyDigit=\"{{ columnConfigs[column]?.char }}\" />\r\n </ng-container>\r\n <ng-template #elseEvent>\r\n <input type=\"text\" [(ngModel)]=\"element[column]\"\r\n style=\"width: 100%; text-align: right\"\r\n onlyDigit=\"{{ columnConfigs[column]?.char }}\" />\r\n </ng-template>\r\n </ng-container>\r\n\r\n <ng-template #elseBlock>\r\n <p class=\"text-align-right\" *ngIf=\"!element.isEditing\">\r\n {{\r\n element[column] | onlyDigit : columnConfigs[column]?.char\r\n }}\r\n </p>\r\n </ng-template>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'default-truncate'\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.customRender; else elseBlock\">\r\n <span matTooltip=\"{{ element[column] }}\">{{\r\n columnConfigs[column]?.customRender(element) | truncate : 10\r\n }}</span>\r\n </ng-container>\r\n <ng-template #elseBlock>\r\n <span matTooltip=\"{{ element[column] }}\">{{\r\n element[column] | truncate : 10\r\n }}</span>\r\n </ng-template>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'percent'\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.customRender; else elseBlock\">\r\n <span matTooltip=\"{{ element[column] | percent }}\">{{\r\n columnConfigs[column]?.customRender(element) | percent\r\n }}</span>\r\n </ng-container>\r\n <ng-template #elseBlock>\r\n <span matTooltip=\"{{ element[column] | percent }}\">{{\r\n element[column] | percent\r\n }}</span>\r\n </ng-template>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'image'\">\r\n <img [src]=\"element[column] | image\" alt=\"Foto gafete\" style=\"width:100px !important\">\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'select'\">\r\n <select [(ngModel)]=\"element[column]\" style=\"width: 100%; height: 25px\"\r\n disabled=\"{{ !columnConfigs[column]?.editable }}\">\r\n <option *ngFor=\"\r\n let item of dataSelect[\r\n columnConfigs[column]?.positionDataSelect\r\n ]\r\n \" [value]=\"item.id\">\r\n {{ item.nombre }}\r\n </option>\r\n </select>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"!columnConfigs[column]\">\r\n {{ element[column] }}\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"columnConfigs[column]?.type === 'dinamic'\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.editable; else elseBlock\">\r\n <ng-container *ngIf=\"columnConfigs[column]?.event; else elseEvent\">\r\n <input type=\"text\" [(ngModel)]=\"element[column]\"\r\n style=\"width: 100%; text-align: right\" (keydown.enter)=\"\r\n columnConfigs[column]?.functionEvent(element)\r\n \" onlyDigit=\"{{ columnConfigs[column]?.char }}\" />\r\n </ng-container>\r\n