@aquera/ngx-smart-table 0.0.3-alpha → 0.0.6-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.
@@ -3499,6 +3499,7 @@ class NileInputEditor {
3499
3499
  class NileSelectEditor {
3500
3500
  constructor(options) {
3501
3501
  this.options = options;
3502
+ this.acceptsInitialKeypress = false;
3502
3503
  this.eventListeners = [];
3503
3504
  this.currentOptions = [];
3504
3505
  // Handle Observable options
@@ -3547,6 +3548,11 @@ class NileSelectEditor {
3547
3548
  var _a, _b;
3548
3549
  (_a = this.select) === null || _a === void 0 ? void 0 : _a.focus();
3549
3550
  (_b = this.select) === null || _b === void 0 ? void 0 : _b.click();
3551
+ // Find the portal div for multi-select click detection
3552
+ if (this.options.multiple) {
3553
+ // Portal div is appended to document body
3554
+ this.portalDiv = document.querySelector('.nile-select-portal-append');
3555
+ }
3550
3556
  }, 0);
3551
3557
  }
3552
3558
  /**
@@ -3645,6 +3651,9 @@ class NileSelectEditor {
3645
3651
  if (this.options.portal !== undefined) {
3646
3652
  this.select.portal = this.options.portal;
3647
3653
  }
3654
+ else {
3655
+ this.select.portal = true; // Default to portal mode for better positioning
3656
+ }
3648
3657
  // Prevent width syncing issues in table cells
3649
3658
  this.select.noWidthSync = true;
3650
3659
  }
@@ -3723,7 +3732,11 @@ class NileSelectEditor {
3723
3732
  (_b = this.select) === null || _b === void 0 ? void 0 : _b.reportValidity();
3724
3733
  return;
3725
3734
  }
3726
- context.onSave(this.parseValue(customEvent.detail.value));
3735
+ // Only save immediately for single-select
3736
+ // Multi-select saves on outside click
3737
+ if (!this.options.multiple) {
3738
+ context.onSave(this.parseValue(customEvent.detail.value));
3739
+ }
3727
3740
  };
3728
3741
  this.select.addEventListener('nile-change', changeHandler);
3729
3742
  this.eventListeners.push({ event: 'nile-change', handler: changeHandler });
@@ -3756,6 +3769,36 @@ class NileSelectEditor {
3756
3769
  this.select.addEventListener('nile-search', searchHandler);
3757
3770
  this.eventListeners.push({ event: 'nile-search', handler: searchHandler });
3758
3771
  }
3772
+ // For multi-select: detect clicks outside portal and select
3773
+ if (this.options.multiple) {
3774
+ const documentClickHandler = (e) => {
3775
+ var _a, _b, _c, _d;
3776
+ const mouseEvent = e;
3777
+ const target = mouseEvent.target;
3778
+ // Check if portal div exists (might take a moment to render)
3779
+ if (!this.portalDiv) {
3780
+ this.portalDiv = document.querySelector('.nile-select-portal-append');
3781
+ }
3782
+ // Determine if click is inside the select or portal
3783
+ const clickedInsideSelect = (_a = this.select) === null || _a === void 0 ? void 0 : _a.contains(target);
3784
+ const clickedInsidePortal = (_b = this.portalDiv) === null || _b === void 0 ? void 0 : _b.contains(target);
3785
+ // If clicked outside both select and portal, save and close
3786
+ if (!clickedInsideSelect && !clickedInsidePortal) {
3787
+ if (validateOnSave && !((_c = this.select) === null || _c === void 0 ? void 0 : _c.checkValidity())) {
3788
+ (_d = this.select) === null || _d === void 0 ? void 0 : _d.reportValidity();
3789
+ return;
3790
+ }
3791
+ context.onSave(this.getCurrentValue());
3792
+ }
3793
+ // Otherwise, click is inside - do nothing (dropdown stays open)
3794
+ };
3795
+ // Add to document with capture phase
3796
+ document.addEventListener('click', documentClickHandler, true);
3797
+ this.eventListeners.push({
3798
+ event: 'document-click',
3799
+ handler: documentClickHandler
3800
+ });
3801
+ }
3759
3802
  }
3760
3803
  /**
3761
3804
  * Parse value based on selection mode
@@ -3784,11 +3827,19 @@ class NileSelectEditor {
3784
3827
  if (this.select) {
3785
3828
  this.eventListeners.forEach(({ event, handler }) => {
3786
3829
  var _a;
3787
- (_a = this.select) === null || _a === void 0 ? void 0 : _a.removeEventListener(event, handler);
3830
+ if (event === 'document-click') {
3831
+ // Remove from document for multi-select click handler
3832
+ document.removeEventListener('click', handler, true);
3833
+ }
3834
+ else {
3835
+ // Remove from select element
3836
+ (_a = this.select) === null || _a === void 0 ? void 0 : _a.removeEventListener(event, handler);
3837
+ }
3788
3838
  });
3789
3839
  this.eventListeners = [];
3790
3840
  this.select.remove();
3791
3841
  this.select = undefined;
3842
+ this.portalDiv = undefined; // Clean up portal reference
3792
3843
  }
3793
3844
  }
3794
3845
  focus() {
@@ -4057,6 +4108,7 @@ class NileAutoCompleteEditor {
4057
4108
  class NileCalendarEditor {
4058
4109
  constructor(options) {
4059
4110
  this.options = options;
4111
+ this.acceptsInitialKeypress = false;
4060
4112
  this.eventListeners = [];
4061
4113
  }
4062
4114
  edit(context) {
@@ -4100,7 +4152,7 @@ class NileCalendarEditor {
4100
4152
  * Apply all configuration options to the NileCalendar element
4101
4153
  */
4102
4154
  applyOptions() {
4103
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
4155
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
4104
4156
  if (!this.calendar)
4105
4157
  return;
4106
4158
  // Date format (default: ISO format)
@@ -4160,10 +4212,6 @@ class NileCalendarEditor {
4160
4212
  if ((_s = this.options) === null || _s === void 0 ? void 0 : _s.size) {
4161
4213
  this.calendar.setAttribute('size', this.options.size);
4162
4214
  }
4163
- // Portal rendering (default: true)
4164
- if (((_t = this.options) === null || _t === void 0 ? void 0 : _t.portal) !== false) {
4165
- this.calendar.setAttribute('portal', 'true');
4166
- }
4167
4215
  }
4168
4216
  /**
4169
4217
  * Set up all event listeners with proper cleanup tracking
@@ -4268,7 +4316,7 @@ class NileCalendarEditor {
4268
4316
 
4269
4317
  /**
4270
4318
  * Custom editor using NileDatePicker from @aquera/nile-elements
4271
- * Supports both single date and range selection with slot-based trigger input
4319
+ * Supports both single date and range selection with slot-based trigger div
4272
4320
  * Returns string values for both modes:
4273
4321
  * - Single mode: 'YYYY-MM-DD'
4274
4322
  * - Range mode: 'YYYY-MM-DD - YYYY-MM-DD'
@@ -4283,10 +4331,11 @@ class NileCalendarEditor {
4283
4331
  class NileDatePickerEditor {
4284
4332
  constructor(options) {
4285
4333
  this.options = options;
4334
+ this.acceptsInitialKeypress = false;
4286
4335
  this.eventListeners = [];
4287
4336
  }
4288
4337
  edit(context) {
4289
- var _a, _b;
4338
+ var _a;
4290
4339
  if (!context.container) {
4291
4340
  console.warn('NileDatePickerEditor requires a container element');
4292
4341
  return;
@@ -4298,52 +4347,55 @@ class NileDatePickerEditor {
4298
4347
  this.datePicker.style.width = 'inherit';
4299
4348
  this.datePicker.style.height = 'inherit';
4300
4349
  this.datePicker.style.boxSizing = 'border-box';
4301
- // Create trigger input with slot using document.createElement
4302
- this.triggerInput = document.createElement('nile-input');
4303
- this.triggerInput.setAttribute('slot', 'trigger');
4350
+ // Create trigger div with slot
4351
+ this.triggerDiv = document.createElement('div');
4352
+ this.triggerDiv.setAttribute('slot', 'trigger');
4353
+ this.triggerDiv.setAttribute('contenteditable', 'false');
4354
+ this.triggerDiv.style.width = '100%';
4355
+ this.triggerDiv.style.minHeight = '28px';
4356
+ this.triggerDiv.style.cursor = 'pointer';
4357
+ this.triggerDiv.style.boxSizing = 'border-box';
4304
4358
  // Set initial value
4305
4359
  this.setInitialValue(context.value);
4306
- if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.label) {
4307
- this.triggerInput.label = this.options.label;
4308
- }
4309
4360
  // Apply configuration options
4310
4361
  this.applyOptions();
4311
4362
  // Append trigger to date picker (slot-based)
4312
- this.datePicker.appendChild(this.triggerInput);
4363
+ this.datePicker.appendChild(this.triggerDiv);
4313
4364
  // Set up event listeners
4314
4365
  this.setupEventListeners(context);
4315
4366
  // Append to container
4316
4367
  context.container.innerHTML = '';
4317
4368
  context.container.appendChild(this.datePicker);
4318
4369
  // Auto focus and open the date picker
4319
- if (((_b = this.options) === null || _b === void 0 ? void 0 : _b.autoFocus) !== false) {
4370
+ if (((_a = this.options) === null || _a === void 0 ? void 0 : _a.autoFocus) !== false) {
4320
4371
  setTimeout(() => {
4321
4372
  var _a, _b;
4322
- (_a = this.triggerInput) === null || _a === void 0 ? void 0 : _a.focus();
4373
+ (_a = this.triggerDiv) === null || _a === void 0 ? void 0 : _a.focus();
4323
4374
  // Trigger a click to open the date picker dropdown
4324
- (_b = this.triggerInput) === null || _b === void 0 ? void 0 : _b.click();
4375
+ (_b = this.triggerDiv) === null || _b === void 0 ? void 0 : _b.click();
4325
4376
  }, 0);
4326
4377
  }
4327
4378
  }
4328
4379
  /**
4329
- * Set initial value in the trigger input
4380
+ * Set initial value in the trigger div
4330
4381
  */
4331
4382
  setInitialValue(value) {
4332
- if (!this.triggerInput)
4383
+ if (!this.triggerDiv)
4333
4384
  return;
4334
4385
  if (!value) {
4335
- this.triggerInput.value = '';
4386
+ this.triggerDiv.textContent = '';
4336
4387
  return;
4337
4388
  }
4338
4389
  // Both single and range modes use string value
4339
- this.triggerInput.value = String(value);
4390
+ this.triggerDiv.textContent = String(value);
4340
4391
  }
4341
4392
  /**
4342
- * Apply all configuration options to both NileDatePicker and trigger input
4393
+ * Apply configuration options to the date picker
4394
+ * Note: Trigger is now a simple div, so input-specific options are not applied
4343
4395
  */
4344
4396
  applyOptions() {
4345
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
4346
- if (!this.datePicker || !this.triggerInput)
4397
+ var _a, _b, _c, _d;
4398
+ if (!this.datePicker)
4347
4399
  return;
4348
4400
  // Configure NileDatePicker
4349
4401
  const dateFormat = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.dateFormat) || 'YYYY-MM-DD';
@@ -4354,49 +4406,21 @@ class NileDatePickerEditor {
4354
4406
  if ((_c = this.options) === null || _c === void 0 ? void 0 : _c.syncDatePicker) {
4355
4407
  this.datePicker.setAttribute('syncDatePicker', String(this.options.syncDatePicker));
4356
4408
  }
4357
- // Configure trigger input
4358
- if ((_d = this.options) === null || _d === void 0 ? void 0 : _d.disabled) {
4359
- this.triggerInput.disabled = this.options.disabled;
4360
- }
4361
- if ((_e = this.options) === null || _e === void 0 ? void 0 : _e.clearable) {
4362
- this.triggerInput.clearable = this.options.clearable;
4363
- }
4364
- if ((_f = this.options) === null || _f === void 0 ? void 0 : _f.helpText) {
4365
- this.triggerInput.setAttribute('help-text', this.options.helpText);
4366
- }
4367
- // Visual states on trigger
4368
- if ((_g = this.options) === null || _g === void 0 ? void 0 : _g.warning) {
4369
- this.triggerInput.setAttribute('warning', String(this.options.warning));
4370
- }
4371
- if ((_h = this.options) === null || _h === void 0 ? void 0 : _h.error) {
4372
- this.triggerInput.setAttribute('error', String(this.options.error));
4373
- }
4374
- if ((_j = this.options) === null || _j === void 0 ? void 0 : _j.success) {
4375
- this.triggerInput.setAttribute('success', String(this.options.success));
4376
- }
4377
- if ((_k = this.options) === null || _k === void 0 ? void 0 : _k.errorMessage) {
4378
- this.triggerInput.setAttribute('error-message', this.options.errorMessage);
4379
- }
4380
- // Styling
4381
- if ((_l = this.options) === null || _l === void 0 ? void 0 : _l.size) {
4382
- this.triggerInput.setAttribute('size', String(this.options.size));
4383
- }
4384
- if ((_m = this.options) === null || _m === void 0 ? void 0 : _m.filled) {
4385
- this.triggerInput.setAttribute('filled', String(this.options.filled));
4386
- }
4387
- if ((_o = this.options) === null || _o === void 0 ? void 0 : _o.pill) {
4388
- this.triggerInput.setAttribute('pill', String(this.options.pill));
4409
+ // Trigger div styling (disabled state can be indicated with opacity)
4410
+ if (this.triggerDiv && ((_d = this.options) === null || _d === void 0 ? void 0 : _d.disabled)) {
4411
+ this.triggerDiv.style.opacity = '0.5';
4412
+ this.triggerDiv.style.pointerEvents = 'none';
4389
4413
  }
4390
4414
  }
4391
4415
  /**
4392
4416
  * Set up all event listeners with proper cleanup tracking
4393
4417
  */
4394
4418
  setupEventListeners(context) {
4395
- var _a, _b;
4396
- if (!this.datePicker || !this.triggerInput)
4419
+ var _a;
4420
+ if (!this.datePicker || !this.triggerDiv)
4397
4421
  return;
4398
4422
  const validateOnSave = ((_a = this.options) === null || _a === void 0 ? void 0 : _a.validateOnSave) !== false;
4399
- // Keyboard events on trigger input
4423
+ // Keyboard events on trigger div
4400
4424
  const keydownHandler = (e) => {
4401
4425
  const keyEvent = e;
4402
4426
  if (keyEvent.key === 'Enter') {
@@ -4414,8 +4438,12 @@ class NileDatePickerEditor {
4414
4438
  keyEvent.stopPropagation();
4415
4439
  this.saveValue(context, validateOnSave);
4416
4440
  }
4441
+ else {
4442
+ // Prevent all other keypresses from typing into the div
4443
+ keyEvent.preventDefault();
4444
+ }
4417
4445
  };
4418
- this.triggerInput.addEventListener('keydown', keydownHandler);
4446
+ this.triggerDiv.addEventListener('keydown', keydownHandler);
4419
4447
  this.eventListeners.push({ event: 'keydown', handler: keydownHandler });
4420
4448
  // nile-change event on date picker
4421
4449
  const changeHandler = (e) => {
@@ -4435,26 +4463,6 @@ class NileDatePickerEditor {
4435
4463
  };
4436
4464
  this.datePicker.addEventListener('nile-change', changeHandler);
4437
4465
  this.eventListeners.push({ event: 'nile-change', handler: changeHandler });
4438
- // nile-blur event on trigger input
4439
- const blurHandler = (e) => {
4440
- var _a;
4441
- const customEvent = e;
4442
- // For blur, get value from trigger input directly
4443
- const inputValue = ((_a = this.triggerInput) === null || _a === void 0 ? void 0 : _a.value) || '';
4444
- if (inputValue) {
4445
- context.onSave(this.parseValue(inputValue));
4446
- }
4447
- };
4448
- this.triggerInput.addEventListener('nile-blur', blurHandler);
4449
- this.eventListeners.push({ event: 'nile-blur', handler: blurHandler });
4450
- // nile-clear event
4451
- if ((_b = this.options) === null || _b === void 0 ? void 0 : _b.clearable) {
4452
- const clearHandler = (e) => {
4453
- context.onSave('');
4454
- };
4455
- this.triggerInput.addEventListener('nile-clear', clearHandler);
4456
- this.eventListeners.push({ event: 'nile-clear', handler: clearHandler });
4457
- }
4458
4466
  }
4459
4467
  /**
4460
4468
  * Save value with optional validation
@@ -4475,29 +4483,29 @@ class NileDatePickerEditor {
4475
4483
  }
4476
4484
  getCurrentValue() {
4477
4485
  var _a;
4478
- if (!this.triggerInput) {
4486
+ if (!this.triggerDiv) {
4479
4487
  return '';
4480
4488
  }
4481
- return this.parseValue((_a = this.triggerInput.value) !== null && _a !== void 0 ? _a : '');
4489
+ return this.parseValue((_a = this.triggerDiv.textContent) !== null && _a !== void 0 ? _a : '');
4482
4490
  }
4483
4491
  destroy() {
4484
- if (this.datePicker || this.triggerInput) {
4492
+ if (this.datePicker || this.triggerDiv) {
4485
4493
  this.eventListeners.forEach(({ event, handler }) => {
4486
4494
  var _a, _b;
4487
4495
  (_a = this.datePicker) === null || _a === void 0 ? void 0 : _a.removeEventListener(event, handler);
4488
- (_b = this.triggerInput) === null || _b === void 0 ? void 0 : _b.removeEventListener(event, handler);
4496
+ (_b = this.triggerDiv) === null || _b === void 0 ? void 0 : _b.removeEventListener(event, handler);
4489
4497
  });
4490
4498
  this.eventListeners = [];
4491
4499
  if (this.datePicker) {
4492
4500
  this.datePicker.remove();
4493
4501
  this.datePicker = undefined;
4494
4502
  }
4495
- this.triggerInput = undefined;
4503
+ this.triggerDiv = undefined;
4496
4504
  }
4497
4505
  }
4498
4506
  focus() {
4499
4507
  var _a;
4500
- (_a = this.triggerInput) === null || _a === void 0 ? void 0 : _a.focus();
4508
+ (_a = this.triggerDiv) === null || _a === void 0 ? void 0 : _a.focus();
4501
4509
  }
4502
4510
  }
4503
4511
 
@@ -4856,12 +4864,18 @@ class StCellComponent {
4856
4864
  startEditWithKey(key) {
4857
4865
  const canStart = this.cell.startEdit();
4858
4866
  if (canStart) {
4859
- // Replace current value with the key
4860
- this.cell.setValue(key, false);
4861
4867
  const column = this.cell.getColumnConfig();
4868
+ const editor = column.editor;
4869
+ // Only set initial keypress value if editor accepts it
4870
+ // Default to true for backward compatibility with existing editors
4871
+ const acceptsKeypress = (editor === null || editor === void 0 ? void 0 : editor.acceptsInitialKeypress) !== false;
4872
+ if (acceptsKeypress) {
4873
+ // Replace current value with the key
4874
+ this.cell.setValue(key, false);
4875
+ }
4862
4876
  const editEvent = {
4863
4877
  cell: this.cell,
4864
- value: key,
4878
+ value: acceptsKeypress ? key : this.cell.getValue(),
4865
4879
  rowData: this.cell.getRowData(),
4866
4880
  columnKey: column.key,
4867
4881
  rowIndex: this.cell.getRowIndex(),
@@ -6800,10 +6814,10 @@ class StColumnMenuDropdownComponent {
6800
6814
  }
6801
6815
  }
6802
6816
  StColumnMenuDropdownComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StColumnMenuDropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6803
- 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"] }] });
6817
+ 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"] }] });
6804
6818
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StColumnMenuDropdownComponent, decorators: [{
6805
6819
  type: Component,
6806
- 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"] }]
6820
+ 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"] }]
6807
6821
  }], propDecorators: { isOpen: [{
6808
6822
  type: Input
6809
6823
  }], position: [{
@@ -6986,370 +7000,70 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
6986
7000
  }] } });
6987
7001
 
6988
7002
  /**
6989
- * Column Editor Component
6990
- * Form-based column configuration
7003
+ * st-header Component
7004
+ * Encapsulates column-specific logic including sorting and filtering
6991
7005
  */
6992
- class ColumnEditorComponent {
6993
- constructor(fb) {
6994
- this.fb = fb;
6995
- this.columnUpdated = new EventEmitter();
6996
- this.cancel = new EventEmitter();
6997
- this.dataTypes = [
6998
- { value: CellDataType.STRING, label: 'Text' },
6999
- { value: CellDataType.NUMBER, label: 'Number' },
7000
- { value: CellDataType.DATE, label: 'Date' },
7001
- { value: CellDataType.BOOLEAN, label: 'Boolean' },
7002
- { value: CellDataType.CUSTOM, label: 'Custom' }
7003
- ];
7004
- this.alignments = [
7005
- { value: CellAlignment.LEFT, label: 'Left' },
7006
- { value: CellAlignment.CENTER, label: 'Center' },
7007
- { value: CellAlignment.RIGHT, label: 'Right' }
7008
- ];
7009
- this.verticalAlignments = [
7010
- { value: CellVerticalAlignment.TOP, label: 'Top' },
7011
- { value: CellVerticalAlignment.MIDDLE, label: 'Middle' },
7012
- { value: CellVerticalAlignment.BOTTOM, label: 'Bottom' }
7013
- ];
7014
- this.editModes = [
7015
- { value: EditMode.CLICK, label: 'Click' },
7016
- { value: EditMode.MANUAL, label: 'Manual' },
7017
- { value: EditMode.NONE, label: 'None' }
7018
- ];
7019
- this.stickyOptions = [
7020
- { value: false, label: 'None' },
7021
- { value: 'left', label: 'Left' },
7022
- { value: 'right', label: 'Right' }
7023
- ];
7024
- }
7025
- ngOnInit() {
7026
- this.buildForm();
7027
- }
7028
- ngOnChanges(changes) {
7029
- if (changes['column'] && !changes['column'].firstChange && this.form) {
7030
- this.updateForm();
7031
- }
7006
+ class StHeaderComponent {
7007
+ constructor() {
7008
+ /**
7009
+ * Column index in the visible columns array
7010
+ */
7011
+ this.columnIndex = 0;
7012
+ /**
7013
+ * Whether this is the first column
7014
+ */
7015
+ this.isFirstColumn = false;
7016
+ /**
7017
+ * Whether this is the last column
7018
+ */
7019
+ this.isLastColumn = false;
7020
+ /**
7021
+ * Whether sorting is enabled globally
7022
+ */
7023
+ this.enableSorting = true;
7024
+ /**
7025
+ * Whether filtering is enabled globally
7026
+ */
7027
+ this.enableFiltering = true;
7028
+ /**
7029
+ * Emits column sort event when sort is toggled
7030
+ */
7031
+ this.sortToggle = new EventEmitter();
7032
+ /**
7033
+ * Emits filter changes (future feature)
7034
+ */
7035
+ this.filterChange = new EventEmitter();
7036
+ /**
7037
+ * Emits when column is moved left or right
7038
+ */
7039
+ this.columnMoved = new EventEmitter();
7040
+ /**
7041
+ * Emits when column menu button is clicked
7042
+ */
7043
+ this.menuClick = new EventEmitter();
7032
7044
  }
7033
7045
  /**
7034
- * Build the reactive form
7046
+ * Cleanup on destroy
7035
7047
  */
7036
- buildForm() {
7037
- var _a;
7038
- this.form = this.fb.group({
7039
- // Basic properties
7040
- key: [this.column.key, [Validators.required, Validators.pattern(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/)]],
7041
- header: [this.column.header || ''],
7042
- dataType: [this.column.dataType || CellDataType.STRING],
7043
- // Feature flags
7044
- editable: [this.column.editable !== false],
7045
- sortable: [this.column.sortable !== false],
7046
- filterable: [this.column.filterable !== false],
7047
- resizable: [this.column.resizable !== false],
7048
- hideable: [this.column.hideable !== false],
7049
- movable: [this.column.movable !== false],
7050
- pinnable: [this.column.pinnable !== false],
7051
- enableMenu: [this.column.enableMenu !== false],
7052
- // Layout
7053
- width: [this.column.width || ''],
7054
- minWidth: [this.column.minWidth || ''],
7055
- maxWidth: [this.column.maxWidth || ''],
7056
- sticky: [this.column.sticky || false],
7057
- alignment: [this.column.alignment || CellAlignment.LEFT],
7058
- verticalAlignment: [this.column.verticalAlignment || CellVerticalAlignment.MIDDLE],
7059
- // Edit mode
7060
- editMode: [this.column.editMode || EditMode.CLICK],
7061
- // Display
7062
- visible: [this.column.visible !== false],
7063
- truncate: [this.column.truncate || false]
7064
- });
7065
- // Subscribe to hideable changes to enforce visible=true when hideable=false
7066
- (_a = this.form.get('hideable')) === null || _a === void 0 ? void 0 : _a.valueChanges.subscribe(hideable => {
7067
- var _a;
7068
- if (!hideable) {
7069
- (_a = this.form.get('visible')) === null || _a === void 0 ? void 0 : _a.setValue(true, { emitEvent: false });
7070
- }
7071
- });
7072
- // Update form when column changes
7073
- this.updateForm();
7048
+ ngOnDestroy() {
7049
+ // No cleanup needed - resize is now handled by directive
7074
7050
  }
7075
7051
  /**
7076
- * Update form values from column
7052
+ * Handle column menu button click
7077
7053
  */
7078
- updateForm() {
7079
- if (!this.form || !this.column)
7080
- return;
7081
- this.form.patchValue({
7082
- key: this.column.key,
7083
- header: this.column.header || '',
7084
- dataType: this.column.dataType || CellDataType.STRING,
7085
- editable: this.column.editable !== false,
7086
- sortable: this.column.sortable !== false,
7087
- filterable: this.column.filterable !== false,
7088
- resizable: this.column.resizable !== false,
7089
- hideable: this.column.hideable !== false,
7090
- movable: this.column.movable !== false,
7091
- pinnable: this.column.pinnable !== false,
7092
- enableMenu: this.column.enableMenu !== false,
7093
- width: this.column.width || '',
7094
- minWidth: this.column.minWidth || '',
7095
- maxWidth: this.column.maxWidth || '',
7096
- sticky: this.column.sticky || false,
7097
- alignment: this.column.alignment || CellAlignment.LEFT,
7098
- verticalAlignment: this.column.verticalAlignment || CellVerticalAlignment.MIDDLE,
7099
- editMode: this.column.editMode || EditMode.CLICK,
7100
- visible: this.column.visible !== false,
7101
- truncate: this.column.truncate || false
7102
- }, { emitEvent: false });
7054
+ onMenuClick(event) {
7055
+ event.preventDefault();
7056
+ event.stopPropagation();
7057
+ this.menuClick.emit(event);
7103
7058
  }
7104
7059
  /**
7105
- * Handle form submission
7060
+ * Check if column menu should be shown
7106
7061
  */
7107
- onSave() {
7108
- if (this.form.valid) {
7109
- const formValue = this.form.value;
7110
- const updates = {
7111
- key: formValue.key,
7112
- header: formValue.header || undefined,
7113
- dataType: formValue.dataType,
7114
- editable: formValue.editable,
7115
- sortable: formValue.sortable,
7116
- filterable: formValue.filterable,
7117
- resizable: formValue.resizable,
7118
- hideable: formValue.hideable,
7119
- movable: formValue.movable,
7120
- pinnable: formValue.pinnable,
7121
- enableMenu: formValue.enableMenu,
7122
- width: formValue.width || undefined,
7123
- minWidth: formValue.minWidth || undefined,
7124
- maxWidth: formValue.maxWidth || undefined,
7125
- sticky: formValue.sticky || undefined,
7126
- alignment: formValue.alignment,
7127
- verticalAlignment: formValue.verticalAlignment,
7128
- editMode: formValue.editMode,
7129
- visible: formValue.visible,
7130
- truncate: formValue.truncate
7131
- };
7132
- // Convert empty strings to undefined
7133
- Object.keys(updates).forEach(key => {
7134
- const value = updates[key];
7135
- if (value === '' || value === null) {
7136
- updates[key] = undefined;
7137
- }
7138
- });
7139
- this.columnUpdated.emit(updates);
7140
- }
7141
- else {
7142
- // Mark all fields as touched to show validation errors
7143
- Object.keys(this.form.controls).forEach(key => {
7144
- var _a;
7145
- (_a = this.form.get(key)) === null || _a === void 0 ? void 0 : _a.markAsTouched();
7146
- });
7147
- }
7062
+ isMenuEnabled() {
7063
+ return this.column.enableMenu !== false;
7148
7064
  }
7149
7065
  /**
7150
- * Handle cancel
7151
- */
7152
- onCancel() {
7153
- this.cancel.emit();
7154
- }
7155
- /**
7156
- * Get form control error message
7157
- */
7158
- getErrorMessage(controlName) {
7159
- const control = this.form.get(controlName);
7160
- if (control === null || control === void 0 ? void 0 : control.hasError('required')) {
7161
- return 'This field is required';
7162
- }
7163
- if (control === null || control === void 0 ? void 0 : control.hasError('pattern')) {
7164
- return 'Invalid format';
7165
- }
7166
- return '';
7167
- }
7168
- /**
7169
- * Check if form control has error
7170
- */
7171
- hasError(controlName) {
7172
- const control = this.form.get(controlName);
7173
- return !!(control && control.invalid && (control.dirty || control.touched));
7174
- }
7175
- }
7176
- 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 });
7177
- 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]" }] });
7178
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ColumnEditorComponent, decorators: [{
7179
- type: Component,
7180
- 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"] }]
7181
- }], ctorParameters: function () { return [{ type: i1$1.FormBuilder }]; }, propDecorators: { column: [{
7182
- type: Input
7183
- }], columnIndex: [{
7184
- type: Input
7185
- }], columnUpdated: [{
7186
- type: Output
7187
- }], cancel: [{
7188
- type: Output
7189
- }] } });
7190
-
7191
- class StColumnEditorModalComponent {
7192
- constructor() {
7193
- this.columnCreated = new EventEmitter();
7194
- this.cancelled = new EventEmitter();
7195
- this.columnIndex = -1;
7196
- }
7197
- ngOnInit() {
7198
- // Create a default column that will be edited
7199
- this.newColumn = this.createDefaultColumn();
7200
- }
7201
- ngOnDestroy() {
7202
- // Cleanup if needed
7203
- }
7204
- /**
7205
- * Create a default column configuration
7206
- */
7207
- createDefaultColumn() {
7208
- return ColumnConfigFactory.text('newColumn', {
7209
- header: 'New Column',
7210
- editable: true,
7211
- sortable: true,
7212
- filterable: true,
7213
- resizable: true,
7214
- hideable: true,
7215
- visible: true
7216
- });
7217
- }
7218
- /**
7219
- * Trigger save from external button
7220
- */
7221
- onSave() {
7222
- // Call the column editor's save method
7223
- if (this.columnEditor) {
7224
- this.columnEditor.onSave();
7225
- }
7226
- }
7227
- /**
7228
- * Handle column updates from the editor
7229
- */
7230
- onColumnUpdated(updates) {
7231
- // Merge updates into the new column
7232
- this.newColumn = Object.assign(Object.assign({}, this.newColumn), updates);
7233
- // Use ColumnConfigFactory to create proper column based on data type
7234
- let finalColumn;
7235
- const key = updates.key || this.newColumn.key;
7236
- const dataType = updates.dataType || this.newColumn.dataType;
7237
- switch (dataType) {
7238
- case CellDataType.NUMBER:
7239
- finalColumn = ColumnConfigFactory.number(key, this.newColumn);
7240
- break;
7241
- case CellDataType.DATE:
7242
- finalColumn = ColumnConfigFactory.date(key, this.newColumn);
7243
- break;
7244
- case CellDataType.BOOLEAN:
7245
- finalColumn = ColumnConfigFactory.boolean(key, this.newColumn);
7246
- break;
7247
- default:
7248
- finalColumn = ColumnConfigFactory.text(key, this.newColumn);
7249
- }
7250
- // Emit the created column
7251
- this.columnCreated.emit(finalColumn);
7252
- }
7253
- /**
7254
- * Handle cancel from the editor
7255
- */
7256
- onCancel() {
7257
- this.cancelled.emit();
7258
- }
7259
- /**
7260
- * Handle backdrop click to close modal
7261
- */
7262
- onBackdropClick(event) {
7263
- if (event.target === event.currentTarget) {
7264
- this.cancelled.emit();
7265
- }
7266
- }
7267
- /**
7268
- * Prevent event propagation from modal content
7269
- */
7270
- onModalContentClick(event) {
7271
- event.stopPropagation();
7272
- }
7273
- }
7274
- StColumnEditorModalComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StColumnEditorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7275
- 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"] }] });
7276
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StColumnEditorModalComponent, decorators: [{
7277
- type: Component,
7278
- 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"] }]
7279
- }], propDecorators: { columnCreated: [{
7280
- type: Output
7281
- }], cancelled: [{
7282
- type: Output
7283
- }], columnEditor: [{
7284
- type: ViewChild,
7285
- args: [ColumnEditorComponent]
7286
- }] } });
7287
-
7288
- /**
7289
- * st-header Component
7290
- * Encapsulates column-specific logic including sorting and filtering
7291
- */
7292
- class StHeaderComponent {
7293
- constructor() {
7294
- /**
7295
- * Column index in the visible columns array
7296
- */
7297
- this.columnIndex = 0;
7298
- /**
7299
- * Whether this is the first column
7300
- */
7301
- this.isFirstColumn = false;
7302
- /**
7303
- * Whether this is the last column
7304
- */
7305
- this.isLastColumn = false;
7306
- /**
7307
- * Whether sorting is enabled globally
7308
- */
7309
- this.enableSorting = true;
7310
- /**
7311
- * Whether filtering is enabled globally
7312
- */
7313
- this.enableFiltering = true;
7314
- /**
7315
- * Emits column sort event when sort is toggled
7316
- */
7317
- this.sortToggle = new EventEmitter();
7318
- /**
7319
- * Emits filter changes (future feature)
7320
- */
7321
- this.filterChange = new EventEmitter();
7322
- /**
7323
- * Emits when column is moved left or right
7324
- */
7325
- this.columnMoved = new EventEmitter();
7326
- /**
7327
- * Emits when column menu button is clicked
7328
- */
7329
- this.menuClick = new EventEmitter();
7330
- }
7331
- /**
7332
- * Cleanup on destroy
7333
- */
7334
- ngOnDestroy() {
7335
- // No cleanup needed - resize is now handled by directive
7336
- }
7337
- /**
7338
- * Handle column menu button click
7339
- */
7340
- onMenuClick(event) {
7341
- event.preventDefault();
7342
- event.stopPropagation();
7343
- this.menuClick.emit(event);
7344
- }
7345
- /**
7346
- * Check if column menu should be shown
7347
- */
7348
- isMenuEnabled() {
7349
- return this.column.enableMenu !== false;
7350
- }
7351
- /**
7352
- * Check if column is sortable
7066
+ * Check if column is sortable
7353
7067
  */
7354
7068
  isSortable() {
7355
7069
  return this.column.sortable !== false && this.enableSorting;
@@ -7512,25 +7226,22 @@ class StTableActionsComponent {
7512
7226
  updateDropdownPosition(event) {
7513
7227
  const button = event.currentTarget;
7514
7228
  const rect = button.getBoundingClientRect();
7515
- const viewportWidth = window.innerWidth;
7516
- const viewportHeight = window.innerHeight;
7517
- const dropdownWidth = 280;
7518
- const dropdownHeight = 400;
7519
- let x = rect.left;
7229
+ const dropdownWidth = 220;
7230
+ let x = rect.right;
7520
7231
  let y = rect.bottom + 5;
7521
- // Adjust horizontal position if dropdown would overflow
7522
- if (x + dropdownWidth > viewportWidth) {
7523
- x = Math.max(10, viewportWidth - dropdownWidth - 10);
7524
- }
7525
- // Adjust vertical position if dropdown would overflow
7526
- if (y + dropdownHeight > viewportHeight) {
7527
- y = rect.top - dropdownHeight - 5;
7528
- }
7232
+ // // Adjust horizontal position if dropdown would overflow
7233
+ // if (x + dropdownWidth > viewportWidth) {
7234
+ // x = Math.max(10, viewportWidth - dropdownWidth - 10);
7235
+ // }
7236
+ // // Adjust vertical position if dropdown would overflow
7237
+ // if (y + dropdownHeight > viewportHeight) {
7238
+ // y = rect.top - dropdownHeight - 5;
7239
+ // }
7529
7240
  this.dropdownStyle = {
7530
- position: 'fixed',
7531
- left: `${x}px`,
7241
+ position: 'absolute',
7242
+ left: `${x - dropdownWidth}px`,
7532
7243
  top: `${y}px`,
7533
- 'z-index': 9999
7244
+ 'z-index': TableZIndex.TABLE_ACTIONS_DROPDOWN
7534
7245
  };
7535
7246
  }
7536
7247
  /**
@@ -7900,7 +7611,7 @@ class StTableComponent {
7900
7611
  }
7901
7612
  }
7902
7613
  ngOnChanges(changes) {
7903
- var _a;
7614
+ var _a, _b;
7904
7615
  // Validate configuration when tableConfig changes
7905
7616
  if (changes['tableConfig'] && !changes['tableConfig'].firstChange && this.tableConfig) {
7906
7617
  this.mergedConfig = mergeTableConfig(this.tableConfig);
@@ -7935,8 +7646,17 @@ class StTableComponent {
7935
7646
  // Update visible columns and cell grid
7936
7647
  this.updateVisibleData();
7937
7648
  }
7649
+ // Handle tableState input changes (when external state is provided, e.g., in workbook)
7650
+ if (changes['tableState'] && !changes['tableState'].firstChange && this.tableState) {
7651
+ // Unsubscribe from old tableState's focus observable to prevent memory leaks
7652
+ (_a = this.focusSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
7653
+ // Reinitialize keyboard navigation with new tableState
7654
+ if (this.isKeyboardNavigationEnabled()) {
7655
+ this.initializeKeyboardNavigation();
7656
+ }
7657
+ }
7938
7658
  // Recalculate virtual scroll when data changes
7939
- if ((changes['data'] || changes['cellGrid']) && !((_a = changes['data']) === null || _a === void 0 ? void 0 : _a.firstChange)) {
7659
+ if ((changes['data'] || changes['cellGrid']) && !((_b = changes['data']) === null || _b === void 0 ? void 0 : _b.firstChange)) {
7940
7660
  if (this.isVirtualScrollEnabled()) {
7941
7661
  const currentState = this.virtualScrollState$.value;
7942
7662
  if (currentState) {
@@ -8976,10 +8696,10 @@ class StTableComponent {
8976
8696
  }
8977
8697
  }
8978
8698
  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 });
8979
- 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 } });
8699
+ 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 } });
8980
8700
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StTableComponent, decorators: [{
8981
8701
  type: Component,
8982
- 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"] }]
8702
+ 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"] }]
8983
8703
  }], ctorParameters: function () { return [{ type: JsonSchemaValidatorService }, { type: ValidationLoggerService }, { type: VirtualScrollService }]; }, propDecorators: { tableConfig: [{
8984
8704
  type: Input
8985
8705
  }], data: [{
@@ -9168,6 +8888,306 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
9168
8888
  type: Output
9169
8889
  }] } });
9170
8890
 
8891
+ /**
8892
+ * Column Editor Component
8893
+ * Form-based column configuration
8894
+ */
8895
+ class ColumnEditorComponent {
8896
+ constructor(fb) {
8897
+ this.fb = fb;
8898
+ this.columnUpdated = new EventEmitter();
8899
+ this.cancel = new EventEmitter();
8900
+ this.dataTypes = [
8901
+ { value: CellDataType.STRING, label: 'Text' },
8902
+ { value: CellDataType.NUMBER, label: 'Number' },
8903
+ { value: CellDataType.DATE, label: 'Date' },
8904
+ { value: CellDataType.BOOLEAN, label: 'Boolean' },
8905
+ { value: CellDataType.CUSTOM, label: 'Custom' }
8906
+ ];
8907
+ this.alignments = [
8908
+ { value: CellAlignment.LEFT, label: 'Left' },
8909
+ { value: CellAlignment.CENTER, label: 'Center' },
8910
+ { value: CellAlignment.RIGHT, label: 'Right' }
8911
+ ];
8912
+ this.verticalAlignments = [
8913
+ { value: CellVerticalAlignment.TOP, label: 'Top' },
8914
+ { value: CellVerticalAlignment.MIDDLE, label: 'Middle' },
8915
+ { value: CellVerticalAlignment.BOTTOM, label: 'Bottom' }
8916
+ ];
8917
+ this.editModes = [
8918
+ { value: EditMode.CLICK, label: 'Click' },
8919
+ { value: EditMode.MANUAL, label: 'Manual' },
8920
+ { value: EditMode.NONE, label: 'None' }
8921
+ ];
8922
+ this.stickyOptions = [
8923
+ { value: false, label: 'None' },
8924
+ { value: 'left', label: 'Left' },
8925
+ { value: 'right', label: 'Right' }
8926
+ ];
8927
+ }
8928
+ ngOnInit() {
8929
+ this.buildForm();
8930
+ }
8931
+ ngOnChanges(changes) {
8932
+ if (changes['column'] && !changes['column'].firstChange && this.form) {
8933
+ this.updateForm();
8934
+ }
8935
+ }
8936
+ /**
8937
+ * Build the reactive form
8938
+ */
8939
+ buildForm() {
8940
+ var _a;
8941
+ this.form = this.fb.group({
8942
+ // Basic properties
8943
+ key: [this.column.key, [Validators.required, Validators.pattern(/^[a-zA-Z_$][a-zA-Z0-9_$]*$/)]],
8944
+ header: [this.column.header || ''],
8945
+ dataType: [this.column.dataType || CellDataType.STRING],
8946
+ // Feature flags
8947
+ editable: [this.column.editable !== false],
8948
+ sortable: [this.column.sortable !== false],
8949
+ filterable: [this.column.filterable !== false],
8950
+ resizable: [this.column.resizable !== false],
8951
+ hideable: [this.column.hideable !== false],
8952
+ movable: [this.column.movable !== false],
8953
+ pinnable: [this.column.pinnable !== false],
8954
+ enableMenu: [this.column.enableMenu !== false],
8955
+ // Layout
8956
+ width: [this.column.width || ''],
8957
+ minWidth: [this.column.minWidth || ''],
8958
+ maxWidth: [this.column.maxWidth || ''],
8959
+ sticky: [this.column.sticky || false],
8960
+ alignment: [this.column.alignment || CellAlignment.LEFT],
8961
+ verticalAlignment: [this.column.verticalAlignment || CellVerticalAlignment.MIDDLE],
8962
+ // Edit mode
8963
+ editMode: [this.column.editMode || EditMode.CLICK],
8964
+ // Display
8965
+ visible: [this.column.visible !== false],
8966
+ truncate: [this.column.truncate || false]
8967
+ });
8968
+ // Subscribe to hideable changes to enforce visible=true when hideable=false
8969
+ (_a = this.form.get('hideable')) === null || _a === void 0 ? void 0 : _a.valueChanges.subscribe(hideable => {
8970
+ var _a;
8971
+ if (!hideable) {
8972
+ (_a = this.form.get('visible')) === null || _a === void 0 ? void 0 : _a.setValue(true, { emitEvent: false });
8973
+ }
8974
+ });
8975
+ // Update form when column changes
8976
+ this.updateForm();
8977
+ }
8978
+ /**
8979
+ * Update form values from column
8980
+ */
8981
+ updateForm() {
8982
+ if (!this.form || !this.column)
8983
+ return;
8984
+ this.form.patchValue({
8985
+ key: this.column.key,
8986
+ header: this.column.header || '',
8987
+ dataType: this.column.dataType || CellDataType.STRING,
8988
+ editable: this.column.editable !== false,
8989
+ sortable: this.column.sortable !== false,
8990
+ filterable: this.column.filterable !== false,
8991
+ resizable: this.column.resizable !== false,
8992
+ hideable: this.column.hideable !== false,
8993
+ movable: this.column.movable !== false,
8994
+ pinnable: this.column.pinnable !== false,
8995
+ enableMenu: this.column.enableMenu !== false,
8996
+ width: this.column.width || '',
8997
+ minWidth: this.column.minWidth || '',
8998
+ maxWidth: this.column.maxWidth || '',
8999
+ sticky: this.column.sticky || false,
9000
+ alignment: this.column.alignment || CellAlignment.LEFT,
9001
+ verticalAlignment: this.column.verticalAlignment || CellVerticalAlignment.MIDDLE,
9002
+ editMode: this.column.editMode || EditMode.CLICK,
9003
+ visible: this.column.visible !== false,
9004
+ truncate: this.column.truncate || false
9005
+ }, { emitEvent: false });
9006
+ }
9007
+ /**
9008
+ * Handle form submission
9009
+ */
9010
+ onSave() {
9011
+ if (this.form.valid) {
9012
+ const formValue = this.form.value;
9013
+ const updates = {
9014
+ key: formValue.key,
9015
+ header: formValue.header || undefined,
9016
+ dataType: formValue.dataType,
9017
+ editable: formValue.editable,
9018
+ sortable: formValue.sortable,
9019
+ filterable: formValue.filterable,
9020
+ resizable: formValue.resizable,
9021
+ hideable: formValue.hideable,
9022
+ movable: formValue.movable,
9023
+ pinnable: formValue.pinnable,
9024
+ enableMenu: formValue.enableMenu,
9025
+ width: formValue.width || undefined,
9026
+ minWidth: formValue.minWidth || undefined,
9027
+ maxWidth: formValue.maxWidth || undefined,
9028
+ sticky: formValue.sticky || undefined,
9029
+ alignment: formValue.alignment,
9030
+ verticalAlignment: formValue.verticalAlignment,
9031
+ editMode: formValue.editMode,
9032
+ visible: formValue.visible,
9033
+ truncate: formValue.truncate
9034
+ };
9035
+ // Convert empty strings to undefined
9036
+ Object.keys(updates).forEach(key => {
9037
+ const value = updates[key];
9038
+ if (value === '' || value === null) {
9039
+ updates[key] = undefined;
9040
+ }
9041
+ });
9042
+ this.columnUpdated.emit(updates);
9043
+ }
9044
+ else {
9045
+ // Mark all fields as touched to show validation errors
9046
+ Object.keys(this.form.controls).forEach(key => {
9047
+ var _a;
9048
+ (_a = this.form.get(key)) === null || _a === void 0 ? void 0 : _a.markAsTouched();
9049
+ });
9050
+ }
9051
+ }
9052
+ /**
9053
+ * Handle cancel
9054
+ */
9055
+ onCancel() {
9056
+ this.cancel.emit();
9057
+ }
9058
+ /**
9059
+ * Get form control error message
9060
+ */
9061
+ getErrorMessage(controlName) {
9062
+ const control = this.form.get(controlName);
9063
+ if (control === null || control === void 0 ? void 0 : control.hasError('required')) {
9064
+ return 'This field is required';
9065
+ }
9066
+ if (control === null || control === void 0 ? void 0 : control.hasError('pattern')) {
9067
+ return 'Invalid format';
9068
+ }
9069
+ return '';
9070
+ }
9071
+ /**
9072
+ * Check if form control has error
9073
+ */
9074
+ hasError(controlName) {
9075
+ const control = this.form.get(controlName);
9076
+ return !!(control && control.invalid && (control.dirty || control.touched));
9077
+ }
9078
+ }
9079
+ 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 });
9080
+ 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]" }] });
9081
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: ColumnEditorComponent, decorators: [{
9082
+ type: Component,
9083
+ 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"] }]
9084
+ }], ctorParameters: function () { return [{ type: i1$1.FormBuilder }]; }, propDecorators: { column: [{
9085
+ type: Input
9086
+ }], columnIndex: [{
9087
+ type: Input
9088
+ }], columnUpdated: [{
9089
+ type: Output
9090
+ }], cancel: [{
9091
+ type: Output
9092
+ }] } });
9093
+
9094
+ class StColumnEditorModalComponent {
9095
+ constructor() {
9096
+ this.columnCreated = new EventEmitter();
9097
+ this.cancelled = new EventEmitter();
9098
+ this.columnIndex = -1;
9099
+ }
9100
+ ngOnInit() {
9101
+ // Create a default column that will be edited
9102
+ this.newColumn = this.createDefaultColumn();
9103
+ }
9104
+ ngOnDestroy() {
9105
+ // Cleanup if needed
9106
+ }
9107
+ /**
9108
+ * Create a default column configuration
9109
+ */
9110
+ createDefaultColumn() {
9111
+ return ColumnConfigFactory.text('newColumn', {
9112
+ header: 'New Column',
9113
+ editable: true,
9114
+ sortable: true,
9115
+ filterable: true,
9116
+ resizable: true,
9117
+ hideable: true,
9118
+ visible: true
9119
+ });
9120
+ }
9121
+ /**
9122
+ * Trigger save from external button
9123
+ */
9124
+ onSave() {
9125
+ // Call the column editor's save method
9126
+ if (this.columnEditor) {
9127
+ this.columnEditor.onSave();
9128
+ }
9129
+ }
9130
+ /**
9131
+ * Handle column updates from the editor
9132
+ */
9133
+ onColumnUpdated(updates) {
9134
+ // Merge updates into the new column
9135
+ this.newColumn = Object.assign(Object.assign({}, this.newColumn), updates);
9136
+ // Use ColumnConfigFactory to create proper column based on data type
9137
+ let finalColumn;
9138
+ const key = updates.key || this.newColumn.key;
9139
+ const dataType = updates.dataType || this.newColumn.dataType;
9140
+ switch (dataType) {
9141
+ case CellDataType.NUMBER:
9142
+ finalColumn = ColumnConfigFactory.number(key, this.newColumn);
9143
+ break;
9144
+ case CellDataType.DATE:
9145
+ finalColumn = ColumnConfigFactory.date(key, this.newColumn);
9146
+ break;
9147
+ case CellDataType.BOOLEAN:
9148
+ finalColumn = ColumnConfigFactory.boolean(key, this.newColumn);
9149
+ break;
9150
+ default:
9151
+ finalColumn = ColumnConfigFactory.text(key, this.newColumn);
9152
+ }
9153
+ // Emit the created column
9154
+ this.columnCreated.emit(finalColumn);
9155
+ }
9156
+ /**
9157
+ * Handle cancel from the editor
9158
+ */
9159
+ onCancel() {
9160
+ this.cancelled.emit();
9161
+ }
9162
+ /**
9163
+ * Handle backdrop click to close modal
9164
+ */
9165
+ onBackdropClick(event) {
9166
+ if (event.target === event.currentTarget) {
9167
+ this.cancelled.emit();
9168
+ }
9169
+ }
9170
+ /**
9171
+ * Prevent event propagation from modal content
9172
+ */
9173
+ onModalContentClick(event) {
9174
+ event.stopPropagation();
9175
+ }
9176
+ }
9177
+ StColumnEditorModalComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StColumnEditorModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9178
+ 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"] }] });
9179
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StColumnEditorModalComponent, decorators: [{
9180
+ type: Component,
9181
+ 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"] }]
9182
+ }], propDecorators: { columnCreated: [{
9183
+ type: Output
9184
+ }], cancelled: [{
9185
+ type: Output
9186
+ }], columnEditor: [{
9187
+ type: ViewChild,
9188
+ args: [ColumnEditorComponent]
9189
+ }] } });
9190
+
9171
9191
  class StSheetActionsComponent {
9172
9192
  constructor() {
9173
9193
  /**