@aquera/ngx-smart-table 0.0.3-alpha → 0.0.5-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.
@@ -3578,6 +3578,7 @@ class NileInputEditor {
3578
3578
  class NileSelectEditor {
3579
3579
  constructor(options) {
3580
3580
  this.options = options;
3581
+ this.acceptsInitialKeypress = false;
3581
3582
  this.eventListeners = [];
3582
3583
  this.currentOptions = [];
3583
3584
  // Handle Observable options
@@ -4122,6 +4123,7 @@ class NileAutoCompleteEditor {
4122
4123
  class NileCalendarEditor {
4123
4124
  constructor(options) {
4124
4125
  this.options = options;
4126
+ this.acceptsInitialKeypress = false;
4125
4127
  this.eventListeners = [];
4126
4128
  }
4127
4129
  edit(context) {
@@ -4222,10 +4224,6 @@ class NileCalendarEditor {
4222
4224
  if (this.options?.size) {
4223
4225
  this.calendar.setAttribute('size', this.options.size);
4224
4226
  }
4225
- // Portal rendering (default: true)
4226
- if (this.options?.portal !== false) {
4227
- this.calendar.setAttribute('portal', 'true');
4228
- }
4229
4227
  }
4230
4228
  /**
4231
4229
  * Set up all event listeners with proper cleanup tracking
@@ -4327,7 +4325,7 @@ class NileCalendarEditor {
4327
4325
 
4328
4326
  /**
4329
4327
  * Custom editor using NileDatePicker from @aquera/nile-elements
4330
- * Supports both single date and range selection with slot-based trigger input
4328
+ * Supports both single date and range selection with slot-based trigger div
4331
4329
  * Returns string values for both modes:
4332
4330
  * - Single mode: 'YYYY-MM-DD'
4333
4331
  * - Range mode: 'YYYY-MM-DD - YYYY-MM-DD'
@@ -4342,6 +4340,7 @@ class NileCalendarEditor {
4342
4340
  class NileDatePickerEditor {
4343
4341
  constructor(options) {
4344
4342
  this.options = options;
4343
+ this.acceptsInitialKeypress = false;
4345
4344
  this.eventListeners = [];
4346
4345
  }
4347
4346
  edit(context) {
@@ -4356,18 +4355,20 @@ class NileDatePickerEditor {
4356
4355
  this.datePicker.style.width = 'inherit';
4357
4356
  this.datePicker.style.height = 'inherit';
4358
4357
  this.datePicker.style.boxSizing = 'border-box';
4359
- // Create trigger input with slot using document.createElement
4360
- this.triggerInput = document.createElement('nile-input');
4361
- this.triggerInput.setAttribute('slot', 'trigger');
4358
+ // Create trigger div with slot
4359
+ this.triggerDiv = document.createElement('div');
4360
+ this.triggerDiv.setAttribute('slot', 'trigger');
4361
+ this.triggerDiv.setAttribute('contenteditable', 'false');
4362
+ this.triggerDiv.style.width = '100%';
4363
+ this.triggerDiv.style.minHeight = '28px';
4364
+ this.triggerDiv.style.cursor = 'pointer';
4365
+ this.triggerDiv.style.boxSizing = 'border-box';
4362
4366
  // Set initial value
4363
4367
  this.setInitialValue(context.value);
4364
- if (this.options?.label) {
4365
- this.triggerInput.label = this.options.label;
4366
- }
4367
4368
  // Apply configuration options
4368
4369
  this.applyOptions();
4369
4370
  // Append trigger to date picker (slot-based)
4370
- this.datePicker.appendChild(this.triggerInput);
4371
+ this.datePicker.appendChild(this.triggerDiv);
4371
4372
  // Set up event listeners
4372
4373
  this.setupEventListeners(context);
4373
4374
  // Append to container
@@ -4376,30 +4377,31 @@ class NileDatePickerEditor {
4376
4377
  // Auto focus and open the date picker
4377
4378
  if (this.options?.autoFocus !== false) {
4378
4379
  setTimeout(() => {
4379
- this.triggerInput?.focus();
4380
+ this.triggerDiv?.focus();
4380
4381
  // Trigger a click to open the date picker dropdown
4381
- this.triggerInput?.click();
4382
+ this.triggerDiv?.click();
4382
4383
  }, 0);
4383
4384
  }
4384
4385
  }
4385
4386
  /**
4386
- * Set initial value in the trigger input
4387
+ * Set initial value in the trigger div
4387
4388
  */
4388
4389
  setInitialValue(value) {
4389
- if (!this.triggerInput)
4390
+ if (!this.triggerDiv)
4390
4391
  return;
4391
4392
  if (!value) {
4392
- this.triggerInput.value = '';
4393
+ this.triggerDiv.textContent = '';
4393
4394
  return;
4394
4395
  }
4395
4396
  // Both single and range modes use string value
4396
- this.triggerInput.value = String(value);
4397
+ this.triggerDiv.textContent = String(value);
4397
4398
  }
4398
4399
  /**
4399
- * Apply all configuration options to both NileDatePicker and trigger input
4400
+ * Apply configuration options to the date picker
4401
+ * Note: Trigger is now a simple div, so input-specific options are not applied
4400
4402
  */
4401
4403
  applyOptions() {
4402
- if (!this.datePicker || !this.triggerInput)
4404
+ if (!this.datePicker)
4403
4405
  return;
4404
4406
  // Configure NileDatePicker
4405
4407
  const dateFormat = this.options?.dateFormat || 'YYYY-MM-DD';
@@ -4410,48 +4412,20 @@ class NileDatePickerEditor {
4410
4412
  if (this.options?.syncDatePicker) {
4411
4413
  this.datePicker.setAttribute('syncDatePicker', String(this.options.syncDatePicker));
4412
4414
  }
4413
- // Configure trigger input
4414
- if (this.options?.disabled) {
4415
- this.triggerInput.disabled = this.options.disabled;
4416
- }
4417
- if (this.options?.clearable) {
4418
- this.triggerInput.clearable = this.options.clearable;
4419
- }
4420
- if (this.options?.helpText) {
4421
- this.triggerInput.setAttribute('help-text', this.options.helpText);
4422
- }
4423
- // Visual states on trigger
4424
- if (this.options?.warning) {
4425
- this.triggerInput.setAttribute('warning', String(this.options.warning));
4426
- }
4427
- if (this.options?.error) {
4428
- this.triggerInput.setAttribute('error', String(this.options.error));
4429
- }
4430
- if (this.options?.success) {
4431
- this.triggerInput.setAttribute('success', String(this.options.success));
4432
- }
4433
- if (this.options?.errorMessage) {
4434
- this.triggerInput.setAttribute('error-message', this.options.errorMessage);
4435
- }
4436
- // Styling
4437
- if (this.options?.size) {
4438
- this.triggerInput.setAttribute('size', String(this.options.size));
4439
- }
4440
- if (this.options?.filled) {
4441
- this.triggerInput.setAttribute('filled', String(this.options.filled));
4442
- }
4443
- if (this.options?.pill) {
4444
- this.triggerInput.setAttribute('pill', String(this.options.pill));
4415
+ // Trigger div styling (disabled state can be indicated with opacity)
4416
+ if (this.triggerDiv && this.options?.disabled) {
4417
+ this.triggerDiv.style.opacity = '0.5';
4418
+ this.triggerDiv.style.pointerEvents = 'none';
4445
4419
  }
4446
4420
  }
4447
4421
  /**
4448
4422
  * Set up all event listeners with proper cleanup tracking
4449
4423
  */
4450
4424
  setupEventListeners(context) {
4451
- if (!this.datePicker || !this.triggerInput)
4425
+ if (!this.datePicker || !this.triggerDiv)
4452
4426
  return;
4453
4427
  const validateOnSave = this.options?.validateOnSave !== false;
4454
- // Keyboard events on trigger input
4428
+ // Keyboard events on trigger div
4455
4429
  const keydownHandler = (e) => {
4456
4430
  const keyEvent = e;
4457
4431
  if (keyEvent.key === 'Enter') {
@@ -4469,8 +4443,12 @@ class NileDatePickerEditor {
4469
4443
  keyEvent.stopPropagation();
4470
4444
  this.saveValue(context, validateOnSave);
4471
4445
  }
4446
+ else {
4447
+ // Prevent all other keypresses from typing into the div
4448
+ keyEvent.preventDefault();
4449
+ }
4472
4450
  };
4473
- this.triggerInput.addEventListener('keydown', keydownHandler);
4451
+ this.triggerDiv.addEventListener('keydown', keydownHandler);
4474
4452
  this.eventListeners.push({ event: 'keydown', handler: keydownHandler });
4475
4453
  // nile-change event on date picker
4476
4454
  const changeHandler = (e) => {
@@ -4490,25 +4468,6 @@ class NileDatePickerEditor {
4490
4468
  };
4491
4469
  this.datePicker.addEventListener('nile-change', changeHandler);
4492
4470
  this.eventListeners.push({ event: 'nile-change', handler: changeHandler });
4493
- // nile-blur event on trigger input
4494
- const blurHandler = (e) => {
4495
- const customEvent = e;
4496
- // For blur, get value from trigger input directly
4497
- const inputValue = this.triggerInput?.value || '';
4498
- if (inputValue) {
4499
- context.onSave(this.parseValue(inputValue));
4500
- }
4501
- };
4502
- this.triggerInput.addEventListener('nile-blur', blurHandler);
4503
- this.eventListeners.push({ event: 'nile-blur', handler: blurHandler });
4504
- // nile-clear event
4505
- if (this.options?.clearable) {
4506
- const clearHandler = (e) => {
4507
- context.onSave('');
4508
- };
4509
- this.triggerInput.addEventListener('nile-clear', clearHandler);
4510
- this.eventListeners.push({ event: 'nile-clear', handler: clearHandler });
4511
- }
4512
4471
  }
4513
4472
  /**
4514
4473
  * Save value with optional validation
@@ -4528,27 +4487,27 @@ class NileDatePickerEditor {
4528
4487
  return (value || '');
4529
4488
  }
4530
4489
  getCurrentValue() {
4531
- if (!this.triggerInput) {
4490
+ if (!this.triggerDiv) {
4532
4491
  return '';
4533
4492
  }
4534
- return this.parseValue(this.triggerInput.value ?? '');
4493
+ return this.parseValue(this.triggerDiv.textContent ?? '');
4535
4494
  }
4536
4495
  destroy() {
4537
- if (this.datePicker || this.triggerInput) {
4496
+ if (this.datePicker || this.triggerDiv) {
4538
4497
  this.eventListeners.forEach(({ event, handler }) => {
4539
4498
  this.datePicker?.removeEventListener(event, handler);
4540
- this.triggerInput?.removeEventListener(event, handler);
4499
+ this.triggerDiv?.removeEventListener(event, handler);
4541
4500
  });
4542
4501
  this.eventListeners = [];
4543
4502
  if (this.datePicker) {
4544
4503
  this.datePicker.remove();
4545
4504
  this.datePicker = undefined;
4546
4505
  }
4547
- this.triggerInput = undefined;
4506
+ this.triggerDiv = undefined;
4548
4507
  }
4549
4508
  }
4550
4509
  focus() {
4551
- this.triggerInput?.focus();
4510
+ this.triggerDiv?.focus();
4552
4511
  }
4553
4512
  }
4554
4513
 
@@ -4933,12 +4892,18 @@ class StCellComponent {
4933
4892
  startEditWithKey(key) {
4934
4893
  const canStart = this.cell.startEdit();
4935
4894
  if (canStart) {
4936
- // Replace current value with the key
4937
- this.cell.setValue(key, false);
4938
4895
  const column = this.cell.getColumnConfig();
4896
+ const editor = column.editor;
4897
+ // Only set initial keypress value if editor accepts it
4898
+ // Default to true for backward compatibility with existing editors
4899
+ const acceptsKeypress = editor?.acceptsInitialKeypress !== false;
4900
+ if (acceptsKeypress) {
4901
+ // Replace current value with the key
4902
+ this.cell.setValue(key, false);
4903
+ }
4939
4904
  const editEvent = {
4940
4905
  cell: this.cell,
4941
- value: key,
4906
+ value: acceptsKeypress ? key : this.cell.getValue(),
4942
4907
  rowData: this.cell.getRowData(),
4943
4908
  columnKey: column.key,
4944
4909
  rowIndex: this.cell.getRowIndex(),
@@ -6864,10 +6829,10 @@ class StColumnMenuDropdownComponent {
6864
6829
  }
6865
6830
  }
6866
6831
  StColumnMenuDropdownComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StColumnMenuDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6867
- StColumnMenuDropdownComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StColumnMenuDropdownComponent, selector: "st-column-menu-dropdown", inputs: { isOpen: "isOpen", position: "position", context: "context" }, outputs: { actionClicked: "actionClicked", closed: "closed" }, host: { listeners: { "click": "onBackdropClick($event)" } }, viewQueries: [{ propertyName: "filterPopup", first: true, predicate: ["filterPopup"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- Dropdown container with backdrop -->\n<div class=\"dropdown-container\" *ngIf=\"isOpen && context\">\n <!-- Backdrop -->\n <div class=\"dropdown-backdrop\" (click)=\"closed.emit()\"></div>\n \n <!-- Dropdown menu -->\n <div class=\"column-menu-dropdown\" [ngStyle]=\"dropdownStyle\">\n <!-- Main menu with actions -->\n <nile-menu *ngIf=\"!isFilterOpen\">\n <!-- Dynamically render all visible actions -->\n <ng-container *ngFor=\"let action of visibleActions; let i = index; let last = last\">\n <nile-menu-item \n (click)=\"onActionClick(action)\"\n [class.disabled]=\"isActionDisabled(action)\"\n [class.active]=\"isActionActive(action)\">\n <span class=\"checkmark\" *ngIf=\"isActionActive(action)\">\u2713</span>\n <nile-icon slot=\"prefix\" *ngIf=\"action.icon && !isActionActive(action)\" [name]=\"action.icon\"></nile-icon>\n <span class=\"action-label\">{{ action.label }}</span>\n </nile-menu-item>\n \n <!-- Add divider after action groups -->\n <nile-divider *ngIf=\"shouldShowDividerAfter(action, i, last)\"></nile-divider>\n </ng-container>\n \n <!-- Fallback if no actions -->\n <nile-menu-item *ngIf=\"visibleActions.length === 0\">\n No actions available\n </nile-menu-item>\n </nile-menu>\n \n <!-- Filter popup (conditionally rendered) -->\n <st-column-filter\n #filterPopup\n *ngIf=\"isFilterOpen && context\"\n [column]=\"context.column\"\n [tableState]=\"context.tableState\"\n [columnIndex]=\"context.columnIndex\"\n [isFirstColumn]=\"context.isFirstColumn\"\n [isLastColumn]=\"context.isLastColumn\"\n [isOpen]=\"isFilterOpen\"\n (filterApplied)=\"onFilterApplied($event)\"\n (filterCleared)=\"onFilterCleared()\"\n (closed)=\"onFilterClosed()\">\n </st-column-filter>\n </div>\n</div>\n", styles: [".dropdown-container{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9998}.dropdown-backdrop{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#0000004d;pointer-events:auto;z-index:9998}.column-menu-dropdown{min-width:200px;max-width:300px;background-color:#fff;border-radius:8px;box-shadow:0 10px 25px #00000026;overflow:hidden;pointer-events:auto;z-index:9999}nile-menu nile-divider::part(divider){margin:0}nile-menu nile-menu-item::part(base){height:2.5rem;min-height:auto}nile-menu nile-menu-item .checkmark{margin-right:8px;color:#4299e1;font-weight:700}\n"], components: [{ type: StColumnFilterComponent, selector: "st-column-filter", inputs: ["column", "tableState", "columnIndex", "isFirstColumn", "isLastColumn", "isOpen", "filterContext"], outputs: ["closed", "filterApplied", "filterCleared"] }], 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"] }] });
6832
+ StColumnMenuDropdownComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StColumnMenuDropdownComponent, selector: "st-column-menu-dropdown", inputs: { isOpen: "isOpen", position: "position", context: "context" }, outputs: { actionClicked: "actionClicked", closed: "closed" }, host: { listeners: { "click": "onBackdropClick($event)" } }, viewQueries: [{ propertyName: "filterPopup", first: true, predicate: ["filterPopup"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<!-- Dropdown container with backdrop -->\n<div class=\"dropdown-container\" *ngIf=\"isOpen && context\">\n <!-- Backdrop -->\n <div class=\"dropdown-backdrop\" (click)=\"closed.emit()\"></div>\n \n <!-- Dropdown menu -->\n <div class=\"column-menu-dropdown\" [ngStyle]=\"dropdownStyle\">\n <!-- Main menu with actions -->\n <nile-menu *ngIf=\"!isFilterOpen\">\n <!-- Dynamically render all visible actions -->\n <ng-container *ngFor=\"let action of visibleActions; let i = index; let last = last\">\n <nile-menu-item \n (click)=\"onActionClick(action)\"\n [class.disabled]=\"isActionDisabled(action)\"\n [class.active]=\"isActionActive(action)\">\n <span class=\"checkmark\" *ngIf=\"isActionActive(action)\">\u2713</span>\n <nile-icon slot=\"prefix\" *ngIf=\"action.icon && !isActionActive(action)\" [name]=\"action.icon\"></nile-icon>\n <span class=\"action-label\">{{ action.label }}</span>\n </nile-menu-item>\n \n <!-- Add divider after action groups -->\n <nile-divider *ngIf=\"shouldShowDividerAfter(action, i, last)\"></nile-divider>\n </ng-container>\n \n <!-- Fallback if no actions -->\n <nile-menu-item *ngIf=\"visibleActions.length === 0\">\n No actions available\n </nile-menu-item>\n </nile-menu>\n \n <!-- Filter popup (conditionally rendered) -->\n <st-column-filter\n #filterPopup\n *ngIf=\"isFilterOpen && context\"\n [column]=\"context.column\"\n [tableState]=\"context.tableState\"\n [columnIndex]=\"context.columnIndex\"\n [isFirstColumn]=\"context.isFirstColumn\"\n [isLastColumn]=\"context.isLastColumn\"\n [isOpen]=\"isFilterOpen\"\n (filterApplied)=\"onFilterApplied($event)\"\n (filterCleared)=\"onFilterCleared()\"\n (closed)=\"onFilterClosed()\">\n </st-column-filter>\n </div>\n</div>\n", styles: [".dropdown-container{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9998}.dropdown-backdrop{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:auto;z-index:9998}.column-menu-dropdown{min-width:200px;max-width:300px;background-color:#fff;border-radius:8px;box-shadow:0 10px 25px #00000026;overflow:hidden;pointer-events:auto;z-index:9999}nile-menu nile-divider::part(divider){margin:0}nile-menu nile-menu-item::part(base){height:2.5rem;min-height:auto}nile-menu nile-menu-item .checkmark{margin-right:8px;color:#4299e1;font-weight:700}\n"], components: [{ type: StColumnFilterComponent, selector: "st-column-filter", inputs: ["column", "tableState", "columnIndex", "isFirstColumn", "isLastColumn", "isOpen", "filterContext"], outputs: ["closed", "filterApplied", "filterCleared"] }], 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"] }] });
6868
6833
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StColumnMenuDropdownComponent, decorators: [{
6869
6834
  type: Component,
6870
- args: [{ selector: 'st-column-menu-dropdown', template: "<!-- Dropdown container with backdrop -->\n<div class=\"dropdown-container\" *ngIf=\"isOpen && context\">\n <!-- Backdrop -->\n <div class=\"dropdown-backdrop\" (click)=\"closed.emit()\"></div>\n \n <!-- Dropdown menu -->\n <div class=\"column-menu-dropdown\" [ngStyle]=\"dropdownStyle\">\n <!-- Main menu with actions -->\n <nile-menu *ngIf=\"!isFilterOpen\">\n <!-- Dynamically render all visible actions -->\n <ng-container *ngFor=\"let action of visibleActions; let i = index; let last = last\">\n <nile-menu-item \n (click)=\"onActionClick(action)\"\n [class.disabled]=\"isActionDisabled(action)\"\n [class.active]=\"isActionActive(action)\">\n <span class=\"checkmark\" *ngIf=\"isActionActive(action)\">\u2713</span>\n <nile-icon slot=\"prefix\" *ngIf=\"action.icon && !isActionActive(action)\" [name]=\"action.icon\"></nile-icon>\n <span class=\"action-label\">{{ action.label }}</span>\n </nile-menu-item>\n \n <!-- Add divider after action groups -->\n <nile-divider *ngIf=\"shouldShowDividerAfter(action, i, last)\"></nile-divider>\n </ng-container>\n \n <!-- Fallback if no actions -->\n <nile-menu-item *ngIf=\"visibleActions.length === 0\">\n No actions available\n </nile-menu-item>\n </nile-menu>\n \n <!-- Filter popup (conditionally rendered) -->\n <st-column-filter\n #filterPopup\n *ngIf=\"isFilterOpen && context\"\n [column]=\"context.column\"\n [tableState]=\"context.tableState\"\n [columnIndex]=\"context.columnIndex\"\n [isFirstColumn]=\"context.isFirstColumn\"\n [isLastColumn]=\"context.isLastColumn\"\n [isOpen]=\"isFilterOpen\"\n (filterApplied)=\"onFilterApplied($event)\"\n (filterCleared)=\"onFilterCleared()\"\n (closed)=\"onFilterClosed()\">\n </st-column-filter>\n </div>\n</div>\n", styles: [".dropdown-container{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9998}.dropdown-backdrop{position:absolute;top:0;left:0;width:100%;height:100%;background-color:#0000004d;pointer-events:auto;z-index:9998}.column-menu-dropdown{min-width:200px;max-width:300px;background-color:#fff;border-radius:8px;box-shadow:0 10px 25px #00000026;overflow:hidden;pointer-events:auto;z-index:9999}nile-menu nile-divider::part(divider){margin:0}nile-menu nile-menu-item::part(base){height:2.5rem;min-height:auto}nile-menu nile-menu-item .checkmark{margin-right:8px;color:#4299e1;font-weight:700}\n"] }]
6835
+ args: [{ selector: 'st-column-menu-dropdown', template: "<!-- Dropdown container with backdrop -->\n<div class=\"dropdown-container\" *ngIf=\"isOpen && context\">\n <!-- Backdrop -->\n <div class=\"dropdown-backdrop\" (click)=\"closed.emit()\"></div>\n \n <!-- Dropdown menu -->\n <div class=\"column-menu-dropdown\" [ngStyle]=\"dropdownStyle\">\n <!-- Main menu with actions -->\n <nile-menu *ngIf=\"!isFilterOpen\">\n <!-- Dynamically render all visible actions -->\n <ng-container *ngFor=\"let action of visibleActions; let i = index; let last = last\">\n <nile-menu-item \n (click)=\"onActionClick(action)\"\n [class.disabled]=\"isActionDisabled(action)\"\n [class.active]=\"isActionActive(action)\">\n <span class=\"checkmark\" *ngIf=\"isActionActive(action)\">\u2713</span>\n <nile-icon slot=\"prefix\" *ngIf=\"action.icon && !isActionActive(action)\" [name]=\"action.icon\"></nile-icon>\n <span class=\"action-label\">{{ action.label }}</span>\n </nile-menu-item>\n \n <!-- Add divider after action groups -->\n <nile-divider *ngIf=\"shouldShowDividerAfter(action, i, last)\"></nile-divider>\n </ng-container>\n \n <!-- Fallback if no actions -->\n <nile-menu-item *ngIf=\"visibleActions.length === 0\">\n No actions available\n </nile-menu-item>\n </nile-menu>\n \n <!-- Filter popup (conditionally rendered) -->\n <st-column-filter\n #filterPopup\n *ngIf=\"isFilterOpen && context\"\n [column]=\"context.column\"\n [tableState]=\"context.tableState\"\n [columnIndex]=\"context.columnIndex\"\n [isFirstColumn]=\"context.isFirstColumn\"\n [isLastColumn]=\"context.isLastColumn\"\n [isOpen]=\"isFilterOpen\"\n (filterApplied)=\"onFilterApplied($event)\"\n (filterCleared)=\"onFilterCleared()\"\n (closed)=\"onFilterClosed()\">\n </st-column-filter>\n </div>\n</div>\n", styles: [".dropdown-container{position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9998}.dropdown-backdrop{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:auto;z-index:9998}.column-menu-dropdown{min-width:200px;max-width:300px;background-color:#fff;border-radius:8px;box-shadow:0 10px 25px #00000026;overflow:hidden;pointer-events:auto;z-index:9999}nile-menu nile-divider::part(divider){margin:0}nile-menu nile-menu-item::part(base){height:2.5rem;min-height:auto}nile-menu nile-menu-item .checkmark{margin-right:8px;color:#4299e1;font-weight:700}\n"] }]
6871
6836
  }], propDecorators: { isOpen: [{
6872
6837
  type: Input
6873
6838
  }], position: [{
@@ -7050,379 +7015,82 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
7050
7015
  }] } });
7051
7016
 
7052
7017
  /**
7053
- * Column Editor Component
7054
- * Form-based column configuration
7018
+ * st-header Component
7019
+ * Encapsulates column-specific logic including sorting and filtering
7055
7020
  */
7056
- class ColumnEditorComponent {
7057
- constructor(fb) {
7058
- this.fb = fb;
7059
- this.columnUpdated = new EventEmitter();
7060
- this.cancel = new EventEmitter();
7061
- this.dataTypes = [
7062
- { value: CellDataType.STRING, label: 'Text' },
7063
- { value: CellDataType.NUMBER, label: 'Number' },
7064
- { value: CellDataType.DATE, label: 'Date' },
7065
- { value: CellDataType.BOOLEAN, label: 'Boolean' },
7066
- { value: CellDataType.CUSTOM, label: 'Custom' }
7067
- ];
7068
- this.alignments = [
7069
- { value: CellAlignment.LEFT, label: 'Left' },
7070
- { value: CellAlignment.CENTER, label: 'Center' },
7071
- { value: CellAlignment.RIGHT, label: 'Right' }
7072
- ];
7073
- this.verticalAlignments = [
7074
- { value: CellVerticalAlignment.TOP, label: 'Top' },
7075
- { value: CellVerticalAlignment.MIDDLE, label: 'Middle' },
7076
- { value: CellVerticalAlignment.BOTTOM, label: 'Bottom' }
7077
- ];
7078
- this.editModes = [
7079
- { value: EditMode.CLICK, label: 'Click' },
7080
- { value: EditMode.MANUAL, label: 'Manual' },
7081
- { value: EditMode.NONE, label: 'None' }
7082
- ];
7083
- this.stickyOptions = [
7084
- { value: false, label: 'None' },
7085
- { value: 'left', label: 'Left' },
7086
- { value: 'right', label: 'Right' }
7087
- ];
7088
- }
7089
- ngOnInit() {
7090
- this.buildForm();
7091
- }
7092
- ngOnChanges(changes) {
7093
- if (changes['column'] && !changes['column'].firstChange && this.form) {
7094
- this.updateForm();
7095
- }
7021
+ class StHeaderComponent {
7022
+ constructor() {
7023
+ /**
7024
+ * Column index in the visible columns array
7025
+ */
7026
+ this.columnIndex = 0;
7027
+ /**
7028
+ * Whether this is the first column
7029
+ */
7030
+ this.isFirstColumn = false;
7031
+ /**
7032
+ * Whether this is the last column
7033
+ */
7034
+ this.isLastColumn = false;
7035
+ /**
7036
+ * Whether sorting is enabled globally
7037
+ */
7038
+ this.enableSorting = true;
7039
+ /**
7040
+ * Whether filtering is enabled globally
7041
+ */
7042
+ this.enableFiltering = true;
7043
+ /**
7044
+ * Emits column sort event when sort is toggled
7045
+ */
7046
+ this.sortToggle = new EventEmitter();
7047
+ /**
7048
+ * Emits filter changes (future feature)
7049
+ */
7050
+ this.filterChange = new EventEmitter();
7051
+ /**
7052
+ * Emits when column is moved left or right
7053
+ */
7054
+ this.columnMoved = new EventEmitter();
7055
+ /**
7056
+ * Emits when column menu button is clicked
7057
+ */
7058
+ this.menuClick = new EventEmitter();
7096
7059
  }
7097
7060
  /**
7098
- * Build the reactive form
7061
+ * Cleanup on destroy
7099
7062
  */
7100
- buildForm() {
7101
- this.form = this.fb.group({
7102
- // Basic properties
7103
- key: [this.column.key, [Validators.required, Validators.pattern(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/)]],
7104
- header: [this.column.header || ''],
7105
- dataType: [this.column.dataType || CellDataType.STRING],
7106
- // Feature flags
7107
- editable: [this.column.editable !== false],
7108
- sortable: [this.column.sortable !== false],
7109
- filterable: [this.column.filterable !== false],
7110
- resizable: [this.column.resizable !== false],
7111
- hideable: [this.column.hideable !== false],
7112
- movable: [this.column.movable !== false],
7113
- pinnable: [this.column.pinnable !== false],
7114
- enableMenu: [this.column.enableMenu !== false],
7115
- // Layout
7116
- width: [this.column.width || ''],
7117
- minWidth: [this.column.minWidth || ''],
7118
- maxWidth: [this.column.maxWidth || ''],
7119
- sticky: [this.column.sticky || false],
7120
- alignment: [this.column.alignment || CellAlignment.LEFT],
7121
- verticalAlignment: [this.column.verticalAlignment || CellVerticalAlignment.MIDDLE],
7122
- // Edit mode
7123
- editMode: [this.column.editMode || EditMode.CLICK],
7124
- // Display
7125
- visible: [this.column.visible !== false],
7126
- truncate: [this.column.truncate || false]
7127
- });
7128
- // Subscribe to hideable changes to enforce visible=true when hideable=false
7129
- this.form.get('hideable')?.valueChanges.subscribe(hideable => {
7130
- if (!hideable) {
7131
- this.form.get('visible')?.setValue(true, { emitEvent: false });
7132
- }
7133
- });
7134
- // Update form when column changes
7135
- this.updateForm();
7063
+ ngOnDestroy() {
7064
+ // No cleanup needed - resize is now handled by directive
7136
7065
  }
7137
7066
  /**
7138
- * Update form values from column
7067
+ * Handle column menu button click
7139
7068
  */
7140
- updateForm() {
7141
- if (!this.form || !this.column)
7142
- return;
7143
- this.form.patchValue({
7144
- key: this.column.key,
7145
- header: this.column.header || '',
7146
- dataType: this.column.dataType || CellDataType.STRING,
7147
- editable: this.column.editable !== false,
7148
- sortable: this.column.sortable !== false,
7149
- filterable: this.column.filterable !== false,
7150
- resizable: this.column.resizable !== false,
7151
- hideable: this.column.hideable !== false,
7152
- movable: this.column.movable !== false,
7153
- pinnable: this.column.pinnable !== false,
7154
- enableMenu: this.column.enableMenu !== false,
7155
- width: this.column.width || '',
7156
- minWidth: this.column.minWidth || '',
7157
- maxWidth: this.column.maxWidth || '',
7158
- sticky: this.column.sticky || false,
7159
- alignment: this.column.alignment || CellAlignment.LEFT,
7160
- verticalAlignment: this.column.verticalAlignment || CellVerticalAlignment.MIDDLE,
7161
- editMode: this.column.editMode || EditMode.CLICK,
7162
- visible: this.column.visible !== false,
7163
- truncate: this.column.truncate || false
7164
- }, { emitEvent: false });
7069
+ onMenuClick(event) {
7070
+ event.preventDefault();
7071
+ event.stopPropagation();
7072
+ this.menuClick.emit(event);
7165
7073
  }
7166
7074
  /**
7167
- * Handle form submission
7075
+ * Check if column menu should be shown
7168
7076
  */
7169
- onSave() {
7170
- if (this.form.valid) {
7171
- const formValue = this.form.value;
7172
- const updates = {
7173
- key: formValue.key,
7174
- header: formValue.header || undefined,
7175
- dataType: formValue.dataType,
7176
- editable: formValue.editable,
7177
- sortable: formValue.sortable,
7178
- filterable: formValue.filterable,
7179
- resizable: formValue.resizable,
7180
- hideable: formValue.hideable,
7181
- movable: formValue.movable,
7182
- pinnable: formValue.pinnable,
7183
- enableMenu: formValue.enableMenu,
7184
- width: formValue.width || undefined,
7185
- minWidth: formValue.minWidth || undefined,
7186
- maxWidth: formValue.maxWidth || undefined,
7187
- sticky: formValue.sticky || undefined,
7188
- alignment: formValue.alignment,
7189
- verticalAlignment: formValue.verticalAlignment,
7190
- editMode: formValue.editMode,
7191
- visible: formValue.visible,
7192
- truncate: formValue.truncate
7193
- };
7194
- // Convert empty strings to undefined
7195
- Object.keys(updates).forEach(key => {
7196
- const value = updates[key];
7197
- if (value === '' || value === null) {
7198
- updates[key] = undefined;
7199
- }
7200
- });
7201
- this.columnUpdated.emit(updates);
7202
- }
7203
- else {
7204
- // Mark all fields as touched to show validation errors
7205
- Object.keys(this.form.controls).forEach(key => {
7206
- this.form.get(key)?.markAsTouched();
7207
- });
7208
- }
7077
+ isMenuEnabled() {
7078
+ return this.column.enableMenu !== false;
7209
7079
  }
7210
7080
  /**
7211
- * Handle cancel
7081
+ * Check if column is sortable
7212
7082
  */
7213
- onCancel() {
7214
- this.cancel.emit();
7083
+ isSortable() {
7084
+ return this.column.sortable !== false && this.enableSorting;
7215
7085
  }
7216
7086
  /**
7217
- * Get form control error message
7087
+ * Get current sort direction
7218
7088
  */
7219
- getErrorMessage(controlName) {
7220
- const control = this.form.get(controlName);
7221
- if (control?.hasError('required')) {
7222
- return 'This field is required';
7223
- }
7224
- if (control?.hasError('pattern')) {
7225
- return 'Invalid format';
7226
- }
7227
- return '';
7089
+ getSortDirection() {
7090
+ return this.column.sortDirection || SortDirection.NONE;
7228
7091
  }
7229
7092
  /**
7230
- * Check if form control has error
7231
- */
7232
- hasError(controlName) {
7233
- const control = this.form.get(controlName);
7234
- return !!(control && control.invalid && (control.dirty || control.touched));
7235
- }
7236
- }
7237
- ColumnEditorComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ColumnEditorComponent, deps: [{ token: i1$1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component });
7238
- ColumnEditorComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: ColumnEditorComponent, selector: "st-column-editor", inputs: { column: "column", columnIndex: "columnIndex" }, outputs: { columnUpdated: "columnUpdated", cancel: "cancel" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"column-editor\">\n <div class=\"editor-header\">\n <h3>Column Editor</h3>\n <div class=\"editor-actions\">\n <button class=\"btn btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n <button class=\"btn btn-primary\" (click)=\"onSave()\">Save</button>\n </div>\n </div>\n\n <form [formGroup]=\"form\" class=\"editor-form\">\n <!-- Basic Properties -->\n <div class=\"form-section\">\n <h4>Basic Properties</h4>\n <div class=\"form-group\">\n <label for=\"key\">Key *</label>\n <input\n id=\"key\"\n type=\"text\"\n formControlName=\"key\"\n class=\"form-control\"\n [class.error]=\"hasError('key')\"\n placeholder=\"columnKey\">\n <span class=\"error-message\" *ngIf=\"hasError('key')\">\n {{ getErrorMessage('key') }}\n </span>\n </div>\n\n <div class=\"form-group\">\n <label for=\"header\">Header</label>\n <input\n id=\"header\"\n type=\"text\"\n formControlName=\"header\"\n class=\"form-control\"\n placeholder=\"Column Header\">\n </div>\n\n <div class=\"form-group\">\n <label for=\"dataType\">Data Type</label>\n <select id=\"dataType\" formControlName=\"dataType\" class=\"form-control\">\n <option *ngFor=\"let type of dataTypes\" [value]=\"type.value\">\n {{ type.label }}\n </option>\n </select>\n </div>\n </div>\n\n <!-- Features -->\n <div class=\"form-section\">\n <h4>Features</h4>\n <div class=\"checkbox-group\">\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"editable\">\n <span>Editable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"sortable\">\n <span>Sortable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"filterable\">\n <span>Filterable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"resizable\">\n <span>Resizable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"hideable\">\n <span>Hideable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"movable\">\n <span>Movable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"pinnable\">\n <span>Pinnable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"enableMenu\">\n <span>Enable Menu</span>\n </label>\n </div>\n </div>\n\n <!-- Layout -->\n <div class=\"form-section\">\n <h4>Layout</h4>\n <div class=\"form-row\">\n <div class=\"form-group\">\n <label for=\"width\">Width</label>\n <input\n id=\"width\"\n type=\"text\"\n formControlName=\"width\"\n class=\"form-control\"\n placeholder=\"150 or 'auto'\">\n </div>\n <div class=\"form-group\">\n <label for=\"minWidth\">Min Width</label>\n <input\n id=\"minWidth\"\n type=\"number\"\n formControlName=\"minWidth\"\n class=\"form-control\"\n placeholder=\"50\">\n </div>\n <div class=\"form-group\">\n <label for=\"maxWidth\">Max Width</label>\n <input\n id=\"maxWidth\"\n type=\"number\"\n formControlName=\"maxWidth\"\n class=\"form-control\"\n placeholder=\"500\">\n </div>\n </div>\n\n <div class=\"form-row\">\n <div class=\"form-group\">\n <label for=\"sticky\">Sticky</label>\n <select id=\"sticky\" formControlName=\"sticky\" class=\"form-control\">\n <option *ngFor=\"let option of stickyOptions\" [value]=\"option.value\">\n {{ option.label }}\n </option>\n </select>\n </div>\n <div class=\"form-group\">\n <label for=\"alignment\">Alignment</label>\n <select id=\"alignment\" formControlName=\"alignment\" class=\"form-control\">\n <option *ngFor=\"let align of alignments\" [value]=\"align.value\">\n {{ align.label }}\n </option>\n </select>\n </div>\n <div class=\"form-group\">\n <label for=\"verticalAlignment\">Vertical Alignment</label>\n <select id=\"verticalAlignment\" formControlName=\"verticalAlignment\" class=\"form-control\">\n <option *ngFor=\"let align of verticalAlignments\" [value]=\"align.value\">\n {{ align.label }}\n </option>\n </select>\n </div>\n </div>\n </div>\n\n <!-- Edit Mode -->\n <div class=\"form-section\">\n <h4>Edit Mode</h4>\n <div class=\"form-group\">\n <label for=\"editMode\">Edit Trigger</label>\n <select id=\"editMode\" formControlName=\"editMode\" class=\"form-control\">\n <option *ngFor=\"let mode of editModes\" [value]=\"mode.value\">\n {{ mode.label }}\n </option>\n </select>\n </div>\n </div>\n\n <!-- Display -->\n <div class=\"form-section\">\n <h4>Display</h4>\n <div class=\"checkbox-group\">\n <label class=\"checkbox-label\" [class.disabled]=\"!form.get('hideable')?.value\">\n <input type=\"checkbox\" \n formControlName=\"visible\"\n [disabled]=\"!form.get('hideable')?.value\"\n [title]=\"!form.get('hideable')?.value ? 'Column visibility cannot be changed when hideable is disabled' : ''\">\n <span>Visible</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"truncate\">\n <span>Truncate Text</span>\n </label>\n </div>\n </div>\n </form>\n</div>\n\n", styles: [".column-editor{display:flex;flex-direction:column;height:100%}.editor-header{display:flex;justify-content:space-between;align-items:center;padding:1rem;border-bottom:1px solid #e0e0e0;background-color:#f8f8f8}.editor-header h3{margin:0;font-size:1.125rem;font-weight:600;color:#333}.editor-actions{display:flex;gap:.5rem}.btn{padding:.5rem 1rem;border:1px solid #d0d0d0;border-radius:4px;cursor:pointer;font-size:.875rem;transition:all .2s}.btn.btn-primary{background-color:#2196f3;color:#fff;border-color:#2196f3}.btn.btn-primary:hover{background-color:#1976d2;border-color:#1976d2}.btn.btn-secondary{background-color:#fff;color:#333}.btn.btn-secondary:hover{background-color:#f5f5f5}.editor-form{flex:1;overflow-y:auto;padding:1rem}.form-section{margin-bottom:2rem}.form-section h4{margin:0 0 1rem;font-size:.875rem;font-weight:600;color:#666;text-transform:uppercase;letter-spacing:.5px}.form-group{margin-bottom:1rem}.form-group label{display:block;margin-bottom:.5rem;font-size:.875rem;font-weight:500;color:#333}.form-control{width:100%;padding:.5rem;border:1px solid #d0d0d0;border-radius:4px;font-size:.875rem;transition:border-color .2s}.form-control:focus{outline:none;border-color:#2196f3}.form-control.error{border-color:#f44336}.form-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));grid-gap:1rem;gap:1rem}.checkbox-group{display:flex;row-gap:1rem;flex-wrap:wrap;gap:.75rem}.checkbox-label{display:flex;align-items:center;gap:.5rem;cursor:pointer;font-size:.875rem;color:#333}.checkbox-label input[type=checkbox]{width:18px;height:18px;cursor:pointer}.checkbox-label input[type=checkbox]:disabled{cursor:not-allowed;opacity:.5}.checkbox-label.disabled{opacity:.6;cursor:not-allowed}.checkbox-label.disabled span{color:#999}.error-message{display:block;margin-top:.25rem;font-size:.75rem;color:#f44336}\n"], directives: [{ type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { type: i1$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }] });
7239
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ColumnEditorComponent, decorators: [{
7240
- type: Component,
7241
- args: [{ selector: 'st-column-editor', template: "<div class=\"column-editor\">\n <div class=\"editor-header\">\n <h3>Column Editor</h3>\n <div class=\"editor-actions\">\n <button class=\"btn btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n <button class=\"btn btn-primary\" (click)=\"onSave()\">Save</button>\n </div>\n </div>\n\n <form [formGroup]=\"form\" class=\"editor-form\">\n <!-- Basic Properties -->\n <div class=\"form-section\">\n <h4>Basic Properties</h4>\n <div class=\"form-group\">\n <label for=\"key\">Key *</label>\n <input\n id=\"key\"\n type=\"text\"\n formControlName=\"key\"\n class=\"form-control\"\n [class.error]=\"hasError('key')\"\n placeholder=\"columnKey\">\n <span class=\"error-message\" *ngIf=\"hasError('key')\">\n {{ getErrorMessage('key') }}\n </span>\n </div>\n\n <div class=\"form-group\">\n <label for=\"header\">Header</label>\n <input\n id=\"header\"\n type=\"text\"\n formControlName=\"header\"\n class=\"form-control\"\n placeholder=\"Column Header\">\n </div>\n\n <div class=\"form-group\">\n <label for=\"dataType\">Data Type</label>\n <select id=\"dataType\" formControlName=\"dataType\" class=\"form-control\">\n <option *ngFor=\"let type of dataTypes\" [value]=\"type.value\">\n {{ type.label }}\n </option>\n </select>\n </div>\n </div>\n\n <!-- Features -->\n <div class=\"form-section\">\n <h4>Features</h4>\n <div class=\"checkbox-group\">\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"editable\">\n <span>Editable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"sortable\">\n <span>Sortable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"filterable\">\n <span>Filterable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"resizable\">\n <span>Resizable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"hideable\">\n <span>Hideable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"movable\">\n <span>Movable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"pinnable\">\n <span>Pinnable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"enableMenu\">\n <span>Enable Menu</span>\n </label>\n </div>\n </div>\n\n <!-- Layout -->\n <div class=\"form-section\">\n <h4>Layout</h4>\n <div class=\"form-row\">\n <div class=\"form-group\">\n <label for=\"width\">Width</label>\n <input\n id=\"width\"\n type=\"text\"\n formControlName=\"width\"\n class=\"form-control\"\n placeholder=\"150 or 'auto'\">\n </div>\n <div class=\"form-group\">\n <label for=\"minWidth\">Min Width</label>\n <input\n id=\"minWidth\"\n type=\"number\"\n formControlName=\"minWidth\"\n class=\"form-control\"\n placeholder=\"50\">\n </div>\n <div class=\"form-group\">\n <label for=\"maxWidth\">Max Width</label>\n <input\n id=\"maxWidth\"\n type=\"number\"\n formControlName=\"maxWidth\"\n class=\"form-control\"\n placeholder=\"500\">\n </div>\n </div>\n\n <div class=\"form-row\">\n <div class=\"form-group\">\n <label for=\"sticky\">Sticky</label>\n <select id=\"sticky\" formControlName=\"sticky\" class=\"form-control\">\n <option *ngFor=\"let option of stickyOptions\" [value]=\"option.value\">\n {{ option.label }}\n </option>\n </select>\n </div>\n <div class=\"form-group\">\n <label for=\"alignment\">Alignment</label>\n <select id=\"alignment\" formControlName=\"alignment\" class=\"form-control\">\n <option *ngFor=\"let align of alignments\" [value]=\"align.value\">\n {{ align.label }}\n </option>\n </select>\n </div>\n <div class=\"form-group\">\n <label for=\"verticalAlignment\">Vertical Alignment</label>\n <select id=\"verticalAlignment\" formControlName=\"verticalAlignment\" class=\"form-control\">\n <option *ngFor=\"let align of verticalAlignments\" [value]=\"align.value\">\n {{ align.label }}\n </option>\n </select>\n </div>\n </div>\n </div>\n\n <!-- Edit Mode -->\n <div class=\"form-section\">\n <h4>Edit Mode</h4>\n <div class=\"form-group\">\n <label for=\"editMode\">Edit Trigger</label>\n <select id=\"editMode\" formControlName=\"editMode\" class=\"form-control\">\n <option *ngFor=\"let mode of editModes\" [value]=\"mode.value\">\n {{ mode.label }}\n </option>\n </select>\n </div>\n </div>\n\n <!-- Display -->\n <div class=\"form-section\">\n <h4>Display</h4>\n <div class=\"checkbox-group\">\n <label class=\"checkbox-label\" [class.disabled]=\"!form.get('hideable')?.value\">\n <input type=\"checkbox\" \n formControlName=\"visible\"\n [disabled]=\"!form.get('hideable')?.value\"\n [title]=\"!form.get('hideable')?.value ? 'Column visibility cannot be changed when hideable is disabled' : ''\">\n <span>Visible</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"truncate\">\n <span>Truncate Text</span>\n </label>\n </div>\n </div>\n </form>\n</div>\n\n", styles: [".column-editor{display:flex;flex-direction:column;height:100%}.editor-header{display:flex;justify-content:space-between;align-items:center;padding:1rem;border-bottom:1px solid #e0e0e0;background-color:#f8f8f8}.editor-header h3{margin:0;font-size:1.125rem;font-weight:600;color:#333}.editor-actions{display:flex;gap:.5rem}.btn{padding:.5rem 1rem;border:1px solid #d0d0d0;border-radius:4px;cursor:pointer;font-size:.875rem;transition:all .2s}.btn.btn-primary{background-color:#2196f3;color:#fff;border-color:#2196f3}.btn.btn-primary:hover{background-color:#1976d2;border-color:#1976d2}.btn.btn-secondary{background-color:#fff;color:#333}.btn.btn-secondary:hover{background-color:#f5f5f5}.editor-form{flex:1;overflow-y:auto;padding:1rem}.form-section{margin-bottom:2rem}.form-section h4{margin:0 0 1rem;font-size:.875rem;font-weight:600;color:#666;text-transform:uppercase;letter-spacing:.5px}.form-group{margin-bottom:1rem}.form-group label{display:block;margin-bottom:.5rem;font-size:.875rem;font-weight:500;color:#333}.form-control{width:100%;padding:.5rem;border:1px solid #d0d0d0;border-radius:4px;font-size:.875rem;transition:border-color .2s}.form-control:focus{outline:none;border-color:#2196f3}.form-control.error{border-color:#f44336}.form-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));grid-gap:1rem;gap:1rem}.checkbox-group{display:flex;row-gap:1rem;flex-wrap:wrap;gap:.75rem}.checkbox-label{display:flex;align-items:center;gap:.5rem;cursor:pointer;font-size:.875rem;color:#333}.checkbox-label input[type=checkbox]{width:18px;height:18px;cursor:pointer}.checkbox-label input[type=checkbox]:disabled{cursor:not-allowed;opacity:.5}.checkbox-label.disabled{opacity:.6;cursor:not-allowed}.checkbox-label.disabled span{color:#999}.error-message{display:block;margin-top:.25rem;font-size:.75rem;color:#f44336}\n"] }]
7242
- }], ctorParameters: function () { return [{ type: i1$1.FormBuilder }]; }, propDecorators: { column: [{
7243
- type: Input
7244
- }], columnIndex: [{
7245
- type: Input
7246
- }], columnUpdated: [{
7247
- type: Output
7248
- }], cancel: [{
7249
- type: Output
7250
- }] } });
7251
-
7252
- class StColumnEditorModalComponent {
7253
- constructor() {
7254
- this.columnCreated = new EventEmitter();
7255
- this.cancelled = new EventEmitter();
7256
- this.columnIndex = -1;
7257
- }
7258
- ngOnInit() {
7259
- // Create a default column that will be edited
7260
- this.newColumn = this.createDefaultColumn();
7261
- }
7262
- ngOnDestroy() {
7263
- // Cleanup if needed
7264
- }
7265
- /**
7266
- * Create a default column configuration
7267
- */
7268
- createDefaultColumn() {
7269
- return ColumnConfigFactory.text('newColumn', {
7270
- header: 'New Column',
7271
- editable: true,
7272
- sortable: true,
7273
- filterable: true,
7274
- resizable: true,
7275
- hideable: true,
7276
- visible: true
7277
- });
7278
- }
7279
- /**
7280
- * Trigger save from external button
7281
- */
7282
- onSave() {
7283
- // Call the column editor's save method
7284
- if (this.columnEditor) {
7285
- this.columnEditor.onSave();
7286
- }
7287
- }
7288
- /**
7289
- * Handle column updates from the editor
7290
- */
7291
- onColumnUpdated(updates) {
7292
- // Merge updates into the new column
7293
- this.newColumn = { ...this.newColumn, ...updates };
7294
- // Use ColumnConfigFactory to create proper column based on data type
7295
- let finalColumn;
7296
- const key = updates.key || this.newColumn.key;
7297
- const dataType = updates.dataType || this.newColumn.dataType;
7298
- switch (dataType) {
7299
- case CellDataType.NUMBER:
7300
- finalColumn = ColumnConfigFactory.number(key, this.newColumn);
7301
- break;
7302
- case CellDataType.DATE:
7303
- finalColumn = ColumnConfigFactory.date(key, this.newColumn);
7304
- break;
7305
- case CellDataType.BOOLEAN:
7306
- finalColumn = ColumnConfigFactory.boolean(key, this.newColumn);
7307
- break;
7308
- default:
7309
- finalColumn = ColumnConfigFactory.text(key, this.newColumn);
7310
- }
7311
- // Emit the created column
7312
- this.columnCreated.emit(finalColumn);
7313
- }
7314
- /**
7315
- * Handle cancel from the editor
7316
- */
7317
- onCancel() {
7318
- this.cancelled.emit();
7319
- }
7320
- /**
7321
- * Handle backdrop click to close modal
7322
- */
7323
- onBackdropClick(event) {
7324
- if (event.target === event.currentTarget) {
7325
- this.cancelled.emit();
7326
- }
7327
- }
7328
- /**
7329
- * Prevent event propagation from modal content
7330
- */
7331
- onModalContentClick(event) {
7332
- event.stopPropagation();
7333
- }
7334
- }
7335
- StColumnEditorModalComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StColumnEditorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7336
- StColumnEditorModalComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StColumnEditorModalComponent, selector: "st-column-editor-modal", outputs: { columnCreated: "columnCreated", cancelled: "cancelled" }, viewQueries: [{ propertyName: "columnEditor", first: true, predicate: ColumnEditorComponent, descendants: true }], ngImport: i0, template: "<div class=\"modal-backdrop\" (click)=\"onBackdropClick($event)\">\n <div class=\"modal-content\" (click)=\"onModalContentClick($event)\">\n <div class=\"modal-header\">\n <h2>Add New Column</h2>\n <button class=\"close-button\" (click)=\"onCancel()\" type=\"button\" aria-label=\"Close\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n </div>\n <div class=\"modal-body\">\n <!-- Now using the shared st-column-editor component from SharedTableComponentsModule -->\n <st-column-editor\n [column]=\"newColumn\"\n [columnIndex]=\"columnIndex\"\n (columnUpdated)=\"onColumnUpdated($event)\"\n (cancel)=\"onCancel()\">\n </st-column-editor>\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"onSave()\">Add Column</button>\n </div>\n </div>\n</div>\n", styles: [".modal-backdrop{position:fixed;inset:0;background-color:#00000080;display:flex;align-items:center;justify-content:center;z-index:2000;animation:fadeIn .2s ease-in-out}.modal-content{background:white;border-radius:8px;box-shadow:0 11px 15px -7px #0003,0 24px 38px 3px #00000024,0 9px 46px 8px #0000001f;max-width:600px;max-height:90vh;width:90%;display:flex;flex-direction:column;animation:slideUp .3s cubic-bezier(.4,0,.2,1)}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;border-bottom:1px solid #e0e0e0;flex-shrink:0}.modal-header h2{margin:0;font-size:20px;font-weight:500;color:#212121}.close-button{background:none;border:none;cursor:pointer;padding:8px;display:flex;align-items:center;justify-content:center;border-radius:50%;color:#757575;transition:all .2s ease}.close-button:hover{background-color:#0000000a;color:#212121}.close-button:active{background-color:#00000014}.close-button:focus{outline:none;box-shadow:0 0 0 2px #1976d24d}.close-button svg{width:24px;height:24px}.modal-body{padding:0;overflow-y:auto;flex:1;min-height:0}.modal-body ::ng-deep st-column-editor{display:block}.modal-body ::ng-deep st-column-editor .column-editor{padding:0;border:none;box-shadow:none}.modal-body ::ng-deep st-column-editor .column-editor .editor-header{display:none}.modal-body ::ng-deep st-column-editor .column-editor .editor-actions{display:none}.modal-body ::ng-deep st-column-editor .column-editor .editor-form{padding:24px}.modal-footer{display:flex;justify-content:flex-end;gap:12px;padding:16px 24px;border-top:1px solid #e0e0e0;flex-shrink:0;background-color:#f5f5f5}.modal-footer .btn{padding:10px 20px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s}.modal-footer .btn:focus{outline:none;box-shadow:0 0 0 2px #0000001a}.modal-footer .btn.btn-secondary{background-color:#fff;color:#424242;border:1px solid #d0d0d0}.modal-footer .btn.btn-secondary:hover{background-color:#f5f5f5}.modal-footer .btn.btn-secondary:active{background-color:#eee}.modal-footer .btn.btn-primary{background-color:#1976d2;color:#fff}.modal-footer .btn.btn-primary:hover{background-color:#1565c0}.modal-footer .btn.btn-primary:active{background-color:#0d47a1}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(50px)}to{opacity:1;transform:translateY(0)}}\n"], components: [{ type: ColumnEditorComponent, selector: "st-column-editor", inputs: ["column", "columnIndex"], outputs: ["columnUpdated", "cancel"] }] });
7337
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StColumnEditorModalComponent, decorators: [{
7338
- type: Component,
7339
- args: [{ selector: 'st-column-editor-modal', template: "<div class=\"modal-backdrop\" (click)=\"onBackdropClick($event)\">\n <div class=\"modal-content\" (click)=\"onModalContentClick($event)\">\n <div class=\"modal-header\">\n <h2>Add New Column</h2>\n <button class=\"close-button\" (click)=\"onCancel()\" type=\"button\" aria-label=\"Close\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n </div>\n <div class=\"modal-body\">\n <!-- Now using the shared st-column-editor component from SharedTableComponentsModule -->\n <st-column-editor\n [column]=\"newColumn\"\n [columnIndex]=\"columnIndex\"\n (columnUpdated)=\"onColumnUpdated($event)\"\n (cancel)=\"onCancel()\">\n </st-column-editor>\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"onSave()\">Add Column</button>\n </div>\n </div>\n</div>\n", styles: [".modal-backdrop{position:fixed;inset:0;background-color:#00000080;display:flex;align-items:center;justify-content:center;z-index:2000;animation:fadeIn .2s ease-in-out}.modal-content{background:white;border-radius:8px;box-shadow:0 11px 15px -7px #0003,0 24px 38px 3px #00000024,0 9px 46px 8px #0000001f;max-width:600px;max-height:90vh;width:90%;display:flex;flex-direction:column;animation:slideUp .3s cubic-bezier(.4,0,.2,1)}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;border-bottom:1px solid #e0e0e0;flex-shrink:0}.modal-header h2{margin:0;font-size:20px;font-weight:500;color:#212121}.close-button{background:none;border:none;cursor:pointer;padding:8px;display:flex;align-items:center;justify-content:center;border-radius:50%;color:#757575;transition:all .2s ease}.close-button:hover{background-color:#0000000a;color:#212121}.close-button:active{background-color:#00000014}.close-button:focus{outline:none;box-shadow:0 0 0 2px #1976d24d}.close-button svg{width:24px;height:24px}.modal-body{padding:0;overflow-y:auto;flex:1;min-height:0}.modal-body ::ng-deep st-column-editor{display:block}.modal-body ::ng-deep st-column-editor .column-editor{padding:0;border:none;box-shadow:none}.modal-body ::ng-deep st-column-editor .column-editor .editor-header{display:none}.modal-body ::ng-deep st-column-editor .column-editor .editor-actions{display:none}.modal-body ::ng-deep st-column-editor .column-editor .editor-form{padding:24px}.modal-footer{display:flex;justify-content:flex-end;gap:12px;padding:16px 24px;border-top:1px solid #e0e0e0;flex-shrink:0;background-color:#f5f5f5}.modal-footer .btn{padding:10px 20px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s}.modal-footer .btn:focus{outline:none;box-shadow:0 0 0 2px #0000001a}.modal-footer .btn.btn-secondary{background-color:#fff;color:#424242;border:1px solid #d0d0d0}.modal-footer .btn.btn-secondary:hover{background-color:#f5f5f5}.modal-footer .btn.btn-secondary:active{background-color:#eee}.modal-footer .btn.btn-primary{background-color:#1976d2;color:#fff}.modal-footer .btn.btn-primary:hover{background-color:#1565c0}.modal-footer .btn.btn-primary:active{background-color:#0d47a1}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(50px)}to{opacity:1;transform:translateY(0)}}\n"] }]
7340
- }], propDecorators: { columnCreated: [{
7341
- type: Output
7342
- }], cancelled: [{
7343
- type: Output
7344
- }], columnEditor: [{
7345
- type: ViewChild,
7346
- args: [ColumnEditorComponent]
7347
- }] } });
7348
-
7349
- /**
7350
- * st-header Component
7351
- * Encapsulates column-specific logic including sorting and filtering
7352
- */
7353
- class StHeaderComponent {
7354
- constructor() {
7355
- /**
7356
- * Column index in the visible columns array
7357
- */
7358
- this.columnIndex = 0;
7359
- /**
7360
- * Whether this is the first column
7361
- */
7362
- this.isFirstColumn = false;
7363
- /**
7364
- * Whether this is the last column
7365
- */
7366
- this.isLastColumn = false;
7367
- /**
7368
- * Whether sorting is enabled globally
7369
- */
7370
- this.enableSorting = true;
7371
- /**
7372
- * Whether filtering is enabled globally
7373
- */
7374
- this.enableFiltering = true;
7375
- /**
7376
- * Emits column sort event when sort is toggled
7377
- */
7378
- this.sortToggle = new EventEmitter();
7379
- /**
7380
- * Emits filter changes (future feature)
7381
- */
7382
- this.filterChange = new EventEmitter();
7383
- /**
7384
- * Emits when column is moved left or right
7385
- */
7386
- this.columnMoved = new EventEmitter();
7387
- /**
7388
- * Emits when column menu button is clicked
7389
- */
7390
- this.menuClick = new EventEmitter();
7391
- }
7392
- /**
7393
- * Cleanup on destroy
7394
- */
7395
- ngOnDestroy() {
7396
- // No cleanup needed - resize is now handled by directive
7397
- }
7398
- /**
7399
- * Handle column menu button click
7400
- */
7401
- onMenuClick(event) {
7402
- event.preventDefault();
7403
- event.stopPropagation();
7404
- this.menuClick.emit(event);
7405
- }
7406
- /**
7407
- * Check if column menu should be shown
7408
- */
7409
- isMenuEnabled() {
7410
- return this.column.enableMenu !== false;
7411
- }
7412
- /**
7413
- * Check if column is sortable
7414
- */
7415
- isSortable() {
7416
- return this.column.sortable !== false && this.enableSorting;
7417
- }
7418
- /**
7419
- * Get current sort direction
7420
- */
7421
- getSortDirection() {
7422
- return this.column.sortDirection || SortDirection.NONE;
7423
- }
7424
- /**
7425
- * Check if column is currently sorted
7093
+ * Check if column is currently sorted
7426
7094
  */
7427
7095
  isSorted() {
7428
7096
  return this.column.sortEnabled || false;
@@ -7572,25 +7240,22 @@ class StTableActionsComponent {
7572
7240
  updateDropdownPosition(event) {
7573
7241
  const button = event.currentTarget;
7574
7242
  const rect = button.getBoundingClientRect();
7575
- const viewportWidth = window.innerWidth;
7576
- const viewportHeight = window.innerHeight;
7577
- const dropdownWidth = 280;
7578
- const dropdownHeight = 400;
7579
- let x = rect.left;
7243
+ const dropdownWidth = 220;
7244
+ let x = rect.right;
7580
7245
  let y = rect.bottom + 5;
7581
- // Adjust horizontal position if dropdown would overflow
7582
- if (x + dropdownWidth > viewportWidth) {
7583
- x = Math.max(10, viewportWidth - dropdownWidth - 10);
7584
- }
7585
- // Adjust vertical position if dropdown would overflow
7586
- if (y + dropdownHeight > viewportHeight) {
7587
- y = rect.top - dropdownHeight - 5;
7588
- }
7246
+ // // Adjust horizontal position if dropdown would overflow
7247
+ // if (x + dropdownWidth > viewportWidth) {
7248
+ // x = Math.max(10, viewportWidth - dropdownWidth - 10);
7249
+ // }
7250
+ // // Adjust vertical position if dropdown would overflow
7251
+ // if (y + dropdownHeight > viewportHeight) {
7252
+ // y = rect.top - dropdownHeight - 5;
7253
+ // }
7589
7254
  this.dropdownStyle = {
7590
- position: 'fixed',
7591
- left: `${x}px`,
7255
+ position: 'absolute',
7256
+ left: `${x - dropdownWidth}px`,
7592
7257
  top: `${y}px`,
7593
- 'z-index': 9999
7258
+ 'z-index': TableZIndex.TABLE_ACTIONS_DROPDOWN
7594
7259
  };
7595
7260
  }
7596
7261
  /**
@@ -7992,6 +7657,15 @@ class StTableComponent {
7992
7657
  // Update visible columns and cell grid
7993
7658
  this.updateVisibleData();
7994
7659
  }
7660
+ // Handle tableState input changes (when external state is provided, e.g., in workbook)
7661
+ if (changes['tableState'] && !changes['tableState'].firstChange && this.tableState) {
7662
+ // Unsubscribe from old tableState's focus observable to prevent memory leaks
7663
+ this.focusSubscription?.unsubscribe();
7664
+ // Reinitialize keyboard navigation with new tableState
7665
+ if (this.isKeyboardNavigationEnabled()) {
7666
+ this.initializeKeyboardNavigation();
7667
+ }
7668
+ }
7995
7669
  // Recalculate virtual scroll when data changes
7996
7670
  if ((changes['data'] || changes['cellGrid']) && !changes['data']?.firstChange) {
7997
7671
  if (this.isVirtualScrollEnabled()) {
@@ -9016,10 +8690,10 @@ class StTableComponent {
9016
8690
  }
9017
8691
  }
9018
8692
  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 });
9019
- 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<!-- Add Column Modal -->\n<st-column-editor-modal *ngIf=\"showColumnModal\" (columnCreated)=\"onColumnCreated($event)\"\n (cancelled)=\"onModalCancelled()\">\n</st-column-editor-modal>\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;background-color:#fff;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:#fff}.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;background-color:#fff;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: StColumnEditorModalComponent, selector: "st-column-editor-modal", outputs: ["columnCreated", "cancelled"] }, { 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 } });
8693
+ 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;background-color:#fff;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:#fff}.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;background-color:#fff;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 } });
9020
8694
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StTableComponent, decorators: [{
9021
8695
  type: Component,
9022
- 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<!-- Add Column Modal -->\n<st-column-editor-modal *ngIf=\"showColumnModal\" (columnCreated)=\"onColumnCreated($event)\"\n (cancelled)=\"onModalCancelled()\">\n</st-column-editor-modal>\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;background-color:#fff;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:#fff}.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;background-color:#fff;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"] }]
8696
+ 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;background-color:#fff;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:#fff}.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;background-color:#fff;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"] }]
9023
8697
  }], ctorParameters: function () { return [{ type: JsonSchemaValidatorService }, { type: ValidationLoggerService }, { type: VirtualScrollService }]; }, propDecorators: { tableConfig: [{
9024
8698
  type: Input
9025
8699
  }], data: [{
@@ -9206,6 +8880,303 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
9206
8880
  type: Output
9207
8881
  }] } });
9208
8882
 
8883
+ /**
8884
+ * Column Editor Component
8885
+ * Form-based column configuration
8886
+ */
8887
+ class ColumnEditorComponent {
8888
+ constructor(fb) {
8889
+ this.fb = fb;
8890
+ this.columnUpdated = new EventEmitter();
8891
+ this.cancel = new EventEmitter();
8892
+ this.dataTypes = [
8893
+ { value: CellDataType.STRING, label: 'Text' },
8894
+ { value: CellDataType.NUMBER, label: 'Number' },
8895
+ { value: CellDataType.DATE, label: 'Date' },
8896
+ { value: CellDataType.BOOLEAN, label: 'Boolean' },
8897
+ { value: CellDataType.CUSTOM, label: 'Custom' }
8898
+ ];
8899
+ this.alignments = [
8900
+ { value: CellAlignment.LEFT, label: 'Left' },
8901
+ { value: CellAlignment.CENTER, label: 'Center' },
8902
+ { value: CellAlignment.RIGHT, label: 'Right' }
8903
+ ];
8904
+ this.verticalAlignments = [
8905
+ { value: CellVerticalAlignment.TOP, label: 'Top' },
8906
+ { value: CellVerticalAlignment.MIDDLE, label: 'Middle' },
8907
+ { value: CellVerticalAlignment.BOTTOM, label: 'Bottom' }
8908
+ ];
8909
+ this.editModes = [
8910
+ { value: EditMode.CLICK, label: 'Click' },
8911
+ { value: EditMode.MANUAL, label: 'Manual' },
8912
+ { value: EditMode.NONE, label: 'None' }
8913
+ ];
8914
+ this.stickyOptions = [
8915
+ { value: false, label: 'None' },
8916
+ { value: 'left', label: 'Left' },
8917
+ { value: 'right', label: 'Right' }
8918
+ ];
8919
+ }
8920
+ ngOnInit() {
8921
+ this.buildForm();
8922
+ }
8923
+ ngOnChanges(changes) {
8924
+ if (changes['column'] && !changes['column'].firstChange && this.form) {
8925
+ this.updateForm();
8926
+ }
8927
+ }
8928
+ /**
8929
+ * Build the reactive form
8930
+ */
8931
+ buildForm() {
8932
+ this.form = this.fb.group({
8933
+ // Basic properties
8934
+ key: [this.column.key, [Validators.required, Validators.pattern(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/)]],
8935
+ header: [this.column.header || ''],
8936
+ dataType: [this.column.dataType || CellDataType.STRING],
8937
+ // Feature flags
8938
+ editable: [this.column.editable !== false],
8939
+ sortable: [this.column.sortable !== false],
8940
+ filterable: [this.column.filterable !== false],
8941
+ resizable: [this.column.resizable !== false],
8942
+ hideable: [this.column.hideable !== false],
8943
+ movable: [this.column.movable !== false],
8944
+ pinnable: [this.column.pinnable !== false],
8945
+ enableMenu: [this.column.enableMenu !== false],
8946
+ // Layout
8947
+ width: [this.column.width || ''],
8948
+ minWidth: [this.column.minWidth || ''],
8949
+ maxWidth: [this.column.maxWidth || ''],
8950
+ sticky: [this.column.sticky || false],
8951
+ alignment: [this.column.alignment || CellAlignment.LEFT],
8952
+ verticalAlignment: [this.column.verticalAlignment || CellVerticalAlignment.MIDDLE],
8953
+ // Edit mode
8954
+ editMode: [this.column.editMode || EditMode.CLICK],
8955
+ // Display
8956
+ visible: [this.column.visible !== false],
8957
+ truncate: [this.column.truncate || false]
8958
+ });
8959
+ // Subscribe to hideable changes to enforce visible=true when hideable=false
8960
+ this.form.get('hideable')?.valueChanges.subscribe(hideable => {
8961
+ if (!hideable) {
8962
+ this.form.get('visible')?.setValue(true, { emitEvent: false });
8963
+ }
8964
+ });
8965
+ // Update form when column changes
8966
+ this.updateForm();
8967
+ }
8968
+ /**
8969
+ * Update form values from column
8970
+ */
8971
+ updateForm() {
8972
+ if (!this.form || !this.column)
8973
+ return;
8974
+ this.form.patchValue({
8975
+ key: this.column.key,
8976
+ header: this.column.header || '',
8977
+ dataType: this.column.dataType || CellDataType.STRING,
8978
+ editable: this.column.editable !== false,
8979
+ sortable: this.column.sortable !== false,
8980
+ filterable: this.column.filterable !== false,
8981
+ resizable: this.column.resizable !== false,
8982
+ hideable: this.column.hideable !== false,
8983
+ movable: this.column.movable !== false,
8984
+ pinnable: this.column.pinnable !== false,
8985
+ enableMenu: this.column.enableMenu !== false,
8986
+ width: this.column.width || '',
8987
+ minWidth: this.column.minWidth || '',
8988
+ maxWidth: this.column.maxWidth || '',
8989
+ sticky: this.column.sticky || false,
8990
+ alignment: this.column.alignment || CellAlignment.LEFT,
8991
+ verticalAlignment: this.column.verticalAlignment || CellVerticalAlignment.MIDDLE,
8992
+ editMode: this.column.editMode || EditMode.CLICK,
8993
+ visible: this.column.visible !== false,
8994
+ truncate: this.column.truncate || false
8995
+ }, { emitEvent: false });
8996
+ }
8997
+ /**
8998
+ * Handle form submission
8999
+ */
9000
+ onSave() {
9001
+ if (this.form.valid) {
9002
+ const formValue = this.form.value;
9003
+ const updates = {
9004
+ key: formValue.key,
9005
+ header: formValue.header || undefined,
9006
+ dataType: formValue.dataType,
9007
+ editable: formValue.editable,
9008
+ sortable: formValue.sortable,
9009
+ filterable: formValue.filterable,
9010
+ resizable: formValue.resizable,
9011
+ hideable: formValue.hideable,
9012
+ movable: formValue.movable,
9013
+ pinnable: formValue.pinnable,
9014
+ enableMenu: formValue.enableMenu,
9015
+ width: formValue.width || undefined,
9016
+ minWidth: formValue.minWidth || undefined,
9017
+ maxWidth: formValue.maxWidth || undefined,
9018
+ sticky: formValue.sticky || undefined,
9019
+ alignment: formValue.alignment,
9020
+ verticalAlignment: formValue.verticalAlignment,
9021
+ editMode: formValue.editMode,
9022
+ visible: formValue.visible,
9023
+ truncate: formValue.truncate
9024
+ };
9025
+ // Convert empty strings to undefined
9026
+ Object.keys(updates).forEach(key => {
9027
+ const value = updates[key];
9028
+ if (value === '' || value === null) {
9029
+ updates[key] = undefined;
9030
+ }
9031
+ });
9032
+ this.columnUpdated.emit(updates);
9033
+ }
9034
+ else {
9035
+ // Mark all fields as touched to show validation errors
9036
+ Object.keys(this.form.controls).forEach(key => {
9037
+ this.form.get(key)?.markAsTouched();
9038
+ });
9039
+ }
9040
+ }
9041
+ /**
9042
+ * Handle cancel
9043
+ */
9044
+ onCancel() {
9045
+ this.cancel.emit();
9046
+ }
9047
+ /**
9048
+ * Get form control error message
9049
+ */
9050
+ getErrorMessage(controlName) {
9051
+ const control = this.form.get(controlName);
9052
+ if (control?.hasError('required')) {
9053
+ return 'This field is required';
9054
+ }
9055
+ if (control?.hasError('pattern')) {
9056
+ return 'Invalid format';
9057
+ }
9058
+ return '';
9059
+ }
9060
+ /**
9061
+ * Check if form control has error
9062
+ */
9063
+ hasError(controlName) {
9064
+ const control = this.form.get(controlName);
9065
+ return !!(control && control.invalid && (control.dirty || control.touched));
9066
+ }
9067
+ }
9068
+ ColumnEditorComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ColumnEditorComponent, deps: [{ token: i1$1.FormBuilder }], target: i0.ɵɵFactoryTarget.Component });
9069
+ ColumnEditorComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: ColumnEditorComponent, selector: "st-column-editor", inputs: { column: "column", columnIndex: "columnIndex" }, outputs: { columnUpdated: "columnUpdated", cancel: "cancel" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"column-editor\">\n <div class=\"editor-header\">\n <h3>Column Editor</h3>\n <div class=\"editor-actions\">\n <button class=\"btn btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n <button class=\"btn btn-primary\" (click)=\"onSave()\">Save</button>\n </div>\n </div>\n\n <form [formGroup]=\"form\" class=\"editor-form\">\n <!-- Basic Properties -->\n <div class=\"form-section\">\n <h4>Basic Properties</h4>\n <div class=\"form-group\">\n <label for=\"key\">Key *</label>\n <input\n id=\"key\"\n type=\"text\"\n formControlName=\"key\"\n class=\"form-control\"\n [class.error]=\"hasError('key')\"\n placeholder=\"columnKey\">\n <span class=\"error-message\" *ngIf=\"hasError('key')\">\n {{ getErrorMessage('key') }}\n </span>\n </div>\n\n <div class=\"form-group\">\n <label for=\"header\">Header</label>\n <input\n id=\"header\"\n type=\"text\"\n formControlName=\"header\"\n class=\"form-control\"\n placeholder=\"Column Header\">\n </div>\n\n <div class=\"form-group\">\n <label for=\"dataType\">Data Type</label>\n <select id=\"dataType\" formControlName=\"dataType\" class=\"form-control\">\n <option *ngFor=\"let type of dataTypes\" [value]=\"type.value\">\n {{ type.label }}\n </option>\n </select>\n </div>\n </div>\n\n <!-- Features -->\n <div class=\"form-section\">\n <h4>Features</h4>\n <div class=\"checkbox-group\">\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"editable\">\n <span>Editable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"sortable\">\n <span>Sortable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"filterable\">\n <span>Filterable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"resizable\">\n <span>Resizable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"hideable\">\n <span>Hideable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"movable\">\n <span>Movable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"pinnable\">\n <span>Pinnable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"enableMenu\">\n <span>Enable Menu</span>\n </label>\n </div>\n </div>\n\n <!-- Layout -->\n <div class=\"form-section\">\n <h4>Layout</h4>\n <div class=\"form-row\">\n <div class=\"form-group\">\n <label for=\"width\">Width</label>\n <input\n id=\"width\"\n type=\"text\"\n formControlName=\"width\"\n class=\"form-control\"\n placeholder=\"150 or 'auto'\">\n </div>\n <div class=\"form-group\">\n <label for=\"minWidth\">Min Width</label>\n <input\n id=\"minWidth\"\n type=\"number\"\n formControlName=\"minWidth\"\n class=\"form-control\"\n placeholder=\"50\">\n </div>\n <div class=\"form-group\">\n <label for=\"maxWidth\">Max Width</label>\n <input\n id=\"maxWidth\"\n type=\"number\"\n formControlName=\"maxWidth\"\n class=\"form-control\"\n placeholder=\"500\">\n </div>\n </div>\n\n <div class=\"form-row\">\n <div class=\"form-group\">\n <label for=\"sticky\">Sticky</label>\n <select id=\"sticky\" formControlName=\"sticky\" class=\"form-control\">\n <option *ngFor=\"let option of stickyOptions\" [value]=\"option.value\">\n {{ option.label }}\n </option>\n </select>\n </div>\n <div class=\"form-group\">\n <label for=\"alignment\">Alignment</label>\n <select id=\"alignment\" formControlName=\"alignment\" class=\"form-control\">\n <option *ngFor=\"let align of alignments\" [value]=\"align.value\">\n {{ align.label }}\n </option>\n </select>\n </div>\n <div class=\"form-group\">\n <label for=\"verticalAlignment\">Vertical Alignment</label>\n <select id=\"verticalAlignment\" formControlName=\"verticalAlignment\" class=\"form-control\">\n <option *ngFor=\"let align of verticalAlignments\" [value]=\"align.value\">\n {{ align.label }}\n </option>\n </select>\n </div>\n </div>\n </div>\n\n <!-- Edit Mode -->\n <div class=\"form-section\">\n <h4>Edit Mode</h4>\n <div class=\"form-group\">\n <label for=\"editMode\">Edit Trigger</label>\n <select id=\"editMode\" formControlName=\"editMode\" class=\"form-control\">\n <option *ngFor=\"let mode of editModes\" [value]=\"mode.value\">\n {{ mode.label }}\n </option>\n </select>\n </div>\n </div>\n\n <!-- Display -->\n <div class=\"form-section\">\n <h4>Display</h4>\n <div class=\"checkbox-group\">\n <label class=\"checkbox-label\" [class.disabled]=\"!form.get('hideable')?.value\">\n <input type=\"checkbox\" \n formControlName=\"visible\"\n [disabled]=\"!form.get('hideable')?.value\"\n [title]=\"!form.get('hideable')?.value ? 'Column visibility cannot be changed when hideable is disabled' : ''\">\n <span>Visible</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"truncate\">\n <span>Truncate Text</span>\n </label>\n </div>\n </div>\n </form>\n</div>\n\n", styles: [".column-editor{display:flex;flex-direction:column;height:100%}.editor-header{display:flex;justify-content:space-between;align-items:center;padding:1rem;border-bottom:1px solid #e0e0e0;background-color:#f8f8f8}.editor-header h3{margin:0;font-size:1.125rem;font-weight:600;color:#333}.editor-actions{display:flex;gap:.5rem}.btn{padding:.5rem 1rem;border:1px solid #d0d0d0;border-radius:4px;cursor:pointer;font-size:.875rem;transition:all .2s}.btn.btn-primary{background-color:#2196f3;color:#fff;border-color:#2196f3}.btn.btn-primary:hover{background-color:#1976d2;border-color:#1976d2}.btn.btn-secondary{background-color:#fff;color:#333}.btn.btn-secondary:hover{background-color:#f5f5f5}.editor-form{flex:1;overflow-y:auto;padding:1rem}.form-section{margin-bottom:2rem}.form-section h4{margin:0 0 1rem;font-size:.875rem;font-weight:600;color:#666;text-transform:uppercase;letter-spacing:.5px}.form-group{margin-bottom:1rem}.form-group label{display:block;margin-bottom:.5rem;font-size:.875rem;font-weight:500;color:#333}.form-control{width:100%;padding:.5rem;border:1px solid #d0d0d0;border-radius:4px;font-size:.875rem;transition:border-color .2s}.form-control:focus{outline:none;border-color:#2196f3}.form-control.error{border-color:#f44336}.form-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));grid-gap:1rem;gap:1rem}.checkbox-group{display:flex;row-gap:1rem;flex-wrap:wrap;gap:.75rem}.checkbox-label{display:flex;align-items:center;gap:.5rem;cursor:pointer;font-size:.875rem;color:#333}.checkbox-label input[type=checkbox]{width:18px;height:18px;cursor:pointer}.checkbox-label input[type=checkbox]:disabled{cursor:not-allowed;opacity:.5}.checkbox-label.disabled{opacity:.6;cursor:not-allowed}.checkbox-label.disabled span{color:#999}.error-message{display:block;margin-top:.25rem;font-size:.75rem;color:#f44336}\n"], directives: [{ type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { type: i1$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }] });
9070
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ColumnEditorComponent, decorators: [{
9071
+ type: Component,
9072
+ args: [{ selector: 'st-column-editor', template: "<div class=\"column-editor\">\n <div class=\"editor-header\">\n <h3>Column Editor</h3>\n <div class=\"editor-actions\">\n <button class=\"btn btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n <button class=\"btn btn-primary\" (click)=\"onSave()\">Save</button>\n </div>\n </div>\n\n <form [formGroup]=\"form\" class=\"editor-form\">\n <!-- Basic Properties -->\n <div class=\"form-section\">\n <h4>Basic Properties</h4>\n <div class=\"form-group\">\n <label for=\"key\">Key *</label>\n <input\n id=\"key\"\n type=\"text\"\n formControlName=\"key\"\n class=\"form-control\"\n [class.error]=\"hasError('key')\"\n placeholder=\"columnKey\">\n <span class=\"error-message\" *ngIf=\"hasError('key')\">\n {{ getErrorMessage('key') }}\n </span>\n </div>\n\n <div class=\"form-group\">\n <label for=\"header\">Header</label>\n <input\n id=\"header\"\n type=\"text\"\n formControlName=\"header\"\n class=\"form-control\"\n placeholder=\"Column Header\">\n </div>\n\n <div class=\"form-group\">\n <label for=\"dataType\">Data Type</label>\n <select id=\"dataType\" formControlName=\"dataType\" class=\"form-control\">\n <option *ngFor=\"let type of dataTypes\" [value]=\"type.value\">\n {{ type.label }}\n </option>\n </select>\n </div>\n </div>\n\n <!-- Features -->\n <div class=\"form-section\">\n <h4>Features</h4>\n <div class=\"checkbox-group\">\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"editable\">\n <span>Editable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"sortable\">\n <span>Sortable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"filterable\">\n <span>Filterable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"resizable\">\n <span>Resizable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"hideable\">\n <span>Hideable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"movable\">\n <span>Movable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"pinnable\">\n <span>Pinnable</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"enableMenu\">\n <span>Enable Menu</span>\n </label>\n </div>\n </div>\n\n <!-- Layout -->\n <div class=\"form-section\">\n <h4>Layout</h4>\n <div class=\"form-row\">\n <div class=\"form-group\">\n <label for=\"width\">Width</label>\n <input\n id=\"width\"\n type=\"text\"\n formControlName=\"width\"\n class=\"form-control\"\n placeholder=\"150 or 'auto'\">\n </div>\n <div class=\"form-group\">\n <label for=\"minWidth\">Min Width</label>\n <input\n id=\"minWidth\"\n type=\"number\"\n formControlName=\"minWidth\"\n class=\"form-control\"\n placeholder=\"50\">\n </div>\n <div class=\"form-group\">\n <label for=\"maxWidth\">Max Width</label>\n <input\n id=\"maxWidth\"\n type=\"number\"\n formControlName=\"maxWidth\"\n class=\"form-control\"\n placeholder=\"500\">\n </div>\n </div>\n\n <div class=\"form-row\">\n <div class=\"form-group\">\n <label for=\"sticky\">Sticky</label>\n <select id=\"sticky\" formControlName=\"sticky\" class=\"form-control\">\n <option *ngFor=\"let option of stickyOptions\" [value]=\"option.value\">\n {{ option.label }}\n </option>\n </select>\n </div>\n <div class=\"form-group\">\n <label for=\"alignment\">Alignment</label>\n <select id=\"alignment\" formControlName=\"alignment\" class=\"form-control\">\n <option *ngFor=\"let align of alignments\" [value]=\"align.value\">\n {{ align.label }}\n </option>\n </select>\n </div>\n <div class=\"form-group\">\n <label for=\"verticalAlignment\">Vertical Alignment</label>\n <select id=\"verticalAlignment\" formControlName=\"verticalAlignment\" class=\"form-control\">\n <option *ngFor=\"let align of verticalAlignments\" [value]=\"align.value\">\n {{ align.label }}\n </option>\n </select>\n </div>\n </div>\n </div>\n\n <!-- Edit Mode -->\n <div class=\"form-section\">\n <h4>Edit Mode</h4>\n <div class=\"form-group\">\n <label for=\"editMode\">Edit Trigger</label>\n <select id=\"editMode\" formControlName=\"editMode\" class=\"form-control\">\n <option *ngFor=\"let mode of editModes\" [value]=\"mode.value\">\n {{ mode.label }}\n </option>\n </select>\n </div>\n </div>\n\n <!-- Display -->\n <div class=\"form-section\">\n <h4>Display</h4>\n <div class=\"checkbox-group\">\n <label class=\"checkbox-label\" [class.disabled]=\"!form.get('hideable')?.value\">\n <input type=\"checkbox\" \n formControlName=\"visible\"\n [disabled]=\"!form.get('hideable')?.value\"\n [title]=\"!form.get('hideable')?.value ? 'Column visibility cannot be changed when hideable is disabled' : ''\">\n <span>Visible</span>\n </label>\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" formControlName=\"truncate\">\n <span>Truncate Text</span>\n </label>\n </div>\n </div>\n </form>\n</div>\n\n", styles: [".column-editor{display:flex;flex-direction:column;height:100%}.editor-header{display:flex;justify-content:space-between;align-items:center;padding:1rem;border-bottom:1px solid #e0e0e0;background-color:#f8f8f8}.editor-header h3{margin:0;font-size:1.125rem;font-weight:600;color:#333}.editor-actions{display:flex;gap:.5rem}.btn{padding:.5rem 1rem;border:1px solid #d0d0d0;border-radius:4px;cursor:pointer;font-size:.875rem;transition:all .2s}.btn.btn-primary{background-color:#2196f3;color:#fff;border-color:#2196f3}.btn.btn-primary:hover{background-color:#1976d2;border-color:#1976d2}.btn.btn-secondary{background-color:#fff;color:#333}.btn.btn-secondary:hover{background-color:#f5f5f5}.editor-form{flex:1;overflow-y:auto;padding:1rem}.form-section{margin-bottom:2rem}.form-section h4{margin:0 0 1rem;font-size:.875rem;font-weight:600;color:#666;text-transform:uppercase;letter-spacing:.5px}.form-group{margin-bottom:1rem}.form-group label{display:block;margin-bottom:.5rem;font-size:.875rem;font-weight:500;color:#333}.form-control{width:100%;padding:.5rem;border:1px solid #d0d0d0;border-radius:4px;font-size:.875rem;transition:border-color .2s}.form-control:focus{outline:none;border-color:#2196f3}.form-control.error{border-color:#f44336}.form-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));grid-gap:1rem;gap:1rem}.checkbox-group{display:flex;row-gap:1rem;flex-wrap:wrap;gap:.75rem}.checkbox-label{display:flex;align-items:center;gap:.5rem;cursor:pointer;font-size:.875rem;color:#333}.checkbox-label input[type=checkbox]{width:18px;height:18px;cursor:pointer}.checkbox-label input[type=checkbox]:disabled{cursor:not-allowed;opacity:.5}.checkbox-label.disabled{opacity:.6;cursor:not-allowed}.checkbox-label.disabled span{color:#999}.error-message{display:block;margin-top:.25rem;font-size:.75rem;color:#f44336}\n"] }]
9073
+ }], ctorParameters: function () { return [{ type: i1$1.FormBuilder }]; }, propDecorators: { column: [{
9074
+ type: Input
9075
+ }], columnIndex: [{
9076
+ type: Input
9077
+ }], columnUpdated: [{
9078
+ type: Output
9079
+ }], cancel: [{
9080
+ type: Output
9081
+ }] } });
9082
+
9083
+ class StColumnEditorModalComponent {
9084
+ constructor() {
9085
+ this.columnCreated = new EventEmitter();
9086
+ this.cancelled = new EventEmitter();
9087
+ this.columnIndex = -1;
9088
+ }
9089
+ ngOnInit() {
9090
+ // Create a default column that will be edited
9091
+ this.newColumn = this.createDefaultColumn();
9092
+ }
9093
+ ngOnDestroy() {
9094
+ // Cleanup if needed
9095
+ }
9096
+ /**
9097
+ * Create a default column configuration
9098
+ */
9099
+ createDefaultColumn() {
9100
+ return ColumnConfigFactory.text('newColumn', {
9101
+ header: 'New Column',
9102
+ editable: true,
9103
+ sortable: true,
9104
+ filterable: true,
9105
+ resizable: true,
9106
+ hideable: true,
9107
+ visible: true
9108
+ });
9109
+ }
9110
+ /**
9111
+ * Trigger save from external button
9112
+ */
9113
+ onSave() {
9114
+ // Call the column editor's save method
9115
+ if (this.columnEditor) {
9116
+ this.columnEditor.onSave();
9117
+ }
9118
+ }
9119
+ /**
9120
+ * Handle column updates from the editor
9121
+ */
9122
+ onColumnUpdated(updates) {
9123
+ // Merge updates into the new column
9124
+ this.newColumn = { ...this.newColumn, ...updates };
9125
+ // Use ColumnConfigFactory to create proper column based on data type
9126
+ let finalColumn;
9127
+ const key = updates.key || this.newColumn.key;
9128
+ const dataType = updates.dataType || this.newColumn.dataType;
9129
+ switch (dataType) {
9130
+ case CellDataType.NUMBER:
9131
+ finalColumn = ColumnConfigFactory.number(key, this.newColumn);
9132
+ break;
9133
+ case CellDataType.DATE:
9134
+ finalColumn = ColumnConfigFactory.date(key, this.newColumn);
9135
+ break;
9136
+ case CellDataType.BOOLEAN:
9137
+ finalColumn = ColumnConfigFactory.boolean(key, this.newColumn);
9138
+ break;
9139
+ default:
9140
+ finalColumn = ColumnConfigFactory.text(key, this.newColumn);
9141
+ }
9142
+ // Emit the created column
9143
+ this.columnCreated.emit(finalColumn);
9144
+ }
9145
+ /**
9146
+ * Handle cancel from the editor
9147
+ */
9148
+ onCancel() {
9149
+ this.cancelled.emit();
9150
+ }
9151
+ /**
9152
+ * Handle backdrop click to close modal
9153
+ */
9154
+ onBackdropClick(event) {
9155
+ if (event.target === event.currentTarget) {
9156
+ this.cancelled.emit();
9157
+ }
9158
+ }
9159
+ /**
9160
+ * Prevent event propagation from modal content
9161
+ */
9162
+ onModalContentClick(event) {
9163
+ event.stopPropagation();
9164
+ }
9165
+ }
9166
+ StColumnEditorModalComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StColumnEditorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9167
+ StColumnEditorModalComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StColumnEditorModalComponent, selector: "st-column-editor-modal", outputs: { columnCreated: "columnCreated", cancelled: "cancelled" }, viewQueries: [{ propertyName: "columnEditor", first: true, predicate: ColumnEditorComponent, descendants: true }], ngImport: i0, template: "<div class=\"modal-backdrop\" (click)=\"onBackdropClick($event)\">\n <div class=\"modal-content\" (click)=\"onModalContentClick($event)\">\n <div class=\"modal-header\">\n <h2>Add New Column</h2>\n <button class=\"close-button\" (click)=\"onCancel()\" type=\"button\" aria-label=\"Close\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n </div>\n <div class=\"modal-body\">\n <!-- Now using the shared st-column-editor component from SharedTableComponentsModule -->\n <st-column-editor\n [column]=\"newColumn\"\n [columnIndex]=\"columnIndex\"\n (columnUpdated)=\"onColumnUpdated($event)\"\n (cancel)=\"onCancel()\">\n </st-column-editor>\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"onSave()\">Add Column</button>\n </div>\n </div>\n</div>\n", styles: [".modal-backdrop{position:fixed;inset:0;background-color:#00000080;display:flex;align-items:center;justify-content:center;z-index:2000;animation:fadeIn .2s ease-in-out}.modal-content{background:white;border-radius:8px;box-shadow:0 11px 15px -7px #0003,0 24px 38px 3px #00000024,0 9px 46px 8px #0000001f;max-width:600px;max-height:90vh;width:90%;display:flex;flex-direction:column;animation:slideUp .3s cubic-bezier(.4,0,.2,1)}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;border-bottom:1px solid #e0e0e0;flex-shrink:0}.modal-header h2{margin:0;font-size:20px;font-weight:500;color:#212121}.close-button{background:none;border:none;cursor:pointer;padding:8px;display:flex;align-items:center;justify-content:center;border-radius:50%;color:#757575;transition:all .2s ease}.close-button:hover{background-color:#0000000a;color:#212121}.close-button:active{background-color:#00000014}.close-button:focus{outline:none;box-shadow:0 0 0 2px #1976d24d}.close-button svg{width:24px;height:24px}.modal-body{padding:0;overflow-y:auto;flex:1;min-height:0}.modal-body ::ng-deep st-column-editor{display:block}.modal-body ::ng-deep st-column-editor .column-editor{padding:0;border:none;box-shadow:none}.modal-body ::ng-deep st-column-editor .column-editor .editor-header{display:none}.modal-body ::ng-deep st-column-editor .column-editor .editor-actions{display:none}.modal-body ::ng-deep st-column-editor .column-editor .editor-form{padding:24px}.modal-footer{display:flex;justify-content:flex-end;gap:12px;padding:16px 24px;border-top:1px solid #e0e0e0;flex-shrink:0;background-color:#f5f5f5}.modal-footer .btn{padding:10px 20px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s}.modal-footer .btn:focus{outline:none;box-shadow:0 0 0 2px #0000001a}.modal-footer .btn.btn-secondary{background-color:#fff;color:#424242;border:1px solid #d0d0d0}.modal-footer .btn.btn-secondary:hover{background-color:#f5f5f5}.modal-footer .btn.btn-secondary:active{background-color:#eee}.modal-footer .btn.btn-primary{background-color:#1976d2;color:#fff}.modal-footer .btn.btn-primary:hover{background-color:#1565c0}.modal-footer .btn.btn-primary:active{background-color:#0d47a1}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(50px)}to{opacity:1;transform:translateY(0)}}\n"], components: [{ type: ColumnEditorComponent, selector: "st-column-editor", inputs: ["column", "columnIndex"], outputs: ["columnUpdated", "cancel"] }] });
9168
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StColumnEditorModalComponent, decorators: [{
9169
+ type: Component,
9170
+ args: [{ selector: 'st-column-editor-modal', template: "<div class=\"modal-backdrop\" (click)=\"onBackdropClick($event)\">\n <div class=\"modal-content\" (click)=\"onModalContentClick($event)\">\n <div class=\"modal-header\">\n <h2>Add New Column</h2>\n <button class=\"close-button\" (click)=\"onCancel()\" type=\"button\" aria-label=\"Close\">\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M18 6L6 18M6 6L18 18\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n </div>\n <div class=\"modal-body\">\n <!-- Now using the shared st-column-editor component from SharedTableComponentsModule -->\n <st-column-editor\n [column]=\"newColumn\"\n [columnIndex]=\"columnIndex\"\n (columnUpdated)=\"onColumnUpdated($event)\"\n (cancel)=\"onCancel()\">\n </st-column-editor>\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n <button type=\"button\" class=\"btn btn-primary\" (click)=\"onSave()\">Add Column</button>\n </div>\n </div>\n</div>\n", styles: [".modal-backdrop{position:fixed;inset:0;background-color:#00000080;display:flex;align-items:center;justify-content:center;z-index:2000;animation:fadeIn .2s ease-in-out}.modal-content{background:white;border-radius:8px;box-shadow:0 11px 15px -7px #0003,0 24px 38px 3px #00000024,0 9px 46px 8px #0000001f;max-width:600px;max-height:90vh;width:90%;display:flex;flex-direction:column;animation:slideUp .3s cubic-bezier(.4,0,.2,1)}.modal-header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;border-bottom:1px solid #e0e0e0;flex-shrink:0}.modal-header h2{margin:0;font-size:20px;font-weight:500;color:#212121}.close-button{background:none;border:none;cursor:pointer;padding:8px;display:flex;align-items:center;justify-content:center;border-radius:50%;color:#757575;transition:all .2s ease}.close-button:hover{background-color:#0000000a;color:#212121}.close-button:active{background-color:#00000014}.close-button:focus{outline:none;box-shadow:0 0 0 2px #1976d24d}.close-button svg{width:24px;height:24px}.modal-body{padding:0;overflow-y:auto;flex:1;min-height:0}.modal-body ::ng-deep st-column-editor{display:block}.modal-body ::ng-deep st-column-editor .column-editor{padding:0;border:none;box-shadow:none}.modal-body ::ng-deep st-column-editor .column-editor .editor-header{display:none}.modal-body ::ng-deep st-column-editor .column-editor .editor-actions{display:none}.modal-body ::ng-deep st-column-editor .column-editor .editor-form{padding:24px}.modal-footer{display:flex;justify-content:flex-end;gap:12px;padding:16px 24px;border-top:1px solid #e0e0e0;flex-shrink:0;background-color:#f5f5f5}.modal-footer .btn{padding:10px 20px;border:none;border-radius:4px;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s}.modal-footer .btn:focus{outline:none;box-shadow:0 0 0 2px #0000001a}.modal-footer .btn.btn-secondary{background-color:#fff;color:#424242;border:1px solid #d0d0d0}.modal-footer .btn.btn-secondary:hover{background-color:#f5f5f5}.modal-footer .btn.btn-secondary:active{background-color:#eee}.modal-footer .btn.btn-primary{background-color:#1976d2;color:#fff}.modal-footer .btn.btn-primary:hover{background-color:#1565c0}.modal-footer .btn.btn-primary:active{background-color:#0d47a1}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes slideUp{0%{opacity:0;transform:translateY(50px)}to{opacity:1;transform:translateY(0)}}\n"] }]
9171
+ }], propDecorators: { columnCreated: [{
9172
+ type: Output
9173
+ }], cancelled: [{
9174
+ type: Output
9175
+ }], columnEditor: [{
9176
+ type: ViewChild,
9177
+ args: [ColumnEditorComponent]
9178
+ }] } });
9179
+
9209
9180
  class StSheetActionsComponent {
9210
9181
  constructor() {
9211
9182
  /**