UNPKG

web-atoms-core

Version:
190 lines (160 loc) • 5.73 kB
import { parsePath, parsePathLists } from "./ExpressionParser"; export interface IAtomComponent { element: any; viewModel: any; localViewModel: any; data: any; app: { callLater: (f: () => void) => void; }; runAfterInit(f: () => void): void; setLocalValue(e: any, name: string, value: any): void; bindEvent(e: any, name: string, handler: any); bind(e: any, name: string, path: any, twoWays: boolean, converter: any, source?: any); } const isEvent = /^event/i; /** * Bindings needs to be cloned... */ export type bindingFunction<T extends IAtomComponent = IAtomComponent> = (control: T, e?: any) => any; function oneTime(name: string, b: Bind, control: IAtomComponent, e: any) { control.runAfterInit(() => { control.setLocalValue(e, name, b.sourcePath(control, e)); }); } function event(name: string, b: Bind, control: IAtomComponent, e: any) { control.runAfterInit(() => { if (isEvent.test(name)) { name = name.substr(5); name = (name[0].toLowerCase() + name.substr(1)); } control.bindEvent(e, name, (e1) => { return (b.sourcePath as any)(control, e1); }); }); } function oneWay(name: string, b: Bind, control: IAtomComponent, e: any, creator: any) { if (b.pathList) { control.bind(e, name, b.pathList , false, () => { // tslint:disable-next-line: ban-types return (b.sourcePath as Function).call(creator, control, e); }); return; } if (b.combined) { const a = { // it is `this` t: creator, // it is first parameter x: control }; control.bind(e, name, b.combined , false, () => { // tslint:disable-next-line: ban-types return (b.sourcePath as Function).call(creator, control, e); }, a); return; } if (b.thisPathList) { control.bind(e, name, b.thisPathList , false, () => { // tslint:disable-next-line: ban-types return (b.sourcePath as Function).call(creator, control, e); }, creator); return; } } function twoWays(name: string, b: Bind, control: IAtomComponent, e: any, creator: any) { control.bind(e, name, b.thisPathList || b.pathList, (b.eventList as any) || true, null, b.thisPathList ? creator : undefined); } function presenter(name: string, b: Bind, control: IAtomComponent, e: any) { const n = b.name || name; let c = control.element as any; while (c) { if (c.atomControl && c.atomControl[n] !== undefined) { break; } c = c._logicalParent || c.parentElement; } ((c && c.atomControl) || control)[n] = e; } export interface IData<T> extends IAtomComponent { data: T; } export interface IVM<T> extends IAtomComponent { viewModel: T; } export interface ILVM<T> extends IAtomComponent { localViewModel: T; } export interface IBinder<T extends IAtomComponent> { presenter(name?: string): Bind; event(handler: (control: T, e?: CustomEvent) => void): any; oneTime(path: bindingFunction<T>): Bind; oneWay(path: bindingFunction<T>): Bind; twoWays(path: bindingFunction<T>, events?: string[]): Bind; } export default class Bind { public static forData<D>(): IBinder<IData<D>> { return Bind as any; } public static forViewModel<D>(): IBinder<IVM<D>> { return Bind as any; } public static forLocalViewModel<D>(): IBinder<ILVM<D>> { return Bind as any; } public static presenter(name?: string): Bind { return new Bind(presenter, null, name as any); } // tslint:disable-next-line: ban-types public static event<T extends IAtomComponent = IAtomComponent>( sourcePath: (control: T, e?: CustomEvent) => void): any { return new Bind(event, sourcePath as any); } public static oneTime<T extends IAtomComponent = IAtomComponent>(sourcePath: bindingFunction<T>): Bind { return new Bind(oneTime, sourcePath); } public static oneWay<T extends IAtomComponent = IAtomComponent>(sourcePath: bindingFunction<T>): Bind { return new Bind(oneWay, sourcePath); } public static twoWays<T extends IAtomComponent = IAtomComponent>( sourcePath: bindingFunction<T>, events?: string[]): Bind { const b = new Bind(twoWays, sourcePath, null, events); if (!(b.thisPathList || b.pathList)) { throw new Error(`Failed to setup twoWay binding on ${sourcePath}`); } return b; } public readonly sourcePath: bindingFunction; public readonly pathList: string[][]; public readonly thisPathList: string[][]; public readonly combined: string[][]; constructor( public readonly setupFunction: ((name: string, b: Bind, c: IAtomComponent, e: any, self?: any) => void), sourcePath: bindingFunction, public readonly name?: string, public readonly eventList?: string[] ) { this.sourcePath = sourcePath; if (!this.sourcePath) { return; } if (Array.isArray(this.sourcePath)) { this.pathList = this.sourcePath as any; // this.setupFunction = null; } else { const lists = parsePathLists(this.sourcePath); if (lists.combined.length) { this.combined = lists.combined; } if (lists.pathList.length) { this.pathList = lists.pathList; } if (lists.thisPath.length) { this.thisPathList = lists.thisPath; } } } }