@aquera/ngx-smart-table 0.0.7-alpha → 0.0.9-alpha

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.
@@ -7044,10 +7044,10 @@ class StRowActionsDropdownComponent {
7044
7044
  }
7045
7045
  }
7046
7046
  StRowActionsDropdownComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StRowActionsDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7047
- StRowActionsDropdownComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StRowActionsDropdownComponent, selector: "st-row-actions-dropdown", inputs: { isOpen: "isOpen", position: "position", context: "context" }, outputs: { actionClicked: "actionClicked", closed: "closed" }, host: { listeners: { "document:keydown.escape": "onEscapeKey($event)" } }, usesOnChanges: true, ngImport: i0, template: "<div class=\"dropdown-container\" *ngIf=\"isOpen && context\">\n <!-- Backdrop -->\n <div class=\"dropdown-overlay\" (click)=\"closed.emit()\"></div>\n \n <!-- Dropdown menu -->\n <div class=\"dropdown-menu\" [ngStyle]=\"dropdownStyle\">\n <nile-menu *ngIf=\"isOpen\" class=\"dropdown-menu\" [style.left.px]=\"dropdownStyle.left\" [style.top.px]=\"dropdownStyle.top\">\n <ng-container *ngFor=\"let action of visibleActions\">\n <nile-menu-item [class.disabled]=\"isActionDisabled(action)\" (click)=\"onActionClick(action)\" class=\"action-label\">\n <nile-icon *ngIf=\"action.icon\" size=\"14\" slot=\"prefix\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </ng-container>\n \n <nile-menu-item *ngIf=\"visibleActions.length === 0\">No actions available</nile-menu-item>\n </nile-menu>\n </div>\n\n </div>\n", styles: [".dropdown-container{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9998}.dropdown-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#0000004d;pointer-events:auto;z-index:9998}.dropdown-menu{position:fixed;background-color:#fff;box-shadow:0 5px 15px #0000004d,0 0 0 1px #0000001a;overflow:hidden;pointer-events:auto;z-index:9999}.action-icon{display:flex;align-items:center;justify-content:center;font-size:16px;width:20px;flex-shrink:0}.action-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dropdown-empty{padding:16px;text-align:center;color:#a0aec0;font-size:14px;font-style:italic}nile-menu{height:fit-content}\n"], directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
7047
+ StRowActionsDropdownComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StRowActionsDropdownComponent, selector: "st-row-actions-dropdown", inputs: { isOpen: "isOpen", position: "position", context: "context" }, outputs: { actionClicked: "actionClicked", closed: "closed" }, host: { listeners: { "document:keydown.escape": "onEscapeKey($event)" } }, usesOnChanges: true, ngImport: i0, template: "<div class=\"dropdown-container\" *ngIf=\"isOpen && context\">\n <!-- Backdrop -->\n <div class=\"dropdown-overlay\" (click)=\"closed.emit()\"></div>\n \n <!-- Dropdown menu -->\n <div class=\"dropdown-menu\" [ngStyle]=\"dropdownStyle\">\n <nile-menu *ngIf=\"isOpen\" class=\"dropdown-menu\" [style.left.px]=\"dropdownStyle.left\" [style.top.px]=\"dropdownStyle.top\">\n <ng-container *ngFor=\"let action of visibleActions\">\n <nile-menu-item [class.disabled]=\"isActionDisabled(action)\" (click)=\"onActionClick(action)\" class=\"action-label\">\n <nile-icon *ngIf=\"action.icon\" size=\"14\" slot=\"prefix\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </ng-container>\n \n <nile-menu-item *ngIf=\"visibleActions.length === 0\">No actions available</nile-menu-item>\n </nile-menu>\n </div>\n\n </div>\n", styles: [".dropdown-container{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9998}.dropdown-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background-color:transparent;pointer-events:auto;z-index:9998}.dropdown-menu{position:fixed;background-color:#fff;box-shadow:0 5px 15px #0000004d,0 0 0 1px #0000001a;overflow:hidden;pointer-events:auto;z-index:9999}.action-icon{display:flex;align-items:center;justify-content:center;font-size:16px;width:20px;flex-shrink:0}.action-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dropdown-empty{padding:16px;text-align:center;color:#a0aec0;font-size:14px;font-style:italic}nile-menu{height:fit-content}\n"], directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
7048
7048
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StRowActionsDropdownComponent, decorators: [{
7049
7049
  type: Component,
7050
- args: [{ selector: 'st-row-actions-dropdown', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"dropdown-container\" *ngIf=\"isOpen && context\">\n <!-- Backdrop -->\n <div class=\"dropdown-overlay\" (click)=\"closed.emit()\"></div>\n \n <!-- Dropdown menu -->\n <div class=\"dropdown-menu\" [ngStyle]=\"dropdownStyle\">\n <nile-menu *ngIf=\"isOpen\" class=\"dropdown-menu\" [style.left.px]=\"dropdownStyle.left\" [style.top.px]=\"dropdownStyle.top\">\n <ng-container *ngFor=\"let action of visibleActions\">\n <nile-menu-item [class.disabled]=\"isActionDisabled(action)\" (click)=\"onActionClick(action)\" class=\"action-label\">\n <nile-icon *ngIf=\"action.icon\" size=\"14\" slot=\"prefix\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </ng-container>\n \n <nile-menu-item *ngIf=\"visibleActions.length === 0\">No actions available</nile-menu-item>\n </nile-menu>\n </div>\n\n </div>\n", styles: [".dropdown-container{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9998}.dropdown-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#0000004d;pointer-events:auto;z-index:9998}.dropdown-menu{position:fixed;background-color:#fff;box-shadow:0 5px 15px #0000004d,0 0 0 1px #0000001a;overflow:hidden;pointer-events:auto;z-index:9999}.action-icon{display:flex;align-items:center;justify-content:center;font-size:16px;width:20px;flex-shrink:0}.action-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dropdown-empty{padding:16px;text-align:center;color:#a0aec0;font-size:14px;font-style:italic}nile-menu{height:fit-content}\n"] }]
7050
+ args: [{ selector: 'st-row-actions-dropdown', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"dropdown-container\" *ngIf=\"isOpen && context\">\n <!-- Backdrop -->\n <div class=\"dropdown-overlay\" (click)=\"closed.emit()\"></div>\n \n <!-- Dropdown menu -->\n <div class=\"dropdown-menu\" [ngStyle]=\"dropdownStyle\">\n <nile-menu *ngIf=\"isOpen\" class=\"dropdown-menu\" [style.left.px]=\"dropdownStyle.left\" [style.top.px]=\"dropdownStyle.top\">\n <ng-container *ngFor=\"let action of visibleActions\">\n <nile-menu-item [class.disabled]=\"isActionDisabled(action)\" (click)=\"onActionClick(action)\" class=\"action-label\">\n <nile-icon *ngIf=\"action.icon\" size=\"14\" slot=\"prefix\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </ng-container>\n \n <nile-menu-item *ngIf=\"visibleActions.length === 0\">No actions available</nile-menu-item>\n </nile-menu>\n </div>\n\n </div>\n", styles: [".dropdown-container{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9998}.dropdown-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background-color:transparent;pointer-events:auto;z-index:9998}.dropdown-menu{position:fixed;background-color:#fff;box-shadow:0 5px 15px #0000004d,0 0 0 1px #0000001a;overflow:hidden;pointer-events:auto;z-index:9999}.action-icon{display:flex;align-items:center;justify-content:center;font-size:16px;width:20px;flex-shrink:0}.action-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dropdown-empty{padding:16px;text-align:center;color:#a0aec0;font-size:14px;font-style:italic}nile-menu{height:fit-content}\n"] }]
7051
7051
  }], propDecorators: { isOpen: [{
7052
7052
  type: Input
7053
7053
  }], position: [{
@@ -7188,10 +7188,10 @@ class StHeaderComponent {
7188
7188
  }
7189
7189
  }
7190
7190
  StHeaderComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7191
- StHeaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StHeaderComponent, selector: "st-header", inputs: { column: "column", columnIndex: "columnIndex", isFirstColumn: "isFirstColumn", isLastColumn: "isLastColumn", tableState: "tableState", enableSorting: "enableSorting", enableFiltering: "enableFiltering" }, outputs: { sortToggle: "sortToggle", filterChange: "filterChange", columnMoved: "columnMoved", menuClick: "menuClick" }, ngImport: i0, template: "<div class=\"st-header\" [class.sortable]=\"isSortable()\" [class.sorted]=\"isSorted()\" [attr.aria-sort]=\"getAriaSort()\">\n <div class=\"header-content\">\n <span class=\"header-text\">{{ column.header || column.key }}</span>\n <button *ngIf=\"isMenuEnabled()\" \n class=\"column-menu-trigger\" \n (click)=\"onMenuClick($event)\"\n type=\"button\" \n aria-label=\"Column actions\">\n <nile-icon name=\"options\"></nile-icon>\n </button>\n </div>\n</div>", styles: [".st-header{position:relative;text-align:left;background-color:#fff;padding-left:8px}.st-header.sortable{cursor:pointer;-webkit-user-select:none;user-select:none;transition:background-color .2s}.st-header.resizing{-webkit-user-select:none;user-select:none;cursor:col-resize}.st-header.align-center{text-align:center}.st-header.align-center .header-content{justify-content:center}.st-header.align-right{text-align:right}.st-header.align-right .header-content{justify-content:flex-end}.st-header .header-content{display:flex;align-items:center;gap:8px}.st-header .header-text{flex:1;font-size:12px;font-weight:500;letter-spacing:.2px;line-height:14px}.st-header .column-menu-trigger{background:none;border:none;padding:4px;cursor:pointer;border-radius:4px;display:flex;align-items:center;justify-content:center}.st-header .column-menu-trigger nile-icon{font-size:16px;pointer-events:none}.st-header .resize-handle{position:absolute;top:0;right:0;bottom:0;width:8px;cursor:col-resize;z-index:10}.st-header .resize-handle:after{content:\"\";position:absolute;top:50%;right:3px;transform:translateY(-50%);width:2px;height:20px;background-color:#cbd5e0;opacity:0;transition:opacity .2s}.st-header .resize-handle:hover:after,.st-header .resize-handle:active:after{opacity:1}\n"], directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
7191
+ StHeaderComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StHeaderComponent, selector: "st-header", inputs: { column: "column", columnIndex: "columnIndex", isFirstColumn: "isFirstColumn", isLastColumn: "isLastColumn", tableState: "tableState", enableSorting: "enableSorting", enableFiltering: "enableFiltering" }, outputs: { sortToggle: "sortToggle", filterChange: "filterChange", columnMoved: "columnMoved", menuClick: "menuClick" }, ngImport: i0, template: "<div class=\"st-header\" [class.sortable]=\"isSortable()\" [class.sorted]=\"isSorted()\" [attr.aria-sort]=\"getAriaSort()\">\n <div class=\"header-content\">\n <span class=\"header-text\">{{ column.header || column.key }}</span>\n <button *ngIf=\"isMenuEnabled()\" \n class=\"column-menu-trigger\" \n (click)=\"onMenuClick($event)\"\n type=\"button\" \n aria-label=\"Column actions\">\n <nile-icon name=\"options\"></nile-icon>\n </button>\n </div>\n</div>", styles: [".st-header{position:relative;text-align:left;padding-left:8px}.st-header.sortable{cursor:pointer;-webkit-user-select:none;user-select:none;transition:background-color .2s}.st-header.resizing{-webkit-user-select:none;user-select:none;cursor:col-resize}.st-header.align-center{text-align:center}.st-header.align-center .header-content{justify-content:center}.st-header.align-right{text-align:right}.st-header.align-right .header-content{justify-content:flex-end}.st-header .header-content{display:flex;align-items:center;gap:8px}.st-header .header-text{flex:1;font-size:12px;font-weight:500;letter-spacing:.2px;line-height:14px}.st-header .column-menu-trigger{background:none;border:none;padding:4px;cursor:pointer;border-radius:4px;display:flex;align-items:center;justify-content:center}.st-header .column-menu-trigger nile-icon{font-size:16px;pointer-events:none}.st-header .resize-handle{position:absolute;top:0;right:0;bottom:0;width:8px;cursor:col-resize;z-index:10}.st-header .resize-handle:after{content:\"\";position:absolute;top:50%;right:3px;transform:translateY(-50%);width:2px;height:20px;background-color:#cbd5e0;opacity:0;transition:opacity .2s}.st-header .resize-handle:hover:after,.st-header .resize-handle:active:after{opacity:1}\n"], directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
7192
7192
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StHeaderComponent, decorators: [{
7193
7193
  type: Component,
7194
- args: [{ selector: 'st-header', template: "<div class=\"st-header\" [class.sortable]=\"isSortable()\" [class.sorted]=\"isSorted()\" [attr.aria-sort]=\"getAriaSort()\">\n <div class=\"header-content\">\n <span class=\"header-text\">{{ column.header || column.key }}</span>\n <button *ngIf=\"isMenuEnabled()\" \n class=\"column-menu-trigger\" \n (click)=\"onMenuClick($event)\"\n type=\"button\" \n aria-label=\"Column actions\">\n <nile-icon name=\"options\"></nile-icon>\n </button>\n </div>\n</div>", styles: [".st-header{position:relative;text-align:left;background-color:#fff;padding-left:8px}.st-header.sortable{cursor:pointer;-webkit-user-select:none;user-select:none;transition:background-color .2s}.st-header.resizing{-webkit-user-select:none;user-select:none;cursor:col-resize}.st-header.align-center{text-align:center}.st-header.align-center .header-content{justify-content:center}.st-header.align-right{text-align:right}.st-header.align-right .header-content{justify-content:flex-end}.st-header .header-content{display:flex;align-items:center;gap:8px}.st-header .header-text{flex:1;font-size:12px;font-weight:500;letter-spacing:.2px;line-height:14px}.st-header .column-menu-trigger{background:none;border:none;padding:4px;cursor:pointer;border-radius:4px;display:flex;align-items:center;justify-content:center}.st-header .column-menu-trigger nile-icon{font-size:16px;pointer-events:none}.st-header .resize-handle{position:absolute;top:0;right:0;bottom:0;width:8px;cursor:col-resize;z-index:10}.st-header .resize-handle:after{content:\"\";position:absolute;top:50%;right:3px;transform:translateY(-50%);width:2px;height:20px;background-color:#cbd5e0;opacity:0;transition:opacity .2s}.st-header .resize-handle:hover:after,.st-header .resize-handle:active:after{opacity:1}\n"] }]
7194
+ args: [{ selector: 'st-header', template: "<div class=\"st-header\" [class.sortable]=\"isSortable()\" [class.sorted]=\"isSorted()\" [attr.aria-sort]=\"getAriaSort()\">\n <div class=\"header-content\">\n <span class=\"header-text\">{{ column.header || column.key }}</span>\n <button *ngIf=\"isMenuEnabled()\" \n class=\"column-menu-trigger\" \n (click)=\"onMenuClick($event)\"\n type=\"button\" \n aria-label=\"Column actions\">\n <nile-icon name=\"options\"></nile-icon>\n </button>\n </div>\n</div>", styles: [".st-header{position:relative;text-align:left;padding-left:8px}.st-header.sortable{cursor:pointer;-webkit-user-select:none;user-select:none;transition:background-color .2s}.st-header.resizing{-webkit-user-select:none;user-select:none;cursor:col-resize}.st-header.align-center{text-align:center}.st-header.align-center .header-content{justify-content:center}.st-header.align-right{text-align:right}.st-header.align-right .header-content{justify-content:flex-end}.st-header .header-content{display:flex;align-items:center;gap:8px}.st-header .header-text{flex:1;font-size:12px;font-weight:500;letter-spacing:.2px;line-height:14px}.st-header .column-menu-trigger{background:none;border:none;padding:4px;cursor:pointer;border-radius:4px;display:flex;align-items:center;justify-content:center}.st-header .column-menu-trigger nile-icon{font-size:16px;pointer-events:none}.st-header .resize-handle{position:absolute;top:0;right:0;bottom:0;width:8px;cursor:col-resize;z-index:10}.st-header .resize-handle:after{content:\"\";position:absolute;top:50%;right:3px;transform:translateY(-50%);width:2px;height:20px;background-color:#cbd5e0;opacity:0;transition:opacity .2s}.st-header .resize-handle:hover:after,.st-header .resize-handle:active:after{opacity:1}\n"] }]
7195
7195
  }], propDecorators: { column: [{
7196
7196
  type: Input
7197
7197
  }], columnIndex: [{
@@ -8623,22 +8623,115 @@ class StTableComponent {
8623
8623
  }
8624
8624
  }
8625
8625
  /**
8626
- * Focus the TD element in the DOM
8626
+ * Focus the TD element in the DOM and scroll it into view,
8627
+ * accounting for sticky columns on left and right
8627
8628
  */
8628
8629
  focusTdElement(rowIndex, colIndex) {
8629
8630
  // Use setTimeout to ensure DOM has updated
8630
8631
  setTimeout(() => {
8631
- // More robust: find the td by data attributes
8632
- const tr = document.querySelector(`tr[data-row-index="${rowIndex}"]`);
8632
+ // Use absolute row index for virtual scroll compatibility
8633
+ const absoluteRowIndex = this.getAbsoluteRowIndex(rowIndex);
8634
+ const tr = document.querySelector(`tr[data-row-index="${absoluteRowIndex}"]`);
8633
8635
  if (tr) {
8634
- const cells = tr.querySelectorAll('td:not(.settings-column)');
8636
+ const cells = tr.querySelectorAll('td:not(.settings-column):not(.row-number-cell)');
8635
8637
  const targetCell = cells[colIndex];
8636
- if (targetCell) {
8637
- targetCell.focus({ preventScroll: false });
8638
+ if (targetCell && typeof targetCell.focus === 'function') {
8639
+ targetCell.focus({ preventScroll: true });
8640
+ this.scrollCellIntoView(targetCell, colIndex);
8638
8641
  }
8639
8642
  }
8640
8643
  }, 0);
8641
8644
  }
8645
+ /**
8646
+ * Scroll a cell into view, accounting for sticky columns
8647
+ */
8648
+ scrollCellIntoView(cell, colIndex) {
8649
+ // Find the scroll container
8650
+ const scrollContainer = this.getScrollContainerElement();
8651
+ if (!scrollContainer)
8652
+ return;
8653
+ const cellRect = cell.getBoundingClientRect();
8654
+ const containerRect = scrollContainer.getBoundingClientRect();
8655
+ // Calculate sticky column widths
8656
+ const { leftStickyWidth, rightStickyWidth } = this.calculateStickyWidths();
8657
+ // Calculate visible area (excluding sticky columns)
8658
+ const visibleLeft = containerRect.left + leftStickyWidth;
8659
+ const visibleRight = containerRect.right - rightStickyWidth;
8660
+ const visibleTop = containerRect.top;
8661
+ const visibleBottom = containerRect.bottom;
8662
+ // Check if column is sticky (don't scroll for sticky columns)
8663
+ const column = this.visibleColumns[colIndex];
8664
+ if (column?.sticky) {
8665
+ // Just ensure vertical visibility for sticky columns
8666
+ if (cellRect.top < visibleTop) {
8667
+ scrollContainer.scrollTop -= (visibleTop - cellRect.top);
8668
+ }
8669
+ else if (cellRect.bottom > visibleBottom) {
8670
+ scrollContainer.scrollTop += (cellRect.bottom - visibleBottom);
8671
+ }
8672
+ return;
8673
+ }
8674
+ // Horizontal scrolling
8675
+ if (cellRect.left < visibleLeft) {
8676
+ // Cell is hidden behind left sticky columns
8677
+ scrollContainer.scrollLeft -= (visibleLeft - cellRect.left);
8678
+ }
8679
+ else if (cellRect.right > visibleRight) {
8680
+ // Cell is hidden behind right sticky columns
8681
+ scrollContainer.scrollLeft += (cellRect.right - visibleRight);
8682
+ }
8683
+ // Vertical scrolling
8684
+ if (cellRect.top < visibleTop) {
8685
+ scrollContainer.scrollTop -= (visibleTop - cellRect.top);
8686
+ }
8687
+ else if (cellRect.bottom > visibleBottom) {
8688
+ scrollContainer.scrollTop += (cellRect.bottom - visibleBottom);
8689
+ }
8690
+ }
8691
+ /**
8692
+ * Get the scroll container element
8693
+ */
8694
+ getScrollContainerElement() {
8695
+ // Prefer cached container if already resolved
8696
+ if (this.scrollContainer) {
8697
+ return this.scrollContainer;
8698
+ }
8699
+ // Virtual scroll uses scrollViewport
8700
+ if (this.isVirtualScrollEnabled() && this.scrollViewport?.nativeElement) {
8701
+ this.scrollContainer = this.scrollViewport.nativeElement;
8702
+ return this.scrollContainer;
8703
+ }
8704
+ // Standard mode uses .st-table container (cache it to avoid selecting wrong table repeatedly)
8705
+ const tableContainer = document.querySelector('.st-table');
8706
+ if (tableContainer) {
8707
+ this.scrollContainer = tableContainer;
8708
+ }
8709
+ return this.scrollContainer || null;
8710
+ }
8711
+ /**
8712
+ * Calculate total width of sticky columns on left and right
8713
+ */
8714
+ calculateStickyWidths() {
8715
+ let leftStickyWidth = 0;
8716
+ let rightStickyWidth = 0;
8717
+ // Account for row number column if enabled
8718
+ if (this.mergedConfig.showRowNumber) {
8719
+ leftStickyWidth += 30; // Row number column width
8720
+ }
8721
+ // Calculate from column configs
8722
+ for (const col of this.visibleColumns) {
8723
+ if (col.sticky === 'left') {
8724
+ leftStickyWidth += col.width || 100;
8725
+ }
8726
+ else if (col.sticky === 'right') {
8727
+ rightStickyWidth += col.width || 100;
8728
+ }
8729
+ }
8730
+ // Account for settings column only when row actions are enabled (use consistent width)
8731
+ const settingsColumnWidth = this.hasRowActions() ? 32 : 0;
8732
+ rightStickyWidth += settingsColumnWidth;
8733
+ return { leftStickyWidth, rightStickyWidth };
8734
+ }
8642
8735
  /**
8643
8736
  * Handle cell click
8644
8737
  */
@@ -8750,10 +8843,10 @@ class StTableComponent {
8750
8843
  }
8751
8844
  }
8752
8845
  StTableComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StTableComponent, deps: [{ token: JsonSchemaValidatorService }, { token: ValidationLoggerService }, { token: VirtualScrollService }], target: i0.ɵɵFactoryTarget.Component });
8753
- StTableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StTableComponent, selector: "st-table", inputs: { tableConfig: "tableConfig", data: "data", data$: "data$", tableState: "tableState", enableSorting: "enableSorting", enableFiltering: "enableFiltering", validateConfig: "validateConfig" }, outputs: { stateChange: "stateChange", dataChange: "dataChange", cellEdit: "cellEdit", cellSave: "cellSave", cellCancel: "cellCancel", cellChange: "cellChange", columnResized: "columnResized", columnMoved: "columnMoved", configValidationErrors: "configValidationErrors", columnAdded: "columnAdded", rowAction: "rowAction", validationStateChange: "validationStateChange" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "scrollViewport", first: true, predicate: ["scrollViewport"], descendants: true, read: ElementRef }], usesOnChanges: true, ngImport: i0, template: "<!-- Top pagination controls -->\n<st-pagination \n *ngIf=\"showTopPagination() && !(mergedConfig.tableSkeleton?.enabled | async)\" \n [tableState]=\"getActiveTableState()\"\n [tableConfig]=\"mergedConfig\"\n position=\"top\">\n</st-pagination>\n\n<div class=\"st-table\" *ngIf=\"!(mergedConfig.tableSkeleton?.enabled | async)\" \n [ngClass]=\"{\n 'virtual-scroll-enabled': isVirtualScrollEnabled(),\n 'keyboard-navigation-enabled': isKeyboardNavigationEnabled()\n }\"\n [ngStyle]=\"{\n 'max-height.px': !isVirtualScrollEnabled() ? mergedConfig.display?.maxHeight : null\n }\"\n stKeyboardNavigation\n [tableState]=\"getActiveTableState()\" \n [attr.tabindex]=\"isKeyboardNavigationEnabled() ? 0 : -1\"\n (focus)=\"onTableContainerFocus($event)\"\n [attr.title]=\"isKeyboardNavigationEnabled() ? 'Click a cell or press Tab to start keyboard navigation' : null\">\n <!-- Unified Table Actions Menu -->\n\n <!-- Virtual scroll viewport wrapper -->\n <div class=\"st-scroll-viewport\" #scrollViewport *ngIf=\"isVirtualScrollEnabled()\"\n [ngStyle]=\"{ 'height.px': getVirtualScrollViewportHeight() }\">\n\n <!-- Spacer to create scrollable area -->\n <div class=\"st-scroll-spacer\" [ngStyle]=\"{ 'height.px': virtualScrollTotalHeight$ | async }\">\n </div>\n\n <!-- Table positioner with transform (instead of tbody) -->\n <div class=\"st-table-positioner\" [ngStyle]=\"{ transform: 'translateY(' + (virtualScrollOffsetY$ | async) + 'px)' }\">\n <!-- Table with only visible rows -->\n <ng-container *ngTemplateOutlet=\"tableTemplate; context: { \n mode: 'virtual',\n theadStyle: { top: (virtualScrollOffsetYNeg$ | async) + 'px' }\n }\"></ng-container>\n </div>\n </div>\n \n <!-- Standard table (when virtual scroll disabled) -->\n <ng-container *ngIf=\"!isVirtualScrollEnabled()\">\n <ng-container *ngTemplateOutlet=\"tableTemplate; context: { \n mode: 'standard',\n theadStyle: null\n }\"></ng-container>\n </ng-container>\n\n <!-- Shared Column Menu Dropdown -->\n <st-column-menu-dropdown \n [isOpen]=\"columnMenuState.isOpen\"\n [position]=\"columnMenuState.position\"\n [context]=\"columnMenuState.context\"\n (actionClicked)=\"onColumnActionClicked($event)\"\n (closed)=\"closeColumnMenu()\">\n </st-column-menu-dropdown>\n</div>\n\n<ng-container *ngIf=\"(mergedConfig.tableSkeleton?.enabled | async)\">\n <ng-container *ngTemplateOutlet=\"skeletonLoader\"></ng-container>\n</ng-container>\n\n<!-- Shared Row Actions Dropdown -->\n<st-row-actions-dropdown [isOpen]=\"dropdownState.isOpen\" [position]=\"dropdownState.position\"\n [context]=\"dropdownState.context\" (actionClicked)=\"onRowActionClicked($event)\" (closed)=\"closeRowActionsDropdown()\">\n</st-row-actions-dropdown>\n\n\n<!-- Bottom pagination controls -->\n<st-pagination \n *ngIf=\"showBottomPagination() && !(mergedConfig.tableSkeleton?.enabled | async)\" \n [tableState]=\"getActiveTableState()\"\n [tableConfig]=\"mergedConfig\"\n position=\"bottom\">\n</st-pagination>\n\n\n <!-- ========================================== -->\n <!-- REUSABLE TABLE TEMPLATE -->\n <!-- ========================================== -->\n <ng-template #tableTemplate let-mode=\"mode\" let-theadStyle=\"theadStyle\">\n <table class=\"st-table-element\">\n <!-- TABLE HEADER -->\n <thead [ngClass]=\"{ 'sticky': mergedConfig.display?.stickyHeader }\" [ngStyle]=\"theadStyle\">\n <tr>\n <!-- Row Number Header -->\n <th *ngIf=\"mergedConfig.showRowNumber\" class=\"row-number-header header-cell sticky-left\"\n [ngStyle]=\"{\n position: 'sticky',\n 'left.px': 0,\n 'z-index': ZIndex.STICKY_HEADER_CELL,\n 'width.px': 30\n }\">\n #\n </th>\n <!-- Column Headers -->\n <th \n *ngFor=\"let column of visibleColumns; let colIndex = index; let isFirst = first; let isLast = last\"\n [ngClass]=\"{\n 'header-cell': mode === 'standard',\n 'sticky-left': column.sticky === 'left',\n 'sticky-right': column.sticky === 'right',\n 'sticky-right-first': column.sticky === 'right' && isFirstStickyRight(column.key),\n 'resizable': column.resizable !== false\n }\"\n [ngStyle]=\"{\n position: column.sticky ? 'sticky' : null,\n 'left.px': column.sticky === 'left' ? (column.stickyOffset || 0) : null,\n 'right.px': column.sticky === 'right' ? (column.stickyOffset || 0) : null,\n 'z-index': column.sticky ? ZIndex.STICKY_HEADER_CELL : null,\n 'width.px': column.width\n }\">\n \n <st-header \n [column]=\"column\"\n [columnIndex]=\"colIndex\"\n [isFirstColumn]=\"isFirst\"\n [isLastColumn]=\"isLast\"\n [tableState]=\"getActiveTableState()\"\n [enableSorting]=\"mergedConfig.sorting?.enabled ?? enableSorting\"\n [enableFiltering]=\"mergedConfig.filtering?.enabled ?? enableFiltering\"\n (columnMoved)=\"onColumnMoved($event)\"\n (menuClick)=\"openColumnMenu($event, column, colIndex, isFirst, isLast)\">\n </st-header>\n\n <div \n class=\"resize-handle\" \n stColumnResize\n [column]=\"column\"\n (columnResized)=\"onColumnResized($event)\"\n *ngIf=\"column.resizable !== false\">\n </div>\n </th>\n \n <!-- Settings Column Header -->\n <th \n class=\"settings-column sticky-right\"\n [ngClass]=\"{ 'header-cell': mode === 'standard' }\"\n [ngStyle]=\"{ 'z-index': ZIndex.STICKY_HEADER_CELL }\">\n <div [ngClass]=\"{ 'flex-center': mode === 'virtual', 'header-content': mode === 'standard' }\">\n <st-table-actions \n [tableState]=\"getActiveTableState()\"\n [allowAddColumn]=\"mergedConfig.features?.columnManagement?.allowAdd || false\"\n (addColumnClicked)=\"onAddColumnClick()\">\n </st-table-actions>\n </div>\n </th>\n </tr>\n </thead>\n\n <!-- TABLE BODY -->\n <tbody>\n <!-- Virtual Scroll Rows -->\n <ng-container *ngIf=\"mode === 'virtual'\">\n <tr *ngFor=\"let row of visibleRows$ | async; let relativeIndex = index; trackBy: trackByRowIndex\"\n [attr.data-row-index]=\"getAbsoluteRowIndex(relativeIndex)\">\n <!-- Row Number Cell -->\n <td *ngIf=\"mergedConfig.showRowNumber\" class=\"row-number-cell\" \n [ngClass]=\"{ 'sticky-left': 'left' }\"\n [ngStyle]=\"{\n 'width.px': 30,\n position: 'sticky',\n 'left.px': 0,\n 'z-index': ZIndex.STICKY_BODY_CELL,\n 'height.px': mode === 'virtual' ? getVirtualScrollItemSize() : null\n }\">\n {{getAbsoluteRowIndex(relativeIndex) + 1}}\n </td>\n <ng-container *ngTemplateOutlet=\"bodyCellTemplate; context: { \n row: row,\n rowIndex: getAbsoluteRowIndex(relativeIndex),\n mode: 'virtual',\n relativeIndex: relativeIndex\n }\"></ng-container>\n </tr>\n </ng-container>\n\n <!-- Standard Rows -->\n <ng-container *ngIf=\"mode === 'standard'\">\n <tr *ngFor=\"let row of visibleCellGrid; let rowIndex = index\" [attr.data-row-index]=\"rowIndex\">\n <!-- Row Number Cell -->\n <td *ngIf=\"mergedConfig.showRowNumber\" class=\"row-number-cell\" \n [ngClass]=\"{ 'sticky-left': 'left' }\"\n [ngStyle]=\"{\n 'width.px': 30,\n position: 'sticky',\n 'left.px': 0,\n 'z-index': ZIndex.STICKY_BODY_CELL,\n 'height.px': mode === 'virtual' ? getVirtualScrollItemSize() : null\n }\">\n {{rowIndex + 1}}\n </td>\n <ng-container *ngTemplateOutlet=\"bodyCellTemplate; context: { \n row: row,\n rowIndex: rowIndex,\n mode: 'standard'\n }\"></ng-container>\n </tr>\n </ng-container>\n </tbody>\n </table>\n </ng-template>\n\n <!-- ========================================== -->\n <!-- REUSABLE BODY CELL TEMPLATE -->\n <!-- ========================================== -->\n <ng-template #bodyCellTemplate let-row=\"row\" let-rowIndex=\"rowIndex\" let-mode=\"mode\" let-relativeIndex=\"relativeIndex\">\n <!-- Data Cells -->\n <td \n *ngFor=\"let cell of row; let colIndex = index\"\n [ngClass]=\"{\n 'sticky-left': visibleColumns[colIndex]?.sticky === 'left',\n 'sticky-right': visibleColumns[colIndex]?.sticky === 'right',\n 'sticky-right-first': visibleColumns[colIndex]?.sticky === 'right' && visibleColumns[colIndex]?.key && isFirstStickyRight(visibleColumns[colIndex].key),\n 'align-center': visibleColumns[colIndex]?.alignment === 'center',\n 'align-right': visibleColumns[colIndex]?.alignment === 'right',\n 'cell-focused': cell.isFocused()\n }\"\n [ngStyle]=\"{\n position: visibleColumns[colIndex]?.sticky ? 'sticky' : null,\n 'left.px': visibleColumns[colIndex]?.sticky === 'left' ? (visibleColumns[colIndex]?.stickyOffset || 0) : null,\n 'right.px': visibleColumns[colIndex]?.sticky === 'right' ? (visibleColumns[colIndex]?.stickyOffset || 0) : null,\n 'z-index': visibleColumns[colIndex]?.sticky ? ZIndex.STICKY_BODY_CELL : null,\n 'width.px': visibleColumns[colIndex]?.width,\n 'height.px': mode === 'virtual' ? getVirtualScrollItemSize() : null\n }\"\n (click)=\"isKeyboardNavigationEnabled() ? onCellClick(rowIndex, colIndex) : null\">\n \n <!-- Virtual Scroll Cell -->\n <st-cell \n *ngIf=\"mode === 'virtual'\"\n [cell]=\"cell\" \n [attr.tabindex]=\"cell.isFocused() ? 0 : -1\" \n [editMode]=\"getEditModeForCells()\"\n [tableState]=\"getActiveTableState()\"\n [tableConfig]=\"mergedConfig\"\n [columnIndex]=\"colIndex\"\n (cellEdit)=\"onCellEdit($event)\" \n (cellSave)=\"onCellSave($event)\"\n (cellSaveAndNavigate)=\"onCellSaveAndNavigate($event)\" \n (cellCancel)=\"onCellCancel($event)\"\n (cellChange)=\"cellChange.emit($event)\">\n </st-cell>\n\n <!-- Standard Cell -->\n <st-cell \n *ngIf=\"mode === 'standard'\"\n [cell]=\"cell\" \n [attr.tabindex]=\"cell.isFocused() ? 0 : -1\"\n [tableState]=\"getActiveTableState()\"\n [tableConfig]=\"mergedConfig\"\n [columnIndex]=\"colIndex\"\n (cellSave)=\"onCellSave($event)\"\n (cellChange)=\"cellChange.emit($event)\">\n </st-cell>\n </td>\n \n <!-- Row Actions Cell -->\n <td class=\"settings-column\"\n [ngClass]=\"{\n 'has-actions': hasRowActions()\n }\"\n [ngStyle]=\"{\n position: hasRowActions() ? 'sticky' : null,\n 'right.px': hasRowActions() ? 0 : null,\n 'z-index': hasRowActions() ? ZIndex.STICKY_BODY_CELL : null\n }\">\n <button \n *ngIf=\"hasRowActions()\"\n class=\"settings-trigger\"\n (click)=\"openRowActionsDropdown($event, getRowData(rowIndex), rowIndex)\"\n type=\"button\" \n aria-label=\"Row actions\">\n \u22EF\n </button>\n </td>\n </ng-template>\n\n<ng-template #skeletonLoader>\n <div class=\"list-row\" *ngIf=\"(mergedConfig.tableSkeleton?.enabled | async)\">\n <div *ngFor=\"let i of skeletonColumns\" class=\"list-content\">\n <nile-skeleton-loader variant=\"text\" width=\"90%\" height=\"18\" *ngFor=\"let j of skeletonRows\"></nile-skeleton-loader>\n </div>\n </div>\n</ng-template>", styles: [".st-table{width:100%;overflow:auto;position:relative;height:100%;max-height:30rem;border-radius:4px;border:1px solid #E6E9EB}.st-table st-table-actions{position:sticky;right:0}.st-table.keyboard-navigation-enabled{cursor:pointer}.st-table.keyboard-navigation-enabled:focus{outline:none;box-shadow:0 0 0 2px #3b82f64d}.st-table.keyboard-navigation-enabled td.cell-focused{outline:2px solid #4299e1;outline-offset:-1px;position:relative;box-shadow:0 0 0 3px #3182ce1a}.st-table.keyboard-navigation-enabled td.cell-focused:focus{outline:2px solid #4299e1;outline-offset:-1px;box-shadow:0 0 0 3px #3182ce1a}.st-table.keyboard-navigation-enabled td.cell-focused:has(.st-cell.editing){box-shadow:0 0 0 4px #2563eb26}.st-table.virtual-scroll-enabled{overflow-x:visible;margin:0;border:1px solid #E6E9EB;border-collapse:collapse}.st-table.virtual-scroll-enabled .st-scroll-viewport{position:relative;overflow-y:auto;overflow-x:auto;scroll-behavior:smooth}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-scroll-spacer{position:absolute;top:0;left:0;width:1px;pointer-events:none;z-index:-1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-positioner{position:absolute;top:0;left:0;right:0;will-change:transform}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element{position:relative;width:100%;border-collapse:collapse;border-spacing:0;table-layout:fixed}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead{background-color:#fff;border-bottom:1px solid #E6E9EB}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead.sticky{position:sticky;top:0;z-index:3;background-color:#fff;will-change:top;backface-visibility:hidden}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead.sticky:after{content:\"\";position:absolute;bottom:-2px;left:0;right:0;height:2px;background:linear-gradient(to bottom,rgba(0,0,0,.1),transparent)}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th{padding:0;vertical-align:middle;position:relative;border:none;background-color:#fff;will-change:top;border-right:1px solid #E6E9EB}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-left{position:sticky;left:0;background-color:#fff;border-right:1px solid #E6E9EB!important}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-left:not(:has(~ th.sticky-left)){box-shadow:2px 0 5px -2px #0003}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-right{position:sticky;right:0;background-color:#fff}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.settings-column{width:2rem;text-align:center;font-weight:600;font-size:12px}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.resizable .resize-handle{position:absolute;top:0;right:0;bottom:0;width:8px;cursor:col-resize;z-index:4;pointer-events:auto}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.resizable .resize-handle:after{content:\"\";position:absolute;top:50%;right:3px;transform:translateY(-50%);width:2px;height:20px;background-color:#cbd5e0;opacity:0;transition:opacity .2s}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.resizable .resize-handle:hover:after,.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.resizable .resize-handle:active:after{opacity:1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.row-number-header{text-align:center;font-weight:300;font-size:12px;background-color:#f8f8f8}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-left:not(:has(~ th.sticky-left)):after{content:\"\";position:absolute;right:-6px;top:0;bottom:-1px;width:5px;border-left:1px solid var(--borderColor);background:linear-gradient(90deg,rgba(0,0,0,.08) 0%,rgba(0,0,0,0) 100%)}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-right-first:not(.settings-column):before{content:\"\";position:absolute;left:-6px;top:0;bottom:-1px;width:5px;border-right:1px solid var(--borderColor);background:linear-gradient(90deg,rgba(0,0,0,0) 0%,rgba(0,0,0,.08) 100%)}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody{will-change:transform;position:relative;z-index:1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr{border-bottom:1px solid #E6E9EB;transition:background-color .15s;height:2rem;box-sizing:border-box}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr:last-child{border-bottom:none}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td{padding:0;vertical-align:middle;box-sizing:border-box;height:2rem;border:1px solid #E6E9EB;border-left:none;background-color:transparent}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.align-center{text-align:center}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.align-right{text-align:right}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-left{position:sticky;z-index:2;border-right:1px solid #E6E9EB!important}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-left:not(:has(~ td.sticky-left)){box-shadow:2px 0 5px -2px #0003}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-right{position:sticky;z-index:2}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.settings-column{width:2rem;text-align:center}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.settings-column .settings-trigger{background:none;border:none;font-size:1rem;line-height:1;cursor:pointer;border-radius:4px;transition:all .15s ease}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.settings-column .settings-trigger:focus{outline:2px solid #4299e1;outline-offset:2px}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.row-number-cell{text-align:center;font-weight:300;font-size:12px;background-color:#f8f8f8}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-left:not(:has(~ td.sticky-left)):after{content:\"\";position:absolute;right:-6px;top:0;bottom:-1px;width:5px;border-left:1px solid var(--borderColor);background:linear-gradient(90deg,rgba(0,0,0,.08) 0%,rgba(0,0,0,0) 100%)}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-right-first:before{content:\"\";position:absolute;left:-6px;top:0;bottom:-1px;width:5px;border-right:1px solid var(--borderColor);background:linear-gradient(90deg,rgba(0,0,0,0) 0%,rgba(0,0,0,.08) 100%)}.st-table:not(.virtual-scroll-enabled) .st-table-element{width:100%;height:100%;overflow:auto;border-collapse:collapse;table-layout:fixed}.st-table:not(.virtual-scroll-enabled) .st-table-element thead{background-color:#fff;border-bottom:1px solid #E6E9EB}.st-table:not(.virtual-scroll-enabled) .st-table-element thead.sticky{position:sticky;top:0;z-index:3;background-color:#fff;will-change:top;backface-visibility:hidden}.st-table:not(.virtual-scroll-enabled) .st-table-element thead.sticky:after{content:\"\";position:absolute;bottom:-2px;left:0;right:0;height:2px;background:linear-gradient(to bottom,rgba(0,0,0,.1),transparent)}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th{padding:0;vertical-align:middle;position:relative;background-color:#fff;border-right:1px solid #E6E9EB}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-left{position:sticky;left:0;border-right:1px solid #E6E9EB!important}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-left:not(:has(~ th.sticky-left)){box-shadow:2px 0 5px -2px #0003}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-right{position:sticky;right:0;border-left:1px solid #E6E9EB!important}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-right-first:not(.settings-column){box-shadow:-2px 0 5px -2px #0003}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.settings-column{width:2rem;text-align:center;font-weight:600;font-size:12px;border-right:none;position:sticky;right:0}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.resizable .resize-handle{position:absolute;top:0;right:0;bottom:0;width:8px;cursor:col-resize;z-index:4;pointer-events:auto}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.resizable .resize-handle:after{content:\"\";position:absolute;top:50%;right:3px;transform:translateY(-50%);width:2px;height:20px;background-color:#cbd5e0;opacity:0;transition:opacity .2s}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.resizable .resize-handle:hover:after,.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.resizable .resize-handle:active:after{opacity:1}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.row-number-header{text-align:center;font-weight:300;font-size:12px;background-color:#f8f8f8}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody{will-change:transform;position:relative;z-index:1}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr{height:2rem;box-sizing:border-box}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr:last-child{border-bottom:none}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td{padding:0;vertical-align:middle;height:2rem;border:1px solid #E6E9EB;border-left:none}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.row-number-cell{text-align:center;font-weight:300;font-size:12px;background-color:#f8f8f8}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.align-center{text-align:center}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.align-right{text-align:right}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-left{position:sticky;z-index:2;border-right:1px solid #E6E9EB!important}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-left:not(:has(~ td.sticky-left)){box-shadow:2px 0 5px -2px #0003}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-right{position:sticky;z-index:2;border-left:1px solid #E6E9EB!important}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-right-first:not(.settings-column){box-shadow:-2px 0 5px -2px #0003}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.settings-column{position:sticky;right:0;width:2rem;text-align:center;border-right:none}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.settings-column .settings-trigger{background:none;border:none;font-size:1rem;line-height:1;cursor:pointer;border-radius:4px;transition:all .15s ease}.st-table:not(.virtual-scroll-enabled) .st-table-element .header-content{display:flex;height:2rem;align-items:center}.st-table:not(.virtual-scroll-enabled) .st-table-element .header-content .table-header-text{flex-grow:1;padding-left:4px}.st-table:not(.virtual-scroll-enabled) .st-table-element .settings-column>.header-content{display:flex;align-items:center;justify-content:center}.flex-center{display:flex;align-items:center;justify-content:center}.list-row{padding:1rem}.list-row .list-content{display:flex;justify-content:space-evenly;gap:4px;margin-bottom:1rem}\n"], components: [{ type: StPaginationComponent, selector: "st-pagination", inputs: ["tableState", "tableConfig", "position"] }, { type: StColumnMenuDropdownComponent, selector: "st-column-menu-dropdown", inputs: ["isOpen", "position", "context"], outputs: ["actionClicked", "closed"] }, { type: StRowActionsDropdownComponent, selector: "st-row-actions-dropdown", inputs: ["isOpen", "position", "context"], outputs: ["actionClicked", "closed"] }, { type: StHeaderComponent, selector: "st-header", inputs: ["column", "columnIndex", "isFirstColumn", "isLastColumn", "tableState", "enableSorting", "enableFiltering"], outputs: ["sortToggle", "filterChange", "columnMoved", "menuClick"] }, { type: StTableActionsComponent, selector: "st-table-actions", inputs: ["tableState", "allowAddColumn"], outputs: ["addColumnClicked"] }, { type: StCellComponent, selector: "st-cell", inputs: ["cell", "editMode", "tableState", "tableConfig", "columnIndex"], outputs: ["cellChange", "cellEdit", "cellSave", "cellCancel", "cellSaveAndNavigate"] }], directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: StKeyboardNavigationDirective, selector: "[stKeyboardNavigation]", inputs: ["tableState"] }, { type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: StColumnResizeDirective, selector: "[stColumnResize]", inputs: ["column"], outputs: ["columnResized"] }], pipes: { "async": i1.AsyncPipe } });
8846
+ StTableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StTableComponent, selector: "st-table", inputs: { tableConfig: "tableConfig", data: "data", data$: "data$", tableState: "tableState", enableSorting: "enableSorting", enableFiltering: "enableFiltering", validateConfig: "validateConfig" }, outputs: { stateChange: "stateChange", dataChange: "dataChange", cellEdit: "cellEdit", cellSave: "cellSave", cellCancel: "cellCancel", cellChange: "cellChange", columnResized: "columnResized", columnMoved: "columnMoved", configValidationErrors: "configValidationErrors", columnAdded: "columnAdded", rowAction: "rowAction", validationStateChange: "validationStateChange" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "scrollViewport", first: true, predicate: ["scrollViewport"], descendants: true, read: ElementRef }], usesOnChanges: true, ngImport: i0, template: "<!-- Top pagination controls -->\n<st-pagination \n *ngIf=\"showTopPagination() && !(mergedConfig.tableSkeleton?.enabled | async)\" \n [tableState]=\"getActiveTableState()\"\n [tableConfig]=\"mergedConfig\"\n position=\"top\">\n</st-pagination>\n\n<div class=\"st-table\" *ngIf=\"!(mergedConfig.tableSkeleton?.enabled | async)\" \n [ngClass]=\"{\n 'virtual-scroll-enabled': isVirtualScrollEnabled(),\n 'keyboard-navigation-enabled': isKeyboardNavigationEnabled()\n }\"\n [ngStyle]=\"{\n 'max-height.px': !isVirtualScrollEnabled() ? mergedConfig.display?.maxHeight : null\n }\"\n stKeyboardNavigation\n [tableState]=\"getActiveTableState()\" \n [attr.tabindex]=\"isKeyboardNavigationEnabled() ? 0 : -1\"\n (focus)=\"onTableContainerFocus($event)\"\n [attr.title]=\"isKeyboardNavigationEnabled() ? 'Click a cell or press Tab to start keyboard navigation' : null\">\n <!-- Unified Table Actions Menu -->\n\n <!-- Virtual scroll viewport wrapper -->\n <div class=\"st-scroll-viewport\" #scrollViewport *ngIf=\"isVirtualScrollEnabled()\"\n [ngStyle]=\"{ 'height.px': getVirtualScrollViewportHeight() }\">\n\n <!-- Spacer to create scrollable area -->\n <div class=\"st-scroll-spacer\" [ngStyle]=\"{ 'height.px': virtualScrollTotalHeight$ | async }\">\n </div>\n\n <!-- Table positioner with transform (instead of tbody) -->\n <div class=\"st-table-positioner\" [ngStyle]=\"{ transform: 'translateY(' + (virtualScrollOffsetY$ | async) + 'px)' }\">\n <!-- Table with only visible rows -->\n <ng-container *ngTemplateOutlet=\"tableTemplate; context: { \n mode: 'virtual',\n theadStyle: { top: (virtualScrollOffsetYNeg$ | async) + 'px' }\n }\"></ng-container>\n </div>\n </div>\n \n <!-- Standard table (when virtual scroll disabled) -->\n <ng-container *ngIf=\"!isVirtualScrollEnabled()\">\n <ng-container *ngTemplateOutlet=\"tableTemplate; context: { \n mode: 'standard',\n theadStyle: null\n }\"></ng-container>\n </ng-container>\n\n <!-- Shared Column Menu Dropdown -->\n <st-column-menu-dropdown \n [isOpen]=\"columnMenuState.isOpen\"\n [position]=\"columnMenuState.position\"\n [context]=\"columnMenuState.context\"\n (actionClicked)=\"onColumnActionClicked($event)\"\n (closed)=\"closeColumnMenu()\">\n </st-column-menu-dropdown>\n</div>\n\n<ng-container *ngIf=\"(mergedConfig.tableSkeleton?.enabled | async)\">\n <ng-container *ngTemplateOutlet=\"skeletonLoader\"></ng-container>\n</ng-container>\n\n<!-- Shared Row Actions Dropdown -->\n<st-row-actions-dropdown [isOpen]=\"dropdownState.isOpen\" [position]=\"dropdownState.position\"\n [context]=\"dropdownState.context\" (actionClicked)=\"onRowActionClicked($event)\" (closed)=\"closeRowActionsDropdown()\">\n</st-row-actions-dropdown>\n\n\n<!-- Bottom pagination controls -->\n<st-pagination \n *ngIf=\"showBottomPagination() && !(mergedConfig.tableSkeleton?.enabled | async)\" \n [tableState]=\"getActiveTableState()\"\n [tableConfig]=\"mergedConfig\"\n position=\"bottom\">\n</st-pagination>\n\n\n <!-- ========================================== -->\n <!-- REUSABLE TABLE TEMPLATE -->\n <!-- ========================================== -->\n <ng-template #tableTemplate let-mode=\"mode\" let-theadStyle=\"theadStyle\">\n <table class=\"st-table-element\">\n <!-- TABLE HEADER -->\n <thead [ngClass]=\"{ 'sticky': mergedConfig.display?.stickyHeader }\" [ngStyle]=\"theadStyle\">\n <tr>\n <!-- Row Number Header -->\n <th *ngIf=\"mergedConfig.showRowNumber\" class=\"row-number-header header-cell sticky-left\"\n [ngStyle]=\"{\n position: 'sticky',\n 'left.px': 0,\n 'z-index': ZIndex.STICKY_HEADER_CELL,\n 'width.px': 30\n }\">\n #\n </th>\n <!-- Column Headers -->\n <th \n *ngFor=\"let column of visibleColumns; let colIndex = index; let isFirst = first; let isLast = last\"\n [ngClass]=\"{\n 'header-cell': mode === 'standard',\n 'sticky-left': column.sticky === 'left',\n 'sticky-right': column.sticky === 'right',\n 'sticky-right-first': column.sticky === 'right' && isFirstStickyRight(column.key),\n 'resizable': column.resizable !== false\n }\"\n [ngStyle]=\"{\n position: column.sticky ? 'sticky' : null,\n 'left.px': column.sticky === 'left' ? (column.stickyOffset || 0) : null,\n 'right.px': column.sticky === 'right' ? (column.stickyOffset || 0) : null,\n 'z-index': column.sticky ? ZIndex.STICKY_HEADER_CELL : null,\n 'width.px': column.width\n }\">\n \n <st-header \n [column]=\"column\"\n [columnIndex]=\"colIndex\"\n [isFirstColumn]=\"isFirst\"\n [isLastColumn]=\"isLast\"\n [tableState]=\"getActiveTableState()\"\n [enableSorting]=\"mergedConfig.sorting?.enabled ?? enableSorting\"\n [enableFiltering]=\"mergedConfig.filtering?.enabled ?? enableFiltering\"\n (columnMoved)=\"onColumnMoved($event)\"\n (menuClick)=\"openColumnMenu($event, column, colIndex, isFirst, isLast)\">\n </st-header>\n\n <div \n class=\"resize-handle\" \n stColumnResize\n [column]=\"column\"\n (columnResized)=\"onColumnResized($event)\"\n *ngIf=\"column.resizable !== false\">\n </div>\n </th>\n \n <!-- Settings Column Header -->\n <th \n class=\"settings-column sticky-right\"\n [ngClass]=\"{ 'header-cell': mode === 'standard' }\"\n [ngStyle]=\"{ 'z-index': ZIndex.STICKY_HEADER_CELL }\">\n <div [ngClass]=\"{ 'flex-center': mode === 'virtual', 'header-content': mode === 'standard' }\">\n <st-table-actions \n [tableState]=\"getActiveTableState()\"\n [allowAddColumn]=\"mergedConfig.features?.columnManagement?.allowAdd || false\"\n (addColumnClicked)=\"onAddColumnClick()\">\n </st-table-actions>\n </div>\n </th>\n </tr>\n </thead>\n\n <!-- TABLE BODY -->\n <tbody>\n <!-- Virtual Scroll Rows -->\n <ng-container *ngIf=\"mode === 'virtual'\">\n <tr *ngFor=\"let row of visibleRows$ | async; let relativeIndex = index; trackBy: trackByRowIndex\"\n [attr.data-row-index]=\"getAbsoluteRowIndex(relativeIndex)\">\n <!-- Row Number Cell -->\n <td *ngIf=\"mergedConfig.showRowNumber\" class=\"row-number-cell\" \n [ngClass]=\"{ 'sticky-left': 'left' }\"\n [ngStyle]=\"{\n 'width.px': 30,\n position: 'sticky',\n 'left.px': 0,\n 'z-index': ZIndex.STICKY_BODY_CELL,\n 'height.px': mode === 'virtual' ? getVirtualScrollItemSize() : null\n }\">\n {{getAbsoluteRowIndex(relativeIndex) + 1}}\n </td>\n <ng-container *ngTemplateOutlet=\"bodyCellTemplate; context: { \n row: row,\n rowIndex: getAbsoluteRowIndex(relativeIndex),\n mode: 'virtual',\n relativeIndex: relativeIndex\n }\"></ng-container>\n </tr>\n </ng-container>\n\n <!-- Standard Rows -->\n <ng-container *ngIf=\"mode === 'standard'\">\n <tr *ngFor=\"let row of visibleCellGrid; let rowIndex = index\" [attr.data-row-index]=\"rowIndex\">\n <!-- Row Number Cell -->\n <td *ngIf=\"mergedConfig.showRowNumber\" class=\"row-number-cell\" \n [ngClass]=\"{ 'sticky-left': 'left' }\"\n [ngStyle]=\"{\n 'width.px': 30,\n position: 'sticky',\n 'left.px': 0,\n 'z-index': ZIndex.STICKY_BODY_CELL,\n 'height.px': mode === 'virtual' ? getVirtualScrollItemSize() : null\n }\">\n {{rowIndex + 1}}\n </td>\n <ng-container *ngTemplateOutlet=\"bodyCellTemplate; context: { \n row: row,\n rowIndex: rowIndex,\n mode: 'standard'\n }\"></ng-container>\n </tr>\n </ng-container>\n </tbody>\n </table>\n </ng-template>\n\n <!-- ========================================== -->\n <!-- REUSABLE BODY CELL TEMPLATE -->\n <!-- ========================================== -->\n <ng-template #bodyCellTemplate let-row=\"row\" let-rowIndex=\"rowIndex\" let-mode=\"mode\" let-relativeIndex=\"relativeIndex\">\n <!-- Data Cells -->\n <td \n *ngFor=\"let cell of row; let colIndex = index\"\n [ngClass]=\"{\n 'sticky-left': visibleColumns[colIndex]?.sticky === 'left',\n 'sticky-right': visibleColumns[colIndex]?.sticky === 'right',\n 'sticky-right-first': visibleColumns[colIndex]?.sticky === 'right' && visibleColumns[colIndex]?.key && isFirstStickyRight(visibleColumns[colIndex].key),\n 'align-center': visibleColumns[colIndex]?.alignment === 'center',\n 'align-right': visibleColumns[colIndex]?.alignment === 'right',\n 'cell-focused': cell.isFocused()\n }\"\n [ngStyle]=\"{\n position: visibleColumns[colIndex]?.sticky ? 'sticky' : null,\n 'left.px': visibleColumns[colIndex]?.sticky === 'left' ? (visibleColumns[colIndex]?.stickyOffset || 0) : null,\n 'right.px': visibleColumns[colIndex]?.sticky === 'right' ? (visibleColumns[colIndex]?.stickyOffset || 0) : null,\n 'z-index': visibleColumns[colIndex]?.sticky ? ZIndex.STICKY_BODY_CELL : null,\n 'width.px': visibleColumns[colIndex]?.width,\n 'height.px': mode === 'virtual' ? getVirtualScrollItemSize() : null\n }\"\n (click)=\"isKeyboardNavigationEnabled() ? onCellClick(rowIndex, colIndex) : null\">\n \n <!-- Virtual Scroll Cell -->\n <st-cell \n *ngIf=\"mode === 'virtual'\"\n [cell]=\"cell\" \n [attr.tabindex]=\"cell.isFocused() ? 0 : -1\" \n [editMode]=\"getEditModeForCells()\"\n [tableState]=\"getActiveTableState()\"\n [tableConfig]=\"mergedConfig\"\n [columnIndex]=\"colIndex\"\n (cellEdit)=\"onCellEdit($event)\" \n (cellSave)=\"onCellSave($event)\"\n (cellSaveAndNavigate)=\"onCellSaveAndNavigate($event)\" \n (cellCancel)=\"onCellCancel($event)\"\n (cellChange)=\"cellChange.emit($event)\">\n </st-cell>\n\n <!-- Standard Cell -->\n <st-cell \n *ngIf=\"mode === 'standard'\"\n [cell]=\"cell\" \n [attr.tabindex]=\"cell.isFocused() ? 0 : -1\"\n [tableState]=\"getActiveTableState()\"\n [tableConfig]=\"mergedConfig\"\n [columnIndex]=\"colIndex\"\n (cellSave)=\"onCellSave($event)\"\n (cellChange)=\"cellChange.emit($event)\">\n </st-cell>\n </td>\n \n <!-- Row Actions Cell -->\n <td class=\"settings-column\"\n [ngClass]=\"{\n 'has-actions': hasRowActions()\n }\"\n [ngStyle]=\"{\n position: hasRowActions() ? 'sticky' : null,\n 'right.px': hasRowActions() ? 0 : null,\n 'z-index': hasRowActions() ? ZIndex.STICKY_BODY_CELL : null\n }\">\n <button \n *ngIf=\"hasRowActions()\"\n class=\"settings-trigger\"\n (click)=\"openRowActionsDropdown($event, getRowData(rowIndex), rowIndex)\"\n type=\"button\" \n aria-label=\"Row actions\">\n \u22EF\n </button>\n </td>\n </ng-template>\n\n<ng-template #skeletonLoader>\n <div class=\"list-row\" *ngIf=\"(mergedConfig.tableSkeleton?.enabled | async)\">\n <div *ngFor=\"let i of skeletonColumns\" class=\"list-content\">\n <nile-skeleton-loader variant=\"text\" width=\"90%\" height=\"18\" *ngFor=\"let j of skeletonRows\"></nile-skeleton-loader>\n </div>\n </div>\n</ng-template>", styles: [".st-table{width:100%;overflow:auto;position:relative;height:100%;max-height:30rem;border-radius:4px;border:1px solid #E6E9EB}.st-table st-table-actions{position:sticky;right:0}.st-table.keyboard-navigation-enabled{cursor:pointer}.st-table.keyboard-navigation-enabled:focus{outline:none;box-shadow:0 0 0 2px #3b82f64d}.st-table.keyboard-navigation-enabled td.cell-focused{outline:2px solid #4299e1;outline-offset:-1px;position:relative;box-shadow:0 0 0 3px #3182ce1a}.st-table.keyboard-navigation-enabled td.cell-focused:focus{outline:2px solid #4299e1;outline-offset:-1px;box-shadow:0 0 0 3px #3182ce1a}.st-table.keyboard-navigation-enabled td.cell-focused:has(.st-cell.editing){box-shadow:0 0 0 4px #2563eb26}.st-table.virtual-scroll-enabled{overflow-x:visible;margin:0;border:1px solid #E6E9EB;border-collapse:separate;border-spacing:0}.st-table.virtual-scroll-enabled .st-scroll-viewport{position:relative;overflow-y:auto;overflow-x:auto;scroll-behavior:smooth}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-scroll-spacer{position:absolute;top:0;left:0;width:1px;pointer-events:none;z-index:-1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-positioner{position:absolute;top:0;left:0;right:0;will-change:transform}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element{position:relative;width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead{background-color:#fff;border-bottom:1px solid #E6E9EB}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead.sticky{position:sticky;top:0;z-index:3;background-color:#fff;will-change:top;backface-visibility:hidden}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead.sticky:after{content:\"\";position:absolute;bottom:-2px;left:0;right:0;height:2px;background:linear-gradient(to bottom,rgba(0,0,0,.1),transparent)}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th{padding:0;vertical-align:middle;position:relative;border:none;background-color:#fff;will-change:top;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-left{position:sticky;left:0;background-color:#fff;z-index:2;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-left:not(:has(~ th.sticky-left)){position:relative;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-left:not(:has(~ th.sticky-left)):after{content:\"\";position:absolute;right:-8px;top:0;bottom:0;width:8px;background:linear-gradient(to left,transparent,rgba(0,0,0,.08));pointer-events:none;z-index:1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-right{position:sticky;right:0;background-color:#fff;z-index:2;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-right-first:not(.settings-column){position:relative;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-right-first:not(.settings-column):before{content:\"\";position:absolute;left:-8px;top:0;bottom:0;width:8px;background:linear-gradient(to right,transparent,rgba(0,0,0,.08));pointer-events:none;z-index:1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.settings-column{width:2rem;text-align:center;font-weight:600;font-size:12px;position:sticky;right:0;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb;z-index:2}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.resizable .resize-handle{position:absolute;top:0;right:0;bottom:0;width:8px;cursor:col-resize;z-index:4;pointer-events:auto}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.resizable .resize-handle:after{content:\"\";position:absolute;top:50%;right:3px;transform:translateY(-50%);width:2px;height:20px;background-color:#cbd5e0;opacity:0;transition:opacity .2s}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.resizable .resize-handle:hover:after,.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.resizable .resize-handle:active:after{opacity:1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.row-number-header{text-align:center;font-weight:300;font-size:12px;background-color:#f8f8f8}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-left:not(:has(~ th.sticky-left)):after{content:\"\";position:absolute;right:-6px;top:0;bottom:-1px;width:5px;border-left:1px solid var(--borderColor);background:linear-gradient(90deg,rgba(0,0,0,.08) 0%,rgba(0,0,0,0) 100%)}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-right-first:not(.settings-column):before{content:\"\";position:absolute;left:-6px;top:0;bottom:-1px;width:5px;border-right:1px solid var(--borderColor);background:linear-gradient(90deg,rgba(0,0,0,0) 0%,rgba(0,0,0,.08) 100%)}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody{will-change:transform;position:relative;z-index:1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr{border-bottom:1px solid #E6E9EB;transition:background-color .15s;height:2rem;box-sizing:border-box}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr:last-child{border-bottom:none}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td{padding:0;vertical-align:middle;box-sizing:border-box;height:2rem;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb;border:none}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.align-center{text-align:center}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.align-right{text-align:right}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-left{position:sticky;z-index:2;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-left:not(:has(~ td.sticky-left)){position:relative;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-left:not(:has(~ td.sticky-left)):after{content:\"\";position:absolute;right:-8px;top:0;bottom:0;width:8px;background:linear-gradient(to left,transparent,rgba(0,0,0,.08));pointer-events:none;z-index:1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-right{position:sticky;z-index:2;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-right-first:not(.settings-column){position:relative;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-right-first:not(.settings-column):before{content:\"\";position:absolute;left:-8px;top:0;bottom:0;width:8px;background:linear-gradient(to right,transparent,rgba(0,0,0,.08));pointer-events:none;z-index:1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.settings-column{width:2rem;text-align:center;position:sticky;right:0;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb;z-index:2}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.settings-column .settings-trigger{background:none;border:none;font-size:1rem;line-height:1;cursor:pointer;border-radius:4px;transition:all .15s ease}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.settings-column .settings-trigger:focus{outline:2px solid #4299e1;outline-offset:2px}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.row-number-cell{text-align:center;font-weight:300;font-size:12px;background-color:#f8f8f8}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-left:not(:has(~ td.sticky-left)):after{content:\"\";position:absolute;right:-6px;top:0;bottom:-1px;width:5px;border-left:1px solid var(--borderColor);background:linear-gradient(90deg,rgba(0,0,0,.08) 0%,rgba(0,0,0,0) 100%)}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-right-first:before{content:\"\";position:absolute;left:-6px;top:0;bottom:-1px;width:5px;border-right:1px solid var(--borderColor);background:linear-gradient(90deg,rgba(0,0,0,0) 0%,rgba(0,0,0,.08) 100%)}.st-table:not(.virtual-scroll-enabled) .st-table-element{width:100%;height:100%;overflow:auto;border-collapse:separate;border-spacing:0;table-layout:fixed}.st-table:not(.virtual-scroll-enabled) .st-table-element thead{background-color:#fff;border-bottom:1px solid #E6E9EB}.st-table:not(.virtual-scroll-enabled) .st-table-element thead.sticky{position:sticky;top:0;z-index:3;background-color:#fff;will-change:top;backface-visibility:hidden}.st-table:not(.virtual-scroll-enabled) .st-table-element thead.sticky:after{content:\"\";position:absolute;bottom:-2px;left:0;right:0;height:2px;background:linear-gradient(to bottom,rgba(0,0,0,.1),transparent)}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th{padding:0;vertical-align:middle;position:relative;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb;border:none}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-left{position:sticky;left:0;background-color:#fff;z-index:2;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-left:not(:has(~ th.sticky-left)){position:relative;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-left:not(:has(~ th.sticky-left)):after{content:\"\";position:absolute;right:-8px;top:0;bottom:0;width:8px;background:linear-gradient(to left,transparent,rgba(0,0,0,.08));pointer-events:none;z-index:1}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-right{position:sticky;right:0;background-color:#fff;z-index:2;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-right-first:not(.settings-column){position:relative;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-right-first:not(.settings-column):before{content:\"\";position:absolute;left:-8px;top:0;bottom:0;width:8px;background:linear-gradient(to right,transparent,rgba(0,0,0,.08));pointer-events:none;z-index:1}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.settings-column{width:2rem;text-align:center;font-weight:600;font-size:12px;position:sticky;right:0;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb;z-index:2}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.resizable .resize-handle{position:absolute;top:0;right:0;bottom:0;width:8px;cursor:col-resize;z-index:4;pointer-events:auto}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.resizable .resize-handle:after{content:\"\";position:absolute;top:50%;right:3px;transform:translateY(-50%);width:2px;height:20px;background-color:#cbd5e0;opacity:0;transition:opacity .2s}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.resizable .resize-handle:hover:after,.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.resizable .resize-handle:active:after{opacity:1}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.row-number-header{text-align:center;font-weight:300;font-size:12px;background-color:#f8f8f8}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody{will-change:transform;position:relative;z-index:1}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr{height:2rem;box-sizing:border-box}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr:last-child{border-bottom:none}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td{padding:0;vertical-align:middle;height:2rem;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb;border:none}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.row-number-cell{text-align:center;font-weight:300;font-size:12px;background-color:#f8f8f8}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.align-center{text-align:center}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.align-right{text-align:right}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-left{position:sticky;z-index:2;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-left:not(:has(~ td.sticky-left)){position:relative;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-left:not(:has(~ td.sticky-left)):after{content:\"\";position:absolute;right:-8px;top:0;bottom:0;width:8px;background:linear-gradient(to left,transparent,rgba(0,0,0,.08));pointer-events:none;z-index:1}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-right{position:sticky;z-index:2;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-right-first:not(.settings-column){position:relative;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-right-first:not(.settings-column):before{content:\"\";position:absolute;left:-8px;top:0;bottom:0;width:8px;background:linear-gradient(to right,transparent,rgba(0,0,0,.08));pointer-events:none;z-index:1}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.settings-column{position:sticky;right:0;width:2rem;text-align:center;border-right:none;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb;z-index:2}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.settings-column .settings-trigger{background:none;border:none;font-size:1rem;line-height:1;cursor:pointer;border-radius:4px;transition:all .15s ease}.st-table:not(.virtual-scroll-enabled) .st-table-element .header-content{display:flex;height:2rem;align-items:center}.st-table:not(.virtual-scroll-enabled) .st-table-element .header-content .table-header-text{flex-grow:1;padding-left:4px}.st-table:not(.virtual-scroll-enabled) .st-table-element .settings-column>.header-content{display:flex;align-items:center;justify-content:center}.flex-center{display:flex;align-items:center;justify-content:center}.list-row{padding:1rem}.list-row .list-content{display:flex;justify-content:space-evenly;gap:4px;margin-bottom:1rem}\n"], components: [{ type: StPaginationComponent, selector: "st-pagination", inputs: ["tableState", "tableConfig", "position"] }, { type: StColumnMenuDropdownComponent, selector: "st-column-menu-dropdown", inputs: ["isOpen", "position", "context"], outputs: ["actionClicked", "closed"] }, { type: StRowActionsDropdownComponent, selector: "st-row-actions-dropdown", inputs: ["isOpen", "position", "context"], outputs: ["actionClicked", "closed"] }, { type: StHeaderComponent, selector: "st-header", inputs: ["column", "columnIndex", "isFirstColumn", "isLastColumn", "tableState", "enableSorting", "enableFiltering"], outputs: ["sortToggle", "filterChange", "columnMoved", "menuClick"] }, { type: StTableActionsComponent, selector: "st-table-actions", inputs: ["tableState", "allowAddColumn"], outputs: ["addColumnClicked"] }, { type: StCellComponent, selector: "st-cell", inputs: ["cell", "editMode", "tableState", "tableConfig", "columnIndex"], outputs: ["cellChange", "cellEdit", "cellSave", "cellCancel", "cellSaveAndNavigate"] }], directives: [{ type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: StKeyboardNavigationDirective, selector: "[stKeyboardNavigation]", inputs: ["tableState"] }, { type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: StColumnResizeDirective, selector: "[stColumnResize]", inputs: ["column"], outputs: ["columnResized"] }], pipes: { "async": i1.AsyncPipe } });
8754
8847
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StTableComponent, decorators: [{
8755
8848
  type: Component,
8756
- args: [{ selector: 'st-table', template: "<!-- Top pagination controls -->\n<st-pagination \n *ngIf=\"showTopPagination() && !(mergedConfig.tableSkeleton?.enabled | async)\" \n [tableState]=\"getActiveTableState()\"\n [tableConfig]=\"mergedConfig\"\n position=\"top\">\n</st-pagination>\n\n<div class=\"st-table\" *ngIf=\"!(mergedConfig.tableSkeleton?.enabled | async)\" \n [ngClass]=\"{\n 'virtual-scroll-enabled': isVirtualScrollEnabled(),\n 'keyboard-navigation-enabled': isKeyboardNavigationEnabled()\n }\"\n [ngStyle]=\"{\n 'max-height.px': !isVirtualScrollEnabled() ? mergedConfig.display?.maxHeight : null\n }\"\n stKeyboardNavigation\n [tableState]=\"getActiveTableState()\" \n [attr.tabindex]=\"isKeyboardNavigationEnabled() ? 0 : -1\"\n (focus)=\"onTableContainerFocus($event)\"\n [attr.title]=\"isKeyboardNavigationEnabled() ? 'Click a cell or press Tab to start keyboard navigation' : null\">\n <!-- Unified Table Actions Menu -->\n\n <!-- Virtual scroll viewport wrapper -->\n <div class=\"st-scroll-viewport\" #scrollViewport *ngIf=\"isVirtualScrollEnabled()\"\n [ngStyle]=\"{ 'height.px': getVirtualScrollViewportHeight() }\">\n\n <!-- Spacer to create scrollable area -->\n <div class=\"st-scroll-spacer\" [ngStyle]=\"{ 'height.px': virtualScrollTotalHeight$ | async }\">\n </div>\n\n <!-- Table positioner with transform (instead of tbody) -->\n <div class=\"st-table-positioner\" [ngStyle]=\"{ transform: 'translateY(' + (virtualScrollOffsetY$ | async) + 'px)' }\">\n <!-- Table with only visible rows -->\n <ng-container *ngTemplateOutlet=\"tableTemplate; context: { \n mode: 'virtual',\n theadStyle: { top: (virtualScrollOffsetYNeg$ | async) + 'px' }\n }\"></ng-container>\n </div>\n </div>\n \n <!-- Standard table (when virtual scroll disabled) -->\n <ng-container *ngIf=\"!isVirtualScrollEnabled()\">\n <ng-container *ngTemplateOutlet=\"tableTemplate; context: { \n mode: 'standard',\n theadStyle: null\n }\"></ng-container>\n </ng-container>\n\n <!-- Shared Column Menu Dropdown -->\n <st-column-menu-dropdown \n [isOpen]=\"columnMenuState.isOpen\"\n [position]=\"columnMenuState.position\"\n [context]=\"columnMenuState.context\"\n (actionClicked)=\"onColumnActionClicked($event)\"\n (closed)=\"closeColumnMenu()\">\n </st-column-menu-dropdown>\n</div>\n\n<ng-container *ngIf=\"(mergedConfig.tableSkeleton?.enabled | async)\">\n <ng-container *ngTemplateOutlet=\"skeletonLoader\"></ng-container>\n</ng-container>\n\n<!-- Shared Row Actions Dropdown -->\n<st-row-actions-dropdown [isOpen]=\"dropdownState.isOpen\" [position]=\"dropdownState.position\"\n [context]=\"dropdownState.context\" (actionClicked)=\"onRowActionClicked($event)\" (closed)=\"closeRowActionsDropdown()\">\n</st-row-actions-dropdown>\n\n\n<!-- Bottom pagination controls -->\n<st-pagination \n *ngIf=\"showBottomPagination() && !(mergedConfig.tableSkeleton?.enabled | async)\" \n [tableState]=\"getActiveTableState()\"\n [tableConfig]=\"mergedConfig\"\n position=\"bottom\">\n</st-pagination>\n\n\n <!-- ========================================== -->\n <!-- REUSABLE TABLE TEMPLATE -->\n <!-- ========================================== -->\n <ng-template #tableTemplate let-mode=\"mode\" let-theadStyle=\"theadStyle\">\n <table class=\"st-table-element\">\n <!-- TABLE HEADER -->\n <thead [ngClass]=\"{ 'sticky': mergedConfig.display?.stickyHeader }\" [ngStyle]=\"theadStyle\">\n <tr>\n <!-- Row Number Header -->\n <th *ngIf=\"mergedConfig.showRowNumber\" class=\"row-number-header header-cell sticky-left\"\n [ngStyle]=\"{\n position: 'sticky',\n 'left.px': 0,\n 'z-index': ZIndex.STICKY_HEADER_CELL,\n 'width.px': 30\n }\">\n #\n </th>\n <!-- Column Headers -->\n <th \n *ngFor=\"let column of visibleColumns; let colIndex = index; let isFirst = first; let isLast = last\"\n [ngClass]=\"{\n 'header-cell': mode === 'standard',\n 'sticky-left': column.sticky === 'left',\n 'sticky-right': column.sticky === 'right',\n 'sticky-right-first': column.sticky === 'right' && isFirstStickyRight(column.key),\n 'resizable': column.resizable !== false\n }\"\n [ngStyle]=\"{\n position: column.sticky ? 'sticky' : null,\n 'left.px': column.sticky === 'left' ? (column.stickyOffset || 0) : null,\n 'right.px': column.sticky === 'right' ? (column.stickyOffset || 0) : null,\n 'z-index': column.sticky ? ZIndex.STICKY_HEADER_CELL : null,\n 'width.px': column.width\n }\">\n \n <st-header \n [column]=\"column\"\n [columnIndex]=\"colIndex\"\n [isFirstColumn]=\"isFirst\"\n [isLastColumn]=\"isLast\"\n [tableState]=\"getActiveTableState()\"\n [enableSorting]=\"mergedConfig.sorting?.enabled ?? enableSorting\"\n [enableFiltering]=\"mergedConfig.filtering?.enabled ?? enableFiltering\"\n (columnMoved)=\"onColumnMoved($event)\"\n (menuClick)=\"openColumnMenu($event, column, colIndex, isFirst, isLast)\">\n </st-header>\n\n <div \n class=\"resize-handle\" \n stColumnResize\n [column]=\"column\"\n (columnResized)=\"onColumnResized($event)\"\n *ngIf=\"column.resizable !== false\">\n </div>\n </th>\n \n <!-- Settings Column Header -->\n <th \n class=\"settings-column sticky-right\"\n [ngClass]=\"{ 'header-cell': mode === 'standard' }\"\n [ngStyle]=\"{ 'z-index': ZIndex.STICKY_HEADER_CELL }\">\n <div [ngClass]=\"{ 'flex-center': mode === 'virtual', 'header-content': mode === 'standard' }\">\n <st-table-actions \n [tableState]=\"getActiveTableState()\"\n [allowAddColumn]=\"mergedConfig.features?.columnManagement?.allowAdd || false\"\n (addColumnClicked)=\"onAddColumnClick()\">\n </st-table-actions>\n </div>\n </th>\n </tr>\n </thead>\n\n <!-- TABLE BODY -->\n <tbody>\n <!-- Virtual Scroll Rows -->\n <ng-container *ngIf=\"mode === 'virtual'\">\n <tr *ngFor=\"let row of visibleRows$ | async; let relativeIndex = index; trackBy: trackByRowIndex\"\n [attr.data-row-index]=\"getAbsoluteRowIndex(relativeIndex)\">\n <!-- Row Number Cell -->\n <td *ngIf=\"mergedConfig.showRowNumber\" class=\"row-number-cell\" \n [ngClass]=\"{ 'sticky-left': 'left' }\"\n [ngStyle]=\"{\n 'width.px': 30,\n position: 'sticky',\n 'left.px': 0,\n 'z-index': ZIndex.STICKY_BODY_CELL,\n 'height.px': mode === 'virtual' ? getVirtualScrollItemSize() : null\n }\">\n {{getAbsoluteRowIndex(relativeIndex) + 1}}\n </td>\n <ng-container *ngTemplateOutlet=\"bodyCellTemplate; context: { \n row: row,\n rowIndex: getAbsoluteRowIndex(relativeIndex),\n mode: 'virtual',\n relativeIndex: relativeIndex\n }\"></ng-container>\n </tr>\n </ng-container>\n\n <!-- Standard Rows -->\n <ng-container *ngIf=\"mode === 'standard'\">\n <tr *ngFor=\"let row of visibleCellGrid; let rowIndex = index\" [attr.data-row-index]=\"rowIndex\">\n <!-- Row Number Cell -->\n <td *ngIf=\"mergedConfig.showRowNumber\" class=\"row-number-cell\" \n [ngClass]=\"{ 'sticky-left': 'left' }\"\n [ngStyle]=\"{\n 'width.px': 30,\n position: 'sticky',\n 'left.px': 0,\n 'z-index': ZIndex.STICKY_BODY_CELL,\n 'height.px': mode === 'virtual' ? getVirtualScrollItemSize() : null\n }\">\n {{rowIndex + 1}}\n </td>\n <ng-container *ngTemplateOutlet=\"bodyCellTemplate; context: { \n row: row,\n rowIndex: rowIndex,\n mode: 'standard'\n }\"></ng-container>\n </tr>\n </ng-container>\n </tbody>\n </table>\n </ng-template>\n\n <!-- ========================================== -->\n <!-- REUSABLE BODY CELL TEMPLATE -->\n <!-- ========================================== -->\n <ng-template #bodyCellTemplate let-row=\"row\" let-rowIndex=\"rowIndex\" let-mode=\"mode\" let-relativeIndex=\"relativeIndex\">\n <!-- Data Cells -->\n <td \n *ngFor=\"let cell of row; let colIndex = index\"\n [ngClass]=\"{\n 'sticky-left': visibleColumns[colIndex]?.sticky === 'left',\n 'sticky-right': visibleColumns[colIndex]?.sticky === 'right',\n 'sticky-right-first': visibleColumns[colIndex]?.sticky === 'right' && visibleColumns[colIndex]?.key && isFirstStickyRight(visibleColumns[colIndex].key),\n 'align-center': visibleColumns[colIndex]?.alignment === 'center',\n 'align-right': visibleColumns[colIndex]?.alignment === 'right',\n 'cell-focused': cell.isFocused()\n }\"\n [ngStyle]=\"{\n position: visibleColumns[colIndex]?.sticky ? 'sticky' : null,\n 'left.px': visibleColumns[colIndex]?.sticky === 'left' ? (visibleColumns[colIndex]?.stickyOffset || 0) : null,\n 'right.px': visibleColumns[colIndex]?.sticky === 'right' ? (visibleColumns[colIndex]?.stickyOffset || 0) : null,\n 'z-index': visibleColumns[colIndex]?.sticky ? ZIndex.STICKY_BODY_CELL : null,\n 'width.px': visibleColumns[colIndex]?.width,\n 'height.px': mode === 'virtual' ? getVirtualScrollItemSize() : null\n }\"\n (click)=\"isKeyboardNavigationEnabled() ? onCellClick(rowIndex, colIndex) : null\">\n \n <!-- Virtual Scroll Cell -->\n <st-cell \n *ngIf=\"mode === 'virtual'\"\n [cell]=\"cell\" \n [attr.tabindex]=\"cell.isFocused() ? 0 : -1\" \n [editMode]=\"getEditModeForCells()\"\n [tableState]=\"getActiveTableState()\"\n [tableConfig]=\"mergedConfig\"\n [columnIndex]=\"colIndex\"\n (cellEdit)=\"onCellEdit($event)\" \n (cellSave)=\"onCellSave($event)\"\n (cellSaveAndNavigate)=\"onCellSaveAndNavigate($event)\" \n (cellCancel)=\"onCellCancel($event)\"\n (cellChange)=\"cellChange.emit($event)\">\n </st-cell>\n\n <!-- Standard Cell -->\n <st-cell \n *ngIf=\"mode === 'standard'\"\n [cell]=\"cell\" \n [attr.tabindex]=\"cell.isFocused() ? 0 : -1\"\n [tableState]=\"getActiveTableState()\"\n [tableConfig]=\"mergedConfig\"\n [columnIndex]=\"colIndex\"\n (cellSave)=\"onCellSave($event)\"\n (cellChange)=\"cellChange.emit($event)\">\n </st-cell>\n </td>\n \n <!-- Row Actions Cell -->\n <td class=\"settings-column\"\n [ngClass]=\"{\n 'has-actions': hasRowActions()\n }\"\n [ngStyle]=\"{\n position: hasRowActions() ? 'sticky' : null,\n 'right.px': hasRowActions() ? 0 : null,\n 'z-index': hasRowActions() ? ZIndex.STICKY_BODY_CELL : null\n }\">\n <button \n *ngIf=\"hasRowActions()\"\n class=\"settings-trigger\"\n (click)=\"openRowActionsDropdown($event, getRowData(rowIndex), rowIndex)\"\n type=\"button\" \n aria-label=\"Row actions\">\n \u22EF\n </button>\n </td>\n </ng-template>\n\n<ng-template #skeletonLoader>\n <div class=\"list-row\" *ngIf=\"(mergedConfig.tableSkeleton?.enabled | async)\">\n <div *ngFor=\"let i of skeletonColumns\" class=\"list-content\">\n <nile-skeleton-loader variant=\"text\" width=\"90%\" height=\"18\" *ngFor=\"let j of skeletonRows\"></nile-skeleton-loader>\n </div>\n </div>\n</ng-template>", styles: [".st-table{width:100%;overflow:auto;position:relative;height:100%;max-height:30rem;border-radius:4px;border:1px solid #E6E9EB}.st-table st-table-actions{position:sticky;right:0}.st-table.keyboard-navigation-enabled{cursor:pointer}.st-table.keyboard-navigation-enabled:focus{outline:none;box-shadow:0 0 0 2px #3b82f64d}.st-table.keyboard-navigation-enabled td.cell-focused{outline:2px solid #4299e1;outline-offset:-1px;position:relative;box-shadow:0 0 0 3px #3182ce1a}.st-table.keyboard-navigation-enabled td.cell-focused:focus{outline:2px solid #4299e1;outline-offset:-1px;box-shadow:0 0 0 3px #3182ce1a}.st-table.keyboard-navigation-enabled td.cell-focused:has(.st-cell.editing){box-shadow:0 0 0 4px #2563eb26}.st-table.virtual-scroll-enabled{overflow-x:visible;margin:0;border:1px solid #E6E9EB;border-collapse:collapse}.st-table.virtual-scroll-enabled .st-scroll-viewport{position:relative;overflow-y:auto;overflow-x:auto;scroll-behavior:smooth}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-scroll-spacer{position:absolute;top:0;left:0;width:1px;pointer-events:none;z-index:-1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-positioner{position:absolute;top:0;left:0;right:0;will-change:transform}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element{position:relative;width:100%;border-collapse:collapse;border-spacing:0;table-layout:fixed}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead{background-color:#fff;border-bottom:1px solid #E6E9EB}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead.sticky{position:sticky;top:0;z-index:3;background-color:#fff;will-change:top;backface-visibility:hidden}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead.sticky:after{content:\"\";position:absolute;bottom:-2px;left:0;right:0;height:2px;background:linear-gradient(to bottom,rgba(0,0,0,.1),transparent)}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th{padding:0;vertical-align:middle;position:relative;border:none;background-color:#fff;will-change:top;border-right:1px solid #E6E9EB}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-left{position:sticky;left:0;background-color:#fff;border-right:1px solid #E6E9EB!important}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-left:not(:has(~ th.sticky-left)){box-shadow:2px 0 5px -2px #0003}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-right{position:sticky;right:0;background-color:#fff}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.settings-column{width:2rem;text-align:center;font-weight:600;font-size:12px}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.resizable .resize-handle{position:absolute;top:0;right:0;bottom:0;width:8px;cursor:col-resize;z-index:4;pointer-events:auto}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.resizable .resize-handle:after{content:\"\";position:absolute;top:50%;right:3px;transform:translateY(-50%);width:2px;height:20px;background-color:#cbd5e0;opacity:0;transition:opacity .2s}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.resizable .resize-handle:hover:after,.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.resizable .resize-handle:active:after{opacity:1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.row-number-header{text-align:center;font-weight:300;font-size:12px;background-color:#f8f8f8}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-left:not(:has(~ th.sticky-left)):after{content:\"\";position:absolute;right:-6px;top:0;bottom:-1px;width:5px;border-left:1px solid var(--borderColor);background:linear-gradient(90deg,rgba(0,0,0,.08) 0%,rgba(0,0,0,0) 100%)}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-right-first:not(.settings-column):before{content:\"\";position:absolute;left:-6px;top:0;bottom:-1px;width:5px;border-right:1px solid var(--borderColor);background:linear-gradient(90deg,rgba(0,0,0,0) 0%,rgba(0,0,0,.08) 100%)}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody{will-change:transform;position:relative;z-index:1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr{border-bottom:1px solid #E6E9EB;transition:background-color .15s;height:2rem;box-sizing:border-box}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr:last-child{border-bottom:none}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td{padding:0;vertical-align:middle;box-sizing:border-box;height:2rem;border:1px solid #E6E9EB;border-left:none;background-color:transparent}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.align-center{text-align:center}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.align-right{text-align:right}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-left{position:sticky;z-index:2;border-right:1px solid #E6E9EB!important}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-left:not(:has(~ td.sticky-left)){box-shadow:2px 0 5px -2px #0003}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-right{position:sticky;z-index:2}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.settings-column{width:2rem;text-align:center}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.settings-column .settings-trigger{background:none;border:none;font-size:1rem;line-height:1;cursor:pointer;border-radius:4px;transition:all .15s ease}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.settings-column .settings-trigger:focus{outline:2px solid #4299e1;outline-offset:2px}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.row-number-cell{text-align:center;font-weight:300;font-size:12px;background-color:#f8f8f8}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-left:not(:has(~ td.sticky-left)):after{content:\"\";position:absolute;right:-6px;top:0;bottom:-1px;width:5px;border-left:1px solid var(--borderColor);background:linear-gradient(90deg,rgba(0,0,0,.08) 0%,rgba(0,0,0,0) 100%)}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-right-first:before{content:\"\";position:absolute;left:-6px;top:0;bottom:-1px;width:5px;border-right:1px solid var(--borderColor);background:linear-gradient(90deg,rgba(0,0,0,0) 0%,rgba(0,0,0,.08) 100%)}.st-table:not(.virtual-scroll-enabled) .st-table-element{width:100%;height:100%;overflow:auto;border-collapse:collapse;table-layout:fixed}.st-table:not(.virtual-scroll-enabled) .st-table-element thead{background-color:#fff;border-bottom:1px solid #E6E9EB}.st-table:not(.virtual-scroll-enabled) .st-table-element thead.sticky{position:sticky;top:0;z-index:3;background-color:#fff;will-change:top;backface-visibility:hidden}.st-table:not(.virtual-scroll-enabled) .st-table-element thead.sticky:after{content:\"\";position:absolute;bottom:-2px;left:0;right:0;height:2px;background:linear-gradient(to bottom,rgba(0,0,0,.1),transparent)}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th{padding:0;vertical-align:middle;position:relative;background-color:#fff;border-right:1px solid #E6E9EB}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-left{position:sticky;left:0;border-right:1px solid #E6E9EB!important}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-left:not(:has(~ th.sticky-left)){box-shadow:2px 0 5px -2px #0003}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-right{position:sticky;right:0;border-left:1px solid #E6E9EB!important}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-right-first:not(.settings-column){box-shadow:-2px 0 5px -2px #0003}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.settings-column{width:2rem;text-align:center;font-weight:600;font-size:12px;border-right:none;position:sticky;right:0}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.resizable .resize-handle{position:absolute;top:0;right:0;bottom:0;width:8px;cursor:col-resize;z-index:4;pointer-events:auto}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.resizable .resize-handle:after{content:\"\";position:absolute;top:50%;right:3px;transform:translateY(-50%);width:2px;height:20px;background-color:#cbd5e0;opacity:0;transition:opacity .2s}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.resizable .resize-handle:hover:after,.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.resizable .resize-handle:active:after{opacity:1}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.row-number-header{text-align:center;font-weight:300;font-size:12px;background-color:#f8f8f8}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody{will-change:transform;position:relative;z-index:1}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr{height:2rem;box-sizing:border-box}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr:last-child{border-bottom:none}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td{padding:0;vertical-align:middle;height:2rem;border:1px solid #E6E9EB;border-left:none}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.row-number-cell{text-align:center;font-weight:300;font-size:12px;background-color:#f8f8f8}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.align-center{text-align:center}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.align-right{text-align:right}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-left{position:sticky;z-index:2;border-right:1px solid #E6E9EB!important}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-left:not(:has(~ td.sticky-left)){box-shadow:2px 0 5px -2px #0003}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-right{position:sticky;z-index:2;border-left:1px solid #E6E9EB!important}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-right-first:not(.settings-column){box-shadow:-2px 0 5px -2px #0003}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.settings-column{position:sticky;right:0;width:2rem;text-align:center;border-right:none}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.settings-column .settings-trigger{background:none;border:none;font-size:1rem;line-height:1;cursor:pointer;border-radius:4px;transition:all .15s ease}.st-table:not(.virtual-scroll-enabled) .st-table-element .header-content{display:flex;height:2rem;align-items:center}.st-table:not(.virtual-scroll-enabled) .st-table-element .header-content .table-header-text{flex-grow:1;padding-left:4px}.st-table:not(.virtual-scroll-enabled) .st-table-element .settings-column>.header-content{display:flex;align-items:center;justify-content:center}.flex-center{display:flex;align-items:center;justify-content:center}.list-row{padding:1rem}.list-row .list-content{display:flex;justify-content:space-evenly;gap:4px;margin-bottom:1rem}\n"] }]
8849
+ args: [{ selector: 'st-table', template: "<!-- Top pagination controls -->\n<st-pagination \n *ngIf=\"showTopPagination() && !(mergedConfig.tableSkeleton?.enabled | async)\" \n [tableState]=\"getActiveTableState()\"\n [tableConfig]=\"mergedConfig\"\n position=\"top\">\n</st-pagination>\n\n<div class=\"st-table\" *ngIf=\"!(mergedConfig.tableSkeleton?.enabled | async)\" \n [ngClass]=\"{\n 'virtual-scroll-enabled': isVirtualScrollEnabled(),\n 'keyboard-navigation-enabled': isKeyboardNavigationEnabled()\n }\"\n [ngStyle]=\"{\n 'max-height.px': !isVirtualScrollEnabled() ? mergedConfig.display?.maxHeight : null\n }\"\n stKeyboardNavigation\n [tableState]=\"getActiveTableState()\" \n [attr.tabindex]=\"isKeyboardNavigationEnabled() ? 0 : -1\"\n (focus)=\"onTableContainerFocus($event)\"\n [attr.title]=\"isKeyboardNavigationEnabled() ? 'Click a cell or press Tab to start keyboard navigation' : null\">\n <!-- Unified Table Actions Menu -->\n\n <!-- Virtual scroll viewport wrapper -->\n <div class=\"st-scroll-viewport\" #scrollViewport *ngIf=\"isVirtualScrollEnabled()\"\n [ngStyle]=\"{ 'height.px': getVirtualScrollViewportHeight() }\">\n\n <!-- Spacer to create scrollable area -->\n <div class=\"st-scroll-spacer\" [ngStyle]=\"{ 'height.px': virtualScrollTotalHeight$ | async }\">\n </div>\n\n <!-- Table positioner with transform (instead of tbody) -->\n <div class=\"st-table-positioner\" [ngStyle]=\"{ transform: 'translateY(' + (virtualScrollOffsetY$ | async) + 'px)' }\">\n <!-- Table with only visible rows -->\n <ng-container *ngTemplateOutlet=\"tableTemplate; context: { \n mode: 'virtual',\n theadStyle: { top: (virtualScrollOffsetYNeg$ | async) + 'px' }\n }\"></ng-container>\n </div>\n </div>\n \n <!-- Standard table (when virtual scroll disabled) -->\n <ng-container *ngIf=\"!isVirtualScrollEnabled()\">\n <ng-container *ngTemplateOutlet=\"tableTemplate; context: { \n mode: 'standard',\n theadStyle: null\n }\"></ng-container>\n </ng-container>\n\n <!-- Shared Column Menu Dropdown -->\n <st-column-menu-dropdown \n [isOpen]=\"columnMenuState.isOpen\"\n [position]=\"columnMenuState.position\"\n [context]=\"columnMenuState.context\"\n (actionClicked)=\"onColumnActionClicked($event)\"\n (closed)=\"closeColumnMenu()\">\n </st-column-menu-dropdown>\n</div>\n\n<ng-container *ngIf=\"(mergedConfig.tableSkeleton?.enabled | async)\">\n <ng-container *ngTemplateOutlet=\"skeletonLoader\"></ng-container>\n</ng-container>\n\n<!-- Shared Row Actions Dropdown -->\n<st-row-actions-dropdown [isOpen]=\"dropdownState.isOpen\" [position]=\"dropdownState.position\"\n [context]=\"dropdownState.context\" (actionClicked)=\"onRowActionClicked($event)\" (closed)=\"closeRowActionsDropdown()\">\n</st-row-actions-dropdown>\n\n\n<!-- Bottom pagination controls -->\n<st-pagination \n *ngIf=\"showBottomPagination() && !(mergedConfig.tableSkeleton?.enabled | async)\" \n [tableState]=\"getActiveTableState()\"\n [tableConfig]=\"mergedConfig\"\n position=\"bottom\">\n</st-pagination>\n\n\n <!-- ========================================== -->\n <!-- REUSABLE TABLE TEMPLATE -->\n <!-- ========================================== -->\n <ng-template #tableTemplate let-mode=\"mode\" let-theadStyle=\"theadStyle\">\n <table class=\"st-table-element\">\n <!-- TABLE HEADER -->\n <thead [ngClass]=\"{ 'sticky': mergedConfig.display?.stickyHeader }\" [ngStyle]=\"theadStyle\">\n <tr>\n <!-- Row Number Header -->\n <th *ngIf=\"mergedConfig.showRowNumber\" class=\"row-number-header header-cell sticky-left\"\n [ngStyle]=\"{\n position: 'sticky',\n 'left.px': 0,\n 'z-index': ZIndex.STICKY_HEADER_CELL,\n 'width.px': 30\n }\">\n #\n </th>\n <!-- Column Headers -->\n <th \n *ngFor=\"let column of visibleColumns; let colIndex = index; let isFirst = first; let isLast = last\"\n [ngClass]=\"{\n 'header-cell': mode === 'standard',\n 'sticky-left': column.sticky === 'left',\n 'sticky-right': column.sticky === 'right',\n 'sticky-right-first': column.sticky === 'right' && isFirstStickyRight(column.key),\n 'resizable': column.resizable !== false\n }\"\n [ngStyle]=\"{\n position: column.sticky ? 'sticky' : null,\n 'left.px': column.sticky === 'left' ? (column.stickyOffset || 0) : null,\n 'right.px': column.sticky === 'right' ? (column.stickyOffset || 0) : null,\n 'z-index': column.sticky ? ZIndex.STICKY_HEADER_CELL : null,\n 'width.px': column.width\n }\">\n \n <st-header \n [column]=\"column\"\n [columnIndex]=\"colIndex\"\n [isFirstColumn]=\"isFirst\"\n [isLastColumn]=\"isLast\"\n [tableState]=\"getActiveTableState()\"\n [enableSorting]=\"mergedConfig.sorting?.enabled ?? enableSorting\"\n [enableFiltering]=\"mergedConfig.filtering?.enabled ?? enableFiltering\"\n (columnMoved)=\"onColumnMoved($event)\"\n (menuClick)=\"openColumnMenu($event, column, colIndex, isFirst, isLast)\">\n </st-header>\n\n <div \n class=\"resize-handle\" \n stColumnResize\n [column]=\"column\"\n (columnResized)=\"onColumnResized($event)\"\n *ngIf=\"column.resizable !== false\">\n </div>\n </th>\n \n <!-- Settings Column Header -->\n <th \n class=\"settings-column sticky-right\"\n [ngClass]=\"{ 'header-cell': mode === 'standard' }\"\n [ngStyle]=\"{ 'z-index': ZIndex.STICKY_HEADER_CELL }\">\n <div [ngClass]=\"{ 'flex-center': mode === 'virtual', 'header-content': mode === 'standard' }\">\n <st-table-actions \n [tableState]=\"getActiveTableState()\"\n [allowAddColumn]=\"mergedConfig.features?.columnManagement?.allowAdd || false\"\n (addColumnClicked)=\"onAddColumnClick()\">\n </st-table-actions>\n </div>\n </th>\n </tr>\n </thead>\n\n <!-- TABLE BODY -->\n <tbody>\n <!-- Virtual Scroll Rows -->\n <ng-container *ngIf=\"mode === 'virtual'\">\n <tr *ngFor=\"let row of visibleRows$ | async; let relativeIndex = index; trackBy: trackByRowIndex\"\n [attr.data-row-index]=\"getAbsoluteRowIndex(relativeIndex)\">\n <!-- Row Number Cell -->\n <td *ngIf=\"mergedConfig.showRowNumber\" class=\"row-number-cell\" \n [ngClass]=\"{ 'sticky-left': 'left' }\"\n [ngStyle]=\"{\n 'width.px': 30,\n position: 'sticky',\n 'left.px': 0,\n 'z-index': ZIndex.STICKY_BODY_CELL,\n 'height.px': mode === 'virtual' ? getVirtualScrollItemSize() : null\n }\">\n {{getAbsoluteRowIndex(relativeIndex) + 1}}\n </td>\n <ng-container *ngTemplateOutlet=\"bodyCellTemplate; context: { \n row: row,\n rowIndex: getAbsoluteRowIndex(relativeIndex),\n mode: 'virtual',\n relativeIndex: relativeIndex\n }\"></ng-container>\n </tr>\n </ng-container>\n\n <!-- Standard Rows -->\n <ng-container *ngIf=\"mode === 'standard'\">\n <tr *ngFor=\"let row of visibleCellGrid; let rowIndex = index\" [attr.data-row-index]=\"rowIndex\">\n <!-- Row Number Cell -->\n <td *ngIf=\"mergedConfig.showRowNumber\" class=\"row-number-cell\" \n [ngClass]=\"{ 'sticky-left': 'left' }\"\n [ngStyle]=\"{\n 'width.px': 30,\n position: 'sticky',\n 'left.px': 0,\n 'z-index': ZIndex.STICKY_BODY_CELL,\n 'height.px': mode === 'virtual' ? getVirtualScrollItemSize() : null\n }\">\n {{rowIndex + 1}}\n </td>\n <ng-container *ngTemplateOutlet=\"bodyCellTemplate; context: { \n row: row,\n rowIndex: rowIndex,\n mode: 'standard'\n }\"></ng-container>\n </tr>\n </ng-container>\n </tbody>\n </table>\n </ng-template>\n\n <!-- ========================================== -->\n <!-- REUSABLE BODY CELL TEMPLATE -->\n <!-- ========================================== -->\n <ng-template #bodyCellTemplate let-row=\"row\" let-rowIndex=\"rowIndex\" let-mode=\"mode\" let-relativeIndex=\"relativeIndex\">\n <!-- Data Cells -->\n <td \n *ngFor=\"let cell of row; let colIndex = index\"\n [ngClass]=\"{\n 'sticky-left': visibleColumns[colIndex]?.sticky === 'left',\n 'sticky-right': visibleColumns[colIndex]?.sticky === 'right',\n 'sticky-right-first': visibleColumns[colIndex]?.sticky === 'right' && visibleColumns[colIndex]?.key && isFirstStickyRight(visibleColumns[colIndex].key),\n 'align-center': visibleColumns[colIndex]?.alignment === 'center',\n 'align-right': visibleColumns[colIndex]?.alignment === 'right',\n 'cell-focused': cell.isFocused()\n }\"\n [ngStyle]=\"{\n position: visibleColumns[colIndex]?.sticky ? 'sticky' : null,\n 'left.px': visibleColumns[colIndex]?.sticky === 'left' ? (visibleColumns[colIndex]?.stickyOffset || 0) : null,\n 'right.px': visibleColumns[colIndex]?.sticky === 'right' ? (visibleColumns[colIndex]?.stickyOffset || 0) : null,\n 'z-index': visibleColumns[colIndex]?.sticky ? ZIndex.STICKY_BODY_CELL : null,\n 'width.px': visibleColumns[colIndex]?.width,\n 'height.px': mode === 'virtual' ? getVirtualScrollItemSize() : null\n }\"\n (click)=\"isKeyboardNavigationEnabled() ? onCellClick(rowIndex, colIndex) : null\">\n \n <!-- Virtual Scroll Cell -->\n <st-cell \n *ngIf=\"mode === 'virtual'\"\n [cell]=\"cell\" \n [attr.tabindex]=\"cell.isFocused() ? 0 : -1\" \n [editMode]=\"getEditModeForCells()\"\n [tableState]=\"getActiveTableState()\"\n [tableConfig]=\"mergedConfig\"\n [columnIndex]=\"colIndex\"\n (cellEdit)=\"onCellEdit($event)\" \n (cellSave)=\"onCellSave($event)\"\n (cellSaveAndNavigate)=\"onCellSaveAndNavigate($event)\" \n (cellCancel)=\"onCellCancel($event)\"\n (cellChange)=\"cellChange.emit($event)\">\n </st-cell>\n\n <!-- Standard Cell -->\n <st-cell \n *ngIf=\"mode === 'standard'\"\n [cell]=\"cell\" \n [attr.tabindex]=\"cell.isFocused() ? 0 : -1\"\n [tableState]=\"getActiveTableState()\"\n [tableConfig]=\"mergedConfig\"\n [columnIndex]=\"colIndex\"\n (cellSave)=\"onCellSave($event)\"\n (cellChange)=\"cellChange.emit($event)\">\n </st-cell>\n </td>\n \n <!-- Row Actions Cell -->\n <td class=\"settings-column\"\n [ngClass]=\"{\n 'has-actions': hasRowActions()\n }\"\n [ngStyle]=\"{\n position: hasRowActions() ? 'sticky' : null,\n 'right.px': hasRowActions() ? 0 : null,\n 'z-index': hasRowActions() ? ZIndex.STICKY_BODY_CELL : null\n }\">\n <button \n *ngIf=\"hasRowActions()\"\n class=\"settings-trigger\"\n (click)=\"openRowActionsDropdown($event, getRowData(rowIndex), rowIndex)\"\n type=\"button\" \n aria-label=\"Row actions\">\n \u22EF\n </button>\n </td>\n </ng-template>\n\n<ng-template #skeletonLoader>\n <div class=\"list-row\" *ngIf=\"(mergedConfig.tableSkeleton?.enabled | async)\">\n <div *ngFor=\"let i of skeletonColumns\" class=\"list-content\">\n <nile-skeleton-loader variant=\"text\" width=\"90%\" height=\"18\" *ngFor=\"let j of skeletonRows\"></nile-skeleton-loader>\n </div>\n </div>\n</ng-template>", styles: [".st-table{width:100%;overflow:auto;position:relative;height:100%;max-height:30rem;border-radius:4px;border:1px solid #E6E9EB}.st-table st-table-actions{position:sticky;right:0}.st-table.keyboard-navigation-enabled{cursor:pointer}.st-table.keyboard-navigation-enabled:focus{outline:none;box-shadow:0 0 0 2px #3b82f64d}.st-table.keyboard-navigation-enabled td.cell-focused{outline:2px solid #4299e1;outline-offset:-1px;position:relative;box-shadow:0 0 0 3px #3182ce1a}.st-table.keyboard-navigation-enabled td.cell-focused:focus{outline:2px solid #4299e1;outline-offset:-1px;box-shadow:0 0 0 3px #3182ce1a}.st-table.keyboard-navigation-enabled td.cell-focused:has(.st-cell.editing){box-shadow:0 0 0 4px #2563eb26}.st-table.virtual-scroll-enabled{overflow-x:visible;margin:0;border:1px solid #E6E9EB;border-collapse:separate;border-spacing:0}.st-table.virtual-scroll-enabled .st-scroll-viewport{position:relative;overflow-y:auto;overflow-x:auto;scroll-behavior:smooth}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-scroll-spacer{position:absolute;top:0;left:0;width:1px;pointer-events:none;z-index:-1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-positioner{position:absolute;top:0;left:0;right:0;will-change:transform}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element{position:relative;width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead{background-color:#fff;border-bottom:1px solid #E6E9EB}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead.sticky{position:sticky;top:0;z-index:3;background-color:#fff;will-change:top;backface-visibility:hidden}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead.sticky:after{content:\"\";position:absolute;bottom:-2px;left:0;right:0;height:2px;background:linear-gradient(to bottom,rgba(0,0,0,.1),transparent)}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th{padding:0;vertical-align:middle;position:relative;border:none;background-color:#fff;will-change:top;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-left{position:sticky;left:0;background-color:#fff;z-index:2;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-left:not(:has(~ th.sticky-left)){position:relative;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-left:not(:has(~ th.sticky-left)):after{content:\"\";position:absolute;right:-8px;top:0;bottom:0;width:8px;background:linear-gradient(to left,transparent,rgba(0,0,0,.08));pointer-events:none;z-index:1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-right{position:sticky;right:0;background-color:#fff;z-index:2;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-right-first:not(.settings-column){position:relative;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-right-first:not(.settings-column):before{content:\"\";position:absolute;left:-8px;top:0;bottom:0;width:8px;background:linear-gradient(to right,transparent,rgba(0,0,0,.08));pointer-events:none;z-index:1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.settings-column{width:2rem;text-align:center;font-weight:600;font-size:12px;position:sticky;right:0;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb;z-index:2}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.resizable .resize-handle{position:absolute;top:0;right:0;bottom:0;width:8px;cursor:col-resize;z-index:4;pointer-events:auto}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.resizable .resize-handle:after{content:\"\";position:absolute;top:50%;right:3px;transform:translateY(-50%);width:2px;height:20px;background-color:#cbd5e0;opacity:0;transition:opacity .2s}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.resizable .resize-handle:hover:after,.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.resizable .resize-handle:active:after{opacity:1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.row-number-header{text-align:center;font-weight:300;font-size:12px;background-color:#f8f8f8}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-left:not(:has(~ th.sticky-left)):after{content:\"\";position:absolute;right:-6px;top:0;bottom:-1px;width:5px;border-left:1px solid var(--borderColor);background:linear-gradient(90deg,rgba(0,0,0,.08) 0%,rgba(0,0,0,0) 100%)}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element thead tr th.sticky-right-first:not(.settings-column):before{content:\"\";position:absolute;left:-6px;top:0;bottom:-1px;width:5px;border-right:1px solid var(--borderColor);background:linear-gradient(90deg,rgba(0,0,0,0) 0%,rgba(0,0,0,.08) 100%)}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody{will-change:transform;position:relative;z-index:1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr{border-bottom:1px solid #E6E9EB;transition:background-color .15s;height:2rem;box-sizing:border-box}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr:last-child{border-bottom:none}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td{padding:0;vertical-align:middle;box-sizing:border-box;height:2rem;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb;border:none}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.align-center{text-align:center}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.align-right{text-align:right}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-left{position:sticky;z-index:2;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-left:not(:has(~ td.sticky-left)){position:relative;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-left:not(:has(~ td.sticky-left)):after{content:\"\";position:absolute;right:-8px;top:0;bottom:0;width:8px;background:linear-gradient(to left,transparent,rgba(0,0,0,.08));pointer-events:none;z-index:1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-right{position:sticky;z-index:2;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-right-first:not(.settings-column){position:relative;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-right-first:not(.settings-column):before{content:\"\";position:absolute;left:-8px;top:0;bottom:0;width:8px;background:linear-gradient(to right,transparent,rgba(0,0,0,.08));pointer-events:none;z-index:1}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.settings-column{width:2rem;text-align:center;position:sticky;right:0;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb;z-index:2}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.settings-column .settings-trigger{background:none;border:none;font-size:1rem;line-height:1;cursor:pointer;border-radius:4px;transition:all .15s ease}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.settings-column .settings-trigger:focus{outline:2px solid #4299e1;outline-offset:2px}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.row-number-cell{text-align:center;font-weight:300;font-size:12px;background-color:#f8f8f8}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-left:not(:has(~ td.sticky-left)):after{content:\"\";position:absolute;right:-6px;top:0;bottom:-1px;width:5px;border-left:1px solid var(--borderColor);background:linear-gradient(90deg,rgba(0,0,0,.08) 0%,rgba(0,0,0,0) 100%)}.st-table.virtual-scroll-enabled .st-scroll-viewport .st-table-element tbody tr td.sticky-right-first:before{content:\"\";position:absolute;left:-6px;top:0;bottom:-1px;width:5px;border-right:1px solid var(--borderColor);background:linear-gradient(90deg,rgba(0,0,0,0) 0%,rgba(0,0,0,.08) 100%)}.st-table:not(.virtual-scroll-enabled) .st-table-element{width:100%;height:100%;overflow:auto;border-collapse:separate;border-spacing:0;table-layout:fixed}.st-table:not(.virtual-scroll-enabled) .st-table-element thead{background-color:#fff;border-bottom:1px solid #E6E9EB}.st-table:not(.virtual-scroll-enabled) .st-table-element thead.sticky{position:sticky;top:0;z-index:3;background-color:#fff;will-change:top;backface-visibility:hidden}.st-table:not(.virtual-scroll-enabled) .st-table-element thead.sticky:after{content:\"\";position:absolute;bottom:-2px;left:0;right:0;height:2px;background:linear-gradient(to bottom,rgba(0,0,0,.1),transparent)}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th{padding:0;vertical-align:middle;position:relative;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb;border:none}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-left{position:sticky;left:0;background-color:#fff;z-index:2;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-left:not(:has(~ th.sticky-left)){position:relative;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-left:not(:has(~ th.sticky-left)):after{content:\"\";position:absolute;right:-8px;top:0;bottom:0;width:8px;background:linear-gradient(to left,transparent,rgba(0,0,0,.08));pointer-events:none;z-index:1}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-right{position:sticky;right:0;background-color:#fff;z-index:2;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-right-first:not(.settings-column){position:relative;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.sticky-right-first:not(.settings-column):before{content:\"\";position:absolute;left:-8px;top:0;bottom:0;width:8px;background:linear-gradient(to right,transparent,rgba(0,0,0,.08));pointer-events:none;z-index:1}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.settings-column{width:2rem;text-align:center;font-weight:600;font-size:12px;position:sticky;right:0;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb;z-index:2}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.resizable .resize-handle{position:absolute;top:0;right:0;bottom:0;width:8px;cursor:col-resize;z-index:4;pointer-events:auto}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.resizable .resize-handle:after{content:\"\";position:absolute;top:50%;right:3px;transform:translateY(-50%);width:2px;height:20px;background-color:#cbd5e0;opacity:0;transition:opacity .2s}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.resizable .resize-handle:hover:after,.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.resizable .resize-handle:active:after{opacity:1}.st-table:not(.virtual-scroll-enabled) .st-table-element thead tr th.row-number-header{text-align:center;font-weight:300;font-size:12px;background-color:#f8f8f8}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody{will-change:transform;position:relative;z-index:1}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr{height:2rem;box-sizing:border-box}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr:last-child{border-bottom:none}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td{padding:0;vertical-align:middle;height:2rem;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb;border:none}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.row-number-cell{text-align:center;font-weight:300;font-size:12px;background-color:#f8f8f8}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.align-center{text-align:center}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.align-right{text-align:right}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-left{position:sticky;z-index:2;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-left:not(:has(~ td.sticky-left)){position:relative;box-shadow:inset 0 -1px #e6e9eb,inset -1px 0 #e6e9eb}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-left:not(:has(~ td.sticky-left)):after{content:\"\";position:absolute;right:-8px;top:0;bottom:0;width:8px;background:linear-gradient(to left,transparent,rgba(0,0,0,.08));pointer-events:none;z-index:1}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-right{position:sticky;z-index:2;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-right-first:not(.settings-column){position:relative;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.sticky-right-first:not(.settings-column):before{content:\"\";position:absolute;left:-8px;top:0;bottom:0;width:8px;background:linear-gradient(to right,transparent,rgba(0,0,0,.08));pointer-events:none;z-index:1}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.settings-column{position:sticky;right:0;width:2rem;text-align:center;border-right:none;background-color:#fff;box-shadow:inset 0 -1px #e6e9eb,inset 1px 0 #e6e9eb;z-index:2}.st-table:not(.virtual-scroll-enabled) .st-table-element tbody tr td.settings-column .settings-trigger{background:none;border:none;font-size:1rem;line-height:1;cursor:pointer;border-radius:4px;transition:all .15s ease}.st-table:not(.virtual-scroll-enabled) .st-table-element .header-content{display:flex;height:2rem;align-items:center}.st-table:not(.virtual-scroll-enabled) .st-table-element .header-content .table-header-text{flex-grow:1;padding-left:4px}.st-table:not(.virtual-scroll-enabled) .st-table-element .settings-column>.header-content{display:flex;align-items:center;justify-content:center}.flex-center{display:flex;align-items:center;justify-content:center}.list-row{padding:1rem}.list-row .list-content{display:flex;justify-content:space-evenly;gap:4px;margin-bottom:1rem}\n"] }]
8757
8850
  }], ctorParameters: function () { return [{ type: JsonSchemaValidatorService }, { type: ValidationLoggerService }, { type: VirtualScrollService }]; }, propDecorators: { tableConfig: [{
8758
8851
  type: Input
8759
8852
  }], data: [{
@@ -9736,17 +9829,28 @@ class StWorkbookComponent {
9736
9829
  }
9737
9830
  ngOnChanges(changes) {
9738
9831
  if (changes['config']) {
9739
- this.initializeComponent();
9832
+ const isFirstChange = changes['config'].firstChange;
9833
+ this.initializeComponent(isFirstChange);
9740
9834
  }
9741
9835
  }
9742
9836
  /**
9743
9837
  * Initialize component state
9838
+ * @param isFirstInit Whether this is the first initialization (true) or a config update (false)
9744
9839
  */
9745
- initializeComponent() {
9840
+ initializeComponent(isFirstInit = true) {
9746
9841
  if (!this.config)
9747
9842
  return;
9748
9843
  this.sheets = [...this.config.sheets];
9749
- this.activeSheetIndex = this.config.initialActiveSheetIndex || 0;
9844
+ // Only reset activeSheetIndex on first init, preserve it on config updates
9845
+ if (isFirstInit) {
9846
+ this.activeSheetIndex = this.config.initialActiveSheetIndex || 0;
9847
+ }
9848
+ else {
9849
+ // Ensure activeSheetIndex is still valid after config change
9850
+ if (this.activeSheetIndex >= this.sheets.length) {
9851
+ this.activeSheetIndex = Math.max(0, this.sheets.length - 1);
9852
+ }
9853
+ }
9750
9854
  if (!this.state) {
9751
9855
  this.internalState = new WorkbookState({
9752
9856
  id: this.config.id,
@@ -9787,6 +9891,8 @@ class StWorkbookComponent {
9787
9891
  // Update selected sheet reference for data subscriptions
9788
9892
  this.selectedSheet = sheet;
9789
9893
  this.selectedSheetIndex = this.activeSheetIndex;
9894
+ // Get sheet data
9895
+ const sheetData = this.sheetsData.getValue()?.get(sheet.id) || [];
9790
9896
  // Create a fresh TableState for this sheet (lazy initialization)
9791
9897
  // This ensures the table reinitializes with new config and state
9792
9898
  const tableState = new TableState(sheet.tableConfig.columns, {
@@ -9796,7 +9902,7 @@ class StWorkbookComponent {
9796
9902
  // Update current table properties (triggers Angular change detection)
9797
9903
  // The st-table component will detect these changes and reinitialize
9798
9904
  this.currentTableConfig = { ...sheet.tableConfig };
9799
- this.currentTableData$.next(this.sheetsData.getValue()?.get(sheet.id) || []);
9905
+ this.currentTableData$.next(sheetData);
9800
9906
  this.currentTableState = tableState;
9801
9907
  // Update component key to force DOM re-creation (true lazy loading)
9802
9908
  // This ensures complete reinitialization of st-table component
@@ -9810,6 +9916,12 @@ class StWorkbookComponent {
9810
9916
  onAddSheet() {
9811
9917
  this.addSheet.emit();
9812
9918
  }
9919
+ /**
9920
+ * TrackBy function to force component recreation when key changes
9921
+ */
9922
+ trackByKey(index, key) {
9923
+ return key;
9924
+ }
9813
9925
  /**
9814
9926
  * Check if can add more sheets
9815
9927
  */
@@ -10068,10 +10180,10 @@ class StWorkbookComponent {
10068
10180
  }
10069
10181
  }
10070
10182
  StWorkbookComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StWorkbookComponent, deps: [{ token: AutosaveService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
10071
- StWorkbookComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StWorkbookComponent, selector: "st-workbook", inputs: { config: "config", sheetsData: "sheetsData", state: "state" }, outputs: { sheetChanged: "sheetChanged", addSheet: "addSheet", sheetTabAction: "sheetTabAction", workbookAction: "workbookAction", cellChange: "cellChange", cellSave: "cellSave", tableStateChange: "tableStateChange", fullscreenToggle: "fullscreenToggle" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"workbook-container\" [class.fullscreen]=\"isFullscreen$ | async\">\n <nile-tab-group [activeIndex]=\"activeSheetIndex\">\n \n <!-- Sheet Tabs (one per sheet) -->\n <nile-tab *ngFor=\"let sheet of sheets; let i = index\"\n slot=\"nav\" \n panel=\"shared-panel\"\n [class.active]=\"i === activeSheetIndex\"\n (click)=\"onTabChange(i)\">\n <div class=\"sheet-tab-content\">\n <span class=\"sheet-name\">{{ sheet.name }}</span>\n \n <!-- Tab actions dropdown button -->\n <button class=\"tab-actions-button\"\n (click)=\"openTabActions($event, sheet, i)\"\n *ngIf=\"hasTabActions(sheet)\">\n <nile-icon name=\"arrowdown\" size=\"14\"></nile-icon>\n </button>\n </div>\n </nile-tab>\n \n <!-- Toolbar Tab (for workbook controls) -->\n <nile-tab slot=\"nav\" \n panel=\"shared-panel\"\n class=\"workbook-toolbar-tab\"\n [disabled]=\"true\">\n <div class=\"workbook-toolbar-content\">\n <!-- Autosave Indicator -->\n <div class=\"autosave-indicator\" *ngIf=\"autosaveEnabled\">\n <nile-icon \n *ngIf=\"!isSaving && lastSaveTime\" \n name=\"save\" \n size=\"14\"\n [title]=\"'Saved at ' + (lastSaveTime | date:'HH:mm:ss')\">\n </nile-icon>\n <nile-icon \n *ngIf=\"isSaving\" \n name=\"loader\" \n size=\"14\"\n title=\"Saving...\">\n </nile-icon>\n </div>\n\n <!-- Workbook Actions Dropdown -->\n <button class=\"workbook-actions-button\"\n *ngIf=\"config.workbookActions && config.workbookActions.length > 0\"\n (click)=\"toggleWorkbookActions($event)\"\n title=\"Workbook Actions\">\n <nile-icon name=\"settings\"></nile-icon>\n </button>\n \n <!-- Add Sheet Button -->\n <button class=\"add-sheet-button\"\n *ngIf=\"canAddSheet\"\n (click)=\"onAddSheet()\"\n title=\"Add Sheet\">\n <nile-icon name=\"plus\"></nile-icon>\n </button>\n \n <!-- Fullscreen Button -->\n <button class=\"fullscreen-button\"\n *ngIf=\"config.display?.allowFullscreen !== false\"\n (click)=\"toggleFullscreen()\"\n [title]=\"(isFullscreen$ | async) ? 'Exit Fullscreen' : 'Fullscreen'\">\n <nile-icon [name]=\"(isFullscreen$ | async) ? 'collapse' : 'expand-06'\"></nile-icon>\n </button>\n </div>\n </nile-tab>\n \n <!-- Single Shared Tab Panel -->\n <nile-tab-panel name=\"shared-panel\">\n <!-- Lazy loading strategy: table is destroyed and recreated with new config/state when sheet changes -->\n <!-- The componentKey ensures complete reinitialization of st-table for each sheet -->\n <st-table *ngIf=\"currentTableConfig && currentTableState && tableComponentKey\"\n [attr.data-sheet-key]=\"tableComponentKey\"\n [tableConfig]=\"currentTableConfig\"\n [data$]=\"currentTableData$\"\n [tableState]=\"currentTableState\"\n (cellChange)=\"onCellChange($event)\"\n (cellSave)=\"onCellSave($event)\"\n (stateChange)=\"onTableStateChange($event)\">\n </st-table>\n </nile-tab-panel>\n \n </nile-tab-group>\n</div>\n\n<!-- Tab Actions Dropdown -->\n<div class=\"tab-actions-dropdown\" \n *ngIf=\"tabActionsOpen\"\n [ngStyle]=\"tabActionsPosition\">\n <div class=\"dropdown-backdrop\" (click)=\"closeTabActions()\"></div>\n <div class=\"dropdown-menu\">\n <nile-menu>\n <nile-menu-item *ngFor=\"let action of selectedSheet?.tabActions\" \n (click)=\"onTabActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" slot=\"prefix\" size=\"14\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </nile-menu>\n </div>\n</div>\n\n<!-- Workbook Actions Dropdown -->\n<div class=\"workbook-actions-dropdown\"\n *ngIf=\"workbookActionsOpen\"\n [ngStyle]=\"workbookActionsPosition\">\n <div class=\"dropdown-backdrop\" (click)=\"closeWorkbookActions()\"></div>\n <div class=\"dropdown-menu\">\n <nile-menu>\n <nile-menu-item *ngFor=\"let action of visibleWorkbookActions\"\n [class.disabled]=\"isActionDisabled(action)\"\n (click)=\"onWorkbookActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" slot=\"prefix\" size=\"14\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </nile-menu>\n </div>\n</div>\n\n<!-- Fullscreen Backdrop -->\n<div class=\"fullscreen-backdrop\" \n *ngIf=\"isFullscreen$ | async\"\n (click)=\"toggleFullscreen()\">\n</div>\n\n", styles: ["@import\"@aquera/nile/lib/styles/variables.css\";:host{display:block;width:100%;height:100%}.workbook-container{display:flex;flex-direction:column;height:100%;background:white;border:1px solid var(--nile-color-neutral-200);border-radius:4px;overflow:hidden}.workbook-container.fullscreen{position:fixed;inset:0;z-index:2000;border:none;border-radius:0}.workbook-container nile-tab-group{height:100%;display:flex;flex-direction:column}.sheet-tab-content{display:flex;align-items:center;gap:8px;padding:0 4px}.sheet-tab-content .sheet-name{font-size:12px;font-weight:500;font-family:var(--nile-font-family-sans-serif);color:#000}.sheet-tab-content .tab-actions-button{width:20px;height:20px;padding:0;background:transparent;border:none;cursor:pointer}.workbook-toolbar-tab{margin-left:auto!important;pointer-events:auto!important;border-left:1px solid var(--nile-color-neutral-200)}.workbook-toolbar-tab .workbook-toolbar-content{display:flex;gap:4px;align-items:center;padding:0 8px}.workbook-toolbar-tab .workbook-toolbar-content .autosave-indicator{display:flex;align-items:center;margin-right:8px;color:var(--nile-color-success-500)}.workbook-toolbar-tab button{display:flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;background:transparent;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s;color:var(--nile-color-neutral-600)}.workbook-toolbar-tab button:hover{background-color:var(--nile-color-neutral-100);color:var(--nile-color-neutral-900)}.workbook-toolbar-tab button:active{background-color:var(--nile-color-neutral-200)}.workbook-toolbar-tab button nile-icon{font-size:16px}.tab-actions-dropdown{position:fixed;z-index:1001}.tab-actions-dropdown .dropdown-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:transparent;z-index:1000}.tab-actions-dropdown .dropdown-menu{position:relative;min-width:180px;background:white;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:1001;overflow:hidden}.tab-actions-dropdown .dropdown-menu nile-menu{display:block}.workbook-actions-dropdown{position:fixed;z-index:1001}.workbook-actions-dropdown .dropdown-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:transparent;z-index:1000}.workbook-actions-dropdown .dropdown-menu{position:relative;min-width:200px;background:white;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:1001;overflow:hidden}.workbook-actions-dropdown .dropdown-menu nile-menu{display:block}.fullscreen-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:1999;cursor:pointer}nile-tab-group::part(nav){background-color:#fafafa}nile-tab-group::part(active-tab-indicator-path){background:none}nile-tab-group::part(active-tab-indicator){border-bottom:none}nile-tab-group::part(tabs){gap:0}nile-tab-group nile-tab{border:1px solid #e0e0e0;border-top-left-radius:6px;border-top-right-radius:6px}nile-tab-group nile-tab::part(base){padding-left:.5rem;padding-top:.5rem;padding-bottom:.5rem;border-bottom-left-radius:0;border-bottom-right-radius:0}nile-tab-group nile-tab.active{background-color:#fff;color:#000}nile-tab-group nile-tab-panel::part(base){padding:0;max-height:30rem}\n"], components: [{ type: StTableComponent, selector: "st-table", inputs: ["tableConfig", "data", "data$", "tableState", "enableSorting", "enableFiltering", "validateConfig"], outputs: ["stateChange", "dataChange", "cellEdit", "cellSave", "cellCancel", "cellChange", "columnResized", "columnMoved", "configValidationErrors", "columnAdded", "rowAction", "validationStateChange"] }], directives: [{ type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], pipes: { "async": i1.AsyncPipe, "date": i1.DatePipe } });
10183
+ StWorkbookComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StWorkbookComponent, selector: "st-workbook", inputs: { config: "config", sheetsData: "sheetsData", state: "state" }, outputs: { sheetChanged: "sheetChanged", addSheet: "addSheet", sheetTabAction: "sheetTabAction", workbookAction: "workbookAction", cellChange: "cellChange", cellSave: "cellSave", tableStateChange: "tableStateChange", fullscreenToggle: "fullscreenToggle" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"workbook-container\" [class.fullscreen]=\"isFullscreen$ | async\">\n <nile-tab-group [activeIndex]=\"activeSheetIndex\">\n \n <!-- Sheet Tabs (one per sheet) -->\n <nile-tab *ngFor=\"let sheet of sheets; let i = index\"\n slot=\"nav\" \n panel=\"shared-panel\"\n [class.active]=\"i === activeSheetIndex\"\n (click)=\"onTabChange(i)\">\n <div class=\"sheet-tab-content\">\n <span class=\"sheet-name\">{{ sheet.name }}</span>\n \n <!-- Tab actions dropdown button -->\n <button class=\"tab-actions-button\"\n (click)=\"openTabActions($event, sheet, i)\"\n *ngIf=\"hasTabActions(sheet)\">\n <nile-icon name=\"arrowdown\" size=\"14\"></nile-icon>\n </button>\n </div>\n </nile-tab>\n \n <!-- Toolbar Tab (for workbook controls) -->\n <nile-tab slot=\"nav\" \n panel=\"shared-panel\"\n class=\"workbook-toolbar-tab\"\n [disabled]=\"true\">\n <div class=\"workbook-toolbar-content\">\n <!-- Autosave Indicator -->\n <div class=\"autosave-indicator\" *ngIf=\"autosaveEnabled\">\n <nile-icon \n *ngIf=\"!isSaving && lastSaveTime\" \n name=\"save\" \n size=\"14\"\n [title]=\"'Saved at ' + (lastSaveTime | date:'HH:mm:ss')\">\n </nile-icon>\n <nile-icon \n *ngIf=\"isSaving\" \n name=\"loader\" \n size=\"14\"\n title=\"Saving...\">\n </nile-icon>\n </div>\n\n <!-- Workbook Actions Dropdown -->\n <button class=\"workbook-actions-button\"\n *ngIf=\"config.workbookActions && config.workbookActions.length > 0\"\n (click)=\"toggleWorkbookActions($event)\"\n title=\"Workbook Actions\">\n <nile-icon name=\"settings\"></nile-icon>\n </button>\n \n <!-- Add Sheet Button -->\n <button class=\"add-sheet-button\"\n *ngIf=\"canAddSheet\"\n (click)=\"onAddSheet()\"\n title=\"Add Sheet\">\n <nile-icon name=\"plus\"></nile-icon>\n </button>\n \n <!-- Fullscreen Button -->\n <button class=\"fullscreen-button\"\n *ngIf=\"config.display?.allowFullscreen !== false\"\n (click)=\"toggleFullscreen()\"\n [title]=\"(isFullscreen$ | async) ? 'Exit Fullscreen' : 'Fullscreen'\">\n <nile-icon [name]=\"(isFullscreen$ | async) ? 'collapse' : 'expand-06'\"></nile-icon>\n </button>\n </div>\n </nile-tab>\n \n <!-- Single Shared Tab Panel -->\n <nile-tab-panel name=\"shared-panel\">\n <!-- Lazy loading strategy: table is destroyed and recreated with new config/state when sheet changes -->\n <!-- Using ngFor with trackBy to force complete reinitialization when tableComponentKey changes -->\n <ng-container *ngFor=\"let key of [tableComponentKey]; trackBy: trackByKey\">\n <st-table *ngIf=\"currentTableConfig && currentTableState\"\n [attr.data-sheet-key]=\"key\"\n [tableConfig]=\"currentTableConfig\"\n [data$]=\"currentTableData$\"\n [tableState]=\"currentTableState\"\n (cellChange)=\"onCellChange($event)\"\n (cellSave)=\"onCellSave($event)\"\n (stateChange)=\"onTableStateChange($event)\">\n </st-table>\n </ng-container>\n </nile-tab-panel>\n \n </nile-tab-group>\n</div>\n\n<!-- Tab Actions Dropdown -->\n<div class=\"tab-actions-dropdown\" \n *ngIf=\"tabActionsOpen\"\n [ngStyle]=\"tabActionsPosition\">\n <div class=\"dropdown-backdrop\" (click)=\"closeTabActions()\"></div>\n <div class=\"dropdown-menu\">\n <nile-menu>\n <nile-menu-item *ngFor=\"let action of selectedSheet?.tabActions\" \n (click)=\"onTabActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" slot=\"prefix\" size=\"14\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </nile-menu>\n </div>\n</div>\n\n<!-- Workbook Actions Dropdown -->\n<div class=\"workbook-actions-dropdown\"\n *ngIf=\"workbookActionsOpen\"\n [ngStyle]=\"workbookActionsPosition\">\n <div class=\"dropdown-backdrop\" (click)=\"closeWorkbookActions()\"></div>\n <div class=\"dropdown-menu\">\n <nile-menu>\n <nile-menu-item *ngFor=\"let action of visibleWorkbookActions\"\n [class.disabled]=\"isActionDisabled(action)\"\n (click)=\"onWorkbookActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" slot=\"prefix\" size=\"14\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </nile-menu>\n </div>\n</div>\n\n<!-- Fullscreen Backdrop -->\n<div class=\"fullscreen-backdrop\" \n *ngIf=\"isFullscreen$ | async\"\n (click)=\"toggleFullscreen()\">\n</div>\n\n", styles: ["@import\"@aquera/nile/lib/styles/variables.css\";:host{display:block;width:100%;height:100%}.workbook-container{display:flex;flex-direction:column;height:100%;background:white;border:1px solid var(--nile-color-neutral-200);border-radius:4px;overflow:hidden}.workbook-container.fullscreen{position:fixed;inset:0;z-index:2000;border:none;border-radius:0}.workbook-container nile-tab-group{height:100%;display:flex;flex-direction:column}.sheet-tab-content{display:flex;align-items:center;gap:8px;padding:0 4px}.sheet-tab-content .sheet-name{font-size:12px;font-weight:500;font-family:var(--nile-font-family-sans-serif);color:#000}.sheet-tab-content .tab-actions-button{width:20px;height:20px;padding:0;background:transparent;border:none;cursor:pointer}.workbook-toolbar-tab{margin-left:auto!important;pointer-events:auto!important;border-left:1px solid var(--nile-color-neutral-200)}.workbook-toolbar-tab .workbook-toolbar-content{display:flex;gap:4px;align-items:center;padding:0 8px}.workbook-toolbar-tab .workbook-toolbar-content .autosave-indicator{display:flex;align-items:center;margin-right:8px;color:var(--nile-color-success-500)}.workbook-toolbar-tab button{display:flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;background:transparent;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s;color:var(--nile-color-neutral-600)}.workbook-toolbar-tab button:hover{background-color:var(--nile-color-neutral-100);color:var(--nile-color-neutral-900)}.workbook-toolbar-tab button:active{background-color:var(--nile-color-neutral-200)}.workbook-toolbar-tab button nile-icon{font-size:16px}.tab-actions-dropdown{position:fixed;z-index:1001}.tab-actions-dropdown .dropdown-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:transparent;z-index:1000}.tab-actions-dropdown .dropdown-menu{position:relative;min-width:180px;background:white;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:1001;overflow:hidden}.tab-actions-dropdown .dropdown-menu nile-menu{display:block}.workbook-actions-dropdown{position:fixed;z-index:1001}.workbook-actions-dropdown .dropdown-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:transparent;z-index:1000}.workbook-actions-dropdown .dropdown-menu{position:relative;min-width:200px;background:white;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:1001;overflow:hidden}.workbook-actions-dropdown .dropdown-menu nile-menu{display:block}.fullscreen-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:1999;cursor:pointer}nile-tab-group::part(nav){background-color:#fafafa}nile-tab-group::part(active-tab-indicator-path){background:none}nile-tab-group::part(active-tab-indicator){border-bottom:none}nile-tab-group::part(tabs){gap:0}nile-tab-group nile-tab{border:1px solid #e0e0e0;border-top-left-radius:6px;border-top-right-radius:6px}nile-tab-group nile-tab::part(base){padding-left:.5rem;padding-top:.5rem;padding-bottom:.5rem;border-bottom-left-radius:0;border-bottom-right-radius:0}nile-tab-group nile-tab.active{background-color:#fff;color:#000}nile-tab-group nile-tab-panel::part(base){padding:0;max-height:30rem}\n"], components: [{ type: StTableComponent, selector: "st-table", inputs: ["tableConfig", "data", "data$", "tableState", "enableSorting", "enableFiltering", "validateConfig"], outputs: ["stateChange", "dataChange", "cellEdit", "cellSave", "cellCancel", "cellChange", "columnResized", "columnMoved", "configValidationErrors", "columnAdded", "rowAction", "validationStateChange"] }], directives: [{ type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], pipes: { "async": i1.AsyncPipe, "date": i1.DatePipe } });
10072
10184
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StWorkbookComponent, decorators: [{
10073
10185
  type: Component,
10074
- args: [{ selector: 'st-workbook', template: "<div class=\"workbook-container\" [class.fullscreen]=\"isFullscreen$ | async\">\n <nile-tab-group [activeIndex]=\"activeSheetIndex\">\n \n <!-- Sheet Tabs (one per sheet) -->\n <nile-tab *ngFor=\"let sheet of sheets; let i = index\"\n slot=\"nav\" \n panel=\"shared-panel\"\n [class.active]=\"i === activeSheetIndex\"\n (click)=\"onTabChange(i)\">\n <div class=\"sheet-tab-content\">\n <span class=\"sheet-name\">{{ sheet.name }}</span>\n \n <!-- Tab actions dropdown button -->\n <button class=\"tab-actions-button\"\n (click)=\"openTabActions($event, sheet, i)\"\n *ngIf=\"hasTabActions(sheet)\">\n <nile-icon name=\"arrowdown\" size=\"14\"></nile-icon>\n </button>\n </div>\n </nile-tab>\n \n <!-- Toolbar Tab (for workbook controls) -->\n <nile-tab slot=\"nav\" \n panel=\"shared-panel\"\n class=\"workbook-toolbar-tab\"\n [disabled]=\"true\">\n <div class=\"workbook-toolbar-content\">\n <!-- Autosave Indicator -->\n <div class=\"autosave-indicator\" *ngIf=\"autosaveEnabled\">\n <nile-icon \n *ngIf=\"!isSaving && lastSaveTime\" \n name=\"save\" \n size=\"14\"\n [title]=\"'Saved at ' + (lastSaveTime | date:'HH:mm:ss')\">\n </nile-icon>\n <nile-icon \n *ngIf=\"isSaving\" \n name=\"loader\" \n size=\"14\"\n title=\"Saving...\">\n </nile-icon>\n </div>\n\n <!-- Workbook Actions Dropdown -->\n <button class=\"workbook-actions-button\"\n *ngIf=\"config.workbookActions && config.workbookActions.length > 0\"\n (click)=\"toggleWorkbookActions($event)\"\n title=\"Workbook Actions\">\n <nile-icon name=\"settings\"></nile-icon>\n </button>\n \n <!-- Add Sheet Button -->\n <button class=\"add-sheet-button\"\n *ngIf=\"canAddSheet\"\n (click)=\"onAddSheet()\"\n title=\"Add Sheet\">\n <nile-icon name=\"plus\"></nile-icon>\n </button>\n \n <!-- Fullscreen Button -->\n <button class=\"fullscreen-button\"\n *ngIf=\"config.display?.allowFullscreen !== false\"\n (click)=\"toggleFullscreen()\"\n [title]=\"(isFullscreen$ | async) ? 'Exit Fullscreen' : 'Fullscreen'\">\n <nile-icon [name]=\"(isFullscreen$ | async) ? 'collapse' : 'expand-06'\"></nile-icon>\n </button>\n </div>\n </nile-tab>\n \n <!-- Single Shared Tab Panel -->\n <nile-tab-panel name=\"shared-panel\">\n <!-- Lazy loading strategy: table is destroyed and recreated with new config/state when sheet changes -->\n <!-- The componentKey ensures complete reinitialization of st-table for each sheet -->\n <st-table *ngIf=\"currentTableConfig && currentTableState && tableComponentKey\"\n [attr.data-sheet-key]=\"tableComponentKey\"\n [tableConfig]=\"currentTableConfig\"\n [data$]=\"currentTableData$\"\n [tableState]=\"currentTableState\"\n (cellChange)=\"onCellChange($event)\"\n (cellSave)=\"onCellSave($event)\"\n (stateChange)=\"onTableStateChange($event)\">\n </st-table>\n </nile-tab-panel>\n \n </nile-tab-group>\n</div>\n\n<!-- Tab Actions Dropdown -->\n<div class=\"tab-actions-dropdown\" \n *ngIf=\"tabActionsOpen\"\n [ngStyle]=\"tabActionsPosition\">\n <div class=\"dropdown-backdrop\" (click)=\"closeTabActions()\"></div>\n <div class=\"dropdown-menu\">\n <nile-menu>\n <nile-menu-item *ngFor=\"let action of selectedSheet?.tabActions\" \n (click)=\"onTabActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" slot=\"prefix\" size=\"14\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </nile-menu>\n </div>\n</div>\n\n<!-- Workbook Actions Dropdown -->\n<div class=\"workbook-actions-dropdown\"\n *ngIf=\"workbookActionsOpen\"\n [ngStyle]=\"workbookActionsPosition\">\n <div class=\"dropdown-backdrop\" (click)=\"closeWorkbookActions()\"></div>\n <div class=\"dropdown-menu\">\n <nile-menu>\n <nile-menu-item *ngFor=\"let action of visibleWorkbookActions\"\n [class.disabled]=\"isActionDisabled(action)\"\n (click)=\"onWorkbookActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" slot=\"prefix\" size=\"14\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </nile-menu>\n </div>\n</div>\n\n<!-- Fullscreen Backdrop -->\n<div class=\"fullscreen-backdrop\" \n *ngIf=\"isFullscreen$ | async\"\n (click)=\"toggleFullscreen()\">\n</div>\n\n", styles: ["@import\"@aquera/nile/lib/styles/variables.css\";:host{display:block;width:100%;height:100%}.workbook-container{display:flex;flex-direction:column;height:100%;background:white;border:1px solid var(--nile-color-neutral-200);border-radius:4px;overflow:hidden}.workbook-container.fullscreen{position:fixed;inset:0;z-index:2000;border:none;border-radius:0}.workbook-container nile-tab-group{height:100%;display:flex;flex-direction:column}.sheet-tab-content{display:flex;align-items:center;gap:8px;padding:0 4px}.sheet-tab-content .sheet-name{font-size:12px;font-weight:500;font-family:var(--nile-font-family-sans-serif);color:#000}.sheet-tab-content .tab-actions-button{width:20px;height:20px;padding:0;background:transparent;border:none;cursor:pointer}.workbook-toolbar-tab{margin-left:auto!important;pointer-events:auto!important;border-left:1px solid var(--nile-color-neutral-200)}.workbook-toolbar-tab .workbook-toolbar-content{display:flex;gap:4px;align-items:center;padding:0 8px}.workbook-toolbar-tab .workbook-toolbar-content .autosave-indicator{display:flex;align-items:center;margin-right:8px;color:var(--nile-color-success-500)}.workbook-toolbar-tab button{display:flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;background:transparent;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s;color:var(--nile-color-neutral-600)}.workbook-toolbar-tab button:hover{background-color:var(--nile-color-neutral-100);color:var(--nile-color-neutral-900)}.workbook-toolbar-tab button:active{background-color:var(--nile-color-neutral-200)}.workbook-toolbar-tab button nile-icon{font-size:16px}.tab-actions-dropdown{position:fixed;z-index:1001}.tab-actions-dropdown .dropdown-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:transparent;z-index:1000}.tab-actions-dropdown .dropdown-menu{position:relative;min-width:180px;background:white;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:1001;overflow:hidden}.tab-actions-dropdown .dropdown-menu nile-menu{display:block}.workbook-actions-dropdown{position:fixed;z-index:1001}.workbook-actions-dropdown .dropdown-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:transparent;z-index:1000}.workbook-actions-dropdown .dropdown-menu{position:relative;min-width:200px;background:white;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:1001;overflow:hidden}.workbook-actions-dropdown .dropdown-menu nile-menu{display:block}.fullscreen-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:1999;cursor:pointer}nile-tab-group::part(nav){background-color:#fafafa}nile-tab-group::part(active-tab-indicator-path){background:none}nile-tab-group::part(active-tab-indicator){border-bottom:none}nile-tab-group::part(tabs){gap:0}nile-tab-group nile-tab{border:1px solid #e0e0e0;border-top-left-radius:6px;border-top-right-radius:6px}nile-tab-group nile-tab::part(base){padding-left:.5rem;padding-top:.5rem;padding-bottom:.5rem;border-bottom-left-radius:0;border-bottom-right-radius:0}nile-tab-group nile-tab.active{background-color:#fff;color:#000}nile-tab-group nile-tab-panel::part(base){padding:0;max-height:30rem}\n"] }]
10186
+ args: [{ selector: 'st-workbook', template: "<div class=\"workbook-container\" [class.fullscreen]=\"isFullscreen$ | async\">\n <nile-tab-group [activeIndex]=\"activeSheetIndex\">\n \n <!-- Sheet Tabs (one per sheet) -->\n <nile-tab *ngFor=\"let sheet of sheets; let i = index\"\n slot=\"nav\" \n panel=\"shared-panel\"\n [class.active]=\"i === activeSheetIndex\"\n (click)=\"onTabChange(i)\">\n <div class=\"sheet-tab-content\">\n <span class=\"sheet-name\">{{ sheet.name }}</span>\n \n <!-- Tab actions dropdown button -->\n <button class=\"tab-actions-button\"\n (click)=\"openTabActions($event, sheet, i)\"\n *ngIf=\"hasTabActions(sheet)\">\n <nile-icon name=\"arrowdown\" size=\"14\"></nile-icon>\n </button>\n </div>\n </nile-tab>\n \n <!-- Toolbar Tab (for workbook controls) -->\n <nile-tab slot=\"nav\" \n panel=\"shared-panel\"\n class=\"workbook-toolbar-tab\"\n [disabled]=\"true\">\n <div class=\"workbook-toolbar-content\">\n <!-- Autosave Indicator -->\n <div class=\"autosave-indicator\" *ngIf=\"autosaveEnabled\">\n <nile-icon \n *ngIf=\"!isSaving && lastSaveTime\" \n name=\"save\" \n size=\"14\"\n [title]=\"'Saved at ' + (lastSaveTime | date:'HH:mm:ss')\">\n </nile-icon>\n <nile-icon \n *ngIf=\"isSaving\" \n name=\"loader\" \n size=\"14\"\n title=\"Saving...\">\n </nile-icon>\n </div>\n\n <!-- Workbook Actions Dropdown -->\n <button class=\"workbook-actions-button\"\n *ngIf=\"config.workbookActions && config.workbookActions.length > 0\"\n (click)=\"toggleWorkbookActions($event)\"\n title=\"Workbook Actions\">\n <nile-icon name=\"settings\"></nile-icon>\n </button>\n \n <!-- Add Sheet Button -->\n <button class=\"add-sheet-button\"\n *ngIf=\"canAddSheet\"\n (click)=\"onAddSheet()\"\n title=\"Add Sheet\">\n <nile-icon name=\"plus\"></nile-icon>\n </button>\n \n <!-- Fullscreen Button -->\n <button class=\"fullscreen-button\"\n *ngIf=\"config.display?.allowFullscreen !== false\"\n (click)=\"toggleFullscreen()\"\n [title]=\"(isFullscreen$ | async) ? 'Exit Fullscreen' : 'Fullscreen'\">\n <nile-icon [name]=\"(isFullscreen$ | async) ? 'collapse' : 'expand-06'\"></nile-icon>\n </button>\n </div>\n </nile-tab>\n \n <!-- Single Shared Tab Panel -->\n <nile-tab-panel name=\"shared-panel\">\n <!-- Lazy loading strategy: table is destroyed and recreated with new config/state when sheet changes -->\n <!-- Using ngFor with trackBy to force complete reinitialization when tableComponentKey changes -->\n <ng-container *ngFor=\"let key of [tableComponentKey]; trackBy: trackByKey\">\n <st-table *ngIf=\"currentTableConfig && currentTableState\"\n [attr.data-sheet-key]=\"key\"\n [tableConfig]=\"currentTableConfig\"\n [data$]=\"currentTableData$\"\n [tableState]=\"currentTableState\"\n (cellChange)=\"onCellChange($event)\"\n (cellSave)=\"onCellSave($event)\"\n (stateChange)=\"onTableStateChange($event)\">\n </st-table>\n </ng-container>\n </nile-tab-panel>\n \n </nile-tab-group>\n</div>\n\n<!-- Tab Actions Dropdown -->\n<div class=\"tab-actions-dropdown\" \n *ngIf=\"tabActionsOpen\"\n [ngStyle]=\"tabActionsPosition\">\n <div class=\"dropdown-backdrop\" (click)=\"closeTabActions()\"></div>\n <div class=\"dropdown-menu\">\n <nile-menu>\n <nile-menu-item *ngFor=\"let action of selectedSheet?.tabActions\" \n (click)=\"onTabActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" slot=\"prefix\" size=\"14\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </nile-menu>\n </div>\n</div>\n\n<!-- Workbook Actions Dropdown -->\n<div class=\"workbook-actions-dropdown\"\n *ngIf=\"workbookActionsOpen\"\n [ngStyle]=\"workbookActionsPosition\">\n <div class=\"dropdown-backdrop\" (click)=\"closeWorkbookActions()\"></div>\n <div class=\"dropdown-menu\">\n <nile-menu>\n <nile-menu-item *ngFor=\"let action of visibleWorkbookActions\"\n [class.disabled]=\"isActionDisabled(action)\"\n (click)=\"onWorkbookActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" slot=\"prefix\" size=\"14\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </nile-menu>\n </div>\n</div>\n\n<!-- Fullscreen Backdrop -->\n<div class=\"fullscreen-backdrop\" \n *ngIf=\"isFullscreen$ | async\"\n (click)=\"toggleFullscreen()\">\n</div>\n\n", styles: ["@import\"@aquera/nile/lib/styles/variables.css\";:host{display:block;width:100%;height:100%}.workbook-container{display:flex;flex-direction:column;height:100%;background:white;border:1px solid var(--nile-color-neutral-200);border-radius:4px;overflow:hidden}.workbook-container.fullscreen{position:fixed;inset:0;z-index:2000;border:none;border-radius:0}.workbook-container nile-tab-group{height:100%;display:flex;flex-direction:column}.sheet-tab-content{display:flex;align-items:center;gap:8px;padding:0 4px}.sheet-tab-content .sheet-name{font-size:12px;font-weight:500;font-family:var(--nile-font-family-sans-serif);color:#000}.sheet-tab-content .tab-actions-button{width:20px;height:20px;padding:0;background:transparent;border:none;cursor:pointer}.workbook-toolbar-tab{margin-left:auto!important;pointer-events:auto!important;border-left:1px solid var(--nile-color-neutral-200)}.workbook-toolbar-tab .workbook-toolbar-content{display:flex;gap:4px;align-items:center;padding:0 8px}.workbook-toolbar-tab .workbook-toolbar-content .autosave-indicator{display:flex;align-items:center;margin-right:8px;color:var(--nile-color-success-500)}.workbook-toolbar-tab button{display:flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;background:transparent;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s;color:var(--nile-color-neutral-600)}.workbook-toolbar-tab button:hover{background-color:var(--nile-color-neutral-100);color:var(--nile-color-neutral-900)}.workbook-toolbar-tab button:active{background-color:var(--nile-color-neutral-200)}.workbook-toolbar-tab button nile-icon{font-size:16px}.tab-actions-dropdown{position:fixed;z-index:1001}.tab-actions-dropdown .dropdown-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:transparent;z-index:1000}.tab-actions-dropdown .dropdown-menu{position:relative;min-width:180px;background:white;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:1001;overflow:hidden}.tab-actions-dropdown .dropdown-menu nile-menu{display:block}.workbook-actions-dropdown{position:fixed;z-index:1001}.workbook-actions-dropdown .dropdown-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:transparent;z-index:1000}.workbook-actions-dropdown .dropdown-menu{position:relative;min-width:200px;background:white;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:1001;overflow:hidden}.workbook-actions-dropdown .dropdown-menu nile-menu{display:block}.fullscreen-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:1999;cursor:pointer}nile-tab-group::part(nav){background-color:#fafafa}nile-tab-group::part(active-tab-indicator-path){background:none}nile-tab-group::part(active-tab-indicator){border-bottom:none}nile-tab-group::part(tabs){gap:0}nile-tab-group nile-tab{border:1px solid #e0e0e0;border-top-left-radius:6px;border-top-right-radius:6px}nile-tab-group nile-tab::part(base){padding-left:.5rem;padding-top:.5rem;padding-bottom:.5rem;border-bottom-left-radius:0;border-bottom-right-radius:0}nile-tab-group nile-tab.active{background-color:#fff;color:#000}nile-tab-group nile-tab-panel::part(base){padding:0;max-height:30rem}\n"] }]
10075
10187
  }], ctorParameters: function () { return [{ type: AutosaveService }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { config: [{
10076
10188
  type: Input
10077
10189
  }], sheetsData: [{