From 04d5b9bfda15b085a7aea8377ec9fb0a62681b06 Mon Sep 17 00:00:00 2001 From: RMidhunSuresh Date: Fri, 18 Feb 2022 16:07:18 +0530 Subject: [PATCH] WIP - 2 --- src/domain/navigation/Navigation.ts | 82 ++++++++++++++++------------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/src/domain/navigation/Navigation.ts b/src/domain/navigation/Navigation.ts index da6841f8..cb6637a9 100644 --- a/src/domain/navigation/Navigation.ts +++ b/src/domain/navigation/Navigation.ts @@ -34,11 +34,11 @@ type SegmentType = { "member": string; }; -export class Navigation { +export class Navigation { private readonly _allowsChild: AllowsChild; - private _path: Path; - private readonly _observables: Map = new Map(); - private readonly _pathObservable: ObservableValue; + private _path: Path; + private readonly _observables: Map> = new Map(); + private readonly _pathObservable: ObservableValue>; constructor(allowsChild: AllowsChild) { this._allowsChild = allowsChild; @@ -46,11 +46,11 @@ export class Navigation { this._pathObservable = new ObservableValue(this._path); } - get pathObservable(): ObservableValue { + get pathObservable(): ObservableValue> { return this._pathObservable; } - get path(): Path { + get path(): Path { return this._path; } @@ -61,7 +61,7 @@ export class Navigation { } } - applyPath(path: Path): void { + applyPath(path: Path): void { // Path is not exported, so you can only create a Path through Navigation, // so we assume it respects the allowsChild rules const oldPath = this._path; @@ -85,7 +85,7 @@ export class Navigation { this._pathObservable.set(this._path); } - observe(type: keyof SegmentType): SegmentObservable { + observe(type: keyof T): SegmentObservable { let observable = this._observables.get(type); if (!observable) { observable = new SegmentObservable(this, type); @@ -94,7 +94,7 @@ export class Navigation { return observable; } - pathFrom(segments: Segment[]): Path { + pathFrom(segments: Segment[]): Path { let parent: Segment | undefined; let i: number; for (i = 0; i < segments.length; i += 1) { @@ -106,12 +106,12 @@ export class Navigation { return new Path(segments, this._allowsChild); } - segment(type: T, value: SegmentType[T]): Segment { + segment(type: K, value: T[K]): Segment { return new Segment(type, value); } } -function segmentValueEqual(a?: SegmentType[keyof SegmentType], b?: SegmentType[keyof SegmentType]): boolean { +function segmentValueEqual(a?: T[keyof T], b?: T[keyof T]): boolean { if (a === b) { return true; } @@ -129,27 +129,27 @@ function segmentValueEqual(a?: SegmentType[keyof SegmentType], b?: SegmentType[k } -export class Segment { +export class Segment { constructor( - public type: T, - public value: SegmentType[T] | true = value === undefined ? true : value + public type: K, + public value: T[K] = (value === undefined ? true : value) as T[K] ) {} } -class Path { - private readonly _segments: Segment[]; +class Path { + private readonly _segments: Segment[]; private readonly _allowsChild: AllowsChild; - constructor(segments: Segment[] = [], allowsChild: AllowsChild) { + constructor(segments: Segment[] = [], allowsChild: AllowsChild) { this._segments = segments; this._allowsChild = allowsChild; } - clone(): Path { + clone(): Path { return new Path(this._segments.slice(), this._allowsChild); } - with(segment: Segment): Path | null { + with(segment: Segment): Path | null { let index = this._segments.length - 1; do { if (this._allowsChild(this._segments[index], segment)) { @@ -164,7 +164,7 @@ class Path { return null; } - until(type: keyof SegmentType): Path { + until(type: keyof T): Path { const index = this._segments.findIndex(s => s.type === type); if (index !== -1) { return new Path(this._segments.slice(0, index + 1), this._allowsChild) @@ -172,11 +172,11 @@ class Path { return new Path([], this._allowsChild); } - get(type: keyof SegmentType): Segment | undefined { + get(type: keyof T): Segment | undefined { return this._segments.find(s => s.type === type); } - replace(segment: Segment): Path | null { + replace(segment: Segment): Path | null { const index = this._segments.findIndex(s => s.type === segment.type); if (index !== -1) { const parent = this._segments[index - 1]; @@ -192,7 +192,7 @@ class Path { return null; } - get segments(): Segment[] { + get segments(): Segment[] { return this._segments; } } @@ -201,19 +201,19 @@ class Path { * custom observable so it always returns what is in navigation.path, even if we haven't emitted the change yet. * This ensures that observers of a segment can also read the most recent value of other segments. */ -class SegmentObservable extends BaseObservableValue { - private readonly _navigation: Navigation; - private _type: keyof SegmentType; - private _lastSetValue?: SegmentType[keyof SegmentType]; +class SegmentObservable extends BaseObservableValue { + private readonly _navigation: Navigation; + private _type: keyof T; + private _lastSetValue?: T[keyof T]; - constructor(navigation: Navigation, type: keyof SegmentType) { + constructor(navigation: Navigation, type: keyof T) { super(); this._navigation = navigation; this._type = type; this._lastSetValue = navigation.path.get(type)?.value; } - get(): SegmentType[keyof SegmentType] | undefined { + get(): T[keyof T] | undefined { const path = this._navigation.path; const segment = path.get(this._type); const value = segment?.value; @@ -222,7 +222,7 @@ class SegmentObservable extends BaseObservableValue(newValue, this._lastSetValue)) { this._lastSetValue = newValue; this.emit(newValue); } @@ -249,7 +249,7 @@ export function tests() { } function observeTypes(nav, types) { - const changes = []; + const changes: {type:string, value:any}[] = []; for (const type of types) { nav.observe(type).subscribe(value => { changes.push({type, value}); @@ -258,6 +258,12 @@ export function tests() { return changes; } + type SegmentType = { + "foo": number; + "bar": number; + "baz": number; + } + return { "applying a path emits an event on the observable": assert => { const nav = createMockNavigation(); @@ -275,18 +281,18 @@ export function tests() { assert.equal(changes[1].value, 8); }, "path.get": assert => { - const path = new Path([new Segment("foo", 5), new Segment("bar", 6)], () => true); - assert.equal(path.get("foo").value, 5); - assert.equal(path.get("bar").value, 6); + const path = new Path([new Segment("foo", 5), new Segment("bar", 6)], () => true); + assert.equal(path.get("foo")!.value, 5); + assert.equal(path.get("bar")!.value, 6); }, "path.replace success": assert => { - const path = new Path([new Segment("foo", 5), new Segment("bar", 6)], () => true); + const path = new Path([new Segment("foo", 5), new Segment("bar", 6)], () => true); const newPath = path.replace(new Segment("foo", 1)); - assert.equal(newPath.get("foo").value, 1); - assert.equal(newPath.get("bar").value, 6); + assert.equal(newPath!.get("foo")!.value, 1); + assert.equal(newPath!.get("bar")!.value, 6); }, "path.replace not found": assert => { - const path = new Path([new Segment("foo", 5), new Segment("bar", 6)], () => true); + const path = new Path([new Segment("foo", 5), new Segment("bar", 6)], () => true); const newPath = path.replace(new Segment("baz", 1)); assert.equal(newPath, null); }