-
-
Notifications
You must be signed in to change notification settings - Fork 151
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(atom): add Cursor, update interfaces, types, readme
- Loading branch information
1 parent
52c25a8
commit 04c3d59
Showing
5 changed files
with
174 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import * as api from "@thi.ng/api/api"; | ||
|
||
export type SwapFn<T> = (curr: T, ...args: any[]) => T; | ||
|
||
export interface ReadonlyAtom<T> extends | ||
api.IDeref<T>, | ||
api.IWatch<T> { | ||
} | ||
|
||
export interface IAtom<T> extends ReadonlyAtom<T> { | ||
reset(val: T): T; | ||
swap(fn: SwapFn<T>, ...args: any[]): T; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { IEquiv, Watch } from "@thi.ng/api/api"; | ||
import { IWatch } from "@thi.ng/api/mixins/iwatch"; | ||
|
||
import { IAtom, SwapFn } from "./api"; | ||
|
||
/** | ||
* Mutable wrapper for an (usually) immutable value. | ||
* Support for watches. | ||
*/ | ||
@IWatch | ||
export class Atom<T> implements | ||
IAtom<T>, | ||
IEquiv { | ||
|
||
protected value: T; | ||
|
||
constructor(val?: T) { | ||
this.value = val; | ||
} | ||
|
||
deref() { | ||
return this.value; | ||
} | ||
|
||
equiv(o: any) { | ||
return this === o; | ||
} | ||
|
||
reset(val: T) { | ||
const old = this.value; | ||
this.value = val; | ||
this.notifyWatches(old, val); | ||
return this.value; | ||
} | ||
|
||
swap(fn: SwapFn<T>, ...args: any[]) { | ||
const old = this.value; | ||
args.unshift(old); | ||
this.value = fn.apply(null, args); | ||
this.notifyWatches(old, this.value); | ||
return this.value; | ||
} | ||
|
||
// mixin stub | ||
addWatch(id: string, fn: Watch<T>) { | ||
return false; | ||
} | ||
|
||
// mixin stub | ||
removeWatch(id: string) { | ||
return false; | ||
} | ||
|
||
// mixin stub | ||
notifyWatches(oldState: T, newState: T) { } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { IID, IRelease, Watch } from "@thi.ng/api/api"; | ||
import { IAtom, SwapFn } from "./api"; | ||
import { Atom } from "./atom"; | ||
|
||
export class Cursor<T> implements | ||
IAtom<T>, | ||
IID<string>, | ||
IRelease { | ||
|
||
static NEXT_ID = 0; | ||
|
||
readonly id: string; | ||
parent: IAtom<any>; | ||
|
||
protected local: Atom<T>; | ||
protected lookup: (s: any) => T; | ||
protected selfUpdate: boolean; | ||
|
||
constructor(parent: IAtom<any>, lookup: (s: any) => T, update: (s: any, v: T) => any) { | ||
this.parent = parent; | ||
this.id = `cursor-${Cursor.NEXT_ID++}`; | ||
this.selfUpdate = false; | ||
this.local = new Atom<T>(lookup(parent.deref())); | ||
this.local.addWatch(this.id, (_, prev, curr) => { | ||
if (prev !== curr) { | ||
this.selfUpdate = true; | ||
parent.swap((state) => update(state, curr)); | ||
this.selfUpdate = false; | ||
} | ||
}); | ||
parent.addWatch(this.id, (_, prev, curr) => { | ||
if (!this.selfUpdate) { | ||
const cval = lookup(curr); | ||
if (cval !== lookup(prev)) { | ||
this.local.reset(cval); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
deref() { | ||
return this.local.deref(); | ||
} | ||
|
||
release() { | ||
this.local.removeWatch(this.id); | ||
this.parent.removeWatch(this.id); | ||
delete this.local; | ||
return true; | ||
} | ||
|
||
reset(val: T) { | ||
return this.local.reset(val); | ||
} | ||
|
||
swap(fn: SwapFn<T>, ...args: any[]) { | ||
return this.local.swap.apply(this.local, [fn, ...args]); | ||
} | ||
|
||
addWatch(id: string, fn: Watch<T>) { | ||
return this.local.addWatch(id, fn); | ||
} | ||
|
||
removeWatch(id: string): boolean { | ||
throw new Error("Method not implemented."); | ||
} | ||
|
||
notifyWatches(oldState: T, newState: T) { | ||
throw new Error("Method not implemented."); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,2 @@ | ||
import * as api from "@thi.ng/api/api"; | ||
import { IWatch } from "@thi.ng/api/mixins/iwatch"; | ||
|
||
/** | ||
* Mutable wrapper for an (usually) immutable value. | ||
* Support for watches. | ||
*/ | ||
@IWatch | ||
export class Atom<T> implements | ||
api.IDeref<T>, | ||
api.IEquiv, | ||
api.IWatch<T> { | ||
|
||
protected value: T; | ||
|
||
constructor(val?: T) { | ||
this.value = val; | ||
} | ||
|
||
public deref() { | ||
return this.value; | ||
} | ||
|
||
public equiv(o: any) { | ||
return this === o; | ||
} | ||
|
||
public reset(val: T) { | ||
const old = this.value; | ||
this.value = val; | ||
this.notifyWatches(old, val); | ||
return this.value; | ||
} | ||
|
||
public swap(fn: (curr: T, ...args: any[]) => T, ...args: any[]) { | ||
const old = this.value; | ||
args.unshift(old); | ||
this.value = fn.apply(null, args); | ||
this.notifyWatches(old, this.value); | ||
return this.value; | ||
} | ||
|
||
// mixin stub | ||
public addWatch(id: string, fn: (id: string, oldState: T, newState: T) => void) { | ||
return false; | ||
} | ||
|
||
// mixin stub | ||
public removeWatch(id: string) { | ||
return false; | ||
} | ||
|
||
// mixin stub | ||
public notifyWatches(oldState: T, newState: T) { } | ||
} | ||
export * from "./atom"; | ||
export * from "./cursor"; |