ng-table-pg
Version:
A powerful and flexible responsive table component for Angular applications with drag-drop, filtering, pagination, and advanced responsive features
1 lines • 126 kB
Source Map (JSON)
{"version":3,"file":"ng-table-pg.mjs","sources":["../../../projects/table-lib/src/lib/table.service.ts","../../../projects/table-lib/src/lib/components/row/table-row.component.ts","../../../projects/table-lib/src/lib/pipes/web3-utils.pipe.ts","../../../projects/table-lib/src/lib/pipes/pipes.module.ts","../../../projects/table-lib/src/lib/components/content/table-row-content.component.ts","../../../projects/table-lib/src/lib/components/skeleton/skeleton-table.component.ts","../../../projects/table-lib/src/lib/components/button-dropdown/button-dropdown.component.ts","../../../projects/table-lib/src/lib/table.component.ts","../../../projects/table-lib/src/lib/table.template.html","../../../projects/table-lib/src/lib/table-translations.module.ts","../../../projects/table-lib/src/public-api.ts","../../../projects/table-lib/src/ng-table-pg.ts"],"sourcesContent":["import { Injectable } from '@angular/core';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class TableService {\n constructor() {}\n}\n","import { Component, Input } from '@angular/core';\nimport { CommonModule } from '@angular/common';\n\n@Component({\n selector: 'table-row',\n standalone: true,\n imports: [CommonModule],\n template: `\n <span class=\"whitespace-nowrap\">{{ text }}</span>\n `,\n})\nexport class TableRowComponent {\n @Input() text!: any;\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\n@Pipe({\n name: 'web3Utils',\n standalone: true\n})\nexport class Web3UtilsPipe implements PipeTransform {\n transform(value: string | number | null, type: string, decimals = 18) {\n if (value == null) {\n return 'NO DATA';\n }\n\n try {\n switch (type) {\n case 'wallet':\n return this.formatWalletAddress(String(value));\n case 'wei':\n return this.fromWei(String(value), decimals);\n case 'token':\n return this.formatTokenAmount(String(value), decimals);\n default:\n return value;\n }\n } catch (error) {\n return type === 'wei' ? '0.0000' : String(value);\n }\n }\n\n private formatWalletAddress(address: string): string {\n return address.length >= 10\n ? `${address.substring(0, 6)}...${address.substring(address.length - 4)}`\n : address;\n }\n\n private fromWei(wei: string, decimals: number): string {\n const value = Number(wei) / Math.pow(10, decimals);\n return isNaN(value) ? '0.0000' : value.toFixed(4);\n }\n\n private formatTokenAmount(amount: string, decimals: number): string {\n const value = Number(amount);\n if (isNaN(value)) return '0.00';\n \n if (value >= 1000000) {\n return (value / 1000000).toFixed(2) + 'M';\n } else if (value >= 1000) {\n return (value / 1000).toFixed(2) + 'K';\n }\n return value.toFixed(2); // Changed to always use 2 decimals for consistency\n }\n}\n","import { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Web3UtilsPipe } from './web3-utils.pipe';\n\n@NgModule({\n imports: [CommonModule, Web3UtilsPipe],\n exports: [Web3UtilsPipe],\n})\nexport class PipesModule {}\n","import { CommonModule } from '@angular/common';\nimport { Component, Input, OnChanges, SimpleChanges } from '@angular/core';\nimport { TableRowComponent } from '../row/table-row.component';\nimport { PipesModule } from '../../pipes/pipes.module';\n\n@Component({\n selector: 'table-row-content',\n standalone: true,\n imports: [CommonModule, TableRowComponent, PipesModule],\n template: `\n <ng-container *ngIf=\"isArrayItemValue; else singleItemTemplate\">\n <ng-container *ngFor=\"let item of itemTransformed\">\n <span class=\"mr-2 last:mr-0\">\n <ng-container\n *ngTemplateOutlet=\"contentTemplate; context: { $implicit: item }\"\n ></ng-container>\n </span>\n </ng-container>\n </ng-container>\n <ng-template #singleItemTemplate>\n <ng-container\n *ngTemplateOutlet=\"\n contentTemplate;\n context: { $implicit: itemTransformed }\n \"\n ></ng-container>\n </ng-template>\n\n <ng-template #contentTemplate let-value>\n <ng-container [ngSwitch]=\"column.type || column.key\">\n <!-- Caso 'step' -->\n <ng-container *ngSwitchCase=\"'step'\">\n <span\n [ngClass]=\"{\n 'px-2 py-1 rounded-full text-xs font-semibold': true,\n 'bg-green-100 text-green-800':\n getStatus(item.step) === 'completed',\n 'bg-yellow-100 text-yellow-800':\n getStatus(item.step) === 'in-progress',\n 'bg-blue-100 text-blue-800': getStatus(item.step) === 'pending'\n }\"\n >\n <table-row [text]=\"value\"></table-row>\n </span>\n </ng-container>\n\n <!-- Caso 'status' -->\n <ng-container *ngSwitchCase=\"'status'\">\n <span [ngClass]=\"getStatusClasses(item[column.key])\">\n <span *ngIf=\"item[column.key] === undefined\">Inactive</span>\n\n <ng-container [ngSwitch]=\"item.status || item[column.key]\">\n <span *ngSwitchCase=\"true\">Active</span>\n <span *ngSwitchCase=\"false\">Inactive</span>\n <span *ngSwitchCase=\"'partial'\">Partial Active</span>\n <span *ngSwitchCase=\"'reject'\">Rejected</span>\n <span *ngSwitchCase=\"'pending_to_send'\">Pending to send</span>\n <span *ngSwitchCase=\"'pending'\">Pending</span>\n <span *ngSwitchCase=\"'approved'\">Approved</span>\n <table-row\n *ngSwitchDefault\n [text]=\"value | titlecase\"\n ></table-row>\n </ng-container>\n </span>\n </ng-container>\n\n <!-- Caso 'HTML' -->\n <ng-container *ngSwitchCase=\"'html'\">\n <span [innerHTML]=\"value\"></span>\n </ng-container>\n\n <!-- Caso 'status:expired' -->\n <ng-container *ngSwitchCase=\"'status:expired'\">\n <span\n [ngClass]=\"{\n 'px-2 py-1 rounded-full text-xs font-semibold text-nowrap': true,\n 'bg-green-100 text-green-800': item[column.key] === true,\n 'bg-red-100 text-red-800': item[column.key] === false,\n }\"\n >\n <ng-container [ngSwitch]=\"item[column.key]\">\n <span *ngSwitchCase=\"true\">Active</span>\n <span *ngSwitchCase=\"false\">Expired</span>\n <table-row\n *ngSwitchDefault\n [text]=\"value | titlecase\"\n ></table-row>\n </ng-container>\n </span>\n </ng-container>\n\n <!-- Caso 'state' -->\n <ng-container *ngSwitchCase=\"'state'\">\n <span\n [ngClass]=\"{\n 'px-2 py-1 rounded-full text-xs font-semibold text-nowrap': true,\n 'bg-blue-100 text-blue-800': item.state === 'pre-approved',\n 'bg-green-100 text-green-800': item.state === 'approved',\n 'bg-yellow-100 text-yellow-800': item.state === 'pending',\n 'bg-red-100 text-red-800': item.state === 'rejected'\n }\"\n >\n <table-row [text]=\"value | titlecase\"></table-row>\n </span>\n </ng-container>\n\n <!-- Caso 'date' -->\n <ng-container *ngSwitchCase=\"'date'\">\n <ng-container *ngIf=\"value\">\n <table-row [text]=\"value | date : 'yyyy-MM-dd'\"></table-row>\n </ng-container>\n </ng-container>\n\n <!-- Caso 'boolean' -->\n <ng-container *ngSwitchCase=\"'boolean'\">\n {{ value ? 'Sí' : 'No' }}\n </ng-container>\n\n <!-- Caso 'date-time' -->\n <ng-container *ngSwitchCase=\"'date-time'\">\n <ng-container *ngIf=\"value\">\n <table-row [text]=\"value | date : 'dd/MM/yyyy HH:mm'\"></table-row>\n </ng-container>\n </ng-container>\n\n <!-- Caso 'timestamp' -->\n <ng-container *ngSwitchCase=\"'timestamp'\">\n <ng-container *ngIf=\"value\">\n <table-row\n [text]=\"value.toDate() | date : 'dd/MM/yyyy HH:mm'\"\n ></table-row>\n </ng-container>\n </ng-container>\n\n <!-- Caso 'json' -->\n <ng-container *ngSwitchCase=\"'json'\">\n <ng-container *ngIf=\"value\">\n <pre\n class=\"bg-slate-50 rounded-lg p-2 text-sm font-mono text-slate-700 overflow-x-auto max-h-[150px] overflow-y-auto shadow-sm border border-slate-200\"\n ><code>{{ formatJson(value) }}</code></pre>\n </ng-container>\n </ng-container>\n\n <!-- Caso 'currency' -->\n <ng-container *ngSwitchCase=\"'currency'\">\n <table-row\n [text]=\"value | currency : 'USD' : 'symbol' : '1.4-4'\"\n ></table-row>\n </ng-container>\n\n <!-- Casos adicionales -->\n <ng-container *ngSwitchCase=\"'uppercase'\">\n <table-row [text]=\"value | uppercase\"></table-row>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'titlecase'\">\n <table-row [text]=\"value | titlecase\"></table-row>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'wallet'\">\n <table-row [text]=\"value | web3Utils : 'wallet'\"></table-row>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'badget'\">\n <span\n class=\"px-2 py-1 text-nowrap rounded-full text-xs font-semibold bg-gray-100 text-gray-800\"\n >\n <table-row [text]=\"value\"></table-row>\n </span>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'badget:info'\">\n <span\n class=\"px-2 py-1 rounded-full text-nowrap text-xs font-semibold bg-blue-100 text-blue-800\"\n >\n <table-row [text]=\"value\"></table-row>\n </span>\n </ng-container>\n\n <ng-container *ngSwitchDefault>\n <table-row [text]=\"value\"></table-row>\n </ng-container>\n </ng-container>\n </ng-template>\n `,\n})\nexport class TableRowContentComponent implements OnChanges {\n @Input() item!: any;\n @Input() column!: any;\n @Input() itemValue!: any;\n\n itemTransformed!: any;\n isArrayItemValue = false;\n\n ngOnChanges(changes: SimpleChanges) {\n if (changes['itemValue']) {\n this.isArrayItemValue = Array.isArray(this.itemValue);\n this.itemTransformed = this.column.transform\n ? this.column.transform(this.itemValue, this.item)\n : this.itemValue;\n }\n }\n\n getStatus(step: string): string {\n const [current, total] = step.split('/').map(Number);\n if (current === total) return 'completed';\n if (current === 1) return 'pending';\n return 'in-progress';\n }\n\n getStatusClass(status: any) {\n const normalizedStatus =\n typeof status === 'string' ? status.toLowerCase() : status || 'inactive';\n switch (normalizedStatus) {\n case 'active':\n case 'activa':\n case 'completed':\n case true:\n return 'bg-green-100 text-green-800';\n case 'inactive':\n case 'inactiva':\n case 'refused':\n case 'rejected':\n case 'reject':\n case false:\n return 'bg-red-100 text-red-800';\n case 'pending_to_send':\n case 'approved':\n return 'bg-blue-100 text-blue-800';\n case 'partial':\n case 'in progress':\n return 'bg-yellow-100 text-yellow-800';\n case 'pending':\n return 'bg-gray-100 text-gray-800';\n default:\n return '';\n }\n }\n\n getStatusClasses(status: any) {\n return {\n 'px-2 py-1 rounded-full text-xs font-semibold text-nowrap': true,\n [this.getStatusClass(status)]: true,\n };\n }\n\n formatJson(value: any): string {\n try {\n const parsed = typeof value === 'string' ? JSON.parse(value) : value;\n const formatIndentation = (str: string) => {\n const lines = str.split('\\n');\n let indent = 0;\n\n return lines\n .map((line) => {\n // Reducir indentación para líneas que cierran bloques\n if (line.includes('}') || line.includes(']')) {\n indent -= 2;\n }\n\n const currentIndent = ' '.repeat(Math.max(0, indent));\n const formattedLine = currentIndent + line.trim();\n\n // Aumentar indentación para la siguiente línea si esta abre un bloque\n if (line.includes('{') || line.includes('[')) {\n indent += 2;\n }\n\n return formattedLine;\n })\n .join('\\n');\n };\n\n return formatIndentation(JSON.stringify(parsed, null, 2))\n .trim()\n .replace(/\": \"/g, '\": \"') // Espaciado consistente después de los dos puntos\n .replace(/,$/gm, ','); // Asegurar que las comas queden en la misma línea\n } catch (e) {\n return String(value);\n }\n }\n}\n","import { Component, ViewEncapsulation } from '@angular/core';\n\n@Component({\n selector: 'ng-table-pg-skeleton',\n standalone: true,\n encapsulation: ViewEncapsulation.None,\n template: `\n <div class=\"space-y-6\">\n <!-- Search and buttons -->\n <div\n class=\"flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4\"\n >\n <div class=\"skeleton h-10 w-full sm:w-2/3\"></div>\n <div class=\"flex flex-col sm:flex-row gap-2\">\n <div class=\"skeleton h-10 w-full sm:w-24\"></div>\n <div class=\"skeleton h-10 w-full sm:w-36\"></div>\n </div>\n </div>\n\n <!-- Table Container with overflow -->\n <div class=\"bg-white rounded-lg shadow overflow-hidden\">\n <div class=\"overflow-x-auto\">\n <table class=\"w-full min-w-full\">\n <!-- Desktop Headers (hidden on mobile) -->\n <thead class=\"bg-gray-50 hidden md:table-header-group\">\n <tr>\n <th class=\"px-3 sm:px-6 py-3 text-left\">\n <div class=\"skeleton h-4 w-8\"></div>\n </th>\n <th class=\"px-3 sm:px-6 py-3 text-left\">\n <div class=\"skeleton h-4 w-16\"></div>\n </th>\n <th class=\"px-3 sm:px-6 py-3 text-left\">\n <div class=\"skeleton h-4 w-12\"></div>\n </th>\n <th class=\"px-3 sm:px-6 py-3 text-left\">\n <div class=\"skeleton h-4 w-20\"></div>\n </th>\n <th class=\"px-3 sm:px-6 py-3 text-left\">\n <div class=\"skeleton h-4 w-24\"></div>\n </th>\n <th class=\"px-3 sm:px-6 py-3 text-left\">\n <div class=\"skeleton h-4 w-20\"></div>\n </th>\n <th class=\"px-3 sm:px-6 py-3 text-left\">\n <div class=\"skeleton h-4 w-16\"></div>\n </th>\n <th class=\"px-3 sm:px-6 py-3 text-left\">\n <div class=\"skeleton h-4 w-12\"></div>\n </th>\n <th class=\"px-3 sm:px-6 py-3 text-left\">\n <div class=\"skeleton h-4 w-8\"></div>\n </th>\n </tr>\n </thead>\n\n <!-- Mobile Headers (visible only on mobile) -->\n <thead class=\"bg-gray-50 md:hidden\">\n <tr>\n <th class=\"px-3 py-3 text-left\">\n <div class=\"skeleton h-4 w-16\"></div>\n </th>\n <th class=\"px-3 py-3 text-left\">\n <div class=\"skeleton h-4 w-20\"></div>\n </th>\n <th class=\"px-3 py-3 text-left\">\n <div class=\"skeleton h-4 w-12\"></div>\n </th>\n <th class=\"px-3 py-3 text-left\">\n <div class=\"skeleton h-4 w-8\"></div>\n </th>\n </tr>\n </thead>\n\n <tbody class=\"divide-y divide-gray-200\">\n <!-- Desktop Rows (hidden on mobile) -->\n @for (row of [1,2,3,4,5,6,7,8,9,10]; track row) {\n <tr class=\"border-t border-gray-200 hidden md:table-row\">\n <td class=\"px-3 sm:px-6 py-4\">\n <div class=\"skeleton h-4 w-8\"></div>\n </td>\n <td class=\"px-3 sm:px-6 py-4\">\n <div class=\"skeleton h-4 w-20\"></div>\n </td>\n <td class=\"px-3 sm:px-6 py-4\">\n <div class=\"skeleton h-6 w-8 rounded-full\"></div>\n </td>\n <td class=\"px-3 sm:px-6 py-4\">\n <div class=\"skeleton h-4 w-32\"></div>\n </td>\n <td class=\"px-3 sm:px-6 py-4\">\n <div class=\"skeleton h-4 w-28\"></div>\n </td>\n <td class=\"px-3 sm:px-6 py-4\">\n <div class=\"skeleton h-4 w-24\"></div>\n </td>\n <td class=\"px-3 sm:px-6 py-4\">\n <div class=\"skeleton h-4 w-16\"></div>\n </td>\n <td class=\"px-3 sm:px-6 py-4\">\n <div class=\"skeleton h-6 w-16 rounded-full\"></div>\n </td>\n <td class=\"px-3 sm:px-6 py-4\">\n <div class=\"skeleton h-6 w-6\"></div>\n </td>\n </tr>\n }\n\n <!-- Mobile Rows (visible only on mobile) -->\n @for (row of [1,2,3,4,5,6,7,8,9,10]; track row) {\n <tr class=\"border-t border-gray-200 md:hidden\">\n <td class=\"px-3 py-4\">\n <div class=\"skeleton h-4 w-16\"></div>\n </td>\n <td class=\"px-3 py-4\">\n <div class=\"skeleton h-4 w-20\"></div>\n </td>\n <td class=\"px-3 py-4\">\n <div class=\"skeleton h-6 w-16 rounded-full\"></div>\n </td>\n <td class=\"px-3 py-4\">\n <div class=\"skeleton h-6 w-6\"></div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n\n <!-- Pagination -->\n <div\n class=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\"\n >\n <div class=\"flex items-center gap-2\">\n <div class=\"skeleton h-4 w-12\"></div>\n <div class=\"skeleton h-8 w-12\"></div>\n <div class=\"skeleton h-4 w-16\"></div>\n </div>\n <div class=\"flex items-center justify-center sm:justify-end gap-2\">\n <div class=\"skeleton h-8 w-8\"></div>\n <div class=\"skeleton h-8 w-8\"></div>\n <div class=\"skeleton h-8 w-8\"></div>\n <div class=\"skeleton h-4 w-4\"></div>\n <div class=\"skeleton h-8 w-8\"></div>\n <div class=\"skeleton h-8 w-8\"></div>\n </div>\n </div>\n </div>\n `,\n styles: [\n `\n /* Estilos específicos del skeleton con !important para máxima compatibilidad */\n ng-table-pg-skeleton .skeleton {\n background-color: rgb(229 231 235) !important;\n animation: ng-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite !important;\n border-radius: 0.25rem !important;\n }\n\n @keyframes ng-skeleton-pulse {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n }\n\n /* Responsive utilities con !important */\n ng-table-pg-skeleton .flex {\n display: flex !important;\n }\n\n ng-table-pg-skeleton .flex-col {\n flex-direction: column !important;\n }\n\n ng-table-pg-skeleton .gap-2 {\n gap: 0.5rem !important;\n }\n\n ng-table-pg-skeleton .gap-4 {\n gap: 1rem !important;\n }\n\n ng-table-pg-skeleton .justify-center {\n justify-content: center !important;\n }\n\n ng-table-pg-skeleton .justify-between {\n justify-content: space-between !important;\n }\n\n ng-table-pg-skeleton .items-center {\n align-items: center !important;\n }\n\n ng-table-pg-skeleton .overflow-x-auto {\n overflow-x: auto !important;\n }\n\n ng-table-pg-skeleton .min-w-full {\n min-width: 100% !important;\n }\n\n ng-table-pg-skeleton .w-full {\n width: 100% !important;\n }\n\n ng-table-pg-skeleton .hidden {\n display: none !important;\n }\n\n /* Responsive breakpoints */\n @media (min-width: 640px) {\n ng-table-pg-skeleton .sm\\\\:flex-row {\n flex-direction: row !important;\n }\n\n ng-table-pg-skeleton .sm\\\\:justify-between {\n justify-content: space-between !important;\n }\n\n ng-table-pg-skeleton .sm\\\\:items-center {\n align-items: center !important;\n }\n\n ng-table-pg-skeleton .sm\\\\:justify-end {\n justify-content: flex-end !important;\n }\n\n ng-table-pg-skeleton .sm\\\\:w-2\\\\/3 {\n width: 66.666667% !important;\n }\n\n ng-table-pg-skeleton .sm\\\\:w-24 {\n width: 6rem !important;\n }\n\n ng-table-pg-skeleton .sm\\\\:w-36 {\n width: 9rem !important;\n }\n\n ng-table-pg-skeleton .sm\\\\:px-6 {\n padding-left: 1.5rem !important;\n padding-right: 1.5rem !important;\n }\n }\n\n @media (min-width: 768px) {\n ng-table-pg-skeleton .md\\\\:hidden {\n display: none !important;\n }\n\n ng-table-pg-skeleton .md\\\\:table-header-group {\n display: table-header-group !important;\n }\n\n ng-table-pg-skeleton .md\\\\:table-row {\n display: table-row !important;\n }\n }\n\n /* Espaciado y padding */\n ng-table-pg-skeleton .space-y-6 > * + * {\n margin-top: 1.5rem !important;\n }\n\n ng-table-pg-skeleton .px-3 {\n padding-left: 0.75rem !important;\n padding-right: 0.75rem !important;\n }\n\n ng-table-pg-skeleton .py-3 {\n padding-top: 0.75rem !important;\n padding-bottom: 0.75rem !important;\n }\n\n ng-table-pg-skeleton .py-4 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n\n ng-table-pg-skeleton .text-left {\n text-align: left !important;\n }\n\n ng-table-pg-skeleton .bg-white {\n background-color: rgb(255 255 255) !important;\n }\n\n ng-table-pg-skeleton .bg-gray-50 {\n background-color: rgb(249 250 251) !important;\n }\n\n ng-table-pg-skeleton .rounded-lg {\n border-radius: 0.5rem !important;\n }\n\n ng-table-pg-skeleton .rounded-full {\n border-radius: 9999px !important;\n }\n\n ng-table-pg-skeleton .shadow {\n box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1),\n 0 1px 2px -1px rgb(0 0 0 / 0.1) !important;\n }\n\n ng-table-pg-skeleton .overflow-hidden {\n overflow: hidden !important;\n }\n\n ng-table-pg-skeleton .border-t {\n border-top-width: 1px !important;\n }\n\n ng-table-pg-skeleton .border-gray-200 {\n border-color: rgb(229 231 235) !important;\n }\n\n ng-table-pg-skeleton .divide-y > * + * {\n border-top-width: 1px !important;\n }\n\n ng-table-pg-skeleton .divide-gray-200 > * + * {\n border-color: rgb(229 231 235) !important;\n }\n\n /* Tamaños específicos */\n ng-table-pg-skeleton .h-4 {\n height: 1rem !important;\n }\n ng-table-pg-skeleton .h-6 {\n height: 1.5rem !important;\n }\n ng-table-pg-skeleton .h-8 {\n height: 2rem !important;\n }\n ng-table-pg-skeleton .h-10 {\n height: 2.5rem !important;\n }\n ng-table-pg-skeleton .w-4 {\n width: 1rem !important;\n }\n ng-table-pg-skeleton .w-6 {\n width: 1.5rem !important;\n }\n ng-table-pg-skeleton .w-8 {\n width: 2rem !important;\n }\n ng-table-pg-skeleton .w-12 {\n width: 3rem !important;\n }\n ng-table-pg-skeleton .w-16 {\n width: 4rem !important;\n }\n ng-table-pg-skeleton .w-20 {\n width: 5rem !important;\n }\n ng-table-pg-skeleton .w-24 {\n width: 6rem !important;\n }\n ng-table-pg-skeleton .w-28 {\n width: 7rem !important;\n }\n ng-table-pg-skeleton .w-32 {\n width: 8rem !important;\n }\n `,\n ],\n})\nexport class SkeletonTableComponent {}\n","import {\n Component,\n Input,\n Output,\n EventEmitter,\n ElementRef,\n HostListener,\n AfterViewInit,\n ChangeDetectorRef,\n OnDestroy,\n ViewChild,\n} from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Subject } from 'rxjs';\n\n@Component({\n selector: 'app-dropdown-button',\n standalone: true,\n imports: [CommonModule],\n template: `\n <div class=\"relative inline-block\">\n <button\n #dropdownButton\n (click)=\"toggleDropdown($event)\"\n [class]=\"buttonClass\"\n >\n <ng-content select=\"[buttonContent]\"></ng-content>\n </button>\n <div\n *ngIf=\"isOpen\"\n #dropdownContent\n [ngStyle]=\"dropdownStyle\"\n [ngClass]=\"{ invisible: dropdownStyle.top == '-9999px' }\"\n class=\"fixed rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-50\"\n (click)=\"$event.stopPropagation()\"\n >\n <div class=\"py-1\">\n <ng-content select=\"[dropdownContent]\"></ng-content>\n </div>\n </div>\n </div>\n `,\n})\nexport class DropdownButtonComponent implements AfterViewInit, OnDestroy {\n @ViewChild('dropdownButton') dropdownButton!: ElementRef;\n @ViewChild('dropdownContent') dropdownContent!: ElementRef;\n\n @Input() buttonClass = 'text-gray-400 hover:text-gray-600';\n @Input() isOpen = false;\n @Output() opened = new EventEmitter<void>();\n @Output() closed = new EventEmitter<void>();\n\n dropdownStyle: any = {\n top: `-9999px`,\n left: `-9999px`,\n };\n private destroy$ = new Subject<void>();\n\n constructor(private elementRef: ElementRef, private cdr: ChangeDetectorRef) {}\n\n ngAfterViewInit() {\n this.updateDropdownPosition(); // Initial position\n }\n\n ngOnDestroy() {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n @HostListener('document:click', ['$event'])\n clickOutside(event: Event) {\n if (!this.elementRef.nativeElement.contains(event.target))\n this.closeDropdown();\n }\n\n @HostListener('window:scroll')\n onScroll() {\n if (this.isOpen) {\n this.updateDropdownPosition();\n }\n }\n\n toggleDropdown(event: Event) {\n event.stopPropagation();\n this.isOpen ? this.closeDropdown() : this.openDropdown();\n }\n\n private openDropdown() {\n this.opened.emit();\n this.isOpen = true;\n setTimeout(() => this.updateDropdownPosition(), 10);\n }\n\n private closeDropdown() {\n this.dropdownStyle = {\n top: `-9999px`,\n left: `-9999px`,\n };\n\n this.isOpen = false;\n this.closed.emit();\n }\n\n private updateDropdownPosition() {\n if (!this.dropdownButton || !this.dropdownContent) return;\n\n const buttonRect =\n this.dropdownButton.nativeElement.getBoundingClientRect();\n const dropdownRect =\n this.dropdownContent.nativeElement.getBoundingClientRect();\n const scrollableContainer = this.findScrollableContainer(\n this.dropdownButton.nativeElement\n );\n\n let top = buttonRect.bottom + 10;\n let left = buttonRect.right - dropdownRect.width;\n\n if (scrollableContainer) {\n const containerRect = scrollableContainer.getBoundingClientRect();\n\n if (top + dropdownRect.height > window.innerHeight) {\n top = buttonRect.top - dropdownRect.height - 10;\n if (top < 10) top = 80;\n }\n\n if (\n top + dropdownRect.height <= window.innerHeight &&\n top + dropdownRect.height > containerRect.bottom\n ) {\n top = buttonRect.top - dropdownRect.height - 10;\n if (top < containerRect.top) top = containerRect.top;\n }\n\n if (left < containerRect.left) {\n left = containerRect.left;\n }\n } else {\n if (top + dropdownRect.height > window.innerHeight) {\n top = buttonRect.top - dropdownRect.height - 10;\n if (top < 10) top = 80;\n }\n\n if (left < 0) {\n left = 0;\n }\n\n if (left + dropdownRect.width > window.innerWidth) {\n left = window.innerWidth - dropdownRect.width;\n }\n }\n\n this.dropdownStyle = {\n top: `${top}px`,\n left: `${left}px`,\n overflowY: 'auto',\n overflowX: 'hidden',\n };\n\n this.cdr.detectChanges();\n }\n\n private findScrollableContainer(element: HTMLElement): HTMLElement | null {\n while (element && element !== document.body) {\n const style = window.getComputedStyle(element);\n const overflow =\n style.getPropertyValue('overflow') +\n style.getPropertyValue('overflow-y') +\n style.getPropertyValue('overflow-x');\n\n if (\n /(auto|scroll)/.test(overflow) &&\n (element.scrollHeight > element.clientHeight ||\n element.scrollWidth > element.clientWidth)\n ) {\n return element;\n }\n element = element.parentElement as HTMLElement;\n }\n return null;\n }\n}\n","import {\n Component,\n EventEmitter,\n HostListener,\n Input,\n OnChanges,\n OnInit,\n Output,\n SimpleChanges,\n ViewEncapsulation,\n} from '@angular/core';\nimport { TableRowContentComponent } from './components/content/table-row-content.component';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { SkeletonTableComponent } from './components/skeleton/skeleton-table.component';\nimport { TranslateModule, TranslateService } from '@ngx-translate/core';\nimport {\n CdkDragDrop,\n DragDropModule,\n moveItemInArray,\n} from '@angular/cdk/drag-drop';\nimport { DropdownButtonComponent } from './components/button-dropdown/button-dropdown.component';\nimport { saveAs } from 'file-saver';\nimport * as XLSX from 'xlsx';\nimport {\n ITableColumns,\n ITableActions,\n ITableButtons,\n ITableOrderChange,\n} from './table.interfaces';\nimport { Web3UtilsPipe } from './pipes/web3-utils.pipe';\n@Component({\n selector: 'ng-table-pg',\n standalone: true,\n imports: [\n CommonModule,\n FormsModule,\n TranslateModule,\n DragDropModule,\n TableRowContentComponent,\n DropdownButtonComponent,\n SkeletonTableComponent,\n ],\n templateUrl: './table.template.html',\n styleUrls: ['./table.styles.css'],\n encapsulation: ViewEncapsulation.None,\n providers: [Web3UtilsPipe],\n styles: [\n `\n /* Estilos críticos con !important para asegurar que funcionen en cualquier proyecto */\n ng-table-pg .space-y-4 > * + * {\n margin-top: 1rem !important;\n }\n ng-table-pg .space-y-6 > * + * {\n margin-top: 1.5rem !important;\n }\n ng-table-pg .space-x-2 > * + * {\n margin-left: 0.5rem !important;\n }\n ng-table-pg .flex {\n display: flex !important;\n }\n ng-table-pg .justify-between {\n justify-content: space-between !important;\n }\n ng-table-pg .items-center {\n align-items: center !important;\n }\n ng-table-pg .relative {\n position: relative !important;\n }\n ng-table-pg .absolute {\n position: absolute !important;\n }\n ng-table-pg .flex-grow {\n flex-grow: 1 !important;\n }\n ng-table-pg .gap-2 {\n gap: 0.5rem !important;\n }\n ng-table-pg .mr-4 {\n margin-right: 1rem !important;\n }\n ng-table-pg .ml-1 {\n margin-left: 0.25rem !important;\n }\n ng-table-pg .ml-2 {\n margin-left: 0.5rem !important;\n }\n ng-table-pg .mb-1 {\n margin-bottom: 0.25rem !important;\n }\n ng-table-pg .mb-4 {\n margin-bottom: 1rem !important;\n }\n ng-table-pg .pl-3 {\n padding-left: 0.75rem !important;\n }\n ng-table-pg .pl-8 {\n padding-left: 2rem !important;\n }\n ng-table-pg .pr-4 {\n padding-right: 1rem !important;\n }\n ng-table-pg .pr-10 {\n padding-right: 2.5rem !important;\n }\n ng-table-pg .px-4 {\n padding-left: 1rem !important;\n padding-right: 1rem !important;\n }\n ng-table-pg .px-6 {\n padding-left: 1.5rem !important;\n padding-right: 1.5rem !important;\n }\n ng-table-pg .py-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n ng-table-pg .py-3 {\n padding-top: 0.75rem !important;\n padding-bottom: 0.75rem !important;\n }\n ng-table-pg .py-4 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n ng-table-pg .p-4 {\n padding: 1rem !important;\n }\n ng-table-pg .left-2 {\n left: 0.5rem !important;\n }\n ng-table-pg .top-2\\\\.5 {\n top: 0.625rem !important;\n }\n ng-table-pg .w-full {\n width: 100% !important;\n }\n ng-table-pg .w-4 {\n width: 1rem !important;\n }\n ng-table-pg .w-5 {\n width: 1.25rem !important;\n }\n ng-table-pg .w-6 {\n width: 1.5rem !important;\n }\n ng-table-pg .w-8 {\n width: 2rem !important;\n }\n ng-table-pg .w-10 {\n width: 2.5rem !important;\n }\n ng-table-pg .w-12 {\n width: 3rem !important;\n }\n ng-table-pg .w-16 {\n width: 4rem !important;\n }\n ng-table-pg .w-20 {\n width: 5rem !important;\n }\n ng-table-pg .w-24 {\n width: 6rem !important;\n }\n ng-table-pg .w-32 {\n width: 8rem !important;\n }\n ng-table-pg .w-36 {\n width: 9rem !important;\n }\n ng-table-pg .w-40 {\n width: 10rem !important;\n }\n ng-table-pg .w-56 {\n width: 14rem !important;\n }\n ng-table-pg .w-2\\\\/3 {\n width: 66.666667% !important;\n }\n ng-table-pg .h-4 {\n height: 1rem !important;\n }\n ng-table-pg .h-5 {\n height: 1.25rem !important;\n }\n ng-table-pg .h-6 {\n height: 1.5rem !important;\n }\n ng-table-pg .h-8 {\n height: 2rem !important;\n }\n ng-table-pg .h-10 {\n height: 2.5rem !important;\n }\n ng-table-pg .min-w-full {\n min-width: 100% !important;\n }\n ng-table-pg .bg-white {\n background-color: rgb(255 255 255) !important;\n }\n ng-table-pg .bg-gray-50 {\n background-color: rgb(249 250 251) !important;\n }\n ng-table-pg .bg-gray-200 {\n background-color: rgb(229 231 235) !important;\n }\n ng-table-pg .text-gray-400 {\n color: rgb(156 163 175) !important;\n }\n ng-table-pg .text-gray-500 {\n color: rgb(107 114 128) !important;\n }\n ng-table-pg .text-gray-700 {\n color: rgb(55 65 81) !important;\n }\n ng-table-pg .hover\\\\:bg-gray-50:hover {\n background-color: rgb(249 250 251) !important;\n }\n ng-table-pg .border {\n border-width: 1px !important;\n }\n ng-table-pg .border-t {\n border-top-width: 1px !important;\n }\n ng-table-pg .border-1 {\n border-width: 1px !important;\n }\n ng-table-pg .border-gray-200 {\n border-color: rgb(229 231 235) !important;\n }\n ng-table-pg .border-gray-300 {\n border-color: rgb(209 213 219) !important;\n }\n ng-table-pg .divide-y > * + * {\n border-top-width: 1px !important;\n }\n ng-table-pg .divide-gray-200 > * + * {\n border-color: rgb(229 231 235) !important;\n }\n ng-table-pg .rounded {\n border-radius: 0.25rem !important;\n }\n ng-table-pg .rounded-md {\n border-radius: 0.375rem !important;\n }\n ng-table-pg .rounded-lg {\n border-radius: 0.5rem !important;\n }\n ng-table-pg .rounded-full {\n border-radius: 9999px !important;\n }\n ng-table-pg .overflow-hidden {\n overflow: hidden !important;\n }\n ng-table-pg .overflow-x-auto {\n overflow-x: auto !important;\n }\n ng-table-pg .text-left {\n text-align: left !important;\n }\n ng-table-pg .text-xs {\n font-size: 0.75rem !important;\n line-height: 1rem !important;\n }\n ng-table-pg .text-sm {\n font-size: 0.875rem !important;\n line-height: 1.25rem !important;\n }\n ng-table-pg .text-base {\n font-size: 1rem !important;\n line-height: 1.5rem !important;\n }\n ng-table-pg .font-medium {\n font-weight: 500 !important;\n }\n ng-table-pg .uppercase {\n text-transform: uppercase !important;\n }\n ng-table-pg .tracking-wider {\n letter-spacing: 0.05em !important;\n }\n ng-table-pg .cursor-pointer {\n cursor: pointer !important;\n }\n ng-table-pg .block {\n display: block !important;\n }\n ng-table-pg .sr-only {\n position: absolute !important;\n width: 1px !important;\n height: 1px !important;\n padding: 0 !important;\n margin: -1px !important;\n overflow: hidden !important;\n clip: rect(0, 0, 0, 0) !important;\n white-space: nowrap !important;\n border-width: 0 !important;\n }\n ng-table-pg .shadow {\n box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1),\n 0 1px 2px -1px rgb(0 0 0 / 0.1) !important;\n }\n ng-table-pg .focus\\\\:outline-none:focus {\n outline: 2px solid transparent !important;\n outline-offset: 2px !important;\n }\n ng-table-pg .focus\\\\:ring-indigo-500:focus {\n --tw-ring-color: rgb(99 102 241) !important;\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0\n var(--tw-ring-offset-width) var(--tw-ring-offset-color) !important;\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0\n calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color) !important;\n box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow),\n var(--tw-shadow, 0 0 #0000) !important;\n }\n ng-table-pg .focus\\\\:border-indigo-500:focus {\n border-color: rgb(99 102 241) !important;\n }\n ng-table-pg .animate-pulse {\n animation: ng-table-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite !important;\n }\n @keyframes ng-table-pulse {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n }\n ng-table-pg .skeleton {\n background-color: rgb(229 231 235) !important;\n animation: ng-table-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite !important;\n border-radius: 0.25rem !important;\n }\n ng-table-pg table {\n table-layout: auto !important;\n width: 100% !important;\n }\n /* Estilos adicionales para responsividad */\n ng-table-pg .table-container {\n position: relative !important;\n overflow: hidden !important;\n border-radius: 0.5rem !important;\n border: 1px solid rgb(229 231 235) !important;\n background: white !important;\n }\n ng-table-pg .table-container.responsive {\n overflow-x: auto !important;\n -webkit-overflow-scrolling: touch !important;\n scrollbar-width: auto !important;\n scrollbar-color: rgb(156 163 175) rgb(243 244 246) !important;\n }\n ng-table-pg .table-container.responsive::-webkit-scrollbar {\n height: 14px !important;\n width: 14px !important;\n }\n ng-table-pg .table-container.responsive::-webkit-scrollbar-track {\n background: rgb(243 244 246) !important;\n border-radius: 8px !important;\n margin: 2px !important;\n }\n ng-table-pg .table-container.responsive::-webkit-scrollbar-thumb {\n background: rgb(156 163 175) !important;\n border-radius: 8px !important;\n border: 2px solid rgb(243 244 246) !important;\n min-height: 20px !important;\n }\n ng-table-pg .table-container.responsive::-webkit-scrollbar-thumb:hover {\n background: rgb(107 114 128) !important;\n border: 2px solid rgb(229 231 235) !important;\n }\n ng-table-pg .table-container.responsive::-webkit-scrollbar-thumb:active {\n background: rgb(75 85 99) !important;\n }\n ng-table-pg .table-container.responsive::-webkit-scrollbar-corner {\n background: rgb(243 244 246) !important;\n }\n /* Scrollbar siempre visible */\n ng-table-pg .table-container.always-show-scrollbar {\n scrollbar-width: auto !important;\n overflow-x: scroll !important;\n }\n ng-table-pg .table-container.always-show-scrollbar::-webkit-scrollbar {\n height: 14px !important;\n width: 14px !important;\n display: block !important;\n }\n /* Estilo prominente del scrollbar */\n ng-table-pg .table-container.scrollbar-prominent::-webkit-scrollbar {\n height: 18px !important;\n width: 18px !important;\n }\n ng-table-pg .table-container.scrollbar-prominent::-webkit-scrollbar-thumb {\n background: rgb(99 102 241) !important;\n border: 3px solid rgb(243 244 246) !important;\n border-radius: 10px !important;\n }\n ng-table-pg .table-container.scrollbar-prominent::-webkit-scrollbar-thumb:hover {\n background: rgb(79 70 229) !important;\n border: 3px solid rgb(229 231 235) !important;\n }\n ng-table-pg .table-container.scrollbar-prominent::-webkit-scrollbar-track {\n background: rgb(243 244 246) !important;\n border-radius: 10px !important;\n border: 1px solid rgb(229 231 235) !important;\n }\n /* Estilo mínimo del scrollbar */\n ng-table-pg .table-container.scrollbar-minimal::-webkit-scrollbar {\n height: 8px !important;\n width: 8px !important;\n }\n ng-table-pg .table-container.scrollbar-minimal::-webkit-scrollbar-thumb {\n background: rgba(156, 163, 175, 0.7) !important;\n border: none !important;\n border-radius: 4px !important;\n }\n ng-table-pg .table-container.scrollbar-minimal::-webkit-scrollbar-track {\n background: transparent !important;\n border: none !important;\n }\n ng-table-pg .scroll-indicator {\n position: absolute !important;\n top: 0 !important;\n bottom: 0 !important;\n width: 20px !important;\n pointer-events: none !important;\n z-index: 10 !important;\n transition: opacity 0.2s ease !important;\n }\n ng-table-pg .scroll-indicator.left {\n left: 0 !important;\n background: linear-gradient(\n to right,\n rgba(255, 255, 255, 0.9),\n transparent\n ) !important;\n }\n ng-table-pg .scroll-indicator.right {\n right: 0 !important;\n background: linear-gradient(\n to left,\n rgba(255, 255, 255, 0.9),\n transparent\n ) !important;\n }\n ng-table-pg .scroll-indicator.hidden {\n opacity: 0 !important;\n }\n ng-table-pg .responsive-table {\n width: 100% !important;\n border-collapse: collapse !important;\n table-layout: auto !important;\n }\n ng-table-pg .responsive-table.compact {\n font-size: 0.875rem !important;\n }\n ng-table-pg .responsive-table.compact th,\n ng-table-pg .responsive-table.compact td {\n padding: 0.5rem 0.75rem !important;\n }\n ng-table-pg .sticky-header {\n position: sticky !important;\n top: 0 !important;\n z-index: 20 !important;\n background: rgb(249 250 251) !important;\n backdrop-filter: blur(8px) !important;\n border-bottom: 2px solid rgb(229 231 235) !important;\n }\n ng-table-pg .cell-content {\n max-width: 200px !important;\n overflow: hidden !important;\n text-overflow: ellipsis !important;\n white-space: nowrap !important;\n }\n ng-table-pg .cell-content.expandable {\n white-space: normal !important;\n word-wrap: break-word !important;\n }\n ng-table-pg .cell-content.truncate {\n display: -webkit-box !important;\n -webkit-line-clamp: 2 !important;\n -webkit-box-orient: vertical !important;\n overflow: hidden !important;\n }\n /* Responsive con scroll horizontal - mantener todas las columnas visibles */\n ng-table-pg .table-container.responsive {\n overflow-x: auto !important;\n -webkit-overflow-scrolling: touch !important;\n }\n \n ng-table-pg .responsive-table {\n table-layout: auto !important;\n width: 100% !important;\n }\n \n /* Comportamiento de scroll horizontal (default) */\n ng-table-pg .table-container.scroll-mode {\n overflow-x: auto !important;\n }\n \n /* Solo aplicar min-width cuando sea necesario */\n ng-table-pg .table-container.scroll-mode .responsive-table {\n width: 100% !important;\n table-layout: auto !important;\n }\n \n /* Asegurar que las columnas mantengan su ancho cuando está en modo scroll */\n ng-table-pg .table-container.scroll-mode .responsive-table th,\n ng-table-pg .table-container.scroll-mode .responsive-table td {\n white-space: nowrap !important;\n overflow: hidden !important;\n text-overflow: ellipsis !important;\n }\n \n /* Comportamiento de ocultar columnas (solo si hideColumnsOnResize es true) */\n @media screen and (max-width: 640px) {\n ng-table-pg .table-container.hide-columns-mode .col-priority-3 {\n display: none !important;\n }\n }\n @media screen and (max-width: 768px) {\n ng-table-pg .table-container.hide-columns-mode .col-priority-2 {\n display: none !important;\n }\n }\n `,\n ],\n})\nexport class TableComponent implements OnInit, OnChanges {\n @Input() data: any[] | null = [];\n @Input() columns: ITableColumns[] = [];\n @Input() filterOptions: { key: string; label: string; options: any[] }[] = [];\n @Input() actions: ITableActions[] = [];\n @Input() buttons: ITableButtons[] = [];\n @Input() loading = false;\n @Input() showSearch = true;\n @Input() enableExport = true;\n @Input() itemsPerPage = 10;\n @Input() showActionRow: (item: any) => boolean = (item: any) => true;\n @Input() enableDragDrop = false;\n\n // Nuevas propiedades para responsividad\n @Input() responsive = true; // Habilita el modo responsive\n @Input() stickyHeader = false; // Header fijo al hacer scroll\n @Input() compactMode = false; // Modo compacto para pantallas pequeñas\n @Input() horizontalScroll = true; // Permite scroll horizontal\n @Input() minTableWidth = '800px'; // Ancho mínimo de la tabla\n @Input() maxTableHeight = 'none'; // Altura máxima de la tabla\n @Input() showScrollIndicators = true; // Muestra indicadores de scroll\n @Input() alwaysShowScrollbar = false; // Fuerza la visibilidad del scrollbar\n @Input() scrollbarStyle: 'default' | 'prominent' | 'minimal' = 'default'; // Estilo del scrollbar\n @Input() hideColumnsOnResize = false; // Si true, oculta columnas por prioridad. Si false, usa scroll horizontal\n\n @Output() orderChanged = new EventEmitter<ITableOrderChange>();\n\n filteredData: any[] = [];\n paginatedData: any[] = [];\n searchTerm = '';\n activeFilters: { [key: string]: any } = {};\n showFilters = false;\n sortColumn = '';\n sortDirection: 'asc' | 'desc' = 'asc';\n currentPage = 1;\n activeDropdownIndex: number | null = null;\n\n isDragging = false;\n dragTimeout: any;\n draggedItem: any;\n dragOverPage: number | null = null;\n pageDropLists: string[] = [];\n\n // Variables para scroll indicators\n showLeftScrollIndicator = false;\n showRightScrollIndicator = false;\n private tableContainer?: HTMLElement;\n private resizeListener?: () => void;\n\n constructor(private translate: TranslateService) {}\n\n ngOnInit() {\n this.filteredData = [...(this.data || [])];\n this.updatePagination();\n this.updatePageDropLists();\n this.setupScrollListeners();\n this.setupResizeListener();\n \n // Log responsive status for debugging\n this.logResponsiveStatus();\n }\n\n ngOnDestroy() {\n this.removeScrollListeners();\n this.removeResizeListener();\n }\n\n /**\n * Configurar listeners para scroll indicators\n */\n private setupScrollListeners(): void {\n setTimeout(() => {\n this.tableContainer = document.querySelector(\n '.table-container'\n ) as HTMLElement;\n if (this.tableContainer && this.showScrollIndicators) {\n this.tableContainer.addEventListener(\n 'scroll',\n this.handleScroll.bind(this)\n );\n // Check initial scroll state\n this.handleScr