@mozaic-ds/angular 2.0.41 → 2.0.43
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';
|
|
@@ -4897,6 +4897,7 @@ class GridStateManager {
|
|
|
4897
4897
|
scrollLeft = signal(0, ...(ngDevMode ? [{ debugName: "scrollLeft" }] : /* istanbul ignore next */ []));
|
|
4898
4898
|
scrollTop = signal(0, ...(ngDevMode ? [{ debugName: "scrollTop" }] : /* istanbul ignore next */ []));
|
|
4899
4899
|
scrollViewportWidth = signal(0, ...(ngDevMode ? [{ debugName: "scrollViewportWidth" }] : /* istanbul ignore next */ []));
|
|
4900
|
+
scrollViewportHeight = signal(0, ...(ngDevMode ? [{ debugName: "scrollViewportHeight" }] : /* istanbul ignore next */ []));
|
|
4900
4901
|
scrollContentTotalWidth = signal(0, ...(ngDevMode ? [{ debugName: "scrollContentTotalWidth" }] : /* istanbul ignore next */ []));
|
|
4901
4902
|
// --- Horizontal virtual scroll ---
|
|
4902
4903
|
horizontalVirtualScrollEnabled = signal(false, ...(ngDevMode ? [{ debugName: "horizontalVirtualScrollEnabled" }] : /* istanbul ignore next */ []));
|
|
@@ -4919,6 +4920,8 @@ class GridStateManager {
|
|
|
4919
4920
|
isFilling = signal(false, ...(ngDevMode ? [{ debugName: "isFilling" }] : /* istanbul ignore next */ []));
|
|
4920
4921
|
fillAnchor = signal(null, ...(ngDevMode ? [{ debugName: "fillAnchor" }] : /* istanbul ignore next */ []));
|
|
4921
4922
|
fillTarget = signal(null, ...(ngDevMode ? [{ debugName: "fillTarget" }] : /* istanbul ignore next */ []));
|
|
4923
|
+
// --- Cut (Ctrl+X) source — drives the marching-ants outline in view ---
|
|
4924
|
+
cutSource = signal(null, ...(ngDevMode ? [{ debugName: "cutSource" }] : /* istanbul ignore next */ []));
|
|
4922
4925
|
// --- Expandable Rows ---
|
|
4923
4926
|
expandedRowIds = signal(new Set(), ...(ngDevMode ? [{ debugName: "expandedRowIds" }] : /* istanbul ignore next */ []));
|
|
4924
4927
|
rowIdField = signal('id', ...(ngDevMode ? [{ debugName: "rowIdField" }] : /* istanbul ignore next */ []));
|
|
@@ -5524,8 +5527,485 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
5524
5527
|
type: Injectable
|
|
5525
5528
|
}] });
|
|
5526
5529
|
|
|
5530
|
+
const PASTE_SKIP$1 = Symbol('PASTE_SKIP');
|
|
5531
|
+
/**
|
|
5532
|
+
* Applies a set of cell-level mutations to sourceData and returns the list of
|
|
5533
|
+
* actual changes that occurred, so the caller (usually the history engine) can
|
|
5534
|
+
* record them. `PASTE_SKIP` return values are filtered out transparently.
|
|
5535
|
+
*/
|
|
5536
|
+
class ClipboardEngine {
|
|
5537
|
+
state = inject(GridStateManager);
|
|
5538
|
+
/** Derived by components (marching-ants outline). */
|
|
5539
|
+
cutRange = computed(() => this.state.cutSource(), ...(ngDevMode ? [{ debugName: "cutRange" }] : /* istanbul ignore next */ []));
|
|
5540
|
+
markCut(range) {
|
|
5541
|
+
this.state.cutSource.set(range);
|
|
5542
|
+
}
|
|
5543
|
+
clearCut() {
|
|
5544
|
+
this.state.cutSource.set(null);
|
|
5545
|
+
}
|
|
5546
|
+
/** Vertical fill: row 0 of the range is the source, subsequent rows are targets. */
|
|
5547
|
+
fillDown(range) {
|
|
5548
|
+
if (range.start.row === range.end.row)
|
|
5549
|
+
return [];
|
|
5550
|
+
const cols = this.state.visibleColumns();
|
|
5551
|
+
const defMap = this.state.columnDefMap();
|
|
5552
|
+
const changes = [];
|
|
5553
|
+
this.state.sourceData.update((data) => {
|
|
5554
|
+
const updated = [...data];
|
|
5555
|
+
const sourceRow = updated[range.start.row];
|
|
5556
|
+
if (!sourceRow)
|
|
5557
|
+
return updated;
|
|
5558
|
+
for (let r = range.start.row + 1; r <= range.end.row; r++) {
|
|
5559
|
+
if (!updated[r])
|
|
5560
|
+
continue;
|
|
5561
|
+
const rowCopy = { ...updated[r] };
|
|
5562
|
+
let changed = false;
|
|
5563
|
+
for (let c = range.start.col; c <= range.end.col; c++) {
|
|
5564
|
+
const field = cols[c]?.field;
|
|
5565
|
+
if (!field)
|
|
5566
|
+
continue;
|
|
5567
|
+
const def = defMap.get(field);
|
|
5568
|
+
if (!def?.editable)
|
|
5569
|
+
continue;
|
|
5570
|
+
const sourceValue = def.valueGetter
|
|
5571
|
+
? def.valueGetter(sourceRow)
|
|
5572
|
+
: sourceRow[field];
|
|
5573
|
+
const coerced = this.coerceAndValidate(field, sourceValue, updated[r]);
|
|
5574
|
+
if (coerced === PASTE_SKIP$1)
|
|
5575
|
+
continue;
|
|
5576
|
+
const before = updated[r][field];
|
|
5577
|
+
if (before === coerced)
|
|
5578
|
+
continue;
|
|
5579
|
+
rowCopy[field] = coerced;
|
|
5580
|
+
changes.push({ rowIndex: r, field, before, after: coerced });
|
|
5581
|
+
changed = true;
|
|
5582
|
+
}
|
|
5583
|
+
if (changed)
|
|
5584
|
+
updated[r] = rowCopy;
|
|
5585
|
+
}
|
|
5586
|
+
return updated;
|
|
5587
|
+
});
|
|
5588
|
+
return changes;
|
|
5589
|
+
}
|
|
5590
|
+
/** Horizontal fill: col 0 of the range is the source, subsequent cols are targets. */
|
|
5591
|
+
fillRight(range) {
|
|
5592
|
+
if (range.start.col === range.end.col)
|
|
5593
|
+
return [];
|
|
5594
|
+
const cols = this.state.visibleColumns();
|
|
5595
|
+
const defMap = this.state.columnDefMap();
|
|
5596
|
+
const sourceField = cols[range.start.col]?.field;
|
|
5597
|
+
if (!sourceField)
|
|
5598
|
+
return [];
|
|
5599
|
+
const sourceDef = defMap.get(sourceField);
|
|
5600
|
+
if (!sourceDef)
|
|
5601
|
+
return [];
|
|
5602
|
+
const changes = [];
|
|
5603
|
+
this.state.sourceData.update((data) => {
|
|
5604
|
+
const updated = [...data];
|
|
5605
|
+
for (let r = range.start.row; r <= range.end.row; r++) {
|
|
5606
|
+
const row = updated[r];
|
|
5607
|
+
if (!row)
|
|
5608
|
+
continue;
|
|
5609
|
+
const sourceValue = sourceDef.valueGetter
|
|
5610
|
+
? sourceDef.valueGetter(row)
|
|
5611
|
+
: row[sourceField];
|
|
5612
|
+
const rowCopy = { ...row };
|
|
5613
|
+
let changed = false;
|
|
5614
|
+
for (let c = range.start.col + 1; c <= range.end.col; c++) {
|
|
5615
|
+
const field = cols[c]?.field;
|
|
5616
|
+
if (!field)
|
|
5617
|
+
continue;
|
|
5618
|
+
const def = defMap.get(field);
|
|
5619
|
+
if (!def?.editable)
|
|
5620
|
+
continue;
|
|
5621
|
+
const coerced = this.coerceAndValidate(field, sourceValue, row);
|
|
5622
|
+
if (coerced === PASTE_SKIP$1)
|
|
5623
|
+
continue;
|
|
5624
|
+
const before = row[field];
|
|
5625
|
+
if (before === coerced)
|
|
5626
|
+
continue;
|
|
5627
|
+
rowCopy[field] = coerced;
|
|
5628
|
+
changes.push({ rowIndex: r, field, before, after: coerced });
|
|
5629
|
+
changed = true;
|
|
5630
|
+
}
|
|
5631
|
+
if (changed)
|
|
5632
|
+
updated[r] = rowCopy;
|
|
5633
|
+
}
|
|
5634
|
+
return updated;
|
|
5635
|
+
});
|
|
5636
|
+
return changes;
|
|
5637
|
+
}
|
|
5638
|
+
/** Ctrl+Enter: write `value` into every editable cell of `range`. */
|
|
5639
|
+
fillSelection(range, value) {
|
|
5640
|
+
const cols = this.state.visibleColumns();
|
|
5641
|
+
const defMap = this.state.columnDefMap();
|
|
5642
|
+
const changes = [];
|
|
5643
|
+
this.state.sourceData.update((data) => {
|
|
5644
|
+
const updated = [...data];
|
|
5645
|
+
for (let r = range.start.row; r <= range.end.row; r++) {
|
|
5646
|
+
const row = updated[r];
|
|
5647
|
+
if (!row)
|
|
5648
|
+
continue;
|
|
5649
|
+
const rowCopy = { ...row };
|
|
5650
|
+
let changed = false;
|
|
5651
|
+
for (let c = range.start.col; c <= range.end.col; c++) {
|
|
5652
|
+
const field = cols[c]?.field;
|
|
5653
|
+
if (!field)
|
|
5654
|
+
continue;
|
|
5655
|
+
const def = defMap.get(field);
|
|
5656
|
+
if (!def?.editable)
|
|
5657
|
+
continue;
|
|
5658
|
+
const coerced = this.coerceAndValidate(field, value, row);
|
|
5659
|
+
if (coerced === PASTE_SKIP$1)
|
|
5660
|
+
continue;
|
|
5661
|
+
const before = row[field];
|
|
5662
|
+
if (before === coerced)
|
|
5663
|
+
continue;
|
|
5664
|
+
rowCopy[field] = coerced;
|
|
5665
|
+
changes.push({ rowIndex: r, field, before, after: coerced });
|
|
5666
|
+
changed = true;
|
|
5667
|
+
}
|
|
5668
|
+
if (changed)
|
|
5669
|
+
updated[r] = rowCopy;
|
|
5670
|
+
}
|
|
5671
|
+
return updated;
|
|
5672
|
+
});
|
|
5673
|
+
return changes;
|
|
5674
|
+
}
|
|
5675
|
+
/** Clears every editable cell in `range` and returns the undo payload. */
|
|
5676
|
+
clearRange(range) {
|
|
5677
|
+
const cols = this.state.visibleColumns();
|
|
5678
|
+
const defMap = this.state.columnDefMap();
|
|
5679
|
+
const changes = [];
|
|
5680
|
+
this.state.sourceData.update((data) => {
|
|
5681
|
+
const updated = [...data];
|
|
5682
|
+
for (let r = range.start.row; r <= range.end.row; r++) {
|
|
5683
|
+
const row = updated[r];
|
|
5684
|
+
if (!row)
|
|
5685
|
+
continue;
|
|
5686
|
+
const rowCopy = { ...row };
|
|
5687
|
+
let changed = false;
|
|
5688
|
+
for (let c = range.start.col; c <= range.end.col; c++) {
|
|
5689
|
+
const field = cols[c]?.field;
|
|
5690
|
+
if (!field)
|
|
5691
|
+
continue;
|
|
5692
|
+
const def = defMap.get(field);
|
|
5693
|
+
if (!def?.editable)
|
|
5694
|
+
continue;
|
|
5695
|
+
const coerced = this.coerceAndValidate(field, null, row);
|
|
5696
|
+
if (coerced === PASTE_SKIP$1)
|
|
5697
|
+
continue;
|
|
5698
|
+
const before = row[field];
|
|
5699
|
+
if (before === coerced)
|
|
5700
|
+
continue;
|
|
5701
|
+
rowCopy[field] = coerced;
|
|
5702
|
+
changes.push({ rowIndex: r, field, before, after: coerced });
|
|
5703
|
+
changed = true;
|
|
5704
|
+
}
|
|
5705
|
+
if (changed)
|
|
5706
|
+
updated[r] = rowCopy;
|
|
5707
|
+
}
|
|
5708
|
+
return updated;
|
|
5709
|
+
});
|
|
5710
|
+
return changes;
|
|
5711
|
+
}
|
|
5712
|
+
/** Applies TSV `pasteRows` starting at `range.start`, returning actual changes. */
|
|
5713
|
+
applyPaste(range, pasteRows) {
|
|
5714
|
+
const cols = this.state.visibleColumns();
|
|
5715
|
+
const changes = [];
|
|
5716
|
+
this.state.sourceData.update((data) => {
|
|
5717
|
+
const updated = [...data];
|
|
5718
|
+
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];
|
|
5723
|
+
if (!row)
|
|
5724
|
+
continue;
|
|
5725
|
+
const rowCopy = { ...row };
|
|
5726
|
+
let changed = false;
|
|
5727
|
+
for (let ci = 0; ci < pasteRows[ri].length; ci++) {
|
|
5728
|
+
const targetCol = range.start.col + ci;
|
|
5729
|
+
if (targetCol >= cols.length)
|
|
5730
|
+
break;
|
|
5731
|
+
const field = cols[targetCol]?.field;
|
|
5732
|
+
if (!field)
|
|
5733
|
+
continue;
|
|
5734
|
+
const coerced = this.coerceAndValidate(field, pasteRows[ri][ci], row);
|
|
5735
|
+
if (coerced === PASTE_SKIP$1)
|
|
5736
|
+
continue;
|
|
5737
|
+
const before = row[field];
|
|
5738
|
+
if (before === coerced)
|
|
5739
|
+
continue;
|
|
5740
|
+
rowCopy[field] = coerced;
|
|
5741
|
+
changes.push({ rowIndex: targetRow, field, before, after: coerced });
|
|
5742
|
+
changed = true;
|
|
5743
|
+
}
|
|
5744
|
+
if (changed)
|
|
5745
|
+
updated[targetRow] = rowCopy;
|
|
5746
|
+
}
|
|
5747
|
+
return updated;
|
|
5748
|
+
});
|
|
5749
|
+
return changes;
|
|
5750
|
+
}
|
|
5751
|
+
/**
|
|
5752
|
+
* Reverses a previously-recorded list of changes by writing `before` back into
|
|
5753
|
+
* the cells. Used by the history engine for both undo and redo.
|
|
5754
|
+
*/
|
|
5755
|
+
applyChanges(changes, direction) {
|
|
5756
|
+
if (changes.length === 0)
|
|
5757
|
+
return;
|
|
5758
|
+
this.state.sourceData.update((data) => {
|
|
5759
|
+
const updated = [...data];
|
|
5760
|
+
// Group changes by rowIndex so each row is cloned once.
|
|
5761
|
+
const byRow = new Map();
|
|
5762
|
+
for (const change of changes) {
|
|
5763
|
+
const list = byRow.get(change.rowIndex) ?? [];
|
|
5764
|
+
list.push(change);
|
|
5765
|
+
byRow.set(change.rowIndex, list);
|
|
5766
|
+
}
|
|
5767
|
+
for (const [rowIndex, rowChanges] of byRow) {
|
|
5768
|
+
const row = updated[rowIndex];
|
|
5769
|
+
if (!row)
|
|
5770
|
+
continue;
|
|
5771
|
+
const rowCopy = { ...row };
|
|
5772
|
+
for (const change of rowChanges) {
|
|
5773
|
+
rowCopy[change.field] = direction === 'before' ? change.before : change.after;
|
|
5774
|
+
}
|
|
5775
|
+
updated[rowIndex] = rowCopy;
|
|
5776
|
+
}
|
|
5777
|
+
return updated;
|
|
5778
|
+
});
|
|
5779
|
+
}
|
|
5780
|
+
/**
|
|
5781
|
+
* Coerces a raw value (string from TSV, unknown from fill, null for clears)
|
|
5782
|
+
* into the editor's expected type, running the field's validator when present.
|
|
5783
|
+
* Returns PASTE_SKIP when the column isn't editable or the value is rejected.
|
|
5784
|
+
*/
|
|
5785
|
+
coerceAndValidate(field, rawValue, row) {
|
|
5786
|
+
const def = this.state.columnDefMap().get(field);
|
|
5787
|
+
if (!def?.editable)
|
|
5788
|
+
return PASTE_SKIP$1;
|
|
5789
|
+
const editorType = def.cellEditor;
|
|
5790
|
+
if (rawValue === null) {
|
|
5791
|
+
let clearValue;
|
|
5792
|
+
switch (editorType) {
|
|
5793
|
+
case 'number':
|
|
5794
|
+
clearValue = null;
|
|
5795
|
+
break;
|
|
5796
|
+
case 'checkbox':
|
|
5797
|
+
clearValue = false;
|
|
5798
|
+
break;
|
|
5799
|
+
default:
|
|
5800
|
+
clearValue = '';
|
|
5801
|
+
}
|
|
5802
|
+
if (def.cellEditorValidator) {
|
|
5803
|
+
const result = def.cellEditorValidator(clearValue, row);
|
|
5804
|
+
if (result === false || typeof result === 'string')
|
|
5805
|
+
return PASTE_SKIP$1;
|
|
5806
|
+
}
|
|
5807
|
+
return clearValue;
|
|
5808
|
+
}
|
|
5809
|
+
let value = rawValue;
|
|
5810
|
+
if (editorType === 'number') {
|
|
5811
|
+
const num = Number(rawValue);
|
|
5812
|
+
if (isNaN(num))
|
|
5813
|
+
return PASTE_SKIP$1;
|
|
5814
|
+
value = num;
|
|
5815
|
+
}
|
|
5816
|
+
else if (editorType === 'checkbox') {
|
|
5817
|
+
if (rawValue === 'true' || rawValue === true) {
|
|
5818
|
+
value = true;
|
|
5819
|
+
}
|
|
5820
|
+
else if (rawValue === 'false' || rawValue === false) {
|
|
5821
|
+
value = false;
|
|
5822
|
+
}
|
|
5823
|
+
else {
|
|
5824
|
+
return PASTE_SKIP$1;
|
|
5825
|
+
}
|
|
5826
|
+
}
|
|
5827
|
+
else if (editorType === 'select' && def.cellEditorOptions?.length) {
|
|
5828
|
+
const allowed = def.cellEditorOptions.map((o) => String(o.value));
|
|
5829
|
+
if (!allowed.includes(String(rawValue)))
|
|
5830
|
+
return PASTE_SKIP$1;
|
|
5831
|
+
value = rawValue;
|
|
5832
|
+
}
|
|
5833
|
+
if (def.cellEditorValidator) {
|
|
5834
|
+
const result = def.cellEditorValidator(value, row);
|
|
5835
|
+
if (result === false || typeof result === 'string')
|
|
5836
|
+
return PASTE_SKIP$1;
|
|
5837
|
+
}
|
|
5838
|
+
return value;
|
|
5839
|
+
}
|
|
5840
|
+
/** TSV string for a range — used by copy / cut. */
|
|
5841
|
+
extractTsv(range) {
|
|
5842
|
+
const cols = this.state.visibleColumns();
|
|
5843
|
+
const data = this.state.sourceData();
|
|
5844
|
+
const defMap = this.state.columnDefMap();
|
|
5845
|
+
const values = [];
|
|
5846
|
+
for (let r = range.start.row; r <= range.end.row; r++) {
|
|
5847
|
+
const row = data[r];
|
|
5848
|
+
if (!row)
|
|
5849
|
+
continue;
|
|
5850
|
+
const rowValues = [];
|
|
5851
|
+
for (let c = range.start.col; c <= range.end.col; c++) {
|
|
5852
|
+
const field = cols[c]?.field;
|
|
5853
|
+
if (!field) {
|
|
5854
|
+
rowValues.push('');
|
|
5855
|
+
continue;
|
|
5856
|
+
}
|
|
5857
|
+
const def = defMap.get(field);
|
|
5858
|
+
const val = def?.valueGetter
|
|
5859
|
+
? def.valueGetter(row)
|
|
5860
|
+
: row[field];
|
|
5861
|
+
rowValues.push(val != null ? String(val) : '');
|
|
5862
|
+
}
|
|
5863
|
+
values.push(rowValues);
|
|
5864
|
+
}
|
|
5865
|
+
return values;
|
|
5866
|
+
}
|
|
5867
|
+
/**
|
|
5868
|
+
* Checks whether a cell sits on any edge of the current cut outline. Four
|
|
5869
|
+
* booleans rather than a single "isCut" so the view can paint only the edges
|
|
5870
|
+
* that face outward — which is what produces the Excel-like marching ants.
|
|
5871
|
+
*/
|
|
5872
|
+
cutEdges(row, col) {
|
|
5873
|
+
const cut = this.state.cutSource();
|
|
5874
|
+
if (!cut)
|
|
5875
|
+
return { top: false, right: false, bottom: false, left: false, any: false };
|
|
5876
|
+
const minRow = Math.min(cut.start.row, cut.end.row);
|
|
5877
|
+
const maxRow = Math.max(cut.start.row, cut.end.row);
|
|
5878
|
+
const minCol = Math.min(cut.start.col, cut.end.col);
|
|
5879
|
+
const maxCol = Math.max(cut.start.col, cut.end.col);
|
|
5880
|
+
if (row < minRow || row > maxRow || col < minCol || col > maxCol) {
|
|
5881
|
+
return { top: false, right: false, bottom: false, left: false, any: false };
|
|
5882
|
+
}
|
|
5883
|
+
return {
|
|
5884
|
+
top: row === minRow,
|
|
5885
|
+
right: col === maxCol,
|
|
5886
|
+
bottom: row === maxRow,
|
|
5887
|
+
left: col === minCol,
|
|
5888
|
+
any: true,
|
|
5889
|
+
};
|
|
5890
|
+
}
|
|
5891
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ClipboardEngine, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
5892
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ClipboardEngine });
|
|
5893
|
+
}
|
|
5894
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: ClipboardEngine, decorators: [{
|
|
5895
|
+
type: Injectable
|
|
5896
|
+
}] });
|
|
5897
|
+
|
|
5898
|
+
const MAX_HISTORY = 50;
|
|
5899
|
+
const STORAGE_PREFIX = 'moz-grid-history:';
|
|
5900
|
+
class HistoryEngine {
|
|
5901
|
+
clipboard = inject(ClipboardEngine);
|
|
5902
|
+
past = signal([], ...(ngDevMode ? [{ debugName: "past" }] : /* istanbul ignore next */ []));
|
|
5903
|
+
future = signal([], ...(ngDevMode ? [{ debugName: "future" }] : /* istanbul ignore next */ []));
|
|
5904
|
+
storageKey = null;
|
|
5905
|
+
canUndo = computed(() => this.past().length > 0, ...(ngDevMode ? [{ debugName: "canUndo" }] : /* istanbul ignore next */ []));
|
|
5906
|
+
canRedo = computed(() => this.future().length > 0, ...(ngDevMode ? [{ debugName: "canRedo" }] : /* istanbul ignore next */ []));
|
|
5907
|
+
/**
|
|
5908
|
+
* Binds a persistence key: all record/undo/redo calls will mirror the stacks
|
|
5909
|
+
* to localStorage, and past state is restored on bind. Pass null to detach.
|
|
5910
|
+
*/
|
|
5911
|
+
attach(gridId) {
|
|
5912
|
+
this.storageKey = gridId ? `${STORAGE_PREFIX}${gridId}` : null;
|
|
5913
|
+
if (!this.storageKey) {
|
|
5914
|
+
this.past.set([]);
|
|
5915
|
+
this.future.set([]);
|
|
5916
|
+
return;
|
|
5917
|
+
}
|
|
5918
|
+
this.restore();
|
|
5919
|
+
}
|
|
5920
|
+
/** Records a new mutation. Clears the redo stack (standard undo semantics). */
|
|
5921
|
+
record(type, changes) {
|
|
5922
|
+
if (changes.length === 0)
|
|
5923
|
+
return;
|
|
5924
|
+
const op = { type, changes, timestamp: Date.now() };
|
|
5925
|
+
this.past.update((stack) => {
|
|
5926
|
+
const next = [...stack, op];
|
|
5927
|
+
return next.length > MAX_HISTORY ? next.slice(next.length - MAX_HISTORY) : next;
|
|
5928
|
+
});
|
|
5929
|
+
this.future.set([]);
|
|
5930
|
+
this.persist();
|
|
5931
|
+
}
|
|
5932
|
+
undo() {
|
|
5933
|
+
const stack = this.past();
|
|
5934
|
+
if (stack.length === 0)
|
|
5935
|
+
return null;
|
|
5936
|
+
const op = stack[stack.length - 1];
|
|
5937
|
+
this.clipboard.applyChanges(op.changes, 'before');
|
|
5938
|
+
this.past.set(stack.slice(0, -1));
|
|
5939
|
+
this.future.update((f) => [...f, op]);
|
|
5940
|
+
this.persist();
|
|
5941
|
+
return op;
|
|
5942
|
+
}
|
|
5943
|
+
redo() {
|
|
5944
|
+
const stack = this.future();
|
|
5945
|
+
if (stack.length === 0)
|
|
5946
|
+
return null;
|
|
5947
|
+
const op = stack[stack.length - 1];
|
|
5948
|
+
this.clipboard.applyChanges(op.changes, 'after');
|
|
5949
|
+
this.future.set(stack.slice(0, -1));
|
|
5950
|
+
this.past.update((p) => [...p, op]);
|
|
5951
|
+
this.persist();
|
|
5952
|
+
return op;
|
|
5953
|
+
}
|
|
5954
|
+
clear() {
|
|
5955
|
+
this.past.set([]);
|
|
5956
|
+
this.future.set([]);
|
|
5957
|
+
if (this.storageKey) {
|
|
5958
|
+
try {
|
|
5959
|
+
localStorage.removeItem(this.storageKey);
|
|
5960
|
+
}
|
|
5961
|
+
catch {
|
|
5962
|
+
// Storage unavailable (private mode, quota) — non-fatal.
|
|
5963
|
+
}
|
|
5964
|
+
}
|
|
5965
|
+
}
|
|
5966
|
+
persist() {
|
|
5967
|
+
if (!this.storageKey)
|
|
5968
|
+
return;
|
|
5969
|
+
try {
|
|
5970
|
+
const payload = JSON.stringify({
|
|
5971
|
+
past: this.past(),
|
|
5972
|
+
future: this.future(),
|
|
5973
|
+
});
|
|
5974
|
+
localStorage.setItem(this.storageKey, payload);
|
|
5975
|
+
}
|
|
5976
|
+
catch {
|
|
5977
|
+
// Quota exceeded or storage disabled — we silently drop persistence.
|
|
5978
|
+
}
|
|
5979
|
+
}
|
|
5980
|
+
restore() {
|
|
5981
|
+
if (!this.storageKey)
|
|
5982
|
+
return;
|
|
5983
|
+
try {
|
|
5984
|
+
const raw = localStorage.getItem(this.storageKey);
|
|
5985
|
+
if (!raw) {
|
|
5986
|
+
this.past.set([]);
|
|
5987
|
+
this.future.set([]);
|
|
5988
|
+
return;
|
|
5989
|
+
}
|
|
5990
|
+
const parsed = JSON.parse(raw);
|
|
5991
|
+
this.past.set(Array.isArray(parsed.past) ? parsed.past : []);
|
|
5992
|
+
this.future.set(Array.isArray(parsed.future) ? parsed.future : []);
|
|
5993
|
+
}
|
|
5994
|
+
catch {
|
|
5995
|
+
this.past.set([]);
|
|
5996
|
+
this.future.set([]);
|
|
5997
|
+
}
|
|
5998
|
+
}
|
|
5999
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: HistoryEngine, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
6000
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: HistoryEngine });
|
|
6001
|
+
}
|
|
6002
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: HistoryEngine, decorators: [{
|
|
6003
|
+
type: Injectable
|
|
6004
|
+
}] });
|
|
6005
|
+
|
|
5527
6006
|
class InlineEditEngine {
|
|
5528
6007
|
state = inject(GridStateManager);
|
|
6008
|
+
history = inject(HistoryEngine);
|
|
5529
6009
|
startEdit(rowIndex, field) {
|
|
5530
6010
|
const defMap = this.state.columnDefMap();
|
|
5531
6011
|
const def = defMap.get(field);
|
|
@@ -5545,6 +6025,54 @@ class InlineEditEngine {
|
|
|
5545
6025
|
validationError: null,
|
|
5546
6026
|
});
|
|
5547
6027
|
}
|
|
6028
|
+
/**
|
|
6029
|
+
* Excel-style "typing-to-edit": starts the editor with the cell value replaced
|
|
6030
|
+
* by the character the user just typed. For non-text editors (select / date /
|
|
6031
|
+
* checkbox / number) we coerce: the character is kept if it's compatible with
|
|
6032
|
+
* the editor, otherwise the editor opens on a cleared value.
|
|
6033
|
+
*/
|
|
6034
|
+
startEditWithChar(rowIndex, field, char) {
|
|
6035
|
+
const defMap = this.state.columnDefMap();
|
|
6036
|
+
const def = defMap.get(field);
|
|
6037
|
+
if (!def?.editable)
|
|
6038
|
+
return;
|
|
6039
|
+
const colIndex = this.state.visibleColumns().findIndex((c) => c.field === field);
|
|
6040
|
+
if (colIndex < 0)
|
|
6041
|
+
return;
|
|
6042
|
+
const row = this.state.sourceData()[rowIndex];
|
|
6043
|
+
if (!row)
|
|
6044
|
+
return;
|
|
6045
|
+
const currentValue = def.valueGetter
|
|
6046
|
+
? def.valueGetter(row)
|
|
6047
|
+
: row[field];
|
|
6048
|
+
const editorType = def.cellEditor ?? this.resolveEditorType(field, currentValue);
|
|
6049
|
+
let draftValue = char;
|
|
6050
|
+
switch (editorType) {
|
|
6051
|
+
case 'number': {
|
|
6052
|
+
const n = Number(char);
|
|
6053
|
+
draftValue = Number.isNaN(n) ? '' : n;
|
|
6054
|
+
break;
|
|
6055
|
+
}
|
|
6056
|
+
case 'checkbox':
|
|
6057
|
+
// A character press toggles the checkbox to true — closest Excel-equivalent
|
|
6058
|
+
// (Excel doesn't have checkbox cells but booleans flip on typing).
|
|
6059
|
+
draftValue = true;
|
|
6060
|
+
break;
|
|
6061
|
+
case 'select':
|
|
6062
|
+
case 'date':
|
|
6063
|
+
// These editors have picker UIs; typing just opens them with an empty draft.
|
|
6064
|
+
draftValue = '';
|
|
6065
|
+
break;
|
|
6066
|
+
default:
|
|
6067
|
+
draftValue = char;
|
|
6068
|
+
}
|
|
6069
|
+
this.state.cellEditState.set({
|
|
6070
|
+
editingCell: { row: rowIndex, col: colIndex },
|
|
6071
|
+
originalValue: currentValue,
|
|
6072
|
+
draftValue,
|
|
6073
|
+
validationError: null,
|
|
6074
|
+
});
|
|
6075
|
+
}
|
|
5548
6076
|
updateDraft(value) {
|
|
5549
6077
|
this.state.cellEditState.update((s) => ({ ...s, draftValue: value }));
|
|
5550
6078
|
}
|
|
@@ -5596,6 +6124,11 @@ class InlineEditEngine {
|
|
|
5596
6124
|
oldValue: editState.originalValue,
|
|
5597
6125
|
newValue: editState.draftValue,
|
|
5598
6126
|
};
|
|
6127
|
+
if (this.state.mode() === 'client' && event.oldValue !== event.newValue) {
|
|
6128
|
+
this.history.record('edit', [
|
|
6129
|
+
{ rowIndex, field, before: event.oldValue, after: event.newValue },
|
|
6130
|
+
]);
|
|
6131
|
+
}
|
|
5599
6132
|
this.state.cellEditState.set({
|
|
5600
6133
|
editingCell: null,
|
|
5601
6134
|
originalValue: undefined,
|
|
@@ -5899,6 +6432,272 @@ class CellSelectionEngine {
|
|
|
5899
6432
|
moveRight() {
|
|
5900
6433
|
this.moveBy(0, 1);
|
|
5901
6434
|
}
|
|
6435
|
+
// --- Home / End / Grid bounds -------------------------------------------------
|
|
6436
|
+
moveToRowStart() {
|
|
6437
|
+
const focused = this.state.focusedCell();
|
|
6438
|
+
if (!focused)
|
|
6439
|
+
return;
|
|
6440
|
+
this.focusCell(focused.row, 0, 'keyboard');
|
|
6441
|
+
}
|
|
6442
|
+
moveToRowEnd() {
|
|
6443
|
+
const focused = this.state.focusedCell();
|
|
6444
|
+
if (!focused)
|
|
6445
|
+
return;
|
|
6446
|
+
const maxCol = this.state.visibleColumns().length - 1;
|
|
6447
|
+
if (maxCol < 0)
|
|
6448
|
+
return;
|
|
6449
|
+
this.focusCell(focused.row, this.findLastNonEmptyCol(focused.row, maxCol), 'keyboard');
|
|
6450
|
+
}
|
|
6451
|
+
moveToGridStart() {
|
|
6452
|
+
const pageStart = this.state.pageIndex() * this.state.pageSize();
|
|
6453
|
+
this.focusCell(pageStart, 0, 'keyboard');
|
|
6454
|
+
}
|
|
6455
|
+
moveToGridEnd() {
|
|
6456
|
+
const pageStart = this.state.pageIndex() * this.state.pageSize();
|
|
6457
|
+
const pageEnd = pageStart + Math.max(0, this.state.visibleRowCount() - 1);
|
|
6458
|
+
const maxCol = this.state.visibleColumns().length - 1;
|
|
6459
|
+
if (maxCol < 0)
|
|
6460
|
+
return;
|
|
6461
|
+
this.focusCell(pageEnd, this.findLastNonEmptyCol(pageEnd, maxCol), 'keyboard');
|
|
6462
|
+
}
|
|
6463
|
+
/**
|
|
6464
|
+
* Excel-style Ctrl+Arrow: jump to the edge of the current data block.
|
|
6465
|
+
* If on an empty cell, jumps to the next non-empty cell. If on a filled
|
|
6466
|
+
* cell, jumps to the last filled cell before the next empty transition
|
|
6467
|
+
* (or to the grid edge if no empty cell is encountered).
|
|
6468
|
+
*/
|
|
6469
|
+
jumpToEdge(direction) {
|
|
6470
|
+
const focused = this.state.focusedCell();
|
|
6471
|
+
if (!focused)
|
|
6472
|
+
return;
|
|
6473
|
+
const { dRow, dCol } = this.directionVector(direction);
|
|
6474
|
+
const bounds = this.pageBounds();
|
|
6475
|
+
const maxCol = this.state.visibleColumns().length - 1;
|
|
6476
|
+
if (maxCol < 0)
|
|
6477
|
+
return;
|
|
6478
|
+
const startFilled = this.isCellFilled(focused.row, focused.col);
|
|
6479
|
+
let row = focused.row;
|
|
6480
|
+
let col = focused.col;
|
|
6481
|
+
// Step once to start looking at the neighbour
|
|
6482
|
+
let nextRow = row + dRow;
|
|
6483
|
+
let nextCol = col + dCol;
|
|
6484
|
+
while (this.inBounds(nextRow, nextCol, bounds, maxCol)) {
|
|
6485
|
+
const filled = this.isCellFilled(nextRow, nextCol);
|
|
6486
|
+
if (startFilled) {
|
|
6487
|
+
// Moving through filled cells — stop right before the next empty gap.
|
|
6488
|
+
if (!filled)
|
|
6489
|
+
break;
|
|
6490
|
+
row = nextRow;
|
|
6491
|
+
col = nextCol;
|
|
6492
|
+
}
|
|
6493
|
+
else {
|
|
6494
|
+
// Moving through empty cells — stop on the first filled cell we meet.
|
|
6495
|
+
if (filled) {
|
|
6496
|
+
row = nextRow;
|
|
6497
|
+
col = nextCol;
|
|
6498
|
+
break;
|
|
6499
|
+
}
|
|
6500
|
+
row = nextRow;
|
|
6501
|
+
col = nextCol;
|
|
6502
|
+
}
|
|
6503
|
+
nextRow += dRow;
|
|
6504
|
+
nextCol += dCol;
|
|
6505
|
+
}
|
|
6506
|
+
this.focusCell(row, col, 'keyboard');
|
|
6507
|
+
}
|
|
6508
|
+
movePage(direction) {
|
|
6509
|
+
const focused = this.state.focusedCell();
|
|
6510
|
+
if (!focused)
|
|
6511
|
+
return;
|
|
6512
|
+
const step = this.pageRowStep() * (direction === 'down' ? 1 : -1);
|
|
6513
|
+
this.moveBy(step, 0);
|
|
6514
|
+
}
|
|
6515
|
+
// --- Shift + navigation : extend current range --------------------------------
|
|
6516
|
+
extendRangeBy(dRow, dCol) {
|
|
6517
|
+
const focused = this.state.focusedCell();
|
|
6518
|
+
if (!focused)
|
|
6519
|
+
return;
|
|
6520
|
+
const range = this.state.cellRange();
|
|
6521
|
+
const bounds = this.pageBounds();
|
|
6522
|
+
const maxCol = this.state.visibleColumns().length - 1;
|
|
6523
|
+
if (maxCol < 0)
|
|
6524
|
+
return;
|
|
6525
|
+
const currentEnd = range ? range.end : focused;
|
|
6526
|
+
const newEnd = {
|
|
6527
|
+
row: Math.max(bounds.start, Math.min(bounds.end, currentEnd.row + dRow)),
|
|
6528
|
+
col: Math.max(0, Math.min(maxCol, currentEnd.col + dCol)),
|
|
6529
|
+
};
|
|
6530
|
+
const start = range ? range.start : focused;
|
|
6531
|
+
this.state.cellRange.set({ start, end: newEnd });
|
|
6532
|
+
}
|
|
6533
|
+
extendRangeToRowStart() {
|
|
6534
|
+
const focused = this.state.focusedCell();
|
|
6535
|
+
if (!focused)
|
|
6536
|
+
return;
|
|
6537
|
+
const range = this.state.cellRange();
|
|
6538
|
+
const start = range?.start ?? focused;
|
|
6539
|
+
this.state.cellRange.set({ start, end: { row: (range?.end ?? focused).row, col: 0 } });
|
|
6540
|
+
}
|
|
6541
|
+
extendRangeToRowEnd() {
|
|
6542
|
+
const focused = this.state.focusedCell();
|
|
6543
|
+
if (!focused)
|
|
6544
|
+
return;
|
|
6545
|
+
const maxCol = this.state.visibleColumns().length - 1;
|
|
6546
|
+
if (maxCol < 0)
|
|
6547
|
+
return;
|
|
6548
|
+
const range = this.state.cellRange();
|
|
6549
|
+
const start = range?.start ?? focused;
|
|
6550
|
+
this.state.cellRange.set({ start, end: { row: (range?.end ?? focused).row, col: maxCol } });
|
|
6551
|
+
}
|
|
6552
|
+
extendRangeToGridStart() {
|
|
6553
|
+
const focused = this.state.focusedCell();
|
|
6554
|
+
if (!focused)
|
|
6555
|
+
return;
|
|
6556
|
+
const bounds = this.pageBounds();
|
|
6557
|
+
const range = this.state.cellRange();
|
|
6558
|
+
const start = range?.start ?? focused;
|
|
6559
|
+
this.state.cellRange.set({ start, end: { row: bounds.start, col: 0 } });
|
|
6560
|
+
}
|
|
6561
|
+
extendRangeToGridEnd() {
|
|
6562
|
+
const focused = this.state.focusedCell();
|
|
6563
|
+
if (!focused)
|
|
6564
|
+
return;
|
|
6565
|
+
const bounds = this.pageBounds();
|
|
6566
|
+
const maxCol = this.state.visibleColumns().length - 1;
|
|
6567
|
+
if (maxCol < 0)
|
|
6568
|
+
return;
|
|
6569
|
+
const range = this.state.cellRange();
|
|
6570
|
+
const start = range?.start ?? focused;
|
|
6571
|
+
this.state.cellRange.set({ start, end: { row: bounds.end, col: maxCol } });
|
|
6572
|
+
}
|
|
6573
|
+
extendRangeJumpToEdge(direction) {
|
|
6574
|
+
const focused = this.state.focusedCell();
|
|
6575
|
+
if (!focused)
|
|
6576
|
+
return;
|
|
6577
|
+
const range = this.state.cellRange();
|
|
6578
|
+
const anchor = range?.start ?? focused;
|
|
6579
|
+
const end = range?.end ?? focused;
|
|
6580
|
+
const target = this.edgeFromCell(end, direction);
|
|
6581
|
+
this.state.cellRange.set({ start: anchor, end: target });
|
|
6582
|
+
}
|
|
6583
|
+
extendRangeByPage(direction) {
|
|
6584
|
+
const step = this.pageRowStep() * (direction === 'down' ? 1 : -1);
|
|
6585
|
+
this.extendRangeBy(step, 0);
|
|
6586
|
+
}
|
|
6587
|
+
// --- Whole row / column / grid selection --------------------------------------
|
|
6588
|
+
selectRow(row) {
|
|
6589
|
+
const maxCol = this.state.visibleColumns().length - 1;
|
|
6590
|
+
if (maxCol < 0)
|
|
6591
|
+
return;
|
|
6592
|
+
this.state.focusedCell.set({ row, col: 0 });
|
|
6593
|
+
this.state.focusSource.set('keyboard');
|
|
6594
|
+
this.state.cellRange.set({
|
|
6595
|
+
start: { row, col: 0 },
|
|
6596
|
+
end: { row, col: maxCol },
|
|
6597
|
+
});
|
|
6598
|
+
}
|
|
6599
|
+
selectColumn(col) {
|
|
6600
|
+
const bounds = this.pageBounds();
|
|
6601
|
+
this.state.focusedCell.set({ row: bounds.start, col });
|
|
6602
|
+
this.state.focusSource.set('keyboard');
|
|
6603
|
+
this.state.cellRange.set({
|
|
6604
|
+
start: { row: bounds.start, col },
|
|
6605
|
+
end: { row: bounds.end, col },
|
|
6606
|
+
});
|
|
6607
|
+
}
|
|
6608
|
+
selectAll() {
|
|
6609
|
+
const bounds = this.pageBounds();
|
|
6610
|
+
const maxCol = this.state.visibleColumns().length - 1;
|
|
6611
|
+
if (maxCol < 0)
|
|
6612
|
+
return;
|
|
6613
|
+
this.state.focusedCell.set({ row: bounds.start, col: 0 });
|
|
6614
|
+
this.state.focusSource.set('keyboard');
|
|
6615
|
+
this.state.cellRange.set({
|
|
6616
|
+
start: { row: bounds.start, col: 0 },
|
|
6617
|
+
end: { row: bounds.end, col: maxCol },
|
|
6618
|
+
});
|
|
6619
|
+
}
|
|
6620
|
+
// --- Private helpers ----------------------------------------------------------
|
|
6621
|
+
pageBounds() {
|
|
6622
|
+
const start = this.state.pageIndex() * this.state.pageSize();
|
|
6623
|
+
const end = start + Math.max(0, this.state.visibleRowCount() - 1);
|
|
6624
|
+
return { start, end };
|
|
6625
|
+
}
|
|
6626
|
+
pageRowStep() {
|
|
6627
|
+
const rowHeight = this.state.rowHeight() || 48;
|
|
6628
|
+
const viewportHeight = this.state.scrollViewportHeight();
|
|
6629
|
+
if (viewportHeight > 0) {
|
|
6630
|
+
return Math.max(1, Math.floor(viewportHeight / rowHeight));
|
|
6631
|
+
}
|
|
6632
|
+
// Fallback: a sensible default when the viewport hasn't been measured yet.
|
|
6633
|
+
return Math.max(1, Math.floor(this.state.visibleRowCount() / 2) || 10);
|
|
6634
|
+
}
|
|
6635
|
+
directionVector(dir) {
|
|
6636
|
+
switch (dir) {
|
|
6637
|
+
case 'up':
|
|
6638
|
+
return { dRow: -1, dCol: 0 };
|
|
6639
|
+
case 'down':
|
|
6640
|
+
return { dRow: 1, dCol: 0 };
|
|
6641
|
+
case 'left':
|
|
6642
|
+
return { dRow: 0, dCol: -1 };
|
|
6643
|
+
case 'right':
|
|
6644
|
+
return { dRow: 0, dCol: 1 };
|
|
6645
|
+
}
|
|
6646
|
+
}
|
|
6647
|
+
inBounds(row, col, bounds, maxCol) {
|
|
6648
|
+
return row >= bounds.start && row <= bounds.end && col >= 0 && col <= maxCol;
|
|
6649
|
+
}
|
|
6650
|
+
isCellFilled(row, col) {
|
|
6651
|
+
const cols = this.state.visibleColumns();
|
|
6652
|
+
const field = cols[col]?.field;
|
|
6653
|
+
if (!field)
|
|
6654
|
+
return false;
|
|
6655
|
+
const data = this.state.sourceData();
|
|
6656
|
+
const rowData = data[row];
|
|
6657
|
+
if (!rowData)
|
|
6658
|
+
return false;
|
|
6659
|
+
const def = this.state.columnDefMap().get(field);
|
|
6660
|
+
const value = def?.valueGetter
|
|
6661
|
+
? def.valueGetter(rowData)
|
|
6662
|
+
: rowData[field];
|
|
6663
|
+
return value !== null && value !== undefined && value !== '';
|
|
6664
|
+
}
|
|
6665
|
+
findLastNonEmptyCol(row, maxCol) {
|
|
6666
|
+
for (let c = maxCol; c >= 0; c--) {
|
|
6667
|
+
if (this.isCellFilled(row, c))
|
|
6668
|
+
return c;
|
|
6669
|
+
}
|
|
6670
|
+
return maxCol;
|
|
6671
|
+
}
|
|
6672
|
+
edgeFromCell(from, direction) {
|
|
6673
|
+
const { dRow, dCol } = this.directionVector(direction);
|
|
6674
|
+
const bounds = this.pageBounds();
|
|
6675
|
+
const maxCol = this.state.visibleColumns().length - 1;
|
|
6676
|
+
if (maxCol < 0)
|
|
6677
|
+
return from;
|
|
6678
|
+
const startFilled = this.isCellFilled(from.row, from.col);
|
|
6679
|
+
let row = from.row;
|
|
6680
|
+
let col = from.col;
|
|
6681
|
+
let nextRow = row + dRow;
|
|
6682
|
+
let nextCol = col + dCol;
|
|
6683
|
+
while (this.inBounds(nextRow, nextCol, bounds, maxCol)) {
|
|
6684
|
+
const filled = this.isCellFilled(nextRow, nextCol);
|
|
6685
|
+
if (startFilled) {
|
|
6686
|
+
if (!filled)
|
|
6687
|
+
break;
|
|
6688
|
+
}
|
|
6689
|
+
else if (filled) {
|
|
6690
|
+
row = nextRow;
|
|
6691
|
+
col = nextCol;
|
|
6692
|
+
break;
|
|
6693
|
+
}
|
|
6694
|
+
row = nextRow;
|
|
6695
|
+
col = nextCol;
|
|
6696
|
+
nextRow += dRow;
|
|
6697
|
+
nextCol += dCol;
|
|
6698
|
+
}
|
|
6699
|
+
return { row, col };
|
|
6700
|
+
}
|
|
5902
6701
|
moveToNextEditableCell() {
|
|
5903
6702
|
const focused = this.state.focusedCell();
|
|
5904
6703
|
if (!focused)
|
|
@@ -6162,113 +6961,203 @@ class KeyboardEngine {
|
|
|
6162
6961
|
cellSelection = inject(CellSelectionEngine);
|
|
6163
6962
|
inlineEdit = inject(InlineEditEngine);
|
|
6164
6963
|
state = inject(GridStateManager);
|
|
6964
|
+
actions = null;
|
|
6965
|
+
registerActions(actions) {
|
|
6966
|
+
this.actions = actions;
|
|
6967
|
+
}
|
|
6165
6968
|
handleKeydown(event) {
|
|
6166
|
-
// Edit
|
|
6167
|
-
|
|
6168
|
-
if (
|
|
6969
|
+
// Edit-mode keys are handled by MozGridComponent.handleEditModeKeydown;
|
|
6970
|
+
// this engine only owns navigation + shortcuts in non-editing state.
|
|
6971
|
+
if (this.state.cellEditState().editingCell !== null)
|
|
6169
6972
|
return;
|
|
6170
|
-
this.handleNavigationKey(event);
|
|
6171
|
-
}
|
|
6172
|
-
handleNavigationKey(event) {
|
|
6173
6973
|
const focused = this.state.focusedCell();
|
|
6174
6974
|
if (!focused)
|
|
6175
6975
|
return;
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
const col = this.state.visibleColumns()[focused.col];
|
|
6225
|
-
if (col) {
|
|
6226
|
-
const def = this.state.columnDefMap().get(col.field);
|
|
6227
|
-
if (def?.editable) {
|
|
6228
|
-
this.inlineEdit.startEdit(focused.row, col.field);
|
|
6229
|
-
}
|
|
6230
|
-
}
|
|
6231
|
-
break;
|
|
6976
|
+
if (this.dispatch(event, focused)) {
|
|
6977
|
+
event.preventDefault();
|
|
6978
|
+
}
|
|
6979
|
+
}
|
|
6980
|
+
/** Returns true when the event was handled (so the caller should preventDefault). */
|
|
6981
|
+
dispatch(event, focused) {
|
|
6982
|
+
const mod = event.ctrlKey || event.metaKey;
|
|
6983
|
+
const shift = event.shiftKey;
|
|
6984
|
+
const alt = event.altKey;
|
|
6985
|
+
const key = event.key;
|
|
6986
|
+
// ---- Clipboard / history shortcuts --------------------------------------
|
|
6987
|
+
if (mod && !shift && !alt) {
|
|
6988
|
+
switch (key.toLowerCase()) {
|
|
6989
|
+
case 'c':
|
|
6990
|
+
this.actions?.copy();
|
|
6991
|
+
return true;
|
|
6992
|
+
case 'v':
|
|
6993
|
+
this.actions?.paste();
|
|
6994
|
+
return true;
|
|
6995
|
+
case 'x':
|
|
6996
|
+
this.actions?.cut();
|
|
6997
|
+
return true;
|
|
6998
|
+
case 'z':
|
|
6999
|
+
this.actions?.undo();
|
|
7000
|
+
return true;
|
|
7001
|
+
case 'y':
|
|
7002
|
+
this.actions?.redo();
|
|
7003
|
+
return true;
|
|
7004
|
+
case 'a':
|
|
7005
|
+
this.cellSelection.selectAll();
|
|
7006
|
+
return true;
|
|
7007
|
+
case 'd':
|
|
7008
|
+
this.actions?.fillDown();
|
|
7009
|
+
return true;
|
|
7010
|
+
case 'r':
|
|
7011
|
+
this.actions?.fillRight();
|
|
7012
|
+
return true;
|
|
7013
|
+
}
|
|
7014
|
+
}
|
|
7015
|
+
if (mod && shift && !alt && key.toLowerCase() === 'z') {
|
|
7016
|
+
this.actions?.redo();
|
|
7017
|
+
return true;
|
|
7018
|
+
}
|
|
7019
|
+
// ---- Whole row / column selection ---------------------------------------
|
|
7020
|
+
if (key === ' ') {
|
|
7021
|
+
if (mod && shift) {
|
|
7022
|
+
this.cellSelection.selectAll();
|
|
7023
|
+
return true;
|
|
6232
7024
|
}
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
7025
|
+
if (mod) {
|
|
7026
|
+
this.cellSelection.selectColumn(focused.col);
|
|
7027
|
+
return true;
|
|
7028
|
+
}
|
|
7029
|
+
if (shift) {
|
|
7030
|
+
this.cellSelection.selectRow(focused.row);
|
|
7031
|
+
return true;
|
|
7032
|
+
}
|
|
7033
|
+
}
|
|
7034
|
+
// ---- Delete / Backspace: clear cells ------------------------------------
|
|
7035
|
+
if (key === 'Delete' || key === 'Backspace') {
|
|
7036
|
+
this.actions?.deleteRange();
|
|
7037
|
+
return true;
|
|
7038
|
+
}
|
|
7039
|
+
// ---- Arrow keys ---------------------------------------------------------
|
|
7040
|
+
if (key === 'ArrowUp' || key === 'ArrowDown' || key === 'ArrowLeft' || key === 'ArrowRight') {
|
|
7041
|
+
return this.handleArrow(key, { mod, shift });
|
|
7042
|
+
}
|
|
7043
|
+
// ---- Home / End / PageUp / PageDown -------------------------------------
|
|
7044
|
+
if (key === 'Home') {
|
|
7045
|
+
if (mod && shift)
|
|
7046
|
+
this.cellSelection.extendRangeToGridStart();
|
|
7047
|
+
else if (mod)
|
|
7048
|
+
this.cellSelection.moveToGridStart();
|
|
7049
|
+
else if (shift)
|
|
7050
|
+
this.cellSelection.extendRangeToRowStart();
|
|
7051
|
+
else
|
|
7052
|
+
this.cellSelection.moveToRowStart();
|
|
7053
|
+
return true;
|
|
7054
|
+
}
|
|
7055
|
+
if (key === 'End') {
|
|
7056
|
+
if (mod && shift)
|
|
7057
|
+
this.cellSelection.extendRangeToGridEnd();
|
|
7058
|
+
else if (mod)
|
|
7059
|
+
this.cellSelection.moveToGridEnd();
|
|
7060
|
+
else if (shift)
|
|
7061
|
+
this.cellSelection.extendRangeToRowEnd();
|
|
7062
|
+
else
|
|
7063
|
+
this.cellSelection.moveToRowEnd();
|
|
7064
|
+
return true;
|
|
7065
|
+
}
|
|
7066
|
+
if (key === 'PageUp') {
|
|
7067
|
+
if (shift)
|
|
7068
|
+
this.cellSelection.extendRangeByPage('up');
|
|
7069
|
+
else
|
|
7070
|
+
this.cellSelection.movePage('up');
|
|
7071
|
+
return true;
|
|
7072
|
+
}
|
|
7073
|
+
if (key === 'PageDown') {
|
|
7074
|
+
if (shift)
|
|
7075
|
+
this.cellSelection.extendRangeByPage('down');
|
|
7076
|
+
else
|
|
7077
|
+
this.cellSelection.movePage('down');
|
|
7078
|
+
return true;
|
|
7079
|
+
}
|
|
7080
|
+
// ---- Tab / Enter / F2 / Escape ------------------------------------------
|
|
7081
|
+
if (key === 'Tab') {
|
|
7082
|
+
if (shift)
|
|
7083
|
+
this.cellSelection.moveLeft();
|
|
7084
|
+
else
|
|
7085
|
+
this.cellSelection.moveRight();
|
|
7086
|
+
return true;
|
|
7087
|
+
}
|
|
7088
|
+
if (key === 'Enter') {
|
|
7089
|
+
const col = this.state.visibleColumns()[focused.col];
|
|
7090
|
+
const def = col ? this.state.columnDefMap().get(col.field) : undefined;
|
|
7091
|
+
if (def?.editable) {
|
|
7092
|
+
this.actions?.startEdit(focused.row, focused.col);
|
|
7093
|
+
return true;
|
|
7094
|
+
}
|
|
7095
|
+
if (shift)
|
|
7096
|
+
this.cellSelection.moveUp();
|
|
7097
|
+
else
|
|
7098
|
+
this.cellSelection.moveDown();
|
|
7099
|
+
return true;
|
|
7100
|
+
}
|
|
7101
|
+
if (key === 'F2') {
|
|
7102
|
+
this.actions?.startEdit(focused.row, focused.col);
|
|
7103
|
+
return true;
|
|
7104
|
+
}
|
|
7105
|
+
if (key === 'Escape') {
|
|
7106
|
+
this.cellSelection.clearFocus();
|
|
7107
|
+
return true;
|
|
7108
|
+
}
|
|
7109
|
+
// ---- Typing-to-edit -----------------------------------------------------
|
|
7110
|
+
if (!mod && !alt && this.isPrintableKey(event)) {
|
|
7111
|
+
this.actions?.startEdit(focused.row, focused.col, key);
|
|
7112
|
+
return true;
|
|
7113
|
+
}
|
|
7114
|
+
return false;
|
|
7115
|
+
}
|
|
7116
|
+
handleArrow(key, mods) {
|
|
7117
|
+
const dir = key === 'ArrowUp' ? 'up' : key === 'ArrowDown' ? 'down' : key === 'ArrowLeft' ? 'left' : 'right';
|
|
7118
|
+
if (mods.mod && mods.shift) {
|
|
7119
|
+
this.cellSelection.extendRangeJumpToEdge(dir);
|
|
7120
|
+
return true;
|
|
7121
|
+
}
|
|
7122
|
+
if (mods.mod) {
|
|
7123
|
+
this.cellSelection.jumpToEdge(dir);
|
|
7124
|
+
return true;
|
|
7125
|
+
}
|
|
7126
|
+
if (mods.shift) {
|
|
7127
|
+
const dRow = dir === 'up' ? -1 : dir === 'down' ? 1 : 0;
|
|
7128
|
+
const dCol = dir === 'left' ? -1 : dir === 'right' ? 1 : 0;
|
|
7129
|
+
this.cellSelection.extendRangeBy(dRow, dCol);
|
|
7130
|
+
return true;
|
|
7131
|
+
}
|
|
7132
|
+
switch (dir) {
|
|
7133
|
+
case 'up':
|
|
7134
|
+
this.cellSelection.moveUp();
|
|
6242
7135
|
break;
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
7136
|
+
case 'down':
|
|
7137
|
+
this.cellSelection.moveDown();
|
|
7138
|
+
break;
|
|
7139
|
+
case 'left':
|
|
7140
|
+
this.cellSelection.moveLeft();
|
|
7141
|
+
break;
|
|
7142
|
+
case 'right':
|
|
7143
|
+
this.cellSelection.moveRight();
|
|
6247
7144
|
break;
|
|
6248
7145
|
}
|
|
7146
|
+
return true;
|
|
6249
7147
|
}
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
if (
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
this.state.cellRange.set({ start: range.start, end: newEnd });
|
|
6264
|
-
}
|
|
6265
|
-
else {
|
|
6266
|
-
const end = {
|
|
6267
|
-
row: Math.max(pageStart, Math.min(pageEnd, focused.row + dRow)),
|
|
6268
|
-
col: Math.max(0, Math.min(maxCol, focused.col + dCol)),
|
|
6269
|
-
};
|
|
6270
|
-
this.state.cellRange.set({ start: focused, end });
|
|
6271
|
-
}
|
|
7148
|
+
/**
|
|
7149
|
+
* A key press is "printable" when it represents exactly one character that
|
|
7150
|
+
* the user intends as input — excluding named keys like F1, Home, Arrow*,
|
|
7151
|
+
* etc. Also excludes IME composition events (event.isComposing).
|
|
7152
|
+
*/
|
|
7153
|
+
isPrintableKey(event) {
|
|
7154
|
+
if (event.isComposing)
|
|
7155
|
+
return false;
|
|
7156
|
+
if (event.key.length !== 1)
|
|
7157
|
+
return false;
|
|
7158
|
+
// Guard against NUL / control characters slipping through on some layouts.
|
|
7159
|
+
const code = event.key.charCodeAt(0);
|
|
7160
|
+
return code >= 32 && code !== 127;
|
|
6272
7161
|
}
|
|
6273
7162
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: KeyboardEngine, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
6274
7163
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: KeyboardEngine });
|
|
@@ -7363,11 +8252,11 @@ class MozGridHeaderComponent {
|
|
|
7363
8252
|
this.dragEngine.startDrag(event, colIndex, center.nativeElement);
|
|
7364
8253
|
}
|
|
7365
8254
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MozGridHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7366
|
-
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
|
|
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\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 });
|
|
7367
8256
|
}
|
|
7368
8257
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MozGridHeaderComponent, decorators: [{
|
|
7369
8258
|
type: Component,
|
|
7370
|
-
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
|
|
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\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"] }]
|
|
7371
8260
|
}], 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"] }] } });
|
|
7372
8261
|
|
|
7373
8262
|
/**
|
|
@@ -7779,6 +8668,7 @@ class MozGridCellComponent {
|
|
|
7779
8668
|
inlineEdit = inject(InlineEditEngine);
|
|
7780
8669
|
cellSelectionEngine = inject(CellSelectionEngine);
|
|
7781
8670
|
validationEngine = inject(CellValidationEngine);
|
|
8671
|
+
clipboard = inject(ClipboardEngine);
|
|
7782
8672
|
elRef = inject((ElementRef));
|
|
7783
8673
|
preEditWidth = null;
|
|
7784
8674
|
constructor() {
|
|
@@ -7893,6 +8783,7 @@ class MozGridCellComponent {
|
|
|
7893
8783
|
isInFillRejectRange = computed(() => {
|
|
7894
8784
|
return this.cellSelectionEngine.isCellInFillRejectRange(this.rowIndex(), this.colIndex());
|
|
7895
8785
|
}, ...(ngDevMode ? [{ debugName: "isInFillRejectRange" }] : /* istanbul ignore next */ []));
|
|
8786
|
+
cutEdges = computed(() => this.clipboard.cutEdges(this.rowIndex(), this.colIndex()), ...(ngDevMode ? [{ debugName: "cutEdges" }] : /* istanbul ignore next */ []));
|
|
7896
8787
|
cellError = computed(() => this.validationEngine.getCellError(this.rowIndex(), this.def().field), ...(ngDevMode ? [{ debugName: "cellError" }] : /* istanbul ignore next */ []));
|
|
7897
8788
|
onCellClick(event) {
|
|
7898
8789
|
// Shift+click: extend range from current focused cell to this cell
|
|
@@ -7997,7 +8888,7 @@ class MozGridCellComponent {
|
|
|
7997
8888
|
});
|
|
7998
8889
|
}
|
|
7999
8890
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MozGridCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8000
|
-
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--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 (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: [":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__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 });
|
|
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()" } }, 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: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 });
|
|
8001
8892
|
}
|
|
8002
8893
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MozGridCellComponent, decorators: [{
|
|
8003
8894
|
type: Component,
|
|
@@ -8013,8 +8904,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
8013
8904
|
'[style.flex]': 'isLast() ? "1 0 auto" : "0 0 auto"',
|
|
8014
8905
|
'[style.width.px]': 'isLast() ? undefined : colState().currentWidth',
|
|
8015
8906
|
'[style.min-width.px]': 'isLast() ? colState().currentWidth : resolvedMinWidth()',
|
|
8016
|
-
|
|
8017
|
-
}, 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--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 (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: [":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__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"] }]
|
|
8907
|
+
}, 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: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"] }]
|
|
8018
8908
|
}], 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"] }] } });
|
|
8019
8909
|
|
|
8020
8910
|
class MozGridRowComponent {
|
|
@@ -8510,6 +9400,85 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
8510
9400
|
], 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"] }]
|
|
8511
9401
|
}] });
|
|
8512
9402
|
|
|
9403
|
+
const EXCEL_SHORTCUTS = [
|
|
9404
|
+
{
|
|
9405
|
+
title: 'Navigation',
|
|
9406
|
+
items: [
|
|
9407
|
+
{ keys: '← ↑ → ↓', label: 'Déplacer la cellule active' },
|
|
9408
|
+
{ keys: 'Ctrl + Flèche', label: 'Sauter au bord du bloc de données' },
|
|
9409
|
+
{ keys: 'Home / End', label: 'Début / fin de la ligne' },
|
|
9410
|
+
{ keys: 'Ctrl + Home / End', label: 'Première / dernière cellule' },
|
|
9411
|
+
{ keys: 'PageUp / PageDown', label: 'Page précédente / suivante' },
|
|
9412
|
+
{ keys: 'Tab / Shift+Tab', label: 'Cellule suivante / précédente' },
|
|
9413
|
+
{ keys: 'Enter / Shift+Enter', label: 'Descendre / remonter' },
|
|
9414
|
+
],
|
|
9415
|
+
},
|
|
9416
|
+
{
|
|
9417
|
+
title: 'Sélection',
|
|
9418
|
+
items: [
|
|
9419
|
+
{ keys: 'Shift + Flèche', label: 'Étendre la plage' },
|
|
9420
|
+
{ keys: 'Shift + Ctrl + Flèche', label: "Étendre jusqu'au bord du bloc" },
|
|
9421
|
+
{ keys: 'Shift + Home / End', label: 'Étendre au début / fin de la ligne' },
|
|
9422
|
+
{ keys: 'Shift + Ctrl + Home / End', label: 'Étendre au début / fin du tableau' },
|
|
9423
|
+
{ keys: 'Shift + PageUp / Down', label: "Étendre d'une page" },
|
|
9424
|
+
{ keys: 'Ctrl + A', label: 'Sélectionner tout' },
|
|
9425
|
+
{ keys: 'Shift + Espace', label: 'Sélectionner la ligne' },
|
|
9426
|
+
{ keys: 'Ctrl + Espace', label: 'Sélectionner la colonne' },
|
|
9427
|
+
],
|
|
9428
|
+
},
|
|
9429
|
+
{
|
|
9430
|
+
title: 'Édition',
|
|
9431
|
+
items: [
|
|
9432
|
+
{ keys: 'Enter / F2', label: 'Entrer en édition' },
|
|
9433
|
+
{ keys: 'Touche imprimable', label: 'Typing-to-edit (remplace la valeur)' },
|
|
9434
|
+
{ keys: 'Escape', label: "Annuler l'édition" },
|
|
9435
|
+
{ keys: 'Enter', label: 'Valider + descendre' },
|
|
9436
|
+
{ keys: 'Tab / Shift+Tab', label: 'Valider + droite / gauche' },
|
|
9437
|
+
{ keys: 'Alt + Enter', label: 'Retour à la ligne (texte)' },
|
|
9438
|
+
{ keys: 'Ctrl + Enter', label: 'Valider + remplir la sélection' },
|
|
9439
|
+
{ keys: 'Backspace / Delete', label: 'Effacer les cellules sélectionnées' },
|
|
9440
|
+
],
|
|
9441
|
+
},
|
|
9442
|
+
{
|
|
9443
|
+
title: 'Presse-papier',
|
|
9444
|
+
items: [
|
|
9445
|
+
{ keys: 'Ctrl + C', label: 'Copier (TSV)' },
|
|
9446
|
+
{ keys: 'Ctrl + X', label: 'Couper (marching ants)' },
|
|
9447
|
+
{ keys: 'Ctrl + V', label: 'Coller (déplace après Ctrl+X)' },
|
|
9448
|
+
{ keys: 'Ctrl + D', label: 'Remplir vers le bas (fill down)' },
|
|
9449
|
+
{ keys: 'Ctrl + R', label: 'Remplir vers la droite (fill right)' },
|
|
9450
|
+
],
|
|
9451
|
+
},
|
|
9452
|
+
{
|
|
9453
|
+
title: 'Historique',
|
|
9454
|
+
items: [
|
|
9455
|
+
{ keys: 'Ctrl + Z', label: 'Annuler (undo)' },
|
|
9456
|
+
{ keys: 'Ctrl + Y', label: 'Rétablir (redo)' },
|
|
9457
|
+
{ keys: 'Ctrl + Shift + Z', label: 'Rétablir (redo, alt.)' },
|
|
9458
|
+
],
|
|
9459
|
+
},
|
|
9460
|
+
];
|
|
9461
|
+
class GridKeyboardShortcutsDrawerComponent {
|
|
9462
|
+
drawerRef = inject(MozDrawerRef);
|
|
9463
|
+
groups = EXCEL_SHORTCUTS.map((group) => ({
|
|
9464
|
+
title: group.title,
|
|
9465
|
+
items: group.items.map((item) => ({
|
|
9466
|
+
keys: item.keys,
|
|
9467
|
+
label: item.label,
|
|
9468
|
+
parts: item.keys.split(' '),
|
|
9469
|
+
})),
|
|
9470
|
+
}));
|
|
9471
|
+
close() {
|
|
9472
|
+
this.drawerRef.close();
|
|
9473
|
+
}
|
|
9474
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GridKeyboardShortcutsDrawerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9475
|
+
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 });
|
|
9476
|
+
}
|
|
9477
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: GridKeyboardShortcutsDrawerComponent, decorators: [{
|
|
9478
|
+
type: Component,
|
|
9479
|
+
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"] }]
|
|
9480
|
+
}] });
|
|
9481
|
+
|
|
8513
9482
|
class MozGridComponent {
|
|
8514
9483
|
state = inject(GridStateManager);
|
|
8515
9484
|
gridEngine = inject(GridEngine);
|
|
@@ -8519,6 +9488,8 @@ class MozGridComponent {
|
|
|
8519
9488
|
rowSelectionEngine = inject(RowSelectionEngine);
|
|
8520
9489
|
cellSelectionEngine = inject(CellSelectionEngine);
|
|
8521
9490
|
keyboardEngine = inject(KeyboardEngine);
|
|
9491
|
+
clipboardEngine = inject(ClipboardEngine);
|
|
9492
|
+
historyEngine = inject(HistoryEngine);
|
|
8522
9493
|
groupEngine = inject(GroupEngine);
|
|
8523
9494
|
filterEngine = inject(FilterEngine);
|
|
8524
9495
|
persistenceEngine = inject(StatePersistenceEngine);
|
|
@@ -8596,6 +9567,24 @@ class MozGridComponent {
|
|
|
8596
9567
|
stateRestored = false;
|
|
8597
9568
|
documentMouseUpHandler = null;
|
|
8598
9569
|
constructor() {
|
|
9570
|
+
this.keyboardEngine.registerActions({
|
|
9571
|
+
copy: () => this.onBulkCopy(),
|
|
9572
|
+
paste: () => {
|
|
9573
|
+
void this.onBulkPaste();
|
|
9574
|
+
},
|
|
9575
|
+
cut: () => this.onCutShortcut(),
|
|
9576
|
+
deleteRange: () => this.onBulkDelete(),
|
|
9577
|
+
undo: () => this.onUndo(),
|
|
9578
|
+
redo: () => this.onRedo(),
|
|
9579
|
+
fillDown: () => this.onFillDownShortcut(),
|
|
9580
|
+
fillRight: () => this.onFillRightShortcut(),
|
|
9581
|
+
startEdit: (row, col, char) => this.onStartEditShortcut(row, col, char),
|
|
9582
|
+
});
|
|
9583
|
+
// Bind history persistence to the grid's stateKey (same key we use for
|
|
9584
|
+
// column/sort persistence — one localStorage namespace per grid).
|
|
9585
|
+
effect(() => {
|
|
9586
|
+
this.historyEngine.attach(this.stateKey());
|
|
9587
|
+
});
|
|
8599
9588
|
// Global mouseup listener for fill handle — if the user drags the fill
|
|
8600
9589
|
// handle outside the grid and releases, we still need to end the fill.
|
|
8601
9590
|
afterNextRender(() => {
|
|
@@ -8631,6 +9620,7 @@ class MozGridComponent {
|
|
|
8631
9620
|
const pushScroll = () => {
|
|
8632
9621
|
this.ngZone.run(() => {
|
|
8633
9622
|
this.horizontalVirtualScrollEngine.onScroll(scrollEl.scrollLeft, scrollEl.clientWidth);
|
|
9623
|
+
this.state.scrollViewportHeight.set(scrollEl.clientHeight);
|
|
8634
9624
|
});
|
|
8635
9625
|
};
|
|
8636
9626
|
// Prime the engine with the initial viewport width so the range is
|
|
@@ -8907,64 +9897,154 @@ class MozGridComponent {
|
|
|
8907
9897
|
if (tag === 'input' || tag === 'select' || tag === 'textarea') {
|
|
8908
9898
|
return;
|
|
8909
9899
|
}
|
|
8910
|
-
//
|
|
9900
|
+
// Row-selection mode forwards a small set of shortcuts without touching
|
|
9901
|
+
// cell focus. Cell-mode (and anywhere else) goes through the keyboard engine.
|
|
8911
9902
|
const selMode = this.state.activeSelectionMode();
|
|
8912
|
-
if (selMode === 'rows'
|
|
8913
|
-
if ((event.ctrlKey || event.metaKey) && event.key === 'c') {
|
|
9903
|
+
if (selMode === 'rows') {
|
|
9904
|
+
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'c') {
|
|
8914
9905
|
event.preventDefault();
|
|
8915
9906
|
this.onBulkCopy();
|
|
8916
9907
|
return;
|
|
8917
9908
|
}
|
|
8918
|
-
if ((event.ctrlKey || event.metaKey) && event.key === 'v') {
|
|
9909
|
+
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'v') {
|
|
8919
9910
|
event.preventDefault();
|
|
8920
|
-
this.onBulkPaste();
|
|
9911
|
+
void this.onBulkPaste();
|
|
8921
9912
|
return;
|
|
8922
9913
|
}
|
|
8923
9914
|
if (event.key === 'Delete' || event.key === 'Backspace') {
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
return;
|
|
8928
|
-
}
|
|
9915
|
+
event.preventDefault();
|
|
9916
|
+
this.onBulkDelete();
|
|
9917
|
+
return;
|
|
8929
9918
|
}
|
|
8930
9919
|
}
|
|
8931
9920
|
this.keyboardEngine.handleKeydown(event);
|
|
8932
9921
|
}
|
|
8933
9922
|
handleEditModeKeydown(event) {
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
this.cellEditCancel.emit(cancelEvt);
|
|
8940
|
-
}
|
|
8941
|
-
break;
|
|
9923
|
+
if (event.key === 'Escape') {
|
|
9924
|
+
event.preventDefault();
|
|
9925
|
+
const cancelEvt = this.inlineEditEngine.cancelEdit();
|
|
9926
|
+
if (cancelEvt) {
|
|
9927
|
+
this.cellEditCancel.emit(cancelEvt);
|
|
8942
9928
|
}
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
9929
|
+
return;
|
|
9930
|
+
}
|
|
9931
|
+
if (event.key === 'Enter') {
|
|
9932
|
+
// Alt+Enter: insert a newline in the draft for text editors (Excel-style).
|
|
9933
|
+
if (event.altKey) {
|
|
9934
|
+
const editState = this.state.cellEditState();
|
|
9935
|
+
const def = this.state
|
|
9936
|
+
.columnDefMap()
|
|
9937
|
+
.get(this.state.visibleColumns()[editState.editingCell?.col ?? -1]?.field ?? '');
|
|
9938
|
+
const editorType = def?.cellEditor ?? 'text';
|
|
9939
|
+
if (editorType === 'text') {
|
|
9940
|
+
event.preventDefault();
|
|
9941
|
+
const draft = editState.draftValue;
|
|
9942
|
+
const next = (typeof draft === 'string' ? draft : String(draft ?? '')) + '\n';
|
|
9943
|
+
this.inlineEditEngine.updateDraft(next);
|
|
8948
9944
|
}
|
|
8949
|
-
|
|
8950
|
-
this.refocusGrid();
|
|
8951
|
-
break;
|
|
9945
|
+
return;
|
|
8952
9946
|
}
|
|
8953
|
-
|
|
9947
|
+
// Ctrl+Enter: commit current draft and broadcast it to the active range.
|
|
9948
|
+
if (event.ctrlKey || event.metaKey) {
|
|
8954
9949
|
event.preventDefault();
|
|
8955
|
-
const
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
this.
|
|
9950
|
+
const editState = this.state.cellEditState();
|
|
9951
|
+
const value = editState.draftValue;
|
|
9952
|
+
const cancel = this.inlineEditEngine.cancelEdit();
|
|
9953
|
+
if (cancel)
|
|
9954
|
+
this.cellEditCancel.emit(cancel);
|
|
9955
|
+
const range = this.cellSelectionEngine.getNormalizedRange();
|
|
9956
|
+
if (range) {
|
|
9957
|
+
const changes = this.clipboardEngine.fillSelection(range, value);
|
|
9958
|
+
this.historyEngine.record('fill-selection', changes);
|
|
8964
9959
|
}
|
|
8965
9960
|
this.refocusGrid();
|
|
8966
|
-
|
|
9961
|
+
return;
|
|
8967
9962
|
}
|
|
9963
|
+
event.preventDefault();
|
|
9964
|
+
this.commitFromEditMode();
|
|
9965
|
+
if (event.shiftKey)
|
|
9966
|
+
this.cellSelectionEngine.moveUp();
|
|
9967
|
+
else
|
|
9968
|
+
this.cellSelectionEngine.moveDown();
|
|
9969
|
+
this.refocusGrid();
|
|
9970
|
+
return;
|
|
9971
|
+
}
|
|
9972
|
+
if (event.key === 'Tab') {
|
|
9973
|
+
event.preventDefault();
|
|
9974
|
+
this.commitFromEditMode();
|
|
9975
|
+
if (event.shiftKey)
|
|
9976
|
+
this.cellSelectionEngine.moveLeft();
|
|
9977
|
+
else
|
|
9978
|
+
this.cellSelectionEngine.moveRight();
|
|
9979
|
+
this.refocusGrid();
|
|
9980
|
+
}
|
|
9981
|
+
}
|
|
9982
|
+
commitFromEditMode() {
|
|
9983
|
+
const evt = this.inlineEditEngine.commitEdit();
|
|
9984
|
+
if (!evt)
|
|
9985
|
+
return;
|
|
9986
|
+
this.cellEdit.emit(evt);
|
|
9987
|
+
this.clipboardEngine.clearCut();
|
|
9988
|
+
}
|
|
9989
|
+
onCutShortcut() {
|
|
9990
|
+
const range = this.cellSelectionEngine.getNormalizedRange();
|
|
9991
|
+
if (!range)
|
|
9992
|
+
return;
|
|
9993
|
+
const values = this.clipboardEngine.extractTsv(range);
|
|
9994
|
+
const tsv = values.map((row) => row.join('\t')).join('\n');
|
|
9995
|
+
navigator.clipboard.writeText(tsv);
|
|
9996
|
+
this.clipboardEngine.markCut(range);
|
|
9997
|
+
this.bulkCopy.emit({
|
|
9998
|
+
range,
|
|
9999
|
+
data: values,
|
|
10000
|
+
rowIds: this.getRangeRowIds(range),
|
|
10001
|
+
fields: this.getRangeFields(range),
|
|
10002
|
+
});
|
|
10003
|
+
}
|
|
10004
|
+
onUndo() {
|
|
10005
|
+
this.historyEngine.undo();
|
|
10006
|
+
this.clipboardEngine.clearCut();
|
|
10007
|
+
}
|
|
10008
|
+
onRedo() {
|
|
10009
|
+
this.historyEngine.redo();
|
|
10010
|
+
this.clipboardEngine.clearCut();
|
|
10011
|
+
}
|
|
10012
|
+
onFillDownShortcut() {
|
|
10013
|
+
const range = this.cellSelectionEngine.getNormalizedRange();
|
|
10014
|
+
if (!range)
|
|
10015
|
+
return;
|
|
10016
|
+
if (this.state.mode() !== 'client')
|
|
10017
|
+
return;
|
|
10018
|
+
const changes = this.clipboardEngine.fillDown(range);
|
|
10019
|
+
if (changes.length === 0)
|
|
10020
|
+
return;
|
|
10021
|
+
this.historyEngine.record('fill-down', changes);
|
|
10022
|
+
this.clipboardEngine.clearCut();
|
|
10023
|
+
}
|
|
10024
|
+
onFillRightShortcut() {
|
|
10025
|
+
const range = this.cellSelectionEngine.getNormalizedRange();
|
|
10026
|
+
if (!range)
|
|
10027
|
+
return;
|
|
10028
|
+
if (this.state.mode() !== 'client')
|
|
10029
|
+
return;
|
|
10030
|
+
const changes = this.clipboardEngine.fillRight(range);
|
|
10031
|
+
if (changes.length === 0)
|
|
10032
|
+
return;
|
|
10033
|
+
this.historyEngine.record('fill-right', changes);
|
|
10034
|
+
this.clipboardEngine.clearCut();
|
|
10035
|
+
}
|
|
10036
|
+
onStartEditShortcut(row, col, initialChar) {
|
|
10037
|
+
const colDef = this.state.visibleColumns()[col];
|
|
10038
|
+
if (!colDef)
|
|
10039
|
+
return;
|
|
10040
|
+
const def = this.state.columnDefMap().get(colDef.field);
|
|
10041
|
+
if (!def?.editable)
|
|
10042
|
+
return;
|
|
10043
|
+
if (initialChar !== undefined) {
|
|
10044
|
+
this.inlineEditEngine.startEditWithChar(row, colDef.field, initialChar);
|
|
10045
|
+
}
|
|
10046
|
+
else {
|
|
10047
|
+
this.inlineEditEngine.startEdit(row, colDef.field);
|
|
8968
10048
|
}
|
|
8969
10049
|
}
|
|
8970
10050
|
resetInfiniteScrollIfActive() {
|
|
@@ -9051,17 +10131,26 @@ class MozGridComponent {
|
|
|
9051
10131
|
if (srcIdx >= 0)
|
|
9052
10132
|
indexMap.set(r, srcIdx);
|
|
9053
10133
|
}
|
|
10134
|
+
const changes = [];
|
|
9054
10135
|
this.state.sourceData.update((d) => {
|
|
9055
10136
|
const updated = [...d];
|
|
9056
10137
|
for (const [, srcIdx] of indexMap) {
|
|
9057
10138
|
if (!updated[srcIdx])
|
|
9058
10139
|
continue;
|
|
10140
|
+
const before = updated[srcIdx][field];
|
|
10141
|
+
if (before === sourceValue)
|
|
10142
|
+
continue;
|
|
9059
10143
|
const rowCopy = { ...updated[srcIdx] };
|
|
9060
10144
|
rowCopy[field] = sourceValue;
|
|
9061
10145
|
updated[srcIdx] = rowCopy;
|
|
10146
|
+
changes.push({ rowIndex: srcIdx, field, before, after: sourceValue });
|
|
9062
10147
|
}
|
|
9063
10148
|
return updated;
|
|
9064
10149
|
});
|
|
10150
|
+
if (changes.length > 0) {
|
|
10151
|
+
this.historyEngine.record('fill', changes);
|
|
10152
|
+
}
|
|
10153
|
+
this.clipboardEngine.clearCut();
|
|
9065
10154
|
}
|
|
9066
10155
|
this.fillDown.emit({
|
|
9067
10156
|
sourceCell: anchor,
|
|
@@ -9102,6 +10191,7 @@ class MozGridComponent {
|
|
|
9102
10191
|
if (targetFields.length === 0)
|
|
9103
10192
|
return;
|
|
9104
10193
|
if (this.state.mode() === 'client') {
|
|
10194
|
+
const changes = [];
|
|
9105
10195
|
this.state.sourceData.update((d) => {
|
|
9106
10196
|
const updated = [...d];
|
|
9107
10197
|
const src = updated[anchorSourceIdx];
|
|
@@ -9109,11 +10199,19 @@ class MozGridComponent {
|
|
|
9109
10199
|
return updated;
|
|
9110
10200
|
const rowCopy = { ...src };
|
|
9111
10201
|
for (const f of targetFields) {
|
|
10202
|
+
const before = src[f];
|
|
10203
|
+
if (before === sourceValue)
|
|
10204
|
+
continue;
|
|
9112
10205
|
rowCopy[f] = sourceValue;
|
|
10206
|
+
changes.push({ rowIndex: anchorSourceIdx, field: f, before, after: sourceValue });
|
|
9113
10207
|
}
|
|
9114
10208
|
updated[anchorSourceIdx] = rowCopy;
|
|
9115
10209
|
return updated;
|
|
9116
10210
|
});
|
|
10211
|
+
if (changes.length > 0) {
|
|
10212
|
+
this.historyEngine.record('fill', changes);
|
|
10213
|
+
}
|
|
10214
|
+
this.clipboardEngine.clearCut();
|
|
9117
10215
|
}
|
|
9118
10216
|
this.fillDown.emit({
|
|
9119
10217
|
sourceCell: anchor,
|
|
@@ -9216,6 +10314,10 @@ class MozGridComponent {
|
|
|
9216
10314
|
groups: [...result.groups],
|
|
9217
10315
|
});
|
|
9218
10316
|
}
|
|
10317
|
+
// --- Keyboard shortcuts drawer ---
|
|
10318
|
+
onKeyboardShortcutsClick() {
|
|
10319
|
+
this.drawerService.open(GridKeyboardShortcutsDrawerComponent, { title: 'Keyboard shortcuts' });
|
|
10320
|
+
}
|
|
9219
10321
|
// --- Settings drawer ---
|
|
9220
10322
|
static DENSITY_ROW_HEIGHT = {
|
|
9221
10323
|
small: 32,
|
|
@@ -9320,6 +10422,8 @@ class MozGridComponent {
|
|
|
9320
10422
|
});
|
|
9321
10423
|
}
|
|
9322
10424
|
onBulkCopy() {
|
|
10425
|
+
// A fresh copy always cancels any pending cut (Excel parity).
|
|
10426
|
+
this.clipboardEngine.clearCut();
|
|
9323
10427
|
if (this.state.activeSelectionMode() === 'rows') {
|
|
9324
10428
|
const event = this.rowSelectionEngine.getSelectionEvent();
|
|
9325
10429
|
const rowData = this.extractRowSelectionData(event.selectedRows);
|
|
@@ -9387,7 +10491,16 @@ class MozGridComponent {
|
|
|
9387
10491
|
},
|
|
9388
10492
|
};
|
|
9389
10493
|
if (this.state.mode() === 'client') {
|
|
9390
|
-
|
|
10494
|
+
// If a cut is pending, first wipe the source cells so cut+paste == move,
|
|
10495
|
+
// and fold both halves into a single undoable history op.
|
|
10496
|
+
const cutSource = this.state.cutSource();
|
|
10497
|
+
const clearChanges = cutSource ? this.clipboardEngine.clearRange(cutSource) : [];
|
|
10498
|
+
const pasteChanges = this.clipboardEngine.applyPaste(pasteRange, rows);
|
|
10499
|
+
const allChanges = [...clearChanges, ...pasteChanges];
|
|
10500
|
+
if (allChanges.length > 0) {
|
|
10501
|
+
this.historyEngine.record(cutSource ? 'cut' : 'paste', allChanges);
|
|
10502
|
+
}
|
|
10503
|
+
this.clipboardEngine.clearCut();
|
|
9391
10504
|
}
|
|
9392
10505
|
this.bulkPaste.emit({
|
|
9393
10506
|
range: pasteRange,
|
|
@@ -9449,33 +10562,14 @@ class MozGridComponent {
|
|
|
9449
10562
|
const pageEnd = pageStart + this.gridEngine.paginatedData().length - 1;
|
|
9450
10563
|
if (range.start.row < pageStart || range.end.row > pageEnd)
|
|
9451
10564
|
return;
|
|
9452
|
-
const cols = this.state.visibleColumns();
|
|
9453
10565
|
const rows = range.end.row - range.start.row + 1;
|
|
9454
10566
|
const colCount = range.end.col - range.start.col + 1;
|
|
9455
10567
|
if (this.state.mode() === 'client') {
|
|
9456
|
-
this.
|
|
9457
|
-
|
|
9458
|
-
|
|
9459
|
-
|
|
9460
|
-
|
|
9461
|
-
const rowCopy = { ...updated[r] };
|
|
9462
|
-
let changed = false;
|
|
9463
|
-
for (let c = range.start.col; c <= range.end.col; c++) {
|
|
9464
|
-
const field = cols[c]?.field;
|
|
9465
|
-
if (!field)
|
|
9466
|
-
continue;
|
|
9467
|
-
const coerced = this.coerceAndValidate(field, null, updated[r]);
|
|
9468
|
-
if (coerced !== PASTE_SKIP) {
|
|
9469
|
-
rowCopy[field] = coerced;
|
|
9470
|
-
changed = true;
|
|
9471
|
-
}
|
|
9472
|
-
}
|
|
9473
|
-
if (changed) {
|
|
9474
|
-
updated[r] = rowCopy;
|
|
9475
|
-
}
|
|
9476
|
-
}
|
|
9477
|
-
return updated;
|
|
9478
|
-
});
|
|
10568
|
+
const changes = this.clipboardEngine.clearRange(range);
|
|
10569
|
+
if (changes.length > 0) {
|
|
10570
|
+
this.historyEngine.record('delete', changes);
|
|
10571
|
+
}
|
|
10572
|
+
this.clipboardEngine.clearCut();
|
|
9479
10573
|
}
|
|
9480
10574
|
this.bulkDelete.emit({
|
|
9481
10575
|
range,
|
|
@@ -9513,36 +10607,6 @@ class MozGridComponent {
|
|
|
9513
10607
|
}
|
|
9514
10608
|
return { range, values };
|
|
9515
10609
|
}
|
|
9516
|
-
applyPasteData(range, pasteRows) {
|
|
9517
|
-
const cols = this.state.visibleColumns();
|
|
9518
|
-
this.state.sourceData.update((data) => {
|
|
9519
|
-
const updated = [...data];
|
|
9520
|
-
for (let ri = 0; ri < pasteRows.length; ri++) {
|
|
9521
|
-
const targetRow = range.start.row + ri;
|
|
9522
|
-
if (targetRow >= updated.length)
|
|
9523
|
-
break;
|
|
9524
|
-
const rowCopy = { ...updated[targetRow] };
|
|
9525
|
-
let changed = false;
|
|
9526
|
-
for (let ci = 0; ci < pasteRows[ri].length; ci++) {
|
|
9527
|
-
const targetCol = range.start.col + ci;
|
|
9528
|
-
if (targetCol >= cols.length)
|
|
9529
|
-
break;
|
|
9530
|
-
const field = cols[targetCol]?.field;
|
|
9531
|
-
if (!field)
|
|
9532
|
-
continue;
|
|
9533
|
-
const coerced = this.coerceAndValidate(field, pasteRows[ri][ci], updated[targetRow]);
|
|
9534
|
-
if (coerced !== PASTE_SKIP) {
|
|
9535
|
-
rowCopy[field] = coerced;
|
|
9536
|
-
changed = true;
|
|
9537
|
-
}
|
|
9538
|
-
}
|
|
9539
|
-
if (changed) {
|
|
9540
|
-
updated[targetRow] = rowCopy;
|
|
9541
|
-
}
|
|
9542
|
-
}
|
|
9543
|
-
return updated;
|
|
9544
|
-
});
|
|
9545
|
-
}
|
|
9546
10610
|
extractRowSelectionData(selectedRows) {
|
|
9547
10611
|
const cols = this.state.visibleColumns();
|
|
9548
10612
|
const defMap = this.state.columnDefMap();
|
|
@@ -9679,6 +10743,8 @@ class MozGridComponent {
|
|
|
9679
10743
|
RowSelectionEngine,
|
|
9680
10744
|
CellSelectionEngine,
|
|
9681
10745
|
KeyboardEngine,
|
|
10746
|
+
ClipboardEngine,
|
|
10747
|
+
HistoryEngine,
|
|
9682
10748
|
GroupEngine,
|
|
9683
10749
|
FilterEngine,
|
|
9684
10750
|
ColumnReorderEngine,
|
|
@@ -9698,30 +10764,29 @@ class MozGridComponent {
|
|
|
9698
10764
|
<div class="moz-grid__toolbar">
|
|
9699
10765
|
<div class="moz-grid__toolbar-left">
|
|
9700
10766
|
@if (fullscreen()) {
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
|
|
9713
|
-
|
|
9714
|
-
}
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
</moz-icon-button>
|
|
10767
|
+
<moz-icon-button
|
|
10768
|
+
id="grid-fullscreen"
|
|
10769
|
+
[ghost]="true"
|
|
10770
|
+
size="s"
|
|
10771
|
+
[ariaLabel]="isFullscreen() ? 'Exit fullscreen' : 'Fullscreen'"
|
|
10772
|
+
(activated)="toggleFullscreen()"
|
|
10773
|
+
>
|
|
10774
|
+
@if (isFullscreen()) {
|
|
10775
|
+
<FullscreenExit20 icon />
|
|
10776
|
+
} @else {
|
|
10777
|
+
<FullscreenEnter20 icon />
|
|
10778
|
+
}
|
|
10779
|
+
</moz-icon-button>
|
|
10780
|
+
} @if (exportable()) {
|
|
10781
|
+
<moz-icon-button
|
|
10782
|
+
id="grid-export"
|
|
10783
|
+
[ghost]="true"
|
|
10784
|
+
size="s"
|
|
10785
|
+
ariaLabel="Export CSV"
|
|
10786
|
+
(activated)="onExportCsv()"
|
|
10787
|
+
>
|
|
10788
|
+
<Download20 icon />
|
|
10789
|
+
</moz-icon-button>
|
|
9725
10790
|
}
|
|
9726
10791
|
<moz-icon-button
|
|
9727
10792
|
id="grid-filter"
|
|
@@ -9750,113 +10815,113 @@ class MozGridComponent {
|
|
|
9750
10815
|
>
|
|
9751
10816
|
<ViewGridX420 icon />
|
|
9752
10817
|
</moz-icon-button>
|
|
10818
|
+
<moz-icon-button
|
|
10819
|
+
id="grid-keyboard-shortcuts"
|
|
10820
|
+
size="s"
|
|
10821
|
+
[ghost]="true"
|
|
10822
|
+
ariaLabel="Keyboard shortcuts"
|
|
10823
|
+
(activated)="onKeyboardShortcutsClick()"
|
|
10824
|
+
>
|
|
10825
|
+
<Keyboard20 icon />
|
|
10826
|
+
</moz-icon-button>
|
|
9753
10827
|
@for (def of toolbarStartDefs(); track def) {
|
|
9754
|
-
|
|
10828
|
+
<ng-container [ngTemplateOutlet]="def.template" />
|
|
9755
10829
|
}
|
|
9756
10830
|
</div>
|
|
9757
10831
|
|
|
9758
10832
|
<!-- Selection banner -->
|
|
9759
10833
|
@if (selectionBannerVisible()) {
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
|
|
9776
|
-
|
|
9777
|
-
|
|
9778
|
-
|
|
9779
|
-
|
|
9780
|
-
[ghost]="true"
|
|
9781
|
-
(click)="onClearSelection()"
|
|
9782
|
-
>
|
|
9783
|
-
Clear
|
|
9784
|
-
</button>
|
|
9785
|
-
</div>
|
|
10834
|
+
<div class="moz-grid__selection-banner">
|
|
10835
|
+
<span class="moz-grid__selection-text">
|
|
10836
|
+
{{ rowSelectionEngine.count() }} row(s) selected
|
|
10837
|
+
</span>
|
|
10838
|
+
@if (rowSelectionEngine.count() < selectionTotalCount()) {
|
|
10839
|
+
<button
|
|
10840
|
+
moz-button
|
|
10841
|
+
type="button"
|
|
10842
|
+
[size]="'s'"
|
|
10843
|
+
[ghost]="true"
|
|
10844
|
+
[appearance]="'accent'"
|
|
10845
|
+
(click)="onSelectAllRows()"
|
|
10846
|
+
>
|
|
10847
|
+
Select all {{ selectionTotalCount() }} rows
|
|
10848
|
+
</button>
|
|
10849
|
+
}
|
|
10850
|
+
<button moz-button type="button" [size]="'s'" [ghost]="true" (click)="onClearSelection()">
|
|
10851
|
+
Clear
|
|
10852
|
+
</button>
|
|
10853
|
+
</div>
|
|
9786
10854
|
}
|
|
9787
10855
|
|
|
9788
10856
|
<div class="moz-grid__toolbar-right">
|
|
9789
10857
|
@for (def of toolbarEndDefs(); track def) {
|
|
9790
|
-
|
|
10858
|
+
<ng-container [ngTemplateOutlet]="def.template" />
|
|
9791
10859
|
}
|
|
9792
10860
|
</div>
|
|
9793
10861
|
</div>
|
|
9794
10862
|
|
|
9795
10863
|
<!-- Tag bars (outside .moz-grid) -->
|
|
9796
10864
|
@if (state.groupColumns().length > 0) {
|
|
9797
|
-
|
|
9798
|
-
|
|
9799
|
-
|
|
9800
|
-
|
|
9801
|
-
|
|
9802
|
-
|
|
9803
|
-
|
|
9804
|
-
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
9809
|
-
|
|
9810
|
-
|
|
9811
|
-
>
|
|
9812
|
-
{{ getColumnLabel(entry.field) }}
|
|
9813
|
-
@if (entry.sortDirection === 'asc') {
|
|
9814
|
-
<ChevronUp20 />
|
|
9815
|
-
} @else {
|
|
9816
|
-
<ChevronDown20 />
|
|
9817
|
-
}
|
|
9818
|
-
</button>
|
|
9819
|
-
</moz-tag>
|
|
9820
|
-
</div>
|
|
9821
|
-
}
|
|
9822
|
-
</div>
|
|
9823
|
-
}
|
|
9824
|
-
@if (hasHiddenColumns()) {
|
|
9825
|
-
<div class="moz-grid__tag-bar">
|
|
9826
|
-
<span class="moz-grid__tag-bar-label">HIDDEN COLUMNS</span>
|
|
9827
|
-
@for (col of hiddenColumnsList(); track col.field) {
|
|
9828
|
-
<moz-tag
|
|
9829
|
-
type="removable"
|
|
9830
|
-
size="s"
|
|
9831
|
-
[id]="'hidden-' + col.field"
|
|
9832
|
-
removableLabel="Restore"
|
|
9833
|
-
(removeTag)="onRestoreColumn(col.field)"
|
|
9834
|
-
>{{ col.label }}</moz-tag
|
|
10865
|
+
<div class="moz-grid__tag-bar">
|
|
10866
|
+
<span class="moz-grid__tag-bar-label">GROUPED BY</span>
|
|
10867
|
+
@for (entry of state.groupColumns(); track entry.field) {
|
|
10868
|
+
<div>
|
|
10869
|
+
<moz-tag
|
|
10870
|
+
type="removable"
|
|
10871
|
+
size="s"
|
|
10872
|
+
[id]="'group-' + entry.field"
|
|
10873
|
+
(removeTag)="onRemoveGroup(entry.field)"
|
|
10874
|
+
>
|
|
10875
|
+
<button
|
|
10876
|
+
type="button"
|
|
10877
|
+
class="moz-grid__group-tag-btn"
|
|
10878
|
+
(click)="onToggleGroupSort(entry.field)"
|
|
9835
10879
|
>
|
|
9836
|
-
|
|
9837
|
-
|
|
9838
|
-
|
|
9839
|
-
|
|
10880
|
+
{{ getColumnLabel(entry.field) }}
|
|
10881
|
+
@if (entry.sortDirection === 'asc') {
|
|
10882
|
+
<ChevronUp20 />
|
|
10883
|
+
} @else {
|
|
10884
|
+
<ChevronDown20 />
|
|
10885
|
+
}
|
|
9840
10886
|
</button>
|
|
9841
|
-
|
|
9842
|
-
</div>
|
|
9843
|
-
}
|
|
9844
|
-
@if (state.activeFilters().length > 0) {
|
|
9845
|
-
<div class="moz-grid__tag-bar">
|
|
9846
|
-
<span class="moz-grid__tag-bar-label">FILTERED BY</span>
|
|
9847
|
-
@for (filter of state.activeFilters(); track filter.field) {
|
|
9848
|
-
<moz-tag
|
|
9849
|
-
[type]="filter.removable ? 'removable' : 'informative'"
|
|
9850
|
-
size="s"
|
|
9851
|
-
[id]="'filter-' + filter.field"
|
|
9852
|
-
(removeTag)="onRemoveFilter(filter.field)"
|
|
9853
|
-
>{{ filter.label }}</moz-tag
|
|
9854
|
-
>
|
|
9855
|
-
}
|
|
9856
|
-
<button type="button" class="moz-grid__tag-action-btn" (click)="onRemoveAllFilters()">
|
|
9857
|
-
Remove all
|
|
9858
|
-
</button>
|
|
10887
|
+
</moz-tag>
|
|
9859
10888
|
</div>
|
|
10889
|
+
}
|
|
10890
|
+
</div>
|
|
10891
|
+
} @if (hasHiddenColumns()) {
|
|
10892
|
+
<div class="moz-grid__tag-bar">
|
|
10893
|
+
<span class="moz-grid__tag-bar-label">HIDDEN COLUMNS</span>
|
|
10894
|
+
@for (col of hiddenColumnsList(); track col.field) {
|
|
10895
|
+
<moz-tag
|
|
10896
|
+
type="removable"
|
|
10897
|
+
size="s"
|
|
10898
|
+
[id]="'hidden-' + col.field"
|
|
10899
|
+
removableLabel="Restore"
|
|
10900
|
+
(removeTag)="onRestoreColumn(col.field)"
|
|
10901
|
+
>{{ col.label }}</moz-tag
|
|
10902
|
+
>
|
|
10903
|
+
} @if (hiddenColumnsList().length > 1) {
|
|
10904
|
+
<button type="button" class="moz-grid__tag-action-btn" (click)="onRestoreAllColumns()">
|
|
10905
|
+
Restore all
|
|
10906
|
+
</button>
|
|
10907
|
+
}
|
|
10908
|
+
</div>
|
|
10909
|
+
} @if (state.activeFilters().length > 0) {
|
|
10910
|
+
<div class="moz-grid__tag-bar">
|
|
10911
|
+
<span class="moz-grid__tag-bar-label">FILTERED BY</span>
|
|
10912
|
+
@for (filter of state.activeFilters(); track filter.field) {
|
|
10913
|
+
<moz-tag
|
|
10914
|
+
[type]="filter.removable ? 'removable' : 'informative'"
|
|
10915
|
+
size="s"
|
|
10916
|
+
[id]="'filter-' + filter.field"
|
|
10917
|
+
(removeTag)="onRemoveFilter(filter.field)"
|
|
10918
|
+
>{{ filter.label }}</moz-tag
|
|
10919
|
+
>
|
|
10920
|
+
}
|
|
10921
|
+
<button type="button" class="moz-grid__tag-action-btn" (click)="onRemoveAllFilters()">
|
|
10922
|
+
Remove all
|
|
10923
|
+
</button>
|
|
10924
|
+
</div>
|
|
9860
10925
|
}
|
|
9861
10926
|
<ng-content select="[mozGridFilterTags]" />
|
|
9862
10927
|
|
|
@@ -9893,13 +10958,12 @@ class MozGridComponent {
|
|
|
9893
10958
|
|
|
9894
10959
|
<!-- Footer: pagination or infinite scroll loading indicator -->
|
|
9895
10960
|
@if (showPagination()) {
|
|
9896
|
-
|
|
9897
|
-
|
|
9898
|
-
|
|
9899
|
-
|
|
9900
|
-
}
|
|
9901
|
-
|
|
9902
|
-
<moz-grid-loading-indicator />
|
|
10961
|
+
<moz-grid-footer
|
|
10962
|
+
[pageSizeOptions]="pageSizeOptions()"
|
|
10963
|
+
(pageChange)="pageChange.emit($event)"
|
|
10964
|
+
/>
|
|
10965
|
+
} @if (showInfiniteScrollLoader()) {
|
|
10966
|
+
<moz-grid-loading-indicator />
|
|
9903
10967
|
}
|
|
9904
10968
|
|
|
9905
10969
|
<!-- Bulk selection action bar -->
|
|
@@ -9912,7 +10976,7 @@ class MozGridComponent {
|
|
|
9912
10976
|
/>
|
|
9913
10977
|
</div>
|
|
9914
10978
|
</div>
|
|
9915
|
-
`, 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 });
|
|
10979
|
+
`, 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 });
|
|
9916
10980
|
}
|
|
9917
10981
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: MozGridComponent, decorators: [{
|
|
9918
10982
|
type: Component,
|
|
@@ -9925,6 +10989,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
9925
10989
|
RowSelectionEngine,
|
|
9926
10990
|
CellSelectionEngine,
|
|
9927
10991
|
KeyboardEngine,
|
|
10992
|
+
ClipboardEngine,
|
|
10993
|
+
HistoryEngine,
|
|
9928
10994
|
GroupEngine,
|
|
9929
10995
|
FilterEngine,
|
|
9930
10996
|
ColumnReorderEngine,
|
|
@@ -9955,6 +11021,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
9955
11021
|
Download20,
|
|
9956
11022
|
ChevronUp20,
|
|
9957
11023
|
ChevronDown20,
|
|
11024
|
+
Keyboard20,
|
|
9958
11025
|
MozButtonComponent,
|
|
9959
11026
|
], template: `
|
|
9960
11027
|
<div class="moz-grid-wrapper" [class.moz-grid-wrapper--fullscreen]="isFullscreen()">
|
|
@@ -9962,30 +11029,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
9962
11029
|
<div class="moz-grid__toolbar">
|
|
9963
11030
|
<div class="moz-grid__toolbar-left">
|
|
9964
11031
|
@if (fullscreen()) {
|
|
9965
|
-
|
|
9966
|
-
|
|
9967
|
-
|
|
9968
|
-
|
|
9969
|
-
|
|
9970
|
-
|
|
9971
|
-
|
|
9972
|
-
|
|
9973
|
-
|
|
9974
|
-
|
|
9975
|
-
|
|
9976
|
-
|
|
9977
|
-
|
|
9978
|
-
}
|
|
9979
|
-
|
|
9980
|
-
|
|
9981
|
-
|
|
9982
|
-
|
|
9983
|
-
|
|
9984
|
-
|
|
9985
|
-
|
|
9986
|
-
|
|
9987
|
-
|
|
9988
|
-
</moz-icon-button>
|
|
11032
|
+
<moz-icon-button
|
|
11033
|
+
id="grid-fullscreen"
|
|
11034
|
+
[ghost]="true"
|
|
11035
|
+
size="s"
|
|
11036
|
+
[ariaLabel]="isFullscreen() ? 'Exit fullscreen' : 'Fullscreen'"
|
|
11037
|
+
(activated)="toggleFullscreen()"
|
|
11038
|
+
>
|
|
11039
|
+
@if (isFullscreen()) {
|
|
11040
|
+
<FullscreenExit20 icon />
|
|
11041
|
+
} @else {
|
|
11042
|
+
<FullscreenEnter20 icon />
|
|
11043
|
+
}
|
|
11044
|
+
</moz-icon-button>
|
|
11045
|
+
} @if (exportable()) {
|
|
11046
|
+
<moz-icon-button
|
|
11047
|
+
id="grid-export"
|
|
11048
|
+
[ghost]="true"
|
|
11049
|
+
size="s"
|
|
11050
|
+
ariaLabel="Export CSV"
|
|
11051
|
+
(activated)="onExportCsv()"
|
|
11052
|
+
>
|
|
11053
|
+
<Download20 icon />
|
|
11054
|
+
</moz-icon-button>
|
|
9989
11055
|
}
|
|
9990
11056
|
<moz-icon-button
|
|
9991
11057
|
id="grid-filter"
|
|
@@ -10014,113 +11080,113 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
10014
11080
|
>
|
|
10015
11081
|
<ViewGridX420 icon />
|
|
10016
11082
|
</moz-icon-button>
|
|
11083
|
+
<moz-icon-button
|
|
11084
|
+
id="grid-keyboard-shortcuts"
|
|
11085
|
+
size="s"
|
|
11086
|
+
[ghost]="true"
|
|
11087
|
+
ariaLabel="Keyboard shortcuts"
|
|
11088
|
+
(activated)="onKeyboardShortcutsClick()"
|
|
11089
|
+
>
|
|
11090
|
+
<Keyboard20 icon />
|
|
11091
|
+
</moz-icon-button>
|
|
10017
11092
|
@for (def of toolbarStartDefs(); track def) {
|
|
10018
|
-
|
|
11093
|
+
<ng-container [ngTemplateOutlet]="def.template" />
|
|
10019
11094
|
}
|
|
10020
11095
|
</div>
|
|
10021
11096
|
|
|
10022
11097
|
<!-- Selection banner -->
|
|
10023
11098
|
@if (selectionBannerVisible()) {
|
|
10024
|
-
|
|
10025
|
-
|
|
10026
|
-
|
|
10027
|
-
|
|
10028
|
-
|
|
10029
|
-
|
|
10030
|
-
|
|
10031
|
-
|
|
10032
|
-
|
|
10033
|
-
|
|
10034
|
-
|
|
10035
|
-
|
|
10036
|
-
|
|
10037
|
-
|
|
10038
|
-
|
|
10039
|
-
|
|
10040
|
-
|
|
10041
|
-
|
|
10042
|
-
|
|
10043
|
-
|
|
10044
|
-
[ghost]="true"
|
|
10045
|
-
(click)="onClearSelection()"
|
|
10046
|
-
>
|
|
10047
|
-
Clear
|
|
10048
|
-
</button>
|
|
10049
|
-
</div>
|
|
11099
|
+
<div class="moz-grid__selection-banner">
|
|
11100
|
+
<span class="moz-grid__selection-text">
|
|
11101
|
+
{{ rowSelectionEngine.count() }} row(s) selected
|
|
11102
|
+
</span>
|
|
11103
|
+
@if (rowSelectionEngine.count() < selectionTotalCount()) {
|
|
11104
|
+
<button
|
|
11105
|
+
moz-button
|
|
11106
|
+
type="button"
|
|
11107
|
+
[size]="'s'"
|
|
11108
|
+
[ghost]="true"
|
|
11109
|
+
[appearance]="'accent'"
|
|
11110
|
+
(click)="onSelectAllRows()"
|
|
11111
|
+
>
|
|
11112
|
+
Select all {{ selectionTotalCount() }} rows
|
|
11113
|
+
</button>
|
|
11114
|
+
}
|
|
11115
|
+
<button moz-button type="button" [size]="'s'" [ghost]="true" (click)="onClearSelection()">
|
|
11116
|
+
Clear
|
|
11117
|
+
</button>
|
|
11118
|
+
</div>
|
|
10050
11119
|
}
|
|
10051
11120
|
|
|
10052
11121
|
<div class="moz-grid__toolbar-right">
|
|
10053
11122
|
@for (def of toolbarEndDefs(); track def) {
|
|
10054
|
-
|
|
11123
|
+
<ng-container [ngTemplateOutlet]="def.template" />
|
|
10055
11124
|
}
|
|
10056
11125
|
</div>
|
|
10057
11126
|
</div>
|
|
10058
11127
|
|
|
10059
11128
|
<!-- Tag bars (outside .moz-grid) -->
|
|
10060
11129
|
@if (state.groupColumns().length > 0) {
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
10072
|
-
|
|
10073
|
-
|
|
10074
|
-
|
|
10075
|
-
>
|
|
10076
|
-
{{ getColumnLabel(entry.field) }}
|
|
10077
|
-
@if (entry.sortDirection === 'asc') {
|
|
10078
|
-
<ChevronUp20 />
|
|
10079
|
-
} @else {
|
|
10080
|
-
<ChevronDown20 />
|
|
10081
|
-
}
|
|
10082
|
-
</button>
|
|
10083
|
-
</moz-tag>
|
|
10084
|
-
</div>
|
|
10085
|
-
}
|
|
10086
|
-
</div>
|
|
10087
|
-
}
|
|
10088
|
-
@if (hasHiddenColumns()) {
|
|
10089
|
-
<div class="moz-grid__tag-bar">
|
|
10090
|
-
<span class="moz-grid__tag-bar-label">HIDDEN COLUMNS</span>
|
|
10091
|
-
@for (col of hiddenColumnsList(); track col.field) {
|
|
10092
|
-
<moz-tag
|
|
10093
|
-
type="removable"
|
|
10094
|
-
size="s"
|
|
10095
|
-
[id]="'hidden-' + col.field"
|
|
10096
|
-
removableLabel="Restore"
|
|
10097
|
-
(removeTag)="onRestoreColumn(col.field)"
|
|
10098
|
-
>{{ col.label }}</moz-tag
|
|
11130
|
+
<div class="moz-grid__tag-bar">
|
|
11131
|
+
<span class="moz-grid__tag-bar-label">GROUPED BY</span>
|
|
11132
|
+
@for (entry of state.groupColumns(); track entry.field) {
|
|
11133
|
+
<div>
|
|
11134
|
+
<moz-tag
|
|
11135
|
+
type="removable"
|
|
11136
|
+
size="s"
|
|
11137
|
+
[id]="'group-' + entry.field"
|
|
11138
|
+
(removeTag)="onRemoveGroup(entry.field)"
|
|
11139
|
+
>
|
|
11140
|
+
<button
|
|
11141
|
+
type="button"
|
|
11142
|
+
class="moz-grid__group-tag-btn"
|
|
11143
|
+
(click)="onToggleGroupSort(entry.field)"
|
|
10099
11144
|
>
|
|
10100
|
-
|
|
10101
|
-
|
|
10102
|
-
|
|
10103
|
-
|
|
11145
|
+
{{ getColumnLabel(entry.field) }}
|
|
11146
|
+
@if (entry.sortDirection === 'asc') {
|
|
11147
|
+
<ChevronUp20 />
|
|
11148
|
+
} @else {
|
|
11149
|
+
<ChevronDown20 />
|
|
11150
|
+
}
|
|
10104
11151
|
</button>
|
|
10105
|
-
|
|
10106
|
-
</div>
|
|
10107
|
-
}
|
|
10108
|
-
@if (state.activeFilters().length > 0) {
|
|
10109
|
-
<div class="moz-grid__tag-bar">
|
|
10110
|
-
<span class="moz-grid__tag-bar-label">FILTERED BY</span>
|
|
10111
|
-
@for (filter of state.activeFilters(); track filter.field) {
|
|
10112
|
-
<moz-tag
|
|
10113
|
-
[type]="filter.removable ? 'removable' : 'informative'"
|
|
10114
|
-
size="s"
|
|
10115
|
-
[id]="'filter-' + filter.field"
|
|
10116
|
-
(removeTag)="onRemoveFilter(filter.field)"
|
|
10117
|
-
>{{ filter.label }}</moz-tag
|
|
10118
|
-
>
|
|
10119
|
-
}
|
|
10120
|
-
<button type="button" class="moz-grid__tag-action-btn" (click)="onRemoveAllFilters()">
|
|
10121
|
-
Remove all
|
|
10122
|
-
</button>
|
|
11152
|
+
</moz-tag>
|
|
10123
11153
|
</div>
|
|
11154
|
+
}
|
|
11155
|
+
</div>
|
|
11156
|
+
} @if (hasHiddenColumns()) {
|
|
11157
|
+
<div class="moz-grid__tag-bar">
|
|
11158
|
+
<span class="moz-grid__tag-bar-label">HIDDEN COLUMNS</span>
|
|
11159
|
+
@for (col of hiddenColumnsList(); track col.field) {
|
|
11160
|
+
<moz-tag
|
|
11161
|
+
type="removable"
|
|
11162
|
+
size="s"
|
|
11163
|
+
[id]="'hidden-' + col.field"
|
|
11164
|
+
removableLabel="Restore"
|
|
11165
|
+
(removeTag)="onRestoreColumn(col.field)"
|
|
11166
|
+
>{{ col.label }}</moz-tag
|
|
11167
|
+
>
|
|
11168
|
+
} @if (hiddenColumnsList().length > 1) {
|
|
11169
|
+
<button type="button" class="moz-grid__tag-action-btn" (click)="onRestoreAllColumns()">
|
|
11170
|
+
Restore all
|
|
11171
|
+
</button>
|
|
11172
|
+
}
|
|
11173
|
+
</div>
|
|
11174
|
+
} @if (state.activeFilters().length > 0) {
|
|
11175
|
+
<div class="moz-grid__tag-bar">
|
|
11176
|
+
<span class="moz-grid__tag-bar-label">FILTERED BY</span>
|
|
11177
|
+
@for (filter of state.activeFilters(); track filter.field) {
|
|
11178
|
+
<moz-tag
|
|
11179
|
+
[type]="filter.removable ? 'removable' : 'informative'"
|
|
11180
|
+
size="s"
|
|
11181
|
+
[id]="'filter-' + filter.field"
|
|
11182
|
+
(removeTag)="onRemoveFilter(filter.field)"
|
|
11183
|
+
>{{ filter.label }}</moz-tag
|
|
11184
|
+
>
|
|
11185
|
+
}
|
|
11186
|
+
<button type="button" class="moz-grid__tag-action-btn" (click)="onRemoveAllFilters()">
|
|
11187
|
+
Remove all
|
|
11188
|
+
</button>
|
|
11189
|
+
</div>
|
|
10124
11190
|
}
|
|
10125
11191
|
<ng-content select="[mozGridFilterTags]" />
|
|
10126
11192
|
|
|
@@ -10157,13 +11223,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
10157
11223
|
|
|
10158
11224
|
<!-- Footer: pagination or infinite scroll loading indicator -->
|
|
10159
11225
|
@if (showPagination()) {
|
|
10160
|
-
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
}
|
|
10165
|
-
|
|
10166
|
-
<moz-grid-loading-indicator />
|
|
11226
|
+
<moz-grid-footer
|
|
11227
|
+
[pageSizeOptions]="pageSizeOptions()"
|
|
11228
|
+
(pageChange)="pageChange.emit($event)"
|
|
11229
|
+
/>
|
|
11230
|
+
} @if (showInfiniteScrollLoader()) {
|
|
11231
|
+
<moz-grid-loading-indicator />
|
|
10167
11232
|
}
|
|
10168
11233
|
|
|
10169
11234
|
<!-- Bulk selection action bar -->
|