@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.
- package/fesm2022/ngneers-controls-accordion.mjs +10 -10
- package/fesm2022/ngneers-controls-accordion.mjs.map +1 -1
- package/fesm2022/ngneers-controls-api-ng.mjs +12 -9
- package/fesm2022/ngneers-controls-api-ng.mjs.map +1 -1
- package/fesm2022/ngneers-controls-avatar.mjs +6 -6
- package/fesm2022/ngneers-controls-avatar.mjs.map +1 -1
- package/fesm2022/ngneers-controls-base.mjs +9 -9
- package/fesm2022/ngneers-controls-base.mjs.map +1 -1
- package/fesm2022/ngneers-controls-breadcrumb.mjs +7 -7
- package/fesm2022/ngneers-controls-breadcrumb.mjs.map +1 -1
- package/fesm2022/ngneers-controls-button-group.mjs +3 -3
- package/fesm2022/ngneers-controls-button-group.mjs.map +1 -1
- package/fesm2022/ngneers-controls-button.mjs +6 -6
- package/fesm2022/ngneers-controls-button.mjs.map +1 -1
- package/fesm2022/{ngneers-controls-calendar-time-Bzlv9Vkn.mjs → ngneers-controls-calendar-time-CKAqRgOP.mjs} +4 -4
- package/fesm2022/{ngneers-controls-calendar-time-Bzlv9Vkn.mjs.map → ngneers-controls-calendar-time-CKAqRgOP.mjs.map} +1 -1
- package/fesm2022/ngneers-controls-calendar.mjs +11 -11
- package/fesm2022/ngneers-controls-calendar.mjs.map +1 -1
- package/fesm2022/ngneers-controls-checkbox.mjs +4 -4
- package/fesm2022/ngneers-controls-checkbox.mjs.map +1 -1
- package/fesm2022/ngneers-controls-chip.mjs +4 -4
- package/fesm2022/ngneers-controls-chip.mjs.map +1 -1
- package/fesm2022/ngneers-controls-default-icons.mjs +61 -0
- package/fesm2022/ngneers-controls-default-icons.mjs.map +1 -0
- package/fesm2022/ngneers-controls-defer.mjs +3 -3
- package/fesm2022/ngneers-controls-defer.mjs.map +1 -1
- package/fesm2022/ngneers-controls-dialog.mjs +10 -10
- package/fesm2022/ngneers-controls-dialog.mjs.map +1 -1
- package/fesm2022/ngneers-controls-directives.mjs +21 -21
- package/fesm2022/ngneers-controls-directives.mjs.map +1 -1
- package/fesm2022/ngneers-controls-drawer.mjs +7 -7
- package/fesm2022/ngneers-controls-drawer.mjs.map +1 -1
- package/fesm2022/ngneers-controls-edit-inplace.mjs +7 -7
- package/fesm2022/ngneers-controls-edit-inplace.mjs.map +1 -1
- package/fesm2022/ngneers-controls-filter.mjs +4 -4
- package/fesm2022/ngneers-controls-filter.mjs.map +1 -1
- package/fesm2022/ngneers-controls-i18n.mjs +3 -3
- package/fesm2022/ngneers-controls-i18n.mjs.map +1 -1
- package/fesm2022/ngneers-controls-icon.mjs +108 -44
- package/fesm2022/ngneers-controls-icon.mjs.map +1 -1
- package/fesm2022/ngneers-controls-inplace.mjs +6 -6
- package/fesm2022/ngneers-controls-inplace.mjs.map +1 -1
- package/fesm2022/ngneers-controls-input-field.mjs +5 -5
- package/fesm2022/ngneers-controls-input-field.mjs.map +1 -1
- package/fesm2022/ngneers-controls-input-mask.mjs +3 -3
- package/fesm2022/ngneers-controls-input-mask.mjs.map +1 -1
- package/fesm2022/ngneers-controls-input.mjs +3 -3
- package/fesm2022/ngneers-controls-input.mjs.map +1 -1
- package/fesm2022/ngneers-controls-item-view.mjs +6 -6
- package/fesm2022/ngneers-controls-item-view.mjs.map +1 -1
- package/fesm2022/ngneers-controls-list-box.mjs +6 -6
- package/fesm2022/ngneers-controls-list-box.mjs.map +1 -1
- package/fesm2022/ngneers-controls-menu.mjs +10 -10
- package/fesm2022/ngneers-controls-menu.mjs.map +1 -1
- package/fesm2022/ngneers-controls-message.mjs +3 -3
- package/fesm2022/ngneers-controls-message.mjs.map +1 -1
- package/fesm2022/ngneers-controls-paginator.mjs +4 -4
- package/fesm2022/ngneers-controls-paginator.mjs.map +1 -1
- package/fesm2022/ngneers-controls-popover.mjs +11 -6
- package/fesm2022/ngneers-controls-popover.mjs.map +1 -1
- package/fesm2022/ngneers-controls-progress.mjs +3 -3
- package/fesm2022/ngneers-controls-progress.mjs.map +1 -1
- package/fesm2022/ngneers-controls-scroll-shadow.mjs +54 -0
- package/fesm2022/ngneers-controls-scroll-shadow.mjs.map +1 -0
- package/fesm2022/ngneers-controls-scroller.mjs +9 -9
- package/fesm2022/ngneers-controls-scroller.mjs.map +1 -1
- package/fesm2022/ngneers-controls-select-button.mjs +4 -4
- package/fesm2022/ngneers-controls-select-button.mjs.map +1 -1
- package/fesm2022/ngneers-controls-select.mjs +8 -7
- package/fesm2022/ngneers-controls-select.mjs.map +1 -1
- package/fesm2022/ngneers-controls-slider.mjs +3 -3
- package/fesm2022/ngneers-controls-slider.mjs.map +1 -1
- package/fesm2022/ngneers-controls-spinner.mjs +3 -3
- package/fesm2022/ngneers-controls-spinner.mjs.map +1 -1
- package/fesm2022/ngneers-controls-splitter.mjs +10 -10
- package/fesm2022/ngneers-controls-splitter.mjs.map +1 -1
- package/fesm2022/ngneers-controls-switch.mjs +3 -3
- package/fesm2022/ngneers-controls-switch.mjs.map +1 -1
- package/fesm2022/ngneers-controls-table.mjs +227 -293
- package/fesm2022/ngneers-controls-table.mjs.map +1 -1
- package/fesm2022/ngneers-controls-tabs.mjs +10 -10
- package/fesm2022/ngneers-controls-tabs.mjs.map +1 -1
- package/fesm2022/ngneers-controls-tag.mjs +3 -3
- package/fesm2022/ngneers-controls-tag.mjs.map +1 -1
- package/fesm2022/ngneers-controls-toast.mjs +10 -10
- package/fesm2022/ngneers-controls-toast.mjs.map +1 -1
- package/fesm2022/ngneers-controls-toggle-button.mjs +4 -4
- package/fesm2022/ngneers-controls-toggle-button.mjs.map +1 -1
- package/fesm2022/ngneers-controls-tooltip.mjs +44 -8
- package/fesm2022/ngneers-controls-tooltip.mjs.map +1 -1
- package/fesm2022/ngneers-controls-utils-ng.mjs +3 -3
- package/fesm2022/ngneers-controls-utils-ng.mjs.map +1 -1
- package/fesm2022/ngneers-controls-utils.mjs +1 -1
- package/fesm2022/ngneers-controls-utils.mjs.map +1 -1
- package/package.json +29 -11
- package/types/ngneers-controls-accordion.d.ts +6 -4
- package/types/ngneers-controls-api-ng.d.ts +5 -0
- package/types/ngneers-controls-api.d.ts +2 -2
- package/types/ngneers-controls-breadcrumb.d.ts +5 -3
- package/types/ngneers-controls-checkbox.d.ts +5 -3
- package/types/ngneers-controls-chip.d.ts +3 -1
- package/types/ngneers-controls-default-icons.d.ts +5 -0
- package/types/ngneers-controls-drawer.d.ts +3 -1
- package/types/ngneers-controls-filter.d.ts +1 -1
- package/types/ngneers-controls-icon.d.ts +23 -40
- package/types/ngneers-controls-input-field.d.ts +3 -1
- package/types/ngneers-controls-item-view.d.ts +4 -2
- package/types/ngneers-controls-menu.d.ts +3 -1
- package/types/ngneers-controls-message.d.ts +3 -1
- package/types/ngneers-controls-popover.d.ts +1 -0
- package/types/ngneers-controls-scroll-shadow.d.ts +16 -0
- package/types/ngneers-controls-select.d.ts +5 -10
- package/types/ngneers-controls-table.d.ts +29 -41
- package/types/ngneers-controls-tabs.d.ts +4 -2
- package/types/ngneers-controls-tag.d.ts +3 -1
- package/types/ngneers-controls-toast.d.ts +4 -2
- package/types/ngneers-controls-toggle-button.d.ts +5 -3
- package/types/ngneers-controls-tooltip.d.ts +7 -1
- package/fesm2022/ngneers-controls-icon-calendar-z5xA9wTT.mjs +0 -4
- package/fesm2022/ngneers-controls-icon-calendar-z5xA9wTT.mjs.map +0 -1
- package/fesm2022/ngneers-controls-icon-check-DdAQ9k-T.mjs +0 -4
- package/fesm2022/ngneers-controls-icon-check-DdAQ9k-T.mjs.map +0 -1
- package/fesm2022/ngneers-controls-icon-chevron_down-HO-z-xOr.mjs +0 -4
- package/fesm2022/ngneers-controls-icon-chevron_down-HO-z-xOr.mjs.map +0 -1
- package/fesm2022/ngneers-controls-icon-chevron_left-CC5psi16.mjs +0 -4
- package/fesm2022/ngneers-controls-icon-chevron_left-CC5psi16.mjs.map +0 -1
- package/fesm2022/ngneers-controls-icon-chevron_right-BRxlzx4G.mjs +0 -4
- package/fesm2022/ngneers-controls-icon-chevron_right-BRxlzx4G.mjs.map +0 -1
- package/fesm2022/ngneers-controls-icon-chevron_up-cqhPO7g2.mjs +0 -4
- package/fesm2022/ngneers-controls-icon-chevron_up-cqhPO7g2.mjs.map +0 -1
- package/fesm2022/ngneers-controls-icon-ellipsis-DXIGA1sk.mjs +0 -4
- package/fesm2022/ngneers-controls-icon-ellipsis-DXIGA1sk.mjs.map +0 -1
- package/fesm2022/ngneers-controls-icon-filter-oU7E6Xgr.mjs +0 -4
- package/fesm2022/ngneers-controls-icon-filter-oU7E6Xgr.mjs.map +0 -1
- package/fesm2022/ngneers-controls-icon-filter_solid-CqIKuR4N.mjs +0 -4
- package/fesm2022/ngneers-controls-icon-filter_solid-CqIKuR4N.mjs.map +0 -1
- package/fesm2022/ngneers-controls-icon-magnifying_glass-T4Hzbkig.mjs +0 -4
- package/fesm2022/ngneers-controls-icon-magnifying_glass-T4Hzbkig.mjs.map +0 -1
- package/fesm2022/ngneers-controls-icon-minus-oGDEFO4E.mjs +0 -4
- package/fesm2022/ngneers-controls-icon-minus-oGDEFO4E.mjs.map +0 -1
- package/fesm2022/ngneers-controls-icon-sort-CRD4tafz.mjs +0 -4
- package/fesm2022/ngneers-controls-icon-sort-CRD4tafz.mjs.map +0 -1
- package/fesm2022/ngneers-controls-icon-sort_down-DiFB1P6R.mjs +0 -4
- package/fesm2022/ngneers-controls-icon-sort_down-DiFB1P6R.mjs.map +0 -1
- package/fesm2022/ngneers-controls-icon-sort_up-Bq_q3hIi.mjs +0 -4
- package/fesm2022/ngneers-controls-icon-sort_up-Bq_q3hIi.mjs.map +0 -1
- package/fesm2022/ngneers-controls-icon-xmark-BZKZORiu.mjs +0 -4
- 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,
|
|
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.
|
|
36
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.
|
|
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.
|
|
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.
|
|
61
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.
|
|
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.
|
|
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
|
-
|
|
628
|
-
|
|
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.
|
|
808
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
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.
|
|
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
|
|
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.
|
|
874
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.
|
|
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.
|
|
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.
|
|
1038
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.
|
|
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.
|
|
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.
|
|
1061
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.
|
|
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.
|
|
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.
|
|
1194
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.
|
|
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.
|
|
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.
|
|
1242
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.
|
|
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.
|
|
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
|
|
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.
|
|
1400
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.
|
|
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.
|
|
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
|
|
1505
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1522
|
-
|
|
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
|
-
|
|
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.
|
|
1626
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.
|
|
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.
|
|
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.
|
|
1637
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.
|
|
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.
|
|
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.
|
|
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() ? '
|
|
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.
|
|
1748
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.
|
|
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.
|
|
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' ? '
|
|
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.
|
|
1805
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.
|
|
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.
|
|
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]',
|