@gridkitjs/core 0.0.0-dev-20261106222533
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +36 -0
- package/dist/index.d.ts +4229 -0
- package/dist/index.js +4757 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,4757 @@
|
|
|
1
|
+
// src/util/grid.ts
|
|
2
|
+
function isNested(value) {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
|
|
4
|
+
}
|
|
5
|
+
function getType(value) {
|
|
6
|
+
if (value instanceof Date) return "dateTime";
|
|
7
|
+
if (typeof value === "string") return "string";
|
|
8
|
+
if (typeof value === "number") return "number";
|
|
9
|
+
if (typeof value === "boolean") return "boolean";
|
|
10
|
+
if (typeof value === "bigint") return "number";
|
|
11
|
+
if (typeof value === "symbol") return "string";
|
|
12
|
+
return "string";
|
|
13
|
+
}
|
|
14
|
+
function alignmentForType(type) {
|
|
15
|
+
switch (type) {
|
|
16
|
+
case "number":
|
|
17
|
+
case "decimal":
|
|
18
|
+
case "currency":
|
|
19
|
+
case "percent":
|
|
20
|
+
return "right";
|
|
21
|
+
default:
|
|
22
|
+
return "left";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function getAlignment(value) {
|
|
26
|
+
return alignmentForType(getType(value));
|
|
27
|
+
}
|
|
28
|
+
function defineColumnsFromRows(rows) {
|
|
29
|
+
const columns = [];
|
|
30
|
+
const seen = /* @__PURE__ */ new Set();
|
|
31
|
+
function add(field, value) {
|
|
32
|
+
if (seen.has(field)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
seen.add(field);
|
|
36
|
+
columns.push({
|
|
37
|
+
field,
|
|
38
|
+
type: getType(value),
|
|
39
|
+
alignment: getAlignment(value)
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
for (const row of rows) {
|
|
43
|
+
if (!isNested(row)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
for (const [key, value] of Object.entries(row)) {
|
|
47
|
+
if (!isNested(value)) {
|
|
48
|
+
add(key, value);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
for (const [nestedKey, nestedValue] of Object.entries(value)) {
|
|
52
|
+
if (!isNested(nestedValue)) {
|
|
53
|
+
add(`${key}.${nestedKey}`, nestedValue);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return columns;
|
|
59
|
+
}
|
|
60
|
+
function getColumnId(column) {
|
|
61
|
+
return column.id ?? column.field;
|
|
62
|
+
}
|
|
63
|
+
function resolveRowId(row, index, getRowId) {
|
|
64
|
+
return getRowId?.(row, index) ?? String(index);
|
|
65
|
+
}
|
|
66
|
+
function labelFromField(field) {
|
|
67
|
+
return field.split(".").join(" ");
|
|
68
|
+
}
|
|
69
|
+
function resolveColumnLabel(column) {
|
|
70
|
+
const { headerTemplate } = column;
|
|
71
|
+
if (headerTemplate === void 0) {
|
|
72
|
+
return column.header ?? labelFromField(column.field);
|
|
73
|
+
}
|
|
74
|
+
return typeof headerTemplate === "function" ? headerTemplate() : headerTemplate;
|
|
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
|
+
}
|
|
83
|
+
function buildKeyShortcuts(capabilities) {
|
|
84
|
+
return [
|
|
85
|
+
capabilities.reorderable ? "Control+ArrowLeft Control+ArrowRight" : "",
|
|
86
|
+
// Alt+Enter is the keyboard equivalent of double-clicking the resize
|
|
87
|
+
// handle to size the column to its content — same gate as the nudge
|
|
88
|
+
// shortcuts, since both are resize actions.
|
|
89
|
+
capabilities.resizable ? "Alt+ArrowLeft Alt+ArrowRight Alt+Enter" : "",
|
|
90
|
+
capabilities.sortable ? "Alt+ArrowUp" : "",
|
|
91
|
+
// Down rather than reusing Up: it reads as "drill into groups", and Up is
|
|
92
|
+
// already sort's own mnemonic on the same header.
|
|
93
|
+
capabilities.groupable ? "Alt+ArrowDown" : "",
|
|
94
|
+
// Excel's own key for its filter dropdown is Alt+ArrowDown, which
|
|
95
|
+
// grouping already holds here; Shift sets the filter apart from it.
|
|
96
|
+
capabilities.filterable === true ? "Alt+Shift+ArrowDown" : "",
|
|
97
|
+
// The ContextMenu key needs no mention: it says what it does on its face.
|
|
98
|
+
capabilities.contextMenu === true ? "Shift+F10" : ""
|
|
99
|
+
].filter(Boolean).join(" ");
|
|
100
|
+
}
|
|
101
|
+
function accessDotted(obj, path) {
|
|
102
|
+
return path.split(".").reduce((value, key) => isNested(value) ? value[key] : void 0, obj);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/util/position.ts
|
|
106
|
+
function renumberRows(entries) {
|
|
107
|
+
return entries.map(
|
|
108
|
+
(entry, index) => entry.rowIndex === index && entry.datasetIndex === index ? entry : { ...entry, rowIndex: index, datasetIndex: index }
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/util/sorting.ts
|
|
113
|
+
function isEmpty(value) {
|
|
114
|
+
return value === null || value === void 0;
|
|
115
|
+
}
|
|
116
|
+
function toTime(value) {
|
|
117
|
+
return value instanceof Date ? value.getTime() : new Date(String(value)).getTime();
|
|
118
|
+
}
|
|
119
|
+
function compareValues(a, b, type) {
|
|
120
|
+
if (isEmpty(a)) {
|
|
121
|
+
return isEmpty(b) ? 0 : 1;
|
|
122
|
+
}
|
|
123
|
+
if (isEmpty(b)) {
|
|
124
|
+
return -1;
|
|
125
|
+
}
|
|
126
|
+
switch (type) {
|
|
127
|
+
case "number":
|
|
128
|
+
case "decimal":
|
|
129
|
+
case "currency":
|
|
130
|
+
case "percent":
|
|
131
|
+
return Number(a) - Number(b);
|
|
132
|
+
case "boolean":
|
|
133
|
+
return Number(Boolean(a)) - Number(Boolean(b));
|
|
134
|
+
case "date":
|
|
135
|
+
case "dateTime":
|
|
136
|
+
case "time":
|
|
137
|
+
return toTime(a) - toTime(b);
|
|
138
|
+
default:
|
|
139
|
+
return String(a).localeCompare(String(b));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function sortDirectionFor(sort, columnId) {
|
|
143
|
+
return sort.find((entry) => entry.columnId === columnId)?.direction ?? null;
|
|
144
|
+
}
|
|
145
|
+
function sortPriorityFor(sort, columnId) {
|
|
146
|
+
const index = sort.findIndex((entry) => entry.columnId === columnId);
|
|
147
|
+
return index === -1 ? null : index + 1;
|
|
148
|
+
}
|
|
149
|
+
function toggleColumnSort(sort, columnId, options) {
|
|
150
|
+
const index = sort.findIndex((entry) => entry.columnId === columnId);
|
|
151
|
+
const isSoleEntry = sort.length === 1 && index === 0;
|
|
152
|
+
if (!options.stack && !isSoleEntry) {
|
|
153
|
+
return [{ columnId, direction: "asc" }];
|
|
154
|
+
}
|
|
155
|
+
const current = index === -1 ? void 0 : sort[index]?.direction;
|
|
156
|
+
const next = current === void 0 ? "asc" : current === "asc" ? "desc" : null;
|
|
157
|
+
if (next === null) {
|
|
158
|
+
return sort.filter((entry) => entry.columnId !== columnId);
|
|
159
|
+
}
|
|
160
|
+
if (index === -1) {
|
|
161
|
+
return [...sort, { columnId, direction: next }];
|
|
162
|
+
}
|
|
163
|
+
return sort.map(
|
|
164
|
+
(entry, i) => i === index ? { columnId, direction: next } : entry
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
function sortRows(rows, sort, columns) {
|
|
168
|
+
if (sort.length === 0) {
|
|
169
|
+
return rows;
|
|
170
|
+
}
|
|
171
|
+
const byId = new Map(columns.map((entry) => [entry.id, entry.column]));
|
|
172
|
+
const active = sort.filter((entry) => byId.has(entry.columnId));
|
|
173
|
+
if (active.length === 0) {
|
|
174
|
+
return rows;
|
|
175
|
+
}
|
|
176
|
+
const sorted = [...rows].sort((a, b) => {
|
|
177
|
+
for (const { columnId, direction } of active) {
|
|
178
|
+
const column = byId.get(columnId);
|
|
179
|
+
if (column === void 0) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const left = accessDotted(a.row, column.field);
|
|
183
|
+
const right = accessDotted(b.row, column.field);
|
|
184
|
+
const leftEmpty = isEmpty(left);
|
|
185
|
+
const rightEmpty = isEmpty(right);
|
|
186
|
+
if (leftEmpty || rightEmpty) {
|
|
187
|
+
if (leftEmpty && rightEmpty) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
return leftEmpty ? 1 : -1;
|
|
191
|
+
}
|
|
192
|
+
const result = compareValues(left, right, column.type);
|
|
193
|
+
if (result !== 0) {
|
|
194
|
+
return direction === "asc" ? result : -result;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return 0;
|
|
198
|
+
});
|
|
199
|
+
return renumberRows(sorted);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/util/grouping.ts
|
|
203
|
+
function groupRowId(path) {
|
|
204
|
+
return JSON.stringify(path);
|
|
205
|
+
}
|
|
206
|
+
function bucketKey(value) {
|
|
207
|
+
if (value === null || value === void 0) {
|
|
208
|
+
return "\0empty";
|
|
209
|
+
}
|
|
210
|
+
if (value instanceof Date) {
|
|
211
|
+
return `\0date:${String(value.getTime())}`;
|
|
212
|
+
}
|
|
213
|
+
switch (typeof value) {
|
|
214
|
+
case "string":
|
|
215
|
+
case "number":
|
|
216
|
+
case "boolean":
|
|
217
|
+
case "bigint":
|
|
218
|
+
case "symbol":
|
|
219
|
+
return `\0${typeof value}:${String(value)}`;
|
|
220
|
+
case "function":
|
|
221
|
+
return "\0function";
|
|
222
|
+
default:
|
|
223
|
+
return `\0object:${JSON.stringify(value)}`;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function partitionByValue(rows, column, direction) {
|
|
227
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
228
|
+
for (const row of rows) {
|
|
229
|
+
const value = accessDotted(row.row, column.field);
|
|
230
|
+
const key = bucketKey(value);
|
|
231
|
+
const bucket = buckets.get(key);
|
|
232
|
+
if (bucket === void 0) {
|
|
233
|
+
buckets.set(key, { value, rows: [row] });
|
|
234
|
+
} else {
|
|
235
|
+
bucket.rows.push(row);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const groups = [...buckets.values()];
|
|
239
|
+
groups.sort((a, b) => {
|
|
240
|
+
const result = compareValues(a.value, b.value, column.type);
|
|
241
|
+
return direction === "asc" ? result : -result;
|
|
242
|
+
});
|
|
243
|
+
return groups;
|
|
244
|
+
}
|
|
245
|
+
function buildLevel(rows, levels, parentPath, expansion, byId, out) {
|
|
246
|
+
const level = levels[0];
|
|
247
|
+
if (level === void 0) {
|
|
248
|
+
out.push(...rows);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const column = byId.get(level.columnId);
|
|
252
|
+
if (column === void 0) {
|
|
253
|
+
out.push(...rows);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const rest = levels.slice(1);
|
|
257
|
+
const groups = partitionByValue(rows, column, level.direction ?? "asc");
|
|
258
|
+
for (const group of groups) {
|
|
259
|
+
const path = [...parentPath, group.value];
|
|
260
|
+
const groupId = groupRowId(path);
|
|
261
|
+
const expanded = !expansion.includes(groupId);
|
|
262
|
+
const header = {
|
|
263
|
+
kind: "group",
|
|
264
|
+
groupId,
|
|
265
|
+
level: parentPath.length,
|
|
266
|
+
columnId: level.columnId,
|
|
267
|
+
value: group.value,
|
|
268
|
+
path,
|
|
269
|
+
expanded,
|
|
270
|
+
count: group.rows.length,
|
|
271
|
+
// Both set by `groupRows` once it has the whole flattened array.
|
|
272
|
+
rowIndex: -1,
|
|
273
|
+
datasetIndex: -1,
|
|
274
|
+
// Set by `withGroupAggregates`, downstream of grouping — empty here
|
|
275
|
+
// since `groupRows` itself never knows about `AggregateState`.
|
|
276
|
+
aggregates: /* @__PURE__ */ new Map()
|
|
277
|
+
};
|
|
278
|
+
out.push(header);
|
|
279
|
+
if (expanded) {
|
|
280
|
+
buildLevel(group.rows, rest, path, expansion, byId, out);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function groupRows(rows, groupBy, expansion, columns) {
|
|
285
|
+
if (groupBy.length === 0) {
|
|
286
|
+
return rows;
|
|
287
|
+
}
|
|
288
|
+
const byId = new Map(columns.map((entry) => [entry.id, entry.column]));
|
|
289
|
+
const active = groupBy.filter((entry) => byId.has(entry.columnId));
|
|
290
|
+
if (active.length === 0) {
|
|
291
|
+
return rows;
|
|
292
|
+
}
|
|
293
|
+
const out = [];
|
|
294
|
+
buildLevel(rows, active, [], expansion, byId, out);
|
|
295
|
+
return renumberRows(out);
|
|
296
|
+
}
|
|
297
|
+
function toggleGroupExpansion(expansion, groupId) {
|
|
298
|
+
return expansion.includes(groupId) ? expansion.filter((id) => id !== groupId) : [...expansion, groupId];
|
|
299
|
+
}
|
|
300
|
+
function expandAllGroups(expansion) {
|
|
301
|
+
return expansion.length === 0 ? expansion : [];
|
|
302
|
+
}
|
|
303
|
+
function collapseAllGroups(rows) {
|
|
304
|
+
return rows.filter(
|
|
305
|
+
(entry) => "kind" in entry && entry.kind === "group"
|
|
306
|
+
).map((entry) => entry.groupId);
|
|
307
|
+
}
|
|
308
|
+
function moveGroupByBefore(groupBy, columnId, beforeColumnId) {
|
|
309
|
+
if (columnId === beforeColumnId) {
|
|
310
|
+
return groupBy;
|
|
311
|
+
}
|
|
312
|
+
const without = groupBy.filter((entry) => entry.columnId !== columnId);
|
|
313
|
+
const moved = groupBy.find((entry) => entry.columnId === columnId) ?? {
|
|
314
|
+
columnId
|
|
315
|
+
};
|
|
316
|
+
const target = beforeColumnId === null ? without.length : without.findIndex((entry) => entry.columnId === beforeColumnId);
|
|
317
|
+
if (target === -1) {
|
|
318
|
+
return groupBy;
|
|
319
|
+
}
|
|
320
|
+
const next = [...without];
|
|
321
|
+
next.splice(target, 0, moved);
|
|
322
|
+
return next.length === groupBy.length && next.every((entry, index) => entry === groupBy[index]) ? groupBy : next;
|
|
323
|
+
}
|
|
324
|
+
function movesGroupBy(groupBy, columnId, beforeColumnId) {
|
|
325
|
+
return moveGroupByBefore(groupBy, columnId, beforeColumnId) !== groupBy;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// src/util/memoize.ts
|
|
329
|
+
function memoizeLast(compute) {
|
|
330
|
+
let last;
|
|
331
|
+
return (...args) => {
|
|
332
|
+
const previous = last;
|
|
333
|
+
if (previous !== void 0 && args.every((arg, index) => Object.is(arg, previous.args[index]))) {
|
|
334
|
+
return previous.result;
|
|
335
|
+
}
|
|
336
|
+
const result = compute(...args);
|
|
337
|
+
last = { args, result };
|
|
338
|
+
return result;
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// src/util/ordering.ts
|
|
343
|
+
function applyColumnOrder(columns, order) {
|
|
344
|
+
if (order.length === 0) {
|
|
345
|
+
return columns;
|
|
346
|
+
}
|
|
347
|
+
const byId = new Map(columns.map((column) => [getColumnId(column), column]));
|
|
348
|
+
const listed = order.map((id) => byId.get(id)).filter((column) => column !== void 0);
|
|
349
|
+
const taken = new Set(listed.map((column) => getColumnId(column)));
|
|
350
|
+
return [
|
|
351
|
+
...listed,
|
|
352
|
+
...columns.filter((column) => !taken.has(getColumnId(column)))
|
|
353
|
+
];
|
|
354
|
+
}
|
|
355
|
+
function resolveDataColumns(columns, order) {
|
|
356
|
+
return applyColumnOrder(columns, order).map((column) => ({
|
|
357
|
+
id: getColumnId(column),
|
|
358
|
+
column
|
|
359
|
+
}));
|
|
360
|
+
}
|
|
361
|
+
function moveColumnBefore(orderedIds, movedId, beforeId) {
|
|
362
|
+
if (movedId === beforeId || !orderedIds.includes(movedId)) {
|
|
363
|
+
return orderedIds;
|
|
364
|
+
}
|
|
365
|
+
const without = orderedIds.filter((id) => id !== movedId);
|
|
366
|
+
const target = beforeId === null ? without.length : without.indexOf(beforeId);
|
|
367
|
+
if (target === -1) {
|
|
368
|
+
return orderedIds;
|
|
369
|
+
}
|
|
370
|
+
const next = [...without];
|
|
371
|
+
next.splice(target, 0, movedId);
|
|
372
|
+
return next.every((id, index) => id === orderedIds[index]) ? orderedIds : next;
|
|
373
|
+
}
|
|
374
|
+
function movesColumn(orderedIds, movedId, beforeId) {
|
|
375
|
+
return moveColumnBefore(orderedIds, movedId, beforeId) !== orderedIds;
|
|
376
|
+
}
|
|
377
|
+
function resolveDropBefore(orderedIds, targetId, side) {
|
|
378
|
+
if (side === "before") {
|
|
379
|
+
return targetId;
|
|
380
|
+
}
|
|
381
|
+
const index = orderedIds.indexOf(targetId);
|
|
382
|
+
return index === -1 ? null : orderedIds[index + 1] ?? null;
|
|
383
|
+
}
|
|
384
|
+
function resolveKeyboardDropTarget(orderedIds, columnId, direction) {
|
|
385
|
+
const index = orderedIds.indexOf(columnId);
|
|
386
|
+
if (index === -1) {
|
|
387
|
+
return void 0;
|
|
388
|
+
}
|
|
389
|
+
const targetIndex = direction === -1 ? index - 1 : index + 2;
|
|
390
|
+
if (targetIndex < 0) {
|
|
391
|
+
return void 0;
|
|
392
|
+
}
|
|
393
|
+
return orderedIds[targetIndex] ?? null;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/util/paging.ts
|
|
397
|
+
function unitsOf(rows) {
|
|
398
|
+
const units = [];
|
|
399
|
+
let start = 0;
|
|
400
|
+
let openedByGroup = false;
|
|
401
|
+
for (let index = 0; index < rows.length; index++) {
|
|
402
|
+
const entry = rows[index];
|
|
403
|
+
if (entry === void 0) {
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
const isTopLevelHeader = "kind" in entry && entry.kind === "group" && entry.level === 0;
|
|
407
|
+
const isBoundary = index !== start && (isTopLevelHeader || !openedByGroup);
|
|
408
|
+
if (isBoundary) {
|
|
409
|
+
units.push({ start, end: index });
|
|
410
|
+
start = index;
|
|
411
|
+
openedByGroup = isTopLevelHeader;
|
|
412
|
+
} else if (index === start) {
|
|
413
|
+
openedByGroup = isTopLevelHeader;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
if (start < rows.length) {
|
|
417
|
+
units.push({ start, end: rows.length });
|
|
418
|
+
}
|
|
419
|
+
return units;
|
|
420
|
+
}
|
|
421
|
+
function paginateRows(rows, pagination) {
|
|
422
|
+
const pageSize = Math.max(1, pagination.pageSize);
|
|
423
|
+
const units = unitsOf(rows);
|
|
424
|
+
const pageCount = Math.ceil(units.length / pageSize);
|
|
425
|
+
if (pageCount === 0) {
|
|
426
|
+
return { rows: [], pageCount: 0, pageIndex: 0 };
|
|
427
|
+
}
|
|
428
|
+
const pageIndex = Math.min(Math.max(pagination.pageIndex, 0), pageCount - 1);
|
|
429
|
+
const firstUnit = units[pageIndex * pageSize];
|
|
430
|
+
const lastUnit = units[Math.min(pageIndex * pageSize + pageSize, units.length) - 1];
|
|
431
|
+
if (firstUnit === void 0 || lastUnit === void 0) {
|
|
432
|
+
return { rows: [], pageCount, pageIndex };
|
|
433
|
+
}
|
|
434
|
+
const page = rows.slice(firstUnit.start, lastUnit.end);
|
|
435
|
+
const renumbered = page.map(
|
|
436
|
+
(entry, index) => entry.rowIndex === index ? entry : { ...entry, rowIndex: index }
|
|
437
|
+
);
|
|
438
|
+
return { rows: renumbered, pageCount, pageIndex };
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// src/util/report.ts
|
|
442
|
+
function reportError(message) {
|
|
443
|
+
console.error(`GridKit: ${message}`);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// src/util/filterOperators.ts
|
|
447
|
+
var matchNothing = () => false;
|
|
448
|
+
function kindOf(type) {
|
|
449
|
+
switch (type) {
|
|
450
|
+
case "number":
|
|
451
|
+
case "decimal":
|
|
452
|
+
case "currency":
|
|
453
|
+
case "percent":
|
|
454
|
+
return "number";
|
|
455
|
+
case "boolean":
|
|
456
|
+
return "boolean";
|
|
457
|
+
case "date":
|
|
458
|
+
case "dateTime":
|
|
459
|
+
case "time":
|
|
460
|
+
return "date";
|
|
461
|
+
default:
|
|
462
|
+
return "text";
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
function isDateType(type) {
|
|
466
|
+
return kindOf(type) === "date";
|
|
467
|
+
}
|
|
468
|
+
function isEmptyCell(value) {
|
|
469
|
+
return value === null || value === void 0 || value === "";
|
|
470
|
+
}
|
|
471
|
+
function toDate(value) {
|
|
472
|
+
const date = value instanceof Date ? value : typeof value === "string" ? new Date(value) : void 0;
|
|
473
|
+
return date === void 0 || Number.isNaN(date.getTime()) ? void 0 : date;
|
|
474
|
+
}
|
|
475
|
+
function comparable(value, kind) {
|
|
476
|
+
switch (kind) {
|
|
477
|
+
case "number":
|
|
478
|
+
return typeof value === "number" && !Number.isNaN(value) ? value : void 0;
|
|
479
|
+
case "boolean":
|
|
480
|
+
return typeof value === "boolean" ? value : void 0;
|
|
481
|
+
case "date":
|
|
482
|
+
return toDate(value);
|
|
483
|
+
case "text":
|
|
484
|
+
return String(value).toLowerCase();
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
function isOperandOfKind(operand, kind) {
|
|
488
|
+
if (kind !== "text") {
|
|
489
|
+
return comparable(operand, kind) !== void 0;
|
|
490
|
+
}
|
|
491
|
+
return typeof operand === "string" || typeof operand === "number" || typeof operand === "boolean";
|
|
492
|
+
}
|
|
493
|
+
function compileComparison(operand, type) {
|
|
494
|
+
const kind = kindOf(type);
|
|
495
|
+
if (!isOperandOfKind(operand, kind)) {
|
|
496
|
+
return void 0;
|
|
497
|
+
}
|
|
498
|
+
const target = comparable(operand, kind);
|
|
499
|
+
return (value) => {
|
|
500
|
+
if (isEmptyCell(value)) {
|
|
501
|
+
return void 0;
|
|
502
|
+
}
|
|
503
|
+
const cell = comparable(value, kind);
|
|
504
|
+
return cell === void 0 ? void 0 : compareValues(cell, target, type);
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
function isScalarList(operand) {
|
|
508
|
+
return Array.isArray(operand);
|
|
509
|
+
}
|
|
510
|
+
function compileOrdering(operator, operand, type) {
|
|
511
|
+
const compare = compileComparison(operand, type);
|
|
512
|
+
if (compare === void 0) {
|
|
513
|
+
return matchNothing;
|
|
514
|
+
}
|
|
515
|
+
const where = (holds) => (value) => {
|
|
516
|
+
const result = compare(value);
|
|
517
|
+
return result !== void 0 && holds(result);
|
|
518
|
+
};
|
|
519
|
+
switch (operator) {
|
|
520
|
+
case "eq":
|
|
521
|
+
return where((result) => result === 0);
|
|
522
|
+
case "ne": {
|
|
523
|
+
const equals = where((result) => result === 0);
|
|
524
|
+
return (value) => !equals(value);
|
|
525
|
+
}
|
|
526
|
+
case "gt":
|
|
527
|
+
return where((result) => result > 0);
|
|
528
|
+
case "gte":
|
|
529
|
+
return where((result) => result >= 0);
|
|
530
|
+
case "lt":
|
|
531
|
+
return where((result) => result < 0);
|
|
532
|
+
case "lte":
|
|
533
|
+
return where((result) => result <= 0);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
function compileBetween(operand, type) {
|
|
537
|
+
if (!isScalarList(operand) || operand.length !== 2) {
|
|
538
|
+
return matchNothing;
|
|
539
|
+
}
|
|
540
|
+
const [low, high] = operand;
|
|
541
|
+
const fromLow = compileComparison(low, type);
|
|
542
|
+
const fromHigh = compileComparison(high, type);
|
|
543
|
+
if (fromLow === void 0 || fromHigh === void 0) {
|
|
544
|
+
return matchNothing;
|
|
545
|
+
}
|
|
546
|
+
return (value) => {
|
|
547
|
+
const aboveLow = fromLow(value);
|
|
548
|
+
const belowHigh = fromHigh(value);
|
|
549
|
+
return aboveLow !== void 0 && belowHigh !== void 0 && aboveLow >= 0 && belowHigh <= 0;
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
function filterValueKey(value, type) {
|
|
553
|
+
return bucketKey(isDateType(type) ? toDate(value) ?? value : value);
|
|
554
|
+
}
|
|
555
|
+
function compileIn(operand, type) {
|
|
556
|
+
if (!isScalarList(operand)) {
|
|
557
|
+
return matchNothing;
|
|
558
|
+
}
|
|
559
|
+
const keys = new Set(
|
|
560
|
+
operand.filter((scalar) => !isEmptyCell(scalar)).map((scalar) => filterValueKey(scalar, type))
|
|
561
|
+
);
|
|
562
|
+
return (value) => !isEmptyCell(value) && keys.has(filterValueKey(value, type));
|
|
563
|
+
}
|
|
564
|
+
function compileText(operator, operand) {
|
|
565
|
+
if (!isOperandOfKind(operand, "text")) {
|
|
566
|
+
return matchNothing;
|
|
567
|
+
}
|
|
568
|
+
const text = String(operand).toLowerCase();
|
|
569
|
+
const read = (value) => String(value).toLowerCase();
|
|
570
|
+
switch (operator) {
|
|
571
|
+
case "contains":
|
|
572
|
+
return (value) => !isEmptyCell(value) && read(value).includes(text);
|
|
573
|
+
case "notContains":
|
|
574
|
+
return (value) => isEmptyCell(value) || !read(value).includes(text);
|
|
575
|
+
case "startsWith":
|
|
576
|
+
return (value) => !isEmptyCell(value) && read(value).startsWith(text);
|
|
577
|
+
case "endsWith":
|
|
578
|
+
return (value) => !isEmptyCell(value) && read(value).endsWith(text);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
function compileOperatorEntry(entry, type) {
|
|
582
|
+
const { operator, value } = entry;
|
|
583
|
+
switch (operator) {
|
|
584
|
+
case "isEmpty":
|
|
585
|
+
return isEmptyCell;
|
|
586
|
+
case "isNotEmpty":
|
|
587
|
+
return (cell) => !isEmptyCell(cell);
|
|
588
|
+
case "contains":
|
|
589
|
+
case "notContains":
|
|
590
|
+
case "startsWith":
|
|
591
|
+
case "endsWith":
|
|
592
|
+
return compileText(operator, value);
|
|
593
|
+
case "between":
|
|
594
|
+
return compileBetween(value, type);
|
|
595
|
+
case "in":
|
|
596
|
+
return compileIn(value, type);
|
|
597
|
+
case "eq":
|
|
598
|
+
case "ne":
|
|
599
|
+
case "gt":
|
|
600
|
+
case "gte":
|
|
601
|
+
case "lt":
|
|
602
|
+
case "lte":
|
|
603
|
+
return compileOrdering(operator, value, type);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// src/util/serialize.ts
|
|
608
|
+
function serializeScalar(scalar) {
|
|
609
|
+
return scalar instanceof Date ? scalar.toISOString() : scalar;
|
|
610
|
+
}
|
|
611
|
+
function serializeOperand(operand) {
|
|
612
|
+
return isScalarList(operand) ? operand.map(serializeScalar) : serializeScalar(operand);
|
|
613
|
+
}
|
|
614
|
+
function serializeEntry(entry) {
|
|
615
|
+
if ("combinator" in entry) {
|
|
616
|
+
const entries = serializeFilter(entry.entries);
|
|
617
|
+
return entries.length === 0 && entry.entries.length > 0 ? void 0 : { combinator: entry.combinator, entries };
|
|
618
|
+
}
|
|
619
|
+
if ("predicate" in entry) {
|
|
620
|
+
reportError(
|
|
621
|
+
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.`
|
|
622
|
+
);
|
|
623
|
+
return void 0;
|
|
624
|
+
}
|
|
625
|
+
if ("operator" in entry) {
|
|
626
|
+
const { value, ...rest } = entry;
|
|
627
|
+
return value === void 0 ? rest : { ...rest, value: serializeOperand(value) };
|
|
628
|
+
}
|
|
629
|
+
if ("value" in entry) {
|
|
630
|
+
const { value, ...rest } = entry;
|
|
631
|
+
return {
|
|
632
|
+
...rest,
|
|
633
|
+
value: value instanceof Date ? value.toISOString() : value
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
return entry;
|
|
637
|
+
}
|
|
638
|
+
function serializeFilter(filter) {
|
|
639
|
+
return filter.map((entry) => serializeEntry(entry)).filter((entry) => entry !== void 0);
|
|
640
|
+
}
|
|
641
|
+
function serializeGridState(state) {
|
|
642
|
+
return { ...state, filter: serializeFilter(state.filter) };
|
|
643
|
+
}
|
|
644
|
+
function reviveScalar(scalar, type) {
|
|
645
|
+
return isDateType(type) && typeof scalar === "string" ? toDate(scalar) ?? scalar : scalar;
|
|
646
|
+
}
|
|
647
|
+
function reviveOperand(operand, type) {
|
|
648
|
+
return isScalarList(operand) ? operand.map((scalar) => reviveScalar(scalar, type)) : reviveScalar(operand, type);
|
|
649
|
+
}
|
|
650
|
+
function reviveEntry(entry, typeOf) {
|
|
651
|
+
if ("combinator" in entry) {
|
|
652
|
+
return {
|
|
653
|
+
combinator: entry.combinator,
|
|
654
|
+
entries: reviveEntries(entry.entries, typeOf)
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
if ("operator" in entry) {
|
|
658
|
+
const { value, ...rest } = entry;
|
|
659
|
+
return value === void 0 ? rest : { ...rest, value: reviveOperand(value, typeOf(entry.columnId)) };
|
|
660
|
+
}
|
|
661
|
+
if ("value" in entry) {
|
|
662
|
+
const { value, ...rest } = entry;
|
|
663
|
+
return {
|
|
664
|
+
...rest,
|
|
665
|
+
value: typeof value === "string" ? new Date(value) : value
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
return entry;
|
|
669
|
+
}
|
|
670
|
+
function reviveEntries(filter, typeOf) {
|
|
671
|
+
return filter.map((entry) => reviveEntry(entry, typeOf));
|
|
672
|
+
}
|
|
673
|
+
function reviveFilter(filter, columns) {
|
|
674
|
+
const types = new Map(columns.map((entry) => [entry.id, entry.column.type]));
|
|
675
|
+
return reviveEntries(filter, (columnId) => types.get(columnId));
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// src/util/sizing.ts
|
|
679
|
+
var DEFAULT_COLUMN_SIZES = {
|
|
680
|
+
width: 150,
|
|
681
|
+
minWidth: 40,
|
|
682
|
+
maxWidth: Number.POSITIVE_INFINITY
|
|
683
|
+
};
|
|
684
|
+
var KEYBOARD_STEP = 10;
|
|
685
|
+
function withDefaults(defaults) {
|
|
686
|
+
return { ...DEFAULT_COLUMN_SIZES, ...defaults };
|
|
687
|
+
}
|
|
688
|
+
function resolveColumnConstraints(column, defaults) {
|
|
689
|
+
const resolved = withDefaults(defaults);
|
|
690
|
+
return {
|
|
691
|
+
minWidth: column.minWidth ?? resolved.minWidth,
|
|
692
|
+
maxWidth: column.maxWidth ?? resolved.maxWidth
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
function clampColumnWidth(width, constraints) {
|
|
696
|
+
return Math.min(Math.max(width, constraints.minWidth), constraints.maxWidth);
|
|
697
|
+
}
|
|
698
|
+
function resolveColumnWidths(columns, sizing, options) {
|
|
699
|
+
const resolved = withDefaults(options?.sizes);
|
|
700
|
+
return columns.map((column) => {
|
|
701
|
+
const id = getColumnId(column);
|
|
702
|
+
const stored = sizing[id];
|
|
703
|
+
return {
|
|
704
|
+
column,
|
|
705
|
+
id,
|
|
706
|
+
sized: stored !== void 0,
|
|
707
|
+
width: clampColumnWidth(
|
|
708
|
+
stored ?? column.width ?? resolved.width,
|
|
709
|
+
resolveColumnConstraints(column, resolved)
|
|
710
|
+
),
|
|
711
|
+
label: resolveColumnLabel(column),
|
|
712
|
+
resizable: column.resizable ?? options?.resizable ?? false,
|
|
713
|
+
reorderable: column.reorderable ?? options?.reorderable ?? false,
|
|
714
|
+
groupByDraggable: column.groupByDraggable ?? options?.groupByDraggable ?? false,
|
|
715
|
+
alignment: column.alignment ?? alignmentForType(column.type ?? "string")
|
|
716
|
+
};
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
function totalColumnWidth(resolved) {
|
|
720
|
+
return resolved.reduce((total, entry) => total + entry.width, 0);
|
|
721
|
+
}
|
|
722
|
+
function beginColumnResize(column, startWidth, startPosition, defaults) {
|
|
723
|
+
return {
|
|
724
|
+
columnId: getColumnId(column),
|
|
725
|
+
startWidth,
|
|
726
|
+
startPosition,
|
|
727
|
+
constraints: resolveColumnConstraints(column, defaults)
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
function applyColumnResize(session, position) {
|
|
731
|
+
return clampColumnWidth(
|
|
732
|
+
session.startWidth + (position - session.startPosition),
|
|
733
|
+
session.constraints
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
function reachedBound(width, item) {
|
|
737
|
+
return item.bound > item.width ? width >= item.bound : width <= item.bound;
|
|
738
|
+
}
|
|
739
|
+
function redistribute(entries, remaining) {
|
|
740
|
+
for (; ; ) {
|
|
741
|
+
const movable = entries.filter(
|
|
742
|
+
(item) => item.adjustable && !reachedBound(item.width, item)
|
|
743
|
+
);
|
|
744
|
+
const left = remaining(entries.reduce((t, i) => t + i.width, 0));
|
|
745
|
+
if (left <= 0 || movable.length === 0) {
|
|
746
|
+
break;
|
|
747
|
+
}
|
|
748
|
+
const base = movable.reduce((total, item) => total + item.width, 0);
|
|
749
|
+
let clamped = false;
|
|
750
|
+
for (const item of movable) {
|
|
751
|
+
const share = base === 0 ? left / movable.length : left * item.width / base;
|
|
752
|
+
const next = item.width + (item.bound > item.width ? share : -share);
|
|
753
|
+
if (reachedBound(next, item)) {
|
|
754
|
+
item.width = item.bound;
|
|
755
|
+
clamped = true;
|
|
756
|
+
} else {
|
|
757
|
+
item.width = next;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
if (!clamped) {
|
|
761
|
+
break;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
function fitColumnsToWidth(resolved, availableWidth, defaults) {
|
|
766
|
+
const target = Math.floor(availableWidth);
|
|
767
|
+
const total = totalColumnWidth(resolved);
|
|
768
|
+
if (resolved.length === 0 || total === target) {
|
|
769
|
+
return resolved;
|
|
770
|
+
}
|
|
771
|
+
const growing = total < target;
|
|
772
|
+
const entries = resolved.map((entry) => {
|
|
773
|
+
const constraints = resolveColumnConstraints(entry.column, defaults);
|
|
774
|
+
return {
|
|
775
|
+
entry,
|
|
776
|
+
width: entry.width,
|
|
777
|
+
bound: growing ? constraints.maxWidth : constraints.minWidth,
|
|
778
|
+
adjustable: !entry.sized
|
|
779
|
+
};
|
|
780
|
+
});
|
|
781
|
+
if (!growing) {
|
|
782
|
+
const floor = entries.reduce(
|
|
783
|
+
(sum, item) => sum + (item.adjustable ? item.bound : item.width),
|
|
784
|
+
0
|
|
785
|
+
);
|
|
786
|
+
if (floor > target) {
|
|
787
|
+
return resolved;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
redistribute(entries, (sum) => growing ? target - sum : sum - target);
|
|
791
|
+
const rounded = entries.map(
|
|
792
|
+
(item) => growing ? Math.floor(item.width) : Math.ceil(item.width)
|
|
793
|
+
);
|
|
794
|
+
let remainder = target - rounded.reduce((total2, width) => total2 + width, 0);
|
|
795
|
+
return entries.map((item, index) => {
|
|
796
|
+
let width = rounded[index] ?? item.width;
|
|
797
|
+
const hasRoom = growing ? width < item.bound : width > item.bound;
|
|
798
|
+
if (remainder !== 0 && item.adjustable && hasRoom) {
|
|
799
|
+
width += growing ? 1 : -1;
|
|
800
|
+
remainder += growing ? -1 : 1;
|
|
801
|
+
}
|
|
802
|
+
return { ...item.entry, width };
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
function sizeColumnToContent(measuredWidth, constraints, padding = 2) {
|
|
806
|
+
return clampColumnWidth(Math.ceil(measuredWidth + padding), constraints);
|
|
807
|
+
}
|
|
808
|
+
function revertColumnSize(current, base, columnId) {
|
|
809
|
+
const next = Object.fromEntries(
|
|
810
|
+
Object.entries(current).filter(([id]) => id !== columnId)
|
|
811
|
+
);
|
|
812
|
+
if (columnId in base) {
|
|
813
|
+
next[columnId] = base[columnId];
|
|
814
|
+
}
|
|
815
|
+
return next;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// src/util/aggregation.ts
|
|
819
|
+
function isEmpty2(value) {
|
|
820
|
+
return value === null || value === void 0;
|
|
821
|
+
}
|
|
822
|
+
function toNumber(value) {
|
|
823
|
+
return Number(value);
|
|
824
|
+
}
|
|
825
|
+
function distinctKey(value) {
|
|
826
|
+
if (isEmpty2(value)) {
|
|
827
|
+
return "\0empty";
|
|
828
|
+
}
|
|
829
|
+
if (value instanceof Date) {
|
|
830
|
+
return `\0date:${String(value.getTime())}`;
|
|
831
|
+
}
|
|
832
|
+
switch (typeof value) {
|
|
833
|
+
case "string":
|
|
834
|
+
case "number":
|
|
835
|
+
case "boolean":
|
|
836
|
+
case "bigint":
|
|
837
|
+
case "symbol":
|
|
838
|
+
return `\0${typeof value}:${String(value)}`;
|
|
839
|
+
case "function":
|
|
840
|
+
return "\0function";
|
|
841
|
+
default:
|
|
842
|
+
return `\0object:${JSON.stringify(value)}`;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
function reduceBuiltIn(rows, fn, column) {
|
|
846
|
+
if (fn === "count") {
|
|
847
|
+
return rows.length;
|
|
848
|
+
}
|
|
849
|
+
const field = column?.column.field;
|
|
850
|
+
const values = field === void 0 ? [] : rows.map((row) => accessDotted(row, field));
|
|
851
|
+
switch (fn) {
|
|
852
|
+
case "sum": {
|
|
853
|
+
let total = 0;
|
|
854
|
+
for (const value of values) {
|
|
855
|
+
if (isEmpty2(value)) continue;
|
|
856
|
+
total += toNumber(value);
|
|
857
|
+
}
|
|
858
|
+
return total;
|
|
859
|
+
}
|
|
860
|
+
case "avg": {
|
|
861
|
+
const present = values.filter((value) => !isEmpty2(value));
|
|
862
|
+
if (present.length === 0) return void 0;
|
|
863
|
+
const total = present.reduce(
|
|
864
|
+
(acc, value) => acc + toNumber(value),
|
|
865
|
+
0
|
|
866
|
+
);
|
|
867
|
+
return total / present.length;
|
|
868
|
+
}
|
|
869
|
+
case "min":
|
|
870
|
+
case "max": {
|
|
871
|
+
const type = column?.column.type;
|
|
872
|
+
let result;
|
|
873
|
+
for (const value of values) {
|
|
874
|
+
if (isEmpty2(value)) continue;
|
|
875
|
+
if (result === void 0) {
|
|
876
|
+
result = value;
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
const comparison = compareValues(value, result, type);
|
|
880
|
+
if (fn === "min" ? comparison < 0 : comparison > 0) {
|
|
881
|
+
result = value;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
return result;
|
|
885
|
+
}
|
|
886
|
+
case "countDistinct": {
|
|
887
|
+
const seen = /* @__PURE__ */ new Set();
|
|
888
|
+
for (const value of values) {
|
|
889
|
+
if (isEmpty2(value)) continue;
|
|
890
|
+
seen.add(distinctKey(value));
|
|
891
|
+
}
|
|
892
|
+
return seen.size;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
function keyOf(spec) {
|
|
897
|
+
return spec.id ?? spec.columnId;
|
|
898
|
+
}
|
|
899
|
+
function computeAggregates(rows, specs, columns) {
|
|
900
|
+
const results = /* @__PURE__ */ new Map();
|
|
901
|
+
if (specs.length === 0) {
|
|
902
|
+
return results;
|
|
903
|
+
}
|
|
904
|
+
const byId = new Map(columns.map((entry) => [entry.id, entry]));
|
|
905
|
+
for (const spec of specs) {
|
|
906
|
+
const value = typeof spec.fn === "function" ? spec.fn(rows) : reduceBuiltIn(rows, spec.fn, byId.get(spec.columnId));
|
|
907
|
+
results.set(keyOf(spec), value);
|
|
908
|
+
}
|
|
909
|
+
return results;
|
|
910
|
+
}
|
|
911
|
+
function leafDescendantsOf(flatRows, path, groupBy, byId) {
|
|
912
|
+
const keys = path.map((value) => bucketKey(value));
|
|
913
|
+
return flatRows.filter(
|
|
914
|
+
(row) => keys.every((key, level) => {
|
|
915
|
+
const columnId = groupBy[level]?.columnId;
|
|
916
|
+
const column = columnId === void 0 ? void 0 : byId.get(columnId);
|
|
917
|
+
if (column === void 0) return false;
|
|
918
|
+
return bucketKey(accessDotted(row.row, column.field)) === key;
|
|
919
|
+
})
|
|
920
|
+
).map((row) => row.row);
|
|
921
|
+
}
|
|
922
|
+
function appendAggregatedSpan(displayRows, start, levelFloor, flatRows, groupBy, byId, specs, columns, display, out) {
|
|
923
|
+
let index = start;
|
|
924
|
+
while (index < displayRows.length) {
|
|
925
|
+
const entry = displayRows[index];
|
|
926
|
+
if (entry === void 0) {
|
|
927
|
+
index++;
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
if (levelFloor !== null && "kind" in entry && entry.kind === "group" && entry.level <= levelFloor) {
|
|
931
|
+
break;
|
|
932
|
+
}
|
|
933
|
+
index++;
|
|
934
|
+
if (!("kind" in entry) || entry.kind !== "group") {
|
|
935
|
+
out.push(entry);
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
const leaves = leafDescendantsOf(flatRows, entry.path, groupBy, byId);
|
|
939
|
+
const aggregates = computeAggregates(leaves, specs, columns);
|
|
940
|
+
const withAggregates = { ...entry, aggregates };
|
|
941
|
+
out.push(withAggregates);
|
|
942
|
+
index = appendAggregatedSpan(
|
|
943
|
+
displayRows,
|
|
944
|
+
index,
|
|
945
|
+
entry.level,
|
|
946
|
+
flatRows,
|
|
947
|
+
groupBy,
|
|
948
|
+
byId,
|
|
949
|
+
specs,
|
|
950
|
+
columns,
|
|
951
|
+
display,
|
|
952
|
+
out
|
|
953
|
+
);
|
|
954
|
+
if (display === "row") {
|
|
955
|
+
const summary = {
|
|
956
|
+
kind: "group-summary",
|
|
957
|
+
groupId: entry.groupId,
|
|
958
|
+
level: entry.level,
|
|
959
|
+
rowIndex: -1,
|
|
960
|
+
datasetIndex: -1,
|
|
961
|
+
aggregates
|
|
962
|
+
};
|
|
963
|
+
out.push(summary);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
return index;
|
|
967
|
+
}
|
|
968
|
+
function withGroupAggregates(displayRows, flatRows, groupBy, specs, columns, display = "inline") {
|
|
969
|
+
if (specs.length === 0) {
|
|
970
|
+
return displayRows;
|
|
971
|
+
}
|
|
972
|
+
const byId = new Map(columns.map((entry) => [entry.id, entry.column]));
|
|
973
|
+
const out = [];
|
|
974
|
+
appendAggregatedSpan(
|
|
975
|
+
displayRows,
|
|
976
|
+
0,
|
|
977
|
+
null,
|
|
978
|
+
flatRows,
|
|
979
|
+
groupBy,
|
|
980
|
+
byId,
|
|
981
|
+
specs,
|
|
982
|
+
columns,
|
|
983
|
+
display,
|
|
984
|
+
out
|
|
985
|
+
);
|
|
986
|
+
return renumberRows(out);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
// src/util/filtering.ts
|
|
990
|
+
function parseFilterPattern(query) {
|
|
991
|
+
const start = query.startsWith("%");
|
|
992
|
+
const end = query.endsWith("%");
|
|
993
|
+
let text = query;
|
|
994
|
+
if (start) {
|
|
995
|
+
text = text.slice(1);
|
|
996
|
+
}
|
|
997
|
+
if (end) {
|
|
998
|
+
text = text.slice(0, -1);
|
|
999
|
+
}
|
|
1000
|
+
text = text.toLowerCase();
|
|
1001
|
+
if (start && end) {
|
|
1002
|
+
return { mode: "contains", text };
|
|
1003
|
+
}
|
|
1004
|
+
if (start) {
|
|
1005
|
+
return { mode: "endsWith", text };
|
|
1006
|
+
}
|
|
1007
|
+
if (end) {
|
|
1008
|
+
return { mode: "startsWith", text };
|
|
1009
|
+
}
|
|
1010
|
+
return { mode: "exact", text };
|
|
1011
|
+
}
|
|
1012
|
+
function isEmptyValue(value) {
|
|
1013
|
+
return value === null || value === void 0;
|
|
1014
|
+
}
|
|
1015
|
+
function compileFilterQuery(query) {
|
|
1016
|
+
const { mode, text } = parseFilterPattern(query);
|
|
1017
|
+
return (value) => {
|
|
1018
|
+
const candidate = (isEmptyValue(value) ? "" : String(value)).toLowerCase();
|
|
1019
|
+
switch (mode) {
|
|
1020
|
+
case "exact":
|
|
1021
|
+
return candidate === text;
|
|
1022
|
+
case "contains":
|
|
1023
|
+
return candidate.includes(text);
|
|
1024
|
+
case "startsWith":
|
|
1025
|
+
return candidate.startsWith(text);
|
|
1026
|
+
case "endsWith":
|
|
1027
|
+
return candidate.endsWith(text);
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
function matchesTypedValue(value, target, type) {
|
|
1032
|
+
if (typeof target === "number") {
|
|
1033
|
+
if (type !== "number" && type !== "decimal" && type !== "currency" && type !== "percent") {
|
|
1034
|
+
return false;
|
|
1035
|
+
}
|
|
1036
|
+
return typeof value === "number" && value === target;
|
|
1037
|
+
}
|
|
1038
|
+
if (typeof target === "boolean") {
|
|
1039
|
+
if (type !== "boolean") {
|
|
1040
|
+
return false;
|
|
1041
|
+
}
|
|
1042
|
+
return typeof value === "boolean" && value === target;
|
|
1043
|
+
}
|
|
1044
|
+
if (type !== "date" && type !== "dateTime" && type !== "time") {
|
|
1045
|
+
return false;
|
|
1046
|
+
}
|
|
1047
|
+
return value instanceof Date && value.getTime() === target.getTime();
|
|
1048
|
+
}
|
|
1049
|
+
function isEntryUsable(entry, byId) {
|
|
1050
|
+
if ("combinator" in entry) {
|
|
1051
|
+
return true;
|
|
1052
|
+
}
|
|
1053
|
+
return entry.columnId === void 0 || byId.has(entry.columnId);
|
|
1054
|
+
}
|
|
1055
|
+
function compileFilterEntry(entry, byId, columns) {
|
|
1056
|
+
if ("combinator" in entry) {
|
|
1057
|
+
const tests = entry.entries.filter((nested) => isEntryUsable(nested, byId)).map((nested) => compileFilterEntry(nested, byId, columns));
|
|
1058
|
+
return entry.combinator === "and" ? (row) => tests.every((test2) => test2(row)) : (row) => tests.some((test2) => test2(row));
|
|
1059
|
+
}
|
|
1060
|
+
if ("predicate" in entry) {
|
|
1061
|
+
const { columnId: columnId2, predicate } = entry;
|
|
1062
|
+
const column = columnId2 === void 0 ? void 0 : byId.get(columnId2);
|
|
1063
|
+
return (row) => predicate(
|
|
1064
|
+
column === void 0 ? void 0 : accessDotted(row, column.field),
|
|
1065
|
+
row
|
|
1066
|
+
);
|
|
1067
|
+
}
|
|
1068
|
+
if ("operator" in entry) {
|
|
1069
|
+
const column = byId.get(entry.columnId);
|
|
1070
|
+
if (column === void 0) {
|
|
1071
|
+
return () => false;
|
|
1072
|
+
}
|
|
1073
|
+
const test2 = compileOperatorEntry(entry, column.type);
|
|
1074
|
+
return (row) => test2(accessDotted(row, column.field));
|
|
1075
|
+
}
|
|
1076
|
+
if ("value" in entry) {
|
|
1077
|
+
const { columnId: columnId2, value } = entry;
|
|
1078
|
+
if (columnId2 !== void 0) {
|
|
1079
|
+
const column = byId.get(columnId2);
|
|
1080
|
+
if (column === void 0) {
|
|
1081
|
+
return () => false;
|
|
1082
|
+
}
|
|
1083
|
+
return (row) => matchesTypedValue(accessDotted(row, column.field), value, column.type);
|
|
1084
|
+
}
|
|
1085
|
+
return (row) => columns.some(
|
|
1086
|
+
(entry2) => matchesTypedValue(
|
|
1087
|
+
accessDotted(row, entry2.column.field),
|
|
1088
|
+
value,
|
|
1089
|
+
entry2.column.type
|
|
1090
|
+
)
|
|
1091
|
+
);
|
|
1092
|
+
}
|
|
1093
|
+
const { columnId, query } = entry;
|
|
1094
|
+
const test = compileFilterQuery(query);
|
|
1095
|
+
if (columnId !== void 0) {
|
|
1096
|
+
const column = byId.get(columnId);
|
|
1097
|
+
if (column === void 0) {
|
|
1098
|
+
return () => false;
|
|
1099
|
+
}
|
|
1100
|
+
return (row) => test(accessDotted(row, column.field));
|
|
1101
|
+
}
|
|
1102
|
+
return (row) => columns.some((entry2) => test(accessDotted(row, entry2.column.field)));
|
|
1103
|
+
}
|
|
1104
|
+
function compileFilter(filter, columns) {
|
|
1105
|
+
const byId = new Map(columns.map((entry) => [entry.id, entry.column]));
|
|
1106
|
+
const compiled = filter.filter((entry) => isEntryUsable(entry, byId)).map((entry) => compileFilterEntry(entry, byId, columns));
|
|
1107
|
+
return compiled.length === 0 ? void 0 : (row) => compiled.every((test) => test(row));
|
|
1108
|
+
}
|
|
1109
|
+
function matchesQuery(value, query) {
|
|
1110
|
+
return compileFilterQuery(query)(value);
|
|
1111
|
+
}
|
|
1112
|
+
function filterQueryFor(filter, columnId) {
|
|
1113
|
+
const entry = filter.find(
|
|
1114
|
+
(candidate) => "query" in candidate && candidate.columnId === columnId
|
|
1115
|
+
);
|
|
1116
|
+
return entry?.query ?? null;
|
|
1117
|
+
}
|
|
1118
|
+
function setColumnFilter(filter, columnId, query) {
|
|
1119
|
+
const index = filter.findIndex(
|
|
1120
|
+
(entry) => !("combinator" in entry) && entry.columnId === columnId
|
|
1121
|
+
);
|
|
1122
|
+
if (query === "") {
|
|
1123
|
+
return index === -1 ? filter : filter.filter((_entry, i) => i !== index);
|
|
1124
|
+
}
|
|
1125
|
+
const next = columnId === void 0 ? { query } : { columnId, query };
|
|
1126
|
+
if (index === -1) {
|
|
1127
|
+
return [...filter, next];
|
|
1128
|
+
}
|
|
1129
|
+
const existing = filter[index];
|
|
1130
|
+
if (existing !== void 0 && "query" in existing && existing.query === query) {
|
|
1131
|
+
return filter;
|
|
1132
|
+
}
|
|
1133
|
+
return filter.map((entry, i) => i === index ? next : entry);
|
|
1134
|
+
}
|
|
1135
|
+
function clearAllFilters(filter) {
|
|
1136
|
+
return filter.length === 0 ? filter : [];
|
|
1137
|
+
}
|
|
1138
|
+
function filterRows(rows, filter, columns) {
|
|
1139
|
+
if (filter.length === 0) {
|
|
1140
|
+
return rows;
|
|
1141
|
+
}
|
|
1142
|
+
const test = compileFilter(filter, columns);
|
|
1143
|
+
if (test === void 0) {
|
|
1144
|
+
return rows;
|
|
1145
|
+
}
|
|
1146
|
+
const filtered = rows.filter((row) => test(row.row));
|
|
1147
|
+
return renumberRows(filtered);
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
// src/util/rows.ts
|
|
1151
|
+
function resolveShownRows(rows, filter, sort, columns) {
|
|
1152
|
+
return sortRows(filterRows(rows, filter, columns), sort, columns);
|
|
1153
|
+
}
|
|
1154
|
+
function resolveRows(rowsById, ids) {
|
|
1155
|
+
return ids.map((id) => rowsById.get(id)).filter((row) => row !== void 0);
|
|
1156
|
+
}
|
|
1157
|
+
function resolveColumns(columns, ids) {
|
|
1158
|
+
return ids.map((id) => {
|
|
1159
|
+
const columnIndex = columns.findIndex((entry) => entry.id === id);
|
|
1160
|
+
const column = columns[columnIndex];
|
|
1161
|
+
return column === void 0 ? void 0 : { columnId: id, column, columnIndex };
|
|
1162
|
+
}).filter((column) => column !== void 0);
|
|
1163
|
+
}
|
|
1164
|
+
function resolveCell(rowsById, columns, cell) {
|
|
1165
|
+
if (cell === null) {
|
|
1166
|
+
return null;
|
|
1167
|
+
}
|
|
1168
|
+
const entry = rowsById.get(cell.rowId);
|
|
1169
|
+
const columnIndex = columns.findIndex(
|
|
1170
|
+
(candidate) => candidate.id === cell.columnId
|
|
1171
|
+
);
|
|
1172
|
+
const column = columns[columnIndex];
|
|
1173
|
+
if (entry === void 0 || column === void 0) {
|
|
1174
|
+
return null;
|
|
1175
|
+
}
|
|
1176
|
+
return {
|
|
1177
|
+
rowId: cell.rowId,
|
|
1178
|
+
columnId: cell.columnId,
|
|
1179
|
+
row: entry.row,
|
|
1180
|
+
column,
|
|
1181
|
+
rowIndex: entry.rowIndex,
|
|
1182
|
+
columnIndex,
|
|
1183
|
+
value: accessDotted(entry.row, column.column.field)
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
// src/util/visibility.ts
|
|
1188
|
+
function isColumnVisible(column, visibility) {
|
|
1189
|
+
return visibility[getColumnId(column)] ?? column.hidden !== true;
|
|
1190
|
+
}
|
|
1191
|
+
function applyColumnVisibility(columns, visibility) {
|
|
1192
|
+
const visible = columns.filter(
|
|
1193
|
+
(entry) => isColumnVisible(entry.column, visibility)
|
|
1194
|
+
);
|
|
1195
|
+
return visible.length === columns.length ? columns : visible;
|
|
1196
|
+
}
|
|
1197
|
+
function setColumnVisibility(columns, visibility, columnIds, visible) {
|
|
1198
|
+
const requested = new Set(columnIds);
|
|
1199
|
+
const changed = columns.filter(
|
|
1200
|
+
(entry) => requested.has(entry.id) && isColumnVisible(entry.column, visibility) !== visible
|
|
1201
|
+
);
|
|
1202
|
+
if (changed.length === 0) {
|
|
1203
|
+
return visibility;
|
|
1204
|
+
}
|
|
1205
|
+
return {
|
|
1206
|
+
...visibility,
|
|
1207
|
+
...Object.fromEntries(changed.map((entry) => [entry.id, visible]))
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
// src/util/view.ts
|
|
1212
|
+
var NO_AGGREGATES = [];
|
|
1213
|
+
function identifyRows(rows, getRowId) {
|
|
1214
|
+
return rows.map((row, rowIndex) => ({
|
|
1215
|
+
rowId: resolveRowId(row, rowIndex, getRowId),
|
|
1216
|
+
row,
|
|
1217
|
+
rowIndex,
|
|
1218
|
+
datasetIndex: rowIndex
|
|
1219
|
+
}));
|
|
1220
|
+
}
|
|
1221
|
+
function sizeColumns(visibleColumns, sizing, sizes, resizable, reorderable, groupByDraggable, resizeMode, viewportWidth) {
|
|
1222
|
+
const widths = resolveColumnWidths(
|
|
1223
|
+
visibleColumns.map((entry) => entry.column),
|
|
1224
|
+
sizing,
|
|
1225
|
+
{ sizes, resizable, reorderable, groupByDraggable }
|
|
1226
|
+
);
|
|
1227
|
+
return resizeMode === "fit" && viewportWidth !== null ? fitColumnsToWidth(widths, viewportWidth, sizes) : widths;
|
|
1228
|
+
}
|
|
1229
|
+
function totalAggregates(rows, aggregates, dataColumns) {
|
|
1230
|
+
return computeAggregates(
|
|
1231
|
+
rows.map((entry) => entry.row),
|
|
1232
|
+
aggregates,
|
|
1233
|
+
dataColumns
|
|
1234
|
+
);
|
|
1235
|
+
}
|
|
1236
|
+
function paginate(displayRows, paginated, pagination) {
|
|
1237
|
+
return paginated ? paginateRows(displayRows, pagination) : { rows: displayRows, pageCount: 1, pageIndex: 0 };
|
|
1238
|
+
}
|
|
1239
|
+
function narrowToDataRows(pageRows, rows, reshaped) {
|
|
1240
|
+
return reshaped ? pageRows.filter((entry) => !("kind" in entry)) : rows;
|
|
1241
|
+
}
|
|
1242
|
+
function createGridViewResolver() {
|
|
1243
|
+
const identify = memoizeLast(identifyRows);
|
|
1244
|
+
const orderColumns = memoizeLast(resolveDataColumns);
|
|
1245
|
+
const showColumns = memoizeLast(applyColumnVisibility);
|
|
1246
|
+
const size = memoizeLast(sizeColumns);
|
|
1247
|
+
const filterAndSort = memoizeLast(resolveShownRows);
|
|
1248
|
+
const group = memoizeLast(groupRows);
|
|
1249
|
+
const aggregate = memoizeLast(withGroupAggregates);
|
|
1250
|
+
const total = memoizeLast(totalAggregates);
|
|
1251
|
+
const page = memoizeLast(paginate);
|
|
1252
|
+
const narrow = memoizeLast(narrowToDataRows);
|
|
1253
|
+
const assemble = memoizeLast(
|
|
1254
|
+
(dataColumns, columns, rows, displayRows, pageRows, displayDataRows, pageCount, pageIndex, grandTotal) => ({
|
|
1255
|
+
dataColumns,
|
|
1256
|
+
columns,
|
|
1257
|
+
rows,
|
|
1258
|
+
displayRows,
|
|
1259
|
+
pageRows,
|
|
1260
|
+
displayDataRows,
|
|
1261
|
+
pageCount,
|
|
1262
|
+
pageIndex,
|
|
1263
|
+
grandTotal
|
|
1264
|
+
})
|
|
1265
|
+
);
|
|
1266
|
+
return ({ rows: data, getRowId, columns: definitions, state, options }) => {
|
|
1267
|
+
const paginated = options.paginated ?? false;
|
|
1268
|
+
const aggregates = options.aggregates ?? NO_AGGREGATES;
|
|
1269
|
+
const dataColumns = orderColumns(definitions, state.columnOrder);
|
|
1270
|
+
const columns = size(
|
|
1271
|
+
showColumns(dataColumns, state.columnVisibility),
|
|
1272
|
+
state.columnSizing,
|
|
1273
|
+
options.columnSizeDefaults,
|
|
1274
|
+
options.resizableColumns,
|
|
1275
|
+
options.reorderableColumns,
|
|
1276
|
+
options.groupByDraggableColumns,
|
|
1277
|
+
options.resizeMode ?? "fit",
|
|
1278
|
+
options.viewportWidth ?? null
|
|
1279
|
+
);
|
|
1280
|
+
const rows = filterAndSort(
|
|
1281
|
+
identify(data, getRowId),
|
|
1282
|
+
state.filter,
|
|
1283
|
+
state.columnSort,
|
|
1284
|
+
dataColumns
|
|
1285
|
+
);
|
|
1286
|
+
const displayRows = aggregate(
|
|
1287
|
+
group(rows, state.groupBy, state.groupExpansion, dataColumns),
|
|
1288
|
+
rows,
|
|
1289
|
+
state.groupBy,
|
|
1290
|
+
aggregates,
|
|
1291
|
+
dataColumns,
|
|
1292
|
+
options.groupAggregateDisplay ?? "inline"
|
|
1293
|
+
);
|
|
1294
|
+
const {
|
|
1295
|
+
rows: pageRows,
|
|
1296
|
+
pageCount,
|
|
1297
|
+
pageIndex
|
|
1298
|
+
} = page(displayRows, paginated, state.pagination);
|
|
1299
|
+
return assemble(
|
|
1300
|
+
dataColumns,
|
|
1301
|
+
columns,
|
|
1302
|
+
rows,
|
|
1303
|
+
displayRows,
|
|
1304
|
+
pageRows,
|
|
1305
|
+
narrow(pageRows, rows, state.groupBy.length > 0 || paginated),
|
|
1306
|
+
pageCount,
|
|
1307
|
+
pageIndex,
|
|
1308
|
+
total(rows, aggregates, dataColumns)
|
|
1309
|
+
);
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
function resolveGridView(input) {
|
|
1313
|
+
return createGridViewResolver()(input);
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// src/controller/changes.ts
|
|
1317
|
+
function changedEntryColumnId(previous, next) {
|
|
1318
|
+
const length = Math.max(previous.length, next.length);
|
|
1319
|
+
for (let index = 0; index < length; index++) {
|
|
1320
|
+
const before = previous[index];
|
|
1321
|
+
const after = next[index];
|
|
1322
|
+
if (before?.columnId !== after?.columnId || before?.direction !== after?.direction) {
|
|
1323
|
+
return (after ?? before)?.columnId;
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
return void 0;
|
|
1327
|
+
}
|
|
1328
|
+
function changedId(previous, next) {
|
|
1329
|
+
const length = Math.max(previous.length, next.length);
|
|
1330
|
+
for (let index = 0; index < length; index++) {
|
|
1331
|
+
if (previous[index] !== next[index]) {
|
|
1332
|
+
return next[index] ?? previous[index];
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return void 0;
|
|
1336
|
+
}
|
|
1337
|
+
function changedKeys(previous, next) {
|
|
1338
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(next), ...Object.keys(previous)]);
|
|
1339
|
+
return [...keys].filter((key) => previous[key] !== next[key]);
|
|
1340
|
+
}
|
|
1341
|
+
function sameItems(previous, next) {
|
|
1342
|
+
return previous.length === next.length && previous.every((item, index) => item === next[index]);
|
|
1343
|
+
}
|
|
1344
|
+
function samePagination(previous, next) {
|
|
1345
|
+
return previous.pageIndex === next.pageIndex && previous.pageSize === next.pageSize;
|
|
1346
|
+
}
|
|
1347
|
+
function sameValue(previous, next) {
|
|
1348
|
+
return previous instanceof Date && next instanceof Date ? previous.getTime() === next.getTime() : previous === next;
|
|
1349
|
+
}
|
|
1350
|
+
function sameOperand(previous, next) {
|
|
1351
|
+
if (isScalarList(previous) || isScalarList(next)) {
|
|
1352
|
+
return isScalarList(previous) && isScalarList(next) && previous.length === next.length && previous.every((scalar, index) => sameValue(scalar, next[index]));
|
|
1353
|
+
}
|
|
1354
|
+
return sameValue(previous, next);
|
|
1355
|
+
}
|
|
1356
|
+
function sameEntry(previous, next) {
|
|
1357
|
+
if ("combinator" in previous || "combinator" in next) {
|
|
1358
|
+
return "combinator" in previous && "combinator" in next && previous.combinator === next.combinator && sameFilter(previous.entries, next.entries);
|
|
1359
|
+
}
|
|
1360
|
+
if (previous.columnId !== next.columnId) {
|
|
1361
|
+
return false;
|
|
1362
|
+
}
|
|
1363
|
+
if ("predicate" in previous || "predicate" in next) {
|
|
1364
|
+
return "predicate" in previous && "predicate" in next && previous.predicate === next.predicate;
|
|
1365
|
+
}
|
|
1366
|
+
if ("operator" in previous || "operator" in next) {
|
|
1367
|
+
return "operator" in previous && "operator" in next && previous.operator === next.operator && sameOperand(previous.value, next.value);
|
|
1368
|
+
}
|
|
1369
|
+
if ("value" in previous || "value" in next) {
|
|
1370
|
+
return "value" in previous && "value" in next && sameValue(previous.value, next.value);
|
|
1371
|
+
}
|
|
1372
|
+
return previous.query === next.query;
|
|
1373
|
+
}
|
|
1374
|
+
function sameFilter(previous, next) {
|
|
1375
|
+
return previous.length === next.length && previous.every((entry, index) => {
|
|
1376
|
+
const other = next[index];
|
|
1377
|
+
return other !== void 0 && sameEntry(entry, other);
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
// src/util/selection.ts
|
|
1382
|
+
var NO_CHANGE = { added: [], removed: [] };
|
|
1383
|
+
function sameSelection(a, b) {
|
|
1384
|
+
return a.length === b.length && a.every((id, index) => id === b[index]);
|
|
1385
|
+
}
|
|
1386
|
+
function toggleSelection(selection, id, mode) {
|
|
1387
|
+
if (mode === false) {
|
|
1388
|
+
return selection;
|
|
1389
|
+
}
|
|
1390
|
+
if (selection.includes(id)) {
|
|
1391
|
+
return selection.filter((selectedId) => selectedId !== id);
|
|
1392
|
+
}
|
|
1393
|
+
return mode === "single" ? [id] : [...selection, id];
|
|
1394
|
+
}
|
|
1395
|
+
function selectOnly(selection, id, mode) {
|
|
1396
|
+
if (mode === false) {
|
|
1397
|
+
return selection;
|
|
1398
|
+
}
|
|
1399
|
+
return selection.length === 1 && selection[0] === id ? selection : [id];
|
|
1400
|
+
}
|
|
1401
|
+
function selectRange(selection, orderedIds, anchorId, focusId, mode) {
|
|
1402
|
+
if (mode !== "multiple") {
|
|
1403
|
+
return selectOnly(selection, focusId, mode);
|
|
1404
|
+
}
|
|
1405
|
+
const anchor = orderedIds.indexOf(anchorId);
|
|
1406
|
+
const focus = orderedIds.indexOf(focusId);
|
|
1407
|
+
if (anchor === -1 || focus === -1) {
|
|
1408
|
+
return selection;
|
|
1409
|
+
}
|
|
1410
|
+
const next = orderedIds.slice(
|
|
1411
|
+
Math.min(anchor, focus),
|
|
1412
|
+
Math.max(anchor, focus) + 1
|
|
1413
|
+
);
|
|
1414
|
+
return sameSelection(selection, next) ? selection : next;
|
|
1415
|
+
}
|
|
1416
|
+
function selectAll(selection, orderedIds, mode) {
|
|
1417
|
+
if (mode !== "multiple") {
|
|
1418
|
+
return selection;
|
|
1419
|
+
}
|
|
1420
|
+
return sameSelection(selection, orderedIds) ? selection : orderedIds;
|
|
1421
|
+
}
|
|
1422
|
+
function clearSelection(selection) {
|
|
1423
|
+
return selection.length === 0 ? selection : [];
|
|
1424
|
+
}
|
|
1425
|
+
function diffSelection(previous, next) {
|
|
1426
|
+
if (previous === next) {
|
|
1427
|
+
return NO_CHANGE;
|
|
1428
|
+
}
|
|
1429
|
+
const before = new Set(previous);
|
|
1430
|
+
const after = new Set(next);
|
|
1431
|
+
return {
|
|
1432
|
+
added: next.filter((id) => !before.has(id)),
|
|
1433
|
+
removed: previous.filter((id) => !after.has(id))
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
function isSameCell(a, b) {
|
|
1437
|
+
if (a === null || b === null) {
|
|
1438
|
+
return a === b;
|
|
1439
|
+
}
|
|
1440
|
+
return a.rowId === b.rowId && a.columnId === b.columnId;
|
|
1441
|
+
}
|
|
1442
|
+
function selectCell(selection, cell, mode) {
|
|
1443
|
+
if (mode === false || isSameCell(selection, cell)) {
|
|
1444
|
+
return selection;
|
|
1445
|
+
}
|
|
1446
|
+
return cell;
|
|
1447
|
+
}
|
|
1448
|
+
function toggleCellSelection(selection, cell, mode) {
|
|
1449
|
+
if (mode === false) {
|
|
1450
|
+
return selection;
|
|
1451
|
+
}
|
|
1452
|
+
return isSameCell(selection, cell) ? null : cell;
|
|
1453
|
+
}
|
|
1454
|
+
function intentOf(event, options) {
|
|
1455
|
+
if (event.shiftKey) {
|
|
1456
|
+
return "range";
|
|
1457
|
+
}
|
|
1458
|
+
if (event.ctrlKey || event.metaKey) {
|
|
1459
|
+
return "toggle";
|
|
1460
|
+
}
|
|
1461
|
+
return options?.toggleOnClick === true ? "toggle" : "replace";
|
|
1462
|
+
}
|
|
1463
|
+
function applySelectionIntent(selection, orderedIds, anchorId, id, intent, mode) {
|
|
1464
|
+
switch (intent) {
|
|
1465
|
+
case "toggle":
|
|
1466
|
+
return toggleSelection(selection, id, mode);
|
|
1467
|
+
// A range with no anchor yet spans from the id to itself, which is the
|
|
1468
|
+
// plain click a first Shift-click may as well be.
|
|
1469
|
+
case "range":
|
|
1470
|
+
return selectRange(selection, orderedIds, anchorId ?? id, id, mode);
|
|
1471
|
+
default:
|
|
1472
|
+
return selectOnly(selection, id, mode);
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// src/controller/selectionEvents.ts
|
|
1477
|
+
function reportRowSelection(previous, next, rowsById, callbacks) {
|
|
1478
|
+
const diff = diffSelection(previous, next);
|
|
1479
|
+
const added = resolveRows(rowsById, diff.added);
|
|
1480
|
+
const removed = resolveRows(rowsById, diff.removed);
|
|
1481
|
+
for (const row of added) {
|
|
1482
|
+
callbacks.onRowSelect?.({ row, selection: next });
|
|
1483
|
+
}
|
|
1484
|
+
if (added.length > 0) {
|
|
1485
|
+
callbacks.onRowsSelect?.({ rows: added, selection: next });
|
|
1486
|
+
}
|
|
1487
|
+
for (const row of removed) {
|
|
1488
|
+
callbacks.onRowDeselect?.({ row, selection: next });
|
|
1489
|
+
}
|
|
1490
|
+
if (removed.length > 0) {
|
|
1491
|
+
callbacks.onRowsDeselect?.({ rows: removed, selection: next });
|
|
1492
|
+
}
|
|
1493
|
+
callbacks.onRowSelectionChange?.({
|
|
1494
|
+
added,
|
|
1495
|
+
removed,
|
|
1496
|
+
selected: resolveRows(rowsById, next),
|
|
1497
|
+
selection: next
|
|
1498
|
+
});
|
|
1499
|
+
return diff;
|
|
1500
|
+
}
|
|
1501
|
+
function reportColumnSelection(previous, next, columns, callbacks) {
|
|
1502
|
+
const diff = diffSelection(previous, next);
|
|
1503
|
+
const added = resolveColumns(columns, diff.added);
|
|
1504
|
+
const removed = resolveColumns(columns, diff.removed);
|
|
1505
|
+
for (const column of added) {
|
|
1506
|
+
callbacks.onColumnSelect?.({ column, selection: next });
|
|
1507
|
+
}
|
|
1508
|
+
if (added.length > 0) {
|
|
1509
|
+
callbacks.onColumnsSelect?.({ columns: added, selection: next });
|
|
1510
|
+
}
|
|
1511
|
+
for (const column of removed) {
|
|
1512
|
+
callbacks.onColumnDeselect?.({ column, selection: next });
|
|
1513
|
+
}
|
|
1514
|
+
if (removed.length > 0) {
|
|
1515
|
+
callbacks.onColumnsDeselect?.({ columns: removed, selection: next });
|
|
1516
|
+
}
|
|
1517
|
+
callbacks.onColumnSelectionChange?.({
|
|
1518
|
+
added,
|
|
1519
|
+
removed,
|
|
1520
|
+
selected: resolveColumns(columns, next),
|
|
1521
|
+
selection: next
|
|
1522
|
+
});
|
|
1523
|
+
return diff;
|
|
1524
|
+
}
|
|
1525
|
+
function reportCellSelection(previous, next, rowsById, columns, callbacks) {
|
|
1526
|
+
const selected = resolveCell(rowsById, columns, next);
|
|
1527
|
+
const deselected = resolveCell(rowsById, columns, previous);
|
|
1528
|
+
if (deselected !== null) {
|
|
1529
|
+
callbacks.onCellDeselect?.({ cell: deselected, selection: next });
|
|
1530
|
+
}
|
|
1531
|
+
if (selected !== null) {
|
|
1532
|
+
callbacks.onCellSelect?.({ cell: selected, selection: next });
|
|
1533
|
+
}
|
|
1534
|
+
callbacks.onCellSelectionChange?.({
|
|
1535
|
+
selected,
|
|
1536
|
+
deselected,
|
|
1537
|
+
selection: next
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
// src/controller/selectionActions.ts
|
|
1542
|
+
function indexRows(rows) {
|
|
1543
|
+
return new Map(rows.map((entry) => [entry.rowId, entry]));
|
|
1544
|
+
}
|
|
1545
|
+
function distinct(ids) {
|
|
1546
|
+
return [...new Set(ids)];
|
|
1547
|
+
}
|
|
1548
|
+
function createSelectionActions(grid) {
|
|
1549
|
+
let rowAnchor = null;
|
|
1550
|
+
let columnAnchor = null;
|
|
1551
|
+
const rowsById = memoizeLast(indexRows);
|
|
1552
|
+
function modes() {
|
|
1553
|
+
const selectable = grid.getOptions().selectable;
|
|
1554
|
+
return {
|
|
1555
|
+
rows: selectable?.rows ?? false,
|
|
1556
|
+
columns: selectable?.columns ?? false,
|
|
1557
|
+
cells: selectable?.cells ?? false
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
function rowIds() {
|
|
1561
|
+
return grid.getView().displayDataRows.map((entry) => entry.rowId);
|
|
1562
|
+
}
|
|
1563
|
+
function columnIds() {
|
|
1564
|
+
return grid.getView().columns.map((entry) => entry.id);
|
|
1565
|
+
}
|
|
1566
|
+
function commitRows(next) {
|
|
1567
|
+
const previous = grid.getState().rowSelection;
|
|
1568
|
+
grid.commit("rowSelection", next, () => {
|
|
1569
|
+
const options = grid.getOptions();
|
|
1570
|
+
const diff = reportRowSelection(
|
|
1571
|
+
previous,
|
|
1572
|
+
next,
|
|
1573
|
+
rowsById(grid.getView().displayDataRows),
|
|
1574
|
+
options
|
|
1575
|
+
);
|
|
1576
|
+
options.onSelectionDiff?.({ target: "rows", ...diff, selection: next });
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
function commitColumns(next) {
|
|
1580
|
+
const previous = grid.getState().columnSelection;
|
|
1581
|
+
grid.commit("columnSelection", next, () => {
|
|
1582
|
+
const options = grid.getOptions();
|
|
1583
|
+
const diff = reportColumnSelection(
|
|
1584
|
+
previous,
|
|
1585
|
+
next,
|
|
1586
|
+
grid.getView().columns,
|
|
1587
|
+
options
|
|
1588
|
+
);
|
|
1589
|
+
options.onSelectionDiff?.({
|
|
1590
|
+
target: "columns",
|
|
1591
|
+
...diff,
|
|
1592
|
+
selection: next
|
|
1593
|
+
});
|
|
1594
|
+
});
|
|
1595
|
+
}
|
|
1596
|
+
function commitCell(next) {
|
|
1597
|
+
const previous = grid.getState().cellSelection;
|
|
1598
|
+
grid.commit("cellSelection", next, () => {
|
|
1599
|
+
const view = grid.getView();
|
|
1600
|
+
reportCellSelection(
|
|
1601
|
+
previous,
|
|
1602
|
+
next,
|
|
1603
|
+
rowsById(view.displayDataRows),
|
|
1604
|
+
view.columns,
|
|
1605
|
+
grid.getOptions()
|
|
1606
|
+
);
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
return {
|
|
1610
|
+
setRowSelection(selection) {
|
|
1611
|
+
if (!sameItems(grid.getState().rowSelection, selection)) {
|
|
1612
|
+
commitRows(selection);
|
|
1613
|
+
}
|
|
1614
|
+
},
|
|
1615
|
+
selectRows(ids, intent = "replace") {
|
|
1616
|
+
const mode = modes().rows;
|
|
1617
|
+
const current = grid.getState().rowSelection;
|
|
1618
|
+
if (mode === false || ids.length === 0) {
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
const next = intent === "toggle" ? ids.reduce(
|
|
1622
|
+
(selection, id) => toggleSelection(selection, id, mode),
|
|
1623
|
+
current
|
|
1624
|
+
) : mode === "single" ? ids.slice(-1) : distinct(ids);
|
|
1625
|
+
rowAnchor = ids.at(-1) ?? null;
|
|
1626
|
+
if (!sameItems(current, next)) {
|
|
1627
|
+
commitRows(next);
|
|
1628
|
+
}
|
|
1629
|
+
},
|
|
1630
|
+
selectRow(rowId, intent) {
|
|
1631
|
+
const next = applySelectionIntent(
|
|
1632
|
+
grid.getState().rowSelection,
|
|
1633
|
+
rowIds(),
|
|
1634
|
+
rowAnchor,
|
|
1635
|
+
rowId,
|
|
1636
|
+
intent,
|
|
1637
|
+
modes().rows
|
|
1638
|
+
);
|
|
1639
|
+
if (intent !== "range") {
|
|
1640
|
+
rowAnchor = rowId;
|
|
1641
|
+
}
|
|
1642
|
+
commitRows(next);
|
|
1643
|
+
},
|
|
1644
|
+
selectAllRows() {
|
|
1645
|
+
commitRows(
|
|
1646
|
+
selectAll(grid.getState().rowSelection, rowIds(), modes().rows)
|
|
1647
|
+
);
|
|
1648
|
+
},
|
|
1649
|
+
clearSelection() {
|
|
1650
|
+
commitRows(clearSelection(grid.getState().rowSelection));
|
|
1651
|
+
commitColumns(clearSelection(grid.getState().columnSelection));
|
|
1652
|
+
commitCell(null);
|
|
1653
|
+
},
|
|
1654
|
+
setColumnSelection(selection) {
|
|
1655
|
+
if (!sameItems(grid.getState().columnSelection, selection)) {
|
|
1656
|
+
commitColumns(selection);
|
|
1657
|
+
}
|
|
1658
|
+
},
|
|
1659
|
+
selectColumn(columnId, intent) {
|
|
1660
|
+
const next = applySelectionIntent(
|
|
1661
|
+
grid.getState().columnSelection,
|
|
1662
|
+
columnIds(),
|
|
1663
|
+
columnAnchor,
|
|
1664
|
+
columnId,
|
|
1665
|
+
intent,
|
|
1666
|
+
modes().columns
|
|
1667
|
+
);
|
|
1668
|
+
if (intent !== "range") {
|
|
1669
|
+
columnAnchor = columnId;
|
|
1670
|
+
}
|
|
1671
|
+
commitColumns(next);
|
|
1672
|
+
},
|
|
1673
|
+
setCellSelection(selection) {
|
|
1674
|
+
if (!isSameCell(grid.getState().cellSelection, selection)) {
|
|
1675
|
+
commitCell(selection);
|
|
1676
|
+
}
|
|
1677
|
+
},
|
|
1678
|
+
selectCell(cell, intent) {
|
|
1679
|
+
const current = grid.getState().cellSelection;
|
|
1680
|
+
const mode = modes().cells;
|
|
1681
|
+
commitCell(
|
|
1682
|
+
intent === "toggle" ? toggleCellSelection(current, cell, mode) : selectCell(current, cell, mode)
|
|
1683
|
+
);
|
|
1684
|
+
}
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
// src/controller/createGridController.ts
|
|
1689
|
+
var DEFAULT_PAGE_SIZE = 25;
|
|
1690
|
+
var NO_ROWS = [];
|
|
1691
|
+
var NO_COLUMNS = [];
|
|
1692
|
+
var SLICE_KEYS = [
|
|
1693
|
+
"columnSizing",
|
|
1694
|
+
"columnOrder",
|
|
1695
|
+
"columnVisibility",
|
|
1696
|
+
"columnSort",
|
|
1697
|
+
"filter",
|
|
1698
|
+
"groupBy",
|
|
1699
|
+
"groupExpansion",
|
|
1700
|
+
"pagination",
|
|
1701
|
+
"rowSelection",
|
|
1702
|
+
"columnSelection",
|
|
1703
|
+
"cellSelection"
|
|
1704
|
+
];
|
|
1705
|
+
var DEFAULT_OPTION = {
|
|
1706
|
+
columnSizing: "defaultColumnSizing",
|
|
1707
|
+
columnOrder: "defaultColumnOrder",
|
|
1708
|
+
columnVisibility: "defaultColumnVisibility",
|
|
1709
|
+
columnSort: "defaultColumnSort",
|
|
1710
|
+
filter: "defaultFilter",
|
|
1711
|
+
groupBy: "defaultGroupBy",
|
|
1712
|
+
groupExpansion: "defaultGroupExpansion",
|
|
1713
|
+
pagination: "defaultPagination",
|
|
1714
|
+
rowSelection: "defaultRowSelection",
|
|
1715
|
+
columnSelection: "defaultColumnSelection",
|
|
1716
|
+
cellSelection: "defaultCellSelection"
|
|
1717
|
+
};
|
|
1718
|
+
function startingState(options) {
|
|
1719
|
+
return {
|
|
1720
|
+
columnSizing: options.defaultColumnSizing ?? {},
|
|
1721
|
+
columnOrder: options.defaultColumnOrder ?? [],
|
|
1722
|
+
columnVisibility: options.defaultColumnVisibility ?? {},
|
|
1723
|
+
columnSort: options.defaultColumnSort ?? [],
|
|
1724
|
+
filter: options.defaultFilter ?? [],
|
|
1725
|
+
groupBy: options.defaultGroupBy ?? [],
|
|
1726
|
+
groupExpansion: options.defaultGroupExpansion ?? [],
|
|
1727
|
+
pagination: options.defaultPagination ?? {
|
|
1728
|
+
pageIndex: 0,
|
|
1729
|
+
pageSize: DEFAULT_PAGE_SIZE
|
|
1730
|
+
},
|
|
1731
|
+
rowSelection: options.defaultRowSelection ?? [],
|
|
1732
|
+
columnSelection: options.defaultColumnSelection ?? [],
|
|
1733
|
+
cellSelection: options.defaultCellSelection ?? null
|
|
1734
|
+
};
|
|
1735
|
+
}
|
|
1736
|
+
function definedColumns(columns, rows) {
|
|
1737
|
+
if (columns !== void 0 && columns.length > 0) {
|
|
1738
|
+
return columns;
|
|
1739
|
+
}
|
|
1740
|
+
if (rows !== void 0 && rows.length > 0) {
|
|
1741
|
+
return defineColumnsFromRows(rows);
|
|
1742
|
+
}
|
|
1743
|
+
return NO_COLUMNS;
|
|
1744
|
+
}
|
|
1745
|
+
function pagingInputsOf(state) {
|
|
1746
|
+
return {
|
|
1747
|
+
filter: state.filter,
|
|
1748
|
+
columnSort: state.columnSort,
|
|
1749
|
+
groupBy: state.groupBy
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
function createGridController(initial) {
|
|
1753
|
+
let options = initial;
|
|
1754
|
+
const own = startingState(initial);
|
|
1755
|
+
const resolveView = createGridViewResolver();
|
|
1756
|
+
const resolveColumns2 = memoizeLast(definedColumns);
|
|
1757
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1758
|
+
const reportedConflicts = /* @__PURE__ */ new Set();
|
|
1759
|
+
let heldReports;
|
|
1760
|
+
let state = resolveSlices();
|
|
1761
|
+
let pagingInputs = pagingInputsOf(state);
|
|
1762
|
+
reportConflicts();
|
|
1763
|
+
function controlledValue(key) {
|
|
1764
|
+
const controlled = options;
|
|
1765
|
+
return controlled[key];
|
|
1766
|
+
}
|
|
1767
|
+
function isControlled(key) {
|
|
1768
|
+
return controlledValue(key) !== void 0;
|
|
1769
|
+
}
|
|
1770
|
+
function resolveSlice(key) {
|
|
1771
|
+
const controlled = controlledValue(key);
|
|
1772
|
+
return controlled === void 0 ? own[key] : controlled;
|
|
1773
|
+
}
|
|
1774
|
+
function resolveSlices() {
|
|
1775
|
+
return {
|
|
1776
|
+
columnSizing: resolveSlice("columnSizing"),
|
|
1777
|
+
columnOrder: resolveSlice("columnOrder"),
|
|
1778
|
+
columnVisibility: resolveSlice("columnVisibility"),
|
|
1779
|
+
columnSort: resolveSlice("columnSort"),
|
|
1780
|
+
filter: resolveSlice("filter"),
|
|
1781
|
+
groupBy: resolveSlice("groupBy"),
|
|
1782
|
+
groupExpansion: resolveSlice("groupExpansion"),
|
|
1783
|
+
pagination: resolveSlice("pagination"),
|
|
1784
|
+
rowSelection: resolveSlice("rowSelection"),
|
|
1785
|
+
columnSelection: resolveSlice("columnSelection"),
|
|
1786
|
+
cellSelection: resolveSlice("cellSelection")
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1789
|
+
function reportConflicts() {
|
|
1790
|
+
for (const key of SLICE_KEYS) {
|
|
1791
|
+
const defaultKey = DEFAULT_OPTION[key];
|
|
1792
|
+
if (reportedConflicts.has(key) || !isControlled(key) || options[defaultKey] === void 0) {
|
|
1793
|
+
continue;
|
|
1794
|
+
}
|
|
1795
|
+
reportedConflicts.add(key);
|
|
1796
|
+
reportError(
|
|
1797
|
+
`\`${key}\` and \`${defaultKey}\` are both set \u2014 \`${key}\` is controlled, so \`${defaultKey}\` is ignored.`
|
|
1798
|
+
);
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
function refresh() {
|
|
1802
|
+
let next = resolveSlices();
|
|
1803
|
+
if (next.filter !== pagingInputs.filter || next.columnSort !== pagingInputs.columnSort || next.groupBy !== pagingInputs.groupBy) {
|
|
1804
|
+
pagingInputs = pagingInputsOf(next);
|
|
1805
|
+
if (!isControlled("pagination") && own.pagination.pageIndex !== 0) {
|
|
1806
|
+
own.pagination = { ...own.pagination, pageIndex: 0 };
|
|
1807
|
+
next = { ...next, pagination: own.pagination };
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
state = SLICE_KEYS.every((key) => next[key] === state[key]) ? state : next;
|
|
1811
|
+
}
|
|
1812
|
+
function notify() {
|
|
1813
|
+
for (const listener of [...listeners]) {
|
|
1814
|
+
listener();
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
function commit(key, next, report) {
|
|
1818
|
+
if (next === state[key]) {
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
if (!isControlled(key)) {
|
|
1822
|
+
own[key] = next;
|
|
1823
|
+
}
|
|
1824
|
+
if (heldReports !== void 0) {
|
|
1825
|
+
heldReports.push(report);
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
1828
|
+
refresh();
|
|
1829
|
+
report();
|
|
1830
|
+
notify();
|
|
1831
|
+
}
|
|
1832
|
+
function getView() {
|
|
1833
|
+
return resolveView({
|
|
1834
|
+
rows: options.dataSource ?? NO_ROWS,
|
|
1835
|
+
getRowId: options.getRowId,
|
|
1836
|
+
columns: resolveColumns2(options.columns, options.dataSource),
|
|
1837
|
+
state,
|
|
1838
|
+
options: {
|
|
1839
|
+
paginated: options.paginated,
|
|
1840
|
+
aggregates: options.aggregates,
|
|
1841
|
+
groupAggregateDisplay: options.groupAggregateDisplay,
|
|
1842
|
+
columnSizeDefaults: options.columnSizeDefaults,
|
|
1843
|
+
resizableColumns: options.resizableColumns,
|
|
1844
|
+
reorderableColumns: options.reorderableColumns,
|
|
1845
|
+
groupByDraggableColumns: options.groupByDraggableColumns,
|
|
1846
|
+
resizeMode: options.resizeMode,
|
|
1847
|
+
viewportWidth: options.viewportWidth
|
|
1848
|
+
}
|
|
1849
|
+
});
|
|
1850
|
+
}
|
|
1851
|
+
function commitColumnSizing(next, columnId, width, phase) {
|
|
1852
|
+
commit("columnSizing", next, () => {
|
|
1853
|
+
options.onColumnResize?.({ columnId, width, sizing: next, phase });
|
|
1854
|
+
});
|
|
1855
|
+
}
|
|
1856
|
+
function definedWidth(columnId) {
|
|
1857
|
+
const entry = getView().dataColumns.find(
|
|
1858
|
+
(candidate) => candidate.id === columnId
|
|
1859
|
+
);
|
|
1860
|
+
const [resolved] = entry === void 0 ? [] : resolveColumnWidths(
|
|
1861
|
+
[entry.column],
|
|
1862
|
+
{},
|
|
1863
|
+
{
|
|
1864
|
+
sizes: options.columnSizeDefaults
|
|
1865
|
+
}
|
|
1866
|
+
);
|
|
1867
|
+
return resolved?.width ?? 0;
|
|
1868
|
+
}
|
|
1869
|
+
function commitColumnVisibility(next) {
|
|
1870
|
+
const previous = state.columnVisibility;
|
|
1871
|
+
commit("columnVisibility", next, () => {
|
|
1872
|
+
const changed = new Set(changedKeys(previous, next));
|
|
1873
|
+
const shown = getView().dataColumns.map((entry) => entry.id).filter((id) => changed.delete(id));
|
|
1874
|
+
options.onColumnVisibilityChange?.({
|
|
1875
|
+
columnIds: [...shown, ...changed],
|
|
1876
|
+
visibility: next
|
|
1877
|
+
});
|
|
1878
|
+
});
|
|
1879
|
+
}
|
|
1880
|
+
function changeColumnVisibility(columnIds, visible) {
|
|
1881
|
+
commitColumnVisibility(
|
|
1882
|
+
setColumnVisibility(
|
|
1883
|
+
getView().dataColumns,
|
|
1884
|
+
state.columnVisibility,
|
|
1885
|
+
columnIds,
|
|
1886
|
+
visible
|
|
1887
|
+
)
|
|
1888
|
+
);
|
|
1889
|
+
}
|
|
1890
|
+
function commitGroupBy(next, columnId) {
|
|
1891
|
+
commit("groupBy", next, () => {
|
|
1892
|
+
options.onGroupByChange?.({ columnId, groupBy: next });
|
|
1893
|
+
});
|
|
1894
|
+
}
|
|
1895
|
+
function commitGroupExpansion(next, groupId) {
|
|
1896
|
+
commit("groupExpansion", next, () => {
|
|
1897
|
+
options.onGroupExpansionChange?.({ groupId, expansion: next });
|
|
1898
|
+
});
|
|
1899
|
+
}
|
|
1900
|
+
function commitPagination(next) {
|
|
1901
|
+
if (samePagination(state.pagination, next)) {
|
|
1902
|
+
return;
|
|
1903
|
+
}
|
|
1904
|
+
commit("pagination", next, () => {
|
|
1905
|
+
options.onPaginationChange?.({
|
|
1906
|
+
pagination: next,
|
|
1907
|
+
pageCount: paginateRows(getView().displayRows, next).pageCount
|
|
1908
|
+
});
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1911
|
+
function goToPage(pageIndex) {
|
|
1912
|
+
const current = state.pagination;
|
|
1913
|
+
const clamped = paginateRows(getView().displayRows, {
|
|
1914
|
+
...current,
|
|
1915
|
+
pageIndex
|
|
1916
|
+
}).pageIndex;
|
|
1917
|
+
commitPagination({ ...current, pageIndex: clamped });
|
|
1918
|
+
}
|
|
1919
|
+
function shownPageIndex() {
|
|
1920
|
+
return paginateRows(getView().displayRows, state.pagination).pageIndex;
|
|
1921
|
+
}
|
|
1922
|
+
const selection = createSelectionActions({
|
|
1923
|
+
getState: () => state,
|
|
1924
|
+
getOptions: () => options,
|
|
1925
|
+
getView,
|
|
1926
|
+
commit
|
|
1927
|
+
});
|
|
1928
|
+
const controller = {
|
|
1929
|
+
...selection,
|
|
1930
|
+
getOptions: () => options,
|
|
1931
|
+
setOptions(next) {
|
|
1932
|
+
options = next;
|
|
1933
|
+
reportConflicts();
|
|
1934
|
+
refresh();
|
|
1935
|
+
},
|
|
1936
|
+
getState: () => state,
|
|
1937
|
+
getView,
|
|
1938
|
+
restoreState(saved) {
|
|
1939
|
+
const reports = [];
|
|
1940
|
+
heldReports = reports;
|
|
1941
|
+
try {
|
|
1942
|
+
restoreSlices(saved);
|
|
1943
|
+
} finally {
|
|
1944
|
+
heldReports = void 0;
|
|
1945
|
+
}
|
|
1946
|
+
if (saved.pagination !== void 0) {
|
|
1947
|
+
pagingInputs = pagingInputsOf(resolveSlices());
|
|
1948
|
+
}
|
|
1949
|
+
refresh();
|
|
1950
|
+
for (const report of reports) {
|
|
1951
|
+
report();
|
|
1952
|
+
}
|
|
1953
|
+
if (reports.length > 0) {
|
|
1954
|
+
notify();
|
|
1955
|
+
}
|
|
1956
|
+
},
|
|
1957
|
+
subscribe(listener) {
|
|
1958
|
+
listeners.add(listener);
|
|
1959
|
+
return () => {
|
|
1960
|
+
listeners.delete(listener);
|
|
1961
|
+
};
|
|
1962
|
+
},
|
|
1963
|
+
setColumnSizing(sizing) {
|
|
1964
|
+
const [columnId] = changedKeys(state.columnSizing, sizing);
|
|
1965
|
+
if (columnId !== void 0) {
|
|
1966
|
+
commitColumnSizing(
|
|
1967
|
+
sizing,
|
|
1968
|
+
columnId,
|
|
1969
|
+
sizing[columnId] ?? definedWidth(columnId),
|
|
1970
|
+
"end"
|
|
1971
|
+
);
|
|
1972
|
+
}
|
|
1973
|
+
},
|
|
1974
|
+
resizeColumn(columnId, width, phase) {
|
|
1975
|
+
commitColumnSizing(
|
|
1976
|
+
{ ...state.columnSizing, [columnId]: width },
|
|
1977
|
+
columnId,
|
|
1978
|
+
width,
|
|
1979
|
+
phase
|
|
1980
|
+
);
|
|
1981
|
+
},
|
|
1982
|
+
cancelColumnResize(columnId, startSizing, startWidth) {
|
|
1983
|
+
commitColumnSizing(
|
|
1984
|
+
revertColumnSize(state.columnSizing, startSizing, columnId),
|
|
1985
|
+
columnId,
|
|
1986
|
+
startWidth,
|
|
1987
|
+
"end"
|
|
1988
|
+
);
|
|
1989
|
+
},
|
|
1990
|
+
setColumnOrder(order) {
|
|
1991
|
+
const columnId = changedId(state.columnOrder, order);
|
|
1992
|
+
if (columnId === void 0) {
|
|
1993
|
+
return;
|
|
1994
|
+
}
|
|
1995
|
+
commit("columnOrder", order, () => {
|
|
1996
|
+
options.onColumnOrderChange?.({ columnId, order });
|
|
1997
|
+
});
|
|
1998
|
+
},
|
|
1999
|
+
/**
|
|
2000
|
+
* Applied to every id in display order rather than to the stored order,
|
|
2001
|
+
* which starts empty and may name only some columns, so would have
|
|
2002
|
+
* nothing to rearrange.
|
|
2003
|
+
*/
|
|
2004
|
+
moveColumn(columnId, beforeId) {
|
|
2005
|
+
const orderedIds = getView().dataColumns.map((entry) => entry.id);
|
|
2006
|
+
const next = moveColumnBefore(orderedIds, columnId, beforeId);
|
|
2007
|
+
if (next === orderedIds) {
|
|
2008
|
+
return;
|
|
2009
|
+
}
|
|
2010
|
+
commit("columnOrder", next, () => {
|
|
2011
|
+
options.onColumnOrderChange?.({ columnId, order: next });
|
|
2012
|
+
});
|
|
2013
|
+
},
|
|
2014
|
+
setColumnVisibility(visibility) {
|
|
2015
|
+
if (changedKeys(state.columnVisibility, visibility).length > 0) {
|
|
2016
|
+
commitColumnVisibility(visibility);
|
|
2017
|
+
}
|
|
2018
|
+
},
|
|
2019
|
+
showColumns(columnIds) {
|
|
2020
|
+
changeColumnVisibility(columnIds, true);
|
|
2021
|
+
},
|
|
2022
|
+
hideColumns(columnIds) {
|
|
2023
|
+
changeColumnVisibility(columnIds, false);
|
|
2024
|
+
},
|
|
2025
|
+
setFilter(filter) {
|
|
2026
|
+
if (sameFilter(state.filter, filter)) {
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
2029
|
+
commit("filter", filter, () => {
|
|
2030
|
+
options.onFilterChange?.({ filter });
|
|
2031
|
+
});
|
|
2032
|
+
},
|
|
2033
|
+
setColumnSort(sort) {
|
|
2034
|
+
const columnId = changedEntryColumnId(state.columnSort, sort);
|
|
2035
|
+
if (columnId === void 0) {
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
commit("columnSort", sort, () => {
|
|
2039
|
+
options.onColumnSortChange?.({ columnId, sort });
|
|
2040
|
+
});
|
|
2041
|
+
},
|
|
2042
|
+
toggleColumnSort(columnId, { stack = false } = {}) {
|
|
2043
|
+
const next = toggleColumnSort(state.columnSort, columnId, { stack });
|
|
2044
|
+
commit("columnSort", next, () => {
|
|
2045
|
+
options.onColumnSortChange?.({ columnId, sort: next });
|
|
2046
|
+
});
|
|
2047
|
+
},
|
|
2048
|
+
clearColumnSort(columnId) {
|
|
2049
|
+
const next = state.columnSort.filter(
|
|
2050
|
+
(entry) => entry.columnId !== columnId
|
|
2051
|
+
);
|
|
2052
|
+
if (next.length === state.columnSort.length) {
|
|
2053
|
+
return;
|
|
2054
|
+
}
|
|
2055
|
+
commit("columnSort", next, () => {
|
|
2056
|
+
options.onColumnSortChange?.({ columnId, sort: next });
|
|
2057
|
+
});
|
|
2058
|
+
},
|
|
2059
|
+
setGroupBy(groupBy) {
|
|
2060
|
+
const columnId = changedEntryColumnId(state.groupBy, groupBy);
|
|
2061
|
+
if (columnId !== void 0) {
|
|
2062
|
+
commitGroupBy(groupBy, columnId);
|
|
2063
|
+
}
|
|
2064
|
+
},
|
|
2065
|
+
toggleGroupBy(columnId) {
|
|
2066
|
+
const grouped = state.groupBy.some(
|
|
2067
|
+
(entry) => entry.columnId === columnId
|
|
2068
|
+
);
|
|
2069
|
+
commitGroupBy(
|
|
2070
|
+
grouped ? state.groupBy.filter((entry) => entry.columnId !== columnId) : [...state.groupBy, { columnId }],
|
|
2071
|
+
columnId
|
|
2072
|
+
);
|
|
2073
|
+
},
|
|
2074
|
+
moveGroupBy(columnId, beforeColumnId) {
|
|
2075
|
+
commitGroupBy(
|
|
2076
|
+
moveGroupByBefore(state.groupBy, columnId, beforeColumnId),
|
|
2077
|
+
columnId
|
|
2078
|
+
);
|
|
2079
|
+
},
|
|
2080
|
+
setGroupExpansion(expansion) {
|
|
2081
|
+
if (!sameItems(state.groupExpansion, expansion)) {
|
|
2082
|
+
commitGroupExpansion(expansion, null);
|
|
2083
|
+
}
|
|
2084
|
+
},
|
|
2085
|
+
toggleGroupExpansion(groupId) {
|
|
2086
|
+
commitGroupExpansion(
|
|
2087
|
+
toggleGroupExpansion(state.groupExpansion, groupId),
|
|
2088
|
+
groupId
|
|
2089
|
+
);
|
|
2090
|
+
},
|
|
2091
|
+
expandAllGroups() {
|
|
2092
|
+
commitGroupExpansion(expandAllGroups(state.groupExpansion), null);
|
|
2093
|
+
},
|
|
2094
|
+
collapseAllGroups() {
|
|
2095
|
+
commitGroupExpansion(collapseAllGroups(getView().displayRows), null);
|
|
2096
|
+
},
|
|
2097
|
+
setPagination: commitPagination,
|
|
2098
|
+
goToPage,
|
|
2099
|
+
nextPage() {
|
|
2100
|
+
goToPage(shownPageIndex() + 1);
|
|
2101
|
+
},
|
|
2102
|
+
previousPage() {
|
|
2103
|
+
goToPage(shownPageIndex() - 1);
|
|
2104
|
+
},
|
|
2105
|
+
setPageSize(pageSize) {
|
|
2106
|
+
const first = { pageIndex: 0, pageSize };
|
|
2107
|
+
commitPagination({
|
|
2108
|
+
...first,
|
|
2109
|
+
pageIndex: paginateRows(getView().displayRows, first).pageIndex
|
|
2110
|
+
});
|
|
2111
|
+
}
|
|
2112
|
+
};
|
|
2113
|
+
function restoreSlices(saved) {
|
|
2114
|
+
if (saved.columnSizing !== void 0) {
|
|
2115
|
+
controller.setColumnSizing(saved.columnSizing);
|
|
2116
|
+
}
|
|
2117
|
+
if (saved.columnOrder !== void 0) {
|
|
2118
|
+
controller.setColumnOrder(saved.columnOrder);
|
|
2119
|
+
}
|
|
2120
|
+
if (saved.columnVisibility !== void 0) {
|
|
2121
|
+
controller.setColumnVisibility(saved.columnVisibility);
|
|
2122
|
+
}
|
|
2123
|
+
if (saved.columnSort !== void 0) {
|
|
2124
|
+
controller.setColumnSort(saved.columnSort);
|
|
2125
|
+
}
|
|
2126
|
+
if (saved.filter !== void 0) {
|
|
2127
|
+
controller.setFilter(reviveFilter(saved.filter, getView().dataColumns));
|
|
2128
|
+
}
|
|
2129
|
+
if (saved.groupBy !== void 0) {
|
|
2130
|
+
controller.setGroupBy(saved.groupBy);
|
|
2131
|
+
}
|
|
2132
|
+
if (saved.groupExpansion !== void 0) {
|
|
2133
|
+
controller.setGroupExpansion(saved.groupExpansion);
|
|
2134
|
+
}
|
|
2135
|
+
if (saved.pagination !== void 0) {
|
|
2136
|
+
controller.setPagination(saved.pagination);
|
|
2137
|
+
}
|
|
2138
|
+
if (saved.rowSelection !== void 0) {
|
|
2139
|
+
controller.setRowSelection(saved.rowSelection);
|
|
2140
|
+
}
|
|
2141
|
+
if (saved.columnSelection !== void 0) {
|
|
2142
|
+
controller.setColumnSelection(saved.columnSelection);
|
|
2143
|
+
}
|
|
2144
|
+
if (saved.cellSelection !== void 0) {
|
|
2145
|
+
controller.setCellSelection(saved.cellSelection);
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
return controller;
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
// src/commands/context.ts
|
|
2152
|
+
function selectedRowsOf(rows, selection) {
|
|
2153
|
+
if (selection.length === 0) {
|
|
2154
|
+
return [];
|
|
2155
|
+
}
|
|
2156
|
+
const selected = new Set(selection);
|
|
2157
|
+
return rows.filter((entry) => selected.has(entry.rowId));
|
|
2158
|
+
}
|
|
2159
|
+
function createCommandContext({
|
|
2160
|
+
controller,
|
|
2161
|
+
target,
|
|
2162
|
+
messages,
|
|
2163
|
+
host
|
|
2164
|
+
}) {
|
|
2165
|
+
const state = controller.getState();
|
|
2166
|
+
const view = controller.getView();
|
|
2167
|
+
let selectedRows;
|
|
2168
|
+
return {
|
|
2169
|
+
target,
|
|
2170
|
+
state,
|
|
2171
|
+
view,
|
|
2172
|
+
options: controller.getOptions(),
|
|
2173
|
+
messages,
|
|
2174
|
+
actions: controller,
|
|
2175
|
+
host,
|
|
2176
|
+
row: target.area === "cell" ? view.rows.find((entry) => entry.rowId === target.rowId) ?? null : null,
|
|
2177
|
+
column: "columnId" in target ? view.dataColumns.find((entry) => entry.id === target.columnId) ?? null : null,
|
|
2178
|
+
selectedRows: () => selectedRows ??= selectedRowsOf(view.rows, state.rowSelection)
|
|
2179
|
+
};
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
// src/util/format.ts
|
|
2183
|
+
var DATE_FIELDS = {
|
|
2184
|
+
year: "numeric",
|
|
2185
|
+
month: "numeric",
|
|
2186
|
+
day: "numeric"
|
|
2187
|
+
};
|
|
2188
|
+
var TIME_FIELDS = {
|
|
2189
|
+
hour: "numeric",
|
|
2190
|
+
minute: "numeric",
|
|
2191
|
+
second: "numeric"
|
|
2192
|
+
};
|
|
2193
|
+
var DATE_TIME_FIELDS = {
|
|
2194
|
+
...DATE_FIELDS,
|
|
2195
|
+
...TIME_FIELDS
|
|
2196
|
+
};
|
|
2197
|
+
var CURRENCY_DIGITS = {
|
|
2198
|
+
minimumFractionDigits: 2,
|
|
2199
|
+
maximumFractionDigits: 2
|
|
2200
|
+
};
|
|
2201
|
+
var NO_FORMAT = {};
|
|
2202
|
+
var formatters = /* @__PURE__ */ new WeakMap();
|
|
2203
|
+
function cached(format, key, create) {
|
|
2204
|
+
const owner = format ?? NO_FORMAT;
|
|
2205
|
+
let byKey = formatters.get(owner);
|
|
2206
|
+
if (byKey === void 0) {
|
|
2207
|
+
byKey = /* @__PURE__ */ new Map();
|
|
2208
|
+
formatters.set(owner, byKey);
|
|
2209
|
+
}
|
|
2210
|
+
const existing = byKey.get(key);
|
|
2211
|
+
if (existing !== void 0) {
|
|
2212
|
+
return existing;
|
|
2213
|
+
}
|
|
2214
|
+
const formatter = create();
|
|
2215
|
+
byKey.set(key, formatter);
|
|
2216
|
+
return formatter;
|
|
2217
|
+
}
|
|
2218
|
+
function localeKey(locale) {
|
|
2219
|
+
return typeof locale === "string" ? locale : locale?.join(",") ?? "";
|
|
2220
|
+
}
|
|
2221
|
+
function isNumericType(type) {
|
|
2222
|
+
return type === "number" || type === "decimal" || type === "currency" || type === "percent";
|
|
2223
|
+
}
|
|
2224
|
+
function numberOptions(type, format) {
|
|
2225
|
+
switch (type) {
|
|
2226
|
+
case "currency":
|
|
2227
|
+
return format?.currency === void 0 ? { ...CURRENCY_DIGITS, ...format } : { style: "currency", ...format };
|
|
2228
|
+
case "percent":
|
|
2229
|
+
return { style: "percent", ...format };
|
|
2230
|
+
default:
|
|
2231
|
+
return format ?? {};
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
function dateOptions(type, format) {
|
|
2235
|
+
if (format !== void 0) {
|
|
2236
|
+
return format;
|
|
2237
|
+
}
|
|
2238
|
+
if (type === "date") return DATE_FIELDS;
|
|
2239
|
+
if (type === "time") return TIME_FIELDS;
|
|
2240
|
+
return DATE_TIME_FIELDS;
|
|
2241
|
+
}
|
|
2242
|
+
function formatNumber(value, type, format, locale) {
|
|
2243
|
+
const formatter = cached(
|
|
2244
|
+
format,
|
|
2245
|
+
`number|${type ?? ""}|${localeKey(locale)}`,
|
|
2246
|
+
() => new Intl.NumberFormat(
|
|
2247
|
+
locale,
|
|
2248
|
+
numberOptions(type, format)
|
|
2249
|
+
)
|
|
2250
|
+
);
|
|
2251
|
+
return formatter.format(value);
|
|
2252
|
+
}
|
|
2253
|
+
function formatDate(value, type, format, locale) {
|
|
2254
|
+
if (Number.isNaN(value.getTime())) {
|
|
2255
|
+
return String(value);
|
|
2256
|
+
}
|
|
2257
|
+
const formatter = cached(
|
|
2258
|
+
format,
|
|
2259
|
+
`date|${type ?? ""}|${localeKey(locale)}`,
|
|
2260
|
+
() => new Intl.DateTimeFormat(
|
|
2261
|
+
locale,
|
|
2262
|
+
dateOptions(type, format)
|
|
2263
|
+
)
|
|
2264
|
+
);
|
|
2265
|
+
return formatter.format(value);
|
|
2266
|
+
}
|
|
2267
|
+
function formatOther(value) {
|
|
2268
|
+
if (typeof value === "function") {
|
|
2269
|
+
return "(function)";
|
|
2270
|
+
}
|
|
2271
|
+
if (typeof value === "object" && value !== null) {
|
|
2272
|
+
try {
|
|
2273
|
+
return JSON.stringify(value);
|
|
2274
|
+
} catch {
|
|
2275
|
+
return Object.prototype.toString.call(value);
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
return String(value);
|
|
2279
|
+
}
|
|
2280
|
+
function formatCellValue(value, column, locale, row, messages) {
|
|
2281
|
+
if (column.valueFormatter !== void 0) {
|
|
2282
|
+
return column.valueFormatter(value, row);
|
|
2283
|
+
}
|
|
2284
|
+
if (value === null || value === void 0) {
|
|
2285
|
+
return "";
|
|
2286
|
+
}
|
|
2287
|
+
if (typeof value === "boolean" && messages !== void 0) {
|
|
2288
|
+
return value ? messages.booleanTrue : messages.booleanFalse;
|
|
2289
|
+
}
|
|
2290
|
+
const { type, format } = column;
|
|
2291
|
+
const formatted = format !== void 0 || locale !== void 0;
|
|
2292
|
+
if (value instanceof Date) {
|
|
2293
|
+
return formatted ? formatDate(value, type, format, locale) : value.toLocaleString();
|
|
2294
|
+
}
|
|
2295
|
+
if (formatted && (typeof value === "number" || typeof value === "bigint") && isNumericType(type)) {
|
|
2296
|
+
return formatNumber(value, type, format, locale);
|
|
2297
|
+
}
|
|
2298
|
+
if (typeof value === "string") {
|
|
2299
|
+
return value;
|
|
2300
|
+
}
|
|
2301
|
+
return formatOther(value);
|
|
2302
|
+
}
|
|
2303
|
+
function aggregateFormatColumn(value, spec, column) {
|
|
2304
|
+
const plain = {
|
|
2305
|
+
field: spec.columnId,
|
|
2306
|
+
type: "number"
|
|
2307
|
+
};
|
|
2308
|
+
if (column === void 0 || spec.fn === "count" || spec.fn === "countDistinct") {
|
|
2309
|
+
return plain;
|
|
2310
|
+
}
|
|
2311
|
+
const numeric = typeof value === "number" || typeof value === "bigint";
|
|
2312
|
+
return column.valueFormatter === void 0 && numeric && !isNumericType(column.type) ? plain : column;
|
|
2313
|
+
}
|
|
2314
|
+
function formatAggregateValue(value, spec, column, locale) {
|
|
2315
|
+
const target = aggregateFormatColumn(value, spec, column);
|
|
2316
|
+
if (typeof value === "number" && target.valueFormatter === void 0 && target.format === void 0 && locale === void 0) {
|
|
2317
|
+
return value.toLocaleString();
|
|
2318
|
+
}
|
|
2319
|
+
return formatCellValue(value, target, locale);
|
|
2320
|
+
}
|
|
2321
|
+
function formatGroupValue(value, column, locale, messages) {
|
|
2322
|
+
return value === null || value === void 0 ? messages.blankGroupValue : formatCellValue(value, column, locale, void 0, messages);
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
// src/export/buildExportTable.ts
|
|
2326
|
+
var EMPTY_CELL = { value: null, text: "" };
|
|
2327
|
+
function isDataRow(entry) {
|
|
2328
|
+
return !("kind" in entry);
|
|
2329
|
+
}
|
|
2330
|
+
function pickColumns(view, selection) {
|
|
2331
|
+
let picked;
|
|
2332
|
+
if (selection === "visible") {
|
|
2333
|
+
picked = view.columns;
|
|
2334
|
+
} else if (selection === "all") {
|
|
2335
|
+
picked = view.dataColumns;
|
|
2336
|
+
} else {
|
|
2337
|
+
const byId = new Map(view.dataColumns.map((entry) => [entry.id, entry]));
|
|
2338
|
+
picked = [...new Set(selection)].flatMap((id) => byId.get(id) ?? []);
|
|
2339
|
+
}
|
|
2340
|
+
return picked.filter((entry) => entry.column.exportable !== false);
|
|
2341
|
+
}
|
|
2342
|
+
function describeColumns(picked, source) {
|
|
2343
|
+
const shown = new Map(source.view.columns.map((entry) => [entry.id, entry]));
|
|
2344
|
+
const resolved = resolveColumnWidths(
|
|
2345
|
+
picked.map((entry) => entry.column),
|
|
2346
|
+
source.state.columnSizing,
|
|
2347
|
+
{ sizes: source.options.columnSizeDefaults }
|
|
2348
|
+
);
|
|
2349
|
+
return resolved.map((entry) => {
|
|
2350
|
+
const rendered = shown.get(entry.id) ?? entry;
|
|
2351
|
+
return {
|
|
2352
|
+
id: rendered.id,
|
|
2353
|
+
header: resolveColumnHeaderText(rendered.column),
|
|
2354
|
+
type: rendered.column.type ?? "string",
|
|
2355
|
+
format: rendered.column.format,
|
|
2356
|
+
alignment: rendered.alignment,
|
|
2357
|
+
width: rendered.width
|
|
2358
|
+
};
|
|
2359
|
+
});
|
|
2360
|
+
}
|
|
2361
|
+
function scopedRows(source, scope) {
|
|
2362
|
+
const { view, state } = source;
|
|
2363
|
+
switch (scope) {
|
|
2364
|
+
case "all":
|
|
2365
|
+
return view.rows;
|
|
2366
|
+
case "page":
|
|
2367
|
+
return view.displayDataRows;
|
|
2368
|
+
case "selection": {
|
|
2369
|
+
const selected = new Set(state.rowSelection);
|
|
2370
|
+
return view.rows.filter((entry) => selected.has(entry.rowId));
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
function typedValue(value, text) {
|
|
2375
|
+
if (value === null || value === void 0) {
|
|
2376
|
+
return null;
|
|
2377
|
+
}
|
|
2378
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value instanceof Date ? value : text;
|
|
2379
|
+
}
|
|
2380
|
+
function dataCell(row, column, context) {
|
|
2381
|
+
const raw = accessDotted(row, column.field);
|
|
2382
|
+
const text = formatCellValue(
|
|
2383
|
+
raw,
|
|
2384
|
+
column,
|
|
2385
|
+
context.locale,
|
|
2386
|
+
row,
|
|
2387
|
+
context.messages
|
|
2388
|
+
);
|
|
2389
|
+
return {
|
|
2390
|
+
value: column.exportValue === void 0 ? typedValue(raw, text) : column.exportValue(raw, row),
|
|
2391
|
+
text
|
|
2392
|
+
};
|
|
2393
|
+
}
|
|
2394
|
+
function aggregateCell(results, specs, column, locale) {
|
|
2395
|
+
const parts = specs.flatMap((spec) => {
|
|
2396
|
+
const value = results.get(spec.id ?? spec.columnId);
|
|
2397
|
+
return value === null || value === void 0 ? [] : [{ value, text: formatAggregateValue(value, spec, column, locale) }];
|
|
2398
|
+
});
|
|
2399
|
+
const [only] = parts;
|
|
2400
|
+
if (only === void 0) {
|
|
2401
|
+
return EMPTY_CELL;
|
|
2402
|
+
}
|
|
2403
|
+
if (parts.length === 1) {
|
|
2404
|
+
return { value: typedValue(only.value, only.text), text: only.text };
|
|
2405
|
+
}
|
|
2406
|
+
const text = parts.map((part) => part.text).join(", ");
|
|
2407
|
+
return { value: text, text };
|
|
2408
|
+
}
|
|
2409
|
+
function aggregateCells(results, context) {
|
|
2410
|
+
return context.columns.map((entry) => {
|
|
2411
|
+
const specs = context.specsByColumn.get(entry.id);
|
|
2412
|
+
return specs === void 0 ? EMPTY_CELL : aggregateCell(results, specs, entry.column, context.locale);
|
|
2413
|
+
});
|
|
2414
|
+
}
|
|
2415
|
+
function groupLabel(entry, source) {
|
|
2416
|
+
const column = source.view.dataColumns.find(
|
|
2417
|
+
(candidate) => candidate.id === entry.columnId
|
|
2418
|
+
)?.column ?? { field: entry.columnId };
|
|
2419
|
+
const value = formatGroupValue(
|
|
2420
|
+
entry.value,
|
|
2421
|
+
column,
|
|
2422
|
+
source.options.locale,
|
|
2423
|
+
source.messages
|
|
2424
|
+
);
|
|
2425
|
+
return `${resolveColumnHeaderText(column)}: ${value}`;
|
|
2426
|
+
}
|
|
2427
|
+
function toExportRow(entry, source, context) {
|
|
2428
|
+
if (isDataRow(entry)) {
|
|
2429
|
+
return {
|
|
2430
|
+
kind: "data",
|
|
2431
|
+
rowId: entry.rowId,
|
|
2432
|
+
cells: context.columns.map(
|
|
2433
|
+
(column) => dataCell(entry.row, column.column, context)
|
|
2434
|
+
)
|
|
2435
|
+
};
|
|
2436
|
+
}
|
|
2437
|
+
if (entry.kind === "group") {
|
|
2438
|
+
return {
|
|
2439
|
+
kind: "group",
|
|
2440
|
+
level: entry.level,
|
|
2441
|
+
groupId: entry.groupId,
|
|
2442
|
+
label: groupLabel(entry, source),
|
|
2443
|
+
count: entry.count,
|
|
2444
|
+
cells: context.display === "inline" ? aggregateCells(entry.aggregates, context) : context.columns.map(() => EMPTY_CELL)
|
|
2445
|
+
};
|
|
2446
|
+
}
|
|
2447
|
+
return {
|
|
2448
|
+
kind: "groupSummary",
|
|
2449
|
+
level: entry.level,
|
|
2450
|
+
cells: aggregateCells(entry.aggregates, context)
|
|
2451
|
+
};
|
|
2452
|
+
}
|
|
2453
|
+
function specsByColumnOf(specs) {
|
|
2454
|
+
const byColumn = /* @__PURE__ */ new Map();
|
|
2455
|
+
for (const spec of specs) {
|
|
2456
|
+
const owned = byColumn.get(spec.columnId);
|
|
2457
|
+
if (owned === void 0) {
|
|
2458
|
+
byColumn.set(spec.columnId, [spec]);
|
|
2459
|
+
} else {
|
|
2460
|
+
owned.push(spec);
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
return byColumn;
|
|
2464
|
+
}
|
|
2465
|
+
function buildExportTable(source, options = {}) {
|
|
2466
|
+
const { view, state } = source;
|
|
2467
|
+
const specs = options.includeAggregates ?? true ? source.options.aggregates ?? [] : [];
|
|
2468
|
+
const columns = pickColumns(view, options.columns ?? "visible");
|
|
2469
|
+
const context = {
|
|
2470
|
+
columns,
|
|
2471
|
+
specsByColumn: specsByColumnOf(specs),
|
|
2472
|
+
display: source.options.groupAggregateDisplay ?? "inline",
|
|
2473
|
+
locale: source.options.locale,
|
|
2474
|
+
messages: source.messages
|
|
2475
|
+
};
|
|
2476
|
+
const rows = scopedRows(source, options.scope ?? "all");
|
|
2477
|
+
const grouped = groupRows(rows, state.groupBy, [], view.dataColumns);
|
|
2478
|
+
const displayRows = options.includeGroups ?? true ? withGroupAggregates(
|
|
2479
|
+
grouped,
|
|
2480
|
+
rows,
|
|
2481
|
+
state.groupBy,
|
|
2482
|
+
specs,
|
|
2483
|
+
view.dataColumns,
|
|
2484
|
+
context.display
|
|
2485
|
+
) : grouped.filter(isDataRow);
|
|
2486
|
+
let footer = null;
|
|
2487
|
+
if (specs.length > 0) {
|
|
2488
|
+
const totals = rows === view.rows ? view.grandTotal : computeAggregates(
|
|
2489
|
+
rows.map((entry) => entry.row),
|
|
2490
|
+
specs,
|
|
2491
|
+
view.dataColumns
|
|
2492
|
+
);
|
|
2493
|
+
footer = { kind: "total", cells: aggregateCells(totals, context) };
|
|
2494
|
+
}
|
|
2495
|
+
return {
|
|
2496
|
+
columns: describeColumns(columns, source),
|
|
2497
|
+
header: options.includeHeader ?? true,
|
|
2498
|
+
rows: displayRows.map((entry) => toExportRow(entry, source, context)),
|
|
2499
|
+
footer
|
|
2500
|
+
};
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
// src/export/htmlTable.ts
|
|
2504
|
+
var ENTITIES = {
|
|
2505
|
+
"&": "&",
|
|
2506
|
+
"<": "<",
|
|
2507
|
+
">": ">",
|
|
2508
|
+
'"': """,
|
|
2509
|
+
"'": "'"
|
|
2510
|
+
};
|
|
2511
|
+
function escapeHtml(text) {
|
|
2512
|
+
return text.replace(/[&<>"']/g, (char) => ENTITIES[char] ?? char);
|
|
2513
|
+
}
|
|
2514
|
+
function cellHtml(tag, text, column) {
|
|
2515
|
+
const alignment = column?.alignment ?? "left";
|
|
2516
|
+
const style = alignment === "left" ? "" : ` style="text-align:${alignment}"`;
|
|
2517
|
+
return `<${tag}${style}>${escapeHtml(text)}</${tag}>`;
|
|
2518
|
+
}
|
|
2519
|
+
function cellsHtml(cells, columns) {
|
|
2520
|
+
return cells.map((cell, index) => cellHtml("td", cell.text, columns[index])).join("");
|
|
2521
|
+
}
|
|
2522
|
+
function groupRowHtml(row, columns) {
|
|
2523
|
+
const firstAggregate = row.cells.findIndex((cell) => cell.text !== "");
|
|
2524
|
+
const span = Math.max(
|
|
2525
|
+
1,
|
|
2526
|
+
firstAggregate === -1 ? row.cells.length : firstAggregate
|
|
2527
|
+
);
|
|
2528
|
+
const colspan = span > 1 ? ` colspan="${String(span)}"` : "";
|
|
2529
|
+
const label = `<td${colspan}>${escapeHtml(row.label)}</td>`;
|
|
2530
|
+
const aggregates = row.cells.slice(span).map((cell, index) => cellHtml("td", cell.text, columns[span + index])).join("");
|
|
2531
|
+
return `<tr class="export-group" data-level="${String(row.level)}">${label}${aggregates}</tr>`;
|
|
2532
|
+
}
|
|
2533
|
+
function rowHtml(row, columns) {
|
|
2534
|
+
switch (row.kind) {
|
|
2535
|
+
case "data":
|
|
2536
|
+
return `<tr>${cellsHtml(row.cells, columns)}</tr>`;
|
|
2537
|
+
case "group":
|
|
2538
|
+
return groupRowHtml(row, columns);
|
|
2539
|
+
case "groupSummary":
|
|
2540
|
+
return `<tr class="export-group-summary" data-level="${String(row.level)}">${cellsHtml(row.cells, columns)}</tr>`;
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
function toHtmlTable(table, options = {}) {
|
|
2544
|
+
const { columns } = table;
|
|
2545
|
+
const caption = options.caption === void 0 ? "" : `<caption>${escapeHtml(options.caption)}</caption>`;
|
|
2546
|
+
const head = table.header ? `<thead><tr>${columns.map((column) => cellHtml("th", column.header, column)).join("")}</tr></thead>` : "";
|
|
2547
|
+
const body = `<tbody>${table.rows.map((row) => rowHtml(row, columns)).join("")}</tbody>`;
|
|
2548
|
+
const foot = table.footer === null ? "" : `<tfoot><tr>${cellsHtml(table.footer.cells, columns)}</tr></tfoot>`;
|
|
2549
|
+
return `<table class="gridkit-export-table">${caption}${head}${body}${foot}</table>`;
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
// src/export/records.ts
|
|
2553
|
+
function rawText(value) {
|
|
2554
|
+
if (value === null) {
|
|
2555
|
+
return "";
|
|
2556
|
+
}
|
|
2557
|
+
if (value instanceof Date) {
|
|
2558
|
+
return Number.isNaN(value.getTime()) ? "" : value.toISOString();
|
|
2559
|
+
}
|
|
2560
|
+
return String(value);
|
|
2561
|
+
}
|
|
2562
|
+
function cellField(cell, values) {
|
|
2563
|
+
return {
|
|
2564
|
+
text: values === "raw" ? rawText(cell.value) : cell.text,
|
|
2565
|
+
isNumber: typeof cell.value === "number"
|
|
2566
|
+
};
|
|
2567
|
+
}
|
|
2568
|
+
function textField(text) {
|
|
2569
|
+
return { text, isNumber: false };
|
|
2570
|
+
}
|
|
2571
|
+
function textRecords(table, values) {
|
|
2572
|
+
const records = [];
|
|
2573
|
+
if (table.header) {
|
|
2574
|
+
records.push(table.columns.map((column) => textField(column.header)));
|
|
2575
|
+
}
|
|
2576
|
+
for (const row of table.rows) {
|
|
2577
|
+
records.push(
|
|
2578
|
+
row.cells.map(
|
|
2579
|
+
(cell, index) => row.kind === "group" && index === 0 ? textField(row.label) : cellField(cell, values)
|
|
2580
|
+
)
|
|
2581
|
+
);
|
|
2582
|
+
}
|
|
2583
|
+
if (table.footer !== null) {
|
|
2584
|
+
records.push(table.footer.cells.map((cell) => cellField(cell, values)));
|
|
2585
|
+
}
|
|
2586
|
+
return records;
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2589
|
+
// src/export/tsv.ts
|
|
2590
|
+
var SEPARATORS = /\r\n|[\t\r\n]/g;
|
|
2591
|
+
function toTsv(table, options = {}) {
|
|
2592
|
+
return textRecords(table, options.values ?? "text").map(
|
|
2593
|
+
(record) => record.map((field) => field.text.replace(SEPARATORS, " ")).join(" ")
|
|
2594
|
+
).join("\n");
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2597
|
+
// src/export/copy.ts
|
|
2598
|
+
function isCopyable(column) {
|
|
2599
|
+
return column.column.exportable !== false;
|
|
2600
|
+
}
|
|
2601
|
+
function hasRow(source, rowId) {
|
|
2602
|
+
return source.view.rows.some((entry) => entry.rowId === rowId);
|
|
2603
|
+
}
|
|
2604
|
+
function hasColumn(source, columnId) {
|
|
2605
|
+
return source.view.columns.some(
|
|
2606
|
+
(entry) => entry.id === columnId && isCopyable(entry)
|
|
2607
|
+
);
|
|
2608
|
+
}
|
|
2609
|
+
function selectionCopy(source) {
|
|
2610
|
+
const { state, view } = source;
|
|
2611
|
+
const selectedRows = new Set(state.rowSelection);
|
|
2612
|
+
const count = view.rows.filter(
|
|
2613
|
+
(entry) => selectedRows.has(entry.rowId)
|
|
2614
|
+
).length;
|
|
2615
|
+
if (count > 0) {
|
|
2616
|
+
return { kind: "rows", count };
|
|
2617
|
+
}
|
|
2618
|
+
const selectedColumns = new Set(state.columnSelection);
|
|
2619
|
+
const columnIds = view.columns.filter((entry) => selectedColumns.has(entry.id) && isCopyable(entry)).map((entry) => entry.id);
|
|
2620
|
+
if (columnIds.length > 0 && view.rows.length > 0) {
|
|
2621
|
+
return { kind: "columns", columnIds };
|
|
2622
|
+
}
|
|
2623
|
+
const cell = state.cellSelection;
|
|
2624
|
+
if (cell !== null && hasRow(source, cell.rowId) && hasColumn(source, cell.columnId)) {
|
|
2625
|
+
return { kind: "cell", rowId: cell.rowId, columnId: cell.columnId };
|
|
2626
|
+
}
|
|
2627
|
+
return null;
|
|
2628
|
+
}
|
|
2629
|
+
function canCopySelection(source) {
|
|
2630
|
+
return selectionCopy(source) !== null;
|
|
2631
|
+
}
|
|
2632
|
+
function withRows(source, rowIds) {
|
|
2633
|
+
return { ...source, state: { ...source.state, rowSelection: rowIds } };
|
|
2634
|
+
}
|
|
2635
|
+
function contentOf(source, { scope, columns, header }, unit) {
|
|
2636
|
+
const table = buildExportTable(source, {
|
|
2637
|
+
scope,
|
|
2638
|
+
columns: columns ?? "visible",
|
|
2639
|
+
includeHeader: header,
|
|
2640
|
+
includeGroups: false,
|
|
2641
|
+
includeAggregates: false
|
|
2642
|
+
});
|
|
2643
|
+
if (table.columns.length === 0 || table.rows.length === 0) {
|
|
2644
|
+
return null;
|
|
2645
|
+
}
|
|
2646
|
+
return {
|
|
2647
|
+
text: toTsv(table),
|
|
2648
|
+
html: toHtmlTable(table),
|
|
2649
|
+
count: unit === "columns" ? table.columns.length : table.rows.length,
|
|
2650
|
+
unit
|
|
2651
|
+
};
|
|
2652
|
+
}
|
|
2653
|
+
function cellContent(source, rowId, columnId) {
|
|
2654
|
+
if (!hasRow(source, rowId) || !hasColumn(source, columnId)) {
|
|
2655
|
+
return null;
|
|
2656
|
+
}
|
|
2657
|
+
return contentOf(
|
|
2658
|
+
withRows(source, [rowId]),
|
|
2659
|
+
{ scope: "selection", columns: [columnId], header: false },
|
|
2660
|
+
"cells"
|
|
2661
|
+
);
|
|
2662
|
+
}
|
|
2663
|
+
function rowContent(source, rowId) {
|
|
2664
|
+
if (!hasRow(source, rowId)) {
|
|
2665
|
+
return null;
|
|
2666
|
+
}
|
|
2667
|
+
return contentOf(
|
|
2668
|
+
withRows(source, [rowId]),
|
|
2669
|
+
{ scope: "selection", header: false },
|
|
2670
|
+
"rows"
|
|
2671
|
+
);
|
|
2672
|
+
}
|
|
2673
|
+
function selectionContent(source) {
|
|
2674
|
+
const selection = selectionCopy(source);
|
|
2675
|
+
switch (selection?.kind) {
|
|
2676
|
+
case void 0:
|
|
2677
|
+
return null;
|
|
2678
|
+
case "rows":
|
|
2679
|
+
return contentOf(
|
|
2680
|
+
source,
|
|
2681
|
+
{ scope: "selection", header: selection.count > 1 },
|
|
2682
|
+
"rows"
|
|
2683
|
+
);
|
|
2684
|
+
case "columns":
|
|
2685
|
+
return contentOf(
|
|
2686
|
+
source,
|
|
2687
|
+
{ scope: "all", columns: selection.columnIds, header: true },
|
|
2688
|
+
"columns"
|
|
2689
|
+
);
|
|
2690
|
+
case "cell":
|
|
2691
|
+
return cellContent(source, selection.rowId, selection.columnId);
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
function copyContent(source, request) {
|
|
2695
|
+
switch (request.kind) {
|
|
2696
|
+
case "cell":
|
|
2697
|
+
return cellContent(source, request.rowId, request.columnId);
|
|
2698
|
+
case "row":
|
|
2699
|
+
return rowContent(source, request.rowId);
|
|
2700
|
+
case "selection":
|
|
2701
|
+
return selectionContent(source);
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2705
|
+
// src/export/exportFile.ts
|
|
2706
|
+
function withExtension(fileName, extension) {
|
|
2707
|
+
const suffix = `.${extension}`;
|
|
2708
|
+
return fileName.toLowerCase().endsWith(suffix.toLowerCase()) ? fileName : fileName + suffix;
|
|
2709
|
+
}
|
|
2710
|
+
function writerFor(format, writers) {
|
|
2711
|
+
if (typeof format !== "string") {
|
|
2712
|
+
return format;
|
|
2713
|
+
}
|
|
2714
|
+
const writer = writers.get(format);
|
|
2715
|
+
if (writer === void 0) {
|
|
2716
|
+
reportError(
|
|
2717
|
+
`there is no export writer for \`${format}\` \u2014 register one through \`exportWriters\`.`
|
|
2718
|
+
);
|
|
2719
|
+
return null;
|
|
2720
|
+
}
|
|
2721
|
+
return writer;
|
|
2722
|
+
}
|
|
2723
|
+
function fileNameFor(options, writer) {
|
|
2724
|
+
const { fileName = "export" } = options;
|
|
2725
|
+
const name = typeof fileName === "function" ? fileName(writer.format) : fileName;
|
|
2726
|
+
return withExtension(name, writer.fileExtension);
|
|
2727
|
+
}
|
|
2728
|
+
function exportFile(source, format, options, writers) {
|
|
2729
|
+
const writer = writerFor(format, writers);
|
|
2730
|
+
if (writer === null) {
|
|
2731
|
+
return null;
|
|
2732
|
+
}
|
|
2733
|
+
const content = writer.write(buildExportTable(source, options), {
|
|
2734
|
+
locale: source.options.locale,
|
|
2735
|
+
messages: source.messages,
|
|
2736
|
+
options
|
|
2737
|
+
});
|
|
2738
|
+
const fileName = fileNameFor(options, writer);
|
|
2739
|
+
const { mimeType } = writer;
|
|
2740
|
+
return content instanceof Promise ? content.then((resolved) => ({ fileName, mimeType, content: resolved })) : { fileName, mimeType, content };
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2743
|
+
// src/export/print.ts
|
|
2744
|
+
var PORTRAIT_COLUMN_LIMIT = 6;
|
|
2745
|
+
function resolveOrientation(table, options) {
|
|
2746
|
+
return options.orientation ?? (table.columns.length > PORTRAIT_COLUMN_LIMIT ? "landscape" : "portrait");
|
|
2747
|
+
}
|
|
2748
|
+
function timestampText(timestamp, locale) {
|
|
2749
|
+
const date = timestamp === true ? /* @__PURE__ */ new Date() : timestamp;
|
|
2750
|
+
return new Intl.DateTimeFormat(locale, {
|
|
2751
|
+
dateStyle: "medium",
|
|
2752
|
+
timeStyle: "short"
|
|
2753
|
+
}).format(date);
|
|
2754
|
+
}
|
|
2755
|
+
function line(tag, className, text) {
|
|
2756
|
+
return text === void 0 || text === "" ? "" : `<${tag} class="${className}">${escapeHtml(text)}</${tag}>`;
|
|
2757
|
+
}
|
|
2758
|
+
function headerHtml(options) {
|
|
2759
|
+
const { timestamp = false } = options;
|
|
2760
|
+
const lines = line("h1", "print-title", options.title) + line("p", "print-subtitle", options.subtitle) + (timestamp === false ? "" : line("p", "print-timestamp", timestampText(timestamp, options.locale)));
|
|
2761
|
+
return lines === "" ? "" : `<header class="gridkit-print-header">${lines}</header>`;
|
|
2762
|
+
}
|
|
2763
|
+
function buildPrintDocument(table, options = {}) {
|
|
2764
|
+
const footer = line("footer", "gridkit-print-footer", options.footer);
|
|
2765
|
+
return {
|
|
2766
|
+
title: options.title ?? "",
|
|
2767
|
+
bodyHtml: `${headerHtml(options)}${toHtmlTable(table)}${footer}`,
|
|
2768
|
+
page: {
|
|
2769
|
+
paperSize: options.paperSize ?? "A4",
|
|
2770
|
+
orientation: resolveOrientation(table, options),
|
|
2771
|
+
pageNumbers: options.pageNumbers ?? true,
|
|
2772
|
+
fitToWidth: options.fitToWidth ?? false
|
|
2773
|
+
}
|
|
2774
|
+
};
|
|
2775
|
+
}
|
|
2776
|
+
function exportPrintDocument(source, options = {}) {
|
|
2777
|
+
return buildPrintDocument(buildExportTable(source, options), {
|
|
2778
|
+
...options,
|
|
2779
|
+
title: options.title ?? source.options.label,
|
|
2780
|
+
locale: options.locale ?? source.options.locale
|
|
2781
|
+
});
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
// src/export/csv.ts
|
|
2785
|
+
var BOM = "\uFEFF";
|
|
2786
|
+
var FORMULA_START = /^[=+\-@\t\r]/;
|
|
2787
|
+
var SIGN_START = /^[+-]/;
|
|
2788
|
+
var NEEDS_QUOTES = /["\r\n]/;
|
|
2789
|
+
function autoDelimiter(locale) {
|
|
2790
|
+
const decimal = new Intl.NumberFormat(locale).formatToParts(1.5).find((part) => part.type === "decimal")?.value;
|
|
2791
|
+
return decimal === "," ? ";" : ",";
|
|
2792
|
+
}
|
|
2793
|
+
function neutralize(field) {
|
|
2794
|
+
const { text } = field;
|
|
2795
|
+
if (!FORMULA_START.test(text) || field.isNumber && SIGN_START.test(text)) {
|
|
2796
|
+
return text;
|
|
2797
|
+
}
|
|
2798
|
+
return `'${text}`;
|
|
2799
|
+
}
|
|
2800
|
+
function quote(text, delimiter) {
|
|
2801
|
+
return text.includes(delimiter) || NEEDS_QUOTES.test(text) ? `"${text.replaceAll('"', '""')}"` : text;
|
|
2802
|
+
}
|
|
2803
|
+
function toCsv(table, options = {}) {
|
|
2804
|
+
const delimiter = options.delimiter === "auto" ? autoDelimiter(options.locale) : options.delimiter ?? ",";
|
|
2805
|
+
const guard = options.neutralizeFormulas ?? true;
|
|
2806
|
+
const lines = textRecords(table, options.values ?? "text").map(
|
|
2807
|
+
(record) => record.map((field) => quote(guard ? neutralize(field) : field.text, delimiter)).join(delimiter)
|
|
2808
|
+
);
|
|
2809
|
+
return (options.bom ?? true ? BOM : "") + lines.join(options.eol ?? "\r\n");
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
// src/export/json.ts
|
|
2813
|
+
function uniqueNames(names) {
|
|
2814
|
+
const used = /* @__PURE__ */ new Set();
|
|
2815
|
+
return names.map((name) => {
|
|
2816
|
+
let unique = name;
|
|
2817
|
+
for (let n = 2; used.has(unique); n += 1) {
|
|
2818
|
+
unique = `${name} (${String(n)})`;
|
|
2819
|
+
}
|
|
2820
|
+
used.add(unique);
|
|
2821
|
+
return unique;
|
|
2822
|
+
});
|
|
2823
|
+
}
|
|
2824
|
+
function toJson(table, options = {}) {
|
|
2825
|
+
const keys = options.keys === "header" ? uniqueNames(table.columns.map((column) => column.header)) : table.columns.map((column) => column.id);
|
|
2826
|
+
const records = table.rows.flatMap(
|
|
2827
|
+
(row) => row.kind === "data" ? [
|
|
2828
|
+
Object.fromEntries(
|
|
2829
|
+
keys.map((key, index) => [key, row.cells[index]?.value ?? null])
|
|
2830
|
+
)
|
|
2831
|
+
] : []
|
|
2832
|
+
);
|
|
2833
|
+
return JSON.stringify(records, null, options.space);
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
// src/export/writer.ts
|
|
2837
|
+
var csvWriter = {
|
|
2838
|
+
format: "csv",
|
|
2839
|
+
fileExtension: "csv",
|
|
2840
|
+
mimeType: "text/csv;charset=utf-8",
|
|
2841
|
+
write: (table, { locale, options }) => toCsv(table, { locale, ...options.csv })
|
|
2842
|
+
};
|
|
2843
|
+
var jsonWriter = {
|
|
2844
|
+
format: "json",
|
|
2845
|
+
fileExtension: "json",
|
|
2846
|
+
mimeType: "application/json",
|
|
2847
|
+
write: (table, { options }) => toJson(table, options.json)
|
|
2848
|
+
};
|
|
2849
|
+
function resolveExportWriters(writers) {
|
|
2850
|
+
return new Map(
|
|
2851
|
+
[csvWriter, jsonWriter, ...writers ?? []].map((writer) => [
|
|
2852
|
+
writer.format,
|
|
2853
|
+
writer
|
|
2854
|
+
])
|
|
2855
|
+
);
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2858
|
+
// src/filterMenu/operators.ts
|
|
2859
|
+
function filterOperatorFamily(type) {
|
|
2860
|
+
return kindOf(type);
|
|
2861
|
+
}
|
|
2862
|
+
var familyOperators = {
|
|
2863
|
+
text: [
|
|
2864
|
+
"contains",
|
|
2865
|
+
"notContains",
|
|
2866
|
+
"eq",
|
|
2867
|
+
"ne",
|
|
2868
|
+
"startsWith",
|
|
2869
|
+
"endsWith",
|
|
2870
|
+
"isEmpty",
|
|
2871
|
+
"isNotEmpty"
|
|
2872
|
+
],
|
|
2873
|
+
number: [
|
|
2874
|
+
"eq",
|
|
2875
|
+
"ne",
|
|
2876
|
+
"gt",
|
|
2877
|
+
"gte",
|
|
2878
|
+
"lt",
|
|
2879
|
+
"lte",
|
|
2880
|
+
"between",
|
|
2881
|
+
"isEmpty",
|
|
2882
|
+
"isNotEmpty"
|
|
2883
|
+
],
|
|
2884
|
+
date: ["eq", "ne", "lt", "gt", "between", "isEmpty", "isNotEmpty"],
|
|
2885
|
+
boolean: ["eq", "isEmpty", "isNotEmpty"]
|
|
2886
|
+
};
|
|
2887
|
+
var instantOperators = [
|
|
2888
|
+
"lt",
|
|
2889
|
+
"gt",
|
|
2890
|
+
"between",
|
|
2891
|
+
"isEmpty",
|
|
2892
|
+
"isNotEmpty"
|
|
2893
|
+
];
|
|
2894
|
+
function defaultFilterOperators(type) {
|
|
2895
|
+
return type === "dateTime" || type === "time" ? instantOperators : familyOperators[filterOperatorFamily(type)];
|
|
2896
|
+
}
|
|
2897
|
+
function filterOperandArity(operator) {
|
|
2898
|
+
switch (operator) {
|
|
2899
|
+
case "isEmpty":
|
|
2900
|
+
case "isNotEmpty":
|
|
2901
|
+
return 0;
|
|
2902
|
+
case "between":
|
|
2903
|
+
return 2;
|
|
2904
|
+
case "eq":
|
|
2905
|
+
case "ne":
|
|
2906
|
+
case "contains":
|
|
2907
|
+
case "notContains":
|
|
2908
|
+
case "startsWith":
|
|
2909
|
+
case "endsWith":
|
|
2910
|
+
case "gt":
|
|
2911
|
+
case "gte":
|
|
2912
|
+
case "lt":
|
|
2913
|
+
case "lte":
|
|
2914
|
+
case "in":
|
|
2915
|
+
return 1;
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
var NUMBER_INPUT = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i;
|
|
2919
|
+
var DATE_INPUT = /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?)?$/;
|
|
2920
|
+
function parseNumber(text) {
|
|
2921
|
+
const value = Number(text);
|
|
2922
|
+
return NUMBER_INPUT.test(text) && Number.isFinite(value) ? value : void 0;
|
|
2923
|
+
}
|
|
2924
|
+
function parseDate(text) {
|
|
2925
|
+
const match = DATE_INPUT.exec(text);
|
|
2926
|
+
if (match === null) {
|
|
2927
|
+
return void 0;
|
|
2928
|
+
}
|
|
2929
|
+
const [
|
|
2930
|
+
,
|
|
2931
|
+
year,
|
|
2932
|
+
month,
|
|
2933
|
+
day,
|
|
2934
|
+
hours = "0",
|
|
2935
|
+
minutes = "0",
|
|
2936
|
+
seconds = "0",
|
|
2937
|
+
fraction = ""
|
|
2938
|
+
] = match;
|
|
2939
|
+
if (Number(hours) > 23 || Number(minutes) > 59 || Number(seconds) > 59) {
|
|
2940
|
+
return void 0;
|
|
2941
|
+
}
|
|
2942
|
+
const date = new Date(
|
|
2943
|
+
Number(year),
|
|
2944
|
+
Number(month) - 1,
|
|
2945
|
+
Number(day),
|
|
2946
|
+
Number(hours),
|
|
2947
|
+
Number(minutes),
|
|
2948
|
+
Number(seconds),
|
|
2949
|
+
Number(fraction.padEnd(3, "0"))
|
|
2950
|
+
);
|
|
2951
|
+
return date.getFullYear() === Number(year) && date.getMonth() === Number(month) - 1 && date.getDate() === Number(day) ? date : void 0;
|
|
2952
|
+
}
|
|
2953
|
+
function parseBoolean(text) {
|
|
2954
|
+
if (text === "true") {
|
|
2955
|
+
return true;
|
|
2956
|
+
}
|
|
2957
|
+
return text === "false" ? false : void 0;
|
|
2958
|
+
}
|
|
2959
|
+
function parseFilterOperand(text, type) {
|
|
2960
|
+
if (text === "") {
|
|
2961
|
+
return void 0;
|
|
2962
|
+
}
|
|
2963
|
+
switch (filterOperatorFamily(type)) {
|
|
2964
|
+
case "text":
|
|
2965
|
+
return text;
|
|
2966
|
+
case "number":
|
|
2967
|
+
return parseNumber(text.trim());
|
|
2968
|
+
case "date":
|
|
2969
|
+
return parseDate(text.trim());
|
|
2970
|
+
case "boolean":
|
|
2971
|
+
return parseBoolean(text.trim());
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
function pad(value, width = 2) {
|
|
2975
|
+
return String(value).padStart(width, "0");
|
|
2976
|
+
}
|
|
2977
|
+
function dayInput(date) {
|
|
2978
|
+
return `${pad(date.getFullYear(), 4)}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
2979
|
+
}
|
|
2980
|
+
function momentInput(date) {
|
|
2981
|
+
const minute = `${dayInput(date)}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
2982
|
+
const seconds = date.getSeconds();
|
|
2983
|
+
const milliseconds = date.getMilliseconds();
|
|
2984
|
+
if (seconds === 0 && milliseconds === 0) {
|
|
2985
|
+
return minute;
|
|
2986
|
+
}
|
|
2987
|
+
const second = `${minute}:${pad(seconds)}`;
|
|
2988
|
+
return milliseconds === 0 ? second : `${second}.${pad(milliseconds, 3)}`;
|
|
2989
|
+
}
|
|
2990
|
+
function formatFilterOperand(value, type) {
|
|
2991
|
+
if (value === null || value === void 0) {
|
|
2992
|
+
return "";
|
|
2993
|
+
}
|
|
2994
|
+
const date = filterOperatorFamily(type) === "date" ? toDate(value) : void 0;
|
|
2995
|
+
if (date === void 0) {
|
|
2996
|
+
return String(value);
|
|
2997
|
+
}
|
|
2998
|
+
return type === "date" ? dayInput(date) : momentInput(date);
|
|
2999
|
+
}
|
|
3000
|
+
|
|
3001
|
+
// src/filterMenu/options.ts
|
|
3002
|
+
var DEFAULT_FILTER_VALUE_LIMIT = 1e4;
|
|
3003
|
+
function conditionOperators(operators) {
|
|
3004
|
+
return operators.includes("in") ? operators.filter((operator) => operator !== "in") : operators;
|
|
3005
|
+
}
|
|
3006
|
+
function resolveColumnFilterMenu(options, column) {
|
|
3007
|
+
if (!(column.filterable ?? options.filterableColumns ?? false)) {
|
|
3008
|
+
return null;
|
|
3009
|
+
}
|
|
3010
|
+
const grid = options.filterMenu;
|
|
3011
|
+
const own = column.filterMenu;
|
|
3012
|
+
return {
|
|
3013
|
+
ui: own?.ui ?? grid?.ui ?? "simple",
|
|
3014
|
+
operators: conditionOperators(
|
|
3015
|
+
own?.operators ?? defaultFilterOperators(column.type)
|
|
3016
|
+
),
|
|
3017
|
+
family: filterOperatorFamily(column.type),
|
|
3018
|
+
showCounts: grid?.showCounts ?? false,
|
|
3019
|
+
valueLimit: grid?.valueLimit ?? DEFAULT_FILTER_VALUE_LIMIT
|
|
3020
|
+
};
|
|
3021
|
+
}
|
|
3022
|
+
|
|
3023
|
+
// src/messages.ts
|
|
3024
|
+
function localeKey2(locale) {
|
|
3025
|
+
return typeof locale === "string" ? locale : locale?.join(",") ?? "";
|
|
3026
|
+
}
|
|
3027
|
+
var countFormats = /* @__PURE__ */ new Map();
|
|
3028
|
+
function countFormat(locale) {
|
|
3029
|
+
const key = localeKey2(locale);
|
|
3030
|
+
let format = countFormats.get(key);
|
|
3031
|
+
if (format === void 0) {
|
|
3032
|
+
const numberFormat = new Intl.NumberFormat(locale, {
|
|
3033
|
+
maximumFractionDigits: 0
|
|
3034
|
+
});
|
|
3035
|
+
format = (count) => numberFormat.format(count);
|
|
3036
|
+
countFormats.set(key, format);
|
|
3037
|
+
}
|
|
3038
|
+
return format;
|
|
3039
|
+
}
|
|
3040
|
+
function columnCount(n, count, change) {
|
|
3041
|
+
return count === 1 ? `1 column ${change}` : `${n(count)} columns ${change}`;
|
|
3042
|
+
}
|
|
3043
|
+
function searchResults(n, count) {
|
|
3044
|
+
if (count === 0) {
|
|
3045
|
+
return "No rows found";
|
|
3046
|
+
}
|
|
3047
|
+
return count === 1 ? "1 row found" : `${n(count)} rows found`;
|
|
3048
|
+
}
|
|
3049
|
+
function selectedCount(n, count, target) {
|
|
3050
|
+
const noun = target === "rows" ? "row" : "column";
|
|
3051
|
+
if (count === 0) {
|
|
3052
|
+
return `No ${noun}s selected`;
|
|
3053
|
+
}
|
|
3054
|
+
return count === 1 ? `1 ${noun} selected` : `${n(count)} ${noun}s selected`;
|
|
3055
|
+
}
|
|
3056
|
+
var operatorLabels = {
|
|
3057
|
+
eq: "Equals",
|
|
3058
|
+
ne: "Does not equal",
|
|
3059
|
+
contains: "Contains",
|
|
3060
|
+
notContains: "Does not contain",
|
|
3061
|
+
startsWith: "Starts with",
|
|
3062
|
+
endsWith: "Ends with",
|
|
3063
|
+
gt: "Greater than",
|
|
3064
|
+
gte: "Greater than or equal to",
|
|
3065
|
+
lt: "Less than",
|
|
3066
|
+
lte: "Less than or equal to",
|
|
3067
|
+
between: "Between",
|
|
3068
|
+
in: "Is one of",
|
|
3069
|
+
isEmpty: "Is blank",
|
|
3070
|
+
isNotEmpty: "Is not blank"
|
|
3071
|
+
};
|
|
3072
|
+
var dateOperatorLabels = {
|
|
3073
|
+
lt: "Before",
|
|
3074
|
+
lte: "On or before",
|
|
3075
|
+
gt: "After",
|
|
3076
|
+
gte: "On or after"
|
|
3077
|
+
};
|
|
3078
|
+
function filterOperatorLabel(operator, family) {
|
|
3079
|
+
return (family === "date" ? dateOperatorLabels[operator] : void 0) ?? operatorLabels[operator];
|
|
3080
|
+
}
|
|
3081
|
+
function filterSearchFirst(n, total) {
|
|
3082
|
+
return total === 1 ? "Type to search 1 value" : `Type to search ${n(total)} values`;
|
|
3083
|
+
}
|
|
3084
|
+
function filterResults(n, shown, total) {
|
|
3085
|
+
return `${n(shown)} of ${n(total)} ${total === 1 ? "row" : "rows"}`;
|
|
3086
|
+
}
|
|
3087
|
+
var filterValueRequests = {
|
|
3088
|
+
text: "Enter a value",
|
|
3089
|
+
number: "Enter a number",
|
|
3090
|
+
date: "Enter a date",
|
|
3091
|
+
boolean: "Choose a value"
|
|
3092
|
+
};
|
|
3093
|
+
var filterOperatorMenus = {
|
|
3094
|
+
text: "Text filters",
|
|
3095
|
+
number: "Number filters",
|
|
3096
|
+
date: "Date filters",
|
|
3097
|
+
boolean: "Boolean filters"
|
|
3098
|
+
};
|
|
3099
|
+
function copied(n, count, unit) {
|
|
3100
|
+
return count === 1 ? `1 ${unit.slice(0, -1)} copied` : `${n(count)} ${unit} copied`;
|
|
3101
|
+
}
|
|
3102
|
+
function createDefaultMessages(locale) {
|
|
3103
|
+
const n = countFormat(locale);
|
|
3104
|
+
return {
|
|
3105
|
+
pagerLabel: "Pagination",
|
|
3106
|
+
firstPage: "First",
|
|
3107
|
+
previousPage: "Previous",
|
|
3108
|
+
nextPage: "Next",
|
|
3109
|
+
lastPage: "Last",
|
|
3110
|
+
pageSizeLabel: "Rows per page",
|
|
3111
|
+
pageButtonLabel: (page) => `Page ${n(page)}`,
|
|
3112
|
+
pageStatus: (page, pageCount) => `Page ${n(page)} of ${n(pageCount)}`,
|
|
3113
|
+
loadingMore: "Loading more\u2026",
|
|
3114
|
+
booleanTrue: "true",
|
|
3115
|
+
booleanFalse: "false",
|
|
3116
|
+
blankGroupValue: "(blank)",
|
|
3117
|
+
groupByBarLabel: "Grouped by",
|
|
3118
|
+
groupByBarPlaceholder: "Drag a column here to group by it",
|
|
3119
|
+
groupByChipLabel: (columnName2, level, levelCount) => levelCount > 1 ? `${columnName2}, level ${n(level)} of ${n(levelCount)}` : columnName2,
|
|
3120
|
+
removeGroupBy: (columnName2) => `Stop grouping by ${columnName2}`,
|
|
3121
|
+
columnResized: (columnName2, width) => `${columnName2}, ${n(width)} pixels wide`,
|
|
3122
|
+
columnMoved: (columnName2, position, columnCount2) => `${columnName2}, column ${n(position)} of ${n(columnCount2)}`,
|
|
3123
|
+
columnSorted: (columnName2, direction, key, keyCount) => {
|
|
3124
|
+
const sorted = `${columnName2} sorted ${direction === "asc" ? "ascending" : "descending"}`;
|
|
3125
|
+
return keyCount > 1 ? `${sorted}, key ${n(key)} of ${n(keyCount)}` : sorted;
|
|
3126
|
+
},
|
|
3127
|
+
sortCleared: (columnName2) => `${columnName2}, sort cleared`,
|
|
3128
|
+
groupedBy: (columnName2, level, levelCount) => levelCount > 1 ? `Grouped by ${columnName2}, level ${n(level)} of ${n(levelCount)}` : `Grouped by ${columnName2}`,
|
|
3129
|
+
groupingRemoved: (columnName2) => `${columnName2}, grouping removed`,
|
|
3130
|
+
groupExpanded: "Group expanded",
|
|
3131
|
+
groupCollapsed: "Group collapsed",
|
|
3132
|
+
allGroupsExpanded: "All groups expanded",
|
|
3133
|
+
allGroupsCollapsed: "All groups collapsed",
|
|
3134
|
+
selectedCount: (count, target) => selectedCount(n, count, target),
|
|
3135
|
+
cellSelected: (columnName2, rowNumber) => `${columnName2}, row ${n(rowNumber)}, selected`,
|
|
3136
|
+
columnHidden: (columnName2) => `${columnName2}, column hidden`,
|
|
3137
|
+
columnShown: (columnName2) => `${columnName2}, column shown`,
|
|
3138
|
+
columnsHidden: (count) => columnCount(n, count, "hidden"),
|
|
3139
|
+
columnsShown: (count) => columnCount(n, count, "shown"),
|
|
3140
|
+
columnsAutoFitted: (count) => columnCount(n, count, "autofitted"),
|
|
3141
|
+
headerMenuLabel: (columnName2) => `${columnName2} column menu`,
|
|
3142
|
+
cellMenuLabel: "Row menu",
|
|
3143
|
+
groupMenuLabel: "Group menu",
|
|
3144
|
+
bodyMenuLabel: "Grid menu",
|
|
3145
|
+
sortAscending: "Sort ascending",
|
|
3146
|
+
sortDescending: "Sort descending",
|
|
3147
|
+
clearSort: "Clear sort",
|
|
3148
|
+
clearAllSorting: "Clear all sorting",
|
|
3149
|
+
groupByColumn: (columnName2) => `Group by ${columnName2}`,
|
|
3150
|
+
ungroupAll: "Ungroup all",
|
|
3151
|
+
expandAllGroups: "Expand all groups",
|
|
3152
|
+
collapseAllGroups: "Collapse all groups",
|
|
3153
|
+
hideColumn: "Hide column",
|
|
3154
|
+
autoFitColumn: "Autofit column",
|
|
3155
|
+
autoFitAllColumns: "Autofit all columns",
|
|
3156
|
+
selectAllRows: "Select all rows",
|
|
3157
|
+
clearSelection: "Clear selection",
|
|
3158
|
+
clearFilters: "Clear filters",
|
|
3159
|
+
chooseColumns: "Choose columns",
|
|
3160
|
+
toolbarLabel: "Grid toolbar",
|
|
3161
|
+
searchLabel: "Search",
|
|
3162
|
+
searchPlaceholder: "Search\u2026",
|
|
3163
|
+
moreToolbarItems: "More",
|
|
3164
|
+
searchResults: (count) => searchResults(n, count),
|
|
3165
|
+
columnChooserLabel: "Columns",
|
|
3166
|
+
columnChooserSearchLabel: "Search columns",
|
|
3167
|
+
showAllColumns: "Show all",
|
|
3168
|
+
noMatchingColumns: "No matching columns",
|
|
3169
|
+
filterOperatorLabel,
|
|
3170
|
+
filterBlanks: "(Blanks)",
|
|
3171
|
+
filterSearchFirst: (total) => filterSearchFirst(n, total),
|
|
3172
|
+
filterMoreValues: (count) => `${n(count)} more, refine the search`,
|
|
3173
|
+
openFilter: "Filter\u2026",
|
|
3174
|
+
clearColumnFilter: "Clear filter",
|
|
3175
|
+
filterMenuLabel: (columnName2) => `${columnName2} filter`,
|
|
3176
|
+
columnFiltered: "Filtered",
|
|
3177
|
+
applyFilter: "OK",
|
|
3178
|
+
cancelFilter: "Cancel",
|
|
3179
|
+
customFilter: "Custom filter",
|
|
3180
|
+
filterResults: (shown, total) => filterResults(n, shown, total),
|
|
3181
|
+
filterConditionLabel: (position) => `Condition ${n(position)}`,
|
|
3182
|
+
filterOperatorFieldLabel: "Operator",
|
|
3183
|
+
filterValueLabel: "Value",
|
|
3184
|
+
filterFromLabel: "From",
|
|
3185
|
+
filterToLabel: "To",
|
|
3186
|
+
filterCombinatorLabel: "Combine conditions",
|
|
3187
|
+
filterAnd: "And",
|
|
3188
|
+
filterOr: "Or",
|
|
3189
|
+
filterValueInvalid: (family) => filterValueRequests[family],
|
|
3190
|
+
filterSearchLabel: "Search values",
|
|
3191
|
+
filterSelectAll: "Select all",
|
|
3192
|
+
filterValuesLabel: "Values",
|
|
3193
|
+
filterByCondition: "Filtered by condition",
|
|
3194
|
+
filterOperatorMenu: (family) => filterOperatorMenus[family],
|
|
3195
|
+
filterBackToValues: "Back to values",
|
|
3196
|
+
exportMenu: "Export",
|
|
3197
|
+
exportCsv: "Export to CSV",
|
|
3198
|
+
exportJson: "Export to JSON",
|
|
3199
|
+
exportXlsx: "Export to Excel",
|
|
3200
|
+
exportPdf: "Export to PDF",
|
|
3201
|
+
exportSelectionCsv: "Export selection to CSV",
|
|
3202
|
+
exportSelectionJson: "Export selection to JSON",
|
|
3203
|
+
exportSelectionXlsx: "Export selection to Excel",
|
|
3204
|
+
exportFailed: "Export failed",
|
|
3205
|
+
copyCell: "Copy cell",
|
|
3206
|
+
copyRow: "Copy row",
|
|
3207
|
+
copySelection: "Copy selection",
|
|
3208
|
+
copied: (count, unit) => copied(n, count, unit),
|
|
3209
|
+
copyFailed: "Copy failed",
|
|
3210
|
+
print: "Print"
|
|
3211
|
+
};
|
|
3212
|
+
}
|
|
3213
|
+
var defaultMessages = createDefaultMessages();
|
|
3214
|
+
var localeDefaults = /* @__PURE__ */ new Map();
|
|
3215
|
+
function defaultsFor(locale) {
|
|
3216
|
+
if (locale === void 0) {
|
|
3217
|
+
return defaultMessages;
|
|
3218
|
+
}
|
|
3219
|
+
const key = localeKey2(locale);
|
|
3220
|
+
let defaults = localeDefaults.get(key);
|
|
3221
|
+
if (defaults === void 0) {
|
|
3222
|
+
defaults = createDefaultMessages(locale);
|
|
3223
|
+
localeDefaults.set(key, defaults);
|
|
3224
|
+
}
|
|
3225
|
+
return defaults;
|
|
3226
|
+
}
|
|
3227
|
+
function resolveMessages(messages, locale) {
|
|
3228
|
+
const defaults = defaultsFor(locale);
|
|
3229
|
+
return messages === void 0 ? defaults : { ...defaults, ...messages };
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3232
|
+
// src/util/filterEditor.ts
|
|
3233
|
+
function touchesColumn(entry, columnId) {
|
|
3234
|
+
return "combinator" in entry ? entry.entries.some((nested) => touchesColumn(nested, columnId)) : entry.columnId === columnId;
|
|
3235
|
+
}
|
|
3236
|
+
function isOperatorOn(entry, columnId) {
|
|
3237
|
+
return "operator" in entry && entry.columnId === columnId;
|
|
3238
|
+
}
|
|
3239
|
+
function isConditionOn(entry, columnId) {
|
|
3240
|
+
return isOperatorOn(entry, columnId) && entry.operator !== "in";
|
|
3241
|
+
}
|
|
3242
|
+
function valuesShape(entry, columnId) {
|
|
3243
|
+
const values = (candidate) => candidate !== void 0 && isOperatorOn(candidate, columnId) && candidate.operator === "in" && isScalarList(candidate.value) ? candidate.value : void 0;
|
|
3244
|
+
const blanks = (candidate) => candidate !== void 0 && isOperatorOn(candidate, columnId) && candidate.operator === "isEmpty";
|
|
3245
|
+
const alone = values(entry);
|
|
3246
|
+
if (alone !== void 0) {
|
|
3247
|
+
return { kind: "values", values: alone, includeBlanks: false };
|
|
3248
|
+
}
|
|
3249
|
+
if (!("combinator" in entry) || entry.combinator !== "or" || entry.entries.length !== 2) {
|
|
3250
|
+
return void 0;
|
|
3251
|
+
}
|
|
3252
|
+
const [first, second] = entry.entries;
|
|
3253
|
+
const listed = blanks(first) ? values(second) : values(first);
|
|
3254
|
+
return listed !== void 0 && (blanks(first) || blanks(second)) ? { kind: "values", values: listed, includeBlanks: true } : void 0;
|
|
3255
|
+
}
|
|
3256
|
+
function conditionsShape(touching, columnId) {
|
|
3257
|
+
const [only] = touching;
|
|
3258
|
+
const grouped = touching.length === 1 && only !== void 0 && "combinator" in only;
|
|
3259
|
+
const entries = grouped ? only.entries : touching;
|
|
3260
|
+
const combinator = grouped ? only.combinator : "and";
|
|
3261
|
+
if (entries.length === 0 || entries.length > 2) {
|
|
3262
|
+
return void 0;
|
|
3263
|
+
}
|
|
3264
|
+
const conditions = entries.filter((entry) => isConditionOn(entry, columnId));
|
|
3265
|
+
return conditions.length === entries.length ? { kind: "conditions", conditions, combinator } : void 0;
|
|
3266
|
+
}
|
|
3267
|
+
function describeColumnFilter(filter, columnId) {
|
|
3268
|
+
const touching = filter.filter((entry) => touchesColumn(entry, columnId));
|
|
3269
|
+
const [only] = touching;
|
|
3270
|
+
if (only === void 0) {
|
|
3271
|
+
return { kind: "none" };
|
|
3272
|
+
}
|
|
3273
|
+
return (touching.length === 1 ? valuesShape(only, columnId) : void 0) ?? conditionsShape(touching, columnId) ?? { kind: "custom" };
|
|
3274
|
+
}
|
|
3275
|
+
function isColumnFiltered(filter, columnId) {
|
|
3276
|
+
return filter.some((entry) => touchesColumn(entry, columnId));
|
|
3277
|
+
}
|
|
3278
|
+
function isOfferable(value) {
|
|
3279
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value instanceof Date;
|
|
3280
|
+
}
|
|
3281
|
+
function matchesSearch(label, search) {
|
|
3282
|
+
return label.toLowerCase().includes(search.toLowerCase());
|
|
3283
|
+
}
|
|
3284
|
+
function columnValueOptions(rows, dataColumns, filter, columnId, query = {}) {
|
|
3285
|
+
const column = dataColumns.find((entry) => entry.id === columnId)?.column;
|
|
3286
|
+
if (column === void 0) {
|
|
3287
|
+
return { options: [], total: 0, truncated: false, blanks: 0 };
|
|
3288
|
+
}
|
|
3289
|
+
const keep = compileFilter(
|
|
3290
|
+
filter.filter((entry) => !touchesColumn(entry, columnId)),
|
|
3291
|
+
dataColumns
|
|
3292
|
+
);
|
|
3293
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
3294
|
+
let blanks = 0;
|
|
3295
|
+
for (const row of rows) {
|
|
3296
|
+
if (keep !== void 0 && !keep(row)) {
|
|
3297
|
+
continue;
|
|
3298
|
+
}
|
|
3299
|
+
const value = accessDotted(row, column.field);
|
|
3300
|
+
if (isEmptyCell(value)) {
|
|
3301
|
+
blanks++;
|
|
3302
|
+
} else if (isOfferable(value)) {
|
|
3303
|
+
const key = filterValueKey(value, column.type);
|
|
3304
|
+
const bucket = buckets.get(key);
|
|
3305
|
+
if (bucket === void 0) {
|
|
3306
|
+
buckets.set(key, { value, row, count: 1 });
|
|
3307
|
+
} else {
|
|
3308
|
+
bucket.count++;
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
const { search = "", limit, locale } = query;
|
|
3313
|
+
const messages = resolveMessages(query.messages, locale);
|
|
3314
|
+
const options = [...buckets.values()].sort((a, b) => compareValues(a.value, b.value, column.type)).map(({ value, row, count }) => ({
|
|
3315
|
+
value,
|
|
3316
|
+
label: formatCellValue(value, column, locale, row, messages),
|
|
3317
|
+
count
|
|
3318
|
+
})).filter((option) => matchesSearch(option.label, search));
|
|
3319
|
+
const shown = limit === void 0 ? options : options.slice(0, limit);
|
|
3320
|
+
return {
|
|
3321
|
+
options: shown,
|
|
3322
|
+
total: options.length,
|
|
3323
|
+
truncated: shown.length < options.length,
|
|
3324
|
+
blanks,
|
|
3325
|
+
columnType: column.type
|
|
3326
|
+
};
|
|
3327
|
+
}
|
|
3328
|
+
function keyOf2(draft, value) {
|
|
3329
|
+
return filterValueKey(value, draft.columnType);
|
|
3330
|
+
}
|
|
3331
|
+
function everyOption(draft) {
|
|
3332
|
+
return new Map(
|
|
3333
|
+
draft.options.map((option) => [keyOf2(draft, option.value), option.value])
|
|
3334
|
+
);
|
|
3335
|
+
}
|
|
3336
|
+
function initialFilterEditorDraft(shape, values) {
|
|
3337
|
+
const draft = {
|
|
3338
|
+
mode: "values",
|
|
3339
|
+
options: values.options,
|
|
3340
|
+
blanks: values.blanks,
|
|
3341
|
+
search: "",
|
|
3342
|
+
checked: everyOption(values),
|
|
3343
|
+
includeBlanks: values.blanks > 0,
|
|
3344
|
+
conditions: [],
|
|
3345
|
+
combinator: "and",
|
|
3346
|
+
columnType: values.columnType
|
|
3347
|
+
};
|
|
3348
|
+
switch (shape.kind) {
|
|
3349
|
+
case "values":
|
|
3350
|
+
return {
|
|
3351
|
+
...draft,
|
|
3352
|
+
checked: new Map(
|
|
3353
|
+
shape.values.filter((value) => !isEmptyCell(value)).map((value) => [keyOf2(draft, value), value])
|
|
3354
|
+
),
|
|
3355
|
+
includeBlanks: shape.includeBlanks
|
|
3356
|
+
};
|
|
3357
|
+
case "conditions":
|
|
3358
|
+
return {
|
|
3359
|
+
...draft,
|
|
3360
|
+
mode: "conditions",
|
|
3361
|
+
conditions: shape.conditions,
|
|
3362
|
+
combinator: shape.combinator
|
|
3363
|
+
};
|
|
3364
|
+
case "none":
|
|
3365
|
+
case "custom":
|
|
3366
|
+
return draft;
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
function visibleFilterOptions(draft) {
|
|
3370
|
+
return draft.search === "" ? draft.options : draft.options.filter(
|
|
3371
|
+
(option) => matchesSearch(option.label, draft.search)
|
|
3372
|
+
);
|
|
3373
|
+
}
|
|
3374
|
+
function selectAllCoversBlanks(draft) {
|
|
3375
|
+
return draft.search === "" && draft.blanks > 0;
|
|
3376
|
+
}
|
|
3377
|
+
function selectAllState(draft) {
|
|
3378
|
+
const states = visibleFilterOptions(draft).map(
|
|
3379
|
+
(option) => draft.checked.has(keyOf2(draft, option.value))
|
|
3380
|
+
);
|
|
3381
|
+
if (selectAllCoversBlanks(draft)) {
|
|
3382
|
+
states.push(draft.includeBlanks);
|
|
3383
|
+
}
|
|
3384
|
+
if (states.length > 0 && states.every(Boolean)) {
|
|
3385
|
+
return "checked";
|
|
3386
|
+
}
|
|
3387
|
+
return states.some(Boolean) ? "mixed" : "unchecked";
|
|
3388
|
+
}
|
|
3389
|
+
function toggleAll(draft) {
|
|
3390
|
+
const check = selectAllState(draft) !== "checked";
|
|
3391
|
+
const checked = new Map(draft.checked);
|
|
3392
|
+
for (const option of visibleFilterOptions(draft)) {
|
|
3393
|
+
const key = keyOf2(draft, option.value);
|
|
3394
|
+
if (check) {
|
|
3395
|
+
checked.set(key, option.value);
|
|
3396
|
+
} else {
|
|
3397
|
+
checked.delete(key);
|
|
3398
|
+
}
|
|
3399
|
+
}
|
|
3400
|
+
return {
|
|
3401
|
+
...draft,
|
|
3402
|
+
mode: "values",
|
|
3403
|
+
checked,
|
|
3404
|
+
includeBlanks: selectAllCoversBlanks(draft) ? check : draft.includeBlanks
|
|
3405
|
+
};
|
|
3406
|
+
}
|
|
3407
|
+
function filterEditorReducer(draft, action) {
|
|
3408
|
+
switch (action.type) {
|
|
3409
|
+
case "search":
|
|
3410
|
+
return { ...draft, search: action.search };
|
|
3411
|
+
case "toggleValue": {
|
|
3412
|
+
const key = keyOf2(draft, action.value);
|
|
3413
|
+
const checked = new Map(draft.checked);
|
|
3414
|
+
if (checked.has(key)) {
|
|
3415
|
+
checked.delete(key);
|
|
3416
|
+
} else {
|
|
3417
|
+
checked.set(key, action.value);
|
|
3418
|
+
}
|
|
3419
|
+
return { ...draft, mode: "values", checked };
|
|
3420
|
+
}
|
|
3421
|
+
case "toggleAll":
|
|
3422
|
+
return toggleAll(draft);
|
|
3423
|
+
case "setIncludeBlanks":
|
|
3424
|
+
return { ...draft, mode: "values", includeBlanks: action.includeBlanks };
|
|
3425
|
+
case "setConditions":
|
|
3426
|
+
return {
|
|
3427
|
+
...draft,
|
|
3428
|
+
mode: "conditions",
|
|
3429
|
+
conditions: action.conditions,
|
|
3430
|
+
combinator: action.combinator ?? draft.combinator
|
|
3431
|
+
};
|
|
3432
|
+
case "clear":
|
|
3433
|
+
return {
|
|
3434
|
+
...draft,
|
|
3435
|
+
search: "",
|
|
3436
|
+
checked: everyOption(draft),
|
|
3437
|
+
includeBlanks: draft.blanks > 0,
|
|
3438
|
+
conditions: []
|
|
3439
|
+
};
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
function checksEverything(draft) {
|
|
3443
|
+
return draft.options.every(
|
|
3444
|
+
(option) => draft.checked.has(keyOf2(draft, option.value))
|
|
3445
|
+
) && (draft.includeBlanks || draft.blanks === 0);
|
|
3446
|
+
}
|
|
3447
|
+
function draftEntries(draft, columnId) {
|
|
3448
|
+
if (draft.mode === "conditions") {
|
|
3449
|
+
const conditions = draft.conditions.map((condition) => ({
|
|
3450
|
+
...condition,
|
|
3451
|
+
columnId
|
|
3452
|
+
}));
|
|
3453
|
+
return conditions.length > 1 ? [{ combinator: draft.combinator, entries: conditions }] : conditions;
|
|
3454
|
+
}
|
|
3455
|
+
if (checksEverything(draft)) {
|
|
3456
|
+
return [];
|
|
3457
|
+
}
|
|
3458
|
+
const picked = {
|
|
3459
|
+
columnId,
|
|
3460
|
+
operator: "in",
|
|
3461
|
+
value: [...draft.checked.values()]
|
|
3462
|
+
};
|
|
3463
|
+
return draft.includeBlanks ? [
|
|
3464
|
+
{
|
|
3465
|
+
combinator: "or",
|
|
3466
|
+
entries: [picked, { columnId, operator: "isEmpty" }]
|
|
3467
|
+
}
|
|
3468
|
+
] : [picked];
|
|
3469
|
+
}
|
|
3470
|
+
function draftToFilter(draft, columnId, current) {
|
|
3471
|
+
const replacement = draftEntries(draft, columnId);
|
|
3472
|
+
const first = current.findIndex((entry) => touchesColumn(entry, columnId));
|
|
3473
|
+
if (first === -1) {
|
|
3474
|
+
return replacement.length === 0 ? current : [...current, ...replacement];
|
|
3475
|
+
}
|
|
3476
|
+
const kept = current.filter((entry) => !touchesColumn(entry, columnId));
|
|
3477
|
+
return [...kept.slice(0, first), ...replacement, ...kept.slice(first)];
|
|
3478
|
+
}
|
|
3479
|
+
|
|
3480
|
+
// src/commands/builtInCommands.ts
|
|
3481
|
+
function isOn(context, areas) {
|
|
3482
|
+
return areas.includes(context.target.area);
|
|
3483
|
+
}
|
|
3484
|
+
function columnOn(context, areas) {
|
|
3485
|
+
return isOn(context, areas) ? context.column : null;
|
|
3486
|
+
}
|
|
3487
|
+
function isGroupedBy(context, columnId) {
|
|
3488
|
+
return context.state.groupBy.some((entry) => entry.columnId === columnId);
|
|
3489
|
+
}
|
|
3490
|
+
function sortableColumn(context) {
|
|
3491
|
+
const column = columnOn(context, ["header"]);
|
|
3492
|
+
return column !== null && (column.column.sortable ?? context.options.sortableColumns ?? false) ? column : null;
|
|
3493
|
+
}
|
|
3494
|
+
function groupableColumn(context) {
|
|
3495
|
+
const column = columnOn(context, ["header"]);
|
|
3496
|
+
return column !== null && (column.column.groupable ?? context.options.groupableColumns ?? false) && !isGroupedBy(context, column.id) ? column : null;
|
|
3497
|
+
}
|
|
3498
|
+
function groupedColumn(context) {
|
|
3499
|
+
const column = columnOn(context, ["header", "group"]);
|
|
3500
|
+
return column !== null && isGroupedBy(context, column.id) ? column : null;
|
|
3501
|
+
}
|
|
3502
|
+
function filterableColumn(context) {
|
|
3503
|
+
const column = columnOn(context, ["header"]);
|
|
3504
|
+
return column !== null && resolveColumnFilterMenu(context.options, column.column) !== null ? column : null;
|
|
3505
|
+
}
|
|
3506
|
+
function hideableColumn(context) {
|
|
3507
|
+
const column = columnOn(context, ["header"]);
|
|
3508
|
+
return column !== null && column.column.hideable !== false ? column : null;
|
|
3509
|
+
}
|
|
3510
|
+
function resizableColumn(context) {
|
|
3511
|
+
const column = columnOn(context, ["header"]);
|
|
3512
|
+
if (column === null) {
|
|
3513
|
+
return null;
|
|
3514
|
+
}
|
|
3515
|
+
return context.view.columns.find(
|
|
3516
|
+
(entry) => entry.id === column.id && entry.resizable
|
|
3517
|
+
) ?? null;
|
|
3518
|
+
}
|
|
3519
|
+
function resizableColumnIds(context) {
|
|
3520
|
+
return context.view.columns.filter((entry) => entry.resizable).map((entry) => entry.id);
|
|
3521
|
+
}
|
|
3522
|
+
function isAnythingSelectable(selectable) {
|
|
3523
|
+
return [selectable?.rows, selectable?.columns, selectable?.cells].some(
|
|
3524
|
+
(mode) => mode !== void 0 && mode !== false
|
|
3525
|
+
);
|
|
3526
|
+
}
|
|
3527
|
+
function columnName(context) {
|
|
3528
|
+
return context.column === null ? "" : resolveColumnHeaderText(context.column.column);
|
|
3529
|
+
}
|
|
3530
|
+
function columnCommand({
|
|
3531
|
+
id,
|
|
3532
|
+
label,
|
|
3533
|
+
icon,
|
|
3534
|
+
find,
|
|
3535
|
+
isEnabled,
|
|
3536
|
+
isChecked,
|
|
3537
|
+
execute
|
|
3538
|
+
}) {
|
|
3539
|
+
function test(predicate) {
|
|
3540
|
+
return (context) => {
|
|
3541
|
+
const column = find(context);
|
|
3542
|
+
return column !== null && predicate(column, context);
|
|
3543
|
+
};
|
|
3544
|
+
}
|
|
3545
|
+
return {
|
|
3546
|
+
id,
|
|
3547
|
+
label,
|
|
3548
|
+
icon,
|
|
3549
|
+
isVisible: (context) => find(context) !== null,
|
|
3550
|
+
isEnabled: isEnabled === void 0 ? void 0 : test(isEnabled),
|
|
3551
|
+
isChecked: isChecked === void 0 ? void 0 : test(isChecked),
|
|
3552
|
+
execute: (context) => {
|
|
3553
|
+
const column = find(context);
|
|
3554
|
+
if (column !== null) {
|
|
3555
|
+
execute(column, context);
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
};
|
|
3559
|
+
}
|
|
3560
|
+
var EXPORT_AREAS = ["grid", "toolbar", "body", "cell"];
|
|
3561
|
+
function exportCommand({
|
|
3562
|
+
id,
|
|
3563
|
+
format,
|
|
3564
|
+
label,
|
|
3565
|
+
selection = false
|
|
3566
|
+
}) {
|
|
3567
|
+
return {
|
|
3568
|
+
id,
|
|
3569
|
+
label: ({ messages }) => label(messages),
|
|
3570
|
+
isVisible: (context) => isOn(context, EXPORT_AREAS) && resolveExportWriters(context.options.exportWriters).has(format) && (!selection || context.state.rowSelection.length > 0),
|
|
3571
|
+
isEnabled: (context) => (selection ? context.selectedRows() : context.view.rows).length > 0,
|
|
3572
|
+
execute: (context) => {
|
|
3573
|
+
const { export: defaults = {}, exportWriters } = context.options;
|
|
3574
|
+
const file = exportFile(
|
|
3575
|
+
context,
|
|
3576
|
+
format,
|
|
3577
|
+
selection ? { ...defaults, scope: "selection" } : defaults,
|
|
3578
|
+
resolveExportWriters(exportWriters)
|
|
3579
|
+
);
|
|
3580
|
+
if (file !== null) {
|
|
3581
|
+
context.host.saveFile(file);
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
};
|
|
3585
|
+
}
|
|
3586
|
+
function cellRequest(context, kind) {
|
|
3587
|
+
const { target } = context;
|
|
3588
|
+
if (target.area !== "cell") {
|
|
3589
|
+
return null;
|
|
3590
|
+
}
|
|
3591
|
+
return kind === "cell" ? { kind, rowId: target.rowId, columnId: target.columnId } : { kind, rowId: target.rowId };
|
|
3592
|
+
}
|
|
3593
|
+
function copyCommand({
|
|
3594
|
+
id,
|
|
3595
|
+
label,
|
|
3596
|
+
request,
|
|
3597
|
+
isEnabled
|
|
3598
|
+
}) {
|
|
3599
|
+
function contentFor(context) {
|
|
3600
|
+
const asked = request(context);
|
|
3601
|
+
return asked === null ? null : copyContent(context, asked);
|
|
3602
|
+
}
|
|
3603
|
+
return {
|
|
3604
|
+
id,
|
|
3605
|
+
label: ({ messages }) => label(messages),
|
|
3606
|
+
icon: "copy",
|
|
3607
|
+
isVisible: (context) => request(context) !== null,
|
|
3608
|
+
isEnabled: isEnabled ?? ((context) => contentFor(context) !== null),
|
|
3609
|
+
execute: (context) => {
|
|
3610
|
+
const content = contentFor(context);
|
|
3611
|
+
if (content !== null) {
|
|
3612
|
+
context.host.writeClipboard(content);
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
};
|
|
3616
|
+
}
|
|
3617
|
+
function sortCommand(direction) {
|
|
3618
|
+
return columnCommand({
|
|
3619
|
+
id: direction === "asc" ? "sort.ascending" : "sort.descending",
|
|
3620
|
+
label: ({ messages }) => direction === "asc" ? messages.sortAscending : messages.sortDescending,
|
|
3621
|
+
icon: direction === "asc" ? "sortAscending" : "sortDescending",
|
|
3622
|
+
find: sortableColumn,
|
|
3623
|
+
isChecked: (column, { state }) => sortDirectionFor(state.columnSort, column.id) === direction,
|
|
3624
|
+
execute: (column, { actions }) => {
|
|
3625
|
+
actions.setColumnSort([{ columnId: column.id, direction }]);
|
|
3626
|
+
}
|
|
3627
|
+
});
|
|
3628
|
+
}
|
|
3629
|
+
function builtInCommands() {
|
|
3630
|
+
return [
|
|
3631
|
+
sortCommand("asc"),
|
|
3632
|
+
sortCommand("desc"),
|
|
3633
|
+
// Cleared rather than set, so the change reports this column — the one
|
|
3634
|
+
// the announcement names — rather than a key that moved up past it.
|
|
3635
|
+
columnCommand({
|
|
3636
|
+
id: "sort.clear",
|
|
3637
|
+
label: ({ messages }) => messages.clearSort,
|
|
3638
|
+
find: sortableColumn,
|
|
3639
|
+
isEnabled: (column, { state }) => sortDirectionFor(state.columnSort, column.id) !== null,
|
|
3640
|
+
execute: (column, { actions }) => {
|
|
3641
|
+
actions.clearColumnSort(column.id);
|
|
3642
|
+
}
|
|
3643
|
+
}),
|
|
3644
|
+
{
|
|
3645
|
+
id: "sort.clearAll",
|
|
3646
|
+
label: ({ messages }) => messages.clearAllSorting,
|
|
3647
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "header"]),
|
|
3648
|
+
isEnabled: ({ state }) => state.columnSort.length > 0,
|
|
3649
|
+
execute: ({ actions }) => {
|
|
3650
|
+
actions.setColumnSort([]);
|
|
3651
|
+
}
|
|
3652
|
+
},
|
|
3653
|
+
columnCommand({
|
|
3654
|
+
id: "group.add",
|
|
3655
|
+
label: (context) => context.messages.groupByColumn(columnName(context)),
|
|
3656
|
+
icon: "group",
|
|
3657
|
+
find: groupableColumn,
|
|
3658
|
+
execute: (column, { actions }) => {
|
|
3659
|
+
actions.toggleGroupBy(column.id);
|
|
3660
|
+
}
|
|
3661
|
+
}),
|
|
3662
|
+
// Toggled rather than set, so the change reports this column — the one
|
|
3663
|
+
// the announcement names — rather than the level that moved up.
|
|
3664
|
+
columnCommand({
|
|
3665
|
+
id: "group.remove",
|
|
3666
|
+
label: (context) => context.messages.removeGroupBy(columnName(context)),
|
|
3667
|
+
icon: "group",
|
|
3668
|
+
find: groupedColumn,
|
|
3669
|
+
execute: (column, { actions }) => {
|
|
3670
|
+
actions.toggleGroupBy(column.id);
|
|
3671
|
+
}
|
|
3672
|
+
}),
|
|
3673
|
+
{
|
|
3674
|
+
id: "group.clearAll",
|
|
3675
|
+
label: ({ messages }) => messages.ungroupAll,
|
|
3676
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar"]) && context.state.groupBy.length > 0,
|
|
3677
|
+
execute: ({ actions }) => {
|
|
3678
|
+
actions.setGroupBy([]);
|
|
3679
|
+
}
|
|
3680
|
+
},
|
|
3681
|
+
{
|
|
3682
|
+
id: "group.expandAll",
|
|
3683
|
+
label: ({ messages }) => messages.expandAllGroups,
|
|
3684
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "group", "cell"]) && context.state.groupBy.length > 0,
|
|
3685
|
+
execute: ({ actions }) => {
|
|
3686
|
+
actions.expandAllGroups();
|
|
3687
|
+
}
|
|
3688
|
+
},
|
|
3689
|
+
{
|
|
3690
|
+
id: "group.collapseAll",
|
|
3691
|
+
label: ({ messages }) => messages.collapseAllGroups,
|
|
3692
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "group", "cell"]) && context.state.groupBy.length > 0,
|
|
3693
|
+
execute: ({ actions }) => {
|
|
3694
|
+
actions.collapseAllGroups();
|
|
3695
|
+
}
|
|
3696
|
+
},
|
|
3697
|
+
columnCommand({
|
|
3698
|
+
id: "column.hide",
|
|
3699
|
+
label: ({ messages }) => messages.hideColumn,
|
|
3700
|
+
icon: "hide",
|
|
3701
|
+
find: hideableColumn,
|
|
3702
|
+
// The last column shown stays: a grid with none has no header left to
|
|
3703
|
+
// bring one back from.
|
|
3704
|
+
isEnabled: (column, { view }) => view.columns.some((entry) => entry.id !== column.id),
|
|
3705
|
+
execute: (column, { actions }) => {
|
|
3706
|
+
actions.hideColumns([column.id]);
|
|
3707
|
+
}
|
|
3708
|
+
}),
|
|
3709
|
+
columnCommand({
|
|
3710
|
+
id: "column.autoFit",
|
|
3711
|
+
label: ({ messages }) => messages.autoFitColumn,
|
|
3712
|
+
icon: "autoFit",
|
|
3713
|
+
find: resizableColumn,
|
|
3714
|
+
execute: (column, { host }) => {
|
|
3715
|
+
host.autoFitColumns([column.id]);
|
|
3716
|
+
}
|
|
3717
|
+
}),
|
|
3718
|
+
{
|
|
3719
|
+
id: "column.autoFitAll",
|
|
3720
|
+
label: ({ messages }) => messages.autoFitAllColumns,
|
|
3721
|
+
icon: "autoFit",
|
|
3722
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "header"]) && resizableColumnIds(context).length > 0,
|
|
3723
|
+
execute: (context) => {
|
|
3724
|
+
context.host.autoFitColumns(resizableColumnIds(context));
|
|
3725
|
+
}
|
|
3726
|
+
},
|
|
3727
|
+
// The target goes along, so the renderer opens the chooser where it was
|
|
3728
|
+
// asked for: beside the toolbar button, below the header, or at the
|
|
3729
|
+
// grid's corner for a call from nowhere in particular.
|
|
3730
|
+
{
|
|
3731
|
+
id: "column.choose",
|
|
3732
|
+
label: ({ messages }) => messages.chooseColumns,
|
|
3733
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "header"]),
|
|
3734
|
+
execute: ({ host, target }) => {
|
|
3735
|
+
host.openPanel("columnChooser", target);
|
|
3736
|
+
}
|
|
3737
|
+
},
|
|
3738
|
+
// Opened below the header it was asked for, as the chooser is.
|
|
3739
|
+
columnCommand({
|
|
3740
|
+
id: "filter.open",
|
|
3741
|
+
label: ({ messages }) => messages.openFilter,
|
|
3742
|
+
find: filterableColumn,
|
|
3743
|
+
execute: (_column, { host, target }) => {
|
|
3744
|
+
host.openPanel("filter", target);
|
|
3745
|
+
}
|
|
3746
|
+
}),
|
|
3747
|
+
// Every entry about the column goes, whichever editor wrote it — or
|
|
3748
|
+
// none did, as with a predicate set in code — as applying an editor
|
|
3749
|
+
// replaces them all.
|
|
3750
|
+
columnCommand({
|
|
3751
|
+
id: "filter.clear",
|
|
3752
|
+
label: ({ messages }) => messages.clearColumnFilter,
|
|
3753
|
+
find: filterableColumn,
|
|
3754
|
+
isEnabled: (column, { state }) => isColumnFiltered(state.filter, column.id),
|
|
3755
|
+
execute: (column, { actions, state }) => {
|
|
3756
|
+
actions.setFilter(
|
|
3757
|
+
state.filter.filter((entry) => !touchesColumn(entry, column.id))
|
|
3758
|
+
);
|
|
3759
|
+
}
|
|
3760
|
+
}),
|
|
3761
|
+
{
|
|
3762
|
+
id: "selection.selectAllRows",
|
|
3763
|
+
label: ({ messages }) => messages.selectAllRows,
|
|
3764
|
+
isVisible: (context) => isOn(context, ["grid", "cell"]) && context.options.selectable?.rows === "multiple",
|
|
3765
|
+
isEnabled: ({ view }) => view.displayDataRows.length > 0,
|
|
3766
|
+
execute: ({ actions }) => {
|
|
3767
|
+
actions.selectAllRows();
|
|
3768
|
+
}
|
|
3769
|
+
},
|
|
3770
|
+
{
|
|
3771
|
+
id: "selection.clear",
|
|
3772
|
+
label: ({ messages }) => messages.clearSelection,
|
|
3773
|
+
isVisible: (context) => isOn(context, ["grid", "cell", "header"]) && isAnythingSelectable(context.options.selectable),
|
|
3774
|
+
isEnabled: ({ state }) => state.rowSelection.length > 0 || state.columnSelection.length > 0 || state.cellSelection !== null,
|
|
3775
|
+
execute: ({ actions }) => {
|
|
3776
|
+
actions.clearSelection();
|
|
3777
|
+
}
|
|
3778
|
+
},
|
|
3779
|
+
{
|
|
3780
|
+
id: "filter.clearAll",
|
|
3781
|
+
label: ({ messages }) => messages.clearFilters,
|
|
3782
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "header"]),
|
|
3783
|
+
isEnabled: ({ state }) => state.filter.length > 0,
|
|
3784
|
+
execute: ({ actions }) => {
|
|
3785
|
+
actions.setFilter([]);
|
|
3786
|
+
}
|
|
3787
|
+
},
|
|
3788
|
+
exportCommand({
|
|
3789
|
+
id: "export.csv",
|
|
3790
|
+
format: "csv",
|
|
3791
|
+
label: ({ exportCsv }) => exportCsv
|
|
3792
|
+
}),
|
|
3793
|
+
exportCommand({
|
|
3794
|
+
id: "export.json",
|
|
3795
|
+
format: "json",
|
|
3796
|
+
label: ({ exportJson }) => exportJson
|
|
3797
|
+
}),
|
|
3798
|
+
exportCommand({
|
|
3799
|
+
id: "export.xlsx",
|
|
3800
|
+
format: "xlsx",
|
|
3801
|
+
label: ({ exportXlsx }) => exportXlsx
|
|
3802
|
+
}),
|
|
3803
|
+
exportCommand({
|
|
3804
|
+
id: "export.pdf",
|
|
3805
|
+
format: "pdf",
|
|
3806
|
+
label: ({ exportPdf }) => exportPdf
|
|
3807
|
+
}),
|
|
3808
|
+
exportCommand({
|
|
3809
|
+
id: "exportSelection.csv",
|
|
3810
|
+
format: "csv",
|
|
3811
|
+
label: ({ exportSelectionCsv }) => exportSelectionCsv,
|
|
3812
|
+
selection: true
|
|
3813
|
+
}),
|
|
3814
|
+
exportCommand({
|
|
3815
|
+
id: "exportSelection.json",
|
|
3816
|
+
format: "json",
|
|
3817
|
+
label: ({ exportSelectionJson }) => exportSelectionJson,
|
|
3818
|
+
selection: true
|
|
3819
|
+
}),
|
|
3820
|
+
exportCommand({
|
|
3821
|
+
id: "exportSelection.xlsx",
|
|
3822
|
+
format: "xlsx",
|
|
3823
|
+
label: ({ exportSelectionXlsx }) => exportSelectionXlsx,
|
|
3824
|
+
selection: true
|
|
3825
|
+
}),
|
|
3826
|
+
copyCommand({
|
|
3827
|
+
id: "copy.cell",
|
|
3828
|
+
label: ({ copyCell }) => copyCell,
|
|
3829
|
+
request: (context) => cellRequest(context, "cell")
|
|
3830
|
+
}),
|
|
3831
|
+
copyCommand({
|
|
3832
|
+
id: "copy.row",
|
|
3833
|
+
label: ({ copyRow }) => copyRow,
|
|
3834
|
+
request: (context) => cellRequest(context, "row")
|
|
3835
|
+
}),
|
|
3836
|
+
// Shown wherever the grid as a whole is asked for, as the exports are,
|
|
3837
|
+
// once anything can be selected; enabled once something is.
|
|
3838
|
+
copyCommand({
|
|
3839
|
+
id: "copy.selection",
|
|
3840
|
+
label: ({ copySelection }) => copySelection,
|
|
3841
|
+
request: (context) => isOn(context, EXPORT_AREAS) && isAnythingSelectable(context.options.selectable) ? { kind: "selection" } : null,
|
|
3842
|
+
isEnabled: canCopySelection
|
|
3843
|
+
}),
|
|
3844
|
+
// Shown where the exports are, under the grid's `print` defaults. A grid
|
|
3845
|
+
// with no rows still prints its header; one with no columns, nothing.
|
|
3846
|
+
{
|
|
3847
|
+
id: "print",
|
|
3848
|
+
label: ({ messages }) => messages.print,
|
|
3849
|
+
icon: "print",
|
|
3850
|
+
isVisible: (context) => isOn(context, EXPORT_AREAS),
|
|
3851
|
+
isEnabled: ({ view }) => view.columns.length > 0,
|
|
3852
|
+
execute: (context) => {
|
|
3853
|
+
const { print: options = {} } = context.options;
|
|
3854
|
+
context.host.print(exportPrintDocument(context, options), options);
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
];
|
|
3858
|
+
}
|
|
3859
|
+
|
|
3860
|
+
// src/commands/registry.ts
|
|
3861
|
+
function isCommandVisible(command, context) {
|
|
3862
|
+
return command.isVisible?.(context) ?? true;
|
|
3863
|
+
}
|
|
3864
|
+
function isCommandEnabled(command, context) {
|
|
3865
|
+
return command.isEnabled?.(context) ?? true;
|
|
3866
|
+
}
|
|
3867
|
+
function resolveCommands(commands) {
|
|
3868
|
+
const registry = new Map(
|
|
3869
|
+
builtInCommands().map((command) => [command.id, command])
|
|
3870
|
+
);
|
|
3871
|
+
for (const command of commands ?? []) {
|
|
3872
|
+
if (command.id === "separator") {
|
|
3873
|
+
reportError(
|
|
3874
|
+
"a command cannot have the id `separator`, which a menu reads as a separator, so it is left out."
|
|
3875
|
+
);
|
|
3876
|
+
continue;
|
|
3877
|
+
}
|
|
3878
|
+
registry.set(command.id, command);
|
|
3879
|
+
}
|
|
3880
|
+
return registry;
|
|
3881
|
+
}
|
|
3882
|
+
function runCommand(command, context) {
|
|
3883
|
+
if (!isCommandVisible(command, context) || !isCommandEnabled(command, context)) {
|
|
3884
|
+
return false;
|
|
3885
|
+
}
|
|
3886
|
+
command.execute(context);
|
|
3887
|
+
return true;
|
|
3888
|
+
}
|
|
3889
|
+
|
|
3890
|
+
// src/menu/resolveMenu.ts
|
|
3891
|
+
var reportedUnknownIds = /* @__PURE__ */ new WeakMap();
|
|
3892
|
+
function reportUnknownId(commands, id) {
|
|
3893
|
+
let reported = reportedUnknownIds.get(commands);
|
|
3894
|
+
if (reported === void 0) {
|
|
3895
|
+
reported = /* @__PURE__ */ new Set();
|
|
3896
|
+
reportedUnknownIds.set(commands, reported);
|
|
3897
|
+
}
|
|
3898
|
+
if (reported.has(id)) {
|
|
3899
|
+
return;
|
|
3900
|
+
}
|
|
3901
|
+
reported.add(id);
|
|
3902
|
+
reportError(
|
|
3903
|
+
`the menu or toolbar entry \`${id}\` names no command, so it is left out.`
|
|
3904
|
+
);
|
|
3905
|
+
}
|
|
3906
|
+
function evaluate(value, context) {
|
|
3907
|
+
return typeof value === "function" ? value(context) : value;
|
|
3908
|
+
}
|
|
3909
|
+
function commandItem(command, context) {
|
|
3910
|
+
if (!isCommandVisible(command, context)) {
|
|
3911
|
+
return null;
|
|
3912
|
+
}
|
|
3913
|
+
return {
|
|
3914
|
+
type: "item",
|
|
3915
|
+
id: command.id,
|
|
3916
|
+
label: evaluate(command.label, context),
|
|
3917
|
+
icon: command.icon,
|
|
3918
|
+
shortcut: command.shortcut,
|
|
3919
|
+
disabled: !isCommandEnabled(command, context),
|
|
3920
|
+
checked: command.isChecked?.(context),
|
|
3921
|
+
run: () => runCommand(command, context)
|
|
3922
|
+
};
|
|
3923
|
+
}
|
|
3924
|
+
function inlineItem(entry, context) {
|
|
3925
|
+
if (evaluate(entry.hidden ?? false, context)) {
|
|
3926
|
+
return null;
|
|
3927
|
+
}
|
|
3928
|
+
const disabled = evaluate(entry.disabled ?? false, context);
|
|
3929
|
+
return {
|
|
3930
|
+
type: "item",
|
|
3931
|
+
id: entry.id,
|
|
3932
|
+
label: evaluate(entry.label, context),
|
|
3933
|
+
icon: entry.icon,
|
|
3934
|
+
shortcut: entry.shortcut,
|
|
3935
|
+
disabled,
|
|
3936
|
+
checked: entry.checked === void 0 ? void 0 : evaluate(entry.checked, context),
|
|
3937
|
+
run: () => {
|
|
3938
|
+
if (disabled) {
|
|
3939
|
+
return false;
|
|
3940
|
+
}
|
|
3941
|
+
entry.onSelect(context);
|
|
3942
|
+
return true;
|
|
3943
|
+
}
|
|
3944
|
+
};
|
|
3945
|
+
}
|
|
3946
|
+
function collapseSeparators(entries, isEdge = () => false) {
|
|
3947
|
+
const collapsed = [];
|
|
3948
|
+
for (const entry of entries) {
|
|
3949
|
+
const previous = collapsed.at(-1);
|
|
3950
|
+
if (entry.type !== "separator") {
|
|
3951
|
+
if (isEdge(entry) && previous?.type === "separator") {
|
|
3952
|
+
collapsed.pop();
|
|
3953
|
+
}
|
|
3954
|
+
collapsed.push(entry);
|
|
3955
|
+
} else if (previous !== void 0 && previous.type !== "separator" && !isEdge(previous)) {
|
|
3956
|
+
collapsed.push(entry);
|
|
3957
|
+
}
|
|
3958
|
+
}
|
|
3959
|
+
if (collapsed.at(-1)?.type === "separator") {
|
|
3960
|
+
collapsed.pop();
|
|
3961
|
+
}
|
|
3962
|
+
return collapsed;
|
|
3963
|
+
}
|
|
3964
|
+
function resolveMenuEntry(entry, index, options) {
|
|
3965
|
+
const { commands, context } = options;
|
|
3966
|
+
if (entry === "separator") {
|
|
3967
|
+
return { type: "separator", key: `separator-${String(index)}` };
|
|
3968
|
+
}
|
|
3969
|
+
if (typeof entry === "string") {
|
|
3970
|
+
const command = commands.get(entry);
|
|
3971
|
+
if (command === void 0) {
|
|
3972
|
+
reportUnknownId(commands, entry);
|
|
3973
|
+
return null;
|
|
3974
|
+
}
|
|
3975
|
+
return commandItem(command, context);
|
|
3976
|
+
}
|
|
3977
|
+
return entry.type === "item" ? inlineItem(entry, context) : resolveSubmenu(entry, options);
|
|
3978
|
+
}
|
|
3979
|
+
function resolveSubmenu(entry, options) {
|
|
3980
|
+
const items = resolveMenu(entry.items, options);
|
|
3981
|
+
return items.length === 0 ? null : {
|
|
3982
|
+
type: "submenu",
|
|
3983
|
+
id: entry.id,
|
|
3984
|
+
label: evaluate(entry.label, options.context),
|
|
3985
|
+
icon: entry.icon,
|
|
3986
|
+
items
|
|
3987
|
+
};
|
|
3988
|
+
}
|
|
3989
|
+
function resolveMenu(entries, options) {
|
|
3990
|
+
return collapseSeparators(
|
|
3991
|
+
entries.map((entry, index) => resolveMenuEntry(entry, index, options)).filter((entry) => entry !== null)
|
|
3992
|
+
);
|
|
3993
|
+
}
|
|
3994
|
+
|
|
3995
|
+
// src/menu/contextMenu.ts
|
|
3996
|
+
var defaultContextMenu = {
|
|
3997
|
+
header: [
|
|
3998
|
+
"sort.ascending",
|
|
3999
|
+
"sort.descending",
|
|
4000
|
+
"sort.clear",
|
|
4001
|
+
"separator",
|
|
4002
|
+
"filter.open",
|
|
4003
|
+
"filter.clear",
|
|
4004
|
+
"separator",
|
|
4005
|
+
"group.add",
|
|
4006
|
+
"group.remove",
|
|
4007
|
+
"separator",
|
|
4008
|
+
"column.autoFit",
|
|
4009
|
+
"column.autoFitAll",
|
|
4010
|
+
"separator",
|
|
4011
|
+
"column.hide",
|
|
4012
|
+
"column.choose"
|
|
4013
|
+
],
|
|
4014
|
+
cell: [
|
|
4015
|
+
"copy.cell",
|
|
4016
|
+
"copy.row",
|
|
4017
|
+
"copy.selection",
|
|
4018
|
+
"separator",
|
|
4019
|
+
"selection.selectAllRows",
|
|
4020
|
+
"selection.clear",
|
|
4021
|
+
"separator",
|
|
4022
|
+
"group.expandAll",
|
|
4023
|
+
"group.collapseAll"
|
|
4024
|
+
],
|
|
4025
|
+
group: ["group.expandAll", "group.collapseAll", "separator", "group.remove"],
|
|
4026
|
+
body: []
|
|
4027
|
+
};
|
|
4028
|
+
function hasContextMenu(contextMenu, area) {
|
|
4029
|
+
if (contextMenu === void 0 || contextMenu === false) {
|
|
4030
|
+
return false;
|
|
4031
|
+
}
|
|
4032
|
+
const entries = (contextMenu === true ? void 0 : contextMenu[area]) ?? defaultContextMenu[area];
|
|
4033
|
+
return typeof entries === "function" || entries.length > 0;
|
|
4034
|
+
}
|
|
4035
|
+
function entriesFor(given, request) {
|
|
4036
|
+
if (given === void 0) {
|
|
4037
|
+
return request.defaults;
|
|
4038
|
+
}
|
|
4039
|
+
return typeof given === "function" ? given(request) ?? [] : given;
|
|
4040
|
+
}
|
|
4041
|
+
function contextMenuEntries(contextMenu, context) {
|
|
4042
|
+
if (contextMenu === void 0 || contextMenu === false) {
|
|
4043
|
+
return [];
|
|
4044
|
+
}
|
|
4045
|
+
const options = contextMenu === true ? {} : contextMenu;
|
|
4046
|
+
const { target } = context;
|
|
4047
|
+
switch (target.area) {
|
|
4048
|
+
case "header":
|
|
4049
|
+
return entriesFor(options.header, {
|
|
4050
|
+
target,
|
|
4051
|
+
defaults: defaultContextMenu.header,
|
|
4052
|
+
context
|
|
4053
|
+
});
|
|
4054
|
+
case "cell":
|
|
4055
|
+
return entriesFor(options.cell, {
|
|
4056
|
+
target,
|
|
4057
|
+
defaults: defaultContextMenu.cell,
|
|
4058
|
+
context
|
|
4059
|
+
});
|
|
4060
|
+
case "group":
|
|
4061
|
+
return entriesFor(options.group, {
|
|
4062
|
+
target,
|
|
4063
|
+
defaults: defaultContextMenu.group,
|
|
4064
|
+
context
|
|
4065
|
+
});
|
|
4066
|
+
case "body":
|
|
4067
|
+
return entriesFor(options.body, {
|
|
4068
|
+
target,
|
|
4069
|
+
defaults: defaultContextMenu.body,
|
|
4070
|
+
context
|
|
4071
|
+
});
|
|
4072
|
+
default:
|
|
4073
|
+
return [];
|
|
4074
|
+
}
|
|
4075
|
+
}
|
|
4076
|
+
|
|
4077
|
+
// src/menu/navigation.ts
|
|
4078
|
+
function startIndex(count, from, move) {
|
|
4079
|
+
const focused = from >= 0 && from < count;
|
|
4080
|
+
switch (move) {
|
|
4081
|
+
case "first":
|
|
4082
|
+
return 0;
|
|
4083
|
+
case "last":
|
|
4084
|
+
return count - 1;
|
|
4085
|
+
case "next":
|
|
4086
|
+
return focused ? from + 1 : 0;
|
|
4087
|
+
case "previous":
|
|
4088
|
+
return focused ? from - 1 : count - 1;
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
function nextMenuIndex(entries, from, move) {
|
|
4092
|
+
const count = entries.length;
|
|
4093
|
+
const step = move === "next" || move === "first" ? 1 : -1;
|
|
4094
|
+
const start = startIndex(count, from, move);
|
|
4095
|
+
for (let offset = 0; offset < count; offset++) {
|
|
4096
|
+
const index = ((start + step * offset) % count + count) % count;
|
|
4097
|
+
if (entries[index]?.type !== "separator") {
|
|
4098
|
+
return index;
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
return -1;
|
|
4102
|
+
}
|
|
4103
|
+
function typeaheadIndex(labels, from, query) {
|
|
4104
|
+
const typed = query.toLowerCase();
|
|
4105
|
+
const firstCodePoint = typed.codePointAt(0);
|
|
4106
|
+
if (firstCodePoint === void 0) {
|
|
4107
|
+
return -1;
|
|
4108
|
+
}
|
|
4109
|
+
const first = String.fromCodePoint(firstCodePoint);
|
|
4110
|
+
const repeated = typed.replaceAll(first, "") === "";
|
|
4111
|
+
const prefix = repeated ? first : typed;
|
|
4112
|
+
const start = repeated ? from + 1 : Math.max(from, 0);
|
|
4113
|
+
for (let offset = 0; offset < labels.length; offset++) {
|
|
4114
|
+
const index = (start + offset) % labels.length;
|
|
4115
|
+
if (labels[index]?.toLowerCase().startsWith(prefix) === true) {
|
|
4116
|
+
return index;
|
|
4117
|
+
}
|
|
4118
|
+
}
|
|
4119
|
+
return -1;
|
|
4120
|
+
}
|
|
4121
|
+
|
|
4122
|
+
// src/toolbar/options.ts
|
|
4123
|
+
var defaultToolbar = ["search", "spacer", "filter.clearAll", "column.choose"];
|
|
4124
|
+
function toolbarEntries(toolbar) {
|
|
4125
|
+
if (toolbar === void 0 || toolbar === false) {
|
|
4126
|
+
return [];
|
|
4127
|
+
}
|
|
4128
|
+
return (toolbar === true ? void 0 : toolbar.items) ?? defaultToolbar;
|
|
4129
|
+
}
|
|
4130
|
+
|
|
4131
|
+
// src/toolbar/resolveToolbar.ts
|
|
4132
|
+
function isSpacer(entry) {
|
|
4133
|
+
return entry.type === "spacer";
|
|
4134
|
+
}
|
|
4135
|
+
function exportMenu() {
|
|
4136
|
+
return {
|
|
4137
|
+
type: "submenu",
|
|
4138
|
+
id: "export",
|
|
4139
|
+
label: ({ messages }) => messages.exportMenu,
|
|
4140
|
+
items: [
|
|
4141
|
+
"export.csv",
|
|
4142
|
+
"export.json",
|
|
4143
|
+
"export.xlsx",
|
|
4144
|
+
"export.pdf",
|
|
4145
|
+
"separator",
|
|
4146
|
+
"exportSelection.csv",
|
|
4147
|
+
"exportSelection.json",
|
|
4148
|
+
"exportSelection.xlsx"
|
|
4149
|
+
]
|
|
4150
|
+
};
|
|
4151
|
+
}
|
|
4152
|
+
function resolveToolbar(entries, options) {
|
|
4153
|
+
const { commands, contextFor } = options;
|
|
4154
|
+
function resolveEntry(entry, index) {
|
|
4155
|
+
if (entry === "search") {
|
|
4156
|
+
return { type: "search", key: "search" };
|
|
4157
|
+
}
|
|
4158
|
+
if (entry === "spacer") {
|
|
4159
|
+
return { type: "spacer", key: `spacer-${String(index)}` };
|
|
4160
|
+
}
|
|
4161
|
+
if (typeof entry === "object" && entry.type === "custom") {
|
|
4162
|
+
return { type: "custom", id: entry.id, key: entry.id };
|
|
4163
|
+
}
|
|
4164
|
+
if (entry === "separator") {
|
|
4165
|
+
return { type: "separator", key: `separator-${String(index)}` };
|
|
4166
|
+
}
|
|
4167
|
+
const menuEntry = entry === "export" ? exportMenu() : entry;
|
|
4168
|
+
const resolved = resolveMenuEntry(menuEntry, index, {
|
|
4169
|
+
commands,
|
|
4170
|
+
context: contextFor({
|
|
4171
|
+
area: "toolbar",
|
|
4172
|
+
itemId: typeof menuEntry === "string" ? menuEntry : menuEntry.id
|
|
4173
|
+
})
|
|
4174
|
+
});
|
|
4175
|
+
if (resolved === null || resolved.type === "separator") {
|
|
4176
|
+
return resolved;
|
|
4177
|
+
}
|
|
4178
|
+
if (resolved.type === "submenu") {
|
|
4179
|
+
return { ...resolved, type: "menu", key: resolved.id };
|
|
4180
|
+
}
|
|
4181
|
+
const { checked, ...item } = resolved;
|
|
4182
|
+
return { ...item, type: "button", key: item.id, pressed: checked };
|
|
4183
|
+
}
|
|
4184
|
+
return collapseSeparators(
|
|
4185
|
+
entries.map(resolveEntry).filter((entry) => entry !== null),
|
|
4186
|
+
isSpacer
|
|
4187
|
+
);
|
|
4188
|
+
}
|
|
4189
|
+
|
|
4190
|
+
// src/toolbar/overflow.ts
|
|
4191
|
+
function fitToolbarItems({
|
|
4192
|
+
widths,
|
|
4193
|
+
available,
|
|
4194
|
+
moreWidth,
|
|
4195
|
+
pinned
|
|
4196
|
+
}) {
|
|
4197
|
+
const total = widths.reduce((sum, width) => sum + width, 0);
|
|
4198
|
+
if (total <= available) {
|
|
4199
|
+
return widths.length;
|
|
4200
|
+
}
|
|
4201
|
+
let used = moreWidth + widths.reduce((sum, width, index) => sum + (pinned[index] ? width : 0), 0);
|
|
4202
|
+
for (const [index, width] of widths.entries()) {
|
|
4203
|
+
if (!pinned[index]) {
|
|
4204
|
+
used += width;
|
|
4205
|
+
if (used > available) {
|
|
4206
|
+
return index;
|
|
4207
|
+
}
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4210
|
+
return widths.length;
|
|
4211
|
+
}
|
|
4212
|
+
function isPinnedToolbarItem(item) {
|
|
4213
|
+
return item.type === "search" || item.type === "custom";
|
|
4214
|
+
}
|
|
4215
|
+
function asMenuEntry(item) {
|
|
4216
|
+
switch (item.type) {
|
|
4217
|
+
case "button":
|
|
4218
|
+
return {
|
|
4219
|
+
type: "item",
|
|
4220
|
+
id: item.id,
|
|
4221
|
+
label: item.label,
|
|
4222
|
+
icon: item.icon,
|
|
4223
|
+
shortcut: item.shortcut,
|
|
4224
|
+
disabled: item.disabled,
|
|
4225
|
+
checked: item.pressed,
|
|
4226
|
+
run: item.run
|
|
4227
|
+
};
|
|
4228
|
+
case "menu":
|
|
4229
|
+
return {
|
|
4230
|
+
type: "submenu",
|
|
4231
|
+
id: item.id,
|
|
4232
|
+
label: item.label,
|
|
4233
|
+
icon: item.icon,
|
|
4234
|
+
items: item.items
|
|
4235
|
+
};
|
|
4236
|
+
case "separator":
|
|
4237
|
+
return item;
|
|
4238
|
+
default:
|
|
4239
|
+
return null;
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
function splitToolbarItems(items, fit) {
|
|
4243
|
+
const fits = (item, index) => index < fit || isPinnedToolbarItem(item);
|
|
4244
|
+
return {
|
|
4245
|
+
shown: collapseSeparators(
|
|
4246
|
+
items.filter(fits),
|
|
4247
|
+
(item) => item.type === "spacer"
|
|
4248
|
+
),
|
|
4249
|
+
overflow: collapseSeparators(
|
|
4250
|
+
items.filter((item, index) => !fits(item, index)).map(asMenuEntry).filter((entry) => entry !== null)
|
|
4251
|
+
)
|
|
4252
|
+
};
|
|
4253
|
+
}
|
|
4254
|
+
|
|
4255
|
+
// src/toolbar/search.ts
|
|
4256
|
+
function searchTextOf(filter) {
|
|
4257
|
+
const query = filterQueryFor(filter) ?? "";
|
|
4258
|
+
const start = query.startsWith("%") ? 1 : 0;
|
|
4259
|
+
const end = query.endsWith("%") ? -1 : void 0;
|
|
4260
|
+
return query.slice(start, end);
|
|
4261
|
+
}
|
|
4262
|
+
function setSearchText(filter, text) {
|
|
4263
|
+
return setColumnFilter(filter, void 0, text === "" ? "" : `%${text}%`);
|
|
4264
|
+
}
|
|
4265
|
+
|
|
4266
|
+
// src/util/navigation.ts
|
|
4267
|
+
var HEADER_ROW = -1;
|
|
4268
|
+
function clampFocus(focus, rowCount, columnCount2) {
|
|
4269
|
+
return {
|
|
4270
|
+
rowIndex: Math.min(Math.max(focus.rowIndex, HEADER_ROW), rowCount - 1),
|
|
4271
|
+
columnIndex: Math.min(
|
|
4272
|
+
Math.max(focus.columnIndex, 0),
|
|
4273
|
+
Math.max(columnCount2 - 1, 0)
|
|
4274
|
+
)
|
|
4275
|
+
};
|
|
4276
|
+
}
|
|
4277
|
+
function noRowsSkippable() {
|
|
4278
|
+
return false;
|
|
4279
|
+
}
|
|
4280
|
+
function skipSkippableRows(rowIndex, direction, rowCount, isSkippableRow, fallback) {
|
|
4281
|
+
let next = rowIndex;
|
|
4282
|
+
while (next >= 0 && next < rowCount && isSkippableRow(next)) {
|
|
4283
|
+
const stepped = next + direction;
|
|
4284
|
+
if (stepped < HEADER_ROW || stepped >= rowCount) {
|
|
4285
|
+
return fallback;
|
|
4286
|
+
}
|
|
4287
|
+
next = stepped;
|
|
4288
|
+
}
|
|
4289
|
+
return next;
|
|
4290
|
+
}
|
|
4291
|
+
function nextFocusForKey(key, modifiers, focus, rowCount, columnCount2, pageSize, isSkippableRow = noRowsSkippable) {
|
|
4292
|
+
if (modifiers.altKey) {
|
|
4293
|
+
return null;
|
|
4294
|
+
}
|
|
4295
|
+
const { rowIndex, columnIndex } = focus;
|
|
4296
|
+
switch (key) {
|
|
4297
|
+
case "ArrowLeft":
|
|
4298
|
+
if (modifiers.ctrlKey) return null;
|
|
4299
|
+
return { rowIndex, columnIndex: columnIndex - 1 };
|
|
4300
|
+
case "ArrowRight":
|
|
4301
|
+
if (modifiers.ctrlKey) return null;
|
|
4302
|
+
return { rowIndex, columnIndex: columnIndex + 1 };
|
|
4303
|
+
case "ArrowUp":
|
|
4304
|
+
return {
|
|
4305
|
+
rowIndex: skipSkippableRows(
|
|
4306
|
+
rowIndex - 1,
|
|
4307
|
+
-1,
|
|
4308
|
+
rowCount,
|
|
4309
|
+
isSkippableRow,
|
|
4310
|
+
rowIndex
|
|
4311
|
+
),
|
|
4312
|
+
columnIndex
|
|
4313
|
+
};
|
|
4314
|
+
case "ArrowDown":
|
|
4315
|
+
return {
|
|
4316
|
+
rowIndex: skipSkippableRows(
|
|
4317
|
+
rowIndex + 1,
|
|
4318
|
+
1,
|
|
4319
|
+
rowCount,
|
|
4320
|
+
isSkippableRow,
|
|
4321
|
+
rowIndex
|
|
4322
|
+
),
|
|
4323
|
+
columnIndex
|
|
4324
|
+
};
|
|
4325
|
+
// Ctrl takes Home and End to the grid's ends rather than the row's, which
|
|
4326
|
+
// is the one place Ctrl is navigation rather than reorder.
|
|
4327
|
+
case "Home":
|
|
4328
|
+
return modifiers.ctrlKey ? { rowIndex: HEADER_ROW, columnIndex: 0 } : { rowIndex, columnIndex: 0 };
|
|
4329
|
+
case "End":
|
|
4330
|
+
return modifiers.ctrlKey ? {
|
|
4331
|
+
rowIndex: skipSkippableRows(
|
|
4332
|
+
rowCount - 1,
|
|
4333
|
+
-1,
|
|
4334
|
+
rowCount,
|
|
4335
|
+
isSkippableRow,
|
|
4336
|
+
rowIndex
|
|
4337
|
+
),
|
|
4338
|
+
columnIndex: columnCount2 - 1
|
|
4339
|
+
} : { rowIndex, columnIndex: columnCount2 - 1 };
|
|
4340
|
+
case "PageUp":
|
|
4341
|
+
return {
|
|
4342
|
+
rowIndex: skipSkippableRows(
|
|
4343
|
+
rowIndex - pageSize,
|
|
4344
|
+
-1,
|
|
4345
|
+
rowCount,
|
|
4346
|
+
isSkippableRow,
|
|
4347
|
+
rowIndex
|
|
4348
|
+
),
|
|
4349
|
+
columnIndex
|
|
4350
|
+
};
|
|
4351
|
+
case "PageDown":
|
|
4352
|
+
return {
|
|
4353
|
+
rowIndex: skipSkippableRows(
|
|
4354
|
+
rowIndex + pageSize,
|
|
4355
|
+
1,
|
|
4356
|
+
rowCount,
|
|
4357
|
+
isSkippableRow,
|
|
4358
|
+
rowIndex
|
|
4359
|
+
),
|
|
4360
|
+
columnIndex
|
|
4361
|
+
};
|
|
4362
|
+
default:
|
|
4363
|
+
return null;
|
|
4364
|
+
}
|
|
4365
|
+
}
|
|
4366
|
+
|
|
4367
|
+
// src/util/paginationWindow.ts
|
|
4368
|
+
function paginationWindow(currentPage, pageCount, options) {
|
|
4369
|
+
if (pageCount <= 0) {
|
|
4370
|
+
return [];
|
|
4371
|
+
}
|
|
4372
|
+
const boundaryCount = Math.max(0, Math.floor(options?.boundaryCount ?? 1));
|
|
4373
|
+
const siblingCount = Math.max(0, Math.floor(options?.siblingCount ?? 1));
|
|
4374
|
+
const slotCount = 2 * boundaryCount + 2 * siblingCount + 3;
|
|
4375
|
+
if (pageCount <= slotCount) {
|
|
4376
|
+
return Array.from({ length: pageCount }, (_, index) => index + 1);
|
|
4377
|
+
}
|
|
4378
|
+
const current = Math.min(Math.max(currentPage, 1), pageCount);
|
|
4379
|
+
const runStart = Math.min(
|
|
4380
|
+
Math.max(current - siblingCount, boundaryCount + 2),
|
|
4381
|
+
pageCount - boundaryCount - 1 - 2 * siblingCount
|
|
4382
|
+
);
|
|
4383
|
+
const runEnd = runStart + 2 * siblingCount;
|
|
4384
|
+
const entries = [];
|
|
4385
|
+
for (let page = 1; page <= boundaryCount; page++) {
|
|
4386
|
+
entries.push(page);
|
|
4387
|
+
}
|
|
4388
|
+
entries.push(runStart === boundaryCount + 2 ? boundaryCount + 1 : "ellipsis");
|
|
4389
|
+
for (let page = runStart; page <= runEnd; page++) {
|
|
4390
|
+
entries.push(page);
|
|
4391
|
+
}
|
|
4392
|
+
entries.push(
|
|
4393
|
+
runEnd === pageCount - boundaryCount - 1 ? pageCount - boundaryCount : "ellipsis"
|
|
4394
|
+
);
|
|
4395
|
+
for (let page = pageCount - boundaryCount + 1; page <= pageCount; page++) {
|
|
4396
|
+
entries.push(page);
|
|
4397
|
+
}
|
|
4398
|
+
return entries;
|
|
4399
|
+
}
|
|
4400
|
+
|
|
4401
|
+
// src/util/columnChooser.ts
|
|
4402
|
+
function isHideable(column) {
|
|
4403
|
+
return column.column.hideable !== false;
|
|
4404
|
+
}
|
|
4405
|
+
function matchesSearch2(label, search) {
|
|
4406
|
+
return label.toLowerCase().includes(search.toLowerCase());
|
|
4407
|
+
}
|
|
4408
|
+
function selectAllStateOf(states) {
|
|
4409
|
+
if (states.length > 0 && states.every(Boolean)) {
|
|
4410
|
+
return "checked";
|
|
4411
|
+
}
|
|
4412
|
+
return states.some(Boolean) ? "mixed" : "unchecked";
|
|
4413
|
+
}
|
|
4414
|
+
function resolveColumnChooser({
|
|
4415
|
+
columns,
|
|
4416
|
+
visibility,
|
|
4417
|
+
search = ""
|
|
4418
|
+
}) {
|
|
4419
|
+
const all = columns.map((column) => ({
|
|
4420
|
+
column,
|
|
4421
|
+
label: resolveColumnHeaderText(column.column),
|
|
4422
|
+
visible: isColumnVisible(column.column, visibility)
|
|
4423
|
+
}));
|
|
4424
|
+
const shownCount = all.filter(({ visible }) => visible).length;
|
|
4425
|
+
const matched = all.filter(({ label }) => matchesSearch2(label, search));
|
|
4426
|
+
const entries = matched.map(
|
|
4427
|
+
({ column, label, visible }) => ({
|
|
4428
|
+
columnId: column.id,
|
|
4429
|
+
label,
|
|
4430
|
+
visible,
|
|
4431
|
+
disabled: visible && (!isHideable(column) || shownCount === 1)
|
|
4432
|
+
})
|
|
4433
|
+
);
|
|
4434
|
+
const covered = matched.filter(({ column }) => isHideable(column));
|
|
4435
|
+
const selectAll2 = selectAllStateOf(covered.map(({ visible }) => visible));
|
|
4436
|
+
return {
|
|
4437
|
+
entries,
|
|
4438
|
+
selectAll: selectAll2,
|
|
4439
|
+
toggleAll: toggleAllOf(covered, selectAll2, shownCount)
|
|
4440
|
+
};
|
|
4441
|
+
}
|
|
4442
|
+
function toggleAllOf(covered, selectAll2, shownCount) {
|
|
4443
|
+
const show = selectAll2 !== "checked";
|
|
4444
|
+
const changing = covered.filter(({ visible }) => visible !== show).map(({ column }) => column.id);
|
|
4445
|
+
const columnIds = !show && changing.length === shownCount ? changing.slice(1) : changing;
|
|
4446
|
+
return columnIds.length === 0 ? null : { visible: show, columnIds };
|
|
4447
|
+
}
|
|
4448
|
+
|
|
4449
|
+
// src/filterMenu/conditions.ts
|
|
4450
|
+
var ROW_COUNT = 2;
|
|
4451
|
+
function rowOf(condition, type) {
|
|
4452
|
+
const { operator, value } = condition;
|
|
4453
|
+
if (isScalarList(value)) {
|
|
4454
|
+
const [low, high] = value;
|
|
4455
|
+
return {
|
|
4456
|
+
operator,
|
|
4457
|
+
text: formatFilterOperand(low, type),
|
|
4458
|
+
textTo: formatFilterOperand(high, type)
|
|
4459
|
+
};
|
|
4460
|
+
}
|
|
4461
|
+
return { operator, text: formatFilterOperand(value, type), textTo: "" };
|
|
4462
|
+
}
|
|
4463
|
+
function conditionRowsFrom(shape, operators, type) {
|
|
4464
|
+
const filled = shape.kind === "conditions" ? shape.conditions.map((condition) => rowOf(condition, type)) : [];
|
|
4465
|
+
const [first] = operators;
|
|
4466
|
+
if (first === void 0) {
|
|
4467
|
+
return filled;
|
|
4468
|
+
}
|
|
4469
|
+
const blank = { operator: first, text: "", textTo: "" };
|
|
4470
|
+
return Array.from(
|
|
4471
|
+
{ length: ROW_COUNT },
|
|
4472
|
+
(_, index) => filled[index] ?? blank
|
|
4473
|
+
);
|
|
4474
|
+
}
|
|
4475
|
+
function isEmptyRow(row) {
|
|
4476
|
+
switch (filterOperandArity(row.operator)) {
|
|
4477
|
+
case 0:
|
|
4478
|
+
return false;
|
|
4479
|
+
case 1:
|
|
4480
|
+
return row.text === "";
|
|
4481
|
+
case 2:
|
|
4482
|
+
return row.text === "" && row.textTo === "";
|
|
4483
|
+
}
|
|
4484
|
+
}
|
|
4485
|
+
function conditionOf(row, columnId, type) {
|
|
4486
|
+
const { operator } = row;
|
|
4487
|
+
switch (filterOperandArity(operator)) {
|
|
4488
|
+
case 0:
|
|
4489
|
+
return { columnId, operator };
|
|
4490
|
+
case 1: {
|
|
4491
|
+
const value = parseFilterOperand(row.text, type);
|
|
4492
|
+
return value === void 0 ? void 0 : { columnId, operator, value };
|
|
4493
|
+
}
|
|
4494
|
+
case 2: {
|
|
4495
|
+
const low = parseFilterOperand(row.text, type);
|
|
4496
|
+
const high = parseFilterOperand(row.textTo, type);
|
|
4497
|
+
return low === void 0 || high === void 0 ? void 0 : { columnId, operator, value: [low, high] };
|
|
4498
|
+
}
|
|
4499
|
+
}
|
|
4500
|
+
}
|
|
4501
|
+
function conditionsFromRows(rows, columnId, type) {
|
|
4502
|
+
const conditions = [];
|
|
4503
|
+
const invalid = [];
|
|
4504
|
+
for (const [index, row] of rows.entries()) {
|
|
4505
|
+
if (index > 0 && isEmptyRow(row)) {
|
|
4506
|
+
continue;
|
|
4507
|
+
}
|
|
4508
|
+
const condition = conditionOf(row, columnId, type);
|
|
4509
|
+
if (condition === void 0) {
|
|
4510
|
+
invalid.push(index);
|
|
4511
|
+
} else {
|
|
4512
|
+
conditions.push(condition);
|
|
4513
|
+
}
|
|
4514
|
+
}
|
|
4515
|
+
return { conditions, invalid };
|
|
4516
|
+
}
|
|
4517
|
+
|
|
4518
|
+
// src/filterMenu/valueCache.ts
|
|
4519
|
+
function sameSource(a, b) {
|
|
4520
|
+
return a.rows === b.rows && a.dataColumns === b.dataColumns && a.locale === b.locale && a.messages === b.messages;
|
|
4521
|
+
}
|
|
4522
|
+
function sameEntries(a, b) {
|
|
4523
|
+
return a.length === b.length && a.every((entry, index) => entry === b[index]);
|
|
4524
|
+
}
|
|
4525
|
+
function createColumnValueOptionsCache() {
|
|
4526
|
+
let source;
|
|
4527
|
+
const lists = /* @__PURE__ */ new Map();
|
|
4528
|
+
return {
|
|
4529
|
+
get: (rows, dataColumns, filter, columnId, query = {}) => {
|
|
4530
|
+
const { locale, messages } = query;
|
|
4531
|
+
const current = { rows, dataColumns, locale, messages };
|
|
4532
|
+
if (source === void 0 || !sameSource(source, current)) {
|
|
4533
|
+
lists.clear();
|
|
4534
|
+
source = current;
|
|
4535
|
+
}
|
|
4536
|
+
const others = filter.filter((entry) => !touchesColumn(entry, columnId));
|
|
4537
|
+
const cached2 = lists.get(columnId);
|
|
4538
|
+
if (cached2 !== void 0 && sameEntries(cached2.others, others)) {
|
|
4539
|
+
return cached2.values;
|
|
4540
|
+
}
|
|
4541
|
+
const values = columnValueOptions(
|
|
4542
|
+
rows,
|
|
4543
|
+
dataColumns,
|
|
4544
|
+
filter,
|
|
4545
|
+
columnId,
|
|
4546
|
+
messages === void 0 ? { locale } : { locale, messages }
|
|
4547
|
+
);
|
|
4548
|
+
lists.set(columnId, { others, values });
|
|
4549
|
+
return values;
|
|
4550
|
+
}
|
|
4551
|
+
};
|
|
4552
|
+
}
|
|
4553
|
+
|
|
4554
|
+
// src/filterMenu/checklist.ts
|
|
4555
|
+
function filterChecklist(draft, { valueLimit, blanksLabel }) {
|
|
4556
|
+
const visible = visibleFilterOptions(draft);
|
|
4557
|
+
const total = visible.length;
|
|
4558
|
+
if (draft.search === "" && total > valueLimit) {
|
|
4559
|
+
return { items: [], needsSearch: true, hiddenCount: 0, total };
|
|
4560
|
+
}
|
|
4561
|
+
const items = visible.slice(0, valueLimit).map(({ value, label, count }) => {
|
|
4562
|
+
const key = keyOf2(draft, value);
|
|
4563
|
+
const checked = draft.checked.has(key);
|
|
4564
|
+
return { kind: "value", key, value, label, count, checked };
|
|
4565
|
+
});
|
|
4566
|
+
const hiddenCount = total - items.length;
|
|
4567
|
+
if (selectAllCoversBlanks(draft)) {
|
|
4568
|
+
items.push({
|
|
4569
|
+
kind: "blanks",
|
|
4570
|
+
// Blank cells' own key, which no offered value has.
|
|
4571
|
+
key: keyOf2(draft, null),
|
|
4572
|
+
label: blanksLabel,
|
|
4573
|
+
count: draft.blanks,
|
|
4574
|
+
checked: draft.includeBlanks
|
|
4575
|
+
});
|
|
4576
|
+
}
|
|
4577
|
+
return { items, needsSearch: false, hiddenCount, total };
|
|
4578
|
+
}
|
|
4579
|
+
|
|
4580
|
+
// src/util/virtualization.ts
|
|
4581
|
+
function visibleRange(heights, estimatedHeight, scrollTop, viewportHeight, overscan) {
|
|
4582
|
+
const rowCount = heights.length;
|
|
4583
|
+
if (rowCount === 0) {
|
|
4584
|
+
return { startIndex: 0, endIndex: -1, offsetBefore: 0, offsetAfter: 0 };
|
|
4585
|
+
}
|
|
4586
|
+
function heightAt(index) {
|
|
4587
|
+
return heights[index] ?? estimatedHeight;
|
|
4588
|
+
}
|
|
4589
|
+
const viewportBottom = scrollTop + viewportHeight;
|
|
4590
|
+
let start = 0;
|
|
4591
|
+
let top = 0;
|
|
4592
|
+
while (start < rowCount - 1 && top + heightAt(start) <= scrollTop) {
|
|
4593
|
+
top += heightAt(start);
|
|
4594
|
+
start += 1;
|
|
4595
|
+
}
|
|
4596
|
+
const startTop = top;
|
|
4597
|
+
let end = start;
|
|
4598
|
+
let bottom = startTop + heightAt(start);
|
|
4599
|
+
while (end < rowCount - 1 && bottom < viewportBottom) {
|
|
4600
|
+
end += 1;
|
|
4601
|
+
bottom += heightAt(end);
|
|
4602
|
+
}
|
|
4603
|
+
const overscanStart = Math.max(0, start - overscan);
|
|
4604
|
+
const overscanEnd = Math.min(rowCount - 1, end + overscan);
|
|
4605
|
+
let offsetBefore = 0;
|
|
4606
|
+
for (let index = 0; index < overscanStart; index++) {
|
|
4607
|
+
offsetBefore += heightAt(index);
|
|
4608
|
+
}
|
|
4609
|
+
let offsetAfter = 0;
|
|
4610
|
+
for (let index = overscanEnd + 1; index < rowCount; index++) {
|
|
4611
|
+
offsetAfter += heightAt(index);
|
|
4612
|
+
}
|
|
4613
|
+
return {
|
|
4614
|
+
startIndex: overscanStart,
|
|
4615
|
+
endIndex: overscanEnd,
|
|
4616
|
+
offsetBefore,
|
|
4617
|
+
offsetAfter
|
|
4618
|
+
};
|
|
4619
|
+
}
|
|
4620
|
+
|
|
4621
|
+
// src/presets.ts
|
|
4622
|
+
var excelFilter = {
|
|
4623
|
+
filterableColumns: true,
|
|
4624
|
+
filterMenu: { ui: "excel", showCounts: true }
|
|
4625
|
+
};
|
|
4626
|
+
export {
|
|
4627
|
+
DEFAULT_COLUMN_SIZES,
|
|
4628
|
+
DEFAULT_FILTER_VALUE_LIMIT,
|
|
4629
|
+
HEADER_ROW,
|
|
4630
|
+
KEYBOARD_STEP,
|
|
4631
|
+
accessDotted,
|
|
4632
|
+
alignmentForType,
|
|
4633
|
+
applyColumnOrder,
|
|
4634
|
+
applyColumnResize,
|
|
4635
|
+
applyColumnVisibility,
|
|
4636
|
+
applySelectionIntent,
|
|
4637
|
+
beginColumnResize,
|
|
4638
|
+
buildExportTable,
|
|
4639
|
+
buildKeyShortcuts,
|
|
4640
|
+
buildPrintDocument,
|
|
4641
|
+
builtInCommands,
|
|
4642
|
+
clampColumnWidth,
|
|
4643
|
+
clampFocus,
|
|
4644
|
+
clearAllFilters,
|
|
4645
|
+
clearSelection,
|
|
4646
|
+
collapseAllGroups,
|
|
4647
|
+
columnValueOptions,
|
|
4648
|
+
compareValues,
|
|
4649
|
+
computeAggregates,
|
|
4650
|
+
conditionRowsFrom,
|
|
4651
|
+
conditionsFromRows,
|
|
4652
|
+
contextMenuEntries,
|
|
4653
|
+
copyContent,
|
|
4654
|
+
createColumnValueOptionsCache,
|
|
4655
|
+
createCommandContext,
|
|
4656
|
+
createDefaultMessages,
|
|
4657
|
+
createGridController,
|
|
4658
|
+
createGridViewResolver,
|
|
4659
|
+
csvWriter,
|
|
4660
|
+
defaultContextMenu,
|
|
4661
|
+
defaultFilterOperators,
|
|
4662
|
+
defaultMessages,
|
|
4663
|
+
defaultToolbar,
|
|
4664
|
+
defineColumnsFromRows,
|
|
4665
|
+
describeColumnFilter,
|
|
4666
|
+
diffSelection,
|
|
4667
|
+
draftToFilter,
|
|
4668
|
+
excelFilter,
|
|
4669
|
+
expandAllGroups,
|
|
4670
|
+
exportFile,
|
|
4671
|
+
exportPrintDocument,
|
|
4672
|
+
filterChecklist,
|
|
4673
|
+
filterEditorReducer,
|
|
4674
|
+
filterOperandArity,
|
|
4675
|
+
filterOperatorFamily,
|
|
4676
|
+
filterQueryFor,
|
|
4677
|
+
filterRows,
|
|
4678
|
+
fitColumnsToWidth,
|
|
4679
|
+
fitToolbarItems,
|
|
4680
|
+
formatAggregateValue,
|
|
4681
|
+
formatCellValue,
|
|
4682
|
+
formatFilterOperand,
|
|
4683
|
+
formatGroupValue,
|
|
4684
|
+
getColumnId,
|
|
4685
|
+
groupRowId,
|
|
4686
|
+
groupRows,
|
|
4687
|
+
hasContextMenu,
|
|
4688
|
+
initialFilterEditorDraft,
|
|
4689
|
+
intentOf,
|
|
4690
|
+
isColumnFiltered,
|
|
4691
|
+
isColumnVisible,
|
|
4692
|
+
isPinnedToolbarItem,
|
|
4693
|
+
isSameCell,
|
|
4694
|
+
jsonWriter,
|
|
4695
|
+
matchesQuery,
|
|
4696
|
+
moveColumnBefore,
|
|
4697
|
+
moveGroupByBefore,
|
|
4698
|
+
movesColumn,
|
|
4699
|
+
movesGroupBy,
|
|
4700
|
+
nextFocusForKey,
|
|
4701
|
+
nextMenuIndex,
|
|
4702
|
+
paginateRows,
|
|
4703
|
+
paginationWindow,
|
|
4704
|
+
parseFilterOperand,
|
|
4705
|
+
resolveCell,
|
|
4706
|
+
resolveColumnChooser,
|
|
4707
|
+
resolveColumnConstraints,
|
|
4708
|
+
resolveColumnFilterMenu,
|
|
4709
|
+
resolveColumnHeaderText,
|
|
4710
|
+
resolveColumnLabel,
|
|
4711
|
+
resolveColumnWidths,
|
|
4712
|
+
resolveColumns,
|
|
4713
|
+
resolveCommands,
|
|
4714
|
+
resolveDataColumns,
|
|
4715
|
+
resolveDropBefore,
|
|
4716
|
+
resolveExportWriters,
|
|
4717
|
+
resolveGridView,
|
|
4718
|
+
resolveKeyboardDropTarget,
|
|
4719
|
+
resolveMenu,
|
|
4720
|
+
resolveMessages,
|
|
4721
|
+
resolveRowId,
|
|
4722
|
+
resolveRows,
|
|
4723
|
+
resolveShownRows,
|
|
4724
|
+
resolveToolbar,
|
|
4725
|
+
revertColumnSize,
|
|
4726
|
+
runCommand,
|
|
4727
|
+
searchTextOf,
|
|
4728
|
+
selectAll,
|
|
4729
|
+
selectAllState,
|
|
4730
|
+
selectCell,
|
|
4731
|
+
selectOnly,
|
|
4732
|
+
selectRange,
|
|
4733
|
+
serializeGridState,
|
|
4734
|
+
setColumnFilter,
|
|
4735
|
+
setColumnVisibility,
|
|
4736
|
+
setSearchText,
|
|
4737
|
+
sizeColumnToContent,
|
|
4738
|
+
sortDirectionFor,
|
|
4739
|
+
sortPriorityFor,
|
|
4740
|
+
sortRows,
|
|
4741
|
+
splitToolbarItems,
|
|
4742
|
+
toCsv,
|
|
4743
|
+
toHtmlTable,
|
|
4744
|
+
toJson,
|
|
4745
|
+
toTsv,
|
|
4746
|
+
toggleCellSelection,
|
|
4747
|
+
toggleColumnSort,
|
|
4748
|
+
toggleGroupExpansion,
|
|
4749
|
+
toggleSelection,
|
|
4750
|
+
toolbarEntries,
|
|
4751
|
+
totalColumnWidth,
|
|
4752
|
+
typeaheadIndex,
|
|
4753
|
+
visibleFilterOptions,
|
|
4754
|
+
visibleRange,
|
|
4755
|
+
withGroupAggregates
|
|
4756
|
+
};
|
|
4757
|
+
//# sourceMappingURL=index.js.map
|