@softium/table-core 0.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/LICENSE +21 -0
- package/README.md +33 -0
- package/dist/index.cjs +769 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +447 -0
- package/dist/index.d.ts +447 -0
- package/dist/index.js +736 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,769 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/adapters/adaptArray.ts
|
|
4
|
+
function adaptArray(raw) {
|
|
5
|
+
if (!Array.isArray(raw)) {
|
|
6
|
+
throw new TypeError(
|
|
7
|
+
`adaptArray expected an array, received ${raw === null ? "null" : typeof raw}.`
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
return raw;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// src/adapters/util.ts
|
|
14
|
+
function isRecord(value) {
|
|
15
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16
|
+
}
|
|
17
|
+
function getProp(value, key) {
|
|
18
|
+
return isRecord(value) ? value[key] : void 0;
|
|
19
|
+
}
|
|
20
|
+
function asNumber(value, fallback) {
|
|
21
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// src/adapters/adaptPaginated.ts
|
|
25
|
+
function adaptPaginated(raw, options = {}) {
|
|
26
|
+
const {
|
|
27
|
+
dataKey = "data",
|
|
28
|
+
totalKey = "total",
|
|
29
|
+
pageKey = "page",
|
|
30
|
+
pageSizeKey = "pageSize"
|
|
31
|
+
} = options;
|
|
32
|
+
const rawData = getProp(raw, dataKey);
|
|
33
|
+
if (!Array.isArray(rawData)) {
|
|
34
|
+
throw new TypeError(
|
|
35
|
+
`adaptPaginated expected an array at "${dataKey}", received ${rawData === void 0 ? "undefined" : typeof rawData}.`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
const data = rawData;
|
|
39
|
+
const total = asNumber(getProp(raw, totalKey), data.length);
|
|
40
|
+
const page = asNumber(getProp(raw, pageKey), 1);
|
|
41
|
+
const pageSize = asNumber(getProp(raw, pageSizeKey), data.length);
|
|
42
|
+
return { data, total, page, pageSize };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/adapters/adaptDynamicSchema.ts
|
|
46
|
+
var KNOWN_TYPES = /* @__PURE__ */ new Set([
|
|
47
|
+
"text",
|
|
48
|
+
"number",
|
|
49
|
+
"date",
|
|
50
|
+
"boolean",
|
|
51
|
+
"select",
|
|
52
|
+
"custom"
|
|
53
|
+
]);
|
|
54
|
+
function toColumnType(value) {
|
|
55
|
+
return typeof value === "string" && KNOWN_TYPES.has(value) ? value : void 0;
|
|
56
|
+
}
|
|
57
|
+
function adaptDynamicSchema(raw, options = {}) {
|
|
58
|
+
const {
|
|
59
|
+
columnsKey = "columns",
|
|
60
|
+
rowsKey = "rows",
|
|
61
|
+
fieldKey = "field",
|
|
62
|
+
labelKey = "label",
|
|
63
|
+
typeKey = "type"
|
|
64
|
+
} = options;
|
|
65
|
+
const rawColumns = getProp(raw, columnsKey);
|
|
66
|
+
if (!Array.isArray(rawColumns)) {
|
|
67
|
+
throw new TypeError(
|
|
68
|
+
`adaptDynamicSchema expected an array at "${columnsKey}", received ${typeof rawColumns}.`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
const rawRows = getProp(raw, rowsKey);
|
|
72
|
+
if (!Array.isArray(rawRows)) {
|
|
73
|
+
throw new TypeError(
|
|
74
|
+
`adaptDynamicSchema expected an array at "${rowsKey}", received ${typeof rawRows}.`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
const columns = rawColumns.map((descriptor, i) => {
|
|
78
|
+
if (!isRecord(descriptor)) {
|
|
79
|
+
throw new TypeError(`adaptDynamicSchema: column descriptor at index ${i} is not an object.`);
|
|
80
|
+
}
|
|
81
|
+
const field = descriptor[fieldKey];
|
|
82
|
+
if (typeof field !== "string" || field.length === 0) {
|
|
83
|
+
throw new TypeError(
|
|
84
|
+
`adaptDynamicSchema: column descriptor at index ${i} has no string "${fieldKey}".`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
const rawLabel = descriptor[labelKey];
|
|
88
|
+
const label = typeof rawLabel === "string" && rawLabel.length > 0 ? rawLabel : field;
|
|
89
|
+
const type = toColumnType(descriptor[typeKey]);
|
|
90
|
+
return {
|
|
91
|
+
key: field,
|
|
92
|
+
label,
|
|
93
|
+
...type ? { type } : {}
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
return { columns, data: rawRows };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/derive/columns.ts
|
|
100
|
+
function createInitialColumnState(defs) {
|
|
101
|
+
return defs.map((def, index) => ({
|
|
102
|
+
key: def.key,
|
|
103
|
+
visible: true,
|
|
104
|
+
order: index,
|
|
105
|
+
width: def.width,
|
|
106
|
+
pinned: null
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
function reconcileColumnState(defs, stored) {
|
|
110
|
+
const storedByKey = new Map(stored.map((s) => [s.key, s]));
|
|
111
|
+
const maxOrder = stored.reduce((m, s) => Math.max(m, s.order), -1);
|
|
112
|
+
let nextOrder = maxOrder + 1;
|
|
113
|
+
return defs.map((def) => {
|
|
114
|
+
const existing = storedByKey.get(def.key);
|
|
115
|
+
if (existing) return existing;
|
|
116
|
+
return {
|
|
117
|
+
key: def.key,
|
|
118
|
+
visible: true,
|
|
119
|
+
order: nextOrder++,
|
|
120
|
+
width: def.width,
|
|
121
|
+
pinned: null
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
var PIN_GROUP = {
|
|
126
|
+
left: 0,
|
|
127
|
+
none: 1,
|
|
128
|
+
right: 2
|
|
129
|
+
};
|
|
130
|
+
function pinGroup(pinned) {
|
|
131
|
+
if (pinned === "left") return PIN_GROUP.left;
|
|
132
|
+
if (pinned === "right") return PIN_GROUP.right;
|
|
133
|
+
return PIN_GROUP.none;
|
|
134
|
+
}
|
|
135
|
+
function resolveColumns(defs, state) {
|
|
136
|
+
const stateByKey = /* @__PURE__ */ new Map();
|
|
137
|
+
for (const s of state) stateByKey.set(s.key, s);
|
|
138
|
+
const resolved = [];
|
|
139
|
+
defs.forEach((def, index) => {
|
|
140
|
+
const s = stateByKey.get(def.key);
|
|
141
|
+
const visible = s?.visible ?? true;
|
|
142
|
+
if (!visible) return;
|
|
143
|
+
const order = s?.order ?? index;
|
|
144
|
+
const pinned = s?.pinned ?? null;
|
|
145
|
+
const labelOverride = s?.labelOverride;
|
|
146
|
+
const type = def.type ?? "text";
|
|
147
|
+
const width = s?.width ?? def.width ?? (def.flex ? void 0 : defaultWidthForType(type));
|
|
148
|
+
resolved.push({
|
|
149
|
+
key: def.key,
|
|
150
|
+
label: def.label,
|
|
151
|
+
displayLabel: labelOverride && labelOverride.length > 0 ? labelOverride : def.label,
|
|
152
|
+
type,
|
|
153
|
+
align: s?.align ?? def.align ?? defaultAlign(def.type),
|
|
154
|
+
order,
|
|
155
|
+
visible: true,
|
|
156
|
+
pinned,
|
|
157
|
+
width,
|
|
158
|
+
minWidth: def.minWidth,
|
|
159
|
+
maxWidth: def.maxWidth,
|
|
160
|
+
flex: def.flex,
|
|
161
|
+
// interaction defaults: on unless opted out; filtering is opt-in via `filterable`
|
|
162
|
+
sortable: def.sortable ?? true,
|
|
163
|
+
filterable: def.filterable ?? false,
|
|
164
|
+
resizable: def.resizable ?? true,
|
|
165
|
+
pinnable: def.pinnable ?? true,
|
|
166
|
+
hideable: def.hideable ?? true,
|
|
167
|
+
editable: def.editable ?? false,
|
|
168
|
+
copyable: def.copyable ?? true,
|
|
169
|
+
renderCell: def.renderCell,
|
|
170
|
+
renderHeader: def.renderHeader,
|
|
171
|
+
def
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
return resolved.sort((a, b) => {
|
|
175
|
+
const groupDiff = pinGroup(a.pinned) - pinGroup(b.pinned);
|
|
176
|
+
if (groupDiff !== 0) return groupDiff;
|
|
177
|
+
return a.order - b.order;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
function defaultAlign(type) {
|
|
181
|
+
if (type === "number") return "right";
|
|
182
|
+
if (type === "boolean") return "center";
|
|
183
|
+
return "left";
|
|
184
|
+
}
|
|
185
|
+
function defaultWidthForType(type) {
|
|
186
|
+
switch (type) {
|
|
187
|
+
case "boolean":
|
|
188
|
+
return 72;
|
|
189
|
+
case "number":
|
|
190
|
+
return 120;
|
|
191
|
+
case "date":
|
|
192
|
+
return 128;
|
|
193
|
+
case "select":
|
|
194
|
+
return 150;
|
|
195
|
+
default:
|
|
196
|
+
return 140;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/derive/columnOps.ts
|
|
201
|
+
function patchColumnState(state, key, patch) {
|
|
202
|
+
return state.map((s) => s.key === key ? { ...s, ...patch } : s);
|
|
203
|
+
}
|
|
204
|
+
function setColumnVisible(state, key, visible) {
|
|
205
|
+
return patchColumnState(state, key, { visible });
|
|
206
|
+
}
|
|
207
|
+
function setColumnPinned(state, key, pinned) {
|
|
208
|
+
return patchColumnState(state, key, { pinned });
|
|
209
|
+
}
|
|
210
|
+
function setColumnWidth(state, key, width) {
|
|
211
|
+
return patchColumnState(state, key, { width: Math.max(0, Math.round(width)) });
|
|
212
|
+
}
|
|
213
|
+
function setColumnAlign(state, key, align) {
|
|
214
|
+
return patchColumnState(state, key, { align });
|
|
215
|
+
}
|
|
216
|
+
function setColumnLabelOverride(state, key, labelOverride) {
|
|
217
|
+
const trimmed = labelOverride?.trim();
|
|
218
|
+
return patchColumnState(state, key, {
|
|
219
|
+
labelOverride: trimmed && trimmed.length > 0 ? trimmed : void 0
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
function arrayMove(arr, from, to) {
|
|
223
|
+
const next = arr.slice();
|
|
224
|
+
const [moved] = next.splice(from, 1);
|
|
225
|
+
if (moved === void 0) return next;
|
|
226
|
+
next.splice(to, 0, moved);
|
|
227
|
+
return next;
|
|
228
|
+
}
|
|
229
|
+
function moveColumn(state, activeKey, overKey) {
|
|
230
|
+
if (activeKey === overKey) return state;
|
|
231
|
+
const ordered = [...state].sort((a, b) => a.order - b.order);
|
|
232
|
+
const from = ordered.findIndex((s) => s.key === activeKey);
|
|
233
|
+
const to = ordered.findIndex((s) => s.key === overKey);
|
|
234
|
+
if (from === -1 || to === -1) return state;
|
|
235
|
+
const moved = arrayMove(ordered, from, to);
|
|
236
|
+
const orderByKey = /* @__PURE__ */ new Map();
|
|
237
|
+
moved.forEach((s, i) => orderByKey.set(s.key, i));
|
|
238
|
+
return state.map((s) => ({ ...s, order: orderByKey.get(s.key) ?? s.order }));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/derive/sort.ts
|
|
242
|
+
function toComparable(value, type) {
|
|
243
|
+
if (value === null || value === void 0)
|
|
244
|
+
return type === "number" ? Number.NEGATIVE_INFINITY : "";
|
|
245
|
+
if (type === "number") {
|
|
246
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
247
|
+
return Number.isNaN(n) ? Number.NEGATIVE_INFINITY : n;
|
|
248
|
+
}
|
|
249
|
+
if (type === "date") {
|
|
250
|
+
const t = value instanceof Date ? value.getTime() : new Date(String(value)).getTime();
|
|
251
|
+
return Number.isNaN(t) ? Number.NEGATIVE_INFINITY : t;
|
|
252
|
+
}
|
|
253
|
+
if (type === "boolean") return value ? 1 : 0;
|
|
254
|
+
return String(value).toLocaleLowerCase();
|
|
255
|
+
}
|
|
256
|
+
function compare(a, b, type) {
|
|
257
|
+
const ca = toComparable(a, type);
|
|
258
|
+
const cb = toComparable(b, type);
|
|
259
|
+
if (typeof ca === "number" && typeof cb === "number") return ca - cb;
|
|
260
|
+
return String(ca).localeCompare(String(cb));
|
|
261
|
+
}
|
|
262
|
+
function sortRows(data, rules, getSort) {
|
|
263
|
+
if (rules.length === 0) return data;
|
|
264
|
+
return [...data].sort((ra, rb) => {
|
|
265
|
+
for (const rule of rules) {
|
|
266
|
+
const cfg = getSort(rule.columnKey);
|
|
267
|
+
let diff;
|
|
268
|
+
if (cfg?.comparator) {
|
|
269
|
+
diff = cfg.comparator(ra, rb);
|
|
270
|
+
} else {
|
|
271
|
+
const key = rule.columnKey;
|
|
272
|
+
const av = cfg?.accessor ? cfg.accessor(ra) : ra[key];
|
|
273
|
+
const bv = cfg?.accessor ? cfg.accessor(rb) : rb[key];
|
|
274
|
+
diff = compare(av, bv, cfg?.type);
|
|
275
|
+
}
|
|
276
|
+
if (diff !== 0) return rule.direction === "asc" ? diff : -diff;
|
|
277
|
+
}
|
|
278
|
+
return 0;
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
function toggleSort(rules, columnKey, multi = false) {
|
|
282
|
+
const existing = rules.find((r) => r.columnKey === columnKey);
|
|
283
|
+
const others = multi ? rules.filter((r) => r.columnKey !== columnKey) : [];
|
|
284
|
+
if (!existing) return [...others, { columnKey, direction: "asc" }];
|
|
285
|
+
if (existing.direction === "asc") return [...others, { columnKey, direction: "desc" }];
|
|
286
|
+
return others;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// src/derive/filter.ts
|
|
290
|
+
function num(value) {
|
|
291
|
+
if (typeof value === "number") return value;
|
|
292
|
+
if (value instanceof Date) return value.getTime();
|
|
293
|
+
const n = Number(value);
|
|
294
|
+
return Number.isNaN(n) ? Number.NaN : n;
|
|
295
|
+
}
|
|
296
|
+
function text(value) {
|
|
297
|
+
return value === null || value === void 0 ? "" : String(value).toLocaleLowerCase();
|
|
298
|
+
}
|
|
299
|
+
function matchesFilter(value, filter, type) {
|
|
300
|
+
const { operator, value: target, value2 } = filter;
|
|
301
|
+
switch (operator) {
|
|
302
|
+
case "eq":
|
|
303
|
+
return type === "number" ? num(value) === num(target) : text(value) === text(target);
|
|
304
|
+
case "neq":
|
|
305
|
+
return type === "number" ? num(value) !== num(target) : text(value) !== text(target);
|
|
306
|
+
case "gt":
|
|
307
|
+
return num(value) > num(target);
|
|
308
|
+
case "lt":
|
|
309
|
+
return num(value) < num(target);
|
|
310
|
+
case "gte":
|
|
311
|
+
return num(value) >= num(target);
|
|
312
|
+
case "lte":
|
|
313
|
+
return num(value) <= num(target);
|
|
314
|
+
case "contains":
|
|
315
|
+
return text(value).includes(text(target));
|
|
316
|
+
case "between": {
|
|
317
|
+
const v = num(value);
|
|
318
|
+
return v >= num(target) && v <= num(value2);
|
|
319
|
+
}
|
|
320
|
+
case "in":
|
|
321
|
+
return Array.isArray(target) && target.some((t) => text(value) === text(t));
|
|
322
|
+
default:
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function applyFilters(data, filters, getType) {
|
|
327
|
+
if (filters.length === 0) return data;
|
|
328
|
+
return data.filter(
|
|
329
|
+
(row) => filters.every((f) => matchesFilter(row[f.columnKey], f, getType(f.columnKey)))
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/derive/search.ts
|
|
334
|
+
function applySearch(data, search, allKeys) {
|
|
335
|
+
const query = search.query.trim().toLocaleLowerCase();
|
|
336
|
+
if (query.length === 0) return data;
|
|
337
|
+
const keys = search.scope === "all" ? allKeys : search.scope;
|
|
338
|
+
return data.filter(
|
|
339
|
+
(row) => keys.some((key) => {
|
|
340
|
+
const value = row[key];
|
|
341
|
+
if (value === null || value === void 0) return false;
|
|
342
|
+
return String(value).toLocaleLowerCase().includes(query);
|
|
343
|
+
})
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// src/derive/paginate.ts
|
|
348
|
+
function getPageCount(total, pageSize) {
|
|
349
|
+
if (pageSize <= 0) return 1;
|
|
350
|
+
return Math.max(1, Math.ceil(total / pageSize));
|
|
351
|
+
}
|
|
352
|
+
function clampPage(page, total, pageSize) {
|
|
353
|
+
const count = getPageCount(total, pageSize);
|
|
354
|
+
return Math.min(Math.max(1, Math.floor(page)), count);
|
|
355
|
+
}
|
|
356
|
+
function paginate(data, page, pageSize) {
|
|
357
|
+
if (pageSize <= 0) {
|
|
358
|
+
return { items: data, offset: 0, page: 1, pageCount: 1 };
|
|
359
|
+
}
|
|
360
|
+
const safePage = clampPage(page, data.length, pageSize);
|
|
361
|
+
const offset = (safePage - 1) * pageSize;
|
|
362
|
+
return {
|
|
363
|
+
items: data.slice(offset, offset + pageSize),
|
|
364
|
+
offset,
|
|
365
|
+
page: safePage,
|
|
366
|
+
pageCount: getPageCount(data.length, pageSize)
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// src/derive/pivot.ts
|
|
371
|
+
function newAcc() {
|
|
372
|
+
return { sum: 0, n: 0, rows: 0, min: Number.POSITIVE_INFINITY, max: Number.NEGATIVE_INFINITY };
|
|
373
|
+
}
|
|
374
|
+
function add(acc, value) {
|
|
375
|
+
acc.rows += 1;
|
|
376
|
+
const num2 = typeof value === "number" ? value : Number(value);
|
|
377
|
+
if (!Number.isNaN(num2) && value !== null && value !== void 0 && value !== "") {
|
|
378
|
+
acc.sum += num2;
|
|
379
|
+
acc.n += 1;
|
|
380
|
+
if (num2 < acc.min) acc.min = num2;
|
|
381
|
+
if (num2 > acc.max) acc.max = num2;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function finalize(acc, agg) {
|
|
385
|
+
if (!acc) return null;
|
|
386
|
+
if (agg === "count") return acc.rows;
|
|
387
|
+
if (acc.n === 0) return null;
|
|
388
|
+
switch (agg) {
|
|
389
|
+
case "sum":
|
|
390
|
+
return acc.sum;
|
|
391
|
+
case "avg":
|
|
392
|
+
return acc.sum / acc.n;
|
|
393
|
+
case "min":
|
|
394
|
+
return acc.min;
|
|
395
|
+
case "max":
|
|
396
|
+
return acc.max;
|
|
397
|
+
default:
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function pathOf(row, fields) {
|
|
402
|
+
return fields.map((f) => {
|
|
403
|
+
const v = row[f];
|
|
404
|
+
return v === null || v === void 0 ? "" : String(v);
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
function pivot(data, config) {
|
|
408
|
+
const { rows: rowFields, columns: colFields, value, aggregate } = config;
|
|
409
|
+
const cells = /* @__PURE__ */ new Map();
|
|
410
|
+
const rowAcc = /* @__PURE__ */ new Map();
|
|
411
|
+
const colAcc = /* @__PURE__ */ new Map();
|
|
412
|
+
const grand = newAcc();
|
|
413
|
+
const rowKeyPaths = /* @__PURE__ */ new Map();
|
|
414
|
+
const colKeyPaths = /* @__PURE__ */ new Map();
|
|
415
|
+
for (const row of data) {
|
|
416
|
+
const rPath = pathOf(row, rowFields);
|
|
417
|
+
const cPath = pathOf(row, colFields);
|
|
418
|
+
const rKey = rPath.join("\0");
|
|
419
|
+
const cKey = cPath.join("\0");
|
|
420
|
+
rowKeyPaths.set(rKey, rPath);
|
|
421
|
+
colKeyPaths.set(cKey, cPath);
|
|
422
|
+
const v = row[value];
|
|
423
|
+
let byCol = cells.get(rKey);
|
|
424
|
+
if (!byCol) {
|
|
425
|
+
byCol = /* @__PURE__ */ new Map();
|
|
426
|
+
cells.set(rKey, byCol);
|
|
427
|
+
}
|
|
428
|
+
let acc = byCol.get(cKey);
|
|
429
|
+
if (!acc) {
|
|
430
|
+
acc = newAcc();
|
|
431
|
+
byCol.set(cKey, acc);
|
|
432
|
+
}
|
|
433
|
+
add(acc, v);
|
|
434
|
+
let ra = rowAcc.get(rKey);
|
|
435
|
+
if (!ra) {
|
|
436
|
+
ra = newAcc();
|
|
437
|
+
rowAcc.set(rKey, ra);
|
|
438
|
+
}
|
|
439
|
+
add(ra, v);
|
|
440
|
+
let ca = colAcc.get(cKey);
|
|
441
|
+
if (!ca) {
|
|
442
|
+
ca = newAcc();
|
|
443
|
+
colAcc.set(cKey, ca);
|
|
444
|
+
}
|
|
445
|
+
add(ca, v);
|
|
446
|
+
add(grand, v);
|
|
447
|
+
}
|
|
448
|
+
const byPath = (a, b) => a.join("\0").localeCompare(b.join("\0"));
|
|
449
|
+
const rowKeys = [...rowKeyPaths.values()].sort(byPath);
|
|
450
|
+
const columnKeys = [...colKeyPaths.values()].sort(byPath);
|
|
451
|
+
const values = rowKeys.map((rPath) => {
|
|
452
|
+
const byCol = cells.get(rPath.join("\0"));
|
|
453
|
+
return columnKeys.map((cPath) => finalize(byCol?.get(cPath.join("\0")), aggregate));
|
|
454
|
+
});
|
|
455
|
+
const rowTotals = rowKeys.map((rPath) => finalize(rowAcc.get(rPath.join("\0")), aggregate));
|
|
456
|
+
const columnTotals = columnKeys.map((cPath) => finalize(colAcc.get(cPath.join("\0")), aggregate));
|
|
457
|
+
return {
|
|
458
|
+
rowFields,
|
|
459
|
+
columnFields: colFields,
|
|
460
|
+
value,
|
|
461
|
+
aggregate,
|
|
462
|
+
rowKeys,
|
|
463
|
+
columnKeys,
|
|
464
|
+
values,
|
|
465
|
+
rowTotals,
|
|
466
|
+
columnTotals,
|
|
467
|
+
grandTotal: finalize(grand, aggregate)
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// src/derive/rows.ts
|
|
472
|
+
function buildRows(data, options = {}) {
|
|
473
|
+
const { getRowId, offset = 0 } = options;
|
|
474
|
+
return data.map((item, i) => {
|
|
475
|
+
const displayIndex = i + 1;
|
|
476
|
+
const globalIndex = offset + i + 1;
|
|
477
|
+
return {
|
|
478
|
+
rowId: getRowId ? getRowId(item) : String(globalIndex),
|
|
479
|
+
displayIndex,
|
|
480
|
+
globalIndex,
|
|
481
|
+
data: item
|
|
482
|
+
};
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// src/export/csv.ts
|
|
487
|
+
function escapeCell(value, delimiter) {
|
|
488
|
+
const s = value == null ? "" : String(value);
|
|
489
|
+
if (s.includes(delimiter) || s.includes('"') || s.includes("\n") || s.includes("\r")) {
|
|
490
|
+
return `"${s.replace(/"/g, '""')}"`;
|
|
491
|
+
}
|
|
492
|
+
return s;
|
|
493
|
+
}
|
|
494
|
+
function toCSV(table, options = {}) {
|
|
495
|
+
const delimiter = options.delimiter ?? ",";
|
|
496
|
+
const lines = [table.headers.map((h) => escapeCell(h, delimiter)).join(delimiter)];
|
|
497
|
+
for (const row of table.rows) {
|
|
498
|
+
lines.push(row.map((c) => escapeCell(c, delimiter)).join(delimiter));
|
|
499
|
+
}
|
|
500
|
+
return lines.join("\r\n");
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// src/export/json.ts
|
|
504
|
+
function toJSON(table, options = {}) {
|
|
505
|
+
const records = table.rows.map((row) => {
|
|
506
|
+
const obj = {};
|
|
507
|
+
table.headers.forEach((h, i) => {
|
|
508
|
+
obj[h] = row[i] ?? null;
|
|
509
|
+
});
|
|
510
|
+
return obj;
|
|
511
|
+
});
|
|
512
|
+
return JSON.stringify(records, null, options.pretty === false ? void 0 : 2);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// src/export/xml.ts
|
|
516
|
+
function escapeXml(value) {
|
|
517
|
+
const s = value == null ? "" : String(value);
|
|
518
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
519
|
+
}
|
|
520
|
+
function toTagName(header, index) {
|
|
521
|
+
let name = header.replace(/[^A-Za-z0-9_.-]/g, "_");
|
|
522
|
+
if (!name || !/^[A-Za-z_]/.test(name)) name = `_${name}`;
|
|
523
|
+
return name || `col${index}`;
|
|
524
|
+
}
|
|
525
|
+
function toXML(table, options = {}) {
|
|
526
|
+
const root = options.rootTag ?? "rows";
|
|
527
|
+
const rowTag = options.rowTag ?? "row";
|
|
528
|
+
const tags = table.headers.map((h, i) => toTagName(h, i));
|
|
529
|
+
const lines = ['<?xml version="1.0" encoding="UTF-8"?>', `<${root}>`];
|
|
530
|
+
for (const row of table.rows) {
|
|
531
|
+
lines.push(` <${rowTag}>`);
|
|
532
|
+
row.forEach((cell, i) => {
|
|
533
|
+
const tag = tags[i] ?? `col${i}`;
|
|
534
|
+
lines.push(` <${tag}>${escapeXml(cell)}</${tag}>`);
|
|
535
|
+
});
|
|
536
|
+
lines.push(` </${rowTag}>`);
|
|
537
|
+
}
|
|
538
|
+
lines.push(`</${root}>`);
|
|
539
|
+
return lines.join("\n");
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// src/export/zip.ts
|
|
543
|
+
var CRC_TABLE = (() => {
|
|
544
|
+
const table = new Uint32Array(256);
|
|
545
|
+
for (let n = 0; n < 256; n++) {
|
|
546
|
+
let c = n;
|
|
547
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
548
|
+
table[n] = c >>> 0;
|
|
549
|
+
}
|
|
550
|
+
return table;
|
|
551
|
+
})();
|
|
552
|
+
function crc32(bytes) {
|
|
553
|
+
let c = 4294967295;
|
|
554
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
555
|
+
const byte = bytes[i] ?? 0;
|
|
556
|
+
const idx = (c ^ byte) & 255;
|
|
557
|
+
c = (CRC_TABLE[idx] ?? 0) ^ c >>> 8;
|
|
558
|
+
}
|
|
559
|
+
return (c ^ 4294967295) >>> 0;
|
|
560
|
+
}
|
|
561
|
+
function utf8Bytes(str) {
|
|
562
|
+
const out = [];
|
|
563
|
+
for (let i = 0; i < str.length; i++) {
|
|
564
|
+
let code = str.charCodeAt(i);
|
|
565
|
+
if (code >= 55296 && code <= 56319 && i + 1 < str.length) {
|
|
566
|
+
const next = str.charCodeAt(i + 1);
|
|
567
|
+
if (next >= 56320 && next <= 57343) {
|
|
568
|
+
code = (code - 55296 << 10) + (next - 56320) + 65536;
|
|
569
|
+
i++;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
if (code < 128) {
|
|
573
|
+
out.push(code);
|
|
574
|
+
} else if (code < 2048) {
|
|
575
|
+
out.push(192 | code >> 6, 128 | code & 63);
|
|
576
|
+
} else if (code < 65536) {
|
|
577
|
+
out.push(224 | code >> 12, 128 | code >> 6 & 63, 128 | code & 63);
|
|
578
|
+
} else {
|
|
579
|
+
out.push(
|
|
580
|
+
240 | code >> 18,
|
|
581
|
+
128 | code >> 12 & 63,
|
|
582
|
+
128 | code >> 6 & 63,
|
|
583
|
+
128 | code & 63
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
return Uint8Array.from(out);
|
|
588
|
+
}
|
|
589
|
+
function zipStore(entries) {
|
|
590
|
+
const prepped = entries.map((e) => {
|
|
591
|
+
const name = utf8Bytes(e.name);
|
|
592
|
+
return { name, data: e.data, crc: crc32(e.data), size: e.data.length };
|
|
593
|
+
});
|
|
594
|
+
let total = 22;
|
|
595
|
+
for (const p of prepped) total += 30 + p.name.length + p.size + 46 + p.name.length;
|
|
596
|
+
const buf = new Uint8Array(total);
|
|
597
|
+
const dv = new DataView(buf.buffer);
|
|
598
|
+
let pos = 0;
|
|
599
|
+
const u16 = (v) => {
|
|
600
|
+
dv.setUint16(pos, v & 65535, true);
|
|
601
|
+
pos += 2;
|
|
602
|
+
};
|
|
603
|
+
const u32 = (v) => {
|
|
604
|
+
dv.setUint32(pos, v >>> 0, true);
|
|
605
|
+
pos += 4;
|
|
606
|
+
};
|
|
607
|
+
const write = (b) => {
|
|
608
|
+
buf.set(b, pos);
|
|
609
|
+
pos += b.length;
|
|
610
|
+
};
|
|
611
|
+
const localOffsets = [];
|
|
612
|
+
for (const p of prepped) {
|
|
613
|
+
localOffsets.push(pos);
|
|
614
|
+
u32(67324752);
|
|
615
|
+
u16(20);
|
|
616
|
+
u16(0);
|
|
617
|
+
u16(0);
|
|
618
|
+
u16(0);
|
|
619
|
+
u16(0);
|
|
620
|
+
u32(p.crc);
|
|
621
|
+
u32(p.size);
|
|
622
|
+
u32(p.size);
|
|
623
|
+
u16(p.name.length);
|
|
624
|
+
u16(0);
|
|
625
|
+
write(p.name);
|
|
626
|
+
write(p.data);
|
|
627
|
+
}
|
|
628
|
+
const cdStart = pos;
|
|
629
|
+
prepped.forEach((p, i) => {
|
|
630
|
+
u32(33639248);
|
|
631
|
+
u16(20);
|
|
632
|
+
u16(20);
|
|
633
|
+
u16(0);
|
|
634
|
+
u16(0);
|
|
635
|
+
u16(0);
|
|
636
|
+
u16(0);
|
|
637
|
+
u32(p.crc);
|
|
638
|
+
u32(p.size);
|
|
639
|
+
u32(p.size);
|
|
640
|
+
u16(p.name.length);
|
|
641
|
+
u16(0);
|
|
642
|
+
u16(0);
|
|
643
|
+
u16(0);
|
|
644
|
+
u16(0);
|
|
645
|
+
u32(0);
|
|
646
|
+
u32(localOffsets[i] ?? 0);
|
|
647
|
+
write(p.name);
|
|
648
|
+
});
|
|
649
|
+
const cdSize = pos - cdStart;
|
|
650
|
+
u32(101010256);
|
|
651
|
+
u16(0);
|
|
652
|
+
u16(0);
|
|
653
|
+
u16(prepped.length);
|
|
654
|
+
u16(prepped.length);
|
|
655
|
+
u32(cdSize);
|
|
656
|
+
u32(cdStart);
|
|
657
|
+
u16(0);
|
|
658
|
+
return buf;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// src/export/xlsx.ts
|
|
662
|
+
function colLetter(index) {
|
|
663
|
+
let s = "";
|
|
664
|
+
let i = index + 1;
|
|
665
|
+
while (i > 0) {
|
|
666
|
+
const rem = (i - 1) % 26;
|
|
667
|
+
s = String.fromCharCode(65 + rem) + s;
|
|
668
|
+
i = Math.floor((i - 1) / 26);
|
|
669
|
+
}
|
|
670
|
+
return s;
|
|
671
|
+
}
|
|
672
|
+
function cellXml(ref, value) {
|
|
673
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
674
|
+
return `<c r="${ref}"><v>${value}</v></c>`;
|
|
675
|
+
}
|
|
676
|
+
const s = value == null ? "" : String(value);
|
|
677
|
+
return `<c r="${ref}" t="inlineStr"><is><t xml:space="preserve">${escapeXml(s)}</t></is></c>`;
|
|
678
|
+
}
|
|
679
|
+
function safeSheetName(name) {
|
|
680
|
+
return (name.replace(/[[\]:*?/\\]/g, " ").trim() || "Sheet1").slice(0, 31);
|
|
681
|
+
}
|
|
682
|
+
var CONTENT_TYPES = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
683
|
+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/></Types>`;
|
|
684
|
+
var ROOT_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
685
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>`;
|
|
686
|
+
var WORKBOOK_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
687
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/></Relationships>`;
|
|
688
|
+
function toXLSX(table, options = {}) {
|
|
689
|
+
const sheetName = safeSheetName(options.sheetName ?? "Sheet1");
|
|
690
|
+
const matrix = [table.headers, ...table.rows];
|
|
691
|
+
const rowsXml = matrix.map((row, r) => {
|
|
692
|
+
const cells = row.map((v, c) => cellXml(`${colLetter(c)}${r + 1}`, v)).join("");
|
|
693
|
+
return `<row r="${r + 1}">${cells}</row>`;
|
|
694
|
+
}).join("");
|
|
695
|
+
const sheet = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
696
|
+
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData>${rowsXml}</sheetData></worksheet>`;
|
|
697
|
+
const workbook = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
698
|
+
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets><sheet name="${escapeXml(sheetName)}" sheetId="1" r:id="rId1"/></sheets></workbook>`;
|
|
699
|
+
return zipStore([
|
|
700
|
+
{ name: "[Content_Types].xml", data: utf8Bytes(CONTENT_TYPES) },
|
|
701
|
+
{ name: "_rels/.rels", data: utf8Bytes(ROOT_RELS) },
|
|
702
|
+
{ name: "xl/workbook.xml", data: utf8Bytes(workbook) },
|
|
703
|
+
{ name: "xl/_rels/workbook.xml.rels", data: utf8Bytes(WORKBOOK_RELS) },
|
|
704
|
+
{ name: "xl/worksheets/sheet1.xml", data: utf8Bytes(sheet) }
|
|
705
|
+
]);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// src/export/index.ts
|
|
709
|
+
var EXPORT_FORMAT_META = {
|
|
710
|
+
csv: { mime: "text/csv;charset=utf-8", ext: "csv" },
|
|
711
|
+
json: { mime: "application/json;charset=utf-8", ext: "json" },
|
|
712
|
+
xml: { mime: "application/xml;charset=utf-8", ext: "xml" },
|
|
713
|
+
xlsx: {
|
|
714
|
+
mime: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
715
|
+
ext: "xlsx"
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
function serializeExport(table, format) {
|
|
719
|
+
switch (format) {
|
|
720
|
+
case "csv":
|
|
721
|
+
return toCSV(table);
|
|
722
|
+
case "json":
|
|
723
|
+
return toJSON(table);
|
|
724
|
+
case "xml":
|
|
725
|
+
return toXML(table);
|
|
726
|
+
case "xlsx":
|
|
727
|
+
return toXLSX(table);
|
|
728
|
+
default:
|
|
729
|
+
return toCSV(table);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// src/index.ts
|
|
734
|
+
var VERSION = "0.0.0";
|
|
735
|
+
|
|
736
|
+
exports.EXPORT_FORMAT_META = EXPORT_FORMAT_META;
|
|
737
|
+
exports.VERSION = VERSION;
|
|
738
|
+
exports.adaptArray = adaptArray;
|
|
739
|
+
exports.adaptDynamicSchema = adaptDynamicSchema;
|
|
740
|
+
exports.adaptPaginated = adaptPaginated;
|
|
741
|
+
exports.applyFilters = applyFilters;
|
|
742
|
+
exports.applySearch = applySearch;
|
|
743
|
+
exports.buildRows = buildRows;
|
|
744
|
+
exports.clampPage = clampPage;
|
|
745
|
+
exports.createInitialColumnState = createInitialColumnState;
|
|
746
|
+
exports.escapeXml = escapeXml;
|
|
747
|
+
exports.getPageCount = getPageCount;
|
|
748
|
+
exports.matchesFilter = matchesFilter;
|
|
749
|
+
exports.moveColumn = moveColumn;
|
|
750
|
+
exports.paginate = paginate;
|
|
751
|
+
exports.patchColumnState = patchColumnState;
|
|
752
|
+
exports.pivot = pivot;
|
|
753
|
+
exports.reconcileColumnState = reconcileColumnState;
|
|
754
|
+
exports.resolveColumns = resolveColumns;
|
|
755
|
+
exports.serializeExport = serializeExport;
|
|
756
|
+
exports.setColumnAlign = setColumnAlign;
|
|
757
|
+
exports.setColumnLabelOverride = setColumnLabelOverride;
|
|
758
|
+
exports.setColumnPinned = setColumnPinned;
|
|
759
|
+
exports.setColumnVisible = setColumnVisible;
|
|
760
|
+
exports.setColumnWidth = setColumnWidth;
|
|
761
|
+
exports.sortRows = sortRows;
|
|
762
|
+
exports.toCSV = toCSV;
|
|
763
|
+
exports.toJSON = toJSON;
|
|
764
|
+
exports.toXLSX = toXLSX;
|
|
765
|
+
exports.toXML = toXML;
|
|
766
|
+
exports.toggleSort = toggleSort;
|
|
767
|
+
exports.zipStore = zipStore;
|
|
768
|
+
//# sourceMappingURL=index.cjs.map
|
|
769
|
+
//# sourceMappingURL=index.cjs.map
|