@shival99/z-ui 1.0.10 → 1.0.12
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.
- package/fesm2022/shival99-z-ui-components-z-calendar.mjs +19 -2
- package/fesm2022/shival99-z-ui-components-z-calendar.mjs.map +1 -1
- package/fesm2022/shival99-z-ui-components-z-table.mjs +2 -2
- package/fesm2022/shival99-z-ui-components-z-table.mjs.map +1 -1
- package/package.json +1 -1
- package/types/shival99-z-ui-components-z-calendar.d.ts +5 -5
- package/types/shival99-z-ui-components-z-input.d.ts +3 -3
|
@@ -2574,7 +2574,7 @@ class ZTableComponent {
|
|
|
2574
2574
|
});
|
|
2575
2575
|
}
|
|
2576
2576
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2577
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZTableComponent, isStandalone: true, selector: "z-table", inputs: { zConfig: { classPropertyName: "zConfig", publicName: "zConfig", isSignal: true, isRequired: false, transformFunction: null }, zLoading: { classPropertyName: "zLoading", publicName: "zLoading", isSignal: true, isRequired: false, transformFunction: null }, zKey: { classPropertyName: "zKey", publicName: "zKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zChange: "zChange", zControl: "zControl" }, host: { classAttribute: "z-table block relative" }, viewQueries: [{ propertyName: "theadWrapper", first: true, predicate: ["theadWrapper"], descendants: true, isSignal: true }, { propertyName: "tbodyContainer", first: true, predicate: ["tbodyContainer"], descendants: true, isSignal: true }, { propertyName: "tbodyWrapper", first: true, predicate: ["tbodyWrapper"], descendants: true, isSignal: true }, { propertyName: "tbodyScrollbar", first: true, predicate: ["tbodyWrapper"], descendants: true, isSignal: true }, { propertyName: "tfootWrapper", first: true, predicate: ["tfootWrapper"], descendants: true, isSignal: true }, { propertyName: "expandedRowTemplate", first: true, predicate: ["expandedRowTemplate"], descendants: true, isSignal: true }], exportAs: ["zTable"], ngImport: i0, template: "<!-- Toolbar: Search & Settings -->\n@if (zConfig().enableSearch || zConfig().enableSettings) {\n <div class=\"z-table-toolbar mb-2 flex items-center justify-between gap-4\">\n <!-- Search -->\n @if (zConfig().enableSearch) {\n <z-input\n class=\"w-64\"\n zSize=\"sm\"\n zPlaceholder=\"Search...\"\n [zSearch]=\"true\"\n [zDebounce]=\"300\"\n (zOnSearch)=\"onSearchChange($event)\" />\n } @else {\n <div></div>\n }\n\n <!-- Settings Button -->\n @if (zConfig().enableSettings) {\n <z-button zType=\"outline\" zSize=\"sm\" zTypeIcon=\"lucideSettings\" (click)=\"openSettingsDrawer()\">Settings</z-button>\n }\n </div>\n}\n\n<div\n class=\"z-table-container shadow-xs\"\n [class.z-hide-horizontal-border]=\"!showHorizontalBorder()\"\n [class.z-hide-vertical-border]=\"!showVerticalBorder()\"\n [style.max-height]=\"zConfig().maxHeight\"\n [style.min-height]=\"zConfig().minHeight\">\n <!-- Shared colgroup template -->\n <ng-template #colGroupTpl>\n <colgroup>\n @for (column of orderedLeafColumns(); track column.id) {\n @if (column.getIsVisible()) {\n <col [style.width]=\"'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n }\n </colgroup>\n </ng-template>\n\n <!-- Header table -->\n <div\n class=\"z-thead-wrapper shadow-xs\"\n [class.z-shadow-header]=\"shouldHeaderShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #theadWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <thead>\n @for (headerGroup of orderedHeaderGroups(); track headerGroup.id) {\n <tr>\n @for (header of headerGroup.headers; track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableHeaderRender: headerGroup.headers : zConfig().columns;\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"header.column | zTablePinningStyles: header : 'header' : table : zConfig().columns\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass'\"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableHeaderPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableHeaderPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableHeaderPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"header.column.id === 'actions'\"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'select') {\n <!-- Header Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\"\n (zChange)=\"table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <!-- Expand All Button -->\n <div class=\"flex items-center justify-center\">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else if (header.column.id === 'actions') {\n <!-- Empty header for actions column -->\n <div class=\"flex items-center justify-center\"></div>\n } @else {\n <!-- Header Content with Sort and Pin -->\n <div class=\"flex w-full items-center gap-1\">\n <!-- Header Text -->\n <div\n class=\"flex min-w-0 flex-1 items-center gap-1.5\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-right'\n \">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n } @else if (headerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\" />\n } @else {\n <!-- Default/innerHTML rendering -->\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [innerHTML]=\"headerContent | zSafeHtml\"></span>\n }\n </ng-container>\n <!-- Sort Icon (clickable) - hidden when table has rowSpan columns -->\n @if (header.column.getCanSort() && !hasBodyRowSpan()) {\n <span\n class=\"shrink-0 cursor-pointer text-gray-500 hover:text-gray-700\"\n (click)=\"handleSort($event, header.column.getToggleSortingHandler())\">\n @if (header.column.getIsSorted() === 'asc') {\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n } @else if (header.column.getIsSorted() === 'desc') {\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n } @else {\n <z-icon zType=\"lucideArrowDownUp\" zSize=\"12\" class=\"opacity-30\" />\n }\n </span>\n }\n </div>\n\n <!-- Column Pin Popover -->\n @if (header.column.getCanPin() && zConfig().enableColumnPinning) {\n <ng-template #colPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideChevronLeft\" zSize=\"12\" />\n <span>Pin left</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideChevronRight\" zSize=\"12\" />\n <span>Pin right</span>\n </button>\n }\n @if (header.column.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, false); colPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>Unpin</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #colPinPopover=\"zPopover\"\n [zPopoverContent]=\"colPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom-right\"\n [zOffset]=\"3\"\n class=\"z-header-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex shrink-0 cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"header.column.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"12\" class=\"rotate-90\" />\n </button>\n }\n </div>\n }\n <!-- Column Filter -->\n @if (header.column.getCanFilter() && hasFiltering()) {\n <div class=\"mt-1\">\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n </div>\n }\n <!-- Column Resizer -->\n @if (\n header.column.id !== 'select' &&\n header.column.id !== 'expand' &&\n zConfig().enableColumnResizing !== false\n ) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n (mousedown)=\"header.getResizeHandler()($event)\"\n (touchstart)=\"header.getResizeHandler()($event)\"></div>\n }\n </th>\n }\n }\n </tr>\n }\n </thead>\n </table>\n </div>\n\n <!-- Body table -->\n <div\n class=\"z-tbody-wrapper relative\"\n #tbodyContainer\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\">\n @if (zLoading() || isProcessing()) {\n <!-- Loading State -->\n <div class=\"z-loading-state\">\n <z-loading [zLoading]=\"true\" zSize=\"lg\" zText=\"Loading...\" />\n </div>\n } @else if (isEmpty()) {\n <div class=\"z-empty-state\">\n @if (isNoSearchResults()) {\n <!-- No search results -->\n <z-icon zType=\"lucideSearchX\" zSize=\"40\" class=\"text-muted-foreground/50\" />\n <p class=\"text-muted-foreground text-sm\">No results found</p>\n } @else {\n <!-- Empty data -->\n <z-icon zType=\"lucidePackageOpen\" zSize=\"40\" class=\"text-muted-foreground/50\" />\n <p class=\"text-muted-foreground text-sm\">No data</p>\n }\n </div>\n } @else {\n <ng-scrollbar class=\"z-tbody-scrollbar\" #tbodyWrapper track=\"all\" (scroll)=\"onTbodyScroll($event)\">\n @if (isVirtual()) {\n <!-- Virtual Scroll Mode -->\n <div\n class=\"z-virtual-scroll-inner\"\n [style.height.px]=\"virtualizer.getTotalSize()\"\n [style.min-width.px]=\"table.getTotalSize()\">\n @for (virtualItem of virtualizer.getVirtualItems(); track virtualItem.index) {\n @let groupRows = dynamicGroupRows()[virtualItem.index] || [];\n <div\n class=\"z-virtual-row\"\n [style.height.px]=\"dynamicGroupHeights()[virtualItem.index] ?? groupSize() * virtualRowHeight()\"\n [style.transform]=\"'translateY(' + virtualItem.start + 'px)'\">\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody [class.z-has-vertical-scroll]=\"hasVerticalScroll()\">\n @for (row of groupRows; track row.id) {\n <tr\n z-table-row-hover\n [style.height.px]=\"virtualRowHeight()\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-selected]=\"row.getIsSelected()\"\n [class.z-indeterminate-selected]=\"row.getIsSomeSelected() && !row.getIsSelected()\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan =\n cell | zTableBodyCellRender: row.getVisibleCells() : zConfig().columns;\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles: cell : 'body' : row.getVisibleCells() : zConfig().columns\n \"\n [class.z-sticky-left]=\"cell.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"cell.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n cell.column.getIsPinned() === 'left' && cell.column.getIsLastColumn('left')\n \"\n [class.z-sticky-right-first]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [attr.rowspan]=\"\n cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\n \"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\"\n [class]=\"cell | zTableCellConfig: zConfig().columns : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig().columns : 'cellStyle'\">\n @if (cell.column.id === 'select') {\n <!-- Row Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"cell.row.getIsSelected()\"\n [zIndeterminate]=\"cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\"\n [zDisabled]=\"!cell.row.getCanSelect()\"\n (zChange)=\"cell.row.toggleSelected()\" />\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button -->\n <div class=\"flex items-center justify-center\">\n @if (cell.row.subRows && cell.row.subRows.length > 0) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n </div>\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig().columns : 'contentTooltip'\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\" />\n } @else {\n <!-- Default/innerHTML rendering -->\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"></div>\n }\n </ng-container>\n }\n </td>\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n } @else {\n <!-- Normal Scroll Mode -->\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody [class.z-has-vertical-scroll]=\"hasVerticalScroll()\">\n <!-- Row Template -->\n <ng-template #rowTemplate let-row>\n <tr\n z-table-row-hover\n [attr.data-row-id]=\"row.id\"\n [ngStyle]=\"row | zTableRow: table : 'pinningStyles' : virtualRowHeight()\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-selected]=\"row.getIsSelected()\"\n [class.z-indeterminate-selected]=\"row.getIsSomeSelected() && !row.getIsSelected()\"\n [class.z-pinned-top]=\"row.getIsPinned() === 'top'\"\n [class.z-pinned-bottom]=\"row.getIsPinned() === 'bottom'\"\n [class.z-shadow-bottom]=\"\n showHeaderFooterShadow() &&\n row.getIsPinned() === 'top' &&\n (row | zTableRow: table : 'isLastTopPinned')\n \"\n [class.z-shadow-top]=\"\n showHeaderFooterShadow() &&\n row.getIsPinned() === 'bottom' &&\n (row | zTableRow: table : 'isFirstBottomPinned')\n \"\n [attr.data-depth]=\"row.depth\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan = cell | zTableBodyCellRender: row.getVisibleCells() : zConfig().columns;\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column | zTablePinningStyles: cell : 'body' : row.getVisibleCells() : zConfig().columns\n \"\n [class]=\"cell | zTableCellConfig: zConfig().columns : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig().columns : 'cellStyle'\"\n [class.z-sticky-left]=\"cell.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"cell.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n cell.column.getIsPinned() === 'left' && cell.column.getIsLastColumn('left')\n \"\n [class.z-sticky-right-first]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"cell.column.id === 'actions'\"\n [attr.rowspan]=\"cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\">\n @if (cell.column.id === 'select') {\n <!-- Row Checkbox with Pin Button -->\n <div class=\"flex items-center justify-center gap-1\">\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"cell.row.getIsSelected()\"\n [zIndeterminate]=\"cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\"\n [zDisabled]=\"!cell.row.getCanSelect()\"\n (zChange)=\"cell.row.toggleSelected()\" />\n @if (zConfig().enableRowPinning && cell.row.depth === 0 && !hasBodyRowSpan()) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>Pin top</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>Pin bottom</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>Unpin</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button with Row Pin Popover -->\n <div class=\"flex items-center justify-center gap-1\">\n @if (cell.row.subRows && cell.row.subRows.length > 0) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n @if (\n zConfig().enableRowPinning &&\n cell.row.depth === 0 &&\n !(cell.row.subRows && cell.row.subRows.length > 0) &&\n !hasBodyRowSpan()\n ) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>Pin top</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>Pin bottom</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>Unpin</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'actions') {\n <!-- Actions Column - Row Pin Only (for parent rows) -->\n @if (cell.row.depth === 0 && !hasBodyRowSpan()) {\n <div class=\"flex items-center justify-center\">\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>Pin top</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>Pin bottom</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>Unpin</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n </div>\n }\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else {\n <!-- Default/innerHTML rendering -->\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"></div>\n }\n </ng-container>\n }\n </td>\n }\n }\n </tr>\n\n <!-- Expanded Row Detail -->\n @if (row.getIsExpanded() && row.depth === 0 && !row.subRows?.length && zConfig().expandedRowTemplate) {\n <tr class=\"z-expanded-row\">\n <td [attr.colspan]=\"row.getVisibleCells().length\" class=\"p-0\">\n <ng-container *ngTemplateOutlet=\"zConfig().expandedRowTemplate!; context: { $implicit: row }\" />\n </td>\n </tr>\n }\n </ng-template>\n\n <!-- Render Top Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of table.getTopRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n\n <!-- Render Center Rows -->\n @for (row of table.getCenterRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n\n <!-- Render Bottom Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of bottomRowsReversed(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n </tbody>\n </table>\n }\n </ng-scrollbar>\n }\n <!-- end @else -->\n </div>\n\n <!-- Footer table -->\n @if (hasFooter()) {\n <div\n class=\"z-tfoot-wrapper\"\n [class.z-shadow-footer]=\"shouldFooterShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #tfootWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tfoot>\n @for (footerGroup of orderedFooterGroups(); track footerGroup.id) {\n @if (footerGroup | zTableFooterContent: zConfig().columns) {\n <tr>\n @for (footer of footerGroup.headers; track footer.id) {\n @let rowSpan = footer | zTableSpan: zConfig().columns : 'footerRowSpan';\n @let shouldRender = footer | zTableFooterRender: footerGroup.headers : zConfig().columns;\n @if (rowSpan && shouldRender) {\n <th\n [ngStyle]=\"footer.column | zTablePinningStyles: footer : 'header' : table : zConfig().columns\"\n [class]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerClass') +\n ' ' +\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass')\n \"\n [style]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerStyle'\"\n [class.z-sticky-left]=\"footer.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"footer.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"footer | zTableHeaderPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"footer | zTableHeaderPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"footer | zTableHeaderPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"footer | zTableCellOffset: orderedLeafColumns()\"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"footer | zTableSpan: zConfig().columns : 'footerColSpan'\">\n @let configFooterContent =\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContent';\n @if (footer.column.columnDef.footer) {\n <ng-container\n *flexRender=\"footer.column.columnDef.footer; props: footer.getContext(); let footerContent\">\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n @if (footerContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <ng-container\n *ngTemplateOutlet=\"footerContent; context: { $implicit: footer.getContext() }\" />\n } @else if (footerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"footerContent\"\n [zTooltip]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip'\n \" />\n } @else {\n <!-- Default/string rendering -->\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n footerContent\n \">\n {{ footerContent }}\n </span>\n }\n </div>\n </ng-container>\n } @else if (configFooterContent) {\n <!-- Fallback for group columns without TanStack footer -->\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n $any(configFooterContent)\n \">\n {{ configFooterContent }}\n </span>\n </div>\n }\n </th>\n }\n }\n </tr>\n }\n }\n </tfoot>\n </table>\n </div>\n }\n</div>\n\n<!-- Pagination -->\n@if (zConfig().enablePagination && zConfig().pagination?.enabled !== false) {\n @let totalRows = table.getFilteredRowModel().rows.length;\n @let totalLabel = zConfig().pagination?.totalLabel ?? '{total} total rows';\n <div class=\"mt-4 flex items-center justify-between gap-4\">\n <div class=\"text-sm text-gray-500\">\n {{ totalLabel.replace('{total}', zConfig().totalCount?.toString() || totalRows.toString()) }}\n </div>\n <z-pagination\n [zTotal]=\"zConfig().totalCount ?? totalRows\"\n [(zPageIndex)]=\"pagination().pageIndex\"\n [(zPageSize)]=\"pagination().pageSize\"\n [zPageSizeOptions]=\"zConfig().pagination?.pageSizeOptions ?? [10, 20, 50, 100]\"\n [zShowSizeChanger]=\"zConfig().pagination?.showSizeChanger ?? true\"\n [zShowQuickJumper]=\"zConfig().pagination?.showQuickJumper ?? false\"\n [zShowTotal]=\"false\"\n [zDisabled]=\"zConfig().pagination?.disabled || zLoading() || isProcessing()\"\n (zOnPageChange)=\"onPageChange($event)\" />\n </div>\n}\n\n<!-- Settings Drawer -->\n<z-drawer\n [(zVisible)]=\"showSettingsDrawer\"\n zTitle=\"Table Settings\"\n zPlacement=\"right\"\n zWidth=\"500px\"\n [zShadow]=\"true\"\n [zOkText]=\"null\"\n zCancelText=\"Close\">\n <div class=\"z-table-settings-drawer px-4\">\n <!-- Display Settings -->\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">Display Settings</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">Customize table appearance</p>\n <div class=\"grid grid-cols-2 gap-x-4 gap-y-3\">\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"showHorizontalBorder()\"\n zText=\"Horizontal Border\"\n (zChange)=\"showHorizontalBorder.set(!showHorizontalBorder())\" />\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"showVerticalBorder()\"\n zText=\"Vertical Border\"\n (zChange)=\"showVerticalBorder.set(!showVerticalBorder())\" />\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"showHeaderFooterShadow()\"\n zText=\"Header/Footer Shadow\"\n (zChange)=\"showHeaderFooterShadow.set(!showHeaderFooterShadow())\" />\n </div>\n </div>\n\n <!-- Divider -->\n <div class=\"border-border my-4 border-t\"></div>\n\n <!-- Unified Column Settings -->\n @if (zConfig().enableSettings) {\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">Column Settings</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">Toggle visibility, drag to reorder, and pin columns</p>\n\n <!-- Unpinned Columns (Draggable) -->\n <div cdkDropList (cdkDropListDropped)=\"onPendingColumnDrop($event)\" class=\"space-y-1.5\">\n @for (columnId of columnOrder(); track columnId; let i = $index) {\n @if (columnId !== 'expand' && columnId !== 'select') {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = columnVisibility()[columnId] !== false;\n @let canPin = column?.getCanPin() !== false && zConfig().enableColumnPinning;\n @if (!isPinned) {\n <div\n cdkDrag\n [cdkDragData]=\"columnId\"\n cdkDragLockAxis=\"y\"\n [cdkDragDisabled]=\"false\"\n class=\"border-border bg-card hover:border-primary flex cursor-move items-center gap-2 rounded border px-2 py-1.5 text-sm transition-all hover:shadow-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Drag Handle -->\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n class=\"border-input h-4 w-4 shrink-0 cursor-pointer rounded\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns }}\n </span>\n @let parents = columnId | zTableColumnParents: zConfig().columns;\n @if (parents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ parents }})</span>\n }\n </span>\n\n <!-- Pin Buttons -->\n @if (canPin) {\n <div class=\"flex shrink-0 items-center gap-0.5\">\n <button\n type=\"button\"\n (click)=\"onToggleColumnPin(columnId, 'left')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors\"\n title=\"Pin Left\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"12\" />\n </button>\n <button\n type=\"button\"\n (click)=\"onToggleColumnPin(columnId, 'right')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors\"\n title=\"Pin Right\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n }\n }\n }\n </div>\n\n <!-- Pinned Columns Section -->\n @if (zConfig().enableColumnPinning) {\n @if (pinnedColumnIds().length > 0) {\n <div class=\"border-border mt-4 border-t pt-4\">\n <h5 class=\"text-muted-foreground mb-2 text-xs font-medium\">Pinned Columns</h5>\n <div class=\"space-y-1.5\">\n @for (columnId of pinnedColumnIds(); track columnId) {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = columnVisibility()[columnId] !== false;\n <div\n class=\"border-border bg-muted/30 flex items-center gap-2 rounded border px-2 py-1.5 text-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Pin Icon -->\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"text-primary shrink-0\" />\n\n <!-- Visibility Checkbox -->\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n class=\"border-input h-4 w-4 shrink-0 cursor-pointer rounded\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns }}\n </span>\n @let pinnedParents = columnId | zTableColumnParents: zConfig().columns;\n @if (pinnedParents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ pinnedParents }})</span>\n }\n </span>\n\n <!-- Position Badge -->\n <span class=\"bg-primary/10 text-primary shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium\">\n {{ isPinned === 'left' ? 'LEFT' : 'RIGHT' }}\n </span>\n\n <!-- Unpin Button -->\n <button\n type=\"button\"\n (click)=\"onToggleColumnPin(columnId, isPinned === 'left' ? 'left' : 'right')\"\n class=\"text-muted-foreground hover:bg-muted hover:text-foreground cursor-pointer rounded p-1 text-xs transition-colors\"\n title=\"Unpin\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n </div>\n }\n }\n </div>\n }\n </div>\n</z-drawer>\n", styles: [":host{display:flex;flex-direction:column;height:100%;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px}.z-table-container{display:flex;flex-direction:column;position:relative;width:100%;height:100%;overflow:hidden;border-radius:8px;border:thin solid var(--border);background-color:var(--card)}.z-table-container.z-hide-horizontal-border th,.z-table-container.z-hide-horizontal-border td{border-bottom:none!important;border-top:none!important}.z-table-container.z-hide-vertical-border th,.z-table-container.z-hide-vertical-border td{border-left:none!important}table{width:fit-content;min-width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed;font-size:14px}.z-table-toolbar .z-settings-btn{transition:all .15s ease}.z-table-toolbar .z-settings-btn:hover{border-color:var(--muted-foreground)}.z-table-cell-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;-webkit-user-select:text;user-select:text}.z-thead-wrapper{background:var(--muted);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.z-thead-wrapper::-webkit-scrollbar{display:none}.z-thead-wrapper th{height:auto;padding:8px 12px;text-align:left;vertical-align:middle;font-weight:500;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--muted);border-left:thin solid var(--border);border-bottom:thin solid var(--border);-webkit-user-select:none;user-select:none}.z-thead-wrapper th.z-at-left-edge{border-left:none}.z-thead-wrapper th[colspan]{text-align:center;background:var(--muted);font-weight:500;color:var(--foreground)}.z-thead-wrapper.z-shadow-header{box-shadow:0 1px 3px #00000014;position:relative;z-index:15}.z-thead-wrapper.z-shadow-header:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d}.z-tbody-wrapper{flex:1;min-height:200px;display:flex;flex-direction:column}.z-tbody-scrollbar{flex:1;height:100%}.z-empty-state,.z-loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;min-height:200px;height:100%;color:var(--muted-foreground);font-size:.875rem;animation:z-fade-in .2s ease-out}.z-tbody-scrollbar,.z-tbody-scrollbar table{animation:z-fade-in .2s ease-out}@keyframes z-fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.z-tbody-wrapper tr{transition:background-color .15s ease}.z-tbody-wrapper tr:hover,.z-tbody-wrapper tr:hover td[style*=sticky]{background-color:var(--muted)}.z-tbody-wrapper tr.z-pinned-top td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-top td.z-sticky-right,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-right{z-index:3}.z-tbody-wrapper tr.z-shadow-bottom{box-shadow:0 1px 3px #00000014!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-bottom:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d!important}.z-tbody-wrapper tr.z-shadow-top{box-shadow:0 -2px 4px #0000000d!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-top:where(.dark,.dark *){box-shadow:0 -2px 4px #0003!important}.z-tbody-wrapper td{padding:8px 12px;height:40px;vertical-align:middle;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--card);border-left:thin solid var(--border);border-bottom:thin solid var(--border);box-sizing:border-box}.z-tbody-wrapper td.z-at-left-edge{border-left:none}.z-tbody-wrapper td i{color:var(--muted-foreground);font-style:italic}.z-tbody-wrapper td[rowspan]{vertical-align:top;padding-top:12px}.z-tbody-wrapper td.z-row-hover{background-color:var(--muted)!important}.z-tbody-wrapper td.z-col-select,.z-tbody-wrapper td.z-col-expand,.z-tbody-wrapper td.z-col-actions{padding:8px 4px!important;text-align:center}.z-tbody-wrapper tr.z-child-row td.z-col-select:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-expand:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-actions:first-child{padding-left:0!important}.z-virtual-scroll-inner{position:relative;width:100%}.z-virtual-row{position:absolute;top:0;left:0;width:100%}tr.z-child-row td:first-child{padding-left:12px!important}tbody tr.z-selected,tbody tr.z-selected td{background-color:color-mix(in srgb,var(--primary) 15%,transparent)!important}tbody tr.z-selected:hover,tbody tr.z-selected:hover td{background-color:color-mix(in srgb,var(--primary) 20%,transparent)!important}tbody tr.z-indeterminate-selected,tbody tr.z-indeterminate-selected td{background-color:color-mix(in srgb,var(--primary) 10%,transparent)!important}tbody tr.z-indeterminate-selected:hover,tbody tr.z-indeterminate-selected:hover td{background-color:color-mix(in srgb,var(--primary) 15%,transparent)!important}tbody tr.z-pinned-top td{background-color:var(--card)!important}tbody tr.z-pinned-top:hover{background-color:var(--muted)}tbody tr.z-pinned-top:hover td{background-color:var(--muted)!important}tbody tr.z-pinned-bottom td{background-color:var(--card)!important}tbody tr.z-pinned-bottom:hover{background-color:var(--muted)}tbody tr.z-pinned-bottom:hover td,tr.z-expanded-row td{background-color:var(--muted)!important}thead th{position:relative}thead th .z-resizer{position:absolute;right:0;top:0;height:100%;width:5px;background:#0000001a;cursor:col-resize;-webkit-user-select:none;user-select:none;touch-action:none;opacity:0;transition:opacity .2s ease}thead th .z-resizer:where(.dark,.dark *){background:#ffffff1a}thead th .z-resizer:hover,thead th .z-resizer.z-is-resizing{opacity:1;background:var(--primary);opacity:.5}thead th .z-resizer.z-is-resizing{background:var(--primary);opacity:1}thead th .z-resizer.z-resizer-left{right:auto;left:0}thead th:hover .z-resizer{opacity:1}.z-thead-wrapper th.z-sticky-left,.z-thead-wrapper th.z-sticky-right,.z-tbody-wrapper th.z-sticky-left,.z-tbody-wrapper th.z-sticky-right,.z-tfoot-wrapper th.z-sticky-left,.z-tfoot-wrapper th.z-sticky-right{background-color:var(--muted);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper td.z-sticky-left,.z-thead-wrapper td.z-sticky-right,.z-tbody-wrapper td.z-sticky-left,.z-tbody-wrapper td.z-sticky-right,.z-tfoot-wrapper td.z-sticky-left,.z-tfoot-wrapper td.z-sticky-right{background-color:var(--card);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper th.z-sticky-left-last,.z-thead-wrapper td.z-sticky-left-last,.z-tbody-wrapper th.z-sticky-left-last,.z-tbody-wrapper td.z-sticky-left-last,.z-tfoot-wrapper th.z-sticky-left-last,.z-tfoot-wrapper td.z-sticky-left-last{position:relative;overflow:visible}.z-thead-wrapper th.z-sticky-left-last:after,.z-thead-wrapper td.z-sticky-left-last:after,.z-tbody-wrapper th.z-sticky-left-last:after,.z-tbody-wrapper td.z-sticky-left-last:after,.z-tfoot-wrapper th.z-sticky-left-last:after,.z-tfoot-wrapper td.z-sticky-left-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-30px;width:30px;pointer-events:none;box-shadow:inset 10px 0 8px -8px #0000001a;z-index:10}.z-thead-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-thead-wrapper td.z-sticky-left-last:after:where(.dark,.dark *),.z-tbody-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-tbody-wrapper td.z-sticky-left-last:after:where(.dark,.dark *),.z-tfoot-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-tfoot-wrapper td.z-sticky-left-last:after:where(.dark,.dark *){box-shadow:inset 10px 0 8px -8px #0000004d}.z-thead-wrapper th.z-sticky-left-last:after,.z-thead-wrapper td.z-sticky-left-last:after,.z-tbody-wrapper th.z-sticky-left-last:after,.z-tbody-wrapper td.z-sticky-left-last:after,.z-tfoot-wrapper th.z-sticky-left-last:after,.z-tfoot-wrapper td.z-sticky-left-last:after{opacity:0;transition:opacity .2s ease}.z-thead-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-thead-wrapper.z-scroll-left td.z-sticky-left-last:after,.z-tbody-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-tbody-wrapper.z-scroll-left td.z-sticky-left-last:after,.z-tfoot-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-tfoot-wrapper.z-scroll-left td.z-sticky-left-last:after{opacity:1}.z-thead-wrapper th.z-sticky-right-first,.z-thead-wrapper td.z-sticky-right-first,.z-tbody-wrapper th.z-sticky-right-first,.z-tbody-wrapper td.z-sticky-right-first,.z-tfoot-wrapper th.z-sticky-right-first,.z-tfoot-wrapper td.z-sticky-right-first{position:relative;overflow:visible}.z-thead-wrapper th.z-sticky-right-first:before,.z-thead-wrapper td.z-sticky-right-first:before,.z-tbody-wrapper th.z-sticky-right-first:before,.z-tbody-wrapper td.z-sticky-right-first:before,.z-tfoot-wrapper th.z-sticky-right-first:before,.z-tfoot-wrapper td.z-sticky-right-first:before{content:\"\";position:absolute;top:0;bottom:0;left:-30px;width:30px;pointer-events:none;box-shadow:inset -10px 0 8px -8px #0000001a;z-index:10;opacity:0;transition:opacity .2s ease}.z-thead-wrapper th.z-sticky-right-first:before:where(.dark,.dark *),.z-thead-wrapper td.z-sticky-right-first:before:where(.dark,.dark *),.z-tbody-wrapper th.z-sticky-right-first:before:where(.dark,.dark *),.z-tbody-wrapper td.z-sticky-right-first:before:where(.dark,.dark *),.z-tfoot-wrapper th.z-sticky-right-first:before:where(.dark,.dark *),.z-tfoot-wrapper td.z-sticky-right-first:before:where(.dark,.dark *){box-shadow:inset -10px 0 8px -8px #0000004d}.z-thead-wrapper.z-scroll-right th.z-sticky-right-first,.z-thead-wrapper.z-scroll-right td.z-sticky-right-first,.z-tbody-wrapper.z-scroll-right th.z-sticky-right-first,.z-tbody-wrapper.z-scroll-right td.z-sticky-right-first,.z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first,.z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first{border-left:none}.z-thead-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-thead-wrapper.z-scroll-right td.z-sticky-right-first:before,.z-tbody-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-tbody-wrapper.z-scroll-right td.z-sticky-right-first:before,.z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first:before{opacity:1}.z-thead-wrapper th.z-sticky-right-last,.z-tfoot-wrapper th.z-sticky-right-last{position:relative}.z-thead-wrapper th.z-sticky-right-last:after,.z-tfoot-wrapper th.z-sticky-right-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-30px;width:30px;background:var(--muted);pointer-events:none}.z-tfoot-wrapper{background:var(--muted);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.z-tfoot-wrapper::-webkit-scrollbar{display:none}.z-tfoot-wrapper th{height:auto;padding:8px 12px;text-align:left;vertical-align:middle;font-weight:500;font-size:12px;color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.5px;background:var(--muted);border-left:thin solid var(--border);border-top:thin solid var(--border)}.z-tfoot-wrapper th.z-at-left-edge{border-left:none}.z-tfoot-wrapper.z-shadow-footer{box-shadow:0 -2px 4px #0000000d;position:relative;z-index:15}.z-tfoot-wrapper.z-shadow-footer:where(.dark,.dark *){box-shadow:0 -2px 4px #0003}.z-pin-btn{padding:2px 4px;border-radius:4px;color:var(--muted-foreground);transition:all .15s ease}.z-pin-btn:hover{background-color:var(--muted);color:var(--foreground)}.z-pin-btn.z-pin-btn-active{color:var(--primary);background-color:var(--primary)}.z-pin-btn.z-pin-btn-active:hover{background-color:var(--primary);opacity:.8}.z-row-pin-trigger{opacity:1}.z-row-pin-trigger.text-primary{color:var(--primary)}.z-header-pin-trigger{opacity:1}.z-header-pin-trigger.text-primary{color:var(--primary)}th{overflow:hidden}th .z-header-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cdk-drag-preview{box-shadow:0 4px 12px #00000026;opacity:.9;border-radius:4px;background-color:var(--card);z-index:9999}.cdk-drag-preview:where(.dark,.dark *){box-shadow:0 4px 12px #0006}.cdk-drag-placeholder{opacity:.3;background-color:var(--muted);border:2px dashed var(--primary)}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.cdk-drop-list-dragging .cdk-drag:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.z-table-settings-drawer input[type=checkbox]{appearance:none;-webkit-appearance:none;-moz-appearance:none;width:1rem;height:1rem;border:thin solid var(--input);border-radius:.25rem;background-color:var(--background);cursor:pointer;position:relative;transition:all .2s ease}.z-table-settings-drawer input[type=checkbox]:hover{border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked{background-color:var(--primary);border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked:after{content:\"\";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:.375rem;height:.625rem;border:solid var(--primary-foreground);border-width:0 2px 2px 0;transform:translate(-50%,-60%) rotate(45deg)}.z-table-settings-drawer input[type=checkbox]:disabled{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "component", type: NgScrollbar, selector: "ng-scrollbar:not([externalViewport]), [ngScrollbar]", exportAs: ["ngScrollbar"] }, { kind: "directive", type: FlexRenderDirective, selector: "[flexRender]", inputs: ["flexRender", "flexRenderProps", "flexRenderInjector"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: ZCheckboxComponent, selector: "z-checkbox", inputs: ["class", "zType", "zSize", "zLabel", "zText", "zDisabled", "zIndeterminate", "zValue", "zOptions", "zOrientation", "zCheckAll", "zCheckAllText", "zChecked", "zGroupValue"], outputs: ["zCheckedChange", "zGroupValueChange", "zChange", "zGroupChange", "zControl"] }, { kind: "component", type: ZIconComponent, selector: "z-icon, [z-icon]", inputs: ["class", "zType", "zSize", "zStrokeWidth", "zSvg"] }, { kind: "component", type: ZInputComponent, selector: "z-input", inputs: ["class", "zType", "zSize", "zLabel", "zLabelClass", "zPlaceholder", "zRequired", "zDisabled", "zReadonly", "zPrefix", "zSuffix", "zMin", "zMax", "zStep", "zShowArrows", "zMask", "zDecimalPlaces", "zAllowNegative", "zThousandSeparator", "zDecimalMarker", "zValidators", "zAsyncValidators", "zAsyncDebounce", "zAsyncValidateOn", "zShowPasswordToggle", "zSearch", "zDebounce", "zAutofocus", "zAutoComplete", "zAllowClear", "zAutoSizeContent", "zRows", "zResize", "zMaxLength", "zAutoSuggest"], outputs: ["zOnSearch", "zOnChange", "zControl"], exportAs: ["zInput"] }, { kind: "component", type: ZLoadingComponent, selector: "z-loading", inputs: ["class", "zSpinner", "zSize", "zColor", "zText", "zOverlay", "zOverlayType", "zFullscreen", "zLoading"] }, { kind: "component", type: ZDrawerComponent, selector: "z-drawer", inputs: ["class", "zVisible", "zTitle", "zDescription", "zWidth", "zHeight", "zPlacement", "zClosable", "zMaskClosable", "zHideFooter", "zOkText", "zCancelText", "zOkDestructive", "zOkDisabled", "zLoading", "zOverlay", "zShadow", "zShape", "zContentLoading", "zSkeletonRows"], outputs: ["zOnOk", "zOnCancel", "zVisibleChange"], exportAs: ["zDrawer"] }, { kind: "component", type: ZPaginationComponent, selector: "z-pagination", inputs: ["zPageIndex", "zPageSize", "zTotal", "zPageSizeOptions", "zShowSizeChanger", "zShowQuickJumper", "zShowTotal", "zSimple", "zSize", "zDisabled", "zTotalLabel", "zPerPageLabel", "zGoToLabel"], outputs: ["zPageIndexChange", "zPageSizeChange", "zOnPageChange"] }, { kind: "component", type: ZTableFilterComponent, selector: "z-table-filter", inputs: ["zColumn", "zTable"] }, { kind: "directive", type: ZPopoverDirective, selector: "[z-popover]", inputs: ["zPopoverContent", "zPosition", "zTrigger", "zClass", "zShowDelay", "zHideDelay", "zDisabled", "zOffset", "zPopoverWidth", "zManualClose", "zScrollClose", "zShowArrow"], outputs: ["zShow", "zHide", "zHideStart", "zControl"], exportAs: ["zPopover"] }, { kind: "component", type: ZButtonComponent, selector: "z-button, button[z-button], a[z-button]", inputs: ["class", "zType", "zSize", "zShape", "zLabel", "zLoading", "zDisabled", "zTypeIcon", "zSizeIcon", "zStrokeWidthIcon", "zWave"], exportAs: ["zButton"] }, { kind: "directive", type: ZTooltipDirective, selector: "[z-tooltip], [zTooltip]", inputs: ["zContent", "zPosition", "zTrigger", "zTooltipType", "zTooltipSize", "zClass", "zShowDelay", "zHideDelay", "zArrow", "zDisabled", "zOffset", "zAutoDetect", "zTriggerElement", "zAlwaysShow", "zMaxWidth"], outputs: ["zShow", "zHide"], exportAs: ["zTooltip"] }, { kind: "directive", type: ZTableRowHoverDirective, selector: "[z-table-row-hover], [zTableRowHover]", inputs: ["zTableRowHover"] }, { kind: "component", type: ZTableIconTextComponent, selector: "z-table-icon-text", inputs: ["zText", "zTooltip"] }, { kind: "pipe", type: ZTableIsTemplateRefPipe, name: "zTableIsTemplateRef" }, { kind: "pipe", type: ZTableHasIconPipe, name: "zTableHasIcon" }, { kind: "pipe", type: ZTableCellConfigPipe, name: "zTableCellConfig" }, { kind: "pipe", type: ZTableCellOffsetPipe, name: "zTableCellOffset" }, { kind: "pipe", type: ZTableColumnConfigPipe, name: "zTableColumnConfig" }, { kind: "pipe", type: ZTableColumnHeaderPipe, name: "zTableColumnHeader" }, { kind: "pipe", type: ZTableColumnParentsPipe, name: "zTableColumnParents" }, { kind: "pipe", type: ZTableFooterContentPipe, name: "zTableFooterContent" }, { kind: "pipe", type: ZTableHeaderPinPipe, name: "zTableHeaderPin" }, { kind: "pipe", type: ZTablePinningStylesPipe, name: "zTablePinningStyles" }, { kind: "pipe", type: ZTableRowPipe, name: "zTableRow" }, { kind: "pipe", type: ZTableSpanPipe, name: "zTableSpan" }, { kind: "pipe", type: ZTableFooterRenderPipe, name: "zTableFooterRender" }, { kind: "pipe", type: ZTableHeaderRenderPipe, name: "zTableHeaderRender" }, { kind: "pipe", type: ZTableBodyCellRenderPipe, name: "zTableBodyCellRender" }, { kind: "pipe", type: ZSafeHtmlPipe, name: "zSafeHtml" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2577
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: ZTableComponent, isStandalone: true, selector: "z-table", inputs: { zConfig: { classPropertyName: "zConfig", publicName: "zConfig", isSignal: true, isRequired: false, transformFunction: null }, zLoading: { classPropertyName: "zLoading", publicName: "zLoading", isSignal: true, isRequired: false, transformFunction: null }, zKey: { classPropertyName: "zKey", publicName: "zKey", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { zChange: "zChange", zControl: "zControl" }, host: { classAttribute: "z-table block relative" }, viewQueries: [{ propertyName: "theadWrapper", first: true, predicate: ["theadWrapper"], descendants: true, isSignal: true }, { propertyName: "tbodyContainer", first: true, predicate: ["tbodyContainer"], descendants: true, isSignal: true }, { propertyName: "tbodyWrapper", first: true, predicate: ["tbodyWrapper"], descendants: true, isSignal: true }, { propertyName: "tbodyScrollbar", first: true, predicate: ["tbodyWrapper"], descendants: true, isSignal: true }, { propertyName: "tfootWrapper", first: true, predicate: ["tfootWrapper"], descendants: true, isSignal: true }, { propertyName: "expandedRowTemplate", first: true, predicate: ["expandedRowTemplate"], descendants: true, isSignal: true }], exportAs: ["zTable"], ngImport: i0, template: "<!-- Toolbar: Search & Settings -->\n@if (zConfig().enableSearch || zConfig().enableSettings) {\n <div class=\"z-table-toolbar mb-2 flex items-center justify-between gap-4\">\n <!-- Search -->\n @if (zConfig().enableSearch) {\n <z-input\n class=\"w-64\"\n zSize=\"sm\"\n zPlaceholder=\"Search...\"\n [zSearch]=\"true\"\n [zDebounce]=\"300\"\n (zOnSearch)=\"onSearchChange($event)\" />\n } @else {\n <div></div>\n }\n\n <!-- Settings Button -->\n @if (zConfig().enableSettings) {\n <z-button zType=\"outline\" zSize=\"sm\" zTypeIcon=\"lucideSettings\" (click)=\"openSettingsDrawer()\">Settings</z-button>\n }\n </div>\n}\n\n<div\n class=\"z-table-container shadow-xs\"\n [class.z-hide-horizontal-border]=\"!showHorizontalBorder()\"\n [class.z-hide-vertical-border]=\"!showVerticalBorder()\"\n [style.max-height]=\"zConfig().maxHeight\"\n [style.min-height]=\"zConfig().minHeight\">\n <!-- Shared colgroup template -->\n <ng-template #colGroupTpl>\n <colgroup>\n @for (column of orderedLeafColumns(); track column.id) {\n @if (column.getIsVisible()) {\n <col [style.width]=\"'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n }\n </colgroup>\n </ng-template>\n\n <!-- Header table -->\n <div\n class=\"z-thead-wrapper shadow-xs\"\n [class.z-shadow-header]=\"shouldHeaderShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #theadWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <thead>\n @for (headerGroup of orderedHeaderGroups(); track headerGroup.id) {\n <tr>\n @for (header of headerGroup.headers; track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableHeaderRender: headerGroup.headers : zConfig().columns;\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"header.column | zTablePinningStyles: header : 'header' : table : zConfig().columns\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass'\"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableHeaderPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableHeaderPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableHeaderPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"header.column.id === 'actions'\"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'select') {\n <!-- Header Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\"\n (zChange)=\"table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <!-- Expand All Button -->\n <div class=\"flex items-center justify-center\">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else if (header.column.id === 'actions') {\n <!-- Empty header for actions column -->\n <div class=\"flex items-center justify-center\"></div>\n } @else {\n <!-- Header Content with Sort and Pin -->\n <div class=\"flex w-full items-center gap-1\">\n <!-- Header Text -->\n <div\n class=\"flex min-w-0 flex-1 items-center gap-1.5\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-right'\n \">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n } @else if (headerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\" />\n } @else {\n <!-- Default/innerHTML rendering -->\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [innerHTML]=\"headerContent | zSafeHtml\"></span>\n }\n </ng-container>\n <!-- Sort Icon (clickable) - hidden when table has rowSpan columns -->\n @if (header.column.getCanSort() && !hasBodyRowSpan()) {\n <span\n class=\"shrink-0 cursor-pointer text-gray-500 hover:text-gray-700\"\n (click)=\"handleSort($event, header.column.getToggleSortingHandler())\">\n @if (header.column.getIsSorted() === 'asc') {\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n } @else if (header.column.getIsSorted() === 'desc') {\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n } @else {\n <z-icon zType=\"lucideArrowDownUp\" zSize=\"12\" class=\"opacity-30\" />\n }\n </span>\n }\n </div>\n\n <!-- Column Pin Popover -->\n @if (header.column.getCanPin() && zConfig().enableColumnPinning) {\n <ng-template #colPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideChevronLeft\" zSize=\"12\" />\n <span>Pin left</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideChevronRight\" zSize=\"12\" />\n <span>Pin right</span>\n </button>\n }\n @if (header.column.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, false); colPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>Unpin</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #colPinPopover=\"zPopover\"\n [zPopoverContent]=\"colPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom-right\"\n [zOffset]=\"3\"\n class=\"z-header-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex shrink-0 cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"header.column.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"12\" class=\"rotate-90\" />\n </button>\n }\n </div>\n }\n <!-- Column Filter -->\n @if (header.column.getCanFilter() && hasFiltering()) {\n <div class=\"mt-1\">\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n </div>\n }\n <!-- Column Resizer -->\n @if (\n header.column.id !== 'select' &&\n header.column.id !== 'expand' &&\n zConfig().enableColumnResizing !== false\n ) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n (mousedown)=\"header.getResizeHandler()($event)\"\n (touchstart)=\"header.getResizeHandler()($event)\"></div>\n }\n </th>\n }\n }\n </tr>\n }\n </thead>\n </table>\n </div>\n\n <!-- Body table -->\n <div\n class=\"z-tbody-wrapper relative\"\n #tbodyContainer\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\">\n @if (zLoading() || isProcessing()) {\n <!-- Loading State -->\n <div class=\"z-loading-state\">\n <z-loading [zLoading]=\"true\" zSize=\"lg\" zText=\"Loading...\" />\n </div>\n } @else if (isEmpty()) {\n <div class=\"z-empty-state\">\n @if (isNoSearchResults()) {\n <!-- No search results -->\n <z-icon zType=\"lucideSearchX\" zSize=\"40\" class=\"text-muted-foreground/50\" />\n <p class=\"text-muted-foreground text-sm\">No results found</p>\n } @else {\n <!-- Empty data -->\n <z-icon zType=\"lucidePackageOpen\" zSize=\"40\" class=\"text-muted-foreground/50\" />\n <p class=\"text-muted-foreground text-sm\">No data</p>\n }\n </div>\n } @else {\n <ng-scrollbar class=\"z-tbody-scrollbar\" #tbodyWrapper track=\"all\" (scroll)=\"onTbodyScroll($event)\">\n @if (isVirtual()) {\n <!-- Virtual Scroll Mode -->\n <div\n class=\"z-virtual-scroll-inner\"\n [style.height.px]=\"virtualizer.getTotalSize()\"\n [style.min-width.px]=\"table.getTotalSize()\">\n @for (virtualItem of virtualizer.getVirtualItems(); track virtualItem.index) {\n @let groupRows = dynamicGroupRows()[virtualItem.index] || [];\n <div\n class=\"z-virtual-row\"\n [style.height.px]=\"dynamicGroupHeights()[virtualItem.index] ?? groupSize() * virtualRowHeight()\"\n [style.transform]=\"'translateY(' + virtualItem.start + 'px)'\">\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody [class.z-has-vertical-scroll]=\"hasVerticalScroll()\">\n @for (row of groupRows; track row.id) {\n <tr\n z-table-row-hover\n [style.height.px]=\"virtualRowHeight()\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-selected]=\"row.getIsSelected()\"\n [class.z-indeterminate-selected]=\"row.getIsSomeSelected() && !row.getIsSelected()\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan =\n cell | zTableBodyCellRender: row.getVisibleCells() : zConfig().columns;\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles: cell : 'body' : row.getVisibleCells() : zConfig().columns\n \"\n [class.z-sticky-left]=\"cell.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"cell.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n cell.column.getIsPinned() === 'left' && cell.column.getIsLastColumn('left')\n \"\n [class.z-sticky-right-first]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [attr.rowspan]=\"\n cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\n \"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\"\n [class]=\"cell | zTableCellConfig: zConfig().columns : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig().columns : 'cellStyle'\">\n @if (cell.column.id === 'select') {\n <!-- Row Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"cell.row.getIsSelected()\"\n [zIndeterminate]=\"cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\"\n [zDisabled]=\"!cell.row.getCanSelect()\"\n (zChange)=\"cell.row.toggleSelected()\" />\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button -->\n <div class=\"flex items-center justify-center\">\n @if (cell.row.subRows && cell.row.subRows.length > 0) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n </div>\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig().columns : 'contentTooltip'\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\" />\n } @else {\n <!-- Default/innerHTML rendering -->\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"></div>\n }\n </ng-container>\n }\n </td>\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n } @else {\n <!-- Normal Scroll Mode -->\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody [class.z-has-vertical-scroll]=\"hasVerticalScroll()\">\n <!-- Row Template -->\n <ng-template #rowTemplate let-row>\n <tr\n z-table-row-hover\n [attr.data-row-id]=\"row.id\"\n [ngStyle]=\"row | zTableRow: table : 'pinningStyles' : virtualRowHeight()\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-selected]=\"row.getIsSelected()\"\n [class.z-indeterminate-selected]=\"row.getIsSomeSelected() && !row.getIsSelected()\"\n [class.z-pinned-top]=\"row.getIsPinned() === 'top'\"\n [class.z-pinned-bottom]=\"row.getIsPinned() === 'bottom'\"\n [class.z-shadow-bottom]=\"\n showHeaderFooterShadow() &&\n row.getIsPinned() === 'top' &&\n (row | zTableRow: table : 'isLastTopPinned')\n \"\n [class.z-shadow-top]=\"\n showHeaderFooterShadow() &&\n row.getIsPinned() === 'bottom' &&\n (row | zTableRow: table : 'isFirstBottomPinned')\n \"\n [attr.data-depth]=\"row.depth\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan = cell | zTableBodyCellRender: row.getVisibleCells() : zConfig().columns;\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column | zTablePinningStyles: cell : 'body' : row.getVisibleCells() : zConfig().columns\n \"\n [class]=\"cell | zTableCellConfig: zConfig().columns : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig().columns : 'cellStyle'\"\n [class.z-sticky-left]=\"cell.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"cell.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n cell.column.getIsPinned() === 'left' && cell.column.getIsLastColumn('left')\n \"\n [class.z-sticky-right-first]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"cell.column.id === 'actions'\"\n [attr.rowspan]=\"cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\">\n @if (cell.column.id === 'select') {\n <!-- Row Checkbox with Pin Button -->\n <div class=\"flex items-center justify-center gap-1\">\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"cell.row.getIsSelected()\"\n [zIndeterminate]=\"cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\"\n [zDisabled]=\"!cell.row.getCanSelect()\"\n (zChange)=\"cell.row.toggleSelected()\" />\n @if (zConfig().enableRowPinning && cell.row.depth === 0 && !hasBodyRowSpan()) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>Pin top</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>Pin bottom</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>Unpin</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button with Row Pin Popover -->\n <div class=\"flex items-center justify-center gap-1\">\n @if (cell.row.subRows && cell.row.subRows.length > 0) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n @if (\n zConfig().enableRowPinning &&\n cell.row.depth === 0 &&\n !(cell.row.subRows && cell.row.subRows.length > 0) &&\n !hasBodyRowSpan()\n ) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>Pin top</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>Pin bottom</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>Unpin</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'actions') {\n <!-- Actions Column - Row Pin Only (for parent rows) -->\n @if (cell.row.depth === 0 && !hasBodyRowSpan()) {\n <div class=\"flex items-center justify-center\">\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>Pin top</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>Pin bottom</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>Unpin</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n </div>\n }\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else {\n <!-- Default/innerHTML rendering -->\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"></div>\n }\n </ng-container>\n }\n </td>\n }\n }\n </tr>\n\n <!-- Expanded Row Detail -->\n @if (row.getIsExpanded() && row.depth === 0 && !row.subRows?.length && zConfig().expandedRowTemplate) {\n <tr class=\"z-expanded-row\">\n <td [attr.colspan]=\"row.getVisibleCells().length\" class=\"p-0\">\n <ng-container *ngTemplateOutlet=\"zConfig().expandedRowTemplate!; context: { $implicit: row }\" />\n </td>\n </tr>\n }\n </ng-template>\n\n <!-- Render Top Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of table.getTopRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n\n <!-- Render Center Rows -->\n @for (row of table.getCenterRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n\n <!-- Render Bottom Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of bottomRowsReversed(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n </tbody>\n </table>\n }\n </ng-scrollbar>\n }\n <!-- end @else -->\n </div>\n\n <!-- Footer table -->\n @if (hasFooter()) {\n <div\n class=\"z-tfoot-wrapper\"\n [class.z-shadow-footer]=\"shouldFooterShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #tfootWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tfoot>\n @for (footerGroup of orderedFooterGroups(); track footerGroup.id) {\n @if (footerGroup | zTableFooterContent: zConfig().columns) {\n <tr>\n @for (footer of footerGroup.headers; track footer.id) {\n @let rowSpan = footer | zTableSpan: zConfig().columns : 'footerRowSpan';\n @let shouldRender = footer | zTableFooterRender: footerGroup.headers : zConfig().columns;\n @if (rowSpan && shouldRender) {\n <th\n [ngStyle]=\"footer.column | zTablePinningStyles: footer : 'header' : table : zConfig().columns\"\n [class]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerClass') +\n ' ' +\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass')\n \"\n [style]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerStyle'\"\n [class.z-sticky-left]=\"footer.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"footer.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"footer | zTableHeaderPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"footer | zTableHeaderPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"footer | zTableHeaderPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"footer | zTableCellOffset: orderedLeafColumns()\"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"footer | zTableSpan: zConfig().columns : 'footerColSpan'\">\n @let configFooterContent =\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContent';\n @if (footer.column.columnDef.footer) {\n <ng-container\n *flexRender=\"footer.column.columnDef.footer; props: footer.getContext(); let footerContent\">\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n @if (footerContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <ng-container\n *ngTemplateOutlet=\"footerContent; context: { $implicit: footer.getContext() }\" />\n } @else if (footerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"footerContent\"\n [zTooltip]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip'\n \" />\n } @else {\n <!-- Default/string rendering -->\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n footerContent\n \">\n {{ footerContent }}\n </span>\n }\n </div>\n </ng-container>\n } @else if (configFooterContent) {\n <!-- Fallback for group columns without TanStack footer -->\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n $any(configFooterContent)\n \">\n {{ configFooterContent }}\n </span>\n </div>\n }\n </th>\n }\n }\n </tr>\n }\n }\n </tfoot>\n </table>\n </div>\n }\n</div>\n\n<!-- Pagination -->\n@if (zConfig().enablePagination && zConfig().pagination?.enabled !== false) {\n @let totalRows = table.getFilteredRowModel().rows.length;\n @let totalLabel = zConfig().pagination?.totalLabel ?? '{total} total rows';\n <div class=\"mt-4 flex items-center justify-between gap-4\">\n <div class=\"text-sm text-gray-500\">\n {{ totalLabel.replace('{total}', zConfig().totalCount?.toString() || totalRows.toString()) }}\n </div>\n <z-pagination\n [zTotal]=\"zConfig().totalCount ?? totalRows\"\n [(zPageIndex)]=\"pagination().pageIndex\"\n [(zPageSize)]=\"pagination().pageSize\"\n [zPageSizeOptions]=\"zConfig().pagination?.pageSizeOptions ?? [10, 20, 50, 100]\"\n [zShowSizeChanger]=\"zConfig().pagination?.showSizeChanger ?? true\"\n [zShowQuickJumper]=\"zConfig().pagination?.showQuickJumper ?? false\"\n [zShowTotal]=\"false\"\n [zDisabled]=\"zConfig().pagination?.disabled || zLoading() || isProcessing()\"\n (zOnPageChange)=\"onPageChange($event)\" />\n </div>\n}\n\n<!-- Settings Drawer -->\n<z-drawer\n [(zVisible)]=\"showSettingsDrawer\"\n zTitle=\"Table Settings\"\n zPlacement=\"right\"\n zWidth=\"500px\"\n [zShadow]=\"true\"\n [zOkText]=\"null\"\n zCancelText=\"Close\">\n <div class=\"z-table-settings-drawer px-4\">\n <!-- Display Settings -->\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">Display Settings</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">Customize table appearance</p>\n <div class=\"grid grid-cols-2 gap-x-4 gap-y-3\">\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"showHorizontalBorder()\"\n zText=\"Horizontal Border\"\n (zChange)=\"showHorizontalBorder.set(!showHorizontalBorder())\" />\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"showVerticalBorder()\"\n zText=\"Vertical Border\"\n (zChange)=\"showVerticalBorder.set(!showVerticalBorder())\" />\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"showHeaderFooterShadow()\"\n zText=\"Header/Footer Shadow\"\n (zChange)=\"showHeaderFooterShadow.set(!showHeaderFooterShadow())\" />\n </div>\n </div>\n\n <!-- Divider -->\n <div class=\"border-border my-4 border-t\"></div>\n\n <!-- Unified Column Settings -->\n @if (zConfig().enableSettings) {\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">Column Settings</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">Toggle visibility, drag to reorder, and pin columns</p>\n\n <!-- Unpinned Columns (Draggable) -->\n <div cdkDropList (cdkDropListDropped)=\"onPendingColumnDrop($event)\" class=\"space-y-1.5\">\n @for (columnId of columnOrder(); track columnId; let i = $index) {\n @if (columnId !== 'expand' && columnId !== 'select') {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = columnVisibility()[columnId] !== false;\n @let canPin = column?.getCanPin() !== false && zConfig().enableColumnPinning;\n @if (!isPinned) {\n <div\n cdkDrag\n [cdkDragData]=\"columnId\"\n cdkDragLockAxis=\"y\"\n [cdkDragDisabled]=\"false\"\n class=\"border-border bg-card hover:border-primary flex cursor-move items-center gap-2 rounded border px-2 py-1.5 text-sm transition-all hover:shadow-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Drag Handle -->\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n class=\"border-input h-4 w-4 shrink-0 cursor-pointer rounded\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns }}\n </span>\n @let parents = columnId | zTableColumnParents: zConfig().columns;\n @if (parents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ parents }})</span>\n }\n </span>\n\n <!-- Pin Buttons -->\n @if (canPin) {\n <div class=\"flex shrink-0 items-center gap-0.5\">\n <button\n type=\"button\"\n (click)=\"onToggleColumnPin(columnId, 'left')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors\"\n title=\"Pin Left\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"12\" />\n </button>\n <button\n type=\"button\"\n (click)=\"onToggleColumnPin(columnId, 'right')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors\"\n title=\"Pin Right\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n }\n }\n }\n </div>\n\n <!-- Pinned Columns Section -->\n @if (zConfig().enableColumnPinning) {\n @if (pinnedColumnIds().length > 0) {\n <div class=\"border-border mt-4 border-t pt-4\">\n <h5 class=\"text-muted-foreground mb-2 text-xs font-medium\">Pinned Columns</h5>\n <div class=\"space-y-1.5\">\n @for (columnId of pinnedColumnIds(); track columnId) {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = columnVisibility()[columnId] !== false;\n <div\n class=\"border-border bg-muted/30 flex items-center gap-2 rounded border px-2 py-1.5 text-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Pin Icon -->\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"text-primary shrink-0\" />\n\n <!-- Visibility Checkbox -->\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n class=\"border-input h-4 w-4 shrink-0 cursor-pointer rounded\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns }}\n </span>\n @let pinnedParents = columnId | zTableColumnParents: zConfig().columns;\n @if (pinnedParents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ pinnedParents }})</span>\n }\n </span>\n\n <!-- Position Badge -->\n <span class=\"bg-primary/10 text-primary shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium\">\n {{ isPinned === 'left' ? 'LEFT' : 'RIGHT' }}\n </span>\n\n <!-- Unpin Button -->\n <button\n type=\"button\"\n (click)=\"onToggleColumnPin(columnId, isPinned === 'left' ? 'left' : 'right')\"\n class=\"text-muted-foreground hover:bg-muted hover:text-foreground cursor-pointer rounded p-1 text-xs transition-colors\"\n title=\"Unpin\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n </div>\n }\n }\n </div>\n }\n </div>\n</z-drawer>\n", styles: [":host{display:flex;flex-direction:column;height:100%;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px}.z-table-container{display:flex;flex-direction:column;position:relative;width:100%;height:100%;overflow:hidden;border-radius:8px;border:thin solid var(--border);background-color:var(--card)}.z-table-container.z-hide-horizontal-border th,.z-table-container.z-hide-horizontal-border td{border-bottom:none!important;border-top:none!important}.z-table-container.z-hide-vertical-border th,.z-table-container.z-hide-vertical-border td{border-left:none!important}table{width:fit-content;min-width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed;font-size:14px}.z-table-toolbar .z-settings-btn{transition:all .15s ease}.z-table-toolbar .z-settings-btn:hover{border-color:var(--muted-foreground)}.z-table-cell-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;-webkit-user-select:text;user-select:text}.z-thead-wrapper{flex-shrink:0;background:var(--muted);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.z-thead-wrapper::-webkit-scrollbar{display:none}.z-thead-wrapper th{height:auto;padding:8px 12px;text-align:left;vertical-align:middle;font-weight:500;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--muted);border-left:thin solid var(--border);border-bottom:thin solid var(--border);-webkit-user-select:none;user-select:none}.z-thead-wrapper th.z-at-left-edge{border-left:none}.z-thead-wrapper th[colspan]{text-align:center;background:var(--muted);font-weight:500;color:var(--foreground)}.z-thead-wrapper.z-shadow-header{box-shadow:0 1px 3px #00000014;position:relative;z-index:15}.z-thead-wrapper.z-shadow-header:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d}.z-tbody-wrapper{flex:1;min-height:100px;display:flex;flex-direction:column}.z-tbody-scrollbar{flex:1;height:100%}.z-empty-state,.z-loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;min-height:100px;height:100%;color:var(--muted-foreground);font-size:.875rem;animation:z-fade-in .2s ease-out}.z-tbody-scrollbar,.z-tbody-scrollbar table{animation:z-fade-in .2s ease-out}@keyframes z-fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.z-tbody-wrapper tr{transition:background-color .15s ease}.z-tbody-wrapper tr:hover,.z-tbody-wrapper tr:hover td[style*=sticky]{background-color:var(--muted)}.z-tbody-wrapper tr.z-pinned-top td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-top td.z-sticky-right,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-right{z-index:3}.z-tbody-wrapper tr.z-shadow-bottom{box-shadow:0 1px 3px #00000014!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-bottom:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d!important}.z-tbody-wrapper tr.z-shadow-top{box-shadow:0 -2px 4px #0000000d!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-top:where(.dark,.dark *){box-shadow:0 -2px 4px #0003!important}.z-tbody-wrapper td{padding:8px 12px;height:40px;vertical-align:middle;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--card);border-left:thin solid var(--border);border-bottom:thin solid var(--border);box-sizing:border-box}.z-tbody-wrapper td.z-at-left-edge{border-left:none}.z-tbody-wrapper td i{color:var(--muted-foreground);font-style:italic}.z-tbody-wrapper td[rowspan]{vertical-align:top;padding-top:12px}.z-tbody-wrapper td.z-row-hover{background-color:var(--muted)!important}.z-tbody-wrapper td.z-col-select,.z-tbody-wrapper td.z-col-expand,.z-tbody-wrapper td.z-col-actions{padding:8px 4px!important;text-align:center}.z-tbody-wrapper tr.z-child-row td.z-col-select:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-expand:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-actions:first-child{padding-left:0!important}.z-virtual-scroll-inner{position:relative;width:100%}.z-virtual-row{position:absolute;top:0;left:0;width:100%}tr.z-child-row td:first-child{padding-left:12px!important}tbody tr.z-selected,tbody tr.z-selected td{background-color:color-mix(in srgb,var(--primary) 15%,transparent)!important}tbody tr.z-selected:hover,tbody tr.z-selected:hover td{background-color:color-mix(in srgb,var(--primary) 20%,transparent)!important}tbody tr.z-indeterminate-selected,tbody tr.z-indeterminate-selected td{background-color:color-mix(in srgb,var(--primary) 10%,transparent)!important}tbody tr.z-indeterminate-selected:hover,tbody tr.z-indeterminate-selected:hover td{background-color:color-mix(in srgb,var(--primary) 15%,transparent)!important}tbody tr.z-pinned-top td{background-color:var(--card)!important}tbody tr.z-pinned-top:hover{background-color:var(--muted)}tbody tr.z-pinned-top:hover td{background-color:var(--muted)!important}tbody tr.z-pinned-bottom td{background-color:var(--card)!important}tbody tr.z-pinned-bottom:hover{background-color:var(--muted)}tbody tr.z-pinned-bottom:hover td,tr.z-expanded-row td{background-color:var(--muted)!important}thead th{position:relative}thead th .z-resizer{position:absolute;right:0;top:0;height:100%;width:5px;background:#0000001a;cursor:col-resize;-webkit-user-select:none;user-select:none;touch-action:none;opacity:0;transition:opacity .2s ease}thead th .z-resizer:where(.dark,.dark *){background:#ffffff1a}thead th .z-resizer:hover,thead th .z-resizer.z-is-resizing{opacity:1;background:var(--primary);opacity:.5}thead th .z-resizer.z-is-resizing{background:var(--primary);opacity:1}thead th .z-resizer.z-resizer-left{right:auto;left:0}thead th:hover .z-resizer{opacity:1}.z-thead-wrapper th.z-sticky-left,.z-thead-wrapper th.z-sticky-right,.z-tbody-wrapper th.z-sticky-left,.z-tbody-wrapper th.z-sticky-right,.z-tfoot-wrapper th.z-sticky-left,.z-tfoot-wrapper th.z-sticky-right{background-color:var(--muted);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper td.z-sticky-left,.z-thead-wrapper td.z-sticky-right,.z-tbody-wrapper td.z-sticky-left,.z-tbody-wrapper td.z-sticky-right,.z-tfoot-wrapper td.z-sticky-left,.z-tfoot-wrapper td.z-sticky-right{background-color:var(--card);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper th.z-sticky-left-last,.z-thead-wrapper td.z-sticky-left-last,.z-tbody-wrapper th.z-sticky-left-last,.z-tbody-wrapper td.z-sticky-left-last,.z-tfoot-wrapper th.z-sticky-left-last,.z-tfoot-wrapper td.z-sticky-left-last{position:relative;overflow:visible}.z-thead-wrapper th.z-sticky-left-last:after,.z-thead-wrapper td.z-sticky-left-last:after,.z-tbody-wrapper th.z-sticky-left-last:after,.z-tbody-wrapper td.z-sticky-left-last:after,.z-tfoot-wrapper th.z-sticky-left-last:after,.z-tfoot-wrapper td.z-sticky-left-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-30px;width:30px;pointer-events:none;box-shadow:inset 10px 0 8px -8px #0000001a;z-index:10}.z-thead-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-thead-wrapper td.z-sticky-left-last:after:where(.dark,.dark *),.z-tbody-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-tbody-wrapper td.z-sticky-left-last:after:where(.dark,.dark *),.z-tfoot-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-tfoot-wrapper td.z-sticky-left-last:after:where(.dark,.dark *){box-shadow:inset 10px 0 8px -8px #0000004d}.z-thead-wrapper th.z-sticky-left-last:after,.z-thead-wrapper td.z-sticky-left-last:after,.z-tbody-wrapper th.z-sticky-left-last:after,.z-tbody-wrapper td.z-sticky-left-last:after,.z-tfoot-wrapper th.z-sticky-left-last:after,.z-tfoot-wrapper td.z-sticky-left-last:after{opacity:0;transition:opacity .2s ease}.z-thead-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-thead-wrapper.z-scroll-left td.z-sticky-left-last:after,.z-tbody-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-tbody-wrapper.z-scroll-left td.z-sticky-left-last:after,.z-tfoot-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-tfoot-wrapper.z-scroll-left td.z-sticky-left-last:after{opacity:1}.z-thead-wrapper th.z-sticky-right-first,.z-thead-wrapper td.z-sticky-right-first,.z-tbody-wrapper th.z-sticky-right-first,.z-tbody-wrapper td.z-sticky-right-first,.z-tfoot-wrapper th.z-sticky-right-first,.z-tfoot-wrapper td.z-sticky-right-first{position:relative;overflow:visible}.z-thead-wrapper th.z-sticky-right-first:before,.z-thead-wrapper td.z-sticky-right-first:before,.z-tbody-wrapper th.z-sticky-right-first:before,.z-tbody-wrapper td.z-sticky-right-first:before,.z-tfoot-wrapper th.z-sticky-right-first:before,.z-tfoot-wrapper td.z-sticky-right-first:before{content:\"\";position:absolute;top:0;bottom:0;left:-30px;width:30px;pointer-events:none;box-shadow:inset -10px 0 8px -8px #0000001a;z-index:10;opacity:0;transition:opacity .2s ease}.z-thead-wrapper th.z-sticky-right-first:before:where(.dark,.dark *),.z-thead-wrapper td.z-sticky-right-first:before:where(.dark,.dark *),.z-tbody-wrapper th.z-sticky-right-first:before:where(.dark,.dark *),.z-tbody-wrapper td.z-sticky-right-first:before:where(.dark,.dark *),.z-tfoot-wrapper th.z-sticky-right-first:before:where(.dark,.dark *),.z-tfoot-wrapper td.z-sticky-right-first:before:where(.dark,.dark *){box-shadow:inset -10px 0 8px -8px #0000004d}.z-thead-wrapper.z-scroll-right th.z-sticky-right-first,.z-thead-wrapper.z-scroll-right td.z-sticky-right-first,.z-tbody-wrapper.z-scroll-right th.z-sticky-right-first,.z-tbody-wrapper.z-scroll-right td.z-sticky-right-first,.z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first,.z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first{border-left:none}.z-thead-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-thead-wrapper.z-scroll-right td.z-sticky-right-first:before,.z-tbody-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-tbody-wrapper.z-scroll-right td.z-sticky-right-first:before,.z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first:before{opacity:1}.z-thead-wrapper th.z-sticky-right-last,.z-tfoot-wrapper th.z-sticky-right-last{position:relative}.z-thead-wrapper th.z-sticky-right-last:after,.z-tfoot-wrapper th.z-sticky-right-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-30px;width:30px;background:var(--muted);pointer-events:none}.z-tfoot-wrapper{flex-shrink:0;background:var(--muted);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.z-tfoot-wrapper::-webkit-scrollbar{display:none}.z-tfoot-wrapper th{height:auto;padding:8px 12px;text-align:left;vertical-align:middle;font-weight:500;font-size:12px;color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.5px;background:var(--muted);border-left:thin solid var(--border);border-top:thin solid var(--border)}.z-tfoot-wrapper th.z-at-left-edge{border-left:none}.z-tfoot-wrapper.z-shadow-footer{box-shadow:0 -2px 4px #0000000d;position:relative;z-index:15}.z-tfoot-wrapper.z-shadow-footer:where(.dark,.dark *){box-shadow:0 -2px 4px #0003}.z-pin-btn{padding:2px 4px;border-radius:4px;color:var(--muted-foreground);transition:all .15s ease}.z-pin-btn:hover{background-color:var(--muted);color:var(--foreground)}.z-pin-btn.z-pin-btn-active{color:var(--primary);background-color:var(--primary)}.z-pin-btn.z-pin-btn-active:hover{background-color:var(--primary);opacity:.8}.z-row-pin-trigger{opacity:1}.z-row-pin-trigger.text-primary{color:var(--primary)}.z-header-pin-trigger{opacity:1}.z-header-pin-trigger.text-primary{color:var(--primary)}th{overflow:hidden}th .z-header-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cdk-drag-preview{box-shadow:0 4px 12px #00000026;opacity:.9;border-radius:4px;background-color:var(--card);z-index:9999}.cdk-drag-preview:where(.dark,.dark *){box-shadow:0 4px 12px #0006}.cdk-drag-placeholder{opacity:.3;background-color:var(--muted);border:2px dashed var(--primary)}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.cdk-drop-list-dragging .cdk-drag:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.z-table-settings-drawer input[type=checkbox]{appearance:none;-webkit-appearance:none;-moz-appearance:none;width:1rem;height:1rem;border:thin solid var(--input);border-radius:.25rem;background-color:var(--background);cursor:pointer;position:relative;transition:all .2s ease}.z-table-settings-drawer input[type=checkbox]:hover{border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked{background-color:var(--primary);border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked:after{content:\"\";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:.375rem;height:.625rem;border:solid var(--primary-foreground);border-width:0 2px 2px 0;transform:translate(-50%,-60%) rotate(45deg)}.z-table-settings-drawer input[type=checkbox]:disabled{opacity:.5;cursor:not-allowed}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "component", type: NgScrollbar, selector: "ng-scrollbar:not([externalViewport]), [ngScrollbar]", exportAs: ["ngScrollbar"] }, { kind: "directive", type: FlexRenderDirective, selector: "[flexRender]", inputs: ["flexRender", "flexRenderProps", "flexRenderInjector"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "component", type: ZCheckboxComponent, selector: "z-checkbox", inputs: ["class", "zType", "zSize", "zLabel", "zText", "zDisabled", "zIndeterminate", "zValue", "zOptions", "zOrientation", "zCheckAll", "zCheckAllText", "zChecked", "zGroupValue"], outputs: ["zCheckedChange", "zGroupValueChange", "zChange", "zGroupChange", "zControl"] }, { kind: "component", type: ZIconComponent, selector: "z-icon, [z-icon]", inputs: ["class", "zType", "zSize", "zStrokeWidth", "zSvg"] }, { kind: "component", type: ZInputComponent, selector: "z-input", inputs: ["class", "zType", "zSize", "zLabel", "zLabelClass", "zPlaceholder", "zRequired", "zDisabled", "zReadonly", "zPrefix", "zSuffix", "zMin", "zMax", "zStep", "zShowArrows", "zMask", "zDecimalPlaces", "zAllowNegative", "zThousandSeparator", "zDecimalMarker", "zValidators", "zAsyncValidators", "zAsyncDebounce", "zAsyncValidateOn", "zShowPasswordToggle", "zSearch", "zDebounce", "zAutofocus", "zAutoComplete", "zAllowClear", "zAutoSizeContent", "zRows", "zResize", "zMaxLength", "zAutoSuggest"], outputs: ["zOnSearch", "zOnChange", "zControl"], exportAs: ["zInput"] }, { kind: "component", type: ZLoadingComponent, selector: "z-loading", inputs: ["class", "zSpinner", "zSize", "zColor", "zText", "zOverlay", "zOverlayType", "zFullscreen", "zLoading"] }, { kind: "component", type: ZDrawerComponent, selector: "z-drawer", inputs: ["class", "zVisible", "zTitle", "zDescription", "zWidth", "zHeight", "zPlacement", "zClosable", "zMaskClosable", "zHideFooter", "zOkText", "zCancelText", "zOkDestructive", "zOkDisabled", "zLoading", "zOverlay", "zShadow", "zShape", "zContentLoading", "zSkeletonRows"], outputs: ["zOnOk", "zOnCancel", "zVisibleChange"], exportAs: ["zDrawer"] }, { kind: "component", type: ZPaginationComponent, selector: "z-pagination", inputs: ["zPageIndex", "zPageSize", "zTotal", "zPageSizeOptions", "zShowSizeChanger", "zShowQuickJumper", "zShowTotal", "zSimple", "zSize", "zDisabled", "zTotalLabel", "zPerPageLabel", "zGoToLabel"], outputs: ["zPageIndexChange", "zPageSizeChange", "zOnPageChange"] }, { kind: "component", type: ZTableFilterComponent, selector: "z-table-filter", inputs: ["zColumn", "zTable"] }, { kind: "directive", type: ZPopoverDirective, selector: "[z-popover]", inputs: ["zPopoverContent", "zPosition", "zTrigger", "zClass", "zShowDelay", "zHideDelay", "zDisabled", "zOffset", "zPopoverWidth", "zManualClose", "zScrollClose", "zShowArrow"], outputs: ["zShow", "zHide", "zHideStart", "zControl"], exportAs: ["zPopover"] }, { kind: "component", type: ZButtonComponent, selector: "z-button, button[z-button], a[z-button]", inputs: ["class", "zType", "zSize", "zShape", "zLabel", "zLoading", "zDisabled", "zTypeIcon", "zSizeIcon", "zStrokeWidthIcon", "zWave"], exportAs: ["zButton"] }, { kind: "directive", type: ZTooltipDirective, selector: "[z-tooltip], [zTooltip]", inputs: ["zContent", "zPosition", "zTrigger", "zTooltipType", "zTooltipSize", "zClass", "zShowDelay", "zHideDelay", "zArrow", "zDisabled", "zOffset", "zAutoDetect", "zTriggerElement", "zAlwaysShow", "zMaxWidth"], outputs: ["zShow", "zHide"], exportAs: ["zTooltip"] }, { kind: "directive", type: ZTableRowHoverDirective, selector: "[z-table-row-hover], [zTableRowHover]", inputs: ["zTableRowHover"] }, { kind: "component", type: ZTableIconTextComponent, selector: "z-table-icon-text", inputs: ["zText", "zTooltip"] }, { kind: "pipe", type: ZTableIsTemplateRefPipe, name: "zTableIsTemplateRef" }, { kind: "pipe", type: ZTableHasIconPipe, name: "zTableHasIcon" }, { kind: "pipe", type: ZTableCellConfigPipe, name: "zTableCellConfig" }, { kind: "pipe", type: ZTableCellOffsetPipe, name: "zTableCellOffset" }, { kind: "pipe", type: ZTableColumnConfigPipe, name: "zTableColumnConfig" }, { kind: "pipe", type: ZTableColumnHeaderPipe, name: "zTableColumnHeader" }, { kind: "pipe", type: ZTableColumnParentsPipe, name: "zTableColumnParents" }, { kind: "pipe", type: ZTableFooterContentPipe, name: "zTableFooterContent" }, { kind: "pipe", type: ZTableHeaderPinPipe, name: "zTableHeaderPin" }, { kind: "pipe", type: ZTablePinningStylesPipe, name: "zTablePinningStyles" }, { kind: "pipe", type: ZTableRowPipe, name: "zTableRow" }, { kind: "pipe", type: ZTableSpanPipe, name: "zTableSpan" }, { kind: "pipe", type: ZTableFooterRenderPipe, name: "zTableFooterRender" }, { kind: "pipe", type: ZTableHeaderRenderPipe, name: "zTableHeaderRender" }, { kind: "pipe", type: ZTableBodyCellRenderPipe, name: "zTableBodyCellRender" }, { kind: "pipe", type: ZSafeHtmlPipe, name: "zSafeHtml" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2578
2578
|
}
|
|
2579
2579
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: ZTableComponent, decorators: [{
|
|
2580
2580
|
type: Component,
|
|
@@ -2617,7 +2617,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
|
|
|
2617
2617
|
ZSafeHtmlPipe,
|
|
2618
2618
|
], standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
2619
2619
|
class: 'z-table block relative',
|
|
2620
|
-
}, exportAs: 'zTable', template: "<!-- Toolbar: Search & Settings -->\n@if (zConfig().enableSearch || zConfig().enableSettings) {\n <div class=\"z-table-toolbar mb-2 flex items-center justify-between gap-4\">\n <!-- Search -->\n @if (zConfig().enableSearch) {\n <z-input\n class=\"w-64\"\n zSize=\"sm\"\n zPlaceholder=\"Search...\"\n [zSearch]=\"true\"\n [zDebounce]=\"300\"\n (zOnSearch)=\"onSearchChange($event)\" />\n } @else {\n <div></div>\n }\n\n <!-- Settings Button -->\n @if (zConfig().enableSettings) {\n <z-button zType=\"outline\" zSize=\"sm\" zTypeIcon=\"lucideSettings\" (click)=\"openSettingsDrawer()\">Settings</z-button>\n }\n </div>\n}\n\n<div\n class=\"z-table-container shadow-xs\"\n [class.z-hide-horizontal-border]=\"!showHorizontalBorder()\"\n [class.z-hide-vertical-border]=\"!showVerticalBorder()\"\n [style.max-height]=\"zConfig().maxHeight\"\n [style.min-height]=\"zConfig().minHeight\">\n <!-- Shared colgroup template -->\n <ng-template #colGroupTpl>\n <colgroup>\n @for (column of orderedLeafColumns(); track column.id) {\n @if (column.getIsVisible()) {\n <col [style.width]=\"'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n }\n </colgroup>\n </ng-template>\n\n <!-- Header table -->\n <div\n class=\"z-thead-wrapper shadow-xs\"\n [class.z-shadow-header]=\"shouldHeaderShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #theadWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <thead>\n @for (headerGroup of orderedHeaderGroups(); track headerGroup.id) {\n <tr>\n @for (header of headerGroup.headers; track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableHeaderRender: headerGroup.headers : zConfig().columns;\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"header.column | zTablePinningStyles: header : 'header' : table : zConfig().columns\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass'\"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableHeaderPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableHeaderPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableHeaderPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"header.column.id === 'actions'\"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'select') {\n <!-- Header Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\"\n (zChange)=\"table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <!-- Expand All Button -->\n <div class=\"flex items-center justify-center\">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else if (header.column.id === 'actions') {\n <!-- Empty header for actions column -->\n <div class=\"flex items-center justify-center\"></div>\n } @else {\n <!-- Header Content with Sort and Pin -->\n <div class=\"flex w-full items-center gap-1\">\n <!-- Header Text -->\n <div\n class=\"flex min-w-0 flex-1 items-center gap-1.5\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-right'\n \">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n } @else if (headerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\" />\n } @else {\n <!-- Default/innerHTML rendering -->\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [innerHTML]=\"headerContent | zSafeHtml\"></span>\n }\n </ng-container>\n <!-- Sort Icon (clickable) - hidden when table has rowSpan columns -->\n @if (header.column.getCanSort() && !hasBodyRowSpan()) {\n <span\n class=\"shrink-0 cursor-pointer text-gray-500 hover:text-gray-700\"\n (click)=\"handleSort($event, header.column.getToggleSortingHandler())\">\n @if (header.column.getIsSorted() === 'asc') {\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n } @else if (header.column.getIsSorted() === 'desc') {\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n } @else {\n <z-icon zType=\"lucideArrowDownUp\" zSize=\"12\" class=\"opacity-30\" />\n }\n </span>\n }\n </div>\n\n <!-- Column Pin Popover -->\n @if (header.column.getCanPin() && zConfig().enableColumnPinning) {\n <ng-template #colPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideChevronLeft\" zSize=\"12\" />\n <span>Pin left</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideChevronRight\" zSize=\"12\" />\n <span>Pin right</span>\n </button>\n }\n @if (header.column.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, false); colPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>Unpin</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #colPinPopover=\"zPopover\"\n [zPopoverContent]=\"colPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom-right\"\n [zOffset]=\"3\"\n class=\"z-header-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex shrink-0 cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"header.column.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"12\" class=\"rotate-90\" />\n </button>\n }\n </div>\n }\n <!-- Column Filter -->\n @if (header.column.getCanFilter() && hasFiltering()) {\n <div class=\"mt-1\">\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n </div>\n }\n <!-- Column Resizer -->\n @if (\n header.column.id !== 'select' &&\n header.column.id !== 'expand' &&\n zConfig().enableColumnResizing !== false\n ) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n (mousedown)=\"header.getResizeHandler()($event)\"\n (touchstart)=\"header.getResizeHandler()($event)\"></div>\n }\n </th>\n }\n }\n </tr>\n }\n </thead>\n </table>\n </div>\n\n <!-- Body table -->\n <div\n class=\"z-tbody-wrapper relative\"\n #tbodyContainer\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\">\n @if (zLoading() || isProcessing()) {\n <!-- Loading State -->\n <div class=\"z-loading-state\">\n <z-loading [zLoading]=\"true\" zSize=\"lg\" zText=\"Loading...\" />\n </div>\n } @else if (isEmpty()) {\n <div class=\"z-empty-state\">\n @if (isNoSearchResults()) {\n <!-- No search results -->\n <z-icon zType=\"lucideSearchX\" zSize=\"40\" class=\"text-muted-foreground/50\" />\n <p class=\"text-muted-foreground text-sm\">No results found</p>\n } @else {\n <!-- Empty data -->\n <z-icon zType=\"lucidePackageOpen\" zSize=\"40\" class=\"text-muted-foreground/50\" />\n <p class=\"text-muted-foreground text-sm\">No data</p>\n }\n </div>\n } @else {\n <ng-scrollbar class=\"z-tbody-scrollbar\" #tbodyWrapper track=\"all\" (scroll)=\"onTbodyScroll($event)\">\n @if (isVirtual()) {\n <!-- Virtual Scroll Mode -->\n <div\n class=\"z-virtual-scroll-inner\"\n [style.height.px]=\"virtualizer.getTotalSize()\"\n [style.min-width.px]=\"table.getTotalSize()\">\n @for (virtualItem of virtualizer.getVirtualItems(); track virtualItem.index) {\n @let groupRows = dynamicGroupRows()[virtualItem.index] || [];\n <div\n class=\"z-virtual-row\"\n [style.height.px]=\"dynamicGroupHeights()[virtualItem.index] ?? groupSize() * virtualRowHeight()\"\n [style.transform]=\"'translateY(' + virtualItem.start + 'px)'\">\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody [class.z-has-vertical-scroll]=\"hasVerticalScroll()\">\n @for (row of groupRows; track row.id) {\n <tr\n z-table-row-hover\n [style.height.px]=\"virtualRowHeight()\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-selected]=\"row.getIsSelected()\"\n [class.z-indeterminate-selected]=\"row.getIsSomeSelected() && !row.getIsSelected()\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan =\n cell | zTableBodyCellRender: row.getVisibleCells() : zConfig().columns;\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles: cell : 'body' : row.getVisibleCells() : zConfig().columns\n \"\n [class.z-sticky-left]=\"cell.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"cell.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n cell.column.getIsPinned() === 'left' && cell.column.getIsLastColumn('left')\n \"\n [class.z-sticky-right-first]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [attr.rowspan]=\"\n cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\n \"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\"\n [class]=\"cell | zTableCellConfig: zConfig().columns : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig().columns : 'cellStyle'\">\n @if (cell.column.id === 'select') {\n <!-- Row Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"cell.row.getIsSelected()\"\n [zIndeterminate]=\"cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\"\n [zDisabled]=\"!cell.row.getCanSelect()\"\n (zChange)=\"cell.row.toggleSelected()\" />\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button -->\n <div class=\"flex items-center justify-center\">\n @if (cell.row.subRows && cell.row.subRows.length > 0) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n </div>\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig().columns : 'contentTooltip'\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\" />\n } @else {\n <!-- Default/innerHTML rendering -->\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"></div>\n }\n </ng-container>\n }\n </td>\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n } @else {\n <!-- Normal Scroll Mode -->\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody [class.z-has-vertical-scroll]=\"hasVerticalScroll()\">\n <!-- Row Template -->\n <ng-template #rowTemplate let-row>\n <tr\n z-table-row-hover\n [attr.data-row-id]=\"row.id\"\n [ngStyle]=\"row | zTableRow: table : 'pinningStyles' : virtualRowHeight()\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-selected]=\"row.getIsSelected()\"\n [class.z-indeterminate-selected]=\"row.getIsSomeSelected() && !row.getIsSelected()\"\n [class.z-pinned-top]=\"row.getIsPinned() === 'top'\"\n [class.z-pinned-bottom]=\"row.getIsPinned() === 'bottom'\"\n [class.z-shadow-bottom]=\"\n showHeaderFooterShadow() &&\n row.getIsPinned() === 'top' &&\n (row | zTableRow: table : 'isLastTopPinned')\n \"\n [class.z-shadow-top]=\"\n showHeaderFooterShadow() &&\n row.getIsPinned() === 'bottom' &&\n (row | zTableRow: table : 'isFirstBottomPinned')\n \"\n [attr.data-depth]=\"row.depth\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan = cell | zTableBodyCellRender: row.getVisibleCells() : zConfig().columns;\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column | zTablePinningStyles: cell : 'body' : row.getVisibleCells() : zConfig().columns\n \"\n [class]=\"cell | zTableCellConfig: zConfig().columns : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig().columns : 'cellStyle'\"\n [class.z-sticky-left]=\"cell.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"cell.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n cell.column.getIsPinned() === 'left' && cell.column.getIsLastColumn('left')\n \"\n [class.z-sticky-right-first]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"cell.column.id === 'actions'\"\n [attr.rowspan]=\"cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\">\n @if (cell.column.id === 'select') {\n <!-- Row Checkbox with Pin Button -->\n <div class=\"flex items-center justify-center gap-1\">\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"cell.row.getIsSelected()\"\n [zIndeterminate]=\"cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\"\n [zDisabled]=\"!cell.row.getCanSelect()\"\n (zChange)=\"cell.row.toggleSelected()\" />\n @if (zConfig().enableRowPinning && cell.row.depth === 0 && !hasBodyRowSpan()) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>Pin top</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>Pin bottom</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>Unpin</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button with Row Pin Popover -->\n <div class=\"flex items-center justify-center gap-1\">\n @if (cell.row.subRows && cell.row.subRows.length > 0) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n @if (\n zConfig().enableRowPinning &&\n cell.row.depth === 0 &&\n !(cell.row.subRows && cell.row.subRows.length > 0) &&\n !hasBodyRowSpan()\n ) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>Pin top</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>Pin bottom</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>Unpin</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'actions') {\n <!-- Actions Column - Row Pin Only (for parent rows) -->\n @if (cell.row.depth === 0 && !hasBodyRowSpan()) {\n <div class=\"flex items-center justify-center\">\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>Pin top</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>Pin bottom</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>Unpin</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n </div>\n }\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else {\n <!-- Default/innerHTML rendering -->\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"></div>\n }\n </ng-container>\n }\n </td>\n }\n }\n </tr>\n\n <!-- Expanded Row Detail -->\n @if (row.getIsExpanded() && row.depth === 0 && !row.subRows?.length && zConfig().expandedRowTemplate) {\n <tr class=\"z-expanded-row\">\n <td [attr.colspan]=\"row.getVisibleCells().length\" class=\"p-0\">\n <ng-container *ngTemplateOutlet=\"zConfig().expandedRowTemplate!; context: { $implicit: row }\" />\n </td>\n </tr>\n }\n </ng-template>\n\n <!-- Render Top Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of table.getTopRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n\n <!-- Render Center Rows -->\n @for (row of table.getCenterRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n\n <!-- Render Bottom Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of bottomRowsReversed(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n </tbody>\n </table>\n }\n </ng-scrollbar>\n }\n <!-- end @else -->\n </div>\n\n <!-- Footer table -->\n @if (hasFooter()) {\n <div\n class=\"z-tfoot-wrapper\"\n [class.z-shadow-footer]=\"shouldFooterShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #tfootWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tfoot>\n @for (footerGroup of orderedFooterGroups(); track footerGroup.id) {\n @if (footerGroup | zTableFooterContent: zConfig().columns) {\n <tr>\n @for (footer of footerGroup.headers; track footer.id) {\n @let rowSpan = footer | zTableSpan: zConfig().columns : 'footerRowSpan';\n @let shouldRender = footer | zTableFooterRender: footerGroup.headers : zConfig().columns;\n @if (rowSpan && shouldRender) {\n <th\n [ngStyle]=\"footer.column | zTablePinningStyles: footer : 'header' : table : zConfig().columns\"\n [class]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerClass') +\n ' ' +\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass')\n \"\n [style]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerStyle'\"\n [class.z-sticky-left]=\"footer.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"footer.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"footer | zTableHeaderPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"footer | zTableHeaderPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"footer | zTableHeaderPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"footer | zTableCellOffset: orderedLeafColumns()\"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"footer | zTableSpan: zConfig().columns : 'footerColSpan'\">\n @let configFooterContent =\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContent';\n @if (footer.column.columnDef.footer) {\n <ng-container\n *flexRender=\"footer.column.columnDef.footer; props: footer.getContext(); let footerContent\">\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n @if (footerContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <ng-container\n *ngTemplateOutlet=\"footerContent; context: { $implicit: footer.getContext() }\" />\n } @else if (footerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"footerContent\"\n [zTooltip]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip'\n \" />\n } @else {\n <!-- Default/string rendering -->\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n footerContent\n \">\n {{ footerContent }}\n </span>\n }\n </div>\n </ng-container>\n } @else if (configFooterContent) {\n <!-- Fallback for group columns without TanStack footer -->\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n $any(configFooterContent)\n \">\n {{ configFooterContent }}\n </span>\n </div>\n }\n </th>\n }\n }\n </tr>\n }\n }\n </tfoot>\n </table>\n </div>\n }\n</div>\n\n<!-- Pagination -->\n@if (zConfig().enablePagination && zConfig().pagination?.enabled !== false) {\n @let totalRows = table.getFilteredRowModel().rows.length;\n @let totalLabel = zConfig().pagination?.totalLabel ?? '{total} total rows';\n <div class=\"mt-4 flex items-center justify-between gap-4\">\n <div class=\"text-sm text-gray-500\">\n {{ totalLabel.replace('{total}', zConfig().totalCount?.toString() || totalRows.toString()) }}\n </div>\n <z-pagination\n [zTotal]=\"zConfig().totalCount ?? totalRows\"\n [(zPageIndex)]=\"pagination().pageIndex\"\n [(zPageSize)]=\"pagination().pageSize\"\n [zPageSizeOptions]=\"zConfig().pagination?.pageSizeOptions ?? [10, 20, 50, 100]\"\n [zShowSizeChanger]=\"zConfig().pagination?.showSizeChanger ?? true\"\n [zShowQuickJumper]=\"zConfig().pagination?.showQuickJumper ?? false\"\n [zShowTotal]=\"false\"\n [zDisabled]=\"zConfig().pagination?.disabled || zLoading() || isProcessing()\"\n (zOnPageChange)=\"onPageChange($event)\" />\n </div>\n}\n\n<!-- Settings Drawer -->\n<z-drawer\n [(zVisible)]=\"showSettingsDrawer\"\n zTitle=\"Table Settings\"\n zPlacement=\"right\"\n zWidth=\"500px\"\n [zShadow]=\"true\"\n [zOkText]=\"null\"\n zCancelText=\"Close\">\n <div class=\"z-table-settings-drawer px-4\">\n <!-- Display Settings -->\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">Display Settings</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">Customize table appearance</p>\n <div class=\"grid grid-cols-2 gap-x-4 gap-y-3\">\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"showHorizontalBorder()\"\n zText=\"Horizontal Border\"\n (zChange)=\"showHorizontalBorder.set(!showHorizontalBorder())\" />\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"showVerticalBorder()\"\n zText=\"Vertical Border\"\n (zChange)=\"showVerticalBorder.set(!showVerticalBorder())\" />\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"showHeaderFooterShadow()\"\n zText=\"Header/Footer Shadow\"\n (zChange)=\"showHeaderFooterShadow.set(!showHeaderFooterShadow())\" />\n </div>\n </div>\n\n <!-- Divider -->\n <div class=\"border-border my-4 border-t\"></div>\n\n <!-- Unified Column Settings -->\n @if (zConfig().enableSettings) {\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">Column Settings</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">Toggle visibility, drag to reorder, and pin columns</p>\n\n <!-- Unpinned Columns (Draggable) -->\n <div cdkDropList (cdkDropListDropped)=\"onPendingColumnDrop($event)\" class=\"space-y-1.5\">\n @for (columnId of columnOrder(); track columnId; let i = $index) {\n @if (columnId !== 'expand' && columnId !== 'select') {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = columnVisibility()[columnId] !== false;\n @let canPin = column?.getCanPin() !== false && zConfig().enableColumnPinning;\n @if (!isPinned) {\n <div\n cdkDrag\n [cdkDragData]=\"columnId\"\n cdkDragLockAxis=\"y\"\n [cdkDragDisabled]=\"false\"\n class=\"border-border bg-card hover:border-primary flex cursor-move items-center gap-2 rounded border px-2 py-1.5 text-sm transition-all hover:shadow-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Drag Handle -->\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n class=\"border-input h-4 w-4 shrink-0 cursor-pointer rounded\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns }}\n </span>\n @let parents = columnId | zTableColumnParents: zConfig().columns;\n @if (parents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ parents }})</span>\n }\n </span>\n\n <!-- Pin Buttons -->\n @if (canPin) {\n <div class=\"flex shrink-0 items-center gap-0.5\">\n <button\n type=\"button\"\n (click)=\"onToggleColumnPin(columnId, 'left')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors\"\n title=\"Pin Left\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"12\" />\n </button>\n <button\n type=\"button\"\n (click)=\"onToggleColumnPin(columnId, 'right')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors\"\n title=\"Pin Right\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n }\n }\n }\n </div>\n\n <!-- Pinned Columns Section -->\n @if (zConfig().enableColumnPinning) {\n @if (pinnedColumnIds().length > 0) {\n <div class=\"border-border mt-4 border-t pt-4\">\n <h5 class=\"text-muted-foreground mb-2 text-xs font-medium\">Pinned Columns</h5>\n <div class=\"space-y-1.5\">\n @for (columnId of pinnedColumnIds(); track columnId) {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = columnVisibility()[columnId] !== false;\n <div\n class=\"border-border bg-muted/30 flex items-center gap-2 rounded border px-2 py-1.5 text-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Pin Icon -->\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"text-primary shrink-0\" />\n\n <!-- Visibility Checkbox -->\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n class=\"border-input h-4 w-4 shrink-0 cursor-pointer rounded\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns }}\n </span>\n @let pinnedParents = columnId | zTableColumnParents: zConfig().columns;\n @if (pinnedParents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ pinnedParents }})</span>\n }\n </span>\n\n <!-- Position Badge -->\n <span class=\"bg-primary/10 text-primary shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium\">\n {{ isPinned === 'left' ? 'LEFT' : 'RIGHT' }}\n </span>\n\n <!-- Unpin Button -->\n <button\n type=\"button\"\n (click)=\"onToggleColumnPin(columnId, isPinned === 'left' ? 'left' : 'right')\"\n class=\"text-muted-foreground hover:bg-muted hover:text-foreground cursor-pointer rounded p-1 text-xs transition-colors\"\n title=\"Unpin\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n </div>\n }\n }\n </div>\n }\n </div>\n</z-drawer>\n", styles: [":host{display:flex;flex-direction:column;height:100%;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px}.z-table-container{display:flex;flex-direction:column;position:relative;width:100%;height:100%;overflow:hidden;border-radius:8px;border:thin solid var(--border);background-color:var(--card)}.z-table-container.z-hide-horizontal-border th,.z-table-container.z-hide-horizontal-border td{border-bottom:none!important;border-top:none!important}.z-table-container.z-hide-vertical-border th,.z-table-container.z-hide-vertical-border td{border-left:none!important}table{width:fit-content;min-width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed;font-size:14px}.z-table-toolbar .z-settings-btn{transition:all .15s ease}.z-table-toolbar .z-settings-btn:hover{border-color:var(--muted-foreground)}.z-table-cell-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;-webkit-user-select:text;user-select:text}.z-thead-wrapper{background:var(--muted);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.z-thead-wrapper::-webkit-scrollbar{display:none}.z-thead-wrapper th{height:auto;padding:8px 12px;text-align:left;vertical-align:middle;font-weight:500;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--muted);border-left:thin solid var(--border);border-bottom:thin solid var(--border);-webkit-user-select:none;user-select:none}.z-thead-wrapper th.z-at-left-edge{border-left:none}.z-thead-wrapper th[colspan]{text-align:center;background:var(--muted);font-weight:500;color:var(--foreground)}.z-thead-wrapper.z-shadow-header{box-shadow:0 1px 3px #00000014;position:relative;z-index:15}.z-thead-wrapper.z-shadow-header:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d}.z-tbody-wrapper{flex:1;min-height:200px;display:flex;flex-direction:column}.z-tbody-scrollbar{flex:1;height:100%}.z-empty-state,.z-loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;min-height:200px;height:100%;color:var(--muted-foreground);font-size:.875rem;animation:z-fade-in .2s ease-out}.z-tbody-scrollbar,.z-tbody-scrollbar table{animation:z-fade-in .2s ease-out}@keyframes z-fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.z-tbody-wrapper tr{transition:background-color .15s ease}.z-tbody-wrapper tr:hover,.z-tbody-wrapper tr:hover td[style*=sticky]{background-color:var(--muted)}.z-tbody-wrapper tr.z-pinned-top td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-top td.z-sticky-right,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-right{z-index:3}.z-tbody-wrapper tr.z-shadow-bottom{box-shadow:0 1px 3px #00000014!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-bottom:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d!important}.z-tbody-wrapper tr.z-shadow-top{box-shadow:0 -2px 4px #0000000d!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-top:where(.dark,.dark *){box-shadow:0 -2px 4px #0003!important}.z-tbody-wrapper td{padding:8px 12px;height:40px;vertical-align:middle;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--card);border-left:thin solid var(--border);border-bottom:thin solid var(--border);box-sizing:border-box}.z-tbody-wrapper td.z-at-left-edge{border-left:none}.z-tbody-wrapper td i{color:var(--muted-foreground);font-style:italic}.z-tbody-wrapper td[rowspan]{vertical-align:top;padding-top:12px}.z-tbody-wrapper td.z-row-hover{background-color:var(--muted)!important}.z-tbody-wrapper td.z-col-select,.z-tbody-wrapper td.z-col-expand,.z-tbody-wrapper td.z-col-actions{padding:8px 4px!important;text-align:center}.z-tbody-wrapper tr.z-child-row td.z-col-select:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-expand:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-actions:first-child{padding-left:0!important}.z-virtual-scroll-inner{position:relative;width:100%}.z-virtual-row{position:absolute;top:0;left:0;width:100%}tr.z-child-row td:first-child{padding-left:12px!important}tbody tr.z-selected,tbody tr.z-selected td{background-color:color-mix(in srgb,var(--primary) 15%,transparent)!important}tbody tr.z-selected:hover,tbody tr.z-selected:hover td{background-color:color-mix(in srgb,var(--primary) 20%,transparent)!important}tbody tr.z-indeterminate-selected,tbody tr.z-indeterminate-selected td{background-color:color-mix(in srgb,var(--primary) 10%,transparent)!important}tbody tr.z-indeterminate-selected:hover,tbody tr.z-indeterminate-selected:hover td{background-color:color-mix(in srgb,var(--primary) 15%,transparent)!important}tbody tr.z-pinned-top td{background-color:var(--card)!important}tbody tr.z-pinned-top:hover{background-color:var(--muted)}tbody tr.z-pinned-top:hover td{background-color:var(--muted)!important}tbody tr.z-pinned-bottom td{background-color:var(--card)!important}tbody tr.z-pinned-bottom:hover{background-color:var(--muted)}tbody tr.z-pinned-bottom:hover td,tr.z-expanded-row td{background-color:var(--muted)!important}thead th{position:relative}thead th .z-resizer{position:absolute;right:0;top:0;height:100%;width:5px;background:#0000001a;cursor:col-resize;-webkit-user-select:none;user-select:none;touch-action:none;opacity:0;transition:opacity .2s ease}thead th .z-resizer:where(.dark,.dark *){background:#ffffff1a}thead th .z-resizer:hover,thead th .z-resizer.z-is-resizing{opacity:1;background:var(--primary);opacity:.5}thead th .z-resizer.z-is-resizing{background:var(--primary);opacity:1}thead th .z-resizer.z-resizer-left{right:auto;left:0}thead th:hover .z-resizer{opacity:1}.z-thead-wrapper th.z-sticky-left,.z-thead-wrapper th.z-sticky-right,.z-tbody-wrapper th.z-sticky-left,.z-tbody-wrapper th.z-sticky-right,.z-tfoot-wrapper th.z-sticky-left,.z-tfoot-wrapper th.z-sticky-right{background-color:var(--muted);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper td.z-sticky-left,.z-thead-wrapper td.z-sticky-right,.z-tbody-wrapper td.z-sticky-left,.z-tbody-wrapper td.z-sticky-right,.z-tfoot-wrapper td.z-sticky-left,.z-tfoot-wrapper td.z-sticky-right{background-color:var(--card);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper th.z-sticky-left-last,.z-thead-wrapper td.z-sticky-left-last,.z-tbody-wrapper th.z-sticky-left-last,.z-tbody-wrapper td.z-sticky-left-last,.z-tfoot-wrapper th.z-sticky-left-last,.z-tfoot-wrapper td.z-sticky-left-last{position:relative;overflow:visible}.z-thead-wrapper th.z-sticky-left-last:after,.z-thead-wrapper td.z-sticky-left-last:after,.z-tbody-wrapper th.z-sticky-left-last:after,.z-tbody-wrapper td.z-sticky-left-last:after,.z-tfoot-wrapper th.z-sticky-left-last:after,.z-tfoot-wrapper td.z-sticky-left-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-30px;width:30px;pointer-events:none;box-shadow:inset 10px 0 8px -8px #0000001a;z-index:10}.z-thead-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-thead-wrapper td.z-sticky-left-last:after:where(.dark,.dark *),.z-tbody-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-tbody-wrapper td.z-sticky-left-last:after:where(.dark,.dark *),.z-tfoot-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-tfoot-wrapper td.z-sticky-left-last:after:where(.dark,.dark *){box-shadow:inset 10px 0 8px -8px #0000004d}.z-thead-wrapper th.z-sticky-left-last:after,.z-thead-wrapper td.z-sticky-left-last:after,.z-tbody-wrapper th.z-sticky-left-last:after,.z-tbody-wrapper td.z-sticky-left-last:after,.z-tfoot-wrapper th.z-sticky-left-last:after,.z-tfoot-wrapper td.z-sticky-left-last:after{opacity:0;transition:opacity .2s ease}.z-thead-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-thead-wrapper.z-scroll-left td.z-sticky-left-last:after,.z-tbody-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-tbody-wrapper.z-scroll-left td.z-sticky-left-last:after,.z-tfoot-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-tfoot-wrapper.z-scroll-left td.z-sticky-left-last:after{opacity:1}.z-thead-wrapper th.z-sticky-right-first,.z-thead-wrapper td.z-sticky-right-first,.z-tbody-wrapper th.z-sticky-right-first,.z-tbody-wrapper td.z-sticky-right-first,.z-tfoot-wrapper th.z-sticky-right-first,.z-tfoot-wrapper td.z-sticky-right-first{position:relative;overflow:visible}.z-thead-wrapper th.z-sticky-right-first:before,.z-thead-wrapper td.z-sticky-right-first:before,.z-tbody-wrapper th.z-sticky-right-first:before,.z-tbody-wrapper td.z-sticky-right-first:before,.z-tfoot-wrapper th.z-sticky-right-first:before,.z-tfoot-wrapper td.z-sticky-right-first:before{content:\"\";position:absolute;top:0;bottom:0;left:-30px;width:30px;pointer-events:none;box-shadow:inset -10px 0 8px -8px #0000001a;z-index:10;opacity:0;transition:opacity .2s ease}.z-thead-wrapper th.z-sticky-right-first:before:where(.dark,.dark *),.z-thead-wrapper td.z-sticky-right-first:before:where(.dark,.dark *),.z-tbody-wrapper th.z-sticky-right-first:before:where(.dark,.dark *),.z-tbody-wrapper td.z-sticky-right-first:before:where(.dark,.dark *),.z-tfoot-wrapper th.z-sticky-right-first:before:where(.dark,.dark *),.z-tfoot-wrapper td.z-sticky-right-first:before:where(.dark,.dark *){box-shadow:inset -10px 0 8px -8px #0000004d}.z-thead-wrapper.z-scroll-right th.z-sticky-right-first,.z-thead-wrapper.z-scroll-right td.z-sticky-right-first,.z-tbody-wrapper.z-scroll-right th.z-sticky-right-first,.z-tbody-wrapper.z-scroll-right td.z-sticky-right-first,.z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first,.z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first{border-left:none}.z-thead-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-thead-wrapper.z-scroll-right td.z-sticky-right-first:before,.z-tbody-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-tbody-wrapper.z-scroll-right td.z-sticky-right-first:before,.z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first:before{opacity:1}.z-thead-wrapper th.z-sticky-right-last,.z-tfoot-wrapper th.z-sticky-right-last{position:relative}.z-thead-wrapper th.z-sticky-right-last:after,.z-tfoot-wrapper th.z-sticky-right-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-30px;width:30px;background:var(--muted);pointer-events:none}.z-tfoot-wrapper{background:var(--muted);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.z-tfoot-wrapper::-webkit-scrollbar{display:none}.z-tfoot-wrapper th{height:auto;padding:8px 12px;text-align:left;vertical-align:middle;font-weight:500;font-size:12px;color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.5px;background:var(--muted);border-left:thin solid var(--border);border-top:thin solid var(--border)}.z-tfoot-wrapper th.z-at-left-edge{border-left:none}.z-tfoot-wrapper.z-shadow-footer{box-shadow:0 -2px 4px #0000000d;position:relative;z-index:15}.z-tfoot-wrapper.z-shadow-footer:where(.dark,.dark *){box-shadow:0 -2px 4px #0003}.z-pin-btn{padding:2px 4px;border-radius:4px;color:var(--muted-foreground);transition:all .15s ease}.z-pin-btn:hover{background-color:var(--muted);color:var(--foreground)}.z-pin-btn.z-pin-btn-active{color:var(--primary);background-color:var(--primary)}.z-pin-btn.z-pin-btn-active:hover{background-color:var(--primary);opacity:.8}.z-row-pin-trigger{opacity:1}.z-row-pin-trigger.text-primary{color:var(--primary)}.z-header-pin-trigger{opacity:1}.z-header-pin-trigger.text-primary{color:var(--primary)}th{overflow:hidden}th .z-header-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cdk-drag-preview{box-shadow:0 4px 12px #00000026;opacity:.9;border-radius:4px;background-color:var(--card);z-index:9999}.cdk-drag-preview:where(.dark,.dark *){box-shadow:0 4px 12px #0006}.cdk-drag-placeholder{opacity:.3;background-color:var(--muted);border:2px dashed var(--primary)}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.cdk-drop-list-dragging .cdk-drag:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.z-table-settings-drawer input[type=checkbox]{appearance:none;-webkit-appearance:none;-moz-appearance:none;width:1rem;height:1rem;border:thin solid var(--input);border-radius:.25rem;background-color:var(--background);cursor:pointer;position:relative;transition:all .2s ease}.z-table-settings-drawer input[type=checkbox]:hover{border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked{background-color:var(--primary);border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked:after{content:\"\";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:.375rem;height:.625rem;border:solid var(--primary-foreground);border-width:0 2px 2px 0;transform:translate(-50%,-60%) rotate(45deg)}.z-table-settings-drawer input[type=checkbox]:disabled{opacity:.5;cursor:not-allowed}\n"] }]
|
|
2620
|
+
}, exportAs: 'zTable', template: "<!-- Toolbar: Search & Settings -->\n@if (zConfig().enableSearch || zConfig().enableSettings) {\n <div class=\"z-table-toolbar mb-2 flex items-center justify-between gap-4\">\n <!-- Search -->\n @if (zConfig().enableSearch) {\n <z-input\n class=\"w-64\"\n zSize=\"sm\"\n zPlaceholder=\"Search...\"\n [zSearch]=\"true\"\n [zDebounce]=\"300\"\n (zOnSearch)=\"onSearchChange($event)\" />\n } @else {\n <div></div>\n }\n\n <!-- Settings Button -->\n @if (zConfig().enableSettings) {\n <z-button zType=\"outline\" zSize=\"sm\" zTypeIcon=\"lucideSettings\" (click)=\"openSettingsDrawer()\">Settings</z-button>\n }\n </div>\n}\n\n<div\n class=\"z-table-container shadow-xs\"\n [class.z-hide-horizontal-border]=\"!showHorizontalBorder()\"\n [class.z-hide-vertical-border]=\"!showVerticalBorder()\"\n [style.max-height]=\"zConfig().maxHeight\"\n [style.min-height]=\"zConfig().minHeight\">\n <!-- Shared colgroup template -->\n <ng-template #colGroupTpl>\n <colgroup>\n @for (column of orderedLeafColumns(); track column.id) {\n @if (column.getIsVisible()) {\n <col [style.width]=\"'calc(var(--col-' + column.id + '-size) * 1px)'\" />\n }\n }\n </colgroup>\n </ng-template>\n\n <!-- Header table -->\n <div\n class=\"z-thead-wrapper shadow-xs\"\n [class.z-shadow-header]=\"shouldHeaderShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #theadWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <thead>\n @for (headerGroup of orderedHeaderGroups(); track headerGroup.id) {\n <tr>\n @for (header of headerGroup.headers; track header.id) {\n @let rowSpan = header | zTableSpan: zConfig().columns : 'headerRowSpan';\n @let shouldRender = header | zTableHeaderRender: headerGroup.headers : zConfig().columns;\n @if (rowSpan && shouldRender) {\n <th\n [attr.id]=\"header.column.id\"\n [ngStyle]=\"header.column | zTablePinningStyles: header : 'header' : table : zConfig().columns\"\n [class]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerClass'\"\n [style]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerStyle'\"\n [class.z-sticky-left]=\"header.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"header.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"header | zTableHeaderPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"header | zTableHeaderPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"header | zTableHeaderPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"header | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"header.column.id === 'select'\"\n [class.z-col-expand]=\"header.column.id === 'expand'\"\n [class.z-col-actions]=\"header.column.id === 'actions'\"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"header | zTableSpan: zConfig().columns : 'headerColSpan'\">\n @if (header.column.id === 'select') {\n <!-- Header Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"table.getIsAllRowsSelected()\"\n [zIndeterminate]=\"table.getIsSomeRowsSelected() && !table.getIsAllRowsSelected()\"\n (zChange)=\"table.toggleAllRowsSelected()\" />\n </div>\n } @else if (header.column.id === 'expand') {\n <!-- Expand All Button -->\n <div class=\"flex items-center justify-center\">\n <button\n type=\"button\"\n (click)=\"table.toggleAllRowsExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"table.getIsSomeRowsExpanded()\" />\n </button>\n </div>\n } @else if (header.column.id === 'actions') {\n <!-- Empty header for actions column -->\n <div class=\"flex items-center justify-center\"></div>\n } @else {\n <!-- Header Content with Sort and Pin -->\n <div class=\"flex w-full items-center gap-1\">\n <!-- Header Text -->\n <div\n class=\"flex min-w-0 flex-1 items-center gap-1.5\"\n [class.justify-center]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerAlignClass') ===\n 'text-right'\n \">\n <ng-container\n *flexRender=\"header.column.columnDef.header; props: header.getContext(); let headerContent\">\n @if (headerContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <ng-container\n *ngTemplateOutlet=\"headerContent; context: { $implicit: header.getContext() }\" />\n } @else if (headerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n class=\"min-w-0 truncate\"\n [zText]=\"headerContent\"\n [zTooltip]=\"header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip'\" />\n } @else {\n <!-- Default/innerHTML rendering -->\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (header.column.id | zTableColumnConfig: zConfig().columns : 'headerTooltip') ||\n headerContent\n \"\n [innerHTML]=\"headerContent | zSafeHtml\"></span>\n }\n </ng-container>\n <!-- Sort Icon (clickable) - hidden when table has rowSpan columns -->\n @if (header.column.getCanSort() && !hasBodyRowSpan()) {\n <span\n class=\"shrink-0 cursor-pointer text-gray-500 hover:text-gray-700\"\n (click)=\"handleSort($event, header.column.getToggleSortingHandler())\">\n @if (header.column.getIsSorted() === 'asc') {\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n } @else if (header.column.getIsSorted() === 'desc') {\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n } @else {\n <z-icon zType=\"lucideArrowDownUp\" zSize=\"12\" class=\"opacity-30\" />\n }\n </span>\n }\n </div>\n\n <!-- Column Pin Popover -->\n @if (header.column.getCanPin() && zConfig().enableColumnPinning) {\n <ng-template #colPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (header.column.getIsPinned() !== 'left') {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, 'left'); colPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideChevronLeft\" zSize=\"12\" />\n <span>Pin left</span>\n </button>\n }\n @if (header.column.getIsPinned() !== 'right') {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, 'right'); colPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideChevronRight\" zSize=\"12\" />\n <span>Pin right</span>\n </button>\n }\n @if (header.column.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"handleColumnPin(header.column.id, false); colPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>Unpin</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #colPinPopover=\"zPopover\"\n [zPopoverContent]=\"colPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom-right\"\n [zOffset]=\"3\"\n class=\"z-header-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex shrink-0 cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"header.column.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"12\" class=\"rotate-90\" />\n </button>\n }\n </div>\n }\n <!-- Column Filter -->\n @if (header.column.getCanFilter() && hasFiltering()) {\n <div class=\"mt-1\">\n <z-table-filter [zColumn]=\"$any(header.column)\" [zTable]=\"$any(table)\" />\n </div>\n }\n <!-- Column Resizer -->\n @if (\n header.column.id !== 'select' &&\n header.column.id !== 'expand' &&\n zConfig().enableColumnResizing !== false\n ) {\n <div\n class=\"z-resizer\"\n [class.z-is-resizing]=\"header.column.getIsResizing()\"\n [class.z-resizer-left]=\"\n header.column.getIsPinned() === 'right' || header.column.getIsLastColumn()\n \"\n (dblclick)=\"header.column.resetSize()\"\n (mousedown)=\"header.getResizeHandler()($event)\"\n (touchstart)=\"header.getResizeHandler()($event)\"></div>\n }\n </th>\n }\n }\n </tr>\n }\n </thead>\n </table>\n </div>\n\n <!-- Body table -->\n <div\n class=\"z-tbody-wrapper relative\"\n #tbodyContainer\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\">\n @if (zLoading() || isProcessing()) {\n <!-- Loading State -->\n <div class=\"z-loading-state\">\n <z-loading [zLoading]=\"true\" zSize=\"lg\" zText=\"Loading...\" />\n </div>\n } @else if (isEmpty()) {\n <div class=\"z-empty-state\">\n @if (isNoSearchResults()) {\n <!-- No search results -->\n <z-icon zType=\"lucideSearchX\" zSize=\"40\" class=\"text-muted-foreground/50\" />\n <p class=\"text-muted-foreground text-sm\">No results found</p>\n } @else {\n <!-- Empty data -->\n <z-icon zType=\"lucidePackageOpen\" zSize=\"40\" class=\"text-muted-foreground/50\" />\n <p class=\"text-muted-foreground text-sm\">No data</p>\n }\n </div>\n } @else {\n <ng-scrollbar class=\"z-tbody-scrollbar\" #tbodyWrapper track=\"all\" (scroll)=\"onTbodyScroll($event)\">\n @if (isVirtual()) {\n <!-- Virtual Scroll Mode -->\n <div\n class=\"z-virtual-scroll-inner\"\n [style.height.px]=\"virtualizer.getTotalSize()\"\n [style.min-width.px]=\"table.getTotalSize()\">\n @for (virtualItem of virtualizer.getVirtualItems(); track virtualItem.index) {\n @let groupRows = dynamicGroupRows()[virtualItem.index] || [];\n <div\n class=\"z-virtual-row\"\n [style.height.px]=\"dynamicGroupHeights()[virtualItem.index] ?? groupSize() * virtualRowHeight()\"\n [style.transform]=\"'translateY(' + virtualItem.start + 'px)'\">\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody [class.z-has-vertical-scroll]=\"hasVerticalScroll()\">\n @for (row of groupRows; track row.id) {\n <tr\n z-table-row-hover\n [style.height.px]=\"virtualRowHeight()\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-selected]=\"row.getIsSelected()\"\n [class.z-indeterminate-selected]=\"row.getIsSomeSelected() && !row.getIsSelected()\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan =\n cell | zTableBodyCellRender: row.getVisibleCells() : zConfig().columns;\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column\n | zTablePinningStyles: cell : 'body' : row.getVisibleCells() : zConfig().columns\n \"\n [class.z-sticky-left]=\"cell.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"cell.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n cell.column.getIsPinned() === 'left' && cell.column.getIsLastColumn('left')\n \"\n [class.z-sticky-right-first]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [attr.rowspan]=\"\n cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\n \"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\"\n [class]=\"cell | zTableCellConfig: zConfig().columns : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig().columns : 'cellStyle'\">\n @if (cell.column.id === 'select') {\n <!-- Row Checkbox -->\n <div class=\"flex items-center justify-center\">\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"cell.row.getIsSelected()\"\n [zIndeterminate]=\"cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\"\n [zDisabled]=\"!cell.row.getCanSelect()\"\n (zChange)=\"cell.row.toggleSelected()\" />\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button -->\n <div class=\"flex items-center justify-center\">\n @if (cell.row.subRows && cell.row.subRows.length > 0) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n </div>\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else if (cellContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"cellContent\"\n [zTooltip]=\"cell | zTableCellConfig: zConfig().columns : 'contentTooltip'\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\" />\n } @else {\n <!-- Default/innerHTML rendering -->\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"></div>\n }\n </ng-container>\n }\n </td>\n }\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n } @else {\n <!-- Normal Scroll Mode -->\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tbody [class.z-has-vertical-scroll]=\"hasVerticalScroll()\">\n <!-- Row Template -->\n <ng-template #rowTemplate let-row>\n <tr\n z-table-row-hover\n [attr.data-row-id]=\"row.id\"\n [ngStyle]=\"row | zTableRow: table : 'pinningStyles' : virtualRowHeight()\"\n [class.z-child-row]=\"row.depth > 0\"\n [class.z-selected]=\"row.getIsSelected()\"\n [class.z-indeterminate-selected]=\"row.getIsSomeSelected() && !row.getIsSelected()\"\n [class.z-pinned-top]=\"row.getIsPinned() === 'top'\"\n [class.z-pinned-bottom]=\"row.getIsPinned() === 'bottom'\"\n [class.z-shadow-bottom]=\"\n showHeaderFooterShadow() &&\n row.getIsPinned() === 'top' &&\n (row | zTableRow: table : 'isLastTopPinned')\n \"\n [class.z-shadow-top]=\"\n showHeaderFooterShadow() &&\n row.getIsPinned() === 'bottom' &&\n (row | zTableRow: table : 'isFirstBottomPinned')\n \"\n [attr.data-depth]=\"row.depth\">\n @for (cell of row.getVisibleCells(); track cell.id) {\n @let shouldRenderRowSpan =\n cell | zTableSpan: zConfig().columns : 'shouldRender' : table.getRowModel().rows;\n @let shouldRenderColSpan = cell | zTableBodyCellRender: row.getVisibleCells() : zConfig().columns;\n @if (shouldRenderRowSpan && shouldRenderColSpan) {\n <td\n [ngStyle]=\"\n cell.column | zTablePinningStyles: cell : 'body' : row.getVisibleCells() : zConfig().columns\n \"\n [class]=\"cell | zTableCellConfig: zConfig().columns : 'cellClass'\"\n [style]=\"cell | zTableCellConfig: zConfig().columns : 'cellStyle'\"\n [class.z-sticky-left]=\"cell.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"cell.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"\n cell.column.getIsPinned() === 'left' && cell.column.getIsLastColumn('left')\n \"\n [class.z-sticky-right-first]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsFirstColumn('right')\n \"\n [class.z-sticky-right-last]=\"\n cell.column.getIsPinned() === 'right' && cell.column.getIsLastColumn('right')\n \"\n [class.z-at-left-edge]=\"cell | zTableCellOffset: orderedLeafColumns()\"\n [class.z-col-select]=\"cell.column.id === 'select'\"\n [class.z-col-expand]=\"cell.column.id === 'expand'\"\n [class.z-col-actions]=\"cell.column.id === 'actions'\"\n [attr.rowspan]=\"cell | zTableSpan: zConfig().columns : 'cellRowSpan' : table.getRowModel().rows\"\n [attr.colspan]=\"cell | zTableSpan: zConfig().columns : 'cellColSpan'\">\n @if (cell.column.id === 'select') {\n <!-- Row Checkbox with Pin Button -->\n <div class=\"flex items-center justify-center gap-1\">\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"cell.row.getIsSelected()\"\n [zIndeterminate]=\"cell.row.getIsSomeSelected() && !cell.row.getIsSelected()\"\n [zDisabled]=\"!cell.row.getCanSelect()\"\n (zChange)=\"cell.row.toggleSelected()\" />\n @if (zConfig().enableRowPinning && cell.row.depth === 0 && !hasBodyRowSpan()) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>Pin top</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>Pin bottom</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>Unpin</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'expand') {\n <!-- Expand Button with Row Pin Popover -->\n <div class=\"flex items-center justify-center gap-1\">\n @if (cell.row.subRows && cell.row.subRows.length > 0) {\n <button\n type=\"button\"\n (click)=\"cell.row.toggleExpanded()\"\n class=\"hover:bg-muted flex h-6 w-6 cursor-pointer items-center justify-center rounded\">\n <z-icon\n zType=\"lucideChevronRight\"\n zSize=\"14\"\n class=\"transition-transform duration-200\"\n [class.rotate-90]=\"cell.row.getIsExpanded()\" />\n </button>\n }\n @if (\n zConfig().enableRowPinning &&\n cell.row.depth === 0 &&\n !(cell.row.subRows && cell.row.subRows.length > 0) &&\n !hasBodyRowSpan()\n ) {\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>Pin top</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>Pin bottom</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>Unpin</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n }\n </div>\n } @else if (cell.column.id === 'actions') {\n <!-- Actions Column - Row Pin Only (for parent rows) -->\n @if (cell.row.depth === 0 && !hasBodyRowSpan()) {\n <div class=\"flex items-center justify-center\">\n <ng-template #rowPinPopoverContent>\n <div class=\"flex flex-col gap-0.5 p-0.5\">\n @if (cell.row.getIsPinned() !== 'top') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('top'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowUp\" zSize=\"12\" />\n <span>Pin top</span>\n </button>\n }\n @if (cell.row.getIsPinned() !== 'bottom') {\n <button\n type=\"button\"\n (click)=\"cell.row.pin('bottom'); rowPinPopover.hideImmediate()\"\n class=\"text-foreground hover:bg-muted flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideArrowDown\" zSize=\"12\" />\n <span>Pin bottom</span>\n </button>\n }\n @if (cell.row.getIsPinned()) {\n <button\n type=\"button\"\n (click)=\"cell.row.pin(false); rowPinPopover.hideImmediate()\"\n class=\"text-destructive hover:bg-destructive/10 flex cursor-pointer items-center gap-1.5 rounded px-2 py-1 text-xs\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n <span>Unpin</span>\n </button>\n }\n </div>\n </ng-template>\n <button\n type=\"button\"\n z-popover\n #rowPinPopover=\"zPopover\"\n [zPopoverContent]=\"rowPinPopoverContent\"\n zTrigger=\"click\"\n zPosition=\"bottom\"\n class=\"z-row-pin-trigger text-muted-foreground hover:bg-muted hover:text-foreground flex cursor-pointer items-center justify-center rounded p-1\"\n [class.text-primary]=\"cell.row.getIsPinned()\">\n <z-icon zType=\"lucideEllipsis\" zSize=\"14\" class=\"rotate-90\" />\n </button>\n </div>\n }\n } @else {\n <ng-container\n *flexRender=\"cell.column.columnDef.cell; props: cell.getContext(); let cellContent\">\n @if (cellContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <div\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\">\n <ng-container\n *ngTemplateOutlet=\"cellContent; context: { $implicit: cell.getContext() }\" />\n </div>\n } @else {\n <!-- Default/innerHTML rendering -->\n <div\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (cell | zTableCellConfig: zConfig().columns : 'contentTooltip') || cellContent\n \"\n [innerHTML]=\"cellContent | zSafeHtml\"\n [ngClass]=\"cell | zTableCellConfig: zConfig().columns : 'contentClass'\"\n [ngStyle]=\"cell | zTableCellConfig: zConfig().columns : 'contentStyle'\"></div>\n }\n </ng-container>\n }\n </td>\n }\n }\n </tr>\n\n <!-- Expanded Row Detail -->\n @if (row.getIsExpanded() && row.depth === 0 && !row.subRows?.length && zConfig().expandedRowTemplate) {\n <tr class=\"z-expanded-row\">\n <td [attr.colspan]=\"row.getVisibleCells().length\" class=\"p-0\">\n <ng-container *ngTemplateOutlet=\"zConfig().expandedRowTemplate!; context: { $implicit: row }\" />\n </td>\n </tr>\n }\n </ng-template>\n\n <!-- Render Top Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of table.getTopRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n\n <!-- Render Center Rows -->\n @for (row of table.getCenterRows(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n\n <!-- Render Bottom Pinned Rows (hidden when filtered data is empty) -->\n @if (!isEmpty()) {\n @for (row of bottomRowsReversed(); track row.id) {\n <ng-container *ngTemplateOutlet=\"rowTemplate; context: { $implicit: row }\" />\n }\n }\n </tbody>\n </table>\n }\n </ng-scrollbar>\n }\n <!-- end @else -->\n </div>\n\n <!-- Footer table -->\n @if (hasFooter()) {\n <div\n class=\"z-tfoot-wrapper\"\n [class.z-shadow-footer]=\"shouldFooterShowShadow()\"\n [class.z-scroll-left]=\"hasScrollLeft()\"\n [class.z-scroll-right]=\"hasScrollRight()\"\n #tfootWrapper>\n <table [ngStyle]=\"columnSizeVars()\" [style.width.px]=\"table.getTotalSize()\">\n <ng-container *ngTemplateOutlet=\"colGroupTpl\" />\n <tfoot>\n @for (footerGroup of orderedFooterGroups(); track footerGroup.id) {\n @if (footerGroup | zTableFooterContent: zConfig().columns) {\n <tr>\n @for (footer of footerGroup.headers; track footer.id) {\n @let rowSpan = footer | zTableSpan: zConfig().columns : 'footerRowSpan';\n @let shouldRender = footer | zTableFooterRender: footerGroup.headers : zConfig().columns;\n @if (rowSpan && shouldRender) {\n <th\n [ngStyle]=\"footer.column | zTablePinningStyles: footer : 'header' : table : zConfig().columns\"\n [class]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerClass') +\n ' ' +\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass')\n \"\n [style]=\"footer.column.id | zTableColumnConfig: zConfig().columns : 'footerStyle'\"\n [class.z-sticky-left]=\"footer.column.getIsPinned() === 'left'\"\n [class.z-sticky-right]=\"footer.column.getIsPinned() === 'right'\"\n [class.z-sticky-left-last]=\"footer | zTableHeaderPin: 'isLastLeftPinned' : zConfig().columns\"\n [class.z-sticky-right-first]=\"footer | zTableHeaderPin: 'isFirstRightPinned' : zConfig().columns\"\n [class.z-sticky-right-last]=\"footer | zTableHeaderPin: 'isLastRightPinned' : zConfig().columns\"\n [class.z-at-left-edge]=\"footer | zTableCellOffset: orderedLeafColumns()\"\n [attr.rowspan]=\"rowSpan\"\n [attr.colspan]=\"footer | zTableSpan: zConfig().columns : 'footerColSpan'\">\n @let configFooterContent =\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerContent';\n @if (footer.column.columnDef.footer) {\n <ng-container\n *flexRender=\"footer.column.columnDef.footer; props: footer.getContext(); let footerContent\">\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n @if (footerContent | zTableIsTemplateRef) {\n <!-- TemplateRef rendering -->\n <ng-container\n *ngTemplateOutlet=\"footerContent; context: { $implicit: footer.getContext() }\" />\n } @else if (footerContent | zTableHasIcon) {\n <!-- Icon syntax rendering -->\n <z-table-icon-text\n [zText]=\"footerContent\"\n [zTooltip]=\"\n footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip'\n \" />\n } @else {\n <!-- Default/string rendering -->\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n footerContent\n \">\n {{ footerContent }}\n </span>\n }\n </div>\n </ng-container>\n } @else if (configFooterContent) {\n <!-- Fallback for group columns without TanStack footer -->\n <div\n class=\"flex w-full items-center\"\n [class.justify-center]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-center'\n \"\n [class.justify-end]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerAlignClass') ===\n 'text-right'\n \">\n <span\n class=\"z-table-cell-text\"\n z-tooltip\n [zContent]=\"\n (footer.column.id | zTableColumnConfig: zConfig().columns : 'footerTooltip') ||\n $any(configFooterContent)\n \">\n {{ configFooterContent }}\n </span>\n </div>\n }\n </th>\n }\n }\n </tr>\n }\n }\n </tfoot>\n </table>\n </div>\n }\n</div>\n\n<!-- Pagination -->\n@if (zConfig().enablePagination && zConfig().pagination?.enabled !== false) {\n @let totalRows = table.getFilteredRowModel().rows.length;\n @let totalLabel = zConfig().pagination?.totalLabel ?? '{total} total rows';\n <div class=\"mt-4 flex items-center justify-between gap-4\">\n <div class=\"text-sm text-gray-500\">\n {{ totalLabel.replace('{total}', zConfig().totalCount?.toString() || totalRows.toString()) }}\n </div>\n <z-pagination\n [zTotal]=\"zConfig().totalCount ?? totalRows\"\n [(zPageIndex)]=\"pagination().pageIndex\"\n [(zPageSize)]=\"pagination().pageSize\"\n [zPageSizeOptions]=\"zConfig().pagination?.pageSizeOptions ?? [10, 20, 50, 100]\"\n [zShowSizeChanger]=\"zConfig().pagination?.showSizeChanger ?? true\"\n [zShowQuickJumper]=\"zConfig().pagination?.showQuickJumper ?? false\"\n [zShowTotal]=\"false\"\n [zDisabled]=\"zConfig().pagination?.disabled || zLoading() || isProcessing()\"\n (zOnPageChange)=\"onPageChange($event)\" />\n </div>\n}\n\n<!-- Settings Drawer -->\n<z-drawer\n [(zVisible)]=\"showSettingsDrawer\"\n zTitle=\"Table Settings\"\n zPlacement=\"right\"\n zWidth=\"500px\"\n [zShadow]=\"true\"\n [zOkText]=\"null\"\n zCancelText=\"Close\">\n <div class=\"z-table-settings-drawer px-4\">\n <!-- Display Settings -->\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">Display Settings</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">Customize table appearance</p>\n <div class=\"grid grid-cols-2 gap-x-4 gap-y-3\">\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"showHorizontalBorder()\"\n zText=\"Horizontal Border\"\n (zChange)=\"showHorizontalBorder.set(!showHorizontalBorder())\" />\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"showVerticalBorder()\"\n zText=\"Vertical Border\"\n (zChange)=\"showVerticalBorder.set(!showVerticalBorder())\" />\n <z-checkbox\n zSize=\"sm\"\n [zChecked]=\"showHeaderFooterShadow()\"\n zText=\"Header/Footer Shadow\"\n (zChange)=\"showHeaderFooterShadow.set(!showHeaderFooterShadow())\" />\n </div>\n </div>\n\n <!-- Divider -->\n <div class=\"border-border my-4 border-t\"></div>\n\n <!-- Unified Column Settings -->\n @if (zConfig().enableSettings) {\n <div class=\"mb-4\">\n <h4 class=\"text-foreground mb-2 text-sm font-semibold\">Column Settings</h4>\n <p class=\"text-muted-foreground mb-3 text-xs\">Toggle visibility, drag to reorder, and pin columns</p>\n\n <!-- Unpinned Columns (Draggable) -->\n <div cdkDropList (cdkDropListDropped)=\"onPendingColumnDrop($event)\" class=\"space-y-1.5\">\n @for (columnId of columnOrder(); track columnId; let i = $index) {\n @if (columnId !== 'expand' && columnId !== 'select') {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = columnVisibility()[columnId] !== false;\n @let canPin = column?.getCanPin() !== false && zConfig().enableColumnPinning;\n @if (!isPinned) {\n <div\n cdkDrag\n [cdkDragData]=\"columnId\"\n cdkDragLockAxis=\"y\"\n [cdkDragDisabled]=\"false\"\n class=\"border-border bg-card hover:border-primary flex cursor-move items-center gap-2 rounded border px-2 py-1.5 text-sm transition-all hover:shadow-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Drag Handle -->\n <z-icon zType=\"lucideGripVertical\" zSize=\"14\" class=\"text-muted-foreground shrink-0\" />\n\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n class=\"border-input h-4 w-4 shrink-0 cursor-pointer rounded\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns }}\n </span>\n @let parents = columnId | zTableColumnParents: zConfig().columns;\n @if (parents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ parents }})</span>\n }\n </span>\n\n <!-- Pin Buttons -->\n @if (canPin) {\n <div class=\"flex shrink-0 items-center gap-0.5\">\n <button\n type=\"button\"\n (click)=\"onToggleColumnPin(columnId, 'left')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors\"\n title=\"Pin Left\">\n <z-icon zType=\"lucideArrowLeft\" zSize=\"12\" />\n </button>\n <button\n type=\"button\"\n (click)=\"onToggleColumnPin(columnId, 'right')\"\n class=\"text-muted-foreground hover:bg-muted cursor-pointer rounded p-1 text-xs transition-colors\"\n title=\"Pin Right\">\n <z-icon zType=\"lucideArrowRight\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n }\n }\n }\n </div>\n\n <!-- Pinned Columns Section -->\n @if (zConfig().enableColumnPinning) {\n @if (pinnedColumnIds().length > 0) {\n <div class=\"border-border mt-4 border-t pt-4\">\n <h5 class=\"text-muted-foreground mb-2 text-xs font-medium\">Pinned Columns</h5>\n <div class=\"space-y-1.5\">\n @for (columnId of pinnedColumnIds(); track columnId) {\n @let column = table.getColumn(columnId);\n @let isPinned = column?.getIsPinned();\n @let isVisible = columnVisibility()[columnId] !== false;\n <div\n class=\"border-border bg-muted/30 flex items-center gap-2 rounded border px-2 py-1.5 text-sm\"\n [class.opacity-60]=\"!isVisible\">\n <!-- Pin Icon -->\n <z-icon zType=\"lucidePin\" zSize=\"14\" class=\"text-primary shrink-0\" />\n\n <!-- Visibility Checkbox -->\n <!-- Visibility Checkbox -->\n <input\n type=\"checkbox\"\n [checked]=\"isVisible\"\n (change)=\"onToggleColumnVisibility(columnId)\"\n class=\"border-input h-4 w-4 shrink-0 cursor-pointer rounded\" />\n\n <!-- Column Name -->\n <span class=\"flex min-w-0 flex-1 flex-col\">\n <span class=\"truncate\" [class.text-muted-foreground]=\"!isVisible\">\n {{ columnId | zTableColumnHeader: zConfig().columns }}\n </span>\n @let pinnedParents = columnId | zTableColumnParents: zConfig().columns;\n @if (pinnedParents) {\n <span class=\"text-muted-foreground truncate text-[10px]\">({{ pinnedParents }})</span>\n }\n </span>\n\n <!-- Position Badge -->\n <span class=\"bg-primary/10 text-primary shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium\">\n {{ isPinned === 'left' ? 'LEFT' : 'RIGHT' }}\n </span>\n\n <!-- Unpin Button -->\n <button\n type=\"button\"\n (click)=\"onToggleColumnPin(columnId, isPinned === 'left' ? 'left' : 'right')\"\n class=\"text-muted-foreground hover:bg-muted hover:text-foreground cursor-pointer rounded p-1 text-xs transition-colors\"\n title=\"Unpin\">\n <z-icon zType=\"lucideX\" zSize=\"12\" />\n </button>\n </div>\n }\n </div>\n </div>\n }\n }\n </div>\n }\n </div>\n</z-drawer>\n", styles: [":host{display:flex;flex-direction:column;height:100%;--scrollbar-track-thickness: 7px;--scrollbar-track-color: transparent;--scrollbar-thumb-shape: 3px}.z-table-container{display:flex;flex-direction:column;position:relative;width:100%;height:100%;overflow:hidden;border-radius:8px;border:thin solid var(--border);background-color:var(--card)}.z-table-container.z-hide-horizontal-border th,.z-table-container.z-hide-horizontal-border td{border-bottom:none!important;border-top:none!important}.z-table-container.z-hide-vertical-border th,.z-table-container.z-hide-vertical-border td{border-left:none!important}table{width:fit-content;min-width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed;font-size:14px}.z-table-toolbar .z-settings-btn{transition:all .15s ease}.z-table-toolbar .z-settings-btn:hover{border-color:var(--muted-foreground)}.z-table-cell-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%;-webkit-user-select:text;user-select:text}.z-thead-wrapper{flex-shrink:0;background:var(--muted);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.z-thead-wrapper::-webkit-scrollbar{display:none}.z-thead-wrapper th{height:auto;padding:8px 12px;text-align:left;vertical-align:middle;font-weight:500;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--muted);border-left:thin solid var(--border);border-bottom:thin solid var(--border);-webkit-user-select:none;user-select:none}.z-thead-wrapper th.z-at-left-edge{border-left:none}.z-thead-wrapper th[colspan]{text-align:center;background:var(--muted);font-weight:500;color:var(--foreground)}.z-thead-wrapper.z-shadow-header{box-shadow:0 1px 3px #00000014;position:relative;z-index:15}.z-thead-wrapper.z-shadow-header:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d}.z-tbody-wrapper{flex:1;min-height:100px;display:flex;flex-direction:column}.z-tbody-scrollbar{flex:1;height:100%}.z-empty-state,.z-loading-state{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;min-height:100px;height:100%;color:var(--muted-foreground);font-size:.875rem;animation:z-fade-in .2s ease-out}.z-tbody-scrollbar,.z-tbody-scrollbar table{animation:z-fade-in .2s ease-out}@keyframes z-fade-in{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}.z-tbody-wrapper tr{transition:background-color .15s ease}.z-tbody-wrapper tr:hover,.z-tbody-wrapper tr:hover td[style*=sticky]{background-color:var(--muted)}.z-tbody-wrapper tr.z-pinned-top td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-top td.z-sticky-right,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-left,.z-tbody-wrapper tr.z-pinned-bottom td.z-sticky-right{z-index:3}.z-tbody-wrapper tr.z-shadow-bottom{box-shadow:0 1px 3px #00000014!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-bottom:where(.dark,.dark *){box-shadow:0 1px 3px #0000004d!important}.z-tbody-wrapper tr.z-shadow-top{box-shadow:0 -2px 4px #0000000d!important;position:relative;z-index:15}.z-tbody-wrapper tr.z-shadow-top:where(.dark,.dark *){box-shadow:0 -2px 4px #0003!important}.z-tbody-wrapper td{padding:8px 12px;height:40px;vertical-align:middle;color:var(--foreground);white-space:nowrap;overflow:hidden;background:var(--card);border-left:thin solid var(--border);border-bottom:thin solid var(--border);box-sizing:border-box}.z-tbody-wrapper td.z-at-left-edge{border-left:none}.z-tbody-wrapper td i{color:var(--muted-foreground);font-style:italic}.z-tbody-wrapper td[rowspan]{vertical-align:top;padding-top:12px}.z-tbody-wrapper td.z-row-hover{background-color:var(--muted)!important}.z-tbody-wrapper td.z-col-select,.z-tbody-wrapper td.z-col-expand,.z-tbody-wrapper td.z-col-actions{padding:8px 4px!important;text-align:center}.z-tbody-wrapper tr.z-child-row td.z-col-select:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-expand:first-child,.z-tbody-wrapper tr.z-child-row td.z-col-actions:first-child{padding-left:0!important}.z-virtual-scroll-inner{position:relative;width:100%}.z-virtual-row{position:absolute;top:0;left:0;width:100%}tr.z-child-row td:first-child{padding-left:12px!important}tbody tr.z-selected,tbody tr.z-selected td{background-color:color-mix(in srgb,var(--primary) 15%,transparent)!important}tbody tr.z-selected:hover,tbody tr.z-selected:hover td{background-color:color-mix(in srgb,var(--primary) 20%,transparent)!important}tbody tr.z-indeterminate-selected,tbody tr.z-indeterminate-selected td{background-color:color-mix(in srgb,var(--primary) 10%,transparent)!important}tbody tr.z-indeterminate-selected:hover,tbody tr.z-indeterminate-selected:hover td{background-color:color-mix(in srgb,var(--primary) 15%,transparent)!important}tbody tr.z-pinned-top td{background-color:var(--card)!important}tbody tr.z-pinned-top:hover{background-color:var(--muted)}tbody tr.z-pinned-top:hover td{background-color:var(--muted)!important}tbody tr.z-pinned-bottom td{background-color:var(--card)!important}tbody tr.z-pinned-bottom:hover{background-color:var(--muted)}tbody tr.z-pinned-bottom:hover td,tr.z-expanded-row td{background-color:var(--muted)!important}thead th{position:relative}thead th .z-resizer{position:absolute;right:0;top:0;height:100%;width:5px;background:#0000001a;cursor:col-resize;-webkit-user-select:none;user-select:none;touch-action:none;opacity:0;transition:opacity .2s ease}thead th .z-resizer:where(.dark,.dark *){background:#ffffff1a}thead th .z-resizer:hover,thead th .z-resizer.z-is-resizing{opacity:1;background:var(--primary);opacity:.5}thead th .z-resizer.z-is-resizing{background:var(--primary);opacity:1}thead th .z-resizer.z-resizer-left{right:auto;left:0}thead th:hover .z-resizer{opacity:1}.z-thead-wrapper th.z-sticky-left,.z-thead-wrapper th.z-sticky-right,.z-tbody-wrapper th.z-sticky-left,.z-tbody-wrapper th.z-sticky-right,.z-tfoot-wrapper th.z-sticky-left,.z-tfoot-wrapper th.z-sticky-right{background-color:var(--muted);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper td.z-sticky-left,.z-thead-wrapper td.z-sticky-right,.z-tbody-wrapper td.z-sticky-left,.z-tbody-wrapper td.z-sticky-right,.z-tfoot-wrapper td.z-sticky-left,.z-tfoot-wrapper td.z-sticky-right{background-color:var(--card);z-index:1;transform:translateZ(0);backface-visibility:hidden}.z-thead-wrapper th.z-sticky-left-last,.z-thead-wrapper td.z-sticky-left-last,.z-tbody-wrapper th.z-sticky-left-last,.z-tbody-wrapper td.z-sticky-left-last,.z-tfoot-wrapper th.z-sticky-left-last,.z-tfoot-wrapper td.z-sticky-left-last{position:relative;overflow:visible}.z-thead-wrapper th.z-sticky-left-last:after,.z-thead-wrapper td.z-sticky-left-last:after,.z-tbody-wrapper th.z-sticky-left-last:after,.z-tbody-wrapper td.z-sticky-left-last:after,.z-tfoot-wrapper th.z-sticky-left-last:after,.z-tfoot-wrapper td.z-sticky-left-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-30px;width:30px;pointer-events:none;box-shadow:inset 10px 0 8px -8px #0000001a;z-index:10}.z-thead-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-thead-wrapper td.z-sticky-left-last:after:where(.dark,.dark *),.z-tbody-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-tbody-wrapper td.z-sticky-left-last:after:where(.dark,.dark *),.z-tfoot-wrapper th.z-sticky-left-last:after:where(.dark,.dark *),.z-tfoot-wrapper td.z-sticky-left-last:after:where(.dark,.dark *){box-shadow:inset 10px 0 8px -8px #0000004d}.z-thead-wrapper th.z-sticky-left-last:after,.z-thead-wrapper td.z-sticky-left-last:after,.z-tbody-wrapper th.z-sticky-left-last:after,.z-tbody-wrapper td.z-sticky-left-last:after,.z-tfoot-wrapper th.z-sticky-left-last:after,.z-tfoot-wrapper td.z-sticky-left-last:after{opacity:0;transition:opacity .2s ease}.z-thead-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-thead-wrapper.z-scroll-left td.z-sticky-left-last:after,.z-tbody-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-tbody-wrapper.z-scroll-left td.z-sticky-left-last:after,.z-tfoot-wrapper.z-scroll-left th.z-sticky-left-last:after,.z-tfoot-wrapper.z-scroll-left td.z-sticky-left-last:after{opacity:1}.z-thead-wrapper th.z-sticky-right-first,.z-thead-wrapper td.z-sticky-right-first,.z-tbody-wrapper th.z-sticky-right-first,.z-tbody-wrapper td.z-sticky-right-first,.z-tfoot-wrapper th.z-sticky-right-first,.z-tfoot-wrapper td.z-sticky-right-first{position:relative;overflow:visible}.z-thead-wrapper th.z-sticky-right-first:before,.z-thead-wrapper td.z-sticky-right-first:before,.z-tbody-wrapper th.z-sticky-right-first:before,.z-tbody-wrapper td.z-sticky-right-first:before,.z-tfoot-wrapper th.z-sticky-right-first:before,.z-tfoot-wrapper td.z-sticky-right-first:before{content:\"\";position:absolute;top:0;bottom:0;left:-30px;width:30px;pointer-events:none;box-shadow:inset -10px 0 8px -8px #0000001a;z-index:10;opacity:0;transition:opacity .2s ease}.z-thead-wrapper th.z-sticky-right-first:before:where(.dark,.dark *),.z-thead-wrapper td.z-sticky-right-first:before:where(.dark,.dark *),.z-tbody-wrapper th.z-sticky-right-first:before:where(.dark,.dark *),.z-tbody-wrapper td.z-sticky-right-first:before:where(.dark,.dark *),.z-tfoot-wrapper th.z-sticky-right-first:before:where(.dark,.dark *),.z-tfoot-wrapper td.z-sticky-right-first:before:where(.dark,.dark *){box-shadow:inset -10px 0 8px -8px #0000004d}.z-thead-wrapper.z-scroll-right th.z-sticky-right-first,.z-thead-wrapper.z-scroll-right td.z-sticky-right-first,.z-tbody-wrapper.z-scroll-right th.z-sticky-right-first,.z-tbody-wrapper.z-scroll-right td.z-sticky-right-first,.z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first,.z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first{border-left:none}.z-thead-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-thead-wrapper.z-scroll-right td.z-sticky-right-first:before,.z-tbody-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-tbody-wrapper.z-scroll-right td.z-sticky-right-first:before,.z-tfoot-wrapper.z-scroll-right th.z-sticky-right-first:before,.z-tfoot-wrapper.z-scroll-right td.z-sticky-right-first:before{opacity:1}.z-thead-wrapper th.z-sticky-right-last,.z-tfoot-wrapper th.z-sticky-right-last{position:relative}.z-thead-wrapper th.z-sticky-right-last:after,.z-tfoot-wrapper th.z-sticky-right-last:after{content:\"\";position:absolute;top:0;bottom:0;right:-30px;width:30px;background:var(--muted);pointer-events:none}.z-tfoot-wrapper{flex-shrink:0;background:var(--muted);overflow-x:auto;overflow-y:hidden;scrollbar-width:none}.z-tfoot-wrapper::-webkit-scrollbar{display:none}.z-tfoot-wrapper th{height:auto;padding:8px 12px;text-align:left;vertical-align:middle;font-weight:500;font-size:12px;color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.5px;background:var(--muted);border-left:thin solid var(--border);border-top:thin solid var(--border)}.z-tfoot-wrapper th.z-at-left-edge{border-left:none}.z-tfoot-wrapper.z-shadow-footer{box-shadow:0 -2px 4px #0000000d;position:relative;z-index:15}.z-tfoot-wrapper.z-shadow-footer:where(.dark,.dark *){box-shadow:0 -2px 4px #0003}.z-pin-btn{padding:2px 4px;border-radius:4px;color:var(--muted-foreground);transition:all .15s ease}.z-pin-btn:hover{background-color:var(--muted);color:var(--foreground)}.z-pin-btn.z-pin-btn-active{color:var(--primary);background-color:var(--primary)}.z-pin-btn.z-pin-btn-active:hover{background-color:var(--primary);opacity:.8}.z-row-pin-trigger{opacity:1}.z-row-pin-trigger.text-primary{color:var(--primary)}.z-header-pin-trigger{opacity:1}.z-header-pin-trigger.text-primary{color:var(--primary)}th{overflow:hidden}th .z-header-content{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cdk-drag-preview{box-shadow:0 4px 12px #00000026;opacity:.9;border-radius:4px;background-color:var(--card);z-index:9999}.cdk-drag-preview:where(.dark,.dark *){box-shadow:0 4px 12px #0006}.cdk-drag-placeholder{opacity:.3;background-color:var(--muted);border:2px dashed var(--primary)}.cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}.cdk-drop-list-dragging .cdk-drag:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}.z-table-settings-drawer input[type=checkbox]{appearance:none;-webkit-appearance:none;-moz-appearance:none;width:1rem;height:1rem;border:thin solid var(--input);border-radius:.25rem;background-color:var(--background);cursor:pointer;position:relative;transition:all .2s ease}.z-table-settings-drawer input[type=checkbox]:hover{border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked{background-color:var(--primary);border-color:var(--primary)}.z-table-settings-drawer input[type=checkbox]:checked:after{content:\"\";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:.375rem;height:.625rem;border:solid var(--primary-foreground);border-width:0 2px 2px 0;transform:translate(-50%,-60%) rotate(45deg)}.z-table-settings-drawer input[type=checkbox]:disabled{opacity:.5;cursor:not-allowed}\n"] }]
|
|
2621
2621
|
}], ctorParameters: () => [], propDecorators: { zConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "zConfig", required: false }] }], zLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "zLoading", required: false }] }], zKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "zKey", required: false }] }], zChange: [{ type: i0.Output, args: ["zChange"] }], zControl: [{ type: i0.Output, args: ["zControl"] }], theadWrapper: [{ type: i0.ViewChild, args: ['theadWrapper', { isSignal: true }] }], tbodyContainer: [{ type: i0.ViewChild, args: ['tbodyContainer', { isSignal: true }] }], tbodyWrapper: [{ type: i0.ViewChild, args: ['tbodyWrapper', { isSignal: true }] }], tbodyScrollbar: [{ type: i0.ViewChild, args: ['tbodyWrapper', { isSignal: true }] }], tfootWrapper: [{ type: i0.ViewChild, args: ['tfootWrapper', { isSignal: true }] }], expandedRowTemplate: [{ type: i0.ViewChild, args: ['expandedRowTemplate', { isSignal: true }] }] } });
|
|
2622
2622
|
|
|
2623
2623
|
/**
|