@ngneers/controls 0.0.1-pre6 → 0.0.1-pre7

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.
Files changed (148) hide show
  1. package/fesm2022/ngneers-controls-accordion.mjs +10 -10
  2. package/fesm2022/ngneers-controls-accordion.mjs.map +1 -1
  3. package/fesm2022/ngneers-controls-api-ng.mjs +12 -9
  4. package/fesm2022/ngneers-controls-api-ng.mjs.map +1 -1
  5. package/fesm2022/ngneers-controls-avatar.mjs +6 -6
  6. package/fesm2022/ngneers-controls-avatar.mjs.map +1 -1
  7. package/fesm2022/ngneers-controls-base.mjs +9 -9
  8. package/fesm2022/ngneers-controls-base.mjs.map +1 -1
  9. package/fesm2022/ngneers-controls-breadcrumb.mjs +7 -7
  10. package/fesm2022/ngneers-controls-breadcrumb.mjs.map +1 -1
  11. package/fesm2022/ngneers-controls-button-group.mjs +3 -3
  12. package/fesm2022/ngneers-controls-button-group.mjs.map +1 -1
  13. package/fesm2022/ngneers-controls-button.mjs +6 -6
  14. package/fesm2022/ngneers-controls-button.mjs.map +1 -1
  15. package/fesm2022/{ngneers-controls-calendar-time-Bzlv9Vkn.mjs → ngneers-controls-calendar-time-CKAqRgOP.mjs} +4 -4
  16. package/fesm2022/{ngneers-controls-calendar-time-Bzlv9Vkn.mjs.map → ngneers-controls-calendar-time-CKAqRgOP.mjs.map} +1 -1
  17. package/fesm2022/ngneers-controls-calendar.mjs +11 -11
  18. package/fesm2022/ngneers-controls-calendar.mjs.map +1 -1
  19. package/fesm2022/ngneers-controls-checkbox.mjs +4 -4
  20. package/fesm2022/ngneers-controls-checkbox.mjs.map +1 -1
  21. package/fesm2022/ngneers-controls-chip.mjs +4 -4
  22. package/fesm2022/ngneers-controls-chip.mjs.map +1 -1
  23. package/fesm2022/ngneers-controls-default-icons.mjs +61 -0
  24. package/fesm2022/ngneers-controls-default-icons.mjs.map +1 -0
  25. package/fesm2022/ngneers-controls-defer.mjs +3 -3
  26. package/fesm2022/ngneers-controls-defer.mjs.map +1 -1
  27. package/fesm2022/ngneers-controls-dialog.mjs +10 -10
  28. package/fesm2022/ngneers-controls-dialog.mjs.map +1 -1
  29. package/fesm2022/ngneers-controls-directives.mjs +21 -21
  30. package/fesm2022/ngneers-controls-directives.mjs.map +1 -1
  31. package/fesm2022/ngneers-controls-drawer.mjs +7 -7
  32. package/fesm2022/ngneers-controls-drawer.mjs.map +1 -1
  33. package/fesm2022/ngneers-controls-edit-inplace.mjs +7 -7
  34. package/fesm2022/ngneers-controls-edit-inplace.mjs.map +1 -1
  35. package/fesm2022/ngneers-controls-filter.mjs +4 -4
  36. package/fesm2022/ngneers-controls-filter.mjs.map +1 -1
  37. package/fesm2022/ngneers-controls-i18n.mjs +3 -3
  38. package/fesm2022/ngneers-controls-i18n.mjs.map +1 -1
  39. package/fesm2022/ngneers-controls-icon.mjs +108 -44
  40. package/fesm2022/ngneers-controls-icon.mjs.map +1 -1
  41. package/fesm2022/ngneers-controls-inplace.mjs +6 -6
  42. package/fesm2022/ngneers-controls-inplace.mjs.map +1 -1
  43. package/fesm2022/ngneers-controls-input-field.mjs +5 -5
  44. package/fesm2022/ngneers-controls-input-field.mjs.map +1 -1
  45. package/fesm2022/ngneers-controls-input-mask.mjs +3 -3
  46. package/fesm2022/ngneers-controls-input-mask.mjs.map +1 -1
  47. package/fesm2022/ngneers-controls-input.mjs +3 -3
  48. package/fesm2022/ngneers-controls-input.mjs.map +1 -1
  49. package/fesm2022/ngneers-controls-item-view.mjs +6 -6
  50. package/fesm2022/ngneers-controls-item-view.mjs.map +1 -1
  51. package/fesm2022/ngneers-controls-list-box.mjs +6 -6
  52. package/fesm2022/ngneers-controls-list-box.mjs.map +1 -1
  53. package/fesm2022/ngneers-controls-menu.mjs +10 -10
  54. package/fesm2022/ngneers-controls-menu.mjs.map +1 -1
  55. package/fesm2022/ngneers-controls-message.mjs +3 -3
  56. package/fesm2022/ngneers-controls-message.mjs.map +1 -1
  57. package/fesm2022/ngneers-controls-paginator.mjs +4 -4
  58. package/fesm2022/ngneers-controls-paginator.mjs.map +1 -1
  59. package/fesm2022/ngneers-controls-popover.mjs +11 -6
  60. package/fesm2022/ngneers-controls-popover.mjs.map +1 -1
  61. package/fesm2022/ngneers-controls-progress.mjs +3 -3
  62. package/fesm2022/ngneers-controls-progress.mjs.map +1 -1
  63. package/fesm2022/ngneers-controls-scroll-shadow.mjs +54 -0
  64. package/fesm2022/ngneers-controls-scroll-shadow.mjs.map +1 -0
  65. package/fesm2022/ngneers-controls-scroller.mjs +9 -9
  66. package/fesm2022/ngneers-controls-scroller.mjs.map +1 -1
  67. package/fesm2022/ngneers-controls-select-button.mjs +4 -4
  68. package/fesm2022/ngneers-controls-select-button.mjs.map +1 -1
  69. package/fesm2022/ngneers-controls-select.mjs +8 -7
  70. package/fesm2022/ngneers-controls-select.mjs.map +1 -1
  71. package/fesm2022/ngneers-controls-slider.mjs +3 -3
  72. package/fesm2022/ngneers-controls-slider.mjs.map +1 -1
  73. package/fesm2022/ngneers-controls-spinner.mjs +3 -3
  74. package/fesm2022/ngneers-controls-spinner.mjs.map +1 -1
  75. package/fesm2022/ngneers-controls-splitter.mjs +10 -10
  76. package/fesm2022/ngneers-controls-splitter.mjs.map +1 -1
  77. package/fesm2022/ngneers-controls-switch.mjs +3 -3
  78. package/fesm2022/ngneers-controls-switch.mjs.map +1 -1
  79. package/fesm2022/ngneers-controls-table.mjs +227 -293
  80. package/fesm2022/ngneers-controls-table.mjs.map +1 -1
  81. package/fesm2022/ngneers-controls-tabs.mjs +10 -10
  82. package/fesm2022/ngneers-controls-tabs.mjs.map +1 -1
  83. package/fesm2022/ngneers-controls-tag.mjs +3 -3
  84. package/fesm2022/ngneers-controls-tag.mjs.map +1 -1
  85. package/fesm2022/ngneers-controls-toast.mjs +10 -10
  86. package/fesm2022/ngneers-controls-toast.mjs.map +1 -1
  87. package/fesm2022/ngneers-controls-toggle-button.mjs +4 -4
  88. package/fesm2022/ngneers-controls-toggle-button.mjs.map +1 -1
  89. package/fesm2022/ngneers-controls-tooltip.mjs +44 -8
  90. package/fesm2022/ngneers-controls-tooltip.mjs.map +1 -1
  91. package/fesm2022/ngneers-controls-utils-ng.mjs +3 -3
  92. package/fesm2022/ngneers-controls-utils-ng.mjs.map +1 -1
  93. package/fesm2022/ngneers-controls-utils.mjs +1 -1
  94. package/fesm2022/ngneers-controls-utils.mjs.map +1 -1
  95. package/package.json +29 -11
  96. package/types/ngneers-controls-accordion.d.ts +6 -4
  97. package/types/ngneers-controls-api-ng.d.ts +5 -0
  98. package/types/ngneers-controls-api.d.ts +2 -2
  99. package/types/ngneers-controls-breadcrumb.d.ts +5 -3
  100. package/types/ngneers-controls-checkbox.d.ts +5 -3
  101. package/types/ngneers-controls-chip.d.ts +3 -1
  102. package/types/ngneers-controls-default-icons.d.ts +5 -0
  103. package/types/ngneers-controls-drawer.d.ts +3 -1
  104. package/types/ngneers-controls-filter.d.ts +1 -1
  105. package/types/ngneers-controls-icon.d.ts +23 -40
  106. package/types/ngneers-controls-input-field.d.ts +3 -1
  107. package/types/ngneers-controls-item-view.d.ts +4 -2
  108. package/types/ngneers-controls-menu.d.ts +3 -1
  109. package/types/ngneers-controls-message.d.ts +3 -1
  110. package/types/ngneers-controls-popover.d.ts +1 -0
  111. package/types/ngneers-controls-scroll-shadow.d.ts +16 -0
  112. package/types/ngneers-controls-select.d.ts +5 -10
  113. package/types/ngneers-controls-table.d.ts +29 -41
  114. package/types/ngneers-controls-tabs.d.ts +4 -2
  115. package/types/ngneers-controls-tag.d.ts +3 -1
  116. package/types/ngneers-controls-toast.d.ts +4 -2
  117. package/types/ngneers-controls-toggle-button.d.ts +5 -3
  118. package/types/ngneers-controls-tooltip.d.ts +7 -1
  119. package/fesm2022/ngneers-controls-icon-calendar-z5xA9wTT.mjs +0 -4
  120. package/fesm2022/ngneers-controls-icon-calendar-z5xA9wTT.mjs.map +0 -1
  121. package/fesm2022/ngneers-controls-icon-check-DdAQ9k-T.mjs +0 -4
  122. package/fesm2022/ngneers-controls-icon-check-DdAQ9k-T.mjs.map +0 -1
  123. package/fesm2022/ngneers-controls-icon-chevron_down-HO-z-xOr.mjs +0 -4
  124. package/fesm2022/ngneers-controls-icon-chevron_down-HO-z-xOr.mjs.map +0 -1
  125. package/fesm2022/ngneers-controls-icon-chevron_left-CC5psi16.mjs +0 -4
  126. package/fesm2022/ngneers-controls-icon-chevron_left-CC5psi16.mjs.map +0 -1
  127. package/fesm2022/ngneers-controls-icon-chevron_right-BRxlzx4G.mjs +0 -4
  128. package/fesm2022/ngneers-controls-icon-chevron_right-BRxlzx4G.mjs.map +0 -1
  129. package/fesm2022/ngneers-controls-icon-chevron_up-cqhPO7g2.mjs +0 -4
  130. package/fesm2022/ngneers-controls-icon-chevron_up-cqhPO7g2.mjs.map +0 -1
  131. package/fesm2022/ngneers-controls-icon-ellipsis-DXIGA1sk.mjs +0 -4
  132. package/fesm2022/ngneers-controls-icon-ellipsis-DXIGA1sk.mjs.map +0 -1
  133. package/fesm2022/ngneers-controls-icon-filter-oU7E6Xgr.mjs +0 -4
  134. package/fesm2022/ngneers-controls-icon-filter-oU7E6Xgr.mjs.map +0 -1
  135. package/fesm2022/ngneers-controls-icon-filter_solid-CqIKuR4N.mjs +0 -4
  136. package/fesm2022/ngneers-controls-icon-filter_solid-CqIKuR4N.mjs.map +0 -1
  137. package/fesm2022/ngneers-controls-icon-magnifying_glass-T4Hzbkig.mjs +0 -4
  138. package/fesm2022/ngneers-controls-icon-magnifying_glass-T4Hzbkig.mjs.map +0 -1
  139. package/fesm2022/ngneers-controls-icon-minus-oGDEFO4E.mjs +0 -4
  140. package/fesm2022/ngneers-controls-icon-minus-oGDEFO4E.mjs.map +0 -1
  141. package/fesm2022/ngneers-controls-icon-sort-CRD4tafz.mjs +0 -4
  142. package/fesm2022/ngneers-controls-icon-sort-CRD4tafz.mjs.map +0 -1
  143. package/fesm2022/ngneers-controls-icon-sort_down-DiFB1P6R.mjs +0 -4
  144. package/fesm2022/ngneers-controls-icon-sort_down-DiFB1P6R.mjs.map +0 -1
  145. package/fesm2022/ngneers-controls-icon-sort_up-Bq_q3hIi.mjs +0 -4
  146. package/fesm2022/ngneers-controls-icon-sort_up-Bq_q3hIi.mjs.map +0 -1
  147. package/fesm2022/ngneers-controls-icon-xmark-BZKZORiu.mjs +0 -4
  148. package/fesm2022/ngneers-controls-icon-xmark-BZKZORiu.mjs.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, inject, ElementRef, effect, Directive, contentChild, ChangeDetectionStrategy, Component, signal, viewChild, model, booleanAttribute, computed, afterNextRender, Renderer2, afterRenderEffect, EnvironmentInjector, ApplicationRef, createComponent, NgModule, ViewContainerRef } from '@angular/core';
2
+ import { input, inject, ElementRef, effect, Directive, contentChild, ChangeDetectionStrategy, Component, signal, viewChild, model, booleanAttribute, computed, afterRenderEffect, afterNextRender, Renderer2, EnvironmentInjector, ApplicationRef, createComponent, NgModule, ViewContainerRef } from '@angular/core';
3
3
  import { NgTemplateOutlet } from '@angular/common';
4
4
  import { executeMultiFilter } from '@ngneers/controls/api';
5
5
  import { injectThemeTemplate, templateTypesFn, elementSizeSignal, NgnTemplate, setComponentInput } from '@ngneers/controls/api/ng';
@@ -7,6 +7,7 @@ import { ResizeEngine, getResizeLimitInPx } from '@ngneers/controls/api/resize';
7
7
  import { NgnBase, NgnPt, provideSelf, getNearestNgnInstance, getNearestNgnInstanceSig } from '@ngneers/controls/base';
8
8
  import { NgnIcon } from '@ngneers/controls/icon';
9
9
  import { NgnPaginator } from '@ngneers/controls/paginator';
10
+ import { NgnScrollShadow } from '@ngneers/controls/scroll-shadow';
10
11
  import { NgnScrollerItem, NgnScroller } from '@ngneers/controls/scroller';
11
12
  import { tableControlTemplate } from '@ngneers/controls-themes/templates/table';
12
13
  import { toggleClass, NgnError } from '@ngneers/controls/utils';
@@ -32,10 +33,10 @@ class NgnTableGroupHeaderTr extends NgnScrollerItem {
32
33
  toggleClass(this._element.nativeElement, this.theme.class('row'), true);
33
34
  toggleClass(this._element.nativeElement, this.theme.class('group-header-row'), true);
34
35
  }
35
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableGroupHeaderTr, deps: [], target: i0.ɵɵFactoryTarget.Directive });
36
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.2", type: NgnTableGroupHeaderTr, isStandalone: true, selector: "[ngnTableGroupHeaderTr]", inputs: { ngnTableGroupHeaderTr: { classPropertyName: "ngnTableGroupHeaderTr", publicName: "ngnTableGroupHeaderTr", isSignal: true, isRequired: true, transformFunction: null }, ngnScrollerItem: { classPropertyName: "ngnScrollerItem", publicName: "ngnScrollerItem", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-rowindex": "ngnTableGroupHeaderTr().index + 2", "style.--ngn-table-row-index": "ngnTableGroupHeaderTr().index + 2" } }, usesInheritance: true, ngImport: i0 });
36
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableGroupHeaderTr, deps: [], target: i0.ɵɵFactoryTarget.Directive });
37
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.10", type: NgnTableGroupHeaderTr, isStandalone: true, selector: "[ngnTableGroupHeaderTr]", inputs: { ngnTableGroupHeaderTr: { classPropertyName: "ngnTableGroupHeaderTr", publicName: "ngnTableGroupHeaderTr", isSignal: true, isRequired: true, transformFunction: null }, ngnScrollerItem: { classPropertyName: "ngnScrollerItem", publicName: "ngnScrollerItem", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-rowindex": "ngnTableGroupHeaderTr().index + 2", "style.--ngn-table-row-index": "ngnTableGroupHeaderTr().index + 2" } }, usesInheritance: true, ngImport: i0 });
37
38
  }
38
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableGroupHeaderTr, decorators: [{
39
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableGroupHeaderTr, decorators: [{
39
40
  type: Directive,
40
41
  args: [{
41
42
  selector: '[ngnTableGroupHeaderTr]',
@@ -57,10 +58,10 @@ class NgnTableTemplates extends NgnBase {
57
58
  * Types for the table templates.
58
59
  */
59
60
  templateTypes = templateTypesFn();
60
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableTemplates, deps: null, target: i0.ɵɵFactoryTarget.Component });
61
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.2", type: NgnTableTemplates, isStandalone: true, selector: "ng-component", queries: [{ propertyName: "_headerTemplate", first: true, predicate: ["header"], descendants: true, isSignal: true }, { propertyName: "_bodyTemplate", first: true, predicate: ["body"], descendants: true, isSignal: true }, { propertyName: "_groupHeaderTemplate", first: true, predicate: ["groupHeader"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
61
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableTemplates, deps: null, target: i0.ɵɵFactoryTarget.Component });
62
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.10", type: NgnTableTemplates, isStandalone: true, selector: "ng-component", queries: [{ propertyName: "_headerTemplate", first: true, predicate: ["header"], descendants: true, isSignal: true }, { propertyName: "_bodyTemplate", first: true, predicate: ["body"], descendants: true, isSignal: true }, { propertyName: "_groupHeaderTemplate", first: true, predicate: ["groupHeader"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
62
63
  }
63
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableTemplates, decorators: [{
64
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableTemplates, decorators: [{
64
65
  type: Component,
65
66
  args: [{
66
67
  template: '',
@@ -88,6 +89,7 @@ class NgnTable extends NgnTableTemplates {
88
89
  reordering: () => this._isReordering(),
89
90
  });
90
91
  _registeredHeaderCells = signal([], ...(ngDevMode ? [{ debugName: "_registeredHeaderCells" }] : /* istanbul ignore next */ []));
92
+ _stickyColumns = signal(new Map(), ...(ngDevMode ? [{ debugName: "_stickyColumns" }] : /* istanbul ignore next */ []));
91
93
  _scroller = viewChild.required(NgnScroller);
92
94
  rows = input.required(...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
93
95
  rowHeight = input(...(ngDevMode ? [undefined, { debugName: "rowHeight" }] : /* istanbul ignore next */ []));
@@ -309,25 +311,6 @@ class NgnTable extends NgnTableTemplates {
309
311
  });
310
312
  }, ...(ngDevMode ? [{ debugName: "_sortedRows" }] : /* istanbul ignore next */ []));
311
313
  columnCount = computed(() => this._registeredHeaderCells().length, ...(ngDevMode ? [{ debugName: "columnCount" }] : /* istanbul ignore next */ []));
312
- // --- Sticky column registry (lightweight — directive owns the logic) ---
313
- _stickyColumnIds = signal(new Map(), ...(ngDevMode ? [{ debugName: "_stickyColumnIds" }] : /* istanbul ignore next */ []));
314
- registerStickyColumnId(columnId, side) {
315
- this._stickyColumnIds.update(m => {
316
- const n = new Map(m);
317
- n.set(columnId, side);
318
- return n;
319
- });
320
- }
321
- unregisterStickyColumnId(columnId) {
322
- this._stickyColumnIds.update(m => {
323
- const n = new Map(m);
324
- n.delete(columnId);
325
- return n;
326
- });
327
- }
328
- getStickyColumnIds() {
329
- return this._stickyColumnIds();
330
- }
331
314
  // --- Column reorder state ---
332
315
  _isReordering = signal(false, ...(ngDevMode ? [{ debugName: "_isReordering" }] : /* istanbul ignore next */ []));
333
316
  _reorderSourceColumnId = signal(null, ...(ngDevMode ? [{ debugName: "_reorderSourceColumnId" }] : /* istanbul ignore next */ []));
@@ -350,6 +333,34 @@ class NgnTable extends NgnTableTemplates {
350
333
  const missing = cellIds.filter(id => !validOrder.includes(id));
351
334
  return [...validOrder, ...missing];
352
335
  }, ...(ngDevMode ? [{ debugName: "_effectiveColumnOrder" }] : /* istanbul ignore next */ []));
336
+ _stickyStartColumns = computed(() => {
337
+ const stickyMap = this._stickyColumns();
338
+ const order = this._effectiveColumnOrder();
339
+ const contiguous = [];
340
+ for (const id of order) {
341
+ if (stickyMap.get(id) === 'start') {
342
+ contiguous.push(id);
343
+ }
344
+ else {
345
+ break;
346
+ }
347
+ }
348
+ return contiguous;
349
+ }, ...(ngDevMode ? [{ debugName: "_stickyStartColumns" }] : /* istanbul ignore next */ []));
350
+ _stickyEndColumns = computed(() => {
351
+ const stickyMap = this._stickyColumns();
352
+ const order = this._effectiveColumnOrder();
353
+ const contiguous = [];
354
+ for (let i = order.length - 1; i >= 0; i--) {
355
+ if (stickyMap.get(order[i]) === 'end') {
356
+ contiguous.unshift(order[i]);
357
+ }
358
+ else {
359
+ break;
360
+ }
361
+ }
362
+ return contiguous;
363
+ }, ...(ngDevMode ? [{ debugName: "_stickyEndColumns" }] : /* istanbul ignore next */ []));
353
364
  /**
354
365
  * Maps column ID → 1-based visual position.
355
366
  */
@@ -416,6 +427,36 @@ class NgnTable extends NgnTableTemplates {
416
427
  }
417
428
  return `${checkboxCol}repeat(${this.columnCount()}, 1fr)`;
418
429
  }, ...(ngDevMode ? [{ debugName: "gridTemplateColumns" }] : /* istanbul ignore next */ []));
430
+ afterRenderEffect(() => {
431
+ const startCols = this._stickyStartColumns();
432
+ const endCols = this._stickyEndColumns();
433
+ if (startCols.length === 0 && endCols.length === 0)
434
+ return;
435
+ if (this._resizeEngine.isDragging())
436
+ return;
437
+ this._tableElementSize();
438
+ const tableEl = this.element.nativeElement.querySelector(':scope > table');
439
+ if (!tableEl)
440
+ return;
441
+ const cells = this._registeredHeaderCells();
442
+ let selectionWidth = 0;
443
+ if (this._hasSelectionColumn()) {
444
+ const selCol = tableEl.querySelector(`.${this.theme.class('selection-column')}`);
445
+ selectionWidth = selCol?.getBoundingClientRect().width ?? 0;
446
+ }
447
+ let cumulativeLeft = selectionWidth;
448
+ for (let i = 0; i < startCols.length; i++) {
449
+ tableEl.style.setProperty(`--ngn-sticky-start-offset-${i}`, `${cumulativeLeft}px`);
450
+ const cell = cells.find(c => c.ngnTableTh() === startCols[i]);
451
+ cumulativeLeft += cell?.element.nativeElement.getBoundingClientRect().width ?? 0;
452
+ }
453
+ let cumulativeRight = 0;
454
+ for (let i = endCols.length - 1; i >= 0; i--) {
455
+ tableEl.style.setProperty(`--ngn-sticky-end-offset-${i}`, `${cumulativeRight}px`);
456
+ const cell = cells.find(c => c.ngnTableTh() === endCols[i]);
457
+ cumulativeRight += cell?.element.nativeElement.getBoundingClientRect().width ?? 0;
458
+ }
459
+ });
419
460
  }
420
461
  pageChanged(event) {
421
462
  this.pageState.set(event);
@@ -438,6 +479,33 @@ class NgnTable extends NgnTableTemplates {
438
479
  getRegisteredHeaderCells() {
439
480
  return this._registeredHeaderCells();
440
481
  }
482
+ registerStickyColumn(columnId, side) {
483
+ this._stickyColumns.update(map => {
484
+ const next = new Map(map);
485
+ next.set(columnId, side);
486
+ return next;
487
+ });
488
+ }
489
+ unregisterStickyColumn(columnId) {
490
+ this._stickyColumns.update(map => {
491
+ const next = new Map(map);
492
+ next.delete(columnId);
493
+ return next;
494
+ });
495
+ }
496
+ getStickyInfo(columnId) {
497
+ const startCols = this._stickyStartColumns();
498
+ const endCols = this._stickyEndColumns();
499
+ const startIndex = startCols.indexOf(columnId);
500
+ if (startIndex >= 0) {
501
+ return { side: 'start', index: startIndex, isEdge: startIndex === startCols.length - 1 };
502
+ }
503
+ const endIndex = endCols.indexOf(columnId);
504
+ if (endIndex >= 0) {
505
+ return { side: 'end', index: endIndex, isEdge: endIndex === 0 };
506
+ }
507
+ return null;
508
+ }
441
509
  // --- Resize operations (called by NgnTableTh) ---
442
510
  startColumnResize(columnIndex, event) {
443
511
  if (!this.resizable())
@@ -594,6 +662,21 @@ class NgnTable extends NgnTableTemplates {
594
662
  this.toggleGroup(row.groupKey);
595
663
  }
596
664
  // --- Reorder operations (called by NgnTableReorderableColumn) ---
665
+ getReorderBounds(columnId) {
666
+ const order = this._effectiveColumnOrder();
667
+ const startCols = this._stickyStartColumns();
668
+ const endCols = this._stickyEndColumns();
669
+ const side = this._stickyColumns().get(columnId);
670
+ if (side === 'start') {
671
+ return { min: 0, max: startCols.length };
672
+ }
673
+ else if (side === 'end') {
674
+ return { min: order.length - endCols.length, max: order.length };
675
+ }
676
+ else {
677
+ return { min: startCols.length, max: order.length - endCols.length };
678
+ }
679
+ }
597
680
  startColumnReorder(columnId) {
598
681
  if (!this.reorderable())
599
682
  return;
@@ -624,27 +707,8 @@ class NgnTable extends NgnTableTemplates {
624
707
  break;
625
708
  }
626
709
  }
627
- // Clamp target so non-sticky columns cannot land inside sticky zones
628
- const stickyMap = this._stickyColumnIds();
629
- if (stickyMap.size > 0) {
630
- // Count sticky-left columns at the start
631
- let stickyLeftCount = 0;
632
- for (const id of effectiveOrder) {
633
- if (stickyMap.get(id) === 'left')
634
- stickyLeftCount++;
635
- else
636
- break;
637
- }
638
- // Count sticky-right columns at the end
639
- let stickyRightCount = 0;
640
- for (let i = effectiveOrder.length - 1; i >= 0; i--) {
641
- if (stickyMap.get(effectiveOrder[i]) === 'right')
642
- stickyRightCount++;
643
- else
644
- break;
645
- }
646
- targetIndex = Math.max(stickyLeftCount, Math.min(targetIndex, effectiveOrder.length - stickyRightCount));
647
- }
710
+ const bounds = this.getReorderBounds(sourceId);
711
+ targetIndex = Math.max(bounds.min, Math.min(bounds.max, targetIndex));
648
712
  this._reorderTargetIndex = targetIndex;
649
713
  // Compute indicator position relative to the table element
650
714
  const tableEl = this.element.nativeElement.querySelector(':scope > table');
@@ -752,7 +816,6 @@ class NgnTable extends NgnTableTemplates {
752
816
  return;
753
817
  const headerEl = headerCell.element.nativeElement;
754
818
  const ctx = autoSizeCanvasCtx();
755
- let maxContentWidth = 0;
756
819
  // Measure header: text width + non-text siblings (icons, controls) + flex gap.
757
820
  // The header cell is a flex container with: cell-text, spacer, filter/sort icons, resize handle.
758
821
  const cellTextClass = this.theme.class('cell-text');
@@ -781,7 +844,7 @@ class NgnTable extends NgnTableTemplates {
781
844
  if (visibleSiblingCount > 0) {
782
845
  headerRequiredWidth += headerGap * visibleSiblingCount;
783
846
  }
784
- maxContentWidth = headerRequiredWidth;
847
+ let maxContentWidth = headerRequiredWidth;
785
848
  // Measure visible body cells (nth-child is 1-indexed, offset by selection column)
786
849
  const selectionOffset = this._hasSelectionColumn() ? 1 : 0;
787
850
  const bodyCells = Array.from(tableEl.querySelectorAll(`tbody tr td:nth-child(${columnIndex + selectionOffset + 1})`));
@@ -804,10 +867,10 @@ class NgnTable extends NgnTableTemplates {
804
867
  this._resizeEngine.setItemSize(headerCell, `${clampedWidth}px`);
805
868
  this._resizeEngine.hasBeenResized.set(true);
806
869
  }
807
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
808
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", type: NgnTable, isStandalone: true, selector: "ngn-table", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, fieldId: { classPropertyName: "fieldId", publicName: "fieldId", isSignal: true, isRequired: true, transformFunction: null }, virtual: { classPropertyName: "virtual", publicName: "virtual", isSignal: true, isRequired: false, transformFunction: null }, virtualPadding: { classPropertyName: "virtualPadding", publicName: "virtualPadding", isSignal: true, isRequired: false, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, paginator: { classPropertyName: "paginator", publicName: "paginator", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, groupBy: { classPropertyName: "groupBy", publicName: "groupBy", isSignal: true, isRequired: false, transformFunction: null }, expandedGroups: { classPropertyName: "expandedGroups", publicName: "expandedGroups", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, resizeMode: { classPropertyName: "resizeMode", publicName: "resizeMode", isSignal: true, isRequired: false, transformFunction: null }, lockSizes: { classPropertyName: "lockSizes", publicName: "lockSizes", isSignal: true, isRequired: false, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null }, columnOrder: { classPropertyName: "columnOrder", publicName: "columnOrder", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selection: "selectionChange", expandedGroups: "expandedGroupsChange", columnOrder: "columnOrderChange", sort: "sortChange", filters: "filtersChange" }, host: { attributes: { "tabindex": "0" }, listeners: { "keydown": "onKeyDown($event)" }, properties: { "attr.aria-multiselectable": "selectionMode() === \"multi\" ? true : null" } }, providers: [provideSelf(NgnTable)], viewQueries: [{ propertyName: "_scroller", first: true, predicate: NgnScroller, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<table\r\n #scrollContainer\r\n [ptInt]=\"this\"\r\n [ptClass]=\"{ table: true, striped: striped() }\"\r\n [attr.aria-rowcount]=\"formattedRows().length\"\r\n [style.--ngn-table-column-count]=\"totalColumnCount()\"\r\n [style.--ngn-table-selection-offset]=\"showCheckboxes() ? 1 : 0\"\r\n [style.grid-template-columns]=\"gridTemplateColumns()\"\r\n [style.--ngn-table-row-height.px]=\"rowHeight()\"\r\n>\r\n <thead [ptInt]=\"this\" [ptClass]=\"'head'\">\r\n <ng-container [ngTemplateOutlet]=\"headerTemplate()\"></ng-container>\r\n </thead>\r\n @if (_dropIndicatorState()) {\r\n <div\r\n [ptInt]=\"this\"\r\n [ptClass]=\"'drop-indicator'\"\r\n [style.left.px]=\"_dropIndicatorState()!.leftPx\"\r\n [style.top.px]=\"_dropIndicatorState()!.topPx\"\r\n [style.height.px]=\"_dropIndicatorState()!.heightPx\"\r\n ></div>\r\n }\r\n\r\n <tbody\r\n [ptInt]=\"this\"\r\n [ptClass]=\"'body'\"\r\n ngn-scroller\r\n #scroller\r\n [scrollContainer]=\"scrollContainer\"\r\n [virtual]=\"virtual()\"\r\n [items]=\"pagedRows()\"\r\n [fieldId]=\"'id'\"\r\n [itemHeight]=\"rowHeight() || 0\"\r\n [virtualPadding]=\"virtualPadding()\"\r\n [style.--ngn-table-item-start-index]=\"scroller.itemStartIndex()\"\r\n >\r\n <ng-template #item [ngnTemplate]=\"scroller.templateTypes.item\" let-item let-index=\"index\">\r\n @if (item.kind === 'group-header') {\r\n @if (groupHeaderTemplate(); as customGroupHeader) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"customGroupHeader\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: {\r\n groupKey: item.groupKey,\r\n groupValue: item.groupValue,\r\n count: item.count,\r\n expanded: item.expanded,\r\n },\r\n }\"\r\n ></ng-container>\r\n } @else {\r\n <tr\r\n [ngnTableGroupHeaderTr]=\"item\"\r\n [class]=\"theme.classes({ 'group-expanded': item.expanded })\"\r\n [attr.aria-expanded]=\"item.expanded\"\r\n role=\"row\"\r\n (click)=\"toggleGroupFromRow(item)\"\r\n >\r\n <td [class]=\"theme.class('group-header-cell')\" role=\"gridcell\">\r\n <ngn-icon [defaultIcon]=\"'chevron_right'\" [class]=\"theme.class('group-toggle')\" />\r\n <span>{{ item.groupValue }}</span>\r\n <span>({{ item.count }})</span>\r\n </td>\r\n </tr>\r\n }\r\n } @else {\r\n <ng-container\r\n [ngTemplateOutlet]=\"bodyTemplate()\"\r\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\r\n ></ng-container>\r\n }\r\n </ng-template>\r\n </tbody>\r\n</table>\r\n@if (paginator()) {\r\n <ngn-paginator [totalItems]=\"rows().length\" (value)=\"pageChanged($event)\" />\r\n}\r\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: NgnScroller, selector: "ngn-scroller, [ngn-scroller]", inputs: ["items", "fieldId", "virtual", "itemHeight", "virtualPadding", "focusable", "fieldSticky"] }, { kind: "component", type: NgnPaginator, selector: "ngn-paginator", inputs: ["totalItems", "pageSize", "possiblePageSizes", "fixedPageSize", "page"], outputs: ["pageSizeChange", "value", "pageChange"] }, { kind: "directive", type: NgnTemplate, selector: "ng-template[ngnTemplate]", inputs: ["ngnTemplate"] }, { kind: "directive", type: NgnPt, selector: "[ptInt]", inputs: ["ptInt", "ptClass"] }, { kind: "component", type: NgnIcon, selector: "ngn-icon", inputs: ["defaultIcon", "icon"] }, { kind: "directive", type: NgnTableGroupHeaderTr, selector: "[ngnTableGroupHeaderTr]", inputs: ["ngnTableGroupHeaderTr", "ngnScrollerItem"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
870
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
871
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: NgnTable, isStandalone: true, selector: "ngn-table", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, rowHeight: { classPropertyName: "rowHeight", publicName: "rowHeight", isSignal: true, isRequired: false, transformFunction: null }, fieldId: { classPropertyName: "fieldId", publicName: "fieldId", isSignal: true, isRequired: true, transformFunction: null }, virtual: { classPropertyName: "virtual", publicName: "virtual", isSignal: true, isRequired: false, transformFunction: null }, virtualPadding: { classPropertyName: "virtualPadding", publicName: "virtualPadding", isSignal: true, isRequired: false, transformFunction: null }, striped: { classPropertyName: "striped", publicName: "striped", isSignal: true, isRequired: false, transformFunction: null }, paginator: { classPropertyName: "paginator", publicName: "paginator", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, selection: { classPropertyName: "selection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, groupBy: { classPropertyName: "groupBy", publicName: "groupBy", isSignal: true, isRequired: false, transformFunction: null }, expandedGroups: { classPropertyName: "expandedGroups", publicName: "expandedGroups", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, resizeMode: { classPropertyName: "resizeMode", publicName: "resizeMode", isSignal: true, isRequired: false, transformFunction: null }, lockSizes: { classPropertyName: "lockSizes", publicName: "lockSizes", isSignal: true, isRequired: false, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null }, columnOrder: { classPropertyName: "columnOrder", publicName: "columnOrder", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, filters: { classPropertyName: "filters", publicName: "filters", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selection: "selectionChange", expandedGroups: "expandedGroupsChange", columnOrder: "columnOrderChange", sort: "sortChange", filters: "filtersChange" }, host: { attributes: { "tabindex": "0" }, listeners: { "keydown": "onKeyDown($event)" }, properties: { "attr.aria-multiselectable": "selectionMode() === \"multi\" ? true : null" } }, providers: [provideSelf(NgnTable)], viewQueries: [{ propertyName: "_scroller", first: true, predicate: NgnScroller, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<table\r\n #scrollContainer\r\n ngnScrollShadow=\"horizontal\"\r\n [ptInt]=\"this\"\r\n [ptClass]=\"{ table: true, striped: striped() }\"\r\n [attr.aria-rowcount]=\"formattedRows().length\"\r\n [style.--ngn-table-column-count]=\"totalColumnCount()\"\r\n [style.--ngn-table-selection-offset]=\"showCheckboxes() ? 1 : 0\"\r\n [style.grid-template-columns]=\"gridTemplateColumns()\"\r\n [style.--ngn-table-row-height.px]=\"rowHeight()\"\r\n>\r\n <thead [ptInt]=\"this\" [ptClass]=\"'head'\">\r\n <ng-container [ngTemplateOutlet]=\"headerTemplate()\"></ng-container>\r\n </thead>\r\n @if (_dropIndicatorState()) {\r\n <div\r\n [ptInt]=\"this\"\r\n [ptClass]=\"'drop-indicator'\"\r\n [style.left.px]=\"_dropIndicatorState()!.leftPx\"\r\n [style.top.px]=\"_dropIndicatorState()!.topPx\"\r\n [style.height.px]=\"_dropIndicatorState()!.heightPx\"\r\n ></div>\r\n }\r\n\r\n <tbody\r\n [ptInt]=\"this\"\r\n [ptClass]=\"'body'\"\r\n ngn-scroller\r\n #scroller\r\n [scrollContainer]=\"scrollContainer\"\r\n [virtual]=\"virtual()\"\r\n [items]=\"pagedRows()\"\r\n [fieldId]=\"'id'\"\r\n [itemHeight]=\"rowHeight() || 0\"\r\n [virtualPadding]=\"virtualPadding()\"\r\n [style.--ngn-table-item-start-index]=\"scroller.itemStartIndex()\"\r\n >\r\n <ng-template #item [ngnTemplate]=\"scroller.templateTypes.item\" let-item let-index=\"index\">\r\n @if (item.kind === 'group-header') {\r\n @if (groupHeaderTemplate(); as customGroupHeader) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"customGroupHeader\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: {\r\n groupKey: item.groupKey,\r\n groupValue: item.groupValue,\r\n count: item.count,\r\n expanded: item.expanded,\r\n },\r\n }\"\r\n ></ng-container>\r\n } @else {\r\n <tr\r\n [ngnTableGroupHeaderTr]=\"item\"\r\n [class]=\"theme.classes({ 'group-expanded': item.expanded })\"\r\n [attr.aria-expanded]=\"item.expanded\"\r\n role=\"row\"\r\n (click)=\"toggleGroupFromRow(item)\"\r\n >\r\n <td [class]=\"theme.class('group-header-cell')\" role=\"gridcell\">\r\n <ngn-icon\r\n [defaultIcon]=\"'table-group-toggle'\"\r\n [class]=\"theme.class('group-toggle')\"\r\n />\r\n <span>{{ item.groupValue }}</span>\r\n <span>({{ item.count }})</span>\r\n </td>\r\n </tr>\r\n }\r\n } @else {\r\n <ng-container\r\n [ngTemplateOutlet]=\"bodyTemplate()\"\r\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\r\n ></ng-container>\r\n }\r\n </ng-template>\r\n </tbody>\r\n</table>\r\n@if (paginator()) {\r\n <ngn-paginator [totalItems]=\"rows().length\" (value)=\"pageChanged($event)\" />\r\n}\r\n", dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: NgnScroller, selector: "ngn-scroller, [ngn-scroller]", inputs: ["items", "fieldId", "virtual", "itemHeight", "virtualPadding", "focusable", "fieldSticky"] }, { kind: "component", type: NgnPaginator, selector: "ngn-paginator", inputs: ["totalItems", "pageSize", "possiblePageSizes", "fixedPageSize", "page"], outputs: ["pageSizeChange", "value", "pageChange"] }, { kind: "directive", type: NgnTemplate, selector: "ng-template[ngnTemplate]", inputs: ["ngnTemplate"] }, { kind: "directive", type: NgnPt, selector: "[ptInt]", inputs: ["ptInt", "ptClass"] }, { kind: "component", type: NgnIcon, selector: "ngn-icon", inputs: ["defaultIcon", "icon"] }, { kind: "directive", type: NgnTableGroupHeaderTr, selector: "[ngnTableGroupHeaderTr]", inputs: ["ngnTableGroupHeaderTr", "ngnScrollerItem"] }, { kind: "directive", type: NgnScrollShadow, selector: "[ngnScrollShadow]", inputs: ["ngnScrollShadow", "scrollShadowTarget"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
809
872
  }
810
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTable, decorators: [{
873
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTable, decorators: [{
811
874
  type: Component,
812
875
  args: [{ selector: 'ngn-table', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
813
876
  NgTemplateOutlet,
@@ -817,11 +880,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
817
880
  NgnPt,
818
881
  NgnIcon,
819
882
  NgnTableGroupHeaderTr,
883
+ NgnScrollShadow,
820
884
  ], providers: [provideSelf(NgnTable)], host: {
821
885
  tabindex: '0',
822
886
  '[attr.aria-multiselectable]': 'selectionMode() === "multi" ? true : null',
823
887
  '(keydown)': 'onKeyDown($event)',
824
- }, template: "<table\r\n #scrollContainer\r\n [ptInt]=\"this\"\r\n [ptClass]=\"{ table: true, striped: striped() }\"\r\n [attr.aria-rowcount]=\"formattedRows().length\"\r\n [style.--ngn-table-column-count]=\"totalColumnCount()\"\r\n [style.--ngn-table-selection-offset]=\"showCheckboxes() ? 1 : 0\"\r\n [style.grid-template-columns]=\"gridTemplateColumns()\"\r\n [style.--ngn-table-row-height.px]=\"rowHeight()\"\r\n>\r\n <thead [ptInt]=\"this\" [ptClass]=\"'head'\">\r\n <ng-container [ngTemplateOutlet]=\"headerTemplate()\"></ng-container>\r\n </thead>\r\n @if (_dropIndicatorState()) {\r\n <div\r\n [ptInt]=\"this\"\r\n [ptClass]=\"'drop-indicator'\"\r\n [style.left.px]=\"_dropIndicatorState()!.leftPx\"\r\n [style.top.px]=\"_dropIndicatorState()!.topPx\"\r\n [style.height.px]=\"_dropIndicatorState()!.heightPx\"\r\n ></div>\r\n }\r\n\r\n <tbody\r\n [ptInt]=\"this\"\r\n [ptClass]=\"'body'\"\r\n ngn-scroller\r\n #scroller\r\n [scrollContainer]=\"scrollContainer\"\r\n [virtual]=\"virtual()\"\r\n [items]=\"pagedRows()\"\r\n [fieldId]=\"'id'\"\r\n [itemHeight]=\"rowHeight() || 0\"\r\n [virtualPadding]=\"virtualPadding()\"\r\n [style.--ngn-table-item-start-index]=\"scroller.itemStartIndex()\"\r\n >\r\n <ng-template #item [ngnTemplate]=\"scroller.templateTypes.item\" let-item let-index=\"index\">\r\n @if (item.kind === 'group-header') {\r\n @if (groupHeaderTemplate(); as customGroupHeader) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"customGroupHeader\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: {\r\n groupKey: item.groupKey,\r\n groupValue: item.groupValue,\r\n count: item.count,\r\n expanded: item.expanded,\r\n },\r\n }\"\r\n ></ng-container>\r\n } @else {\r\n <tr\r\n [ngnTableGroupHeaderTr]=\"item\"\r\n [class]=\"theme.classes({ 'group-expanded': item.expanded })\"\r\n [attr.aria-expanded]=\"item.expanded\"\r\n role=\"row\"\r\n (click)=\"toggleGroupFromRow(item)\"\r\n >\r\n <td [class]=\"theme.class('group-header-cell')\" role=\"gridcell\">\r\n <ngn-icon [defaultIcon]=\"'chevron_right'\" [class]=\"theme.class('group-toggle')\" />\r\n <span>{{ item.groupValue }}</span>\r\n <span>({{ item.count }})</span>\r\n </td>\r\n </tr>\r\n }\r\n } @else {\r\n <ng-container\r\n [ngTemplateOutlet]=\"bodyTemplate()\"\r\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\r\n ></ng-container>\r\n }\r\n </ng-template>\r\n </tbody>\r\n</table>\r\n@if (paginator()) {\r\n <ngn-paginator [totalItems]=\"rows().length\" (value)=\"pageChanged($event)\" />\r\n}\r\n" }]
888
+ }, template: "<table\r\n #scrollContainer\r\n ngnScrollShadow=\"horizontal\"\r\n [ptInt]=\"this\"\r\n [ptClass]=\"{ table: true, striped: striped() }\"\r\n [attr.aria-rowcount]=\"formattedRows().length\"\r\n [style.--ngn-table-column-count]=\"totalColumnCount()\"\r\n [style.--ngn-table-selection-offset]=\"showCheckboxes() ? 1 : 0\"\r\n [style.grid-template-columns]=\"gridTemplateColumns()\"\r\n [style.--ngn-table-row-height.px]=\"rowHeight()\"\r\n>\r\n <thead [ptInt]=\"this\" [ptClass]=\"'head'\">\r\n <ng-container [ngTemplateOutlet]=\"headerTemplate()\"></ng-container>\r\n </thead>\r\n @if (_dropIndicatorState()) {\r\n <div\r\n [ptInt]=\"this\"\r\n [ptClass]=\"'drop-indicator'\"\r\n [style.left.px]=\"_dropIndicatorState()!.leftPx\"\r\n [style.top.px]=\"_dropIndicatorState()!.topPx\"\r\n [style.height.px]=\"_dropIndicatorState()!.heightPx\"\r\n ></div>\r\n }\r\n\r\n <tbody\r\n [ptInt]=\"this\"\r\n [ptClass]=\"'body'\"\r\n ngn-scroller\r\n #scroller\r\n [scrollContainer]=\"scrollContainer\"\r\n [virtual]=\"virtual()\"\r\n [items]=\"pagedRows()\"\r\n [fieldId]=\"'id'\"\r\n [itemHeight]=\"rowHeight() || 0\"\r\n [virtualPadding]=\"virtualPadding()\"\r\n [style.--ngn-table-item-start-index]=\"scroller.itemStartIndex()\"\r\n >\r\n <ng-template #item [ngnTemplate]=\"scroller.templateTypes.item\" let-item let-index=\"index\">\r\n @if (item.kind === 'group-header') {\r\n @if (groupHeaderTemplate(); as customGroupHeader) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"customGroupHeader\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: {\r\n groupKey: item.groupKey,\r\n groupValue: item.groupValue,\r\n count: item.count,\r\n expanded: item.expanded,\r\n },\r\n }\"\r\n ></ng-container>\r\n } @else {\r\n <tr\r\n [ngnTableGroupHeaderTr]=\"item\"\r\n [class]=\"theme.classes({ 'group-expanded': item.expanded })\"\r\n [attr.aria-expanded]=\"item.expanded\"\r\n role=\"row\"\r\n (click)=\"toggleGroupFromRow(item)\"\r\n >\r\n <td [class]=\"theme.class('group-header-cell')\" role=\"gridcell\">\r\n <ngn-icon\r\n [defaultIcon]=\"'table-group-toggle'\"\r\n [class]=\"theme.class('group-toggle')\"\r\n />\r\n <span>{{ item.groupValue }}</span>\r\n <span>({{ item.count }})</span>\r\n </td>\r\n </tr>\r\n }\r\n } @else {\r\n <ng-container\r\n [ngTemplateOutlet]=\"bodyTemplate()\"\r\n [ngTemplateOutletContext]=\"{ $implicit: item }\"\r\n ></ng-container>\r\n }\r\n </ng-template>\r\n </tbody>\r\n</table>\r\n@if (paginator()) {\r\n <ngn-paginator [totalItems]=\"rows().length\" (value)=\"pageChanged($event)\" />\r\n}\r\n" }]
825
889
  }], ctorParameters: () => [], propDecorators: { _scroller: [{ type: i0.ViewChild, args: [i0.forwardRef(() => NgnScroller), { isSignal: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], fieldId: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldId", required: true }] }], virtual: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtual", required: false }] }], virtualPadding: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualPadding", required: false }] }], striped: [{ type: i0.Input, args: [{ isSignal: true, alias: "striped", required: false }] }], paginator: [{ type: i0.Input, args: [{ isSignal: true, alias: "paginator", required: false }] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }, { type: i0.Output, args: ["selectionChange"] }], groupBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupBy", required: false }] }], expandedGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedGroups", required: false }] }, { type: i0.Output, args: ["expandedGroupsChange"] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], resizeMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizeMode", required: false }] }], lockSizes: [{ type: i0.Input, args: [{ isSignal: true, alias: "lockSizes", required: false }] }], reorderable: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorderable", required: false }] }], columnOrder: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnOrder", required: false }] }, { type: i0.Output, args: ["columnOrderChange"] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }, { type: i0.Output, args: ["sortChange"] }], filters: [{ type: i0.Input, args: [{ isSignal: true, alias: "filters", required: false }] }, { type: i0.Output, args: ["filtersChange"] }] } });
826
890
  // --- Auto-size helpers ---
827
891
  /** Lazily created canvas context for text measurement (no DOM reflows). */
@@ -854,6 +918,22 @@ class NgnTableTd extends NgnBase {
854
918
  }
855
919
  return logicalIndex + 1;
856
920
  }, ...(ngDevMode ? [{ debugName: "_visualColumnIndex" }] : /* istanbul ignore next */ []));
921
+ _columnId = computed(() => {
922
+ const table = this._table();
923
+ if (!table)
924
+ return null;
925
+ const offset = table.showCheckboxes() ? 1 : 0;
926
+ const logicalIndex = this._logicalIndex() - offset;
927
+ const cells = table.getRegisteredHeaderCells();
928
+ return cells[logicalIndex]?.ngnTableTh() ?? null;
929
+ }, ...(ngDevMode ? [{ debugName: "_columnId" }] : /* istanbul ignore next */ []));
930
+ _stickyInfo = computed(() => {
931
+ const table = this._table();
932
+ const columnId = this._columnId();
933
+ if (!table || !columnId)
934
+ return null;
935
+ return table.getStickyInfo(columnId);
936
+ }, ...(ngDevMode ? [{ debugName: "_stickyInfo" }] : /* istanbul ignore next */ []));
857
937
  constructor() {
858
938
  super();
859
939
  this.prepareDom();
@@ -866,14 +946,38 @@ class NgnTableTd extends NgnBase {
866
946
  const table = getNearestNgnInstance(this.element.nativeElement, NgnTable);
867
947
  this._table.set(table);
868
948
  });
949
+ afterRenderEffect(() => {
950
+ const info = this._stickyInfo();
951
+ const el = this.element.nativeElement;
952
+ toggleClass(el, this.theme.class('sticky-start'), info?.side === 'start');
953
+ toggleClass(el, this.theme.class('sticky-end'), info?.side === 'end');
954
+ toggleClass(el, this.theme.class('sticky-start-edge'), info?.side === 'start' && info.isEdge);
955
+ toggleClass(el, this.theme.class('sticky-end-edge'), info?.side === 'end' && info.isEdge);
956
+ if (info) {
957
+ el.style.position = 'sticky';
958
+ if (info.side === 'start') {
959
+ el.style.left = `var(--ngn-sticky-start-offset-${info.index})`;
960
+ el.style.removeProperty('right');
961
+ }
962
+ else {
963
+ el.style.right = `var(--ngn-sticky-end-offset-${info.index})`;
964
+ el.style.removeProperty('left');
965
+ }
966
+ }
967
+ else {
968
+ el.style.removeProperty('position');
969
+ el.style.removeProperty('left');
970
+ el.style.removeProperty('right');
971
+ }
972
+ });
869
973
  }
870
974
  prepareDom() {
871
975
  toggleClass(this.element.nativeElement, this.theme.class('cell'), true);
872
976
  }
873
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableTd, deps: [], target: i0.ɵɵFactoryTarget.Directive });
874
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.2", type: NgnTableTd, isStandalone: true, selector: "[ngnTableTd]", host: { properties: { "style.--ngn-table-column-index": "_visualColumnIndex()" } }, usesInheritance: true, ngImport: i0 });
977
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableTd, deps: [], target: i0.ɵɵFactoryTarget.Directive });
978
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.10", type: NgnTableTd, isStandalone: true, selector: "[ngnTableTd]", host: { properties: { "style.--ngn-table-column-index": "_visualColumnIndex()" } }, usesInheritance: true, ngImport: i0 });
875
979
  }
876
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableTd, decorators: [{
980
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableTd, decorators: [{
877
981
  type: Directive,
878
982
  args: [{
879
983
  selector: '[ngnTableTd]',
@@ -884,6 +988,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
884
988
  class NgnTableTh extends NgnBase {
885
989
  theme = this.injectThemeTemplate(tableControlTemplate);
886
990
  _table;
991
+ _tableSignal = signal(null, ...(ngDevMode ? [{ debugName: "_tableSignal" }] : /* istanbul ignore next */ []));
887
992
  _resizeHandle;
888
993
  ngnTableTh = input.required(...(ngDevMode ? [{ debugName: "ngnTableTh" }] : /* istanbul ignore next */ []));
889
994
  /**
@@ -969,6 +1074,34 @@ class NgnTableTh extends NgnBase {
969
1074
  this._resizeHandle.style.display = show ? '' : 'none';
970
1075
  }
971
1076
  });
1077
+ // Sticky column classes and offsets
1078
+ afterRenderEffect(() => {
1079
+ const table = this._tableSignal();
1080
+ if (!table)
1081
+ return;
1082
+ const info = table.getStickyInfo(this.ngnTableTh());
1083
+ const el = this.element.nativeElement;
1084
+ toggleClass(el, this.theme.class('sticky-start'), info?.side === 'start');
1085
+ toggleClass(el, this.theme.class('sticky-end'), info?.side === 'end');
1086
+ toggleClass(el, this.theme.class('sticky-start-edge'), info?.side === 'start' && info.isEdge);
1087
+ toggleClass(el, this.theme.class('sticky-end-edge'), info?.side === 'end' && info.isEdge);
1088
+ if (info) {
1089
+ el.style.position = 'sticky';
1090
+ if (info.side === 'start') {
1091
+ el.style.left = `var(--ngn-sticky-start-offset-${info.index})`;
1092
+ el.style.removeProperty('right');
1093
+ }
1094
+ else {
1095
+ el.style.right = `var(--ngn-sticky-end-offset-${info.index})`;
1096
+ el.style.removeProperty('left');
1097
+ }
1098
+ }
1099
+ else {
1100
+ el.style.removeProperty('position');
1101
+ el.style.removeProperty('left');
1102
+ el.style.removeProperty('right');
1103
+ }
1104
+ });
972
1105
  }
973
1106
  ngOnInit() {
974
1107
  const table = getNearestNgnInstance(this.element.nativeElement, (NgnTable));
@@ -976,6 +1109,7 @@ class NgnTableTh extends NgnBase {
976
1109
  throw new NgnError('ngnTableTh', 'ngnTableTh must be used within an NgnTable component');
977
1110
  }
978
1111
  this._table = table;
1112
+ this._tableSignal.set(table);
979
1113
  this._table.registerHeaderCell(this);
980
1114
  }
981
1115
  ngOnDestroy() {
@@ -1034,10 +1168,10 @@ class NgnTableTh extends NgnBase {
1034
1168
  const cells = this._table.getRegisteredHeaderCells();
1035
1169
  return cells.indexOf(this);
1036
1170
  }
1037
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableTh, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1038
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.2", type: NgnTableTh, isStandalone: true, selector: "[ngnTableTh]", inputs: { ngnTableTh: { classPropertyName: "ngnTableTh", publicName: "ngnTableTh", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, minSize: { classPropertyName: "minSize", publicName: "minSize", isSignal: true, isRequired: false, transformFunction: null }, maxSize: { classPropertyName: "maxSize", publicName: "maxSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { size: "sizeChange" }, host: { properties: { "style.--ngn-table-column-index": "_visualColumnIndex()" } }, usesInheritance: true, ngImport: i0 });
1171
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableTh, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1172
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.10", type: NgnTableTh, isStandalone: true, selector: "[ngnTableTh]", inputs: { ngnTableTh: { classPropertyName: "ngnTableTh", publicName: "ngnTableTh", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, minSize: { classPropertyName: "minSize", publicName: "minSize", isSignal: true, isRequired: false, transformFunction: null }, maxSize: { classPropertyName: "maxSize", publicName: "maxSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { size: "sizeChange" }, host: { properties: { "style.--ngn-table-column-index": "_visualColumnIndex()" } }, usesInheritance: true, ngImport: i0 });
1039
1173
  }
1040
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableTh, decorators: [{
1174
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableTh, decorators: [{
1041
1175
  type: Directive,
1042
1176
  args: [{
1043
1177
  selector: '[ngnTableTh]',
@@ -1057,10 +1191,10 @@ class NgnTableHeadTr extends NgnBase {
1057
1191
  prepareDom() {
1058
1192
  toggleClass(this._element.nativeElement, this.theme.class('row'), true);
1059
1193
  }
1060
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableHeadTr, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1061
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.2", type: NgnTableHeadTr, isStandalone: true, selector: "[ngnTableHeadTr]", host: { properties: { "attr.aria-rowindex": "1" } }, usesInheritance: true, ngImport: i0 });
1194
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableHeadTr, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1195
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.10", type: NgnTableHeadTr, isStandalone: true, selector: "[ngnTableHeadTr]", host: { properties: { "attr.aria-rowindex": "1" } }, usesInheritance: true, ngImport: i0 });
1062
1196
  }
1063
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableHeadTr, decorators: [{
1197
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableHeadTr, decorators: [{
1064
1198
  type: Directive,
1065
1199
  args: [{ selector: '[ngnTableHeadTr]', host: { '[attr.aria-rowindex]': '1' } }]
1066
1200
  }], ctorParameters: () => [] });
@@ -1102,10 +1236,6 @@ class NgnTableReorderableColumn {
1102
1236
  const target = event.target;
1103
1237
  if (target.classList.contains(this.theme.class('resize-handle')))
1104
1238
  return;
1105
- // Don't allow reordering of sticky columns
1106
- const stickyMap = table.getStickyColumnIds();
1107
- if (stickyMap.has(this._columnId()))
1108
- return;
1109
1239
  event.preventDefault();
1110
1240
  this._startX = event.clientX;
1111
1241
  this._startY = event.clientY;
@@ -1190,10 +1320,10 @@ class NgnTableReorderableColumn {
1190
1320
  this._boundOnKeyDown = null;
1191
1321
  }
1192
1322
  }
1193
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableReorderableColumn, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1194
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.2", type: NgnTableReorderableColumn, isStandalone: true, selector: "[ngnTableReorderableColumn]", ngImport: i0 });
1323
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableReorderableColumn, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1324
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.10", type: NgnTableReorderableColumn, isStandalone: true, selector: "[ngnTableReorderableColumn]", ngImport: i0 });
1195
1325
  }
1196
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableReorderableColumn, decorators: [{
1326
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableReorderableColumn, decorators: [{
1197
1327
  type: Directive,
1198
1328
  args: [{
1199
1329
  selector: '[ngnTableReorderableColumn]',
@@ -1238,10 +1368,10 @@ class NgnTableBodyTr extends NgnScrollerItem {
1238
1368
  return;
1239
1369
  table.handleRowClick(this.ngnTableBodyTr(), event);
1240
1370
  }
1241
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableBodyTr, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1242
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.2", type: NgnTableBodyTr, isStandalone: true, selector: "[ngnTableBodyTr]", inputs: { ngnTableBodyTr: { classPropertyName: "ngnTableBodyTr", publicName: "ngnTableBodyTr", isSignal: true, isRequired: true, transformFunction: null }, ngnScrollerItem: { classPropertyName: "ngnScrollerItem", publicName: "ngnScrollerItem", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "row" }, listeners: { "click": "onRowClick($event)" }, properties: { "attr.aria-rowindex": "ngnTableBodyTr().index + 2", "style.--ngn-table-row-index": "ngnTableBodyTr().index + 2", "class": "theme.classes({\n 'even': ngnTableBodyTr().index % 2 === 0,\n 'selected-row': selected(),\n 'focused-row': focused()\n })", "attr.aria-selected": "selectable() ? selected() : null" } }, usesInheritance: true, ngImport: i0 });
1371
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableBodyTr, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1372
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.10", type: NgnTableBodyTr, isStandalone: true, selector: "[ngnTableBodyTr]", inputs: { ngnTableBodyTr: { classPropertyName: "ngnTableBodyTr", publicName: "ngnTableBodyTr", isSignal: true, isRequired: true, transformFunction: null }, ngnScrollerItem: { classPropertyName: "ngnScrollerItem", publicName: "ngnScrollerItem", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "row" }, listeners: { "click": "onRowClick($event)" }, properties: { "attr.aria-rowindex": "ngnTableBodyTr().index + 2", "style.--ngn-table-row-index": "ngnTableBodyTr().index + 2", "class": "theme.classes({\n 'even': ngnTableBodyTr().index % 2 === 0,\n 'selected-row': selected(),\n 'focused-row': focused()\n })", "attr.aria-selected": "selectable() ? selected() : null" } }, usesInheritance: true, ngImport: i0 });
1243
1373
  }
1244
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableBodyTr, decorators: [{
1374
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableBodyTr, decorators: [{
1245
1375
  type: Directive,
1246
1376
  args: [{
1247
1377
  selector: '[ngnTableBodyTr]',
@@ -1352,7 +1482,7 @@ class NgnTableSelectionColumn {
1352
1482
  const cbEl = this._checkboxRef.location.nativeElement;
1353
1483
  cbEl.classList.add(this.theme.class('selection-checkbox'));
1354
1484
  if (this._isHeader) {
1355
- this._checkboxRef.setInput('allowIndeterminate', true);
1485
+ setComponentInput(this._checkboxRef, 'allowIndeterminate', true);
1356
1486
  }
1357
1487
  this._element.nativeElement.appendChild(cbEl);
1358
1488
  }
@@ -1396,10 +1526,10 @@ class NgnTableSelectionColumn {
1396
1526
  }
1397
1527
  }
1398
1528
  }
1399
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableSelectionColumn, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1400
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.2", type: NgnTableSelectionColumn, isStandalone: true, selector: "[ngnTableSelectionColumn]", host: { listeners: { "click": "onClick($event)" } }, ngImport: i0 });
1529
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableSelectionColumn, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1530
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.10", type: NgnTableSelectionColumn, isStandalone: true, selector: "[ngnTableSelectionColumn]", host: { listeners: { "click": "onClick($event)" } }, ngImport: i0 });
1401
1531
  }
1402
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableSelectionColumn, decorators: [{
1532
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableSelectionColumn, decorators: [{
1403
1533
  type: Directive,
1404
1534
  args: [{
1405
1535
  selector: '[ngnTableSelectionColumn]',
@@ -1409,223 +1539,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
1409
1539
  }]
1410
1540
  }], ctorParameters: () => [] });
1411
1541
 
1412
- const tableTracker = new WeakMap();
1413
- function attachTableTracking(scrollContainer, tableHostEl, directive, theme) {
1414
- const existing = tableTracker.get(scrollContainer);
1415
- if (existing) {
1416
- existing.directives.add(directive);
1417
- return;
1418
- }
1419
- const updateScrollClasses = () => {
1420
- const el = scrollContainer;
1421
- const scrolledLeft = el.scrollLeft > 1;
1422
- const scrolledRight = el.scrollLeft < el.scrollWidth - el.clientWidth - 1;
1423
- toggleClass(tableHostEl, theme.class('sticky-scrolled-left'), scrolledLeft);
1424
- toggleClass(tableHostEl, theme.class('sticky-scrolled-right'), scrolledRight);
1425
- };
1426
- const stickyColumnClass = theme.class('sticky-column');
1427
- const selectionColumnClass = theme.class('selection-column');
1428
- const recalcAllOffsets = () => {
1429
- const state = tableTracker.get(scrollContainer);
1430
- if (!state)
1431
- return;
1432
- // Make all checkbox/selection columns sticky (they're created dynamically,
1433
- // not via template, so the directive can't be placed on them).
1434
- const selectionCols = Array.from(scrollContainer.querySelectorAll(`.${selectionColumnClass}`));
1435
- for (const col of selectionCols) {
1436
- if (!col.classList.contains(stickyColumnClass)) {
1437
- toggleClass(col, stickyColumnClass, true);
1438
- toggleClass(col, theme.class('sticky-left'), true);
1439
- }
1440
- col.style.left = '0px';
1441
- }
1442
- for (const dir of state.directives) {
1443
- dir._recalcOffset();
1444
- }
1445
- updateScrollClasses();
1446
- };
1447
- scrollContainer.addEventListener('scroll', updateScrollClasses, { passive: true });
1448
- const directives = new Set();
1449
- directives.add(directive);
1450
- // Coalesce rapid ResizeObserver callbacks (e.g. from selection class toggles)
1451
- // into a single recalculation per animation frame.
1452
- const state = {
1453
- scrollListener: updateScrollClasses,
1454
- resizeObserver: null,
1455
- recalcRafId: 0,
1456
- directives,
1457
- tableHostEl,
1458
- };
1459
- const debouncedRecalc = () => {
1460
- if (state.recalcRafId)
1461
- return;
1462
- state.recalcRafId = requestAnimationFrame(() => {
1463
- state.recalcRafId = 0;
1464
- recalcAllOffsets();
1465
- });
1466
- };
1467
- state.resizeObserver = new ResizeObserver(() => debouncedRecalc());
1468
- state.resizeObserver.observe(scrollContainer);
1469
- // Also observe <thead> — its subgrid cells change size when grid-template-columns updates,
1470
- // but the table container's border-box may not change (overflow: auto).
1471
- const thead = scrollContainer.querySelector('thead');
1472
- if (thead) {
1473
- state.resizeObserver.observe(thead);
1474
- }
1475
- // Initial update
1476
- requestAnimationFrame(() => {
1477
- recalcAllOffsets();
1478
- updateScrollClasses();
1479
- });
1480
- tableTracker.set(scrollContainer, state);
1481
- }
1482
- function detachTableTracking(scrollContainer, directive) {
1483
- const state = tableTracker.get(scrollContainer);
1484
- if (!state)
1485
- return;
1486
- state.directives.delete(directive);
1487
- if (state.directives.size === 0) {
1488
- scrollContainer.removeEventListener('scroll', state.scrollListener);
1489
- state.resizeObserver.disconnect();
1490
- if (state.recalcRafId)
1491
- cancelAnimationFrame(state.recalcRafId);
1492
- toggleClass(state.tableHostEl, state.tableHostEl.className.split(' ').find(c => c.includes('sticky-scrolled-left')) ?? '', false);
1493
- toggleClass(state.tableHostEl, state.tableHostEl.className.split(' ').find(c => c.includes('sticky-scrolled-right')) ?? '', false);
1494
- tableTracker.delete(scrollContainer);
1495
- }
1496
- }
1497
- // ── Directive ───────────────────────────────────────────────────────────────
1498
- /**
1499
- * @category directive
1500
- */
1501
1542
  class NgnTableStickyColumn {
1502
- theme = injectThemeTemplate(tableControlTemplate);
1503
1543
  _element = inject(ElementRef);
1504
- _headerCell = inject(NgnTableTh, { optional: true });
1505
- _table = getNearestNgnInstanceSig(this._element.nativeElement, NgnTable);
1506
- /**
1507
- * Which side this column sticks to.
1508
- */
1544
+ _headerCell = inject(NgnTableTh);
1545
+ _columnId = this._headerCell.ngnTableTh;
1509
1546
  ngnTableStickyColumn = input.required(...(ngDevMode ? [{ debugName: "ngnTableStickyColumn" }] : /* istanbul ignore next */ []));
1510
- _scrollContainer = null;
1547
+ _table = getNearestNgnInstanceSig(this._element.nativeElement, NgnTable);
1511
1548
  constructor() {
1512
- const el = this._element.nativeElement;
1513
- // Apply static sticky classes immediately
1514
- toggleClass(el, this.theme.class('sticky-column'), true);
1515
1549
  afterNextRender(() => {
1516
- const side = this.ngnTableStickyColumn();
1517
- toggleClass(el, this.theme.class('sticky-left'), side === 'left');
1518
- toggleClass(el, this.theme.class('sticky-right'), side === 'right');
1519
- // Register with table for reorder awareness
1520
1550
  const table = this._table();
1521
- const columnId = this._getColumnId();
1522
- if (table && columnId) {
1523
- table.registerStickyColumnId(columnId, side);
1551
+ if (table) {
1552
+ table.registerStickyColumn(this._columnId(), this.ngnTableStickyColumn());
1524
1553
  }
1525
- // Set up shared scroll + resize listener
1526
- this._scrollContainer = el.closest('table');
1527
- if (this._scrollContainer && table) {
1528
- attachTableTracking(this._scrollContainer, table.element.nativeElement, this, this.theme);
1529
- }
1530
- // Initial offset + boundary calculation
1531
- this._recalcOffset();
1532
1554
  });
1533
1555
  }
1534
1556
  ngOnDestroy() {
1535
- const table = this._table();
1536
- const columnId = this._getColumnId();
1537
- if (table && columnId) {
1538
- table.unregisterStickyColumnId(columnId);
1539
- }
1540
- if (this._scrollContainer) {
1541
- detachTableTracking(this._scrollContainer, this);
1542
- }
1543
- }
1544
- /**
1545
- * Recalculates sticky offset and boundary classes. Called by the shared
1546
- * ResizeObserver when the table container changes size.
1547
- * @internal
1548
- */
1549
- _recalcOffset() {
1550
- const el = this._element.nativeElement;
1551
- const side = this.ngnTableStickyColumn();
1552
- this._applyStickyOffset(el, side);
1553
- this._applyBoundaryClasses(el, side);
1554
- }
1555
- _getColumnId() {
1556
- return this._headerCell?.ngnTableTh() ?? null;
1557
- }
1558
- /**
1559
- * Computes and applies the sticky offset by measuring sibling sticky elements.
1560
- * For left: offset = sum of widths of all preceding sticky-left cells + selection column.
1561
- * For right: offset = sum of widths of all following sticky-right cells.
1562
- */
1563
- _applyStickyOffset(el, side) {
1564
- const stickyLeftClass = this.theme.class('sticky-left');
1565
- const stickyRightClass = this.theme.class('sticky-right');
1566
- const selectionColumnClass = this.theme.class('selection-column');
1567
- // Find the row-level container.
1568
- // For headers: all sticky cells in <thead>
1569
- // For body: all sticky cells in the same <tr>
1570
- const container = el.tagName === 'TH' ? el.closest('thead') : el.parentElement;
1571
- if (!container)
1572
- return;
1573
- if (side === 'left') {
1574
- let offset = 0;
1575
- // Sum widths of all preceding sticky-left siblings (includes selection column
1576
- // which was given sticky-left by the shared tracker)
1577
- const stickyCells = Array.from(container.querySelectorAll(`.${stickyLeftClass}`));
1578
- for (const cell of stickyCells) {
1579
- if (cell === el)
1580
- break;
1581
- offset += cell.getBoundingClientRect().width;
1582
- }
1583
- el.style.left = `${offset}px`;
1584
- el.style.right = '';
1585
- }
1586
- else {
1587
- let offset = 0;
1588
- // Sum widths of all following sticky-right siblings
1589
- const stickyCells = Array.from(container.querySelectorAll(`.${stickyRightClass}`));
1590
- let found = false;
1591
- for (let i = stickyCells.length - 1; i >= 0; i--) {
1592
- if (stickyCells[i] === el) {
1593
- found = true;
1594
- break;
1595
- }
1596
- if (!found) {
1597
- offset += stickyCells[i].getBoundingClientRect().width;
1598
- }
1599
- }
1600
- el.style.right = `${offset}px`;
1601
- el.style.left = '';
1602
- }
1603
- }
1604
- /**
1605
- * Marks boundary cells: last sticky-left gets `sticky-left-last`,
1606
- * first sticky-right gets `sticky-right-first`.
1607
- */
1608
- _applyBoundaryClasses(el, side) {
1609
- const stickyLeftClass = this.theme.class('sticky-left');
1610
- const stickyRightClass = this.theme.class('sticky-right');
1611
- const container = el.tagName === 'TH' ? el.closest('thead') : el.parentElement;
1612
- if (!container)
1613
- return;
1614
- if (side === 'left') {
1615
- const stickyCells = Array.from(container.querySelectorAll(`.${stickyLeftClass}`));
1616
- const isLast = stickyCells[stickyCells.length - 1] === el;
1617
- toggleClass(el, this.theme.class('sticky-left-last'), isLast);
1618
- }
1619
- else {
1620
- const stickyCells = Array.from(container.querySelectorAll(`.${stickyRightClass}`));
1621
- const isFirst = stickyCells[0] === el;
1622
- toggleClass(el, this.theme.class('sticky-right-first'), isFirst);
1623
- }
1557
+ this._table()?.unregisterStickyColumn(this._columnId());
1624
1558
  }
1625
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableStickyColumn, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1626
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.2", type: NgnTableStickyColumn, isStandalone: true, selector: "[ngnTableStickyColumn]", inputs: { ngnTableStickyColumn: { classPropertyName: "ngnTableStickyColumn", publicName: "ngnTableStickyColumn", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
1559
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableStickyColumn, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1560
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.10", type: NgnTableStickyColumn, isStandalone: true, selector: "[ngnTableStickyColumn]", inputs: { ngnTableStickyColumn: { classPropertyName: "ngnTableStickyColumn", publicName: "ngnTableStickyColumn", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 });
1627
1561
  }
1628
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableStickyColumn, decorators: [{
1562
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableStickyColumn, decorators: [{
1629
1563
  type: Directive,
1630
1564
  args: [{
1631
1565
  selector: '[ngnTableStickyColumn]',
@@ -1633,8 +1567,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImpor
1633
1567
  }], ctorParameters: () => [], propDecorators: { ngnTableStickyColumn: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngnTableStickyColumn", required: true }] }] } });
1634
1568
 
1635
1569
  class NgnTableModule {
1636
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1637
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.2", ngImport: i0, type: NgnTableModule, imports: [NgnTable,
1570
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1571
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.10", ngImport: i0, type: NgnTableModule, imports: [NgnTable,
1638
1572
  NgnTableBodyTr,
1639
1573
  NgnTableTd,
1640
1574
  NgnTableTh,
@@ -1651,9 +1585,9 @@ class NgnTableModule {
1651
1585
  NgnTableReorderableColumn,
1652
1586
  NgnTableSelectionColumn,
1653
1587
  NgnTableStickyColumn] });
1654
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableModule, imports: [NgnTable] });
1588
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableModule, imports: [NgnTable] });
1655
1589
  }
1656
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableModule, decorators: [{
1590
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableModule, decorators: [{
1657
1591
  type: NgModule,
1658
1592
  args: [{
1659
1593
  imports: [
@@ -1716,7 +1650,7 @@ class NgnTableFilterableColumn {
1716
1650
  label: 'Filter',
1717
1651
  value: null,
1718
1652
  kind: 'icon',
1719
- defaultIcon: this.filter() ? 'filter_solid' : 'filter',
1653
+ defaultIcon: this.filter() ? 'filter-active' : 'filter-inactive',
1720
1654
  action: event => {
1721
1655
  event.stopPropagation();
1722
1656
  this._ngnFilter.instance.show();
@@ -1744,10 +1678,10 @@ class NgnTableFilterableColumn {
1744
1678
  this._ngnActionButton.destroy();
1745
1679
  this._ngnFilter.destroy();
1746
1680
  }
1747
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableFilterableColumn, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1748
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.2", type: NgnTableFilterableColumn, isStandalone: true, selector: "[ngnTableFilterableColumn]", inputs: { ngnTableFilterableColumn: { classPropertyName: "ngnTableFilterableColumn", publicName: "ngnTableFilterableColumn", isSignal: true, isRequired: false, transformFunction: null }, ngnTableFilterableColumnType: { classPropertyName: "ngnTableFilterableColumnType", publicName: "ngnTableFilterableColumnType", isSignal: true, isRequired: true, transformFunction: null }, ngnTableFilterableColumnItems: { classPropertyName: "ngnTableFilterableColumnItems", publicName: "ngnTableFilterableColumnItems", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "theme.classes({'filterable-column': true, 'filtered-column': !!filter() })" } }, ngImport: i0 });
1681
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableFilterableColumn, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1682
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.10", type: NgnTableFilterableColumn, isStandalone: true, selector: "[ngnTableFilterableColumn]", inputs: { ngnTableFilterableColumn: { classPropertyName: "ngnTableFilterableColumn", publicName: "ngnTableFilterableColumn", isSignal: true, isRequired: false, transformFunction: null }, ngnTableFilterableColumnType: { classPropertyName: "ngnTableFilterableColumnType", publicName: "ngnTableFilterableColumnType", isSignal: true, isRequired: true, transformFunction: null }, ngnTableFilterableColumnItems: { classPropertyName: "ngnTableFilterableColumnItems", publicName: "ngnTableFilterableColumnItems", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "theme.classes({'filterable-column': true, 'filtered-column': !!filter() })" } }, ngImport: i0 });
1749
1683
  }
1750
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableFilterableColumn, decorators: [{
1684
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableFilterableColumn, decorators: [{
1751
1685
  type: Directive,
1752
1686
  args: [{
1753
1687
  selector: '[ngnTableFilterableColumn]',
@@ -1780,7 +1714,7 @@ class NgnTableSortableColumn {
1780
1714
  this._ngnIcon.location.nativeElement.classList.add(this.theme.class('sort-control'));
1781
1715
  effect(() => {
1782
1716
  const sort = this.sort();
1783
- setComponentInput(this._ngnIcon, 'defaultIcon', sort === 'asc' ? 'sort_down' : sort === 'desc' ? 'sort_up' : 'sort');
1717
+ setComponentInput(this._ngnIcon, 'defaultIcon', sort === 'asc' ? 'sort-ascending' : sort === 'desc' ? 'sort-descending' : 'sort-neutral');
1784
1718
  });
1785
1719
  }
1786
1720
  ngOnDestroy() {
@@ -1801,10 +1735,10 @@ class NgnTableSortableColumn {
1801
1735
  ? { column: this.columnId(), direction: 'desc' }
1802
1736
  : null);
1803
1737
  }
1804
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableSortableColumn, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1805
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.2", type: NgnTableSortableColumn, isStandalone: true, selector: "[ngnTableSortableColumn]", inputs: { ngnTableSortableColumn: { classPropertyName: "ngnTableSortableColumn", publicName: "ngnTableSortableColumn", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onHostClick($event)" }, properties: { "class": "theme.classes({'sortable-column': true, 'sorted-column': !!sort() })" } }, ngImport: i0 });
1738
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableSortableColumn, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1739
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.10", type: NgnTableSortableColumn, isStandalone: true, selector: "[ngnTableSortableColumn]", inputs: { ngnTableSortableColumn: { classPropertyName: "ngnTableSortableColumn", publicName: "ngnTableSortableColumn", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onHostClick($event)" }, properties: { "class": "theme.classes({'sortable-column': true, 'sorted-column': !!sort() })" } }, ngImport: i0 });
1806
1740
  }
1807
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: NgnTableSortableColumn, decorators: [{
1741
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: NgnTableSortableColumn, decorators: [{
1808
1742
  type: Directive,
1809
1743
  args: [{
1810
1744
  selector: '[ngnTableSortableColumn]',