@@ -0,0 +1,64 | |||
|
1 | import { isNull, mixin, argumentNotNull } from "@implab/core-amd/safe"; | |
|
2 | import { isPlainObject, isNode, isBuildContext } from "./traits"; | |
|
3 | ||
|
4 | export abstract class BuildContextBase { | |
|
5 | _attrs = {}; | |
|
6 | ||
|
7 | _children = new Array(); | |
|
8 | ||
|
9 | _created: boolean = false; | |
|
10 | ||
|
11 | visitNext(v: any) { | |
|
12 | if (isNull(v)) | |
|
13 | return; | |
|
14 | ||
|
15 | if (isPlainObject(v)) { | |
|
16 | ||
|
17 | if (this._created) | |
|
18 | this._setAttrs(v); | |
|
19 | else | |
|
20 | mixin(this._attrs, v); | |
|
21 | ||
|
22 | } else { | |
|
23 | if (this._created) | |
|
24 | this._addChild(v); | |
|
25 | else | |
|
26 | this._children.push(v); | |
|
27 | } | |
|
28 | } | |
|
29 | ||
|
30 | getChildDom(v: any) { | |
|
31 | const tv = typeof v; | |
|
32 | if (tv === "string" || tv === "number" || tv === "boolean" || v instanceof RegExp || v instanceof Date) { | |
|
33 | return document.createTextNode(v.toString()); | |
|
34 | } else if (isNode(v)) { | |
|
35 | return v; | |
|
36 | } else if (isBuildContext(v)) { | |
|
37 | return v.getDomElement(); | |
|
38 | } else { | |
|
39 | throw new Error("Invalid parameter"); | |
|
40 | } | |
|
41 | } | |
|
42 | ||
|
43 | abstract _getDomElement(): HTMLElement; | |
|
44 | ||
|
45 | ensureCreated() { | |
|
46 | if (!this._created) { | |
|
47 | this._create(this._attrs, this._children); | |
|
48 | this._children = []; | |
|
49 | this._attrs = {}; | |
|
50 | this._created = true; | |
|
51 | } | |
|
52 | } | |
|
53 | ||
|
54 | getDomElement() { | |
|
55 | this.ensureCreated(); | |
|
56 | return this._getDomElement(); | |
|
57 | } | |
|
58 | ||
|
59 | abstract _addChild(child: any): void; | |
|
60 | ||
|
61 | abstract _setAttrs(attrs: object): void; | |
|
62 | ||
|
63 | abstract _create(attrs: object, children: any[]): void; | |
|
64 | } |
@@ -0,0 +1,51 | |||
|
1 | import { djbase, djclass } from "../declare"; | |
|
2 | import _WidgetBase = require("dijit/_WidgetBase"); | |
|
3 | import _AttachMixin = require("dijit/_AttachMixin"); | |
|
4 | import { BuildContext, isNode } from "./traits"; | |
|
5 | import registry = require("dijit/registry"); | |
|
6 | // import { Handle } from "dojo/interfaces"; | |
|
7 | type Handle = dojo.Handle; | |
|
8 | ||
|
9 | @djclass | |
|
10 | export abstract class DjxWidgetBase extends djbase(_WidgetBase, _AttachMixin) { | |
|
11 | ||
|
12 | buildRendering() { | |
|
13 | this.domNode = this.render().getDomElement(); | |
|
14 | super.buildRendering(); | |
|
15 | } | |
|
16 | ||
|
17 | abstract render(): BuildContext; | |
|
18 | ||
|
19 | _processTemplateNode<T extends (Element | Node | _WidgetBase)>( | |
|
20 | baseNode: T, | |
|
21 | getAttrFunc: (baseNode: T, attr: string) => string, | |
|
22 | // tslint:disable-next-line: ban-types | |
|
23 | attachFunc: (node: T, type: string, func?: Function) => Handle | |
|
24 | ): boolean { | |
|
25 | if (isNode(baseNode)) { | |
|
26 | const w = registry.byNode(baseNode); | |
|
27 | if (w) { | |
|
28 | // from dijit/_WidgetsInTemplateMixin | |
|
29 | this._processTemplateNode(w, | |
|
30 | (n, p) => n.get(p), // callback to get a property of a widget | |
|
31 | (widget, type, callback) => { | |
|
32 | if (!callback) | |
|
33 | throw new Error("The callback must be specified"); | |
|
34 | ||
|
35 | // callback to do data-dojo-attach-event to a widget | |
|
36 | if (type in widget) { | |
|
37 | // back-compat, remove for 2.0 | |
|
38 | return widget.connect(widget, type, callback as EventListener); | |
|
39 | } else { | |
|
40 | // 1.x may never hit this branch, but it's the default for 2.0 | |
|
41 | return widget.on(type, callback); | |
|
42 | } | |
|
43 | ||
|
44 | }); | |
|
45 | // don't process widgets internals | |
|
46 | return false; | |
|
47 | } | |
|
48 | } | |
|
49 | return super._processTemplateNode(baseNode, getAttrFunc, attachFunc); | |
|
50 | } | |
|
51 | } |
@@ -0,0 +1,45 | |||
|
1 | import dom = require("dojo/dom-construct"); | |
|
2 | import attr = require("dojo/dom-attr"); | |
|
3 | import { argumentNotEmptyString } from "@implab/core-amd/safe"; | |
|
4 | import { BuildContextBase } from "./BuildContextBase"; | |
|
5 | ||
|
6 | export class HtmlElementContext extends BuildContextBase { | |
|
7 | elementType: string; | |
|
8 | ||
|
9 | _element: HTMLElement | undefined; | |
|
10 | ||
|
11 | constructor(elementType: string) { | |
|
12 | argumentNotEmptyString(elementType, "elementType"); | |
|
13 | super(); | |
|
14 | ||
|
15 | this.elementType = elementType; | |
|
16 | } | |
|
17 | ||
|
18 | _addChild(child: any): void { | |
|
19 | if (!this._element) | |
|
20 | throw new Error("The HTML element isn't created"); | |
|
21 | dom.place(this.getChildDom(child), this._element); | |
|
22 | } | |
|
23 | ||
|
24 | _setAttrs(attrs: object): void { | |
|
25 | if (!this._element) | |
|
26 | throw new Error("The HTML element isn't created"); | |
|
27 | ||
|
28 | attr.set(this._element, attrs); | |
|
29 | } | |
|
30 | ||
|
31 | _create(attrs: object, children: any[]) { | |
|
32 | this._element = dom.create(this.elementType, attrs); | |
|
33 | ||
|
34 | if (children) | |
|
35 | children.forEach(v => this._addChild(v)); | |
|
36 | } | |
|
37 | ||
|
38 | _getDomElement() { | |
|
39 | if (!this._element) | |
|
40 | throw new Error("The HTML element isn't created"); | |
|
41 | ||
|
42 | return this._element; | |
|
43 | } | |
|
44 | ||
|
45 | } |
@@ -0,0 +1,43 | |||
|
1 | import dom = require("dojo/dom-construct"); | |
|
2 | import { argumentNotNull } from "@implab/core-amd/safe"; | |
|
3 | import _WidgetBase = require("dijit/_WidgetBase"); | |
|
4 | import { BuildContextBase } from "./BuildContextBase"; | |
|
5 | ||
|
6 | type _WidgetBaseConstructor = typeof _WidgetBase; | |
|
7 | ||
|
8 | export class WidgetContext extends BuildContextBase { | |
|
9 | widgetClass: _WidgetBaseConstructor; | |
|
10 | ||
|
11 | _instance: _WidgetBase | undefined; | |
|
12 | ||
|
13 | constructor(widgetClass: _WidgetBaseConstructor) { | |
|
14 | super(); | |
|
15 | argumentNotNull(widgetClass, "widgetClass"); | |
|
16 | ||
|
17 | this.widgetClass = widgetClass; | |
|
18 | } | |
|
19 | ||
|
20 | _addChild(child: any): void { | |
|
21 | if (!this._instance || !this._instance.containerNode) | |
|
22 | throw new Error("Widget doesn't support adding children"); | |
|
23 | ||
|
24 | dom.place(this.getChildDom(child), this._instance.containerNode); | |
|
25 | } | |
|
26 | ||
|
27 | _setAttrs(attrs: object): void { | |
|
28 | this._instance?.set(attrs); | |
|
29 | } | |
|
30 | ||
|
31 | _create(attrs: object, children: any[]) { | |
|
32 | this._instance = new this.widgetClass(this._attrs); | |
|
33 | if (children) | |
|
34 | children.forEach(x => this._addChild(x)); | |
|
35 | } | |
|
36 | ||
|
37 | _getDomElement() { | |
|
38 | if (!this._instance) | |
|
39 | throw new Error("The instance of the widget isn't created"); | |
|
40 | return this._instance.domNode; | |
|
41 | } | |
|
42 | ||
|
43 | } |
@@ -0,0 +1,32 | |||
|
1 | import _WidgetBase = require("dijit/_WidgetBase"); | |
|
2 | ||
|
3 | type _WidgetBaseConstructor = typeof _WidgetBase; | |
|
4 | ||
|
5 | ||
|
6 | export interface BuildContext { | |
|
7 | getDomElement(): HTMLElement; | |
|
8 | } | |
|
9 | ||
|
10 | export function isNode(el: any): el is HTMLElement { | |
|
11 | return el && el.nodeName && el.nodeType; | |
|
12 | } | |
|
13 | ||
|
14 | export function isWidget(v: any): v is _WidgetBase { | |
|
15 | return v && "domNode" in v; | |
|
16 | } | |
|
17 | ||
|
18 | export function isBuildContext(v: any): v is BuildContext { | |
|
19 | return typeof v === "object" && typeof v.getDomElement === "function"; | |
|
20 | } | |
|
21 | ||
|
22 | export function isPlainObject(v: object) { | |
|
23 | if (typeof v !== "object") | |
|
24 | return false; | |
|
25 | ||
|
26 | const vp = Object.getPrototypeOf(v); | |
|
27 | return !vp || vp === Object.prototype; | |
|
28 | } | |
|
29 | ||
|
30 | export function isWidgetConstructor(v: any): v is _WidgetBaseConstructor { | |
|
31 | return typeof v === "function" && v.prototype && "domNode" in v.prototype; | |
|
32 | } |
@@ -0,0 +1,4 | |||
|
1 | declare module "@implab/djx/css!*" { | |
|
2 | declare const result: { url: string }; | |
|
3 | export = result; | |
|
4 | } |
@@ -1,62 +1,62 | |||
|
1 | 1 | plugins { |
|
2 |
id "org.implab.gradle-typescript" version "1.3. |
|
|
2 | id "org.implab.gradle-typescript" version "1.3.2" | |
|
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 | 16 | jsx = "react" |
|
17 | 17 | jsxFactory = "createElement" |
|
18 | 18 | moduleResolution = "node" |
|
19 | 19 | // dojo-typings are sick |
|
20 | 20 | skipLibCheck = true |
|
21 | 21 | // traceResolution = true |
|
22 | 22 | // baseUrl = "./" |
|
23 | 23 | // paths = [ "*": [ "$projectDir/src/typings/*" ] ] |
|
24 | 24 | // baseUrl = "$projectDir/src/typings" |
|
25 | 25 | // typeRoots = ["$projectDir/src/typings"] |
|
26 | 26 | } |
|
27 | 27 | |
|
28 | 28 | tscCmd = "$projectDir/node_modules/.bin/tsc" |
|
29 | 29 | tsLintCmd = "$projectDir/node_modules/.bin/tslint" |
|
30 | 30 | esLintCmd = "$projectDir/node_modules/.bin/eslint" |
|
31 | 31 | } |
|
32 | 32 | |
|
33 | 33 | configureTsMain { |
|
34 | 34 | compilerOptions { |
|
35 | 35 | types = ["requirejs", "dojo-typings"] |
|
36 | 36 | } |
|
37 | 37 | } |
|
38 | 38 | |
|
39 | 39 | npmPackMeta { |
|
40 | 40 | meta { |
|
41 | 41 | name = "@$npmScope/$project.name" |
|
42 | 42 | } |
|
43 | 43 | } |
|
44 | 44 | |
|
45 | 45 | task npmPackTypings(type: Copy) { |
|
46 | dependsOn sources.main.output | |
|
46 | dependsOn typings | |
|
47 | 47 | |
|
48 | 48 | npmPackContents.dependsOn it |
|
49 | 49 | |
|
50 |
from |
|
|
50 | from typescript.typingsDir | |
|
51 | 51 | into npm.packageDir |
|
52 | 52 | } |
|
53 | 53 | |
|
54 | 54 | task printVersion { |
|
55 | 55 | doLast { |
|
56 | 56 | println "packageName: ${npmPackMeta.metadata.get().name}"; |
|
57 | 57 | println "version: $version"; |
|
58 | 58 | println "target: $typescript.compilerOptions.target"; |
|
59 | 59 | println "module: $typescript.compilerOptions.module"; |
|
60 | 60 | println "symbols: $symbols"; |
|
61 | 61 | } |
|
62 | 62 | } No newline at end of file |
@@ -1,17 +1,17 | |||
|
1 | import inject = require("./inject"); | |
|
1 | import inject = require("./dom-inject"); | |
|
2 | 2 | import { id as mid} from "module"; |
|
3 | 3 | import { TraceSource } from "@implab/core-amd/log/TraceSource"; |
|
4 | 4 | const log = TraceSource.get(mid); |
|
5 | 5 | |
|
6 | 6 | const plugin = { |
|
7 | 7 | load: async (id: string, require: Require, cb: (param: any) => void) => { |
|
8 | 8 | const url = require.toUrl(id); |
|
9 | 9 | try { |
|
10 | 10 | await inject.injectStylesheet(url); |
|
11 | 11 | cb({ url }); |
|
12 | 12 | } catch (e) { |
|
13 | 13 | log.error("CSS plugin failed to load {0} ({1}): {2}", id, url, e); |
|
14 | 14 | } |
|
15 | 15 | } |
|
16 | 16 | }; |
|
17 | 17 | export = plugin; |
@@ -1,33 +1,33 | |||
|
1 | 1 | import { Constructor } from "@implab/core-amd/interfaces"; |
|
2 |
import { HtmlElementContext } from "./ |
|
|
3 |
import { WidgetContext } from "./ |
|
|
4 |
import { isWidgetConstructor, BuildContext } from "./ |
|
|
2 | import { HtmlElementContext } from "./tsx/HtmlElementContext"; | |
|
3 | import { WidgetContext } from "./tsx/WidgetContext"; | |
|
4 | import { isWidgetConstructor, BuildContext } from "./tsx/traits"; | |
|
5 | 5 | |
|
6 | 6 | export function createElement<T extends Constructor>(elementType: string | T, ...args: any[]): BuildContext { |
|
7 | 7 | if (typeof elementType === "string") { |
|
8 | 8 | const ctx = new HtmlElementContext(elementType); |
|
9 | 9 | if (args) |
|
10 | 10 | args.forEach(x => ctx.visitNext(x)); |
|
11 | 11 | |
|
12 | 12 | return ctx; |
|
13 | 13 | } else if (isWidgetConstructor(elementType)) { |
|
14 | 14 | const ctx = new WidgetContext(elementType); |
|
15 | 15 | if (args) |
|
16 | 16 | args.forEach(x => ctx.visitNext(x)); |
|
17 | 17 | |
|
18 | 18 | return ctx; |
|
19 | 19 | } else { |
|
20 | 20 | throw new Error(`The element type '${elementType}' is unsupported`); |
|
21 | 21 | } |
|
22 | 22 | } |
|
23 | 23 | |
|
24 | 24 | export interface EventDetails<T = any> { |
|
25 | 25 | detail: T; |
|
26 | 26 | } |
|
27 | 27 | |
|
28 | 28 | export interface EventSelector { |
|
29 | 29 | selectorTarget: HTMLElement; |
|
30 | 30 | target: HTMLElement; |
|
31 | 31 | } |
|
32 | 32 | |
|
33 | 33 | export type DojoMouseEvent<T = any> = MouseEvent & EventSelector & EventDetails<T>; |
@@ -1,16 +1,18 | |||
|
1 | 1 | import { test } from "./TestTraits"; |
|
2 | 2 | import { delay } from "@implab/core-amd/safe"; |
|
3 | 3 | import { assert } from "chai"; |
|
4 | import "@implab/djx"; | |
|
5 | import css = require("@implab/djx/css!my,css"); | |
|
4 | 6 | |
|
5 | 7 | test("simple", (ok, fail, log) => { |
|
6 | 8 | setTimeout(() => { |
|
7 | 9 | // end should be called after the last assertion |
|
8 | 10 | ok("async assert"); |
|
9 | 11 | }, 100); |
|
10 | 12 | }); |
|
11 | 13 | |
|
12 | 14 | test("simple", async log => { |
|
13 | 15 | await delay(0); |
|
14 | 16 | |
|
15 | 17 | assert.ok(true); // everything is fine |
|
16 | 18 | }); |
@@ -1,11 +1,18 | |||
|
1 | 1 | { |
|
2 | 2 | "extends": "../tsconfig", |
|
3 | 3 | "compilerOptions": { |
|
4 | "baseUrl": ".", | |
|
4 | 5 | "rootDir": "ts", |
|
5 | 6 | "rootDirs": [ |
|
6 | 7 | "ts", |
|
7 |
" |
|
|
8 | "typings", | |
|
9 | "../main/ts", | |
|
10 | "../main/typings" | |
|
8 | 11 | ], |
|
12 | "paths": { | |
|
13 | "@implab/djx" : ["../main/ts", "../main/typings"] | |
|
14 | //"@implab/djx/*" : ["../main/ts/*", "../main/typings/*" ] | |
|
15 | }, | |
|
9 | 16 | "types": ["requirejs"] |
|
10 | 17 | } |
|
11 | 18 | } No newline at end of file |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now