forty-cdk 0.10.0 → 0.11.0
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/forty-cdk-table.mjs +958 -289
- package/fesm2022/forty-cdk-table.mjs.map +1 -1
- package/fesm2022/forty-cdk-virtualization.mjs +5 -1
- package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
- package/package.json +1 -1
- package/table/README.md +449 -27
- package/types/forty-cdk-table.d.ts +450 -115
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, booleanAttribute, inject, isSignal, signal, computed, input, model, ElementRef, Directive, TemplateRef, contentChild, Injector,
|
|
2
|
+
import { InjectionToken, booleanAttribute, inject, isSignal, signal, computed, input, model, ElementRef, Directive, TemplateRef, contentChild, Injector, PLATFORM_ID, output, DestroyRef, DOCUMENT, effect, afterNextRender, numberAttribute, viewChildren, afterEveryRender, contentChildren, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
3
|
import { injectTextDirection, injectElementSize, Collection, RovingTabindex, firstEnabledHost, computeFlatHierarchy, reconcileRovingActive, findFirstFocusable, resolveTreegridExpandCollapse, resolveGridNavigation, moveGridIndex, registerHandle, createPointerDragSession, DRAG_DEAD_ZONE_PX, clampToRange, translateWindowReorder, LiveAnnouncer, createKeyboardDragMediator, isDragLiftKey, resolveLiftedDragControl, resolveScrubReorder } from 'forty-cdk/core';
|
|
4
4
|
import { isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
|
|
5
5
|
import * as i1 from 'forty-cdk/drag-drop';
|
|
6
|
-
import { ForDropList, moveItemInArray, FOR_DROP_LIST_DEFAULT_ORIENTATION, FOR_DROP_LIST_ROVING_DELEGATE, FOR_DRAGGABLE_LIFT_GUARD, FOR_DRAG_DROP_DEFAULTS } from 'forty-cdk/drag-drop';
|
|
6
|
+
import { ForDropList, moveItemInArray, FOR_DROP_LIST_DEFAULT_ORIENTATION, FOR_DROP_LIST_ROVING_DELEGATE, FOR_DRAGGABLE_LIFT_GUARD, ForDraggable, ForDragPlaceholder, FOR_DRAG_DROP_DEFAULTS } from 'forty-cdk/drag-drop';
|
|
7
7
|
|
|
8
8
|
const FOR_TABLE_CONTEXT = new InjectionToken('FOR_TABLE_CONTEXT');
|
|
9
9
|
const FOR_TABLE_ROW_CONTEXT = new InjectionToken('FOR_TABLE_ROW_CONTEXT');
|
|
@@ -346,12 +346,25 @@ class ForTable {
|
|
|
346
346
|
_dirInput = input(null, { ...(ngDevMode ? { debugName: "_dirInput" } : /* istanbul ignore next */ {}), alias: 'dir' });
|
|
347
347
|
dir = injectTextDirection(this._dirInput);
|
|
348
348
|
/**
|
|
349
|
-
*
|
|
350
|
-
*
|
|
351
|
-
*
|
|
349
|
+
* Explicit override for the true total data-row count (`aria-rowcount` and the
|
|
350
|
+
* virtualized scroll range). A declarative `<for-table-body>` supplies this
|
|
351
|
+
* automatically from its `rows` dataset length, so bind `[rowCount]` only for a
|
|
352
|
+
* server-known total larger than the loaded rows; when set it wins over the
|
|
353
|
+
* body-derived count. Defaults to the body count, else the rendered data-row
|
|
354
|
+
* count. Ignored in `mode="table"`.
|
|
352
355
|
*/
|
|
353
|
-
|
|
354
|
-
|
|
356
|
+
_rowCountInput = input(undefined, { ...(ngDevMode ? { debugName: "_rowCountInput" } : /* istanbul ignore next */ {}), alias: 'rowCount' });
|
|
357
|
+
/** The declarative body's dataset length, registered by `<for-table-body>`; `null` when none. */
|
|
358
|
+
#bodyRowCount = signal(null, /* @ts-ignore */
|
|
359
|
+
...(ngDevMode ? [{ debugName: "#bodyRowCount" }] : /* istanbul ignore next */ []));
|
|
360
|
+
/**
|
|
361
|
+
* Resolved true total data-row count: the explicit `[rowCount]` input when set,
|
|
362
|
+
* else the declarative `<for-table-body>`'s dataset length, else `undefined`
|
|
363
|
+
* (readers fall back to the rendered count). Feeds `aria-rowcount`, the
|
|
364
|
+
* cross-window navigation total, and the virtualizer's count.
|
|
365
|
+
*/
|
|
366
|
+
rowCount = computed(() => this._rowCountInput() ?? this.#bodyRowCount()?.(), /* @ts-ignore */
|
|
367
|
+
...(ngDevMode ? [{ debugName: "rowCount" }] : /* istanbul ignore next */ []));
|
|
355
368
|
/**
|
|
356
369
|
* True total number of columns for `aria-colcount`. Defaults to the rendered
|
|
357
370
|
* column count (the cells of the first data row that has any). Ignored in
|
|
@@ -558,6 +571,9 @@ class ForTable {
|
|
|
558
571
|
registerVirtualWindow(window) {
|
|
559
572
|
this.#virtualWindow.set(window);
|
|
560
573
|
}
|
|
574
|
+
registerBodyRowCount(count) {
|
|
575
|
+
this.#bodyRowCount.set(count);
|
|
576
|
+
}
|
|
561
577
|
setReorderingRow(index) {
|
|
562
578
|
this.#reorderingRow.set(index);
|
|
563
579
|
}
|
|
@@ -774,7 +790,7 @@ class ForTable {
|
|
|
774
790
|
return Math.max(1, this.#rows.items().length);
|
|
775
791
|
}
|
|
776
792
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTable, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
777
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForTable, isStandalone: true, selector: "[forTable]", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, _dirInput: { classPropertyName: "_dirInput", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null },
|
|
793
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForTable, isStandalone: true, selector: "[forTable]", inputs: { mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, _dirInput: { classPropertyName: "_dirInput", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null }, _rowCountInput: { classPropertyName: "_rowCountInput", publicName: "rowCount", isSignal: true, isRequired: false, transformFunction: null }, colCount: { classPropertyName: "colCount", publicName: "colCount", isSignal: true, isRequired: false, transformFunction: null }, selectionMode: { classPropertyName: "selectionMode", publicName: "selectionMode", isSignal: true, isRequired: false, transformFunction: null }, selectionBehavior: { classPropertyName: "selectionBehavior", publicName: "selectionBehavior", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, compareWith: { classPropertyName: "compareWith", publicName: "compareWith", isSignal: true, isRequired: false, transformFunction: null }, selectableValues: { classPropertyName: "selectableValues", publicName: "selectableValues", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", expanded: "expandedChange" }, host: { properties: { "attr.role": "mode()", "attr.aria-label": "ariaLabel() || null", "attr.dir": "dir()", "attr.data-mode": "mode()", "style.--for-table-header-height.px": "headerSize()?.height ?? null", "attr.aria-rowcount": "rowCountAttr()", "attr.aria-colcount": "colCountAttr()", "attr.aria-multiselectable": "selectionMode() === \"multiple\" ? \"true\" : null" } }, providers: [{ provide: FOR_TABLE_CONTEXT, useExisting: ForTable }], exportAs: ["forTable"], ngImport: i0 });
|
|
778
794
|
}
|
|
779
795
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTable, decorators: [{
|
|
780
796
|
type: Directive,
|
|
@@ -793,7 +809,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
793
809
|
},
|
|
794
810
|
providers: [{ provide: FOR_TABLE_CONTEXT, useExisting: ForTable }],
|
|
795
811
|
}]
|
|
796
|
-
}], ctorParameters: () => [], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], _dirInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }],
|
|
812
|
+
}], ctorParameters: () => [], propDecorators: { mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], _dirInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }], _rowCountInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowCount", required: false }] }], colCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "colCount", required: false }] }], selectionMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionMode", required: false }] }], selectionBehavior: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectionBehavior", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], compareWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "compareWith", required: false }] }], selectableValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectableValues", required: false }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }, { type: i0.Output, args: ["expandedChange"] }] } });
|
|
797
813
|
/**
|
|
798
814
|
* Resolves the absolute `(row, 0-based column)` target and travel `direction`
|
|
799
815
|
* for a row-crossing grid action against the true `total` row count. Arrow
|
|
@@ -851,6 +867,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
851
867
|
* Bind `[forDataCellRow]` to the same array passed to `ForTableBody`'s `rows`
|
|
852
868
|
* to type `let-row` — the input is read only for type inference, never at
|
|
853
869
|
* runtime.
|
|
870
|
+
*
|
|
871
|
+
* When the row type is a discriminated union whose variant members render
|
|
872
|
+
* through a `[forRowDef]` instead of the per-column cells, bind
|
|
873
|
+
* `[forDataCellUnless]` to the same type guard(s) used on those defs' `[when]`
|
|
874
|
+
* so `let-row` is narrowed to the variant-excluded members (`Exclude<T, V>`).
|
|
854
875
|
*/
|
|
855
876
|
class ForDataCell {
|
|
856
877
|
/** The captured data-cell template, typed with `ForDataCellContext<T>`. */
|
|
@@ -861,17 +882,26 @@ class ForDataCell {
|
|
|
861
882
|
* directive never touches its value.
|
|
862
883
|
*/
|
|
863
884
|
rowType = input([], { ...(ngDevMode ? { debugName: "rowType" } : /* istanbul ignore next */ {}), alias: 'forDataCellRow' });
|
|
885
|
+
/**
|
|
886
|
+
* Type-inference hint: bind the type guard(s) that match the variant rows
|
|
887
|
+
* rendered by `[forRowDef]` (the same predicate used on their `[when]`) so
|
|
888
|
+
* `let-row` is narrowed to `Exclude<T, V>` — the members this per-column
|
|
889
|
+
* template actually receives. Compose several variants into one union guard
|
|
890
|
+
* (`(r): r is A | B => …`). Read only by the compiler; the directive never
|
|
891
|
+
* touches its value. Omitting it leaves `let-row` typed as the full `T`.
|
|
892
|
+
*/
|
|
893
|
+
excludeType = input(null, { ...(ngDevMode ? { debugName: "excludeType" } : /* istanbul ignore next */ {}), alias: 'forDataCellUnless' });
|
|
864
894
|
/** Narrows the template context type for `let-row` under strict template checking. */
|
|
865
895
|
static ngTemplateContextGuard(_directive, _context) {
|
|
866
896
|
return true;
|
|
867
897
|
}
|
|
868
898
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDataCell, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
869
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForDataCell, isStandalone: true, selector: "ng-template[forDataCell]", inputs: { rowType: { classPropertyName: "rowType", publicName: "forDataCellRow", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
899
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForDataCell, isStandalone: true, selector: "ng-template[forDataCell]", inputs: { rowType: { classPropertyName: "rowType", publicName: "forDataCellRow", isSignal: true, isRequired: false, transformFunction: null }, excludeType: { classPropertyName: "excludeType", publicName: "forDataCellUnless", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
870
900
|
}
|
|
871
901
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForDataCell, decorators: [{
|
|
872
902
|
type: Directive,
|
|
873
903
|
args: [{ selector: 'ng-template[forDataCell]' }]
|
|
874
|
-
}], propDecorators: { rowType: [{ type: i0.Input, args: [{ isSignal: true, alias: "forDataCellRow", required: false }] }] } });
|
|
904
|
+
}], propDecorators: { rowType: [{ type: i0.Input, args: [{ isSignal: true, alias: "forDataCellRow", required: false }] }], excludeType: [{ type: i0.Input, args: [{ isSignal: true, alias: "forDataCellUnless", required: false }] }] } });
|
|
875
905
|
/**
|
|
876
906
|
* Marks the placeholder/skeleton template of a column definition. Optional;
|
|
877
907
|
* place on an `<ng-template forPlaceholderCell>` inside a `[forColumnDef]`. When
|
|
@@ -889,6 +919,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
889
919
|
type: Directive,
|
|
890
920
|
args: [{ selector: 'ng-template[forPlaceholderCell]' }]
|
|
891
921
|
}] });
|
|
922
|
+
/**
|
|
923
|
+
* Marks the shared drag placeholder for the reorderable columns of a
|
|
924
|
+
* `<for-table-body>`. Optional and declared **once per body** (not per column);
|
|
925
|
+
* place on an `<ng-template forColumnDragPlaceholder>` among the `[forColumnDef]`s.
|
|
926
|
+
* `ForTableBody` stamps it as every reorderable header cell's
|
|
927
|
+
* `[forDragPlaceholder]`, so during a pointer reorder the dragged column's slot
|
|
928
|
+
* shows this template. Omit it to keep drag-drop's default placeholder behaviour.
|
|
929
|
+
*/
|
|
930
|
+
class ForColumnDragPlaceholder {
|
|
931
|
+
/** The captured placeholder template rendered in a reordered column's slot. */
|
|
932
|
+
template = inject(TemplateRef);
|
|
933
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForColumnDragPlaceholder, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
934
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForColumnDragPlaceholder, isStandalone: true, selector: "ng-template[forColumnDragPlaceholder]", ngImport: i0 });
|
|
935
|
+
}
|
|
936
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForColumnDragPlaceholder, decorators: [{
|
|
937
|
+
type: Directive,
|
|
938
|
+
args: [{ selector: 'ng-template[forColumnDragPlaceholder]' }]
|
|
939
|
+
}] });
|
|
892
940
|
/**
|
|
893
941
|
* Declarative definition of a single table column, co-locating its header,
|
|
894
942
|
* data, and (optional) placeholder templates plus its per-column config in one
|
|
@@ -921,11 +969,22 @@ class ForColumnDef {
|
|
|
921
969
|
*/
|
|
922
970
|
sortable = input(false, { ...(ngDevMode ? { debugName: "sortable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
923
971
|
/**
|
|
924
|
-
* When set, `ForTableBody` renders a `[forTableColumnResizer]`
|
|
925
|
-
*
|
|
926
|
-
*
|
|
972
|
+
* When set, `ForTableBody` renders a `[forTableColumnResizer]` inside the column's
|
|
973
|
+
* header cell and re-emits its commits through the body's `resizeCommit` output.
|
|
974
|
+
* Provide `resizeAriaLabel` so the handle is named. Tune the handle per column with
|
|
975
|
+
* `resizeMin` / `resizeMax` / `resizeStep` / `autoFit` / `fitIncludesHeader`, and
|
|
976
|
+
* seed / track its width through the body's `[(columnWidths)]`.
|
|
927
977
|
*/
|
|
928
978
|
resizable = input(false, { ...(ngDevMode ? { debugName: "resizable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
979
|
+
/**
|
|
980
|
+
* When set, the column's header cell becomes a drag-reorder handle: with at least
|
|
981
|
+
* one `reorderable` column, `ForTableBody` applies `[forTableColumnReorder]` to the
|
|
982
|
+
* stamped header row and `[forDraggable]` (with `[dragData]` set to this column's
|
|
983
|
+
* `name`) to this header cell, and re-emits committed reorders through the body's
|
|
984
|
+
* `columnReorder` output. Non-reorderable columns stay static (not draggable). Note
|
|
985
|
+
* that any `reorderable` column makes the body bundle `forty-cdk/drag-drop`.
|
|
986
|
+
*/
|
|
987
|
+
reorderable = input(false, { ...(ngDevMode ? { debugName: "reorderable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
929
988
|
/**
|
|
930
989
|
* Accessible name for the auto-wired resize handle (only meaningful with
|
|
931
990
|
* `resizable`). Supplied by the consumer so it is localizable; `null` (default)
|
|
@@ -933,14 +992,70 @@ class ForColumnDef {
|
|
|
933
992
|
*/
|
|
934
993
|
resizeAriaLabel = input(null, /* @ts-ignore */
|
|
935
994
|
...(ngDevMode ? [{ debugName: "resizeAriaLabel" }] : /* istanbul ignore next */ []));
|
|
995
|
+
/**
|
|
996
|
+
* Minimum width (px) the auto-wired resize handle clamps to (only meaningful with
|
|
997
|
+
* `resizable`). Forwarded to the stamped `[forTableColumnResizer]`'s `min`; drives
|
|
998
|
+
* its `aria-valuemin`. Default `0`.
|
|
999
|
+
*/
|
|
1000
|
+
resizeMin = input(0, /* @ts-ignore */
|
|
1001
|
+
...(ngDevMode ? [{ debugName: "resizeMin" }] : /* istanbul ignore next */ []));
|
|
1002
|
+
/**
|
|
1003
|
+
* Maximum width (px) the auto-wired resize handle clamps to (only meaningful with
|
|
1004
|
+
* `resizable`). Forwarded to the stamped `[forTableColumnResizer]`'s `max`; drives
|
|
1005
|
+
* its `aria-valuemax` (omitted when non-finite). Default `Infinity` (no upper bound).
|
|
1006
|
+
*/
|
|
1007
|
+
resizeMax = input(Infinity, /* @ts-ignore */
|
|
1008
|
+
...(ngDevMode ? [{ debugName: "resizeMax" }] : /* istanbul ignore next */ []));
|
|
1009
|
+
/**
|
|
1010
|
+
* Pixels applied per `ArrowLeft` / `ArrowRight` press on the auto-wired resize
|
|
1011
|
+
* handle (only meaningful with `resizable`). Forwarded to the stamped
|
|
1012
|
+
* `[forTableColumnResizer]`'s `step`. Default `10`.
|
|
1013
|
+
*/
|
|
1014
|
+
resizeStep = input(10, /* @ts-ignore */
|
|
1015
|
+
...(ngDevMode ? [{ debugName: "resizeStep" }] : /* istanbul ignore next */ []));
|
|
1016
|
+
/**
|
|
1017
|
+
* Whether double-clicking the auto-wired resize handle fits the column to its
|
|
1018
|
+
* widest content (only meaningful with `resizable`). Forwarded to the stamped
|
|
1019
|
+
* `[forTableColumnResizer]`'s `autoFit`. Default `true` — the historical
|
|
1020
|
+
* hardcoded behaviour; set `false` to make the double-click a no-op.
|
|
1021
|
+
*/
|
|
1022
|
+
autoFit = input(true, { ...(ngDevMode ? { debugName: "autoFit" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1023
|
+
/**
|
|
1024
|
+
* Whether header-inclusive auto-fit also accounts for the column header's label
|
|
1025
|
+
* (only meaningful with `resizable` + `autoFit`). Forwarded to the stamped
|
|
1026
|
+
* `[forTableColumnResizer]`'s `fitIncludesHeader`; isolate the header text with a
|
|
1027
|
+
* `[forTableColumnLabel]` inside the `[forHeaderCell]` template. Default `false`.
|
|
1028
|
+
*/
|
|
1029
|
+
fitIncludesHeader = input(false, { ...(ngDevMode ? { debugName: "fitIncludesHeader" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
936
1030
|
/**
|
|
937
1031
|
* `grid-template-columns` track fragment for this column (e.g. `'160px'`,
|
|
938
1032
|
* `'minmax(160px, 1fr)'`). When unset, `ForTableBody` falls back to the
|
|
939
1033
|
* published `--for-table-col-<name>-width` resize var with a `minmax(0, 1fr)`
|
|
940
|
-
* default, so a resized column drives its own track.
|
|
1034
|
+
* default, so a resized column drives its own track. A static `width`
|
|
1035
|
+
* **takes precedence** over that resize var — so leave it unset on a
|
|
1036
|
+
* `resizable` column whose width you drive through `resizeCommit` or the
|
|
1037
|
+
* body's `[(columnWidths)]`, otherwise the pinned track ignores the resized
|
|
1038
|
+
* width (the handle still reports `aria-valuenow` but the column won't move).
|
|
941
1039
|
*/
|
|
942
1040
|
width = input(null, /* @ts-ignore */
|
|
943
1041
|
...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
1042
|
+
/**
|
|
1043
|
+
* Static class(es) applied to this column's stamped `[forTableHeaderCell]`.
|
|
1044
|
+
* `ForTableBody` owns the header cell element, so this is the styling seam a
|
|
1045
|
+
* consumer (or wrapping design system) uses to reach it without scoping CSS to
|
|
1046
|
+
* the body's template internals. `null` (default) adds no class attribute.
|
|
1047
|
+
*/
|
|
1048
|
+
headerClass = input(null, /* @ts-ignore */
|
|
1049
|
+
...(ngDevMode ? [{ debugName: "headerClass" }] : /* istanbul ignore next */ []));
|
|
1050
|
+
/**
|
|
1051
|
+
* Static class(es) applied to this column's stamped `[forTableCell]` on every
|
|
1052
|
+
* data **and** placeholder row. The styling seam for the cell box itself
|
|
1053
|
+
* (padding, truncation, alignment, sticky backgrounds) that `ForTableBody`
|
|
1054
|
+
* owns. Per-datum row styling is out of scope. `null` (default) adds no class
|
|
1055
|
+
* attribute.
|
|
1056
|
+
*/
|
|
1057
|
+
cellClass = input(null, /* @ts-ignore */
|
|
1058
|
+
...(ngDevMode ? [{ debugName: "cellClass" }] : /* istanbul ignore next */ []));
|
|
944
1059
|
/** The column's header-cell template. */
|
|
945
1060
|
header = contentChild.required(ForHeaderCell);
|
|
946
1061
|
/** The column's data-cell template. */
|
|
@@ -949,12 +1064,12 @@ class ForColumnDef {
|
|
|
949
1064
|
placeholderCell = contentChild(ForPlaceholderCell, /* @ts-ignore */
|
|
950
1065
|
...(ngDevMode ? [{ debugName: "placeholderCell" }] : /* istanbul ignore next */ []));
|
|
951
1066
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForColumnDef, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
952
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.2", type: ForColumnDef, isStandalone: true, selector: "[forColumnDef]", inputs: { name: { classPropertyName: "name", publicName: "forColumnDef", isSignal: true, isRequired: true, transformFunction: null }, sticky: { classPropertyName: "sticky", publicName: "sticky", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, resizeAriaLabel: { classPropertyName: "resizeAriaLabel", publicName: "resizeAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "header", first: true, predicate: ForHeaderCell, descendants: true, isSignal: true }, { propertyName: "dataCell", first: true, predicate: ForDataCell, descendants: true, isSignal: true }, { propertyName: "placeholderCell", first: true, predicate: ForPlaceholderCell, descendants: true, isSignal: true }], ngImport: i0 });
|
|
1067
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.2", type: ForColumnDef, isStandalone: true, selector: "[forColumnDef]", inputs: { name: { classPropertyName: "name", publicName: "forColumnDef", isSignal: true, isRequired: true, transformFunction: null }, sticky: { classPropertyName: "sticky", publicName: "sticky", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null }, resizeAriaLabel: { classPropertyName: "resizeAriaLabel", publicName: "resizeAriaLabel", isSignal: true, isRequired: false, transformFunction: null }, resizeMin: { classPropertyName: "resizeMin", publicName: "resizeMin", isSignal: true, isRequired: false, transformFunction: null }, resizeMax: { classPropertyName: "resizeMax", publicName: "resizeMax", isSignal: true, isRequired: false, transformFunction: null }, resizeStep: { classPropertyName: "resizeStep", publicName: "resizeStep", isSignal: true, isRequired: false, transformFunction: null }, autoFit: { classPropertyName: "autoFit", publicName: "autoFit", isSignal: true, isRequired: false, transformFunction: null }, fitIncludesHeader: { classPropertyName: "fitIncludesHeader", publicName: "fitIncludesHeader", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, headerClass: { classPropertyName: "headerClass", publicName: "headerClass", isSignal: true, isRequired: false, transformFunction: null }, cellClass: { classPropertyName: "cellClass", publicName: "cellClass", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "header", first: true, predicate: ForHeaderCell, descendants: true, isSignal: true }, { propertyName: "dataCell", first: true, predicate: ForDataCell, descendants: true, isSignal: true }, { propertyName: "placeholderCell", first: true, predicate: ForPlaceholderCell, descendants: true, isSignal: true }], ngImport: i0 });
|
|
953
1068
|
}
|
|
954
1069
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForColumnDef, decorators: [{
|
|
955
1070
|
type: Directive,
|
|
956
1071
|
args: [{ selector: '[forColumnDef]' }]
|
|
957
|
-
}], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "forColumnDef", required: true }] }], sticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "sticky", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], resizeAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizeAriaLabel", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], header: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ForHeaderCell), { isSignal: true }] }], dataCell: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ForDataCell), { isSignal: true }] }], placeholderCell: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ForPlaceholderCell), { isSignal: true }] }] } });
|
|
1072
|
+
}], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "forColumnDef", required: true }] }], sticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "sticky", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], reorderable: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorderable", required: false }] }], resizeAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizeAriaLabel", required: false }] }], resizeMin: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizeMin", required: false }] }], resizeMax: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizeMax", required: false }] }], resizeStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizeStep", required: false }] }], autoFit: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoFit", required: false }] }], fitIncludesHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "fitIncludesHeader", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], headerClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerClass", required: false }] }], cellClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellClass", required: false }] }], header: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ForHeaderCell), { isSignal: true }] }], dataCell: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ForDataCell), { isSignal: true }] }], placeholderCell: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ForPlaceholderCell), { isSignal: true }] }] } });
|
|
958
1073
|
|
|
959
1074
|
/**
|
|
960
1075
|
* Marks the content template of a full-span row variant. Place on an
|
|
@@ -970,6 +1085,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
970
1085
|
*
|
|
971
1086
|
* Bind `[forRowCellRow]` to the same array passed to `ForTableBody`'s `rows` to
|
|
972
1087
|
* type `let-row` — the input is read only for type inference, never at runtime.
|
|
1088
|
+
*
|
|
1089
|
+
* When the row type is a discriminated union, also bind `[forRowCellWhen]` to
|
|
1090
|
+
* the same type guard used on the def's `[when]` so `let-row` is narrowed to the
|
|
1091
|
+
* matched variant member (`V`) instead of staying the full union.
|
|
973
1092
|
*/
|
|
974
1093
|
class ForRowCell {
|
|
975
1094
|
/** The captured row-variant template, typed with `ForDataCellContext<T>`. */
|
|
@@ -980,31 +1099,48 @@ class ForRowCell {
|
|
|
980
1099
|
* directive never touches its value.
|
|
981
1100
|
*/
|
|
982
1101
|
rowType = input([], { ...(ngDevMode ? { debugName: "rowType" } : /* istanbul ignore next */ {}), alias: 'forRowCellRow' });
|
|
1102
|
+
/**
|
|
1103
|
+
* Type-inference hint: bind the same type guard used on this def's `[when]`
|
|
1104
|
+
* so `let-row` is narrowed to the matched variant member (`V`). Read only by
|
|
1105
|
+
* the compiler; the directive never touches its value. Omitting it leaves
|
|
1106
|
+
* `let-row` typed as the full `T`.
|
|
1107
|
+
*/
|
|
1108
|
+
narrowType = input(null, { ...(ngDevMode ? { debugName: "narrowType" } : /* istanbul ignore next */ {}), alias: 'forRowCellWhen' });
|
|
983
1109
|
/** Narrows the template context type for `let-row` under strict template checking. */
|
|
984
1110
|
static ngTemplateContextGuard(_directive, _context) {
|
|
985
1111
|
return true;
|
|
986
1112
|
}
|
|
987
1113
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForRowCell, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
988
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForRowCell, isStandalone: true, selector: "ng-template[forRowCell]", inputs: { rowType: { classPropertyName: "rowType", publicName: "forRowCellRow", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
1114
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForRowCell, isStandalone: true, selector: "ng-template[forRowCell]", inputs: { rowType: { classPropertyName: "rowType", publicName: "forRowCellRow", isSignal: true, isRequired: false, transformFunction: null }, narrowType: { classPropertyName: "narrowType", publicName: "forRowCellWhen", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
989
1115
|
}
|
|
990
1116
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForRowCell, decorators: [{
|
|
991
1117
|
type: Directive,
|
|
992
1118
|
args: [{ selector: 'ng-template[forRowCell]' }]
|
|
993
|
-
}], propDecorators: { rowType: [{ type: i0.Input, args: [{ isSignal: true, alias: "forRowCellRow", required: false }] }] } });
|
|
1119
|
+
}], propDecorators: { rowType: [{ type: i0.Input, args: [{ isSignal: true, alias: "forRowCellRow", required: false }] }], narrowType: [{ type: i0.Input, args: [{ isSignal: true, alias: "forRowCellWhen", required: false }] }] } });
|
|
994
1120
|
/**
|
|
995
|
-
* Declarative definition of a
|
|
1121
|
+
* Declarative definition of a row variant for `<for-table-body>`. Place
|
|
996
1122
|
* `[forRowDef]` on an `<ng-container>` alongside the `[forColumnDef]`s and bind a
|
|
997
|
-
* `[when]` predicate; for every datum the predicate matches, `ForTableBody`
|
|
998
|
-
*
|
|
999
|
-
*
|
|
1000
|
-
* separators, and full-width summary / empty-state rows.
|
|
1123
|
+
* `[when]` predicate; for every datum the predicate matches, `ForTableBody` renders
|
|
1124
|
+
* this variant instead of the per-column data cells. A def comes in one of two
|
|
1125
|
+
* shapes, and must declare **exactly one** of them:
|
|
1001
1126
|
*
|
|
1002
|
-
*
|
|
1003
|
-
*
|
|
1004
|
-
*
|
|
1005
|
-
*
|
|
1006
|
-
*
|
|
1007
|
-
*
|
|
1127
|
+
* - **Full-span** (a `[forRowCell]` template): the matched row's single cell spans
|
|
1128
|
+
* all columns and renders the template — group-header rows, section separators,
|
|
1129
|
+
* full-width summary / empty-state rows. The spanning cell carries the row's
|
|
1130
|
+
* `role` (`gridcell` in grid / treegrid mode) plus `aria-colindex="1"` and
|
|
1131
|
+
* `aria-colspan` equal to the column count, but it does **not** join the roving 2D
|
|
1132
|
+
* navigation grid (it registers no cell handle), so arrow-key navigation moves
|
|
1133
|
+
* between the regular data cells and steps over the variant row.
|
|
1134
|
+
* - **Placeholder cells** (the `placeholderCells` flag, no `[forRowCell]`): the
|
|
1135
|
+
* matched row stamps one cell per displayed column from each column's
|
|
1136
|
+
* `[forPlaceholderCell]` template — interleaved / trailing skeleton rows for
|
|
1137
|
+
* infinite-scroll or paginated tables. Unlike the full-span shape these keep the
|
|
1138
|
+
* roving grid **rectangular** (one cell per column), and the cells are stamped
|
|
1139
|
+
* disabled so grid-mode arrow navigation steps over them.
|
|
1140
|
+
*
|
|
1141
|
+
* Either way the variant row is **presentational** and **non-selectable** (its
|
|
1142
|
+
* `value` stays `undefined`), while still occupying a row slot and counting towards
|
|
1143
|
+
* `aria-rowindex` / `aria-rowcount` (reading order is preserved).
|
|
1008
1144
|
*
|
|
1009
1145
|
* When several `[forRowDef]`s match a datum, the first in DOM order wins. A datum
|
|
1010
1146
|
* matched by no `[forRowDef]` renders the standard per-column row.
|
|
@@ -1015,11 +1151,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
1015
1151
|
* <ng-container forColumnDef="name">
|
|
1016
1152
|
* <ng-template forHeaderCell>Name</ng-template>
|
|
1017
1153
|
* <ng-template forDataCell [forDataCellRow]="rows()" let-row>{{ row.name }}</ng-template>
|
|
1154
|
+
* <ng-template forPlaceholderCell><span class="skeleton"></span></ng-template>
|
|
1018
1155
|
* </ng-container>
|
|
1019
1156
|
*
|
|
1157
|
+
* <!-- full-span group header -->
|
|
1020
1158
|
* <ng-container forRowDef [when]="isGroupHeader">
|
|
1021
1159
|
* <ng-template forRowCell [forRowCellRow]="rows()" let-row>{{ row.group }}</ng-template>
|
|
1022
1160
|
* </ng-container>
|
|
1161
|
+
*
|
|
1162
|
+
* <!-- per-column skeleton rows while the next page loads -->
|
|
1163
|
+
* <ng-container forRowDef [when]="isPlaceholder" placeholderCells />
|
|
1023
1164
|
* </for-table-body>
|
|
1024
1165
|
* ```
|
|
1025
1166
|
*/
|
|
@@ -1034,15 +1175,58 @@ class ForRowDef {
|
|
|
1034
1175
|
*/
|
|
1035
1176
|
when = input.required(/* @ts-ignore */
|
|
1036
1177
|
...(ngDevMode ? [{ debugName: "when" }] : /* istanbul ignore next */ []));
|
|
1037
|
-
/**
|
|
1038
|
-
|
|
1178
|
+
/**
|
|
1179
|
+
* The variant's full-span content template. Present for a full-span def; absent
|
|
1180
|
+
* (and unused) when `placeholderCells` is set. A def must declare exactly one of
|
|
1181
|
+
* a `[forRowCell]` template or `placeholderCells` — the body validates this and
|
|
1182
|
+
* throws a `[forty-cdk/table]` error otherwise.
|
|
1183
|
+
*/
|
|
1184
|
+
cell = contentChild(ForRowCell, /* @ts-ignore */
|
|
1185
|
+
...(ngDevMode ? [{ debugName: "cell" }] : /* istanbul ignore next */ []));
|
|
1186
|
+
/**
|
|
1187
|
+
* Render this variant's matched rows as **per-column placeholder cells** instead
|
|
1188
|
+
* of a full-span `[forRowCell]`. Set it (the bare `placeholderCells` attribute)
|
|
1189
|
+
* for interleaved / trailing skeleton rows — infinite-scroll or paginated tables
|
|
1190
|
+
* that keep their loaded rows and append placeholder rows while the next page
|
|
1191
|
+
* loads. The body stamps one `[forTableCell]` per displayed column from that
|
|
1192
|
+
* column's `[forPlaceholderCell]` template (an empty cell when the column omits
|
|
1193
|
+
* it), exactly like the `loading` state, but stamps the cells disabled so
|
|
1194
|
+
* grid-mode arrow navigation steps over them and the roving grid stays
|
|
1195
|
+
* rectangular.
|
|
1196
|
+
*
|
|
1197
|
+
* A def must declare **exactly one** of a `[forRowCell]` template or
|
|
1198
|
+
* `placeholderCells`; declaring both or neither throws a `[forty-cdk/table]`
|
|
1199
|
+
* error.
|
|
1200
|
+
*/
|
|
1201
|
+
placeholderCells = input(false, { ...(ngDevMode ? { debugName: "placeholderCells" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1039
1202
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForRowDef, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1040
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.2", type: ForRowDef, isStandalone: true, selector: "[forRowDef]", inputs: { when: { classPropertyName: "when", publicName: "when", isSignal: true, isRequired: true, transformFunction: null } }, queries: [{ propertyName: "cell", first: true, predicate: ForRowCell, descendants: true, isSignal: true }], ngImport: i0 });
|
|
1203
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.2", type: ForRowDef, isStandalone: true, selector: "[forRowDef]", inputs: { when: { classPropertyName: "when", publicName: "when", isSignal: true, isRequired: true, transformFunction: null }, placeholderCells: { classPropertyName: "placeholderCells", publicName: "placeholderCells", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "cell", first: true, predicate: ForRowCell, descendants: true, isSignal: true }], ngImport: i0 });
|
|
1041
1204
|
}
|
|
1042
1205
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForRowDef, decorators: [{
|
|
1043
1206
|
type: Directive,
|
|
1044
1207
|
args: [{ selector: '[forRowDef]' }]
|
|
1045
|
-
}], propDecorators: { when: [{ type: i0.Input, args: [{ isSignal: true, alias: "when", required: true }] }], cell: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ForRowCell), { isSignal: true }] }] } });
|
|
1208
|
+
}], propDecorators: { when: [{ type: i0.Input, args: [{ isSignal: true, alias: "when", required: true }] }], cell: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ForRowCell), { isSignal: true }] }], placeholderCells: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholderCells", required: false }] }] } });
|
|
1209
|
+
|
|
1210
|
+
const INTERACTIVE_DESCENDANT_SELECTOR = 'button, a[href], input, select, textarea, summary, [contenteditable="true"]';
|
|
1211
|
+
/**
|
|
1212
|
+
* Whether `event` originated from an interactive element nested inside the row it
|
|
1213
|
+
* is bound to — a consumer-placed `button`, `a[href]`, `input`, `select`,
|
|
1214
|
+
* `textarea`, `summary`, or `contenteditable` descendant of the row host. Resolves
|
|
1215
|
+
* the event target's closest interactive element and reports `true` only when it is
|
|
1216
|
+
* a strict descendant of `event.currentTarget` (the row), so a plain click on cell
|
|
1217
|
+
* text, the gaps between cells, or the row host itself reports `false`. Shared by
|
|
1218
|
+
* the two row-interaction call sites — `ForTableBody`'s whole-row activation and
|
|
1219
|
+
* `ForTableRow`'s selection — so both skip firing for clicks the inner control owns.
|
|
1220
|
+
*/
|
|
1221
|
+
function eventFromInteractiveDescendant(event) {
|
|
1222
|
+
const target = event.target;
|
|
1223
|
+
const rowEl = event.currentTarget;
|
|
1224
|
+
if (!(target instanceof HTMLElement) || !(rowEl instanceof HTMLElement)) {
|
|
1225
|
+
return false;
|
|
1226
|
+
}
|
|
1227
|
+
const interactive = target.closest(INTERACTIVE_DESCENDANT_SELECTOR);
|
|
1228
|
+
return interactive !== null && interactive !== rowEl && rowEl.contains(interactive);
|
|
1229
|
+
}
|
|
1046
1230
|
|
|
1047
1231
|
/**
|
|
1048
1232
|
* Marks a data cell. The `role` is derived from the root's `mode`: `'cell'` in
|
|
@@ -1130,6 +1314,157 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
1130
1314
|
}]
|
|
1131
1315
|
}], ctorParameters: () => [], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], sticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "sticky", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
|
|
1132
1316
|
|
|
1317
|
+
/**
|
|
1318
|
+
* Opt-in **column reordering** for `ForTable`, composed over the drag-drop primitive.
|
|
1319
|
+
*
|
|
1320
|
+
* Apply on the `[forTableHeaderRow]` element. It wraps `[forDropList]` (via
|
|
1321
|
+
* `hostDirectives`) so the header cells become a reorderable list, then translates
|
|
1322
|
+
* drag-drop's generic drop into the table-friendly `columnReorder` output. Mark each
|
|
1323
|
+
* `[forTableHeaderCell]` as `[forDraggable]` with `[dragData]` set to the column name.
|
|
1324
|
+
* On a committed drop (pointer or keyboard) it emits the previous / new index and the
|
|
1325
|
+
* new column-name order; the consumer applies it to their own column array. **It never
|
|
1326
|
+
* reorders columns itself** (BYO-data).
|
|
1327
|
+
*
|
|
1328
|
+
* The wrapped list defaults to `orientation="horizontal"` (a column reorder is always along
|
|
1329
|
+
* the row axis), so no `orientation` binding is needed. Bind `orientation="vertical"` to
|
|
1330
|
+
* override for the rare case.
|
|
1331
|
+
*
|
|
1332
|
+
* In `mode="grid"` / `mode="treegrid"` the draggable header cells join the table's composite
|
|
1333
|
+
* roving grid as its first row, so a sortable + column-reorderable grid keeps the **single
|
|
1334
|
+
* tab stop** the WAI-ARIA Data Grid pattern calls for: `Tab` enters the grid once, Arrow keys
|
|
1335
|
+
* cross between header and body, and `Space` on a header cell lifts it for keyboard reordering.
|
|
1336
|
+
* It hands its drop-list roving to the grid via `FOR_DROP_LIST_ROVING_DELEGATE` and routes idle
|
|
1337
|
+
* header navigation through the table's grid keyboard handler.
|
|
1338
|
+
*
|
|
1339
|
+
* When a header cell is both sortable (`[forTableSortHeader]`) and reorderable, the two
|
|
1340
|
+
* keyboard activations split along WAI-ARIA lines so a single key never both sorts and lifts:
|
|
1341
|
+
* `Space` lifts the column, `Enter` toggles the sort. The split is enforced via a
|
|
1342
|
+
* `FOR_DRAGGABLE_LIFT_GUARD` that defers `Enter` to the sort header on cells carrying the
|
|
1343
|
+
* `data-sortable` marker — detected by DOM marker, so `forty-cdk/drag-drop` needs no table
|
|
1344
|
+
* import. A reorder-only header (no sort header) still lifts on both `Enter` and `Space`.
|
|
1345
|
+
*
|
|
1346
|
+
* @example
|
|
1347
|
+
* ```html
|
|
1348
|
+
* <div forTableHeaderRow forTableColumnReorder (columnReorder)="columns.set($event.columns)">
|
|
1349
|
+
* @for (col of columns(); track col) {
|
|
1350
|
+
* <div forTableHeaderCell [name]="col" forDraggable [dragData]="col">{{ col }}</div>
|
|
1351
|
+
* }
|
|
1352
|
+
* </div>
|
|
1353
|
+
* ```
|
|
1354
|
+
*/
|
|
1355
|
+
class ForTableColumnReorder {
|
|
1356
|
+
ctx = injectTableContext('ForTableColumnReorder');
|
|
1357
|
+
#list = inject(ForDropList);
|
|
1358
|
+
#host = inject(ElementRef).nativeElement;
|
|
1359
|
+
#isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
1360
|
+
/**
|
|
1361
|
+
* Fires once per committed reorder gesture (pointer drop or keyboard drop) with the
|
|
1362
|
+
* previous / new column index and the full column-name order after the move.
|
|
1363
|
+
*/
|
|
1364
|
+
columnReorder = output();
|
|
1365
|
+
constructor() {
|
|
1366
|
+
const destroyRef = inject(DestroyRef);
|
|
1367
|
+
const sub = this.#list.dragDrop.subscribe((event) => this.#emit(event));
|
|
1368
|
+
destroyRef.onDestroy(() => sub.unsubscribe());
|
|
1369
|
+
if (this.#isBrowser) {
|
|
1370
|
+
const onCaptureKeydown = (event) => this.#onCaptureKeydown(event);
|
|
1371
|
+
this.#host.addEventListener('keydown', onCaptureKeydown, { capture: true });
|
|
1372
|
+
destroyRef.onDestroy(() => this.#host.removeEventListener('keydown', onCaptureKeydown, { capture: true }));
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
/**
|
|
1376
|
+
* When the header row joins the composite grid, intercepts idle (not-lifted) Arrow /
|
|
1377
|
+
* Home / End / Page keys on a draggable header cell in the capture phase and routes them
|
|
1378
|
+
* through the table's grid navigation, then stops propagation so the draggable's own
|
|
1379
|
+
* keydown does not also navigate. Space / Enter (and every key while a keyboard drag is in
|
|
1380
|
+
* progress) fall through untouched, so the draggable still owns the lift / move / drop /
|
|
1381
|
+
* cancel gesture — keeping the header one navigation continuum with the body while
|
|
1382
|
+
* preserving column reordering.
|
|
1383
|
+
*/
|
|
1384
|
+
#onCaptureKeydown(event) {
|
|
1385
|
+
if (!this.ctx.headerParticipatesInRoving()) {
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
const target = event.target;
|
|
1389
|
+
if (!(target instanceof HTMLElement)) {
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
1392
|
+
if (!this.#list.items().some((handle) => handle.host === target) ||
|
|
1393
|
+
this.#list.isLifted(target)) {
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
if (this.ctx.handleHeaderCellKeydown(event, target)) {
|
|
1397
|
+
event.stopPropagation();
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
#emit(event) {
|
|
1401
|
+
const names = this.#list.items().map((handle) => String(handle.data()));
|
|
1402
|
+
const columns = moveItemInArray(names, event.previousIndex, event.currentIndex);
|
|
1403
|
+
this.columnReorder.emit({ from: event.previousIndex, to: event.currentIndex, columns });
|
|
1404
|
+
}
|
|
1405
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableColumnReorder, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1406
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForTableColumnReorder, isStandalone: true, selector: "[forTableColumnReorder]", outputs: { columnReorder: "columnReorder" }, providers: [
|
|
1407
|
+
{ provide: FOR_DROP_LIST_DEFAULT_ORIENTATION, useValue: 'horizontal' },
|
|
1408
|
+
{
|
|
1409
|
+
provide: FOR_DROP_LIST_ROVING_DELEGATE,
|
|
1410
|
+
useFactory: () => {
|
|
1411
|
+
const ctx = injectTableContext('ForTableColumnReorder');
|
|
1412
|
+
return {
|
|
1413
|
+
itemTabindex: (el) => ctx.headerParticipatesInRoving() ? ctx.headerCellTabIndex(el) : null,
|
|
1414
|
+
isItemHighlighted: (el) => ctx.headerParticipatesInRoving() ? ctx.isCellHighlighted(el) : null,
|
|
1415
|
+
};
|
|
1416
|
+
},
|
|
1417
|
+
},
|
|
1418
|
+
{
|
|
1419
|
+
provide: FOR_DRAGGABLE_LIFT_GUARD,
|
|
1420
|
+
useValue: {
|
|
1421
|
+
canLiftOnKey: (event, host) => !(event.key === 'Enter' && hostHasSortActivation(host)),
|
|
1422
|
+
},
|
|
1423
|
+
},
|
|
1424
|
+
], exportAs: ["forTableColumnReorder"], hostDirectives: [{ directive: i1.ForDropList, inputs: ["orientation", "orientation", "dir", "dir", "disabled", "disabled", "autoScroll", "autoScroll", "animateReorder", "animateReorder", "liveSort", "liveSort", "boundary", "boundary", "lockAxis", "lockAxis"] }], ngImport: i0 });
|
|
1425
|
+
}
|
|
1426
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableColumnReorder, decorators: [{
|
|
1427
|
+
type: Directive,
|
|
1428
|
+
args: [{
|
|
1429
|
+
selector: '[forTableColumnReorder]',
|
|
1430
|
+
exportAs: 'forTableColumnReorder',
|
|
1431
|
+
providers: [
|
|
1432
|
+
{ provide: FOR_DROP_LIST_DEFAULT_ORIENTATION, useValue: 'horizontal' },
|
|
1433
|
+
{
|
|
1434
|
+
provide: FOR_DROP_LIST_ROVING_DELEGATE,
|
|
1435
|
+
useFactory: () => {
|
|
1436
|
+
const ctx = injectTableContext('ForTableColumnReorder');
|
|
1437
|
+
return {
|
|
1438
|
+
itemTabindex: (el) => ctx.headerParticipatesInRoving() ? ctx.headerCellTabIndex(el) : null,
|
|
1439
|
+
isItemHighlighted: (el) => ctx.headerParticipatesInRoving() ? ctx.isCellHighlighted(el) : null,
|
|
1440
|
+
};
|
|
1441
|
+
},
|
|
1442
|
+
},
|
|
1443
|
+
{
|
|
1444
|
+
provide: FOR_DRAGGABLE_LIFT_GUARD,
|
|
1445
|
+
useValue: {
|
|
1446
|
+
canLiftOnKey: (event, host) => !(event.key === 'Enter' && hostHasSortActivation(host)),
|
|
1447
|
+
},
|
|
1448
|
+
},
|
|
1449
|
+
],
|
|
1450
|
+
hostDirectives: [
|
|
1451
|
+
{
|
|
1452
|
+
directive: ForDropList,
|
|
1453
|
+
inputs: [
|
|
1454
|
+
'orientation',
|
|
1455
|
+
'dir',
|
|
1456
|
+
'disabled',
|
|
1457
|
+
'autoScroll',
|
|
1458
|
+
'animateReorder',
|
|
1459
|
+
'liveSort',
|
|
1460
|
+
'boundary',
|
|
1461
|
+
'lockAxis',
|
|
1462
|
+
],
|
|
1463
|
+
},
|
|
1464
|
+
],
|
|
1465
|
+
}]
|
|
1466
|
+
}], ctorParameters: () => [], propDecorators: { columnReorder: [{ type: i0.Output, args: ["columnReorder"] }] } });
|
|
1467
|
+
|
|
1133
1468
|
/**
|
|
1134
1469
|
* Marks a header cell (`role="columnheader"`). Requires a `name` input that
|
|
1135
1470
|
* identifies the column — reflected as `data-column` for later phases (sort,
|
|
@@ -1731,7 +2066,9 @@ class ForTableRow {
|
|
|
1731
2066
|
}
|
|
1732
2067
|
onClick(event) {
|
|
1733
2068
|
const v = this.value();
|
|
1734
|
-
if (this.ctx.selectionMode() === 'none' ||
|
|
2069
|
+
if (this.ctx.selectionMode() === 'none' ||
|
|
2070
|
+
v === undefined ||
|
|
2071
|
+
eventFromInteractiveDescendant(event)) {
|
|
1735
2072
|
return;
|
|
1736
2073
|
}
|
|
1737
2074
|
this.ctx.selectRow(v, {
|
|
@@ -1764,6 +2101,47 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
1764
2101
|
}]
|
|
1765
2102
|
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], level: [{ type: i0.Input, args: [{ isSignal: true, alias: "level", required: false }] }], expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], virtualIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualIndex", required: false }] }] } });
|
|
1766
2103
|
|
|
2104
|
+
/**
|
|
2105
|
+
* Applies a per-row attribute map to a `<for-table-body>` stamped row. The body
|
|
2106
|
+
* places it on every stamped row so its `rowAttrs` hook can set or remove
|
|
2107
|
+
* arbitrary attributes derived from the row datum, without the body owning a
|
|
2108
|
+
* static list of attribute names. A key mapped to `null` (or dropped from a
|
|
2109
|
+
* later map) removes that attribute. Internal to `forty-cdk/table`.
|
|
2110
|
+
*/
|
|
2111
|
+
class ForTableRowAttrs {
|
|
2112
|
+
#host = inject(ElementRef).nativeElement;
|
|
2113
|
+
/** The attribute map to reflect on the row host, or `undefined` for none. */
|
|
2114
|
+
attrs = input(undefined, { ...(ngDevMode ? { debugName: "attrs" } : /* istanbul ignore next */ {}), alias: 'forTableRowAttrs' });
|
|
2115
|
+
constructor() {
|
|
2116
|
+
let applied = [];
|
|
2117
|
+
effect(() => {
|
|
2118
|
+
const next = this.attrs() ?? {};
|
|
2119
|
+
for (const key of applied) {
|
|
2120
|
+
if (!(key in next)) {
|
|
2121
|
+
this.#host.removeAttribute(key);
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
const keys = [];
|
|
2125
|
+
for (const [key, value] of Object.entries(next)) {
|
|
2126
|
+
if (value == null) {
|
|
2127
|
+
this.#host.removeAttribute(key);
|
|
2128
|
+
}
|
|
2129
|
+
else {
|
|
2130
|
+
this.#host.setAttribute(key, value);
|
|
2131
|
+
}
|
|
2132
|
+
keys.push(key);
|
|
2133
|
+
}
|
|
2134
|
+
applied = keys;
|
|
2135
|
+
});
|
|
2136
|
+
}
|
|
2137
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableRowAttrs, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
2138
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForTableRowAttrs, isStandalone: true, selector: "[forTableRowAttrs]", inputs: { attrs: { classPropertyName: "attrs", publicName: "forTableRowAttrs", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 });
|
|
2139
|
+
}
|
|
2140
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableRowAttrs, decorators: [{
|
|
2141
|
+
type: Directive,
|
|
2142
|
+
args: [{ selector: '[forTableRowAttrs]' }]
|
|
2143
|
+
}], ctorParameters: () => [], propDecorators: { attrs: [{ type: i0.Input, args: [{ isSignal: true, alias: "forTableRowAttrs", required: false }] }] } });
|
|
2144
|
+
|
|
1767
2145
|
/**
|
|
1768
2146
|
* Turns a `[forTableHeaderCell]` into a sortable affordance that emits `aria-sort`
|
|
1769
2147
|
* and fires `sortChange` on activation (click, Enter, Space). The directive is
|
|
@@ -1913,13 +2291,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
1913
2291
|
}], ctorParameters: () => [], propDecorators: { column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }, { type: i0.Output, args: ["directionChange"] }], disableClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableClear", required: false }] }], firstClickDirection: [{ type: i0.Input, args: [{ isSignal: true, alias: "firstClickDirection", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }] } });
|
|
1914
2292
|
|
|
1915
2293
|
/**
|
|
1916
|
-
* Ergonomic declarative renderer for `[forTable]
|
|
2294
|
+
* Ergonomic declarative renderer for the columns of a `[forTable]`.
|
|
1917
2295
|
* Place `<for-table-body>` inside a `[forTable]` element and declare one
|
|
1918
2296
|
* `[forColumnDef]` per column; the body harvests the defs and stamps the header
|
|
1919
2297
|
* row and one data row per item out of the raw cell primitives, so a column is
|
|
1920
2298
|
* authored in a single block instead of being smeared across header, data, and
|
|
1921
2299
|
* placeholder rows.
|
|
1922
2300
|
*
|
|
2301
|
+
* **Supported modes: `table` and `grid`.** Nothing in the body is grid-specific
|
|
2302
|
+
* — it derives each stamped cell's role from the table `mode` (`cell` in
|
|
2303
|
+
* `table`, `gridcell` in `grid`) and applies no mode guard. Choose `mode="grid"`
|
|
2304
|
+
* for interactive cells (roving 2D navigation, cell widgets, cell-entry); choose
|
|
2305
|
+
* the default `mode="table"` for read-only or whole-row navigation lists, where
|
|
2306
|
+
* `role="grid"` would announce an interaction model the list does not have. The
|
|
2307
|
+
* table-mode row-interaction companion feature (whole-row activate / context
|
|
2308
|
+
* menu) is tracked in
|
|
2309
|
+
* [#1349](https://github.com/tutkli/forty-cdk/issues/1349). `mode="treegrid"` is
|
|
2310
|
+
* out of scope — the body stamps no expansion affordances.
|
|
2311
|
+
*
|
|
1923
2312
|
* It owns only the grid **structure** — it applies `display: grid` and the
|
|
1924
2313
|
* derived `grid-template-columns` track (from each def's `width` and the
|
|
1925
2314
|
* published `--for-table-col-<name>-width` resize var) to the header row and
|
|
@@ -1931,33 +2320,72 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
1931
2320
|
* raw `[forTableCell]` / `[forTableHeaderCell]` primitives directly.
|
|
1932
2321
|
*
|
|
1933
2322
|
* Sort and resize affordances are auto-wired from the per-column `sortable` /
|
|
1934
|
-
* `resizable` flags
|
|
1935
|
-
*
|
|
1936
|
-
*
|
|
1937
|
-
*
|
|
2323
|
+
* `resizable` flags. Sort stays consumer-applied (BYO-data): the body derives each
|
|
2324
|
+
* header's `aria-sort` from `sort` and re-emits activation through `sortChange`.
|
|
2325
|
+
* Resize width state can be owned by the body instead — `[(columnWidths)]` seeds
|
|
2326
|
+
* each `resizable` handle's width (keyed by column name) and tracks its live
|
|
2327
|
+
* changes, so the resized track drives itself with no per-consumer plumbing; each
|
|
2328
|
+
* `resizable` column is tuned per def via `resizeMin` / `resizeMax` / `resizeStep`
|
|
2329
|
+
* / `autoFit` / `fitIncludesHeader`, and gesture-end commits still surface through
|
|
2330
|
+
* `resizeCommit`. Selection stays consumer-placed: drop `[forTableRowSelector]` /
|
|
2331
|
+
* `[forTableSelectAll]` into the cell templates and set `rowKey` so each row
|
|
2332
|
+
* carries a selection identity.
|
|
1938
2333
|
*
|
|
1939
2334
|
* **Virtualization is transparent.** Adding `[forTableVirtualized]` to the same
|
|
1940
2335
|
* `[forTable]` element switches the body to windowed rendering automatically:
|
|
1941
2336
|
* it reads the published window off the table context (no cross-entry import),
|
|
1942
2337
|
* renders only the visible slice indexed into `rows`, sizes its rowgroup to the
|
|
1943
2338
|
* full scroll height, and absolutely positions each row at its offset. The
|
|
1944
|
-
* consumer passes the whole dataset to `rows`
|
|
1945
|
-
* `[
|
|
1946
|
-
*
|
|
1947
|
-
*
|
|
2339
|
+
* consumer passes the whole dataset to `rows` — the body derives the true total
|
|
2340
|
+
* from its length, so `[rowCount]` on `[forTable]` is unnecessary (bind it only
|
|
2341
|
+
* for a server-known total larger than the loaded rows). No `#v` reference,
|
|
2342
|
+
* manual sizer, `@for` window, or `[virtualIndex]` binding. Rows are fixed-size
|
|
2343
|
+
* by default (drive row height in CSS); set `measureRows` for measured /
|
|
2344
|
+
* variable row heights (denser variant rows, group separators) — the body feeds
|
|
2345
|
+
* each rendered row's real height back to the virtualizer so the window stays
|
|
2346
|
+
* aligned after scroll.
|
|
1948
2347
|
*
|
|
1949
2348
|
* **Row variants.** Declare one or more `[forRowDef]` alongside the columns to
|
|
1950
|
-
* render a
|
|
1951
|
-
*
|
|
1952
|
-
*
|
|
1953
|
-
*
|
|
1954
|
-
*
|
|
1955
|
-
*
|
|
1956
|
-
*
|
|
1957
|
-
*
|
|
2349
|
+
* render a variant row for the data they match. For each datum the body picks the
|
|
2350
|
+
* first `[forRowDef]` whose `[when]` predicate returns `true`; unmatched data
|
|
2351
|
+
* renders the standard per-column row. Each def declares one of two shapes: a
|
|
2352
|
+
* `[forRowCell]` template stamps a **full-span** row (group headers, section
|
|
2353
|
+
* separators, summary / empty-state rows) whose single cell spans every column;
|
|
2354
|
+
* the `placeholderCells` flag stamps **per-column placeholder cells** from each
|
|
2355
|
+
* column's `[forPlaceholderCell]` (interleaved / trailing skeleton rows for
|
|
2356
|
+
* infinite scroll). Either way variant rows are presentational and
|
|
2357
|
+
* non-selectable, and still count towards `aria-rowindex` / `aria-rowcount`. A
|
|
2358
|
+
* full-span cell stays out of the roving 2D navigation grid (registers no cell
|
|
2359
|
+
* handle, so arrow keys step over the row); placeholder cells keep the grid
|
|
2360
|
+
* rectangular (one cell per column) but are stamped disabled so grid navigation
|
|
2361
|
+
* steps over them.
|
|
1958
2362
|
*/
|
|
1959
2363
|
class ForTableBody {
|
|
1960
2364
|
#ctx = injectTableContext('ForTableBody');
|
|
2365
|
+
rowEls = viewChildren('rowEl', /* @ts-ignore */
|
|
2366
|
+
...(ngDevMode ? [{ debugName: "rowEls" }] : /* istanbul ignore next */ []));
|
|
2367
|
+
#measuredAt = new WeakMap();
|
|
2368
|
+
constructor() {
|
|
2369
|
+
const bodyRowCount = computed(() => this.rows().length, /* @ts-ignore */
|
|
2370
|
+
...(ngDevMode ? [{ debugName: "bodyRowCount" }] : /* istanbul ignore next */ []));
|
|
2371
|
+
this.#ctx.registerBodyRowCount(bodyRowCount);
|
|
2372
|
+
inject(DestroyRef).onDestroy(() => this.#ctx.registerBodyRowCount(null));
|
|
2373
|
+
afterEveryRender(() => {
|
|
2374
|
+
const window = this.#ctx.virtualWindow();
|
|
2375
|
+
if (!window || !this.measureRows()) {
|
|
2376
|
+
return;
|
|
2377
|
+
}
|
|
2378
|
+
for (const ref of this.rowEls()) {
|
|
2379
|
+
const el = ref.nativeElement;
|
|
2380
|
+
const index = el.getAttribute('data-index');
|
|
2381
|
+
if (index === null || this.#measuredAt.get(el) === index) {
|
|
2382
|
+
continue;
|
|
2383
|
+
}
|
|
2384
|
+
this.#measuredAt.set(el, index);
|
|
2385
|
+
window.measureRow(el);
|
|
2386
|
+
}
|
|
2387
|
+
});
|
|
2388
|
+
}
|
|
1961
2389
|
/** The rows to render — already sorted / filtered / paged by the consumer (BYO-data). */
|
|
1962
2390
|
rows = input.required(/* @ts-ignore */
|
|
1963
2391
|
...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
|
|
@@ -1986,16 +2414,108 @@ class ForTableBody {
|
|
|
1986
2414
|
/** Number of placeholder rows rendered while `loading`. Default `3`. */
|
|
1987
2415
|
placeholderRows = input(3, /* @ts-ignore */
|
|
1988
2416
|
...(ngDevMode ? [{ debugName: "placeholderRows" }] : /* istanbul ignore next */ []));
|
|
2417
|
+
/**
|
|
2418
|
+
* Opt in to **measured (variable) row heights** under `[forTableVirtualized]`.
|
|
2419
|
+
* When set, the body measures each stamped row after render and feeds its real
|
|
2420
|
+
* height back to the virtualizer, which replaces the `estimateRowSize` estimate
|
|
2421
|
+
* and re-aligns the offsets of the rows below — so a window mixing row shapes
|
|
2422
|
+
* (denser variant rows, group separators) stays contiguous after scroll.
|
|
2423
|
+
*
|
|
2424
|
+
* Off by default: a uniform-height table keeps the pure `estimateRowSize` fast
|
|
2425
|
+
* path with no per-row measurement work. Has no effect without
|
|
2426
|
+
* `[forTableVirtualized]` (there is no window to measure against).
|
|
2427
|
+
*/
|
|
2428
|
+
measureRows = input(false, { ...(ngDevMode ? { debugName: "measureRows" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1989
2429
|
/** Fires when a `sortable` header is activated; forwarded from the internal `[forTableSortHeader]`. */
|
|
1990
2430
|
sortChange = output();
|
|
1991
2431
|
/** Fires when a `resizable` column commits a width; forwarded from the internal `[forTableColumnResizer]`. */
|
|
1992
2432
|
resizeCommit = output();
|
|
2433
|
+
/**
|
|
2434
|
+
* Fires once per committed column reorder gesture (pointer drop or keyboard drop),
|
|
2435
|
+
* forwarded unchanged from the internal `[forTableColumnReorder]`. Its `columns`
|
|
2436
|
+
* lists the reorderable columns in their new order (equal to the full displayed
|
|
2437
|
+
* order when every displayed column is `reorderable`); apply it to your own column
|
|
2438
|
+
* order and feed it back through `displayedColumns` (BYO-data). Only present when at
|
|
2439
|
+
* least one `[forColumnDef]` is `reorderable`.
|
|
2440
|
+
*/
|
|
2441
|
+
columnReorder = output();
|
|
2442
|
+
/**
|
|
2443
|
+
* Two-way map of column widths (px), keyed by column `name`. It seeds each
|
|
2444
|
+
* `resizable` column's stamped handle `[width]` — so the `role="separator"`
|
|
2445
|
+
* handle exposes `aria-valuenow` from the first render and the column's grid
|
|
2446
|
+
* track picks up the seeded width immediately — and is updated immutably on
|
|
2447
|
+
* every live width change the handle reports (pointer drag, keyboard resize,
|
|
2448
|
+
* auto-fit). Only `resizable` columns participate; other names are ignored.
|
|
2449
|
+
*
|
|
2450
|
+
* The map is JSON-serializable, so persisting a user's column layout is
|
|
2451
|
+
* `[(columnWidths)]` plus one storage write. Together with `[displayedColumns]`
|
|
2452
|
+
* and `[sort]` it makes the full user-configurable table state three bindings.
|
|
2453
|
+
* Its implicit `columnWidthsChange` fires only on handle-driven updates, not on
|
|
2454
|
+
* consumer writes through `[(columnWidths)]`.
|
|
2455
|
+
*/
|
|
2456
|
+
columnWidths = model({}, /* @ts-ignore */
|
|
2457
|
+
...(ngDevMode ? [{ debugName: "columnWidths" }] : /* istanbul ignore next */ []));
|
|
2458
|
+
/**
|
|
2459
|
+
* Opt-in whole-row interaction for **navigation lists**, active only in the
|
|
2460
|
+
* default `mode="table"`. When set, each data row becomes a focusable tab stop
|
|
2461
|
+
* (`tabindex="0"`) and a pointer click or `Enter` emits `rowActivate`, while a
|
|
2462
|
+
* `contextmenu` (right-click or the context-menu key) emits `rowContextMenu`.
|
|
2463
|
+
* Full-span `[forRowDef]` variant rows stay non-interactive. Ignored in `grid`
|
|
2464
|
+
* / `treegrid` mode, where roving 2D navigation and cell-entry own the keyboard
|
|
2465
|
+
* and whole-row activation would conflict.
|
|
2466
|
+
*
|
|
2467
|
+
* Interactive content inside a data cell owns its own events: a click or
|
|
2468
|
+
* `Enter` originating from a `button`, `a[href]`, `input`, `select`,
|
|
2469
|
+
* `textarea`, `summary`, or `contenteditable` descendant does **not** emit
|
|
2470
|
+
* `rowActivate`, and its native default action is left intact — a trailing
|
|
2471
|
+
* per-row action button keeps working, and `Enter` on it is not
|
|
2472
|
+
* `preventDefault`ed. The row still activates from anywhere else: cell text,
|
|
2473
|
+
* the gaps between cells, or the focused row host itself. `rowContextMenu` is
|
|
2474
|
+
* deliberately unguarded — a right-click anywhere on the row, including over an
|
|
2475
|
+
* inner control, still offers the row's context menu, matching native lists.
|
|
2476
|
+
*/
|
|
2477
|
+
interactiveRows = input(false, { ...(ngDevMode ? { debugName: "interactiveRows" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
2478
|
+
/**
|
|
2479
|
+
* Fires when a data row is activated by a pointer click or `Enter`, carrying
|
|
2480
|
+
* the row datum, its dataset index, and the originating event. Requires
|
|
2481
|
+
* `interactiveRows` and `mode="table"`.
|
|
2482
|
+
*/
|
|
2483
|
+
rowActivate = output();
|
|
2484
|
+
/**
|
|
2485
|
+
* Fires when a data row receives a `contextmenu` event (right-click or the
|
|
2486
|
+
* context-menu key), carrying the row datum, its dataset index, and the event —
|
|
2487
|
+
* position your own overlay from it. Requires `interactiveRows` and
|
|
2488
|
+
* `mode="table"`.
|
|
2489
|
+
*/
|
|
2490
|
+
rowContextMenu = output();
|
|
2491
|
+
/**
|
|
2492
|
+
* Per-row class hook, applied to data **and** variant rows in **every** mode.
|
|
2493
|
+
* Receives the row datum and its 0-based dataset index and returns the class(es)
|
|
2494
|
+
* to apply — a string, a `{ className: boolean }` map, or `undefined` for none.
|
|
2495
|
+
* This is the only seam for styling a body-owned row from its datum (an
|
|
2496
|
+
* "active" / "menu-open" highlight, error or dimmed rows). Evaluated on every
|
|
2497
|
+
* change-detection pass, so keep it cheap and free of side effects.
|
|
2498
|
+
*/
|
|
2499
|
+
rowClass = input(/* @ts-ignore */
|
|
2500
|
+
...(ngDevMode ? [undefined, { debugName: "rowClass" }] : /* istanbul ignore next */ []));
|
|
2501
|
+
/**
|
|
2502
|
+
* Per-row attribute hook, applied to data **and** variant rows in **every**
|
|
2503
|
+
* mode. Receives the row datum and its 0-based dataset index and returns an
|
|
2504
|
+
* attribute map to reflect on the row host; a key mapped to `null` (or dropped
|
|
2505
|
+
* from a later map) removes that attribute. Evaluated on every change-detection
|
|
2506
|
+
* pass, so keep it cheap and free of side effects.
|
|
2507
|
+
*/
|
|
2508
|
+
rowAttrs = input(/* @ts-ignore */
|
|
2509
|
+
...(ngDevMode ? [undefined, { debugName: "rowAttrs" }] : /* istanbul ignore next */ []));
|
|
1993
2510
|
/** Declared column definitions, in DOM order. */
|
|
1994
2511
|
columns = contentChildren(ForColumnDef, /* @ts-ignore */
|
|
1995
2512
|
...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
1996
2513
|
/** Declared full-span row variants, in DOM order (first match wins per datum). */
|
|
1997
2514
|
rowDefs = contentChildren(ForRowDef, /* @ts-ignore */
|
|
1998
2515
|
...(ngDevMode ? [{ debugName: "rowDefs" }] : /* istanbul ignore next */ []));
|
|
2516
|
+
/** The optional shared drag placeholder for reorderable columns, or `undefined`. */
|
|
2517
|
+
columnDragPlaceholder = contentChild(ForColumnDragPlaceholder, /* @ts-ignore */
|
|
2518
|
+
...(ngDevMode ? [{ debugName: "columnDragPlaceholder" }] : /* istanbul ignore next */ []));
|
|
1999
2519
|
/** The role a stamped cell carries: `'cell'` in `table` mode, `'gridcell'` otherwise. */
|
|
2000
2520
|
cellRole = computed(() => this.#ctx.mode() === 'table' ? 'cell' : 'gridcell', /* @ts-ignore */
|
|
2001
2521
|
...(ngDevMode ? [{ debugName: "cellRole" }] : /* istanbul ignore next */ []));
|
|
@@ -2010,6 +2530,13 @@ class ForTableBody {
|
|
|
2010
2530
|
return order.map((name) => byName.get(name)).filter((def) => def != null);
|
|
2011
2531
|
}, /* @ts-ignore */
|
|
2012
2532
|
...(ngDevMode ? [{ debugName: "orderedColumns" }] : /* istanbul ignore next */ []));
|
|
2533
|
+
/**
|
|
2534
|
+
* `true` when at least one displayed column is `reorderable`, switching the stamped
|
|
2535
|
+
* header row to the drag-reorder path (`[forTableColumnReorder]` + per-cell
|
|
2536
|
+
* `[forDraggable]`). A body with no reorderable column keeps the plain header row.
|
|
2537
|
+
*/
|
|
2538
|
+
hasReorderable = computed(() => this.orderedColumns().some((col) => col.reorderable()), /* @ts-ignore */
|
|
2539
|
+
...(ngDevMode ? [{ debugName: "hasReorderable" }] : /* istanbul ignore next */ []));
|
|
2013
2540
|
/**
|
|
2014
2541
|
* The derived `grid-template-columns` track, applied to the header row and every
|
|
2015
2542
|
* data row and exposed for consumers who want to bind it elsewhere. Each column
|
|
@@ -2029,6 +2556,9 @@ class ForTableBody {
|
|
|
2029
2556
|
const data = this.rows();
|
|
2030
2557
|
const key = this.rowKey();
|
|
2031
2558
|
const variants = this.rowDefs();
|
|
2559
|
+
for (const def of variants) {
|
|
2560
|
+
this.#assertRowDefConfig(def);
|
|
2561
|
+
}
|
|
2032
2562
|
const matchVariant = (datum, index) => variants.find((def) => def.when()(datum, index)) ?? null;
|
|
2033
2563
|
if (window) {
|
|
2034
2564
|
const out = [];
|
|
@@ -2066,6 +2596,14 @@ class ForTableBody {
|
|
|
2066
2596
|
});
|
|
2067
2597
|
}, /* @ts-ignore */
|
|
2068
2598
|
...(ngDevMode ? [{ debugName: "renderRows" }] : /* istanbul ignore next */ []));
|
|
2599
|
+
/** Enforces that each `[forRowDef]` declares exactly one of `[forRowCell]` / `placeholderCells`. */
|
|
2600
|
+
#assertRowDefConfig(def) {
|
|
2601
|
+
const hasCell = def.cell() != null;
|
|
2602
|
+
const hasPlaceholder = def.placeholderCells();
|
|
2603
|
+
if (hasCell === hasPlaceholder) {
|
|
2604
|
+
throw new Error(`[forty-cdk/table] A [forRowDef] must declare exactly one of a [forRowCell] template or the placeholderCells flag (this def declares ${hasCell ? 'both' : 'neither'}).`);
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2069
2607
|
/** Full scroll height (px) applied to the rowgroup when virtualized, else `null` (natural height). */
|
|
2070
2608
|
sizerHeight = computed(() => {
|
|
2071
2609
|
const window = this.#ctx.virtualWindow();
|
|
@@ -2079,37 +2617,163 @@ class ForTableBody {
|
|
|
2079
2617
|
const descriptor = this.sort();
|
|
2080
2618
|
return descriptor && descriptor.column === column ? descriptor.direction : 'none';
|
|
2081
2619
|
}
|
|
2620
|
+
/** Whether whole-row interaction is active: opted in via `interactiveRows` and in `table` mode. */
|
|
2621
|
+
rowsInteractive = computed(() => this.interactiveRows() && this.#ctx.mode() === 'table', /* @ts-ignore */
|
|
2622
|
+
...(ngDevMode ? [{ debugName: "rowsInteractive" }] : /* istanbul ignore next */ []));
|
|
2623
|
+
/** The `tabindex` for a stamped row: `0` for an interactive data row, `null` otherwise. */
|
|
2624
|
+
rowTabIndex(row) {
|
|
2625
|
+
return this.rowsInteractive() && !row.variant ? 0 : null;
|
|
2626
|
+
}
|
|
2627
|
+
/** Resolves the `rowClass` hook for a stamped row, or `undefined` when unset. */
|
|
2628
|
+
rowClassFor(row) {
|
|
2629
|
+
return this.rowClass()?.(row.datum, row.index);
|
|
2630
|
+
}
|
|
2631
|
+
/** Resolves the `rowAttrs` hook for a stamped row, or `undefined` when unset. */
|
|
2632
|
+
rowAttrsFor(row) {
|
|
2633
|
+
return this.rowAttrs()?.(row.datum, row.index);
|
|
2634
|
+
}
|
|
2635
|
+
/**
|
|
2636
|
+
* Folds a stamped resizer's live width update into the `[(columnWidths)]` map
|
|
2637
|
+
* immutably, keyed by the column name. Ignores the resizer's initial unset
|
|
2638
|
+
* (`undefined`) emission and no-ops when the width is unchanged, so seeding the
|
|
2639
|
+
* handle from `columnWidths` never loops back into a redundant model write.
|
|
2640
|
+
*/
|
|
2641
|
+
onColumnWidthChange(column, width) {
|
|
2642
|
+
if (width === undefined) {
|
|
2643
|
+
return;
|
|
2644
|
+
}
|
|
2645
|
+
const current = this.columnWidths();
|
|
2646
|
+
if (current[column] === width) {
|
|
2647
|
+
return;
|
|
2648
|
+
}
|
|
2649
|
+
this.columnWidths.set({ ...current, [column]: width });
|
|
2650
|
+
}
|
|
2651
|
+
onRowClick(row, event) {
|
|
2652
|
+
this.#activateRow(row, event);
|
|
2653
|
+
}
|
|
2654
|
+
onRowEnter(row, event) {
|
|
2655
|
+
if (this.#activateRow(row, event)) {
|
|
2656
|
+
event.preventDefault();
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
onRowContextMenu(row, event) {
|
|
2660
|
+
if (!this.rowsInteractive() || row.variant) {
|
|
2661
|
+
return;
|
|
2662
|
+
}
|
|
2663
|
+
this.rowContextMenu.emit({ row: row.datum, index: row.index, event });
|
|
2664
|
+
}
|
|
2665
|
+
#activateRow(row, event) {
|
|
2666
|
+
if (!this.rowsInteractive() || row.variant || eventFromInteractiveDescendant(event)) {
|
|
2667
|
+
return false;
|
|
2668
|
+
}
|
|
2669
|
+
this.rowActivate.emit({ row: row.datum, index: row.index, event });
|
|
2670
|
+
return true;
|
|
2671
|
+
}
|
|
2082
2672
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableBody, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2083
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: ForTableBody, isStandalone: true, selector: "for-table-body", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, rowKey: { classPropertyName: "rowKey", publicName: "rowKey", isSignal: true, isRequired: false, transformFunction: null }, displayedColumns: { classPropertyName: "displayedColumns", publicName: "displayedColumns", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, placeholderRows: { classPropertyName: "placeholderRows", publicName: "placeholderRows", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortChange: "sortChange", resizeCommit: "resizeCommit" }, host: { styleAttribute: "display: contents" }, queries: [{ propertyName: "columns", predicate: ForColumnDef, isSignal: true }, { propertyName: "rowDefs", predicate: ForRowDef, isSignal: true }], ngImport: i0, template: `
|
|
2084
|
-
<
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2673
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.2", type: ForTableBody, isStandalone: true, selector: "for-table-body", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, rowKey: { classPropertyName: "rowKey", publicName: "rowKey", isSignal: true, isRequired: false, transformFunction: null }, displayedColumns: { classPropertyName: "displayedColumns", publicName: "displayedColumns", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, placeholderRows: { classPropertyName: "placeholderRows", publicName: "placeholderRows", isSignal: true, isRequired: false, transformFunction: null }, measureRows: { classPropertyName: "measureRows", publicName: "measureRows", isSignal: true, isRequired: false, transformFunction: null }, columnWidths: { classPropertyName: "columnWidths", publicName: "columnWidths", isSignal: true, isRequired: false, transformFunction: null }, interactiveRows: { classPropertyName: "interactiveRows", publicName: "interactiveRows", isSignal: true, isRequired: false, transformFunction: null }, rowClass: { classPropertyName: "rowClass", publicName: "rowClass", isSignal: true, isRequired: false, transformFunction: null }, rowAttrs: { classPropertyName: "rowAttrs", publicName: "rowAttrs", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortChange: "sortChange", resizeCommit: "resizeCommit", columnReorder: "columnReorder", columnWidths: "columnWidthsChange", rowActivate: "rowActivate", rowContextMenu: "rowContextMenu" }, host: { styleAttribute: "display: contents" }, queries: [{ propertyName: "columns", predicate: ForColumnDef, isSignal: true }, { propertyName: "rowDefs", predicate: ForRowDef, isSignal: true }, { propertyName: "columnDragPlaceholder", first: true, predicate: ForColumnDragPlaceholder, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "rowEls", predicate: ["rowEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
2674
|
+
<ng-template #headerCellContent let-col let-cell="cell">
|
|
2675
|
+
<ng-container
|
|
2676
|
+
[ngTemplateOutlet]="col.header().template"
|
|
2677
|
+
[ngTemplateOutletInjector]="cell.injector"
|
|
2678
|
+
/>
|
|
2679
|
+
@if (col.resizable()) {
|
|
2680
|
+
<button
|
|
2681
|
+
forTableColumnResizer
|
|
2092
2682
|
[column]="col.name()"
|
|
2093
|
-
[
|
|
2094
|
-
[
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2683
|
+
[width]="columnWidths()[col.name()]"
|
|
2684
|
+
[min]="col.resizeMin()"
|
|
2685
|
+
[max]="col.resizeMax()"
|
|
2686
|
+
[step]="col.resizeStep()"
|
|
2687
|
+
[autoFit]="col.autoFit()"
|
|
2688
|
+
[fitIncludesHeader]="col.fitIncludesHeader()"
|
|
2689
|
+
[attr.aria-label]="col.resizeAriaLabel()"
|
|
2690
|
+
(widthChange)="onColumnWidthChange(col.name(), $event)"
|
|
2691
|
+
(resizeCommit)="resizeCommit.emit($event)"
|
|
2692
|
+
></button>
|
|
2693
|
+
}
|
|
2694
|
+
</ng-template>
|
|
2695
|
+
|
|
2696
|
+
@if (hasReorderable()) {
|
|
2697
|
+
<div
|
|
2698
|
+
forTableHeaderRow
|
|
2699
|
+
forTableColumnReorder
|
|
2700
|
+
[style.display]="'grid'"
|
|
2701
|
+
[style.grid-template-columns]="track()"
|
|
2702
|
+
(columnReorder)="columnReorder.emit($event)"
|
|
2703
|
+
>
|
|
2704
|
+
@for (col of orderedColumns(); track col.name()) {
|
|
2705
|
+
@if (col.reorderable()) {
|
|
2706
|
+
<div
|
|
2707
|
+
#headerCell="forTableHeaderCell"
|
|
2708
|
+
forTableHeaderCell
|
|
2709
|
+
[name]="col.name()"
|
|
2710
|
+
[sticky]="col.sticky()"
|
|
2711
|
+
[class]="col.headerClass()"
|
|
2712
|
+
forTableSortHeader
|
|
2713
|
+
[column]="col.name()"
|
|
2714
|
+
[sortable]="col.sortable()"
|
|
2715
|
+
[direction]="directionFor(col.name())"
|
|
2716
|
+
(sortChange)="sortChange.emit($event)"
|
|
2717
|
+
forDraggable
|
|
2718
|
+
[dragData]="col.name()"
|
|
2719
|
+
>
|
|
2720
|
+
<ng-container
|
|
2721
|
+
[ngTemplateOutlet]="headerCellContent"
|
|
2722
|
+
[ngTemplateOutletInjector]="headerCell.injector"
|
|
2723
|
+
[ngTemplateOutletContext]="{ $implicit: col, cell: headerCell }"
|
|
2724
|
+
/>
|
|
2725
|
+
@if (columnDragPlaceholder(); as placeholder) {
|
|
2726
|
+
<ng-template forDragPlaceholder>
|
|
2727
|
+
<ng-container [ngTemplateOutlet]="placeholder.template" />
|
|
2728
|
+
</ng-template>
|
|
2729
|
+
}
|
|
2730
|
+
</div>
|
|
2731
|
+
} @else {
|
|
2732
|
+
<div
|
|
2733
|
+
#headerCell="forTableHeaderCell"
|
|
2734
|
+
forTableHeaderCell
|
|
2735
|
+
[name]="col.name()"
|
|
2736
|
+
[sticky]="col.sticky()"
|
|
2737
|
+
[class]="col.headerClass()"
|
|
2738
|
+
forTableSortHeader
|
|
2105
2739
|
[column]="col.name()"
|
|
2106
|
-
[
|
|
2107
|
-
|
|
2108
|
-
|
|
2740
|
+
[sortable]="col.sortable()"
|
|
2741
|
+
[direction]="directionFor(col.name())"
|
|
2742
|
+
(sortChange)="sortChange.emit($event)"
|
|
2743
|
+
>
|
|
2744
|
+
<ng-container
|
|
2745
|
+
[ngTemplateOutlet]="headerCellContent"
|
|
2746
|
+
[ngTemplateOutletInjector]="headerCell.injector"
|
|
2747
|
+
[ngTemplateOutletContext]="{ $implicit: col, cell: headerCell }"
|
|
2748
|
+
/>
|
|
2749
|
+
</div>
|
|
2109
2750
|
}
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2751
|
+
}
|
|
2752
|
+
</div>
|
|
2753
|
+
} @else {
|
|
2754
|
+
<div forTableHeaderRow [style.display]="'grid'" [style.grid-template-columns]="track()">
|
|
2755
|
+
@for (col of orderedColumns(); track col.name()) {
|
|
2756
|
+
<div
|
|
2757
|
+
#headerCell="forTableHeaderCell"
|
|
2758
|
+
forTableHeaderCell
|
|
2759
|
+
[name]="col.name()"
|
|
2760
|
+
[sticky]="col.sticky()"
|
|
2761
|
+
[class]="col.headerClass()"
|
|
2762
|
+
forTableSortHeader
|
|
2763
|
+
[column]="col.name()"
|
|
2764
|
+
[sortable]="col.sortable()"
|
|
2765
|
+
[direction]="directionFor(col.name())"
|
|
2766
|
+
(sortChange)="sortChange.emit($event)"
|
|
2767
|
+
>
|
|
2768
|
+
<ng-container
|
|
2769
|
+
[ngTemplateOutlet]="headerCellContent"
|
|
2770
|
+
[ngTemplateOutletInjector]="headerCell.injector"
|
|
2771
|
+
[ngTemplateOutletContext]="{ $implicit: col, cell: headerCell }"
|
|
2772
|
+
/>
|
|
2773
|
+
</div>
|
|
2774
|
+
}
|
|
2775
|
+
</div>
|
|
2776
|
+
}
|
|
2113
2777
|
<div
|
|
2114
2778
|
role="rowgroup"
|
|
2115
2779
|
[style.position]="sizerHeight() !== null ? 'relative' : null"
|
|
@@ -2119,7 +2783,13 @@ class ForTableBody {
|
|
|
2119
2783
|
@for (placeholder of placeholderRange(); track placeholder) {
|
|
2120
2784
|
<div forTableRow [style.display]="'grid'" [style.grid-template-columns]="track()">
|
|
2121
2785
|
@for (col of orderedColumns(); track col.name()) {
|
|
2122
|
-
<div
|
|
2786
|
+
<div
|
|
2787
|
+
#cell="forTableCell"
|
|
2788
|
+
forTableCell
|
|
2789
|
+
[name]="col.name()"
|
|
2790
|
+
[sticky]="col.sticky()"
|
|
2791
|
+
[class]="col.cellClass()"
|
|
2792
|
+
>
|
|
2123
2793
|
@if (col.placeholderCell(); as placeholderCell) {
|
|
2124
2794
|
<ng-container
|
|
2125
2795
|
[ngTemplateOutlet]="placeholderCell.template"
|
|
@@ -2134,34 +2804,67 @@ class ForTableBody {
|
|
|
2134
2804
|
@for (r of renderRows(); track r.key) {
|
|
2135
2805
|
<div
|
|
2136
2806
|
#rowRef="forTableRow"
|
|
2807
|
+
#rowEl
|
|
2137
2808
|
forTableRow
|
|
2138
2809
|
[value]="r.value"
|
|
2139
2810
|
[virtualIndex]="r.virtualIndex"
|
|
2140
2811
|
[attr.data-index]="r.virtualIndex"
|
|
2812
|
+
[attr.tabindex]="rowTabIndex(r)"
|
|
2813
|
+
[class]="rowClassFor(r)"
|
|
2814
|
+
[forTableRowAttrs]="rowAttrsFor(r)"
|
|
2141
2815
|
[style.display]="'grid'"
|
|
2142
2816
|
[style.grid-template-columns]="track()"
|
|
2143
2817
|
[style.position]="r.start !== null ? 'absolute' : null"
|
|
2144
2818
|
[style.left]="r.start !== null ? '0' : null"
|
|
2145
2819
|
[style.right]="r.start !== null ? '0' : null"
|
|
2146
2820
|
[style.transform]="r.start !== null ? 'translateY(' + r.start + 'px)' : null"
|
|
2821
|
+
(click)="onRowClick(r, $event)"
|
|
2822
|
+
(keydown.enter)="onRowEnter(r, $event)"
|
|
2823
|
+
(contextmenu)="onRowContextMenu(r, $event)"
|
|
2147
2824
|
>
|
|
2148
2825
|
@if (r.variant; as variant) {
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2826
|
+
@if (variant.placeholderCells()) {
|
|
2827
|
+
@for (col of orderedColumns(); track col.name()) {
|
|
2828
|
+
<div
|
|
2829
|
+
#cell="forTableCell"
|
|
2830
|
+
forTableCell
|
|
2831
|
+
disabled
|
|
2832
|
+
[name]="col.name()"
|
|
2833
|
+
[sticky]="col.sticky()"
|
|
2834
|
+
[class]="col.cellClass()"
|
|
2835
|
+
>
|
|
2836
|
+
@if (col.placeholderCell(); as placeholderCell) {
|
|
2837
|
+
<ng-container
|
|
2838
|
+
[ngTemplateOutlet]="placeholderCell.template"
|
|
2839
|
+
[ngTemplateOutletInjector]="cell.injector"
|
|
2840
|
+
/>
|
|
2841
|
+
}
|
|
2842
|
+
</div>
|
|
2843
|
+
}
|
|
2844
|
+
} @else {
|
|
2845
|
+
<div
|
|
2846
|
+
[attr.role]="cellRole()"
|
|
2847
|
+
[attr.aria-colindex]="1"
|
|
2848
|
+
[attr.aria-colspan]="orderedColumns().length"
|
|
2849
|
+
[attr.data-row-variant]="''"
|
|
2850
|
+
[style.grid-column]="'1 / -1'"
|
|
2851
|
+
>
|
|
2852
|
+
<ng-container
|
|
2853
|
+
[ngTemplateOutlet]="variant.cell()?.template ?? null"
|
|
2854
|
+
[ngTemplateOutletInjector]="rowRef.injector"
|
|
2855
|
+
[ngTemplateOutletContext]="{ $implicit: r.datum, index: r.index }"
|
|
2856
|
+
/>
|
|
2857
|
+
</div>
|
|
2858
|
+
}
|
|
2162
2859
|
} @else {
|
|
2163
2860
|
@for (col of orderedColumns(); track col.name()) {
|
|
2164
|
-
<div
|
|
2861
|
+
<div
|
|
2862
|
+
#cell="forTableCell"
|
|
2863
|
+
forTableCell
|
|
2864
|
+
[name]="col.name()"
|
|
2865
|
+
[sticky]="col.sticky()"
|
|
2866
|
+
[class]="col.cellClass()"
|
|
2867
|
+
>
|
|
2165
2868
|
<ng-container
|
|
2166
2869
|
[ngTemplateOutlet]="col.dataCell().template"
|
|
2167
2870
|
[ngTemplateOutletInjector]="cell.injector"
|
|
@@ -2175,7 +2878,7 @@ class ForTableBody {
|
|
|
2175
2878
|
}
|
|
2176
2879
|
</div>
|
|
2177
2880
|
<ng-content />
|
|
2178
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: ForTableHeaderRow, selector: "[forTableHeaderRow]", exportAs: ["forTableHeaderRow"] }, { kind: "directive", type: ForTableHeaderCell, selector: "[forTableHeaderCell]", inputs: ["name", "sticky"], exportAs: ["forTableHeaderCell"] }, { kind: "directive", type: ForTableCell, selector: "[forTableCell]", inputs: ["name", "sticky", "disabled"], exportAs: ["forTableCell"] }, { kind: "directive", type: ForTableRow, selector: "[forTableRow]", inputs: ["value", "level", "expandable", "virtualIndex"], exportAs: ["forTableRow"] }, { kind: "directive", type: ForTableSortHeader, selector: "[forTableSortHeader]", inputs: ["column", "direction", "disableClear", "firstClickDirection", "sortable"], outputs: ["directionChange", "sortChange"], exportAs: ["forTableSortHeader"] }, { kind: "directive", type: ForTableColumnResizer, selector: "[forTableColumnResizer]", inputs: ["column", "width", "min", "max", "step", "autoFit", "fitIncludesHeader"], outputs: ["widthChange", "resizeCommit"], exportAs: ["forTableColumnResizer"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2881
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: ForTableHeaderRow, selector: "[forTableHeaderRow]", exportAs: ["forTableHeaderRow"] }, { kind: "directive", type: ForTableHeaderCell, selector: "[forTableHeaderCell]", inputs: ["name", "sticky"], exportAs: ["forTableHeaderCell"] }, { kind: "directive", type: ForTableCell, selector: "[forTableCell]", inputs: ["name", "sticky", "disabled"], exportAs: ["forTableCell"] }, { kind: "directive", type: ForTableRow, selector: "[forTableRow]", inputs: ["value", "level", "expandable", "virtualIndex"], exportAs: ["forTableRow"] }, { kind: "directive", type: ForTableRowAttrs, selector: "[forTableRowAttrs]", inputs: ["forTableRowAttrs"] }, { kind: "directive", type: ForTableSortHeader, selector: "[forTableSortHeader]", inputs: ["column", "direction", "disableClear", "firstClickDirection", "sortable"], outputs: ["directionChange", "sortChange"], exportAs: ["forTableSortHeader"] }, { kind: "directive", type: ForTableColumnResizer, selector: "[forTableColumnResizer]", inputs: ["column", "width", "min", "max", "step", "autoFit", "fitIncludesHeader"], outputs: ["widthChange", "resizeCommit"], exportAs: ["forTableColumnResizer"] }, { kind: "directive", type: ForTableColumnReorder, selector: "[forTableColumnReorder]", outputs: ["columnReorder"], exportAs: ["forTableColumnReorder"] }, { kind: "directive", type: ForDraggable, selector: "[forDraggable]", inputs: ["dragData", "dragDisabled"], outputs: ["dragStart", "dragEnd"], exportAs: ["forDraggable"] }, { kind: "directive", type: ForDragPlaceholder, selector: "ng-template[forDragPlaceholder]", exportAs: ["forDragPlaceholder"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2179
2882
|
}
|
|
2180
2883
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableBody, decorators: [{
|
|
2181
2884
|
type: Component,
|
|
@@ -2189,39 +2892,117 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
2189
2892
|
ForTableHeaderCell,
|
|
2190
2893
|
ForTableCell,
|
|
2191
2894
|
ForTableRow,
|
|
2895
|
+
ForTableRowAttrs,
|
|
2192
2896
|
ForTableSortHeader,
|
|
2193
2897
|
ForTableColumnResizer,
|
|
2898
|
+
ForTableColumnReorder,
|
|
2899
|
+
ForDraggable,
|
|
2900
|
+
ForDragPlaceholder,
|
|
2194
2901
|
],
|
|
2195
2902
|
template: `
|
|
2196
|
-
<
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2903
|
+
<ng-template #headerCellContent let-col let-cell="cell">
|
|
2904
|
+
<ng-container
|
|
2905
|
+
[ngTemplateOutlet]="col.header().template"
|
|
2906
|
+
[ngTemplateOutletInjector]="cell.injector"
|
|
2907
|
+
/>
|
|
2908
|
+
@if (col.resizable()) {
|
|
2909
|
+
<button
|
|
2910
|
+
forTableColumnResizer
|
|
2204
2911
|
[column]="col.name()"
|
|
2205
|
-
[
|
|
2206
|
-
[
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2912
|
+
[width]="columnWidths()[col.name()]"
|
|
2913
|
+
[min]="col.resizeMin()"
|
|
2914
|
+
[max]="col.resizeMax()"
|
|
2915
|
+
[step]="col.resizeStep()"
|
|
2916
|
+
[autoFit]="col.autoFit()"
|
|
2917
|
+
[fitIncludesHeader]="col.fitIncludesHeader()"
|
|
2918
|
+
[attr.aria-label]="col.resizeAriaLabel()"
|
|
2919
|
+
(widthChange)="onColumnWidthChange(col.name(), $event)"
|
|
2920
|
+
(resizeCommit)="resizeCommit.emit($event)"
|
|
2921
|
+
></button>
|
|
2922
|
+
}
|
|
2923
|
+
</ng-template>
|
|
2924
|
+
|
|
2925
|
+
@if (hasReorderable()) {
|
|
2926
|
+
<div
|
|
2927
|
+
forTableHeaderRow
|
|
2928
|
+
forTableColumnReorder
|
|
2929
|
+
[style.display]="'grid'"
|
|
2930
|
+
[style.grid-template-columns]="track()"
|
|
2931
|
+
(columnReorder)="columnReorder.emit($event)"
|
|
2932
|
+
>
|
|
2933
|
+
@for (col of orderedColumns(); track col.name()) {
|
|
2934
|
+
@if (col.reorderable()) {
|
|
2935
|
+
<div
|
|
2936
|
+
#headerCell="forTableHeaderCell"
|
|
2937
|
+
forTableHeaderCell
|
|
2938
|
+
[name]="col.name()"
|
|
2939
|
+
[sticky]="col.sticky()"
|
|
2940
|
+
[class]="col.headerClass()"
|
|
2941
|
+
forTableSortHeader
|
|
2942
|
+
[column]="col.name()"
|
|
2943
|
+
[sortable]="col.sortable()"
|
|
2944
|
+
[direction]="directionFor(col.name())"
|
|
2945
|
+
(sortChange)="sortChange.emit($event)"
|
|
2946
|
+
forDraggable
|
|
2947
|
+
[dragData]="col.name()"
|
|
2948
|
+
>
|
|
2949
|
+
<ng-container
|
|
2950
|
+
[ngTemplateOutlet]="headerCellContent"
|
|
2951
|
+
[ngTemplateOutletInjector]="headerCell.injector"
|
|
2952
|
+
[ngTemplateOutletContext]="{ $implicit: col, cell: headerCell }"
|
|
2953
|
+
/>
|
|
2954
|
+
@if (columnDragPlaceholder(); as placeholder) {
|
|
2955
|
+
<ng-template forDragPlaceholder>
|
|
2956
|
+
<ng-container [ngTemplateOutlet]="placeholder.template" />
|
|
2957
|
+
</ng-template>
|
|
2958
|
+
}
|
|
2959
|
+
</div>
|
|
2960
|
+
} @else {
|
|
2961
|
+
<div
|
|
2962
|
+
#headerCell="forTableHeaderCell"
|
|
2963
|
+
forTableHeaderCell
|
|
2964
|
+
[name]="col.name()"
|
|
2965
|
+
[sticky]="col.sticky()"
|
|
2966
|
+
[class]="col.headerClass()"
|
|
2967
|
+
forTableSortHeader
|
|
2217
2968
|
[column]="col.name()"
|
|
2218
|
-
[
|
|
2219
|
-
|
|
2220
|
-
|
|
2969
|
+
[sortable]="col.sortable()"
|
|
2970
|
+
[direction]="directionFor(col.name())"
|
|
2971
|
+
(sortChange)="sortChange.emit($event)"
|
|
2972
|
+
>
|
|
2973
|
+
<ng-container
|
|
2974
|
+
[ngTemplateOutlet]="headerCellContent"
|
|
2975
|
+
[ngTemplateOutletInjector]="headerCell.injector"
|
|
2976
|
+
[ngTemplateOutletContext]="{ $implicit: col, cell: headerCell }"
|
|
2977
|
+
/>
|
|
2978
|
+
</div>
|
|
2221
2979
|
}
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2980
|
+
}
|
|
2981
|
+
</div>
|
|
2982
|
+
} @else {
|
|
2983
|
+
<div forTableHeaderRow [style.display]="'grid'" [style.grid-template-columns]="track()">
|
|
2984
|
+
@for (col of orderedColumns(); track col.name()) {
|
|
2985
|
+
<div
|
|
2986
|
+
#headerCell="forTableHeaderCell"
|
|
2987
|
+
forTableHeaderCell
|
|
2988
|
+
[name]="col.name()"
|
|
2989
|
+
[sticky]="col.sticky()"
|
|
2990
|
+
[class]="col.headerClass()"
|
|
2991
|
+
forTableSortHeader
|
|
2992
|
+
[column]="col.name()"
|
|
2993
|
+
[sortable]="col.sortable()"
|
|
2994
|
+
[direction]="directionFor(col.name())"
|
|
2995
|
+
(sortChange)="sortChange.emit($event)"
|
|
2996
|
+
>
|
|
2997
|
+
<ng-container
|
|
2998
|
+
[ngTemplateOutlet]="headerCellContent"
|
|
2999
|
+
[ngTemplateOutletInjector]="headerCell.injector"
|
|
3000
|
+
[ngTemplateOutletContext]="{ $implicit: col, cell: headerCell }"
|
|
3001
|
+
/>
|
|
3002
|
+
</div>
|
|
3003
|
+
}
|
|
3004
|
+
</div>
|
|
3005
|
+
}
|
|
2225
3006
|
<div
|
|
2226
3007
|
role="rowgroup"
|
|
2227
3008
|
[style.position]="sizerHeight() !== null ? 'relative' : null"
|
|
@@ -2231,7 +3012,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
2231
3012
|
@for (placeholder of placeholderRange(); track placeholder) {
|
|
2232
3013
|
<div forTableRow [style.display]="'grid'" [style.grid-template-columns]="track()">
|
|
2233
3014
|
@for (col of orderedColumns(); track col.name()) {
|
|
2234
|
-
<div
|
|
3015
|
+
<div
|
|
3016
|
+
#cell="forTableCell"
|
|
3017
|
+
forTableCell
|
|
3018
|
+
[name]="col.name()"
|
|
3019
|
+
[sticky]="col.sticky()"
|
|
3020
|
+
[class]="col.cellClass()"
|
|
3021
|
+
>
|
|
2235
3022
|
@if (col.placeholderCell(); as placeholderCell) {
|
|
2236
3023
|
<ng-container
|
|
2237
3024
|
[ngTemplateOutlet]="placeholderCell.template"
|
|
@@ -2246,34 +3033,67 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
2246
3033
|
@for (r of renderRows(); track r.key) {
|
|
2247
3034
|
<div
|
|
2248
3035
|
#rowRef="forTableRow"
|
|
3036
|
+
#rowEl
|
|
2249
3037
|
forTableRow
|
|
2250
3038
|
[value]="r.value"
|
|
2251
3039
|
[virtualIndex]="r.virtualIndex"
|
|
2252
3040
|
[attr.data-index]="r.virtualIndex"
|
|
3041
|
+
[attr.tabindex]="rowTabIndex(r)"
|
|
3042
|
+
[class]="rowClassFor(r)"
|
|
3043
|
+
[forTableRowAttrs]="rowAttrsFor(r)"
|
|
2253
3044
|
[style.display]="'grid'"
|
|
2254
3045
|
[style.grid-template-columns]="track()"
|
|
2255
3046
|
[style.position]="r.start !== null ? 'absolute' : null"
|
|
2256
3047
|
[style.left]="r.start !== null ? '0' : null"
|
|
2257
3048
|
[style.right]="r.start !== null ? '0' : null"
|
|
2258
3049
|
[style.transform]="r.start !== null ? 'translateY(' + r.start + 'px)' : null"
|
|
3050
|
+
(click)="onRowClick(r, $event)"
|
|
3051
|
+
(keydown.enter)="onRowEnter(r, $event)"
|
|
3052
|
+
(contextmenu)="onRowContextMenu(r, $event)"
|
|
2259
3053
|
>
|
|
2260
3054
|
@if (r.variant; as variant) {
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
3055
|
+
@if (variant.placeholderCells()) {
|
|
3056
|
+
@for (col of orderedColumns(); track col.name()) {
|
|
3057
|
+
<div
|
|
3058
|
+
#cell="forTableCell"
|
|
3059
|
+
forTableCell
|
|
3060
|
+
disabled
|
|
3061
|
+
[name]="col.name()"
|
|
3062
|
+
[sticky]="col.sticky()"
|
|
3063
|
+
[class]="col.cellClass()"
|
|
3064
|
+
>
|
|
3065
|
+
@if (col.placeholderCell(); as placeholderCell) {
|
|
3066
|
+
<ng-container
|
|
3067
|
+
[ngTemplateOutlet]="placeholderCell.template"
|
|
3068
|
+
[ngTemplateOutletInjector]="cell.injector"
|
|
3069
|
+
/>
|
|
3070
|
+
}
|
|
3071
|
+
</div>
|
|
3072
|
+
}
|
|
3073
|
+
} @else {
|
|
3074
|
+
<div
|
|
3075
|
+
[attr.role]="cellRole()"
|
|
3076
|
+
[attr.aria-colindex]="1"
|
|
3077
|
+
[attr.aria-colspan]="orderedColumns().length"
|
|
3078
|
+
[attr.data-row-variant]="''"
|
|
3079
|
+
[style.grid-column]="'1 / -1'"
|
|
3080
|
+
>
|
|
3081
|
+
<ng-container
|
|
3082
|
+
[ngTemplateOutlet]="variant.cell()?.template ?? null"
|
|
3083
|
+
[ngTemplateOutletInjector]="rowRef.injector"
|
|
3084
|
+
[ngTemplateOutletContext]="{ $implicit: r.datum, index: r.index }"
|
|
3085
|
+
/>
|
|
3086
|
+
</div>
|
|
3087
|
+
}
|
|
2274
3088
|
} @else {
|
|
2275
3089
|
@for (col of orderedColumns(); track col.name()) {
|
|
2276
|
-
<div
|
|
3090
|
+
<div
|
|
3091
|
+
#cell="forTableCell"
|
|
3092
|
+
forTableCell
|
|
3093
|
+
[name]="col.name()"
|
|
3094
|
+
[sticky]="col.sticky()"
|
|
3095
|
+
[class]="col.cellClass()"
|
|
3096
|
+
>
|
|
2277
3097
|
<ng-container
|
|
2278
3098
|
[ngTemplateOutlet]="col.dataCell().template"
|
|
2279
3099
|
[ngTemplateOutletInjector]="cell.injector"
|
|
@@ -2289,7 +3109,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
2289
3109
|
<ng-content />
|
|
2290
3110
|
`,
|
|
2291
3111
|
}]
|
|
2292
|
-
}], propDecorators: { rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], rowKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowKey", required: false }] }], displayedColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayedColumns", required: false }] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], placeholderRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholderRows", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], resizeCommit: [{ type: i0.Output, args: ["resizeCommit"] }], columns: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ForColumnDef), { isSignal: true }] }], rowDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ForRowDef), { isSignal: true }] }] } });
|
|
3112
|
+
}], ctorParameters: () => [], propDecorators: { rowEls: [{ type: i0.ViewChildren, args: ['rowEl', { isSignal: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], rowKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowKey", required: false }] }], displayedColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayedColumns", required: false }] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], placeholderRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholderRows", required: false }] }], measureRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "measureRows", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], resizeCommit: [{ type: i0.Output, args: ["resizeCommit"] }], columnReorder: [{ type: i0.Output, args: ["columnReorder"] }], columnWidths: [{ type: i0.Input, args: [{ isSignal: true, alias: "columnWidths", required: false }] }, { type: i0.Output, args: ["columnWidthsChange"] }], interactiveRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactiveRows", required: false }] }], rowActivate: [{ type: i0.Output, args: ["rowActivate"] }], rowContextMenu: [{ type: i0.Output, args: ["rowContextMenu"] }], rowClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowClass", required: false }] }], rowAttrs: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowAttrs", required: false }] }], columns: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ForColumnDef), { isSignal: true }] }], rowDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ForRowDef), { isSignal: true }] }], columnDragPlaceholder: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ForColumnDragPlaceholder), { isSignal: true }] }] } });
|
|
2293
3113
|
|
|
2294
3114
|
/**
|
|
2295
3115
|
* Decorative per-row selection affordance inside a `[forTableRow]`. Clicking it
|
|
@@ -2412,157 +3232,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
2412
3232
|
}]
|
|
2413
3233
|
}], ctorParameters: () => [] });
|
|
2414
3234
|
|
|
2415
|
-
/**
|
|
2416
|
-
* Opt-in **column reordering** for `ForTable`, composed over the drag-drop primitive.
|
|
2417
|
-
*
|
|
2418
|
-
* Apply on the `[forTableHeaderRow]` element. It wraps `[forDropList]` (via
|
|
2419
|
-
* `hostDirectives`) so the header cells become a reorderable list, then translates
|
|
2420
|
-
* drag-drop's generic drop into the table-friendly `columnReorder` output. Mark each
|
|
2421
|
-
* `[forTableHeaderCell]` as `[forDraggable]` with `[dragData]` set to the column name.
|
|
2422
|
-
* On a committed drop (pointer or keyboard) it emits the previous / new index and the
|
|
2423
|
-
* new column-name order; the consumer applies it to their own column array. **It never
|
|
2424
|
-
* reorders columns itself** (BYO-data).
|
|
2425
|
-
*
|
|
2426
|
-
* The wrapped list defaults to `orientation="horizontal"` (a column reorder is always along
|
|
2427
|
-
* the row axis), so no `orientation` binding is needed. Bind `orientation="vertical"` to
|
|
2428
|
-
* override for the rare case.
|
|
2429
|
-
*
|
|
2430
|
-
* In `mode="grid"` / `mode="treegrid"` the draggable header cells join the table's composite
|
|
2431
|
-
* roving grid as its first row, so a sortable + column-reorderable grid keeps the **single
|
|
2432
|
-
* tab stop** the WAI-ARIA Data Grid pattern calls for: `Tab` enters the grid once, Arrow keys
|
|
2433
|
-
* cross between header and body, and `Space` on a header cell lifts it for keyboard reordering.
|
|
2434
|
-
* It hands its drop-list roving to the grid via `FOR_DROP_LIST_ROVING_DELEGATE` and routes idle
|
|
2435
|
-
* header navigation through the table's grid keyboard handler.
|
|
2436
|
-
*
|
|
2437
|
-
* When a header cell is both sortable (`[forTableSortHeader]`) and reorderable, the two
|
|
2438
|
-
* keyboard activations split along WAI-ARIA lines so a single key never both sorts and lifts:
|
|
2439
|
-
* `Space` lifts the column, `Enter` toggles the sort. The split is enforced via a
|
|
2440
|
-
* `FOR_DRAGGABLE_LIFT_GUARD` that defers `Enter` to the sort header on cells carrying the
|
|
2441
|
-
* `data-sortable` marker — detected by DOM marker, so `forty-cdk/drag-drop` needs no table
|
|
2442
|
-
* import. A reorder-only header (no sort header) still lifts on both `Enter` and `Space`.
|
|
2443
|
-
*
|
|
2444
|
-
* @example
|
|
2445
|
-
* ```html
|
|
2446
|
-
* <div forTableHeaderRow forTableColumnReorder (columnReorder)="columns.set($event.columns)">
|
|
2447
|
-
* @for (col of columns(); track col) {
|
|
2448
|
-
* <div forTableHeaderCell [name]="col" forDraggable [dragData]="col">{{ col }}</div>
|
|
2449
|
-
* }
|
|
2450
|
-
* </div>
|
|
2451
|
-
* ```
|
|
2452
|
-
*/
|
|
2453
|
-
class ForTableColumnReorder {
|
|
2454
|
-
ctx = injectTableContext('ForTableColumnReorder');
|
|
2455
|
-
#list = inject(ForDropList);
|
|
2456
|
-
#host = inject(ElementRef).nativeElement;
|
|
2457
|
-
#isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
2458
|
-
/**
|
|
2459
|
-
* Fires once per committed reorder gesture (pointer drop or keyboard drop) with the
|
|
2460
|
-
* previous / new column index and the full column-name order after the move.
|
|
2461
|
-
*/
|
|
2462
|
-
columnReorder = output();
|
|
2463
|
-
constructor() {
|
|
2464
|
-
const destroyRef = inject(DestroyRef);
|
|
2465
|
-
const sub = this.#list.dragDrop.subscribe((event) => this.#emit(event));
|
|
2466
|
-
destroyRef.onDestroy(() => sub.unsubscribe());
|
|
2467
|
-
if (this.#isBrowser) {
|
|
2468
|
-
const onCaptureKeydown = (event) => this.#onCaptureKeydown(event);
|
|
2469
|
-
this.#host.addEventListener('keydown', onCaptureKeydown, { capture: true });
|
|
2470
|
-
destroyRef.onDestroy(() => this.#host.removeEventListener('keydown', onCaptureKeydown, { capture: true }));
|
|
2471
|
-
}
|
|
2472
|
-
}
|
|
2473
|
-
/**
|
|
2474
|
-
* When the header row joins the composite grid, intercepts idle (not-lifted) Arrow /
|
|
2475
|
-
* Home / End / Page keys on a draggable header cell in the capture phase and routes them
|
|
2476
|
-
* through the table's grid navigation, then stops propagation so the draggable's own
|
|
2477
|
-
* keydown does not also navigate. Space / Enter (and every key while a keyboard drag is in
|
|
2478
|
-
* progress) fall through untouched, so the draggable still owns the lift / move / drop /
|
|
2479
|
-
* cancel gesture — keeping the header one navigation continuum with the body while
|
|
2480
|
-
* preserving column reordering.
|
|
2481
|
-
*/
|
|
2482
|
-
#onCaptureKeydown(event) {
|
|
2483
|
-
if (!this.ctx.headerParticipatesInRoving()) {
|
|
2484
|
-
return;
|
|
2485
|
-
}
|
|
2486
|
-
const target = event.target;
|
|
2487
|
-
if (!(target instanceof HTMLElement)) {
|
|
2488
|
-
return;
|
|
2489
|
-
}
|
|
2490
|
-
if (!this.#list.items().some((handle) => handle.host === target) ||
|
|
2491
|
-
this.#list.isLifted(target)) {
|
|
2492
|
-
return;
|
|
2493
|
-
}
|
|
2494
|
-
if (this.ctx.handleHeaderCellKeydown(event, target)) {
|
|
2495
|
-
event.stopPropagation();
|
|
2496
|
-
}
|
|
2497
|
-
}
|
|
2498
|
-
#emit(event) {
|
|
2499
|
-
const names = this.#list.items().map((handle) => String(handle.data()));
|
|
2500
|
-
const columns = moveItemInArray(names, event.previousIndex, event.currentIndex);
|
|
2501
|
-
this.columnReorder.emit({ from: event.previousIndex, to: event.currentIndex, columns });
|
|
2502
|
-
}
|
|
2503
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableColumnReorder, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
2504
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForTableColumnReorder, isStandalone: true, selector: "[forTableColumnReorder]", outputs: { columnReorder: "columnReorder" }, providers: [
|
|
2505
|
-
{ provide: FOR_DROP_LIST_DEFAULT_ORIENTATION, useValue: 'horizontal' },
|
|
2506
|
-
{
|
|
2507
|
-
provide: FOR_DROP_LIST_ROVING_DELEGATE,
|
|
2508
|
-
useFactory: () => {
|
|
2509
|
-
const ctx = injectTableContext('ForTableColumnReorder');
|
|
2510
|
-
return {
|
|
2511
|
-
itemTabindex: (el) => ctx.headerParticipatesInRoving() ? ctx.headerCellTabIndex(el) : null,
|
|
2512
|
-
isItemHighlighted: (el) => ctx.headerParticipatesInRoving() ? ctx.isCellHighlighted(el) : null,
|
|
2513
|
-
};
|
|
2514
|
-
},
|
|
2515
|
-
},
|
|
2516
|
-
{
|
|
2517
|
-
provide: FOR_DRAGGABLE_LIFT_GUARD,
|
|
2518
|
-
useValue: {
|
|
2519
|
-
canLiftOnKey: (event, host) => !(event.key === 'Enter' && hostHasSortActivation(host)),
|
|
2520
|
-
},
|
|
2521
|
-
},
|
|
2522
|
-
], exportAs: ["forTableColumnReorder"], hostDirectives: [{ directive: i1.ForDropList, inputs: ["orientation", "orientation", "dir", "dir", "disabled", "disabled", "autoScroll", "autoScroll", "animateReorder", "animateReorder", "liveSort", "liveSort", "boundary", "boundary", "lockAxis", "lockAxis"] }], ngImport: i0 });
|
|
2523
|
-
}
|
|
2524
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableColumnReorder, decorators: [{
|
|
2525
|
-
type: Directive,
|
|
2526
|
-
args: [{
|
|
2527
|
-
selector: '[forTableColumnReorder]',
|
|
2528
|
-
exportAs: 'forTableColumnReorder',
|
|
2529
|
-
providers: [
|
|
2530
|
-
{ provide: FOR_DROP_LIST_DEFAULT_ORIENTATION, useValue: 'horizontal' },
|
|
2531
|
-
{
|
|
2532
|
-
provide: FOR_DROP_LIST_ROVING_DELEGATE,
|
|
2533
|
-
useFactory: () => {
|
|
2534
|
-
const ctx = injectTableContext('ForTableColumnReorder');
|
|
2535
|
-
return {
|
|
2536
|
-
itemTabindex: (el) => ctx.headerParticipatesInRoving() ? ctx.headerCellTabIndex(el) : null,
|
|
2537
|
-
isItemHighlighted: (el) => ctx.headerParticipatesInRoving() ? ctx.isCellHighlighted(el) : null,
|
|
2538
|
-
};
|
|
2539
|
-
},
|
|
2540
|
-
},
|
|
2541
|
-
{
|
|
2542
|
-
provide: FOR_DRAGGABLE_LIFT_GUARD,
|
|
2543
|
-
useValue: {
|
|
2544
|
-
canLiftOnKey: (event, host) => !(event.key === 'Enter' && hostHasSortActivation(host)),
|
|
2545
|
-
},
|
|
2546
|
-
},
|
|
2547
|
-
],
|
|
2548
|
-
hostDirectives: [
|
|
2549
|
-
{
|
|
2550
|
-
directive: ForDropList,
|
|
2551
|
-
inputs: [
|
|
2552
|
-
'orientation',
|
|
2553
|
-
'dir',
|
|
2554
|
-
'disabled',
|
|
2555
|
-
'autoScroll',
|
|
2556
|
-
'animateReorder',
|
|
2557
|
-
'liveSort',
|
|
2558
|
-
'boundary',
|
|
2559
|
-
'lockAxis',
|
|
2560
|
-
],
|
|
2561
|
-
},
|
|
2562
|
-
],
|
|
2563
|
-
}]
|
|
2564
|
-
}], ctorParameters: () => [], propDecorators: { columnReorder: [{ type: i0.Output, args: ["columnReorder"] }] } });
|
|
2565
|
-
|
|
2566
3235
|
const POINTER_ARM_THRESHOLD_PX = 5;
|
|
2567
3236
|
const LIFTED_NAV_KEYS = new Set([
|
|
2568
3237
|
'ArrowDown',
|
|
@@ -2978,5 +3647,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
|
|
|
2978
3647
|
* Generated bundle index. Do not edit.
|
|
2979
3648
|
*/
|
|
2980
3649
|
|
|
2981
|
-
export { FOR_TABLE_CONTEXT, ForColumnDef, ForDataCell, ForHeaderCell, ForPlaceholderCell, ForRowCell, ForRowDef, ForTable, ForTableBody, ForTableCell, ForTableColumnLabel, ForTableColumnReorder, ForTableColumnResizer, ForTableHeaderCell, ForTableHeaderRow, ForTableRow, ForTableRowReorder, ForTableRowSelector, ForTableSelectAll, ForTableSortHeader };
|
|
3650
|
+
export { FOR_TABLE_CONTEXT, ForColumnDef, ForColumnDragPlaceholder, ForDataCell, ForHeaderCell, ForPlaceholderCell, ForRowCell, ForRowDef, ForTable, ForTableBody, ForTableCell, ForTableColumnLabel, ForTableColumnReorder, ForTableColumnResizer, ForTableHeaderCell, ForTableHeaderRow, ForTableRow, ForTableRowReorder, ForTableRowSelector, ForTableSelectAll, ForTableSortHeader };
|
|
2982
3651
|
//# sourceMappingURL=forty-cdk-table.mjs.map
|