@thkl/agrid 0.1.7 → 0.1.10
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/README.md +169 -11
- package/fesm2022/thkl-agrid.mjs +1811 -167
- package/fesm2022/thkl-agrid.mjs.map +1 -1
- package/package.json +3 -3
- package/types/thkl-agrid.d.ts +601 -30
- package/types/thkl-agrid.d.ts.map +1 -1
package/fesm2022/thkl-agrid.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, output, signal, computed, viewChild, effect, ChangeDetectionStrategy, Component, linkedSignal, isWritableSignal,
|
|
2
|
+
import { input, output, signal, computed, viewChild, effect, ChangeDetectionStrategy, Component, inject, ElementRef, DestroyRef, afterNextRender, linkedSignal, isWritableSignal, forwardRef, Directive, HostListener } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/cdk/scrolling';
|
|
4
|
-
import { ScrollingModule } from '@angular/cdk/scrolling';
|
|
4
|
+
import { VIRTUAL_SCROLL_STRATEGY, ScrollingModule } from '@angular/cdk/scrolling';
|
|
5
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
6
|
+
import { Subject, distinctUntilChanged } from 'rxjs';
|
|
5
7
|
|
|
6
8
|
// Display resolution
|
|
7
9
|
// Matches YYYY-MM-DD with optional time component — strict enough to avoid false positives.
|
|
@@ -77,7 +79,11 @@ function getDisplayForField(col, raw, locale) {
|
|
|
77
79
|
}
|
|
78
80
|
/** Returns whether a virtual-scroll item represents a data row. */
|
|
79
81
|
function isDataRowItem(item) {
|
|
80
|
-
return typeof item === 'object' && item !== null && 'row' in item;
|
|
82
|
+
return typeof item === 'object' && item !== null && 'row' in item && !('detailFor' in item);
|
|
83
|
+
}
|
|
84
|
+
/** Returns whether a virtual-scroll item represents a master/detail panel row. */
|
|
85
|
+
function isDetailRowItem(item) {
|
|
86
|
+
return typeof item === 'object' && item !== null && 'detailFor' in item;
|
|
81
87
|
}
|
|
82
88
|
/** Returns whether a virtual-scroll item represents a group header. */
|
|
83
89
|
function isGroupHeaderItem(item) {
|
|
@@ -90,7 +96,15 @@ function isGroupHeaderItem(item) {
|
|
|
90
96
|
* additionally narrows to the indentation/expansion fields via the `level` discriminator.
|
|
91
97
|
*/
|
|
92
98
|
function isTreeRowItem(item) {
|
|
93
|
-
return
|
|
99
|
+
return isDataRowItem(item) && 'level' in item;
|
|
100
|
+
}
|
|
101
|
+
/** Returns whether a virtual-scroll item is a generated path-tree branch node. */
|
|
102
|
+
function isPathTreeNodeItem(item) {
|
|
103
|
+
return typeof item === 'object' && item !== null && 'pathNodeId' in item;
|
|
104
|
+
}
|
|
105
|
+
/** Returns whether a tree configuration derives its hierarchy from path segments. */
|
|
106
|
+
function isPathTreeConfig(config) {
|
|
107
|
+
return typeof config.getPath === 'function';
|
|
94
108
|
}
|
|
95
109
|
// Filtering
|
|
96
110
|
/** Apply text substring and value-set filters, returning the surviving row indices. */
|
|
@@ -107,17 +121,33 @@ function applyTextAndValueFilters(rows, indices, filters, colMap, locale) {
|
|
|
107
121
|
result = result.filter(i => allowed.has(String(rows[i][field] ?? '')));
|
|
108
122
|
}
|
|
109
123
|
if (filter.operator && filter.operand != null && filter.operand !== '') {
|
|
110
|
-
result = result.filter(i =>
|
|
124
|
+
result = result.filter(i => passesConditionFilter(col, rows[i][field], filter, locale));
|
|
111
125
|
}
|
|
112
126
|
}
|
|
113
127
|
return result;
|
|
114
128
|
}
|
|
115
129
|
/**
|
|
116
|
-
* Evaluate a
|
|
117
|
-
* Date columns compare epoch-millis; everything else compares as numbers.
|
|
118
|
-
* Rows whose value can't be parsed to the comparison type are excluded.
|
|
130
|
+
* Evaluate a text, number, or date condition for one cell value.
|
|
119
131
|
*/
|
|
120
|
-
function
|
|
132
|
+
function passesConditionFilter(col, raw, filter, locale) {
|
|
133
|
+
if (col?.type !== 'number' && col?.type !== 'date') {
|
|
134
|
+
const value = getDisplayForField(col, raw, locale).toLocaleLowerCase(locale);
|
|
135
|
+
const operand = String(filter.operand ?? '').toLocaleLowerCase(locale);
|
|
136
|
+
switch (filter.operator) {
|
|
137
|
+
case 'eq': return value === operand;
|
|
138
|
+
case 'neq': return value !== operand;
|
|
139
|
+
case 'startsWith': return value.startsWith(operand);
|
|
140
|
+
case 'endsWith': return value.endsWith(operand);
|
|
141
|
+
case 'includes': return value.includes(operand);
|
|
142
|
+
case 'notIncludes': return !value.includes(operand);
|
|
143
|
+
case 'like': {
|
|
144
|
+
const escaped = operand.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
145
|
+
const pattern = escaped.replace(/%/g, '.*').replace(/_/g, '.');
|
|
146
|
+
return new RegExp(`^${pattern}$`, 'u').test(value);
|
|
147
|
+
}
|
|
148
|
+
default: return true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
121
151
|
const isDate = col?.type === 'date' || looksLikeDate(raw);
|
|
122
152
|
const toNum = (v) => isDate
|
|
123
153
|
? (v instanceof Date ? v.getTime() : new Date(v).getTime())
|
|
@@ -164,18 +194,18 @@ function applySortToIndices(rows, indices, sortEntries, colMap, locale) {
|
|
|
164
194
|
if (sortEntries.length === 0)
|
|
165
195
|
return indices;
|
|
166
196
|
const collator = new Intl.Collator(locale, { numeric: true, sensitivity: 'base' });
|
|
167
|
-
const fields = sortEntries.map(([field, filter]) =>
|
|
168
|
-
field
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
position
|
|
175
|
-
|
|
197
|
+
const fields = sortEntries.map(([field, filter]) => {
|
|
198
|
+
const col = colMap.get(field);
|
|
199
|
+
const dateLike = new Uint8Array(indices.length);
|
|
200
|
+
const dateValues = new Float64Array(indices.length);
|
|
201
|
+
const numericValues = new Float64Array(indices.length);
|
|
202
|
+
numericValues.fill(Number.NaN);
|
|
203
|
+
const displayValues = new Array(indices.length);
|
|
204
|
+
for (let position = 0; position < indices.length; position++) {
|
|
205
|
+
const index = indices[position];
|
|
176
206
|
const raw = rows[index][field];
|
|
177
|
-
const
|
|
178
|
-
const dateValue =
|
|
207
|
+
const isDateLike = col?.type === 'date' || looksLikeDate(raw);
|
|
208
|
+
const dateValue = isDateLike
|
|
179
209
|
? raw instanceof Date ? raw.getTime() : new Date(raw).getTime()
|
|
180
210
|
: Number.NaN;
|
|
181
211
|
const numericValue = col?.type === 'number'
|
|
@@ -184,36 +214,42 @@ function applySortToIndices(rows, indices, sortEntries, colMap, locale) {
|
|
|
184
214
|
&& typeof raw === 'number'
|
|
185
215
|
&& Number.isFinite(raw)
|
|
186
216
|
? raw
|
|
187
|
-
:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
217
|
+
: Number.NaN;
|
|
218
|
+
dateLike[position] = isDateLike ? 1 : 0;
|
|
219
|
+
dateValues[position] = Number.isNaN(dateValue) ? -1 : dateValue;
|
|
220
|
+
numericValues[position] = numericValue;
|
|
221
|
+
displayValues[position] = isDateLike ? '' : getDisplayForField(col, raw, locale);
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
direction: filter.sort === 'desc' ? -1 : 1,
|
|
225
|
+
dateLike,
|
|
226
|
+
dateValues,
|
|
227
|
+
numericValues,
|
|
228
|
+
displayValues,
|
|
229
|
+
};
|
|
230
|
+
});
|
|
231
|
+
const positions = Array.from({ length: indices.length }, (_, position) => position);
|
|
232
|
+
positions.sort((a, b) => {
|
|
197
233
|
for (let fieldIndex = 0; fieldIndex < fields.length; fieldIndex++) {
|
|
198
|
-
const
|
|
199
|
-
const keyB = b.keys[fieldIndex];
|
|
234
|
+
const field = fields[fieldIndex];
|
|
200
235
|
let comparison;
|
|
201
|
-
if (
|
|
202
|
-
comparison =
|
|
236
|
+
if (field.dateLike[a] || field.dateLike[b]) {
|
|
237
|
+
comparison = field.dateValues[a] - field.dateValues[b];
|
|
203
238
|
}
|
|
204
|
-
else if (
|
|
205
|
-
|
|
239
|
+
else if (!Number.isNaN(field.numericValues[a])
|
|
240
|
+
&& !Number.isNaN(field.numericValues[b])) {
|
|
241
|
+
comparison = field.numericValues[a] - field.numericValues[b];
|
|
206
242
|
}
|
|
207
243
|
else {
|
|
208
|
-
comparison = collator.compare(
|
|
244
|
+
comparison = collator.compare(field.displayValues[a], field.displayValues[b]);
|
|
209
245
|
}
|
|
210
246
|
if (comparison !== 0) {
|
|
211
|
-
return comparison *
|
|
247
|
+
return comparison * field.direction;
|
|
212
248
|
}
|
|
213
249
|
}
|
|
214
|
-
return a
|
|
250
|
+
return a - b;
|
|
215
251
|
});
|
|
216
|
-
return
|
|
252
|
+
return positions.map(position => indices[position]);
|
|
217
253
|
}
|
|
218
254
|
/**
|
|
219
255
|
* Compute aggregate values for the given rows across every column that has a static
|
|
@@ -226,29 +262,45 @@ function computeAggregates(rows, indices, cols, controlAggregates) {
|
|
|
226
262
|
const aggregate = controlAggregates[col.field] ?? col.aggregate;
|
|
227
263
|
if (!aggregate)
|
|
228
264
|
continue;
|
|
229
|
-
const values = indices.map(index => rows[index][col.field]);
|
|
230
265
|
if (typeof aggregate === 'function') {
|
|
266
|
+
const values = indices.map(index => rows[index][col.field]);
|
|
231
267
|
result[col.field] = aggregate(values);
|
|
232
268
|
continue;
|
|
233
269
|
}
|
|
234
|
-
|
|
270
|
+
let count = 0;
|
|
271
|
+
let numericCount = 0;
|
|
272
|
+
let sum = 0;
|
|
273
|
+
let min = Infinity;
|
|
274
|
+
let max = -Infinity;
|
|
275
|
+
for (const index of indices) {
|
|
276
|
+
const raw = rows[index][col.field];
|
|
277
|
+
if (raw != null && raw !== '')
|
|
278
|
+
count++;
|
|
279
|
+
const value = Number(raw);
|
|
280
|
+
if (Number.isNaN(value))
|
|
281
|
+
continue;
|
|
282
|
+
numericCount++;
|
|
283
|
+
sum += value;
|
|
284
|
+
if (value < min)
|
|
285
|
+
min = value;
|
|
286
|
+
if (value > max)
|
|
287
|
+
max = value;
|
|
288
|
+
}
|
|
235
289
|
switch (aggregate) {
|
|
236
290
|
case 'sum':
|
|
237
|
-
result[col.field] =
|
|
291
|
+
result[col.field] = sum;
|
|
238
292
|
break;
|
|
239
293
|
case 'avg':
|
|
240
|
-
result[col.field] =
|
|
241
|
-
? numbers.reduce((sum, value) => sum + value, 0) / numbers.length
|
|
242
|
-
: null;
|
|
294
|
+
result[col.field] = numericCount ? sum / numericCount : null;
|
|
243
295
|
break;
|
|
244
296
|
case 'min':
|
|
245
|
-
result[col.field] =
|
|
297
|
+
result[col.field] = numericCount ? min : null;
|
|
246
298
|
break;
|
|
247
299
|
case 'max':
|
|
248
|
-
result[col.field] =
|
|
300
|
+
result[col.field] = numericCount ? max : null;
|
|
249
301
|
break;
|
|
250
302
|
case 'count':
|
|
251
|
-
result[col.field] =
|
|
303
|
+
result[col.field] = count;
|
|
252
304
|
break;
|
|
253
305
|
}
|
|
254
306
|
}
|
|
@@ -344,6 +396,123 @@ function buildTreeItems(rows, indices, accessors, expandedIds, forceExpandedIds)
|
|
|
344
396
|
visit(rootIndex, 0);
|
|
345
397
|
return items;
|
|
346
398
|
}
|
|
399
|
+
/** Stable expansion id for a generated path prefix. */
|
|
400
|
+
function pathTreeNodeId(path) {
|
|
401
|
+
return `__agrid_path__${JSON.stringify(path.map(String))}`;
|
|
402
|
+
}
|
|
403
|
+
function pathTreeNodeUuid(path) {
|
|
404
|
+
const source = JSON.stringify(path.map(String));
|
|
405
|
+
let a = 0x9e3779b9;
|
|
406
|
+
let b = 0x85ebca6b;
|
|
407
|
+
let c = 0xc2b2ae35;
|
|
408
|
+
let d = 0x27d4eb2f;
|
|
409
|
+
for (let i = 0; i < source.length; i++) {
|
|
410
|
+
const code = source.charCodeAt(i);
|
|
411
|
+
a = Math.imul(a ^ code, 0x85ebca6b);
|
|
412
|
+
b = Math.imul(b ^ code, 0xc2b2ae35);
|
|
413
|
+
c = Math.imul(c ^ code, 0x27d4eb2f);
|
|
414
|
+
d = Math.imul(d ^ code, 0x165667b1);
|
|
415
|
+
}
|
|
416
|
+
const hex = [a, b, c, d]
|
|
417
|
+
.map(value => (value >>> 0).toString(16).padStart(8, '0'))
|
|
418
|
+
.join('')
|
|
419
|
+
.split('');
|
|
420
|
+
hex[12] = '5';
|
|
421
|
+
hex[16] = ((parseInt(hex[16], 16) & 0x3) | 0x8).toString(16);
|
|
422
|
+
return `${hex.slice(0, 8).join('')}-${hex.slice(8, 12).join('')}-${hex.slice(12, 16).join('')}-${hex.slice(16, 20).join('')}-${hex.slice(20, 32).join('')}`;
|
|
423
|
+
}
|
|
424
|
+
/** Builds display-only branch nodes and datasource-backed leaves from path segments. */
|
|
425
|
+
function buildPathTreeItems(rows, indices, config, expandedIds, forceExpanded = false) {
|
|
426
|
+
const roots = new Map();
|
|
427
|
+
for (const originalIndex of indices) {
|
|
428
|
+
const rawPath = config.getPath(rows[originalIndex]).filter(segment => String(segment).length > 0);
|
|
429
|
+
if (rawPath.length === 0)
|
|
430
|
+
continue;
|
|
431
|
+
const path = rawPath.map(String);
|
|
432
|
+
const formatSegment = (level, leaf) => config.formatPathSegment?.({
|
|
433
|
+
row: rows[originalIndex],
|
|
434
|
+
segment: rawPath[level],
|
|
435
|
+
level,
|
|
436
|
+
path: rawPath.slice(0, level + 1),
|
|
437
|
+
leaf,
|
|
438
|
+
}) ?? path[level];
|
|
439
|
+
const resolveNodeUuid = (prefix) => String(config.nodeUuid?.(rows[originalIndex])
|
|
440
|
+
?? config.nodeUUid?.(rows[originalIndex])
|
|
441
|
+
?? pathTreeNodeUuid(prefix));
|
|
442
|
+
let branches = roots;
|
|
443
|
+
for (let level = 0; level < path.length - 1; level++) {
|
|
444
|
+
const prefix = path.slice(0, level + 1);
|
|
445
|
+
const id = pathTreeNodeId(prefix);
|
|
446
|
+
let branch = branches.get(id);
|
|
447
|
+
if (!branch) {
|
|
448
|
+
branch = {
|
|
449
|
+
id,
|
|
450
|
+
uuid: resolveNodeUuid(prefix),
|
|
451
|
+
label: formatSegment(level, false),
|
|
452
|
+
level,
|
|
453
|
+
children: new Map(),
|
|
454
|
+
leaves: [],
|
|
455
|
+
};
|
|
456
|
+
branches.set(id, branch);
|
|
457
|
+
}
|
|
458
|
+
if (level === path.length - 2) {
|
|
459
|
+
branch.leaves.push({
|
|
460
|
+
originalIndex,
|
|
461
|
+
label: formatSegment(path.length - 1, true),
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
branches = branch.children;
|
|
465
|
+
}
|
|
466
|
+
if (path.length === 1) {
|
|
467
|
+
const id = pathTreeNodeId([]);
|
|
468
|
+
let branch = roots.get(id);
|
|
469
|
+
if (!branch) {
|
|
470
|
+
branch = {
|
|
471
|
+
id,
|
|
472
|
+
uuid: resolveNodeUuid([]),
|
|
473
|
+
label: '',
|
|
474
|
+
level: -1,
|
|
475
|
+
children: new Map(),
|
|
476
|
+
leaves: [],
|
|
477
|
+
};
|
|
478
|
+
roots.set(id, branch);
|
|
479
|
+
}
|
|
480
|
+
branch.leaves.push({ originalIndex, label: formatSegment(0, true) });
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const items = [];
|
|
484
|
+
const visit = (branch) => {
|
|
485
|
+
const isHiddenRoot = branch.level < 0;
|
|
486
|
+
const expanded = forceExpanded || expandedIds.has(branch.id);
|
|
487
|
+
if (!isHiddenRoot) {
|
|
488
|
+
items.push({
|
|
489
|
+
uuid: branch.uuid,
|
|
490
|
+
pathNodeId: branch.id,
|
|
491
|
+
pathLabel: branch.label,
|
|
492
|
+
level: branch.level,
|
|
493
|
+
expandable: true,
|
|
494
|
+
expanded,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
if (!isHiddenRoot && !expanded)
|
|
498
|
+
return;
|
|
499
|
+
for (const child of branch.children.values())
|
|
500
|
+
visit(child);
|
|
501
|
+
for (const leaf of branch.leaves) {
|
|
502
|
+
items.push({
|
|
503
|
+
row: rows[leaf.originalIndex],
|
|
504
|
+
originalIndex: leaf.originalIndex,
|
|
505
|
+
level: isHiddenRoot ? 0 : branch.level + 1,
|
|
506
|
+
expandable: false,
|
|
507
|
+
expanded: false,
|
|
508
|
+
treeLabel: leaf.label,
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
for (const branch of roots.values())
|
|
513
|
+
visit(branch);
|
|
514
|
+
return items;
|
|
515
|
+
}
|
|
347
516
|
// Selection range
|
|
348
517
|
/** Build the set of original indices spanning from `fromOrig` to `toOrig` in display order. */
|
|
349
518
|
function buildSelectionRange(fromOrig, toOrig, items) {
|
|
@@ -360,6 +529,11 @@ function buildSelectionRange(fromOrig, toOrig, items) {
|
|
|
360
529
|
}
|
|
361
530
|
return next;
|
|
362
531
|
}
|
|
532
|
+
/** Test a complete proposed editor value against a regex input mask. */
|
|
533
|
+
function matchesInputMask(value, mask) {
|
|
534
|
+
const flags = mask.flags.replace(/[gy]/g, '');
|
|
535
|
+
return new RegExp(`^(?:${mask.source})$`, flags).test(String(value ?? ''));
|
|
536
|
+
}
|
|
363
537
|
|
|
364
538
|
/**
|
|
365
539
|
* Individual cell component used inside `AgridComponent`.
|
|
@@ -378,6 +552,8 @@ class AgridCellComponent {
|
|
|
378
552
|
colIndex = input.required(...(ngDevMode ? [{ debugName: "colIndex" }] : /* istanbul ignore next */ []));
|
|
379
553
|
/** Current field value from the data source (displayed when not editing). */
|
|
380
554
|
value = input.required(...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
555
|
+
/** Optional display-only text override; editing still uses the original {@link value}. */
|
|
556
|
+
displayValueOverride = input(null, ...(ngDevMode ? [{ debugName: "displayValueOverride" }] : /* istanbul ignore next */ []));
|
|
381
557
|
/** Full row data — passed to `cellRenderer` when set. */
|
|
382
558
|
row = input({}, ...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
|
|
383
559
|
/** Locale used for built-in date formatting. */
|
|
@@ -388,6 +564,8 @@ class AgridCellComponent {
|
|
|
388
564
|
editing = input(false, ...(ngDevMode ? [{ debugName: "editing" }] : /* istanbul ignore next */ []));
|
|
389
565
|
/** Whether this cell may be edited (drives the boolean checkbox enabled state). */
|
|
390
566
|
editable = input(true, ...(ngDevMode ? [{ debugName: "editable" }] : /* istanbul ignore next */ []));
|
|
567
|
+
/** Whether a right-aligned information button is visible in this cell. */
|
|
568
|
+
showInfoIcon = input(false, ...(ngDevMode ? [{ debugName: "showInfoIcon" }] : /* istanbul ignore next */ []));
|
|
391
569
|
/** Validation error message to show under the editor, or `null` when the value is valid. */
|
|
392
570
|
error = input(null, ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
393
571
|
/** Whether this cell is the tree column and should render indentation + a twisty. */
|
|
@@ -407,6 +585,8 @@ class AgridCellComponent {
|
|
|
407
585
|
* while the cell is selected (type-to-start-editing behavior).
|
|
408
586
|
*/
|
|
409
587
|
seedChar = input('', ...(ngDevMode ? [{ debugName: "seedChar" }] : /* istanbul ignore next */ []));
|
|
588
|
+
/** Whether an unseeded text editor should select all text when focused. */
|
|
589
|
+
selectTextOnEdit = input(true, ...(ngDevMode ? [{ debugName: "selectTextOnEdit" }] : /* istanbul ignore next */ []));
|
|
410
590
|
/**
|
|
411
591
|
* Emitted on single click — the grid selects this cell.
|
|
412
592
|
* For `values` columns the grid also enters edit mode immediately.
|
|
@@ -416,6 +596,8 @@ class AgridCellComponent {
|
|
|
416
596
|
startEdit = output();
|
|
417
597
|
/** Emitted when a boolean-column checkbox is toggled, carrying the new value. */
|
|
418
598
|
booleanToggle = output();
|
|
599
|
+
/** Emitted when the cell's optional information button is clicked. */
|
|
600
|
+
infoClick = output();
|
|
419
601
|
/**
|
|
420
602
|
* Emitted on every keystroke inside the edit input or on every select change.
|
|
421
603
|
* The grid stores the latest value in `currentDraft` so it can commit on Tab / Enter.
|
|
@@ -426,10 +608,23 @@ class AgridCellComponent {
|
|
|
426
608
|
/** String value accepted by the active native input element. */
|
|
427
609
|
editorValue = computed(() => {
|
|
428
610
|
const draft = this.draft();
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
611
|
+
if (this.col().type === 'date') {
|
|
612
|
+
return getDateInputValue(draft);
|
|
613
|
+
}
|
|
614
|
+
return String(draft ?? '');
|
|
432
615
|
}, ...(ngDevMode ? [{ debugName: "editorValue" }] : /* istanbul ignore next */ []));
|
|
616
|
+
/** Mask selected for the current row and cell, if this is a maskable text column. */
|
|
617
|
+
resolvedInputMask = computed(() => {
|
|
618
|
+
const col = this.col();
|
|
619
|
+
if (!col.inputMask || col.type === 'number' || col.type === 'date' || col.type === 'boolean') {
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
return col.inputMask({
|
|
623
|
+
row: this.row(),
|
|
624
|
+
value: this.value(),
|
|
625
|
+
column: col,
|
|
626
|
+
}) ?? null;
|
|
627
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedInputMask" }] : /* istanbul ignore next */ []));
|
|
433
628
|
/** Whether this cell renders as an inline boolean checkbox (no edit mode). */
|
|
434
629
|
booleanCell = computed(() => this.col().type === 'boolean' && !this.col().cellRenderer, ...(ngDevMode ? [{ debugName: "booleanCell" }] : /* istanbul ignore next */ []));
|
|
435
630
|
/** Truthiness of the current boolean value (accepts `true`, `'true'`, `1`, `'1'`). */
|
|
@@ -458,19 +653,8 @@ class AgridCellComponent {
|
|
|
458
653
|
* Priority: ValueOption label → `ColDef.formatter` → raw string.
|
|
459
654
|
*/
|
|
460
655
|
displayValue = computed(() => {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
if (col.values?.length) {
|
|
464
|
-
const opt = col.values.find(v => typeof v === 'string' ? v === raw : v.value === raw);
|
|
465
|
-
if (opt !== undefined)
|
|
466
|
-
return typeof opt === 'string' ? opt : opt.label;
|
|
467
|
-
}
|
|
468
|
-
if (col.formatter)
|
|
469
|
-
return col.formatter(raw);
|
|
470
|
-
if (col.type === 'date' || looksLikeDate(raw)) {
|
|
471
|
-
return formatDateValue(raw, this.locale(), col.type === 'date');
|
|
472
|
-
}
|
|
473
|
-
return String(raw ?? '');
|
|
656
|
+
return this.displayValueOverride()
|
|
657
|
+
?? getDisplayForField(this.col(), this.value(), this.locale());
|
|
474
658
|
}, ...(ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []));
|
|
475
659
|
/**
|
|
476
660
|
* Index of the currently selected option in `valueOptions`.
|
|
@@ -493,16 +677,20 @@ class AgridCellComponent {
|
|
|
493
677
|
const initialValue = isDate
|
|
494
678
|
? dateInitial
|
|
495
679
|
: seed !== '' ? seed : this.value();
|
|
496
|
-
this.
|
|
680
|
+
const mask = this.resolvedInputMask();
|
|
681
|
+
const acceptedInitialValue = seed !== '' && mask && !matchesInputMask(initialValue, mask)
|
|
682
|
+
? this.value()
|
|
683
|
+
: initialValue;
|
|
684
|
+
this.draft.set(acceptedInitialValue);
|
|
497
685
|
setTimeout(() => {
|
|
498
686
|
const input = this.inputEl()?.nativeElement;
|
|
499
687
|
if (input) {
|
|
500
688
|
const displaySeed = isDate
|
|
501
689
|
? dateInitial
|
|
502
|
-
:
|
|
690
|
+
: String(acceptedInitialValue ?? '');
|
|
503
691
|
input.value = displaySeed;
|
|
504
692
|
input.focus();
|
|
505
|
-
if (input.type === 'text' && !seed)
|
|
693
|
+
if (input.type === 'text' && !seed && this.selectTextOnEdit())
|
|
506
694
|
input.select();
|
|
507
695
|
else if (input.type === 'text') {
|
|
508
696
|
const len = displaySeed.length;
|
|
@@ -540,9 +728,22 @@ class AgridCellComponent {
|
|
|
540
728
|
event.stopPropagation();
|
|
541
729
|
this.treeToggle.emit();
|
|
542
730
|
}
|
|
731
|
+
/** Emits the information action without activating or editing the cell. */
|
|
732
|
+
onInfoClick(event) {
|
|
733
|
+
event.preventDefault();
|
|
734
|
+
event.stopPropagation();
|
|
735
|
+
this.infoClick.emit();
|
|
736
|
+
}
|
|
543
737
|
/** Forward `<input>` changes to the grid. */
|
|
544
738
|
onInput(event) {
|
|
545
|
-
const
|
|
739
|
+
const input = event.target;
|
|
740
|
+
const val = input.value;
|
|
741
|
+
const mask = this.resolvedInputMask();
|
|
742
|
+
if (mask && !matchesInputMask(val, mask)) {
|
|
743
|
+
input.value = String(this.draft() ?? '');
|
|
744
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
546
747
|
const draft = this.col().type === 'date'
|
|
547
748
|
? coerceDateInputValue(val, this.value())
|
|
548
749
|
: val;
|
|
@@ -560,8 +761,8 @@ class AgridCellComponent {
|
|
|
560
761
|
this.draft.set(rawValue);
|
|
561
762
|
this.draftChange.emit(rawValue);
|
|
562
763
|
}
|
|
563
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
564
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
764
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
765
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AgridCellComponent, isStandalone: true, selector: "agrid-cell", inputs: { col: { classPropertyName: "col", publicName: "col", isSignal: true, isRequired: true, transformFunction: null }, rowIndex: { classPropertyName: "rowIndex", publicName: "rowIndex", isSignal: true, isRequired: true, transformFunction: null }, colIndex: { classPropertyName: "colIndex", publicName: "colIndex", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, displayValueOverride: { classPropertyName: "displayValueOverride", publicName: "displayValueOverride", isSignal: true, isRequired: false, transformFunction: null }, row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, editing: { classPropertyName: "editing", publicName: "editing", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, showInfoIcon: { classPropertyName: "showInfoIcon", publicName: "showInfoIcon", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, treeCell: { classPropertyName: "treeCell", publicName: "treeCell", isSignal: true, isRequired: false, transformFunction: null }, treeLevel: { classPropertyName: "treeLevel", publicName: "treeLevel", isSignal: true, isRequired: false, transformFunction: null }, treeExpandable: { classPropertyName: "treeExpandable", publicName: "treeExpandable", isSignal: true, isRequired: false, transformFunction: null }, treeExpanded: { classPropertyName: "treeExpanded", publicName: "treeExpanded", isSignal: true, isRequired: false, transformFunction: null }, seedChar: { classPropertyName: "seedChar", publicName: "seedChar", isSignal: true, isRequired: false, transformFunction: null }, selectTextOnEdit: { classPropertyName: "selectTextOnEdit", publicName: "selectTextOnEdit", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { treeToggle: "treeToggle", activate: "activate", startEdit: "startEdit", booleanToggle: "booleanToggle", infoClick: "infoClick", draftChange: "draftChange" }, host: { attributes: { "role": "gridcell", "tabindex": "-1" }, listeners: { "click": "activate.emit($event)", "dblclick": "startEdit.emit()" }, properties: { "class.selected": "selected()", "class.editing": "editing()", "class.ag-cell--tree": "treeCell()", "class.ag-cell--with-info": "showInfoIcon() && !editing()", "attr.aria-readonly": "!editable() ? \"true\" : null", "attr.title": "displayValue()" } }, viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["editInput"], descendants: true, isSignal: true }, { propertyName: "selectEl", first: true, predicate: ["editSelect"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
565
766
|
@if (booleanCell()) {
|
|
566
767
|
<input
|
|
567
768
|
type="checkbox"
|
|
@@ -618,16 +819,29 @@ class AgridCellComponent {
|
|
|
618
819
|
<span class="ag-cell-value">{{ displayValue() }}</span>
|
|
619
820
|
}
|
|
620
821
|
}
|
|
621
|
-
|
|
822
|
+
@if (showInfoIcon() && !editing()) {
|
|
823
|
+
<button
|
|
824
|
+
type="button"
|
|
825
|
+
class="ag-cell-info"
|
|
826
|
+
aria-label="More information"
|
|
827
|
+
title="More information"
|
|
828
|
+
(pointerdown)="$event.stopPropagation()"
|
|
829
|
+
(click)="onInfoClick($event)"
|
|
830
|
+
(dblclick)="$event.stopPropagation()"
|
|
831
|
+
>?</button>
|
|
832
|
+
}
|
|
833
|
+
`, isInline: true, styles: [":host{position:relative;display:block;border-right:1px solid var(--agrid-color-border);border-bottom:1px solid var(--agrid-color-border);overflow:hidden;white-space:nowrap;padding:0 6px;line-height:32px;cursor:default;-webkit-user-select:none;user-select:none;outline:none;box-sizing:border-box}:host(.ag-cell--changed):after{content:\"\";position:absolute;top:0;right:0;width:0;height:0;border-top:8px solid var(--agrid-color-cell-changed);border-left:8px solid transparent;pointer-events:none}:host(.selected){outline:2px solid var(--agrid-color-accent);outline-offset:-2px}:host(.ag-cell--range-selected){outline:2px solid var(--agrid-color-accent);outline-offset:-2px}:host(.editing){padding:0;background:var(--agrid-color-bg)}.ag-cell-value{display:block;overflow:hidden;text-overflow:ellipsis}:host(.ag-cell--tree){display:flex;align-items:center}:host(.ag-cell--tree) .ag-cell-value{flex:1 1 auto;min-width:0}:host(.ag-cell--with-info) .ag-cell-value{padding-right:22px}.ag-cell-info{position:absolute;top:50%;right:4px;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:1px solid var(--agrid-color-border);border-radius:50%;color:var(--agrid-color-text-muted);background:var(--agrid-color-bg);font:600 11px/1 sans-serif;cursor:pointer}.ag-cell-info:hover,.ag-cell-info:focus-visible{color:var(--agrid-color-accent);border-color:var(--agrid-color-accent);outline:none}.ag-tree-prefix{flex:0 0 auto;display:inline-flex;align-items:center;height:100%}.ag-tree-twisty,.ag-tree-twisty-spacer{width:16px;flex:0 0 16px}.ag-tree-twisty{display:inline-flex;align-items:center;justify-content:center;height:100%;padding:0;border:none;background:transparent;color:var(--agrid-color-text-muted);font-size:9px;line-height:1;cursor:pointer;transition:transform .15s ease}.ag-tree-twisty--expanded{transform:rotate(90deg)}.ag-cell-input{width:100%;height:100%;border:none;outline:none;font:inherit;padding:0 6px;box-sizing:border-box;background:transparent;user-select:text;-webkit-user-select:text}.ag-cell-select{width:100%;height:100%;border:none;outline:none;font:inherit;padding:0 2px;box-sizing:border-box;background:transparent;cursor:pointer}.ag-cell-checkbox{display:block;margin:9px auto;cursor:pointer;accent-color:var(--agrid-color-accent)}.ag-cell-checkbox:disabled{cursor:not-allowed;opacity:.6}.ag-cell-input--invalid{outline:2px solid var(--agrid-color-danger);outline-offset:-2px}.ag-cell-error{position:absolute;left:0;top:100%;z-index:5;max-width:240px;padding:3px 8px;font-size:12px;line-height:1.4;white-space:normal;color:#fff;background:var(--agrid-color-danger);border-radius:0 0 4px 4px;box-shadow:0 2px 6px #0000002e;pointer-events:none}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
622
834
|
}
|
|
623
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
835
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridCellComponent, decorators: [{
|
|
624
836
|
type: Component,
|
|
625
837
|
args: [{ selector: 'agrid-cell', changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
626
838
|
role: 'gridcell',
|
|
627
839
|
'[class.selected]': 'selected()',
|
|
628
840
|
'[class.editing]': 'editing()',
|
|
629
841
|
'[class.ag-cell--tree]': 'treeCell()',
|
|
630
|
-
'[
|
|
842
|
+
'[class.ag-cell--with-info]': 'showInfoIcon() && !editing()',
|
|
843
|
+
'[attr.aria-readonly]': '!editable() ? "true" : null',
|
|
844
|
+
'[attr.title]': 'displayValue()',
|
|
631
845
|
'(click)': 'activate.emit($event)',
|
|
632
846
|
'(dblclick)': 'startEdit.emit()',
|
|
633
847
|
tabindex: '-1',
|
|
@@ -688,8 +902,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
688
902
|
<span class="ag-cell-value">{{ displayValue() }}</span>
|
|
689
903
|
}
|
|
690
904
|
}
|
|
691
|
-
|
|
692
|
-
|
|
905
|
+
@if (showInfoIcon() && !editing()) {
|
|
906
|
+
<button
|
|
907
|
+
type="button"
|
|
908
|
+
class="ag-cell-info"
|
|
909
|
+
aria-label="More information"
|
|
910
|
+
title="More information"
|
|
911
|
+
(pointerdown)="$event.stopPropagation()"
|
|
912
|
+
(click)="onInfoClick($event)"
|
|
913
|
+
(dblclick)="$event.stopPropagation()"
|
|
914
|
+
>?</button>
|
|
915
|
+
}
|
|
916
|
+
`, styles: [":host{position:relative;display:block;border-right:1px solid var(--agrid-color-border);border-bottom:1px solid var(--agrid-color-border);overflow:hidden;white-space:nowrap;padding:0 6px;line-height:32px;cursor:default;-webkit-user-select:none;user-select:none;outline:none;box-sizing:border-box}:host(.ag-cell--changed):after{content:\"\";position:absolute;top:0;right:0;width:0;height:0;border-top:8px solid var(--agrid-color-cell-changed);border-left:8px solid transparent;pointer-events:none}:host(.selected){outline:2px solid var(--agrid-color-accent);outline-offset:-2px}:host(.ag-cell--range-selected){outline:2px solid var(--agrid-color-accent);outline-offset:-2px}:host(.editing){padding:0;background:var(--agrid-color-bg)}.ag-cell-value{display:block;overflow:hidden;text-overflow:ellipsis}:host(.ag-cell--tree){display:flex;align-items:center}:host(.ag-cell--tree) .ag-cell-value{flex:1 1 auto;min-width:0}:host(.ag-cell--with-info) .ag-cell-value{padding-right:22px}.ag-cell-info{position:absolute;top:50%;right:4px;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:1px solid var(--agrid-color-border);border-radius:50%;color:var(--agrid-color-text-muted);background:var(--agrid-color-bg);font:600 11px/1 sans-serif;cursor:pointer}.ag-cell-info:hover,.ag-cell-info:focus-visible{color:var(--agrid-color-accent);border-color:var(--agrid-color-accent);outline:none}.ag-tree-prefix{flex:0 0 auto;display:inline-flex;align-items:center;height:100%}.ag-tree-twisty,.ag-tree-twisty-spacer{width:16px;flex:0 0 16px}.ag-tree-twisty{display:inline-flex;align-items:center;justify-content:center;height:100%;padding:0;border:none;background:transparent;color:var(--agrid-color-text-muted);font-size:9px;line-height:1;cursor:pointer;transition:transform .15s ease}.ag-tree-twisty--expanded{transform:rotate(90deg)}.ag-cell-input{width:100%;height:100%;border:none;outline:none;font:inherit;padding:0 6px;box-sizing:border-box;background:transparent;user-select:text;-webkit-user-select:text}.ag-cell-select{width:100%;height:100%;border:none;outline:none;font:inherit;padding:0 2px;box-sizing:border-box;background:transparent;cursor:pointer}.ag-cell-checkbox{display:block;margin:9px auto;cursor:pointer;accent-color:var(--agrid-color-accent)}.ag-cell-checkbox:disabled{cursor:not-allowed;opacity:.6}.ag-cell-input--invalid{outline:2px solid var(--agrid-color-danger);outline-offset:-2px}.ag-cell-error{position:absolute;left:0;top:100%;z-index:5;max-width:240px;padding:3px 8px;font-size:12px;line-height:1.4;white-space:normal;color:#fff;background:var(--agrid-color-danger);border-radius:0 0 4px 4px;box-shadow:0 2px 6px #0000002e;pointer-events:none}\n"] }]
|
|
917
|
+
}], ctorParameters: () => [], propDecorators: { col: [{ type: i0.Input, args: [{ isSignal: true, alias: "col", required: true }] }], rowIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowIndex", required: true }] }], colIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "colIndex", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], displayValueOverride: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayValueOverride", required: false }] }], row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }], editing: [{ type: i0.Input, args: [{ isSignal: true, alias: "editing", required: false }] }], editable: [{ type: i0.Input, args: [{ isSignal: true, alias: "editable", required: false }] }], showInfoIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "showInfoIcon", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], treeCell: [{ type: i0.Input, args: [{ isSignal: true, alias: "treeCell", required: false }] }], treeLevel: [{ type: i0.Input, args: [{ isSignal: true, alias: "treeLevel", required: false }] }], treeExpandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "treeExpandable", required: false }] }], treeExpanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "treeExpanded", required: false }] }], treeToggle: [{ type: i0.Output, args: ["treeToggle"] }], seedChar: [{ type: i0.Input, args: [{ isSignal: true, alias: "seedChar", required: false }] }], selectTextOnEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectTextOnEdit", required: false }] }], activate: [{ type: i0.Output, args: ["activate"] }], startEdit: [{ type: i0.Output, args: ["startEdit"] }], booleanToggle: [{ type: i0.Output, args: ["booleanToggle"] }], infoClick: [{ type: i0.Output, args: ["infoClick"] }], draftChange: [{ type: i0.Output, args: ["draftChange"] }], inputEl: [{ type: i0.ViewChild, args: ['editInput', { isSignal: true }] }], selectEl: [{ type: i0.ViewChild, args: ['editSelect', { isSignal: true }] }] } });
|
|
693
918
|
|
|
694
919
|
/** Guarded access to browser-only APIs used by the grid. @internal */
|
|
695
920
|
class AgridBrowserAdapter {
|
|
@@ -734,6 +959,10 @@ class AgridBrowserAdapter {
|
|
|
734
959
|
viewportWidth() {
|
|
735
960
|
return this.win?.innerWidth ?? Number.POSITIVE_INFINITY;
|
|
736
961
|
}
|
|
962
|
+
/** Returns the viewport height or infinity during server rendering. */
|
|
963
|
+
viewportHeight() {
|
|
964
|
+
return this.win?.innerHeight ?? Number.POSITIVE_INFINITY;
|
|
965
|
+
}
|
|
737
966
|
/** Returns computed styles when a window is available. */
|
|
738
967
|
computedStyle(element) {
|
|
739
968
|
return this.win?.getComputedStyle?.(element) ?? null;
|
|
@@ -877,7 +1106,7 @@ class AgridClipboardHandler {
|
|
|
877
1106
|
for (let colOffset = 0; colOffset < rows[rowOffset].length; colOffset++) {
|
|
878
1107
|
const colIndex = start.colIndex + colOffset;
|
|
879
1108
|
const col = cols[colIndex];
|
|
880
|
-
if (!col || !this.opts.isCellEditable(col))
|
|
1109
|
+
if (!col || !this.opts.isCellEditable(col, item.originalIndex))
|
|
881
1110
|
continue;
|
|
882
1111
|
const oldValue = dataSource.getRow(item.originalIndex)[col.field];
|
|
883
1112
|
const newValue = this.coercePastedValue(rows[rowOffset][colOffset], col);
|
|
@@ -1155,8 +1384,11 @@ const AGRID_LOCALE_TEXT = {
|
|
|
1155
1384
|
columnMenu: 'Column menu',
|
|
1156
1385
|
columns: 'Columns',
|
|
1157
1386
|
detail: 'Detail',
|
|
1387
|
+
toggleDetail: 'Toggle detail panel',
|
|
1158
1388
|
hiddenColumn: '(hidden)',
|
|
1159
1389
|
filterCondition: 'Condition',
|
|
1390
|
+
filterConditionMenu: 'Filter condition',
|
|
1391
|
+
filterValue: 'Value',
|
|
1160
1392
|
filterNoCondition: 'No condition',
|
|
1161
1393
|
filterOpEquals: 'Equals',
|
|
1162
1394
|
filterOpNotEquals: 'Not equal',
|
|
@@ -1168,6 +1400,11 @@ const AGRID_LOCALE_TEXT = {
|
|
|
1168
1400
|
filterOpBefore: 'Before',
|
|
1169
1401
|
filterOpAfter: 'After',
|
|
1170
1402
|
filterOpOn: 'On',
|
|
1403
|
+
filterOpLike: 'Like (% and _ wildcards)',
|
|
1404
|
+
filterOpStartsWith: 'Starts with',
|
|
1405
|
+
filterOpEndsWith: 'Ends with',
|
|
1406
|
+
filterOpIncludes: 'Includes',
|
|
1407
|
+
filterOpNotIncludes: 'Does not include',
|
|
1171
1408
|
copyCellValue: 'Copy cell',
|
|
1172
1409
|
copyRow: 'Copy row',
|
|
1173
1410
|
confirmDeleteRow: 'Sure to delete?',
|
|
@@ -1191,6 +1428,9 @@ const AGRID_LOCALE_TEXT = {
|
|
|
1191
1428
|
pinColumn: 'Pin left',
|
|
1192
1429
|
pinColumnRight: 'Pin right',
|
|
1193
1430
|
unpinColumnRight: 'Unpin right',
|
|
1431
|
+
pinRowTop: 'Pin row to top',
|
|
1432
|
+
pinRowBottom: 'Pin row to bottom',
|
|
1433
|
+
unpinRow: 'Unpin row',
|
|
1194
1434
|
previous: 'Previous',
|
|
1195
1435
|
resizeColumn: 'Resize column',
|
|
1196
1436
|
rows: count => `${count} ${count === 1 ? 'row' : 'rows'}`,
|
|
@@ -1222,8 +1462,11 @@ const AGRID_LOCALE_TEXT = {
|
|
|
1222
1462
|
columnMenu: 'Spaltenmenü',
|
|
1223
1463
|
columns: 'Spalten',
|
|
1224
1464
|
detail: 'Details',
|
|
1465
|
+
toggleDetail: 'Detailbereich umschalten',
|
|
1225
1466
|
hiddenColumn: '(ausgeblendet)',
|
|
1226
1467
|
filterCondition: 'Bedingung',
|
|
1468
|
+
filterConditionMenu: 'Filterbedingung',
|
|
1469
|
+
filterValue: 'Wert',
|
|
1227
1470
|
filterNoCondition: 'Keine Bedingung',
|
|
1228
1471
|
filterOpEquals: 'Gleich',
|
|
1229
1472
|
filterOpNotEquals: 'Ungleich',
|
|
@@ -1235,6 +1478,11 @@ const AGRID_LOCALE_TEXT = {
|
|
|
1235
1478
|
filterOpBefore: 'Vor',
|
|
1236
1479
|
filterOpAfter: 'Nach',
|
|
1237
1480
|
filterOpOn: 'Am',
|
|
1481
|
+
filterOpLike: 'Wie (% und _ Platzhalter)',
|
|
1482
|
+
filterOpStartsWith: 'Beginnt mit',
|
|
1483
|
+
filterOpEndsWith: 'Endet mit',
|
|
1484
|
+
filterOpIncludes: 'Enthält',
|
|
1485
|
+
filterOpNotIncludes: 'Enthält nicht',
|
|
1238
1486
|
copyCellValue: 'Zelle kopieren',
|
|
1239
1487
|
copyRow: 'Zeile kopieren',
|
|
1240
1488
|
confirmDeleteRow: 'Wirklich löschen?',
|
|
@@ -1258,6 +1506,9 @@ const AGRID_LOCALE_TEXT = {
|
|
|
1258
1506
|
pinColumn: 'Links fixieren',
|
|
1259
1507
|
pinColumnRight: 'Rechts fixieren',
|
|
1260
1508
|
unpinColumnRight: 'Rechts lösen',
|
|
1509
|
+
pinRowTop: 'Zeile oben fixieren',
|
|
1510
|
+
pinRowBottom: 'Zeile unten fixieren',
|
|
1511
|
+
unpinRow: 'Zeilenfixierung lösen',
|
|
1261
1512
|
previous: 'Zurück',
|
|
1262
1513
|
resizeColumn: 'Spaltenbreite ändern',
|
|
1263
1514
|
rows: count => `${count} ${count === 1 ? 'Zeile' : 'Zeilen'}`,
|
|
@@ -1303,14 +1554,27 @@ function resolveAgridLocaleText(locale, localizations) {
|
|
|
1303
1554
|
return { ...base, ...overrides };
|
|
1304
1555
|
}
|
|
1305
1556
|
|
|
1557
|
+
/** Clamp a floating menu so it remains inside the browser viewport. @internal */
|
|
1558
|
+
function fitColumnMenuToViewport(x, y, width, height, viewportWidth, viewportHeight, margin = 8) {
|
|
1559
|
+
return {
|
|
1560
|
+
x: Math.max(margin, Math.min(x, viewportWidth - width - margin)),
|
|
1561
|
+
y: Math.max(margin, Math.min(y, viewportHeight - height - margin)),
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1306
1564
|
/** Floating column menu for sort, column actions, grouping, and value filters. */
|
|
1307
1565
|
class AgridColumnMenuComponent {
|
|
1566
|
+
elementRef = inject(ElementRef);
|
|
1567
|
+
destroyRef = inject(DestroyRef);
|
|
1568
|
+
browser = new AgridBrowserAdapter();
|
|
1569
|
+
resizeObserver = null;
|
|
1308
1570
|
/** Localized labels used in the menu. */
|
|
1309
1571
|
localeText = input(AGRID_LOCALE_TEXT.en, ...(ngDevMode ? [{ debugName: "localeText" }] : /* istanbul ignore next */ []));
|
|
1310
1572
|
/** Fixed viewport x-position for the menu. */
|
|
1311
1573
|
x = input.required(...(ngDevMode ? [{ debugName: "x" }] : /* istanbul ignore next */ []));
|
|
1312
1574
|
/** Fixed viewport y-position for the menu. */
|
|
1313
1575
|
y = input.required(...(ngDevMode ? [{ debugName: "y" }] : /* istanbul ignore next */ []));
|
|
1576
|
+
/** Viewport-fitted coordinates used by the rendered menu. */
|
|
1577
|
+
position = signal({ x: 0, y: 0 }, ...(ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []));
|
|
1314
1578
|
/** Header label for the active column. */
|
|
1315
1579
|
header = input.required(...(ngDevMode ? [{ debugName: "header" }] : /* istanbul ignore next */ []));
|
|
1316
1580
|
/** Current sort direction for the active column. */
|
|
@@ -1329,19 +1593,19 @@ class AgridColumnMenuComponent {
|
|
|
1329
1593
|
filterable = input(false, ...(ngDevMode ? [{ debugName: "filterable" }] : /* istanbul ignore next */ []));
|
|
1330
1594
|
/** Whether to show the Excel-style distinct-value picker. */
|
|
1331
1595
|
showValueFilter = input(true, ...(ngDevMode ? [{ debugName: "showValueFilter" }] : /* istanbul ignore next */ []));
|
|
1332
|
-
/**
|
|
1596
|
+
/** Condition-filter input type for the active column, or `null` to hide the condition UI. */
|
|
1333
1597
|
filterType = input(null, ...(ngDevMode ? [{ debugName: "filterType" }] : /* istanbul ignore next */ []));
|
|
1334
|
-
/** Current
|
|
1598
|
+
/** Current condition operator, or `null` when none is selected. */
|
|
1335
1599
|
operator = input(null, ...(ngDevMode ? [{ debugName: "operator" }] : /* istanbul ignore next */ []));
|
|
1336
|
-
/** Current primary
|
|
1600
|
+
/** Current primary condition operand. */
|
|
1337
1601
|
operand = input('', ...(ngDevMode ? [{ debugName: "operand" }] : /* istanbul ignore next */ []));
|
|
1338
|
-
/** Current secondary
|
|
1602
|
+
/** Current secondary condition operand (used by `between`). */
|
|
1339
1603
|
operand2 = input('', ...(ngDevMode ? [{ debugName: "operand2" }] : /* istanbul ignore next */ []));
|
|
1340
|
-
/** Emits the selected
|
|
1604
|
+
/** Emits the selected condition operator, or `null` to clear it. */
|
|
1341
1605
|
operatorChange = output();
|
|
1342
|
-
/** Emits the primary
|
|
1606
|
+
/** Emits the primary condition operand text. */
|
|
1343
1607
|
operandChange = output();
|
|
1344
|
-
/** Emits the secondary
|
|
1608
|
+
/** Emits the secondary condition operand text (used by `between`). */
|
|
1345
1609
|
operand2Change = output();
|
|
1346
1610
|
/** Current search text for the value-filter option list. */
|
|
1347
1611
|
search = input('', ...(ngDevMode ? [{ debugName: "search" }] : /* istanbul ignore next */ []));
|
|
@@ -1394,7 +1658,7 @@ class AgridColumnMenuComponent {
|
|
|
1394
1658
|
{ value: 'count', symbol: '#', label: t.aggregateCount },
|
|
1395
1659
|
];
|
|
1396
1660
|
}, ...(ngDevMode ? [{ debugName: "aggregateOptions" }] : /* istanbul ignore next */ []));
|
|
1397
|
-
/**
|
|
1661
|
+
/** Condition operators offered for the active column, labeled per column type. */
|
|
1398
1662
|
operatorOptions = computed(() => {
|
|
1399
1663
|
const t = this.localeText();
|
|
1400
1664
|
if (this.filterType() === 'date') {
|
|
@@ -1405,6 +1669,17 @@ class AgridColumnMenuComponent {
|
|
|
1405
1669
|
{ value: 'between', label: t.filterOpBetween },
|
|
1406
1670
|
];
|
|
1407
1671
|
}
|
|
1672
|
+
if (this.filterType() === 'text') {
|
|
1673
|
+
return [
|
|
1674
|
+
{ value: 'eq', label: t.filterOpEquals },
|
|
1675
|
+
{ value: 'neq', label: t.filterOpNotEquals },
|
|
1676
|
+
{ value: 'like', label: t.filterOpLike },
|
|
1677
|
+
{ value: 'startsWith', label: t.filterOpStartsWith },
|
|
1678
|
+
{ value: 'endsWith', label: t.filterOpEndsWith },
|
|
1679
|
+
{ value: 'includes', label: t.filterOpIncludes },
|
|
1680
|
+
{ value: 'notIncludes', label: t.filterOpNotIncludes },
|
|
1681
|
+
];
|
|
1682
|
+
}
|
|
1408
1683
|
return [
|
|
1409
1684
|
{ value: 'eq', label: t.filterOpEquals },
|
|
1410
1685
|
{ value: 'neq', label: t.filterOpNotEquals },
|
|
@@ -1416,14 +1691,42 @@ class AgridColumnMenuComponent {
|
|
|
1416
1691
|
];
|
|
1417
1692
|
}, ...(ngDevMode ? [{ debugName: "operatorOptions" }] : /* istanbul ignore next */ []));
|
|
1418
1693
|
/** Native input type for operand fields (date columns use a date picker). */
|
|
1419
|
-
operandInputType = computed(() =>
|
|
1420
|
-
|
|
1421
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: AgridColumnMenuComponent, isStandalone: true, selector: "agrid-column-menu", inputs: { localeText: { classPropertyName: "localeText", publicName: "localeText", isSignal: true, isRequired: false, transformFunction: null }, x: { classPropertyName: "x", publicName: "x", isSignal: true, isRequired: true, transformFunction: null }, y: { classPropertyName: "y", publicName: "y", isSignal: true, isRequired: true, transformFunction: null }, header: { classPropertyName: "header", publicName: "header", isSignal: true, isRequired: true, transformFunction: null }, sortDir: { classPropertyName: "sortDir", publicName: "sortDir", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, showColumnActions: { classPropertyName: "showColumnActions", publicName: "showColumnActions", isSignal: true, isRequired: false, transformFunction: null }, pinned: { classPropertyName: "pinned", publicName: "pinned", isSignal: true, isRequired: false, transformFunction: null }, groupable: { classPropertyName: "groupable", publicName: "groupable", isSignal: true, isRequired: false, transformFunction: null }, grouped: { classPropertyName: "grouped", publicName: "grouped", isSignal: true, isRequired: false, transformFunction: null }, filterable: { classPropertyName: "filterable", publicName: "filterable", isSignal: true, isRequired: false, transformFunction: null }, showValueFilter: { classPropertyName: "showValueFilter", publicName: "showValueFilter", isSignal: true, isRequired: false, transformFunction: null }, filterType: { classPropertyName: "filterType", publicName: "filterType", isSignal: true, isRequired: false, transformFunction: null }, operator: { classPropertyName: "operator", publicName: "operator", isSignal: true, isRequired: false, transformFunction: null }, operand: { classPropertyName: "operand", publicName: "operand", isSignal: true, isRequired: false, transformFunction: null }, operand2: { classPropertyName: "operand2", publicName: "operand2", isSignal: true, isRequired: false, transformFunction: null }, search: { classPropertyName: "search", publicName: "search", isSignal: true, isRequired: false, transformFunction: null }, allSelected: { classPropertyName: "allSelected", publicName: "allSelected", isSignal: true, isRequired: false, transformFunction: null }, valueItems: { classPropertyName: "valueItems", publicName: "valueItems", isSignal: true, isRequired: false, transformFunction: null }, sortPriority: { classPropertyName: "sortPriority", publicName: "sortPriority", isSignal: true, isRequired: false, transformFunction: null }, hasMultiSort: { classPropertyName: "hasMultiSort", publicName: "hasMultiSort", isSignal: true, isRequired: false, transformFunction: null }, aggregate: { classPropertyName: "aggregate", publicName: "aggregate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { operatorChange: "operatorChange", operandChange: "operandChange", operand2Change: "operand2Change", sort: "sort", resetSort: "resetSort", autosize: "autosize", togglePin: "togglePin", togglePinRight: "togglePinRight", hide: "hide", toggleGroup: "toggleGroup", clearFilter: "clearFilter", clearAll: "clearAll", searchChange: "searchChange", toggleAll: "toggleAll", toggleValue: "toggleValue", setAggregate: "setAggregate" }, ngImport: i0, template: "<div\n class=\"ag-filter-menu\"\n role=\"dialog\"\n [attr.aria-label]=\"localeText().columnMenu + ': ' + header()\"\n [style.left.px]=\"x()\"\n [style.top.px]=\"y()\"\n (click)=\"$event.stopPropagation()\"\n>\n @if (sortable()) {\n <div class=\"ag-filter-menu-section\">\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"sortDir() === 'asc'\"\n (click)=\"sort.emit('asc')\"\n >\n \u2191 {{ localeText().sortAscending }}\n @if (sortPriority() > 0) {\n <span class=\"ag-sort-priority-badge\">{{ sortPriority() }}</span>\n }\n </button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"sortDir() === 'desc'\"\n (click)=\"sort.emit('desc')\"\n >\n \u2193 {{ localeText().sortDescending }}\n @if (sortPriority() > 0) {\n <span class=\"ag-sort-priority-badge\">{{ sortPriority() }}</span>\n }\n </button>\n @if (hasMultiSort() && sortPriority() > 0) {\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"resetSort.emit(sortDir()!)\">{{ localeText().sortOnlyByThis }}</button>\n }\n @if (sortDir()) {\n <button class=\"ag-filter-menu-item\" (click)=\"sort.emit(sortDir()!)\">{{ localeText().clearSort }}</button>\n }\n </div>\n }\n\n @if (showColumnActions()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button class=\"ag-filter-menu-item\" (click)=\"autosize.emit()\">\u2194 {{ localeText().autosizeColumn }}</button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"pinned() === 'left'\"\n (click)=\"togglePin.emit()\"\n >\u22A3 {{ pinned() === 'left' ? localeText().unpinColumn : localeText().pinColumn }}</button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"pinned() === 'right'\"\n (click)=\"togglePinRight.emit()\"\n >\u22A2 {{ pinned() === 'right' ? localeText().unpinColumnRight : localeText().pinColumnRight }}</button>\n <button class=\"ag-filter-menu-item\" (click)=\"hide.emit()\">{{ localeText().hideColumn }}</button>\n </div>\n }\n\n @if (showColumnActions()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <span class=\"ag-filter-menu-aggregate-label\">{{ localeText().aggregate }}</span>\n <div class=\"ag-filter-menu-aggregate-btns\">\n @for (opt of aggregateOptions(); track opt.value) {\n <button\n class=\"ag-aggregate-btn\"\n [class.ag-aggregate-btn--active]=\"aggregate() === opt.value\"\n [title]=\"opt.label\"\n (click)=\"setAggregate.emit(aggregate() === opt.value ? null : opt.value)\"\n >{{ opt.symbol }}</button>\n }\n </div>\n </div>\n }\n\n @if (groupable()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"grouped()\"\n (click)=\"toggleGroup.emit()\"\n >{{ grouped() ? '\u229F ' + localeText().ungroup : '\u229E ' + localeText().groupBy(header()) }}</button>\n </div>\n }\n\n @if (filterable()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"clearFilter.emit()\">\n \u2715 {{ localeText().clearFilter }}\n </button>\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"clearAll.emit()\">\n \u2715 {{ localeText().clearAllFilters }}\n </button>\n </div>\n\n @if (filterType()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border ag-filter-menu-condition\">\n <span class=\"ag-filter-menu-aggregate-label\">{{ localeText().filterCondition }}</span>\n <select\n class=\"ag-filter-menu-operator\"\n (change)=\"operatorChange.emit($any($event.target).value || null)\"\n (click)=\"$event.stopPropagation()\"\n >\n <option value=\"\" [selected]=\"!operator()\">{{ localeText().filterNoCondition }}</option>\n @for (op of operatorOptions(); track op.value) {\n <option [value]=\"op.value\" [selected]=\"op.value === operator()\">{{ op.label }}</option>\n }\n </select>\n @if (operator()) {\n <input\n class=\"ag-filter-menu-operand\"\n [type]=\"operandInputType()\"\n [value]=\"operand()\"\n (input)=\"operandChange.emit($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n @if (operator() === 'between') {\n <input\n class=\"ag-filter-menu-operand\"\n [type]=\"operandInputType()\"\n [value]=\"operand2()\"\n (input)=\"operand2Change.emit($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n }\n </div>\n }\n\n @if (showValueFilter()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <input\n class=\"ag-filter-menu-search\"\n [value]=\"search()\"\n (input)=\"searchChange.emit($any($event.target).value)\"\n [placeholder]=\"localeText().searchValuesPlaceholder\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n\n <div class=\"ag-filter-menu-values\">\n <label class=\"ag-filter-menu-value\">\n <input\n type=\"checkbox\"\n [checked]=\"allSelected()\"\n (change)=\"toggleAll.emit()\"\n />\n <span class=\"ag-filter-menu-value-label\">{{ localeText().selectAll }}</span>\n </label>\n @for (item of valueItems(); track item.rawStr) {\n <label\n class=\"ag-filter-menu-value\"\n [class.ag-filter-menu-value--inactive]=\"!item.active\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"item.selected\"\n [disabled]=\"!item.active\"\n (change)=\"toggleValue.emit(item.rawStr)\"\n />\n <span class=\"ag-filter-menu-value-label\">{{ item.label || localeText().blank }}</span>\n </label>\n }\n </div>\n }\n }\n</div>\n", styles: [".ag-filter-menu{position:fixed;z-index:1001;background:var(--agrid-color-bg-subtle);border:1px solid var(--agrid-color-border);border-radius:6px;box-shadow:0 8px 24px var(--agrid-color-shadow);min-width:200px;font-size:13px;overflow:hidden}.ag-filter-menu-section{padding:4px 0}.ag-filter-menu-section--border{border-top:1px solid var(--agrid-color-border)}.ag-filter-menu-item{display:block;width:100%;padding:5px 14px;text-align:left;background:none;border:none;cursor:pointer;color:var(--agrid-color-text);font:inherit;font-size:12px}.ag-filter-menu-item:hover{background:var(--agrid-color-bg-muted)}.ag-filter-menu-item--active{color:var(--agrid-color-accent);font-weight:600}.ag-filter-menu-item--muted{color:var(--agrid-color-text-muted)}.ag-filter-menu-item--muted:hover{background:var(--mat-toolbar-container-background-color);color:var(--agrid-color-text-muted);cursor:not-allowed}.ag-filter-menu-search{background:var(--agrid-color-bg-subtle);display:block;width:calc(100% - 16px);margin:0 8px;height:24px;border:1px solid var(--agrid-color-border);border-radius:3px;outline:none;font:inherit;font-size:12px;padding:0 6px}.ag-filter-menu-search:focus{border-color:var(--agrid-color-accent)}.ag-filter-menu-values{max-height:200px;overflow-y:auto;padding:4px 0;border-top:1px solid var(--agrid-color-border)}.ag-filter-menu-value{display:flex;align-items:center;gap:6px;padding:3px 14px;cursor:pointer;font-size:12px}.ag-filter-menu-value:hover{background:var(--agrid-color-bg-muted)}.ag-filter-menu-value input[type=checkbox]{cursor:pointer;margin:0}.ag-filter-menu-value--inactive{opacity:.38;cursor:default}.ag-filter-menu-value--inactive input[type=checkbox]{cursor:not-allowed}.ag-filter-menu-value-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ag-sort-priority-badge{display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;border-radius:50%;background:var(--agrid-color-accent);color:#fff;font-size:9px;font-weight:700;margin-left:auto}.ag-filter-menu-aggregate-label{display:block;padding:4px 14px 2px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--agrid-color-text-muted);-webkit-user-select:none;user-select:none}.ag-filter-menu-aggregate-btns{display:flex;gap:4px;padding:2px 14px 6px}.ag-aggregate-btn{flex:1;height:26px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text-muted);font-size:13px;cursor:pointer;padding:0;transition:background 80ms,color 80ms}.ag-aggregate-btn:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text);border-color:var(--agrid-color-text-muted)}.ag-aggregate-btn--active{background:var(--agrid-color-accent-subtle);border-color:var(--agrid-color-accent-border);color:var(--agrid-color-accent-fg);font-weight:700}.ag-filter-menu-condition{display:flex;flex-direction:column;gap:6px;padding:6px 14px 8px}.ag-filter-menu-operator,.ag-filter-menu-operand{height:28px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font-size:13px;padding:0 8px;box-sizing:border-box;width:100%}.ag-filter-menu-operator:focus,.ag-filter-menu-operand:focus{outline:none;border-color:var(--agrid-color-accent-border)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1694
|
+
operandInputType = computed(() => this.filterType() === 'date' ? 'date' : this.filterType() === 'number' ? 'number' : 'text', ...(ngDevMode ? [{ debugName: "operandInputType" }] : /* istanbul ignore next */ []));
|
|
1695
|
+
constructor() {
|
|
1696
|
+
effect(() => {
|
|
1697
|
+
this.position.set({ x: this.x(), y: this.y() });
|
|
1698
|
+
this.scheduleViewportFit();
|
|
1699
|
+
});
|
|
1700
|
+
afterNextRender(() => {
|
|
1701
|
+
const menu = this.menuElement();
|
|
1702
|
+
this.fitToViewport();
|
|
1703
|
+
if (menu && typeof ResizeObserver !== 'undefined') {
|
|
1704
|
+
this.resizeObserver = new ResizeObserver(() => this.fitToViewport());
|
|
1705
|
+
this.resizeObserver.observe(menu);
|
|
1706
|
+
}
|
|
1707
|
+
});
|
|
1708
|
+
this.destroyRef.onDestroy(() => this.resizeObserver?.disconnect());
|
|
1709
|
+
}
|
|
1710
|
+
scheduleViewportFit() {
|
|
1711
|
+
setTimeout(() => this.fitToViewport());
|
|
1712
|
+
}
|
|
1713
|
+
fitToViewport() {
|
|
1714
|
+
const menu = this.menuElement();
|
|
1715
|
+
if (!menu || !this.browser.available)
|
|
1716
|
+
return;
|
|
1717
|
+
const rect = menu.getBoundingClientRect();
|
|
1718
|
+
this.position.set(fitColumnMenuToViewport(this.x(), this.y(), rect.width, rect.height, this.browser.viewportWidth(), this.browser.viewportHeight()));
|
|
1719
|
+
}
|
|
1720
|
+
menuElement() {
|
|
1721
|
+
return this.elementRef.nativeElement.querySelector('.ag-filter-menu');
|
|
1722
|
+
}
|
|
1723
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridColumnMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1724
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AgridColumnMenuComponent, isStandalone: true, selector: "agrid-column-menu", inputs: { localeText: { classPropertyName: "localeText", publicName: "localeText", isSignal: true, isRequired: false, transformFunction: null }, x: { classPropertyName: "x", publicName: "x", isSignal: true, isRequired: true, transformFunction: null }, y: { classPropertyName: "y", publicName: "y", isSignal: true, isRequired: true, transformFunction: null }, header: { classPropertyName: "header", publicName: "header", isSignal: true, isRequired: true, transformFunction: null }, sortDir: { classPropertyName: "sortDir", publicName: "sortDir", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, showColumnActions: { classPropertyName: "showColumnActions", publicName: "showColumnActions", isSignal: true, isRequired: false, transformFunction: null }, pinned: { classPropertyName: "pinned", publicName: "pinned", isSignal: true, isRequired: false, transformFunction: null }, groupable: { classPropertyName: "groupable", publicName: "groupable", isSignal: true, isRequired: false, transformFunction: null }, grouped: { classPropertyName: "grouped", publicName: "grouped", isSignal: true, isRequired: false, transformFunction: null }, filterable: { classPropertyName: "filterable", publicName: "filterable", isSignal: true, isRequired: false, transformFunction: null }, showValueFilter: { classPropertyName: "showValueFilter", publicName: "showValueFilter", isSignal: true, isRequired: false, transformFunction: null }, filterType: { classPropertyName: "filterType", publicName: "filterType", isSignal: true, isRequired: false, transformFunction: null }, operator: { classPropertyName: "operator", publicName: "operator", isSignal: true, isRequired: false, transformFunction: null }, operand: { classPropertyName: "operand", publicName: "operand", isSignal: true, isRequired: false, transformFunction: null }, operand2: { classPropertyName: "operand2", publicName: "operand2", isSignal: true, isRequired: false, transformFunction: null }, search: { classPropertyName: "search", publicName: "search", isSignal: true, isRequired: false, transformFunction: null }, allSelected: { classPropertyName: "allSelected", publicName: "allSelected", isSignal: true, isRequired: false, transformFunction: null }, valueItems: { classPropertyName: "valueItems", publicName: "valueItems", isSignal: true, isRequired: false, transformFunction: null }, sortPriority: { classPropertyName: "sortPriority", publicName: "sortPriority", isSignal: true, isRequired: false, transformFunction: null }, hasMultiSort: { classPropertyName: "hasMultiSort", publicName: "hasMultiSort", isSignal: true, isRequired: false, transformFunction: null }, aggregate: { classPropertyName: "aggregate", publicName: "aggregate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { operatorChange: "operatorChange", operandChange: "operandChange", operand2Change: "operand2Change", sort: "sort", resetSort: "resetSort", autosize: "autosize", togglePin: "togglePin", togglePinRight: "togglePinRight", hide: "hide", toggleGroup: "toggleGroup", clearFilter: "clearFilter", clearAll: "clearAll", searchChange: "searchChange", toggleAll: "toggleAll", toggleValue: "toggleValue", setAggregate: "setAggregate" }, ngImport: i0, template: "<div\n class=\"ag-filter-menu\"\n role=\"dialog\"\n [attr.aria-label]=\"localeText().columnMenu + ': ' + header()\"\n [style.left.px]=\"position().x\"\n [style.top.px]=\"position().y\"\n (click)=\"$event.stopPropagation()\"\n>\n @if (sortable()) {\n <div class=\"ag-filter-menu-section\">\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"sortDir() === 'asc'\"\n (click)=\"sort.emit('asc')\"\n >\n \u2191 {{ localeText().sortAscending }}\n @if (sortPriority() > 0) {\n <span class=\"ag-sort-priority-badge\">{{ sortPriority() }}</span>\n }\n </button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"sortDir() === 'desc'\"\n (click)=\"sort.emit('desc')\"\n >\n \u2193 {{ localeText().sortDescending }}\n @if (sortPriority() > 0) {\n <span class=\"ag-sort-priority-badge\">{{ sortPriority() }}</span>\n }\n </button>\n @if (hasMultiSort() && sortPriority() > 0) {\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"resetSort.emit(sortDir()!)\">{{ localeText().sortOnlyByThis }}</button>\n }\n @if (sortDir()) {\n <button class=\"ag-filter-menu-item\" (click)=\"sort.emit(sortDir()!)\">{{ localeText().clearSort }}</button>\n }\n </div>\n }\n\n @if (showColumnActions()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button class=\"ag-filter-menu-item\" (click)=\"autosize.emit()\">\u2194 {{ localeText().autosizeColumn }}</button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"pinned() === 'left'\"\n (click)=\"togglePin.emit()\"\n >\u22A3 {{ pinned() === 'left' ? localeText().unpinColumn : localeText().pinColumn }}</button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"pinned() === 'right'\"\n (click)=\"togglePinRight.emit()\"\n >\u22A2 {{ pinned() === 'right' ? localeText().unpinColumnRight : localeText().pinColumnRight }}</button>\n <button class=\"ag-filter-menu-item\" (click)=\"hide.emit()\">{{ localeText().hideColumn }}</button>\n </div>\n }\n\n @if (showColumnActions()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <span class=\"ag-filter-menu-aggregate-label\">{{ localeText().aggregate }}</span>\n <div class=\"ag-filter-menu-aggregate-btns\">\n @for (opt of aggregateOptions(); track opt.value) {\n <button\n class=\"ag-aggregate-btn\"\n [class.ag-aggregate-btn--active]=\"aggregate() === opt.value\"\n [title]=\"opt.label\"\n (click)=\"setAggregate.emit(aggregate() === opt.value ? null : opt.value)\"\n >{{ opt.symbol }}</button>\n }\n </div>\n </div>\n }\n\n @if (groupable()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"grouped()\"\n (click)=\"toggleGroup.emit()\"\n >{{ grouped() ? '\u229F ' + localeText().ungroup : '\u229E ' + localeText().groupBy(header()) }}</button>\n </div>\n }\n\n @if (filterable()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"clearFilter.emit()\">\n \u2715 {{ localeText().clearFilter }}\n </button>\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"clearAll.emit()\">\n \u2715 {{ localeText().clearAllFilters }}\n </button>\n </div>\n\n @if (filterType()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border ag-filter-menu-condition\">\n <span class=\"ag-filter-menu-aggregate-label\">{{ localeText().filterCondition }}</span>\n <div class=\"ag-filter-menu-operators\">\n <button\n type=\"button\"\n class=\"ag-filter-menu-operator-btn ag-filter-menu-operator-btn--clear\"\n [class.ag-filter-menu-operator-btn--active]=\"!operator()\"\n (click)=\"operatorChange.emit(null)\"\n >{{ localeText().filterNoCondition }}</button>\n @for (op of operatorOptions(); track op.value) {\n <button\n type=\"button\"\n class=\"ag-filter-menu-operator-btn\"\n [class.ag-filter-menu-operator-btn--active]=\"op.value === operator()\"\n (click)=\"operatorChange.emit(op.value)\"\n >{{ op.label }}</button>\n }\n </div>\n @if (operator()) {\n <label class=\"ag-filter-menu-operand-label\" for=\"agrid-filter-operand\">\n {{ localeText().filterValue }}\n </label>\n <input\n id=\"agrid-filter-operand\"\n class=\"ag-filter-menu-operand\"\n [type]=\"operandInputType()\"\n [value]=\"operand()\"\n (input)=\"operandChange.emit($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n @if (operator() === 'between') {\n <input\n class=\"ag-filter-menu-operand\"\n [type]=\"operandInputType()\"\n [value]=\"operand2()\"\n (input)=\"operand2Change.emit($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n }\n </div>\n }\n\n @if (showValueFilter()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <input\n class=\"ag-filter-menu-search\"\n [value]=\"search()\"\n (input)=\"searchChange.emit($any($event.target).value)\"\n [placeholder]=\"localeText().searchValuesPlaceholder\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n\n <div class=\"ag-filter-menu-values\">\n <label class=\"ag-filter-menu-value\">\n <input\n type=\"checkbox\"\n [checked]=\"allSelected()\"\n (change)=\"toggleAll.emit()\"\n />\n <span class=\"ag-filter-menu-value-label\">{{ localeText().selectAll }}</span>\n </label>\n @for (item of valueItems(); track item.rawStr) {\n <label\n class=\"ag-filter-menu-value\"\n [class.ag-filter-menu-value--inactive]=\"!item.active\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"item.selected\"\n [disabled]=\"!item.active\"\n (change)=\"toggleValue.emit(item.rawStr)\"\n />\n <span class=\"ag-filter-menu-value-label\">{{ item.label || localeText().blank }}</span>\n </label>\n }\n </div>\n }\n }\n</div>\n", styles: [".ag-filter-menu{position:fixed;z-index:1001;background:var(--agrid-color-bg-subtle);border:1px solid var(--agrid-color-border);border-radius:6px;box-shadow:0 8px 24px var(--agrid-color-shadow);min-width:200px;max-height:calc(100vh - 16px);font-size:13px;overflow-y:auto}.ag-filter-menu-section{padding:4px 0}.ag-filter-menu-section--border{border-top:1px solid var(--agrid-color-border)}.ag-filter-menu-item{display:block;width:100%;padding:5px 14px;text-align:left;background:none;border:none;cursor:pointer;color:var(--agrid-color-text);font:inherit;font-size:12px}.ag-filter-menu-item:hover{background:var(--agrid-color-bg-muted)}.ag-filter-menu-item--active{color:var(--agrid-color-accent);font-weight:600}.ag-filter-menu-item--muted{color:var(--agrid-color-text-muted)}.ag-filter-menu-item--muted:hover{background:var(--mat-toolbar-container-background-color);color:var(--agrid-color-text-muted);cursor:not-allowed}.ag-filter-menu-search{background:var(--agrid-color-bg-subtle);display:block;width:calc(100% - 16px);margin:0 8px;height:24px;border:1px solid var(--agrid-color-border);border-radius:3px;outline:none;font:inherit;font-size:12px;padding:0 6px}.ag-filter-menu-search:focus{border-color:var(--agrid-color-accent)}.ag-filter-menu-values{max-height:120px;overflow-y:auto;padding:4px 0;border-top:1px solid var(--agrid-color-border)}.ag-filter-menu-value{display:flex;align-items:center;gap:6px;padding:3px 14px;cursor:pointer;font-size:12px}.ag-filter-menu-value:hover{background:var(--agrid-color-bg-muted)}.ag-filter-menu-value input[type=checkbox]{cursor:pointer;margin:0}.ag-filter-menu-value--inactive{opacity:.38;cursor:default}.ag-filter-menu-value--inactive input[type=checkbox]{cursor:not-allowed}.ag-filter-menu-value-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ag-sort-priority-badge{display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;border-radius:50%;background:var(--agrid-color-accent);color:#fff;font-size:9px;font-weight:700;margin-left:auto}.ag-filter-menu-aggregate-label{display:block;padding:4px 14px 2px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--agrid-color-text-muted);-webkit-user-select:none;user-select:none}.ag-filter-menu-aggregate-btns{display:flex;gap:4px;padding:2px 14px 6px}.ag-aggregate-btn{flex:1;height:26px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text-muted);font-size:13px;cursor:pointer;padding:0;transition:background 80ms,color 80ms}.ag-aggregate-btn:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text);border-color:var(--agrid-color-text-muted)}.ag-aggregate-btn--active{background:var(--agrid-color-accent-subtle);border-color:var(--agrid-color-accent-border);color:var(--agrid-color-accent-fg);font-weight:700}.ag-filter-menu-condition{display:flex;flex-direction:column;gap:6px;padding:6px 14px 8px}.ag-filter-menu-operand{height:28px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font-size:13px;padding:0 8px;box-sizing:border-box;width:100%}.ag-filter-menu-operand:focus{outline:none;border-color:var(--agrid-color-accent-border)}.ag-filter-menu-operators{display:grid;grid-template-columns:1fr 1fr;gap:4px}.ag-filter-menu-operator-btn{min-height:26px;padding:3px 6px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font:inherit;font-size:11px;line-height:1.15;text-align:left;cursor:pointer}.ag-filter-menu-operator-btn:hover,.ag-filter-menu-operator-btn:focus-visible{border-color:var(--agrid-color-accent-border);outline:none}.ag-filter-menu-operator-btn--active{border-color:var(--agrid-color-accent-border);background:var(--agrid-color-accent-subtle);color:var(--agrid-color-accent-fg)}.ag-filter-menu-operator-btn--clear{grid-column:1 / -1}.ag-filter-menu-operand-label{font-size:11px;color:var(--agrid-color-text-muted)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1422
1725
|
}
|
|
1423
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1726
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridColumnMenuComponent, decorators: [{
|
|
1424
1727
|
type: Component,
|
|
1425
|
-
args: [{ selector: 'agrid-column-menu', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"ag-filter-menu\"\n role=\"dialog\"\n [attr.aria-label]=\"localeText().columnMenu + ': ' + header()\"\n [style.left.px]=\"x()\"\n [style.top.px]=\"y()\"\n (click)=\"$event.stopPropagation()\"\n>\n @if (sortable()) {\n <div class=\"ag-filter-menu-section\">\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"sortDir() === 'asc'\"\n (click)=\"sort.emit('asc')\"\n >\n \u2191 {{ localeText().sortAscending }}\n @if (sortPriority() > 0) {\n <span class=\"ag-sort-priority-badge\">{{ sortPriority() }}</span>\n }\n </button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"sortDir() === 'desc'\"\n (click)=\"sort.emit('desc')\"\n >\n \u2193 {{ localeText().sortDescending }}\n @if (sortPriority() > 0) {\n <span class=\"ag-sort-priority-badge\">{{ sortPriority() }}</span>\n }\n </button>\n @if (hasMultiSort() && sortPriority() > 0) {\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"resetSort.emit(sortDir()!)\">{{ localeText().sortOnlyByThis }}</button>\n }\n @if (sortDir()) {\n <button class=\"ag-filter-menu-item\" (click)=\"sort.emit(sortDir()!)\">{{ localeText().clearSort }}</button>\n }\n </div>\n }\n\n @if (showColumnActions()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button class=\"ag-filter-menu-item\" (click)=\"autosize.emit()\">\u2194 {{ localeText().autosizeColumn }}</button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"pinned() === 'left'\"\n (click)=\"togglePin.emit()\"\n >\u22A3 {{ pinned() === 'left' ? localeText().unpinColumn : localeText().pinColumn }}</button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"pinned() === 'right'\"\n (click)=\"togglePinRight.emit()\"\n >\u22A2 {{ pinned() === 'right' ? localeText().unpinColumnRight : localeText().pinColumnRight }}</button>\n <button class=\"ag-filter-menu-item\" (click)=\"hide.emit()\">{{ localeText().hideColumn }}</button>\n </div>\n }\n\n @if (showColumnActions()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <span class=\"ag-filter-menu-aggregate-label\">{{ localeText().aggregate }}</span>\n <div class=\"ag-filter-menu-aggregate-btns\">\n @for (opt of aggregateOptions(); track opt.value) {\n <button\n class=\"ag-aggregate-btn\"\n [class.ag-aggregate-btn--active]=\"aggregate() === opt.value\"\n [title]=\"opt.label\"\n (click)=\"setAggregate.emit(aggregate() === opt.value ? null : opt.value)\"\n >{{ opt.symbol }}</button>\n }\n </div>\n </div>\n }\n\n @if (groupable()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"grouped()\"\n (click)=\"toggleGroup.emit()\"\n >{{ grouped() ? '\u229F ' + localeText().ungroup : '\u229E ' + localeText().groupBy(header()) }}</button>\n </div>\n }\n\n @if (filterable()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"clearFilter.emit()\">\n \u2715 {{ localeText().clearFilter }}\n </button>\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"clearAll.emit()\">\n \u2715 {{ localeText().clearAllFilters }}\n </button>\n </div>\n\n @if (filterType()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border ag-filter-menu-condition\">\n <span class=\"ag-filter-menu-aggregate-label\">{{ localeText().filterCondition }}</span>\n <select\n class=\"ag-filter-menu-operator\"\n (change)=\"operatorChange.emit($any($event.target).value || null)\"\n (click)=\"$event.stopPropagation()\"\n >\n <option value=\"\" [selected]=\"!operator()\">{{ localeText().filterNoCondition }}</option>\n @for (op of operatorOptions(); track op.value) {\n <option [value]=\"op.value\" [selected]=\"op.value === operator()\">{{ op.label }}</option>\n }\n </select>\n @if (operator()) {\n <input\n class=\"ag-filter-menu-operand\"\n [type]=\"operandInputType()\"\n [value]=\"operand()\"\n (input)=\"operandChange.emit($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n @if (operator() === 'between') {\n <input\n class=\"ag-filter-menu-operand\"\n [type]=\"operandInputType()\"\n [value]=\"operand2()\"\n (input)=\"operand2Change.emit($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n }\n </div>\n }\n\n @if (showValueFilter()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <input\n class=\"ag-filter-menu-search\"\n [value]=\"search()\"\n (input)=\"searchChange.emit($any($event.target).value)\"\n [placeholder]=\"localeText().searchValuesPlaceholder\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n\n <div class=\"ag-filter-menu-values\">\n <label class=\"ag-filter-menu-value\">\n <input\n type=\"checkbox\"\n [checked]=\"allSelected()\"\n (change)=\"toggleAll.emit()\"\n />\n <span class=\"ag-filter-menu-value-label\">{{ localeText().selectAll }}</span>\n </label>\n @for (item of valueItems(); track item.rawStr) {\n <label\n class=\"ag-filter-menu-value\"\n [class.ag-filter-menu-value--inactive]=\"!item.active\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"item.selected\"\n [disabled]=\"!item.active\"\n (change)=\"toggleValue.emit(item.rawStr)\"\n />\n <span class=\"ag-filter-menu-value-label\">{{ item.label || localeText().blank }}</span>\n </label>\n }\n </div>\n }\n }\n</div>\n", styles: [".ag-filter-menu{position:fixed;z-index:1001;background:var(--agrid-color-bg-subtle);border:1px solid var(--agrid-color-border);border-radius:6px;box-shadow:0 8px 24px var(--agrid-color-shadow);min-width:200px;font-size:13px;overflow:hidden}.ag-filter-menu-section{padding:4px 0}.ag-filter-menu-section--border{border-top:1px solid var(--agrid-color-border)}.ag-filter-menu-item{display:block;width:100%;padding:5px 14px;text-align:left;background:none;border:none;cursor:pointer;color:var(--agrid-color-text);font:inherit;font-size:12px}.ag-filter-menu-item:hover{background:var(--agrid-color-bg-muted)}.ag-filter-menu-item--active{color:var(--agrid-color-accent);font-weight:600}.ag-filter-menu-item--muted{color:var(--agrid-color-text-muted)}.ag-filter-menu-item--muted:hover{background:var(--mat-toolbar-container-background-color);color:var(--agrid-color-text-muted);cursor:not-allowed}.ag-filter-menu-search{background:var(--agrid-color-bg-subtle);display:block;width:calc(100% - 16px);margin:0 8px;height:24px;border:1px solid var(--agrid-color-border);border-radius:3px;outline:none;font:inherit;font-size:12px;padding:0 6px}.ag-filter-menu-search:focus{border-color:var(--agrid-color-accent)}.ag-filter-menu-values{max-height:200px;overflow-y:auto;padding:4px 0;border-top:1px solid var(--agrid-color-border)}.ag-filter-menu-value{display:flex;align-items:center;gap:6px;padding:3px 14px;cursor:pointer;font-size:12px}.ag-filter-menu-value:hover{background:var(--agrid-color-bg-muted)}.ag-filter-menu-value input[type=checkbox]{cursor:pointer;margin:0}.ag-filter-menu-value--inactive{opacity:.38;cursor:default}.ag-filter-menu-value--inactive input[type=checkbox]{cursor:not-allowed}.ag-filter-menu-value-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ag-sort-priority-badge{display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;border-radius:50%;background:var(--agrid-color-accent);color:#fff;font-size:9px;font-weight:700;margin-left:auto}.ag-filter-menu-aggregate-label{display:block;padding:4px 14px 2px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--agrid-color-text-muted);-webkit-user-select:none;user-select:none}.ag-filter-menu-aggregate-btns{display:flex;gap:4px;padding:2px 14px 6px}.ag-aggregate-btn{flex:1;height:26px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text-muted);font-size:13px;cursor:pointer;padding:0;transition:background 80ms,color 80ms}.ag-aggregate-btn:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text);border-color:var(--agrid-color-text-muted)}.ag-aggregate-btn--active{background:var(--agrid-color-accent-subtle);border-color:var(--agrid-color-accent-border);color:var(--agrid-color-accent-fg);font-weight:700}.ag-filter-menu-condition{display:flex;flex-direction:column;gap:6px;padding:6px 14px 8px}.ag-filter-menu-operator,.ag-filter-menu-operand{height:28px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font-size:13px;padding:0 8px;box-sizing:border-box;width:100%}.ag-filter-menu-operator:focus,.ag-filter-menu-operand:focus{outline:none;border-color:var(--agrid-color-accent-border)}\n"] }]
|
|
1426
|
-
}], propDecorators: { localeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "localeText", required: false }] }], x: [{ type: i0.Input, args: [{ isSignal: true, alias: "x", required: true }] }], y: [{ type: i0.Input, args: [{ isSignal: true, alias: "y", required: true }] }], header: [{ type: i0.Input, args: [{ isSignal: true, alias: "header", required: true }] }], sortDir: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortDir", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], showColumnActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showColumnActions", required: false }] }], pinned: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinned", required: false }] }], groupable: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupable", required: false }] }], grouped: [{ type: i0.Input, args: [{ isSignal: true, alias: "grouped", required: false }] }], filterable: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterable", required: false }] }], showValueFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValueFilter", required: false }] }], filterType: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterType", required: false }] }], operator: [{ type: i0.Input, args: [{ isSignal: true, alias: "operator", required: false }] }], operand: [{ type: i0.Input, args: [{ isSignal: true, alias: "operand", required: false }] }], operand2: [{ type: i0.Input, args: [{ isSignal: true, alias: "operand2", required: false }] }], operatorChange: [{ type: i0.Output, args: ["operatorChange"] }], operandChange: [{ type: i0.Output, args: ["operandChange"] }], operand2Change: [{ type: i0.Output, args: ["operand2Change"] }], search: [{ type: i0.Input, args: [{ isSignal: true, alias: "search", required: false }] }], allSelected: [{ type: i0.Input, args: [{ isSignal: true, alias: "allSelected", required: false }] }], valueItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueItems", required: false }] }], sortPriority: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortPriority", required: false }] }], hasMultiSort: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasMultiSort", required: false }] }], sort: [{ type: i0.Output, args: ["sort"] }], resetSort: [{ type: i0.Output, args: ["resetSort"] }], autosize: [{ type: i0.Output, args: ["autosize"] }], togglePin: [{ type: i0.Output, args: ["togglePin"] }], togglePinRight: [{ type: i0.Output, args: ["togglePinRight"] }], hide: [{ type: i0.Output, args: ["hide"] }], toggleGroup: [{ type: i0.Output, args: ["toggleGroup"] }], clearFilter: [{ type: i0.Output, args: ["clearFilter"] }], clearAll: [{ type: i0.Output, args: ["clearAll"] }], searchChange: [{ type: i0.Output, args: ["searchChange"] }], toggleAll: [{ type: i0.Output, args: ["toggleAll"] }], toggleValue: [{ type: i0.Output, args: ["toggleValue"] }], aggregate: [{ type: i0.Input, args: [{ isSignal: true, alias: "aggregate", required: false }] }], setAggregate: [{ type: i0.Output, args: ["setAggregate"] }] } });
|
|
1728
|
+
args: [{ selector: 'agrid-column-menu', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"ag-filter-menu\"\n role=\"dialog\"\n [attr.aria-label]=\"localeText().columnMenu + ': ' + header()\"\n [style.left.px]=\"position().x\"\n [style.top.px]=\"position().y\"\n (click)=\"$event.stopPropagation()\"\n>\n @if (sortable()) {\n <div class=\"ag-filter-menu-section\">\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"sortDir() === 'asc'\"\n (click)=\"sort.emit('asc')\"\n >\n \u2191 {{ localeText().sortAscending }}\n @if (sortPriority() > 0) {\n <span class=\"ag-sort-priority-badge\">{{ sortPriority() }}</span>\n }\n </button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"sortDir() === 'desc'\"\n (click)=\"sort.emit('desc')\"\n >\n \u2193 {{ localeText().sortDescending }}\n @if (sortPriority() > 0) {\n <span class=\"ag-sort-priority-badge\">{{ sortPriority() }}</span>\n }\n </button>\n @if (hasMultiSort() && sortPriority() > 0) {\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"resetSort.emit(sortDir()!)\">{{ localeText().sortOnlyByThis }}</button>\n }\n @if (sortDir()) {\n <button class=\"ag-filter-menu-item\" (click)=\"sort.emit(sortDir()!)\">{{ localeText().clearSort }}</button>\n }\n </div>\n }\n\n @if (showColumnActions()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button class=\"ag-filter-menu-item\" (click)=\"autosize.emit()\">\u2194 {{ localeText().autosizeColumn }}</button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"pinned() === 'left'\"\n (click)=\"togglePin.emit()\"\n >\u22A3 {{ pinned() === 'left' ? localeText().unpinColumn : localeText().pinColumn }}</button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"pinned() === 'right'\"\n (click)=\"togglePinRight.emit()\"\n >\u22A2 {{ pinned() === 'right' ? localeText().unpinColumnRight : localeText().pinColumnRight }}</button>\n <button class=\"ag-filter-menu-item\" (click)=\"hide.emit()\">{{ localeText().hideColumn }}</button>\n </div>\n }\n\n @if (showColumnActions()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <span class=\"ag-filter-menu-aggregate-label\">{{ localeText().aggregate }}</span>\n <div class=\"ag-filter-menu-aggregate-btns\">\n @for (opt of aggregateOptions(); track opt.value) {\n <button\n class=\"ag-aggregate-btn\"\n [class.ag-aggregate-btn--active]=\"aggregate() === opt.value\"\n [title]=\"opt.label\"\n (click)=\"setAggregate.emit(aggregate() === opt.value ? null : opt.value)\"\n >{{ opt.symbol }}</button>\n }\n </div>\n </div>\n }\n\n @if (groupable()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"grouped()\"\n (click)=\"toggleGroup.emit()\"\n >{{ grouped() ? '\u229F ' + localeText().ungroup : '\u229E ' + localeText().groupBy(header()) }}</button>\n </div>\n }\n\n @if (filterable()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"clearFilter.emit()\">\n \u2715 {{ localeText().clearFilter }}\n </button>\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"clearAll.emit()\">\n \u2715 {{ localeText().clearAllFilters }}\n </button>\n </div>\n\n @if (filterType()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border ag-filter-menu-condition\">\n <span class=\"ag-filter-menu-aggregate-label\">{{ localeText().filterCondition }}</span>\n <div class=\"ag-filter-menu-operators\">\n <button\n type=\"button\"\n class=\"ag-filter-menu-operator-btn ag-filter-menu-operator-btn--clear\"\n [class.ag-filter-menu-operator-btn--active]=\"!operator()\"\n (click)=\"operatorChange.emit(null)\"\n >{{ localeText().filterNoCondition }}</button>\n @for (op of operatorOptions(); track op.value) {\n <button\n type=\"button\"\n class=\"ag-filter-menu-operator-btn\"\n [class.ag-filter-menu-operator-btn--active]=\"op.value === operator()\"\n (click)=\"operatorChange.emit(op.value)\"\n >{{ op.label }}</button>\n }\n </div>\n @if (operator()) {\n <label class=\"ag-filter-menu-operand-label\" for=\"agrid-filter-operand\">\n {{ localeText().filterValue }}\n </label>\n <input\n id=\"agrid-filter-operand\"\n class=\"ag-filter-menu-operand\"\n [type]=\"operandInputType()\"\n [value]=\"operand()\"\n (input)=\"operandChange.emit($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n @if (operator() === 'between') {\n <input\n class=\"ag-filter-menu-operand\"\n [type]=\"operandInputType()\"\n [value]=\"operand2()\"\n (input)=\"operand2Change.emit($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n }\n </div>\n }\n\n @if (showValueFilter()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <input\n class=\"ag-filter-menu-search\"\n [value]=\"search()\"\n (input)=\"searchChange.emit($any($event.target).value)\"\n [placeholder]=\"localeText().searchValuesPlaceholder\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n\n <div class=\"ag-filter-menu-values\">\n <label class=\"ag-filter-menu-value\">\n <input\n type=\"checkbox\"\n [checked]=\"allSelected()\"\n (change)=\"toggleAll.emit()\"\n />\n <span class=\"ag-filter-menu-value-label\">{{ localeText().selectAll }}</span>\n </label>\n @for (item of valueItems(); track item.rawStr) {\n <label\n class=\"ag-filter-menu-value\"\n [class.ag-filter-menu-value--inactive]=\"!item.active\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"item.selected\"\n [disabled]=\"!item.active\"\n (change)=\"toggleValue.emit(item.rawStr)\"\n />\n <span class=\"ag-filter-menu-value-label\">{{ item.label || localeText().blank }}</span>\n </label>\n }\n </div>\n }\n }\n</div>\n", styles: [".ag-filter-menu{position:fixed;z-index:1001;background:var(--agrid-color-bg-subtle);border:1px solid var(--agrid-color-border);border-radius:6px;box-shadow:0 8px 24px var(--agrid-color-shadow);min-width:200px;max-height:calc(100vh - 16px);font-size:13px;overflow-y:auto}.ag-filter-menu-section{padding:4px 0}.ag-filter-menu-section--border{border-top:1px solid var(--agrid-color-border)}.ag-filter-menu-item{display:block;width:100%;padding:5px 14px;text-align:left;background:none;border:none;cursor:pointer;color:var(--agrid-color-text);font:inherit;font-size:12px}.ag-filter-menu-item:hover{background:var(--agrid-color-bg-muted)}.ag-filter-menu-item--active{color:var(--agrid-color-accent);font-weight:600}.ag-filter-menu-item--muted{color:var(--agrid-color-text-muted)}.ag-filter-menu-item--muted:hover{background:var(--mat-toolbar-container-background-color);color:var(--agrid-color-text-muted);cursor:not-allowed}.ag-filter-menu-search{background:var(--agrid-color-bg-subtle);display:block;width:calc(100% - 16px);margin:0 8px;height:24px;border:1px solid var(--agrid-color-border);border-radius:3px;outline:none;font:inherit;font-size:12px;padding:0 6px}.ag-filter-menu-search:focus{border-color:var(--agrid-color-accent)}.ag-filter-menu-values{max-height:120px;overflow-y:auto;padding:4px 0;border-top:1px solid var(--agrid-color-border)}.ag-filter-menu-value{display:flex;align-items:center;gap:6px;padding:3px 14px;cursor:pointer;font-size:12px}.ag-filter-menu-value:hover{background:var(--agrid-color-bg-muted)}.ag-filter-menu-value input[type=checkbox]{cursor:pointer;margin:0}.ag-filter-menu-value--inactive{opacity:.38;cursor:default}.ag-filter-menu-value--inactive input[type=checkbox]{cursor:not-allowed}.ag-filter-menu-value-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ag-sort-priority-badge{display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;border-radius:50%;background:var(--agrid-color-accent);color:#fff;font-size:9px;font-weight:700;margin-left:auto}.ag-filter-menu-aggregate-label{display:block;padding:4px 14px 2px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--agrid-color-text-muted);-webkit-user-select:none;user-select:none}.ag-filter-menu-aggregate-btns{display:flex;gap:4px;padding:2px 14px 6px}.ag-aggregate-btn{flex:1;height:26px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text-muted);font-size:13px;cursor:pointer;padding:0;transition:background 80ms,color 80ms}.ag-aggregate-btn:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text);border-color:var(--agrid-color-text-muted)}.ag-aggregate-btn--active{background:var(--agrid-color-accent-subtle);border-color:var(--agrid-color-accent-border);color:var(--agrid-color-accent-fg);font-weight:700}.ag-filter-menu-condition{display:flex;flex-direction:column;gap:6px;padding:6px 14px 8px}.ag-filter-menu-operand{height:28px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font-size:13px;padding:0 8px;box-sizing:border-box;width:100%}.ag-filter-menu-operand:focus{outline:none;border-color:var(--agrid-color-accent-border)}.ag-filter-menu-operators{display:grid;grid-template-columns:1fr 1fr;gap:4px}.ag-filter-menu-operator-btn{min-height:26px;padding:3px 6px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font:inherit;font-size:11px;line-height:1.15;text-align:left;cursor:pointer}.ag-filter-menu-operator-btn:hover,.ag-filter-menu-operator-btn:focus-visible{border-color:var(--agrid-color-accent-border);outline:none}.ag-filter-menu-operator-btn--active{border-color:var(--agrid-color-accent-border);background:var(--agrid-color-accent-subtle);color:var(--agrid-color-accent-fg)}.ag-filter-menu-operator-btn--clear{grid-column:1 / -1}.ag-filter-menu-operand-label{font-size:11px;color:var(--agrid-color-text-muted)}\n"] }]
|
|
1729
|
+
}], ctorParameters: () => [], propDecorators: { localeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "localeText", required: false }] }], x: [{ type: i0.Input, args: [{ isSignal: true, alias: "x", required: true }] }], y: [{ type: i0.Input, args: [{ isSignal: true, alias: "y", required: true }] }], header: [{ type: i0.Input, args: [{ isSignal: true, alias: "header", required: true }] }], sortDir: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortDir", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], showColumnActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showColumnActions", required: false }] }], pinned: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinned", required: false }] }], groupable: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupable", required: false }] }], grouped: [{ type: i0.Input, args: [{ isSignal: true, alias: "grouped", required: false }] }], filterable: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterable", required: false }] }], showValueFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValueFilter", required: false }] }], filterType: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterType", required: false }] }], operator: [{ type: i0.Input, args: [{ isSignal: true, alias: "operator", required: false }] }], operand: [{ type: i0.Input, args: [{ isSignal: true, alias: "operand", required: false }] }], operand2: [{ type: i0.Input, args: [{ isSignal: true, alias: "operand2", required: false }] }], operatorChange: [{ type: i0.Output, args: ["operatorChange"] }], operandChange: [{ type: i0.Output, args: ["operandChange"] }], operand2Change: [{ type: i0.Output, args: ["operand2Change"] }], search: [{ type: i0.Input, args: [{ isSignal: true, alias: "search", required: false }] }], allSelected: [{ type: i0.Input, args: [{ isSignal: true, alias: "allSelected", required: false }] }], valueItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueItems", required: false }] }], sortPriority: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortPriority", required: false }] }], hasMultiSort: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasMultiSort", required: false }] }], sort: [{ type: i0.Output, args: ["sort"] }], resetSort: [{ type: i0.Output, args: ["resetSort"] }], autosize: [{ type: i0.Output, args: ["autosize"] }], togglePin: [{ type: i0.Output, args: ["togglePin"] }], togglePinRight: [{ type: i0.Output, args: ["togglePinRight"] }], hide: [{ type: i0.Output, args: ["hide"] }], toggleGroup: [{ type: i0.Output, args: ["toggleGroup"] }], clearFilter: [{ type: i0.Output, args: ["clearFilter"] }], clearAll: [{ type: i0.Output, args: ["clearAll"] }], searchChange: [{ type: i0.Output, args: ["searchChange"] }], toggleAll: [{ type: i0.Output, args: ["toggleAll"] }], toggleValue: [{ type: i0.Output, args: ["toggleValue"] }], aggregate: [{ type: i0.Input, args: [{ isSignal: true, alias: "aggregate", required: false }] }], setAggregate: [{ type: i0.Output, args: ["setAggregate"] }] } });
|
|
1427
1730
|
|
|
1428
1731
|
/** Owns column-menu state, filters, sorting, and menu-triggered column mutations. @internal */
|
|
1429
1732
|
class AgridColumnMenuController {
|
|
@@ -1481,6 +1784,10 @@ class AgridColumnMenuController {
|
|
|
1481
1784
|
const allowed = new Set(filter.selectedValues);
|
|
1482
1785
|
indices = indices.filter(index => allowed.has(String(rows[index][field] ?? '')));
|
|
1483
1786
|
}
|
|
1787
|
+
if (filter.operator && filter.operand != null && filter.operand !== '') {
|
|
1788
|
+
const filterCol = this.getColDef(field);
|
|
1789
|
+
indices = indices.filter(index => passesConditionFilter(filterCol, rows[index][field], filter));
|
|
1790
|
+
}
|
|
1484
1791
|
}
|
|
1485
1792
|
}
|
|
1486
1793
|
return new Set(indices.map(index => String(rows[index][menu.field] ?? '')));
|
|
@@ -1509,24 +1816,26 @@ class AgridColumnMenuController {
|
|
|
1509
1816
|
getTextFilter(field) {
|
|
1510
1817
|
return this.opts.control()?.getFilter(field).text ?? '';
|
|
1511
1818
|
}
|
|
1512
|
-
/** Returns the
|
|
1819
|
+
/** Returns the condition-filter input type for a filterable field. */
|
|
1513
1820
|
getFilterType(field) {
|
|
1514
|
-
const
|
|
1515
|
-
|
|
1821
|
+
const col = this.getColDef(field);
|
|
1822
|
+
if (!col?.filterable || col.type === 'boolean')
|
|
1823
|
+
return null;
|
|
1824
|
+
return col.type === 'number' || col.type === 'date' ? col.type : 'text';
|
|
1516
1825
|
}
|
|
1517
|
-
/** Returns the
|
|
1826
|
+
/** Returns the condition operator for a field, or `null`. */
|
|
1518
1827
|
getFilterOperator(field) {
|
|
1519
1828
|
return this.opts.control()?.getFilter(field).operator ?? null;
|
|
1520
1829
|
}
|
|
1521
|
-
/** Returns the primary
|
|
1830
|
+
/** Returns the primary condition operand for a field. */
|
|
1522
1831
|
getFilterOperand(field) {
|
|
1523
1832
|
return this.opts.control()?.getFilter(field).operand ?? '';
|
|
1524
1833
|
}
|
|
1525
|
-
/** Returns the secondary
|
|
1834
|
+
/** Returns the secondary condition operand (used by `between`). */
|
|
1526
1835
|
getFilterOperand2(field) {
|
|
1527
1836
|
return this.opts.control()?.getFilter(field).operand2 ?? '';
|
|
1528
1837
|
}
|
|
1529
|
-
/** Sets the
|
|
1838
|
+
/** Sets the condition operator; clearing it (`null`) also drops the operands. */
|
|
1530
1839
|
setFilterOperator(field, operator) {
|
|
1531
1840
|
const control = this.opts.control();
|
|
1532
1841
|
if (!control)
|
|
@@ -1540,7 +1849,7 @@ class AgridColumnMenuController {
|
|
|
1540
1849
|
}
|
|
1541
1850
|
this.emitRangeServer(field, false);
|
|
1542
1851
|
}
|
|
1543
|
-
/** Sets the primary
|
|
1852
|
+
/** Sets the primary condition operand. */
|
|
1544
1853
|
setFilterOperand(field, value) {
|
|
1545
1854
|
const control = this.opts.control();
|
|
1546
1855
|
if (!control)
|
|
@@ -1549,7 +1858,7 @@ class AgridColumnMenuController {
|
|
|
1549
1858
|
control.setRangeFilter(field, current.operator ?? 'eq', value, current.operand2 ?? '');
|
|
1550
1859
|
this.emitRangeServer(field, true);
|
|
1551
1860
|
}
|
|
1552
|
-
/** Sets the secondary
|
|
1861
|
+
/** Sets the secondary condition operand (used by `between`). */
|
|
1553
1862
|
setFilterOperand2(field, value) {
|
|
1554
1863
|
const control = this.opts.control();
|
|
1555
1864
|
if (!control)
|
|
@@ -1559,7 +1868,7 @@ class AgridColumnMenuController {
|
|
|
1559
1868
|
this.emitRangeServer(field, true);
|
|
1560
1869
|
}
|
|
1561
1870
|
/**
|
|
1562
|
-
* In server-side filtering mode, emit a {@link FilterChangeEvent} carrying the current
|
|
1871
|
+
* In server-side filtering mode, emit a {@link FilterChangeEvent} carrying the current
|
|
1563
1872
|
* condition so the host can refetch. Operand edits are debounced; operator changes are immediate.
|
|
1564
1873
|
* No-op in client mode (the projection layer filters locally).
|
|
1565
1874
|
*/
|
|
@@ -1666,6 +1975,9 @@ class AgridColumnMenuController {
|
|
|
1666
1975
|
if (previous.selectedValues !== null) {
|
|
1667
1976
|
control.setSelectedValues(field, previous.selectedValues);
|
|
1668
1977
|
}
|
|
1978
|
+
if (previous.operator) {
|
|
1979
|
+
control.setRangeFilter(field, previous.operator, previous.operand ?? null, previous.operand2 ?? null);
|
|
1980
|
+
}
|
|
1669
1981
|
if (this.opts.serverSideFiltering()) {
|
|
1670
1982
|
this.opts.onSortChange({ field, direction: null });
|
|
1671
1983
|
}
|
|
@@ -1982,7 +2294,7 @@ class AgridColumnReorderController {
|
|
|
1982
2294
|
const root = header?.closest('.ag-wrapper') ?? header?.ownerDocument;
|
|
1983
2295
|
if (!root)
|
|
1984
2296
|
return [];
|
|
1985
|
-
return
|
|
2297
|
+
return Array.from(root.querySelectorAll('.ag-header-cell[data-col-field]'))
|
|
1986
2298
|
.map(element => {
|
|
1987
2299
|
const field = element.dataset['colField'];
|
|
1988
2300
|
const rect = element.getBoundingClientRect();
|
|
@@ -2155,13 +2467,15 @@ class AgridColumnStateService {
|
|
|
2155
2467
|
constructor(opts) {
|
|
2156
2468
|
this.opts = opts;
|
|
2157
2469
|
}
|
|
2470
|
+
columnsByField = computed(() => new Map(this.opts.colDefs().map(col => [col.field, col])), ...(ngDevMode ? [{ debugName: "columnsByField" }] : /* istanbul ignore next */ []));
|
|
2471
|
+
visibleIndexByField = computed(() => new Map(this.opts.visibleColDefs().map((col, index) => [col.field, index])), ...(ngDevMode ? [{ debugName: "visibleIndexByField" }] : /* istanbul ignore next */ []));
|
|
2158
2472
|
/** Finds a column definition by field name. */
|
|
2159
2473
|
getColDef(field) {
|
|
2160
|
-
return this.
|
|
2474
|
+
return this.columnsByField().get(field);
|
|
2161
2475
|
}
|
|
2162
2476
|
/** Returns a field's zero-based index among visible columns. */
|
|
2163
2477
|
getVisibleColIndex(field) {
|
|
2164
|
-
return this.
|
|
2478
|
+
return this.visibleIndexByField().get(field) ?? -1;
|
|
2165
2479
|
}
|
|
2166
2480
|
/** Converts a visible data-column index to a one-based ARIA index. */
|
|
2167
2481
|
getAriaColIndex(colIndex) {
|
|
@@ -2376,23 +2690,37 @@ class AgridEditController {
|
|
|
2376
2690
|
editingCell = signal(null, ...(ngDevMode ? [{ debugName: "editingCell" }] : /* istanbul ignore next */ []));
|
|
2377
2691
|
currentDraft = signal(null, ...(ngDevMode ? [{ debugName: "currentDraft" }] : /* istanbul ignore next */ []));
|
|
2378
2692
|
editSeedChar = signal('', ...(ngDevMode ? [{ debugName: "editSeedChar" }] : /* istanbul ignore next */ []));
|
|
2693
|
+
/** Whether a newly focused text editor should select all text. */
|
|
2694
|
+
selectTextOnEdit = signal(true, ...(ngDevMode ? [{ debugName: "selectTextOnEdit" }] : /* istanbul ignore next */ []));
|
|
2379
2695
|
/** The active cell validation error, or `null` when the last commit was accepted. */
|
|
2380
2696
|
validationError = signal(null, ...(ngDevMode ? [{ debugName: "validationError" }] : /* istanbul ignore next */ []));
|
|
2381
2697
|
constructor(opts) {
|
|
2382
2698
|
this.opts = opts;
|
|
2383
2699
|
}
|
|
2384
|
-
/** Returns whether a
|
|
2385
|
-
isCellEditable(col) {
|
|
2386
|
-
|
|
2700
|
+
/** Returns whether a cell can be edited in the current grid state. */
|
|
2701
|
+
isCellEditable(col, originalIndex) {
|
|
2702
|
+
if (!col || this.opts.readonlyGrid() || col.editable === false)
|
|
2703
|
+
return false;
|
|
2704
|
+
if (originalIndex === undefined || !col.cellReadonly)
|
|
2705
|
+
return true;
|
|
2706
|
+
const row = this.opts.dataSource().getRow(originalIndex);
|
|
2707
|
+
if (!row)
|
|
2708
|
+
return false;
|
|
2709
|
+
return !col.cellReadonly({
|
|
2710
|
+
row,
|
|
2711
|
+
value: row[col.field],
|
|
2712
|
+
column: col,
|
|
2713
|
+
originalIndex,
|
|
2714
|
+
});
|
|
2387
2715
|
}
|
|
2388
2716
|
/** Replaces the value currently staged by the active editor. */
|
|
2389
2717
|
setDraft(value) {
|
|
2390
2718
|
this.currentDraft.set(value);
|
|
2391
2719
|
}
|
|
2392
2720
|
/** Starts editing a cell, optionally seeded by a typed character. */
|
|
2393
|
-
start(originalIndex, colIndex, seedChar) {
|
|
2721
|
+
start(originalIndex, colIndex, seedChar, selectText = true) {
|
|
2394
2722
|
const col = this.opts.visibleColDefs()[colIndex];
|
|
2395
|
-
if (!this.isCellEditable(col))
|
|
2723
|
+
if (!this.isCellEditable(col, originalIndex))
|
|
2396
2724
|
return;
|
|
2397
2725
|
this.validationError.set(null);
|
|
2398
2726
|
const currentValue = this.opts.dataSource().getRow(originalIndex)[col.field];
|
|
@@ -2410,6 +2738,7 @@ class AgridEditController {
|
|
|
2410
2738
|
}
|
|
2411
2739
|
this.currentDraft.set(initialDraft);
|
|
2412
2740
|
this.editSeedChar.set(seedChar);
|
|
2741
|
+
this.selectTextOnEdit.set(selectText);
|
|
2413
2742
|
this.editingCell.set({ rowIndex: originalIndex, colIndex });
|
|
2414
2743
|
const displayIndex = this.opts.findDisplayIndex(originalIndex);
|
|
2415
2744
|
if (displayIndex >= 0)
|
|
@@ -2430,6 +2759,10 @@ class AgridEditController {
|
|
|
2430
2759
|
return true;
|
|
2431
2760
|
}
|
|
2432
2761
|
const row = this.opts.dataSource().getRow(position.rowIndex);
|
|
2762
|
+
if (!this.isCellEditable(col, position.rowIndex)) {
|
|
2763
|
+
this.cancel();
|
|
2764
|
+
return true;
|
|
2765
|
+
}
|
|
2433
2766
|
const oldValue = row[col.field];
|
|
2434
2767
|
const newValue = this.currentDraft();
|
|
2435
2768
|
if (oldValue !== newValue) {
|
|
@@ -2459,7 +2792,7 @@ class AgridEditController {
|
|
|
2459
2792
|
*/
|
|
2460
2793
|
setCellValue(rowIndex, colIndex, newValue) {
|
|
2461
2794
|
const col = this.opts.visibleColDefs()[colIndex];
|
|
2462
|
-
if (!this.isCellEditable(col))
|
|
2795
|
+
if (!this.isCellEditable(col, rowIndex))
|
|
2463
2796
|
return false;
|
|
2464
2797
|
const row = this.opts.dataSource().getRow(rowIndex);
|
|
2465
2798
|
const oldValue = row[col.field];
|
|
@@ -2529,6 +2862,7 @@ class AgridEditController {
|
|
|
2529
2862
|
clearEditState() {
|
|
2530
2863
|
this.editingCell.set(null);
|
|
2531
2864
|
this.editSeedChar.set('');
|
|
2865
|
+
this.selectTextOnEdit.set(true);
|
|
2532
2866
|
}
|
|
2533
2867
|
}
|
|
2534
2868
|
|
|
@@ -2543,15 +2877,17 @@ class AgridFindController {
|
|
|
2543
2877
|
if (!query)
|
|
2544
2878
|
return [];
|
|
2545
2879
|
const cols = this.opts.visibleColDefs();
|
|
2880
|
+
const rows = this.opts.dataSource().rows();
|
|
2546
2881
|
const matches = [];
|
|
2547
|
-
this.opts.
|
|
2548
|
-
|
|
2882
|
+
this.opts.filteredSortedIndices().forEach(rowIndex => {
|
|
2883
|
+
const row = rows[rowIndex];
|
|
2884
|
+
if (!row)
|
|
2549
2885
|
return;
|
|
2550
2886
|
for (let colIndex = 0; colIndex < cols.length; colIndex++) {
|
|
2551
2887
|
const col = cols[colIndex];
|
|
2552
|
-
const value = getDisplayForField(col,
|
|
2888
|
+
const value = getDisplayForField(col, row[col.field], this.opts.locale()).toLowerCase();
|
|
2553
2889
|
if (value.includes(query)) {
|
|
2554
|
-
matches.push({ rowIndex
|
|
2890
|
+
matches.push({ rowIndex, colIndex });
|
|
2555
2891
|
}
|
|
2556
2892
|
}
|
|
2557
2893
|
});
|
|
@@ -2562,6 +2898,9 @@ class AgridFindController {
|
|
|
2562
2898
|
}
|
|
2563
2899
|
/** Opens the find panel. */
|
|
2564
2900
|
show() {
|
|
2901
|
+
this.opts.selectedCell.set(null);
|
|
2902
|
+
this.opts.selectedRange.set(null);
|
|
2903
|
+
this.activeIndex.set(-1);
|
|
2565
2904
|
this.open.set(true);
|
|
2566
2905
|
}
|
|
2567
2906
|
/** Closes the find panel and returns focus to the grid. */
|
|
@@ -2569,11 +2908,10 @@ class AgridFindController {
|
|
|
2569
2908
|
this.open.set(false);
|
|
2570
2909
|
this.opts.focusGrid();
|
|
2571
2910
|
}
|
|
2572
|
-
/** Replaces the search query
|
|
2911
|
+
/** Replaces the search query without moving focus away from the find input. */
|
|
2573
2912
|
setQuery(value) {
|
|
2574
2913
|
this.query.set(value);
|
|
2575
2914
|
this.activeIndex.set(-1);
|
|
2576
|
-
this.goToMatch(1);
|
|
2577
2915
|
}
|
|
2578
2916
|
/** Selects the next or previous match, wrapping at either end. */
|
|
2579
2917
|
goToMatch(direction) {
|
|
@@ -2590,7 +2928,7 @@ class AgridFindController {
|
|
|
2590
2928
|
const match = matches[next];
|
|
2591
2929
|
this.opts.selectedRange.set(null);
|
|
2592
2930
|
this.opts.selectedCell.set({ rowIndex: match.rowIndex, colIndex: match.colIndex });
|
|
2593
|
-
this.opts.
|
|
2931
|
+
this.opts.revealMatch(match.rowIndex, match.colIndex);
|
|
2594
2932
|
}
|
|
2595
2933
|
/** Returns whether a cell matches the current query. */
|
|
2596
2934
|
isMatchCell(originalIndex, colIndex) {
|
|
@@ -2647,10 +2985,10 @@ class AgridFindPanelComponent {
|
|
|
2647
2985
|
this.close.emit();
|
|
2648
2986
|
}
|
|
2649
2987
|
}
|
|
2650
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
2651
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.
|
|
2988
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridFindPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2989
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.17", type: AgridFindPanelComponent, isStandalone: true, selector: "agrid-find-panel", inputs: { localeText: { classPropertyName: "localeText", publicName: "localeText", isSignal: true, isRequired: false, transformFunction: null }, query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null }, matchCount: { classPropertyName: "matchCount", publicName: "matchCount", isSignal: true, isRequired: false, transformFunction: null }, activeIndex: { classPropertyName: "activeIndex", publicName: "activeIndex", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { queryChange: "queryChange", next: "next", previous: "previous", close: "close" }, viewQueries: [{ propertyName: "findInput", first: true, predicate: ["findInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"ag-find-panel\" role=\"search\" (click)=\"$event.stopPropagation()\">\n <input\n #findInput\n class=\"ag-find-input\"\n [value]=\"query()\"\n (input)=\"queryChange.emit($any($event.target).value)\"\n (keydown)=\"onKeyDown($event)\"\n [placeholder]=\"localeText().findPlaceholder\"\n [attr.aria-label]=\"localeText().findPlaceholder\"\n />\n <span class=\"ag-find-count\" aria-live=\"polite\">{{ countLabel() }}</span>\n <button class=\"ag-find-btn\" [title]=\"localeText().previous\" [attr.aria-label]=\"localeText().previous\"\n (click)=\"previous.emit()\">\u2191</button>\n <button class=\"ag-find-btn\" [title]=\"localeText().next\" [attr.aria-label]=\"localeText().next\"\n (click)=\"next.emit()\">\u2193</button>\n <button class=\"ag-find-btn\" [title]=\"localeText().close\" [attr.aria-label]=\"localeText().close\"\n (click)=\"close.emit()\">\u2715</button>\n</div>\n", styles: [".ag-find-panel{position:absolute;top:8px;right:8px;z-index:1100;display:flex;align-items:center;gap:4px;padding:5px;background:var(--agrid-color-bg);border:1px solid var(--agrid-color-border);border-radius:4px;box-shadow:0 8px 24px var(--agrid-color-shadow)}.ag-find-input{width:160px;height:24px;border:1px solid var(--agrid-color-border);border-radius:3px;outline:none;font:inherit;font-size:12px;padding:0 6px;box-sizing:border-box}.ag-find-input:focus{border-color:var(--agrid-color-accent)}.ag-find-count{min-width:46px;text-align:center;font-size:11px;color:var(--agrid-color-text-muted)}.ag-find-btn{width:24px;height:24px;border:1px solid transparent;border-radius:3px;background:none;color:var(--agrid-color-text-muted);cursor:pointer;font:inherit;font-size:12px;line-height:1;padding:0}.ag-find-btn:hover{background:var(--agrid-color-bg-muted);border-color:var(--agrid-color-border);color:var(--agrid-color-text)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2652
2990
|
}
|
|
2653
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
2991
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridFindPanelComponent, decorators: [{
|
|
2654
2992
|
type: Component,
|
|
2655
2993
|
args: [{ selector: 'agrid-find-panel', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ag-find-panel\" role=\"search\" (click)=\"$event.stopPropagation()\">\n <input\n #findInput\n class=\"ag-find-input\"\n [value]=\"query()\"\n (input)=\"queryChange.emit($any($event.target).value)\"\n (keydown)=\"onKeyDown($event)\"\n [placeholder]=\"localeText().findPlaceholder\"\n [attr.aria-label]=\"localeText().findPlaceholder\"\n />\n <span class=\"ag-find-count\" aria-live=\"polite\">{{ countLabel() }}</span>\n <button class=\"ag-find-btn\" [title]=\"localeText().previous\" [attr.aria-label]=\"localeText().previous\"\n (click)=\"previous.emit()\">\u2191</button>\n <button class=\"ag-find-btn\" [title]=\"localeText().next\" [attr.aria-label]=\"localeText().next\"\n (click)=\"next.emit()\">\u2193</button>\n <button class=\"ag-find-btn\" [title]=\"localeText().close\" [attr.aria-label]=\"localeText().close\"\n (click)=\"close.emit()\">\u2715</button>\n</div>\n", styles: [".ag-find-panel{position:absolute;top:8px;right:8px;z-index:1100;display:flex;align-items:center;gap:4px;padding:5px;background:var(--agrid-color-bg);border:1px solid var(--agrid-color-border);border-radius:4px;box-shadow:0 8px 24px var(--agrid-color-shadow)}.ag-find-input{width:160px;height:24px;border:1px solid var(--agrid-color-border);border-radius:3px;outline:none;font:inherit;font-size:12px;padding:0 6px;box-sizing:border-box}.ag-find-input:focus{border-color:var(--agrid-color-accent)}.ag-find-count{min-width:46px;text-align:center;font-size:11px;color:var(--agrid-color-text-muted)}.ag-find-btn{width:24px;height:24px;border:1px solid transparent;border-radius:3px;background:none;color:var(--agrid-color-text-muted);cursor:pointer;font:inherit;font-size:12px;line-height:1;padding:0}.ag-find-btn:hover{background:var(--agrid-color-bg-muted);border-color:var(--agrid-color-border);color:var(--agrid-color-text)}\n"] }]
|
|
2656
2994
|
}], propDecorators: { localeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "localeText", required: false }] }], query: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }], matchCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "matchCount", required: false }] }], activeIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeIndex", required: false }] }], queryChange: [{ type: i0.Output, args: ["queryChange"] }], next: [{ type: i0.Output, args: ["next"] }], previous: [{ type: i0.Output, args: ["previous"] }], close: [{ type: i0.Output, args: ["close"] }], findInput: [{ type: i0.ViewChild, args: ['findInput', { isSignal: true }] }] } });
|
|
@@ -2814,7 +3152,7 @@ class AgridNavigationController {
|
|
|
2814
3152
|
}
|
|
2815
3153
|
/** Handles grid-level navigation, editing, history, find, and add-row shortcuts. */
|
|
2816
3154
|
handleKeyDown(event) {
|
|
2817
|
-
if (event.target?.closest('.ag-sidebar, .ag-filter-input, .ag-filter-menu'))
|
|
3155
|
+
if (event.target?.closest('.ag-sidebar, .ag-filter-input, .ag-filter-menu, .ag-menu-bar'))
|
|
2818
3156
|
return;
|
|
2819
3157
|
if ((event.ctrlKey || event.metaKey) && !event.altKey && event.key.toLowerCase() === 'f') {
|
|
2820
3158
|
event.preventDefault();
|
|
@@ -2833,6 +3171,16 @@ class AgridNavigationController {
|
|
|
2833
3171
|
return;
|
|
2834
3172
|
}
|
|
2835
3173
|
}
|
|
3174
|
+
const sel = this.opts.selectedCell();
|
|
3175
|
+
if (event.key === 'Enter'
|
|
3176
|
+
&& (event.ctrlKey || event.metaKey)
|
|
3177
|
+
&& !event.altKey
|
|
3178
|
+
&& !event.shiftKey
|
|
3179
|
+
&& sel
|
|
3180
|
+
&& this.opts.toggleTreeCell(sel.rowIndex, sel.colIndex)) {
|
|
3181
|
+
event.preventDefault();
|
|
3182
|
+
return;
|
|
3183
|
+
}
|
|
2836
3184
|
if (this.opts.editingCell()) {
|
|
2837
3185
|
switch (event.key) {
|
|
2838
3186
|
case 'Tab':
|
|
@@ -2843,7 +3191,7 @@ class AgridNavigationController {
|
|
|
2843
3191
|
case 'Enter':
|
|
2844
3192
|
event.preventDefault();
|
|
2845
3193
|
if (this.opts.commitEdit())
|
|
2846
|
-
this.
|
|
3194
|
+
this.applyEnterEditAction(event.shiftKey);
|
|
2847
3195
|
break;
|
|
2848
3196
|
case 'Escape':
|
|
2849
3197
|
event.preventDefault();
|
|
@@ -2853,7 +3201,6 @@ class AgridNavigationController {
|
|
|
2853
3201
|
}
|
|
2854
3202
|
return;
|
|
2855
3203
|
}
|
|
2856
|
-
const sel = this.opts.selectedCell();
|
|
2857
3204
|
const isOnAddRow = this.opts.allowAddRows()
|
|
2858
3205
|
&& !this.opts.autoAddRows()
|
|
2859
3206
|
&& sel?.rowIndex === this.opts.dataSource().length;
|
|
@@ -2879,6 +3226,14 @@ class AgridNavigationController {
|
|
|
2879
3226
|
this.moveSelection(0, event.shiftKey ? -1 : 1);
|
|
2880
3227
|
break;
|
|
2881
3228
|
case 'Enter':
|
|
3229
|
+
event.preventDefault();
|
|
3230
|
+
if (sel) {
|
|
3231
|
+
if (isOnAddRow)
|
|
3232
|
+
this.addRowAndSelect();
|
|
3233
|
+
else
|
|
3234
|
+
this.opts.startEdit(sel.rowIndex, sel.colIndex, '');
|
|
3235
|
+
}
|
|
3236
|
+
break;
|
|
2882
3237
|
case 'F2':
|
|
2883
3238
|
event.preventDefault();
|
|
2884
3239
|
if (sel) {
|
|
@@ -2897,6 +3252,63 @@ class AgridNavigationController {
|
|
|
2897
3252
|
}
|
|
2898
3253
|
}
|
|
2899
3254
|
}
|
|
3255
|
+
applyEnterEditAction(reverse) {
|
|
3256
|
+
switch (this.opts.enterEditAction()) {
|
|
3257
|
+
case 'nextColumn':
|
|
3258
|
+
this.moveToNextEditableColumn(reverse ? -1 : 1);
|
|
3259
|
+
break;
|
|
3260
|
+
case 'nextRow':
|
|
3261
|
+
this.moveSelection(reverse ? -1 : 1, 0);
|
|
3262
|
+
break;
|
|
3263
|
+
case 'nothing':
|
|
3264
|
+
this.opts.focusGrid();
|
|
3265
|
+
break;
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3268
|
+
moveToNextEditableColumn(direction) {
|
|
3269
|
+
const items = this.opts.filteredItems();
|
|
3270
|
+
const cols = this.opts.visibleColDefs();
|
|
3271
|
+
const selected = this.opts.selectedCell();
|
|
3272
|
+
if (!selected || items.length === 0 || cols.length === 0) {
|
|
3273
|
+
this.opts.focusGrid();
|
|
3274
|
+
return;
|
|
3275
|
+
}
|
|
3276
|
+
const currentDisplayIndex = this.selectedDisplayIndex();
|
|
3277
|
+
if (currentDisplayIndex < 0) {
|
|
3278
|
+
this.opts.focusGrid();
|
|
3279
|
+
return;
|
|
3280
|
+
}
|
|
3281
|
+
let displayIndex = currentDisplayIndex;
|
|
3282
|
+
let colIndex = selected.colIndex + direction;
|
|
3283
|
+
while (displayIndex >= 0 && displayIndex < items.length) {
|
|
3284
|
+
while (colIndex >= 0 && colIndex < cols.length) {
|
|
3285
|
+
const item = items[displayIndex];
|
|
3286
|
+
const col = cols[colIndex];
|
|
3287
|
+
if (isDataRowItem(item) && this.opts.isCellEditable(col, item.originalIndex)) {
|
|
3288
|
+
this.opts.selectedRange.set(null);
|
|
3289
|
+
this.opts.selectedCell.set({ rowIndex: item.originalIndex, colIndex });
|
|
3290
|
+
this.scrollToKeepVisible(displayIndex, colIndex);
|
|
3291
|
+
if (this.isTextColumn(col))
|
|
3292
|
+
this.opts.startEdit(item.originalIndex, colIndex, '', false);
|
|
3293
|
+
else
|
|
3294
|
+
this.opts.focusGrid();
|
|
3295
|
+
return;
|
|
3296
|
+
}
|
|
3297
|
+
colIndex += direction;
|
|
3298
|
+
}
|
|
3299
|
+
displayIndex += direction;
|
|
3300
|
+
colIndex = direction > 0 ? 0 : cols.length - 1;
|
|
3301
|
+
while (displayIndex >= 0
|
|
3302
|
+
&& displayIndex < items.length
|
|
3303
|
+
&& isGroupHeaderItem(items[displayIndex])) {
|
|
3304
|
+
displayIndex += direction;
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
this.opts.focusGrid();
|
|
3308
|
+
}
|
|
3309
|
+
isTextColumn(col) {
|
|
3310
|
+
return !col.values?.length && (col.type === undefined || col.type === 'text');
|
|
3311
|
+
}
|
|
2900
3312
|
/** Finds a data row's current index in the projected display items. */
|
|
2901
3313
|
findDisplayIndex(originalIndex) {
|
|
2902
3314
|
return this.opts.filteredItems().findIndex(item => isDataRowItem(item) && item.originalIndex === originalIndex);
|
|
@@ -3046,6 +3458,10 @@ class AgridPresentationService {
|
|
|
3046
3458
|
getCellClass(col, value, row) {
|
|
3047
3459
|
return col.cellClass?.({ value, row }) ?? '';
|
|
3048
3460
|
}
|
|
3461
|
+
/** Resolves dynamic CSS classes configured for a whole data row. */
|
|
3462
|
+
getRowClass(row, index) {
|
|
3463
|
+
return this.opts.getRowClass?.()?.({ row, index }) ?? '';
|
|
3464
|
+
}
|
|
3049
3465
|
/** Returns the compact label for a built-in aggregate. */
|
|
3050
3466
|
getAggregateLabel(col) {
|
|
3051
3467
|
const aggregate = this.opts.control()?.aggregates()[col.field] ?? col.aggregate;
|
|
@@ -3085,6 +3501,91 @@ class AgridPresentationService {
|
|
|
3085
3501
|
}
|
|
3086
3502
|
}
|
|
3087
3503
|
|
|
3504
|
+
/** Internal renderer for provider-configured grid commands and dropdown items. */
|
|
3505
|
+
class AgridMenuBarComponent {
|
|
3506
|
+
items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
3507
|
+
context = input.required(...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
|
|
3508
|
+
label = input('Actions', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
3509
|
+
openItemId = input(null, ...(ngDevMode ? [{ debugName: "openItemId" }] : /* istanbul ignore next */ []));
|
|
3510
|
+
action = output();
|
|
3511
|
+
openItemIdChange = output();
|
|
3512
|
+
resolveState(state, fallback) {
|
|
3513
|
+
if (typeof state === 'function')
|
|
3514
|
+
return state(this.context());
|
|
3515
|
+
return state ?? fallback;
|
|
3516
|
+
}
|
|
3517
|
+
isActive(item) {
|
|
3518
|
+
return this.resolveState(item.active, false);
|
|
3519
|
+
}
|
|
3520
|
+
isDisabled(item) {
|
|
3521
|
+
return this.resolveState(item.disabled, false);
|
|
3522
|
+
}
|
|
3523
|
+
visibleChildren(item) {
|
|
3524
|
+
return (item.items ?? []).filter(child => this.resolveState(child.visible, true));
|
|
3525
|
+
}
|
|
3526
|
+
runAction(event, item) {
|
|
3527
|
+
event.stopPropagation();
|
|
3528
|
+
if (!this.resolveState(item.visible, true) || this.isDisabled(item))
|
|
3529
|
+
return;
|
|
3530
|
+
this.action.emit(item.id);
|
|
3531
|
+
this.openItemIdChange.emit(null);
|
|
3532
|
+
}
|
|
3533
|
+
toggleMenu(event, item) {
|
|
3534
|
+
event.preventDefault();
|
|
3535
|
+
event.stopPropagation();
|
|
3536
|
+
if (this.isDisabled(item) || this.visibleChildren(item).length === 0)
|
|
3537
|
+
return;
|
|
3538
|
+
this.openItemIdChange.emit(this.openItemId() === item.id ? null : item.id);
|
|
3539
|
+
}
|
|
3540
|
+
onTriggerKeydown(event, item) {
|
|
3541
|
+
if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp')
|
|
3542
|
+
return;
|
|
3543
|
+
event.preventDefault();
|
|
3544
|
+
event.stopPropagation();
|
|
3545
|
+
if (this.isDisabled(item) || this.visibleChildren(item).length === 0)
|
|
3546
|
+
return;
|
|
3547
|
+
this.openItemIdChange.emit(item.id);
|
|
3548
|
+
const group = event.currentTarget.closest('.ag-menu-bar-group');
|
|
3549
|
+
setTimeout(() => {
|
|
3550
|
+
const enabled = group?.querySelectorAll('.ag-menu-bar-dropdown [role="menuitem"]:not(:disabled)');
|
|
3551
|
+
const target = event.key === 'ArrowUp' ? enabled?.[enabled.length - 1] : enabled?.[0];
|
|
3552
|
+
target?.focus();
|
|
3553
|
+
});
|
|
3554
|
+
}
|
|
3555
|
+
onMenuKeydown(event) {
|
|
3556
|
+
const menu = event.currentTarget;
|
|
3557
|
+
const items = Array.from(menu.querySelectorAll('[role="menuitem"]:not(:disabled)'));
|
|
3558
|
+
if (event.key === 'Escape') {
|
|
3559
|
+
event.preventDefault();
|
|
3560
|
+
event.stopPropagation();
|
|
3561
|
+
this.openItemIdChange.emit(null);
|
|
3562
|
+
menu.closest('.ag-menu-bar-group')
|
|
3563
|
+
?.querySelector('.ag-menu-bar-trigger')
|
|
3564
|
+
?.focus();
|
|
3565
|
+
return;
|
|
3566
|
+
}
|
|
3567
|
+
if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key) || items.length === 0)
|
|
3568
|
+
return;
|
|
3569
|
+
event.preventDefault();
|
|
3570
|
+
event.stopPropagation();
|
|
3571
|
+
const current = items.indexOf(event.target);
|
|
3572
|
+
const next = event.key === 'Home'
|
|
3573
|
+
? 0
|
|
3574
|
+
: event.key === 'End'
|
|
3575
|
+
? items.length - 1
|
|
3576
|
+
: event.key === 'ArrowDown'
|
|
3577
|
+
? (current + 1 + items.length) % items.length
|
|
3578
|
+
: (current - 1 + items.length) % items.length;
|
|
3579
|
+
items[next].focus();
|
|
3580
|
+
}
|
|
3581
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridMenuBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3582
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AgridMenuBarComponent, isStandalone: true, selector: "agrid-menu-bar", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, openItemId: { classPropertyName: "openItemId", publicName: "openItemId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { action: "action", openItemIdChange: "openItemIdChange" }, ngImport: i0, template: "<div class=\"ag-menu-bar\" role=\"toolbar\" [attr.aria-label]=\"label()\">\n @for (item of items(); track item.id) {\n @let children = visibleChildren(item);\n <div class=\"ag-menu-bar-group\" [attr.data-menu-id]=\"item.id\">\n <button type=\"button\" class=\"ag-menu-bar-button\"\n [class.ag-menu-bar-button--active]=\"isActive(item)\"\n [class.ag-menu-bar-button--split]=\"children.length > 0\"\n [disabled]=\"isDisabled(item)\"\n [attr.aria-pressed]=\"item.active !== undefined ? isActive(item) : null\"\n (click)=\"runAction($event, item)\">\n @if (item.icon) {\n <span class=\"ag-menu-bar-icon\" aria-hidden=\"true\">{{ item.icon }}</span>\n }\n <span class=\"ag-menu-bar-label\">{{ item.label }}</span>\n </button>\n @if (children.length > 0) {\n <button type=\"button\" class=\"ag-menu-bar-trigger\"\n [class.ag-menu-bar-trigger--open]=\"openItemId() === item.id\"\n [attr.aria-label]=\"item.label + ': ' + label()\"\n [attr.aria-expanded]=\"openItemId() === item.id\" aria-haspopup=\"menu\"\n [disabled]=\"isDisabled(item)\"\n (click)=\"toggleMenu($event, item)\"\n (keydown)=\"onTriggerKeydown($event, item)\">▾</button>\n @if (openItemId() === item.id) {\n <div class=\"ag-menu-bar-dropdown\" role=\"menu\" [attr.aria-label]=\"item.label\"\n (click)=\"$event.stopPropagation()\" (keydown)=\"onMenuKeydown($event)\">\n @for (child of children; track child.id) {\n <button type=\"button\" class=\"ag-menu-bar-dropdown-item\" role=\"menuitem\"\n [class.ag-menu-bar-dropdown-item--active]=\"isActive(child)\"\n [disabled]=\"isDisabled(child)\"\n [attr.aria-current]=\"isActive(child) ? 'true' : null\"\n (click)=\"runAction($event, child)\">\n <span class=\"ag-menu-bar-dropdown-check\" aria-hidden=\"true\">{{ isActive(child) ? '✓' : '' }}</span>\n @if (child.icon) {\n <span class=\"ag-menu-bar-icon\" aria-hidden=\"true\">{{ child.icon }}</span>\n }\n <span class=\"ag-menu-bar-label\">{{ child.label }}</span>\n </button>\n }\n </div>\n }\n }\n </div>\n }\n</div>\n", styles: [":host{display:block;min-width:0}.ag-menu-bar{display:flex;align-items:center;flex-wrap:wrap;gap:4px;min-width:0}.ag-menu-bar-group{position:relative;display:inline-flex;align-items:stretch}.ag-menu-bar-button,.ag-menu-bar-trigger{display:inline-flex;align-items:center;justify-content:center;height:30px;border:1px solid var(--agrid-color-border);background:var(--agrid-color-bg);color:var(--agrid-color-text);cursor:pointer;font:inherit;transition:transform .12s cubic-bezier(.23,1,.32,1)}.ag-menu-bar-button{gap:6px;min-width:0;padding:0 10px;border-radius:4px;font-weight:500;white-space:nowrap}.ag-menu-bar-button--split{border-radius:4px 0 0 4px}.ag-menu-bar-trigger{width:28px;padding:0;margin-left:-1px;border-radius:0 4px 4px 0;color:var(--agrid-color-text-muted);font-size:10px}.ag-menu-bar-button--active,.ag-menu-bar-trigger--open{position:relative;z-index:1;border-color:var(--agrid-color-accent-border);background:var(--agrid-color-accent-subtle);color:var(--agrid-color-accent-fg)}.ag-menu-bar-button:focus-visible,.ag-menu-bar-trigger:focus-visible,.ag-menu-bar-dropdown-item:focus-visible{position:relative;z-index:2;outline:2px solid var(--agrid-color-accent);outline-offset:1px}.ag-menu-bar-button:active:not(:disabled),.ag-menu-bar-trigger:active:not(:disabled),.ag-menu-bar-dropdown-item:active:not(:disabled){transform:scale(.97)}.ag-menu-bar-button:disabled,.ag-menu-bar-trigger:disabled,.ag-menu-bar-dropdown-item:disabled{opacity:.48;cursor:not-allowed}.ag-menu-bar-icon{display:inline-flex;flex:0 0 auto;align-items:center;justify-content:center;min-width:14px;line-height:1}.ag-menu-bar-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ag-menu-bar-dropdown{position:absolute;z-index:12;top:calc(100% + 4px);left:0;display:flex;flex-direction:column;min-width:184px;max-width:min(280px,calc(100vw - 24px));padding:4px;border:1px solid var(--agrid-color-border);border-radius:6px;background:var(--agrid-color-bg);box-shadow:0 10px 28px var(--agrid-color-shadow);transform-origin:top left}.ag-menu-bar-group:last-child .ag-menu-bar-dropdown{right:0;left:auto;transform-origin:top right}.ag-menu-bar-dropdown-item{display:flex;align-items:center;gap:7px;width:100%;min-height:30px;padding:4px 8px;border:0;border-radius:4px;background:transparent;color:var(--agrid-color-text);cursor:pointer;font:inherit;text-align:left;transition:transform .12s cubic-bezier(.23,1,.32,1)}.ag-menu-bar-dropdown-item--active{background:var(--agrid-color-accent-subtle);color:var(--agrid-color-accent-fg)}.ag-menu-bar-dropdown-check{flex:0 0 12px;color:var(--agrid-color-accent);font-size:11px;font-weight:700;text-align:center}@media(hover:hover)and (pointer:fine){.ag-menu-bar-button:hover:not(:disabled),.ag-menu-bar-trigger:hover:not(:disabled),.ag-menu-bar-dropdown-item:hover:not(:disabled){background:var(--agrid-color-bg-muted)}.ag-menu-bar-button--active:hover:not(:disabled),.ag-menu-bar-trigger--open:hover:not(:disabled),.ag-menu-bar-dropdown-item--active:hover:not(:disabled){background:var(--agrid-color-accent-subtle)}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3583
|
+
}
|
|
3584
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridMenuBarComponent, decorators: [{
|
|
3585
|
+
type: Component,
|
|
3586
|
+
args: [{ selector: 'agrid-menu-bar', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ag-menu-bar\" role=\"toolbar\" [attr.aria-label]=\"label()\">\n @for (item of items(); track item.id) {\n @let children = visibleChildren(item);\n <div class=\"ag-menu-bar-group\" [attr.data-menu-id]=\"item.id\">\n <button type=\"button\" class=\"ag-menu-bar-button\"\n [class.ag-menu-bar-button--active]=\"isActive(item)\"\n [class.ag-menu-bar-button--split]=\"children.length > 0\"\n [disabled]=\"isDisabled(item)\"\n [attr.aria-pressed]=\"item.active !== undefined ? isActive(item) : null\"\n (click)=\"runAction($event, item)\">\n @if (item.icon) {\n <span class=\"ag-menu-bar-icon\" aria-hidden=\"true\">{{ item.icon }}</span>\n }\n <span class=\"ag-menu-bar-label\">{{ item.label }}</span>\n </button>\n @if (children.length > 0) {\n <button type=\"button\" class=\"ag-menu-bar-trigger\"\n [class.ag-menu-bar-trigger--open]=\"openItemId() === item.id\"\n [attr.aria-label]=\"item.label + ': ' + label()\"\n [attr.aria-expanded]=\"openItemId() === item.id\" aria-haspopup=\"menu\"\n [disabled]=\"isDisabled(item)\"\n (click)=\"toggleMenu($event, item)\"\n (keydown)=\"onTriggerKeydown($event, item)\">▾</button>\n @if (openItemId() === item.id) {\n <div class=\"ag-menu-bar-dropdown\" role=\"menu\" [attr.aria-label]=\"item.label\"\n (click)=\"$event.stopPropagation()\" (keydown)=\"onMenuKeydown($event)\">\n @for (child of children; track child.id) {\n <button type=\"button\" class=\"ag-menu-bar-dropdown-item\" role=\"menuitem\"\n [class.ag-menu-bar-dropdown-item--active]=\"isActive(child)\"\n [disabled]=\"isDisabled(child)\"\n [attr.aria-current]=\"isActive(child) ? 'true' : null\"\n (click)=\"runAction($event, child)\">\n <span class=\"ag-menu-bar-dropdown-check\" aria-hidden=\"true\">{{ isActive(child) ? '✓' : '' }}</span>\n @if (child.icon) {\n <span class=\"ag-menu-bar-icon\" aria-hidden=\"true\">{{ child.icon }}</span>\n }\n <span class=\"ag-menu-bar-label\">{{ child.label }}</span>\n </button>\n }\n </div>\n }\n }\n </div>\n }\n</div>\n", styles: [":host{display:block;min-width:0}.ag-menu-bar{display:flex;align-items:center;flex-wrap:wrap;gap:4px;min-width:0}.ag-menu-bar-group{position:relative;display:inline-flex;align-items:stretch}.ag-menu-bar-button,.ag-menu-bar-trigger{display:inline-flex;align-items:center;justify-content:center;height:30px;border:1px solid var(--agrid-color-border);background:var(--agrid-color-bg);color:var(--agrid-color-text);cursor:pointer;font:inherit;transition:transform .12s cubic-bezier(.23,1,.32,1)}.ag-menu-bar-button{gap:6px;min-width:0;padding:0 10px;border-radius:4px;font-weight:500;white-space:nowrap}.ag-menu-bar-button--split{border-radius:4px 0 0 4px}.ag-menu-bar-trigger{width:28px;padding:0;margin-left:-1px;border-radius:0 4px 4px 0;color:var(--agrid-color-text-muted);font-size:10px}.ag-menu-bar-button--active,.ag-menu-bar-trigger--open{position:relative;z-index:1;border-color:var(--agrid-color-accent-border);background:var(--agrid-color-accent-subtle);color:var(--agrid-color-accent-fg)}.ag-menu-bar-button:focus-visible,.ag-menu-bar-trigger:focus-visible,.ag-menu-bar-dropdown-item:focus-visible{position:relative;z-index:2;outline:2px solid var(--agrid-color-accent);outline-offset:1px}.ag-menu-bar-button:active:not(:disabled),.ag-menu-bar-trigger:active:not(:disabled),.ag-menu-bar-dropdown-item:active:not(:disabled){transform:scale(.97)}.ag-menu-bar-button:disabled,.ag-menu-bar-trigger:disabled,.ag-menu-bar-dropdown-item:disabled{opacity:.48;cursor:not-allowed}.ag-menu-bar-icon{display:inline-flex;flex:0 0 auto;align-items:center;justify-content:center;min-width:14px;line-height:1}.ag-menu-bar-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ag-menu-bar-dropdown{position:absolute;z-index:12;top:calc(100% + 4px);left:0;display:flex;flex-direction:column;min-width:184px;max-width:min(280px,calc(100vw - 24px));padding:4px;border:1px solid var(--agrid-color-border);border-radius:6px;background:var(--agrid-color-bg);box-shadow:0 10px 28px var(--agrid-color-shadow);transform-origin:top left}.ag-menu-bar-group:last-child .ag-menu-bar-dropdown{right:0;left:auto;transform-origin:top right}.ag-menu-bar-dropdown-item{display:flex;align-items:center;gap:7px;width:100%;min-height:30px;padding:4px 8px;border:0;border-radius:4px;background:transparent;color:var(--agrid-color-text);cursor:pointer;font:inherit;text-align:left;transition:transform .12s cubic-bezier(.23,1,.32,1)}.ag-menu-bar-dropdown-item--active{background:var(--agrid-color-accent-subtle);color:var(--agrid-color-accent-fg)}.ag-menu-bar-dropdown-check{flex:0 0 12px;color:var(--agrid-color-accent);font-size:11px;font-weight:700;text-align:center}@media(hover:hover)and (pointer:fine){.ag-menu-bar-button:hover:not(:disabled),.ag-menu-bar-trigger:hover:not(:disabled),.ag-menu-bar-dropdown-item:hover:not(:disabled){background:var(--agrid-color-bg-muted)}.ag-menu-bar-button--active:hover:not(:disabled),.ag-menu-bar-trigger--open:hover:not(:disabled),.ag-menu-bar-dropdown-item--active:hover:not(:disabled){background:var(--agrid-color-accent-subtle)}}\n"] }]
|
|
3587
|
+
}], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], openItemId: [{ type: i0.Input, args: [{ isSignal: true, alias: "openItemId", required: false }] }], action: [{ type: i0.Output, args: ["action"] }], openItemIdChange: [{ type: i0.Output, args: ["openItemIdChange"] }] } });
|
|
3588
|
+
|
|
3088
3589
|
/**
|
|
3089
3590
|
* Signal-based container for mutable grid UI state such as column widths and active filters.
|
|
3090
3591
|
*
|
|
@@ -3452,7 +3953,8 @@ class AgridControl {
|
|
|
3452
3953
|
}));
|
|
3453
3954
|
}
|
|
3454
3955
|
/**
|
|
3455
|
-
* Set
|
|
3956
|
+
* Set a column condition filter. Text columns support string operators while number/date
|
|
3957
|
+
* columns support comparison operators.
|
|
3456
3958
|
* Pass `operator: null` (or an empty `operand`) to clear it. `operand2` is only used
|
|
3457
3959
|
* by the `'between'` operator.
|
|
3458
3960
|
*/
|
|
@@ -3755,12 +4257,16 @@ class AgridProvider {
|
|
|
3755
4257
|
filterDebounceMs;
|
|
3756
4258
|
/** Whether the global quick-filter box is shown above the grid. */
|
|
3757
4259
|
enableQuickFilter;
|
|
4260
|
+
/** Commands rendered in the optional menu bar above the column headers. */
|
|
4261
|
+
menuBarItems;
|
|
3758
4262
|
/** Enabled sorting mode. */
|
|
3759
4263
|
sortOption;
|
|
3760
4264
|
/** Toggle auto-add-rows without recreating the provider. @default signal(false) */
|
|
3761
4265
|
autoAddRows;
|
|
3762
4266
|
/** Enabled row-selection mode. */
|
|
3763
4267
|
rowSelection;
|
|
4268
|
+
/** Behavior after pressing Enter while an inline cell editor is active. */
|
|
4269
|
+
enterEditAction;
|
|
3764
4270
|
/** Optional description shown beside each group heading. */
|
|
3765
4271
|
groupDescription;
|
|
3766
4272
|
/** Actions available from group headers. */
|
|
@@ -3777,6 +4283,16 @@ class AgridProvider {
|
|
|
3777
4283
|
emptyText;
|
|
3778
4284
|
/** Whether edits are restricted to the sidebar editor. */
|
|
3779
4285
|
useSidebarEditor;
|
|
4286
|
+
/** Optional callback returning CSS classes for a whole data row. */
|
|
4287
|
+
getRowClass;
|
|
4288
|
+
/** Optional callback designating rows pinned to the top or bottom of the body. */
|
|
4289
|
+
pinRow;
|
|
4290
|
+
/** Whether master/detail expandable detail rows are enabled. */
|
|
4291
|
+
masterDetail;
|
|
4292
|
+
/** Returns the sanitized HTML rendered inside an expanded detail panel. */
|
|
4293
|
+
detailRenderer;
|
|
4294
|
+
/** Fixed height in pixels of an expanded detail panel row. */
|
|
4295
|
+
detailRowHeight;
|
|
3780
4296
|
/** Toggle the loading overlay without recreating the provider. @default signal(false) */
|
|
3781
4297
|
loading;
|
|
3782
4298
|
/** Toggle readonly mode without recreating the provider. @default signal(false) */
|
|
@@ -3801,8 +4317,10 @@ class AgridProvider {
|
|
|
3801
4317
|
this.serverSideFiltering = config.serverSideFiltering ?? false;
|
|
3802
4318
|
this.filterDebounceMs = Math.max(0, config.filterDebounceMs ?? 300);
|
|
3803
4319
|
this.enableQuickFilter = config.enableQuickFilter ?? false;
|
|
4320
|
+
this.menuBarItems = config.menuBarItems ?? [];
|
|
3804
4321
|
this.sortOption = config.sortOption ?? 'multi';
|
|
3805
4322
|
this.rowSelection = config.rowSelection ?? 'none';
|
|
4323
|
+
this.enterEditAction = config.enterEditAction ?? 'nextRow';
|
|
3806
4324
|
this.groupDescription = config.groupDescription ?? null;
|
|
3807
4325
|
this.groupActions = config.groupActions ?? [];
|
|
3808
4326
|
this.cellMenuItems = config.cellMenuItems ?? [];
|
|
@@ -3813,6 +4331,11 @@ class AgridProvider {
|
|
|
3813
4331
|
this.loading = signal(config.loading ?? false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
3814
4332
|
this.readonlyGrid = signal(config.readonly ?? false, ...(ngDevMode ? [{ debugName: "readonlyGrid" }] : /* istanbul ignore next */ []));
|
|
3815
4333
|
this.useSidebarEditor = config.useSidebarEditor ?? false;
|
|
4334
|
+
this.getRowClass = config.getRowClass;
|
|
4335
|
+
this.pinRow = config.pinRow;
|
|
4336
|
+
this.masterDetail = config.masterDetail ?? false;
|
|
4337
|
+
this.detailRenderer = config.detailRenderer;
|
|
4338
|
+
this.detailRowHeight = config.detailRowHeight ?? 200;
|
|
3816
4339
|
}
|
|
3817
4340
|
/** Returns the current reactive row array. */
|
|
3818
4341
|
getGridData() {
|
|
@@ -3852,6 +4375,41 @@ class AgridProjectionModel {
|
|
|
3852
4375
|
}, ...(ngDevMode ? [{ debugName: "filteredSortedIndices" }] : /* istanbul ignore next */ []));
|
|
3853
4376
|
/** Total filtered row count, unaffected by client-side pagination. */
|
|
3854
4377
|
filteredRowCount = computed(() => this.filteredSortedIndices().length, ...(ngDevMode ? [{ debugName: "filteredRowCount" }] : /* istanbul ignore next */ []));
|
|
4378
|
+
/** Whether row pinning is active: a `pinRow` callback is supplied and not in tree mode. */
|
|
4379
|
+
pinningActive = computed(() => !!this.opts.pinRow?.() && !this.opts.treeConfig(), ...(ngDevMode ? [{ debugName: "pinningActive" }] : /* istanbul ignore next */ []));
|
|
4380
|
+
/**
|
|
4381
|
+
* Partitions the filtered+sorted indices into top-pinned, bottom-pinned, and a body set.
|
|
4382
|
+
* Pinned rows keep their real source index, so editing/selection over them is unchanged.
|
|
4383
|
+
*/
|
|
4384
|
+
partitionPinned = computed(() => {
|
|
4385
|
+
if (!this.pinningActive())
|
|
4386
|
+
return { top: [], bottom: [], bodySet: new Set() };
|
|
4387
|
+
const rows = this.opts.dataSource().rows();
|
|
4388
|
+
const pinRow = this.opts.pinRow();
|
|
4389
|
+
const top = [];
|
|
4390
|
+
const bottom = [];
|
|
4391
|
+
const bodySet = new Set();
|
|
4392
|
+
for (const index of this.filteredSortedIndices()) {
|
|
4393
|
+
const where = pinRow(rows[index], index);
|
|
4394
|
+
if (where === 'top')
|
|
4395
|
+
top.push(index);
|
|
4396
|
+
else if (where === 'bottom')
|
|
4397
|
+
bottom.push(index);
|
|
4398
|
+
else
|
|
4399
|
+
bodySet.add(index);
|
|
4400
|
+
}
|
|
4401
|
+
return { top, bottom, bodySet };
|
|
4402
|
+
}, ...(ngDevMode ? [{ debugName: "partitionPinned" }] : /* istanbul ignore next */ []));
|
|
4403
|
+
/** Top-pinned rows as data-row items, in filtered+sorted order. */
|
|
4404
|
+
pinnedTopItems = computed(() => {
|
|
4405
|
+
const rows = this.opts.dataSource().rows();
|
|
4406
|
+
return this.partitionPinned().top.map(originalIndex => ({ row: rows[originalIndex], originalIndex }));
|
|
4407
|
+
}, ...(ngDevMode ? [{ debugName: "pinnedTopItems" }] : /* istanbul ignore next */ []));
|
|
4408
|
+
/** Bottom-pinned rows as data-row items, in filtered+sorted order. */
|
|
4409
|
+
pinnedBottomItems = computed(() => {
|
|
4410
|
+
const rows = this.opts.dataSource().rows();
|
|
4411
|
+
return this.partitionPinned().bottom.map(originalIndex => ({ row: rows[originalIndex], originalIndex }));
|
|
4412
|
+
}, ...(ngDevMode ? [{ debugName: "pinnedBottomItems" }] : /* istanbul ignore next */ []));
|
|
3855
4413
|
/** All source indices in active sort order, ignoring filters. Used for tree ancestor ordering. */
|
|
3856
4414
|
allSortedIndices = computed(() => {
|
|
3857
4415
|
const rows = this.opts.dataSource().rows();
|
|
@@ -3904,6 +4462,11 @@ class AgridProjectionModel {
|
|
|
3904
4462
|
const filters = control?.filters() ?? {};
|
|
3905
4463
|
const filterActive = !this.opts.serverSideFiltering()
|
|
3906
4464
|
&& Object.values(filters).some(f => f.text || f.selectedValues !== null);
|
|
4465
|
+
if (isPathTreeConfig(treeConfig)) {
|
|
4466
|
+
const items = this.appendTreeDetailItems(buildPathTreeItems(rows, indices, treeConfig, expandedIds, filterActive && treeConfig.keepAncestorsOnFilter !== false), rows, treeConfig);
|
|
4467
|
+
this.appendAddRow(items);
|
|
4468
|
+
return items;
|
|
4469
|
+
}
|
|
3907
4470
|
if (filterActive && treeConfig.keepAncestorsOnFilter !== false) {
|
|
3908
4471
|
const { getId, getParentId } = treeConfig;
|
|
3909
4472
|
const idToIndex = new Map();
|
|
@@ -3922,14 +4485,19 @@ class AgridProjectionModel {
|
|
|
3922
4485
|
}
|
|
3923
4486
|
}
|
|
3924
4487
|
const ordered = this.allSortedIndices().filter(index => visible.has(index));
|
|
3925
|
-
const items = buildTreeItems(rows, ordered, treeConfig, expandedIds, forced);
|
|
4488
|
+
const items = this.appendTreeDetailItems(buildTreeItems(rows, ordered, treeConfig, expandedIds, forced), rows, treeConfig);
|
|
3926
4489
|
this.appendAddRow(items);
|
|
3927
4490
|
return items;
|
|
3928
4491
|
}
|
|
3929
|
-
const items = buildTreeItems(rows, indices, treeConfig, expandedIds);
|
|
4492
|
+
const items = this.appendTreeDetailItems(buildTreeItems(rows, indices, treeConfig, expandedIds), rows, treeConfig);
|
|
3930
4493
|
this.appendAddRow(items);
|
|
3931
4494
|
return items;
|
|
3932
4495
|
}
|
|
4496
|
+
// Pull pinned rows out of the body; they render in fixed top/bottom containers instead.
|
|
4497
|
+
if (this.pinningActive()) {
|
|
4498
|
+
const bodySet = this.partitionPinned().bodySet;
|
|
4499
|
+
indices = indices.filter(index => bodySet.has(index));
|
|
4500
|
+
}
|
|
3933
4501
|
if (control) {
|
|
3934
4502
|
const groupField = control.groupByField();
|
|
3935
4503
|
const pageSize = control.pageSize();
|
|
@@ -3952,10 +4520,15 @@ class AgridProjectionModel {
|
|
|
3952
4520
|
return items;
|
|
3953
4521
|
}
|
|
3954
4522
|
}
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
4523
|
+
// Flat mode: optionally interleave a master/detail panel item after each expanded row.
|
|
4524
|
+
const expandedDetail = this.opts.masterDetail?.() ? this.opts.expandedDetailIds?.() : null;
|
|
4525
|
+
const items = [];
|
|
4526
|
+
for (const originalIndex of indices) {
|
|
4527
|
+
const row = rows[originalIndex];
|
|
4528
|
+
items.push({ row, originalIndex });
|
|
4529
|
+
if (expandedDetail?.has(originalIndex))
|
|
4530
|
+
items.push({ detailFor: originalIndex, row });
|
|
4531
|
+
}
|
|
3959
4532
|
this.appendAddRow(items);
|
|
3960
4533
|
return items;
|
|
3961
4534
|
}, ...(ngDevMode ? [{ debugName: "filteredItems" }] : /* istanbul ignore next */ []));
|
|
@@ -3975,6 +4548,29 @@ class AgridProjectionModel {
|
|
|
3975
4548
|
.map(field => [field, filters[field]])
|
|
3976
4549
|
.filter(([, filter]) => !!filter?.sort);
|
|
3977
4550
|
}
|
|
4551
|
+
appendTreeDetailItems(items, rows, treeConfig) {
|
|
4552
|
+
const expandedDetail = this.opts.masterDetail?.() ? this.opts.expandedDetailIds?.() : null;
|
|
4553
|
+
if (!expandedDetail?.size)
|
|
4554
|
+
return items;
|
|
4555
|
+
const parentIds = !isPathTreeConfig(treeConfig)
|
|
4556
|
+
? new Set(rows
|
|
4557
|
+
.map(row => treeConfig.getParentId(row))
|
|
4558
|
+
.filter((id) => id !== null && id !== undefined))
|
|
4559
|
+
: new Set();
|
|
4560
|
+
const result = [];
|
|
4561
|
+
for (const item of items) {
|
|
4562
|
+
result.push(item);
|
|
4563
|
+
if (item
|
|
4564
|
+
&& typeof item === 'object'
|
|
4565
|
+
&& 'originalIndex' in item
|
|
4566
|
+
&& (isPathTreeConfig(treeConfig)
|
|
4567
|
+
|| !parentIds.has(treeConfig.getId(item.row)))
|
|
4568
|
+
&& expandedDetail.has(item.originalIndex)) {
|
|
4569
|
+
result.push({ detailFor: item.originalIndex, row: item.row });
|
|
4570
|
+
}
|
|
4571
|
+
}
|
|
4572
|
+
return result;
|
|
4573
|
+
}
|
|
3978
4574
|
appendAddRow(items) {
|
|
3979
4575
|
if (this.opts.allowAddRows() && !this.opts.autoAddRows())
|
|
3980
4576
|
items.push(null);
|
|
@@ -4109,7 +4705,7 @@ class AgridRangeController {
|
|
|
4109
4705
|
if (insideSource)
|
|
4110
4706
|
continue;
|
|
4111
4707
|
const col = cols[colIndex];
|
|
4112
|
-
if (!col || !this.opts.isCellEditable(col))
|
|
4708
|
+
if (!col || !this.opts.isCellEditable(col, item.originalIndex))
|
|
4113
4709
|
continue;
|
|
4114
4710
|
const sourceRowIndex = rowOffset % sourceValues.length;
|
|
4115
4711
|
const sourceRowValues = sourceValues[sourceRowIndex];
|
|
@@ -4492,6 +5088,8 @@ class AgridSidebarController {
|
|
|
4492
5088
|
const index = this.opts.selectedRowIndex();
|
|
4493
5089
|
if (index === null)
|
|
4494
5090
|
return;
|
|
5091
|
+
if (!this.opts.isCellEditable(col, index))
|
|
5092
|
+
return;
|
|
4495
5093
|
let newValue = stringValue;
|
|
4496
5094
|
if (col.type === 'number') {
|
|
4497
5095
|
newValue = stringValue === '' ? null : Number(stringValue);
|
|
@@ -4541,11 +5139,13 @@ class AgridSidebarComponent {
|
|
|
4541
5139
|
columns = input([], ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
4542
5140
|
headerGroups = input([], ...(ngDevMode ? [{ debugName: "headerGroups" }] : /* istanbul ignore next */ []));
|
|
4543
5141
|
row = input(null, ...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
|
|
5142
|
+
rowIndex = input(null, ...(ngDevMode ? [{ debugName: "rowIndex" }] : /* istanbul ignore next */ []));
|
|
4544
5143
|
hiddenColumns = input(new Set(), ...(ngDevMode ? [{ debugName: "hiddenColumns" }] : /* istanbul ignore next */ []));
|
|
4545
5144
|
locale = input(undefined, ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
|
|
4546
5145
|
localeText = input(AGRID_LOCALE_TEXT.en, ...(ngDevMode ? [{ debugName: "localeText" }] : /* istanbul ignore next */ []));
|
|
4547
5146
|
readonlyGrid = input(false, ...(ngDevMode ? [{ debugName: "readonlyGrid" }] : /* istanbul ignore next */ []));
|
|
4548
5147
|
useSidebarEditor = input(false, ...(ngDevMode ? [{ debugName: "useSidebarEditor" }] : /* istanbul ignore next */ []));
|
|
5148
|
+
isCellEditable = input(col => col.editable !== false, ...(ngDevMode ? [{ debugName: "isCellEditable" }] : /* istanbul ignore next */ []));
|
|
4549
5149
|
/** Per-field validation messages (`field → message`) for rejected detail edits. */
|
|
4550
5150
|
errors = input({}, ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
|
|
4551
5151
|
close = output();
|
|
@@ -4593,6 +5193,32 @@ class AgridSidebarComponent {
|
|
|
4593
5193
|
visible: event.target.checked,
|
|
4594
5194
|
});
|
|
4595
5195
|
}
|
|
5196
|
+
/** Apply the row-aware input mask while preserving the sidebar's change-to-commit behavior. */
|
|
5197
|
+
onDetailMaskInput(field, event) {
|
|
5198
|
+
const input = event.target;
|
|
5199
|
+
const row = this.row();
|
|
5200
|
+
if (row && field.col.inputMask && field.col.type !== 'number' && field.col.type !== 'date') {
|
|
5201
|
+
const mask = field.col.inputMask({
|
|
5202
|
+
row,
|
|
5203
|
+
value: field.rawValue,
|
|
5204
|
+
column: field.col,
|
|
5205
|
+
});
|
|
5206
|
+
if (mask && !matchesInputMask(input.value, mask)) {
|
|
5207
|
+
input.value = input.dataset['agridMaskValue'] ?? field.inputValue;
|
|
5208
|
+
return;
|
|
5209
|
+
}
|
|
5210
|
+
input.dataset['agridMaskValue'] = input.value;
|
|
5211
|
+
}
|
|
5212
|
+
}
|
|
5213
|
+
/** Forward the final sidebar value after native change/blur. */
|
|
5214
|
+
onDetailChange(field, event) {
|
|
5215
|
+
this.onDetailMaskInput(field, event);
|
|
5216
|
+
this.detailEdit.emit({
|
|
5217
|
+
field: field.col.field,
|
|
5218
|
+
col: field.col,
|
|
5219
|
+
value: event.target.value,
|
|
5220
|
+
});
|
|
5221
|
+
}
|
|
4596
5222
|
detailFields = computed(() => {
|
|
4597
5223
|
const row = this.row();
|
|
4598
5224
|
if (!row)
|
|
@@ -4600,6 +5226,7 @@ class AgridSidebarComponent {
|
|
|
4600
5226
|
const locale = this.locale();
|
|
4601
5227
|
const readonlyGrid = this.readonlyGrid();
|
|
4602
5228
|
const hiddenColumns = this.hiddenColumns();
|
|
5229
|
+
const rowIndex = this.rowIndex();
|
|
4603
5230
|
return this.columns().map(col => {
|
|
4604
5231
|
const rawValue = row[col.field];
|
|
4605
5232
|
let inputValue = String(rawValue ?? '');
|
|
@@ -4612,18 +5239,160 @@ class AgridSidebarComponent {
|
|
|
4612
5239
|
rawValue,
|
|
4613
5240
|
inputValue,
|
|
4614
5241
|
hidden: hiddenColumns.has(col.field),
|
|
4615
|
-
editable: !readonlyGrid &&
|
|
5242
|
+
editable: !readonlyGrid && rowIndex !== null && this.isCellEditable()(col, rowIndex),
|
|
4616
5243
|
col,
|
|
4617
5244
|
};
|
|
4618
5245
|
});
|
|
4619
5246
|
}, ...(ngDevMode ? [{ debugName: "detailFields" }] : /* istanbul ignore next */ []));
|
|
4620
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
4621
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: AgridSidebarComponent, isStandalone: true, selector: "agrid-sidebar", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, activeTab: { classPropertyName: "activeTab", publicName: "activeTab", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, headerGroups: { classPropertyName: "headerGroups", publicName: "headerGroups", isSignal: true, isRequired: false, transformFunction: null }, row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: false, transformFunction: null }, hiddenColumns: { classPropertyName: "hiddenColumns", publicName: "hiddenColumns", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, localeText: { classPropertyName: "localeText", publicName: "localeText", isSignal: true, isRequired: false, transformFunction: null }, readonlyGrid: { classPropertyName: "readonlyGrid", publicName: "readonlyGrid", isSignal: true, isRequired: false, transformFunction: null }, useSidebarEditor: { classPropertyName: "useSidebarEditor", publicName: "useSidebarEditor", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", tabChange: "tabChange", toggleColumn: "toggleColumn", toggleColumnGroup: "toggleColumnGroup", detailEdit: "detailEdit", save: "save" }, ngImport: i0, template: "@if (open()) {\n <div class=\"ag-sidebar\" role=\"region\"\n [attr.aria-label]=\"activeTab() === 'columns' ? localeText().columns : localeText().detail\">\n <div class=\"ag-sidebar-header\">\n <span>{{ activeTab() === 'columns' ? localeText().columns : localeText().detail }}</span>\n <button class=\"ag-sidebar-close\" (click)=\"close.emit()\" [title]=\"localeText().close\"\n [attr.aria-label]=\"localeText().close\">\u2715</button>\n </div>\n\n @if (activeTab() === 'columns') {\n @for (entry of columnEntries(); track entry.kind === 'group' ? 'group-' + entry.id : 'column-' + entry.col.field) {\n @if (entry.kind === 'group') {\n <div class=\"ag-sidebar-group\">\n <label class=\"ag-sidebar-item ag-sidebar-group-label\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"isGroupVisible(entry.columns)\"\n [indeterminate]=\"isGroupPartiallyVisible(entry.columns)\"\n (change)=\"onGroupToggle(entry.columns, $event)\"\n />\n {{ entry.label }}\n </label>\n <div class=\"ag-sidebar-group-children\">\n @for (col of entry.columns; track col.field) {\n <label class=\"ag-sidebar-item ag-sidebar-group-child\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"!hiddenColumns().has(col.field)\"\n (change)=\"toggleColumn.emit(col.field)\"\n />\n {{ col.header }}\n </label>\n }\n </div>\n </div>\n } @else {\n <label class=\"ag-sidebar-item\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"!hiddenColumns().has(entry.col.field)\"\n (change)=\"toggleColumn.emit(entry.col.field)\"\n />\n {{ entry.col.header }}\n </label>\n }\n }\n } @else {\n @if (detailFields(); as fields) {\n @for (field of fields; track field.col.field) {\n <div class=\"ag-detail-field\">\n <span class=\"ag-detail-label\">\n {{ field.label }}\n @if (field.hidden) {\n <span class=\"ag-detail-hidden\">{{ localeText().hiddenColumn }}</span>\n }\n </span>\n @if ((field.editable || useSidebarEditor()) && field.col.values?.length) {\n <select\n class=\"ag-detail-input\"\n [class.ag-detail-input--invalid]=\"!!errors()[field.col.field]\"\n (change)=\"detailEdit.emit({\n field: field.col.field,\n col: field.col,\n value: $any($event.target).value\n })\"\n >\n @for (opt of field.col.values; track $index) {\n @if ($any(opt).value !== undefined) {\n <option [value]=\"$any(opt).value\" [selected]=\"$any(opt).value === field.rawValue\">\n {{ $any(opt).label }}\n </option>\n } @else {\n <option [value]=\"opt\" [selected]=\"opt === field.rawValue\">{{ opt }}</option>\n }\n }\n </select>\n } @else if (field.editable || useSidebarEditor()) {\n <input\n class=\"ag-detail-input\"\n [class.ag-detail-input--invalid]=\"!!errors()[field.col.field]\"\n [type]=\"field.col.type === 'number' ? 'number' : field.col.type === 'date' ? 'date' : 'text'\"\n [value]=\"field.inputValue\"\n (change)=\"detailEdit.emit({\n field: field.col.field,\n col: field.col,\n value: $any($event.target).value\n })\"\n />\n } @else {\n <span class=\"ag-detail-value\">{{ field.value }}</span>\n }\n @if (errors()[field.col.field]; as msg) {\n <span class=\"ag-detail-error\" role=\"alert\">{{ msg }}</span>\n }\n </div>\n }\n @if (useSidebarEditor()) {\n <div class=\"ag-detail-field\">\n <button class=\"ag-button\" (click)=\"save.emit(fields)\">{{ localeText().save }}</button>\n </div>\n }\n } @else {\n <div class=\"ag-detail-empty\">{{ localeText().noRows }}</div>\n }\n }\n </div>\n}\n\n<div class=\"ag-sidebar-strip\">\n <button\n class=\"ag-sidebar-strip-btn\"\n [class.active]=\"open() && activeTab() === 'columns'\"\n [attr.aria-pressed]=\"open() && activeTab() === 'columns'\"\n (click)=\"tabChange.emit('columns')\"\n >{{ localeText().columns }}</button>\n <button\n class=\"ag-sidebar-strip-btn\"\n [class.active]=\"open() && activeTab() === 'detail'\"\n [attr.aria-pressed]=\"open() && activeTab() === 'detail'\"\n (click)=\"tabChange.emit('detail')\"\n >{{ localeText().detail }}</button>\n</div>\n", styles: [":host{display:flex;flex-shrink:0;min-height:0}.ag-sidebar{width:200px;flex-shrink:0;border-left:1px solid var(--agrid-color-border);background:var(--agrid-color-bg);display:flex;flex-direction:column;overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-sidebar::-webkit-scrollbar{width:8px;height:8px}.ag-sidebar::-webkit-scrollbar-track{background:transparent}.ag-sidebar::-webkit-scrollbar-thumb{background:#0000002e;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-sidebar::-webkit-scrollbar-thumb:hover{background:#00000052}.ag-sidebar-strip{width:24px;flex-shrink:0;border-left:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted);display:flex;flex-direction:column}.ag-sidebar-strip-btn{background:none;border:none;border-bottom:1px solid var(--agrid-color-border);cursor:pointer;color:var(--agrid-color-text-muted);-webkit-user-select:none;user-select:none;writing-mode:vertical-rl;transform:rotate(180deg);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;padding:10px 5px;text-align:center}.ag-sidebar-strip-btn:hover{background:var(--agrid-color-bg);color:var(--agrid-color-text)}.ag-sidebar-strip-btn.active{color:var(--agrid-color-text);background:var(--agrid-color-bg);border-left:2px solid var(--agrid-color-accent, #4f8ef7)}.ag-sidebar-header{display:flex;align-items:center;justify-content:space-between;padding:0 8px 0 12px;height:32px;flex-shrink:0;border-bottom:1px solid var(--agrid-color-border);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--agrid-color-text-muted);background:var(--agrid-color-bg-muted)}.ag-sidebar-close{background:none;border:none;cursor:pointer;color:var(--agrid-color-text-muted);font-size:11px;padding:2px 4px;border-radius:3px;line-height:1}.ag-sidebar-close:hover,.ag-sidebar-item:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text)}.ag-sidebar-item{display:flex;align-items:center;gap:8px;padding:5px 12px;font-size:13px;cursor:pointer;-webkit-user-select:none;user-select:none}.ag-sidebar-item input[type=checkbox]{cursor:pointer;margin:0;flex-shrink:0}.ag-sidebar-group-label{font-weight:600}.ag-sidebar-group-children{position:relative}.ag-sidebar-group-children:before{content:\"\";position:absolute;top:0;bottom:8px;left:19px;border-left:1px solid var(--agrid-color-border)}.ag-sidebar-group-child{position:relative;padding-left:30px}.ag-sidebar-group-child:before{content:\"\";position:absolute;left:19px;width:7px;border-top:1px solid var(--agrid-color-border)}.ag-detail-field{display:flex;flex-direction:column;gap:2px;padding:7px 12px;border-bottom:1px solid var(--agrid-color-border);font-size:13px}.ag-detail-label{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--agrid-color-text-muted)}.ag-detail-hidden{font-weight:400;text-transform:none;letter-spacing:0;opacity:.6}.ag-detail-value{color:var(--agrid-color-text);word-break:break-word}.ag-detail-empty{padding:16px 12px;font-size:13px;color:var(--agrid-color-text-muted);text-align:center}.ag-detail-input{width:100%;box-sizing:border-box;font-size:13px;font-family:inherit;color:var(--agrid-color-text);background:var(--agrid-color-bg);border:1px solid var(--agrid-color-border);border-radius:3px;padding:3px 6px;outline:none}.ag-detail-input:focus{border-color:var(--agrid-color-accent, #4f8ef7);box-shadow:0 0 0 2px color-mix(in srgb,var(--agrid-color-accent, #4f8ef7) 20%,transparent)}.ag-button{display:flex;align-items:center;justify-content:center;height:24px;border:1px solid var(--agrid-color-border);border-radius:3px;background:var(--agrid-color-bg-subtle);color:var(--agrid-color-text);cursor:pointer;font-size:13px;padding:0;line-height:1}.ag-detail-input--invalid{border-color:var(--agrid-color-danger);outline:1px solid var(--agrid-color-danger)}.ag-detail-error{display:block;margin-top:3px;font-size:12px;line-height:1.4;color:var(--agrid-color-danger)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5247
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridSidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5248
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AgridSidebarComponent, isStandalone: true, selector: "agrid-sidebar", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, activeTab: { classPropertyName: "activeTab", publicName: "activeTab", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, headerGroups: { classPropertyName: "headerGroups", publicName: "headerGroups", isSignal: true, isRequired: false, transformFunction: null }, row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: false, transformFunction: null }, rowIndex: { classPropertyName: "rowIndex", publicName: "rowIndex", isSignal: true, isRequired: false, transformFunction: null }, hiddenColumns: { classPropertyName: "hiddenColumns", publicName: "hiddenColumns", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, localeText: { classPropertyName: "localeText", publicName: "localeText", isSignal: true, isRequired: false, transformFunction: null }, readonlyGrid: { classPropertyName: "readonlyGrid", publicName: "readonlyGrid", isSignal: true, isRequired: false, transformFunction: null }, useSidebarEditor: { classPropertyName: "useSidebarEditor", publicName: "useSidebarEditor", isSignal: true, isRequired: false, transformFunction: null }, isCellEditable: { classPropertyName: "isCellEditable", publicName: "isCellEditable", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", tabChange: "tabChange", toggleColumn: "toggleColumn", toggleColumnGroup: "toggleColumnGroup", detailEdit: "detailEdit", save: "save" }, ngImport: i0, template: "@if (open()) {\n <div class=\"ag-sidebar\" role=\"region\"\n [attr.aria-label]=\"activeTab() === 'columns' ? localeText().columns : localeText().detail\">\n <div class=\"ag-sidebar-header\">\n <span>{{ activeTab() === 'columns' ? localeText().columns : localeText().detail }}</span>\n <button class=\"ag-sidebar-close\" (click)=\"close.emit()\" [title]=\"localeText().close\"\n [attr.aria-label]=\"localeText().close\">\u2715</button>\n </div>\n\n @if (activeTab() === 'columns') {\n @for (entry of columnEntries(); track entry.kind === 'group' ? 'group-' + entry.id : 'column-' + entry.col.field) {\n @if (entry.kind === 'group') {\n <div class=\"ag-sidebar-group\">\n <label class=\"ag-sidebar-item ag-sidebar-group-label\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"isGroupVisible(entry.columns)\"\n [indeterminate]=\"isGroupPartiallyVisible(entry.columns)\"\n (change)=\"onGroupToggle(entry.columns, $event)\"\n />\n {{ entry.label }}\n </label>\n <div class=\"ag-sidebar-group-children\">\n @for (col of entry.columns; track col.field) {\n <label class=\"ag-sidebar-item ag-sidebar-group-child\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"!hiddenColumns().has(col.field)\"\n (change)=\"toggleColumn.emit(col.field)\"\n />\n {{ col.header }}\n </label>\n }\n </div>\n </div>\n } @else {\n <label class=\"ag-sidebar-item\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"!hiddenColumns().has(entry.col.field)\"\n (change)=\"toggleColumn.emit(entry.col.field)\"\n />\n {{ entry.col.header }}\n </label>\n }\n }\n } @else {\n @if (detailFields(); as fields) {\n @for (field of fields; track field.col.field) {\n <div class=\"ag-detail-field\">\n <span class=\"ag-detail-label\">\n {{ field.label }}\n @if (field.hidden) {\n <span class=\"ag-detail-hidden\">{{ localeText().hiddenColumn }}</span>\n }\n </span>\n @if (field.editable && field.col.values?.length) {\n <select\n class=\"ag-detail-input\"\n [class.ag-detail-input--invalid]=\"!!errors()[field.col.field]\"\n (change)=\"detailEdit.emit({\n field: field.col.field,\n col: field.col,\n value: $any($event.target).value\n })\"\n >\n @for (opt of field.col.values; track $index) {\n @if ($any(opt).value !== undefined) {\n <option [value]=\"$any(opt).value\" [selected]=\"$any(opt).value === field.rawValue\">\n {{ $any(opt).label }}\n </option>\n } @else {\n <option [value]=\"opt\" [selected]=\"opt === field.rawValue\">{{ opt }}</option>\n }\n }\n </select>\n } @else if (field.editable) {\n <input\n class=\"ag-detail-input\"\n [class.ag-detail-input--invalid]=\"!!errors()[field.col.field]\"\n [type]=\"field.col.type === 'number' ? 'number' : field.col.type === 'date' ? 'date' : 'text'\"\n [value]=\"field.inputValue\"\n (input)=\"onDetailMaskInput(field, $event)\"\n (change)=\"onDetailChange(field, $event)\"\n />\n } @else {\n <span class=\"ag-detail-value\">{{ field.value }}</span>\n }\n @if (errors()[field.col.field]; as msg) {\n <span class=\"ag-detail-error\" role=\"alert\">{{ msg }}</span>\n }\n </div>\n }\n @if (useSidebarEditor()) {\n <div class=\"ag-detail-field\">\n <button class=\"ag-button\" (click)=\"save.emit(fields)\">{{ localeText().save }}</button>\n </div>\n }\n } @else {\n <div class=\"ag-detail-empty\">{{ localeText().noRows }}</div>\n }\n }\n </div>\n}\n\n<div class=\"ag-sidebar-strip\">\n <button\n class=\"ag-sidebar-strip-btn\"\n [class.active]=\"open() && activeTab() === 'columns'\"\n [attr.aria-pressed]=\"open() && activeTab() === 'columns'\"\n (click)=\"tabChange.emit('columns')\"\n >{{ localeText().columns }}</button>\n <button\n class=\"ag-sidebar-strip-btn\"\n [class.active]=\"open() && activeTab() === 'detail'\"\n [attr.aria-pressed]=\"open() && activeTab() === 'detail'\"\n (click)=\"tabChange.emit('detail')\"\n >{{ localeText().detail }}</button>\n</div>\n", styles: [":host{display:flex;flex-shrink:0;min-height:0}.ag-sidebar{width:200px;flex-shrink:0;border-left:1px solid var(--agrid-color-border);background:var(--agrid-color-bg);display:flex;flex-direction:column;overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-sidebar::-webkit-scrollbar{width:8px;height:8px}.ag-sidebar::-webkit-scrollbar-track{background:transparent}.ag-sidebar::-webkit-scrollbar-thumb{background:#0000002e;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-sidebar::-webkit-scrollbar-thumb:hover{background:#00000052}.ag-sidebar-strip{width:24px;flex-shrink:0;border-left:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted);display:flex;flex-direction:column}.ag-sidebar-strip-btn{background:none;border:none;border-bottom:1px solid var(--agrid-color-border);cursor:pointer;color:var(--agrid-color-text-muted);-webkit-user-select:none;user-select:none;writing-mode:vertical-rl;transform:rotate(180deg);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;padding:10px 5px;text-align:center}.ag-sidebar-strip-btn:hover{background:var(--agrid-color-bg);color:var(--agrid-color-text)}.ag-sidebar-strip-btn.active{color:var(--agrid-color-text);background:var(--agrid-color-bg);border-left:2px solid var(--agrid-color-accent, #4f8ef7)}.ag-sidebar-header{display:flex;align-items:center;justify-content:space-between;padding:0 8px 0 12px;height:32px;flex-shrink:0;border-bottom:1px solid var(--agrid-color-border);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--agrid-color-text-muted);background:var(--agrid-color-bg-muted)}.ag-sidebar-close{background:none;border:none;cursor:pointer;color:var(--agrid-color-text-muted);font-size:11px;padding:2px 4px;border-radius:3px;line-height:1}.ag-sidebar-close:hover,.ag-sidebar-item:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text)}.ag-sidebar-item{display:flex;align-items:center;gap:8px;padding:5px 12px;font-size:13px;cursor:pointer;-webkit-user-select:none;user-select:none}.ag-sidebar-item input[type=checkbox]{cursor:pointer;margin:0;flex-shrink:0}.ag-sidebar-group-label{font-weight:600}.ag-sidebar-group-children{position:relative}.ag-sidebar-group-children:before{content:\"\";position:absolute;top:0;bottom:8px;left:19px;border-left:1px solid var(--agrid-color-border)}.ag-sidebar-group-child{position:relative;padding-left:30px}.ag-sidebar-group-child:before{content:\"\";position:absolute;left:19px;width:7px;border-top:1px solid var(--agrid-color-border)}.ag-detail-field{display:flex;flex-direction:column;gap:2px;padding:7px 12px;border-bottom:1px solid var(--agrid-color-border);font-size:13px}.ag-detail-label{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--agrid-color-text-muted)}.ag-detail-hidden{font-weight:400;text-transform:none;letter-spacing:0;opacity:.6}.ag-detail-value{color:var(--agrid-color-text);word-break:break-word}.ag-detail-empty{padding:16px 12px;font-size:13px;color:var(--agrid-color-text-muted);text-align:center}.ag-detail-input{width:100%;box-sizing:border-box;font-size:13px;font-family:inherit;color:var(--agrid-color-text);background:var(--agrid-color-bg);border:1px solid var(--agrid-color-border);border-radius:3px;padding:3px 6px;outline:none}.ag-detail-input:focus{border-color:var(--agrid-color-accent, #4f8ef7);box-shadow:0 0 0 2px color-mix(in srgb,var(--agrid-color-accent, #4f8ef7) 20%,transparent)}.ag-button{display:flex;align-items:center;justify-content:center;height:24px;border:1px solid var(--agrid-color-border);border-radius:3px;background:var(--agrid-color-bg-subtle);color:var(--agrid-color-text);cursor:pointer;font-size:13px;padding:0;line-height:1}.ag-detail-input--invalid{border-color:var(--agrid-color-danger);outline:1px solid var(--agrid-color-danger)}.ag-detail-error{display:block;margin-top:3px;font-size:12px;line-height:1.4;color:var(--agrid-color-danger)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4622
5249
|
}
|
|
4623
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
5250
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridSidebarComponent, decorators: [{
|
|
4624
5251
|
type: Component,
|
|
4625
|
-
args: [{ selector: 'agrid-sidebar', changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (open()) {\n <div class=\"ag-sidebar\" role=\"region\"\n [attr.aria-label]=\"activeTab() === 'columns' ? localeText().columns : localeText().detail\">\n <div class=\"ag-sidebar-header\">\n <span>{{ activeTab() === 'columns' ? localeText().columns : localeText().detail }}</span>\n <button class=\"ag-sidebar-close\" (click)=\"close.emit()\" [title]=\"localeText().close\"\n [attr.aria-label]=\"localeText().close\">\u2715</button>\n </div>\n\n @if (activeTab() === 'columns') {\n @for (entry of columnEntries(); track entry.kind === 'group' ? 'group-' + entry.id : 'column-' + entry.col.field) {\n @if (entry.kind === 'group') {\n <div class=\"ag-sidebar-group\">\n <label class=\"ag-sidebar-item ag-sidebar-group-label\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"isGroupVisible(entry.columns)\"\n [indeterminate]=\"isGroupPartiallyVisible(entry.columns)\"\n (change)=\"onGroupToggle(entry.columns, $event)\"\n />\n {{ entry.label }}\n </label>\n <div class=\"ag-sidebar-group-children\">\n @for (col of entry.columns; track col.field) {\n <label class=\"ag-sidebar-item ag-sidebar-group-child\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"!hiddenColumns().has(col.field)\"\n (change)=\"toggleColumn.emit(col.field)\"\n />\n {{ col.header }}\n </label>\n }\n </div>\n </div>\n } @else {\n <label class=\"ag-sidebar-item\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"!hiddenColumns().has(entry.col.field)\"\n (change)=\"toggleColumn.emit(entry.col.field)\"\n />\n {{ entry.col.header }}\n </label>\n }\n }\n } @else {\n @if (detailFields(); as fields) {\n @for (field of fields; track field.col.field) {\n <div class=\"ag-detail-field\">\n <span class=\"ag-detail-label\">\n {{ field.label }}\n @if (field.hidden) {\n <span class=\"ag-detail-hidden\">{{ localeText().hiddenColumn }}</span>\n }\n </span>\n @if (
|
|
4626
|
-
}], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], activeTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeTab", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], headerGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerGroups", required: false }] }], row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: false }] }], hiddenColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "hiddenColumns", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], localeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "localeText", required: false }] }], readonlyGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonlyGrid", required: false }] }], useSidebarEditor: [{ type: i0.Input, args: [{ isSignal: true, alias: "useSidebarEditor", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], close: [{ type: i0.Output, args: ["close"] }], tabChange: [{ type: i0.Output, args: ["tabChange"] }], toggleColumn: [{ type: i0.Output, args: ["toggleColumn"] }], toggleColumnGroup: [{ type: i0.Output, args: ["toggleColumnGroup"] }], detailEdit: [{ type: i0.Output, args: ["detailEdit"] }], save: [{ type: i0.Output, args: ["save"] }] } });
|
|
5252
|
+
args: [{ selector: 'agrid-sidebar', changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (open()) {\n <div class=\"ag-sidebar\" role=\"region\"\n [attr.aria-label]=\"activeTab() === 'columns' ? localeText().columns : localeText().detail\">\n <div class=\"ag-sidebar-header\">\n <span>{{ activeTab() === 'columns' ? localeText().columns : localeText().detail }}</span>\n <button class=\"ag-sidebar-close\" (click)=\"close.emit()\" [title]=\"localeText().close\"\n [attr.aria-label]=\"localeText().close\">\u2715</button>\n </div>\n\n @if (activeTab() === 'columns') {\n @for (entry of columnEntries(); track entry.kind === 'group' ? 'group-' + entry.id : 'column-' + entry.col.field) {\n @if (entry.kind === 'group') {\n <div class=\"ag-sidebar-group\">\n <label class=\"ag-sidebar-item ag-sidebar-group-label\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"isGroupVisible(entry.columns)\"\n [indeterminate]=\"isGroupPartiallyVisible(entry.columns)\"\n (change)=\"onGroupToggle(entry.columns, $event)\"\n />\n {{ entry.label }}\n </label>\n <div class=\"ag-sidebar-group-children\">\n @for (col of entry.columns; track col.field) {\n <label class=\"ag-sidebar-item ag-sidebar-group-child\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"!hiddenColumns().has(col.field)\"\n (change)=\"toggleColumn.emit(col.field)\"\n />\n {{ col.header }}\n </label>\n }\n </div>\n </div>\n } @else {\n <label class=\"ag-sidebar-item\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"!hiddenColumns().has(entry.col.field)\"\n (change)=\"toggleColumn.emit(entry.col.field)\"\n />\n {{ entry.col.header }}\n </label>\n }\n }\n } @else {\n @if (detailFields(); as fields) {\n @for (field of fields; track field.col.field) {\n <div class=\"ag-detail-field\">\n <span class=\"ag-detail-label\">\n {{ field.label }}\n @if (field.hidden) {\n <span class=\"ag-detail-hidden\">{{ localeText().hiddenColumn }}</span>\n }\n </span>\n @if (field.editable && field.col.values?.length) {\n <select\n class=\"ag-detail-input\"\n [class.ag-detail-input--invalid]=\"!!errors()[field.col.field]\"\n (change)=\"detailEdit.emit({\n field: field.col.field,\n col: field.col,\n value: $any($event.target).value\n })\"\n >\n @for (opt of field.col.values; track $index) {\n @if ($any(opt).value !== undefined) {\n <option [value]=\"$any(opt).value\" [selected]=\"$any(opt).value === field.rawValue\">\n {{ $any(opt).label }}\n </option>\n } @else {\n <option [value]=\"opt\" [selected]=\"opt === field.rawValue\">{{ opt }}</option>\n }\n }\n </select>\n } @else if (field.editable) {\n <input\n class=\"ag-detail-input\"\n [class.ag-detail-input--invalid]=\"!!errors()[field.col.field]\"\n [type]=\"field.col.type === 'number' ? 'number' : field.col.type === 'date' ? 'date' : 'text'\"\n [value]=\"field.inputValue\"\n (input)=\"onDetailMaskInput(field, $event)\"\n (change)=\"onDetailChange(field, $event)\"\n />\n } @else {\n <span class=\"ag-detail-value\">{{ field.value }}</span>\n }\n @if (errors()[field.col.field]; as msg) {\n <span class=\"ag-detail-error\" role=\"alert\">{{ msg }}</span>\n }\n </div>\n }\n @if (useSidebarEditor()) {\n <div class=\"ag-detail-field\">\n <button class=\"ag-button\" (click)=\"save.emit(fields)\">{{ localeText().save }}</button>\n </div>\n }\n } @else {\n <div class=\"ag-detail-empty\">{{ localeText().noRows }}</div>\n }\n }\n </div>\n}\n\n<div class=\"ag-sidebar-strip\">\n <button\n class=\"ag-sidebar-strip-btn\"\n [class.active]=\"open() && activeTab() === 'columns'\"\n [attr.aria-pressed]=\"open() && activeTab() === 'columns'\"\n (click)=\"tabChange.emit('columns')\"\n >{{ localeText().columns }}</button>\n <button\n class=\"ag-sidebar-strip-btn\"\n [class.active]=\"open() && activeTab() === 'detail'\"\n [attr.aria-pressed]=\"open() && activeTab() === 'detail'\"\n (click)=\"tabChange.emit('detail')\"\n >{{ localeText().detail }}</button>\n</div>\n", styles: [":host{display:flex;flex-shrink:0;min-height:0}.ag-sidebar{width:200px;flex-shrink:0;border-left:1px solid var(--agrid-color-border);background:var(--agrid-color-bg);display:flex;flex-direction:column;overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-sidebar::-webkit-scrollbar{width:8px;height:8px}.ag-sidebar::-webkit-scrollbar-track{background:transparent}.ag-sidebar::-webkit-scrollbar-thumb{background:#0000002e;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-sidebar::-webkit-scrollbar-thumb:hover{background:#00000052}.ag-sidebar-strip{width:24px;flex-shrink:0;border-left:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted);display:flex;flex-direction:column}.ag-sidebar-strip-btn{background:none;border:none;border-bottom:1px solid var(--agrid-color-border);cursor:pointer;color:var(--agrid-color-text-muted);-webkit-user-select:none;user-select:none;writing-mode:vertical-rl;transform:rotate(180deg);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;padding:10px 5px;text-align:center}.ag-sidebar-strip-btn:hover{background:var(--agrid-color-bg);color:var(--agrid-color-text)}.ag-sidebar-strip-btn.active{color:var(--agrid-color-text);background:var(--agrid-color-bg);border-left:2px solid var(--agrid-color-accent, #4f8ef7)}.ag-sidebar-header{display:flex;align-items:center;justify-content:space-between;padding:0 8px 0 12px;height:32px;flex-shrink:0;border-bottom:1px solid var(--agrid-color-border);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--agrid-color-text-muted);background:var(--agrid-color-bg-muted)}.ag-sidebar-close{background:none;border:none;cursor:pointer;color:var(--agrid-color-text-muted);font-size:11px;padding:2px 4px;border-radius:3px;line-height:1}.ag-sidebar-close:hover,.ag-sidebar-item:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text)}.ag-sidebar-item{display:flex;align-items:center;gap:8px;padding:5px 12px;font-size:13px;cursor:pointer;-webkit-user-select:none;user-select:none}.ag-sidebar-item input[type=checkbox]{cursor:pointer;margin:0;flex-shrink:0}.ag-sidebar-group-label{font-weight:600}.ag-sidebar-group-children{position:relative}.ag-sidebar-group-children:before{content:\"\";position:absolute;top:0;bottom:8px;left:19px;border-left:1px solid var(--agrid-color-border)}.ag-sidebar-group-child{position:relative;padding-left:30px}.ag-sidebar-group-child:before{content:\"\";position:absolute;left:19px;width:7px;border-top:1px solid var(--agrid-color-border)}.ag-detail-field{display:flex;flex-direction:column;gap:2px;padding:7px 12px;border-bottom:1px solid var(--agrid-color-border);font-size:13px}.ag-detail-label{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--agrid-color-text-muted)}.ag-detail-hidden{font-weight:400;text-transform:none;letter-spacing:0;opacity:.6}.ag-detail-value{color:var(--agrid-color-text);word-break:break-word}.ag-detail-empty{padding:16px 12px;font-size:13px;color:var(--agrid-color-text-muted);text-align:center}.ag-detail-input{width:100%;box-sizing:border-box;font-size:13px;font-family:inherit;color:var(--agrid-color-text);background:var(--agrid-color-bg);border:1px solid var(--agrid-color-border);border-radius:3px;padding:3px 6px;outline:none}.ag-detail-input:focus{border-color:var(--agrid-color-accent, #4f8ef7);box-shadow:0 0 0 2px color-mix(in srgb,var(--agrid-color-accent, #4f8ef7) 20%,transparent)}.ag-button{display:flex;align-items:center;justify-content:center;height:24px;border:1px solid var(--agrid-color-border);border-radius:3px;background:var(--agrid-color-bg-subtle);color:var(--agrid-color-text);cursor:pointer;font-size:13px;padding:0;line-height:1}.ag-detail-input--invalid{border-color:var(--agrid-color-danger);outline:1px solid var(--agrid-color-danger)}.ag-detail-error{display:block;margin-top:3px;font-size:12px;line-height:1.4;color:var(--agrid-color-danger)}\n"] }]
|
|
5253
|
+
}], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], activeTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeTab", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], headerGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerGroups", required: false }] }], row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: false }] }], rowIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowIndex", required: false }] }], hiddenColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "hiddenColumns", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], localeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "localeText", required: false }] }], readonlyGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonlyGrid", required: false }] }], useSidebarEditor: [{ type: i0.Input, args: [{ isSignal: true, alias: "useSidebarEditor", required: false }] }], isCellEditable: [{ type: i0.Input, args: [{ isSignal: true, alias: "isCellEditable", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], close: [{ type: i0.Output, args: ["close"] }], tabChange: [{ type: i0.Output, args: ["tabChange"] }], toggleColumn: [{ type: i0.Output, args: ["toggleColumn"] }], toggleColumnGroup: [{ type: i0.Output, args: ["toggleColumnGroup"] }], detailEdit: [{ type: i0.Output, args: ["detailEdit"] }], save: [{ type: i0.Output, args: ["save"] }] } });
|
|
5254
|
+
|
|
5255
|
+
/** Pixels rendered above and below the viewport so scrolling does not reveal blank rows. */
|
|
5256
|
+
const BUFFER_PX = 200;
|
|
5257
|
+
/**
|
|
5258
|
+
* A {@link VirtualScrollStrategy} that supports a different height per item instead of the single
|
|
5259
|
+
* fixed `itemSize` used by CDK's {@link FixedSizeVirtualScrollStrategy}.
|
|
5260
|
+
*
|
|
5261
|
+
* Item heights are supplied as a flat array (one entry per virtual item) and converted into a
|
|
5262
|
+
* prefix-sum offset table, so any row — a normal data row or a tall master/detail panel — can have
|
|
5263
|
+
* its own height while scroll position math stays O(log n) via binary search.
|
|
5264
|
+
*
|
|
5265
|
+
* When every item shares the same height (the common case with no detail rows open) the behavior
|
|
5266
|
+
* is equivalent to the fixed-size strategy, so the default code path is regression-safe.
|
|
5267
|
+
* @internal
|
|
5268
|
+
*/
|
|
5269
|
+
class AgridVariableRowSizeStrategy {
|
|
5270
|
+
viewport = null;
|
|
5271
|
+
sizes = [];
|
|
5272
|
+
/** Cumulative offsets; `offsets[i]` is the top of item `i`, `offsets[n]` is the total size. */
|
|
5273
|
+
offsets = [0];
|
|
5274
|
+
indexChange = new Subject();
|
|
5275
|
+
scrolledIndexChange = this.indexChange.pipe(distinctUntilChanged());
|
|
5276
|
+
attach(viewport) {
|
|
5277
|
+
this.viewport = viewport;
|
|
5278
|
+
this.updateTotalContentSize();
|
|
5279
|
+
this.updateRenderedRange();
|
|
5280
|
+
}
|
|
5281
|
+
detach() {
|
|
5282
|
+
this.indexChange.complete();
|
|
5283
|
+
this.viewport = null;
|
|
5284
|
+
}
|
|
5285
|
+
/** Replace the per-item heights and re-measure. Called by the directive when inputs change. */
|
|
5286
|
+
updateItemSizes(sizes) {
|
|
5287
|
+
this.sizes = sizes;
|
|
5288
|
+
const offsets = new Array(sizes.length + 1);
|
|
5289
|
+
offsets[0] = 0;
|
|
5290
|
+
for (let i = 0; i < sizes.length; i++)
|
|
5291
|
+
offsets[i + 1] = offsets[i] + sizes[i];
|
|
5292
|
+
this.offsets = offsets;
|
|
5293
|
+
if (this.viewport) {
|
|
5294
|
+
this.updateTotalContentSize();
|
|
5295
|
+
this.updateRenderedRange();
|
|
5296
|
+
}
|
|
5297
|
+
}
|
|
5298
|
+
onContentScrolled() {
|
|
5299
|
+
this.updateRenderedRange();
|
|
5300
|
+
}
|
|
5301
|
+
onDataLengthChanged() {
|
|
5302
|
+
this.updateTotalContentSize();
|
|
5303
|
+
this.updateRenderedRange();
|
|
5304
|
+
}
|
|
5305
|
+
onContentRendered() {
|
|
5306
|
+
/* no-op: heights are supplied, not measured from the DOM */
|
|
5307
|
+
}
|
|
5308
|
+
onRenderedOffsetChanged() {
|
|
5309
|
+
/* no-op */
|
|
5310
|
+
}
|
|
5311
|
+
scrollToIndex(index, behavior) {
|
|
5312
|
+
if (!this.viewport)
|
|
5313
|
+
return;
|
|
5314
|
+
const clamped = Math.max(0, Math.min(index, this.sizes.length));
|
|
5315
|
+
this.viewport.scrollToOffset(this.offsets[clamped] ?? 0, behavior);
|
|
5316
|
+
}
|
|
5317
|
+
get totalSize() {
|
|
5318
|
+
return this.offsets[this.offsets.length - 1] ?? 0;
|
|
5319
|
+
}
|
|
5320
|
+
updateTotalContentSize() {
|
|
5321
|
+
this.viewport?.setTotalContentSize(this.totalSize);
|
|
5322
|
+
}
|
|
5323
|
+
/** Largest item index whose top offset is `<= offset` (binary search over the prefix sums). */
|
|
5324
|
+
indexAt(offset) {
|
|
5325
|
+
const o = this.offsets;
|
|
5326
|
+
let lo = 0;
|
|
5327
|
+
let hi = o.length - 1;
|
|
5328
|
+
while (lo < hi) {
|
|
5329
|
+
const mid = (lo + hi + 1) >> 1;
|
|
5330
|
+
if (o[mid] <= offset)
|
|
5331
|
+
lo = mid;
|
|
5332
|
+
else
|
|
5333
|
+
hi = mid - 1;
|
|
5334
|
+
}
|
|
5335
|
+
return lo;
|
|
5336
|
+
}
|
|
5337
|
+
updateRenderedRange() {
|
|
5338
|
+
const viewport = this.viewport;
|
|
5339
|
+
if (!viewport)
|
|
5340
|
+
return;
|
|
5341
|
+
const dataLength = viewport.getDataLength();
|
|
5342
|
+
const n = Math.min(this.sizes.length, dataLength);
|
|
5343
|
+
if (n === 0) {
|
|
5344
|
+
viewport.setRenderedRange({ start: 0, end: 0 });
|
|
5345
|
+
viewport.setRenderedContentOffset(0);
|
|
5346
|
+
this.indexChange.next(0);
|
|
5347
|
+
return;
|
|
5348
|
+
}
|
|
5349
|
+
const scrollOffset = viewport.measureScrollOffset();
|
|
5350
|
+
const viewportSize = viewport.getViewportSize();
|
|
5351
|
+
const start = Math.max(0, this.indexAt(scrollOffset - BUFFER_PX));
|
|
5352
|
+
const end = Math.min(n, this.indexAt(scrollOffset + viewportSize + BUFFER_PX) + 1);
|
|
5353
|
+
const range = { start, end };
|
|
5354
|
+
viewport.setRenderedRange(range);
|
|
5355
|
+
viewport.setRenderedContentOffset(this.offsets[start] ?? 0);
|
|
5356
|
+
this.indexChange.next(this.indexAt(scrollOffset));
|
|
5357
|
+
}
|
|
5358
|
+
}
|
|
5359
|
+
/**
|
|
5360
|
+
* Wires {@link AgridVariableRowSizeStrategy} into a `cdk-virtual-scroll-viewport`.
|
|
5361
|
+
*
|
|
5362
|
+
* Apply alongside an `agridVariableRowSize` binding carrying the per-item height array. Replaces
|
|
5363
|
+
* the viewport's `[itemSize]` binding. The same height array is fed to every (left/body/right)
|
|
5364
|
+
* viewport so the panes stay row-aligned.
|
|
5365
|
+
* @internal
|
|
5366
|
+
*/
|
|
5367
|
+
class AgridVariableRowSizeDirective {
|
|
5368
|
+
/** Per-item heights in pixels, one entry per virtual-scroll item. */
|
|
5369
|
+
itemSizes = input([], { ...(ngDevMode ? { debugName: "itemSizes" } : /* istanbul ignore next */ {}), alias: 'agridVariableRowSize' });
|
|
5370
|
+
strategy = new AgridVariableRowSizeStrategy();
|
|
5371
|
+
ngOnChanges() {
|
|
5372
|
+
this.strategy.updateItemSizes(this.itemSizes());
|
|
5373
|
+
}
|
|
5374
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridVariableRowSizeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
5375
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.17", type: AgridVariableRowSizeDirective, isStandalone: true, selector: "cdk-virtual-scroll-viewport[agridVariableRowSize]", inputs: { itemSizes: { classPropertyName: "itemSizes", publicName: "agridVariableRowSize", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
5376
|
+
{
|
|
5377
|
+
provide: VIRTUAL_SCROLL_STRATEGY,
|
|
5378
|
+
useFactory: (d) => d.strategy,
|
|
5379
|
+
deps: [forwardRef(() => AgridVariableRowSizeDirective)],
|
|
5380
|
+
},
|
|
5381
|
+
], usesOnChanges: true, ngImport: i0 });
|
|
5382
|
+
}
|
|
5383
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridVariableRowSizeDirective, decorators: [{
|
|
5384
|
+
type: Directive,
|
|
5385
|
+
args: [{
|
|
5386
|
+
selector: 'cdk-virtual-scroll-viewport[agridVariableRowSize]',
|
|
5387
|
+
providers: [
|
|
5388
|
+
{
|
|
5389
|
+
provide: VIRTUAL_SCROLL_STRATEGY,
|
|
5390
|
+
useFactory: (d) => d.strategy,
|
|
5391
|
+
deps: [forwardRef(() => AgridVariableRowSizeDirective)],
|
|
5392
|
+
},
|
|
5393
|
+
],
|
|
5394
|
+
}]
|
|
5395
|
+
}], propDecorators: { itemSizes: [{ type: i0.Input, args: [{ isSignal: true, alias: "agridVariableRowSize", required: false }] }] } });
|
|
4627
5396
|
|
|
4628
5397
|
/**
|
|
4629
5398
|
* Excel-like data grid for Angular 21.
|
|
@@ -4638,10 +5407,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
4638
5407
|
* |-----|--------|
|
|
4639
5408
|
* | Arrow keys | Move selection |
|
|
4640
5409
|
* | Tab / Shift+Tab | Move right / left (wraps rows) |
|
|
4641
|
-
* | Enter
|
|
5410
|
+
* | Enter | Enter edit mode |
|
|
5411
|
+
* | Ctrl/Cmd+Enter | Toggle an expandable tree node |
|
|
5412
|
+
* | F2 | Enter edit mode |
|
|
4642
5413
|
* | Printable key | Enter edit mode with seeded character |
|
|
4643
|
-
* | Escape |
|
|
4644
|
-
* | Tab / Enter (while editing) | Commit and move
|
|
5414
|
+
* | Escape | Close any open menu or cancel edit |
|
|
5415
|
+
* | Tab / Enter (while editing) | Commit and move according to navigation settings |
|
|
4645
5416
|
*/
|
|
4646
5417
|
class AgridComponent {
|
|
4647
5418
|
// ── Inputs ───────────────────────────────────────────────────────────────────
|
|
@@ -4654,16 +5425,18 @@ class AgridComponent {
|
|
|
4654
5425
|
allowAddRows = computed(() => this.provider().allowAddRows, ...(ngDevMode ? [{ debugName: "allowAddRows" }] : /* istanbul ignore next */ []));
|
|
4655
5426
|
autoAddRows = computed(() => this.provider().autoAddRows(), ...(ngDevMode ? [{ debugName: "autoAddRows" }] : /* istanbul ignore next */ []));
|
|
4656
5427
|
enableRowMarking = computed(() => this.provider().enableRowMarking, ...(ngDevMode ? [{ debugName: "enableRowMarking" }] : /* istanbul ignore next */ []));
|
|
4657
|
-
showControlColumn = computed(() => this.provider().showControlColumn || this.enableRowMarking(), ...(ngDevMode ? [{ debugName: "showControlColumn" }] : /* istanbul ignore next */ []));
|
|
5428
|
+
showControlColumn = computed(() => this.provider().showControlColumn || this.enableRowMarking() || this.masterDetail(), ...(ngDevMode ? [{ debugName: "showControlColumn" }] : /* istanbul ignore next */ []));
|
|
4658
5429
|
controlColumnWidth = computed(() => this.enableRowMarking() ? 48 : 24, ...(ngDevMode ? [{ debugName: "controlColumnWidth" }] : /* istanbul ignore next */ []));
|
|
4659
5430
|
showSidebar = computed(() => this.provider().showSidebar, ...(ngDevMode ? [{ debugName: "showSidebar" }] : /* istanbul ignore next */ []));
|
|
4660
5431
|
autoOpenDetail = computed(() => this.provider().autoOpenDetail, ...(ngDevMode ? [{ debugName: "autoOpenDetail" }] : /* istanbul ignore next */ []));
|
|
4661
5432
|
serverSideFiltering = computed(() => this.provider().serverSideFiltering, ...(ngDevMode ? [{ debugName: "serverSideFiltering" }] : /* istanbul ignore next */ []));
|
|
4662
5433
|
filterDebounceMs = computed(() => this.provider().filterDebounceMs, ...(ngDevMode ? [{ debugName: "filterDebounceMs" }] : /* istanbul ignore next */ []));
|
|
4663
5434
|
enableQuickFilter = computed(() => this.provider().enableQuickFilter, ...(ngDevMode ? [{ debugName: "enableQuickFilter" }] : /* istanbul ignore next */ []));
|
|
5435
|
+
menuBarItems = computed(() => this.provider().menuBarItems, ...(ngDevMode ? [{ debugName: "menuBarItems" }] : /* istanbul ignore next */ []));
|
|
4664
5436
|
quickFilterValue = computed(() => this.control()?.quickFilter() ?? '', ...(ngDevMode ? [{ debugName: "quickFilterValue" }] : /* istanbul ignore next */ []));
|
|
4665
5437
|
sortOption = computed(() => this.provider().sortOption, ...(ngDevMode ? [{ debugName: "sortOption" }] : /* istanbul ignore next */ []));
|
|
4666
5438
|
rowSelection = computed(() => this.provider().rowSelection, ...(ngDevMode ? [{ debugName: "rowSelection" }] : /* istanbul ignore next */ []));
|
|
5439
|
+
enterEditAction = computed(() => this.provider().enterEditAction, ...(ngDevMode ? [{ debugName: "enterEditAction" }] : /* istanbul ignore next */ []));
|
|
4667
5440
|
groupDescription = computed(() => this.provider().groupDescription, ...(ngDevMode ? [{ debugName: "groupDescription" }] : /* istanbul ignore next */ []));
|
|
4668
5441
|
groupActions = computed(() => this.provider().groupActions, ...(ngDevMode ? [{ debugName: "groupActions" }] : /* istanbul ignore next */ []));
|
|
4669
5442
|
cellMenuItems = computed(() => this.provider().cellMenuItems, ...(ngDevMode ? [{ debugName: "cellMenuItems" }] : /* istanbul ignore next */ []));
|
|
@@ -4676,10 +5449,48 @@ class AgridComponent {
|
|
|
4676
5449
|
loading = computed(() => this.provider().loading(), ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
4677
5450
|
emptyText = computed(() => this.provider().emptyText, ...(ngDevMode ? [{ debugName: "emptyText" }] : /* istanbul ignore next */ []));
|
|
4678
5451
|
useSidebarEditor = computed(() => this.provider().useSidebarEditor, ...(ngDevMode ? [{ debugName: "useSidebarEditor" }] : /* istanbul ignore next */ []));
|
|
5452
|
+
/** Host callback for per-row CSS classes, or `undefined`. */
|
|
5453
|
+
rowClassFn = computed(() => this.provider().getRowClass, ...(ngDevMode ? [{ debugName: "rowClassFn" }] : /* istanbul ignore next */ []));
|
|
5454
|
+
/** Host callback designating pinned rows, or `undefined`. */
|
|
5455
|
+
pinRowFn = computed(() => this.provider().pinRow, ...(ngDevMode ? [{ debugName: "pinRowFn" }] : /* istanbul ignore next */ []));
|
|
5456
|
+
/**
|
|
5457
|
+
* Effective pin resolver fed to the projection: a runtime UI override wins (including an explicit
|
|
5458
|
+
* `null` unpin), otherwise the provider `pinRow` predicate decides. Returns `undefined` when
|
|
5459
|
+
* neither pinning source is active, so the projection's pinning path stays off.
|
|
5460
|
+
*/
|
|
5461
|
+
effectivePinRow = computed(() => {
|
|
5462
|
+
const predicate = this.pinRowFn();
|
|
5463
|
+
const overrides = this._pinnedRows();
|
|
5464
|
+
if (!predicate && overrides.size === 0)
|
|
5465
|
+
return undefined;
|
|
5466
|
+
return (row, index) => {
|
|
5467
|
+
const override = overrides.get(index);
|
|
5468
|
+
if (override !== undefined)
|
|
5469
|
+
return override ?? undefined;
|
|
5470
|
+
return predicate?.(row, index);
|
|
5471
|
+
};
|
|
5472
|
+
}, ...(ngDevMode ? [{ debugName: "effectivePinRow" }] : /* istanbul ignore next */ []));
|
|
5473
|
+
/** Whether master/detail is enabled and applicable (flat rows or tree leaves; not grouped). */
|
|
5474
|
+
masterDetail = computed(() => this.provider().masterDetail && !!this.provider().detailRenderer
|
|
5475
|
+
&& !this.control()?.groupByField(), ...(ngDevMode ? [{ debugName: "masterDetail" }] : /* istanbul ignore next */ []));
|
|
5476
|
+
/** Fixed detail-panel height in pixels. */
|
|
5477
|
+
detailRowHeight = computed(() => this.provider().detailRowHeight, ...(ngDevMode ? [{ debugName: "detailRowHeight" }] : /* istanbul ignore next */ []));
|
|
4679
5478
|
/** Column definitions from the active provider. */
|
|
4680
5479
|
colDefs = computed(() => this.provider().columns(), ...(ngDevMode ? [{ debugName: "colDefs" }] : /* istanbul ignore next */ []));
|
|
4681
5480
|
/** Signal-based data container from the active provider. */
|
|
4682
5481
|
dataSource = computed(() => this.provider().datasource, ...(ngDevMode ? [{ debugName: "dataSource" }] : /* istanbul ignore next */ []));
|
|
5482
|
+
treeParentIds = computed(() => {
|
|
5483
|
+
const config = this.treeConfig();
|
|
5484
|
+
if (!config || isPathTreeConfig(config))
|
|
5485
|
+
return new Set();
|
|
5486
|
+
const ids = new Set();
|
|
5487
|
+
for (const row of this.dataSource().rows()) {
|
|
5488
|
+
const parentId = config.getParentId(row);
|
|
5489
|
+
if (parentId !== null && parentId !== undefined)
|
|
5490
|
+
ids.add(parentId);
|
|
5491
|
+
}
|
|
5492
|
+
return ids;
|
|
5493
|
+
}, ...(ngDevMode ? [{ debugName: "treeParentIds" }] : /* istanbul ignore next */ []));
|
|
4683
5494
|
/** Grid UI state container from the active provider. */
|
|
4684
5495
|
control = computed(() => this.provider().control, ...(ngDevMode ? [{ debugName: "control" }] : /* istanbul ignore next */ []));
|
|
4685
5496
|
/** Resolved locale code used for date formatting and built-in localization lookup. 'auto' is replaced with navigator.language. */
|
|
@@ -4707,6 +5518,10 @@ class AgridComponent {
|
|
|
4707
5518
|
rowDoubleClicked = output();
|
|
4708
5519
|
/** Emitted when the user single-clicks a data row. */
|
|
4709
5520
|
rowClick = output();
|
|
5521
|
+
/** Emitted when the user single-clicks a generated path-tree branch node. */
|
|
5522
|
+
treeNodeClick = output();
|
|
5523
|
+
/** Emitted when the user double-clicks a generated path-tree branch node. */
|
|
5524
|
+
treeNodeDoubleClicked = output();
|
|
4710
5525
|
/**
|
|
4711
5526
|
* Emitted once after a changed row is left during inline editing, or when the sidebar editor
|
|
4712
5527
|
* save button is used.
|
|
@@ -4730,11 +5545,23 @@ class AgridComponent {
|
|
|
4730
5545
|
quickFilterChange = output();
|
|
4731
5546
|
/** Emitted when a `ColDef.validate` hook rejects a committed value (inline or sidebar). */
|
|
4732
5547
|
validationFailed = output();
|
|
5548
|
+
/** Emitted when a column's optional cell information button is clicked. */
|
|
5549
|
+
cellInfo = output();
|
|
5550
|
+
/** Emitted for every enabled menu-bar button or dropdown item, carrying its configured id. */
|
|
5551
|
+
menuBarAction = output();
|
|
4733
5552
|
// ── Public state ─────────────────────────────────────────────────────────────
|
|
4734
5553
|
/** Currently focused cell, or `null`. */
|
|
4735
5554
|
selectedCell = signal(null, ...(ngDevMode ? [{ debugName: "selectedCell" }] : /* istanbul ignore next */ []));
|
|
4736
5555
|
/** Original index of the row awaiting delete confirmation, or `null`. */
|
|
4737
5556
|
pendingDeleteRow = signal(null, ...(ngDevMode ? [{ debugName: "pendingDeleteRow" }] : /* istanbul ignore next */ []));
|
|
5557
|
+
/** Original indices of rows whose master/detail panel is currently expanded. */
|
|
5558
|
+
_expandedDetailIds = signal(new Set(), ...(ngDevMode ? [{ debugName: "_expandedDetailIds" }] : /* istanbul ignore next */ []));
|
|
5559
|
+
/**
|
|
5560
|
+
* Runtime per-row pin overrides set through the UI (keyed by original index). A `null` value
|
|
5561
|
+
* explicitly unpins a row that the `pinRow` predicate would otherwise pin. Merged with the
|
|
5562
|
+
* provider predicate by {@link effectivePinRow}.
|
|
5563
|
+
*/
|
|
5564
|
+
_pinnedRows = signal(new Map(), ...(ngDevMode ? [{ debugName: "_pinnedRows" }] : /* istanbul ignore next */ []));
|
|
4738
5565
|
markedIndices = signal(new Set(), ...(ngDevMode ? [{ debugName: "markedIndices" }] : /* istanbul ignore next */ []));
|
|
4739
5566
|
/** Original datasource indices marked for inclusion in copy operations. */
|
|
4740
5567
|
markedRowIndices = this.markedIndices.asReadonly();
|
|
@@ -4744,6 +5571,8 @@ class AgridComponent {
|
|
|
4744
5571
|
deleteConfirmationWidth = signal(0, ...(ngDevMode ? [{ debugName: "deleteConfirmationWidth" }] : /* istanbul ignore next */ []));
|
|
4745
5572
|
/** Rectangular cell range selected by Shift+arrow or Shift+click. */
|
|
4746
5573
|
selectedRange = signal(null, ...(ngDevMode ? [{ debugName: "selectedRange" }] : /* istanbul ignore next */ []));
|
|
5574
|
+
/** @internal Stable callback passed to child components for row-aware editability checks. */
|
|
5575
|
+
isCellEditableForRow = (col, originalIndex) => this.isCellEditable(col, originalIndex);
|
|
4747
5576
|
/** Fill-handle drag preview bounds, in visible row/column coordinates. */
|
|
4748
5577
|
get fillPreviewBounds() { return this.rangeController.fillPreviewBounds; }
|
|
4749
5578
|
/** Position of the cell in edit mode, or `null`. */
|
|
@@ -4752,6 +5581,8 @@ class AgridComponent {
|
|
|
4752
5581
|
get currentDraft() { return this.editController.currentDraft; }
|
|
4753
5582
|
/** Seed character typed to enter edit mode (e.g. pressing 'A'). */
|
|
4754
5583
|
get editSeedChar() { return this.editController.editSeedChar; }
|
|
5584
|
+
/** Whether the active text editor should select all text when it opens. */
|
|
5585
|
+
get selectTextOnEdit() { return this.editController.selectTextOnEdit; }
|
|
4755
5586
|
/** Toggle the sidebar open/closed. */
|
|
4756
5587
|
toggleSidebar() { this.sidebarController.toggle(); }
|
|
4757
5588
|
/** @internal */
|
|
@@ -4898,6 +5729,9 @@ class AgridComponent {
|
|
|
4898
5729
|
expandedGroups: this.groupController.expandedGroups,
|
|
4899
5730
|
treeConfig: this.treeConfig,
|
|
4900
5731
|
expandedTreeIds: this.treeController.expandedIds,
|
|
5732
|
+
pinRow: this.effectivePinRow,
|
|
5733
|
+
masterDetail: this.masterDetail,
|
|
5734
|
+
expandedDetailIds: this._expandedDetailIds,
|
|
4901
5735
|
});
|
|
4902
5736
|
editController = new AgridEditController({
|
|
4903
5737
|
control: this.control,
|
|
@@ -4919,8 +5753,9 @@ class AgridComponent {
|
|
|
4919
5753
|
showPagination = this.projection.showPagination;
|
|
4920
5754
|
/** Number of semantic header rows currently rendered. */
|
|
4921
5755
|
headerRowCount = computed(() => this.hasHeaderGroups() ? 2 : 1, ...(ngDevMode ? [{ debugName: "headerRowCount" }] : /* istanbul ignore next */ []));
|
|
4922
|
-
/** Number of rendered semantic rows, including header rows. */
|
|
4923
|
-
ariaRowCount = computed(() => this.displayItems().length + this.headerRowCount() + (this.showFooter() ? 1 : 0)
|
|
5756
|
+
/** Number of rendered semantic rows, including header and pinned rows. */
|
|
5757
|
+
ariaRowCount = computed(() => this.displayItems().length + this.headerRowCount() + (this.showFooter() ? 1 : 0)
|
|
5758
|
+
+ this.pinnedTopItems().length + this.pinnedBottomItems().length, ...(ngDevMode ? [{ debugName: "ariaRowCount" }] : /* istanbul ignore next */ []));
|
|
4924
5759
|
/** Number of visible semantic columns, including the optional control column. */
|
|
4925
5760
|
ariaColCount = computed(() => this.visibleColDefs().length + (this.showControlColumn() ? 1 : 0), ...(ngDevMode ? [{ debugName: "ariaColCount" }] : /* istanbul ignore next */ []));
|
|
4926
5761
|
/** True when no rows or group headers are visible (ignores add-row sentinel and ghost). */
|
|
@@ -4982,6 +5817,24 @@ class AgridComponent {
|
|
|
4982
5817
|
result.splice(insertAt, 0, 'ghost');
|
|
4983
5818
|
return result;
|
|
4984
5819
|
}, ...(ngDevMode ? [{ debugName: "displayItems" }] : /* istanbul ignore next */ []));
|
|
5820
|
+
/** Rows pinned to the top of the body (rendered in a fixed container, outside virtual scroll). */
|
|
5821
|
+
pinnedTopItems = this.projection.pinnedTopItems;
|
|
5822
|
+
/** Rows pinned to the bottom of the body (rendered in a fixed container, outside virtual scroll). */
|
|
5823
|
+
pinnedBottomItems = this.projection.pinnedBottomItems;
|
|
5824
|
+
/** Whether any top-pinned rows are present. */
|
|
5825
|
+
hasPinnedTopRows = computed(() => this.pinnedTopItems().length > 0, ...(ngDevMode ? [{ debugName: "hasPinnedTopRows" }] : /* istanbul ignore next */ []));
|
|
5826
|
+
/** Whether any bottom-pinned rows are present. */
|
|
5827
|
+
hasPinnedBottomRows = computed(() => this.pinnedBottomItems().length > 0, ...(ngDevMode ? [{ debugName: "hasPinnedBottomRows" }] : /* istanbul ignore next */ []));
|
|
5828
|
+
/**
|
|
5829
|
+
* Per-item heights fed to the variable-size virtual-scroll strategy: a detail panel uses the
|
|
5830
|
+
* configured detail height, every other row uses the standard row height. With no detail rows
|
|
5831
|
+
* open the array is uniform, so scrolling matches the fixed-size strategy.
|
|
5832
|
+
*/
|
|
5833
|
+
itemSizes = computed(() => {
|
|
5834
|
+
const h = this.rowHeight();
|
|
5835
|
+
const dh = this.detailRowHeight();
|
|
5836
|
+
return this.displayItems().map(item => (isDetailRowItem(item) ? dh : h));
|
|
5837
|
+
}, ...(ngDevMode ? [{ debugName: "itemSizes" }] : /* istanbul ignore next */ []));
|
|
4985
5838
|
// ── Menu signals ─────────────────────────────────────────────────────────────
|
|
4986
5839
|
groupActionsMenu = this.groupController.actionsMenu;
|
|
4987
5840
|
// ── Infrastructure ────────────────────────────────────────────────────────────
|
|
@@ -5000,7 +5853,7 @@ class AgridComponent {
|
|
|
5000
5853
|
visibleColDefs: this.visibleColDefs,
|
|
5001
5854
|
selectedCell: this.selectedCell,
|
|
5002
5855
|
selectedRange: this.selectedRange,
|
|
5003
|
-
isCellEditable: col => this.isCellEditable(col),
|
|
5856
|
+
isCellEditable: (col, originalIndex) => this.isCellEditable(col, originalIndex),
|
|
5004
5857
|
cancelEdit: () => this.cancelCurrent(),
|
|
5005
5858
|
findDisplayIndex: originalIndex => this.findDisplayIndex(originalIndex),
|
|
5006
5859
|
scrollToCell: (displayIndex, colIndex) => this.scrollToKeepVisible(displayIndex, colIndex),
|
|
@@ -5039,14 +5892,16 @@ class AgridComponent {
|
|
|
5039
5892
|
visibleColDefs: this.visibleColDefs,
|
|
5040
5893
|
filteredItems: this.filteredItems,
|
|
5041
5894
|
locale: this.locale,
|
|
5895
|
+
getRowClass: this.rowClassFn,
|
|
5042
5896
|
});
|
|
5043
5897
|
findController = new AgridFindController({
|
|
5044
|
-
|
|
5898
|
+
dataSource: this.dataSource,
|
|
5899
|
+
filteredSortedIndices: this.projection.filteredSortedIndices,
|
|
5045
5900
|
visibleColDefs: this.visibleColDefs,
|
|
5046
5901
|
locale: this.locale,
|
|
5047
5902
|
selectedCell: this.selectedCell,
|
|
5048
5903
|
selectedRange: this.selectedRange,
|
|
5049
|
-
|
|
5904
|
+
revealMatch: (originalIndex, colIndex) => this.revealFindMatch(originalIndex, colIndex),
|
|
5050
5905
|
focusGrid: () => this.wrapperEl().nativeElement.focus(),
|
|
5051
5906
|
});
|
|
5052
5907
|
findOpen = this.findController.open;
|
|
@@ -5063,11 +5918,14 @@ class AgridComponent {
|
|
|
5063
5918
|
rowHeight: this.rowHeight,
|
|
5064
5919
|
allowAddRows: this.allowAddRows,
|
|
5065
5920
|
autoAddRows: this.autoAddRows,
|
|
5921
|
+
enterEditAction: this.enterEditAction,
|
|
5066
5922
|
selectedCell: this.selectedCell,
|
|
5067
5923
|
selectedRange: this.selectedRange,
|
|
5068
5924
|
editingCell: this.editController.editingCell,
|
|
5069
5925
|
isEditing: (originalIndex, colIndex) => this.isEditing(originalIndex, colIndex),
|
|
5070
|
-
|
|
5926
|
+
isCellEditable: (col, originalIndex) => this.editController.isCellEditable(col, originalIndex),
|
|
5927
|
+
toggleTreeCell: (originalIndex, colIndex) => this.toggleTreeCell(originalIndex, colIndex),
|
|
5928
|
+
startEdit: (originalIndex, colIndex, seedChar, selectText) => this.editController.start(originalIndex, colIndex, seedChar, selectText),
|
|
5071
5929
|
commitEdit: () => this.editController.commit(),
|
|
5072
5930
|
cancelEdit: () => this.editController.cancel(),
|
|
5073
5931
|
undoEdit: () => this.editController.undo(),
|
|
@@ -5112,6 +5970,26 @@ class AgridComponent {
|
|
|
5112
5970
|
selectedRowIndex = this.rowController.selectedRowIndex;
|
|
5113
5971
|
contextMenu = this.rowController.contextMenu;
|
|
5114
5972
|
cellContextMenuState = this.rowController.cellContextMenu;
|
|
5973
|
+
/** Id of the menu-bar button whose dropdown is open, or `null`. */
|
|
5974
|
+
openMenuBarItemId = signal(null, ...(ngDevMode ? [{ debugName: "openMenuBarItemId" }] : /* istanbul ignore next */ []));
|
|
5975
|
+
/** Runtime state passed to menu-bar visibility, active, and disabled resolvers. */
|
|
5976
|
+
menuBarContext = computed(() => {
|
|
5977
|
+
const datasource = this.dataSource();
|
|
5978
|
+
const rows = datasource.rows();
|
|
5979
|
+
const selectedRows = [...this.selectedRowIndices()]
|
|
5980
|
+
.sort((a, b) => a - b)
|
|
5981
|
+
.map(originalIndex => ({ row: rows[originalIndex], originalIndex }))
|
|
5982
|
+
.filter((entry) => !!entry.row);
|
|
5983
|
+
return {
|
|
5984
|
+
rows,
|
|
5985
|
+
selectedRows,
|
|
5986
|
+
selectedCell: this.selectedCell(),
|
|
5987
|
+
provider: this.provider(),
|
|
5988
|
+
datasource,
|
|
5989
|
+
};
|
|
5990
|
+
}, ...(ngDevMode ? [{ debugName: "menuBarContext" }] : /* istanbul ignore next */ []));
|
|
5991
|
+
/** Menu-bar buttons currently allowed by their visibility resolvers. */
|
|
5992
|
+
visibleMenuBarItems = computed(() => this.menuBarItems().filter(item => this.isMenuBarItemVisible(item)), ...(ngDevMode ? [{ debugName: "visibleMenuBarItems" }] : /* istanbul ignore next */ []));
|
|
5115
5993
|
sidebarController = new AgridSidebarController({
|
|
5116
5994
|
control: this.control,
|
|
5117
5995
|
dataSource: this.dataSource,
|
|
@@ -5120,6 +5998,7 @@ class AgridComponent {
|
|
|
5120
5998
|
selectedRowIndex: this.selectedRowIndex,
|
|
5121
5999
|
autoOpenDetail: this.autoOpenDetail,
|
|
5122
6000
|
useSidebarEditor: this.useSidebarEditor,
|
|
6001
|
+
isCellEditable: (col, originalIndex) => this.isCellEditable(col, originalIndex),
|
|
5123
6002
|
onFieldChange: event => this.markCellChanged(event),
|
|
5124
6003
|
onCellEdit: event => this.emitSidebarEditEvents(event),
|
|
5125
6004
|
onValidationFailed: event => this.validationFailed.emit({ ...event, source: 'sidebar' }),
|
|
@@ -5139,7 +6018,7 @@ class AgridComponent {
|
|
|
5139
6018
|
selectedCell: this.selectedCell,
|
|
5140
6019
|
selectedRange: this.selectedRange,
|
|
5141
6020
|
markedRowIndices: this.markedRowIndices,
|
|
5142
|
-
isCellEditable: col => this.isCellEditable(col),
|
|
6021
|
+
isCellEditable: (col, originalIndex) => this.isCellEditable(col, originalIndex),
|
|
5143
6022
|
onCellEdit: event => this.emitEditEvents(event),
|
|
5144
6023
|
scrollToCell: (displayIndex, colIndex) => this.scrollToKeepVisible(displayIndex, colIndex),
|
|
5145
6024
|
});
|
|
@@ -5188,6 +6067,10 @@ class AgridComponent {
|
|
|
5188
6067
|
getColReorderOffset(field) {
|
|
5189
6068
|
return this.columnReorder.getHeaderOffset(field);
|
|
5190
6069
|
}
|
|
6070
|
+
hasContextMenuEntries() {
|
|
6071
|
+
const hme = (!this.readonlyGrid() && !this.treeConfig());
|
|
6072
|
+
return hme;
|
|
6073
|
+
}
|
|
5191
6074
|
// ── Setup ─────────────────────────────────────────────────────────────────────
|
|
5192
6075
|
_seededControls = new WeakSet();
|
|
5193
6076
|
dirtyInlineRows = new Set();
|
|
@@ -5312,6 +6195,15 @@ class AgridComponent {
|
|
|
5312
6195
|
wrapper.addEventListener('keydown', onKeyDown, { capture: true });
|
|
5313
6196
|
this.destroyRef.onDestroy(() => wrapper.removeEventListener('keydown', onKeyDown, { capture: true }));
|
|
5314
6197
|
});
|
|
6198
|
+
const onDocumentKeyDown = (event) => {
|
|
6199
|
+
if (event.key !== 'Escape' || event.defaultPrevented)
|
|
6200
|
+
return;
|
|
6201
|
+
if (this.closeOpenMenus()) {
|
|
6202
|
+
event.preventDefault();
|
|
6203
|
+
event.stopPropagation();
|
|
6204
|
+
}
|
|
6205
|
+
};
|
|
6206
|
+
this.browser.addDocumentListener('keydown', onDocumentKeyDown);
|
|
5315
6207
|
// Emit pageChange whenever page or pageSize changes in server-side pagination mode.
|
|
5316
6208
|
effect(() => {
|
|
5317
6209
|
const ctrl = this.control();
|
|
@@ -5332,19 +6224,22 @@ class AgridComponent {
|
|
|
5332
6224
|
});
|
|
5333
6225
|
// Deselect when clicking outside the grid.
|
|
5334
6226
|
const onOutsidePointerDown = (e) => {
|
|
5335
|
-
|
|
6227
|
+
const isInsideGrid = this._hostEl.nativeElement.contains(e.target);
|
|
6228
|
+
if (!isInsideGrid) {
|
|
6229
|
+
this.closeOpenMenus();
|
|
5336
6230
|
queueMicrotask(() => this.flushDirtyInlineRows());
|
|
5337
6231
|
}
|
|
5338
6232
|
if (this.rowSelection() === 'none')
|
|
5339
6233
|
return;
|
|
5340
6234
|
if (this.selectedRowIndices().size === 0)
|
|
5341
6235
|
return;
|
|
5342
|
-
if (
|
|
6236
|
+
if (isInsideGrid)
|
|
5343
6237
|
return;
|
|
5344
6238
|
this.rowController.clearSelection();
|
|
5345
6239
|
};
|
|
5346
6240
|
this.browser.addDocumentListener('pointerdown', onOutsidePointerDown);
|
|
5347
6241
|
this.destroyRef.onDestroy(() => {
|
|
6242
|
+
this.browser.removeDocumentListener('keydown', onDocumentKeyDown);
|
|
5348
6243
|
this.browser.removeDocumentListener('pointerdown', onOutsidePointerDown);
|
|
5349
6244
|
if (this.quickFilterTimer !== null)
|
|
5350
6245
|
clearTimeout(this.quickFilterTimer);
|
|
@@ -5391,9 +6286,94 @@ class AgridComponent {
|
|
|
5391
6286
|
return isGroupHeaderItem(item);
|
|
5392
6287
|
}
|
|
5393
6288
|
/** @internal */
|
|
6289
|
+
isPathTreeNodeItem(item) {
|
|
6290
|
+
return isPathTreeNodeItem(item);
|
|
6291
|
+
}
|
|
6292
|
+
/** @internal */
|
|
5394
6293
|
getItemOriginalIndex(item) {
|
|
5395
6294
|
return isDataRowItem(item) ? item.originalIndex : null;
|
|
5396
6295
|
}
|
|
6296
|
+
/** @internal True when the item is a master/detail panel row. */
|
|
6297
|
+
isDetailRowItem(item) {
|
|
6298
|
+
return isDetailRowItem(item);
|
|
6299
|
+
}
|
|
6300
|
+
/** @internal Rendered pixel height of a virtual-scroll item (detail panels are taller). */
|
|
6301
|
+
rowPx(item) {
|
|
6302
|
+
return isDetailRowItem(item) ? this.detailRowHeight() : this.rowHeight();
|
|
6303
|
+
}
|
|
6304
|
+
/** @internal Resolved HTML for an expanded detail panel (auto-sanitized by `[innerHTML]`). */
|
|
6305
|
+
detailHtml(item) {
|
|
6306
|
+
if (!isDetailRowItem(item))
|
|
6307
|
+
return '';
|
|
6308
|
+
return this.provider().detailRenderer?.({ row: item.row }) ?? '';
|
|
6309
|
+
}
|
|
6310
|
+
/** @internal Resolved per-row CSS classes from the host `getRowClass` callback. */
|
|
6311
|
+
getRowClass(row, index) {
|
|
6312
|
+
return this.presentation.getRowClass(row, index);
|
|
6313
|
+
}
|
|
6314
|
+
/** Whether the master/detail panel for `originalIndex` is currently expanded. */
|
|
6315
|
+
isDetailExpanded(originalIndex) {
|
|
6316
|
+
return this._expandedDetailIds().has(originalIndex);
|
|
6317
|
+
}
|
|
6318
|
+
/** @internal Whether a data row may show a master/detail panel. */
|
|
6319
|
+
canToggleDetail(item) {
|
|
6320
|
+
if (!this.masterDetail() || !isDataRowItem(item))
|
|
6321
|
+
return false;
|
|
6322
|
+
const config = this.treeConfig();
|
|
6323
|
+
return !config
|
|
6324
|
+
|| isPathTreeConfig(config)
|
|
6325
|
+
|| !this.treeParentIds().has(config.getId(item.row));
|
|
6326
|
+
}
|
|
6327
|
+
/** Toggle the master/detail panel for a row by its original (data-source) index. */
|
|
6328
|
+
toggleDetail(originalIndex) {
|
|
6329
|
+
const row = this.dataSource().getRow(originalIndex);
|
|
6330
|
+
const config = this.treeConfig();
|
|
6331
|
+
if (!row
|
|
6332
|
+
|| (config
|
|
6333
|
+
&& !isPathTreeConfig(config)
|
|
6334
|
+
&& this.treeParentIds().has(config.getId(row))))
|
|
6335
|
+
return;
|
|
6336
|
+
this._expandedDetailIds.update(ids => {
|
|
6337
|
+
const next = new Set(ids);
|
|
6338
|
+
if (next.has(originalIndex))
|
|
6339
|
+
next.delete(originalIndex);
|
|
6340
|
+
else
|
|
6341
|
+
next.add(originalIndex);
|
|
6342
|
+
return next;
|
|
6343
|
+
});
|
|
6344
|
+
}
|
|
6345
|
+
/** @internal Template handler for the detail expander chevron. */
|
|
6346
|
+
onDetailToggle(originalIndex) {
|
|
6347
|
+
this.toggleDetail(originalIndex);
|
|
6348
|
+
}
|
|
6349
|
+
/** Effective pin position of a row (`'top'`, `'bottom'`, or `undefined`). */
|
|
6350
|
+
rowPinState(originalIndex) {
|
|
6351
|
+
const resolver = this.effectivePinRow();
|
|
6352
|
+
if (!resolver)
|
|
6353
|
+
return undefined;
|
|
6354
|
+
return resolver(this.dataSource().rows()[originalIndex], originalIndex);
|
|
6355
|
+
}
|
|
6356
|
+
/**
|
|
6357
|
+
* Pin a row to the top or bottom of the body, or unpin it with `null`.
|
|
6358
|
+
* Keyed by the row's original (data-source) index; the pinned row stays fully interactive.
|
|
6359
|
+
*/
|
|
6360
|
+
pinRowTo(originalIndex, position) {
|
|
6361
|
+
this._pinnedRows.update(map => {
|
|
6362
|
+
const next = new Map(map);
|
|
6363
|
+
// When no predicate could re-pin the row, an unpin can simply drop the override.
|
|
6364
|
+
if (position === null && !this.pinRowFn())
|
|
6365
|
+
next.delete(originalIndex);
|
|
6366
|
+
else
|
|
6367
|
+
next.set(originalIndex, position);
|
|
6368
|
+
return next;
|
|
6369
|
+
});
|
|
6370
|
+
}
|
|
6371
|
+
/** @internal Template handler for the pin/unpin context-menu items; closes the open menus. */
|
|
6372
|
+
onPinRow(originalIndex, position) {
|
|
6373
|
+
this.pinRowTo(originalIndex, position);
|
|
6374
|
+
this.rowController.closeContextMenu();
|
|
6375
|
+
this.rowController.closeCellContextMenu();
|
|
6376
|
+
}
|
|
5397
6377
|
// ── Template helpers — tree ───────────────────────────────────────────────────
|
|
5398
6378
|
/** @internal True when `col` is the configured tree column. */
|
|
5399
6379
|
isTreeCell(col) {
|
|
@@ -5401,28 +6381,101 @@ class AgridComponent {
|
|
|
5401
6381
|
}
|
|
5402
6382
|
/** @internal Tree depth of a row item (0 when not a tree row). */
|
|
5403
6383
|
treeRowLevel(item) {
|
|
5404
|
-
return isTreeRowItem(item) ? item.level : 0;
|
|
6384
|
+
return isTreeRowItem(item) || isPathTreeNodeItem(item) ? item.level : 0;
|
|
5405
6385
|
}
|
|
5406
6386
|
/** @internal Whether a tree row has children and can be expanded. */
|
|
5407
6387
|
treeRowExpandable(item) {
|
|
5408
|
-
return isTreeRowItem(item) && item.expandable;
|
|
6388
|
+
return (isTreeRowItem(item) || isPathTreeNodeItem(item)) && item.expandable;
|
|
5409
6389
|
}
|
|
5410
6390
|
/** @internal Whether a tree row is currently expanded. */
|
|
5411
6391
|
treeRowExpanded(item) {
|
|
5412
|
-
return isTreeRowItem(item) && item.expanded;
|
|
6392
|
+
return (isTreeRowItem(item) || isPathTreeNodeItem(item)) && item.expanded;
|
|
6393
|
+
}
|
|
6394
|
+
/** @internal Display-only final path segment for a datasource-backed path-tree leaf. */
|
|
6395
|
+
treeCellDisplayOverride(item, col) {
|
|
6396
|
+
return isTreeRowItem(item) && this.isTreeCell(col) ? item.treeLabel ?? null : null;
|
|
6397
|
+
}
|
|
6398
|
+
/** @internal Whether the configured info action is visible for this cell. */
|
|
6399
|
+
showCellInfoIcon(col, row) {
|
|
6400
|
+
return typeof col.infoIcon === 'function'
|
|
6401
|
+
? col.infoIcon({ value: row[col.field], row })
|
|
6402
|
+
: col.infoIcon === true;
|
|
6403
|
+
}
|
|
6404
|
+
/** @internal Emits the typed cell information action. */
|
|
6405
|
+
onCellInfo(originalIndex, col, row) {
|
|
6406
|
+
this.cellInfo.emit({
|
|
6407
|
+
row: row,
|
|
6408
|
+
field: col.field,
|
|
6409
|
+
value: row[col.field],
|
|
6410
|
+
originalIndex,
|
|
6411
|
+
column: col,
|
|
6412
|
+
});
|
|
6413
|
+
}
|
|
6414
|
+
/** @internal Label of a generated path-tree branch. */
|
|
6415
|
+
pathTreeLabel(item) {
|
|
6416
|
+
return isPathTreeNodeItem(item) ? item.pathLabel : '';
|
|
5413
6417
|
}
|
|
5414
6418
|
/** @internal Toggle the expand/collapse state of a tree row from its twisty. */
|
|
5415
6419
|
onTreeToggle(item) {
|
|
5416
6420
|
const config = this.treeConfig();
|
|
5417
|
-
if (!config
|
|
6421
|
+
if (!config)
|
|
6422
|
+
return;
|
|
6423
|
+
if (isPathTreeNodeItem(item)) {
|
|
6424
|
+
this.treeController.toggle(item.pathNodeId);
|
|
6425
|
+
}
|
|
6426
|
+
else if (isTreeRowItem(item) && !isPathTreeConfig(config)) {
|
|
6427
|
+
this.treeController.toggle(config.getId(item.row));
|
|
6428
|
+
}
|
|
6429
|
+
}
|
|
6430
|
+
/** @internal Emits the generated path-tree branch click event. */
|
|
6431
|
+
onTreeNodeClick(item) {
|
|
6432
|
+
if (!isPathTreeNodeItem(item))
|
|
5418
6433
|
return;
|
|
6434
|
+
this.treeNodeClick.emit(this.toTreeNodeClickEvent(item));
|
|
6435
|
+
}
|
|
6436
|
+
/** @internal Emits the generated path-tree branch double-click event. */
|
|
6437
|
+
onTreeNodeDoubleClick(item) {
|
|
6438
|
+
if (!isPathTreeNodeItem(item))
|
|
6439
|
+
return;
|
|
6440
|
+
this.treeNodeDoubleClicked.emit(this.toTreeNodeClickEvent(item));
|
|
6441
|
+
}
|
|
6442
|
+
toTreeNodeClickEvent(item) {
|
|
6443
|
+
return {
|
|
6444
|
+
uuid: item.uuid,
|
|
6445
|
+
pathNodeId: item.pathNodeId,
|
|
6446
|
+
pathLabel: item.pathLabel,
|
|
6447
|
+
level: item.level,
|
|
6448
|
+
expanded: item.expanded,
|
|
6449
|
+
node: { ...item },
|
|
6450
|
+
};
|
|
6451
|
+
}
|
|
6452
|
+
toggleTreeCell(originalIndex, colIndex) {
|
|
6453
|
+
const config = this.treeConfig();
|
|
6454
|
+
const col = this.visibleColDefs()[colIndex];
|
|
6455
|
+
if (!config || !col || config.treeField !== col.field)
|
|
6456
|
+
return false;
|
|
6457
|
+
const item = this.displayItems().find(candidate => isDataRowItem(candidate) && candidate.originalIndex === originalIndex);
|
|
6458
|
+
if (!item || !isTreeRowItem(item) || !item.expandable || isPathTreeConfig(config))
|
|
6459
|
+
return false;
|
|
5419
6460
|
this.treeController.toggle(config.getId(item.row));
|
|
6461
|
+
return true;
|
|
5420
6462
|
}
|
|
5421
6463
|
/** Expand every expandable node in the tree. No-op when not in tree mode. */
|
|
5422
6464
|
expandAllNodes() {
|
|
5423
6465
|
const config = this.treeConfig();
|
|
5424
6466
|
if (!config)
|
|
5425
6467
|
return;
|
|
6468
|
+
if (isPathTreeConfig(config)) {
|
|
6469
|
+
const ids = new Set();
|
|
6470
|
+
for (const row of this.dataSource().rows()) {
|
|
6471
|
+
const path = config.getPath(row).map(String).filter(Boolean);
|
|
6472
|
+
for (let length = 1; length < path.length; length++) {
|
|
6473
|
+
ids.add(pathTreeNodeId(path.slice(0, length)));
|
|
6474
|
+
}
|
|
6475
|
+
}
|
|
6476
|
+
this.treeController.expandAll(ids);
|
|
6477
|
+
return;
|
|
6478
|
+
}
|
|
5426
6479
|
const expandable = new Set();
|
|
5427
6480
|
for (const row of this.dataSource().rows()) {
|
|
5428
6481
|
const parentId = config.getParentId(row);
|
|
@@ -5443,6 +6496,10 @@ class AgridComponent {
|
|
|
5443
6496
|
return -1;
|
|
5444
6497
|
if (isGroupHeaderItem(item))
|
|
5445
6498
|
return `__group__${item.groupLabel}`;
|
|
6499
|
+
if (isDetailRowItem(item))
|
|
6500
|
+
return `__detail__${item.detailFor}`;
|
|
6501
|
+
if (isPathTreeNodeItem(item))
|
|
6502
|
+
return item.pathNodeId;
|
|
5446
6503
|
return item.originalIndex;
|
|
5447
6504
|
};
|
|
5448
6505
|
// ── Template helpers — cell/selection state ───────────────────────────────────
|
|
@@ -5482,10 +6539,28 @@ class AgridComponent {
|
|
|
5482
6539
|
return this.columnMenuController.hasMultiSort();
|
|
5483
6540
|
}
|
|
5484
6541
|
getTextFilter(field) { return this.columnMenuController.getTextFilter(field); }
|
|
5485
|
-
/** @internal
|
|
6542
|
+
/** @internal Condition input type for a column, or `null` when unsupported. */
|
|
5486
6543
|
getMenuFilterType(field) {
|
|
5487
6544
|
return this.columnMenuController.getFilterType(field);
|
|
5488
6545
|
}
|
|
6546
|
+
/** @internal Short label for an active header condition. */
|
|
6547
|
+
getConditionButtonLabel(field) {
|
|
6548
|
+
switch (this.getMenuOperator(field)) {
|
|
6549
|
+
case 'eq': return '=';
|
|
6550
|
+
case 'neq': return '≠';
|
|
6551
|
+
case 'gt': return '>';
|
|
6552
|
+
case 'gte': return '≥';
|
|
6553
|
+
case 'lt': return '<';
|
|
6554
|
+
case 'lte': return '≤';
|
|
6555
|
+
case 'between': return '↔';
|
|
6556
|
+
case 'like': return '~';
|
|
6557
|
+
case 'startsWith': return 'A…';
|
|
6558
|
+
case 'endsWith': return '…Z';
|
|
6559
|
+
case 'includes': return '⊃';
|
|
6560
|
+
case 'notIncludes': return '⊅';
|
|
6561
|
+
default: return '⋯';
|
|
6562
|
+
}
|
|
6563
|
+
}
|
|
5489
6564
|
/** @internal */
|
|
5490
6565
|
getMenuOperator(field) {
|
|
5491
6566
|
return this.columnMenuController.getFilterOperator(field);
|
|
@@ -5611,7 +6686,9 @@ class AgridComponent {
|
|
|
5611
6686
|
}, delay);
|
|
5612
6687
|
}
|
|
5613
6688
|
/** @internal Whether a column is editable in the current grid state (drives boolean checkboxes). */
|
|
5614
|
-
isColEditable(col) {
|
|
6689
|
+
isColEditable(col, originalIndex) {
|
|
6690
|
+
return this.editController.isCellEditable(col, originalIndex);
|
|
6691
|
+
}
|
|
5615
6692
|
/** @internal Inline validation message for a cell, or `null` when the cell has no active error. */
|
|
5616
6693
|
cellValidationError(originalIndex, ci) {
|
|
5617
6694
|
const error = this.editController.validationError();
|
|
@@ -5627,6 +6704,11 @@ class AgridComponent {
|
|
|
5627
6704
|
}
|
|
5628
6705
|
/** @internal Main keyboard handler delegated from the wrapper div. */
|
|
5629
6706
|
onKeyDown(event) {
|
|
6707
|
+
if (event.key === 'Escape' && this.closeOpenMenus()) {
|
|
6708
|
+
event.preventDefault();
|
|
6709
|
+
event.stopPropagation();
|
|
6710
|
+
return;
|
|
6711
|
+
}
|
|
5630
6712
|
if (event.key === 'Escape' && this.pendingDeleteRow() !== null) {
|
|
5631
6713
|
event.preventDefault();
|
|
5632
6714
|
event.stopPropagation();
|
|
@@ -5657,6 +6739,47 @@ class AgridComponent {
|
|
|
5657
6739
|
goToFindMatch(direction) {
|
|
5658
6740
|
this.findController.goToMatch(direction);
|
|
5659
6741
|
}
|
|
6742
|
+
revealFindMatch(originalIndex, colIndex) {
|
|
6743
|
+
const config = this.treeConfig();
|
|
6744
|
+
if (config) {
|
|
6745
|
+
const rows = this.dataSource().rows();
|
|
6746
|
+
const expanded = new Set(this.treeController.expandedIds());
|
|
6747
|
+
if (isPathTreeConfig(config)) {
|
|
6748
|
+
const path = config.getPath(rows[originalIndex]).map(String).filter(Boolean);
|
|
6749
|
+
for (let length = 1; length < path.length; length++) {
|
|
6750
|
+
expanded.add(pathTreeNodeId(path.slice(0, length)));
|
|
6751
|
+
}
|
|
6752
|
+
}
|
|
6753
|
+
else {
|
|
6754
|
+
const idToRow = new Map(rows.map(row => [config.getId(row), row]));
|
|
6755
|
+
const visited = new Set();
|
|
6756
|
+
let parentId = config.getParentId(rows[originalIndex]);
|
|
6757
|
+
while (parentId !== null && parentId !== undefined && !visited.has(parentId)) {
|
|
6758
|
+
visited.add(parentId);
|
|
6759
|
+
expanded.add(parentId);
|
|
6760
|
+
const parent = idToRow.get(parentId);
|
|
6761
|
+
if (!parent)
|
|
6762
|
+
break;
|
|
6763
|
+
parentId = config.getParentId(parent);
|
|
6764
|
+
}
|
|
6765
|
+
}
|
|
6766
|
+
this.treeController.expandAll(expanded);
|
|
6767
|
+
}
|
|
6768
|
+
else {
|
|
6769
|
+
const control = this.control();
|
|
6770
|
+
const pageSize = control?.pageSize() ?? 0;
|
|
6771
|
+
const filteredIndex = this.projection.filteredSortedIndices().indexOf(originalIndex);
|
|
6772
|
+
if (control && pageSize > 0 && control.totalRows() === 0 && filteredIndex >= 0) {
|
|
6773
|
+
control.setPage(Math.floor(filteredIndex / pageSize) + 1);
|
|
6774
|
+
}
|
|
6775
|
+
}
|
|
6776
|
+
setTimeout(() => {
|
|
6777
|
+
const displayIndex = this.navigationController.findDisplayIndex(originalIndex);
|
|
6778
|
+
if (displayIndex >= 0) {
|
|
6779
|
+
this.navigationController.scrollToKeepVisible(displayIndex, colIndex);
|
|
6780
|
+
}
|
|
6781
|
+
});
|
|
6782
|
+
}
|
|
5660
6783
|
// ── Row reorder ───────────────────────────────────────────────────────────────
|
|
5661
6784
|
/** @internal Ghost cell display during a reorder drag. */
|
|
5662
6785
|
getGhostCellDisplay(col) { return this.dragHandler.getGhostDisplay(col); }
|
|
@@ -5669,7 +6792,7 @@ class AgridComponent {
|
|
|
5669
6792
|
/** @internal Handles the control column without letting the row receive a second pointer event. */
|
|
5670
6793
|
onControlPointerDown(event, originalIndex) {
|
|
5671
6794
|
event.stopPropagation();
|
|
5672
|
-
if (this.allowRowReorder()) {
|
|
6795
|
+
if (this.allowRowReorder() && event.pointerType === 'mouse' && event.button === 0) {
|
|
5673
6796
|
this.onHandlePointerDown(event, originalIndex);
|
|
5674
6797
|
return;
|
|
5675
6798
|
}
|
|
@@ -5737,6 +6860,121 @@ class AgridComponent {
|
|
|
5737
6860
|
}
|
|
5738
6861
|
/** @internal */
|
|
5739
6862
|
closeCellContextMenu() { this.rowController.closeCellContextMenu(); }
|
|
6863
|
+
/** @internal Closes any row, cell, menu-bar, group-action, or column menu owned by this grid. */
|
|
6864
|
+
closeOpenMenus() {
|
|
6865
|
+
const hadOpenMenu = this.contextMenu() !== null
|
|
6866
|
+
|| this.cellContextMenuState() !== null
|
|
6867
|
+
|| this.openMenuBarItemId() !== null
|
|
6868
|
+
|| this.groupActionsMenu() !== null
|
|
6869
|
+
|| this.filterMenu() !== null;
|
|
6870
|
+
if (!hadOpenMenu)
|
|
6871
|
+
return false;
|
|
6872
|
+
this.rowController.closeContextMenu();
|
|
6873
|
+
this.rowController.closeCellContextMenu();
|
|
6874
|
+
this.closeMenuBarMenu();
|
|
6875
|
+
this.groupController.closeActionsMenu();
|
|
6876
|
+
this.columnMenuController.close();
|
|
6877
|
+
return true;
|
|
6878
|
+
}
|
|
6879
|
+
/** @internal Resolves a menu-bar state callback against the current grid state. */
|
|
6880
|
+
resolveMenuBarState(state, fallback) {
|
|
6881
|
+
if (typeof state === 'function')
|
|
6882
|
+
return state(this.menuBarContext());
|
|
6883
|
+
return state ?? fallback;
|
|
6884
|
+
}
|
|
6885
|
+
/** @internal Whether a menu-bar button or dropdown item should be rendered. */
|
|
6886
|
+
isMenuBarItemVisible(item) {
|
|
6887
|
+
return this.resolveMenuBarState(item.visible, true);
|
|
6888
|
+
}
|
|
6889
|
+
/** @internal Whether a menu-bar button or dropdown item is active. */
|
|
6890
|
+
isMenuBarItemActive(item) {
|
|
6891
|
+
return this.resolveMenuBarState(item.active, false);
|
|
6892
|
+
}
|
|
6893
|
+
/** @internal Whether a menu-bar button or dropdown item is disabled. */
|
|
6894
|
+
isMenuBarItemDisabled(item) {
|
|
6895
|
+
return this.resolveMenuBarState(item.disabled, false);
|
|
6896
|
+
}
|
|
6897
|
+
/** @internal Visible dropdown entries for a menu-bar button. */
|
|
6898
|
+
visibleMenuBarChildren(item) {
|
|
6899
|
+
return (item.items ?? []).filter(child => this.isMenuBarItemVisible(child));
|
|
6900
|
+
}
|
|
6901
|
+
/** @internal Emits one menu-bar action and closes its dropdown. */
|
|
6902
|
+
runMenuBarAction(event, item) {
|
|
6903
|
+
event.stopPropagation();
|
|
6904
|
+
if (!this.isMenuBarItemVisible(item) || this.isMenuBarItemDisabled(item))
|
|
6905
|
+
return;
|
|
6906
|
+
this.menuBarAction.emit(item.id);
|
|
6907
|
+
this.closeMenuBarMenu();
|
|
6908
|
+
}
|
|
6909
|
+
/** @internal Opens or closes a split button's additional command menu. */
|
|
6910
|
+
toggleMenuBarMenu(event, item) {
|
|
6911
|
+
event.preventDefault();
|
|
6912
|
+
event.stopPropagation();
|
|
6913
|
+
if (this.visibleMenuBarChildren(item).length === 0)
|
|
6914
|
+
return;
|
|
6915
|
+
this.rowController.closeContextMenu();
|
|
6916
|
+
this.rowController.closeCellContextMenu();
|
|
6917
|
+
this.groupController.closeActionsMenu();
|
|
6918
|
+
this.columnMenuController.close();
|
|
6919
|
+
this.openMenuBarItemId.update(id => id === item.id ? null : item.id);
|
|
6920
|
+
}
|
|
6921
|
+
/** @internal Opens a dropdown from the keyboard and focuses its first/last enabled item. */
|
|
6922
|
+
onMenuBarTriggerKeydown(event, item) {
|
|
6923
|
+
if (event.key !== 'ArrowDown' && event.key !== 'ArrowUp')
|
|
6924
|
+
return;
|
|
6925
|
+
event.preventDefault();
|
|
6926
|
+
event.stopPropagation();
|
|
6927
|
+
if (this.visibleMenuBarChildren(item).length === 0)
|
|
6928
|
+
return;
|
|
6929
|
+
this.openMenuBarItemId.set(item.id);
|
|
6930
|
+
const group = event.currentTarget.closest('.ag-menu-bar-group');
|
|
6931
|
+
setTimeout(() => {
|
|
6932
|
+
const enabled = group?.querySelectorAll('.ag-menu-bar-dropdown [role="menuitem"]:not(:disabled)');
|
|
6933
|
+
const target = event.key === 'ArrowUp' ? enabled?.[enabled.length - 1] : enabled?.[0];
|
|
6934
|
+
target?.focus();
|
|
6935
|
+
});
|
|
6936
|
+
}
|
|
6937
|
+
/** @internal Provides standard keyboard navigation within an open menu-bar dropdown. */
|
|
6938
|
+
onMenuBarMenuKeydown(event) {
|
|
6939
|
+
const menu = event.currentTarget;
|
|
6940
|
+
const items = Array.from(menu.querySelectorAll('[role="menuitem"]:not(:disabled)'));
|
|
6941
|
+
if (event.key === 'Escape') {
|
|
6942
|
+
event.preventDefault();
|
|
6943
|
+
event.stopPropagation();
|
|
6944
|
+
this.closeMenuBarMenu();
|
|
6945
|
+
menu.closest('.ag-menu-bar-group')
|
|
6946
|
+
?.querySelector('.ag-menu-bar-trigger')
|
|
6947
|
+
?.focus();
|
|
6948
|
+
return;
|
|
6949
|
+
}
|
|
6950
|
+
if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key) || items.length === 0)
|
|
6951
|
+
return;
|
|
6952
|
+
event.preventDefault();
|
|
6953
|
+
event.stopPropagation();
|
|
6954
|
+
const current = items.indexOf(event.target);
|
|
6955
|
+
const next = event.key === 'Home'
|
|
6956
|
+
? 0
|
|
6957
|
+
: event.key === 'End'
|
|
6958
|
+
? items.length - 1
|
|
6959
|
+
: event.key === 'ArrowDown'
|
|
6960
|
+
? (current + 1 + items.length) % items.length
|
|
6961
|
+
: (current - 1 + items.length) % items.length;
|
|
6962
|
+
items[next].focus();
|
|
6963
|
+
}
|
|
6964
|
+
/** @internal Closes the currently open menu-bar dropdown. */
|
|
6965
|
+
closeMenuBarMenu() {
|
|
6966
|
+
this.openMenuBarItemId.set(null);
|
|
6967
|
+
}
|
|
6968
|
+
/** @internal Synchronizes dropdown state and closes competing grid menus when one opens. */
|
|
6969
|
+
onMenuBarOpenItemChange(id) {
|
|
6970
|
+
if (id !== null) {
|
|
6971
|
+
this.rowController.closeContextMenu();
|
|
6972
|
+
this.rowController.closeCellContextMenu();
|
|
6973
|
+
this.groupController.closeActionsMenu();
|
|
6974
|
+
this.columnMenuController.close();
|
|
6975
|
+
}
|
|
6976
|
+
this.openMenuBarItemId.set(id);
|
|
6977
|
+
}
|
|
5740
6978
|
/** @internal Runs a typed provider context-menu action against erased controller state. */
|
|
5741
6979
|
runCellMenuItem(item, menu) {
|
|
5742
6980
|
item.action({
|
|
@@ -5950,11 +7188,11 @@ class AgridComponent {
|
|
|
5950
7188
|
return index;
|
|
5951
7189
|
})));
|
|
5952
7190
|
}
|
|
5953
|
-
isCellEditable(col) {
|
|
5954
|
-
return this.editController.isCellEditable(col);
|
|
7191
|
+
isCellEditable(col, originalIndex) {
|
|
7192
|
+
return this.editController.isCellEditable(col, originalIndex);
|
|
5955
7193
|
}
|
|
5956
|
-
enterEdit(originalIndex, ci, seedChar) {
|
|
5957
|
-
this.editController.start(originalIndex, ci, seedChar);
|
|
7194
|
+
enterEdit(originalIndex, ci, seedChar, selectText = true) {
|
|
7195
|
+
this.editController.start(originalIndex, ci, seedChar, selectText);
|
|
5958
7196
|
}
|
|
5959
7197
|
cancelCurrent() {
|
|
5960
7198
|
this.editController.cancel();
|
|
@@ -5989,14 +7227,17 @@ class AgridComponent {
|
|
|
5989
7227
|
getColumnWidthToken(col) {
|
|
5990
7228
|
return this.columnSizing.getWidthToken(col);
|
|
5991
7229
|
}
|
|
5992
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
5993
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: AgridComponent, isStandalone: true, selector: "agrid", inputs: { provider: { classPropertyName: "provider", publicName: "provider", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cellEdit: "cellEdit", recordEdit: "recordEdit", rowRemoved: "rowRemoved", prepareAddRecord: "prepareAddRecord", rowReorder: "rowReorder", rowSelect: "rowSelect", rowDoubleClicked: "rowDoubleClicked", rowClick: "rowClick", rowChanged: "rowChanged", pageChange: "pageChange", filterChange: "filterChange", sortChange: "sortChange", quickFilterChange: "quickFilterChange", validationFailed: "validationFailed" }, host: { properties: { "class.ag-zebra": "zebraStripes()", "style.min-height": "minHeight()", "style.max-height": "maxHeight()" } }, viewQueries: [{ propertyName: "viewport", first: true, predicate: ["scrollViewport"], descendants: true, isSignal: true }, { propertyName: "pinnedViewport", first: true, predicate: ["pinnedViewport"], descendants: true, isSignal: true }, { propertyName: "rightPinnedViewport", first: true, predicate: ["rightPinnedViewport"], descendants: true, isSignal: true }, { propertyName: "wrapperEl", first: true, predicate: ["wrapper"], descendants: true, isSignal: true }, { propertyName: "horizontalScrollerEl", first: true, predicate: ["horizontalScroller"], descendants: true, isSignal: true }], ngImport: i0, template: "<div #wrapper class=\"ag-wrapper\" tabindex=\"0\" role=\"grid\" [attr.aria-label]=\"localeText().grid\"\n [attr.aria-rowcount]=\"ariaRowCount()\" [attr.aria-colcount]=\"ariaColCount()\"\n [attr.aria-multiselectable]=\"rowSelection() === 'multi' ? 'true' : null\"\n [attr.aria-readonly]=\"readonlyGrid() ? 'true' : null\"\n [attr.aria-busy]=\"loading() ? 'true' : null\" (copy)=\"onCopy($event)\" (paste)=\"onPaste($event)\"\n (focusin)=\"onGridFocusIn($event)\"\n (click)=\"closeContextMenu(); closeCellContextMenu(); closeFilterMenu(); closeGroupActionsMenu()\">\n @if (enableQuickFilter()) {\n <div class=\"ag-toolbar\">\n <input\n class=\"ag-quick-filter\"\n type=\"search\"\n [value]=\"quickFilterValue()\"\n (input)=\"onQuickFilterInput($event)\"\n [placeholder]=\"localeText().quickFilterPlaceholder\"\n [attr.aria-label]=\"localeText().quickFilterPlaceholder\"\n />\n </div>\n }\n <div class=\"ag-main-area\">\n <div class=\"ag-grid-split\" [class.ag-has-right-pane]=\"hasRightPinnedPane()\">\n @if (hasPinnedPane()) {\n <div class=\"ag-pinned-pane\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups ag-header-groups--pinned\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (showControlColumn()) {\n <div class=\"ag-header-group-cell ag-header-group-cell--empty\"></div>\n }\n @for (run of pinnedHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\"\n [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header ag-header--pinned\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (showControlColumn()) {\n <div class=\"ag-header-cell ag-control-header\" role=\"columnheader\" aria-colindex=\"1\">\n <div class=\"ag-header-cell-top\"></div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\"></div>\n }\n </div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-header-cell ag-header-cell--pinned\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-header-cell--pinned-last]=\"isLastPinnedColumn(col.field)\" [attr.data-col-field]=\"col.field\"\n (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\"\n (pointerdown)=\"$event.stopPropagation()\" (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <cdk-virtual-scroll-viewport #pinnedViewport class=\"ag-body ag-pinned-body\" [itemSize]=\"rowHeight()\"\n [style.width.px]=\"pinnedPaneWidth()\">\n <div *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\" role=\"row\"\n [attr.aria-rowindex]=\"di + headerRowCount() + 1\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-add-row--selected]=\"item === null && isAddRowSelected()\" [class.ag-ghost-row]=\"item === 'ghost'\"\n [class.ag-group-header-row]=\"isGroupHeaderItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isPinnedPaneRowSelected(item)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [attr.data-original-index]=\"getItemOriginalIndex(item)\" [style.height.px]=\"rowHeight()\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? pinnedGridTemplateColumns() : null\"\n (pointerdown)=\"isDataRowItem(item) && onRowPointerDown($event, item.originalIndex)\"\n (click)=\"isDataRowItem(item) && onRowClick($event, item)\">\n @if (isDataRowItem(item)) {\n @if (showControlColumn()) {\n <div class=\"ag-control-cell\" role=\"rowheader\" aria-colindex=\"1\"\n [class.ag-control-cell--reorder]=\"allowRowReorder()\"\n (contextmenu)=\"onControlContextMenu($event, item.originalIndex)\" (click)=\"$event.stopPropagation()\"\n (pointerdown)=\"onControlPointerDown($event, item.originalIndex)\">\n @if (enableRowMarking()) {\n <input class=\"ag-row-marker\" type=\"checkbox\" [checked]=\"isRowMarked(item.originalIndex)\"\n [attr.aria-label]=\"localeText().markRow\"\n (pointerdown)=\"$event.stopPropagation()\" (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRowMarked(item.originalIndex)\" />\n }\n </div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.title]=\"getCellTitle(col, item.row[col.field])\"\n [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\"\n [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\" [editable]=\"isColEditable(col)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\"\n [treeCell]=\"isTreeCell(col)\" [treeLevel]=\"treeRowLevel(item)\"\n [treeExpandable]=\"treeRowExpandable(item)\" [treeExpanded]=\"treeRowExpanded(item)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class]=\"getCellClass(col, item.row[col.field], item.row)\" [class.ag-cell--pinned]=\"true\"\n [class.ag-cell--pinned-last]=\"isLastPinnedColumn(col.field)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\" (startEdit)=\"onStartEdit(item.originalIndex, ci)\"\n (draftChange)=\"onDraftChange($event)\" (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\" (treeToggle)=\"onTreeToggle(item)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-pinned-row-spacer\"></div>\n } @else if (item === 'ghost') {\n @if (showControlColumn()) {\n <div class=\"ag-control-cell ag-control-cell--reorder ag-ghost-handle\"></div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-ghost-cell ag-cell--pinned\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--pinned-last]=\"isLastPinnedColumn(col.field)\">{{\n getGhostCellDisplay(col) }}</div>\n }\n } @else {\n <div class=\"ag-pinned-group-spacer\"></div>\n }\n </div>\n </cdk-virtual-scroll-viewport>\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\"\n [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\">\n @if (showControlColumn()) {\n <div class=\"ag-footer-cell ag-footer-cell--control\" role=\"gridcell\" aria-colindex=\"1\"></div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [class.ag-footer-cell--pinned-last]=\"isLastPinnedColumn(col.field)\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n <div class=\"ag-scroll-pane\">\n <div #horizontalScroller class=\"ag-horizontal-scroll\" (scroll)=\"onHorizontalScroll()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (run of scrollableHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\"\n [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-header-cell\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.data-col-field]=\"col.field\" (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n @if (isGroupedByField(col.field)) {\n <span class=\"ag-sort-badge\">\u229F</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\"\n (pointerdown)=\"$event.stopPropagation()\" (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <cdk-virtual-scroll-viewport #scrollViewport class=\"ag-body\" [itemSize]=\"rowHeight()\"\n [style.min-width.px]=\"scrollableTotalWidth()\" (scroll)=\"onBodyScroll()\">\n <div *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\" role=\"row\"\n [attr.aria-rowindex]=\"di + headerRowCount() + 1\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-add-row--selected]=\"item === null && isAddRowSelected()\" [class.ag-ghost-row]=\"item === 'ghost'\"\n [class.ag-group-header-row]=\"isGroupHeaderItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [attr.data-original-index]=\"getItemOriginalIndex(item)\" [style.height.px]=\"rowHeight()\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? scrollableGridTemplateColumns() : null\"\n (pointerdown)=\"isDataRowItem(item) && onRowPointerDown($event, item.originalIndex)\"\n (click)=\"isDataRowItem(item) && onRowClick($event, item)\">\n @if (isDataRowItem(item)) {\n @for (col of scrollableColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.title]=\"getCellTitle(col, item.row[col.field])\"\n [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\"\n [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\" [editable]=\"isColEditable(col)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\"\n [treeCell]=\"isTreeCell(col)\" [treeLevel]=\"treeRowLevel(item)\"\n [treeExpandable]=\"treeRowExpandable(item)\" [treeExpanded]=\"treeRowExpanded(item)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class]=\"getCellClass(col, item.row[col.field], item.row)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\"\n (startEdit)=\"onStartEdit(item.originalIndex, ci)\" (draftChange)=\"onDraftChange($event)\" (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\"\n (treeToggle)=\"onTreeToggle(item)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-add-row-label\" (click)=\"onActivateAddRow()\">\n <span class=\"ag-add-row-icon\">+</span> {{ localeText().addRow }}\n </div>\n } @else if (item === 'ghost') {\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-ghost-cell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\">{{\n getGhostCellDisplay(col) }}</div>\n }\n } @else {\n <div class=\"ag-group-header-content\" [style.min-width.px]=\"scrollableTotalWidth()\">\n <div class=\"ag-group-header-main\"\n (click)=\"onGroupHeaderClick(item.groupLabel); $event.stopPropagation()\">\n <span class=\"ag-group-icon\" [class.ag-group-icon--expanded]=\"!item.collapsed\">\u25B6</span>\n <span class=\"ag-group-label\">{{ item.groupLabel }}</span>\n @if (groupActions().length > 0) {\n <button class=\"ag-group-actions-btn\" [title]=\"localeText().actions\"\n (click)=\"openGroupActionsMenu($event, item.groupLabel)\">\u22EE</button>\n }\n <span class=\"ag-group-count\">{{ item.count }}</span>\n @if (getGroupDescription(item.groupLabel); as desc) {\n <span class=\"ag-group-description\">{{ desc }}</span>\n }\n @if (item.aggregates; as aggs) {\n <span class=\"ag-group-aggregates\">\n @for (col of visibleColDefs(); track col.field) {\n @if (hasAggregate(col) && aggs[col.field] !== undefined) {\n <span class=\"ag-group-aggregate\">\n <span class=\"ag-group-aggregate-col\">{{ col.header }}</span>\n <span class=\"ag-group-aggregate-op\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, aggs[col.field]) }}\n </span>\n }\n }\n </span>\n }\n </div>\n </div>\n }\n @if (isDataRowItem(item) && isRowPendingDelete(item.originalIndex)) {\n <div class=\"ag-delete-confirmation\" role=\"alertdialog\"\n [attr.aria-label]=\"localeText().confirmDeleteRow\"\n [style.left.px]=\"deleteConfirmationLeft()\"\n [style.width.px]=\"deleteConfirmationWidth() || null\"\n (pointerdown)=\"$event.stopPropagation()\" (click)=\"$event.stopPropagation()\">\n <span class=\"ag-delete-confirmation-text\">{{ localeText().confirmDeleteRow }}</span>\n <button class=\"ag-delete-confirmation-btn ag-delete-confirmation-btn--yes\"\n type=\"button\" (click)=\"confirmPendingRowDelete()\">{{ localeText().confirmYes }}</button>\n <button class=\"ag-delete-confirmation-btn\" type=\"button\" data-delete-confirm-no\n (click)=\"cancelRowDelete()\">{{ localeText().confirmNo }}</button>\n </div>\n }\n </div>\n </cdk-virtual-scroll-viewport>\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\"\n [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if (hasRightPinnedPane()) {\n <div class=\"ag-pinned-pane ag-pinned-pane--right\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @for (run of rightHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\"\n [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-header-cell ag-header-cell--pinned\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-header-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\" [attr.data-col-field]=\"col.field\"\n (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n @if (isGroupedByField(col.field)) {\n <span class=\"ag-sort-badge\">'\u229F '</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\"\n (pointerdown)=\"$event.stopPropagation()\" (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <cdk-virtual-scroll-viewport #rightPinnedViewport class=\"ag-body ag-pinned-body ag-right-pinned-body\"\n [itemSize]=\"rowHeight()\" [style.width.px]=\"rightPinnedPaneWidth()\" (scroll)=\"onRightPinnedBodyScroll()\">\n <div *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\" role=\"row\"\n [attr.aria-rowindex]=\"di + headerRowCount() + 1\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-ghost-row]=\"item === 'ghost'\" [class.ag-group-header-row]=\"isGroupHeaderItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [style.height.px]=\"rowHeight()\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? rightGridTemplateColumns() : null\">\n @if (isDataRowItem(item)) {\n @for (col of rightPinnedColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.title]=\"getCellTitle(col, item.row[col.field])\"\n [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\"\n [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\" [editable]=\"isColEditable(col)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\"\n [treeCell]=\"isTreeCell(col)\" [treeLevel]=\"treeRowLevel(item)\"\n [treeExpandable]=\"treeRowExpandable(item)\" [treeExpanded]=\"treeRowExpanded(item)\"\n [class]=\"getCellClass(col, item.row[col.field], item.row)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--pinned]=\"true\" [class.ag-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\" (startEdit)=\"onStartEdit(item.originalIndex, ci)\"\n (draftChange)=\"onDraftChange($event)\" (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\" (treeToggle)=\"onTreeToggle(item)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-pinned-row-spacer\"></div>\n } @else if (item === 'ghost') {\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-ghost-cell ag-cell--pinned\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\">{{ getGhostCellDisplay(col) }}</div>\n }\n } @else {\n <div class=\"ag-pinned-group-spacer\"></div>\n }\n </div>\n </cdk-virtual-scroll-viewport>\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\"\n [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\">\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [class.ag-footer-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n\n @if (loading()) {\n <div class=\"ag-state-overlay\" role=\"status\" aria-live=\"polite\">{{ localeText().loading }}</div>\n } @else if (isEmpty()) {\n <div class=\"ag-state-overlay\" role=\"status\" aria-live=\"polite\">{{ emptyTextLabel() }}</div>\n }\n\n @if (showSidebar()) {\n <agrid-sidebar\n [open]=\"sidebarOpen()\"\n [activeTab]=\"sidebarTab()\"\n [columns]=\"colDefs()\"\n [headerGroups]=\"headerGroups()\"\n [row]=\"sidebarRow()\"\n [hiddenColumns]=\"sidebarHiddenColumns()\"\n [locale]=\"locale()\"\n [localeText]=\"localeText()\"\n [readonlyGrid]=\"readonlyGrid()\"\n [useSidebarEditor]=\"useSidebarEditor()\"\n [errors]=\"sidebarValidationErrors()\"\n (close)=\"toggleSidebar()\"\n (tabChange)=\"onSidebarStripClick($event)\"\n (toggleColumn)=\"onSidebarToggleColumn($event)\"\n (toggleColumnGroup)=\"onSidebarToggleColumnGroup($event.fields, $event.visible)\"\n (detailEdit)=\"onSidebarDetailEdit($event)\"\n (save)=\"onSidebarDetailSave($event)\"\n />\n }\n </div><!-- /.ag-main-area -->\n\n @if (showPagination()) {\n <nav class=\"ag-pagination\" [attr.aria-label]=\"localeText().pagination\">\n <button class=\"ag-page-btn\" [attr.aria-label]=\"localeText().firstPage\"\n [disabled]=\"control()!.currentPage() <= 1\" (click)=\"goToFirstPage()\">\u00AB</button>\n <button class=\"ag-page-btn\" [attr.aria-label]=\"localeText().previous\"\n [disabled]=\"control()!.currentPage() <= 1\" (click)=\"goToPrevPage()\">\u2039</button>\n <span class=\"ag-page-info\" aria-live=\"polite\">{{ control()!.currentPage() }} / {{ totalPages() }}</span>\n <button class=\"ag-page-btn\" [disabled]=\"control()!.currentPage() >= totalPages()\"\n [attr.aria-label]=\"localeText().next\"\n (click)=\"goToNextPage()\">\u203A</button>\n <button class=\"ag-page-btn\" [disabled]=\"control()!.currentPage() >= totalPages()\"\n [attr.aria-label]=\"localeText().lastPage\"\n (click)=\"goToLastPage()\">\u00BB</button>\n <span class=\"ag-page-count\">{{ localeText().rows(filteredRowCount()) }}</span>\n </nav>\n }\n\n @if (findOpen()) {\n <agrid-find-panel [query]=\"findQuery()\" [matchCount]=\"findMatches().length\" [activeIndex]=\"findActiveIndex()\"\n [localeText]=\"localeText()\" (queryChange)=\"onFindInput($event)\" (previous)=\"goToFindMatch(-1)\"\n (next)=\"goToFindMatch(1)\" (close)=\"closeFind()\" />\n }\n\n <!-- Row context menu -->\n @if (contextMenu(); as menu) {\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n <button class=\"ag-context-item ag-context-item--danger\" role=\"menuitem\" (click)=\"deleteRow(menu.rowIndex)\">\n {{ localeText().deleteRow }}\n </button>\n </div>\n }\n\n <!-- Cell context menu -->\n @if (cellContextMenuState(); as menu) {\n @let col = getColDef(menu.field)!;\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"copyCellToClipboard(menu.rowIndex, col)\">{{ localeText().copyCellValue\n }}</button>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"copyRowToClipboard(menu.rowIndex)\">{{ localeText().copyRow }}</button>\n @if (allowAddRows() && !readonlyGrid()) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"insertRowAt(menu.rowIndex)\">{{ localeText().insertRowAbove }}</button>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"insertRowAt(menu.rowIndex + 1)\">{{ localeText().insertRowBelow }}</button>\n }\n @if (!readonlyGrid()) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n <button class=\"ag-context-item ag-context-item--danger\" role=\"menuitem\"\n (click)=\"deleteRow(menu.rowIndex)\">{{ localeText().deleteRow }}</button>\n }\n @if (cellMenuItems().length) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n @for (item of cellMenuItems(); track $index) {\n @if (item === null) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n } @else {\n <button class=\"ag-context-item\" role=\"menuitem\" [class.ag-context-item--danger]=\"item.danger\" [disabled]=\"item.disabled\"\n (click)=\"runCellMenuItem(item, menu)\">{{\n item.label }}</button>\n }\n }\n }\n </div>\n }\n\n <!-- Group actions menu -->\n @if (groupActionsMenu(); as menu) {\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n @for (action of groupActions(); track action.label) {\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onGroupAction(action, menu.label)\">\n {{ action.label }}\n </button>\n }\n </div>\n }\n\n <!-- Filter dropdown -->\n @if (filterMenu(); as menu) {\n <agrid-column-menu [x]=\"menu.x\" [y]=\"menu.y\" [header]=\"getColDef(menu.field)?.header ?? menu.field\"\n [sortDir]=\"getSort(menu.field)\" [sortable]=\"sortOption() !== 'none'\"\n [showColumnActions]=\"!!control()\" [pinned]=\"getColumnPinState(menu.field)\"\n [groupable]=\"!!getColDef(menu.field)?.groupable\" [grouped]=\"isGroupedByField(menu.field)\"\n [filterable]=\"!!getColDef(menu.field)?.filterable\" [showValueFilter]=\"!serverSideFiltering()\"\n [filterType]=\"getMenuFilterType(menu.field)\"\n [operator]=\"getMenuOperator(menu.field)\" [operand]=\"getMenuOperand(menu.field)\"\n [operand2]=\"getMenuOperand2(menu.field)\"\n (operatorChange)=\"onMenuOperatorChange(menu.field, $event)\"\n (operandChange)=\"onMenuOperandChange(menu.field, $event)\"\n (operand2Change)=\"onMenuOperand2Change(menu.field, $event)\"\n [search]=\"filterMenuSearch()\"\n [allSelected]=\"isMenuAllSelected(menu.field)\"\n [valueItems]=\"serverSideFiltering() || !getColDef(menu.field)?.filterable ? [] : columnMenuValueItems()\"\n [localeText]=\"localeText()\"\n [sortPriority]=\"getSortPriority(menu.field)\" [hasMultiSort]=\"hasMultiSort()\" (sort)=\"onMenuSort(menu.field, $event)\"\n (resetSort)=\"onMenuResetSort(menu.field, $event)\" (autosize)=\"onMenuAutosizeColumn(menu.field)\"\n (togglePin)=\"onMenuTogglePin(menu.field)\" (togglePinRight)=\"onMenuTogglePinRight(menu.field)\"\n (hide)=\"onMenuHideColumn(menu.field)\" (toggleGroup)=\"onMenuToggleGroupBy(menu.field)\"\n (clearFilter)=\"onMenuClearFilter(menu.field)\" (clearAll)=\"onMenuClearAll()\"\n (searchChange)=\"onFilterMenuSearch($event)\" (toggleAll)=\"onMenuToggleAll(menu.field)\"\n (toggleValue)=\"onMenuToggleValue(menu.field, $event)\" [aggregate]=\"getEffectiveAggregate(getColDef(menu.field)!)\"\n (setAggregate)=\"onMenuSetAggregate(menu.field, $event)\" />\n }\n\n @if (columnDragPreview(); as preview) {\n <div class=\"ag-column-drag-preview\"\n [style.left.px]=\"preview.x\" [style.top.px]=\"preview.y\"\n [style.width.px]=\"preview.width\" [style.height.px]=\"preview.height\">\n <span>{{ preview.label }}</span>\n </div>\n }\n</div>\n", styles: ["@layer agrid-defaults{:host{--agrid-color-text: #24292f;--agrid-color-text-muted: #57606a;--agrid-color-accent: #1a73e8;--agrid-color-accent-subtle: #e8f0fe;--agrid-color-accent-fg: #1558b0;--agrid-color-accent-border: #c8d8f8;--agrid-color-danger: #d1242f;--agrid-color-danger-subtle: #fff1f0;--agrid-color-border: #d0d7de;--agrid-color-bg: #ffffff;--agrid-color-bg-subtle: #fafbfc;--agrid-color-bg-muted: #f6f8fa;--agrid-color-shadow: rgba(140, 149, 159, .2);--agrid-color-bg-stripe: #f0f2f5;--agrid-color-cell-changed: #f59e0b;--agrid-color-row-marked: #fff8c5}}:host{display:flex;flex-direction:column;min-height:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:13px;color:var(--agrid-color-text)}.ag-wrapper{position:relative;display:flex;flex-direction:column;flex:1;min-height:0;border:1px solid var(--agrid-color-border);overflow:hidden;outline:none;border-radius:4px}.ag-state-overlay{position:absolute;inset:34px 0 0;display:flex;align-items:center;justify-content:center;color:var(--agrid-color-text-muted);background:color-mix(in srgb,var(--agrid-color-bg) 78%,transparent);pointer-events:none;z-index:3}.ag-header{display:grid;flex-shrink:0;background:var(--agrid-color-bg-muted);border-bottom:2px solid var(--agrid-color-border)}.ag-header-groups{display:grid;flex-shrink:0;height:28px;background:color-mix(in srgb,var(--agrid-color-bg-muted) 72%,var(--agrid-color-bg));border-bottom:1px solid var(--agrid-color-border)}.ag-header-group-cell{display:flex;align-items:center;justify-content:center;min-width:0;padding:0 8px;overflow:hidden;border-right:1px solid var(--agrid-color-border);box-sizing:border-box;color:var(--agrid-color-text);font-size:12px;font-weight:600;text-overflow:ellipsis;white-space:nowrap;cursor:grab;-webkit-user-select:none;user-select:none;transition:opacity .1s ease}.ag-header-group-cell:last-child{border-right:none}.ag-header-group-cell--empty,.ag-header-group-cell--locked{cursor:default}.ag-header-group-cell--dragging{opacity:.12}.ag-header-cell{position:relative;display:flex;align-items:center;font-weight:600;border-right:1px solid var(--agrid-color-border);overflow:hidden;white-space:nowrap;-webkit-user-select:none;user-select:none;box-sizing:border-box}.ag-header-cell:last-child{border-right:none}.ag-header-cell-top{display:flex;align-items:center;flex:1;min-width:0;padding:0 6px;height:32px;overflow:hidden;white-space:nowrap}.ag-header-cell-label{overflow:hidden;text-overflow:ellipsis;flex:1}.ag-header--with-filters .ag-header-cell{flex-direction:column;align-items:stretch;height:auto;white-space:normal}.ag-header--with-filters .ag-header-cell-top{flex:0 0 32px}.ag-header-cell-filter{height:28px;display:flex;align-items:center;padding:0 2px;border-top:1px solid var(--agrid-color-border);background:var(--agrid-color-bg)}.ag-header-cell{cursor:grab;transition:transform .16s cubic-bezier(.2,0,0,1),opacity .1s ease;will-change:transform}.ag-row>agrid-cell,.ag-ghost-cell,.ag-footer-cell{transition:transform .16s cubic-bezier(.2,0,0,1),opacity .1s ease;will-change:transform}.ag-header-cell--dragging{opacity:.12;cursor:grabbing}.ag-column-reorder-item--dragging{opacity:.12}.ag-header-cell--drop-before,.ag-header-cell--drop-after{z-index:1}.ag-column-drag-preview{position:fixed;z-index:1000;display:flex;align-items:flex-start;padding:8px 10px;border:1px solid var(--agrid-color-accent);border-radius:5px;color:var(--agrid-color-text);background:color-mix(in srgb,var(--agrid-color-bg) 94%,var(--agrid-color-accent));box-shadow:0 10px 28px var(--agrid-color-shadow);font-weight:600;line-height:16px;box-sizing:border-box;pointer-events:none;opacity:.96}.ag-sort-badge{font-size:11px;color:var(--agrid-color-accent);flex-shrink:0;line-height:1}.ag-sort-priority{font-size:9px;vertical-align:super;opacity:.75}.ag-resize-handle{position:absolute;top:0;right:0;width:5px;height:100%;cursor:col-resize;z-index:1}.ag-resize-handle:hover{background:var(--agrid-color-accent);opacity:.5}.ag-filter-input{flex:1;min-width:0;height:20px;border:1px solid var(--agrid-color-border);border-radius:3px;outline:none;font:inherit;font-size:12px;padding:0 4px;background:var(--agrid-color-bg)}.ag-filter-input:focus{border-color:var(--agrid-color-accent)}.ag-header-cell--filtered .ag-header-cell-label:after{content:\" \\25be\";font-size:9px;color:var(--agrid-color-accent)}.ag-main-area{display:flex;flex:1;min-height:0;overflow:hidden}.ag-grid-split{display:flex;flex:1;min-width:0;min-height:0;overflow:hidden}.ag-pinned-pane{flex-shrink:0;min-height:0;display:flex;flex-direction:column;overflow:hidden;border-right:1px solid var(--agrid-color-border);background:var(--agrid-color-bg)}.ag-scroll-pane{flex:1;min-width:0;min-height:0;display:flex}.ag-horizontal-scroll{flex:1;min-width:0;min-height:0;overflow-x:auto;overflow-y:hidden;display:flex;flex-direction:column}.ag-body{flex:1;overflow-x:clip;overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-horizontal-scroll{scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-body::-webkit-scrollbar,.ag-horizontal-scroll::-webkit-scrollbar{width:8px;height:8px}.ag-body::-webkit-scrollbar-track,.ag-horizontal-scroll::-webkit-scrollbar-track{background:transparent}.ag-body::-webkit-scrollbar-thumb,.ag-horizontal-scroll::-webkit-scrollbar-thumb{background:#0000002e;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-body::-webkit-scrollbar-thumb:hover,.ag-horizontal-scroll::-webkit-scrollbar-thumb:hover{background:#00000052;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-pinned-body{overflow:hidden}.ag-right-pinned-body{overflow-y:auto}.ag-has-right-pane .ag-body:not(.ag-pinned-body){scrollbar-width:none}.ag-has-right-pane .ag-body:not(.ag-pinned-body)::-webkit-scrollbar{display:none}.ag-row{display:grid;position:relative}.ag-row--pending-delete>:not(.ag-delete-confirmation){opacity:.2;pointer-events:none;transition:opacity .14s ease}.ag-delete-confirmation{position:absolute;top:0;bottom:0;z-index:7;display:flex;align-items:center;justify-content:center;gap:8px;min-width:min(100%,320px);padding:2px 12px;box-sizing:border-box;color:var(--agrid-color-text);background:color-mix(in srgb,var(--agrid-color-danger-subtle) 88%,transparent);border-block:1px solid color-mix(in srgb,var(--agrid-color-danger) 35%,transparent)}.ag-delete-confirmation-text{font-weight:600;white-space:nowrap}.ag-delete-confirmation-btn{min-width:42px;padding:3px 9px;border:1px solid var(--agrid-color-border);border-radius:4px;color:var(--agrid-color-text);background:var(--agrid-color-bg);font:inherit;cursor:pointer}.ag-delete-confirmation-btn:hover,.ag-delete-confirmation-btn:focus-visible{border-color:var(--agrid-color-accent);outline:none}.ag-delete-confirmation-btn--yes{color:#fff;border-color:var(--agrid-color-danger);background:var(--agrid-color-danger)}.ag-delete-confirmation-btn--yes:hover,.ag-delete-confirmation-btn--yes:focus-visible{border-color:color-mix(in srgb,var(--agrid-color-danger) 75%,#000);background:color-mix(in srgb,var(--agrid-color-danger) 85%,#000)}:host(.ag-zebra) .ag-row--odd agrid-cell:not(.editing){background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd .ag-cell--pinned{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd .ag-control-cell{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd:hover agrid-cell:not(.editing){background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd:hover .ag-cell--pinned,:host(.ag-zebra) .ag-row--odd:hover .ag-control-cell{background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd.ag-row--selected agrid-cell:not(.editing){background:var(--agrid-color-accent-subtle)}:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-cell--pinned,:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-control-cell{background:var(--agrid-color-accent-subtle)}:host(.ag-zebra) .ag-row--odd .ag-cell--pinned-first{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd:hover .ag-cell--pinned-first{background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-cell--pinned-first{background:var(--agrid-color-accent-subtle)}.ag-row:hover agrid-cell:not(.editing){background:var(--agrid-color-bg-muted)}.ag-row--selected agrid-cell:not(.editing){background:var(--agrid-color-accent-subtle)}.ag-row--marked agrid-cell:not(.editing),.ag-row--marked .ag-control-cell{background:var(--agrid-color-row-marked)}.ag-row--marked{box-shadow:inset 3px 0 #bf8700}.ag-row--selected.ag-row--marked{box-shadow:inset 3px 0 0 var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--range-selected:not(.editing){background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1px var(--agrid-color-accent-border)}.ag-row agrid-cell.ag-cell--find-match:not(.editing){background:#fff7cc}.ag-row agrid-cell.ag-cell--find-active:not(.editing){background:#ffe58a;box-shadow:inset 0 0 0 2px #b7791f}.ag-row agrid-cell.ag-cell--range-selected.selected:not(.editing){box-shadow:inset 0 0 0 2px var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--fill-preview:not(.editing){background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1px var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--fill-handle:not(.editing){position:relative}.ag-row agrid-cell.ag-cell--fill-handle:not(.editing):after{content:\"\";position:absolute;right:1px;bottom:1px;width:6px;height:6px;background:var(--agrid-color-accent);border:1px solid var(--agrid-color-bg);box-sizing:border-box;cursor:crosshair;z-index:4}.ag-row--selected .ag-control-cell{background:var(--agrid-color-accent-subtle)}.ag-row--selected{box-shadow:inset 3px 0 0 var(--agrid-color-accent)}.ag-control-header{z-index:3;border-right:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted)}.ag-control-header .ag-header-cell-filter{background:var(--agrid-color-bg-subtle)}.ag-control-cell{z-index:2;border-right:1px solid var(--agrid-color-border);border-bottom:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-subtle);cursor:context-menu;display:flex;align-items:center;justify-content:center;gap:6px;box-sizing:border-box;flex-direction:row-reverse}.ag-control-cell:after{content:\"\\22ee\";font-size:11px;color:var(--agrid-color-border);line-height:1}.ag-control-cell:hover:after{color:var(--agrid-color-text-muted)}.ag-control-cell--reorder{cursor:grab}.ag-control-cell--reorder:after{content:\"\\283f\";font-size:13px;color:var(--agrid-color-text)}.ag-control-cell--reorder:hover:after{color:var(--agrid-color-text-muted)}.ag-control-cell--reorder:active{cursor:grabbing}.ag-row-marker{position:relative;z-index:1;width:14px;height:14px;margin:0;accent-color:var(--agrid-color-accent);cursor:pointer}.ag-ghost-row{background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1.5px var(--agrid-color-accent);pointer-events:none;animation:ag-ghost-in .1s ease}@keyframes ag-ghost-in{0%{opacity:0}to{opacity:1}}.ag-ghost-handle:after{color:var(--agrid-color-accent)!important}.ag-ghost-cell{display:flex;align-items:center;padding:0 6px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;border-right:1px solid var(--agrid-color-accent-border);color:var(--agrid-color-accent-fg);font-size:13px;box-sizing:border-box;-webkit-user-select:none;user-select:none}.ag-ghost-cell:last-child{border-right:none}.ag-pinned-row-spacer,.ag-pinned-group-spacer{height:100%;border-bottom:1px solid var(--agrid-color-border);box-sizing:border-box}.ag-pinned-group-spacer{background:var(--agrid-color-bg-muted)}.ag-context-menu{position:fixed;z-index:1000;background:var(--agrid-color-bg-subtle);border:1px solid var(--agrid-color-border);border-radius:6px;box-shadow:0 8px 24px var(--agrid-color-shadow);min-width:160px;padding:4px 0;font-size:13px}.ag-context-item{display:block;width:100%;padding:6px 16px;text-align:left;background:none;border:none;cursor:pointer;color:var(--agrid-color-text);font:inherit}.ag-context-item:hover{background:var(--agrid-color-bg-muted)}.ag-context-separator{height:1px;background:var(--agrid-color-border);margin:3px 0}.ag-context-item--danger{color:var(--agrid-color-danger)}.ag-context-item--danger:hover{background:var(--agrid-color-danger-subtle)}.ag-group-header-row{display:flex;align-items:stretch;background:var(--agrid-color-bg-muted);border-bottom:1px solid var(--agrid-color-border);overflow:hidden}.ag-group-header-content{display:flex;align-items:stretch;height:100%;width:100%;-webkit-user-select:none;user-select:none}.ag-group-header-main{display:flex;align-items:center;gap:6px;padding:0 10px;flex:1;min-width:0;cursor:pointer}.ag-group-header-main:hover{background:var(--agrid-color-bg-subtle)}.ag-group-description{font-size:11px;color:var(--agrid-color-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ag-group-actions-btn{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border:none;background:none;border-radius:3px;cursor:pointer;color:var(--agrid-color-text-muted);font-size:14px;padding:0}.ag-group-actions-btn:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text)}.ag-group-icon{font-size:9px;color:var(--agrid-color-text-muted);line-height:1;display:inline-block;transition:transform .15s ease}.ag-group-icon--expanded{transform:rotate(90deg)}.ag-group-label{font-weight:600;font-size:12px;color:var(--agrid-color-text)}.ag-group-count{font-size:11px;color:var(--agrid-color-text-muted);background:var(--agrid-color-bg);border:1px solid var(--agrid-color-border);border-radius:10px;padding:0 7px;line-height:16px}.ag-group-aggregates{display:inline-flex;align-items:center;gap:10px;margin-left:4px;flex-wrap:wrap}.ag-group-aggregate{font-size:11px;color:var(--agrid-color-text);white-space:nowrap}.ag-group-aggregate-col{color:var(--agrid-color-text-muted);margin-right:4px}.ag-group-aggregate-op{color:var(--agrid-color-text-muted);margin-right:3px}.ag-footer{display:grid;flex-shrink:0;background:var(--agrid-color-bg-muted);border-top:2px solid var(--agrid-color-border)}.ag-footer-cell{display:flex;align-items:center;gap:4px;padding:0 6px;height:30px;border-right:1px solid var(--agrid-color-border);box-sizing:border-box;overflow:hidden;white-space:nowrap;font-weight:600;font-size:12px;color:var(--agrid-color-text)}.ag-footer-cell:last-child{border-right:none}.ag-footer-cell--control{background:var(--agrid-color-bg-subtle);border-right:1px solid var(--agrid-color-border)}.ag-footer-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-footer-label{font-size:10px;font-weight:400;color:var(--agrid-color-text-muted);flex-shrink:0}.ag-pagination{display:flex;align-items:center;gap:4px;padding:0 10px;height:34px;flex-shrink:0;border-top:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted);font-size:12px;color:var(--agrid-color-text-muted)}.ag-page-btn{display:flex;align-items:center;justify-content:center;width:24px;height:22px;border:1px solid var(--agrid-color-border);border-radius:3px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font-size:13px;cursor:pointer;padding:0;line-height:1}.ag-page-btn:hover:not(:disabled){background:var(--agrid-color-bg-muted);border-color:var(--agrid-color-text-muted)}.ag-page-btn:disabled{opacity:.35;cursor:default}.ag-page-info{padding:0 6px;font-weight:500;color:var(--agrid-color-text);min-width:48px;text-align:center}.ag-page-count{margin-left:auto;color:var(--agrid-color-text-muted)}.ag-add-row{border-bottom:1px dashed var(--agrid-color-border);color:var(--agrid-color-text-muted);cursor:pointer}.ag-add-row:hover .ag-add-row-label{background:var(--agrid-color-bg-muted)}.ag-add-row--selected .ag-add-row-label{outline:2px solid var(--agrid-color-accent);outline-offset:-2px}.ag-add-row-label{display:flex;align-items:center;gap:4px;height:100%;padding:0 6px;font-size:12px;-webkit-user-select:none;user-select:none}.ag-add-row-icon{font-size:16px;line-height:1;color:var(--agrid-color-text-muted)}.ag-pinned-pane--right{border-left:1px solid var(--agrid-color-border);border-right:none;box-shadow:-3px 0 6px -2px var(--agrid-color-shadow)}.ag-header-cell--pinned{background:var(--agrid-color-bg-muted)}.ag-header-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-header-cell--pinned-first{box-shadow:none}.ag-cell--pinned{background:var(--agrid-color-bg)}.ag-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-cell--pinned-first{box-shadow:none}.ag-row:hover .ag-cell--pinned{background:var(--agrid-color-bg-muted)}.ag-row--selected .ag-cell--pinned,.ag-row .ag-cell--pinned.ag-cell--range-selected,.ag-row .ag-cell--pinned.ag-cell--fill-preview{background:var(--agrid-color-accent-subtle)}.ag-row .ag-cell--pinned.ag-cell--find-match{background:#fff7cc}.ag-row .ag-cell--pinned.ag-cell--find-active{background:#ffe58a}.ag-header-menu-btn{flex-shrink:0;width:16px;height:16px;padding:0;margin-right:2px;background:none;border:1px solid transparent;border-radius:3px;cursor:pointer;font-size:10px;color:var(--agrid-color-text-muted);display:flex;align-items:center;justify-content:center;opacity:1;transition:opacity .12s;line-height:1}.ag-header-menu-btn--active{color:var(--agrid-color-accent);border-color:var(--agrid-color-accent-border);background:var(--agrid-color-accent-subtle)}.ag-header-menu-btn:hover{background:var(--agrid-color-bg);border-color:var(--agrid-color-border)}.ag-toolbar{display:flex;align-items:center;padding:6px 8px;border-bottom:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-subtle)}.ag-quick-filter{width:100%;max-width:260px;height:30px;padding:0 10px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font-size:13px;box-sizing:border-box}.ag-quick-filter:focus{outline:none;border-color:var(--agrid-color-accent-border)}\n"], dependencies: [{ kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i1.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i1.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "component", type: AgridCellComponent, selector: "agrid-cell", inputs: ["col", "rowIndex", "colIndex", "value", "row", "locale", "selected", "editing", "editable", "error", "treeCell", "treeLevel", "treeExpandable", "treeExpanded", "seedChar"], outputs: ["treeToggle", "activate", "startEdit", "booleanToggle", "draftChange"] }, { kind: "component", type: AgridColumnMenuComponent, selector: "agrid-column-menu", inputs: ["localeText", "x", "y", "header", "sortDir", "sortable", "showColumnActions", "pinned", "groupable", "grouped", "filterable", "showValueFilter", "filterType", "operator", "operand", "operand2", "search", "allSelected", "valueItems", "sortPriority", "hasMultiSort", "aggregate"], outputs: ["operatorChange", "operandChange", "operand2Change", "sort", "resetSort", "autosize", "togglePin", "togglePinRight", "hide", "toggleGroup", "clearFilter", "clearAll", "searchChange", "toggleAll", "toggleValue", "setAggregate"] }, { kind: "component", type: AgridFindPanelComponent, selector: "agrid-find-panel", inputs: ["localeText", "query", "matchCount", "activeIndex"], outputs: ["queryChange", "next", "previous", "close"] }, { kind: "component", type: AgridSidebarComponent, selector: "agrid-sidebar", inputs: ["open", "activeTab", "columns", "headerGroups", "row", "hiddenColumns", "locale", "localeText", "readonlyGrid", "useSidebarEditor", "errors"], outputs: ["close", "tabChange", "toggleColumn", "toggleColumnGroup", "detailEdit", "save"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7230
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7231
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AgridComponent, isStandalone: true, selector: "agrid", inputs: { provider: { classPropertyName: "provider", publicName: "provider", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cellEdit: "cellEdit", recordEdit: "recordEdit", rowRemoved: "rowRemoved", prepareAddRecord: "prepareAddRecord", rowReorder: "rowReorder", rowSelect: "rowSelect", rowDoubleClicked: "rowDoubleClicked", rowClick: "rowClick", treeNodeClick: "treeNodeClick", treeNodeDoubleClicked: "treeNodeDoubleClicked", rowChanged: "rowChanged", pageChange: "pageChange", filterChange: "filterChange", sortChange: "sortChange", quickFilterChange: "quickFilterChange", validationFailed: "validationFailed", cellInfo: "cellInfo", menuBarAction: "menuBarAction" }, host: { properties: { "class.ag-zebra": "zebraStripes()", "style.min-height": "minHeight()", "style.max-height": "maxHeight()" } }, viewQueries: [{ propertyName: "viewport", first: true, predicate: ["scrollViewport"], descendants: true, isSignal: true }, { propertyName: "pinnedViewport", first: true, predicate: ["pinnedViewport"], descendants: true, isSignal: true }, { propertyName: "rightPinnedViewport", first: true, predicate: ["rightPinnedViewport"], descendants: true, isSignal: true }, { propertyName: "wrapperEl", first: true, predicate: ["wrapper"], descendants: true, isSignal: true }, { propertyName: "horizontalScrollerEl", first: true, predicate: ["horizontalScroller"], descendants: true, isSignal: true }], ngImport: i0, template: "<div #wrapper class=\"ag-wrapper\" tabindex=\"0\" role=\"grid\" [attr.aria-label]=\"localeText().grid\"\n [attr.aria-rowcount]=\"ariaRowCount()\" [attr.aria-colcount]=\"ariaColCount()\"\n [attr.aria-multiselectable]=\"rowSelection() === 'multi' ? 'true' : null\"\n [attr.aria-readonly]=\"readonlyGrid() ? 'true' : null\" [attr.aria-busy]=\"loading() ? 'true' : null\"\n (copy)=\"onCopy($event)\" (paste)=\"onPaste($event)\" (focusin)=\"onGridFocusIn($event)\"\n (click)=\"closeContextMenu(); closeCellContextMenu(); closeFilterMenu(); closeGroupActionsMenu(); closeMenuBarMenu()\">\n @if (visibleMenuBarItems().length > 0 || enableQuickFilter()) {\n <div class=\"ag-toolbar\">\n @if (visibleMenuBarItems().length > 0) {\n <agrid-menu-bar [items]=\"visibleMenuBarItems()\" [context]=\"menuBarContext()\"\n [label]=\"localeText().actions\" [openItemId]=\"openMenuBarItemId()\"\n (openItemIdChange)=\"onMenuBarOpenItemChange($event)\" (action)=\"menuBarAction.emit($event)\" />\n }\n @if (enableQuickFilter()) {\n <input class=\"ag-quick-filter\" type=\"search\" [value]=\"quickFilterValue()\" (input)=\"onQuickFilterInput($event)\"\n [placeholder]=\"localeText().quickFilterPlaceholder\" [attr.aria-label]=\"localeText().quickFilterPlaceholder\" />\n }\n </div>\n }\n <div class=\"ag-main-area\">\n <div class=\"ag-grid-split\" [class.ag-has-right-pane]=\"hasRightPinnedPane()\">\n @if (hasPinnedPane()) {\n <div class=\"ag-pinned-pane\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups ag-header-groups--pinned\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (showControlColumn()) {\n <div class=\"ag-header-group-cell ag-header-group-cell--empty\"></div>\n }\n @for (run of pinnedHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\" [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header ag-header--pinned\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (showControlColumn()) {\n <div class=\"ag-header-cell ag-control-header\" role=\"columnheader\" aria-colindex=\"1\">\n <div class=\"ag-header-cell-top\"></div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\"></div>\n }\n </div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-header-cell ag-header-cell--pinned\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-header-cell--pinned-last]=\"isLastPinnedColumn(col.field)\" [attr.data-col-field]=\"col.field\"\n (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\" (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n @if (getMenuFilterType(col.field)) {\n <button type=\"button\" class=\"ag-filter-condition-btn\"\n [class.ag-filter-condition-btn--active]=\"!!getMenuOperator(col.field)\"\n [title]=\"localeText().filterConditionMenu\"\n [attr.aria-label]=\"localeText().filterConditionMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">{{ getConditionButtonLabel(col.field) }}</button>\n }\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\" (pointerdown)=\"$event.stopPropagation()\"\n (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <ng-template #leftRow let-item let-ariaIndex=\"ariaIndex\">\n <div role=\"row\" [attr.aria-rowindex]=\"ariaIndex\"\n [attr.data-control]=\"true\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item) && !isDetailRowItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-add-row--selected]=\"item === null && isAddRowSelected()\" [class.ag-ghost-row]=\"item === 'ghost'\"\n [class.ag-group-header-row]=\"isGroupHeaderItem(item)\" [class.ag-detail-row]=\"isDetailRowItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isPinnedPaneRowSelected(item)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [class]=\"isDataRowItem(item) ? getRowClass(item.row, item.originalIndex) : ''\"\n [attr.data-original-index]=\"getItemOriginalIndex(item)\" [style.height.px]=\"rowPx(item)\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? pinnedGridTemplateColumns() : null\"\n (pointerdown)=\"isDataRowItem(item) && onRowPointerDown($event, item.originalIndex)\"\n (click)=\"isDataRowItem(item) && onRowClick($event, item)\">\n @if (isDataRowItem(item)) {\n @if (showControlColumn()) {\n <div class=\"ag-control-cell\" role=\"rowheader\" aria-colindex=\"1\"\n [class.ag-control-cell--reorder]=\"allowRowReorder()\"\n (contextmenu)=\"onControlContextMenu($event, item.originalIndex)\" (click)=\"$event.stopPropagation()\"\n (pointerdown)=\"onControlPointerDown($event, item.originalIndex)\">\n @if (canToggleDetail(item)) {\n <button class=\"ag-detail-toggle\" type=\"button\"\n [class.ag-detail-toggle--expanded]=\"isDetailExpanded(item.originalIndex)\"\n [attr.aria-expanded]=\"isDetailExpanded(item.originalIndex)\"\n [attr.aria-label]=\"localeText().toggleDetail\" (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"onDetailToggle(item.originalIndex); $event.stopPropagation()\">\u25B6</button>\n }\n @if (enableRowMarking()) {\n <input class=\"ag-row-marker\" type=\"checkbox\" [checked]=\"isRowMarked(item.originalIndex)\"\n [attr.aria-label]=\"localeText().markRow\" (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation()\" (change)=\"toggleRowMarked(item.originalIndex)\" />\n }\n </div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [displayValueOverride]=\"treeCellDisplayOverride(item, col)\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\" [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\"\n [editable]=\"isColEditable(col, item.originalIndex)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [showInfoIcon]=\"showCellInfoIcon(col, item.row)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\" [selectTextOnEdit]=\"selectTextOnEdit()\"\n [treeCell]=\"isTreeCell(col)\"\n [treeLevel]=\"treeRowLevel(item)\" [treeExpandable]=\"treeRowExpandable(item)\"\n [treeExpanded]=\"treeRowExpanded(item)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class]=\"getCellClass(col, item.row[col.field], item.row)\" [class.ag-cell--pinned]=\"true\"\n [class.ag-cell--pinned-last]=\"isLastPinnedColumn(col.field)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\" (startEdit)=\"onStartEdit(item.originalIndex, ci)\"\n (draftChange)=\"onDraftChange($event)\" (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\"\n (infoClick)=\"onCellInfo(item.originalIndex, col, item.row)\"\n (treeToggle)=\"onTreeToggle(item)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-pinned-row-spacer\"></div>\n } @else if (item === 'ghost') {\n @if (showControlColumn()) {\n <div class=\"ag-control-cell ag-control-cell--reorder ag-ghost-handle\"></div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-ghost-cell ag-cell--pinned\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--pinned-last]=\"isLastPinnedColumn(col.field)\">{{\n getGhostCellDisplay(col) }}</div>\n }\n } @else {\n <div class=\"ag-pinned-row-spacer\"></div>\n }\n </div>\n </ng-template>\n\n @if (hasPinnedTopRows()) {\n <div class=\"ag-pinned-rows ag-pinned-rows--top ag-pinned-body\" [style.width.px]=\"pinnedPaneWidth()\">\n @for (item of pinnedTopItems(); track item.originalIndex) {\n <ng-container [ngTemplateOutlet]=\"leftRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: headerRowCount() + 1 + $index }\" />\n }\n </div>\n }\n\n <cdk-virtual-scroll-viewport #pinnedViewport class=\"ag-body ag-pinned-body\" [agridVariableRowSize]=\"itemSizes()\"\n [style.width.px]=\"pinnedPaneWidth()\">\n <ng-container *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\">\n <ng-container [ngTemplateOutlet]=\"leftRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: di + headerRowCount() + 1 + pinnedTopItems().length }\" />\n </ng-container>\n </cdk-virtual-scroll-viewport>\n\n @if (hasPinnedBottomRows()) {\n <div class=\"ag-pinned-rows ag-pinned-rows--bottom ag-pinned-body\" [style.width.px]=\"pinnedPaneWidth()\">\n @for (item of pinnedBottomItems(); track item.originalIndex) {\n <ng-container [ngTemplateOutlet]=\"leftRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: headerRowCount() + 1 + pinnedTopItems().length + displayItems().length + $index }\" />\n }\n </div>\n }\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\" [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\">\n @if (showControlColumn()) {\n <div class=\"ag-footer-cell ag-footer-cell--control\" role=\"gridcell\" aria-colindex=\"1\"></div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [class.ag-footer-cell--pinned-last]=\"isLastPinnedColumn(col.field)\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n <div class=\"ag-scroll-pane\">\n <div #horizontalScroller class=\"ag-horizontal-scroll\" (scroll)=\"onHorizontalScroll()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (run of scrollableHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\" [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-header-cell\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.data-col-field]=\"col.field\" (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n @if (isGroupedByField(col.field)) {\n <span class=\"ag-sort-badge\">\u229F</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\" (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n @if (getMenuFilterType(col.field)) {\n <button type=\"button\" class=\"ag-filter-condition-btn\"\n [class.ag-filter-condition-btn--active]=\"!!getMenuOperator(col.field)\"\n [title]=\"localeText().filterConditionMenu\"\n [attr.aria-label]=\"localeText().filterConditionMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">{{ getConditionButtonLabel(col.field) }}</button>\n }\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\" (pointerdown)=\"$event.stopPropagation()\"\n (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <ng-template #bodyRow let-item let-ariaIndex=\"ariaIndex\">\n <div role=\"row\" [attr.aria-rowindex]=\"ariaIndex\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item) && !isDetailRowItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-add-row--selected]=\"item === null && isAddRowSelected()\" [class.ag-ghost-row]=\"item === 'ghost'\"\n [class.ag-group-header-row]=\"isGroupHeaderItem(item)\" [class.ag-detail-row]=\"isDetailRowItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [class]=\"isDataRowItem(item) ? getRowClass(item.row, item.originalIndex) : ''\"\n [attr.data-original-index]=\"getItemOriginalIndex(item)\" [style.height.px]=\"rowPx(item)\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? scrollableGridTemplateColumns() : null\"\n (pointerdown)=\"isDataRowItem(item) && onRowPointerDown($event, item.originalIndex)\"\n (click)=\"isDataRowItem(item) && onRowClick($event, item)\">\n @if (isDataRowItem(item)) {\n @for (col of scrollableColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [displayValueOverride]=\"treeCellDisplayOverride(item, col)\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\" [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\"\n [editable]=\"isColEditable(col, item.originalIndex)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [showInfoIcon]=\"showCellInfoIcon(col, item.row)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\" [selectTextOnEdit]=\"selectTextOnEdit()\"\n [treeCell]=\"isTreeCell(col)\"\n [treeLevel]=\"treeRowLevel(item)\" [treeExpandable]=\"treeRowExpandable(item)\"\n [treeExpanded]=\"treeRowExpanded(item)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class]=\"getCellClass(col, item.row[col.field], item.row)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\"\n (startEdit)=\"onStartEdit(item.originalIndex, ci)\" (draftChange)=\"onDraftChange($event)\"\n (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\" (treeToggle)=\"onTreeToggle(item)\"\n (infoClick)=\"onCellInfo(item.originalIndex, col, item.row)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-add-row-label\" (click)=\"onActivateAddRow()\">\n <span class=\"ag-add-row-icon\">+</span> {{ localeText().addRow }}\n </div>\n } @else if (item === 'ghost') {\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-ghost-cell\" [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\">{{\n getGhostCellDisplay(col) }}</div>\n }\n } @else if (isDetailRowItem(item)) {\n <div class=\"ag-detail-panel\" [style.height.px]=\"detailRowHeight()\"\n [style.min-width.px]=\"scrollableTotalWidth()\" [innerHTML]=\"detailHtml(item)\"></div>\n } @else if (isPathTreeNodeItem(item)) {\n <div class=\"ag-path-tree-content\" [style.min-width.px]=\"scrollableTotalWidth()\"\n [style.padding-left.px]=\"treeRowLevel(item) * 16 + 8\"\n (click)=\"onTreeNodeClick(item); $event.stopPropagation()\"\n (dblclick)=\"onTreeNodeDoubleClick(item); $event.stopPropagation()\">\n <button type=\"button\" class=\"ag-tree-twisty\"\n [class.ag-tree-twisty--expanded]=\"treeRowExpanded(item)\"\n [attr.aria-expanded]=\"treeRowExpanded(item)\"\n (click)=\"onTreeToggle(item); $event.stopPropagation()\">\u25B6</button>\n <span>{{ pathTreeLabel(item) }}</span>\n </div>\n } @else {\n <div class=\"ag-group-header-content\" [style.min-width.px]=\"scrollableTotalWidth()\">\n <div class=\"ag-group-header-main\"\n (click)=\"onGroupHeaderClick(item.groupLabel); $event.stopPropagation()\">\n <span class=\"ag-group-icon\" [class.ag-group-icon--expanded]=\"!item.collapsed\">\u25B6</span>\n <span class=\"ag-group-label\">{{ item.groupLabel }}</span>\n @if (groupActions().length > 0) {\n <button class=\"ag-group-actions-btn\" [title]=\"localeText().actions\"\n (click)=\"openGroupActionsMenu($event, item.groupLabel)\">\u22EE</button>\n }\n <span class=\"ag-group-count\">{{ item.count }}</span>\n @if (getGroupDescription(item.groupLabel); as desc) {\n <span class=\"ag-group-description\">{{ desc }}</span>\n }\n @if (item.aggregates; as aggs) {\n <span class=\"ag-group-aggregates\">\n @for (col of visibleColDefs(); track col.field) {\n @if (hasAggregate(col) && aggs[col.field] !== undefined) {\n <span class=\"ag-group-aggregate\">\n <span class=\"ag-group-aggregate-col\">{{ col.header }}</span>\n <span class=\"ag-group-aggregate-op\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, aggs[col.field]) }}\n </span>\n }\n }\n </span>\n }\n </div>\n </div>\n }\n @if (isDataRowItem(item) && isRowPendingDelete(item.originalIndex)) {\n <div class=\"ag-delete-confirmation\" role=\"alertdialog\" [attr.aria-label]=\"localeText().confirmDeleteRow\"\n [style.left.px]=\"deleteConfirmationLeft()\" [style.width.px]=\"deleteConfirmationWidth() || null\"\n (pointerdown)=\"$event.stopPropagation()\" (click)=\"$event.stopPropagation()\">\n <span class=\"ag-delete-confirmation-text\">{{ localeText().confirmDeleteRow }}</span>\n <button class=\"ag-delete-confirmation-btn ag-delete-confirmation-btn--yes\" type=\"button\"\n (click)=\"confirmPendingRowDelete()\">{{ localeText().confirmYes }}</button>\n <button class=\"ag-delete-confirmation-btn\" type=\"button\" data-delete-confirm-no\n (click)=\"cancelRowDelete()\">{{ localeText().confirmNo }}</button>\n </div>\n }\n </div>\n </ng-template>\n\n @if (hasPinnedTopRows()) {\n <div class=\"ag-pinned-rows ag-pinned-rows--top\" [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (item of pinnedTopItems(); track item.originalIndex) {\n <ng-container [ngTemplateOutlet]=\"bodyRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: headerRowCount() + 1 + $index }\" />\n }\n </div>\n }\n\n <cdk-virtual-scroll-viewport #scrollViewport class=\"ag-body\" [agridVariableRowSize]=\"itemSizes()\"\n [style.min-width.px]=\"scrollableTotalWidth()\" (scroll)=\"onBodyScroll()\">\n <ng-container *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\">\n <ng-container [ngTemplateOutlet]=\"bodyRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: di + headerRowCount() + 1 + pinnedTopItems().length }\" />\n </ng-container>\n </cdk-virtual-scroll-viewport>\n\n @if (hasPinnedBottomRows()) {\n <div class=\"ag-pinned-rows ag-pinned-rows--bottom\" [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (item of pinnedBottomItems(); track item.originalIndex) {\n <ng-container [ngTemplateOutlet]=\"bodyRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: headerRowCount() + 1 + pinnedTopItems().length + displayItems().length + $index }\" />\n }\n </div>\n }\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\" [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if (hasRightPinnedPane()) {\n <div class=\"ag-pinned-pane ag-pinned-pane--right\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @for (run of rightHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\" [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-header-cell ag-header-cell--pinned\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-header-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\" [attr.data-col-field]=\"col.field\"\n (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n @if (isGroupedByField(col.field)) {\n <span class=\"ag-sort-badge\">'\u229F '</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\" (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n @if (getMenuFilterType(col.field)) {\n <button type=\"button\" class=\"ag-filter-condition-btn\"\n [class.ag-filter-condition-btn--active]=\"!!getMenuOperator(col.field)\"\n [title]=\"localeText().filterConditionMenu\"\n [attr.aria-label]=\"localeText().filterConditionMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">{{ getConditionButtonLabel(col.field) }}</button>\n }\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\" (pointerdown)=\"$event.stopPropagation()\"\n (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <ng-template #rightRow let-item let-ariaIndex=\"ariaIndex\">\n <div role=\"row\" [attr.aria-rowindex]=\"ariaIndex\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item) && !isDetailRowItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-ghost-row]=\"item === 'ghost'\" [class.ag-group-header-row]=\"isGroupHeaderItem(item)\"\n [class.ag-detail-row]=\"isDetailRowItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [class]=\"isDataRowItem(item) ? getRowClass(item.row, item.originalIndex) : ''\"\n [style.height.px]=\"rowPx(item)\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? rightGridTemplateColumns() : null\">\n @if (isDataRowItem(item)) {\n @for (col of rightPinnedColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [displayValueOverride]=\"treeCellDisplayOverride(item, col)\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\" [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\"\n [editable]=\"isColEditable(col, item.originalIndex)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [showInfoIcon]=\"showCellInfoIcon(col, item.row)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\" [selectTextOnEdit]=\"selectTextOnEdit()\"\n [treeCell]=\"isTreeCell(col)\"\n [treeLevel]=\"treeRowLevel(item)\" [treeExpandable]=\"treeRowExpandable(item)\"\n [treeExpanded]=\"treeRowExpanded(item)\" [class]=\"getCellClass(col, item.row[col.field], item.row)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--pinned]=\"true\" [class.ag-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\" (startEdit)=\"onStartEdit(item.originalIndex, ci)\"\n (draftChange)=\"onDraftChange($event)\" (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\"\n (infoClick)=\"onCellInfo(item.originalIndex, col, item.row)\"\n (treeToggle)=\"onTreeToggle(item)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-pinned-row-spacer\"></div>\n } @else if (item === 'ghost') {\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-ghost-cell ag-cell--pinned\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\">{{ getGhostCellDisplay(col) }}</div>\n }\n } @else {\n <div class=\"ag-pinned-row-spacer\"></div>\n }\n </div>\n </ng-template>\n\n @if (hasPinnedTopRows()) {\n <div class=\"ag-pinned-rows ag-pinned-rows--top ag-pinned-body ag-right-pinned-body\"\n [style.width.px]=\"rightPinnedPaneWidth()\">\n @for (item of pinnedTopItems(); track item.originalIndex) {\n <ng-container [ngTemplateOutlet]=\"rightRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: headerRowCount() + 1 + $index }\" />\n }\n </div>\n }\n\n <cdk-virtual-scroll-viewport #rightPinnedViewport class=\"ag-body ag-pinned-body ag-right-pinned-body\"\n [agridVariableRowSize]=\"itemSizes()\" [style.width.px]=\"rightPinnedPaneWidth()\"\n (scroll)=\"onRightPinnedBodyScroll()\">\n <ng-container *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\">\n <ng-container [ngTemplateOutlet]=\"rightRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: di + headerRowCount() + 1 + pinnedTopItems().length }\" />\n </ng-container>\n </cdk-virtual-scroll-viewport>\n\n @if (hasPinnedBottomRows()) {\n <div class=\"ag-pinned-rows ag-pinned-rows--bottom ag-pinned-body ag-right-pinned-body\"\n [style.width.px]=\"rightPinnedPaneWidth()\">\n @for (item of pinnedBottomItems(); track item.originalIndex) {\n <ng-container [ngTemplateOutlet]=\"rightRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: headerRowCount() + 1 + pinnedTopItems().length + displayItems().length + $index }\" />\n }\n </div>\n }\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\" [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\">\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [class.ag-footer-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n\n @if (loading()) {\n <div class=\"ag-state-overlay\" role=\"status\" aria-live=\"polite\">{{ localeText().loading }}</div>\n } @else if (isEmpty()) {\n <div class=\"ag-state-overlay\" role=\"status\" aria-live=\"polite\">{{ emptyTextLabel() }}</div>\n }\n\n @if (showSidebar()) {\n <agrid-sidebar [open]=\"sidebarOpen()\" [activeTab]=\"sidebarTab()\" [columns]=\"colDefs()\"\n [headerGroups]=\"headerGroups()\" [row]=\"sidebarRow()\" [rowIndex]=\"selectedRowIndex()\"\n [hiddenColumns]=\"sidebarHiddenColumns()\" [locale]=\"locale()\"\n [localeText]=\"localeText()\" [readonlyGrid]=\"readonlyGrid()\" [useSidebarEditor]=\"useSidebarEditor()\"\n [isCellEditable]=\"isCellEditableForRow\"\n [errors]=\"sidebarValidationErrors()\" (close)=\"toggleSidebar()\" (tabChange)=\"onSidebarStripClick($event)\"\n (toggleColumn)=\"onSidebarToggleColumn($event)\"\n (toggleColumnGroup)=\"onSidebarToggleColumnGroup($event.fields, $event.visible)\"\n (detailEdit)=\"onSidebarDetailEdit($event)\" (save)=\"onSidebarDetailSave($event)\" />\n }\n </div><!-- /.ag-main-area -->\n\n @if (showPagination()) {\n <nav class=\"ag-pagination\" [attr.aria-label]=\"localeText().pagination\">\n <button class=\"ag-page-btn\" [attr.aria-label]=\"localeText().firstPage\" [disabled]=\"control()!.currentPage() <= 1\"\n (click)=\"goToFirstPage()\">\u00AB</button>\n <button class=\"ag-page-btn\" [attr.aria-label]=\"localeText().previous\" [disabled]=\"control()!.currentPage() <= 1\"\n (click)=\"goToPrevPage()\">\u2039</button>\n <span class=\"ag-page-info\" aria-live=\"polite\">{{ control()!.currentPage() }} / {{ totalPages() }}</span>\n <button class=\"ag-page-btn\" [disabled]=\"control()!.currentPage() >= totalPages()\"\n [attr.aria-label]=\"localeText().next\" (click)=\"goToNextPage()\">\u203A</button>\n <button class=\"ag-page-btn\" [disabled]=\"control()!.currentPage() >= totalPages()\"\n [attr.aria-label]=\"localeText().lastPage\" (click)=\"goToLastPage()\">\u00BB</button>\n <span class=\"ag-page-count\">{{ localeText().rows(filteredRowCount()) }}</span>\n </nav>\n }\n\n @if (findOpen()) {\n <agrid-find-panel [query]=\"findQuery()\" [matchCount]=\"findMatches().length\" [activeIndex]=\"findActiveIndex()\"\n [localeText]=\"localeText()\" (queryChange)=\"onFindInput($event)\" (previous)=\"goToFindMatch(-1)\"\n (next)=\"goToFindMatch(1)\" (close)=\"closeFind()\" />\n }\n\n <!-- Row context menu -->\n @if (contextMenu(); as menu) {\n @if(hasContextMenuEntries()) {\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n @if (!treeConfig()) {\n @if (rowPinState(menu.rowIndex)) {\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onPinRow(menu.rowIndex, null)\">{{ localeText().unpinRow\n }}</button>\n } @else {\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onPinRow(menu.rowIndex, 'top')\">{{ localeText().pinRowTop\n }}</button>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onPinRow(menu.rowIndex, 'bottom')\">{{\n localeText().pinRowBottom }}</button>\n }\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n }\n @if (!readonlyGrid()) {\n <button class=\"ag-context-item ag-context-item--danger\" role=\"menuitem\" (click)=\"deleteRow(menu.rowIndex)\">\n {{ localeText().deleteRow }}\n </button>\n }\n </div>\n }\n }\n\n <!-- Cell context menu -->\n @if (cellContextMenuState(); as menu) {\n @let col = getColDef(menu.field)!;\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"copyCellToClipboard(menu.rowIndex, col)\">{{\n localeText().copyCellValue\n }}</button>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"copyRowToClipboard(menu.rowIndex)\">{{ localeText().copyRow\n }}</button>\n @if (!treeConfig()) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n @if (rowPinState(menu.rowIndex)) {\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onPinRow(menu.rowIndex, null)\">{{ localeText().unpinRow\n }}</button>\n } @else {\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onPinRow(menu.rowIndex, 'top')\">{{ localeText().pinRowTop\n }}</button>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onPinRow(menu.rowIndex, 'bottom')\">{{\n localeText().pinRowBottom }}</button>\n }\n }\n @if (allowAddRows() && !readonlyGrid()) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"insertRowAt(menu.rowIndex)\">{{ localeText().insertRowAbove\n }}</button>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"insertRowAt(menu.rowIndex + 1)\">{{\n localeText().insertRowBelow }}</button>\n }\n @if (!readonlyGrid()) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n <button class=\"ag-context-item ag-context-item--danger\" role=\"menuitem\" (click)=\"deleteRow(menu.rowIndex)\">{{\n localeText().deleteRow }}</button>\n }\n @if (cellMenuItems().length) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n @for (item of cellMenuItems(); track $index) {\n @if (item === null) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n } @else {\n <button class=\"ag-context-item\" role=\"menuitem\" [class.ag-context-item--danger]=\"item.danger\"\n [disabled]=\"item.disabled\" (click)=\"runCellMenuItem(item, menu)\">{{\n item.label }}</button>\n }\n }\n }\n </div>\n }\n\n <!-- Group actions menu -->\n @if (groupActionsMenu(); as menu) {\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n @for (action of groupActions(); track action.label) {\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onGroupAction(action, menu.label)\">\n {{ action.label }}\n </button>\n }\n </div>\n }\n\n <!-- Filter dropdown -->\n @if (filterMenu(); as menu) {\n <agrid-column-menu [x]=\"menu.x\" [y]=\"menu.y\" [header]=\"getColDef(menu.field)?.header ?? menu.field\"\n [sortDir]=\"getSort(menu.field)\" [sortable]=\"sortOption() !== 'none'\" [showColumnActions]=\"!!control()\"\n [pinned]=\"getColumnPinState(menu.field)\" [groupable]=\"!!getColDef(menu.field)?.groupable\"\n [grouped]=\"isGroupedByField(menu.field)\" [filterable]=\"!!getColDef(menu.field)?.filterable\"\n [showValueFilter]=\"!serverSideFiltering()\" [filterType]=\"getMenuFilterType(menu.field)\"\n [operator]=\"getMenuOperator(menu.field)\" [operand]=\"getMenuOperand(menu.field)\"\n [operand2]=\"getMenuOperand2(menu.field)\" (operatorChange)=\"onMenuOperatorChange(menu.field, $event)\"\n (operandChange)=\"onMenuOperandChange(menu.field, $event)\"\n (operand2Change)=\"onMenuOperand2Change(menu.field, $event)\" [search]=\"filterMenuSearch()\"\n [allSelected]=\"isMenuAllSelected(menu.field)\"\n [valueItems]=\"serverSideFiltering() || !getColDef(menu.field)?.filterable ? [] : columnMenuValueItems()\"\n [localeText]=\"localeText()\" [sortPriority]=\"getSortPriority(menu.field)\" [hasMultiSort]=\"hasMultiSort()\"\n (sort)=\"onMenuSort(menu.field, $event)\" (resetSort)=\"onMenuResetSort(menu.field, $event)\"\n (autosize)=\"onMenuAutosizeColumn(menu.field)\" (togglePin)=\"onMenuTogglePin(menu.field)\"\n (togglePinRight)=\"onMenuTogglePinRight(menu.field)\" (hide)=\"onMenuHideColumn(menu.field)\"\n (toggleGroup)=\"onMenuToggleGroupBy(menu.field)\" (clearFilter)=\"onMenuClearFilter(menu.field)\"\n (clearAll)=\"onMenuClearAll()\" (searchChange)=\"onFilterMenuSearch($event)\" (toggleAll)=\"onMenuToggleAll(menu.field)\"\n (toggleValue)=\"onMenuToggleValue(menu.field, $event)\" [aggregate]=\"getEffectiveAggregate(getColDef(menu.field)!)\"\n (setAggregate)=\"onMenuSetAggregate(menu.field, $event)\" />\n }\n\n @if (columnDragPreview(); as preview) {\n <div class=\"ag-column-drag-preview\" [style.left.px]=\"preview.x\" [style.top.px]=\"preview.y\"\n [style.width.px]=\"preview.width\" [style.height.px]=\"preview.height\">\n <span>{{ preview.label }}</span>\n </div>\n }\n</div>\n", styles: ["@layer agrid-defaults{:host{--agrid-color-text: #24292f;--agrid-color-text-muted: #57606a;--agrid-color-accent: #1a73e8;--agrid-color-accent-subtle: #e8f0fe;--agrid-color-accent-fg: #1558b0;--agrid-color-accent-border: #c8d8f8;--agrid-color-danger: #d1242f;--agrid-color-danger-subtle: #fff1f0;--agrid-color-border: #d0d7de;--agrid-color-bg: #ffffff;--agrid-color-bg-subtle: #fafbfc;--agrid-color-bg-muted: #f6f8fa;--agrid-color-shadow: rgba(140, 149, 159, .2);--agrid-color-bg-stripe: #f0f2f5;--agrid-color-cell-changed: #f59e0b;--agrid-color-row-marked: #fff8c5}}:host{display:flex;flex-direction:column;min-height:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:13px;color:var(--agrid-color-text)}.ag-wrapper{position:relative;display:flex;flex-direction:column;flex:1;min-height:0;border:1px solid var(--agrid-color-border);overflow:hidden;outline:none;border-radius:4px}.ag-state-overlay{position:absolute;inset:34px 0 0;display:flex;align-items:center;justify-content:center;color:var(--agrid-color-text-muted);background:color-mix(in srgb,var(--agrid-color-bg) 78%,transparent);pointer-events:none;z-index:3}.ag-header{display:grid;flex-shrink:0;background:var(--agrid-color-bg-muted);border-bottom:2px solid var(--agrid-color-border)}.ag-header-groups{display:grid;flex-shrink:0;height:28px;background:color-mix(in srgb,var(--agrid-color-bg-muted) 72%,var(--agrid-color-bg));border-bottom:1px solid var(--agrid-color-border)}.ag-header-group-cell{display:flex;align-items:center;justify-content:center;min-width:0;padding:0 8px;overflow:hidden;border-right:1px solid var(--agrid-color-border);box-sizing:border-box;color:var(--agrid-color-text);font-size:12px;font-weight:600;text-overflow:ellipsis;white-space:nowrap;cursor:grab;-webkit-user-select:none;user-select:none;transition:opacity .1s ease}.ag-header-group-cell:last-child{border-right:none}.ag-header-group-cell--empty,.ag-header-group-cell--locked{cursor:default}.ag-header-group-cell--dragging{opacity:.12}.ag-header-cell{position:relative;display:flex;align-items:center;font-weight:600;border-right:1px solid var(--agrid-color-border);overflow:hidden;white-space:nowrap;-webkit-user-select:none;user-select:none;box-sizing:border-box}.ag-header-cell:last-child{border-right:none}.ag-header-cell-top{display:flex;align-items:center;flex:1;min-width:0;padding:0 6px;height:32px;overflow:hidden;white-space:nowrap}.ag-header-cell-label{overflow:hidden;text-overflow:ellipsis;flex:1}.ag-header--with-filters .ag-header-cell{flex-direction:column;align-items:stretch;height:auto;white-space:normal}.ag-header--with-filters .ag-header-cell-top{flex:0 0 32px}.ag-header-cell-filter{height:28px;display:flex;align-items:center;padding:0 2px;border-top:1px solid var(--agrid-color-border);background:var(--agrid-color-bg)}.ag-header-cell{cursor:grab;transition:transform .16s cubic-bezier(.2,0,0,1),opacity .1s ease;will-change:transform}.ag-row>agrid-cell,.ag-ghost-cell,.ag-footer-cell{transition:transform .16s cubic-bezier(.2,0,0,1),opacity .1s ease;will-change:transform}.ag-header-cell--dragging{opacity:.12;cursor:grabbing}.ag-column-reorder-item--dragging{opacity:.12}.ag-header-cell--drop-before,.ag-header-cell--drop-after{z-index:1}.ag-column-drag-preview{position:fixed;z-index:1000;display:flex;align-items:flex-start;padding:8px 10px;border:1px solid var(--agrid-color-accent);border-radius:5px;color:var(--agrid-color-text);background:color-mix(in srgb,var(--agrid-color-bg) 94%,var(--agrid-color-accent));box-shadow:0 10px 28px var(--agrid-color-shadow);font-weight:600;line-height:16px;box-sizing:border-box;pointer-events:none;opacity:.96}.ag-sort-badge{font-size:11px;color:var(--agrid-color-accent);flex-shrink:0;line-height:1}.ag-sort-priority{font-size:9px;vertical-align:super;opacity:.75}.ag-resize-handle{position:absolute;top:0;right:0;width:5px;height:100%;cursor:col-resize;z-index:1}.ag-resize-handle:hover{background:var(--agrid-color-accent);opacity:.5}.ag-filter-input{flex:1;min-width:0;height:20px;border:1px solid var(--agrid-color-border);border-radius:3px;outline:none;font:inherit;font-size:12px;padding:0 4px;background:var(--agrid-color-bg)}.ag-filter-condition-btn{flex:0 0 24px;height:20px;padding:0;border:1px solid var(--agrid-color-border);border-radius:3px;background:var(--agrid-color-bg-subtle);color:var(--agrid-color-text-muted);cursor:pointer}.ag-filter-condition-btn:hover,.ag-filter-condition-btn:focus-visible{border-color:var(--agrid-color-accent-border);color:var(--agrid-color-accent-fg)}.ag-filter-condition-btn--active{border-color:var(--agrid-color-accent-border);background:var(--agrid-color-accent-subtle);color:var(--agrid-color-accent-fg)}.ag-filter-input:focus{border-color:var(--agrid-color-accent)}.ag-header-cell--filtered .ag-header-cell-label:after{content:\" \\25be\";font-size:9px;color:var(--agrid-color-accent)}.ag-main-area{display:flex;flex:1;min-height:0;overflow:hidden}.ag-grid-split{display:flex;flex:1;min-width:0;min-height:0;overflow:hidden}.ag-pinned-pane{flex-shrink:0;min-height:0;display:flex;flex-direction:column;overflow:hidden;border-right:1px solid var(--agrid-color-border);background:var(--agrid-color-bg)}.ag-scroll-pane{flex:1;min-width:0;min-height:0;display:flex}.ag-horizontal-scroll{flex:1;min-width:0;min-height:0;overflow-x:auto;overflow-y:hidden;display:flex;flex-direction:column}.ag-body{flex:1;overflow-x:clip;overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-horizontal-scroll{scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-body::-webkit-scrollbar,.ag-horizontal-scroll::-webkit-scrollbar{width:8px;height:8px}.ag-body::-webkit-scrollbar-track,.ag-horizontal-scroll::-webkit-scrollbar-track{background:transparent}.ag-body::-webkit-scrollbar-thumb,.ag-horizontal-scroll::-webkit-scrollbar-thumb{background:#0000002e;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-body::-webkit-scrollbar-thumb:hover,.ag-horizontal-scroll::-webkit-scrollbar-thumb:hover{background:#00000052;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-pinned-body{overflow:hidden}.ag-right-pinned-body{overflow-y:auto}.ag-has-right-pane .ag-body:not(.ag-pinned-body){scrollbar-width:none}.ag-has-right-pane .ag-body:not(.ag-pinned-body)::-webkit-scrollbar{display:none}.ag-row{display:grid;position:relative}.ag-row--pending-delete>:not(.ag-delete-confirmation){opacity:.2;pointer-events:none;transition:opacity .14s ease}.ag-delete-confirmation{position:absolute;top:0;bottom:0;z-index:7;display:flex;align-items:center;justify-content:center;gap:8px;min-width:min(100%,320px);padding:2px 12px;box-sizing:border-box;color:var(--agrid-color-text);background:color-mix(in srgb,var(--agrid-color-danger-subtle) 88%,transparent);border-block:1px solid color-mix(in srgb,var(--agrid-color-danger) 35%,transparent)}.ag-delete-confirmation-text{font-weight:600;white-space:nowrap}.ag-delete-confirmation-btn{min-width:42px;padding:3px 9px;border:1px solid var(--agrid-color-border);border-radius:4px;color:var(--agrid-color-text);background:var(--agrid-color-bg);font:inherit;cursor:pointer}.ag-delete-confirmation-btn:hover,.ag-delete-confirmation-btn:focus-visible{border-color:var(--agrid-color-accent);outline:none}.ag-delete-confirmation-btn--yes{color:#fff;border-color:var(--agrid-color-danger);background:var(--agrid-color-danger)}.ag-delete-confirmation-btn--yes:hover,.ag-delete-confirmation-btn--yes:focus-visible{border-color:color-mix(in srgb,var(--agrid-color-danger) 75%,#000);background:color-mix(in srgb,var(--agrid-color-danger) 85%,#000)}:host(.ag-zebra) .ag-row--odd agrid-cell:not(.editing){background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd .ag-cell--pinned{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd .ag-control-cell{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd:hover agrid-cell:not(.editing){background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd:hover .ag-cell--pinned,:host(.ag-zebra) .ag-row--odd:hover .ag-control-cell{background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd.ag-row--selected agrid-cell:not(.editing){background:var(--agrid-color-accent-subtle)}:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-cell--pinned,:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-control-cell{background:var(--agrid-color-accent-subtle)}:host(.ag-zebra) .ag-row--odd .ag-cell--pinned-first{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd:hover .ag-cell--pinned-first{background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-cell--pinned-first{background:var(--agrid-color-accent-subtle)}.ag-row:hover agrid-cell:not(.editing){background:var(--agrid-color-bg-muted)}.ag-row--selected agrid-cell:not(.editing){background:var(--agrid-color-accent-subtle)}.ag-row--marked agrid-cell:not(.editing),.ag-row--marked .ag-control-cell{background:var(--agrid-color-row-marked)}.ag-row--marked{box-shadow:inset 3px 0 #bf8700}.ag-row--selected.ag-row--marked{box-shadow:inset 3px 0 0 var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--range-selected:not(.editing){background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1px var(--agrid-color-accent-border)}.ag-row agrid-cell.ag-cell--find-match:not(.editing){background:#fff7cc}.ag-row agrid-cell.ag-cell--find-active:not(.editing){background:#ffe58a;box-shadow:inset 0 0 0 2px #b7791f}.ag-row agrid-cell.ag-cell--range-selected.selected:not(.editing){box-shadow:inset 0 0 0 2px var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--fill-preview:not(.editing){background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1px var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--fill-handle:not(.editing){position:relative}.ag-row agrid-cell.ag-cell--fill-handle:not(.editing):after{content:\"\";position:absolute;right:1px;bottom:1px;width:6px;height:6px;background:var(--agrid-color-accent);border:1px solid var(--agrid-color-bg);box-sizing:border-box;cursor:crosshair;z-index:4}.ag-row--selected .ag-control-cell{background:var(--agrid-color-accent-subtle)}.ag-row--selected{box-shadow:inset 3px 0 0 var(--agrid-color-accent)}.ag-control-header{z-index:3;border-right:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted)}.ag-control-header .ag-header-cell-filter{background:var(--agrid-color-bg-subtle)}.ag-control-cell{z-index:2;border-right:1px solid var(--agrid-color-border);border-bottom:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-subtle);cursor:context-menu;display:flex;align-items:center;justify-content:center;gap:6px;box-sizing:border-box;flex-direction:row-reverse}.ag-control-cell:after{content:\"\\22ee\";font-size:11px;color:var(--agrid-color-border);line-height:1}.ag-control-cell:hover:after{color:var(--agrid-color-text-muted)}.ag-control-cell--reorder{cursor:grab}.ag-control-cell--reorder:after{content:\"\\283f\";font-size:13px;color:var(--agrid-color-text)}.ag-control-cell--reorder:hover:after{color:var(--agrid-color-text-muted)}.ag-control-cell--reorder:active{cursor:grabbing}.ag-row-marker{position:relative;z-index:1;width:14px;height:14px;margin:0;accent-color:var(--agrid-color-accent);cursor:pointer}.ag-ghost-row{background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1.5px var(--agrid-color-accent);pointer-events:none;animation:ag-ghost-in .1s ease}@keyframes ag-ghost-in{0%{opacity:0}to{opacity:1}}.ag-ghost-handle:after{color:var(--agrid-color-accent)!important}.ag-ghost-cell{display:flex;align-items:center;padding:0 6px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;border-right:1px solid var(--agrid-color-accent-border);color:var(--agrid-color-accent-fg);font-size:13px;box-sizing:border-box;-webkit-user-select:none;user-select:none}.ag-ghost-cell:last-child{border-right:none}.ag-pinned-row-spacer,.ag-pinned-group-spacer{height:100%;border-bottom:1px solid var(--agrid-color-border);box-sizing:border-box}.ag-pinned-group-spacer{background:var(--agrid-color-bg-muted)}.ag-context-menu{position:fixed;z-index:1000;background:var(--agrid-color-bg-subtle);border:1px solid var(--agrid-color-border);border-radius:6px;box-shadow:0 8px 24px var(--agrid-color-shadow);min-width:160px;padding:4px 0;font-size:13px}.ag-context-item{display:block;width:100%;padding:6px 16px;text-align:left;background:none;border:none;cursor:pointer;color:var(--agrid-color-text);font:inherit}.ag-context-item:hover{background:var(--agrid-color-bg-muted)}.ag-context-separator{height:1px;background:var(--agrid-color-border);margin:3px 0}.ag-context-item--danger{color:var(--agrid-color-danger)}.ag-context-item--danger:hover{background:var(--agrid-color-danger-subtle)}.ag-group-header-row{display:flex;align-items:stretch;background:var(--agrid-color-bg-muted);border-bottom:1px solid var(--agrid-color-border);overflow:hidden}.ag-path-tree-content{display:flex;align-items:center;gap:4px;height:100%;box-sizing:border-box;color:var(--agrid-color-text);font-weight:600;border-bottom:1px solid var(--agrid-color-border)}.ag-path-tree-content .ag-tree-twisty{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:0;color:inherit;background:transparent;cursor:pointer;font-size:10px;transition:transform .12s ease}.ag-path-tree-content .ag-tree-twisty--expanded{transform:rotate(90deg)}.ag-group-header-content{display:flex;align-items:stretch;height:100%;width:100%;-webkit-user-select:none;user-select:none}.ag-group-header-main{display:flex;align-items:center;gap:6px;padding:0 10px;flex:1;min-width:0;cursor:pointer}.ag-group-header-main:hover{background:var(--agrid-color-bg-subtle)}.ag-group-description{font-size:11px;color:var(--agrid-color-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ag-group-actions-btn{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border:none;background:none;border-radius:3px;cursor:pointer;color:var(--agrid-color-text-muted);font-size:14px;padding:0}.ag-group-actions-btn:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text)}.ag-group-icon{font-size:9px;color:var(--agrid-color-text-muted);line-height:1;display:inline-block;transition:transform .15s ease}.ag-group-icon--expanded{transform:rotate(90deg)}.ag-group-label{font-weight:600;font-size:12px;color:var(--agrid-color-text)}.ag-group-count{font-size:11px;color:var(--agrid-color-text-muted);background:var(--agrid-color-bg);border:1px solid var(--agrid-color-border);border-radius:10px;padding:0 7px;line-height:16px}.ag-group-aggregates{display:inline-flex;align-items:center;gap:10px;margin-left:4px;flex-wrap:wrap}.ag-group-aggregate{font-size:11px;color:var(--agrid-color-text);white-space:nowrap}.ag-group-aggregate-col{color:var(--agrid-color-text-muted);margin-right:4px}.ag-group-aggregate-op{color:var(--agrid-color-text-muted);margin-right:3px}.ag-footer{display:grid;flex-shrink:0;background:var(--agrid-color-bg-muted);border-top:2px solid var(--agrid-color-border)}.ag-footer-cell{display:flex;align-items:center;gap:4px;padding:0 6px;height:30px;border-right:1px solid var(--agrid-color-border);box-sizing:border-box;overflow:hidden;white-space:nowrap;font-weight:600;font-size:12px;color:var(--agrid-color-text)}.ag-footer-cell:last-child{border-right:none}.ag-footer-cell--control{background:var(--agrid-color-bg-subtle);border-right:1px solid var(--agrid-color-border)}.ag-footer-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-footer-label{font-size:10px;font-weight:400;color:var(--agrid-color-text-muted);flex-shrink:0}.ag-pagination{display:flex;align-items:center;gap:4px;padding:0 10px;height:34px;flex-shrink:0;border-top:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted);font-size:12px;color:var(--agrid-color-text-muted)}.ag-page-btn{display:flex;align-items:center;justify-content:center;width:24px;height:22px;border:1px solid var(--agrid-color-border);border-radius:3px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font-size:13px;cursor:pointer;padding:0;line-height:1}.ag-page-btn:hover:not(:disabled){background:var(--agrid-color-bg-muted);border-color:var(--agrid-color-text-muted)}.ag-page-btn:disabled{opacity:.35;cursor:default}.ag-page-info{padding:0 6px;font-weight:500;color:var(--agrid-color-text);min-width:48px;text-align:center}.ag-page-count{margin-left:auto;color:var(--agrid-color-text-muted)}.ag-add-row{border-bottom:1px dashed var(--agrid-color-border);color:var(--agrid-color-text-muted);cursor:pointer}.ag-add-row:hover .ag-add-row-label{background:var(--agrid-color-bg-muted)}.ag-add-row--selected .ag-add-row-label{outline:2px solid var(--agrid-color-accent);outline-offset:-2px}.ag-add-row-label{display:flex;align-items:center;gap:4px;height:100%;padding:0 6px;font-size:12px;-webkit-user-select:none;user-select:none}.ag-add-row-icon{font-size:16px;line-height:1;color:var(--agrid-color-text-muted)}.ag-pinned-pane--right{border-left:1px solid var(--agrid-color-border);border-right:none;box-shadow:-3px 0 6px -2px var(--agrid-color-shadow)}.ag-header-cell--pinned{background:var(--agrid-color-bg-muted)}.ag-header-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-header-cell--pinned-first{box-shadow:none}.ag-cell--pinned{background:var(--agrid-color-bg)}.ag-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-cell--pinned-first{box-shadow:none}.ag-row:hover .ag-cell--pinned{background:var(--agrid-color-bg-muted)}.ag-row--selected .ag-cell--pinned,.ag-row .ag-cell--pinned.ag-cell--range-selected,.ag-row .ag-cell--pinned.ag-cell--fill-preview{background:var(--agrid-color-accent-subtle)}.ag-row .ag-cell--pinned.ag-cell--find-match{background:#fff7cc}.ag-row .ag-cell--pinned.ag-cell--find-active{background:#ffe58a}.ag-header-menu-btn{flex-shrink:0;width:16px;height:16px;padding:0;margin-right:2px;background:none;border:1px solid transparent;border-radius:3px;cursor:pointer;font-size:10px;color:var(--agrid-color-text-muted);display:flex;align-items:center;justify-content:center;opacity:1;transition:opacity .12s;line-height:1}.ag-header-menu-btn--active{color:var(--agrid-color-accent);border-color:var(--agrid-color-accent-border);background:var(--agrid-color-accent-subtle)}.ag-header-menu-btn:hover{background:var(--agrid-color-bg);border-color:var(--agrid-color-border)}.ag-toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:6px 12px;padding:6px 8px;border-bottom:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-subtle)}.ag-quick-filter{margin-left:auto;width:100%;max-width:260px;height:30px;padding:0 10px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text)}.ag-quick-filter:focus{border-color:var(--agrid-color-accent-border)}.ag-detail-toggle{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:none;background:transparent;cursor:pointer;font-size:9px;line-height:1;color:var(--agrid-color-text-muted);transition:transform .12s ease,color .12s ease}.ag-detail-toggle:hover{color:var(--agrid-color-text)}.ag-detail-toggle--expanded{transform:rotate(90deg)}.ag-detail-row{display:block;position:relative;box-sizing:border-box}.ag-detail-panel{box-sizing:border-box;height:100%;overflow:auto;padding:8px 12px;background:var(--agrid-color-bg-subtle);border-bottom:1px solid var(--agrid-color-border)}.ag-pinned-rows{position:relative;z-index:3;flex:none;background:var(--agrid-color-bg)}.ag-pinned-rows--top{box-shadow:0 2px 4px -2px var(--agrid-color-shadow)}.ag-pinned-rows--bottom{box-shadow:0 -2px 4px -2px var(--agrid-color-shadow)}.ag-pinned-rows .ag-row{background:var(--agrid-color-bg-subtle)}\n"], dependencies: [{ kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i1.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: AgridVariableRowSizeDirective, selector: "cdk-virtual-scroll-viewport[agridVariableRowSize]", inputs: ["agridVariableRowSize"] }, { kind: "component", type: AgridCellComponent, selector: "agrid-cell", inputs: ["col", "rowIndex", "colIndex", "value", "displayValueOverride", "row", "locale", "selected", "editing", "editable", "showInfoIcon", "error", "treeCell", "treeLevel", "treeExpandable", "treeExpanded", "seedChar", "selectTextOnEdit"], outputs: ["treeToggle", "activate", "startEdit", "booleanToggle", "infoClick", "draftChange"] }, { kind: "component", type: AgridMenuBarComponent, selector: "agrid-menu-bar", inputs: ["items", "context", "label", "openItemId"], outputs: ["action", "openItemIdChange"] }, { kind: "component", type: AgridColumnMenuComponent, selector: "agrid-column-menu", inputs: ["localeText", "x", "y", "header", "sortDir", "sortable", "showColumnActions", "pinned", "groupable", "grouped", "filterable", "showValueFilter", "filterType", "operator", "operand", "operand2", "search", "allSelected", "valueItems", "sortPriority", "hasMultiSort", "aggregate"], outputs: ["operatorChange", "operandChange", "operand2Change", "sort", "resetSort", "autosize", "togglePin", "togglePinRight", "hide", "toggleGroup", "clearFilter", "clearAll", "searchChange", "toggleAll", "toggleValue", "setAggregate"] }, { kind: "component", type: AgridFindPanelComponent, selector: "agrid-find-panel", inputs: ["localeText", "query", "matchCount", "activeIndex"], outputs: ["queryChange", "next", "previous", "close"] }, { kind: "component", type: AgridSidebarComponent, selector: "agrid-sidebar", inputs: ["open", "activeTab", "columns", "headerGroups", "row", "rowIndex", "hiddenColumns", "locale", "localeText", "readonlyGrid", "useSidebarEditor", "isCellEditable", "errors"], outputs: ["close", "tabChange", "toggleColumn", "toggleColumnGroup", "detailEdit", "save"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5994
7232
|
}
|
|
5995
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
7233
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridComponent, decorators: [{
|
|
5996
7234
|
type: Component,
|
|
5997
7235
|
args: [{ selector: 'agrid', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
5998
7236
|
ScrollingModule,
|
|
7237
|
+
NgTemplateOutlet,
|
|
7238
|
+
AgridVariableRowSizeDirective,
|
|
5999
7239
|
AgridCellComponent,
|
|
7240
|
+
AgridMenuBarComponent,
|
|
6000
7241
|
AgridColumnMenuComponent,
|
|
6001
7242
|
AgridFindPanelComponent,
|
|
6002
7243
|
AgridSidebarComponent,
|
|
@@ -6004,8 +7245,411 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
6004
7245
|
'[class.ag-zebra]': 'zebraStripes()',
|
|
6005
7246
|
'[style.min-height]': 'minHeight()',
|
|
6006
7247
|
'[style.max-height]': 'maxHeight()',
|
|
6007
|
-
}, template: "<div #wrapper class=\"ag-wrapper\" tabindex=\"0\" role=\"grid\" [attr.aria-label]=\"localeText().grid\"\n [attr.aria-rowcount]=\"ariaRowCount()\" [attr.aria-colcount]=\"ariaColCount()\"\n [attr.aria-multiselectable]=\"rowSelection() === 'multi' ? 'true' : null\"\n [attr.aria-readonly]=\"readonlyGrid() ? 'true' : null\"\n [attr.aria-busy]=\"loading() ? 'true' : null\" (copy)=\"onCopy($event)\" (paste)=\"onPaste($event)\"\n (focusin)=\"onGridFocusIn($event)\"\n (click)=\"closeContextMenu(); closeCellContextMenu(); closeFilterMenu(); closeGroupActionsMenu()\">\n @if (enableQuickFilter()) {\n <div class=\"ag-toolbar\">\n <input\n class=\"ag-quick-filter\"\n type=\"search\"\n [value]=\"quickFilterValue()\"\n (input)=\"onQuickFilterInput($event)\"\n [placeholder]=\"localeText().quickFilterPlaceholder\"\n [attr.aria-label]=\"localeText().quickFilterPlaceholder\"\n />\n </div>\n }\n <div class=\"ag-main-area\">\n <div class=\"ag-grid-split\" [class.ag-has-right-pane]=\"hasRightPinnedPane()\">\n @if (hasPinnedPane()) {\n <div class=\"ag-pinned-pane\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups ag-header-groups--pinned\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (showControlColumn()) {\n <div class=\"ag-header-group-cell ag-header-group-cell--empty\"></div>\n }\n @for (run of pinnedHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\"\n [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header ag-header--pinned\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (showControlColumn()) {\n <div class=\"ag-header-cell ag-control-header\" role=\"columnheader\" aria-colindex=\"1\">\n <div class=\"ag-header-cell-top\"></div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\"></div>\n }\n </div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-header-cell ag-header-cell--pinned\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-header-cell--pinned-last]=\"isLastPinnedColumn(col.field)\" [attr.data-col-field]=\"col.field\"\n (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\"\n (pointerdown)=\"$event.stopPropagation()\" (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <cdk-virtual-scroll-viewport #pinnedViewport class=\"ag-body ag-pinned-body\" [itemSize]=\"rowHeight()\"\n [style.width.px]=\"pinnedPaneWidth()\">\n <div *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\" role=\"row\"\n [attr.aria-rowindex]=\"di + headerRowCount() + 1\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-add-row--selected]=\"item === null && isAddRowSelected()\" [class.ag-ghost-row]=\"item === 'ghost'\"\n [class.ag-group-header-row]=\"isGroupHeaderItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isPinnedPaneRowSelected(item)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [attr.data-original-index]=\"getItemOriginalIndex(item)\" [style.height.px]=\"rowHeight()\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? pinnedGridTemplateColumns() : null\"\n (pointerdown)=\"isDataRowItem(item) && onRowPointerDown($event, item.originalIndex)\"\n (click)=\"isDataRowItem(item) && onRowClick($event, item)\">\n @if (isDataRowItem(item)) {\n @if (showControlColumn()) {\n <div class=\"ag-control-cell\" role=\"rowheader\" aria-colindex=\"1\"\n [class.ag-control-cell--reorder]=\"allowRowReorder()\"\n (contextmenu)=\"onControlContextMenu($event, item.originalIndex)\" (click)=\"$event.stopPropagation()\"\n (pointerdown)=\"onControlPointerDown($event, item.originalIndex)\">\n @if (enableRowMarking()) {\n <input class=\"ag-row-marker\" type=\"checkbox\" [checked]=\"isRowMarked(item.originalIndex)\"\n [attr.aria-label]=\"localeText().markRow\"\n (pointerdown)=\"$event.stopPropagation()\" (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRowMarked(item.originalIndex)\" />\n }\n </div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.title]=\"getCellTitle(col, item.row[col.field])\"\n [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\"\n [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\" [editable]=\"isColEditable(col)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\"\n [treeCell]=\"isTreeCell(col)\" [treeLevel]=\"treeRowLevel(item)\"\n [treeExpandable]=\"treeRowExpandable(item)\" [treeExpanded]=\"treeRowExpanded(item)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class]=\"getCellClass(col, item.row[col.field], item.row)\" [class.ag-cell--pinned]=\"true\"\n [class.ag-cell--pinned-last]=\"isLastPinnedColumn(col.field)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\" (startEdit)=\"onStartEdit(item.originalIndex, ci)\"\n (draftChange)=\"onDraftChange($event)\" (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\" (treeToggle)=\"onTreeToggle(item)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-pinned-row-spacer\"></div>\n } @else if (item === 'ghost') {\n @if (showControlColumn()) {\n <div class=\"ag-control-cell ag-control-cell--reorder ag-ghost-handle\"></div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-ghost-cell ag-cell--pinned\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--pinned-last]=\"isLastPinnedColumn(col.field)\">{{\n getGhostCellDisplay(col) }}</div>\n }\n } @else {\n <div class=\"ag-pinned-group-spacer\"></div>\n }\n </div>\n </cdk-virtual-scroll-viewport>\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\"\n [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\">\n @if (showControlColumn()) {\n <div class=\"ag-footer-cell ag-footer-cell--control\" role=\"gridcell\" aria-colindex=\"1\"></div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [class.ag-footer-cell--pinned-last]=\"isLastPinnedColumn(col.field)\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n <div class=\"ag-scroll-pane\">\n <div #horizontalScroller class=\"ag-horizontal-scroll\" (scroll)=\"onHorizontalScroll()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (run of scrollableHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\"\n [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-header-cell\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.data-col-field]=\"col.field\" (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n @if (isGroupedByField(col.field)) {\n <span class=\"ag-sort-badge\">\u229F</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\"\n (pointerdown)=\"$event.stopPropagation()\" (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <cdk-virtual-scroll-viewport #scrollViewport class=\"ag-body\" [itemSize]=\"rowHeight()\"\n [style.min-width.px]=\"scrollableTotalWidth()\" (scroll)=\"onBodyScroll()\">\n <div *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\" role=\"row\"\n [attr.aria-rowindex]=\"di + headerRowCount() + 1\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-add-row--selected]=\"item === null && isAddRowSelected()\" [class.ag-ghost-row]=\"item === 'ghost'\"\n [class.ag-group-header-row]=\"isGroupHeaderItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [attr.data-original-index]=\"getItemOriginalIndex(item)\" [style.height.px]=\"rowHeight()\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? scrollableGridTemplateColumns() : null\"\n (pointerdown)=\"isDataRowItem(item) && onRowPointerDown($event, item.originalIndex)\"\n (click)=\"isDataRowItem(item) && onRowClick($event, item)\">\n @if (isDataRowItem(item)) {\n @for (col of scrollableColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.title]=\"getCellTitle(col, item.row[col.field])\"\n [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\"\n [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\" [editable]=\"isColEditable(col)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\"\n [treeCell]=\"isTreeCell(col)\" [treeLevel]=\"treeRowLevel(item)\"\n [treeExpandable]=\"treeRowExpandable(item)\" [treeExpanded]=\"treeRowExpanded(item)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class]=\"getCellClass(col, item.row[col.field], item.row)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\"\n (startEdit)=\"onStartEdit(item.originalIndex, ci)\" (draftChange)=\"onDraftChange($event)\" (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\"\n (treeToggle)=\"onTreeToggle(item)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-add-row-label\" (click)=\"onActivateAddRow()\">\n <span class=\"ag-add-row-icon\">+</span> {{ localeText().addRow }}\n </div>\n } @else if (item === 'ghost') {\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-ghost-cell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\">{{\n getGhostCellDisplay(col) }}</div>\n }\n } @else {\n <div class=\"ag-group-header-content\" [style.min-width.px]=\"scrollableTotalWidth()\">\n <div class=\"ag-group-header-main\"\n (click)=\"onGroupHeaderClick(item.groupLabel); $event.stopPropagation()\">\n <span class=\"ag-group-icon\" [class.ag-group-icon--expanded]=\"!item.collapsed\">\u25B6</span>\n <span class=\"ag-group-label\">{{ item.groupLabel }}</span>\n @if (groupActions().length > 0) {\n <button class=\"ag-group-actions-btn\" [title]=\"localeText().actions\"\n (click)=\"openGroupActionsMenu($event, item.groupLabel)\">\u22EE</button>\n }\n <span class=\"ag-group-count\">{{ item.count }}</span>\n @if (getGroupDescription(item.groupLabel); as desc) {\n <span class=\"ag-group-description\">{{ desc }}</span>\n }\n @if (item.aggregates; as aggs) {\n <span class=\"ag-group-aggregates\">\n @for (col of visibleColDefs(); track col.field) {\n @if (hasAggregate(col) && aggs[col.field] !== undefined) {\n <span class=\"ag-group-aggregate\">\n <span class=\"ag-group-aggregate-col\">{{ col.header }}</span>\n <span class=\"ag-group-aggregate-op\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, aggs[col.field]) }}\n </span>\n }\n }\n </span>\n }\n </div>\n </div>\n }\n @if (isDataRowItem(item) && isRowPendingDelete(item.originalIndex)) {\n <div class=\"ag-delete-confirmation\" role=\"alertdialog\"\n [attr.aria-label]=\"localeText().confirmDeleteRow\"\n [style.left.px]=\"deleteConfirmationLeft()\"\n [style.width.px]=\"deleteConfirmationWidth() || null\"\n (pointerdown)=\"$event.stopPropagation()\" (click)=\"$event.stopPropagation()\">\n <span class=\"ag-delete-confirmation-text\">{{ localeText().confirmDeleteRow }}</span>\n <button class=\"ag-delete-confirmation-btn ag-delete-confirmation-btn--yes\"\n type=\"button\" (click)=\"confirmPendingRowDelete()\">{{ localeText().confirmYes }}</button>\n <button class=\"ag-delete-confirmation-btn\" type=\"button\" data-delete-confirm-no\n (click)=\"cancelRowDelete()\">{{ localeText().confirmNo }}</button>\n </div>\n }\n </div>\n </cdk-virtual-scroll-viewport>\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\"\n [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if (hasRightPinnedPane()) {\n <div class=\"ag-pinned-pane ag-pinned-pane--right\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @for (run of rightHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\"\n [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-header-cell ag-header-cell--pinned\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-header-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\" [attr.data-col-field]=\"col.field\"\n (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n @if (isGroupedByField(col.field)) {\n <span class=\"ag-sort-badge\">'\u229F '</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\"\n (pointerdown)=\"$event.stopPropagation()\" (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <cdk-virtual-scroll-viewport #rightPinnedViewport class=\"ag-body ag-pinned-body ag-right-pinned-body\"\n [itemSize]=\"rowHeight()\" [style.width.px]=\"rightPinnedPaneWidth()\" (scroll)=\"onRightPinnedBodyScroll()\">\n <div *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\" role=\"row\"\n [attr.aria-rowindex]=\"di + headerRowCount() + 1\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-ghost-row]=\"item === 'ghost'\" [class.ag-group-header-row]=\"isGroupHeaderItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [style.height.px]=\"rowHeight()\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? rightGridTemplateColumns() : null\">\n @if (isDataRowItem(item)) {\n @for (col of rightPinnedColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.title]=\"getCellTitle(col, item.row[col.field])\"\n [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\"\n [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\" [editable]=\"isColEditable(col)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\"\n [treeCell]=\"isTreeCell(col)\" [treeLevel]=\"treeRowLevel(item)\"\n [treeExpandable]=\"treeRowExpandable(item)\" [treeExpanded]=\"treeRowExpanded(item)\"\n [class]=\"getCellClass(col, item.row[col.field], item.row)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--pinned]=\"true\" [class.ag-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\" (startEdit)=\"onStartEdit(item.originalIndex, ci)\"\n (draftChange)=\"onDraftChange($event)\" (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\" (treeToggle)=\"onTreeToggle(item)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-pinned-row-spacer\"></div>\n } @else if (item === 'ghost') {\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-ghost-cell ag-cell--pinned\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\">{{ getGhostCellDisplay(col) }}</div>\n }\n } @else {\n <div class=\"ag-pinned-group-spacer\"></div>\n }\n </div>\n </cdk-virtual-scroll-viewport>\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\"\n [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\">\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [class.ag-footer-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n\n @if (loading()) {\n <div class=\"ag-state-overlay\" role=\"status\" aria-live=\"polite\">{{ localeText().loading }}</div>\n } @else if (isEmpty()) {\n <div class=\"ag-state-overlay\" role=\"status\" aria-live=\"polite\">{{ emptyTextLabel() }}</div>\n }\n\n @if (showSidebar()) {\n <agrid-sidebar\n [open]=\"sidebarOpen()\"\n [activeTab]=\"sidebarTab()\"\n [columns]=\"colDefs()\"\n [headerGroups]=\"headerGroups()\"\n [row]=\"sidebarRow()\"\n [hiddenColumns]=\"sidebarHiddenColumns()\"\n [locale]=\"locale()\"\n [localeText]=\"localeText()\"\n [readonlyGrid]=\"readonlyGrid()\"\n [useSidebarEditor]=\"useSidebarEditor()\"\n [errors]=\"sidebarValidationErrors()\"\n (close)=\"toggleSidebar()\"\n (tabChange)=\"onSidebarStripClick($event)\"\n (toggleColumn)=\"onSidebarToggleColumn($event)\"\n (toggleColumnGroup)=\"onSidebarToggleColumnGroup($event.fields, $event.visible)\"\n (detailEdit)=\"onSidebarDetailEdit($event)\"\n (save)=\"onSidebarDetailSave($event)\"\n />\n }\n </div><!-- /.ag-main-area -->\n\n @if (showPagination()) {\n <nav class=\"ag-pagination\" [attr.aria-label]=\"localeText().pagination\">\n <button class=\"ag-page-btn\" [attr.aria-label]=\"localeText().firstPage\"\n [disabled]=\"control()!.currentPage() <= 1\" (click)=\"goToFirstPage()\">\u00AB</button>\n <button class=\"ag-page-btn\" [attr.aria-label]=\"localeText().previous\"\n [disabled]=\"control()!.currentPage() <= 1\" (click)=\"goToPrevPage()\">\u2039</button>\n <span class=\"ag-page-info\" aria-live=\"polite\">{{ control()!.currentPage() }} / {{ totalPages() }}</span>\n <button class=\"ag-page-btn\" [disabled]=\"control()!.currentPage() >= totalPages()\"\n [attr.aria-label]=\"localeText().next\"\n (click)=\"goToNextPage()\">\u203A</button>\n <button class=\"ag-page-btn\" [disabled]=\"control()!.currentPage() >= totalPages()\"\n [attr.aria-label]=\"localeText().lastPage\"\n (click)=\"goToLastPage()\">\u00BB</button>\n <span class=\"ag-page-count\">{{ localeText().rows(filteredRowCount()) }}</span>\n </nav>\n }\n\n @if (findOpen()) {\n <agrid-find-panel [query]=\"findQuery()\" [matchCount]=\"findMatches().length\" [activeIndex]=\"findActiveIndex()\"\n [localeText]=\"localeText()\" (queryChange)=\"onFindInput($event)\" (previous)=\"goToFindMatch(-1)\"\n (next)=\"goToFindMatch(1)\" (close)=\"closeFind()\" />\n }\n\n <!-- Row context menu -->\n @if (contextMenu(); as menu) {\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n <button class=\"ag-context-item ag-context-item--danger\" role=\"menuitem\" (click)=\"deleteRow(menu.rowIndex)\">\n {{ localeText().deleteRow }}\n </button>\n </div>\n }\n\n <!-- Cell context menu -->\n @if (cellContextMenuState(); as menu) {\n @let col = getColDef(menu.field)!;\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"copyCellToClipboard(menu.rowIndex, col)\">{{ localeText().copyCellValue\n }}</button>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"copyRowToClipboard(menu.rowIndex)\">{{ localeText().copyRow }}</button>\n @if (allowAddRows() && !readonlyGrid()) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"insertRowAt(menu.rowIndex)\">{{ localeText().insertRowAbove }}</button>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"insertRowAt(menu.rowIndex + 1)\">{{ localeText().insertRowBelow }}</button>\n }\n @if (!readonlyGrid()) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n <button class=\"ag-context-item ag-context-item--danger\" role=\"menuitem\"\n (click)=\"deleteRow(menu.rowIndex)\">{{ localeText().deleteRow }}</button>\n }\n @if (cellMenuItems().length) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n @for (item of cellMenuItems(); track $index) {\n @if (item === null) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n } @else {\n <button class=\"ag-context-item\" role=\"menuitem\" [class.ag-context-item--danger]=\"item.danger\" [disabled]=\"item.disabled\"\n (click)=\"runCellMenuItem(item, menu)\">{{\n item.label }}</button>\n }\n }\n }\n </div>\n }\n\n <!-- Group actions menu -->\n @if (groupActionsMenu(); as menu) {\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n @for (action of groupActions(); track action.label) {\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onGroupAction(action, menu.label)\">\n {{ action.label }}\n </button>\n }\n </div>\n }\n\n <!-- Filter dropdown -->\n @if (filterMenu(); as menu) {\n <agrid-column-menu [x]=\"menu.x\" [y]=\"menu.y\" [header]=\"getColDef(menu.field)?.header ?? menu.field\"\n [sortDir]=\"getSort(menu.field)\" [sortable]=\"sortOption() !== 'none'\"\n [showColumnActions]=\"!!control()\" [pinned]=\"getColumnPinState(menu.field)\"\n [groupable]=\"!!getColDef(menu.field)?.groupable\" [grouped]=\"isGroupedByField(menu.field)\"\n [filterable]=\"!!getColDef(menu.field)?.filterable\" [showValueFilter]=\"!serverSideFiltering()\"\n [filterType]=\"getMenuFilterType(menu.field)\"\n [operator]=\"getMenuOperator(menu.field)\" [operand]=\"getMenuOperand(menu.field)\"\n [operand2]=\"getMenuOperand2(menu.field)\"\n (operatorChange)=\"onMenuOperatorChange(menu.field, $event)\"\n (operandChange)=\"onMenuOperandChange(menu.field, $event)\"\n (operand2Change)=\"onMenuOperand2Change(menu.field, $event)\"\n [search]=\"filterMenuSearch()\"\n [allSelected]=\"isMenuAllSelected(menu.field)\"\n [valueItems]=\"serverSideFiltering() || !getColDef(menu.field)?.filterable ? [] : columnMenuValueItems()\"\n [localeText]=\"localeText()\"\n [sortPriority]=\"getSortPriority(menu.field)\" [hasMultiSort]=\"hasMultiSort()\" (sort)=\"onMenuSort(menu.field, $event)\"\n (resetSort)=\"onMenuResetSort(menu.field, $event)\" (autosize)=\"onMenuAutosizeColumn(menu.field)\"\n (togglePin)=\"onMenuTogglePin(menu.field)\" (togglePinRight)=\"onMenuTogglePinRight(menu.field)\"\n (hide)=\"onMenuHideColumn(menu.field)\" (toggleGroup)=\"onMenuToggleGroupBy(menu.field)\"\n (clearFilter)=\"onMenuClearFilter(menu.field)\" (clearAll)=\"onMenuClearAll()\"\n (searchChange)=\"onFilterMenuSearch($event)\" (toggleAll)=\"onMenuToggleAll(menu.field)\"\n (toggleValue)=\"onMenuToggleValue(menu.field, $event)\" [aggregate]=\"getEffectiveAggregate(getColDef(menu.field)!)\"\n (setAggregate)=\"onMenuSetAggregate(menu.field, $event)\" />\n }\n\n @if (columnDragPreview(); as preview) {\n <div class=\"ag-column-drag-preview\"\n [style.left.px]=\"preview.x\" [style.top.px]=\"preview.y\"\n [style.width.px]=\"preview.width\" [style.height.px]=\"preview.height\">\n <span>{{ preview.label }}</span>\n </div>\n }\n</div>\n", styles: ["@layer agrid-defaults{:host{--agrid-color-text: #24292f;--agrid-color-text-muted: #57606a;--agrid-color-accent: #1a73e8;--agrid-color-accent-subtle: #e8f0fe;--agrid-color-accent-fg: #1558b0;--agrid-color-accent-border: #c8d8f8;--agrid-color-danger: #d1242f;--agrid-color-danger-subtle: #fff1f0;--agrid-color-border: #d0d7de;--agrid-color-bg: #ffffff;--agrid-color-bg-subtle: #fafbfc;--agrid-color-bg-muted: #f6f8fa;--agrid-color-shadow: rgba(140, 149, 159, .2);--agrid-color-bg-stripe: #f0f2f5;--agrid-color-cell-changed: #f59e0b;--agrid-color-row-marked: #fff8c5}}:host{display:flex;flex-direction:column;min-height:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:13px;color:var(--agrid-color-text)}.ag-wrapper{position:relative;display:flex;flex-direction:column;flex:1;min-height:0;border:1px solid var(--agrid-color-border);overflow:hidden;outline:none;border-radius:4px}.ag-state-overlay{position:absolute;inset:34px 0 0;display:flex;align-items:center;justify-content:center;color:var(--agrid-color-text-muted);background:color-mix(in srgb,var(--agrid-color-bg) 78%,transparent);pointer-events:none;z-index:3}.ag-header{display:grid;flex-shrink:0;background:var(--agrid-color-bg-muted);border-bottom:2px solid var(--agrid-color-border)}.ag-header-groups{display:grid;flex-shrink:0;height:28px;background:color-mix(in srgb,var(--agrid-color-bg-muted) 72%,var(--agrid-color-bg));border-bottom:1px solid var(--agrid-color-border)}.ag-header-group-cell{display:flex;align-items:center;justify-content:center;min-width:0;padding:0 8px;overflow:hidden;border-right:1px solid var(--agrid-color-border);box-sizing:border-box;color:var(--agrid-color-text);font-size:12px;font-weight:600;text-overflow:ellipsis;white-space:nowrap;cursor:grab;-webkit-user-select:none;user-select:none;transition:opacity .1s ease}.ag-header-group-cell:last-child{border-right:none}.ag-header-group-cell--empty,.ag-header-group-cell--locked{cursor:default}.ag-header-group-cell--dragging{opacity:.12}.ag-header-cell{position:relative;display:flex;align-items:center;font-weight:600;border-right:1px solid var(--agrid-color-border);overflow:hidden;white-space:nowrap;-webkit-user-select:none;user-select:none;box-sizing:border-box}.ag-header-cell:last-child{border-right:none}.ag-header-cell-top{display:flex;align-items:center;flex:1;min-width:0;padding:0 6px;height:32px;overflow:hidden;white-space:nowrap}.ag-header-cell-label{overflow:hidden;text-overflow:ellipsis;flex:1}.ag-header--with-filters .ag-header-cell{flex-direction:column;align-items:stretch;height:auto;white-space:normal}.ag-header--with-filters .ag-header-cell-top{flex:0 0 32px}.ag-header-cell-filter{height:28px;display:flex;align-items:center;padding:0 2px;border-top:1px solid var(--agrid-color-border);background:var(--agrid-color-bg)}.ag-header-cell{cursor:grab;transition:transform .16s cubic-bezier(.2,0,0,1),opacity .1s ease;will-change:transform}.ag-row>agrid-cell,.ag-ghost-cell,.ag-footer-cell{transition:transform .16s cubic-bezier(.2,0,0,1),opacity .1s ease;will-change:transform}.ag-header-cell--dragging{opacity:.12;cursor:grabbing}.ag-column-reorder-item--dragging{opacity:.12}.ag-header-cell--drop-before,.ag-header-cell--drop-after{z-index:1}.ag-column-drag-preview{position:fixed;z-index:1000;display:flex;align-items:flex-start;padding:8px 10px;border:1px solid var(--agrid-color-accent);border-radius:5px;color:var(--agrid-color-text);background:color-mix(in srgb,var(--agrid-color-bg) 94%,var(--agrid-color-accent));box-shadow:0 10px 28px var(--agrid-color-shadow);font-weight:600;line-height:16px;box-sizing:border-box;pointer-events:none;opacity:.96}.ag-sort-badge{font-size:11px;color:var(--agrid-color-accent);flex-shrink:0;line-height:1}.ag-sort-priority{font-size:9px;vertical-align:super;opacity:.75}.ag-resize-handle{position:absolute;top:0;right:0;width:5px;height:100%;cursor:col-resize;z-index:1}.ag-resize-handle:hover{background:var(--agrid-color-accent);opacity:.5}.ag-filter-input{flex:1;min-width:0;height:20px;border:1px solid var(--agrid-color-border);border-radius:3px;outline:none;font:inherit;font-size:12px;padding:0 4px;background:var(--agrid-color-bg)}.ag-filter-input:focus{border-color:var(--agrid-color-accent)}.ag-header-cell--filtered .ag-header-cell-label:after{content:\" \\25be\";font-size:9px;color:var(--agrid-color-accent)}.ag-main-area{display:flex;flex:1;min-height:0;overflow:hidden}.ag-grid-split{display:flex;flex:1;min-width:0;min-height:0;overflow:hidden}.ag-pinned-pane{flex-shrink:0;min-height:0;display:flex;flex-direction:column;overflow:hidden;border-right:1px solid var(--agrid-color-border);background:var(--agrid-color-bg)}.ag-scroll-pane{flex:1;min-width:0;min-height:0;display:flex}.ag-horizontal-scroll{flex:1;min-width:0;min-height:0;overflow-x:auto;overflow-y:hidden;display:flex;flex-direction:column}.ag-body{flex:1;overflow-x:clip;overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-horizontal-scroll{scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-body::-webkit-scrollbar,.ag-horizontal-scroll::-webkit-scrollbar{width:8px;height:8px}.ag-body::-webkit-scrollbar-track,.ag-horizontal-scroll::-webkit-scrollbar-track{background:transparent}.ag-body::-webkit-scrollbar-thumb,.ag-horizontal-scroll::-webkit-scrollbar-thumb{background:#0000002e;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-body::-webkit-scrollbar-thumb:hover,.ag-horizontal-scroll::-webkit-scrollbar-thumb:hover{background:#00000052;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-pinned-body{overflow:hidden}.ag-right-pinned-body{overflow-y:auto}.ag-has-right-pane .ag-body:not(.ag-pinned-body){scrollbar-width:none}.ag-has-right-pane .ag-body:not(.ag-pinned-body)::-webkit-scrollbar{display:none}.ag-row{display:grid;position:relative}.ag-row--pending-delete>:not(.ag-delete-confirmation){opacity:.2;pointer-events:none;transition:opacity .14s ease}.ag-delete-confirmation{position:absolute;top:0;bottom:0;z-index:7;display:flex;align-items:center;justify-content:center;gap:8px;min-width:min(100%,320px);padding:2px 12px;box-sizing:border-box;color:var(--agrid-color-text);background:color-mix(in srgb,var(--agrid-color-danger-subtle) 88%,transparent);border-block:1px solid color-mix(in srgb,var(--agrid-color-danger) 35%,transparent)}.ag-delete-confirmation-text{font-weight:600;white-space:nowrap}.ag-delete-confirmation-btn{min-width:42px;padding:3px 9px;border:1px solid var(--agrid-color-border);border-radius:4px;color:var(--agrid-color-text);background:var(--agrid-color-bg);font:inherit;cursor:pointer}.ag-delete-confirmation-btn:hover,.ag-delete-confirmation-btn:focus-visible{border-color:var(--agrid-color-accent);outline:none}.ag-delete-confirmation-btn--yes{color:#fff;border-color:var(--agrid-color-danger);background:var(--agrid-color-danger)}.ag-delete-confirmation-btn--yes:hover,.ag-delete-confirmation-btn--yes:focus-visible{border-color:color-mix(in srgb,var(--agrid-color-danger) 75%,#000);background:color-mix(in srgb,var(--agrid-color-danger) 85%,#000)}:host(.ag-zebra) .ag-row--odd agrid-cell:not(.editing){background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd .ag-cell--pinned{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd .ag-control-cell{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd:hover agrid-cell:not(.editing){background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd:hover .ag-cell--pinned,:host(.ag-zebra) .ag-row--odd:hover .ag-control-cell{background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd.ag-row--selected agrid-cell:not(.editing){background:var(--agrid-color-accent-subtle)}:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-cell--pinned,:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-control-cell{background:var(--agrid-color-accent-subtle)}:host(.ag-zebra) .ag-row--odd .ag-cell--pinned-first{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd:hover .ag-cell--pinned-first{background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-cell--pinned-first{background:var(--agrid-color-accent-subtle)}.ag-row:hover agrid-cell:not(.editing){background:var(--agrid-color-bg-muted)}.ag-row--selected agrid-cell:not(.editing){background:var(--agrid-color-accent-subtle)}.ag-row--marked agrid-cell:not(.editing),.ag-row--marked .ag-control-cell{background:var(--agrid-color-row-marked)}.ag-row--marked{box-shadow:inset 3px 0 #bf8700}.ag-row--selected.ag-row--marked{box-shadow:inset 3px 0 0 var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--range-selected:not(.editing){background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1px var(--agrid-color-accent-border)}.ag-row agrid-cell.ag-cell--find-match:not(.editing){background:#fff7cc}.ag-row agrid-cell.ag-cell--find-active:not(.editing){background:#ffe58a;box-shadow:inset 0 0 0 2px #b7791f}.ag-row agrid-cell.ag-cell--range-selected.selected:not(.editing){box-shadow:inset 0 0 0 2px var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--fill-preview:not(.editing){background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1px var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--fill-handle:not(.editing){position:relative}.ag-row agrid-cell.ag-cell--fill-handle:not(.editing):after{content:\"\";position:absolute;right:1px;bottom:1px;width:6px;height:6px;background:var(--agrid-color-accent);border:1px solid var(--agrid-color-bg);box-sizing:border-box;cursor:crosshair;z-index:4}.ag-row--selected .ag-control-cell{background:var(--agrid-color-accent-subtle)}.ag-row--selected{box-shadow:inset 3px 0 0 var(--agrid-color-accent)}.ag-control-header{z-index:3;border-right:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted)}.ag-control-header .ag-header-cell-filter{background:var(--agrid-color-bg-subtle)}.ag-control-cell{z-index:2;border-right:1px solid var(--agrid-color-border);border-bottom:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-subtle);cursor:context-menu;display:flex;align-items:center;justify-content:center;gap:6px;box-sizing:border-box;flex-direction:row-reverse}.ag-control-cell:after{content:\"\\22ee\";font-size:11px;color:var(--agrid-color-border);line-height:1}.ag-control-cell:hover:after{color:var(--agrid-color-text-muted)}.ag-control-cell--reorder{cursor:grab}.ag-control-cell--reorder:after{content:\"\\283f\";font-size:13px;color:var(--agrid-color-text)}.ag-control-cell--reorder:hover:after{color:var(--agrid-color-text-muted)}.ag-control-cell--reorder:active{cursor:grabbing}.ag-row-marker{position:relative;z-index:1;width:14px;height:14px;margin:0;accent-color:var(--agrid-color-accent);cursor:pointer}.ag-ghost-row{background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1.5px var(--agrid-color-accent);pointer-events:none;animation:ag-ghost-in .1s ease}@keyframes ag-ghost-in{0%{opacity:0}to{opacity:1}}.ag-ghost-handle:after{color:var(--agrid-color-accent)!important}.ag-ghost-cell{display:flex;align-items:center;padding:0 6px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;border-right:1px solid var(--agrid-color-accent-border);color:var(--agrid-color-accent-fg);font-size:13px;box-sizing:border-box;-webkit-user-select:none;user-select:none}.ag-ghost-cell:last-child{border-right:none}.ag-pinned-row-spacer,.ag-pinned-group-spacer{height:100%;border-bottom:1px solid var(--agrid-color-border);box-sizing:border-box}.ag-pinned-group-spacer{background:var(--agrid-color-bg-muted)}.ag-context-menu{position:fixed;z-index:1000;background:var(--agrid-color-bg-subtle);border:1px solid var(--agrid-color-border);border-radius:6px;box-shadow:0 8px 24px var(--agrid-color-shadow);min-width:160px;padding:4px 0;font-size:13px}.ag-context-item{display:block;width:100%;padding:6px 16px;text-align:left;background:none;border:none;cursor:pointer;color:var(--agrid-color-text);font:inherit}.ag-context-item:hover{background:var(--agrid-color-bg-muted)}.ag-context-separator{height:1px;background:var(--agrid-color-border);margin:3px 0}.ag-context-item--danger{color:var(--agrid-color-danger)}.ag-context-item--danger:hover{background:var(--agrid-color-danger-subtle)}.ag-group-header-row{display:flex;align-items:stretch;background:var(--agrid-color-bg-muted);border-bottom:1px solid var(--agrid-color-border);overflow:hidden}.ag-group-header-content{display:flex;align-items:stretch;height:100%;width:100%;-webkit-user-select:none;user-select:none}.ag-group-header-main{display:flex;align-items:center;gap:6px;padding:0 10px;flex:1;min-width:0;cursor:pointer}.ag-group-header-main:hover{background:var(--agrid-color-bg-subtle)}.ag-group-description{font-size:11px;color:var(--agrid-color-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ag-group-actions-btn{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border:none;background:none;border-radius:3px;cursor:pointer;color:var(--agrid-color-text-muted);font-size:14px;padding:0}.ag-group-actions-btn:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text)}.ag-group-icon{font-size:9px;color:var(--agrid-color-text-muted);line-height:1;display:inline-block;transition:transform .15s ease}.ag-group-icon--expanded{transform:rotate(90deg)}.ag-group-label{font-weight:600;font-size:12px;color:var(--agrid-color-text)}.ag-group-count{font-size:11px;color:var(--agrid-color-text-muted);background:var(--agrid-color-bg);border:1px solid var(--agrid-color-border);border-radius:10px;padding:0 7px;line-height:16px}.ag-group-aggregates{display:inline-flex;align-items:center;gap:10px;margin-left:4px;flex-wrap:wrap}.ag-group-aggregate{font-size:11px;color:var(--agrid-color-text);white-space:nowrap}.ag-group-aggregate-col{color:var(--agrid-color-text-muted);margin-right:4px}.ag-group-aggregate-op{color:var(--agrid-color-text-muted);margin-right:3px}.ag-footer{display:grid;flex-shrink:0;background:var(--agrid-color-bg-muted);border-top:2px solid var(--agrid-color-border)}.ag-footer-cell{display:flex;align-items:center;gap:4px;padding:0 6px;height:30px;border-right:1px solid var(--agrid-color-border);box-sizing:border-box;overflow:hidden;white-space:nowrap;font-weight:600;font-size:12px;color:var(--agrid-color-text)}.ag-footer-cell:last-child{border-right:none}.ag-footer-cell--control{background:var(--agrid-color-bg-subtle);border-right:1px solid var(--agrid-color-border)}.ag-footer-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-footer-label{font-size:10px;font-weight:400;color:var(--agrid-color-text-muted);flex-shrink:0}.ag-pagination{display:flex;align-items:center;gap:4px;padding:0 10px;height:34px;flex-shrink:0;border-top:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted);font-size:12px;color:var(--agrid-color-text-muted)}.ag-page-btn{display:flex;align-items:center;justify-content:center;width:24px;height:22px;border:1px solid var(--agrid-color-border);border-radius:3px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font-size:13px;cursor:pointer;padding:0;line-height:1}.ag-page-btn:hover:not(:disabled){background:var(--agrid-color-bg-muted);border-color:var(--agrid-color-text-muted)}.ag-page-btn:disabled{opacity:.35;cursor:default}.ag-page-info{padding:0 6px;font-weight:500;color:var(--agrid-color-text);min-width:48px;text-align:center}.ag-page-count{margin-left:auto;color:var(--agrid-color-text-muted)}.ag-add-row{border-bottom:1px dashed var(--agrid-color-border);color:var(--agrid-color-text-muted);cursor:pointer}.ag-add-row:hover .ag-add-row-label{background:var(--agrid-color-bg-muted)}.ag-add-row--selected .ag-add-row-label{outline:2px solid var(--agrid-color-accent);outline-offset:-2px}.ag-add-row-label{display:flex;align-items:center;gap:4px;height:100%;padding:0 6px;font-size:12px;-webkit-user-select:none;user-select:none}.ag-add-row-icon{font-size:16px;line-height:1;color:var(--agrid-color-text-muted)}.ag-pinned-pane--right{border-left:1px solid var(--agrid-color-border);border-right:none;box-shadow:-3px 0 6px -2px var(--agrid-color-shadow)}.ag-header-cell--pinned{background:var(--agrid-color-bg-muted)}.ag-header-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-header-cell--pinned-first{box-shadow:none}.ag-cell--pinned{background:var(--agrid-color-bg)}.ag-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-cell--pinned-first{box-shadow:none}.ag-row:hover .ag-cell--pinned{background:var(--agrid-color-bg-muted)}.ag-row--selected .ag-cell--pinned,.ag-row .ag-cell--pinned.ag-cell--range-selected,.ag-row .ag-cell--pinned.ag-cell--fill-preview{background:var(--agrid-color-accent-subtle)}.ag-row .ag-cell--pinned.ag-cell--find-match{background:#fff7cc}.ag-row .ag-cell--pinned.ag-cell--find-active{background:#ffe58a}.ag-header-menu-btn{flex-shrink:0;width:16px;height:16px;padding:0;margin-right:2px;background:none;border:1px solid transparent;border-radius:3px;cursor:pointer;font-size:10px;color:var(--agrid-color-text-muted);display:flex;align-items:center;justify-content:center;opacity:1;transition:opacity .12s;line-height:1}.ag-header-menu-btn--active{color:var(--agrid-color-accent);border-color:var(--agrid-color-accent-border);background:var(--agrid-color-accent-subtle)}.ag-header-menu-btn:hover{background:var(--agrid-color-bg);border-color:var(--agrid-color-border)}.ag-toolbar{display:flex;align-items:center;padding:6px 8px;border-bottom:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-subtle)}.ag-quick-filter{width:100%;max-width:260px;height:30px;padding:0 10px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font-size:13px;box-sizing:border-box}.ag-quick-filter:focus{outline:none;border-color:var(--agrid-color-accent-border)}\n"] }]
|
|
6008
|
-
}], ctorParameters: () => [], propDecorators: { provider: [{ type: i0.Input, args: [{ isSignal: true, alias: "provider", required: false }] }], cellEdit: [{ type: i0.Output, args: ["cellEdit"] }], recordEdit: [{ type: i0.Output, args: ["recordEdit"] }], rowRemoved: [{ type: i0.Output, args: ["rowRemoved"] }], prepareAddRecord: [{ type: i0.Output, args: ["prepareAddRecord"] }], rowReorder: [{ type: i0.Output, args: ["rowReorder"] }], rowSelect: [{ type: i0.Output, args: ["rowSelect"] }], rowDoubleClicked: [{ type: i0.Output, args: ["rowDoubleClicked"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], rowChanged: [{ type: i0.Output, args: ["rowChanged"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], quickFilterChange: [{ type: i0.Output, args: ["quickFilterChange"] }], validationFailed: [{ type: i0.Output, args: ["validationFailed"] }], viewport: [{ type: i0.ViewChild, args: ['scrollViewport', { isSignal: true }] }], pinnedViewport: [{ type: i0.ViewChild, args: ['pinnedViewport', { isSignal: true }] }], rightPinnedViewport: [{ type: i0.ViewChild, args: ['rightPinnedViewport', { isSignal: true }] }], wrapperEl: [{ type: i0.ViewChild, args: ['wrapper', { isSignal: true }] }], horizontalScrollerEl: [{ type: i0.ViewChild, args: ['horizontalScroller', { isSignal: true }] }] } });
|
|
7248
|
+
}, template: "<div #wrapper class=\"ag-wrapper\" tabindex=\"0\" role=\"grid\" [attr.aria-label]=\"localeText().grid\"\n [attr.aria-rowcount]=\"ariaRowCount()\" [attr.aria-colcount]=\"ariaColCount()\"\n [attr.aria-multiselectable]=\"rowSelection() === 'multi' ? 'true' : null\"\n [attr.aria-readonly]=\"readonlyGrid() ? 'true' : null\" [attr.aria-busy]=\"loading() ? 'true' : null\"\n (copy)=\"onCopy($event)\" (paste)=\"onPaste($event)\" (focusin)=\"onGridFocusIn($event)\"\n (click)=\"closeContextMenu(); closeCellContextMenu(); closeFilterMenu(); closeGroupActionsMenu(); closeMenuBarMenu()\">\n @if (visibleMenuBarItems().length > 0 || enableQuickFilter()) {\n <div class=\"ag-toolbar\">\n @if (visibleMenuBarItems().length > 0) {\n <agrid-menu-bar [items]=\"visibleMenuBarItems()\" [context]=\"menuBarContext()\"\n [label]=\"localeText().actions\" [openItemId]=\"openMenuBarItemId()\"\n (openItemIdChange)=\"onMenuBarOpenItemChange($event)\" (action)=\"menuBarAction.emit($event)\" />\n }\n @if (enableQuickFilter()) {\n <input class=\"ag-quick-filter\" type=\"search\" [value]=\"quickFilterValue()\" (input)=\"onQuickFilterInput($event)\"\n [placeholder]=\"localeText().quickFilterPlaceholder\" [attr.aria-label]=\"localeText().quickFilterPlaceholder\" />\n }\n </div>\n }\n <div class=\"ag-main-area\">\n <div class=\"ag-grid-split\" [class.ag-has-right-pane]=\"hasRightPinnedPane()\">\n @if (hasPinnedPane()) {\n <div class=\"ag-pinned-pane\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups ag-header-groups--pinned\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (showControlColumn()) {\n <div class=\"ag-header-group-cell ag-header-group-cell--empty\"></div>\n }\n @for (run of pinnedHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\" [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header ag-header--pinned\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (showControlColumn()) {\n <div class=\"ag-header-cell ag-control-header\" role=\"columnheader\" aria-colindex=\"1\">\n <div class=\"ag-header-cell-top\"></div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\"></div>\n }\n </div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-header-cell ag-header-cell--pinned\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-header-cell--pinned-last]=\"isLastPinnedColumn(col.field)\" [attr.data-col-field]=\"col.field\"\n (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\" (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n @if (getMenuFilterType(col.field)) {\n <button type=\"button\" class=\"ag-filter-condition-btn\"\n [class.ag-filter-condition-btn--active]=\"!!getMenuOperator(col.field)\"\n [title]=\"localeText().filterConditionMenu\"\n [attr.aria-label]=\"localeText().filterConditionMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">{{ getConditionButtonLabel(col.field) }}</button>\n }\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\" (pointerdown)=\"$event.stopPropagation()\"\n (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <ng-template #leftRow let-item let-ariaIndex=\"ariaIndex\">\n <div role=\"row\" [attr.aria-rowindex]=\"ariaIndex\"\n [attr.data-control]=\"true\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item) && !isDetailRowItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-add-row--selected]=\"item === null && isAddRowSelected()\" [class.ag-ghost-row]=\"item === 'ghost'\"\n [class.ag-group-header-row]=\"isGroupHeaderItem(item)\" [class.ag-detail-row]=\"isDetailRowItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isPinnedPaneRowSelected(item)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [class]=\"isDataRowItem(item) ? getRowClass(item.row, item.originalIndex) : ''\"\n [attr.data-original-index]=\"getItemOriginalIndex(item)\" [style.height.px]=\"rowPx(item)\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? pinnedGridTemplateColumns() : null\"\n (pointerdown)=\"isDataRowItem(item) && onRowPointerDown($event, item.originalIndex)\"\n (click)=\"isDataRowItem(item) && onRowClick($event, item)\">\n @if (isDataRowItem(item)) {\n @if (showControlColumn()) {\n <div class=\"ag-control-cell\" role=\"rowheader\" aria-colindex=\"1\"\n [class.ag-control-cell--reorder]=\"allowRowReorder()\"\n (contextmenu)=\"onControlContextMenu($event, item.originalIndex)\" (click)=\"$event.stopPropagation()\"\n (pointerdown)=\"onControlPointerDown($event, item.originalIndex)\">\n @if (canToggleDetail(item)) {\n <button class=\"ag-detail-toggle\" type=\"button\"\n [class.ag-detail-toggle--expanded]=\"isDetailExpanded(item.originalIndex)\"\n [attr.aria-expanded]=\"isDetailExpanded(item.originalIndex)\"\n [attr.aria-label]=\"localeText().toggleDetail\" (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"onDetailToggle(item.originalIndex); $event.stopPropagation()\">\u25B6</button>\n }\n @if (enableRowMarking()) {\n <input class=\"ag-row-marker\" type=\"checkbox\" [checked]=\"isRowMarked(item.originalIndex)\"\n [attr.aria-label]=\"localeText().markRow\" (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation()\" (change)=\"toggleRowMarked(item.originalIndex)\" />\n }\n </div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [displayValueOverride]=\"treeCellDisplayOverride(item, col)\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\" [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\"\n [editable]=\"isColEditable(col, item.originalIndex)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [showInfoIcon]=\"showCellInfoIcon(col, item.row)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\" [selectTextOnEdit]=\"selectTextOnEdit()\"\n [treeCell]=\"isTreeCell(col)\"\n [treeLevel]=\"treeRowLevel(item)\" [treeExpandable]=\"treeRowExpandable(item)\"\n [treeExpanded]=\"treeRowExpanded(item)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class]=\"getCellClass(col, item.row[col.field], item.row)\" [class.ag-cell--pinned]=\"true\"\n [class.ag-cell--pinned-last]=\"isLastPinnedColumn(col.field)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\" (startEdit)=\"onStartEdit(item.originalIndex, ci)\"\n (draftChange)=\"onDraftChange($event)\" (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\"\n (infoClick)=\"onCellInfo(item.originalIndex, col, item.row)\"\n (treeToggle)=\"onTreeToggle(item)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-pinned-row-spacer\"></div>\n } @else if (item === 'ghost') {\n @if (showControlColumn()) {\n <div class=\"ag-control-cell ag-control-cell--reorder ag-ghost-handle\"></div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-ghost-cell ag-cell--pinned\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--pinned-last]=\"isLastPinnedColumn(col.field)\">{{\n getGhostCellDisplay(col) }}</div>\n }\n } @else {\n <div class=\"ag-pinned-row-spacer\"></div>\n }\n </div>\n </ng-template>\n\n @if (hasPinnedTopRows()) {\n <div class=\"ag-pinned-rows ag-pinned-rows--top ag-pinned-body\" [style.width.px]=\"pinnedPaneWidth()\">\n @for (item of pinnedTopItems(); track item.originalIndex) {\n <ng-container [ngTemplateOutlet]=\"leftRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: headerRowCount() + 1 + $index }\" />\n }\n </div>\n }\n\n <cdk-virtual-scroll-viewport #pinnedViewport class=\"ag-body ag-pinned-body\" [agridVariableRowSize]=\"itemSizes()\"\n [style.width.px]=\"pinnedPaneWidth()\">\n <ng-container *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\">\n <ng-container [ngTemplateOutlet]=\"leftRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: di + headerRowCount() + 1 + pinnedTopItems().length }\" />\n </ng-container>\n </cdk-virtual-scroll-viewport>\n\n @if (hasPinnedBottomRows()) {\n <div class=\"ag-pinned-rows ag-pinned-rows--bottom ag-pinned-body\" [style.width.px]=\"pinnedPaneWidth()\">\n @for (item of pinnedBottomItems(); track item.originalIndex) {\n <ng-container [ngTemplateOutlet]=\"leftRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: headerRowCount() + 1 + pinnedTopItems().length + displayItems().length + $index }\" />\n }\n </div>\n }\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\" [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\">\n @if (showControlColumn()) {\n <div class=\"ag-footer-cell ag-footer-cell--control\" role=\"gridcell\" aria-colindex=\"1\"></div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [class.ag-footer-cell--pinned-last]=\"isLastPinnedColumn(col.field)\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n <div class=\"ag-scroll-pane\">\n <div #horizontalScroller class=\"ag-horizontal-scroll\" (scroll)=\"onHorizontalScroll()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (run of scrollableHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\" [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-header-cell\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.data-col-field]=\"col.field\" (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n @if (isGroupedByField(col.field)) {\n <span class=\"ag-sort-badge\">\u229F</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\" (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n @if (getMenuFilterType(col.field)) {\n <button type=\"button\" class=\"ag-filter-condition-btn\"\n [class.ag-filter-condition-btn--active]=\"!!getMenuOperator(col.field)\"\n [title]=\"localeText().filterConditionMenu\"\n [attr.aria-label]=\"localeText().filterConditionMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">{{ getConditionButtonLabel(col.field) }}</button>\n }\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\" (pointerdown)=\"$event.stopPropagation()\"\n (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <ng-template #bodyRow let-item let-ariaIndex=\"ariaIndex\">\n <div role=\"row\" [attr.aria-rowindex]=\"ariaIndex\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item) && !isDetailRowItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-add-row--selected]=\"item === null && isAddRowSelected()\" [class.ag-ghost-row]=\"item === 'ghost'\"\n [class.ag-group-header-row]=\"isGroupHeaderItem(item)\" [class.ag-detail-row]=\"isDetailRowItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [class]=\"isDataRowItem(item) ? getRowClass(item.row, item.originalIndex) : ''\"\n [attr.data-original-index]=\"getItemOriginalIndex(item)\" [style.height.px]=\"rowPx(item)\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? scrollableGridTemplateColumns() : null\"\n (pointerdown)=\"isDataRowItem(item) && onRowPointerDown($event, item.originalIndex)\"\n (click)=\"isDataRowItem(item) && onRowClick($event, item)\">\n @if (isDataRowItem(item)) {\n @for (col of scrollableColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [displayValueOverride]=\"treeCellDisplayOverride(item, col)\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\" [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\"\n [editable]=\"isColEditable(col, item.originalIndex)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [showInfoIcon]=\"showCellInfoIcon(col, item.row)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\" [selectTextOnEdit]=\"selectTextOnEdit()\"\n [treeCell]=\"isTreeCell(col)\"\n [treeLevel]=\"treeRowLevel(item)\" [treeExpandable]=\"treeRowExpandable(item)\"\n [treeExpanded]=\"treeRowExpanded(item)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class]=\"getCellClass(col, item.row[col.field], item.row)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\"\n (startEdit)=\"onStartEdit(item.originalIndex, ci)\" (draftChange)=\"onDraftChange($event)\"\n (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\" (treeToggle)=\"onTreeToggle(item)\"\n (infoClick)=\"onCellInfo(item.originalIndex, col, item.row)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-add-row-label\" (click)=\"onActivateAddRow()\">\n <span class=\"ag-add-row-icon\">+</span> {{ localeText().addRow }}\n </div>\n } @else if (item === 'ghost') {\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-ghost-cell\" [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\">{{\n getGhostCellDisplay(col) }}</div>\n }\n } @else if (isDetailRowItem(item)) {\n <div class=\"ag-detail-panel\" [style.height.px]=\"detailRowHeight()\"\n [style.min-width.px]=\"scrollableTotalWidth()\" [innerHTML]=\"detailHtml(item)\"></div>\n } @else if (isPathTreeNodeItem(item)) {\n <div class=\"ag-path-tree-content\" [style.min-width.px]=\"scrollableTotalWidth()\"\n [style.padding-left.px]=\"treeRowLevel(item) * 16 + 8\"\n (click)=\"onTreeNodeClick(item); $event.stopPropagation()\"\n (dblclick)=\"onTreeNodeDoubleClick(item); $event.stopPropagation()\">\n <button type=\"button\" class=\"ag-tree-twisty\"\n [class.ag-tree-twisty--expanded]=\"treeRowExpanded(item)\"\n [attr.aria-expanded]=\"treeRowExpanded(item)\"\n (click)=\"onTreeToggle(item); $event.stopPropagation()\">\u25B6</button>\n <span>{{ pathTreeLabel(item) }}</span>\n </div>\n } @else {\n <div class=\"ag-group-header-content\" [style.min-width.px]=\"scrollableTotalWidth()\">\n <div class=\"ag-group-header-main\"\n (click)=\"onGroupHeaderClick(item.groupLabel); $event.stopPropagation()\">\n <span class=\"ag-group-icon\" [class.ag-group-icon--expanded]=\"!item.collapsed\">\u25B6</span>\n <span class=\"ag-group-label\">{{ item.groupLabel }}</span>\n @if (groupActions().length > 0) {\n <button class=\"ag-group-actions-btn\" [title]=\"localeText().actions\"\n (click)=\"openGroupActionsMenu($event, item.groupLabel)\">\u22EE</button>\n }\n <span class=\"ag-group-count\">{{ item.count }}</span>\n @if (getGroupDescription(item.groupLabel); as desc) {\n <span class=\"ag-group-description\">{{ desc }}</span>\n }\n @if (item.aggregates; as aggs) {\n <span class=\"ag-group-aggregates\">\n @for (col of visibleColDefs(); track col.field) {\n @if (hasAggregate(col) && aggs[col.field] !== undefined) {\n <span class=\"ag-group-aggregate\">\n <span class=\"ag-group-aggregate-col\">{{ col.header }}</span>\n <span class=\"ag-group-aggregate-op\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, aggs[col.field]) }}\n </span>\n }\n }\n </span>\n }\n </div>\n </div>\n }\n @if (isDataRowItem(item) && isRowPendingDelete(item.originalIndex)) {\n <div class=\"ag-delete-confirmation\" role=\"alertdialog\" [attr.aria-label]=\"localeText().confirmDeleteRow\"\n [style.left.px]=\"deleteConfirmationLeft()\" [style.width.px]=\"deleteConfirmationWidth() || null\"\n (pointerdown)=\"$event.stopPropagation()\" (click)=\"$event.stopPropagation()\">\n <span class=\"ag-delete-confirmation-text\">{{ localeText().confirmDeleteRow }}</span>\n <button class=\"ag-delete-confirmation-btn ag-delete-confirmation-btn--yes\" type=\"button\"\n (click)=\"confirmPendingRowDelete()\">{{ localeText().confirmYes }}</button>\n <button class=\"ag-delete-confirmation-btn\" type=\"button\" data-delete-confirm-no\n (click)=\"cancelRowDelete()\">{{ localeText().confirmNo }}</button>\n </div>\n }\n </div>\n </ng-template>\n\n @if (hasPinnedTopRows()) {\n <div class=\"ag-pinned-rows ag-pinned-rows--top\" [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (item of pinnedTopItems(); track item.originalIndex) {\n <ng-container [ngTemplateOutlet]=\"bodyRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: headerRowCount() + 1 + $index }\" />\n }\n </div>\n }\n\n <cdk-virtual-scroll-viewport #scrollViewport class=\"ag-body\" [agridVariableRowSize]=\"itemSizes()\"\n [style.min-width.px]=\"scrollableTotalWidth()\" (scroll)=\"onBodyScroll()\">\n <ng-container *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\">\n <ng-container [ngTemplateOutlet]=\"bodyRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: di + headerRowCount() + 1 + pinnedTopItems().length }\" />\n </ng-container>\n </cdk-virtual-scroll-viewport>\n\n @if (hasPinnedBottomRows()) {\n <div class=\"ag-pinned-rows ag-pinned-rows--bottom\" [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (item of pinnedBottomItems(); track item.originalIndex) {\n <ng-container [ngTemplateOutlet]=\"bodyRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: headerRowCount() + 1 + pinnedTopItems().length + displayItems().length + $index }\" />\n }\n </div>\n }\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\" [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if (hasRightPinnedPane()) {\n <div class=\"ag-pinned-pane ag-pinned-pane--right\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @for (run of rightHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\" [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-header-cell ag-header-cell--pinned\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-header-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\" [attr.data-col-field]=\"col.field\"\n (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n @if (isGroupedByField(col.field)) {\n <span class=\"ag-sort-badge\">'\u229F '</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\" (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n @if (getMenuFilterType(col.field)) {\n <button type=\"button\" class=\"ag-filter-condition-btn\"\n [class.ag-filter-condition-btn--active]=\"!!getMenuOperator(col.field)\"\n [title]=\"localeText().filterConditionMenu\"\n [attr.aria-label]=\"localeText().filterConditionMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">{{ getConditionButtonLabel(col.field) }}</button>\n }\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\" (pointerdown)=\"$event.stopPropagation()\"\n (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <ng-template #rightRow let-item let-ariaIndex=\"ariaIndex\">\n <div role=\"row\" [attr.aria-rowindex]=\"ariaIndex\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item) && !isDetailRowItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-ghost-row]=\"item === 'ghost'\" [class.ag-group-header-row]=\"isGroupHeaderItem(item)\"\n [class.ag-detail-row]=\"isDetailRowItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [class]=\"isDataRowItem(item) ? getRowClass(item.row, item.originalIndex) : ''\"\n [style.height.px]=\"rowPx(item)\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? rightGridTemplateColumns() : null\">\n @if (isDataRowItem(item)) {\n @for (col of rightPinnedColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [displayValueOverride]=\"treeCellDisplayOverride(item, col)\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\" [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\"\n [editable]=\"isColEditable(col, item.originalIndex)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [showInfoIcon]=\"showCellInfoIcon(col, item.row)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\" [selectTextOnEdit]=\"selectTextOnEdit()\"\n [treeCell]=\"isTreeCell(col)\"\n [treeLevel]=\"treeRowLevel(item)\" [treeExpandable]=\"treeRowExpandable(item)\"\n [treeExpanded]=\"treeRowExpanded(item)\" [class]=\"getCellClass(col, item.row[col.field], item.row)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--pinned]=\"true\" [class.ag-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\" (startEdit)=\"onStartEdit(item.originalIndex, ci)\"\n (draftChange)=\"onDraftChange($event)\" (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\"\n (infoClick)=\"onCellInfo(item.originalIndex, col, item.row)\"\n (treeToggle)=\"onTreeToggle(item)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-pinned-row-spacer\"></div>\n } @else if (item === 'ghost') {\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-ghost-cell ag-cell--pinned\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\">{{ getGhostCellDisplay(col) }}</div>\n }\n } @else {\n <div class=\"ag-pinned-row-spacer\"></div>\n }\n </div>\n </ng-template>\n\n @if (hasPinnedTopRows()) {\n <div class=\"ag-pinned-rows ag-pinned-rows--top ag-pinned-body ag-right-pinned-body\"\n [style.width.px]=\"rightPinnedPaneWidth()\">\n @for (item of pinnedTopItems(); track item.originalIndex) {\n <ng-container [ngTemplateOutlet]=\"rightRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: headerRowCount() + 1 + $index }\" />\n }\n </div>\n }\n\n <cdk-virtual-scroll-viewport #rightPinnedViewport class=\"ag-body ag-pinned-body ag-right-pinned-body\"\n [agridVariableRowSize]=\"itemSizes()\" [style.width.px]=\"rightPinnedPaneWidth()\"\n (scroll)=\"onRightPinnedBodyScroll()\">\n <ng-container *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\">\n <ng-container [ngTemplateOutlet]=\"rightRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: di + headerRowCount() + 1 + pinnedTopItems().length }\" />\n </ng-container>\n </cdk-virtual-scroll-viewport>\n\n @if (hasPinnedBottomRows()) {\n <div class=\"ag-pinned-rows ag-pinned-rows--bottom ag-pinned-body ag-right-pinned-body\"\n [style.width.px]=\"rightPinnedPaneWidth()\">\n @for (item of pinnedBottomItems(); track item.originalIndex) {\n <ng-container [ngTemplateOutlet]=\"rightRow\"\n [ngTemplateOutletContext]=\"{ $implicit: item, ariaIndex: headerRowCount() + 1 + pinnedTopItems().length + displayItems().length + $index }\" />\n }\n </div>\n }\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\" [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\">\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [class.ag-footer-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n\n @if (loading()) {\n <div class=\"ag-state-overlay\" role=\"status\" aria-live=\"polite\">{{ localeText().loading }}</div>\n } @else if (isEmpty()) {\n <div class=\"ag-state-overlay\" role=\"status\" aria-live=\"polite\">{{ emptyTextLabel() }}</div>\n }\n\n @if (showSidebar()) {\n <agrid-sidebar [open]=\"sidebarOpen()\" [activeTab]=\"sidebarTab()\" [columns]=\"colDefs()\"\n [headerGroups]=\"headerGroups()\" [row]=\"sidebarRow()\" [rowIndex]=\"selectedRowIndex()\"\n [hiddenColumns]=\"sidebarHiddenColumns()\" [locale]=\"locale()\"\n [localeText]=\"localeText()\" [readonlyGrid]=\"readonlyGrid()\" [useSidebarEditor]=\"useSidebarEditor()\"\n [isCellEditable]=\"isCellEditableForRow\"\n [errors]=\"sidebarValidationErrors()\" (close)=\"toggleSidebar()\" (tabChange)=\"onSidebarStripClick($event)\"\n (toggleColumn)=\"onSidebarToggleColumn($event)\"\n (toggleColumnGroup)=\"onSidebarToggleColumnGroup($event.fields, $event.visible)\"\n (detailEdit)=\"onSidebarDetailEdit($event)\" (save)=\"onSidebarDetailSave($event)\" />\n }\n </div><!-- /.ag-main-area -->\n\n @if (showPagination()) {\n <nav class=\"ag-pagination\" [attr.aria-label]=\"localeText().pagination\">\n <button class=\"ag-page-btn\" [attr.aria-label]=\"localeText().firstPage\" [disabled]=\"control()!.currentPage() <= 1\"\n (click)=\"goToFirstPage()\">\u00AB</button>\n <button class=\"ag-page-btn\" [attr.aria-label]=\"localeText().previous\" [disabled]=\"control()!.currentPage() <= 1\"\n (click)=\"goToPrevPage()\">\u2039</button>\n <span class=\"ag-page-info\" aria-live=\"polite\">{{ control()!.currentPage() }} / {{ totalPages() }}</span>\n <button class=\"ag-page-btn\" [disabled]=\"control()!.currentPage() >= totalPages()\"\n [attr.aria-label]=\"localeText().next\" (click)=\"goToNextPage()\">\u203A</button>\n <button class=\"ag-page-btn\" [disabled]=\"control()!.currentPage() >= totalPages()\"\n [attr.aria-label]=\"localeText().lastPage\" (click)=\"goToLastPage()\">\u00BB</button>\n <span class=\"ag-page-count\">{{ localeText().rows(filteredRowCount()) }}</span>\n </nav>\n }\n\n @if (findOpen()) {\n <agrid-find-panel [query]=\"findQuery()\" [matchCount]=\"findMatches().length\" [activeIndex]=\"findActiveIndex()\"\n [localeText]=\"localeText()\" (queryChange)=\"onFindInput($event)\" (previous)=\"goToFindMatch(-1)\"\n (next)=\"goToFindMatch(1)\" (close)=\"closeFind()\" />\n }\n\n <!-- Row context menu -->\n @if (contextMenu(); as menu) {\n @if(hasContextMenuEntries()) {\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n @if (!treeConfig()) {\n @if (rowPinState(menu.rowIndex)) {\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onPinRow(menu.rowIndex, null)\">{{ localeText().unpinRow\n }}</button>\n } @else {\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onPinRow(menu.rowIndex, 'top')\">{{ localeText().pinRowTop\n }}</button>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onPinRow(menu.rowIndex, 'bottom')\">{{\n localeText().pinRowBottom }}</button>\n }\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n }\n @if (!readonlyGrid()) {\n <button class=\"ag-context-item ag-context-item--danger\" role=\"menuitem\" (click)=\"deleteRow(menu.rowIndex)\">\n {{ localeText().deleteRow }}\n </button>\n }\n </div>\n }\n }\n\n <!-- Cell context menu -->\n @if (cellContextMenuState(); as menu) {\n @let col = getColDef(menu.field)!;\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"copyCellToClipboard(menu.rowIndex, col)\">{{\n localeText().copyCellValue\n }}</button>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"copyRowToClipboard(menu.rowIndex)\">{{ localeText().copyRow\n }}</button>\n @if (!treeConfig()) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n @if (rowPinState(menu.rowIndex)) {\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onPinRow(menu.rowIndex, null)\">{{ localeText().unpinRow\n }}</button>\n } @else {\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onPinRow(menu.rowIndex, 'top')\">{{ localeText().pinRowTop\n }}</button>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onPinRow(menu.rowIndex, 'bottom')\">{{\n localeText().pinRowBottom }}</button>\n }\n }\n @if (allowAddRows() && !readonlyGrid()) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"insertRowAt(menu.rowIndex)\">{{ localeText().insertRowAbove\n }}</button>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"insertRowAt(menu.rowIndex + 1)\">{{\n localeText().insertRowBelow }}</button>\n }\n @if (!readonlyGrid()) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n <button class=\"ag-context-item ag-context-item--danger\" role=\"menuitem\" (click)=\"deleteRow(menu.rowIndex)\">{{\n localeText().deleteRow }}</button>\n }\n @if (cellMenuItems().length) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n @for (item of cellMenuItems(); track $index) {\n @if (item === null) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n } @else {\n <button class=\"ag-context-item\" role=\"menuitem\" [class.ag-context-item--danger]=\"item.danger\"\n [disabled]=\"item.disabled\" (click)=\"runCellMenuItem(item, menu)\">{{\n item.label }}</button>\n }\n }\n }\n </div>\n }\n\n <!-- Group actions menu -->\n @if (groupActionsMenu(); as menu) {\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n @for (action of groupActions(); track action.label) {\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onGroupAction(action, menu.label)\">\n {{ action.label }}\n </button>\n }\n </div>\n }\n\n <!-- Filter dropdown -->\n @if (filterMenu(); as menu) {\n <agrid-column-menu [x]=\"menu.x\" [y]=\"menu.y\" [header]=\"getColDef(menu.field)?.header ?? menu.field\"\n [sortDir]=\"getSort(menu.field)\" [sortable]=\"sortOption() !== 'none'\" [showColumnActions]=\"!!control()\"\n [pinned]=\"getColumnPinState(menu.field)\" [groupable]=\"!!getColDef(menu.field)?.groupable\"\n [grouped]=\"isGroupedByField(menu.field)\" [filterable]=\"!!getColDef(menu.field)?.filterable\"\n [showValueFilter]=\"!serverSideFiltering()\" [filterType]=\"getMenuFilterType(menu.field)\"\n [operator]=\"getMenuOperator(menu.field)\" [operand]=\"getMenuOperand(menu.field)\"\n [operand2]=\"getMenuOperand2(menu.field)\" (operatorChange)=\"onMenuOperatorChange(menu.field, $event)\"\n (operandChange)=\"onMenuOperandChange(menu.field, $event)\"\n (operand2Change)=\"onMenuOperand2Change(menu.field, $event)\" [search]=\"filterMenuSearch()\"\n [allSelected]=\"isMenuAllSelected(menu.field)\"\n [valueItems]=\"serverSideFiltering() || !getColDef(menu.field)?.filterable ? [] : columnMenuValueItems()\"\n [localeText]=\"localeText()\" [sortPriority]=\"getSortPriority(menu.field)\" [hasMultiSort]=\"hasMultiSort()\"\n (sort)=\"onMenuSort(menu.field, $event)\" (resetSort)=\"onMenuResetSort(menu.field, $event)\"\n (autosize)=\"onMenuAutosizeColumn(menu.field)\" (togglePin)=\"onMenuTogglePin(menu.field)\"\n (togglePinRight)=\"onMenuTogglePinRight(menu.field)\" (hide)=\"onMenuHideColumn(menu.field)\"\n (toggleGroup)=\"onMenuToggleGroupBy(menu.field)\" (clearFilter)=\"onMenuClearFilter(menu.field)\"\n (clearAll)=\"onMenuClearAll()\" (searchChange)=\"onFilterMenuSearch($event)\" (toggleAll)=\"onMenuToggleAll(menu.field)\"\n (toggleValue)=\"onMenuToggleValue(menu.field, $event)\" [aggregate]=\"getEffectiveAggregate(getColDef(menu.field)!)\"\n (setAggregate)=\"onMenuSetAggregate(menu.field, $event)\" />\n }\n\n @if (columnDragPreview(); as preview) {\n <div class=\"ag-column-drag-preview\" [style.left.px]=\"preview.x\" [style.top.px]=\"preview.y\"\n [style.width.px]=\"preview.width\" [style.height.px]=\"preview.height\">\n <span>{{ preview.label }}</span>\n </div>\n }\n</div>\n", styles: ["@layer agrid-defaults{:host{--agrid-color-text: #24292f;--agrid-color-text-muted: #57606a;--agrid-color-accent: #1a73e8;--agrid-color-accent-subtle: #e8f0fe;--agrid-color-accent-fg: #1558b0;--agrid-color-accent-border: #c8d8f8;--agrid-color-danger: #d1242f;--agrid-color-danger-subtle: #fff1f0;--agrid-color-border: #d0d7de;--agrid-color-bg: #ffffff;--agrid-color-bg-subtle: #fafbfc;--agrid-color-bg-muted: #f6f8fa;--agrid-color-shadow: rgba(140, 149, 159, .2);--agrid-color-bg-stripe: #f0f2f5;--agrid-color-cell-changed: #f59e0b;--agrid-color-row-marked: #fff8c5}}:host{display:flex;flex-direction:column;min-height:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:13px;color:var(--agrid-color-text)}.ag-wrapper{position:relative;display:flex;flex-direction:column;flex:1;min-height:0;border:1px solid var(--agrid-color-border);overflow:hidden;outline:none;border-radius:4px}.ag-state-overlay{position:absolute;inset:34px 0 0;display:flex;align-items:center;justify-content:center;color:var(--agrid-color-text-muted);background:color-mix(in srgb,var(--agrid-color-bg) 78%,transparent);pointer-events:none;z-index:3}.ag-header{display:grid;flex-shrink:0;background:var(--agrid-color-bg-muted);border-bottom:2px solid var(--agrid-color-border)}.ag-header-groups{display:grid;flex-shrink:0;height:28px;background:color-mix(in srgb,var(--agrid-color-bg-muted) 72%,var(--agrid-color-bg));border-bottom:1px solid var(--agrid-color-border)}.ag-header-group-cell{display:flex;align-items:center;justify-content:center;min-width:0;padding:0 8px;overflow:hidden;border-right:1px solid var(--agrid-color-border);box-sizing:border-box;color:var(--agrid-color-text);font-size:12px;font-weight:600;text-overflow:ellipsis;white-space:nowrap;cursor:grab;-webkit-user-select:none;user-select:none;transition:opacity .1s ease}.ag-header-group-cell:last-child{border-right:none}.ag-header-group-cell--empty,.ag-header-group-cell--locked{cursor:default}.ag-header-group-cell--dragging{opacity:.12}.ag-header-cell{position:relative;display:flex;align-items:center;font-weight:600;border-right:1px solid var(--agrid-color-border);overflow:hidden;white-space:nowrap;-webkit-user-select:none;user-select:none;box-sizing:border-box}.ag-header-cell:last-child{border-right:none}.ag-header-cell-top{display:flex;align-items:center;flex:1;min-width:0;padding:0 6px;height:32px;overflow:hidden;white-space:nowrap}.ag-header-cell-label{overflow:hidden;text-overflow:ellipsis;flex:1}.ag-header--with-filters .ag-header-cell{flex-direction:column;align-items:stretch;height:auto;white-space:normal}.ag-header--with-filters .ag-header-cell-top{flex:0 0 32px}.ag-header-cell-filter{height:28px;display:flex;align-items:center;padding:0 2px;border-top:1px solid var(--agrid-color-border);background:var(--agrid-color-bg)}.ag-header-cell{cursor:grab;transition:transform .16s cubic-bezier(.2,0,0,1),opacity .1s ease;will-change:transform}.ag-row>agrid-cell,.ag-ghost-cell,.ag-footer-cell{transition:transform .16s cubic-bezier(.2,0,0,1),opacity .1s ease;will-change:transform}.ag-header-cell--dragging{opacity:.12;cursor:grabbing}.ag-column-reorder-item--dragging{opacity:.12}.ag-header-cell--drop-before,.ag-header-cell--drop-after{z-index:1}.ag-column-drag-preview{position:fixed;z-index:1000;display:flex;align-items:flex-start;padding:8px 10px;border:1px solid var(--agrid-color-accent);border-radius:5px;color:var(--agrid-color-text);background:color-mix(in srgb,var(--agrid-color-bg) 94%,var(--agrid-color-accent));box-shadow:0 10px 28px var(--agrid-color-shadow);font-weight:600;line-height:16px;box-sizing:border-box;pointer-events:none;opacity:.96}.ag-sort-badge{font-size:11px;color:var(--agrid-color-accent);flex-shrink:0;line-height:1}.ag-sort-priority{font-size:9px;vertical-align:super;opacity:.75}.ag-resize-handle{position:absolute;top:0;right:0;width:5px;height:100%;cursor:col-resize;z-index:1}.ag-resize-handle:hover{background:var(--agrid-color-accent);opacity:.5}.ag-filter-input{flex:1;min-width:0;height:20px;border:1px solid var(--agrid-color-border);border-radius:3px;outline:none;font:inherit;font-size:12px;padding:0 4px;background:var(--agrid-color-bg)}.ag-filter-condition-btn{flex:0 0 24px;height:20px;padding:0;border:1px solid var(--agrid-color-border);border-radius:3px;background:var(--agrid-color-bg-subtle);color:var(--agrid-color-text-muted);cursor:pointer}.ag-filter-condition-btn:hover,.ag-filter-condition-btn:focus-visible{border-color:var(--agrid-color-accent-border);color:var(--agrid-color-accent-fg)}.ag-filter-condition-btn--active{border-color:var(--agrid-color-accent-border);background:var(--agrid-color-accent-subtle);color:var(--agrid-color-accent-fg)}.ag-filter-input:focus{border-color:var(--agrid-color-accent)}.ag-header-cell--filtered .ag-header-cell-label:after{content:\" \\25be\";font-size:9px;color:var(--agrid-color-accent)}.ag-main-area{display:flex;flex:1;min-height:0;overflow:hidden}.ag-grid-split{display:flex;flex:1;min-width:0;min-height:0;overflow:hidden}.ag-pinned-pane{flex-shrink:0;min-height:0;display:flex;flex-direction:column;overflow:hidden;border-right:1px solid var(--agrid-color-border);background:var(--agrid-color-bg)}.ag-scroll-pane{flex:1;min-width:0;min-height:0;display:flex}.ag-horizontal-scroll{flex:1;min-width:0;min-height:0;overflow-x:auto;overflow-y:hidden;display:flex;flex-direction:column}.ag-body{flex:1;overflow-x:clip;overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-horizontal-scroll{scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-body::-webkit-scrollbar,.ag-horizontal-scroll::-webkit-scrollbar{width:8px;height:8px}.ag-body::-webkit-scrollbar-track,.ag-horizontal-scroll::-webkit-scrollbar-track{background:transparent}.ag-body::-webkit-scrollbar-thumb,.ag-horizontal-scroll::-webkit-scrollbar-thumb{background:#0000002e;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-body::-webkit-scrollbar-thumb:hover,.ag-horizontal-scroll::-webkit-scrollbar-thumb:hover{background:#00000052;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-pinned-body{overflow:hidden}.ag-right-pinned-body{overflow-y:auto}.ag-has-right-pane .ag-body:not(.ag-pinned-body){scrollbar-width:none}.ag-has-right-pane .ag-body:not(.ag-pinned-body)::-webkit-scrollbar{display:none}.ag-row{display:grid;position:relative}.ag-row--pending-delete>:not(.ag-delete-confirmation){opacity:.2;pointer-events:none;transition:opacity .14s ease}.ag-delete-confirmation{position:absolute;top:0;bottom:0;z-index:7;display:flex;align-items:center;justify-content:center;gap:8px;min-width:min(100%,320px);padding:2px 12px;box-sizing:border-box;color:var(--agrid-color-text);background:color-mix(in srgb,var(--agrid-color-danger-subtle) 88%,transparent);border-block:1px solid color-mix(in srgb,var(--agrid-color-danger) 35%,transparent)}.ag-delete-confirmation-text{font-weight:600;white-space:nowrap}.ag-delete-confirmation-btn{min-width:42px;padding:3px 9px;border:1px solid var(--agrid-color-border);border-radius:4px;color:var(--agrid-color-text);background:var(--agrid-color-bg);font:inherit;cursor:pointer}.ag-delete-confirmation-btn:hover,.ag-delete-confirmation-btn:focus-visible{border-color:var(--agrid-color-accent);outline:none}.ag-delete-confirmation-btn--yes{color:#fff;border-color:var(--agrid-color-danger);background:var(--agrid-color-danger)}.ag-delete-confirmation-btn--yes:hover,.ag-delete-confirmation-btn--yes:focus-visible{border-color:color-mix(in srgb,var(--agrid-color-danger) 75%,#000);background:color-mix(in srgb,var(--agrid-color-danger) 85%,#000)}:host(.ag-zebra) .ag-row--odd agrid-cell:not(.editing){background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd .ag-cell--pinned{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd .ag-control-cell{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd:hover agrid-cell:not(.editing){background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd:hover .ag-cell--pinned,:host(.ag-zebra) .ag-row--odd:hover .ag-control-cell{background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd.ag-row--selected agrid-cell:not(.editing){background:var(--agrid-color-accent-subtle)}:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-cell--pinned,:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-control-cell{background:var(--agrid-color-accent-subtle)}:host(.ag-zebra) .ag-row--odd .ag-cell--pinned-first{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd:hover .ag-cell--pinned-first{background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-cell--pinned-first{background:var(--agrid-color-accent-subtle)}.ag-row:hover agrid-cell:not(.editing){background:var(--agrid-color-bg-muted)}.ag-row--selected agrid-cell:not(.editing){background:var(--agrid-color-accent-subtle)}.ag-row--marked agrid-cell:not(.editing),.ag-row--marked .ag-control-cell{background:var(--agrid-color-row-marked)}.ag-row--marked{box-shadow:inset 3px 0 #bf8700}.ag-row--selected.ag-row--marked{box-shadow:inset 3px 0 0 var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--range-selected:not(.editing){background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1px var(--agrid-color-accent-border)}.ag-row agrid-cell.ag-cell--find-match:not(.editing){background:#fff7cc}.ag-row agrid-cell.ag-cell--find-active:not(.editing){background:#ffe58a;box-shadow:inset 0 0 0 2px #b7791f}.ag-row agrid-cell.ag-cell--range-selected.selected:not(.editing){box-shadow:inset 0 0 0 2px var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--fill-preview:not(.editing){background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1px var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--fill-handle:not(.editing){position:relative}.ag-row agrid-cell.ag-cell--fill-handle:not(.editing):after{content:\"\";position:absolute;right:1px;bottom:1px;width:6px;height:6px;background:var(--agrid-color-accent);border:1px solid var(--agrid-color-bg);box-sizing:border-box;cursor:crosshair;z-index:4}.ag-row--selected .ag-control-cell{background:var(--agrid-color-accent-subtle)}.ag-row--selected{box-shadow:inset 3px 0 0 var(--agrid-color-accent)}.ag-control-header{z-index:3;border-right:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted)}.ag-control-header .ag-header-cell-filter{background:var(--agrid-color-bg-subtle)}.ag-control-cell{z-index:2;border-right:1px solid var(--agrid-color-border);border-bottom:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-subtle);cursor:context-menu;display:flex;align-items:center;justify-content:center;gap:6px;box-sizing:border-box;flex-direction:row-reverse}.ag-control-cell:after{content:\"\\22ee\";font-size:11px;color:var(--agrid-color-border);line-height:1}.ag-control-cell:hover:after{color:var(--agrid-color-text-muted)}.ag-control-cell--reorder{cursor:grab}.ag-control-cell--reorder:after{content:\"\\283f\";font-size:13px;color:var(--agrid-color-text)}.ag-control-cell--reorder:hover:after{color:var(--agrid-color-text-muted)}.ag-control-cell--reorder:active{cursor:grabbing}.ag-row-marker{position:relative;z-index:1;width:14px;height:14px;margin:0;accent-color:var(--agrid-color-accent);cursor:pointer}.ag-ghost-row{background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1.5px var(--agrid-color-accent);pointer-events:none;animation:ag-ghost-in .1s ease}@keyframes ag-ghost-in{0%{opacity:0}to{opacity:1}}.ag-ghost-handle:after{color:var(--agrid-color-accent)!important}.ag-ghost-cell{display:flex;align-items:center;padding:0 6px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;border-right:1px solid var(--agrid-color-accent-border);color:var(--agrid-color-accent-fg);font-size:13px;box-sizing:border-box;-webkit-user-select:none;user-select:none}.ag-ghost-cell:last-child{border-right:none}.ag-pinned-row-spacer,.ag-pinned-group-spacer{height:100%;border-bottom:1px solid var(--agrid-color-border);box-sizing:border-box}.ag-pinned-group-spacer{background:var(--agrid-color-bg-muted)}.ag-context-menu{position:fixed;z-index:1000;background:var(--agrid-color-bg-subtle);border:1px solid var(--agrid-color-border);border-radius:6px;box-shadow:0 8px 24px var(--agrid-color-shadow);min-width:160px;padding:4px 0;font-size:13px}.ag-context-item{display:block;width:100%;padding:6px 16px;text-align:left;background:none;border:none;cursor:pointer;color:var(--agrid-color-text);font:inherit}.ag-context-item:hover{background:var(--agrid-color-bg-muted)}.ag-context-separator{height:1px;background:var(--agrid-color-border);margin:3px 0}.ag-context-item--danger{color:var(--agrid-color-danger)}.ag-context-item--danger:hover{background:var(--agrid-color-danger-subtle)}.ag-group-header-row{display:flex;align-items:stretch;background:var(--agrid-color-bg-muted);border-bottom:1px solid var(--agrid-color-border);overflow:hidden}.ag-path-tree-content{display:flex;align-items:center;gap:4px;height:100%;box-sizing:border-box;color:var(--agrid-color-text);font-weight:600;border-bottom:1px solid var(--agrid-color-border)}.ag-path-tree-content .ag-tree-twisty{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:0;color:inherit;background:transparent;cursor:pointer;font-size:10px;transition:transform .12s ease}.ag-path-tree-content .ag-tree-twisty--expanded{transform:rotate(90deg)}.ag-group-header-content{display:flex;align-items:stretch;height:100%;width:100%;-webkit-user-select:none;user-select:none}.ag-group-header-main{display:flex;align-items:center;gap:6px;padding:0 10px;flex:1;min-width:0;cursor:pointer}.ag-group-header-main:hover{background:var(--agrid-color-bg-subtle)}.ag-group-description{font-size:11px;color:var(--agrid-color-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ag-group-actions-btn{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border:none;background:none;border-radius:3px;cursor:pointer;color:var(--agrid-color-text-muted);font-size:14px;padding:0}.ag-group-actions-btn:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text)}.ag-group-icon{font-size:9px;color:var(--agrid-color-text-muted);line-height:1;display:inline-block;transition:transform .15s ease}.ag-group-icon--expanded{transform:rotate(90deg)}.ag-group-label{font-weight:600;font-size:12px;color:var(--agrid-color-text)}.ag-group-count{font-size:11px;color:var(--agrid-color-text-muted);background:var(--agrid-color-bg);border:1px solid var(--agrid-color-border);border-radius:10px;padding:0 7px;line-height:16px}.ag-group-aggregates{display:inline-flex;align-items:center;gap:10px;margin-left:4px;flex-wrap:wrap}.ag-group-aggregate{font-size:11px;color:var(--agrid-color-text);white-space:nowrap}.ag-group-aggregate-col{color:var(--agrid-color-text-muted);margin-right:4px}.ag-group-aggregate-op{color:var(--agrid-color-text-muted);margin-right:3px}.ag-footer{display:grid;flex-shrink:0;background:var(--agrid-color-bg-muted);border-top:2px solid var(--agrid-color-border)}.ag-footer-cell{display:flex;align-items:center;gap:4px;padding:0 6px;height:30px;border-right:1px solid var(--agrid-color-border);box-sizing:border-box;overflow:hidden;white-space:nowrap;font-weight:600;font-size:12px;color:var(--agrid-color-text)}.ag-footer-cell:last-child{border-right:none}.ag-footer-cell--control{background:var(--agrid-color-bg-subtle);border-right:1px solid var(--agrid-color-border)}.ag-footer-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-footer-label{font-size:10px;font-weight:400;color:var(--agrid-color-text-muted);flex-shrink:0}.ag-pagination{display:flex;align-items:center;gap:4px;padding:0 10px;height:34px;flex-shrink:0;border-top:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted);font-size:12px;color:var(--agrid-color-text-muted)}.ag-page-btn{display:flex;align-items:center;justify-content:center;width:24px;height:22px;border:1px solid var(--agrid-color-border);border-radius:3px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font-size:13px;cursor:pointer;padding:0;line-height:1}.ag-page-btn:hover:not(:disabled){background:var(--agrid-color-bg-muted);border-color:var(--agrid-color-text-muted)}.ag-page-btn:disabled{opacity:.35;cursor:default}.ag-page-info{padding:0 6px;font-weight:500;color:var(--agrid-color-text);min-width:48px;text-align:center}.ag-page-count{margin-left:auto;color:var(--agrid-color-text-muted)}.ag-add-row{border-bottom:1px dashed var(--agrid-color-border);color:var(--agrid-color-text-muted);cursor:pointer}.ag-add-row:hover .ag-add-row-label{background:var(--agrid-color-bg-muted)}.ag-add-row--selected .ag-add-row-label{outline:2px solid var(--agrid-color-accent);outline-offset:-2px}.ag-add-row-label{display:flex;align-items:center;gap:4px;height:100%;padding:0 6px;font-size:12px;-webkit-user-select:none;user-select:none}.ag-add-row-icon{font-size:16px;line-height:1;color:var(--agrid-color-text-muted)}.ag-pinned-pane--right{border-left:1px solid var(--agrid-color-border);border-right:none;box-shadow:-3px 0 6px -2px var(--agrid-color-shadow)}.ag-header-cell--pinned{background:var(--agrid-color-bg-muted)}.ag-header-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-header-cell--pinned-first{box-shadow:none}.ag-cell--pinned{background:var(--agrid-color-bg)}.ag-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-cell--pinned-first{box-shadow:none}.ag-row:hover .ag-cell--pinned{background:var(--agrid-color-bg-muted)}.ag-row--selected .ag-cell--pinned,.ag-row .ag-cell--pinned.ag-cell--range-selected,.ag-row .ag-cell--pinned.ag-cell--fill-preview{background:var(--agrid-color-accent-subtle)}.ag-row .ag-cell--pinned.ag-cell--find-match{background:#fff7cc}.ag-row .ag-cell--pinned.ag-cell--find-active{background:#ffe58a}.ag-header-menu-btn{flex-shrink:0;width:16px;height:16px;padding:0;margin-right:2px;background:none;border:1px solid transparent;border-radius:3px;cursor:pointer;font-size:10px;color:var(--agrid-color-text-muted);display:flex;align-items:center;justify-content:center;opacity:1;transition:opacity .12s;line-height:1}.ag-header-menu-btn--active{color:var(--agrid-color-accent);border-color:var(--agrid-color-accent-border);background:var(--agrid-color-accent-subtle)}.ag-header-menu-btn:hover{background:var(--agrid-color-bg);border-color:var(--agrid-color-border)}.ag-toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:6px 12px;padding:6px 8px;border-bottom:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-subtle)}.ag-quick-filter{margin-left:auto;width:100%;max-width:260px;height:30px;padding:0 10px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text)}.ag-quick-filter:focus{border-color:var(--agrid-color-accent-border)}.ag-detail-toggle{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:none;background:transparent;cursor:pointer;font-size:9px;line-height:1;color:var(--agrid-color-text-muted);transition:transform .12s ease,color .12s ease}.ag-detail-toggle:hover{color:var(--agrid-color-text)}.ag-detail-toggle--expanded{transform:rotate(90deg)}.ag-detail-row{display:block;position:relative;box-sizing:border-box}.ag-detail-panel{box-sizing:border-box;height:100%;overflow:auto;padding:8px 12px;background:var(--agrid-color-bg-subtle);border-bottom:1px solid var(--agrid-color-border)}.ag-pinned-rows{position:relative;z-index:3;flex:none;background:var(--agrid-color-bg)}.ag-pinned-rows--top{box-shadow:0 2px 4px -2px var(--agrid-color-shadow)}.ag-pinned-rows--bottom{box-shadow:0 -2px 4px -2px var(--agrid-color-shadow)}.ag-pinned-rows .ag-row{background:var(--agrid-color-bg-subtle)}\n"] }]
|
|
7249
|
+
}], ctorParameters: () => [], propDecorators: { provider: [{ type: i0.Input, args: [{ isSignal: true, alias: "provider", required: false }] }], cellEdit: [{ type: i0.Output, args: ["cellEdit"] }], recordEdit: [{ type: i0.Output, args: ["recordEdit"] }], rowRemoved: [{ type: i0.Output, args: ["rowRemoved"] }], prepareAddRecord: [{ type: i0.Output, args: ["prepareAddRecord"] }], rowReorder: [{ type: i0.Output, args: ["rowReorder"] }], rowSelect: [{ type: i0.Output, args: ["rowSelect"] }], rowDoubleClicked: [{ type: i0.Output, args: ["rowDoubleClicked"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], treeNodeClick: [{ type: i0.Output, args: ["treeNodeClick"] }], treeNodeDoubleClicked: [{ type: i0.Output, args: ["treeNodeDoubleClicked"] }], rowChanged: [{ type: i0.Output, args: ["rowChanged"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], quickFilterChange: [{ type: i0.Output, args: ["quickFilterChange"] }], validationFailed: [{ type: i0.Output, args: ["validationFailed"] }], cellInfo: [{ type: i0.Output, args: ["cellInfo"] }], menuBarAction: [{ type: i0.Output, args: ["menuBarAction"] }], viewport: [{ type: i0.ViewChild, args: ['scrollViewport', { isSignal: true }] }], pinnedViewport: [{ type: i0.ViewChild, args: ['pinnedViewport', { isSignal: true }] }], rightPinnedViewport: [{ type: i0.ViewChild, args: ['rightPinnedViewport', { isSignal: true }] }], wrapperEl: [{ type: i0.ViewChild, args: ['wrapper', { isSignal: true }] }], horizontalScrollerEl: [{ type: i0.ViewChild, args: ['horizontalScroller', { isSignal: true }] }] } });
|
|
7250
|
+
|
|
7251
|
+
let nextPageSelectorId = 0;
|
|
7252
|
+
/** Compact previous/input/dropdown/next control for navigating a labeled page list. */
|
|
7253
|
+
class AgridPageSelectorComponent {
|
|
7254
|
+
elementRef = inject(ElementRef);
|
|
7255
|
+
listboxId = `agrid-page-options-${nextPageSelectorId++}`;
|
|
7256
|
+
items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
7257
|
+
selectedId = input(null, ...(ngDevMode ? [{ debugName: "selectedId" }] : /* istanbul ignore next */ []));
|
|
7258
|
+
selectedPageNumber = input(null, ...(ngDevMode ? [{ debugName: "selectedPageNumber" }] : /* istanbul ignore next */ []));
|
|
7259
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
7260
|
+
previousLabel = input('Previous page', ...(ngDevMode ? [{ debugName: "previousLabel" }] : /* istanbul ignore next */ []));
|
|
7261
|
+
nextLabel = input('Next page', ...(ngDevMode ? [{ debugName: "nextLabel" }] : /* istanbul ignore next */ []));
|
|
7262
|
+
inputLabel = input('Page ID', ...(ngDevMode ? [{ debugName: "inputLabel" }] : /* istanbul ignore next */ []));
|
|
7263
|
+
menuLabel = input('Choose a page', ...(ngDevMode ? [{ debugName: "menuLabel" }] : /* istanbul ignore next */ []));
|
|
7264
|
+
emptyText = input('No pages', ...(ngDevMode ? [{ debugName: "emptyText" }] : /* istanbul ignore next */ []));
|
|
7265
|
+
selectPage = output();
|
|
7266
|
+
menuOpen = signal(false, ...(ngDevMode ? [{ debugName: "menuOpen" }] : /* istanbul ignore next */ []));
|
|
7267
|
+
draft = signal('', ...(ngDevMode ? [{ debugName: "draft" }] : /* istanbul ignore next */ []));
|
|
7268
|
+
invalid = signal(false, ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
|
|
7269
|
+
activeId = signal(null, ...(ngDevMode ? [{ debugName: "activeId" }] : /* istanbul ignore next */ []));
|
|
7270
|
+
focusedOptionIndex = signal(-1, ...(ngDevMode ? [{ debugName: "focusedOptionIndex" }] : /* istanbul ignore next */ []));
|
|
7271
|
+
activeIndex = computed(() => this.items().findIndex(item => this.idsEqual(item.id, this.activeId())), ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
|
|
7272
|
+
hasPrevious = computed(() => !this.disabled() && this.activeIndex() > 0, ...(ngDevMode ? [{ debugName: "hasPrevious" }] : /* istanbul ignore next */ []));
|
|
7273
|
+
hasNext = computed(() => {
|
|
7274
|
+
const index = this.activeIndex();
|
|
7275
|
+
return !this.disabled() && index >= 0 && index < this.items().length - 1;
|
|
7276
|
+
}, ...(ngDevMode ? [{ debugName: "hasNext" }] : /* istanbul ignore next */ []));
|
|
7277
|
+
constructor() {
|
|
7278
|
+
effect(() => {
|
|
7279
|
+
const id = this.selectedId();
|
|
7280
|
+
this.activeId.set(id);
|
|
7281
|
+
const item = this.items().find(i => i.id === id);
|
|
7282
|
+
this.draft.set(item?.pageNumber == null ? '' : String(item?.pageNumber));
|
|
7283
|
+
this.draft.set(id == null ? '' : String(id));
|
|
7284
|
+
this.invalid.set(false);
|
|
7285
|
+
});
|
|
7286
|
+
effect(() => {
|
|
7287
|
+
const nr = this.selectedPageNumber();
|
|
7288
|
+
// fetch the page
|
|
7289
|
+
const item = this.items().find(i => i.pageNumber === nr);
|
|
7290
|
+
this.activeId.set(item?.id ?? null);
|
|
7291
|
+
this.draft.set(item?.pageNumber == null ? '' : String(item?.pageNumber));
|
|
7292
|
+
this.invalid.set(false);
|
|
7293
|
+
});
|
|
7294
|
+
}
|
|
7295
|
+
previous() {
|
|
7296
|
+
if (!this.hasPrevious())
|
|
7297
|
+
return;
|
|
7298
|
+
this.choose(this.items()[this.activeIndex() - 1]);
|
|
7299
|
+
}
|
|
7300
|
+
next() {
|
|
7301
|
+
if (!this.hasNext())
|
|
7302
|
+
return;
|
|
7303
|
+
this.choose(this.items()[this.activeIndex() + 1]);
|
|
7304
|
+
}
|
|
7305
|
+
toggleMenu(event) {
|
|
7306
|
+
event.preventDefault();
|
|
7307
|
+
event.stopPropagation();
|
|
7308
|
+
if (this.disabled() || this.items().length === 0)
|
|
7309
|
+
return;
|
|
7310
|
+
this.menuOpen() ? this.closeMenu() : this.openMenu();
|
|
7311
|
+
}
|
|
7312
|
+
openMenu() {
|
|
7313
|
+
if (this.disabled() || this.items().length === 0)
|
|
7314
|
+
return;
|
|
7315
|
+
const current = this.activeIndex();
|
|
7316
|
+
this.focusedOptionIndex.set(current >= 0 ? current : 0);
|
|
7317
|
+
this.menuOpen.set(true);
|
|
7318
|
+
this.scrollFocusedOptionIntoView();
|
|
7319
|
+
}
|
|
7320
|
+
closeMenu() {
|
|
7321
|
+
this.menuOpen.set(false);
|
|
7322
|
+
this.focusedOptionIndex.set(-1);
|
|
7323
|
+
}
|
|
7324
|
+
onInput(event) {
|
|
7325
|
+
this.draft.set(event.target.value);
|
|
7326
|
+
this.invalid.set(false);
|
|
7327
|
+
this.closeMenu();
|
|
7328
|
+
}
|
|
7329
|
+
onInputKeydown(event) {
|
|
7330
|
+
if (event.key === 'Escape') {
|
|
7331
|
+
event.preventDefault();
|
|
7332
|
+
this.closeMenu();
|
|
7333
|
+
this.resetDraft();
|
|
7334
|
+
return;
|
|
7335
|
+
}
|
|
7336
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
7337
|
+
event.preventDefault();
|
|
7338
|
+
if (!this.menuOpen())
|
|
7339
|
+
this.openMenu();
|
|
7340
|
+
else
|
|
7341
|
+
this.moveOption(event.key === 'ArrowDown' ? 1 : -1);
|
|
7342
|
+
return;
|
|
7343
|
+
}
|
|
7344
|
+
if (event.key === 'Home' && this.menuOpen()) {
|
|
7345
|
+
event.preventDefault();
|
|
7346
|
+
this.focusedOptionIndex.set(0);
|
|
7347
|
+
this.scrollFocusedOptionIntoView();
|
|
7348
|
+
return;
|
|
7349
|
+
}
|
|
7350
|
+
if (event.key === 'End' && this.menuOpen()) {
|
|
7351
|
+
event.preventDefault();
|
|
7352
|
+
this.focusedOptionIndex.set(this.items().length - 1);
|
|
7353
|
+
this.scrollFocusedOptionIntoView();
|
|
7354
|
+
return;
|
|
7355
|
+
}
|
|
7356
|
+
if (event.key !== 'Enter')
|
|
7357
|
+
return;
|
|
7358
|
+
event.preventDefault();
|
|
7359
|
+
const focused = this.menuOpen() ? this.items()[this.focusedOptionIndex()] : undefined;
|
|
7360
|
+
if (focused)
|
|
7361
|
+
this.choose(focused);
|
|
7362
|
+
else
|
|
7363
|
+
this.selectDraft();
|
|
7364
|
+
}
|
|
7365
|
+
choose(item) {
|
|
7366
|
+
if (this.disabled())
|
|
7367
|
+
return;
|
|
7368
|
+
this.activeId.set(item.id);
|
|
7369
|
+
this.draft.set(String(item.id));
|
|
7370
|
+
this.invalid.set(false);
|
|
7371
|
+
this.closeMenu();
|
|
7372
|
+
this.selectPage.emit(item);
|
|
7373
|
+
}
|
|
7374
|
+
optionId(index) {
|
|
7375
|
+
return `${this.listboxId}-option-${index}`;
|
|
7376
|
+
}
|
|
7377
|
+
isSelected(item) {
|
|
7378
|
+
return this.idsEqual(item.id, this.activeId());
|
|
7379
|
+
}
|
|
7380
|
+
onDocumentPointerDown(event) {
|
|
7381
|
+
if (!this.menuOpen())
|
|
7382
|
+
return;
|
|
7383
|
+
if (!this.elementRef.nativeElement.contains(event.target))
|
|
7384
|
+
this.closeMenu();
|
|
7385
|
+
}
|
|
7386
|
+
selectDraft() {
|
|
7387
|
+
const value = this.draft().trim();
|
|
7388
|
+
const item = this.items().find(candidate => String(candidate.id) === value);
|
|
7389
|
+
if (item)
|
|
7390
|
+
this.choose(item);
|
|
7391
|
+
else
|
|
7392
|
+
this.invalid.set(true);
|
|
7393
|
+
}
|
|
7394
|
+
resetDraft() {
|
|
7395
|
+
const id = this.activeId();
|
|
7396
|
+
this.draft.set(id == null ? '' : String(id));
|
|
7397
|
+
this.invalid.set(false);
|
|
7398
|
+
}
|
|
7399
|
+
moveOption(direction) {
|
|
7400
|
+
const count = this.items().length;
|
|
7401
|
+
if (count === 0)
|
|
7402
|
+
return;
|
|
7403
|
+
const current = this.focusedOptionIndex();
|
|
7404
|
+
this.focusedOptionIndex.set((current + direction + count) % count);
|
|
7405
|
+
this.scrollFocusedOptionIntoView();
|
|
7406
|
+
}
|
|
7407
|
+
scrollFocusedOptionIntoView() {
|
|
7408
|
+
queueMicrotask(() => this.elementRef.nativeElement
|
|
7409
|
+
.querySelector(`#${this.optionId(this.focusedOptionIndex())}`)
|
|
7410
|
+
?.scrollIntoView?.({ block: 'nearest' }));
|
|
7411
|
+
}
|
|
7412
|
+
idsEqual(left, right) {
|
|
7413
|
+
return right !== null && left === right;
|
|
7414
|
+
}
|
|
7415
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridPageSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7416
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AgridPageSelectorComponent, isStandalone: true, selector: "agrid-page-selector", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, selectedId: { classPropertyName: "selectedId", publicName: "selectedId", isSignal: true, isRequired: false, transformFunction: null }, selectedPageNumber: { classPropertyName: "selectedPageNumber", publicName: "selectedPageNumber", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, previousLabel: { classPropertyName: "previousLabel", publicName: "previousLabel", isSignal: true, isRequired: false, transformFunction: null }, nextLabel: { classPropertyName: "nextLabel", publicName: "nextLabel", isSignal: true, isRequired: false, transformFunction: null }, inputLabel: { classPropertyName: "inputLabel", publicName: "inputLabel", isSignal: true, isRequired: false, transformFunction: null }, menuLabel: { classPropertyName: "menuLabel", publicName: "menuLabel", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectPage: "selectPage" }, host: { listeners: { "document:pointerdown": "onDocumentPointerDown($event)" } }, ngImport: i0, template: "<div class=\"ag-page-selector\" [class.ag-page-selector--disabled]=\"disabled()\">\n <button type=\"button\" class=\"ag-page-nav\" [disabled]=\"!hasPrevious()\"\n [attr.aria-label]=\"previousLabel()\" [title]=\"previousLabel()\" (click)=\"previous()\">\n <span aria-hidden=\"true\">‹</span>\n </button>\n\n <div class=\"ag-page-combo\">\n <div class=\"ag-page-input-group\" [class.ag-page-input-group--open]=\"menuOpen()\"\n [class.ag-page-input-group--invalid]=\"invalid()\">\n <input class=\"ag-page-input\" type=\"text\" inputmode=\"text\" autocomplete=\"off\"\n role=\"combobox\" [value]=\"draft()\" [disabled]=\"disabled()\"\n [attr.aria-label]=\"inputLabel()\" [attr.aria-expanded]=\"menuOpen()\"\n [attr.aria-controls]=\"menuOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"menuOpen() && focusedOptionIndex() >= 0 ? optionId(focusedOptionIndex()) : null\"\n [attr.aria-invalid]=\"invalid() ? 'true' : null\"\n (input)=\"onInput($event)\" (keydown)=\"onInputKeydown($event)\" />\n <button type=\"button\" class=\"ag-page-trigger\" [disabled]=\"disabled() || items().length === 0\"\n [attr.aria-label]=\"menuLabel()\" [title]=\"menuLabel()\"\n [attr.aria-expanded]=\"menuOpen()\" aria-haspopup=\"listbox\"\n (click)=\"toggleMenu($event)\">\n <span aria-hidden=\"true\">▾</span>\n </button>\n </div>\n\n @if (menuOpen()) {\n <div [id]=\"listboxId\" class=\"ag-page-options\" role=\"listbox\"\n [attr.aria-label]=\"menuLabel()\" (pointerdown)=\"$event.stopPropagation()\">\n @for (item of items(); track item.id; let index = $index) {\n <button type=\"button\" class=\"ag-page-option\" role=\"option\" [id]=\"optionId(index)\"\n [class.ag-page-option--selected]=\"isSelected(item)\"\n [class.ag-page-option--focused]=\"focusedOptionIndex() === index\"\n [attr.aria-selected]=\"isSelected(item)\"\n (mouseenter)=\"focusedOptionIndex.set(index)\" (click)=\"choose(item)\">\n <span class=\"ag-page-option-id\">{{ item.pageNumber }}</span>\n <span class=\"ag-page-option-label\">{{ item.label }}</span>\n </button>\n } @empty {\n <div class=\"ag-page-empty\">{{ emptyText() }}</div>\n }\n </div>\n }\n </div>\n\n <button type=\"button\" class=\"ag-page-nav\" [disabled]=\"!hasNext()\"\n [attr.aria-label]=\"nextLabel()\" [title]=\"nextLabel()\" (click)=\"next()\">\n <span aria-hidden=\"true\">›</span>\n </button>\n</div>\n", styles: ["@layer agrid-defaults{:host{--agrid-color-text: #24292f;--agrid-color-text-muted: #57606a;--agrid-color-accent: #1a73e8;--agrid-color-accent-subtle: #e8f0fe;--agrid-color-accent-fg: #1558b0;--agrid-color-accent-border: #c8d8f8;--agrid-color-border: #d0d7de;--agrid-color-bg: #ffffff;--agrid-color-bg-muted: #f6f8fa;--agrid-color-shadow: rgba(140, 149, 159, .2)}}:host{display:inline-block;color:var(--agrid-color-text);font:13px/1.35 -apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.ag-page-selector{display:inline-flex;align-items:center;gap:4px}.ag-page-nav,.ag-page-trigger{display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0;border:1px solid var(--agrid-color-border);background:var(--agrid-color-bg);color:var(--agrid-color-text-muted);cursor:pointer;font:inherit;transition:transform .12s cubic-bezier(.23,1,.32,1)}.ag-page-nav{width:32px;border-radius:4px;font-size:20px}.ag-page-trigger{width:30px;border-width:0 0 0 1px;font-size:10px}.ag-page-nav:active:not(:disabled),.ag-page-trigger:active:not(:disabled),.ag-page-option:active{transform:scale(.97)}.ag-page-nav:disabled,.ag-page-trigger:disabled,.ag-page-input:disabled{opacity:.48;cursor:not-allowed}.ag-page-nav:focus-visible,.ag-page-trigger:focus-visible,.ag-page-input:focus-visible,.ag-page-option:focus-visible{position:relative;z-index:2;outline:2px solid var(--agrid-color-accent);outline-offset:1px}.ag-page-combo{position:relative;width:clamp(150px,24vw,230px)}.ag-page-input-group{display:flex;height:32px;overflow:hidden;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg)}.ag-page-input-group:focus-within,.ag-page-input-group--open{border-color:var(--agrid-color-accent)}.ag-page-input-group--invalid{border-color:var(--agrid-color-danger, #d1242f)}.ag-page-input{min-width:0;flex:1;padding:0 9px;border:0;outline:0;background:transparent;color:var(--agrid-color-text);font:inherit}.ag-page-options{position:absolute;z-index:20;top:calc(100% + 4px);left:0;width:max(100%,220px);max-width:min(320px,calc(100vw - 24px));max-height:280px;overflow-y:auto;padding:4px;border:1px solid var(--agrid-color-border);border-radius:6px;background:var(--agrid-color-bg);box-shadow:0 10px 28px var(--agrid-color-shadow);transform-origin:top right}.ag-page-option{display:grid;grid-template-columns:minmax(32px,auto) minmax(0,1fr);gap:10px;align-items:center;width:100%;min-height:34px;padding:5px 8px;border:0;border-radius:4px;background:transparent;color:var(--agrid-color-text);cursor:pointer;font:inherit;text-align:left;transition:transform .12s cubic-bezier(.23,1,.32,1)}.ag-page-option--selected{color:var(--agrid-color-accent-fg);background:var(--agrid-color-accent-subtle)}.ag-page-option-id{color:var(--agrid-color-text-muted);font-variant-numeric:tabular-nums}.ag-page-option-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ag-page-empty{padding:12px 8px;color:var(--agrid-color-text-muted)}@media(hover:hover)and (pointer:fine){.ag-page-nav:hover:not(:disabled),.ag-page-trigger:hover:not(:disabled),.ag-page-option--focused:not(.ag-page-option--selected){background:var(--agrid-color-bg-muted)}}@media(max-width:420px){.ag-page-combo{width:min(210px,calc(100vw - 92px))}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7417
|
+
}
|
|
7418
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridPageSelectorComponent, decorators: [{
|
|
7419
|
+
type: Component,
|
|
7420
|
+
args: [{ selector: 'agrid-page-selector', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ag-page-selector\" [class.ag-page-selector--disabled]=\"disabled()\">\n <button type=\"button\" class=\"ag-page-nav\" [disabled]=\"!hasPrevious()\"\n [attr.aria-label]=\"previousLabel()\" [title]=\"previousLabel()\" (click)=\"previous()\">\n <span aria-hidden=\"true\">‹</span>\n </button>\n\n <div class=\"ag-page-combo\">\n <div class=\"ag-page-input-group\" [class.ag-page-input-group--open]=\"menuOpen()\"\n [class.ag-page-input-group--invalid]=\"invalid()\">\n <input class=\"ag-page-input\" type=\"text\" inputmode=\"text\" autocomplete=\"off\"\n role=\"combobox\" [value]=\"draft()\" [disabled]=\"disabled()\"\n [attr.aria-label]=\"inputLabel()\" [attr.aria-expanded]=\"menuOpen()\"\n [attr.aria-controls]=\"menuOpen() ? listboxId : null\"\n [attr.aria-activedescendant]=\"menuOpen() && focusedOptionIndex() >= 0 ? optionId(focusedOptionIndex()) : null\"\n [attr.aria-invalid]=\"invalid() ? 'true' : null\"\n (input)=\"onInput($event)\" (keydown)=\"onInputKeydown($event)\" />\n <button type=\"button\" class=\"ag-page-trigger\" [disabled]=\"disabled() || items().length === 0\"\n [attr.aria-label]=\"menuLabel()\" [title]=\"menuLabel()\"\n [attr.aria-expanded]=\"menuOpen()\" aria-haspopup=\"listbox\"\n (click)=\"toggleMenu($event)\">\n <span aria-hidden=\"true\">▾</span>\n </button>\n </div>\n\n @if (menuOpen()) {\n <div [id]=\"listboxId\" class=\"ag-page-options\" role=\"listbox\"\n [attr.aria-label]=\"menuLabel()\" (pointerdown)=\"$event.stopPropagation()\">\n @for (item of items(); track item.id; let index = $index) {\n <button type=\"button\" class=\"ag-page-option\" role=\"option\" [id]=\"optionId(index)\"\n [class.ag-page-option--selected]=\"isSelected(item)\"\n [class.ag-page-option--focused]=\"focusedOptionIndex() === index\"\n [attr.aria-selected]=\"isSelected(item)\"\n (mouseenter)=\"focusedOptionIndex.set(index)\" (click)=\"choose(item)\">\n <span class=\"ag-page-option-id\">{{ item.pageNumber }}</span>\n <span class=\"ag-page-option-label\">{{ item.label }}</span>\n </button>\n } @empty {\n <div class=\"ag-page-empty\">{{ emptyText() }}</div>\n }\n </div>\n }\n </div>\n\n <button type=\"button\" class=\"ag-page-nav\" [disabled]=\"!hasNext()\"\n [attr.aria-label]=\"nextLabel()\" [title]=\"nextLabel()\" (click)=\"next()\">\n <span aria-hidden=\"true\">›</span>\n </button>\n</div>\n", styles: ["@layer agrid-defaults{:host{--agrid-color-text: #24292f;--agrid-color-text-muted: #57606a;--agrid-color-accent: #1a73e8;--agrid-color-accent-subtle: #e8f0fe;--agrid-color-accent-fg: #1558b0;--agrid-color-accent-border: #c8d8f8;--agrid-color-border: #d0d7de;--agrid-color-bg: #ffffff;--agrid-color-bg-muted: #f6f8fa;--agrid-color-shadow: rgba(140, 149, 159, .2)}}:host{display:inline-block;color:var(--agrid-color-text);font:13px/1.35 -apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.ag-page-selector{display:inline-flex;align-items:center;gap:4px}.ag-page-nav,.ag-page-trigger{display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0;border:1px solid var(--agrid-color-border);background:var(--agrid-color-bg);color:var(--agrid-color-text-muted);cursor:pointer;font:inherit;transition:transform .12s cubic-bezier(.23,1,.32,1)}.ag-page-nav{width:32px;border-radius:4px;font-size:20px}.ag-page-trigger{width:30px;border-width:0 0 0 1px;font-size:10px}.ag-page-nav:active:not(:disabled),.ag-page-trigger:active:not(:disabled),.ag-page-option:active{transform:scale(.97)}.ag-page-nav:disabled,.ag-page-trigger:disabled,.ag-page-input:disabled{opacity:.48;cursor:not-allowed}.ag-page-nav:focus-visible,.ag-page-trigger:focus-visible,.ag-page-input:focus-visible,.ag-page-option:focus-visible{position:relative;z-index:2;outline:2px solid var(--agrid-color-accent);outline-offset:1px}.ag-page-combo{position:relative;width:clamp(150px,24vw,230px)}.ag-page-input-group{display:flex;height:32px;overflow:hidden;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg)}.ag-page-input-group:focus-within,.ag-page-input-group--open{border-color:var(--agrid-color-accent)}.ag-page-input-group--invalid{border-color:var(--agrid-color-danger, #d1242f)}.ag-page-input{min-width:0;flex:1;padding:0 9px;border:0;outline:0;background:transparent;color:var(--agrid-color-text);font:inherit}.ag-page-options{position:absolute;z-index:20;top:calc(100% + 4px);left:0;width:max(100%,220px);max-width:min(320px,calc(100vw - 24px));max-height:280px;overflow-y:auto;padding:4px;border:1px solid var(--agrid-color-border);border-radius:6px;background:var(--agrid-color-bg);box-shadow:0 10px 28px var(--agrid-color-shadow);transform-origin:top right}.ag-page-option{display:grid;grid-template-columns:minmax(32px,auto) minmax(0,1fr);gap:10px;align-items:center;width:100%;min-height:34px;padding:5px 8px;border:0;border-radius:4px;background:transparent;color:var(--agrid-color-text);cursor:pointer;font:inherit;text-align:left;transition:transform .12s cubic-bezier(.23,1,.32,1)}.ag-page-option--selected{color:var(--agrid-color-accent-fg);background:var(--agrid-color-accent-subtle)}.ag-page-option-id{color:var(--agrid-color-text-muted);font-variant-numeric:tabular-nums}.ag-page-option-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ag-page-empty{padding:12px 8px;color:var(--agrid-color-text-muted)}@media(hover:hover)and (pointer:fine){.ag-page-nav:hover:not(:disabled),.ag-page-trigger:hover:not(:disabled),.ag-page-option--focused:not(.ag-page-option--selected){background:var(--agrid-color-bg-muted)}}@media(max-width:420px){.ag-page-combo{width:min(210px,calc(100vw - 92px))}}\n"] }]
|
|
7421
|
+
}], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], selectedId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedId", required: false }] }], selectedPageNumber: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedPageNumber", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], previousLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "previousLabel", required: false }] }], nextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "nextLabel", required: false }] }], inputLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputLabel", required: false }] }], menuLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuLabel", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], selectPage: [{ type: i0.Output, args: ["selectPage"] }], onDocumentPointerDown: [{
|
|
7422
|
+
type: HostListener,
|
|
7423
|
+
args: ['document:pointerdown', ['$event']]
|
|
7424
|
+
}] } });
|
|
7425
|
+
|
|
7426
|
+
function isPathNode(item) {
|
|
7427
|
+
return 'pathNodeId' in item;
|
|
7428
|
+
}
|
|
7429
|
+
/** Standalone accessible tree control backed by the same projection logic as `AgridComponent`. */
|
|
7430
|
+
class AgridTreeComponent {
|
|
7431
|
+
provider = input.required(...(ngDevMode ? [{ debugName: "provider" }] : /* istanbul ignore next */ []));
|
|
7432
|
+
nodeClick = output();
|
|
7433
|
+
nodeDoubleClicked = output();
|
|
7434
|
+
selectionChange = output();
|
|
7435
|
+
treeController = new AgridTreeController();
|
|
7436
|
+
treeElement = viewChild('tree', ...(ngDevMode ? [{ debugName: "treeElement" }] : /* istanbul ignore next */ []));
|
|
7437
|
+
initializedProvider = null;
|
|
7438
|
+
focusedIndex = signal(0, ...(ngDevMode ? [{ debugName: "focusedIndex" }] : /* istanbul ignore next */ []));
|
|
7439
|
+
selectedKeys = signal(new Set(), ...(ngDevMode ? [{ debugName: "selectedKeys" }] : /* istanbul ignore next */ []));
|
|
7440
|
+
expandedIds = this.treeController.expandedIds;
|
|
7441
|
+
items = computed(() => {
|
|
7442
|
+
const provider = this.provider();
|
|
7443
|
+
const rows = provider.datasource.rows();
|
|
7444
|
+
const indices = rows.map((_, index) => index);
|
|
7445
|
+
const config = provider.treeConfig;
|
|
7446
|
+
const projected = isPathTreeConfig(config)
|
|
7447
|
+
? buildPathTreeItems(rows, indices, config, this.expandedIds())
|
|
7448
|
+
: buildTreeItems(rows, indices, config, this.expandedIds());
|
|
7449
|
+
return projected.filter((item) => item !== null && typeof item === 'object' && 'level' in item
|
|
7450
|
+
&& ('pathNodeId' in item || 'row' in item));
|
|
7451
|
+
}, ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
|
|
7452
|
+
constructor() {
|
|
7453
|
+
effect(() => {
|
|
7454
|
+
const provider = this.provider();
|
|
7455
|
+
if (provider === this.initializedProvider)
|
|
7456
|
+
return;
|
|
7457
|
+
this.initializedProvider = provider;
|
|
7458
|
+
this.selectedKeys.set(new Set());
|
|
7459
|
+
this.focusedIndex.set(0);
|
|
7460
|
+
provider.treeConfig.defaultExpanded ? this.expandAllNodes() : this.collapseAllNodes();
|
|
7461
|
+
});
|
|
7462
|
+
}
|
|
7463
|
+
/** Expands every branch currently represented by the datasource. */
|
|
7464
|
+
expandAllNodes() {
|
|
7465
|
+
const provider = this.provider();
|
|
7466
|
+
const config = provider.treeConfig;
|
|
7467
|
+
const rows = provider.datasource.rows();
|
|
7468
|
+
if (isPathTreeConfig(config)) {
|
|
7469
|
+
const ids = new Set();
|
|
7470
|
+
for (const row of rows) {
|
|
7471
|
+
const path = config.getPath(row).map(String).filter(Boolean);
|
|
7472
|
+
for (let length = 1; length < path.length; length++) {
|
|
7473
|
+
ids.add(pathTreeNodeId(path.slice(0, length)));
|
|
7474
|
+
}
|
|
7475
|
+
}
|
|
7476
|
+
this.treeController.expandAll(ids);
|
|
7477
|
+
return;
|
|
7478
|
+
}
|
|
7479
|
+
const ids = new Set();
|
|
7480
|
+
for (const row of rows) {
|
|
7481
|
+
const parentId = config.getParentId(row);
|
|
7482
|
+
if (parentId != null)
|
|
7483
|
+
ids.add(parentId);
|
|
7484
|
+
}
|
|
7485
|
+
this.treeController.expandAll(ids);
|
|
7486
|
+
}
|
|
7487
|
+
/** Collapses every branch. */
|
|
7488
|
+
collapseAllNodes() {
|
|
7489
|
+
this.treeController.collapseAll();
|
|
7490
|
+
}
|
|
7491
|
+
/** Toggles one expandable node. */
|
|
7492
|
+
toggleNode(item) {
|
|
7493
|
+
if (!item.expandable)
|
|
7494
|
+
return;
|
|
7495
|
+
this.treeController.toggle(this.expansionId(item));
|
|
7496
|
+
}
|
|
7497
|
+
/** @internal */
|
|
7498
|
+
label(item) {
|
|
7499
|
+
if (isPathNode(item))
|
|
7500
|
+
return item.pathLabel;
|
|
7501
|
+
if (item.treeLabel !== undefined)
|
|
7502
|
+
return item.treeLabel;
|
|
7503
|
+
const provider = this.provider();
|
|
7504
|
+
return provider.getLabel?.(item.row) ?? String(item.row[provider.treeConfig.treeField] ?? '');
|
|
7505
|
+
}
|
|
7506
|
+
/** @internal */
|
|
7507
|
+
description(item) {
|
|
7508
|
+
return isPathNode(item) ? undefined : this.provider().getDescription?.(item.row);
|
|
7509
|
+
}
|
|
7510
|
+
/** @internal */
|
|
7511
|
+
isSelected(item) {
|
|
7512
|
+
return this.selectedKeys().has(this.selectionKey(item));
|
|
7513
|
+
}
|
|
7514
|
+
/** @internal */
|
|
7515
|
+
onNodeClick(event, item, index) {
|
|
7516
|
+
event.stopPropagation();
|
|
7517
|
+
this.focusedIndex.set(index);
|
|
7518
|
+
this.select(item, event.metaKey || event.ctrlKey);
|
|
7519
|
+
this.nodeClick.emit(this.toEvent(item));
|
|
7520
|
+
}
|
|
7521
|
+
/** @internal */
|
|
7522
|
+
onNodeDoubleClick(event, item) {
|
|
7523
|
+
event.stopPropagation();
|
|
7524
|
+
this.nodeDoubleClicked.emit(this.toEvent(item));
|
|
7525
|
+
}
|
|
7526
|
+
/** @internal */
|
|
7527
|
+
onKeydown(event, item, index) {
|
|
7528
|
+
const items = this.items();
|
|
7529
|
+
if (event.key === 'ArrowDown')
|
|
7530
|
+
return this.moveFocus(event, Math.min(index + 1, items.length - 1));
|
|
7531
|
+
if (event.key === 'ArrowUp')
|
|
7532
|
+
return this.moveFocus(event, Math.max(index - 1, 0));
|
|
7533
|
+
if (event.key === 'Home')
|
|
7534
|
+
return this.moveFocus(event, 0);
|
|
7535
|
+
if (event.key === 'End')
|
|
7536
|
+
return this.moveFocus(event, items.length - 1);
|
|
7537
|
+
if (event.key === 'ArrowRight' && item.expandable) {
|
|
7538
|
+
event.preventDefault();
|
|
7539
|
+
if (!item.expanded)
|
|
7540
|
+
this.toggleNode(item);
|
|
7541
|
+
else
|
|
7542
|
+
this.moveFocus(event, Math.min(index + 1, items.length - 1));
|
|
7543
|
+
return;
|
|
7544
|
+
}
|
|
7545
|
+
if (event.key === 'ArrowLeft') {
|
|
7546
|
+
event.preventDefault();
|
|
7547
|
+
if (item.expandable && item.expanded)
|
|
7548
|
+
this.toggleNode(item);
|
|
7549
|
+
else {
|
|
7550
|
+
const parent = this.findParentIndex(index, item.level);
|
|
7551
|
+
if (parent >= 0)
|
|
7552
|
+
this.focusNode(parent);
|
|
7553
|
+
}
|
|
7554
|
+
return;
|
|
7555
|
+
}
|
|
7556
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
7557
|
+
event.preventDefault();
|
|
7558
|
+
this.select(item, event.metaKey || event.ctrlKey);
|
|
7559
|
+
this.nodeClick.emit(this.toEvent(item));
|
|
7560
|
+
}
|
|
7561
|
+
}
|
|
7562
|
+
/** @internal */
|
|
7563
|
+
trackItem(_index, item) {
|
|
7564
|
+
return this.selectionKey(item);
|
|
7565
|
+
}
|
|
7566
|
+
select(item, additive) {
|
|
7567
|
+
const mode = this.provider().selection;
|
|
7568
|
+
if (mode === 'none')
|
|
7569
|
+
return;
|
|
7570
|
+
const key = this.selectionKey(item);
|
|
7571
|
+
this.selectedKeys.update(current => {
|
|
7572
|
+
const next = mode === 'multi' && additive ? new Set(current) : new Set();
|
|
7573
|
+
if (mode === 'multi' && additive && next.has(key))
|
|
7574
|
+
next.delete(key);
|
|
7575
|
+
else
|
|
7576
|
+
next.add(key);
|
|
7577
|
+
return next;
|
|
7578
|
+
});
|
|
7579
|
+
const selected = this.items().filter(candidate => this.selectedKeys().has(this.selectionKey(candidate)));
|
|
7580
|
+
this.selectionChange.emit({ nodes: selected.map(candidate => this.toEvent(candidate)) });
|
|
7581
|
+
}
|
|
7582
|
+
toEvent(item) {
|
|
7583
|
+
if (isPathNode(item)) {
|
|
7584
|
+
return {
|
|
7585
|
+
kind: 'branch', id: item.pathNodeId, uuid: item.uuid, label: item.pathLabel,
|
|
7586
|
+
level: item.level, expandable: true, expanded: item.expanded,
|
|
7587
|
+
};
|
|
7588
|
+
}
|
|
7589
|
+
const config = this.provider().treeConfig;
|
|
7590
|
+
const id = isPathTreeConfig(config) ? item.originalIndex : config.getId(item.row);
|
|
7591
|
+
return {
|
|
7592
|
+
kind: 'row', id, label: this.label(item), level: item.level,
|
|
7593
|
+
expandable: item.expandable, expanded: item.expanded,
|
|
7594
|
+
row: item.row, originalIndex: item.originalIndex,
|
|
7595
|
+
};
|
|
7596
|
+
}
|
|
7597
|
+
expansionId(item) {
|
|
7598
|
+
if (isPathNode(item))
|
|
7599
|
+
return item.pathNodeId;
|
|
7600
|
+
const config = this.provider().treeConfig;
|
|
7601
|
+
return isPathTreeConfig(config) ? item.originalIndex : config.getId(item.row);
|
|
7602
|
+
}
|
|
7603
|
+
selectionKey(item) {
|
|
7604
|
+
return isPathNode(item)
|
|
7605
|
+
? `branch:${item.pathNodeId}`
|
|
7606
|
+
: `row:${this.expansionId(item)}`;
|
|
7607
|
+
}
|
|
7608
|
+
moveFocus(event, index) {
|
|
7609
|
+
event.preventDefault();
|
|
7610
|
+
this.focusNode(index);
|
|
7611
|
+
}
|
|
7612
|
+
focusNode(index) {
|
|
7613
|
+
this.focusedIndex.set(index);
|
|
7614
|
+
queueMicrotask(() => this.treeElement()?.nativeElement
|
|
7615
|
+
.querySelector(`[data-tree-index="${index}"]`)?.focus());
|
|
7616
|
+
}
|
|
7617
|
+
findParentIndex(index, level) {
|
|
7618
|
+
for (let candidate = index - 1; candidate >= 0; candidate--) {
|
|
7619
|
+
if (this.items()[candidate].level < level)
|
|
7620
|
+
return candidate;
|
|
7621
|
+
}
|
|
7622
|
+
return -1;
|
|
7623
|
+
}
|
|
7624
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7625
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AgridTreeComponent, isStandalone: true, selector: "agrid-tree", inputs: { provider: { classPropertyName: "provider", publicName: "provider", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { nodeClick: "nodeClick", nodeDoubleClicked: "nodeDoubleClicked", selectionChange: "selectionChange" }, host: { properties: { "style.--agrid-tree-row-height.px": "provider().rowHeight" } }, viewQueries: [{ propertyName: "treeElement", first: true, predicate: ["tree"], descendants: true, isSignal: true }], ngImport: i0, template: "<div #tree class=\"ag-tree\" role=\"tree\" [attr.aria-label]=\"provider().ariaLabel\"\n [attr.aria-multiselectable]=\"provider().selection === 'multi' ? 'true' : null\">\n @for (item of items(); track trackItem($index, item); let index = $index) {\n <div class=\"ag-tree-node\" role=\"treeitem\" [attr.data-tree-index]=\"index\"\n [attr.tabindex]=\"focusedIndex() === index ? 0 : -1\"\n [attr.aria-level]=\"item.level + 1\"\n [attr.aria-expanded]=\"item.expandable ? item.expanded : null\"\n [attr.aria-selected]=\"provider().selection !== 'none' ? isSelected(item) : null\"\n [class.ag-tree-node--selected]=\"isSelected(item)\"\n [style.padding-left.px]=\"8 + item.level * 20\"\n (click)=\"onNodeClick($event, item, index)\"\n (dblclick)=\"onNodeDoubleClick($event, item)\"\n (keydown)=\"onKeydown($event, item, index)\">\n <button class=\"ag-tree-toggle\" type=\"button\" tabindex=\"-1\"\n [class.ag-tree-toggle--expanded]=\"item.expanded\"\n [class.ag-tree-toggle--hidden]=\"!item.expandable\"\n [attr.aria-label]=\"(item.expanded ? 'Collapse ' : 'Expand ') + label(item)\"\n (click)=\"toggleNode(item); $event.stopPropagation()\">▸</button>\n <span class=\"ag-tree-content\">\n <span class=\"ag-tree-label\">{{ label(item) }}</span>\n @if (description(item); as detail) {\n <span class=\"ag-tree-description\">{{ detail }}</span>\n }\n </span>\n </div>\n } @empty {\n <div class=\"ag-tree-empty\">{{ provider().emptyText }}</div>\n }\n</div>\n", styles: ["@layer agrid-defaults{:host{--agrid-color-text: #24292f;--agrid-color-text-muted: #57606a;--agrid-color-accent: #1a73e8;--agrid-color-accent-subtle: #e8f0fe;--agrid-color-accent-fg: #1558b0;--agrid-color-border: #d0d7de;--agrid-color-bg: #ffffff;--agrid-color-bg-muted: #f6f8fa}}:host{display:block;min-width:180px;min-height:0;overflow:auto;color:var(--agrid-color-text);background:var(--agrid-color-bg);font:13px/1.35 -apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.ag-tree{min-width:max-content;padding:4px}.ag-tree-node{display:flex;align-items:center;min-width:100%;height:var(--agrid-tree-row-height);padding-right:10px;border-radius:4px;box-sizing:border-box;cursor:default;outline:none;-webkit-user-select:none;user-select:none}.ag-tree-node--selected{color:var(--agrid-color-accent-fg);background:var(--agrid-color-accent-subtle)}.ag-tree-node:focus-visible{box-shadow:inset 0 0 0 2px var(--agrid-color-accent)}.ag-tree-toggle{display:inline-flex;flex:0 0 24px;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:0;border-radius:4px;color:var(--agrid-color-text-muted);background:transparent;font:inherit;cursor:pointer;transition:transform .12s cubic-bezier(.23,1,.32,1)}.ag-tree-toggle--expanded{transform:rotate(90deg)}.ag-tree-toggle--hidden{visibility:hidden}.ag-tree-toggle:active{scale:.92}.ag-tree-content{display:flex;min-width:0;flex-direction:column;justify-content:center}.ag-tree-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500}.ag-tree-description{overflow:hidden;color:var(--agrid-color-text-muted);text-overflow:ellipsis;white-space:nowrap;font-size:11px}.ag-tree-empty{padding:16px 12px;color:var(--agrid-color-text-muted)}@media(hover:hover)and (pointer:fine){.ag-tree-node:hover:not(.ag-tree-node--selected),.ag-tree-toggle:hover{background:var(--agrid-color-bg-muted)}}@media(prefers-reduced-motion:reduce){.ag-tree-toggle{transition:none}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7626
|
+
}
|
|
7627
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AgridTreeComponent, decorators: [{
|
|
7628
|
+
type: Component,
|
|
7629
|
+
args: [{ selector: 'agrid-tree', changeDetection: ChangeDetectionStrategy.OnPush, host: { '[style.--agrid-tree-row-height.px]': 'provider().rowHeight' }, template: "<div #tree class=\"ag-tree\" role=\"tree\" [attr.aria-label]=\"provider().ariaLabel\"\n [attr.aria-multiselectable]=\"provider().selection === 'multi' ? 'true' : null\">\n @for (item of items(); track trackItem($index, item); let index = $index) {\n <div class=\"ag-tree-node\" role=\"treeitem\" [attr.data-tree-index]=\"index\"\n [attr.tabindex]=\"focusedIndex() === index ? 0 : -1\"\n [attr.aria-level]=\"item.level + 1\"\n [attr.aria-expanded]=\"item.expandable ? item.expanded : null\"\n [attr.aria-selected]=\"provider().selection !== 'none' ? isSelected(item) : null\"\n [class.ag-tree-node--selected]=\"isSelected(item)\"\n [style.padding-left.px]=\"8 + item.level * 20\"\n (click)=\"onNodeClick($event, item, index)\"\n (dblclick)=\"onNodeDoubleClick($event, item)\"\n (keydown)=\"onKeydown($event, item, index)\">\n <button class=\"ag-tree-toggle\" type=\"button\" tabindex=\"-1\"\n [class.ag-tree-toggle--expanded]=\"item.expanded\"\n [class.ag-tree-toggle--hidden]=\"!item.expandable\"\n [attr.aria-label]=\"(item.expanded ? 'Collapse ' : 'Expand ') + label(item)\"\n (click)=\"toggleNode(item); $event.stopPropagation()\">▸</button>\n <span class=\"ag-tree-content\">\n <span class=\"ag-tree-label\">{{ label(item) }}</span>\n @if (description(item); as detail) {\n <span class=\"ag-tree-description\">{{ detail }}</span>\n }\n </span>\n </div>\n } @empty {\n <div class=\"ag-tree-empty\">{{ provider().emptyText }}</div>\n }\n</div>\n", styles: ["@layer agrid-defaults{:host{--agrid-color-text: #24292f;--agrid-color-text-muted: #57606a;--agrid-color-accent: #1a73e8;--agrid-color-accent-subtle: #e8f0fe;--agrid-color-accent-fg: #1558b0;--agrid-color-border: #d0d7de;--agrid-color-bg: #ffffff;--agrid-color-bg-muted: #f6f8fa}}:host{display:block;min-width:180px;min-height:0;overflow:auto;color:var(--agrid-color-text);background:var(--agrid-color-bg);font:13px/1.35 -apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.ag-tree{min-width:max-content;padding:4px}.ag-tree-node{display:flex;align-items:center;min-width:100%;height:var(--agrid-tree-row-height);padding-right:10px;border-radius:4px;box-sizing:border-box;cursor:default;outline:none;-webkit-user-select:none;user-select:none}.ag-tree-node--selected{color:var(--agrid-color-accent-fg);background:var(--agrid-color-accent-subtle)}.ag-tree-node:focus-visible{box-shadow:inset 0 0 0 2px var(--agrid-color-accent)}.ag-tree-toggle{display:inline-flex;flex:0 0 24px;align-items:center;justify-content:center;width:24px;height:24px;padding:0;border:0;border-radius:4px;color:var(--agrid-color-text-muted);background:transparent;font:inherit;cursor:pointer;transition:transform .12s cubic-bezier(.23,1,.32,1)}.ag-tree-toggle--expanded{transform:rotate(90deg)}.ag-tree-toggle--hidden{visibility:hidden}.ag-tree-toggle:active{scale:.92}.ag-tree-content{display:flex;min-width:0;flex-direction:column;justify-content:center}.ag-tree-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500}.ag-tree-description{overflow:hidden;color:var(--agrid-color-text-muted);text-overflow:ellipsis;white-space:nowrap;font-size:11px}.ag-tree-empty{padding:16px 12px;color:var(--agrid-color-text-muted)}@media(hover:hover)and (pointer:fine){.ag-tree-node:hover:not(.ag-tree-node--selected),.ag-tree-toggle:hover{background:var(--agrid-color-bg-muted)}}@media(prefers-reduced-motion:reduce){.ag-tree-toggle{transition:none}}\n"] }]
|
|
7630
|
+
}], ctorParameters: () => [], propDecorators: { provider: [{ type: i0.Input, args: [{ isSignal: true, alias: "provider", required: true }] }], nodeClick: [{ type: i0.Output, args: ["nodeClick"] }], nodeDoubleClicked: [{ type: i0.Output, args: ["nodeDoubleClicked"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], treeElement: [{ type: i0.ViewChild, args: ['tree', { isSignal: true }] }] } });
|
|
7631
|
+
|
|
7632
|
+
/** Provider-style configuration and datasource container for `<agrid-tree>`. */
|
|
7633
|
+
class AgridTreeProvider {
|
|
7634
|
+
datasource;
|
|
7635
|
+
treeConfig;
|
|
7636
|
+
getLabel;
|
|
7637
|
+
getDescription;
|
|
7638
|
+
selection;
|
|
7639
|
+
rowHeight;
|
|
7640
|
+
ariaLabel;
|
|
7641
|
+
emptyText;
|
|
7642
|
+
constructor(config) {
|
|
7643
|
+
this.datasource = config.datasource;
|
|
7644
|
+
this.treeConfig = config.treeConfig;
|
|
7645
|
+
this.getLabel = config.getLabel;
|
|
7646
|
+
this.getDescription = config.getDescription;
|
|
7647
|
+
this.selection = config.selection ?? 'single';
|
|
7648
|
+
this.rowHeight = config.rowHeight ?? 36;
|
|
7649
|
+
this.ariaLabel = config.ariaLabel ?? 'Tree';
|
|
7650
|
+
this.emptyText = config.emptyText ?? 'No items';
|
|
7651
|
+
}
|
|
7652
|
+
}
|
|
6009
7653
|
|
|
6010
7654
|
/** Width sentinel that makes a column fill the remaining horizontal space. */
|
|
6011
7655
|
const ColDefAutoSize = -1;
|
|
@@ -6014,5 +7658,5 @@ const ColDefAutoSize = -1;
|
|
|
6014
7658
|
* Generated bundle index. Do not edit.
|
|
6015
7659
|
*/
|
|
6016
7660
|
|
|
6017
|
-
export { AGRID_LOCALE_TEXT, AgridComponent, AgridControl, AgridDataSource, AgridProvider, ColDefAutoSize };
|
|
7661
|
+
export { AGRID_LOCALE_TEXT, AgridComponent, AgridControl, AgridDataSource, AgridPageSelectorComponent, AgridProvider, AgridTreeComponent, AgridTreeProvider, ColDefAutoSize };
|
|
6018
7662
|
//# sourceMappingURL=thkl-agrid.mjs.map
|