# HG changeset patch # User cin # Date 2021-10-24 23:00:50 # Node ID 43e5b81813465237e0519d4614d09fde31798f06 # Parent a842d051d22782ef1040e566b04bda54fd321cce # Parent 77da935ee870e3c326aa32e8314ec79d6060f3dc Слияние diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -25,3 +25,12 @@ d4f0cdae9577caa605d9317d0ceb93f7b67895f3 1a0018655d1c3dafc16ae50971cb535b8555e246 v1.1.0 ff3695b0a48f00155ad6c3d7e000fe185ee6f95a v1.1.1 c276b9b7fa833242d1bf75eeeaae9924feee51e8 v1.1.2 +fdde09e66c009f4950ec69a393a8e789725da758 v1.2.0 +16678c6055f20ce1a49bdca5e6dabce88ee8deed v1.2.1 +bc7556143fe536e3df374bc0070146311884284e v1.2.2 +e5bb5e80ce96fc4ca0fbbf0b5390443a616e99fa v1.2.3 +cc5be30e84f8a0d9a8e25447651576d9e46ab154 v1.2.4 +2807ab11174c7446830d91d5f1b652a18c6ecae5 v1.2.5 +35a7b6319ebe24973fe10b8d82c19d3d86857b4e v1.2.6 +70058deb750dc18fcd9be83c28cb8371530fffd8 v1.2.7 +367f8caa5bf8fd2d4a56a6cff40be31ab82a2727 v1.2.8 diff --git a/djx/.eslintrc.json b/djx/.eslintrc.json new file mode 100644 --- /dev/null +++ b/djx/.eslintrc.json @@ -0,0 +1,25 @@ +{ + "env": { + "browser": true, + "amd": true + }, + "parserOptions": { + "ecmaFeatures": { + "jsx": true + }, + "sourceType": "script" + }, + "extends": "eslint:recommended", + "rules": { + "no-const-assign": "warn", + "no-this-before-super": "warn", + "no-undef": "error", + "no-unreachable": "warn", + "no-unused-vars": "warn", + "constructor-super": "warn", + "valid-typeof": "warn", + "semi" : "warn", + "no-invalid-this" : "error", + "no-console": "off" + } +} \ No newline at end of file diff --git a/djx/build.gradle b/djx/build.gradle --- a/djx/build.gradle +++ b/djx/build.gradle @@ -13,6 +13,7 @@ typescript { module = "amd" it.target = "es5" experimentalDecorators = true + noUnusedLocals = true jsx = "react" jsxFactory = "createElement" moduleResolution = "node" @@ -40,7 +41,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,11 @@ "license": "BSD-2-Clause", "devDependencies": { "@implab/core-amd": "^1.4.0", + "@implab/dojo-typings": "1.0.0", "@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 +61,12 @@ "integrity": "sha512-gaJX1mhri7YpmXDTAYELZnmTznzXYpk2AI7Decsttdi6xY+bqGgH24q0AFcKrx8RY2jfsFXxDdf0fITz2HpBbw==", "dev": true }, + "node_modules/@implab/dojo-typings": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@implab/dojo-typings/-/dojo-typings-1.0.0.tgz", + "integrity": "sha512-B2kvlKJgvyIQAl/k1pVyNmtp4ADvBDCs4Lw/qfBC+Sz/ft4c7EuRXmN/+2dhWrd6A5SMjEgex5oeq6Ja2ntrig==", + "dev": true + }, "node_modules/@types/chai": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.3.tgz", @@ -152,15 +157,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -204,23 +200,6 @@ "node": ">=6" } }, - "node_modules/chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -241,15 +220,6 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, - "node_modules/check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -329,18 +299,6 @@ "ms": "^2.1.1" } }, - "node_modules/deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=0.12" - } - }, "node_modules/deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -374,15 +332,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", @@ -640,15 +589,6 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -1020,15 +960,6 @@ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, - "node_modules/pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -1427,15 +1358,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -1561,6 +1483,12 @@ "integrity": "sha512-gaJX1mhri7YpmXDTAYELZnmTznzXYpk2AI7Decsttdi6xY+bqGgH24q0AFcKrx8RY2jfsFXxDdf0fITz2HpBbw==", "dev": true }, + "@implab/dojo-typings": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@implab/dojo-typings/-/dojo-typings-1.0.0.tgz", + "integrity": "sha512-B2kvlKJgvyIQAl/k1pVyNmtp4ADvBDCs4Lw/qfBC+Sz/ft4c7EuRXmN/+2dhWrd6A5SMjEgex5oeq6Ja2ntrig==", + "dev": true + }, "@types/chai": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.3.tgz", @@ -1636,12 +1564,6 @@ "sprintf-js": "~1.0.2" } }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -1676,20 +1598,6 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, - "chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" - } - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -1707,12 +1615,6 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -1785,15 +1687,6 @@ "ms": "^2.1.1" } }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -1821,15 +1714,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", @@ -2035,12 +1919,6 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, "glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", @@ -2345,12 +2223,6 @@ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true - }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -2668,12 +2540,6 @@ "prelude-ls": "~1.1.2" } }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", 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", - "dojo-typings": "~1.11.9", + "@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/NlsBundle.ts b/djx/src/main/ts/NlsBundle.ts --- a/djx/src/main/ts/NlsBundle.ts +++ b/djx/src/main/ts/NlsBundle.ts @@ -1,9 +1,5 @@ import { MapOf, PromiseOrValue } from "@implab/core-amd/interfaces"; import { argumentNotEmptyString, isPromise, mixin } from "@implab/core-amd/safe"; -import { id as mid } from "module"; -import { TraceSource } from "@implab/core-amd/log/TraceSource"; - -const trace = TraceSource.get(mid); export type LocaleProvider = () => PromiseOrValue; @@ -19,10 +15,6 @@ function isCallback(v: ResolveCallbac return typeof v === "function"; } -function defaultResolver(module: string) { - return import(module).then(x => x && x.default ? x.default : x); -} - function chainObjects(o1: T, o2: T) { if (!o1) return o2; 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 @@ -1,7 +1,6 @@ import declare = require("dojo/_base/declare"); import { each } from "@implab/core-amd/safe"; import { Constructor } from "@implab/core-amd/interfaces"; -import dojo = require("dojo/_base/kernel"); // declare const declare: any; @@ -121,10 +120,8 @@ export function djclass(bc.bases, target.prototype); // bc - базовый класс, bc.prototype используется как super // при вызове базовых методов. Нужно создать bc.prototype diff --git a/djx/src/main/ts/dom-inject.ts b/djx/src/main/ts/dom-inject.ts --- a/djx/src/main/ts/dom-inject.ts +++ b/djx/src/main/ts/dom-inject.ts @@ -94,7 +94,7 @@ class DomInject { throw e; } } -}; +} const instance = new DomInject(); export default instance; 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/BuildContextBase.ts b/djx/src/main/ts/tsx/BuildContextBase.ts --- a/djx/src/main/ts/tsx/BuildContextBase.ts +++ b/djx/src/main/ts/tsx/BuildContextBase.ts @@ -3,9 +3,9 @@ import { RenditionBase } from "./Renditi /** * @deprecated use RenditionBase instead */ - export type BuildContextBase = RenditionBase; +export type BuildContextBase = RenditionBase; /** - * @deprecated use RenditionBase instead - */ - export const BuildContextBase = RenditionBase; + * @deprecated use RenditionBase instead + */ +export const BuildContextBase = RenditionBase; diff --git a/djx/src/main/ts/tsx/DjxFragment.ts b/djx/src/main/ts/tsx/DjxFragment.ts --- a/djx/src/main/ts/tsx/DjxFragment.ts +++ b/djx/src/main/ts/tsx/DjxFragment.ts @@ -1,5 +1,5 @@ /** Special functional component used to create a document fragment */ -export function DjxFragment({children}: {children?: Node | Node[]}){ +export function DjxFragment({ children }: { children?: Node | Node[] }) { const fragment = document.createDocumentFragment(); if (children) (children instanceof Array ? children : [children]).forEach(child => fragment.appendChild(child)); 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,8 +1,9 @@ import { djbase, djclass } from "../declare"; import _WidgetBase = require("dijit/_WidgetBase"); import _AttachMixin = require("dijit/_AttachMixin"); -import { Rendition, isNode, startupWidgets } from "./traits"; +import { Rendition, isNode } from "./traits"; import registry = require("dijit/registry"); +import on = require("dojo/on"); // type Handle = dojo.Handle; @@ -14,18 +15,31 @@ 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 { + + /** This property is declared only for type inference to work, it is never assigned + * and should not be used. + */ + readonly _eventMap: Events & GlobalEventHandlersEventMap; - on(eventName: K, cb: (evt: Events[K]) => void): dojo.WatchHandle; - - emit(eventName: K, evt: Omit & EventArgs): void; + /** The list of pairs of event and method names. When the widget is created all methods from + * this list will be connected to corresponding events. + * + * This property is maintained in the prototype + */ + _eventHandlers: Array<{ + eventName: string, + handlerMethod: keyof any; + }>; } +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(); @@ -36,6 +50,9 @@ export abstract class DjxWidgetBase; + private _connectEventHandlers() { + if (this._eventHandlers) + this._eventHandlers.forEach(({ eventName, handlerMethod }) => { + const handler = this[handlerMethod as keyof this]; + if (typeof handler === "function") + on(this.domNode, eventName, handler.bind(this)); + }); + } + _processTemplateNode( 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 +81,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/RenditionBase.ts b/djx/src/main/ts/tsx/RenditionBase.ts --- a/djx/src/main/ts/tsx/RenditionBase.ts +++ b/djx/src/main/ts/tsx/RenditionBase.ts @@ -90,7 +90,7 @@ export abstract class RenditionBase { if (node.parentNode) { @@ -100,9 +100,9 @@ export abstract class RenditionBase = Rendition; @@ -60,7 +60,7 @@ export function isWidget(v: any): v is _ } export function isRendition(v: any): v is Rendition { - return typeof v === "object" && typeof v.getDomElement === "function"; + return v && typeof v.getDomElement === "function"; } /** @@ -107,9 +107,9 @@ export function destroy(target: Node | I } else if (isDestroyable(target)) { target.destroy(); } else if (isNode(target)) { - const self = registry.byNode(target); - if (self) { - self.destroyRecursive(); + const w = isElementNode(target) ? registry.byNode(target) : undefined; + if (w) { + w.destroyRecursive(); } else { registry.findWidgets(target).forEach(destroy); dom.destroy(target); @@ -129,7 +129,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) { @@ -142,7 +142,7 @@ export function startupWidgets(target: N registry.findWidgets(target, skipNode).forEach(x => x.startup()); } } else { - if(target.startup) + if (target.startup) target.startup(); } -} \ No newline at end of file +} 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 deleted file mode 100644 --- a/djx/src/main/typings/dijit.d.ts +++ /dev/null @@ -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/main/typings/index.d.ts b/djx/src/main/typings/index.d.ts --- a/djx/src/main/typings/index.d.ts +++ b/djx/src/main/typings/index.d.ts @@ -1,5 +1,4 @@ /// -/// declare namespace JSX { diff --git a/djx/src/test/ts/DeclareTests.ts b/djx/src/test/ts/DeclareTests.ts --- a/djx/src/test/ts/DeclareTests.ts +++ b/djx/src/test/ts/DeclareTests.ts @@ -1,5 +1,6 @@ import { Baz } from "./mock/Baz"; +// tslint:disable-next-line: no-console console.log("Declare tests"); const baz = new Baz(); @@ -7,4 +8,5 @@ const baz = new Baz(); const data: string[] = []; baz.writeHello(data); +// tslint:disable-next-line: no-console console.log(data.join("\n")); diff --git a/djx/src/test/ts/dummy.ts b/djx/src/test/ts/dummy.ts --- a/djx/src/test/ts/dummy.ts +++ b/djx/src/test/ts/dummy.ts @@ -1,7 +1,6 @@ import { test } from "./TestTraits"; import { delay } from "@implab/core-amd/safe"; import { assert } from "chai"; -import css = require("@implab/djx/css!my.css"); test("simple", (ok, fail, log) => { setTimeout(() => { 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,7 +1,7 @@ import { djbase, djclass, bind, prototype, AbstractConstructor } from "../declare"; import { DjxWidgetBase } from "../tsx/DjxWidgetBase"; -import { createElement } from "../tsx"; +import { createElement, on } from "../tsx"; interface MyWidgetAttrs { title: string; @@ -10,9 +10,13 @@ interface MyWidgetAttrs { } interface MyWidgetEvents { - "count-inc": Event; + "count-inc": Event & { + detail: number; + }; - "count-dec": Event; + "count-dec": Event & { + detail: number; + }; } @@ -36,15 +40,31 @@ 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.emit("count-inc", { bubbles: false }); } _onDecClick() { - this.emit("count-dec", { bubbles: false }); + this.emit("count-dec", { bubbles: false, detail: this.counter }); + } + + @on("count-inc") + _onCounterInc(evt: Event & { detail: number; x?: number; }) { } -} + + @on("click", "keydown") + protected _onClick(event: MouseEvent | KeyboardEvent) { + + } +} \ No newline at end of file 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 diff --git a/djx/src/tsconfig.json b/djx/src/tsconfig.json --- a/djx/src/tsconfig.json +++ b/djx/src/tsconfig.json @@ -6,8 +6,10 @@ "types": [], "experimentalDecorators": true, "jsxFactory": "createElement", + "target": "ES5", //"skipLibCheck": true, "jsx": "react", - "lib": ["es5", "es2015.promise", "es2015.symbol", "es2015.iterable", "dom", "scripthost"] + "lib": ["es5", "es2015.promise", "es2015.symbol", "es2015.iterable", "dom", "scripthost"], + "noUnusedLocals": true } } \ No newline at end of file diff --git a/djx/tslint.json b/djx/tslint.json new file mode 100644 --- /dev/null +++ b/djx/tslint.json @@ -0,0 +1,48 @@ +{ + "extends": "tslint:recommended", + "defaultSeverity": "warn", + "rules": { + "align": [ + true, + "parameters", + "statements" + ], + "interface-name": [ + false + ], + "max-line-length": [ + true, + 185 + ], + "quotemark": [true, "double", "avoid-escape"], + "member-access": false, + "semicolon": [true, "always", "ignore-bound-class-methods"], + "no-bitwise": false, + "no-empty": false, + "no-namespace": false, + "ordered-imports": false, + "no-return-await": true, + "no-floating-promises": true, + "one-line": [ + true, + "check-open-brace", + "check-catch", + "check-whitespace" + ], + "object-literal-sort-keys": false, + "trailing-comma": [ + true, + { + "singleline": "never", + "multiline": "never" + } + ], + "variable-name": false, + "curly": false, + "array-type": false, + "arrow-parens": [ + true, + "ban-single-arg-parens" + ] + } +} \ No newline at end of file diff --git a/playground/build.gradle b/playground/build.gradle deleted file mode 100644 diff --git a/settings.gradle b/settings.gradle --- a/settings.gradle +++ b/settings.gradle @@ -14,5 +14,4 @@ rootProject.name = 'implabjs-djx' -include 'djx' -include 'playground' \ No newline at end of file +include 'djx' \ No newline at end of file