UNPKG

@domx/statecontroller

Version:

A StateController base class for handling data state changes on a LitElement

126 lines (111 loc) 4.34 kB
import { StateController } from "../StateController"; import { produce } from "immer"; export { produce } type NextFunction<TState> = (state:TState) => void; type TapFunction<TState> = (product:Product<TState>) => void; /** * A monad like class that promotes functional style * state changes with a StateController */ export class Product<TState> { /** * Creates a new Product object. */ static of<TState>(controller: StateController, stateName?:string) { return new Product<TState>(controller, stateName || "state"); } /** A lifting function that calls next */ static nextWith = <TState>(product: Product<TState>) => (fn:NextFunction<TState>) => product.continue(product).next(fn); /** A lifting function that calls tap */ static tapWith = <TState>(product: Product<TState>) => (fn:TapFunction<TState>) => product.continue(product).tap(fn); /** A chainable call to requestUpdate */ static requestUpdate = (event:Event|string) => <TState>(product: Product<TState>) => product.requestUpdate(event); /** A chainable call to dispatchHostEvent */ static dispatchHostEvent = (event:Event) => <TState>(product:Product<TState>) => product.dispatchHostEvent(event); /** A chainable call to next */ static next = <TState>(fn:NextFunction<TState>) => (product: Product<TState>) => product.next(fn); /** A chainable call to tap */ static tap = <TState>(fn:TapFunction<TState>) => (product: Product<TState>) => product.tap(fn); /** Returns the current state */ static getState = <TState>(product: Product<TState>) => product.getState(); /** * * @param controller * @param stateName */ constructor(controller: StateController, stateName:string) { this.controller = controller; this.stateName = stateName; } public controller:StateController; private stateName:string; /** * Returns a snapshot of the state property. * Similar to a flatten method. * @returns {TState} */ getState():TState { return this.controller[this.stateName] as TState; } /** * The primary mapping function. * @param {NextFunction<TState>} fn * @returns {Product<TState>} */ next(fn: NextFunction<TState>) { const state = this.controller[this.stateName] as TState; this.controller[this.stateName] = produce(state, (draft:TState) => fn(draft)) as TState; return this.continue(this); } /** * Use to perform branching operations. * @param {TapFunction<TState>} fn * @returns {Product<TState>} */ tap(fn: TapFunction<TState>) { fn(this); return this.continue(this); } /** * Enables running multiple 'next' functions in a pipe. * @param {Array<NextFunction<TState>>} fns a series of functions to call next on. * @returns {Product<TState>} */ pipeNext(...fns: Array<NextFunction<TState>>):Product<TState> { return fns.reduce((v:Product<TState>, f:NextFunction<TState>) => v.next(f), this); } /** * Enables running multiple 'tap' functions in a pipe. * @param {...any} fns a series of functions to call tap on. * @returns {Product<TState>} */ pipeTap(...fns: Array<TapFunction<TState>>):Product<TState> { return fns.reduce((v:Product<TState>, f:TapFunction<TState>) => v.tap(f), this); } /** * Calls requestUpdate on the controller. * @param {Event|string} event * @returns {Product<TState>} */ requestUpdate(event:Event|string) { this.controller.requestUpdate(event); return this.continue(this); } /** * Dispatches an event on the controllers host element. * @param event * @returns {Product<TState>} */ dispatchHostEvent(event:Event) { this.controller.host.dispatchEvent(event); return this.continue(this); } /** * Returns a new Product object based on the current one. * Convenient for mapping methods. * @returns {Product<TState>} */ continue(product:Product<TState>) { return Product.of<TState>(product.controller, product.stateName); } }