import { Constructor, IDestroyable, IRemovable } from "@implab/core-amd/interfaces"; import { HtmlRendition } from "./tsx/HtmlRendition"; import { WidgetRendition } from "./tsx/WidgetRendition"; import { destroy, isWidgetConstructor, Rendition } from "./tsx/traits"; import { FunctionRendition } from "./tsx/FunctionRendition"; import Stateful = require("dojo/Stateful"); import _WidgetBase = require("dijit/_WidgetBase"); import { DjxWidgetBase } from "./tsx/DjxWidgetBase"; export function createElement Element)>(elementType: T, ...args: any[]): Rendition { if (typeof elementType === "string") { const ctx = new HtmlRendition(elementType); if (args) args.forEach(x => ctx.visitNext(x)); return ctx; } else if (isWidgetConstructor(elementType)) { const ctx = new WidgetRendition(elementType); if (args) args.forEach(x => ctx.visitNext(x)); return ctx; } else if (typeof elementType === "function") { const ctx = new FunctionRendition(elementType as (props: any) => Element); if (args) args.forEach(x => ctx.visitNext(x)); return ctx; } else { throw new Error(`The element type '${elementType}' is unsupported`); } } export interface EventDetails { detail: T; } export interface EventSelector { selectorTarget: HTMLElement; target: HTMLElement; } export type DojoMouseEvent = MouseEvent & EventSelector & EventDetails; type StatefulProps = T extends Stateful ? A : never; type CleanFn = (instance: IRemovable | IDestroyable) => void; /** * Observers the property and calls render callback each change. * * @param target The target object which property will be observed. * @param prop The name of the property. * @param render The callback which will be called every time the value is changed * @param cleanupOrOwner The object with method `own` or an callback to register lifecycle for the observer. * @returns Rendition which is created instantly */ export function watch( target: W, prop: K, render: (model: W[K]) => any, cleanupOrOwner?: { own: CleanFn } | CleanFn ): Rendition; /** * Observers the property and calls render callback each change. * * @param target The target object which property will be observed. * @param prop The name of the property. * @param render The callback which will be called every time the value is changed * @param cleanupOrOwner The object with method `own` or an callback to register lifecycle for the observer. * @returns Rendition which is created instantly */ export function watch>( target: T, prop: K, render: (model: StatefulProps[K]) => any, cleanupOrOwner?: { own: CleanFn } | CleanFn ): Rendition; export function watch & string>( target: T, prop: K, render: (model: StatefulProps[K]) => any, cleanupOrOwner: { own: CleanFn } | CleanFn = () => { } ) { let rendition = new FunctionRendition(() => render(target.get(prop))); const _own = cleanupOrOwner instanceof Function ? cleanupOrOwner : (x: IRemovable) => cleanupOrOwner.own(x); _own(target.watch(prop, (_name, oldValue, newValue) => { if (oldValue !== newValue) { const newRendition = new FunctionRendition(() => render(newValue)); newRendition.placeAt(rendition.getDomNode(), "replace"); destroy(rendition.getDomNode()); rendition = newRendition; } })); return rendition; } /** Decorates the method which will be registered as the handle for the specified event. * This decorator can be applied to DjxWidgetBase subclass methods. * * ``` * @on("click") * _onClick(eventObj: MouseEvent) { * // ... * } * ``` */ export const on = (...eventNames: E[]) => , EV extends Event >( target: T, key: K, _descriptor: TypedPropertyDescriptor<(eventObj: EV) => void> | TypedPropertyDescriptor<() => void> ): any => { const handlers = eventNames.map(eventName => ({ eventName, handlerMethod: key })); target._eventHandlers = target._eventHandlers ? target._eventHandlers.concat(handlers) : handlers; };