diff --git a/djx/src/main/ts/tsx.ts b/djx/src/main/ts/tsx.ts --- a/djx/src/main/ts/tsx.ts +++ b/djx/src/main/ts/tsx.ts @@ -1,8 +1,11 @@ -import { Constructor } from "@implab/core-amd/interfaces"; +import { Constructor, IDestroyable, IRemovable } from "@implab/core-amd/interfaces"; import { HtmlRendition } from "./tsx/HtmlRendition"; import { WidgetRendition } from "./tsx/WidgetRendition"; -import { isWidgetConstructor, Rendition } from "./tsx/traits"; +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") { @@ -38,3 +41,79 @@ export interface EventSelector { } 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; + }; diff --git a/djx/src/main/ts/tsx/traits.ts b/djx/src/main/ts/tsx/traits.ts --- a/djx/src/main/ts/tsx/traits.ts +++ b/djx/src/main/ts/tsx/traits.ts @@ -1,11 +1,8 @@ -import { IDestroyable, IRemovable } from "@implab/core-amd/interfaces"; +import { IDestroyable } from "@implab/core-amd/interfaces"; import { isDestroyable } from "@implab/core-amd/safe"; import _WidgetBase = require("dijit/_WidgetBase"); import registry = require("dijit/registry"); import dom = require("dojo/dom-construct"); -import Stateful = require("dojo/Stateful"); -import { FunctionRendition } from "./FunctionRendition"; -import { DjxWidgetBase } from "./DjxWidgetBase"; type _WidgetBaseConstructor = typeof _WidgetBase; @@ -149,80 +146,3 @@ export function startupWidgets(target: N target.startup(); } } - - -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; - }; diff --git a/djx/src/test/ts/view/MyWidget.tsx b/djx/src/test/ts/view/MyWidget.tsx --- a/djx/src/test/ts/view/MyWidget.tsx +++ b/djx/src/test/ts/view/MyWidget.tsx @@ -1,8 +1,7 @@ import { djbase, djclass, bind, prototype, AbstractConstructor } from "../declare"; import { DjxWidgetBase } from "../tsx/DjxWidgetBase"; -import { createElement } from "../tsx"; -import { on } from "../tsx/traits"; +import { createElement, on } from "../tsx"; interface MyWidgetAttrs { title: string;