@vuu-ui/vuu-data-local 3.0.0 → 3.1.0-beta.2

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.
Files changed (46) hide show
  1. package/package.json +17 -13
  2. package/src/array-data-source/aggregate-utils.js +121 -0
  3. package/src/array-data-source/array-data-source.js +689 -0
  4. package/src/array-data-source/array-data-utils.js +41 -0
  5. package/src/array-data-source/group-utils.js +138 -0
  6. package/src/array-data-source/sort-utils.js +56 -0
  7. package/src/index.js +3 -0
  8. package/src/json-data-source/JsonDataSource.js +319 -0
  9. package/src/tree-data-source/IconProvider.js +11 -0
  10. package/src/tree-data-source/TreeDataSource.js +353 -0
  11. package/cjs/array-data-source/aggregate-utils.js +0 -237
  12. package/cjs/array-data-source/aggregate-utils.js.map +0 -1
  13. package/cjs/array-data-source/array-data-source.js +0 -944
  14. package/cjs/array-data-source/array-data-source.js.map +0 -1
  15. package/cjs/array-data-source/array-data-utils.js +0 -62
  16. package/cjs/array-data-source/array-data-utils.js.map +0 -1
  17. package/cjs/array-data-source/group-utils.js +0 -187
  18. package/cjs/array-data-source/group-utils.js.map +0 -1
  19. package/cjs/array-data-source/sort-utils.js +0 -73
  20. package/cjs/array-data-source/sort-utils.js.map +0 -1
  21. package/cjs/index.js +0 -12
  22. package/cjs/index.js.map +0 -1
  23. package/cjs/json-data-source/JsonDataSource.js +0 -404
  24. package/cjs/json-data-source/JsonDataSource.js.map +0 -1
  25. package/cjs/tree-data-source/IconProvider.js +0 -28
  26. package/cjs/tree-data-source/IconProvider.js.map +0 -1
  27. package/cjs/tree-data-source/TreeDataSource.js +0 -434
  28. package/cjs/tree-data-source/TreeDataSource.js.map +0 -1
  29. package/esm/array-data-source/aggregate-utils.js +0 -235
  30. package/esm/array-data-source/aggregate-utils.js.map +0 -1
  31. package/esm/array-data-source/array-data-source.js +0 -942
  32. package/esm/array-data-source/array-data-source.js.map +0 -1
  33. package/esm/array-data-source/array-data-utils.js +0 -59
  34. package/esm/array-data-source/array-data-utils.js.map +0 -1
  35. package/esm/array-data-source/group-utils.js +0 -183
  36. package/esm/array-data-source/group-utils.js.map +0 -1
  37. package/esm/array-data-source/sort-utils.js +0 -69
  38. package/esm/array-data-source/sort-utils.js.map +0 -1
  39. package/esm/index.js +0 -4
  40. package/esm/index.js.map +0 -1
  41. package/esm/json-data-source/JsonDataSource.js +0 -402
  42. package/esm/json-data-source/JsonDataSource.js.map +0 -1
  43. package/esm/tree-data-source/IconProvider.js +0 -26
  44. package/esm/tree-data-source/IconProvider.js.map +0 -1
  45. package/esm/tree-data-source/TreeDataSource.js +0 -432
  46. package/esm/tree-data-source/TreeDataSource.js.map +0 -1
@@ -0,0 +1,689 @@
1
+ import { filterPredicate, parseFilter } from "@vuu-ui/vuu-filter-parser";
2
+ import { EventEmitter, KeySet, NULL_RANGE, Range, buildColumnMap, combineFilters, filterAsQuery, getAddedItems, hasBaseFilter, hasFilter, hasGroupBy, hasSort, isConfigChanged, isGroupByChanged, logger, metadataKeys, rangeNewItems, toSchemaColumn, uuid, vanillaConfig, withConfigDefaults } from "@vuu-ui/vuu-utils";
3
+ import { aggregateData } from "./aggregate-utils.js";
4
+ import { buildDataToClientMap, toClientRow } from "./array-data-utils.js";
5
+ import { collapseGroup, expandGroup, groupRows } from "./group-utils.js";
6
+ import { binarySearch, sortComparator, sortRows } from "./sort-utils.js";
7
+ const { debug: debug, info: info } = logger("ArrayDataSource");
8
+ const { KEY: KEY } = metadataKeys;
9
+ const toDataSourceRow = (indexOfKeyColumn, index)=>(data, idx)=>{
10
+ const key = `${data[indexOfKeyColumn]}`;
11
+ index?.set(key, idx);
12
+ return [
13
+ idx,
14
+ idx,
15
+ true,
16
+ false,
17
+ 1,
18
+ 0,
19
+ key,
20
+ 0,
21
+ 0,
22
+ false,
23
+ ...data
24
+ ];
25
+ };
26
+ const buildTableSchema = (columns, keyColumn)=>{
27
+ const schema = {
28
+ columns: columns.map(toSchemaColumn),
29
+ key: keyColumn ?? columns[0].name,
30
+ table: {
31
+ module: "",
32
+ table: "Array"
33
+ }
34
+ };
35
+ return schema;
36
+ };
37
+ class ArrayDataSource extends EventEmitter {
38
+ clientCallback;
39
+ columnDescriptors;
40
+ dataIndices;
41
+ dataMap;
42
+ groupMap;
43
+ key;
44
+ lastRangeServed = {
45
+ from: 0,
46
+ to: 0
47
+ };
48
+ rangeChangeRowset;
49
+ openTreeNodes = [];
50
+ preserveScrollPositionAcrossConfigChange = false;
51
+ #columnMap;
52
+ _config = vanillaConfig;
53
+ #data;
54
+ #freezeTimestamp = void 0;
55
+ #keys = new KeySet(NULL_RANGE);
56
+ #links;
57
+ #maxRangeEnd = Number.MAX_SAFE_INTEGER;
58
+ #range = Range(0, 0);
59
+ #status = "initialising";
60
+ #title;
61
+ _menu;
62
+ selectedRows = new Set();
63
+ index = new Map();
64
+ tableSchema;
65
+ viewport;
66
+ processedData = void 0;
67
+ constructor({ aggregations, baseFilterSpec, columnDescriptors, data, dataMap, filterSpec, groupBy, keyColumn, rangeChangeRowset = "delta", sort, title, viewport }){
68
+ super();
69
+ if (!data || !columnDescriptors) throw Error("ArrayDataSource constructor called without data or without columnDescriptors");
70
+ this.columnDescriptors = columnDescriptors;
71
+ this.dataMap = dataMap;
72
+ this.key = keyColumn ? this.columnDescriptors.findIndex((col)=>col.name === keyColumn) : 0;
73
+ this.rangeChangeRowset = rangeChangeRowset;
74
+ this.tableSchema = buildTableSchema(columnDescriptors, keyColumn);
75
+ this.viewport = viewport || uuid();
76
+ this.#title = title;
77
+ const columns = columnDescriptors.map((col)=>col.name);
78
+ this.#columnMap = buildColumnMap(columns);
79
+ this.dataIndices = buildDataToClientMap(this.#columnMap, this.dataMap);
80
+ this.#data = data.map(toDataSourceRow(this.key, this.index));
81
+ this.config = {
82
+ ...this._config,
83
+ aggregations: aggregations || this._config.aggregations,
84
+ baseFilterSpec,
85
+ columns,
86
+ filterSpec: filterSpec || this._config.filterSpec,
87
+ groupBy: groupBy || this._config.groupBy,
88
+ sort: sort || this._config.sort
89
+ };
90
+ debug?.(`columnMap: ${JSON.stringify(this.#columnMap)}`);
91
+ }
92
+ async subscribe({ viewport = this.viewport ?? (this.viewport = uuid()), columns, aggregations, baseFilterSpec, range, sort, groupBy, filterSpec }, callback) {
93
+ this.clientCallback = callback;
94
+ this.viewport = viewport;
95
+ this.#status = "subscribed";
96
+ this.lastRangeServed = {
97
+ from: 0,
98
+ to: 0
99
+ };
100
+ if (this.tableSchema.rangeLimits) this.#maxRangeEnd = this.tableSchema.rangeLimits.maxRangeEnd;
101
+ let config = this._config;
102
+ const hasConfigProps = aggregations || columns || filterSpec || groupBy || sort;
103
+ if (hasConfigProps) config = {
104
+ ...config,
105
+ aggregations: aggregations || this._config.aggregations,
106
+ baseFilterSpec: baseFilterSpec || this._config.baseFilterSpec,
107
+ columns: columns || this._config.columns,
108
+ filterSpec: filterSpec || this._config.filterSpec,
109
+ groupBy: groupBy || this._config.groupBy,
110
+ sort: sort || this._config.sort
111
+ };
112
+ const subscribedMessage = {
113
+ ...config,
114
+ type: "subscribed",
115
+ clientViewportId: this.viewport,
116
+ range: this.#range,
117
+ tableSchema: this.tableSchema
118
+ };
119
+ this.clientCallback?.(subscribedMessage);
120
+ this.emit("subscribed", subscribedMessage);
121
+ if (hasConfigProps) this.config = config;
122
+ else {
123
+ this.sendSizeUpdateToClient();
124
+ this.emit("resize", this.processedData ? this.processedData.length : this.#data.length, this.#maxRangeEnd);
125
+ if (range && !this.#range.equals(range)) this.range = range;
126
+ else if (this.#range !== NULL_RANGE) this.sendRowsToClient();
127
+ if (0 !== this.range.to) {
128
+ const pageCount = Math.ceil(this.size / (this.range.to - this.range.from));
129
+ this.emit("page-count", pageCount);
130
+ }
131
+ }
132
+ }
133
+ unsubscribe() {
134
+ this.#status = "unsubscribed";
135
+ this.emit("unsubscribed", this.viewport);
136
+ this.removeAllListeners();
137
+ this.clientCallback = void 0;
138
+ }
139
+ suspend() {
140
+ console.log('[ArrayDataSource] suspend');
141
+ if ("unsubscribed" !== this.#status) {
142
+ info?.(`suspend #${this.viewport}, current status ${this.#status}`);
143
+ this.#status = "suspended";
144
+ this.emit("suspended", this.viewport);
145
+ }
146
+ }
147
+ resume(callback) {
148
+ const isSuspended = "suspended" === this.#status;
149
+ info?.(`resume #${this.viewport}, current status ${this.#status}`);
150
+ if (callback) this.clientCallback = callback;
151
+ if (isSuspended) this.#status = "subscribed";
152
+ this.emit("resumed", this.viewport);
153
+ if (this.selectedRows.size > 0) if (this.selectedRows.has("*")) this.emit("row-selection", this.size);
154
+ else this.emit("row-selection", this.selectedRows.size);
155
+ this.sendRowsToClient(true);
156
+ }
157
+ disable() {
158
+ this.emit("disabled", this.viewport);
159
+ }
160
+ enable() {
161
+ this.emit("enabled", this.viewport);
162
+ }
163
+ select(selectRequest) {
164
+ switch(selectRequest.type){
165
+ case "SELECT_ROW":
166
+ {
167
+ const { preserveExistingSelection, rowKey } = selectRequest;
168
+ if (!preserveExistingSelection) this.selectedRows.clear();
169
+ this.selectedRows.add(rowKey);
170
+ break;
171
+ }
172
+ case "DESELECT_ROW":
173
+ {
174
+ const { preserveExistingSelection, rowKey } = selectRequest;
175
+ if (preserveExistingSelection) if (this.selectedRows.has("*")) {
176
+ this.selectedRows.clear();
177
+ for (const key of this.index.keys())if (key !== rowKey) this.selectedRows.add(key);
178
+ } else this.selectedRows.delete(rowKey);
179
+ else this.selectedRows.clear();
180
+ break;
181
+ }
182
+ case "SELECT_ROW_RANGE":
183
+ {
184
+ const { preserveExistingSelection, fromRowKey, toRowKey } = selectRequest;
185
+ if (!preserveExistingSelection) this.selectedRows.clear();
186
+ const fromIdx = this.index.get(fromRowKey);
187
+ const toIdx = this.index.get(toRowKey);
188
+ if ("number" == typeof fromIdx && "number" == typeof toIdx) for(let i = fromIdx; i <= toIdx; i++){
189
+ const { [KEY]: rowKey } = this.#data[i];
190
+ this.selectedRows.add(rowKey);
191
+ }
192
+ break;
193
+ }
194
+ case "SELECT_ALL":
195
+ this.selectedRows.clear();
196
+ this.selectedRows.add("*");
197
+ break;
198
+ case "DESELECT_ALL":
199
+ this.selectedRows.clear();
200
+ break;
201
+ }
202
+ this.setRange(this.#range, true);
203
+ this.emit("row-selection", "SELECT_ALL" === selectRequest.type ? this.size : this.selectedRows.size);
204
+ }
205
+ getRowKey(keyOrIndex) {
206
+ if ("string" == typeof keyOrIndex) return keyOrIndex;
207
+ const row = this.getRowAtIndex(keyOrIndex);
208
+ if (void 0 === row) throw Error(`row not found at index ${keyOrIndex}`);
209
+ return row?.[KEY];
210
+ }
211
+ openTreeNode(keyOrIndex) {
212
+ const key = this.getRowKey(keyOrIndex);
213
+ this.openTreeNodes.push(key);
214
+ this.processedData = expandGroup(this.openTreeNodes, this.#data, this._config.groupBy, this.#columnMap, this.groupMap, this.processedData);
215
+ this.setRange(this.#range.reset, true);
216
+ }
217
+ closeTreeNode(keyOrIndex) {
218
+ const key = this.getRowKey(keyOrIndex);
219
+ this.openTreeNodes = this.openTreeNodes.filter((value)=>value !== key);
220
+ if (this.processedData) {
221
+ this.processedData = collapseGroup(key, this.processedData);
222
+ this.setRange(this.#range.reset, true);
223
+ }
224
+ }
225
+ get pageSize() {
226
+ return this.#range.to - this.#range.from;
227
+ }
228
+ get links() {
229
+ return this.#links;
230
+ }
231
+ set links(links) {
232
+ this.#links = links;
233
+ if (links) this._clientCallback?.({
234
+ clientViewportId: this.viewport,
235
+ type: "vuu-links",
236
+ links
237
+ });
238
+ }
239
+ get menu() {
240
+ return this._menu;
241
+ }
242
+ get status() {
243
+ return this.#status;
244
+ }
245
+ get data() {
246
+ return this.#data;
247
+ }
248
+ get currentData() {
249
+ return this.processedData ?? this.#data;
250
+ }
251
+ get table() {
252
+ return this.tableSchema.table;
253
+ }
254
+ get columns() {
255
+ return this._config.columns;
256
+ }
257
+ set columns(columns) {}
258
+ get config() {
259
+ return this._config;
260
+ }
261
+ set config(config) {
262
+ const originalConfig = this._config;
263
+ const configChanges = this.applyConfig(config);
264
+ if (configChanges) {
265
+ if (config) {
266
+ const newConfig = config?.filterSpec?.filter && config?.filterSpec.filterStruct === void 0 ? {
267
+ ...config,
268
+ filterSpec: {
269
+ filter: config.filterSpec.filter,
270
+ filterStruct: parseFilter(config.filterSpec.filter)
271
+ }
272
+ } : config;
273
+ this._config = withConfigDefaults(newConfig);
274
+ let processedData;
275
+ if (hasFilter(config) || hasBaseFilter(config)) {
276
+ const fn = this.getFilterPredicate();
277
+ processedData = this.#data.filter(fn);
278
+ }
279
+ if (configChanges.columnsChanged) this.processNewColumns(originalConfig.columns, config.columns);
280
+ if (hasSort(config)) processedData = sortRows(processedData ?? this.#data, config.sort, this.#columnMap);
281
+ if (this.openTreeNodes.length > 0 && isGroupByChanged(originalConfig, config)) {
282
+ if (0 === this._config.groupBy.length) this.openTreeNodes.length = 0;
283
+ }
284
+ if (hasGroupBy(config)) {
285
+ const [groupedData, groupMap] = groupRows(processedData ?? this.#data, config.groupBy, this.#columnMap);
286
+ this.groupMap = groupMap;
287
+ processedData = groupedData;
288
+ if (this.openTreeNodes.length > 0) processedData = expandGroup(this.openTreeNodes, this.#data, this._config.groupBy, this.#columnMap, this.groupMap, processedData);
289
+ }
290
+ if (processedData) this.processedData = this.indexProcessedData(processedData);
291
+ else this.processedData = void 0;
292
+ }
293
+ if (configChanges.filterChanged || configChanges.baseFilterChanged || configChanges.groupByChanged) requestAnimationFrame(()=>{
294
+ this.emit("resize", this.size);
295
+ });
296
+ if ("subscribed" === this.#status) requestAnimationFrame(()=>{
297
+ this.sendSizeUpdateToClient();
298
+ if (this.preserveScrollPositionAcrossConfigChange) this.preserveScrollPositionAcrossConfigChange = false;
299
+ else this.setRange(this.#range.reset, true);
300
+ this.emit("config", this._config, this.range, void 0, configChanges);
301
+ });
302
+ }
303
+ }
304
+ processNewColumns(originalColumns, columns) {
305
+ const addedColumns = getAddedItems(originalColumns, columns);
306
+ addedColumns.length;
307
+ this.#columnMap = buildColumnMap(columns);
308
+ this.dataIndices = buildDataToClientMap(this.#columnMap, this.dataMap);
309
+ }
310
+ indexProcessedData(data) {
311
+ return data?.map((row, i)=>{
312
+ const dolly = row.slice();
313
+ dolly[0] = i;
314
+ dolly[1] = i;
315
+ return dolly;
316
+ });
317
+ }
318
+ getFilterPredicate() {
319
+ const { filterSpec: { filterStruct } } = combineFilters(this._config);
320
+ if (filterStruct) return filterPredicate(this.#columnMap, filterStruct);
321
+ throw Error("filter must include filterStruct");
322
+ }
323
+ applyConfig(config, preserveExistingConfigAttributes = false) {
324
+ const { noChanges, ...otherChanges } = isConfigChanged(this._config, config);
325
+ if (true !== noChanges) {
326
+ if (config) {
327
+ const newConfig = config?.filterSpec?.filter && config?.filterSpec.filterStruct === void 0 ? {
328
+ ...config,
329
+ filterSpec: {
330
+ filter: config.filterSpec.filter,
331
+ filterStruct: parseFilter(config.filterSpec.filter)
332
+ }
333
+ } : config;
334
+ if (preserveExistingConfigAttributes) this._config = {
335
+ ...this._config,
336
+ ...config
337
+ };
338
+ else this._config = withConfigDefaults(newConfig);
339
+ return otherChanges;
340
+ }
341
+ }
342
+ }
343
+ get columnMap() {
344
+ return this.#columnMap;
345
+ }
346
+ get selectedRowsCount() {
347
+ return this.selectedRows.size;
348
+ }
349
+ get size() {
350
+ return this.processedData?.length ?? this.#data.length;
351
+ }
352
+ get maxRangeEnd() {
353
+ return this.#maxRangeEnd;
354
+ }
355
+ get range() {
356
+ return this.#range;
357
+ }
358
+ set range(range) {
359
+ this.setRange(range);
360
+ }
361
+ delete(row) {
362
+ console.log(`delete row ${row.join(",")}`);
363
+ }
364
+ insert = (row)=>{
365
+ const dataSourceRow = toDataSourceRow(this.key, this.index)(row, this.size);
366
+ this.#data.push(dataSourceRow);
367
+ const { from, to } = this.#range;
368
+ const [rowIdx] = dataSourceRow;
369
+ const isSorted = hasSort(this.config);
370
+ const isFiltered = hasFilter(this.config) || hasBaseFilter(this.config);
371
+ if (isSorted && isFiltered) {
372
+ const meetsFilterCriteria = this.getFilterPredicate();
373
+ if (meetsFilterCriteria(dataSourceRow)) this.insertIntoSortedData(dataSourceRow);
374
+ } else if (isSorted) this.insertIntoSortedData(dataSourceRow);
375
+ else if (isFiltered) {
376
+ const meetsFilterCriteria = this.getFilterPredicate();
377
+ if (meetsFilterCriteria(dataSourceRow)) {
378
+ this.processedData?.push(dataSourceRow);
379
+ this.sendSizeUpdateToClient();
380
+ if (rowIdx >= from && rowIdx < to) this.sendRowsToClient();
381
+ this.emit("resize", this.#data.length);
382
+ }
383
+ } else {
384
+ this.sendSizeUpdateToClient();
385
+ if (rowIdx >= from && rowIdx < to) this.sendRowsToClient();
386
+ this.emit("resize", this.size);
387
+ }
388
+ };
389
+ insertIntoSortedData(row) {
390
+ const indexedSortDefs = this.config.sort.sortDefs.map(({ column, sortType })=>[
391
+ this.columnMap[column],
392
+ sortType
393
+ ]);
394
+ if (this.processedData) {
395
+ const comparator = sortComparator(indexedSortDefs);
396
+ const insertPos = binarySearch(this.processedData, row, comparator);
397
+ this.sendSizeUpdateToClient();
398
+ if (-1 === insertPos) {
399
+ this.processedData?.unshift(row);
400
+ if (this.processedData) this.processedData = this.indexProcessedData(this.processedData);
401
+ if (insertPos <= this.#range.to) this.sendRowsToClient(true);
402
+ if (this.processedData) this.emit("resize", this.processedData.length);
403
+ } else if (this.processedData) this.emit("resize", this.processedData.length);
404
+ }
405
+ }
406
+ validateDataValue(columnName, value) {
407
+ const columnDescriptor = this.columnDescriptors.find((col)=>col.name === columnName);
408
+ if (columnDescriptor) switch(columnDescriptor.serverDataType){
409
+ case "int":
410
+ if ("number" == typeof value) {
411
+ if (Math.floor(value) !== value) throw Error(`${columnName} is int but value = ${value}`);
412
+ } else if ("string" == typeof value) {
413
+ const numericValue = parseFloat(value);
414
+ if (Math.floor(numericValue) !== numericValue) throw Error(`${columnName} is ${value} is not a valid integer`);
415
+ }
416
+ break;
417
+ default:
418
+ }
419
+ else throw Error(`Unknown column ${columnName}`);
420
+ }
421
+ updateDataItem = (keyValue, columnName, value)=>{
422
+ this.validateDataValue(columnName, value);
423
+ const colIndex = this.#columnMap[columnName];
424
+ const dataColIndex = this.dataMap?.[columnName];
425
+ const dataIndex = this.indexOfRowWithKey(keyValue);
426
+ if (-1 !== dataIndex && void 0 !== dataColIndex) {
427
+ const dataSourceRow = this.#data[dataIndex];
428
+ dataSourceRow[colIndex] = value;
429
+ const { from, to } = this.#range;
430
+ const [rowIdx] = dataSourceRow;
431
+ if (rowIdx >= from && rowIdx < to) this.sendRowsToClient(false, dataSourceRow);
432
+ }
433
+ };
434
+ getRowByKey(key) {
435
+ const data = this.processedData ?? this.#data;
436
+ return data.find((row)=>row[KEY] === key);
437
+ }
438
+ getRowAtIndex(rowIndex) {
439
+ return (this.processedData ?? this.#data)[rowIndex];
440
+ }
441
+ indexOfRowWithKey = (key)=>this.#data.findIndex((row)=>row[KEY] === key);
442
+ update = (row, columnName)=>{
443
+ const keyValue = row[this.key];
444
+ const dataColIndex = this.dataMap?.[columnName];
445
+ return this.updateDataItem(keyValue, columnName, row[dataColIndex]);
446
+ };
447
+ updateRow = (row, _columnName)=>{
448
+ const keyValue = row[this.key];
449
+ const dataIndex = this.#data.findIndex((row)=>row[KEY] === keyValue);
450
+ if (-1 !== dataIndex) {
451
+ const dataSourceRow = toDataSourceRow(this.key)(row, dataIndex);
452
+ this.#data[dataIndex] = dataSourceRow;
453
+ const { from, to } = this.#range;
454
+ const isFiltered = hasFilter(this.config) || hasBaseFilter(this.config);
455
+ const isSorted = hasSort(this.config);
456
+ if (isFiltered) {
457
+ const meetsFilterCriteria = this.getFilterPredicate();
458
+ if (meetsFilterCriteria(dataSourceRow) && this.processedData) {
459
+ const dataIndex = this.processedData.findIndex((row)=>row[KEY] === keyValue);
460
+ if (-1 !== dataIndex) {
461
+ const existingRow = this.processedData[dataIndex];
462
+ const newFilteredRow = existingRow.slice(0, 10).concat(dataSourceRow.slice(10));
463
+ this.processedData[dataIndex] = newFilteredRow;
464
+ if (dataIndex >= from && dataIndex < to) this.sendRowsToClient(false, newFilteredRow);
465
+ }
466
+ } else if (this.processedData) {
467
+ const dataIndex = this.processedData.findIndex((row)=>row[KEY] === keyValue);
468
+ if (-1 !== dataIndex) console.log('dataRow no longer in filter set');
469
+ }
470
+ } else if (isSorted) {
471
+ if (this.processedData) {
472
+ const dataIndex = this.processedData.findIndex((row)=>row[KEY] === keyValue);
473
+ if (-1 !== dataIndex) {
474
+ const existingRow = this.processedData[dataIndex];
475
+ const newSortedRow = existingRow.slice(0, 10).concat(dataSourceRow.slice(10));
476
+ this.processedData[dataIndex] = newSortedRow;
477
+ if (dataIndex >= from && dataIndex < to) this.sendRowsToClient(false, newSortedRow);
478
+ }
479
+ }
480
+ } else if (dataIndex >= from && dataIndex < to) this.sendRowsToClient(false, dataSourceRow);
481
+ }
482
+ };
483
+ handleDeleteFromTable = async (key)=>{
484
+ const dataIndex = this.#data.findIndex((row)=>row[KEY] === key);
485
+ if (-1 === dataIndex) return "row not found";
486
+ let doomedIndex;
487
+ if (this.processedData) {
488
+ for(let i = 0; i < this.processedData.length; i++)if (this.processedData[i][KEY] === key) doomedIndex = i;
489
+ else if (void 0 !== doomedIndex) this.processedData[i][0] -= 1;
490
+ if (void 0 !== doomedIndex) this.processedData.splice(doomedIndex, 1);
491
+ }
492
+ this.#data.splice(dataIndex, 1);
493
+ for(let i = dataIndex; i < this.#data.length; i++)this.#data[i][0] -= 1;
494
+ this.sendSizeUpdateToClient();
495
+ const { from, to } = this.#range;
496
+ const deletedIndex = doomedIndex ?? dataIndex;
497
+ if (deletedIndex >= from && deletedIndex < to) {
498
+ this.#keys.reset(this.rangeWithBufferWithinMaxRangeEnd);
499
+ this.sendRowsToClient(true);
500
+ }
501
+ this.emit("resize", this.size);
502
+ return true;
503
+ };
504
+ setRange(range, forceFullRefresh = false) {
505
+ this.constrainRangeToMaxRangeEnd(range);
506
+ if (range.from !== this.#range.from || range.to !== this.#range.to) {
507
+ const currentPageCount = Math.ceil(this.size / (this.#range.to - this.#range.from));
508
+ const newPageCount = Math.ceil(this.size / (range.to - range.from));
509
+ this.#range = range;
510
+ const keysResequenced = this.#keys.reset(this.rangeWithBufferWithinMaxRangeEnd);
511
+ this.sendRowsToClient(forceFullRefresh || keysResequenced);
512
+ requestAnimationFrame(()=>{
513
+ if (newPageCount !== currentPageCount) this.emit("page-count", newPageCount);
514
+ this.emit("range", range);
515
+ });
516
+ } else if (forceFullRefresh) this.sendRowsToClient(forceFullRefresh);
517
+ }
518
+ constrainRangeToMaxRangeEnd(range) {
519
+ if (this.#maxRangeEnd === Number.MAX_SAFE_INTEGER) return;
520
+ const pageSize = Math.max(0, range.to - range.from);
521
+ if (range.from >= this.#maxRangeEnd) {
522
+ range.from = Math.max(0, this.#maxRangeEnd - pageSize);
523
+ range.to = this.#maxRangeEnd;
524
+ } else if (range.to > this.#maxRangeEnd) range.to = this.#maxRangeEnd;
525
+ }
526
+ get rangeWithBufferWithinMaxRangeEnd() {
527
+ const withBuffer = this.#range.withBuffer;
528
+ return this.#maxRangeEnd === Number.MAX_SAFE_INTEGER ? withBuffer : {
529
+ from: withBuffer.from,
530
+ to: Math.min(withBuffer.to, this.#maxRangeEnd)
531
+ };
532
+ }
533
+ sendSizeUpdateToClient() {
534
+ this.clientCallback?.({
535
+ clientViewportId: this.viewport,
536
+ mode: "size-only",
537
+ type: "viewport-update",
538
+ size: this.processedData ? this.processedData.length : this.#data.length
539
+ });
540
+ }
541
+ sendRowsToClient(forceFullRefresh = false, row) {
542
+ if (row) this.clientCallback?.({
543
+ clientViewportId: this.viewport,
544
+ mode: "update",
545
+ rows: [
546
+ toClientRow(row, this.#keys, this.selectedRows, this.dataIndices)
547
+ ],
548
+ type: "viewport-update"
549
+ });
550
+ else {
551
+ const rowRange = "delta" !== this.rangeChangeRowset || forceFullRefresh ? this.rangeWithBufferWithinMaxRangeEnd : rangeNewItems(this.lastRangeServed, this.rangeWithBufferWithinMaxRangeEnd);
552
+ const data = this.processedData ?? this.#data;
553
+ const rowsWithinViewport = data.slice(rowRange.from, rowRange.to).map((row)=>toClientRow(row, this.#keys, this.selectedRows, this.dataIndices));
554
+ this.clientCallback?.({
555
+ clientViewportId: this.viewport,
556
+ mode: "batch",
557
+ range: this.#range,
558
+ rows: rowsWithinViewport,
559
+ size: data.length,
560
+ type: "viewport-update"
561
+ });
562
+ this.lastRangeServed = {
563
+ from: this.#range.from,
564
+ to: Math.min(this.#range.to, this.#range.from + rowsWithinViewport.length)
565
+ };
566
+ }
567
+ }
568
+ get aggregations() {
569
+ return this._config.aggregations;
570
+ }
571
+ set aggregations(aggregations) {
572
+ this._config = {
573
+ ...this._config,
574
+ aggregations
575
+ };
576
+ const targetData = this.processedData ?? this.#data;
577
+ const leafData = this.#data;
578
+ aggregateData(aggregations, targetData, this._config.groupBy, leafData, this.#columnMap, this.groupMap);
579
+ this.setRange(this.#range.reset, true);
580
+ this.emit("config", this._config, this.range);
581
+ }
582
+ get sort() {
583
+ return this._config.sort;
584
+ }
585
+ set sort(sort) {
586
+ debug?.(`sort ${JSON.stringify(sort)}`);
587
+ this.config = {
588
+ ...this._config,
589
+ sort
590
+ };
591
+ }
592
+ get baseFilter() {
593
+ return this._config.baseFilterSpec;
594
+ }
595
+ set baseFilter(baseFilter) {
596
+ debug?.(`baseFilter ${JSON.stringify(baseFilter)}`);
597
+ this.config = {
598
+ ...this._config,
599
+ baseFilterSpec: baseFilter
600
+ };
601
+ }
602
+ get filter() {
603
+ return this._config.filterSpec;
604
+ }
605
+ set filter(filter) {
606
+ debug?.(`filter ${JSON.stringify(filter)}`);
607
+ this.config = {
608
+ ...this._config,
609
+ filterSpec: filter
610
+ };
611
+ }
612
+ setFilter(filter) {
613
+ const dataSourceFilter = {
614
+ filter: filterAsQuery(filter),
615
+ filterStruct: filter
616
+ };
617
+ this.filter = dataSourceFilter;
618
+ }
619
+ clearFilter() {
620
+ this.filter = {
621
+ filter: ""
622
+ };
623
+ }
624
+ get groupBy() {
625
+ return this._config.groupBy;
626
+ }
627
+ set groupBy(groupBy) {
628
+ this.config = {
629
+ ...this._config,
630
+ groupBy
631
+ };
632
+ }
633
+ get title() {
634
+ return this.#title ?? `${this.table.module} ${this.table.table}`;
635
+ }
636
+ set title(title) {
637
+ this.#title = title;
638
+ this.emit("title-changed", this.viewport, title);
639
+ }
640
+ get _clientCallback() {
641
+ return this.clientCallback;
642
+ }
643
+ createLink({ parentVpId, link: { fromColumn, toColumn } }) {
644
+ console.log("create link", {
645
+ parentVpId,
646
+ fromColumn,
647
+ toColumn
648
+ });
649
+ }
650
+ removeLink() {
651
+ console.log("remove link");
652
+ }
653
+ async remoteProcedureCall() {
654
+ return Promise.reject();
655
+ }
656
+ async menuRpcCall(rpcRequest) {
657
+ console.log({
658
+ rpcRequest
659
+ });
660
+ return new Promise(()=>{});
661
+ }
662
+ freeze() {
663
+ if (this.isFrozen) throw Error("[BaseDataSource] cannot freeze, dataSource is already frozen");
664
+ this.#freezeTimestamp = Date.now();
665
+ this.emit("freeze", true, this.#freezeTimestamp);
666
+ this.preserveScrollPositionAcrossConfigChange = true;
667
+ this.baseFilter = {
668
+ filter: `vuuCreatedTimestamp < ${this.#freezeTimestamp}`
669
+ };
670
+ }
671
+ unfreeze() {
672
+ if (this.isFrozen) {
673
+ const freezeTimestamp = this.#freezeTimestamp;
674
+ this.#freezeTimestamp = void 0;
675
+ this.emit("freeze", false, freezeTimestamp);
676
+ this.preserveScrollPositionAcrossConfigChange = true;
677
+ this.baseFilter = {
678
+ filter: ""
679
+ };
680
+ } else throw Error("[BaseDataSource] cannot freeze, dataSource is already frozen");
681
+ }
682
+ get freezeTimestamp() {
683
+ return this.#freezeTimestamp;
684
+ }
685
+ get isFrozen() {
686
+ return "number" == typeof this.#freezeTimestamp;
687
+ }
688
+ }
689
+ export { ArrayDataSource };