forty-cdk 0.2.0 → 0.3.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/accordion/README.md +122 -0
- package/aspect-ratio/README.md +76 -0
- package/avatar/README.md +100 -0
- package/breadcrumbs/README.md +49 -0
- package/breakpoints/README.md +81 -0
- package/button/README.md +49 -0
- package/calendar/README.md +458 -0
- package/carousel/README.md +358 -0
- package/checkbox/README.md +146 -0
- package/combobox/README.md +535 -0
- package/context-menu/README.md +139 -0
- package/date-field/README.md +184 -0
- package/date-picker/README.md +338 -0
- package/dialog/README.md +388 -0
- package/disclosure/README.md +114 -0
- package/drag-drop/README.md +359 -0
- package/drawer/README.md +560 -0
- package/dropdown-menu/README.md +176 -0
- package/fesm2022/forty-cdk-accordion.mjs +348 -0
- package/fesm2022/forty-cdk-accordion.mjs.map +1 -0
- package/fesm2022/forty-cdk-aspect-ratio.mjs +74 -0
- package/fesm2022/forty-cdk-aspect-ratio.mjs.map +1 -0
- package/fesm2022/forty-cdk-avatar.mjs +308 -0
- package/fesm2022/forty-cdk-avatar.mjs.map +1 -0
- package/fesm2022/forty-cdk-breadcrumbs.mjs +125 -0
- package/fesm2022/forty-cdk-breadcrumbs.mjs.map +1 -0
- package/fesm2022/forty-cdk-breakpoints.mjs +117 -0
- package/fesm2022/forty-cdk-breakpoints.mjs.map +1 -0
- package/fesm2022/forty-cdk-button.mjs +134 -0
- package/fesm2022/forty-cdk-button.mjs.map +1 -0
- package/fesm2022/forty-cdk-calendar.mjs +2034 -0
- package/fesm2022/forty-cdk-calendar.mjs.map +1 -0
- package/fesm2022/forty-cdk-carousel.mjs +968 -0
- package/fesm2022/forty-cdk-carousel.mjs.map +1 -0
- package/fesm2022/forty-cdk-checkbox.mjs +226 -0
- package/fesm2022/forty-cdk-checkbox.mjs.map +1 -0
- package/fesm2022/forty-cdk-combobox.mjs +2596 -0
- package/fesm2022/forty-cdk-combobox.mjs.map +1 -0
- package/fesm2022/forty-cdk-context-menu.mjs +413 -0
- package/fesm2022/forty-cdk-context-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-core.mjs +9022 -0
- package/fesm2022/forty-cdk-core.mjs.map +1 -0
- package/fesm2022/forty-cdk-date-field.mjs +744 -0
- package/fesm2022/forty-cdk-date-field.mjs.map +1 -0
- package/fesm2022/forty-cdk-date-picker.mjs +1011 -0
- package/fesm2022/forty-cdk-date-picker.mjs.map +1 -0
- package/fesm2022/forty-cdk-dialog.mjs +707 -0
- package/fesm2022/forty-cdk-dialog.mjs.map +1 -0
- package/fesm2022/forty-cdk-disclosure.mjs +190 -0
- package/fesm2022/forty-cdk-disclosure.mjs.map +1 -0
- package/fesm2022/forty-cdk-drag-drop.mjs +1180 -0
- package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -0
- package/fesm2022/forty-cdk-drawer.mjs +1641 -0
- package/fesm2022/forty-cdk-drawer.mjs.map +1 -0
- package/fesm2022/forty-cdk-dropdown-menu.mjs +350 -0
- package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-field.mjs +425 -0
- package/fesm2022/forty-cdk-field.mjs.map +1 -0
- package/fesm2022/forty-cdk-fieldset.mjs +164 -0
- package/fesm2022/forty-cdk-fieldset.mjs.map +1 -0
- package/fesm2022/forty-cdk-file-upload.mjs +221 -0
- package/fesm2022/forty-cdk-file-upload.mjs.map +1 -0
- package/fesm2022/forty-cdk-hover-card.mjs +496 -0
- package/fesm2022/forty-cdk-hover-card.mjs.map +1 -0
- package/fesm2022/forty-cdk-input.mjs +274 -0
- package/fesm2022/forty-cdk-input.mjs.map +1 -0
- package/fesm2022/forty-cdk-internationalized-date.mjs +1 -1
- package/fesm2022/forty-cdk-internationalized-date.mjs.map +1 -1
- package/fesm2022/forty-cdk-listbox.mjs +1279 -0
- package/fesm2022/forty-cdk-listbox.mjs.map +1 -0
- package/fesm2022/forty-cdk-menu.mjs +1439 -0
- package/fesm2022/forty-cdk-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-menubar.mjs +787 -0
- package/fesm2022/forty-cdk-menubar.mjs.map +1 -0
- package/fesm2022/forty-cdk-meter.mjs +211 -0
- package/fesm2022/forty-cdk-meter.mjs.map +1 -0
- package/fesm2022/forty-cdk-navigation-menu.mjs +1145 -0
- package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-number-input.mjs +559 -0
- package/fesm2022/forty-cdk-number-input.mjs.map +1 -0
- package/fesm2022/forty-cdk-otp-input.mjs +527 -0
- package/fesm2022/forty-cdk-otp-input.mjs.map +1 -0
- package/fesm2022/forty-cdk-pagination.mjs +323 -0
- package/fesm2022/forty-cdk-pagination.mjs.map +1 -0
- package/fesm2022/forty-cdk-pane-resizer.mjs +297 -0
- package/fesm2022/forty-cdk-pane-resizer.mjs.map +1 -0
- package/fesm2022/forty-cdk-popover.mjs +698 -0
- package/fesm2022/forty-cdk-popover.mjs.map +1 -0
- package/fesm2022/forty-cdk-progress.mjs +226 -0
- package/fesm2022/forty-cdk-progress.mjs.map +1 -0
- package/fesm2022/forty-cdk-radio-group.mjs +378 -0
- package/fesm2022/forty-cdk-radio-group.mjs.map +1 -0
- package/fesm2022/forty-cdk-scroll-area.mjs +640 -0
- package/fesm2022/forty-cdk-scroll-area.mjs.map +1 -0
- package/fesm2022/forty-cdk-search.mjs +205 -0
- package/fesm2022/forty-cdk-search.mjs.map +1 -0
- package/fesm2022/forty-cdk-select.mjs +1661 -0
- package/fesm2022/forty-cdk-select.mjs.map +1 -0
- package/fesm2022/forty-cdk-separator.mjs +82 -0
- package/fesm2022/forty-cdk-separator.mjs.map +1 -0
- package/fesm2022/forty-cdk-signal-forms.mjs +97 -0
- package/fesm2022/forty-cdk-signal-forms.mjs.map +1 -0
- package/fesm2022/forty-cdk-slider.mjs +803 -0
- package/fesm2022/forty-cdk-slider.mjs.map +1 -0
- package/fesm2022/forty-cdk-stepper.mjs +886 -0
- package/fesm2022/forty-cdk-stepper.mjs.map +1 -0
- package/fesm2022/forty-cdk-switch.mjs +137 -0
- package/fesm2022/forty-cdk-switch.mjs.map +1 -0
- package/fesm2022/forty-cdk-table.mjs +1518 -0
- package/fesm2022/forty-cdk-table.mjs.map +1 -0
- package/fesm2022/forty-cdk-tabs.mjs +400 -0
- package/fesm2022/forty-cdk-tabs.mjs.map +1 -0
- package/fesm2022/forty-cdk-time-field.mjs +593 -0
- package/fesm2022/forty-cdk-time-field.mjs.map +1 -0
- package/fesm2022/forty-cdk-time-picker.mjs +1013 -0
- package/fesm2022/forty-cdk-time-picker.mjs.map +1 -0
- package/fesm2022/forty-cdk-toast.mjs +1153 -0
- package/fesm2022/forty-cdk-toast.mjs.map +1 -0
- package/fesm2022/forty-cdk-toggle.mjs +516 -0
- package/fesm2022/forty-cdk-toggle.mjs.map +1 -0
- package/fesm2022/forty-cdk-toolbar.mjs +374 -0
- package/fesm2022/forty-cdk-toolbar.mjs.map +1 -0
- package/fesm2022/forty-cdk-tooltip.mjs +672 -0
- package/fesm2022/forty-cdk-tooltip.mjs.map +1 -0
- package/fesm2022/forty-cdk-tree.mjs +2007 -0
- package/fesm2022/forty-cdk-tree.mjs.map +1 -0
- package/fesm2022/forty-cdk-virtualization.mjs +1 -1
- package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
- package/fesm2022/forty-cdk.mjs +0 -43310
- package/fesm2022/forty-cdk.mjs.map +1 -1
- package/field/README.md +97 -0
- package/fieldset/README.md +86 -0
- package/file-upload/README.md +73 -0
- package/hover-card/README.md +171 -0
- package/input/README.md +156 -0
- package/listbox/README.md +424 -0
- package/menu/README.md +181 -0
- package/menubar/README.md +140 -0
- package/meter/README.md +128 -0
- package/navigation-menu/README.md +253 -0
- package/number-input/README.md +171 -0
- package/otp-input/README.md +198 -0
- package/package.json +213 -1
- package/pagination/README.md +61 -0
- package/pane-resizer/README.md +136 -0
- package/popover/README.md +262 -0
- package/progress/README.md +115 -0
- package/radio-group/README.md +129 -0
- package/scroll-area/README.md +184 -0
- package/search/README.md +42 -0
- package/select/README.md +488 -0
- package/separator/README.md +84 -0
- package/signal-forms/README.md +72 -0
- package/slider/README.md +152 -0
- package/stepper/README.md +292 -0
- package/switch/README.md +116 -0
- package/table/README.md +769 -0
- package/tabs/README.md +130 -0
- package/time-field/README.md +157 -0
- package/time-picker/README.md +172 -0
- package/toast/README.md +398 -0
- package/toggle/README.md +224 -0
- package/toolbar/README.md +109 -0
- package/tooltip/README.md +274 -0
- package/tree/README.md +708 -0
- package/types/forty-cdk-accordion.d.ts +242 -0
- package/types/forty-cdk-aspect-ratio.d.ts +59 -0
- package/types/forty-cdk-avatar.d.ts +133 -0
- package/types/forty-cdk-breadcrumbs.d.ts +92 -0
- package/types/forty-cdk-breakpoints.d.ts +141 -0
- package/types/forty-cdk-button.d.ts +80 -0
- package/types/forty-cdk-calendar.d.ts +914 -0
- package/types/forty-cdk-carousel.d.ts +530 -0
- package/types/forty-cdk-checkbox.d.ts +141 -0
- package/types/forty-cdk-combobox.d.ts +1259 -0
- package/types/forty-cdk-context-menu.d.ts +313 -0
- package/types/forty-cdk-core.d.ts +5774 -0
- package/types/forty-cdk-date-field.d.ts +307 -0
- package/types/forty-cdk-date-picker.d.ts +622 -0
- package/types/forty-cdk-dialog.d.ts +546 -0
- package/types/forty-cdk-disclosure.d.ts +127 -0
- package/types/forty-cdk-drag-drop.d.ts +456 -0
- package/types/forty-cdk-drawer.d.ts +871 -0
- package/types/forty-cdk-dropdown-menu.d.ts +242 -0
- package/types/forty-cdk-field.d.ts +236 -0
- package/types/forty-cdk-fieldset.d.ts +119 -0
- package/types/forty-cdk-file-upload.d.ts +124 -0
- package/types/forty-cdk-hover-card.d.ts +320 -0
- package/types/forty-cdk-input.d.ts +169 -0
- package/types/forty-cdk-internationalized-date.d.ts +1 -1
- package/types/forty-cdk-listbox.d.ts +513 -0
- package/types/forty-cdk-menu.d.ts +629 -0
- package/types/forty-cdk-menubar.d.ts +451 -0
- package/types/forty-cdk-meter.d.ts +122 -0
- package/types/forty-cdk-navigation-menu.d.ts +514 -0
- package/types/forty-cdk-number-input.d.ts +319 -0
- package/types/forty-cdk-otp-input.d.ts +248 -0
- package/types/forty-cdk-pagination.d.ts +214 -0
- package/types/forty-cdk-pane-resizer.d.ts +145 -0
- package/types/forty-cdk-popover.d.ts +509 -0
- package/types/forty-cdk-progress.d.ts +143 -0
- package/types/forty-cdk-radio-group.d.ts +222 -0
- package/types/forty-cdk-scroll-area.d.ts +258 -0
- package/types/forty-cdk-search.d.ts +142 -0
- package/types/forty-cdk-select.d.ts +899 -0
- package/types/forty-cdk-separator.d.ts +59 -0
- package/types/forty-cdk-signal-forms.d.ts +58 -0
- package/types/forty-cdk-slider.d.ts +379 -0
- package/types/forty-cdk-stepper.d.ts +650 -0
- package/types/forty-cdk-switch.d.ts +87 -0
- package/types/forty-cdk-table.d.ts +723 -0
- package/types/forty-cdk-tabs.d.ts +235 -0
- package/types/forty-cdk-time-field.d.ts +307 -0
- package/types/forty-cdk-time-picker.d.ts +578 -0
- package/types/forty-cdk-toast.d.ts +598 -0
- package/types/forty-cdk-toggle.d.ts +310 -0
- package/types/forty-cdk-toolbar.d.ts +217 -0
- package/types/forty-cdk-tooltip.d.ts +436 -0
- package/types/forty-cdk-tree.d.ts +688 -0
- package/types/forty-cdk.d.ts +1 -19743
|
@@ -0,0 +1,1518 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, booleanAttribute, inject, signal, computed, input, model, ElementRef, Directive, DestroyRef, numberAttribute, output, effect, PLATFORM_ID, afterNextRender, DOCUMENT } from '@angular/core';
|
|
3
|
+
import { SelectionModel, injectTextDirection, injectElementSize, Collection, RovingTabindex, firstEnabledHost, computeFlatHierarchy, reconcileRovingActive, resolveTreegridExpandCollapse, resolveGridNavigation, moveGridIndex, registerHandle, startPointerResize, clampToRange, translateWindowReorder, LiveAnnouncer, createKeyboardDragMediator, createDefaults } from 'forty-cdk/core';
|
|
4
|
+
import * as i1 from 'forty-cdk/drag-drop';
|
|
5
|
+
import { FOR_DRAGGABLE_CONTEXT, ForDropList, moveItemInArray, FOR_DROP_LIST_DEFAULT_ORIENTATION, FOR_DRAG_DROP_DEFAULTS } from 'forty-cdk/drag-drop';
|
|
6
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
7
|
+
|
|
8
|
+
const FOR_TABLE_CONTEXT = new InjectionToken('FOR_TABLE_CONTEXT');
|
|
9
|
+
const FOR_TABLE_ROW_CONTEXT = new InjectionToken('FOR_TABLE_ROW_CONTEXT');
|
|
10
|
+
/**
|
|
11
|
+
* Coerces the `sticky` input value for header and data cells.
|
|
12
|
+
* The string `'end'` pins the cell to the end edge; any other truthy value
|
|
13
|
+
* (including the empty string from a bare `sticky` attribute) pins it to the
|
|
14
|
+
* start edge; a falsy value means not sticky.
|
|
15
|
+
*/
|
|
16
|
+
function coerceSticky(value) {
|
|
17
|
+
return value === 'end' ? 'end' : booleanAttribute(value);
|
|
18
|
+
}
|
|
19
|
+
function injectTableContext(piece) {
|
|
20
|
+
const ctx = inject(FOR_TABLE_CONTEXT, { optional: true });
|
|
21
|
+
if (!ctx) {
|
|
22
|
+
throw new Error(`[forty-cdk/table] ${piece} must be used inside a [forTable] element.`);
|
|
23
|
+
}
|
|
24
|
+
return ctx;
|
|
25
|
+
}
|
|
26
|
+
function injectTableRowContext(piece) {
|
|
27
|
+
const ctx = inject(FOR_TABLE_ROW_CONTEXT, { optional: true });
|
|
28
|
+
if (!ctx) {
|
|
29
|
+
throw new Error(`[forty-cdk/table] ${piece} must be used inside a [forTableRow] element.`);
|
|
30
|
+
}
|
|
31
|
+
return ctx;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Row-selection sub-model for `ForTable`. Owns the `SelectionModel`, the range
|
|
36
|
+
* anchor, and the toggle / replace / range / select-all algorithms, decoupled
|
|
37
|
+
* from the table root.
|
|
38
|
+
*
|
|
39
|
+
* Internal — not re-exported from `table/index.ts` or `public-api.ts`.
|
|
40
|
+
*/
|
|
41
|
+
class TableRowSelection {
|
|
42
|
+
#selectionMode;
|
|
43
|
+
#selectionBehavior;
|
|
44
|
+
#compareWith;
|
|
45
|
+
#aggregateValues;
|
|
46
|
+
#model;
|
|
47
|
+
#anchor = signal(undefined, /* @ts-ignore */
|
|
48
|
+
...(ngDevMode ? [{ debugName: "#anchor" }] : /* istanbul ignore next */ []));
|
|
49
|
+
/** Aggregate selection state across all selectable rows (`'none'` / `'some'` / `'all'`). */
|
|
50
|
+
selectAllState = computed(() => {
|
|
51
|
+
const values = this.#aggregateValues();
|
|
52
|
+
if (values.length === 0) {
|
|
53
|
+
return 'none';
|
|
54
|
+
}
|
|
55
|
+
let count = 0;
|
|
56
|
+
for (const v of values) {
|
|
57
|
+
if (this.#model.isSelected(v)) {
|
|
58
|
+
count += 1;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (count === 0) {
|
|
62
|
+
return 'none';
|
|
63
|
+
}
|
|
64
|
+
return count === values.length ? 'all' : 'some';
|
|
65
|
+
}, /* @ts-ignore */
|
|
66
|
+
...(ngDevMode ? [{ debugName: "selectAllState" }] : /* istanbul ignore next */ []));
|
|
67
|
+
constructor(deps) {
|
|
68
|
+
this.#selectionMode = deps.selectionMode;
|
|
69
|
+
this.#selectionBehavior = deps.selectionBehavior;
|
|
70
|
+
this.#compareWith = deps.compareWith;
|
|
71
|
+
this.#aggregateValues = deps.aggregateValues;
|
|
72
|
+
this.#model = new SelectionModel(deps.selection, {
|
|
73
|
+
multiple: computed(() => deps.selectionMode() === 'multiple'),
|
|
74
|
+
compareWith: (a, b) => deps.compareWith()(a, b),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/** Whether `value` is currently in the selection. */
|
|
78
|
+
isSelected(value) {
|
|
79
|
+
return this.#model.isSelected(value);
|
|
80
|
+
}
|
|
81
|
+
/** Toggles `value` in or out of the selection and re-anchors. No-op in `'none'` mode. */
|
|
82
|
+
toggle(value) {
|
|
83
|
+
if (this.#selectionMode() === 'none') {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this.#model.toggle(value);
|
|
87
|
+
this.#anchor.set(value);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Applies a row selection click with optional modifier keys, honoring
|
|
91
|
+
* `selectionBehavior`: `'toggle'` always flips; `'replace'` replaces (Ctrl/Cmd
|
|
92
|
+
* toggles a single item, Shift extends a range in multiple mode).
|
|
93
|
+
*/
|
|
94
|
+
select(value, modifiers) {
|
|
95
|
+
const mode = this.#selectionMode();
|
|
96
|
+
if (mode === 'none') {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (this.#selectionBehavior() === 'toggle') {
|
|
100
|
+
this.#model.toggle(value);
|
|
101
|
+
this.#anchor.set(value);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const multiple = mode === 'multiple';
|
|
105
|
+
if (multiple && modifiers?.shiftKey) {
|
|
106
|
+
this.#selectRange(value);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (multiple && (modifiers?.ctrlKey || modifiers?.metaKey)) {
|
|
110
|
+
this.#model.toggle(value);
|
|
111
|
+
this.#anchor.set(value);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
this.#model.setSelection(value);
|
|
115
|
+
this.#anchor.set(value);
|
|
116
|
+
}
|
|
117
|
+
/** Selects all selectable rows when not all are selected; clears when all are. No-op outside `'multiple'` mode. */
|
|
118
|
+
toggleSelectAll() {
|
|
119
|
+
if (this.#selectionMode() !== 'multiple') {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (this.selectAllState() === 'all') {
|
|
123
|
+
this.#model.clear();
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
this.#model.select(...this.#aggregateValues());
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
#selectRange(toValue) {
|
|
130
|
+
const values = this.#aggregateValues();
|
|
131
|
+
const equals = this.#compareWith();
|
|
132
|
+
const toIdx = values.findIndex((v) => equals(v, toValue));
|
|
133
|
+
if (toIdx < 0) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const anchor = this.#anchor();
|
|
137
|
+
const anchorIdx = anchor === undefined ? -1 : values.findIndex((v) => equals(v, anchor));
|
|
138
|
+
const start = anchorIdx < 0 ? toIdx : anchorIdx;
|
|
139
|
+
const [lo, hi] = start <= toIdx ? [start, toIdx] : [toIdx, start];
|
|
140
|
+
this.#model.setSelection(...values.slice(lo, hi + 1));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Treegrid expansion sub-model for `ForTable`. Owns the membership and mutation
|
|
146
|
+
* algorithm for the `[(expanded)]` open-rows set, decoupled from the table root.
|
|
147
|
+
*
|
|
148
|
+
* Internal — not re-exported from `table/index.ts` or `public-api.ts`.
|
|
149
|
+
*/
|
|
150
|
+
class TableExpansion {
|
|
151
|
+
#expanded;
|
|
152
|
+
#compareWith;
|
|
153
|
+
constructor(deps) {
|
|
154
|
+
this.#expanded = deps.expanded;
|
|
155
|
+
this.#compareWith = deps.compareWith;
|
|
156
|
+
}
|
|
157
|
+
/** Whether `value` is currently in the open-rows set. */
|
|
158
|
+
isExpanded(value) {
|
|
159
|
+
return this.#expanded().some((v) => this.#compareWith()(v, value));
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Sets a parent row's expansion in or out of the open-rows set. No-op when
|
|
163
|
+
* `value` is undefined or already in the requested state.
|
|
164
|
+
*/
|
|
165
|
+
setExpanded(value, open) {
|
|
166
|
+
if (value === undefined) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const current = this.#expanded();
|
|
170
|
+
const has = this.isExpanded(value);
|
|
171
|
+
if (open && !has) {
|
|
172
|
+
this.#expanded.set([...current, value]);
|
|
173
|
+
}
|
|
174
|
+
else if (!open && has) {
|
|
175
|
+
this.#expanded.set(current.filter((v) => !this.#compareWith()(v, value)));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/** Toggles a parent row's expansion. No-op when `value` is undefined. */
|
|
179
|
+
toggle(value) {
|
|
180
|
+
if (value === undefined) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
this.setExpanded(value, !this.isExpanded(value));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Grid actions whose target may lie on a row outside the rendered virtualized window. */
|
|
188
|
+
const ROW_CROSSING_ACTIONS = new Set([
|
|
189
|
+
'next-row',
|
|
190
|
+
'prev-row',
|
|
191
|
+
'first',
|
|
192
|
+
'last',
|
|
193
|
+
]);
|
|
194
|
+
/**
|
|
195
|
+
* Root of the Table primitive. Sets the ARIA `role` from `mode`, reflects
|
|
196
|
+
* writing direction, and publishes the `--for-table-header-height` CSS custom
|
|
197
|
+
* property (driven by a `ResizeObserver` on the first registered header row)
|
|
198
|
+
* so consumers can `position: sticky` header cells without hard-coding offsets.
|
|
199
|
+
*
|
|
200
|
+
* Implements the [WAI-ARIA Table pattern](https://www.w3.org/WAI/ARIA/apg/patterns/table/)
|
|
201
|
+
* and the [WAI-ARIA Grid pattern](https://www.w3.org/WAI/ARIA/apg/patterns/grid/).
|
|
202
|
+
*
|
|
203
|
+
* Use `mode="grid"` or `mode="treegrid"` for interactive grid semantics: a
|
|
204
|
+
* single-tab-stop roving group with 2D arrow navigation over data cells.
|
|
205
|
+
* The default `mode="table"` is the static read-only structure.
|
|
206
|
+
*/
|
|
207
|
+
class ForTable {
|
|
208
|
+
/**
|
|
209
|
+
* ARIA role emitted on the host. `'table'` is the default static read-only
|
|
210
|
+
* structure. `'grid'` and `'treegrid'` provide single-tab-stop roving + 2D
|
|
211
|
+
* arrow navigation over data cells.
|
|
212
|
+
*/
|
|
213
|
+
mode = input('table', /* @ts-ignore */
|
|
214
|
+
...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
215
|
+
/**
|
|
216
|
+
* Accessible label for the table. When set, reflected as `aria-label`.
|
|
217
|
+
* Consumers with a visible caption should prefer pointing native
|
|
218
|
+
* `aria-labelledby` at it instead; this input is the reactive convenience
|
|
219
|
+
* hook for cases where no visible label element exists.
|
|
220
|
+
*/
|
|
221
|
+
ariaLabel = input(null, /* @ts-ignore */
|
|
222
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
223
|
+
/**
|
|
224
|
+
* Writing direction. When unset (default `null`), the inherited ambient
|
|
225
|
+
* direction is resolved from the nearest ancestor carrying a `dir` attribute
|
|
226
|
+
* (or `<html dir>`), defaulting to `'ltr'`. An explicit `[dir]` always wins.
|
|
227
|
+
* The resolved value is reflected to the host `dir` attribute.
|
|
228
|
+
*/
|
|
229
|
+
_dirInput = input(null, { ...(ngDevMode ? { debugName: "_dirInput" } : /* istanbul ignore next */ {}), alias: 'dir' });
|
|
230
|
+
dir = injectTextDirection(this._dirInput);
|
|
231
|
+
/**
|
|
232
|
+
* True total number of data rows for `aria-rowcount`, e.g. for a server-paged or
|
|
233
|
+
* (later) virtualized table that renders only a window of rows. Defaults to the
|
|
234
|
+
* rendered data-row count. Ignored in `mode="table"`.
|
|
235
|
+
*/
|
|
236
|
+
rowCount = input(/* @ts-ignore */
|
|
237
|
+
...(ngDevMode ? [undefined, { debugName: "rowCount" }] : /* istanbul ignore next */ []));
|
|
238
|
+
/**
|
|
239
|
+
* True total number of columns for `aria-colcount`. Defaults to the rendered
|
|
240
|
+
* column count (the cells of the first data row). Ignored in `mode="table"`.
|
|
241
|
+
*/
|
|
242
|
+
colCount = input(/* @ts-ignore */
|
|
243
|
+
...(ngDevMode ? [undefined, { debugName: "colCount" }] : /* istanbul ignore next */ []));
|
|
244
|
+
/** Row selection mode. `'none'` (default) disables selection entirely. */
|
|
245
|
+
selectionMode = input('none', /* @ts-ignore */
|
|
246
|
+
...(ngDevMode ? [{ debugName: "selectionMode" }] : /* istanbul ignore next */ []));
|
|
247
|
+
/**
|
|
248
|
+
* How a row click changes the selection. `'toggle'` (default) flips the clicked
|
|
249
|
+
* row. `'replace'` replaces the selection with the clicked row; Ctrl/Cmd-click
|
|
250
|
+
* toggles a single row and Shift-click extends a range (multiple mode only).
|
|
251
|
+
*/
|
|
252
|
+
selectionBehavior = input('toggle', /* @ts-ignore */
|
|
253
|
+
...(ngDevMode ? [{ debugName: "selectionBehavior" }] : /* istanbul ignore next */ []));
|
|
254
|
+
/**
|
|
255
|
+
* Two-way bindable selected row values (each row's `[value]`). Single mode keeps
|
|
256
|
+
* 0–1 entries. The implicit `selectionChange` fires only on internal mutations
|
|
257
|
+
* (selector / row click / Space / select-all), never on consumer writes.
|
|
258
|
+
*/
|
|
259
|
+
selection = model([], /* @ts-ignore */
|
|
260
|
+
...(ngDevMode ? [{ debugName: "selection" }] : /* istanbul ignore next */ []));
|
|
261
|
+
/** Equality comparator for row values. Defaults to `===`; supply id-based for objects. */
|
|
262
|
+
compareWith = input((a, b) => a === b, /* @ts-ignore */
|
|
263
|
+
...(ngDevMode ? [{ debugName: "compareWith" }] : /* istanbul ignore next */ []));
|
|
264
|
+
/**
|
|
265
|
+
* Full ordered set of selectable row values (each row's `[value]`), for a
|
|
266
|
+
* virtualized or server-paged table whose aggregate selection operations must
|
|
267
|
+
* span rows beyond the rendered window. When `null` (default), the select-all
|
|
268
|
+
* tri-state, `toggleSelectAll`, and Shift-click range selection compute against
|
|
269
|
+
* the registered (rendered) rows only. When supplied, they use this set as the
|
|
270
|
+
* universe of selectable values, so a range can span unmounted rows and the
|
|
271
|
+
* tri-state reflects the true dataset. Per-row selection is unaffected.
|
|
272
|
+
*/
|
|
273
|
+
selectableValues = input(null, /* @ts-ignore */
|
|
274
|
+
...(ngDevMode ? [{ debugName: "selectableValues" }] : /* istanbul ignore next */ []));
|
|
275
|
+
/**
|
|
276
|
+
* Two-way bindable open parent-row values (each row's `[value]`), for
|
|
277
|
+
* `mode="treegrid"`. The implicit `expandedChange` fires only on internal
|
|
278
|
+
* expand/collapse (ArrowRight/ArrowLeft, `toggleRowExpansion`), never on
|
|
279
|
+
* consumer writes through `[(expanded)]`. Ignored outside `treegrid` mode.
|
|
280
|
+
*/
|
|
281
|
+
expanded = model([], /* @ts-ignore */
|
|
282
|
+
...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
|
|
283
|
+
#rootEl = inject(ElementRef).nativeElement;
|
|
284
|
+
#headerRowEl = signal(null, /* @ts-ignore */
|
|
285
|
+
...(ngDevMode ? [{ debugName: "#headerRowEl" }] : /* istanbul ignore next */ []));
|
|
286
|
+
headerSize = injectElementSize(this.#headerRowEl);
|
|
287
|
+
#rows = new Collection();
|
|
288
|
+
#roving = new RovingTabindex();
|
|
289
|
+
/** Live registered data rows, exposed to `[forTableVirtualized]`'s cross-window navigation bridge. */
|
|
290
|
+
rows = this.#rows.items;
|
|
291
|
+
#virtualNav = signal(null, /* @ts-ignore */
|
|
292
|
+
...(ngDevMode ? [{ debugName: "#virtualNav" }] : /* istanbul ignore next */ []));
|
|
293
|
+
#reorderingRow = signal(null, /* @ts-ignore */
|
|
294
|
+
...(ngDevMode ? [{ debugName: "#reorderingRow" }] : /* istanbul ignore next */ []));
|
|
295
|
+
/**
|
|
296
|
+
* Absolute index of the row currently being pointer-reordered, or `null`. Set by
|
|
297
|
+
* `[forTableRowReorder]` and read by `[forTableVirtualized]` to keep the lifted
|
|
298
|
+
* row mounted across recycling during a drag.
|
|
299
|
+
*/
|
|
300
|
+
reorderingRowIndex = this.#reorderingRow.asReadonly();
|
|
301
|
+
virtualRowNavigation = this.#virtualNav.asReadonly();
|
|
302
|
+
#flatCells = computed(() => this.#rows.items().flatMap((row) => row.cells()), /* @ts-ignore */
|
|
303
|
+
...(ngDevMode ? [{ debugName: "#flatCells" }] : /* istanbul ignore next */ []));
|
|
304
|
+
#cols = computed(() => this.#rows.items()[0]?.cells().length ?? 0, /* @ts-ignore */
|
|
305
|
+
...(ngDevMode ? [{ debugName: "#cols" }] : /* istanbul ignore next */ []));
|
|
306
|
+
#firstEnabledCell = computed(() => firstEnabledHost(this.#flatCells()), /* @ts-ignore */
|
|
307
|
+
...(ngDevMode ? [{ debugName: "#firstEnabledCell" }] : /* istanbul ignore next */ []));
|
|
308
|
+
#registeredValues = computed(() => this.#rows
|
|
309
|
+
.items()
|
|
310
|
+
.map((row) => row.value())
|
|
311
|
+
.filter((v) => v !== undefined), /* @ts-ignore */
|
|
312
|
+
...(ngDevMode ? [{ debugName: "#registeredValues" }] : /* istanbul ignore next */ []));
|
|
313
|
+
#aggregateValues = computed(() => this.selectableValues() ?? this.#registeredValues(), /* @ts-ignore */
|
|
314
|
+
...(ngDevMode ? [{ debugName: "#aggregateValues" }] : /* istanbul ignore next */ []));
|
|
315
|
+
#selection = new TableRowSelection({
|
|
316
|
+
selection: this.selection,
|
|
317
|
+
selectionMode: this.selectionMode,
|
|
318
|
+
selectionBehavior: this.selectionBehavior,
|
|
319
|
+
compareWith: this.compareWith,
|
|
320
|
+
aggregateValues: this.#aggregateValues,
|
|
321
|
+
});
|
|
322
|
+
selectAllState = this.#selection.selectAllState;
|
|
323
|
+
#expansion = new TableExpansion({
|
|
324
|
+
expanded: this.expanded,
|
|
325
|
+
compareWith: this.compareWith,
|
|
326
|
+
});
|
|
327
|
+
#rowHierarchy = computed(() => computeFlatHierarchy(this.#rows.items().map((row) => row.level())), /* @ts-ignore */
|
|
328
|
+
...(ngDevMode ? [{ debugName: "#rowHierarchy" }] : /* istanbul ignore next */ []));
|
|
329
|
+
#rowOfCell(cellHost) {
|
|
330
|
+
return this.#rows.items().find((row) => row.cells().some((cell) => cell.host === cellHost));
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Absolute index of the row that owns the currently roving-focused cell, or `null`
|
|
334
|
+
* when no cell is focused (or the focused row carries no `virtualIndex`). Used by
|
|
335
|
+
* `[forTableVirtualized]` to keep the focused row mounted across recycling.
|
|
336
|
+
*/
|
|
337
|
+
focusedRowIndex = computed(() => {
|
|
338
|
+
const active = this.#roving.active();
|
|
339
|
+
if (active === null) {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
return this.#rowOfCell(active)?.virtualIndex() ?? null;
|
|
343
|
+
}, /* @ts-ignore */
|
|
344
|
+
...(ngDevMode ? [{ debugName: "focusedRowIndex" }] : /* istanbul ignore next */ []));
|
|
345
|
+
rowCountAttr = computed(() => this.mode() === 'table' ? null : (this.rowCount() ?? this.#rows.items().length), /* @ts-ignore */
|
|
346
|
+
...(ngDevMode ? [{ debugName: "rowCountAttr" }] : /* istanbul ignore next */ []));
|
|
347
|
+
colCountAttr = computed(() => this.mode() === 'table' ? null : (this.colCount() ?? this.#cols()), /* @ts-ignore */
|
|
348
|
+
...(ngDevMode ? [{ debugName: "colCountAttr" }] : /* istanbul ignore next */ []));
|
|
349
|
+
constructor() {
|
|
350
|
+
reconcileRovingActive(this.#roving, this.#flatCells);
|
|
351
|
+
}
|
|
352
|
+
registerHeaderRow(el) {
|
|
353
|
+
this.#headerRowEl.set(el);
|
|
354
|
+
}
|
|
355
|
+
setColumnWidth(column, width) {
|
|
356
|
+
this.#rootEl.style.setProperty(`--for-table-col-${column}-width`, `${width}px`);
|
|
357
|
+
}
|
|
358
|
+
isRowExpanded(value) {
|
|
359
|
+
return this.#expansion.isExpanded(value);
|
|
360
|
+
}
|
|
361
|
+
toggleRowExpansion(value) {
|
|
362
|
+
this.#expansion.toggle(value);
|
|
363
|
+
}
|
|
364
|
+
rowPosinset(host) {
|
|
365
|
+
const index = this.#rows.indexOfHost(host);
|
|
366
|
+
return index < 0 ? 1 : (this.#rowHierarchy()[index]?.posinset ?? 1);
|
|
367
|
+
}
|
|
368
|
+
rowSetsize(host) {
|
|
369
|
+
const index = this.#rows.indexOfHost(host);
|
|
370
|
+
return index < 0 ? 1 : (this.#rowHierarchy()[index]?.setsize ?? 1);
|
|
371
|
+
}
|
|
372
|
+
unregisterHeaderRow(el) {
|
|
373
|
+
if (this.#headerRowEl() === el) {
|
|
374
|
+
this.#headerRowEl.set(null);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
registerVirtualNavigation(navigation) {
|
|
378
|
+
this.#virtualNav.set(navigation);
|
|
379
|
+
}
|
|
380
|
+
setReorderingRow(index) {
|
|
381
|
+
this.#reorderingRow.set(index);
|
|
382
|
+
}
|
|
383
|
+
registerRow(handle) {
|
|
384
|
+
this.#rows.register(handle);
|
|
385
|
+
}
|
|
386
|
+
unregisterRow(handle) {
|
|
387
|
+
this.#rows.unregister(handle);
|
|
388
|
+
}
|
|
389
|
+
rowIndexOf(host) {
|
|
390
|
+
return this.#rows.indexOfHost(host);
|
|
391
|
+
}
|
|
392
|
+
cellTabIndex(host) {
|
|
393
|
+
if (this.#roving.hasActive()) {
|
|
394
|
+
return this.#roving.tabindexFor(host);
|
|
395
|
+
}
|
|
396
|
+
return this.#firstEnabledCell() === host ? 0 : -1;
|
|
397
|
+
}
|
|
398
|
+
isCellHighlighted(host) {
|
|
399
|
+
return this.#roving.active() === host;
|
|
400
|
+
}
|
|
401
|
+
activateCell(host) {
|
|
402
|
+
if (this.mode() !== 'table') {
|
|
403
|
+
this.#roving.setActive(host);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
isRowSelected(value) {
|
|
407
|
+
return this.#selection.isSelected(value);
|
|
408
|
+
}
|
|
409
|
+
toggleRowSelection(value) {
|
|
410
|
+
this.#selection.toggle(value);
|
|
411
|
+
}
|
|
412
|
+
selectRow(value, modifiers) {
|
|
413
|
+
this.#selection.select(value, modifiers);
|
|
414
|
+
}
|
|
415
|
+
toggleSelectAll() {
|
|
416
|
+
this.#selection.toggleSelectAll();
|
|
417
|
+
}
|
|
418
|
+
#rowValueOfCell(cellHost) {
|
|
419
|
+
for (const row of this.#rows.items()) {
|
|
420
|
+
if (row.cells().some((cell) => cell.host === cellHost)) {
|
|
421
|
+
return row.value();
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return undefined;
|
|
425
|
+
}
|
|
426
|
+
handleCellKeydown(event, host) {
|
|
427
|
+
if (this.mode() === 'table') {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
if (this.#handleSelectionKeydown(event, host)) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
if (this.#handleExpansionKeydown(event, host)) {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
this.#handleGridNavigationKeydown(event, host);
|
|
437
|
+
}
|
|
438
|
+
#handleSelectionKeydown(event, host) {
|
|
439
|
+
if (event.key !== ' ' || event.target !== host || this.selectionMode() === 'none') {
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
const value = this.#rowValueOfCell(host);
|
|
443
|
+
if (value === undefined) {
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
event.preventDefault();
|
|
447
|
+
this.#selection.toggle(value);
|
|
448
|
+
return true;
|
|
449
|
+
}
|
|
450
|
+
#handleExpansionKeydown(event, host) {
|
|
451
|
+
if (this.mode() !== 'treegrid') {
|
|
452
|
+
return false;
|
|
453
|
+
}
|
|
454
|
+
const intent = resolveTreegridExpandCollapse(event, this.dir());
|
|
455
|
+
if (intent === null) {
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
const row = this.#rowOfCell(host);
|
|
459
|
+
if (!row?.expandable()) {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
const value = row.value();
|
|
463
|
+
const open = this.#expansion.isExpanded(value);
|
|
464
|
+
if (intent === 'expand' && !open) {
|
|
465
|
+
event.preventDefault();
|
|
466
|
+
this.#expansion.setExpanded(value, true);
|
|
467
|
+
return true;
|
|
468
|
+
}
|
|
469
|
+
if (intent === 'collapse' && open) {
|
|
470
|
+
event.preventDefault();
|
|
471
|
+
this.#expansion.setExpanded(value, false);
|
|
472
|
+
return true;
|
|
473
|
+
}
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
#handleGridNavigationKeydown(event, host) {
|
|
477
|
+
const cols = this.#cols();
|
|
478
|
+
const cells = this.#flatCells();
|
|
479
|
+
if (cols === 0 || cells.length === 0) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const action = resolveGridNavigation(event, {
|
|
483
|
+
cols,
|
|
484
|
+
dir: this.dir(),
|
|
485
|
+
pageKeys: true,
|
|
486
|
+
});
|
|
487
|
+
if (action === null) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
event.preventDefault();
|
|
491
|
+
const currentIndex = cells.findIndex((cell) => cell.host === host);
|
|
492
|
+
const navigation = this.#virtualNav();
|
|
493
|
+
const fromRow = this.focusedRowIndex();
|
|
494
|
+
const total = this.rowCount();
|
|
495
|
+
if (navigation !== null &&
|
|
496
|
+
total !== undefined &&
|
|
497
|
+
fromRow !== null &&
|
|
498
|
+
ROW_CROSSING_ACTIONS.has(action)) {
|
|
499
|
+
const col = currentIndex < 0 ? 0 : currentIndex % cols;
|
|
500
|
+
const target = resolveCrossWindowRowTarget(action, fromRow, col, total, cols);
|
|
501
|
+
if (target !== null) {
|
|
502
|
+
navigation.navigateTo(target.row, target.col);
|
|
503
|
+
}
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
const next = moveGridIndex(currentIndex < 0 ? 0 : currentIndex, cells.length, action, {
|
|
507
|
+
cols,
|
|
508
|
+
isDisabled: (i) => cells[i].disabled(),
|
|
509
|
+
});
|
|
510
|
+
if (next === null) {
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
this.#roving.focusActive(cells[next].host);
|
|
514
|
+
}
|
|
515
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTable, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
516
|
+
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 }, rowCount: { classPropertyName: "rowCount", 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 }, selection: { classPropertyName: "selection", publicName: "selection", 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: { selection: "selectionChange", 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 });
|
|
517
|
+
}
|
|
518
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTable, decorators: [{
|
|
519
|
+
type: Directive,
|
|
520
|
+
args: [{
|
|
521
|
+
selector: '[forTable]',
|
|
522
|
+
exportAs: 'forTable',
|
|
523
|
+
host: {
|
|
524
|
+
'[attr.role]': 'mode()',
|
|
525
|
+
'[attr.aria-label]': 'ariaLabel() || null',
|
|
526
|
+
'[attr.dir]': 'dir()',
|
|
527
|
+
'[attr.data-mode]': 'mode()',
|
|
528
|
+
'[style.--for-table-header-height.px]': 'headerSize()?.height ?? null',
|
|
529
|
+
'[attr.aria-rowcount]': 'rowCountAttr()',
|
|
530
|
+
'[attr.aria-colcount]': 'colCountAttr()',
|
|
531
|
+
'[attr.aria-multiselectable]': 'selectionMode() === "multiple" ? "true" : null',
|
|
532
|
+
},
|
|
533
|
+
providers: [{ provide: FOR_TABLE_CONTEXT, useExisting: ForTable }],
|
|
534
|
+
}]
|
|
535
|
+
}], 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 }] }], rowCount: [{ 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 }] }], selection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }, { type: i0.Output, args: ["selectionChange"] }], 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"] }] } });
|
|
536
|
+
/**
|
|
537
|
+
* Resolves the absolute `(row, 0-based column)` target for a row-crossing grid
|
|
538
|
+
* action against the true `total` row count. Arrow row-moves preserve the
|
|
539
|
+
* current column; `first` / `last` jump to the first / last cell of the whole
|
|
540
|
+
* grid. Returns `null` when the move would step past the dataset bounds.
|
|
541
|
+
*/
|
|
542
|
+
function resolveCrossWindowRowTarget(action, fromRow, col, total, cols) {
|
|
543
|
+
switch (action) {
|
|
544
|
+
case 'next-row':
|
|
545
|
+
return fromRow + 1 < total ? { row: fromRow + 1, col } : null;
|
|
546
|
+
case 'prev-row':
|
|
547
|
+
return fromRow - 1 >= 0 ? { row: fromRow - 1, col } : null;
|
|
548
|
+
case 'first':
|
|
549
|
+
return { row: 0, col: 0 };
|
|
550
|
+
case 'last':
|
|
551
|
+
return { row: total - 1, col: cols - 1 };
|
|
552
|
+
default:
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Marks the header row of the table (`role="row"`). Registers its host
|
|
559
|
+
* element with the root so `ForTable` can measure the header's height and
|
|
560
|
+
* publish `--for-table-header-height` for sticky-cell CSS.
|
|
561
|
+
*/
|
|
562
|
+
class ForTableHeaderRow {
|
|
563
|
+
ctx = injectTableContext('ForTableHeaderRow');
|
|
564
|
+
#host = inject(ElementRef);
|
|
565
|
+
constructor() {
|
|
566
|
+
const el = this.#host.nativeElement;
|
|
567
|
+
this.ctx.registerHeaderRow(el);
|
|
568
|
+
inject(DestroyRef).onDestroy(() => this.ctx.unregisterHeaderRow(el));
|
|
569
|
+
}
|
|
570
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableHeaderRow, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
571
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForTableHeaderRow, isStandalone: true, selector: "[forTableHeaderRow]", host: { attributes: { "role": "row" } }, exportAs: ["forTableHeaderRow"], ngImport: i0 });
|
|
572
|
+
}
|
|
573
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableHeaderRow, decorators: [{
|
|
574
|
+
type: Directive,
|
|
575
|
+
args: [{
|
|
576
|
+
selector: '[forTableHeaderRow]',
|
|
577
|
+
exportAs: 'forTableHeaderRow',
|
|
578
|
+
host: {
|
|
579
|
+
role: 'row',
|
|
580
|
+
},
|
|
581
|
+
}]
|
|
582
|
+
}], ctorParameters: () => [] });
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Marks a data row (`role="row"`). Owns the registry of its data cells (for
|
|
586
|
+
* `aria-colindex`) and registers itself with the root so it joins the row index
|
|
587
|
+
* space (`aria-rowindex`, 1-based over data rows in `grid`/`treegrid` mode) and
|
|
588
|
+
* the 2D navigation grid.
|
|
589
|
+
*/
|
|
590
|
+
class ForTableRow {
|
|
591
|
+
ctx = injectTableContext('ForTableRow');
|
|
592
|
+
#host = inject(ElementRef).nativeElement;
|
|
593
|
+
#cells = new Collection();
|
|
594
|
+
/** This row's selection identity, written into the table's `[(selection)]`. Leave unset for non-selectable rows. */
|
|
595
|
+
value = input(/* @ts-ignore */
|
|
596
|
+
...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
|
|
597
|
+
/** 1-based tree depth for `aria-level` in `mode="treegrid"`. Ignored in other modes. */
|
|
598
|
+
level = input(1, { ...(ngDevMode ? { debugName: "level" } : /* istanbul ignore next */ {}), transform: numberAttribute });
|
|
599
|
+
/** Marks this row as an expandable parent — emits `aria-expanded` + `data-state`. */
|
|
600
|
+
expandable = input(false, { ...(ngDevMode ? { debugName: "expandable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
601
|
+
/**
|
|
602
|
+
* Absolute 0-based index of this row in the full virtualized dataset. Set by the
|
|
603
|
+
* consumer when rendering a window via `[forTableVirtualized]`; drives the absolute
|
|
604
|
+
* `aria-rowindex` and keeps the focused row mounted across recycling. Leave unset
|
|
605
|
+
* (default `null`) for non-virtualized tables. Ignored in `mode="table"`.
|
|
606
|
+
*/
|
|
607
|
+
virtualIndex = input(null, { ...(ngDevMode ? { debugName: "virtualIndex" } : /* istanbul ignore next */ {}), transform: (v) => (v == null ? null : numberAttribute(v)) });
|
|
608
|
+
rowIndex = computed(() => {
|
|
609
|
+
if (this.ctx.mode() === 'table') {
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
const vi = this.virtualIndex();
|
|
613
|
+
return vi !== null ? vi + 1 : this.ctx.rowIndexOf(this.#host) + 1;
|
|
614
|
+
}, /* @ts-ignore */
|
|
615
|
+
...(ngDevMode ? [{ debugName: "rowIndex" }] : /* istanbul ignore next */ []));
|
|
616
|
+
selectionMode = this.ctx.selectionMode;
|
|
617
|
+
selected = computed(() => {
|
|
618
|
+
const v = this.value();
|
|
619
|
+
return v !== undefined && this.ctx.isRowSelected(v);
|
|
620
|
+
}, /* @ts-ignore */
|
|
621
|
+
...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
|
|
622
|
+
ariaSelected = computed(() => this.ctx.selectionMode() === 'none' ? null : this.selected() ? 'true' : 'false', /* @ts-ignore */
|
|
623
|
+
...(ngDevMode ? [{ debugName: "ariaSelected" }] : /* istanbul ignore next */ []));
|
|
624
|
+
/** Whether this expandable row is currently open. False for non-expandable rows. */
|
|
625
|
+
expanded = computed(() => {
|
|
626
|
+
const v = this.value();
|
|
627
|
+
return v !== undefined && this.ctx.isRowExpanded(v);
|
|
628
|
+
}, /* @ts-ignore */
|
|
629
|
+
...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
|
|
630
|
+
/** Toggles this row's expansion. No-op when the row is not expandable or has no `[value]`. */
|
|
631
|
+
toggleExpanded() {
|
|
632
|
+
if (this.expandable()) {
|
|
633
|
+
this.ctx.toggleRowExpansion(this.value());
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
ariaLevel = computed(() => this.ctx.mode() === 'treegrid' ? this.level() : null, /* @ts-ignore */
|
|
637
|
+
...(ngDevMode ? [{ debugName: "ariaLevel" }] : /* istanbul ignore next */ []));
|
|
638
|
+
posinset = computed(() => this.ctx.mode() === 'treegrid' ? this.ctx.rowPosinset(this.#host) : null, /* @ts-ignore */
|
|
639
|
+
...(ngDevMode ? [{ debugName: "posinset" }] : /* istanbul ignore next */ []));
|
|
640
|
+
setsize = computed(() => this.ctx.mode() === 'treegrid' ? this.ctx.rowSetsize(this.#host) : null, /* @ts-ignore */
|
|
641
|
+
...(ngDevMode ? [{ debugName: "setsize" }] : /* istanbul ignore next */ []));
|
|
642
|
+
ariaExpanded = computed(() => this.ctx.mode() === 'treegrid' && this.expandable()
|
|
643
|
+
? this.expanded()
|
|
644
|
+
? 'true'
|
|
645
|
+
: 'false'
|
|
646
|
+
: null, /* @ts-ignore */
|
|
647
|
+
...(ngDevMode ? [{ debugName: "ariaExpanded" }] : /* istanbul ignore next */ []));
|
|
648
|
+
expandState = computed(() => this.ctx.mode() === 'treegrid' && this.expandable()
|
|
649
|
+
? this.expanded()
|
|
650
|
+
? 'open'
|
|
651
|
+
: 'closed'
|
|
652
|
+
: null, /* @ts-ignore */
|
|
653
|
+
...(ngDevMode ? [{ debugName: "expandState" }] : /* istanbul ignore next */ []));
|
|
654
|
+
constructor() {
|
|
655
|
+
const handle = {
|
|
656
|
+
host: this.#host,
|
|
657
|
+
cells: this.#cells.items,
|
|
658
|
+
value: this.value,
|
|
659
|
+
level: this.level,
|
|
660
|
+
expandable: this.expandable,
|
|
661
|
+
virtualIndex: this.virtualIndex,
|
|
662
|
+
};
|
|
663
|
+
registerHandle(handle, (h) => this.ctx.registerRow(h), (h) => this.ctx.unregisterRow(h));
|
|
664
|
+
}
|
|
665
|
+
registerCell(handle) {
|
|
666
|
+
this.#cells.register(handle);
|
|
667
|
+
}
|
|
668
|
+
unregisterCell(handle) {
|
|
669
|
+
this.#cells.unregister(handle);
|
|
670
|
+
}
|
|
671
|
+
cellIndexOf(host) {
|
|
672
|
+
return this.#cells.indexOfHost(host);
|
|
673
|
+
}
|
|
674
|
+
toggleSelected() {
|
|
675
|
+
const v = this.value();
|
|
676
|
+
if (v !== undefined) {
|
|
677
|
+
this.ctx.toggleRowSelection(v);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
onClick(event) {
|
|
681
|
+
const v = this.value();
|
|
682
|
+
if (this.ctx.selectionMode() === 'none' || v === undefined) {
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
this.ctx.selectRow(v, {
|
|
686
|
+
ctrlKey: event.ctrlKey,
|
|
687
|
+
metaKey: event.metaKey,
|
|
688
|
+
shiftKey: event.shiftKey,
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableRow, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
692
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForTableRow, isStandalone: true, selector: "[forTableRow]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, level: { classPropertyName: "level", publicName: "level", isSignal: true, isRequired: false, transformFunction: null }, expandable: { classPropertyName: "expandable", publicName: "expandable", isSignal: true, isRequired: false, transformFunction: null }, virtualIndex: { classPropertyName: "virtualIndex", publicName: "virtualIndex", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "row" }, listeners: { "click": "onClick($event)" }, properties: { "attr.aria-rowindex": "rowIndex()", "attr.aria-selected": "ariaSelected()", "attr.data-selected": "selected() ? \"\" : null", "attr.aria-level": "ariaLevel()", "attr.aria-posinset": "posinset()", "attr.aria-setsize": "setsize()", "attr.aria-expanded": "ariaExpanded()", "attr.data-state": "expandState()" } }, providers: [{ provide: FOR_TABLE_ROW_CONTEXT, useExisting: ForTableRow }], exportAs: ["forTableRow"], ngImport: i0 });
|
|
693
|
+
}
|
|
694
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableRow, decorators: [{
|
|
695
|
+
type: Directive,
|
|
696
|
+
args: [{
|
|
697
|
+
selector: '[forTableRow]',
|
|
698
|
+
exportAs: 'forTableRow',
|
|
699
|
+
host: {
|
|
700
|
+
role: 'row',
|
|
701
|
+
'[attr.aria-rowindex]': 'rowIndex()',
|
|
702
|
+
'[attr.aria-selected]': 'ariaSelected()',
|
|
703
|
+
'[attr.data-selected]': 'selected() ? "" : null',
|
|
704
|
+
'[attr.aria-level]': 'ariaLevel()',
|
|
705
|
+
'[attr.aria-posinset]': 'posinset()',
|
|
706
|
+
'[attr.aria-setsize]': 'setsize()',
|
|
707
|
+
'[attr.aria-expanded]': 'ariaExpanded()',
|
|
708
|
+
'[attr.data-state]': 'expandState()',
|
|
709
|
+
'(click)': 'onClick($event)',
|
|
710
|
+
},
|
|
711
|
+
providers: [{ provide: FOR_TABLE_ROW_CONTEXT, useExisting: ForTableRow }],
|
|
712
|
+
}]
|
|
713
|
+
}], 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 }] }] } });
|
|
714
|
+
|
|
715
|
+
/**
|
|
716
|
+
* Marks a header cell (`role="columnheader"`). Requires a `name` input that
|
|
717
|
+
* identifies the column — reflected as `data-column` for later phases (sort,
|
|
718
|
+
* resize, reorder) to key off. Optionally sticky via the `sticky` input.
|
|
719
|
+
*/
|
|
720
|
+
class ForTableHeaderCell {
|
|
721
|
+
ctx = injectTableContext('ForTableHeaderCell');
|
|
722
|
+
/** Column identifier, reflected as `data-column`. Required by later phases (sort, resize, reorder). */
|
|
723
|
+
name = input.required(/* @ts-ignore */
|
|
724
|
+
...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
|
|
725
|
+
/**
|
|
726
|
+
* Sticky placement for this header cell. `true` (or the bare `sticky`
|
|
727
|
+
* attribute) pins to the start edge; `'end'` pins to the end edge; `false`
|
|
728
|
+
* (default) is not sticky. The consumer applies `position: sticky` and the
|
|
729
|
+
* appropriate `top` / `left` / `right` offset in CSS — this input only
|
|
730
|
+
* provides the `data-sticky` hook.
|
|
731
|
+
*/
|
|
732
|
+
sticky = input(false, { ...(ngDevMode ? { debugName: "sticky" } : /* istanbul ignore next */ {}), transform: coerceSticky });
|
|
733
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableHeaderCell, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
734
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForTableHeaderCell, isStandalone: true, selector: "[forTableHeaderCell]", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, sticky: { classPropertyName: "sticky", publicName: "sticky", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "columnheader" }, properties: { "attr.data-column": "name()", "attr.data-sticky": "sticky() ? (sticky() === 'end' ? 'end' : '') : null" } }, exportAs: ["forTableHeaderCell"], ngImport: i0 });
|
|
735
|
+
}
|
|
736
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableHeaderCell, decorators: [{
|
|
737
|
+
type: Directive,
|
|
738
|
+
args: [{
|
|
739
|
+
selector: '[forTableHeaderCell]',
|
|
740
|
+
exportAs: 'forTableHeaderCell',
|
|
741
|
+
host: {
|
|
742
|
+
role: 'columnheader',
|
|
743
|
+
'[attr.data-column]': 'name()',
|
|
744
|
+
'[attr.data-sticky]': "sticky() ? (sticky() === 'end' ? 'end' : '') : null",
|
|
745
|
+
},
|
|
746
|
+
}]
|
|
747
|
+
}], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], sticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "sticky", required: false }] }] } });
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Marks a data cell. The `role` is derived from the root's `mode`: `'cell'` in
|
|
751
|
+
* `table` mode and `'gridcell'` in `grid` / `treegrid` mode. In grid / treegrid
|
|
752
|
+
* mode the cell is a roving-tabindex target — the single active cell carries
|
|
753
|
+
* `tabindex="0"` (others `-1`), reflects `data-highlighted` when focused, and
|
|
754
|
+
* carries a 1-based `aria-colindex`. Arrow / Home / End / Ctrl+Home / Ctrl+End /
|
|
755
|
+
* PageUp / PageDown move focus between cells. Requires a `name` input that
|
|
756
|
+
* identifies the column — reflected as `data-column`. Optionally sticky and
|
|
757
|
+
* optionally disabled (disabled cells are skipped during navigation).
|
|
758
|
+
*/
|
|
759
|
+
class ForTableCell {
|
|
760
|
+
ctx = injectTableContext('ForTableCell');
|
|
761
|
+
rowCtx = injectTableRowContext('ForTableCell');
|
|
762
|
+
#host = inject(ElementRef).nativeElement;
|
|
763
|
+
role = computed(() => (this.ctx.mode() === 'table' ? 'cell' : 'gridcell'), /* @ts-ignore */
|
|
764
|
+
...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
|
|
765
|
+
/** Column identifier, reflected as `data-column`. Required by later phases (sort, resize, reorder). */
|
|
766
|
+
name = input.required(/* @ts-ignore */
|
|
767
|
+
...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
|
|
768
|
+
/**
|
|
769
|
+
* Sticky placement for this data cell. `true` (or the bare `sticky` attribute)
|
|
770
|
+
* pins to the start edge; `'end'` pins to the end edge; `false` (default) is not
|
|
771
|
+
* sticky. The consumer applies `position: sticky` and the offsets in CSS — this
|
|
772
|
+
* input only provides the `data-sticky` hook.
|
|
773
|
+
*/
|
|
774
|
+
sticky = input(false, { ...(ngDevMode ? { debugName: "sticky" } : /* istanbul ignore next */ {}), transform: coerceSticky });
|
|
775
|
+
/**
|
|
776
|
+
* Whether this data cell is disabled. Disabled cells are skipped during arrow-key
|
|
777
|
+
* navigation, drop out of the tab order, and reflect `aria-disabled` / `data-disabled`.
|
|
778
|
+
* Only meaningful in `grid` / `treegrid` mode.
|
|
779
|
+
*/
|
|
780
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
781
|
+
tabindex = computed(() => {
|
|
782
|
+
if (this.ctx.mode() === 'table') {
|
|
783
|
+
return null;
|
|
784
|
+
}
|
|
785
|
+
if (this.disabled()) {
|
|
786
|
+
return -1;
|
|
787
|
+
}
|
|
788
|
+
return this.ctx.cellTabIndex(this.#host);
|
|
789
|
+
}, /* @ts-ignore */
|
|
790
|
+
...(ngDevMode ? [{ debugName: "tabindex" }] : /* istanbul ignore next */ []));
|
|
791
|
+
colIndex = computed(() => this.ctx.mode() === 'table' ? null : this.rowCtx.cellIndexOf(this.#host) + 1, /* @ts-ignore */
|
|
792
|
+
...(ngDevMode ? [{ debugName: "colIndex" }] : /* istanbul ignore next */ []));
|
|
793
|
+
highlighted = computed(() => this.ctx.mode() !== 'table' && this.ctx.isCellHighlighted(this.#host), /* @ts-ignore */
|
|
794
|
+
...(ngDevMode ? [{ debugName: "highlighted" }] : /* istanbul ignore next */ []));
|
|
795
|
+
constructor() {
|
|
796
|
+
const handle = { host: this.#host, disabled: this.disabled };
|
|
797
|
+
registerHandle(handle, (h) => this.rowCtx.registerCell(h), (h) => this.rowCtx.unregisterCell(h));
|
|
798
|
+
}
|
|
799
|
+
onFocus() {
|
|
800
|
+
this.ctx.activateCell(this.#host);
|
|
801
|
+
}
|
|
802
|
+
onKeyDown(event) {
|
|
803
|
+
this.ctx.handleCellKeydown(event, this.#host);
|
|
804
|
+
}
|
|
805
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableCell, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
806
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForTableCell, isStandalone: true, selector: "[forTableCell]", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, sticky: { classPropertyName: "sticky", publicName: "sticky", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "focus": "onFocus()", "keydown": "onKeyDown($event)" }, properties: { "attr.role": "role()", "attr.tabindex": "tabindex()", "attr.aria-colindex": "colIndex()", "attr.aria-disabled": "disabled() ? \"true\" : null", "attr.data-column": "name()", "attr.data-sticky": "sticky() ? (sticky() === 'end' ? 'end' : '') : null", "attr.data-highlighted": "highlighted() ? \"\" : null", "attr.data-disabled": "disabled() ? \"\" : null" } }, exportAs: ["forTableCell"], ngImport: i0 });
|
|
807
|
+
}
|
|
808
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableCell, decorators: [{
|
|
809
|
+
type: Directive,
|
|
810
|
+
args: [{
|
|
811
|
+
selector: '[forTableCell]',
|
|
812
|
+
exportAs: 'forTableCell',
|
|
813
|
+
host: {
|
|
814
|
+
'[attr.role]': 'role()',
|
|
815
|
+
'[attr.tabindex]': 'tabindex()',
|
|
816
|
+
'[attr.aria-colindex]': 'colIndex()',
|
|
817
|
+
'[attr.aria-disabled]': 'disabled() ? "true" : null',
|
|
818
|
+
'[attr.data-column]': 'name()',
|
|
819
|
+
'[attr.data-sticky]': "sticky() ? (sticky() === 'end' ? 'end' : '') : null",
|
|
820
|
+
'[attr.data-highlighted]': 'highlighted() ? "" : null',
|
|
821
|
+
'[attr.data-disabled]': 'disabled() ? "" : null',
|
|
822
|
+
'(focus)': 'onFocus()',
|
|
823
|
+
'(keydown)': 'onKeyDown($event)',
|
|
824
|
+
},
|
|
825
|
+
}]
|
|
826
|
+
}], 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 }] }] } });
|
|
827
|
+
|
|
828
|
+
/**
|
|
829
|
+
* Decorative per-row selection affordance inside a `[forTableRow]`. Clicking it
|
|
830
|
+
* toggles the row's selection; the enclosing row owns the announced
|
|
831
|
+
* `aria-selected`, so this element is `aria-hidden` and reflects
|
|
832
|
+
* `data-state="checked" | "unchecked"` for styling only. Place a glyph inside.
|
|
833
|
+
*/
|
|
834
|
+
class ForTableRowSelector {
|
|
835
|
+
row = injectTableRowContext('ForTableRowSelector');
|
|
836
|
+
dataState = computed(() => (this.row.selected() ? 'checked' : 'unchecked'), /* @ts-ignore */
|
|
837
|
+
...(ngDevMode ? [{ debugName: "dataState" }] : /* istanbul ignore next */ []));
|
|
838
|
+
onClick(event) {
|
|
839
|
+
event.stopPropagation();
|
|
840
|
+
this.row.toggleSelected();
|
|
841
|
+
}
|
|
842
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableRowSelector, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
843
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForTableRowSelector, isStandalone: true, selector: "[forTableRowSelector]", host: { attributes: { "aria-hidden": "true" }, listeners: { "click": "onClick($event)" }, properties: { "attr.data-state": "dataState()" } }, exportAs: ["forTableRowSelector"], ngImport: i0 });
|
|
844
|
+
}
|
|
845
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableRowSelector, decorators: [{
|
|
846
|
+
type: Directive,
|
|
847
|
+
args: [{
|
|
848
|
+
selector: '[forTableRowSelector]',
|
|
849
|
+
exportAs: 'forTableRowSelector',
|
|
850
|
+
host: {
|
|
851
|
+
'aria-hidden': 'true',
|
|
852
|
+
'[attr.data-state]': 'dataState()',
|
|
853
|
+
'(click)': 'onClick($event)',
|
|
854
|
+
},
|
|
855
|
+
}]
|
|
856
|
+
}] });
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Header "select all" checkbox for a `[forTable]` in `selectionMode="multiple"`.
|
|
860
|
+
* Reflects `aria-checked="true" | "false" | "mixed"` and
|
|
861
|
+
* `data-state="checked" | "unchecked" | "indeterminate"` derived from how many
|
|
862
|
+
* selectable rows are selected. Clicking (or Space / Enter) selects all rows
|
|
863
|
+
* when none/some are selected, and clears when all are. No-op outside multiple
|
|
864
|
+
* mode. Apply on a focusable element (e.g. a `<span>` you make tabbable, or a
|
|
865
|
+
* `<button type="button">`).
|
|
866
|
+
*/
|
|
867
|
+
class ForTableSelectAll {
|
|
868
|
+
ctx = injectTableContext('ForTableSelectAll');
|
|
869
|
+
/** Accessible label for the control (e.g. "Select all rows"). Truthy-only. */
|
|
870
|
+
ariaLabel = input(null, /* @ts-ignore */
|
|
871
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
872
|
+
ariaChecked = computed(() => {
|
|
873
|
+
const state = this.ctx.selectAllState();
|
|
874
|
+
return state === 'all' ? 'true' : state === 'some' ? 'mixed' : 'false';
|
|
875
|
+
}, /* @ts-ignore */
|
|
876
|
+
...(ngDevMode ? [{ debugName: "ariaChecked" }] : /* istanbul ignore next */ []));
|
|
877
|
+
dataState = computed(() => {
|
|
878
|
+
const state = this.ctx.selectAllState();
|
|
879
|
+
return state === 'all' ? 'checked' : state === 'some' ? 'indeterminate' : 'unchecked';
|
|
880
|
+
}, /* @ts-ignore */
|
|
881
|
+
...(ngDevMode ? [{ debugName: "dataState" }] : /* istanbul ignore next */ []));
|
|
882
|
+
onClick() {
|
|
883
|
+
this.ctx.toggleSelectAll();
|
|
884
|
+
}
|
|
885
|
+
onKeyDown(event) {
|
|
886
|
+
if (event.key === ' ' || event.key === 'Enter') {
|
|
887
|
+
event.preventDefault();
|
|
888
|
+
this.ctx.toggleSelectAll();
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableSelectAll, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
892
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForTableSelectAll, isStandalone: true, selector: "[forTableSelectAll]", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "checkbox", "tabindex": "0" }, listeners: { "click": "onClick()", "keydown": "onKeyDown($event)" }, properties: { "attr.aria-checked": "ariaChecked()", "attr.aria-label": "ariaLabel() || null", "attr.data-state": "dataState()" } }, exportAs: ["forTableSelectAll"], ngImport: i0 });
|
|
893
|
+
}
|
|
894
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableSelectAll, decorators: [{
|
|
895
|
+
type: Directive,
|
|
896
|
+
args: [{
|
|
897
|
+
selector: '[forTableSelectAll]',
|
|
898
|
+
exportAs: 'forTableSelectAll',
|
|
899
|
+
host: {
|
|
900
|
+
role: 'checkbox',
|
|
901
|
+
tabindex: '0',
|
|
902
|
+
'[attr.aria-checked]': 'ariaChecked()',
|
|
903
|
+
'[attr.aria-label]': 'ariaLabel() || null',
|
|
904
|
+
'[attr.data-state]': 'dataState()',
|
|
905
|
+
'(click)': 'onClick()',
|
|
906
|
+
'(keydown)': 'onKeyDown($event)',
|
|
907
|
+
},
|
|
908
|
+
}]
|
|
909
|
+
}], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* Turns a `[forTableHeaderCell]` into a sortable affordance that emits `aria-sort`
|
|
913
|
+
* and fires `sortChange` on activation (click, Enter, Space). The directive is
|
|
914
|
+
* **self-contained**: it owns only its own `direction` state and does NOT register
|
|
915
|
+
* with the table context or auto-reset sibling headers. The "one sorted column at a
|
|
916
|
+
* time" guarantee is the consumer's responsibility — hold a single sort descriptor
|
|
917
|
+
* signal and derive each header's `direction` from it. Apply this directive on the
|
|
918
|
+
* same element as `[forTableHeaderCell]`.
|
|
919
|
+
*
|
|
920
|
+
* When a `[forDraggable]` (column reorder) shares the same host cell, this directive
|
|
921
|
+
* yields its `tabindex` to the draggable's roving tab stop so the two never collide on
|
|
922
|
+
* the host attribute; `aria-sort` / `data-sorted` and click / keyboard activation stay
|
|
923
|
+
* on the cell.
|
|
924
|
+
*
|
|
925
|
+
* Cycle (default `firstClickDirection='ascending'`): `none → ascending → descending → none`.
|
|
926
|
+
* With `disableClear`: `none → ascending → descending → ascending`.
|
|
927
|
+
*
|
|
928
|
+
* `firstClickDirection='descending'` flips the entry pole, so a freshly activated
|
|
929
|
+
* column starts descending: `none → descending → ascending → none` (and with
|
|
930
|
+
* `disableClear`: `none → descending → ascending → descending`) — the descending-first
|
|
931
|
+
* behavior used by single-always-active sort descriptors.
|
|
932
|
+
*/
|
|
933
|
+
class ForTableSortHeader {
|
|
934
|
+
ctx = injectTableContext('ForTableSortHeader');
|
|
935
|
+
#draggable = inject(FOR_DRAGGABLE_CONTEXT, { self: true, optional: true });
|
|
936
|
+
/** Column identity included in the `sortChange` payload. */
|
|
937
|
+
column = input.required(/* @ts-ignore */
|
|
938
|
+
...(ngDevMode ? [{ debugName: "column" }] : /* istanbul ignore next */ []));
|
|
939
|
+
/**
|
|
940
|
+
* Current sort direction. Acts as both the controlled value and the initial value.
|
|
941
|
+
* Its implicit `directionChange` output fires on every internal update (via
|
|
942
|
+
* `[(direction)]`). `sortChange` is the primary column-aware event consumers bind:
|
|
943
|
+
* it carries a `{ column, direction }` descriptor and fires on every activation,
|
|
944
|
+
* regardless of whether the consumer uses two-way binding.
|
|
945
|
+
*/
|
|
946
|
+
direction = model('none', /* @ts-ignore */
|
|
947
|
+
...(ngDevMode ? [{ debugName: "direction" }] : /* istanbul ignore next */ []));
|
|
948
|
+
/**
|
|
949
|
+
* When `true`, the cycle skips the `'none'` step: `ascending → descending → ascending`.
|
|
950
|
+
* Useful when clearing the sort is not allowed.
|
|
951
|
+
*/
|
|
952
|
+
disableClear = input(false, { ...(ngDevMode ? { debugName: "disableClear" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
953
|
+
/**
|
|
954
|
+
* Direction a previously-unsorted column enters on its first activation (the
|
|
955
|
+
* `'none' → ?` step of the cycle). Defaults to `'ascending'`. Set to `'descending'`
|
|
956
|
+
* for descending-first columns. The toggle between the two sorted directions and the
|
|
957
|
+
* optional `'none'` step (`disableClear`) are unchanged.
|
|
958
|
+
*/
|
|
959
|
+
firstClickDirection = input('ascending', /* @ts-ignore */
|
|
960
|
+
...(ngDevMode ? [{ debugName: "firstClickDirection" }] : /* istanbul ignore next */ []));
|
|
961
|
+
/**
|
|
962
|
+
* When `false`, the header is fully inert: no `tabindex`, no `aria-sort`, and click /
|
|
963
|
+
* keyboard handlers are no-ops. Defaults to `true`.
|
|
964
|
+
*/
|
|
965
|
+
sortable = input(true, { ...(ngDevMode ? { debugName: "sortable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
966
|
+
/**
|
|
967
|
+
* Fires on every activation with the column identity and the new direction.
|
|
968
|
+
* Consumers bind this to update their own sort descriptor and reorder rows.
|
|
969
|
+
*/
|
|
970
|
+
sortChange = output();
|
|
971
|
+
/**
|
|
972
|
+
* Truthy-only `aria-sort` / `data-sorted` value: `'ascending'` or `'descending'`
|
|
973
|
+
* while sorted, `null` (absent) when `direction` is `'none'` or `sortable` is `false`.
|
|
974
|
+
*/
|
|
975
|
+
activeDirection = computed(() => this.sortable() && this.direction() !== 'none' ? this.direction() : null, /* @ts-ignore */
|
|
976
|
+
...(ngDevMode ? [{ debugName: "activeDirection" }] : /* istanbul ignore next */ []));
|
|
977
|
+
tabindex = computed(() => !this.#draggable && this.sortable() ? '0' : null, /* @ts-ignore */
|
|
978
|
+
...(ngDevMode ? [{ debugName: "tabindex" }] : /* istanbul ignore next */ []));
|
|
979
|
+
/** Activates the sort: computes the next direction, updates the model, and emits `sortChange`. */
|
|
980
|
+
activate() {
|
|
981
|
+
if (!this.sortable())
|
|
982
|
+
return;
|
|
983
|
+
const next = this.#next(this.direction());
|
|
984
|
+
this.direction.set(next);
|
|
985
|
+
this.sortChange.emit({ column: this.column(), direction: next });
|
|
986
|
+
}
|
|
987
|
+
/** Handles Enter and Space keyboard events, forwarding to `activate()`. */
|
|
988
|
+
onKeyDown(event) {
|
|
989
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
990
|
+
event.preventDefault();
|
|
991
|
+
this.activate();
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
#next(current) {
|
|
995
|
+
const first = this.firstClickDirection();
|
|
996
|
+
const second = first === 'ascending' ? 'descending' : 'ascending';
|
|
997
|
+
if (current === 'none')
|
|
998
|
+
return first;
|
|
999
|
+
if (current === first)
|
|
1000
|
+
return second;
|
|
1001
|
+
return this.disableClear() ? first : 'none';
|
|
1002
|
+
}
|
|
1003
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableSortHeader, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1004
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForTableSortHeader, isStandalone: true, selector: "[forTableSortHeader]", inputs: { column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, disableClear: { classPropertyName: "disableClear", publicName: "disableClear", isSignal: true, isRequired: false, transformFunction: null }, firstClickDirection: { classPropertyName: "firstClickDirection", publicName: "firstClickDirection", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { direction: "directionChange", sortChange: "sortChange" }, host: { listeners: { "click": "activate()", "keydown": "onKeyDown($event)" }, properties: { "attr.aria-sort": "activeDirection()", "attr.data-sorted": "activeDirection()", "attr.tabindex": "tabindex()" } }, exportAs: ["forTableSortHeader"], ngImport: i0 });
|
|
1005
|
+
}
|
|
1006
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableSortHeader, decorators: [{
|
|
1007
|
+
type: Directive,
|
|
1008
|
+
args: [{
|
|
1009
|
+
selector: '[forTableSortHeader]',
|
|
1010
|
+
exportAs: 'forTableSortHeader',
|
|
1011
|
+
host: {
|
|
1012
|
+
'[attr.aria-sort]': 'activeDirection()',
|
|
1013
|
+
'[attr.data-sorted]': 'activeDirection()',
|
|
1014
|
+
'[attr.tabindex]': 'tabindex()',
|
|
1015
|
+
'(click)': 'activate()',
|
|
1016
|
+
'(keydown)': 'onKeyDown($event)',
|
|
1017
|
+
},
|
|
1018
|
+
}]
|
|
1019
|
+
}], 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"] }] } });
|
|
1020
|
+
|
|
1021
|
+
/**
|
|
1022
|
+
* Turns a focusable element inside a `[forTableHeaderCell]` into a column-resize
|
|
1023
|
+
* handle. Supports pointer drag (with a dead-zone so a plain click is a no-op) and
|
|
1024
|
+
* `ArrowLeft` / `ArrowRight` keyboard resize, both constrained to `[min, max]`.
|
|
1025
|
+
*
|
|
1026
|
+
* On every change it publishes the resolved width as the CSS custom property
|
|
1027
|
+
* `--for-table-col-<column>-width` on the table root, so the consumer can apply it
|
|
1028
|
+
* to their layout (`grid-template-columns` in `<div>` mode, a `<col>` / cell width
|
|
1029
|
+
* in native `<table>` mode). **It never lays out columns itself and never resizes
|
|
1030
|
+
* data** — it owns the affordance, accessibility, and the published width only.
|
|
1031
|
+
*
|
|
1032
|
+
* Reflects `data-resizing` (empty string) while a pointer drag is active. Carries
|
|
1033
|
+
* `role="separator"` with `aria-orientation="vertical"` and live `aria-value*`,
|
|
1034
|
+
* mirroring `[forPaneResizer]`. The consumer supplies `aria-label`. Before the first
|
|
1035
|
+
* gesture, `aria-valuenow` falls back to the header-cell width measured once on mount
|
|
1036
|
+
* (browser-only), so a separator with no `[width]` is never announced without a
|
|
1037
|
+
* current value; an explicit `[width]` always takes precedence.
|
|
1038
|
+
*
|
|
1039
|
+
* @example
|
|
1040
|
+
* ```html
|
|
1041
|
+
* <th forTableHeaderCell name="name">
|
|
1042
|
+
* Name
|
|
1043
|
+
* <button forTableColumnResizer column="name" [(width)]="nameWidth"
|
|
1044
|
+
* aria-label="Resize Name column"></button>
|
|
1045
|
+
* </th>
|
|
1046
|
+
* ```
|
|
1047
|
+
*/
|
|
1048
|
+
class ForTableColumnResizer {
|
|
1049
|
+
ctx = injectTableContext('ForTableColumnResizer');
|
|
1050
|
+
#host = inject(ElementRef).nativeElement;
|
|
1051
|
+
/** Column identity; included in the `resizeCommit` payload and the published CSS var name. */
|
|
1052
|
+
column = input.required(/* @ts-ignore */
|
|
1053
|
+
...(ngDevMode ? [{ debugName: "column" }] : /* istanbul ignore next */ []));
|
|
1054
|
+
/**
|
|
1055
|
+
* Current column width in pixels. Two-way bindable via `[(width)]`. Acts as both
|
|
1056
|
+
* the controlled value and the base a pointer drag / arrow step is applied to.
|
|
1057
|
+
* When unset, the base for the first gesture is measured from the header cell.
|
|
1058
|
+
* Its implicit `widthChange` fires on every live update (drag tick, arrow press);
|
|
1059
|
+
* `resizeCommit` is the distinct column-aware gesture-end event.
|
|
1060
|
+
*/
|
|
1061
|
+
width = model(/* @ts-ignore */
|
|
1062
|
+
...(ngDevMode ? [undefined, { debugName: "width" }] : /* istanbul ignore next */ []));
|
|
1063
|
+
/** Minimum width in pixels. Default `0`. */
|
|
1064
|
+
min = input(0, /* @ts-ignore */
|
|
1065
|
+
...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
|
|
1066
|
+
/** Maximum width in pixels. Default `Infinity` (no upper bound). */
|
|
1067
|
+
max = input(Infinity, /* @ts-ignore */
|
|
1068
|
+
...(ngDevMode ? [{ debugName: "max" }] : /* istanbul ignore next */ []));
|
|
1069
|
+
/** Pixels applied per `ArrowLeft` / `ArrowRight` press. Default `10`. */
|
|
1070
|
+
step = input(10, /* @ts-ignore */
|
|
1071
|
+
...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
|
|
1072
|
+
/**
|
|
1073
|
+
* Fires once per resize gesture — at pointer-up after a drag, and on every arrow
|
|
1074
|
+
* press — with the column identity and its committed width. Bind it to persist
|
|
1075
|
+
* the width; live updates during a drag come through `[(width)]` / `widthChange`.
|
|
1076
|
+
*/
|
|
1077
|
+
resizeCommit = output();
|
|
1078
|
+
/** `aria-valuemax`, omitted when `max` is non-finite (the default unbounded case). */
|
|
1079
|
+
ariaValueMax = computed(() => Number.isFinite(this.max()) ? this.max() : null, /* @ts-ignore */
|
|
1080
|
+
...(ngDevMode ? [{ debugName: "ariaValueMax" }] : /* istanbul ignore next */ []));
|
|
1081
|
+
#resizing = signal(false, /* @ts-ignore */
|
|
1082
|
+
...(ngDevMode ? [{ debugName: "#resizing" }] : /* istanbul ignore next */ []));
|
|
1083
|
+
/** Whether a pointer drag is currently active (drives `data-resizing`). */
|
|
1084
|
+
resizing = this.#resizing.asReadonly();
|
|
1085
|
+
#measuredWidth = signal(null, /* @ts-ignore */
|
|
1086
|
+
...(ngDevMode ? [{ debugName: "#measuredWidth" }] : /* istanbul ignore next */ []));
|
|
1087
|
+
/**
|
|
1088
|
+
* Header-cell width measured once on mount (browser-only). Backs `aria-valuenow`
|
|
1089
|
+
* before any explicit `[width]` so the focusable separator never ships without a
|
|
1090
|
+
* current value on the measured-fallback path; an explicit `[width]` still wins.
|
|
1091
|
+
*/
|
|
1092
|
+
measuredWidth = this.#measuredWidth.asReadonly();
|
|
1093
|
+
#disposePointer = null;
|
|
1094
|
+
constructor() {
|
|
1095
|
+
inject(DestroyRef).onDestroy(() => this.#disposePointer?.());
|
|
1096
|
+
effect(() => {
|
|
1097
|
+
const w = this.width();
|
|
1098
|
+
if (w != null) {
|
|
1099
|
+
this.ctx.setColumnWidth(this.column(), w);
|
|
1100
|
+
}
|
|
1101
|
+
});
|
|
1102
|
+
if (isPlatformBrowser(inject(PLATFORM_ID))) {
|
|
1103
|
+
afterNextRender(() => this.#measuredWidth.set(this.#measureBaseWidth()));
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
onPointerDown(event) {
|
|
1107
|
+
if (event.button !== 0) {
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
event.preventDefault();
|
|
1111
|
+
event.stopPropagation();
|
|
1112
|
+
const ltr = this.ctx.dir() !== 'rtl';
|
|
1113
|
+
this.#disposePointer = startPointerResize(event, {
|
|
1114
|
+
host: this.#host,
|
|
1115
|
+
axis: 'x',
|
|
1116
|
+
startValue: this.width() ?? this.#measureBaseWidth(),
|
|
1117
|
+
invert: !ltr,
|
|
1118
|
+
constrain: (n) => clampToRange(n, this.min(), this.max()),
|
|
1119
|
+
onResize: (w) => {
|
|
1120
|
+
this.#resizing.set(true);
|
|
1121
|
+
this.width.set(w);
|
|
1122
|
+
},
|
|
1123
|
+
onCommit: (w) => {
|
|
1124
|
+
this.#resizing.set(false);
|
|
1125
|
+
this.resizeCommit.emit({ column: this.column(), width: w });
|
|
1126
|
+
},
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
onKeyDown(event) {
|
|
1130
|
+
const ltr = this.ctx.dir() !== 'rtl';
|
|
1131
|
+
const base = this.width() ?? this.#measureBaseWidth();
|
|
1132
|
+
let next;
|
|
1133
|
+
if (event.key === 'ArrowRight') {
|
|
1134
|
+
next = clampToRange(base + (ltr ? this.step() : -this.step()), this.min(), this.max());
|
|
1135
|
+
}
|
|
1136
|
+
else if (event.key === 'ArrowLeft') {
|
|
1137
|
+
next = clampToRange(base + (ltr ? -this.step() : this.step()), this.min(), this.max());
|
|
1138
|
+
}
|
|
1139
|
+
else {
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
event.preventDefault();
|
|
1143
|
+
if (next === this.width()) {
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
this.width.set(next);
|
|
1147
|
+
this.resizeCommit.emit({ column: this.column(), width: next });
|
|
1148
|
+
}
|
|
1149
|
+
onClick(event) {
|
|
1150
|
+
event.stopPropagation();
|
|
1151
|
+
}
|
|
1152
|
+
#measureBaseWidth() {
|
|
1153
|
+
const cell = this.#host.closest('[forTableHeaderCell]');
|
|
1154
|
+
return (cell ?? this.#host).getBoundingClientRect().width;
|
|
1155
|
+
}
|
|
1156
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableColumnResizer, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1157
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForTableColumnResizer, isStandalone: true, selector: "[forTableColumnResizer]", inputs: { column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { width: "widthChange", resizeCommit: "resizeCommit" }, host: { attributes: { "role": "separator", "aria-orientation": "vertical" }, listeners: { "pointerdown": "onPointerDown($event)", "keydown": "onKeyDown($event)", "click": "onClick($event)" }, properties: { "attr.tabindex": "\"0\"", "attr.aria-valuenow": "width() ?? measuredWidth() ?? null", "attr.aria-valuemin": "min()", "attr.aria-valuemax": "ariaValueMax()", "attr.data-resizing": "resizing() ? \"\" : null" } }, exportAs: ["forTableColumnResizer"], ngImport: i0 });
|
|
1158
|
+
}
|
|
1159
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableColumnResizer, decorators: [{
|
|
1160
|
+
type: Directive,
|
|
1161
|
+
args: [{
|
|
1162
|
+
selector: '[forTableColumnResizer]',
|
|
1163
|
+
exportAs: 'forTableColumnResizer',
|
|
1164
|
+
host: {
|
|
1165
|
+
role: 'separator',
|
|
1166
|
+
'aria-orientation': 'vertical',
|
|
1167
|
+
'[attr.tabindex]': '"0"',
|
|
1168
|
+
'[attr.aria-valuenow]': 'width() ?? measuredWidth() ?? null',
|
|
1169
|
+
'[attr.aria-valuemin]': 'min()',
|
|
1170
|
+
'[attr.aria-valuemax]': 'ariaValueMax()',
|
|
1171
|
+
'[attr.data-resizing]': 'resizing() ? "" : null',
|
|
1172
|
+
'(pointerdown)': 'onPointerDown($event)',
|
|
1173
|
+
'(keydown)': 'onKeyDown($event)',
|
|
1174
|
+
'(click)': 'onClick($event)',
|
|
1175
|
+
},
|
|
1176
|
+
}]
|
|
1177
|
+
}], ctorParameters: () => [], propDecorators: { column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }, { type: i0.Output, args: ["widthChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], resizeCommit: [{ type: i0.Output, args: ["resizeCommit"] }] } });
|
|
1178
|
+
|
|
1179
|
+
/**
|
|
1180
|
+
* Opt-in **column reordering** for `ForTable`, composed over the drag-drop primitive.
|
|
1181
|
+
*
|
|
1182
|
+
* Apply on the `[forTableHeaderRow]` element. It wraps `[forDropList]` (via
|
|
1183
|
+
* `hostDirectives`) so the header cells become a reorderable list, then translates
|
|
1184
|
+
* drag-drop's generic drop into the table-friendly `columnReorder` output. Mark each
|
|
1185
|
+
* `[forTableHeaderCell]` as `[forDraggable]` with `[dragData]` set to the column name.
|
|
1186
|
+
* On a committed drop (pointer or keyboard) it emits the previous / new index and the
|
|
1187
|
+
* new column-name order; the consumer applies it to their own column array. **It never
|
|
1188
|
+
* reorders columns itself** (BYO-data).
|
|
1189
|
+
*
|
|
1190
|
+
* The wrapped list defaults to `orientation="horizontal"` (a column reorder is always along
|
|
1191
|
+
* the row axis), so no `orientation` binding is needed. Bind `orientation="vertical"` to
|
|
1192
|
+
* override for the rare case.
|
|
1193
|
+
*
|
|
1194
|
+
* @example
|
|
1195
|
+
* ```html
|
|
1196
|
+
* <div forTableHeaderRow forTableColumnReorder (columnReorder)="columns.set($event.columns)">
|
|
1197
|
+
* @for (col of columns(); track col) {
|
|
1198
|
+
* <div forTableHeaderCell [name]="col" forDraggable [dragData]="col">{{ col }}</div>
|
|
1199
|
+
* }
|
|
1200
|
+
* </div>
|
|
1201
|
+
* ```
|
|
1202
|
+
*/
|
|
1203
|
+
class ForTableColumnReorder {
|
|
1204
|
+
ctx = injectTableContext('ForTableColumnReorder');
|
|
1205
|
+
#list = inject(ForDropList);
|
|
1206
|
+
/**
|
|
1207
|
+
* Fires once per committed reorder gesture (pointer drop or keyboard drop) with the
|
|
1208
|
+
* previous / new column index and the full column-name order after the move.
|
|
1209
|
+
*/
|
|
1210
|
+
columnReorder = output();
|
|
1211
|
+
constructor() {
|
|
1212
|
+
const sub = this.#list.dragDrop.subscribe((event) => this.#emit(event));
|
|
1213
|
+
inject(DestroyRef).onDestroy(() => sub.unsubscribe());
|
|
1214
|
+
}
|
|
1215
|
+
#emit(event) {
|
|
1216
|
+
const names = this.#list.items().map((handle) => String(handle.data()));
|
|
1217
|
+
const columns = moveItemInArray(names, event.previousIndex, event.currentIndex);
|
|
1218
|
+
this.columnReorder.emit({ from: event.previousIndex, to: event.currentIndex, columns });
|
|
1219
|
+
}
|
|
1220
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableColumnReorder, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1221
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForTableColumnReorder, isStandalone: true, selector: "[forTableColumnReorder]", outputs: { columnReorder: "columnReorder" }, providers: [{ provide: FOR_DROP_LIST_DEFAULT_ORIENTATION, useValue: 'horizontal' }], 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 });
|
|
1222
|
+
}
|
|
1223
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableColumnReorder, decorators: [{
|
|
1224
|
+
type: Directive,
|
|
1225
|
+
args: [{
|
|
1226
|
+
selector: '[forTableColumnReorder]',
|
|
1227
|
+
exportAs: 'forTableColumnReorder',
|
|
1228
|
+
providers: [{ provide: FOR_DROP_LIST_DEFAULT_ORIENTATION, useValue: 'horizontal' }],
|
|
1229
|
+
hostDirectives: [
|
|
1230
|
+
{
|
|
1231
|
+
directive: ForDropList,
|
|
1232
|
+
inputs: [
|
|
1233
|
+
'orientation',
|
|
1234
|
+
'dir',
|
|
1235
|
+
'disabled',
|
|
1236
|
+
'autoScroll',
|
|
1237
|
+
'animateReorder',
|
|
1238
|
+
'liveSort',
|
|
1239
|
+
'boundary',
|
|
1240
|
+
'lockAxis',
|
|
1241
|
+
],
|
|
1242
|
+
},
|
|
1243
|
+
],
|
|
1244
|
+
}]
|
|
1245
|
+
}], ctorParameters: () => [], propDecorators: { columnReorder: [{ type: i0.Output, args: ["columnReorder"] }] } });
|
|
1246
|
+
|
|
1247
|
+
/**
|
|
1248
|
+
* Translates a drop-list's window-relative `previousIndex` / `currentIndex` into
|
|
1249
|
+
* absolute dataset indices, so a virtualized table's consumer can apply
|
|
1250
|
+
* `moveItemInArray` over the **full** row array. `windowIndices` holds the
|
|
1251
|
+
* absolute `virtualIndex` of every rendered draggable row, in DOM (ascending)
|
|
1252
|
+
* order. Thin table-facing wrapper over the shared
|
|
1253
|
+
* {@link translateWindowReorder} helper, which owns the post-removal index math.
|
|
1254
|
+
*/
|
|
1255
|
+
function translateRowReorderIndices(windowIndices, previousIndex, currentIndex) {
|
|
1256
|
+
return translateWindowReorder(windowIndices, previousIndex, currentIndex);
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Opt-in **row reordering** for `ForTable`, composed over the drag-drop primitive.
|
|
1260
|
+
*
|
|
1261
|
+
* Apply on the rowgroup element that wraps the data rows (`<div role="rowgroup">` in
|
|
1262
|
+
* `<div>` mode, `<tbody>` in native `<table>` mode). It wraps `[forDropList]` (via
|
|
1263
|
+
* `hostDirectives`, vertical by default) so the rows become a reorderable list, then
|
|
1264
|
+
* translates drag-drop's generic drop into the table-friendly `rowReorder` output. Mark
|
|
1265
|
+
* each `[forTableRow]` as `[forDraggable]` with a `[dragData]`. On a committed drop it
|
|
1266
|
+
* emits the previous / new index; the consumer applies the move to their own row array
|
|
1267
|
+
* (e.g. `moveItemInArray`). **It never reorders rows itself** (BYO-data).
|
|
1268
|
+
*
|
|
1269
|
+
* Under `[forTableVirtualized]`, `rowReorder` emits **absolute** dataset indices
|
|
1270
|
+
* (derived from each rendered row's `virtualIndex`) so `moveItemInArray` over the full
|
|
1271
|
+
* array moves the right row. Pointer drag works within the rendered window and reaches
|
|
1272
|
+
* rows beyond it via auto-scroll (the lifted row is kept mounted for the drag); keyboard
|
|
1273
|
+
* reorder steps the target across the entire dataset (scrolling unmounted rows into view).
|
|
1274
|
+
* A non-virtualized table emits rendered-order indices unchanged.
|
|
1275
|
+
*
|
|
1276
|
+
* @example
|
|
1277
|
+
* ```html
|
|
1278
|
+
* <div role="rowgroup" forTableRowReorder (rowReorder)="onReorder($event)">
|
|
1279
|
+
* @for (row of rows(); track row.id) {
|
|
1280
|
+
* <div forTableRow [value]="row.id" forDraggable [dragData]="row.id">…</div>
|
|
1281
|
+
* }
|
|
1282
|
+
* </div>
|
|
1283
|
+
* ```
|
|
1284
|
+
*/
|
|
1285
|
+
class ForTableRowReorder {
|
|
1286
|
+
ctx = injectTableContext('ForTableRowReorder');
|
|
1287
|
+
#list = inject(ForDropList);
|
|
1288
|
+
#host = inject(ElementRef).nativeElement;
|
|
1289
|
+
#document = inject(DOCUMENT);
|
|
1290
|
+
#isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
1291
|
+
#announcer = inject(LiveAnnouncer);
|
|
1292
|
+
#dragDefaults = inject(FOR_DRAG_DROP_DEFAULTS);
|
|
1293
|
+
#kbLiftedHost = null;
|
|
1294
|
+
#kbFrom = 0;
|
|
1295
|
+
#kbTarget = 0;
|
|
1296
|
+
/** Fires once per committed reorder gesture with the previous / new row index. */
|
|
1297
|
+
rowReorder = output();
|
|
1298
|
+
constructor() {
|
|
1299
|
+
const destroyRef = inject(DestroyRef);
|
|
1300
|
+
const sub = this.#list.dragDrop.subscribe((event) => this.rowReorder.emit(this.#resolveDescriptor(event)));
|
|
1301
|
+
destroyRef.onDestroy(() => sub.unsubscribe());
|
|
1302
|
+
if (this.#isBrowser) {
|
|
1303
|
+
const onPointerDown = (event) => this.#pinFromPointer(event);
|
|
1304
|
+
const onPointerEnd = () => this.ctx.setReorderingRow(null);
|
|
1305
|
+
this.#host.addEventListener('pointerdown', onPointerDown, { capture: true });
|
|
1306
|
+
this.#document.addEventListener('pointerup', onPointerEnd, { capture: true });
|
|
1307
|
+
this.#document.addEventListener('pointercancel', onPointerEnd, { capture: true });
|
|
1308
|
+
createKeyboardDragMediator({
|
|
1309
|
+
host: this.#host,
|
|
1310
|
+
isBrowser: this.#isBrowser,
|
|
1311
|
+
destroyRef,
|
|
1312
|
+
isLifted: () => this.#kbLiftedHost !== null,
|
|
1313
|
+
onIdleKeydown: (event) => this.#onIdleKeydown(event),
|
|
1314
|
+
onLiftedKeydown: (event) => this.#onLiftedKeydown(event),
|
|
1315
|
+
onFocusOut: (event) => {
|
|
1316
|
+
if (this.#kbLiftedHost !== null && event.target === this.#kbLiftedHost) {
|
|
1317
|
+
this.#kbCancel();
|
|
1318
|
+
}
|
|
1319
|
+
},
|
|
1320
|
+
});
|
|
1321
|
+
destroyRef.onDestroy(() => {
|
|
1322
|
+
this.#host.removeEventListener('pointerdown', onPointerDown, { capture: true });
|
|
1323
|
+
this.#document.removeEventListener('pointerup', onPointerEnd, { capture: true });
|
|
1324
|
+
this.#document.removeEventListener('pointercancel', onPointerEnd, { capture: true });
|
|
1325
|
+
if (this.#kbLiftedHost !== null) {
|
|
1326
|
+
this.#kbCancel();
|
|
1327
|
+
}
|
|
1328
|
+
this.ctx.setReorderingRow(null);
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
#onLiftedKeydown(event) {
|
|
1333
|
+
if (this.ctx.virtualRowNavigation() === null) {
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
const key = event.key;
|
|
1337
|
+
if (key === ' ' || key === 'Enter') {
|
|
1338
|
+
event.preventDefault();
|
|
1339
|
+
event.stopPropagation();
|
|
1340
|
+
this.#kbCommit();
|
|
1341
|
+
}
|
|
1342
|
+
else if (key === 'Escape') {
|
|
1343
|
+
event.preventDefault();
|
|
1344
|
+
event.stopPropagation();
|
|
1345
|
+
this.#kbCancel();
|
|
1346
|
+
}
|
|
1347
|
+
else if (key === 'ArrowDown') {
|
|
1348
|
+
this.#setTarget(this.#kbTarget + 1);
|
|
1349
|
+
event.preventDefault();
|
|
1350
|
+
event.stopPropagation();
|
|
1351
|
+
this.#kbApplyTarget();
|
|
1352
|
+
}
|
|
1353
|
+
else if (key === 'ArrowUp') {
|
|
1354
|
+
this.#setTarget(this.#kbTarget - 1);
|
|
1355
|
+
event.preventDefault();
|
|
1356
|
+
event.stopPropagation();
|
|
1357
|
+
this.#kbApplyTarget();
|
|
1358
|
+
}
|
|
1359
|
+
else if (key === 'Home') {
|
|
1360
|
+
this.#setTarget(0);
|
|
1361
|
+
event.preventDefault();
|
|
1362
|
+
event.stopPropagation();
|
|
1363
|
+
this.#kbApplyTarget();
|
|
1364
|
+
}
|
|
1365
|
+
else if (key === 'End') {
|
|
1366
|
+
this.#setTarget(this.#count() - 1);
|
|
1367
|
+
event.preventDefault();
|
|
1368
|
+
event.stopPropagation();
|
|
1369
|
+
this.#kbApplyTarget();
|
|
1370
|
+
}
|
|
1371
|
+
else if (key === 'PageDown') {
|
|
1372
|
+
this.#setTarget(this.#kbTarget + this.#page());
|
|
1373
|
+
event.preventDefault();
|
|
1374
|
+
event.stopPropagation();
|
|
1375
|
+
this.#kbApplyTarget();
|
|
1376
|
+
}
|
|
1377
|
+
else if (key === 'PageUp') {
|
|
1378
|
+
this.#setTarget(this.#kbTarget - this.#page());
|
|
1379
|
+
event.preventDefault();
|
|
1380
|
+
event.stopPropagation();
|
|
1381
|
+
this.#kbApplyTarget();
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
#onIdleKeydown(event) {
|
|
1385
|
+
if (this.ctx.virtualRowNavigation() === null) {
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
const key = event.key;
|
|
1389
|
+
if (key !== ' ' && key !== 'Enter') {
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
1392
|
+
const draggable = this.#list.items().find((h) => h.host === event.target);
|
|
1393
|
+
if (draggable === undefined || draggable.disabled()) {
|
|
1394
|
+
return;
|
|
1395
|
+
}
|
|
1396
|
+
const handle = this.ctx.rows().find((r) => r.host === event.target);
|
|
1397
|
+
const vi = handle?.virtualIndex() ?? null;
|
|
1398
|
+
if (vi === null) {
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
event.preventDefault();
|
|
1402
|
+
event.stopPropagation();
|
|
1403
|
+
this.#kbLift(event.target, vi);
|
|
1404
|
+
}
|
|
1405
|
+
#kbLift(host, vi) {
|
|
1406
|
+
this.#kbLiftedHost = host;
|
|
1407
|
+
this.#kbFrom = vi;
|
|
1408
|
+
this.#kbTarget = vi;
|
|
1409
|
+
this.ctx.setReorderingRow(vi);
|
|
1410
|
+
const total = this.#count();
|
|
1411
|
+
this.#announcer.announce(this.#dragDefaults.announceLift(this.#label(), vi + 1, total), 'assertive');
|
|
1412
|
+
}
|
|
1413
|
+
#kbApplyTarget() {
|
|
1414
|
+
this.ctx.virtualRowNavigation()?.scrollToRow(this.#kbTarget);
|
|
1415
|
+
this.#announcer.announce(this.#dragDefaults.announceMove(this.#label(), this.#kbTarget + 1, this.#count()), 'polite');
|
|
1416
|
+
}
|
|
1417
|
+
#kbCommit() {
|
|
1418
|
+
this.rowReorder.emit({ from: this.#kbFrom, to: this.#kbTarget });
|
|
1419
|
+
this.#announcer.announce(this.#dragDefaults.announceDrop(this.#label(), this.#kbTarget + 1, this.#count()), 'assertive');
|
|
1420
|
+
this.#kbTeardown();
|
|
1421
|
+
}
|
|
1422
|
+
#kbCancel() {
|
|
1423
|
+
this.#announcer.announce(this.#dragDefaults.announceCancel(this.#label()), 'assertive');
|
|
1424
|
+
this.#kbTeardown();
|
|
1425
|
+
}
|
|
1426
|
+
#kbTeardown() {
|
|
1427
|
+
this.#kbLiftedHost = null;
|
|
1428
|
+
this.#kbFrom = 0;
|
|
1429
|
+
this.#kbTarget = 0;
|
|
1430
|
+
this.ctx.setReorderingRow(null);
|
|
1431
|
+
}
|
|
1432
|
+
#count() {
|
|
1433
|
+
return this.ctx.rowCount() ?? this.#list.items().length;
|
|
1434
|
+
}
|
|
1435
|
+
#page() {
|
|
1436
|
+
return Math.max(1, this.#list.items().length);
|
|
1437
|
+
}
|
|
1438
|
+
#label() {
|
|
1439
|
+
return (this.#kbLiftedHost?.textContent ?? '').trim();
|
|
1440
|
+
}
|
|
1441
|
+
#setTarget(value) {
|
|
1442
|
+
this.#kbTarget = Math.max(0, Math.min(this.#count() - 1, value));
|
|
1443
|
+
}
|
|
1444
|
+
#pinFromPointer(event) {
|
|
1445
|
+
const target = event.target;
|
|
1446
|
+
if (!(target instanceof HTMLElement)) {
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
const rowHost = target.closest('[forTableRow]');
|
|
1450
|
+
if (rowHost === null) {
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
1453
|
+
const handle = this.ctx.rows().find((r) => r.host === rowHost);
|
|
1454
|
+
this.ctx.setReorderingRow(handle?.virtualIndex() ?? null);
|
|
1455
|
+
}
|
|
1456
|
+
#resolveDescriptor(event) {
|
|
1457
|
+
const fallback = {
|
|
1458
|
+
from: event.previousIndex,
|
|
1459
|
+
to: event.currentIndex,
|
|
1460
|
+
};
|
|
1461
|
+
if (event.container !== event.previousContainer) {
|
|
1462
|
+
return fallback;
|
|
1463
|
+
}
|
|
1464
|
+
const rowByHost = new Map(this.ctx.rows().map((r) => [r.host, r]));
|
|
1465
|
+
const windowIndices = [];
|
|
1466
|
+
for (const item of this.#list.items()) {
|
|
1467
|
+
const index = rowByHost.get(item.host)?.virtualIndex() ?? null;
|
|
1468
|
+
if (index === null) {
|
|
1469
|
+
return fallback;
|
|
1470
|
+
}
|
|
1471
|
+
windowIndices.push(index);
|
|
1472
|
+
}
|
|
1473
|
+
return translateRowReorderIndices(windowIndices, event.previousIndex, event.currentIndex);
|
|
1474
|
+
}
|
|
1475
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableRowReorder, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
1476
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForTableRowReorder, isStandalone: true, selector: "[forTableRowReorder]", outputs: { rowReorder: "rowReorder" }, exportAs: ["forTableRowReorder"], hostDirectives: [{ directive: i1.ForDropList, inputs: ["dir", "dir", "disabled", "disabled", "autoScroll", "autoScroll", "animateReorder", "animateReorder", "liveSort", "liveSort", "boundary", "boundary", "lockAxis", "lockAxis"] }], ngImport: i0 });
|
|
1477
|
+
}
|
|
1478
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForTableRowReorder, decorators: [{
|
|
1479
|
+
type: Directive,
|
|
1480
|
+
args: [{
|
|
1481
|
+
selector: '[forTableRowReorder]',
|
|
1482
|
+
exportAs: 'forTableRowReorder',
|
|
1483
|
+
hostDirectives: [
|
|
1484
|
+
{
|
|
1485
|
+
directive: ForDropList,
|
|
1486
|
+
inputs: [
|
|
1487
|
+
'dir',
|
|
1488
|
+
'disabled',
|
|
1489
|
+
'autoScroll',
|
|
1490
|
+
'animateReorder',
|
|
1491
|
+
'liveSort',
|
|
1492
|
+
'boundary',
|
|
1493
|
+
'lockAxis',
|
|
1494
|
+
],
|
|
1495
|
+
},
|
|
1496
|
+
],
|
|
1497
|
+
}]
|
|
1498
|
+
}], ctorParameters: () => [], propDecorators: { rowReorder: [{ type: i0.Output, args: ["rowReorder"] }] } });
|
|
1499
|
+
|
|
1500
|
+
const FALLBACK = {};
|
|
1501
|
+
const { token, provideDefaults } = createDefaults('FOR_TABLE_DEFAULTS', FALLBACK);
|
|
1502
|
+
/** Token holding the resolved table defaults for the current scope. */
|
|
1503
|
+
const FOR_TABLE_DEFAULTS = token;
|
|
1504
|
+
/**
|
|
1505
|
+
* Configures forty-cdk table defaults for this injector scope. Partial
|
|
1506
|
+
* overrides inherit unspecified keys from the parent scope (or library
|
|
1507
|
+
* defaults at the root).
|
|
1508
|
+
*/
|
|
1509
|
+
function provideForTableDefaults(defaults = {}) {
|
|
1510
|
+
return provideDefaults(defaults);
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
/**
|
|
1514
|
+
* Generated bundle index. Do not edit.
|
|
1515
|
+
*/
|
|
1516
|
+
|
|
1517
|
+
export { FOR_TABLE_CONTEXT, FOR_TABLE_DEFAULTS, ForTable, ForTableCell, ForTableColumnReorder, ForTableColumnResizer, ForTableHeaderCell, ForTableHeaderRow, ForTableRow, ForTableRowReorder, ForTableRowSelector, ForTableSelectAll, ForTableSortHeader, provideForTableDefaults };
|
|
1518
|
+
//# sourceMappingURL=forty-cdk-table.mjs.map
|