@vuu-ui/vuu-data-local 2.1.19-beta.1 → 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.
- package/package.json +9 -10
- package/src/array-data-source/aggregate-utils.js +121 -0
- package/src/array-data-source/array-data-source.js +671 -0
- package/src/array-data-source/array-data-utils.js +41 -0
- package/src/array-data-source/group-utils.js +138 -0
- package/src/array-data-source/sort-utils.js +56 -0
- package/src/index.js +3 -0
- package/src/json-data-source/JsonDataSource.js +319 -0
- package/src/tree-data-source/IconProvider.js +11 -0
- package/src/tree-data-source/TreeDataSource.js +353 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { BaseDataSource, KeySet, NULL_RANGE, getParentRow, isSingleValueFilter, lastPathSegment, metadataKeys, missingAncestor, treeToDataSourceRows, uuid } from "@vuu-ui/vuu-utils";
|
|
2
|
+
import { IconProvider } from "./IconProvider.js";
|
|
3
|
+
import { parseFilter } from "@vuu-ui/vuu-filter-parser";
|
|
4
|
+
const TREE_SCHEMA = {
|
|
5
|
+
columns: [
|
|
6
|
+
{
|
|
7
|
+
name: "nodeData",
|
|
8
|
+
serverDataType: "string"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
name: "Level 1",
|
|
12
|
+
serverDataType: "string"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: "Level 2",
|
|
16
|
+
serverDataType: "string"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: "Level 3",
|
|
20
|
+
serverDataType: "string"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "Level 4",
|
|
24
|
+
serverDataType: "string"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: "Level 5",
|
|
28
|
+
serverDataType: "string"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
key: "",
|
|
32
|
+
table: {
|
|
33
|
+
module: "",
|
|
34
|
+
table: ""
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const { COUNT: COUNT, DEPTH: DEPTH, IDX: IDX, IS_EXPANDED: IS_EXPANDED, IS_LEAF: IS_LEAF, KEY: KEY, SELECTED: SELECTED } = metadataKeys;
|
|
38
|
+
const toClientRow = (row, keys)=>{
|
|
39
|
+
const [rowIndex] = row;
|
|
40
|
+
const clientRow = row.slice();
|
|
41
|
+
clientRow[1] = keys.keyFor(rowIndex);
|
|
42
|
+
return clientRow;
|
|
43
|
+
};
|
|
44
|
+
class TreeDataSource extends BaseDataSource {
|
|
45
|
+
columnDescriptors;
|
|
46
|
+
clientCallback;
|
|
47
|
+
expandedRows = new Set();
|
|
48
|
+
visibleRows = [];
|
|
49
|
+
visibleRowIndex = {};
|
|
50
|
+
#aggregations = [];
|
|
51
|
+
#data;
|
|
52
|
+
#iconProvider;
|
|
53
|
+
#selectedRowsCount = 0;
|
|
54
|
+
#size = 0;
|
|
55
|
+
#status = "initialising";
|
|
56
|
+
#filterSet;
|
|
57
|
+
rowCount;
|
|
58
|
+
keys = new KeySet(this._range);
|
|
59
|
+
constructor({ data, ...props }){
|
|
60
|
+
super(props);
|
|
61
|
+
if (!data) throw Error("TreeDataSource constructor called without data");
|
|
62
|
+
this.#iconProvider = new IconProvider();
|
|
63
|
+
[this.columnDescriptors, this.#data] = treeToDataSourceRows(data, this.#iconProvider);
|
|
64
|
+
console.table(this.#data);
|
|
65
|
+
if (this.columnDescriptors) {
|
|
66
|
+
const columns = this.columnDescriptors.map((c)=>c.name);
|
|
67
|
+
this._configWithVisualLink = {
|
|
68
|
+
...this._configWithVisualLink,
|
|
69
|
+
columns,
|
|
70
|
+
groupBy: columns.slice(1)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async subscribe({ aggregations, columns, range, revealSelected, selectedKeyValues, viewport = this.viewport ?? uuid() }, callback) {
|
|
75
|
+
this.clientCallback = callback;
|
|
76
|
+
if (aggregations) this.#aggregations = aggregations;
|
|
77
|
+
if (columns) this._configWithVisualLink = {
|
|
78
|
+
...this._configWithVisualLink,
|
|
79
|
+
columns
|
|
80
|
+
};
|
|
81
|
+
if ("initialising" !== this.#status) return;
|
|
82
|
+
this.viewport = viewport;
|
|
83
|
+
this.#status = "subscribed";
|
|
84
|
+
this.#selectedRowsCount = selectedKeyValues?.length ?? 0;
|
|
85
|
+
if (selectedKeyValues) this.applySelectedKeyValues(selectedKeyValues, revealSelected);
|
|
86
|
+
[this.visibleRows, this.visibleRowIndex] = getVisibleRows(this.#data, this.expandedRows);
|
|
87
|
+
this.clientCallback?.({
|
|
88
|
+
aggregations: this.#aggregations,
|
|
89
|
+
type: "subscribed",
|
|
90
|
+
clientViewportId: this.viewport,
|
|
91
|
+
columns: this.columns,
|
|
92
|
+
filterSpec: this.filter,
|
|
93
|
+
groupBy: this._configWithVisualLink.groupBy,
|
|
94
|
+
range: this.range,
|
|
95
|
+
sort: this.sort,
|
|
96
|
+
tableSchema: TREE_SCHEMA
|
|
97
|
+
});
|
|
98
|
+
this.clientCallback({
|
|
99
|
+
clientViewportId: this.viewport,
|
|
100
|
+
mode: "size-only",
|
|
101
|
+
type: "viewport-update",
|
|
102
|
+
size: this.visibleRows.length
|
|
103
|
+
});
|
|
104
|
+
if (range && !this._range.equals(range)) this.range = range;
|
|
105
|
+
else if (this._range !== NULL_RANGE) this.sendRowsToClient();
|
|
106
|
+
}
|
|
107
|
+
unsubscribe() {
|
|
108
|
+
console.log("noop");
|
|
109
|
+
}
|
|
110
|
+
suspend() {
|
|
111
|
+
console.log("noop");
|
|
112
|
+
return this;
|
|
113
|
+
}
|
|
114
|
+
resume() {
|
|
115
|
+
console.log("noop");
|
|
116
|
+
return this;
|
|
117
|
+
}
|
|
118
|
+
disable() {
|
|
119
|
+
console.log("noop");
|
|
120
|
+
return this;
|
|
121
|
+
}
|
|
122
|
+
enable() {
|
|
123
|
+
console.log("noop");
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
set data(data) {
|
|
127
|
+
[this.columnDescriptors, this.#data] = treeToDataSourceRows(data);
|
|
128
|
+
requestAnimationFrame(()=>{
|
|
129
|
+
this.sendRowsToClient();
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
get filter() {
|
|
133
|
+
return this._configWithVisualLink.filterSpec;
|
|
134
|
+
}
|
|
135
|
+
set filter(filter) {
|
|
136
|
+
this._configWithVisualLink = {
|
|
137
|
+
...this._configWithVisualLink,
|
|
138
|
+
filterSpec: filter
|
|
139
|
+
};
|
|
140
|
+
if (filter.filter) this.applyFilter(filter);
|
|
141
|
+
else this.#filterSet = void 0;
|
|
142
|
+
[this.visibleRows, this.visibleRowIndex] = getVisibleRows(this.#data, this.expandedRows, this.#filterSet);
|
|
143
|
+
const { from, to } = this.range;
|
|
144
|
+
this.clientCallback?.({
|
|
145
|
+
clientViewportId: this.viewport,
|
|
146
|
+
mode: "batch",
|
|
147
|
+
rows: this.visibleRows.slice(from, to).map((row)=>toClientRow(row, this.keys)),
|
|
148
|
+
size: this.visibleRows.length,
|
|
149
|
+
type: "viewport-update"
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
applyFilter({ filter: filterQuery, filterStruct }) {
|
|
153
|
+
const filter = filterStruct ?? parseFilter(filterQuery);
|
|
154
|
+
if (isSingleValueFilter(filter)) {
|
|
155
|
+
const filterSet = [];
|
|
156
|
+
const regex = new RegExp(`${filter.value}`, "i");
|
|
157
|
+
for (const row of this.#data){
|
|
158
|
+
const { [KEY]: key, [IDX]: idx } = row;
|
|
159
|
+
if (regex.test(lastPathSegment(key, "|"))) filterSet.push(idx);
|
|
160
|
+
}
|
|
161
|
+
this.#filterSet = filterSet;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
applySelectedKeyValues(keys, revealSelected = false) {
|
|
165
|
+
keys.forEach((key)=>{
|
|
166
|
+
const rowIdx = this.indexOfRowWithKey(key);
|
|
167
|
+
const row = this.#data[rowIdx];
|
|
168
|
+
row[SELECTED] = 1;
|
|
169
|
+
if (revealSelected && 1 !== row[DEPTH]) {
|
|
170
|
+
const keys = key.slice(6).split("|").slice(0, -1);
|
|
171
|
+
let path = "$root";
|
|
172
|
+
do {
|
|
173
|
+
path = `${path}|${keys.shift()}`;
|
|
174
|
+
this.expandedRows.add(path);
|
|
175
|
+
}while (keys.length)
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
indexOfRowWithKey = (key)=>this.#data.findIndex((row)=>row[KEY] === key);
|
|
180
|
+
select(selectRequest) {
|
|
181
|
+
const updatedRows = [];
|
|
182
|
+
switch(selectRequest.type){
|
|
183
|
+
case "SELECT_ROW":
|
|
184
|
+
{
|
|
185
|
+
const { preserveExistingSelection, rowKey } = selectRequest;
|
|
186
|
+
for (const row of this.visibleRows){
|
|
187
|
+
const { [IDX]: rowIndex, [KEY]: key, [SELECTED]: sel } = row;
|
|
188
|
+
if (1 === sel && false === preserveExistingSelection && key !== rowKey) {
|
|
189
|
+
const deselectedRow = row.slice();
|
|
190
|
+
deselectedRow[SELECTED] = 0;
|
|
191
|
+
this.visibleRows[rowIndex] = deselectedRow;
|
|
192
|
+
updatedRows.push(deselectedRow);
|
|
193
|
+
} else if (key === rowKey) {
|
|
194
|
+
const selectedRow = row.slice();
|
|
195
|
+
selectedRow[SELECTED] = 1;
|
|
196
|
+
this.visibleRows[rowIndex] = selectedRow;
|
|
197
|
+
updatedRows.push(selectedRow);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
case "DESELECT_ROW":
|
|
203
|
+
break;
|
|
204
|
+
default:
|
|
205
|
+
}
|
|
206
|
+
if (updatedRows.length > 0) this.clientCallback?.({
|
|
207
|
+
clientViewportId: this.viewport,
|
|
208
|
+
mode: "update",
|
|
209
|
+
type: "viewport-update",
|
|
210
|
+
rows: updatedRows
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
getRowKey(keyOrIndex) {
|
|
214
|
+
if ("string" == typeof keyOrIndex) return keyOrIndex;
|
|
215
|
+
const row = this.getRowAtIndex(keyOrIndex);
|
|
216
|
+
if (void 0 === row) throw Error(`row not found at index ${keyOrIndex}`);
|
|
217
|
+
return row[KEY];
|
|
218
|
+
}
|
|
219
|
+
openTreeNode(keyOrIndex) {
|
|
220
|
+
const key = this.getRowKey(keyOrIndex);
|
|
221
|
+
this.expandedRows.add(key);
|
|
222
|
+
[this.visibleRows, this.visibleRowIndex] = getVisibleRows(this.#data, this.expandedRows);
|
|
223
|
+
const { from, to } = this._range;
|
|
224
|
+
this.clientCallback?.({
|
|
225
|
+
clientViewportId: this.viewport,
|
|
226
|
+
mode: "batch",
|
|
227
|
+
rows: this.visibleRows.slice(from, to).map((row)=>toClientRow(row, this.keys)),
|
|
228
|
+
size: this.visibleRows.length,
|
|
229
|
+
type: "viewport-update"
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
closeTreeNode(keyOrIndex, cascade = false) {
|
|
233
|
+
const key = this.getRowKey(keyOrIndex);
|
|
234
|
+
this.expandedRows.delete(key);
|
|
235
|
+
if (cascade) {
|
|
236
|
+
for (const rowKey of this.expandedRows.keys())if (rowKey.startsWith(key)) this.expandedRows.delete(rowKey);
|
|
237
|
+
}
|
|
238
|
+
[this.visibleRows, this.visibleRowIndex] = getVisibleRows(this.#data, this.expandedRows);
|
|
239
|
+
this.sendRowsToClient();
|
|
240
|
+
}
|
|
241
|
+
get status() {
|
|
242
|
+
return this.#status;
|
|
243
|
+
}
|
|
244
|
+
get selectedRowsCount() {
|
|
245
|
+
return this.#selectedRowsCount;
|
|
246
|
+
}
|
|
247
|
+
get size() {
|
|
248
|
+
return this.#size;
|
|
249
|
+
}
|
|
250
|
+
rangeRequest(range) {
|
|
251
|
+
this.keys.reset(range);
|
|
252
|
+
requestAnimationFrame(()=>{
|
|
253
|
+
this.sendRowsToClient();
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
sendRowsToClient() {
|
|
257
|
+
const { from, to } = this._range;
|
|
258
|
+
this.clientCallback?.({
|
|
259
|
+
clientViewportId: this.viewport,
|
|
260
|
+
mode: "batch",
|
|
261
|
+
rows: this.visibleRows.slice(from, to).map((row)=>toClientRow(row, this.keys)),
|
|
262
|
+
size: this.visibleRows.length,
|
|
263
|
+
type: "viewport-update"
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
createLink({ parentVpId, link: { fromColumn, toColumn } }) {
|
|
267
|
+
console.log("create link", {
|
|
268
|
+
parentVpId,
|
|
269
|
+
fromColumn,
|
|
270
|
+
toColumn
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
removeLink() {
|
|
274
|
+
console.log("remove link");
|
|
275
|
+
}
|
|
276
|
+
async remoteProcedureCall() {
|
|
277
|
+
return Promise.reject();
|
|
278
|
+
}
|
|
279
|
+
async menuRpcCall(rpcRequest) {
|
|
280
|
+
console.log("rmenuRpcCall", {
|
|
281
|
+
rpcRequest
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
getChildRows(rowKey) {
|
|
285
|
+
const parentRow = this.#data.find((row)=>row[KEY] === rowKey);
|
|
286
|
+
if (parentRow) {
|
|
287
|
+
const { [IDX]: parentIdx, [DEPTH]: parentDepth } = parentRow;
|
|
288
|
+
let rowIdx = parentIdx + 1;
|
|
289
|
+
const childRows = [];
|
|
290
|
+
do {
|
|
291
|
+
const { [DEPTH]: depth } = this.#data[rowIdx];
|
|
292
|
+
if (depth === parentDepth + 1) childRows.push(this.#data[rowIdx]);
|
|
293
|
+
else if (depth <= parentDepth) break;
|
|
294
|
+
rowIdx += 1;
|
|
295
|
+
}while (rowIdx < this.#data.length)
|
|
296
|
+
return childRows;
|
|
297
|
+
}
|
|
298
|
+
console.warn(`JsonDataSource getChildRows row not found for key ${rowKey}`);
|
|
299
|
+
return [];
|
|
300
|
+
}
|
|
301
|
+
getRowsAtDepth(depth, visibleOnly = true) {
|
|
302
|
+
const rows = visibleOnly ? this.visibleRows : this.#data;
|
|
303
|
+
return rows.filter((row)=>row[DEPTH] === depth);
|
|
304
|
+
}
|
|
305
|
+
getRowAtIndex(rowIdx) {
|
|
306
|
+
return this.visibleRows[rowIdx];
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
function getVisibleRows(rows, expandedKeys, filterset) {
|
|
310
|
+
const visibleRows = [];
|
|
311
|
+
const visibleRowIndex = {};
|
|
312
|
+
const data = filterset ?? rows;
|
|
313
|
+
for(let i = 0, index = 0; i < data.length; i++){
|
|
314
|
+
const idx = filterset ? filterset[i] : i;
|
|
315
|
+
const row = rows[idx];
|
|
316
|
+
const { [COUNT]: count, [DEPTH]: depth, [KEY]: key, [IS_LEAF]: isLeaf } = row;
|
|
317
|
+
if (filterset) {
|
|
318
|
+
const previousRow = visibleRows.at(-1);
|
|
319
|
+
if (missingAncestor(row, previousRow)) {
|
|
320
|
+
let currentRow = row;
|
|
321
|
+
const missingRows = [];
|
|
322
|
+
while(currentRow){
|
|
323
|
+
currentRow = getParentRow(rows, currentRow);
|
|
324
|
+
if (currentRow) missingRows.unshift(currentRow);
|
|
325
|
+
}
|
|
326
|
+
missingRows.forEach((row)=>{
|
|
327
|
+
visibleRows.push(cloneRow(row, index++, true));
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
visibleRows.push(cloneRow(row, index++, true));
|
|
331
|
+
visibleRowIndex[index] = i;
|
|
332
|
+
} else {
|
|
333
|
+
const isExpanded = expandedKeys.has(key);
|
|
334
|
+
visibleRows.push(cloneRow(row, index, isExpanded));
|
|
335
|
+
visibleRowIndex[index++] = i;
|
|
336
|
+
const skipNonVisibleRows = !isLeaf && !isExpanded && count > 0;
|
|
337
|
+
if (skipNonVisibleRows) do i += 1;
|
|
338
|
+
while (i < rows.length - 1 && rows[i + 1][DEPTH] > depth)
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return [
|
|
342
|
+
visibleRows,
|
|
343
|
+
visibleRowIndex
|
|
344
|
+
];
|
|
345
|
+
}
|
|
346
|
+
const cloneRow = (row, index, isExpanded)=>{
|
|
347
|
+
const dolly = row.slice();
|
|
348
|
+
dolly[0] = index;
|
|
349
|
+
dolly[1] = index;
|
|
350
|
+
if (isExpanded) dolly[IS_EXPANDED] = true;
|
|
351
|
+
return dolly;
|
|
352
|
+
};
|
|
353
|
+
export { TreeDataSource };
|