@gridkitjs/core 0.10.0 → 0.12.0
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/index.d.ts +2012 -22
- package/dist/index.js +3197 -853
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -63,13 +63,23 @@ function getColumnId(column) {
|
|
|
63
63
|
function resolveRowId(row, index, getRowId) {
|
|
64
64
|
return getRowId?.(row, index) ?? String(index);
|
|
65
65
|
}
|
|
66
|
+
function labelFromField(field) {
|
|
67
|
+
return field.split(".").join(" ");
|
|
68
|
+
}
|
|
66
69
|
function resolveColumnLabel(column) {
|
|
67
70
|
const { headerTemplate } = column;
|
|
68
71
|
if (headerTemplate === void 0) {
|
|
69
|
-
return column.
|
|
72
|
+
return column.header ?? labelFromField(column.field);
|
|
70
73
|
}
|
|
71
74
|
return typeof headerTemplate === "function" ? headerTemplate() : headerTemplate;
|
|
72
75
|
}
|
|
76
|
+
function resolveColumnHeaderText(column) {
|
|
77
|
+
if (column.header !== void 0) {
|
|
78
|
+
return column.header;
|
|
79
|
+
}
|
|
80
|
+
const label = resolveColumnLabel(column);
|
|
81
|
+
return typeof label === "string" ? label : labelFromField(column.field);
|
|
82
|
+
}
|
|
73
83
|
function buildKeyShortcuts(capabilities) {
|
|
74
84
|
return [
|
|
75
85
|
capabilities.reorderable ? "Control+ArrowLeft Control+ArrowRight" : "",
|
|
@@ -80,855 +90,2808 @@ function buildKeyShortcuts(capabilities) {
|
|
|
80
90
|
capabilities.sortable ? "Alt+ArrowUp" : "",
|
|
81
91
|
// Down rather than reusing Up: it reads as "drill into groups", and Up is
|
|
82
92
|
// already sort's own mnemonic on the same header.
|
|
83
|
-
capabilities.groupable ? "Alt+ArrowDown" : ""
|
|
93
|
+
capabilities.groupable ? "Alt+ArrowDown" : "",
|
|
94
|
+
// The ContextMenu key needs no mention: it says what it does on its face.
|
|
95
|
+
capabilities.contextMenu === true ? "Shift+F10" : ""
|
|
84
96
|
].filter(Boolean).join(" ");
|
|
85
97
|
}
|
|
86
98
|
function accessDotted(obj, path) {
|
|
87
99
|
return path.split(".").reduce((value, key) => isNested(value) ? value[key] : void 0, obj);
|
|
88
100
|
}
|
|
89
101
|
|
|
90
|
-
// src/util/
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
rowIndex: Math.min(Math.max(focus.rowIndex, HEADER_ROW), rowCount - 1),
|
|
95
|
-
columnIndex: Math.min(
|
|
96
|
-
Math.max(focus.columnIndex, 0),
|
|
97
|
-
Math.max(columnCount - 1, 0)
|
|
98
|
-
)
|
|
99
|
-
};
|
|
102
|
+
// src/util/sorting.ts
|
|
103
|
+
function isEmpty(value) {
|
|
104
|
+
return value === null || value === void 0;
|
|
100
105
|
}
|
|
101
|
-
function
|
|
102
|
-
return
|
|
106
|
+
function toTime(value) {
|
|
107
|
+
return value instanceof Date ? value.getTime() : new Date(String(value)).getTime();
|
|
103
108
|
}
|
|
104
|
-
function
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const stepped = next + direction;
|
|
108
|
-
if (stepped < HEADER_ROW || stepped >= rowCount) {
|
|
109
|
-
return fallback;
|
|
110
|
-
}
|
|
111
|
-
next = stepped;
|
|
109
|
+
function compareValues(a, b, type) {
|
|
110
|
+
if (isEmpty(a)) {
|
|
111
|
+
return isEmpty(b) ? 0 : 1;
|
|
112
112
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
function nextFocusForKey(key, modifiers, focus, rowCount, columnCount, pageSize, isSkippableRow = noRowsSkippable) {
|
|
116
|
-
if (modifiers.altKey) {
|
|
117
|
-
return null;
|
|
113
|
+
if (isEmpty(b)) {
|
|
114
|
+
return -1;
|
|
118
115
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
case "
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return
|
|
127
|
-
case "
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
-1,
|
|
132
|
-
rowCount,
|
|
133
|
-
isSkippableRow,
|
|
134
|
-
rowIndex
|
|
135
|
-
),
|
|
136
|
-
columnIndex
|
|
137
|
-
};
|
|
138
|
-
case "ArrowDown":
|
|
139
|
-
return {
|
|
140
|
-
rowIndex: skipSkippableRows(
|
|
141
|
-
rowIndex + 1,
|
|
142
|
-
1,
|
|
143
|
-
rowCount,
|
|
144
|
-
isSkippableRow,
|
|
145
|
-
rowIndex
|
|
146
|
-
),
|
|
147
|
-
columnIndex
|
|
148
|
-
};
|
|
149
|
-
// Ctrl takes Home and End to the grid's ends rather than the row's, which
|
|
150
|
-
// is the one place Ctrl is navigation rather than reorder.
|
|
151
|
-
case "Home":
|
|
152
|
-
return modifiers.ctrlKey ? { rowIndex: HEADER_ROW, columnIndex: 0 } : { rowIndex, columnIndex: 0 };
|
|
153
|
-
case "End":
|
|
154
|
-
return modifiers.ctrlKey ? {
|
|
155
|
-
rowIndex: skipSkippableRows(
|
|
156
|
-
rowCount - 1,
|
|
157
|
-
-1,
|
|
158
|
-
rowCount,
|
|
159
|
-
isSkippableRow,
|
|
160
|
-
rowIndex
|
|
161
|
-
),
|
|
162
|
-
columnIndex: columnCount - 1
|
|
163
|
-
} : { rowIndex, columnIndex: columnCount - 1 };
|
|
164
|
-
case "PageUp":
|
|
165
|
-
return {
|
|
166
|
-
rowIndex: skipSkippableRows(
|
|
167
|
-
rowIndex - pageSize,
|
|
168
|
-
-1,
|
|
169
|
-
rowCount,
|
|
170
|
-
isSkippableRow,
|
|
171
|
-
rowIndex
|
|
172
|
-
),
|
|
173
|
-
columnIndex
|
|
174
|
-
};
|
|
175
|
-
case "PageDown":
|
|
176
|
-
return {
|
|
177
|
-
rowIndex: skipSkippableRows(
|
|
178
|
-
rowIndex + pageSize,
|
|
179
|
-
1,
|
|
180
|
-
rowCount,
|
|
181
|
-
isSkippableRow,
|
|
182
|
-
rowIndex
|
|
183
|
-
),
|
|
184
|
-
columnIndex
|
|
185
|
-
};
|
|
116
|
+
switch (type) {
|
|
117
|
+
case "number":
|
|
118
|
+
case "decimal":
|
|
119
|
+
case "currency":
|
|
120
|
+
case "percent":
|
|
121
|
+
return Number(a) - Number(b);
|
|
122
|
+
case "boolean":
|
|
123
|
+
return Number(Boolean(a)) - Number(Boolean(b));
|
|
124
|
+
case "date":
|
|
125
|
+
case "dateTime":
|
|
126
|
+
case "time":
|
|
127
|
+
return toTime(a) - toTime(b);
|
|
186
128
|
default:
|
|
187
|
-
return
|
|
129
|
+
return String(a).localeCompare(String(b));
|
|
188
130
|
}
|
|
189
131
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
function applyColumnOrder(columns, order) {
|
|
193
|
-
if (order.length === 0) {
|
|
194
|
-
return columns;
|
|
195
|
-
}
|
|
196
|
-
const byId = new Map(columns.map((column) => [getColumnId(column), column]));
|
|
197
|
-
const listed = order.map((id) => byId.get(id)).filter((column) => column !== void 0);
|
|
198
|
-
const taken = new Set(listed.map((column) => getColumnId(column)));
|
|
199
|
-
return [
|
|
200
|
-
...listed,
|
|
201
|
-
...columns.filter((column) => !taken.has(getColumnId(column)))
|
|
202
|
-
];
|
|
132
|
+
function sortDirectionFor(sort, columnId) {
|
|
133
|
+
return sort.find((entry) => entry.columnId === columnId)?.direction ?? null;
|
|
203
134
|
}
|
|
204
|
-
function
|
|
205
|
-
|
|
206
|
-
|
|
135
|
+
function sortPriorityFor(sort, columnId) {
|
|
136
|
+
const index = sort.findIndex((entry) => entry.columnId === columnId);
|
|
137
|
+
return index === -1 ? null : index + 1;
|
|
138
|
+
}
|
|
139
|
+
function toggleColumnSort(sort, columnId, options) {
|
|
140
|
+
const index = sort.findIndex((entry) => entry.columnId === columnId);
|
|
141
|
+
const isSoleEntry = sort.length === 1 && index === 0;
|
|
142
|
+
if (!options.stack && !isSoleEntry) {
|
|
143
|
+
return [{ columnId, direction: "asc" }];
|
|
207
144
|
}
|
|
208
|
-
const
|
|
209
|
-
const
|
|
210
|
-
if (
|
|
211
|
-
return
|
|
145
|
+
const current = index === -1 ? void 0 : sort[index]?.direction;
|
|
146
|
+
const next = current === void 0 ? "asc" : current === "asc" ? "desc" : null;
|
|
147
|
+
if (next === null) {
|
|
148
|
+
return sort.filter((entry) => entry.columnId !== columnId);
|
|
212
149
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
return next.every((id, index) => id === orderedIds[index]) ? orderedIds : next;
|
|
216
|
-
}
|
|
217
|
-
function movesColumn(orderedIds, movedId, beforeId) {
|
|
218
|
-
return moveColumnBefore(orderedIds, movedId, beforeId) !== orderedIds;
|
|
219
|
-
}
|
|
220
|
-
function resolveDropBefore(orderedIds, targetId, side) {
|
|
221
|
-
if (side === "before") {
|
|
222
|
-
return targetId;
|
|
150
|
+
if (index === -1) {
|
|
151
|
+
return [...sort, { columnId, direction: next }];
|
|
223
152
|
}
|
|
224
|
-
|
|
225
|
-
|
|
153
|
+
return sort.map(
|
|
154
|
+
(entry, i) => i === index ? { columnId, direction: next } : entry
|
|
155
|
+
);
|
|
226
156
|
}
|
|
227
|
-
function
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
return void 0;
|
|
157
|
+
function sortRows(rows, sort, columns) {
|
|
158
|
+
if (sort.length === 0) {
|
|
159
|
+
return rows;
|
|
231
160
|
}
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
161
|
+
const byId = new Map(columns.map((entry) => [entry.id, entry.column]));
|
|
162
|
+
const active = sort.filter((entry) => byId.has(entry.columnId));
|
|
163
|
+
if (active.length === 0) {
|
|
164
|
+
return rows;
|
|
235
165
|
}
|
|
236
|
-
|
|
166
|
+
const sorted = [...rows].sort((a, b) => {
|
|
167
|
+
for (const { columnId, direction } of active) {
|
|
168
|
+
const column = byId.get(columnId);
|
|
169
|
+
if (column === void 0) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const left = accessDotted(a.row, column.field);
|
|
173
|
+
const right = accessDotted(b.row, column.field);
|
|
174
|
+
const leftEmpty = isEmpty(left);
|
|
175
|
+
const rightEmpty = isEmpty(right);
|
|
176
|
+
if (leftEmpty || rightEmpty) {
|
|
177
|
+
if (leftEmpty && rightEmpty) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
return leftEmpty ? 1 : -1;
|
|
181
|
+
}
|
|
182
|
+
const result = compareValues(left, right, column.type);
|
|
183
|
+
if (result !== 0) {
|
|
184
|
+
return direction === "asc" ? result : -result;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return 0;
|
|
188
|
+
});
|
|
189
|
+
return sorted.map(
|
|
190
|
+
(row, index) => row.rowIndex === index ? row : { ...row, rowIndex: index }
|
|
191
|
+
);
|
|
237
192
|
}
|
|
238
193
|
|
|
239
|
-
// src/util/
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
return a.length === b.length && a.every((id, index) => id === b[index]);
|
|
194
|
+
// src/util/grouping.ts
|
|
195
|
+
function groupRowId(path) {
|
|
196
|
+
return JSON.stringify(path);
|
|
243
197
|
}
|
|
244
|
-
function
|
|
245
|
-
if (
|
|
246
|
-
return
|
|
198
|
+
function bucketKey(value) {
|
|
199
|
+
if (value === null || value === void 0) {
|
|
200
|
+
return "\0empty";
|
|
247
201
|
}
|
|
248
|
-
if (
|
|
249
|
-
return
|
|
202
|
+
if (value instanceof Date) {
|
|
203
|
+
return `\0date:${String(value.getTime())}`;
|
|
250
204
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
205
|
+
switch (typeof value) {
|
|
206
|
+
case "string":
|
|
207
|
+
case "number":
|
|
208
|
+
case "boolean":
|
|
209
|
+
case "bigint":
|
|
210
|
+
case "symbol":
|
|
211
|
+
return `\0${typeof value}:${String(value)}`;
|
|
212
|
+
case "function":
|
|
213
|
+
return "\0function";
|
|
214
|
+
default:
|
|
215
|
+
return `\0object:${JSON.stringify(value)}`;
|
|
256
216
|
}
|
|
257
|
-
return selection.length === 1 && selection[0] === id ? selection : [id];
|
|
258
217
|
}
|
|
259
|
-
function
|
|
260
|
-
|
|
261
|
-
|
|
218
|
+
function partitionByValue(rows, column, direction) {
|
|
219
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
220
|
+
for (const row of rows) {
|
|
221
|
+
const value = accessDotted(row.row, column.field);
|
|
222
|
+
const key = bucketKey(value);
|
|
223
|
+
const bucket = buckets.get(key);
|
|
224
|
+
if (bucket === void 0) {
|
|
225
|
+
buckets.set(key, { value, rows: [row] });
|
|
226
|
+
} else {
|
|
227
|
+
bucket.rows.push(row);
|
|
228
|
+
}
|
|
262
229
|
}
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
return
|
|
230
|
+
const groups = [...buckets.values()];
|
|
231
|
+
groups.sort((a, b) => {
|
|
232
|
+
const result = compareValues(a.value, b.value, column.type);
|
|
233
|
+
return direction === "asc" ? result : -result;
|
|
234
|
+
});
|
|
235
|
+
return groups;
|
|
236
|
+
}
|
|
237
|
+
function buildLevel(rows, levels, parentPath, expansion, byId, out) {
|
|
238
|
+
const level = levels[0];
|
|
239
|
+
if (level === void 0) {
|
|
240
|
+
out.push(...rows);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const column = byId.get(level.columnId);
|
|
244
|
+
if (column === void 0) {
|
|
245
|
+
out.push(...rows);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
const rest = levels.slice(1);
|
|
249
|
+
const groups = partitionByValue(rows, column, level.direction ?? "asc");
|
|
250
|
+
for (const group of groups) {
|
|
251
|
+
const path = [...parentPath, group.value];
|
|
252
|
+
const groupId = groupRowId(path);
|
|
253
|
+
const expanded = !expansion.includes(groupId);
|
|
254
|
+
const header = {
|
|
255
|
+
kind: "group",
|
|
256
|
+
groupId,
|
|
257
|
+
level: parentPath.length,
|
|
258
|
+
columnId: level.columnId,
|
|
259
|
+
value: group.value,
|
|
260
|
+
path,
|
|
261
|
+
expanded,
|
|
262
|
+
count: group.rows.length,
|
|
263
|
+
// Renumbered globally once `groupRows` has the whole flattened array —
|
|
264
|
+
// a header has no row of its own to carry a dataset position, so
|
|
265
|
+
// `datasetIndex` takes the same renumbered value `rowIndex` does.
|
|
266
|
+
rowIndex: -1,
|
|
267
|
+
datasetIndex: -1,
|
|
268
|
+
// Set by `withGroupAggregates`, downstream of grouping — empty here
|
|
269
|
+
// since `groupRows` itself never knows about `AggregateState`.
|
|
270
|
+
aggregates: /* @__PURE__ */ new Map()
|
|
271
|
+
};
|
|
272
|
+
out.push(header);
|
|
273
|
+
if (expanded) {
|
|
274
|
+
buildLevel(group.rows, rest, path, expansion, byId, out);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function groupRows(rows, groupBy, expansion, columns) {
|
|
279
|
+
if (groupBy.length === 0) {
|
|
280
|
+
return rows;
|
|
281
|
+
}
|
|
282
|
+
const byId = new Map(columns.map((entry) => [entry.id, entry.column]));
|
|
283
|
+
const active = groupBy.filter((entry) => byId.has(entry.columnId));
|
|
284
|
+
if (active.length === 0) {
|
|
285
|
+
return rows;
|
|
286
|
+
}
|
|
287
|
+
const out = [];
|
|
288
|
+
buildLevel(rows, active, [], expansion, byId, out);
|
|
289
|
+
return out.map((entry, index) => {
|
|
290
|
+
if ("kind" in entry) {
|
|
291
|
+
return entry.rowIndex === index && entry.datasetIndex === index ? entry : { ...entry, rowIndex: index, datasetIndex: index };
|
|
292
|
+
}
|
|
293
|
+
return entry.rowIndex === index ? entry : { ...entry, rowIndex: index };
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
function toggleGroupExpansion(expansion, groupId) {
|
|
297
|
+
return expansion.includes(groupId) ? expansion.filter((id) => id !== groupId) : [...expansion, groupId];
|
|
298
|
+
}
|
|
299
|
+
function expandAllGroups(expansion) {
|
|
300
|
+
return expansion.length === 0 ? expansion : [];
|
|
301
|
+
}
|
|
302
|
+
function collapseAllGroups(rows) {
|
|
303
|
+
return rows.filter(
|
|
304
|
+
(entry) => "kind" in entry && entry.kind === "group"
|
|
305
|
+
).map((entry) => entry.groupId);
|
|
306
|
+
}
|
|
307
|
+
function moveGroupByBefore(groupBy, columnId, beforeColumnId) {
|
|
308
|
+
if (columnId === beforeColumnId) {
|
|
309
|
+
return groupBy;
|
|
310
|
+
}
|
|
311
|
+
const without = groupBy.filter((entry) => entry.columnId !== columnId);
|
|
312
|
+
const moved = groupBy.find((entry) => entry.columnId === columnId) ?? {
|
|
313
|
+
columnId
|
|
314
|
+
};
|
|
315
|
+
const target = beforeColumnId === null ? without.length : without.findIndex((entry) => entry.columnId === beforeColumnId);
|
|
316
|
+
if (target === -1) {
|
|
317
|
+
return groupBy;
|
|
318
|
+
}
|
|
319
|
+
const next = [...without];
|
|
320
|
+
next.splice(target, 0, moved);
|
|
321
|
+
return next.length === groupBy.length && next.every((entry, index) => entry === groupBy[index]) ? groupBy : next;
|
|
322
|
+
}
|
|
323
|
+
function movesGroupBy(groupBy, columnId, beforeColumnId) {
|
|
324
|
+
return moveGroupByBefore(groupBy, columnId, beforeColumnId) !== groupBy;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// src/util/memoize.ts
|
|
328
|
+
function memoizeLast(compute) {
|
|
329
|
+
let last;
|
|
330
|
+
return (...args) => {
|
|
331
|
+
const previous = last;
|
|
332
|
+
if (previous !== void 0 && args.every((arg, index) => Object.is(arg, previous.args[index]))) {
|
|
333
|
+
return previous.result;
|
|
334
|
+
}
|
|
335
|
+
const result = compute(...args);
|
|
336
|
+
last = { args, result };
|
|
337
|
+
return result;
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// src/util/ordering.ts
|
|
342
|
+
function applyColumnOrder(columns, order) {
|
|
343
|
+
if (order.length === 0) {
|
|
344
|
+
return columns;
|
|
345
|
+
}
|
|
346
|
+
const byId = new Map(columns.map((column) => [getColumnId(column), column]));
|
|
347
|
+
const listed = order.map((id) => byId.get(id)).filter((column) => column !== void 0);
|
|
348
|
+
const taken = new Set(listed.map((column) => getColumnId(column)));
|
|
349
|
+
return [
|
|
350
|
+
...listed,
|
|
351
|
+
...columns.filter((column) => !taken.has(getColumnId(column)))
|
|
352
|
+
];
|
|
353
|
+
}
|
|
354
|
+
function resolveDataColumns(columns, order) {
|
|
355
|
+
return applyColumnOrder(columns, order).map((column) => ({
|
|
356
|
+
id: getColumnId(column),
|
|
357
|
+
column
|
|
358
|
+
}));
|
|
359
|
+
}
|
|
360
|
+
function moveColumnBefore(orderedIds, movedId, beforeId) {
|
|
361
|
+
if (movedId === beforeId || !orderedIds.includes(movedId)) {
|
|
362
|
+
return orderedIds;
|
|
363
|
+
}
|
|
364
|
+
const without = orderedIds.filter((id) => id !== movedId);
|
|
365
|
+
const target = beforeId === null ? without.length : without.indexOf(beforeId);
|
|
366
|
+
if (target === -1) {
|
|
367
|
+
return orderedIds;
|
|
368
|
+
}
|
|
369
|
+
const next = [...without];
|
|
370
|
+
next.splice(target, 0, movedId);
|
|
371
|
+
return next.every((id, index) => id === orderedIds[index]) ? orderedIds : next;
|
|
372
|
+
}
|
|
373
|
+
function movesColumn(orderedIds, movedId, beforeId) {
|
|
374
|
+
return moveColumnBefore(orderedIds, movedId, beforeId) !== orderedIds;
|
|
375
|
+
}
|
|
376
|
+
function resolveDropBefore(orderedIds, targetId, side) {
|
|
377
|
+
if (side === "before") {
|
|
378
|
+
return targetId;
|
|
379
|
+
}
|
|
380
|
+
const index = orderedIds.indexOf(targetId);
|
|
381
|
+
return index === -1 ? null : orderedIds[index + 1] ?? null;
|
|
382
|
+
}
|
|
383
|
+
function resolveKeyboardDropTarget(orderedIds, columnId, direction) {
|
|
384
|
+
const index = orderedIds.indexOf(columnId);
|
|
385
|
+
if (index === -1) {
|
|
386
|
+
return void 0;
|
|
387
|
+
}
|
|
388
|
+
const targetIndex = direction === -1 ? index - 1 : index + 2;
|
|
389
|
+
if (targetIndex < 0) {
|
|
390
|
+
return void 0;
|
|
391
|
+
}
|
|
392
|
+
return orderedIds[targetIndex] ?? null;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/util/paging.ts
|
|
396
|
+
function unitsOf(rows) {
|
|
397
|
+
const units = [];
|
|
398
|
+
let start = 0;
|
|
399
|
+
let openedByGroup = false;
|
|
400
|
+
for (let index = 0; index < rows.length; index++) {
|
|
401
|
+
const entry = rows[index];
|
|
402
|
+
if (entry === void 0) {
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
const isTopLevelHeader = "kind" in entry && entry.kind === "group" && entry.level === 0;
|
|
406
|
+
const isBoundary = index !== start && (isTopLevelHeader || !openedByGroup);
|
|
407
|
+
if (isBoundary) {
|
|
408
|
+
units.push({ start, end: index });
|
|
409
|
+
start = index;
|
|
410
|
+
openedByGroup = isTopLevelHeader;
|
|
411
|
+
} else if (index === start) {
|
|
412
|
+
openedByGroup = isTopLevelHeader;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (start < rows.length) {
|
|
416
|
+
units.push({ start, end: rows.length });
|
|
417
|
+
}
|
|
418
|
+
return units;
|
|
419
|
+
}
|
|
420
|
+
function paginateRows(rows, pagination) {
|
|
421
|
+
const pageSize = Math.max(1, pagination.pageSize);
|
|
422
|
+
const units = unitsOf(rows);
|
|
423
|
+
const pageCount = Math.ceil(units.length / pageSize);
|
|
424
|
+
if (pageCount === 0) {
|
|
425
|
+
return { rows: [], pageCount: 0, pageIndex: 0 };
|
|
426
|
+
}
|
|
427
|
+
const pageIndex = Math.min(Math.max(pagination.pageIndex, 0), pageCount - 1);
|
|
428
|
+
const firstUnit = units[pageIndex * pageSize];
|
|
429
|
+
const lastUnit = units[Math.min(pageIndex * pageSize + pageSize, units.length) - 1];
|
|
430
|
+
if (firstUnit === void 0 || lastUnit === void 0) {
|
|
431
|
+
return { rows: [], pageCount, pageIndex };
|
|
432
|
+
}
|
|
433
|
+
const page = rows.slice(firstUnit.start, lastUnit.end);
|
|
434
|
+
const renumbered = page.map(
|
|
435
|
+
(entry, index) => entry.rowIndex === index ? entry : { ...entry, rowIndex: index }
|
|
436
|
+
);
|
|
437
|
+
return { rows: renumbered, pageCount, pageIndex };
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// src/util/report.ts
|
|
441
|
+
function reportError(message) {
|
|
442
|
+
console.error(`GridKit: ${message}`);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// src/util/filterOperators.ts
|
|
446
|
+
var matchNothing = () => false;
|
|
447
|
+
function kindOf(type) {
|
|
448
|
+
switch (type) {
|
|
449
|
+
case "number":
|
|
450
|
+
case "decimal":
|
|
451
|
+
case "currency":
|
|
452
|
+
case "percent":
|
|
453
|
+
return "number";
|
|
454
|
+
case "boolean":
|
|
455
|
+
return "boolean";
|
|
456
|
+
case "date":
|
|
457
|
+
case "dateTime":
|
|
458
|
+
case "time":
|
|
459
|
+
return "date";
|
|
460
|
+
default:
|
|
461
|
+
return "text";
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
function isDateType(type) {
|
|
465
|
+
return kindOf(type) === "date";
|
|
466
|
+
}
|
|
467
|
+
function isEmptyCell(value) {
|
|
468
|
+
return value === null || value === void 0 || value === "";
|
|
469
|
+
}
|
|
470
|
+
function toDate(value) {
|
|
471
|
+
const date = value instanceof Date ? value : typeof value === "string" ? new Date(value) : void 0;
|
|
472
|
+
return date === void 0 || Number.isNaN(date.getTime()) ? void 0 : date;
|
|
473
|
+
}
|
|
474
|
+
function comparable(value, kind) {
|
|
475
|
+
switch (kind) {
|
|
476
|
+
case "number":
|
|
477
|
+
return typeof value === "number" && !Number.isNaN(value) ? value : void 0;
|
|
478
|
+
case "boolean":
|
|
479
|
+
return typeof value === "boolean" ? value : void 0;
|
|
480
|
+
case "date":
|
|
481
|
+
return toDate(value);
|
|
482
|
+
case "text":
|
|
483
|
+
return String(value).toLowerCase();
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
function isOperandOfKind(operand, kind) {
|
|
487
|
+
if (kind !== "text") {
|
|
488
|
+
return comparable(operand, kind) !== void 0;
|
|
489
|
+
}
|
|
490
|
+
return typeof operand === "string" || typeof operand === "number" || typeof operand === "boolean";
|
|
491
|
+
}
|
|
492
|
+
function compileComparison(operand, type) {
|
|
493
|
+
const kind = kindOf(type);
|
|
494
|
+
if (!isOperandOfKind(operand, kind)) {
|
|
495
|
+
return void 0;
|
|
496
|
+
}
|
|
497
|
+
const target = comparable(operand, kind);
|
|
498
|
+
return (value) => {
|
|
499
|
+
if (isEmptyCell(value)) {
|
|
500
|
+
return void 0;
|
|
501
|
+
}
|
|
502
|
+
const cell = comparable(value, kind);
|
|
503
|
+
return cell === void 0 ? void 0 : compareValues(cell, target, type);
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
function isScalarList(operand) {
|
|
507
|
+
return Array.isArray(operand);
|
|
508
|
+
}
|
|
509
|
+
function compileOrdering(operator, operand, type) {
|
|
510
|
+
const compare = compileComparison(operand, type);
|
|
511
|
+
if (compare === void 0) {
|
|
512
|
+
return matchNothing;
|
|
513
|
+
}
|
|
514
|
+
const where = (holds) => (value) => {
|
|
515
|
+
const result = compare(value);
|
|
516
|
+
return result !== void 0 && holds(result);
|
|
517
|
+
};
|
|
518
|
+
switch (operator) {
|
|
519
|
+
case "eq":
|
|
520
|
+
return where((result) => result === 0);
|
|
521
|
+
case "ne": {
|
|
522
|
+
const equals = where((result) => result === 0);
|
|
523
|
+
return (value) => !equals(value);
|
|
524
|
+
}
|
|
525
|
+
case "gt":
|
|
526
|
+
return where((result) => result > 0);
|
|
527
|
+
case "gte":
|
|
528
|
+
return where((result) => result >= 0);
|
|
529
|
+
case "lt":
|
|
530
|
+
return where((result) => result < 0);
|
|
531
|
+
case "lte":
|
|
532
|
+
return where((result) => result <= 0);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
function compileBetween(operand, type) {
|
|
536
|
+
if (!isScalarList(operand) || operand.length !== 2) {
|
|
537
|
+
return matchNothing;
|
|
538
|
+
}
|
|
539
|
+
const [low, high] = operand;
|
|
540
|
+
const fromLow = compileComparison(low, type);
|
|
541
|
+
const fromHigh = compileComparison(high, type);
|
|
542
|
+
if (fromLow === void 0 || fromHigh === void 0) {
|
|
543
|
+
return matchNothing;
|
|
544
|
+
}
|
|
545
|
+
return (value) => {
|
|
546
|
+
const aboveLow = fromLow(value);
|
|
547
|
+
const belowHigh = fromHigh(value);
|
|
548
|
+
return aboveLow !== void 0 && belowHigh !== void 0 && aboveLow >= 0 && belowHigh <= 0;
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
function filterValueKey(value, type) {
|
|
552
|
+
return bucketKey(isDateType(type) ? toDate(value) ?? value : value);
|
|
553
|
+
}
|
|
554
|
+
function compileIn(operand, type) {
|
|
555
|
+
if (!isScalarList(operand)) {
|
|
556
|
+
return matchNothing;
|
|
557
|
+
}
|
|
558
|
+
const keys = new Set(
|
|
559
|
+
operand.filter((scalar) => !isEmptyCell(scalar)).map((scalar) => filterValueKey(scalar, type))
|
|
560
|
+
);
|
|
561
|
+
return (value) => !isEmptyCell(value) && keys.has(filterValueKey(value, type));
|
|
562
|
+
}
|
|
563
|
+
function compileText(operator, operand) {
|
|
564
|
+
if (!isOperandOfKind(operand, "text")) {
|
|
565
|
+
return matchNothing;
|
|
566
|
+
}
|
|
567
|
+
const text = String(operand).toLowerCase();
|
|
568
|
+
const read = (value) => String(value).toLowerCase();
|
|
569
|
+
switch (operator) {
|
|
570
|
+
case "contains":
|
|
571
|
+
return (value) => !isEmptyCell(value) && read(value).includes(text);
|
|
572
|
+
case "notContains":
|
|
573
|
+
return (value) => isEmptyCell(value) || !read(value).includes(text);
|
|
574
|
+
case "startsWith":
|
|
575
|
+
return (value) => !isEmptyCell(value) && read(value).startsWith(text);
|
|
576
|
+
case "endsWith":
|
|
577
|
+
return (value) => !isEmptyCell(value) && read(value).endsWith(text);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
function compileOperatorEntry(entry, type) {
|
|
581
|
+
const { operator, value } = entry;
|
|
582
|
+
switch (operator) {
|
|
583
|
+
case "isEmpty":
|
|
584
|
+
return isEmptyCell;
|
|
585
|
+
case "isNotEmpty":
|
|
586
|
+
return (cell) => !isEmptyCell(cell);
|
|
587
|
+
case "contains":
|
|
588
|
+
case "notContains":
|
|
589
|
+
case "startsWith":
|
|
590
|
+
case "endsWith":
|
|
591
|
+
return compileText(operator, value);
|
|
592
|
+
case "between":
|
|
593
|
+
return compileBetween(value, type);
|
|
594
|
+
case "in":
|
|
595
|
+
return compileIn(value, type);
|
|
596
|
+
case "eq":
|
|
597
|
+
case "ne":
|
|
598
|
+
case "gt":
|
|
599
|
+
case "gte":
|
|
600
|
+
case "lt":
|
|
601
|
+
case "lte":
|
|
602
|
+
return compileOrdering(operator, value, type);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// src/util/serialize.ts
|
|
607
|
+
function serializeScalar(scalar) {
|
|
608
|
+
return scalar instanceof Date ? scalar.toISOString() : scalar;
|
|
609
|
+
}
|
|
610
|
+
function serializeOperand(operand) {
|
|
611
|
+
return isScalarList(operand) ? operand.map(serializeScalar) : serializeScalar(operand);
|
|
612
|
+
}
|
|
613
|
+
function serializeEntry(entry) {
|
|
614
|
+
if ("combinator" in entry) {
|
|
615
|
+
const entries = serializeFilter(entry.entries);
|
|
616
|
+
return entries.length === 0 && entry.entries.length > 0 ? void 0 : { combinator: entry.combinator, entries };
|
|
617
|
+
}
|
|
618
|
+
if ("predicate" in entry) {
|
|
619
|
+
reportError(
|
|
620
|
+
entry.columnId === void 0 ? "a predicate filter entry across every column cannot be serialised, so it is left out of the saved state." : `a predicate filter entry on \`${entry.columnId}\` cannot be serialised, so it is left out of the saved state.`
|
|
621
|
+
);
|
|
622
|
+
return void 0;
|
|
623
|
+
}
|
|
624
|
+
if ("operator" in entry) {
|
|
625
|
+
const { value, ...rest } = entry;
|
|
626
|
+
return value === void 0 ? rest : { ...rest, value: serializeOperand(value) };
|
|
627
|
+
}
|
|
628
|
+
if ("value" in entry) {
|
|
629
|
+
const { value, ...rest } = entry;
|
|
630
|
+
return {
|
|
631
|
+
...rest,
|
|
632
|
+
value: value instanceof Date ? value.toISOString() : value
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
return entry;
|
|
636
|
+
}
|
|
637
|
+
function serializeFilter(filter) {
|
|
638
|
+
return filter.map((entry) => serializeEntry(entry)).filter((entry) => entry !== void 0);
|
|
639
|
+
}
|
|
640
|
+
function serializeGridState(state) {
|
|
641
|
+
return { ...state, filter: serializeFilter(state.filter) };
|
|
642
|
+
}
|
|
643
|
+
function reviveScalar(scalar, type) {
|
|
644
|
+
return isDateType(type) && typeof scalar === "string" ? toDate(scalar) ?? scalar : scalar;
|
|
645
|
+
}
|
|
646
|
+
function reviveOperand(operand, type) {
|
|
647
|
+
return isScalarList(operand) ? operand.map((scalar) => reviveScalar(scalar, type)) : reviveScalar(operand, type);
|
|
648
|
+
}
|
|
649
|
+
function reviveEntry(entry, typeOf) {
|
|
650
|
+
if ("combinator" in entry) {
|
|
651
|
+
return {
|
|
652
|
+
combinator: entry.combinator,
|
|
653
|
+
entries: reviveEntries(entry.entries, typeOf)
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
if ("operator" in entry) {
|
|
657
|
+
const { value, ...rest } = entry;
|
|
658
|
+
return value === void 0 ? rest : { ...rest, value: reviveOperand(value, typeOf(entry.columnId)) };
|
|
659
|
+
}
|
|
660
|
+
if ("value" in entry) {
|
|
661
|
+
const { value, ...rest } = entry;
|
|
662
|
+
return {
|
|
663
|
+
...rest,
|
|
664
|
+
value: typeof value === "string" ? new Date(value) : value
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
return entry;
|
|
668
|
+
}
|
|
669
|
+
function reviveEntries(filter, typeOf) {
|
|
670
|
+
return filter.map((entry) => reviveEntry(entry, typeOf));
|
|
671
|
+
}
|
|
672
|
+
function reviveFilter(filter, columns) {
|
|
673
|
+
const types = new Map(columns.map((entry) => [entry.id, entry.column.type]));
|
|
674
|
+
return reviveEntries(filter, (columnId) => types.get(columnId));
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// src/util/sizing.ts
|
|
678
|
+
var DEFAULT_COLUMN_SIZES = {
|
|
679
|
+
width: 150,
|
|
680
|
+
minWidth: 40,
|
|
681
|
+
maxWidth: Number.POSITIVE_INFINITY
|
|
682
|
+
};
|
|
683
|
+
var KEYBOARD_STEP = 10;
|
|
684
|
+
function withDefaults(defaults) {
|
|
685
|
+
return { ...DEFAULT_COLUMN_SIZES, ...defaults };
|
|
686
|
+
}
|
|
687
|
+
function resolveColumnConstraints(column, defaults) {
|
|
688
|
+
const resolved = withDefaults(defaults);
|
|
689
|
+
return {
|
|
690
|
+
minWidth: column.minWidth ?? resolved.minWidth,
|
|
691
|
+
maxWidth: column.maxWidth ?? resolved.maxWidth
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
function clampColumnWidth(width, constraints) {
|
|
695
|
+
return Math.min(Math.max(width, constraints.minWidth), constraints.maxWidth);
|
|
696
|
+
}
|
|
697
|
+
function resolveColumnWidths(columns, sizing, options) {
|
|
698
|
+
const resolved = withDefaults(options?.sizes);
|
|
699
|
+
return columns.map((column) => {
|
|
700
|
+
const id = getColumnId(column);
|
|
701
|
+
const stored = sizing[id];
|
|
702
|
+
return {
|
|
703
|
+
column,
|
|
704
|
+
id,
|
|
705
|
+
sized: stored !== void 0,
|
|
706
|
+
width: clampColumnWidth(
|
|
707
|
+
stored ?? column.width ?? resolved.width,
|
|
708
|
+
resolveColumnConstraints(column, resolved)
|
|
709
|
+
),
|
|
710
|
+
label: resolveColumnLabel(column),
|
|
711
|
+
resizable: column.resizable ?? options?.resizable ?? false,
|
|
712
|
+
reorderable: column.reorderable ?? options?.reorderable ?? false,
|
|
713
|
+
groupByDraggable: column.groupByDraggable ?? options?.groupByDraggable ?? false,
|
|
714
|
+
alignment: column.alignment ?? alignmentForType(column.type ?? "string")
|
|
715
|
+
};
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
function totalColumnWidth(resolved) {
|
|
719
|
+
return resolved.reduce((total, entry) => total + entry.width, 0);
|
|
720
|
+
}
|
|
721
|
+
function beginColumnResize(column, startWidth, startPosition, defaults) {
|
|
722
|
+
return {
|
|
723
|
+
columnId: getColumnId(column),
|
|
724
|
+
startWidth,
|
|
725
|
+
startPosition,
|
|
726
|
+
constraints: resolveColumnConstraints(column, defaults)
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
function applyColumnResize(session, position) {
|
|
730
|
+
return clampColumnWidth(
|
|
731
|
+
session.startWidth + (position - session.startPosition),
|
|
732
|
+
session.constraints
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
function reachedBound(width, item) {
|
|
736
|
+
return item.bound > item.width ? width >= item.bound : width <= item.bound;
|
|
737
|
+
}
|
|
738
|
+
function redistribute(entries, remaining) {
|
|
739
|
+
for (; ; ) {
|
|
740
|
+
const movable = entries.filter(
|
|
741
|
+
(item) => item.adjustable && !reachedBound(item.width, item)
|
|
742
|
+
);
|
|
743
|
+
const left = remaining(entries.reduce((t, i) => t + i.width, 0));
|
|
744
|
+
if (left <= 0 || movable.length === 0) {
|
|
745
|
+
break;
|
|
746
|
+
}
|
|
747
|
+
const base = movable.reduce((total, item) => total + item.width, 0);
|
|
748
|
+
let clamped = false;
|
|
749
|
+
for (const item of movable) {
|
|
750
|
+
const share = base === 0 ? left / movable.length : left * item.width / base;
|
|
751
|
+
const next = item.width + (item.bound > item.width ? share : -share);
|
|
752
|
+
if (reachedBound(next, item)) {
|
|
753
|
+
item.width = item.bound;
|
|
754
|
+
clamped = true;
|
|
755
|
+
} else {
|
|
756
|
+
item.width = next;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
if (!clamped) {
|
|
760
|
+
break;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
function fitColumnsToWidth(resolved, availableWidth, defaults) {
|
|
765
|
+
const target = Math.floor(availableWidth);
|
|
766
|
+
const total = totalColumnWidth(resolved);
|
|
767
|
+
if (resolved.length === 0 || total === target) {
|
|
768
|
+
return resolved;
|
|
769
|
+
}
|
|
770
|
+
const growing = total < target;
|
|
771
|
+
const entries = resolved.map((entry) => {
|
|
772
|
+
const constraints = resolveColumnConstraints(entry.column, defaults);
|
|
773
|
+
return {
|
|
774
|
+
entry,
|
|
775
|
+
width: entry.width,
|
|
776
|
+
bound: growing ? constraints.maxWidth : constraints.minWidth,
|
|
777
|
+
adjustable: !entry.sized
|
|
778
|
+
};
|
|
779
|
+
});
|
|
780
|
+
if (!growing) {
|
|
781
|
+
const floor = entries.reduce(
|
|
782
|
+
(sum, item) => sum + (item.adjustable ? item.bound : item.width),
|
|
783
|
+
0
|
|
784
|
+
);
|
|
785
|
+
if (floor > target) {
|
|
786
|
+
return resolved;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
redistribute(entries, (sum) => growing ? target - sum : sum - target);
|
|
790
|
+
const rounded = entries.map(
|
|
791
|
+
(item) => growing ? Math.floor(item.width) : Math.ceil(item.width)
|
|
792
|
+
);
|
|
793
|
+
let remainder = target - rounded.reduce((total2, width) => total2 + width, 0);
|
|
794
|
+
return entries.map((item, index) => {
|
|
795
|
+
let width = rounded[index] ?? item.width;
|
|
796
|
+
const hasRoom = growing ? width < item.bound : width > item.bound;
|
|
797
|
+
if (remainder !== 0 && item.adjustable && hasRoom) {
|
|
798
|
+
width += growing ? 1 : -1;
|
|
799
|
+
remainder += growing ? -1 : 1;
|
|
800
|
+
}
|
|
801
|
+
return { ...item.entry, width };
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
function sizeColumnToContent(measuredWidth, constraints, padding = 2) {
|
|
805
|
+
return clampColumnWidth(Math.ceil(measuredWidth + padding), constraints);
|
|
806
|
+
}
|
|
807
|
+
function revertColumnSize(current, base, columnId) {
|
|
808
|
+
const next = Object.fromEntries(
|
|
809
|
+
Object.entries(current).filter(([id]) => id !== columnId)
|
|
810
|
+
);
|
|
811
|
+
if (columnId in base) {
|
|
812
|
+
next[columnId] = base[columnId];
|
|
813
|
+
}
|
|
814
|
+
return next;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// src/util/aggregation.ts
|
|
818
|
+
function isEmpty2(value) {
|
|
819
|
+
return value === null || value === void 0;
|
|
820
|
+
}
|
|
821
|
+
function toNumber(value) {
|
|
822
|
+
return Number(value);
|
|
823
|
+
}
|
|
824
|
+
function distinctKey(value) {
|
|
825
|
+
if (isEmpty2(value)) {
|
|
826
|
+
return "\0empty";
|
|
827
|
+
}
|
|
828
|
+
if (value instanceof Date) {
|
|
829
|
+
return `\0date:${String(value.getTime())}`;
|
|
830
|
+
}
|
|
831
|
+
switch (typeof value) {
|
|
832
|
+
case "string":
|
|
833
|
+
case "number":
|
|
834
|
+
case "boolean":
|
|
835
|
+
case "bigint":
|
|
836
|
+
case "symbol":
|
|
837
|
+
return `\0${typeof value}:${String(value)}`;
|
|
838
|
+
case "function":
|
|
839
|
+
return "\0function";
|
|
840
|
+
default:
|
|
841
|
+
return `\0object:${JSON.stringify(value)}`;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
function reduceBuiltIn(rows, fn, column) {
|
|
845
|
+
if (fn === "count") {
|
|
846
|
+
return rows.length;
|
|
847
|
+
}
|
|
848
|
+
const field = column?.column.field;
|
|
849
|
+
const values = field === void 0 ? [] : rows.map((row) => accessDotted(row, field));
|
|
850
|
+
switch (fn) {
|
|
851
|
+
case "sum": {
|
|
852
|
+
let total = 0;
|
|
853
|
+
for (const value of values) {
|
|
854
|
+
if (isEmpty2(value)) continue;
|
|
855
|
+
total += toNumber(value);
|
|
856
|
+
}
|
|
857
|
+
return total;
|
|
858
|
+
}
|
|
859
|
+
case "avg": {
|
|
860
|
+
const present = values.filter((value) => !isEmpty2(value));
|
|
861
|
+
if (present.length === 0) return void 0;
|
|
862
|
+
const total = present.reduce(
|
|
863
|
+
(acc, value) => acc + toNumber(value),
|
|
864
|
+
0
|
|
865
|
+
);
|
|
866
|
+
return total / present.length;
|
|
867
|
+
}
|
|
868
|
+
case "min":
|
|
869
|
+
case "max": {
|
|
870
|
+
const type = column?.column.type;
|
|
871
|
+
let result;
|
|
872
|
+
for (const value of values) {
|
|
873
|
+
if (isEmpty2(value)) continue;
|
|
874
|
+
if (result === void 0) {
|
|
875
|
+
result = value;
|
|
876
|
+
continue;
|
|
877
|
+
}
|
|
878
|
+
const comparison = compareValues(value, result, type);
|
|
879
|
+
if (fn === "min" ? comparison < 0 : comparison > 0) {
|
|
880
|
+
result = value;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
return result;
|
|
884
|
+
}
|
|
885
|
+
case "countDistinct": {
|
|
886
|
+
const seen = /* @__PURE__ */ new Set();
|
|
887
|
+
for (const value of values) {
|
|
888
|
+
if (isEmpty2(value)) continue;
|
|
889
|
+
seen.add(distinctKey(value));
|
|
890
|
+
}
|
|
891
|
+
return seen.size;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
function keyOf(spec) {
|
|
896
|
+
return spec.id ?? spec.columnId;
|
|
897
|
+
}
|
|
898
|
+
function computeAggregates(rows, specs, columns) {
|
|
899
|
+
const results = /* @__PURE__ */ new Map();
|
|
900
|
+
if (specs.length === 0) {
|
|
901
|
+
return results;
|
|
902
|
+
}
|
|
903
|
+
const byId = new Map(columns.map((entry) => [entry.id, entry]));
|
|
904
|
+
for (const spec of specs) {
|
|
905
|
+
const value = typeof spec.fn === "function" ? spec.fn(rows) : reduceBuiltIn(rows, spec.fn, byId.get(spec.columnId));
|
|
906
|
+
results.set(keyOf(spec), value);
|
|
907
|
+
}
|
|
908
|
+
return results;
|
|
909
|
+
}
|
|
910
|
+
function leafDescendantsOf(flatRows, path, groupBy, byId) {
|
|
911
|
+
const keys = path.map((value) => bucketKey(value));
|
|
912
|
+
return flatRows.filter(
|
|
913
|
+
(row) => keys.every((key, level) => {
|
|
914
|
+
const columnId = groupBy[level]?.columnId;
|
|
915
|
+
const column = columnId === void 0 ? void 0 : byId.get(columnId);
|
|
916
|
+
if (column === void 0) return false;
|
|
917
|
+
return bucketKey(accessDotted(row.row, column.field)) === key;
|
|
918
|
+
})
|
|
919
|
+
).map((row) => row.row);
|
|
920
|
+
}
|
|
921
|
+
function appendAggregatedSpan(displayRows, start, levelFloor, flatRows, groupBy, byId, specs, columns, display, out) {
|
|
922
|
+
let index = start;
|
|
923
|
+
while (index < displayRows.length) {
|
|
924
|
+
const entry = displayRows[index];
|
|
925
|
+
if (entry === void 0) {
|
|
926
|
+
index++;
|
|
927
|
+
continue;
|
|
928
|
+
}
|
|
929
|
+
if (levelFloor !== null && "kind" in entry && entry.kind === "group" && entry.level <= levelFloor) {
|
|
930
|
+
break;
|
|
931
|
+
}
|
|
932
|
+
index++;
|
|
933
|
+
if (!("kind" in entry) || entry.kind !== "group") {
|
|
934
|
+
out.push(entry);
|
|
935
|
+
continue;
|
|
936
|
+
}
|
|
937
|
+
const leaves = leafDescendantsOf(flatRows, entry.path, groupBy, byId);
|
|
938
|
+
const aggregates = computeAggregates(leaves, specs, columns);
|
|
939
|
+
const withAggregates = { ...entry, aggregates };
|
|
940
|
+
out.push(withAggregates);
|
|
941
|
+
index = appendAggregatedSpan(
|
|
942
|
+
displayRows,
|
|
943
|
+
index,
|
|
944
|
+
entry.level,
|
|
945
|
+
flatRows,
|
|
946
|
+
groupBy,
|
|
947
|
+
byId,
|
|
948
|
+
specs,
|
|
949
|
+
columns,
|
|
950
|
+
display,
|
|
951
|
+
out
|
|
952
|
+
);
|
|
953
|
+
if (display === "row") {
|
|
954
|
+
const summary = {
|
|
955
|
+
kind: "group-summary",
|
|
956
|
+
groupId: entry.groupId,
|
|
957
|
+
level: entry.level,
|
|
958
|
+
rowIndex: -1,
|
|
959
|
+
datasetIndex: -1,
|
|
960
|
+
aggregates
|
|
961
|
+
};
|
|
962
|
+
out.push(summary);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
return index;
|
|
966
|
+
}
|
|
967
|
+
function withGroupAggregates(displayRows, flatRows, groupBy, specs, columns, display = "inline") {
|
|
968
|
+
if (specs.length === 0) {
|
|
969
|
+
return displayRows;
|
|
970
|
+
}
|
|
971
|
+
const byId = new Map(columns.map((entry) => [entry.id, entry.column]));
|
|
972
|
+
const out = [];
|
|
973
|
+
appendAggregatedSpan(
|
|
974
|
+
displayRows,
|
|
975
|
+
0,
|
|
976
|
+
null,
|
|
977
|
+
flatRows,
|
|
978
|
+
groupBy,
|
|
979
|
+
byId,
|
|
980
|
+
specs,
|
|
981
|
+
columns,
|
|
982
|
+
display,
|
|
983
|
+
out
|
|
984
|
+
);
|
|
985
|
+
return out.map((entry, position) => {
|
|
986
|
+
if ("kind" in entry) {
|
|
987
|
+
return entry.rowIndex === position && entry.datasetIndex === position ? entry : { ...entry, rowIndex: position, datasetIndex: position };
|
|
988
|
+
}
|
|
989
|
+
return entry.rowIndex === position ? entry : { ...entry, rowIndex: position };
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// src/util/filtering.ts
|
|
994
|
+
function parseFilterPattern(query) {
|
|
995
|
+
const start = query.startsWith("%");
|
|
996
|
+
const end = query.endsWith("%");
|
|
997
|
+
let text = query;
|
|
998
|
+
if (start) {
|
|
999
|
+
text = text.slice(1);
|
|
1000
|
+
}
|
|
1001
|
+
if (end) {
|
|
1002
|
+
text = text.slice(0, -1);
|
|
1003
|
+
}
|
|
1004
|
+
text = text.toLowerCase();
|
|
1005
|
+
if (start && end) {
|
|
1006
|
+
return { mode: "contains", text };
|
|
1007
|
+
}
|
|
1008
|
+
if (start) {
|
|
1009
|
+
return { mode: "endsWith", text };
|
|
1010
|
+
}
|
|
1011
|
+
if (end) {
|
|
1012
|
+
return { mode: "startsWith", text };
|
|
1013
|
+
}
|
|
1014
|
+
return { mode: "exact", text };
|
|
1015
|
+
}
|
|
1016
|
+
function isEmptyValue(value) {
|
|
1017
|
+
return value === null || value === void 0;
|
|
1018
|
+
}
|
|
1019
|
+
function compileFilterQuery(query) {
|
|
1020
|
+
const { mode, text } = parseFilterPattern(query);
|
|
1021
|
+
return (value) => {
|
|
1022
|
+
const candidate = (isEmptyValue(value) ? "" : String(value)).toLowerCase();
|
|
1023
|
+
switch (mode) {
|
|
1024
|
+
case "exact":
|
|
1025
|
+
return candidate === text;
|
|
1026
|
+
case "contains":
|
|
1027
|
+
return candidate.includes(text);
|
|
1028
|
+
case "startsWith":
|
|
1029
|
+
return candidate.startsWith(text);
|
|
1030
|
+
case "endsWith":
|
|
1031
|
+
return candidate.endsWith(text);
|
|
1032
|
+
}
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
function matchesTypedValue(value, target, type) {
|
|
1036
|
+
if (typeof target === "number") {
|
|
1037
|
+
if (type !== "number" && type !== "decimal" && type !== "currency" && type !== "percent") {
|
|
1038
|
+
return false;
|
|
1039
|
+
}
|
|
1040
|
+
return typeof value === "number" && value === target;
|
|
1041
|
+
}
|
|
1042
|
+
if (typeof target === "boolean") {
|
|
1043
|
+
if (type !== "boolean") {
|
|
1044
|
+
return false;
|
|
1045
|
+
}
|
|
1046
|
+
return typeof value === "boolean" && value === target;
|
|
1047
|
+
}
|
|
1048
|
+
if (type !== "date" && type !== "dateTime" && type !== "time") {
|
|
1049
|
+
return false;
|
|
1050
|
+
}
|
|
1051
|
+
return value instanceof Date && value.getTime() === target.getTime();
|
|
1052
|
+
}
|
|
1053
|
+
function isEntryUsable(entry, byId) {
|
|
1054
|
+
if ("combinator" in entry) {
|
|
1055
|
+
return true;
|
|
1056
|
+
}
|
|
1057
|
+
return entry.columnId === void 0 || byId.has(entry.columnId);
|
|
1058
|
+
}
|
|
1059
|
+
function compileFilterEntry(entry, byId, columns) {
|
|
1060
|
+
if ("combinator" in entry) {
|
|
1061
|
+
const tests = entry.entries.filter((nested) => isEntryUsable(nested, byId)).map((nested) => compileFilterEntry(nested, byId, columns));
|
|
1062
|
+
return entry.combinator === "and" ? (row) => tests.every((test2) => test2(row)) : (row) => tests.some((test2) => test2(row));
|
|
1063
|
+
}
|
|
1064
|
+
if ("predicate" in entry) {
|
|
1065
|
+
const { columnId: columnId2, predicate } = entry;
|
|
1066
|
+
const column = columnId2 === void 0 ? void 0 : byId.get(columnId2);
|
|
1067
|
+
return (row) => predicate(
|
|
1068
|
+
column === void 0 ? void 0 : accessDotted(row, column.field),
|
|
1069
|
+
row
|
|
1070
|
+
);
|
|
267
1071
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
if (mode !== "multiple") {
|
|
276
|
-
return selection;
|
|
1072
|
+
if ("operator" in entry) {
|
|
1073
|
+
const column = byId.get(entry.columnId);
|
|
1074
|
+
if (column === void 0) {
|
|
1075
|
+
return () => false;
|
|
1076
|
+
}
|
|
1077
|
+
const test2 = compileOperatorEntry(entry, column.type);
|
|
1078
|
+
return (row) => test2(accessDotted(row, column.field));
|
|
277
1079
|
}
|
|
278
|
-
|
|
1080
|
+
if ("value" in entry) {
|
|
1081
|
+
const { columnId: columnId2, value } = entry;
|
|
1082
|
+
if (columnId2 !== void 0) {
|
|
1083
|
+
const column = byId.get(columnId2);
|
|
1084
|
+
if (column === void 0) {
|
|
1085
|
+
return () => false;
|
|
1086
|
+
}
|
|
1087
|
+
return (row) => matchesTypedValue(accessDotted(row, column.field), value, column.type);
|
|
1088
|
+
}
|
|
1089
|
+
return (row) => columns.some(
|
|
1090
|
+
(entry2) => matchesTypedValue(
|
|
1091
|
+
accessDotted(row, entry2.column.field),
|
|
1092
|
+
value,
|
|
1093
|
+
entry2.column.type
|
|
1094
|
+
)
|
|
1095
|
+
);
|
|
1096
|
+
}
|
|
1097
|
+
const { columnId, query } = entry;
|
|
1098
|
+
const test = compileFilterQuery(query);
|
|
1099
|
+
if (columnId !== void 0) {
|
|
1100
|
+
const column = byId.get(columnId);
|
|
1101
|
+
if (column === void 0) {
|
|
1102
|
+
return () => false;
|
|
1103
|
+
}
|
|
1104
|
+
return (row) => test(accessDotted(row, column.field));
|
|
1105
|
+
}
|
|
1106
|
+
return (row) => columns.some((entry2) => test(accessDotted(row, entry2.column.field)));
|
|
279
1107
|
}
|
|
280
|
-
function
|
|
281
|
-
|
|
1108
|
+
function compileFilter(filter, columns) {
|
|
1109
|
+
const byId = new Map(columns.map((entry) => [entry.id, entry.column]));
|
|
1110
|
+
const compiled = filter.filter((entry) => isEntryUsable(entry, byId)).map((entry) => compileFilterEntry(entry, byId, columns));
|
|
1111
|
+
return compiled.length === 0 ? void 0 : (row) => compiled.every((test) => test(row));
|
|
282
1112
|
}
|
|
283
|
-
function
|
|
284
|
-
|
|
285
|
-
return NO_CHANGE;
|
|
286
|
-
}
|
|
287
|
-
const before = new Set(previous);
|
|
288
|
-
const after = new Set(next);
|
|
289
|
-
return {
|
|
290
|
-
added: next.filter((id) => !before.has(id)),
|
|
291
|
-
removed: previous.filter((id) => !after.has(id))
|
|
292
|
-
};
|
|
1113
|
+
function matchesQuery(value, query) {
|
|
1114
|
+
return compileFilterQuery(query)(value);
|
|
293
1115
|
}
|
|
294
|
-
function
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
return
|
|
1116
|
+
function filterQueryFor(filter, columnId) {
|
|
1117
|
+
const entry = filter.find(
|
|
1118
|
+
(candidate) => "query" in candidate && candidate.columnId === columnId
|
|
1119
|
+
);
|
|
1120
|
+
return entry?.query ?? null;
|
|
299
1121
|
}
|
|
300
|
-
function
|
|
301
|
-
|
|
302
|
-
|
|
1122
|
+
function setColumnFilter(filter, columnId, query) {
|
|
1123
|
+
const index = filter.findIndex(
|
|
1124
|
+
(entry) => !("combinator" in entry) && entry.columnId === columnId
|
|
1125
|
+
);
|
|
1126
|
+
if (query === "") {
|
|
1127
|
+
return index === -1 ? filter : filter.filter((_entry, i) => i !== index);
|
|
303
1128
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (mode === false) {
|
|
308
|
-
return selection;
|
|
1129
|
+
const next = columnId === void 0 ? { query } : { columnId, query };
|
|
1130
|
+
if (index === -1) {
|
|
1131
|
+
return [...filter, next];
|
|
309
1132
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if (event.shiftKey) {
|
|
314
|
-
return "range";
|
|
1133
|
+
const existing = filter[index];
|
|
1134
|
+
if (existing !== void 0 && "query" in existing && existing.query === query) {
|
|
1135
|
+
return filter;
|
|
315
1136
|
}
|
|
316
|
-
return
|
|
1137
|
+
return filter.map((entry, i) => i === index ? next : entry);
|
|
317
1138
|
}
|
|
318
|
-
function
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
1139
|
+
function clearAllFilters(filter) {
|
|
1140
|
+
return filter.length === 0 ? filter : [];
|
|
1141
|
+
}
|
|
1142
|
+
function filterRows(rows, filter, columns) {
|
|
1143
|
+
if (filter.length === 0) {
|
|
1144
|
+
return rows;
|
|
1145
|
+
}
|
|
1146
|
+
const test = compileFilter(filter, columns);
|
|
1147
|
+
if (test === void 0) {
|
|
1148
|
+
return rows;
|
|
328
1149
|
}
|
|
1150
|
+
const filtered = rows.filter((row) => test(row.row));
|
|
1151
|
+
return filtered.map(
|
|
1152
|
+
(row, index) => row.rowIndex === index ? row : { ...row, rowIndex: index }
|
|
1153
|
+
);
|
|
329
1154
|
}
|
|
330
1155
|
|
|
331
|
-
// src/util/
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
minWidth: 40,
|
|
335
|
-
maxWidth: Number.POSITIVE_INFINITY
|
|
336
|
-
};
|
|
337
|
-
var KEYBOARD_STEP = 10;
|
|
338
|
-
function withDefaults(defaults) {
|
|
339
|
-
return { ...DEFAULT_COLUMN_SIZES, ...defaults };
|
|
1156
|
+
// src/util/rows.ts
|
|
1157
|
+
function resolveShownRows(rows, filter, sort, columns) {
|
|
1158
|
+
return sortRows(filterRows(rows, filter, columns), sort, columns);
|
|
340
1159
|
}
|
|
341
|
-
function
|
|
342
|
-
|
|
1160
|
+
function resolveRows(rowsById, ids) {
|
|
1161
|
+
return ids.map((id) => rowsById.get(id)).filter((row) => row !== void 0);
|
|
1162
|
+
}
|
|
1163
|
+
function resolveColumns(columns, ids) {
|
|
1164
|
+
return ids.map((id) => {
|
|
1165
|
+
const columnIndex = columns.findIndex((entry) => entry.id === id);
|
|
1166
|
+
const column = columns[columnIndex];
|
|
1167
|
+
return column === void 0 ? void 0 : { columnId: id, column, columnIndex };
|
|
1168
|
+
}).filter((column) => column !== void 0);
|
|
1169
|
+
}
|
|
1170
|
+
function resolveCell(rowsById, columns, cell) {
|
|
1171
|
+
if (cell === null) {
|
|
1172
|
+
return null;
|
|
1173
|
+
}
|
|
1174
|
+
const entry = rowsById.get(cell.rowId);
|
|
1175
|
+
const columnIndex = columns.findIndex(
|
|
1176
|
+
(candidate) => candidate.id === cell.columnId
|
|
1177
|
+
);
|
|
1178
|
+
const column = columns[columnIndex];
|
|
1179
|
+
if (entry === void 0 || column === void 0) {
|
|
1180
|
+
return null;
|
|
1181
|
+
}
|
|
343
1182
|
return {
|
|
344
|
-
|
|
345
|
-
|
|
1183
|
+
rowId: cell.rowId,
|
|
1184
|
+
columnId: cell.columnId,
|
|
1185
|
+
row: entry.row,
|
|
1186
|
+
column,
|
|
1187
|
+
rowIndex: entry.rowIndex,
|
|
1188
|
+
columnIndex,
|
|
1189
|
+
value: accessDotted(entry.row, column.column.field)
|
|
346
1190
|
};
|
|
347
1191
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
const resolved = withDefaults(options?.sizes);
|
|
353
|
-
return columns.map((column) => {
|
|
354
|
-
const id = getColumnId(column);
|
|
355
|
-
const stored = sizing[id];
|
|
356
|
-
return {
|
|
357
|
-
column,
|
|
358
|
-
id,
|
|
359
|
-
sized: stored !== void 0,
|
|
360
|
-
width: clampColumnWidth(
|
|
361
|
-
stored ?? column.width ?? resolved.width,
|
|
362
|
-
resolveColumnConstraints(column, resolved)
|
|
363
|
-
),
|
|
364
|
-
label: resolveColumnLabel(column),
|
|
365
|
-
resizable: column.resizable ?? options?.resizable ?? false,
|
|
366
|
-
reorderable: column.reorderable ?? options?.reorderable ?? false,
|
|
367
|
-
groupByDraggable: column.groupByDraggable ?? options?.groupByDraggable ?? false,
|
|
368
|
-
alignment: column.alignment ?? alignmentForType(column.type ?? "string")
|
|
369
|
-
};
|
|
370
|
-
});
|
|
1192
|
+
|
|
1193
|
+
// src/util/visibility.ts
|
|
1194
|
+
function isColumnVisible(column, visibility) {
|
|
1195
|
+
return visibility[getColumnId(column)] ?? column.hidden !== true;
|
|
371
1196
|
}
|
|
372
|
-
function
|
|
373
|
-
|
|
1197
|
+
function applyColumnVisibility(columns, visibility) {
|
|
1198
|
+
const visible = columns.filter(
|
|
1199
|
+
(entry) => isColumnVisible(entry.column, visibility)
|
|
1200
|
+
);
|
|
1201
|
+
return visible.length === columns.length ? columns : visible;
|
|
374
1202
|
}
|
|
375
|
-
function
|
|
1203
|
+
function setColumnVisibility(columns, visibility, columnIds, visible) {
|
|
1204
|
+
const requested = new Set(columnIds);
|
|
1205
|
+
const changed = columns.filter(
|
|
1206
|
+
(entry) => requested.has(entry.id) && isColumnVisible(entry.column, visibility) !== visible
|
|
1207
|
+
);
|
|
1208
|
+
if (changed.length === 0) {
|
|
1209
|
+
return visibility;
|
|
1210
|
+
}
|
|
376
1211
|
return {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
startPosition,
|
|
380
|
-
constraints: resolveColumnConstraints(column, defaults)
|
|
1212
|
+
...visibility,
|
|
1213
|
+
...Object.fromEntries(changed.map((entry) => [entry.id, visible]))
|
|
381
1214
|
};
|
|
382
1215
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
1216
|
+
|
|
1217
|
+
// src/util/view.ts
|
|
1218
|
+
var NO_AGGREGATES = [];
|
|
1219
|
+
function identifyRows(rows, getRowId) {
|
|
1220
|
+
return rows.map((row, rowIndex) => ({
|
|
1221
|
+
rowId: resolveRowId(row, rowIndex, getRowId),
|
|
1222
|
+
row,
|
|
1223
|
+
rowIndex,
|
|
1224
|
+
datasetIndex: rowIndex
|
|
1225
|
+
}));
|
|
1226
|
+
}
|
|
1227
|
+
function sizeColumns(visibleColumns, sizing, sizes, resizable, reorderable, groupByDraggable, resizeMode, viewportWidth) {
|
|
1228
|
+
const widths = resolveColumnWidths(
|
|
1229
|
+
visibleColumns.map((entry) => entry.column),
|
|
1230
|
+
sizing,
|
|
1231
|
+
{ sizes, resizable, reorderable, groupByDraggable }
|
|
387
1232
|
);
|
|
1233
|
+
return resizeMode === "fit" && viewportWidth !== null ? fitColumnsToWidth(widths, viewportWidth, sizes) : widths;
|
|
388
1234
|
}
|
|
389
|
-
function
|
|
390
|
-
return
|
|
1235
|
+
function totalAggregates(rows, aggregates, dataColumns) {
|
|
1236
|
+
return computeAggregates(
|
|
1237
|
+
rows.map((entry) => entry.row),
|
|
1238
|
+
aggregates,
|
|
1239
|
+
dataColumns
|
|
1240
|
+
);
|
|
391
1241
|
}
|
|
392
|
-
function
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
1242
|
+
function paginate(displayRows, paginated, pagination) {
|
|
1243
|
+
return paginated ? paginateRows(displayRows, pagination) : { rows: displayRows, pageCount: 1, pageIndex: 0 };
|
|
1244
|
+
}
|
|
1245
|
+
function narrowToDataRows(pageRows, rows, reshaped) {
|
|
1246
|
+
return reshaped ? pageRows.filter((entry) => !("kind" in entry)) : rows;
|
|
1247
|
+
}
|
|
1248
|
+
function createGridViewResolver() {
|
|
1249
|
+
const identify = memoizeLast(identifyRows);
|
|
1250
|
+
const orderColumns = memoizeLast(resolveDataColumns);
|
|
1251
|
+
const showColumns = memoizeLast(applyColumnVisibility);
|
|
1252
|
+
const size = memoizeLast(sizeColumns);
|
|
1253
|
+
const filterAndSort = memoizeLast(resolveShownRows);
|
|
1254
|
+
const group = memoizeLast(groupRows);
|
|
1255
|
+
const aggregate = memoizeLast(withGroupAggregates);
|
|
1256
|
+
const total = memoizeLast(totalAggregates);
|
|
1257
|
+
const page = memoizeLast(paginate);
|
|
1258
|
+
const narrow = memoizeLast(narrowToDataRows);
|
|
1259
|
+
const assemble = memoizeLast(
|
|
1260
|
+
(dataColumns, columns, rows, displayRows, pageRows, displayDataRows, pageCount, pageIndex, grandTotal) => ({
|
|
1261
|
+
dataColumns,
|
|
1262
|
+
columns,
|
|
1263
|
+
rows,
|
|
1264
|
+
displayRows,
|
|
1265
|
+
pageRows,
|
|
1266
|
+
displayDataRows,
|
|
1267
|
+
pageCount,
|
|
1268
|
+
pageIndex,
|
|
1269
|
+
grandTotal
|
|
1270
|
+
})
|
|
1271
|
+
);
|
|
1272
|
+
return ({ rows: data, getRowId, columns: definitions, state, options }) => {
|
|
1273
|
+
const paginated = options.paginated ?? false;
|
|
1274
|
+
const aggregates = options.aggregates ?? NO_AGGREGATES;
|
|
1275
|
+
const dataColumns = orderColumns(definitions, state.columnOrder);
|
|
1276
|
+
const columns = size(
|
|
1277
|
+
showColumns(dataColumns, state.columnVisibility),
|
|
1278
|
+
state.columnSizing,
|
|
1279
|
+
options.columnSizeDefaults,
|
|
1280
|
+
options.resizableColumns,
|
|
1281
|
+
options.reorderableColumns,
|
|
1282
|
+
options.groupByDraggableColumns,
|
|
1283
|
+
options.resizeMode ?? "fit",
|
|
1284
|
+
options.viewportWidth ?? null
|
|
396
1285
|
);
|
|
397
|
-
const
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
1286
|
+
const rows = filterAndSort(
|
|
1287
|
+
identify(data, getRowId),
|
|
1288
|
+
state.filter,
|
|
1289
|
+
state.columnSort,
|
|
1290
|
+
dataColumns
|
|
1291
|
+
);
|
|
1292
|
+
const displayRows = aggregate(
|
|
1293
|
+
group(rows, state.groupBy, state.groupExpansion, dataColumns),
|
|
1294
|
+
rows,
|
|
1295
|
+
state.groupBy,
|
|
1296
|
+
aggregates,
|
|
1297
|
+
dataColumns,
|
|
1298
|
+
options.groupAggregateDisplay ?? "inline"
|
|
1299
|
+
);
|
|
1300
|
+
const {
|
|
1301
|
+
rows: pageRows,
|
|
1302
|
+
pageCount,
|
|
1303
|
+
pageIndex
|
|
1304
|
+
} = page(displayRows, paginated, state.pagination);
|
|
1305
|
+
return assemble(
|
|
1306
|
+
dataColumns,
|
|
1307
|
+
columns,
|
|
1308
|
+
rows,
|
|
1309
|
+
displayRows,
|
|
1310
|
+
pageRows,
|
|
1311
|
+
narrow(pageRows, rows, state.groupBy.length > 0 || paginated),
|
|
1312
|
+
pageCount,
|
|
1313
|
+
pageIndex,
|
|
1314
|
+
total(rows, aggregates, dataColumns)
|
|
1315
|
+
);
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
function resolveGridView(input) {
|
|
1319
|
+
return createGridViewResolver()(input);
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
// src/controller/changes.ts
|
|
1323
|
+
function changedEntryColumnId(previous, next) {
|
|
1324
|
+
const length = Math.max(previous.length, next.length);
|
|
1325
|
+
for (let index = 0; index < length; index++) {
|
|
1326
|
+
const before = previous[index];
|
|
1327
|
+
const after = next[index];
|
|
1328
|
+
if (before?.columnId !== after?.columnId || before?.direction !== after?.direction) {
|
|
1329
|
+
return (after ?? before)?.columnId;
|
|
412
1330
|
}
|
|
413
|
-
|
|
414
|
-
|
|
1331
|
+
}
|
|
1332
|
+
return void 0;
|
|
1333
|
+
}
|
|
1334
|
+
function changedId(previous, next) {
|
|
1335
|
+
const length = Math.max(previous.length, next.length);
|
|
1336
|
+
for (let index = 0; index < length; index++) {
|
|
1337
|
+
if (previous[index] !== next[index]) {
|
|
1338
|
+
return next[index] ?? previous[index];
|
|
415
1339
|
}
|
|
416
1340
|
}
|
|
1341
|
+
return void 0;
|
|
1342
|
+
}
|
|
1343
|
+
function changedKeys(previous, next) {
|
|
1344
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(next), ...Object.keys(previous)]);
|
|
1345
|
+
return [...keys].filter((key) => previous[key] !== next[key]);
|
|
1346
|
+
}
|
|
1347
|
+
function sameItems(previous, next) {
|
|
1348
|
+
return previous.length === next.length && previous.every((item, index) => item === next[index]);
|
|
1349
|
+
}
|
|
1350
|
+
function samePagination(previous, next) {
|
|
1351
|
+
return previous.pageIndex === next.pageIndex && previous.pageSize === next.pageSize;
|
|
1352
|
+
}
|
|
1353
|
+
function sameValue(previous, next) {
|
|
1354
|
+
return previous instanceof Date && next instanceof Date ? previous.getTime() === next.getTime() : previous === next;
|
|
1355
|
+
}
|
|
1356
|
+
function sameOperand(previous, next) {
|
|
1357
|
+
if (isScalarList(previous) || isScalarList(next)) {
|
|
1358
|
+
return isScalarList(previous) && isScalarList(next) && previous.length === next.length && previous.every((scalar, index) => sameValue(scalar, next[index]));
|
|
1359
|
+
}
|
|
1360
|
+
return sameValue(previous, next);
|
|
1361
|
+
}
|
|
1362
|
+
function sameEntry(previous, next) {
|
|
1363
|
+
if ("combinator" in previous || "combinator" in next) {
|
|
1364
|
+
return "combinator" in previous && "combinator" in next && previous.combinator === next.combinator && sameFilter(previous.entries, next.entries);
|
|
1365
|
+
}
|
|
1366
|
+
if (previous.columnId !== next.columnId) {
|
|
1367
|
+
return false;
|
|
1368
|
+
}
|
|
1369
|
+
if ("predicate" in previous || "predicate" in next) {
|
|
1370
|
+
return "predicate" in previous && "predicate" in next && previous.predicate === next.predicate;
|
|
1371
|
+
}
|
|
1372
|
+
if ("operator" in previous || "operator" in next) {
|
|
1373
|
+
return "operator" in previous && "operator" in next && previous.operator === next.operator && sameOperand(previous.value, next.value);
|
|
1374
|
+
}
|
|
1375
|
+
if ("value" in previous || "value" in next) {
|
|
1376
|
+
return "value" in previous && "value" in next && sameValue(previous.value, next.value);
|
|
1377
|
+
}
|
|
1378
|
+
return previous.query === next.query;
|
|
1379
|
+
}
|
|
1380
|
+
function sameFilter(previous, next) {
|
|
1381
|
+
return previous.length === next.length && previous.every((entry, index) => {
|
|
1382
|
+
const other = next[index];
|
|
1383
|
+
return other !== void 0 && sameEntry(entry, other);
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
// src/util/selection.ts
|
|
1388
|
+
var NO_CHANGE = { added: [], removed: [] };
|
|
1389
|
+
function sameSelection(a, b) {
|
|
1390
|
+
return a.length === b.length && a.every((id, index) => id === b[index]);
|
|
1391
|
+
}
|
|
1392
|
+
function toggleSelection(selection, id, mode) {
|
|
1393
|
+
if (mode === false) {
|
|
1394
|
+
return selection;
|
|
1395
|
+
}
|
|
1396
|
+
if (selection.includes(id)) {
|
|
1397
|
+
return selection.filter((selectedId) => selectedId !== id);
|
|
1398
|
+
}
|
|
1399
|
+
return mode === "single" ? [id] : [...selection, id];
|
|
417
1400
|
}
|
|
418
|
-
function
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
if (resolved.length === 0 || total === target) {
|
|
422
|
-
return resolved;
|
|
1401
|
+
function selectOnly(selection, id, mode) {
|
|
1402
|
+
if (mode === false) {
|
|
1403
|
+
return selection;
|
|
423
1404
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
width: entry.width,
|
|
430
|
-
bound: growing ? constraints.maxWidth : constraints.minWidth,
|
|
431
|
-
adjustable: !entry.sized
|
|
432
|
-
};
|
|
433
|
-
});
|
|
434
|
-
if (!growing) {
|
|
435
|
-
const floor = entries.reduce(
|
|
436
|
-
(sum, item) => sum + (item.adjustable ? item.bound : item.width),
|
|
437
|
-
0
|
|
438
|
-
);
|
|
439
|
-
if (floor > target) {
|
|
440
|
-
return resolved;
|
|
441
|
-
}
|
|
1405
|
+
return selection.length === 1 && selection[0] === id ? selection : [id];
|
|
1406
|
+
}
|
|
1407
|
+
function selectRange(selection, orderedIds, anchorId, focusId, mode) {
|
|
1408
|
+
if (mode !== "multiple") {
|
|
1409
|
+
return selectOnly(selection, focusId, mode);
|
|
442
1410
|
}
|
|
443
|
-
|
|
444
|
-
const
|
|
445
|
-
|
|
1411
|
+
const anchor = orderedIds.indexOf(anchorId);
|
|
1412
|
+
const focus = orderedIds.indexOf(focusId);
|
|
1413
|
+
if (anchor === -1 || focus === -1) {
|
|
1414
|
+
return selection;
|
|
1415
|
+
}
|
|
1416
|
+
const next = orderedIds.slice(
|
|
1417
|
+
Math.min(anchor, focus),
|
|
1418
|
+
Math.max(anchor, focus) + 1
|
|
446
1419
|
);
|
|
447
|
-
|
|
448
|
-
return entries.map((item, index) => {
|
|
449
|
-
let width = rounded[index] ?? item.width;
|
|
450
|
-
const hasRoom = growing ? width < item.bound : width > item.bound;
|
|
451
|
-
if (remainder !== 0 && item.adjustable && hasRoom) {
|
|
452
|
-
width += growing ? 1 : -1;
|
|
453
|
-
remainder += growing ? -1 : 1;
|
|
454
|
-
}
|
|
455
|
-
return { ...item.entry, width };
|
|
456
|
-
});
|
|
1420
|
+
return sameSelection(selection, next) ? selection : next;
|
|
457
1421
|
}
|
|
458
|
-
function
|
|
459
|
-
|
|
1422
|
+
function selectAll(selection, orderedIds, mode) {
|
|
1423
|
+
if (mode !== "multiple") {
|
|
1424
|
+
return selection;
|
|
1425
|
+
}
|
|
1426
|
+
return sameSelection(selection, orderedIds) ? selection : orderedIds;
|
|
460
1427
|
}
|
|
461
|
-
function
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
if (
|
|
466
|
-
|
|
1428
|
+
function clearSelection(selection) {
|
|
1429
|
+
return selection.length === 0 ? selection : [];
|
|
1430
|
+
}
|
|
1431
|
+
function diffSelection(previous, next) {
|
|
1432
|
+
if (previous === next) {
|
|
1433
|
+
return NO_CHANGE;
|
|
467
1434
|
}
|
|
468
|
-
|
|
1435
|
+
const before = new Set(previous);
|
|
1436
|
+
const after = new Set(next);
|
|
1437
|
+
return {
|
|
1438
|
+
added: next.filter((id) => !before.has(id)),
|
|
1439
|
+
removed: previous.filter((id) => !after.has(id))
|
|
1440
|
+
};
|
|
469
1441
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
1442
|
+
function isSameCell(a, b) {
|
|
1443
|
+
if (a === null || b === null) {
|
|
1444
|
+
return a === b;
|
|
1445
|
+
}
|
|
1446
|
+
return a.rowId === b.rowId && a.columnId === b.columnId;
|
|
474
1447
|
}
|
|
475
|
-
function
|
|
476
|
-
|
|
1448
|
+
function selectCell(selection, cell, mode) {
|
|
1449
|
+
if (mode === false || isSameCell(selection, cell)) {
|
|
1450
|
+
return selection;
|
|
1451
|
+
}
|
|
1452
|
+
return cell;
|
|
477
1453
|
}
|
|
478
|
-
function
|
|
479
|
-
if (
|
|
480
|
-
return
|
|
1454
|
+
function toggleCellSelection(selection, cell, mode) {
|
|
1455
|
+
if (mode === false) {
|
|
1456
|
+
return selection;
|
|
481
1457
|
}
|
|
482
|
-
|
|
483
|
-
|
|
1458
|
+
return isSameCell(selection, cell) ? null : cell;
|
|
1459
|
+
}
|
|
1460
|
+
function intentOf(event, options) {
|
|
1461
|
+
if (event.shiftKey) {
|
|
1462
|
+
return "range";
|
|
484
1463
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
case "decimal":
|
|
488
|
-
case "currency":
|
|
489
|
-
case "percent":
|
|
490
|
-
return Number(a) - Number(b);
|
|
491
|
-
case "boolean":
|
|
492
|
-
return Number(Boolean(a)) - Number(Boolean(b));
|
|
493
|
-
case "date":
|
|
494
|
-
case "dateTime":
|
|
495
|
-
case "time":
|
|
496
|
-
return toTime(a) - toTime(b);
|
|
497
|
-
default:
|
|
498
|
-
return String(a).localeCompare(String(b));
|
|
1464
|
+
if (event.ctrlKey || event.metaKey) {
|
|
1465
|
+
return "toggle";
|
|
499
1466
|
}
|
|
1467
|
+
return options?.toggleOnClick === true ? "toggle" : "replace";
|
|
500
1468
|
}
|
|
501
|
-
function
|
|
502
|
-
|
|
1469
|
+
function applySelectionIntent(selection, orderedIds, anchorId, id, intent, mode) {
|
|
1470
|
+
switch (intent) {
|
|
1471
|
+
case "toggle":
|
|
1472
|
+
return toggleSelection(selection, id, mode);
|
|
1473
|
+
// A range with no anchor yet spans from the id to itself, which is the
|
|
1474
|
+
// plain click a first Shift-click may as well be.
|
|
1475
|
+
case "range":
|
|
1476
|
+
return selectRange(selection, orderedIds, anchorId ?? id, id, mode);
|
|
1477
|
+
default:
|
|
1478
|
+
return selectOnly(selection, id, mode);
|
|
1479
|
+
}
|
|
503
1480
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
1481
|
+
|
|
1482
|
+
// src/controller/selectionEvents.ts
|
|
1483
|
+
function reportRowSelection(previous, next, rowsById, callbacks) {
|
|
1484
|
+
const diff = diffSelection(previous, next);
|
|
1485
|
+
const added = resolveRows(rowsById, diff.added);
|
|
1486
|
+
const removed = resolveRows(rowsById, diff.removed);
|
|
1487
|
+
for (const row of added) {
|
|
1488
|
+
callbacks.onRowSelect?.({ row, selection: next });
|
|
1489
|
+
}
|
|
1490
|
+
if (added.length > 0) {
|
|
1491
|
+
callbacks.onRowsSelect?.({ rows: added, selection: next });
|
|
1492
|
+
}
|
|
1493
|
+
for (const row of removed) {
|
|
1494
|
+
callbacks.onRowDeselect?.({ row, selection: next });
|
|
1495
|
+
}
|
|
1496
|
+
if (removed.length > 0) {
|
|
1497
|
+
callbacks.onRowsDeselect?.({ rows: removed, selection: next });
|
|
1498
|
+
}
|
|
1499
|
+
callbacks.onRowSelectionChange?.({
|
|
1500
|
+
added,
|
|
1501
|
+
removed,
|
|
1502
|
+
selected: resolveRows(rowsById, next),
|
|
1503
|
+
selection: next
|
|
1504
|
+
});
|
|
1505
|
+
return diff;
|
|
507
1506
|
}
|
|
508
|
-
function
|
|
509
|
-
const
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
1507
|
+
function reportColumnSelection(previous, next, columns, callbacks) {
|
|
1508
|
+
const diff = diffSelection(previous, next);
|
|
1509
|
+
const added = resolveColumns(columns, diff.added);
|
|
1510
|
+
const removed = resolveColumns(columns, diff.removed);
|
|
1511
|
+
for (const column of added) {
|
|
1512
|
+
callbacks.onColumnSelect?.({ column, selection: next });
|
|
513
1513
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
if (next === null) {
|
|
517
|
-
return sort.filter((entry) => entry.columnId !== columnId);
|
|
1514
|
+
if (added.length > 0) {
|
|
1515
|
+
callbacks.onColumnsSelect?.({ columns: added, selection: next });
|
|
518
1516
|
}
|
|
519
|
-
|
|
520
|
-
|
|
1517
|
+
for (const column of removed) {
|
|
1518
|
+
callbacks.onColumnDeselect?.({ column, selection: next });
|
|
521
1519
|
}
|
|
522
|
-
|
|
523
|
-
(
|
|
524
|
-
|
|
1520
|
+
if (removed.length > 0) {
|
|
1521
|
+
callbacks.onColumnsDeselect?.({ columns: removed, selection: next });
|
|
1522
|
+
}
|
|
1523
|
+
callbacks.onColumnSelectionChange?.({
|
|
1524
|
+
added,
|
|
1525
|
+
removed,
|
|
1526
|
+
selected: resolveColumns(columns, next),
|
|
1527
|
+
selection: next
|
|
1528
|
+
});
|
|
1529
|
+
return diff;
|
|
525
1530
|
}
|
|
526
|
-
function
|
|
527
|
-
|
|
528
|
-
|
|
1531
|
+
function reportCellSelection(previous, next, rowsById, columns, callbacks) {
|
|
1532
|
+
const selected = resolveCell(rowsById, columns, next);
|
|
1533
|
+
const deselected = resolveCell(rowsById, columns, previous);
|
|
1534
|
+
if (deselected !== null) {
|
|
1535
|
+
callbacks.onCellDeselect?.({ cell: deselected, selection: next });
|
|
529
1536
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
if (active.length === 0) {
|
|
533
|
-
return rows;
|
|
1537
|
+
if (selected !== null) {
|
|
1538
|
+
callbacks.onCellSelect?.({ cell: selected, selection: next });
|
|
534
1539
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
continue;
|
|
540
|
-
}
|
|
541
|
-
const left = accessDotted(a.row, column.field);
|
|
542
|
-
const right = accessDotted(b.row, column.field);
|
|
543
|
-
const leftEmpty = isEmpty(left);
|
|
544
|
-
const rightEmpty = isEmpty(right);
|
|
545
|
-
if (leftEmpty || rightEmpty) {
|
|
546
|
-
if (leftEmpty && rightEmpty) {
|
|
547
|
-
continue;
|
|
548
|
-
}
|
|
549
|
-
return leftEmpty ? 1 : -1;
|
|
550
|
-
}
|
|
551
|
-
const result = compareValues(left, right, column.type);
|
|
552
|
-
if (result !== 0) {
|
|
553
|
-
return direction === "asc" ? result : -result;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
return 0;
|
|
1540
|
+
callbacks.onCellSelectionChange?.({
|
|
1541
|
+
selected,
|
|
1542
|
+
deselected,
|
|
1543
|
+
selection: next
|
|
557
1544
|
});
|
|
558
|
-
return sorted.map(
|
|
559
|
-
(row, index) => row.rowIndex === index ? row : { ...row, rowIndex: index }
|
|
560
|
-
);
|
|
561
1545
|
}
|
|
562
1546
|
|
|
563
|
-
// src/
|
|
564
|
-
function
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
1547
|
+
// src/controller/selectionActions.ts
|
|
1548
|
+
function indexRows(rows) {
|
|
1549
|
+
return new Map(rows.map((entry) => [entry.rowId, entry]));
|
|
1550
|
+
}
|
|
1551
|
+
function distinct(ids) {
|
|
1552
|
+
return [...new Set(ids)];
|
|
1553
|
+
}
|
|
1554
|
+
function createSelectionActions(grid) {
|
|
1555
|
+
let rowAnchor = null;
|
|
1556
|
+
let columnAnchor = null;
|
|
1557
|
+
const rowsById = memoizeLast(indexRows);
|
|
1558
|
+
function modes() {
|
|
1559
|
+
const selectable = grid.getOptions().selectable;
|
|
1560
|
+
return {
|
|
1561
|
+
rows: selectable?.rows ?? false,
|
|
1562
|
+
columns: selectable?.columns ?? false,
|
|
1563
|
+
cells: selectable?.cells ?? false
|
|
1564
|
+
};
|
|
573
1565
|
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
1566
|
+
function rowIds() {
|
|
1567
|
+
return grid.getView().displayDataRows.map((entry) => entry.rowId);
|
|
1568
|
+
}
|
|
1569
|
+
function columnIds() {
|
|
1570
|
+
return grid.getView().columns.map((entry) => entry.id);
|
|
1571
|
+
}
|
|
1572
|
+
function commitRows(next) {
|
|
1573
|
+
const previous = grid.getState().rowSelection;
|
|
1574
|
+
grid.commit("rowSelection", next, () => {
|
|
1575
|
+
const options = grid.getOptions();
|
|
1576
|
+
const diff = reportRowSelection(
|
|
1577
|
+
previous,
|
|
1578
|
+
next,
|
|
1579
|
+
rowsById(grid.getView().displayDataRows),
|
|
1580
|
+
options
|
|
1581
|
+
);
|
|
1582
|
+
options.onSelectionDiff?.({ target: "rows", ...diff, selection: next });
|
|
1583
|
+
});
|
|
577
1584
|
}
|
|
578
|
-
|
|
579
|
-
|
|
1585
|
+
function commitColumns(next) {
|
|
1586
|
+
const previous = grid.getState().columnSelection;
|
|
1587
|
+
grid.commit("columnSelection", next, () => {
|
|
1588
|
+
const options = grid.getOptions();
|
|
1589
|
+
const diff = reportColumnSelection(
|
|
1590
|
+
previous,
|
|
1591
|
+
next,
|
|
1592
|
+
grid.getView().columns,
|
|
1593
|
+
options
|
|
1594
|
+
);
|
|
1595
|
+
options.onSelectionDiff?.({
|
|
1596
|
+
target: "columns",
|
|
1597
|
+
...diff,
|
|
1598
|
+
selection: next
|
|
1599
|
+
});
|
|
1600
|
+
});
|
|
580
1601
|
}
|
|
581
|
-
|
|
582
|
-
|
|
1602
|
+
function commitCell(next) {
|
|
1603
|
+
const previous = grid.getState().cellSelection;
|
|
1604
|
+
grid.commit("cellSelection", next, () => {
|
|
1605
|
+
const view = grid.getView();
|
|
1606
|
+
reportCellSelection(
|
|
1607
|
+
previous,
|
|
1608
|
+
next,
|
|
1609
|
+
rowsById(view.displayDataRows),
|
|
1610
|
+
view.columns,
|
|
1611
|
+
grid.getOptions()
|
|
1612
|
+
);
|
|
1613
|
+
});
|
|
583
1614
|
}
|
|
584
|
-
return {
|
|
1615
|
+
return {
|
|
1616
|
+
setRowSelection(selection) {
|
|
1617
|
+
if (!sameItems(grid.getState().rowSelection, selection)) {
|
|
1618
|
+
commitRows(selection);
|
|
1619
|
+
}
|
|
1620
|
+
},
|
|
1621
|
+
selectRows(ids, intent = "replace") {
|
|
1622
|
+
const mode = modes().rows;
|
|
1623
|
+
const current = grid.getState().rowSelection;
|
|
1624
|
+
if (mode === false || ids.length === 0) {
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
const next = intent === "toggle" ? ids.reduce(
|
|
1628
|
+
(selection, id) => toggleSelection(selection, id, mode),
|
|
1629
|
+
current
|
|
1630
|
+
) : mode === "single" ? ids.slice(-1) : distinct(ids);
|
|
1631
|
+
rowAnchor = ids.at(-1) ?? null;
|
|
1632
|
+
if (!sameItems(current, next)) {
|
|
1633
|
+
commitRows(next);
|
|
1634
|
+
}
|
|
1635
|
+
},
|
|
1636
|
+
selectRow(rowId, intent) {
|
|
1637
|
+
const next = applySelectionIntent(
|
|
1638
|
+
grid.getState().rowSelection,
|
|
1639
|
+
rowIds(),
|
|
1640
|
+
rowAnchor,
|
|
1641
|
+
rowId,
|
|
1642
|
+
intent,
|
|
1643
|
+
modes().rows
|
|
1644
|
+
);
|
|
1645
|
+
if (intent !== "range") {
|
|
1646
|
+
rowAnchor = rowId;
|
|
1647
|
+
}
|
|
1648
|
+
commitRows(next);
|
|
1649
|
+
},
|
|
1650
|
+
selectAllRows() {
|
|
1651
|
+
commitRows(
|
|
1652
|
+
selectAll(grid.getState().rowSelection, rowIds(), modes().rows)
|
|
1653
|
+
);
|
|
1654
|
+
},
|
|
1655
|
+
clearSelection() {
|
|
1656
|
+
commitRows(clearSelection(grid.getState().rowSelection));
|
|
1657
|
+
commitColumns(clearSelection(grid.getState().columnSelection));
|
|
1658
|
+
commitCell(null);
|
|
1659
|
+
},
|
|
1660
|
+
setColumnSelection(selection) {
|
|
1661
|
+
if (!sameItems(grid.getState().columnSelection, selection)) {
|
|
1662
|
+
commitColumns(selection);
|
|
1663
|
+
}
|
|
1664
|
+
},
|
|
1665
|
+
selectColumn(columnId, intent) {
|
|
1666
|
+
const next = applySelectionIntent(
|
|
1667
|
+
grid.getState().columnSelection,
|
|
1668
|
+
columnIds(),
|
|
1669
|
+
columnAnchor,
|
|
1670
|
+
columnId,
|
|
1671
|
+
intent,
|
|
1672
|
+
modes().columns
|
|
1673
|
+
);
|
|
1674
|
+
if (intent !== "range") {
|
|
1675
|
+
columnAnchor = columnId;
|
|
1676
|
+
}
|
|
1677
|
+
commitColumns(next);
|
|
1678
|
+
},
|
|
1679
|
+
setCellSelection(selection) {
|
|
1680
|
+
if (!isSameCell(grid.getState().cellSelection, selection)) {
|
|
1681
|
+
commitCell(selection);
|
|
1682
|
+
}
|
|
1683
|
+
},
|
|
1684
|
+
selectCell(cell, intent) {
|
|
1685
|
+
const current = grid.getState().cellSelection;
|
|
1686
|
+
const mode = modes().cells;
|
|
1687
|
+
commitCell(
|
|
1688
|
+
intent === "toggle" ? toggleCellSelection(current, cell, mode) : selectCell(current, cell, mode)
|
|
1689
|
+
);
|
|
1690
|
+
}
|
|
1691
|
+
};
|
|
585
1692
|
}
|
|
586
|
-
|
|
587
|
-
|
|
1693
|
+
|
|
1694
|
+
// src/controller/createGridController.ts
|
|
1695
|
+
var DEFAULT_PAGE_SIZE = 25;
|
|
1696
|
+
var NO_ROWS = [];
|
|
1697
|
+
var NO_COLUMNS = [];
|
|
1698
|
+
var SLICE_KEYS = [
|
|
1699
|
+
"columnSizing",
|
|
1700
|
+
"columnOrder",
|
|
1701
|
+
"columnVisibility",
|
|
1702
|
+
"columnSort",
|
|
1703
|
+
"filter",
|
|
1704
|
+
"groupBy",
|
|
1705
|
+
"groupExpansion",
|
|
1706
|
+
"pagination",
|
|
1707
|
+
"rowSelection",
|
|
1708
|
+
"columnSelection",
|
|
1709
|
+
"cellSelection"
|
|
1710
|
+
];
|
|
1711
|
+
var DEFAULT_OPTION = {
|
|
1712
|
+
columnSizing: "defaultColumnSizing",
|
|
1713
|
+
columnOrder: "defaultColumnOrder",
|
|
1714
|
+
columnVisibility: "defaultColumnVisibility",
|
|
1715
|
+
columnSort: "defaultColumnSort",
|
|
1716
|
+
filter: "defaultFilter",
|
|
1717
|
+
groupBy: "defaultGroupBy",
|
|
1718
|
+
groupExpansion: "defaultGroupExpansion",
|
|
1719
|
+
pagination: "defaultPagination",
|
|
1720
|
+
rowSelection: "defaultRowSelection",
|
|
1721
|
+
columnSelection: "defaultColumnSelection",
|
|
1722
|
+
cellSelection: "defaultCellSelection"
|
|
1723
|
+
};
|
|
1724
|
+
function startingState(options) {
|
|
1725
|
+
return {
|
|
1726
|
+
columnSizing: options.defaultColumnSizing ?? {},
|
|
1727
|
+
columnOrder: options.defaultColumnOrder ?? [],
|
|
1728
|
+
columnVisibility: options.defaultColumnVisibility ?? {},
|
|
1729
|
+
columnSort: options.defaultColumnSort ?? [],
|
|
1730
|
+
filter: options.defaultFilter ?? [],
|
|
1731
|
+
groupBy: options.defaultGroupBy ?? [],
|
|
1732
|
+
groupExpansion: options.defaultGroupExpansion ?? [],
|
|
1733
|
+
pagination: options.defaultPagination ?? {
|
|
1734
|
+
pageIndex: 0,
|
|
1735
|
+
pageSize: DEFAULT_PAGE_SIZE
|
|
1736
|
+
},
|
|
1737
|
+
rowSelection: options.defaultRowSelection ?? [],
|
|
1738
|
+
columnSelection: options.defaultColumnSelection ?? [],
|
|
1739
|
+
cellSelection: options.defaultCellSelection ?? null
|
|
1740
|
+
};
|
|
588
1741
|
}
|
|
589
|
-
function
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
1742
|
+
function definedColumns(columns, rows) {
|
|
1743
|
+
if (columns !== void 0 && columns.length > 0) {
|
|
1744
|
+
return columns;
|
|
1745
|
+
}
|
|
1746
|
+
if (rows !== void 0 && rows.length > 0) {
|
|
1747
|
+
return defineColumnsFromRows(rows);
|
|
1748
|
+
}
|
|
1749
|
+
return NO_COLUMNS;
|
|
1750
|
+
}
|
|
1751
|
+
function pagingInputsOf(state) {
|
|
1752
|
+
return {
|
|
1753
|
+
filter: state.filter,
|
|
1754
|
+
columnSort: state.columnSort,
|
|
1755
|
+
groupBy: state.groupBy
|
|
603
1756
|
};
|
|
604
1757
|
}
|
|
605
|
-
function
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
1758
|
+
function createGridController(initial) {
|
|
1759
|
+
let options = initial;
|
|
1760
|
+
const own = startingState(initial);
|
|
1761
|
+
const resolveView = createGridViewResolver();
|
|
1762
|
+
const resolveColumns2 = memoizeLast(definedColumns);
|
|
1763
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1764
|
+
const reportedConflicts = /* @__PURE__ */ new Set();
|
|
1765
|
+
let heldReports;
|
|
1766
|
+
let state = resolveSlices();
|
|
1767
|
+
let pagingInputs = pagingInputsOf(state);
|
|
1768
|
+
reportConflicts();
|
|
1769
|
+
function controlledValue(key) {
|
|
1770
|
+
const controlled = options;
|
|
1771
|
+
return controlled[key];
|
|
1772
|
+
}
|
|
1773
|
+
function isControlled(key) {
|
|
1774
|
+
return controlledValue(key) !== void 0;
|
|
1775
|
+
}
|
|
1776
|
+
function resolveSlice(key) {
|
|
1777
|
+
const controlled = controlledValue(key);
|
|
1778
|
+
return controlled === void 0 ? own[key] : controlled;
|
|
1779
|
+
}
|
|
1780
|
+
function resolveSlices() {
|
|
1781
|
+
return {
|
|
1782
|
+
columnSizing: resolveSlice("columnSizing"),
|
|
1783
|
+
columnOrder: resolveSlice("columnOrder"),
|
|
1784
|
+
columnVisibility: resolveSlice("columnVisibility"),
|
|
1785
|
+
columnSort: resolveSlice("columnSort"),
|
|
1786
|
+
filter: resolveSlice("filter"),
|
|
1787
|
+
groupBy: resolveSlice("groupBy"),
|
|
1788
|
+
groupExpansion: resolveSlice("groupExpansion"),
|
|
1789
|
+
pagination: resolveSlice("pagination"),
|
|
1790
|
+
rowSelection: resolveSlice("rowSelection"),
|
|
1791
|
+
columnSelection: resolveSlice("columnSelection"),
|
|
1792
|
+
cellSelection: resolveSlice("cellSelection")
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
function reportConflicts() {
|
|
1796
|
+
for (const key of SLICE_KEYS) {
|
|
1797
|
+
const defaultKey = DEFAULT_OPTION[key];
|
|
1798
|
+
if (reportedConflicts.has(key) || !isControlled(key) || options[defaultKey] === void 0) {
|
|
1799
|
+
continue;
|
|
1800
|
+
}
|
|
1801
|
+
reportedConflicts.add(key);
|
|
1802
|
+
reportError(
|
|
1803
|
+
`\`${key}\` and \`${defaultKey}\` are both set \u2014 \`${key}\` is controlled, so \`${defaultKey}\` is ignored.`
|
|
1804
|
+
);
|
|
609
1805
|
}
|
|
610
|
-
return typeof value === "number" && value === target;
|
|
611
1806
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
1807
|
+
function refresh() {
|
|
1808
|
+
let next = resolveSlices();
|
|
1809
|
+
if (next.filter !== pagingInputs.filter || next.columnSort !== pagingInputs.columnSort || next.groupBy !== pagingInputs.groupBy) {
|
|
1810
|
+
pagingInputs = pagingInputsOf(next);
|
|
1811
|
+
if (!isControlled("pagination") && own.pagination.pageIndex !== 0) {
|
|
1812
|
+
own.pagination = { ...own.pagination, pageIndex: 0 };
|
|
1813
|
+
next = { ...next, pagination: own.pagination };
|
|
1814
|
+
}
|
|
615
1815
|
}
|
|
616
|
-
|
|
1816
|
+
state = SLICE_KEYS.every((key) => next[key] === state[key]) ? state : next;
|
|
617
1817
|
}
|
|
618
|
-
|
|
619
|
-
|
|
1818
|
+
function notify() {
|
|
1819
|
+
for (const listener of [...listeners]) {
|
|
1820
|
+
listener();
|
|
1821
|
+
}
|
|
620
1822
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
1823
|
+
function commit(key, next, report) {
|
|
1824
|
+
if (next === state[key]) {
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
if (!isControlled(key)) {
|
|
1828
|
+
own[key] = next;
|
|
1829
|
+
}
|
|
1830
|
+
if (heldReports !== void 0) {
|
|
1831
|
+
heldReports.push(report);
|
|
1832
|
+
return;
|
|
1833
|
+
}
|
|
1834
|
+
refresh();
|
|
1835
|
+
report();
|
|
1836
|
+
notify();
|
|
1837
|
+
}
|
|
1838
|
+
function getView() {
|
|
1839
|
+
return resolveView({
|
|
1840
|
+
rows: options.dataSource ?? NO_ROWS,
|
|
1841
|
+
getRowId: options.getRowId,
|
|
1842
|
+
columns: resolveColumns2(options.columns, options.dataSource),
|
|
1843
|
+
state,
|
|
1844
|
+
options: {
|
|
1845
|
+
paginated: options.paginated,
|
|
1846
|
+
aggregates: options.aggregates,
|
|
1847
|
+
groupAggregateDisplay: options.groupAggregateDisplay,
|
|
1848
|
+
columnSizeDefaults: options.columnSizeDefaults,
|
|
1849
|
+
resizableColumns: options.resizableColumns,
|
|
1850
|
+
reorderableColumns: options.reorderableColumns,
|
|
1851
|
+
groupByDraggableColumns: options.groupByDraggableColumns,
|
|
1852
|
+
resizeMode: options.resizeMode,
|
|
1853
|
+
viewportWidth: options.viewportWidth
|
|
1854
|
+
}
|
|
1855
|
+
});
|
|
626
1856
|
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
const tests = entry.entries.filter((nested) => isEntryUsable(nested, byId)).map((nested) => compileFilterEntry(nested, byId, columns));
|
|
632
|
-
return entry.combinator === "and" ? (row) => tests.every((test2) => test2(row)) : (row) => tests.some((test2) => test2(row));
|
|
1857
|
+
function commitColumnSizing(next, columnId, width, phase) {
|
|
1858
|
+
commit("columnSizing", next, () => {
|
|
1859
|
+
options.onColumnResize?.({ columnId, width, sizing: next, phase });
|
|
1860
|
+
});
|
|
633
1861
|
}
|
|
634
|
-
|
|
635
|
-
const
|
|
636
|
-
|
|
637
|
-
return (row) => predicate(
|
|
638
|
-
column === void 0 ? void 0 : accessDotted(row.row, column.field),
|
|
639
|
-
row.row
|
|
1862
|
+
function definedWidth(columnId) {
|
|
1863
|
+
const entry = getView().dataColumns.find(
|
|
1864
|
+
(candidate) => candidate.id === columnId
|
|
640
1865
|
);
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
if (column === void 0) {
|
|
647
|
-
return () => false;
|
|
1866
|
+
const [resolved] = entry === void 0 ? [] : resolveColumnWidths(
|
|
1867
|
+
[entry.column],
|
|
1868
|
+
{},
|
|
1869
|
+
{
|
|
1870
|
+
sizes: options.columnSizeDefaults
|
|
648
1871
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
(
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
1872
|
+
);
|
|
1873
|
+
return resolved?.width ?? 0;
|
|
1874
|
+
}
|
|
1875
|
+
function commitColumnVisibility(next) {
|
|
1876
|
+
const previous = state.columnVisibility;
|
|
1877
|
+
commit("columnVisibility", next, () => {
|
|
1878
|
+
const changed = new Set(changedKeys(previous, next));
|
|
1879
|
+
const shown = getView().dataColumns.map((entry) => entry.id).filter((id) => changed.delete(id));
|
|
1880
|
+
options.onColumnVisibilityChange?.({
|
|
1881
|
+
columnIds: [...shown, ...changed],
|
|
1882
|
+
visibility: next
|
|
1883
|
+
});
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1886
|
+
function changeColumnVisibility(columnIds, visible) {
|
|
1887
|
+
commitColumnVisibility(
|
|
1888
|
+
setColumnVisibility(
|
|
1889
|
+
getView().dataColumns,
|
|
1890
|
+
state.columnVisibility,
|
|
1891
|
+
columnIds,
|
|
1892
|
+
visible
|
|
660
1893
|
)
|
|
661
1894
|
);
|
|
662
1895
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
if (column === void 0) {
|
|
668
|
-
return () => false;
|
|
669
|
-
}
|
|
670
|
-
return (row) => test(accessDotted(row.row, column.field));
|
|
1896
|
+
function commitGroupBy(next, columnId) {
|
|
1897
|
+
commit("groupBy", next, () => {
|
|
1898
|
+
options.onGroupByChange?.({ columnId, groupBy: next });
|
|
1899
|
+
});
|
|
671
1900
|
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
}
|
|
677
|
-
function filterQueryFor(filter, columnId) {
|
|
678
|
-
const entry = filter.find(
|
|
679
|
-
(candidate) => "query" in candidate && candidate.columnId === columnId
|
|
680
|
-
);
|
|
681
|
-
return entry?.query ?? null;
|
|
682
|
-
}
|
|
683
|
-
function setColumnFilter(filter, columnId, query) {
|
|
684
|
-
const index = filter.findIndex(
|
|
685
|
-
(entry) => !("combinator" in entry) && entry.columnId === columnId
|
|
686
|
-
);
|
|
687
|
-
if (query === "") {
|
|
688
|
-
return index === -1 ? filter : filter.filter((_entry, i) => i !== index);
|
|
1901
|
+
function commitGroupExpansion(next, groupId) {
|
|
1902
|
+
commit("groupExpansion", next, () => {
|
|
1903
|
+
options.onGroupExpansionChange?.({ groupId, expansion: next });
|
|
1904
|
+
});
|
|
689
1905
|
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
1906
|
+
function commitPagination(next) {
|
|
1907
|
+
if (samePagination(state.pagination, next)) {
|
|
1908
|
+
return;
|
|
1909
|
+
}
|
|
1910
|
+
commit("pagination", next, () => {
|
|
1911
|
+
options.onPaginationChange?.({
|
|
1912
|
+
pagination: next,
|
|
1913
|
+
pageCount: paginateRows(getView().displayRows, next).pageCount
|
|
1914
|
+
});
|
|
1915
|
+
});
|
|
693
1916
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
1917
|
+
function goToPage(pageIndex) {
|
|
1918
|
+
const current = state.pagination;
|
|
1919
|
+
const clamped = paginateRows(getView().displayRows, {
|
|
1920
|
+
...current,
|
|
1921
|
+
pageIndex
|
|
1922
|
+
}).pageIndex;
|
|
1923
|
+
commitPagination({ ...current, pageIndex: clamped });
|
|
1924
|
+
}
|
|
1925
|
+
function shownPageIndex() {
|
|
1926
|
+
return paginateRows(getView().displayRows, state.pagination).pageIndex;
|
|
1927
|
+
}
|
|
1928
|
+
const selection = createSelectionActions({
|
|
1929
|
+
getState: () => state,
|
|
1930
|
+
getOptions: () => options,
|
|
1931
|
+
getView,
|
|
1932
|
+
commit
|
|
1933
|
+
});
|
|
1934
|
+
const controller = {
|
|
1935
|
+
...selection,
|
|
1936
|
+
getOptions: () => options,
|
|
1937
|
+
setOptions(next) {
|
|
1938
|
+
options = next;
|
|
1939
|
+
reportConflicts();
|
|
1940
|
+
refresh();
|
|
1941
|
+
},
|
|
1942
|
+
getState: () => state,
|
|
1943
|
+
getView,
|
|
1944
|
+
restoreState(saved) {
|
|
1945
|
+
const reports = [];
|
|
1946
|
+
heldReports = reports;
|
|
1947
|
+
try {
|
|
1948
|
+
restoreSlices(saved);
|
|
1949
|
+
} finally {
|
|
1950
|
+
heldReports = void 0;
|
|
1951
|
+
}
|
|
1952
|
+
if (saved.pagination !== void 0) {
|
|
1953
|
+
pagingInputs = pagingInputsOf(resolveSlices());
|
|
1954
|
+
}
|
|
1955
|
+
refresh();
|
|
1956
|
+
for (const report of reports) {
|
|
1957
|
+
report();
|
|
1958
|
+
}
|
|
1959
|
+
if (reports.length > 0) {
|
|
1960
|
+
notify();
|
|
1961
|
+
}
|
|
1962
|
+
},
|
|
1963
|
+
subscribe(listener) {
|
|
1964
|
+
listeners.add(listener);
|
|
1965
|
+
return () => {
|
|
1966
|
+
listeners.delete(listener);
|
|
1967
|
+
};
|
|
1968
|
+
},
|
|
1969
|
+
setColumnSizing(sizing) {
|
|
1970
|
+
const [columnId] = changedKeys(state.columnSizing, sizing);
|
|
1971
|
+
if (columnId !== void 0) {
|
|
1972
|
+
commitColumnSizing(
|
|
1973
|
+
sizing,
|
|
1974
|
+
columnId,
|
|
1975
|
+
sizing[columnId] ?? definedWidth(columnId),
|
|
1976
|
+
"end"
|
|
1977
|
+
);
|
|
1978
|
+
}
|
|
1979
|
+
},
|
|
1980
|
+
resizeColumn(columnId, width, phase) {
|
|
1981
|
+
commitColumnSizing(
|
|
1982
|
+
{ ...state.columnSizing, [columnId]: width },
|
|
1983
|
+
columnId,
|
|
1984
|
+
width,
|
|
1985
|
+
phase
|
|
1986
|
+
);
|
|
1987
|
+
},
|
|
1988
|
+
cancelColumnResize(columnId, startSizing, startWidth) {
|
|
1989
|
+
commitColumnSizing(
|
|
1990
|
+
revertColumnSize(state.columnSizing, startSizing, columnId),
|
|
1991
|
+
columnId,
|
|
1992
|
+
startWidth,
|
|
1993
|
+
"end"
|
|
1994
|
+
);
|
|
1995
|
+
},
|
|
1996
|
+
setColumnOrder(order) {
|
|
1997
|
+
const columnId = changedId(state.columnOrder, order);
|
|
1998
|
+
if (columnId === void 0) {
|
|
1999
|
+
return;
|
|
2000
|
+
}
|
|
2001
|
+
commit("columnOrder", order, () => {
|
|
2002
|
+
options.onColumnOrderChange?.({ columnId, order });
|
|
2003
|
+
});
|
|
2004
|
+
},
|
|
2005
|
+
/**
|
|
2006
|
+
* Applied to every id in display order rather than to the stored order,
|
|
2007
|
+
* which starts empty and may name only some columns, so would have
|
|
2008
|
+
* nothing to rearrange.
|
|
2009
|
+
*/
|
|
2010
|
+
moveColumn(columnId, beforeId) {
|
|
2011
|
+
const orderedIds = getView().dataColumns.map((entry) => entry.id);
|
|
2012
|
+
const next = moveColumnBefore(orderedIds, columnId, beforeId);
|
|
2013
|
+
if (next === orderedIds) {
|
|
2014
|
+
return;
|
|
2015
|
+
}
|
|
2016
|
+
commit("columnOrder", next, () => {
|
|
2017
|
+
options.onColumnOrderChange?.({ columnId, order: next });
|
|
2018
|
+
});
|
|
2019
|
+
},
|
|
2020
|
+
setColumnVisibility(visibility) {
|
|
2021
|
+
if (changedKeys(state.columnVisibility, visibility).length > 0) {
|
|
2022
|
+
commitColumnVisibility(visibility);
|
|
2023
|
+
}
|
|
2024
|
+
},
|
|
2025
|
+
showColumns(columnIds) {
|
|
2026
|
+
changeColumnVisibility(columnIds, true);
|
|
2027
|
+
},
|
|
2028
|
+
hideColumns(columnIds) {
|
|
2029
|
+
changeColumnVisibility(columnIds, false);
|
|
2030
|
+
},
|
|
2031
|
+
setFilter(filter) {
|
|
2032
|
+
if (sameFilter(state.filter, filter)) {
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
2035
|
+
commit("filter", filter, () => {
|
|
2036
|
+
options.onFilterChange?.({ filter });
|
|
2037
|
+
});
|
|
2038
|
+
},
|
|
2039
|
+
setColumnSort(sort) {
|
|
2040
|
+
const columnId = changedEntryColumnId(state.columnSort, sort);
|
|
2041
|
+
if (columnId === void 0) {
|
|
2042
|
+
return;
|
|
2043
|
+
}
|
|
2044
|
+
commit("columnSort", sort, () => {
|
|
2045
|
+
options.onColumnSortChange?.({ columnId, sort });
|
|
2046
|
+
});
|
|
2047
|
+
},
|
|
2048
|
+
toggleColumnSort(columnId, { stack = false } = {}) {
|
|
2049
|
+
const next = toggleColumnSort(state.columnSort, columnId, { stack });
|
|
2050
|
+
commit("columnSort", next, () => {
|
|
2051
|
+
options.onColumnSortChange?.({ columnId, sort: next });
|
|
2052
|
+
});
|
|
2053
|
+
},
|
|
2054
|
+
clearColumnSort(columnId) {
|
|
2055
|
+
const next = state.columnSort.filter(
|
|
2056
|
+
(entry) => entry.columnId !== columnId
|
|
2057
|
+
);
|
|
2058
|
+
if (next.length === state.columnSort.length) {
|
|
2059
|
+
return;
|
|
2060
|
+
}
|
|
2061
|
+
commit("columnSort", next, () => {
|
|
2062
|
+
options.onColumnSortChange?.({ columnId, sort: next });
|
|
2063
|
+
});
|
|
2064
|
+
},
|
|
2065
|
+
setGroupBy(groupBy) {
|
|
2066
|
+
const columnId = changedEntryColumnId(state.groupBy, groupBy);
|
|
2067
|
+
if (columnId !== void 0) {
|
|
2068
|
+
commitGroupBy(groupBy, columnId);
|
|
2069
|
+
}
|
|
2070
|
+
},
|
|
2071
|
+
toggleGroupBy(columnId) {
|
|
2072
|
+
const grouped = state.groupBy.some(
|
|
2073
|
+
(entry) => entry.columnId === columnId
|
|
2074
|
+
);
|
|
2075
|
+
commitGroupBy(
|
|
2076
|
+
grouped ? state.groupBy.filter((entry) => entry.columnId !== columnId) : [...state.groupBy, { columnId }],
|
|
2077
|
+
columnId
|
|
2078
|
+
);
|
|
2079
|
+
},
|
|
2080
|
+
moveGroupBy(columnId, beforeColumnId) {
|
|
2081
|
+
commitGroupBy(
|
|
2082
|
+
moveGroupByBefore(state.groupBy, columnId, beforeColumnId),
|
|
2083
|
+
columnId
|
|
2084
|
+
);
|
|
2085
|
+
},
|
|
2086
|
+
setGroupExpansion(expansion) {
|
|
2087
|
+
if (!sameItems(state.groupExpansion, expansion)) {
|
|
2088
|
+
commitGroupExpansion(expansion, null);
|
|
2089
|
+
}
|
|
2090
|
+
},
|
|
2091
|
+
toggleGroupExpansion(groupId) {
|
|
2092
|
+
commitGroupExpansion(
|
|
2093
|
+
toggleGroupExpansion(state.groupExpansion, groupId),
|
|
2094
|
+
groupId
|
|
2095
|
+
);
|
|
2096
|
+
},
|
|
2097
|
+
expandAllGroups() {
|
|
2098
|
+
commitGroupExpansion(expandAllGroups(state.groupExpansion), null);
|
|
2099
|
+
},
|
|
2100
|
+
collapseAllGroups() {
|
|
2101
|
+
commitGroupExpansion(collapseAllGroups(getView().displayRows), null);
|
|
2102
|
+
},
|
|
2103
|
+
setPagination: commitPagination,
|
|
2104
|
+
goToPage,
|
|
2105
|
+
nextPage() {
|
|
2106
|
+
goToPage(shownPageIndex() + 1);
|
|
2107
|
+
},
|
|
2108
|
+
previousPage() {
|
|
2109
|
+
goToPage(shownPageIndex() - 1);
|
|
2110
|
+
},
|
|
2111
|
+
setPageSize(pageSize) {
|
|
2112
|
+
const first = { pageIndex: 0, pageSize };
|
|
2113
|
+
commitPagination({
|
|
2114
|
+
...first,
|
|
2115
|
+
pageIndex: paginateRows(getView().displayRows, first).pageIndex
|
|
2116
|
+
});
|
|
2117
|
+
}
|
|
2118
|
+
};
|
|
2119
|
+
function restoreSlices(saved) {
|
|
2120
|
+
if (saved.columnSizing !== void 0) {
|
|
2121
|
+
controller.setColumnSizing(saved.columnSizing);
|
|
2122
|
+
}
|
|
2123
|
+
if (saved.columnOrder !== void 0) {
|
|
2124
|
+
controller.setColumnOrder(saved.columnOrder);
|
|
2125
|
+
}
|
|
2126
|
+
if (saved.columnVisibility !== void 0) {
|
|
2127
|
+
controller.setColumnVisibility(saved.columnVisibility);
|
|
2128
|
+
}
|
|
2129
|
+
if (saved.columnSort !== void 0) {
|
|
2130
|
+
controller.setColumnSort(saved.columnSort);
|
|
2131
|
+
}
|
|
2132
|
+
if (saved.filter !== void 0) {
|
|
2133
|
+
controller.setFilter(reviveFilter(saved.filter, getView().dataColumns));
|
|
2134
|
+
}
|
|
2135
|
+
if (saved.groupBy !== void 0) {
|
|
2136
|
+
controller.setGroupBy(saved.groupBy);
|
|
2137
|
+
}
|
|
2138
|
+
if (saved.groupExpansion !== void 0) {
|
|
2139
|
+
controller.setGroupExpansion(saved.groupExpansion);
|
|
2140
|
+
}
|
|
2141
|
+
if (saved.pagination !== void 0) {
|
|
2142
|
+
controller.setPagination(saved.pagination);
|
|
2143
|
+
}
|
|
2144
|
+
if (saved.rowSelection !== void 0) {
|
|
2145
|
+
controller.setRowSelection(saved.rowSelection);
|
|
2146
|
+
}
|
|
2147
|
+
if (saved.columnSelection !== void 0) {
|
|
2148
|
+
controller.setColumnSelection(saved.columnSelection);
|
|
2149
|
+
}
|
|
2150
|
+
if (saved.cellSelection !== void 0) {
|
|
2151
|
+
controller.setCellSelection(saved.cellSelection);
|
|
2152
|
+
}
|
|
697
2153
|
}
|
|
698
|
-
return
|
|
2154
|
+
return controller;
|
|
699
2155
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
return rows;
|
|
706
|
-
}
|
|
707
|
-
const byId = new Map(columns.map((entry) => [entry.id, entry.column]));
|
|
708
|
-
const active = filter.filter((entry) => isEntryUsable(entry, byId));
|
|
709
|
-
if (active.length === 0) {
|
|
710
|
-
return rows;
|
|
2156
|
+
|
|
2157
|
+
// src/commands/context.ts
|
|
2158
|
+
function selectedRowsOf(rows, selection) {
|
|
2159
|
+
if (selection.length === 0) {
|
|
2160
|
+
return [];
|
|
711
2161
|
}
|
|
712
|
-
const
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
2162
|
+
const selected = new Set(selection);
|
|
2163
|
+
return rows.filter((entry) => selected.has(entry.rowId));
|
|
2164
|
+
}
|
|
2165
|
+
function createCommandContext({
|
|
2166
|
+
controller,
|
|
2167
|
+
target,
|
|
2168
|
+
messages,
|
|
2169
|
+
host
|
|
2170
|
+
}) {
|
|
2171
|
+
const state = controller.getState();
|
|
2172
|
+
const view = controller.getView();
|
|
2173
|
+
let selectedRows;
|
|
2174
|
+
return {
|
|
2175
|
+
target,
|
|
2176
|
+
state,
|
|
2177
|
+
view,
|
|
2178
|
+
options: controller.getOptions(),
|
|
2179
|
+
messages,
|
|
2180
|
+
actions: controller,
|
|
2181
|
+
host,
|
|
2182
|
+
row: target.area === "cell" ? view.rows.find((entry) => entry.rowId === target.rowId) ?? null : null,
|
|
2183
|
+
column: "columnId" in target ? view.dataColumns.find((entry) => entry.id === target.columnId) ?? null : null,
|
|
2184
|
+
selectedRows: () => selectedRows ??= selectedRowsOf(view.rows, state.rowSelection)
|
|
2185
|
+
};
|
|
719
2186
|
}
|
|
720
2187
|
|
|
721
|
-
// src/
|
|
722
|
-
function
|
|
723
|
-
return
|
|
2188
|
+
// src/commands/builtInCommands.ts
|
|
2189
|
+
function isOn(context, areas) {
|
|
2190
|
+
return areas.includes(context.target.area);
|
|
724
2191
|
}
|
|
725
|
-
function
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
2192
|
+
function columnOn(context, areas) {
|
|
2193
|
+
return isOn(context, areas) ? context.column : null;
|
|
2194
|
+
}
|
|
2195
|
+
function isGroupedBy(context, columnId) {
|
|
2196
|
+
return context.state.groupBy.some((entry) => entry.columnId === columnId);
|
|
2197
|
+
}
|
|
2198
|
+
function sortableColumn(context) {
|
|
2199
|
+
const column = columnOn(context, ["header"]);
|
|
2200
|
+
return column !== null && (column.column.sortable ?? context.options.sortableColumns ?? false) ? column : null;
|
|
2201
|
+
}
|
|
2202
|
+
function groupableColumn(context) {
|
|
2203
|
+
const column = columnOn(context, ["header"]);
|
|
2204
|
+
return column !== null && (column.column.groupable ?? context.options.groupableColumns ?? false) && !isGroupedBy(context, column.id) ? column : null;
|
|
2205
|
+
}
|
|
2206
|
+
function groupedColumn(context) {
|
|
2207
|
+
const column = columnOn(context, ["header", "group"]);
|
|
2208
|
+
return column !== null && isGroupedBy(context, column.id) ? column : null;
|
|
2209
|
+
}
|
|
2210
|
+
function hideableColumn(context) {
|
|
2211
|
+
const column = columnOn(context, ["header"]);
|
|
2212
|
+
return column !== null && column.column.hideable !== false ? column : null;
|
|
2213
|
+
}
|
|
2214
|
+
function resizableColumn(context) {
|
|
2215
|
+
const column = columnOn(context, ["header"]);
|
|
2216
|
+
if (column === null) {
|
|
2217
|
+
return null;
|
|
731
2218
|
}
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
2219
|
+
return context.view.columns.find(
|
|
2220
|
+
(entry) => entry.id === column.id && entry.resizable
|
|
2221
|
+
) ?? null;
|
|
2222
|
+
}
|
|
2223
|
+
function resizableColumnIds(context) {
|
|
2224
|
+
return context.view.columns.filter((entry) => entry.resizable).map((entry) => entry.id);
|
|
2225
|
+
}
|
|
2226
|
+
function isAnythingSelectable(selectable) {
|
|
2227
|
+
return [selectable?.rows, selectable?.columns, selectable?.cells].some(
|
|
2228
|
+
(mode) => mode !== void 0 && mode !== false
|
|
2229
|
+
);
|
|
2230
|
+
}
|
|
2231
|
+
function columnName(context) {
|
|
2232
|
+
return context.column === null ? "" : resolveColumnHeaderText(context.column.column);
|
|
2233
|
+
}
|
|
2234
|
+
function columnCommand({
|
|
2235
|
+
id,
|
|
2236
|
+
label,
|
|
2237
|
+
icon,
|
|
2238
|
+
find,
|
|
2239
|
+
isEnabled,
|
|
2240
|
+
isChecked,
|
|
2241
|
+
execute
|
|
2242
|
+
}) {
|
|
2243
|
+
function test(predicate) {
|
|
2244
|
+
return (context) => {
|
|
2245
|
+
const column = find(context);
|
|
2246
|
+
return column !== null && predicate(column, context);
|
|
2247
|
+
};
|
|
743
2248
|
}
|
|
2249
|
+
return {
|
|
2250
|
+
id,
|
|
2251
|
+
label,
|
|
2252
|
+
icon,
|
|
2253
|
+
isVisible: (context) => find(context) !== null,
|
|
2254
|
+
isEnabled: isEnabled === void 0 ? void 0 : test(isEnabled),
|
|
2255
|
+
isChecked: isChecked === void 0 ? void 0 : test(isChecked),
|
|
2256
|
+
execute: (context) => {
|
|
2257
|
+
const column = find(context);
|
|
2258
|
+
if (column !== null) {
|
|
2259
|
+
execute(column, context);
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
};
|
|
744
2263
|
}
|
|
745
|
-
function
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
bucket.rows.push(row);
|
|
2264
|
+
function sortCommand(direction) {
|
|
2265
|
+
return columnCommand({
|
|
2266
|
+
id: direction === "asc" ? "sort.ascending" : "sort.descending",
|
|
2267
|
+
label: ({ messages }) => direction === "asc" ? messages.sortAscending : messages.sortDescending,
|
|
2268
|
+
icon: direction === "asc" ? "sortAscending" : "sortDescending",
|
|
2269
|
+
find: sortableColumn,
|
|
2270
|
+
isChecked: (column, { state }) => sortDirectionFor(state.columnSort, column.id) === direction,
|
|
2271
|
+
execute: (column, { actions }) => {
|
|
2272
|
+
actions.setColumnSort([{ columnId: column.id, direction }]);
|
|
755
2273
|
}
|
|
756
|
-
}
|
|
757
|
-
const groups = [...buckets.values()];
|
|
758
|
-
groups.sort((a, b) => {
|
|
759
|
-
const result = compareValues(a.value, b.value, column.type);
|
|
760
|
-
return direction === "asc" ? result : -result;
|
|
761
2274
|
});
|
|
762
|
-
return groups;
|
|
763
2275
|
}
|
|
764
|
-
function
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
2276
|
+
function builtInCommands() {
|
|
2277
|
+
return [
|
|
2278
|
+
sortCommand("asc"),
|
|
2279
|
+
sortCommand("desc"),
|
|
2280
|
+
// Cleared rather than set, so the change reports this column — the one
|
|
2281
|
+
// the announcement names — rather than a key that moved up past it.
|
|
2282
|
+
columnCommand({
|
|
2283
|
+
id: "sort.clear",
|
|
2284
|
+
label: ({ messages }) => messages.clearSort,
|
|
2285
|
+
find: sortableColumn,
|
|
2286
|
+
isEnabled: (column, { state }) => sortDirectionFor(state.columnSort, column.id) !== null,
|
|
2287
|
+
execute: (column, { actions }) => {
|
|
2288
|
+
actions.clearColumnSort(column.id);
|
|
2289
|
+
}
|
|
2290
|
+
}),
|
|
2291
|
+
{
|
|
2292
|
+
id: "sort.clearAll",
|
|
2293
|
+
label: ({ messages }) => messages.clearAllSorting,
|
|
2294
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "header"]),
|
|
2295
|
+
isEnabled: ({ state }) => state.columnSort.length > 0,
|
|
2296
|
+
execute: ({ actions }) => {
|
|
2297
|
+
actions.setColumnSort([]);
|
|
2298
|
+
}
|
|
2299
|
+
},
|
|
2300
|
+
columnCommand({
|
|
2301
|
+
id: "group.add",
|
|
2302
|
+
label: (context) => context.messages.groupByColumn(columnName(context)),
|
|
2303
|
+
icon: "group",
|
|
2304
|
+
find: groupableColumn,
|
|
2305
|
+
execute: (column, { actions }) => {
|
|
2306
|
+
actions.toggleGroupBy(column.id);
|
|
2307
|
+
}
|
|
2308
|
+
}),
|
|
2309
|
+
// Toggled rather than set, so the change reports this column — the one
|
|
2310
|
+
// the announcement names — rather than the level that moved up.
|
|
2311
|
+
columnCommand({
|
|
2312
|
+
id: "group.remove",
|
|
2313
|
+
label: (context) => context.messages.removeGroupBy(columnName(context)),
|
|
2314
|
+
icon: "group",
|
|
2315
|
+
find: groupedColumn,
|
|
2316
|
+
execute: (column, { actions }) => {
|
|
2317
|
+
actions.toggleGroupBy(column.id);
|
|
2318
|
+
}
|
|
2319
|
+
}),
|
|
2320
|
+
{
|
|
2321
|
+
id: "group.clearAll",
|
|
2322
|
+
label: ({ messages }) => messages.ungroupAll,
|
|
2323
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar"]) && context.state.groupBy.length > 0,
|
|
2324
|
+
execute: ({ actions }) => {
|
|
2325
|
+
actions.setGroupBy([]);
|
|
2326
|
+
}
|
|
2327
|
+
},
|
|
2328
|
+
{
|
|
2329
|
+
id: "group.expandAll",
|
|
2330
|
+
label: ({ messages }) => messages.expandAllGroups,
|
|
2331
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "group", "cell"]) && context.state.groupBy.length > 0,
|
|
2332
|
+
execute: ({ actions }) => {
|
|
2333
|
+
actions.expandAllGroups();
|
|
2334
|
+
}
|
|
2335
|
+
},
|
|
2336
|
+
{
|
|
2337
|
+
id: "group.collapseAll",
|
|
2338
|
+
label: ({ messages }) => messages.collapseAllGroups,
|
|
2339
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "group", "cell"]) && context.state.groupBy.length > 0,
|
|
2340
|
+
execute: ({ actions }) => {
|
|
2341
|
+
actions.collapseAllGroups();
|
|
2342
|
+
}
|
|
2343
|
+
},
|
|
2344
|
+
columnCommand({
|
|
2345
|
+
id: "column.hide",
|
|
2346
|
+
label: ({ messages }) => messages.hideColumn,
|
|
2347
|
+
icon: "hide",
|
|
2348
|
+
find: hideableColumn,
|
|
2349
|
+
// The last column shown stays: a grid with none has no header left to
|
|
2350
|
+
// bring one back from.
|
|
2351
|
+
isEnabled: (column, { view }) => view.columns.some((entry) => entry.id !== column.id),
|
|
2352
|
+
execute: (column, { actions }) => {
|
|
2353
|
+
actions.hideColumns([column.id]);
|
|
2354
|
+
}
|
|
2355
|
+
}),
|
|
2356
|
+
columnCommand({
|
|
2357
|
+
id: "column.autoFit",
|
|
2358
|
+
label: ({ messages }) => messages.autoFitColumn,
|
|
2359
|
+
icon: "autoFit",
|
|
2360
|
+
find: resizableColumn,
|
|
2361
|
+
execute: (column, { host }) => {
|
|
2362
|
+
host.autoFitColumns([column.id]);
|
|
2363
|
+
}
|
|
2364
|
+
}),
|
|
2365
|
+
{
|
|
2366
|
+
id: "column.autoFitAll",
|
|
2367
|
+
label: ({ messages }) => messages.autoFitAllColumns,
|
|
2368
|
+
icon: "autoFit",
|
|
2369
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "header"]) && resizableColumnIds(context).length > 0,
|
|
2370
|
+
execute: (context) => {
|
|
2371
|
+
context.host.autoFitColumns(resizableColumnIds(context));
|
|
2372
|
+
}
|
|
2373
|
+
},
|
|
2374
|
+
// The target goes along, so the renderer opens the chooser where it was
|
|
2375
|
+
// asked for: beside the toolbar button, below the header, or at the
|
|
2376
|
+
// grid's corner for a call from nowhere in particular.
|
|
2377
|
+
{
|
|
2378
|
+
id: "column.choose",
|
|
2379
|
+
label: ({ messages }) => messages.chooseColumns,
|
|
2380
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "header"]),
|
|
2381
|
+
execute: ({ host, target }) => {
|
|
2382
|
+
host.openPanel("columnChooser", target);
|
|
2383
|
+
}
|
|
2384
|
+
},
|
|
2385
|
+
{
|
|
2386
|
+
id: "selection.selectAllRows",
|
|
2387
|
+
label: ({ messages }) => messages.selectAllRows,
|
|
2388
|
+
isVisible: (context) => isOn(context, ["grid", "cell"]) && context.options.selectable?.rows === "multiple",
|
|
2389
|
+
isEnabled: ({ view }) => view.displayDataRows.length > 0,
|
|
2390
|
+
execute: ({ actions }) => {
|
|
2391
|
+
actions.selectAllRows();
|
|
2392
|
+
}
|
|
2393
|
+
},
|
|
2394
|
+
{
|
|
2395
|
+
id: "selection.clear",
|
|
2396
|
+
label: ({ messages }) => messages.clearSelection,
|
|
2397
|
+
isVisible: (context) => isOn(context, ["grid", "cell", "header"]) && isAnythingSelectable(context.options.selectable),
|
|
2398
|
+
isEnabled: ({ state }) => state.rowSelection.length > 0 || state.columnSelection.length > 0 || state.cellSelection !== null,
|
|
2399
|
+
execute: ({ actions }) => {
|
|
2400
|
+
actions.clearSelection();
|
|
2401
|
+
}
|
|
2402
|
+
},
|
|
2403
|
+
{
|
|
2404
|
+
id: "filter.clearAll",
|
|
2405
|
+
label: ({ messages }) => messages.clearFilters,
|
|
2406
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "header"]),
|
|
2407
|
+
isEnabled: ({ state }) => state.filter.length > 0,
|
|
2408
|
+
execute: ({ actions }) => {
|
|
2409
|
+
actions.setFilter([]);
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
];
|
|
2413
|
+
}
|
|
2414
|
+
|
|
2415
|
+
// src/commands/registry.ts
|
|
2416
|
+
function isCommandVisible(command, context) {
|
|
2417
|
+
return command.isVisible?.(context) ?? true;
|
|
2418
|
+
}
|
|
2419
|
+
function isCommandEnabled(command, context) {
|
|
2420
|
+
return command.isEnabled?.(context) ?? true;
|
|
2421
|
+
}
|
|
2422
|
+
function resolveCommands(commands) {
|
|
2423
|
+
const registry = new Map(
|
|
2424
|
+
builtInCommands().map((command) => [command.id, command])
|
|
2425
|
+
);
|
|
2426
|
+
for (const command of commands ?? []) {
|
|
2427
|
+
if (command.id === "separator") {
|
|
2428
|
+
reportError(
|
|
2429
|
+
"a command cannot have the id `separator`, which a menu reads as a separator, so it is left out."
|
|
2430
|
+
);
|
|
2431
|
+
continue;
|
|
802
2432
|
}
|
|
2433
|
+
registry.set(command.id, command);
|
|
803
2434
|
}
|
|
2435
|
+
return registry;
|
|
804
2436
|
}
|
|
805
|
-
function
|
|
806
|
-
if (
|
|
807
|
-
return
|
|
2437
|
+
function runCommand(command, context) {
|
|
2438
|
+
if (!isCommandVisible(command, context) || !isCommandEnabled(command, context)) {
|
|
2439
|
+
return false;
|
|
808
2440
|
}
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
2441
|
+
command.execute(context);
|
|
2442
|
+
return true;
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
// src/menu/resolveMenu.ts
|
|
2446
|
+
var reportedUnknownIds = /* @__PURE__ */ new WeakMap();
|
|
2447
|
+
function reportUnknownId(commands, id) {
|
|
2448
|
+
let reported = reportedUnknownIds.get(commands);
|
|
2449
|
+
if (reported === void 0) {
|
|
2450
|
+
reported = /* @__PURE__ */ new Set();
|
|
2451
|
+
reportedUnknownIds.set(commands, reported);
|
|
2452
|
+
}
|
|
2453
|
+
if (reported.has(id)) {
|
|
2454
|
+
return;
|
|
813
2455
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
return entry.rowIndex === index && entry.datasetIndex === index ? entry : { ...entry, rowIndex: index, datasetIndex: index };
|
|
819
|
-
}
|
|
820
|
-
return entry.rowIndex === index ? entry : { ...entry, rowIndex: index };
|
|
821
|
-
});
|
|
2456
|
+
reported.add(id);
|
|
2457
|
+
reportError(
|
|
2458
|
+
`the menu or toolbar entry \`${id}\` names no command, so it is left out.`
|
|
2459
|
+
);
|
|
822
2460
|
}
|
|
823
|
-
function
|
|
824
|
-
return
|
|
2461
|
+
function evaluate(value, context) {
|
|
2462
|
+
return typeof value === "function" ? value(context) : value;
|
|
825
2463
|
}
|
|
826
|
-
function
|
|
827
|
-
|
|
2464
|
+
function commandItem(command, context) {
|
|
2465
|
+
if (!isCommandVisible(command, context)) {
|
|
2466
|
+
return null;
|
|
2467
|
+
}
|
|
2468
|
+
return {
|
|
2469
|
+
type: "item",
|
|
2470
|
+
id: command.id,
|
|
2471
|
+
label: evaluate(command.label, context),
|
|
2472
|
+
icon: command.icon,
|
|
2473
|
+
shortcut: command.shortcut,
|
|
2474
|
+
disabled: !isCommandEnabled(command, context),
|
|
2475
|
+
checked: command.isChecked?.(context),
|
|
2476
|
+
run: () => runCommand(command, context)
|
|
2477
|
+
};
|
|
828
2478
|
}
|
|
829
|
-
function
|
|
830
|
-
|
|
2479
|
+
function inlineItem(entry, context) {
|
|
2480
|
+
if (evaluate(entry.hidden ?? false, context)) {
|
|
2481
|
+
return null;
|
|
2482
|
+
}
|
|
2483
|
+
const disabled = evaluate(entry.disabled ?? false, context);
|
|
2484
|
+
return {
|
|
2485
|
+
type: "item",
|
|
2486
|
+
id: entry.id,
|
|
2487
|
+
label: evaluate(entry.label, context),
|
|
2488
|
+
icon: entry.icon,
|
|
2489
|
+
shortcut: entry.shortcut,
|
|
2490
|
+
disabled,
|
|
2491
|
+
checked: entry.checked === void 0 ? void 0 : evaluate(entry.checked, context),
|
|
2492
|
+
run: () => {
|
|
2493
|
+
if (disabled) {
|
|
2494
|
+
return false;
|
|
2495
|
+
}
|
|
2496
|
+
entry.onSelect(context);
|
|
2497
|
+
return true;
|
|
2498
|
+
}
|
|
2499
|
+
};
|
|
831
2500
|
}
|
|
832
|
-
function
|
|
833
|
-
|
|
834
|
-
|
|
2501
|
+
function collapseSeparators(entries, isEdge = () => false) {
|
|
2502
|
+
const collapsed = [];
|
|
2503
|
+
for (const entry of entries) {
|
|
2504
|
+
const previous = collapsed.at(-1);
|
|
2505
|
+
if (entry.type !== "separator") {
|
|
2506
|
+
if (isEdge(entry) && previous?.type === "separator") {
|
|
2507
|
+
collapsed.pop();
|
|
2508
|
+
}
|
|
2509
|
+
collapsed.push(entry);
|
|
2510
|
+
} else if (previous !== void 0 && previous.type !== "separator" && !isEdge(previous)) {
|
|
2511
|
+
collapsed.push(entry);
|
|
2512
|
+
}
|
|
835
2513
|
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
2514
|
+
if (collapsed.at(-1)?.type === "separator") {
|
|
2515
|
+
collapsed.pop();
|
|
2516
|
+
}
|
|
2517
|
+
return collapsed;
|
|
2518
|
+
}
|
|
2519
|
+
function resolveMenuEntry(entry, index, options) {
|
|
2520
|
+
const { commands, context } = options;
|
|
2521
|
+
if (entry === "separator") {
|
|
2522
|
+
return { type: "separator", key: `separator-${String(index)}` };
|
|
2523
|
+
}
|
|
2524
|
+
if (typeof entry === "string") {
|
|
2525
|
+
const command = commands.get(entry);
|
|
2526
|
+
if (command === void 0) {
|
|
2527
|
+
reportUnknownId(commands, entry);
|
|
2528
|
+
return null;
|
|
2529
|
+
}
|
|
2530
|
+
return commandItem(command, context);
|
|
2531
|
+
}
|
|
2532
|
+
return entry.type === "item" ? inlineItem(entry, context) : resolveSubmenu(entry, options);
|
|
2533
|
+
}
|
|
2534
|
+
function resolveSubmenu(entry, options) {
|
|
2535
|
+
const items = resolveMenu(entry.items, options);
|
|
2536
|
+
return items.length === 0 ? null : {
|
|
2537
|
+
type: "submenu",
|
|
2538
|
+
id: entry.id,
|
|
2539
|
+
label: evaluate(entry.label, options.context),
|
|
2540
|
+
icon: entry.icon,
|
|
2541
|
+
items
|
|
839
2542
|
};
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
2543
|
+
}
|
|
2544
|
+
function resolveMenu(entries, options) {
|
|
2545
|
+
return collapseSeparators(
|
|
2546
|
+
entries.map((entry, index) => resolveMenuEntry(entry, index, options)).filter((entry) => entry !== null)
|
|
2547
|
+
);
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
// src/menu/contextMenu.ts
|
|
2551
|
+
var defaultContextMenu = {
|
|
2552
|
+
header: [
|
|
2553
|
+
"sort.ascending",
|
|
2554
|
+
"sort.descending",
|
|
2555
|
+
"sort.clear",
|
|
2556
|
+
"separator",
|
|
2557
|
+
"group.add",
|
|
2558
|
+
"group.remove",
|
|
2559
|
+
"separator",
|
|
2560
|
+
"column.autoFit",
|
|
2561
|
+
"column.autoFitAll",
|
|
2562
|
+
"separator",
|
|
2563
|
+
"column.hide",
|
|
2564
|
+
"column.choose"
|
|
2565
|
+
],
|
|
2566
|
+
cell: [
|
|
2567
|
+
"selection.selectAllRows",
|
|
2568
|
+
"selection.clear",
|
|
2569
|
+
"separator",
|
|
2570
|
+
"group.expandAll",
|
|
2571
|
+
"group.collapseAll"
|
|
2572
|
+
],
|
|
2573
|
+
group: ["group.expandAll", "group.collapseAll", "separator", "group.remove"],
|
|
2574
|
+
body: []
|
|
2575
|
+
};
|
|
2576
|
+
function hasContextMenu(contextMenu, area) {
|
|
2577
|
+
if (contextMenu === void 0 || contextMenu === false) {
|
|
2578
|
+
return false;
|
|
843
2579
|
}
|
|
844
|
-
const
|
|
845
|
-
|
|
846
|
-
return next.length === groupBy.length && next.every((entry, index) => entry === groupBy[index]) ? groupBy : next;
|
|
2580
|
+
const entries = (contextMenu === true ? void 0 : contextMenu[area]) ?? defaultContextMenu[area];
|
|
2581
|
+
return typeof entries === "function" || entries.length > 0;
|
|
847
2582
|
}
|
|
848
|
-
function
|
|
849
|
-
|
|
2583
|
+
function entriesFor(given, request) {
|
|
2584
|
+
if (given === void 0) {
|
|
2585
|
+
return request.defaults;
|
|
2586
|
+
}
|
|
2587
|
+
return typeof given === "function" ? given(request) ?? [] : given;
|
|
2588
|
+
}
|
|
2589
|
+
function contextMenuEntries(contextMenu, context) {
|
|
2590
|
+
if (contextMenu === void 0 || contextMenu === false) {
|
|
2591
|
+
return [];
|
|
2592
|
+
}
|
|
2593
|
+
const options = contextMenu === true ? {} : contextMenu;
|
|
2594
|
+
const { target } = context;
|
|
2595
|
+
switch (target.area) {
|
|
2596
|
+
case "header":
|
|
2597
|
+
return entriesFor(options.header, {
|
|
2598
|
+
target,
|
|
2599
|
+
defaults: defaultContextMenu.header,
|
|
2600
|
+
context
|
|
2601
|
+
});
|
|
2602
|
+
case "cell":
|
|
2603
|
+
return entriesFor(options.cell, {
|
|
2604
|
+
target,
|
|
2605
|
+
defaults: defaultContextMenu.cell,
|
|
2606
|
+
context
|
|
2607
|
+
});
|
|
2608
|
+
case "group":
|
|
2609
|
+
return entriesFor(options.group, {
|
|
2610
|
+
target,
|
|
2611
|
+
defaults: defaultContextMenu.group,
|
|
2612
|
+
context
|
|
2613
|
+
});
|
|
2614
|
+
case "body":
|
|
2615
|
+
return entriesFor(options.body, {
|
|
2616
|
+
target,
|
|
2617
|
+
defaults: defaultContextMenu.body,
|
|
2618
|
+
context
|
|
2619
|
+
});
|
|
2620
|
+
default:
|
|
2621
|
+
return [];
|
|
2622
|
+
}
|
|
850
2623
|
}
|
|
851
2624
|
|
|
852
|
-
// src/
|
|
853
|
-
function
|
|
854
|
-
|
|
2625
|
+
// src/menu/navigation.ts
|
|
2626
|
+
function startIndex(count, from, move) {
|
|
2627
|
+
const focused = from >= 0 && from < count;
|
|
2628
|
+
switch (move) {
|
|
2629
|
+
case "first":
|
|
2630
|
+
return 0;
|
|
2631
|
+
case "last":
|
|
2632
|
+
return count - 1;
|
|
2633
|
+
case "next":
|
|
2634
|
+
return focused ? from + 1 : 0;
|
|
2635
|
+
case "previous":
|
|
2636
|
+
return focused ? from - 1 : count - 1;
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
function nextMenuIndex(entries, from, move) {
|
|
2640
|
+
const count = entries.length;
|
|
2641
|
+
const step = move === "next" || move === "first" ? 1 : -1;
|
|
2642
|
+
const start = startIndex(count, from, move);
|
|
2643
|
+
for (let offset = 0; offset < count; offset++) {
|
|
2644
|
+
const index = ((start + step * offset) % count + count) % count;
|
|
2645
|
+
if (entries[index]?.type !== "separator") {
|
|
2646
|
+
return index;
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
return -1;
|
|
855
2650
|
}
|
|
856
|
-
function
|
|
857
|
-
|
|
2651
|
+
function typeaheadIndex(labels, from, query) {
|
|
2652
|
+
const typed = query.toLowerCase();
|
|
2653
|
+
const firstCodePoint = typed.codePointAt(0);
|
|
2654
|
+
if (firstCodePoint === void 0) {
|
|
2655
|
+
return -1;
|
|
2656
|
+
}
|
|
2657
|
+
const first = String.fromCodePoint(firstCodePoint);
|
|
2658
|
+
const repeated = typed.replaceAll(first, "") === "";
|
|
2659
|
+
const prefix = repeated ? first : typed;
|
|
2660
|
+
const start = repeated ? from + 1 : Math.max(from, 0);
|
|
2661
|
+
for (let offset = 0; offset < labels.length; offset++) {
|
|
2662
|
+
const index = (start + offset) % labels.length;
|
|
2663
|
+
if (labels[index]?.toLowerCase().startsWith(prefix) === true) {
|
|
2664
|
+
return index;
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
return -1;
|
|
858
2668
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
2669
|
+
|
|
2670
|
+
// src/toolbar/options.ts
|
|
2671
|
+
var defaultToolbar = ["search", "spacer", "filter.clearAll", "column.choose"];
|
|
2672
|
+
function toolbarEntries(toolbar) {
|
|
2673
|
+
if (toolbar === void 0 || toolbar === false) {
|
|
2674
|
+
return [];
|
|
2675
|
+
}
|
|
2676
|
+
return (toolbar === true ? void 0 : toolbar.items) ?? defaultToolbar;
|
|
865
2677
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
2678
|
+
|
|
2679
|
+
// src/toolbar/resolveToolbar.ts
|
|
2680
|
+
function isSpacer(entry) {
|
|
2681
|
+
return entry.type === "spacer";
|
|
2682
|
+
}
|
|
2683
|
+
function resolveToolbar(entries, options) {
|
|
2684
|
+
const { commands, contextFor } = options;
|
|
2685
|
+
function resolveEntry(entry, index) {
|
|
2686
|
+
if (entry === "search") {
|
|
2687
|
+
return { type: "search", key: "search" };
|
|
2688
|
+
}
|
|
2689
|
+
if (entry === "spacer") {
|
|
2690
|
+
return { type: "spacer", key: `spacer-${String(index)}` };
|
|
2691
|
+
}
|
|
2692
|
+
if (typeof entry === "object" && entry.type === "custom") {
|
|
2693
|
+
return { type: "custom", id: entry.id, key: entry.id };
|
|
2694
|
+
}
|
|
2695
|
+
if (entry === "separator") {
|
|
2696
|
+
return { type: "separator", key: `separator-${String(index)}` };
|
|
2697
|
+
}
|
|
2698
|
+
const resolved = resolveMenuEntry(entry, index, {
|
|
2699
|
+
commands,
|
|
2700
|
+
context: contextFor({
|
|
2701
|
+
area: "toolbar",
|
|
2702
|
+
itemId: typeof entry === "string" ? entry : entry.id
|
|
2703
|
+
})
|
|
2704
|
+
});
|
|
2705
|
+
if (resolved === null || resolved.type === "separator") {
|
|
2706
|
+
return resolved;
|
|
2707
|
+
}
|
|
2708
|
+
if (resolved.type === "submenu") {
|
|
2709
|
+
return { ...resolved, type: "menu", key: resolved.id };
|
|
2710
|
+
}
|
|
2711
|
+
const { checked, ...item } = resolved;
|
|
2712
|
+
return { ...item, type: "button", key: item.id, pressed: checked };
|
|
869
2713
|
}
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
2714
|
+
return collapseSeparators(
|
|
2715
|
+
entries.map(resolveEntry).filter((entry) => entry !== null),
|
|
2716
|
+
isSpacer
|
|
873
2717
|
);
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
2718
|
+
}
|
|
2719
|
+
|
|
2720
|
+
// src/toolbar/overflow.ts
|
|
2721
|
+
function fitToolbarItems({
|
|
2722
|
+
widths,
|
|
2723
|
+
available,
|
|
2724
|
+
moreWidth,
|
|
2725
|
+
pinned
|
|
2726
|
+
}) {
|
|
2727
|
+
const total = widths.reduce((sum, width) => sum + width, 0);
|
|
2728
|
+
if (total <= available) {
|
|
2729
|
+
return widths.length;
|
|
2730
|
+
}
|
|
2731
|
+
let used = moreWidth + widths.reduce((sum, width, index) => sum + (pinned[index] ? width : 0), 0);
|
|
2732
|
+
for (const [index, width] of widths.entries()) {
|
|
2733
|
+
if (!pinned[index]) {
|
|
2734
|
+
used += width;
|
|
2735
|
+
if (used > available) {
|
|
2736
|
+
return index;
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
return widths.length;
|
|
2741
|
+
}
|
|
2742
|
+
function isPinnedToolbarItem(item) {
|
|
2743
|
+
return item.type === "search" || item.type === "custom";
|
|
2744
|
+
}
|
|
2745
|
+
function asMenuEntry(item) {
|
|
2746
|
+
switch (item.type) {
|
|
2747
|
+
case "button":
|
|
2748
|
+
return {
|
|
2749
|
+
type: "item",
|
|
2750
|
+
id: item.id,
|
|
2751
|
+
label: item.label,
|
|
2752
|
+
icon: item.icon,
|
|
2753
|
+
shortcut: item.shortcut,
|
|
2754
|
+
disabled: item.disabled,
|
|
2755
|
+
checked: item.pressed,
|
|
2756
|
+
run: item.run
|
|
2757
|
+
};
|
|
2758
|
+
case "menu":
|
|
2759
|
+
return {
|
|
2760
|
+
type: "submenu",
|
|
2761
|
+
id: item.id,
|
|
2762
|
+
label: item.label,
|
|
2763
|
+
icon: item.icon,
|
|
2764
|
+
items: item.items
|
|
2765
|
+
};
|
|
2766
|
+
case "separator":
|
|
2767
|
+
return item;
|
|
2768
|
+
default:
|
|
2769
|
+
return null;
|
|
877
2770
|
}
|
|
2771
|
+
}
|
|
2772
|
+
function splitToolbarItems(items, fit) {
|
|
2773
|
+
const fits = (item, index) => index < fit || isPinnedToolbarItem(item);
|
|
878
2774
|
return {
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
2775
|
+
shown: collapseSeparators(
|
|
2776
|
+
items.filter(fits),
|
|
2777
|
+
(item) => item.type === "spacer"
|
|
2778
|
+
),
|
|
2779
|
+
overflow: collapseSeparators(
|
|
2780
|
+
items.filter((item, index) => !fits(item, index)).map(asMenuEntry).filter((entry) => entry !== null)
|
|
2781
|
+
)
|
|
886
2782
|
};
|
|
887
2783
|
}
|
|
888
2784
|
|
|
889
|
-
// src/
|
|
890
|
-
function
|
|
891
|
-
const
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
2785
|
+
// src/toolbar/search.ts
|
|
2786
|
+
function searchTextOf(filter) {
|
|
2787
|
+
const query = filterQueryFor(filter) ?? "";
|
|
2788
|
+
const start = query.startsWith("%") ? 1 : 0;
|
|
2789
|
+
const end = query.endsWith("%") ? -1 : void 0;
|
|
2790
|
+
return query.slice(start, end);
|
|
2791
|
+
}
|
|
2792
|
+
function setSearchText(filter, text) {
|
|
2793
|
+
return setColumnFilter(filter, void 0, text === "" ? "" : `%${text}%`);
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
// src/util/navigation.ts
|
|
2797
|
+
var HEADER_ROW = -1;
|
|
2798
|
+
function clampFocus(focus, rowCount, columnCount2) {
|
|
2799
|
+
return {
|
|
2800
|
+
rowIndex: Math.min(Math.max(focus.rowIndex, HEADER_ROW), rowCount - 1),
|
|
2801
|
+
columnIndex: Math.min(
|
|
2802
|
+
Math.max(focus.columnIndex, 0),
|
|
2803
|
+
Math.max(columnCount2 - 1, 0)
|
|
2804
|
+
)
|
|
2805
|
+
};
|
|
2806
|
+
}
|
|
2807
|
+
function noRowsSkippable() {
|
|
2808
|
+
return false;
|
|
2809
|
+
}
|
|
2810
|
+
function skipSkippableRows(rowIndex, direction, rowCount, isSkippableRow, fallback) {
|
|
2811
|
+
let next = rowIndex;
|
|
2812
|
+
while (next >= 0 && next < rowCount && isSkippableRow(next)) {
|
|
2813
|
+
const stepped = next + direction;
|
|
2814
|
+
if (stepped < HEADER_ROW || stepped >= rowCount) {
|
|
2815
|
+
return fallback;
|
|
907
2816
|
}
|
|
2817
|
+
next = stepped;
|
|
908
2818
|
}
|
|
909
|
-
|
|
910
|
-
units.push({ start, end: rows.length });
|
|
911
|
-
}
|
|
912
|
-
return units;
|
|
2819
|
+
return next;
|
|
913
2820
|
}
|
|
914
|
-
function
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
const pageCount = Math.ceil(units.length / pageSize);
|
|
918
|
-
if (pageCount === 0) {
|
|
919
|
-
return { rows: [], pageCount: 0, pageIndex: 0 };
|
|
2821
|
+
function nextFocusForKey(key, modifiers, focus, rowCount, columnCount2, pageSize, isSkippableRow = noRowsSkippable) {
|
|
2822
|
+
if (modifiers.altKey) {
|
|
2823
|
+
return null;
|
|
920
2824
|
}
|
|
921
|
-
const
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
2825
|
+
const { rowIndex, columnIndex } = focus;
|
|
2826
|
+
switch (key) {
|
|
2827
|
+
case "ArrowLeft":
|
|
2828
|
+
if (modifiers.ctrlKey) return null;
|
|
2829
|
+
return { rowIndex, columnIndex: columnIndex - 1 };
|
|
2830
|
+
case "ArrowRight":
|
|
2831
|
+
if (modifiers.ctrlKey) return null;
|
|
2832
|
+
return { rowIndex, columnIndex: columnIndex + 1 };
|
|
2833
|
+
case "ArrowUp":
|
|
2834
|
+
return {
|
|
2835
|
+
rowIndex: skipSkippableRows(
|
|
2836
|
+
rowIndex - 1,
|
|
2837
|
+
-1,
|
|
2838
|
+
rowCount,
|
|
2839
|
+
isSkippableRow,
|
|
2840
|
+
rowIndex
|
|
2841
|
+
),
|
|
2842
|
+
columnIndex
|
|
2843
|
+
};
|
|
2844
|
+
case "ArrowDown":
|
|
2845
|
+
return {
|
|
2846
|
+
rowIndex: skipSkippableRows(
|
|
2847
|
+
rowIndex + 1,
|
|
2848
|
+
1,
|
|
2849
|
+
rowCount,
|
|
2850
|
+
isSkippableRow,
|
|
2851
|
+
rowIndex
|
|
2852
|
+
),
|
|
2853
|
+
columnIndex
|
|
2854
|
+
};
|
|
2855
|
+
// Ctrl takes Home and End to the grid's ends rather than the row's, which
|
|
2856
|
+
// is the one place Ctrl is navigation rather than reorder.
|
|
2857
|
+
case "Home":
|
|
2858
|
+
return modifiers.ctrlKey ? { rowIndex: HEADER_ROW, columnIndex: 0 } : { rowIndex, columnIndex: 0 };
|
|
2859
|
+
case "End":
|
|
2860
|
+
return modifiers.ctrlKey ? {
|
|
2861
|
+
rowIndex: skipSkippableRows(
|
|
2862
|
+
rowCount - 1,
|
|
2863
|
+
-1,
|
|
2864
|
+
rowCount,
|
|
2865
|
+
isSkippableRow,
|
|
2866
|
+
rowIndex
|
|
2867
|
+
),
|
|
2868
|
+
columnIndex: columnCount2 - 1
|
|
2869
|
+
} : { rowIndex, columnIndex: columnCount2 - 1 };
|
|
2870
|
+
case "PageUp":
|
|
2871
|
+
return {
|
|
2872
|
+
rowIndex: skipSkippableRows(
|
|
2873
|
+
rowIndex - pageSize,
|
|
2874
|
+
-1,
|
|
2875
|
+
rowCount,
|
|
2876
|
+
isSkippableRow,
|
|
2877
|
+
rowIndex
|
|
2878
|
+
),
|
|
2879
|
+
columnIndex
|
|
2880
|
+
};
|
|
2881
|
+
case "PageDown":
|
|
2882
|
+
return {
|
|
2883
|
+
rowIndex: skipSkippableRows(
|
|
2884
|
+
rowIndex + pageSize,
|
|
2885
|
+
1,
|
|
2886
|
+
rowCount,
|
|
2887
|
+
isSkippableRow,
|
|
2888
|
+
rowIndex
|
|
2889
|
+
),
|
|
2890
|
+
columnIndex
|
|
2891
|
+
};
|
|
2892
|
+
default:
|
|
2893
|
+
return null;
|
|
926
2894
|
}
|
|
927
|
-
const page = rows.slice(firstUnit.start, lastUnit.end);
|
|
928
|
-
const renumbered = page.map(
|
|
929
|
-
(entry, index) => entry.rowIndex === index ? entry : { ...entry, rowIndex: index }
|
|
930
|
-
);
|
|
931
|
-
return { rows: renumbered, pageCount, pageIndex };
|
|
932
2895
|
}
|
|
933
2896
|
|
|
934
2897
|
// src/util/paginationWindow.ts
|
|
@@ -966,180 +2929,522 @@ function paginationWindow(currentPage, pageCount, options) {
|
|
|
966
2929
|
return entries;
|
|
967
2930
|
}
|
|
968
2931
|
|
|
969
|
-
// src/util/
|
|
970
|
-
function
|
|
971
|
-
return
|
|
2932
|
+
// src/util/columnChooser.ts
|
|
2933
|
+
function isHideable(column) {
|
|
2934
|
+
return column.column.hideable !== false;
|
|
972
2935
|
}
|
|
973
|
-
function
|
|
974
|
-
return
|
|
2936
|
+
function matchesSearch(label, search) {
|
|
2937
|
+
return label.toLowerCase().includes(search.toLowerCase());
|
|
975
2938
|
}
|
|
976
|
-
function
|
|
977
|
-
if (
|
|
978
|
-
return "
|
|
979
|
-
}
|
|
980
|
-
if (value instanceof Date) {
|
|
981
|
-
return `\0date:${String(value.getTime())}`;
|
|
2939
|
+
function selectAllStateOf(states) {
|
|
2940
|
+
if (states.length > 0 && states.every(Boolean)) {
|
|
2941
|
+
return "checked";
|
|
982
2942
|
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
2943
|
+
return states.some(Boolean) ? "mixed" : "unchecked";
|
|
2944
|
+
}
|
|
2945
|
+
function resolveColumnChooser({
|
|
2946
|
+
columns,
|
|
2947
|
+
visibility,
|
|
2948
|
+
search = ""
|
|
2949
|
+
}) {
|
|
2950
|
+
const all = columns.map((column) => ({
|
|
2951
|
+
column,
|
|
2952
|
+
label: resolveColumnHeaderText(column.column),
|
|
2953
|
+
visible: isColumnVisible(column.column, visibility)
|
|
2954
|
+
}));
|
|
2955
|
+
const shownCount = all.filter(({ visible }) => visible).length;
|
|
2956
|
+
const matched = all.filter(({ label }) => matchesSearch(label, search));
|
|
2957
|
+
const entries = matched.map(
|
|
2958
|
+
({ column, label, visible }) => ({
|
|
2959
|
+
columnId: column.id,
|
|
2960
|
+
label,
|
|
2961
|
+
visible,
|
|
2962
|
+
disabled: visible && (!isHideable(column) || shownCount === 1)
|
|
2963
|
+
})
|
|
2964
|
+
);
|
|
2965
|
+
const covered = matched.filter(({ column }) => isHideable(column));
|
|
2966
|
+
const selectAll2 = selectAllStateOf(covered.map(({ visible }) => visible));
|
|
2967
|
+
return {
|
|
2968
|
+
entries,
|
|
2969
|
+
selectAll: selectAll2,
|
|
2970
|
+
toggleAll: toggleAllOf(covered, selectAll2, shownCount)
|
|
2971
|
+
};
|
|
2972
|
+
}
|
|
2973
|
+
function toggleAllOf(covered, selectAll2, shownCount) {
|
|
2974
|
+
const show = selectAll2 !== "checked";
|
|
2975
|
+
const changing = covered.filter(({ visible }) => visible !== show).map(({ column }) => column.id);
|
|
2976
|
+
const columnIds = !show && changing.length === shownCount ? changing.slice(1) : changing;
|
|
2977
|
+
return columnIds.length === 0 ? null : { visible: show, columnIds };
|
|
2978
|
+
}
|
|
2979
|
+
|
|
2980
|
+
// src/messages.ts
|
|
2981
|
+
function columnCount(count, change) {
|
|
2982
|
+
return count === 1 ? `1 column ${change}` : `${String(count)} columns ${change}`;
|
|
2983
|
+
}
|
|
2984
|
+
function searchResults(count) {
|
|
2985
|
+
if (count === 0) {
|
|
2986
|
+
return "No rows found";
|
|
2987
|
+
}
|
|
2988
|
+
return count === 1 ? "1 row found" : `${String(count)} rows found`;
|
|
2989
|
+
}
|
|
2990
|
+
function selectedCount(count, target) {
|
|
2991
|
+
const noun = target === "rows" ? "row" : "column";
|
|
2992
|
+
if (count === 0) {
|
|
2993
|
+
return `No ${noun}s selected`;
|
|
2994
|
+
}
|
|
2995
|
+
return count === 1 ? `1 ${noun} selected` : `${String(count)} ${noun}s selected`;
|
|
2996
|
+
}
|
|
2997
|
+
var defaultMessages = {
|
|
2998
|
+
pagerLabel: "Pagination",
|
|
2999
|
+
previousPage: "Previous",
|
|
3000
|
+
nextPage: "Next",
|
|
3001
|
+
pageSizeLabel: "Rows per page",
|
|
3002
|
+
pageButtonLabel: (page) => `Page ${String(page)}`,
|
|
3003
|
+
pageStatus: (page, pageCount) => `Page ${String(page)} of ${String(pageCount)}`,
|
|
3004
|
+
loadingMore: "Loading more\u2026",
|
|
3005
|
+
booleanTrue: "true",
|
|
3006
|
+
booleanFalse: "false",
|
|
3007
|
+
blankGroupValue: "(blank)",
|
|
3008
|
+
groupByBarLabel: "Grouped by",
|
|
3009
|
+
groupByBarPlaceholder: "Drag a column here to group by it",
|
|
3010
|
+
groupByChipLabel: (columnName2, level, levelCount) => levelCount > 1 ? `${columnName2}, level ${String(level)} of ${String(levelCount)}` : columnName2,
|
|
3011
|
+
removeGroupBy: (columnName2) => `Stop grouping by ${columnName2}`,
|
|
3012
|
+
columnResized: (columnName2, width) => `${columnName2}, ${String(width)} pixels wide`,
|
|
3013
|
+
columnMoved: (columnName2, position, columnCount2) => `${columnName2}, column ${String(position)} of ${String(columnCount2)}`,
|
|
3014
|
+
columnSorted: (columnName2, direction, key, keyCount) => {
|
|
3015
|
+
const sorted = `${columnName2} sorted ${direction === "asc" ? "ascending" : "descending"}`;
|
|
3016
|
+
return keyCount > 1 ? `${sorted}, key ${String(key)} of ${String(keyCount)}` : sorted;
|
|
3017
|
+
},
|
|
3018
|
+
sortCleared: (columnName2) => `${columnName2}, sort cleared`,
|
|
3019
|
+
groupedBy: (columnName2, level, levelCount) => levelCount > 1 ? `Grouped by ${columnName2}, level ${String(level)} of ${String(levelCount)}` : `Grouped by ${columnName2}`,
|
|
3020
|
+
groupingRemoved: (columnName2) => `${columnName2}, grouping removed`,
|
|
3021
|
+
groupExpanded: "Group expanded",
|
|
3022
|
+
groupCollapsed: "Group collapsed",
|
|
3023
|
+
allGroupsExpanded: "All groups expanded",
|
|
3024
|
+
allGroupsCollapsed: "All groups collapsed",
|
|
3025
|
+
selectedCount,
|
|
3026
|
+
cellSelected: (columnName2, rowNumber) => `${columnName2}, row ${String(rowNumber)}, selected`,
|
|
3027
|
+
columnHidden: (columnName2) => `${columnName2}, column hidden`,
|
|
3028
|
+
columnShown: (columnName2) => `${columnName2}, column shown`,
|
|
3029
|
+
columnsHidden: (count) => columnCount(count, "hidden"),
|
|
3030
|
+
columnsShown: (count) => columnCount(count, "shown"),
|
|
3031
|
+
columnsAutoFitted: (count) => columnCount(count, "autofitted"),
|
|
3032
|
+
headerMenuLabel: (columnName2) => `${columnName2} column menu`,
|
|
3033
|
+
cellMenuLabel: "Row menu",
|
|
3034
|
+
groupMenuLabel: "Group menu",
|
|
3035
|
+
bodyMenuLabel: "Grid menu",
|
|
3036
|
+
sortAscending: "Sort ascending",
|
|
3037
|
+
sortDescending: "Sort descending",
|
|
3038
|
+
clearSort: "Clear sort",
|
|
3039
|
+
clearAllSorting: "Clear all sorting",
|
|
3040
|
+
groupByColumn: (columnName2) => `Group by ${columnName2}`,
|
|
3041
|
+
ungroupAll: "Ungroup all",
|
|
3042
|
+
expandAllGroups: "Expand all groups",
|
|
3043
|
+
collapseAllGroups: "Collapse all groups",
|
|
3044
|
+
hideColumn: "Hide column",
|
|
3045
|
+
autoFitColumn: "Autofit column",
|
|
3046
|
+
autoFitAllColumns: "Autofit all columns",
|
|
3047
|
+
selectAllRows: "Select all rows",
|
|
3048
|
+
clearSelection: "Clear selection",
|
|
3049
|
+
clearFilters: "Clear filters",
|
|
3050
|
+
chooseColumns: "Choose columns",
|
|
3051
|
+
toolbarLabel: "Grid toolbar",
|
|
3052
|
+
searchLabel: "Search",
|
|
3053
|
+
searchPlaceholder: "Search\u2026",
|
|
3054
|
+
moreToolbarItems: "More",
|
|
3055
|
+
searchResults,
|
|
3056
|
+
columnChooserLabel: "Columns",
|
|
3057
|
+
columnChooserSearchLabel: "Search columns",
|
|
3058
|
+
showAllColumns: "Show all",
|
|
3059
|
+
noMatchingColumns: "No matching columns"
|
|
3060
|
+
};
|
|
3061
|
+
function resolveMessages(messages) {
|
|
3062
|
+
return messages === void 0 ? defaultMessages : { ...defaultMessages, ...messages };
|
|
3063
|
+
}
|
|
3064
|
+
|
|
3065
|
+
// src/util/format.ts
|
|
3066
|
+
var DATE_FIELDS = {
|
|
3067
|
+
year: "numeric",
|
|
3068
|
+
month: "numeric",
|
|
3069
|
+
day: "numeric"
|
|
3070
|
+
};
|
|
3071
|
+
var TIME_FIELDS = {
|
|
3072
|
+
hour: "numeric",
|
|
3073
|
+
minute: "numeric",
|
|
3074
|
+
second: "numeric"
|
|
3075
|
+
};
|
|
3076
|
+
var DATE_TIME_FIELDS = {
|
|
3077
|
+
...DATE_FIELDS,
|
|
3078
|
+
...TIME_FIELDS
|
|
3079
|
+
};
|
|
3080
|
+
var CURRENCY_DIGITS = {
|
|
3081
|
+
minimumFractionDigits: 2,
|
|
3082
|
+
maximumFractionDigits: 2
|
|
3083
|
+
};
|
|
3084
|
+
var NO_FORMAT = {};
|
|
3085
|
+
var formatters = /* @__PURE__ */ new WeakMap();
|
|
3086
|
+
function cached(format, key, create) {
|
|
3087
|
+
const owner = format ?? NO_FORMAT;
|
|
3088
|
+
let byKey = formatters.get(owner);
|
|
3089
|
+
if (byKey === void 0) {
|
|
3090
|
+
byKey = /* @__PURE__ */ new Map();
|
|
3091
|
+
formatters.set(owner, byKey);
|
|
3092
|
+
}
|
|
3093
|
+
const existing = byKey.get(key);
|
|
3094
|
+
if (existing !== void 0) {
|
|
3095
|
+
return existing;
|
|
3096
|
+
}
|
|
3097
|
+
const formatter = create();
|
|
3098
|
+
byKey.set(key, formatter);
|
|
3099
|
+
return formatter;
|
|
3100
|
+
}
|
|
3101
|
+
function localeKey(locale) {
|
|
3102
|
+
return typeof locale === "string" ? locale : locale?.join(",") ?? "";
|
|
3103
|
+
}
|
|
3104
|
+
function isNumericType(type) {
|
|
3105
|
+
return type === "number" || type === "decimal" || type === "currency" || type === "percent";
|
|
3106
|
+
}
|
|
3107
|
+
function numberOptions(type, format) {
|
|
3108
|
+
switch (type) {
|
|
3109
|
+
case "currency":
|
|
3110
|
+
return format?.currency === void 0 ? { ...CURRENCY_DIGITS, ...format } : { style: "currency", ...format };
|
|
3111
|
+
case "percent":
|
|
3112
|
+
return { style: "percent", ...format };
|
|
992
3113
|
default:
|
|
993
|
-
return
|
|
3114
|
+
return format ?? {};
|
|
994
3115
|
}
|
|
995
3116
|
}
|
|
996
|
-
function
|
|
997
|
-
if (
|
|
998
|
-
return
|
|
3117
|
+
function dateOptions(type, format) {
|
|
3118
|
+
if (format !== void 0) {
|
|
3119
|
+
return format;
|
|
999
3120
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
return
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1039
|
-
for (const value of values) {
|
|
1040
|
-
if (isEmpty2(value)) continue;
|
|
1041
|
-
seen.add(distinctKey(value));
|
|
1042
|
-
}
|
|
1043
|
-
return seen.size;
|
|
3121
|
+
if (type === "date") return DATE_FIELDS;
|
|
3122
|
+
if (type === "time") return TIME_FIELDS;
|
|
3123
|
+
return DATE_TIME_FIELDS;
|
|
3124
|
+
}
|
|
3125
|
+
function formatNumber(value, type, format, locale) {
|
|
3126
|
+
const formatter = cached(
|
|
3127
|
+
format,
|
|
3128
|
+
`number|${type ?? ""}|${localeKey(locale)}`,
|
|
3129
|
+
() => new Intl.NumberFormat(
|
|
3130
|
+
locale,
|
|
3131
|
+
numberOptions(type, format)
|
|
3132
|
+
)
|
|
3133
|
+
);
|
|
3134
|
+
return formatter.format(value);
|
|
3135
|
+
}
|
|
3136
|
+
function formatDate(value, type, format, locale) {
|
|
3137
|
+
if (Number.isNaN(value.getTime())) {
|
|
3138
|
+
return String(value);
|
|
3139
|
+
}
|
|
3140
|
+
const formatter = cached(
|
|
3141
|
+
format,
|
|
3142
|
+
`date|${type ?? ""}|${localeKey(locale)}`,
|
|
3143
|
+
() => new Intl.DateTimeFormat(
|
|
3144
|
+
locale,
|
|
3145
|
+
dateOptions(type, format)
|
|
3146
|
+
)
|
|
3147
|
+
);
|
|
3148
|
+
return formatter.format(value);
|
|
3149
|
+
}
|
|
3150
|
+
function formatOther(value) {
|
|
3151
|
+
if (typeof value === "function") {
|
|
3152
|
+
return "(function)";
|
|
3153
|
+
}
|
|
3154
|
+
if (typeof value === "object" && value !== null) {
|
|
3155
|
+
try {
|
|
3156
|
+
return JSON.stringify(value);
|
|
3157
|
+
} catch {
|
|
3158
|
+
return Object.prototype.toString.call(value);
|
|
1044
3159
|
}
|
|
1045
3160
|
}
|
|
3161
|
+
return String(value);
|
|
1046
3162
|
}
|
|
1047
|
-
function
|
|
1048
|
-
|
|
3163
|
+
function formatCellValue(value, column, locale, row, messages) {
|
|
3164
|
+
if (column.valueFormatter !== void 0) {
|
|
3165
|
+
return column.valueFormatter(value, row);
|
|
3166
|
+
}
|
|
3167
|
+
if (value === null || value === void 0) {
|
|
3168
|
+
return "";
|
|
3169
|
+
}
|
|
3170
|
+
if (typeof value === "boolean" && messages !== void 0) {
|
|
3171
|
+
return value ? messages.booleanTrue : messages.booleanFalse;
|
|
3172
|
+
}
|
|
3173
|
+
const { type, format } = column;
|
|
3174
|
+
const formatted = format !== void 0 || locale !== void 0;
|
|
3175
|
+
if (value instanceof Date) {
|
|
3176
|
+
return formatted ? formatDate(value, type, format, locale) : value.toLocaleString();
|
|
3177
|
+
}
|
|
3178
|
+
if (formatted && (typeof value === "number" || typeof value === "bigint") && isNumericType(type)) {
|
|
3179
|
+
return formatNumber(value, type, format, locale);
|
|
3180
|
+
}
|
|
3181
|
+
if (typeof value === "string") {
|
|
3182
|
+
return value;
|
|
3183
|
+
}
|
|
3184
|
+
return formatOther(value);
|
|
1049
3185
|
}
|
|
1050
|
-
function
|
|
1051
|
-
const
|
|
1052
|
-
|
|
1053
|
-
|
|
3186
|
+
function aggregateFormatColumn(value, spec, column) {
|
|
3187
|
+
const plain = {
|
|
3188
|
+
field: spec.columnId,
|
|
3189
|
+
type: "number"
|
|
3190
|
+
};
|
|
3191
|
+
if (column === void 0 || spec.fn === "count" || spec.fn === "countDistinct") {
|
|
3192
|
+
return plain;
|
|
1054
3193
|
}
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
3194
|
+
const numeric = typeof value === "number" || typeof value === "bigint";
|
|
3195
|
+
return column.valueFormatter === void 0 && numeric && !isNumericType(column.type) ? plain : column;
|
|
3196
|
+
}
|
|
3197
|
+
function formatAggregateValue(value, spec, column, locale) {
|
|
3198
|
+
const target = aggregateFormatColumn(value, spec, column);
|
|
3199
|
+
if (typeof value === "number" && target.valueFormatter === void 0 && target.format === void 0 && locale === void 0) {
|
|
3200
|
+
return value.toLocaleString();
|
|
1059
3201
|
}
|
|
1060
|
-
return
|
|
3202
|
+
return formatCellValue(value, target, locale);
|
|
1061
3203
|
}
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
const columnId = groupBy[level]?.columnId;
|
|
1067
|
-
const column = columnId === void 0 ? void 0 : byId.get(columnId);
|
|
1068
|
-
if (column === void 0) return false;
|
|
1069
|
-
return bucketKey(accessDotted(row.row, column.field)) === key;
|
|
1070
|
-
})
|
|
1071
|
-
).map((row) => row.row);
|
|
3204
|
+
|
|
3205
|
+
// src/util/filterEditor.ts
|
|
3206
|
+
function touchesColumn(entry, columnId) {
|
|
3207
|
+
return "combinator" in entry ? entry.entries.some((nested) => touchesColumn(nested, columnId)) : entry.columnId === columnId;
|
|
1072
3208
|
}
|
|
1073
|
-
function
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
3209
|
+
function isOperatorOn(entry, columnId) {
|
|
3210
|
+
return "operator" in entry && entry.columnId === columnId;
|
|
3211
|
+
}
|
|
3212
|
+
function isConditionOn(entry, columnId) {
|
|
3213
|
+
return isOperatorOn(entry, columnId) && entry.operator !== "in";
|
|
3214
|
+
}
|
|
3215
|
+
function valuesShape(entry, columnId) {
|
|
3216
|
+
const values = (candidate) => candidate !== void 0 && isOperatorOn(candidate, columnId) && candidate.operator === "in" && isScalarList(candidate.value) ? candidate.value : void 0;
|
|
3217
|
+
const blanks = (candidate) => candidate !== void 0 && isOperatorOn(candidate, columnId) && candidate.operator === "isEmpty";
|
|
3218
|
+
const alone = values(entry);
|
|
3219
|
+
if (alone !== void 0) {
|
|
3220
|
+
return { kind: "values", values: alone, includeBlanks: false };
|
|
3221
|
+
}
|
|
3222
|
+
if (!("combinator" in entry) || entry.combinator !== "or" || entry.entries.length !== 2) {
|
|
3223
|
+
return void 0;
|
|
3224
|
+
}
|
|
3225
|
+
const [first, second] = entry.entries;
|
|
3226
|
+
const listed = blanks(first) ? values(second) : values(first);
|
|
3227
|
+
return listed !== void 0 && (blanks(first) || blanks(second)) ? { kind: "values", values: listed, includeBlanks: true } : void 0;
|
|
3228
|
+
}
|
|
3229
|
+
function conditionsShape(touching, columnId) {
|
|
3230
|
+
const [only] = touching;
|
|
3231
|
+
const grouped = touching.length === 1 && only !== void 0 && "combinator" in only;
|
|
3232
|
+
const entries = grouped ? only.entries : touching;
|
|
3233
|
+
const combinator = grouped ? only.combinator : "and";
|
|
3234
|
+
if (entries.length === 0 || entries.length > 2) {
|
|
3235
|
+
return void 0;
|
|
3236
|
+
}
|
|
3237
|
+
const conditions = entries.filter((entry) => isConditionOn(entry, columnId));
|
|
3238
|
+
return conditions.length === entries.length ? { kind: "conditions", conditions, combinator } : void 0;
|
|
3239
|
+
}
|
|
3240
|
+
function describeColumnFilter(filter, columnId) {
|
|
3241
|
+
const touching = filter.filter((entry) => touchesColumn(entry, columnId));
|
|
3242
|
+
const [only] = touching;
|
|
3243
|
+
if (only === void 0) {
|
|
3244
|
+
return { kind: "none" };
|
|
3245
|
+
}
|
|
3246
|
+
return (touching.length === 1 ? valuesShape(only, columnId) : void 0) ?? conditionsShape(touching, columnId) ?? { kind: "custom" };
|
|
3247
|
+
}
|
|
3248
|
+
function isOfferable(value) {
|
|
3249
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value instanceof Date;
|
|
3250
|
+
}
|
|
3251
|
+
function matchesSearch2(label, search) {
|
|
3252
|
+
return label.toLowerCase().includes(search.toLowerCase());
|
|
3253
|
+
}
|
|
3254
|
+
function columnValueOptions(rows, dataColumns, filter, columnId, query = {}) {
|
|
3255
|
+
const column = dataColumns.find((entry) => entry.id === columnId)?.column;
|
|
3256
|
+
if (column === void 0) {
|
|
3257
|
+
return { options: [], total: 0, truncated: false, blanks: 0 };
|
|
3258
|
+
}
|
|
3259
|
+
const keep = compileFilter(
|
|
3260
|
+
filter.filter((entry) => !touchesColumn(entry, columnId)),
|
|
3261
|
+
dataColumns
|
|
3262
|
+
);
|
|
3263
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
3264
|
+
let blanks = 0;
|
|
3265
|
+
for (const row of rows) {
|
|
3266
|
+
if (keep !== void 0 && !keep(row)) {
|
|
1079
3267
|
continue;
|
|
1080
3268
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
3269
|
+
const value = accessDotted(row, column.field);
|
|
3270
|
+
if (isEmptyCell(value)) {
|
|
3271
|
+
blanks++;
|
|
3272
|
+
} else if (isOfferable(value)) {
|
|
3273
|
+
const key = filterValueKey(value, column.type);
|
|
3274
|
+
const bucket = buckets.get(key);
|
|
3275
|
+
if (bucket === void 0) {
|
|
3276
|
+
buckets.set(key, { value, row, count: 1 });
|
|
3277
|
+
} else {
|
|
3278
|
+
bucket.count++;
|
|
3279
|
+
}
|
|
1088
3280
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
3281
|
+
}
|
|
3282
|
+
const { search = "", limit, locale } = query;
|
|
3283
|
+
const messages = resolveMessages(query.messages);
|
|
3284
|
+
const options = [...buckets.values()].sort((a, b) => compareValues(a.value, b.value, column.type)).map(({ value, row, count }) => ({
|
|
3285
|
+
value,
|
|
3286
|
+
label: formatCellValue(value, column, locale, row, messages),
|
|
3287
|
+
count
|
|
3288
|
+
})).filter((option) => matchesSearch2(option.label, search));
|
|
3289
|
+
const shown = limit === void 0 ? options : options.slice(0, limit);
|
|
3290
|
+
return {
|
|
3291
|
+
options: shown,
|
|
3292
|
+
total: options.length,
|
|
3293
|
+
truncated: shown.length < options.length,
|
|
3294
|
+
blanks,
|
|
3295
|
+
columnType: column.type
|
|
3296
|
+
};
|
|
3297
|
+
}
|
|
3298
|
+
function keyOf2(draft, value) {
|
|
3299
|
+
return filterValueKey(value, draft.columnType);
|
|
3300
|
+
}
|
|
3301
|
+
function everyOption(draft) {
|
|
3302
|
+
return new Map(
|
|
3303
|
+
draft.options.map((option) => [keyOf2(draft, option.value), option.value])
|
|
3304
|
+
);
|
|
3305
|
+
}
|
|
3306
|
+
function initialFilterEditorDraft(shape, values) {
|
|
3307
|
+
const draft = {
|
|
3308
|
+
mode: "values",
|
|
3309
|
+
options: values.options,
|
|
3310
|
+
blanks: values.blanks,
|
|
3311
|
+
search: "",
|
|
3312
|
+
checked: everyOption(values),
|
|
3313
|
+
includeBlanks: values.blanks > 0,
|
|
3314
|
+
conditions: [],
|
|
3315
|
+
combinator: "and",
|
|
3316
|
+
columnType: values.columnType
|
|
3317
|
+
};
|
|
3318
|
+
switch (shape.kind) {
|
|
3319
|
+
case "values":
|
|
3320
|
+
return {
|
|
3321
|
+
...draft,
|
|
3322
|
+
checked: new Map(
|
|
3323
|
+
shape.values.filter((value) => !isEmptyCell(value)).map((value) => [keyOf2(draft, value), value])
|
|
3324
|
+
),
|
|
3325
|
+
includeBlanks: shape.includeBlanks
|
|
1113
3326
|
};
|
|
1114
|
-
|
|
3327
|
+
case "conditions":
|
|
3328
|
+
return {
|
|
3329
|
+
...draft,
|
|
3330
|
+
mode: "conditions",
|
|
3331
|
+
conditions: shape.conditions,
|
|
3332
|
+
combinator: shape.combinator
|
|
3333
|
+
};
|
|
3334
|
+
case "none":
|
|
3335
|
+
case "custom":
|
|
3336
|
+
return draft;
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
function visibleFilterOptions(draft) {
|
|
3340
|
+
return draft.search === "" ? draft.options : draft.options.filter(
|
|
3341
|
+
(option) => matchesSearch2(option.label, draft.search)
|
|
3342
|
+
);
|
|
3343
|
+
}
|
|
3344
|
+
function selectAllCoversBlanks(draft) {
|
|
3345
|
+
return draft.search === "" && draft.blanks > 0;
|
|
3346
|
+
}
|
|
3347
|
+
function selectAllState(draft) {
|
|
3348
|
+
const states = visibleFilterOptions(draft).map(
|
|
3349
|
+
(option) => draft.checked.has(keyOf2(draft, option.value))
|
|
3350
|
+
);
|
|
3351
|
+
if (selectAllCoversBlanks(draft)) {
|
|
3352
|
+
states.push(draft.includeBlanks);
|
|
3353
|
+
}
|
|
3354
|
+
if (states.length > 0 && states.every(Boolean)) {
|
|
3355
|
+
return "checked";
|
|
3356
|
+
}
|
|
3357
|
+
return states.some(Boolean) ? "mixed" : "unchecked";
|
|
3358
|
+
}
|
|
3359
|
+
function toggleAll(draft) {
|
|
3360
|
+
const check = selectAllState(draft) !== "checked";
|
|
3361
|
+
const checked = new Map(draft.checked);
|
|
3362
|
+
for (const option of visibleFilterOptions(draft)) {
|
|
3363
|
+
const key = keyOf2(draft, option.value);
|
|
3364
|
+
if (check) {
|
|
3365
|
+
checked.set(key, option.value);
|
|
3366
|
+
} else {
|
|
3367
|
+
checked.delete(key);
|
|
1115
3368
|
}
|
|
1116
3369
|
}
|
|
1117
|
-
return
|
|
3370
|
+
return {
|
|
3371
|
+
...draft,
|
|
3372
|
+
mode: "values",
|
|
3373
|
+
checked,
|
|
3374
|
+
includeBlanks: selectAllCoversBlanks(draft) ? check : draft.includeBlanks
|
|
3375
|
+
};
|
|
1118
3376
|
}
|
|
1119
|
-
function
|
|
1120
|
-
|
|
1121
|
-
|
|
3377
|
+
function filterEditorReducer(draft, action) {
|
|
3378
|
+
switch (action.type) {
|
|
3379
|
+
case "search":
|
|
3380
|
+
return { ...draft, search: action.search };
|
|
3381
|
+
case "toggleValue": {
|
|
3382
|
+
const key = keyOf2(draft, action.value);
|
|
3383
|
+
const checked = new Map(draft.checked);
|
|
3384
|
+
if (checked.has(key)) {
|
|
3385
|
+
checked.delete(key);
|
|
3386
|
+
} else {
|
|
3387
|
+
checked.set(key, action.value);
|
|
3388
|
+
}
|
|
3389
|
+
return { ...draft, mode: "values", checked };
|
|
3390
|
+
}
|
|
3391
|
+
case "toggleAll":
|
|
3392
|
+
return toggleAll(draft);
|
|
3393
|
+
case "setIncludeBlanks":
|
|
3394
|
+
return { ...draft, mode: "values", includeBlanks: action.includeBlanks };
|
|
3395
|
+
case "setConditions":
|
|
3396
|
+
return {
|
|
3397
|
+
...draft,
|
|
3398
|
+
mode: "conditions",
|
|
3399
|
+
conditions: action.conditions,
|
|
3400
|
+
combinator: action.combinator ?? draft.combinator
|
|
3401
|
+
};
|
|
3402
|
+
case "clear":
|
|
3403
|
+
return {
|
|
3404
|
+
...draft,
|
|
3405
|
+
search: "",
|
|
3406
|
+
checked: everyOption(draft),
|
|
3407
|
+
includeBlanks: draft.blanks > 0,
|
|
3408
|
+
conditions: []
|
|
3409
|
+
};
|
|
1122
3410
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
3411
|
+
}
|
|
3412
|
+
function checksEverything(draft) {
|
|
3413
|
+
return draft.options.every(
|
|
3414
|
+
(option) => draft.checked.has(keyOf2(draft, option.value))
|
|
3415
|
+
) && (draft.includeBlanks || draft.blanks === 0);
|
|
3416
|
+
}
|
|
3417
|
+
function draftEntries(draft, columnId) {
|
|
3418
|
+
if (draft.mode === "conditions") {
|
|
3419
|
+
const conditions = draft.conditions.map((condition) => ({
|
|
3420
|
+
...condition,
|
|
3421
|
+
columnId
|
|
3422
|
+
}));
|
|
3423
|
+
return conditions.length > 1 ? [{ combinator: draft.combinator, entries: conditions }] : conditions;
|
|
3424
|
+
}
|
|
3425
|
+
if (checksEverything(draft)) {
|
|
3426
|
+
return [];
|
|
3427
|
+
}
|
|
3428
|
+
const picked = {
|
|
3429
|
+
columnId,
|
|
3430
|
+
operator: "in",
|
|
3431
|
+
value: [...draft.checked.values()]
|
|
3432
|
+
};
|
|
3433
|
+
return draft.includeBlanks ? [
|
|
3434
|
+
{
|
|
3435
|
+
combinator: "or",
|
|
3436
|
+
entries: [picked, { columnId, operator: "isEmpty" }]
|
|
1140
3437
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
3438
|
+
] : [picked];
|
|
3439
|
+
}
|
|
3440
|
+
function draftToFilter(draft, columnId, current) {
|
|
3441
|
+
const replacement = draftEntries(draft, columnId);
|
|
3442
|
+
const first = current.findIndex((entry) => touchesColumn(entry, columnId));
|
|
3443
|
+
if (first === -1) {
|
|
3444
|
+
return replacement.length === 0 ? current : [...current, ...replacement];
|
|
3445
|
+
}
|
|
3446
|
+
const kept = current.filter((entry) => !touchesColumn(entry, columnId));
|
|
3447
|
+
return [...kept.slice(0, first), ...replacement, ...kept.slice(first)];
|
|
1143
3448
|
}
|
|
1144
3449
|
|
|
1145
3450
|
// src/util/virtualization.ts
|
|
@@ -1190,26 +3495,46 @@ export {
|
|
|
1190
3495
|
alignmentForType,
|
|
1191
3496
|
applyColumnOrder,
|
|
1192
3497
|
applyColumnResize,
|
|
3498
|
+
applyColumnVisibility,
|
|
1193
3499
|
applySelectionIntent,
|
|
1194
3500
|
beginColumnResize,
|
|
1195
3501
|
buildKeyShortcuts,
|
|
3502
|
+
builtInCommands,
|
|
1196
3503
|
clampColumnWidth,
|
|
1197
3504
|
clampFocus,
|
|
1198
3505
|
clearAllFilters,
|
|
1199
3506
|
clearSelection,
|
|
1200
3507
|
collapseAllGroups,
|
|
3508
|
+
columnValueOptions,
|
|
1201
3509
|
compareValues,
|
|
1202
3510
|
computeAggregates,
|
|
3511
|
+
contextMenuEntries,
|
|
3512
|
+
createCommandContext,
|
|
3513
|
+
createGridController,
|
|
3514
|
+
createGridViewResolver,
|
|
3515
|
+
defaultContextMenu,
|
|
3516
|
+
defaultMessages,
|
|
3517
|
+
defaultToolbar,
|
|
1203
3518
|
defineColumnsFromRows,
|
|
3519
|
+
describeColumnFilter,
|
|
1204
3520
|
diffSelection,
|
|
3521
|
+
draftToFilter,
|
|
1205
3522
|
expandAllGroups,
|
|
3523
|
+
filterEditorReducer,
|
|
1206
3524
|
filterQueryFor,
|
|
1207
3525
|
filterRows,
|
|
1208
3526
|
fitColumnsToWidth,
|
|
3527
|
+
fitToolbarItems,
|
|
3528
|
+
formatAggregateValue,
|
|
3529
|
+
formatCellValue,
|
|
1209
3530
|
getColumnId,
|
|
1210
3531
|
groupRowId,
|
|
1211
3532
|
groupRows,
|
|
3533
|
+
hasContextMenu,
|
|
3534
|
+
initialFilterEditorDraft,
|
|
1212
3535
|
intentOf,
|
|
3536
|
+
isColumnVisible,
|
|
3537
|
+
isPinnedToolbarItem,
|
|
1213
3538
|
isSameCell,
|
|
1214
3539
|
matchesQuery,
|
|
1215
3540
|
moveColumnBefore,
|
|
@@ -1217,33 +3542,52 @@ export {
|
|
|
1217
3542
|
movesColumn,
|
|
1218
3543
|
movesGroupBy,
|
|
1219
3544
|
nextFocusForKey,
|
|
3545
|
+
nextMenuIndex,
|
|
1220
3546
|
paginateRows,
|
|
1221
3547
|
paginationWindow,
|
|
1222
3548
|
resolveCell,
|
|
3549
|
+
resolveColumnChooser,
|
|
1223
3550
|
resolveColumnConstraints,
|
|
3551
|
+
resolveColumnHeaderText,
|
|
1224
3552
|
resolveColumnLabel,
|
|
1225
3553
|
resolveColumnWidths,
|
|
1226
3554
|
resolveColumns,
|
|
3555
|
+
resolveCommands,
|
|
3556
|
+
resolveDataColumns,
|
|
1227
3557
|
resolveDropBefore,
|
|
3558
|
+
resolveGridView,
|
|
1228
3559
|
resolveKeyboardDropTarget,
|
|
3560
|
+
resolveMenu,
|
|
3561
|
+
resolveMessages,
|
|
1229
3562
|
resolveRowId,
|
|
1230
3563
|
resolveRows,
|
|
1231
3564
|
resolveShownRows,
|
|
3565
|
+
resolveToolbar,
|
|
1232
3566
|
revertColumnSize,
|
|
3567
|
+
runCommand,
|
|
3568
|
+
searchTextOf,
|
|
1233
3569
|
selectAll,
|
|
3570
|
+
selectAllState,
|
|
1234
3571
|
selectCell,
|
|
1235
3572
|
selectOnly,
|
|
1236
3573
|
selectRange,
|
|
3574
|
+
serializeGridState,
|
|
1237
3575
|
setColumnFilter,
|
|
3576
|
+
setColumnVisibility,
|
|
3577
|
+
setSearchText,
|
|
1238
3578
|
sizeColumnToContent,
|
|
1239
3579
|
sortDirectionFor,
|
|
1240
3580
|
sortPriorityFor,
|
|
1241
3581
|
sortRows,
|
|
3582
|
+
splitToolbarItems,
|
|
1242
3583
|
toggleCellSelection,
|
|
1243
3584
|
toggleColumnSort,
|
|
1244
3585
|
toggleGroupExpansion,
|
|
1245
3586
|
toggleSelection,
|
|
3587
|
+
toolbarEntries,
|
|
1246
3588
|
totalColumnWidth,
|
|
3589
|
+
typeaheadIndex,
|
|
3590
|
+
visibleFilterOptions,
|
|
1247
3591
|
visibleRange,
|
|
1248
3592
|
withGroupAggregates
|
|
1249
3593
|
};
|