##// END OF EJS Templates
minor code cleanups
cin -
r93:d99e2014cea4 default
parent child
Show More
@@ -1,78 +1,78
1 1 plugins {
2 2 id "org.implab.gradle-typescript" version "1.3.3"
3 3 id "ivy-publish"
4 4 }
5 5
6 6 typescript {
7 7 compilerOptions {
8 8 lib = ["es5", "dom", "scripthost", "es2015.promise", "es2015.symbol", "es2015.iterable"]
9 9 //listFiles = true
10 10 declaration = true
11 11 strict = true
12 12 types = []
13 13 module = "amd"
14 14 it.target = "es5"
15 15 experimentalDecorators = true
16 noUnusedLocals = true
16 noUnusedLocals = false
17 17 jsx = "react"
18 18 jsxFactory = "createElement"
19 19 moduleResolution = "node"
20 20 // dojo-typings are sick
21 21 skipLibCheck = true
22 22 // traceResolution = true
23 23 // baseUrl = "./"
24 24 // paths = [ "*": [ "$projectDir/src/typings/*" ] ]
25 25 // baseUrl = "$projectDir/src/typings"
26 26 // typeRoots = ["$projectDir/src/typings"]
27 27 }
28 28
29 29 tscCmd = "$projectDir/node_modules/.bin/tsc"
30 30 tsLintCmd = "$projectDir/node_modules/.bin/tslint"
31 31 esLintCmd = "$projectDir/node_modules/.bin/eslint"
32 32 }
33 33
34 34 configureTsMain {
35 35 sourceFiles {
36 36 from sources.main.typings
37 37 }
38 38 compilerOptions {
39 39 // baseUrl = "$projectDir/src"
40 40 /*paths = [
41 41 "dojo/*" : [ "typings/dojo/*" ],
42 42 "dijit/*" : [ "typings/dijit/*" ]
43 43 ]*/
44 44 types = ["requirejs", "@implab/dojo-typings"]
45 45 }
46 46 }
47 47
48 48 configureTsTest {
49 49 compilerOptions {
50 50 typeRoots = []
51 51 types = ["requirejs", sources.main.output.typingsDir.get().toString() ]
52 52 }
53 53 }
54 54
55 55 npmPackMeta {
56 56 meta {
57 57 name = "@$npmScope/$project.name"
58 58 }
59 59 }
60 60
61 61 task npmPackTypings(type: Copy) {
62 62 dependsOn typings
63 63
64 64 npmPackContents.dependsOn it
65 65
66 66 from typescript.typingsDir
67 67 into npm.packageDir
68 68 }
69 69
70 70 task printVersion {
71 71 doLast {
72 72 println "packageName: ${npmPackMeta.metadata.get().name}";
73 73 println "version: $version";
74 74 println "target: $typescript.compilerOptions.target";
75 75 println "module: $typescript.compilerOptions.module";
76 76 println "symbols: $symbols";
77 77 }
78 78 } No newline at end of file
@@ -1,119 +1,119
1 1 import { Constructor, IDestroyable, IRemovable } from "@implab/core-amd/interfaces";
2 2 import { HtmlRendition } from "./tsx/HtmlRendition";
3 3 import { WidgetRendition } from "./tsx/WidgetRendition";
4 4 import { destroy, isWidgetConstructor, Rendition } from "./tsx/traits";
5 5 import { FunctionRendition } from "./tsx/FunctionRendition";
6 6 import Stateful = require("dojo/Stateful");
7 7 import _WidgetBase = require("dijit/_WidgetBase");
8 8 import { DjxWidgetBase } from "./tsx/DjxWidgetBase";
9 9
10 10 export function createElement<T extends Constructor | string | ((props: any) => Element)>(elementType: T, ...args: any[]): Rendition {
11 11 if (typeof elementType === "string") {
12 12 const ctx = new HtmlRendition(elementType);
13 13 if (args)
14 14 args.forEach(x => ctx.visitNext(x));
15 15
16 16 return ctx;
17 17 } else if (isWidgetConstructor(elementType)) {
18 18 const ctx = new WidgetRendition(elementType);
19 19 if (args)
20 20 args.forEach(x => ctx.visitNext(x));
21 21
22 22 return ctx;
23 23 } else if (typeof elementType === "function") {
24 24 const ctx = new FunctionRendition(elementType as (props: any) => Element);
25 25 if (args)
26 26 args.forEach(x => ctx.visitNext(x));
27 27
28 28 return ctx;
29 29 } else {
30 30 throw new Error(`The element type '${elementType}' is unsupported`);
31 31 }
32 32 }
33 33
34 34 export interface EventDetails<T = any> {
35 35 detail: T;
36 36 }
37 37
38 38 export interface EventSelector {
39 39 selectorTarget: HTMLElement;
40 40 target: HTMLElement;
41 41 }
42 42
43 43 export type DojoMouseEvent<T = any> = MouseEvent & EventSelector & EventDetails<T>;
44 44
45 45 type StatefulProps<T> = T extends Stateful<infer A> ? A : never;
46 46
47 47 type CleanFn = (instance: IRemovable | IDestroyable) => void;
48 48
49 49 /**
50 50 * Observers the property and calls render callback each change.
51 51 *
52 52 * @param target The target object which property will be observed.
53 53 * @param prop The name of the property.
54 54 * @param render The callback which will be called every time the value is changed
55 55 * @param cleanupOrOwner The object with method `own` or an callback to register lifecycle for the observer.
56 56 * @returns Rendition which is created instantly
57 57 */
58 58 export function watch<W extends _WidgetBase, K extends keyof W>(
59 59 target: W,
60 60 prop: K,
61 61 render: (model: W[K]) => any,
62 62 cleanupOrOwner?: { own: CleanFn } | CleanFn
63 63 ): Rendition;
64 64 /**
65 65 * Observers the property and calls render callback each change.
66 66 *
67 67 * @param target The target object which property will be observed.
68 68 * @param prop The name of the property.
69 69 * @param render The callback which will be called every time the value is changed
70 70 * @param cleanupOrOwner The object with method `own` or an callback to register lifecycle for the observer.
71 71 * @returns Rendition which is created instantly
72 72 */
73 73 export function watch<T extends Stateful, K extends keyof StatefulProps<T>>(
74 74 target: T,
75 75 prop: K,
76 76 render: (model: StatefulProps<T>[K]) => any,
77 77 cleanupOrOwner?: { own: CleanFn } | CleanFn
78 78 ): Rendition;
79 79 export function watch<T extends Stateful, K extends keyof StatefulProps<T> & string>(
80 80 target: T,
81 81 prop: K,
82 82 render: (model: StatefulProps<T>[K]) => any,
83 83 cleanupOrOwner: { own: CleanFn } | CleanFn = () => { }
84 84 ) {
85 85 let rendition = new FunctionRendition(() => render(target.get(prop)));
86 86 const _own = cleanupOrOwner instanceof Function ? cleanupOrOwner : (x: IRemovable) => cleanupOrOwner.own(x);
87 87 _own(target.watch(prop, (_name, oldValue, newValue) => {
88 88 if (oldValue !== newValue) {
89 89 const newRendition = new FunctionRendition(() => render(newValue));
90 90 newRendition.placeAt(rendition.getDomNode(), "replace");
91 91 destroy(rendition.getDomNode());
92 92 rendition = newRendition;
93 93 }
94 94 }));
95 95 return rendition;
96 96 }
97 97
98 98 /** Decorates the method which will be registered as the handle for the specified event.
99 99 * This decorator can be applied to DjxWidgetBase subclass methods.
100 100 *
101 101 * ```
102 102 * @on("click")
103 103 * _onClick(eventObj: MouseEvent) {
104 104 * // ...
105 105 * }
106 106 * ```
107 107 */
108 108 export const on = <E extends string>(...eventNames: E[]) =>
109 109 <K extends string,
110 110 T extends DjxWidgetBase<any, { [p in E]: EV }>,
111 111 EV extends Event
112 112 >(
113 113 target: T,
114 114 key: K,
115 115 _descriptor: TypedPropertyDescriptor<(eventObj: EV) => void> | TypedPropertyDescriptor<() => void>
116 116 ): any => {
117 const handlers = eventNames.map(eventName => ({ eventName, handlerMethod: key }))
117 const handlers = eventNames.map(eventName => ({ eventName, handlerMethod: key }));
118 118 target._eventHandlers = target._eventHandlers ? target._eventHandlers.concat(handlers) : handlers;
119 119 };
@@ -1,37 +1,36
1 1 import dom = require("dojo/dom-construct");
2 2 import { argumentNotEmptyString } from "@implab/core-amd/safe";
3 3 import { RenditionBase } from "./RenditionBase";
4 4
5 5 export class HtmlRendition extends RenditionBase<HTMLElement> {
6 6 elementType: string;
7 7
8 8 _element: HTMLElement | undefined;
9 9
10 10 constructor(elementType: string) {
11 11 argumentNotEmptyString(elementType, "elementType");
12 12 super();
13 13
14 14 this.elementType = elementType;
15 15 }
16 16
17 17 _addChild(child: any): void {
18 18 if (!this._element)
19 19 throw new Error("The HTML element isn't created");
20 20 dom.place(this.getItemDom(child), this._element);
21 21 }
22 22
23 23 _create(attrs: object, children: any[]) {
24 24 this._element = dom.create(this.elementType, attrs);
25 25
26 if (children)
27 26 children.forEach(v => this._addChild(v));
28 27 }
29 28
30 29 _getDomNode() {
31 30 if (!this._element)
32 31 throw new Error("The HTML element isn't created");
33 32
34 33 return this._element;
35 34 }
36 35
37 36 }
@@ -1,116 +1,117
1 1 import { isNull, mixin } from "@implab/core-amd/safe";
2 2 import { isPlainObject, isNode, isRendition, DojoNodePosition, Rendition, isInPage, isWidget, isDocumentFragmentNode, startupWidgets } from "./traits";
3 3
4 4 import dom = require("dojo/dom-construct");
5 5 import registry = require("dijit/registry");
6 6
7 7
8 8 export abstract class RenditionBase<TNode extends Node> implements Rendition<TNode> {
9 9 private _attrs = {};
10 10
11 11 private _children = new Array();
12 12
13 13 private _created: boolean = false;
14 14
15 15 visitNext(v: any) {
16 16 if (this._created)
17 17 throw new Error("The Element is already created");
18 18
19 19 if (isNull(v) || typeof v === "boolean")
20 20 // skip null, undefined, booleans ( this will work: {value && <span>{value}</span>} )
21 21 return;
22 22
23 23 if (isPlainObject(v)) {
24 24 mixin(this._attrs, v);
25 25 } else if (v instanceof Array) {
26 26 v.forEach(x => this.visitNext(x));
27 27 } else {
28 28 this._children.push(v);
29 29 }
30 30 }
31 31
32 /** Renders DOM element for different types of the argument. */
32 33 protected getItemDom(v: any) {
33 34 const tv = typeof v;
34 35
35 36 if (tv === "string" || tv === "number" || v instanceof RegExp || v instanceof Date) {
36 37 // primitive types converted to the text nodes
37 38 return document.createTextNode(v.toString());
38 39 } else if (isNode(v)) {
39 40 // nodes are kept as is
40 41 return v;
41 42 } else if (isRendition(v)) {
42 // renditions as instantinated
43 // renditions are instantiated
43 44 return v.getDomNode();
44 45 } else if (isWidget(v)) {
45 46 // widgets are converted to it's markup
46 47 return v.domNode;
47 48 } else if (tv === "boolean" || v === null || v === undefined) {
48 49 // null | undefined | boolean are removed, converted to comments
49 50 return document.createComment(`[${tv} ${String(v)}]`);
50 51 } else {
51 52 // bug: explicit error otherwise
52 53 throw new Error("Invalid parameter: " + v);
53 54 }
54 55 }
55 56
56 57 ensureCreated() {
57 58 if (!this._created) {
58 59 this._create(this._attrs, this._children);
59 60 this._children = [];
60 61 this._attrs = {};
61 62 this._created = true;
62 63 }
63 64 }
64 65
65 /** @deprecated will be removed in 1.0.0, use getDomNode() */
66 getDomElement() {
67 return this.getDomNode();
66 /** Is rendition was instantiated to the DOM node */
67 isCreated() {
68 return this._created;
68 69 }
69 70
70 71 /** Creates DOM node if not created. No additional actions are taken. */
71 72 getDomNode() {
72 73 this.ensureCreated();
73 74 return this._getDomNode();
74 75 }
75 76
76 77 /** Creates DOM node if not created, places it to the specified position
77 78 * and calls startup() method for all widgets contained by this node.
78 79 *
79 80 * @param {string | Node} refNode The reference node where the created
80 81 * DOM should be placed.
81 82 * @param {DojoNodePosition} position Optional parameter, specifies the
82 83 * position relative to refNode. Default is "last" (i.e. last child).
83 84 */
84 85 placeAt(refNode: string | Node, position?: DojoNodePosition) {
85 86 const domNode = this.getDomNode();
86 87
87 88 const collect = (collection: HTMLCollection) => {
88 89 const items = [];
89 90 for (let i = 0, n = collection.length; i < n; i++) {
90 91 items.push(collection[i]);
91 92 }
92 93 return items;
93 94 };
94 95
95 96 const startup = (node: Node) => {
96 97 if (node.parentNode) {
97 98 const parentWidget = registry.getEnclosingWidget(node.parentNode);
98 99 if (parentWidget && parentWidget._started)
99 100 return startupWidgets(node);
100 101 }
101 102 if (isInPage(node))
102 103 startupWidgets(node);
103 104 };
104 105
105 106 const startupPending = isDocumentFragmentNode(domNode) ? collect(domNode.children) : [domNode];
106 107
107 108 dom.place(domNode, refNode, position);
108 109
109 110 startupPending.forEach(startup);
110 111
111 112 }
112 113
113 114 protected abstract _create(attrs: object, children: any[]): void;
114 115
115 116 protected abstract _getDomNode(): TNode;
116 117 }
@@ -1,148 +1,152
1 1 import { IDestroyable } from "@implab/core-amd/interfaces";
2 2 import { isDestroyable } from "@implab/core-amd/safe";
3 3 import _WidgetBase = require("dijit/_WidgetBase");
4 4 import registry = require("dijit/registry");
5 5 import dom = require("dojo/dom-construct");
6 6
7 7 type _WidgetBaseConstructor = typeof _WidgetBase;
8 8
9 9 export type DojoNodePosition = "first" | "after" | "before" | "last" | "replace" | "only" | number;
10 10
11 11 export interface Rendition<TNode extends Node = Node> {
12 12 getDomNode(): TNode;
13 13
14 14 placeAt(refNode: string | Node, position?: DojoNodePosition): void;
15 15 }
16 16
17 17 /**
18 18 * @deprecated use Rendition
19 19 */
20 20 export type BuildContext<TNode extends Node = Node> = Rendition<TNode>;
21 21
22 22 export interface IRecursivelyDestroyable {
23 23 destroyRecursive(): void;
24 24 }
25 25
26 26 export function isNode(el: any): el is Node {
27 27 return el && el.nodeName && el.nodeType;
28 28 }
29 29
30 30 export function isElementNode(el: any): el is Element {
31 31 return isNode(el) && el.nodeType === 1;
32 32 }
33 33
34 34 export function isTextNode(el: any): el is Text {
35 35 return isNode(el) && el.nodeType === 3;
36 36 }
37 37
38 38 export function isProcessingInstructionNode(el: any): el is ProcessingInstruction {
39 39 return isNode(el) && el.nodeType === 7;
40 40 }
41 41
42 42 export function isCommentNode(el: any): el is Comment {
43 43 return isNode(el) && el.nodeType === 8;
44 44 }
45 45
46 46 export function isDocumentNode(el: any): el is Document {
47 47 return isNode(el) && el.nodeType === 9;
48 48 }
49 49
50 50 export function isDocumentTypeNode(el: any): el is DocumentType {
51 51 return isNode(el) && el.nodeType === 10;
52 52 }
53 53
54 54 export function isDocumentFragmentNode(el: any): el is DocumentFragment {
55 55 return isNode(el) && el.nodeType === 11;
56 56 }
57 57
58 58 export function isWidget(v: any): v is _WidgetBase {
59 59 return v && "domNode" in v;
60 60 }
61 61
62 62 export function isRendition(v: any): v is Rendition {
63 63 return v && typeof v.getDomElement === "function";
64 64 }
65 65
66 66 /**
67 67 * @deprecated use isRendition
68 68 */
69 69 export const isBuildContext = isRendition;
70 70
71 71 export function isPlainObject(v: object) {
72 72 if (typeof v !== "object")
73 73 return false;
74 74
75 75 const vp = Object.getPrototypeOf(v);
76 76 return !vp || vp === Object.prototype;
77 77 }
78 78
79 79 export function isWidgetConstructor(v: any): v is _WidgetBaseConstructor {
80 80 return typeof v === "function" && v.prototype && (
81 81 "domNode" in v.prototype ||
82 82 "buildRendering" in v.prototype
83 83 );
84 84 }
85 85
86 86 /** Tests whether the specified node is placed in visible dom.
87 87 * @param {Node} node The node to test
88 88 */
89 89 export function isInPage(node: Node) {
90 90 return (node === document.body) ? false : document.body.contains(node);
91 91 }
92 92
93 93 export function isRecursivelyDestroyable(target: any): target is IRecursivelyDestroyable {
94 94 return target && typeof target.destroyRecursive === "function";
95 95 }
96 96
97 97
98 98 /** Destroys DOM Node with all contained widgets.
99 99 * If the specified node is the root node of a widget, then the
100 100 * widget will be destroyed.
101 101 *
102 102 * @param target DOM Node or widget to destroy
103 103 */
104 104 export function destroy(target: Node | IDestroyable | IRecursivelyDestroyable) {
105 105 if (isRecursivelyDestroyable(target)) {
106 106 target.destroyRecursive();
107 107 } else if (isDestroyable(target)) {
108 108 target.destroy();
109 109 } else if (isNode(target)) {
110 const w = isElementNode(target) ? registry.byNode(target) : undefined;
110 if (isElementNode(target)) {
111 const w = registry.byNode(target);
111 112 if (w) {
112 113 w.destroyRecursive();
113 114 } else {
114 115 registry.findWidgets(target).forEach(destroy);
115 116 dom.destroy(target);
116 117 }
117 118 }
118 119 }
120 }
119 121
120 122 /** Empties a content of the specified node and destroys all contained widgets.
121 123 *
122 124 * @param target DOM node to .
123 125 */
124 126 export function emptyNode(target: Node) {
125 127 registry.findWidgets(target).forEach(destroy);
126 128 dom.empty(target);
127 129 }
128 130
129 131 /** This function starts all widgets inside the DOM node if the target is a node
130 132 * or starts widget itself if the target is the widget. If the specified node
131 133 * associated with the widget that widget will be started.
132 134 *
133 135 * @param target DOM node to find and start widgets or the widget itself.
134 136 */
135 137 export function startupWidgets(target: Node | _WidgetBase, skipNode?: Node) {
136 138 if (isNode(target)) {
137 const w = isElementNode(target) ? registry.byNode(target) : undefined;
139 if (isElementNode(target)) {
140 const w = registry.byNode(target);
138 141 if (w) {
139 142 if (w.startup)
140 143 w.startup();
141 144 } else {
142 145 registry.findWidgets(target, skipNode).forEach(x => x.startup());
143 146 }
147 }
144 148 } else {
145 149 if (target.startup)
146 150 target.startup();
147 151 }
148 152 }
@@ -1,70 +1,70
1 1 import { djbase, djclass, bind, prototype, AbstractConstructor } from "../declare";
2 2
3 3 import { DjxWidgetBase } from "../tsx/DjxWidgetBase";
4 4 import { createElement, on } from "../tsx";
5 5
6 6 interface MyWidgetAttrs {
7 7 title: string;
8 8
9 9 counter: number;
10 10 }
11 11
12 12 interface MyWidgetEvents {
13 13 "count-inc": Event & {
14 14 detail: number;
15 15 };
16 16
17 17 "count-dec": Event & {
18 18 detail: number;
19 19 };
20 20 }
21 21
22 22
23 23 @djclass
24 24 export class MyWidget extends djbase(DjxWidgetBase as AbstractConstructor<DjxWidgetBase<MyWidgetAttrs, MyWidgetEvents>>) {
25 25
26 26 @bind({ node: "titleNode", type: "innerHTML" })
27 27 title = "";
28 28
29 29 @prototype()
30 30 counter = 0;
31 31
32 32 render() {
33 33 const Frame = (props: any) => <div>{props.children}</div>;
34 34 return <div className="myWidget" onsubmit={e => this._onSubmit(e)} tabIndex={3} style={{ alignContent: "center", border: "1px solid" }} >
35 35 <h1 data-dojo-attach-point="titleNode"></h1>
36 36 <Frame>
37 37 <span class="up-button" onclick={e => this._onIncClick(e)}>[+]</span>
38 38 <span class="down-button" onclick={() => this._onDecClick()}>[-]</span>
39 39 </Frame>
40 40 </div>;
41 41 }
42 42
43 43 postCreate() {
44 44 super.postCreate();
45 45
46 46 this.on("click", () => {});
47 47 }
48 48
49 49 _onSubmit(e: Event) {
50 50 }
51 51
52 52 _onIncClick(e: MouseEvent) {
53 53 this.set("counter", this.counter + 1);
54 54
55 55 this.emit("count-inc", { bubbles: false });
56 56 }
57 57
58 58 _onDecClick() {
59 59 this.emit("count-dec", { bubbles: false, detail: this.counter });
60 60 }
61 61
62 62 @on("count-inc")
63 _onCounterInc(evt: Event & { detail: number; x?: number; }) {
63 private _onCounterInc(evt: Event & { detail: number; x?: number; }) {
64 64 }
65 65
66 66 @on("click", "keydown")
67 67 protected _onClick(event: MouseEvent | KeyboardEvent) {
68 68
69 69 }
70 70 } No newline at end of file
@@ -1,13 +1,14
1 1 {
2 2 "extends": "../tsconfig",
3 3 "compilerOptions": {
4 "noUnusedLocals": false,
4 5 //"rootDir": "ts",
5 6 "rootDirs": [
6 7 "ts",
7 8 "typings",
8 9 "../main/ts",
9 10 "../main/typings"
10 11 ],
11 12 "types": ["requirejs", "../main/typings", "@implab/dojo-typings"]
12 13 }
13 14 } No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now