@gp-grid/angular 0.10.3 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +203 -203
- package/dist/styles.css +950 -0
- package/fesm2022/gp-grid-angular.mjs +1630 -0
- package/package.json +10 -22
- package/types/gp-grid-angular.d.ts +444 -0
- package/ng-package.json +0 -10
- package/src/lib/components/filter-popup/filter-logic.ts +0 -243
- package/src/lib/components/filter-popup.component.ts +0 -236
- package/src/lib/components/filter-popup.template.ts +0 -182
- package/src/lib/components/grid-body.component.ts +0 -284
- package/src/lib/components/grid-body.template.ts +0 -93
- package/src/lib/components/grid-header.component.ts +0 -206
- package/src/lib/components/grid-overlays.component.ts +0 -140
- package/src/lib/components/index.ts +0 -4
- package/src/lib/createGridData.ts +0 -82
- package/src/lib/gp-grid-bindings.ts +0 -150
- package/src/lib/gp-grid-view-model.ts +0 -148
- package/src/lib/gp-grid.component.ts +0 -279
- package/src/lib/gp-grid.factory.ts +0 -52
- package/src/lib/gp-grid.template.ts +0 -77
- package/src/lib/styles/index.ts +0 -0
- package/src/lib/types.ts +0 -28
- package/src/public-api.ts +0 -57
- package/tsconfig.json +0 -11
- package/tsconfig.lib.json +0 -27
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ChangeDetectionStrategy,
|
|
3
|
-
Component,
|
|
4
|
-
computed,
|
|
5
|
-
input,
|
|
6
|
-
output,
|
|
7
|
-
} from '@angular/core';
|
|
8
|
-
import type {
|
|
9
|
-
CellValue,
|
|
10
|
-
ColumnDefinition,
|
|
11
|
-
ColumnFilterModel,
|
|
12
|
-
DragState,
|
|
13
|
-
VisibleColumnInfo,
|
|
14
|
-
} from '@gp-grid/core';
|
|
15
|
-
import { FilterPopupComponent } from './filter-popup.component';
|
|
16
|
-
|
|
17
|
-
export interface ActiveFilterPopup {
|
|
18
|
-
colIndex: number;
|
|
19
|
-
column: ColumnDefinition;
|
|
20
|
-
distinctValues: CellValue[];
|
|
21
|
-
currentFilter?: ColumnFilterModel;
|
|
22
|
-
anchorEl: HTMLElement | null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const TEMPLATE = `
|
|
26
|
-
@if (filterPopup(); as fp) {
|
|
27
|
-
<gp-grid-filter-popup
|
|
28
|
-
[column]="fp.column"
|
|
29
|
-
[colIndex]="fp.colIndex"
|
|
30
|
-
[anchorEl]="fp.anchorEl!"
|
|
31
|
-
[distinctValues]="fp.distinctValues"
|
|
32
|
-
[currentFilter]="fp.currentFilter"
|
|
33
|
-
(apply)="filterApply.emit($event)"
|
|
34
|
-
(close)="filterClose.emit()"
|
|
35
|
-
/>
|
|
36
|
-
}
|
|
37
|
-
@if (isResizing()) {
|
|
38
|
-
<div class="gp-grid-column-resize-line" [style.left.px]="resizeLineLeft()"></div>
|
|
39
|
-
}
|
|
40
|
-
@if (isLoading()) {
|
|
41
|
-
<div
|
|
42
|
-
style="position: absolute; left: 0; right: 0; bottom: 0; z-index: 50; pointer-events: none;"
|
|
43
|
-
[style.top.px]="headerHeight()">
|
|
44
|
-
<div class="gp-grid-loading-overlay"></div>
|
|
45
|
-
<div class="gp-grid-loading">
|
|
46
|
-
<div class="gp-grid-loading-spinner"></div>
|
|
47
|
-
</div>
|
|
48
|
-
</div>
|
|
49
|
-
}
|
|
50
|
-
@if (errorMessage(); as msg) {
|
|
51
|
-
<div class="gp-grid-error">Error: {{ msg }}</div>
|
|
52
|
-
}
|
|
53
|
-
@if (columnMove(); as cm) {
|
|
54
|
-
<div
|
|
55
|
-
class="gp-grid-column-move-ghost"
|
|
56
|
-
[style.left.px]="cm.currentX - cm.ghostWidth / 2"
|
|
57
|
-
[style.top.px]="cm.currentY - cm.ghostHeight / 2"
|
|
58
|
-
[style.width.px]="cm.ghostWidth"
|
|
59
|
-
[style.height.px]="cm.ghostHeight">
|
|
60
|
-
{{ columnMoveGhostText() }}
|
|
61
|
-
</div>
|
|
62
|
-
@if (columnMoveDropLeft() !== null) {
|
|
63
|
-
<div
|
|
64
|
-
class="gp-grid-column-drop-indicator"
|
|
65
|
-
[style.left.px]="columnMoveDropLeft()"
|
|
66
|
-
[style.height.px]="headerHeight()"></div>
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
@if (rowDragGhost(); as rd) {
|
|
70
|
-
<div
|
|
71
|
-
class="gp-grid-row-drag-ghost"
|
|
72
|
-
[style.left.px]="rd.currentX + 12"
|
|
73
|
-
[style.top.px]="rd.currentY - rowHeight() / 2"
|
|
74
|
-
[style.width.px]="rowDragGhostWidth()"
|
|
75
|
-
[style.height.px]="rowHeight()"></div>
|
|
76
|
-
}
|
|
77
|
-
`;
|
|
78
|
-
|
|
79
|
-
@Component({
|
|
80
|
-
selector: 'gp-grid-overlays',
|
|
81
|
-
standalone: true,
|
|
82
|
-
imports: [FilterPopupComponent],
|
|
83
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
84
|
-
template: TEMPLATE,
|
|
85
|
-
})
|
|
86
|
-
export class GridOverlaysComponent {
|
|
87
|
-
filterPopup = input<ActiveFilterPopup | null>(null);
|
|
88
|
-
isLoading = input<boolean>(false);
|
|
89
|
-
errorMessage = input<string | null>(null);
|
|
90
|
-
headerHeight = input.required<number>();
|
|
91
|
-
rowHeight = input.required<number>();
|
|
92
|
-
dragState = input.required<DragState>();
|
|
93
|
-
visibleColumnWithIndices = input.required<VisibleColumnInfo[]>();
|
|
94
|
-
columnPositions = input.required<number[]>();
|
|
95
|
-
scrollLeft = input.required<number>();
|
|
96
|
-
effectiveColumns = input.required<ColumnDefinition[]>();
|
|
97
|
-
totalWidth = input.required<number>();
|
|
98
|
-
|
|
99
|
-
filterApply = output<{ colId: string; filter: ColumnFilterModel | null }>();
|
|
100
|
-
filterClose = output<void>();
|
|
101
|
-
|
|
102
|
-
protected isResizing = computed(() => this.dragState().dragType === 'column-resize');
|
|
103
|
-
|
|
104
|
-
protected resizeLineLeft = computed<number>(() => {
|
|
105
|
-
const cr = this.dragState().columnResize;
|
|
106
|
-
if (cr === null) return 0;
|
|
107
|
-
const visibleIndex = this.visibleColumnWithIndices().findIndex(
|
|
108
|
-
v => v.originalIndex === cr.colIndex
|
|
109
|
-
);
|
|
110
|
-
if (visibleIndex === -1) return 0;
|
|
111
|
-
const positions = this.columnPositions();
|
|
112
|
-
return (positions[visibleIndex] ?? 0) + cr.currentWidth - this.scrollLeft();
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
protected columnMove = computed(() => {
|
|
116
|
-
if (this.dragState().dragType !== 'column-move') return null;
|
|
117
|
-
return this.dragState().columnMove;
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
protected rowDragGhost = computed(() => {
|
|
121
|
-
if (this.dragState().dragType !== 'row-drag') return null;
|
|
122
|
-
return this.dragState().rowDrag;
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
protected columnMoveGhostText = computed<string>(() => {
|
|
126
|
-
const cm = this.columnMove();
|
|
127
|
-
if (cm === null) return '';
|
|
128
|
-
const column = this.effectiveColumns()[cm.sourceColIndex];
|
|
129
|
-
return column?.headerName ?? column?.field ?? '';
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
protected columnMoveDropLeft = computed<number | null>(() => {
|
|
133
|
-
const cm = this.columnMove();
|
|
134
|
-
if (cm === null || cm.dropTargetIndex === null) return null;
|
|
135
|
-
const positions = this.columnPositions();
|
|
136
|
-
return (positions[cm.dropTargetIndex] ?? 0) - this.scrollLeft();
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
protected rowDragGhostWidth = computed<number>(() => Math.min(300, this.totalWidth()));
|
|
140
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { createMutableClientDataSource } from "@gp-grid/core";
|
|
2
|
-
import type {
|
|
3
|
-
RowId,
|
|
4
|
-
CellValue,
|
|
5
|
-
MutableDataSource,
|
|
6
|
-
ParallelSortOptions,
|
|
7
|
-
} from "@gp-grid/core";
|
|
8
|
-
|
|
9
|
-
export interface CreateGridDataOptions<TData> {
|
|
10
|
-
/** Function to extract a unique ID from each row. Required. */
|
|
11
|
-
getRowId: (row: TData) => RowId;
|
|
12
|
-
/** Debounce time for batching transactions in ms. Default 50. */
|
|
13
|
-
debounceMs?: number;
|
|
14
|
-
/** Use Web Worker for sorting large datasets (default: true) */
|
|
15
|
-
useWorker?: boolean;
|
|
16
|
-
/** Options for parallel sorting (only used when useWorker is true) */
|
|
17
|
-
parallelSort?: ParallelSortOptions | false;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface GridDataApi<TData> {
|
|
21
|
-
/** The data source to pass to <gp-grid [dataSource]="dataSource" />. */
|
|
22
|
-
dataSource: MutableDataSource<TData>;
|
|
23
|
-
/** Update a single row by ID with partial data. */
|
|
24
|
-
updateRow: (id: RowId, data: Partial<TData>) => void;
|
|
25
|
-
/** Add rows to the data source. */
|
|
26
|
-
addRows: (rows: TData[]) => void;
|
|
27
|
-
/** Remove rows by ID. */
|
|
28
|
-
removeRows: (ids: RowId[]) => void;
|
|
29
|
-
/** Update a single cell value. */
|
|
30
|
-
updateCell: (id: RowId, field: string, value: CellValue) => void;
|
|
31
|
-
/** Clear all data from the data source. */
|
|
32
|
-
clear: () => void;
|
|
33
|
-
/** Get a row by its ID. */
|
|
34
|
-
getRowById: (id: RowId) => TData | undefined;
|
|
35
|
-
/** Get the current total row count. */
|
|
36
|
-
getTotalRowCount: () => number;
|
|
37
|
-
/** Force immediate processing of queued transactions. */
|
|
38
|
-
flushTransactions: () => Promise<void>;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Angular helper for efficient grid data mutations.
|
|
43
|
-
*
|
|
44
|
-
* Wraps `createMutableClientDataSource` to provide a simple API for
|
|
45
|
-
* updating grid data without triggering full pipeline rebuilds.
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* ```ts
|
|
49
|
-
* private readonly grid = createGridData(initialRows, {
|
|
50
|
-
* getRowId: (row) => row.id,
|
|
51
|
-
* });
|
|
52
|
-
*
|
|
53
|
-
* // Template:
|
|
54
|
-
* // <gp-grid [dataSource]="grid.dataSource" [columns]="columns" />
|
|
55
|
-
*
|
|
56
|
-
* // Update a row imperatively:
|
|
57
|
-
* this.grid.updateRow(42, { name: 'New name' });
|
|
58
|
-
* ```
|
|
59
|
-
*/
|
|
60
|
-
export const createGridData = <TData = unknown>(
|
|
61
|
-
initialData: TData[],
|
|
62
|
-
options: CreateGridDataOptions<TData>,
|
|
63
|
-
): GridDataApi<TData> => {
|
|
64
|
-
const ds = createMutableClientDataSource<TData>(initialData, {
|
|
65
|
-
getRowId: options.getRowId,
|
|
66
|
-
debounceMs: options.debounceMs,
|
|
67
|
-
useWorker: options.useWorker,
|
|
68
|
-
parallelSort: options.parallelSort,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
return {
|
|
72
|
-
dataSource: ds,
|
|
73
|
-
updateRow: ds.updateRow,
|
|
74
|
-
addRows: ds.addRows,
|
|
75
|
-
removeRows: ds.removeRows,
|
|
76
|
-
updateCell: ds.updateCell,
|
|
77
|
-
clear: ds.clear,
|
|
78
|
-
getRowById: ds.getRowById,
|
|
79
|
-
getTotalRowCount: ds.getTotalRowCount,
|
|
80
|
-
flushTransactions: ds.flushTransactions,
|
|
81
|
-
};
|
|
82
|
-
};
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AutoScrollDriver,
|
|
3
|
-
DataSourceOwner,
|
|
4
|
-
GridCore,
|
|
5
|
-
InputEventAdapter,
|
|
6
|
-
PendingRowDragController,
|
|
7
|
-
applyBatchInstructions,
|
|
8
|
-
scrollCellIntoView,
|
|
9
|
-
} from '@gp-grid/core';
|
|
10
|
-
import type {
|
|
11
|
-
ColumnDefinition,
|
|
12
|
-
DataSource,
|
|
13
|
-
HighlightingOptions,
|
|
14
|
-
} from '@gp-grid/core';
|
|
15
|
-
import type { GpGridViewModel } from './gp-grid-view-model';
|
|
16
|
-
|
|
17
|
-
export interface GpGridBindingsDeps {
|
|
18
|
-
vm: GpGridViewModel;
|
|
19
|
-
isBrowser: boolean;
|
|
20
|
-
getContainer: () => HTMLElement | null;
|
|
21
|
-
getBody: () => HTMLElement | null;
|
|
22
|
-
getRowHeight: () => number;
|
|
23
|
-
getHeaderHeight: () => number;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Owns the core grid instance plus every framework-agnostic adapter the
|
|
28
|
-
* Angular component drives (auto-scroll, pending row-drag, input events,
|
|
29
|
-
* data source ownership). The component becomes a thin shell that holds
|
|
30
|
-
* lifecycle + Angular template bindings and delegates state work here.
|
|
31
|
-
*/
|
|
32
|
-
export class GpGridBindings<TData = unknown> {
|
|
33
|
-
readonly dataSourceOwner = new DataSourceOwner<TData>();
|
|
34
|
-
readonly autoScroll: AutoScrollDriver;
|
|
35
|
-
readonly pendingRowDrag: PendingRowDragController;
|
|
36
|
-
readonly input: InputEventAdapter<TData>;
|
|
37
|
-
|
|
38
|
-
coreRef: GridCore<TData> | null = null;
|
|
39
|
-
private unsubscribe: (() => void) | null = null;
|
|
40
|
-
private resizeObserver: ResizeObserver | null = null;
|
|
41
|
-
|
|
42
|
-
constructor(private readonly deps: GpGridBindingsDeps) {
|
|
43
|
-
this.autoScroll = new AutoScrollDriver(
|
|
44
|
-
() => this.deps.getBody(),
|
|
45
|
-
(event) => this.input.dragMove(event),
|
|
46
|
-
);
|
|
47
|
-
this.pendingRowDrag = new PendingRowDragController({
|
|
48
|
-
getCore: () => this.coreRef,
|
|
49
|
-
getContainer: this.deps.getContainer,
|
|
50
|
-
isBrowser: this.deps.isBrowser,
|
|
51
|
-
onDragConfirmed: (state) => this.deps.vm.dragState.set(state),
|
|
52
|
-
});
|
|
53
|
-
this.input = new InputEventAdapter<TData>({
|
|
54
|
-
getCore: () => this.coreRef,
|
|
55
|
-
getBodyEl: this.deps.getBody,
|
|
56
|
-
autoScroll: this.autoScroll,
|
|
57
|
-
pendingRowDrag: this.pendingRowDrag,
|
|
58
|
-
onDragStateChange: (state) => this.deps.vm.dragState.set(state),
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
attach(core: GridCore<TData>): void {
|
|
63
|
-
this.coreRef = core;
|
|
64
|
-
this.unsubscribe = core.onBatchInstruction((instructions) => {
|
|
65
|
-
const vm = this.deps.vm;
|
|
66
|
-
const maps = applyBatchInstructions(
|
|
67
|
-
instructions,
|
|
68
|
-
vm.slots(),
|
|
69
|
-
vm.headerState(),
|
|
70
|
-
vm.batchSetters,
|
|
71
|
-
);
|
|
72
|
-
vm.slots.set(new Map(maps.slots));
|
|
73
|
-
vm.headerState.set(new Map(maps.headers));
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
core.initialize();
|
|
77
|
-
core.input.updateDeps({
|
|
78
|
-
getHeaderHeight: this.deps.getHeaderHeight,
|
|
79
|
-
getRowHeight: this.deps.getRowHeight,
|
|
80
|
-
getColumnPositions: () => this.deps.vm.columnPositions(),
|
|
81
|
-
getColumnCount: () => this.deps.vm.visibleColumnWithIndices().length,
|
|
82
|
-
getOriginalColumnIndex: (visibleIndex) =>
|
|
83
|
-
this.deps.vm.visibleColumnWithIndices()[visibleIndex]?.originalIndex ?? visibleIndex,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
observeViewport(container: HTMLElement, bodyEl: HTMLElement): void {
|
|
88
|
-
this.deps.vm.viewportWidth.set(container.clientWidth);
|
|
89
|
-
this.resizeObserver = new ResizeObserver((entries) => {
|
|
90
|
-
const entry = entries[0];
|
|
91
|
-
if (entry) this.deps.vm.viewportWidth.set(entry.contentRect.width);
|
|
92
|
-
});
|
|
93
|
-
this.resizeObserver.observe(container);
|
|
94
|
-
this.coreRef?.setViewport(0, 0, container.clientWidth, bodyEl.clientHeight);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
destroy(): void {
|
|
98
|
-
this.autoScroll.stop();
|
|
99
|
-
this.pendingRowDrag.cancel();
|
|
100
|
-
this.pendingRowDrag.releaseLocks();
|
|
101
|
-
this.unsubscribe?.();
|
|
102
|
-
this.resizeObserver?.disconnect();
|
|
103
|
-
this.coreRef?.destroy();
|
|
104
|
-
this.dataSourceOwner.destroy();
|
|
105
|
-
this.coreRef = null;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
syncHighlighting(opts: HighlightingOptions | null): void {
|
|
109
|
-
const core = this.coreRef;
|
|
110
|
-
if (core?.highlight && opts) {
|
|
111
|
-
core.highlight.updateOptions(opts as HighlightingOptions<TData>);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
syncColumns(cols: ColumnDefinition[]): void {
|
|
116
|
-
const core = this.coreRef;
|
|
117
|
-
if (core === null) return;
|
|
118
|
-
if (this.dataSourceOwner.syncColumns(cols)) core.setColumns(cols);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
syncRows(rows: TData[], dataSource: DataSource<TData> | null): void {
|
|
122
|
-
const core = this.coreRef;
|
|
123
|
-
if (core === null) return;
|
|
124
|
-
const newDs = this.dataSourceOwner.syncRows(rows, dataSource);
|
|
125
|
-
if (newDs !== null) core.setDataSource(newDs);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
applyPendingScroll(): void {
|
|
129
|
-
const top = this.deps.vm.pendingScrollTop();
|
|
130
|
-
const body = this.deps.getBody();
|
|
131
|
-
if (top !== null && body) {
|
|
132
|
-
body.scrollTop = top;
|
|
133
|
-
this.deps.vm.pendingScrollTop.set(null);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
scrollToRow(row: number): void {
|
|
138
|
-
const core = this.coreRef;
|
|
139
|
-
const body = this.deps.getBody();
|
|
140
|
-
if (core === null || body === null) return;
|
|
141
|
-
scrollCellIntoView(
|
|
142
|
-
core,
|
|
143
|
-
body,
|
|
144
|
-
row,
|
|
145
|
-
this.deps.getRowHeight(),
|
|
146
|
-
this.deps.vm.slots(),
|
|
147
|
-
this.deps.vm.rowsWrapperOffset(),
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import { Signal, computed, signal } from '@angular/core';
|
|
2
|
-
import {
|
|
3
|
-
calculateFillHandlePosition,
|
|
4
|
-
calculateScaledColumnPositions,
|
|
5
|
-
getTotalWidth,
|
|
6
|
-
} from '@gp-grid/core';
|
|
7
|
-
import type {
|
|
8
|
-
BatchChangeSetters,
|
|
9
|
-
CellPosition,
|
|
10
|
-
CellRange,
|
|
11
|
-
ColumnDefinition,
|
|
12
|
-
DragState,
|
|
13
|
-
FillHandlePosition,
|
|
14
|
-
FilterPopupState,
|
|
15
|
-
HeaderData,
|
|
16
|
-
SlotData,
|
|
17
|
-
VisibleColumnInfo,
|
|
18
|
-
} from '@gp-grid/core';
|
|
19
|
-
import type { ActiveFilterPopup, EditingCellState } from './components';
|
|
20
|
-
import type { AngularColumnDefinition } from './types';
|
|
21
|
-
|
|
22
|
-
export interface GpGridViewModelDeps {
|
|
23
|
-
getColumns: () => AngularColumnDefinition[];
|
|
24
|
-
getRows: () => unknown[];
|
|
25
|
-
getRowHeight: () => number;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const INITIAL_DRAG_STATE: DragState = {
|
|
29
|
-
isDragging: false,
|
|
30
|
-
dragType: null,
|
|
31
|
-
fillSourceRange: null,
|
|
32
|
-
fillTarget: null,
|
|
33
|
-
columnResize: null,
|
|
34
|
-
columnMove: null,
|
|
35
|
-
rowDrag: null,
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Reactive view-state container for GpGridComponent.
|
|
40
|
-
*
|
|
41
|
-
* Owns every signal and computed the template binds against, plus the
|
|
42
|
-
* batch-change setters bag wired into those signals. The component
|
|
43
|
-
* becomes a thin shell that holds lifecycle, event handlers, and inputs.
|
|
44
|
-
*
|
|
45
|
-
* Angular-specific (uses signal/computed from @angular/core) — lives in
|
|
46
|
-
* the angular package, not core.
|
|
47
|
-
*/
|
|
48
|
-
export class GpGridViewModel {
|
|
49
|
-
readonly headerState = signal<Map<number, HeaderData>>(new Map());
|
|
50
|
-
readonly viewportWidth = signal<number>(0);
|
|
51
|
-
readonly scrollLeft = signal<number>(0);
|
|
52
|
-
readonly isLoading = signal<boolean>(false);
|
|
53
|
-
readonly errorMessage = signal<string | null>(null);
|
|
54
|
-
readonly filterPopup = signal<ActiveFilterPopup | null>(null);
|
|
55
|
-
readonly pendingScrollTop = signal<number | null>(null);
|
|
56
|
-
readonly activeCell = signal<CellPosition | null>(null);
|
|
57
|
-
readonly selectionRange = signal<CellRange | null>(null);
|
|
58
|
-
readonly editingCell = signal<EditingCellState | null>(null);
|
|
59
|
-
readonly hoverPosition = signal<CellPosition | null>(null);
|
|
60
|
-
readonly columnsOverride = signal<ColumnDefinition[] | null>(null);
|
|
61
|
-
readonly dragState = signal<DragState>(INITIAL_DRAG_STATE);
|
|
62
|
-
readonly contentWidth = signal<number>(0);
|
|
63
|
-
readonly contentHeight = signal<number>(0);
|
|
64
|
-
readonly rowsWrapperOffset = signal<number>(0);
|
|
65
|
-
readonly slots = signal<Map<string, SlotData>>(new Map());
|
|
66
|
-
|
|
67
|
-
readonly effectiveColumns: Signal<ColumnDefinition[]>;
|
|
68
|
-
readonly visibleColumnWithIndices: Signal<VisibleColumnInfo[]>;
|
|
69
|
-
readonly columnPositions: Signal<number[]>;
|
|
70
|
-
readonly columnWidths: Signal<number[]>;
|
|
71
|
-
readonly totalWidth: Signal<number>;
|
|
72
|
-
readonly fillHandlePosition: Signal<FillHandlePosition | null>;
|
|
73
|
-
readonly slotsArray: Signal<SlotData[]>;
|
|
74
|
-
readonly totalRows: Signal<number>;
|
|
75
|
-
|
|
76
|
-
readonly batchSetters: BatchChangeSetters;
|
|
77
|
-
|
|
78
|
-
private filterAnchorEl: HTMLElement | null = null;
|
|
79
|
-
|
|
80
|
-
constructor(deps: GpGridViewModelDeps) {
|
|
81
|
-
this.effectiveColumns = computed(() =>
|
|
82
|
-
this.columnsOverride() ?? (deps.getColumns() as unknown as ColumnDefinition[])
|
|
83
|
-
);
|
|
84
|
-
this.visibleColumnWithIndices = computed(() =>
|
|
85
|
-
this.effectiveColumns()
|
|
86
|
-
.map((col, index) => ({ column: col, originalIndex: index }))
|
|
87
|
-
.filter(({ column }) => !column.hidden)
|
|
88
|
-
);
|
|
89
|
-
const columnLayout = computed(() =>
|
|
90
|
-
calculateScaledColumnPositions(
|
|
91
|
-
this.visibleColumnWithIndices().map(v => v.column),
|
|
92
|
-
this.viewportWidth(),
|
|
93
|
-
)
|
|
94
|
-
);
|
|
95
|
-
this.columnPositions = computed(() => columnLayout().positions);
|
|
96
|
-
this.columnWidths = computed(() => columnLayout().widths);
|
|
97
|
-
this.totalWidth = computed(() => getTotalWidth(this.columnPositions()));
|
|
98
|
-
this.fillHandlePosition = computed(() =>
|
|
99
|
-
calculateFillHandlePosition({
|
|
100
|
-
activeCell: this.activeCell(),
|
|
101
|
-
selectionRange: this.selectionRange(),
|
|
102
|
-
slots: this.slots(),
|
|
103
|
-
columns: this.effectiveColumns(),
|
|
104
|
-
visibleColumnsWithIndices: this.visibleColumnWithIndices(),
|
|
105
|
-
columnPositions: this.columnPositions(),
|
|
106
|
-
columnWidths: this.columnWidths(),
|
|
107
|
-
rowHeight: deps.getRowHeight(),
|
|
108
|
-
})
|
|
109
|
-
);
|
|
110
|
-
this.slotsArray = computed(() => [...this.slots().values()]);
|
|
111
|
-
this.totalRows = computed(() => deps.getRows().length);
|
|
112
|
-
|
|
113
|
-
this.batchSetters = {
|
|
114
|
-
setContentWidth: (v) => this.contentWidth.set(v),
|
|
115
|
-
setContentHeight: (v) => this.contentHeight.set(v),
|
|
116
|
-
setRowsWrapperOffset: (v) => this.rowsWrapperOffset.set(v),
|
|
117
|
-
setIsLoading: (v) => this.isLoading.set(v),
|
|
118
|
-
setErrorMessage: (v) => this.errorMessage.set(v),
|
|
119
|
-
setPendingScrollTop: (v) => this.pendingScrollTop.set(v),
|
|
120
|
-
setActiveCell: (v) => this.activeCell.set(v),
|
|
121
|
-
setSelectionRange: (v) => this.selectionRange.set(v),
|
|
122
|
-
setEditingCell: (v) => this.editingCell.set(v),
|
|
123
|
-
setHoverPosition: (v) => this.hoverPosition.set(v),
|
|
124
|
-
setColumnsOverride: (v) => this.columnsOverride.set(v),
|
|
125
|
-
onFilterPopupChange: (v) => this.materializeFilterPopup(v),
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
setFilterAnchor(el: HTMLElement | null): void {
|
|
130
|
-
this.filterAnchorEl = el;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
private materializeFilterPopup(state: FilterPopupState | null): void {
|
|
134
|
-
if (state === null) {
|
|
135
|
-
this.filterPopup.set(null);
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
if (state.isOpen && state.column) {
|
|
139
|
-
this.filterPopup.set({
|
|
140
|
-
colIndex: state.colIndex,
|
|
141
|
-
column: state.column,
|
|
142
|
-
distinctValues: state.distinctValues,
|
|
143
|
-
currentFilter: state.currentFilter,
|
|
144
|
-
anchorEl: this.filterAnchorEl,
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|