@thkl/agrid 0.1.5 → 0.1.9
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 +268 -4
- package/fesm2022/thkl-agrid.mjs +2256 -181
- package/fesm2022/thkl-agrid.mjs.map +1 -1
- package/package.json +4 -4
- package/types/thkl-agrid.d.ts +730 -27
- 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. */
|
|
@@ -106,9 +120,70 @@ function applyTextAndValueFilters(rows, indices, filters, colMap, locale) {
|
|
|
106
120
|
const allowed = new Set(filter.selectedValues);
|
|
107
121
|
result = result.filter(i => allowed.has(String(rows[i][field] ?? '')));
|
|
108
122
|
}
|
|
123
|
+
if (filter.operator && filter.operand != null && filter.operand !== '') {
|
|
124
|
+
result = result.filter(i => passesConditionFilter(col, rows[i][field], filter, locale));
|
|
125
|
+
}
|
|
109
126
|
}
|
|
110
127
|
return result;
|
|
111
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Evaluate a text, number, or date condition for one cell value.
|
|
131
|
+
*/
|
|
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
|
+
}
|
|
151
|
+
const isDate = col?.type === 'date' || looksLikeDate(raw);
|
|
152
|
+
const toNum = (v) => isDate
|
|
153
|
+
? (v instanceof Date ? v.getTime() : new Date(v).getTime())
|
|
154
|
+
: Number(v);
|
|
155
|
+
const value = toNum(raw);
|
|
156
|
+
if (Number.isNaN(value))
|
|
157
|
+
return false;
|
|
158
|
+
const a = toNum(filter.operand);
|
|
159
|
+
if (Number.isNaN(a))
|
|
160
|
+
return true;
|
|
161
|
+
switch (filter.operator) {
|
|
162
|
+
case 'eq': return value === a;
|
|
163
|
+
case 'neq': return value !== a;
|
|
164
|
+
case 'gt': return value > a;
|
|
165
|
+
case 'gte': return value >= a;
|
|
166
|
+
case 'lt': return value < a;
|
|
167
|
+
case 'lte': return value <= a;
|
|
168
|
+
case 'between': {
|
|
169
|
+
const b = toNum(filter.operand2);
|
|
170
|
+
if (Number.isNaN(b))
|
|
171
|
+
return value >= a;
|
|
172
|
+
return value >= Math.min(a, b) && value <= Math.max(a, b);
|
|
173
|
+
}
|
|
174
|
+
default: return true;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Keep only the rows where at least one of the given columns' display value contains
|
|
179
|
+
* `text` (case-insensitive). An empty/whitespace `text` returns the indices unchanged.
|
|
180
|
+
*/
|
|
181
|
+
function applyQuickFilter(rows, indices, text, cols, locale) {
|
|
182
|
+
const q = text.trim().toLowerCase();
|
|
183
|
+
if (!q)
|
|
184
|
+
return indices;
|
|
185
|
+
return indices.filter(i => cols.some(col => getDisplayForField(col, rows[i][col.field], locale).toLowerCase().includes(q)));
|
|
186
|
+
}
|
|
112
187
|
// Sorting
|
|
113
188
|
/**
|
|
114
189
|
* Sort indices by one or more columns in priority order.
|
|
@@ -119,18 +194,18 @@ function applySortToIndices(rows, indices, sortEntries, colMap, locale) {
|
|
|
119
194
|
if (sortEntries.length === 0)
|
|
120
195
|
return indices;
|
|
121
196
|
const collator = new Intl.Collator(locale, { numeric: true, sensitivity: 'base' });
|
|
122
|
-
const fields = sortEntries.map(([field, filter]) =>
|
|
123
|
-
field
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
position
|
|
130
|
-
|
|
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];
|
|
131
206
|
const raw = rows[index][field];
|
|
132
|
-
const
|
|
133
|
-
const dateValue =
|
|
207
|
+
const isDateLike = col?.type === 'date' || looksLikeDate(raw);
|
|
208
|
+
const dateValue = isDateLike
|
|
134
209
|
? raw instanceof Date ? raw.getTime() : new Date(raw).getTime()
|
|
135
210
|
: Number.NaN;
|
|
136
211
|
const numericValue = col?.type === 'number'
|
|
@@ -139,44 +214,100 @@ function applySortToIndices(rows, indices, sortEntries, colMap, locale) {
|
|
|
139
214
|
&& typeof raw === 'number'
|
|
140
215
|
&& Number.isFinite(raw)
|
|
141
216
|
? raw
|
|
142
|
-
:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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) => {
|
|
152
233
|
for (let fieldIndex = 0; fieldIndex < fields.length; fieldIndex++) {
|
|
153
|
-
const
|
|
154
|
-
const keyB = b.keys[fieldIndex];
|
|
234
|
+
const field = fields[fieldIndex];
|
|
155
235
|
let comparison;
|
|
156
|
-
if (
|
|
157
|
-
comparison =
|
|
236
|
+
if (field.dateLike[a] || field.dateLike[b]) {
|
|
237
|
+
comparison = field.dateValues[a] - field.dateValues[b];
|
|
158
238
|
}
|
|
159
|
-
else if (
|
|
160
|
-
|
|
239
|
+
else if (!Number.isNaN(field.numericValues[a])
|
|
240
|
+
&& !Number.isNaN(field.numericValues[b])) {
|
|
241
|
+
comparison = field.numericValues[a] - field.numericValues[b];
|
|
161
242
|
}
|
|
162
243
|
else {
|
|
163
|
-
comparison = collator.compare(
|
|
244
|
+
comparison = collator.compare(field.displayValues[a], field.displayValues[b]);
|
|
164
245
|
}
|
|
165
246
|
if (comparison !== 0) {
|
|
166
|
-
return comparison *
|
|
247
|
+
return comparison * field.direction;
|
|
167
248
|
}
|
|
168
249
|
}
|
|
169
|
-
return a
|
|
250
|
+
return a - b;
|
|
170
251
|
});
|
|
171
|
-
return
|
|
252
|
+
return positions.map(position => indices[position]);
|
|
172
253
|
}
|
|
173
|
-
// Grouping
|
|
174
254
|
/**
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
255
|
+
* Compute aggregate values for the given rows across every column that has a static
|
|
256
|
+
* (`ColDef.aggregate`) or control-configured aggregate. Returns a `field → value` map containing
|
|
257
|
+
* only aggregated columns. Shared by the grid footer and per-group subtotals.
|
|
178
258
|
*/
|
|
179
|
-
function
|
|
259
|
+
function computeAggregates(rows, indices, cols, controlAggregates) {
|
|
260
|
+
const result = {};
|
|
261
|
+
for (const col of cols) {
|
|
262
|
+
const aggregate = controlAggregates[col.field] ?? col.aggregate;
|
|
263
|
+
if (!aggregate)
|
|
264
|
+
continue;
|
|
265
|
+
if (typeof aggregate === 'function') {
|
|
266
|
+
const values = indices.map(index => rows[index][col.field]);
|
|
267
|
+
result[col.field] = aggregate(values);
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
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
|
+
}
|
|
289
|
+
switch (aggregate) {
|
|
290
|
+
case 'sum':
|
|
291
|
+
result[col.field] = sum;
|
|
292
|
+
break;
|
|
293
|
+
case 'avg':
|
|
294
|
+
result[col.field] = numericCount ? sum / numericCount : null;
|
|
295
|
+
break;
|
|
296
|
+
case 'min':
|
|
297
|
+
result[col.field] = numericCount ? min : null;
|
|
298
|
+
break;
|
|
299
|
+
case 'max':
|
|
300
|
+
result[col.field] = numericCount ? max : null;
|
|
301
|
+
break;
|
|
302
|
+
case 'count':
|
|
303
|
+
result[col.field] = count;
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return result;
|
|
308
|
+
}
|
|
309
|
+
function buildGroupedItems(rows, indices, groupField, colMap, sortEntries, expandedLabels, locale, aggregateCols = [], controlAggregates = {}) {
|
|
310
|
+
const hasAggregates = aggregateCols.some(col => controlAggregates[col.field] ?? col.aggregate);
|
|
180
311
|
const groupCol = colMap.get(groupField);
|
|
181
312
|
const groups = new Map();
|
|
182
313
|
for (const i of indices) {
|
|
@@ -197,7 +328,10 @@ function buildGroupedItems(rows, indices, groupField, colMap, sortEntries, expan
|
|
|
197
328
|
for (const key of sortedKeys) {
|
|
198
329
|
const groupRows = groups.get(key);
|
|
199
330
|
const isExpanded = expandedLabels.has(key);
|
|
200
|
-
|
|
331
|
+
const aggregates = hasAggregates
|
|
332
|
+
? computeAggregates(rows, groupRows, aggregateCols, controlAggregates)
|
|
333
|
+
: undefined;
|
|
334
|
+
items.push({ groupLabel: key, count: groupRows.length, collapsed: !isExpanded, aggregates });
|
|
201
335
|
if (isExpanded) {
|
|
202
336
|
for (const i of groupRows)
|
|
203
337
|
items.push({ row: rows[i], originalIndex: i });
|
|
@@ -262,6 +396,123 @@ function buildTreeItems(rows, indices, accessors, expandedIds, forceExpandedIds)
|
|
|
262
396
|
visit(rootIndex, 0);
|
|
263
397
|
return items;
|
|
264
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
|
+
}
|
|
265
516
|
// Selection range
|
|
266
517
|
/** Build the set of original indices spanning from `fromOrig` to `toOrig` in display order. */
|
|
267
518
|
function buildSelectionRange(fromOrig, toOrig, items) {
|
|
@@ -278,6 +529,11 @@ function buildSelectionRange(fromOrig, toOrig, items) {
|
|
|
278
529
|
}
|
|
279
530
|
return next;
|
|
280
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
|
+
}
|
|
281
537
|
|
|
282
538
|
/**
|
|
283
539
|
* Individual cell component used inside `AgridComponent`.
|
|
@@ -296,6 +552,8 @@ class AgridCellComponent {
|
|
|
296
552
|
colIndex = input.required(...(ngDevMode ? [{ debugName: "colIndex" }] : /* istanbul ignore next */ []));
|
|
297
553
|
/** Current field value from the data source (displayed when not editing). */
|
|
298
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 */ []));
|
|
299
557
|
/** Full row data — passed to `cellRenderer` when set. */
|
|
300
558
|
row = input({}, ...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
|
|
301
559
|
/** Locale used for built-in date formatting. */
|
|
@@ -304,6 +562,12 @@ class AgridCellComponent {
|
|
|
304
562
|
selected = input(false, ...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
|
|
305
563
|
/** Whether this cell is currently in edit mode. */
|
|
306
564
|
editing = input(false, ...(ngDevMode ? [{ debugName: "editing" }] : /* istanbul ignore next */ []));
|
|
565
|
+
/** Whether this cell may be edited (drives the boolean checkbox enabled state). */
|
|
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 */ []));
|
|
569
|
+
/** Validation error message to show under the editor, or `null` when the value is valid. */
|
|
570
|
+
error = input(null, ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
|
|
307
571
|
/** Whether this cell is the tree column and should render indentation + a twisty. */
|
|
308
572
|
treeCell = input(false, ...(ngDevMode ? [{ debugName: "treeCell" }] : /* istanbul ignore next */ []));
|
|
309
573
|
/** Depth of the row in the tree (drives indentation). Root rows are `0`. */
|
|
@@ -321,6 +585,8 @@ class AgridCellComponent {
|
|
|
321
585
|
* while the cell is selected (type-to-start-editing behavior).
|
|
322
586
|
*/
|
|
323
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 */ []));
|
|
324
590
|
/**
|
|
325
591
|
* Emitted on single click — the grid selects this cell.
|
|
326
592
|
* For `values` columns the grid also enters edit mode immediately.
|
|
@@ -328,6 +594,10 @@ class AgridCellComponent {
|
|
|
328
594
|
activate = output();
|
|
329
595
|
/** Emitted on double-click — the grid enters edit mode. */
|
|
330
596
|
startEdit = output();
|
|
597
|
+
/** Emitted when a boolean-column checkbox is toggled, carrying the new value. */
|
|
598
|
+
booleanToggle = output();
|
|
599
|
+
/** Emitted when the cell's optional information button is clicked. */
|
|
600
|
+
infoClick = output();
|
|
331
601
|
/**
|
|
332
602
|
* Emitted on every keystroke inside the edit input or on every select change.
|
|
333
603
|
* The grid stores the latest value in `currentDraft` so it can commit on Tab / Enter.
|
|
@@ -338,10 +608,30 @@ class AgridCellComponent {
|
|
|
338
608
|
/** String value accepted by the active native input element. */
|
|
339
609
|
editorValue = computed(() => {
|
|
340
610
|
const draft = this.draft();
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
611
|
+
if (this.col().type === 'date') {
|
|
612
|
+
return getDateInputValue(draft);
|
|
613
|
+
}
|
|
614
|
+
return String(draft ?? '');
|
|
344
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 */ []));
|
|
628
|
+
/** Whether this cell renders as an inline boolean checkbox (no edit mode). */
|
|
629
|
+
booleanCell = computed(() => this.col().type === 'boolean' && !this.col().cellRenderer, ...(ngDevMode ? [{ debugName: "booleanCell" }] : /* istanbul ignore next */ []));
|
|
630
|
+
/** Truthiness of the current boolean value (accepts `true`, `'true'`, `1`, `'1'`). */
|
|
631
|
+
booleanChecked = computed(() => {
|
|
632
|
+
const v = this.value();
|
|
633
|
+
return v === true || v === 1 || v === 'true' || v === '1';
|
|
634
|
+
}, ...(ngDevMode ? [{ debugName: "booleanChecked" }] : /* istanbul ignore next */ []));
|
|
345
635
|
renderedHtml = computed(() => {
|
|
346
636
|
const renderer = this.col().cellRenderer;
|
|
347
637
|
if (!renderer)
|
|
@@ -363,19 +653,8 @@ class AgridCellComponent {
|
|
|
363
653
|
* Priority: ValueOption label → `ColDef.formatter` → raw string.
|
|
364
654
|
*/
|
|
365
655
|
displayValue = computed(() => {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
if (col.values?.length) {
|
|
369
|
-
const opt = col.values.find(v => typeof v === 'string' ? v === raw : v.value === raw);
|
|
370
|
-
if (opt !== undefined)
|
|
371
|
-
return typeof opt === 'string' ? opt : opt.label;
|
|
372
|
-
}
|
|
373
|
-
if (col.formatter)
|
|
374
|
-
return col.formatter(raw);
|
|
375
|
-
if (col.type === 'date' || looksLikeDate(raw)) {
|
|
376
|
-
return formatDateValue(raw, this.locale(), col.type === 'date');
|
|
377
|
-
}
|
|
378
|
-
return String(raw ?? '');
|
|
656
|
+
return this.displayValueOverride()
|
|
657
|
+
?? getDisplayForField(this.col(), this.value(), this.locale());
|
|
379
658
|
}, ...(ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []));
|
|
380
659
|
/**
|
|
381
660
|
* Index of the currently selected option in `valueOptions`.
|
|
@@ -398,16 +677,20 @@ class AgridCellComponent {
|
|
|
398
677
|
const initialValue = isDate
|
|
399
678
|
? dateInitial
|
|
400
679
|
: seed !== '' ? seed : this.value();
|
|
401
|
-
this.
|
|
680
|
+
const mask = this.resolvedInputMask();
|
|
681
|
+
const acceptedInitialValue = seed !== '' && mask && !matchesInputMask(initialValue, mask)
|
|
682
|
+
? this.value()
|
|
683
|
+
: initialValue;
|
|
684
|
+
this.draft.set(acceptedInitialValue);
|
|
402
685
|
setTimeout(() => {
|
|
403
686
|
const input = this.inputEl()?.nativeElement;
|
|
404
687
|
if (input) {
|
|
405
688
|
const displaySeed = isDate
|
|
406
689
|
? dateInitial
|
|
407
|
-
:
|
|
690
|
+
: String(acceptedInitialValue ?? '');
|
|
408
691
|
input.value = displaySeed;
|
|
409
692
|
input.focus();
|
|
410
|
-
if (input.type === 'text' && !seed)
|
|
693
|
+
if (input.type === 'text' && !seed && this.selectTextOnEdit())
|
|
411
694
|
input.select();
|
|
412
695
|
else if (input.type === 'text') {
|
|
413
696
|
const len = displaySeed.length;
|
|
@@ -432,14 +715,35 @@ class AgridCellComponent {
|
|
|
432
715
|
}
|
|
433
716
|
});
|
|
434
717
|
}
|
|
718
|
+
/** Toggle a boolean cell's value, driven entirely from data (no DOM toggle). */
|
|
719
|
+
onCheckboxClick(event) {
|
|
720
|
+
event.stopPropagation();
|
|
721
|
+
event.preventDefault();
|
|
722
|
+
if (!this.editable())
|
|
723
|
+
return;
|
|
724
|
+
this.booleanToggle.emit(!this.booleanChecked());
|
|
725
|
+
}
|
|
435
726
|
/** Emit a tree expand/collapse request without selecting or editing the cell. */
|
|
436
727
|
onTreeToggle(event) {
|
|
437
728
|
event.stopPropagation();
|
|
438
729
|
this.treeToggle.emit();
|
|
439
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
|
+
}
|
|
440
737
|
/** Forward `<input>` changes to the grid. */
|
|
441
738
|
onInput(event) {
|
|
442
|
-
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
|
+
}
|
|
443
747
|
const draft = this.col().type === 'date'
|
|
444
748
|
? coerceDateInputValue(val, this.value())
|
|
445
749
|
: val;
|
|
@@ -458,8 +762,18 @@ class AgridCellComponent {
|
|
|
458
762
|
this.draftChange.emit(rawValue);
|
|
459
763
|
}
|
|
460
764
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AgridCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
461
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", 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 }, 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 }, 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 } }, outputs: { treeToggle: "treeToggle", activate: "activate", startEdit: "startEdit", 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()", "attr.aria-readonly": "
|
|
462
|
-
@if (
|
|
765
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", 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: `
|
|
766
|
+
@if (booleanCell()) {
|
|
767
|
+
<input
|
|
768
|
+
type="checkbox"
|
|
769
|
+
class="ag-cell-checkbox"
|
|
770
|
+
[checked]="booleanChecked()"
|
|
771
|
+
[disabled]="!editable()"
|
|
772
|
+
[attr.aria-label]="col().header"
|
|
773
|
+
(click)="onCheckboxClick($event)"
|
|
774
|
+
(dblclick)="$event.stopPropagation()"
|
|
775
|
+
/>
|
|
776
|
+
} @else if (editing()) {
|
|
463
777
|
@if (col().values?.length) {
|
|
464
778
|
<select
|
|
465
779
|
#editSelect
|
|
@@ -474,11 +788,15 @@ class AgridCellComponent {
|
|
|
474
788
|
<input
|
|
475
789
|
#editInput
|
|
476
790
|
class="ag-cell-input"
|
|
791
|
+
[class.ag-cell-input--invalid]="!!error()"
|
|
477
792
|
[type]="col().type === 'date' ? 'date' : col().type === 'number' ? 'number' : 'text'"
|
|
478
793
|
[value]="editorValue()"
|
|
479
794
|
(input)="onInput($event)"
|
|
480
795
|
/>
|
|
481
796
|
}
|
|
797
|
+
@if (error(); as msg) {
|
|
798
|
+
<span class="ag-cell-error" role="alert">{{ msg }}</span>
|
|
799
|
+
}
|
|
482
800
|
} @else {
|
|
483
801
|
@if (treeCell()) {
|
|
484
802
|
<span class="ag-tree-prefix" [style.padding-left.px]="treeLevel() * treeIndent">
|
|
@@ -501,7 +819,18 @@ class AgridCellComponent {
|
|
|
501
819
|
<span class="ag-cell-value">{{ displayValue() }}</span>
|
|
502
820
|
}
|
|
503
821
|
}
|
|
504
|
-
|
|
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 });
|
|
505
834
|
}
|
|
506
835
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AgridCellComponent, decorators: [{
|
|
507
836
|
type: Component,
|
|
@@ -510,12 +839,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
510
839
|
'[class.selected]': 'selected()',
|
|
511
840
|
'[class.editing]': 'editing()',
|
|
512
841
|
'[class.ag-cell--tree]': 'treeCell()',
|
|
513
|
-
'[
|
|
842
|
+
'[class.ag-cell--with-info]': 'showInfoIcon() && !editing()',
|
|
843
|
+
'[attr.aria-readonly]': '!editable() ? "true" : null',
|
|
844
|
+
'[attr.title]': 'displayValue()',
|
|
514
845
|
'(click)': 'activate.emit($event)',
|
|
515
846
|
'(dblclick)': 'startEdit.emit()',
|
|
516
847
|
tabindex: '-1',
|
|
517
848
|
}, template: `
|
|
518
|
-
@if (
|
|
849
|
+
@if (booleanCell()) {
|
|
850
|
+
<input
|
|
851
|
+
type="checkbox"
|
|
852
|
+
class="ag-cell-checkbox"
|
|
853
|
+
[checked]="booleanChecked()"
|
|
854
|
+
[disabled]="!editable()"
|
|
855
|
+
[attr.aria-label]="col().header"
|
|
856
|
+
(click)="onCheckboxClick($event)"
|
|
857
|
+
(dblclick)="$event.stopPropagation()"
|
|
858
|
+
/>
|
|
859
|
+
} @else if (editing()) {
|
|
519
860
|
@if (col().values?.length) {
|
|
520
861
|
<select
|
|
521
862
|
#editSelect
|
|
@@ -530,11 +871,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
530
871
|
<input
|
|
531
872
|
#editInput
|
|
532
873
|
class="ag-cell-input"
|
|
874
|
+
[class.ag-cell-input--invalid]="!!error()"
|
|
533
875
|
[type]="col().type === 'date' ? 'date' : col().type === 'number' ? 'number' : 'text'"
|
|
534
876
|
[value]="editorValue()"
|
|
535
877
|
(input)="onInput($event)"
|
|
536
878
|
/>
|
|
537
879
|
}
|
|
880
|
+
@if (error(); as msg) {
|
|
881
|
+
<span class="ag-cell-error" role="alert">{{ msg }}</span>
|
|
882
|
+
}
|
|
538
883
|
} @else {
|
|
539
884
|
@if (treeCell()) {
|
|
540
885
|
<span class="ag-tree-prefix" [style.padding-left.px]="treeLevel() * treeIndent">
|
|
@@ -557,8 +902,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
557
902
|
<span class="ag-cell-value">{{ displayValue() }}</span>
|
|
558
903
|
}
|
|
559
904
|
}
|
|
560
|
-
|
|
561
|
-
|
|
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 }] }] } });
|
|
562
918
|
|
|
563
919
|
/** Guarded access to browser-only APIs used by the grid. @internal */
|
|
564
920
|
class AgridBrowserAdapter {
|
|
@@ -603,6 +959,10 @@ class AgridBrowserAdapter {
|
|
|
603
959
|
viewportWidth() {
|
|
604
960
|
return this.win?.innerWidth ?? Number.POSITIVE_INFINITY;
|
|
605
961
|
}
|
|
962
|
+
/** Returns the viewport height or infinity during server rendering. */
|
|
963
|
+
viewportHeight() {
|
|
964
|
+
return this.win?.innerHeight ?? Number.POSITIVE_INFINITY;
|
|
965
|
+
}
|
|
606
966
|
/** Returns computed styles when a window is available. */
|
|
607
967
|
computedStyle(element) {
|
|
608
968
|
return this.win?.getComputedStyle?.(element) ?? null;
|
|
@@ -746,7 +1106,7 @@ class AgridClipboardHandler {
|
|
|
746
1106
|
for (let colOffset = 0; colOffset < rows[rowOffset].length; colOffset++) {
|
|
747
1107
|
const colIndex = start.colIndex + colOffset;
|
|
748
1108
|
const col = cols[colIndex];
|
|
749
|
-
if (!col || !this.opts.isCellEditable(col))
|
|
1109
|
+
if (!col || !this.opts.isCellEditable(col, item.originalIndex))
|
|
750
1110
|
continue;
|
|
751
1111
|
const oldValue = dataSource.getRow(item.originalIndex)[col.field];
|
|
752
1112
|
const newValue = this.coercePastedValue(rows[rowOffset][colOffset], col);
|
|
@@ -1024,7 +1384,27 @@ const AGRID_LOCALE_TEXT = {
|
|
|
1024
1384
|
columnMenu: 'Column menu',
|
|
1025
1385
|
columns: 'Columns',
|
|
1026
1386
|
detail: 'Detail',
|
|
1387
|
+
toggleDetail: 'Toggle detail panel',
|
|
1027
1388
|
hiddenColumn: '(hidden)',
|
|
1389
|
+
filterCondition: 'Condition',
|
|
1390
|
+
filterConditionMenu: 'Filter condition',
|
|
1391
|
+
filterValue: 'Value',
|
|
1392
|
+
filterNoCondition: 'No condition',
|
|
1393
|
+
filterOpEquals: 'Equals',
|
|
1394
|
+
filterOpNotEquals: 'Not equal',
|
|
1395
|
+
filterOpGreater: 'Greater than',
|
|
1396
|
+
filterOpGreaterEqual: 'Greater or equal',
|
|
1397
|
+
filterOpLess: 'Less than',
|
|
1398
|
+
filterOpLessEqual: 'Less or equal',
|
|
1399
|
+
filterOpBetween: 'Between',
|
|
1400
|
+
filterOpBefore: 'Before',
|
|
1401
|
+
filterOpAfter: 'After',
|
|
1402
|
+
filterOpOn: 'On',
|
|
1403
|
+
filterOpLike: 'Like (% and _ wildcards)',
|
|
1404
|
+
filterOpStartsWith: 'Starts with',
|
|
1405
|
+
filterOpEndsWith: 'Ends with',
|
|
1406
|
+
filterOpIncludes: 'Includes',
|
|
1407
|
+
filterOpNotIncludes: 'Does not include',
|
|
1028
1408
|
copyCellValue: 'Copy cell',
|
|
1029
1409
|
copyRow: 'Copy row',
|
|
1030
1410
|
confirmDeleteRow: 'Sure to delete?',
|
|
@@ -1048,9 +1428,13 @@ const AGRID_LOCALE_TEXT = {
|
|
|
1048
1428
|
pinColumn: 'Pin left',
|
|
1049
1429
|
pinColumnRight: 'Pin right',
|
|
1050
1430
|
unpinColumnRight: 'Unpin right',
|
|
1431
|
+
pinRowTop: 'Pin row to top',
|
|
1432
|
+
pinRowBottom: 'Pin row to bottom',
|
|
1433
|
+
unpinRow: 'Unpin row',
|
|
1051
1434
|
previous: 'Previous',
|
|
1052
1435
|
resizeColumn: 'Resize column',
|
|
1053
1436
|
rows: count => `${count} ${count === 1 ? 'row' : 'rows'}`,
|
|
1437
|
+
quickFilterPlaceholder: 'Search all columns...',
|
|
1054
1438
|
searchValuesPlaceholder: 'Search values...',
|
|
1055
1439
|
selectAll: '(Select All)',
|
|
1056
1440
|
sortOnlyByThis: 'Sort only by this column',
|
|
@@ -1078,7 +1462,27 @@ const AGRID_LOCALE_TEXT = {
|
|
|
1078
1462
|
columnMenu: 'Spaltenmenü',
|
|
1079
1463
|
columns: 'Spalten',
|
|
1080
1464
|
detail: 'Details',
|
|
1465
|
+
toggleDetail: 'Detailbereich umschalten',
|
|
1081
1466
|
hiddenColumn: '(ausgeblendet)',
|
|
1467
|
+
filterCondition: 'Bedingung',
|
|
1468
|
+
filterConditionMenu: 'Filterbedingung',
|
|
1469
|
+
filterValue: 'Wert',
|
|
1470
|
+
filterNoCondition: 'Keine Bedingung',
|
|
1471
|
+
filterOpEquals: 'Gleich',
|
|
1472
|
+
filterOpNotEquals: 'Ungleich',
|
|
1473
|
+
filterOpGreater: 'Größer als',
|
|
1474
|
+
filterOpGreaterEqual: 'Größer oder gleich',
|
|
1475
|
+
filterOpLess: 'Kleiner als',
|
|
1476
|
+
filterOpLessEqual: 'Kleiner oder gleich',
|
|
1477
|
+
filterOpBetween: 'Zwischen',
|
|
1478
|
+
filterOpBefore: 'Vor',
|
|
1479
|
+
filterOpAfter: 'Nach',
|
|
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',
|
|
1082
1486
|
copyCellValue: 'Zelle kopieren',
|
|
1083
1487
|
copyRow: 'Zeile kopieren',
|
|
1084
1488
|
confirmDeleteRow: 'Wirklich löschen?',
|
|
@@ -1102,9 +1506,13 @@ const AGRID_LOCALE_TEXT = {
|
|
|
1102
1506
|
pinColumn: 'Links fixieren',
|
|
1103
1507
|
pinColumnRight: 'Rechts fixieren',
|
|
1104
1508
|
unpinColumnRight: 'Rechts lösen',
|
|
1509
|
+
pinRowTop: 'Zeile oben fixieren',
|
|
1510
|
+
pinRowBottom: 'Zeile unten fixieren',
|
|
1511
|
+
unpinRow: 'Zeilenfixierung lösen',
|
|
1105
1512
|
previous: 'Zurück',
|
|
1106
1513
|
resizeColumn: 'Spaltenbreite ändern',
|
|
1107
1514
|
rows: count => `${count} ${count === 1 ? 'Zeile' : 'Zeilen'}`,
|
|
1515
|
+
quickFilterPlaceholder: 'Alle Spalten durchsuchen...',
|
|
1108
1516
|
searchValuesPlaceholder: 'Werte suchen...',
|
|
1109
1517
|
selectAll: '(Alle auswählen)',
|
|
1110
1518
|
sortOnlyByThis: 'Nur nach dieser Spalte sortieren',
|
|
@@ -1146,14 +1554,27 @@ function resolveAgridLocaleText(locale, localizations) {
|
|
|
1146
1554
|
return { ...base, ...overrides };
|
|
1147
1555
|
}
|
|
1148
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
|
+
}
|
|
1149
1564
|
/** Floating column menu for sort, column actions, grouping, and value filters. */
|
|
1150
1565
|
class AgridColumnMenuComponent {
|
|
1566
|
+
elementRef = inject(ElementRef);
|
|
1567
|
+
destroyRef = inject(DestroyRef);
|
|
1568
|
+
browser = new AgridBrowserAdapter();
|
|
1569
|
+
resizeObserver = null;
|
|
1151
1570
|
/** Localized labels used in the menu. */
|
|
1152
1571
|
localeText = input(AGRID_LOCALE_TEXT.en, ...(ngDevMode ? [{ debugName: "localeText" }] : /* istanbul ignore next */ []));
|
|
1153
1572
|
/** Fixed viewport x-position for the menu. */
|
|
1154
1573
|
x = input.required(...(ngDevMode ? [{ debugName: "x" }] : /* istanbul ignore next */ []));
|
|
1155
1574
|
/** Fixed viewport y-position for the menu. */
|
|
1156
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 */ []));
|
|
1157
1578
|
/** Header label for the active column. */
|
|
1158
1579
|
header = input.required(...(ngDevMode ? [{ debugName: "header" }] : /* istanbul ignore next */ []));
|
|
1159
1580
|
/** Current sort direction for the active column. */
|
|
@@ -1172,6 +1593,20 @@ class AgridColumnMenuComponent {
|
|
|
1172
1593
|
filterable = input(false, ...(ngDevMode ? [{ debugName: "filterable" }] : /* istanbul ignore next */ []));
|
|
1173
1594
|
/** Whether to show the Excel-style distinct-value picker. */
|
|
1174
1595
|
showValueFilter = input(true, ...(ngDevMode ? [{ debugName: "showValueFilter" }] : /* istanbul ignore next */ []));
|
|
1596
|
+
/** Condition-filter input type for the active column, or `null` to hide the condition UI. */
|
|
1597
|
+
filterType = input(null, ...(ngDevMode ? [{ debugName: "filterType" }] : /* istanbul ignore next */ []));
|
|
1598
|
+
/** Current condition operator, or `null` when none is selected. */
|
|
1599
|
+
operator = input(null, ...(ngDevMode ? [{ debugName: "operator" }] : /* istanbul ignore next */ []));
|
|
1600
|
+
/** Current primary condition operand. */
|
|
1601
|
+
operand = input('', ...(ngDevMode ? [{ debugName: "operand" }] : /* istanbul ignore next */ []));
|
|
1602
|
+
/** Current secondary condition operand (used by `between`). */
|
|
1603
|
+
operand2 = input('', ...(ngDevMode ? [{ debugName: "operand2" }] : /* istanbul ignore next */ []));
|
|
1604
|
+
/** Emits the selected condition operator, or `null` to clear it. */
|
|
1605
|
+
operatorChange = output();
|
|
1606
|
+
/** Emits the primary condition operand text. */
|
|
1607
|
+
operandChange = output();
|
|
1608
|
+
/** Emits the secondary condition operand text (used by `between`). */
|
|
1609
|
+
operand2Change = output();
|
|
1175
1610
|
/** Current search text for the value-filter option list. */
|
|
1176
1611
|
search = input('', ...(ngDevMode ? [{ debugName: "search" }] : /* istanbul ignore next */ []));
|
|
1177
1612
|
/** Whether the value filter currently allows every value. */
|
|
@@ -1223,13 +1658,75 @@ class AgridColumnMenuComponent {
|
|
|
1223
1658
|
{ value: 'count', symbol: '#', label: t.aggregateCount },
|
|
1224
1659
|
];
|
|
1225
1660
|
}, ...(ngDevMode ? [{ debugName: "aggregateOptions" }] : /* istanbul ignore next */ []));
|
|
1661
|
+
/** Condition operators offered for the active column, labeled per column type. */
|
|
1662
|
+
operatorOptions = computed(() => {
|
|
1663
|
+
const t = this.localeText();
|
|
1664
|
+
if (this.filterType() === 'date') {
|
|
1665
|
+
return [
|
|
1666
|
+
{ value: 'eq', label: t.filterOpOn },
|
|
1667
|
+
{ value: 'lt', label: t.filterOpBefore },
|
|
1668
|
+
{ value: 'gt', label: t.filterOpAfter },
|
|
1669
|
+
{ value: 'between', label: t.filterOpBetween },
|
|
1670
|
+
];
|
|
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
|
+
}
|
|
1683
|
+
return [
|
|
1684
|
+
{ value: 'eq', label: t.filterOpEquals },
|
|
1685
|
+
{ value: 'neq', label: t.filterOpNotEquals },
|
|
1686
|
+
{ value: 'gt', label: t.filterOpGreater },
|
|
1687
|
+
{ value: 'gte', label: t.filterOpGreaterEqual },
|
|
1688
|
+
{ value: 'lt', label: t.filterOpLess },
|
|
1689
|
+
{ value: 'lte', label: t.filterOpLessEqual },
|
|
1690
|
+
{ value: 'between', label: t.filterOpBetween },
|
|
1691
|
+
];
|
|
1692
|
+
}, ...(ngDevMode ? [{ debugName: "operatorOptions" }] : /* istanbul ignore next */ []));
|
|
1693
|
+
/** Native input type for operand fields (date columns use a date picker). */
|
|
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
|
+
}
|
|
1226
1723
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AgridColumnMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1227
|
-
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 }, 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: { 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 (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}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1724
|
+
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]=\"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 });
|
|
1228
1725
|
}
|
|
1229
1726
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AgridColumnMenuComponent, decorators: [{
|
|
1230
1727
|
type: Component,
|
|
1231
|
-
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 (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}\n"] }]
|
|
1232
|
-
}], 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 }] }], 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"] }] } });
|
|
1233
1730
|
|
|
1234
1731
|
/** Owns column-menu state, filters, sorting, and menu-triggered column mutations. @internal */
|
|
1235
1732
|
class AgridColumnMenuController {
|
|
@@ -1287,6 +1784,10 @@ class AgridColumnMenuController {
|
|
|
1287
1784
|
const allowed = new Set(filter.selectedValues);
|
|
1288
1785
|
indices = indices.filter(index => allowed.has(String(rows[index][field] ?? '')));
|
|
1289
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
|
+
}
|
|
1290
1791
|
}
|
|
1291
1792
|
}
|
|
1292
1793
|
return new Set(indices.map(index => String(rows[index][menu.field] ?? '')));
|
|
@@ -1315,6 +1816,86 @@ class AgridColumnMenuController {
|
|
|
1315
1816
|
getTextFilter(field) {
|
|
1316
1817
|
return this.opts.control()?.getFilter(field).text ?? '';
|
|
1317
1818
|
}
|
|
1819
|
+
/** Returns the condition-filter input type for a filterable field. */
|
|
1820
|
+
getFilterType(field) {
|
|
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';
|
|
1825
|
+
}
|
|
1826
|
+
/** Returns the condition operator for a field, or `null`. */
|
|
1827
|
+
getFilterOperator(field) {
|
|
1828
|
+
return this.opts.control()?.getFilter(field).operator ?? null;
|
|
1829
|
+
}
|
|
1830
|
+
/** Returns the primary condition operand for a field. */
|
|
1831
|
+
getFilterOperand(field) {
|
|
1832
|
+
return this.opts.control()?.getFilter(field).operand ?? '';
|
|
1833
|
+
}
|
|
1834
|
+
/** Returns the secondary condition operand (used by `between`). */
|
|
1835
|
+
getFilterOperand2(field) {
|
|
1836
|
+
return this.opts.control()?.getFilter(field).operand2 ?? '';
|
|
1837
|
+
}
|
|
1838
|
+
/** Sets the condition operator; clearing it (`null`) also drops the operands. */
|
|
1839
|
+
setFilterOperator(field, operator) {
|
|
1840
|
+
const control = this.opts.control();
|
|
1841
|
+
if (!control)
|
|
1842
|
+
return;
|
|
1843
|
+
if (!operator) {
|
|
1844
|
+
control.setRangeFilter(field, null, null, null);
|
|
1845
|
+
}
|
|
1846
|
+
else {
|
|
1847
|
+
const current = control.getFilter(field);
|
|
1848
|
+
control.setRangeFilter(field, operator, current.operand ?? '', current.operand2 ?? '');
|
|
1849
|
+
}
|
|
1850
|
+
this.emitRangeServer(field, false);
|
|
1851
|
+
}
|
|
1852
|
+
/** Sets the primary condition operand. */
|
|
1853
|
+
setFilterOperand(field, value) {
|
|
1854
|
+
const control = this.opts.control();
|
|
1855
|
+
if (!control)
|
|
1856
|
+
return;
|
|
1857
|
+
const current = control.getFilter(field);
|
|
1858
|
+
control.setRangeFilter(field, current.operator ?? 'eq', value, current.operand2 ?? '');
|
|
1859
|
+
this.emitRangeServer(field, true);
|
|
1860
|
+
}
|
|
1861
|
+
/** Sets the secondary condition operand (used by `between`). */
|
|
1862
|
+
setFilterOperand2(field, value) {
|
|
1863
|
+
const control = this.opts.control();
|
|
1864
|
+
if (!control)
|
|
1865
|
+
return;
|
|
1866
|
+
const current = control.getFilter(field);
|
|
1867
|
+
control.setRangeFilter(field, current.operator ?? 'between', current.operand ?? '', value);
|
|
1868
|
+
this.emitRangeServer(field, true);
|
|
1869
|
+
}
|
|
1870
|
+
/**
|
|
1871
|
+
* In server-side filtering mode, emit a {@link FilterChangeEvent} carrying the current
|
|
1872
|
+
* condition so the host can refetch. Operand edits are debounced; operator changes are immediate.
|
|
1873
|
+
* No-op in client mode (the projection layer filters locally).
|
|
1874
|
+
*/
|
|
1875
|
+
emitRangeServer(field, debounce) {
|
|
1876
|
+
if (!this.opts.serverSideFiltering())
|
|
1877
|
+
return;
|
|
1878
|
+
const emit = () => {
|
|
1879
|
+
const f = this.opts.control()?.getFilter(field);
|
|
1880
|
+
this.opts.onFilterChange({
|
|
1881
|
+
field,
|
|
1882
|
+
value: '',
|
|
1883
|
+
operator: f?.operator ?? null,
|
|
1884
|
+
operand: f?.operand ?? null,
|
|
1885
|
+
operand2: f?.operand2 ?? null,
|
|
1886
|
+
});
|
|
1887
|
+
};
|
|
1888
|
+
this.cancelFilterDebounce(field);
|
|
1889
|
+
const delay = this.opts.filterDebounceMs();
|
|
1890
|
+
if (!debounce || delay === 0) {
|
|
1891
|
+
emit();
|
|
1892
|
+
return;
|
|
1893
|
+
}
|
|
1894
|
+
this.filterDebounces.set(field, setTimeout(() => {
|
|
1895
|
+
this.filterDebounces.delete(field);
|
|
1896
|
+
emit();
|
|
1897
|
+
}, delay));
|
|
1898
|
+
}
|
|
1318
1899
|
/** Returns the active sort direction, respecting disabled sorting. */
|
|
1319
1900
|
getSort(field) {
|
|
1320
1901
|
return this.opts.sortOption() === 'none'
|
|
@@ -1394,6 +1975,9 @@ class AgridColumnMenuController {
|
|
|
1394
1975
|
if (previous.selectedValues !== null) {
|
|
1395
1976
|
control.setSelectedValues(field, previous.selectedValues);
|
|
1396
1977
|
}
|
|
1978
|
+
if (previous.operator) {
|
|
1979
|
+
control.setRangeFilter(field, previous.operator, previous.operand ?? null, previous.operand2 ?? null);
|
|
1980
|
+
}
|
|
1397
1981
|
if (this.opts.serverSideFiltering()) {
|
|
1398
1982
|
this.opts.onSortChange({ field, direction: null });
|
|
1399
1983
|
}
|
|
@@ -1427,6 +2011,9 @@ class AgridColumnMenuController {
|
|
|
1427
2011
|
if (this.opts.serverSideFiltering()) {
|
|
1428
2012
|
if (previous.text)
|
|
1429
2013
|
this.opts.onFilterChange({ field, value: '' });
|
|
2014
|
+
if (previous.operator) {
|
|
2015
|
+
this.opts.onFilterChange({ field, value: '', operator: null, operand: null, operand2: null });
|
|
2016
|
+
}
|
|
1430
2017
|
if (previous.sort)
|
|
1431
2018
|
this.opts.onSortChange({ field, direction: null });
|
|
1432
2019
|
}
|
|
@@ -1468,6 +2055,9 @@ class AgridColumnMenuController {
|
|
|
1468
2055
|
for (const [field, filter] of Object.entries(previous)) {
|
|
1469
2056
|
if (filter.text)
|
|
1470
2057
|
this.opts.onFilterChange({ field, value: '' });
|
|
2058
|
+
if (filter.operator) {
|
|
2059
|
+
this.opts.onFilterChange({ field, value: '', operator: null, operand: null, operand2: null });
|
|
2060
|
+
}
|
|
1471
2061
|
if (filter.sort)
|
|
1472
2062
|
this.opts.onSortChange({ field, direction: null });
|
|
1473
2063
|
}
|
|
@@ -1704,7 +2294,7 @@ class AgridColumnReorderController {
|
|
|
1704
2294
|
const root = header?.closest('.ag-wrapper') ?? header?.ownerDocument;
|
|
1705
2295
|
if (!root)
|
|
1706
2296
|
return [];
|
|
1707
|
-
return
|
|
2297
|
+
return Array.from(root.querySelectorAll('.ag-header-cell[data-col-field]'))
|
|
1708
2298
|
.map(element => {
|
|
1709
2299
|
const field = element.dataset['colField'];
|
|
1710
2300
|
const rect = element.getBoundingClientRect();
|
|
@@ -1877,13 +2467,15 @@ class AgridColumnStateService {
|
|
|
1877
2467
|
constructor(opts) {
|
|
1878
2468
|
this.opts = opts;
|
|
1879
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 */ []));
|
|
1880
2472
|
/** Finds a column definition by field name. */
|
|
1881
2473
|
getColDef(field) {
|
|
1882
|
-
return this.
|
|
2474
|
+
return this.columnsByField().get(field);
|
|
1883
2475
|
}
|
|
1884
2476
|
/** Returns a field's zero-based index among visible columns. */
|
|
1885
2477
|
getVisibleColIndex(field) {
|
|
1886
|
-
return this.
|
|
2478
|
+
return this.visibleIndexByField().get(field) ?? -1;
|
|
1887
2479
|
}
|
|
1888
2480
|
/** Converts a visible data-column index to a one-based ARIA index. */
|
|
1889
2481
|
getAriaColIndex(colIndex) {
|
|
@@ -2098,22 +2690,39 @@ class AgridEditController {
|
|
|
2098
2690
|
editingCell = signal(null, ...(ngDevMode ? [{ debugName: "editingCell" }] : /* istanbul ignore next */ []));
|
|
2099
2691
|
currentDraft = signal(null, ...(ngDevMode ? [{ debugName: "currentDraft" }] : /* istanbul ignore next */ []));
|
|
2100
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 */ []));
|
|
2695
|
+
/** The active cell validation error, or `null` when the last commit was accepted. */
|
|
2696
|
+
validationError = signal(null, ...(ngDevMode ? [{ debugName: "validationError" }] : /* istanbul ignore next */ []));
|
|
2101
2697
|
constructor(opts) {
|
|
2102
2698
|
this.opts = opts;
|
|
2103
2699
|
}
|
|
2104
|
-
/** Returns whether a
|
|
2105
|
-
isCellEditable(col) {
|
|
2106
|
-
|
|
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
|
+
});
|
|
2107
2715
|
}
|
|
2108
2716
|
/** Replaces the value currently staged by the active editor. */
|
|
2109
2717
|
setDraft(value) {
|
|
2110
2718
|
this.currentDraft.set(value);
|
|
2111
2719
|
}
|
|
2112
2720
|
/** Starts editing a cell, optionally seeded by a typed character. */
|
|
2113
|
-
start(originalIndex, colIndex, seedChar) {
|
|
2721
|
+
start(originalIndex, colIndex, seedChar, selectText = true) {
|
|
2114
2722
|
const col = this.opts.visibleColDefs()[colIndex];
|
|
2115
|
-
if (!this.isCellEditable(col))
|
|
2723
|
+
if (!this.isCellEditable(col, originalIndex))
|
|
2116
2724
|
return;
|
|
2725
|
+
this.validationError.set(null);
|
|
2117
2726
|
const currentValue = this.opts.dataSource().getRow(originalIndex)[col.field];
|
|
2118
2727
|
this.opts.selectedRange.set(null);
|
|
2119
2728
|
this.opts.selectedCell.set({ rowIndex: originalIndex, colIndex });
|
|
@@ -2129,24 +2738,40 @@ class AgridEditController {
|
|
|
2129
2738
|
}
|
|
2130
2739
|
this.currentDraft.set(initialDraft);
|
|
2131
2740
|
this.editSeedChar.set(seedChar);
|
|
2741
|
+
this.selectTextOnEdit.set(selectText);
|
|
2132
2742
|
this.editingCell.set({ rowIndex: originalIndex, colIndex });
|
|
2133
2743
|
const displayIndex = this.opts.findDisplayIndex(originalIndex);
|
|
2134
2744
|
if (displayIndex >= 0)
|
|
2135
2745
|
this.opts.scrollToCell(displayIndex, colIndex);
|
|
2136
2746
|
}
|
|
2137
|
-
/**
|
|
2747
|
+
/**
|
|
2748
|
+
* Commits the staged value and records the edit in history.
|
|
2749
|
+
* Returns `false` (keeping the cell in edit mode) when a `ColDef.validate` hook rejects the
|
|
2750
|
+
* value; `true` when the edit is committed or there was no change.
|
|
2751
|
+
*/
|
|
2138
2752
|
commit() {
|
|
2139
2753
|
const position = this.editingCell();
|
|
2140
2754
|
if (!position)
|
|
2141
|
-
return;
|
|
2755
|
+
return true;
|
|
2142
2756
|
const col = this.opts.visibleColDefs()[position.colIndex];
|
|
2143
2757
|
if (!col) {
|
|
2144
2758
|
this.cancel();
|
|
2145
|
-
return;
|
|
2759
|
+
return true;
|
|
2760
|
+
}
|
|
2761
|
+
const row = this.opts.dataSource().getRow(position.rowIndex);
|
|
2762
|
+
if (!this.isCellEditable(col, position.rowIndex)) {
|
|
2763
|
+
this.cancel();
|
|
2764
|
+
return true;
|
|
2146
2765
|
}
|
|
2147
|
-
const oldValue =
|
|
2766
|
+
const oldValue = row[col.field];
|
|
2148
2767
|
const newValue = this.currentDraft();
|
|
2149
2768
|
if (oldValue !== newValue) {
|
|
2769
|
+
const message = col.validate?.(newValue, row) ?? null;
|
|
2770
|
+
if (message) {
|
|
2771
|
+
this.validationError.set({ ...position, field: col.field, message });
|
|
2772
|
+
this.opts.onValidationFailed({ rowIndex: position.rowIndex, field: col.field, value: newValue, message });
|
|
2773
|
+
return false;
|
|
2774
|
+
}
|
|
2150
2775
|
this.opts.dataSource().patchRow(position.rowIndex, { [col.field]: newValue });
|
|
2151
2776
|
this.opts.control()?.pushEdit({
|
|
2152
2777
|
rowIndex: position.rowIndex,
|
|
@@ -2156,11 +2781,43 @@ class AgridEditController {
|
|
|
2156
2781
|
});
|
|
2157
2782
|
this.opts.onCellEdit({ position, field: col.field, oldValue, newValue });
|
|
2158
2783
|
}
|
|
2784
|
+
this.validationError.set(null);
|
|
2159
2785
|
this.clearEditState();
|
|
2160
2786
|
this.opts.focusGrid();
|
|
2787
|
+
return true;
|
|
2788
|
+
}
|
|
2789
|
+
/**
|
|
2790
|
+
* Commit a value directly to a cell without entering edit mode (e.g. a boolean checkbox
|
|
2791
|
+
* toggle). Records the change in history and emits the edit just like {@link commit}.
|
|
2792
|
+
*/
|
|
2793
|
+
setCellValue(rowIndex, colIndex, newValue) {
|
|
2794
|
+
const col = this.opts.visibleColDefs()[colIndex];
|
|
2795
|
+
if (!this.isCellEditable(col, rowIndex))
|
|
2796
|
+
return false;
|
|
2797
|
+
const row = this.opts.dataSource().getRow(rowIndex);
|
|
2798
|
+
const oldValue = row[col.field];
|
|
2799
|
+
if (oldValue === newValue)
|
|
2800
|
+
return true;
|
|
2801
|
+
const message = col.validate?.(newValue, row) ?? null;
|
|
2802
|
+
if (message) {
|
|
2803
|
+
this.validationError.set({ rowIndex, colIndex, field: col.field, message });
|
|
2804
|
+
this.opts.onValidationFailed({ rowIndex, field: col.field, value: newValue, message });
|
|
2805
|
+
return false;
|
|
2806
|
+
}
|
|
2807
|
+
this.opts.dataSource().patchRow(rowIndex, { [col.field]: newValue });
|
|
2808
|
+
this.opts.control()?.pushEdit({ rowIndex, field: col.field, oldValue, newValue });
|
|
2809
|
+
this.opts.onCellEdit({
|
|
2810
|
+
position: { rowIndex, colIndex },
|
|
2811
|
+
field: col.field,
|
|
2812
|
+
oldValue,
|
|
2813
|
+
newValue,
|
|
2814
|
+
});
|
|
2815
|
+
this.validationError.set(null);
|
|
2816
|
+
return true;
|
|
2161
2817
|
}
|
|
2162
2818
|
/** Discards the active edit without changing row data. */
|
|
2163
2819
|
cancel() {
|
|
2820
|
+
this.validationError.set(null);
|
|
2164
2821
|
this.clearEditState();
|
|
2165
2822
|
}
|
|
2166
2823
|
/** Applies the previous edit-history value to the data source. */
|
|
@@ -2205,6 +2862,7 @@ class AgridEditController {
|
|
|
2205
2862
|
clearEditState() {
|
|
2206
2863
|
this.editingCell.set(null);
|
|
2207
2864
|
this.editSeedChar.set('');
|
|
2865
|
+
this.selectTextOnEdit.set(true);
|
|
2208
2866
|
}
|
|
2209
2867
|
}
|
|
2210
2868
|
|
|
@@ -2219,15 +2877,17 @@ class AgridFindController {
|
|
|
2219
2877
|
if (!query)
|
|
2220
2878
|
return [];
|
|
2221
2879
|
const cols = this.opts.visibleColDefs();
|
|
2880
|
+
const rows = this.opts.dataSource().rows();
|
|
2222
2881
|
const matches = [];
|
|
2223
|
-
this.opts.
|
|
2224
|
-
|
|
2882
|
+
this.opts.filteredSortedIndices().forEach(rowIndex => {
|
|
2883
|
+
const row = rows[rowIndex];
|
|
2884
|
+
if (!row)
|
|
2225
2885
|
return;
|
|
2226
2886
|
for (let colIndex = 0; colIndex < cols.length; colIndex++) {
|
|
2227
2887
|
const col = cols[colIndex];
|
|
2228
|
-
const value = getDisplayForField(col,
|
|
2888
|
+
const value = getDisplayForField(col, row[col.field], this.opts.locale()).toLowerCase();
|
|
2229
2889
|
if (value.includes(query)) {
|
|
2230
|
-
matches.push({ rowIndex
|
|
2890
|
+
matches.push({ rowIndex, colIndex });
|
|
2231
2891
|
}
|
|
2232
2892
|
}
|
|
2233
2893
|
});
|
|
@@ -2238,6 +2898,9 @@ class AgridFindController {
|
|
|
2238
2898
|
}
|
|
2239
2899
|
/** Opens the find panel. */
|
|
2240
2900
|
show() {
|
|
2901
|
+
this.opts.selectedCell.set(null);
|
|
2902
|
+
this.opts.selectedRange.set(null);
|
|
2903
|
+
this.activeIndex.set(-1);
|
|
2241
2904
|
this.open.set(true);
|
|
2242
2905
|
}
|
|
2243
2906
|
/** Closes the find panel and returns focus to the grid. */
|
|
@@ -2245,11 +2908,10 @@ class AgridFindController {
|
|
|
2245
2908
|
this.open.set(false);
|
|
2246
2909
|
this.opts.focusGrid();
|
|
2247
2910
|
}
|
|
2248
|
-
/** Replaces the search query
|
|
2911
|
+
/** Replaces the search query without moving focus away from the find input. */
|
|
2249
2912
|
setQuery(value) {
|
|
2250
2913
|
this.query.set(value);
|
|
2251
2914
|
this.activeIndex.set(-1);
|
|
2252
|
-
this.goToMatch(1);
|
|
2253
2915
|
}
|
|
2254
2916
|
/** Selects the next or previous match, wrapping at either end. */
|
|
2255
2917
|
goToMatch(direction) {
|
|
@@ -2266,7 +2928,7 @@ class AgridFindController {
|
|
|
2266
2928
|
const match = matches[next];
|
|
2267
2929
|
this.opts.selectedRange.set(null);
|
|
2268
2930
|
this.opts.selectedCell.set({ rowIndex: match.rowIndex, colIndex: match.colIndex });
|
|
2269
|
-
this.opts.
|
|
2931
|
+
this.opts.revealMatch(match.rowIndex, match.colIndex);
|
|
2270
2932
|
}
|
|
2271
2933
|
/** Returns whether a cell matches the current query. */
|
|
2272
2934
|
isMatchCell(originalIndex, colIndex) {
|
|
@@ -2490,7 +3152,7 @@ class AgridNavigationController {
|
|
|
2490
3152
|
}
|
|
2491
3153
|
/** Handles grid-level navigation, editing, history, find, and add-row shortcuts. */
|
|
2492
3154
|
handleKeyDown(event) {
|
|
2493
|
-
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'))
|
|
2494
3156
|
return;
|
|
2495
3157
|
if ((event.ctrlKey || event.metaKey) && !event.altKey && event.key.toLowerCase() === 'f') {
|
|
2496
3158
|
event.preventDefault();
|
|
@@ -2509,17 +3171,27 @@ class AgridNavigationController {
|
|
|
2509
3171
|
return;
|
|
2510
3172
|
}
|
|
2511
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
|
+
}
|
|
2512
3184
|
if (this.opts.editingCell()) {
|
|
2513
3185
|
switch (event.key) {
|
|
2514
3186
|
case 'Tab':
|
|
2515
3187
|
event.preventDefault();
|
|
2516
|
-
this.opts.commitEdit()
|
|
2517
|
-
|
|
3188
|
+
if (this.opts.commitEdit())
|
|
3189
|
+
this.moveSelection(0, event.shiftKey ? -1 : 1);
|
|
2518
3190
|
break;
|
|
2519
3191
|
case 'Enter':
|
|
2520
3192
|
event.preventDefault();
|
|
2521
|
-
this.opts.commitEdit()
|
|
2522
|
-
|
|
3193
|
+
if (this.opts.commitEdit())
|
|
3194
|
+
this.applyEnterEditAction(event.shiftKey);
|
|
2523
3195
|
break;
|
|
2524
3196
|
case 'Escape':
|
|
2525
3197
|
event.preventDefault();
|
|
@@ -2529,7 +3201,6 @@ class AgridNavigationController {
|
|
|
2529
3201
|
}
|
|
2530
3202
|
return;
|
|
2531
3203
|
}
|
|
2532
|
-
const sel = this.opts.selectedCell();
|
|
2533
3204
|
const isOnAddRow = this.opts.allowAddRows()
|
|
2534
3205
|
&& !this.opts.autoAddRows()
|
|
2535
3206
|
&& sel?.rowIndex === this.opts.dataSource().length;
|
|
@@ -2555,6 +3226,14 @@ class AgridNavigationController {
|
|
|
2555
3226
|
this.moveSelection(0, event.shiftKey ? -1 : 1);
|
|
2556
3227
|
break;
|
|
2557
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;
|
|
2558
3237
|
case 'F2':
|
|
2559
3238
|
event.preventDefault();
|
|
2560
3239
|
if (sel) {
|
|
@@ -2573,6 +3252,63 @@ class AgridNavigationController {
|
|
|
2573
3252
|
}
|
|
2574
3253
|
}
|
|
2575
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
|
+
}
|
|
2576
3312
|
/** Finds a data row's current index in the projected display items. */
|
|
2577
3313
|
findDisplayIndex(originalIndex) {
|
|
2578
3314
|
return this.opts.filteredItems().findIndex(item => isDataRowItem(item) && item.originalIndex === originalIndex);
|
|
@@ -2722,6 +3458,10 @@ class AgridPresentationService {
|
|
|
2722
3458
|
getCellClass(col, value, row) {
|
|
2723
3459
|
return col.cellClass?.({ value, row }) ?? '';
|
|
2724
3460
|
}
|
|
3461
|
+
/** Resolves dynamic CSS classes configured for a whole data row. */
|
|
3462
|
+
getRowClass(row, index) {
|
|
3463
|
+
return this.opts.getRowClass?.()?.({ row, index }) ?? '';
|
|
3464
|
+
}
|
|
2725
3465
|
/** Returns the compact label for a built-in aggregate. */
|
|
2726
3466
|
getAggregateLabel(col) {
|
|
2727
3467
|
const aggregate = this.opts.control()?.aggregates()[col.field] ?? col.aggregate;
|
|
@@ -2761,6 +3501,91 @@ class AgridPresentationService {
|
|
|
2761
3501
|
}
|
|
2762
3502
|
}
|
|
2763
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.15", ngImport: i0, type: AgridMenuBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3582
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", 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.15", 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
|
+
|
|
2764
3589
|
/**
|
|
2765
3590
|
* Signal-based container for mutable grid UI state such as column widths and active filters.
|
|
2766
3591
|
*
|
|
@@ -2781,6 +3606,7 @@ class AgridPresentationService {
|
|
|
2781
3606
|
class AgridControl {
|
|
2782
3607
|
_columnWidths = signal({}, ...(ngDevMode ? [{ debugName: "_columnWidths" }] : /* istanbul ignore next */ []));
|
|
2783
3608
|
_filters = signal({}, ...(ngDevMode ? [{ debugName: "_filters" }] : /* istanbul ignore next */ []));
|
|
3609
|
+
_quickFilter = signal('', ...(ngDevMode ? [{ debugName: "_quickFilter" }] : /* istanbul ignore next */ []));
|
|
2784
3610
|
_allowRowReorder = signal(false, ...(ngDevMode ? [{ debugName: "_allowRowReorder" }] : /* istanbul ignore next */ []));
|
|
2785
3611
|
_groupByField = signal(null, ...(ngDevMode ? [{ debugName: "_groupByField" }] : /* istanbul ignore next */ []));
|
|
2786
3612
|
_hiddenColumns = signal(new Set(), ...(ngDevMode ? [{ debugName: "_hiddenColumns" }] : /* istanbul ignore next */ []));
|
|
@@ -2798,6 +3624,8 @@ class AgridControl {
|
|
|
2798
3624
|
this._columnWidths.set({ ...state.columnWidths });
|
|
2799
3625
|
if (state?.filters)
|
|
2800
3626
|
this._filters.set({ ...state.filters });
|
|
3627
|
+
if (state?.quickFilter)
|
|
3628
|
+
this._quickFilter.set(state.quickFilter);
|
|
2801
3629
|
if (state?.allowRowReorder)
|
|
2802
3630
|
this._allowRowReorder.set(state.allowRowReorder);
|
|
2803
3631
|
if (state?.groupByField !== undefined)
|
|
@@ -2842,6 +3670,16 @@ class AgridControl {
|
|
|
2842
3670
|
setGroupBy(field) {
|
|
2843
3671
|
this._groupByField.set(field);
|
|
2844
3672
|
}
|
|
3673
|
+
/**
|
|
3674
|
+
* Global quick-filter text. When non-empty (and not in server-side filtering mode),
|
|
3675
|
+
* the grid keeps only rows where at least one visible column's display value contains
|
|
3676
|
+
* this text (case-insensitive).
|
|
3677
|
+
*/
|
|
3678
|
+
quickFilter = this._quickFilter.asReadonly();
|
|
3679
|
+
/** Set the global quick-filter text. Pass an empty string to clear it. */
|
|
3680
|
+
setQuickFilter(text) {
|
|
3681
|
+
this._quickFilter.set(text);
|
|
3682
|
+
}
|
|
2845
3683
|
/**
|
|
2846
3684
|
* Reactive set of field names that are currently hidden.
|
|
2847
3685
|
* An empty set means all columns are visible.
|
|
@@ -3114,9 +3952,21 @@ class AgridControl {
|
|
|
3114
3952
|
[field]: { ...this.getFilter(field), selectedValues: values },
|
|
3115
3953
|
}));
|
|
3116
3954
|
}
|
|
3117
|
-
/**
|
|
3118
|
-
|
|
3119
|
-
|
|
3955
|
+
/**
|
|
3956
|
+
* Set a column condition filter. Text columns support string operators while number/date
|
|
3957
|
+
* columns support comparison operators.
|
|
3958
|
+
* Pass `operator: null` (or an empty `operand`) to clear it. `operand2` is only used
|
|
3959
|
+
* by the `'between'` operator.
|
|
3960
|
+
*/
|
|
3961
|
+
setRangeFilter(field, operator, operand, operand2 = null) {
|
|
3962
|
+
this._filters.update(f => ({
|
|
3963
|
+
...f,
|
|
3964
|
+
[field]: { ...this.getFilter(field), operator, operand, operand2 },
|
|
3965
|
+
}));
|
|
3966
|
+
}
|
|
3967
|
+
/** Ordered list of sorted field names, from highest to lowest priority. */
|
|
3968
|
+
sortOrder = this._sortOrder.asReadonly();
|
|
3969
|
+
/** Return the 1-based sort priority of a field, or `0` if it is not sorted. */
|
|
3120
3970
|
getSortPriority(field) {
|
|
3121
3971
|
const idx = this._sortOrder().indexOf(field);
|
|
3122
3972
|
return idx === -1 ? 0 : idx + 1;
|
|
@@ -3161,10 +4011,11 @@ class AgridControl {
|
|
|
3161
4011
|
});
|
|
3162
4012
|
this._sortOrder.update(o => o.filter(f => f !== field));
|
|
3163
4013
|
}
|
|
3164
|
-
/** Remove all active filters and sorts for every column. */
|
|
4014
|
+
/** Remove all active filters and sorts for every column, including the quick filter. */
|
|
3165
4015
|
clearAllFilters() {
|
|
3166
4016
|
this._filters.set({});
|
|
3167
4017
|
this._sortOrder.set([]);
|
|
4018
|
+
this._quickFilter.set('');
|
|
3168
4019
|
}
|
|
3169
4020
|
/**
|
|
3170
4021
|
* Return `true` when the given field has any active filter or sort.
|
|
@@ -3172,11 +4023,13 @@ class AgridControl {
|
|
|
3172
4023
|
*/
|
|
3173
4024
|
hasActiveFilter(field) {
|
|
3174
4025
|
const f = this.getFilter(field);
|
|
3175
|
-
|
|
4026
|
+
const hasRange = !!f.operator && f.operand != null && f.operand !== '';
|
|
4027
|
+
return !!(f.text || f.selectedValues !== null || f.sort || hasRange);
|
|
3176
4028
|
}
|
|
3177
|
-
/** Return `true` when ANY column has an active filter or sort. */
|
|
4029
|
+
/** Return `true` when the quick filter or ANY column has an active filter or sort. */
|
|
3178
4030
|
hasAnyActiveFilter() {
|
|
3179
|
-
return Object.values(this._filters()).some(f => f.text || f.selectedValues !== null || f.sort
|
|
4031
|
+
return !!this._quickFilter() || Object.values(this._filters()).some(f => f.text || f.selectedValues !== null || f.sort
|
|
4032
|
+
|| (!!f.operator && f.operand != null && f.operand !== ''));
|
|
3180
4033
|
}
|
|
3181
4034
|
// ── Serialization ──────────────────────────────────────────────────────────
|
|
3182
4035
|
/** Serialize current state to a plain object suitable for JSON storage. */
|
|
@@ -3184,6 +4037,7 @@ class AgridControl {
|
|
|
3184
4037
|
return {
|
|
3185
4038
|
columnWidths: { ...this._columnWidths() },
|
|
3186
4039
|
filters: { ...this._filters() },
|
|
4040
|
+
quickFilter: this._quickFilter() || undefined,
|
|
3187
4041
|
allowRowReorder: this._allowRowReorder(),
|
|
3188
4042
|
groupByField: this._groupByField() ?? undefined,
|
|
3189
4043
|
hiddenColumns: [...this._hiddenColumns()],
|
|
@@ -3401,12 +4255,18 @@ class AgridProvider {
|
|
|
3401
4255
|
serverSideFiltering;
|
|
3402
4256
|
/** Delay before server-side filter events are emitted. */
|
|
3403
4257
|
filterDebounceMs;
|
|
4258
|
+
/** Whether the global quick-filter box is shown above the grid. */
|
|
4259
|
+
enableQuickFilter;
|
|
4260
|
+
/** Commands rendered in the optional menu bar above the column headers. */
|
|
4261
|
+
menuBarItems;
|
|
3404
4262
|
/** Enabled sorting mode. */
|
|
3405
4263
|
sortOption;
|
|
3406
4264
|
/** Toggle auto-add-rows without recreating the provider. @default signal(false) */
|
|
3407
4265
|
autoAddRows;
|
|
3408
4266
|
/** Enabled row-selection mode. */
|
|
3409
4267
|
rowSelection;
|
|
4268
|
+
/** Behavior after pressing Enter while an inline cell editor is active. */
|
|
4269
|
+
enterEditAction;
|
|
3410
4270
|
/** Optional description shown beside each group heading. */
|
|
3411
4271
|
groupDescription;
|
|
3412
4272
|
/** Actions available from group headers. */
|
|
@@ -3423,6 +4283,16 @@ class AgridProvider {
|
|
|
3423
4283
|
emptyText;
|
|
3424
4284
|
/** Whether edits are restricted to the sidebar editor. */
|
|
3425
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;
|
|
3426
4296
|
/** Toggle the loading overlay without recreating the provider. @default signal(false) */
|
|
3427
4297
|
loading;
|
|
3428
4298
|
/** Toggle readonly mode without recreating the provider. @default signal(false) */
|
|
@@ -3446,8 +4316,11 @@ class AgridProvider {
|
|
|
3446
4316
|
this.autoOpenDetail = config.autoOpenDetail ?? false;
|
|
3447
4317
|
this.serverSideFiltering = config.serverSideFiltering ?? false;
|
|
3448
4318
|
this.filterDebounceMs = Math.max(0, config.filterDebounceMs ?? 300);
|
|
4319
|
+
this.enableQuickFilter = config.enableQuickFilter ?? false;
|
|
4320
|
+
this.menuBarItems = config.menuBarItems ?? [];
|
|
3449
4321
|
this.sortOption = config.sortOption ?? 'multi';
|
|
3450
4322
|
this.rowSelection = config.rowSelection ?? 'none';
|
|
4323
|
+
this.enterEditAction = config.enterEditAction ?? 'nextRow';
|
|
3451
4324
|
this.groupDescription = config.groupDescription ?? null;
|
|
3452
4325
|
this.groupActions = config.groupActions ?? [];
|
|
3453
4326
|
this.cellMenuItems = config.cellMenuItems ?? [];
|
|
@@ -3458,6 +4331,11 @@ class AgridProvider {
|
|
|
3458
4331
|
this.loading = signal(config.loading ?? false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
3459
4332
|
this.readonlyGrid = signal(config.readonly ?? false, ...(ngDevMode ? [{ debugName: "readonlyGrid" }] : /* istanbul ignore next */ []));
|
|
3460
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;
|
|
3461
4339
|
}
|
|
3462
4340
|
/** Returns the current reactive row array. */
|
|
3463
4341
|
getGridData() {
|
|
@@ -3484,6 +4362,10 @@ class AgridProjectionModel {
|
|
|
3484
4362
|
return indices;
|
|
3485
4363
|
const filters = control.filters();
|
|
3486
4364
|
indices = applyTextAndValueFilters(rows, indices, filters, colMap, this.opts.locale());
|
|
4365
|
+
const quick = control.quickFilter();
|
|
4366
|
+
if (quick) {
|
|
4367
|
+
indices = applyQuickFilter(rows, indices, quick, this.opts.visibleColDefs(), this.opts.locale());
|
|
4368
|
+
}
|
|
3487
4369
|
if (control.groupByField())
|
|
3488
4370
|
return indices;
|
|
3489
4371
|
const sortEntries = this.sortEntries(filters);
|
|
@@ -3493,6 +4375,41 @@ class AgridProjectionModel {
|
|
|
3493
4375
|
}, ...(ngDevMode ? [{ debugName: "filteredSortedIndices" }] : /* istanbul ignore next */ []));
|
|
3494
4376
|
/** Total filtered row count, unaffected by client-side pagination. */
|
|
3495
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 */ []));
|
|
3496
4413
|
/** All source indices in active sort order, ignoring filters. Used for tree ancestor ordering. */
|
|
3497
4414
|
allSortedIndices = computed(() => {
|
|
3498
4415
|
const rows = this.opts.dataSource().rows();
|
|
@@ -3529,43 +4446,7 @@ class AgridProjectionModel {
|
|
|
3529
4446
|
return this.opts.visibleColDefs().some(col => col.aggregate || aggregates[col.field]);
|
|
3530
4447
|
}, ...(ngDevMode ? [{ debugName: "showFooter" }] : /* istanbul ignore next */ []));
|
|
3531
4448
|
/** Aggregate values computed over all filtered rows, before pagination. */
|
|
3532
|
-
footerValues = computed(() => {
|
|
3533
|
-
const rows = this.opts.dataSource().rows();
|
|
3534
|
-
const indices = this.filteredSortedIndices();
|
|
3535
|
-
const controlAggregates = this.opts.control()?.aggregates() ?? {};
|
|
3536
|
-
const result = {};
|
|
3537
|
-
for (const col of this.opts.visibleColDefs()) {
|
|
3538
|
-
const aggregate = controlAggregates[col.field] ?? col.aggregate;
|
|
3539
|
-
if (!aggregate)
|
|
3540
|
-
continue;
|
|
3541
|
-
const values = indices.map(index => rows[index][col.field]);
|
|
3542
|
-
if (typeof aggregate === 'function') {
|
|
3543
|
-
result[col.field] = aggregate(values);
|
|
3544
|
-
continue;
|
|
3545
|
-
}
|
|
3546
|
-
const numbers = values.map(Number).filter(value => !Number.isNaN(value));
|
|
3547
|
-
switch (aggregate) {
|
|
3548
|
-
case 'sum':
|
|
3549
|
-
result[col.field] = numbers.reduce((sum, value) => sum + value, 0);
|
|
3550
|
-
break;
|
|
3551
|
-
case 'avg':
|
|
3552
|
-
result[col.field] = numbers.length
|
|
3553
|
-
? numbers.reduce((sum, value) => sum + value, 0) / numbers.length
|
|
3554
|
-
: null;
|
|
3555
|
-
break;
|
|
3556
|
-
case 'min':
|
|
3557
|
-
result[col.field] = numbers.length ? Math.min(...numbers) : null;
|
|
3558
|
-
break;
|
|
3559
|
-
case 'max':
|
|
3560
|
-
result[col.field] = numbers.length ? Math.max(...numbers) : null;
|
|
3561
|
-
break;
|
|
3562
|
-
case 'count':
|
|
3563
|
-
result[col.field] = values.filter(value => value != null && value !== '').length;
|
|
3564
|
-
break;
|
|
3565
|
-
}
|
|
3566
|
-
}
|
|
3567
|
-
return result;
|
|
3568
|
-
}, ...(ngDevMode ? [{ debugName: "footerValues" }] : /* istanbul ignore next */ []));
|
|
4449
|
+
footerValues = computed(() => computeAggregates(this.opts.dataSource().rows(), this.filteredSortedIndices(), this.opts.visibleColDefs(), this.opts.control()?.aggregates() ?? {}), ...(ngDevMode ? [{ debugName: "footerValues" }] : /* istanbul ignore next */ []));
|
|
3569
4450
|
/** Filtered, sorted, paginated, and optionally grouped virtual-scroll items. */
|
|
3570
4451
|
filteredItems = computed(() => {
|
|
3571
4452
|
const rows = this.opts.dataSource().rows();
|
|
@@ -3581,6 +4462,11 @@ class AgridProjectionModel {
|
|
|
3581
4462
|
const filters = control?.filters() ?? {};
|
|
3582
4463
|
const filterActive = !this.opts.serverSideFiltering()
|
|
3583
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
|
+
}
|
|
3584
4470
|
if (filterActive && treeConfig.keepAncestorsOnFilter !== false) {
|
|
3585
4471
|
const { getId, getParentId } = treeConfig;
|
|
3586
4472
|
const idToIndex = new Map();
|
|
@@ -3599,14 +4485,19 @@ class AgridProjectionModel {
|
|
|
3599
4485
|
}
|
|
3600
4486
|
}
|
|
3601
4487
|
const ordered = this.allSortedIndices().filter(index => visible.has(index));
|
|
3602
|
-
const items = buildTreeItems(rows, ordered, treeConfig, expandedIds, forced);
|
|
4488
|
+
const items = this.appendTreeDetailItems(buildTreeItems(rows, ordered, treeConfig, expandedIds, forced), rows, treeConfig);
|
|
3603
4489
|
this.appendAddRow(items);
|
|
3604
4490
|
return items;
|
|
3605
4491
|
}
|
|
3606
|
-
const items = buildTreeItems(rows, indices, treeConfig, expandedIds);
|
|
4492
|
+
const items = this.appendTreeDetailItems(buildTreeItems(rows, indices, treeConfig, expandedIds), rows, treeConfig);
|
|
3607
4493
|
this.appendAddRow(items);
|
|
3608
4494
|
return items;
|
|
3609
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
|
+
}
|
|
3610
4501
|
if (control) {
|
|
3611
4502
|
const groupField = control.groupByField();
|
|
3612
4503
|
const pageSize = control.pageSize();
|
|
@@ -3624,15 +4515,20 @@ class AgridProjectionModel {
|
|
|
3624
4515
|
const sortEntries = this.opts.serverSideFiltering()
|
|
3625
4516
|
? []
|
|
3626
4517
|
: this.sortEntries(filters);
|
|
3627
|
-
const items = buildGroupedItems(rows, indices, groupField, this.columnMap(), sortEntries, expandedLabels, this.opts.locale());
|
|
4518
|
+
const items = buildGroupedItems(rows, indices, groupField, this.columnMap(), sortEntries, expandedLabels, this.opts.locale(), this.opts.visibleColDefs(), control.aggregates());
|
|
3628
4519
|
this.appendAddRow(items);
|
|
3629
4520
|
return items;
|
|
3630
4521
|
}
|
|
3631
4522
|
}
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
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
|
+
}
|
|
3636
4532
|
this.appendAddRow(items);
|
|
3637
4533
|
return items;
|
|
3638
4534
|
}, ...(ngDevMode ? [{ debugName: "filteredItems" }] : /* istanbul ignore next */ []));
|
|
@@ -3652,6 +4548,29 @@ class AgridProjectionModel {
|
|
|
3652
4548
|
.map(field => [field, filters[field]])
|
|
3653
4549
|
.filter(([, filter]) => !!filter?.sort);
|
|
3654
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
|
+
}
|
|
3655
4574
|
appendAddRow(items) {
|
|
3656
4575
|
if (this.opts.allowAddRows() && !this.opts.autoAddRows())
|
|
3657
4576
|
items.push(null);
|
|
@@ -3786,7 +4705,7 @@ class AgridRangeController {
|
|
|
3786
4705
|
if (insideSource)
|
|
3787
4706
|
continue;
|
|
3788
4707
|
const col = cols[colIndex];
|
|
3789
|
-
if (!col || !this.opts.isCellEditable(col))
|
|
4708
|
+
if (!col || !this.opts.isCellEditable(col, item.originalIndex))
|
|
3790
4709
|
continue;
|
|
3791
4710
|
const sourceRowIndex = rowOffset % sourceValues.length;
|
|
3792
4711
|
const sourceRowValues = sourceValues[sourceRowIndex];
|
|
@@ -3909,6 +4828,17 @@ class AgridRangeController {
|
|
|
3909
4828
|
}
|
|
3910
4829
|
}
|
|
3911
4830
|
|
|
4831
|
+
/**
|
|
4832
|
+
* Whether a pointer event originated inside an editable form control. Used to avoid calling
|
|
4833
|
+
* `preventDefault()` on a row pointerdown, which would otherwise cancel native text selection
|
|
4834
|
+
* (and caret placement) inside an active cell editor.
|
|
4835
|
+
*/
|
|
4836
|
+
function isEditableEventTarget(target) {
|
|
4837
|
+
return target instanceof HTMLInputElement
|
|
4838
|
+
|| target instanceof HTMLTextAreaElement
|
|
4839
|
+
|| target instanceof HTMLSelectElement
|
|
4840
|
+
|| (target instanceof HTMLElement && target.isContentEditable);
|
|
4841
|
+
}
|
|
3912
4842
|
/**
|
|
3913
4843
|
* Owns row selection, row/cell context menus, and row-level operations.
|
|
3914
4844
|
* @internal
|
|
@@ -3972,7 +4902,7 @@ class AgridRowController {
|
|
|
3972
4902
|
this.emitSelection();
|
|
3973
4903
|
}
|
|
3974
4904
|
else {
|
|
3975
|
-
if (!(event.target
|
|
4905
|
+
if (!isEditableEventTarget(event.target))
|
|
3976
4906
|
event.preventDefault();
|
|
3977
4907
|
this.selectedIndices.set(new Set([originalIndex]));
|
|
3978
4908
|
this.selectionPivot = originalIndex;
|
|
@@ -4101,9 +5031,21 @@ class AgridSidebarController {
|
|
|
4101
5031
|
return index === null ? null : this.opts.dataSource().rows()[index] ?? null;
|
|
4102
5032
|
}, ...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
|
|
4103
5033
|
hiddenColumns = computed(() => this.opts.control()?.hiddenColumns() ?? new Set(), ...(ngDevMode ? [{ debugName: "hiddenColumns" }] : /* istanbul ignore next */ []));
|
|
5034
|
+
/** Per-field validation messages for rejected sidebar edits (`field → message`). */
|
|
5035
|
+
validationErrors = signal({}, ...(ngDevMode ? [{ debugName: "validationErrors" }] : /* istanbul ignore next */ []));
|
|
4104
5036
|
constructor(opts) {
|
|
4105
5037
|
this.opts = opts;
|
|
4106
5038
|
}
|
|
5039
|
+
/** Remove the stored validation message for one field, if any. */
|
|
5040
|
+
clearFieldError(field) {
|
|
5041
|
+
if (!(field in this.validationErrors()))
|
|
5042
|
+
return;
|
|
5043
|
+
this.validationErrors.update(errors => {
|
|
5044
|
+
const next = { ...errors };
|
|
5045
|
+
delete next[field];
|
|
5046
|
+
return next;
|
|
5047
|
+
});
|
|
5048
|
+
}
|
|
4107
5049
|
/** Opens the detail tab when automatic detail display is enabled. */
|
|
4108
5050
|
syncAutoOpen() {
|
|
4109
5051
|
if (this.opts.autoOpenDetail() && this.opts.selectedRowIndex() !== null) {
|
|
@@ -4146,6 +5088,8 @@ class AgridSidebarController {
|
|
|
4146
5088
|
const index = this.opts.selectedRowIndex();
|
|
4147
5089
|
if (index === null)
|
|
4148
5090
|
return;
|
|
5091
|
+
if (!this.opts.isCellEditable(col, index))
|
|
5092
|
+
return;
|
|
4149
5093
|
let newValue = stringValue;
|
|
4150
5094
|
if (col.type === 'number') {
|
|
4151
5095
|
newValue = stringValue === '' ? null : Number(stringValue);
|
|
@@ -4161,9 +5105,17 @@ class AgridSidebarController {
|
|
|
4161
5105
|
? stringValue
|
|
4162
5106
|
: typeof option === 'string' ? option : option.value;
|
|
4163
5107
|
}
|
|
4164
|
-
const
|
|
5108
|
+
const row = this.opts.dataSource().getRow(index);
|
|
5109
|
+
const oldValue = row[field];
|
|
4165
5110
|
if (oldValue === newValue)
|
|
4166
5111
|
return;
|
|
5112
|
+
const message = col.validate?.(newValue, row) ?? null;
|
|
5113
|
+
if (message) {
|
|
5114
|
+
this.validationErrors.update(errors => ({ ...errors, [field]: message }));
|
|
5115
|
+
this.opts.onValidationFailed({ rowIndex: index, field, value: newValue, message });
|
|
5116
|
+
return;
|
|
5117
|
+
}
|
|
5118
|
+
this.clearFieldError(field);
|
|
4167
5119
|
this.opts.dataSource().patchRow(index, { [field]: newValue });
|
|
4168
5120
|
const colIndex = this.opts.visibleColDefs().findIndex(column => column.field === field);
|
|
4169
5121
|
this.opts.control()?.pushEdit({ rowIndex: index, field, oldValue, newValue });
|
|
@@ -4187,11 +5139,15 @@ class AgridSidebarComponent {
|
|
|
4187
5139
|
columns = input([], ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
4188
5140
|
headerGroups = input([], ...(ngDevMode ? [{ debugName: "headerGroups" }] : /* istanbul ignore next */ []));
|
|
4189
5141
|
row = input(null, ...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
|
|
5142
|
+
rowIndex = input(null, ...(ngDevMode ? [{ debugName: "rowIndex" }] : /* istanbul ignore next */ []));
|
|
4190
5143
|
hiddenColumns = input(new Set(), ...(ngDevMode ? [{ debugName: "hiddenColumns" }] : /* istanbul ignore next */ []));
|
|
4191
5144
|
locale = input(undefined, ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
|
|
4192
5145
|
localeText = input(AGRID_LOCALE_TEXT.en, ...(ngDevMode ? [{ debugName: "localeText" }] : /* istanbul ignore next */ []));
|
|
4193
5146
|
readonlyGrid = input(false, ...(ngDevMode ? [{ debugName: "readonlyGrid" }] : /* istanbul ignore next */ []));
|
|
4194
5147
|
useSidebarEditor = input(false, ...(ngDevMode ? [{ debugName: "useSidebarEditor" }] : /* istanbul ignore next */ []));
|
|
5148
|
+
isCellEditable = input(col => col.editable !== false, ...(ngDevMode ? [{ debugName: "isCellEditable" }] : /* istanbul ignore next */ []));
|
|
5149
|
+
/** Per-field validation messages (`field → message`) for rejected detail edits. */
|
|
5150
|
+
errors = input({}, ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
|
|
4195
5151
|
close = output();
|
|
4196
5152
|
tabChange = output();
|
|
4197
5153
|
toggleColumn = output();
|
|
@@ -4237,6 +5193,32 @@ class AgridSidebarComponent {
|
|
|
4237
5193
|
visible: event.target.checked,
|
|
4238
5194
|
});
|
|
4239
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
|
+
}
|
|
4240
5222
|
detailFields = computed(() => {
|
|
4241
5223
|
const row = this.row();
|
|
4242
5224
|
if (!row)
|
|
@@ -4244,6 +5226,7 @@ class AgridSidebarComponent {
|
|
|
4244
5226
|
const locale = this.locale();
|
|
4245
5227
|
const readonlyGrid = this.readonlyGrid();
|
|
4246
5228
|
const hiddenColumns = this.hiddenColumns();
|
|
5229
|
+
const rowIndex = this.rowIndex();
|
|
4247
5230
|
return this.columns().map(col => {
|
|
4248
5231
|
const rawValue = row[col.field];
|
|
4249
5232
|
let inputValue = String(rawValue ?? '');
|
|
@@ -4256,18 +5239,160 @@ class AgridSidebarComponent {
|
|
|
4256
5239
|
rawValue,
|
|
4257
5240
|
inputValue,
|
|
4258
5241
|
hidden: hiddenColumns.has(col.field),
|
|
4259
|
-
editable: !readonlyGrid &&
|
|
5242
|
+
editable: !readonlyGrid && rowIndex !== null && this.isCellEditable()(col, rowIndex),
|
|
4260
5243
|
col,
|
|
4261
5244
|
};
|
|
4262
5245
|
});
|
|
4263
5246
|
}, ...(ngDevMode ? [{ debugName: "detailFields" }] : /* istanbul ignore next */ []));
|
|
4264
5247
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AgridSidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4265
|
-
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 } }, 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 (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 [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 </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}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5248
|
+
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 }, 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 });
|
|
4266
5249
|
}
|
|
4267
5250
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AgridSidebarComponent, decorators: [{
|
|
4268
5251
|
type: Component,
|
|
4269
|
-
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 (
|
|
4270
|
-
}], 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 }] }], 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.15", ngImport: i0, type: AgridVariableRowSizeDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
5375
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.15", 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.15", 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 }] }] } });
|
|
4271
5396
|
|
|
4272
5397
|
/**
|
|
4273
5398
|
* Excel-like data grid for Angular 21.
|
|
@@ -4282,10 +5407,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
4282
5407
|
* |-----|--------|
|
|
4283
5408
|
* | Arrow keys | Move selection |
|
|
4284
5409
|
* | Tab / Shift+Tab | Move right / left (wraps rows) |
|
|
4285
|
-
* | Enter
|
|
5410
|
+
* | Enter | Enter edit mode |
|
|
5411
|
+
* | Ctrl/Cmd+Enter | Toggle an expandable tree node |
|
|
5412
|
+
* | F2 | Enter edit mode |
|
|
4286
5413
|
* | Printable key | Enter edit mode with seeded character |
|
|
4287
|
-
* | Escape |
|
|
4288
|
-
* | 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 |
|
|
4289
5416
|
*/
|
|
4290
5417
|
class AgridComponent {
|
|
4291
5418
|
// ── Inputs ───────────────────────────────────────────────────────────────────
|
|
@@ -4298,14 +5425,18 @@ class AgridComponent {
|
|
|
4298
5425
|
allowAddRows = computed(() => this.provider().allowAddRows, ...(ngDevMode ? [{ debugName: "allowAddRows" }] : /* istanbul ignore next */ []));
|
|
4299
5426
|
autoAddRows = computed(() => this.provider().autoAddRows(), ...(ngDevMode ? [{ debugName: "autoAddRows" }] : /* istanbul ignore next */ []));
|
|
4300
5427
|
enableRowMarking = computed(() => this.provider().enableRowMarking, ...(ngDevMode ? [{ debugName: "enableRowMarking" }] : /* istanbul ignore next */ []));
|
|
4301
|
-
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 */ []));
|
|
4302
5429
|
controlColumnWidth = computed(() => this.enableRowMarking() ? 48 : 24, ...(ngDevMode ? [{ debugName: "controlColumnWidth" }] : /* istanbul ignore next */ []));
|
|
4303
5430
|
showSidebar = computed(() => this.provider().showSidebar, ...(ngDevMode ? [{ debugName: "showSidebar" }] : /* istanbul ignore next */ []));
|
|
4304
5431
|
autoOpenDetail = computed(() => this.provider().autoOpenDetail, ...(ngDevMode ? [{ debugName: "autoOpenDetail" }] : /* istanbul ignore next */ []));
|
|
4305
5432
|
serverSideFiltering = computed(() => this.provider().serverSideFiltering, ...(ngDevMode ? [{ debugName: "serverSideFiltering" }] : /* istanbul ignore next */ []));
|
|
4306
5433
|
filterDebounceMs = computed(() => this.provider().filterDebounceMs, ...(ngDevMode ? [{ debugName: "filterDebounceMs" }] : /* istanbul ignore next */ []));
|
|
5434
|
+
enableQuickFilter = computed(() => this.provider().enableQuickFilter, ...(ngDevMode ? [{ debugName: "enableQuickFilter" }] : /* istanbul ignore next */ []));
|
|
5435
|
+
menuBarItems = computed(() => this.provider().menuBarItems, ...(ngDevMode ? [{ debugName: "menuBarItems" }] : /* istanbul ignore next */ []));
|
|
5436
|
+
quickFilterValue = computed(() => this.control()?.quickFilter() ?? '', ...(ngDevMode ? [{ debugName: "quickFilterValue" }] : /* istanbul ignore next */ []));
|
|
4307
5437
|
sortOption = computed(() => this.provider().sortOption, ...(ngDevMode ? [{ debugName: "sortOption" }] : /* istanbul ignore next */ []));
|
|
4308
5438
|
rowSelection = computed(() => this.provider().rowSelection, ...(ngDevMode ? [{ debugName: "rowSelection" }] : /* istanbul ignore next */ []));
|
|
5439
|
+
enterEditAction = computed(() => this.provider().enterEditAction, ...(ngDevMode ? [{ debugName: "enterEditAction" }] : /* istanbul ignore next */ []));
|
|
4309
5440
|
groupDescription = computed(() => this.provider().groupDescription, ...(ngDevMode ? [{ debugName: "groupDescription" }] : /* istanbul ignore next */ []));
|
|
4310
5441
|
groupActions = computed(() => this.provider().groupActions, ...(ngDevMode ? [{ debugName: "groupActions" }] : /* istanbul ignore next */ []));
|
|
4311
5442
|
cellMenuItems = computed(() => this.provider().cellMenuItems, ...(ngDevMode ? [{ debugName: "cellMenuItems" }] : /* istanbul ignore next */ []));
|
|
@@ -4318,10 +5449,48 @@ class AgridComponent {
|
|
|
4318
5449
|
loading = computed(() => this.provider().loading(), ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
4319
5450
|
emptyText = computed(() => this.provider().emptyText, ...(ngDevMode ? [{ debugName: "emptyText" }] : /* istanbul ignore next */ []));
|
|
4320
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 */ []));
|
|
4321
5478
|
/** Column definitions from the active provider. */
|
|
4322
5479
|
colDefs = computed(() => this.provider().columns(), ...(ngDevMode ? [{ debugName: "colDefs" }] : /* istanbul ignore next */ []));
|
|
4323
5480
|
/** Signal-based data container from the active provider. */
|
|
4324
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 */ []));
|
|
4325
5494
|
/** Grid UI state container from the active provider. */
|
|
4326
5495
|
control = computed(() => this.provider().control, ...(ngDevMode ? [{ debugName: "control" }] : /* istanbul ignore next */ []));
|
|
4327
5496
|
/** Resolved locale code used for date formatting and built-in localization lookup. 'auto' is replaced with navigator.language. */
|
|
@@ -4349,6 +5518,10 @@ class AgridComponent {
|
|
|
4349
5518
|
rowDoubleClicked = output();
|
|
4350
5519
|
/** Emitted when the user single-clicks a data row. */
|
|
4351
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();
|
|
4352
5525
|
/**
|
|
4353
5526
|
* Emitted once after a changed row is left during inline editing, or when the sidebar editor
|
|
4354
5527
|
* save button is used.
|
|
@@ -4364,11 +5537,31 @@ class AgridComponent {
|
|
|
4364
5537
|
filterChange = output();
|
|
4365
5538
|
/** Emitted when a column sort changes in server-side filtering mode. */
|
|
4366
5539
|
sortChange = output();
|
|
5540
|
+
/**
|
|
5541
|
+
* Emitted (debounced) when the global quick-filter text changes in server-side filtering mode.
|
|
5542
|
+
* The host should refetch rows matching the text. Not emitted in client mode, where the grid
|
|
5543
|
+
* filters locally.
|
|
5544
|
+
*/
|
|
5545
|
+
quickFilterChange = output();
|
|
5546
|
+
/** Emitted when a `ColDef.validate` hook rejects a committed value (inline or sidebar). */
|
|
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();
|
|
4367
5552
|
// ── Public state ─────────────────────────────────────────────────────────────
|
|
4368
5553
|
/** Currently focused cell, or `null`. */
|
|
4369
5554
|
selectedCell = signal(null, ...(ngDevMode ? [{ debugName: "selectedCell" }] : /* istanbul ignore next */ []));
|
|
4370
5555
|
/** Original index of the row awaiting delete confirmation, or `null`. */
|
|
4371
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 */ []));
|
|
4372
5565
|
markedIndices = signal(new Set(), ...(ngDevMode ? [{ debugName: "markedIndices" }] : /* istanbul ignore next */ []));
|
|
4373
5566
|
/** Original datasource indices marked for inclusion in copy operations. */
|
|
4374
5567
|
markedRowIndices = this.markedIndices.asReadonly();
|
|
@@ -4378,6 +5571,8 @@ class AgridComponent {
|
|
|
4378
5571
|
deleteConfirmationWidth = signal(0, ...(ngDevMode ? [{ debugName: "deleteConfirmationWidth" }] : /* istanbul ignore next */ []));
|
|
4379
5572
|
/** Rectangular cell range selected by Shift+arrow or Shift+click. */
|
|
4380
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);
|
|
4381
5576
|
/** Fill-handle drag preview bounds, in visible row/column coordinates. */
|
|
4382
5577
|
get fillPreviewBounds() { return this.rangeController.fillPreviewBounds; }
|
|
4383
5578
|
/** Position of the cell in edit mode, or `null`. */
|
|
@@ -4386,6 +5581,8 @@ class AgridComponent {
|
|
|
4386
5581
|
get currentDraft() { return this.editController.currentDraft; }
|
|
4387
5582
|
/** Seed character typed to enter edit mode (e.g. pressing 'A'). */
|
|
4388
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; }
|
|
4389
5586
|
/** Toggle the sidebar open/closed. */
|
|
4390
5587
|
toggleSidebar() { this.sidebarController.toggle(); }
|
|
4391
5588
|
/** @internal */
|
|
@@ -4532,6 +5729,9 @@ class AgridComponent {
|
|
|
4532
5729
|
expandedGroups: this.groupController.expandedGroups,
|
|
4533
5730
|
treeConfig: this.treeConfig,
|
|
4534
5731
|
expandedTreeIds: this.treeController.expandedIds,
|
|
5732
|
+
pinRow: this.effectivePinRow,
|
|
5733
|
+
masterDetail: this.masterDetail,
|
|
5734
|
+
expandedDetailIds: this._expandedDetailIds,
|
|
4535
5735
|
});
|
|
4536
5736
|
editController = new AgridEditController({
|
|
4537
5737
|
control: this.control,
|
|
@@ -4544,6 +5744,7 @@ class AgridComponent {
|
|
|
4544
5744
|
scrollToCell: (displayIndex, colIndex) => this.scrollToKeepVisible(displayIndex, colIndex),
|
|
4545
5745
|
focusGrid: () => this.wrapperEl().nativeElement.focus(),
|
|
4546
5746
|
onCellEdit: event => this.emitEditEvents(event),
|
|
5747
|
+
onValidationFailed: event => this.validationFailed.emit({ ...event, source: 'inline' }),
|
|
4547
5748
|
});
|
|
4548
5749
|
/** Total filtered row count regardless of current page. */
|
|
4549
5750
|
filteredRowCount = this.projection.filteredRowCount;
|
|
@@ -4552,8 +5753,9 @@ class AgridComponent {
|
|
|
4552
5753
|
showPagination = this.projection.showPagination;
|
|
4553
5754
|
/** Number of semantic header rows currently rendered. */
|
|
4554
5755
|
headerRowCount = computed(() => this.hasHeaderGroups() ? 2 : 1, ...(ngDevMode ? [{ debugName: "headerRowCount" }] : /* istanbul ignore next */ []));
|
|
4555
|
-
/** Number of rendered semantic rows, including header rows. */
|
|
4556
|
-
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 */ []));
|
|
4557
5759
|
/** Number of visible semantic columns, including the optional control column. */
|
|
4558
5760
|
ariaColCount = computed(() => this.visibleColDefs().length + (this.showControlColumn() ? 1 : 0), ...(ngDevMode ? [{ debugName: "ariaColCount" }] : /* istanbul ignore next */ []));
|
|
4559
5761
|
/** True when no rows or group headers are visible (ignores add-row sentinel and ghost). */
|
|
@@ -4615,6 +5817,24 @@ class AgridComponent {
|
|
|
4615
5817
|
result.splice(insertAt, 0, 'ghost');
|
|
4616
5818
|
return result;
|
|
4617
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 */ []));
|
|
4618
5838
|
// ── Menu signals ─────────────────────────────────────────────────────────────
|
|
4619
5839
|
groupActionsMenu = this.groupController.actionsMenu;
|
|
4620
5840
|
// ── Infrastructure ────────────────────────────────────────────────────────────
|
|
@@ -4633,7 +5853,7 @@ class AgridComponent {
|
|
|
4633
5853
|
visibleColDefs: this.visibleColDefs,
|
|
4634
5854
|
selectedCell: this.selectedCell,
|
|
4635
5855
|
selectedRange: this.selectedRange,
|
|
4636
|
-
isCellEditable: col => this.isCellEditable(col),
|
|
5856
|
+
isCellEditable: (col, originalIndex) => this.isCellEditable(col, originalIndex),
|
|
4637
5857
|
cancelEdit: () => this.cancelCurrent(),
|
|
4638
5858
|
findDisplayIndex: originalIndex => this.findDisplayIndex(originalIndex),
|
|
4639
5859
|
scrollToCell: (displayIndex, colIndex) => this.scrollToKeepVisible(displayIndex, colIndex),
|
|
@@ -4672,14 +5892,16 @@ class AgridComponent {
|
|
|
4672
5892
|
visibleColDefs: this.visibleColDefs,
|
|
4673
5893
|
filteredItems: this.filteredItems,
|
|
4674
5894
|
locale: this.locale,
|
|
5895
|
+
getRowClass: this.rowClassFn,
|
|
4675
5896
|
});
|
|
4676
5897
|
findController = new AgridFindController({
|
|
4677
|
-
|
|
5898
|
+
dataSource: this.dataSource,
|
|
5899
|
+
filteredSortedIndices: this.projection.filteredSortedIndices,
|
|
4678
5900
|
visibleColDefs: this.visibleColDefs,
|
|
4679
5901
|
locale: this.locale,
|
|
4680
5902
|
selectedCell: this.selectedCell,
|
|
4681
5903
|
selectedRange: this.selectedRange,
|
|
4682
|
-
|
|
5904
|
+
revealMatch: (originalIndex, colIndex) => this.revealFindMatch(originalIndex, colIndex),
|
|
4683
5905
|
focusGrid: () => this.wrapperEl().nativeElement.focus(),
|
|
4684
5906
|
});
|
|
4685
5907
|
findOpen = this.findController.open;
|
|
@@ -4696,11 +5918,14 @@ class AgridComponent {
|
|
|
4696
5918
|
rowHeight: this.rowHeight,
|
|
4697
5919
|
allowAddRows: this.allowAddRows,
|
|
4698
5920
|
autoAddRows: this.autoAddRows,
|
|
5921
|
+
enterEditAction: this.enterEditAction,
|
|
4699
5922
|
selectedCell: this.selectedCell,
|
|
4700
5923
|
selectedRange: this.selectedRange,
|
|
4701
5924
|
editingCell: this.editController.editingCell,
|
|
4702
5925
|
isEditing: (originalIndex, colIndex) => this.isEditing(originalIndex, colIndex),
|
|
4703
|
-
|
|
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),
|
|
4704
5929
|
commitEdit: () => this.editController.commit(),
|
|
4705
5930
|
cancelEdit: () => this.editController.cancel(),
|
|
4706
5931
|
undoEdit: () => this.editController.undo(),
|
|
@@ -4745,6 +5970,26 @@ class AgridComponent {
|
|
|
4745
5970
|
selectedRowIndex = this.rowController.selectedRowIndex;
|
|
4746
5971
|
contextMenu = this.rowController.contextMenu;
|
|
4747
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 */ []));
|
|
4748
5993
|
sidebarController = new AgridSidebarController({
|
|
4749
5994
|
control: this.control,
|
|
4750
5995
|
dataSource: this.dataSource,
|
|
@@ -4753,10 +5998,14 @@ class AgridComponent {
|
|
|
4753
5998
|
selectedRowIndex: this.selectedRowIndex,
|
|
4754
5999
|
autoOpenDetail: this.autoOpenDetail,
|
|
4755
6000
|
useSidebarEditor: this.useSidebarEditor,
|
|
6001
|
+
isCellEditable: (col, originalIndex) => this.isCellEditable(col, originalIndex),
|
|
4756
6002
|
onFieldChange: event => this.markCellChanged(event),
|
|
4757
6003
|
onCellEdit: event => this.emitSidebarEditEvents(event),
|
|
6004
|
+
onValidationFailed: event => this.validationFailed.emit({ ...event, source: 'sidebar' }),
|
|
4758
6005
|
});
|
|
4759
6006
|
sidebarOpen = this.sidebarController.open;
|
|
6007
|
+
/** @internal Per-field sidebar validation messages. */
|
|
6008
|
+
sidebarValidationErrors = this.sidebarController.validationErrors;
|
|
4760
6009
|
sidebarTab = this.sidebarController.tab;
|
|
4761
6010
|
sidebarRow = this.sidebarController.row;
|
|
4762
6011
|
sidebarHiddenColumns = this.sidebarController.hiddenColumns;
|
|
@@ -4769,7 +6018,7 @@ class AgridComponent {
|
|
|
4769
6018
|
selectedCell: this.selectedCell,
|
|
4770
6019
|
selectedRange: this.selectedRange,
|
|
4771
6020
|
markedRowIndices: this.markedRowIndices,
|
|
4772
|
-
isCellEditable: col => this.isCellEditable(col),
|
|
6021
|
+
isCellEditable: (col, originalIndex) => this.isCellEditable(col, originalIndex),
|
|
4773
6022
|
onCellEdit: event => this.emitEditEvents(event),
|
|
4774
6023
|
scrollToCell: (displayIndex, colIndex) => this.scrollToKeepVisible(displayIndex, colIndex),
|
|
4775
6024
|
});
|
|
@@ -4818,6 +6067,10 @@ class AgridComponent {
|
|
|
4818
6067
|
getColReorderOffset(field) {
|
|
4819
6068
|
return this.columnReorder.getHeaderOffset(field);
|
|
4820
6069
|
}
|
|
6070
|
+
hasContextMenuEntries() {
|
|
6071
|
+
const hme = (!this.readonlyGrid() && !this.treeConfig());
|
|
6072
|
+
return hme;
|
|
6073
|
+
}
|
|
4821
6074
|
// ── Setup ─────────────────────────────────────────────────────────────────────
|
|
4822
6075
|
_seededControls = new WeakSet();
|
|
4823
6076
|
dirtyInlineRows = new Set();
|
|
@@ -4942,6 +6195,15 @@ class AgridComponent {
|
|
|
4942
6195
|
wrapper.addEventListener('keydown', onKeyDown, { capture: true });
|
|
4943
6196
|
this.destroyRef.onDestroy(() => wrapper.removeEventListener('keydown', onKeyDown, { capture: true }));
|
|
4944
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);
|
|
4945
6207
|
// Emit pageChange whenever page or pageSize changes in server-side pagination mode.
|
|
4946
6208
|
effect(() => {
|
|
4947
6209
|
const ctrl = this.control();
|
|
@@ -4962,20 +6224,25 @@ class AgridComponent {
|
|
|
4962
6224
|
});
|
|
4963
6225
|
// Deselect when clicking outside the grid.
|
|
4964
6226
|
const onOutsidePointerDown = (e) => {
|
|
4965
|
-
|
|
6227
|
+
const isInsideGrid = this._hostEl.nativeElement.contains(e.target);
|
|
6228
|
+
if (!isInsideGrid) {
|
|
6229
|
+
this.closeOpenMenus();
|
|
4966
6230
|
queueMicrotask(() => this.flushDirtyInlineRows());
|
|
4967
6231
|
}
|
|
4968
6232
|
if (this.rowSelection() === 'none')
|
|
4969
6233
|
return;
|
|
4970
6234
|
if (this.selectedRowIndices().size === 0)
|
|
4971
6235
|
return;
|
|
4972
|
-
if (
|
|
6236
|
+
if (isInsideGrid)
|
|
4973
6237
|
return;
|
|
4974
6238
|
this.rowController.clearSelection();
|
|
4975
6239
|
};
|
|
4976
6240
|
this.browser.addDocumentListener('pointerdown', onOutsidePointerDown);
|
|
4977
6241
|
this.destroyRef.onDestroy(() => {
|
|
6242
|
+
this.browser.removeDocumentListener('keydown', onDocumentKeyDown);
|
|
4978
6243
|
this.browser.removeDocumentListener('pointerdown', onOutsidePointerDown);
|
|
6244
|
+
if (this.quickFilterTimer !== null)
|
|
6245
|
+
clearTimeout(this.quickFilterTimer);
|
|
4979
6246
|
});
|
|
4980
6247
|
// Re-sync pinned pane scroll after displayItems changes — CDK independently adjusts
|
|
4981
6248
|
// each viewport when item count changes (group/ungroup, collapse), which can leave
|
|
@@ -5019,9 +6286,94 @@ class AgridComponent {
|
|
|
5019
6286
|
return isGroupHeaderItem(item);
|
|
5020
6287
|
}
|
|
5021
6288
|
/** @internal */
|
|
6289
|
+
isPathTreeNodeItem(item) {
|
|
6290
|
+
return isPathTreeNodeItem(item);
|
|
6291
|
+
}
|
|
6292
|
+
/** @internal */
|
|
5022
6293
|
getItemOriginalIndex(item) {
|
|
5023
6294
|
return isDataRowItem(item) ? item.originalIndex : null;
|
|
5024
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
|
+
}
|
|
5025
6377
|
// ── Template helpers — tree ───────────────────────────────────────────────────
|
|
5026
6378
|
/** @internal True when `col` is the configured tree column. */
|
|
5027
6379
|
isTreeCell(col) {
|
|
@@ -5029,28 +6381,101 @@ class AgridComponent {
|
|
|
5029
6381
|
}
|
|
5030
6382
|
/** @internal Tree depth of a row item (0 when not a tree row). */
|
|
5031
6383
|
treeRowLevel(item) {
|
|
5032
|
-
return isTreeRowItem(item) ? item.level : 0;
|
|
6384
|
+
return isTreeRowItem(item) || isPathTreeNodeItem(item) ? item.level : 0;
|
|
5033
6385
|
}
|
|
5034
6386
|
/** @internal Whether a tree row has children and can be expanded. */
|
|
5035
6387
|
treeRowExpandable(item) {
|
|
5036
|
-
return isTreeRowItem(item) && item.expandable;
|
|
6388
|
+
return (isTreeRowItem(item) || isPathTreeNodeItem(item)) && item.expandable;
|
|
5037
6389
|
}
|
|
5038
6390
|
/** @internal Whether a tree row is currently expanded. */
|
|
5039
6391
|
treeRowExpanded(item) {
|
|
5040
|
-
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 : '';
|
|
5041
6417
|
}
|
|
5042
6418
|
/** @internal Toggle the expand/collapse state of a tree row from its twisty. */
|
|
5043
6419
|
onTreeToggle(item) {
|
|
5044
6420
|
const config = this.treeConfig();
|
|
5045
|
-
if (!config
|
|
6421
|
+
if (!config)
|
|
5046
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))
|
|
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;
|
|
5047
6460
|
this.treeController.toggle(config.getId(item.row));
|
|
6461
|
+
return true;
|
|
5048
6462
|
}
|
|
5049
6463
|
/** Expand every expandable node in the tree. No-op when not in tree mode. */
|
|
5050
6464
|
expandAllNodes() {
|
|
5051
6465
|
const config = this.treeConfig();
|
|
5052
6466
|
if (!config)
|
|
5053
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
|
+
}
|
|
5054
6479
|
const expandable = new Set();
|
|
5055
6480
|
for (const row of this.dataSource().rows()) {
|
|
5056
6481
|
const parentId = config.getParentId(row);
|
|
@@ -5071,6 +6496,10 @@ class AgridComponent {
|
|
|
5071
6496
|
return -1;
|
|
5072
6497
|
if (isGroupHeaderItem(item))
|
|
5073
6498
|
return `__group__${item.groupLabel}`;
|
|
6499
|
+
if (isDetailRowItem(item))
|
|
6500
|
+
return `__detail__${item.detailFor}`;
|
|
6501
|
+
if (isPathTreeNodeItem(item))
|
|
6502
|
+
return item.pathNodeId;
|
|
5074
6503
|
return item.originalIndex;
|
|
5075
6504
|
};
|
|
5076
6505
|
// ── Template helpers — cell/selection state ───────────────────────────────────
|
|
@@ -5110,6 +6539,50 @@ class AgridComponent {
|
|
|
5110
6539
|
return this.columnMenuController.hasMultiSort();
|
|
5111
6540
|
}
|
|
5112
6541
|
getTextFilter(field) { return this.columnMenuController.getTextFilter(field); }
|
|
6542
|
+
/** @internal Condition input type for a column, or `null` when unsupported. */
|
|
6543
|
+
getMenuFilterType(field) {
|
|
6544
|
+
return this.columnMenuController.getFilterType(field);
|
|
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
|
+
}
|
|
6564
|
+
/** @internal */
|
|
6565
|
+
getMenuOperator(field) {
|
|
6566
|
+
return this.columnMenuController.getFilterOperator(field);
|
|
6567
|
+
}
|
|
6568
|
+
/** @internal */
|
|
6569
|
+
getMenuOperand(field) { return this.columnMenuController.getFilterOperand(field); }
|
|
6570
|
+
/** @internal */
|
|
6571
|
+
getMenuOperand2(field) {
|
|
6572
|
+
return this.columnMenuController.getFilterOperand2(field);
|
|
6573
|
+
}
|
|
6574
|
+
/** @internal */
|
|
6575
|
+
onMenuOperatorChange(field, operator) {
|
|
6576
|
+
this.columnMenuController.setFilterOperator(field, operator);
|
|
6577
|
+
}
|
|
6578
|
+
/** @internal */
|
|
6579
|
+
onMenuOperandChange(field, value) {
|
|
6580
|
+
this.columnMenuController.setFilterOperand(field, value);
|
|
6581
|
+
}
|
|
6582
|
+
/** @internal */
|
|
6583
|
+
onMenuOperand2Change(field, value) {
|
|
6584
|
+
this.columnMenuController.setFilterOperand2(field, value);
|
|
6585
|
+
}
|
|
5113
6586
|
/** @internal */
|
|
5114
6587
|
getSort(field) {
|
|
5115
6588
|
return this.columnMenuController.getSort(field);
|
|
@@ -5190,12 +6663,52 @@ class AgridComponent {
|
|
|
5190
6663
|
}
|
|
5191
6664
|
/** @internal */
|
|
5192
6665
|
onDraftChange(value) { this.editController.setDraft(value); }
|
|
6666
|
+
quickFilterTimer = null;
|
|
6667
|
+
/**
|
|
6668
|
+
* @internal Quick-filter input handler. Stores the text on the control (drives the bound value
|
|
6669
|
+
* and client-side filtering) and, in server mode, emits a debounced `quickFilterChange` instead.
|
|
6670
|
+
*/
|
|
6671
|
+
onQuickFilterInput(event) {
|
|
6672
|
+
const text = event.target.value;
|
|
6673
|
+
this.control()?.setQuickFilter(text);
|
|
6674
|
+
if (!this.serverSideFiltering())
|
|
6675
|
+
return;
|
|
6676
|
+
if (this.quickFilterTimer !== null)
|
|
6677
|
+
clearTimeout(this.quickFilterTimer);
|
|
6678
|
+
const delay = this.filterDebounceMs();
|
|
6679
|
+
if (delay === 0) {
|
|
6680
|
+
this.quickFilterChange.emit(text);
|
|
6681
|
+
return;
|
|
6682
|
+
}
|
|
6683
|
+
this.quickFilterTimer = setTimeout(() => {
|
|
6684
|
+
this.quickFilterTimer = null;
|
|
6685
|
+
this.quickFilterChange.emit(text);
|
|
6686
|
+
}, delay);
|
|
6687
|
+
}
|
|
6688
|
+
/** @internal Whether a column is editable in the current grid state (drives boolean checkboxes). */
|
|
6689
|
+
isColEditable(col, originalIndex) {
|
|
6690
|
+
return this.editController.isCellEditable(col, originalIndex);
|
|
6691
|
+
}
|
|
6692
|
+
/** @internal Inline validation message for a cell, or `null` when the cell has no active error. */
|
|
6693
|
+
cellValidationError(originalIndex, ci) {
|
|
6694
|
+
const error = this.editController.validationError();
|
|
6695
|
+
return error && error.rowIndex === originalIndex && error.colIndex === ci ? error.message : null;
|
|
6696
|
+
}
|
|
6697
|
+
/** @internal Commit a boolean-column checkbox toggle directly to the data source. */
|
|
6698
|
+
onBooleanToggle(originalIndex, ci, value) {
|
|
6699
|
+
this.editController.setCellValue(originalIndex, ci, value);
|
|
6700
|
+
}
|
|
5193
6701
|
/** @internal Starts a fill-handle drag from the bottom-right corner of the selection. */
|
|
5194
6702
|
onCellPointerDown(event, originalIndex, colIndex) {
|
|
5195
6703
|
this.rangeController.startFill(event, originalIndex, colIndex);
|
|
5196
6704
|
}
|
|
5197
6705
|
/** @internal Main keyboard handler delegated from the wrapper div. */
|
|
5198
6706
|
onKeyDown(event) {
|
|
6707
|
+
if (event.key === 'Escape' && this.closeOpenMenus()) {
|
|
6708
|
+
event.preventDefault();
|
|
6709
|
+
event.stopPropagation();
|
|
6710
|
+
return;
|
|
6711
|
+
}
|
|
5199
6712
|
if (event.key === 'Escape' && this.pendingDeleteRow() !== null) {
|
|
5200
6713
|
event.preventDefault();
|
|
5201
6714
|
event.stopPropagation();
|
|
@@ -5226,6 +6739,47 @@ class AgridComponent {
|
|
|
5226
6739
|
goToFindMatch(direction) {
|
|
5227
6740
|
this.findController.goToMatch(direction);
|
|
5228
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
|
+
}
|
|
5229
6783
|
// ── Row reorder ───────────────────────────────────────────────────────────────
|
|
5230
6784
|
/** @internal Ghost cell display during a reorder drag. */
|
|
5231
6785
|
getGhostCellDisplay(col) { return this.dragHandler.getGhostDisplay(col); }
|
|
@@ -5238,7 +6792,7 @@ class AgridComponent {
|
|
|
5238
6792
|
/** @internal Handles the control column without letting the row receive a second pointer event. */
|
|
5239
6793
|
onControlPointerDown(event, originalIndex) {
|
|
5240
6794
|
event.stopPropagation();
|
|
5241
|
-
if (this.allowRowReorder()) {
|
|
6795
|
+
if (this.allowRowReorder() && event.pointerType === 'mouse' && event.button === 0) {
|
|
5242
6796
|
this.onHandlePointerDown(event, originalIndex);
|
|
5243
6797
|
return;
|
|
5244
6798
|
}
|
|
@@ -5306,6 +6860,121 @@ class AgridComponent {
|
|
|
5306
6860
|
}
|
|
5307
6861
|
/** @internal */
|
|
5308
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
|
+
}
|
|
5309
6978
|
/** @internal Runs a typed provider context-menu action against erased controller state. */
|
|
5310
6979
|
runCellMenuItem(item, menu) {
|
|
5311
6980
|
item.action({
|
|
@@ -5519,11 +7188,11 @@ class AgridComponent {
|
|
|
5519
7188
|
return index;
|
|
5520
7189
|
})));
|
|
5521
7190
|
}
|
|
5522
|
-
isCellEditable(col) {
|
|
5523
|
-
return this.editController.isCellEditable(col);
|
|
7191
|
+
isCellEditable(col, originalIndex) {
|
|
7192
|
+
return this.editController.isCellEditable(col, originalIndex);
|
|
5524
7193
|
}
|
|
5525
|
-
enterEdit(originalIndex, ci, seedChar) {
|
|
5526
|
-
this.editController.start(originalIndex, ci, seedChar);
|
|
7194
|
+
enterEdit(originalIndex, ci, seedChar, selectText = true) {
|
|
7195
|
+
this.editController.start(originalIndex, ci, seedChar, selectText);
|
|
5527
7196
|
}
|
|
5528
7197
|
cancelCurrent() {
|
|
5529
7198
|
this.editController.cancel();
|
|
@@ -5559,13 +7228,16 @@ class AgridComponent {
|
|
|
5559
7228
|
return this.columnSizing.getWidthToken(col);
|
|
5560
7229
|
}
|
|
5561
7230
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AgridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5562
|
-
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" }, 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 <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)\"\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)\" (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)\"\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)\"\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 </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)\"\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)\" (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 (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 [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-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)}\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", "treeCell", "treeLevel", "treeExpandable", "treeExpanded", "seedChar"], outputs: ["treeToggle", "activate", "startEdit", "draftChange"] }, { kind: "component", type: AgridColumnMenuComponent, selector: "agrid-column-menu", inputs: ["localeText", "x", "y", "header", "sortDir", "sortable", "showColumnActions", "pinned", "groupable", "grouped", "filterable", "showValueFilter", "search", "allSelected", "valueItems", "sortPriority", "hasMultiSort", "aggregate"], outputs: ["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"], outputs: ["close", "tabChange", "toggleColumn", "toggleColumnGroup", "detailEdit", "save"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7231
|
+
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", 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 });
|
|
5563
7232
|
}
|
|
5564
7233
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AgridComponent, decorators: [{
|
|
5565
7234
|
type: Component,
|
|
5566
7235
|
args: [{ selector: 'agrid', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
5567
7236
|
ScrollingModule,
|
|
7237
|
+
NgTemplateOutlet,
|
|
7238
|
+
AgridVariableRowSizeDirective,
|
|
5568
7239
|
AgridCellComponent,
|
|
7240
|
+
AgridMenuBarComponent,
|
|
5569
7241
|
AgridColumnMenuComponent,
|
|
5570
7242
|
AgridFindPanelComponent,
|
|
5571
7243
|
AgridSidebarComponent,
|
|
@@ -5573,8 +7245,411 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
|
|
|
5573
7245
|
'[class.ag-zebra]': 'zebraStripes()',
|
|
5574
7246
|
'[style.min-height]': 'minHeight()',
|
|
5575
7247
|
'[style.max-height]': 'maxHeight()',
|
|
5576
|
-
}, 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 <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)\"\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)\" (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)\"\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)\"\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 </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)\"\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)\" (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 (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 [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-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)}\n"] }]
|
|
5577
|
-
}], 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"] }], 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.15", ngImport: i0, type: AgridPageSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7416
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", 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.15", 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.15", ngImport: i0, type: AgridTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7625
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", 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.15", 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
|
+
}
|
|
5578
7653
|
|
|
5579
7654
|
/** Width sentinel that makes a column fill the remaining horizontal space. */
|
|
5580
7655
|
const ColDefAutoSize = -1;
|
|
@@ -5583,5 +7658,5 @@ const ColDefAutoSize = -1;
|
|
|
5583
7658
|
* Generated bundle index. Do not edit.
|
|
5584
7659
|
*/
|
|
5585
7660
|
|
|
5586
|
-
export { AGRID_LOCALE_TEXT, AgridComponent, AgridControl, AgridDataSource, AgridProvider, ColDefAutoSize };
|
|
7661
|
+
export { AGRID_LOCALE_TEXT, AgridComponent, AgridControl, AgridDataSource, AgridPageSelectorComponent, AgridProvider, AgridTreeComponent, AgridTreeProvider, ColDefAutoSize };
|
|
5587
7662
|
//# sourceMappingURL=thkl-agrid.mjs.map
|