@thkl/agrid 0.1.4 → 0.1.7

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.
@@ -83,6 +83,15 @@ function isDataRowItem(item) {
83
83
  function isGroupHeaderItem(item) {
84
84
  return typeof item === 'object' && item !== null && 'groupLabel' in item;
85
85
  }
86
+ /**
87
+ * Returns whether a virtual-scroll item represents a tree row.
88
+ *
89
+ * Tree rows also pass {@link isDataRowItem} (they carry `row` + `originalIndex`); this guard
90
+ * additionally narrows to the indentation/expansion fields via the `level` discriminator.
91
+ */
92
+ function isTreeRowItem(item) {
93
+ return typeof item === 'object' && item !== null && 'level' in item;
94
+ }
86
95
  // Filtering
87
96
  /** Apply text substring and value-set filters, returning the surviving row indices. */
88
97
  function applyTextAndValueFilters(rows, indices, filters, colMap, locale) {
@@ -97,9 +106,54 @@ function applyTextAndValueFilters(rows, indices, filters, colMap, locale) {
97
106
  const allowed = new Set(filter.selectedValues);
98
107
  result = result.filter(i => allowed.has(String(rows[i][field] ?? '')));
99
108
  }
109
+ if (filter.operator && filter.operand != null && filter.operand !== '') {
110
+ result = result.filter(i => passesRangeFilter(col, rows[i][field], filter));
111
+ }
100
112
  }
101
113
  return result;
102
114
  }
115
+ /**
116
+ * Evaluate a typed range filter (`number` / `date`) for one cell value.
117
+ * Date columns compare epoch-millis; everything else compares as numbers.
118
+ * Rows whose value can't be parsed to the comparison type are excluded.
119
+ */
120
+ function passesRangeFilter(col, raw, filter) {
121
+ const isDate = col?.type === 'date' || looksLikeDate(raw);
122
+ const toNum = (v) => isDate
123
+ ? (v instanceof Date ? v.getTime() : new Date(v).getTime())
124
+ : Number(v);
125
+ const value = toNum(raw);
126
+ if (Number.isNaN(value))
127
+ return false;
128
+ const a = toNum(filter.operand);
129
+ if (Number.isNaN(a))
130
+ return true;
131
+ switch (filter.operator) {
132
+ case 'eq': return value === a;
133
+ case 'neq': return value !== a;
134
+ case 'gt': return value > a;
135
+ case 'gte': return value >= a;
136
+ case 'lt': return value < a;
137
+ case 'lte': return value <= a;
138
+ case 'between': {
139
+ const b = toNum(filter.operand2);
140
+ if (Number.isNaN(b))
141
+ return value >= a;
142
+ return value >= Math.min(a, b) && value <= Math.max(a, b);
143
+ }
144
+ default: return true;
145
+ }
146
+ }
147
+ /**
148
+ * Keep only the rows where at least one of the given columns' display value contains
149
+ * `text` (case-insensitive). An empty/whitespace `text` returns the indices unchanged.
150
+ */
151
+ function applyQuickFilter(rows, indices, text, cols, locale) {
152
+ const q = text.trim().toLowerCase();
153
+ if (!q)
154
+ return indices;
155
+ return indices.filter(i => cols.some(col => getDisplayForField(col, rows[i][col.field], locale).toLowerCase().includes(q)));
156
+ }
103
157
  // Sorting
104
158
  /**
105
159
  * Sort indices by one or more columns in priority order.
@@ -161,13 +215,47 @@ function applySortToIndices(rows, indices, sortEntries, colMap, locale) {
161
215
  });
162
216
  return decorated.map(item => item.index);
163
217
  }
164
- // Grouping
165
218
  /**
166
- * Bucket rows by the display value of `groupField`, sort group keys alphabetically,
167
- * optionally sort within groups by a secondary sort, and interleave group-header items.
168
- * Does NOT append the add-row null sentinel the caller does that.
219
+ * Compute aggregate values for the given rows across every column that has a static
220
+ * (`ColDef.aggregate`) or control-configured aggregate. Returns a `field value` map containing
221
+ * only aggregated columns. Shared by the grid footer and per-group subtotals.
169
222
  */
170
- function buildGroupedItems(rows, indices, groupField, colMap, sortEntries, expandedLabels, locale) {
223
+ function computeAggregates(rows, indices, cols, controlAggregates) {
224
+ const result = {};
225
+ for (const col of cols) {
226
+ const aggregate = controlAggregates[col.field] ?? col.aggregate;
227
+ if (!aggregate)
228
+ continue;
229
+ const values = indices.map(index => rows[index][col.field]);
230
+ if (typeof aggregate === 'function') {
231
+ result[col.field] = aggregate(values);
232
+ continue;
233
+ }
234
+ const numbers = values.map(Number).filter(value => !Number.isNaN(value));
235
+ switch (aggregate) {
236
+ case 'sum':
237
+ result[col.field] = numbers.reduce((sum, value) => sum + value, 0);
238
+ break;
239
+ case 'avg':
240
+ result[col.field] = numbers.length
241
+ ? numbers.reduce((sum, value) => sum + value, 0) / numbers.length
242
+ : null;
243
+ break;
244
+ case 'min':
245
+ result[col.field] = numbers.length ? Math.min(...numbers) : null;
246
+ break;
247
+ case 'max':
248
+ result[col.field] = numbers.length ? Math.max(...numbers) : null;
249
+ break;
250
+ case 'count':
251
+ result[col.field] = values.filter(value => value != null && value !== '').length;
252
+ break;
253
+ }
254
+ }
255
+ return result;
256
+ }
257
+ function buildGroupedItems(rows, indices, groupField, colMap, sortEntries, expandedLabels, locale, aggregateCols = [], controlAggregates = {}) {
258
+ const hasAggregates = aggregateCols.some(col => controlAggregates[col.field] ?? col.aggregate);
171
259
  const groupCol = colMap.get(groupField);
172
260
  const groups = new Map();
173
261
  for (const i of indices) {
@@ -188,7 +276,10 @@ function buildGroupedItems(rows, indices, groupField, colMap, sortEntries, expan
188
276
  for (const key of sortedKeys) {
189
277
  const groupRows = groups.get(key);
190
278
  const isExpanded = expandedLabels.has(key);
191
- items.push({ groupLabel: key, count: groupRows.length, collapsed: !isExpanded });
279
+ const aggregates = hasAggregates
280
+ ? computeAggregates(rows, groupRows, aggregateCols, controlAggregates)
281
+ : undefined;
282
+ items.push({ groupLabel: key, count: groupRows.length, collapsed: !isExpanded, aggregates });
192
283
  if (isExpanded) {
193
284
  for (const i of groupRows)
194
285
  items.push({ row: rows[i], originalIndex: i });
@@ -196,6 +287,63 @@ function buildGroupedItems(rows, indices, groupField, colMap, sortEntries, expan
196
287
  }
197
288
  return items;
198
289
  }
290
+ /**
291
+ * Flatten a parent/child hierarchy into a depth-first list of {@link TreeRowItem}s for the
292
+ * virtual scroll list, honoring the expanded-id set.
293
+ *
294
+ * `indices` is the already filtered and sorted set of source-array positions; sibling order
295
+ * follows that order. A row is treated as a **root** when its parent id is `null`/`undefined`
296
+ * or when that parent is not present in `indices` (orphan). Children of a collapsed row are
297
+ * omitted. Cycles and rows visited more than once are guarded so the walk always terminates.
298
+ *
299
+ * @param rows The full data-source array.
300
+ * @param indices Filtered/sorted source positions to include, in display order.
301
+ * @param accessors `getId` / `getParentId` for the rows.
302
+ * @param expandedIds Ids whose children should be rendered.
303
+ * @param forceExpandedIds Ids forced open regardless of `expandedIds` — used to reveal the
304
+ * ancestor path of filter matches. Optional.
305
+ */
306
+ function buildTreeItems(rows, indices, accessors, expandedIds, forceExpandedIds) {
307
+ const { getId, getParentId } = accessors;
308
+ const includedIds = new Set();
309
+ for (const i of indices)
310
+ includedIds.add(getId(rows[i]));
311
+ // Bucket each included row under its parent, preserving the incoming (sorted) order.
312
+ const childrenByParent = new Map();
313
+ const roots = [];
314
+ for (const i of indices) {
315
+ const parentId = getParentId(rows[i]);
316
+ if (parentId == null || !includedIds.has(parentId)) {
317
+ roots.push(i);
318
+ continue;
319
+ }
320
+ const siblings = childrenByParent.get(parentId);
321
+ if (siblings)
322
+ siblings.push(i);
323
+ else
324
+ childrenByParent.set(parentId, [i]);
325
+ }
326
+ const items = [];
327
+ const visited = new Set();
328
+ const visit = (index, level) => {
329
+ const row = rows[index];
330
+ const id = getId(row);
331
+ if (visited.has(id))
332
+ return; // cycle / duplicate-parent guard
333
+ visited.add(id);
334
+ const children = childrenByParent.get(id);
335
+ const expandable = !!children?.length;
336
+ const expanded = expandable && (expandedIds.has(id) || !!forceExpandedIds?.has(id));
337
+ items.push({ row, originalIndex: index, level, expandable, expanded });
338
+ if (expanded && children) {
339
+ for (const childIndex of children)
340
+ visit(childIndex, level + 1);
341
+ }
342
+ };
343
+ for (const rootIndex of roots)
344
+ visit(rootIndex, 0);
345
+ return items;
346
+ }
199
347
  // Selection range
200
348
  /** Build the set of original indices spanning from `fromOrig` to `toOrig` in display order. */
201
349
  function buildSelectionRange(fromOrig, toOrig, items) {
@@ -238,6 +386,22 @@ class AgridCellComponent {
238
386
  selected = input(false, ...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
239
387
  /** Whether this cell is currently in edit mode. */
240
388
  editing = input(false, ...(ngDevMode ? [{ debugName: "editing" }] : /* istanbul ignore next */ []));
389
+ /** Whether this cell may be edited (drives the boolean checkbox enabled state). */
390
+ editable = input(true, ...(ngDevMode ? [{ debugName: "editable" }] : /* istanbul ignore next */ []));
391
+ /** Validation error message to show under the editor, or `null` when the value is valid. */
392
+ error = input(null, ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
393
+ /** Whether this cell is the tree column and should render indentation + a twisty. */
394
+ treeCell = input(false, ...(ngDevMode ? [{ debugName: "treeCell" }] : /* istanbul ignore next */ []));
395
+ /** Depth of the row in the tree (drives indentation). Root rows are `0`. */
396
+ treeLevel = input(0, ...(ngDevMode ? [{ debugName: "treeLevel" }] : /* istanbul ignore next */ []));
397
+ /** Whether the row has children and can be expanded/collapsed. */
398
+ treeExpandable = input(false, ...(ngDevMode ? [{ debugName: "treeExpandable" }] : /* istanbul ignore next */ []));
399
+ /** Whether the row is currently expanded. */
400
+ treeExpanded = input(false, ...(ngDevMode ? [{ debugName: "treeExpanded" }] : /* istanbul ignore next */ []));
401
+ /** Emitted when the user clicks the expand/collapse twisty. */
402
+ treeToggle = output();
403
+ /** Indentation in pixels applied per tree level. */
404
+ treeIndent = 16;
241
405
  /**
242
406
  * Optional character to pre-fill the edit input when the user presses a printable key
243
407
  * while the cell is selected (type-to-start-editing behavior).
@@ -250,6 +414,8 @@ class AgridCellComponent {
250
414
  activate = output();
251
415
  /** Emitted on double-click — the grid enters edit mode. */
252
416
  startEdit = output();
417
+ /** Emitted when a boolean-column checkbox is toggled, carrying the new value. */
418
+ booleanToggle = output();
253
419
  /**
254
420
  * Emitted on every keystroke inside the edit input or on every select change.
255
421
  * The grid stores the latest value in `currentDraft` so it can commit on Tab / Enter.
@@ -264,6 +430,13 @@ class AgridCellComponent {
264
430
  ? getDateInputValue(draft)
265
431
  : String(draft ?? '');
266
432
  }, ...(ngDevMode ? [{ debugName: "editorValue" }] : /* istanbul ignore next */ []));
433
+ /** Whether this cell renders as an inline boolean checkbox (no edit mode). */
434
+ booleanCell = computed(() => this.col().type === 'boolean' && !this.col().cellRenderer, ...(ngDevMode ? [{ debugName: "booleanCell" }] : /* istanbul ignore next */ []));
435
+ /** Truthiness of the current boolean value (accepts `true`, `'true'`, `1`, `'1'`). */
436
+ booleanChecked = computed(() => {
437
+ const v = this.value();
438
+ return v === true || v === 1 || v === 'true' || v === '1';
439
+ }, ...(ngDevMode ? [{ debugName: "booleanChecked" }] : /* istanbul ignore next */ []));
267
440
  renderedHtml = computed(() => {
268
441
  const renderer = this.col().cellRenderer;
269
442
  if (!renderer)
@@ -354,6 +527,19 @@ class AgridCellComponent {
354
527
  }
355
528
  });
356
529
  }
530
+ /** Toggle a boolean cell's value, driven entirely from data (no DOM toggle). */
531
+ onCheckboxClick(event) {
532
+ event.stopPropagation();
533
+ event.preventDefault();
534
+ if (!this.editable())
535
+ return;
536
+ this.booleanToggle.emit(!this.booleanChecked());
537
+ }
538
+ /** Emit a tree expand/collapse request without selecting or editing the cell. */
539
+ onTreeToggle(event) {
540
+ event.stopPropagation();
541
+ this.treeToggle.emit();
542
+ }
357
543
  /** Forward `<input>` changes to the grid. */
358
544
  onInput(event) {
359
545
  const val = event.target.value;
@@ -375,8 +561,18 @@ class AgridCellComponent {
375
561
  this.draftChange.emit(rawValue);
376
562
  }
377
563
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AgridCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
378
- 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 }, seedChar: { classPropertyName: "seedChar", publicName: "seedChar", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { 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()", "attr.aria-readonly": "col().editable === false ? \"true\" : null" } }, viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["editInput"], descendants: true, isSignal: true }, { propertyName: "selectEl", first: true, predicate: ["editSelect"], descendants: true, isSignal: true }], ngImport: i0, template: `
379
- @if (editing()) {
564
+ 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 }, editable: { classPropertyName: "editable", publicName: "editable", 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 } }, outputs: { treeToggle: "treeToggle", activate: "activate", startEdit: "startEdit", booleanToggle: "booleanToggle", 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": "col().editable === false ? \"true\" : null" } }, viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["editInput"], descendants: true, isSignal: true }, { propertyName: "selectEl", first: true, predicate: ["editSelect"], descendants: true, isSignal: true }], ngImport: i0, template: `
565
+ @if (booleanCell()) {
566
+ <input
567
+ type="checkbox"
568
+ class="ag-cell-checkbox"
569
+ [checked]="booleanChecked()"
570
+ [disabled]="!editable()"
571
+ [attr.aria-label]="col().header"
572
+ (click)="onCheckboxClick($event)"
573
+ (dblclick)="$event.stopPropagation()"
574
+ />
575
+ } @else if (editing()) {
380
576
  @if (col().values?.length) {
381
577
  <select
382
578
  #editSelect
@@ -391,17 +587,38 @@ class AgridCellComponent {
391
587
  <input
392
588
  #editInput
393
589
  class="ag-cell-input"
590
+ [class.ag-cell-input--invalid]="!!error()"
394
591
  [type]="col().type === 'date' ? 'date' : col().type === 'number' ? 'number' : 'text'"
395
592
  [value]="editorValue()"
396
593
  (input)="onInput($event)"
397
594
  />
398
595
  }
399
- } @else if (col().cellRenderer) {
400
- <span class="ag-cell-value" [innerHTML]="renderedHtml()"></span>
596
+ @if (error(); as msg) {
597
+ <span class="ag-cell-error" role="alert">{{ msg }}</span>
598
+ }
401
599
  } @else {
402
- <span class="ag-cell-value">{{ displayValue() }}</span>
600
+ @if (treeCell()) {
601
+ <span class="ag-tree-prefix" [style.padding-left.px]="treeLevel() * treeIndent">
602
+ @if (treeExpandable()) {
603
+ <button
604
+ type="button"
605
+ class="ag-tree-twisty"
606
+ [class.ag-tree-twisty--expanded]="treeExpanded()"
607
+ [attr.aria-label]="treeExpanded() ? 'Collapse' : 'Expand'"
608
+ (click)="onTreeToggle($event)"
609
+ >▶</button>
610
+ } @else {
611
+ <span class="ag-tree-twisty-spacer"></span>
612
+ }
613
+ </span>
614
+ }
615
+ @if (col().cellRenderer) {
616
+ <span class="ag-cell-value" [innerHTML]="renderedHtml()"></span>
617
+ } @else {
618
+ <span class="ag-cell-value">{{ displayValue() }}</span>
619
+ }
403
620
  }
404
- `, 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}.ag-cell-input{width:100%;height:100%;border:none;outline:none;font:inherit;padding:0 6px;box-sizing:border-box;background:transparent}.ag-cell-select{width:100%;height:100%;border:none;outline:none;font:inherit;padding:0 2px;box-sizing:border-box;background:transparent;cursor:pointer}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
621
+ `, 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}.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 });
405
622
  }
406
623
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AgridCellComponent, decorators: [{
407
624
  type: Component,
@@ -409,12 +626,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
409
626
  role: 'gridcell',
410
627
  '[class.selected]': 'selected()',
411
628
  '[class.editing]': 'editing()',
629
+ '[class.ag-cell--tree]': 'treeCell()',
412
630
  '[attr.aria-readonly]': 'col().editable === false ? "true" : null',
413
631
  '(click)': 'activate.emit($event)',
414
632
  '(dblclick)': 'startEdit.emit()',
415
633
  tabindex: '-1',
416
634
  }, template: `
417
- @if (editing()) {
635
+ @if (booleanCell()) {
636
+ <input
637
+ type="checkbox"
638
+ class="ag-cell-checkbox"
639
+ [checked]="booleanChecked()"
640
+ [disabled]="!editable()"
641
+ [attr.aria-label]="col().header"
642
+ (click)="onCheckboxClick($event)"
643
+ (dblclick)="$event.stopPropagation()"
644
+ />
645
+ } @else if (editing()) {
418
646
  @if (col().values?.length) {
419
647
  <select
420
648
  #editSelect
@@ -429,18 +657,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
429
657
  <input
430
658
  #editInput
431
659
  class="ag-cell-input"
660
+ [class.ag-cell-input--invalid]="!!error()"
432
661
  [type]="col().type === 'date' ? 'date' : col().type === 'number' ? 'number' : 'text'"
433
662
  [value]="editorValue()"
434
663
  (input)="onInput($event)"
435
664
  />
436
665
  }
437
- } @else if (col().cellRenderer) {
438
- <span class="ag-cell-value" [innerHTML]="renderedHtml()"></span>
666
+ @if (error(); as msg) {
667
+ <span class="ag-cell-error" role="alert">{{ msg }}</span>
668
+ }
439
669
  } @else {
440
- <span class="ag-cell-value">{{ displayValue() }}</span>
670
+ @if (treeCell()) {
671
+ <span class="ag-tree-prefix" [style.padding-left.px]="treeLevel() * treeIndent">
672
+ @if (treeExpandable()) {
673
+ <button
674
+ type="button"
675
+ class="ag-tree-twisty"
676
+ [class.ag-tree-twisty--expanded]="treeExpanded()"
677
+ [attr.aria-label]="treeExpanded() ? 'Collapse' : 'Expand'"
678
+ (click)="onTreeToggle($event)"
679
+ >▶</button>
680
+ } @else {
681
+ <span class="ag-tree-twisty-spacer"></span>
682
+ }
683
+ </span>
684
+ }
685
+ @if (col().cellRenderer) {
686
+ <span class="ag-cell-value" [innerHTML]="renderedHtml()"></span>
687
+ } @else {
688
+ <span class="ag-cell-value">{{ displayValue() }}</span>
689
+ }
441
690
  }
442
- `, 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}.ag-cell-input{width:100%;height:100%;border:none;outline:none;font:inherit;padding:0 6px;box-sizing:border-box;background:transparent}.ag-cell-select{width:100%;height:100%;border:none;outline:none;font:inherit;padding:0 2px;box-sizing:border-box;background:transparent;cursor:pointer}\n"] }]
443
- }], 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 }] }], 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 }] }], seedChar: [{ type: i0.Input, args: [{ isSignal: true, alias: "seedChar", required: false }] }], activate: [{ type: i0.Output, args: ["activate"] }], startEdit: [{ type: i0.Output, args: ["startEdit"] }], draftChange: [{ type: i0.Output, args: ["draftChange"] }], inputEl: [{ type: i0.ViewChild, args: ['editInput', { isSignal: true }] }], selectEl: [{ type: i0.ViewChild, args: ['editSelect', { isSignal: true }] }] } });
691
+ `, 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}.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"] }]
692
+ }], 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 }] }], 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 }] }], 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 }] }], activate: [{ type: i0.Output, args: ["activate"] }], startEdit: [{ type: i0.Output, args: ["startEdit"] }], booleanToggle: [{ type: i0.Output, args: ["booleanToggle"] }], draftChange: [{ type: i0.Output, args: ["draftChange"] }], inputEl: [{ type: i0.ViewChild, args: ['editInput', { isSignal: true }] }], selectEl: [{ type: i0.ViewChild, args: ['editSelect', { isSignal: true }] }] } });
444
693
 
445
694
  /** Guarded access to browser-only APIs used by the grid. @internal */
446
695
  class AgridBrowserAdapter {
@@ -907,6 +1156,18 @@ const AGRID_LOCALE_TEXT = {
907
1156
  columns: 'Columns',
908
1157
  detail: 'Detail',
909
1158
  hiddenColumn: '(hidden)',
1159
+ filterCondition: 'Condition',
1160
+ filterNoCondition: 'No condition',
1161
+ filterOpEquals: 'Equals',
1162
+ filterOpNotEquals: 'Not equal',
1163
+ filterOpGreater: 'Greater than',
1164
+ filterOpGreaterEqual: 'Greater or equal',
1165
+ filterOpLess: 'Less than',
1166
+ filterOpLessEqual: 'Less or equal',
1167
+ filterOpBetween: 'Between',
1168
+ filterOpBefore: 'Before',
1169
+ filterOpAfter: 'After',
1170
+ filterOpOn: 'On',
910
1171
  copyCellValue: 'Copy cell',
911
1172
  copyRow: 'Copy row',
912
1173
  confirmDeleteRow: 'Sure to delete?',
@@ -933,6 +1194,7 @@ const AGRID_LOCALE_TEXT = {
933
1194
  previous: 'Previous',
934
1195
  resizeColumn: 'Resize column',
935
1196
  rows: count => `${count} ${count === 1 ? 'row' : 'rows'}`,
1197
+ quickFilterPlaceholder: 'Search all columns...',
936
1198
  searchValuesPlaceholder: 'Search values...',
937
1199
  selectAll: '(Select All)',
938
1200
  sortOnlyByThis: 'Sort only by this column',
@@ -961,6 +1223,18 @@ const AGRID_LOCALE_TEXT = {
961
1223
  columns: 'Spalten',
962
1224
  detail: 'Details',
963
1225
  hiddenColumn: '(ausgeblendet)',
1226
+ filterCondition: 'Bedingung',
1227
+ filterNoCondition: 'Keine Bedingung',
1228
+ filterOpEquals: 'Gleich',
1229
+ filterOpNotEquals: 'Ungleich',
1230
+ filterOpGreater: 'Größer als',
1231
+ filterOpGreaterEqual: 'Größer oder gleich',
1232
+ filterOpLess: 'Kleiner als',
1233
+ filterOpLessEqual: 'Kleiner oder gleich',
1234
+ filterOpBetween: 'Zwischen',
1235
+ filterOpBefore: 'Vor',
1236
+ filterOpAfter: 'Nach',
1237
+ filterOpOn: 'Am',
964
1238
  copyCellValue: 'Zelle kopieren',
965
1239
  copyRow: 'Zeile kopieren',
966
1240
  confirmDeleteRow: 'Wirklich löschen?',
@@ -987,6 +1261,7 @@ const AGRID_LOCALE_TEXT = {
987
1261
  previous: 'Zurück',
988
1262
  resizeColumn: 'Spaltenbreite ändern',
989
1263
  rows: count => `${count} ${count === 1 ? 'Zeile' : 'Zeilen'}`,
1264
+ quickFilterPlaceholder: 'Alle Spalten durchsuchen...',
990
1265
  searchValuesPlaceholder: 'Werte suchen...',
991
1266
  selectAll: '(Alle auswählen)',
992
1267
  sortOnlyByThis: 'Nur nach dieser Spalte sortieren',
@@ -1054,6 +1329,20 @@ class AgridColumnMenuComponent {
1054
1329
  filterable = input(false, ...(ngDevMode ? [{ debugName: "filterable" }] : /* istanbul ignore next */ []));
1055
1330
  /** Whether to show the Excel-style distinct-value picker. */
1056
1331
  showValueFilter = input(true, ...(ngDevMode ? [{ debugName: "showValueFilter" }] : /* istanbul ignore next */ []));
1332
+ /** Range-filter input type for the active column, or `null` to hide the condition UI. */
1333
+ filterType = input(null, ...(ngDevMode ? [{ debugName: "filterType" }] : /* istanbul ignore next */ []));
1334
+ /** Current range-filter operator, or `null` when none is selected. */
1335
+ operator = input(null, ...(ngDevMode ? [{ debugName: "operator" }] : /* istanbul ignore next */ []));
1336
+ /** Current primary range-filter operand. */
1337
+ operand = input('', ...(ngDevMode ? [{ debugName: "operand" }] : /* istanbul ignore next */ []));
1338
+ /** Current secondary range-filter operand (used by `between`). */
1339
+ operand2 = input('', ...(ngDevMode ? [{ debugName: "operand2" }] : /* istanbul ignore next */ []));
1340
+ /** Emits the selected range-filter operator, or `null` to clear the condition. */
1341
+ operatorChange = output();
1342
+ /** Emits the primary range-filter operand text. */
1343
+ operandChange = output();
1344
+ /** Emits the secondary range-filter operand text (used by `between`). */
1345
+ operand2Change = output();
1057
1346
  /** Current search text for the value-filter option list. */
1058
1347
  search = input('', ...(ngDevMode ? [{ debugName: "search" }] : /* istanbul ignore next */ []));
1059
1348
  /** Whether the value filter currently allows every value. */
@@ -1105,13 +1394,36 @@ class AgridColumnMenuComponent {
1105
1394
  { value: 'count', symbol: '#', label: t.aggregateCount },
1106
1395
  ];
1107
1396
  }, ...(ngDevMode ? [{ debugName: "aggregateOptions" }] : /* istanbul ignore next */ []));
1397
+ /** Range-filter operators offered for the active column, labeled per column type. */
1398
+ operatorOptions = computed(() => {
1399
+ const t = this.localeText();
1400
+ if (this.filterType() === 'date') {
1401
+ return [
1402
+ { value: 'eq', label: t.filterOpOn },
1403
+ { value: 'lt', label: t.filterOpBefore },
1404
+ { value: 'gt', label: t.filterOpAfter },
1405
+ { value: 'between', label: t.filterOpBetween },
1406
+ ];
1407
+ }
1408
+ return [
1409
+ { value: 'eq', label: t.filterOpEquals },
1410
+ { value: 'neq', label: t.filterOpNotEquals },
1411
+ { value: 'gt', label: t.filterOpGreater },
1412
+ { value: 'gte', label: t.filterOpGreaterEqual },
1413
+ { value: 'lt', label: t.filterOpLess },
1414
+ { value: 'lte', label: t.filterOpLessEqual },
1415
+ { value: 'between', label: t.filterOpBetween },
1416
+ ];
1417
+ }, ...(ngDevMode ? [{ debugName: "operatorOptions" }] : /* istanbul ignore next */ []));
1418
+ /** Native input type for operand fields (date columns use a date picker). */
1419
+ operandInputType = computed(() => (this.filterType() === 'date' ? 'date' : 'number'), ...(ngDevMode ? [{ debugName: "operandInputType" }] : /* istanbul ignore next */ []));
1108
1420
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AgridColumnMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1109
- 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 });
1421
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: AgridColumnMenuComponent, isStandalone: true, selector: "agrid-column-menu", inputs: { localeText: { classPropertyName: "localeText", publicName: "localeText", isSignal: true, isRequired: false, transformFunction: null }, x: { classPropertyName: "x", publicName: "x", isSignal: true, isRequired: true, transformFunction: null }, y: { classPropertyName: "y", publicName: "y", isSignal: true, isRequired: true, transformFunction: null }, header: { classPropertyName: "header", publicName: "header", isSignal: true, isRequired: true, transformFunction: null }, sortDir: { classPropertyName: "sortDir", publicName: "sortDir", isSignal: true, isRequired: false, transformFunction: null }, sortable: { classPropertyName: "sortable", publicName: "sortable", isSignal: true, isRequired: false, transformFunction: null }, showColumnActions: { classPropertyName: "showColumnActions", publicName: "showColumnActions", isSignal: true, isRequired: false, transformFunction: null }, pinned: { classPropertyName: "pinned", publicName: "pinned", isSignal: true, isRequired: false, transformFunction: null }, groupable: { classPropertyName: "groupable", publicName: "groupable", isSignal: true, isRequired: false, transformFunction: null }, grouped: { classPropertyName: "grouped", publicName: "grouped", isSignal: true, isRequired: false, transformFunction: null }, filterable: { classPropertyName: "filterable", publicName: "filterable", isSignal: true, isRequired: false, transformFunction: null }, showValueFilter: { classPropertyName: "showValueFilter", publicName: "showValueFilter", isSignal: true, isRequired: false, transformFunction: null }, filterType: { classPropertyName: "filterType", publicName: "filterType", isSignal: true, isRequired: false, transformFunction: null }, operator: { classPropertyName: "operator", publicName: "operator", isSignal: true, isRequired: false, transformFunction: null }, operand: { classPropertyName: "operand", publicName: "operand", isSignal: true, isRequired: false, transformFunction: null }, operand2: { classPropertyName: "operand2", publicName: "operand2", isSignal: true, isRequired: false, transformFunction: null }, search: { classPropertyName: "search", publicName: "search", isSignal: true, isRequired: false, transformFunction: null }, allSelected: { classPropertyName: "allSelected", publicName: "allSelected", isSignal: true, isRequired: false, transformFunction: null }, valueItems: { classPropertyName: "valueItems", publicName: "valueItems", isSignal: true, isRequired: false, transformFunction: null }, sortPriority: { classPropertyName: "sortPriority", publicName: "sortPriority", isSignal: true, isRequired: false, transformFunction: null }, hasMultiSort: { classPropertyName: "hasMultiSort", publicName: "hasMultiSort", isSignal: true, isRequired: false, transformFunction: null }, aggregate: { classPropertyName: "aggregate", publicName: "aggregate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { operatorChange: "operatorChange", operandChange: "operandChange", operand2Change: "operand2Change", sort: "sort", resetSort: "resetSort", autosize: "autosize", togglePin: "togglePin", togglePinRight: "togglePinRight", hide: "hide", toggleGroup: "toggleGroup", clearFilter: "clearFilter", clearAll: "clearAll", searchChange: "searchChange", toggleAll: "toggleAll", toggleValue: "toggleValue", setAggregate: "setAggregate" }, ngImport: i0, template: "<div\n class=\"ag-filter-menu\"\n role=\"dialog\"\n [attr.aria-label]=\"localeText().columnMenu + ': ' + header()\"\n [style.left.px]=\"x()\"\n [style.top.px]=\"y()\"\n (click)=\"$event.stopPropagation()\"\n>\n @if (sortable()) {\n <div class=\"ag-filter-menu-section\">\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"sortDir() === 'asc'\"\n (click)=\"sort.emit('asc')\"\n >\n \u2191 {{ localeText().sortAscending }}\n @if (sortPriority() > 0) {\n <span class=\"ag-sort-priority-badge\">{{ sortPriority() }}</span>\n }\n </button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"sortDir() === 'desc'\"\n (click)=\"sort.emit('desc')\"\n >\n \u2193 {{ localeText().sortDescending }}\n @if (sortPriority() > 0) {\n <span class=\"ag-sort-priority-badge\">{{ sortPriority() }}</span>\n }\n </button>\n @if (hasMultiSort() && sortPriority() > 0) {\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"resetSort.emit(sortDir()!)\">{{ localeText().sortOnlyByThis }}</button>\n }\n @if (sortDir()) {\n <button class=\"ag-filter-menu-item\" (click)=\"sort.emit(sortDir()!)\">{{ localeText().clearSort }}</button>\n }\n </div>\n }\n\n @if (showColumnActions()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button class=\"ag-filter-menu-item\" (click)=\"autosize.emit()\">\u2194 {{ localeText().autosizeColumn }}</button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"pinned() === 'left'\"\n (click)=\"togglePin.emit()\"\n >\u22A3 {{ pinned() === 'left' ? localeText().unpinColumn : localeText().pinColumn }}</button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"pinned() === 'right'\"\n (click)=\"togglePinRight.emit()\"\n >\u22A2 {{ pinned() === 'right' ? localeText().unpinColumnRight : localeText().pinColumnRight }}</button>\n <button class=\"ag-filter-menu-item\" (click)=\"hide.emit()\">{{ localeText().hideColumn }}</button>\n </div>\n }\n\n @if (showColumnActions()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <span class=\"ag-filter-menu-aggregate-label\">{{ localeText().aggregate }}</span>\n <div class=\"ag-filter-menu-aggregate-btns\">\n @for (opt of aggregateOptions(); track opt.value) {\n <button\n class=\"ag-aggregate-btn\"\n [class.ag-aggregate-btn--active]=\"aggregate() === opt.value\"\n [title]=\"opt.label\"\n (click)=\"setAggregate.emit(aggregate() === opt.value ? null : opt.value)\"\n >{{ opt.symbol }}</button>\n }\n </div>\n </div>\n }\n\n @if (groupable()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"grouped()\"\n (click)=\"toggleGroup.emit()\"\n >{{ grouped() ? '\u229F ' + localeText().ungroup : '\u229E ' + localeText().groupBy(header()) }}</button>\n </div>\n }\n\n @if (filterable()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"clearFilter.emit()\">\n \u2715 {{ localeText().clearFilter }}\n </button>\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"clearAll.emit()\">\n \u2715 {{ localeText().clearAllFilters }}\n </button>\n </div>\n\n @if (filterType()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border ag-filter-menu-condition\">\n <span class=\"ag-filter-menu-aggregate-label\">{{ localeText().filterCondition }}</span>\n <select\n class=\"ag-filter-menu-operator\"\n (change)=\"operatorChange.emit($any($event.target).value || null)\"\n (click)=\"$event.stopPropagation()\"\n >\n <option value=\"\" [selected]=\"!operator()\">{{ localeText().filterNoCondition }}</option>\n @for (op of operatorOptions(); track op.value) {\n <option [value]=\"op.value\" [selected]=\"op.value === operator()\">{{ op.label }}</option>\n }\n </select>\n @if (operator()) {\n <input\n class=\"ag-filter-menu-operand\"\n [type]=\"operandInputType()\"\n [value]=\"operand()\"\n (input)=\"operandChange.emit($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n @if (operator() === 'between') {\n <input\n class=\"ag-filter-menu-operand\"\n [type]=\"operandInputType()\"\n [value]=\"operand2()\"\n (input)=\"operand2Change.emit($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n }\n </div>\n }\n\n @if (showValueFilter()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <input\n class=\"ag-filter-menu-search\"\n [value]=\"search()\"\n (input)=\"searchChange.emit($any($event.target).value)\"\n [placeholder]=\"localeText().searchValuesPlaceholder\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n\n <div class=\"ag-filter-menu-values\">\n <label class=\"ag-filter-menu-value\">\n <input\n type=\"checkbox\"\n [checked]=\"allSelected()\"\n (change)=\"toggleAll.emit()\"\n />\n <span class=\"ag-filter-menu-value-label\">{{ localeText().selectAll }}</span>\n </label>\n @for (item of valueItems(); track item.rawStr) {\n <label\n class=\"ag-filter-menu-value\"\n [class.ag-filter-menu-value--inactive]=\"!item.active\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"item.selected\"\n [disabled]=\"!item.active\"\n (change)=\"toggleValue.emit(item.rawStr)\"\n />\n <span class=\"ag-filter-menu-value-label\">{{ item.label || localeText().blank }}</span>\n </label>\n }\n </div>\n }\n }\n</div>\n", styles: [".ag-filter-menu{position:fixed;z-index:1001;background:var(--agrid-color-bg-subtle);border:1px solid var(--agrid-color-border);border-radius:6px;box-shadow:0 8px 24px var(--agrid-color-shadow);min-width:200px;font-size:13px;overflow:hidden}.ag-filter-menu-section{padding:4px 0}.ag-filter-menu-section--border{border-top:1px solid var(--agrid-color-border)}.ag-filter-menu-item{display:block;width:100%;padding:5px 14px;text-align:left;background:none;border:none;cursor:pointer;color:var(--agrid-color-text);font:inherit;font-size:12px}.ag-filter-menu-item:hover{background:var(--agrid-color-bg-muted)}.ag-filter-menu-item--active{color:var(--agrid-color-accent);font-weight:600}.ag-filter-menu-item--muted{color:var(--agrid-color-text-muted)}.ag-filter-menu-item--muted:hover{background:var(--mat-toolbar-container-background-color);color:var(--agrid-color-text-muted);cursor:not-allowed}.ag-filter-menu-search{background:var(--agrid-color-bg-subtle);display:block;width:calc(100% - 16px);margin:0 8px;height:24px;border:1px solid var(--agrid-color-border);border-radius:3px;outline:none;font:inherit;font-size:12px;padding:0 6px}.ag-filter-menu-search:focus{border-color:var(--agrid-color-accent)}.ag-filter-menu-values{max-height:200px;overflow-y:auto;padding:4px 0;border-top:1px solid var(--agrid-color-border)}.ag-filter-menu-value{display:flex;align-items:center;gap:6px;padding:3px 14px;cursor:pointer;font-size:12px}.ag-filter-menu-value:hover{background:var(--agrid-color-bg-muted)}.ag-filter-menu-value input[type=checkbox]{cursor:pointer;margin:0}.ag-filter-menu-value--inactive{opacity:.38;cursor:default}.ag-filter-menu-value--inactive input[type=checkbox]{cursor:not-allowed}.ag-filter-menu-value-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ag-sort-priority-badge{display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;border-radius:50%;background:var(--agrid-color-accent);color:#fff;font-size:9px;font-weight:700;margin-left:auto}.ag-filter-menu-aggregate-label{display:block;padding:4px 14px 2px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--agrid-color-text-muted);-webkit-user-select:none;user-select:none}.ag-filter-menu-aggregate-btns{display:flex;gap:4px;padding:2px 14px 6px}.ag-aggregate-btn{flex:1;height:26px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text-muted);font-size:13px;cursor:pointer;padding:0;transition:background 80ms,color 80ms}.ag-aggregate-btn:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text);border-color:var(--agrid-color-text-muted)}.ag-aggregate-btn--active{background:var(--agrid-color-accent-subtle);border-color:var(--agrid-color-accent-border);color:var(--agrid-color-accent-fg);font-weight:700}.ag-filter-menu-condition{display:flex;flex-direction:column;gap:6px;padding:6px 14px 8px}.ag-filter-menu-operator,.ag-filter-menu-operand{height:28px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font-size:13px;padding:0 8px;box-sizing:border-box;width:100%}.ag-filter-menu-operator:focus,.ag-filter-menu-operand:focus{outline:none;border-color:var(--agrid-color-accent-border)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1110
1422
  }
1111
1423
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AgridColumnMenuComponent, decorators: [{
1112
1424
  type: Component,
1113
- 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"] }]
1114
- }], 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"] }] } });
1425
+ args: [{ selector: 'agrid-column-menu', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"ag-filter-menu\"\n role=\"dialog\"\n [attr.aria-label]=\"localeText().columnMenu + ': ' + header()\"\n [style.left.px]=\"x()\"\n [style.top.px]=\"y()\"\n (click)=\"$event.stopPropagation()\"\n>\n @if (sortable()) {\n <div class=\"ag-filter-menu-section\">\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"sortDir() === 'asc'\"\n (click)=\"sort.emit('asc')\"\n >\n \u2191 {{ localeText().sortAscending }}\n @if (sortPriority() > 0) {\n <span class=\"ag-sort-priority-badge\">{{ sortPriority() }}</span>\n }\n </button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"sortDir() === 'desc'\"\n (click)=\"sort.emit('desc')\"\n >\n \u2193 {{ localeText().sortDescending }}\n @if (sortPriority() > 0) {\n <span class=\"ag-sort-priority-badge\">{{ sortPriority() }}</span>\n }\n </button>\n @if (hasMultiSort() && sortPriority() > 0) {\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"resetSort.emit(sortDir()!)\">{{ localeText().sortOnlyByThis }}</button>\n }\n @if (sortDir()) {\n <button class=\"ag-filter-menu-item\" (click)=\"sort.emit(sortDir()!)\">{{ localeText().clearSort }}</button>\n }\n </div>\n }\n\n @if (showColumnActions()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button class=\"ag-filter-menu-item\" (click)=\"autosize.emit()\">\u2194 {{ localeText().autosizeColumn }}</button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"pinned() === 'left'\"\n (click)=\"togglePin.emit()\"\n >\u22A3 {{ pinned() === 'left' ? localeText().unpinColumn : localeText().pinColumn }}</button>\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"pinned() === 'right'\"\n (click)=\"togglePinRight.emit()\"\n >\u22A2 {{ pinned() === 'right' ? localeText().unpinColumnRight : localeText().pinColumnRight }}</button>\n <button class=\"ag-filter-menu-item\" (click)=\"hide.emit()\">{{ localeText().hideColumn }}</button>\n </div>\n }\n\n @if (showColumnActions()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <span class=\"ag-filter-menu-aggregate-label\">{{ localeText().aggregate }}</span>\n <div class=\"ag-filter-menu-aggregate-btns\">\n @for (opt of aggregateOptions(); track opt.value) {\n <button\n class=\"ag-aggregate-btn\"\n [class.ag-aggregate-btn--active]=\"aggregate() === opt.value\"\n [title]=\"opt.label\"\n (click)=\"setAggregate.emit(aggregate() === opt.value ? null : opt.value)\"\n >{{ opt.symbol }}</button>\n }\n </div>\n </div>\n }\n\n @if (groupable()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button\n class=\"ag-filter-menu-item\"\n [class.ag-filter-menu-item--active]=\"grouped()\"\n (click)=\"toggleGroup.emit()\"\n >{{ grouped() ? '\u229F ' + localeText().ungroup : '\u229E ' + localeText().groupBy(header()) }}</button>\n </div>\n }\n\n @if (filterable()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"clearFilter.emit()\">\n \u2715 {{ localeText().clearFilter }}\n </button>\n <button class=\"ag-filter-menu-item ag-filter-menu-item--muted\" (click)=\"clearAll.emit()\">\n \u2715 {{ localeText().clearAllFilters }}\n </button>\n </div>\n\n @if (filterType()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border ag-filter-menu-condition\">\n <span class=\"ag-filter-menu-aggregate-label\">{{ localeText().filterCondition }}</span>\n <select\n class=\"ag-filter-menu-operator\"\n (change)=\"operatorChange.emit($any($event.target).value || null)\"\n (click)=\"$event.stopPropagation()\"\n >\n <option value=\"\" [selected]=\"!operator()\">{{ localeText().filterNoCondition }}</option>\n @for (op of operatorOptions(); track op.value) {\n <option [value]=\"op.value\" [selected]=\"op.value === operator()\">{{ op.label }}</option>\n }\n </select>\n @if (operator()) {\n <input\n class=\"ag-filter-menu-operand\"\n [type]=\"operandInputType()\"\n [value]=\"operand()\"\n (input)=\"operandChange.emit($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n @if (operator() === 'between') {\n <input\n class=\"ag-filter-menu-operand\"\n [type]=\"operandInputType()\"\n [value]=\"operand2()\"\n (input)=\"operand2Change.emit($any($event.target).value)\"\n (click)=\"$event.stopPropagation()\"\n />\n }\n }\n </div>\n }\n\n @if (showValueFilter()) {\n <div class=\"ag-filter-menu-section ag-filter-menu-section--border\">\n <input\n class=\"ag-filter-menu-search\"\n [value]=\"search()\"\n (input)=\"searchChange.emit($any($event.target).value)\"\n [placeholder]=\"localeText().searchValuesPlaceholder\"\n (click)=\"$event.stopPropagation()\"\n />\n </div>\n\n <div class=\"ag-filter-menu-values\">\n <label class=\"ag-filter-menu-value\">\n <input\n type=\"checkbox\"\n [checked]=\"allSelected()\"\n (change)=\"toggleAll.emit()\"\n />\n <span class=\"ag-filter-menu-value-label\">{{ localeText().selectAll }}</span>\n </label>\n @for (item of valueItems(); track item.rawStr) {\n <label\n class=\"ag-filter-menu-value\"\n [class.ag-filter-menu-value--inactive]=\"!item.active\"\n >\n <input\n type=\"checkbox\"\n [checked]=\"item.selected\"\n [disabled]=\"!item.active\"\n (change)=\"toggleValue.emit(item.rawStr)\"\n />\n <span class=\"ag-filter-menu-value-label\">{{ item.label || localeText().blank }}</span>\n </label>\n }\n </div>\n }\n }\n</div>\n", styles: [".ag-filter-menu{position:fixed;z-index:1001;background:var(--agrid-color-bg-subtle);border:1px solid var(--agrid-color-border);border-radius:6px;box-shadow:0 8px 24px var(--agrid-color-shadow);min-width:200px;font-size:13px;overflow:hidden}.ag-filter-menu-section{padding:4px 0}.ag-filter-menu-section--border{border-top:1px solid var(--agrid-color-border)}.ag-filter-menu-item{display:block;width:100%;padding:5px 14px;text-align:left;background:none;border:none;cursor:pointer;color:var(--agrid-color-text);font:inherit;font-size:12px}.ag-filter-menu-item:hover{background:var(--agrid-color-bg-muted)}.ag-filter-menu-item--active{color:var(--agrid-color-accent);font-weight:600}.ag-filter-menu-item--muted{color:var(--agrid-color-text-muted)}.ag-filter-menu-item--muted:hover{background:var(--mat-toolbar-container-background-color);color:var(--agrid-color-text-muted);cursor:not-allowed}.ag-filter-menu-search{background:var(--agrid-color-bg-subtle);display:block;width:calc(100% - 16px);margin:0 8px;height:24px;border:1px solid var(--agrid-color-border);border-radius:3px;outline:none;font:inherit;font-size:12px;padding:0 6px}.ag-filter-menu-search:focus{border-color:var(--agrid-color-accent)}.ag-filter-menu-values{max-height:200px;overflow-y:auto;padding:4px 0;border-top:1px solid var(--agrid-color-border)}.ag-filter-menu-value{display:flex;align-items:center;gap:6px;padding:3px 14px;cursor:pointer;font-size:12px}.ag-filter-menu-value:hover{background:var(--agrid-color-bg-muted)}.ag-filter-menu-value input[type=checkbox]{cursor:pointer;margin:0}.ag-filter-menu-value--inactive{opacity:.38;cursor:default}.ag-filter-menu-value--inactive input[type=checkbox]{cursor:not-allowed}.ag-filter-menu-value-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ag-sort-priority-badge{display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;border-radius:50%;background:var(--agrid-color-accent);color:#fff;font-size:9px;font-weight:700;margin-left:auto}.ag-filter-menu-aggregate-label{display:block;padding:4px 14px 2px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--agrid-color-text-muted);-webkit-user-select:none;user-select:none}.ag-filter-menu-aggregate-btns{display:flex;gap:4px;padding:2px 14px 6px}.ag-aggregate-btn{flex:1;height:26px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text-muted);font-size:13px;cursor:pointer;padding:0;transition:background 80ms,color 80ms}.ag-aggregate-btn:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text);border-color:var(--agrid-color-text-muted)}.ag-aggregate-btn--active{background:var(--agrid-color-accent-subtle);border-color:var(--agrid-color-accent-border);color:var(--agrid-color-accent-fg);font-weight:700}.ag-filter-menu-condition{display:flex;flex-direction:column;gap:6px;padding:6px 14px 8px}.ag-filter-menu-operator,.ag-filter-menu-operand{height:28px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font-size:13px;padding:0 8px;box-sizing:border-box;width:100%}.ag-filter-menu-operator:focus,.ag-filter-menu-operand:focus{outline:none;border-color:var(--agrid-color-accent-border)}\n"] }]
1426
+ }], propDecorators: { localeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "localeText", required: false }] }], x: [{ type: i0.Input, args: [{ isSignal: true, alias: "x", required: true }] }], y: [{ type: i0.Input, args: [{ isSignal: true, alias: "y", required: true }] }], header: [{ type: i0.Input, args: [{ isSignal: true, alias: "header", required: true }] }], sortDir: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortDir", required: false }] }], sortable: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortable", required: false }] }], showColumnActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showColumnActions", required: false }] }], pinned: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinned", required: false }] }], groupable: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupable", required: false }] }], grouped: [{ type: i0.Input, args: [{ isSignal: true, alias: "grouped", required: false }] }], filterable: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterable", required: false }] }], showValueFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValueFilter", required: false }] }], filterType: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterType", required: false }] }], operator: [{ type: i0.Input, args: [{ isSignal: true, alias: "operator", required: false }] }], operand: [{ type: i0.Input, args: [{ isSignal: true, alias: "operand", required: false }] }], operand2: [{ type: i0.Input, args: [{ isSignal: true, alias: "operand2", required: false }] }], operatorChange: [{ type: i0.Output, args: ["operatorChange"] }], operandChange: [{ type: i0.Output, args: ["operandChange"] }], operand2Change: [{ type: i0.Output, args: ["operand2Change"] }], search: [{ type: i0.Input, args: [{ isSignal: true, alias: "search", required: false }] }], allSelected: [{ type: i0.Input, args: [{ isSignal: true, alias: "allSelected", required: false }] }], valueItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueItems", required: false }] }], sortPriority: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortPriority", required: false }] }], hasMultiSort: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasMultiSort", required: false }] }], sort: [{ type: i0.Output, args: ["sort"] }], resetSort: [{ type: i0.Output, args: ["resetSort"] }], autosize: [{ type: i0.Output, args: ["autosize"] }], togglePin: [{ type: i0.Output, args: ["togglePin"] }], togglePinRight: [{ type: i0.Output, args: ["togglePinRight"] }], hide: [{ type: i0.Output, args: ["hide"] }], toggleGroup: [{ type: i0.Output, args: ["toggleGroup"] }], clearFilter: [{ type: i0.Output, args: ["clearFilter"] }], clearAll: [{ type: i0.Output, args: ["clearAll"] }], searchChange: [{ type: i0.Output, args: ["searchChange"] }], toggleAll: [{ type: i0.Output, args: ["toggleAll"] }], toggleValue: [{ type: i0.Output, args: ["toggleValue"] }], aggregate: [{ type: i0.Input, args: [{ isSignal: true, alias: "aggregate", required: false }] }], setAggregate: [{ type: i0.Output, args: ["setAggregate"] }] } });
1115
1427
 
1116
1428
  /** Owns column-menu state, filters, sorting, and menu-triggered column mutations. @internal */
1117
1429
  class AgridColumnMenuController {
@@ -1197,6 +1509,84 @@ class AgridColumnMenuController {
1197
1509
  getTextFilter(field) {
1198
1510
  return this.opts.control()?.getFilter(field).text ?? '';
1199
1511
  }
1512
+ /** Returns the range-filter input type for a field, or `null` when not range-filterable. */
1513
+ getFilterType(field) {
1514
+ const type = this.getColDef(field)?.type;
1515
+ return type === 'number' || type === 'date' ? type : null;
1516
+ }
1517
+ /** Returns the typed range-filter operator for a field, or `null`. */
1518
+ getFilterOperator(field) {
1519
+ return this.opts.control()?.getFilter(field).operator ?? null;
1520
+ }
1521
+ /** Returns the primary range-filter operand for a field. */
1522
+ getFilterOperand(field) {
1523
+ return this.opts.control()?.getFilter(field).operand ?? '';
1524
+ }
1525
+ /** Returns the secondary range-filter operand (used by `between`). */
1526
+ getFilterOperand2(field) {
1527
+ return this.opts.control()?.getFilter(field).operand2 ?? '';
1528
+ }
1529
+ /** Sets the range-filter operator; clearing it (`null`) also drops the operands. */
1530
+ setFilterOperator(field, operator) {
1531
+ const control = this.opts.control();
1532
+ if (!control)
1533
+ return;
1534
+ if (!operator) {
1535
+ control.setRangeFilter(field, null, null, null);
1536
+ }
1537
+ else {
1538
+ const current = control.getFilter(field);
1539
+ control.setRangeFilter(field, operator, current.operand ?? '', current.operand2 ?? '');
1540
+ }
1541
+ this.emitRangeServer(field, false);
1542
+ }
1543
+ /** Sets the primary range-filter operand. */
1544
+ setFilterOperand(field, value) {
1545
+ const control = this.opts.control();
1546
+ if (!control)
1547
+ return;
1548
+ const current = control.getFilter(field);
1549
+ control.setRangeFilter(field, current.operator ?? 'eq', value, current.operand2 ?? '');
1550
+ this.emitRangeServer(field, true);
1551
+ }
1552
+ /** Sets the secondary range-filter operand (used by `between`). */
1553
+ setFilterOperand2(field, value) {
1554
+ const control = this.opts.control();
1555
+ if (!control)
1556
+ return;
1557
+ const current = control.getFilter(field);
1558
+ control.setRangeFilter(field, current.operator ?? 'between', current.operand ?? '', value);
1559
+ this.emitRangeServer(field, true);
1560
+ }
1561
+ /**
1562
+ * In server-side filtering mode, emit a {@link FilterChangeEvent} carrying the current range
1563
+ * condition so the host can refetch. Operand edits are debounced; operator changes are immediate.
1564
+ * No-op in client mode (the projection layer filters locally).
1565
+ */
1566
+ emitRangeServer(field, debounce) {
1567
+ if (!this.opts.serverSideFiltering())
1568
+ return;
1569
+ const emit = () => {
1570
+ const f = this.opts.control()?.getFilter(field);
1571
+ this.opts.onFilterChange({
1572
+ field,
1573
+ value: '',
1574
+ operator: f?.operator ?? null,
1575
+ operand: f?.operand ?? null,
1576
+ operand2: f?.operand2 ?? null,
1577
+ });
1578
+ };
1579
+ this.cancelFilterDebounce(field);
1580
+ const delay = this.opts.filterDebounceMs();
1581
+ if (!debounce || delay === 0) {
1582
+ emit();
1583
+ return;
1584
+ }
1585
+ this.filterDebounces.set(field, setTimeout(() => {
1586
+ this.filterDebounces.delete(field);
1587
+ emit();
1588
+ }, delay));
1589
+ }
1200
1590
  /** Returns the active sort direction, respecting disabled sorting. */
1201
1591
  getSort(field) {
1202
1592
  return this.opts.sortOption() === 'none'
@@ -1309,6 +1699,9 @@ class AgridColumnMenuController {
1309
1699
  if (this.opts.serverSideFiltering()) {
1310
1700
  if (previous.text)
1311
1701
  this.opts.onFilterChange({ field, value: '' });
1702
+ if (previous.operator) {
1703
+ this.opts.onFilterChange({ field, value: '', operator: null, operand: null, operand2: null });
1704
+ }
1312
1705
  if (previous.sort)
1313
1706
  this.opts.onSortChange({ field, direction: null });
1314
1707
  }
@@ -1350,6 +1743,9 @@ class AgridColumnMenuController {
1350
1743
  for (const [field, filter] of Object.entries(previous)) {
1351
1744
  if (filter.text)
1352
1745
  this.opts.onFilterChange({ field, value: '' });
1746
+ if (filter.operator) {
1747
+ this.opts.onFilterChange({ field, value: '', operator: null, operand: null, operand2: null });
1748
+ }
1353
1749
  if (filter.sort)
1354
1750
  this.opts.onSortChange({ field, direction: null });
1355
1751
  }
@@ -1407,6 +1803,15 @@ class AgridColumnMenuController {
1407
1803
  toggleColumnVisibility(field) {
1408
1804
  this.opts.control()?.toggleColumnVisibility(field);
1409
1805
  }
1806
+ /** Sets visibility for several fields from a sidebar header-group toggle. */
1807
+ setColumnsVisibility(fields, visible) {
1808
+ const control = this.opts.control();
1809
+ if (!control)
1810
+ return;
1811
+ for (const field of fields) {
1812
+ control.setColumnVisibility(field, visible);
1813
+ }
1814
+ }
1410
1815
  getColDef(field) {
1411
1816
  return this.opts.colDefs().find(col => col.field === field);
1412
1817
  }
@@ -1971,6 +2376,8 @@ class AgridEditController {
1971
2376
  editingCell = signal(null, ...(ngDevMode ? [{ debugName: "editingCell" }] : /* istanbul ignore next */ []));
1972
2377
  currentDraft = signal(null, ...(ngDevMode ? [{ debugName: "currentDraft" }] : /* istanbul ignore next */ []));
1973
2378
  editSeedChar = signal('', ...(ngDevMode ? [{ debugName: "editSeedChar" }] : /* istanbul ignore next */ []));
2379
+ /** The active cell validation error, or `null` when the last commit was accepted. */
2380
+ validationError = signal(null, ...(ngDevMode ? [{ debugName: "validationError" }] : /* istanbul ignore next */ []));
1974
2381
  constructor(opts) {
1975
2382
  this.opts = opts;
1976
2383
  }
@@ -1987,6 +2394,7 @@ class AgridEditController {
1987
2394
  const col = this.opts.visibleColDefs()[colIndex];
1988
2395
  if (!this.isCellEditable(col))
1989
2396
  return;
2397
+ this.validationError.set(null);
1990
2398
  const currentValue = this.opts.dataSource().getRow(originalIndex)[col.field];
1991
2399
  this.opts.selectedRange.set(null);
1992
2400
  this.opts.selectedCell.set({ rowIndex: originalIndex, colIndex });
@@ -2007,19 +2415,30 @@ class AgridEditController {
2007
2415
  if (displayIndex >= 0)
2008
2416
  this.opts.scrollToCell(displayIndex, colIndex);
2009
2417
  }
2010
- /** Commits the staged value and records the edit in history. */
2418
+ /**
2419
+ * Commits the staged value and records the edit in history.
2420
+ * Returns `false` (keeping the cell in edit mode) when a `ColDef.validate` hook rejects the
2421
+ * value; `true` when the edit is committed or there was no change.
2422
+ */
2011
2423
  commit() {
2012
2424
  const position = this.editingCell();
2013
2425
  if (!position)
2014
- return;
2426
+ return true;
2015
2427
  const col = this.opts.visibleColDefs()[position.colIndex];
2016
2428
  if (!col) {
2017
2429
  this.cancel();
2018
- return;
2430
+ return true;
2019
2431
  }
2020
- const oldValue = this.opts.dataSource().getRow(position.rowIndex)[col.field];
2432
+ const row = this.opts.dataSource().getRow(position.rowIndex);
2433
+ const oldValue = row[col.field];
2021
2434
  const newValue = this.currentDraft();
2022
2435
  if (oldValue !== newValue) {
2436
+ const message = col.validate?.(newValue, row) ?? null;
2437
+ if (message) {
2438
+ this.validationError.set({ ...position, field: col.field, message });
2439
+ this.opts.onValidationFailed({ rowIndex: position.rowIndex, field: col.field, value: newValue, message });
2440
+ return false;
2441
+ }
2023
2442
  this.opts.dataSource().patchRow(position.rowIndex, { [col.field]: newValue });
2024
2443
  this.opts.control()?.pushEdit({
2025
2444
  rowIndex: position.rowIndex,
@@ -2029,11 +2448,43 @@ class AgridEditController {
2029
2448
  });
2030
2449
  this.opts.onCellEdit({ position, field: col.field, oldValue, newValue });
2031
2450
  }
2451
+ this.validationError.set(null);
2032
2452
  this.clearEditState();
2033
2453
  this.opts.focusGrid();
2454
+ return true;
2455
+ }
2456
+ /**
2457
+ * Commit a value directly to a cell without entering edit mode (e.g. a boolean checkbox
2458
+ * toggle). Records the change in history and emits the edit just like {@link commit}.
2459
+ */
2460
+ setCellValue(rowIndex, colIndex, newValue) {
2461
+ const col = this.opts.visibleColDefs()[colIndex];
2462
+ if (!this.isCellEditable(col))
2463
+ return false;
2464
+ const row = this.opts.dataSource().getRow(rowIndex);
2465
+ const oldValue = row[col.field];
2466
+ if (oldValue === newValue)
2467
+ return true;
2468
+ const message = col.validate?.(newValue, row) ?? null;
2469
+ if (message) {
2470
+ this.validationError.set({ rowIndex, colIndex, field: col.field, message });
2471
+ this.opts.onValidationFailed({ rowIndex, field: col.field, value: newValue, message });
2472
+ return false;
2473
+ }
2474
+ this.opts.dataSource().patchRow(rowIndex, { [col.field]: newValue });
2475
+ this.opts.control()?.pushEdit({ rowIndex, field: col.field, oldValue, newValue });
2476
+ this.opts.onCellEdit({
2477
+ position: { rowIndex, colIndex },
2478
+ field: col.field,
2479
+ oldValue,
2480
+ newValue,
2481
+ });
2482
+ this.validationError.set(null);
2483
+ return true;
2034
2484
  }
2035
2485
  /** Discards the active edit without changing row data. */
2036
2486
  cancel() {
2487
+ this.validationError.set(null);
2037
2488
  this.clearEditState();
2038
2489
  }
2039
2490
  /** Applies the previous edit-history value to the data source. */
@@ -2264,6 +2715,59 @@ class AgridGroupController {
2264
2715
  }
2265
2716
  }
2266
2717
 
2718
+ /**
2719
+ * Owns the expand/collapse state of a hierarchical (tree) grid.
2720
+ *
2721
+ * Expansion is keyed by **node id** (from {@link AgridTreeConfig.getId}), independent of any
2722
+ * column or grouping field, so it works at arbitrary depth. Ids that no longer exist in the
2723
+ * data simply never match and are harmless, so the state does not need to be reset when the
2724
+ * underlying rows change.
2725
+ *
2726
+ * Mirrors {@link AgridGroupController} in spirit but is intentionally option-free — the grid
2727
+ * component supplies node ids to {@link expandAll} from the current data source.
2728
+ * @internal
2729
+ */
2730
+ class AgridTreeController {
2731
+ /** Ids of the nodes that are currently expanded (their children are visible). */
2732
+ expandedIds = signal(new Set(), ...(ngDevMode ? [{ debugName: "expandedIds" }] : /* istanbul ignore next */ []));
2733
+ /** Returns `true` when the node with `id` is currently expanded. */
2734
+ isExpanded(id) {
2735
+ return this.expandedIds().has(id);
2736
+ }
2737
+ /** Toggle the expanded state of a single node. */
2738
+ toggle(id) {
2739
+ this.expandedIds.update(ids => {
2740
+ const next = new Set(ids);
2741
+ if (next.has(id))
2742
+ next.delete(id);
2743
+ else
2744
+ next.add(id);
2745
+ return next;
2746
+ });
2747
+ }
2748
+ /** Explicitly expand or collapse a single node. */
2749
+ setExpanded(id, expanded) {
2750
+ this.expandedIds.update(ids => {
2751
+ if (expanded === ids.has(id))
2752
+ return ids;
2753
+ const next = new Set(ids);
2754
+ if (expanded)
2755
+ next.add(id);
2756
+ else
2757
+ next.delete(id);
2758
+ return next;
2759
+ });
2760
+ }
2761
+ /** Expand every node in `ids` (typically all expandable nodes in the data source). */
2762
+ expandAll(ids) {
2763
+ this.expandedIds.set(new Set(ids));
2764
+ }
2765
+ /** Collapse every node. */
2766
+ collapseAll() {
2767
+ this.expandedIds.set(new Set());
2768
+ }
2769
+ }
2770
+
2267
2771
  /**
2268
2772
  * Owns grid keyboard navigation, cell activation, add-row creation, and visibility scrolling.
2269
2773
  * @internal
@@ -2333,13 +2837,13 @@ class AgridNavigationController {
2333
2837
  switch (event.key) {
2334
2838
  case 'Tab':
2335
2839
  event.preventDefault();
2336
- this.opts.commitEdit();
2337
- this.moveSelection(0, event.shiftKey ? -1 : 1);
2840
+ if (this.opts.commitEdit())
2841
+ this.moveSelection(0, event.shiftKey ? -1 : 1);
2338
2842
  break;
2339
2843
  case 'Enter':
2340
2844
  event.preventDefault();
2341
- this.opts.commitEdit();
2342
- this.moveSelection(1, 0);
2845
+ if (this.opts.commitEdit())
2846
+ this.moveSelection(1, 0);
2343
2847
  break;
2344
2848
  case 'Escape':
2345
2849
  event.preventDefault();
@@ -2601,6 +3105,7 @@ class AgridPresentationService {
2601
3105
  class AgridControl {
2602
3106
  _columnWidths = signal({}, ...(ngDevMode ? [{ debugName: "_columnWidths" }] : /* istanbul ignore next */ []));
2603
3107
  _filters = signal({}, ...(ngDevMode ? [{ debugName: "_filters" }] : /* istanbul ignore next */ []));
3108
+ _quickFilter = signal('', ...(ngDevMode ? [{ debugName: "_quickFilter" }] : /* istanbul ignore next */ []));
2604
3109
  _allowRowReorder = signal(false, ...(ngDevMode ? [{ debugName: "_allowRowReorder" }] : /* istanbul ignore next */ []));
2605
3110
  _groupByField = signal(null, ...(ngDevMode ? [{ debugName: "_groupByField" }] : /* istanbul ignore next */ []));
2606
3111
  _hiddenColumns = signal(new Set(), ...(ngDevMode ? [{ debugName: "_hiddenColumns" }] : /* istanbul ignore next */ []));
@@ -2618,6 +3123,8 @@ class AgridControl {
2618
3123
  this._columnWidths.set({ ...state.columnWidths });
2619
3124
  if (state?.filters)
2620
3125
  this._filters.set({ ...state.filters });
3126
+ if (state?.quickFilter)
3127
+ this._quickFilter.set(state.quickFilter);
2621
3128
  if (state?.allowRowReorder)
2622
3129
  this._allowRowReorder.set(state.allowRowReorder);
2623
3130
  if (state?.groupByField !== undefined)
@@ -2662,6 +3169,16 @@ class AgridControl {
2662
3169
  setGroupBy(field) {
2663
3170
  this._groupByField.set(field);
2664
3171
  }
3172
+ /**
3173
+ * Global quick-filter text. When non-empty (and not in server-side filtering mode),
3174
+ * the grid keeps only rows where at least one visible column's display value contains
3175
+ * this text (case-insensitive).
3176
+ */
3177
+ quickFilter = this._quickFilter.asReadonly();
3178
+ /** Set the global quick-filter text. Pass an empty string to clear it. */
3179
+ setQuickFilter(text) {
3180
+ this._quickFilter.set(text);
3181
+ }
2665
3182
  /**
2666
3183
  * Reactive set of field names that are currently hidden.
2667
3184
  * An empty set means all columns are visible.
@@ -2934,6 +3451,17 @@ class AgridControl {
2934
3451
  [field]: { ...this.getFilter(field), selectedValues: values },
2935
3452
  }));
2936
3453
  }
3454
+ /**
3455
+ * Set the typed range filter for a `number` / `date` column.
3456
+ * Pass `operator: null` (or an empty `operand`) to clear it. `operand2` is only used
3457
+ * by the `'between'` operator.
3458
+ */
3459
+ setRangeFilter(field, operator, operand, operand2 = null) {
3460
+ this._filters.update(f => ({
3461
+ ...f,
3462
+ [field]: { ...this.getFilter(field), operator, operand, operand2 },
3463
+ }));
3464
+ }
2937
3465
  /** Ordered list of sorted field names, from highest to lowest priority. */
2938
3466
  sortOrder = this._sortOrder.asReadonly();
2939
3467
  /** Return the 1-based sort priority of a field, or `0` if it is not sorted. */
@@ -2981,10 +3509,11 @@ class AgridControl {
2981
3509
  });
2982
3510
  this._sortOrder.update(o => o.filter(f => f !== field));
2983
3511
  }
2984
- /** Remove all active filters and sorts for every column. */
3512
+ /** Remove all active filters and sorts for every column, including the quick filter. */
2985
3513
  clearAllFilters() {
2986
3514
  this._filters.set({});
2987
3515
  this._sortOrder.set([]);
3516
+ this._quickFilter.set('');
2988
3517
  }
2989
3518
  /**
2990
3519
  * Return `true` when the given field has any active filter or sort.
@@ -2992,11 +3521,13 @@ class AgridControl {
2992
3521
  */
2993
3522
  hasActiveFilter(field) {
2994
3523
  const f = this.getFilter(field);
2995
- return !!(f.text || f.selectedValues !== null || f.sort);
3524
+ const hasRange = !!f.operator && f.operand != null && f.operand !== '';
3525
+ return !!(f.text || f.selectedValues !== null || f.sort || hasRange);
2996
3526
  }
2997
- /** Return `true` when ANY column has an active filter or sort. */
3527
+ /** Return `true` when the quick filter or ANY column has an active filter or sort. */
2998
3528
  hasAnyActiveFilter() {
2999
- return Object.values(this._filters()).some(f => f.text || f.selectedValues !== null || f.sort);
3529
+ return !!this._quickFilter() || Object.values(this._filters()).some(f => f.text || f.selectedValues !== null || f.sort
3530
+ || (!!f.operator && f.operand != null && f.operand !== ''));
3000
3531
  }
3001
3532
  // ── Serialization ──────────────────────────────────────────────────────────
3002
3533
  /** Serialize current state to a plain object suitable for JSON storage. */
@@ -3004,6 +3535,7 @@ class AgridControl {
3004
3535
  return {
3005
3536
  columnWidths: { ...this._columnWidths() },
3006
3537
  filters: { ...this._filters() },
3538
+ quickFilter: this._quickFilter() || undefined,
3007
3539
  allowRowReorder: this._allowRowReorder(),
3008
3540
  groupByField: this._groupByField() ?? undefined,
3009
3541
  hiddenColumns: [...this._hiddenColumns()],
@@ -3180,6 +3712,8 @@ class AgridProvider {
3180
3712
  columns;
3181
3713
  /** Header-group labels referenced by column definitions. */
3182
3714
  headerGroups;
3715
+ /** Tree configuration, or `null` when the grid is not in tree mode. */
3716
+ treeConfig;
3183
3717
  /** Shared grid options such as locale. */
3184
3718
  options;
3185
3719
  _localizations = new Map();
@@ -3219,6 +3753,8 @@ class AgridProvider {
3219
3753
  serverSideFiltering;
3220
3754
  /** Delay before server-side filter events are emitted. */
3221
3755
  filterDebounceMs;
3756
+ /** Whether the global quick-filter box is shown above the grid. */
3757
+ enableQuickFilter;
3222
3758
  /** Enabled sorting mode. */
3223
3759
  sortOption;
3224
3760
  /** Toggle auto-add-rows without recreating the provider. @default signal(false) */
@@ -3252,6 +3788,7 @@ class AgridProvider {
3252
3788
  this.control = config.control ?? new AgridControl({ allowRowReorder: true });
3253
3789
  this.columns = signal(config.columns ?? [], ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
3254
3790
  this.headerGroups = config.headerGroups ?? [];
3791
+ this.treeConfig = config.treeConfig ?? null;
3255
3792
  this.rowHeight = config.rowHeight ?? 32;
3256
3793
  this.minHeight = config.minHeight;
3257
3794
  this.maxHeight = config.maxHeight;
@@ -3263,6 +3800,7 @@ class AgridProvider {
3263
3800
  this.autoOpenDetail = config.autoOpenDetail ?? false;
3264
3801
  this.serverSideFiltering = config.serverSideFiltering ?? false;
3265
3802
  this.filterDebounceMs = Math.max(0, config.filterDebounceMs ?? 300);
3803
+ this.enableQuickFilter = config.enableQuickFilter ?? false;
3266
3804
  this.sortOption = config.sortOption ?? 'multi';
3267
3805
  this.rowSelection = config.rowSelection ?? 'none';
3268
3806
  this.groupDescription = config.groupDescription ?? null;
@@ -3301,6 +3839,10 @@ class AgridProjectionModel {
3301
3839
  return indices;
3302
3840
  const filters = control.filters();
3303
3841
  indices = applyTextAndValueFilters(rows, indices, filters, colMap, this.opts.locale());
3842
+ const quick = control.quickFilter();
3843
+ if (quick) {
3844
+ indices = applyQuickFilter(rows, indices, quick, this.opts.visibleColDefs(), this.opts.locale());
3845
+ }
3304
3846
  if (control.groupByField())
3305
3847
  return indices;
3306
3848
  const sortEntries = this.sortEntries(filters);
@@ -3310,6 +3852,18 @@ class AgridProjectionModel {
3310
3852
  }, ...(ngDevMode ? [{ debugName: "filteredSortedIndices" }] : /* istanbul ignore next */ []));
3311
3853
  /** Total filtered row count, unaffected by client-side pagination. */
3312
3854
  filteredRowCount = computed(() => this.filteredSortedIndices().length, ...(ngDevMode ? [{ debugName: "filteredRowCount" }] : /* istanbul ignore next */ []));
3855
+ /** All source indices in active sort order, ignoring filters. Used for tree ancestor ordering. */
3856
+ allSortedIndices = computed(() => {
3857
+ const rows = this.opts.dataSource().rows();
3858
+ const control = this.opts.control();
3859
+ const indices = rows.map((_, index) => index);
3860
+ if (!control || this.opts.serverSideFiltering())
3861
+ return indices;
3862
+ const sortEntries = this.sortEntries(control.filters());
3863
+ return sortEntries.length
3864
+ ? applySortToIndices(rows, indices, sortEntries, this.columnMap(), this.opts.locale())
3865
+ : indices;
3866
+ }, ...(ngDevMode ? [{ debugName: "allSortedIndices" }] : /* istanbul ignore next */ []));
3313
3867
  /** Total page count using server total when server-side pagination is active. */
3314
3868
  totalPages = computed(() => {
3315
3869
  const control = this.opts.control();
@@ -3324,6 +3878,8 @@ class AgridProjectionModel {
3324
3878
  /** Whether client or server pagination controls should be rendered. */
3325
3879
  showPagination = computed(() => {
3326
3880
  const control = this.opts.control();
3881
+ if (this.opts.treeConfig())
3882
+ return false;
3327
3883
  return (control?.pageSize() ?? 0) > 0 && !control?.groupByField();
3328
3884
  }, ...(ngDevMode ? [{ debugName: "showPagination" }] : /* istanbul ignore next */ []));
3329
3885
  /** Whether at least one visible column has an aggregate footer. */
@@ -3332,48 +3888,48 @@ class AgridProjectionModel {
3332
3888
  return this.opts.visibleColDefs().some(col => col.aggregate || aggregates[col.field]);
3333
3889
  }, ...(ngDevMode ? [{ debugName: "showFooter" }] : /* istanbul ignore next */ []));
3334
3890
  /** Aggregate values computed over all filtered rows, before pagination. */
3335
- footerValues = computed(() => {
3336
- const rows = this.opts.dataSource().rows();
3337
- const indices = this.filteredSortedIndices();
3338
- const controlAggregates = this.opts.control()?.aggregates() ?? {};
3339
- const result = {};
3340
- for (const col of this.opts.visibleColDefs()) {
3341
- const aggregate = controlAggregates[col.field] ?? col.aggregate;
3342
- if (!aggregate)
3343
- continue;
3344
- const values = indices.map(index => rows[index][col.field]);
3345
- if (typeof aggregate === 'function') {
3346
- result[col.field] = aggregate(values);
3347
- continue;
3348
- }
3349
- const numbers = values.map(Number).filter(value => !Number.isNaN(value));
3350
- switch (aggregate) {
3351
- case 'sum':
3352
- result[col.field] = numbers.reduce((sum, value) => sum + value, 0);
3353
- break;
3354
- case 'avg':
3355
- result[col.field] = numbers.length
3356
- ? numbers.reduce((sum, value) => sum + value, 0) / numbers.length
3357
- : null;
3358
- break;
3359
- case 'min':
3360
- result[col.field] = numbers.length ? Math.min(...numbers) : null;
3361
- break;
3362
- case 'max':
3363
- result[col.field] = numbers.length ? Math.max(...numbers) : null;
3364
- break;
3365
- case 'count':
3366
- result[col.field] = values.filter(value => value != null && value !== '').length;
3367
- break;
3368
- }
3369
- }
3370
- return result;
3371
- }, ...(ngDevMode ? [{ debugName: "footerValues" }] : /* istanbul ignore next */ []));
3891
+ footerValues = computed(() => computeAggregates(this.opts.dataSource().rows(), this.filteredSortedIndices(), this.opts.visibleColDefs(), this.opts.control()?.aggregates() ?? {}), ...(ngDevMode ? [{ debugName: "footerValues" }] : /* istanbul ignore next */ []));
3372
3892
  /** Filtered, sorted, paginated, and optionally grouped virtual-scroll items. */
3373
3893
  filteredItems = computed(() => {
3374
3894
  const rows = this.opts.dataSource().rows();
3375
3895
  const control = this.opts.control();
3376
3896
  let indices = this.filteredSortedIndices();
3897
+ // Tree mode takes precedence over grouping/pagination: the already filtered-and-sorted
3898
+ // indices are flattened into a hierarchy honoring the expanded-id set.
3899
+ const treeConfig = this.opts.treeConfig();
3900
+ if (treeConfig) {
3901
+ const expandedIds = this.opts.expandedTreeIds();
3902
+ // When a text/value filter is active, keep the ancestors of every match visible (and
3903
+ // force their path open) so deep matches don't vanish under filtered-out parents.
3904
+ const filters = control?.filters() ?? {};
3905
+ const filterActive = !this.opts.serverSideFiltering()
3906
+ && Object.values(filters).some(f => f.text || f.selectedValues !== null);
3907
+ if (filterActive && treeConfig.keepAncestorsOnFilter !== false) {
3908
+ const { getId, getParentId } = treeConfig;
3909
+ const idToIndex = new Map();
3910
+ rows.forEach((row, index) => idToIndex.set(getId(row), index));
3911
+ const visible = new Set(indices);
3912
+ const forced = new Set();
3913
+ for (const matched of indices) {
3914
+ let parentId = getParentId(rows[matched]);
3915
+ while (parentId != null && idToIndex.has(parentId)) {
3916
+ const parentIndex = idToIndex.get(parentId);
3917
+ forced.add(parentId);
3918
+ if (visible.has(parentIndex))
3919
+ break; // chain already added by an earlier match
3920
+ visible.add(parentIndex);
3921
+ parentId = getParentId(rows[parentIndex]);
3922
+ }
3923
+ }
3924
+ const ordered = this.allSortedIndices().filter(index => visible.has(index));
3925
+ const items = buildTreeItems(rows, ordered, treeConfig, expandedIds, forced);
3926
+ this.appendAddRow(items);
3927
+ return items;
3928
+ }
3929
+ const items = buildTreeItems(rows, indices, treeConfig, expandedIds);
3930
+ this.appendAddRow(items);
3931
+ return items;
3932
+ }
3377
3933
  if (control) {
3378
3934
  const groupField = control.groupByField();
3379
3935
  const pageSize = control.pageSize();
@@ -3391,7 +3947,7 @@ class AgridProjectionModel {
3391
3947
  const sortEntries = this.opts.serverSideFiltering()
3392
3948
  ? []
3393
3949
  : this.sortEntries(filters);
3394
- const items = buildGroupedItems(rows, indices, groupField, this.columnMap(), sortEntries, expandedLabels, this.opts.locale());
3950
+ const items = buildGroupedItems(rows, indices, groupField, this.columnMap(), sortEntries, expandedLabels, this.opts.locale(), this.opts.visibleColDefs(), control.aggregates());
3395
3951
  this.appendAddRow(items);
3396
3952
  return items;
3397
3953
  }
@@ -3676,6 +4232,17 @@ class AgridRangeController {
3676
4232
  }
3677
4233
  }
3678
4234
 
4235
+ /**
4236
+ * Whether a pointer event originated inside an editable form control. Used to avoid calling
4237
+ * `preventDefault()` on a row pointerdown, which would otherwise cancel native text selection
4238
+ * (and caret placement) inside an active cell editor.
4239
+ */
4240
+ function isEditableEventTarget(target) {
4241
+ return target instanceof HTMLInputElement
4242
+ || target instanceof HTMLTextAreaElement
4243
+ || target instanceof HTMLSelectElement
4244
+ || (target instanceof HTMLElement && target.isContentEditable);
4245
+ }
3679
4246
  /**
3680
4247
  * Owns row selection, row/cell context menus, and row-level operations.
3681
4248
  * @internal
@@ -3739,7 +4306,7 @@ class AgridRowController {
3739
4306
  this.emitSelection();
3740
4307
  }
3741
4308
  else {
3742
- if (!(event.target instanceof HTMLSelectElement))
4309
+ if (!isEditableEventTarget(event.target))
3743
4310
  event.preventDefault();
3744
4311
  this.selectedIndices.set(new Set([originalIndex]));
3745
4312
  this.selectionPivot = originalIndex;
@@ -3868,9 +4435,21 @@ class AgridSidebarController {
3868
4435
  return index === null ? null : this.opts.dataSource().rows()[index] ?? null;
3869
4436
  }, ...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
3870
4437
  hiddenColumns = computed(() => this.opts.control()?.hiddenColumns() ?? new Set(), ...(ngDevMode ? [{ debugName: "hiddenColumns" }] : /* istanbul ignore next */ []));
4438
+ /** Per-field validation messages for rejected sidebar edits (`field → message`). */
4439
+ validationErrors = signal({}, ...(ngDevMode ? [{ debugName: "validationErrors" }] : /* istanbul ignore next */ []));
3871
4440
  constructor(opts) {
3872
4441
  this.opts = opts;
3873
4442
  }
4443
+ /** Remove the stored validation message for one field, if any. */
4444
+ clearFieldError(field) {
4445
+ if (!(field in this.validationErrors()))
4446
+ return;
4447
+ this.validationErrors.update(errors => {
4448
+ const next = { ...errors };
4449
+ delete next[field];
4450
+ return next;
4451
+ });
4452
+ }
3874
4453
  /** Opens the detail tab when automatic detail display is enabled. */
3875
4454
  syncAutoOpen() {
3876
4455
  if (this.opts.autoOpenDetail() && this.opts.selectedRowIndex() !== null) {
@@ -3928,9 +4507,17 @@ class AgridSidebarController {
3928
4507
  ? stringValue
3929
4508
  : typeof option === 'string' ? option : option.value;
3930
4509
  }
3931
- const oldValue = this.opts.dataSource().getRow(index)[field];
4510
+ const row = this.opts.dataSource().getRow(index);
4511
+ const oldValue = row[field];
3932
4512
  if (oldValue === newValue)
3933
4513
  return;
4514
+ const message = col.validate?.(newValue, row) ?? null;
4515
+ if (message) {
4516
+ this.validationErrors.update(errors => ({ ...errors, [field]: message }));
4517
+ this.opts.onValidationFailed({ rowIndex: index, field, value: newValue, message });
4518
+ return;
4519
+ }
4520
+ this.clearFieldError(field);
3934
4521
  this.opts.dataSource().patchRow(index, { [field]: newValue });
3935
4522
  const colIndex = this.opts.visibleColDefs().findIndex(column => column.field === field);
3936
4523
  this.opts.control()?.pushEdit({ rowIndex: index, field, oldValue, newValue });
@@ -3952,17 +4539,60 @@ class AgridSidebarComponent {
3952
4539
  open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
3953
4540
  activeTab = input('columns', ...(ngDevMode ? [{ debugName: "activeTab" }] : /* istanbul ignore next */ []));
3954
4541
  columns = input([], ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
4542
+ headerGroups = input([], ...(ngDevMode ? [{ debugName: "headerGroups" }] : /* istanbul ignore next */ []));
3955
4543
  row = input(null, ...(ngDevMode ? [{ debugName: "row" }] : /* istanbul ignore next */ []));
3956
4544
  hiddenColumns = input(new Set(), ...(ngDevMode ? [{ debugName: "hiddenColumns" }] : /* istanbul ignore next */ []));
3957
4545
  locale = input(undefined, ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
3958
4546
  localeText = input(AGRID_LOCALE_TEXT.en, ...(ngDevMode ? [{ debugName: "localeText" }] : /* istanbul ignore next */ []));
3959
4547
  readonlyGrid = input(false, ...(ngDevMode ? [{ debugName: "readonlyGrid" }] : /* istanbul ignore next */ []));
3960
4548
  useSidebarEditor = input(false, ...(ngDevMode ? [{ debugName: "useSidebarEditor" }] : /* istanbul ignore next */ []));
4549
+ /** Per-field validation messages (`field → message`) for rejected detail edits. */
4550
+ errors = input({}, ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
3961
4551
  close = output();
3962
4552
  tabChange = output();
3963
4553
  toggleColumn = output();
4554
+ toggleColumnGroup = output();
3964
4555
  detailEdit = output();
3965
4556
  save = output();
4557
+ columnEntries = computed(() => {
4558
+ const groupLabels = new Map(this.headerGroups().map(group => [group.id, group.label]));
4559
+ const entries = [];
4560
+ const groupedEntries = new Map();
4561
+ for (const col of this.columns()) {
4562
+ const groupId = col.group;
4563
+ const groupLabel = groupId ? groupLabels.get(groupId) : undefined;
4564
+ if (!groupId || !groupLabel) {
4565
+ entries.push({ kind: 'column', col });
4566
+ continue;
4567
+ }
4568
+ let entry = groupedEntries.get(groupId);
4569
+ if (!entry) {
4570
+ entry = { kind: 'group', id: groupId, label: groupLabel, columns: [] };
4571
+ groupedEntries.set(groupId, entry);
4572
+ entries.push(entry);
4573
+ }
4574
+ entry.columns.push(col);
4575
+ }
4576
+ return entries;
4577
+ }, ...(ngDevMode ? [{ debugName: "columnEntries" }] : /* istanbul ignore next */ []));
4578
+ /** Whether every column in a group is currently visible. */
4579
+ isGroupVisible(columns) {
4580
+ const hidden = this.hiddenColumns();
4581
+ return columns.every(col => !hidden.has(col.field));
4582
+ }
4583
+ /** Whether a group contains both visible and hidden columns. */
4584
+ isGroupPartiallyVisible(columns) {
4585
+ const hidden = this.hiddenColumns();
4586
+ const visibleCount = columns.filter(col => !hidden.has(col.field)).length;
4587
+ return visibleCount > 0 && visibleCount < columns.length;
4588
+ }
4589
+ /** Emits a visibility request for all columns belonging to a group. */
4590
+ onGroupToggle(columns, event) {
4591
+ this.toggleColumnGroup.emit({
4592
+ fields: columns.map(col => col.field),
4593
+ visible: event.target.checked,
4594
+ });
4595
+ }
3966
4596
  detailFields = computed(() => {
3967
4597
  const row = this.row();
3968
4598
  if (!row)
@@ -3988,12 +4618,12 @@ class AgridSidebarComponent {
3988
4618
  });
3989
4619
  }, ...(ngDevMode ? [{ debugName: "detailFields" }] : /* istanbul ignore next */ []));
3990
4620
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AgridSidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3991
- 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 }, 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", 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 (col of columns(); track col.field) {\n <label class=\"ag-sidebar-item\" (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 } @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-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 });
4621
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: AgridSidebarComponent, isStandalone: true, selector: "agrid-sidebar", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, activeTab: { classPropertyName: "activeTab", publicName: "activeTab", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, headerGroups: { classPropertyName: "headerGroups", publicName: "headerGroups", isSignal: true, isRequired: false, transformFunction: null }, row: { classPropertyName: "row", publicName: "row", isSignal: true, isRequired: false, transformFunction: null }, hiddenColumns: { classPropertyName: "hiddenColumns", publicName: "hiddenColumns", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, localeText: { classPropertyName: "localeText", publicName: "localeText", isSignal: true, isRequired: false, transformFunction: null }, readonlyGrid: { classPropertyName: "readonlyGrid", publicName: "readonlyGrid", isSignal: true, isRequired: false, transformFunction: null }, useSidebarEditor: { classPropertyName: "useSidebarEditor", publicName: "useSidebarEditor", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", tabChange: "tabChange", toggleColumn: "toggleColumn", toggleColumnGroup: "toggleColumnGroup", detailEdit: "detailEdit", save: "save" }, ngImport: i0, template: "@if (open()) {\n <div class=\"ag-sidebar\" role=\"region\"\n [attr.aria-label]=\"activeTab() === 'columns' ? localeText().columns : localeText().detail\">\n <div class=\"ag-sidebar-header\">\n <span>{{ activeTab() === 'columns' ? localeText().columns : localeText().detail }}</span>\n <button class=\"ag-sidebar-close\" (click)=\"close.emit()\" [title]=\"localeText().close\"\n [attr.aria-label]=\"localeText().close\">\u2715</button>\n </div>\n\n @if (activeTab() === 'columns') {\n @for (entry of columnEntries(); track entry.kind === 'group' ? 'group-' + entry.id : 'column-' + entry.col.field) {\n @if (entry.kind === 'group') {\n <div class=\"ag-sidebar-group\">\n <label class=\"ag-sidebar-item ag-sidebar-group-label\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"isGroupVisible(entry.columns)\"\n [indeterminate]=\"isGroupPartiallyVisible(entry.columns)\"\n (change)=\"onGroupToggle(entry.columns, $event)\"\n />\n {{ entry.label }}\n </label>\n <div class=\"ag-sidebar-group-children\">\n @for (col of entry.columns; track col.field) {\n <label class=\"ag-sidebar-item ag-sidebar-group-child\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"!hiddenColumns().has(col.field)\"\n (change)=\"toggleColumn.emit(col.field)\"\n />\n {{ col.header }}\n </label>\n }\n </div>\n </div>\n } @else {\n <label class=\"ag-sidebar-item\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"!hiddenColumns().has(entry.col.field)\"\n (change)=\"toggleColumn.emit(entry.col.field)\"\n />\n {{ entry.col.header }}\n </label>\n }\n }\n } @else {\n @if (detailFields(); as fields) {\n @for (field of fields; track field.col.field) {\n <div class=\"ag-detail-field\">\n <span class=\"ag-detail-label\">\n {{ field.label }}\n @if (field.hidden) {\n <span class=\"ag-detail-hidden\">{{ localeText().hiddenColumn }}</span>\n }\n </span>\n @if ((field.editable || useSidebarEditor()) && field.col.values?.length) {\n <select\n class=\"ag-detail-input\"\n [class.ag-detail-input--invalid]=\"!!errors()[field.col.field]\"\n (change)=\"detailEdit.emit({\n field: field.col.field,\n col: field.col,\n value: $any($event.target).value\n })\"\n >\n @for (opt of field.col.values; track $index) {\n @if ($any(opt).value !== undefined) {\n <option [value]=\"$any(opt).value\" [selected]=\"$any(opt).value === field.rawValue\">\n {{ $any(opt).label }}\n </option>\n } @else {\n <option [value]=\"opt\" [selected]=\"opt === field.rawValue\">{{ opt }}</option>\n }\n }\n </select>\n } @else if (field.editable || useSidebarEditor()) {\n <input\n class=\"ag-detail-input\"\n [class.ag-detail-input--invalid]=\"!!errors()[field.col.field]\"\n [type]=\"field.col.type === 'number' ? 'number' : field.col.type === 'date' ? 'date' : 'text'\"\n [value]=\"field.inputValue\"\n (change)=\"detailEdit.emit({\n field: field.col.field,\n col: field.col,\n value: $any($event.target).value\n })\"\n />\n } @else {\n <span class=\"ag-detail-value\">{{ field.value }}</span>\n }\n @if (errors()[field.col.field]; as msg) {\n <span class=\"ag-detail-error\" role=\"alert\">{{ msg }}</span>\n }\n </div>\n }\n @if (useSidebarEditor()) {\n <div class=\"ag-detail-field\">\n <button class=\"ag-button\" (click)=\"save.emit(fields)\">{{ localeText().save }}</button>\n </div>\n }\n } @else {\n <div class=\"ag-detail-empty\">{{ localeText().noRows }}</div>\n }\n }\n </div>\n}\n\n<div class=\"ag-sidebar-strip\">\n <button\n class=\"ag-sidebar-strip-btn\"\n [class.active]=\"open() && activeTab() === 'columns'\"\n [attr.aria-pressed]=\"open() && activeTab() === 'columns'\"\n (click)=\"tabChange.emit('columns')\"\n >{{ localeText().columns }}</button>\n <button\n class=\"ag-sidebar-strip-btn\"\n [class.active]=\"open() && activeTab() === 'detail'\"\n [attr.aria-pressed]=\"open() && activeTab() === 'detail'\"\n (click)=\"tabChange.emit('detail')\"\n >{{ localeText().detail }}</button>\n</div>\n", styles: [":host{display:flex;flex-shrink:0;min-height:0}.ag-sidebar{width:200px;flex-shrink:0;border-left:1px solid var(--agrid-color-border);background:var(--agrid-color-bg);display:flex;flex-direction:column;overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-sidebar::-webkit-scrollbar{width:8px;height:8px}.ag-sidebar::-webkit-scrollbar-track{background:transparent}.ag-sidebar::-webkit-scrollbar-thumb{background:#0000002e;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-sidebar::-webkit-scrollbar-thumb:hover{background:#00000052}.ag-sidebar-strip{width:24px;flex-shrink:0;border-left:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted);display:flex;flex-direction:column}.ag-sidebar-strip-btn{background:none;border:none;border-bottom:1px solid var(--agrid-color-border);cursor:pointer;color:var(--agrid-color-text-muted);-webkit-user-select:none;user-select:none;writing-mode:vertical-rl;transform:rotate(180deg);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;padding:10px 5px;text-align:center}.ag-sidebar-strip-btn:hover{background:var(--agrid-color-bg);color:var(--agrid-color-text)}.ag-sidebar-strip-btn.active{color:var(--agrid-color-text);background:var(--agrid-color-bg);border-left:2px solid var(--agrid-color-accent, #4f8ef7)}.ag-sidebar-header{display:flex;align-items:center;justify-content:space-between;padding:0 8px 0 12px;height:32px;flex-shrink:0;border-bottom:1px solid var(--agrid-color-border);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--agrid-color-text-muted);background:var(--agrid-color-bg-muted)}.ag-sidebar-close{background:none;border:none;cursor:pointer;color:var(--agrid-color-text-muted);font-size:11px;padding:2px 4px;border-radius:3px;line-height:1}.ag-sidebar-close:hover,.ag-sidebar-item:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text)}.ag-sidebar-item{display:flex;align-items:center;gap:8px;padding:5px 12px;font-size:13px;cursor:pointer;-webkit-user-select:none;user-select:none}.ag-sidebar-item input[type=checkbox]{cursor:pointer;margin:0;flex-shrink:0}.ag-sidebar-group-label{font-weight:600}.ag-sidebar-group-children{position:relative}.ag-sidebar-group-children:before{content:\"\";position:absolute;top:0;bottom:8px;left:19px;border-left:1px solid var(--agrid-color-border)}.ag-sidebar-group-child{position:relative;padding-left:30px}.ag-sidebar-group-child:before{content:\"\";position:absolute;left:19px;width:7px;border-top:1px solid var(--agrid-color-border)}.ag-detail-field{display:flex;flex-direction:column;gap:2px;padding:7px 12px;border-bottom:1px solid var(--agrid-color-border);font-size:13px}.ag-detail-label{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--agrid-color-text-muted)}.ag-detail-hidden{font-weight:400;text-transform:none;letter-spacing:0;opacity:.6}.ag-detail-value{color:var(--agrid-color-text);word-break:break-word}.ag-detail-empty{padding:16px 12px;font-size:13px;color:var(--agrid-color-text-muted);text-align:center}.ag-detail-input{width:100%;box-sizing:border-box;font-size:13px;font-family:inherit;color:var(--agrid-color-text);background:var(--agrid-color-bg);border:1px solid var(--agrid-color-border);border-radius:3px;padding:3px 6px;outline:none}.ag-detail-input:focus{border-color:var(--agrid-color-accent, #4f8ef7);box-shadow:0 0 0 2px color-mix(in srgb,var(--agrid-color-accent, #4f8ef7) 20%,transparent)}.ag-button{display:flex;align-items:center;justify-content:center;height:24px;border:1px solid var(--agrid-color-border);border-radius:3px;background:var(--agrid-color-bg-subtle);color:var(--agrid-color-text);cursor:pointer;font-size:13px;padding:0;line-height:1}.ag-detail-input--invalid{border-color:var(--agrid-color-danger);outline:1px solid var(--agrid-color-danger)}.ag-detail-error{display:block;margin-top:3px;font-size:12px;line-height:1.4;color:var(--agrid-color-danger)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3992
4622
  }
3993
4623
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AgridSidebarComponent, decorators: [{
3994
4624
  type: Component,
3995
- 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 (col of columns(); track col.field) {\n <label class=\"ag-sidebar-item\" (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 } @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-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"] }]
3996
- }], 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 }] }], 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"] }], detailEdit: [{ type: i0.Output, args: ["detailEdit"] }], save: [{ type: i0.Output, args: ["save"] }] } });
4625
+ args: [{ selector: 'agrid-sidebar', changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (open()) {\n <div class=\"ag-sidebar\" role=\"region\"\n [attr.aria-label]=\"activeTab() === 'columns' ? localeText().columns : localeText().detail\">\n <div class=\"ag-sidebar-header\">\n <span>{{ activeTab() === 'columns' ? localeText().columns : localeText().detail }}</span>\n <button class=\"ag-sidebar-close\" (click)=\"close.emit()\" [title]=\"localeText().close\"\n [attr.aria-label]=\"localeText().close\">\u2715</button>\n </div>\n\n @if (activeTab() === 'columns') {\n @for (entry of columnEntries(); track entry.kind === 'group' ? 'group-' + entry.id : 'column-' + entry.col.field) {\n @if (entry.kind === 'group') {\n <div class=\"ag-sidebar-group\">\n <label class=\"ag-sidebar-item ag-sidebar-group-label\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"isGroupVisible(entry.columns)\"\n [indeterminate]=\"isGroupPartiallyVisible(entry.columns)\"\n (change)=\"onGroupToggle(entry.columns, $event)\"\n />\n {{ entry.label }}\n </label>\n <div class=\"ag-sidebar-group-children\">\n @for (col of entry.columns; track col.field) {\n <label class=\"ag-sidebar-item ag-sidebar-group-child\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"!hiddenColumns().has(col.field)\"\n (change)=\"toggleColumn.emit(col.field)\"\n />\n {{ col.header }}\n </label>\n }\n </div>\n </div>\n } @else {\n <label class=\"ag-sidebar-item\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"!hiddenColumns().has(entry.col.field)\"\n (change)=\"toggleColumn.emit(entry.col.field)\"\n />\n {{ entry.col.header }}\n </label>\n }\n }\n } @else {\n @if (detailFields(); as fields) {\n @for (field of fields; track field.col.field) {\n <div class=\"ag-detail-field\">\n <span class=\"ag-detail-label\">\n {{ field.label }}\n @if (field.hidden) {\n <span class=\"ag-detail-hidden\">{{ localeText().hiddenColumn }}</span>\n }\n </span>\n @if ((field.editable || useSidebarEditor()) && field.col.values?.length) {\n <select\n class=\"ag-detail-input\"\n [class.ag-detail-input--invalid]=\"!!errors()[field.col.field]\"\n (change)=\"detailEdit.emit({\n field: field.col.field,\n col: field.col,\n value: $any($event.target).value\n })\"\n >\n @for (opt of field.col.values; track $index) {\n @if ($any(opt).value !== undefined) {\n <option [value]=\"$any(opt).value\" [selected]=\"$any(opt).value === field.rawValue\">\n {{ $any(opt).label }}\n </option>\n } @else {\n <option [value]=\"opt\" [selected]=\"opt === field.rawValue\">{{ opt }}</option>\n }\n }\n </select>\n } @else if (field.editable || useSidebarEditor()) {\n <input\n class=\"ag-detail-input\"\n [class.ag-detail-input--invalid]=\"!!errors()[field.col.field]\"\n [type]=\"field.col.type === 'number' ? 'number' : field.col.type === 'date' ? 'date' : 'text'\"\n [value]=\"field.inputValue\"\n (change)=\"detailEdit.emit({\n field: field.col.field,\n col: field.col,\n value: $any($event.target).value\n })\"\n />\n } @else {\n <span class=\"ag-detail-value\">{{ field.value }}</span>\n }\n @if (errors()[field.col.field]; as msg) {\n <span class=\"ag-detail-error\" role=\"alert\">{{ msg }}</span>\n }\n </div>\n }\n @if (useSidebarEditor()) {\n <div class=\"ag-detail-field\">\n <button class=\"ag-button\" (click)=\"save.emit(fields)\">{{ localeText().save }}</button>\n </div>\n }\n } @else {\n <div class=\"ag-detail-empty\">{{ localeText().noRows }}</div>\n }\n }\n </div>\n}\n\n<div class=\"ag-sidebar-strip\">\n <button\n class=\"ag-sidebar-strip-btn\"\n [class.active]=\"open() && activeTab() === 'columns'\"\n [attr.aria-pressed]=\"open() && activeTab() === 'columns'\"\n (click)=\"tabChange.emit('columns')\"\n >{{ localeText().columns }}</button>\n <button\n class=\"ag-sidebar-strip-btn\"\n [class.active]=\"open() && activeTab() === 'detail'\"\n [attr.aria-pressed]=\"open() && activeTab() === 'detail'\"\n (click)=\"tabChange.emit('detail')\"\n >{{ localeText().detail }}</button>\n</div>\n", styles: [":host{display:flex;flex-shrink:0;min-height:0}.ag-sidebar{width:200px;flex-shrink:0;border-left:1px solid var(--agrid-color-border);background:var(--agrid-color-bg);display:flex;flex-direction:column;overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-sidebar::-webkit-scrollbar{width:8px;height:8px}.ag-sidebar::-webkit-scrollbar-track{background:transparent}.ag-sidebar::-webkit-scrollbar-thumb{background:#0000002e;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-sidebar::-webkit-scrollbar-thumb:hover{background:#00000052}.ag-sidebar-strip{width:24px;flex-shrink:0;border-left:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted);display:flex;flex-direction:column}.ag-sidebar-strip-btn{background:none;border:none;border-bottom:1px solid var(--agrid-color-border);cursor:pointer;color:var(--agrid-color-text-muted);-webkit-user-select:none;user-select:none;writing-mode:vertical-rl;transform:rotate(180deg);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;padding:10px 5px;text-align:center}.ag-sidebar-strip-btn:hover{background:var(--agrid-color-bg);color:var(--agrid-color-text)}.ag-sidebar-strip-btn.active{color:var(--agrid-color-text);background:var(--agrid-color-bg);border-left:2px solid var(--agrid-color-accent, #4f8ef7)}.ag-sidebar-header{display:flex;align-items:center;justify-content:space-between;padding:0 8px 0 12px;height:32px;flex-shrink:0;border-bottom:1px solid var(--agrid-color-border);font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--agrid-color-text-muted);background:var(--agrid-color-bg-muted)}.ag-sidebar-close{background:none;border:none;cursor:pointer;color:var(--agrid-color-text-muted);font-size:11px;padding:2px 4px;border-radius:3px;line-height:1}.ag-sidebar-close:hover,.ag-sidebar-item:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text)}.ag-sidebar-item{display:flex;align-items:center;gap:8px;padding:5px 12px;font-size:13px;cursor:pointer;-webkit-user-select:none;user-select:none}.ag-sidebar-item input[type=checkbox]{cursor:pointer;margin:0;flex-shrink:0}.ag-sidebar-group-label{font-weight:600}.ag-sidebar-group-children{position:relative}.ag-sidebar-group-children:before{content:\"\";position:absolute;top:0;bottom:8px;left:19px;border-left:1px solid var(--agrid-color-border)}.ag-sidebar-group-child{position:relative;padding-left:30px}.ag-sidebar-group-child:before{content:\"\";position:absolute;left:19px;width:7px;border-top:1px solid var(--agrid-color-border)}.ag-detail-field{display:flex;flex-direction:column;gap:2px;padding:7px 12px;border-bottom:1px solid var(--agrid-color-border);font-size:13px}.ag-detail-label{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.4px;color:var(--agrid-color-text-muted)}.ag-detail-hidden{font-weight:400;text-transform:none;letter-spacing:0;opacity:.6}.ag-detail-value{color:var(--agrid-color-text);word-break:break-word}.ag-detail-empty{padding:16px 12px;font-size:13px;color:var(--agrid-color-text-muted);text-align:center}.ag-detail-input{width:100%;box-sizing:border-box;font-size:13px;font-family:inherit;color:var(--agrid-color-text);background:var(--agrid-color-bg);border:1px solid var(--agrid-color-border);border-radius:3px;padding:3px 6px;outline:none}.ag-detail-input:focus{border-color:var(--agrid-color-accent, #4f8ef7);box-shadow:0 0 0 2px color-mix(in srgb,var(--agrid-color-accent, #4f8ef7) 20%,transparent)}.ag-button{display:flex;align-items:center;justify-content:center;height:24px;border:1px solid var(--agrid-color-border);border-radius:3px;background:var(--agrid-color-bg-subtle);color:var(--agrid-color-text);cursor:pointer;font-size:13px;padding:0;line-height:1}.ag-detail-input--invalid{border-color:var(--agrid-color-danger);outline:1px solid var(--agrid-color-danger)}.ag-detail-error{display:block;margin-top:3px;font-size:12px;line-height:1.4;color:var(--agrid-color-danger)}\n"] }]
4626
+ }], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], activeTab: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeTab", required: false }] }], columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: false }] }], headerGroups: [{ type: i0.Input, args: [{ isSignal: true, alias: "headerGroups", required: false }] }], row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: false }] }], hiddenColumns: [{ type: i0.Input, args: [{ isSignal: true, alias: "hiddenColumns", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], localeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "localeText", required: false }] }], readonlyGrid: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonlyGrid", required: false }] }], useSidebarEditor: [{ type: i0.Input, args: [{ isSignal: true, alias: "useSidebarEditor", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], close: [{ type: i0.Output, args: ["close"] }], tabChange: [{ type: i0.Output, args: ["tabChange"] }], toggleColumn: [{ type: i0.Output, args: ["toggleColumn"] }], toggleColumnGroup: [{ type: i0.Output, args: ["toggleColumnGroup"] }], detailEdit: [{ type: i0.Output, args: ["detailEdit"] }], save: [{ type: i0.Output, args: ["save"] }] } });
3997
4627
 
3998
4628
  /**
3999
4629
  * Excel-like data grid for Angular 21.
@@ -4030,12 +4660,15 @@ class AgridComponent {
4030
4660
  autoOpenDetail = computed(() => this.provider().autoOpenDetail, ...(ngDevMode ? [{ debugName: "autoOpenDetail" }] : /* istanbul ignore next */ []));
4031
4661
  serverSideFiltering = computed(() => this.provider().serverSideFiltering, ...(ngDevMode ? [{ debugName: "serverSideFiltering" }] : /* istanbul ignore next */ []));
4032
4662
  filterDebounceMs = computed(() => this.provider().filterDebounceMs, ...(ngDevMode ? [{ debugName: "filterDebounceMs" }] : /* istanbul ignore next */ []));
4663
+ enableQuickFilter = computed(() => this.provider().enableQuickFilter, ...(ngDevMode ? [{ debugName: "enableQuickFilter" }] : /* istanbul ignore next */ []));
4664
+ quickFilterValue = computed(() => this.control()?.quickFilter() ?? '', ...(ngDevMode ? [{ debugName: "quickFilterValue" }] : /* istanbul ignore next */ []));
4033
4665
  sortOption = computed(() => this.provider().sortOption, ...(ngDevMode ? [{ debugName: "sortOption" }] : /* istanbul ignore next */ []));
4034
4666
  rowSelection = computed(() => this.provider().rowSelection, ...(ngDevMode ? [{ debugName: "rowSelection" }] : /* istanbul ignore next */ []));
4035
4667
  groupDescription = computed(() => this.provider().groupDescription, ...(ngDevMode ? [{ debugName: "groupDescription" }] : /* istanbul ignore next */ []));
4036
4668
  groupActions = computed(() => this.provider().groupActions, ...(ngDevMode ? [{ debugName: "groupActions" }] : /* istanbul ignore next */ []));
4037
4669
  cellMenuItems = computed(() => this.provider().cellMenuItems, ...(ngDevMode ? [{ debugName: "cellMenuItems" }] : /* istanbul ignore next */ []));
4038
4670
  headerGroups = computed(() => this.provider().headerGroups, ...(ngDevMode ? [{ debugName: "headerGroups" }] : /* istanbul ignore next */ []));
4671
+ treeConfig = computed(() => this.provider().treeConfig, ...(ngDevMode ? [{ debugName: "treeConfig" }] : /* istanbul ignore next */ []));
4039
4672
  zebraStripes = computed(() => this.provider().zebraStripes, ...(ngDevMode ? [{ debugName: "zebraStripes" }] : /* istanbul ignore next */ []));
4040
4673
  showChangedCellIndicator = computed(() => this.provider().showChangedCellIndicator, ...(ngDevMode ? [{ debugName: "showChangedCellIndicator" }] : /* istanbul ignore next */ []));
4041
4674
  confirmRowDelete = computed(() => this.provider().confirmRowDelete, ...(ngDevMode ? [{ debugName: "confirmRowDelete" }] : /* istanbul ignore next */ []));
@@ -4089,6 +4722,14 @@ class AgridComponent {
4089
4722
  filterChange = output();
4090
4723
  /** Emitted when a column sort changes in server-side filtering mode. */
4091
4724
  sortChange = output();
4725
+ /**
4726
+ * Emitted (debounced) when the global quick-filter text changes in server-side filtering mode.
4727
+ * The host should refetch rows matching the text. Not emitted in client mode, where the grid
4728
+ * filters locally.
4729
+ */
4730
+ quickFilterChange = output();
4731
+ /** Emitted when a `ColDef.validate` hook rejects a committed value (inline or sidebar). */
4732
+ validationFailed = output();
4092
4733
  // ── Public state ─────────────────────────────────────────────────────────────
4093
4734
  /** Currently focused cell, or `null`. */
4094
4735
  selectedCell = signal(null, ...(ngDevMode ? [{ debugName: "selectedCell" }] : /* istanbul ignore next */ []));
@@ -4203,6 +4844,7 @@ class AgridComponent {
4203
4844
  control: this.control,
4204
4845
  groupDescription: this.groupDescription,
4205
4846
  });
4847
+ treeController = new AgridTreeController();
4206
4848
  // ── Derived state ─────────────────────────────────────────────────────────────
4207
4849
  allowRowReorder = computed(() => (this.control()?.allowRowReorder() ?? false) && !this.control()?.groupByField(), ...(ngDevMode ? [{ debugName: "allowRowReorder" }] : /* istanbul ignore next */ []));
4208
4850
  /** `true` when there is a committed edit that can be undone (Ctrl+Z). */
@@ -4254,6 +4896,8 @@ class AgridComponent {
4254
4896
  allowAddRows: this.allowAddRows,
4255
4897
  autoAddRows: this.autoAddRows,
4256
4898
  expandedGroups: this.groupController.expandedGroups,
4899
+ treeConfig: this.treeConfig,
4900
+ expandedTreeIds: this.treeController.expandedIds,
4257
4901
  });
4258
4902
  editController = new AgridEditController({
4259
4903
  control: this.control,
@@ -4266,6 +4910,7 @@ class AgridComponent {
4266
4910
  scrollToCell: (displayIndex, colIndex) => this.scrollToKeepVisible(displayIndex, colIndex),
4267
4911
  focusGrid: () => this.wrapperEl().nativeElement.focus(),
4268
4912
  onCellEdit: event => this.emitEditEvents(event),
4913
+ onValidationFailed: event => this.validationFailed.emit({ ...event, source: 'inline' }),
4269
4914
  });
4270
4915
  /** Total filtered row count regardless of current page. */
4271
4916
  filteredRowCount = this.projection.filteredRowCount;
@@ -4477,8 +5122,11 @@ class AgridComponent {
4477
5122
  useSidebarEditor: this.useSidebarEditor,
4478
5123
  onFieldChange: event => this.markCellChanged(event),
4479
5124
  onCellEdit: event => this.emitSidebarEditEvents(event),
5125
+ onValidationFailed: event => this.validationFailed.emit({ ...event, source: 'sidebar' }),
4480
5126
  });
4481
5127
  sidebarOpen = this.sidebarController.open;
5128
+ /** @internal Per-field sidebar validation messages. */
5129
+ sidebarValidationErrors = this.sidebarController.validationErrors;
4482
5130
  sidebarTab = this.sidebarController.tab;
4483
5131
  sidebarRow = this.sidebarController.row;
4484
5132
  sidebarHiddenColumns = this.sidebarController.hiddenColumns;
@@ -4698,6 +5346,8 @@ class AgridComponent {
4698
5346
  this.browser.addDocumentListener('pointerdown', onOutsidePointerDown);
4699
5347
  this.destroyRef.onDestroy(() => {
4700
5348
  this.browser.removeDocumentListener('pointerdown', onOutsidePointerDown);
5349
+ if (this.quickFilterTimer !== null)
5350
+ clearTimeout(this.quickFilterTimer);
4701
5351
  });
4702
5352
  // Re-sync pinned pane scroll after displayItems changes — CDK independently adjusts
4703
5353
  // each viewport when item count changes (group/ungroup, collapse), which can leave
@@ -4744,6 +5394,47 @@ class AgridComponent {
4744
5394
  getItemOriginalIndex(item) {
4745
5395
  return isDataRowItem(item) ? item.originalIndex : null;
4746
5396
  }
5397
+ // ── Template helpers — tree ───────────────────────────────────────────────────
5398
+ /** @internal True when `col` is the configured tree column. */
5399
+ isTreeCell(col) {
5400
+ return this.treeConfig()?.treeField === col.field;
5401
+ }
5402
+ /** @internal Tree depth of a row item (0 when not a tree row). */
5403
+ treeRowLevel(item) {
5404
+ return isTreeRowItem(item) ? item.level : 0;
5405
+ }
5406
+ /** @internal Whether a tree row has children and can be expanded. */
5407
+ treeRowExpandable(item) {
5408
+ return isTreeRowItem(item) && item.expandable;
5409
+ }
5410
+ /** @internal Whether a tree row is currently expanded. */
5411
+ treeRowExpanded(item) {
5412
+ return isTreeRowItem(item) && item.expanded;
5413
+ }
5414
+ /** @internal Toggle the expand/collapse state of a tree row from its twisty. */
5415
+ onTreeToggle(item) {
5416
+ const config = this.treeConfig();
5417
+ if (!config || !isTreeRowItem(item))
5418
+ return;
5419
+ this.treeController.toggle(config.getId(item.row));
5420
+ }
5421
+ /** Expand every expandable node in the tree. No-op when not in tree mode. */
5422
+ expandAllNodes() {
5423
+ const config = this.treeConfig();
5424
+ if (!config)
5425
+ return;
5426
+ const expandable = new Set();
5427
+ for (const row of this.dataSource().rows()) {
5428
+ const parentId = config.getParentId(row);
5429
+ if (parentId != null)
5430
+ expandable.add(parentId);
5431
+ }
5432
+ this.treeController.expandAll(expandable);
5433
+ }
5434
+ /** Collapse every node in the tree. No-op when not in tree mode. */
5435
+ collapseAllNodes() {
5436
+ this.treeController.collapseAll();
5437
+ }
4747
5438
  /** @internal CDK trackBy — arrow to preserve `this`. */
4748
5439
  trackByItem = (_di, item) => {
4749
5440
  if (item === 'ghost')
@@ -4791,6 +5482,32 @@ class AgridComponent {
4791
5482
  return this.columnMenuController.hasMultiSort();
4792
5483
  }
4793
5484
  getTextFilter(field) { return this.columnMenuController.getTextFilter(field); }
5485
+ /** @internal Range-filter input type for a column, or `null` when not range-filterable. */
5486
+ getMenuFilterType(field) {
5487
+ return this.columnMenuController.getFilterType(field);
5488
+ }
5489
+ /** @internal */
5490
+ getMenuOperator(field) {
5491
+ return this.columnMenuController.getFilterOperator(field);
5492
+ }
5493
+ /** @internal */
5494
+ getMenuOperand(field) { return this.columnMenuController.getFilterOperand(field); }
5495
+ /** @internal */
5496
+ getMenuOperand2(field) {
5497
+ return this.columnMenuController.getFilterOperand2(field);
5498
+ }
5499
+ /** @internal */
5500
+ onMenuOperatorChange(field, operator) {
5501
+ this.columnMenuController.setFilterOperator(field, operator);
5502
+ }
5503
+ /** @internal */
5504
+ onMenuOperandChange(field, value) {
5505
+ this.columnMenuController.setFilterOperand(field, value);
5506
+ }
5507
+ /** @internal */
5508
+ onMenuOperand2Change(field, value) {
5509
+ this.columnMenuController.setFilterOperand2(field, value);
5510
+ }
4794
5511
  /** @internal */
4795
5512
  getSort(field) {
4796
5513
  return this.columnMenuController.getSort(field);
@@ -4871,6 +5588,39 @@ class AgridComponent {
4871
5588
  }
4872
5589
  /** @internal */
4873
5590
  onDraftChange(value) { this.editController.setDraft(value); }
5591
+ quickFilterTimer = null;
5592
+ /**
5593
+ * @internal Quick-filter input handler. Stores the text on the control (drives the bound value
5594
+ * and client-side filtering) and, in server mode, emits a debounced `quickFilterChange` instead.
5595
+ */
5596
+ onQuickFilterInput(event) {
5597
+ const text = event.target.value;
5598
+ this.control()?.setQuickFilter(text);
5599
+ if (!this.serverSideFiltering())
5600
+ return;
5601
+ if (this.quickFilterTimer !== null)
5602
+ clearTimeout(this.quickFilterTimer);
5603
+ const delay = this.filterDebounceMs();
5604
+ if (delay === 0) {
5605
+ this.quickFilterChange.emit(text);
5606
+ return;
5607
+ }
5608
+ this.quickFilterTimer = setTimeout(() => {
5609
+ this.quickFilterTimer = null;
5610
+ this.quickFilterChange.emit(text);
5611
+ }, delay);
5612
+ }
5613
+ /** @internal Whether a column is editable in the current grid state (drives boolean checkboxes). */
5614
+ isColEditable(col) { return this.editController.isCellEditable(col); }
5615
+ /** @internal Inline validation message for a cell, or `null` when the cell has no active error. */
5616
+ cellValidationError(originalIndex, ci) {
5617
+ const error = this.editController.validationError();
5618
+ return error && error.rowIndex === originalIndex && error.colIndex === ci ? error.message : null;
5619
+ }
5620
+ /** @internal Commit a boolean-column checkbox toggle directly to the data source. */
5621
+ onBooleanToggle(originalIndex, ci, value) {
5622
+ this.editController.setCellValue(originalIndex, ci, value);
5623
+ }
4874
5624
  /** @internal Starts a fill-handle drag from the bottom-right corner of the selection. */
4875
5625
  onCellPointerDown(event, originalIndex, colIndex) {
4876
5626
  this.rangeController.startFill(event, originalIndex, colIndex);
@@ -5125,6 +5875,10 @@ class AgridComponent {
5125
5875
  onSidebarToggleColumn(field) {
5126
5876
  this.columnMenuController.toggleColumnVisibility(field);
5127
5877
  }
5878
+ /** @internal Sets every column in a sidebar header group to the requested visibility. */
5879
+ onSidebarToggleColumnGroup(fields, visible) {
5880
+ this.columnMenuController.setColumnsVisibility(fields, visible);
5881
+ }
5128
5882
  /** @internal Mirrors vertical scrolling from the main viewport into both pinned panes. */
5129
5883
  onBodyScroll() {
5130
5884
  const offset = this.viewport().measureScrollOffset();
@@ -5236,7 +5990,7 @@ class AgridComponent {
5236
5990
  return this.columnSizing.getWidthToken(col);
5237
5991
  }
5238
5992
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AgridComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5239
- 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 [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)\"\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 [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 (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 [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)\"\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 [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 (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", "seedChar"], outputs: ["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", "row", "hiddenColumns", "locale", "localeText", "readonlyGrid", "useSidebarEditor"], outputs: ["close", "tabChange", "toggleColumn", "detailEdit", "save"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5993
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: AgridComponent, isStandalone: true, selector: "agrid", inputs: { provider: { classPropertyName: "provider", publicName: "provider", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cellEdit: "cellEdit", recordEdit: "recordEdit", rowRemoved: "rowRemoved", prepareAddRecord: "prepareAddRecord", rowReorder: "rowReorder", rowSelect: "rowSelect", rowDoubleClicked: "rowDoubleClicked", rowClick: "rowClick", rowChanged: "rowChanged", pageChange: "pageChange", filterChange: "filterChange", sortChange: "sortChange", quickFilterChange: "quickFilterChange", validationFailed: "validationFailed" }, host: { properties: { "class.ag-zebra": "zebraStripes()", "style.min-height": "minHeight()", "style.max-height": "maxHeight()" } }, viewQueries: [{ propertyName: "viewport", first: true, predicate: ["scrollViewport"], descendants: true, isSignal: true }, { propertyName: "pinnedViewport", first: true, predicate: ["pinnedViewport"], descendants: true, isSignal: true }, { propertyName: "rightPinnedViewport", first: true, predicate: ["rightPinnedViewport"], descendants: true, isSignal: true }, { propertyName: "wrapperEl", first: true, predicate: ["wrapper"], descendants: true, isSignal: true }, { propertyName: "horizontalScrollerEl", first: true, predicate: ["horizontalScroller"], descendants: true, isSignal: true }], ngImport: i0, template: "<div #wrapper class=\"ag-wrapper\" tabindex=\"0\" role=\"grid\" [attr.aria-label]=\"localeText().grid\"\n [attr.aria-rowcount]=\"ariaRowCount()\" [attr.aria-colcount]=\"ariaColCount()\"\n [attr.aria-multiselectable]=\"rowSelection() === 'multi' ? 'true' : null\"\n [attr.aria-readonly]=\"readonlyGrid() ? 'true' : null\"\n [attr.aria-busy]=\"loading() ? 'true' : null\" (copy)=\"onCopy($event)\" (paste)=\"onPaste($event)\"\n (focusin)=\"onGridFocusIn($event)\"\n (click)=\"closeContextMenu(); closeCellContextMenu(); closeFilterMenu(); closeGroupActionsMenu()\">\n @if (enableQuickFilter()) {\n <div class=\"ag-toolbar\">\n <input\n class=\"ag-quick-filter\"\n type=\"search\"\n [value]=\"quickFilterValue()\"\n (input)=\"onQuickFilterInput($event)\"\n [placeholder]=\"localeText().quickFilterPlaceholder\"\n [attr.aria-label]=\"localeText().quickFilterPlaceholder\"\n />\n </div>\n }\n <div class=\"ag-main-area\">\n <div class=\"ag-grid-split\" [class.ag-has-right-pane]=\"hasRightPinnedPane()\">\n @if (hasPinnedPane()) {\n <div class=\"ag-pinned-pane\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups ag-header-groups--pinned\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (showControlColumn()) {\n <div class=\"ag-header-group-cell ag-header-group-cell--empty\"></div>\n }\n @for (run of pinnedHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\"\n [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header ag-header--pinned\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (showControlColumn()) {\n <div class=\"ag-header-cell ag-control-header\" role=\"columnheader\" aria-colindex=\"1\">\n <div class=\"ag-header-cell-top\"></div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\"></div>\n }\n </div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-header-cell ag-header-cell--pinned\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-header-cell--pinned-last]=\"isLastPinnedColumn(col.field)\" [attr.data-col-field]=\"col.field\"\n (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\"\n (pointerdown)=\"$event.stopPropagation()\" (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <cdk-virtual-scroll-viewport #pinnedViewport class=\"ag-body ag-pinned-body\" [itemSize]=\"rowHeight()\"\n [style.width.px]=\"pinnedPaneWidth()\">\n <div *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\" role=\"row\"\n [attr.aria-rowindex]=\"di + headerRowCount() + 1\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-add-row--selected]=\"item === null && isAddRowSelected()\" [class.ag-ghost-row]=\"item === 'ghost'\"\n [class.ag-group-header-row]=\"isGroupHeaderItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isPinnedPaneRowSelected(item)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [attr.data-original-index]=\"getItemOriginalIndex(item)\" [style.height.px]=\"rowHeight()\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? pinnedGridTemplateColumns() : null\"\n (pointerdown)=\"isDataRowItem(item) && onRowPointerDown($event, item.originalIndex)\"\n (click)=\"isDataRowItem(item) && onRowClick($event, item)\">\n @if (isDataRowItem(item)) {\n @if (showControlColumn()) {\n <div class=\"ag-control-cell\" role=\"rowheader\" aria-colindex=\"1\"\n [class.ag-control-cell--reorder]=\"allowRowReorder()\"\n (contextmenu)=\"onControlContextMenu($event, item.originalIndex)\" (click)=\"$event.stopPropagation()\"\n (pointerdown)=\"onControlPointerDown($event, item.originalIndex)\">\n @if (enableRowMarking()) {\n <input class=\"ag-row-marker\" type=\"checkbox\" [checked]=\"isRowMarked(item.originalIndex)\"\n [attr.aria-label]=\"localeText().markRow\"\n (pointerdown)=\"$event.stopPropagation()\" (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRowMarked(item.originalIndex)\" />\n }\n </div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.title]=\"getCellTitle(col, item.row[col.field])\"\n [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\"\n [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\" [editable]=\"isColEditable(col)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\"\n [treeCell]=\"isTreeCell(col)\" [treeLevel]=\"treeRowLevel(item)\"\n [treeExpandable]=\"treeRowExpandable(item)\" [treeExpanded]=\"treeRowExpanded(item)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class]=\"getCellClass(col, item.row[col.field], item.row)\" [class.ag-cell--pinned]=\"true\"\n [class.ag-cell--pinned-last]=\"isLastPinnedColumn(col.field)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\" (startEdit)=\"onStartEdit(item.originalIndex, ci)\"\n (draftChange)=\"onDraftChange($event)\" (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\" (treeToggle)=\"onTreeToggle(item)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-pinned-row-spacer\"></div>\n } @else if (item === 'ghost') {\n @if (showControlColumn()) {\n <div class=\"ag-control-cell ag-control-cell--reorder ag-ghost-handle\"></div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-ghost-cell ag-cell--pinned\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--pinned-last]=\"isLastPinnedColumn(col.field)\">{{\n getGhostCellDisplay(col) }}</div>\n }\n } @else {\n <div class=\"ag-pinned-group-spacer\"></div>\n }\n </div>\n </cdk-virtual-scroll-viewport>\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\"\n [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\">\n @if (showControlColumn()) {\n <div class=\"ag-footer-cell ag-footer-cell--control\" role=\"gridcell\" aria-colindex=\"1\"></div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [class.ag-footer-cell--pinned-last]=\"isLastPinnedColumn(col.field)\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n <div class=\"ag-scroll-pane\">\n <div #horizontalScroller class=\"ag-horizontal-scroll\" (scroll)=\"onHorizontalScroll()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (run of scrollableHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\"\n [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-header-cell\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.data-col-field]=\"col.field\" (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n @if (isGroupedByField(col.field)) {\n <span class=\"ag-sort-badge\">\u229F</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\"\n (pointerdown)=\"$event.stopPropagation()\" (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <cdk-virtual-scroll-viewport #scrollViewport class=\"ag-body\" [itemSize]=\"rowHeight()\"\n [style.min-width.px]=\"scrollableTotalWidth()\" (scroll)=\"onBodyScroll()\">\n <div *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\" role=\"row\"\n [attr.aria-rowindex]=\"di + headerRowCount() + 1\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-add-row--selected]=\"item === null && isAddRowSelected()\" [class.ag-ghost-row]=\"item === 'ghost'\"\n [class.ag-group-header-row]=\"isGroupHeaderItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [attr.data-original-index]=\"getItemOriginalIndex(item)\" [style.height.px]=\"rowHeight()\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? scrollableGridTemplateColumns() : null\"\n (pointerdown)=\"isDataRowItem(item) && onRowPointerDown($event, item.originalIndex)\"\n (click)=\"isDataRowItem(item) && onRowClick($event, item)\">\n @if (isDataRowItem(item)) {\n @for (col of scrollableColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.title]=\"getCellTitle(col, item.row[col.field])\"\n [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\"\n [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\" [editable]=\"isColEditable(col)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\"\n [treeCell]=\"isTreeCell(col)\" [treeLevel]=\"treeRowLevel(item)\"\n [treeExpandable]=\"treeRowExpandable(item)\" [treeExpanded]=\"treeRowExpanded(item)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class]=\"getCellClass(col, item.row[col.field], item.row)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\"\n (startEdit)=\"onStartEdit(item.originalIndex, ci)\" (draftChange)=\"onDraftChange($event)\" (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\"\n (treeToggle)=\"onTreeToggle(item)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-add-row-label\" (click)=\"onActivateAddRow()\">\n <span class=\"ag-add-row-icon\">+</span> {{ localeText().addRow }}\n </div>\n } @else if (item === 'ghost') {\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-ghost-cell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\">{{\n getGhostCellDisplay(col) }}</div>\n }\n } @else {\n <div class=\"ag-group-header-content\" [style.min-width.px]=\"scrollableTotalWidth()\">\n <div class=\"ag-group-header-main\"\n (click)=\"onGroupHeaderClick(item.groupLabel); $event.stopPropagation()\">\n <span class=\"ag-group-icon\" [class.ag-group-icon--expanded]=\"!item.collapsed\">\u25B6</span>\n <span class=\"ag-group-label\">{{ item.groupLabel }}</span>\n @if (groupActions().length > 0) {\n <button class=\"ag-group-actions-btn\" [title]=\"localeText().actions\"\n (click)=\"openGroupActionsMenu($event, item.groupLabel)\">\u22EE</button>\n }\n <span class=\"ag-group-count\">{{ item.count }}</span>\n @if (getGroupDescription(item.groupLabel); as desc) {\n <span class=\"ag-group-description\">{{ desc }}</span>\n }\n @if (item.aggregates; as aggs) {\n <span class=\"ag-group-aggregates\">\n @for (col of visibleColDefs(); track col.field) {\n @if (hasAggregate(col) && aggs[col.field] !== undefined) {\n <span class=\"ag-group-aggregate\">\n <span class=\"ag-group-aggregate-col\">{{ col.header }}</span>\n <span class=\"ag-group-aggregate-op\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, aggs[col.field]) }}\n </span>\n }\n }\n </span>\n }\n </div>\n </div>\n }\n @if (isDataRowItem(item) && isRowPendingDelete(item.originalIndex)) {\n <div class=\"ag-delete-confirmation\" role=\"alertdialog\"\n [attr.aria-label]=\"localeText().confirmDeleteRow\"\n [style.left.px]=\"deleteConfirmationLeft()\"\n [style.width.px]=\"deleteConfirmationWidth() || null\"\n (pointerdown)=\"$event.stopPropagation()\" (click)=\"$event.stopPropagation()\">\n <span class=\"ag-delete-confirmation-text\">{{ localeText().confirmDeleteRow }}</span>\n <button class=\"ag-delete-confirmation-btn ag-delete-confirmation-btn--yes\"\n type=\"button\" (click)=\"confirmPendingRowDelete()\">{{ localeText().confirmYes }}</button>\n <button class=\"ag-delete-confirmation-btn\" type=\"button\" data-delete-confirm-no\n (click)=\"cancelRowDelete()\">{{ localeText().confirmNo }}</button>\n </div>\n }\n </div>\n </cdk-virtual-scroll-viewport>\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\"\n [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if (hasRightPinnedPane()) {\n <div class=\"ag-pinned-pane ag-pinned-pane--right\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @for (run of rightHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\"\n [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-header-cell ag-header-cell--pinned\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-header-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\" [attr.data-col-field]=\"col.field\"\n (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n @if (isGroupedByField(col.field)) {\n <span class=\"ag-sort-badge\">'\u229F '</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\"\n (pointerdown)=\"$event.stopPropagation()\" (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <cdk-virtual-scroll-viewport #rightPinnedViewport class=\"ag-body ag-pinned-body ag-right-pinned-body\"\n [itemSize]=\"rowHeight()\" [style.width.px]=\"rightPinnedPaneWidth()\" (scroll)=\"onRightPinnedBodyScroll()\">\n <div *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\" role=\"row\"\n [attr.aria-rowindex]=\"di + headerRowCount() + 1\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-ghost-row]=\"item === 'ghost'\" [class.ag-group-header-row]=\"isGroupHeaderItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [style.height.px]=\"rowHeight()\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? rightGridTemplateColumns() : null\">\n @if (isDataRowItem(item)) {\n @for (col of rightPinnedColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.title]=\"getCellTitle(col, item.row[col.field])\"\n [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\"\n [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\" [editable]=\"isColEditable(col)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\"\n [treeCell]=\"isTreeCell(col)\" [treeLevel]=\"treeRowLevel(item)\"\n [treeExpandable]=\"treeRowExpandable(item)\" [treeExpanded]=\"treeRowExpanded(item)\"\n [class]=\"getCellClass(col, item.row[col.field], item.row)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--pinned]=\"true\" [class.ag-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\" (startEdit)=\"onStartEdit(item.originalIndex, ci)\"\n (draftChange)=\"onDraftChange($event)\" (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\" (treeToggle)=\"onTreeToggle(item)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-pinned-row-spacer\"></div>\n } @else if (item === 'ghost') {\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-ghost-cell ag-cell--pinned\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\">{{ getGhostCellDisplay(col) }}</div>\n }\n } @else {\n <div class=\"ag-pinned-group-spacer\"></div>\n }\n </div>\n </cdk-virtual-scroll-viewport>\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\"\n [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\">\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [class.ag-footer-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n\n @if (loading()) {\n <div class=\"ag-state-overlay\" role=\"status\" aria-live=\"polite\">{{ localeText().loading }}</div>\n } @else if (isEmpty()) {\n <div class=\"ag-state-overlay\" role=\"status\" aria-live=\"polite\">{{ emptyTextLabel() }}</div>\n }\n\n @if (showSidebar()) {\n <agrid-sidebar\n [open]=\"sidebarOpen()\"\n [activeTab]=\"sidebarTab()\"\n [columns]=\"colDefs()\"\n [headerGroups]=\"headerGroups()\"\n [row]=\"sidebarRow()\"\n [hiddenColumns]=\"sidebarHiddenColumns()\"\n [locale]=\"locale()\"\n [localeText]=\"localeText()\"\n [readonlyGrid]=\"readonlyGrid()\"\n [useSidebarEditor]=\"useSidebarEditor()\"\n [errors]=\"sidebarValidationErrors()\"\n (close)=\"toggleSidebar()\"\n (tabChange)=\"onSidebarStripClick($event)\"\n (toggleColumn)=\"onSidebarToggleColumn($event)\"\n (toggleColumnGroup)=\"onSidebarToggleColumnGroup($event.fields, $event.visible)\"\n (detailEdit)=\"onSidebarDetailEdit($event)\"\n (save)=\"onSidebarDetailSave($event)\"\n />\n }\n </div><!-- /.ag-main-area -->\n\n @if (showPagination()) {\n <nav class=\"ag-pagination\" [attr.aria-label]=\"localeText().pagination\">\n <button class=\"ag-page-btn\" [attr.aria-label]=\"localeText().firstPage\"\n [disabled]=\"control()!.currentPage() <= 1\" (click)=\"goToFirstPage()\">\u00AB</button>\n <button class=\"ag-page-btn\" [attr.aria-label]=\"localeText().previous\"\n [disabled]=\"control()!.currentPage() <= 1\" (click)=\"goToPrevPage()\">\u2039</button>\n <span class=\"ag-page-info\" aria-live=\"polite\">{{ control()!.currentPage() }} / {{ totalPages() }}</span>\n <button class=\"ag-page-btn\" [disabled]=\"control()!.currentPage() >= totalPages()\"\n [attr.aria-label]=\"localeText().next\"\n (click)=\"goToNextPage()\">\u203A</button>\n <button class=\"ag-page-btn\" [disabled]=\"control()!.currentPage() >= totalPages()\"\n [attr.aria-label]=\"localeText().lastPage\"\n (click)=\"goToLastPage()\">\u00BB</button>\n <span class=\"ag-page-count\">{{ localeText().rows(filteredRowCount()) }}</span>\n </nav>\n }\n\n @if (findOpen()) {\n <agrid-find-panel [query]=\"findQuery()\" [matchCount]=\"findMatches().length\" [activeIndex]=\"findActiveIndex()\"\n [localeText]=\"localeText()\" (queryChange)=\"onFindInput($event)\" (previous)=\"goToFindMatch(-1)\"\n (next)=\"goToFindMatch(1)\" (close)=\"closeFind()\" />\n }\n\n <!-- Row context menu -->\n @if (contextMenu(); as menu) {\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n <button class=\"ag-context-item ag-context-item--danger\" role=\"menuitem\" (click)=\"deleteRow(menu.rowIndex)\">\n {{ localeText().deleteRow }}\n </button>\n </div>\n }\n\n <!-- Cell context menu -->\n @if (cellContextMenuState(); as menu) {\n @let col = getColDef(menu.field)!;\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"copyCellToClipboard(menu.rowIndex, col)\">{{ localeText().copyCellValue\n }}</button>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"copyRowToClipboard(menu.rowIndex)\">{{ localeText().copyRow }}</button>\n @if (allowAddRows() && !readonlyGrid()) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"insertRowAt(menu.rowIndex)\">{{ localeText().insertRowAbove }}</button>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"insertRowAt(menu.rowIndex + 1)\">{{ localeText().insertRowBelow }}</button>\n }\n @if (!readonlyGrid()) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n <button class=\"ag-context-item ag-context-item--danger\" role=\"menuitem\"\n (click)=\"deleteRow(menu.rowIndex)\">{{ localeText().deleteRow }}</button>\n }\n @if (cellMenuItems().length) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n @for (item of cellMenuItems(); track $index) {\n @if (item === null) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n } @else {\n <button class=\"ag-context-item\" role=\"menuitem\" [class.ag-context-item--danger]=\"item.danger\" [disabled]=\"item.disabled\"\n (click)=\"runCellMenuItem(item, menu)\">{{\n item.label }}</button>\n }\n }\n }\n </div>\n }\n\n <!-- Group actions menu -->\n @if (groupActionsMenu(); as menu) {\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n @for (action of groupActions(); track action.label) {\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onGroupAction(action, menu.label)\">\n {{ action.label }}\n </button>\n }\n </div>\n }\n\n <!-- Filter dropdown -->\n @if (filterMenu(); as menu) {\n <agrid-column-menu [x]=\"menu.x\" [y]=\"menu.y\" [header]=\"getColDef(menu.field)?.header ?? menu.field\"\n [sortDir]=\"getSort(menu.field)\" [sortable]=\"sortOption() !== 'none'\"\n [showColumnActions]=\"!!control()\" [pinned]=\"getColumnPinState(menu.field)\"\n [groupable]=\"!!getColDef(menu.field)?.groupable\" [grouped]=\"isGroupedByField(menu.field)\"\n [filterable]=\"!!getColDef(menu.field)?.filterable\" [showValueFilter]=\"!serverSideFiltering()\"\n [filterType]=\"getMenuFilterType(menu.field)\"\n [operator]=\"getMenuOperator(menu.field)\" [operand]=\"getMenuOperand(menu.field)\"\n [operand2]=\"getMenuOperand2(menu.field)\"\n (operatorChange)=\"onMenuOperatorChange(menu.field, $event)\"\n (operandChange)=\"onMenuOperandChange(menu.field, $event)\"\n (operand2Change)=\"onMenuOperand2Change(menu.field, $event)\"\n [search]=\"filterMenuSearch()\"\n [allSelected]=\"isMenuAllSelected(menu.field)\"\n [valueItems]=\"serverSideFiltering() || !getColDef(menu.field)?.filterable ? [] : columnMenuValueItems()\"\n [localeText]=\"localeText()\"\n [sortPriority]=\"getSortPriority(menu.field)\" [hasMultiSort]=\"hasMultiSort()\" (sort)=\"onMenuSort(menu.field, $event)\"\n (resetSort)=\"onMenuResetSort(menu.field, $event)\" (autosize)=\"onMenuAutosizeColumn(menu.field)\"\n (togglePin)=\"onMenuTogglePin(menu.field)\" (togglePinRight)=\"onMenuTogglePinRight(menu.field)\"\n (hide)=\"onMenuHideColumn(menu.field)\" (toggleGroup)=\"onMenuToggleGroupBy(menu.field)\"\n (clearFilter)=\"onMenuClearFilter(menu.field)\" (clearAll)=\"onMenuClearAll()\"\n (searchChange)=\"onFilterMenuSearch($event)\" (toggleAll)=\"onMenuToggleAll(menu.field)\"\n (toggleValue)=\"onMenuToggleValue(menu.field, $event)\" [aggregate]=\"getEffectiveAggregate(getColDef(menu.field)!)\"\n (setAggregate)=\"onMenuSetAggregate(menu.field, $event)\" />\n }\n\n @if (columnDragPreview(); as preview) {\n <div class=\"ag-column-drag-preview\"\n [style.left.px]=\"preview.x\" [style.top.px]=\"preview.y\"\n [style.width.px]=\"preview.width\" [style.height.px]=\"preview.height\">\n <span>{{ preview.label }}</span>\n </div>\n }\n</div>\n", styles: ["@layer agrid-defaults{:host{--agrid-color-text: #24292f;--agrid-color-text-muted: #57606a;--agrid-color-accent: #1a73e8;--agrid-color-accent-subtle: #e8f0fe;--agrid-color-accent-fg: #1558b0;--agrid-color-accent-border: #c8d8f8;--agrid-color-danger: #d1242f;--agrid-color-danger-subtle: #fff1f0;--agrid-color-border: #d0d7de;--agrid-color-bg: #ffffff;--agrid-color-bg-subtle: #fafbfc;--agrid-color-bg-muted: #f6f8fa;--agrid-color-shadow: rgba(140, 149, 159, .2);--agrid-color-bg-stripe: #f0f2f5;--agrid-color-cell-changed: #f59e0b;--agrid-color-row-marked: #fff8c5}}:host{display:flex;flex-direction:column;min-height:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:13px;color:var(--agrid-color-text)}.ag-wrapper{position:relative;display:flex;flex-direction:column;flex:1;min-height:0;border:1px solid var(--agrid-color-border);overflow:hidden;outline:none;border-radius:4px}.ag-state-overlay{position:absolute;inset:34px 0 0;display:flex;align-items:center;justify-content:center;color:var(--agrid-color-text-muted);background:color-mix(in srgb,var(--agrid-color-bg) 78%,transparent);pointer-events:none;z-index:3}.ag-header{display:grid;flex-shrink:0;background:var(--agrid-color-bg-muted);border-bottom:2px solid var(--agrid-color-border)}.ag-header-groups{display:grid;flex-shrink:0;height:28px;background:color-mix(in srgb,var(--agrid-color-bg-muted) 72%,var(--agrid-color-bg));border-bottom:1px solid var(--agrid-color-border)}.ag-header-group-cell{display:flex;align-items:center;justify-content:center;min-width:0;padding:0 8px;overflow:hidden;border-right:1px solid var(--agrid-color-border);box-sizing:border-box;color:var(--agrid-color-text);font-size:12px;font-weight:600;text-overflow:ellipsis;white-space:nowrap;cursor:grab;-webkit-user-select:none;user-select:none;transition:opacity .1s ease}.ag-header-group-cell:last-child{border-right:none}.ag-header-group-cell--empty,.ag-header-group-cell--locked{cursor:default}.ag-header-group-cell--dragging{opacity:.12}.ag-header-cell{position:relative;display:flex;align-items:center;font-weight:600;border-right:1px solid var(--agrid-color-border);overflow:hidden;white-space:nowrap;-webkit-user-select:none;user-select:none;box-sizing:border-box}.ag-header-cell:last-child{border-right:none}.ag-header-cell-top{display:flex;align-items:center;flex:1;min-width:0;padding:0 6px;height:32px;overflow:hidden;white-space:nowrap}.ag-header-cell-label{overflow:hidden;text-overflow:ellipsis;flex:1}.ag-header--with-filters .ag-header-cell{flex-direction:column;align-items:stretch;height:auto;white-space:normal}.ag-header--with-filters .ag-header-cell-top{flex:0 0 32px}.ag-header-cell-filter{height:28px;display:flex;align-items:center;padding:0 2px;border-top:1px solid var(--agrid-color-border);background:var(--agrid-color-bg)}.ag-header-cell{cursor:grab;transition:transform .16s cubic-bezier(.2,0,0,1),opacity .1s ease;will-change:transform}.ag-row>agrid-cell,.ag-ghost-cell,.ag-footer-cell{transition:transform .16s cubic-bezier(.2,0,0,1),opacity .1s ease;will-change:transform}.ag-header-cell--dragging{opacity:.12;cursor:grabbing}.ag-column-reorder-item--dragging{opacity:.12}.ag-header-cell--drop-before,.ag-header-cell--drop-after{z-index:1}.ag-column-drag-preview{position:fixed;z-index:1000;display:flex;align-items:flex-start;padding:8px 10px;border:1px solid var(--agrid-color-accent);border-radius:5px;color:var(--agrid-color-text);background:color-mix(in srgb,var(--agrid-color-bg) 94%,var(--agrid-color-accent));box-shadow:0 10px 28px var(--agrid-color-shadow);font-weight:600;line-height:16px;box-sizing:border-box;pointer-events:none;opacity:.96}.ag-sort-badge{font-size:11px;color:var(--agrid-color-accent);flex-shrink:0;line-height:1}.ag-sort-priority{font-size:9px;vertical-align:super;opacity:.75}.ag-resize-handle{position:absolute;top:0;right:0;width:5px;height:100%;cursor:col-resize;z-index:1}.ag-resize-handle:hover{background:var(--agrid-color-accent);opacity:.5}.ag-filter-input{flex:1;min-width:0;height:20px;border:1px solid var(--agrid-color-border);border-radius:3px;outline:none;font:inherit;font-size:12px;padding:0 4px;background:var(--agrid-color-bg)}.ag-filter-input:focus{border-color:var(--agrid-color-accent)}.ag-header-cell--filtered .ag-header-cell-label:after{content:\" \\25be\";font-size:9px;color:var(--agrid-color-accent)}.ag-main-area{display:flex;flex:1;min-height:0;overflow:hidden}.ag-grid-split{display:flex;flex:1;min-width:0;min-height:0;overflow:hidden}.ag-pinned-pane{flex-shrink:0;min-height:0;display:flex;flex-direction:column;overflow:hidden;border-right:1px solid var(--agrid-color-border);background:var(--agrid-color-bg)}.ag-scroll-pane{flex:1;min-width:0;min-height:0;display:flex}.ag-horizontal-scroll{flex:1;min-width:0;min-height:0;overflow-x:auto;overflow-y:hidden;display:flex;flex-direction:column}.ag-body{flex:1;overflow-x:clip;overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-horizontal-scroll{scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-body::-webkit-scrollbar,.ag-horizontal-scroll::-webkit-scrollbar{width:8px;height:8px}.ag-body::-webkit-scrollbar-track,.ag-horizontal-scroll::-webkit-scrollbar-track{background:transparent}.ag-body::-webkit-scrollbar-thumb,.ag-horizontal-scroll::-webkit-scrollbar-thumb{background:#0000002e;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-body::-webkit-scrollbar-thumb:hover,.ag-horizontal-scroll::-webkit-scrollbar-thumb:hover{background:#00000052;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-pinned-body{overflow:hidden}.ag-right-pinned-body{overflow-y:auto}.ag-has-right-pane .ag-body:not(.ag-pinned-body){scrollbar-width:none}.ag-has-right-pane .ag-body:not(.ag-pinned-body)::-webkit-scrollbar{display:none}.ag-row{display:grid;position:relative}.ag-row--pending-delete>:not(.ag-delete-confirmation){opacity:.2;pointer-events:none;transition:opacity .14s ease}.ag-delete-confirmation{position:absolute;top:0;bottom:0;z-index:7;display:flex;align-items:center;justify-content:center;gap:8px;min-width:min(100%,320px);padding:2px 12px;box-sizing:border-box;color:var(--agrid-color-text);background:color-mix(in srgb,var(--agrid-color-danger-subtle) 88%,transparent);border-block:1px solid color-mix(in srgb,var(--agrid-color-danger) 35%,transparent)}.ag-delete-confirmation-text{font-weight:600;white-space:nowrap}.ag-delete-confirmation-btn{min-width:42px;padding:3px 9px;border:1px solid var(--agrid-color-border);border-radius:4px;color:var(--agrid-color-text);background:var(--agrid-color-bg);font:inherit;cursor:pointer}.ag-delete-confirmation-btn:hover,.ag-delete-confirmation-btn:focus-visible{border-color:var(--agrid-color-accent);outline:none}.ag-delete-confirmation-btn--yes{color:#fff;border-color:var(--agrid-color-danger);background:var(--agrid-color-danger)}.ag-delete-confirmation-btn--yes:hover,.ag-delete-confirmation-btn--yes:focus-visible{border-color:color-mix(in srgb,var(--agrid-color-danger) 75%,#000);background:color-mix(in srgb,var(--agrid-color-danger) 85%,#000)}:host(.ag-zebra) .ag-row--odd agrid-cell:not(.editing){background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd .ag-cell--pinned{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd .ag-control-cell{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd:hover agrid-cell:not(.editing){background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd:hover .ag-cell--pinned,:host(.ag-zebra) .ag-row--odd:hover .ag-control-cell{background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd.ag-row--selected agrid-cell:not(.editing){background:var(--agrid-color-accent-subtle)}:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-cell--pinned,:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-control-cell{background:var(--agrid-color-accent-subtle)}:host(.ag-zebra) .ag-row--odd .ag-cell--pinned-first{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd:hover .ag-cell--pinned-first{background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-cell--pinned-first{background:var(--agrid-color-accent-subtle)}.ag-row:hover agrid-cell:not(.editing){background:var(--agrid-color-bg-muted)}.ag-row--selected agrid-cell:not(.editing){background:var(--agrid-color-accent-subtle)}.ag-row--marked agrid-cell:not(.editing),.ag-row--marked .ag-control-cell{background:var(--agrid-color-row-marked)}.ag-row--marked{box-shadow:inset 3px 0 #bf8700}.ag-row--selected.ag-row--marked{box-shadow:inset 3px 0 0 var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--range-selected:not(.editing){background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1px var(--agrid-color-accent-border)}.ag-row agrid-cell.ag-cell--find-match:not(.editing){background:#fff7cc}.ag-row agrid-cell.ag-cell--find-active:not(.editing){background:#ffe58a;box-shadow:inset 0 0 0 2px #b7791f}.ag-row agrid-cell.ag-cell--range-selected.selected:not(.editing){box-shadow:inset 0 0 0 2px var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--fill-preview:not(.editing){background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1px var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--fill-handle:not(.editing){position:relative}.ag-row agrid-cell.ag-cell--fill-handle:not(.editing):after{content:\"\";position:absolute;right:1px;bottom:1px;width:6px;height:6px;background:var(--agrid-color-accent);border:1px solid var(--agrid-color-bg);box-sizing:border-box;cursor:crosshair;z-index:4}.ag-row--selected .ag-control-cell{background:var(--agrid-color-accent-subtle)}.ag-row--selected{box-shadow:inset 3px 0 0 var(--agrid-color-accent)}.ag-control-header{z-index:3;border-right:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted)}.ag-control-header .ag-header-cell-filter{background:var(--agrid-color-bg-subtle)}.ag-control-cell{z-index:2;border-right:1px solid var(--agrid-color-border);border-bottom:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-subtle);cursor:context-menu;display:flex;align-items:center;justify-content:center;gap:6px;box-sizing:border-box;flex-direction:row-reverse}.ag-control-cell:after{content:\"\\22ee\";font-size:11px;color:var(--agrid-color-border);line-height:1}.ag-control-cell:hover:after{color:var(--agrid-color-text-muted)}.ag-control-cell--reorder{cursor:grab}.ag-control-cell--reorder:after{content:\"\\283f\";font-size:13px;color:var(--agrid-color-text)}.ag-control-cell--reorder:hover:after{color:var(--agrid-color-text-muted)}.ag-control-cell--reorder:active{cursor:grabbing}.ag-row-marker{position:relative;z-index:1;width:14px;height:14px;margin:0;accent-color:var(--agrid-color-accent);cursor:pointer}.ag-ghost-row{background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1.5px var(--agrid-color-accent);pointer-events:none;animation:ag-ghost-in .1s ease}@keyframes ag-ghost-in{0%{opacity:0}to{opacity:1}}.ag-ghost-handle:after{color:var(--agrid-color-accent)!important}.ag-ghost-cell{display:flex;align-items:center;padding:0 6px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;border-right:1px solid var(--agrid-color-accent-border);color:var(--agrid-color-accent-fg);font-size:13px;box-sizing:border-box;-webkit-user-select:none;user-select:none}.ag-ghost-cell:last-child{border-right:none}.ag-pinned-row-spacer,.ag-pinned-group-spacer{height:100%;border-bottom:1px solid var(--agrid-color-border);box-sizing:border-box}.ag-pinned-group-spacer{background:var(--agrid-color-bg-muted)}.ag-context-menu{position:fixed;z-index:1000;background:var(--agrid-color-bg-subtle);border:1px solid var(--agrid-color-border);border-radius:6px;box-shadow:0 8px 24px var(--agrid-color-shadow);min-width:160px;padding:4px 0;font-size:13px}.ag-context-item{display:block;width:100%;padding:6px 16px;text-align:left;background:none;border:none;cursor:pointer;color:var(--agrid-color-text);font:inherit}.ag-context-item:hover{background:var(--agrid-color-bg-muted)}.ag-context-separator{height:1px;background:var(--agrid-color-border);margin:3px 0}.ag-context-item--danger{color:var(--agrid-color-danger)}.ag-context-item--danger:hover{background:var(--agrid-color-danger-subtle)}.ag-group-header-row{display:flex;align-items:stretch;background:var(--agrid-color-bg-muted);border-bottom:1px solid var(--agrid-color-border);overflow:hidden}.ag-group-header-content{display:flex;align-items:stretch;height:100%;width:100%;-webkit-user-select:none;user-select:none}.ag-group-header-main{display:flex;align-items:center;gap:6px;padding:0 10px;flex:1;min-width:0;cursor:pointer}.ag-group-header-main:hover{background:var(--agrid-color-bg-subtle)}.ag-group-description{font-size:11px;color:var(--agrid-color-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ag-group-actions-btn{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border:none;background:none;border-radius:3px;cursor:pointer;color:var(--agrid-color-text-muted);font-size:14px;padding:0}.ag-group-actions-btn:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text)}.ag-group-icon{font-size:9px;color:var(--agrid-color-text-muted);line-height:1;display:inline-block;transition:transform .15s ease}.ag-group-icon--expanded{transform:rotate(90deg)}.ag-group-label{font-weight:600;font-size:12px;color:var(--agrid-color-text)}.ag-group-count{font-size:11px;color:var(--agrid-color-text-muted);background:var(--agrid-color-bg);border:1px solid var(--agrid-color-border);border-radius:10px;padding:0 7px;line-height:16px}.ag-group-aggregates{display:inline-flex;align-items:center;gap:10px;margin-left:4px;flex-wrap:wrap}.ag-group-aggregate{font-size:11px;color:var(--agrid-color-text);white-space:nowrap}.ag-group-aggregate-col{color:var(--agrid-color-text-muted);margin-right:4px}.ag-group-aggregate-op{color:var(--agrid-color-text-muted);margin-right:3px}.ag-footer{display:grid;flex-shrink:0;background:var(--agrid-color-bg-muted);border-top:2px solid var(--agrid-color-border)}.ag-footer-cell{display:flex;align-items:center;gap:4px;padding:0 6px;height:30px;border-right:1px solid var(--agrid-color-border);box-sizing:border-box;overflow:hidden;white-space:nowrap;font-weight:600;font-size:12px;color:var(--agrid-color-text)}.ag-footer-cell:last-child{border-right:none}.ag-footer-cell--control{background:var(--agrid-color-bg-subtle);border-right:1px solid var(--agrid-color-border)}.ag-footer-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-footer-label{font-size:10px;font-weight:400;color:var(--agrid-color-text-muted);flex-shrink:0}.ag-pagination{display:flex;align-items:center;gap:4px;padding:0 10px;height:34px;flex-shrink:0;border-top:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted);font-size:12px;color:var(--agrid-color-text-muted)}.ag-page-btn{display:flex;align-items:center;justify-content:center;width:24px;height:22px;border:1px solid var(--agrid-color-border);border-radius:3px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font-size:13px;cursor:pointer;padding:0;line-height:1}.ag-page-btn:hover:not(:disabled){background:var(--agrid-color-bg-muted);border-color:var(--agrid-color-text-muted)}.ag-page-btn:disabled{opacity:.35;cursor:default}.ag-page-info{padding:0 6px;font-weight:500;color:var(--agrid-color-text);min-width:48px;text-align:center}.ag-page-count{margin-left:auto;color:var(--agrid-color-text-muted)}.ag-add-row{border-bottom:1px dashed var(--agrid-color-border);color:var(--agrid-color-text-muted);cursor:pointer}.ag-add-row:hover .ag-add-row-label{background:var(--agrid-color-bg-muted)}.ag-add-row--selected .ag-add-row-label{outline:2px solid var(--agrid-color-accent);outline-offset:-2px}.ag-add-row-label{display:flex;align-items:center;gap:4px;height:100%;padding:0 6px;font-size:12px;-webkit-user-select:none;user-select:none}.ag-add-row-icon{font-size:16px;line-height:1;color:var(--agrid-color-text-muted)}.ag-pinned-pane--right{border-left:1px solid var(--agrid-color-border);border-right:none;box-shadow:-3px 0 6px -2px var(--agrid-color-shadow)}.ag-header-cell--pinned{background:var(--agrid-color-bg-muted)}.ag-header-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-header-cell--pinned-first{box-shadow:none}.ag-cell--pinned{background:var(--agrid-color-bg)}.ag-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-cell--pinned-first{box-shadow:none}.ag-row:hover .ag-cell--pinned{background:var(--agrid-color-bg-muted)}.ag-row--selected .ag-cell--pinned,.ag-row .ag-cell--pinned.ag-cell--range-selected,.ag-row .ag-cell--pinned.ag-cell--fill-preview{background:var(--agrid-color-accent-subtle)}.ag-row .ag-cell--pinned.ag-cell--find-match{background:#fff7cc}.ag-row .ag-cell--pinned.ag-cell--find-active{background:#ffe58a}.ag-header-menu-btn{flex-shrink:0;width:16px;height:16px;padding:0;margin-right:2px;background:none;border:1px solid transparent;border-radius:3px;cursor:pointer;font-size:10px;color:var(--agrid-color-text-muted);display:flex;align-items:center;justify-content:center;opacity:1;transition:opacity .12s;line-height:1}.ag-header-menu-btn--active{color:var(--agrid-color-accent);border-color:var(--agrid-color-accent-border);background:var(--agrid-color-accent-subtle)}.ag-header-menu-btn:hover{background:var(--agrid-color-bg);border-color:var(--agrid-color-border)}.ag-toolbar{display:flex;align-items:center;padding:6px 8px;border-bottom:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-subtle)}.ag-quick-filter{width:100%;max-width:260px;height:30px;padding:0 10px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font-size:13px;box-sizing:border-box}.ag-quick-filter:focus{outline:none;border-color:var(--agrid-color-accent-border)}\n"], dependencies: [{ kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i1.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i1.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "component", type: AgridCellComponent, selector: "agrid-cell", inputs: ["col", "rowIndex", "colIndex", "value", "row", "locale", "selected", "editing", "editable", "error", "treeCell", "treeLevel", "treeExpandable", "treeExpanded", "seedChar"], outputs: ["treeToggle", "activate", "startEdit", "booleanToggle", "draftChange"] }, { kind: "component", type: AgridColumnMenuComponent, selector: "agrid-column-menu", inputs: ["localeText", "x", "y", "header", "sortDir", "sortable", "showColumnActions", "pinned", "groupable", "grouped", "filterable", "showValueFilter", "filterType", "operator", "operand", "operand2", "search", "allSelected", "valueItems", "sortPriority", "hasMultiSort", "aggregate"], outputs: ["operatorChange", "operandChange", "operand2Change", "sort", "resetSort", "autosize", "togglePin", "togglePinRight", "hide", "toggleGroup", "clearFilter", "clearAll", "searchChange", "toggleAll", "toggleValue", "setAggregate"] }, { kind: "component", type: AgridFindPanelComponent, selector: "agrid-find-panel", inputs: ["localeText", "query", "matchCount", "activeIndex"], outputs: ["queryChange", "next", "previous", "close"] }, { kind: "component", type: AgridSidebarComponent, selector: "agrid-sidebar", inputs: ["open", "activeTab", "columns", "headerGroups", "row", "hiddenColumns", "locale", "localeText", "readonlyGrid", "useSidebarEditor", "errors"], outputs: ["close", "tabChange", "toggleColumn", "toggleColumnGroup", "detailEdit", "save"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5240
5994
  }
5241
5995
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AgridComponent, decorators: [{
5242
5996
  type: Component,
@@ -5250,8 +6004,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
5250
6004
  '[class.ag-zebra]': 'zebraStripes()',
5251
6005
  '[style.min-height]': 'minHeight()',
5252
6006
  '[style.max-height]': 'maxHeight()',
5253
- }, 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 [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)\"\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 [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 (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 [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)\"\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 [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 (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"] }]
5254
- }], 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 }] }] } });
6007
+ }, template: "<div #wrapper class=\"ag-wrapper\" tabindex=\"0\" role=\"grid\" [attr.aria-label]=\"localeText().grid\"\n [attr.aria-rowcount]=\"ariaRowCount()\" [attr.aria-colcount]=\"ariaColCount()\"\n [attr.aria-multiselectable]=\"rowSelection() === 'multi' ? 'true' : null\"\n [attr.aria-readonly]=\"readonlyGrid() ? 'true' : null\"\n [attr.aria-busy]=\"loading() ? 'true' : null\" (copy)=\"onCopy($event)\" (paste)=\"onPaste($event)\"\n (focusin)=\"onGridFocusIn($event)\"\n (click)=\"closeContextMenu(); closeCellContextMenu(); closeFilterMenu(); closeGroupActionsMenu()\">\n @if (enableQuickFilter()) {\n <div class=\"ag-toolbar\">\n <input\n class=\"ag-quick-filter\"\n type=\"search\"\n [value]=\"quickFilterValue()\"\n (input)=\"onQuickFilterInput($event)\"\n [placeholder]=\"localeText().quickFilterPlaceholder\"\n [attr.aria-label]=\"localeText().quickFilterPlaceholder\"\n />\n </div>\n }\n <div class=\"ag-main-area\">\n <div class=\"ag-grid-split\" [class.ag-has-right-pane]=\"hasRightPinnedPane()\">\n @if (hasPinnedPane()) {\n <div class=\"ag-pinned-pane\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups ag-header-groups--pinned\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (showControlColumn()) {\n <div class=\"ag-header-group-cell ag-header-group-cell--empty\"></div>\n }\n @for (run of pinnedHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\"\n [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header ag-header--pinned\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\" [style.width.px]=\"pinnedPaneWidth()\">\n @if (showControlColumn()) {\n <div class=\"ag-header-cell ag-control-header\" role=\"columnheader\" aria-colindex=\"1\">\n <div class=\"ag-header-cell-top\"></div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\"></div>\n }\n </div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-header-cell ag-header-cell--pinned\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-header-cell--pinned-last]=\"isLastPinnedColumn(col.field)\" [attr.data-col-field]=\"col.field\"\n (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\"\n (pointerdown)=\"$event.stopPropagation()\" (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <cdk-virtual-scroll-viewport #pinnedViewport class=\"ag-body ag-pinned-body\" [itemSize]=\"rowHeight()\"\n [style.width.px]=\"pinnedPaneWidth()\">\n <div *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\" role=\"row\"\n [attr.aria-rowindex]=\"di + headerRowCount() + 1\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-add-row--selected]=\"item === null && isAddRowSelected()\" [class.ag-ghost-row]=\"item === 'ghost'\"\n [class.ag-group-header-row]=\"isGroupHeaderItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isPinnedPaneRowSelected(item)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [attr.data-original-index]=\"getItemOriginalIndex(item)\" [style.height.px]=\"rowHeight()\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? pinnedGridTemplateColumns() : null\"\n (pointerdown)=\"isDataRowItem(item) && onRowPointerDown($event, item.originalIndex)\"\n (click)=\"isDataRowItem(item) && onRowClick($event, item)\">\n @if (isDataRowItem(item)) {\n @if (showControlColumn()) {\n <div class=\"ag-control-cell\" role=\"rowheader\" aria-colindex=\"1\"\n [class.ag-control-cell--reorder]=\"allowRowReorder()\"\n (contextmenu)=\"onControlContextMenu($event, item.originalIndex)\" (click)=\"$event.stopPropagation()\"\n (pointerdown)=\"onControlPointerDown($event, item.originalIndex)\">\n @if (enableRowMarking()) {\n <input class=\"ag-row-marker\" type=\"checkbox\" [checked]=\"isRowMarked(item.originalIndex)\"\n [attr.aria-label]=\"localeText().markRow\"\n (pointerdown)=\"$event.stopPropagation()\" (click)=\"$event.stopPropagation()\"\n (change)=\"toggleRowMarked(item.originalIndex)\" />\n }\n </div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.title]=\"getCellTitle(col, item.row[col.field])\"\n [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\"\n [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\" [editable]=\"isColEditable(col)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\"\n [treeCell]=\"isTreeCell(col)\" [treeLevel]=\"treeRowLevel(item)\"\n [treeExpandable]=\"treeRowExpandable(item)\" [treeExpanded]=\"treeRowExpanded(item)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class]=\"getCellClass(col, item.row[col.field], item.row)\" [class.ag-cell--pinned]=\"true\"\n [class.ag-cell--pinned-last]=\"isLastPinnedColumn(col.field)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\" (startEdit)=\"onStartEdit(item.originalIndex, ci)\"\n (draftChange)=\"onDraftChange($event)\" (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\" (treeToggle)=\"onTreeToggle(item)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-pinned-row-spacer\"></div>\n } @else if (item === 'ghost') {\n @if (showControlColumn()) {\n <div class=\"ag-control-cell ag-control-cell--reorder ag-ghost-handle\"></div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-ghost-cell ag-cell--pinned\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--pinned-last]=\"isLastPinnedColumn(col.field)\">{{\n getGhostCellDisplay(col) }}</div>\n }\n } @else {\n <div class=\"ag-pinned-group-spacer\"></div>\n }\n </div>\n </cdk-virtual-scroll-viewport>\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\"\n [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"pinnedGridTemplateColumns()\">\n @if (showControlColumn()) {\n <div class=\"ag-footer-cell ag-footer-cell--control\" role=\"gridcell\" aria-colindex=\"1\"></div>\n }\n @for (col of pinnedColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [class.ag-footer-cell--pinned-last]=\"isLastPinnedColumn(col.field)\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n <div class=\"ag-scroll-pane\">\n <div #horizontalScroller class=\"ag-horizontal-scroll\" (scroll)=\"onHorizontalScroll()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (run of scrollableHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\"\n [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-header-cell\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.data-col-field]=\"col.field\" (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n @if (isGroupedByField(col.field)) {\n <span class=\"ag-sort-badge\">\u229F</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\"\n (pointerdown)=\"$event.stopPropagation()\" (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <cdk-virtual-scroll-viewport #scrollViewport class=\"ag-body\" [itemSize]=\"rowHeight()\"\n [style.min-width.px]=\"scrollableTotalWidth()\" (scroll)=\"onBodyScroll()\">\n <div *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\" role=\"row\"\n [attr.aria-rowindex]=\"di + headerRowCount() + 1\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-add-row--selected]=\"item === null && isAddRowSelected()\" [class.ag-ghost-row]=\"item === 'ghost'\"\n [class.ag-group-header-row]=\"isGroupHeaderItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [attr.data-original-index]=\"getItemOriginalIndex(item)\" [style.height.px]=\"rowHeight()\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? scrollableGridTemplateColumns() : null\"\n (pointerdown)=\"isDataRowItem(item) && onRowPointerDown($event, item.originalIndex)\"\n (click)=\"isDataRowItem(item) && onRowClick($event, item)\">\n @if (isDataRowItem(item)) {\n @for (col of scrollableColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.title]=\"getCellTitle(col, item.row[col.field])\"\n [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\"\n [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\" [editable]=\"isColEditable(col)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\"\n [treeCell]=\"isTreeCell(col)\" [treeLevel]=\"treeRowLevel(item)\"\n [treeExpandable]=\"treeRowExpandable(item)\" [treeExpanded]=\"treeRowExpanded(item)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class]=\"getCellClass(col, item.row[col.field], item.row)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\"\n (startEdit)=\"onStartEdit(item.originalIndex, ci)\" (draftChange)=\"onDraftChange($event)\" (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\"\n (treeToggle)=\"onTreeToggle(item)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-add-row-label\" (click)=\"onActivateAddRow()\">\n <span class=\"ag-add-row-icon\">+</span> {{ localeText().addRow }}\n </div>\n } @else if (item === 'ghost') {\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-ghost-cell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\">{{\n getGhostCellDisplay(col) }}</div>\n }\n } @else {\n <div class=\"ag-group-header-content\" [style.min-width.px]=\"scrollableTotalWidth()\">\n <div class=\"ag-group-header-main\"\n (click)=\"onGroupHeaderClick(item.groupLabel); $event.stopPropagation()\">\n <span class=\"ag-group-icon\" [class.ag-group-icon--expanded]=\"!item.collapsed\">\u25B6</span>\n <span class=\"ag-group-label\">{{ item.groupLabel }}</span>\n @if (groupActions().length > 0) {\n <button class=\"ag-group-actions-btn\" [title]=\"localeText().actions\"\n (click)=\"openGroupActionsMenu($event, item.groupLabel)\">\u22EE</button>\n }\n <span class=\"ag-group-count\">{{ item.count }}</span>\n @if (getGroupDescription(item.groupLabel); as desc) {\n <span class=\"ag-group-description\">{{ desc }}</span>\n }\n @if (item.aggregates; as aggs) {\n <span class=\"ag-group-aggregates\">\n @for (col of visibleColDefs(); track col.field) {\n @if (hasAggregate(col) && aggs[col.field] !== undefined) {\n <span class=\"ag-group-aggregate\">\n <span class=\"ag-group-aggregate-col\">{{ col.header }}</span>\n <span class=\"ag-group-aggregate-op\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, aggs[col.field]) }}\n </span>\n }\n }\n </span>\n }\n </div>\n </div>\n }\n @if (isDataRowItem(item) && isRowPendingDelete(item.originalIndex)) {\n <div class=\"ag-delete-confirmation\" role=\"alertdialog\"\n [attr.aria-label]=\"localeText().confirmDeleteRow\"\n [style.left.px]=\"deleteConfirmationLeft()\"\n [style.width.px]=\"deleteConfirmationWidth() || null\"\n (pointerdown)=\"$event.stopPropagation()\" (click)=\"$event.stopPropagation()\">\n <span class=\"ag-delete-confirmation-text\">{{ localeText().confirmDeleteRow }}</span>\n <button class=\"ag-delete-confirmation-btn ag-delete-confirmation-btn--yes\"\n type=\"button\" (click)=\"confirmPendingRowDelete()\">{{ localeText().confirmYes }}</button>\n <button class=\"ag-delete-confirmation-btn\" type=\"button\" data-delete-confirm-no\n (click)=\"cancelRowDelete()\">{{ localeText().confirmNo }}</button>\n </div>\n }\n </div>\n </cdk-virtual-scroll-viewport>\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\"\n [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"scrollableGridTemplateColumns()\"\n [style.min-width.px]=\"scrollableTotalWidth()\">\n @for (col of scrollableColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n @if (hasRightPinnedPane()) {\n <div class=\"ag-pinned-pane ag-pinned-pane--right\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @if (hasHeaderGroups()) {\n <div class=\"ag-header-groups\" role=\"row\" aria-rowindex=\"1\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @for (run of rightHeaderGroupRuns(); track run.key) {\n <div class=\"ag-header-group-cell\" role=\"columnheader\"\n [attr.data-header-group]=\"run.id\"\n [class.ag-header-group-cell--empty]=\"!run.id\"\n [class.ag-header-group-cell--locked]=\"isHeaderGroupLocked(run.fields)\"\n [class.ag-header-group-cell--dragging]=\"isHeaderGroupDragging(run.fields)\"\n [style.grid-column]=\"'span ' + run.span\"\n (pointerdown)=\"run.id && onHeaderGroupPointerDown($event, run.fields, run.label)\">\n {{ run.label }}\n </div>\n }\n </div>\n }\n <div class=\"ag-header\" role=\"row\" [attr.aria-rowindex]=\"headerRowCount()\"\n [class.ag-header--with-filters]=\"hasFilterableColumns()\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\" [style.width.px]=\"rightPinnedPaneWidth()\">\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-header-cell ag-header-cell--pinned\" role=\"columnheader\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [attr.aria-sort]=\"getSort(col.field) === 'asc' ? 'ascending' : getSort(col.field) === 'desc' ? 'descending' : 'none'\"\n [class.ag-header-cell--filtered]=\"hasActiveFilter(col.field)\"\n [class.ag-header-cell--dragging]=\"isColDragging(col.field)\"\n [class.ag-header-cell--drop-before]=\"getColDropSide(col.field) === 'before'\"\n [class.ag-header-cell--drop-after]=\"getColDropSide(col.field) === 'after'\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-header-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\" [attr.data-col-field]=\"col.field\"\n (pointerdown)=\"onColHeaderPointerDown($event, col.field)\">\n <div class=\"ag-header-cell-top\">\n <span class=\"ag-header-cell-label\">{{ col.header }}</span>\n @if (getSort(col.field); as dir) {\n <span class=\"ag-sort-badge\">{{ dir === 'asc' ? '\u2191' : '\u2193' }}@if (hasMultiSort()) {<sup\n class=\"ag-sort-priority\">{{ getSortPriority(col.field) }}</sup>}</span>\n }\n @if (isGroupedByField(col.field)) {\n <span class=\"ag-sort-badge\">'\u229F '</span>\n }\n <button class=\"ag-header-menu-btn\" [class.ag-header-menu-btn--active]=\"hasActiveFilter(col.field)\"\n [title]=\"localeText().columnMenu\" [attr.aria-label]=\"localeText().columnMenu + ': ' + col.header\"\n [attr.aria-expanded]=\"filterMenu()?.field === col.field\" aria-haspopup=\"menu\"\n (click)=\"openFilterMenu($event, col.field)\"\n (pointerdown)=\"$event.stopPropagation()\">\u25BE</button>\n </div>\n @if (hasFilterableColumns()) {\n <div class=\"ag-header-cell-filter\">\n @if (col.filterable) {\n <input class=\"ag-filter-input\" [value]=\"getTextFilter(col.field)\"\n (input)=\"onTextFilterChange($event, col.field)\" (click)=\"$event.stopPropagation()\"\n [placeholder]=\"localeText().filterPlaceholder\"\n [attr.aria-label]=\"localeText().filterPlaceholder + ' ' + col.header\" />\n }\n </div>\n }\n <div class=\"ag-resize-handle\" role=\"separator\" [attr.tabindex]=\"col.locked ? -1 : 0\"\n aria-orientation=\"vertical\" [attr.aria-disabled]=\"col.locked ? 'true' : null\"\n [attr.aria-label]=\"localeText().resizeColumn + ': ' + col.header\"\n [attr.aria-valuenow]=\"getColumnWidth(col)\" (keydown)=\"onResizeKeyDown($event, col)\"\n (mousedown)=\"onResizeStart($event, col)\"\n (pointerdown)=\"$event.stopPropagation()\" (dblclick)=\"onAutosizeColumn($event, col)\"></div>\n </div>\n }\n </div>\n\n <cdk-virtual-scroll-viewport #rightPinnedViewport class=\"ag-body ag-pinned-body ag-right-pinned-body\"\n [itemSize]=\"rowHeight()\" [style.width.px]=\"rightPinnedPaneWidth()\" (scroll)=\"onRightPinnedBodyScroll()\">\n <div *cdkVirtualFor=\"let item of displayItems(); let di = index; trackBy: trackByItem\" role=\"row\"\n [attr.aria-rowindex]=\"di + headerRowCount() + 1\"\n [attr.aria-selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex) ? 'true' : null\"\n [class.ag-row]=\"!isGroupHeaderItem(item)\" [class.ag-add-row]=\"item === null\"\n [class.ag-ghost-row]=\"item === 'ghost'\" [class.ag-group-header-row]=\"isGroupHeaderItem(item)\"\n [class.ag-row--odd]=\"isDataRowItem(item) && dataRowIsOdd().get(item.originalIndex) === true\"\n [class.ag-row--selected]=\"isDataRowItem(item) && isRowSelected(item.originalIndex)\"\n [class.ag-row--marked]=\"isDataRowItem(item) && isRowMarked(item.originalIndex)\"\n [class.ag-row--pending-delete]=\"isDataRowItem(item) && isRowPendingDelete(item.originalIndex)\"\n [style.height.px]=\"rowHeight()\"\n [style.grid-template-columns]=\"isDataRowItem(item) || item === 'ghost' ? rightGridTemplateColumns() : null\">\n @if (isDataRowItem(item)) {\n @for (col of rightPinnedColDefs(); track col.field) {\n @let ci = getVisibleColIndex(col.field);\n <agrid-cell [col]=\"col\" [rowIndex]=\"item.originalIndex\" [colIndex]=\"ci\" [value]=\"item.row[col.field]\"\n [row]=\"item.row\" [locale]=\"locale()\" [attr.title]=\"getCellTitle(col, item.row[col.field])\"\n [attr.data-cell-row]=\"item.originalIndex\" [attr.data-cell-col]=\"ci\"\n [attr.data-col-field]=\"col.field\"\n [attr.aria-colindex]=\"getAriaColIndex(ci)\"\n [attr.aria-selected]=\"isSelected(item.originalIndex, ci) || isRangeSelected(item.originalIndex, ci)\"\n [selected]=\"isSelected(item.originalIndex, ci)\" [editing]=\"isEditing(item.originalIndex, ci)\" [editable]=\"isColEditable(col)\" [error]=\"cellValidationError(item.originalIndex, ci)\"\n [seedChar]=\"getSeedChar(item.originalIndex, ci)\"\n [treeCell]=\"isTreeCell(col)\" [treeLevel]=\"treeRowLevel(item)\"\n [treeExpandable]=\"treeRowExpandable(item)\" [treeExpanded]=\"treeRowExpanded(item)\"\n [class]=\"getCellClass(col, item.row[col.field], item.row)\"\n [class.ag-cell--range-selected]=\"isRangeSelected(item.originalIndex, ci)\"\n [class.ag-cell--fill-preview]=\"isFillPreviewCell(item.originalIndex, ci)\"\n [class.ag-cell--fill-handle]=\"isFillHandleCell(item.originalIndex, ci)\"\n [class.ag-cell--changed]=\"isCellChanged(item.originalIndex, col.field)\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--find-match]=\"isFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--find-active]=\"isActiveFindMatchCell(item.originalIndex, ci)\"\n [class.ag-cell--pinned]=\"true\" [class.ag-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\"\n (pointerdown)=\"onCellPointerDown($event, item.originalIndex, ci)\"\n (activate)=\"onActivate(item.originalIndex, ci, $event)\" (startEdit)=\"onStartEdit(item.originalIndex, ci)\"\n (draftChange)=\"onDraftChange($event)\" (booleanToggle)=\"onBooleanToggle(item.originalIndex, ci, $event)\" (treeToggle)=\"onTreeToggle(item)\"\n (contextmenu)=\"onCellContextMenu($event, item.originalIndex, ci, col, item.row)\" />\n }\n } @else if (item === null) {\n <div class=\"ag-pinned-row-spacer\"></div>\n } @else if (item === 'ghost') {\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-ghost-cell ag-cell--pinned\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [class.ag-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\">{{ getGhostCellDisplay(col) }}</div>\n }\n } @else {\n <div class=\"ag-pinned-group-spacer\"></div>\n }\n </div>\n </cdk-virtual-scroll-viewport>\n\n @if (showFooter()) {\n <div class=\"ag-footer\" role=\"row\"\n [attr.aria-rowindex]=\"displayItems().length + headerRowCount() + 1\"\n [style.grid-template-columns]=\"rightGridTemplateColumns()\">\n @for (col of rightPinnedColDefs(); track col.field) {\n <div class=\"ag-footer-cell\" role=\"gridcell\"\n [class.ag-column-reorder-item--dragging]=\"isColDragging(col.field)\"\n [style.transform]=\"'translateX(' + getColReorderOffset(col.field) + 'px)'\"\n [attr.aria-colindex]=\"getAriaColIndex(getVisibleColIndex(col.field))\"\n [class.ag-footer-cell--pinned-first]=\"isFirstRightPinnedColumn(col.field)\">\n @if (hasAggregate(col)) {\n <span class=\"ag-footer-label\">{{ getAggregateLabel(col) }}</span>\n {{ getFooterDisplay(col, footerValues()[col.field]) }}\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n\n @if (loading()) {\n <div class=\"ag-state-overlay\" role=\"status\" aria-live=\"polite\">{{ localeText().loading }}</div>\n } @else if (isEmpty()) {\n <div class=\"ag-state-overlay\" role=\"status\" aria-live=\"polite\">{{ emptyTextLabel() }}</div>\n }\n\n @if (showSidebar()) {\n <agrid-sidebar\n [open]=\"sidebarOpen()\"\n [activeTab]=\"sidebarTab()\"\n [columns]=\"colDefs()\"\n [headerGroups]=\"headerGroups()\"\n [row]=\"sidebarRow()\"\n [hiddenColumns]=\"sidebarHiddenColumns()\"\n [locale]=\"locale()\"\n [localeText]=\"localeText()\"\n [readonlyGrid]=\"readonlyGrid()\"\n [useSidebarEditor]=\"useSidebarEditor()\"\n [errors]=\"sidebarValidationErrors()\"\n (close)=\"toggleSidebar()\"\n (tabChange)=\"onSidebarStripClick($event)\"\n (toggleColumn)=\"onSidebarToggleColumn($event)\"\n (toggleColumnGroup)=\"onSidebarToggleColumnGroup($event.fields, $event.visible)\"\n (detailEdit)=\"onSidebarDetailEdit($event)\"\n (save)=\"onSidebarDetailSave($event)\"\n />\n }\n </div><!-- /.ag-main-area -->\n\n @if (showPagination()) {\n <nav class=\"ag-pagination\" [attr.aria-label]=\"localeText().pagination\">\n <button class=\"ag-page-btn\" [attr.aria-label]=\"localeText().firstPage\"\n [disabled]=\"control()!.currentPage() <= 1\" (click)=\"goToFirstPage()\">\u00AB</button>\n <button class=\"ag-page-btn\" [attr.aria-label]=\"localeText().previous\"\n [disabled]=\"control()!.currentPage() <= 1\" (click)=\"goToPrevPage()\">\u2039</button>\n <span class=\"ag-page-info\" aria-live=\"polite\">{{ control()!.currentPage() }} / {{ totalPages() }}</span>\n <button class=\"ag-page-btn\" [disabled]=\"control()!.currentPage() >= totalPages()\"\n [attr.aria-label]=\"localeText().next\"\n (click)=\"goToNextPage()\">\u203A</button>\n <button class=\"ag-page-btn\" [disabled]=\"control()!.currentPage() >= totalPages()\"\n [attr.aria-label]=\"localeText().lastPage\"\n (click)=\"goToLastPage()\">\u00BB</button>\n <span class=\"ag-page-count\">{{ localeText().rows(filteredRowCount()) }}</span>\n </nav>\n }\n\n @if (findOpen()) {\n <agrid-find-panel [query]=\"findQuery()\" [matchCount]=\"findMatches().length\" [activeIndex]=\"findActiveIndex()\"\n [localeText]=\"localeText()\" (queryChange)=\"onFindInput($event)\" (previous)=\"goToFindMatch(-1)\"\n (next)=\"goToFindMatch(1)\" (close)=\"closeFind()\" />\n }\n\n <!-- Row context menu -->\n @if (contextMenu(); as menu) {\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n <button class=\"ag-context-item ag-context-item--danger\" role=\"menuitem\" (click)=\"deleteRow(menu.rowIndex)\">\n {{ localeText().deleteRow }}\n </button>\n </div>\n }\n\n <!-- Cell context menu -->\n @if (cellContextMenuState(); as menu) {\n @let col = getColDef(menu.field)!;\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"copyCellToClipboard(menu.rowIndex, col)\">{{ localeText().copyCellValue\n }}</button>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"copyRowToClipboard(menu.rowIndex)\">{{ localeText().copyRow }}</button>\n @if (allowAddRows() && !readonlyGrid()) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"insertRowAt(menu.rowIndex)\">{{ localeText().insertRowAbove }}</button>\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"insertRowAt(menu.rowIndex + 1)\">{{ localeText().insertRowBelow }}</button>\n }\n @if (!readonlyGrid()) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n <button class=\"ag-context-item ag-context-item--danger\" role=\"menuitem\"\n (click)=\"deleteRow(menu.rowIndex)\">{{ localeText().deleteRow }}</button>\n }\n @if (cellMenuItems().length) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n @for (item of cellMenuItems(); track $index) {\n @if (item === null) {\n <div class=\"ag-context-separator\" role=\"separator\"></div>\n } @else {\n <button class=\"ag-context-item\" role=\"menuitem\" [class.ag-context-item--danger]=\"item.danger\" [disabled]=\"item.disabled\"\n (click)=\"runCellMenuItem(item, menu)\">{{\n item.label }}</button>\n }\n }\n }\n </div>\n }\n\n <!-- Group actions menu -->\n @if (groupActionsMenu(); as menu) {\n <div class=\"ag-context-menu\" role=\"menu\" [style.left.px]=\"menu.x\" [style.top.px]=\"menu.y\"\n (click)=\"$event.stopPropagation()\">\n @for (action of groupActions(); track action.label) {\n <button class=\"ag-context-item\" role=\"menuitem\" (click)=\"onGroupAction(action, menu.label)\">\n {{ action.label }}\n </button>\n }\n </div>\n }\n\n <!-- Filter dropdown -->\n @if (filterMenu(); as menu) {\n <agrid-column-menu [x]=\"menu.x\" [y]=\"menu.y\" [header]=\"getColDef(menu.field)?.header ?? menu.field\"\n [sortDir]=\"getSort(menu.field)\" [sortable]=\"sortOption() !== 'none'\"\n [showColumnActions]=\"!!control()\" [pinned]=\"getColumnPinState(menu.field)\"\n [groupable]=\"!!getColDef(menu.field)?.groupable\" [grouped]=\"isGroupedByField(menu.field)\"\n [filterable]=\"!!getColDef(menu.field)?.filterable\" [showValueFilter]=\"!serverSideFiltering()\"\n [filterType]=\"getMenuFilterType(menu.field)\"\n [operator]=\"getMenuOperator(menu.field)\" [operand]=\"getMenuOperand(menu.field)\"\n [operand2]=\"getMenuOperand2(menu.field)\"\n (operatorChange)=\"onMenuOperatorChange(menu.field, $event)\"\n (operandChange)=\"onMenuOperandChange(menu.field, $event)\"\n (operand2Change)=\"onMenuOperand2Change(menu.field, $event)\"\n [search]=\"filterMenuSearch()\"\n [allSelected]=\"isMenuAllSelected(menu.field)\"\n [valueItems]=\"serverSideFiltering() || !getColDef(menu.field)?.filterable ? [] : columnMenuValueItems()\"\n [localeText]=\"localeText()\"\n [sortPriority]=\"getSortPriority(menu.field)\" [hasMultiSort]=\"hasMultiSort()\" (sort)=\"onMenuSort(menu.field, $event)\"\n (resetSort)=\"onMenuResetSort(menu.field, $event)\" (autosize)=\"onMenuAutosizeColumn(menu.field)\"\n (togglePin)=\"onMenuTogglePin(menu.field)\" (togglePinRight)=\"onMenuTogglePinRight(menu.field)\"\n (hide)=\"onMenuHideColumn(menu.field)\" (toggleGroup)=\"onMenuToggleGroupBy(menu.field)\"\n (clearFilter)=\"onMenuClearFilter(menu.field)\" (clearAll)=\"onMenuClearAll()\"\n (searchChange)=\"onFilterMenuSearch($event)\" (toggleAll)=\"onMenuToggleAll(menu.field)\"\n (toggleValue)=\"onMenuToggleValue(menu.field, $event)\" [aggregate]=\"getEffectiveAggregate(getColDef(menu.field)!)\"\n (setAggregate)=\"onMenuSetAggregate(menu.field, $event)\" />\n }\n\n @if (columnDragPreview(); as preview) {\n <div class=\"ag-column-drag-preview\"\n [style.left.px]=\"preview.x\" [style.top.px]=\"preview.y\"\n [style.width.px]=\"preview.width\" [style.height.px]=\"preview.height\">\n <span>{{ preview.label }}</span>\n </div>\n }\n</div>\n", styles: ["@layer agrid-defaults{:host{--agrid-color-text: #24292f;--agrid-color-text-muted: #57606a;--agrid-color-accent: #1a73e8;--agrid-color-accent-subtle: #e8f0fe;--agrid-color-accent-fg: #1558b0;--agrid-color-accent-border: #c8d8f8;--agrid-color-danger: #d1242f;--agrid-color-danger-subtle: #fff1f0;--agrid-color-border: #d0d7de;--agrid-color-bg: #ffffff;--agrid-color-bg-subtle: #fafbfc;--agrid-color-bg-muted: #f6f8fa;--agrid-color-shadow: rgba(140, 149, 159, .2);--agrid-color-bg-stripe: #f0f2f5;--agrid-color-cell-changed: #f59e0b;--agrid-color-row-marked: #fff8c5}}:host{display:flex;flex-direction:column;min-height:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:13px;color:var(--agrid-color-text)}.ag-wrapper{position:relative;display:flex;flex-direction:column;flex:1;min-height:0;border:1px solid var(--agrid-color-border);overflow:hidden;outline:none;border-radius:4px}.ag-state-overlay{position:absolute;inset:34px 0 0;display:flex;align-items:center;justify-content:center;color:var(--agrid-color-text-muted);background:color-mix(in srgb,var(--agrid-color-bg) 78%,transparent);pointer-events:none;z-index:3}.ag-header{display:grid;flex-shrink:0;background:var(--agrid-color-bg-muted);border-bottom:2px solid var(--agrid-color-border)}.ag-header-groups{display:grid;flex-shrink:0;height:28px;background:color-mix(in srgb,var(--agrid-color-bg-muted) 72%,var(--agrid-color-bg));border-bottom:1px solid var(--agrid-color-border)}.ag-header-group-cell{display:flex;align-items:center;justify-content:center;min-width:0;padding:0 8px;overflow:hidden;border-right:1px solid var(--agrid-color-border);box-sizing:border-box;color:var(--agrid-color-text);font-size:12px;font-weight:600;text-overflow:ellipsis;white-space:nowrap;cursor:grab;-webkit-user-select:none;user-select:none;transition:opacity .1s ease}.ag-header-group-cell:last-child{border-right:none}.ag-header-group-cell--empty,.ag-header-group-cell--locked{cursor:default}.ag-header-group-cell--dragging{opacity:.12}.ag-header-cell{position:relative;display:flex;align-items:center;font-weight:600;border-right:1px solid var(--agrid-color-border);overflow:hidden;white-space:nowrap;-webkit-user-select:none;user-select:none;box-sizing:border-box}.ag-header-cell:last-child{border-right:none}.ag-header-cell-top{display:flex;align-items:center;flex:1;min-width:0;padding:0 6px;height:32px;overflow:hidden;white-space:nowrap}.ag-header-cell-label{overflow:hidden;text-overflow:ellipsis;flex:1}.ag-header--with-filters .ag-header-cell{flex-direction:column;align-items:stretch;height:auto;white-space:normal}.ag-header--with-filters .ag-header-cell-top{flex:0 0 32px}.ag-header-cell-filter{height:28px;display:flex;align-items:center;padding:0 2px;border-top:1px solid var(--agrid-color-border);background:var(--agrid-color-bg)}.ag-header-cell{cursor:grab;transition:transform .16s cubic-bezier(.2,0,0,1),opacity .1s ease;will-change:transform}.ag-row>agrid-cell,.ag-ghost-cell,.ag-footer-cell{transition:transform .16s cubic-bezier(.2,0,0,1),opacity .1s ease;will-change:transform}.ag-header-cell--dragging{opacity:.12;cursor:grabbing}.ag-column-reorder-item--dragging{opacity:.12}.ag-header-cell--drop-before,.ag-header-cell--drop-after{z-index:1}.ag-column-drag-preview{position:fixed;z-index:1000;display:flex;align-items:flex-start;padding:8px 10px;border:1px solid var(--agrid-color-accent);border-radius:5px;color:var(--agrid-color-text);background:color-mix(in srgb,var(--agrid-color-bg) 94%,var(--agrid-color-accent));box-shadow:0 10px 28px var(--agrid-color-shadow);font-weight:600;line-height:16px;box-sizing:border-box;pointer-events:none;opacity:.96}.ag-sort-badge{font-size:11px;color:var(--agrid-color-accent);flex-shrink:0;line-height:1}.ag-sort-priority{font-size:9px;vertical-align:super;opacity:.75}.ag-resize-handle{position:absolute;top:0;right:0;width:5px;height:100%;cursor:col-resize;z-index:1}.ag-resize-handle:hover{background:var(--agrid-color-accent);opacity:.5}.ag-filter-input{flex:1;min-width:0;height:20px;border:1px solid var(--agrid-color-border);border-radius:3px;outline:none;font:inherit;font-size:12px;padding:0 4px;background:var(--agrid-color-bg)}.ag-filter-input:focus{border-color:var(--agrid-color-accent)}.ag-header-cell--filtered .ag-header-cell-label:after{content:\" \\25be\";font-size:9px;color:var(--agrid-color-accent)}.ag-main-area{display:flex;flex:1;min-height:0;overflow:hidden}.ag-grid-split{display:flex;flex:1;min-width:0;min-height:0;overflow:hidden}.ag-pinned-pane{flex-shrink:0;min-height:0;display:flex;flex-direction:column;overflow:hidden;border-right:1px solid var(--agrid-color-border);background:var(--agrid-color-bg)}.ag-scroll-pane{flex:1;min-width:0;min-height:0;display:flex}.ag-horizontal-scroll{flex:1;min-width:0;min-height:0;overflow-x:auto;overflow-y:hidden;display:flex;flex-direction:column}.ag-body{flex:1;overflow-x:clip;overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-horizontal-scroll{scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.18) transparent}.ag-body::-webkit-scrollbar,.ag-horizontal-scroll::-webkit-scrollbar{width:8px;height:8px}.ag-body::-webkit-scrollbar-track,.ag-horizontal-scroll::-webkit-scrollbar-track{background:transparent}.ag-body::-webkit-scrollbar-thumb,.ag-horizontal-scroll::-webkit-scrollbar-thumb{background:#0000002e;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-body::-webkit-scrollbar-thumb:hover,.ag-horizontal-scroll::-webkit-scrollbar-thumb:hover{background:#00000052;border-radius:10px;border:2px solid transparent;background-clip:padding-box}.ag-pinned-body{overflow:hidden}.ag-right-pinned-body{overflow-y:auto}.ag-has-right-pane .ag-body:not(.ag-pinned-body){scrollbar-width:none}.ag-has-right-pane .ag-body:not(.ag-pinned-body)::-webkit-scrollbar{display:none}.ag-row{display:grid;position:relative}.ag-row--pending-delete>:not(.ag-delete-confirmation){opacity:.2;pointer-events:none;transition:opacity .14s ease}.ag-delete-confirmation{position:absolute;top:0;bottom:0;z-index:7;display:flex;align-items:center;justify-content:center;gap:8px;min-width:min(100%,320px);padding:2px 12px;box-sizing:border-box;color:var(--agrid-color-text);background:color-mix(in srgb,var(--agrid-color-danger-subtle) 88%,transparent);border-block:1px solid color-mix(in srgb,var(--agrid-color-danger) 35%,transparent)}.ag-delete-confirmation-text{font-weight:600;white-space:nowrap}.ag-delete-confirmation-btn{min-width:42px;padding:3px 9px;border:1px solid var(--agrid-color-border);border-radius:4px;color:var(--agrid-color-text);background:var(--agrid-color-bg);font:inherit;cursor:pointer}.ag-delete-confirmation-btn:hover,.ag-delete-confirmation-btn:focus-visible{border-color:var(--agrid-color-accent);outline:none}.ag-delete-confirmation-btn--yes{color:#fff;border-color:var(--agrid-color-danger);background:var(--agrid-color-danger)}.ag-delete-confirmation-btn--yes:hover,.ag-delete-confirmation-btn--yes:focus-visible{border-color:color-mix(in srgb,var(--agrid-color-danger) 75%,#000);background:color-mix(in srgb,var(--agrid-color-danger) 85%,#000)}:host(.ag-zebra) .ag-row--odd agrid-cell:not(.editing){background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd .ag-cell--pinned{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd .ag-control-cell{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd:hover agrid-cell:not(.editing){background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd:hover .ag-cell--pinned,:host(.ag-zebra) .ag-row--odd:hover .ag-control-cell{background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd.ag-row--selected agrid-cell:not(.editing){background:var(--agrid-color-accent-subtle)}:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-cell--pinned,:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-control-cell{background:var(--agrid-color-accent-subtle)}:host(.ag-zebra) .ag-row--odd .ag-cell--pinned-first{background:var(--agrid-color-bg-stripe)}:host(.ag-zebra) .ag-row--odd:hover .ag-cell--pinned-first{background:var(--agrid-color-bg-muted)}:host(.ag-zebra) .ag-row--odd.ag-row--selected .ag-cell--pinned-first{background:var(--agrid-color-accent-subtle)}.ag-row:hover agrid-cell:not(.editing){background:var(--agrid-color-bg-muted)}.ag-row--selected agrid-cell:not(.editing){background:var(--agrid-color-accent-subtle)}.ag-row--marked agrid-cell:not(.editing),.ag-row--marked .ag-control-cell{background:var(--agrid-color-row-marked)}.ag-row--marked{box-shadow:inset 3px 0 #bf8700}.ag-row--selected.ag-row--marked{box-shadow:inset 3px 0 0 var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--range-selected:not(.editing){background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1px var(--agrid-color-accent-border)}.ag-row agrid-cell.ag-cell--find-match:not(.editing){background:#fff7cc}.ag-row agrid-cell.ag-cell--find-active:not(.editing){background:#ffe58a;box-shadow:inset 0 0 0 2px #b7791f}.ag-row agrid-cell.ag-cell--range-selected.selected:not(.editing){box-shadow:inset 0 0 0 2px var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--fill-preview:not(.editing){background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1px var(--agrid-color-accent)}.ag-row agrid-cell.ag-cell--fill-handle:not(.editing){position:relative}.ag-row agrid-cell.ag-cell--fill-handle:not(.editing):after{content:\"\";position:absolute;right:1px;bottom:1px;width:6px;height:6px;background:var(--agrid-color-accent);border:1px solid var(--agrid-color-bg);box-sizing:border-box;cursor:crosshair;z-index:4}.ag-row--selected .ag-control-cell{background:var(--agrid-color-accent-subtle)}.ag-row--selected{box-shadow:inset 3px 0 0 var(--agrid-color-accent)}.ag-control-header{z-index:3;border-right:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted)}.ag-control-header .ag-header-cell-filter{background:var(--agrid-color-bg-subtle)}.ag-control-cell{z-index:2;border-right:1px solid var(--agrid-color-border);border-bottom:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-subtle);cursor:context-menu;display:flex;align-items:center;justify-content:center;gap:6px;box-sizing:border-box;flex-direction:row-reverse}.ag-control-cell:after{content:\"\\22ee\";font-size:11px;color:var(--agrid-color-border);line-height:1}.ag-control-cell:hover:after{color:var(--agrid-color-text-muted)}.ag-control-cell--reorder{cursor:grab}.ag-control-cell--reorder:after{content:\"\\283f\";font-size:13px;color:var(--agrid-color-text)}.ag-control-cell--reorder:hover:after{color:var(--agrid-color-text-muted)}.ag-control-cell--reorder:active{cursor:grabbing}.ag-row-marker{position:relative;z-index:1;width:14px;height:14px;margin:0;accent-color:var(--agrid-color-accent);cursor:pointer}.ag-ghost-row{background:var(--agrid-color-accent-subtle);box-shadow:inset 0 0 0 1.5px var(--agrid-color-accent);pointer-events:none;animation:ag-ghost-in .1s ease}@keyframes ag-ghost-in{0%{opacity:0}to{opacity:1}}.ag-ghost-handle:after{color:var(--agrid-color-accent)!important}.ag-ghost-cell{display:flex;align-items:center;padding:0 6px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;border-right:1px solid var(--agrid-color-accent-border);color:var(--agrid-color-accent-fg);font-size:13px;box-sizing:border-box;-webkit-user-select:none;user-select:none}.ag-ghost-cell:last-child{border-right:none}.ag-pinned-row-spacer,.ag-pinned-group-spacer{height:100%;border-bottom:1px solid var(--agrid-color-border);box-sizing:border-box}.ag-pinned-group-spacer{background:var(--agrid-color-bg-muted)}.ag-context-menu{position:fixed;z-index:1000;background:var(--agrid-color-bg-subtle);border:1px solid var(--agrid-color-border);border-radius:6px;box-shadow:0 8px 24px var(--agrid-color-shadow);min-width:160px;padding:4px 0;font-size:13px}.ag-context-item{display:block;width:100%;padding:6px 16px;text-align:left;background:none;border:none;cursor:pointer;color:var(--agrid-color-text);font:inherit}.ag-context-item:hover{background:var(--agrid-color-bg-muted)}.ag-context-separator{height:1px;background:var(--agrid-color-border);margin:3px 0}.ag-context-item--danger{color:var(--agrid-color-danger)}.ag-context-item--danger:hover{background:var(--agrid-color-danger-subtle)}.ag-group-header-row{display:flex;align-items:stretch;background:var(--agrid-color-bg-muted);border-bottom:1px solid var(--agrid-color-border);overflow:hidden}.ag-group-header-content{display:flex;align-items:stretch;height:100%;width:100%;-webkit-user-select:none;user-select:none}.ag-group-header-main{display:flex;align-items:center;gap:6px;padding:0 10px;flex:1;min-width:0;cursor:pointer}.ag-group-header-main:hover{background:var(--agrid-color-bg-subtle)}.ag-group-description{font-size:11px;color:var(--agrid-color-text-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ag-group-actions-btn{display:flex;align-items:center;justify-content:center;width:22px;height:22px;border:none;background:none;border-radius:3px;cursor:pointer;color:var(--agrid-color-text-muted);font-size:14px;padding:0}.ag-group-actions-btn:hover{background:var(--agrid-color-bg-muted);color:var(--agrid-color-text)}.ag-group-icon{font-size:9px;color:var(--agrid-color-text-muted);line-height:1;display:inline-block;transition:transform .15s ease}.ag-group-icon--expanded{transform:rotate(90deg)}.ag-group-label{font-weight:600;font-size:12px;color:var(--agrid-color-text)}.ag-group-count{font-size:11px;color:var(--agrid-color-text-muted);background:var(--agrid-color-bg);border:1px solid var(--agrid-color-border);border-radius:10px;padding:0 7px;line-height:16px}.ag-group-aggregates{display:inline-flex;align-items:center;gap:10px;margin-left:4px;flex-wrap:wrap}.ag-group-aggregate{font-size:11px;color:var(--agrid-color-text);white-space:nowrap}.ag-group-aggregate-col{color:var(--agrid-color-text-muted);margin-right:4px}.ag-group-aggregate-op{color:var(--agrid-color-text-muted);margin-right:3px}.ag-footer{display:grid;flex-shrink:0;background:var(--agrid-color-bg-muted);border-top:2px solid var(--agrid-color-border)}.ag-footer-cell{display:flex;align-items:center;gap:4px;padding:0 6px;height:30px;border-right:1px solid var(--agrid-color-border);box-sizing:border-box;overflow:hidden;white-space:nowrap;font-weight:600;font-size:12px;color:var(--agrid-color-text)}.ag-footer-cell:last-child{border-right:none}.ag-footer-cell--control{background:var(--agrid-color-bg-subtle);border-right:1px solid var(--agrid-color-border)}.ag-footer-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-footer-label{font-size:10px;font-weight:400;color:var(--agrid-color-text-muted);flex-shrink:0}.ag-pagination{display:flex;align-items:center;gap:4px;padding:0 10px;height:34px;flex-shrink:0;border-top:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-muted);font-size:12px;color:var(--agrid-color-text-muted)}.ag-page-btn{display:flex;align-items:center;justify-content:center;width:24px;height:22px;border:1px solid var(--agrid-color-border);border-radius:3px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font-size:13px;cursor:pointer;padding:0;line-height:1}.ag-page-btn:hover:not(:disabled){background:var(--agrid-color-bg-muted);border-color:var(--agrid-color-text-muted)}.ag-page-btn:disabled{opacity:.35;cursor:default}.ag-page-info{padding:0 6px;font-weight:500;color:var(--agrid-color-text);min-width:48px;text-align:center}.ag-page-count{margin-left:auto;color:var(--agrid-color-text-muted)}.ag-add-row{border-bottom:1px dashed var(--agrid-color-border);color:var(--agrid-color-text-muted);cursor:pointer}.ag-add-row:hover .ag-add-row-label{background:var(--agrid-color-bg-muted)}.ag-add-row--selected .ag-add-row-label{outline:2px solid var(--agrid-color-accent);outline-offset:-2px}.ag-add-row-label{display:flex;align-items:center;gap:4px;height:100%;padding:0 6px;font-size:12px;-webkit-user-select:none;user-select:none}.ag-add-row-icon{font-size:16px;line-height:1;color:var(--agrid-color-text-muted)}.ag-pinned-pane--right{border-left:1px solid var(--agrid-color-border);border-right:none;box-shadow:-3px 0 6px -2px var(--agrid-color-shadow)}.ag-header-cell--pinned{background:var(--agrid-color-bg-muted)}.ag-header-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-header-cell--pinned-first{box-shadow:none}.ag-cell--pinned{background:var(--agrid-color-bg)}.ag-cell--pinned-last{box-shadow:inset -1px 0 0 var(--agrid-color-border),3px 0 6px -2px var(--agrid-color-shadow)}.ag-cell--pinned-first{box-shadow:none}.ag-row:hover .ag-cell--pinned{background:var(--agrid-color-bg-muted)}.ag-row--selected .ag-cell--pinned,.ag-row .ag-cell--pinned.ag-cell--range-selected,.ag-row .ag-cell--pinned.ag-cell--fill-preview{background:var(--agrid-color-accent-subtle)}.ag-row .ag-cell--pinned.ag-cell--find-match{background:#fff7cc}.ag-row .ag-cell--pinned.ag-cell--find-active{background:#ffe58a}.ag-header-menu-btn{flex-shrink:0;width:16px;height:16px;padding:0;margin-right:2px;background:none;border:1px solid transparent;border-radius:3px;cursor:pointer;font-size:10px;color:var(--agrid-color-text-muted);display:flex;align-items:center;justify-content:center;opacity:1;transition:opacity .12s;line-height:1}.ag-header-menu-btn--active{color:var(--agrid-color-accent);border-color:var(--agrid-color-accent-border);background:var(--agrid-color-accent-subtle)}.ag-header-menu-btn:hover{background:var(--agrid-color-bg);border-color:var(--agrid-color-border)}.ag-toolbar{display:flex;align-items:center;padding:6px 8px;border-bottom:1px solid var(--agrid-color-border);background:var(--agrid-color-bg-subtle)}.ag-quick-filter{width:100%;max-width:260px;height:30px;padding:0 10px;border:1px solid var(--agrid-color-border);border-radius:4px;background:var(--agrid-color-bg);color:var(--agrid-color-text);font-size:13px;box-sizing:border-box}.ag-quick-filter:focus{outline:none;border-color:var(--agrid-color-accent-border)}\n"] }]
6008
+ }], ctorParameters: () => [], propDecorators: { provider: [{ type: i0.Input, args: [{ isSignal: true, alias: "provider", required: false }] }], cellEdit: [{ type: i0.Output, args: ["cellEdit"] }], recordEdit: [{ type: i0.Output, args: ["recordEdit"] }], rowRemoved: [{ type: i0.Output, args: ["rowRemoved"] }], prepareAddRecord: [{ type: i0.Output, args: ["prepareAddRecord"] }], rowReorder: [{ type: i0.Output, args: ["rowReorder"] }], rowSelect: [{ type: i0.Output, args: ["rowSelect"] }], rowDoubleClicked: [{ type: i0.Output, args: ["rowDoubleClicked"] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], rowChanged: [{ type: i0.Output, args: ["rowChanged"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], quickFilterChange: [{ type: i0.Output, args: ["quickFilterChange"] }], validationFailed: [{ type: i0.Output, args: ["validationFailed"] }], viewport: [{ type: i0.ViewChild, args: ['scrollViewport', { isSignal: true }] }], pinnedViewport: [{ type: i0.ViewChild, args: ['pinnedViewport', { isSignal: true }] }], rightPinnedViewport: [{ type: i0.ViewChild, args: ['rightPinnedViewport', { isSignal: true }] }], wrapperEl: [{ type: i0.ViewChild, args: ['wrapper', { isSignal: true }] }], horizontalScrollerEl: [{ type: i0.ViewChild, args: ['horizontalScroller', { isSignal: true }] }] } });
5255
6009
 
5256
6010
  /** Width sentinel that makes a column fill the remaining horizontal space. */
5257
6011
  const ColDefAutoSize = -1;