diff --git a/djx/package.json b/djx/package.json --- a/djx/package.json +++ b/djx/package.json @@ -23,9 +23,8 @@ "@types/chai": "4.1.3", "@types/requirejs": "2.1.31", "@types/yaml": "1.2.0", - "chai": "4.2.0", "dojo": "1.16.0", - "@implab/dojo-typings": "/home/sergey/projects/implabjs-dojo-typings/dojo-typings/build/npm/package/implab-dojo-typings-v1.0.0-rc4.tgz", + "@implab/dojo-typings": "1.0.0", "eslint": "6.8.0", "requirejs": "2.3.6", "tslint": "^6.1.3", diff --git a/djx/src/main/ts/tsx/DjxWidgetBase.ts b/djx/src/main/ts/tsx/DjxWidgetBase.ts --- a/djx/src/main/ts/tsx/DjxWidgetBase.ts +++ b/djx/src/main/ts/tsx/DjxWidgetBase.ts @@ -16,6 +16,8 @@ export interface EventArgs { export interface DjxWidgetBase extends _WidgetBase { + + readonly _eventMap: Events & GlobalEventHandlersEventMap; } type _super = { @@ -25,6 +27,11 @@ type _super = { @djclass export abstract class DjxWidgetBase extends djbase<_super, _AttachMixin>(_WidgetBase, _AttachMixin) { + _eventHandlers: Array<{ + eventName: keyof Events, + handlerMethod: string; + }> = []; + buildRendering() { this.domNode = this.render().getDomNode(); super.buildRendering(); @@ -42,6 +49,13 @@ export abstract class DjxWidgetBase; + private _connectEventHandlers() { + this._eventHandlers.forEach(({eventName, handlerMethod}) => { + if (typeof this[handlerMethod as keyof this] === "function") + this.on(eventName, this[handlerMethod] as Function); + }); + } + _processTemplateNode( baseNode: T, getAttrFunc: (baseNode: T, attr: string) => any, 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 @@ -5,6 +5,7 @@ import registry = require("dijit/registr import dom = require("dojo/dom-construct"); import Stateful = require("dojo/Stateful"); import { FunctionRendition } from "./FunctionRendition"; +import { DjxWidgetBase } from "./DjxWidgetBase"; type _WidgetBaseConstructor = typeof _WidgetBase; @@ -16,7 +17,7 @@ export interface Rendition = Rendition; @@ -131,7 +132,7 @@ export function emptyNode(target: Node) /** This function starts all widgets inside the DOM node if the target is a node * or starts widget itself if the target is the widget. If the specified node * associated with the widget that widget will be started. - * + * * @param target DOM node to find and start widgets or the widget itself. */ export function startupWidgets(target: Node | _WidgetBase, skipNode?: Node) { @@ -144,7 +145,7 @@ export function startupWidgets(target: N registry.findWidgets(target, skipNode).forEach(x => x.startup()); } } else { - if(target.startup) + if (target.startup) target.startup(); } } @@ -163,11 +164,11 @@ type CleanFn = (instance: IRemovable | I * @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 ( +export function watch( target: W, prop: K, render: (model: W[K]) => any, - cleanupOrOwner?: {own: CleanFn} | CleanFn + cleanupOrOwner?: { own: CleanFn } | CleanFn ): Rendition; /** * Observers the property and calls render callback each change. @@ -182,13 +183,13 @@ export function watch[K]) => any, - cleanupOrOwner?: {own: CleanFn} | CleanFn + cleanupOrOwner?: { own: CleanFn } | CleanFn ): Rendition; export function watch & string>( target: T, prop: K, - render: (model: StatefulProps[K]) => any, - cleanupOrOwner: {own: CleanFn} | CleanFn = () => {} + 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) @@ -202,3 +203,20 @@ export function watch = W extends DjxWidgetBase ? EM : never; + +export type HandlerType = W extends { + on(eventName: E, handler: infer H): any; +} ? H : never; + +export const on = (eventName: E) => + ( + target: T, + key: K, + descriptor: PropertyDescriptor + ): any => { + target. + }; 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 @@ -2,6 +2,7 @@ import { djbase, djclass, bind, prototyp import { DjxWidgetBase } from "../tsx/DjxWidgetBase"; import { createElement } from "../tsx"; +import { HandlerType, on, WidgetEvents } from "../tsx/traits"; interface MyWidgetAttrs { title: string; @@ -40,11 +41,17 @@ export class MyWidget extends djbase(Djx ; } + postCreate() { + super.postCreate(); + + this.on("click", () => {}); + } + _onSubmit(e: Event) { } _onIncClick(e: MouseEvent) { - this.set("counter", this.counter+1); + this.set("counter", this.counter + 1); this.emit("count-inc", { bubbles: false }); } @@ -52,4 +59,15 @@ export class MyWidget extends djbase(Djx _onDecClick() { this.emit("count-dec", { bubbles: false, detail: this.counter }); } + + @on("count-inc") + _onCounterInc(evt: Event & { detail: number; }) { + + } } + +declare const w: MyWidget; +w.on("click", () => {}); + +declare const ev: WidgetEvents; +ev["count-inc"]; diff --git a/djx/src/tsconfig.json b/djx/src/tsconfig.json --- a/djx/src/tsconfig.json +++ b/djx/src/tsconfig.json @@ -6,6 +6,7 @@ "types": [], "experimentalDecorators": true, "jsxFactory": "createElement", + "target": "ES5", //"skipLibCheck": true, "jsx": "react", "lib": ["es5", "es2015.promise", "es2015.symbol", "es2015.iterable", "dom", "scripthost"]