@mozaic-ds/angular 2.0.42 → 2.0.44

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.
@@ -4,7 +4,7 @@ import { RouterLink, RouterLinkActive, RouterLinkWithHref } from '@angular/route
4
4
  import { NgTemplateOutlet, NgClass, NgComponentOutlet, JsonPipe, DOCUMENT } from '@angular/common';
5
5
  import * as i1 from '@angular/forms';
6
6
  import { FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
7
- import { WarningCircle32, Uploading32, CheckCircle32, CrossCircleFilled20, Refresh32, Refresh20, Eye20, Upload24, Cross24, ChevronLeft24, ChevronRight24, ChevronLeft20, ChevronRight20, CrossCircleFilled24, More24, Less24, InfoCircle32, CrossCircle32, Cross20, CrossCircle24, ImageAlt32, ChevronDown24, CheckCircleFilled32, WarningCircleFilled32, CrossCircleFilled32, InfoCircleFilled32, SidebarExpand24, ChevronDown20, InfoCircleFilled24, WarningCircleFilled24, CheckCircleFilled24, ArrowBottomRight24, ArrowTopRight24, StarHalf32, StarFilled32, Star32, StarHalf24, StarFilled24, Star24, StarHalf20, StarFilled20, Star20, Check20, Check24, ArrowBack24, ArrowNext24, HelpCircle24, Menu24, Notification24, Search24, PauseCircle24, PlayCircle24, ChevronUp20, Settings20, ErrorFilled24, Drag20, ListAdd20, ViewGridX420, Filter20, FullscreenEnter20, FullscreenExit20, Download20, CheckCircle24 } from '@mozaic-ds/icons-angular';
7
+ import { WarningCircle32, Uploading32, CheckCircle32, CrossCircleFilled20, Refresh32, Refresh20, Eye20, Upload24, Cross24, ChevronLeft24, ChevronRight24, ChevronLeft20, ChevronRight20, CrossCircleFilled24, More24, Less24, InfoCircle32, CrossCircle32, Cross20, CrossCircle24, ImageAlt32, ChevronDown24, CheckCircleFilled32, WarningCircleFilled32, CrossCircleFilled32, InfoCircleFilled32, SidebarExpand24, ChevronDown20, InfoCircleFilled24, WarningCircleFilled24, CheckCircleFilled24, ArrowBottomRight24, ArrowTopRight24, StarHalf32, StarFilled32, Star32, StarHalf24, StarFilled24, Star24, StarHalf20, StarFilled20, Star20, Check20, Check24, ArrowBack24, ArrowNext24, HelpCircle24, Menu24, Notification24, Search24, PauseCircle24, PlayCircle24, ChevronUp20, Settings20, ErrorFilled24, Drag20, ListAdd20, ViewGridX420, Filter20, FullscreenEnter20, FullscreenExit20, Download20, Keyboard20, CheckCircle24 } from '@mozaic-ds/icons-angular';
8
8
  import { Overlay, OverlayConfig, OverlayPositionBuilder, CdkConnectedOverlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
9
9
  import { CdkPortalOutlet, ComponentPortal } from '@angular/cdk/portal';
10
10
  import { Subject, take, tap, of, firstValueFrom } from 'rxjs';
@@ -5458,6 +5458,19 @@ class GridEngine {
5458
5458
  return this.state.totalItems();
5459
5459
  return this.filteredData().length;
5460
5460
  }, ...(ngDevMode ? [{ debugName: "computedTotalItems" }] : /* istanbul ignore next */ []));
5461
+ /**
5462
+ * Resolves a display row index (as emitted via `DisplayRow.index`) to the
5463
+ * actual index in `sourceData()`. When sort/filter/group is active these do
5464
+ * not match, so we look up the display row's data object and search for it
5465
+ * in sourceData. Returns -1 when the display index is unknown.
5466
+ */
5467
+ displayIndexToSourceIndex(displayIndex) {
5468
+ const rows = this.displayRows();
5469
+ const row = rows.find((r) => r.type === 'data' && r.index === displayIndex);
5470
+ if (!row || row.type !== 'data')
5471
+ return -1;
5472
+ return this.state.sourceData().indexOf(row.data);
5473
+ }
5461
5474
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GridEngine, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
5462
5475
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GridEngine });
5463
5476
  }
@@ -5527,7 +5540,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
5527
5540
  type: Injectable
5528
5541
  }] });
5529
5542
 
5530
- const PASTE_SKIP$1 = Symbol('PASTE_SKIP');
5543
+ const PASTE_SKIP = Symbol('PASTE_SKIP');
5531
5544
  /**
5532
5545
  * Applies a set of cell-level mutations to sourceData and returns the list of
5533
5546
  * actual changes that occurred, so the caller (usually the history engine) can
@@ -5535,8 +5548,23 @@ const PASTE_SKIP$1 = Symbol('PASTE_SKIP');
5535
5548
  */
5536
5549
  class ClipboardEngine {
5537
5550
  state = inject(GridStateManager);
5551
+ gridEngine = inject(GridEngine);
5538
5552
  /** Derived by components (marching-ants outline). */
5539
5553
  cutRange = computed(() => this.state.cutSource(), ...(ngDevMode ? [{ debugName: "cutRange" }] : /* istanbul ignore next */ []));
5554
+ /**
5555
+ * Maps every display row index inside `range` to its actual sourceData
5556
+ * index. Unknown indices (e.g. outside the current page) are skipped so
5557
+ * callers can safely iterate only over rows that exist in sourceData.
5558
+ */
5559
+ resolveRangeSourceIndices(range) {
5560
+ const out = [];
5561
+ for (let r = range.start.row; r <= range.end.row; r++) {
5562
+ const srcIdx = this.gridEngine.displayIndexToSourceIndex(r);
5563
+ if (srcIdx >= 0)
5564
+ out.push(srcIdx);
5565
+ }
5566
+ return out;
5567
+ }
5540
5568
  markCut(range) {
5541
5569
  this.state.cutSource.set(range);
5542
5570
  }
@@ -5550,15 +5578,19 @@ class ClipboardEngine {
5550
5578
  const cols = this.state.visibleColumns();
5551
5579
  const defMap = this.state.columnDefMap();
5552
5580
  const changes = [];
5581
+ const sourceIndices = this.resolveRangeSourceIndices(range);
5582
+ if (sourceIndices.length < 2)
5583
+ return [];
5584
+ const [sourceIdx, ...targetIndices] = sourceIndices;
5553
5585
  this.state.sourceData.update((data) => {
5554
5586
  const updated = [...data];
5555
- const sourceRow = updated[range.start.row];
5587
+ const sourceRow = updated[sourceIdx];
5556
5588
  if (!sourceRow)
5557
5589
  return updated;
5558
- for (let r = range.start.row + 1; r <= range.end.row; r++) {
5559
- if (!updated[r])
5590
+ for (const targetIdx of targetIndices) {
5591
+ if (!updated[targetIdx])
5560
5592
  continue;
5561
- const rowCopy = { ...updated[r] };
5593
+ const rowCopy = { ...updated[targetIdx] };
5562
5594
  let changed = false;
5563
5595
  for (let c = range.start.col; c <= range.end.col; c++) {
5564
5596
  const field = cols[c]?.field;
@@ -5570,18 +5602,18 @@ class ClipboardEngine {
5570
5602
  const sourceValue = def.valueGetter
5571
5603
  ? def.valueGetter(sourceRow)
5572
5604
  : sourceRow[field];
5573
- const coerced = this.coerceAndValidate(field, sourceValue, updated[r]);
5574
- if (coerced === PASTE_SKIP$1)
5605
+ const coerced = this.coerceAndValidate(field, sourceValue, updated[targetIdx]);
5606
+ if (coerced === PASTE_SKIP)
5575
5607
  continue;
5576
- const before = updated[r][field];
5608
+ const before = updated[targetIdx][field];
5577
5609
  if (before === coerced)
5578
5610
  continue;
5579
5611
  rowCopy[field] = coerced;
5580
- changes.push({ rowIndex: r, field, before, after: coerced });
5612
+ changes.push({ rowIndex: targetIdx, field, before, after: coerced });
5581
5613
  changed = true;
5582
5614
  }
5583
5615
  if (changed)
5584
- updated[r] = rowCopy;
5616
+ updated[targetIdx] = rowCopy;
5585
5617
  }
5586
5618
  return updated;
5587
5619
  });
@@ -5600,10 +5632,11 @@ class ClipboardEngine {
5600
5632
  if (!sourceDef)
5601
5633
  return [];
5602
5634
  const changes = [];
5635
+ const sourceIndices = this.resolveRangeSourceIndices(range);
5603
5636
  this.state.sourceData.update((data) => {
5604
5637
  const updated = [...data];
5605
- for (let r = range.start.row; r <= range.end.row; r++) {
5606
- const row = updated[r];
5638
+ for (const sourceIdx of sourceIndices) {
5639
+ const row = updated[sourceIdx];
5607
5640
  if (!row)
5608
5641
  continue;
5609
5642
  const sourceValue = sourceDef.valueGetter
@@ -5619,17 +5652,17 @@ class ClipboardEngine {
5619
5652
  if (!def?.editable)
5620
5653
  continue;
5621
5654
  const coerced = this.coerceAndValidate(field, sourceValue, row);
5622
- if (coerced === PASTE_SKIP$1)
5655
+ if (coerced === PASTE_SKIP)
5623
5656
  continue;
5624
5657
  const before = row[field];
5625
5658
  if (before === coerced)
5626
5659
  continue;
5627
5660
  rowCopy[field] = coerced;
5628
- changes.push({ rowIndex: r, field, before, after: coerced });
5661
+ changes.push({ rowIndex: sourceIdx, field, before, after: coerced });
5629
5662
  changed = true;
5630
5663
  }
5631
5664
  if (changed)
5632
- updated[r] = rowCopy;
5665
+ updated[sourceIdx] = rowCopy;
5633
5666
  }
5634
5667
  return updated;
5635
5668
  });
@@ -5640,10 +5673,11 @@ class ClipboardEngine {
5640
5673
  const cols = this.state.visibleColumns();
5641
5674
  const defMap = this.state.columnDefMap();
5642
5675
  const changes = [];
5676
+ const sourceIndices = this.resolveRangeSourceIndices(range);
5643
5677
  this.state.sourceData.update((data) => {
5644
5678
  const updated = [...data];
5645
- for (let r = range.start.row; r <= range.end.row; r++) {
5646
- const row = updated[r];
5679
+ for (const sourceIdx of sourceIndices) {
5680
+ const row = updated[sourceIdx];
5647
5681
  if (!row)
5648
5682
  continue;
5649
5683
  const rowCopy = { ...row };
@@ -5656,17 +5690,17 @@ class ClipboardEngine {
5656
5690
  if (!def?.editable)
5657
5691
  continue;
5658
5692
  const coerced = this.coerceAndValidate(field, value, row);
5659
- if (coerced === PASTE_SKIP$1)
5693
+ if (coerced === PASTE_SKIP)
5660
5694
  continue;
5661
5695
  const before = row[field];
5662
5696
  if (before === coerced)
5663
5697
  continue;
5664
5698
  rowCopy[field] = coerced;
5665
- changes.push({ rowIndex: r, field, before, after: coerced });
5699
+ changes.push({ rowIndex: sourceIdx, field, before, after: coerced });
5666
5700
  changed = true;
5667
5701
  }
5668
5702
  if (changed)
5669
- updated[r] = rowCopy;
5703
+ updated[sourceIdx] = rowCopy;
5670
5704
  }
5671
5705
  return updated;
5672
5706
  });
@@ -5677,10 +5711,11 @@ class ClipboardEngine {
5677
5711
  const cols = this.state.visibleColumns();
5678
5712
  const defMap = this.state.columnDefMap();
5679
5713
  const changes = [];
5714
+ const sourceIndices = this.resolveRangeSourceIndices(range);
5680
5715
  this.state.sourceData.update((data) => {
5681
5716
  const updated = [...data];
5682
- for (let r = range.start.row; r <= range.end.row; r++) {
5683
- const row = updated[r];
5717
+ for (const sourceIdx of sourceIndices) {
5718
+ const row = updated[sourceIdx];
5684
5719
  if (!row)
5685
5720
  continue;
5686
5721
  const rowCopy = { ...row };
@@ -5693,17 +5728,17 @@ class ClipboardEngine {
5693
5728
  if (!def?.editable)
5694
5729
  continue;
5695
5730
  const coerced = this.coerceAndValidate(field, null, row);
5696
- if (coerced === PASTE_SKIP$1)
5731
+ if (coerced === PASTE_SKIP)
5697
5732
  continue;
5698
5733
  const before = row[field];
5699
5734
  if (before === coerced)
5700
5735
  continue;
5701
5736
  rowCopy[field] = coerced;
5702
- changes.push({ rowIndex: r, field, before, after: coerced });
5737
+ changes.push({ rowIndex: sourceIdx, field, before, after: coerced });
5703
5738
  changed = true;
5704
5739
  }
5705
5740
  if (changed)
5706
- updated[r] = rowCopy;
5741
+ updated[sourceIdx] = rowCopy;
5707
5742
  }
5708
5743
  return updated;
5709
5744
  });
@@ -5716,10 +5751,11 @@ class ClipboardEngine {
5716
5751
  this.state.sourceData.update((data) => {
5717
5752
  const updated = [...data];
5718
5753
  for (let ri = 0; ri < pasteRows.length; ri++) {
5719
- const targetRow = range.start.row + ri;
5720
- if (targetRow >= updated.length)
5721
- break;
5722
- const row = updated[targetRow];
5754
+ const targetDisplayRow = range.start.row + ri;
5755
+ const sourceIdx = this.gridEngine.displayIndexToSourceIndex(targetDisplayRow);
5756
+ if (sourceIdx < 0)
5757
+ continue;
5758
+ const row = updated[sourceIdx];
5723
5759
  if (!row)
5724
5760
  continue;
5725
5761
  const rowCopy = { ...row };
@@ -5732,17 +5768,17 @@ class ClipboardEngine {
5732
5768
  if (!field)
5733
5769
  continue;
5734
5770
  const coerced = this.coerceAndValidate(field, pasteRows[ri][ci], row);
5735
- if (coerced === PASTE_SKIP$1)
5771
+ if (coerced === PASTE_SKIP)
5736
5772
  continue;
5737
5773
  const before = row[field];
5738
5774
  if (before === coerced)
5739
5775
  continue;
5740
5776
  rowCopy[field] = coerced;
5741
- changes.push({ rowIndex: targetRow, field, before, after: coerced });
5777
+ changes.push({ rowIndex: sourceIdx, field, before, after: coerced });
5742
5778
  changed = true;
5743
5779
  }
5744
5780
  if (changed)
5745
- updated[targetRow] = rowCopy;
5781
+ updated[sourceIdx] = rowCopy;
5746
5782
  }
5747
5783
  return updated;
5748
5784
  });
@@ -5785,7 +5821,7 @@ class ClipboardEngine {
5785
5821
  coerceAndValidate(field, rawValue, row) {
5786
5822
  const def = this.state.columnDefMap().get(field);
5787
5823
  if (!def?.editable)
5788
- return PASTE_SKIP$1;
5824
+ return PASTE_SKIP;
5789
5825
  const editorType = def.cellEditor;
5790
5826
  if (rawValue === null) {
5791
5827
  let clearValue;
@@ -5802,7 +5838,7 @@ class ClipboardEngine {
5802
5838
  if (def.cellEditorValidator) {
5803
5839
  const result = def.cellEditorValidator(clearValue, row);
5804
5840
  if (result === false || typeof result === 'string')
5805
- return PASTE_SKIP$1;
5841
+ return PASTE_SKIP;
5806
5842
  }
5807
5843
  return clearValue;
5808
5844
  }
@@ -5810,7 +5846,7 @@ class ClipboardEngine {
5810
5846
  if (editorType === 'number') {
5811
5847
  const num = Number(rawValue);
5812
5848
  if (isNaN(num))
5813
- return PASTE_SKIP$1;
5849
+ return PASTE_SKIP;
5814
5850
  value = num;
5815
5851
  }
5816
5852
  else if (editorType === 'checkbox') {
@@ -5821,19 +5857,19 @@ class ClipboardEngine {
5821
5857
  value = false;
5822
5858
  }
5823
5859
  else {
5824
- return PASTE_SKIP$1;
5860
+ return PASTE_SKIP;
5825
5861
  }
5826
5862
  }
5827
5863
  else if (editorType === 'select' && def.cellEditorOptions?.length) {
5828
5864
  const allowed = def.cellEditorOptions.map((o) => String(o.value));
5829
5865
  if (!allowed.includes(String(rawValue)))
5830
- return PASTE_SKIP$1;
5866
+ return PASTE_SKIP;
5831
5867
  value = rawValue;
5832
5868
  }
5833
5869
  if (def.cellEditorValidator) {
5834
5870
  const result = def.cellEditorValidator(value, row);
5835
5871
  if (result === false || typeof result === 'string')
5836
- return PASTE_SKIP$1;
5872
+ return PASTE_SKIP;
5837
5873
  }
5838
5874
  return value;
5839
5875
  }
@@ -6006,6 +6042,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6006
6042
  class InlineEditEngine {
6007
6043
  state = inject(GridStateManager);
6008
6044
  history = inject(HistoryEngine);
6045
+ gridEngine = inject(GridEngine);
6009
6046
  startEdit(rowIndex, field) {
6010
6047
  const defMap = this.state.columnDefMap();
6011
6048
  const def = defMap.get(field);
@@ -6014,7 +6051,8 @@ class InlineEditEngine {
6014
6051
  const colIndex = this.state.visibleColumns().findIndex((c) => c.field === field);
6015
6052
  if (colIndex < 0)
6016
6053
  return;
6017
- const row = this.state.sourceData()[rowIndex];
6054
+ const sourceIndex = this.gridEngine.displayIndexToSourceIndex(rowIndex);
6055
+ const row = sourceIndex >= 0 ? this.state.sourceData()[sourceIndex] : undefined;
6018
6056
  if (!row)
6019
6057
  return;
6020
6058
  const value = def.valueGetter ? def.valueGetter(row) : row[field];
@@ -6039,7 +6077,8 @@ class InlineEditEngine {
6039
6077
  const colIndex = this.state.visibleColumns().findIndex((c) => c.field === field);
6040
6078
  if (colIndex < 0)
6041
6079
  return;
6042
- const row = this.state.sourceData()[rowIndex];
6080
+ const sourceIndex = this.gridEngine.displayIndexToSourceIndex(rowIndex);
6081
+ const row = sourceIndex >= 0 ? this.state.sourceData()[sourceIndex] : undefined;
6043
6082
  if (!row)
6044
6083
  return;
6045
6084
  const currentValue = def.valueGetter
@@ -6086,7 +6125,10 @@ class InlineEditEngine {
6086
6125
  if (!field)
6087
6126
  return null;
6088
6127
  const def = this.state.columnDefMap().get(field);
6089
- let row = this.state.sourceData()[rowIndex];
6128
+ const sourceIndex = this.gridEngine.displayIndexToSourceIndex(rowIndex);
6129
+ if (sourceIndex < 0)
6130
+ return null;
6131
+ let row = this.state.sourceData()[sourceIndex];
6090
6132
  if (!row)
6091
6133
  return null;
6092
6134
  // Validation
@@ -6107,26 +6149,25 @@ class InlineEditEngine {
6107
6149
  return null;
6108
6150
  }
6109
6151
  }
6110
- // In client mode, update data immutably so signals detect the change
6111
- if (this.state.mode() === 'client') {
6112
- this.state.sourceData.update((data) => {
6113
- const updated = [...data];
6114
- updated[rowIndex] = { ...updated[rowIndex], [field]: editState.draftValue };
6115
- return updated;
6116
- });
6117
- // Re-read the updated row for the event
6118
- row = this.state.sourceData()[rowIndex];
6119
- }
6152
+ // Always update data immutably so signals detect the change, regardless
6153
+ // of client/server mode. Consumers can still re-fetch/override via output.
6154
+ this.state.sourceData.update((data) => {
6155
+ const updated = [...data];
6156
+ updated[sourceIndex] = { ...updated[sourceIndex], [field]: editState.draftValue };
6157
+ return updated;
6158
+ });
6159
+ // Re-read the updated row for the event
6160
+ row = this.state.sourceData()[sourceIndex];
6120
6161
  const event = {
6121
6162
  row,
6122
- rowIndex,
6163
+ rowIndex: sourceIndex,
6123
6164
  field,
6124
6165
  oldValue: editState.originalValue,
6125
6166
  newValue: editState.draftValue,
6126
6167
  };
6127
- if (this.state.mode() === 'client' && event.oldValue !== event.newValue) {
6168
+ if (event.oldValue !== event.newValue) {
6128
6169
  this.history.record('edit', [
6129
- { rowIndex, field, before: event.oldValue, after: event.newValue },
6170
+ { rowIndex: sourceIndex, field, before: event.oldValue, after: event.newValue },
6130
6171
  ]);
6131
6172
  }
6132
6173
  this.state.cellEditState.set({
@@ -6182,7 +6223,13 @@ class RowSelectionEngine {
6182
6223
  engine = inject(GridEngine);
6183
6224
  selectedIds = computed(() => this.state.selectedRowIds(), ...(ngDevMode ? [{ debugName: "selectedIds" }] : /* istanbul ignore next */ []));
6184
6225
  excludedIds = computed(() => this.state.excludedRowIds(), ...(ngDevMode ? [{ debugName: "excludedIds" }] : /* istanbul ignore next */ []));
6185
- lastToggledIndex = signal(-1, ...(ngDevMode ? [{ debugName: "lastToggledIndex" }] : /* istanbul ignore next */ []));
6226
+ /**
6227
+ * Anchor row for shift-click range selection. We track the row object
6228
+ * (not its index) so the anchor stays valid across grouping, sorting and
6229
+ * filtering — where display indices shift without the underlying row
6230
+ * changing.
6231
+ */
6232
+ lastToggledRow = signal(null, ...(ngDevMode ? [{ debugName: "lastToggledRow" }] : /* istanbul ignore next */ []));
6186
6233
  count = computed(() => {
6187
6234
  const mode = this.state.selectAllMode();
6188
6235
  if (mode === 'all') {
@@ -6208,23 +6255,41 @@ class RowSelectionEngine {
6208
6255
  const pageSelected = pageData.filter((row) => this.isRowSelected(row)).length;
6209
6256
  return pageSelected > 0 && pageSelected < pageData.length;
6210
6257
  }, ...(ngDevMode ? [{ debugName: "isIndeterminate" }] : /* istanbul ignore next */ []));
6211
- selectRowRange(fromIndex, toIndex) {
6258
+ /**
6259
+ * Extends the selection from the last-toggled row (the anchor) to `endRow`,
6260
+ * resolving positions by object identity against the currently paginated
6261
+ * data. This is robust to grouping / sorting / filtering because we don't
6262
+ * rely on numeric indices, and works across any visible page slice.
6263
+ */
6264
+ selectRowRangeToRow(endRow) {
6265
+ const anchor = this.lastToggledRow();
6266
+ if (!anchor) {
6267
+ this.toggleRow(endRow);
6268
+ this.lastToggledRow.set(endRow);
6269
+ return;
6270
+ }
6212
6271
  const pageData = this.engine.paginatedData();
6213
- const start = Math.min(fromIndex, toIndex);
6214
- const end = Math.max(fromIndex, toIndex);
6272
+ const anchorIdx = pageData.indexOf(anchor);
6273
+ const endIdx = pageData.indexOf(endRow);
6274
+ if (anchorIdx < 0 || endIdx < 0) {
6275
+ // Anchor left the visible page — fall back to a plain toggle
6276
+ this.toggleRow(endRow);
6277
+ this.lastToggledRow.set(endRow);
6278
+ return;
6279
+ }
6280
+ const start = Math.min(anchorIdx, endIdx);
6281
+ const end = Math.max(anchorIdx, endIdx);
6215
6282
  this.state.selectedRowIds.update((ids) => {
6216
6283
  const next = new Set(ids);
6217
6284
  for (let i = start; i <= end; i++) {
6218
- if (i >= 0 && i < pageData.length) {
6219
- next.add(this.getRowId(pageData[i]));
6220
- }
6285
+ next.add(this.getRowId(pageData[i]));
6221
6286
  }
6222
6287
  return next;
6223
6288
  });
6224
6289
  if (this.state.selectAllMode() === 'none') {
6225
6290
  this.state.selectAllMode.set('page');
6226
6291
  }
6227
- this.lastToggledIndex.set(toIndex);
6292
+ this.lastToggledRow.set(endRow);
6228
6293
  }
6229
6294
  toggleRow(row) {
6230
6295
  const id = this.getRowId(row);
@@ -8110,13 +8175,6 @@ class MozGridHeaderCellComponent {
8110
8175
  if (def.sortable !== false) {
8111
8176
  items.push({ id: 'sort-asc', icon: ChevronUp20, label: 'Sort A → Z' }, { id: 'sort-desc', icon: ChevronDown20, label: 'Sort Z → A' });
8112
8177
  }
8113
- if (def.filterable) {
8114
- items.push({
8115
- id: 'filter-column',
8116
- label: 'Filter in this column',
8117
- divider: items.length > 0,
8118
- });
8119
- }
8120
8178
  if (def.groupable) {
8121
8179
  items.push({
8122
8180
  id: 'group-column',
@@ -8151,11 +8209,6 @@ class MozGridHeaderCellComponent {
8151
8209
  divider: items.length > 0,
8152
8210
  });
8153
8211
  }
8154
- items.push({
8155
- id: 'toggle-column-search',
8156
- label: colState.searchVisible ? 'Hide search by column' : 'Show search by column',
8157
- divider: def.hideable === false && items.length > 0,
8158
- });
8159
8212
  return items;
8160
8213
  }, ...(ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []));
8161
8214
  onHeaderClick(event) {
@@ -8252,11 +8305,11 @@ class MozGridHeaderComponent {
8252
8305
  this.dragEngine.startDrag(event, colIndex, center.nativeElement);
8253
8306
  }
8254
8307
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MozGridHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8255
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: MozGridHeaderComponent, isStandalone: true, selector: "moz-grid-header", inputs: { showCheckbox: { classPropertyName: "showCheckbox", publicName: "showCheckbox", isSignal: true, isRequired: false, transformFunction: null }, showExpand: { classPropertyName: "showExpand", publicName: "showExpand", isSignal: true, isRequired: false, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortClick: "sortClick", menuAction: "menuAction", resizeStart: "resizeStart", selectAllToggle: "selectAllToggle", columnReorder: "columnReorder" }, viewQueries: [{ propertyName: "headerCenter", first: true, predicate: ["headerCenter"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"grid-header\" [class.grid-header--no-bottom-border]=\"hasFilterRow()\" role=\"row\">\n <!-- Left pinned -->\n @if (hasLeftSection()) {\n <div class=\"grid-header__left\" [style.width.px]=\"leftSectionWidth()\">\n @if (showExpand()) {\n <div class=\"grid-header__expand-spacer\"></div>\n } @if (showCheckbox()) {\n <div class=\"grid-header__checkbox\">\n <moz-checkbox\n id=\"grid-select-all\"\n label=\"Select all rows\"\n [indeterminate]=\"rowSelection.isIndeterminate()\"\n [ngModel]=\"rowSelection.isAllSelected()\"\n (change)=\"onSelectAllClick($event)\"\n />\n </div>\n } @for (col of state.pinnedLeftColumns(); track trackByField($index, col)) {\n <moz-grid-header-cell\n [columnState]=\"col\"\n [def]=\"state.columnDefMap().get(col.field)!\"\n [isLast]=\"false\"\n (sortClick)=\"sortClick.emit($event)\"\n (menuAction)=\"menuAction.emit($event)\"\n (resizeStart)=\"resizeStart.emit({ field: col.field, event: $event })\"\n />\n }\n </div>\n }\n\n <!-- Center (scrolls natively inside viewport) -->\n <div class=\"grid-header__center\" #headerCenter [style.min-width.px]=\"state.unpinnedWidth()\">\n @for (col of state.unpinnedColumns(); track trackByField($index, col)) {\n <div class=\"grid-header__cell-wrapper\" [class.grid-header__cell-wrapper--last]=\"$last\">\n <moz-grid-header-cell\n [columnState]=\"col\"\n [def]=\"state.columnDefMap().get(col.field)!\"\n [isLast]=\"$last\"\n [reorderable]=\"reorderable()\"\n (sortClick)=\"sortClick.emit($event)\"\n (menuAction)=\"menuAction.emit($event)\"\n (resizeStart)=\"resizeStart.emit({ field: col.field, event: $event })\"\n (mousedown)=\"onDragStart($event, $index)\"\n />\n </div>\n }\n </div>\n\n <!-- Right pinned -->\n @if (state.pinnedRightColumns().length > 0) {\n <div class=\"grid-header__right\" [style.width.px]=\"state.pinnedRightWidth()\">\n @for (col of state.pinnedRightColumns(); track trackByField($index, col)) {\n <moz-grid-header-cell\n [columnState]=\"col\"\n [def]=\"state.columnDefMap().get(col.field)!\"\n [pinnedEnd]=\"true\"\n [isLast]=\"$last\"\n (sortClick)=\"sortClick.emit($event)\"\n (menuAction)=\"menuAction.emit($event)\"\n (resizeStart)=\"resizeStart.emit({ field: col.field, event: $event })\"\n />\n }\n </div>\n }\n</div>\n\n@if (hasFilterRow()) {\n<div class=\"grid-header__filter-row\">\n @if (hasLeftSection()) {\n <div class=\"grid-header__filter-left\" [style.width.px]=\"leftSectionWidth()\">\n @if (showExpand()) {\n <div\n class=\"grid-header__filter-cell grid-header__filter-cell--spacer\"\n style=\"width: 36px; min-width: 36px;\"\n ></div>\n } @if (showCheckbox()) {\n <div\n class=\"grid-header__filter-cell grid-header__filter-cell--spacer\"\n style=\"width: 48px; min-width: 48px;\"\n ></div>\n } @for (col of state.pinnedLeftColumns(); track trackByField($index, col)) {\n <div class=\"grid-header__filter-cell\" [style.width.px]=\"col.currentWidth\">\n @if (getFilterTemplate(col.field); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col.field }\"\n />\n }\n </div>\n }\n </div>\n }\n <div class=\"grid-header__filter-center\" [style.min-width.px]=\"state.unpinnedWidth()\">\n @for (col of state.unpinnedColumns(); track trackByField($index, col)) {\n <div class=\"grid-header__filter-cell\" [class.grid-header__filter-cell--last]=\"$last\" [style.width.px]=\"col.currentWidth\">\n @if (getFilterTemplate(col.field); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col.field }\"\n />\n }\n </div>\n }\n <div class=\"grid-header__filler\"></div>\n </div>\n @if (state.pinnedRightColumns().length > 0) {\n <div class=\"grid-header__filter-right\" [style.width.px]=\"state.pinnedRightWidth()\">\n @for (col of state.pinnedRightColumns(); track trackByField($index, col)) {\n <div class=\"grid-header__filter-cell grid-header__filter-cell--pinned-end\" [style.width.px]=\"col.currentWidth\">\n @if (getFilterTemplate(col.field); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col.field }\"\n />\n }\n </div>\n }\n </div>\n }\n</div>\n}\n", styles: [":host{display:block;flex-shrink:0;z-index:3;overflow:hidden}.grid-header{display:flex;height:48px;width:100%;min-width:fit-content;border-bottom:1px solid var(--color-border-primary);background:var(--color-background-primary, #fff)}.grid-header--no-bottom-border{border-bottom:none}.grid-header__left{display:flex;flex:0 0 auto;position:sticky;left:0;z-index:3;background:var(--color-background-primary, #fff);box-shadow:4px 0 8px -2px #0000001a;box-sizing:border-box}.grid-header__center{display:flex;flex:1 0 auto;height:100%}.grid-header__right{display:flex;flex:0 0 auto;position:sticky;right:0;z-index:3;background:var(--color-background-primary, #fff);box-shadow:-4px 0 8px -2px #0000001a;box-sizing:border-box}.grid-header__checkbox{display:flex;align-items:center;justify-content:center;width:48px;min-width:48px;box-sizing:border-box;cursor:pointer;background:inherit}.grid-header__checkbox ::ng-deep .checkbox{margin:0}.grid-header__checkbox ::ng-deep .checkbox__label{display:none}.grid-header__cell-wrapper{display:flex;position:relative;flex:0 0 auto}.grid-header__cell-wrapper--last{flex:1 0 auto}.grid-header__expand-spacer{width:36px;min-width:36px;box-sizing:border-box;background:inherit}.grid-header__filter-row{display:flex;min-width:fit-content;height:40px;background:var(--color-background-primary);border-bottom:1px solid var(--color-border-primary)}.grid-header__filter-left{display:flex;flex:0 0 auto;position:sticky;left:0;z-index:3;background:var(--color-background-primary);box-shadow:4px 0 8px -2px #0000001a;box-sizing:border-box}.grid-header__filter-center{display:flex;flex:1 0 auto;height:100%}.grid-header__filter-right{display:flex;flex:0 0 auto;position:sticky;right:0;z-index:3;background:var(--color-background-primary);box-shadow:-4px 0 8px -2px #0000001a;box-sizing:border-box}.grid-header__filter-cell{display:flex;align-items:center;padding:0 var(--spacing-xs, 4px);box-sizing:border-box;overflow:hidden;height:100%;border-right:1px solid var(--color-border-primary)}.grid-header__filter-cell--spacer{flex-shrink:0;border-right:none}.grid-header__left moz-grid-header-cell:last-child ::ng-deep .grid-header-cell{border-right:none}.grid-header__right moz-grid-header-cell:first-child ::ng-deep .grid-header-cell{border-left:none}.grid-header__filter-cell--last{border-right:none}.grid-header__filter-left .grid-header__filter-cell:last-child{border-right:none}.grid-header__filter-cell--pinned-end{border-right:none;border-left:1px solid var(--color-border-primary)}.grid-header__filter-right .grid-header__filter-cell--pinned-end:first-child{border-left:none}\n"], dependencies: [{ kind: "component", type: MozGridHeaderCellComponent, selector: "moz-grid-header-cell", inputs: ["columnState", "def", "isLast", "pinnedEnd", "reorderable"], outputs: ["sortClick", "menuAction", "resizeStart"] }, { kind: "component", type: MozCheckboxComponent, selector: "moz-checkbox", inputs: ["id", "name", "label", "indeterminate", "isInvalid", "disabled", "indented"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
8308
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: MozGridHeaderComponent, isStandalone: true, selector: "moz-grid-header", inputs: { showCheckbox: { classPropertyName: "showCheckbox", publicName: "showCheckbox", isSignal: true, isRequired: false, transformFunction: null }, showExpand: { classPropertyName: "showExpand", publicName: "showExpand", isSignal: true, isRequired: false, transformFunction: null }, reorderable: { classPropertyName: "reorderable", publicName: "reorderable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortClick: "sortClick", menuAction: "menuAction", resizeStart: "resizeStart", selectAllToggle: "selectAllToggle", columnReorder: "columnReorder" }, viewQueries: [{ propertyName: "headerCenter", first: true, predicate: ["headerCenter"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"grid-header\" [class.grid-header--no-bottom-border]=\"hasFilterRow()\" role=\"row\">\n <!-- Left pinned -->\n @if (hasLeftSection()) {\n <div class=\"grid-header__left\" [style.width.px]=\"leftSectionWidth()\">\n @if (showExpand()) {\n <div class=\"grid-header__expand-spacer\"></div>\n } @if (showCheckbox()) {\n <div class=\"grid-header__checkbox\">\n <moz-checkbox\n id=\"grid-select-all\"\n label=\"Select all rows\"\n [indeterminate]=\"rowSelection.isIndeterminate()\"\n [ngModel]=\"rowSelection.isAllSelected()\"\n (change)=\"onSelectAllClick($event)\"\n />\n </div>\n } @for (col of state.pinnedLeftColumns(); track trackByField($index, col)) {\n <moz-grid-header-cell\n [columnState]=\"col\"\n [def]=\"state.columnDefMap().get(col.field)!\"\n [isLast]=\"false\"\n (sortClick)=\"sortClick.emit($event)\"\n (menuAction)=\"menuAction.emit($event)\"\n (resizeStart)=\"resizeStart.emit({ field: col.field, event: $event })\"\n />\n }\n </div>\n }\n\n <!-- Center (scrolls natively inside viewport) -->\n <div class=\"grid-header__center\" #headerCenter [style.min-width.px]=\"state.unpinnedWidth()\">\n @for (col of state.unpinnedColumns(); track trackByField($index, col)) {\n <div class=\"grid-header__cell-wrapper\" [class.grid-header__cell-wrapper--last]=\"$last\">\n <moz-grid-header-cell\n [columnState]=\"col\"\n [def]=\"state.columnDefMap().get(col.field)!\"\n [isLast]=\"$last\"\n [reorderable]=\"reorderable()\"\n (sortClick)=\"sortClick.emit($event)\"\n (menuAction)=\"menuAction.emit($event)\"\n (resizeStart)=\"resizeStart.emit({ field: col.field, event: $event })\"\n (mousedown)=\"onDragStart($event, $index)\"\n />\n </div>\n }\n </div>\n\n <!-- Right pinned -->\n @if (state.pinnedRightColumns().length > 0) {\n <div class=\"grid-header__right\" [style.width.px]=\"state.pinnedRightWidth()\">\n @for (col of state.pinnedRightColumns(); track trackByField($index, col)) {\n <moz-grid-header-cell\n [columnState]=\"col\"\n [def]=\"state.columnDefMap().get(col.field)!\"\n [pinnedEnd]=\"true\"\n [isLast]=\"$last\"\n (sortClick)=\"sortClick.emit($event)\"\n (menuAction)=\"menuAction.emit($event)\"\n (resizeStart)=\"resizeStart.emit({ field: col.field, event: $event })\"\n />\n }\n </div>\n }\n</div>\n\n@if (hasFilterRow()) {\n<div class=\"grid-header__filter-row\">\n @if (hasLeftSection()) {\n <div class=\"grid-header__filter-left\" [style.width.px]=\"leftSectionWidth()\">\n @if (showExpand()) {\n <div\n class=\"grid-header__filter-cell grid-header__filter-cell--spacer\"\n style=\"width: 36px; min-width: 36px\"\n ></div>\n } @if (showCheckbox()) {\n <div\n class=\"grid-header__filter-cell grid-header__filter-cell--spacer\"\n style=\"width: 48px; min-width: 48px\"\n ></div>\n } @for (col of state.pinnedLeftColumns(); track trackByField($index, col)) {\n <div class=\"grid-header__filter-cell\" [style.width.px]=\"col.currentWidth\">\n @if (getFilterTemplate(col.field); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col.field }\"\n />\n }\n </div>\n }\n </div>\n }\n <div class=\"grid-header__filter-center\" [style.min-width.px]=\"state.unpinnedWidth()\">\n @for (col of state.unpinnedColumns(); track trackByField($index, col)) {\n <div\n class=\"grid-header__filter-cell\"\n [class.grid-header__filter-cell--last]=\"$last\"\n [style.width.px]=\"col.currentWidth\"\n >\n @if (getFilterTemplate(col.field); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col.field }\"\n />\n }\n </div>\n }\n <div class=\"grid-header__filler\"></div>\n </div>\n @if (state.pinnedRightColumns().length > 0) {\n <div class=\"grid-header__filter-right\" [style.width.px]=\"state.pinnedRightWidth()\">\n @for (col of state.pinnedRightColumns(); track trackByField($index, col)) {\n <div\n class=\"grid-header__filter-cell grid-header__filter-cell--pinned-end\"\n [style.width.px]=\"col.currentWidth\"\n >\n @if (getFilterTemplate(col.field); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col.field }\"\n />\n }\n </div>\n }\n </div>\n }\n</div>\n}\n", styles: [":host{display:block;flex-shrink:0;z-index:3;overflow:hidden}.grid-header{display:flex;height:48px;width:100%;min-width:fit-content;border-bottom:1px solid var(--color-border-primary);background:var(--color-background-primary, #fff)}.grid-header--no-bottom-border{border-bottom:none}.grid-header__left{display:flex;flex:0 0 auto;position:sticky;left:0;z-index:3;background:var(--color-background-primary, #fff);box-shadow:4px 0 8px -2px #0000001a;box-sizing:border-box}.grid-header__center{display:flex;flex:1 0 auto;height:100%}.grid-header__right{display:flex;flex:0 0 auto;position:sticky;right:0;z-index:3;background:var(--color-background-primary, #fff);box-shadow:-4px 0 8px -2px #0000001a;box-sizing:border-box}.grid-header__checkbox{display:flex;align-items:center;justify-content:center;width:48px;min-width:48px;box-sizing:border-box;cursor:pointer;background:inherit}.grid-header__checkbox ::ng-deep .checkbox{margin:0}.grid-header__checkbox ::ng-deep .checkbox__label{display:none}.grid-header__cell-wrapper{display:flex;position:relative;flex:0 0 auto}.grid-header__cell-wrapper--last{flex:1 0 auto}.grid-header__filler{flex:1 1 auto;min-width:0;height:100%;background:inherit}.grid-header__expand-spacer{width:36px;min-width:36px;box-sizing:border-box;background:inherit}.grid-header__filter-row{display:flex;min-width:fit-content;height:40px;background:var(--color-background-primary);border-bottom:1px solid var(--color-border-primary)}.grid-header__filter-left{display:flex;flex:0 0 auto;position:sticky;left:0;z-index:3;background:var(--color-background-primary);box-shadow:4px 0 8px -2px #0000001a;box-sizing:border-box}.grid-header__filter-center{display:flex;flex:1 0 auto;height:100%}.grid-header__filter-right{display:flex;flex:0 0 auto;position:sticky;right:0;z-index:3;background:var(--color-background-primary);box-shadow:-4px 0 8px -2px #0000001a;box-sizing:border-box}.grid-header__filter-cell{display:flex;align-items:center;padding:0 var(--spacing-xs, 4px);box-sizing:border-box;overflow:hidden;height:100%;border-right:1px solid var(--color-border-primary)}.grid-header__filter-cell--spacer{flex-shrink:0;border-right:none}.grid-header__left moz-grid-header-cell:last-child ::ng-deep .grid-header-cell{border-right:none}.grid-header__right moz-grid-header-cell:first-child ::ng-deep .grid-header-cell{border-left:none}.grid-header__filter-cell--last{border-right:none}.grid-header__filter-left .grid-header__filter-cell:last-child{border-right:none}.grid-header__filter-cell--pinned-end{border-right:none;border-left:1px solid var(--color-border-primary)}.grid-header__filter-right .grid-header__filter-cell--pinned-end:first-child{border-left:none}\n"], dependencies: [{ kind: "component", type: MozGridHeaderCellComponent, selector: "moz-grid-header-cell", inputs: ["columnState", "def", "isLast", "pinnedEnd", "reorderable"], outputs: ["sortClick", "menuAction", "resizeStart"] }, { kind: "component", type: MozCheckboxComponent, selector: "moz-checkbox", inputs: ["id", "name", "label", "indeterminate", "isInvalid", "disabled", "indented"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
8256
8309
  }
8257
8310
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MozGridHeaderComponent, decorators: [{
8258
8311
  type: Component,
8259
- args: [{ selector: 'moz-grid-header', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MozGridHeaderCellComponent, MozCheckboxComponent, FormsModule, NgTemplateOutlet], template: "<div class=\"grid-header\" [class.grid-header--no-bottom-border]=\"hasFilterRow()\" role=\"row\">\n <!-- Left pinned -->\n @if (hasLeftSection()) {\n <div class=\"grid-header__left\" [style.width.px]=\"leftSectionWidth()\">\n @if (showExpand()) {\n <div class=\"grid-header__expand-spacer\"></div>\n } @if (showCheckbox()) {\n <div class=\"grid-header__checkbox\">\n <moz-checkbox\n id=\"grid-select-all\"\n label=\"Select all rows\"\n [indeterminate]=\"rowSelection.isIndeterminate()\"\n [ngModel]=\"rowSelection.isAllSelected()\"\n (change)=\"onSelectAllClick($event)\"\n />\n </div>\n } @for (col of state.pinnedLeftColumns(); track trackByField($index, col)) {\n <moz-grid-header-cell\n [columnState]=\"col\"\n [def]=\"state.columnDefMap().get(col.field)!\"\n [isLast]=\"false\"\n (sortClick)=\"sortClick.emit($event)\"\n (menuAction)=\"menuAction.emit($event)\"\n (resizeStart)=\"resizeStart.emit({ field: col.field, event: $event })\"\n />\n }\n </div>\n }\n\n <!-- Center (scrolls natively inside viewport) -->\n <div class=\"grid-header__center\" #headerCenter [style.min-width.px]=\"state.unpinnedWidth()\">\n @for (col of state.unpinnedColumns(); track trackByField($index, col)) {\n <div class=\"grid-header__cell-wrapper\" [class.grid-header__cell-wrapper--last]=\"$last\">\n <moz-grid-header-cell\n [columnState]=\"col\"\n [def]=\"state.columnDefMap().get(col.field)!\"\n [isLast]=\"$last\"\n [reorderable]=\"reorderable()\"\n (sortClick)=\"sortClick.emit($event)\"\n (menuAction)=\"menuAction.emit($event)\"\n (resizeStart)=\"resizeStart.emit({ field: col.field, event: $event })\"\n (mousedown)=\"onDragStart($event, $index)\"\n />\n </div>\n }\n </div>\n\n <!-- Right pinned -->\n @if (state.pinnedRightColumns().length > 0) {\n <div class=\"grid-header__right\" [style.width.px]=\"state.pinnedRightWidth()\">\n @for (col of state.pinnedRightColumns(); track trackByField($index, col)) {\n <moz-grid-header-cell\n [columnState]=\"col\"\n [def]=\"state.columnDefMap().get(col.field)!\"\n [pinnedEnd]=\"true\"\n [isLast]=\"$last\"\n (sortClick)=\"sortClick.emit($event)\"\n (menuAction)=\"menuAction.emit($event)\"\n (resizeStart)=\"resizeStart.emit({ field: col.field, event: $event })\"\n />\n }\n </div>\n }\n</div>\n\n@if (hasFilterRow()) {\n<div class=\"grid-header__filter-row\">\n @if (hasLeftSection()) {\n <div class=\"grid-header__filter-left\" [style.width.px]=\"leftSectionWidth()\">\n @if (showExpand()) {\n <div\n class=\"grid-header__filter-cell grid-header__filter-cell--spacer\"\n style=\"width: 36px; min-width: 36px;\"\n ></div>\n } @if (showCheckbox()) {\n <div\n class=\"grid-header__filter-cell grid-header__filter-cell--spacer\"\n style=\"width: 48px; min-width: 48px;\"\n ></div>\n } @for (col of state.pinnedLeftColumns(); track trackByField($index, col)) {\n <div class=\"grid-header__filter-cell\" [style.width.px]=\"col.currentWidth\">\n @if (getFilterTemplate(col.field); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col.field }\"\n />\n }\n </div>\n }\n </div>\n }\n <div class=\"grid-header__filter-center\" [style.min-width.px]=\"state.unpinnedWidth()\">\n @for (col of state.unpinnedColumns(); track trackByField($index, col)) {\n <div class=\"grid-header__filter-cell\" [class.grid-header__filter-cell--last]=\"$last\" [style.width.px]=\"col.currentWidth\">\n @if (getFilterTemplate(col.field); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col.field }\"\n />\n }\n </div>\n }\n <div class=\"grid-header__filler\"></div>\n </div>\n @if (state.pinnedRightColumns().length > 0) {\n <div class=\"grid-header__filter-right\" [style.width.px]=\"state.pinnedRightWidth()\">\n @for (col of state.pinnedRightColumns(); track trackByField($index, col)) {\n <div class=\"grid-header__filter-cell grid-header__filter-cell--pinned-end\" [style.width.px]=\"col.currentWidth\">\n @if (getFilterTemplate(col.field); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col.field }\"\n />\n }\n </div>\n }\n </div>\n }\n</div>\n}\n", styles: [":host{display:block;flex-shrink:0;z-index:3;overflow:hidden}.grid-header{display:flex;height:48px;width:100%;min-width:fit-content;border-bottom:1px solid var(--color-border-primary);background:var(--color-background-primary, #fff)}.grid-header--no-bottom-border{border-bottom:none}.grid-header__left{display:flex;flex:0 0 auto;position:sticky;left:0;z-index:3;background:var(--color-background-primary, #fff);box-shadow:4px 0 8px -2px #0000001a;box-sizing:border-box}.grid-header__center{display:flex;flex:1 0 auto;height:100%}.grid-header__right{display:flex;flex:0 0 auto;position:sticky;right:0;z-index:3;background:var(--color-background-primary, #fff);box-shadow:-4px 0 8px -2px #0000001a;box-sizing:border-box}.grid-header__checkbox{display:flex;align-items:center;justify-content:center;width:48px;min-width:48px;box-sizing:border-box;cursor:pointer;background:inherit}.grid-header__checkbox ::ng-deep .checkbox{margin:0}.grid-header__checkbox ::ng-deep .checkbox__label{display:none}.grid-header__cell-wrapper{display:flex;position:relative;flex:0 0 auto}.grid-header__cell-wrapper--last{flex:1 0 auto}.grid-header__expand-spacer{width:36px;min-width:36px;box-sizing:border-box;background:inherit}.grid-header__filter-row{display:flex;min-width:fit-content;height:40px;background:var(--color-background-primary);border-bottom:1px solid var(--color-border-primary)}.grid-header__filter-left{display:flex;flex:0 0 auto;position:sticky;left:0;z-index:3;background:var(--color-background-primary);box-shadow:4px 0 8px -2px #0000001a;box-sizing:border-box}.grid-header__filter-center{display:flex;flex:1 0 auto;height:100%}.grid-header__filter-right{display:flex;flex:0 0 auto;position:sticky;right:0;z-index:3;background:var(--color-background-primary);box-shadow:-4px 0 8px -2px #0000001a;box-sizing:border-box}.grid-header__filter-cell{display:flex;align-items:center;padding:0 var(--spacing-xs, 4px);box-sizing:border-box;overflow:hidden;height:100%;border-right:1px solid var(--color-border-primary)}.grid-header__filter-cell--spacer{flex-shrink:0;border-right:none}.grid-header__left moz-grid-header-cell:last-child ::ng-deep .grid-header-cell{border-right:none}.grid-header__right moz-grid-header-cell:first-child ::ng-deep .grid-header-cell{border-left:none}.grid-header__filter-cell--last{border-right:none}.grid-header__filter-left .grid-header__filter-cell:last-child{border-right:none}.grid-header__filter-cell--pinned-end{border-right:none;border-left:1px solid var(--color-border-primary)}.grid-header__filter-right .grid-header__filter-cell--pinned-end:first-child{border-left:none}\n"] }]
8312
+ args: [{ selector: 'moz-grid-header', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MozGridHeaderCellComponent, MozCheckboxComponent, FormsModule, NgTemplateOutlet], template: "<div class=\"grid-header\" [class.grid-header--no-bottom-border]=\"hasFilterRow()\" role=\"row\">\n <!-- Left pinned -->\n @if (hasLeftSection()) {\n <div class=\"grid-header__left\" [style.width.px]=\"leftSectionWidth()\">\n @if (showExpand()) {\n <div class=\"grid-header__expand-spacer\"></div>\n } @if (showCheckbox()) {\n <div class=\"grid-header__checkbox\">\n <moz-checkbox\n id=\"grid-select-all\"\n label=\"Select all rows\"\n [indeterminate]=\"rowSelection.isIndeterminate()\"\n [ngModel]=\"rowSelection.isAllSelected()\"\n (change)=\"onSelectAllClick($event)\"\n />\n </div>\n } @for (col of state.pinnedLeftColumns(); track trackByField($index, col)) {\n <moz-grid-header-cell\n [columnState]=\"col\"\n [def]=\"state.columnDefMap().get(col.field)!\"\n [isLast]=\"false\"\n (sortClick)=\"sortClick.emit($event)\"\n (menuAction)=\"menuAction.emit($event)\"\n (resizeStart)=\"resizeStart.emit({ field: col.field, event: $event })\"\n />\n }\n </div>\n }\n\n <!-- Center (scrolls natively inside viewport) -->\n <div class=\"grid-header__center\" #headerCenter [style.min-width.px]=\"state.unpinnedWidth()\">\n @for (col of state.unpinnedColumns(); track trackByField($index, col)) {\n <div class=\"grid-header__cell-wrapper\" [class.grid-header__cell-wrapper--last]=\"$last\">\n <moz-grid-header-cell\n [columnState]=\"col\"\n [def]=\"state.columnDefMap().get(col.field)!\"\n [isLast]=\"$last\"\n [reorderable]=\"reorderable()\"\n (sortClick)=\"sortClick.emit($event)\"\n (menuAction)=\"menuAction.emit($event)\"\n (resizeStart)=\"resizeStart.emit({ field: col.field, event: $event })\"\n (mousedown)=\"onDragStart($event, $index)\"\n />\n </div>\n }\n </div>\n\n <!-- Right pinned -->\n @if (state.pinnedRightColumns().length > 0) {\n <div class=\"grid-header__right\" [style.width.px]=\"state.pinnedRightWidth()\">\n @for (col of state.pinnedRightColumns(); track trackByField($index, col)) {\n <moz-grid-header-cell\n [columnState]=\"col\"\n [def]=\"state.columnDefMap().get(col.field)!\"\n [pinnedEnd]=\"true\"\n [isLast]=\"$last\"\n (sortClick)=\"sortClick.emit($event)\"\n (menuAction)=\"menuAction.emit($event)\"\n (resizeStart)=\"resizeStart.emit({ field: col.field, event: $event })\"\n />\n }\n </div>\n }\n</div>\n\n@if (hasFilterRow()) {\n<div class=\"grid-header__filter-row\">\n @if (hasLeftSection()) {\n <div class=\"grid-header__filter-left\" [style.width.px]=\"leftSectionWidth()\">\n @if (showExpand()) {\n <div\n class=\"grid-header__filter-cell grid-header__filter-cell--spacer\"\n style=\"width: 36px; min-width: 36px\"\n ></div>\n } @if (showCheckbox()) {\n <div\n class=\"grid-header__filter-cell grid-header__filter-cell--spacer\"\n style=\"width: 48px; min-width: 48px\"\n ></div>\n } @for (col of state.pinnedLeftColumns(); track trackByField($index, col)) {\n <div class=\"grid-header__filter-cell\" [style.width.px]=\"col.currentWidth\">\n @if (getFilterTemplate(col.field); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col.field }\"\n />\n }\n </div>\n }\n </div>\n }\n <div class=\"grid-header__filter-center\" [style.min-width.px]=\"state.unpinnedWidth()\">\n @for (col of state.unpinnedColumns(); track trackByField($index, col)) {\n <div\n class=\"grid-header__filter-cell\"\n [class.grid-header__filter-cell--last]=\"$last\"\n [style.width.px]=\"col.currentWidth\"\n >\n @if (getFilterTemplate(col.field); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col.field }\"\n />\n }\n </div>\n }\n <div class=\"grid-header__filler\"></div>\n </div>\n @if (state.pinnedRightColumns().length > 0) {\n <div class=\"grid-header__filter-right\" [style.width.px]=\"state.pinnedRightWidth()\">\n @for (col of state.pinnedRightColumns(); track trackByField($index, col)) {\n <div\n class=\"grid-header__filter-cell grid-header__filter-cell--pinned-end\"\n [style.width.px]=\"col.currentWidth\"\n >\n @if (getFilterTemplate(col.field); as tmpl) {\n <ng-container\n [ngTemplateOutlet]=\"tmpl\"\n [ngTemplateOutletContext]=\"{ $implicit: col.field }\"\n />\n }\n </div>\n }\n </div>\n }\n</div>\n}\n", styles: [":host{display:block;flex-shrink:0;z-index:3;overflow:hidden}.grid-header{display:flex;height:48px;width:100%;min-width:fit-content;border-bottom:1px solid var(--color-border-primary);background:var(--color-background-primary, #fff)}.grid-header--no-bottom-border{border-bottom:none}.grid-header__left{display:flex;flex:0 0 auto;position:sticky;left:0;z-index:3;background:var(--color-background-primary, #fff);box-shadow:4px 0 8px -2px #0000001a;box-sizing:border-box}.grid-header__center{display:flex;flex:1 0 auto;height:100%}.grid-header__right{display:flex;flex:0 0 auto;position:sticky;right:0;z-index:3;background:var(--color-background-primary, #fff);box-shadow:-4px 0 8px -2px #0000001a;box-sizing:border-box}.grid-header__checkbox{display:flex;align-items:center;justify-content:center;width:48px;min-width:48px;box-sizing:border-box;cursor:pointer;background:inherit}.grid-header__checkbox ::ng-deep .checkbox{margin:0}.grid-header__checkbox ::ng-deep .checkbox__label{display:none}.grid-header__cell-wrapper{display:flex;position:relative;flex:0 0 auto}.grid-header__cell-wrapper--last{flex:1 0 auto}.grid-header__filler{flex:1 1 auto;min-width:0;height:100%;background:inherit}.grid-header__expand-spacer{width:36px;min-width:36px;box-sizing:border-box;background:inherit}.grid-header__filter-row{display:flex;min-width:fit-content;height:40px;background:var(--color-background-primary);border-bottom:1px solid var(--color-border-primary)}.grid-header__filter-left{display:flex;flex:0 0 auto;position:sticky;left:0;z-index:3;background:var(--color-background-primary);box-shadow:4px 0 8px -2px #0000001a;box-sizing:border-box}.grid-header__filter-center{display:flex;flex:1 0 auto;height:100%}.grid-header__filter-right{display:flex;flex:0 0 auto;position:sticky;right:0;z-index:3;background:var(--color-background-primary);box-shadow:-4px 0 8px -2px #0000001a;box-sizing:border-box}.grid-header__filter-cell{display:flex;align-items:center;padding:0 var(--spacing-xs, 4px);box-sizing:border-box;overflow:hidden;height:100%;border-right:1px solid var(--color-border-primary)}.grid-header__filter-cell--spacer{flex-shrink:0;border-right:none}.grid-header__left moz-grid-header-cell:last-child ::ng-deep .grid-header-cell{border-right:none}.grid-header__right moz-grid-header-cell:first-child ::ng-deep .grid-header-cell{border-left:none}.grid-header__filter-cell--last{border-right:none}.grid-header__filter-left .grid-header__filter-cell:last-child{border-right:none}.grid-header__filter-cell--pinned-end{border-right:none;border-left:1px solid var(--color-border-primary)}.grid-header__filter-right .grid-header__filter-cell--pinned-end:first-child{border-left:none}\n"] }]
8260
8313
  }], ctorParameters: () => [], propDecorators: { headerCenter: [{ type: i0.ViewChild, args: ['headerCenter', { isSignal: true }] }], showCheckbox: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCheckbox", required: false }] }], showExpand: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExpand", required: false }] }], reorderable: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorderable", required: false }] }], sortClick: [{ type: i0.Output, args: ["sortClick"] }], menuAction: [{ type: i0.Output, args: ["menuAction"] }], resizeStart: [{ type: i0.Output, args: ["resizeStart"] }], selectAllToggle: [{ type: i0.Output, args: ["selectAllToggle"] }], columnReorder: [{ type: i0.Output, args: ["columnReorder"] }] } });
8261
8314
 
8262
8315
  /**
@@ -8669,6 +8722,7 @@ class MozGridCellComponent {
8669
8722
  cellSelectionEngine = inject(CellSelectionEngine);
8670
8723
  validationEngine = inject(CellValidationEngine);
8671
8724
  clipboard = inject(ClipboardEngine);
8725
+ gridEngine = inject(GridEngine);
8672
8726
  elRef = inject((ElementRef));
8673
8727
  preEditWidth = null;
8674
8728
  constructor() {
@@ -8784,7 +8838,15 @@ class MozGridCellComponent {
8784
8838
  return this.cellSelectionEngine.isCellInFillRejectRange(this.rowIndex(), this.colIndex());
8785
8839
  }, ...(ngDevMode ? [{ debugName: "isInFillRejectRange" }] : /* istanbul ignore next */ []));
8786
8840
  cutEdges = computed(() => this.clipboard.cutEdges(this.rowIndex(), this.colIndex()), ...(ngDevMode ? [{ debugName: "cutEdges" }] : /* istanbul ignore next */ []));
8787
- cellError = computed(() => this.validationEngine.getCellError(this.rowIndex(), this.def().field), ...(ngDevMode ? [{ debugName: "cellError" }] : /* istanbul ignore next */ []));
8841
+ cellError = computed(() => {
8842
+ // Validation errors are keyed by sourceData index, but `rowIndex()` is a
8843
+ // display/paginated index. Resolve to the source index so errors show on
8844
+ // the right cells when sort / filter / grouping is active.
8845
+ const sourceIndex = this.gridEngine.displayIndexToSourceIndex(this.rowIndex());
8846
+ if (sourceIndex < 0)
8847
+ return null;
8848
+ return this.validationEngine.getCellError(sourceIndex, this.def().field);
8849
+ }, ...(ngDevMode ? [{ debugName: "cellError" }] : /* istanbul ignore next */ []));
8788
8850
  onCellClick(event) {
8789
8851
  // Shift+click: extend range from current focused cell to this cell
8790
8852
  if (event.shiftKey) {
@@ -8888,7 +8950,7 @@ class MozGridCellComponent {
8888
8950
  });
8889
8951
  }
8890
8952
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MozGridCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8891
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: MozGridCellComponent, isStandalone: true, selector: "moz-grid-cell", inputs: { row: { classPropertyName: "row", publicName: "row", 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 }, colState: { classPropertyName: "colState", publicName: "colState", isSignal: true, isRequired: true, transformFunction: null }, def: { classPropertyName: "def", publicName: "def", isSignal: true, isRequired: true, transformFunction: null }, isLast: { classPropertyName: "isLast", publicName: "isLast", isSignal: true, isRequired: false, transformFunction: null }, pinnedEnd: { classPropertyName: "pinnedEnd", publicName: "pinnedEnd", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { commitEdit: "commitEdit", cancelEdit: "cancelEdit" }, host: { properties: { "style.flex": "isLast() ? \"1 0 auto\" : \"0 0 auto\"", "style.width.px": "isLast() ? undefined : colState().currentWidth", "style.min-width.px": "isLast() ? colState().currentWidth : resolvedMinWidth()", "style.overflow": "isFocused() && !isEditing() ? \"visible\" : \"hidden\"" } }, ngImport: i0, template: "<div\n class=\"grid-cell\"\n [class.grid-cell--focused]=\"isFocused()\"\n [class.grid-cell--in-range]=\"isInRange()\"\n [class.grid-cell--in-fill-range]=\"isInFillRange()\"\n [class.grid-cell--in-fill-reject-range]=\"isInFillRejectRange()\"\n [class.grid-cell--cut]=\"cutEdges().any\"\n [class.grid-cell--last]=\"isLast()\"\n [class.grid-cell--pinned-end]=\"pinnedEnd()\"\n [class.grid-cell--readonly]=\"!def().editable\"\n [class.grid-cell--error]=\"cellError()\"\n [attr.aria-invalid]=\"cellError() ? 'true' : null\"\n (click)=\"onCellClick($event)\"\n (dblclick)=\"onDoubleClick()\"\n (mousedown)=\"onMouseDown($event)\"\n (mouseenter)=\"onMouseEnter()\"\n>\n @if (isEditing()) {\n <div class=\"grid-cell__editor\" (focusout)=\"onEditorBlur($event)\">\n @if (editTemplate()) {\n <div class=\"grid-cell__editor-custom\">\n <ng-container\n [ngTemplateOutlet]=\"editTemplate()!\"\n [ngTemplateOutletContext]=\"{\n $implicit: value(),\n row: row(),\n field: def().field,\n draft: editState().draftValue,\n updateDraft: updateDraftFn,\n commitEdit: commitEditFn\n }\"\n />\n </div>\n } @else { @switch (editorType()) { @case ('text') {\n <input\n class=\"grid-cell__input grid-cell__input--plain\"\n type=\"text\"\n [value]=\"editState().draftValue\"\n (input)=\"onEditorInput($event)\"\n />\n } @case ('number') {\n <input\n class=\"grid-cell__input grid-cell__input--plain\"\n type=\"number\"\n [value]=\"editState().draftValue\"\n (input)=\"onEditorInput($event)\"\n />\n } @case ('select') {\n <moz-select\n name=\"cell-editor\"\n [options]=\"def().cellEditorOptions ?? []\"\n [ngModel]=\"editState().draftValue\"\n (change)=\"onSelectChange($event)\"\n [size]=\"'s'\"\n />\n } @case ('checkbox') {\n <moz-checkbox\n [id]=\"'grid-cell-editor-' + rowIndex() + '-' + colIndex()\"\n [ngModel]=\"!!editState().draftValue\"\n (change)=\"onCheckboxChange($event)\"\n />\n } @case ('date') {\n <moz-datepicker\n [id]=\"'grid-cell-editor-' + rowIndex() + '-' + colIndex()\"\n size=\"s\"\n [ngModel]=\"editState().draftValue\"\n (ngModelChange)=\"onDateChange($event)\"\n />\n } @default {\n <input\n class=\"grid-cell__input grid-cell__input--plain\"\n type=\"text\"\n [value]=\"editState().draftValue\"\n (input)=\"onEditorInput($event)\"\n />\n } } }\n </div>\n } @else { @if (cellTemplate()) {\n <div class=\"grid-cell__custom\">\n <ng-container\n [ngTemplateOutlet]=\"cellTemplate()!\"\n [ngTemplateOutletContext]=\"{ $implicit: value(), row: row(), field: def().field }\"\n />\n </div>\n } @else {\n <span class=\"grid-cell__value\">{{ displayValue() }}</span>\n } } @if (cutEdges(); as edges) { @if (edges.top) {\n <div class=\"grid-cell__cut-mark grid-cell__cut-mark--top\"></div>\n } @if (edges.bottom) {\n <div class=\"grid-cell__cut-mark grid-cell__cut-mark--bottom\"></div>\n } @if (edges.left) {\n <div class=\"grid-cell__cut-mark grid-cell__cut-mark--left\"></div>\n } @if (edges.right) {\n <div class=\"grid-cell__cut-mark grid-cell__cut-mark--right\"></div>\n } } @if (isFocused() && !isEditing() && def().editable) {\n <div class=\"grid-cell__fill-handle\" (mousedown)=\"onFillHandleMouseDown($event)\"></div>\n } @if (cellError(); as error) {\n <div\n class=\"grid-cell__error-icon\"\n [mozTooltip]=\"error.message\"\n tooltipPosition=\"top\"\n aria-label=\"Erreur de validation\"\n >\n <ErrorFilled24 />\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%;min-width:0}.grid-cell{display:flex;align-items:center;position:relative;padding:0 var(--spacing-s, 8px);height:100%;border-right:1px solid var(--color-border-primary);overflow:hidden;box-sizing:border-box;min-width:0;background:inherit;cursor:pointer}.grid-cell--last{border-right:none}.grid-cell--pinned-end{border-right:none;border-left:1px solid var(--color-border-primary)}:host(:first-child) .grid-cell--pinned-end{border-left:none}.grid-cell__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--font-size-s, 14px);color:var(--color-text-primary);position:relative;z-index:1}.grid-cell__editor{width:100%;min-width:0;max-width:100%;height:100%;display:flex;align-items:center;overflow:hidden;box-sizing:border-box;position:relative;z-index:1}.grid-cell__editor-custom{width:100%;min-width:0;max-width:100%;overflow:hidden;box-sizing:border-box;display:flex;align-items:center;flex-wrap:wrap;gap:4px}.grid-cell__editor input,.grid-cell__editor moz-select{width:100%;min-width:0;box-sizing:border-box}.grid-cell__input--plain{border:none;outline:none;background:transparent;font-family:inherit;font-size:var(--font-size-s, 14px);color:var(--color-text-primary);padding:0;height:100%;width:100%;min-width:0;box-sizing:border-box}.grid-cell__input--plain:focus{outline:none}.grid-cell__input--plain[type=number]::-webkit-inner-spin-button,.grid-cell__input--plain[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.grid-cell__input--plain[type=number]{-moz-appearance:textfield}.grid-cell__editor ::ng-deep .text-input{width:100%;min-width:0;box-sizing:border-box}.grid-cell__editor ::ng-deep .select,.grid-cell__editor ::ng-deep .select__trigger{min-width:0;width:100%}.grid-cell__editor ::ng-deep *{max-width:100%}.grid-cell__editor moz-datepicker{width:100%;min-width:0;box-sizing:border-box}.grid-cell__editor ::ng-deep .mc-datepicker,.grid-cell__editor ::ng-deep .mc-text-input{width:100%;min-width:0;box-sizing:border-box;height:28px;font-size:var(--font-size-xs, 12px)}.grid-cell__editor ::ng-deep moz-datepicker label{display:none}.grid-cell>*:not(.grid-cell__fill-handle){position:relative;z-index:1}.grid-cell:before{content:\"\";position:absolute;inset:3px;border-radius:4px;pointer-events:none;z-index:0}.grid-cell:hover:before{background:#f1f3f4}.grid-cell--focused:hover:before,.grid-cell--in-range:hover:before,.grid-cell--in-fill-range:hover:before,.grid-cell--in-fill-reject-range:hover:before{background:transparent}.grid-cell--focused{z-index:2;overflow:visible}.grid-cell--focused:after{content:\"\";position:absolute;inset:0;border:2px solid var(--color-background-accent-inverse);border-radius:4px;pointer-events:none}.grid-cell--in-range{background:var(--color-background-accent)}.grid-cell--readonly .grid-cell__value{color:var(--color-text-secondary, #666)}.grid-cell--in-fill-range{background:#34a85314}.grid-cell--in-fill-range:after{content:\"\";position:absolute;inset:0;border:1px dashed var(--color-success, #34a853);border-radius:4px;pointer-events:none}.grid-cell--in-fill-reject-range{background:#ea302d1f;cursor:not-allowed;z-index:2}.grid-cell--in-fill-reject-range:after{content:\"\";position:absolute;inset:0;border:2px dashed var(--Status-Standalone-element-Error, #ea302d);border-radius:4px;pointer-events:none;z-index:3}.grid-cell--cut{background:#1a73e80f}.grid-cell__cut-mark{position:absolute;pointer-events:none;z-index:5;--cut-color: var(--color-background-accent-inverse, #1a73e8)}.grid-cell__cut-mark--top,.grid-cell__cut-mark--bottom{left:0;right:0;height:2px;background-image:linear-gradient(90deg,var(--cut-color) 50%,transparent 50%);background-size:8px 2px;background-repeat:repeat-x;animation:moz-grid-marching-ants-x .5s linear infinite}.grid-cell__cut-mark--top{top:0}.grid-cell__cut-mark--bottom{bottom:0}.grid-cell__cut-mark--left,.grid-cell__cut-mark--right{top:0;bottom:0;width:2px;background-image:linear-gradient(180deg,var(--cut-color) 50%,transparent 50%);background-size:2px 8px;background-repeat:repeat-y;animation:moz-grid-marching-ants-y .5s linear infinite}.grid-cell__cut-mark--left{left:0}.grid-cell__cut-mark--right{right:0}@keyframes moz-grid-marching-ants-x{0%{background-position-x:0}to{background-position-x:8px}}@keyframes moz-grid-marching-ants-y{0%{background-position-y:0}to{background-position-y:8px}}.grid-cell__fill-handle{position:absolute;right:-4px;bottom:-4px;width:8px;height:8px;background:var(--color-background-primary, #fff);border:1px solid var(--color-background-accent-inverse);border-radius:2px;cursor:crosshair;z-index:4}.grid-cell--error{background:var(--Background-Primary, #FFF);outline:2px solid var(--Status-Border-Error, #EF5F5C);outline-offset:-2px;z-index:1}.grid-cell--error:hover:before{background:transparent}.grid-cell__error-icon{display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-left:auto;cursor:help;position:relative;z-index:2}.grid-cell__error-icon ::ng-deep svg{fill:var(--Status-Standalone-element-Error, #EA302D)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: MozSelectComponent, selector: "moz-select", inputs: ["id", "name", "options", "placeholder", "isInvalid", "disabled", "readonly", "size"] }, { kind: "component", type: MozCheckboxComponent, selector: "moz-checkbox", inputs: ["id", "name", "label", "indeterminate", "isInvalid", "disabled", "indented"] }, { kind: "component", type: MozDatepickerComponent, selector: "moz-datepicker", inputs: ["id", "disabled", "readonly", "invalid", "error", "clearable", "size", "label"] }, { kind: "directive", type: MozTooltipDirective, selector: "[mozTooltip]", inputs: ["mozTooltip", "tooltipPosition", "tooltipNoPointer"] }, { kind: "component", type: ErrorFilled24, selector: "ErrorFilled24", inputs: ["hostClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
8953
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: MozGridCellComponent, isStandalone: true, selector: "moz-grid-cell", inputs: { row: { classPropertyName: "row", publicName: "row", 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 }, colState: { classPropertyName: "colState", publicName: "colState", isSignal: true, isRequired: true, transformFunction: null }, def: { classPropertyName: "def", publicName: "def", isSignal: true, isRequired: true, transformFunction: null }, isLast: { classPropertyName: "isLast", publicName: "isLast", isSignal: true, isRequired: false, transformFunction: null }, pinnedEnd: { classPropertyName: "pinnedEnd", publicName: "pinnedEnd", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { commitEdit: "commitEdit", cancelEdit: "cancelEdit" }, host: { properties: { "style.flex": "isLast() ? \"1 0 auto\" : \"0 0 auto\"", "style.width.px": "isLast() ? undefined : colState().currentWidth", "style.min-width.px": "isLast() ? colState().currentWidth : resolvedMinWidth()" } }, ngImport: i0, template: "<div\n class=\"grid-cell\"\n [class.grid-cell--focused]=\"isFocused()\"\n [class.grid-cell--in-range]=\"isInRange()\"\n [class.grid-cell--in-fill-range]=\"isInFillRange()\"\n [class.grid-cell--in-fill-reject-range]=\"isInFillRejectRange()\"\n [class.grid-cell--cut]=\"cutEdges().any\"\n [class.grid-cell--last]=\"isLast()\"\n [class.grid-cell--pinned-end]=\"pinnedEnd()\"\n [class.grid-cell--readonly]=\"!def().editable\"\n [class.grid-cell--error]=\"cellError()\"\n [attr.aria-invalid]=\"cellError() ? 'true' : null\"\n (click)=\"onCellClick($event)\"\n (dblclick)=\"onDoubleClick()\"\n (mousedown)=\"onMouseDown($event)\"\n (mouseenter)=\"onMouseEnter()\"\n>\n @if (isEditing()) {\n <div class=\"grid-cell__editor\" (focusout)=\"onEditorBlur($event)\">\n @if (editTemplate()) {\n <div class=\"grid-cell__editor-custom\">\n <ng-container\n [ngTemplateOutlet]=\"editTemplate()!\"\n [ngTemplateOutletContext]=\"{\n $implicit: value(),\n row: row(),\n field: def().field,\n draft: editState().draftValue,\n updateDraft: updateDraftFn,\n commitEdit: commitEditFn\n }\"\n />\n </div>\n } @else { @switch (editorType()) { @case ('text') {\n <input\n class=\"grid-cell__input grid-cell__input--plain\"\n type=\"text\"\n [value]=\"editState().draftValue\"\n (input)=\"onEditorInput($event)\"\n />\n } @case ('number') {\n <input\n class=\"grid-cell__input grid-cell__input--plain\"\n type=\"number\"\n [value]=\"editState().draftValue\"\n (input)=\"onEditorInput($event)\"\n />\n } @case ('select') {\n <moz-select\n name=\"cell-editor\"\n [options]=\"def().cellEditorOptions ?? []\"\n [ngModel]=\"editState().draftValue\"\n (change)=\"onSelectChange($event)\"\n [size]=\"'s'\"\n />\n } @case ('checkbox') {\n <moz-checkbox\n [id]=\"'grid-cell-editor-' + rowIndex() + '-' + colIndex()\"\n [ngModel]=\"!!editState().draftValue\"\n (change)=\"onCheckboxChange($event)\"\n />\n } @case ('date') {\n <moz-datepicker\n [id]=\"'grid-cell-editor-' + rowIndex() + '-' + colIndex()\"\n size=\"s\"\n [ngModel]=\"editState().draftValue\"\n (ngModelChange)=\"onDateChange($event)\"\n />\n } @default {\n <input\n class=\"grid-cell__input grid-cell__input--plain\"\n type=\"text\"\n [value]=\"editState().draftValue\"\n (input)=\"onEditorInput($event)\"\n />\n } } }\n </div>\n } @else { @if (cellTemplate()) {\n <div class=\"grid-cell__custom\">\n <ng-container\n [ngTemplateOutlet]=\"cellTemplate()!\"\n [ngTemplateOutletContext]=\"{ $implicit: value(), row: row(), field: def().field }\"\n />\n </div>\n } @else {\n <span class=\"grid-cell__value\">{{ displayValue() }}</span>\n } } @if (cutEdges(); as edges) { @if (edges.top) {\n <div class=\"grid-cell__cut-mark grid-cell__cut-mark--top\"></div>\n } @if (edges.bottom) {\n <div class=\"grid-cell__cut-mark grid-cell__cut-mark--bottom\"></div>\n } @if (edges.left) {\n <div class=\"grid-cell__cut-mark grid-cell__cut-mark--left\"></div>\n } @if (edges.right) {\n <div class=\"grid-cell__cut-mark grid-cell__cut-mark--right\"></div>\n } } @if (isFocused() && !isEditing() && def().editable) {\n <div class=\"grid-cell__fill-handle\" (mousedown)=\"onFillHandleMouseDown($event)\"></div>\n } @if (cellError(); as error) {\n <div\n class=\"grid-cell__error-icon\"\n [mozTooltip]=\"error.message\"\n tooltipPosition=\"top\"\n aria-label=\"Erreur de validation\"\n >\n <ErrorFilled24 />\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%;min-width:0}.grid-cell{display:flex;align-items:center;position:relative;padding:0 var(--spacing-s, 8px);height:100%;border-right:1px solid var(--color-border-primary);overflow:hidden;box-sizing:border-box;min-width:0;background:inherit;cursor:pointer}.grid-cell--last{border-right:none}.grid-cell--pinned-end{border-right:none;border-left:1px solid var(--color-border-primary)}:host(:first-child) .grid-cell--pinned-end{border-left:none}.grid-cell__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--font-size-s, 14px);color:var(--color-text-primary);position:relative;z-index:1}.grid-cell__editor{width:100%;min-width:0;max-width:100%;height:100%;display:flex;align-items:center;overflow:hidden;box-sizing:border-box;position:relative;z-index:1}.grid-cell__editor-custom{width:100%;min-width:0;max-width:100%;overflow:hidden;box-sizing:border-box;display:flex;align-items:center;flex-wrap:wrap;gap:4px}.grid-cell__editor input,.grid-cell__editor moz-select{width:100%;min-width:0;box-sizing:border-box}.grid-cell__input--plain{border:none;outline:none;background:transparent;font-family:inherit;font-size:var(--font-size-s, 14px);color:var(--color-text-primary);padding:0;height:100%;width:100%;min-width:0;box-sizing:border-box}.grid-cell__input--plain:focus{outline:none}.grid-cell__input--plain[type=number]::-webkit-inner-spin-button,.grid-cell__input--plain[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.grid-cell__input--plain[type=number]{-moz-appearance:textfield}.grid-cell__editor ::ng-deep .text-input{width:100%;min-width:0;box-sizing:border-box}.grid-cell__editor ::ng-deep .select,.grid-cell__editor ::ng-deep .select__trigger{min-width:0;width:100%}.grid-cell__editor ::ng-deep *{max-width:100%}.grid-cell__editor moz-datepicker{width:100%;min-width:0;box-sizing:border-box}.grid-cell__editor ::ng-deep .mc-datepicker,.grid-cell__editor ::ng-deep .mc-text-input{width:100%;min-width:0;box-sizing:border-box;height:28px;font-size:var(--font-size-xs, 12px)}.grid-cell__editor ::ng-deep moz-datepicker label{display:none}.grid-cell>*:not(.grid-cell__fill-handle){position:relative;z-index:1}.grid-cell:before{content:\"\";position:absolute;inset:3px;border-radius:4px;pointer-events:none;z-index:0}.grid-cell:hover:before{background:#f1f3f4}.grid-cell--focused:hover:before,.grid-cell--in-range:hover:before,.grid-cell--in-fill-range:hover:before,.grid-cell--in-fill-reject-range:hover:before{background:transparent}.grid-cell--focused{z-index:2}.grid-cell--focused:after{content:\"\";position:absolute;inset:0;border:2px solid var(--color-background-accent-inverse);border-radius:4px;pointer-events:none}.grid-cell--in-range{background:var(--color-background-accent)}.grid-cell--readonly .grid-cell__value{color:var(--color-text-secondary, #666)}.grid-cell--in-fill-range{background:#34a85314}.grid-cell--in-fill-range:after{content:\"\";position:absolute;inset:0;border:1px dashed var(--color-success, #34a853);border-radius:4px;pointer-events:none}.grid-cell--in-fill-reject-range{background:#ea302d1f;cursor:not-allowed;z-index:2}.grid-cell--in-fill-reject-range:after{content:\"\";position:absolute;inset:0;border:2px dashed var(--Status-Standalone-element-Error, #ea302d);border-radius:4px;pointer-events:none;z-index:3}.grid-cell--cut{background:#1a73e80f}.grid-cell__cut-mark{position:absolute;pointer-events:none;z-index:5;--cut-color: var(--color-background-accent-inverse, #1a73e8)}.grid-cell__cut-mark--top,.grid-cell__cut-mark--bottom{left:0;right:0;height:2px;background-image:linear-gradient(90deg,var(--cut-color) 50%,transparent 50%);background-size:8px 2px;background-repeat:repeat-x;animation:moz-grid-marching-ants-x .5s linear infinite}.grid-cell__cut-mark--top{top:0}.grid-cell__cut-mark--bottom{bottom:0}.grid-cell__cut-mark--left,.grid-cell__cut-mark--right{top:0;bottom:0;width:2px;background-image:linear-gradient(180deg,var(--cut-color) 50%,transparent 50%);background-size:2px 8px;background-repeat:repeat-y;animation:moz-grid-marching-ants-y .5s linear infinite}.grid-cell__cut-mark--left{left:0}.grid-cell__cut-mark--right{right:0}@keyframes moz-grid-marching-ants-x{0%{background-position-x:0}to{background-position-x:8px}}@keyframes moz-grid-marching-ants-y{0%{background-position-y:0}to{background-position-y:8px}}.grid-cell__fill-handle{position:absolute;right:0;bottom:0;width:8px;height:8px;background:var(--color-background-accent-inverse);cursor:crosshair;z-index:4}.grid-cell--error{background:var(--Background-Primary, #fff);outline:1px solid var(--Status-Border-Error, #ef5f5c);outline-offset:-2px;z-index:1;border-radius:4px}.grid-cell--error:hover:before{background:transparent}.grid-cell__error-icon{display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-left:auto;cursor:help;position:relative;z-index:2}.grid-cell__error-icon ::ng-deep svg{fill:var(--Status-Standalone-element-Error, #ea302d)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: MozSelectComponent, selector: "moz-select", inputs: ["id", "name", "options", "placeholder", "isInvalid", "disabled", "readonly", "size"] }, { kind: "component", type: MozCheckboxComponent, selector: "moz-checkbox", inputs: ["id", "name", "label", "indeterminate", "isInvalid", "disabled", "indented"] }, { kind: "component", type: MozDatepickerComponent, selector: "moz-datepicker", inputs: ["id", "disabled", "readonly", "invalid", "error", "clearable", "size", "label"] }, { kind: "directive", type: MozTooltipDirective, selector: "[mozTooltip]", inputs: ["mozTooltip", "tooltipPosition", "tooltipNoPointer"] }, { kind: "component", type: ErrorFilled24, selector: "ErrorFilled24", inputs: ["hostClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
8892
8954
  }
8893
8955
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MozGridCellComponent, decorators: [{
8894
8956
  type: Component,
@@ -8904,8 +8966,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
8904
8966
  '[style.flex]': 'isLast() ? "1 0 auto" : "0 0 auto"',
8905
8967
  '[style.width.px]': 'isLast() ? undefined : colState().currentWidth',
8906
8968
  '[style.min-width.px]': 'isLast() ? colState().currentWidth : resolvedMinWidth()',
8907
- '[style.overflow]': 'isFocused() && !isEditing() ? "visible" : "hidden"',
8908
- }, template: "<div\n class=\"grid-cell\"\n [class.grid-cell--focused]=\"isFocused()\"\n [class.grid-cell--in-range]=\"isInRange()\"\n [class.grid-cell--in-fill-range]=\"isInFillRange()\"\n [class.grid-cell--in-fill-reject-range]=\"isInFillRejectRange()\"\n [class.grid-cell--cut]=\"cutEdges().any\"\n [class.grid-cell--last]=\"isLast()\"\n [class.grid-cell--pinned-end]=\"pinnedEnd()\"\n [class.grid-cell--readonly]=\"!def().editable\"\n [class.grid-cell--error]=\"cellError()\"\n [attr.aria-invalid]=\"cellError() ? 'true' : null\"\n (click)=\"onCellClick($event)\"\n (dblclick)=\"onDoubleClick()\"\n (mousedown)=\"onMouseDown($event)\"\n (mouseenter)=\"onMouseEnter()\"\n>\n @if (isEditing()) {\n <div class=\"grid-cell__editor\" (focusout)=\"onEditorBlur($event)\">\n @if (editTemplate()) {\n <div class=\"grid-cell__editor-custom\">\n <ng-container\n [ngTemplateOutlet]=\"editTemplate()!\"\n [ngTemplateOutletContext]=\"{\n $implicit: value(),\n row: row(),\n field: def().field,\n draft: editState().draftValue,\n updateDraft: updateDraftFn,\n commitEdit: commitEditFn\n }\"\n />\n </div>\n } @else { @switch (editorType()) { @case ('text') {\n <input\n class=\"grid-cell__input grid-cell__input--plain\"\n type=\"text\"\n [value]=\"editState().draftValue\"\n (input)=\"onEditorInput($event)\"\n />\n } @case ('number') {\n <input\n class=\"grid-cell__input grid-cell__input--plain\"\n type=\"number\"\n [value]=\"editState().draftValue\"\n (input)=\"onEditorInput($event)\"\n />\n } @case ('select') {\n <moz-select\n name=\"cell-editor\"\n [options]=\"def().cellEditorOptions ?? []\"\n [ngModel]=\"editState().draftValue\"\n (change)=\"onSelectChange($event)\"\n [size]=\"'s'\"\n />\n } @case ('checkbox') {\n <moz-checkbox\n [id]=\"'grid-cell-editor-' + rowIndex() + '-' + colIndex()\"\n [ngModel]=\"!!editState().draftValue\"\n (change)=\"onCheckboxChange($event)\"\n />\n } @case ('date') {\n <moz-datepicker\n [id]=\"'grid-cell-editor-' + rowIndex() + '-' + colIndex()\"\n size=\"s\"\n [ngModel]=\"editState().draftValue\"\n (ngModelChange)=\"onDateChange($event)\"\n />\n } @default {\n <input\n class=\"grid-cell__input grid-cell__input--plain\"\n type=\"text\"\n [value]=\"editState().draftValue\"\n (input)=\"onEditorInput($event)\"\n />\n } } }\n </div>\n } @else { @if (cellTemplate()) {\n <div class=\"grid-cell__custom\">\n <ng-container\n [ngTemplateOutlet]=\"cellTemplate()!\"\n [ngTemplateOutletContext]=\"{ $implicit: value(), row: row(), field: def().field }\"\n />\n </div>\n } @else {\n <span class=\"grid-cell__value\">{{ displayValue() }}</span>\n } } @if (cutEdges(); as edges) { @if (edges.top) {\n <div class=\"grid-cell__cut-mark grid-cell__cut-mark--top\"></div>\n } @if (edges.bottom) {\n <div class=\"grid-cell__cut-mark grid-cell__cut-mark--bottom\"></div>\n } @if (edges.left) {\n <div class=\"grid-cell__cut-mark grid-cell__cut-mark--left\"></div>\n } @if (edges.right) {\n <div class=\"grid-cell__cut-mark grid-cell__cut-mark--right\"></div>\n } } @if (isFocused() && !isEditing() && def().editable) {\n <div class=\"grid-cell__fill-handle\" (mousedown)=\"onFillHandleMouseDown($event)\"></div>\n } @if (cellError(); as error) {\n <div\n class=\"grid-cell__error-icon\"\n [mozTooltip]=\"error.message\"\n tooltipPosition=\"top\"\n aria-label=\"Erreur de validation\"\n >\n <ErrorFilled24 />\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%;min-width:0}.grid-cell{display:flex;align-items:center;position:relative;padding:0 var(--spacing-s, 8px);height:100%;border-right:1px solid var(--color-border-primary);overflow:hidden;box-sizing:border-box;min-width:0;background:inherit;cursor:pointer}.grid-cell--last{border-right:none}.grid-cell--pinned-end{border-right:none;border-left:1px solid var(--color-border-primary)}:host(:first-child) .grid-cell--pinned-end{border-left:none}.grid-cell__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--font-size-s, 14px);color:var(--color-text-primary);position:relative;z-index:1}.grid-cell__editor{width:100%;min-width:0;max-width:100%;height:100%;display:flex;align-items:center;overflow:hidden;box-sizing:border-box;position:relative;z-index:1}.grid-cell__editor-custom{width:100%;min-width:0;max-width:100%;overflow:hidden;box-sizing:border-box;display:flex;align-items:center;flex-wrap:wrap;gap:4px}.grid-cell__editor input,.grid-cell__editor moz-select{width:100%;min-width:0;box-sizing:border-box}.grid-cell__input--plain{border:none;outline:none;background:transparent;font-family:inherit;font-size:var(--font-size-s, 14px);color:var(--color-text-primary);padding:0;height:100%;width:100%;min-width:0;box-sizing:border-box}.grid-cell__input--plain:focus{outline:none}.grid-cell__input--plain[type=number]::-webkit-inner-spin-button,.grid-cell__input--plain[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.grid-cell__input--plain[type=number]{-moz-appearance:textfield}.grid-cell__editor ::ng-deep .text-input{width:100%;min-width:0;box-sizing:border-box}.grid-cell__editor ::ng-deep .select,.grid-cell__editor ::ng-deep .select__trigger{min-width:0;width:100%}.grid-cell__editor ::ng-deep *{max-width:100%}.grid-cell__editor moz-datepicker{width:100%;min-width:0;box-sizing:border-box}.grid-cell__editor ::ng-deep .mc-datepicker,.grid-cell__editor ::ng-deep .mc-text-input{width:100%;min-width:0;box-sizing:border-box;height:28px;font-size:var(--font-size-xs, 12px)}.grid-cell__editor ::ng-deep moz-datepicker label{display:none}.grid-cell>*:not(.grid-cell__fill-handle){position:relative;z-index:1}.grid-cell:before{content:\"\";position:absolute;inset:3px;border-radius:4px;pointer-events:none;z-index:0}.grid-cell:hover:before{background:#f1f3f4}.grid-cell--focused:hover:before,.grid-cell--in-range:hover:before,.grid-cell--in-fill-range:hover:before,.grid-cell--in-fill-reject-range:hover:before{background:transparent}.grid-cell--focused{z-index:2;overflow:visible}.grid-cell--focused:after{content:\"\";position:absolute;inset:0;border:2px solid var(--color-background-accent-inverse);border-radius:4px;pointer-events:none}.grid-cell--in-range{background:var(--color-background-accent)}.grid-cell--readonly .grid-cell__value{color:var(--color-text-secondary, #666)}.grid-cell--in-fill-range{background:#34a85314}.grid-cell--in-fill-range:after{content:\"\";position:absolute;inset:0;border:1px dashed var(--color-success, #34a853);border-radius:4px;pointer-events:none}.grid-cell--in-fill-reject-range{background:#ea302d1f;cursor:not-allowed;z-index:2}.grid-cell--in-fill-reject-range:after{content:\"\";position:absolute;inset:0;border:2px dashed var(--Status-Standalone-element-Error, #ea302d);border-radius:4px;pointer-events:none;z-index:3}.grid-cell--cut{background:#1a73e80f}.grid-cell__cut-mark{position:absolute;pointer-events:none;z-index:5;--cut-color: var(--color-background-accent-inverse, #1a73e8)}.grid-cell__cut-mark--top,.grid-cell__cut-mark--bottom{left:0;right:0;height:2px;background-image:linear-gradient(90deg,var(--cut-color) 50%,transparent 50%);background-size:8px 2px;background-repeat:repeat-x;animation:moz-grid-marching-ants-x .5s linear infinite}.grid-cell__cut-mark--top{top:0}.grid-cell__cut-mark--bottom{bottom:0}.grid-cell__cut-mark--left,.grid-cell__cut-mark--right{top:0;bottom:0;width:2px;background-image:linear-gradient(180deg,var(--cut-color) 50%,transparent 50%);background-size:2px 8px;background-repeat:repeat-y;animation:moz-grid-marching-ants-y .5s linear infinite}.grid-cell__cut-mark--left{left:0}.grid-cell__cut-mark--right{right:0}@keyframes moz-grid-marching-ants-x{0%{background-position-x:0}to{background-position-x:8px}}@keyframes moz-grid-marching-ants-y{0%{background-position-y:0}to{background-position-y:8px}}.grid-cell__fill-handle{position:absolute;right:-4px;bottom:-4px;width:8px;height:8px;background:var(--color-background-primary, #fff);border:1px solid var(--color-background-accent-inverse);border-radius:2px;cursor:crosshair;z-index:4}.grid-cell--error{background:var(--Background-Primary, #FFF);outline:2px solid var(--Status-Border-Error, #EF5F5C);outline-offset:-2px;z-index:1}.grid-cell--error:hover:before{background:transparent}.grid-cell__error-icon{display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-left:auto;cursor:help;position:relative;z-index:2}.grid-cell__error-icon ::ng-deep svg{fill:var(--Status-Standalone-element-Error, #EA302D)}\n"] }]
8969
+ }, template: "<div\n class=\"grid-cell\"\n [class.grid-cell--focused]=\"isFocused()\"\n [class.grid-cell--in-range]=\"isInRange()\"\n [class.grid-cell--in-fill-range]=\"isInFillRange()\"\n [class.grid-cell--in-fill-reject-range]=\"isInFillRejectRange()\"\n [class.grid-cell--cut]=\"cutEdges().any\"\n [class.grid-cell--last]=\"isLast()\"\n [class.grid-cell--pinned-end]=\"pinnedEnd()\"\n [class.grid-cell--readonly]=\"!def().editable\"\n [class.grid-cell--error]=\"cellError()\"\n [attr.aria-invalid]=\"cellError() ? 'true' : null\"\n (click)=\"onCellClick($event)\"\n (dblclick)=\"onDoubleClick()\"\n (mousedown)=\"onMouseDown($event)\"\n (mouseenter)=\"onMouseEnter()\"\n>\n @if (isEditing()) {\n <div class=\"grid-cell__editor\" (focusout)=\"onEditorBlur($event)\">\n @if (editTemplate()) {\n <div class=\"grid-cell__editor-custom\">\n <ng-container\n [ngTemplateOutlet]=\"editTemplate()!\"\n [ngTemplateOutletContext]=\"{\n $implicit: value(),\n row: row(),\n field: def().field,\n draft: editState().draftValue,\n updateDraft: updateDraftFn,\n commitEdit: commitEditFn\n }\"\n />\n </div>\n } @else { @switch (editorType()) { @case ('text') {\n <input\n class=\"grid-cell__input grid-cell__input--plain\"\n type=\"text\"\n [value]=\"editState().draftValue\"\n (input)=\"onEditorInput($event)\"\n />\n } @case ('number') {\n <input\n class=\"grid-cell__input grid-cell__input--plain\"\n type=\"number\"\n [value]=\"editState().draftValue\"\n (input)=\"onEditorInput($event)\"\n />\n } @case ('select') {\n <moz-select\n name=\"cell-editor\"\n [options]=\"def().cellEditorOptions ?? []\"\n [ngModel]=\"editState().draftValue\"\n (change)=\"onSelectChange($event)\"\n [size]=\"'s'\"\n />\n } @case ('checkbox') {\n <moz-checkbox\n [id]=\"'grid-cell-editor-' + rowIndex() + '-' + colIndex()\"\n [ngModel]=\"!!editState().draftValue\"\n (change)=\"onCheckboxChange($event)\"\n />\n } @case ('date') {\n <moz-datepicker\n [id]=\"'grid-cell-editor-' + rowIndex() + '-' + colIndex()\"\n size=\"s\"\n [ngModel]=\"editState().draftValue\"\n (ngModelChange)=\"onDateChange($event)\"\n />\n } @default {\n <input\n class=\"grid-cell__input grid-cell__input--plain\"\n type=\"text\"\n [value]=\"editState().draftValue\"\n (input)=\"onEditorInput($event)\"\n />\n } } }\n </div>\n } @else { @if (cellTemplate()) {\n <div class=\"grid-cell__custom\">\n <ng-container\n [ngTemplateOutlet]=\"cellTemplate()!\"\n [ngTemplateOutletContext]=\"{ $implicit: value(), row: row(), field: def().field }\"\n />\n </div>\n } @else {\n <span class=\"grid-cell__value\">{{ displayValue() }}</span>\n } } @if (cutEdges(); as edges) { @if (edges.top) {\n <div class=\"grid-cell__cut-mark grid-cell__cut-mark--top\"></div>\n } @if (edges.bottom) {\n <div class=\"grid-cell__cut-mark grid-cell__cut-mark--bottom\"></div>\n } @if (edges.left) {\n <div class=\"grid-cell__cut-mark grid-cell__cut-mark--left\"></div>\n } @if (edges.right) {\n <div class=\"grid-cell__cut-mark grid-cell__cut-mark--right\"></div>\n } } @if (isFocused() && !isEditing() && def().editable) {\n <div class=\"grid-cell__fill-handle\" (mousedown)=\"onFillHandleMouseDown($event)\"></div>\n } @if (cellError(); as error) {\n <div\n class=\"grid-cell__error-icon\"\n [mozTooltip]=\"error.message\"\n tooltipPosition=\"top\"\n aria-label=\"Erreur de validation\"\n >\n <ErrorFilled24 />\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%;min-width:0}.grid-cell{display:flex;align-items:center;position:relative;padding:0 var(--spacing-s, 8px);height:100%;border-right:1px solid var(--color-border-primary);overflow:hidden;box-sizing:border-box;min-width:0;background:inherit;cursor:pointer}.grid-cell--last{border-right:none}.grid-cell--pinned-end{border-right:none;border-left:1px solid var(--color-border-primary)}:host(:first-child) .grid-cell--pinned-end{border-left:none}.grid-cell__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--font-size-s, 14px);color:var(--color-text-primary);position:relative;z-index:1}.grid-cell__editor{width:100%;min-width:0;max-width:100%;height:100%;display:flex;align-items:center;overflow:hidden;box-sizing:border-box;position:relative;z-index:1}.grid-cell__editor-custom{width:100%;min-width:0;max-width:100%;overflow:hidden;box-sizing:border-box;display:flex;align-items:center;flex-wrap:wrap;gap:4px}.grid-cell__editor input,.grid-cell__editor moz-select{width:100%;min-width:0;box-sizing:border-box}.grid-cell__input--plain{border:none;outline:none;background:transparent;font-family:inherit;font-size:var(--font-size-s, 14px);color:var(--color-text-primary);padding:0;height:100%;width:100%;min-width:0;box-sizing:border-box}.grid-cell__input--plain:focus{outline:none}.grid-cell__input--plain[type=number]::-webkit-inner-spin-button,.grid-cell__input--plain[type=number]::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.grid-cell__input--plain[type=number]{-moz-appearance:textfield}.grid-cell__editor ::ng-deep .text-input{width:100%;min-width:0;box-sizing:border-box}.grid-cell__editor ::ng-deep .select,.grid-cell__editor ::ng-deep .select__trigger{min-width:0;width:100%}.grid-cell__editor ::ng-deep *{max-width:100%}.grid-cell__editor moz-datepicker{width:100%;min-width:0;box-sizing:border-box}.grid-cell__editor ::ng-deep .mc-datepicker,.grid-cell__editor ::ng-deep .mc-text-input{width:100%;min-width:0;box-sizing:border-box;height:28px;font-size:var(--font-size-xs, 12px)}.grid-cell__editor ::ng-deep moz-datepicker label{display:none}.grid-cell>*:not(.grid-cell__fill-handle){position:relative;z-index:1}.grid-cell:before{content:\"\";position:absolute;inset:3px;border-radius:4px;pointer-events:none;z-index:0}.grid-cell:hover:before{background:#f1f3f4}.grid-cell--focused:hover:before,.grid-cell--in-range:hover:before,.grid-cell--in-fill-range:hover:before,.grid-cell--in-fill-reject-range:hover:before{background:transparent}.grid-cell--focused{z-index:2}.grid-cell--focused:after{content:\"\";position:absolute;inset:0;border:2px solid var(--color-background-accent-inverse);border-radius:4px;pointer-events:none}.grid-cell--in-range{background:var(--color-background-accent)}.grid-cell--readonly .grid-cell__value{color:var(--color-text-secondary, #666)}.grid-cell--in-fill-range{background:#34a85314}.grid-cell--in-fill-range:after{content:\"\";position:absolute;inset:0;border:1px dashed var(--color-success, #34a853);border-radius:4px;pointer-events:none}.grid-cell--in-fill-reject-range{background:#ea302d1f;cursor:not-allowed;z-index:2}.grid-cell--in-fill-reject-range:after{content:\"\";position:absolute;inset:0;border:2px dashed var(--Status-Standalone-element-Error, #ea302d);border-radius:4px;pointer-events:none;z-index:3}.grid-cell--cut{background:#1a73e80f}.grid-cell__cut-mark{position:absolute;pointer-events:none;z-index:5;--cut-color: var(--color-background-accent-inverse, #1a73e8)}.grid-cell__cut-mark--top,.grid-cell__cut-mark--bottom{left:0;right:0;height:2px;background-image:linear-gradient(90deg,var(--cut-color) 50%,transparent 50%);background-size:8px 2px;background-repeat:repeat-x;animation:moz-grid-marching-ants-x .5s linear infinite}.grid-cell__cut-mark--top{top:0}.grid-cell__cut-mark--bottom{bottom:0}.grid-cell__cut-mark--left,.grid-cell__cut-mark--right{top:0;bottom:0;width:2px;background-image:linear-gradient(180deg,var(--cut-color) 50%,transparent 50%);background-size:2px 8px;background-repeat:repeat-y;animation:moz-grid-marching-ants-y .5s linear infinite}.grid-cell__cut-mark--left{left:0}.grid-cell__cut-mark--right{right:0}@keyframes moz-grid-marching-ants-x{0%{background-position-x:0}to{background-position-x:8px}}@keyframes moz-grid-marching-ants-y{0%{background-position-y:0}to{background-position-y:8px}}.grid-cell__fill-handle{position:absolute;right:0;bottom:0;width:8px;height:8px;background:var(--color-background-accent-inverse);cursor:crosshair;z-index:4}.grid-cell--error{background:var(--Background-Primary, #fff);outline:1px solid var(--Status-Border-Error, #ef5f5c);outline-offset:-2px;z-index:1;border-radius:4px}.grid-cell--error:hover:before{background:transparent}.grid-cell__error-icon{display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-left:auto;cursor:help;position:relative;z-index:2}.grid-cell__error-icon ::ng-deep svg{fill:var(--Status-Standalone-element-Error, #ea302d)}\n"] }]
8909
8970
  }], ctorParameters: () => [], propDecorators: { row: [{ type: i0.Input, args: [{ isSignal: true, alias: "row", required: true }] }], rowIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowIndex", required: true }] }], colIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "colIndex", required: true }] }], colState: [{ type: i0.Input, args: [{ isSignal: true, alias: "colState", required: true }] }], def: [{ type: i0.Input, args: [{ isSignal: true, alias: "def", required: true }] }], isLast: [{ type: i0.Input, args: [{ isSignal: true, alias: "isLast", required: false }] }], pinnedEnd: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinnedEnd", required: false }] }], commitEdit: [{ type: i0.Output, args: ["commitEdit"] }], cancelEdit: [{ type: i0.Output, args: ["cancelEdit"] }] } });
8910
8971
 
8911
8972
  class MozGridRowComponent {
@@ -8945,12 +9006,12 @@ class MozGridRowComponent {
8945
9006
  }
8946
9007
  onCheckboxClick(event) {
8947
9008
  event.stopPropagation();
8948
- if (event.shiftKey && this.rowSelection.lastToggledIndex() >= 0) {
8949
- this.rowSelection.selectRowRange(this.rowSelection.lastToggledIndex(), this.rowIndex());
9009
+ if (event.shiftKey && this.rowSelection.lastToggledRow() !== null) {
9010
+ this.rowSelection.selectRowRangeToRow(this.row());
8950
9011
  }
8951
9012
  else {
8952
9013
  this.rowSelection.toggleRow(this.row());
8953
- this.rowSelection.lastToggledIndex.set(this.rowIndex());
9014
+ this.rowSelection.lastToggledRow.set(this.row());
8954
9015
  }
8955
9016
  this.rowSelectionToggle.emit();
8956
9017
  }
@@ -8993,11 +9054,11 @@ class MozGridGroupRowComponent {
8993
9054
  return String(this.groupRow().value ?? '');
8994
9055
  }, ...(ngDevMode ? [{ debugName: "groupValue" }] : /* istanbul ignore next */ []));
8995
9056
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MozGridGroupRowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
8996
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: MozGridGroupRowComponent, isStandalone: true, selector: "moz-grid-group-row", inputs: { groupRow: { classPropertyName: "groupRow", publicName: "groupRow", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { toggleExpand: "toggleExpand" }, ngImport: i0, template: "<div\n class=\"grid-group-row\"\n role=\"row\"\n [style.padding-left.px]=\"groupRow().depth * 24 + 16\"\n (click)=\"toggleExpand.emit(groupRow().groupKey)\"\n>\n <span\n class=\"grid-group-row__toggle\"\n [class.grid-group-row__toggle--expanded]=\"groupRow().expanded\"\n >\n <ChevronRight20 />\n </span>\n <div class=\"grid-group-row__info\">\n <span class=\"grid-group-row__field\">{{ fieldLabel() }}</span>\n <span class=\"grid-group-row__value\">{{ groupValue() }}</span>\n </div>\n <span class=\"grid-group-row__count\">{{ groupRow().count }}</span>\n</div>\n", styles: [":host{display:block;position:sticky;left:0;overflow:hidden;box-sizing:border-box}.grid-group-row{display:flex;align-items:center;gap:var(--spacing-s, 8px);height:73px;background:var(--color-background-secondary);border-bottom:1px solid var(--color-border-primary);cursor:pointer;-webkit-user-select:none;user-select:none;box-sizing:border-box}.grid-group-row:hover{background:var(--color-background-tertiary, #f0f0f0)}.grid-group-row__toggle{display:flex;align-items:center;color:var(--color-text-secondary);flex-shrink:0;transition:transform .2s ease}.grid-group-row__toggle--expanded{transform:rotate(90deg)}.grid-group-row__info{display:flex;flex-direction:column;gap:2px;min-width:0}.grid-group-row__field{font-size:var(--font-size-xs, 12px);font-weight:500;color:var(--color-text-secondary);text-transform:uppercase;letter-spacing:.5px}.grid-group-row__value{font-size:var(--font-size-l, 18px);font-weight:700;color:var(--color-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.grid-group-row__count{margin-left:auto;align-self:center;font-size:var(--font-size-s, 14px);color:var(--color-text-secondary);font-weight:400;padding-right:16px}\n"], dependencies: [{ kind: "component", type: ChevronRight20, selector: "ChevronRight20", inputs: ["hostClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9057
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.9", type: MozGridGroupRowComponent, isStandalone: true, selector: "moz-grid-group-row", inputs: { groupRow: { classPropertyName: "groupRow", publicName: "groupRow", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { toggleExpand: "toggleExpand" }, ngImport: i0, template: "<div\n class=\"grid-group-row\"\n role=\"row\"\n [style.padding-left.px]=\"groupRow().depth * 24 + 16\"\n (click)=\"toggleExpand.emit(groupRow().groupKey)\"\n>\n <span\n class=\"grid-group-row__toggle\"\n [class.grid-group-row__toggle--expanded]=\"groupRow().expanded\"\n >\n <ChevronRight20 />\n </span>\n <div class=\"grid-group-row__info\">\n <span class=\"grid-group-row__field\">{{ fieldLabel() }}</span>\n <span class=\"grid-group-row__value\">{{ groupValue() }}</span>\n </div>\n <span class=\"grid-group-row__count\">{{ groupRow().count }}</span>\n</div>\n", styles: [":host{display:block;position:sticky;left:0;overflow:hidden;box-sizing:border-box}.grid-group-row{display:flex;align-items:center;gap:var(--spacing-s, 8px);height:73px;background:var(--color-background-secondary);border-bottom:1px solid var(--color-border-primary);cursor:pointer;-webkit-user-select:none;user-select:none;box-sizing:border-box}.grid-group-row:hover{background:var(--color-background-tertiary, #f0f0f0)}.grid-group-row__toggle{display:flex;align-items:center;color:var(--color-text-secondary);flex-shrink:0;transition:transform .2s ease}.grid-group-row__toggle--expanded{transform:rotate(90deg)}.grid-group-row__info{display:flex;flex-direction:column;gap:2px;min-width:0}.grid-group-row__field{font-size:var(--font-size-xs, 12px);font-weight:500;color:var(--color-text-secondary);text-transform:uppercase;letter-spacing:.5px}.grid-group-row__value{font-size:var(--font-size-l, 18px);font-weight:700;color:var(--color-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.grid-group-row__count{align-self:center;font-size:var(--font-size-s, 14px);color:var(--color-text-secondary);font-weight:400;padding-right:16px;padding-left:12px}\n"], dependencies: [{ kind: "component", type: ChevronRight20, selector: "ChevronRight20", inputs: ["hostClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
8997
9058
  }
8998
9059
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MozGridGroupRowComponent, decorators: [{
8999
9060
  type: Component,
9000
- args: [{ selector: 'moz-grid-group-row', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ChevronRight20], template: "<div\n class=\"grid-group-row\"\n role=\"row\"\n [style.padding-left.px]=\"groupRow().depth * 24 + 16\"\n (click)=\"toggleExpand.emit(groupRow().groupKey)\"\n>\n <span\n class=\"grid-group-row__toggle\"\n [class.grid-group-row__toggle--expanded]=\"groupRow().expanded\"\n >\n <ChevronRight20 />\n </span>\n <div class=\"grid-group-row__info\">\n <span class=\"grid-group-row__field\">{{ fieldLabel() }}</span>\n <span class=\"grid-group-row__value\">{{ groupValue() }}</span>\n </div>\n <span class=\"grid-group-row__count\">{{ groupRow().count }}</span>\n</div>\n", styles: [":host{display:block;position:sticky;left:0;overflow:hidden;box-sizing:border-box}.grid-group-row{display:flex;align-items:center;gap:var(--spacing-s, 8px);height:73px;background:var(--color-background-secondary);border-bottom:1px solid var(--color-border-primary);cursor:pointer;-webkit-user-select:none;user-select:none;box-sizing:border-box}.grid-group-row:hover{background:var(--color-background-tertiary, #f0f0f0)}.grid-group-row__toggle{display:flex;align-items:center;color:var(--color-text-secondary);flex-shrink:0;transition:transform .2s ease}.grid-group-row__toggle--expanded{transform:rotate(90deg)}.grid-group-row__info{display:flex;flex-direction:column;gap:2px;min-width:0}.grid-group-row__field{font-size:var(--font-size-xs, 12px);font-weight:500;color:var(--color-text-secondary);text-transform:uppercase;letter-spacing:.5px}.grid-group-row__value{font-size:var(--font-size-l, 18px);font-weight:700;color:var(--color-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.grid-group-row__count{margin-left:auto;align-self:center;font-size:var(--font-size-s, 14px);color:var(--color-text-secondary);font-weight:400;padding-right:16px}\n"] }]
9061
+ args: [{ selector: 'moz-grid-group-row', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ChevronRight20], template: "<div\n class=\"grid-group-row\"\n role=\"row\"\n [style.padding-left.px]=\"groupRow().depth * 24 + 16\"\n (click)=\"toggleExpand.emit(groupRow().groupKey)\"\n>\n <span\n class=\"grid-group-row__toggle\"\n [class.grid-group-row__toggle--expanded]=\"groupRow().expanded\"\n >\n <ChevronRight20 />\n </span>\n <div class=\"grid-group-row__info\">\n <span class=\"grid-group-row__field\">{{ fieldLabel() }}</span>\n <span class=\"grid-group-row__value\">{{ groupValue() }}</span>\n </div>\n <span class=\"grid-group-row__count\">{{ groupRow().count }}</span>\n</div>\n", styles: [":host{display:block;position:sticky;left:0;overflow:hidden;box-sizing:border-box}.grid-group-row{display:flex;align-items:center;gap:var(--spacing-s, 8px);height:73px;background:var(--color-background-secondary);border-bottom:1px solid var(--color-border-primary);cursor:pointer;-webkit-user-select:none;user-select:none;box-sizing:border-box}.grid-group-row:hover{background:var(--color-background-tertiary, #f0f0f0)}.grid-group-row__toggle{display:flex;align-items:center;color:var(--color-text-secondary);flex-shrink:0;transition:transform .2s ease}.grid-group-row__toggle--expanded{transform:rotate(90deg)}.grid-group-row__info{display:flex;flex-direction:column;gap:2px;min-width:0}.grid-group-row__field{font-size:var(--font-size-xs, 12px);font-weight:500;color:var(--color-text-secondary);text-transform:uppercase;letter-spacing:.5px}.grid-group-row__value{font-size:var(--font-size-l, 18px);font-weight:700;color:var(--color-text-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.grid-group-row__count{align-self:center;font-size:var(--font-size-s, 14px);color:var(--color-text-secondary);font-weight:400;padding-right:16px;padding-left:12px}\n"] }]
9001
9062
  }], propDecorators: { groupRow: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupRow", required: true }] }], toggleExpand: [{ type: i0.Output, args: ["toggleExpand"] }] } });
9002
9063
 
9003
9064
  class MozGridDetailRowComponent {
@@ -9318,7 +9379,7 @@ class GridSettingsDrawerComponent {
9318
9379
  this.searchQuery.set('');
9319
9380
  }
9320
9381
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GridSettingsDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9321
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: GridSettingsDrawerComponent, isStandalone: true, selector: "moz-grid-settings-drawer", ngImport: i0, template: "@switch (screen()) { @case ('main') {\n<div class=\"settings-list\">\n <button type=\"button\" class=\"settings-list__item\" (click)=\"goTo('density')\">\n <div class=\"settings-list__item-text\">\n <span class=\"settings-list__item-title\">Data density</span>\n <span class=\"settings-list__item-subtitle\">{{ densityLabel() }}</span>\n </div>\n <ChevronRight20 />\n </button>\n <button type=\"button\" class=\"settings-list__item\" (click)=\"goTo('columns')\">\n <div class=\"settings-list__item-text\">\n <span class=\"settings-list__item-title\">Display columns</span>\n <span class=\"settings-list__item-subtitle\">{{ columnsLabel() }}</span>\n </div>\n <ChevronRight20 />\n </button>\n</div>\n} @case ('density') {\n<div class=\"settings-density\">\n <label class=\"settings-density__label\">Data density</label>\n <moz-select\n name=\"density\"\n [options]=\"densityOptions\"\n [ngModel]=\"draftDensity()\"\n (ngModelChange)=\"draftDensity.set($event)\"\n />\n</div>\n} @case ('columns') {\n<div class=\"settings-columns\">\n <input\n class=\"settings-columns__search\"\n type=\"text\"\n placeholder=\"Find a column\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($event)\"\n aria-label=\"Search columns\"\n />\n <div class=\"settings-columns__list\" cdkDropList (cdkDropListDropped)=\"onColumnDrop($event)\">\n @for (col of filteredColumns(); track col.field) {\n <div class=\"settings-columns__item\" cdkDrag>\n <div class=\"settings-columns__item-left\">\n <span class=\"settings-columns__drag-handle\" cdkDragHandle>\n <Drag20 />\n </span>\n <span class=\"settings-columns__item-label\">{{ col.headerName }}</span>\n </div>\n <moz-toggle\n [id]=\"'col-toggle-' + col.field\"\n [ngModel]=\"col.visible\"\n (ngModelChange)=\"onColumnToggle(col.field, $event)\"\n />\n </div>\n }\n </div>\n <div class=\"settings-columns__bulk-actions\">\n <button type=\"button\" class=\"settings-columns__bulk-btn\" (click)=\"hideAll()\">\n Hide all\n </button>\n <button type=\"button\" class=\"settings-columns__bulk-btn\" (click)=\"showAll()\">\n Show all\n </button>\n </div>\n</div>\n} }\n\n<ng-template mozDrawerFooter>\n <button moz-button (click)=\"apply()\">Apply</button>\n <button moz-button [outlined]=\"true\" (click)=\"reset()\">Reset</button>\n</ng-template>\n", styles: [".settings-list{display:flex;flex-direction:column;border:1px solid var(--color-border-primary);border-radius:var(--border-radius-m, 8px);overflow:hidden}.settings-list__item{display:flex;align-items:center;justify-content:space-between;padding:16px;background:var(--color-background-primary);border:none;border-bottom:1px solid var(--color-border-primary);cursor:pointer;text-align:left;width:100%}.settings-list__item:last-child{border-bottom:none}.settings-list__item:hover{background:var(--color-background-secondary)}.settings-list__item-text{display:flex;flex-direction:column;gap:2px}.settings-list__item-title{font-weight:600;font-size:var(--font-size-m, 16px);color:var(--color-text-primary)}.settings-list__item-subtitle{font-size:var(--font-size-s, 14px);color:var(--color-text-secondary)}.settings-density{display:flex;flex-direction:column;gap:8px}.settings-density__label{font-weight:600;font-size:var(--font-size-s, 14px);color:var(--color-text-primary)}.settings-columns{display:flex;flex-direction:column;gap:12px}.settings-columns__search{width:100%;padding:8px 12px;border:1px solid var(--color-border-primary);border-radius:var(--border-radius-s, 4px);font-size:var(--font-size-s, 14px);color:var(--color-text-primary);background:var(--color-background-primary, #fff);box-sizing:border-box}.settings-columns__search::placeholder{color:var(--color-text-secondary)}.settings-columns__search:focus{outline:2px solid var(--color-background-accent-inverse);outline-offset:-1px}.settings-columns__list{display:flex;flex-direction:column}.settings-columns__bulk-actions{display:flex;gap:8px;padding-top:8px;border-top:1px solid var(--color-border-primary)}.settings-columns__bulk-btn{flex:1;padding:8px 12px;border:1px solid var(--color-border-primary);border-radius:var(--border-radius-s, 4px);background:var(--color-background-primary, #fff);font-size:var(--font-size-s, 14px);font-weight:500;color:var(--color-text-primary);cursor:pointer}.settings-columns__bulk-btn:hover{background:var(--color-background-secondary, #f5f5f5)}.settings-columns__item{display:flex;align-items:center;justify-content:space-between;padding:16px 0;border-bottom:1px solid var(--color-border-primary);background:var(--color-background-primary, #fff)}.settings-columns__item:last-child{border-bottom:none}.settings-columns__item-left{display:flex;align-items:center;gap:8px}.settings-columns__drag-handle{display:flex;align-items:center;cursor:grab;color:var(--color-text-secondary)}.settings-columns__drag-handle:active{cursor:grabbing}.settings-columns__item-label{font-size:var(--font-size-m, 16px);color:var(--color-text-primary)}.cdk-drag-preview{display:flex;align-items:center;justify-content:space-between;padding:16px 8px;background:var(--color-background-primary, #fff);border:1px solid var(--color-border-primary);border-radius:4px;box-shadow:0 4px 12px #00000026;font-size:var(--font-size-m, 16px)}.cdk-drag-placeholder{opacity:.3}.cdk-drag-animating{transition:transform .2s ease}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: MozButtonComponent, selector: "button[moz-button]", inputs: ["appearance", "size", "disabled", "ghost", "outlined", "iconPosition", "type", "isLoading"] }, { kind: "directive", type: MozDrawerFooterDirective, selector: "[mozDrawerFooter]" }, { kind: "component", type: MozSelectComponent, selector: "moz-select", inputs: ["id", "name", "options", "placeholder", "isInvalid", "disabled", "readonly", "size"] }, { kind: "component", type: MozToggleComponent, selector: "moz-toggle", inputs: ["id", "name", "size", "disabled"] }, { kind: "component", type: ChevronRight20, selector: "ChevronRight20", inputs: ["hostClass"] }, { kind: "component", type: Drag20, selector: "Drag20", inputs: ["hostClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9382
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: GridSettingsDrawerComponent, isStandalone: true, selector: "moz-grid-settings-drawer", ngImport: i0, template: "@switch (screen()) { @case ('main') {\n<div class=\"settings-list\">\n <button type=\"button\" class=\"settings-list__item\" (click)=\"goTo('density')\">\n <div class=\"settings-list__item-text\">\n <span class=\"settings-list__item-title\">Data density</span>\n <span class=\"settings-list__item-subtitle\">{{ densityLabel() }}</span>\n </div>\n <ChevronRight20 />\n </button>\n <button type=\"button\" class=\"settings-list__item\" (click)=\"goTo('columns')\">\n <div class=\"settings-list__item-text\">\n <span class=\"settings-list__item-title\">Display columns</span>\n <span class=\"settings-list__item-subtitle\">{{ columnsLabel() }}</span>\n </div>\n <ChevronRight20 />\n </button>\n</div>\n} @case ('density') {\n<div class=\"settings-density\">\n <label class=\"settings-density__label\">Data density</label>\n <moz-select\n name=\"density\"\n [options]=\"densityOptions\"\n [ngModel]=\"draftDensity()\"\n (ngModelChange)=\"draftDensity.set($event)\"\n />\n</div>\n} @case ('columns') {\n<div class=\"settings-columns\">\n <input\n class=\"settings-columns__search\"\n type=\"text\"\n placeholder=\"Find a column\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($event)\"\n aria-label=\"Search columns\"\n />\n <div class=\"settings-columns__list\" cdkDropList (cdkDropListDropped)=\"onColumnDrop($event)\">\n @for (col of filteredColumns(); track col.field) {\n <div class=\"settings-columns__item\" cdkDrag>\n <div class=\"settings-columns__item-left\">\n <span class=\"settings-columns__drag-handle\" cdkDragHandle>\n <Drag20 />\n </span>\n <span class=\"settings-columns__item-label\">{{ col.headerName }}</span>\n </div>\n <moz-toggle\n [id]=\"'col-toggle-' + col.field\"\n [ngModel]=\"col.visible\"\n (ngModelChange)=\"onColumnToggle(col.field, $event)\"\n />\n </div>\n }\n </div>\n <div class=\"settings-columns__bulk-actions\">\n <button type=\"button\" class=\"settings-columns__bulk-btn\" (click)=\"hideAll()\">Hide all</button>\n <button type=\"button\" class=\"settings-columns__bulk-btn\" (click)=\"showAll()\">Show all</button>\n </div>\n</div>\n} }\n\n<ng-template mozDrawerFooter>\n <button moz-button (click)=\"apply()\" [appearance]=\"'accent'\">Apply</button>\n <button moz-button [outlined]=\"true\" (click)=\"reset()\">Reset</button>\n</ng-template>\n", styles: [".settings-list{display:flex;flex-direction:column;border:1px solid var(--color-border-primary);border-radius:var(--border-radius-m, 8px);overflow:hidden}.settings-list__item{display:flex;align-items:center;justify-content:space-between;padding:16px;background:var(--color-background-primary);border:none;border-bottom:1px solid var(--color-border-primary);cursor:pointer;text-align:left;width:100%}.settings-list__item:last-child{border-bottom:none}.settings-list__item:hover{background:var(--color-background-secondary)}.settings-list__item-text{display:flex;flex-direction:column;gap:2px}.settings-list__item-title{font-weight:600;font-size:var(--font-size-m, 16px);color:var(--color-text-primary)}.settings-list__item-subtitle{font-size:var(--font-size-s, 14px);color:var(--color-text-secondary)}.settings-density{display:flex;flex-direction:column;gap:8px}.settings-density__label{font-weight:600;font-size:var(--font-size-s, 14px);color:var(--color-text-primary)}.settings-columns{display:flex;flex-direction:column;gap:12px}.settings-columns__search{width:100%;padding:8px 12px;border:1px solid var(--color-border-primary);border-radius:var(--border-radius-s, 4px);font-size:var(--font-size-s, 14px);color:var(--color-text-primary);background:var(--color-background-primary, #fff);box-sizing:border-box}.settings-columns__search::placeholder{color:var(--color-text-secondary)}.settings-columns__search:focus{outline:2px solid var(--color-background-accent-inverse);outline-offset:-1px}.settings-columns__list{display:flex;flex-direction:column}.settings-columns__bulk-actions{display:flex;gap:8px;padding-top:8px;border-top:1px solid var(--color-border-primary)}.settings-columns__bulk-btn{flex:1;padding:8px 12px;border:1px solid var(--color-border-primary);border-radius:var(--border-radius-s, 4px);background:var(--color-background-primary, #fff);font-size:var(--font-size-s, 14px);font-weight:500;color:var(--color-text-primary);cursor:pointer}.settings-columns__bulk-btn:hover{background:var(--color-background-secondary, #f5f5f5)}.settings-columns__item{display:flex;align-items:center;justify-content:space-between;padding:16px 0;border-bottom:1px solid var(--color-border-primary);background:var(--color-background-primary, #fff)}.settings-columns__item:last-child{border-bottom:none}.settings-columns__item-left{display:flex;align-items:center;gap:8px}.settings-columns__drag-handle{display:flex;align-items:center;cursor:grab;color:var(--color-text-secondary)}.settings-columns__drag-handle:active{cursor:grabbing}.settings-columns__item-label{font-size:var(--font-size-m, 16px);color:var(--color-text-primary)}.cdk-drag-preview{display:flex;align-items:center;justify-content:space-between;padding:16px 8px;background:var(--color-background-primary, #fff);border:1px solid var(--color-border-primary);border-radius:4px;box-shadow:0 4px 12px #00000026;font-size:var(--font-size-m, 16px)}.cdk-drag-placeholder{opacity:.3}.cdk-drag-animating{transition:transform .2s ease}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: MozButtonComponent, selector: "button[moz-button]", inputs: ["appearance", "size", "disabled", "ghost", "outlined", "iconPosition", "type", "isLoading"] }, { kind: "directive", type: MozDrawerFooterDirective, selector: "[mozDrawerFooter]" }, { kind: "component", type: MozSelectComponent, selector: "moz-select", inputs: ["id", "name", "options", "placeholder", "isInvalid", "disabled", "readonly", "size"] }, { kind: "component", type: MozToggleComponent, selector: "moz-toggle", inputs: ["id", "name", "size", "disabled"] }, { kind: "component", type: ChevronRight20, selector: "ChevronRight20", inputs: ["hostClass"] }, { kind: "component", type: Drag20, selector: "Drag20", inputs: ["hostClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9322
9383
  }
9323
9384
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GridSettingsDrawerComponent, decorators: [{
9324
9385
  type: Component,
@@ -9333,7 +9394,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
9333
9394
  MozToggleComponent,
9334
9395
  ChevronRight20,
9335
9396
  Drag20,
9336
- ], template: "@switch (screen()) { @case ('main') {\n<div class=\"settings-list\">\n <button type=\"button\" class=\"settings-list__item\" (click)=\"goTo('density')\">\n <div class=\"settings-list__item-text\">\n <span class=\"settings-list__item-title\">Data density</span>\n <span class=\"settings-list__item-subtitle\">{{ densityLabel() }}</span>\n </div>\n <ChevronRight20 />\n </button>\n <button type=\"button\" class=\"settings-list__item\" (click)=\"goTo('columns')\">\n <div class=\"settings-list__item-text\">\n <span class=\"settings-list__item-title\">Display columns</span>\n <span class=\"settings-list__item-subtitle\">{{ columnsLabel() }}</span>\n </div>\n <ChevronRight20 />\n </button>\n</div>\n} @case ('density') {\n<div class=\"settings-density\">\n <label class=\"settings-density__label\">Data density</label>\n <moz-select\n name=\"density\"\n [options]=\"densityOptions\"\n [ngModel]=\"draftDensity()\"\n (ngModelChange)=\"draftDensity.set($event)\"\n />\n</div>\n} @case ('columns') {\n<div class=\"settings-columns\">\n <input\n class=\"settings-columns__search\"\n type=\"text\"\n placeholder=\"Find a column\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($event)\"\n aria-label=\"Search columns\"\n />\n <div class=\"settings-columns__list\" cdkDropList (cdkDropListDropped)=\"onColumnDrop($event)\">\n @for (col of filteredColumns(); track col.field) {\n <div class=\"settings-columns__item\" cdkDrag>\n <div class=\"settings-columns__item-left\">\n <span class=\"settings-columns__drag-handle\" cdkDragHandle>\n <Drag20 />\n </span>\n <span class=\"settings-columns__item-label\">{{ col.headerName }}</span>\n </div>\n <moz-toggle\n [id]=\"'col-toggle-' + col.field\"\n [ngModel]=\"col.visible\"\n (ngModelChange)=\"onColumnToggle(col.field, $event)\"\n />\n </div>\n }\n </div>\n <div class=\"settings-columns__bulk-actions\">\n <button type=\"button\" class=\"settings-columns__bulk-btn\" (click)=\"hideAll()\">\n Hide all\n </button>\n <button type=\"button\" class=\"settings-columns__bulk-btn\" (click)=\"showAll()\">\n Show all\n </button>\n </div>\n</div>\n} }\n\n<ng-template mozDrawerFooter>\n <button moz-button (click)=\"apply()\">Apply</button>\n <button moz-button [outlined]=\"true\" (click)=\"reset()\">Reset</button>\n</ng-template>\n", styles: [".settings-list{display:flex;flex-direction:column;border:1px solid var(--color-border-primary);border-radius:var(--border-radius-m, 8px);overflow:hidden}.settings-list__item{display:flex;align-items:center;justify-content:space-between;padding:16px;background:var(--color-background-primary);border:none;border-bottom:1px solid var(--color-border-primary);cursor:pointer;text-align:left;width:100%}.settings-list__item:last-child{border-bottom:none}.settings-list__item:hover{background:var(--color-background-secondary)}.settings-list__item-text{display:flex;flex-direction:column;gap:2px}.settings-list__item-title{font-weight:600;font-size:var(--font-size-m, 16px);color:var(--color-text-primary)}.settings-list__item-subtitle{font-size:var(--font-size-s, 14px);color:var(--color-text-secondary)}.settings-density{display:flex;flex-direction:column;gap:8px}.settings-density__label{font-weight:600;font-size:var(--font-size-s, 14px);color:var(--color-text-primary)}.settings-columns{display:flex;flex-direction:column;gap:12px}.settings-columns__search{width:100%;padding:8px 12px;border:1px solid var(--color-border-primary);border-radius:var(--border-radius-s, 4px);font-size:var(--font-size-s, 14px);color:var(--color-text-primary);background:var(--color-background-primary, #fff);box-sizing:border-box}.settings-columns__search::placeholder{color:var(--color-text-secondary)}.settings-columns__search:focus{outline:2px solid var(--color-background-accent-inverse);outline-offset:-1px}.settings-columns__list{display:flex;flex-direction:column}.settings-columns__bulk-actions{display:flex;gap:8px;padding-top:8px;border-top:1px solid var(--color-border-primary)}.settings-columns__bulk-btn{flex:1;padding:8px 12px;border:1px solid var(--color-border-primary);border-radius:var(--border-radius-s, 4px);background:var(--color-background-primary, #fff);font-size:var(--font-size-s, 14px);font-weight:500;color:var(--color-text-primary);cursor:pointer}.settings-columns__bulk-btn:hover{background:var(--color-background-secondary, #f5f5f5)}.settings-columns__item{display:flex;align-items:center;justify-content:space-between;padding:16px 0;border-bottom:1px solid var(--color-border-primary);background:var(--color-background-primary, #fff)}.settings-columns__item:last-child{border-bottom:none}.settings-columns__item-left{display:flex;align-items:center;gap:8px}.settings-columns__drag-handle{display:flex;align-items:center;cursor:grab;color:var(--color-text-secondary)}.settings-columns__drag-handle:active{cursor:grabbing}.settings-columns__item-label{font-size:var(--font-size-m, 16px);color:var(--color-text-primary)}.cdk-drag-preview{display:flex;align-items:center;justify-content:space-between;padding:16px 8px;background:var(--color-background-primary, #fff);border:1px solid var(--color-border-primary);border-radius:4px;box-shadow:0 4px 12px #00000026;font-size:var(--font-size-m, 16px)}.cdk-drag-placeholder{opacity:.3}.cdk-drag-animating{transition:transform .2s ease}\n"] }]
9397
+ ], template: "@switch (screen()) { @case ('main') {\n<div class=\"settings-list\">\n <button type=\"button\" class=\"settings-list__item\" (click)=\"goTo('density')\">\n <div class=\"settings-list__item-text\">\n <span class=\"settings-list__item-title\">Data density</span>\n <span class=\"settings-list__item-subtitle\">{{ densityLabel() }}</span>\n </div>\n <ChevronRight20 />\n </button>\n <button type=\"button\" class=\"settings-list__item\" (click)=\"goTo('columns')\">\n <div class=\"settings-list__item-text\">\n <span class=\"settings-list__item-title\">Display columns</span>\n <span class=\"settings-list__item-subtitle\">{{ columnsLabel() }}</span>\n </div>\n <ChevronRight20 />\n </button>\n</div>\n} @case ('density') {\n<div class=\"settings-density\">\n <label class=\"settings-density__label\">Data density</label>\n <moz-select\n name=\"density\"\n [options]=\"densityOptions\"\n [ngModel]=\"draftDensity()\"\n (ngModelChange)=\"draftDensity.set($event)\"\n />\n</div>\n} @case ('columns') {\n<div class=\"settings-columns\">\n <input\n class=\"settings-columns__search\"\n type=\"text\"\n placeholder=\"Find a column\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($event)\"\n aria-label=\"Search columns\"\n />\n <div class=\"settings-columns__list\" cdkDropList (cdkDropListDropped)=\"onColumnDrop($event)\">\n @for (col of filteredColumns(); track col.field) {\n <div class=\"settings-columns__item\" cdkDrag>\n <div class=\"settings-columns__item-left\">\n <span class=\"settings-columns__drag-handle\" cdkDragHandle>\n <Drag20 />\n </span>\n <span class=\"settings-columns__item-label\">{{ col.headerName }}</span>\n </div>\n <moz-toggle\n [id]=\"'col-toggle-' + col.field\"\n [ngModel]=\"col.visible\"\n (ngModelChange)=\"onColumnToggle(col.field, $event)\"\n />\n </div>\n }\n </div>\n <div class=\"settings-columns__bulk-actions\">\n <button type=\"button\" class=\"settings-columns__bulk-btn\" (click)=\"hideAll()\">Hide all</button>\n <button type=\"button\" class=\"settings-columns__bulk-btn\" (click)=\"showAll()\">Show all</button>\n </div>\n</div>\n} }\n\n<ng-template mozDrawerFooter>\n <button moz-button (click)=\"apply()\" [appearance]=\"'accent'\">Apply</button>\n <button moz-button [outlined]=\"true\" (click)=\"reset()\">Reset</button>\n</ng-template>\n", styles: [".settings-list{display:flex;flex-direction:column;border:1px solid var(--color-border-primary);border-radius:var(--border-radius-m, 8px);overflow:hidden}.settings-list__item{display:flex;align-items:center;justify-content:space-between;padding:16px;background:var(--color-background-primary);border:none;border-bottom:1px solid var(--color-border-primary);cursor:pointer;text-align:left;width:100%}.settings-list__item:last-child{border-bottom:none}.settings-list__item:hover{background:var(--color-background-secondary)}.settings-list__item-text{display:flex;flex-direction:column;gap:2px}.settings-list__item-title{font-weight:600;font-size:var(--font-size-m, 16px);color:var(--color-text-primary)}.settings-list__item-subtitle{font-size:var(--font-size-s, 14px);color:var(--color-text-secondary)}.settings-density{display:flex;flex-direction:column;gap:8px}.settings-density__label{font-weight:600;font-size:var(--font-size-s, 14px);color:var(--color-text-primary)}.settings-columns{display:flex;flex-direction:column;gap:12px}.settings-columns__search{width:100%;padding:8px 12px;border:1px solid var(--color-border-primary);border-radius:var(--border-radius-s, 4px);font-size:var(--font-size-s, 14px);color:var(--color-text-primary);background:var(--color-background-primary, #fff);box-sizing:border-box}.settings-columns__search::placeholder{color:var(--color-text-secondary)}.settings-columns__search:focus{outline:2px solid var(--color-background-accent-inverse);outline-offset:-1px}.settings-columns__list{display:flex;flex-direction:column}.settings-columns__bulk-actions{display:flex;gap:8px;padding-top:8px;border-top:1px solid var(--color-border-primary)}.settings-columns__bulk-btn{flex:1;padding:8px 12px;border:1px solid var(--color-border-primary);border-radius:var(--border-radius-s, 4px);background:var(--color-background-primary, #fff);font-size:var(--font-size-s, 14px);font-weight:500;color:var(--color-text-primary);cursor:pointer}.settings-columns__bulk-btn:hover{background:var(--color-background-secondary, #f5f5f5)}.settings-columns__item{display:flex;align-items:center;justify-content:space-between;padding:16px 0;border-bottom:1px solid var(--color-border-primary);background:var(--color-background-primary, #fff)}.settings-columns__item:last-child{border-bottom:none}.settings-columns__item-left{display:flex;align-items:center;gap:8px}.settings-columns__drag-handle{display:flex;align-items:center;cursor:grab;color:var(--color-text-secondary)}.settings-columns__drag-handle:active{cursor:grabbing}.settings-columns__item-label{font-size:var(--font-size-m, 16px);color:var(--color-text-primary)}.cdk-drag-preview{display:flex;align-items:center;justify-content:space-between;padding:16px 8px;background:var(--color-background-primary, #fff);border:1px solid var(--color-border-primary);border-radius:4px;box-shadow:0 4px 12px #00000026;font-size:var(--font-size-m, 16px)}.cdk-drag-placeholder{opacity:.3}.cdk-drag-animating{transition:transform .2s ease}\n"] }]
9337
9398
  }], ctorParameters: () => [] });
9338
9399
 
9339
9400
  class GridGroupDrawerComponent {
@@ -9385,7 +9446,7 @@ class GridGroupDrawerComponent {
9385
9446
  this.draftGrouped.set([]);
9386
9447
  }
9387
9448
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GridGroupDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9388
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: GridGroupDrawerComponent, isStandalone: true, selector: "moz-grid-group-drawer", ngImport: i0, template: "<div class=\"group-drawer__list\" cdkDropList (cdkDropListDropped)=\"onDrop($event)\">\n @for (item of draftGrouped(); track item.field; let idx = $index) {\n <div class=\"group-drawer__item\" cdkDrag>\n <span class=\"group-drawer__drag-handle\" cdkDragHandle>\n <Drag20 />\n </span>\n <span class=\"group-drawer__item-label\">{{ item.headerName }}</span>\n <select\n class=\"group-drawer__sort-select\"\n [value]=\"item.sortDirection\"\n [attr.aria-label]=\"'Sort direction for ' + item.headerName\"\n (change)=\"onSortDirectionChange(idx, $event)\"\n >\n <option value=\"asc\">A \u2192 Z</option>\n <option value=\"desc\">Z \u2192 A</option>\n </select>\n <button\n type=\"button\"\n moz-button\n [attr.aria-label]=\"'Remove ' + item.headerName\"\n (click)=\"removeGroup(item.field)\"\n [iconPosition]=\"'only'\"\n [size]=\"'s'\"\n [ghost]=\"true\"\n >\n <Cross20 icon />\n </button>\n </div>\n }\n</div>\n\n@if (available().length > 0) {\n<div class=\"group-drawer__available\">\n <h4 class=\"group-drawer__section-title\">Available columns</h4>\n @for (col of available(); track col.field) {\n <div class=\"group-drawer__available-item\">\n <span class=\"group-drawer__available-label\">{{ col.headerName }}</span>\n <button\n type=\"button\"\n class=\"group-drawer__add-btn\"\n [attr.aria-label]=\"'Add ' + col.headerName + ' as group'\"\n (click)=\"addGroup(col.field)\"\n >\n <ListAdd20 />\n </button>\n </div>\n }\n</div>\n} @if (draftGrouped().length === 0 && available().length === 0) {\n<p class=\"group-drawer__empty\">No groupable columns available.</p>\n}\n\n<ng-template mozDrawerFooter>\n <button moz-button (click)=\"apply()\">Apply</button>\n <button moz-button [outlined]=\"true\" (click)=\"reset()\">Reset</button>\n</ng-template>\n", styles: [".group-drawer__list{display:flex;flex-direction:column}.group-drawer__item{display:flex;align-items:center;gap:8px;padding:12px 0;border-bottom:1px solid var(--color-border-primary);background:var(--color-background-primary, #fff)}.group-drawer__item:last-child{border-bottom:none}.group-drawer__drag-handle{display:flex;align-items:center;cursor:grab;color:var(--color-text-secondary);flex-shrink:0}.group-drawer__drag-handle:active{cursor:grabbing}.group-drawer__item-label{flex:1;font-size:var(--font-size-m, 16px);color:var(--color-text-primary);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.group-drawer__sort-select{flex-shrink:0;padding:4px 8px;border:1px solid var(--color-border-primary);border-radius:var(--border-radius-s, 4px);background:var(--color-background-primary, #fff);font-size:var(--font-size-s, 14px);color:var(--color-text-primary);cursor:pointer}.group-drawer__sort-select:focus{outline:2px solid var(--color-background-accent-inverse);outline-offset:-1px}.group-drawer__available{margin-top:16px;margin-bottom:32px}.group-drawer__section-title{font-size:var(--font-size-s, 14px);font-weight:600;color:var(--color-text-secondary);margin:0 0 8px;text-transform:uppercase;letter-spacing:.5px}.group-drawer__available-item{display:flex;align-items:center;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--color-border-primary)}.group-drawer__available-item:last-child{border-bottom:none}.group-drawer__available-label{font-size:var(--font-size-m, 16px);color:var(--color-text-primary)}.group-drawer__add-btn{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:28px;height:28px;padding:0;border:none;border-radius:var(--border-radius-s, 4px);background:transparent;cursor:pointer;color:var(--color-background-accent-inverse)}.group-drawer__add-btn:hover{background:#1a73e814;color:var(--color-primary-dark, #1557b0)}.group-drawer__empty{font-size:var(--font-size-s, 14px);color:var(--color-text-secondary);text-align:center;padding:24px 0}.cdk-drag-preview{display:flex;align-items:center;gap:8px;padding:12px 8px;background:var(--color-background-primary, #fff);border:1px solid var(--color-border-primary);border-radius:4px;box-shadow:0 4px 12px #00000026;font-size:var(--font-size-m, 16px)}.cdk-drag-placeholder{opacity:.3}.cdk-drag-animating{transition:transform .2s ease}\n"], dependencies: [{ kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: MozButtonComponent, selector: "button[moz-button]", inputs: ["appearance", "size", "disabled", "ghost", "outlined", "iconPosition", "type", "isLoading"] }, { kind: "directive", type: MozDrawerFooterDirective, selector: "[mozDrawerFooter]" }, { kind: "component", type: Drag20, selector: "Drag20", inputs: ["hostClass"] }, { kind: "component", type: Cross20, selector: "Cross20", inputs: ["hostClass"] }, { kind: "component", type: ListAdd20, selector: "ListAdd20", inputs: ["hostClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9449
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: GridGroupDrawerComponent, isStandalone: true, selector: "moz-grid-group-drawer", ngImport: i0, template: "<div class=\"group-drawer__list\" cdkDropList (cdkDropListDropped)=\"onDrop($event)\">\n @for (item of draftGrouped(); track item.field; let idx = $index) {\n <div class=\"group-drawer__item\" cdkDrag>\n <span class=\"group-drawer__drag-handle\" cdkDragHandle>\n <Drag20 />\n </span>\n <span class=\"group-drawer__item-label\">{{ item.headerName }}</span>\n <select\n class=\"group-drawer__sort-select\"\n [value]=\"item.sortDirection\"\n [attr.aria-label]=\"'Sort direction for ' + item.headerName\"\n (change)=\"onSortDirectionChange(idx, $event)\"\n >\n <option value=\"asc\">A \u2192 Z</option>\n <option value=\"desc\">Z \u2192 A</option>\n </select>\n <button\n type=\"button\"\n moz-button\n [attr.aria-label]=\"'Remove ' + item.headerName\"\n (click)=\"removeGroup(item.field)\"\n [iconPosition]=\"'only'\"\n [size]=\"'s'\"\n [ghost]=\"true\"\n >\n <Cross20 icon />\n </button>\n </div>\n }\n</div>\n\n@if (available().length > 0) {\n<div class=\"group-drawer__available\">\n <h4 class=\"group-drawer__section-title\">Available columns</h4>\n @for (col of available(); track col.field) {\n <div class=\"group-drawer__available-item\">\n <span class=\"group-drawer__available-label\">{{ col.headerName }}</span>\n <button\n type=\"button\"\n class=\"group-drawer__add-btn\"\n [attr.aria-label]=\"'Add ' + col.headerName + ' as group'\"\n (click)=\"addGroup(col.field)\"\n >\n <ListAdd20 />\n </button>\n </div>\n }\n</div>\n} @if (draftGrouped().length === 0 && available().length === 0) {\n<p class=\"group-drawer__empty\">No groupable columns available.</p>\n}\n\n<ng-template mozDrawerFooter>\n <button moz-button (click)=\"apply()\" [appearance]=\"'accent'\">Apply</button>\n <button moz-button [outlined]=\"true\" (click)=\"reset()\">Reset</button>\n</ng-template>\n", styles: [".group-drawer__list{display:flex;flex-direction:column}.group-drawer__item{display:flex;align-items:center;gap:8px;padding:12px 0;border-bottom:1px solid var(--color-border-primary);background:var(--color-background-primary, #fff)}.group-drawer__item:last-child{border-bottom:none}.group-drawer__drag-handle{display:flex;align-items:center;cursor:grab;color:var(--color-text-secondary);flex-shrink:0}.group-drawer__drag-handle:active{cursor:grabbing}.group-drawer__item-label{flex:1;font-size:var(--font-size-m, 16px);color:var(--color-text-primary);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.group-drawer__sort-select{flex-shrink:0;padding:4px 8px;border:1px solid var(--color-border-primary);border-radius:var(--border-radius-s, 4px);background:var(--color-background-primary, #fff);font-size:var(--font-size-s, 14px);color:var(--color-text-primary);cursor:pointer}.group-drawer__sort-select:focus{outline:2px solid var(--color-background-accent-inverse);outline-offset:-1px}.group-drawer__available{margin-top:16px;margin-bottom:32px}.group-drawer__section-title{font-size:var(--font-size-s, 14px);font-weight:600;color:var(--color-text-secondary);margin:0 0 8px;text-transform:uppercase;letter-spacing:.5px}.group-drawer__available-item{display:flex;align-items:center;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--color-border-primary)}.group-drawer__available-item:last-child{border-bottom:none}.group-drawer__available-label{font-size:var(--font-size-m, 16px);color:var(--color-text-primary)}.group-drawer__add-btn{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:28px;height:28px;padding:0;border:none;border-radius:var(--border-radius-s, 4px);background:transparent;cursor:pointer;color:var(--color-background-accent-inverse)}.group-drawer__add-btn:hover{background:#1a73e814;color:var(--color-primary-dark, #1557b0)}.group-drawer__empty{font-size:var(--font-size-s, 14px);color:var(--color-text-secondary);text-align:center;padding:24px 0}.cdk-drag-preview{display:flex;align-items:center;gap:8px;padding:12px 8px;background:var(--color-background-primary, #fff);border:1px solid var(--color-border-primary);border-radius:4px;box-shadow:0 4px 12px #00000026;font-size:var(--font-size-m, 16px)}.cdk-drag-placeholder{opacity:.3}.cdk-drag-animating{transition:transform .2s ease}\n"], dependencies: [{ kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: MozButtonComponent, selector: "button[moz-button]", inputs: ["appearance", "size", "disabled", "ghost", "outlined", "iconPosition", "type", "isLoading"] }, { kind: "directive", type: MozDrawerFooterDirective, selector: "[mozDrawerFooter]" }, { kind: "component", type: Drag20, selector: "Drag20", inputs: ["hostClass"] }, { kind: "component", type: Cross20, selector: "Cross20", inputs: ["hostClass"] }, { kind: "component", type: ListAdd20, selector: "ListAdd20", inputs: ["hostClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9389
9450
  }
9390
9451
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GridGroupDrawerComponent, decorators: [{
9391
9452
  type: Component,
@@ -9398,7 +9459,86 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
9398
9459
  Drag20,
9399
9460
  Cross20,
9400
9461
  ListAdd20,
9401
- ], template: "<div class=\"group-drawer__list\" cdkDropList (cdkDropListDropped)=\"onDrop($event)\">\n @for (item of draftGrouped(); track item.field; let idx = $index) {\n <div class=\"group-drawer__item\" cdkDrag>\n <span class=\"group-drawer__drag-handle\" cdkDragHandle>\n <Drag20 />\n </span>\n <span class=\"group-drawer__item-label\">{{ item.headerName }}</span>\n <select\n class=\"group-drawer__sort-select\"\n [value]=\"item.sortDirection\"\n [attr.aria-label]=\"'Sort direction for ' + item.headerName\"\n (change)=\"onSortDirectionChange(idx, $event)\"\n >\n <option value=\"asc\">A \u2192 Z</option>\n <option value=\"desc\">Z \u2192 A</option>\n </select>\n <button\n type=\"button\"\n moz-button\n [attr.aria-label]=\"'Remove ' + item.headerName\"\n (click)=\"removeGroup(item.field)\"\n [iconPosition]=\"'only'\"\n [size]=\"'s'\"\n [ghost]=\"true\"\n >\n <Cross20 icon />\n </button>\n </div>\n }\n</div>\n\n@if (available().length > 0) {\n<div class=\"group-drawer__available\">\n <h4 class=\"group-drawer__section-title\">Available columns</h4>\n @for (col of available(); track col.field) {\n <div class=\"group-drawer__available-item\">\n <span class=\"group-drawer__available-label\">{{ col.headerName }}</span>\n <button\n type=\"button\"\n class=\"group-drawer__add-btn\"\n [attr.aria-label]=\"'Add ' + col.headerName + ' as group'\"\n (click)=\"addGroup(col.field)\"\n >\n <ListAdd20 />\n </button>\n </div>\n }\n</div>\n} @if (draftGrouped().length === 0 && available().length === 0) {\n<p class=\"group-drawer__empty\">No groupable columns available.</p>\n}\n\n<ng-template mozDrawerFooter>\n <button moz-button (click)=\"apply()\">Apply</button>\n <button moz-button [outlined]=\"true\" (click)=\"reset()\">Reset</button>\n</ng-template>\n", styles: [".group-drawer__list{display:flex;flex-direction:column}.group-drawer__item{display:flex;align-items:center;gap:8px;padding:12px 0;border-bottom:1px solid var(--color-border-primary);background:var(--color-background-primary, #fff)}.group-drawer__item:last-child{border-bottom:none}.group-drawer__drag-handle{display:flex;align-items:center;cursor:grab;color:var(--color-text-secondary);flex-shrink:0}.group-drawer__drag-handle:active{cursor:grabbing}.group-drawer__item-label{flex:1;font-size:var(--font-size-m, 16px);color:var(--color-text-primary);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.group-drawer__sort-select{flex-shrink:0;padding:4px 8px;border:1px solid var(--color-border-primary);border-radius:var(--border-radius-s, 4px);background:var(--color-background-primary, #fff);font-size:var(--font-size-s, 14px);color:var(--color-text-primary);cursor:pointer}.group-drawer__sort-select:focus{outline:2px solid var(--color-background-accent-inverse);outline-offset:-1px}.group-drawer__available{margin-top:16px;margin-bottom:32px}.group-drawer__section-title{font-size:var(--font-size-s, 14px);font-weight:600;color:var(--color-text-secondary);margin:0 0 8px;text-transform:uppercase;letter-spacing:.5px}.group-drawer__available-item{display:flex;align-items:center;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--color-border-primary)}.group-drawer__available-item:last-child{border-bottom:none}.group-drawer__available-label{font-size:var(--font-size-m, 16px);color:var(--color-text-primary)}.group-drawer__add-btn{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:28px;height:28px;padding:0;border:none;border-radius:var(--border-radius-s, 4px);background:transparent;cursor:pointer;color:var(--color-background-accent-inverse)}.group-drawer__add-btn:hover{background:#1a73e814;color:var(--color-primary-dark, #1557b0)}.group-drawer__empty{font-size:var(--font-size-s, 14px);color:var(--color-text-secondary);text-align:center;padding:24px 0}.cdk-drag-preview{display:flex;align-items:center;gap:8px;padding:12px 8px;background:var(--color-background-primary, #fff);border:1px solid var(--color-border-primary);border-radius:4px;box-shadow:0 4px 12px #00000026;font-size:var(--font-size-m, 16px)}.cdk-drag-placeholder{opacity:.3}.cdk-drag-animating{transition:transform .2s ease}\n"] }]
9462
+ ], template: "<div class=\"group-drawer__list\" cdkDropList (cdkDropListDropped)=\"onDrop($event)\">\n @for (item of draftGrouped(); track item.field; let idx = $index) {\n <div class=\"group-drawer__item\" cdkDrag>\n <span class=\"group-drawer__drag-handle\" cdkDragHandle>\n <Drag20 />\n </span>\n <span class=\"group-drawer__item-label\">{{ item.headerName }}</span>\n <select\n class=\"group-drawer__sort-select\"\n [value]=\"item.sortDirection\"\n [attr.aria-label]=\"'Sort direction for ' + item.headerName\"\n (change)=\"onSortDirectionChange(idx, $event)\"\n >\n <option value=\"asc\">A \u2192 Z</option>\n <option value=\"desc\">Z \u2192 A</option>\n </select>\n <button\n type=\"button\"\n moz-button\n [attr.aria-label]=\"'Remove ' + item.headerName\"\n (click)=\"removeGroup(item.field)\"\n [iconPosition]=\"'only'\"\n [size]=\"'s'\"\n [ghost]=\"true\"\n >\n <Cross20 icon />\n </button>\n </div>\n }\n</div>\n\n@if (available().length > 0) {\n<div class=\"group-drawer__available\">\n <h4 class=\"group-drawer__section-title\">Available columns</h4>\n @for (col of available(); track col.field) {\n <div class=\"group-drawer__available-item\">\n <span class=\"group-drawer__available-label\">{{ col.headerName }}</span>\n <button\n type=\"button\"\n class=\"group-drawer__add-btn\"\n [attr.aria-label]=\"'Add ' + col.headerName + ' as group'\"\n (click)=\"addGroup(col.field)\"\n >\n <ListAdd20 />\n </button>\n </div>\n }\n</div>\n} @if (draftGrouped().length === 0 && available().length === 0) {\n<p class=\"group-drawer__empty\">No groupable columns available.</p>\n}\n\n<ng-template mozDrawerFooter>\n <button moz-button (click)=\"apply()\" [appearance]=\"'accent'\">Apply</button>\n <button moz-button [outlined]=\"true\" (click)=\"reset()\">Reset</button>\n</ng-template>\n", styles: [".group-drawer__list{display:flex;flex-direction:column}.group-drawer__item{display:flex;align-items:center;gap:8px;padding:12px 0;border-bottom:1px solid var(--color-border-primary);background:var(--color-background-primary, #fff)}.group-drawer__item:last-child{border-bottom:none}.group-drawer__drag-handle{display:flex;align-items:center;cursor:grab;color:var(--color-text-secondary);flex-shrink:0}.group-drawer__drag-handle:active{cursor:grabbing}.group-drawer__item-label{flex:1;font-size:var(--font-size-m, 16px);color:var(--color-text-primary);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.group-drawer__sort-select{flex-shrink:0;padding:4px 8px;border:1px solid var(--color-border-primary);border-radius:var(--border-radius-s, 4px);background:var(--color-background-primary, #fff);font-size:var(--font-size-s, 14px);color:var(--color-text-primary);cursor:pointer}.group-drawer__sort-select:focus{outline:2px solid var(--color-background-accent-inverse);outline-offset:-1px}.group-drawer__available{margin-top:16px;margin-bottom:32px}.group-drawer__section-title{font-size:var(--font-size-s, 14px);font-weight:600;color:var(--color-text-secondary);margin:0 0 8px;text-transform:uppercase;letter-spacing:.5px}.group-drawer__available-item{display:flex;align-items:center;justify-content:space-between;padding:10px 0;border-bottom:1px solid var(--color-border-primary)}.group-drawer__available-item:last-child{border-bottom:none}.group-drawer__available-label{font-size:var(--font-size-m, 16px);color:var(--color-text-primary)}.group-drawer__add-btn{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:28px;height:28px;padding:0;border:none;border-radius:var(--border-radius-s, 4px);background:transparent;cursor:pointer;color:var(--color-background-accent-inverse)}.group-drawer__add-btn:hover{background:#1a73e814;color:var(--color-primary-dark, #1557b0)}.group-drawer__empty{font-size:var(--font-size-s, 14px);color:var(--color-text-secondary);text-align:center;padding:24px 0}.cdk-drag-preview{display:flex;align-items:center;gap:8px;padding:12px 8px;background:var(--color-background-primary, #fff);border:1px solid var(--color-border-primary);border-radius:4px;box-shadow:0 4px 12px #00000026;font-size:var(--font-size-m, 16px)}.cdk-drag-placeholder{opacity:.3}.cdk-drag-animating{transition:transform .2s ease}\n"] }]
9463
+ }] });
9464
+
9465
+ const EXCEL_SHORTCUTS = [
9466
+ {
9467
+ title: 'Navigation',
9468
+ items: [
9469
+ { keys: '← ↑ → ↓', label: 'Déplacer la cellule active' },
9470
+ { keys: 'Ctrl + Flèche', label: 'Sauter au bord du bloc de données' },
9471
+ { keys: 'Home / End', label: 'Début / fin de la ligne' },
9472
+ { keys: 'Ctrl + Home / End', label: 'Première / dernière cellule' },
9473
+ { keys: 'PageUp / PageDown', label: 'Page précédente / suivante' },
9474
+ { keys: 'Tab / Shift+Tab', label: 'Cellule suivante / précédente' },
9475
+ { keys: 'Enter / Shift+Enter', label: 'Descendre / remonter' },
9476
+ ],
9477
+ },
9478
+ {
9479
+ title: 'Sélection',
9480
+ items: [
9481
+ { keys: 'Shift + Flèche', label: 'Étendre la plage' },
9482
+ { keys: 'Shift + Ctrl + Flèche', label: "Étendre jusqu'au bord du bloc" },
9483
+ { keys: 'Shift + Home / End', label: 'Étendre au début / fin de la ligne' },
9484
+ { keys: 'Shift + Ctrl + Home / End', label: 'Étendre au début / fin du tableau' },
9485
+ { keys: 'Shift + PageUp / Down', label: "Étendre d'une page" },
9486
+ { keys: 'Ctrl + A', label: 'Sélectionner tout' },
9487
+ { keys: 'Shift + Espace', label: 'Sélectionner la ligne' },
9488
+ { keys: 'Ctrl + Espace', label: 'Sélectionner la colonne' },
9489
+ ],
9490
+ },
9491
+ {
9492
+ title: 'Édition',
9493
+ items: [
9494
+ { keys: 'Enter / F2', label: 'Entrer en édition' },
9495
+ { keys: 'Touche imprimable', label: 'Typing-to-edit (remplace la valeur)' },
9496
+ { keys: 'Escape', label: "Annuler l'édition" },
9497
+ { keys: 'Enter', label: 'Valider + descendre' },
9498
+ { keys: 'Tab / Shift+Tab', label: 'Valider + droite / gauche' },
9499
+ { keys: 'Alt + Enter', label: 'Retour à la ligne (texte)' },
9500
+ { keys: 'Ctrl + Enter', label: 'Valider + remplir la sélection' },
9501
+ { keys: 'Backspace / Delete', label: 'Effacer les cellules sélectionnées' },
9502
+ ],
9503
+ },
9504
+ {
9505
+ title: 'Presse-papier',
9506
+ items: [
9507
+ { keys: 'Ctrl + C', label: 'Copier (TSV)' },
9508
+ { keys: 'Ctrl + X', label: 'Couper (marching ants)' },
9509
+ { keys: 'Ctrl + V', label: 'Coller (déplace après Ctrl+X)' },
9510
+ { keys: 'Ctrl + D', label: 'Remplir vers le bas (fill down)' },
9511
+ { keys: 'Ctrl + R', label: 'Remplir vers la droite (fill right)' },
9512
+ ],
9513
+ },
9514
+ {
9515
+ title: 'Historique',
9516
+ items: [
9517
+ { keys: 'Ctrl + Z', label: 'Annuler (undo)' },
9518
+ { keys: 'Ctrl + Y', label: 'Rétablir (redo)' },
9519
+ { keys: 'Ctrl + Shift + Z', label: 'Rétablir (redo, alt.)' },
9520
+ ],
9521
+ },
9522
+ ];
9523
+ class GridKeyboardShortcutsDrawerComponent {
9524
+ drawerRef = inject(MozDrawerRef);
9525
+ groups = EXCEL_SHORTCUTS.map((group) => ({
9526
+ title: group.title,
9527
+ items: group.items.map((item) => ({
9528
+ keys: item.keys,
9529
+ label: item.label,
9530
+ parts: item.keys.split(' '),
9531
+ })),
9532
+ }));
9533
+ close() {
9534
+ this.drawerRef.close();
9535
+ }
9536
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GridKeyboardShortcutsDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9537
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: GridKeyboardShortcutsDrawerComponent, isStandalone: true, selector: "moz-grid-keyboard-shortcuts-drawer", ngImport: i0, template: "<div class=\"shortcuts\">\n @for (group of groups; track group.title) {\n <section class=\"shortcuts__group\">\n <h4 class=\"shortcuts__group-title\">{{ group.title }}</h4>\n <dl class=\"shortcuts__list\">\n @for (item of group.items; track item.keys) {\n <div class=\"shortcuts__item\">\n <dt class=\"shortcuts__keys\">\n @for (part of item.parts; track $index) { @if (part === '+' || part === '/') {\n <span class=\"shortcuts__separator\">{{ part }}</span>\n } @else {\n <kbd class=\"shortcuts__key\">{{ part }}</kbd>\n } }\n </dt>\n <dd class=\"shortcuts__label\">{{ item.label }}</dd>\n </div>\n }\n </dl>\n </section>\n }\n</div>\n\n<ng-template mozDrawerFooter>\n <button moz-button (click)=\"close()\">Close</button>\n</ng-template>\n", styles: [".shortcuts{padding-bottom:24px}.shortcuts__group{margin-bottom:24px}.shortcuts__group:last-child{margin-bottom:0}.shortcuts__group-title{margin:0 0 8px;font-size:var(--font-size-s, 14px);font-weight:700;color:var(--color-text-secondary);text-transform:uppercase;letter-spacing:.5px}.shortcuts__list{display:flex;flex-direction:column;margin:0;padding:0}.shortcuts__item{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:10px 0;border-bottom:1px solid var(--color-border-primary)}.shortcuts__item:last-child{border-bottom:none}.shortcuts__label{margin:0;flex:1;font-size:var(--font-size-s, 14px);color:var(--color-text-primary)}.shortcuts__keys{display:flex;align-items:center;flex-wrap:wrap;gap:4px;flex-shrink:0;margin:0}.shortcuts__key{display:inline-flex;align-items:center;justify-content:center;min-width:24px;height:24px;padding:0 6px;background:var(--color-background-secondary, #f5f5f5);border:1px solid var(--color-border-primary);border-radius:var(--border-radius-s, 4px);font-family:var(--font-family-monospace, \"SFMono-Regular\", Consolas, monospace);font-size:var(--font-size-50, 12px);font-weight:600;color:var(--color-text-primary);box-shadow:inset 0 -1px #00000014}.shortcuts__separator{font-size:var(--font-size-s, 14px);color:var(--color-text-secondary);padding:0 2px}\n"], dependencies: [{ kind: "component", type: MozButtonComponent, selector: "button[moz-button]", inputs: ["appearance", "size", "disabled", "ghost", "outlined", "iconPosition", "type", "isLoading"] }, { kind: "directive", type: MozDrawerFooterDirective, selector: "[mozDrawerFooter]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
9538
+ }
9539
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GridKeyboardShortcutsDrawerComponent, decorators: [{
9540
+ type: Component,
9541
+ args: [{ selector: 'moz-grid-keyboard-shortcuts-drawer', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MozButtonComponent, MozDrawerFooterDirective], template: "<div class=\"shortcuts\">\n @for (group of groups; track group.title) {\n <section class=\"shortcuts__group\">\n <h4 class=\"shortcuts__group-title\">{{ group.title }}</h4>\n <dl class=\"shortcuts__list\">\n @for (item of group.items; track item.keys) {\n <div class=\"shortcuts__item\">\n <dt class=\"shortcuts__keys\">\n @for (part of item.parts; track $index) { @if (part === '+' || part === '/') {\n <span class=\"shortcuts__separator\">{{ part }}</span>\n } @else {\n <kbd class=\"shortcuts__key\">{{ part }}</kbd>\n } }\n </dt>\n <dd class=\"shortcuts__label\">{{ item.label }}</dd>\n </div>\n }\n </dl>\n </section>\n }\n</div>\n\n<ng-template mozDrawerFooter>\n <button moz-button (click)=\"close()\">Close</button>\n</ng-template>\n", styles: [".shortcuts{padding-bottom:24px}.shortcuts__group{margin-bottom:24px}.shortcuts__group:last-child{margin-bottom:0}.shortcuts__group-title{margin:0 0 8px;font-size:var(--font-size-s, 14px);font-weight:700;color:var(--color-text-secondary);text-transform:uppercase;letter-spacing:.5px}.shortcuts__list{display:flex;flex-direction:column;margin:0;padding:0}.shortcuts__item{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:10px 0;border-bottom:1px solid var(--color-border-primary)}.shortcuts__item:last-child{border-bottom:none}.shortcuts__label{margin:0;flex:1;font-size:var(--font-size-s, 14px);color:var(--color-text-primary)}.shortcuts__keys{display:flex;align-items:center;flex-wrap:wrap;gap:4px;flex-shrink:0;margin:0}.shortcuts__key{display:inline-flex;align-items:center;justify-content:center;min-width:24px;height:24px;padding:0 6px;background:var(--color-background-secondary, #f5f5f5);border:1px solid var(--color-border-primary);border-radius:var(--border-radius-s, 4px);font-family:var(--font-family-monospace, \"SFMono-Regular\", Consolas, monospace);font-size:var(--font-size-50, 12px);font-weight:600;color:var(--color-text-primary);box-shadow:inset 0 -1px #00000014}.shortcuts__separator{font-size:var(--font-size-s, 14px);color:var(--color-text-secondary);padding:0 2px}\n"] }]
9402
9542
  }] });
9403
9543
 
9404
9544
  class MozGridComponent {
@@ -9491,7 +9631,9 @@ class MozGridComponent {
9491
9631
  constructor() {
9492
9632
  this.keyboardEngine.registerActions({
9493
9633
  copy: () => this.onBulkCopy(),
9494
- paste: () => { void this.onBulkPaste(); },
9634
+ paste: () => {
9635
+ void this.onBulkPaste();
9636
+ },
9495
9637
  cut: () => this.onCutShortcut(),
9496
9638
  deleteRange: () => this.onBulkDelete(),
9497
9639
  undo: () => this.onUndo(),
@@ -9852,7 +9994,9 @@ class MozGridComponent {
9852
9994
  // Alt+Enter: insert a newline in the draft for text editors (Excel-style).
9853
9995
  if (event.altKey) {
9854
9996
  const editState = this.state.cellEditState();
9855
- const def = this.state.columnDefMap().get(this.state.visibleColumns()[editState.editingCell?.col ?? -1]?.field ?? '');
9997
+ const def = this.state
9998
+ .columnDefMap()
9999
+ .get(this.state.visibleColumns()[editState.editingCell?.col ?? -1]?.field ?? '');
9856
10000
  const editorType = def?.cellEditor ?? 'text';
9857
10001
  if (editorType === 'text') {
9858
10002
  event.preventDefault();
@@ -9931,8 +10075,6 @@ class MozGridComponent {
9931
10075
  const range = this.cellSelectionEngine.getNormalizedRange();
9932
10076
  if (!range)
9933
10077
  return;
9934
- if (this.state.mode() !== 'client')
9935
- return;
9936
10078
  const changes = this.clipboardEngine.fillDown(range);
9937
10079
  if (changes.length === 0)
9938
10080
  return;
@@ -9943,8 +10085,6 @@ class MozGridComponent {
9943
10085
  const range = this.cellSelectionEngine.getNormalizedRange();
9944
10086
  if (!range)
9945
10087
  return;
9946
- if (this.state.mode() !== 'client')
9947
- return;
9948
10088
  const changes = this.clipboardEngine.fillRight(range);
9949
10089
  if (changes.length === 0)
9950
10090
  return;
@@ -10039,37 +10179,35 @@ class MozGridComponent {
10039
10179
  const affected = maxRow - minRow;
10040
10180
  if (affected === 0)
10041
10181
  return;
10042
- if (this.state.mode() === 'client') {
10043
- // Build a map of display index → sourceData index for the fill range
10044
- const indexMap = new Map();
10045
- for (let r = minRow; r <= maxRow; r++) {
10046
- if (r === anchor.row)
10182
+ // Build a map of display index → sourceData index for the fill range
10183
+ const indexMap = new Map();
10184
+ for (let r = minRow; r <= maxRow; r++) {
10185
+ if (r === anchor.row)
10186
+ continue;
10187
+ const srcIdx = this.displayIndexToSourceIndex(r);
10188
+ if (srcIdx >= 0)
10189
+ indexMap.set(r, srcIdx);
10190
+ }
10191
+ const changes = [];
10192
+ this.state.sourceData.update((d) => {
10193
+ const updated = [...d];
10194
+ for (const [, srcIdx] of indexMap) {
10195
+ if (!updated[srcIdx])
10047
10196
  continue;
10048
- const srcIdx = this.displayIndexToSourceIndex(r);
10049
- if (srcIdx >= 0)
10050
- indexMap.set(r, srcIdx);
10051
- }
10052
- const changes = [];
10053
- this.state.sourceData.update((d) => {
10054
- const updated = [...d];
10055
- for (const [, srcIdx] of indexMap) {
10056
- if (!updated[srcIdx])
10057
- continue;
10058
- const before = updated[srcIdx][field];
10059
- if (before === sourceValue)
10060
- continue;
10061
- const rowCopy = { ...updated[srcIdx] };
10062
- rowCopy[field] = sourceValue;
10063
- updated[srcIdx] = rowCopy;
10064
- changes.push({ rowIndex: srcIdx, field, before, after: sourceValue });
10065
- }
10066
- return updated;
10067
- });
10068
- if (changes.length > 0) {
10069
- this.historyEngine.record('fill', changes);
10197
+ const before = updated[srcIdx][field];
10198
+ if (before === sourceValue)
10199
+ continue;
10200
+ const rowCopy = { ...updated[srcIdx] };
10201
+ rowCopy[field] = sourceValue;
10202
+ updated[srcIdx] = rowCopy;
10203
+ changes.push({ rowIndex: srcIdx, field, before, after: sourceValue });
10070
10204
  }
10071
- this.clipboardEngine.clearCut();
10205
+ return updated;
10206
+ });
10207
+ if (changes.length > 0) {
10208
+ this.historyEngine.record('fill', changes);
10072
10209
  }
10210
+ this.clipboardEngine.clearCut();
10073
10211
  this.fillDown.emit({
10074
10212
  sourceCell: anchor,
10075
10213
  sourceValue,
@@ -10108,29 +10246,27 @@ class MozGridComponent {
10108
10246
  }
10109
10247
  if (targetFields.length === 0)
10110
10248
  return;
10111
- if (this.state.mode() === 'client') {
10112
- const changes = [];
10113
- this.state.sourceData.update((d) => {
10114
- const updated = [...d];
10115
- const src = updated[anchorSourceIdx];
10116
- if (!src)
10117
- return updated;
10118
- const rowCopy = { ...src };
10119
- for (const f of targetFields) {
10120
- const before = src[f];
10121
- if (before === sourceValue)
10122
- continue;
10123
- rowCopy[f] = sourceValue;
10124
- changes.push({ rowIndex: anchorSourceIdx, field: f, before, after: sourceValue });
10125
- }
10126
- updated[anchorSourceIdx] = rowCopy;
10249
+ const changes = [];
10250
+ this.state.sourceData.update((d) => {
10251
+ const updated = [...d];
10252
+ const src = updated[anchorSourceIdx];
10253
+ if (!src)
10127
10254
  return updated;
10128
- });
10129
- if (changes.length > 0) {
10130
- this.historyEngine.record('fill', changes);
10255
+ const rowCopy = { ...src };
10256
+ for (const f of targetFields) {
10257
+ const before = src[f];
10258
+ if (before === sourceValue)
10259
+ continue;
10260
+ rowCopy[f] = sourceValue;
10261
+ changes.push({ rowIndex: anchorSourceIdx, field: f, before, after: sourceValue });
10131
10262
  }
10132
- this.clipboardEngine.clearCut();
10263
+ updated[anchorSourceIdx] = rowCopy;
10264
+ return updated;
10265
+ });
10266
+ if (changes.length > 0) {
10267
+ this.historyEngine.record('fill', changes);
10133
10268
  }
10269
+ this.clipboardEngine.clearCut();
10134
10270
  this.fillDown.emit({
10135
10271
  sourceCell: anchor,
10136
10272
  sourceValue,
@@ -10232,6 +10368,10 @@ class MozGridComponent {
10232
10368
  groups: [...result.groups],
10233
10369
  });
10234
10370
  }
10371
+ // --- Keyboard shortcuts drawer ---
10372
+ onKeyboardShortcutsClick() {
10373
+ this.drawerService.open(GridKeyboardShortcutsDrawerComponent, { title: 'Keyboard shortcuts' });
10374
+ }
10235
10375
  // --- Settings drawer ---
10236
10376
  static DENSITY_ROW_HEIGHT = {
10237
10377
  small: 32,
@@ -10371,8 +10511,9 @@ class MozGridComponent {
10371
10511
  try {
10372
10512
  const text = await navigator.clipboard.readText();
10373
10513
  const pasteRows = text.split('\n').map((line) => line.split('\t'));
10374
- if (this.state.mode() === 'client') {
10375
- this.applyPasteToSelectedRows(event.selectedRows, pasteRows);
10514
+ const changes = this.applyPasteToSelectedRows(event.selectedRows, pasteRows);
10515
+ if (changes.length > 0) {
10516
+ this.historyEngine.record('paste', changes);
10376
10517
  }
10377
10518
  this.bulkPaste.emit({
10378
10519
  range: null,
@@ -10404,18 +10545,16 @@ class MozGridComponent {
10404
10545
  col: Math.min(focused.col + (rows[0]?.length ?? 1) - 1, cols.length - 1),
10405
10546
  },
10406
10547
  };
10407
- if (this.state.mode() === 'client') {
10408
- // If a cut is pending, first wipe the source cells so cut+paste == move,
10409
- // and fold both halves into a single undoable history op.
10410
- const cutSource = this.state.cutSource();
10411
- const clearChanges = cutSource ? this.clipboardEngine.clearRange(cutSource) : [];
10412
- const pasteChanges = this.clipboardEngine.applyPaste(pasteRange, rows);
10413
- const allChanges = [...clearChanges, ...pasteChanges];
10414
- if (allChanges.length > 0) {
10415
- this.historyEngine.record(cutSource ? 'cut' : 'paste', allChanges);
10416
- }
10417
- this.clipboardEngine.clearCut();
10548
+ // If a cut is pending, first wipe the source cells so cut+paste == move,
10549
+ // and fold both halves into a single undoable history op.
10550
+ const cutSource = this.state.cutSource();
10551
+ const clearChanges = cutSource ? this.clipboardEngine.clearRange(cutSource) : [];
10552
+ const pasteChanges = this.clipboardEngine.applyPaste(pasteRange, rows);
10553
+ const allChanges = [...clearChanges, ...pasteChanges];
10554
+ if (allChanges.length > 0) {
10555
+ this.historyEngine.record(cutSource ? 'cut' : 'paste', allChanges);
10418
10556
  }
10557
+ this.clipboardEngine.clearCut();
10419
10558
  this.bulkPaste.emit({
10420
10559
  range: pasteRange,
10421
10560
  data: rows,
@@ -10455,9 +10594,12 @@ class MozGridComponent {
10455
10594
  onBulkDelete() {
10456
10595
  if (this.state.activeSelectionMode() === 'rows') {
10457
10596
  const event = this.rowSelectionEngine.getSelectionEvent();
10458
- if (this.state.mode() === 'client') {
10459
- this.deleteSelectedRows(event.selectedRows);
10597
+ const changes = this.deleteSelectedRows(event.selectedRows);
10598
+ if (changes.length > 0) {
10599
+ this.historyEngine.record('delete', changes);
10460
10600
  }
10601
+ // Intentionally keep the selection active so users can chain actions
10602
+ // (undo, then paste, etc.) — the overlay only closes via the ✕ button.
10461
10603
  this.bulkDelete.emit({
10462
10604
  range: null,
10463
10605
  cellCount: event.count,
@@ -10478,13 +10620,11 @@ class MozGridComponent {
10478
10620
  return;
10479
10621
  const rows = range.end.row - range.start.row + 1;
10480
10622
  const colCount = range.end.col - range.start.col + 1;
10481
- if (this.state.mode() === 'client') {
10482
- const changes = this.clipboardEngine.clearRange(range);
10483
- if (changes.length > 0) {
10484
- this.historyEngine.record('delete', changes);
10485
- }
10486
- this.clipboardEngine.clearCut();
10623
+ const changes = this.clipboardEngine.clearRange(range);
10624
+ if (changes.length > 0) {
10625
+ this.historyEngine.record('delete', changes);
10487
10626
  }
10627
+ this.clipboardEngine.clearCut();
10488
10628
  this.bulkDelete.emit({
10489
10629
  range,
10490
10630
  cellCount: rows * colCount,
@@ -10537,6 +10677,7 @@ class MozGridComponent {
10537
10677
  applyPasteToSelectedRows(selectedRows, pasteRows) {
10538
10678
  const cols = this.state.visibleColumns();
10539
10679
  const idField = this.state.rowIdField?.() ?? 'id';
10680
+ const changes = [];
10540
10681
  this.state.sourceData.update((data) => {
10541
10682
  const updated = [...data];
10542
10683
  for (let ri = 0; ri < Math.min(selectedRows.length, pasteRows.length); ri++) {
@@ -10552,10 +10693,14 @@ class MozGridComponent {
10552
10693
  if (!field)
10553
10694
  continue;
10554
10695
  const coerced = this.coerceAndValidate(field, pasteRows[ri][ci], updated[dataIndex]);
10555
- if (coerced !== PASTE_SKIP) {
10556
- rowCopy[field] = coerced;
10557
- changed = true;
10558
- }
10696
+ if (coerced === PASTE_SKIP)
10697
+ continue;
10698
+ const before = updated[dataIndex][field];
10699
+ if (before === coerced)
10700
+ continue;
10701
+ rowCopy[field] = coerced;
10702
+ changes.push({ rowIndex: dataIndex, field, before, after: coerced });
10703
+ changed = true;
10559
10704
  }
10560
10705
  if (changed) {
10561
10706
  updated[dataIndex] = rowCopy;
@@ -10563,10 +10708,12 @@ class MozGridComponent {
10563
10708
  }
10564
10709
  return updated;
10565
10710
  });
10711
+ return changes;
10566
10712
  }
10567
10713
  deleteSelectedRows(selectedRows) {
10568
10714
  const cols = this.state.visibleColumns();
10569
10715
  const idField = this.state.rowIdField?.() ?? 'id';
10716
+ const changes = [];
10570
10717
  this.state.sourceData.update((data) => {
10571
10718
  const updated = [...data];
10572
10719
  for (const selectedRow of selectedRows) {
@@ -10578,10 +10725,14 @@ class MozGridComponent {
10578
10725
  let changed = false;
10579
10726
  for (const col of cols) {
10580
10727
  const coerced = this.coerceAndValidate(col.field, null, updated[dataIndex]);
10581
- if (coerced !== PASTE_SKIP) {
10582
- rowCopy[col.field] = coerced;
10583
- changed = true;
10584
- }
10728
+ if (coerced === PASTE_SKIP)
10729
+ continue;
10730
+ const before = updated[dataIndex][col.field];
10731
+ if (before === coerced)
10732
+ continue;
10733
+ rowCopy[col.field] = coerced;
10734
+ changes.push({ rowIndex: dataIndex, field: col.field, before, after: coerced });
10735
+ changed = true;
10585
10736
  }
10586
10737
  if (changed) {
10587
10738
  updated[dataIndex] = rowCopy;
@@ -10589,6 +10740,7 @@ class MozGridComponent {
10589
10740
  }
10590
10741
  return updated;
10591
10742
  });
10743
+ return changes;
10592
10744
  }
10593
10745
  coerceAndValidate(field, rawValue, row) {
10594
10746
  const def = this.state.columnDefMap().get(field);
@@ -10678,30 +10830,29 @@ class MozGridComponent {
10678
10830
  <div class="moz-grid__toolbar">
10679
10831
  <div class="moz-grid__toolbar-left">
10680
10832
  @if (fullscreen()) {
10681
- <moz-icon-button
10682
- id="grid-fullscreen"
10683
- [ghost]="true"
10684
- size="s"
10685
- [ariaLabel]="isFullscreen() ? 'Exit fullscreen' : 'Fullscreen'"
10686
- (activated)="toggleFullscreen()"
10687
- >
10688
- @if (isFullscreen()) {
10689
- <FullscreenExit20 icon />
10690
- } @else {
10691
- <FullscreenEnter20 icon />
10692
- }
10693
- </moz-icon-button>
10694
- }
10695
- @if (exportable()) {
10696
- <moz-icon-button
10697
- id="grid-export"
10698
- [ghost]="true"
10699
- size="s"
10700
- ariaLabel="Export CSV"
10701
- (activated)="onExportCsv()"
10702
- >
10703
- <Download20 icon />
10704
- </moz-icon-button>
10833
+ <moz-icon-button
10834
+ id="grid-fullscreen"
10835
+ [ghost]="true"
10836
+ size="s"
10837
+ [ariaLabel]="isFullscreen() ? 'Exit fullscreen' : 'Fullscreen'"
10838
+ (activated)="toggleFullscreen()"
10839
+ >
10840
+ @if (isFullscreen()) {
10841
+ <FullscreenExit20 icon />
10842
+ } @else {
10843
+ <FullscreenEnter20 icon />
10844
+ }
10845
+ </moz-icon-button>
10846
+ } @if (exportable()) {
10847
+ <moz-icon-button
10848
+ id="grid-export"
10849
+ [ghost]="true"
10850
+ size="s"
10851
+ ariaLabel="Export CSV"
10852
+ (activated)="onExportCsv()"
10853
+ >
10854
+ <Download20 icon />
10855
+ </moz-icon-button>
10705
10856
  }
10706
10857
  <moz-icon-button
10707
10858
  id="grid-filter"
@@ -10730,113 +10881,113 @@ class MozGridComponent {
10730
10881
  >
10731
10882
  <ViewGridX420 icon />
10732
10883
  </moz-icon-button>
10884
+ <moz-icon-button
10885
+ id="grid-keyboard-shortcuts"
10886
+ size="s"
10887
+ [ghost]="true"
10888
+ ariaLabel="Keyboard shortcuts"
10889
+ (activated)="onKeyboardShortcutsClick()"
10890
+ >
10891
+ <Keyboard20 icon />
10892
+ </moz-icon-button>
10733
10893
  @for (def of toolbarStartDefs(); track def) {
10734
- <ng-container [ngTemplateOutlet]="def.template" />
10894
+ <ng-container [ngTemplateOutlet]="def.template" />
10735
10895
  }
10736
10896
  </div>
10737
10897
 
10738
10898
  <!-- Selection banner -->
10739
10899
  @if (selectionBannerVisible()) {
10740
- <div class="moz-grid__selection-banner">
10741
- <span class="moz-grid__selection-text">
10742
- {{ rowSelectionEngine.count() }} row(s) selected
10743
- </span>
10744
- @if (rowSelectionEngine.count() < selectionTotalCount()) {
10745
- <button
10746
- moz-button
10747
- type="button"
10748
- [size]="'s'"
10749
- [ghost]="true"
10750
- [appearance]="'accent'"
10751
- (click)="onSelectAllRows()"
10752
- >
10753
- Select all {{ selectionTotalCount() }} rows
10754
- </button>
10755
- }
10756
- <button
10757
- moz-button
10758
- type="button"
10759
- [size]="'s'"
10760
- [ghost]="true"
10761
- (click)="onClearSelection()"
10762
- >
10763
- Clear
10764
- </button>
10765
- </div>
10900
+ <div class="moz-grid__selection-banner">
10901
+ <span class="moz-grid__selection-text">
10902
+ {{ rowSelectionEngine.count() }} row(s) selected
10903
+ </span>
10904
+ @if (rowSelectionEngine.count() < selectionTotalCount()) {
10905
+ <button
10906
+ moz-button
10907
+ type="button"
10908
+ [size]="'s'"
10909
+ [ghost]="true"
10910
+ [appearance]="'accent'"
10911
+ (click)="onSelectAllRows()"
10912
+ >
10913
+ Select all {{ selectionTotalCount() }} rows
10914
+ </button>
10915
+ }
10916
+ <button moz-button type="button" [size]="'s'" [ghost]="true" (click)="onClearSelection()">
10917
+ Clear
10918
+ </button>
10919
+ </div>
10766
10920
  }
10767
10921
 
10768
10922
  <div class="moz-grid__toolbar-right">
10769
10923
  @for (def of toolbarEndDefs(); track def) {
10770
- <ng-container [ngTemplateOutlet]="def.template" />
10924
+ <ng-container [ngTemplateOutlet]="def.template" />
10771
10925
  }
10772
10926
  </div>
10773
10927
  </div>
10774
10928
 
10775
10929
  <!-- Tag bars (outside .moz-grid) -->
10776
10930
  @if (state.groupColumns().length > 0) {
10777
- <div class="moz-grid__tag-bar">
10778
- <span class="moz-grid__tag-bar-label">GROUPED BY</span>
10779
- @for (entry of state.groupColumns(); track entry.field) {
10780
- <div>
10781
- <moz-tag
10782
- type="removable"
10783
- size="s"
10784
- [id]="'group-' + entry.field"
10785
- (removeTag)="onRemoveGroup(entry.field)"
10786
- >
10787
- <button
10788
- type="button"
10789
- class="moz-grid__group-tag-btn"
10790
- (click)="onToggleGroupSort(entry.field)"
10791
- >
10792
- {{ getColumnLabel(entry.field) }}
10793
- @if (entry.sortDirection === 'asc') {
10794
- <ChevronUp20 />
10795
- } @else {
10796
- <ChevronDown20 />
10797
- }
10798
- </button>
10799
- </moz-tag>
10800
- </div>
10801
- }
10802
- </div>
10803
- }
10804
- @if (hasHiddenColumns()) {
10805
- <div class="moz-grid__tag-bar">
10806
- <span class="moz-grid__tag-bar-label">HIDDEN COLUMNS</span>
10807
- @for (col of hiddenColumnsList(); track col.field) {
10808
- <moz-tag
10809
- type="removable"
10810
- size="s"
10811
- [id]="'hidden-' + col.field"
10812
- removableLabel="Restore"
10813
- (removeTag)="onRestoreColumn(col.field)"
10814
- >{{ col.label }}</moz-tag
10931
+ <div class="moz-grid__tag-bar">
10932
+ <span class="moz-grid__tag-bar-label">GROUPED BY</span>
10933
+ @for (entry of state.groupColumns(); track entry.field) {
10934
+ <div>
10935
+ <moz-tag
10936
+ type="removable"
10937
+ size="s"
10938
+ [id]="'group-' + entry.field"
10939
+ (removeTag)="onRemoveGroup(entry.field)"
10940
+ >
10941
+ <button
10942
+ type="button"
10943
+ class="moz-grid__group-tag-btn"
10944
+ (click)="onToggleGroupSort(entry.field)"
10815
10945
  >
10816
- }
10817
- @if (hiddenColumnsList().length > 1) {
10818
- <button type="button" class="moz-grid__tag-action-btn" (click)="onRestoreAllColumns()">
10819
- Restore all
10946
+ {{ getColumnLabel(entry.field) }}
10947
+ @if (entry.sortDirection === 'asc') {
10948
+ <ChevronUp20 />
10949
+ } @else {
10950
+ <ChevronDown20 />
10951
+ }
10820
10952
  </button>
10821
- }
10822
- </div>
10823
- }
10824
- @if (state.activeFilters().length > 0) {
10825
- <div class="moz-grid__tag-bar">
10826
- <span class="moz-grid__tag-bar-label">FILTERED BY</span>
10827
- @for (filter of state.activeFilters(); track filter.field) {
10828
- <moz-tag
10829
- [type]="filter.removable ? 'removable' : 'informative'"
10830
- size="s"
10831
- [id]="'filter-' + filter.field"
10832
- (removeTag)="onRemoveFilter(filter.field)"
10833
- >{{ filter.label }}</moz-tag
10834
- >
10835
- }
10836
- <button type="button" class="moz-grid__tag-action-btn" (click)="onRemoveAllFilters()">
10837
- Remove all
10838
- </button>
10953
+ </moz-tag>
10839
10954
  </div>
10955
+ }
10956
+ </div>
10957
+ } @if (hasHiddenColumns()) {
10958
+ <div class="moz-grid__tag-bar">
10959
+ <span class="moz-grid__tag-bar-label">HIDDEN COLUMNS</span>
10960
+ @for (col of hiddenColumnsList(); track col.field) {
10961
+ <moz-tag
10962
+ type="removable"
10963
+ size="s"
10964
+ [id]="'hidden-' + col.field"
10965
+ removableLabel="Restore"
10966
+ (removeTag)="onRestoreColumn(col.field)"
10967
+ >{{ col.label }}</moz-tag
10968
+ >
10969
+ } @if (hiddenColumnsList().length > 1) {
10970
+ <button type="button" class="moz-grid__tag-action-btn" (click)="onRestoreAllColumns()">
10971
+ Restore all
10972
+ </button>
10973
+ }
10974
+ </div>
10975
+ } @if (state.activeFilters().length > 0) {
10976
+ <div class="moz-grid__tag-bar">
10977
+ <span class="moz-grid__tag-bar-label">FILTERED BY</span>
10978
+ @for (filter of state.activeFilters(); track filter.field) {
10979
+ <moz-tag
10980
+ [type]="filter.removable ? 'removable' : 'informative'"
10981
+ size="s"
10982
+ [id]="'filter-' + filter.field"
10983
+ (removeTag)="onRemoveFilter(filter.field)"
10984
+ >{{ filter.label }}</moz-tag
10985
+ >
10986
+ }
10987
+ <button type="button" class="moz-grid__tag-action-btn" (click)="onRemoveAllFilters()">
10988
+ Remove all
10989
+ </button>
10990
+ </div>
10840
10991
  }
10841
10992
  <ng-content select="[mozGridFilterTags]" />
10842
10993
 
@@ -10873,13 +11024,12 @@ class MozGridComponent {
10873
11024
 
10874
11025
  <!-- Footer: pagination or infinite scroll loading indicator -->
10875
11026
  @if (showPagination()) {
10876
- <moz-grid-footer
10877
- [pageSizeOptions]="pageSizeOptions()"
10878
- (pageChange)="pageChange.emit($event)"
10879
- />
10880
- }
10881
- @if (showInfiniteScrollLoader()) {
10882
- <moz-grid-loading-indicator />
11027
+ <moz-grid-footer
11028
+ [pageSizeOptions]="pageSizeOptions()"
11029
+ (pageChange)="pageChange.emit($event)"
11030
+ />
11031
+ } @if (showInfiniteScrollLoader()) {
11032
+ <moz-grid-loading-indicator />
10883
11033
  }
10884
11034
 
10885
11035
  <!-- Bulk selection action bar -->
@@ -10892,7 +11042,7 @@ class MozGridComponent {
10892
11042
  />
10893
11043
  </div>
10894
11044
  </div>
10895
- `, isInline: true, styles: [":host{display:block;height:100%}.moz-grid-wrapper{display:flex;flex-direction:column;font-family:var(--font-family-primary);height:100%;min-height:0;gap:16px}.moz-grid-wrapper--fullscreen{position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:9999;background:var(--color-background-primary)}.moz-grid{display:flex;flex-direction:column;border-radius:var(--border-radius-l);overflow:hidden;background:var(--color-background-primary);flex:1;min-height:0;position:relative;box-shadow:0 0 6px #cdd4d8}.moz-grid:focus{outline:none}.moz-grid--loading{opacity:.6;pointer-events:none}.moz-grid__toolbar{display:flex;align-items:center;justify-content:space-between;flex-shrink:0;min-height:48px;gap:var(--spacing-s, 8px)}.moz-grid__toolbar-left,.moz-grid__toolbar-right{display:flex;align-items:center;gap:var(--spacing-xs, 4px)}.moz-grid__selection-banner{display:flex;align-items:center;gap:var(--spacing-s, 8px);flex:1;justify-content:center;border-radius:var(--border-radius-s);border:1px solid var(--Border-Primary, #cdd4d8);background:var(--Neutral-Grey-000, #fff)}.moz-grid__selection-text{font-size:var(--font-size-s, 14px);color:var(--color-text-primary);white-space:nowrap}.moz-grid__selection-link{padding:0;border:none;background:transparent;color:var(--color-background-accent-inverse);font-size:var(--font-size-s, 14px);font-weight:600;cursor:pointer;white-space:nowrap;text-decoration:underline}.moz-grid__selection-link:hover{color:var(--color-primary-dark, #1557b0)}.moz-grid__tag-bar{display:flex;align-items:center;flex-wrap:wrap;gap:var(--spacing-xs, 4px);padding:var(--spacing-xxs, 2px) var(--spacing-s, 8px);flex-shrink:0}.moz-grid__tag-bar-label{width:100%;font-size:var(--font-size-xs, 12px);text-transform:uppercase;white-space:nowrap;color:var(--text-icon-tertiary);font-size:var(--Typography-Font-size-Body-XS, 12px);font-weight:400}.moz-grid__tag-action-btn{padding:2px 8px;border:none;background:transparent;color:var(--color-background-accent-inverse);font-size:var(--font-size-xs, 12px);font-weight:600;cursor:pointer}.moz-grid__tag-action-btn:hover{text-decoration:underline}.moz-grid__group-tag-btn{display:inline-flex;align-items:center;gap:2px;padding:0;border:none;background:transparent;cursor:pointer;font:inherit;color:inherit;line-height:1}.moz-grid__group-tag-btn ::ng-deep svg{fill:#fff!important;width:16px;height:16px}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MozGridHeaderComponent, selector: "moz-grid-header", inputs: ["showCheckbox", "showExpand", "reorderable"], outputs: ["sortClick", "menuAction", "resizeStart", "selectAllToggle", "columnReorder"] }, { kind: "component", type: MozGridBodyComponent, selector: "moz-grid-body", inputs: ["showCheckbox", "showExpand", "detailTemplate"], outputs: ["cellEdit", "cellEditCancel", "rowSelectionToggle", "groupToggle"] }, { kind: "component", type: MozGridFooterComponent, selector: "moz-grid-footer", inputs: ["pageSizeOptions"], outputs: ["pageChange"] }, { kind: "component", type: MozGridLoadingIndicatorComponent, selector: "moz-grid-loading-indicator" }, { kind: "component", type: MozGridSelectionBarComponent, selector: "moz-grid-selection-bar", outputs: ["editClick", "copyClick", "pasteClick", "deleteClick", "exportClick"] }, { kind: "component", type: MozTagComponent, selector: "moz-tag", inputs: ["type", "size", "id", "name", "disabled", "contextualisedNumber", "removableLabel"], outputs: ["removeTag"] }, { kind: "component", type: MozIconButtonComponent, selector: "moz-icon-button", inputs: ["id", "appearance", "size", "disabled", "ghost", "outlined", "type", "ariaLabel"], outputs: ["activated"] }, { kind: "component", type: ViewGridX420, selector: "ViewGridX420", inputs: ["hostClass"] }, { kind: "component", type: Filter20, selector: "Filter20", inputs: ["hostClass"] }, { kind: "component", type: Settings20, selector: "Settings20", inputs: ["hostClass"] }, { kind: "component", type: FullscreenEnter20, selector: "FullscreenEnter20", inputs: ["hostClass"] }, { kind: "component", type: FullscreenExit20, selector: "FullscreenExit20", inputs: ["hostClass"] }, { kind: "component", type: Download20, selector: "Download20", inputs: ["hostClass"] }, { kind: "component", type: ChevronUp20, selector: "ChevronUp20", inputs: ["hostClass"] }, { kind: "component", type: ChevronDown20, selector: "ChevronDown20", inputs: ["hostClass"] }, { kind: "component", type: MozButtonComponent, selector: "button[moz-button]", inputs: ["appearance", "size", "disabled", "ghost", "outlined", "iconPosition", "type", "isLoading"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11045
+ `, isInline: true, styles: [":host{display:block;height:100%}.moz-grid-wrapper{display:flex;flex-direction:column;font-family:var(--font-family-primary);height:100%;min-height:0;gap:16px}.moz-grid-wrapper--fullscreen{position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:9999;background:var(--color-background-primary)}.moz-grid{display:flex;flex-direction:column;border-radius:var(--border-radius-l);overflow:hidden;background:var(--color-background-primary);flex:1;min-height:0;position:relative;box-shadow:0 0 6px #cdd4d8}.moz-grid:focus{outline:none}.moz-grid--loading{opacity:.6;pointer-events:none}.moz-grid__toolbar{display:flex;align-items:center;justify-content:space-between;flex-shrink:0;min-height:48px;gap:var(--spacing-s, 8px)}.moz-grid__toolbar-left,.moz-grid__toolbar-right{display:flex;align-items:center;gap:var(--spacing-xs, 4px)}.moz-grid__selection-banner{display:flex;align-items:center;gap:var(--spacing-s, 8px);flex:1;justify-content:center;border-radius:var(--border-radius-s);border:1px solid var(--Border-Primary, #cdd4d8);background:var(--Neutral-Grey-000, #fff)}.moz-grid__selection-text{font-size:var(--font-size-s, 14px);color:var(--color-text-primary);white-space:nowrap}.moz-grid__selection-link{padding:0;border:none;background:transparent;color:var(--color-background-accent-inverse);font-size:var(--font-size-s, 14px);font-weight:600;cursor:pointer;white-space:nowrap;text-decoration:underline}.moz-grid__selection-link:hover{color:var(--color-primary-dark, #1557b0)}.moz-grid__tag-bar{display:flex;align-items:center;flex-wrap:wrap;gap:var(--spacing-xs, 4px);padding:var(--spacing-xxs, 2px) var(--spacing-s, 8px);flex-shrink:0}.moz-grid__tag-bar-label{width:100%;font-size:var(--font-size-xs, 12px);text-transform:uppercase;white-space:nowrap;color:var(--text-icon-tertiary);font-size:var(--Typography-Font-size-Body-XS, 12px);font-weight:400}.moz-grid__tag-action-btn{padding:2px 8px;border:none;background:transparent;color:var(--color-background-accent-inverse);font-size:var(--font-size-xs, 12px);font-weight:600;cursor:pointer}.moz-grid__tag-action-btn:hover{text-decoration:underline}.moz-grid__group-tag-btn{display:inline-flex;align-items:center;gap:2px;padding:0;border:none;background:transparent;cursor:pointer;font:inherit;color:inherit;line-height:1}.moz-grid__group-tag-btn ::ng-deep svg{fill:#fff!important;width:16px;height:16px}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: MozGridHeaderComponent, selector: "moz-grid-header", inputs: ["showCheckbox", "showExpand", "reorderable"], outputs: ["sortClick", "menuAction", "resizeStart", "selectAllToggle", "columnReorder"] }, { kind: "component", type: MozGridBodyComponent, selector: "moz-grid-body", inputs: ["showCheckbox", "showExpand", "detailTemplate"], outputs: ["cellEdit", "cellEditCancel", "rowSelectionToggle", "groupToggle"] }, { kind: "component", type: MozGridFooterComponent, selector: "moz-grid-footer", inputs: ["pageSizeOptions"], outputs: ["pageChange"] }, { kind: "component", type: MozGridLoadingIndicatorComponent, selector: "moz-grid-loading-indicator" }, { kind: "component", type: MozGridSelectionBarComponent, selector: "moz-grid-selection-bar", outputs: ["editClick", "copyClick", "pasteClick", "deleteClick", "exportClick"] }, { kind: "component", type: MozTagComponent, selector: "moz-tag", inputs: ["type", "size", "id", "name", "disabled", "contextualisedNumber", "removableLabel"], outputs: ["removeTag"] }, { kind: "component", type: MozIconButtonComponent, selector: "moz-icon-button", inputs: ["id", "appearance", "size", "disabled", "ghost", "outlined", "type", "ariaLabel"], outputs: ["activated"] }, { kind: "component", type: ViewGridX420, selector: "ViewGridX420", inputs: ["hostClass"] }, { kind: "component", type: Filter20, selector: "Filter20", inputs: ["hostClass"] }, { kind: "component", type: Settings20, selector: "Settings20", inputs: ["hostClass"] }, { kind: "component", type: FullscreenEnter20, selector: "FullscreenEnter20", inputs: ["hostClass"] }, { kind: "component", type: FullscreenExit20, selector: "FullscreenExit20", inputs: ["hostClass"] }, { kind: "component", type: Download20, selector: "Download20", inputs: ["hostClass"] }, { kind: "component", type: ChevronUp20, selector: "ChevronUp20", inputs: ["hostClass"] }, { kind: "component", type: ChevronDown20, selector: "ChevronDown20", inputs: ["hostClass"] }, { kind: "component", type: Keyboard20, selector: "Keyboard20", inputs: ["hostClass"] }, { kind: "component", type: MozButtonComponent, selector: "button[moz-button]", inputs: ["appearance", "size", "disabled", "ghost", "outlined", "iconPosition", "type", "isLoading"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10896
11046
  }
10897
11047
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MozGridComponent, decorators: [{
10898
11048
  type: Component,
@@ -10937,6 +11087,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
10937
11087
  Download20,
10938
11088
  ChevronUp20,
10939
11089
  ChevronDown20,
11090
+ Keyboard20,
10940
11091
  MozButtonComponent,
10941
11092
  ], template: `
10942
11093
  <div class="moz-grid-wrapper" [class.moz-grid-wrapper--fullscreen]="isFullscreen()">
@@ -10944,30 +11095,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
10944
11095
  <div class="moz-grid__toolbar">
10945
11096
  <div class="moz-grid__toolbar-left">
10946
11097
  @if (fullscreen()) {
10947
- <moz-icon-button
10948
- id="grid-fullscreen"
10949
- [ghost]="true"
10950
- size="s"
10951
- [ariaLabel]="isFullscreen() ? 'Exit fullscreen' : 'Fullscreen'"
10952
- (activated)="toggleFullscreen()"
10953
- >
10954
- @if (isFullscreen()) {
10955
- <FullscreenExit20 icon />
10956
- } @else {
10957
- <FullscreenEnter20 icon />
10958
- }
10959
- </moz-icon-button>
10960
- }
10961
- @if (exportable()) {
10962
- <moz-icon-button
10963
- id="grid-export"
10964
- [ghost]="true"
10965
- size="s"
10966
- ariaLabel="Export CSV"
10967
- (activated)="onExportCsv()"
10968
- >
10969
- <Download20 icon />
10970
- </moz-icon-button>
11098
+ <moz-icon-button
11099
+ id="grid-fullscreen"
11100
+ [ghost]="true"
11101
+ size="s"
11102
+ [ariaLabel]="isFullscreen() ? 'Exit fullscreen' : 'Fullscreen'"
11103
+ (activated)="toggleFullscreen()"
11104
+ >
11105
+ @if (isFullscreen()) {
11106
+ <FullscreenExit20 icon />
11107
+ } @else {
11108
+ <FullscreenEnter20 icon />
11109
+ }
11110
+ </moz-icon-button>
11111
+ } @if (exportable()) {
11112
+ <moz-icon-button
11113
+ id="grid-export"
11114
+ [ghost]="true"
11115
+ size="s"
11116
+ ariaLabel="Export CSV"
11117
+ (activated)="onExportCsv()"
11118
+ >
11119
+ <Download20 icon />
11120
+ </moz-icon-button>
10971
11121
  }
10972
11122
  <moz-icon-button
10973
11123
  id="grid-filter"
@@ -10996,113 +11146,113 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
10996
11146
  >
10997
11147
  <ViewGridX420 icon />
10998
11148
  </moz-icon-button>
11149
+ <moz-icon-button
11150
+ id="grid-keyboard-shortcuts"
11151
+ size="s"
11152
+ [ghost]="true"
11153
+ ariaLabel="Keyboard shortcuts"
11154
+ (activated)="onKeyboardShortcutsClick()"
11155
+ >
11156
+ <Keyboard20 icon />
11157
+ </moz-icon-button>
10999
11158
  @for (def of toolbarStartDefs(); track def) {
11000
- <ng-container [ngTemplateOutlet]="def.template" />
11159
+ <ng-container [ngTemplateOutlet]="def.template" />
11001
11160
  }
11002
11161
  </div>
11003
11162
 
11004
11163
  <!-- Selection banner -->
11005
11164
  @if (selectionBannerVisible()) {
11006
- <div class="moz-grid__selection-banner">
11007
- <span class="moz-grid__selection-text">
11008
- {{ rowSelectionEngine.count() }} row(s) selected
11009
- </span>
11010
- @if (rowSelectionEngine.count() < selectionTotalCount()) {
11011
- <button
11012
- moz-button
11013
- type="button"
11014
- [size]="'s'"
11015
- [ghost]="true"
11016
- [appearance]="'accent'"
11017
- (click)="onSelectAllRows()"
11018
- >
11019
- Select all {{ selectionTotalCount() }} rows
11020
- </button>
11021
- }
11022
- <button
11023
- moz-button
11024
- type="button"
11025
- [size]="'s'"
11026
- [ghost]="true"
11027
- (click)="onClearSelection()"
11028
- >
11029
- Clear
11030
- </button>
11031
- </div>
11165
+ <div class="moz-grid__selection-banner">
11166
+ <span class="moz-grid__selection-text">
11167
+ {{ rowSelectionEngine.count() }} row(s) selected
11168
+ </span>
11169
+ @if (rowSelectionEngine.count() < selectionTotalCount()) {
11170
+ <button
11171
+ moz-button
11172
+ type="button"
11173
+ [size]="'s'"
11174
+ [ghost]="true"
11175
+ [appearance]="'accent'"
11176
+ (click)="onSelectAllRows()"
11177
+ >
11178
+ Select all {{ selectionTotalCount() }} rows
11179
+ </button>
11180
+ }
11181
+ <button moz-button type="button" [size]="'s'" [ghost]="true" (click)="onClearSelection()">
11182
+ Clear
11183
+ </button>
11184
+ </div>
11032
11185
  }
11033
11186
 
11034
11187
  <div class="moz-grid__toolbar-right">
11035
11188
  @for (def of toolbarEndDefs(); track def) {
11036
- <ng-container [ngTemplateOutlet]="def.template" />
11189
+ <ng-container [ngTemplateOutlet]="def.template" />
11037
11190
  }
11038
11191
  </div>
11039
11192
  </div>
11040
11193
 
11041
11194
  <!-- Tag bars (outside .moz-grid) -->
11042
11195
  @if (state.groupColumns().length > 0) {
11043
- <div class="moz-grid__tag-bar">
11044
- <span class="moz-grid__tag-bar-label">GROUPED BY</span>
11045
- @for (entry of state.groupColumns(); track entry.field) {
11046
- <div>
11047
- <moz-tag
11048
- type="removable"
11049
- size="s"
11050
- [id]="'group-' + entry.field"
11051
- (removeTag)="onRemoveGroup(entry.field)"
11052
- >
11053
- <button
11054
- type="button"
11055
- class="moz-grid__group-tag-btn"
11056
- (click)="onToggleGroupSort(entry.field)"
11057
- >
11058
- {{ getColumnLabel(entry.field) }}
11059
- @if (entry.sortDirection === 'asc') {
11060
- <ChevronUp20 />
11061
- } @else {
11062
- <ChevronDown20 />
11063
- }
11064
- </button>
11065
- </moz-tag>
11066
- </div>
11067
- }
11068
- </div>
11069
- }
11070
- @if (hasHiddenColumns()) {
11071
- <div class="moz-grid__tag-bar">
11072
- <span class="moz-grid__tag-bar-label">HIDDEN COLUMNS</span>
11073
- @for (col of hiddenColumnsList(); track col.field) {
11074
- <moz-tag
11075
- type="removable"
11076
- size="s"
11077
- [id]="'hidden-' + col.field"
11078
- removableLabel="Restore"
11079
- (removeTag)="onRestoreColumn(col.field)"
11080
- >{{ col.label }}</moz-tag
11196
+ <div class="moz-grid__tag-bar">
11197
+ <span class="moz-grid__tag-bar-label">GROUPED BY</span>
11198
+ @for (entry of state.groupColumns(); track entry.field) {
11199
+ <div>
11200
+ <moz-tag
11201
+ type="removable"
11202
+ size="s"
11203
+ [id]="'group-' + entry.field"
11204
+ (removeTag)="onRemoveGroup(entry.field)"
11205
+ >
11206
+ <button
11207
+ type="button"
11208
+ class="moz-grid__group-tag-btn"
11209
+ (click)="onToggleGroupSort(entry.field)"
11081
11210
  >
11082
- }
11083
- @if (hiddenColumnsList().length > 1) {
11084
- <button type="button" class="moz-grid__tag-action-btn" (click)="onRestoreAllColumns()">
11085
- Restore all
11211
+ {{ getColumnLabel(entry.field) }}
11212
+ @if (entry.sortDirection === 'asc') {
11213
+ <ChevronUp20 />
11214
+ } @else {
11215
+ <ChevronDown20 />
11216
+ }
11086
11217
  </button>
11087
- }
11088
- </div>
11089
- }
11090
- @if (state.activeFilters().length > 0) {
11091
- <div class="moz-grid__tag-bar">
11092
- <span class="moz-grid__tag-bar-label">FILTERED BY</span>
11093
- @for (filter of state.activeFilters(); track filter.field) {
11094
- <moz-tag
11095
- [type]="filter.removable ? 'removable' : 'informative'"
11096
- size="s"
11097
- [id]="'filter-' + filter.field"
11098
- (removeTag)="onRemoveFilter(filter.field)"
11099
- >{{ filter.label }}</moz-tag
11100
- >
11101
- }
11102
- <button type="button" class="moz-grid__tag-action-btn" (click)="onRemoveAllFilters()">
11103
- Remove all
11104
- </button>
11218
+ </moz-tag>
11105
11219
  </div>
11220
+ }
11221
+ </div>
11222
+ } @if (hasHiddenColumns()) {
11223
+ <div class="moz-grid__tag-bar">
11224
+ <span class="moz-grid__tag-bar-label">HIDDEN COLUMNS</span>
11225
+ @for (col of hiddenColumnsList(); track col.field) {
11226
+ <moz-tag
11227
+ type="removable"
11228
+ size="s"
11229
+ [id]="'hidden-' + col.field"
11230
+ removableLabel="Restore"
11231
+ (removeTag)="onRestoreColumn(col.field)"
11232
+ >{{ col.label }}</moz-tag
11233
+ >
11234
+ } @if (hiddenColumnsList().length > 1) {
11235
+ <button type="button" class="moz-grid__tag-action-btn" (click)="onRestoreAllColumns()">
11236
+ Restore all
11237
+ </button>
11238
+ }
11239
+ </div>
11240
+ } @if (state.activeFilters().length > 0) {
11241
+ <div class="moz-grid__tag-bar">
11242
+ <span class="moz-grid__tag-bar-label">FILTERED BY</span>
11243
+ @for (filter of state.activeFilters(); track filter.field) {
11244
+ <moz-tag
11245
+ [type]="filter.removable ? 'removable' : 'informative'"
11246
+ size="s"
11247
+ [id]="'filter-' + filter.field"
11248
+ (removeTag)="onRemoveFilter(filter.field)"
11249
+ >{{ filter.label }}</moz-tag
11250
+ >
11251
+ }
11252
+ <button type="button" class="moz-grid__tag-action-btn" (click)="onRemoveAllFilters()">
11253
+ Remove all
11254
+ </button>
11255
+ </div>
11106
11256
  }
11107
11257
  <ng-content select="[mozGridFilterTags]" />
11108
11258
 
@@ -11139,13 +11289,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
11139
11289
 
11140
11290
  <!-- Footer: pagination or infinite scroll loading indicator -->
11141
11291
  @if (showPagination()) {
11142
- <moz-grid-footer
11143
- [pageSizeOptions]="pageSizeOptions()"
11144
- (pageChange)="pageChange.emit($event)"
11145
- />
11146
- }
11147
- @if (showInfiniteScrollLoader()) {
11148
- <moz-grid-loading-indicator />
11292
+ <moz-grid-footer
11293
+ [pageSizeOptions]="pageSizeOptions()"
11294
+ (pageChange)="pageChange.emit($event)"
11295
+ />
11296
+ } @if (showInfiniteScrollLoader()) {
11297
+ <moz-grid-loading-indicator />
11149
11298
  }
11150
11299
 
11151
11300
  <!-- Bulk selection action bar -->
@@ -11160,7 +11309,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
11160
11309
  </div>
11161
11310
  `, styles: [":host{display:block;height:100%}.moz-grid-wrapper{display:flex;flex-direction:column;font-family:var(--font-family-primary);height:100%;min-height:0;gap:16px}.moz-grid-wrapper--fullscreen{position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:9999;background:var(--color-background-primary)}.moz-grid{display:flex;flex-direction:column;border-radius:var(--border-radius-l);overflow:hidden;background:var(--color-background-primary);flex:1;min-height:0;position:relative;box-shadow:0 0 6px #cdd4d8}.moz-grid:focus{outline:none}.moz-grid--loading{opacity:.6;pointer-events:none}.moz-grid__toolbar{display:flex;align-items:center;justify-content:space-between;flex-shrink:0;min-height:48px;gap:var(--spacing-s, 8px)}.moz-grid__toolbar-left,.moz-grid__toolbar-right{display:flex;align-items:center;gap:var(--spacing-xs, 4px)}.moz-grid__selection-banner{display:flex;align-items:center;gap:var(--spacing-s, 8px);flex:1;justify-content:center;border-radius:var(--border-radius-s);border:1px solid var(--Border-Primary, #cdd4d8);background:var(--Neutral-Grey-000, #fff)}.moz-grid__selection-text{font-size:var(--font-size-s, 14px);color:var(--color-text-primary);white-space:nowrap}.moz-grid__selection-link{padding:0;border:none;background:transparent;color:var(--color-background-accent-inverse);font-size:var(--font-size-s, 14px);font-weight:600;cursor:pointer;white-space:nowrap;text-decoration:underline}.moz-grid__selection-link:hover{color:var(--color-primary-dark, #1557b0)}.moz-grid__tag-bar{display:flex;align-items:center;flex-wrap:wrap;gap:var(--spacing-xs, 4px);padding:var(--spacing-xxs, 2px) var(--spacing-s, 8px);flex-shrink:0}.moz-grid__tag-bar-label{width:100%;font-size:var(--font-size-xs, 12px);text-transform:uppercase;white-space:nowrap;color:var(--text-icon-tertiary);font-size:var(--Typography-Font-size-Body-XS, 12px);font-weight:400}.moz-grid__tag-action-btn{padding:2px 8px;border:none;background:transparent;color:var(--color-background-accent-inverse);font-size:var(--font-size-xs, 12px);font-weight:600;cursor:pointer}.moz-grid__tag-action-btn:hover{text-decoration:underline}.moz-grid__group-tag-btn{display:inline-flex;align-items:center;gap:2px;padding:0;border:none;background:transparent;cursor:pointer;font:inherit;color:inherit;line-height:1}.moz-grid__group-tag-btn ::ng-deep svg{fill:#fff!important;width:16px;height:16px}\n"] }]
11162
11311
  }], ctorParameters: () => [], propDecorators: { gridBody: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MozGridBodyComponent), { isSignal: true }] }], gridContainer: [{ type: i0.ViewChild, args: ['gridContainer', { isSignal: true }] }], columnDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MozGridColumnDef), { isSignal: true }] }], toolbarDefs: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => MozGridToolbarDef), { isSignal: true }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], totalItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "totalItems", required: false }] }], pagination: [{ type: i0.Input, args: [{ isSignal: true, alias: "pagination", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], rowHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowHeight", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], rowSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowSelection", required: false }] }], expandable: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandable", required: false }] }], rowIdField: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowIdField", required: false }] }], detailTemplate: [{ type: i0.Input, args: [{ isSignal: true, alias: "detailTemplate", required: false }] }], fullscreen: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullscreen", required: false }] }], reorderable: [{ type: i0.Input, args: [{ isSignal: true, alias: "reorderable", required: false }] }], stateKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "stateKey", required: false }] }], exportable: [{ type: i0.Input, args: [{ isSignal: true, alias: "exportable", required: false }] }], horizontalVirtualScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "horizontalVirtualScroll", required: false }] }], loadingStrategy: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingStrategy", required: false }] }], scrollThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollThreshold", required: false }] }], plugins: [{ type: i0.Input, args: [{ isSignal: true, alias: "plugins", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], loadMore: [{ type: i0.Output, args: ["loadMore"] }], cellEdit: [{ type: i0.Output, args: ["cellEdit"] }], cellEditCancel: [{ type: i0.Output, args: ["cellEditCancel"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], cellSelectionChange: [{ type: i0.Output, args: ["cellSelectionChange"] }], groupChange: [{ type: i0.Output, args: ["groupChange"] }], filterChange: [{ type: i0.Output, args: ["filterChange"] }], bulkEdit: [{ type: i0.Output, args: ["bulkEdit"] }], bulkCopy: [{ type: i0.Output, args: ["bulkCopy"] }], bulkPaste: [{ type: i0.Output, args: ["bulkPaste"] }], bulkDelete: [{ type: i0.Output, args: ["bulkDelete"] }], fillDown: [{ type: i0.Output, args: ["fillDown"] }], settingsChange: [{ type: i0.Output, args: ["settingsChange"] }] } });
11163
- const PASTE_SKIP = Symbol('PASTE_SKIP');
11164
11312
 
11165
11313
  const DEFAULT_GRID_OPTIONS = {
11166
11314
  mode: 'client',