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