@affino/datagrid-vue-app 0.1.1 → 0.1.2
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/dist/DataGrid.d.ts +9 -9
- package/dist/DataGridTableStage.vue.d.ts +112 -0
- package/dist/chunks/useDataGridAppRowModel-CcTL-h8L.js +11251 -0
- package/dist/gantt/DataGridGanttStage.vue.d.ts +21 -0
- package/dist/host/DataGridDefaultRenderer.d.ts +3 -3
- package/dist/host/DataGridRuntimeHost.d.ts +1 -1
- package/dist/index.js +1125 -4
- package/dist/internal.js +37 -14
- package/dist/overlays/DataGridAdvancedFilterPopover.vue.d.ts +62 -0
- package/dist/overlays/DataGridAggregationsPopover.vue.d.ts +62 -0
- package/dist/overlays/DataGridCellComboboxEditor.vue.d.ts +29 -0
- package/dist/overlays/DataGridColumnLayoutPopover.vue.d.ts +51 -0
- package/dist/overlays/DataGridColumnMenu.vue.d.ts +65 -0
- package/dist/overlays/DataGridFilterableCombobox.vue.d.ts +76 -0
- package/dist/stage/DataGridTableStage.vue.d.ts +115 -0
- package/dist/stage/DataGridTableStageCenterPane.vue.d.ts +121 -0
- package/dist/stage/DataGridTableStageFillActionMenu.vue.d.ts +30 -0
- package/dist/stage/DataGridTableStageHeader.vue.d.ts +84 -0
- package/dist/stage/DataGridTableStageOverlayLayer.vue.d.ts +34 -0
- package/dist/stage/DataGridTableStagePinnedPane.vue.d.ts +43 -0
- package/package.json +8 -5
- package/dist/DataGrid.js +0 -565
- package/dist/DataGridDefaultRenderer.js +0 -2
- package/dist/DataGridModuleHost.js +0 -2
- package/dist/DataGridRuntimeHost.js +0 -1
- package/dist/config/dataGridAdvancedFilter.js +0 -21
- package/dist/config/dataGridAggregations.js +0 -21
- package/dist/config/dataGridColumnLayout.js +0 -21
- package/dist/config/dataGridFormulaOptions.js +0 -137
- package/dist/config/dataGridPublicProps.js +0 -58
- package/dist/config/dataGridSavedView.js +0 -73
- package/dist/config/dataGridVirtualization.js +0 -32
- package/dist/dataGridAdvancedFilter.js +0 -1
- package/dist/dataGridAggregations.js +0 -1
- package/dist/dataGridAppContext.js +0 -1
- package/dist/dataGridCellComboboxState.d.ts +0 -10
- package/dist/dataGridCellComboboxState.js +0 -67
- package/dist/dataGridColumnLayout.js +0 -1
- package/dist/dataGridColumnMenu.d.ts +0 -9
- package/dist/dataGridColumnMenu.js +0 -21
- package/dist/dataGridEditability.js +0 -1
- package/dist/dataGridFilterableCombobox.js +0 -1
- package/dist/dataGridFormulaOptions.js +0 -1
- package/dist/dataGridGantt.d.ts +0 -3
- package/dist/dataGridGantt.js +0 -1
- package/dist/dataGridGanttDependencySelection.d.ts +0 -7
- package/dist/dataGridGanttDependencySelection.js +0 -46
- package/dist/dataGridGanttLabel.d.ts +0 -2
- package/dist/dataGridGanttLabel.js +0 -30
- package/dist/dataGridGanttSplit.d.ts +0 -20
- package/dist/dataGridGanttSplit.js +0 -31
- package/dist/dataGridGanttWheel.d.ts +0 -10
- package/dist/dataGridGanttWheel.js +0 -30
- package/dist/dataGridOverlayThemeVars.d.ts +0 -1
- package/dist/dataGridOverlayThemeVars.js +0 -32
- package/dist/dataGridPublicProps.js +0 -1
- package/dist/dataGridTableStage.types.js +0 -1
- package/dist/dataGridTableStageBody.types.js +0 -1
- package/dist/dataGridTableStageContext.js +0 -1
- package/dist/dataGridTheme.js +0 -1
- package/dist/dataGridVirtualization.js +0 -1
- package/dist/ensureDataGridAppStyles.js +0 -1
- package/dist/gantt/dataGridGantt.js +0 -1
- package/dist/gantt/dataGridGanttDependencySelection.js +0 -46
- package/dist/gantt/dataGridGanttLabel.js +0 -30
- package/dist/gantt/dataGridGanttSplit.js +0 -31
- package/dist/gantt/dataGridGanttWheel.js +0 -30
- package/dist/host/DataGridDefaultRenderer.js +0 -1847
- package/dist/host/DataGridModuleHost.js +0 -23
- package/dist/host/DataGridRuntimeHost.js +0 -174
- package/dist/overlays/dataGridCellComboboxState.js +0 -67
- package/dist/overlays/dataGridColumnMenu.js +0 -190
- package/dist/overlays/dataGridContextMenu.js +0 -218
- package/dist/overlays/dataGridFilterableCombobox.js +0 -74
- package/dist/overlays/dataGridOverlayThemeVars.js +0 -32
- package/dist/stage/dataGridTableStage.types.js +0 -1
- package/dist/stage/dataGridTableStageBody.types.js +0 -1
- package/dist/stage/dataGridTableStageContext.js +0 -88
- package/dist/stage/useDataGridTableStageBindings.js +0 -162
- package/dist/stage/useDataGridTableStageCellIo.js +0 -62
- package/dist/stage/useDataGridTableStageColumns.js +0 -124
- package/dist/stage/useDataGridTableStageFillAction.js +0 -28
- package/dist/stage/useDataGridTableStageHistory.js +0 -46
- package/dist/stage/useDataGridTableStageRowSelection.js +0 -107
- package/dist/stage/useDataGridTableStageRuntime.js +0 -526
- package/dist/stage/useDataGridTableStageScrollSync.js +0 -49
- package/dist/stage/useDataGridTableStageViewportKeyboard.js +0 -58
- package/dist/stage/useDataGridTableStageVisualSelection.js +0 -83
- package/dist/theme/dataGridTheme.js +0 -84
- package/dist/theme/ensureDataGridAppStyles.js +0 -2656
- package/dist/useDataGridAppControlledState.js +0 -390
- package/dist/useDataGridAppRowModel.js +0 -85
- package/dist/useDataGridTableStageBindings.js +0 -1
- package/dist/useDataGridTableStageCellIo.d.ts +0 -28
- package/dist/useDataGridTableStageCellIo.js +0 -62
- package/dist/useDataGridTableStageColumns.d.ts +0 -21
- package/dist/useDataGridTableStageColumns.js +0 -122
- package/dist/useDataGridTableStageFillAction.d.ts +0 -19
- package/dist/useDataGridTableStageFillAction.js +0 -28
- package/dist/useDataGridTableStageHistory.d.ts +0 -31
- package/dist/useDataGridTableStageHistory.js +0 -46
- package/dist/useDataGridTableStageRowSelection.d.ts +0 -28
- package/dist/useDataGridTableStageRowSelection.js +0 -103
- package/dist/useDataGridTableStageRuntime.js +0 -1
- package/dist/useDataGridTableStageScrollSync.d.ts +0 -17
- package/dist/useDataGridTableStageScrollSync.js +0 -49
- package/dist/useDataGridTableStageViewportKeyboard.d.ts +0 -20
- package/dist/useDataGridTableStageViewportKeyboard.js +0 -58
- package/dist/useDataGridTableStageVisualSelection.d.ts +0 -24
- package/dist/useDataGridTableStageVisualSelection.js +0 -70
|
@@ -1,1847 +0,0 @@
|
|
|
1
|
-
import { computed, defineComponent, h, nextTick, onBeforeUnmount, ref, watch, } from "vue";
|
|
2
|
-
import { cloneDataGridFilterSnapshot, useDataGridContextMenu, useDataGridAppAdvancedFilterBuilder, useDataGridAppColumnLayoutPanel, } from "@affino/datagrid-vue";
|
|
3
|
-
import { useDataGridContextMenuActionRouter, useDataGridContextMenuAnchor, useDataGridViewportContextMenuRouter, } from "@affino/datagrid-vue/advanced";
|
|
4
|
-
import DataGridAdvancedFilterPopover from "../overlays/DataGridAdvancedFilterPopover.vue";
|
|
5
|
-
import DataGridAggregationsPopover from "../overlays/DataGridAggregationsPopover.vue";
|
|
6
|
-
import DataGridColumnLayoutPopover from "../overlays/DataGridColumnLayoutPopover.vue";
|
|
7
|
-
import DataGridModuleHost, {} from "./DataGridModuleHost";
|
|
8
|
-
import DataGridGanttStage from "../gantt/DataGridGanttStage.vue";
|
|
9
|
-
import DataGridTableStage from "../stage/DataGridTableStage.vue";
|
|
10
|
-
import { resolveDataGridColumnMenuActionOptions, resolveDataGridColumnMenuDisabledItems, resolveDataGridColumnMenuDisabledReasons, resolveDataGridColumnMenuLabels, resolveDataGridColumnMenuItems, } from "../overlays/dataGridColumnMenu";
|
|
11
|
-
import { resolveDataGridCellMenuActionOptions, resolveDataGridCellMenuDisabledItems, resolveDataGridCellMenuDisabledReasons, resolveDataGridCellMenuItems, resolveDataGridRowIndexMenuActionOptions, resolveDataGridRowIndexMenuDisabledItems, resolveDataGridRowIndexMenuDisabledReasons, resolveDataGridRowIndexMenuItems, } from "../overlays/dataGridContextMenu";
|
|
12
|
-
import { normalizeDataGridGanttOptions, } from "../gantt/dataGridGantt";
|
|
13
|
-
import { useDataGridTableStageRuntime } from "../stage/useDataGridTableStageRuntime";
|
|
14
|
-
function normalizeBaseRowHeight(value) {
|
|
15
|
-
if (!Number.isFinite(value)) {
|
|
16
|
-
return 31;
|
|
17
|
-
}
|
|
18
|
-
return Math.max(24, Math.trunc(value));
|
|
19
|
-
}
|
|
20
|
-
function escapeCssAttributeValue(value) {
|
|
21
|
-
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
|
|
22
|
-
return CSS.escape(value);
|
|
23
|
-
}
|
|
24
|
-
return value.replace(/\\/g, "\\\\").replace(/\"/g, '\\"');
|
|
25
|
-
}
|
|
26
|
-
function resolveInitialSortState(sortModel) {
|
|
27
|
-
return (sortModel ?? []).map(entry => ({
|
|
28
|
-
key: entry.key,
|
|
29
|
-
direction: entry.direction,
|
|
30
|
-
}));
|
|
31
|
-
}
|
|
32
|
-
function createEmptyFilterModel() {
|
|
33
|
-
return {
|
|
34
|
-
columnFilters: {},
|
|
35
|
-
advancedFilters: {},
|
|
36
|
-
advancedExpression: null,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function cloneFilterModelState(filterModel) {
|
|
40
|
-
return cloneDataGridFilterSnapshot(filterModel ?? createEmptyFilterModel()) ?? createEmptyFilterModel();
|
|
41
|
-
}
|
|
42
|
-
function normalizeColumnMenuToken(token) {
|
|
43
|
-
return token.startsWith("string:")
|
|
44
|
-
? `string:${token.slice("string:".length).toLowerCase()}`
|
|
45
|
-
: token;
|
|
46
|
-
}
|
|
47
|
-
function pruneFilterModel(filterModel) {
|
|
48
|
-
const columnFilters = {};
|
|
49
|
-
for (const [columnKey, entry] of Object.entries(filterModel.columnFilters ?? {})) {
|
|
50
|
-
if (entry.kind === "valueSet") {
|
|
51
|
-
const tokens = Array.from(new Set((entry.tokens ?? [])
|
|
52
|
-
.map(token => normalizeColumnMenuToken(String(token ?? "")))
|
|
53
|
-
.filter(token => token.length > 0)));
|
|
54
|
-
if (tokens.length === 0) {
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
columnFilters[columnKey] = {
|
|
58
|
-
kind: "valueSet",
|
|
59
|
-
tokens,
|
|
60
|
-
};
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
columnFilters[columnKey] = {
|
|
64
|
-
kind: "predicate",
|
|
65
|
-
operator: entry.operator,
|
|
66
|
-
value: entry.value,
|
|
67
|
-
value2: entry.value2,
|
|
68
|
-
caseSensitive: entry.caseSensitive,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
const advancedFilters = { ...(filterModel.advancedFilters ?? {}) };
|
|
72
|
-
const advancedExpression = filterModel.advancedExpression ?? null;
|
|
73
|
-
if (Object.keys(columnFilters).length === 0
|
|
74
|
-
&& Object.keys(advancedFilters).length === 0
|
|
75
|
-
&& !advancedExpression) {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
return {
|
|
79
|
-
columnFilters,
|
|
80
|
-
advancedFilters,
|
|
81
|
-
advancedExpression,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
function resolveInitialFilterTexts(filterModel) {
|
|
85
|
-
const result = {};
|
|
86
|
-
const columnFilters = filterModel?.columnFilters ?? {};
|
|
87
|
-
for (const [columnKey, filter] of Object.entries(columnFilters)) {
|
|
88
|
-
if (filter?.kind === "predicate" && typeof filter.value === "string") {
|
|
89
|
-
result[columnKey] = filter.value;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return result;
|
|
93
|
-
}
|
|
94
|
-
function cloneRowData(row) {
|
|
95
|
-
if (typeof globalThis.structuredClone === "function") {
|
|
96
|
-
try {
|
|
97
|
-
return globalThis.structuredClone(row);
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
// Fall back to a plain-data clone when rows carry live references such as Window.
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return cloneRowDataFallback(row, new WeakMap());
|
|
104
|
-
}
|
|
105
|
-
function cloneRowDataFallback(row, seen) {
|
|
106
|
-
if (row == null || typeof row !== "object") {
|
|
107
|
-
return row;
|
|
108
|
-
}
|
|
109
|
-
if (row instanceof Date) {
|
|
110
|
-
return new Date(row.getTime());
|
|
111
|
-
}
|
|
112
|
-
if (Array.isArray(row)) {
|
|
113
|
-
if (seen.has(row)) {
|
|
114
|
-
return seen.get(row);
|
|
115
|
-
}
|
|
116
|
-
const cloned = [];
|
|
117
|
-
seen.set(row, cloned);
|
|
118
|
-
for (const value of row) {
|
|
119
|
-
cloned.push(cloneRowDataFallback(value, seen));
|
|
120
|
-
}
|
|
121
|
-
return cloned;
|
|
122
|
-
}
|
|
123
|
-
if (isPlainRowObject(row)) {
|
|
124
|
-
if (seen.has(row)) {
|
|
125
|
-
return seen.get(row);
|
|
126
|
-
}
|
|
127
|
-
const cloned = {};
|
|
128
|
-
seen.set(row, cloned);
|
|
129
|
-
for (const [key, value] of Object.entries(row)) {
|
|
130
|
-
cloned[key] = cloneRowDataFallback(value, seen);
|
|
131
|
-
}
|
|
132
|
-
return cloned;
|
|
133
|
-
}
|
|
134
|
-
if (row && typeof row === "object") {
|
|
135
|
-
return { ...row };
|
|
136
|
-
}
|
|
137
|
-
return row;
|
|
138
|
-
}
|
|
139
|
-
function isPlainRowObject(value) {
|
|
140
|
-
if (!value || typeof value !== "object") {
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
const prototype = Object.getPrototypeOf(value);
|
|
144
|
-
return prototype === Object.prototype || prototype === null;
|
|
145
|
-
}
|
|
146
|
-
function serializeRowClipboardRows(rows) {
|
|
147
|
-
const seen = new WeakSet();
|
|
148
|
-
try {
|
|
149
|
-
return JSON.stringify(rows, (_key, value) => {
|
|
150
|
-
if (value == null || typeof value !== "object") {
|
|
151
|
-
return value;
|
|
152
|
-
}
|
|
153
|
-
if (value instanceof Date) {
|
|
154
|
-
return value.toISOString();
|
|
155
|
-
}
|
|
156
|
-
if (Array.isArray(value) || isPlainRowObject(value)) {
|
|
157
|
-
if (seen.has(value)) {
|
|
158
|
-
return undefined;
|
|
159
|
-
}
|
|
160
|
-
seen.add(value);
|
|
161
|
-
return value;
|
|
162
|
-
}
|
|
163
|
-
return undefined;
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
catch {
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
function cloneAggregationModelState(model) {
|
|
171
|
-
if (!model) {
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
return {
|
|
175
|
-
columns: model.columns.map(column => ({ ...column })),
|
|
176
|
-
basis: model.basis === "source" ? "source" : "filtered",
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
function cloneGroupBySpec(groupBy) {
|
|
180
|
-
if (!groupBy) {
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
183
|
-
return {
|
|
184
|
-
fields: [...groupBy.fields],
|
|
185
|
-
expandedByDefault: groupBy.expandedByDefault,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
function formatFilterDisplayValue(value) {
|
|
189
|
-
if (value == null) {
|
|
190
|
-
return "blank";
|
|
191
|
-
}
|
|
192
|
-
if (value instanceof Date) {
|
|
193
|
-
return value.toISOString();
|
|
194
|
-
}
|
|
195
|
-
if (typeof value === "string") {
|
|
196
|
-
return `"${value}"`;
|
|
197
|
-
}
|
|
198
|
-
return String(value);
|
|
199
|
-
}
|
|
200
|
-
function formatColumnFilterOperator(operator) {
|
|
201
|
-
switch (operator) {
|
|
202
|
-
case "contains":
|
|
203
|
-
return "contains";
|
|
204
|
-
case "startsWith":
|
|
205
|
-
case "starts-with":
|
|
206
|
-
return "starts with";
|
|
207
|
-
case "endsWith":
|
|
208
|
-
case "ends-with":
|
|
209
|
-
return "ends with";
|
|
210
|
-
case "equals":
|
|
211
|
-
return "=";
|
|
212
|
-
case "notEquals":
|
|
213
|
-
case "not-equals":
|
|
214
|
-
return "!=";
|
|
215
|
-
case "gt":
|
|
216
|
-
return ">";
|
|
217
|
-
case "gte":
|
|
218
|
-
return ">=";
|
|
219
|
-
case "lt":
|
|
220
|
-
return "<";
|
|
221
|
-
case "lte":
|
|
222
|
-
return "<=";
|
|
223
|
-
case "between":
|
|
224
|
-
return "between";
|
|
225
|
-
case "isEmpty":
|
|
226
|
-
case "is-empty":
|
|
227
|
-
return "is empty";
|
|
228
|
-
case "notEmpty":
|
|
229
|
-
case "not-empty":
|
|
230
|
-
return "is not empty";
|
|
231
|
-
case "isNull":
|
|
232
|
-
case "is-null":
|
|
233
|
-
return "is null";
|
|
234
|
-
case "notNull":
|
|
235
|
-
case "not-null":
|
|
236
|
-
return "is not null";
|
|
237
|
-
default:
|
|
238
|
-
return operator;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
function decodeColumnFilterToken(token) {
|
|
242
|
-
const normalized = String(token ?? "");
|
|
243
|
-
if (normalized === "null") {
|
|
244
|
-
return "(Blanks)";
|
|
245
|
-
}
|
|
246
|
-
const separatorIndex = normalized.indexOf(":");
|
|
247
|
-
if (separatorIndex < 0) {
|
|
248
|
-
return normalized;
|
|
249
|
-
}
|
|
250
|
-
const kind = normalized.slice(0, separatorIndex);
|
|
251
|
-
const payload = normalized.slice(separatorIndex + 1);
|
|
252
|
-
if (kind === "string"
|
|
253
|
-
|| kind === "number"
|
|
254
|
-
|| kind === "boolean"
|
|
255
|
-
|| kind === "bigint"
|
|
256
|
-
|| kind === "date"
|
|
257
|
-
|| kind === "repr"
|
|
258
|
-
|| kind === "json") {
|
|
259
|
-
return payload;
|
|
260
|
-
}
|
|
261
|
-
return normalized;
|
|
262
|
-
}
|
|
263
|
-
function formatColumnFilterSummary(label, filter) {
|
|
264
|
-
if (filter.kind === "valueSet") {
|
|
265
|
-
if (filter.tokens.length === 1) {
|
|
266
|
-
return `${label}: ${decodeColumnFilterToken(filter.tokens[0] ?? "")}`;
|
|
267
|
-
}
|
|
268
|
-
return `${label}: ${filter.tokens.length} values`;
|
|
269
|
-
}
|
|
270
|
-
if (filter.operator === "between") {
|
|
271
|
-
return `${label} between ${formatFilterDisplayValue(filter.value)} and ${formatFilterDisplayValue(filter.value2)}`;
|
|
272
|
-
}
|
|
273
|
-
if (filter.operator === "isEmpty"
|
|
274
|
-
|| filter.operator === "notEmpty"
|
|
275
|
-
|| filter.operator === "isNull"
|
|
276
|
-
|| filter.operator === "notNull") {
|
|
277
|
-
return `${label} ${formatColumnFilterOperator(filter.operator)}`;
|
|
278
|
-
}
|
|
279
|
-
return `${label} ${formatColumnFilterOperator(filter.operator)} ${formatFilterDisplayValue(filter.value)}`;
|
|
280
|
-
}
|
|
281
|
-
function formatLegacyAdvancedFilterSummary(label, filter) {
|
|
282
|
-
const parts = filter.clauses
|
|
283
|
-
.map((clause, clauseIndex) => {
|
|
284
|
-
const prefix = clauseIndex === 0 ? "" : `${String(clause.join ?? "and").toUpperCase()} `;
|
|
285
|
-
if (clause.operator === "between") {
|
|
286
|
-
return `${prefix}${formatColumnFilterOperator(clause.operator)} ${formatFilterDisplayValue(clause.value)} and ${formatFilterDisplayValue(clause.value2)}`;
|
|
287
|
-
}
|
|
288
|
-
if (clause.operator === "isEmpty"
|
|
289
|
-
|| clause.operator === "notEmpty"
|
|
290
|
-
|| clause.operator === "isNull"
|
|
291
|
-
|| clause.operator === "notNull") {
|
|
292
|
-
return `${prefix}${formatColumnFilterOperator(clause.operator)}`;
|
|
293
|
-
}
|
|
294
|
-
return `${prefix}${formatColumnFilterOperator(clause.operator)} ${formatFilterDisplayValue(clause.value)}`;
|
|
295
|
-
})
|
|
296
|
-
.filter(part => part.length > 0);
|
|
297
|
-
if (parts.length === 0) {
|
|
298
|
-
return `${label}: active`;
|
|
299
|
-
}
|
|
300
|
-
return `${label} ${parts.join(" ")}`;
|
|
301
|
-
}
|
|
302
|
-
function formatAdvancedExpressionSummary(expression, resolveColumnLabel) {
|
|
303
|
-
if (expression.kind === "condition") {
|
|
304
|
-
const label = resolveColumnLabel(expression.key);
|
|
305
|
-
if (expression.operator === "between") {
|
|
306
|
-
return `${label} between ${formatFilterDisplayValue(expression.value)} and ${formatFilterDisplayValue(expression.value2)}`;
|
|
307
|
-
}
|
|
308
|
-
if (expression.operator === "isEmpty"
|
|
309
|
-
|| expression.operator === "notEmpty"
|
|
310
|
-
|| expression.operator === "isNull"
|
|
311
|
-
|| expression.operator === "notNull") {
|
|
312
|
-
return `${label} ${formatColumnFilterOperator(expression.operator)}`;
|
|
313
|
-
}
|
|
314
|
-
return `${label} ${formatColumnFilterOperator(expression.operator)} ${formatFilterDisplayValue(expression.value)}`;
|
|
315
|
-
}
|
|
316
|
-
if (expression.kind === "not") {
|
|
317
|
-
return `NOT (${formatAdvancedExpressionSummary(expression.child, resolveColumnLabel)})`;
|
|
318
|
-
}
|
|
319
|
-
return expression.children
|
|
320
|
-
.map(child => formatAdvancedExpressionSummary(child, resolveColumnLabel))
|
|
321
|
-
.filter(part => part.length > 0)
|
|
322
|
-
.join(` ${expression.operator.toUpperCase()} `);
|
|
323
|
-
}
|
|
324
|
-
const NUMERIC_AGG_OPS = [
|
|
325
|
-
"sum",
|
|
326
|
-
"avg",
|
|
327
|
-
"min",
|
|
328
|
-
"max",
|
|
329
|
-
"count",
|
|
330
|
-
"countNonNull",
|
|
331
|
-
"first",
|
|
332
|
-
"last",
|
|
333
|
-
];
|
|
334
|
-
const ORDERED_AGG_OPS = [
|
|
335
|
-
"min",
|
|
336
|
-
"max",
|
|
337
|
-
"count",
|
|
338
|
-
"countNonNull",
|
|
339
|
-
"first",
|
|
340
|
-
"last",
|
|
341
|
-
];
|
|
342
|
-
const BASIC_AGG_OPS = [
|
|
343
|
-
"count",
|
|
344
|
-
"countNonNull",
|
|
345
|
-
"first",
|
|
346
|
-
"last",
|
|
347
|
-
];
|
|
348
|
-
function resolveAllowedAggregationOps(dataType) {
|
|
349
|
-
if (dataType === "number" || dataType === "currency" || dataType === "percent") {
|
|
350
|
-
return NUMERIC_AGG_OPS;
|
|
351
|
-
}
|
|
352
|
-
if (dataType === "date" || dataType === "datetime") {
|
|
353
|
-
return ORDERED_AGG_OPS;
|
|
354
|
-
}
|
|
355
|
-
return BASIC_AGG_OPS;
|
|
356
|
-
}
|
|
357
|
-
const CELL_MENU_ACTION_IDS_BY_ITEM = {
|
|
358
|
-
clipboard: ["cut", "copy", "paste"],
|
|
359
|
-
edit: ["clear"],
|
|
360
|
-
};
|
|
361
|
-
const ROW_INDEX_MENU_ACTION_IDS_BY_ITEM = {
|
|
362
|
-
insert: ["insert-row-above", "insert-row-below"],
|
|
363
|
-
clipboard: ["cut-row", "copy-row", "paste-row"],
|
|
364
|
-
selection: ["delete-selected-rows"],
|
|
365
|
-
};
|
|
366
|
-
const DEFAULT_CONTEXT_MENU_ACTION_LABELS = {
|
|
367
|
-
cut: "Cut",
|
|
368
|
-
copy: "Copy",
|
|
369
|
-
paste: "Paste",
|
|
370
|
-
clear: "Clear values",
|
|
371
|
-
"insert-row-above": "Insert above",
|
|
372
|
-
"insert-row-below": "Insert below",
|
|
373
|
-
"copy-row": "Copy row",
|
|
374
|
-
"paste-row": "Paste row",
|
|
375
|
-
"cut-row": "Cut row",
|
|
376
|
-
"delete-selected-rows": "Delete selected rows",
|
|
377
|
-
"sort-asc": "Sort ascending",
|
|
378
|
-
"sort-desc": "Sort descending",
|
|
379
|
-
"sort-clear": "Clear sort",
|
|
380
|
-
filter: "Filter column",
|
|
381
|
-
"auto-size": "Auto size column",
|
|
382
|
-
};
|
|
383
|
-
const ROW_INDEX_MENU_SHORTCUT_HINTS = {
|
|
384
|
-
"insert-row-above": "Insert / Ctrl/Cmd+I",
|
|
385
|
-
"copy-row": "Ctrl/Cmd+C",
|
|
386
|
-
"paste-row": "Ctrl/Cmd+V",
|
|
387
|
-
"cut-row": "Ctrl/Cmd+X",
|
|
388
|
-
};
|
|
389
|
-
export default defineComponent({
|
|
390
|
-
name: "DataGridDefaultRenderer",
|
|
391
|
-
props: {
|
|
392
|
-
mode: {
|
|
393
|
-
type: String,
|
|
394
|
-
required: true,
|
|
395
|
-
},
|
|
396
|
-
rows: {
|
|
397
|
-
type: Array,
|
|
398
|
-
default: () => [],
|
|
399
|
-
},
|
|
400
|
-
runtime: {
|
|
401
|
-
type: Object,
|
|
402
|
-
required: true,
|
|
403
|
-
},
|
|
404
|
-
runtimeRowModel: {
|
|
405
|
-
type: Object,
|
|
406
|
-
required: true,
|
|
407
|
-
},
|
|
408
|
-
selectionSnapshot: {
|
|
409
|
-
type: Object,
|
|
410
|
-
required: true,
|
|
411
|
-
},
|
|
412
|
-
selectionAnchor: {
|
|
413
|
-
type: Object,
|
|
414
|
-
required: true,
|
|
415
|
-
},
|
|
416
|
-
rowSelectionSnapshot: {
|
|
417
|
-
type: Object,
|
|
418
|
-
required: true,
|
|
419
|
-
},
|
|
420
|
-
syncSelectionSnapshotFromRuntime: {
|
|
421
|
-
type: Function,
|
|
422
|
-
required: true,
|
|
423
|
-
},
|
|
424
|
-
syncRowSelectionSnapshotFromRuntime: {
|
|
425
|
-
type: Function,
|
|
426
|
-
default: () => undefined,
|
|
427
|
-
},
|
|
428
|
-
sortModel: {
|
|
429
|
-
type: Array,
|
|
430
|
-
default: undefined,
|
|
431
|
-
},
|
|
432
|
-
filterModel: {
|
|
433
|
-
type: Object,
|
|
434
|
-
default: undefined,
|
|
435
|
-
},
|
|
436
|
-
groupBy: {
|
|
437
|
-
type: Object,
|
|
438
|
-
default: undefined,
|
|
439
|
-
},
|
|
440
|
-
pivotModel: {
|
|
441
|
-
type: Object,
|
|
442
|
-
default: undefined,
|
|
443
|
-
},
|
|
444
|
-
renderMode: {
|
|
445
|
-
type: String,
|
|
446
|
-
default: "virtualization",
|
|
447
|
-
},
|
|
448
|
-
virtualization: {
|
|
449
|
-
type: Object,
|
|
450
|
-
required: true,
|
|
451
|
-
},
|
|
452
|
-
columnMenu: {
|
|
453
|
-
type: Object,
|
|
454
|
-
required: true,
|
|
455
|
-
},
|
|
456
|
-
cellMenu: {
|
|
457
|
-
type: Object,
|
|
458
|
-
required: true,
|
|
459
|
-
},
|
|
460
|
-
rowIndexMenu: {
|
|
461
|
-
type: Object,
|
|
462
|
-
required: true,
|
|
463
|
-
},
|
|
464
|
-
columnLayout: {
|
|
465
|
-
type: Object,
|
|
466
|
-
required: true,
|
|
467
|
-
},
|
|
468
|
-
aggregations: {
|
|
469
|
-
type: Object,
|
|
470
|
-
required: true,
|
|
471
|
-
},
|
|
472
|
-
advancedFilter: {
|
|
473
|
-
type: Object,
|
|
474
|
-
required: true,
|
|
475
|
-
},
|
|
476
|
-
rowHeightMode: {
|
|
477
|
-
type: String,
|
|
478
|
-
default: "fixed",
|
|
479
|
-
},
|
|
480
|
-
baseRowHeight: {
|
|
481
|
-
type: Number,
|
|
482
|
-
default: 31,
|
|
483
|
-
},
|
|
484
|
-
rowHover: {
|
|
485
|
-
type: Boolean,
|
|
486
|
-
default: false,
|
|
487
|
-
},
|
|
488
|
-
stripedRows: {
|
|
489
|
-
type: Boolean,
|
|
490
|
-
default: false,
|
|
491
|
-
},
|
|
492
|
-
showRowIndex: {
|
|
493
|
-
type: Boolean,
|
|
494
|
-
default: true,
|
|
495
|
-
},
|
|
496
|
-
rowSelection: {
|
|
497
|
-
type: Boolean,
|
|
498
|
-
default: true,
|
|
499
|
-
},
|
|
500
|
-
isCellEditable: {
|
|
501
|
-
type: Function,
|
|
502
|
-
default: undefined,
|
|
503
|
-
},
|
|
504
|
-
viewMode: {
|
|
505
|
-
type: String,
|
|
506
|
-
default: "table",
|
|
507
|
-
},
|
|
508
|
-
gantt: {
|
|
509
|
-
type: [Boolean, Object],
|
|
510
|
-
default: undefined,
|
|
511
|
-
},
|
|
512
|
-
toolbarModules: {
|
|
513
|
-
type: Array,
|
|
514
|
-
default: () => [],
|
|
515
|
-
},
|
|
516
|
-
inspectorPanel: {
|
|
517
|
-
type: Object,
|
|
518
|
-
default: null,
|
|
519
|
-
},
|
|
520
|
-
},
|
|
521
|
-
setup(props) {
|
|
522
|
-
const rowVersion = ref(0);
|
|
523
|
-
const sortState = ref(resolveInitialSortState(props.sortModel));
|
|
524
|
-
const filterModelState = ref(cloneFilterModelState(props.filterModel));
|
|
525
|
-
const columnFilterTextByKey = computed(() => (resolveInitialFilterTexts(filterModelState.value)));
|
|
526
|
-
let lastRowModelVersionKey = "";
|
|
527
|
-
const resolveRowModelVersionKey = () => {
|
|
528
|
-
const snapshot = props.runtimeRowModel.getSnapshot();
|
|
529
|
-
return [
|
|
530
|
-
snapshot.kind,
|
|
531
|
-
snapshot.revision ?? "",
|
|
532
|
-
snapshot.rowCount,
|
|
533
|
-
snapshot.loading ? 1 : 0,
|
|
534
|
-
snapshot.projection?.recomputeVersion ?? snapshot.projection?.version ?? "",
|
|
535
|
-
].join("|");
|
|
536
|
-
};
|
|
537
|
-
lastRowModelVersionKey = resolveRowModelVersionKey();
|
|
538
|
-
const unsubscribeRowModel = props.runtimeRowModel.subscribe(() => {
|
|
539
|
-
const nextVersionKey = resolveRowModelVersionKey();
|
|
540
|
-
if (nextVersionKey === lastRowModelVersionKey) {
|
|
541
|
-
return;
|
|
542
|
-
}
|
|
543
|
-
lastRowModelVersionKey = nextVersionKey;
|
|
544
|
-
rowVersion.value += 1;
|
|
545
|
-
});
|
|
546
|
-
onBeforeUnmount(() => {
|
|
547
|
-
unsubscribeRowModel();
|
|
548
|
-
});
|
|
549
|
-
watch(() => props.sortModel, nextSortModel => {
|
|
550
|
-
sortState.value = resolveInitialSortState(nextSortModel);
|
|
551
|
-
}, { deep: true });
|
|
552
|
-
watch(() => props.filterModel, nextFilterModel => {
|
|
553
|
-
filterModelState.value = cloneFilterModelState(nextFilterModel);
|
|
554
|
-
}, { deep: true });
|
|
555
|
-
const modeRef = computed(() => props.mode);
|
|
556
|
-
const rowsRef = computed(() => props.rows);
|
|
557
|
-
const totalRuntimeRows = computed(() => {
|
|
558
|
-
void rowVersion.value;
|
|
559
|
-
return props.runtime.api.rows.getSnapshot().rowCount;
|
|
560
|
-
});
|
|
561
|
-
const visibleColumns = computed(() => props.runtime.columnSnapshot.value.visibleColumns);
|
|
562
|
-
const allColumns = computed(() => props.runtime.columnSnapshot.value.columns ?? []);
|
|
563
|
-
const advancedFilterColumns = computed(() => {
|
|
564
|
-
return visibleColumns.value
|
|
565
|
-
.filter(column => column.visible !== false)
|
|
566
|
-
.map(column => ({
|
|
567
|
-
key: column.key,
|
|
568
|
-
label: column.column.label ?? column.key,
|
|
569
|
-
}));
|
|
570
|
-
});
|
|
571
|
-
const columnLayoutDraftColumns = computed(() => {
|
|
572
|
-
return allColumns.value.map(column => ({
|
|
573
|
-
key: column.key,
|
|
574
|
-
label: column.column.label ?? column.key,
|
|
575
|
-
visible: column.visible !== false,
|
|
576
|
-
}));
|
|
577
|
-
});
|
|
578
|
-
const aggregatableColumns = computed(() => {
|
|
579
|
-
return allColumns.value
|
|
580
|
-
.filter(column => column.column.capabilities?.aggregatable === true)
|
|
581
|
-
.map(column => ({
|
|
582
|
-
key: column.key,
|
|
583
|
-
label: column.column.label ?? column.key,
|
|
584
|
-
allowedOps: resolveAllowedAggregationOps(column.column.dataType),
|
|
585
|
-
}));
|
|
586
|
-
});
|
|
587
|
-
const rowHeightMode = ref(props.rowHeightMode);
|
|
588
|
-
const normalizedBaseRowHeight = computed(() => normalizeBaseRowHeight(props.baseRowHeight));
|
|
589
|
-
const resolvedGanttOptions = computed(() => normalizeDataGridGanttOptions(props.gantt));
|
|
590
|
-
const firstColumnKey = computed(() => visibleColumns.value[0]?.key ?? "name");
|
|
591
|
-
const columnLabelByKey = computed(() => {
|
|
592
|
-
const map = new Map();
|
|
593
|
-
for (const column of allColumns.value) {
|
|
594
|
-
map.set(column.key, column.column.label ?? column.key);
|
|
595
|
-
}
|
|
596
|
-
return map;
|
|
597
|
-
});
|
|
598
|
-
const { isColumnLayoutPanelOpen, columnLayoutPanelItems, openColumnLayoutPanel, cancelColumnLayoutPanel, applyColumnLayoutPanel, moveColumnUp, moveColumnDown, updateColumnVisibility, } = useDataGridAppColumnLayoutPanel({
|
|
599
|
-
resolveColumns: () => columnLayoutDraftColumns.value,
|
|
600
|
-
applyColumnLayout: payload => {
|
|
601
|
-
props.runtime.api.columns.setOrder(payload.order);
|
|
602
|
-
for (const [columnKey, visible] of Object.entries(payload.visibilityByKey)) {
|
|
603
|
-
props.runtime.api.columns.setVisibility(columnKey, visible);
|
|
604
|
-
}
|
|
605
|
-
},
|
|
606
|
-
});
|
|
607
|
-
const { isAdvancedFilterPanelOpen, advancedFilterDraftClauses, appliedAdvancedFilterExpression, openAdvancedFilterPanel, addAdvancedFilterClause, removeAdvancedFilterClause, updateAdvancedFilterClause, cancelAdvancedFilterPanel, applyAdvancedFilterPanel, clearAdvancedFilterPanel, } = useDataGridAppAdvancedFilterBuilder({
|
|
608
|
-
resolveColumns: () => advancedFilterColumns.value,
|
|
609
|
-
});
|
|
610
|
-
const isAggregationsPanelOpen = ref(false);
|
|
611
|
-
const aggregationDraftModel = ref(null);
|
|
612
|
-
const aggregationGroupingEnabled = computed(() => Boolean(props.groupBy?.fields?.length));
|
|
613
|
-
const currentAggregationModel = computed(() => {
|
|
614
|
-
void rowVersion.value;
|
|
615
|
-
return cloneAggregationModelState(props.runtime.api.rows.getAggregationModel());
|
|
616
|
-
});
|
|
617
|
-
const currentGroupBy = computed(() => {
|
|
618
|
-
void rowVersion.value;
|
|
619
|
-
return cloneGroupBySpec(props.runtime.api.rows.getSnapshot().groupBy);
|
|
620
|
-
});
|
|
621
|
-
const aggregationPanelItems = computed(() => {
|
|
622
|
-
const enabledByKey = new Map((aggregationDraftModel.value?.columns ?? []).map(column => [column.key, column.op]));
|
|
623
|
-
return aggregatableColumns.value.map(column => {
|
|
624
|
-
const firstAllowedOp = column.allowedOps[0] ?? "count";
|
|
625
|
-
const currentOp = enabledByKey.get(column.key);
|
|
626
|
-
const normalizedOp = currentOp && column.allowedOps.includes(currentOp) ? currentOp : firstAllowedOp;
|
|
627
|
-
return {
|
|
628
|
-
key: column.key,
|
|
629
|
-
label: column.label,
|
|
630
|
-
enabled: enabledByKey.has(column.key),
|
|
631
|
-
op: normalizedOp,
|
|
632
|
-
allowedOps: column.allowedOps,
|
|
633
|
-
};
|
|
634
|
-
});
|
|
635
|
-
});
|
|
636
|
-
const aggregationBasis = computed(() => {
|
|
637
|
-
return aggregationDraftModel.value?.basis === "source" ? "source" : "filtered";
|
|
638
|
-
});
|
|
639
|
-
watch(currentAggregationModel, nextModel => {
|
|
640
|
-
if (isAggregationsPanelOpen.value) {
|
|
641
|
-
return;
|
|
642
|
-
}
|
|
643
|
-
aggregationDraftModel.value = cloneAggregationModelState(nextModel);
|
|
644
|
-
}, { immediate: true, deep: true });
|
|
645
|
-
watch(() => props.rowHeightMode, nextMode => {
|
|
646
|
-
rowHeightMode.value = nextMode;
|
|
647
|
-
});
|
|
648
|
-
const isColumnFilterActive = (columnKey) => {
|
|
649
|
-
const entry = filterModelState.value.columnFilters?.[columnKey];
|
|
650
|
-
if (!entry) {
|
|
651
|
-
return false;
|
|
652
|
-
}
|
|
653
|
-
return entry.kind === "valueSet"
|
|
654
|
-
? entry.tokens.length > 0
|
|
655
|
-
: true;
|
|
656
|
-
};
|
|
657
|
-
const resolveCurrentValueFilterTokens = (columnKey) => {
|
|
658
|
-
const entry = filterModelState.value.columnFilters?.[columnKey];
|
|
659
|
-
if (!entry || entry.kind !== "valueSet") {
|
|
660
|
-
return [];
|
|
661
|
-
}
|
|
662
|
-
return entry.tokens.map(token => normalizeColumnMenuToken(String(token ?? "")));
|
|
663
|
-
};
|
|
664
|
-
const resolveColumnMenuItems = (columnKey) => {
|
|
665
|
-
return resolveDataGridColumnMenuItems(props.columnMenu, columnKey);
|
|
666
|
-
};
|
|
667
|
-
const resolveColumnMenuDisabledItems = (columnKey) => {
|
|
668
|
-
return resolveDataGridColumnMenuDisabledItems(props.columnMenu, columnKey);
|
|
669
|
-
};
|
|
670
|
-
const resolveColumnMenuDisabledReasons = (columnKey) => {
|
|
671
|
-
return resolveDataGridColumnMenuDisabledReasons(props.columnMenu, columnKey);
|
|
672
|
-
};
|
|
673
|
-
const resolveColumnMenuLabels = (columnKey) => {
|
|
674
|
-
return resolveDataGridColumnMenuLabels(props.columnMenu, columnKey);
|
|
675
|
-
};
|
|
676
|
-
const resolveColumnMenuActionOptions = (columnKey) => {
|
|
677
|
-
return resolveDataGridColumnMenuActionOptions(props.columnMenu, columnKey);
|
|
678
|
-
};
|
|
679
|
-
const resolveColumnGroupOrder = (columnKey) => {
|
|
680
|
-
const index = currentGroupBy.value?.fields.findIndex(field => field === columnKey) ?? -1;
|
|
681
|
-
return index >= 0 ? index : null;
|
|
682
|
-
};
|
|
683
|
-
const isColumnGrouped = (columnKey) => {
|
|
684
|
-
return resolveColumnGroupOrder(columnKey) !== null;
|
|
685
|
-
};
|
|
686
|
-
const applySortAndFilter = () => {
|
|
687
|
-
const nextSortModel = sortState.value.map(entry => ({
|
|
688
|
-
key: entry.key,
|
|
689
|
-
direction: entry.direction,
|
|
690
|
-
}));
|
|
691
|
-
const advancedExpression = props.advancedFilter.enabled
|
|
692
|
-
? appliedAdvancedFilterExpression.value
|
|
693
|
-
: (filterModelState.value.advancedExpression ?? null);
|
|
694
|
-
props.runtime.api.rows.setSortAndFilterModel({
|
|
695
|
-
sortModel: nextSortModel,
|
|
696
|
-
filterModel: pruneFilterModel({
|
|
697
|
-
...filterModelState.value,
|
|
698
|
-
advancedExpression,
|
|
699
|
-
}),
|
|
700
|
-
});
|
|
701
|
-
};
|
|
702
|
-
const effectiveAdvancedExpression = computed(() => {
|
|
703
|
-
if (props.advancedFilter.enabled) {
|
|
704
|
-
return appliedAdvancedFilterExpression.value ?? filterModelState.value.advancedExpression ?? null;
|
|
705
|
-
}
|
|
706
|
-
return filterModelState.value.advancedExpression ?? null;
|
|
707
|
-
});
|
|
708
|
-
const activeFilterSummaryItems = computed(() => {
|
|
709
|
-
const resolveColumnLabel = (columnKey) => columnLabelByKey.value.get(columnKey) ?? columnKey;
|
|
710
|
-
const items = [];
|
|
711
|
-
for (const [columnKey, entry] of Object.entries(filterModelState.value.columnFilters ?? {})) {
|
|
712
|
-
if (!entry) {
|
|
713
|
-
continue;
|
|
714
|
-
}
|
|
715
|
-
items.push(formatColumnFilterSummary(resolveColumnLabel(columnKey), entry));
|
|
716
|
-
}
|
|
717
|
-
for (const [columnKey, entry] of Object.entries(filterModelState.value.advancedFilters ?? {})) {
|
|
718
|
-
if (!entry) {
|
|
719
|
-
continue;
|
|
720
|
-
}
|
|
721
|
-
items.push(`Advanced: ${formatLegacyAdvancedFilterSummary(resolveColumnLabel(columnKey), entry)}`);
|
|
722
|
-
}
|
|
723
|
-
if (effectiveAdvancedExpression.value) {
|
|
724
|
-
items.push(`Advanced: ${formatAdvancedExpressionSummary(effectiveAdvancedExpression.value, resolveColumnLabel)}`);
|
|
725
|
-
}
|
|
726
|
-
return Object.freeze(items);
|
|
727
|
-
});
|
|
728
|
-
const hasActiveFilters = computed(() => activeFilterSummaryItems.value.length > 0);
|
|
729
|
-
const resetAllFilters = () => {
|
|
730
|
-
filterModelState.value = createEmptyFilterModel();
|
|
731
|
-
clearAdvancedFilterPanel();
|
|
732
|
-
applySortAndFilter();
|
|
733
|
-
};
|
|
734
|
-
watch(appliedAdvancedFilterExpression, () => {
|
|
735
|
-
if (!props.advancedFilter.enabled) {
|
|
736
|
-
return;
|
|
737
|
-
}
|
|
738
|
-
applySortAndFilter();
|
|
739
|
-
}, { deep: true });
|
|
740
|
-
const toggleSortForColumn = (columnKey, additive = false) => {
|
|
741
|
-
const currentIndex = sortState.value.findIndex(entry => entry.key === columnKey);
|
|
742
|
-
const current = currentIndex >= 0 ? sortState.value[currentIndex] : null;
|
|
743
|
-
if (!current) {
|
|
744
|
-
const nextEntry = { key: columnKey, direction: "asc" };
|
|
745
|
-
sortState.value = additive ? [...sortState.value, nextEntry] : [nextEntry];
|
|
746
|
-
applySortAndFilter();
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
|
-
if (current.direction === "asc") {
|
|
750
|
-
const nextEntry = { key: columnKey, direction: "desc" };
|
|
751
|
-
if (additive) {
|
|
752
|
-
sortState.value = sortState.value.map(entry => (entry.key === columnKey ? nextEntry : entry));
|
|
753
|
-
}
|
|
754
|
-
else {
|
|
755
|
-
sortState.value = [nextEntry];
|
|
756
|
-
}
|
|
757
|
-
applySortAndFilter();
|
|
758
|
-
return;
|
|
759
|
-
}
|
|
760
|
-
sortState.value = additive
|
|
761
|
-
? sortState.value.filter(entry => entry.key !== columnKey)
|
|
762
|
-
: [];
|
|
763
|
-
applySortAndFilter();
|
|
764
|
-
};
|
|
765
|
-
const sortIndicator = (columnKey) => {
|
|
766
|
-
const currentIndex = sortState.value.findIndex(entry => entry.key === columnKey);
|
|
767
|
-
if (currentIndex < 0) {
|
|
768
|
-
return "";
|
|
769
|
-
}
|
|
770
|
-
const current = sortState.value[currentIndex];
|
|
771
|
-
if (!current) {
|
|
772
|
-
return "";
|
|
773
|
-
}
|
|
774
|
-
const direction = current.direction === "asc" ? "↑" : "↓";
|
|
775
|
-
return sortState.value.length > 1 ? `${direction}${currentIndex + 1}` : direction;
|
|
776
|
-
};
|
|
777
|
-
const setColumnFilterText = (columnKey, value) => {
|
|
778
|
-
const nextFilterModel = cloneFilterModelState(filterModelState.value);
|
|
779
|
-
const normalizedValue = value.trim();
|
|
780
|
-
if (!normalizedValue) {
|
|
781
|
-
delete nextFilterModel.columnFilters[columnKey];
|
|
782
|
-
}
|
|
783
|
-
else {
|
|
784
|
-
nextFilterModel.columnFilters[columnKey] = {
|
|
785
|
-
kind: "predicate",
|
|
786
|
-
operator: "contains",
|
|
787
|
-
value: normalizedValue,
|
|
788
|
-
caseSensitive: false,
|
|
789
|
-
};
|
|
790
|
-
}
|
|
791
|
-
filterModelState.value = nextFilterModel;
|
|
792
|
-
applySortAndFilter();
|
|
793
|
-
};
|
|
794
|
-
const applyRowHeightSettings = () => {
|
|
795
|
-
props.runtime.api.view.setRowHeightMode(rowHeightMode.value);
|
|
796
|
-
props.runtime.api.view.setBaseRowHeight(normalizedBaseRowHeight.value);
|
|
797
|
-
};
|
|
798
|
-
const openAggregationsPanel = () => {
|
|
799
|
-
aggregationDraftModel.value = cloneAggregationModelState(currentAggregationModel.value);
|
|
800
|
-
isAggregationsPanelOpen.value = true;
|
|
801
|
-
};
|
|
802
|
-
const cancelAggregationsPanel = () => {
|
|
803
|
-
aggregationDraftModel.value = cloneAggregationModelState(currentAggregationModel.value);
|
|
804
|
-
isAggregationsPanelOpen.value = false;
|
|
805
|
-
};
|
|
806
|
-
watch(aggregationGroupingEnabled, enabled => {
|
|
807
|
-
if (enabled) {
|
|
808
|
-
return;
|
|
809
|
-
}
|
|
810
|
-
cancelAggregationsPanel();
|
|
811
|
-
}, { immediate: true });
|
|
812
|
-
const clearAggregationsPanel = () => {
|
|
813
|
-
aggregationDraftModel.value = null;
|
|
814
|
-
};
|
|
815
|
-
const updateAggregationBasis = (basis) => {
|
|
816
|
-
aggregationDraftModel.value = {
|
|
817
|
-
basis,
|
|
818
|
-
columns: aggregationDraftModel.value?.columns ?? [],
|
|
819
|
-
};
|
|
820
|
-
};
|
|
821
|
-
const toggleAggregationColumn = (key, enabled) => {
|
|
822
|
-
const column = aggregatableColumns.value.find(candidate => candidate.key === key);
|
|
823
|
-
if (!column) {
|
|
824
|
-
return;
|
|
825
|
-
}
|
|
826
|
-
const existingColumns = aggregationDraftModel.value?.columns ?? [];
|
|
827
|
-
if (!enabled) {
|
|
828
|
-
const nextColumns = existingColumns.filter(candidate => candidate.key !== key);
|
|
829
|
-
aggregationDraftModel.value = nextColumns.length > 0
|
|
830
|
-
? { basis: aggregationBasis.value, columns: nextColumns }
|
|
831
|
-
: null;
|
|
832
|
-
return;
|
|
833
|
-
}
|
|
834
|
-
const existing = existingColumns.find(candidate => candidate.key === key);
|
|
835
|
-
const nextColumn = existing ?? {
|
|
836
|
-
key,
|
|
837
|
-
op: column.allowedOps[0] ?? "count",
|
|
838
|
-
};
|
|
839
|
-
aggregationDraftModel.value = {
|
|
840
|
-
basis: aggregationBasis.value,
|
|
841
|
-
columns: [
|
|
842
|
-
...existingColumns.filter(candidate => candidate.key !== key),
|
|
843
|
-
nextColumn,
|
|
844
|
-
],
|
|
845
|
-
};
|
|
846
|
-
};
|
|
847
|
-
const updateAggregationOp = (key, op) => {
|
|
848
|
-
const column = aggregatableColumns.value.find(candidate => candidate.key === key);
|
|
849
|
-
if (!column || !column.allowedOps.includes(op)) {
|
|
850
|
-
return;
|
|
851
|
-
}
|
|
852
|
-
const nextColumns = (aggregationDraftModel.value?.columns ?? []).map(candidate => {
|
|
853
|
-
if (candidate.key !== key) {
|
|
854
|
-
return candidate;
|
|
855
|
-
}
|
|
856
|
-
return {
|
|
857
|
-
...candidate,
|
|
858
|
-
op: op,
|
|
859
|
-
};
|
|
860
|
-
});
|
|
861
|
-
aggregationDraftModel.value = {
|
|
862
|
-
basis: aggregationBasis.value,
|
|
863
|
-
columns: nextColumns,
|
|
864
|
-
};
|
|
865
|
-
};
|
|
866
|
-
const applyAggregationsPanel = () => {
|
|
867
|
-
const nextModel = aggregationDraftModel.value && aggregationDraftModel.value.columns.length > 0
|
|
868
|
-
? aggregationDraftModel.value
|
|
869
|
-
: null;
|
|
870
|
-
props.runtime.api.rows.setAggregationModel(nextModel);
|
|
871
|
-
aggregationDraftModel.value = cloneAggregationModelState(nextModel);
|
|
872
|
-
isAggregationsPanelOpen.value = false;
|
|
873
|
-
};
|
|
874
|
-
const resolveColumnMenuSortDirection = (columnKey) => {
|
|
875
|
-
return sortState.value.find(entry => entry.key === columnKey)?.direction ?? null;
|
|
876
|
-
};
|
|
877
|
-
const applyColumnMenuSort = (columnKey, direction) => {
|
|
878
|
-
const targetColumn = visibleColumns.value.find(column => column.key === columnKey);
|
|
879
|
-
if (!targetColumn || targetColumn.column.capabilities?.sortable === false) {
|
|
880
|
-
return;
|
|
881
|
-
}
|
|
882
|
-
sortState.value = direction === null
|
|
883
|
-
? sortState.value.filter(entry => entry.key !== columnKey)
|
|
884
|
-
: [{ key: columnKey, direction }];
|
|
885
|
-
applySortAndFilter();
|
|
886
|
-
};
|
|
887
|
-
const applyColumnMenuPin = (columnKey, pin) => {
|
|
888
|
-
props.runtime.api.columns.setPin(columnKey, pin);
|
|
889
|
-
};
|
|
890
|
-
const applyColumnMenuGroupBy = (columnKey, grouped) => {
|
|
891
|
-
const current = currentGroupBy.value;
|
|
892
|
-
const nextFields = grouped
|
|
893
|
-
? Array.from(new Set([...(current?.fields ?? []), columnKey]))
|
|
894
|
-
: (current?.fields ?? []).filter(field => field !== columnKey);
|
|
895
|
-
props.runtime.api.rows.setGroupBy(nextFields.length > 0
|
|
896
|
-
? {
|
|
897
|
-
fields: nextFields,
|
|
898
|
-
expandedByDefault: current?.expandedByDefault ?? true,
|
|
899
|
-
}
|
|
900
|
-
: null);
|
|
901
|
-
};
|
|
902
|
-
const applyColumnMenuFilter = (columnKey, tokens) => {
|
|
903
|
-
const normalizedTokens = Array.from(new Set(tokens
|
|
904
|
-
.map(token => normalizeColumnMenuToken(String(token ?? "")))
|
|
905
|
-
.filter(token => token.length > 0)));
|
|
906
|
-
if (normalizedTokens.length === 0) {
|
|
907
|
-
clearColumnMenuFilter(columnKey);
|
|
908
|
-
return;
|
|
909
|
-
}
|
|
910
|
-
const nextFilterModel = cloneFilterModelState(filterModelState.value);
|
|
911
|
-
nextFilterModel.columnFilters[columnKey] = {
|
|
912
|
-
kind: "valueSet",
|
|
913
|
-
tokens: normalizedTokens,
|
|
914
|
-
};
|
|
915
|
-
filterModelState.value = nextFilterModel;
|
|
916
|
-
applySortAndFilter();
|
|
917
|
-
};
|
|
918
|
-
const clearColumnMenuFilter = (columnKey) => {
|
|
919
|
-
const nextFilterModel = cloneFilterModelState(filterModelState.value);
|
|
920
|
-
delete nextFilterModel.columnFilters[columnKey];
|
|
921
|
-
filterModelState.value = nextFilterModel;
|
|
922
|
-
applySortAndFilter();
|
|
923
|
-
};
|
|
924
|
-
const stageHostRef = ref(null);
|
|
925
|
-
const rowClipboardBuffer = ref(null);
|
|
926
|
-
let generatedRowIdentitySequence = 0;
|
|
927
|
-
const resolveCurrentSelectionRange = () => {
|
|
928
|
-
const snapshot = props.selectionSnapshot.value;
|
|
929
|
-
const range = snapshot?.ranges[snapshot.activeRangeIndex] ?? null;
|
|
930
|
-
if (!range) {
|
|
931
|
-
return null;
|
|
932
|
-
}
|
|
933
|
-
return {
|
|
934
|
-
startRow: range.startRow,
|
|
935
|
-
endRow: range.endRow,
|
|
936
|
-
startColumn: range.startCol,
|
|
937
|
-
endColumn: range.endCol,
|
|
938
|
-
};
|
|
939
|
-
};
|
|
940
|
-
const resolveCurrentCellCoord = () => {
|
|
941
|
-
const activeCell = props.selectionSnapshot.value?.activeCell;
|
|
942
|
-
if (!activeCell) {
|
|
943
|
-
return null;
|
|
944
|
-
}
|
|
945
|
-
return {
|
|
946
|
-
rowIndex: activeCell.rowIndex,
|
|
947
|
-
columnIndex: activeCell.colIndex,
|
|
948
|
-
};
|
|
949
|
-
};
|
|
950
|
-
const isMultiCellSelection = (range) => {
|
|
951
|
-
if (!range) {
|
|
952
|
-
return false;
|
|
953
|
-
}
|
|
954
|
-
return range.startRow !== range.endRow || range.startColumn !== range.endColumn;
|
|
955
|
-
};
|
|
956
|
-
const isCoordInsideRange = (coord, range) => {
|
|
957
|
-
return coord.rowIndex >= Math.min(range.startRow, range.endRow)
|
|
958
|
-
&& coord.rowIndex <= Math.max(range.startRow, range.endRow)
|
|
959
|
-
&& coord.columnIndex >= Math.min(range.startColumn, range.endColumn)
|
|
960
|
-
&& coord.columnIndex <= Math.max(range.startColumn, range.endColumn);
|
|
961
|
-
};
|
|
962
|
-
const resolveCellCoordFromDataset = (rowId, columnKey) => {
|
|
963
|
-
const rowIndex = props.runtime.resolveBodyRowIndexById(rowId);
|
|
964
|
-
const columnIndex = visibleColumns.value.findIndex(column => column.key === columnKey);
|
|
965
|
-
if (rowIndex < 0 || columnIndex < 0) {
|
|
966
|
-
return null;
|
|
967
|
-
}
|
|
968
|
-
return { rowIndex, columnIndex };
|
|
969
|
-
};
|
|
970
|
-
const buildSingleCellSelectionSnapshot = (coord) => ({
|
|
971
|
-
ranges: [{
|
|
972
|
-
startRow: coord.rowIndex,
|
|
973
|
-
endRow: coord.rowIndex,
|
|
974
|
-
startCol: coord.columnIndex,
|
|
975
|
-
endCol: coord.columnIndex,
|
|
976
|
-
anchor: {
|
|
977
|
-
rowIndex: coord.rowIndex,
|
|
978
|
-
colIndex: coord.columnIndex,
|
|
979
|
-
rowId: props.runtime.getBodyRowAtIndex(coord.rowIndex)?.rowId ?? null,
|
|
980
|
-
},
|
|
981
|
-
focus: {
|
|
982
|
-
rowIndex: coord.rowIndex,
|
|
983
|
-
colIndex: coord.columnIndex,
|
|
984
|
-
rowId: props.runtime.getBodyRowAtIndex(coord.rowIndex)?.rowId ?? null,
|
|
985
|
-
},
|
|
986
|
-
startRowId: props.runtime.getBodyRowAtIndex(coord.rowIndex)?.rowId ?? null,
|
|
987
|
-
endRowId: props.runtime.getBodyRowAtIndex(coord.rowIndex)?.rowId ?? null,
|
|
988
|
-
}],
|
|
989
|
-
activeRangeIndex: 0,
|
|
990
|
-
activeCell: {
|
|
991
|
-
rowIndex: coord.rowIndex,
|
|
992
|
-
colIndex: coord.columnIndex,
|
|
993
|
-
rowId: props.runtime.getBodyRowAtIndex(coord.rowIndex)?.rowId ?? null,
|
|
994
|
-
},
|
|
995
|
-
});
|
|
996
|
-
const resolveRuntimeRowById = (rowId) => {
|
|
997
|
-
const rowCount = props.runtime.api.rows.getCount();
|
|
998
|
-
for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
|
|
999
|
-
const row = props.runtime.api.rows.get(rowIndex);
|
|
1000
|
-
if (row && String(row.rowId) === rowId) {
|
|
1001
|
-
return row;
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
return null;
|
|
1005
|
-
};
|
|
1006
|
-
const writeClipboardText = async (payload) => {
|
|
1007
|
-
try {
|
|
1008
|
-
if (!globalThis.navigator?.clipboard?.writeText) {
|
|
1009
|
-
return false;
|
|
1010
|
-
}
|
|
1011
|
-
await globalThis.navigator.clipboard.writeText(payload);
|
|
1012
|
-
return true;
|
|
1013
|
-
}
|
|
1014
|
-
catch {
|
|
1015
|
-
return false;
|
|
1016
|
-
}
|
|
1017
|
-
};
|
|
1018
|
-
const readClipboardText = async () => {
|
|
1019
|
-
try {
|
|
1020
|
-
if (!globalThis.navigator?.clipboard?.readText) {
|
|
1021
|
-
return "";
|
|
1022
|
-
}
|
|
1023
|
-
return await globalThis.navigator.clipboard.readText();
|
|
1024
|
-
}
|
|
1025
|
-
catch {
|
|
1026
|
-
return "";
|
|
1027
|
-
}
|
|
1028
|
-
};
|
|
1029
|
-
const readRowClipboardRows = async () => {
|
|
1030
|
-
if (rowClipboardBuffer.value?.rows.length) {
|
|
1031
|
-
return rowClipboardBuffer.value.rows.map(row => cloneRowData(row));
|
|
1032
|
-
}
|
|
1033
|
-
const payload = (await readClipboardText()).trim();
|
|
1034
|
-
if (!payload) {
|
|
1035
|
-
return null;
|
|
1036
|
-
}
|
|
1037
|
-
try {
|
|
1038
|
-
const parsed = JSON.parse(payload);
|
|
1039
|
-
if (Array.isArray(parsed)) {
|
|
1040
|
-
return parsed
|
|
1041
|
-
.filter((value) => Boolean(value) && typeof value === "object")
|
|
1042
|
-
.map(row => cloneRowData(row));
|
|
1043
|
-
}
|
|
1044
|
-
if (parsed && typeof parsed === "object") {
|
|
1045
|
-
return [cloneRowData(parsed)];
|
|
1046
|
-
}
|
|
1047
|
-
}
|
|
1048
|
-
catch {
|
|
1049
|
-
return null;
|
|
1050
|
-
}
|
|
1051
|
-
return null;
|
|
1052
|
-
};
|
|
1053
|
-
const setRowClipboardRows = async (rows, operation, sourceRowIds = []) => {
|
|
1054
|
-
const clonedRows = rows.map(row => cloneRowData(row));
|
|
1055
|
-
rowClipboardBuffer.value = {
|
|
1056
|
-
rows: clonedRows,
|
|
1057
|
-
operation,
|
|
1058
|
-
sourceRowIds: sourceRowIds.map(rowId => String(rowId)),
|
|
1059
|
-
};
|
|
1060
|
-
const clipboardPayload = serializeRowClipboardRows(clonedRows);
|
|
1061
|
-
if (clipboardPayload) {
|
|
1062
|
-
await writeClipboardText(clipboardPayload);
|
|
1063
|
-
}
|
|
1064
|
-
return true;
|
|
1065
|
-
};
|
|
1066
|
-
const canInsertRows = () => props.runtime.api.rows.hasInsertSupport();
|
|
1067
|
-
const canMutateRows = () => props.runtime.api.rows.hasDataMutationSupport();
|
|
1068
|
-
const isStructuredSourceRowInput = (candidate) => {
|
|
1069
|
-
if (!candidate || typeof candidate !== "object") {
|
|
1070
|
-
return false;
|
|
1071
|
-
}
|
|
1072
|
-
return ("data" in candidate
|
|
1073
|
-
|| "row" in candidate
|
|
1074
|
-
|| "kind" in candidate
|
|
1075
|
-
|| "state" in candidate
|
|
1076
|
-
|| "sourceIndex" in candidate
|
|
1077
|
-
|| "originalIndex" in candidate
|
|
1078
|
-
|| "displayIndex" in candidate);
|
|
1079
|
-
};
|
|
1080
|
-
const resolveDataRowId = (candidate) => {
|
|
1081
|
-
const candidateRecord = candidate;
|
|
1082
|
-
if (candidateRecord.rowId != null) {
|
|
1083
|
-
return String(candidateRecord.rowId);
|
|
1084
|
-
}
|
|
1085
|
-
if (candidateRecord.id != null) {
|
|
1086
|
-
return String(candidateRecord.id);
|
|
1087
|
-
}
|
|
1088
|
-
return null;
|
|
1089
|
-
};
|
|
1090
|
-
const resolveSourceRowTemplateByRowId = (rowId) => {
|
|
1091
|
-
for (const candidate of props.rows) {
|
|
1092
|
-
if (!candidate || typeof candidate !== "object") {
|
|
1093
|
-
continue;
|
|
1094
|
-
}
|
|
1095
|
-
const record = candidate;
|
|
1096
|
-
const candidateRowId = record.rowId != null
|
|
1097
|
-
? String(record.rowId)
|
|
1098
|
-
: record.rowKey != null
|
|
1099
|
-
? String(record.rowKey)
|
|
1100
|
-
: null;
|
|
1101
|
-
if (candidateRowId === rowId && isStructuredSourceRowInput(record)) {
|
|
1102
|
-
return record;
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
for (const candidate of props.rows) {
|
|
1106
|
-
if (!candidate || typeof candidate !== "object") {
|
|
1107
|
-
continue;
|
|
1108
|
-
}
|
|
1109
|
-
const record = candidate;
|
|
1110
|
-
if (isStructuredSourceRowInput(record)) {
|
|
1111
|
-
return record;
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
return null;
|
|
1115
|
-
};
|
|
1116
|
-
const createGeneratedRowIdentity = () => {
|
|
1117
|
-
generatedRowIdentitySequence += 1;
|
|
1118
|
-
return `datagrid-row-${Date.now()}-${generatedRowIdentitySequence}`;
|
|
1119
|
-
};
|
|
1120
|
-
const cloneRawRowWithFreshIdentity = (row) => {
|
|
1121
|
-
const cloned = cloneRowData(row);
|
|
1122
|
-
const nextIdentity = createGeneratedRowIdentity();
|
|
1123
|
-
let assignedIdentity = false;
|
|
1124
|
-
if ("id" in cloned) {
|
|
1125
|
-
cloned.id = nextIdentity;
|
|
1126
|
-
assignedIdentity = true;
|
|
1127
|
-
}
|
|
1128
|
-
if ("rowId" in cloned) {
|
|
1129
|
-
cloned.rowId = nextIdentity;
|
|
1130
|
-
assignedIdentity = true;
|
|
1131
|
-
}
|
|
1132
|
-
if (!assignedIdentity) {
|
|
1133
|
-
cloned.id = nextIdentity;
|
|
1134
|
-
}
|
|
1135
|
-
return cloned;
|
|
1136
|
-
};
|
|
1137
|
-
const buildBlankRawRowInput = (row) => {
|
|
1138
|
-
const nextIdentity = createGeneratedRowIdentity();
|
|
1139
|
-
const nextRow = {};
|
|
1140
|
-
for (const [key, value] of Object.entries(row)) {
|
|
1141
|
-
if (key === "id" || key === "rowId") {
|
|
1142
|
-
nextRow[key] = nextIdentity;
|
|
1143
|
-
continue;
|
|
1144
|
-
}
|
|
1145
|
-
nextRow[key] = typeof value === "number" ? null : "";
|
|
1146
|
-
}
|
|
1147
|
-
if (!("id" in nextRow) && !("rowId" in nextRow)) {
|
|
1148
|
-
nextRow.id = nextIdentity;
|
|
1149
|
-
}
|
|
1150
|
-
return nextRow;
|
|
1151
|
-
};
|
|
1152
|
-
const buildStructuredRowInput = (row, template, mode) => {
|
|
1153
|
-
const nextIdentity = createGeneratedRowIdentity();
|
|
1154
|
-
const nextData = mode === "clone"
|
|
1155
|
-
? cloneRowData(row)
|
|
1156
|
-
: Object.entries(row).reduce((result, [key, value]) => {
|
|
1157
|
-
result[key] = typeof value === "number" ? null : "";
|
|
1158
|
-
return result;
|
|
1159
|
-
}, {});
|
|
1160
|
-
if ("id" in nextData || !("rowId" in nextData) && !("id" in nextData)) {
|
|
1161
|
-
nextData.id = nextIdentity;
|
|
1162
|
-
}
|
|
1163
|
-
if ("rowId" in nextData) {
|
|
1164
|
-
nextData.rowId = nextIdentity;
|
|
1165
|
-
}
|
|
1166
|
-
if ("rowKey" in nextData) {
|
|
1167
|
-
nextData.rowKey = nextIdentity;
|
|
1168
|
-
}
|
|
1169
|
-
const templateState = typeof template.state === "object" && template.state != null
|
|
1170
|
-
? template.state
|
|
1171
|
-
: null;
|
|
1172
|
-
return {
|
|
1173
|
-
kind: template.kind === "group" ? "leaf" : (template.kind ?? "leaf"),
|
|
1174
|
-
rowId: nextIdentity,
|
|
1175
|
-
rowKey: nextIdentity,
|
|
1176
|
-
state: {
|
|
1177
|
-
...(templateState ?? {}),
|
|
1178
|
-
selected: false,
|
|
1179
|
-
group: false,
|
|
1180
|
-
pinned: "none",
|
|
1181
|
-
expanded: false,
|
|
1182
|
-
},
|
|
1183
|
-
data: nextData,
|
|
1184
|
-
row: cloneRowData(nextData),
|
|
1185
|
-
};
|
|
1186
|
-
};
|
|
1187
|
-
const cloneRowWithFreshIdentity = (row, template = null) => {
|
|
1188
|
-
if (template && isStructuredSourceRowInput(template)) {
|
|
1189
|
-
return buildStructuredRowInput(row, template, "clone");
|
|
1190
|
-
}
|
|
1191
|
-
return cloneRawRowWithFreshIdentity(row);
|
|
1192
|
-
};
|
|
1193
|
-
const buildInsertedRowInput = (row, template = null) => {
|
|
1194
|
-
if (template && isStructuredSourceRowInput(template)) {
|
|
1195
|
-
return buildStructuredRowInput(row, template, "blank");
|
|
1196
|
-
}
|
|
1197
|
-
return buildBlankRawRowInput(row);
|
|
1198
|
-
};
|
|
1199
|
-
const resolveSelectedRuntimeRowIds = (targetRowId) => {
|
|
1200
|
-
const selectedRowIds = props.rowSelectionSnapshot.value?.selectedRows ?? [];
|
|
1201
|
-
const normalizedSelected = selectedRowIds
|
|
1202
|
-
.map(rowId => String(rowId))
|
|
1203
|
-
.filter(rowId => rowId.length > 0 && resolveRuntimeRowById(rowId)?.kind !== "group");
|
|
1204
|
-
if (normalizedSelected.length > 0) {
|
|
1205
|
-
return normalizedSelected;
|
|
1206
|
-
}
|
|
1207
|
-
const selectionRange = resolveCurrentSelectionRange();
|
|
1208
|
-
const lastVisibleColumnIndex = visibleColumns.value.length - 1;
|
|
1209
|
-
if (selectionRange && lastVisibleColumnIndex >= 0) {
|
|
1210
|
-
const startColumn = Math.min(selectionRange.startColumn, selectionRange.endColumn);
|
|
1211
|
-
const endColumn = Math.max(selectionRange.startColumn, selectionRange.endColumn);
|
|
1212
|
-
const startRow = Math.min(selectionRange.startRow, selectionRange.endRow);
|
|
1213
|
-
const endRow = Math.max(selectionRange.startRow, selectionRange.endRow);
|
|
1214
|
-
const targetRowIndex = targetRowId.length > 0 ? props.runtime.resolveBodyRowIndexById(targetRowId) : -1;
|
|
1215
|
-
if (startColumn === 0
|
|
1216
|
-
&& endColumn === lastVisibleColumnIndex
|
|
1217
|
-
&& (targetRowIndex < 0 || (targetRowIndex >= startRow && targetRowIndex <= endRow))) {
|
|
1218
|
-
const rangedRowIds = [];
|
|
1219
|
-
for (let rowIndex = startRow; rowIndex <= endRow; rowIndex += 1) {
|
|
1220
|
-
const row = props.runtime.getBodyRowAtIndex(rowIndex);
|
|
1221
|
-
if (!row || row.kind === "group" || row.rowId == null) {
|
|
1222
|
-
continue;
|
|
1223
|
-
}
|
|
1224
|
-
rangedRowIds.push(String(row.rowId));
|
|
1225
|
-
}
|
|
1226
|
-
if (rangedRowIds.length > 0) {
|
|
1227
|
-
return rangedRowIds;
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
return targetRowId.length > 0 ? [targetRowId] : [];
|
|
1232
|
-
};
|
|
1233
|
-
const readCurrentRuntimeDataRows = () => {
|
|
1234
|
-
const rows = [];
|
|
1235
|
-
const rowCount = props.runtime.api.rows.getCount();
|
|
1236
|
-
for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
|
|
1237
|
-
const row = props.runtime.api.rows.get(rowIndex);
|
|
1238
|
-
if (!row || row.kind === "group") {
|
|
1239
|
-
continue;
|
|
1240
|
-
}
|
|
1241
|
-
rows.push(row.data);
|
|
1242
|
-
}
|
|
1243
|
-
return rows;
|
|
1244
|
-
};
|
|
1245
|
-
const removeRuntimeRows = (rowIds) => {
|
|
1246
|
-
if (!canMutateRows() || rowIds.length === 0) {
|
|
1247
|
-
return false;
|
|
1248
|
-
}
|
|
1249
|
-
const rowIdSet = new Set(rowIds);
|
|
1250
|
-
const nextRows = readCurrentRuntimeDataRows().filter(candidate => {
|
|
1251
|
-
const normalized = resolveDataRowId(candidate);
|
|
1252
|
-
return normalized == null || !rowIdSet.has(normalized);
|
|
1253
|
-
});
|
|
1254
|
-
if (nextRows.length === props.runtime.api.rows.getCount()) {
|
|
1255
|
-
return false;
|
|
1256
|
-
}
|
|
1257
|
-
props.runtime.api.rows.replaceData(nextRows);
|
|
1258
|
-
if (props.runtime.api.rowSelection.hasSupport()) {
|
|
1259
|
-
props.runtime.api.rowSelection.clearSelectedRows();
|
|
1260
|
-
props.syncRowSelectionSnapshotFromRuntime?.();
|
|
1261
|
-
}
|
|
1262
|
-
return true;
|
|
1263
|
-
};
|
|
1264
|
-
const moveRuntimeRowsAfter = (sourceRowIds, targetRowId) => {
|
|
1265
|
-
if (!canMutateRows() || sourceRowIds.length === 0 || !targetRowId) {
|
|
1266
|
-
return false;
|
|
1267
|
-
}
|
|
1268
|
-
const currentRows = readCurrentRuntimeDataRows();
|
|
1269
|
-
const sourceRowIdSet = new Set(sourceRowIds.map(rowId => String(rowId)));
|
|
1270
|
-
if (sourceRowIdSet.has(String(targetRowId))) {
|
|
1271
|
-
return false;
|
|
1272
|
-
}
|
|
1273
|
-
const movedRows = currentRows.filter(candidate => {
|
|
1274
|
-
const normalized = resolveDataRowId(candidate);
|
|
1275
|
-
return normalized != null && sourceRowIdSet.has(normalized);
|
|
1276
|
-
});
|
|
1277
|
-
if (movedRows.length === 0) {
|
|
1278
|
-
return false;
|
|
1279
|
-
}
|
|
1280
|
-
const remainingRows = currentRows.filter(candidate => {
|
|
1281
|
-
const normalized = resolveDataRowId(candidate);
|
|
1282
|
-
return normalized == null || !sourceRowIdSet.has(normalized);
|
|
1283
|
-
});
|
|
1284
|
-
const targetIndex = remainingRows.findIndex(candidate => resolveDataRowId(candidate) === targetRowId);
|
|
1285
|
-
if (targetIndex < 0) {
|
|
1286
|
-
return false;
|
|
1287
|
-
}
|
|
1288
|
-
props.runtime.api.rows.replaceData([
|
|
1289
|
-
...remainingRows.slice(0, targetIndex + 1),
|
|
1290
|
-
...movedRows,
|
|
1291
|
-
...remainingRows.slice(targetIndex + 1),
|
|
1292
|
-
]);
|
|
1293
|
-
if (props.runtime.api.rowSelection.hasSupport()) {
|
|
1294
|
-
props.runtime.api.rowSelection.selectRows(sourceRowIds);
|
|
1295
|
-
props.syncRowSelectionSnapshotFromRuntime?.();
|
|
1296
|
-
}
|
|
1297
|
-
return true;
|
|
1298
|
-
};
|
|
1299
|
-
const clearPendingRowClipboardOperation = () => {
|
|
1300
|
-
if (!rowClipboardBuffer.value) {
|
|
1301
|
-
return false;
|
|
1302
|
-
}
|
|
1303
|
-
rowClipboardBuffer.value = null;
|
|
1304
|
-
return true;
|
|
1305
|
-
};
|
|
1306
|
-
const isRowInPendingClipboardCut = (row) => {
|
|
1307
|
-
if (row.kind === "group") {
|
|
1308
|
-
return false;
|
|
1309
|
-
}
|
|
1310
|
-
const pending = rowClipboardBuffer.value;
|
|
1311
|
-
if (!pending) {
|
|
1312
|
-
return false;
|
|
1313
|
-
}
|
|
1314
|
-
return pending.sourceRowIds.includes(String(row.rowId));
|
|
1315
|
-
};
|
|
1316
|
-
let contextMenuVisible = () => false;
|
|
1317
|
-
let closeRuntimeContextMenu = () => undefined;
|
|
1318
|
-
let openRuntimeContextMenuFromCurrentCell = () => undefined;
|
|
1319
|
-
let runRowIndexContextAction = async (_action, _rowId) => false;
|
|
1320
|
-
const resolveRowIndexMenuLabel = (actionId, customLabel) => {
|
|
1321
|
-
if (customLabel) {
|
|
1322
|
-
return customLabel;
|
|
1323
|
-
}
|
|
1324
|
-
const label = DEFAULT_CONTEXT_MENU_ACTION_LABELS[actionId];
|
|
1325
|
-
const shortcutHint = ROW_INDEX_MENU_SHORTCUT_HINTS[actionId];
|
|
1326
|
-
return shortcutHint ? `${label} (${shortcutHint})` : label;
|
|
1327
|
-
};
|
|
1328
|
-
const { tableStageProps, tableStageContext, copySelectedCells, pasteSelectedCells, cutSelectedCells, clearSelectedCells, captureHistorySnapshot, recordHistoryIntentTransaction, } = useDataGridTableStageRuntime({
|
|
1329
|
-
mode: modeRef,
|
|
1330
|
-
rows: rowsRef,
|
|
1331
|
-
sourceRows: rowsRef,
|
|
1332
|
-
runtime: props.runtime,
|
|
1333
|
-
rowVersion,
|
|
1334
|
-
totalRuntimeRows,
|
|
1335
|
-
visibleColumns,
|
|
1336
|
-
rowRenderMode: computed(() => props.renderMode),
|
|
1337
|
-
rowHeightMode,
|
|
1338
|
-
normalizedBaseRowHeight,
|
|
1339
|
-
selectionSnapshot: props.selectionSnapshot,
|
|
1340
|
-
selectionAnchor: props.selectionAnchor,
|
|
1341
|
-
rowSelectionSnapshot: props.rowSelectionSnapshot,
|
|
1342
|
-
rowHover: computed(() => props.rowHover),
|
|
1343
|
-
stripedRows: computed(() => props.stripedRows),
|
|
1344
|
-
showRowIndex: computed(() => props.showRowIndex),
|
|
1345
|
-
showRowSelection: computed(() => props.rowSelection),
|
|
1346
|
-
isRowInPendingClipboardCut,
|
|
1347
|
-
syncSelectionSnapshotFromRuntime: props.syncSelectionSnapshotFromRuntime,
|
|
1348
|
-
syncRowSelectionSnapshotFromRuntime: props.syncRowSelectionSnapshotFromRuntime,
|
|
1349
|
-
clearExternalPendingClipboardOperation: clearPendingRowClipboardOperation,
|
|
1350
|
-
firstColumnKey,
|
|
1351
|
-
columnFilterTextByKey,
|
|
1352
|
-
virtualization: computed(() => props.virtualization),
|
|
1353
|
-
toggleSortForColumn,
|
|
1354
|
-
sortIndicator,
|
|
1355
|
-
setColumnFilterText,
|
|
1356
|
-
columnMenuEnabled: computed(() => props.columnMenu.enabled),
|
|
1357
|
-
columnMenuMaxFilterValues: computed(() => props.columnMenu.maxFilterValues),
|
|
1358
|
-
resolveColumnMenuItems,
|
|
1359
|
-
resolveColumnMenuDisabledItems,
|
|
1360
|
-
resolveColumnMenuDisabledReasons,
|
|
1361
|
-
resolveColumnMenuLabels,
|
|
1362
|
-
resolveColumnMenuActionOptions,
|
|
1363
|
-
isColumnFilterActive,
|
|
1364
|
-
isColumnGrouped,
|
|
1365
|
-
resolveColumnGroupOrder,
|
|
1366
|
-
resolveColumnMenuSortDirection,
|
|
1367
|
-
resolveColumnMenuSelectedTokens: resolveCurrentValueFilterTokens,
|
|
1368
|
-
applyColumnMenuSort,
|
|
1369
|
-
applyColumnMenuPin,
|
|
1370
|
-
applyColumnMenuGroupBy,
|
|
1371
|
-
applyColumnMenuFilter,
|
|
1372
|
-
clearColumnMenuFilter,
|
|
1373
|
-
applyRowHeightSettings,
|
|
1374
|
-
cloneRowData,
|
|
1375
|
-
isCellEditable: props.isCellEditable,
|
|
1376
|
-
isContextMenuVisible: () => contextMenuVisible(),
|
|
1377
|
-
closeContextMenu: () => closeRuntimeContextMenu(),
|
|
1378
|
-
openContextMenuFromCurrentCell: () => {
|
|
1379
|
-
openRuntimeContextMenuFromCurrentCell();
|
|
1380
|
-
},
|
|
1381
|
-
runRowIndexKeyboardAction: (action, rowId) => runRowIndexContextAction(action, rowId),
|
|
1382
|
-
});
|
|
1383
|
-
const { contextMenu, contextMenuRef, contextMenuStyle, closeContextMenu, openContextMenu, onContextMenuKeyDown, } = useDataGridContextMenu();
|
|
1384
|
-
contextMenuVisible = () => contextMenu.value.visible;
|
|
1385
|
-
closeRuntimeContextMenu = closeContextMenu;
|
|
1386
|
-
const viewportContextRouter = useDataGridViewportContextMenuRouter({
|
|
1387
|
-
isInteractionBlocked: () => false,
|
|
1388
|
-
isRangeMoveModifierActive: () => false,
|
|
1389
|
-
resolveSelectionRange: resolveCurrentSelectionRange,
|
|
1390
|
-
resolveCellCoordFromDataset,
|
|
1391
|
-
applyCellSelection: (coord) => {
|
|
1392
|
-
props.runtime.api.selection.setSnapshot(buildSingleCellSelectionSnapshot(coord));
|
|
1393
|
-
props.syncSelectionSnapshotFromRuntime();
|
|
1394
|
-
},
|
|
1395
|
-
resolveActiveCellCoord: resolveCurrentCellCoord,
|
|
1396
|
-
setActiveCellCoord: (coord) => {
|
|
1397
|
-
props.runtime.api.selection.setSnapshot(buildSingleCellSelectionSnapshot(coord));
|
|
1398
|
-
props.syncSelectionSnapshotFromRuntime();
|
|
1399
|
-
},
|
|
1400
|
-
cellCoordsEqual: (left, right) => left?.rowIndex === right?.rowIndex && left?.columnIndex === right?.columnIndex,
|
|
1401
|
-
isMultiCellSelection,
|
|
1402
|
-
isCoordInsideRange,
|
|
1403
|
-
openContextMenu,
|
|
1404
|
-
closeContextMenu,
|
|
1405
|
-
isColumnContextEnabled: (columnKey) => {
|
|
1406
|
-
if (!props.cellMenu.enabled) {
|
|
1407
|
-
return false;
|
|
1408
|
-
}
|
|
1409
|
-
return resolveDataGridCellMenuItems(props.cellMenu, columnKey).length > 0;
|
|
1410
|
-
},
|
|
1411
|
-
isRowIndexContextEnabled: () => props.rowIndexMenu.enabled && props.showRowIndex,
|
|
1412
|
-
});
|
|
1413
|
-
const contextMenuAnchor = useDataGridContextMenuAnchor({
|
|
1414
|
-
resolveCurrentCellCoord,
|
|
1415
|
-
resolveViewportElement: () => stageHostRef.value,
|
|
1416
|
-
resolveRowAtIndex: (rowIndex) => props.runtime.getBodyRowAtIndex(rowIndex) ?? undefined,
|
|
1417
|
-
resolveColumnAtIndex: (columnIndex) => visibleColumns.value[columnIndex] ?? undefined,
|
|
1418
|
-
resolveSelectionRange: resolveCurrentSelectionRange,
|
|
1419
|
-
isMultiCellSelection: (range) => isMultiCellSelection(range),
|
|
1420
|
-
isCoordInsideRange,
|
|
1421
|
-
openContextMenu,
|
|
1422
|
-
isColumnContextEnabled: column => props.cellMenu.enabled && resolveDataGridCellMenuItems(props.cellMenu, column.key).length > 0,
|
|
1423
|
-
});
|
|
1424
|
-
openRuntimeContextMenuFromCurrentCell = () => {
|
|
1425
|
-
contextMenuAnchor.openContextMenuFromCurrentCell();
|
|
1426
|
-
};
|
|
1427
|
-
const openRuntimeRowIndexContextMenu = (rowId) => {
|
|
1428
|
-
if (!props.rowIndexMenu.enabled || !props.showRowIndex || !stageHostRef.value) {
|
|
1429
|
-
return false;
|
|
1430
|
-
}
|
|
1431
|
-
const normalizedRowId = String(rowId);
|
|
1432
|
-
const selector = `.datagrid-stage__row-index-cell[data-row-id="${escapeCssAttributeValue(normalizedRowId)}"]`;
|
|
1433
|
-
const rowIndexCell = stageHostRef.value.querySelector(selector);
|
|
1434
|
-
if (!rowIndexCell) {
|
|
1435
|
-
return false;
|
|
1436
|
-
}
|
|
1437
|
-
const rect = rowIndexCell.getBoundingClientRect();
|
|
1438
|
-
openContextMenu(rect.left + Math.max(10, Math.min(rect.width / 2, Math.max(10, rect.width - 10))), rect.bottom - 4, {
|
|
1439
|
-
zone: "row-index",
|
|
1440
|
-
rowId: normalizedRowId,
|
|
1441
|
-
});
|
|
1442
|
-
return true;
|
|
1443
|
-
};
|
|
1444
|
-
const recordRowMutation = (beforeSnapshot, label) => {
|
|
1445
|
-
recordHistoryIntentTransaction({
|
|
1446
|
-
intent: "edit",
|
|
1447
|
-
label,
|
|
1448
|
-
}, beforeSnapshot);
|
|
1449
|
-
};
|
|
1450
|
-
runRowIndexContextAction = async (action, rowId) => {
|
|
1451
|
-
const normalizedRowId = String(rowId);
|
|
1452
|
-
if (action === "open-row-menu") {
|
|
1453
|
-
return openRuntimeRowIndexContextMenu(normalizedRowId);
|
|
1454
|
-
}
|
|
1455
|
-
const targetRow = resolveRuntimeRowById(normalizedRowId);
|
|
1456
|
-
if (!targetRow || targetRow.kind === "group") {
|
|
1457
|
-
return false;
|
|
1458
|
-
}
|
|
1459
|
-
if (action === "insert-row-above") {
|
|
1460
|
-
if (!canInsertRows()) {
|
|
1461
|
-
return false;
|
|
1462
|
-
}
|
|
1463
|
-
const beforeSnapshot = captureHistorySnapshot();
|
|
1464
|
-
const inserted = props.runtime.api.rows.insertDataBefore(rowId, [buildInsertedRowInput(targetRow.data, resolveSourceRowTemplateByRowId(normalizedRowId))]);
|
|
1465
|
-
if (inserted) {
|
|
1466
|
-
recordRowMutation(beforeSnapshot, "Insert row above");
|
|
1467
|
-
}
|
|
1468
|
-
return inserted;
|
|
1469
|
-
}
|
|
1470
|
-
if (action === "insert-row-below") {
|
|
1471
|
-
if (!canInsertRows()) {
|
|
1472
|
-
return false;
|
|
1473
|
-
}
|
|
1474
|
-
const beforeSnapshot = captureHistorySnapshot();
|
|
1475
|
-
const inserted = props.runtime.api.rows.insertDataAfter(rowId, [buildInsertedRowInput(targetRow.data, resolveSourceRowTemplateByRowId(normalizedRowId))]);
|
|
1476
|
-
if (inserted) {
|
|
1477
|
-
recordRowMutation(beforeSnapshot, "Insert row below");
|
|
1478
|
-
}
|
|
1479
|
-
return inserted;
|
|
1480
|
-
}
|
|
1481
|
-
if (action === "copy-row") {
|
|
1482
|
-
return setRowClipboardRows([targetRow.data], "copy", [normalizedRowId]);
|
|
1483
|
-
}
|
|
1484
|
-
if (action === "cut-row") {
|
|
1485
|
-
return setRowClipboardRows([targetRow.data], "cut", [normalizedRowId]);
|
|
1486
|
-
}
|
|
1487
|
-
if (action === "paste-row") {
|
|
1488
|
-
if (!canInsertRows()) {
|
|
1489
|
-
return false;
|
|
1490
|
-
}
|
|
1491
|
-
const rows = await readRowClipboardRows();
|
|
1492
|
-
if (!rows || rows.length === 0) {
|
|
1493
|
-
return false;
|
|
1494
|
-
}
|
|
1495
|
-
const beforeSnapshot = captureHistorySnapshot();
|
|
1496
|
-
const pendingCutSourceRowIds = rowClipboardBuffer.value?.operation === "cut"
|
|
1497
|
-
? rowClipboardBuffer.value.sourceRowIds
|
|
1498
|
-
: [];
|
|
1499
|
-
const sourceRowTemplate = resolveSourceRowTemplateByRowId(pendingCutSourceRowIds[0] ?? normalizedRowId) ?? resolveSourceRowTemplateByRowId(normalizedRowId);
|
|
1500
|
-
const inserted = pendingCutSourceRowIds.length > 0
|
|
1501
|
-
? moveRuntimeRowsAfter(pendingCutSourceRowIds, normalizedRowId)
|
|
1502
|
-
: props.runtime.api.rows.insertDataAfter(rowId, rows.map(row => cloneRowWithFreshIdentity(row, sourceRowTemplate)));
|
|
1503
|
-
if (inserted && rowClipboardBuffer.value?.operation === "cut") {
|
|
1504
|
-
rowClipboardBuffer.value = null;
|
|
1505
|
-
}
|
|
1506
|
-
if (inserted) {
|
|
1507
|
-
recordRowMutation(beforeSnapshot, pendingCutSourceRowIds.length > 0
|
|
1508
|
-
? (pendingCutSourceRowIds.length > 1 ? `Move ${pendingCutSourceRowIds.length} rows` : "Move row")
|
|
1509
|
-
: (rows.length > 1 ? `Paste ${rows.length} rows` : "Paste row"));
|
|
1510
|
-
}
|
|
1511
|
-
return inserted;
|
|
1512
|
-
}
|
|
1513
|
-
if (action === "delete-selected-rows") {
|
|
1514
|
-
const rowIds = resolveSelectedRuntimeRowIds(normalizedRowId);
|
|
1515
|
-
if (rowIds.length === 0) {
|
|
1516
|
-
return false;
|
|
1517
|
-
}
|
|
1518
|
-
const beforeSnapshot = captureHistorySnapshot();
|
|
1519
|
-
const removed = removeRuntimeRows(rowIds);
|
|
1520
|
-
if (removed) {
|
|
1521
|
-
recordRowMutation(beforeSnapshot, rowIds.length > 1 ? `Delete ${rowIds.length} rows` : "Delete row");
|
|
1522
|
-
}
|
|
1523
|
-
return removed;
|
|
1524
|
-
}
|
|
1525
|
-
return false;
|
|
1526
|
-
};
|
|
1527
|
-
const contextMenuActionRouter = useDataGridContextMenuActionRouter({
|
|
1528
|
-
resolveContextMenuState: () => ({
|
|
1529
|
-
zone: contextMenu.value.zone,
|
|
1530
|
-
columnKey: contextMenu.value.columnKey,
|
|
1531
|
-
rowId: contextMenu.value.rowId,
|
|
1532
|
-
}),
|
|
1533
|
-
runHeaderContextAction: () => false,
|
|
1534
|
-
runRowIndexContextAction,
|
|
1535
|
-
copySelection: copySelectedCells,
|
|
1536
|
-
pasteSelection: pasteSelectedCells,
|
|
1537
|
-
cutSelection: cutSelectedCells,
|
|
1538
|
-
clearCurrentSelection: clearSelectedCells,
|
|
1539
|
-
closeContextMenu,
|
|
1540
|
-
});
|
|
1541
|
-
const menuActionEntries = computed(() => {
|
|
1542
|
-
if (!contextMenu.value.visible) {
|
|
1543
|
-
return [];
|
|
1544
|
-
}
|
|
1545
|
-
const zone = contextMenu.value.zone;
|
|
1546
|
-
if (zone === "cell" || zone === "range") {
|
|
1547
|
-
const columnKey = contextMenu.value.columnKey ?? "";
|
|
1548
|
-
if (!props.cellMenu.enabled || !columnKey) {
|
|
1549
|
-
return [];
|
|
1550
|
-
}
|
|
1551
|
-
const items = resolveDataGridCellMenuItems(props.cellMenu, columnKey);
|
|
1552
|
-
const disabledItems = new Set(resolveDataGridCellMenuDisabledItems(props.cellMenu, columnKey));
|
|
1553
|
-
const disabledReasons = resolveDataGridCellMenuDisabledReasons(props.cellMenu, columnKey);
|
|
1554
|
-
const actionOptions = resolveDataGridCellMenuActionOptions(props.cellMenu, columnKey);
|
|
1555
|
-
return items.flatMap((item, itemIndex) => {
|
|
1556
|
-
const itemActions = CELL_MENU_ACTION_IDS_BY_ITEM[item] ?? [];
|
|
1557
|
-
return itemActions.flatMap(actionId => {
|
|
1558
|
-
const actionKey = actionId;
|
|
1559
|
-
const option = actionOptions[actionKey];
|
|
1560
|
-
if (option?.hidden) {
|
|
1561
|
-
return [];
|
|
1562
|
-
}
|
|
1563
|
-
const disabled = disabledItems.has(item) || option?.disabled === true;
|
|
1564
|
-
return [{
|
|
1565
|
-
id: actionId,
|
|
1566
|
-
label: option?.label ?? DEFAULT_CONTEXT_MENU_ACTION_LABELS[actionId],
|
|
1567
|
-
disabled,
|
|
1568
|
-
title: option?.disabledReason ?? disabledReasons[item],
|
|
1569
|
-
separatorBefore: itemIndex > 0 && actionId === itemActions[0],
|
|
1570
|
-
}];
|
|
1571
|
-
});
|
|
1572
|
-
});
|
|
1573
|
-
}
|
|
1574
|
-
if (zone === "row-index") {
|
|
1575
|
-
if (!props.rowIndexMenu.enabled) {
|
|
1576
|
-
return [];
|
|
1577
|
-
}
|
|
1578
|
-
const items = resolveDataGridRowIndexMenuItems(props.rowIndexMenu);
|
|
1579
|
-
const disabledItems = new Set(resolveDataGridRowIndexMenuDisabledItems(props.rowIndexMenu));
|
|
1580
|
-
const disabledReasons = resolveDataGridRowIndexMenuDisabledReasons(props.rowIndexMenu);
|
|
1581
|
-
const actionOptions = resolveDataGridRowIndexMenuActionOptions(props.rowIndexMenu);
|
|
1582
|
-
return items.flatMap((item, itemIndex) => {
|
|
1583
|
-
const itemActions = ROW_INDEX_MENU_ACTION_IDS_BY_ITEM[item] ?? [];
|
|
1584
|
-
return itemActions.flatMap(actionId => {
|
|
1585
|
-
const actionKey = actionId === "insert-row-above"
|
|
1586
|
-
? "insertAbove"
|
|
1587
|
-
: actionId === "insert-row-below"
|
|
1588
|
-
? "insertBelow"
|
|
1589
|
-
: actionId === "copy-row"
|
|
1590
|
-
? "copy"
|
|
1591
|
-
: actionId === "paste-row"
|
|
1592
|
-
? "paste"
|
|
1593
|
-
: actionId === "delete-selected-rows"
|
|
1594
|
-
? "deleteSelected"
|
|
1595
|
-
: "cut";
|
|
1596
|
-
const option = actionOptions[actionKey];
|
|
1597
|
-
if (option?.hidden) {
|
|
1598
|
-
return [];
|
|
1599
|
-
}
|
|
1600
|
-
let disabled = disabledItems.has(item) || option?.disabled === true;
|
|
1601
|
-
let title = option?.disabledReason ?? disabledReasons[item];
|
|
1602
|
-
if ((actionId === "insert-row-above" || actionId === "insert-row-below" || actionId === "paste-row") && !canInsertRows()) {
|
|
1603
|
-
disabled = true;
|
|
1604
|
-
title = title ?? "Row insertion is not supported by the current row model";
|
|
1605
|
-
}
|
|
1606
|
-
if ((actionId === "cut-row" || actionId === "delete-selected-rows") && !canMutateRows()) {
|
|
1607
|
-
disabled = true;
|
|
1608
|
-
title = title ?? "Row deletion is not supported by the current row model";
|
|
1609
|
-
}
|
|
1610
|
-
if (actionId === "delete-selected-rows") {
|
|
1611
|
-
const effectiveRowIds = resolveSelectedRuntimeRowIds(contextMenu.value.rowId ?? "");
|
|
1612
|
-
if (effectiveRowIds.length === 0) {
|
|
1613
|
-
disabled = true;
|
|
1614
|
-
title = title ?? "No deletable rows are selected";
|
|
1615
|
-
}
|
|
1616
|
-
}
|
|
1617
|
-
return [{
|
|
1618
|
-
id: actionId,
|
|
1619
|
-
label: resolveRowIndexMenuLabel(actionId, option?.label),
|
|
1620
|
-
disabled,
|
|
1621
|
-
title,
|
|
1622
|
-
separatorBefore: itemIndex > 0 && actionId === itemActions[0],
|
|
1623
|
-
}];
|
|
1624
|
-
});
|
|
1625
|
-
});
|
|
1626
|
-
}
|
|
1627
|
-
return [];
|
|
1628
|
-
});
|
|
1629
|
-
const handleViewportContextMenu = (event) => {
|
|
1630
|
-
viewportContextRouter.dispatchViewportContextMenu(event);
|
|
1631
|
-
};
|
|
1632
|
-
const focusGridViewport = () => {
|
|
1633
|
-
const viewport = stageHostRef.value?.querySelector(".grid-body-viewport");
|
|
1634
|
-
viewport?.focus({ preventScroll: true });
|
|
1635
|
-
};
|
|
1636
|
-
const scheduleGridViewportFocus = () => {
|
|
1637
|
-
void nextTick(() => {
|
|
1638
|
-
if (typeof window !== "undefined") {
|
|
1639
|
-
window.requestAnimationFrame(() => {
|
|
1640
|
-
focusGridViewport();
|
|
1641
|
-
});
|
|
1642
|
-
return;
|
|
1643
|
-
}
|
|
1644
|
-
focusGridViewport();
|
|
1645
|
-
});
|
|
1646
|
-
};
|
|
1647
|
-
const handleContextMenuAction = async (action) => {
|
|
1648
|
-
const handled = await contextMenuActionRouter.runContextMenuAction(action);
|
|
1649
|
-
if (handled) {
|
|
1650
|
-
focusGridViewport();
|
|
1651
|
-
closeContextMenu();
|
|
1652
|
-
scheduleGridViewportFocus();
|
|
1653
|
-
}
|
|
1654
|
-
};
|
|
1655
|
-
watch(() => contextMenu.value.visible, (visible, _previous, onCleanup) => {
|
|
1656
|
-
if (!visible || typeof window === "undefined") {
|
|
1657
|
-
return;
|
|
1658
|
-
}
|
|
1659
|
-
const handleWindowPointerDown = (event) => {
|
|
1660
|
-
const target = event.target;
|
|
1661
|
-
if (target && contextMenuRef.value?.contains(target)) {
|
|
1662
|
-
return;
|
|
1663
|
-
}
|
|
1664
|
-
closeContextMenu();
|
|
1665
|
-
};
|
|
1666
|
-
const handleWindowBlur = () => {
|
|
1667
|
-
closeContextMenu();
|
|
1668
|
-
};
|
|
1669
|
-
window.addEventListener("mousedown", handleWindowPointerDown, true);
|
|
1670
|
-
window.addEventListener("blur", handleWindowBlur);
|
|
1671
|
-
onCleanup(() => {
|
|
1672
|
-
window.removeEventListener("mousedown", handleWindowPointerDown, true);
|
|
1673
|
-
window.removeEventListener("blur", handleWindowBlur);
|
|
1674
|
-
});
|
|
1675
|
-
});
|
|
1676
|
-
const stageProps = computed(() => ({
|
|
1677
|
-
...tableStageProps.value,
|
|
1678
|
-
columns: {
|
|
1679
|
-
...tableStageProps.value.columns,
|
|
1680
|
-
columnMenuEnabled: props.columnMenu.enabled,
|
|
1681
|
-
columnMenuMaxFilterValues: props.columnMenu.maxFilterValues,
|
|
1682
|
-
resolveColumnMenuItems,
|
|
1683
|
-
resolveColumnMenuDisabledItems,
|
|
1684
|
-
resolveColumnMenuDisabledReasons,
|
|
1685
|
-
resolveColumnMenuLabels,
|
|
1686
|
-
resolveColumnMenuActionOptions,
|
|
1687
|
-
isColumnFilterActive,
|
|
1688
|
-
isColumnGrouped,
|
|
1689
|
-
resolveColumnGroupOrder,
|
|
1690
|
-
resolveColumnMenuSortDirection,
|
|
1691
|
-
resolveColumnMenuSelectedTokens: resolveCurrentValueFilterTokens,
|
|
1692
|
-
applyColumnMenuSort,
|
|
1693
|
-
applyColumnMenuPin,
|
|
1694
|
-
applyColumnMenuGroupBy,
|
|
1695
|
-
applyColumnMenuFilter,
|
|
1696
|
-
clearColumnMenuFilter,
|
|
1697
|
-
},
|
|
1698
|
-
rows: {
|
|
1699
|
-
...tableStageProps.value.rows,
|
|
1700
|
-
sourceRows: props.rows,
|
|
1701
|
-
rowHover: props.rowHover,
|
|
1702
|
-
stripedRows: props.stripedRows,
|
|
1703
|
-
},
|
|
1704
|
-
}));
|
|
1705
|
-
const toolbarModules = computed(() => {
|
|
1706
|
-
const modules = [];
|
|
1707
|
-
if (props.columnLayout.enabled) {
|
|
1708
|
-
modules.push({
|
|
1709
|
-
key: "column-layout",
|
|
1710
|
-
component: DataGridColumnLayoutPopover,
|
|
1711
|
-
props: {
|
|
1712
|
-
isOpen: isColumnLayoutPanelOpen.value,
|
|
1713
|
-
items: columnLayoutPanelItems.value,
|
|
1714
|
-
buttonLabel: props.columnLayout.buttonLabel,
|
|
1715
|
-
active: false,
|
|
1716
|
-
onOpen: openColumnLayoutPanel,
|
|
1717
|
-
onToggleVisibility: updateColumnVisibility,
|
|
1718
|
-
onMoveUp: moveColumnUp,
|
|
1719
|
-
onMoveDown: moveColumnDown,
|
|
1720
|
-
onApply: applyColumnLayoutPanel,
|
|
1721
|
-
onCancel: cancelColumnLayoutPanel,
|
|
1722
|
-
},
|
|
1723
|
-
});
|
|
1724
|
-
}
|
|
1725
|
-
if (props.advancedFilter.enabled) {
|
|
1726
|
-
modules.push({
|
|
1727
|
-
key: "advanced-filter",
|
|
1728
|
-
component: DataGridAdvancedFilterPopover,
|
|
1729
|
-
props: {
|
|
1730
|
-
isOpen: isAdvancedFilterPanelOpen.value,
|
|
1731
|
-
clauses: advancedFilterDraftClauses.value,
|
|
1732
|
-
columns: advancedFilterColumns.value,
|
|
1733
|
-
appliedFilterSummaryItems: activeFilterSummaryItems.value,
|
|
1734
|
-
hasAnyFilters: hasActiveFilters.value,
|
|
1735
|
-
buttonLabel: props.advancedFilter.buttonLabel,
|
|
1736
|
-
active: hasActiveFilters.value,
|
|
1737
|
-
onOpen: openAdvancedFilterPanel,
|
|
1738
|
-
onAdd: addAdvancedFilterClause,
|
|
1739
|
-
onRemove: removeAdvancedFilterClause,
|
|
1740
|
-
onUpdateClause: updateAdvancedFilterClause,
|
|
1741
|
-
onApply: applyAdvancedFilterPanel,
|
|
1742
|
-
onCancel: cancelAdvancedFilterPanel,
|
|
1743
|
-
onResetAll: resetAllFilters,
|
|
1744
|
-
},
|
|
1745
|
-
});
|
|
1746
|
-
}
|
|
1747
|
-
if (props.aggregations.enabled && props.mode !== "pivot") {
|
|
1748
|
-
modules.push({
|
|
1749
|
-
key: "aggregations",
|
|
1750
|
-
component: DataGridAggregationsPopover,
|
|
1751
|
-
props: {
|
|
1752
|
-
isOpen: isAggregationsPanelOpen.value,
|
|
1753
|
-
basis: aggregationBasis.value,
|
|
1754
|
-
items: aggregationPanelItems.value,
|
|
1755
|
-
buttonLabel: props.aggregations.buttonLabel,
|
|
1756
|
-
active: aggregationGroupingEnabled.value && (isAggregationsPanelOpen.value || Boolean(currentAggregationModel.value?.columns.length)),
|
|
1757
|
-
disabled: !aggregationGroupingEnabled.value,
|
|
1758
|
-
disabledReason: aggregationGroupingEnabled.value
|
|
1759
|
-
? ""
|
|
1760
|
-
: "Aggregations require an active group-by model.",
|
|
1761
|
-
onOpen: openAggregationsPanel,
|
|
1762
|
-
onUpdateBasis: updateAggregationBasis,
|
|
1763
|
-
onToggleColumn: toggleAggregationColumn,
|
|
1764
|
-
onUpdateOp: updateAggregationOp,
|
|
1765
|
-
onClear: clearAggregationsPanel,
|
|
1766
|
-
onApply: applyAggregationsPanel,
|
|
1767
|
-
onCancel: cancelAggregationsPanel,
|
|
1768
|
-
},
|
|
1769
|
-
});
|
|
1770
|
-
}
|
|
1771
|
-
return [...modules, ...props.toolbarModules];
|
|
1772
|
-
});
|
|
1773
|
-
return () => h("div", {
|
|
1774
|
-
class: "datagrid-app-layout",
|
|
1775
|
-
}, [
|
|
1776
|
-
h(DataGridModuleHost, {
|
|
1777
|
-
modules: toolbarModules.value,
|
|
1778
|
-
}),
|
|
1779
|
-
h("div", {
|
|
1780
|
-
class: "datagrid-app-workspace",
|
|
1781
|
-
}, [
|
|
1782
|
-
h("div", {
|
|
1783
|
-
class: "datagrid-app-stage",
|
|
1784
|
-
ref: stageHostRef,
|
|
1785
|
-
}, [
|
|
1786
|
-
props.viewMode === "gantt"
|
|
1787
|
-
? h(DataGridGanttStage, {
|
|
1788
|
-
stageContext: tableStageContext,
|
|
1789
|
-
runtime: props.runtime,
|
|
1790
|
-
gantt: resolvedGanttOptions.value,
|
|
1791
|
-
baseRowHeight: normalizedBaseRowHeight.value,
|
|
1792
|
-
rowVersion: rowVersion.value,
|
|
1793
|
-
})
|
|
1794
|
-
: h(DataGridTableStage, {
|
|
1795
|
-
...stageProps.value,
|
|
1796
|
-
stageContext: tableStageContext,
|
|
1797
|
-
onViewportContextMenu: handleViewportContextMenu,
|
|
1798
|
-
}),
|
|
1799
|
-
contextMenu.value.visible && menuActionEntries.value.length > 0
|
|
1800
|
-
? h("div", {
|
|
1801
|
-
ref: contextMenuRef,
|
|
1802
|
-
class: "datagrid-context-menu",
|
|
1803
|
-
style: contextMenuStyle.value,
|
|
1804
|
-
role: "menu",
|
|
1805
|
-
tabindex: -1,
|
|
1806
|
-
onKeydown: (event) => {
|
|
1807
|
-
onContextMenuKeyDown(event, {
|
|
1808
|
-
onEscape: () => {
|
|
1809
|
-
focusGridViewport();
|
|
1810
|
-
},
|
|
1811
|
-
});
|
|
1812
|
-
},
|
|
1813
|
-
}, menuActionEntries.value.flatMap(entry => {
|
|
1814
|
-
const nodes = [];
|
|
1815
|
-
if (entry.separatorBefore) {
|
|
1816
|
-
nodes.push(h("div", {
|
|
1817
|
-
class: "datagrid-context-menu__separator",
|
|
1818
|
-
"aria-hidden": "true",
|
|
1819
|
-
}));
|
|
1820
|
-
}
|
|
1821
|
-
nodes.push(h("button", {
|
|
1822
|
-
type: "button",
|
|
1823
|
-
class: "datagrid-context-menu__item",
|
|
1824
|
-
"data-datagrid-menu-action": entry.id,
|
|
1825
|
-
disabled: entry.disabled,
|
|
1826
|
-
title: entry.title,
|
|
1827
|
-
onClick: () => {
|
|
1828
|
-
void handleContextMenuAction(entry.id);
|
|
1829
|
-
},
|
|
1830
|
-
}, entry.label));
|
|
1831
|
-
return nodes;
|
|
1832
|
-
}))
|
|
1833
|
-
: null,
|
|
1834
|
-
]),
|
|
1835
|
-
props.inspectorPanel
|
|
1836
|
-
? h("aside", {
|
|
1837
|
-
class: "datagrid-app-inspector-shell",
|
|
1838
|
-
}, [
|
|
1839
|
-
h(props.inspectorPanel.component, {
|
|
1840
|
-
...(props.inspectorPanel.props ?? {}),
|
|
1841
|
-
}),
|
|
1842
|
-
])
|
|
1843
|
-
: null,
|
|
1844
|
-
]),
|
|
1845
|
-
]);
|
|
1846
|
-
},
|
|
1847
|
-
});
|