@gridkitjs/core 0.12.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.ts +2816 -1825
- package/dist/index.js +2191 -1055
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -91,6 +91,9 @@ function buildKeyShortcuts(capabilities) {
|
|
|
91
91
|
// Down rather than reusing Up: it reads as "drill into groups", and Up is
|
|
92
92
|
// already sort's own mnemonic on the same header.
|
|
93
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" : "",
|
|
94
97
|
// The ContextMenu key needs no mention: it says what it does on its face.
|
|
95
98
|
capabilities.contextMenu === true ? "Shift+F10" : ""
|
|
96
99
|
].filter(Boolean).join(" ");
|
|
@@ -2185,807 +2188,856 @@ function createCommandContext({
|
|
|
2185
2188
|
};
|
|
2186
2189
|
}
|
|
2187
2190
|
|
|
2188
|
-
// src/
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
+
// src/util/format.ts
|
|
2192
|
+
var DATE_FIELDS = {
|
|
2193
|
+
year: "numeric",
|
|
2194
|
+
month: "numeric",
|
|
2195
|
+
day: "numeric"
|
|
2196
|
+
};
|
|
2197
|
+
var TIME_FIELDS = {
|
|
2198
|
+
hour: "numeric",
|
|
2199
|
+
minute: "numeric",
|
|
2200
|
+
second: "numeric"
|
|
2201
|
+
};
|
|
2202
|
+
var DATE_TIME_FIELDS = {
|
|
2203
|
+
...DATE_FIELDS,
|
|
2204
|
+
...TIME_FIELDS
|
|
2205
|
+
};
|
|
2206
|
+
var CURRENCY_DIGITS = {
|
|
2207
|
+
minimumFractionDigits: 2,
|
|
2208
|
+
maximumFractionDigits: 2
|
|
2209
|
+
};
|
|
2210
|
+
var NO_FORMAT = {};
|
|
2211
|
+
var formatters = /* @__PURE__ */ new WeakMap();
|
|
2212
|
+
function cached(format, key, create) {
|
|
2213
|
+
const owner = format ?? NO_FORMAT;
|
|
2214
|
+
let byKey = formatters.get(owner);
|
|
2215
|
+
if (byKey === void 0) {
|
|
2216
|
+
byKey = /* @__PURE__ */ new Map();
|
|
2217
|
+
formatters.set(owner, byKey);
|
|
2218
|
+
}
|
|
2219
|
+
const existing = byKey.get(key);
|
|
2220
|
+
if (existing !== void 0) {
|
|
2221
|
+
return existing;
|
|
2222
|
+
}
|
|
2223
|
+
const formatter = create();
|
|
2224
|
+
byKey.set(key, formatter);
|
|
2225
|
+
return formatter;
|
|
2191
2226
|
}
|
|
2192
|
-
function
|
|
2193
|
-
return
|
|
2227
|
+
function localeKey(locale) {
|
|
2228
|
+
return typeof locale === "string" ? locale : locale?.join(",") ?? "";
|
|
2194
2229
|
}
|
|
2195
|
-
function
|
|
2196
|
-
return
|
|
2230
|
+
function isNumericType(type) {
|
|
2231
|
+
return type === "number" || type === "decimal" || type === "currency" || type === "percent";
|
|
2197
2232
|
}
|
|
2198
|
-
function
|
|
2199
|
-
|
|
2200
|
-
|
|
2233
|
+
function numberOptions(type, format) {
|
|
2234
|
+
switch (type) {
|
|
2235
|
+
case "currency":
|
|
2236
|
+
return format?.currency === void 0 ? { ...CURRENCY_DIGITS, ...format } : { style: "currency", ...format };
|
|
2237
|
+
case "percent":
|
|
2238
|
+
return { style: "percent", ...format };
|
|
2239
|
+
default:
|
|
2240
|
+
return format ?? {};
|
|
2241
|
+
}
|
|
2201
2242
|
}
|
|
2202
|
-
function
|
|
2203
|
-
|
|
2204
|
-
|
|
2243
|
+
function dateOptions(type, format) {
|
|
2244
|
+
if (format !== void 0) {
|
|
2245
|
+
return format;
|
|
2246
|
+
}
|
|
2247
|
+
if (type === "date") return DATE_FIELDS;
|
|
2248
|
+
if (type === "time") return TIME_FIELDS;
|
|
2249
|
+
return DATE_TIME_FIELDS;
|
|
2205
2250
|
}
|
|
2206
|
-
function
|
|
2207
|
-
const
|
|
2208
|
-
|
|
2251
|
+
function formatNumber(value, type, format, locale) {
|
|
2252
|
+
const formatter = cached(
|
|
2253
|
+
format,
|
|
2254
|
+
`number|${type ?? ""}|${localeKey(locale)}`,
|
|
2255
|
+
() => new Intl.NumberFormat(
|
|
2256
|
+
locale,
|
|
2257
|
+
numberOptions(type, format)
|
|
2258
|
+
)
|
|
2259
|
+
);
|
|
2260
|
+
return formatter.format(value);
|
|
2209
2261
|
}
|
|
2210
|
-
function
|
|
2211
|
-
|
|
2212
|
-
|
|
2262
|
+
function formatDate(value, type, format, locale) {
|
|
2263
|
+
if (Number.isNaN(value.getTime())) {
|
|
2264
|
+
return String(value);
|
|
2265
|
+
}
|
|
2266
|
+
const formatter = cached(
|
|
2267
|
+
format,
|
|
2268
|
+
`date|${type ?? ""}|${localeKey(locale)}`,
|
|
2269
|
+
() => new Intl.DateTimeFormat(
|
|
2270
|
+
locale,
|
|
2271
|
+
dateOptions(type, format)
|
|
2272
|
+
)
|
|
2273
|
+
);
|
|
2274
|
+
return formatter.format(value);
|
|
2213
2275
|
}
|
|
2214
|
-
function
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
return null;
|
|
2276
|
+
function formatOther(value) {
|
|
2277
|
+
if (typeof value === "function") {
|
|
2278
|
+
return "(function)";
|
|
2218
2279
|
}
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2280
|
+
if (typeof value === "object" && value !== null) {
|
|
2281
|
+
try {
|
|
2282
|
+
return JSON.stringify(value);
|
|
2283
|
+
} catch {
|
|
2284
|
+
return Object.prototype.toString.call(value);
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
return String(value);
|
|
2222
2288
|
}
|
|
2223
|
-
function
|
|
2224
|
-
|
|
2289
|
+
function formatCellValue(value, column, locale, row, messages) {
|
|
2290
|
+
if (column.valueFormatter !== void 0) {
|
|
2291
|
+
return column.valueFormatter(value, row);
|
|
2292
|
+
}
|
|
2293
|
+
if (value === null || value === void 0) {
|
|
2294
|
+
return "";
|
|
2295
|
+
}
|
|
2296
|
+
if (typeof value === "boolean" && messages !== void 0) {
|
|
2297
|
+
return value ? messages.booleanTrue : messages.booleanFalse;
|
|
2298
|
+
}
|
|
2299
|
+
const { type, format } = column;
|
|
2300
|
+
const formatted = format !== void 0 || locale !== void 0;
|
|
2301
|
+
if (value instanceof Date) {
|
|
2302
|
+
return formatted ? formatDate(value, type, format, locale) : value.toLocaleString();
|
|
2303
|
+
}
|
|
2304
|
+
if (formatted && (typeof value === "number" || typeof value === "bigint") && isNumericType(type)) {
|
|
2305
|
+
return formatNumber(value, type, format, locale);
|
|
2306
|
+
}
|
|
2307
|
+
if (typeof value === "string") {
|
|
2308
|
+
return value;
|
|
2309
|
+
}
|
|
2310
|
+
return formatOther(value);
|
|
2225
2311
|
}
|
|
2226
|
-
function
|
|
2227
|
-
|
|
2228
|
-
|
|
2312
|
+
function aggregateFormatColumn(value, spec, column) {
|
|
2313
|
+
const plain = {
|
|
2314
|
+
field: spec.columnId,
|
|
2315
|
+
type: "number"
|
|
2316
|
+
};
|
|
2317
|
+
if (column === void 0 || spec.fn === "count" || spec.fn === "countDistinct") {
|
|
2318
|
+
return plain;
|
|
2319
|
+
}
|
|
2320
|
+
const numeric = typeof value === "number" || typeof value === "bigint";
|
|
2321
|
+
return column.valueFormatter === void 0 && numeric && !isNumericType(column.type) ? plain : column;
|
|
2322
|
+
}
|
|
2323
|
+
function formatAggregateValue(value, spec, column, locale) {
|
|
2324
|
+
const target = aggregateFormatColumn(value, spec, column);
|
|
2325
|
+
if (typeof value === "number" && target.valueFormatter === void 0 && target.format === void 0 && locale === void 0) {
|
|
2326
|
+
return value.toLocaleString();
|
|
2327
|
+
}
|
|
2328
|
+
return formatCellValue(value, target, locale);
|
|
2329
|
+
}
|
|
2330
|
+
function formatGroupValue(value, column, locale, messages) {
|
|
2331
|
+
return value === null || value === void 0 ? messages.blankGroupValue : formatCellValue(value, column, locale, void 0, messages);
|
|
2332
|
+
}
|
|
2333
|
+
|
|
2334
|
+
// src/export/buildExportTable.ts
|
|
2335
|
+
var EMPTY_CELL = { value: null, text: "" };
|
|
2336
|
+
function isDataRow(entry) {
|
|
2337
|
+
return !("kind" in entry);
|
|
2338
|
+
}
|
|
2339
|
+
function pickColumns(view, selection) {
|
|
2340
|
+
let picked;
|
|
2341
|
+
if (selection === "visible") {
|
|
2342
|
+
picked = view.columns;
|
|
2343
|
+
} else if (selection === "all") {
|
|
2344
|
+
picked = view.dataColumns;
|
|
2345
|
+
} else {
|
|
2346
|
+
const byId = new Map(view.dataColumns.map((entry) => [entry.id, entry]));
|
|
2347
|
+
picked = [...new Set(selection)].flatMap((id) => byId.get(id) ?? []);
|
|
2348
|
+
}
|
|
2349
|
+
return picked.filter((entry) => entry.column.exportable !== false);
|
|
2350
|
+
}
|
|
2351
|
+
function describeColumns(picked, source) {
|
|
2352
|
+
const shown = new Map(source.view.columns.map((entry) => [entry.id, entry]));
|
|
2353
|
+
const resolved = resolveColumnWidths(
|
|
2354
|
+
picked.map((entry) => entry.column),
|
|
2355
|
+
source.state.columnSizing,
|
|
2356
|
+
{ sizes: source.options.columnSizeDefaults }
|
|
2229
2357
|
);
|
|
2358
|
+
return resolved.map((entry) => {
|
|
2359
|
+
const rendered = shown.get(entry.id) ?? entry;
|
|
2360
|
+
return {
|
|
2361
|
+
id: rendered.id,
|
|
2362
|
+
header: resolveColumnHeaderText(rendered.column),
|
|
2363
|
+
type: rendered.column.type ?? "string",
|
|
2364
|
+
format: rendered.column.format,
|
|
2365
|
+
alignment: rendered.alignment,
|
|
2366
|
+
width: rendered.width
|
|
2367
|
+
};
|
|
2368
|
+
});
|
|
2230
2369
|
}
|
|
2231
|
-
function
|
|
2232
|
-
|
|
2370
|
+
function scopedRows(source, scope) {
|
|
2371
|
+
const { view, state } = source;
|
|
2372
|
+
switch (scope) {
|
|
2373
|
+
case "all":
|
|
2374
|
+
return view.rows;
|
|
2375
|
+
case "page":
|
|
2376
|
+
return view.displayDataRows;
|
|
2377
|
+
case "selection": {
|
|
2378
|
+
const selected = new Set(state.rowSelection);
|
|
2379
|
+
return view.rows.filter((entry) => selected.has(entry.rowId));
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2233
2382
|
}
|
|
2234
|
-
function
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2383
|
+
function typedValue(value, text) {
|
|
2384
|
+
if (value === null || value === void 0) {
|
|
2385
|
+
return null;
|
|
2386
|
+
}
|
|
2387
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value instanceof Date ? value : text;
|
|
2388
|
+
}
|
|
2389
|
+
function dataCell(row, column, context) {
|
|
2390
|
+
const raw = accessDotted(row, column.field);
|
|
2391
|
+
const text = formatCellValue(
|
|
2392
|
+
raw,
|
|
2393
|
+
column,
|
|
2394
|
+
context.locale,
|
|
2395
|
+
row,
|
|
2396
|
+
context.messages
|
|
2397
|
+
);
|
|
2398
|
+
return {
|
|
2399
|
+
value: column.exportValue === void 0 ? typedValue(raw, text) : column.exportValue(raw, row),
|
|
2400
|
+
text
|
|
2401
|
+
};
|
|
2402
|
+
}
|
|
2403
|
+
function aggregateCell(results, specs, column, locale) {
|
|
2404
|
+
const parts = specs.flatMap((spec) => {
|
|
2405
|
+
const value = results.get(spec.id ?? spec.columnId);
|
|
2406
|
+
return value === null || value === void 0 ? [] : [{ value, text: formatAggregateValue(value, spec, column, locale) }];
|
|
2407
|
+
});
|
|
2408
|
+
const [only] = parts;
|
|
2409
|
+
if (only === void 0) {
|
|
2410
|
+
return EMPTY_CELL;
|
|
2411
|
+
}
|
|
2412
|
+
if (parts.length === 1) {
|
|
2413
|
+
return { value: typedValue(only.value, only.text), text: only.text };
|
|
2414
|
+
}
|
|
2415
|
+
const text = parts.map((part) => part.text).join(", ");
|
|
2416
|
+
return { value: text, text };
|
|
2417
|
+
}
|
|
2418
|
+
function aggregateCells(results, context) {
|
|
2419
|
+
return context.columns.map((entry) => {
|
|
2420
|
+
const specs = context.specsByColumn.get(entry.id);
|
|
2421
|
+
return specs === void 0 ? EMPTY_CELL : aggregateCell(results, specs, entry.column, context.locale);
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
2424
|
+
function groupLabel(entry, source) {
|
|
2425
|
+
const column = source.view.dataColumns.find(
|
|
2426
|
+
(candidate) => candidate.id === entry.columnId
|
|
2427
|
+
)?.column ?? { field: entry.columnId };
|
|
2428
|
+
const value = formatGroupValue(
|
|
2429
|
+
entry.value,
|
|
2430
|
+
column,
|
|
2431
|
+
source.options.locale,
|
|
2432
|
+
source.messages
|
|
2433
|
+
);
|
|
2434
|
+
return `${resolveColumnHeaderText(column)}: ${value}`;
|
|
2435
|
+
}
|
|
2436
|
+
function toExportRow(entry, source, context) {
|
|
2437
|
+
if (isDataRow(entry)) {
|
|
2438
|
+
return {
|
|
2439
|
+
kind: "data",
|
|
2440
|
+
rowId: entry.rowId,
|
|
2441
|
+
cells: context.columns.map(
|
|
2442
|
+
(column) => dataCell(entry.row, column.column, context)
|
|
2443
|
+
)
|
|
2444
|
+
};
|
|
2445
|
+
}
|
|
2446
|
+
if (entry.kind === "group") {
|
|
2447
|
+
return {
|
|
2448
|
+
kind: "group",
|
|
2449
|
+
level: entry.level,
|
|
2450
|
+
groupId: entry.groupId,
|
|
2451
|
+
label: groupLabel(entry, source),
|
|
2452
|
+
count: entry.count,
|
|
2453
|
+
cells: context.display === "inline" ? aggregateCells(entry.aggregates, context) : context.columns.map(() => EMPTY_CELL)
|
|
2247
2454
|
};
|
|
2248
2455
|
}
|
|
2249
2456
|
return {
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
isVisible: (context) => find(context) !== null,
|
|
2254
|
-
isEnabled: isEnabled === void 0 ? void 0 : test(isEnabled),
|
|
2255
|
-
isChecked: isChecked === void 0 ? void 0 : test(isChecked),
|
|
2256
|
-
execute: (context) => {
|
|
2257
|
-
const column = find(context);
|
|
2258
|
-
if (column !== null) {
|
|
2259
|
-
execute(column, context);
|
|
2260
|
-
}
|
|
2261
|
-
}
|
|
2457
|
+
kind: "groupSummary",
|
|
2458
|
+
level: entry.level,
|
|
2459
|
+
cells: aggregateCells(entry.aggregates, context)
|
|
2262
2460
|
};
|
|
2263
2461
|
}
|
|
2264
|
-
function
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
actions.setColumnSort([{ columnId: column.id, direction }]);
|
|
2462
|
+
function specsByColumnOf(specs) {
|
|
2463
|
+
const byColumn = /* @__PURE__ */ new Map();
|
|
2464
|
+
for (const spec of specs) {
|
|
2465
|
+
const owned = byColumn.get(spec.columnId);
|
|
2466
|
+
if (owned === void 0) {
|
|
2467
|
+
byColumn.set(spec.columnId, [spec]);
|
|
2468
|
+
} else {
|
|
2469
|
+
owned.push(spec);
|
|
2273
2470
|
}
|
|
2274
|
-
}
|
|
2471
|
+
}
|
|
2472
|
+
return byColumn;
|
|
2275
2473
|
}
|
|
2276
|
-
function
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
id: "group.remove",
|
|
2313
|
-
label: (context) => context.messages.removeGroupBy(columnName(context)),
|
|
2314
|
-
icon: "group",
|
|
2315
|
-
find: groupedColumn,
|
|
2316
|
-
execute: (column, { actions }) => {
|
|
2317
|
-
actions.toggleGroupBy(column.id);
|
|
2318
|
-
}
|
|
2319
|
-
}),
|
|
2320
|
-
{
|
|
2321
|
-
id: "group.clearAll",
|
|
2322
|
-
label: ({ messages }) => messages.ungroupAll,
|
|
2323
|
-
isVisible: (context) => isOn(context, ["grid", "toolbar"]) && context.state.groupBy.length > 0,
|
|
2324
|
-
execute: ({ actions }) => {
|
|
2325
|
-
actions.setGroupBy([]);
|
|
2326
|
-
}
|
|
2327
|
-
},
|
|
2328
|
-
{
|
|
2329
|
-
id: "group.expandAll",
|
|
2330
|
-
label: ({ messages }) => messages.expandAllGroups,
|
|
2331
|
-
isVisible: (context) => isOn(context, ["grid", "toolbar", "group", "cell"]) && context.state.groupBy.length > 0,
|
|
2332
|
-
execute: ({ actions }) => {
|
|
2333
|
-
actions.expandAllGroups();
|
|
2334
|
-
}
|
|
2335
|
-
},
|
|
2336
|
-
{
|
|
2337
|
-
id: "group.collapseAll",
|
|
2338
|
-
label: ({ messages }) => messages.collapseAllGroups,
|
|
2339
|
-
isVisible: (context) => isOn(context, ["grid", "toolbar", "group", "cell"]) && context.state.groupBy.length > 0,
|
|
2340
|
-
execute: ({ actions }) => {
|
|
2341
|
-
actions.collapseAllGroups();
|
|
2342
|
-
}
|
|
2343
|
-
},
|
|
2344
|
-
columnCommand({
|
|
2345
|
-
id: "column.hide",
|
|
2346
|
-
label: ({ messages }) => messages.hideColumn,
|
|
2347
|
-
icon: "hide",
|
|
2348
|
-
find: hideableColumn,
|
|
2349
|
-
// The last column shown stays: a grid with none has no header left to
|
|
2350
|
-
// bring one back from.
|
|
2351
|
-
isEnabled: (column, { view }) => view.columns.some((entry) => entry.id !== column.id),
|
|
2352
|
-
execute: (column, { actions }) => {
|
|
2353
|
-
actions.hideColumns([column.id]);
|
|
2354
|
-
}
|
|
2355
|
-
}),
|
|
2356
|
-
columnCommand({
|
|
2357
|
-
id: "column.autoFit",
|
|
2358
|
-
label: ({ messages }) => messages.autoFitColumn,
|
|
2359
|
-
icon: "autoFit",
|
|
2360
|
-
find: resizableColumn,
|
|
2361
|
-
execute: (column, { host }) => {
|
|
2362
|
-
host.autoFitColumns([column.id]);
|
|
2363
|
-
}
|
|
2364
|
-
}),
|
|
2365
|
-
{
|
|
2366
|
-
id: "column.autoFitAll",
|
|
2367
|
-
label: ({ messages }) => messages.autoFitAllColumns,
|
|
2368
|
-
icon: "autoFit",
|
|
2369
|
-
isVisible: (context) => isOn(context, ["grid", "toolbar", "header"]) && resizableColumnIds(context).length > 0,
|
|
2370
|
-
execute: (context) => {
|
|
2371
|
-
context.host.autoFitColumns(resizableColumnIds(context));
|
|
2372
|
-
}
|
|
2373
|
-
},
|
|
2374
|
-
// The target goes along, so the renderer opens the chooser where it was
|
|
2375
|
-
// asked for: beside the toolbar button, below the header, or at the
|
|
2376
|
-
// grid's corner for a call from nowhere in particular.
|
|
2377
|
-
{
|
|
2378
|
-
id: "column.choose",
|
|
2379
|
-
label: ({ messages }) => messages.chooseColumns,
|
|
2380
|
-
isVisible: (context) => isOn(context, ["grid", "toolbar", "header"]),
|
|
2381
|
-
execute: ({ host, target }) => {
|
|
2382
|
-
host.openPanel("columnChooser", target);
|
|
2383
|
-
}
|
|
2384
|
-
},
|
|
2385
|
-
{
|
|
2386
|
-
id: "selection.selectAllRows",
|
|
2387
|
-
label: ({ messages }) => messages.selectAllRows,
|
|
2388
|
-
isVisible: (context) => isOn(context, ["grid", "cell"]) && context.options.selectable?.rows === "multiple",
|
|
2389
|
-
isEnabled: ({ view }) => view.displayDataRows.length > 0,
|
|
2390
|
-
execute: ({ actions }) => {
|
|
2391
|
-
actions.selectAllRows();
|
|
2392
|
-
}
|
|
2393
|
-
},
|
|
2394
|
-
{
|
|
2395
|
-
id: "selection.clear",
|
|
2396
|
-
label: ({ messages }) => messages.clearSelection,
|
|
2397
|
-
isVisible: (context) => isOn(context, ["grid", "cell", "header"]) && isAnythingSelectable(context.options.selectable),
|
|
2398
|
-
isEnabled: ({ state }) => state.rowSelection.length > 0 || state.columnSelection.length > 0 || state.cellSelection !== null,
|
|
2399
|
-
execute: ({ actions }) => {
|
|
2400
|
-
actions.clearSelection();
|
|
2401
|
-
}
|
|
2402
|
-
},
|
|
2403
|
-
{
|
|
2404
|
-
id: "filter.clearAll",
|
|
2405
|
-
label: ({ messages }) => messages.clearFilters,
|
|
2406
|
-
isVisible: (context) => isOn(context, ["grid", "toolbar", "header"]),
|
|
2407
|
-
isEnabled: ({ state }) => state.filter.length > 0,
|
|
2408
|
-
execute: ({ actions }) => {
|
|
2409
|
-
actions.setFilter([]);
|
|
2410
|
-
}
|
|
2411
|
-
}
|
|
2412
|
-
];
|
|
2474
|
+
function buildExportTable(source, options = {}) {
|
|
2475
|
+
const { view, state } = source;
|
|
2476
|
+
const specs = options.includeAggregates ?? true ? source.options.aggregates ?? [] : [];
|
|
2477
|
+
const columns = pickColumns(view, options.columns ?? "visible");
|
|
2478
|
+
const context = {
|
|
2479
|
+
columns,
|
|
2480
|
+
specsByColumn: specsByColumnOf(specs),
|
|
2481
|
+
display: source.options.groupAggregateDisplay ?? "inline",
|
|
2482
|
+
locale: source.options.locale,
|
|
2483
|
+
messages: source.messages
|
|
2484
|
+
};
|
|
2485
|
+
const rows = scopedRows(source, options.scope ?? "all");
|
|
2486
|
+
const grouped = groupRows(rows, state.groupBy, [], view.dataColumns);
|
|
2487
|
+
const displayRows = options.includeGroups ?? true ? withGroupAggregates(
|
|
2488
|
+
grouped,
|
|
2489
|
+
rows,
|
|
2490
|
+
state.groupBy,
|
|
2491
|
+
specs,
|
|
2492
|
+
view.dataColumns,
|
|
2493
|
+
context.display
|
|
2494
|
+
) : grouped.filter(isDataRow);
|
|
2495
|
+
let footer = null;
|
|
2496
|
+
if (specs.length > 0) {
|
|
2497
|
+
const totals = rows === view.rows ? view.grandTotal : computeAggregates(
|
|
2498
|
+
rows.map((entry) => entry.row),
|
|
2499
|
+
specs,
|
|
2500
|
+
view.dataColumns
|
|
2501
|
+
);
|
|
2502
|
+
footer = { kind: "total", cells: aggregateCells(totals, context) };
|
|
2503
|
+
}
|
|
2504
|
+
return {
|
|
2505
|
+
columns: describeColumns(columns, source),
|
|
2506
|
+
header: options.includeHeader ?? true,
|
|
2507
|
+
rows: displayRows.map((entry) => toExportRow(entry, source, context)),
|
|
2508
|
+
footer
|
|
2509
|
+
};
|
|
2413
2510
|
}
|
|
2414
2511
|
|
|
2415
|
-
// src/
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2512
|
+
// src/export/htmlTable.ts
|
|
2513
|
+
var ENTITIES = {
|
|
2514
|
+
"&": "&",
|
|
2515
|
+
"<": "<",
|
|
2516
|
+
">": ">",
|
|
2517
|
+
'"': """,
|
|
2518
|
+
"'": "'"
|
|
2519
|
+
};
|
|
2520
|
+
function escapeHtml(text) {
|
|
2521
|
+
return text.replace(/[&<>"']/g, (char) => ENTITIES[char] ?? char);
|
|
2522
|
+
}
|
|
2523
|
+
function cellHtml(tag, text, column) {
|
|
2524
|
+
const alignment = column?.alignment ?? "left";
|
|
2525
|
+
const style = alignment === "left" ? "" : ` style="text-align:${alignment}"`;
|
|
2526
|
+
return `<${tag}${style}>${escapeHtml(text)}</${tag}>`;
|
|
2527
|
+
}
|
|
2528
|
+
function cellsHtml(cells, columns) {
|
|
2529
|
+
return cells.map((cell, index) => cellHtml("td", cell.text, columns[index])).join("");
|
|
2530
|
+
}
|
|
2531
|
+
function groupRowHtml(row, columns) {
|
|
2532
|
+
const firstAggregate = row.cells.findIndex((cell) => cell.text !== "");
|
|
2533
|
+
const span = Math.max(
|
|
2534
|
+
1,
|
|
2535
|
+
firstAggregate === -1 ? row.cells.length : firstAggregate
|
|
2425
2536
|
);
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2537
|
+
const colspan = span > 1 ? ` colspan="${String(span)}"` : "";
|
|
2538
|
+
const label = `<td${colspan}>${escapeHtml(row.label)}</td>`;
|
|
2539
|
+
const aggregates = row.cells.slice(span).map((cell, index) => cellHtml("td", cell.text, columns[span + index])).join("");
|
|
2540
|
+
return `<tr class="export-group" data-level="${String(row.level)}">${label}${aggregates}</tr>`;
|
|
2541
|
+
}
|
|
2542
|
+
function rowHtml(row, columns) {
|
|
2543
|
+
switch (row.kind) {
|
|
2544
|
+
case "data":
|
|
2545
|
+
return `<tr>${cellsHtml(row.cells, columns)}</tr>`;
|
|
2546
|
+
case "group":
|
|
2547
|
+
return groupRowHtml(row, columns);
|
|
2548
|
+
case "groupSummary":
|
|
2549
|
+
return `<tr class="export-group-summary" data-level="${String(row.level)}">${cellsHtml(row.cells, columns)}</tr>`;
|
|
2434
2550
|
}
|
|
2435
|
-
return registry;
|
|
2436
2551
|
}
|
|
2437
|
-
function
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
}
|
|
2441
|
-
|
|
2442
|
-
|
|
2552
|
+
function toHtmlTable(table, options = {}) {
|
|
2553
|
+
const { columns } = table;
|
|
2554
|
+
const caption = options.caption === void 0 ? "" : `<caption>${escapeHtml(options.caption)}</caption>`;
|
|
2555
|
+
const head = table.header ? `<thead><tr>${columns.map((column) => cellHtml("th", column.header, column)).join("")}</tr></thead>` : "";
|
|
2556
|
+
const body = `<tbody>${table.rows.map((row) => rowHtml(row, columns)).join("")}</tbody>`;
|
|
2557
|
+
const foot = table.footer === null ? "" : `<tfoot><tr>${cellsHtml(table.footer.cells, columns)}</tr></tfoot>`;
|
|
2558
|
+
return `<table class="gridkit-export-table">${caption}${head}${body}${foot}</table>`;
|
|
2443
2559
|
}
|
|
2444
2560
|
|
|
2445
|
-
// src/
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
if (reported === void 0) {
|
|
2450
|
-
reported = /* @__PURE__ */ new Set();
|
|
2451
|
-
reportedUnknownIds.set(commands, reported);
|
|
2561
|
+
// src/export/records.ts
|
|
2562
|
+
function rawText(value) {
|
|
2563
|
+
if (value === null) {
|
|
2564
|
+
return "";
|
|
2452
2565
|
}
|
|
2453
|
-
if (
|
|
2454
|
-
return;
|
|
2566
|
+
if (value instanceof Date) {
|
|
2567
|
+
return Number.isNaN(value.getTime()) ? "" : value.toISOString();
|
|
2455
2568
|
}
|
|
2456
|
-
|
|
2457
|
-
reportError(
|
|
2458
|
-
`the menu or toolbar entry \`${id}\` names no command, so it is left out.`
|
|
2459
|
-
);
|
|
2460
|
-
}
|
|
2461
|
-
function evaluate(value, context) {
|
|
2462
|
-
return typeof value === "function" ? value(context) : value;
|
|
2569
|
+
return String(value);
|
|
2463
2570
|
}
|
|
2464
|
-
function
|
|
2465
|
-
if (!isCommandVisible(command, context)) {
|
|
2466
|
-
return null;
|
|
2467
|
-
}
|
|
2571
|
+
function cellField(cell, values) {
|
|
2468
2572
|
return {
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
label: evaluate(command.label, context),
|
|
2472
|
-
icon: command.icon,
|
|
2473
|
-
shortcut: command.shortcut,
|
|
2474
|
-
disabled: !isCommandEnabled(command, context),
|
|
2475
|
-
checked: command.isChecked?.(context),
|
|
2476
|
-
run: () => runCommand(command, context)
|
|
2573
|
+
text: values === "raw" ? rawText(cell.value) : cell.text,
|
|
2574
|
+
isNumber: typeof cell.value === "number"
|
|
2477
2575
|
};
|
|
2478
2576
|
}
|
|
2479
|
-
function
|
|
2480
|
-
|
|
2577
|
+
function textField(text) {
|
|
2578
|
+
return { text, isNumber: false };
|
|
2579
|
+
}
|
|
2580
|
+
function textRecords(table, values) {
|
|
2581
|
+
const records = [];
|
|
2582
|
+
if (table.header) {
|
|
2583
|
+
records.push(table.columns.map((column) => textField(column.header)));
|
|
2584
|
+
}
|
|
2585
|
+
for (const row of table.rows) {
|
|
2586
|
+
records.push(
|
|
2587
|
+
row.cells.map(
|
|
2588
|
+
(cell, index) => row.kind === "group" && index === 0 ? textField(row.label) : cellField(cell, values)
|
|
2589
|
+
)
|
|
2590
|
+
);
|
|
2591
|
+
}
|
|
2592
|
+
if (table.footer !== null) {
|
|
2593
|
+
records.push(table.footer.cells.map((cell) => cellField(cell, values)));
|
|
2594
|
+
}
|
|
2595
|
+
return records;
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2598
|
+
// src/export/tsv.ts
|
|
2599
|
+
var SEPARATORS = /\r\n|[\t\r\n]/g;
|
|
2600
|
+
function toTsv(table, options = {}) {
|
|
2601
|
+
return textRecords(table, options.values ?? "text").map(
|
|
2602
|
+
(record) => record.map((field) => field.text.replace(SEPARATORS, " ")).join(" ")
|
|
2603
|
+
).join("\n");
|
|
2604
|
+
}
|
|
2605
|
+
|
|
2606
|
+
// src/export/copy.ts
|
|
2607
|
+
function isCopyable(column) {
|
|
2608
|
+
return column.column.exportable !== false;
|
|
2609
|
+
}
|
|
2610
|
+
function hasRow(source, rowId) {
|
|
2611
|
+
return source.view.rows.some((entry) => entry.rowId === rowId);
|
|
2612
|
+
}
|
|
2613
|
+
function hasColumn(source, columnId) {
|
|
2614
|
+
return source.view.columns.some(
|
|
2615
|
+
(entry) => entry.id === columnId && isCopyable(entry)
|
|
2616
|
+
);
|
|
2617
|
+
}
|
|
2618
|
+
function selectionCopy(source) {
|
|
2619
|
+
const { state, view } = source;
|
|
2620
|
+
const selectedRows = new Set(state.rowSelection);
|
|
2621
|
+
const count = view.rows.filter(
|
|
2622
|
+
(entry) => selectedRows.has(entry.rowId)
|
|
2623
|
+
).length;
|
|
2624
|
+
if (count > 0) {
|
|
2625
|
+
return { kind: "rows", count };
|
|
2626
|
+
}
|
|
2627
|
+
const selectedColumns = new Set(state.columnSelection);
|
|
2628
|
+
const columnIds = view.columns.filter((entry) => selectedColumns.has(entry.id) && isCopyable(entry)).map((entry) => entry.id);
|
|
2629
|
+
if (columnIds.length > 0 && view.rows.length > 0) {
|
|
2630
|
+
return { kind: "columns", columnIds };
|
|
2631
|
+
}
|
|
2632
|
+
const cell = state.cellSelection;
|
|
2633
|
+
if (cell !== null && hasRow(source, cell.rowId) && hasColumn(source, cell.columnId)) {
|
|
2634
|
+
return { kind: "cell", rowId: cell.rowId, columnId: cell.columnId };
|
|
2635
|
+
}
|
|
2636
|
+
return null;
|
|
2637
|
+
}
|
|
2638
|
+
function canCopySelection(source) {
|
|
2639
|
+
return selectionCopy(source) !== null;
|
|
2640
|
+
}
|
|
2641
|
+
function withRows(source, rowIds) {
|
|
2642
|
+
return { ...source, state: { ...source.state, rowSelection: rowIds } };
|
|
2643
|
+
}
|
|
2644
|
+
function contentOf(source, { scope, columns, header }, unit) {
|
|
2645
|
+
const table = buildExportTable(source, {
|
|
2646
|
+
scope,
|
|
2647
|
+
columns: columns ?? "visible",
|
|
2648
|
+
includeHeader: header,
|
|
2649
|
+
includeGroups: false,
|
|
2650
|
+
includeAggregates: false
|
|
2651
|
+
});
|
|
2652
|
+
if (table.columns.length === 0 || table.rows.length === 0) {
|
|
2481
2653
|
return null;
|
|
2482
2654
|
}
|
|
2483
|
-
const disabled = evaluate(entry.disabled ?? false, context);
|
|
2484
2655
|
return {
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
shortcut: entry.shortcut,
|
|
2490
|
-
disabled,
|
|
2491
|
-
checked: entry.checked === void 0 ? void 0 : evaluate(entry.checked, context),
|
|
2492
|
-
run: () => {
|
|
2493
|
-
if (disabled) {
|
|
2494
|
-
return false;
|
|
2495
|
-
}
|
|
2496
|
-
entry.onSelect(context);
|
|
2497
|
-
return true;
|
|
2498
|
-
}
|
|
2656
|
+
text: toTsv(table),
|
|
2657
|
+
html: toHtmlTable(table),
|
|
2658
|
+
count: unit === "columns" ? table.columns.length : table.rows.length,
|
|
2659
|
+
unit
|
|
2499
2660
|
};
|
|
2500
2661
|
}
|
|
2501
|
-
function
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
const previous = collapsed.at(-1);
|
|
2505
|
-
if (entry.type !== "separator") {
|
|
2506
|
-
if (isEdge(entry) && previous?.type === "separator") {
|
|
2507
|
-
collapsed.pop();
|
|
2508
|
-
}
|
|
2509
|
-
collapsed.push(entry);
|
|
2510
|
-
} else if (previous !== void 0 && previous.type !== "separator" && !isEdge(previous)) {
|
|
2511
|
-
collapsed.push(entry);
|
|
2512
|
-
}
|
|
2513
|
-
}
|
|
2514
|
-
if (collapsed.at(-1)?.type === "separator") {
|
|
2515
|
-
collapsed.pop();
|
|
2662
|
+
function cellContent(source, rowId, columnId) {
|
|
2663
|
+
if (!hasRow(source, rowId) || !hasColumn(source, columnId)) {
|
|
2664
|
+
return null;
|
|
2516
2665
|
}
|
|
2517
|
-
return
|
|
2666
|
+
return contentOf(
|
|
2667
|
+
withRows(source, [rowId]),
|
|
2668
|
+
{ scope: "selection", columns: [columnId], header: false },
|
|
2669
|
+
"cells"
|
|
2670
|
+
);
|
|
2518
2671
|
}
|
|
2519
|
-
function
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
return { type: "separator", key: `separator-${String(index)}` };
|
|
2672
|
+
function rowContent(source, rowId) {
|
|
2673
|
+
if (!hasRow(source, rowId)) {
|
|
2674
|
+
return null;
|
|
2523
2675
|
}
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2676
|
+
return contentOf(
|
|
2677
|
+
withRows(source, [rowId]),
|
|
2678
|
+
{ scope: "selection", header: false },
|
|
2679
|
+
"rows"
|
|
2680
|
+
);
|
|
2681
|
+
}
|
|
2682
|
+
function selectionContent(source) {
|
|
2683
|
+
const selection = selectionCopy(source);
|
|
2684
|
+
switch (selection?.kind) {
|
|
2685
|
+
case void 0:
|
|
2528
2686
|
return null;
|
|
2529
|
-
|
|
2530
|
-
|
|
2687
|
+
case "rows":
|
|
2688
|
+
return contentOf(
|
|
2689
|
+
source,
|
|
2690
|
+
{ scope: "selection", header: selection.count > 1 },
|
|
2691
|
+
"rows"
|
|
2692
|
+
);
|
|
2693
|
+
case "columns":
|
|
2694
|
+
return contentOf(
|
|
2695
|
+
source,
|
|
2696
|
+
{ scope: "all", columns: selection.columnIds, header: true },
|
|
2697
|
+
"columns"
|
|
2698
|
+
);
|
|
2699
|
+
case "cell":
|
|
2700
|
+
return cellContent(source, selection.rowId, selection.columnId);
|
|
2531
2701
|
}
|
|
2532
|
-
return entry.type === "item" ? inlineItem(entry, context) : resolveSubmenu(entry, options);
|
|
2533
2702
|
}
|
|
2534
|
-
function
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
}
|
|
2703
|
+
function copyContent(source, request) {
|
|
2704
|
+
switch (request.kind) {
|
|
2705
|
+
case "cell":
|
|
2706
|
+
return cellContent(source, request.rowId, request.columnId);
|
|
2707
|
+
case "row":
|
|
2708
|
+
return rowContent(source, request.rowId);
|
|
2709
|
+
case "selection":
|
|
2710
|
+
return selectionContent(source);
|
|
2711
|
+
}
|
|
2543
2712
|
}
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2713
|
+
|
|
2714
|
+
// src/export/exportFile.ts
|
|
2715
|
+
function withExtension(fileName, extension) {
|
|
2716
|
+
const suffix = `.${extension}`;
|
|
2717
|
+
return fileName.toLowerCase().endsWith(suffix.toLowerCase()) ? fileName : fileName + suffix;
|
|
2718
|
+
}
|
|
2719
|
+
function writerFor(format, writers) {
|
|
2720
|
+
if (typeof format !== "string") {
|
|
2721
|
+
return format;
|
|
2722
|
+
}
|
|
2723
|
+
const writer = writers.get(format);
|
|
2724
|
+
if (writer === void 0) {
|
|
2725
|
+
reportError(
|
|
2726
|
+
`there is no export writer for \`${format}\` \u2014 register one through \`exportWriters\`.`
|
|
2727
|
+
);
|
|
2728
|
+
return null;
|
|
2729
|
+
}
|
|
2730
|
+
return writer;
|
|
2731
|
+
}
|
|
2732
|
+
function fileNameFor(options, writer) {
|
|
2733
|
+
const { fileName = "export" } = options;
|
|
2734
|
+
const name = typeof fileName === "function" ? fileName(writer.format) : fileName;
|
|
2735
|
+
return withExtension(name, writer.fileExtension);
|
|
2736
|
+
}
|
|
2737
|
+
function exportFile(source, format, options, writers) {
|
|
2738
|
+
const writer = writerFor(format, writers);
|
|
2739
|
+
if (writer === null) {
|
|
2740
|
+
return null;
|
|
2741
|
+
}
|
|
2742
|
+
const content = writer.write(buildExportTable(source, options), {
|
|
2743
|
+
locale: source.options.locale,
|
|
2744
|
+
messages: source.messages,
|
|
2745
|
+
options
|
|
2746
|
+
});
|
|
2747
|
+
const fileName = fileNameFor(options, writer);
|
|
2748
|
+
const { mimeType } = writer;
|
|
2749
|
+
return content instanceof Promise ? content.then((resolved) => ({ fileName, mimeType, content: resolved })) : { fileName, mimeType, content };
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
// src/export/print.ts
|
|
2753
|
+
var PORTRAIT_COLUMN_LIMIT = 6;
|
|
2754
|
+
function resolveOrientation(table, options) {
|
|
2755
|
+
return options.orientation ?? (table.columns.length > PORTRAIT_COLUMN_LIMIT ? "landscape" : "portrait");
|
|
2756
|
+
}
|
|
2757
|
+
function timestampText(timestamp, locale) {
|
|
2758
|
+
const date = timestamp === true ? /* @__PURE__ */ new Date() : timestamp;
|
|
2759
|
+
return new Intl.DateTimeFormat(locale, {
|
|
2760
|
+
dateStyle: "medium",
|
|
2761
|
+
timeStyle: "short"
|
|
2762
|
+
}).format(date);
|
|
2763
|
+
}
|
|
2764
|
+
function line(tag, className, text) {
|
|
2765
|
+
return text === void 0 || text === "" ? "" : `<${tag} class="${className}">${escapeHtml(text)}</${tag}>`;
|
|
2766
|
+
}
|
|
2767
|
+
function headerHtml(options) {
|
|
2768
|
+
const { timestamp = false } = options;
|
|
2769
|
+
const lines = line("h1", "print-title", options.title) + line("p", "print-subtitle", options.subtitle) + (timestamp === false ? "" : line("p", "print-timestamp", timestampText(timestamp, options.locale)));
|
|
2770
|
+
return lines === "" ? "" : `<header class="gridkit-print-header">${lines}</header>`;
|
|
2771
|
+
}
|
|
2772
|
+
function buildPrintDocument(table, options = {}) {
|
|
2773
|
+
const footer = line("footer", "gridkit-print-footer", options.footer);
|
|
2774
|
+
return {
|
|
2775
|
+
title: options.title ?? "",
|
|
2776
|
+
bodyHtml: `${headerHtml(options)}${toHtmlTable(table)}${footer}`,
|
|
2777
|
+
page: {
|
|
2778
|
+
paperSize: options.paperSize ?? "A4",
|
|
2779
|
+
orientation: resolveOrientation(table, options),
|
|
2780
|
+
pageNumbers: options.pageNumbers ?? true,
|
|
2781
|
+
fitToWidth: options.fitToWidth ?? false
|
|
2782
|
+
}
|
|
2783
|
+
};
|
|
2784
|
+
}
|
|
2785
|
+
function exportPrintDocument(source, options = {}) {
|
|
2786
|
+
return buildPrintDocument(buildExportTable(source, options), {
|
|
2787
|
+
...options,
|
|
2788
|
+
title: options.title ?? source.options.label,
|
|
2789
|
+
locale: options.locale ?? source.options.locale
|
|
2790
|
+
});
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2793
|
+
// src/export/csv.ts
|
|
2794
|
+
var BOM = "\uFEFF";
|
|
2795
|
+
var FORMULA_START = /^[=+\-@\t\r]/;
|
|
2796
|
+
var SIGN_START = /^[+-]/;
|
|
2797
|
+
var NEEDS_QUOTES = /["\r\n]/;
|
|
2798
|
+
function autoDelimiter(locale) {
|
|
2799
|
+
const decimal = new Intl.NumberFormat(locale).formatToParts(1.5).find((part) => part.type === "decimal")?.value;
|
|
2800
|
+
return decimal === "," ? ";" : ",";
|
|
2801
|
+
}
|
|
2802
|
+
function neutralize(field) {
|
|
2803
|
+
const { text } = field;
|
|
2804
|
+
if (!FORMULA_START.test(text) || field.isNumber && SIGN_START.test(text)) {
|
|
2805
|
+
return text;
|
|
2806
|
+
}
|
|
2807
|
+
return `'${text}`;
|
|
2808
|
+
}
|
|
2809
|
+
function quote(text, delimiter) {
|
|
2810
|
+
return text.includes(delimiter) || NEEDS_QUOTES.test(text) ? `"${text.replaceAll('"', '""')}"` : text;
|
|
2811
|
+
}
|
|
2812
|
+
function toCsv(table, options = {}) {
|
|
2813
|
+
const delimiter = options.delimiter === "auto" ? autoDelimiter(options.locale) : options.delimiter ?? ",";
|
|
2814
|
+
const guard = options.neutralizeFormulas ?? true;
|
|
2815
|
+
const lines = textRecords(table, options.values ?? "text").map(
|
|
2816
|
+
(record) => record.map((field) => quote(guard ? neutralize(field) : field.text, delimiter)).join(delimiter)
|
|
2547
2817
|
);
|
|
2818
|
+
return (options.bom ?? true ? BOM : "") + lines.join(options.eol ?? "\r\n");
|
|
2548
2819
|
}
|
|
2549
2820
|
|
|
2550
|
-
// src/
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2821
|
+
// src/export/json.ts
|
|
2822
|
+
function uniqueNames(names) {
|
|
2823
|
+
const used = /* @__PURE__ */ new Set();
|
|
2824
|
+
return names.map((name) => {
|
|
2825
|
+
let unique = name;
|
|
2826
|
+
for (let n = 2; used.has(unique); n += 1) {
|
|
2827
|
+
unique = `${name} (${String(n)})`;
|
|
2828
|
+
}
|
|
2829
|
+
used.add(unique);
|
|
2830
|
+
return unique;
|
|
2831
|
+
});
|
|
2832
|
+
}
|
|
2833
|
+
function toJson(table, options = {}) {
|
|
2834
|
+
const keys = options.keys === "header" ? uniqueNames(table.columns.map((column) => column.header)) : table.columns.map((column) => column.id);
|
|
2835
|
+
const records = table.rows.flatMap(
|
|
2836
|
+
(row) => row.kind === "data" ? [
|
|
2837
|
+
Object.fromEntries(
|
|
2838
|
+
keys.map((key, index) => [key, row.cells[index]?.value ?? null])
|
|
2839
|
+
)
|
|
2840
|
+
] : []
|
|
2841
|
+
);
|
|
2842
|
+
return JSON.stringify(records, null, options.space);
|
|
2843
|
+
}
|
|
2844
|
+
|
|
2845
|
+
// src/export/writer.ts
|
|
2846
|
+
var csvWriter = {
|
|
2847
|
+
format: "csv",
|
|
2848
|
+
fileExtension: "csv",
|
|
2849
|
+
mimeType: "text/csv;charset=utf-8",
|
|
2850
|
+
write: (table, { locale, options }) => toCsv(table, { locale, ...options.csv })
|
|
2851
|
+
};
|
|
2852
|
+
var jsonWriter = {
|
|
2853
|
+
format: "json",
|
|
2854
|
+
fileExtension: "json",
|
|
2855
|
+
mimeType: "application/json",
|
|
2856
|
+
write: (table, { options }) => toJson(table, options.json)
|
|
2857
|
+
};
|
|
2858
|
+
function resolveExportWriters(writers) {
|
|
2859
|
+
return new Map(
|
|
2860
|
+
[csvWriter, jsonWriter, ...writers ?? []].map((writer) => [
|
|
2861
|
+
writer.format,
|
|
2862
|
+
writer
|
|
2863
|
+
])
|
|
2864
|
+
);
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
// src/filterMenu/operators.ts
|
|
2868
|
+
function filterOperatorFamily(type) {
|
|
2869
|
+
return kindOf(type);
|
|
2870
|
+
}
|
|
2871
|
+
var familyOperators = {
|
|
2872
|
+
text: [
|
|
2873
|
+
"contains",
|
|
2874
|
+
"notContains",
|
|
2875
|
+
"eq",
|
|
2876
|
+
"ne",
|
|
2877
|
+
"startsWith",
|
|
2878
|
+
"endsWith",
|
|
2879
|
+
"isEmpty",
|
|
2880
|
+
"isNotEmpty"
|
|
2565
2881
|
],
|
|
2566
|
-
|
|
2567
|
-
"
|
|
2568
|
-
"
|
|
2569
|
-
"
|
|
2570
|
-
"
|
|
2571
|
-
"
|
|
2882
|
+
number: [
|
|
2883
|
+
"eq",
|
|
2884
|
+
"ne",
|
|
2885
|
+
"gt",
|
|
2886
|
+
"gte",
|
|
2887
|
+
"lt",
|
|
2888
|
+
"lte",
|
|
2889
|
+
"between",
|
|
2890
|
+
"isEmpty",
|
|
2891
|
+
"isNotEmpty"
|
|
2572
2892
|
],
|
|
2573
|
-
|
|
2574
|
-
|
|
2893
|
+
date: ["eq", "ne", "lt", "gt", "between", "isEmpty", "isNotEmpty"],
|
|
2894
|
+
boolean: ["eq", "isEmpty", "isNotEmpty"]
|
|
2575
2895
|
};
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2896
|
+
var instantOperators = [
|
|
2897
|
+
"lt",
|
|
2898
|
+
"gt",
|
|
2899
|
+
"between",
|
|
2900
|
+
"isEmpty",
|
|
2901
|
+
"isNotEmpty"
|
|
2902
|
+
];
|
|
2903
|
+
function defaultFilterOperators(type) {
|
|
2904
|
+
return type === "dateTime" || type === "time" ? instantOperators : familyOperators[filterOperatorFamily(type)];
|
|
2582
2905
|
}
|
|
2583
|
-
function
|
|
2584
|
-
|
|
2585
|
-
|
|
2906
|
+
function filterOperandArity(operator) {
|
|
2907
|
+
switch (operator) {
|
|
2908
|
+
case "isEmpty":
|
|
2909
|
+
case "isNotEmpty":
|
|
2910
|
+
return 0;
|
|
2911
|
+
case "between":
|
|
2912
|
+
return 2;
|
|
2913
|
+
case "eq":
|
|
2914
|
+
case "ne":
|
|
2915
|
+
case "contains":
|
|
2916
|
+
case "notContains":
|
|
2917
|
+
case "startsWith":
|
|
2918
|
+
case "endsWith":
|
|
2919
|
+
case "gt":
|
|
2920
|
+
case "gte":
|
|
2921
|
+
case "lt":
|
|
2922
|
+
case "lte":
|
|
2923
|
+
case "in":
|
|
2924
|
+
return 1;
|
|
2586
2925
|
}
|
|
2587
|
-
return typeof given === "function" ? given(request) ?? [] : given;
|
|
2588
2926
|
}
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2927
|
+
var NUMBER_INPUT = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i;
|
|
2928
|
+
var DATE_INPUT = /^(\d{4})-(\d{2})-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?)?$/;
|
|
2929
|
+
function parseNumber(text) {
|
|
2930
|
+
const value = Number(text);
|
|
2931
|
+
return NUMBER_INPUT.test(text) && Number.isFinite(value) ? value : void 0;
|
|
2932
|
+
}
|
|
2933
|
+
function parseDate(text) {
|
|
2934
|
+
const match = DATE_INPUT.exec(text);
|
|
2935
|
+
if (match === null) {
|
|
2936
|
+
return void 0;
|
|
2592
2937
|
}
|
|
2593
|
-
const
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
defaults: defaultContextMenu.cell,
|
|
2606
|
-
context
|
|
2607
|
-
});
|
|
2608
|
-
case "group":
|
|
2609
|
-
return entriesFor(options.group, {
|
|
2610
|
-
target,
|
|
2611
|
-
defaults: defaultContextMenu.group,
|
|
2612
|
-
context
|
|
2613
|
-
});
|
|
2614
|
-
case "body":
|
|
2615
|
-
return entriesFor(options.body, {
|
|
2616
|
-
target,
|
|
2617
|
-
defaults: defaultContextMenu.body,
|
|
2618
|
-
context
|
|
2619
|
-
});
|
|
2620
|
-
default:
|
|
2621
|
-
return [];
|
|
2938
|
+
const [
|
|
2939
|
+
,
|
|
2940
|
+
year,
|
|
2941
|
+
month,
|
|
2942
|
+
day,
|
|
2943
|
+
hours = "0",
|
|
2944
|
+
minutes = "0",
|
|
2945
|
+
seconds = "0",
|
|
2946
|
+
fraction = ""
|
|
2947
|
+
] = match;
|
|
2948
|
+
if (Number(hours) > 23 || Number(minutes) > 59 || Number(seconds) > 59) {
|
|
2949
|
+
return void 0;
|
|
2622
2950
|
}
|
|
2951
|
+
const date = new Date(
|
|
2952
|
+
Number(year),
|
|
2953
|
+
Number(month) - 1,
|
|
2954
|
+
Number(day),
|
|
2955
|
+
Number(hours),
|
|
2956
|
+
Number(minutes),
|
|
2957
|
+
Number(seconds),
|
|
2958
|
+
Number(fraction.padEnd(3, "0"))
|
|
2959
|
+
);
|
|
2960
|
+
return date.getFullYear() === Number(year) && date.getMonth() === Number(month) - 1 && date.getDate() === Number(day) ? date : void 0;
|
|
2623
2961
|
}
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
const focused = from >= 0 && from < count;
|
|
2628
|
-
switch (move) {
|
|
2629
|
-
case "first":
|
|
2630
|
-
return 0;
|
|
2631
|
-
case "last":
|
|
2632
|
-
return count - 1;
|
|
2633
|
-
case "next":
|
|
2634
|
-
return focused ? from + 1 : 0;
|
|
2635
|
-
case "previous":
|
|
2636
|
-
return focused ? from - 1 : count - 1;
|
|
2962
|
+
function parseBoolean(text) {
|
|
2963
|
+
if (text === "true") {
|
|
2964
|
+
return true;
|
|
2637
2965
|
}
|
|
2966
|
+
return text === "false" ? false : void 0;
|
|
2638
2967
|
}
|
|
2639
|
-
function
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
const start = startIndex(count, from, move);
|
|
2643
|
-
for (let offset = 0; offset < count; offset++) {
|
|
2644
|
-
const index = ((start + step * offset) % count + count) % count;
|
|
2645
|
-
if (entries[index]?.type !== "separator") {
|
|
2646
|
-
return index;
|
|
2647
|
-
}
|
|
2968
|
+
function parseFilterOperand(text, type) {
|
|
2969
|
+
if (text === "") {
|
|
2970
|
+
return void 0;
|
|
2648
2971
|
}
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2972
|
+
switch (filterOperatorFamily(type)) {
|
|
2973
|
+
case "text":
|
|
2974
|
+
return text;
|
|
2975
|
+
case "number":
|
|
2976
|
+
return parseNumber(text.trim());
|
|
2977
|
+
case "date":
|
|
2978
|
+
return parseDate(text.trim());
|
|
2979
|
+
case "boolean":
|
|
2980
|
+
return parseBoolean(text.trim());
|
|
2656
2981
|
}
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2982
|
+
}
|
|
2983
|
+
function pad(value, width = 2) {
|
|
2984
|
+
return String(value).padStart(width, "0");
|
|
2985
|
+
}
|
|
2986
|
+
function dayInput(date) {
|
|
2987
|
+
return `${pad(date.getFullYear(), 4)}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
2988
|
+
}
|
|
2989
|
+
function momentInput(date) {
|
|
2990
|
+
const minute = `${dayInput(date)}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
2991
|
+
const seconds = date.getSeconds();
|
|
2992
|
+
const milliseconds = date.getMilliseconds();
|
|
2993
|
+
if (seconds === 0 && milliseconds === 0) {
|
|
2994
|
+
return minute;
|
|
2666
2995
|
}
|
|
2667
|
-
|
|
2996
|
+
const second = `${minute}:${pad(seconds)}`;
|
|
2997
|
+
return milliseconds === 0 ? second : `${second}.${pad(milliseconds, 3)}`;
|
|
2668
2998
|
}
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
function toolbarEntries(toolbar) {
|
|
2673
|
-
if (toolbar === void 0 || toolbar === false) {
|
|
2674
|
-
return [];
|
|
2999
|
+
function formatFilterOperand(value, type) {
|
|
3000
|
+
if (value === null || value === void 0) {
|
|
3001
|
+
return "";
|
|
2675
3002
|
}
|
|
2676
|
-
|
|
3003
|
+
const date = filterOperatorFamily(type) === "date" ? toDate(value) : void 0;
|
|
3004
|
+
if (date === void 0) {
|
|
3005
|
+
return String(value);
|
|
3006
|
+
}
|
|
3007
|
+
return type === "date" ? dayInput(date) : momentInput(date);
|
|
2677
3008
|
}
|
|
2678
3009
|
|
|
2679
|
-
// src/
|
|
2680
|
-
|
|
2681
|
-
|
|
3010
|
+
// src/filterMenu/options.ts
|
|
3011
|
+
var DEFAULT_FILTER_VALUE_LIMIT = 1e4;
|
|
3012
|
+
function conditionOperators(operators) {
|
|
3013
|
+
return operators.includes("in") ? operators.filter((operator) => operator !== "in") : operators;
|
|
2682
3014
|
}
|
|
2683
|
-
function
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
if (entry === "search") {
|
|
2687
|
-
return { type: "search", key: "search" };
|
|
2688
|
-
}
|
|
2689
|
-
if (entry === "spacer") {
|
|
2690
|
-
return { type: "spacer", key: `spacer-${String(index)}` };
|
|
2691
|
-
}
|
|
2692
|
-
if (typeof entry === "object" && entry.type === "custom") {
|
|
2693
|
-
return { type: "custom", id: entry.id, key: entry.id };
|
|
2694
|
-
}
|
|
2695
|
-
if (entry === "separator") {
|
|
2696
|
-
return { type: "separator", key: `separator-${String(index)}` };
|
|
2697
|
-
}
|
|
2698
|
-
const resolved = resolveMenuEntry(entry, index, {
|
|
2699
|
-
commands,
|
|
2700
|
-
context: contextFor({
|
|
2701
|
-
area: "toolbar",
|
|
2702
|
-
itemId: typeof entry === "string" ? entry : entry.id
|
|
2703
|
-
})
|
|
2704
|
-
});
|
|
2705
|
-
if (resolved === null || resolved.type === "separator") {
|
|
2706
|
-
return resolved;
|
|
2707
|
-
}
|
|
2708
|
-
if (resolved.type === "submenu") {
|
|
2709
|
-
return { ...resolved, type: "menu", key: resolved.id };
|
|
2710
|
-
}
|
|
2711
|
-
const { checked, ...item } = resolved;
|
|
2712
|
-
return { ...item, type: "button", key: item.id, pressed: checked };
|
|
3015
|
+
function resolveColumnFilterMenu(options, column) {
|
|
3016
|
+
if (!(column.filterable ?? options.filterableColumns ?? false)) {
|
|
3017
|
+
return null;
|
|
2713
3018
|
}
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
3019
|
+
const grid = options.filterMenu;
|
|
3020
|
+
const own = column.filterMenu;
|
|
3021
|
+
return {
|
|
3022
|
+
ui: own?.ui ?? grid?.ui ?? "simple",
|
|
3023
|
+
operators: conditionOperators(
|
|
3024
|
+
own?.operators ?? defaultFilterOperators(column.type)
|
|
3025
|
+
),
|
|
3026
|
+
family: filterOperatorFamily(column.type),
|
|
3027
|
+
showCounts: grid?.showCounts ?? false,
|
|
3028
|
+
valueLimit: grid?.valueLimit ?? DEFAULT_FILTER_VALUE_LIMIT
|
|
3029
|
+
};
|
|
2718
3030
|
}
|
|
2719
3031
|
|
|
2720
|
-
// src/
|
|
2721
|
-
function
|
|
2722
|
-
|
|
2723
|
-
available,
|
|
2724
|
-
moreWidth,
|
|
2725
|
-
pinned
|
|
2726
|
-
}) {
|
|
2727
|
-
const total = widths.reduce((sum, width) => sum + width, 0);
|
|
2728
|
-
if (total <= available) {
|
|
2729
|
-
return widths.length;
|
|
2730
|
-
}
|
|
2731
|
-
let used = moreWidth + widths.reduce((sum, width, index) => sum + (pinned[index] ? width : 0), 0);
|
|
2732
|
-
for (const [index, width] of widths.entries()) {
|
|
2733
|
-
if (!pinned[index]) {
|
|
2734
|
-
used += width;
|
|
2735
|
-
if (used > available) {
|
|
2736
|
-
return index;
|
|
2737
|
-
}
|
|
2738
|
-
}
|
|
2739
|
-
}
|
|
2740
|
-
return widths.length;
|
|
3032
|
+
// src/messages.ts
|
|
3033
|
+
function columnCount(count, change) {
|
|
3034
|
+
return count === 1 ? `1 column ${change}` : `${String(count)} columns ${change}`;
|
|
2741
3035
|
}
|
|
2742
|
-
function
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
case "button":
|
|
2748
|
-
return {
|
|
2749
|
-
type: "item",
|
|
2750
|
-
id: item.id,
|
|
2751
|
-
label: item.label,
|
|
2752
|
-
icon: item.icon,
|
|
2753
|
-
shortcut: item.shortcut,
|
|
2754
|
-
disabled: item.disabled,
|
|
2755
|
-
checked: item.pressed,
|
|
2756
|
-
run: item.run
|
|
2757
|
-
};
|
|
2758
|
-
case "menu":
|
|
2759
|
-
return {
|
|
2760
|
-
type: "submenu",
|
|
2761
|
-
id: item.id,
|
|
2762
|
-
label: item.label,
|
|
2763
|
-
icon: item.icon,
|
|
2764
|
-
items: item.items
|
|
2765
|
-
};
|
|
2766
|
-
case "separator":
|
|
2767
|
-
return item;
|
|
2768
|
-
default:
|
|
2769
|
-
return null;
|
|
2770
|
-
}
|
|
2771
|
-
}
|
|
2772
|
-
function splitToolbarItems(items, fit) {
|
|
2773
|
-
const fits = (item, index) => index < fit || isPinnedToolbarItem(item);
|
|
2774
|
-
return {
|
|
2775
|
-
shown: collapseSeparators(
|
|
2776
|
-
items.filter(fits),
|
|
2777
|
-
(item) => item.type === "spacer"
|
|
2778
|
-
),
|
|
2779
|
-
overflow: collapseSeparators(
|
|
2780
|
-
items.filter((item, index) => !fits(item, index)).map(asMenuEntry).filter((entry) => entry !== null)
|
|
2781
|
-
)
|
|
2782
|
-
};
|
|
2783
|
-
}
|
|
2784
|
-
|
|
2785
|
-
// src/toolbar/search.ts
|
|
2786
|
-
function searchTextOf(filter) {
|
|
2787
|
-
const query = filterQueryFor(filter) ?? "";
|
|
2788
|
-
const start = query.startsWith("%") ? 1 : 0;
|
|
2789
|
-
const end = query.endsWith("%") ? -1 : void 0;
|
|
2790
|
-
return query.slice(start, end);
|
|
2791
|
-
}
|
|
2792
|
-
function setSearchText(filter, text) {
|
|
2793
|
-
return setColumnFilter(filter, void 0, text === "" ? "" : `%${text}%`);
|
|
2794
|
-
}
|
|
2795
|
-
|
|
2796
|
-
// src/util/navigation.ts
|
|
2797
|
-
var HEADER_ROW = -1;
|
|
2798
|
-
function clampFocus(focus, rowCount, columnCount2) {
|
|
2799
|
-
return {
|
|
2800
|
-
rowIndex: Math.min(Math.max(focus.rowIndex, HEADER_ROW), rowCount - 1),
|
|
2801
|
-
columnIndex: Math.min(
|
|
2802
|
-
Math.max(focus.columnIndex, 0),
|
|
2803
|
-
Math.max(columnCount2 - 1, 0)
|
|
2804
|
-
)
|
|
2805
|
-
};
|
|
2806
|
-
}
|
|
2807
|
-
function noRowsSkippable() {
|
|
2808
|
-
return false;
|
|
2809
|
-
}
|
|
2810
|
-
function skipSkippableRows(rowIndex, direction, rowCount, isSkippableRow, fallback) {
|
|
2811
|
-
let next = rowIndex;
|
|
2812
|
-
while (next >= 0 && next < rowCount && isSkippableRow(next)) {
|
|
2813
|
-
const stepped = next + direction;
|
|
2814
|
-
if (stepped < HEADER_ROW || stepped >= rowCount) {
|
|
2815
|
-
return fallback;
|
|
2816
|
-
}
|
|
2817
|
-
next = stepped;
|
|
2818
|
-
}
|
|
2819
|
-
return next;
|
|
2820
|
-
}
|
|
2821
|
-
function nextFocusForKey(key, modifiers, focus, rowCount, columnCount2, pageSize, isSkippableRow = noRowsSkippable) {
|
|
2822
|
-
if (modifiers.altKey) {
|
|
2823
|
-
return null;
|
|
2824
|
-
}
|
|
2825
|
-
const { rowIndex, columnIndex } = focus;
|
|
2826
|
-
switch (key) {
|
|
2827
|
-
case "ArrowLeft":
|
|
2828
|
-
if (modifiers.ctrlKey) return null;
|
|
2829
|
-
return { rowIndex, columnIndex: columnIndex - 1 };
|
|
2830
|
-
case "ArrowRight":
|
|
2831
|
-
if (modifiers.ctrlKey) return null;
|
|
2832
|
-
return { rowIndex, columnIndex: columnIndex + 1 };
|
|
2833
|
-
case "ArrowUp":
|
|
2834
|
-
return {
|
|
2835
|
-
rowIndex: skipSkippableRows(
|
|
2836
|
-
rowIndex - 1,
|
|
2837
|
-
-1,
|
|
2838
|
-
rowCount,
|
|
2839
|
-
isSkippableRow,
|
|
2840
|
-
rowIndex
|
|
2841
|
-
),
|
|
2842
|
-
columnIndex
|
|
2843
|
-
};
|
|
2844
|
-
case "ArrowDown":
|
|
2845
|
-
return {
|
|
2846
|
-
rowIndex: skipSkippableRows(
|
|
2847
|
-
rowIndex + 1,
|
|
2848
|
-
1,
|
|
2849
|
-
rowCount,
|
|
2850
|
-
isSkippableRow,
|
|
2851
|
-
rowIndex
|
|
2852
|
-
),
|
|
2853
|
-
columnIndex
|
|
2854
|
-
};
|
|
2855
|
-
// Ctrl takes Home and End to the grid's ends rather than the row's, which
|
|
2856
|
-
// is the one place Ctrl is navigation rather than reorder.
|
|
2857
|
-
case "Home":
|
|
2858
|
-
return modifiers.ctrlKey ? { rowIndex: HEADER_ROW, columnIndex: 0 } : { rowIndex, columnIndex: 0 };
|
|
2859
|
-
case "End":
|
|
2860
|
-
return modifiers.ctrlKey ? {
|
|
2861
|
-
rowIndex: skipSkippableRows(
|
|
2862
|
-
rowCount - 1,
|
|
2863
|
-
-1,
|
|
2864
|
-
rowCount,
|
|
2865
|
-
isSkippableRow,
|
|
2866
|
-
rowIndex
|
|
2867
|
-
),
|
|
2868
|
-
columnIndex: columnCount2 - 1
|
|
2869
|
-
} : { rowIndex, columnIndex: columnCount2 - 1 };
|
|
2870
|
-
case "PageUp":
|
|
2871
|
-
return {
|
|
2872
|
-
rowIndex: skipSkippableRows(
|
|
2873
|
-
rowIndex - pageSize,
|
|
2874
|
-
-1,
|
|
2875
|
-
rowCount,
|
|
2876
|
-
isSkippableRow,
|
|
2877
|
-
rowIndex
|
|
2878
|
-
),
|
|
2879
|
-
columnIndex
|
|
2880
|
-
};
|
|
2881
|
-
case "PageDown":
|
|
2882
|
-
return {
|
|
2883
|
-
rowIndex: skipSkippableRows(
|
|
2884
|
-
rowIndex + pageSize,
|
|
2885
|
-
1,
|
|
2886
|
-
rowCount,
|
|
2887
|
-
isSkippableRow,
|
|
2888
|
-
rowIndex
|
|
2889
|
-
),
|
|
2890
|
-
columnIndex
|
|
2891
|
-
};
|
|
2892
|
-
default:
|
|
2893
|
-
return null;
|
|
2894
|
-
}
|
|
2895
|
-
}
|
|
2896
|
-
|
|
2897
|
-
// src/util/paginationWindow.ts
|
|
2898
|
-
function paginationWindow(currentPage, pageCount, options) {
|
|
2899
|
-
if (pageCount <= 0) {
|
|
2900
|
-
return [];
|
|
2901
|
-
}
|
|
2902
|
-
const boundaryCount = Math.max(0, options?.boundaryCount ?? 1);
|
|
2903
|
-
const siblingCount = Math.max(0, options?.siblingCount ?? 1);
|
|
2904
|
-
const current = Math.min(Math.max(currentPage, 1), pageCount);
|
|
2905
|
-
const pages = /* @__PURE__ */ new Set();
|
|
2906
|
-
for (let page = 1; page <= Math.min(boundaryCount, pageCount); page++) {
|
|
2907
|
-
pages.add(page);
|
|
2908
|
-
}
|
|
2909
|
-
for (let page = Math.max(pageCount - boundaryCount + 1, 1); page <= pageCount; page++) {
|
|
2910
|
-
pages.add(page);
|
|
2911
|
-
}
|
|
2912
|
-
for (let page = Math.max(current - siblingCount, 1); page <= Math.min(current + siblingCount, pageCount); page++) {
|
|
2913
|
-
pages.add(page);
|
|
2914
|
-
}
|
|
2915
|
-
const sorted = Array.from(pages).sort((a, b) => a - b);
|
|
2916
|
-
const entries = [];
|
|
2917
|
-
let previous;
|
|
2918
|
-
for (const page of sorted) {
|
|
2919
|
-
if (previous !== void 0) {
|
|
2920
|
-
if (page - previous === 2) {
|
|
2921
|
-
entries.push(previous + 1);
|
|
2922
|
-
} else if (page - previous > 2) {
|
|
2923
|
-
entries.push("ellipsis");
|
|
2924
|
-
}
|
|
2925
|
-
}
|
|
2926
|
-
entries.push(page);
|
|
2927
|
-
previous = page;
|
|
2928
|
-
}
|
|
2929
|
-
return entries;
|
|
2930
|
-
}
|
|
2931
|
-
|
|
2932
|
-
// src/util/columnChooser.ts
|
|
2933
|
-
function isHideable(column) {
|
|
2934
|
-
return column.column.hideable !== false;
|
|
2935
|
-
}
|
|
2936
|
-
function matchesSearch(label, search) {
|
|
2937
|
-
return label.toLowerCase().includes(search.toLowerCase());
|
|
2938
|
-
}
|
|
2939
|
-
function selectAllStateOf(states) {
|
|
2940
|
-
if (states.length > 0 && states.every(Boolean)) {
|
|
2941
|
-
return "checked";
|
|
2942
|
-
}
|
|
2943
|
-
return states.some(Boolean) ? "mixed" : "unchecked";
|
|
2944
|
-
}
|
|
2945
|
-
function resolveColumnChooser({
|
|
2946
|
-
columns,
|
|
2947
|
-
visibility,
|
|
2948
|
-
search = ""
|
|
2949
|
-
}) {
|
|
2950
|
-
const all = columns.map((column) => ({
|
|
2951
|
-
column,
|
|
2952
|
-
label: resolveColumnHeaderText(column.column),
|
|
2953
|
-
visible: isColumnVisible(column.column, visibility)
|
|
2954
|
-
}));
|
|
2955
|
-
const shownCount = all.filter(({ visible }) => visible).length;
|
|
2956
|
-
const matched = all.filter(({ label }) => matchesSearch(label, search));
|
|
2957
|
-
const entries = matched.map(
|
|
2958
|
-
({ column, label, visible }) => ({
|
|
2959
|
-
columnId: column.id,
|
|
2960
|
-
label,
|
|
2961
|
-
visible,
|
|
2962
|
-
disabled: visible && (!isHideable(column) || shownCount === 1)
|
|
2963
|
-
})
|
|
2964
|
-
);
|
|
2965
|
-
const covered = matched.filter(({ column }) => isHideable(column));
|
|
2966
|
-
const selectAll2 = selectAllStateOf(covered.map(({ visible }) => visible));
|
|
2967
|
-
return {
|
|
2968
|
-
entries,
|
|
2969
|
-
selectAll: selectAll2,
|
|
2970
|
-
toggleAll: toggleAllOf(covered, selectAll2, shownCount)
|
|
2971
|
-
};
|
|
2972
|
-
}
|
|
2973
|
-
function toggleAllOf(covered, selectAll2, shownCount) {
|
|
2974
|
-
const show = selectAll2 !== "checked";
|
|
2975
|
-
const changing = covered.filter(({ visible }) => visible !== show).map(({ column }) => column.id);
|
|
2976
|
-
const columnIds = !show && changing.length === shownCount ? changing.slice(1) : changing;
|
|
2977
|
-
return columnIds.length === 0 ? null : { visible: show, columnIds };
|
|
2978
|
-
}
|
|
2979
|
-
|
|
2980
|
-
// src/messages.ts
|
|
2981
|
-
function columnCount(count, change) {
|
|
2982
|
-
return count === 1 ? `1 column ${change}` : `${String(count)} columns ${change}`;
|
|
2983
|
-
}
|
|
2984
|
-
function searchResults(count) {
|
|
2985
|
-
if (count === 0) {
|
|
2986
|
-
return "No rows found";
|
|
2987
|
-
}
|
|
2988
|
-
return count === 1 ? "1 row found" : `${String(count)} rows found`;
|
|
3036
|
+
function searchResults(count) {
|
|
3037
|
+
if (count === 0) {
|
|
3038
|
+
return "No rows found";
|
|
3039
|
+
}
|
|
3040
|
+
return count === 1 ? "1 row found" : `${String(count)} rows found`;
|
|
2989
3041
|
}
|
|
2990
3042
|
function selectedCount(count, target) {
|
|
2991
3043
|
const noun = target === "rows" ? "row" : "column";
|
|
@@ -2994,6 +3046,52 @@ function selectedCount(count, target) {
|
|
|
2994
3046
|
}
|
|
2995
3047
|
return count === 1 ? `1 ${noun} selected` : `${String(count)} ${noun}s selected`;
|
|
2996
3048
|
}
|
|
3049
|
+
var operatorLabels = {
|
|
3050
|
+
eq: "Equals",
|
|
3051
|
+
ne: "Does not equal",
|
|
3052
|
+
contains: "Contains",
|
|
3053
|
+
notContains: "Does not contain",
|
|
3054
|
+
startsWith: "Starts with",
|
|
3055
|
+
endsWith: "Ends with",
|
|
3056
|
+
gt: "Greater than",
|
|
3057
|
+
gte: "Greater than or equal to",
|
|
3058
|
+
lt: "Less than",
|
|
3059
|
+
lte: "Less than or equal to",
|
|
3060
|
+
between: "Between",
|
|
3061
|
+
in: "Is one of",
|
|
3062
|
+
isEmpty: "Is blank",
|
|
3063
|
+
isNotEmpty: "Is not blank"
|
|
3064
|
+
};
|
|
3065
|
+
var dateOperatorLabels = {
|
|
3066
|
+
lt: "Before",
|
|
3067
|
+
lte: "On or before",
|
|
3068
|
+
gt: "After",
|
|
3069
|
+
gte: "On or after"
|
|
3070
|
+
};
|
|
3071
|
+
function filterOperatorLabel(operator, family) {
|
|
3072
|
+
return (family === "date" ? dateOperatorLabels[operator] : void 0) ?? operatorLabels[operator];
|
|
3073
|
+
}
|
|
3074
|
+
function filterSearchFirst(total) {
|
|
3075
|
+
return total === 1 ? "Type to search 1 value" : `Type to search ${String(total)} values`;
|
|
3076
|
+
}
|
|
3077
|
+
function filterResults(shown, total) {
|
|
3078
|
+
return `${String(shown)} of ${String(total)} ${total === 1 ? "row" : "rows"}`;
|
|
3079
|
+
}
|
|
3080
|
+
var filterValueRequests = {
|
|
3081
|
+
text: "Enter a value",
|
|
3082
|
+
number: "Enter a number",
|
|
3083
|
+
date: "Enter a date",
|
|
3084
|
+
boolean: "Choose a value"
|
|
3085
|
+
};
|
|
3086
|
+
var filterOperatorMenus = {
|
|
3087
|
+
text: "Text filters",
|
|
3088
|
+
number: "Number filters",
|
|
3089
|
+
date: "Date filters",
|
|
3090
|
+
boolean: "Boolean filters"
|
|
3091
|
+
};
|
|
3092
|
+
function copied(count, unit) {
|
|
3093
|
+
return count === 1 ? `1 ${unit.slice(0, -1)} copied` : `${String(count)} ${unit} copied`;
|
|
3094
|
+
}
|
|
2997
3095
|
var defaultMessages = {
|
|
2998
3096
|
pagerLabel: "Pagination",
|
|
2999
3097
|
previousPage: "Previous",
|
|
@@ -3056,152 +3154,54 @@ var defaultMessages = {
|
|
|
3056
3154
|
columnChooserLabel: "Columns",
|
|
3057
3155
|
columnChooserSearchLabel: "Search columns",
|
|
3058
3156
|
showAllColumns: "Show all",
|
|
3059
|
-
noMatchingColumns: "No matching columns"
|
|
3157
|
+
noMatchingColumns: "No matching columns",
|
|
3158
|
+
filterOperatorLabel,
|
|
3159
|
+
filterBlanks: "(Blanks)",
|
|
3160
|
+
filterSearchFirst,
|
|
3161
|
+
filterMoreValues: (count) => `${String(count)} more, refine the search`,
|
|
3162
|
+
openFilter: "Filter\u2026",
|
|
3163
|
+
clearColumnFilter: "Clear filter",
|
|
3164
|
+
filterMenuLabel: (columnName2) => `${columnName2} filter`,
|
|
3165
|
+
columnFiltered: "Filtered",
|
|
3166
|
+
applyFilter: "OK",
|
|
3167
|
+
cancelFilter: "Cancel",
|
|
3168
|
+
customFilter: "Custom filter",
|
|
3169
|
+
filterResults,
|
|
3170
|
+
filterConditionLabel: (position) => `Condition ${String(position)}`,
|
|
3171
|
+
filterOperatorFieldLabel: "Operator",
|
|
3172
|
+
filterValueLabel: "Value",
|
|
3173
|
+
filterFromLabel: "From",
|
|
3174
|
+
filterToLabel: "To",
|
|
3175
|
+
filterCombinatorLabel: "Combine conditions",
|
|
3176
|
+
filterAnd: "And",
|
|
3177
|
+
filterOr: "Or",
|
|
3178
|
+
filterValueInvalid: (family) => filterValueRequests[family],
|
|
3179
|
+
filterSearchLabel: "Search values",
|
|
3180
|
+
filterSelectAll: "Select all",
|
|
3181
|
+
filterValuesLabel: "Values",
|
|
3182
|
+
filterByCondition: "Filtered by condition",
|
|
3183
|
+
filterOperatorMenu: (family) => filterOperatorMenus[family],
|
|
3184
|
+
filterBackToValues: "Back to values",
|
|
3185
|
+
exportMenu: "Export",
|
|
3186
|
+
exportCsv: "Export to CSV",
|
|
3187
|
+
exportJson: "Export to JSON",
|
|
3188
|
+
exportXlsx: "Export to Excel",
|
|
3189
|
+
exportPdf: "Export to PDF",
|
|
3190
|
+
exportSelectionCsv: "Export selection to CSV",
|
|
3191
|
+
exportSelectionJson: "Export selection to JSON",
|
|
3192
|
+
exportSelectionXlsx: "Export selection to Excel",
|
|
3193
|
+
exportFailed: "Export failed",
|
|
3194
|
+
copyCell: "Copy cell",
|
|
3195
|
+
copyRow: "Copy row",
|
|
3196
|
+
copySelection: "Copy selection",
|
|
3197
|
+
copied,
|
|
3198
|
+
copyFailed: "Copy failed",
|
|
3199
|
+
print: "Print"
|
|
3060
3200
|
};
|
|
3061
3201
|
function resolveMessages(messages) {
|
|
3062
3202
|
return messages === void 0 ? defaultMessages : { ...defaultMessages, ...messages };
|
|
3063
3203
|
}
|
|
3064
3204
|
|
|
3065
|
-
// src/util/format.ts
|
|
3066
|
-
var DATE_FIELDS = {
|
|
3067
|
-
year: "numeric",
|
|
3068
|
-
month: "numeric",
|
|
3069
|
-
day: "numeric"
|
|
3070
|
-
};
|
|
3071
|
-
var TIME_FIELDS = {
|
|
3072
|
-
hour: "numeric",
|
|
3073
|
-
minute: "numeric",
|
|
3074
|
-
second: "numeric"
|
|
3075
|
-
};
|
|
3076
|
-
var DATE_TIME_FIELDS = {
|
|
3077
|
-
...DATE_FIELDS,
|
|
3078
|
-
...TIME_FIELDS
|
|
3079
|
-
};
|
|
3080
|
-
var CURRENCY_DIGITS = {
|
|
3081
|
-
minimumFractionDigits: 2,
|
|
3082
|
-
maximumFractionDigits: 2
|
|
3083
|
-
};
|
|
3084
|
-
var NO_FORMAT = {};
|
|
3085
|
-
var formatters = /* @__PURE__ */ new WeakMap();
|
|
3086
|
-
function cached(format, key, create) {
|
|
3087
|
-
const owner = format ?? NO_FORMAT;
|
|
3088
|
-
let byKey = formatters.get(owner);
|
|
3089
|
-
if (byKey === void 0) {
|
|
3090
|
-
byKey = /* @__PURE__ */ new Map();
|
|
3091
|
-
formatters.set(owner, byKey);
|
|
3092
|
-
}
|
|
3093
|
-
const existing = byKey.get(key);
|
|
3094
|
-
if (existing !== void 0) {
|
|
3095
|
-
return existing;
|
|
3096
|
-
}
|
|
3097
|
-
const formatter = create();
|
|
3098
|
-
byKey.set(key, formatter);
|
|
3099
|
-
return formatter;
|
|
3100
|
-
}
|
|
3101
|
-
function localeKey(locale) {
|
|
3102
|
-
return typeof locale === "string" ? locale : locale?.join(",") ?? "";
|
|
3103
|
-
}
|
|
3104
|
-
function isNumericType(type) {
|
|
3105
|
-
return type === "number" || type === "decimal" || type === "currency" || type === "percent";
|
|
3106
|
-
}
|
|
3107
|
-
function numberOptions(type, format) {
|
|
3108
|
-
switch (type) {
|
|
3109
|
-
case "currency":
|
|
3110
|
-
return format?.currency === void 0 ? { ...CURRENCY_DIGITS, ...format } : { style: "currency", ...format };
|
|
3111
|
-
case "percent":
|
|
3112
|
-
return { style: "percent", ...format };
|
|
3113
|
-
default:
|
|
3114
|
-
return format ?? {};
|
|
3115
|
-
}
|
|
3116
|
-
}
|
|
3117
|
-
function dateOptions(type, format) {
|
|
3118
|
-
if (format !== void 0) {
|
|
3119
|
-
return format;
|
|
3120
|
-
}
|
|
3121
|
-
if (type === "date") return DATE_FIELDS;
|
|
3122
|
-
if (type === "time") return TIME_FIELDS;
|
|
3123
|
-
return DATE_TIME_FIELDS;
|
|
3124
|
-
}
|
|
3125
|
-
function formatNumber(value, type, format, locale) {
|
|
3126
|
-
const formatter = cached(
|
|
3127
|
-
format,
|
|
3128
|
-
`number|${type ?? ""}|${localeKey(locale)}`,
|
|
3129
|
-
() => new Intl.NumberFormat(
|
|
3130
|
-
locale,
|
|
3131
|
-
numberOptions(type, format)
|
|
3132
|
-
)
|
|
3133
|
-
);
|
|
3134
|
-
return formatter.format(value);
|
|
3135
|
-
}
|
|
3136
|
-
function formatDate(value, type, format, locale) {
|
|
3137
|
-
if (Number.isNaN(value.getTime())) {
|
|
3138
|
-
return String(value);
|
|
3139
|
-
}
|
|
3140
|
-
const formatter = cached(
|
|
3141
|
-
format,
|
|
3142
|
-
`date|${type ?? ""}|${localeKey(locale)}`,
|
|
3143
|
-
() => new Intl.DateTimeFormat(
|
|
3144
|
-
locale,
|
|
3145
|
-
dateOptions(type, format)
|
|
3146
|
-
)
|
|
3147
|
-
);
|
|
3148
|
-
return formatter.format(value);
|
|
3149
|
-
}
|
|
3150
|
-
function formatOther(value) {
|
|
3151
|
-
if (typeof value === "function") {
|
|
3152
|
-
return "(function)";
|
|
3153
|
-
}
|
|
3154
|
-
if (typeof value === "object" && value !== null) {
|
|
3155
|
-
try {
|
|
3156
|
-
return JSON.stringify(value);
|
|
3157
|
-
} catch {
|
|
3158
|
-
return Object.prototype.toString.call(value);
|
|
3159
|
-
}
|
|
3160
|
-
}
|
|
3161
|
-
return String(value);
|
|
3162
|
-
}
|
|
3163
|
-
function formatCellValue(value, column, locale, row, messages) {
|
|
3164
|
-
if (column.valueFormatter !== void 0) {
|
|
3165
|
-
return column.valueFormatter(value, row);
|
|
3166
|
-
}
|
|
3167
|
-
if (value === null || value === void 0) {
|
|
3168
|
-
return "";
|
|
3169
|
-
}
|
|
3170
|
-
if (typeof value === "boolean" && messages !== void 0) {
|
|
3171
|
-
return value ? messages.booleanTrue : messages.booleanFalse;
|
|
3172
|
-
}
|
|
3173
|
-
const { type, format } = column;
|
|
3174
|
-
const formatted = format !== void 0 || locale !== void 0;
|
|
3175
|
-
if (value instanceof Date) {
|
|
3176
|
-
return formatted ? formatDate(value, type, format, locale) : value.toLocaleString();
|
|
3177
|
-
}
|
|
3178
|
-
if (formatted && (typeof value === "number" || typeof value === "bigint") && isNumericType(type)) {
|
|
3179
|
-
return formatNumber(value, type, format, locale);
|
|
3180
|
-
}
|
|
3181
|
-
if (typeof value === "string") {
|
|
3182
|
-
return value;
|
|
3183
|
-
}
|
|
3184
|
-
return formatOther(value);
|
|
3185
|
-
}
|
|
3186
|
-
function aggregateFormatColumn(value, spec, column) {
|
|
3187
|
-
const plain = {
|
|
3188
|
-
field: spec.columnId,
|
|
3189
|
-
type: "number"
|
|
3190
|
-
};
|
|
3191
|
-
if (column === void 0 || spec.fn === "count" || spec.fn === "countDistinct") {
|
|
3192
|
-
return plain;
|
|
3193
|
-
}
|
|
3194
|
-
const numeric = typeof value === "number" || typeof value === "bigint";
|
|
3195
|
-
return column.valueFormatter === void 0 && numeric && !isNumericType(column.type) ? plain : column;
|
|
3196
|
-
}
|
|
3197
|
-
function formatAggregateValue(value, spec, column, locale) {
|
|
3198
|
-
const target = aggregateFormatColumn(value, spec, column);
|
|
3199
|
-
if (typeof value === "number" && target.valueFormatter === void 0 && target.format === void 0 && locale === void 0) {
|
|
3200
|
-
return value.toLocaleString();
|
|
3201
|
-
}
|
|
3202
|
-
return formatCellValue(value, target, locale);
|
|
3203
|
-
}
|
|
3204
|
-
|
|
3205
3205
|
// src/util/filterEditor.ts
|
|
3206
3206
|
function touchesColumn(entry, columnId) {
|
|
3207
3207
|
return "combinator" in entry ? entry.entries.some((nested) => touchesColumn(nested, columnId)) : entry.columnId === columnId;
|
|
@@ -3234,217 +3234,1321 @@ function conditionsShape(touching, columnId) {
|
|
|
3234
3234
|
if (entries.length === 0 || entries.length > 2) {
|
|
3235
3235
|
return void 0;
|
|
3236
3236
|
}
|
|
3237
|
-
const conditions = entries.filter((entry) => isConditionOn(entry, columnId));
|
|
3238
|
-
return conditions.length === entries.length ? { kind: "conditions", conditions, combinator } : void 0;
|
|
3239
|
-
}
|
|
3240
|
-
function describeColumnFilter(filter, columnId) {
|
|
3241
|
-
const touching = filter.filter((entry) => touchesColumn(entry, columnId));
|
|
3242
|
-
const [only] = touching;
|
|
3243
|
-
if (only === void 0) {
|
|
3244
|
-
return { kind: "none" };
|
|
3237
|
+
const conditions = entries.filter((entry) => isConditionOn(entry, columnId));
|
|
3238
|
+
return conditions.length === entries.length ? { kind: "conditions", conditions, combinator } : void 0;
|
|
3239
|
+
}
|
|
3240
|
+
function describeColumnFilter(filter, columnId) {
|
|
3241
|
+
const touching = filter.filter((entry) => touchesColumn(entry, columnId));
|
|
3242
|
+
const [only] = touching;
|
|
3243
|
+
if (only === void 0) {
|
|
3244
|
+
return { kind: "none" };
|
|
3245
|
+
}
|
|
3246
|
+
return (touching.length === 1 ? valuesShape(only, columnId) : void 0) ?? conditionsShape(touching, columnId) ?? { kind: "custom" };
|
|
3247
|
+
}
|
|
3248
|
+
function isColumnFiltered(filter, columnId) {
|
|
3249
|
+
return filter.some((entry) => touchesColumn(entry, columnId));
|
|
3250
|
+
}
|
|
3251
|
+
function isOfferable(value) {
|
|
3252
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value instanceof Date;
|
|
3253
|
+
}
|
|
3254
|
+
function matchesSearch(label, search) {
|
|
3255
|
+
return label.toLowerCase().includes(search.toLowerCase());
|
|
3256
|
+
}
|
|
3257
|
+
function columnValueOptions(rows, dataColumns, filter, columnId, query = {}) {
|
|
3258
|
+
const column = dataColumns.find((entry) => entry.id === columnId)?.column;
|
|
3259
|
+
if (column === void 0) {
|
|
3260
|
+
return { options: [], total: 0, truncated: false, blanks: 0 };
|
|
3261
|
+
}
|
|
3262
|
+
const keep = compileFilter(
|
|
3263
|
+
filter.filter((entry) => !touchesColumn(entry, columnId)),
|
|
3264
|
+
dataColumns
|
|
3265
|
+
);
|
|
3266
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
3267
|
+
let blanks = 0;
|
|
3268
|
+
for (const row of rows) {
|
|
3269
|
+
if (keep !== void 0 && !keep(row)) {
|
|
3270
|
+
continue;
|
|
3271
|
+
}
|
|
3272
|
+
const value = accessDotted(row, column.field);
|
|
3273
|
+
if (isEmptyCell(value)) {
|
|
3274
|
+
blanks++;
|
|
3275
|
+
} else if (isOfferable(value)) {
|
|
3276
|
+
const key = filterValueKey(value, column.type);
|
|
3277
|
+
const bucket = buckets.get(key);
|
|
3278
|
+
if (bucket === void 0) {
|
|
3279
|
+
buckets.set(key, { value, row, count: 1 });
|
|
3280
|
+
} else {
|
|
3281
|
+
bucket.count++;
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3284
|
+
}
|
|
3285
|
+
const { search = "", limit, locale } = query;
|
|
3286
|
+
const messages = resolveMessages(query.messages);
|
|
3287
|
+
const options = [...buckets.values()].sort((a, b) => compareValues(a.value, b.value, column.type)).map(({ value, row, count }) => ({
|
|
3288
|
+
value,
|
|
3289
|
+
label: formatCellValue(value, column, locale, row, messages),
|
|
3290
|
+
count
|
|
3291
|
+
})).filter((option) => matchesSearch(option.label, search));
|
|
3292
|
+
const shown = limit === void 0 ? options : options.slice(0, limit);
|
|
3293
|
+
return {
|
|
3294
|
+
options: shown,
|
|
3295
|
+
total: options.length,
|
|
3296
|
+
truncated: shown.length < options.length,
|
|
3297
|
+
blanks,
|
|
3298
|
+
columnType: column.type
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3301
|
+
function keyOf2(draft, value) {
|
|
3302
|
+
return filterValueKey(value, draft.columnType);
|
|
3303
|
+
}
|
|
3304
|
+
function everyOption(draft) {
|
|
3305
|
+
return new Map(
|
|
3306
|
+
draft.options.map((option) => [keyOf2(draft, option.value), option.value])
|
|
3307
|
+
);
|
|
3308
|
+
}
|
|
3309
|
+
function initialFilterEditorDraft(shape, values) {
|
|
3310
|
+
const draft = {
|
|
3311
|
+
mode: "values",
|
|
3312
|
+
options: values.options,
|
|
3313
|
+
blanks: values.blanks,
|
|
3314
|
+
search: "",
|
|
3315
|
+
checked: everyOption(values),
|
|
3316
|
+
includeBlanks: values.blanks > 0,
|
|
3317
|
+
conditions: [],
|
|
3318
|
+
combinator: "and",
|
|
3319
|
+
columnType: values.columnType
|
|
3320
|
+
};
|
|
3321
|
+
switch (shape.kind) {
|
|
3322
|
+
case "values":
|
|
3323
|
+
return {
|
|
3324
|
+
...draft,
|
|
3325
|
+
checked: new Map(
|
|
3326
|
+
shape.values.filter((value) => !isEmptyCell(value)).map((value) => [keyOf2(draft, value), value])
|
|
3327
|
+
),
|
|
3328
|
+
includeBlanks: shape.includeBlanks
|
|
3329
|
+
};
|
|
3330
|
+
case "conditions":
|
|
3331
|
+
return {
|
|
3332
|
+
...draft,
|
|
3333
|
+
mode: "conditions",
|
|
3334
|
+
conditions: shape.conditions,
|
|
3335
|
+
combinator: shape.combinator
|
|
3336
|
+
};
|
|
3337
|
+
case "none":
|
|
3338
|
+
case "custom":
|
|
3339
|
+
return draft;
|
|
3340
|
+
}
|
|
3341
|
+
}
|
|
3342
|
+
function visibleFilterOptions(draft) {
|
|
3343
|
+
return draft.search === "" ? draft.options : draft.options.filter(
|
|
3344
|
+
(option) => matchesSearch(option.label, draft.search)
|
|
3345
|
+
);
|
|
3346
|
+
}
|
|
3347
|
+
function selectAllCoversBlanks(draft) {
|
|
3348
|
+
return draft.search === "" && draft.blanks > 0;
|
|
3349
|
+
}
|
|
3350
|
+
function selectAllState(draft) {
|
|
3351
|
+
const states = visibleFilterOptions(draft).map(
|
|
3352
|
+
(option) => draft.checked.has(keyOf2(draft, option.value))
|
|
3353
|
+
);
|
|
3354
|
+
if (selectAllCoversBlanks(draft)) {
|
|
3355
|
+
states.push(draft.includeBlanks);
|
|
3356
|
+
}
|
|
3357
|
+
if (states.length > 0 && states.every(Boolean)) {
|
|
3358
|
+
return "checked";
|
|
3359
|
+
}
|
|
3360
|
+
return states.some(Boolean) ? "mixed" : "unchecked";
|
|
3361
|
+
}
|
|
3362
|
+
function toggleAll(draft) {
|
|
3363
|
+
const check = selectAllState(draft) !== "checked";
|
|
3364
|
+
const checked = new Map(draft.checked);
|
|
3365
|
+
for (const option of visibleFilterOptions(draft)) {
|
|
3366
|
+
const key = keyOf2(draft, option.value);
|
|
3367
|
+
if (check) {
|
|
3368
|
+
checked.set(key, option.value);
|
|
3369
|
+
} else {
|
|
3370
|
+
checked.delete(key);
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
return {
|
|
3374
|
+
...draft,
|
|
3375
|
+
mode: "values",
|
|
3376
|
+
checked,
|
|
3377
|
+
includeBlanks: selectAllCoversBlanks(draft) ? check : draft.includeBlanks
|
|
3378
|
+
};
|
|
3379
|
+
}
|
|
3380
|
+
function filterEditorReducer(draft, action) {
|
|
3381
|
+
switch (action.type) {
|
|
3382
|
+
case "search":
|
|
3383
|
+
return { ...draft, search: action.search };
|
|
3384
|
+
case "toggleValue": {
|
|
3385
|
+
const key = keyOf2(draft, action.value);
|
|
3386
|
+
const checked = new Map(draft.checked);
|
|
3387
|
+
if (checked.has(key)) {
|
|
3388
|
+
checked.delete(key);
|
|
3389
|
+
} else {
|
|
3390
|
+
checked.set(key, action.value);
|
|
3391
|
+
}
|
|
3392
|
+
return { ...draft, mode: "values", checked };
|
|
3393
|
+
}
|
|
3394
|
+
case "toggleAll":
|
|
3395
|
+
return toggleAll(draft);
|
|
3396
|
+
case "setIncludeBlanks":
|
|
3397
|
+
return { ...draft, mode: "values", includeBlanks: action.includeBlanks };
|
|
3398
|
+
case "setConditions":
|
|
3399
|
+
return {
|
|
3400
|
+
...draft,
|
|
3401
|
+
mode: "conditions",
|
|
3402
|
+
conditions: action.conditions,
|
|
3403
|
+
combinator: action.combinator ?? draft.combinator
|
|
3404
|
+
};
|
|
3405
|
+
case "clear":
|
|
3406
|
+
return {
|
|
3407
|
+
...draft,
|
|
3408
|
+
search: "",
|
|
3409
|
+
checked: everyOption(draft),
|
|
3410
|
+
includeBlanks: draft.blanks > 0,
|
|
3411
|
+
conditions: []
|
|
3412
|
+
};
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
function checksEverything(draft) {
|
|
3416
|
+
return draft.options.every(
|
|
3417
|
+
(option) => draft.checked.has(keyOf2(draft, option.value))
|
|
3418
|
+
) && (draft.includeBlanks || draft.blanks === 0);
|
|
3419
|
+
}
|
|
3420
|
+
function draftEntries(draft, columnId) {
|
|
3421
|
+
if (draft.mode === "conditions") {
|
|
3422
|
+
const conditions = draft.conditions.map((condition) => ({
|
|
3423
|
+
...condition,
|
|
3424
|
+
columnId
|
|
3425
|
+
}));
|
|
3426
|
+
return conditions.length > 1 ? [{ combinator: draft.combinator, entries: conditions }] : conditions;
|
|
3427
|
+
}
|
|
3428
|
+
if (checksEverything(draft)) {
|
|
3429
|
+
return [];
|
|
3430
|
+
}
|
|
3431
|
+
const picked = {
|
|
3432
|
+
columnId,
|
|
3433
|
+
operator: "in",
|
|
3434
|
+
value: [...draft.checked.values()]
|
|
3435
|
+
};
|
|
3436
|
+
return draft.includeBlanks ? [
|
|
3437
|
+
{
|
|
3438
|
+
combinator: "or",
|
|
3439
|
+
entries: [picked, { columnId, operator: "isEmpty" }]
|
|
3440
|
+
}
|
|
3441
|
+
] : [picked];
|
|
3442
|
+
}
|
|
3443
|
+
function draftToFilter(draft, columnId, current) {
|
|
3444
|
+
const replacement = draftEntries(draft, columnId);
|
|
3445
|
+
const first = current.findIndex((entry) => touchesColumn(entry, columnId));
|
|
3446
|
+
if (first === -1) {
|
|
3447
|
+
return replacement.length === 0 ? current : [...current, ...replacement];
|
|
3448
|
+
}
|
|
3449
|
+
const kept = current.filter((entry) => !touchesColumn(entry, columnId));
|
|
3450
|
+
return [...kept.slice(0, first), ...replacement, ...kept.slice(first)];
|
|
3451
|
+
}
|
|
3452
|
+
|
|
3453
|
+
// src/commands/builtInCommands.ts
|
|
3454
|
+
function isOn(context, areas) {
|
|
3455
|
+
return areas.includes(context.target.area);
|
|
3456
|
+
}
|
|
3457
|
+
function columnOn(context, areas) {
|
|
3458
|
+
return isOn(context, areas) ? context.column : null;
|
|
3459
|
+
}
|
|
3460
|
+
function isGroupedBy(context, columnId) {
|
|
3461
|
+
return context.state.groupBy.some((entry) => entry.columnId === columnId);
|
|
3462
|
+
}
|
|
3463
|
+
function sortableColumn(context) {
|
|
3464
|
+
const column = columnOn(context, ["header"]);
|
|
3465
|
+
return column !== null && (column.column.sortable ?? context.options.sortableColumns ?? false) ? column : null;
|
|
3466
|
+
}
|
|
3467
|
+
function groupableColumn(context) {
|
|
3468
|
+
const column = columnOn(context, ["header"]);
|
|
3469
|
+
return column !== null && (column.column.groupable ?? context.options.groupableColumns ?? false) && !isGroupedBy(context, column.id) ? column : null;
|
|
3470
|
+
}
|
|
3471
|
+
function groupedColumn(context) {
|
|
3472
|
+
const column = columnOn(context, ["header", "group"]);
|
|
3473
|
+
return column !== null && isGroupedBy(context, column.id) ? column : null;
|
|
3474
|
+
}
|
|
3475
|
+
function filterableColumn(context) {
|
|
3476
|
+
const column = columnOn(context, ["header"]);
|
|
3477
|
+
return column !== null && resolveColumnFilterMenu(context.options, column.column) !== null ? column : null;
|
|
3478
|
+
}
|
|
3479
|
+
function hideableColumn(context) {
|
|
3480
|
+
const column = columnOn(context, ["header"]);
|
|
3481
|
+
return column !== null && column.column.hideable !== false ? column : null;
|
|
3482
|
+
}
|
|
3483
|
+
function resizableColumn(context) {
|
|
3484
|
+
const column = columnOn(context, ["header"]);
|
|
3485
|
+
if (column === null) {
|
|
3486
|
+
return null;
|
|
3487
|
+
}
|
|
3488
|
+
return context.view.columns.find(
|
|
3489
|
+
(entry) => entry.id === column.id && entry.resizable
|
|
3490
|
+
) ?? null;
|
|
3491
|
+
}
|
|
3492
|
+
function resizableColumnIds(context) {
|
|
3493
|
+
return context.view.columns.filter((entry) => entry.resizable).map((entry) => entry.id);
|
|
3494
|
+
}
|
|
3495
|
+
function isAnythingSelectable(selectable) {
|
|
3496
|
+
return [selectable?.rows, selectable?.columns, selectable?.cells].some(
|
|
3497
|
+
(mode) => mode !== void 0 && mode !== false
|
|
3498
|
+
);
|
|
3499
|
+
}
|
|
3500
|
+
function columnName(context) {
|
|
3501
|
+
return context.column === null ? "" : resolveColumnHeaderText(context.column.column);
|
|
3502
|
+
}
|
|
3503
|
+
function columnCommand({
|
|
3504
|
+
id,
|
|
3505
|
+
label,
|
|
3506
|
+
icon,
|
|
3507
|
+
find,
|
|
3508
|
+
isEnabled,
|
|
3509
|
+
isChecked,
|
|
3510
|
+
execute
|
|
3511
|
+
}) {
|
|
3512
|
+
function test(predicate) {
|
|
3513
|
+
return (context) => {
|
|
3514
|
+
const column = find(context);
|
|
3515
|
+
return column !== null && predicate(column, context);
|
|
3516
|
+
};
|
|
3517
|
+
}
|
|
3518
|
+
return {
|
|
3519
|
+
id,
|
|
3520
|
+
label,
|
|
3521
|
+
icon,
|
|
3522
|
+
isVisible: (context) => find(context) !== null,
|
|
3523
|
+
isEnabled: isEnabled === void 0 ? void 0 : test(isEnabled),
|
|
3524
|
+
isChecked: isChecked === void 0 ? void 0 : test(isChecked),
|
|
3525
|
+
execute: (context) => {
|
|
3526
|
+
const column = find(context);
|
|
3527
|
+
if (column !== null) {
|
|
3528
|
+
execute(column, context);
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3531
|
+
};
|
|
3532
|
+
}
|
|
3533
|
+
var EXPORT_AREAS = ["grid", "toolbar", "body", "cell"];
|
|
3534
|
+
function exportCommand({
|
|
3535
|
+
id,
|
|
3536
|
+
format,
|
|
3537
|
+
label,
|
|
3538
|
+
selection = false
|
|
3539
|
+
}) {
|
|
3540
|
+
return {
|
|
3541
|
+
id,
|
|
3542
|
+
label: ({ messages }) => label(messages),
|
|
3543
|
+
isVisible: (context) => isOn(context, EXPORT_AREAS) && resolveExportWriters(context.options.exportWriters).has(format) && (!selection || context.state.rowSelection.length > 0),
|
|
3544
|
+
isEnabled: (context) => (selection ? context.selectedRows() : context.view.rows).length > 0,
|
|
3545
|
+
execute: (context) => {
|
|
3546
|
+
const { export: defaults = {}, exportWriters } = context.options;
|
|
3547
|
+
const file = exportFile(
|
|
3548
|
+
context,
|
|
3549
|
+
format,
|
|
3550
|
+
selection ? { ...defaults, scope: "selection" } : defaults,
|
|
3551
|
+
resolveExportWriters(exportWriters)
|
|
3552
|
+
);
|
|
3553
|
+
if (file !== null) {
|
|
3554
|
+
context.host.saveFile(file);
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
};
|
|
3558
|
+
}
|
|
3559
|
+
function cellRequest(context, kind) {
|
|
3560
|
+
const { target } = context;
|
|
3561
|
+
if (target.area !== "cell") {
|
|
3562
|
+
return null;
|
|
3563
|
+
}
|
|
3564
|
+
return kind === "cell" ? { kind, rowId: target.rowId, columnId: target.columnId } : { kind, rowId: target.rowId };
|
|
3565
|
+
}
|
|
3566
|
+
function copyCommand({
|
|
3567
|
+
id,
|
|
3568
|
+
label,
|
|
3569
|
+
request,
|
|
3570
|
+
isEnabled
|
|
3571
|
+
}) {
|
|
3572
|
+
function contentFor(context) {
|
|
3573
|
+
const asked = request(context);
|
|
3574
|
+
return asked === null ? null : copyContent(context, asked);
|
|
3575
|
+
}
|
|
3576
|
+
return {
|
|
3577
|
+
id,
|
|
3578
|
+
label: ({ messages }) => label(messages),
|
|
3579
|
+
icon: "copy",
|
|
3580
|
+
isVisible: (context) => request(context) !== null,
|
|
3581
|
+
isEnabled: isEnabled ?? ((context) => contentFor(context) !== null),
|
|
3582
|
+
execute: (context) => {
|
|
3583
|
+
const content = contentFor(context);
|
|
3584
|
+
if (content !== null) {
|
|
3585
|
+
context.host.writeClipboard(content);
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
};
|
|
3589
|
+
}
|
|
3590
|
+
function sortCommand(direction) {
|
|
3591
|
+
return columnCommand({
|
|
3592
|
+
id: direction === "asc" ? "sort.ascending" : "sort.descending",
|
|
3593
|
+
label: ({ messages }) => direction === "asc" ? messages.sortAscending : messages.sortDescending,
|
|
3594
|
+
icon: direction === "asc" ? "sortAscending" : "sortDescending",
|
|
3595
|
+
find: sortableColumn,
|
|
3596
|
+
isChecked: (column, { state }) => sortDirectionFor(state.columnSort, column.id) === direction,
|
|
3597
|
+
execute: (column, { actions }) => {
|
|
3598
|
+
actions.setColumnSort([{ columnId: column.id, direction }]);
|
|
3599
|
+
}
|
|
3600
|
+
});
|
|
3601
|
+
}
|
|
3602
|
+
function builtInCommands() {
|
|
3603
|
+
return [
|
|
3604
|
+
sortCommand("asc"),
|
|
3605
|
+
sortCommand("desc"),
|
|
3606
|
+
// Cleared rather than set, so the change reports this column — the one
|
|
3607
|
+
// the announcement names — rather than a key that moved up past it.
|
|
3608
|
+
columnCommand({
|
|
3609
|
+
id: "sort.clear",
|
|
3610
|
+
label: ({ messages }) => messages.clearSort,
|
|
3611
|
+
find: sortableColumn,
|
|
3612
|
+
isEnabled: (column, { state }) => sortDirectionFor(state.columnSort, column.id) !== null,
|
|
3613
|
+
execute: (column, { actions }) => {
|
|
3614
|
+
actions.clearColumnSort(column.id);
|
|
3615
|
+
}
|
|
3616
|
+
}),
|
|
3617
|
+
{
|
|
3618
|
+
id: "sort.clearAll",
|
|
3619
|
+
label: ({ messages }) => messages.clearAllSorting,
|
|
3620
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "header"]),
|
|
3621
|
+
isEnabled: ({ state }) => state.columnSort.length > 0,
|
|
3622
|
+
execute: ({ actions }) => {
|
|
3623
|
+
actions.setColumnSort([]);
|
|
3624
|
+
}
|
|
3625
|
+
},
|
|
3626
|
+
columnCommand({
|
|
3627
|
+
id: "group.add",
|
|
3628
|
+
label: (context) => context.messages.groupByColumn(columnName(context)),
|
|
3629
|
+
icon: "group",
|
|
3630
|
+
find: groupableColumn,
|
|
3631
|
+
execute: (column, { actions }) => {
|
|
3632
|
+
actions.toggleGroupBy(column.id);
|
|
3633
|
+
}
|
|
3634
|
+
}),
|
|
3635
|
+
// Toggled rather than set, so the change reports this column — the one
|
|
3636
|
+
// the announcement names — rather than the level that moved up.
|
|
3637
|
+
columnCommand({
|
|
3638
|
+
id: "group.remove",
|
|
3639
|
+
label: (context) => context.messages.removeGroupBy(columnName(context)),
|
|
3640
|
+
icon: "group",
|
|
3641
|
+
find: groupedColumn,
|
|
3642
|
+
execute: (column, { actions }) => {
|
|
3643
|
+
actions.toggleGroupBy(column.id);
|
|
3644
|
+
}
|
|
3645
|
+
}),
|
|
3646
|
+
{
|
|
3647
|
+
id: "group.clearAll",
|
|
3648
|
+
label: ({ messages }) => messages.ungroupAll,
|
|
3649
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar"]) && context.state.groupBy.length > 0,
|
|
3650
|
+
execute: ({ actions }) => {
|
|
3651
|
+
actions.setGroupBy([]);
|
|
3652
|
+
}
|
|
3653
|
+
},
|
|
3654
|
+
{
|
|
3655
|
+
id: "group.expandAll",
|
|
3656
|
+
label: ({ messages }) => messages.expandAllGroups,
|
|
3657
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "group", "cell"]) && context.state.groupBy.length > 0,
|
|
3658
|
+
execute: ({ actions }) => {
|
|
3659
|
+
actions.expandAllGroups();
|
|
3660
|
+
}
|
|
3661
|
+
},
|
|
3662
|
+
{
|
|
3663
|
+
id: "group.collapseAll",
|
|
3664
|
+
label: ({ messages }) => messages.collapseAllGroups,
|
|
3665
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "group", "cell"]) && context.state.groupBy.length > 0,
|
|
3666
|
+
execute: ({ actions }) => {
|
|
3667
|
+
actions.collapseAllGroups();
|
|
3668
|
+
}
|
|
3669
|
+
},
|
|
3670
|
+
columnCommand({
|
|
3671
|
+
id: "column.hide",
|
|
3672
|
+
label: ({ messages }) => messages.hideColumn,
|
|
3673
|
+
icon: "hide",
|
|
3674
|
+
find: hideableColumn,
|
|
3675
|
+
// The last column shown stays: a grid with none has no header left to
|
|
3676
|
+
// bring one back from.
|
|
3677
|
+
isEnabled: (column, { view }) => view.columns.some((entry) => entry.id !== column.id),
|
|
3678
|
+
execute: (column, { actions }) => {
|
|
3679
|
+
actions.hideColumns([column.id]);
|
|
3680
|
+
}
|
|
3681
|
+
}),
|
|
3682
|
+
columnCommand({
|
|
3683
|
+
id: "column.autoFit",
|
|
3684
|
+
label: ({ messages }) => messages.autoFitColumn,
|
|
3685
|
+
icon: "autoFit",
|
|
3686
|
+
find: resizableColumn,
|
|
3687
|
+
execute: (column, { host }) => {
|
|
3688
|
+
host.autoFitColumns([column.id]);
|
|
3689
|
+
}
|
|
3690
|
+
}),
|
|
3691
|
+
{
|
|
3692
|
+
id: "column.autoFitAll",
|
|
3693
|
+
label: ({ messages }) => messages.autoFitAllColumns,
|
|
3694
|
+
icon: "autoFit",
|
|
3695
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "header"]) && resizableColumnIds(context).length > 0,
|
|
3696
|
+
execute: (context) => {
|
|
3697
|
+
context.host.autoFitColumns(resizableColumnIds(context));
|
|
3698
|
+
}
|
|
3699
|
+
},
|
|
3700
|
+
// The target goes along, so the renderer opens the chooser where it was
|
|
3701
|
+
// asked for: beside the toolbar button, below the header, or at the
|
|
3702
|
+
// grid's corner for a call from nowhere in particular.
|
|
3703
|
+
{
|
|
3704
|
+
id: "column.choose",
|
|
3705
|
+
label: ({ messages }) => messages.chooseColumns,
|
|
3706
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "header"]),
|
|
3707
|
+
execute: ({ host, target }) => {
|
|
3708
|
+
host.openPanel("columnChooser", target);
|
|
3709
|
+
}
|
|
3710
|
+
},
|
|
3711
|
+
// Opened below the header it was asked for, as the chooser is.
|
|
3712
|
+
columnCommand({
|
|
3713
|
+
id: "filter.open",
|
|
3714
|
+
label: ({ messages }) => messages.openFilter,
|
|
3715
|
+
find: filterableColumn,
|
|
3716
|
+
execute: (_column, { host, target }) => {
|
|
3717
|
+
host.openPanel("filter", target);
|
|
3718
|
+
}
|
|
3719
|
+
}),
|
|
3720
|
+
// Every entry about the column goes, whichever editor wrote it — or
|
|
3721
|
+
// none did, as with a predicate set in code — as applying an editor
|
|
3722
|
+
// replaces them all.
|
|
3723
|
+
columnCommand({
|
|
3724
|
+
id: "filter.clear",
|
|
3725
|
+
label: ({ messages }) => messages.clearColumnFilter,
|
|
3726
|
+
find: filterableColumn,
|
|
3727
|
+
isEnabled: (column, { state }) => isColumnFiltered(state.filter, column.id),
|
|
3728
|
+
execute: (column, { actions, state }) => {
|
|
3729
|
+
actions.setFilter(
|
|
3730
|
+
state.filter.filter((entry) => !touchesColumn(entry, column.id))
|
|
3731
|
+
);
|
|
3732
|
+
}
|
|
3733
|
+
}),
|
|
3734
|
+
{
|
|
3735
|
+
id: "selection.selectAllRows",
|
|
3736
|
+
label: ({ messages }) => messages.selectAllRows,
|
|
3737
|
+
isVisible: (context) => isOn(context, ["grid", "cell"]) && context.options.selectable?.rows === "multiple",
|
|
3738
|
+
isEnabled: ({ view }) => view.displayDataRows.length > 0,
|
|
3739
|
+
execute: ({ actions }) => {
|
|
3740
|
+
actions.selectAllRows();
|
|
3741
|
+
}
|
|
3742
|
+
},
|
|
3743
|
+
{
|
|
3744
|
+
id: "selection.clear",
|
|
3745
|
+
label: ({ messages }) => messages.clearSelection,
|
|
3746
|
+
isVisible: (context) => isOn(context, ["grid", "cell", "header"]) && isAnythingSelectable(context.options.selectable),
|
|
3747
|
+
isEnabled: ({ state }) => state.rowSelection.length > 0 || state.columnSelection.length > 0 || state.cellSelection !== null,
|
|
3748
|
+
execute: ({ actions }) => {
|
|
3749
|
+
actions.clearSelection();
|
|
3750
|
+
}
|
|
3751
|
+
},
|
|
3752
|
+
{
|
|
3753
|
+
id: "filter.clearAll",
|
|
3754
|
+
label: ({ messages }) => messages.clearFilters,
|
|
3755
|
+
isVisible: (context) => isOn(context, ["grid", "toolbar", "header"]),
|
|
3756
|
+
isEnabled: ({ state }) => state.filter.length > 0,
|
|
3757
|
+
execute: ({ actions }) => {
|
|
3758
|
+
actions.setFilter([]);
|
|
3759
|
+
}
|
|
3760
|
+
},
|
|
3761
|
+
exportCommand({
|
|
3762
|
+
id: "export.csv",
|
|
3763
|
+
format: "csv",
|
|
3764
|
+
label: ({ exportCsv }) => exportCsv
|
|
3765
|
+
}),
|
|
3766
|
+
exportCommand({
|
|
3767
|
+
id: "export.json",
|
|
3768
|
+
format: "json",
|
|
3769
|
+
label: ({ exportJson }) => exportJson
|
|
3770
|
+
}),
|
|
3771
|
+
exportCommand({
|
|
3772
|
+
id: "export.xlsx",
|
|
3773
|
+
format: "xlsx",
|
|
3774
|
+
label: ({ exportXlsx }) => exportXlsx
|
|
3775
|
+
}),
|
|
3776
|
+
exportCommand({
|
|
3777
|
+
id: "export.pdf",
|
|
3778
|
+
format: "pdf",
|
|
3779
|
+
label: ({ exportPdf }) => exportPdf
|
|
3780
|
+
}),
|
|
3781
|
+
exportCommand({
|
|
3782
|
+
id: "exportSelection.csv",
|
|
3783
|
+
format: "csv",
|
|
3784
|
+
label: ({ exportSelectionCsv }) => exportSelectionCsv,
|
|
3785
|
+
selection: true
|
|
3786
|
+
}),
|
|
3787
|
+
exportCommand({
|
|
3788
|
+
id: "exportSelection.json",
|
|
3789
|
+
format: "json",
|
|
3790
|
+
label: ({ exportSelectionJson }) => exportSelectionJson,
|
|
3791
|
+
selection: true
|
|
3792
|
+
}),
|
|
3793
|
+
exportCommand({
|
|
3794
|
+
id: "exportSelection.xlsx",
|
|
3795
|
+
format: "xlsx",
|
|
3796
|
+
label: ({ exportSelectionXlsx }) => exportSelectionXlsx,
|
|
3797
|
+
selection: true
|
|
3798
|
+
}),
|
|
3799
|
+
copyCommand({
|
|
3800
|
+
id: "copy.cell",
|
|
3801
|
+
label: ({ copyCell }) => copyCell,
|
|
3802
|
+
request: (context) => cellRequest(context, "cell")
|
|
3803
|
+
}),
|
|
3804
|
+
copyCommand({
|
|
3805
|
+
id: "copy.row",
|
|
3806
|
+
label: ({ copyRow }) => copyRow,
|
|
3807
|
+
request: (context) => cellRequest(context, "row")
|
|
3808
|
+
}),
|
|
3809
|
+
// Shown wherever the grid as a whole is asked for, as the exports are,
|
|
3810
|
+
// once anything can be selected; enabled once something is.
|
|
3811
|
+
copyCommand({
|
|
3812
|
+
id: "copy.selection",
|
|
3813
|
+
label: ({ copySelection }) => copySelection,
|
|
3814
|
+
request: (context) => isOn(context, EXPORT_AREAS) && isAnythingSelectable(context.options.selectable) ? { kind: "selection" } : null,
|
|
3815
|
+
isEnabled: canCopySelection
|
|
3816
|
+
}),
|
|
3817
|
+
// Shown where the exports are, under the grid's `print` defaults. A grid
|
|
3818
|
+
// with no rows still prints its header; one with no columns, nothing.
|
|
3819
|
+
{
|
|
3820
|
+
id: "print",
|
|
3821
|
+
label: ({ messages }) => messages.print,
|
|
3822
|
+
icon: "print",
|
|
3823
|
+
isVisible: (context) => isOn(context, EXPORT_AREAS),
|
|
3824
|
+
isEnabled: ({ view }) => view.columns.length > 0,
|
|
3825
|
+
execute: (context) => {
|
|
3826
|
+
const { print: options = {} } = context.options;
|
|
3827
|
+
context.host.print(exportPrintDocument(context, options), options);
|
|
3828
|
+
}
|
|
3829
|
+
}
|
|
3830
|
+
];
|
|
3831
|
+
}
|
|
3832
|
+
|
|
3833
|
+
// src/commands/registry.ts
|
|
3834
|
+
function isCommandVisible(command, context) {
|
|
3835
|
+
return command.isVisible?.(context) ?? true;
|
|
3836
|
+
}
|
|
3837
|
+
function isCommandEnabled(command, context) {
|
|
3838
|
+
return command.isEnabled?.(context) ?? true;
|
|
3839
|
+
}
|
|
3840
|
+
function resolveCommands(commands) {
|
|
3841
|
+
const registry = new Map(
|
|
3842
|
+
builtInCommands().map((command) => [command.id, command])
|
|
3843
|
+
);
|
|
3844
|
+
for (const command of commands ?? []) {
|
|
3845
|
+
if (command.id === "separator") {
|
|
3846
|
+
reportError(
|
|
3847
|
+
"a command cannot have the id `separator`, which a menu reads as a separator, so it is left out."
|
|
3848
|
+
);
|
|
3849
|
+
continue;
|
|
3850
|
+
}
|
|
3851
|
+
registry.set(command.id, command);
|
|
3852
|
+
}
|
|
3853
|
+
return registry;
|
|
3854
|
+
}
|
|
3855
|
+
function runCommand(command, context) {
|
|
3856
|
+
if (!isCommandVisible(command, context) || !isCommandEnabled(command, context)) {
|
|
3857
|
+
return false;
|
|
3858
|
+
}
|
|
3859
|
+
command.execute(context);
|
|
3860
|
+
return true;
|
|
3861
|
+
}
|
|
3862
|
+
|
|
3863
|
+
// src/menu/resolveMenu.ts
|
|
3864
|
+
var reportedUnknownIds = /* @__PURE__ */ new WeakMap();
|
|
3865
|
+
function reportUnknownId(commands, id) {
|
|
3866
|
+
let reported = reportedUnknownIds.get(commands);
|
|
3867
|
+
if (reported === void 0) {
|
|
3868
|
+
reported = /* @__PURE__ */ new Set();
|
|
3869
|
+
reportedUnknownIds.set(commands, reported);
|
|
3870
|
+
}
|
|
3871
|
+
if (reported.has(id)) {
|
|
3872
|
+
return;
|
|
3873
|
+
}
|
|
3874
|
+
reported.add(id);
|
|
3875
|
+
reportError(
|
|
3876
|
+
`the menu or toolbar entry \`${id}\` names no command, so it is left out.`
|
|
3877
|
+
);
|
|
3878
|
+
}
|
|
3879
|
+
function evaluate(value, context) {
|
|
3880
|
+
return typeof value === "function" ? value(context) : value;
|
|
3881
|
+
}
|
|
3882
|
+
function commandItem(command, context) {
|
|
3883
|
+
if (!isCommandVisible(command, context)) {
|
|
3884
|
+
return null;
|
|
3885
|
+
}
|
|
3886
|
+
return {
|
|
3887
|
+
type: "item",
|
|
3888
|
+
id: command.id,
|
|
3889
|
+
label: evaluate(command.label, context),
|
|
3890
|
+
icon: command.icon,
|
|
3891
|
+
shortcut: command.shortcut,
|
|
3892
|
+
disabled: !isCommandEnabled(command, context),
|
|
3893
|
+
checked: command.isChecked?.(context),
|
|
3894
|
+
run: () => runCommand(command, context)
|
|
3895
|
+
};
|
|
3896
|
+
}
|
|
3897
|
+
function inlineItem(entry, context) {
|
|
3898
|
+
if (evaluate(entry.hidden ?? false, context)) {
|
|
3899
|
+
return null;
|
|
3900
|
+
}
|
|
3901
|
+
const disabled = evaluate(entry.disabled ?? false, context);
|
|
3902
|
+
return {
|
|
3903
|
+
type: "item",
|
|
3904
|
+
id: entry.id,
|
|
3905
|
+
label: evaluate(entry.label, context),
|
|
3906
|
+
icon: entry.icon,
|
|
3907
|
+
shortcut: entry.shortcut,
|
|
3908
|
+
disabled,
|
|
3909
|
+
checked: entry.checked === void 0 ? void 0 : evaluate(entry.checked, context),
|
|
3910
|
+
run: () => {
|
|
3911
|
+
if (disabled) {
|
|
3912
|
+
return false;
|
|
3913
|
+
}
|
|
3914
|
+
entry.onSelect(context);
|
|
3915
|
+
return true;
|
|
3916
|
+
}
|
|
3917
|
+
};
|
|
3918
|
+
}
|
|
3919
|
+
function collapseSeparators(entries, isEdge = () => false) {
|
|
3920
|
+
const collapsed = [];
|
|
3921
|
+
for (const entry of entries) {
|
|
3922
|
+
const previous = collapsed.at(-1);
|
|
3923
|
+
if (entry.type !== "separator") {
|
|
3924
|
+
if (isEdge(entry) && previous?.type === "separator") {
|
|
3925
|
+
collapsed.pop();
|
|
3926
|
+
}
|
|
3927
|
+
collapsed.push(entry);
|
|
3928
|
+
} else if (previous !== void 0 && previous.type !== "separator" && !isEdge(previous)) {
|
|
3929
|
+
collapsed.push(entry);
|
|
3930
|
+
}
|
|
3931
|
+
}
|
|
3932
|
+
if (collapsed.at(-1)?.type === "separator") {
|
|
3933
|
+
collapsed.pop();
|
|
3934
|
+
}
|
|
3935
|
+
return collapsed;
|
|
3936
|
+
}
|
|
3937
|
+
function resolveMenuEntry(entry, index, options) {
|
|
3938
|
+
const { commands, context } = options;
|
|
3939
|
+
if (entry === "separator") {
|
|
3940
|
+
return { type: "separator", key: `separator-${String(index)}` };
|
|
3941
|
+
}
|
|
3942
|
+
if (typeof entry === "string") {
|
|
3943
|
+
const command = commands.get(entry);
|
|
3944
|
+
if (command === void 0) {
|
|
3945
|
+
reportUnknownId(commands, entry);
|
|
3946
|
+
return null;
|
|
3947
|
+
}
|
|
3948
|
+
return commandItem(command, context);
|
|
3949
|
+
}
|
|
3950
|
+
return entry.type === "item" ? inlineItem(entry, context) : resolveSubmenu(entry, options);
|
|
3951
|
+
}
|
|
3952
|
+
function resolveSubmenu(entry, options) {
|
|
3953
|
+
const items = resolveMenu(entry.items, options);
|
|
3954
|
+
return items.length === 0 ? null : {
|
|
3955
|
+
type: "submenu",
|
|
3956
|
+
id: entry.id,
|
|
3957
|
+
label: evaluate(entry.label, options.context),
|
|
3958
|
+
icon: entry.icon,
|
|
3959
|
+
items
|
|
3960
|
+
};
|
|
3961
|
+
}
|
|
3962
|
+
function resolveMenu(entries, options) {
|
|
3963
|
+
return collapseSeparators(
|
|
3964
|
+
entries.map((entry, index) => resolveMenuEntry(entry, index, options)).filter((entry) => entry !== null)
|
|
3965
|
+
);
|
|
3966
|
+
}
|
|
3967
|
+
|
|
3968
|
+
// src/menu/contextMenu.ts
|
|
3969
|
+
var defaultContextMenu = {
|
|
3970
|
+
header: [
|
|
3971
|
+
"sort.ascending",
|
|
3972
|
+
"sort.descending",
|
|
3973
|
+
"sort.clear",
|
|
3974
|
+
"separator",
|
|
3975
|
+
"filter.open",
|
|
3976
|
+
"filter.clear",
|
|
3977
|
+
"separator",
|
|
3978
|
+
"group.add",
|
|
3979
|
+
"group.remove",
|
|
3980
|
+
"separator",
|
|
3981
|
+
"column.autoFit",
|
|
3982
|
+
"column.autoFitAll",
|
|
3983
|
+
"separator",
|
|
3984
|
+
"column.hide",
|
|
3985
|
+
"column.choose"
|
|
3986
|
+
],
|
|
3987
|
+
cell: [
|
|
3988
|
+
"copy.cell",
|
|
3989
|
+
"copy.row",
|
|
3990
|
+
"copy.selection",
|
|
3991
|
+
"separator",
|
|
3992
|
+
"selection.selectAllRows",
|
|
3993
|
+
"selection.clear",
|
|
3994
|
+
"separator",
|
|
3995
|
+
"group.expandAll",
|
|
3996
|
+
"group.collapseAll"
|
|
3997
|
+
],
|
|
3998
|
+
group: ["group.expandAll", "group.collapseAll", "separator", "group.remove"],
|
|
3999
|
+
body: []
|
|
4000
|
+
};
|
|
4001
|
+
function hasContextMenu(contextMenu, area) {
|
|
4002
|
+
if (contextMenu === void 0 || contextMenu === false) {
|
|
4003
|
+
return false;
|
|
4004
|
+
}
|
|
4005
|
+
const entries = (contextMenu === true ? void 0 : contextMenu[area]) ?? defaultContextMenu[area];
|
|
4006
|
+
return typeof entries === "function" || entries.length > 0;
|
|
4007
|
+
}
|
|
4008
|
+
function entriesFor(given, request) {
|
|
4009
|
+
if (given === void 0) {
|
|
4010
|
+
return request.defaults;
|
|
4011
|
+
}
|
|
4012
|
+
return typeof given === "function" ? given(request) ?? [] : given;
|
|
4013
|
+
}
|
|
4014
|
+
function contextMenuEntries(contextMenu, context) {
|
|
4015
|
+
if (contextMenu === void 0 || contextMenu === false) {
|
|
4016
|
+
return [];
|
|
4017
|
+
}
|
|
4018
|
+
const options = contextMenu === true ? {} : contextMenu;
|
|
4019
|
+
const { target } = context;
|
|
4020
|
+
switch (target.area) {
|
|
4021
|
+
case "header":
|
|
4022
|
+
return entriesFor(options.header, {
|
|
4023
|
+
target,
|
|
4024
|
+
defaults: defaultContextMenu.header,
|
|
4025
|
+
context
|
|
4026
|
+
});
|
|
4027
|
+
case "cell":
|
|
4028
|
+
return entriesFor(options.cell, {
|
|
4029
|
+
target,
|
|
4030
|
+
defaults: defaultContextMenu.cell,
|
|
4031
|
+
context
|
|
4032
|
+
});
|
|
4033
|
+
case "group":
|
|
4034
|
+
return entriesFor(options.group, {
|
|
4035
|
+
target,
|
|
4036
|
+
defaults: defaultContextMenu.group,
|
|
4037
|
+
context
|
|
4038
|
+
});
|
|
4039
|
+
case "body":
|
|
4040
|
+
return entriesFor(options.body, {
|
|
4041
|
+
target,
|
|
4042
|
+
defaults: defaultContextMenu.body,
|
|
4043
|
+
context
|
|
4044
|
+
});
|
|
4045
|
+
default:
|
|
4046
|
+
return [];
|
|
4047
|
+
}
|
|
4048
|
+
}
|
|
4049
|
+
|
|
4050
|
+
// src/menu/navigation.ts
|
|
4051
|
+
function startIndex(count, from, move) {
|
|
4052
|
+
const focused = from >= 0 && from < count;
|
|
4053
|
+
switch (move) {
|
|
4054
|
+
case "first":
|
|
4055
|
+
return 0;
|
|
4056
|
+
case "last":
|
|
4057
|
+
return count - 1;
|
|
4058
|
+
case "next":
|
|
4059
|
+
return focused ? from + 1 : 0;
|
|
4060
|
+
case "previous":
|
|
4061
|
+
return focused ? from - 1 : count - 1;
|
|
4062
|
+
}
|
|
4063
|
+
}
|
|
4064
|
+
function nextMenuIndex(entries, from, move) {
|
|
4065
|
+
const count = entries.length;
|
|
4066
|
+
const step = move === "next" || move === "first" ? 1 : -1;
|
|
4067
|
+
const start = startIndex(count, from, move);
|
|
4068
|
+
for (let offset = 0; offset < count; offset++) {
|
|
4069
|
+
const index = ((start + step * offset) % count + count) % count;
|
|
4070
|
+
if (entries[index]?.type !== "separator") {
|
|
4071
|
+
return index;
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
return -1;
|
|
4075
|
+
}
|
|
4076
|
+
function typeaheadIndex(labels, from, query) {
|
|
4077
|
+
const typed = query.toLowerCase();
|
|
4078
|
+
const firstCodePoint = typed.codePointAt(0);
|
|
4079
|
+
if (firstCodePoint === void 0) {
|
|
4080
|
+
return -1;
|
|
4081
|
+
}
|
|
4082
|
+
const first = String.fromCodePoint(firstCodePoint);
|
|
4083
|
+
const repeated = typed.replaceAll(first, "") === "";
|
|
4084
|
+
const prefix = repeated ? first : typed;
|
|
4085
|
+
const start = repeated ? from + 1 : Math.max(from, 0);
|
|
4086
|
+
for (let offset = 0; offset < labels.length; offset++) {
|
|
4087
|
+
const index = (start + offset) % labels.length;
|
|
4088
|
+
if (labels[index]?.toLowerCase().startsWith(prefix) === true) {
|
|
4089
|
+
return index;
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
return -1;
|
|
4093
|
+
}
|
|
4094
|
+
|
|
4095
|
+
// src/toolbar/options.ts
|
|
4096
|
+
var defaultToolbar = ["search", "spacer", "filter.clearAll", "column.choose"];
|
|
4097
|
+
function toolbarEntries(toolbar) {
|
|
4098
|
+
if (toolbar === void 0 || toolbar === false) {
|
|
4099
|
+
return [];
|
|
4100
|
+
}
|
|
4101
|
+
return (toolbar === true ? void 0 : toolbar.items) ?? defaultToolbar;
|
|
4102
|
+
}
|
|
4103
|
+
|
|
4104
|
+
// src/toolbar/resolveToolbar.ts
|
|
4105
|
+
function isSpacer(entry) {
|
|
4106
|
+
return entry.type === "spacer";
|
|
4107
|
+
}
|
|
4108
|
+
function exportMenu() {
|
|
4109
|
+
return {
|
|
4110
|
+
type: "submenu",
|
|
4111
|
+
id: "export",
|
|
4112
|
+
label: ({ messages }) => messages.exportMenu,
|
|
4113
|
+
items: [
|
|
4114
|
+
"export.csv",
|
|
4115
|
+
"export.json",
|
|
4116
|
+
"export.xlsx",
|
|
4117
|
+
"export.pdf",
|
|
4118
|
+
"separator",
|
|
4119
|
+
"exportSelection.csv",
|
|
4120
|
+
"exportSelection.json",
|
|
4121
|
+
"exportSelection.xlsx"
|
|
4122
|
+
]
|
|
4123
|
+
};
|
|
4124
|
+
}
|
|
4125
|
+
function resolveToolbar(entries, options) {
|
|
4126
|
+
const { commands, contextFor } = options;
|
|
4127
|
+
function resolveEntry(entry, index) {
|
|
4128
|
+
if (entry === "search") {
|
|
4129
|
+
return { type: "search", key: "search" };
|
|
4130
|
+
}
|
|
4131
|
+
if (entry === "spacer") {
|
|
4132
|
+
return { type: "spacer", key: `spacer-${String(index)}` };
|
|
4133
|
+
}
|
|
4134
|
+
if (typeof entry === "object" && entry.type === "custom") {
|
|
4135
|
+
return { type: "custom", id: entry.id, key: entry.id };
|
|
4136
|
+
}
|
|
4137
|
+
if (entry === "separator") {
|
|
4138
|
+
return { type: "separator", key: `separator-${String(index)}` };
|
|
4139
|
+
}
|
|
4140
|
+
const menuEntry = entry === "export" ? exportMenu() : entry;
|
|
4141
|
+
const resolved = resolveMenuEntry(menuEntry, index, {
|
|
4142
|
+
commands,
|
|
4143
|
+
context: contextFor({
|
|
4144
|
+
area: "toolbar",
|
|
4145
|
+
itemId: typeof menuEntry === "string" ? menuEntry : menuEntry.id
|
|
4146
|
+
})
|
|
4147
|
+
});
|
|
4148
|
+
if (resolved === null || resolved.type === "separator") {
|
|
4149
|
+
return resolved;
|
|
4150
|
+
}
|
|
4151
|
+
if (resolved.type === "submenu") {
|
|
4152
|
+
return { ...resolved, type: "menu", key: resolved.id };
|
|
4153
|
+
}
|
|
4154
|
+
const { checked, ...item } = resolved;
|
|
4155
|
+
return { ...item, type: "button", key: item.id, pressed: checked };
|
|
4156
|
+
}
|
|
4157
|
+
return collapseSeparators(
|
|
4158
|
+
entries.map(resolveEntry).filter((entry) => entry !== null),
|
|
4159
|
+
isSpacer
|
|
4160
|
+
);
|
|
4161
|
+
}
|
|
4162
|
+
|
|
4163
|
+
// src/toolbar/overflow.ts
|
|
4164
|
+
function fitToolbarItems({
|
|
4165
|
+
widths,
|
|
4166
|
+
available,
|
|
4167
|
+
moreWidth,
|
|
4168
|
+
pinned
|
|
4169
|
+
}) {
|
|
4170
|
+
const total = widths.reduce((sum, width) => sum + width, 0);
|
|
4171
|
+
if (total <= available) {
|
|
4172
|
+
return widths.length;
|
|
4173
|
+
}
|
|
4174
|
+
let used = moreWidth + widths.reduce((sum, width, index) => sum + (pinned[index] ? width : 0), 0);
|
|
4175
|
+
for (const [index, width] of widths.entries()) {
|
|
4176
|
+
if (!pinned[index]) {
|
|
4177
|
+
used += width;
|
|
4178
|
+
if (used > available) {
|
|
4179
|
+
return index;
|
|
4180
|
+
}
|
|
4181
|
+
}
|
|
4182
|
+
}
|
|
4183
|
+
return widths.length;
|
|
4184
|
+
}
|
|
4185
|
+
function isPinnedToolbarItem(item) {
|
|
4186
|
+
return item.type === "search" || item.type === "custom";
|
|
4187
|
+
}
|
|
4188
|
+
function asMenuEntry(item) {
|
|
4189
|
+
switch (item.type) {
|
|
4190
|
+
case "button":
|
|
4191
|
+
return {
|
|
4192
|
+
type: "item",
|
|
4193
|
+
id: item.id,
|
|
4194
|
+
label: item.label,
|
|
4195
|
+
icon: item.icon,
|
|
4196
|
+
shortcut: item.shortcut,
|
|
4197
|
+
disabled: item.disabled,
|
|
4198
|
+
checked: item.pressed,
|
|
4199
|
+
run: item.run
|
|
4200
|
+
};
|
|
4201
|
+
case "menu":
|
|
4202
|
+
return {
|
|
4203
|
+
type: "submenu",
|
|
4204
|
+
id: item.id,
|
|
4205
|
+
label: item.label,
|
|
4206
|
+
icon: item.icon,
|
|
4207
|
+
items: item.items
|
|
4208
|
+
};
|
|
4209
|
+
case "separator":
|
|
4210
|
+
return item;
|
|
4211
|
+
default:
|
|
4212
|
+
return null;
|
|
4213
|
+
}
|
|
4214
|
+
}
|
|
4215
|
+
function splitToolbarItems(items, fit) {
|
|
4216
|
+
const fits = (item, index) => index < fit || isPinnedToolbarItem(item);
|
|
4217
|
+
return {
|
|
4218
|
+
shown: collapseSeparators(
|
|
4219
|
+
items.filter(fits),
|
|
4220
|
+
(item) => item.type === "spacer"
|
|
4221
|
+
),
|
|
4222
|
+
overflow: collapseSeparators(
|
|
4223
|
+
items.filter((item, index) => !fits(item, index)).map(asMenuEntry).filter((entry) => entry !== null)
|
|
4224
|
+
)
|
|
4225
|
+
};
|
|
4226
|
+
}
|
|
4227
|
+
|
|
4228
|
+
// src/toolbar/search.ts
|
|
4229
|
+
function searchTextOf(filter) {
|
|
4230
|
+
const query = filterQueryFor(filter) ?? "";
|
|
4231
|
+
const start = query.startsWith("%") ? 1 : 0;
|
|
4232
|
+
const end = query.endsWith("%") ? -1 : void 0;
|
|
4233
|
+
return query.slice(start, end);
|
|
4234
|
+
}
|
|
4235
|
+
function setSearchText(filter, text) {
|
|
4236
|
+
return setColumnFilter(filter, void 0, text === "" ? "" : `%${text}%`);
|
|
4237
|
+
}
|
|
4238
|
+
|
|
4239
|
+
// src/util/navigation.ts
|
|
4240
|
+
var HEADER_ROW = -1;
|
|
4241
|
+
function clampFocus(focus, rowCount, columnCount2) {
|
|
4242
|
+
return {
|
|
4243
|
+
rowIndex: Math.min(Math.max(focus.rowIndex, HEADER_ROW), rowCount - 1),
|
|
4244
|
+
columnIndex: Math.min(
|
|
4245
|
+
Math.max(focus.columnIndex, 0),
|
|
4246
|
+
Math.max(columnCount2 - 1, 0)
|
|
4247
|
+
)
|
|
4248
|
+
};
|
|
4249
|
+
}
|
|
4250
|
+
function noRowsSkippable() {
|
|
4251
|
+
return false;
|
|
4252
|
+
}
|
|
4253
|
+
function skipSkippableRows(rowIndex, direction, rowCount, isSkippableRow, fallback) {
|
|
4254
|
+
let next = rowIndex;
|
|
4255
|
+
while (next >= 0 && next < rowCount && isSkippableRow(next)) {
|
|
4256
|
+
const stepped = next + direction;
|
|
4257
|
+
if (stepped < HEADER_ROW || stepped >= rowCount) {
|
|
4258
|
+
return fallback;
|
|
4259
|
+
}
|
|
4260
|
+
next = stepped;
|
|
4261
|
+
}
|
|
4262
|
+
return next;
|
|
4263
|
+
}
|
|
4264
|
+
function nextFocusForKey(key, modifiers, focus, rowCount, columnCount2, pageSize, isSkippableRow = noRowsSkippable) {
|
|
4265
|
+
if (modifiers.altKey) {
|
|
4266
|
+
return null;
|
|
4267
|
+
}
|
|
4268
|
+
const { rowIndex, columnIndex } = focus;
|
|
4269
|
+
switch (key) {
|
|
4270
|
+
case "ArrowLeft":
|
|
4271
|
+
if (modifiers.ctrlKey) return null;
|
|
4272
|
+
return { rowIndex, columnIndex: columnIndex - 1 };
|
|
4273
|
+
case "ArrowRight":
|
|
4274
|
+
if (modifiers.ctrlKey) return null;
|
|
4275
|
+
return { rowIndex, columnIndex: columnIndex + 1 };
|
|
4276
|
+
case "ArrowUp":
|
|
4277
|
+
return {
|
|
4278
|
+
rowIndex: skipSkippableRows(
|
|
4279
|
+
rowIndex - 1,
|
|
4280
|
+
-1,
|
|
4281
|
+
rowCount,
|
|
4282
|
+
isSkippableRow,
|
|
4283
|
+
rowIndex
|
|
4284
|
+
),
|
|
4285
|
+
columnIndex
|
|
4286
|
+
};
|
|
4287
|
+
case "ArrowDown":
|
|
4288
|
+
return {
|
|
4289
|
+
rowIndex: skipSkippableRows(
|
|
4290
|
+
rowIndex + 1,
|
|
4291
|
+
1,
|
|
4292
|
+
rowCount,
|
|
4293
|
+
isSkippableRow,
|
|
4294
|
+
rowIndex
|
|
4295
|
+
),
|
|
4296
|
+
columnIndex
|
|
4297
|
+
};
|
|
4298
|
+
// Ctrl takes Home and End to the grid's ends rather than the row's, which
|
|
4299
|
+
// is the one place Ctrl is navigation rather than reorder.
|
|
4300
|
+
case "Home":
|
|
4301
|
+
return modifiers.ctrlKey ? { rowIndex: HEADER_ROW, columnIndex: 0 } : { rowIndex, columnIndex: 0 };
|
|
4302
|
+
case "End":
|
|
4303
|
+
return modifiers.ctrlKey ? {
|
|
4304
|
+
rowIndex: skipSkippableRows(
|
|
4305
|
+
rowCount - 1,
|
|
4306
|
+
-1,
|
|
4307
|
+
rowCount,
|
|
4308
|
+
isSkippableRow,
|
|
4309
|
+
rowIndex
|
|
4310
|
+
),
|
|
4311
|
+
columnIndex: columnCount2 - 1
|
|
4312
|
+
} : { rowIndex, columnIndex: columnCount2 - 1 };
|
|
4313
|
+
case "PageUp":
|
|
4314
|
+
return {
|
|
4315
|
+
rowIndex: skipSkippableRows(
|
|
4316
|
+
rowIndex - pageSize,
|
|
4317
|
+
-1,
|
|
4318
|
+
rowCount,
|
|
4319
|
+
isSkippableRow,
|
|
4320
|
+
rowIndex
|
|
4321
|
+
),
|
|
4322
|
+
columnIndex
|
|
4323
|
+
};
|
|
4324
|
+
case "PageDown":
|
|
4325
|
+
return {
|
|
4326
|
+
rowIndex: skipSkippableRows(
|
|
4327
|
+
rowIndex + pageSize,
|
|
4328
|
+
1,
|
|
4329
|
+
rowCount,
|
|
4330
|
+
isSkippableRow,
|
|
4331
|
+
rowIndex
|
|
4332
|
+
),
|
|
4333
|
+
columnIndex
|
|
4334
|
+
};
|
|
4335
|
+
default:
|
|
4336
|
+
return null;
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
|
|
4340
|
+
// src/util/paginationWindow.ts
|
|
4341
|
+
function paginationWindow(currentPage, pageCount, options) {
|
|
4342
|
+
if (pageCount <= 0) {
|
|
4343
|
+
return [];
|
|
4344
|
+
}
|
|
4345
|
+
const boundaryCount = Math.max(0, options?.boundaryCount ?? 1);
|
|
4346
|
+
const siblingCount = Math.max(0, options?.siblingCount ?? 1);
|
|
4347
|
+
const current = Math.min(Math.max(currentPage, 1), pageCount);
|
|
4348
|
+
const pages = /* @__PURE__ */ new Set();
|
|
4349
|
+
for (let page = 1; page <= Math.min(boundaryCount, pageCount); page++) {
|
|
4350
|
+
pages.add(page);
|
|
4351
|
+
}
|
|
4352
|
+
for (let page = Math.max(pageCount - boundaryCount + 1, 1); page <= pageCount; page++) {
|
|
4353
|
+
pages.add(page);
|
|
4354
|
+
}
|
|
4355
|
+
for (let page = Math.max(current - siblingCount, 1); page <= Math.min(current + siblingCount, pageCount); page++) {
|
|
4356
|
+
pages.add(page);
|
|
4357
|
+
}
|
|
4358
|
+
const sorted = Array.from(pages).sort((a, b) => a - b);
|
|
4359
|
+
const entries = [];
|
|
4360
|
+
let previous;
|
|
4361
|
+
for (const page of sorted) {
|
|
4362
|
+
if (previous !== void 0) {
|
|
4363
|
+
if (page - previous === 2) {
|
|
4364
|
+
entries.push(previous + 1);
|
|
4365
|
+
} else if (page - previous > 2) {
|
|
4366
|
+
entries.push("ellipsis");
|
|
4367
|
+
}
|
|
4368
|
+
}
|
|
4369
|
+
entries.push(page);
|
|
4370
|
+
previous = page;
|
|
3245
4371
|
}
|
|
3246
|
-
return
|
|
4372
|
+
return entries;
|
|
3247
4373
|
}
|
|
3248
|
-
|
|
3249
|
-
|
|
4374
|
+
|
|
4375
|
+
// src/util/columnChooser.ts
|
|
4376
|
+
function isHideable(column) {
|
|
4377
|
+
return column.column.hideable !== false;
|
|
3250
4378
|
}
|
|
3251
4379
|
function matchesSearch2(label, search) {
|
|
3252
4380
|
return label.toLowerCase().includes(search.toLowerCase());
|
|
3253
4381
|
}
|
|
3254
|
-
function
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
return { options: [], total: 0, truncated: false, blanks: 0 };
|
|
4382
|
+
function selectAllStateOf(states) {
|
|
4383
|
+
if (states.length > 0 && states.every(Boolean)) {
|
|
4384
|
+
return "checked";
|
|
3258
4385
|
}
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
4386
|
+
return states.some(Boolean) ? "mixed" : "unchecked";
|
|
4387
|
+
}
|
|
4388
|
+
function resolveColumnChooser({
|
|
4389
|
+
columns,
|
|
4390
|
+
visibility,
|
|
4391
|
+
search = ""
|
|
4392
|
+
}) {
|
|
4393
|
+
const all = columns.map((column) => ({
|
|
4394
|
+
column,
|
|
4395
|
+
label: resolveColumnHeaderText(column.column),
|
|
4396
|
+
visible: isColumnVisible(column.column, visibility)
|
|
4397
|
+
}));
|
|
4398
|
+
const shownCount = all.filter(({ visible }) => visible).length;
|
|
4399
|
+
const matched = all.filter(({ label }) => matchesSearch2(label, search));
|
|
4400
|
+
const entries = matched.map(
|
|
4401
|
+
({ column, label, visible }) => ({
|
|
4402
|
+
columnId: column.id,
|
|
4403
|
+
label,
|
|
4404
|
+
visible,
|
|
4405
|
+
disabled: visible && (!isHideable(column) || shownCount === 1)
|
|
4406
|
+
})
|
|
3262
4407
|
);
|
|
3263
|
-
const
|
|
3264
|
-
|
|
3265
|
-
for (const row of rows) {
|
|
3266
|
-
if (keep !== void 0 && !keep(row)) {
|
|
3267
|
-
continue;
|
|
3268
|
-
}
|
|
3269
|
-
const value = accessDotted(row, column.field);
|
|
3270
|
-
if (isEmptyCell(value)) {
|
|
3271
|
-
blanks++;
|
|
3272
|
-
} else if (isOfferable(value)) {
|
|
3273
|
-
const key = filterValueKey(value, column.type);
|
|
3274
|
-
const bucket = buckets.get(key);
|
|
3275
|
-
if (bucket === void 0) {
|
|
3276
|
-
buckets.set(key, { value, row, count: 1 });
|
|
3277
|
-
} else {
|
|
3278
|
-
bucket.count++;
|
|
3279
|
-
}
|
|
3280
|
-
}
|
|
3281
|
-
}
|
|
3282
|
-
const { search = "", limit, locale } = query;
|
|
3283
|
-
const messages = resolveMessages(query.messages);
|
|
3284
|
-
const options = [...buckets.values()].sort((a, b) => compareValues(a.value, b.value, column.type)).map(({ value, row, count }) => ({
|
|
3285
|
-
value,
|
|
3286
|
-
label: formatCellValue(value, column, locale, row, messages),
|
|
3287
|
-
count
|
|
3288
|
-
})).filter((option) => matchesSearch2(option.label, search));
|
|
3289
|
-
const shown = limit === void 0 ? options : options.slice(0, limit);
|
|
4408
|
+
const covered = matched.filter(({ column }) => isHideable(column));
|
|
4409
|
+
const selectAll2 = selectAllStateOf(covered.map(({ visible }) => visible));
|
|
3290
4410
|
return {
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
blanks,
|
|
3295
|
-
columnType: column.type
|
|
4411
|
+
entries,
|
|
4412
|
+
selectAll: selectAll2,
|
|
4413
|
+
toggleAll: toggleAllOf(covered, selectAll2, shownCount)
|
|
3296
4414
|
};
|
|
3297
4415
|
}
|
|
3298
|
-
function
|
|
3299
|
-
|
|
3300
|
-
}
|
|
3301
|
-
|
|
3302
|
-
return
|
|
3303
|
-
draft.options.map((option) => [keyOf2(draft, option.value), option.value])
|
|
3304
|
-
);
|
|
4416
|
+
function toggleAllOf(covered, selectAll2, shownCount) {
|
|
4417
|
+
const show = selectAll2 !== "checked";
|
|
4418
|
+
const changing = covered.filter(({ visible }) => visible !== show).map(({ column }) => column.id);
|
|
4419
|
+
const columnIds = !show && changing.length === shownCount ? changing.slice(1) : changing;
|
|
4420
|
+
return columnIds.length === 0 ? null : { visible: show, columnIds };
|
|
3305
4421
|
}
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
switch (shape.kind) {
|
|
3319
|
-
case "values":
|
|
3320
|
-
return {
|
|
3321
|
-
...draft,
|
|
3322
|
-
checked: new Map(
|
|
3323
|
-
shape.values.filter((value) => !isEmptyCell(value)).map((value) => [keyOf2(draft, value), value])
|
|
3324
|
-
),
|
|
3325
|
-
includeBlanks: shape.includeBlanks
|
|
3326
|
-
};
|
|
3327
|
-
case "conditions":
|
|
3328
|
-
return {
|
|
3329
|
-
...draft,
|
|
3330
|
-
mode: "conditions",
|
|
3331
|
-
conditions: shape.conditions,
|
|
3332
|
-
combinator: shape.combinator
|
|
3333
|
-
};
|
|
3334
|
-
case "none":
|
|
3335
|
-
case "custom":
|
|
3336
|
-
return draft;
|
|
4422
|
+
|
|
4423
|
+
// src/filterMenu/conditions.ts
|
|
4424
|
+
var ROW_COUNT = 2;
|
|
4425
|
+
function rowOf(condition, type) {
|
|
4426
|
+
const { operator, value } = condition;
|
|
4427
|
+
if (isScalarList(value)) {
|
|
4428
|
+
const [low, high] = value;
|
|
4429
|
+
return {
|
|
4430
|
+
operator,
|
|
4431
|
+
text: formatFilterOperand(low, type),
|
|
4432
|
+
textTo: formatFilterOperand(high, type)
|
|
4433
|
+
};
|
|
3337
4434
|
}
|
|
4435
|
+
return { operator, text: formatFilterOperand(value, type), textTo: "" };
|
|
3338
4436
|
}
|
|
3339
|
-
function
|
|
3340
|
-
|
|
3341
|
-
|
|
4437
|
+
function conditionRowsFrom(shape, operators, type) {
|
|
4438
|
+
const filled = shape.kind === "conditions" ? shape.conditions.map((condition) => rowOf(condition, type)) : [];
|
|
4439
|
+
const [first] = operators;
|
|
4440
|
+
if (first === void 0) {
|
|
4441
|
+
return filled;
|
|
4442
|
+
}
|
|
4443
|
+
const blank = { operator: first, text: "", textTo: "" };
|
|
4444
|
+
return Array.from(
|
|
4445
|
+
{ length: ROW_COUNT },
|
|
4446
|
+
(_, index) => filled[index] ?? blank
|
|
3342
4447
|
);
|
|
3343
4448
|
}
|
|
3344
|
-
function
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
4449
|
+
function isEmptyRow(row) {
|
|
4450
|
+
switch (filterOperandArity(row.operator)) {
|
|
4451
|
+
case 0:
|
|
4452
|
+
return false;
|
|
4453
|
+
case 1:
|
|
4454
|
+
return row.text === "";
|
|
4455
|
+
case 2:
|
|
4456
|
+
return row.text === "" && row.textTo === "";
|
|
4457
|
+
}
|
|
4458
|
+
}
|
|
4459
|
+
function conditionOf(row, columnId, type) {
|
|
4460
|
+
const { operator } = row;
|
|
4461
|
+
switch (filterOperandArity(operator)) {
|
|
4462
|
+
case 0:
|
|
4463
|
+
return { columnId, operator };
|
|
4464
|
+
case 1: {
|
|
4465
|
+
const value = parseFilterOperand(row.text, type);
|
|
4466
|
+
return value === void 0 ? void 0 : { columnId, operator, value };
|
|
4467
|
+
}
|
|
4468
|
+
case 2: {
|
|
4469
|
+
const low = parseFilterOperand(row.text, type);
|
|
4470
|
+
const high = parseFilterOperand(row.textTo, type);
|
|
4471
|
+
return low === void 0 || high === void 0 ? void 0 : { columnId, operator, value: [low, high] };
|
|
4472
|
+
}
|
|
3356
4473
|
}
|
|
3357
|
-
return states.some(Boolean) ? "mixed" : "unchecked";
|
|
3358
4474
|
}
|
|
3359
|
-
function
|
|
3360
|
-
const
|
|
3361
|
-
const
|
|
3362
|
-
for (const
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
4475
|
+
function conditionsFromRows(rows, columnId, type) {
|
|
4476
|
+
const conditions = [];
|
|
4477
|
+
const invalid = [];
|
|
4478
|
+
for (const [index, row] of rows.entries()) {
|
|
4479
|
+
if (index > 0 && isEmptyRow(row)) {
|
|
4480
|
+
continue;
|
|
4481
|
+
}
|
|
4482
|
+
const condition = conditionOf(row, columnId, type);
|
|
4483
|
+
if (condition === void 0) {
|
|
4484
|
+
invalid.push(index);
|
|
3366
4485
|
} else {
|
|
3367
|
-
|
|
4486
|
+
conditions.push(condition);
|
|
3368
4487
|
}
|
|
3369
4488
|
}
|
|
3370
|
-
return {
|
|
3371
|
-
...draft,
|
|
3372
|
-
mode: "values",
|
|
3373
|
-
checked,
|
|
3374
|
-
includeBlanks: selectAllCoversBlanks(draft) ? check : draft.includeBlanks
|
|
3375
|
-
};
|
|
4489
|
+
return { conditions, invalid };
|
|
3376
4490
|
}
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
case "toggleValue": {
|
|
3382
|
-
const key = keyOf2(draft, action.value);
|
|
3383
|
-
const checked = new Map(draft.checked);
|
|
3384
|
-
if (checked.has(key)) {
|
|
3385
|
-
checked.delete(key);
|
|
3386
|
-
} else {
|
|
3387
|
-
checked.set(key, action.value);
|
|
3388
|
-
}
|
|
3389
|
-
return { ...draft, mode: "values", checked };
|
|
3390
|
-
}
|
|
3391
|
-
case "toggleAll":
|
|
3392
|
-
return toggleAll(draft);
|
|
3393
|
-
case "setIncludeBlanks":
|
|
3394
|
-
return { ...draft, mode: "values", includeBlanks: action.includeBlanks };
|
|
3395
|
-
case "setConditions":
|
|
3396
|
-
return {
|
|
3397
|
-
...draft,
|
|
3398
|
-
mode: "conditions",
|
|
3399
|
-
conditions: action.conditions,
|
|
3400
|
-
combinator: action.combinator ?? draft.combinator
|
|
3401
|
-
};
|
|
3402
|
-
case "clear":
|
|
3403
|
-
return {
|
|
3404
|
-
...draft,
|
|
3405
|
-
search: "",
|
|
3406
|
-
checked: everyOption(draft),
|
|
3407
|
-
includeBlanks: draft.blanks > 0,
|
|
3408
|
-
conditions: []
|
|
3409
|
-
};
|
|
3410
|
-
}
|
|
4491
|
+
|
|
4492
|
+
// src/filterMenu/valueCache.ts
|
|
4493
|
+
function sameSource(a, b) {
|
|
4494
|
+
return a.rows === b.rows && a.dataColumns === b.dataColumns && a.locale === b.locale && a.messages === b.messages;
|
|
3411
4495
|
}
|
|
3412
|
-
function
|
|
3413
|
-
return
|
|
3414
|
-
(option) => draft.checked.has(keyOf2(draft, option.value))
|
|
3415
|
-
) && (draft.includeBlanks || draft.blanks === 0);
|
|
4496
|
+
function sameEntries(a, b) {
|
|
4497
|
+
return a.length === b.length && a.every((entry, index) => entry === b[index]);
|
|
3416
4498
|
}
|
|
3417
|
-
function
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
4499
|
+
function createColumnValueOptionsCache() {
|
|
4500
|
+
let source;
|
|
4501
|
+
const lists = /* @__PURE__ */ new Map();
|
|
4502
|
+
return {
|
|
4503
|
+
get: (rows, dataColumns, filter, columnId, query = {}) => {
|
|
4504
|
+
const { locale, messages } = query;
|
|
4505
|
+
const current = { rows, dataColumns, locale, messages };
|
|
4506
|
+
if (source === void 0 || !sameSource(source, current)) {
|
|
4507
|
+
lists.clear();
|
|
4508
|
+
source = current;
|
|
4509
|
+
}
|
|
4510
|
+
const others = filter.filter((entry) => !touchesColumn(entry, columnId));
|
|
4511
|
+
const cached2 = lists.get(columnId);
|
|
4512
|
+
if (cached2 !== void 0 && sameEntries(cached2.others, others)) {
|
|
4513
|
+
return cached2.values;
|
|
4514
|
+
}
|
|
4515
|
+
const values = columnValueOptions(
|
|
4516
|
+
rows,
|
|
4517
|
+
dataColumns,
|
|
4518
|
+
filter,
|
|
4519
|
+
columnId,
|
|
4520
|
+
messages === void 0 ? { locale } : { locale, messages }
|
|
4521
|
+
);
|
|
4522
|
+
lists.set(columnId, { others, values });
|
|
4523
|
+
return values;
|
|
3437
4524
|
}
|
|
3438
|
-
|
|
4525
|
+
};
|
|
3439
4526
|
}
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
4527
|
+
|
|
4528
|
+
// src/filterMenu/checklist.ts
|
|
4529
|
+
function filterChecklist(draft, { valueLimit, blanksLabel }) {
|
|
4530
|
+
const visible = visibleFilterOptions(draft);
|
|
4531
|
+
const total = visible.length;
|
|
4532
|
+
if (draft.search === "" && total > valueLimit) {
|
|
4533
|
+
return { items: [], needsSearch: true, hiddenCount: 0, total };
|
|
4534
|
+
}
|
|
4535
|
+
const items = visible.slice(0, valueLimit).map(({ value, label, count }) => {
|
|
4536
|
+
const key = keyOf2(draft, value);
|
|
4537
|
+
const checked = draft.checked.has(key);
|
|
4538
|
+
return { kind: "value", key, value, label, count, checked };
|
|
4539
|
+
});
|
|
4540
|
+
const hiddenCount = total - items.length;
|
|
4541
|
+
if (selectAllCoversBlanks(draft)) {
|
|
4542
|
+
items.push({
|
|
4543
|
+
kind: "blanks",
|
|
4544
|
+
// Blank cells' own key, which no offered value has.
|
|
4545
|
+
key: keyOf2(draft, null),
|
|
4546
|
+
label: blanksLabel,
|
|
4547
|
+
count: draft.blanks,
|
|
4548
|
+
checked: draft.includeBlanks
|
|
4549
|
+
});
|
|
3445
4550
|
}
|
|
3446
|
-
|
|
3447
|
-
return [...kept.slice(0, first), ...replacement, ...kept.slice(first)];
|
|
4551
|
+
return { items, needsSearch: false, hiddenCount, total };
|
|
3448
4552
|
}
|
|
3449
4553
|
|
|
3450
4554
|
// src/util/virtualization.ts
|
|
@@ -3487,8 +4591,15 @@ function visibleRange(heights, estimatedHeight, scrollTop, viewportHeight, overs
|
|
|
3487
4591
|
offsetAfter
|
|
3488
4592
|
};
|
|
3489
4593
|
}
|
|
4594
|
+
|
|
4595
|
+
// src/presets.ts
|
|
4596
|
+
var excelFilter = {
|
|
4597
|
+
filterableColumns: true,
|
|
4598
|
+
filterMenu: { ui: "excel", showCounts: true }
|
|
4599
|
+
};
|
|
3490
4600
|
export {
|
|
3491
4601
|
DEFAULT_COLUMN_SIZES,
|
|
4602
|
+
DEFAULT_FILTER_VALUE_LIMIT,
|
|
3492
4603
|
HEADER_ROW,
|
|
3493
4604
|
KEYBOARD_STEP,
|
|
3494
4605
|
accessDotted,
|
|
@@ -3498,7 +4609,9 @@ export {
|
|
|
3498
4609
|
applyColumnVisibility,
|
|
3499
4610
|
applySelectionIntent,
|
|
3500
4611
|
beginColumnResize,
|
|
4612
|
+
buildExportTable,
|
|
3501
4613
|
buildKeyShortcuts,
|
|
4614
|
+
buildPrintDocument,
|
|
3502
4615
|
builtInCommands,
|
|
3503
4616
|
clampColumnWidth,
|
|
3504
4617
|
clampFocus,
|
|
@@ -3508,34 +4621,50 @@ export {
|
|
|
3508
4621
|
columnValueOptions,
|
|
3509
4622
|
compareValues,
|
|
3510
4623
|
computeAggregates,
|
|
4624
|
+
conditionRowsFrom,
|
|
4625
|
+
conditionsFromRows,
|
|
3511
4626
|
contextMenuEntries,
|
|
4627
|
+
copyContent,
|
|
4628
|
+
createColumnValueOptionsCache,
|
|
3512
4629
|
createCommandContext,
|
|
3513
4630
|
createGridController,
|
|
3514
4631
|
createGridViewResolver,
|
|
4632
|
+
csvWriter,
|
|
3515
4633
|
defaultContextMenu,
|
|
4634
|
+
defaultFilterOperators,
|
|
3516
4635
|
defaultMessages,
|
|
3517
4636
|
defaultToolbar,
|
|
3518
4637
|
defineColumnsFromRows,
|
|
3519
4638
|
describeColumnFilter,
|
|
3520
4639
|
diffSelection,
|
|
3521
4640
|
draftToFilter,
|
|
4641
|
+
excelFilter,
|
|
3522
4642
|
expandAllGroups,
|
|
4643
|
+
exportFile,
|
|
4644
|
+
exportPrintDocument,
|
|
4645
|
+
filterChecklist,
|
|
3523
4646
|
filterEditorReducer,
|
|
4647
|
+
filterOperandArity,
|
|
4648
|
+
filterOperatorFamily,
|
|
3524
4649
|
filterQueryFor,
|
|
3525
4650
|
filterRows,
|
|
3526
4651
|
fitColumnsToWidth,
|
|
3527
4652
|
fitToolbarItems,
|
|
3528
4653
|
formatAggregateValue,
|
|
3529
4654
|
formatCellValue,
|
|
4655
|
+
formatFilterOperand,
|
|
4656
|
+
formatGroupValue,
|
|
3530
4657
|
getColumnId,
|
|
3531
4658
|
groupRowId,
|
|
3532
4659
|
groupRows,
|
|
3533
4660
|
hasContextMenu,
|
|
3534
4661
|
initialFilterEditorDraft,
|
|
3535
4662
|
intentOf,
|
|
4663
|
+
isColumnFiltered,
|
|
3536
4664
|
isColumnVisible,
|
|
3537
4665
|
isPinnedToolbarItem,
|
|
3538
4666
|
isSameCell,
|
|
4667
|
+
jsonWriter,
|
|
3539
4668
|
matchesQuery,
|
|
3540
4669
|
moveColumnBefore,
|
|
3541
4670
|
moveGroupByBefore,
|
|
@@ -3545,9 +4674,11 @@ export {
|
|
|
3545
4674
|
nextMenuIndex,
|
|
3546
4675
|
paginateRows,
|
|
3547
4676
|
paginationWindow,
|
|
4677
|
+
parseFilterOperand,
|
|
3548
4678
|
resolveCell,
|
|
3549
4679
|
resolveColumnChooser,
|
|
3550
4680
|
resolveColumnConstraints,
|
|
4681
|
+
resolveColumnFilterMenu,
|
|
3551
4682
|
resolveColumnHeaderText,
|
|
3552
4683
|
resolveColumnLabel,
|
|
3553
4684
|
resolveColumnWidths,
|
|
@@ -3555,6 +4686,7 @@ export {
|
|
|
3555
4686
|
resolveCommands,
|
|
3556
4687
|
resolveDataColumns,
|
|
3557
4688
|
resolveDropBefore,
|
|
4689
|
+
resolveExportWriters,
|
|
3558
4690
|
resolveGridView,
|
|
3559
4691
|
resolveKeyboardDropTarget,
|
|
3560
4692
|
resolveMenu,
|
|
@@ -3580,6 +4712,10 @@ export {
|
|
|
3580
4712
|
sortPriorityFor,
|
|
3581
4713
|
sortRows,
|
|
3582
4714
|
splitToolbarItems,
|
|
4715
|
+
toCsv,
|
|
4716
|
+
toHtmlTable,
|
|
4717
|
+
toJson,
|
|
4718
|
+
toTsv,
|
|
3583
4719
|
toggleCellSelection,
|
|
3584
4720
|
toggleColumnSort,
|
|
3585
4721
|
toggleGroupExpansion,
|