@@ -1,74 +1,74 | |||||
1 | plugins { |
|
1 | plugins { | |
2 | id "org.implab.gradle-typescript" version "1.3.3" |
|
2 | id "org.implab.gradle-typescript" version "1.3.3" | |
3 | id "ivy-publish" |
|
3 | id "ivy-publish" | |
4 | } |
|
4 | } | |
5 |
|
5 | |||
6 | typescript { |
|
6 | typescript { | |
7 | compilerOptions { |
|
7 | compilerOptions { | |
8 | lib = ["es5", "dom", "scripthost", "es2015.promise", "es2015.symbol", "es2015.iterable"] |
|
8 | lib = ["es5", "dom", "scripthost", "es2015.promise", "es2015.symbol", "es2015.iterable"] | |
9 | //listFiles = true |
|
9 | //listFiles = true | |
10 | declaration = true |
|
10 | declaration = true | |
11 | strict = true |
|
11 | strict = true | |
12 | types = [] |
|
12 | types = [] | |
13 | module = "amd" |
|
13 | module = "amd" | |
14 | it.target = "es5" |
|
14 | it.target = "es5" | |
15 | experimentalDecorators = true |
|
15 | experimentalDecorators = true | |
16 | jsx = "react" |
|
16 | jsx = "react" | |
17 | jsxFactory = "createElement" |
|
17 | jsxFactory = "createElement" | |
18 | moduleResolution = "node" |
|
18 | moduleResolution = "node" | |
19 | // dojo-typings are sick |
|
19 | // dojo-typings are sick | |
20 | skipLibCheck = true |
|
20 | skipLibCheck = true | |
21 | // traceResolution = true |
|
21 | // traceResolution = true | |
22 | // baseUrl = "./" |
|
22 | // baseUrl = "./" | |
23 | // paths = [ "*": [ "$projectDir/src/typings/*" ] ] |
|
23 | // paths = [ "*": [ "$projectDir/src/typings/*" ] ] | |
24 | // baseUrl = "$projectDir/src/typings" |
|
24 | // baseUrl = "$projectDir/src/typings" | |
25 | // typeRoots = ["$projectDir/src/typings"] |
|
25 | // typeRoots = ["$projectDir/src/typings"] | |
26 | } |
|
26 | } | |
27 |
|
27 | |||
28 | tscCmd = "$projectDir/node_modules/.bin/tsc" |
|
28 | tscCmd = "$projectDir/node_modules/.bin/tsc" | |
29 | tsLintCmd = "$projectDir/node_modules/.bin/tslint" |
|
29 | tsLintCmd = "$projectDir/node_modules/.bin/tslint" | |
30 | esLintCmd = "$projectDir/node_modules/.bin/eslint" |
|
30 | esLintCmd = "$projectDir/node_modules/.bin/eslint" | |
31 | } |
|
31 | } | |
32 |
|
32 | |||
33 | configureTsMain { |
|
33 | configureTsMain { | |
34 | compilerOptions { |
|
34 | compilerOptions { | |
35 | /*baseUrl = "$projectDir/src" |
|
35 | /*baseUrl = "$projectDir/src" | |
36 | paths = [ |
|
36 | paths = [ | |
37 | "dojo/*" : [ "typings/dojo/*" ], |
|
37 | "dojo/*" : [ "typings/dojo/*" ], | |
38 | "dijit/*" : [ "typings/dijit/*" ] |
|
38 | "dijit/*" : [ "typings/dijit/*" ] | |
39 | ]*/ |
|
39 | ]*/ | |
40 | types = ["requirejs", "dojo-typings"] |
|
40 | types = ["requirejs", "dojo-typings"] | |
41 | } |
|
41 | } | |
42 | } |
|
42 | } | |
43 |
|
43 | |||
44 | configureTsTest { |
|
44 | configureTsTest { | |
45 | compilerOptions { |
|
45 | compilerOptions { | |
46 | typeRoots = [] |
|
46 | typeRoots = [] | |
47 |
types = ["requirejs", sources.main.output.typingsDir.get().toString() |
|
47 | types = ["requirejs", sources.main.output.typingsDir.get().toString() ] | |
48 | } |
|
48 | } | |
49 | } |
|
49 | } | |
50 |
|
50 | |||
51 | npmPackMeta { |
|
51 | npmPackMeta { | |
52 | meta { |
|
52 | meta { | |
53 | name = "@$npmScope/$project.name" |
|
53 | name = "@$npmScope/$project.name" | |
54 | } |
|
54 | } | |
55 | } |
|
55 | } | |
56 |
|
56 | |||
57 | task npmPackTypings(type: Copy) { |
|
57 | task npmPackTypings(type: Copy) { | |
58 | dependsOn typings |
|
58 | dependsOn typings | |
59 |
|
59 | |||
60 | npmPackContents.dependsOn it |
|
60 | npmPackContents.dependsOn it | |
61 |
|
61 | |||
62 | from typescript.typingsDir |
|
62 | from typescript.typingsDir | |
63 | into npm.packageDir |
|
63 | into npm.packageDir | |
64 | } |
|
64 | } | |
65 |
|
65 | |||
66 | task printVersion { |
|
66 | task printVersion { | |
67 | doLast { |
|
67 | doLast { | |
68 | println "packageName: ${npmPackMeta.metadata.get().name}"; |
|
68 | println "packageName: ${npmPackMeta.metadata.get().name}"; | |
69 | println "version: $version"; |
|
69 | println "version: $version"; | |
70 | println "target: $typescript.compilerOptions.target"; |
|
70 | println "target: $typescript.compilerOptions.target"; | |
71 | println "module: $typescript.compilerOptions.module"; |
|
71 | println "module: $typescript.compilerOptions.module"; | |
72 | println "symbols: $symbols"; |
|
72 | println "symbols: $symbols"; | |
73 | } |
|
73 | } | |
74 | } No newline at end of file |
|
74 | } |
@@ -1,3 +1,198 | |||||
1 | # @implab/djx |
|
1 | # @implab/djx | |
2 |
|
2 | |||
3 | TODO No newline at end of file |
|
3 | ## SYNOPSIS | |
|
4 | ||||
|
5 | ```tsx | |||
|
6 | import { djbase, djclass, bind, prototype, AbstractConstructor } from "@implab/djx/declare"; | |||
|
7 | ||||
|
8 | import { DjxWidgetBase } from "@implab/djx/tsx/DjxWidgetBase"; | |||
|
9 | import { createElement } from "@implab/djx/tsx"; | |||
|
10 | ||||
|
11 | interface MyWidgetAttrs { | |||
|
12 | title: string; | |||
|
13 | ||||
|
14 | counter: number; | |||
|
15 | } | |||
|
16 | ||||
|
17 | interface MyWidgetEvents { | |||
|
18 | "count-inc": Event; | |||
|
19 | ||||
|
20 | "count-dec": Event; | |||
|
21 | } | |||
|
22 | ||||
|
23 | ||||
|
24 | @djclass | |||
|
25 | export class MyWidget extends djbase( | |||
|
26 | DjxWidgetBase as AbstractConstructor<DjxWidgetBase<MyWidgetAttrs, MyWidgetEvents>> | |||
|
27 | ) { | |||
|
28 | ||||
|
29 | @bind({ node: "titleNode", type: "innerHTML" }) | |||
|
30 | title = ""; | |||
|
31 | ||||
|
32 | @prototype() | |||
|
33 | counter = 0; | |||
|
34 | ||||
|
35 | render() { | |||
|
36 | const Frame = (props: any) => <div>{props.children}</div>; | |||
|
37 | return <div | |||
|
38 | className="myWidget" | |||
|
39 | tabIndex={3} | |||
|
40 | style={ alignContent: "center", border: "1px solid" } | |||
|
41 | > | |||
|
42 | <h1 data-dojo-attach-point="titleNode"></h1> | |||
|
43 | <Frame> | |||
|
44 | <span class="up-button" onclick={e => this._onIncClick(e)}>[+]</span> | |||
|
45 | <span class="down-button" onclick={() => this._onDecClick()}>[-]</span> | |||
|
46 | </Frame> | |||
|
47 | </div>; | |||
|
48 | } | |||
|
49 | ||||
|
50 | _onIncClick(e: MouseEvent) { | |||
|
51 | this.emit("count-inc", { bubbles: false }); | |||
|
52 | } | |||
|
53 | ||||
|
54 | _onDecClick() { | |||
|
55 | this.emit("count-dec", { bubbles: false }); | |||
|
56 | } | |||
|
57 | } | |||
|
58 | ||||
|
59 | ``` | |||
|
60 | ||||
|
61 | ## DESCRIPTION | |||
|
62 | ||||
|
63 | This package provides you with tools to glue your good-fellow dojo with modern | |||
|
64 | techniques of building the webapp. The core concept is built around widgets and | |||
|
65 | using .tsx to write it. Here some features: | |||
|
66 | ||||
|
67 | * `djbase()`, `@djaclass` - traits to declare your classes with `dojo/_base/declare` | |||
|
68 | * `@implab/djx/tsx` - traits to build the rendering of your widgets with tsx | |||
|
69 | * `DjxWidgetBase` - abstract class which supports tsx markup and | |||
|
70 | `data-dojo-attach-*` attributes. | |||
|
71 | * `@bind(...)` - annotations provides an easy way of using standard dojo widget | |||
|
72 | attribute bindings. | |||
|
73 | ||||
|
74 | ### djbase, @djclass | |||
|
75 | ||||
|
76 | These two traits provides convenient way of using `dojo/_base/declare` in Typescript | |||
|
77 | for declaring your classes. | |||
|
78 | ||||
|
79 | `djbase(...constructors)` - this method accepts a list of constructors in its | |||
|
80 | parameters and returns the **fake** base type which then can be used to derive | |||
|
81 | your own class. This allows you to provide the Typescript with the correct | |||
|
82 | information about the base type and even use `super`!. The only one caveat of | |||
|
83 | this approach is that you **MUST** decorate your class with `@djclass` annotation. | |||
|
84 | ||||
|
85 | Consider the following example: | |||
|
86 | ||||
|
87 | ```ts | |||
|
88 | import { djbase, djclass } from "@implab/djx/declare"; | |||
|
89 | import { FooMixin } from "./FooMixin"; | |||
|
90 | import { BarMixin } from "./BarMixin"; | |||
|
91 | import { BoxMixin } from "./BoxMixin"; | |||
|
92 | ||||
|
93 | @djclass | |||
|
94 | export class Baz extends djbase(FooMixin, BarMixin, BoxMixin) { | |||
|
95 | writeHello(out: string[]) { | |||
|
96 | out.push("-> Baz"); | |||
|
97 | ||||
|
98 | super.writeHello(out); | |||
|
99 | ||||
|
100 | out.push("<- Baz"); | |||
|
101 | } | |||
|
102 | } | |||
|
103 | ||||
|
104 | ``` | |||
|
105 | ||||
|
106 | All mixins are declared like the one below: | |||
|
107 | ||||
|
108 | ```ts | |||
|
109 | import { djclass, djbase } from "@implab/djx/declare"; | |||
|
110 | ||||
|
111 | interface Super { | |||
|
112 | writeHello(out: string[]): void; | |||
|
113 | ||||
|
114 | } | |||
|
115 | ||||
|
116 | @djclass | |||
|
117 | export class BarMixin extends djbase<Super>() { | |||
|
118 | writeHello(out: string[]) { | |||
|
119 | out.push("-> Bar"); | |||
|
120 | ||||
|
121 | super.writeHello(out); | |||
|
122 | ||||
|
123 | out.push("<- Bar"); | |||
|
124 | } | |||
|
125 | } | |||
|
126 | ``` | |||
|
127 | ||||
|
128 | finally create an instance and call the `writeHello` method | |||
|
129 | ||||
|
130 | ```ts | |||
|
131 | const baz = new Baz(); | |||
|
132 | ||||
|
133 | const data: string[] = []; | |||
|
134 | baz.writeHello(data); | |||
|
135 | ||||
|
136 | console.log(data.join("\n")); | |||
|
137 | ||||
|
138 | ``` | |||
|
139 | ||||
|
140 | you will get the following output: | |||
|
141 | ||||
|
142 | ```text | |||
|
143 | -> Baz | |||
|
144 | -> Box | |||
|
145 | -> Bar | |||
|
146 | -> Foo | |||
|
147 | <- Foo | |||
|
148 | <- Bar | |||
|
149 | <- Box | |||
|
150 | <- Baz | |||
|
151 | ``` | |||
|
152 | ||||
|
153 | Let's take a closer look to the `Baz` declaration it uses `djbase` to derive | |||
|
154 | from three mixins and the class is decorated with `@djclass` to accomplish the | |||
|
155 | declaration and make a real constructor. | |||
|
156 | ||||
|
157 | To allow an access to the next sibling method (in terms of multiple inheritance) | |||
|
158 | Dojo provides `this.inherited(arguments)` method but this approach leads to the | |||
|
159 | problem with 'strict' mode of ES5 and eliminates the type information about a | |||
|
160 | calling method. This library solves the problem calling inherited/next method by | |||
|
161 | utilizing `super` keyword. Under the hood there are proxy methods generated in | |||
|
162 | the prototype of the declared class which make calls to `this.inherited(...)` | |||
|
163 | method. This technique is compatible with 'strict' mode. | |||
|
164 | ||||
|
165 | Mixins are declared the similar, they are also may have the base types although | |||
|
166 | the most common case is declaring the mixin without any base classes. To allow | |||
|
167 | the mixin to access the next method you should declare the interface with | |||
|
168 | desired methods and use the special form of `djbase<Super>()` without arguments. | |||
|
169 | ||||
|
170 | ### DjxWidgetBase<Attrs, Events> | |||
|
171 | ||||
|
172 | TODO | |||
|
173 | ||||
|
174 | ### Markup (.tsx) | |||
|
175 | ||||
|
176 | Add to your `tsconfig.json` the following options | |||
|
177 | ||||
|
178 | ```json | |||
|
179 | { | |||
|
180 | "compilerOptions": { | |||
|
181 | "types": ["@implab/djx"], | |||
|
182 | "experimentalDecorators": true, | |||
|
183 | "jsxFactory": "createElement", | |||
|
184 | "jsx": "react", | |||
|
185 | } | |||
|
186 | } | |||
|
187 | ||||
|
188 | ``` | |||
|
189 | ||||
|
190 | Import `createElement` into your `.tsx` file | |||
|
191 | ||||
|
192 | ```ts | |||
|
193 | import { createElement } from "@implab/djx/tsx"; | |||
|
194 | ``` | |||
|
195 | ||||
|
196 | You are ready to go! | |||
|
197 | ||||
|
198 | TODO |
@@ -1,34 +1,52 | |||||
1 | /// <reference path="./css.d.ts"/> |
|
1 | /// <reference path="./css.d.ts"/> | |
2 | /// <reference path="./dijit.d.ts"/> |
|
2 | /// <reference path="./dijit.d.ts"/> | |
3 |
|
3 | |||
4 | declare namespace JSX { |
|
4 | declare namespace JSX { | |
5 |
|
5 | |||
6 | interface DjxIntrinsicAttributes { |
|
6 | interface DjxIntrinsicAttributes { | |
|
7 | /** alias for className */ | |||
7 | class: string; |
|
8 | class: string; | |
|
9 | ||||
|
10 | /** specifies the name of the property in the widget where the the | |||
|
11 | * reference to the current object will be stored | |||
|
12 | */ | |||
8 | "data-dojo-attach-point": string; |
|
13 | "data-dojo-attach-point": string; | |
|
14 | ||||
|
15 | /** specifies handlers map for the events */ | |||
9 | "data-dojo-attach-event": string; |
|
16 | "data-dojo-attach-event": string; | |
|
17 | ||||
|
18 | [attr: string]: any; | |||
|
19 | } | |||
|
20 | ||||
|
21 | interface DjxIntrinsicElements { | |||
10 | } |
|
22 | } | |
11 |
|
23 | |||
12 | type RecursivePartial<T> = T extends string | number | boolean | null | undefined | Function ? |
|
24 | type RecursivePartial<T> = T extends string | number | boolean | null | undefined | Function ? | |
13 | T : |
|
25 | T : | |
14 | { [k in keyof T]?: RecursivePartial<T[k]> }; |
|
26 | { [k in keyof T]?: RecursivePartial<T[k]> }; | |
15 |
|
27 | |||
16 | type MatchingMemberKeys<T, U> = { |
|
28 | type MatchingMemberKeys<T, U> = { | |
17 | [K in keyof T]: T[K] extends U ? K : never; |
|
29 | [K in keyof T]: T[K] extends U ? K : never; | |
18 | }[keyof T]; |
|
30 | }[keyof T]; | |
19 | type NotMatchingMemberKeys<T, U> = { |
|
31 | type NotMatchingMemberKeys<T, U> = { | |
20 | [K in keyof T]: T[K] extends U ? never : K; |
|
32 | [K in keyof T]: T[K] extends U ? never : K; | |
21 | }[keyof T]; |
|
33 | }[keyof T]; | |
|
34 | ||||
|
35 | type ExtractMembers<T, U> = Pick<T, MatchingMemberKeys<T, U>>; | |||
|
36 | ||||
|
37 | type ExcludeMembers<T, U> = Pick<T, NotMatchingMemberKeys<T, U>>; | |||
|
38 | ||||
22 | type ElementAttrNames<E> = NotMatchingMemberKeys<E, (...args: any[]) => any>; |
|
39 | type ElementAttrNames<E> = NotMatchingMemberKeys<E, (...args: any[]) => any>; | |
23 |
|
40 | |||
24 | type ElementAttrType<E, K extends keyof any> = K extends keyof E ? RecursivePartial<E[K]> : string; |
|
41 | type ElementAttrType<E, K extends keyof any> = K extends keyof E ? RecursivePartial<E[K]> : string; | |
25 |
|
42 | |||
26 | type LaxElement<E extends object> = E & { } |
|
43 | type LaxElement<E extends object> = ExcludeMembers<Omit<E, "children">, (...args: any[]) => any> & DjxIntrinsicAttributes; | |
27 |
|
44 | |||
28 |
type L |
|
45 | type LaxIntrinsicElementsMap = { | |
29 | [attr in ElementAttrNames<E>]?: ElementAttrType<E, attr>; |
|
46 | [tag in keyof HTMLElementTagNameMap]: LaxElement<HTMLElementTagNameMap[tag]> | |
30 |
} |
|
47 | } & DjxIntrinsicElements; | |
31 | interface IntrinsicElements { |
|
48 | ||
32 | [tag: keyof HTMLElementTagNameMap]: LegacyElementAttributes<HTMLElementTagNameMap[tag]>; |
|
49 | type IntrinsicElements = { | |
|
50 | [tag in keyof LaxIntrinsicElementsMap]: RecursivePartial<LaxIntrinsicElementsMap[tag]>; | |||
33 | } |
|
51 | } | |
34 | } |
|
52 | } |
@@ -1,51 +1,46 | |||||
1 | import { djbase, djclass, bind, prototype, AbstractConstructor } from "../declare"; |
|
1 | import { djbase, djclass, bind, prototype, AbstractConstructor } from "../declare"; | |
2 |
|
2 | |||
3 | import { DjxWidgetBase } from "../tsx/DjxWidgetBase"; |
|
3 | import { DjxWidgetBase } from "../tsx/DjxWidgetBase"; | |
4 | import { createElement } from "../tsx"; |
|
4 | import { createElement } from "../tsx"; | |
5 |
|
5 | |||
6 | interface MyWidgetAttrs { |
|
6 | interface MyWidgetAttrs { | |
7 | title: string; |
|
7 | title: string; | |
8 |
|
8 | |||
9 | counter: number; |
|
9 | counter: number; | |
10 | } |
|
10 | } | |
11 |
|
11 | |||
12 | interface MyWidgetEvents { |
|
12 | interface MyWidgetEvents { | |
13 | "count-inc": Event; |
|
13 | "count-inc": Event; | |
14 |
|
14 | |||
15 | "count-dec": Event; |
|
15 | "count-dec": Event; | |
16 | } |
|
16 | } | |
17 |
|
17 | |||
18 |
|
18 | |||
19 | @djclass |
|
19 | @djclass | |
20 | export class MyWidget extends djbase(DjxWidgetBase as AbstractConstructor<DjxWidgetBase<MyWidgetAttrs, MyWidgetEvents>>) { |
|
20 | export class MyWidget extends djbase(DjxWidgetBase as AbstractConstructor<DjxWidgetBase<MyWidgetAttrs, MyWidgetEvents>>) { | |
21 |
|
21 | |||
22 | @bind({ node: "titleNode", type: "innerHTML" }) |
|
22 | @bind({ node: "titleNode", type: "innerHTML" }) | |
23 | title = ""; |
|
23 | title = ""; | |
24 |
|
24 | |||
25 | @prototype() |
|
25 | @prototype() | |
26 | counter = 0; |
|
26 | counter = 0; | |
27 |
|
27 | |||
28 | render() { |
|
28 | render() { | |
29 | const Frame = (props: any) => <div>{props.children}</div>; |
|
29 | const Frame = (props: any) => <div>{props.children}</div>; | |
30 |
return <div |
|
30 | return <div className="myWidget" tabIndex={3} style={{ alignContent: "center", border: "1px solid" }} > | |
31 | <h1 data-dojo-attach-point="titleNode"></h1> |
|
31 | <h1 data-dojo-attach-point="titleNode"></h1> | |
32 | <Frame> |
|
32 | <Frame> | |
33 | <span class="up-button" onclick={e => this._onIncClick(e)}>[+]</span> |
|
33 | <span class="up-button" onclick={e => this._onIncClick(e)}>[+]</span> | |
34 |
<span class="down-button" |
|
34 | <span class="down-button" onclick={() => this._onDecClick()}>[-]</span> | |
35 | </Frame> |
|
35 | </Frame> | |
36 | <table style="pretty"> |
|
|||
37 | <tr> |
|
|||
38 | <td colSpan={2} colspan="3" onclick=""></td> |
|
|||
39 | </tr> |
|
|||
40 | </table> |
|
|||
41 | </div>; |
|
36 | </div>; | |
42 | } |
|
37 | } | |
43 |
|
38 | |||
44 | _onIncClick(e: MouseEvent) { |
|
39 | _onIncClick(e: MouseEvent) { | |
45 | this.emit("count-inc", { bubbles: false }); |
|
40 | this.emit("count-inc", { bubbles: false }); | |
46 | } |
|
41 | } | |
47 |
|
42 | |||
48 | _onDecClick() { |
|
43 | _onDecClick() { | |
49 | this.emit("count-dec", { bubbles: false }); |
|
44 | this.emit("count-dec", { bubbles: false }); | |
50 | } |
|
45 | } | |
51 | } |
|
46 | } |
@@ -1,13 +1,13 | |||||
1 | { |
|
1 | { | |
2 | "extends": "../tsconfig", |
|
2 | "extends": "../tsconfig", | |
3 | "compilerOptions": { |
|
3 | "compilerOptions": { | |
4 | //"rootDir": "ts", |
|
4 | //"rootDir": "ts", | |
5 | "rootDirs": [ |
|
5 | "rootDirs": [ | |
6 | "ts", |
|
6 | "ts", | |
7 | "typings", |
|
7 | "typings", | |
8 | "../main/ts", |
|
8 | "../main/ts", | |
9 | "../main/typings" |
|
9 | "../main/typings" | |
10 | ], |
|
10 | ], | |
11 |
"types": ["requirejs", "../main/typings |
|
11 | "types": ["requirejs", "../main/typings", "dojo-typings"] | |
12 | } |
|
12 | } | |
13 | } No newline at end of file |
|
13 | } |
General Comments 0
You need to be logged in to leave comments.
Login now