import { distinctUntilKeyChanged } from "rxjs/operators" ; import { emission } from "../shared/emission" ; import { KeyMap , KeyFunc , ChangeMap as _ChangeMap , diff } from "../util/keyed-array-diff" ; import { map } from "../pin/map" ; import { sink } from "../pin/sink" ; import { group } from "../pin/group" ; import { value } from "../pin/value" ; import { pipe } from "../pin/pipe" ; import { PinLike } from "../pin/pin-like" ; import { SimpleDeep , DeepAccessor , DeepChildFactory } from "./simple-deep" ; import { State , EqualityFunc } from "./state" ; import { TrackCallback } from "../shared/types" ; export interface ChangeMap extends _ChangeMap { /**
     *
     * Whether this is the first change map being calculated for a state or not.
     *
     */ initial : boolean ; } /**
 *
 * Represents a [keyed deep state](https://connective.dev/docs/deep#keyed-deep).
 *
 */ export class KeyedDeep extends SimpleDeep { private _keyMap : KeyMap = { } ; constructor ( state , keyfunc ) ; constructor ( accessor , keyfunc , compare ? ) ; constructor ( stateOrAccessor , keyfunc , compare ? ) ; param stateOrAccessor param keyfunc param compare constructor ( stateOrAccessor , readonly keyfunc , compare ? ) { super ( stateOrAccessor , compare , { inputs : [ 'value' ] , outputs : [ 'value' , 'changes' ] } ) ; } public key ( key ) ; public key < T extends SimpleDeep > ( key , factory ) ; param key param factory public key < T extends SimpleDeep > ( key , factory ? ) { let initialized = false ; let _this = this ; let _factory = factory || ( ( accessor , compare ) => new SimpleDeep ( accessor , compare ) ) ; return _factory ( { initial : ( _this . _keyMap [ key ] || { item : undefined } ) . item || ( Object . values ( this . value ) || [ ] ) . find ( ( i ) => this . keyfunc ( i ) == key ) , get : group ( _this . changes , _this . reemit ) . to ( map ( ( ) => ( _this . _keyMap [ key ] || { item : undefined } ) . item ) ) , set : sink ( ( v , context ) => { try { let _entry = _this . _keyMap [ key ] ; if ( _entry ) { _entry . item = v ; if ( _this . accessor ) { _this . value = ( Array . isArray ( _this . value ) ) ? Object . assign ( [ ] , _this . value , { [ _entry . index ] : v } ) : Object . assign ( { } , _this . value , { [ _entry . index ] : v } ) ; } else { _this . value [ _entry . index ] = v ; if ( initialized ) this . reemit . emit ( emission ( v , context ) ) ; else initialized = true ; } } } catch ( err ) { } } ) , bind ( track ) { return track ( this . set . subscribe ( ) ) ; } , } , this . state . compare ) ; } param key public index ( key ) { let initial ; if ( this . _keyMap [ key ] ) initial = this . _keyMap [ key ] . index ; else initial = ( ( Object . entries ( this . value ) || [ ] ) . find ( ( [ index , item ] ) => this . keyfunc ( item ) == key ) || [ - 1 , undefined ] ) [ 0 ] ; return group ( value ( initial ) , group ( this . changes , this . reemit ) . to ( map ( ( ) => ( this . _keyMap [ key ] || { index : - 1 } ) . index ) ) ) . to ( pipe ( distinctUntilKeyChanged ( 'value' ) ) ) ; } /**
     *
     * Will bind the underlying state, and cause deep change-detection to happen upon
     * changes of the state value. [Read this](https://connective.dev/docs/deep#change-detection)
     * for more information on deep change-detection.
     *
     * If this is a sub-state, also enables up-propagation
     * of state value, causing the parent state to pick up changes made to the value of this
     * sub-state. [Read this](https://connective.dev/docs/deep#two-way-keyed) for more details
     * and examples.
     *
     */ bind ( ) { super . bind ( ) ; this . track ( this . changes . subscribe ( ) ) ; return this ; } /**
     *
     * Keys that entities within the value of the state are identified with. Entity
     * `x` is said to be indetified with key `k` if `state.keyfunc(x) === k`.
     *
     * **WARNING** the keys will not be calculcated unless deep change-detection is active.
     * You can ensure deep change-detection is active by subscribing on `.changes` or
     * calling `.bind()`. [Read this](https://connective.dev/docs/deep#change-detection)
     * for more information on deep change-detection.
     *
     */ public get keys ( ) { return Object . keys ( this . _keyMap ) ; } /**
     *
     * A [pin](https://connective.dev/docs/pin) that emits changes to this deep state's list value.
     * These changes include entities being added to the list, removed from it or moved around in it.
     * [Read this](https://connective.dev/docs/deep#change-detection) for more information on
     * deep change-detection.
     *
     */ public get changes ( ) { return this . out ( 'changes' ) ; } protected createOutput ( label ) { if ( label === 'changes' ) { this . output ; let initial = true ; return this . state . to ( map ( ( value , done ) => { const result = diff ( value , this . _keyMap , this . keyfunc ) ; this . _keyMap = result . newKeyMap ; let changes = Object . assign ( { } , result . changes , { initial } ) ; initial = false ; done ( changes ) ; } ) ) ; } else return super . createOutput ( label ) ; } }