import * as B from "baconjs" ; import * as L from "./lens" ; import { getCurrentValue } from "./currentvalue" ; export interface Atom < A > extends B . Property < A > { set ( newValue ) : this ; get ( ) : A ; modify ( fn ) : this ; view < K extends keyof A > ( key ) : K extends number ? Atom < A [ K ] | undefined > : Atom < A [ K ] > ; view < B > ( lens ) : Atom < B > ; freezeUnless < E extends A > ( fn ) : Atom < E > ; freezeUnless ( fn ) : Atom < A > ; eager : boolean ; } param initial export function atom < A > ( initial ) ; param input param onChange export function atom < A > ( input , onChange ) ; export function atom < A > ( x , y ? ) { if ( arguments . length == 1 ) { const initial = x as A ; const bus = new B . Bus < ( a : A ) => A > ( ) ; const theAtom = bus . scan ( initial , ( v , fn ) => { const newValue = fn ( v ) ; ( theAtom as any ) . value = newValue ; return newValue ; } ) . skipDuplicates ( ( a , b ) => a === b ) ; theAtom . value = initial ; const get = ( ) => theAtom . value ; const modify = ( f ) => { bus . push ( f ) ; return theAtom ; } ; const set = ( a ) => { bus . push ( ( ) => a ) ; return theAtom ; } ; return mkAtom < A > ( theAtom , get , modify , set , true ) ; } else { const property = x as B . Property < A > ; const eager = ( property as any ) . eager ; const onChange = y ; const theAtom = property . map ( ( x ) => x ) . skipDuplicates ( ( a , b ) => a === b ) ; const get = ( ) => getCurrentValue ( property ) ; const set = ( newValue ) => { onChange ( newValue ) ; return theAtom as Atom < A > ; } ; const modify = ( f ) => { set ( f ( get ( ) ) ) ; return theAtom as Atom < A > ; } ; return mkAtom ( theAtom , get , modify , set , eager ) ; } } export function isAtom < A > ( x ) { return ! ! ( x instanceof B . Property && ( x as any ) . get && ( x as any ) . freezeUnless ) ; } function mkAtom < A > ( observable , get , modify , set , eager ) { const theAtom = observable as Atom < A > ; theAtom . set = set ; theAtom . modify = modify ; theAtom . get = get ; theAtom . freezeUnless = ( freezeUnlessFn ) => { let previousValue = getCurrentValue ( observable ) ; if ( ! freezeUnlessFn ( previousValue ) ) { throw Error ( "Cannot create frozen atom with initial value not passing the given filter function" ) ; } let freezingProperty = theAtom . filter ( freezeUnlessFn ) . doAction ( ( v ) => { previousValue = v ; } ) ; freezingProperty . eager = true ; let onChange = ( newValue ) => theAtom . set ( newValue ) ; let fa = atom ( freezingProperty , onChange ) ; fa . get = ( ) => { let wouldBeValue = getCurrentValue ( observable ) ; if ( freezeUnlessFn ( wouldBeValue ) ) { previousValue = wouldBeValue ; } return previousValue ; } ; return fa ; } ; theAtom . view = ( view ) : any => { if ( typeof view === "string" ) { return lensedAtom ( theAtom , L . prop < A , any > ( view ) ) ; } else if ( typeof view === "number" ) { return lensedAtom ( theAtom , L . item ( view as number ) as any ) ; } else { const lens = view ; return lensedAtom ( theAtom , lens ) ; } } ; theAtom . eager = eager ; if ( eager ) theAtom . subscribe ( ( ) => { } ) ; return theAtom ; } function lensedAtom < A , B > ( root , lens ) { const theAtom = root . map ( ( value ) => lens . get ( value ) ) . skipDuplicates ( ) as any ; const get = ( ) => lens . get ( root . get ( ) ) ; const modify = ( fn ) => { root . modify ( ( currentRootValue ) => { const currentChildValue = lens . get ( currentRootValue ) ; const newChildValue = fn ( currentChildValue ) ; const newRootValue = lens . set ( currentRootValue , newChildValue ) ; return newRootValue ; } ) ; return theAtom ; } ; const set = ( b ) => { const currentRootValue = root . get ( ) ; const newRootValue = lens . set ( currentRootValue , b ) ; root . set ( newRootValue ) ; return theAtom ; } ; return mkAtom ( theAtom , get , modify , set , root . eager ) ; }