@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.
@@ -0,0 +1,41 @@
1
+ import { metadataKeys } from "@vuu-ui/vuu-utils";
2
+ const { KEY: KEY, RENDER_IDX: RENDER_IDX, SELECTED: SELECTED } = metadataKeys;
3
+ const replaceBigInt = (value)=>{
4
+ if ("bigint" == typeof value) return value.toString();
5
+ return value;
6
+ };
7
+ const toClientRow = (row, keys, selectedRows, dataIndices)=>{
8
+ const [rowIndex] = row;
9
+ const selectedAll = selectedRows.has("*");
10
+ let clientRow;
11
+ if (dataIndices) {
12
+ const { count } = metadataKeys;
13
+ clientRow = row.slice(0, count).concat(dataIndices.map((idx)=>replaceBigInt(row[idx])));
14
+ } else clientRow = row.map(replaceBigInt);
15
+ clientRow[RENDER_IDX] = keys.keyFor(rowIndex);
16
+ clientRow[SELECTED] = selectedAll || selectedRows.has(row[KEY]) ? 1 : 0;
17
+ return clientRow;
18
+ };
19
+ const divergentMaps = (columnMap, dataMap)=>{
20
+ if (dataMap) {
21
+ const { count: mapOffset } = metadataKeys;
22
+ for (const [columnName, index] of Object.entries(columnMap)){
23
+ const dataIdx = dataMap[columnName];
24
+ if (void 0 === dataIdx) throw Error(`ArrayDataSource column ${columnName} is not in underlying data set`);
25
+ if (dataIdx !== index - mapOffset) return true;
26
+ }
27
+ }
28
+ return false;
29
+ };
30
+ const getDataIndices = (columnMap, dataMap)=>{
31
+ const { count: mapOffset } = metadataKeys;
32
+ const result = [];
33
+ Object.entries(columnMap).forEach(([columnName])=>{
34
+ result.push(dataMap[columnName] + mapOffset);
35
+ });
36
+ return result;
37
+ };
38
+ const buildDataToClientMap = (columnMap, dataMap)=>{
39
+ if (dataMap && divergentMaps(columnMap, dataMap)) return getDataIndices(columnMap, dataMap);
40
+ };
41
+ export { buildDataToClientMap, toClientRow };
@@ -0,0 +1,138 @@
1
+ import { metadataKeys } from "@vuu-ui/vuu-utils";
2
+ const { DEPTH: DEPTH, IS_EXPANDED: IS_EXPANDED, KEY: KEY } = metadataKeys;
3
+ const timestamp = 0;
4
+ const isNew = false;
5
+ const collapseGroup = (key, groupedRows)=>{
6
+ const rows = [];
7
+ for(let i = 0, idx = 0, collapsed = false, len = groupedRows.length; i < len; i++){
8
+ const row = groupedRows[i];
9
+ const { [DEPTH]: depth, [KEY]: rowKey } = row;
10
+ if (rowKey === key) {
11
+ const collapsedRow = row.slice();
12
+ collapsedRow[IS_EXPANDED] = false;
13
+ rows.push(collapsedRow);
14
+ idx += 1;
15
+ collapsed = true;
16
+ while(i < len - 1 && groupedRows[i + 1][DEPTH] > depth)i += 1;
17
+ } else if (collapsed) {
18
+ const newRow = row.slice();
19
+ newRow[0] = idx;
20
+ newRow[1] = idx;
21
+ rows.push(newRow);
22
+ idx += 1;
23
+ } else {
24
+ rows.push(row);
25
+ idx += 1;
26
+ }
27
+ }
28
+ return rows;
29
+ };
30
+ const expandGroup = (keys, sourceRows, groupBy, columnMap, groupMap, processedData)=>{
31
+ const groupIndices = groupBy.map((column)=>columnMap[column]);
32
+ return dataRowsFromGroups2(groupMap, groupIndices, keys, sourceRows, void 0, void 0, void 0, processedData);
33
+ };
34
+ const dataRowsFromGroups2 = (groupMap, groupIndices, openKeys, sourceRows = [], root = "$root", depth = 1, rows = [], processedData)=>{
35
+ const keys = Object.keys(groupMap).sort();
36
+ for (const key of keys){
37
+ const idx = rows.length;
38
+ const groupKey = `${root}|${key}`;
39
+ const row = [
40
+ idx,
41
+ idx,
42
+ false,
43
+ false,
44
+ depth,
45
+ childCount(groupMap[key]),
46
+ groupKey,
47
+ 0,
48
+ timestamp,
49
+ isNew
50
+ ];
51
+ row[groupIndices[depth - 1]] = key;
52
+ rows.push(row);
53
+ if (openKeys.includes(groupKey)) {
54
+ row[IS_EXPANDED] = true;
55
+ if (Array.isArray(groupMap[key])) pushChildren(rows, groupMap[key], sourceRows, groupKey, depth + 1);
56
+ else dataRowsFromGroups2(groupMap[key], groupIndices, openKeys, sourceRows, groupKey, depth + 1, rows, processedData);
57
+ }
58
+ }
59
+ for(const key in rows)for(const index in rows)if (false === rows[key][2] && void 0 != processedData[index]) {
60
+ if (rows[key][groupIndices[0]] === processedData[index][groupIndices[0]]) {
61
+ rows[key] = rows[key].splice(0, 8).concat(processedData[index].slice(8, processedData[index].length));
62
+ break;
63
+ }
64
+ }
65
+ return rows;
66
+ };
67
+ const pushChildren = (rows, tree, sourceRows, parentKey, depth)=>{
68
+ for (const rowIdx of tree){
69
+ const idx = rows.length;
70
+ const sourceRow = sourceRows[rowIdx].slice();
71
+ sourceRow[0] = idx;
72
+ sourceRow[1] = idx;
73
+ sourceRow[DEPTH] = depth;
74
+ sourceRow[KEY] = `${parentKey}|${sourceRow[KEY]}`;
75
+ rows.push(sourceRow);
76
+ }
77
+ };
78
+ const groupRows = (rows, groupBy, columnMap)=>{
79
+ const groupIndices = groupBy.map((column)=>columnMap[column]);
80
+ const groupTree = groupLeafRows(rows, groupIndices);
81
+ const groupedDataRows = dataRowsFromGroups(groupTree, groupIndices);
82
+ return [
83
+ groupedDataRows,
84
+ groupTree
85
+ ];
86
+ };
87
+ const dataRowsFromGroups = (groupTree, groupIndices)=>{
88
+ const depth = 0;
89
+ const rows = [];
90
+ let idx = 0;
91
+ const keys = Object.keys(groupTree).sort();
92
+ for (const key of keys){
93
+ const row = [
94
+ idx,
95
+ idx,
96
+ false,
97
+ false,
98
+ 1,
99
+ childCount(groupTree[key]),
100
+ `$root|${key}`,
101
+ 0,
102
+ timestamp,
103
+ isNew
104
+ ];
105
+ row[groupIndices[depth]] = key;
106
+ rows.push(row);
107
+ idx += 1;
108
+ }
109
+ return rows;
110
+ };
111
+ function childCount(list) {
112
+ if (Array.isArray(list)) return list.length;
113
+ return Object.keys(list).length;
114
+ }
115
+ function groupLeafRows(leafRows, groupby) {
116
+ const groups = {};
117
+ const levels = groupby.length;
118
+ const lastLevel = levels - 1;
119
+ for(let i = 0, len = leafRows.length; i < len; i++){
120
+ const leafRow = leafRows[i];
121
+ let target = groups;
122
+ let targetNode;
123
+ let key;
124
+ for(let level = 0; level < levels; level++){
125
+ const colIdx = groupby[level];
126
+ key = leafRow[colIdx].toString();
127
+ targetNode = target[key];
128
+ if (targetNode && level === lastLevel) targetNode.push(i);
129
+ else if (targetNode) target = targetNode;
130
+ else if (!targetNode && level < lastLevel) target = target[key] = {};
131
+ else if (!targetNode) target[key] = [
132
+ i
133
+ ];
134
+ }
135
+ }
136
+ return groups;
137
+ }
138
+ export { collapseGroup, expandGroup, groupRows };
@@ -0,0 +1,56 @@
1
+ const defaultSortPredicate = (r1, r2, [i, direction])=>{
2
+ const val1 = "D" === direction ? r2[i] : r1[i];
3
+ const val2 = "D" === direction ? r1[i] : r2[i];
4
+ if (val1 === val2) return 0;
5
+ if (null === val2 || val1 > val2) return 1;
6
+ return -1;
7
+ };
8
+ const sortComparator = (sortDefs)=>{
9
+ if (1 === sortDefs.length) return singleColComparator(sortDefs);
10
+ if (2 === sortDefs.length) return twoColComparator(sortDefs);
11
+ return multiColComparator(sortDefs);
12
+ };
13
+ const singleColComparator = ([[i, direction]])=>(r1, r2)=>{
14
+ const v1 = "D" === direction ? r2[i] : r1[i];
15
+ const v2 = "D" === direction ? r1[i] : r2[i];
16
+ return v1 > v2 ? 1 : v2 > v1 ? -1 : 0;
17
+ };
18
+ const twoColComparator = ([[idx1, direction1], [idx2, direction2]])=>(r1, r2)=>{
19
+ const v1 = "D" === direction1 ? r2[idx1] : r1[idx1];
20
+ const v2 = "D" === direction1 ? r1[idx1] : r2[idx1];
21
+ const v3 = "D" === direction2 ? r2[idx2] : r1[idx2];
22
+ const v4 = "D" === direction2 ? r1[idx2] : r2[idx2];
23
+ return v1 > v2 ? 1 : v2 > v1 ? -1 : v3 > v4 ? 1 : v4 > v3 ? -1 : 0;
24
+ };
25
+ const multiColComparator = (sortDefs, test = defaultSortPredicate)=>(r1, r2)=>{
26
+ for (const sortDef of sortDefs){
27
+ const result = test(r1, r2, sortDef);
28
+ if (0 !== result) return result;
29
+ }
30
+ return 0;
31
+ };
32
+ const sortRows = (rows, { sortDefs }, columnMap)=>{
33
+ const indexedSortDefs = sortDefs.map(({ column, sortType })=>[
34
+ columnMap[column],
35
+ sortType
36
+ ]);
37
+ const comparator = sortComparator(indexedSortDefs);
38
+ return rows.slice().sort(comparator);
39
+ };
40
+ function binarySearch(items, item, comparator) {
41
+ let l = 0;
42
+ let h = items.length - 1;
43
+ let m;
44
+ let comparison;
45
+ while(l <= h){
46
+ m = l + h >>> 1;
47
+ comparison = comparator(items[m], item);
48
+ if (comparison < 0) l = m + 1;
49
+ else {
50
+ if (!(comparison > 0)) return m;
51
+ h = m - 1;
52
+ }
53
+ }
54
+ return ~l;
55
+ }
56
+ export { binarySearch, sortComparator, sortRows };
package/src/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./array-data-source/array-data-source.js";
2
+ export * from "./json-data-source/JsonDataSource.js";
3
+ export * from "./tree-data-source/TreeDataSource.js";
@@ -0,0 +1,319 @@
1
+ import { EventEmitter, KeySet, NO_CONFIG_CHANGES, NULL_RANGE, Range, jsonToDataSourceRows, metadataKeys, uuid, vanillaConfig } from "@vuu-ui/vuu-utils";
2
+ const NULL_SCHEMA = {
3
+ columns: [],
4
+ key: "",
5
+ table: {
6
+ module: "",
7
+ table: ""
8
+ }
9
+ };
10
+ const { DEPTH: DEPTH, IDX: IDX, IS_EXPANDED: IS_EXPANDED, IS_LEAF: IS_LEAF, KEY: KEY, SELECTED: SELECTED } = metadataKeys;
11
+ const toClientRow = (row, keys)=>{
12
+ const [rowIndex] = row;
13
+ const clientRow = row.slice();
14
+ clientRow[1] = keys.keyFor(rowIndex);
15
+ return clientRow;
16
+ };
17
+ class JsonDataSource extends EventEmitter {
18
+ columnDescriptors;
19
+ clientCallback;
20
+ expandedRows = new Set();
21
+ visibleRows = [];
22
+ #aggregations = [];
23
+ #config = vanillaConfig;
24
+ #data;
25
+ #filter = {
26
+ filter: ""
27
+ };
28
+ #groupBy = [];
29
+ #range = Range(0, 0);
30
+ #selectedRowsCount = 0;
31
+ #size = 0;
32
+ #sort = {
33
+ sortDefs: []
34
+ };
35
+ #status = "initialising";
36
+ #title;
37
+ rowCount;
38
+ viewport;
39
+ keys = new KeySet(this.#range);
40
+ constructor({ aggregations, data, filterSpec, groupBy, sort, title, viewport }){
41
+ super();
42
+ if (!data) throw Error("JsonDataSource constructor called without data");
43
+ [this.columnDescriptors, this.#data] = jsonToDataSourceRows(data);
44
+ this.visibleRows = this.#data.filter((row)=>0 === row[DEPTH]).map((row, index)=>[
45
+ index,
46
+ index
47
+ ].concat(row.slice(2)));
48
+ this.viewport = viewport || uuid();
49
+ if (aggregations) this.#aggregations = aggregations;
50
+ if (this.columnDescriptors) this.#config = {
51
+ ...this.#config,
52
+ columns: this.columnDescriptors.map((c)=>c.name)
53
+ };
54
+ if (filterSpec) this.#filter = filterSpec;
55
+ if (groupBy) this.#groupBy = groupBy;
56
+ if (sort) this.#sort = sort;
57
+ this.#title = title;
58
+ }
59
+ async subscribe({ viewport = this.viewport ?? uuid(), columns, aggregations, range, sort, groupBy, filterSpec }, callback) {
60
+ this.clientCallback = callback;
61
+ if (aggregations) this.#aggregations = aggregations;
62
+ if (columns) this.#config = {
63
+ ...this.#config,
64
+ columns
65
+ };
66
+ if (filterSpec) this.#filter = filterSpec;
67
+ if (groupBy) this.#groupBy = groupBy;
68
+ if (sort) this.#sort = sort;
69
+ if ("initialising" !== this.#status) return;
70
+ this.viewport = viewport;
71
+ this.#status = "subscribed";
72
+ this.clientCallback?.({
73
+ aggregations: this.#aggregations,
74
+ type: "subscribed",
75
+ clientViewportId: this.viewport,
76
+ columns: this.#config.columns,
77
+ filterSpec: this.#filter,
78
+ groupBy: this.#groupBy,
79
+ range: this.#range,
80
+ sort: this.#sort,
81
+ tableSchema: NULL_SCHEMA
82
+ });
83
+ this.clientCallback({
84
+ clientViewportId: this.viewport,
85
+ mode: "size-only",
86
+ type: "viewport-update",
87
+ size: this.visibleRows.length
88
+ });
89
+ if (range && !this.#range.equals(range)) this.range = range;
90
+ else if (this.#range !== NULL_RANGE) this.sendRowsToClient();
91
+ }
92
+ unsubscribe() {
93
+ console.log("noop");
94
+ }
95
+ suspend() {
96
+ console.log("noop");
97
+ return this;
98
+ }
99
+ resume() {
100
+ console.log("noop");
101
+ return this;
102
+ }
103
+ disable() {
104
+ console.log("noop");
105
+ return this;
106
+ }
107
+ enable() {
108
+ console.log("noop");
109
+ return this;
110
+ }
111
+ set data(data) {
112
+ [this.columnDescriptors, this.#data] = jsonToDataSourceRows(data);
113
+ this.visibleRows = this.#data.filter((row)=>0 === row[DEPTH]).map((row, index)=>[
114
+ index,
115
+ index
116
+ ].concat(row.slice(2)));
117
+ requestAnimationFrame(()=>{
118
+ this.sendRowsToClient();
119
+ });
120
+ }
121
+ select(selectRequest) {
122
+ const updatedRows = [];
123
+ switch(selectRequest.type){
124
+ case "SELECT_ROW":
125
+ {
126
+ const { preserveExistingSelection, rowKey } = selectRequest;
127
+ for (const row of this.#data){
128
+ const { [IDX]: rowIndex, [KEY]: key, [SELECTED]: sel } = row;
129
+ if (1 === sel && false === preserveExistingSelection && key !== rowKey) {
130
+ const deselectedRow = row.slice();
131
+ deselectedRow[SELECTED] = 0;
132
+ this.#data[rowIndex] = deselectedRow;
133
+ updatedRows.push(deselectedRow);
134
+ } else if (key === rowKey) {
135
+ const selectedRow = row.slice();
136
+ selectedRow[SELECTED] = 1;
137
+ this.#data[rowIndex] = selectedRow;
138
+ updatedRows.push(selectedRow);
139
+ }
140
+ }
141
+ break;
142
+ }
143
+ case "DESELECT_ROW":
144
+ break;
145
+ default:
146
+ }
147
+ if (updatedRows.length > 0) this.clientCallback?.({
148
+ clientViewportId: this.viewport,
149
+ mode: "update",
150
+ type: "viewport-update",
151
+ rows: updatedRows
152
+ });
153
+ }
154
+ getRowKey(keyOrIndex) {
155
+ if ("string" == typeof keyOrIndex) return keyOrIndex;
156
+ const row = this.visibleRows[keyOrIndex];
157
+ if (void 0 === row) throw Error(`row not found at index ${keyOrIndex}`);
158
+ return row?.[KEY];
159
+ }
160
+ openTreeNode(keyOrIndex) {
161
+ const key = this.getRowKey(keyOrIndex);
162
+ this.expandedRows.add(key);
163
+ this.visibleRows = getVisibleRows(this.#data, this.expandedRows);
164
+ const { from, to } = this.#range;
165
+ this.clientCallback?.({
166
+ clientViewportId: this.viewport,
167
+ mode: "batch",
168
+ rows: this.visibleRows.slice(from, to).map((row)=>toClientRow(row, this.keys)),
169
+ size: this.visibleRows.length,
170
+ type: "viewport-update"
171
+ });
172
+ }
173
+ closeTreeNode(keyOrIndex, cascade = false) {
174
+ const key = this.getRowKey(keyOrIndex);
175
+ this.expandedRows.delete(key);
176
+ if (cascade) {
177
+ for (const rowKey of this.expandedRows.keys())if (rowKey.startsWith(key)) this.expandedRows.delete(rowKey);
178
+ }
179
+ this.visibleRows = getVisibleRows(this.#data, this.expandedRows);
180
+ this.sendRowsToClient();
181
+ }
182
+ get status() {
183
+ return this.#status;
184
+ }
185
+ get config() {
186
+ return this.#config;
187
+ }
188
+ applyConfig() {
189
+ return NO_CONFIG_CHANGES;
190
+ }
191
+ get selectedRowsCount() {
192
+ return this.#selectedRowsCount;
193
+ }
194
+ get size() {
195
+ return this.#size;
196
+ }
197
+ get range() {
198
+ return this.#range;
199
+ }
200
+ set range(range) {
201
+ this.#range = range;
202
+ this.keys.reset(range);
203
+ requestAnimationFrame(()=>{
204
+ this.sendRowsToClient();
205
+ });
206
+ }
207
+ sendRowsToClient() {
208
+ const { from, to } = this.#range;
209
+ this.clientCallback?.({
210
+ clientViewportId: this.viewport,
211
+ mode: "batch",
212
+ rows: this.visibleRows.slice(from, to).map((row)=>toClientRow(row, this.keys)),
213
+ size: this.visibleRows.length,
214
+ type: "viewport-update"
215
+ });
216
+ }
217
+ get columns() {
218
+ return this.#config.columns;
219
+ }
220
+ set columns(columns) {
221
+ this.#config = {
222
+ ...this.#config,
223
+ columns
224
+ };
225
+ }
226
+ get aggregations() {
227
+ return this.#aggregations;
228
+ }
229
+ set aggregations(aggregations) {
230
+ this.#aggregations = aggregations;
231
+ }
232
+ get sort() {
233
+ return this.#sort;
234
+ }
235
+ set sort(sort) {
236
+ this.#sort = sort;
237
+ }
238
+ get filter() {
239
+ return this.#filter;
240
+ }
241
+ set filter(filter) {
242
+ this.#filter = filter;
243
+ }
244
+ get groupBy() {
245
+ return this.#groupBy;
246
+ }
247
+ set groupBy(groupBy) {
248
+ this.#groupBy = groupBy;
249
+ }
250
+ get title() {
251
+ return this.#title ?? "";
252
+ }
253
+ set title(title) {
254
+ this.#title = title;
255
+ }
256
+ createLink({ parentVpId, link: { fromColumn, toColumn } }) {
257
+ console.log("create link", {
258
+ parentVpId,
259
+ fromColumn,
260
+ toColumn
261
+ });
262
+ }
263
+ removeLink() {
264
+ console.log("remove link");
265
+ }
266
+ async remoteProcedureCall() {
267
+ return Promise.reject();
268
+ }
269
+ async menuRpcCall(rpcRequest) {
270
+ console.log("rmenuRpcCall", {
271
+ rpcRequest
272
+ });
273
+ }
274
+ getChildRows(rowKey) {
275
+ const parentRow = this.#data.find((row)=>row[KEY] === rowKey);
276
+ if (parentRow) {
277
+ const { [IDX]: parentIdx, [DEPTH]: parentDepth } = parentRow;
278
+ let rowIdx = parentIdx + 1;
279
+ const childRows = [];
280
+ do {
281
+ const { [DEPTH]: depth } = this.#data[rowIdx];
282
+ if (depth === parentDepth + 1) childRows.push(this.#data[rowIdx]);
283
+ else if (depth <= parentDepth) break;
284
+ rowIdx += 1;
285
+ }while (rowIdx < this.#data.length)
286
+ return childRows;
287
+ }
288
+ console.warn(`JsonDataSource getChildRows row not found for key ${rowKey}`);
289
+ return [];
290
+ }
291
+ getRowsAtDepth(depth, visibleOnly = true) {
292
+ const rows = visibleOnly ? this.visibleRows : this.#data;
293
+ return rows.filter((row)=>row[DEPTH] === depth);
294
+ }
295
+ }
296
+ function getVisibleRows(rows, expandedKeys) {
297
+ const visibleRows = [];
298
+ const index = {
299
+ value: 0
300
+ };
301
+ for(let i = 0; i < rows.length; i++){
302
+ const row = rows[i];
303
+ const { [DEPTH]: depth, [KEY]: key, [IS_LEAF]: isLeaf } = row;
304
+ const isExpanded = expandedKeys.has(key);
305
+ visibleRows.push(cloneRow(row, index, isExpanded));
306
+ if (!isLeaf && !isExpanded) do i += 1;
307
+ while (i < rows.length - 1 && rows[i + 1][DEPTH] > depth)
308
+ }
309
+ return visibleRows;
310
+ }
311
+ const cloneRow = (row, index, isExpanded)=>{
312
+ const dolly = row.slice();
313
+ dolly[0] = index.value;
314
+ dolly[1] = index.value;
315
+ if (isExpanded) dolly[IS_EXPANDED] = true;
316
+ index.value += 1;
317
+ return dolly;
318
+ };
319
+ export { JsonDataSource };
@@ -0,0 +1,11 @@
1
+ class IconProvider {
2
+ #iconMap = {};
3
+ getIcon = (dataRow)=>{
4
+ const key = dataRow.key;
5
+ return this.#iconMap[key];
6
+ };
7
+ setIcon(key, icon) {
8
+ this.#iconMap[key] = icon;
9
+ }
10
+ }
11
+ export { IconProvider };