@vuu-ui/vuu-data-test 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 +8 -9
- package/src/ArrayProxy.js +38 -0
- package/src/RuntimeVisualLink.js +45 -0
- package/src/SessionTable.js +49 -0
- package/src/Table.js +96 -0
- package/src/TickingArrayDataSource.js +208 -0
- package/src/UpdateGenerator.js +72 -0
- package/src/basket/BasketModule.js +272 -0
- package/src/basket/basket-schemas.js +355 -0
- package/src/basket/index.js +2 -0
- package/src/basket/reference-data/constituents.js +76 -0
- package/src/basket/reference-data/ftse100.js +686 -0
- package/src/basket/reference-data/hsi.js +1047 -0
- package/src/basket/reference-data/nasdaq100.js +812 -0
- package/src/basket/reference-data/prices.js +43 -0
- package/src/basket/reference-data/sp500.js +315 -0
- package/src/core/module/ModuleContainer.js +21 -0
- package/src/core/module/VuuModule.js +476 -0
- package/src/core/table/TableContainer.js +97 -0
- package/src/data-utils.js +53 -0
- package/src/index.js +13 -0
- package/src/local-datasource-provider/LocalDatasourceProvider.js +44 -0
- package/src/makeSuggestions.js +20 -0
- package/src/rowUpdates.js +0 -0
- package/src/schemas.js +19 -0
- package/src/session-table-utils.js +16 -0
- package/src/simul/SimulModule.js +175 -0
- package/src/simul/index.js +2 -0
- package/src/simul/reference-data/accounts.js +23 -0
- package/src/simul/reference-data/algos.js +9 -0
- package/src/simul/reference-data/currencies.js +8 -0
- package/src/simul/reference-data/index.js +5 -0
- package/src/simul/reference-data/instrument-prices.js +48 -0
- package/src/simul/reference-data/instruments-extended.js +12 -0
- package/src/simul/reference-data/instruments.js +67 -0
- package/src/simul/reference-data/isin-generator/convert-string-to-base-ten-number.js +2 -0
- package/src/simul/reference-data/isin-generator/fixtures/alphabet.js +27 -0
- package/src/simul/reference-data/isin-generator/fixtures/alphanumeric-values.js +7 -0
- package/src/simul/reference-data/isin-generator/fixtures/base-ten-numbers.js +13 -0
- package/src/simul/reference-data/isin-generator/generate-cusip-check-code.js +17 -0
- package/src/simul/reference-data/isin-generator/generate-cusip-without-check-code.js +8 -0
- package/src/simul/reference-data/isin-generator/generate-cusip.js +8 -0
- package/src/simul/reference-data/isin-generator/generate-random-alphanumeric.js +3 -0
- package/src/simul/reference-data/isin-generator/generate-random-base-ten-number.js +2 -0
- package/src/simul/reference-data/isin-generator/generate-random-string-of-alphanumeric-chars.js +7 -0
- package/src/simul/reference-data/isin-generator/generate-random-string-of-base-ten-chars.js +7 -0
- package/src/simul/reference-data/isin-generator/index.js +280 -0
- package/src/simul/reference-data/isin-generator/is-odd.js +2 -0
- package/src/simul/reference-data/locations.js +45 -0
- package/src/simul/reference-data/lotsizes.js +24 -0
- package/src/simul/reference-data/orderStatus.js +6 -0
- package/src/simul/reference-data/orders.js +64 -0
- package/src/simul/reference-data/parent-child-orders.js +117 -0
- package/src/simul/reference-data/priceStrategies.js +8 -0
- package/src/simul/reference-data/prices.js +78 -0
- package/src/simul/reference-data/sides.js +5 -0
- package/src/simul/simul-schemas.js +428 -0
- package/src/test/TestModule.js +95 -0
- package/src/test/index.js +1 -0
- package/src/test/test-schemas.js +74 -0
- package/src/vuu-row-generator.js +71 -0
package/package.json
CHANGED
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.1.19-beta.
|
|
2
|
+
"version": "2.1.19-beta.2",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"author": "heswell",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@vuu-ui/vuu-data-local": "2.1.19-beta.
|
|
8
|
-
"@vuu-ui/vuu-utils": "2.1.19-beta.
|
|
7
|
+
"@vuu-ui/vuu-data-local": "2.1.19-beta.2",
|
|
8
|
+
"@vuu-ui/vuu-utils": "2.1.19-beta.2"
|
|
9
9
|
},
|
|
10
10
|
"devDependencies": {
|
|
11
|
-
"@vuu-ui/vuu-protocol-types": "2.1.19-beta.
|
|
12
|
-
"@vuu-ui/vuu-data-types": "2.1.19-beta.
|
|
13
|
-
"@vuu-ui/vuu-table-types": "2.1.19-beta.
|
|
11
|
+
"@vuu-ui/vuu-protocol-types": "2.1.19-beta.2",
|
|
12
|
+
"@vuu-ui/vuu-data-types": "2.1.19-beta.2",
|
|
13
|
+
"@vuu-ui/vuu-table-types": "2.1.19-beta.2"
|
|
14
14
|
},
|
|
15
15
|
"sideEffects": false,
|
|
16
16
|
"files": [
|
|
17
17
|
"README.md",
|
|
18
|
-
"
|
|
19
|
-
"cjs",
|
|
18
|
+
"src",
|
|
20
19
|
"/types"
|
|
21
20
|
],
|
|
22
21
|
"exports": {
|
|
23
22
|
".": {
|
|
24
|
-
"import": "./index.js",
|
|
23
|
+
"import": "./src/index.js",
|
|
25
24
|
"types": "./types/index.d.ts"
|
|
26
25
|
}
|
|
27
26
|
},
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
class ArrayProxy {
|
|
2
|
+
#getItem;
|
|
3
|
+
length = 0;
|
|
4
|
+
constructor(size, getRow){
|
|
5
|
+
this.#getItem = getRow;
|
|
6
|
+
this.length = size;
|
|
7
|
+
const handler = {
|
|
8
|
+
get: (target, prop)=>{
|
|
9
|
+
if ("length" === prop) return target.length;
|
|
10
|
+
if ("slice" === prop) return target.slice;
|
|
11
|
+
if ("map" === prop) return target.map;
|
|
12
|
+
if ("toString" === prop) return "[ArrayProxy]";
|
|
13
|
+
if ("string" == typeof prop) {
|
|
14
|
+
const index = parseInt(prop, 10);
|
|
15
|
+
if (!isNaN(index)) if (index < this.length) return target.#getItem(index);
|
|
16
|
+
else return;
|
|
17
|
+
}
|
|
18
|
+
throw Error(`unsupported property ${String(prop)} on ArrayProxy`);
|
|
19
|
+
},
|
|
20
|
+
set: (target, prop, newVal)=>{
|
|
21
|
+
if ("length" === prop) {
|
|
22
|
+
target.length = newVal;
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
throw Error("ArrayProxy is immutable except for length");
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
return new Proxy(this, handler);
|
|
29
|
+
}
|
|
30
|
+
map = (func)=>new ArrayProxy(this.length, (i)=>func(this.#getItem(i), i));
|
|
31
|
+
slice = (from, to)=>{
|
|
32
|
+
const out = [];
|
|
33
|
+
const end = Math.min(this.length, to);
|
|
34
|
+
for(let i = from; i < end; i++)out.push(this.#getItem(i));
|
|
35
|
+
return out;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export { ArrayProxy };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { buildColumnMap } from "@vuu-ui/vuu-utils";
|
|
2
|
+
class RuntimeVisualLink {
|
|
3
|
+
#childColumnName;
|
|
4
|
+
#childDataSource;
|
|
5
|
+
#parentColumnName;
|
|
6
|
+
#parentDataSource;
|
|
7
|
+
constructor(childDataSource, parentDataSource, childColumnName, parentColumnName){
|
|
8
|
+
this.#childColumnName = childColumnName;
|
|
9
|
+
this.#childDataSource = childDataSource;
|
|
10
|
+
this.#parentColumnName = parentColumnName;
|
|
11
|
+
this.#parentDataSource = parentDataSource;
|
|
12
|
+
parentDataSource.on("row-selection", this.handleParentSelectEvent);
|
|
13
|
+
}
|
|
14
|
+
destroy() {}
|
|
15
|
+
remove() {
|
|
16
|
+
this.#parentDataSource?.removeListener("row-selection", this.handleParentSelectEvent);
|
|
17
|
+
this.#childDataSource.baseFilter = {
|
|
18
|
+
filter: ""
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
handleParentSelectEvent = ()=>{
|
|
22
|
+
if (this.#childDataSource) {
|
|
23
|
+
const selectedValues = this.pickUniqueSelectedValues();
|
|
24
|
+
if (0 === selectedValues.length) this.#childDataSource.baseFilter = void 0;
|
|
25
|
+
else if (1 === selectedValues.length) this.#childDataSource.baseFilter = {
|
|
26
|
+
filter: `${this.#childColumnName} = "${selectedValues[0]}"`
|
|
27
|
+
};
|
|
28
|
+
else this.#childDataSource.baseFilter = {
|
|
29
|
+
filter: `${this.#childColumnName} in ["${selectedValues.join('","')}"]`
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
pickUniqueSelectedValues() {
|
|
34
|
+
const dataSource = this.#parentDataSource;
|
|
35
|
+
const map = buildColumnMap(dataSource.columns);
|
|
36
|
+
const colIndex = map[this.#parentColumnName];
|
|
37
|
+
const set = new Set();
|
|
38
|
+
for (const key of dataSource.getSelectedRowIds()){
|
|
39
|
+
const row = dataSource.getRowByKey(key);
|
|
40
|
+
if (row) set.add(row[colIndex]);
|
|
41
|
+
}
|
|
42
|
+
return Array.from(set);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export { RuntimeVisualLink };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { EventEmitter } from "@vuu-ui/vuu-utils";
|
|
2
|
+
const isProxy = Symbol("proxy-session-table");
|
|
3
|
+
const isProxySessionTable = (table)=>table[isProxy];
|
|
4
|
+
const SessionTable = (table, sessionId)=>{
|
|
5
|
+
const updates = new Map();
|
|
6
|
+
const eventEmitter = new EventEmitter();
|
|
7
|
+
const getSessionUpdates = ()=>updates;
|
|
8
|
+
const addEventListener = (event, handler)=>{
|
|
9
|
+
eventEmitter.on(event, handler);
|
|
10
|
+
};
|
|
11
|
+
const update = (key, columnName, value)=>{
|
|
12
|
+
const row = table.findByKey(key);
|
|
13
|
+
if (row) {
|
|
14
|
+
const tsIndex = table.map.vuuUpdatedTimestamp;
|
|
15
|
+
let updatesForRow = updates.get(key);
|
|
16
|
+
if (void 0 === updatesForRow) {
|
|
17
|
+
updatesForRow = {
|
|
18
|
+
lastUpdateTimestamp: row[tsIndex],
|
|
19
|
+
cellUpdates: {}
|
|
20
|
+
};
|
|
21
|
+
updates.set(key, updatesForRow);
|
|
22
|
+
}
|
|
23
|
+
updatesForRow.cellUpdates[columnName] = value;
|
|
24
|
+
const newRow = row.slice();
|
|
25
|
+
for (const [columnName, value] of Object.entries(updatesForRow.cellUpdates)){
|
|
26
|
+
const colIndex = table.map[columnName];
|
|
27
|
+
newRow[colIndex] = value;
|
|
28
|
+
}
|
|
29
|
+
eventEmitter.emit("update", newRow, columnName, sessionId);
|
|
30
|
+
} else console.warn(`SessionTable update row ${key} not found`);
|
|
31
|
+
};
|
|
32
|
+
return new Proxy(table, {
|
|
33
|
+
get (_obj, prop) {
|
|
34
|
+
if ("symbol" == typeof prop) {
|
|
35
|
+
if (prop === isProxy) return true;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if ("getSessionUpdates" === prop) return getSessionUpdates;
|
|
39
|
+
if ("on" === prop || "addEventListener" === prop) return addEventListener;
|
|
40
|
+
if ("update" === prop) return update;
|
|
41
|
+
if ("isProxy" === prop) return true;
|
|
42
|
+
return table[prop];
|
|
43
|
+
},
|
|
44
|
+
set () {
|
|
45
|
+
throw new TypeError("SessionTable is readonly");
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
export { SessionTable, isProxySessionTable };
|
package/src/Table.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { EventEmitter } from "@vuu-ui/vuu-utils";
|
|
2
|
+
class Table extends EventEmitter {
|
|
3
|
+
#data;
|
|
4
|
+
#dataMap;
|
|
5
|
+
#indexOfKey;
|
|
6
|
+
#index = new Map();
|
|
7
|
+
#schema;
|
|
8
|
+
constructor(schema, data, dataMap, updateGenerator){
|
|
9
|
+
super();
|
|
10
|
+
this.#data = data;
|
|
11
|
+
this.#dataMap = dataMap;
|
|
12
|
+
this.#schema = schema;
|
|
13
|
+
this.#indexOfKey = dataMap[schema.key];
|
|
14
|
+
this.buildIndex();
|
|
15
|
+
updateGenerator?.setTable(this);
|
|
16
|
+
updateGenerator?.setRange({
|
|
17
|
+
from: 0,
|
|
18
|
+
to: 100
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
buildIndex() {
|
|
22
|
+
for(let i = 0; i < this.#data.length; i++){
|
|
23
|
+
const key = this.#data[i][this.#indexOfKey];
|
|
24
|
+
this.#index.set(key, i);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
get data() {
|
|
28
|
+
return this.#data;
|
|
29
|
+
}
|
|
30
|
+
get map() {
|
|
31
|
+
return this.#dataMap;
|
|
32
|
+
}
|
|
33
|
+
get schema() {
|
|
34
|
+
return this.#schema;
|
|
35
|
+
}
|
|
36
|
+
get name() {
|
|
37
|
+
return this.#schema.table.table;
|
|
38
|
+
}
|
|
39
|
+
delete(key, emitEvent = true) {
|
|
40
|
+
const index = this.#index.get(key) ?? -1;
|
|
41
|
+
if (-1 !== index) {
|
|
42
|
+
this.#index.delete(key);
|
|
43
|
+
this.#data.splice(index, 1);
|
|
44
|
+
for(let i = index; i < this.#data.length; i++)this.#index.set(this.#data[i][this.#indexOfKey], i);
|
|
45
|
+
if (emitEvent) this.emit("delete", key);
|
|
46
|
+
} else throw Error(`[Table] delete key ${key} not found`);
|
|
47
|
+
}
|
|
48
|
+
insert(row, emitEvent = true) {
|
|
49
|
+
const index = this.#data.length;
|
|
50
|
+
this.#data.push(row);
|
|
51
|
+
const key = row[this.#indexOfKey];
|
|
52
|
+
this.#index.set(key, index);
|
|
53
|
+
if (emitEvent) this.emit("insert", row);
|
|
54
|
+
}
|
|
55
|
+
findByKey(key) {
|
|
56
|
+
const index = this.#index.get(key) ?? -1;
|
|
57
|
+
return this.#data[index];
|
|
58
|
+
}
|
|
59
|
+
update(key, columnName, value) {
|
|
60
|
+
const rowIndex = this.#data.findIndex((row)=>row[this.#indexOfKey] === key);
|
|
61
|
+
const colIndex = this.#dataMap[columnName];
|
|
62
|
+
if (-1 !== rowIndex) {
|
|
63
|
+
const row = this.#data[rowIndex];
|
|
64
|
+
const newRow = row.slice();
|
|
65
|
+
newRow[colIndex] = value;
|
|
66
|
+
const tsIndex = this.#dataMap.vuuUpdatedTimestamp;
|
|
67
|
+
if ("number" == typeof tsIndex) {
|
|
68
|
+
const lastUpdated = Date.now();
|
|
69
|
+
newRow[tsIndex] = lastUpdated;
|
|
70
|
+
}
|
|
71
|
+
this.#data[rowIndex] = newRow;
|
|
72
|
+
this.emit("update", newRow, columnName);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
updateRow(row) {
|
|
76
|
+
const key = row[this.#indexOfKey];
|
|
77
|
+
const rowIndex = this.#data.findIndex((row)=>row[this.#indexOfKey] === key);
|
|
78
|
+
if (-1 !== rowIndex) {
|
|
79
|
+
const tsIndex = this.#dataMap.vuuUpdatedTimestamp;
|
|
80
|
+
const lastUpdated = Date.now();
|
|
81
|
+
row[tsIndex] = lastUpdated;
|
|
82
|
+
this.#data[rowIndex] = row;
|
|
83
|
+
this.emit("update", row);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function buildDataColumnMapFromSchema(schema) {
|
|
88
|
+
return Object.values(schema.columns).reduce((map, col, index)=>({
|
|
89
|
+
...map,
|
|
90
|
+
[col.name]: index
|
|
91
|
+
}), {});
|
|
92
|
+
}
|
|
93
|
+
function buildDataColumnMap(schemas, tableName) {
|
|
94
|
+
return buildDataColumnMapFromSchema(schemas[tableName]);
|
|
95
|
+
}
|
|
96
|
+
export { Table, buildDataColumnMap, buildDataColumnMapFromSchema };
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { ArrayDataSource } from "@vuu-ui/vuu-data-local";
|
|
2
|
+
import { isInlineEditingSession, isRpcSuccess, isTypeaheadRequest } from "@vuu-ui/vuu-utils";
|
|
3
|
+
import { makeSuggestions } from "./makeSuggestions.js";
|
|
4
|
+
class TickingArrayDataSource extends ArrayDataSource {
|
|
5
|
+
#menuRpcServices;
|
|
6
|
+
#pendingVisualLink;
|
|
7
|
+
#rpcMenuServices;
|
|
8
|
+
#rpcServices;
|
|
9
|
+
#sessionTables;
|
|
10
|
+
#sessionDataSource = void 0;
|
|
11
|
+
#table;
|
|
12
|
+
#selectionLinkSubscribers;
|
|
13
|
+
#visualLinkService;
|
|
14
|
+
#getVisualLinks;
|
|
15
|
+
#vuuModule;
|
|
16
|
+
constructor({ data, getVisualLinks, rpcServices, rpcMenuServices, sessionTables, table, menu, visualLink, visualLinkService, vuuModule, ...arrayDataSourceProps }){
|
|
17
|
+
if (void 0 === data && void 0 === table) throw Error("TickingArrayDataSource must be constructed with data");
|
|
18
|
+
super({
|
|
19
|
+
...arrayDataSourceProps,
|
|
20
|
+
data: data ?? table?.data ?? [],
|
|
21
|
+
dataMap: table?.map
|
|
22
|
+
});
|
|
23
|
+
this._menu = menu;
|
|
24
|
+
this.#rpcMenuServices = rpcMenuServices;
|
|
25
|
+
this.#pendingVisualLink = visualLink;
|
|
26
|
+
this.#rpcServices = rpcServices;
|
|
27
|
+
this.#sessionTables = sessionTables;
|
|
28
|
+
this.#table = table;
|
|
29
|
+
this.#visualLinkService = visualLinkService;
|
|
30
|
+
this.#getVisualLinks = getVisualLinks;
|
|
31
|
+
this.#vuuModule = vuuModule;
|
|
32
|
+
if (table) {
|
|
33
|
+
this.tableSchema = table.schema;
|
|
34
|
+
table.on("insert", this.insert);
|
|
35
|
+
table.on("update", this.updateRowWithSessionCheck);
|
|
36
|
+
table.on("delete", this.deleteRow);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
updateRowWithSessionCheck = (row, columnName, sessionId)=>{
|
|
40
|
+
if (sessionId && sessionId === this.viewport) this.updateRow(row, columnName);
|
|
41
|
+
else if (sessionId) console.warn("THIS IS NEVER EXPECTED TO HAPPEN");
|
|
42
|
+
else this.#sessionDataSource || this.updateRow(row, columnName);
|
|
43
|
+
};
|
|
44
|
+
async subscribe(subscribeProps, callback) {
|
|
45
|
+
const subscription = super.subscribe(subscribeProps, callback);
|
|
46
|
+
if (this.#pendingVisualLink) {
|
|
47
|
+
this.visualLink = this.#pendingVisualLink;
|
|
48
|
+
this.#pendingVisualLink = void 0;
|
|
49
|
+
}
|
|
50
|
+
return subscription;
|
|
51
|
+
}
|
|
52
|
+
unsubscribe() {
|
|
53
|
+
super.unsubscribe();
|
|
54
|
+
this.#table = void 0;
|
|
55
|
+
}
|
|
56
|
+
set range(range) {
|
|
57
|
+
super.range = range;
|
|
58
|
+
}
|
|
59
|
+
get range() {
|
|
60
|
+
return super.range;
|
|
61
|
+
}
|
|
62
|
+
set links(links) {
|
|
63
|
+
super.links = links;
|
|
64
|
+
}
|
|
65
|
+
get links() {
|
|
66
|
+
return this.#getVisualLinks?.(this.table.table);
|
|
67
|
+
}
|
|
68
|
+
getSelectedRowIds() {
|
|
69
|
+
return Array.from(this.selectedRows);
|
|
70
|
+
}
|
|
71
|
+
handleSessionMessage = (msg)=>{
|
|
72
|
+
if ("subscribed" === msg.type) ;
|
|
73
|
+
else if ("viewport-update" === msg.type) {
|
|
74
|
+
if (void 0 !== msg.size && msg.size !== this.size) this.emit("resize", msg.size);
|
|
75
|
+
this.clientCallback?.(msg);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
createSessionDataSource(sessionTable) {
|
|
79
|
+
if (this.#vuuModule) return this.#vuuModule?.createDataSource(sessionTable.table, sessionTable.table);
|
|
80
|
+
throw Error("[TickingArrayDataSource] unable to createSessionDataSource, not constructed with VuuModule");
|
|
81
|
+
}
|
|
82
|
+
async beginEditSession(editSessionMode = "inline-all-rows") {
|
|
83
|
+
const rpcResponse = await this?.rpcRequest?.({
|
|
84
|
+
type: "RPC_REQUEST",
|
|
85
|
+
rpcName: "beginEditSession",
|
|
86
|
+
params: {
|
|
87
|
+
editSessionMode
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
if (isRpcSuccess(rpcResponse)) {
|
|
91
|
+
const { table: sessionTable } = rpcResponse.data;
|
|
92
|
+
if (!isInlineEditingSession(editSessionMode)) return this.#vuuModule?.createDataSource(sessionTable.table, sessionTable.table, this.config);
|
|
93
|
+
this.#sessionDataSource = this.#vuuModule?.createDataSource(sessionTable.table, sessionTable.table, this.config);
|
|
94
|
+
this.#sessionDataSource?.subscribe({
|
|
95
|
+
range: this.range
|
|
96
|
+
}, this.handleSessionMessage);
|
|
97
|
+
} else throw Error(`[VuuDataSource] beginEditSession ${rpcResponse.errorMessage}`);
|
|
98
|
+
}
|
|
99
|
+
async editCell(key, column, data) {
|
|
100
|
+
console.log(`[VuuDataSource] editCell ${this.#sessionDataSource?.viewport} rowKey ${key}, column ${column}, value ${data}`);
|
|
101
|
+
const rpcHost = this.#sessionDataSource ?? this;
|
|
102
|
+
return rpcHost.rpcRequest?.({
|
|
103
|
+
type: "RPC_REQUEST",
|
|
104
|
+
rpcName: "editCell",
|
|
105
|
+
params: {
|
|
106
|
+
column,
|
|
107
|
+
data,
|
|
108
|
+
key
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
async endEditSession(saveChanges = false) {
|
|
113
|
+
const type = "RPC_REQUEST";
|
|
114
|
+
const rpcName = "endEditSession";
|
|
115
|
+
const sessionDataSource = this.#sessionDataSource;
|
|
116
|
+
const rpcHost = sessionDataSource ?? this;
|
|
117
|
+
if (sessionDataSource) this.#sessionDataSource = void 0;
|
|
118
|
+
const rpcResponse = await rpcHost.rpcRequest?.(saveChanges ? {
|
|
119
|
+
type,
|
|
120
|
+
rpcName,
|
|
121
|
+
params: {
|
|
122
|
+
save: true
|
|
123
|
+
}
|
|
124
|
+
} : {
|
|
125
|
+
type,
|
|
126
|
+
rpcName,
|
|
127
|
+
params: {}
|
|
128
|
+
});
|
|
129
|
+
if (isRpcSuccess(rpcResponse)) {
|
|
130
|
+
sessionDataSource?.unsubscribe();
|
|
131
|
+
this.sendRowsToClient(true);
|
|
132
|
+
} else if (rpcResponse?.errorMessage === "stale update") {
|
|
133
|
+
sessionDataSource?.unsubscribe();
|
|
134
|
+
this.sendRowsToClient(true);
|
|
135
|
+
} else throw Error("unknown error");
|
|
136
|
+
}
|
|
137
|
+
async rpcRequest(rpcRequest) {
|
|
138
|
+
if (isTypeaheadRequest(rpcRequest)) {
|
|
139
|
+
const { params: { column, starts } } = rpcRequest;
|
|
140
|
+
const data = await this.getTypeaheadSuggestions(column, starts);
|
|
141
|
+
return {
|
|
142
|
+
type: "SUCCESS_RESULT",
|
|
143
|
+
data
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
{
|
|
147
|
+
const rpcService = this.#rpcServices?.find((service)=>service.rpcName === rpcRequest.rpcName);
|
|
148
|
+
if (rpcService) return rpcService.service({
|
|
149
|
+
...rpcRequest,
|
|
150
|
+
context: {
|
|
151
|
+
type: "VIEWPORT_CONTEXT",
|
|
152
|
+
viewPortId: this.viewport
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
throw Error(`[TickingArrayDataSource] no service to handle RPC request ${rpcRequest.rpcName}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async menuRpcCall(rpcRequest) {
|
|
159
|
+
const rpcService = this.#rpcMenuServices?.find((service)=>service.rpcName === rpcRequest.rpcName);
|
|
160
|
+
if (rpcService) return rpcService.service({
|
|
161
|
+
...rpcRequest,
|
|
162
|
+
vpId: this.viewport
|
|
163
|
+
});
|
|
164
|
+
throw Error(`[TickingArrayDataSource] menuRpcCall no service for ${rpcRequest.rpcName}`);
|
|
165
|
+
}
|
|
166
|
+
getTypeaheadSuggestions(column, pattern) {
|
|
167
|
+
if (this.#table) {
|
|
168
|
+
const columnIndex = this.columnMap[column];
|
|
169
|
+
if (void 0 !== columnIndex) return makeSuggestions(this.currentData, columnIndex, pattern);
|
|
170
|
+
console.warn(`[TickingArrayDataSource] getTypeaheadSuggestions. No column ${column}`);
|
|
171
|
+
return Promise.resolve([]);
|
|
172
|
+
}
|
|
173
|
+
throw Error("cannot call getTypeaheadSuggestions on TickingDataSource if table has not been provided");
|
|
174
|
+
}
|
|
175
|
+
get visualLink() {
|
|
176
|
+
return this._config.visualLink;
|
|
177
|
+
}
|
|
178
|
+
set visualLink(visualLink) {
|
|
179
|
+
this._config = {
|
|
180
|
+
...this._config,
|
|
181
|
+
visualLink
|
|
182
|
+
};
|
|
183
|
+
if (visualLink) {
|
|
184
|
+
const { parentClientVpId, link: { fromColumn, toColumn } } = visualLink;
|
|
185
|
+
if (this.viewport) this.#visualLinkService?.({
|
|
186
|
+
childVpId: this.viewport,
|
|
187
|
+
childColumnName: fromColumn,
|
|
188
|
+
type: "CREATE_VISUAL_LINK",
|
|
189
|
+
parentVpId: parentClientVpId,
|
|
190
|
+
parentColumnName: toColumn
|
|
191
|
+
}).then((response)=>{
|
|
192
|
+
this.emit("visual-link-created", response);
|
|
193
|
+
});
|
|
194
|
+
} else this.#visualLinkService?.({
|
|
195
|
+
childVpId: this.viewport,
|
|
196
|
+
type: "REMOVE_VISUAL_LINK"
|
|
197
|
+
}).then(()=>{
|
|
198
|
+
this.emit("visual-link-removed");
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
freeze() {
|
|
202
|
+
super.freeze();
|
|
203
|
+
}
|
|
204
|
+
unfreeze() {
|
|
205
|
+
super.unfreeze();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
export { TickingArrayDataSource };
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { generateNextBidAsk, nextRandomDouble, random } from "./data-utils.js";
|
|
2
|
+
const getNewValue = (value)=>{
|
|
3
|
+
const multiplier = random(0, 100) / 1000;
|
|
4
|
+
const direction = random(0, 10) >= 5 ? 1 : -1;
|
|
5
|
+
return value + value * multiplier * direction;
|
|
6
|
+
};
|
|
7
|
+
class BaseUpdateGenerator {
|
|
8
|
+
table;
|
|
9
|
+
range;
|
|
10
|
+
updating = false;
|
|
11
|
+
timer;
|
|
12
|
+
tickingColumns;
|
|
13
|
+
constructor(tickingColumns){
|
|
14
|
+
this.tickingColumns = tickingColumns;
|
|
15
|
+
}
|
|
16
|
+
setRange(range) {
|
|
17
|
+
this.range = range;
|
|
18
|
+
if (!this.updating && this.table) this.startUpdating();
|
|
19
|
+
}
|
|
20
|
+
setTable(table) {
|
|
21
|
+
this.table = table;
|
|
22
|
+
}
|
|
23
|
+
startUpdating() {
|
|
24
|
+
this.updating = true;
|
|
25
|
+
this.update();
|
|
26
|
+
}
|
|
27
|
+
stopUpdating() {
|
|
28
|
+
this.updating = false;
|
|
29
|
+
if (this.timer) {
|
|
30
|
+
window.clearTimeout(this.timer);
|
|
31
|
+
this.timer = void 0;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
update = ()=>{
|
|
35
|
+
if (this.range && this.table) {
|
|
36
|
+
const data = this.table?.data;
|
|
37
|
+
const { bid, ask, last, ...rest } = this.tickingColumns;
|
|
38
|
+
if (data && data?.length > 0) {
|
|
39
|
+
const maxRange = Math.min(this.range.to, data.length);
|
|
40
|
+
for(let rowIndex = this.range.from; rowIndex < maxRange; rowIndex++){
|
|
41
|
+
let updateCount = 0;
|
|
42
|
+
const shallUpdateRow = random(0, 10) < 2;
|
|
43
|
+
if (shallUpdateRow) {
|
|
44
|
+
const rowUpdates = this.table.data[rowIndex];
|
|
45
|
+
const row = data[rowIndex];
|
|
46
|
+
if (void 0 !== bid && void 0 !== ask) {
|
|
47
|
+
const { [bid]: currentBid, [ask]: currentAsk } = row;
|
|
48
|
+
const [newBid, newAsk] = generateNextBidAsk(currentBid, currentAsk, 10, 5, nextRandomDouble);
|
|
49
|
+
rowUpdates[ask] = newAsk;
|
|
50
|
+
rowUpdates[bid] = newBid;
|
|
51
|
+
if (void 0 !== last) {
|
|
52
|
+
const newLast = Math.round((currentAsk + (newAsk - currentAsk) / 2) * 100) / 100.0;
|
|
53
|
+
rowUpdates[last] = newLast;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const colIdx of Object.values(rest)){
|
|
57
|
+
const shallUpdateColumn = random(0, 10) < 5;
|
|
58
|
+
if (shallUpdateColumn) {
|
|
59
|
+
updateCount += 1;
|
|
60
|
+
const newValue = getNewValue(row[colIdx]);
|
|
61
|
+
if (this.table) rowUpdates[colIdx] = newValue;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (this.table && updateCount > 0) this.table.updateRow(rowUpdates);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (this.updating) this.timer = window.setTimeout(this.update, 500);
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export { BaseUpdateGenerator };
|