@x12i/data 1.0.0 → 1.1.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 +524 -27
- package/dist/chunk-ASP4ABDG.js +1987 -0
- package/dist/chunk-ASP4ABDG.js.map +1 -0
- package/dist/index.d.ts +375 -89
- package/dist/index.js +218 -54
- package/dist/index.js.map +1 -1
- package/dist/messages-BF20Mq97.d.ts +508 -0
- package/dist/react.d.ts +328 -0
- package/dist/react.js +3078 -0
- package/dist/react.js.map +1 -0
- package/dist/styles.css +1169 -0
- package/package.json +58 -5
|
@@ -0,0 +1,1987 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
function defineColumns(columns) {
|
|
3
|
+
return columns;
|
|
4
|
+
}
|
|
5
|
+
var SAVED_VIEW_VERSION = 1;
|
|
6
|
+
var FILTER_OPERATORS = [
|
|
7
|
+
"contains",
|
|
8
|
+
"not-contains",
|
|
9
|
+
"starts-with",
|
|
10
|
+
"equals",
|
|
11
|
+
"not-equals",
|
|
12
|
+
"gt",
|
|
13
|
+
"gte",
|
|
14
|
+
"lt",
|
|
15
|
+
"lte",
|
|
16
|
+
"between",
|
|
17
|
+
"in",
|
|
18
|
+
"before",
|
|
19
|
+
"after",
|
|
20
|
+
"is-empty",
|
|
21
|
+
"is-not-empty"
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
// src/columns.ts
|
|
25
|
+
var DEFAULT_COLUMN_WIDTH = 160;
|
|
26
|
+
var DEFAULT_COLUMN_MIN_WIDTH = 64;
|
|
27
|
+
var DEFAULT_COLUMN_MAX_WIDTH = 1200;
|
|
28
|
+
function getColumnValue(column, row) {
|
|
29
|
+
if (column.accessor !== void 0) return column.accessor(row);
|
|
30
|
+
if (column.field !== void 0) return row[column.field];
|
|
31
|
+
return void 0;
|
|
32
|
+
}
|
|
33
|
+
function columnFieldName(column) {
|
|
34
|
+
return column.field ?? column.id;
|
|
35
|
+
}
|
|
36
|
+
var NUMERIC = /* @__PURE__ */ new Set(["number", "currency", "percent"]);
|
|
37
|
+
function isNumericCellType(cellType) {
|
|
38
|
+
return cellType !== void 0 && NUMERIC.has(cellType);
|
|
39
|
+
}
|
|
40
|
+
function resolveColumnAlign(column) {
|
|
41
|
+
if (column.align !== void 0) return column.align;
|
|
42
|
+
if (isNumericCellType(column.cellType)) return "end";
|
|
43
|
+
if (column.cellType === "boolean") return "center";
|
|
44
|
+
return "start";
|
|
45
|
+
}
|
|
46
|
+
function resolveFilterType(column) {
|
|
47
|
+
if (column.filterType !== void 0) return column.filterType;
|
|
48
|
+
switch (column.cellType) {
|
|
49
|
+
case "number":
|
|
50
|
+
case "currency":
|
|
51
|
+
case "percent":
|
|
52
|
+
case "progress":
|
|
53
|
+
return "number";
|
|
54
|
+
case "date":
|
|
55
|
+
case "datetime":
|
|
56
|
+
return "date";
|
|
57
|
+
case "boolean":
|
|
58
|
+
return "boolean";
|
|
59
|
+
case "status":
|
|
60
|
+
return "enum";
|
|
61
|
+
case "tags":
|
|
62
|
+
return column.options !== void 0 ? "enum" : "text";
|
|
63
|
+
default:
|
|
64
|
+
return column.options !== void 0 ? "enum" : "text";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function resolveEditor(column) {
|
|
68
|
+
if (column.editor !== void 0) return column.editor;
|
|
69
|
+
switch (column.cellType) {
|
|
70
|
+
case "number":
|
|
71
|
+
case "currency":
|
|
72
|
+
case "percent":
|
|
73
|
+
case "progress":
|
|
74
|
+
return "number";
|
|
75
|
+
case "date":
|
|
76
|
+
case "datetime":
|
|
77
|
+
return "date";
|
|
78
|
+
case "boolean":
|
|
79
|
+
return "boolean";
|
|
80
|
+
case "status":
|
|
81
|
+
return "select";
|
|
82
|
+
default:
|
|
83
|
+
return column.options !== void 0 ? "select" : "text";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
var INERT = /* @__PURE__ */ new Set(["actions", "custom"]);
|
|
87
|
+
function isSortable(column) {
|
|
88
|
+
if (column.sortable !== void 0) return column.sortable;
|
|
89
|
+
return column.cellType === void 0 || !INERT.has(column.cellType);
|
|
90
|
+
}
|
|
91
|
+
function isFilterable(column) {
|
|
92
|
+
if (column.filterable !== void 0) return column.filterable;
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
function isResizable(column) {
|
|
96
|
+
return column.resizable ?? true;
|
|
97
|
+
}
|
|
98
|
+
function isEditable(column, row) {
|
|
99
|
+
if (column.editable !== true) return false;
|
|
100
|
+
return column.canEdit === void 0 || column.canEdit(row);
|
|
101
|
+
}
|
|
102
|
+
function clampColumnWidth(column, width) {
|
|
103
|
+
const min = column.minWidth ?? DEFAULT_COLUMN_MIN_WIDTH;
|
|
104
|
+
const max = Math.max(min, column.maxWidth ?? DEFAULT_COLUMN_MAX_WIDTH);
|
|
105
|
+
return Math.min(max, Math.max(min, Math.round(width)));
|
|
106
|
+
}
|
|
107
|
+
function initialColumnWidth(column) {
|
|
108
|
+
return clampColumnWidth(column, column.width ?? DEFAULT_COLUMN_WIDTH);
|
|
109
|
+
}
|
|
110
|
+
function indexColumns(columns) {
|
|
111
|
+
const map = /* @__PURE__ */ new Map();
|
|
112
|
+
for (const column of columns) if (!map.has(column.id)) map.set(column.id, column);
|
|
113
|
+
return map;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/values.ts
|
|
117
|
+
var DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
118
|
+
function isDateOnlyString(value) {
|
|
119
|
+
return typeof value === "string" && DATE_ONLY.test(value);
|
|
120
|
+
}
|
|
121
|
+
function isEmptyValue(value) {
|
|
122
|
+
return value === null || value === void 0 || value === "" || typeof value === "number" && Number.isNaN(value) || Array.isArray(value) && value.length === 0;
|
|
123
|
+
}
|
|
124
|
+
function toNumber(value) {
|
|
125
|
+
if (typeof value === "number") return value;
|
|
126
|
+
if (typeof value === "bigint") return Number(value);
|
|
127
|
+
if (typeof value === "string") {
|
|
128
|
+
const text = value.trim();
|
|
129
|
+
return text === "" ? Number.NaN : Number(text);
|
|
130
|
+
}
|
|
131
|
+
return Number.NaN;
|
|
132
|
+
}
|
|
133
|
+
function toTimestamp(value) {
|
|
134
|
+
if (value instanceof Date) return value.getTime();
|
|
135
|
+
if (typeof value === "number") return value;
|
|
136
|
+
if (typeof value === "string") return value === "" ? Number.NaN : Date.parse(value);
|
|
137
|
+
return Number.NaN;
|
|
138
|
+
}
|
|
139
|
+
function toDate(value) {
|
|
140
|
+
const time = toTimestamp(value);
|
|
141
|
+
return Number.isNaN(time) ? null : new Date(time);
|
|
142
|
+
}
|
|
143
|
+
function toText(value) {
|
|
144
|
+
if (value === null || value === void 0) return "";
|
|
145
|
+
if (typeof value === "string") return value;
|
|
146
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
147
|
+
return String(value);
|
|
148
|
+
}
|
|
149
|
+
if (value instanceof Date) return Number.isNaN(value.getTime()) ? "" : value.toISOString();
|
|
150
|
+
if (Array.isArray(value)) return value.map(toText).join(", ");
|
|
151
|
+
if (typeof value === "object") {
|
|
152
|
+
try {
|
|
153
|
+
return JSON.stringify(value);
|
|
154
|
+
} catch {
|
|
155
|
+
return "";
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return String(value);
|
|
159
|
+
}
|
|
160
|
+
function normalizeText(value, locale) {
|
|
161
|
+
return value.normalize("NFD").replace(/\p{M}+/gu, "").toLocaleLowerCase(locale);
|
|
162
|
+
}
|
|
163
|
+
function toBoolean(value) {
|
|
164
|
+
if (typeof value === "boolean") return value;
|
|
165
|
+
if (value === "true") return true;
|
|
166
|
+
if (value === "false") return false;
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// src/formatters.ts
|
|
171
|
+
var defaultFormatLabels = { yes: "Yes", no: "No" };
|
|
172
|
+
var cache = /* @__PURE__ */ new Map();
|
|
173
|
+
function numberFormat(locale, options) {
|
|
174
|
+
const key = `n|${locale ?? ""}|${JSON.stringify(options)}`;
|
|
175
|
+
let format = cache.get(key);
|
|
176
|
+
if (format === void 0) {
|
|
177
|
+
try {
|
|
178
|
+
format = new Intl.NumberFormat(locale, options);
|
|
179
|
+
} catch {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
cache.set(key, format);
|
|
183
|
+
}
|
|
184
|
+
return format;
|
|
185
|
+
}
|
|
186
|
+
function dateFormat(locale, options) {
|
|
187
|
+
const key = `d|${locale ?? ""}|${JSON.stringify(options)}`;
|
|
188
|
+
let format = cache.get(key);
|
|
189
|
+
if (format === void 0) {
|
|
190
|
+
try {
|
|
191
|
+
format = new Intl.DateTimeFormat(locale, options);
|
|
192
|
+
} catch {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
cache.set(key, format);
|
|
196
|
+
}
|
|
197
|
+
return format;
|
|
198
|
+
}
|
|
199
|
+
function resolveNumberFormatOptions(column) {
|
|
200
|
+
const format = column.format;
|
|
201
|
+
const type = column.cellType;
|
|
202
|
+
if (type === "currency") {
|
|
203
|
+
const code = column.currency ?? (format !== void 0 && /^[A-Za-z]{3}$/.test(format) ? format.toUpperCase() : "USD");
|
|
204
|
+
return { style: "currency", currency: code, ...column.numberFormat };
|
|
205
|
+
}
|
|
206
|
+
if (type === "percent" || type === "progress") {
|
|
207
|
+
return { style: "percent", minimumFractionDigits: 0, maximumFractionDigits: 1, ...column.numberFormat };
|
|
208
|
+
}
|
|
209
|
+
const base = {};
|
|
210
|
+
if (format === "integer") base.maximumFractionDigits = 0;
|
|
211
|
+
else if (format === "compact") base.notation = "compact";
|
|
212
|
+
else if (format?.startsWith("decimal:")) {
|
|
213
|
+
const digits = Number(format.slice("decimal:".length));
|
|
214
|
+
if (Number.isInteger(digits) && digits >= 0 && digits <= 20) {
|
|
215
|
+
base.minimumFractionDigits = digits;
|
|
216
|
+
base.maximumFractionDigits = digits;
|
|
217
|
+
}
|
|
218
|
+
} else if (format?.startsWith("unit:")) {
|
|
219
|
+
base.style = "unit";
|
|
220
|
+
base.unit = format.slice("unit:".length);
|
|
221
|
+
}
|
|
222
|
+
return { ...base, ...column.numberFormat };
|
|
223
|
+
}
|
|
224
|
+
var DATE_STYLES = /* @__PURE__ */ new Set(["short", "medium", "long", "full"]);
|
|
225
|
+
function resolveDateFormatOptions(column) {
|
|
226
|
+
if (column.dateFormat !== void 0) return column.dateFormat;
|
|
227
|
+
const style = DATE_STYLES.has(column.format ?? "") ? column.format : "medium";
|
|
228
|
+
if (column.cellType === "datetime") {
|
|
229
|
+
return { dateStyle: style, timeStyle: style === "short" || style === "medium" ? "short" : "medium" };
|
|
230
|
+
}
|
|
231
|
+
return { dateStyle: style };
|
|
232
|
+
}
|
|
233
|
+
function formatDate(column, value, ctx, options) {
|
|
234
|
+
const date = toDate(value);
|
|
235
|
+
if (date === null) return toText(value);
|
|
236
|
+
const dateOnly = isDateOnlyString(value);
|
|
237
|
+
if (column.format === "iso") {
|
|
238
|
+
return column.cellType === "datetime" && !dateOnly ? date.toISOString() : date.toISOString().slice(0, 10);
|
|
239
|
+
}
|
|
240
|
+
const effective = { ...options };
|
|
241
|
+
if (dateOnly) effective.timeZone = "UTC";
|
|
242
|
+
else if (ctx.timeZone !== void 0) effective.timeZone = ctx.timeZone;
|
|
243
|
+
const format = dateFormat(ctx.locale, effective);
|
|
244
|
+
return format === null ? date.toISOString() : format.format(date);
|
|
245
|
+
}
|
|
246
|
+
function optionLabel(column, value) {
|
|
247
|
+
const text = toText(value);
|
|
248
|
+
return column.options?.find((option) => option.value === text)?.label ?? text;
|
|
249
|
+
}
|
|
250
|
+
function createCellFormatter(column, ctx = {}) {
|
|
251
|
+
if (column.formatter !== void 0) {
|
|
252
|
+
const custom = column.formatter;
|
|
253
|
+
return (value, row) => custom(value, row);
|
|
254
|
+
}
|
|
255
|
+
const labels = ctx.labels ?? defaultFormatLabels;
|
|
256
|
+
const type = column.cellType;
|
|
257
|
+
const numeric = numberFormat(ctx.locale, resolveNumberFormatOptions({ ...column, cellType: type ?? "number" }));
|
|
258
|
+
const dateOptions = resolveDateFormatOptions({ ...column, cellType: type ?? "date" });
|
|
259
|
+
const percentWhole = column.format === "whole";
|
|
260
|
+
const progressMax = column.progressMax ?? 100;
|
|
261
|
+
const formatNumber2 = (value, scale = 1) => {
|
|
262
|
+
const number = toNumber(value);
|
|
263
|
+
if (Number.isNaN(number)) return toText(value);
|
|
264
|
+
return numeric === null ? String(number) : numeric.format(number / scale);
|
|
265
|
+
};
|
|
266
|
+
return (value) => {
|
|
267
|
+
if (isEmptyValue(value)) return "";
|
|
268
|
+
switch (type) {
|
|
269
|
+
case "number":
|
|
270
|
+
case "currency":
|
|
271
|
+
return formatNumber2(value);
|
|
272
|
+
case "percent":
|
|
273
|
+
return formatNumber2(value, percentWhole ? 100 : 1);
|
|
274
|
+
case "progress":
|
|
275
|
+
return formatNumber2(value, progressMax);
|
|
276
|
+
case "date":
|
|
277
|
+
case "datetime":
|
|
278
|
+
return formatDate(column, value, ctx, dateOptions);
|
|
279
|
+
case "boolean": {
|
|
280
|
+
const flag = toBoolean(value);
|
|
281
|
+
return flag === null ? toText(value) : flag ? labels.yes : labels.no;
|
|
282
|
+
}
|
|
283
|
+
case "status":
|
|
284
|
+
return optionLabel(column, value);
|
|
285
|
+
case "tags":
|
|
286
|
+
return Array.isArray(value) ? value.map((item) => optionLabel(column, item)).join(", ") : toText(value);
|
|
287
|
+
case "sparkline":
|
|
288
|
+
return Array.isArray(value) ? value.map(toText).join(" ") : toText(value);
|
|
289
|
+
case "actions":
|
|
290
|
+
return "";
|
|
291
|
+
case void 0: {
|
|
292
|
+
if (typeof value === "number") return formatNumber2(value);
|
|
293
|
+
if (value instanceof Date) return formatDate(column, value, ctx, resolveDateFormatOptions({ ...column, cellType: "date" }));
|
|
294
|
+
if (typeof value === "boolean") return value ? labels.yes : labels.no;
|
|
295
|
+
if (column.options !== void 0) return optionLabel(column, value);
|
|
296
|
+
return toText(value);
|
|
297
|
+
}
|
|
298
|
+
default:
|
|
299
|
+
return toText(value);
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function formatCellValue(column, value, row, ctx = {}) {
|
|
304
|
+
return createCellFormatter(column, ctx)(value, row);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// src/sort.ts
|
|
308
|
+
function createCollator(options = {}) {
|
|
309
|
+
return new Intl.Collator(options.locale, {
|
|
310
|
+
numeric: options.numeric ?? true,
|
|
311
|
+
sensitivity: options.caseInsensitive === false ? "variant" : "base"
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
function toSortKey(value) {
|
|
315
|
+
if (value === null || value === void 0) return null;
|
|
316
|
+
if (typeof value === "number") return Number.isNaN(value) ? null : value;
|
|
317
|
+
if (typeof value === "bigint") return Number(value);
|
|
318
|
+
if (typeof value === "boolean") return value ? 1 : 0;
|
|
319
|
+
if (value instanceof Date) {
|
|
320
|
+
const time = value.getTime();
|
|
321
|
+
return Number.isNaN(time) ? null : time;
|
|
322
|
+
}
|
|
323
|
+
if (typeof value === "string") return value === "" ? null : value;
|
|
324
|
+
if (Array.isArray(value)) return value.length === 0 ? null : toText(value);
|
|
325
|
+
return toText(value);
|
|
326
|
+
}
|
|
327
|
+
function columnSortKey(column, value) {
|
|
328
|
+
switch (column.cellType) {
|
|
329
|
+
case "number":
|
|
330
|
+
case "currency":
|
|
331
|
+
case "percent":
|
|
332
|
+
case "progress": {
|
|
333
|
+
const number = toNumber(value);
|
|
334
|
+
return Number.isNaN(number) ? null : number;
|
|
335
|
+
}
|
|
336
|
+
case "date":
|
|
337
|
+
case "datetime": {
|
|
338
|
+
const time = toTimestamp(value);
|
|
339
|
+
return Number.isNaN(time) ? null : time;
|
|
340
|
+
}
|
|
341
|
+
default:
|
|
342
|
+
return toSortKey(value);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
function compareKeys(a, b, collator) {
|
|
346
|
+
if (typeof a === "number") {
|
|
347
|
+
if (typeof b === "number") return a < b ? -1 : a > b ? 1 : 0;
|
|
348
|
+
return -1;
|
|
349
|
+
}
|
|
350
|
+
if (typeof b === "number") return 1;
|
|
351
|
+
return collator.compare(a, b);
|
|
352
|
+
}
|
|
353
|
+
function compareValues(a, b, collator = createCollator()) {
|
|
354
|
+
const keyA = toSortKey(a);
|
|
355
|
+
const keyB = toSortKey(b);
|
|
356
|
+
if (keyA === null) return keyB === null ? 0 : 1;
|
|
357
|
+
if (keyB === null) return -1;
|
|
358
|
+
return compareKeys(keyA, keyB, collator);
|
|
359
|
+
}
|
|
360
|
+
function sortIndexes(rows, indexes, sort, columns, options = {}) {
|
|
361
|
+
const byId = indexColumns(columns);
|
|
362
|
+
const specs = [];
|
|
363
|
+
for (const item of sort) {
|
|
364
|
+
const column = byId.get(item.field);
|
|
365
|
+
if (column !== void 0) specs.push({ column, sign: item.direction === "desc" ? -1 : 1 });
|
|
366
|
+
}
|
|
367
|
+
const count = indexes.length;
|
|
368
|
+
if (specs.length === 0 || count < 2) return indexes.slice();
|
|
369
|
+
const collator = createCollator(options);
|
|
370
|
+
const keys = specs.map(({ column }) => {
|
|
371
|
+
const out = new Array(count);
|
|
372
|
+
const custom = column.comparator !== void 0;
|
|
373
|
+
for (let position = 0; position < count; position += 1) {
|
|
374
|
+
const row = rows[indexes[position]];
|
|
375
|
+
const value = getColumnValue(column, row);
|
|
376
|
+
out[position] = custom ? value : columnSortKey(column, value);
|
|
377
|
+
}
|
|
378
|
+
return out;
|
|
379
|
+
});
|
|
380
|
+
const order = new Array(count);
|
|
381
|
+
for (let position = 0; position < count; position += 1) order[position] = position;
|
|
382
|
+
order.sort((a, b) => {
|
|
383
|
+
for (let s = 0; s < specs.length; s += 1) {
|
|
384
|
+
const spec = specs[s];
|
|
385
|
+
const column = spec.column;
|
|
386
|
+
const list = keys[s];
|
|
387
|
+
const keyA = list[a];
|
|
388
|
+
const keyB = list[b];
|
|
389
|
+
let result;
|
|
390
|
+
if (column.comparator !== void 0) {
|
|
391
|
+
result = column.comparator(
|
|
392
|
+
keyA,
|
|
393
|
+
keyB,
|
|
394
|
+
rows[indexes[a]],
|
|
395
|
+
rows[indexes[b]]
|
|
396
|
+
) * spec.sign;
|
|
397
|
+
} else if (keyA === null) {
|
|
398
|
+
result = keyB === null ? 0 : 1;
|
|
399
|
+
} else if (keyB === null) {
|
|
400
|
+
result = -1;
|
|
401
|
+
} else {
|
|
402
|
+
result = compareKeys(keyA, keyB, collator) * spec.sign;
|
|
403
|
+
}
|
|
404
|
+
if (result !== 0) return result;
|
|
405
|
+
}
|
|
406
|
+
return a - b;
|
|
407
|
+
});
|
|
408
|
+
return order.map((position) => indexes[position]);
|
|
409
|
+
}
|
|
410
|
+
function sortRows(rows, sort, columns, options = {}) {
|
|
411
|
+
const all = new Array(rows.length);
|
|
412
|
+
for (let i = 0; i < rows.length; i += 1) all[i] = i;
|
|
413
|
+
return sortIndexes(rows, all, sort, columns, options).map((index) => rows[index]);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// src/filter.ts
|
|
417
|
+
var OPERATORS_BY_FILTER_TYPE = {
|
|
418
|
+
text: ["contains", "not-contains", "starts-with", "equals", "not-equals", "is-empty", "is-not-empty"],
|
|
419
|
+
number: ["equals", "not-equals", "gt", "gte", "lt", "lte", "between", "is-empty", "is-not-empty"],
|
|
420
|
+
date: ["equals", "before", "after", "between", "is-empty", "is-not-empty"],
|
|
421
|
+
enum: ["in", "is-empty", "is-not-empty"],
|
|
422
|
+
boolean: ["equals", "is-empty", "is-not-empty"]
|
|
423
|
+
};
|
|
424
|
+
function operatorsFor(type) {
|
|
425
|
+
return OPERATORS_BY_FILTER_TYPE[type];
|
|
426
|
+
}
|
|
427
|
+
function isValuelessOperator(operator) {
|
|
428
|
+
return operator === "is-empty" || operator === "is-not-empty";
|
|
429
|
+
}
|
|
430
|
+
function hasText(value) {
|
|
431
|
+
return !isEmptyValue(value) && toText(value).trim() !== "";
|
|
432
|
+
}
|
|
433
|
+
function isFilterEffective(item) {
|
|
434
|
+
if (isValuelessOperator(item.operator)) return true;
|
|
435
|
+
if (item.operator === "between") {
|
|
436
|
+
const range = Array.isArray(item.value) ? item.value : [];
|
|
437
|
+
return hasText(range[0]) || hasText(range[1]);
|
|
438
|
+
}
|
|
439
|
+
if (item.operator === "in") return Array.isArray(item.value) && item.value.length > 0;
|
|
440
|
+
return hasText(item.value);
|
|
441
|
+
}
|
|
442
|
+
function pad(value) {
|
|
443
|
+
return String(value).padStart(2, "0");
|
|
444
|
+
}
|
|
445
|
+
function toDayString(value) {
|
|
446
|
+
if (isDateOnlyString(value)) return value;
|
|
447
|
+
if (value instanceof Date && !Number.isNaN(value.getTime())) {
|
|
448
|
+
return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`;
|
|
449
|
+
}
|
|
450
|
+
if (typeof value === "string" && value.length >= 10 && isDateOnlyString(value.slice(0, 10))) {
|
|
451
|
+
return value.slice(0, 10);
|
|
452
|
+
}
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
function offsetMs(timestamp, timeZone) {
|
|
456
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
457
|
+
timeZone,
|
|
458
|
+
hourCycle: "h23",
|
|
459
|
+
year: "numeric",
|
|
460
|
+
month: "numeric",
|
|
461
|
+
day: "numeric",
|
|
462
|
+
hour: "numeric",
|
|
463
|
+
minute: "numeric",
|
|
464
|
+
second: "numeric"
|
|
465
|
+
}).formatToParts(timestamp);
|
|
466
|
+
const get = (type) => Number(parts.find((part) => part.type === type)?.value);
|
|
467
|
+
const asUtc = Date.UTC(get("year"), get("month") - 1, get("day"), get("hour"), get("minute"), get("second"));
|
|
468
|
+
return asUtc - Math.floor(timestamp / 1e3) * 1e3;
|
|
469
|
+
}
|
|
470
|
+
function dayStart(day, timeZone) {
|
|
471
|
+
const [year, month, date] = day.split("-").map(Number);
|
|
472
|
+
if (timeZone === void 0) return new Date(year, month - 1, date).getTime();
|
|
473
|
+
const guess = Date.UTC(year, month - 1, date);
|
|
474
|
+
if (timeZone === "UTC") return guess;
|
|
475
|
+
try {
|
|
476
|
+
const first = offsetMs(guess, timeZone);
|
|
477
|
+
let result = guess - first;
|
|
478
|
+
const second = offsetMs(result, timeZone);
|
|
479
|
+
if (second !== first) result = guess - second;
|
|
480
|
+
return result;
|
|
481
|
+
} catch {
|
|
482
|
+
return guess;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
function nextDay(day) {
|
|
486
|
+
const [year, month, date] = day.split("-").map(Number);
|
|
487
|
+
const next = new Date(Date.UTC(year, month - 1, date + 1));
|
|
488
|
+
return `${next.getUTCFullYear()}-${pad(next.getUTCMonth() + 1)}-${pad(next.getUTCDate())}`;
|
|
489
|
+
}
|
|
490
|
+
var ALWAYS = () => true;
|
|
491
|
+
function textPredicate(item, ctx) {
|
|
492
|
+
const { operator } = item;
|
|
493
|
+
const norm = (value) => normalizeText(toText(value), ctx.locale);
|
|
494
|
+
if (operator === "in") {
|
|
495
|
+
const wanted = new Set((Array.isArray(item.value) ? item.value : []).map((entry) => norm(entry)));
|
|
496
|
+
return (value) => Array.isArray(value) ? value.some((entry) => wanted.has(norm(entry))) : wanted.has(norm(value));
|
|
497
|
+
}
|
|
498
|
+
if (operator === "between" || operator === "gt" || operator === "gte" || operator === "lt" || operator === "lte") {
|
|
499
|
+
const collator = createCollator({ ...ctx.locale === void 0 ? {} : { locale: ctx.locale } });
|
|
500
|
+
const [low, high] = operator === "between" && Array.isArray(item.value) ? item.value : [item.value, item.value];
|
|
501
|
+
return (value) => {
|
|
502
|
+
if (isEmptyValue(value)) return false;
|
|
503
|
+
const text = toText(value);
|
|
504
|
+
switch (operator) {
|
|
505
|
+
case "gt":
|
|
506
|
+
return collator.compare(text, toText(item.value)) > 0;
|
|
507
|
+
case "gte":
|
|
508
|
+
return collator.compare(text, toText(item.value)) >= 0;
|
|
509
|
+
case "lt":
|
|
510
|
+
return collator.compare(text, toText(item.value)) < 0;
|
|
511
|
+
case "lte":
|
|
512
|
+
return collator.compare(text, toText(item.value)) <= 0;
|
|
513
|
+
default:
|
|
514
|
+
return (!hasText(low) || collator.compare(text, toText(low)) >= 0) && (!hasText(high) || collator.compare(text, toText(high)) <= 0);
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
const needle = norm(item.value);
|
|
519
|
+
switch (operator) {
|
|
520
|
+
case "contains":
|
|
521
|
+
return (value) => norm(value).includes(needle);
|
|
522
|
+
case "not-contains":
|
|
523
|
+
return (value) => !norm(value).includes(needle);
|
|
524
|
+
case "starts-with":
|
|
525
|
+
return (value) => norm(value).startsWith(needle);
|
|
526
|
+
case "equals":
|
|
527
|
+
return (value) => norm(value) === needle;
|
|
528
|
+
case "not-equals":
|
|
529
|
+
return (value) => norm(value) !== needle;
|
|
530
|
+
// Date operators on a text column compare as text; they are meaningless but harmless.
|
|
531
|
+
case "before":
|
|
532
|
+
case "after":
|
|
533
|
+
return ALWAYS;
|
|
534
|
+
default:
|
|
535
|
+
return ALWAYS;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
function numberPredicate(item) {
|
|
539
|
+
const { operator } = item;
|
|
540
|
+
if (operator === "in") {
|
|
541
|
+
const wanted = new Set((Array.isArray(item.value) ? item.value : []).map(toNumber));
|
|
542
|
+
return (value) => wanted.has(toNumber(value));
|
|
543
|
+
}
|
|
544
|
+
if (operator === "between") {
|
|
545
|
+
const range = Array.isArray(item.value) ? item.value : [];
|
|
546
|
+
const low = hasText(range[0]) ? toNumber(range[0]) : Number.NEGATIVE_INFINITY;
|
|
547
|
+
const high = hasText(range[1]) ? toNumber(range[1]) : Number.POSITIVE_INFINITY;
|
|
548
|
+
return (value) => {
|
|
549
|
+
const number = toNumber(value);
|
|
550
|
+
return !Number.isNaN(number) && number >= low && number <= high;
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
const target = toNumber(item.value);
|
|
554
|
+
if (Number.isNaN(target)) return ALWAYS;
|
|
555
|
+
switch (operator) {
|
|
556
|
+
case "equals":
|
|
557
|
+
return (value) => toNumber(value) === target;
|
|
558
|
+
case "not-equals":
|
|
559
|
+
return (value) => toNumber(value) !== target;
|
|
560
|
+
case "gt":
|
|
561
|
+
case "after":
|
|
562
|
+
return (value) => toNumber(value) > target;
|
|
563
|
+
case "gte":
|
|
564
|
+
return (value) => toNumber(value) >= target;
|
|
565
|
+
case "lt":
|
|
566
|
+
case "before":
|
|
567
|
+
return (value) => toNumber(value) < target;
|
|
568
|
+
case "lte":
|
|
569
|
+
return (value) => toNumber(value) <= target;
|
|
570
|
+
default:
|
|
571
|
+
return ALWAYS;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
function datePredicate(item, ctx) {
|
|
575
|
+
const { operator } = item;
|
|
576
|
+
const range = operator === "between" && Array.isArray(item.value) ? item.value : [item.value, item.value];
|
|
577
|
+
const first = toDayString(operator === "between" ? range[0] : item.value);
|
|
578
|
+
const second = toDayString(operator === "between" ? range[1] : item.value);
|
|
579
|
+
const startMs = (day) => dayStart(day, ctx.timeZone);
|
|
580
|
+
const test = (inRange) => (value) => {
|
|
581
|
+
if (isEmptyValue(value)) return false;
|
|
582
|
+
if (isDateOnlyString(value)) return inRange(value, Number.NaN);
|
|
583
|
+
const time = toTimestamp(value);
|
|
584
|
+
return !Number.isNaN(time) && inRange(null, time);
|
|
585
|
+
};
|
|
586
|
+
switch (operator) {
|
|
587
|
+
case "equals":
|
|
588
|
+
case "not-equals": {
|
|
589
|
+
if (first === null) return ALWAYS;
|
|
590
|
+
const equals = test(
|
|
591
|
+
(day, time) => day !== null ? day === first : time >= startMs(first) && time < startMs(nextDay(first))
|
|
592
|
+
);
|
|
593
|
+
return operator === "equals" ? equals : (value) => !equals(value);
|
|
594
|
+
}
|
|
595
|
+
case "before":
|
|
596
|
+
case "lt": {
|
|
597
|
+
if (first === null) return ALWAYS;
|
|
598
|
+
return test((day, time) => day !== null ? day < first : time < startMs(first));
|
|
599
|
+
}
|
|
600
|
+
case "lte": {
|
|
601
|
+
if (first === null) return ALWAYS;
|
|
602
|
+
return test((day, time) => day !== null ? day <= first : time < startMs(nextDay(first)));
|
|
603
|
+
}
|
|
604
|
+
case "after":
|
|
605
|
+
case "gt": {
|
|
606
|
+
if (first === null) return ALWAYS;
|
|
607
|
+
return test((day, time) => day !== null ? day > first : time >= startMs(nextDay(first)));
|
|
608
|
+
}
|
|
609
|
+
case "gte": {
|
|
610
|
+
if (first === null) return ALWAYS;
|
|
611
|
+
return test((day, time) => day !== null ? day >= first : time >= startMs(first));
|
|
612
|
+
}
|
|
613
|
+
case "between": {
|
|
614
|
+
if (first === null && second === null) return ALWAYS;
|
|
615
|
+
return test((day, time) => {
|
|
616
|
+
if (day !== null) return (first === null || day >= first) && (second === null || day <= second);
|
|
617
|
+
return (first === null || time >= startMs(first)) && (second === null || time < startMs(nextDay(second)));
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
default:
|
|
621
|
+
return ALWAYS;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
function enumPredicate(item) {
|
|
625
|
+
const { operator } = item;
|
|
626
|
+
const values = Array.isArray(item.value) ? item.value : [item.value];
|
|
627
|
+
const wanted = new Set(values.filter((entry) => entry !== void 0 && entry !== null).map(String));
|
|
628
|
+
const hit = (value) => Array.isArray(value) ? value.some((entry) => wanted.has(String(entry))) : wanted.has(String(value));
|
|
629
|
+
switch (operator) {
|
|
630
|
+
case "in":
|
|
631
|
+
case "equals":
|
|
632
|
+
return hit;
|
|
633
|
+
case "not-equals":
|
|
634
|
+
return (value) => !hit(value);
|
|
635
|
+
default:
|
|
636
|
+
return ALWAYS;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
function booleanPredicate(item) {
|
|
640
|
+
const target = toBoolean(item.value);
|
|
641
|
+
if (target === null) return ALWAYS;
|
|
642
|
+
switch (item.operator) {
|
|
643
|
+
case "equals":
|
|
644
|
+
return (value) => toBoolean(value) === target;
|
|
645
|
+
case "not-equals":
|
|
646
|
+
return (value) => toBoolean(value) !== target;
|
|
647
|
+
default:
|
|
648
|
+
return ALWAYS;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
function createValuePredicate(item, column, ctx = {}) {
|
|
652
|
+
if (item.operator === "is-empty") return isEmptyValue;
|
|
653
|
+
if (item.operator === "is-not-empty") return (value) => !isEmptyValue(value);
|
|
654
|
+
if (!isFilterEffective(item)) return ALWAYS;
|
|
655
|
+
let predicate;
|
|
656
|
+
switch (resolveFilterType(column)) {
|
|
657
|
+
case "number":
|
|
658
|
+
predicate = numberPredicate(item);
|
|
659
|
+
break;
|
|
660
|
+
case "date":
|
|
661
|
+
predicate = datePredicate(item, ctx);
|
|
662
|
+
break;
|
|
663
|
+
case "enum":
|
|
664
|
+
predicate = enumPredicate(item);
|
|
665
|
+
break;
|
|
666
|
+
case "boolean":
|
|
667
|
+
predicate = booleanPredicate(item);
|
|
668
|
+
break;
|
|
669
|
+
default:
|
|
670
|
+
predicate = textPredicate(item, ctx);
|
|
671
|
+
}
|
|
672
|
+
if (predicate === ALWAYS) return ALWAYS;
|
|
673
|
+
const keepsEmpty = item.operator === "not-equals" || item.operator === "not-contains";
|
|
674
|
+
return (value) => isEmptyValue(value) ? keepsEmpty : predicate(value);
|
|
675
|
+
}
|
|
676
|
+
function matchesFilter(value, item, column, ctx = {}) {
|
|
677
|
+
return createValuePredicate(item, column, ctx)(value);
|
|
678
|
+
}
|
|
679
|
+
function parseQuickFilter(text, locale) {
|
|
680
|
+
return normalizeText(text, locale).split(/\s+/).filter((token) => token !== "");
|
|
681
|
+
}
|
|
682
|
+
var NOT_SEARCHABLE = /* @__PURE__ */ new Set(["actions", "sparkline", "custom"]);
|
|
683
|
+
function isSearchable(column) {
|
|
684
|
+
if (column.searchable !== void 0) return column.searchable;
|
|
685
|
+
return column.cellType === void 0 || !NOT_SEARCHABLE.has(column.cellType);
|
|
686
|
+
}
|
|
687
|
+
function createSearchIndex(rows, columns, ctx = {}) {
|
|
688
|
+
const searchable = columns.filter(isSearchable).map((column) => ({
|
|
689
|
+
column,
|
|
690
|
+
format: createCellFormatter(column, ctx)
|
|
691
|
+
}));
|
|
692
|
+
const out = new Array(rows.length);
|
|
693
|
+
for (let i = 0; i < rows.length; i += 1) {
|
|
694
|
+
const row = rows[i];
|
|
695
|
+
let text = "";
|
|
696
|
+
for (const { column, format } of searchable) {
|
|
697
|
+
const value = getColumnValue(column, row);
|
|
698
|
+
if (isEmptyValue(value)) continue;
|
|
699
|
+
text += `${format(value, row)}`;
|
|
700
|
+
}
|
|
701
|
+
out[i] = normalizeText(text, ctx.locale);
|
|
702
|
+
}
|
|
703
|
+
return out;
|
|
704
|
+
}
|
|
705
|
+
function filterIndexes(rows, filter, columns, options = {}) {
|
|
706
|
+
const byId = indexColumns(columns);
|
|
707
|
+
const ctx = {
|
|
708
|
+
...options.locale === void 0 ? {} : { locale: options.locale },
|
|
709
|
+
...options.timeZone === void 0 ? {} : { timeZone: options.timeZone }
|
|
710
|
+
};
|
|
711
|
+
const tests = [];
|
|
712
|
+
for (const item of filter) {
|
|
713
|
+
const column = byId.get(item.field);
|
|
714
|
+
if (column === void 0) continue;
|
|
715
|
+
const test = createValuePredicate(item, column, ctx);
|
|
716
|
+
if (test !== ALWAYS) tests.push({ column, test });
|
|
717
|
+
}
|
|
718
|
+
const tokens = options.quickFilter === void 0 ? [] : parseQuickFilter(options.quickFilter, options.locale);
|
|
719
|
+
let haystack = null;
|
|
720
|
+
if (tokens.length > 0) {
|
|
721
|
+
haystack = options.searchIndex ?? createSearchIndex(rows, options.searchColumns ?? columns, {
|
|
722
|
+
...ctx,
|
|
723
|
+
...options.labels === void 0 ? {} : { labels: options.labels }
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
const out = [];
|
|
727
|
+
const total = rows.length;
|
|
728
|
+
for (let i = 0; i < total; i += 1) {
|
|
729
|
+
const row = rows[i];
|
|
730
|
+
let pass = true;
|
|
731
|
+
for (let t = 0; t < tests.length; t += 1) {
|
|
732
|
+
const entry = tests[t];
|
|
733
|
+
if (!entry.test(getColumnValue(entry.column, row))) {
|
|
734
|
+
pass = false;
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
if (pass && haystack !== null) {
|
|
739
|
+
const text = haystack[i] ?? "";
|
|
740
|
+
for (const token of tokens) {
|
|
741
|
+
if (!text.includes(token)) {
|
|
742
|
+
pass = false;
|
|
743
|
+
break;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
if (pass) out.push(i);
|
|
748
|
+
}
|
|
749
|
+
return out;
|
|
750
|
+
}
|
|
751
|
+
function distinctValues(rows, column, limit = 500) {
|
|
752
|
+
const seen = /* @__PURE__ */ new Set();
|
|
753
|
+
for (const row of rows) {
|
|
754
|
+
const value = getColumnValue(column, row);
|
|
755
|
+
if (isEmptyValue(value)) continue;
|
|
756
|
+
if (Array.isArray(value)) for (const entry of value) seen.add(entry);
|
|
757
|
+
else seen.add(value);
|
|
758
|
+
if (seen.size >= limit) break;
|
|
759
|
+
}
|
|
760
|
+
return [...seen].sort((a, b) => compareValues(a, b));
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// src/aggregate.ts
|
|
764
|
+
function aggregateValues(values, name, options = {}) {
|
|
765
|
+
if (name === "count") {
|
|
766
|
+
let count = 0;
|
|
767
|
+
for (const value of values) if (!isEmptyValue(value)) count += 1;
|
|
768
|
+
return count;
|
|
769
|
+
}
|
|
770
|
+
const numeric = (value) => options.dates === true ? toTimestamp(value) : toNumber(value);
|
|
771
|
+
if (name === "sum" || name === "avg") {
|
|
772
|
+
let sum = 0;
|
|
773
|
+
let count = 0;
|
|
774
|
+
for (const value of values) {
|
|
775
|
+
const number = toNumber(value);
|
|
776
|
+
if (Number.isNaN(number)) continue;
|
|
777
|
+
sum += number;
|
|
778
|
+
count += 1;
|
|
779
|
+
}
|
|
780
|
+
if (name === "sum") return sum;
|
|
781
|
+
return count === 0 ? null : sum / count;
|
|
782
|
+
}
|
|
783
|
+
let best = null;
|
|
784
|
+
let bestKey = Number.NaN;
|
|
785
|
+
for (const value of values) {
|
|
786
|
+
const key = numeric(value);
|
|
787
|
+
if (Number.isNaN(key)) continue;
|
|
788
|
+
if (Number.isNaN(bestKey) || (name === "min" ? key < bestKey : key > bestKey)) {
|
|
789
|
+
best = value;
|
|
790
|
+
bestKey = key;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return best;
|
|
794
|
+
}
|
|
795
|
+
function computeAggregate(column, rows) {
|
|
796
|
+
const aggregate = column.aggregate;
|
|
797
|
+
if (aggregate === void 0) return void 0;
|
|
798
|
+
const values = rows.map((row) => getColumnValue(column, row));
|
|
799
|
+
if (typeof aggregate === "function") return aggregate(values, rows);
|
|
800
|
+
return aggregateValues(values, aggregate, { dates: column.cellType === "date" || column.cellType === "datetime" });
|
|
801
|
+
}
|
|
802
|
+
function computeAggregates(columns, rows) {
|
|
803
|
+
const out = {};
|
|
804
|
+
for (const column of columns) {
|
|
805
|
+
if (column.aggregate === void 0) continue;
|
|
806
|
+
out[column.id] = computeAggregate(column, rows);
|
|
807
|
+
}
|
|
808
|
+
return out;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// src/group.ts
|
|
812
|
+
function encode(value) {
|
|
813
|
+
return encodeURIComponent(value);
|
|
814
|
+
}
|
|
815
|
+
function buildGroupTree(rows, indexes, group, columns, options = {}) {
|
|
816
|
+
const byId = indexColumns(columns);
|
|
817
|
+
const levels = group.map((item) => byId.get(item.field)).filter((column) => column !== void 0);
|
|
818
|
+
if (levels.length === 0) return [];
|
|
819
|
+
const collator = createCollator(options);
|
|
820
|
+
const build = (subset, depth, parentId) => {
|
|
821
|
+
const column = levels[depth];
|
|
822
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
823
|
+
for (const index of subset) {
|
|
824
|
+
const value = getColumnValue(column, rows[index]);
|
|
825
|
+
const key = toText(value);
|
|
826
|
+
let bucket = buckets.get(key);
|
|
827
|
+
if (bucket === void 0) {
|
|
828
|
+
bucket = { value, indexes: [] };
|
|
829
|
+
buckets.set(key, bucket);
|
|
830
|
+
}
|
|
831
|
+
bucket.indexes.push(index);
|
|
832
|
+
}
|
|
833
|
+
let entries = [...buckets.entries()];
|
|
834
|
+
const order = typeof options.order === "function" ? options.order(column.id) : options.order ?? "asc";
|
|
835
|
+
if (order !== "first") {
|
|
836
|
+
const sign = order === "desc" ? -1 : 1;
|
|
837
|
+
entries = entries.sort((a, b) => sign * compareValues(a[1].value, b[1].value, collator));
|
|
838
|
+
}
|
|
839
|
+
return entries.map(([key, bucket]) => {
|
|
840
|
+
const id = `${parentId === "" ? "" : `${parentId}/`}${encode(column.id)}=${encode(key)}`;
|
|
841
|
+
return {
|
|
842
|
+
id,
|
|
843
|
+
field: column.id,
|
|
844
|
+
value: bucket.value,
|
|
845
|
+
depth,
|
|
846
|
+
indexes: bucket.indexes,
|
|
847
|
+
children: depth + 1 < levels.length ? build(bucket.indexes, depth + 1, id) : [],
|
|
848
|
+
aggregates: computeAggregates(
|
|
849
|
+
columns,
|
|
850
|
+
bucket.indexes.map((index) => rows[index])
|
|
851
|
+
)
|
|
852
|
+
};
|
|
853
|
+
});
|
|
854
|
+
};
|
|
855
|
+
return build(indexes, 0, "");
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// src/view.ts
|
|
859
|
+
function computeViewIndexes(rows, columns, options = {}) {
|
|
860
|
+
const matched = filterIndexes(rows, options.filter ?? [], columns, options);
|
|
861
|
+
return sortIndexes(rows, matched, options.sort ?? [], columns, options);
|
|
862
|
+
}
|
|
863
|
+
function buildDisplayRows(rows, indexes, columns, options) {
|
|
864
|
+
const out = [];
|
|
865
|
+
const expandedRows = options.expandedRows ?? {};
|
|
866
|
+
const pushData = (index, depth) => {
|
|
867
|
+
const row = rows[index];
|
|
868
|
+
const id = options.getRowId(row, index);
|
|
869
|
+
out.push({ kind: "data", id, row, index, depth });
|
|
870
|
+
if (options.withDetail === true && expandedRows[id] === true) {
|
|
871
|
+
out.push({ kind: "detail", id: `${id}:detail`, parentId: id, row, index, depth });
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
const group = options.group ?? [];
|
|
875
|
+
const sorted = new Map((options.sort ?? []).map((item) => [item.field, item.direction]));
|
|
876
|
+
const tree = buildGroupTree(rows, indexes, group, columns, {
|
|
877
|
+
...options.locale === void 0 ? {} : { locale: options.locale },
|
|
878
|
+
order: (field) => sorted.get(field) ?? "asc"
|
|
879
|
+
});
|
|
880
|
+
if (tree.length === 0) {
|
|
881
|
+
for (const index of indexes) pushData(index, 0);
|
|
882
|
+
return { rows: out, groups: [] };
|
|
883
|
+
}
|
|
884
|
+
const expansion = options.groupExpansion ?? {};
|
|
885
|
+
const byDefault = options.groupsExpandedByDefault ?? true;
|
|
886
|
+
const walk = (nodes) => {
|
|
887
|
+
for (const node of nodes) {
|
|
888
|
+
const expanded = expansion[node.id] ?? byDefault;
|
|
889
|
+
out.push({
|
|
890
|
+
kind: "group",
|
|
891
|
+
id: node.id,
|
|
892
|
+
field: node.field,
|
|
893
|
+
value: node.value,
|
|
894
|
+
depth: node.depth,
|
|
895
|
+
count: node.indexes.length,
|
|
896
|
+
expanded,
|
|
897
|
+
aggregates: node.aggregates
|
|
898
|
+
});
|
|
899
|
+
if (!expanded) continue;
|
|
900
|
+
if (node.children.length > 0) walk(node.children);
|
|
901
|
+
else for (const index of node.indexes) pushData(index, node.depth + 1);
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
walk(tree);
|
|
905
|
+
return { rows: out, groups: tree };
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// src/selection.ts
|
|
909
|
+
var EMPTY_SELECTION = Object.freeze({ mode: "include", ids: /* @__PURE__ */ new Set() });
|
|
910
|
+
function createSelection(ids = []) {
|
|
911
|
+
return { mode: "include", ids: new Set(ids) };
|
|
912
|
+
}
|
|
913
|
+
function isSelected(state, id) {
|
|
914
|
+
return state.mode === "include" ? state.ids.has(id) : !state.ids.has(id);
|
|
915
|
+
}
|
|
916
|
+
function isSelectionEmpty(state) {
|
|
917
|
+
return state.mode === "include" && state.ids.size === 0;
|
|
918
|
+
}
|
|
919
|
+
function toggleRow(state, id, selected) {
|
|
920
|
+
const next = selected ?? !isSelected(state, id);
|
|
921
|
+
if (isSelected(state, id) === next) return state;
|
|
922
|
+
const ids = new Set(state.ids);
|
|
923
|
+
if (next === (state.mode === "include")) ids.add(id);
|
|
924
|
+
else ids.delete(id);
|
|
925
|
+
return { mode: state.mode, ids };
|
|
926
|
+
}
|
|
927
|
+
function selectOnly(id) {
|
|
928
|
+
return createSelection([id]);
|
|
929
|
+
}
|
|
930
|
+
function selectRange(state, orderedIds, from, to, selected = true) {
|
|
931
|
+
if (orderedIds.length === 0) return state;
|
|
932
|
+
const last = orderedIds.length - 1;
|
|
933
|
+
const start = Math.max(0, Math.min(last, Math.min(from, to)));
|
|
934
|
+
const end = Math.max(0, Math.min(last, Math.max(from, to)));
|
|
935
|
+
const ids = new Set(state.ids);
|
|
936
|
+
const add = selected === (state.mode === "include");
|
|
937
|
+
for (let position = start; position <= end; position += 1) {
|
|
938
|
+
const id = orderedIds[position];
|
|
939
|
+
if (add) ids.add(id);
|
|
940
|
+
else ids.delete(id);
|
|
941
|
+
}
|
|
942
|
+
return { mode: state.mode, ids };
|
|
943
|
+
}
|
|
944
|
+
function selectAll(ids) {
|
|
945
|
+
return createSelection(ids);
|
|
946
|
+
}
|
|
947
|
+
function selectAllExceptNone() {
|
|
948
|
+
return { mode: "exclude", ids: /* @__PURE__ */ new Set() };
|
|
949
|
+
}
|
|
950
|
+
function clearSelection() {
|
|
951
|
+
return createSelection();
|
|
952
|
+
}
|
|
953
|
+
function countSelected(state, total) {
|
|
954
|
+
return state.mode === "include" ? state.ids.size : Math.max(0, total - state.ids.size);
|
|
955
|
+
}
|
|
956
|
+
function summarizeSelection(state, ids) {
|
|
957
|
+
if (ids.length === 0) return "none";
|
|
958
|
+
let selected = 0;
|
|
959
|
+
for (const id of ids) if (isSelected(state, id)) selected += 1;
|
|
960
|
+
if (selected === 0) return "none";
|
|
961
|
+
return selected === ids.length ? "all" : "some";
|
|
962
|
+
}
|
|
963
|
+
function resolveSelectedIds(state, allIds) {
|
|
964
|
+
const out = [];
|
|
965
|
+
for (const id of allIds) if (isSelected(state, id)) out.push(id);
|
|
966
|
+
return out;
|
|
967
|
+
}
|
|
968
|
+
function serializeSelection(state) {
|
|
969
|
+
return { mode: state.mode, ids: [...state.ids] };
|
|
970
|
+
}
|
|
971
|
+
function deserializeSelection(value) {
|
|
972
|
+
return { mode: value.mode === "exclude" ? "exclude" : "include", ids: new Set(value.ids) };
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// src/keyboard.ts
|
|
976
|
+
var NAVIGATION_KEYS = /* @__PURE__ */ new Set([
|
|
977
|
+
"ArrowUp",
|
|
978
|
+
"ArrowDown",
|
|
979
|
+
"ArrowLeft",
|
|
980
|
+
"ArrowRight",
|
|
981
|
+
"Home",
|
|
982
|
+
"End",
|
|
983
|
+
"PageUp",
|
|
984
|
+
"PageDown"
|
|
985
|
+
]);
|
|
986
|
+
function isNavigationKey(input) {
|
|
987
|
+
if (!NAVIGATION_KEYS.has(input.key)) return false;
|
|
988
|
+
if (input.altKey === true || input.metaKey === true) return false;
|
|
989
|
+
if (input.ctrlKey === true) return input.key === "Home" || input.key === "End";
|
|
990
|
+
return true;
|
|
991
|
+
}
|
|
992
|
+
var clamp = (value, max) => Math.max(0, Math.min(max, value));
|
|
993
|
+
function clampNav(state, bounds) {
|
|
994
|
+
const row = clamp(state.row, bounds.rowCount - 1);
|
|
995
|
+
const col = clamp(state.col, bounds.columnCount - 1);
|
|
996
|
+
return row === state.row && col === state.col ? state : { row, col };
|
|
997
|
+
}
|
|
998
|
+
function navigate(state, input, bounds) {
|
|
999
|
+
if (!isNavigationKey(input) || bounds.rowCount <= 0 || bounds.columnCount <= 0) return state;
|
|
1000
|
+
const lastRow = bounds.rowCount - 1;
|
|
1001
|
+
const lastCol = bounds.columnCount - 1;
|
|
1002
|
+
const forward = bounds.rtl === true ? "ArrowLeft" : "ArrowRight";
|
|
1003
|
+
const pageSize = Math.max(1, Math.floor(bounds.pageSize));
|
|
1004
|
+
let row = state.row;
|
|
1005
|
+
let col = state.col;
|
|
1006
|
+
switch (input.key) {
|
|
1007
|
+
case "ArrowUp":
|
|
1008
|
+
row -= 1;
|
|
1009
|
+
break;
|
|
1010
|
+
case "ArrowDown":
|
|
1011
|
+
row += 1;
|
|
1012
|
+
break;
|
|
1013
|
+
case "ArrowLeft":
|
|
1014
|
+
case "ArrowRight":
|
|
1015
|
+
col += input.key === forward ? 1 : -1;
|
|
1016
|
+
break;
|
|
1017
|
+
case "Home":
|
|
1018
|
+
col = 0;
|
|
1019
|
+
if (input.ctrlKey === true) row = 0;
|
|
1020
|
+
break;
|
|
1021
|
+
case "End":
|
|
1022
|
+
col = lastCol;
|
|
1023
|
+
if (input.ctrlKey === true) row = lastRow;
|
|
1024
|
+
break;
|
|
1025
|
+
case "PageUp":
|
|
1026
|
+
row -= pageSize;
|
|
1027
|
+
break;
|
|
1028
|
+
case "PageDown":
|
|
1029
|
+
row += pageSize;
|
|
1030
|
+
break;
|
|
1031
|
+
default:
|
|
1032
|
+
return state;
|
|
1033
|
+
}
|
|
1034
|
+
row = clamp(row, lastRow);
|
|
1035
|
+
col = clamp(col, lastCol);
|
|
1036
|
+
return row === state.row && col === state.col ? state : { row, col };
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
// src/state.ts
|
|
1040
|
+
function createViewState(columns) {
|
|
1041
|
+
const visibility = {};
|
|
1042
|
+
const pinning = { start: [], end: [] };
|
|
1043
|
+
for (const column of columns) {
|
|
1044
|
+
if (column.hidden === true) visibility[column.id] = false;
|
|
1045
|
+
if (column.pinned === "start") pinning.start.push(column.id);
|
|
1046
|
+
if (column.pinned === "end") pinning.end.push(column.id);
|
|
1047
|
+
}
|
|
1048
|
+
return {
|
|
1049
|
+
sort: [],
|
|
1050
|
+
filter: [],
|
|
1051
|
+
group: [],
|
|
1052
|
+
columnOrder: columns.map((column) => column.id),
|
|
1053
|
+
columnVisibility: visibility,
|
|
1054
|
+
columnSizing: {},
|
|
1055
|
+
columnPinning: pinning
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
function resolveColumnOrder(columns, order) {
|
|
1059
|
+
const known = new Set(columns.map((column) => column.id));
|
|
1060
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1061
|
+
const out = [];
|
|
1062
|
+
for (const id of order) {
|
|
1063
|
+
if (known.has(id) && !seen.has(id)) {
|
|
1064
|
+
seen.add(id);
|
|
1065
|
+
out.push(id);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
for (const column of columns) {
|
|
1069
|
+
if (!seen.has(column.id)) {
|
|
1070
|
+
seen.add(column.id);
|
|
1071
|
+
out.push(column.id);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
return out;
|
|
1075
|
+
}
|
|
1076
|
+
function layoutColumns(columns, state) {
|
|
1077
|
+
const byId = indexColumns(columns);
|
|
1078
|
+
const visible = (id) => byId.has(id) && state.columnVisibility[id] !== false;
|
|
1079
|
+
const start = state.columnPinning.start.filter(visible);
|
|
1080
|
+
const end = state.columnPinning.end.filter((id) => visible(id) && !start.includes(id));
|
|
1081
|
+
const pinned = /* @__PURE__ */ new Set([...start, ...end]);
|
|
1082
|
+
const center = resolveColumnOrder(columns, state.columnOrder).filter((id) => visible(id) && !pinned.has(id));
|
|
1083
|
+
return { start, center, end };
|
|
1084
|
+
}
|
|
1085
|
+
function toggleSort(sort, field, multi = false) {
|
|
1086
|
+
const current = sort.find((item) => item.field === field)?.direction;
|
|
1087
|
+
const next = current === void 0 ? "asc" : current === "asc" ? "desc" : null;
|
|
1088
|
+
return setSort(sort, field, next, multi);
|
|
1089
|
+
}
|
|
1090
|
+
function setSort(sort, field, direction, multi = false) {
|
|
1091
|
+
if (!multi) return direction === null ? [] : [{ field, direction }];
|
|
1092
|
+
const exists = sort.some((item) => item.field === field);
|
|
1093
|
+
if (direction === null) return sort.filter((item) => item.field !== field);
|
|
1094
|
+
if (!exists) return [...sort, { field, direction }];
|
|
1095
|
+
return sort.map((item) => item.field === field ? { field, direction } : item);
|
|
1096
|
+
}
|
|
1097
|
+
function sortRank(sort, field) {
|
|
1098
|
+
const position = sort.findIndex((item) => item.field === field);
|
|
1099
|
+
return position < 0 ? null : position + 1;
|
|
1100
|
+
}
|
|
1101
|
+
function setColumnFilter(filter, field, item) {
|
|
1102
|
+
const rest = filter.filter((entry) => entry.field !== field);
|
|
1103
|
+
return item === null ? rest : [...rest, item];
|
|
1104
|
+
}
|
|
1105
|
+
function toggleGroup(group, field) {
|
|
1106
|
+
return group.some((item) => item.field === field) ? group.filter((item) => item.field !== field) : [...group, { field }];
|
|
1107
|
+
}
|
|
1108
|
+
function setPinning(pinning, id, pin) {
|
|
1109
|
+
const start = pinning.start.filter((entry) => entry !== id);
|
|
1110
|
+
const end = pinning.end.filter((entry) => entry !== id);
|
|
1111
|
+
if (pin === "start") start.push(id);
|
|
1112
|
+
if (pin === "end") end.push(id);
|
|
1113
|
+
return { start, end };
|
|
1114
|
+
}
|
|
1115
|
+
function moveWithin(list, id, targetId) {
|
|
1116
|
+
const from = list.indexOf(id);
|
|
1117
|
+
const to = list.indexOf(targetId);
|
|
1118
|
+
if (from < 0 || to < 0 || from === to) return list.slice();
|
|
1119
|
+
const next = list.slice();
|
|
1120
|
+
next.splice(from, 1);
|
|
1121
|
+
next.splice(to, 0, id);
|
|
1122
|
+
return next;
|
|
1123
|
+
}
|
|
1124
|
+
function moveColumn(columns, state, id, delta) {
|
|
1125
|
+
const layout = layoutColumns(columns, state);
|
|
1126
|
+
const order = resolveColumnOrder(columns, state.columnOrder);
|
|
1127
|
+
const unchanged = { columnOrder: order, columnPinning: state.columnPinning };
|
|
1128
|
+
const zone = layout.start.includes(id) ? "start" : layout.end.includes(id) ? "end" : layout.center.includes(id) ? "center" : null;
|
|
1129
|
+
if (zone === null) return unchanged;
|
|
1130
|
+
const ids = layout[zone];
|
|
1131
|
+
const target = ids[ids.indexOf(id) + delta];
|
|
1132
|
+
if (target === void 0) return unchanged;
|
|
1133
|
+
if (zone === "center") return { columnOrder: moveWithin(order, id, target), columnPinning: state.columnPinning };
|
|
1134
|
+
const pinning = { start: state.columnPinning.start.slice(), end: state.columnPinning.end.slice() };
|
|
1135
|
+
pinning[zone] = moveWithin(pinning[zone], id, target);
|
|
1136
|
+
return { columnOrder: order, columnPinning: pinning };
|
|
1137
|
+
}
|
|
1138
|
+
function moveColumnTo(columns, state, id, targetId, placement) {
|
|
1139
|
+
const layout = layoutColumns(columns, state);
|
|
1140
|
+
const order = resolveColumnOrder(columns, state.columnOrder);
|
|
1141
|
+
const unchanged = { columnOrder: order, columnPinning: state.columnPinning };
|
|
1142
|
+
const zoneOf = (columnId) => layout.start.includes(columnId) ? "start" : layout.end.includes(columnId) ? "end" : layout.center.includes(columnId) ? "center" : null;
|
|
1143
|
+
const zone = zoneOf(id);
|
|
1144
|
+
if (zone === null || id === targetId || zone !== zoneOf(targetId)) return unchanged;
|
|
1145
|
+
const place = (list) => {
|
|
1146
|
+
if (!list.includes(id) || !list.includes(targetId)) return null;
|
|
1147
|
+
const rest = list.filter((entry) => entry !== id);
|
|
1148
|
+
rest.splice(rest.indexOf(targetId) + (placement === "after" ? 1 : 0), 0, id);
|
|
1149
|
+
return rest;
|
|
1150
|
+
};
|
|
1151
|
+
if (zone === "center") {
|
|
1152
|
+
const next2 = place(order);
|
|
1153
|
+
return next2 === null ? unchanged : { columnOrder: next2, columnPinning: state.columnPinning };
|
|
1154
|
+
}
|
|
1155
|
+
const next = place(state.columnPinning[zone]);
|
|
1156
|
+
if (next === null) return unchanged;
|
|
1157
|
+
return { columnOrder: order, columnPinning: { ...state.columnPinning, [zone]: next } };
|
|
1158
|
+
}
|
|
1159
|
+
function resizeColumn(columns, sizing, id, width) {
|
|
1160
|
+
const column = indexColumns(columns).get(id);
|
|
1161
|
+
if (column === void 0) return { ...sizing };
|
|
1162
|
+
return { ...sizing, [id]: clampColumnWidth(column, width) };
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
// src/csv.ts
|
|
1166
|
+
var PLAIN_NUMBER = /^[+-][\p{Sc}\s\d.,%()]*$/u;
|
|
1167
|
+
function escapeDelimitedField(text, delimiter = ",") {
|
|
1168
|
+
if (text === "") return "";
|
|
1169
|
+
const needsQuotes = text.includes(delimiter) || text.includes('"') || text.includes("\n") || text.includes("\r");
|
|
1170
|
+
return needsQuotes ? `"${text.replace(/"/g, '""')}"` : text;
|
|
1171
|
+
}
|
|
1172
|
+
function escapeCsvField(text, delimiter = ",") {
|
|
1173
|
+
return escapeDelimitedField(text, delimiter);
|
|
1174
|
+
}
|
|
1175
|
+
function neutralizeFormula(text) {
|
|
1176
|
+
if (text === "") return text;
|
|
1177
|
+
const first = text.charAt(0);
|
|
1178
|
+
if (first === "=" || first === "@" || first === " " || first === "\r") return `'${text}`;
|
|
1179
|
+
if ((first === "+" || first === "-") && !PLAIN_NUMBER.test(text)) return `'${text}`;
|
|
1180
|
+
return text;
|
|
1181
|
+
}
|
|
1182
|
+
function rawText(value) {
|
|
1183
|
+
if (isEmptyValue(value)) return "";
|
|
1184
|
+
return toText(value);
|
|
1185
|
+
}
|
|
1186
|
+
function exportableColumns(columns) {
|
|
1187
|
+
return columns.filter((column) => column.cellType !== "actions");
|
|
1188
|
+
}
|
|
1189
|
+
function toDelimited(rows, columns, options, defaults) {
|
|
1190
|
+
const delimiter = options.delimiter ?? defaults.delimiter;
|
|
1191
|
+
const eol = options.eol ?? defaults.eol;
|
|
1192
|
+
const sanitize = options.sanitizeFormulas ?? defaults.sanitize;
|
|
1193
|
+
const formatted = (options.values ?? "formatted") === "formatted";
|
|
1194
|
+
const fields = exportableColumns(columns).map((column) => ({
|
|
1195
|
+
column,
|
|
1196
|
+
format: formatted ? createCellFormatter(column, options) : null
|
|
1197
|
+
}));
|
|
1198
|
+
const cell = (text) => escapeDelimitedField(sanitize ? neutralizeFormula(text) : text, delimiter);
|
|
1199
|
+
const lines = [];
|
|
1200
|
+
if (options.header !== false) {
|
|
1201
|
+
lines.push(fields.map(({ column }) => cell(column.header)).join(delimiter));
|
|
1202
|
+
}
|
|
1203
|
+
for (const row of rows) {
|
|
1204
|
+
lines.push(
|
|
1205
|
+
fields.map(({ column, format }) => {
|
|
1206
|
+
const value = getColumnValue(column, row);
|
|
1207
|
+
return cell(format === null ? rawText(value) : format(value, row));
|
|
1208
|
+
}).join(delimiter)
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
return lines.join(eol);
|
|
1212
|
+
}
|
|
1213
|
+
function gridToCsv(rows, columns, options = {}) {
|
|
1214
|
+
const body = toDelimited(rows, columns, options, { delimiter: ",", eol: "\r\n", sanitize: true });
|
|
1215
|
+
return options.bom === true ? `\uFEFF${body}` : body;
|
|
1216
|
+
}
|
|
1217
|
+
function gridToTsv(rows, columns, options = {}) {
|
|
1218
|
+
return toDelimited(rows, columns, options, { delimiter: " ", eol: "\n", sanitize: false });
|
|
1219
|
+
}
|
|
1220
|
+
function parseDelimited(text, delimiter = ",") {
|
|
1221
|
+
const source = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
|
|
1222
|
+
const rows = [];
|
|
1223
|
+
let row = [];
|
|
1224
|
+
let field = "";
|
|
1225
|
+
let quoted = false;
|
|
1226
|
+
for (let i = 0; i < source.length; i += 1) {
|
|
1227
|
+
const char = source.charAt(i);
|
|
1228
|
+
if (quoted) {
|
|
1229
|
+
if (char === '"') {
|
|
1230
|
+
if (source.charAt(i + 1) === '"') {
|
|
1231
|
+
field += '"';
|
|
1232
|
+
i += 1;
|
|
1233
|
+
} else quoted = false;
|
|
1234
|
+
} else field += char;
|
|
1235
|
+
} else if (char === '"') quoted = true;
|
|
1236
|
+
else if (char === delimiter) {
|
|
1237
|
+
row.push(field);
|
|
1238
|
+
field = "";
|
|
1239
|
+
} else if (char === "\n" || char === "\r") {
|
|
1240
|
+
if (char === "\r" && source.charAt(i + 1) === "\n") i += 1;
|
|
1241
|
+
row.push(field);
|
|
1242
|
+
rows.push(row);
|
|
1243
|
+
row = [];
|
|
1244
|
+
field = "";
|
|
1245
|
+
} else field += char;
|
|
1246
|
+
}
|
|
1247
|
+
if (field !== "" || row.length > 0) {
|
|
1248
|
+
row.push(field);
|
|
1249
|
+
rows.push(row);
|
|
1250
|
+
}
|
|
1251
|
+
return rows;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
// src/saved-views.ts
|
|
1255
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1256
|
+
var isOptionalNumber = (value) => value === void 0 || typeof value === "number" && Number.isFinite(value);
|
|
1257
|
+
function parseSavedView(value) {
|
|
1258
|
+
if (!isRecord(value) || value.version !== SAVED_VIEW_VERSION) return null;
|
|
1259
|
+
if (value.name !== void 0 && typeof value.name !== "string") return null;
|
|
1260
|
+
const { columns, sort, filter, group } = value;
|
|
1261
|
+
if (!Array.isArray(columns) || !Array.isArray(sort) || !Array.isArray(filter)) return null;
|
|
1262
|
+
if (group !== void 0 && !Array.isArray(group)) return null;
|
|
1263
|
+
for (const column of columns) {
|
|
1264
|
+
if (!isRecord(column) || typeof column.id !== "string") return null;
|
|
1265
|
+
if (!isOptionalNumber(column.width)) return null;
|
|
1266
|
+
if (column.hidden !== void 0 && typeof column.hidden !== "boolean") return null;
|
|
1267
|
+
if (column.pinned !== void 0 && column.pinned !== "start" && column.pinned !== "end") {
|
|
1268
|
+
return null;
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
for (const item of sort) {
|
|
1272
|
+
if (!isRecord(item) || typeof item.field !== "string") return null;
|
|
1273
|
+
if (item.direction !== "asc" && item.direction !== "desc") return null;
|
|
1274
|
+
}
|
|
1275
|
+
for (const item of filter) {
|
|
1276
|
+
if (!isRecord(item) || typeof item.field !== "string") return null;
|
|
1277
|
+
if (!FILTER_OPERATORS.includes(item.operator)) return null;
|
|
1278
|
+
}
|
|
1279
|
+
for (const item of group ?? []) {
|
|
1280
|
+
if (!isRecord(item) || typeof item.field !== "string") return null;
|
|
1281
|
+
}
|
|
1282
|
+
return value;
|
|
1283
|
+
}
|
|
1284
|
+
function captureView(state, columns, options = {}) {
|
|
1285
|
+
const known = indexColumns(columns);
|
|
1286
|
+
const pinnedStart = state.columnPinning.start.filter((id) => known.has(id));
|
|
1287
|
+
const pinnedEnd = state.columnPinning.end.filter((id) => known.has(id) && !pinnedStart.includes(id));
|
|
1288
|
+
const pinned = /* @__PURE__ */ new Set([...pinnedStart, ...pinnedEnd]);
|
|
1289
|
+
const center = resolveColumnOrder(columns, state.columnOrder).filter((id) => !pinned.has(id));
|
|
1290
|
+
const entry = (id, pin) => {
|
|
1291
|
+
const out = { id };
|
|
1292
|
+
const width = state.columnSizing[id];
|
|
1293
|
+
if (width !== void 0) out.width = width;
|
|
1294
|
+
if (state.columnVisibility[id] === false) out.hidden = true;
|
|
1295
|
+
if (pin !== void 0) out.pinned = pin;
|
|
1296
|
+
return out;
|
|
1297
|
+
};
|
|
1298
|
+
const view = {
|
|
1299
|
+
version: SAVED_VIEW_VERSION,
|
|
1300
|
+
columns: [
|
|
1301
|
+
...pinnedStart.map((id) => entry(id, "start")),
|
|
1302
|
+
...center.map((id) => entry(id)),
|
|
1303
|
+
...pinnedEnd.map((id) => entry(id, "end"))
|
|
1304
|
+
],
|
|
1305
|
+
sort: state.sort.filter((item) => known.has(item.field)).map((item) => ({ ...item })),
|
|
1306
|
+
filter: state.filter.filter((item) => known.has(item.field)).map((item) => ({ ...item }))
|
|
1307
|
+
};
|
|
1308
|
+
if (options.name !== void 0) view.name = options.name;
|
|
1309
|
+
if (state.group.length > 0) view.group = state.group.filter((item) => known.has(item.field)).map((item) => ({ ...item }));
|
|
1310
|
+
return view;
|
|
1311
|
+
}
|
|
1312
|
+
function applySavedView(view, columns) {
|
|
1313
|
+
const defaults = createViewState(columns);
|
|
1314
|
+
const known = indexColumns(columns);
|
|
1315
|
+
const inView = view.columns.filter((column) => known.has(column.id));
|
|
1316
|
+
const mentioned = new Set(inView.map((column) => column.id));
|
|
1317
|
+
const visibility = {};
|
|
1318
|
+
for (const column of columns) {
|
|
1319
|
+
if (!mentioned.has(column.id) && defaults.columnVisibility[column.id] === false) visibility[column.id] = false;
|
|
1320
|
+
}
|
|
1321
|
+
const sizing = {};
|
|
1322
|
+
const pinning = { start: [], end: [] };
|
|
1323
|
+
for (const column of inView) {
|
|
1324
|
+
if (column.hidden === true) visibility[column.id] = false;
|
|
1325
|
+
if (column.width !== void 0) sizing[column.id] = column.width;
|
|
1326
|
+
if (column.pinned === "start") pinning.start.push(column.id);
|
|
1327
|
+
if (column.pinned === "end") pinning.end.push(column.id);
|
|
1328
|
+
}
|
|
1329
|
+
for (const column of columns) {
|
|
1330
|
+
if (mentioned.has(column.id)) continue;
|
|
1331
|
+
if (column.pinned === "start") pinning.start.push(column.id);
|
|
1332
|
+
if (column.pinned === "end") pinning.end.push(column.id);
|
|
1333
|
+
}
|
|
1334
|
+
return {
|
|
1335
|
+
sort: view.sort.filter((item) => known.has(item.field)).map((item) => ({ ...item })),
|
|
1336
|
+
filter: view.filter.filter((item) => known.has(item.field)).map((item) => ({ ...item })),
|
|
1337
|
+
group: (view.group ?? []).filter((item) => known.has(item.field)).map((item) => ({ ...item })),
|
|
1338
|
+
columnOrder: resolveColumnOrder(
|
|
1339
|
+
columns,
|
|
1340
|
+
inView.map((column) => column.id)
|
|
1341
|
+
),
|
|
1342
|
+
columnVisibility: visibility,
|
|
1343
|
+
columnSizing: sizing,
|
|
1344
|
+
columnPinning: pinning
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1347
|
+
function createMemorySavedViewStore(initial = {}) {
|
|
1348
|
+
const views = new Map(Object.entries(initial));
|
|
1349
|
+
return {
|
|
1350
|
+
list: async () => [...views.entries()].map(([id, view]) => view.name === void 0 ? { id } : { id, name: view.name }),
|
|
1351
|
+
load: async (id) => {
|
|
1352
|
+
const view = views.get(id);
|
|
1353
|
+
return view === void 0 ? null : parseSavedView(JSON.parse(JSON.stringify(view)));
|
|
1354
|
+
},
|
|
1355
|
+
save: async (id, view) => {
|
|
1356
|
+
views.set(id, JSON.parse(JSON.stringify(view)));
|
|
1357
|
+
},
|
|
1358
|
+
delete: async (id) => {
|
|
1359
|
+
views.delete(id);
|
|
1360
|
+
}
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
function createLocalStorageSavedViewStore(options = {}) {
|
|
1364
|
+
const key = options.key ?? "x12i-data-views";
|
|
1365
|
+
const fallback = /* @__PURE__ */ new Map();
|
|
1366
|
+
const resolve = () => {
|
|
1367
|
+
if (options.storage !== void 0) return options.storage;
|
|
1368
|
+
try {
|
|
1369
|
+
const candidate = globalThis.localStorage;
|
|
1370
|
+
if (candidate !== void 0) return candidate;
|
|
1371
|
+
} catch {
|
|
1372
|
+
}
|
|
1373
|
+
return { getItem: (k) => fallback.get(k) ?? null, setItem: (k, v) => void fallback.set(k, v) };
|
|
1374
|
+
};
|
|
1375
|
+
const read = () => {
|
|
1376
|
+
try {
|
|
1377
|
+
const parsed = JSON.parse(resolve().getItem(key) ?? "{}");
|
|
1378
|
+
return isRecord(parsed) ? parsed : {};
|
|
1379
|
+
} catch {
|
|
1380
|
+
return {};
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
return {
|
|
1384
|
+
list: async () => {
|
|
1385
|
+
const out = [];
|
|
1386
|
+
for (const [id, value] of Object.entries(read())) {
|
|
1387
|
+
const view = parseSavedView(value);
|
|
1388
|
+
if (view !== null) out.push(view.name === void 0 ? { id } : { id, name: view.name });
|
|
1389
|
+
}
|
|
1390
|
+
return out;
|
|
1391
|
+
},
|
|
1392
|
+
load: async (id) => parseSavedView(read()[id]),
|
|
1393
|
+
save: async (id, view) => {
|
|
1394
|
+
resolve().setItem(key, JSON.stringify({ ...read(), [id]: view }));
|
|
1395
|
+
},
|
|
1396
|
+
delete: async (id) => {
|
|
1397
|
+
const all = read();
|
|
1398
|
+
delete all[id];
|
|
1399
|
+
resolve().setItem(key, JSON.stringify(all));
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
// src/server.ts
|
|
1405
|
+
function createAbortError(message = "Aborted") {
|
|
1406
|
+
if (typeof DOMException === "function") return new DOMException(message, "AbortError");
|
|
1407
|
+
const error = new Error(message);
|
|
1408
|
+
error.name = "AbortError";
|
|
1409
|
+
return error;
|
|
1410
|
+
}
|
|
1411
|
+
function isAbortError(error) {
|
|
1412
|
+
return typeof error === "object" && error !== null && error.name === "AbortError";
|
|
1413
|
+
}
|
|
1414
|
+
function mapFields(items, map) {
|
|
1415
|
+
return items.map((item) => ({ ...item, field: map.get(item.field) ?? item.field }));
|
|
1416
|
+
}
|
|
1417
|
+
function toServerQuery(columns, query) {
|
|
1418
|
+
const map = new Map(columns.map((column) => [column.id, columnFieldName(column)]));
|
|
1419
|
+
const out = {
|
|
1420
|
+
sort: mapFields(query.sort, map),
|
|
1421
|
+
filter: mapFields(query.filter, map),
|
|
1422
|
+
group: mapFields(query.group, map)
|
|
1423
|
+
};
|
|
1424
|
+
if (query.quickFilter !== void 0 && query.quickFilter !== "") out.quickFilter = query.quickFilter;
|
|
1425
|
+
return out;
|
|
1426
|
+
}
|
|
1427
|
+
function fromServerQuery(columns, query) {
|
|
1428
|
+
const map = new Map(columns.map((column) => [columnFieldName(column), column.id]));
|
|
1429
|
+
const out = {
|
|
1430
|
+
sort: mapFields(query.sort, map),
|
|
1431
|
+
filter: mapFields(query.filter, map),
|
|
1432
|
+
group: mapFields(query.group, map)
|
|
1433
|
+
};
|
|
1434
|
+
if (query.quickFilter !== void 0) out.quickFilter = query.quickFilter;
|
|
1435
|
+
return out;
|
|
1436
|
+
}
|
|
1437
|
+
function delay(ms, signal) {
|
|
1438
|
+
return new Promise((resolve, reject) => {
|
|
1439
|
+
if (signal?.aborted === true) {
|
|
1440
|
+
reject(createAbortError());
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
const timer = setTimeout(() => {
|
|
1444
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1445
|
+
resolve();
|
|
1446
|
+
}, ms);
|
|
1447
|
+
const onAbort = () => {
|
|
1448
|
+
clearTimeout(timer);
|
|
1449
|
+
reject(createAbortError());
|
|
1450
|
+
};
|
|
1451
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1452
|
+
});
|
|
1453
|
+
}
|
|
1454
|
+
function createClientDataSource(rows, columns, options = {}) {
|
|
1455
|
+
return {
|
|
1456
|
+
async getRows(request, requestOptions) {
|
|
1457
|
+
const latency = typeof options.latency === "function" ? options.latency() : options.latency ?? 0;
|
|
1458
|
+
await delay(latency, requestOptions?.signal);
|
|
1459
|
+
const failure = options.fail?.(request);
|
|
1460
|
+
if (failure !== void 0 && failure !== false && failure !== null) {
|
|
1461
|
+
throw failure instanceof Error ? failure : new Error(String(failure));
|
|
1462
|
+
}
|
|
1463
|
+
const local = fromServerQuery(columns, request);
|
|
1464
|
+
const indexes = computeViewIndexes(rows, columns, {
|
|
1465
|
+
sort: local.sort,
|
|
1466
|
+
filter: local.filter,
|
|
1467
|
+
...local.quickFilter === void 0 ? {} : { quickFilter: local.quickFilter },
|
|
1468
|
+
...options.locale === void 0 ? {} : { locale: options.locale }
|
|
1469
|
+
});
|
|
1470
|
+
const slice = indexes.slice(request.startRow, request.endRow);
|
|
1471
|
+
const result = { rows: slice.map((index) => rows[index]), total: indexes.length };
|
|
1472
|
+
return result;
|
|
1473
|
+
}
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
function createServerRowStore(options) {
|
|
1477
|
+
const { dataSource } = options;
|
|
1478
|
+
let blockSize = Math.max(1, Math.floor(options.blockSize));
|
|
1479
|
+
let query = options.query ?? { sort: [], filter: [], group: [] };
|
|
1480
|
+
let queryKey = JSON.stringify(query);
|
|
1481
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
1482
|
+
const cursors = /* @__PURE__ */ new Map();
|
|
1483
|
+
let cursorMode = false;
|
|
1484
|
+
let total = null;
|
|
1485
|
+
let totalStale = false;
|
|
1486
|
+
let lastError = null;
|
|
1487
|
+
let version = 0;
|
|
1488
|
+
let destroyed = false;
|
|
1489
|
+
let windowStart = 0;
|
|
1490
|
+
let windowEnd = 0;
|
|
1491
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1492
|
+
const compute = () => {
|
|
1493
|
+
let loading = false;
|
|
1494
|
+
let stale = totalStale;
|
|
1495
|
+
const first = Math.floor(windowStart / blockSize);
|
|
1496
|
+
const last = Math.floor((windowEnd - 1) / blockSize);
|
|
1497
|
+
for (const [index, block] of blocks) {
|
|
1498
|
+
if (block.status === "loading") loading = true;
|
|
1499
|
+
if (block.stale && index >= first && index <= last) stale = true;
|
|
1500
|
+
}
|
|
1501
|
+
return { version, total, loading, error: lastError, stale };
|
|
1502
|
+
};
|
|
1503
|
+
let snapshot = compute();
|
|
1504
|
+
const emit = () => {
|
|
1505
|
+
version += 1;
|
|
1506
|
+
snapshot = compute();
|
|
1507
|
+
for (const listener of [...listeners]) listener();
|
|
1508
|
+
};
|
|
1509
|
+
const abortAll = () => {
|
|
1510
|
+
for (const block of blocks.values()) {
|
|
1511
|
+
block.controller?.abort();
|
|
1512
|
+
block.controller = null;
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
const load = (index) => {
|
|
1516
|
+
const existing = blocks.get(index);
|
|
1517
|
+
const block = existing ?? { status: "idle", rows: [], stale: false, controller: null };
|
|
1518
|
+
blocks.set(index, block);
|
|
1519
|
+
block.controller?.abort();
|
|
1520
|
+
const controller = new AbortController();
|
|
1521
|
+
block.controller = controller;
|
|
1522
|
+
block.status = "loading";
|
|
1523
|
+
const request = {
|
|
1524
|
+
startRow: index * blockSize,
|
|
1525
|
+
endRow: (index + 1) * blockSize,
|
|
1526
|
+
sort: query.sort,
|
|
1527
|
+
filter: query.filter,
|
|
1528
|
+
group: query.group
|
|
1529
|
+
};
|
|
1530
|
+
if (query.quickFilter !== void 0 && query.quickFilter !== "") request.quickFilter = query.quickFilter;
|
|
1531
|
+
if (cursorMode) request.cursor = index === 0 ? null : cursors.get(index) ?? null;
|
|
1532
|
+
return dataSource.getRows(request, { signal: controller.signal }).then(
|
|
1533
|
+
(result) => {
|
|
1534
|
+
if (destroyed || block.controller !== controller || controller.signal.aborted) return;
|
|
1535
|
+
block.controller = null;
|
|
1536
|
+
block.status = "loaded";
|
|
1537
|
+
block.rows = result.rows;
|
|
1538
|
+
block.stale = false;
|
|
1539
|
+
total = result.total;
|
|
1540
|
+
totalStale = false;
|
|
1541
|
+
lastError = null;
|
|
1542
|
+
if (result.nextCursor !== void 0) {
|
|
1543
|
+
cursorMode = true;
|
|
1544
|
+
cursors.set(index + 1, result.nextCursor);
|
|
1545
|
+
}
|
|
1546
|
+
emit();
|
|
1547
|
+
},
|
|
1548
|
+
(error) => {
|
|
1549
|
+
if (destroyed || block.controller !== controller || controller.signal.aborted || isAbortError(error)) return;
|
|
1550
|
+
block.controller = null;
|
|
1551
|
+
block.status = "error";
|
|
1552
|
+
lastError = error;
|
|
1553
|
+
emit();
|
|
1554
|
+
}
|
|
1555
|
+
);
|
|
1556
|
+
};
|
|
1557
|
+
const loadWithCursors = async (index) => {
|
|
1558
|
+
let start = index;
|
|
1559
|
+
while (start > 0 && !cursors.has(start)) start -= 1;
|
|
1560
|
+
for (let next = start; next <= index; next += 1) {
|
|
1561
|
+
const block = blocks.get(next);
|
|
1562
|
+
if (block !== void 0 && block.status === "loaded" && !block.stale) continue;
|
|
1563
|
+
await load(next);
|
|
1564
|
+
if (blocks.get(next)?.status !== "loaded") return;
|
|
1565
|
+
}
|
|
1566
|
+
};
|
|
1567
|
+
const needsLoad = (block) => block === void 0 || block.status === "idle" || block.status === "loaded" && block.stale;
|
|
1568
|
+
return {
|
|
1569
|
+
subscribe(listener) {
|
|
1570
|
+
listeners.add(listener);
|
|
1571
|
+
return () => void listeners.delete(listener);
|
|
1572
|
+
},
|
|
1573
|
+
getSnapshot: () => snapshot,
|
|
1574
|
+
setQuery(next) {
|
|
1575
|
+
const key = JSON.stringify(next);
|
|
1576
|
+
if (key === queryKey) return false;
|
|
1577
|
+
queryKey = key;
|
|
1578
|
+
query = next;
|
|
1579
|
+
abortAll();
|
|
1580
|
+
cursors.clear();
|
|
1581
|
+
for (const [index, block] of [...blocks.entries()]) {
|
|
1582
|
+
if (block.status === "loaded") {
|
|
1583
|
+
block.stale = true;
|
|
1584
|
+
block.status = "idle";
|
|
1585
|
+
} else {
|
|
1586
|
+
blocks.delete(index);
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
lastError = null;
|
|
1590
|
+
totalStale = total !== null;
|
|
1591
|
+
emit();
|
|
1592
|
+
return true;
|
|
1593
|
+
},
|
|
1594
|
+
setBlockSize(size) {
|
|
1595
|
+
const next = Math.max(1, Math.floor(size));
|
|
1596
|
+
if (next === blockSize) return;
|
|
1597
|
+
blockSize = next;
|
|
1598
|
+
abortAll();
|
|
1599
|
+
blocks.clear();
|
|
1600
|
+
cursors.clear();
|
|
1601
|
+
total = null;
|
|
1602
|
+
totalStale = false;
|
|
1603
|
+
lastError = null;
|
|
1604
|
+
emit();
|
|
1605
|
+
},
|
|
1606
|
+
request(startRow, endRow) {
|
|
1607
|
+
if (destroyed || endRow <= startRow) return;
|
|
1608
|
+
windowStart = Math.max(0, startRow);
|
|
1609
|
+
windowEnd = endRow;
|
|
1610
|
+
const first = Math.floor(Math.max(0, startRow) / blockSize);
|
|
1611
|
+
const last = Math.floor((endRow - 1) / blockSize);
|
|
1612
|
+
for (let index = first; index <= last; index += 1) {
|
|
1613
|
+
if (!needsLoad(blocks.get(index))) continue;
|
|
1614
|
+
if (cursorMode && index > 0) {
|
|
1615
|
+
if (cursors.has(index)) void load(index);
|
|
1616
|
+
else void loadWithCursors(index);
|
|
1617
|
+
} else {
|
|
1618
|
+
void load(index);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
const next = compute();
|
|
1622
|
+
if (snapshot.loading !== next.loading || snapshot.stale !== next.stale) emit();
|
|
1623
|
+
},
|
|
1624
|
+
getRow(index) {
|
|
1625
|
+
const block = blocks.get(Math.floor(index / blockSize));
|
|
1626
|
+
return block?.rows[index % blockSize];
|
|
1627
|
+
},
|
|
1628
|
+
getBlockStatus(index) {
|
|
1629
|
+
const block = blocks.get(Math.floor(index / blockSize));
|
|
1630
|
+
if (block === void 0) return "idle";
|
|
1631
|
+
return block.stale && block.status === "idle" ? "loaded" : block.status;
|
|
1632
|
+
},
|
|
1633
|
+
isStale(index) {
|
|
1634
|
+
return blocks.get(Math.floor(index / blockSize))?.stale === true;
|
|
1635
|
+
},
|
|
1636
|
+
retry() {
|
|
1637
|
+
const failed = [...blocks.entries()].filter(([, block]) => block.status === "error").map(([index]) => index);
|
|
1638
|
+
if (failed.length === 0) return;
|
|
1639
|
+
lastError = null;
|
|
1640
|
+
for (const index of failed) {
|
|
1641
|
+
if (cursorMode && index > 0 && !cursors.has(index)) void loadWithCursors(index);
|
|
1642
|
+
else void load(index);
|
|
1643
|
+
}
|
|
1644
|
+
emit();
|
|
1645
|
+
},
|
|
1646
|
+
reset() {
|
|
1647
|
+
abortAll();
|
|
1648
|
+
blocks.clear();
|
|
1649
|
+
cursors.clear();
|
|
1650
|
+
total = null;
|
|
1651
|
+
totalStale = false;
|
|
1652
|
+
lastError = null;
|
|
1653
|
+
emit();
|
|
1654
|
+
},
|
|
1655
|
+
destroy() {
|
|
1656
|
+
destroyed = true;
|
|
1657
|
+
abortAll();
|
|
1658
|
+
listeners.clear();
|
|
1659
|
+
}
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
// src/message-format.ts
|
|
1664
|
+
function formatMessage(template, values = {}, locale) {
|
|
1665
|
+
return render(template, values, locale, void 0);
|
|
1666
|
+
}
|
|
1667
|
+
function formatNumber(value, locale) {
|
|
1668
|
+
try {
|
|
1669
|
+
return new Intl.NumberFormat(locale).format(value);
|
|
1670
|
+
} catch {
|
|
1671
|
+
return String(value);
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
function formatValue(value, locale) {
|
|
1675
|
+
if (value === void 0) return "";
|
|
1676
|
+
return typeof value === "number" ? formatNumber(value, locale) : value;
|
|
1677
|
+
}
|
|
1678
|
+
function closingBrace(text, open) {
|
|
1679
|
+
let depth = 0;
|
|
1680
|
+
for (let i = open; i < text.length; i += 1) {
|
|
1681
|
+
const char = text.charAt(i);
|
|
1682
|
+
if (char === "{") depth += 1;
|
|
1683
|
+
else if (char === "}") {
|
|
1684
|
+
depth -= 1;
|
|
1685
|
+
if (depth === 0) return i;
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
return -1;
|
|
1689
|
+
}
|
|
1690
|
+
function render(template, values, locale, pluralValue) {
|
|
1691
|
+
let out = "";
|
|
1692
|
+
let i = 0;
|
|
1693
|
+
while (i < template.length) {
|
|
1694
|
+
const char = template.charAt(i);
|
|
1695
|
+
if (char === "{") {
|
|
1696
|
+
const end = closingBrace(template, i);
|
|
1697
|
+
if (end < 0) {
|
|
1698
|
+
out += template.slice(i);
|
|
1699
|
+
break;
|
|
1700
|
+
}
|
|
1701
|
+
out += evaluate(template.slice(i + 1, end), values, locale);
|
|
1702
|
+
i = end + 1;
|
|
1703
|
+
} else if (char === "#" && pluralValue !== void 0) {
|
|
1704
|
+
out += formatNumber(pluralValue, locale);
|
|
1705
|
+
i += 1;
|
|
1706
|
+
} else {
|
|
1707
|
+
out += char;
|
|
1708
|
+
i += 1;
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
return out;
|
|
1712
|
+
}
|
|
1713
|
+
function evaluate(body, values, locale) {
|
|
1714
|
+
const firstComma = body.indexOf(",");
|
|
1715
|
+
if (firstComma < 0) return formatValue(values[body.trim()], locale);
|
|
1716
|
+
const name = body.slice(0, firstComma).trim();
|
|
1717
|
+
const rest = body.slice(firstComma + 1);
|
|
1718
|
+
const secondComma = rest.indexOf(",");
|
|
1719
|
+
if (secondComma < 0) return formatValue(values[name], locale);
|
|
1720
|
+
const kind = rest.slice(0, secondComma).trim();
|
|
1721
|
+
const branchesSource = rest.slice(secondComma + 1);
|
|
1722
|
+
const branches = /* @__PURE__ */ new Map();
|
|
1723
|
+
let i = 0;
|
|
1724
|
+
while (i < branchesSource.length) {
|
|
1725
|
+
while (i < branchesSource.length && /\s/.test(branchesSource.charAt(i))) i += 1;
|
|
1726
|
+
let selector = "";
|
|
1727
|
+
while (i < branchesSource.length && !/[\s{]/.test(branchesSource.charAt(i))) {
|
|
1728
|
+
selector += branchesSource.charAt(i);
|
|
1729
|
+
i += 1;
|
|
1730
|
+
}
|
|
1731
|
+
while (i < branchesSource.length && /\s/.test(branchesSource.charAt(i))) i += 1;
|
|
1732
|
+
if (branchesSource.charAt(i) !== "{") break;
|
|
1733
|
+
const end = closingBrace(branchesSource, i);
|
|
1734
|
+
if (end < 0) break;
|
|
1735
|
+
branches.set(selector, branchesSource.slice(i + 1, end));
|
|
1736
|
+
i = end + 1;
|
|
1737
|
+
}
|
|
1738
|
+
const value = values[name];
|
|
1739
|
+
if (kind === "plural") {
|
|
1740
|
+
const count = typeof value === "number" ? value : Number(value);
|
|
1741
|
+
if (Number.isNaN(count)) return "";
|
|
1742
|
+
let category = "other";
|
|
1743
|
+
try {
|
|
1744
|
+
category = new Intl.PluralRules(locale).select(count);
|
|
1745
|
+
} catch {
|
|
1746
|
+
}
|
|
1747
|
+
const chosen = branches.get(`=${count}`) ?? branches.get(category) ?? branches.get("other") ?? "";
|
|
1748
|
+
return render(chosen, values, locale, count);
|
|
1749
|
+
}
|
|
1750
|
+
if (kind === "select") {
|
|
1751
|
+
const chosen = branches.get(String(value)) ?? branches.get("other") ?? "";
|
|
1752
|
+
return render(chosen, values, locale, void 0);
|
|
1753
|
+
}
|
|
1754
|
+
return formatValue(value, locale);
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
// src/messages.ts
|
|
1758
|
+
var defaultMessages = {
|
|
1759
|
+
loading: "Loading rows",
|
|
1760
|
+
loadingMore: "Loading more rows",
|
|
1761
|
+
empty: "No rows to show",
|
|
1762
|
+
emptyFiltered: "No rows match the current filters",
|
|
1763
|
+
error: "Could not load rows",
|
|
1764
|
+
retry: "Try again",
|
|
1765
|
+
clearFilters: "Clear filters",
|
|
1766
|
+
toolbarLabel: "Grid tools",
|
|
1767
|
+
quickSearch: "Search rows",
|
|
1768
|
+
quickSearchPlaceholder: "Search",
|
|
1769
|
+
clearSearch: "Clear search",
|
|
1770
|
+
columns: "Columns",
|
|
1771
|
+
columnsMenuLabel: "Show or hide columns",
|
|
1772
|
+
showAllColumns: "Show all columns",
|
|
1773
|
+
exportCsv: "Export CSV",
|
|
1774
|
+
copySelection: "Copy",
|
|
1775
|
+
copied: "{count, plural, one {Copied # row} other {Copied # rows}}",
|
|
1776
|
+
copyFailed: "Could not copy to the clipboard",
|
|
1777
|
+
selectedCount: "{count, plural, one {# row selected} other {# rows selected}}",
|
|
1778
|
+
allSelected: "All {count, plural, one {# row} other {# rows}} selected",
|
|
1779
|
+
clearSelection: "Clear selection",
|
|
1780
|
+
selectAll: "Select all rows",
|
|
1781
|
+
selectRow: "Select row",
|
|
1782
|
+
sortAscending: "Sort ascending",
|
|
1783
|
+
sortDescending: "Sort descending",
|
|
1784
|
+
clearSort: "Clear sort",
|
|
1785
|
+
filterColumn: "Filter {column}",
|
|
1786
|
+
columnMenu: "Options for {column}",
|
|
1787
|
+
hideColumn: "Hide column",
|
|
1788
|
+
pinStart: "Pin to start",
|
|
1789
|
+
pinEnd: "Pin to end",
|
|
1790
|
+
unpin: "Unpin",
|
|
1791
|
+
moveEarlier: "Move earlier",
|
|
1792
|
+
moveLater: "Move later",
|
|
1793
|
+
groupBy: "Group by {column}",
|
|
1794
|
+
ungroup: "Stop grouping by {column}",
|
|
1795
|
+
resizeColumn: "Resize {column}",
|
|
1796
|
+
sortRank: "Sort priority {rank}",
|
|
1797
|
+
filterOperator: "Operator",
|
|
1798
|
+
filterValue: "Value",
|
|
1799
|
+
filterFrom: "From",
|
|
1800
|
+
filterTo: "To",
|
|
1801
|
+
filterApply: "Apply",
|
|
1802
|
+
filterClear: "Clear",
|
|
1803
|
+
filterAny: "Any",
|
|
1804
|
+
filterNoOptions: "No values to choose from",
|
|
1805
|
+
filterBlank: "(Blank)",
|
|
1806
|
+
operators: {
|
|
1807
|
+
contains: "contains",
|
|
1808
|
+
"not-contains": "does not contain",
|
|
1809
|
+
"starts-with": "starts with",
|
|
1810
|
+
equals: "is",
|
|
1811
|
+
"not-equals": "is not",
|
|
1812
|
+
gt: "is greater than",
|
|
1813
|
+
gte: "is at least",
|
|
1814
|
+
lt: "is less than",
|
|
1815
|
+
lte: "is at most",
|
|
1816
|
+
between: "is between",
|
|
1817
|
+
in: "is one of",
|
|
1818
|
+
before: "is before",
|
|
1819
|
+
after: "is after",
|
|
1820
|
+
"is-empty": "is empty",
|
|
1821
|
+
"is-not-empty": "is not empty"
|
|
1822
|
+
},
|
|
1823
|
+
activeFilters: "Active filters",
|
|
1824
|
+
filterSummary: "{column} {operator} {value}",
|
|
1825
|
+
removeFilter: "Remove filter {summary}",
|
|
1826
|
+
clearAllFilters: "Clear all",
|
|
1827
|
+
groupedBy: "Grouped by {column}",
|
|
1828
|
+
removeGrouping: "Stop grouping by {column}",
|
|
1829
|
+
expandRow: "Expand row",
|
|
1830
|
+
collapseRow: "Collapse row",
|
|
1831
|
+
expandGroup: "Expand group",
|
|
1832
|
+
collapseGroup: "Collapse group",
|
|
1833
|
+
groupLabel: "{value} ({count})",
|
|
1834
|
+
rowActions: "Row actions",
|
|
1835
|
+
detailRegion: "Row details",
|
|
1836
|
+
totals: "Total",
|
|
1837
|
+
yes: "Yes",
|
|
1838
|
+
no: "No",
|
|
1839
|
+
moreTags: "+{count}",
|
|
1840
|
+
progressLabel: "{value} complete",
|
|
1841
|
+
editCell: "Edit {column}",
|
|
1842
|
+
cellEditFailed: "Could not save {column}. The change was undone.",
|
|
1843
|
+
cellSaved: "{column} saved",
|
|
1844
|
+
invalidNumber: "Enter a number",
|
|
1845
|
+
invalidDate: "Enter a valid date",
|
|
1846
|
+
announceSorted: "Sorted by {column}, {direction, select, desc {descending} other {ascending}}. {count, plural, one {# row} other {# rows}}.",
|
|
1847
|
+
announceSortCleared: "Sorting cleared. {count, plural, one {# row} other {# rows}}.",
|
|
1848
|
+
announceFiltered: "{count, plural, =0 {No rows match} one {# row matches} other {# rows match}} the filters.",
|
|
1849
|
+
announceFilterCleared: "Filters cleared. {count, plural, one {# row} other {# rows}}.",
|
|
1850
|
+
announceGrouped: "Grouped by {column}.",
|
|
1851
|
+
announceColumnMoved: "{column} moved to position {position} of {total}.",
|
|
1852
|
+
announceColumnHidden: "{column} hidden.",
|
|
1853
|
+
announceColumnResized: "{column} width {width} pixels.",
|
|
1854
|
+
announcePinned: "{column} pinned to the {side, select, end {end} other {start}}.",
|
|
1855
|
+
announceSelection: "{count, plural, one {# row selected} other {# rows selected}}.",
|
|
1856
|
+
rowLoading: "Loading row",
|
|
1857
|
+
rowFailed: "Row could not be loaded",
|
|
1858
|
+
paginationLabel: "Pagination",
|
|
1859
|
+
pageSize: "Rows per page",
|
|
1860
|
+
firstPage: "First page",
|
|
1861
|
+
previousPage: "Previous page",
|
|
1862
|
+
nextPage: "Next page",
|
|
1863
|
+
lastPage: "Last page",
|
|
1864
|
+
pageRange: "{start}\u2013{end} of {total}",
|
|
1865
|
+
pageStatus: "Page {page} of {pages}"
|
|
1866
|
+
};
|
|
1867
|
+
function resolveMessages(overrides) {
|
|
1868
|
+
if (overrides === void 0) return defaultMessages;
|
|
1869
|
+
const { operators, ...rest } = overrides;
|
|
1870
|
+
const merged = { ...defaultMessages };
|
|
1871
|
+
for (const [key, value] of Object.entries(rest)) if (value !== void 0) merged[key] = value;
|
|
1872
|
+
return {
|
|
1873
|
+
...merged,
|
|
1874
|
+
operators: { ...defaultMessages.operators, ...operators }
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
export {
|
|
1879
|
+
defineColumns,
|
|
1880
|
+
SAVED_VIEW_VERSION,
|
|
1881
|
+
FILTER_OPERATORS,
|
|
1882
|
+
DEFAULT_COLUMN_WIDTH,
|
|
1883
|
+
DEFAULT_COLUMN_MIN_WIDTH,
|
|
1884
|
+
DEFAULT_COLUMN_MAX_WIDTH,
|
|
1885
|
+
getColumnValue,
|
|
1886
|
+
columnFieldName,
|
|
1887
|
+
isNumericCellType,
|
|
1888
|
+
resolveColumnAlign,
|
|
1889
|
+
resolveFilterType,
|
|
1890
|
+
resolveEditor,
|
|
1891
|
+
isSortable,
|
|
1892
|
+
isFilterable,
|
|
1893
|
+
isResizable,
|
|
1894
|
+
isEditable,
|
|
1895
|
+
clampColumnWidth,
|
|
1896
|
+
initialColumnWidth,
|
|
1897
|
+
indexColumns,
|
|
1898
|
+
isDateOnlyString,
|
|
1899
|
+
isEmptyValue,
|
|
1900
|
+
toNumber,
|
|
1901
|
+
toTimestamp,
|
|
1902
|
+
toDate,
|
|
1903
|
+
toText,
|
|
1904
|
+
normalizeText,
|
|
1905
|
+
toBoolean,
|
|
1906
|
+
defaultFormatLabels,
|
|
1907
|
+
resolveNumberFormatOptions,
|
|
1908
|
+
resolveDateFormatOptions,
|
|
1909
|
+
createCellFormatter,
|
|
1910
|
+
formatCellValue,
|
|
1911
|
+
createCollator,
|
|
1912
|
+
toSortKey,
|
|
1913
|
+
compareValues,
|
|
1914
|
+
sortIndexes,
|
|
1915
|
+
sortRows,
|
|
1916
|
+
OPERATORS_BY_FILTER_TYPE,
|
|
1917
|
+
operatorsFor,
|
|
1918
|
+
isValuelessOperator,
|
|
1919
|
+
isFilterEffective,
|
|
1920
|
+
toDayString,
|
|
1921
|
+
dayStart,
|
|
1922
|
+
nextDay,
|
|
1923
|
+
createValuePredicate,
|
|
1924
|
+
matchesFilter,
|
|
1925
|
+
parseQuickFilter,
|
|
1926
|
+
isSearchable,
|
|
1927
|
+
createSearchIndex,
|
|
1928
|
+
filterIndexes,
|
|
1929
|
+
distinctValues,
|
|
1930
|
+
aggregateValues,
|
|
1931
|
+
computeAggregate,
|
|
1932
|
+
computeAggregates,
|
|
1933
|
+
buildGroupTree,
|
|
1934
|
+
computeViewIndexes,
|
|
1935
|
+
buildDisplayRows,
|
|
1936
|
+
EMPTY_SELECTION,
|
|
1937
|
+
createSelection,
|
|
1938
|
+
isSelected,
|
|
1939
|
+
isSelectionEmpty,
|
|
1940
|
+
toggleRow,
|
|
1941
|
+
selectOnly,
|
|
1942
|
+
selectRange,
|
|
1943
|
+
selectAll,
|
|
1944
|
+
selectAllExceptNone,
|
|
1945
|
+
clearSelection,
|
|
1946
|
+
countSelected,
|
|
1947
|
+
summarizeSelection,
|
|
1948
|
+
resolveSelectedIds,
|
|
1949
|
+
serializeSelection,
|
|
1950
|
+
deserializeSelection,
|
|
1951
|
+
isNavigationKey,
|
|
1952
|
+
clampNav,
|
|
1953
|
+
navigate,
|
|
1954
|
+
createViewState,
|
|
1955
|
+
resolveColumnOrder,
|
|
1956
|
+
layoutColumns,
|
|
1957
|
+
toggleSort,
|
|
1958
|
+
setSort,
|
|
1959
|
+
sortRank,
|
|
1960
|
+
setColumnFilter,
|
|
1961
|
+
toggleGroup,
|
|
1962
|
+
setPinning,
|
|
1963
|
+
moveColumn,
|
|
1964
|
+
moveColumnTo,
|
|
1965
|
+
resizeColumn,
|
|
1966
|
+
escapeDelimitedField,
|
|
1967
|
+
escapeCsvField,
|
|
1968
|
+
exportableColumns,
|
|
1969
|
+
gridToCsv,
|
|
1970
|
+
gridToTsv,
|
|
1971
|
+
parseDelimited,
|
|
1972
|
+
parseSavedView,
|
|
1973
|
+
captureView,
|
|
1974
|
+
applySavedView,
|
|
1975
|
+
createMemorySavedViewStore,
|
|
1976
|
+
createLocalStorageSavedViewStore,
|
|
1977
|
+
createAbortError,
|
|
1978
|
+
isAbortError,
|
|
1979
|
+
toServerQuery,
|
|
1980
|
+
fromServerQuery,
|
|
1981
|
+
createClientDataSource,
|
|
1982
|
+
createServerRowStore,
|
|
1983
|
+
formatMessage,
|
|
1984
|
+
defaultMessages,
|
|
1985
|
+
resolveMessages
|
|
1986
|
+
};
|
|
1987
|
+
//# sourceMappingURL=chunk-ASP4ABDG.js.map
|