mn-angular-lib 0.0.56 → 0.0.57

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.
@@ -3521,11 +3521,11 @@ class MnTable {
3521
3521
  this.selectionChange.emit(rows);
3522
3522
  }
3523
3523
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MnTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
3524
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: MnTable, isStandalone: true, selector: "mn-table", inputs: { dataSource: "dataSource" }, outputs: { sortChange: "sortChange", selectionChange: "selectionChange", rowClick: "rowClick" }, ngImport: i0, template: "<!-- Toolbar: search + custom toolbar template -->\n<div class=\"flex flex-row items-center justify-end gap-2 mb-3\">\n <div *ngIf=\"dataSource.canSearch\">\n <input\n type=\"text\"\n class=\"input input-sm rounded border border-gray-300 bg-gray-100 px-3 py-1.5 text-sm focus:outline-none focus:border-brand-500 w-full max-w-xs dark:bg-gray-800 dark:border-gray-600 dark:text-gray-100 dark:placeholder-gray-400\"\n [placeholder]=\"dataSource.searchPlaceholder ?? 'Search...'\"\n (input)=\"onSearch($any($event.target).value)\"\n aria-label=\"Search table\"\n />\n </div>\n <ng-container *ngIf=\"dataSource.toolbarTemplate\" [ngTemplateOutlet]=\"dataSource.toolbarTemplate\"></ng-container>\n</div>\n\n<!-- Table wrapper with horizontal scroll -->\n<div class=\"overflow-x-auto\" role=\"region\" aria-label=\"Data table\">\n <table [class]=\"tableClasses\">\n <thead>\n <tr>\n <!-- Selection checkbox column header -->\n <th *ngIf=\"hasSelection\" class=\"w-10 text-center text-sm bg-gray-50 px-2 py-2 dark:bg-gray-800\">\n <label *ngIf=\"isMultiSelect\">\n <input\n type=\"checkbox\"\n class=\"accent-brand-500\"\n [checked]=\"allSelected\"\n (change)=\"toggleAll()\"\n aria-label=\"Select all rows\"\n />\n </label>\n </th>\n\n <!-- Data columns -->\n <th\n *ngFor=\"let column of dataSource.columns; trackBy: trackByKey\"\n class=\"text-sm px-4 py-2\"\n [class.cursor-pointer]=\"isSortable(column)\"\n [class.select-none]=\"isSortable(column)\"\n [class.hover:bg-gray-100]=\"isSortable(column)\"\n [class.text-left]=\"(column.align ?? 'left') === 'left'\"\n [class.text-center]=\"column.align === 'center'\"\n [class.text-right]=\"column.align === 'right'\"\n [class.hidden]=\"column.hiddenBelow === 'sm' || column.hiddenBelow === 'md' || column.hiddenBelow === 'lg'\"\n [class.sm:table-cell]=\"column.hiddenBelow === 'sm'\"\n [class.md:table-cell]=\"column.hiddenBelow === 'md'\"\n [class.lg:table-cell]=\"column.hiddenBelow === 'lg'\"\n [style.width]=\"column.width ?? null\"\n [attr.aria-sort]=\"currentSort?.columnKey === column.key ? (currentSort!.direction === 'asc' ? 'ascending' : 'descending') : null\"\n (click)=\"sort(column)\"\n >\n <span class=\"inline-flex items-center gap-1\">\n <ng-container *ngIf=\"isTemplateRef(column.header); else stringHeader\">\n <ng-container [ngTemplateOutlet]=\"$any(column.header)\"></ng-container>\n </ng-container>\n <ng-template #stringHeader>\n <span>{{ column.header }}</span>\n </ng-template>\n <span *ngIf=\"isSortable(column)\" class=\"text-[0.65rem] opacity-70 min-w-3 inline-block\">{{ getSortIcon(column) }}</span>\n </span>\n </th>\n\n <!-- Actions column header -->\n <th *ngIf=\"hasRowActions\" class=\"text-sm px-4 py-2 text-right\">Actions</th>\n </tr>\n </thead>\n\n <tbody>\n <!-- Loading state -->\n <ng-container *ngIf=\"dataSource.isDataLoading; else content\">\n <tr *ngFor=\"let _ of skeletonRows\" class=\"animate-pulse\">\n <td *ngIf=\"hasSelection\" class=\"px-2 py-3\"><div class=\"h-4 w-4 rounded bg-gray-200 dark:bg-gray-700\"></div></td>\n <td *ngFor=\"let column of dataSource.columns; trackBy: trackByKey\" class=\"px-4 py-3\"\n [class.hidden]=\"column.hiddenBelow === 'sm' || column.hiddenBelow === 'md' || column.hiddenBelow === 'lg'\"\n [class.sm:table-cell]=\"column.hiddenBelow === 'sm'\"\n [class.md:table-cell]=\"column.hiddenBelow === 'md'\"\n [class.lg:table-cell]=\"column.hiddenBelow === 'lg'\"\n >\n <div class=\"h-4 w-3/4 rounded bg-gray-200 dark:bg-gray-700\"></div>\n </td>\n <td *ngIf=\"hasRowActions\" class=\"px-4 py-3\"><div class=\"h-4 w-16 rounded bg-gray-200 dark:bg-gray-700\"></div></td>\n </tr>\n </ng-container>\n\n <!-- Content -->\n <ng-template #content>\n <!-- Empty state -->\n <tr *ngIf=\"filteredItems.length === 0\">\n <td [attr.colspan]=\"totalColumnCount\" class=\"text-center text-xs py-8\">\n <ng-container *ngIf=\"dataSource.emptyTemplate; else defaultEmpty\">\n <ng-container [ngTemplateOutlet]=\"dataSource.emptyTemplate\"></ng-container>\n </ng-container>\n <ng-template #defaultEmpty>\n <div class=\"flex flex-col items-center gap-2 text-gray-400 dark:text-gray-500\">\n <p class=\"text-sm\">{{ dataSource.emptyMessage }}</p>\n </div>\n </ng-template>\n </td>\n </tr>\n\n <!-- Data rows -->\n <tr\n *ngFor=\"let row of filteredItems; trackBy: trackByID; let odd = odd; let last = last\"\n class=\"bg-white transition-colors duration-150 dark:bg-gray-900\"\n [class.bg-brand-50]=\"isSelected(row)\"\n [class.bg-gray-50]=\"!isSelected(row) && odd && dataSource.appearance?.striped\"\n [class.hover:bg-gray-100]=\"dataSource.appearance?.hover !== false\"\n [class.cursor-pointer]=\"!!dataSource.onRowClick\"\n [class.border-b]=\"!last\"\n [class.border-gray-200]=\"!last\"\n [class.border-b-1]=\"last\"\n [class.border-black]=\"last\"\n [class.shadow-3xl]=\"last\"\n (click)=\"onRowClick(row)\"\n >\n <!-- Selection checkbox -->\n <td *ngIf=\"hasSelection\" class=\"w-10 text-center px-2 py-2\">\n <label>\n <input\n *ngIf=\"isMultiSelect; else radioTpl\"\n type=\"checkbox\"\n class=\"accent-brand-500\"\n [checked]=\"isSelected(row)\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(row)\"\n />\n <ng-template #radioTpl>\n <input\n type=\"radio\"\n class=\"accent-brand-500\"\n [checked]=\"isSelected(row)\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(row)\"\n name=\"mn-table-single-select\"\n />\n </ng-template>\n </label>\n </td>\n\n <!-- Data cells -->\n <td\n *ngFor=\"let column of dataSource.columns; trackBy: trackByKey\"\n class=\"text-xs px-4 py-2\"\n [class.text-left]=\"(column.align ?? 'left') === 'left'\"\n [class.text-center]=\"column.align === 'center'\"\n [class.text-right]=\"column.align === 'right'\"\n [class.hidden]=\"column.hiddenBelow === 'sm' || column.hiddenBelow === 'md' || column.hiddenBelow === 'lg'\"\n [class.sm:table-cell]=\"column.hiddenBelow === 'sm'\"\n [class.md:table-cell]=\"column.hiddenBelow === 'md'\"\n [class.lg:table-cell]=\"column.hiddenBelow === 'lg'\"\n [style.width]=\"column.width ?? null\"\n >\n <ng-container *ngIf=\"isTemplateRef(column.cell); else stringCell\">\n <ng-container [ngTemplateOutlet]=\"$any(column.cell)\" [ngTemplateOutletContext]=\"{ $implicit: row, data: row }\"></ng-container>\n </ng-container>\n <ng-template #stringCell>{{ getCellValue(column, row) }}</ng-template>\n </td>\n\n <!-- Row actions -->\n <td *ngIf=\"hasRowActions\" class=\"text-right px-4 py-2\">\n <div class=\"inline-flex items-center justify-end gap-2\">\n <button\n *ngFor=\"let action of getVisibleActions(row)\"\n class=\"px-2 py-1 text-xs rounded hover:bg-gray-100 transition-colors disabled:opacity-50 disabled:cursor-not-allowed dark:hover:bg-gray-700\"\n [ngClass]=\"action.cssClass ?? ''\"\n [disabled]=\"action.isDisabled ? action.isDisabled(row) : false\"\n (click)=\"$event.stopPropagation(); action.onClick(row)\"\n [attr.aria-label]=\"action.label\"\n >\n <span *ngIf=\"action.icon\" [innerHTML]=\"action.icon\"></span>\n <span *ngIf=\"action.label\">{{ action.label }}</span>\n </button>\n </div>\n </td>\n </tr>\n </ng-template>\n </tbody>\n </table>\n</div>\n\n<!-- Load more button -->\n<div *ngIf=\"showLoadMore\" class=\"flex justify-center py-4\">\n <button\n class=\"px-4 py-1.5 text-sm rounded border border-brand-500 text-brand-500 hover:bg-brand-100 transition-colors disabled:opacity-50\"\n (click)=\"loadMoreRows()\"\n [disabled]=\"loadingMoreRows\"\n >\n <span *ngIf=\"loadingMoreRows\" class=\"inline-block w-3 h-3 border-2 border-brand-500 border-t-transparent rounded-full animate-spin mr-2\"></span>\n Load more\n </button>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
3524
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: MnTable, isStandalone: true, selector: "mn-table", inputs: { dataSource: "dataSource" }, outputs: { sortChange: "sortChange", selectionChange: "selectionChange", rowClick: "rowClick" }, ngImport: i0, template: "<!-- Toolbar: search + custom toolbar template -->\n<div class=\"flex flex-row items-center justify-end gap-2 mb-3\">\n <div *ngIf=\"dataSource.canSearch\">\n <input\n type=\"text\"\n class=\"input input-sm rounded border border-gray-300 bg-gray-100 px-3 py-1.5 text-sm focus:outline-none focus:border-brand-500 w-full max-w-xs dark:bg-gray-800 dark:border-gray-600 dark:text-gray-100 dark:placeholder-gray-400\"\n [placeholder]=\"dataSource.searchPlaceholder ?? 'Search...'\"\n (input)=\"onSearch($any($event.target).value)\"\n aria-label=\"Search table\"\n />\n </div>\n <ng-container *ngIf=\"dataSource.toolbarTemplate\" [ngTemplateOutlet]=\"dataSource.toolbarTemplate\"></ng-container>\n</div>\n\n<!-- Table wrapper with horizontal scroll -->\n<div class=\"overflow-x-auto\" role=\"region\" aria-label=\"Data table\">\n <table [class]=\"tableClasses\">\n <thead>\n <tr>\n <!-- Selection checkbox column header -->\n <th *ngIf=\"hasSelection\" class=\"w-10 text-center text-sm bg-gray-50 px-2 py-2 dark:bg-gray-800\">\n <label *ngIf=\"isMultiSelect\">\n <input\n type=\"checkbox\"\n class=\"accent-brand-500\"\n [checked]=\"allSelected\"\n (change)=\"toggleAll()\"\n aria-label=\"Select all rows\"\n />\n </label>\n </th>\n\n <!-- Data columns -->\n <th\n *ngFor=\"let column of dataSource.columns; trackBy: trackByKey\"\n class=\"text-sm px-4 py-2\"\n [class.cursor-pointer]=\"isSortable(column)\"\n [class.select-none]=\"isSortable(column)\"\n [class.hover:bg-gray-100]=\"isSortable(column)\"\n [class.dark:hover:bg-gray-700]=\"isSortable(column)\"\n [class.text-left]=\"(column.align ?? 'left') === 'left'\"\n [class.text-center]=\"column.align === 'center'\"\n [class.text-right]=\"column.align === 'right'\"\n [class.hidden]=\"column.hiddenBelow === 'sm' || column.hiddenBelow === 'md' || column.hiddenBelow === 'lg'\"\n [class.sm:table-cell]=\"column.hiddenBelow === 'sm'\"\n [class.md:table-cell]=\"column.hiddenBelow === 'md'\"\n [class.lg:table-cell]=\"column.hiddenBelow === 'lg'\"\n [style.width]=\"column.width ?? null\"\n [attr.aria-sort]=\"currentSort?.columnKey === column.key ? (currentSort!.direction === 'asc' ? 'ascending' : 'descending') : null\"\n (click)=\"sort(column)\"\n >\n <span class=\"inline-flex items-center gap-1\">\n <ng-container *ngIf=\"isTemplateRef(column.header); else stringHeader\">\n <ng-container [ngTemplateOutlet]=\"$any(column.header)\"></ng-container>\n </ng-container>\n <ng-template #stringHeader>\n <span>{{ column.header }}</span>\n </ng-template>\n <span *ngIf=\"isSortable(column)\" class=\"text-[0.65rem] opacity-70 min-w-3 inline-block\">{{ getSortIcon(column) }}</span>\n </span>\n </th>\n\n <!-- Actions column header -->\n <th *ngIf=\"hasRowActions\" class=\"text-sm px-4 py-2 text-right\">Actions</th>\n </tr>\n </thead>\n\n <tbody>\n <!-- Loading state -->\n <ng-container *ngIf=\"dataSource.isDataLoading; else content\">\n <tr *ngFor=\"let _ of skeletonRows\" class=\"animate-pulse\">\n <td *ngIf=\"hasSelection\" class=\"px-2 py-3\"><div class=\"h-4 w-4 rounded bg-gray-200 dark:bg-gray-700\"></div></td>\n <td *ngFor=\"let column of dataSource.columns; trackBy: trackByKey\" class=\"px-4 py-3\"\n [class.hidden]=\"column.hiddenBelow === 'sm' || column.hiddenBelow === 'md' || column.hiddenBelow === 'lg'\"\n [class.sm:table-cell]=\"column.hiddenBelow === 'sm'\"\n [class.md:table-cell]=\"column.hiddenBelow === 'md'\"\n [class.lg:table-cell]=\"column.hiddenBelow === 'lg'\"\n >\n <div class=\"h-4 w-3/4 rounded bg-gray-200 dark:bg-gray-700\"></div>\n </td>\n <td *ngIf=\"hasRowActions\" class=\"px-4 py-3\"><div class=\"h-4 w-16 rounded bg-gray-200 dark:bg-gray-700\"></div></td>\n </tr>\n </ng-container>\n\n <!-- Content -->\n <ng-template #content>\n <!-- Empty state -->\n <tr *ngIf=\"filteredItems.length === 0\">\n <td [attr.colspan]=\"totalColumnCount\" class=\"text-center text-xs py-8\">\n <ng-container *ngIf=\"dataSource.emptyTemplate; else defaultEmpty\">\n <ng-container [ngTemplateOutlet]=\"dataSource.emptyTemplate\"></ng-container>\n </ng-container>\n <ng-template #defaultEmpty>\n <div class=\"flex flex-col items-center gap-2 text-gray-400 dark:text-gray-500\">\n <p class=\"text-sm\">{{ dataSource.emptyMessage }}</p>\n </div>\n </ng-template>\n </td>\n </tr>\n\n <!-- Data rows -->\n <tr\n *ngFor=\"let row of filteredItems; trackBy: trackByID; let odd = odd; let last = last\"\n class=\"bg-white transition-colors duration-150 dark:bg-gray-900\"\n [class.bg-brand-50]=\"isSelected(row)\"\n [class.bg-gray-50]=\"!isSelected(row) && odd && dataSource.appearance?.striped\"\n [class.dark:bg-gray-800]=\"!isSelected(row) && odd && dataSource.appearance?.striped\"\n [class.hover:bg-gray-100]=\"dataSource.appearance?.hover !== false\"\n [class.dark:hover:bg-gray-800]=\"dataSource.appearance?.hover !== false\"\n [class.cursor-pointer]=\"!!dataSource.onRowClick\"\n [class.border-b]=\"!last\"\n [class.border-gray-200]=\"!last\"\n [class.dark:border-gray-700]=\"!last\"\n [class.border-b-1]=\"last\"\n [class.border-black]=\"last\"\n [class.shadow-3xl]=\"last\"\n (click)=\"onRowClick(row)\"\n >\n <!-- Selection checkbox -->\n <td *ngIf=\"hasSelection\" class=\"w-10 text-center px-2 py-2\">\n <label>\n <input\n *ngIf=\"isMultiSelect; else radioTpl\"\n type=\"checkbox\"\n class=\"accent-brand-500\"\n [checked]=\"isSelected(row)\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(row)\"\n />\n <ng-template #radioTpl>\n <input\n type=\"radio\"\n class=\"accent-brand-500\"\n [checked]=\"isSelected(row)\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(row)\"\n name=\"mn-table-single-select\"\n />\n </ng-template>\n </label>\n </td>\n\n <!-- Data cells -->\n <td\n *ngFor=\"let column of dataSource.columns; trackBy: trackByKey\"\n class=\"text-xs px-4 py-2\"\n [class.text-left]=\"(column.align ?? 'left') === 'left'\"\n [class.text-center]=\"column.align === 'center'\"\n [class.text-right]=\"column.align === 'right'\"\n [class.hidden]=\"column.hiddenBelow === 'sm' || column.hiddenBelow === 'md' || column.hiddenBelow === 'lg'\"\n [class.sm:table-cell]=\"column.hiddenBelow === 'sm'\"\n [class.md:table-cell]=\"column.hiddenBelow === 'md'\"\n [class.lg:table-cell]=\"column.hiddenBelow === 'lg'\"\n [style.width]=\"column.width ?? null\"\n >\n <ng-container *ngIf=\"isTemplateRef(column.cell); else stringCell\">\n <ng-container [ngTemplateOutlet]=\"$any(column.cell)\" [ngTemplateOutletContext]=\"{ $implicit: row, data: row }\"></ng-container>\n </ng-container>\n <ng-template #stringCell>{{ getCellValue(column, row) }}</ng-template>\n </td>\n\n <!-- Row actions -->\n <td *ngIf=\"hasRowActions\" class=\"text-right px-4 py-2\">\n <div class=\"inline-flex items-center justify-end gap-2\">\n <button\n *ngFor=\"let action of getVisibleActions(row)\"\n class=\"px-2 py-1 text-xs rounded hover:bg-gray-100 transition-colors disabled:opacity-50 disabled:cursor-not-allowed dark:hover:bg-gray-700\"\n [ngClass]=\"action.cssClass ?? ''\"\n [disabled]=\"action.isDisabled ? action.isDisabled(row) : false\"\n (click)=\"$event.stopPropagation(); action.onClick(row)\"\n [attr.aria-label]=\"action.label\"\n >\n <span *ngIf=\"action.icon\" [innerHTML]=\"action.icon\"></span>\n <span *ngIf=\"action.label\">{{ action.label }}</span>\n </button>\n </div>\n </td>\n </tr>\n </ng-template>\n </tbody>\n </table>\n</div>\n\n<!-- Load more button -->\n<div *ngIf=\"showLoadMore\" class=\"flex justify-center py-4\">\n <button\n class=\"px-4 py-1.5 text-sm rounded border border-brand-500 text-brand-500 hover:bg-brand-100 transition-colors disabled:opacity-50\"\n (click)=\"loadMoreRows()\"\n [disabled]=\"loadingMoreRows\"\n >\n <span *ngIf=\"loadingMoreRows\" class=\"inline-block w-3 h-3 border-2 border-brand-500 border-t-transparent rounded-full animate-spin mr-2\"></span>\n Load more\n </button>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
3525
3525
  }
3526
3526
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: MnTable, decorators: [{
3527
3527
  type: Component,
3528
- args: [{ selector: 'mn-table', standalone: true, imports: [NgClass, NgForOf, NgIf, NgTemplateOutlet], template: "<!-- Toolbar: search + custom toolbar template -->\n<div class=\"flex flex-row items-center justify-end gap-2 mb-3\">\n <div *ngIf=\"dataSource.canSearch\">\n <input\n type=\"text\"\n class=\"input input-sm rounded border border-gray-300 bg-gray-100 px-3 py-1.5 text-sm focus:outline-none focus:border-brand-500 w-full max-w-xs dark:bg-gray-800 dark:border-gray-600 dark:text-gray-100 dark:placeholder-gray-400\"\n [placeholder]=\"dataSource.searchPlaceholder ?? 'Search...'\"\n (input)=\"onSearch($any($event.target).value)\"\n aria-label=\"Search table\"\n />\n </div>\n <ng-container *ngIf=\"dataSource.toolbarTemplate\" [ngTemplateOutlet]=\"dataSource.toolbarTemplate\"></ng-container>\n</div>\n\n<!-- Table wrapper with horizontal scroll -->\n<div class=\"overflow-x-auto\" role=\"region\" aria-label=\"Data table\">\n <table [class]=\"tableClasses\">\n <thead>\n <tr>\n <!-- Selection checkbox column header -->\n <th *ngIf=\"hasSelection\" class=\"w-10 text-center text-sm bg-gray-50 px-2 py-2 dark:bg-gray-800\">\n <label *ngIf=\"isMultiSelect\">\n <input\n type=\"checkbox\"\n class=\"accent-brand-500\"\n [checked]=\"allSelected\"\n (change)=\"toggleAll()\"\n aria-label=\"Select all rows\"\n />\n </label>\n </th>\n\n <!-- Data columns -->\n <th\n *ngFor=\"let column of dataSource.columns; trackBy: trackByKey\"\n class=\"text-sm px-4 py-2\"\n [class.cursor-pointer]=\"isSortable(column)\"\n [class.select-none]=\"isSortable(column)\"\n [class.hover:bg-gray-100]=\"isSortable(column)\"\n [class.text-left]=\"(column.align ?? 'left') === 'left'\"\n [class.text-center]=\"column.align === 'center'\"\n [class.text-right]=\"column.align === 'right'\"\n [class.hidden]=\"column.hiddenBelow === 'sm' || column.hiddenBelow === 'md' || column.hiddenBelow === 'lg'\"\n [class.sm:table-cell]=\"column.hiddenBelow === 'sm'\"\n [class.md:table-cell]=\"column.hiddenBelow === 'md'\"\n [class.lg:table-cell]=\"column.hiddenBelow === 'lg'\"\n [style.width]=\"column.width ?? null\"\n [attr.aria-sort]=\"currentSort?.columnKey === column.key ? (currentSort!.direction === 'asc' ? 'ascending' : 'descending') : null\"\n (click)=\"sort(column)\"\n >\n <span class=\"inline-flex items-center gap-1\">\n <ng-container *ngIf=\"isTemplateRef(column.header); else stringHeader\">\n <ng-container [ngTemplateOutlet]=\"$any(column.header)\"></ng-container>\n </ng-container>\n <ng-template #stringHeader>\n <span>{{ column.header }}</span>\n </ng-template>\n <span *ngIf=\"isSortable(column)\" class=\"text-[0.65rem] opacity-70 min-w-3 inline-block\">{{ getSortIcon(column) }}</span>\n </span>\n </th>\n\n <!-- Actions column header -->\n <th *ngIf=\"hasRowActions\" class=\"text-sm px-4 py-2 text-right\">Actions</th>\n </tr>\n </thead>\n\n <tbody>\n <!-- Loading state -->\n <ng-container *ngIf=\"dataSource.isDataLoading; else content\">\n <tr *ngFor=\"let _ of skeletonRows\" class=\"animate-pulse\">\n <td *ngIf=\"hasSelection\" class=\"px-2 py-3\"><div class=\"h-4 w-4 rounded bg-gray-200 dark:bg-gray-700\"></div></td>\n <td *ngFor=\"let column of dataSource.columns; trackBy: trackByKey\" class=\"px-4 py-3\"\n [class.hidden]=\"column.hiddenBelow === 'sm' || column.hiddenBelow === 'md' || column.hiddenBelow === 'lg'\"\n [class.sm:table-cell]=\"column.hiddenBelow === 'sm'\"\n [class.md:table-cell]=\"column.hiddenBelow === 'md'\"\n [class.lg:table-cell]=\"column.hiddenBelow === 'lg'\"\n >\n <div class=\"h-4 w-3/4 rounded bg-gray-200 dark:bg-gray-700\"></div>\n </td>\n <td *ngIf=\"hasRowActions\" class=\"px-4 py-3\"><div class=\"h-4 w-16 rounded bg-gray-200 dark:bg-gray-700\"></div></td>\n </tr>\n </ng-container>\n\n <!-- Content -->\n <ng-template #content>\n <!-- Empty state -->\n <tr *ngIf=\"filteredItems.length === 0\">\n <td [attr.colspan]=\"totalColumnCount\" class=\"text-center text-xs py-8\">\n <ng-container *ngIf=\"dataSource.emptyTemplate; else defaultEmpty\">\n <ng-container [ngTemplateOutlet]=\"dataSource.emptyTemplate\"></ng-container>\n </ng-container>\n <ng-template #defaultEmpty>\n <div class=\"flex flex-col items-center gap-2 text-gray-400 dark:text-gray-500\">\n <p class=\"text-sm\">{{ dataSource.emptyMessage }}</p>\n </div>\n </ng-template>\n </td>\n </tr>\n\n <!-- Data rows -->\n <tr\n *ngFor=\"let row of filteredItems; trackBy: trackByID; let odd = odd; let last = last\"\n class=\"bg-white transition-colors duration-150 dark:bg-gray-900\"\n [class.bg-brand-50]=\"isSelected(row)\"\n [class.bg-gray-50]=\"!isSelected(row) && odd && dataSource.appearance?.striped\"\n [class.hover:bg-gray-100]=\"dataSource.appearance?.hover !== false\"\n [class.cursor-pointer]=\"!!dataSource.onRowClick\"\n [class.border-b]=\"!last\"\n [class.border-gray-200]=\"!last\"\n [class.border-b-1]=\"last\"\n [class.border-black]=\"last\"\n [class.shadow-3xl]=\"last\"\n (click)=\"onRowClick(row)\"\n >\n <!-- Selection checkbox -->\n <td *ngIf=\"hasSelection\" class=\"w-10 text-center px-2 py-2\">\n <label>\n <input\n *ngIf=\"isMultiSelect; else radioTpl\"\n type=\"checkbox\"\n class=\"accent-brand-500\"\n [checked]=\"isSelected(row)\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(row)\"\n />\n <ng-template #radioTpl>\n <input\n type=\"radio\"\n class=\"accent-brand-500\"\n [checked]=\"isSelected(row)\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(row)\"\n name=\"mn-table-single-select\"\n />\n </ng-template>\n </label>\n </td>\n\n <!-- Data cells -->\n <td\n *ngFor=\"let column of dataSource.columns; trackBy: trackByKey\"\n class=\"text-xs px-4 py-2\"\n [class.text-left]=\"(column.align ?? 'left') === 'left'\"\n [class.text-center]=\"column.align === 'center'\"\n [class.text-right]=\"column.align === 'right'\"\n [class.hidden]=\"column.hiddenBelow === 'sm' || column.hiddenBelow === 'md' || column.hiddenBelow === 'lg'\"\n [class.sm:table-cell]=\"column.hiddenBelow === 'sm'\"\n [class.md:table-cell]=\"column.hiddenBelow === 'md'\"\n [class.lg:table-cell]=\"column.hiddenBelow === 'lg'\"\n [style.width]=\"column.width ?? null\"\n >\n <ng-container *ngIf=\"isTemplateRef(column.cell); else stringCell\">\n <ng-container [ngTemplateOutlet]=\"$any(column.cell)\" [ngTemplateOutletContext]=\"{ $implicit: row, data: row }\"></ng-container>\n </ng-container>\n <ng-template #stringCell>{{ getCellValue(column, row) }}</ng-template>\n </td>\n\n <!-- Row actions -->\n <td *ngIf=\"hasRowActions\" class=\"text-right px-4 py-2\">\n <div class=\"inline-flex items-center justify-end gap-2\">\n <button\n *ngFor=\"let action of getVisibleActions(row)\"\n class=\"px-2 py-1 text-xs rounded hover:bg-gray-100 transition-colors disabled:opacity-50 disabled:cursor-not-allowed dark:hover:bg-gray-700\"\n [ngClass]=\"action.cssClass ?? ''\"\n [disabled]=\"action.isDisabled ? action.isDisabled(row) : false\"\n (click)=\"$event.stopPropagation(); action.onClick(row)\"\n [attr.aria-label]=\"action.label\"\n >\n <span *ngIf=\"action.icon\" [innerHTML]=\"action.icon\"></span>\n <span *ngIf=\"action.label\">{{ action.label }}</span>\n </button>\n </div>\n </td>\n </tr>\n </ng-template>\n </tbody>\n </table>\n</div>\n\n<!-- Load more button -->\n<div *ngIf=\"showLoadMore\" class=\"flex justify-center py-4\">\n <button\n class=\"px-4 py-1.5 text-sm rounded border border-brand-500 text-brand-500 hover:bg-brand-100 transition-colors disabled:opacity-50\"\n (click)=\"loadMoreRows()\"\n [disabled]=\"loadingMoreRows\"\n >\n <span *ngIf=\"loadingMoreRows\" class=\"inline-block w-3 h-3 border-2 border-brand-500 border-t-transparent rounded-full animate-spin mr-2\"></span>\n Load more\n </button>\n</div>\n" }]
3528
+ args: [{ selector: 'mn-table', standalone: true, imports: [NgClass, NgForOf, NgIf, NgTemplateOutlet], template: "<!-- Toolbar: search + custom toolbar template -->\n<div class=\"flex flex-row items-center justify-end gap-2 mb-3\">\n <div *ngIf=\"dataSource.canSearch\">\n <input\n type=\"text\"\n class=\"input input-sm rounded border border-gray-300 bg-gray-100 px-3 py-1.5 text-sm focus:outline-none focus:border-brand-500 w-full max-w-xs dark:bg-gray-800 dark:border-gray-600 dark:text-gray-100 dark:placeholder-gray-400\"\n [placeholder]=\"dataSource.searchPlaceholder ?? 'Search...'\"\n (input)=\"onSearch($any($event.target).value)\"\n aria-label=\"Search table\"\n />\n </div>\n <ng-container *ngIf=\"dataSource.toolbarTemplate\" [ngTemplateOutlet]=\"dataSource.toolbarTemplate\"></ng-container>\n</div>\n\n<!-- Table wrapper with horizontal scroll -->\n<div class=\"overflow-x-auto\" role=\"region\" aria-label=\"Data table\">\n <table [class]=\"tableClasses\">\n <thead>\n <tr>\n <!-- Selection checkbox column header -->\n <th *ngIf=\"hasSelection\" class=\"w-10 text-center text-sm bg-gray-50 px-2 py-2 dark:bg-gray-800\">\n <label *ngIf=\"isMultiSelect\">\n <input\n type=\"checkbox\"\n class=\"accent-brand-500\"\n [checked]=\"allSelected\"\n (change)=\"toggleAll()\"\n aria-label=\"Select all rows\"\n />\n </label>\n </th>\n\n <!-- Data columns -->\n <th\n *ngFor=\"let column of dataSource.columns; trackBy: trackByKey\"\n class=\"text-sm px-4 py-2\"\n [class.cursor-pointer]=\"isSortable(column)\"\n [class.select-none]=\"isSortable(column)\"\n [class.hover:bg-gray-100]=\"isSortable(column)\"\n [class.dark:hover:bg-gray-700]=\"isSortable(column)\"\n [class.text-left]=\"(column.align ?? 'left') === 'left'\"\n [class.text-center]=\"column.align === 'center'\"\n [class.text-right]=\"column.align === 'right'\"\n [class.hidden]=\"column.hiddenBelow === 'sm' || column.hiddenBelow === 'md' || column.hiddenBelow === 'lg'\"\n [class.sm:table-cell]=\"column.hiddenBelow === 'sm'\"\n [class.md:table-cell]=\"column.hiddenBelow === 'md'\"\n [class.lg:table-cell]=\"column.hiddenBelow === 'lg'\"\n [style.width]=\"column.width ?? null\"\n [attr.aria-sort]=\"currentSort?.columnKey === column.key ? (currentSort!.direction === 'asc' ? 'ascending' : 'descending') : null\"\n (click)=\"sort(column)\"\n >\n <span class=\"inline-flex items-center gap-1\">\n <ng-container *ngIf=\"isTemplateRef(column.header); else stringHeader\">\n <ng-container [ngTemplateOutlet]=\"$any(column.header)\"></ng-container>\n </ng-container>\n <ng-template #stringHeader>\n <span>{{ column.header }}</span>\n </ng-template>\n <span *ngIf=\"isSortable(column)\" class=\"text-[0.65rem] opacity-70 min-w-3 inline-block\">{{ getSortIcon(column) }}</span>\n </span>\n </th>\n\n <!-- Actions column header -->\n <th *ngIf=\"hasRowActions\" class=\"text-sm px-4 py-2 text-right\">Actions</th>\n </tr>\n </thead>\n\n <tbody>\n <!-- Loading state -->\n <ng-container *ngIf=\"dataSource.isDataLoading; else content\">\n <tr *ngFor=\"let _ of skeletonRows\" class=\"animate-pulse\">\n <td *ngIf=\"hasSelection\" class=\"px-2 py-3\"><div class=\"h-4 w-4 rounded bg-gray-200 dark:bg-gray-700\"></div></td>\n <td *ngFor=\"let column of dataSource.columns; trackBy: trackByKey\" class=\"px-4 py-3\"\n [class.hidden]=\"column.hiddenBelow === 'sm' || column.hiddenBelow === 'md' || column.hiddenBelow === 'lg'\"\n [class.sm:table-cell]=\"column.hiddenBelow === 'sm'\"\n [class.md:table-cell]=\"column.hiddenBelow === 'md'\"\n [class.lg:table-cell]=\"column.hiddenBelow === 'lg'\"\n >\n <div class=\"h-4 w-3/4 rounded bg-gray-200 dark:bg-gray-700\"></div>\n </td>\n <td *ngIf=\"hasRowActions\" class=\"px-4 py-3\"><div class=\"h-4 w-16 rounded bg-gray-200 dark:bg-gray-700\"></div></td>\n </tr>\n </ng-container>\n\n <!-- Content -->\n <ng-template #content>\n <!-- Empty state -->\n <tr *ngIf=\"filteredItems.length === 0\">\n <td [attr.colspan]=\"totalColumnCount\" class=\"text-center text-xs py-8\">\n <ng-container *ngIf=\"dataSource.emptyTemplate; else defaultEmpty\">\n <ng-container [ngTemplateOutlet]=\"dataSource.emptyTemplate\"></ng-container>\n </ng-container>\n <ng-template #defaultEmpty>\n <div class=\"flex flex-col items-center gap-2 text-gray-400 dark:text-gray-500\">\n <p class=\"text-sm\">{{ dataSource.emptyMessage }}</p>\n </div>\n </ng-template>\n </td>\n </tr>\n\n <!-- Data rows -->\n <tr\n *ngFor=\"let row of filteredItems; trackBy: trackByID; let odd = odd; let last = last\"\n class=\"bg-white transition-colors duration-150 dark:bg-gray-900\"\n [class.bg-brand-50]=\"isSelected(row)\"\n [class.bg-gray-50]=\"!isSelected(row) && odd && dataSource.appearance?.striped\"\n [class.dark:bg-gray-800]=\"!isSelected(row) && odd && dataSource.appearance?.striped\"\n [class.hover:bg-gray-100]=\"dataSource.appearance?.hover !== false\"\n [class.dark:hover:bg-gray-800]=\"dataSource.appearance?.hover !== false\"\n [class.cursor-pointer]=\"!!dataSource.onRowClick\"\n [class.border-b]=\"!last\"\n [class.border-gray-200]=\"!last\"\n [class.dark:border-gray-700]=\"!last\"\n [class.border-b-1]=\"last\"\n [class.border-black]=\"last\"\n [class.shadow-3xl]=\"last\"\n (click)=\"onRowClick(row)\"\n >\n <!-- Selection checkbox -->\n <td *ngIf=\"hasSelection\" class=\"w-10 text-center px-2 py-2\">\n <label>\n <input\n *ngIf=\"isMultiSelect; else radioTpl\"\n type=\"checkbox\"\n class=\"accent-brand-500\"\n [checked]=\"isSelected(row)\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(row)\"\n />\n <ng-template #radioTpl>\n <input\n type=\"radio\"\n class=\"accent-brand-500\"\n [checked]=\"isSelected(row)\"\n (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRow(row)\"\n name=\"mn-table-single-select\"\n />\n </ng-template>\n </label>\n </td>\n\n <!-- Data cells -->\n <td\n *ngFor=\"let column of dataSource.columns; trackBy: trackByKey\"\n class=\"text-xs px-4 py-2\"\n [class.text-left]=\"(column.align ?? 'left') === 'left'\"\n [class.text-center]=\"column.align === 'center'\"\n [class.text-right]=\"column.align === 'right'\"\n [class.hidden]=\"column.hiddenBelow === 'sm' || column.hiddenBelow === 'md' || column.hiddenBelow === 'lg'\"\n [class.sm:table-cell]=\"column.hiddenBelow === 'sm'\"\n [class.md:table-cell]=\"column.hiddenBelow === 'md'\"\n [class.lg:table-cell]=\"column.hiddenBelow === 'lg'\"\n [style.width]=\"column.width ?? null\"\n >\n <ng-container *ngIf=\"isTemplateRef(column.cell); else stringCell\">\n <ng-container [ngTemplateOutlet]=\"$any(column.cell)\" [ngTemplateOutletContext]=\"{ $implicit: row, data: row }\"></ng-container>\n </ng-container>\n <ng-template #stringCell>{{ getCellValue(column, row) }}</ng-template>\n </td>\n\n <!-- Row actions -->\n <td *ngIf=\"hasRowActions\" class=\"text-right px-4 py-2\">\n <div class=\"inline-flex items-center justify-end gap-2\">\n <button\n *ngFor=\"let action of getVisibleActions(row)\"\n class=\"px-2 py-1 text-xs rounded hover:bg-gray-100 transition-colors disabled:opacity-50 disabled:cursor-not-allowed dark:hover:bg-gray-700\"\n [ngClass]=\"action.cssClass ?? ''\"\n [disabled]=\"action.isDisabled ? action.isDisabled(row) : false\"\n (click)=\"$event.stopPropagation(); action.onClick(row)\"\n [attr.aria-label]=\"action.label\"\n >\n <span *ngIf=\"action.icon\" [innerHTML]=\"action.icon\"></span>\n <span *ngIf=\"action.label\">{{ action.label }}</span>\n </button>\n </div>\n </td>\n </tr>\n </ng-template>\n </tbody>\n </table>\n</div>\n\n<!-- Load more button -->\n<div *ngIf=\"showLoadMore\" class=\"flex justify-center py-4\">\n <button\n class=\"px-4 py-1.5 text-sm rounded border border-brand-500 text-brand-500 hover:bg-brand-100 transition-colors disabled:opacity-50\"\n (click)=\"loadMoreRows()\"\n [disabled]=\"loadingMoreRows\"\n >\n <span *ngIf=\"loadingMoreRows\" class=\"inline-block w-3 h-3 border-2 border-brand-500 border-t-transparent rounded-full animate-spin mr-2\"></span>\n Load more\n </button>\n</div>\n" }]
3529
3529
  }], propDecorators: { dataSource: [{
3530
3530
  type: Input
3531
3531
  }], sortChange: [{