Skip to content

Commit d4476da

Browse files
committed
feat(layered-storage): add a new storage for options
This is intended to replace the prototype plus hacks insanity used at the moment. However this is still WIP. There is no documentations yet and more testing is necessary. TODO: - Add off. - Write docs. - Probably some other things too. In a quick test I was able to resolve the issue discussed in visjs/vis-network#178 and visjs/vis-network#213 with just a few lines of code. Which is much better than the massive mess of weird hacks that doesn't work reliably anyway. Putting this to use will be a lot of work but fortunately it should be possible to do it in parts. I would first use this in LayoutEngine and EdgesHandler to resolve the forementioned issues and then probably one module at the time. Features: - Encapsulates options merging. - Explicit layer/segment/key structure instead of prototype chains. - Observable. - Overrides. * - Type safety in TypeScript. * Hierarchical layout is incompatible with smooth edges and has to disable them. Overrides combined with observing easily and elegently solve that. See the forementioned issues for current state.
1 parent cb12f54 commit d4476da

19 files changed

+1567
-0
lines changed

.eslintrc.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ module.exports = {
5353

5454
// Empty functions are useful sometimes.
5555
"@typescript-eslint/no-empty-function": "off",
56+
// This would be great if TypeScript was perfect but sometimes tsc can't infer the correct type.
57+
'@typescript-eslint/no-non-null-assertion': 'off',
5658
// This is really crazy given the functions in this package.
5759
"@typescript-eslint/no-explicit-any": "off",
5860
// These are hoisted, I have no idea why it reports them by default.

package-lock.json

Lines changed: 93 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@
125125
"rollup-plugin-copy-glob": "^0.3.1",
126126
"rollup-plugin-node-resolve": "^5.2.0",
127127
"rollup-plugin-terser": "^5.1.3",
128+
"rollup-plugin-typescript2": "^0.25.3",
128129
"semantic-release": "^16.0.0",
129130
"sinon": "^8.0.1",
130131
"snap-shot-it": "^7.9.1",

rollup.config.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import babel from "rollup-plugin-babel";
22
import commonjs from "rollup-plugin-commonjs";
33
import copyGlob from "rollup-plugin-copy-glob";
44
import resolve from "rollup-plugin-node-resolve";
5+
import typescript from "rollup-plugin-typescript2";
56
import { generateHeader } from "vis-dev-utils";
67
import { terser } from "rollup-plugin-terser";
78

@@ -12,6 +13,10 @@ const commonPlugins = [
1213
extensions: [".ts", ".js", ".json"]
1314
}),
1415
commonjs(),
16+
typescript({
17+
objectHashIgnoreUnknownHack: true,
18+
tsconfig: "tsconfig.code.json"
19+
}),
1520
babel({
1621
extensions: [".ts", ".js"],
1722
runtimeHelpers: true

src/layered-storage/common.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export type KeyRange = number | string | symbol;
2+
export type KeyValueLookup = Record<KeyRange, any>;
3+
export type LayerRange = number;
4+
export type Segment = boolean | number | object | string | symbol;
5+
6+
export type EventCallback<Key> = (keys: Key[]) => void;

src/layered-storage/core.ts

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { KeyValueLookup, LayerRange, Segment } from "./common";
2+
3+
const reverseNumeric = (a: number, b: number): number => b - a;
4+
5+
export class LayeredStorageCore<
6+
KeyValue extends KeyValueLookup,
7+
Layer extends LayerRange
8+
> {
9+
public monolithic = Symbol("Monolithic");
10+
11+
private _data = new Map<
12+
Layer,
13+
Map<Segment, Map<keyof KeyValue, KeyValue[keyof KeyValue]>>
14+
>();
15+
16+
private _layers: Layer[] = [];
17+
private readonly _segments = new Set<Segment>();
18+
19+
private readonly _topLevelCache = new Map<
20+
Segment,
21+
Map<keyof KeyValue, KeyValue[keyof KeyValue]>
22+
>();
23+
24+
private _updateCache(key: keyof KeyValue): void {
25+
segmentsLoop: for (const segment of this._segments) {
26+
const sCache =
27+
this._topLevelCache.get(segment) ||
28+
this._topLevelCache.set(segment, new Map()).get(segment)!;
29+
30+
sCache.delete(key);
31+
32+
for (const layer of this._layers) {
33+
const lsData = this._getLSData(layer, segment);
34+
if (lsData.has(key)) {
35+
sCache.set(key, lsData.get(key)!);
36+
continue segmentsLoop;
37+
}
38+
39+
const lmData = this._getLSData(layer, this.monolithic);
40+
if (lmData.has(key)) {
41+
sCache.set(key, lmData.get(key)!);
42+
continue segmentsLoop;
43+
}
44+
}
45+
}
46+
}
47+
48+
private _getLSData(
49+
layer: Layer,
50+
segment: Segment
51+
): Map<keyof KeyValue, KeyValue[keyof KeyValue]> {
52+
let lData = this._data.get(layer);
53+
if (lData == null) {
54+
lData = new Map();
55+
this._data.set(layer, lData);
56+
57+
this._layers = [...this._data.keys()].sort(reverseNumeric);
58+
}
59+
60+
let lsData = lData.get(segment);
61+
if (lsData == null) {
62+
lsData = new Map();
63+
lData.set(segment, lsData);
64+
65+
this._segments.add(segment);
66+
}
67+
68+
return lsData;
69+
}
70+
71+
public get<Key extends keyof KeyValue>(
72+
segment: Segment,
73+
key: Key
74+
): KeyValue[Key] | undefined {
75+
const sData = this._topLevelCache.get(segment);
76+
if (sData == null) {
77+
return;
78+
}
79+
80+
return sData.get(key);
81+
}
82+
83+
public has<Key extends keyof KeyValue>(segment: Segment, key: Key): boolean {
84+
const sData = this._topLevelCache.get(segment);
85+
if (sData == null) {
86+
return false;
87+
}
88+
89+
return sData.has(key);
90+
}
91+
92+
public set<Key extends keyof KeyValue>(
93+
layer: Layer,
94+
segment: Segment,
95+
key: Key,
96+
value: KeyValue[Key]
97+
): void {
98+
const lsData = this._getLSData(layer, segment);
99+
lsData.set(key, value);
100+
101+
this._updateCache(key);
102+
}
103+
104+
public delete<Key extends keyof KeyValue>(
105+
layer: Layer,
106+
segment: Segment,
107+
key: Key
108+
): boolean {
109+
const lsData = this._getLSData(layer, segment);
110+
const didItExist = lsData.delete(key);
111+
112+
this._updateCache(key);
113+
114+
return didItExist;
115+
}
116+
117+
public deleteSegmentData(segment: Segment): void {
118+
for (const lData of this._data.values()) {
119+
lData.delete(segment);
120+
}
121+
this._topLevelCache.delete(segment);
122+
this._segments.delete(segment);
123+
}
124+
}

src/layered-storage/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export {
2+
LayeredStorage,
3+
LayeredStorageSegmentTransaction,
4+
LayeredStorageTransaction
5+
} from "./layered-storage";
6+
export { LayeredStorageSegment } from "./segment";
7+
export { EventCallback, KeyValueLookup, LayerRange, Segment } from "./common";

0 commit comments

Comments
 (0)