diff --git a/djx/build.gradle b/djx/build.gradle --- a/djx/build.gradle +++ b/djx/build.gradle @@ -40,7 +40,7 @@ configureTsMain { "dojo/*" : [ "typings/dojo/*" ], "dijit/*" : [ "typings/dijit/*" ] ]*/ - types = ["requirejs", "dojo-typings"] + types = ["requirejs", "@implab/dojo-typings"] } } diff --git a/djx/package-lock.json b/djx/package-lock.json --- a/djx/package-lock.json +++ b/djx/package-lock.json @@ -10,12 +10,12 @@ "license": "BSD-2-Clause", "devDependencies": { "@implab/core-amd": "^1.4.0", + "@implab/dojo-typings": "/home/sergey/projects/implabjs-dojo-typings/dojo-typings/build/npm/package/implab-dojo-typings-v1.0.0-rc4.tgz", "@types/chai": "4.1.3", "@types/requirejs": "2.1.31", "@types/yaml": "1.2.0", "chai": "4.2.0", "dojo": "1.16.0", - "dojo-typings": "~1.11.9", "eslint": "6.8.0", "requirejs": "2.3.6", "tslint": "^6.1.3", @@ -62,6 +62,13 @@ "integrity": "sha512-gaJX1mhri7YpmXDTAYELZnmTznzXYpk2AI7Decsttdi6xY+bqGgH24q0AFcKrx8RY2jfsFXxDdf0fITz2HpBbw==", "dev": true }, + "node_modules/@implab/dojo-typings": { + "version": "v1.0.0-rc4", + "resolved": "file:../../implabjs-dojo-typings/dojo-typings/build/npm/package/implab-dojo-typings-v1.0.0-rc4.tgz", + "integrity": "sha512-whWA/3shw3j4WFdmh5ml9BYZMj4KFruv1YXtz29W0LXMNpmj5nyyM8FJYYCdtvWJEGvnEUcUCQ0zsZ+Gh5ah9Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/chai": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.3.tgz", @@ -374,15 +381,6 @@ "integrity": "sha512-DUiXyoLK6vMF5BPr/qiMLTxDMfiM9qlzN1jxfDsVfuvB/CwhYpNxA/M4mbqKN8PCVGLmccXBJbfmFJPP5+zmzw==", "dev": true }, - "node_modules/dojo-typings": { - "version": "1.11.9", - "resolved": "https://registry.npmjs.org/dojo-typings/-/dojo-typings-1.11.9.tgz", - "integrity": "sha512-mh8w+Mau2Y1QfTEszEAdO7j6ycNhYxF/Ing6nAk1eUg6NxjeT0viVHjICMd9sU3U463vM2G+KfBBK5grk3/Mlw==", - "dev": true, - "dependencies": { - "@types/chai": "^4.0.4" - } - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1561,6 +1559,11 @@ "integrity": "sha512-gaJX1mhri7YpmXDTAYELZnmTznzXYpk2AI7Decsttdi6xY+bqGgH24q0AFcKrx8RY2jfsFXxDdf0fITz2HpBbw==", "dev": true }, + "@implab/dojo-typings": { + "version": "file:/home/sergey/projects/implabjs-dojo-typings/dojo-typings/build/npm/package/implab-dojo-typings-v1.0.0-rc4.tgz", + "integrity": "sha512-whWA/3shw3j4WFdmh5ml9BYZMj4KFruv1YXtz29W0LXMNpmj5nyyM8FJYYCdtvWJEGvnEUcUCQ0zsZ+Gh5ah9Q==", + "dev": true + }, "@types/chai": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.3.tgz", @@ -1821,15 +1824,6 @@ "integrity": "sha512-DUiXyoLK6vMF5BPr/qiMLTxDMfiM9qlzN1jxfDsVfuvB/CwhYpNxA/M4mbqKN8PCVGLmccXBJbfmFJPP5+zmzw==", "dev": true }, - "dojo-typings": { - "version": "1.11.9", - "resolved": "https://registry.npmjs.org/dojo-typings/-/dojo-typings-1.11.9.tgz", - "integrity": "sha512-mh8w+Mau2Y1QfTEszEAdO7j6ycNhYxF/Ing6nAk1eUg6NxjeT0viVHjICMd9sU3U463vM2G+KfBBK5grk3/Mlw==", - "dev": true, - "requires": { - "@types/chai": "^4.0.4" - } - }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", diff --git a/djx/package.json b/djx/package.json --- a/djx/package.json +++ b/djx/package.json @@ -25,7 +25,7 @@ "@types/yaml": "1.2.0", "chai": "4.2.0", "dojo": "1.16.0", - "dojo-typings": "~1.11.9", + "@implab/dojo-typings": "/home/sergey/projects/implabjs-dojo-typings/dojo-typings/build/npm/package/implab-dojo-typings-v1.0.0-rc4.tgz", "eslint": "6.8.0", "requirejs": "2.3.6", "tslint": "^6.1.3", diff --git a/djx/src/main/ts/declare.ts b/djx/src/main/ts/declare.ts --- a/djx/src/main/ts/declare.ts +++ b/djx/src/main/ts/declare.ts @@ -124,7 +124,7 @@ export function djclass(bc.bases, target.prototype); // bc - базовый класс, bc.prototype используется как super // при вызове базовых методов. Нужно создать bc.prototype 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 @@ -1,4 +1,4 @@ -import { djbase, djclass } from "../declare"; +import { AbstractConstructor, djbase, djclass } from "../declare"; import _WidgetBase = require("dijit/_WidgetBase"); import _AttachMixin = require("dijit/_AttachMixin"); import { Rendition, isNode, startupWidgets } from "./traits"; @@ -14,18 +14,16 @@ export interface EventArgs { composed?: boolean; } -export interface DjxWidgetBase { - set(key: K, value: Attrs[K]): this; - set(props: Partial): this; - get(key: K): Attrs[K]; +export interface DjxWidgetBase extends + _WidgetBase { +} - on(eventName: K, cb: (evt: Events[K]) => void): dojo.WatchHandle; - - emit(eventName: K, evt: Omit & EventArgs): void; +type _super = { + startup(): void; } @djclass -export abstract class DjxWidgetBase extends djbase(_WidgetBase, _AttachMixin) { +export abstract class DjxWidgetBase extends djbase<_super, _AttachMixin>(_WidgetBase, _AttachMixin) { buildRendering() { this.domNode = this.render().getDomNode(); @@ -46,7 +44,7 @@ export abstract class DjxWidgetBase( baseNode: T, - getAttrFunc: (baseNode: T, attr: string) => string, + getAttrFunc: (baseNode: T, attr: string) => any, // tslint:disable-next-line: ban-types attachFunc: (node: T, type: string, func?: Function) => dojo.Handle ): boolean { @@ -55,7 +53,7 @@ export abstract class DjxWidgetBase n.get(p), // callback to get a property of a widget + (n, p) => n.get(p as any), // callback to get a property of a widget (widget, type, callback) => { if (!callback) throw new Error("The callback must be specified"); 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,8 +1,10 @@ -import { IDestroyable } from "@implab/core-amd/interfaces"; +import { IDestroyable, IRemovable } 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"; type _WidgetBaseConstructor = typeof _WidgetBase; @@ -145,4 +147,58 @@ export function startupWidgets(target: N if(target.startup) target.startup(); } -} \ No newline at end of file +} + + +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; +} diff --git a/djx/src/main/tsconfig.json b/djx/src/main/tsconfig.json --- a/djx/src/main/tsconfig.json +++ b/djx/src/main/tsconfig.json @@ -7,7 +7,7 @@ "typings" ], "types": [ - "requirejs", "./typings", "dojo-typings" + "requirejs", "./typings", "@implab/dojo-typings" ] } } diff --git a/djx/src/main/typings/dijit.d.ts b/djx/src/main/typings/dijit.d.ts --- a/djx/src/main/typings/dijit.d.ts +++ b/djx/src/main/typings/dijit.d.ts @@ -1,14 +0,0 @@ -declare namespace dijit { - interface _WidgetBase { - - _started?: boolean; - - _set(key: K, value: this[K]): void; - } - - interface TooltipDialog { - - content: any; - - } -} 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 @@ -10,9 +10,13 @@ interface MyWidgetAttrs { } interface MyWidgetEvents { - "count-inc": Event; + "count-inc": Event & { + detail: number; + }; - "count-dec": Event; + "count-dec": Event & { + detail: number; + }; } @@ -37,14 +41,15 @@ export class MyWidget extends djbase(Djx } _onSubmit(e: Event) { - } _onIncClick(e: MouseEvent) { + this.set("counter", this.counter+1); + this.emit("count-inc", { bubbles: false }); } _onDecClick() { - this.emit("count-dec", { bubbles: false }); + this.emit("count-dec", { bubbles: false, detail: this.counter }); } } diff --git a/djx/src/test/tsconfig.json b/djx/src/test/tsconfig.json --- a/djx/src/test/tsconfig.json +++ b/djx/src/test/tsconfig.json @@ -8,6 +8,6 @@ "../main/ts", "../main/typings" ], - "types": ["requirejs", "../main/typings", "dojo-typings"] + "types": ["requirejs", "../main/typings", "@implab/dojo-typings"] } } \ No newline at end of file