##// END OF EJS Templates
Merge with v1.3
cin -
r104:15c829aa08a2 merge v1.3.0 default
parent child
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,147
1 /**
2 * The interface for the consumer of an observable sequence
3 */
4 export interface Observer<T> {
5 /**
6 * Called for the next element in the sequence
7 */
8 next: (value: T) => void;
9
10 /**
11 * Called once when the error occurs in the sequence.
12 */
13 error: (e: unknown) => void;
14
15 /**
16 * Called once at the end of the sequence.
17 */
18 complete: () => void;
19 }
20
21 /**
22 * The group of functions to feed an observable. This methods are provided to
23 * the producer to generate a stream of events.
24 */
25 export type Sink<T> = {
26 [k in keyof Observer<T>]: (this: void, ...args: Parameters<Observer<T>[k]>) => void;
27 };
28
29 export type Producer<T> = (sink: Sink<T>) => (void | (() => void));
30
31 export interface Unsubscribable {
32 unsubscribe(): void;
33 }
34
35 export const isUnsubsribable = (v: unknown): v is Unsubscribable =>
36 v !== null && v !== undefined && typeof (v as Unsubscribable).unsubscribe === "function";
37
38 export const isSubsribable = (v: unknown): v is Subscribable<unknown> =>
39 v !== null && v !== undefined && typeof (v as Subscribable<unknown>).subscribe === "function";
40
41 export interface Subscribable<T> {
42 subscribe(consumer: Partial<Observer<T>>): Unsubscribable;
43 }
44
45 /** The observable source of items. */
46 export interface Observable<T> extends Subscribable<T> {
47 /** Transforms elements of the sequence with the specified mapper
48 *
49 * @param mapper The mapper used to transform the values
50 */
51 map<T2>(mapper: (value: T) => T2): Observable<T2>;
52
53 /** Filters elements of the sequence. The resulting sequence will
54 * contain only elements which match the specified predicate.
55 *
56 * @param predicate The filter predicate.
57 */
58 filter(predicate: (value: T) => boolean): Observable<T>;
59
60 /** Applies accumulator to each value in the sequence and
61 * emits the accumulated value for each source element
62 *
63 * @param accumulator
64 * @param initial
65 */
66 scan<A>(accumulator: (acc: A, value: T) => A, initial: A): Observable<A>;
67 }
68
69 const noop = () => { };
70
71 const sink = <T>(consumer: Partial<Observer<T>>) => {
72 const { next, error, complete } = consumer;
73 return {
74 next: next ? next.bind(consumer) : noop,
75 error: error ? error.bind(consumer) : noop,
76 complete: complete ? complete.bind(consumer) : noop
77 }
78 };
79
80 const fuse = <T>({ next, error, complete }: Sink<T>) => {
81 let done = false;
82 return {
83 next: (value: T) => { !done && next(value) },
84 error: (e: unknown) => { !done && (done = true, error(e)) },
85 complete: () => { !done && (done = true, complete()) }
86 }
87 }
88
89 const _observe = <T>(producer: Producer<T>): Observable<T> => ({
90 subscribe: (consumer: Partial<Observer<T>>) => ({
91 unsubscribe: producer(sink(consumer)) ?? noop
92 }),
93 map: (mapper) => _observe(({ next, error, complete }) =>
94 producer({
95 next: next !== noop ? (v: T) => next(mapper(v)) : noop,
96 error,
97 complete
98 })
99 ),
100 filter: (predicate) => _observe(({ next, error, complete }) =>
101 producer({
102 next: next !== noop ?
103 (v: T) => predicate(v) ? next(v) : void(0) : noop,
104 error,
105 complete
106 })
107 ),
108 scan: (accumulator, initial) => _observe(({ next, error, complete }) => {
109 let _acc = initial;
110 return producer({
111 next: next !== noop ?
112 (v: T) => next(_acc = accumulator(_acc, v)) : noop,
113 error,
114 complete
115 });
116 })
117 });
118
119 export const observe = <T>(producer: Producer<T>): Observable<T> => ({
120 subscribe: (consumer: Partial<Observer<T>>) => ({
121 unsubscribe: producer(fuse(sink(consumer))) ?? noop
122 }),
123 map: (mapper) => _observe(({ next, error, complete }) =>
124 producer(fuse({
125 next: next !== noop ?
126 (v: T) => next(mapper(v)) : noop,
127 error,
128 complete
129 }))
130 ),
131 filter: (predicate) => _observe(({ next, error, complete }) =>
132 producer(fuse({
133 next: next !== noop ?
134 (v: T) => predicate(v) ? next(v) : void (0) : noop,
135 error,
136 complete
137 }))
138 ),
139 scan: (accumulator, initial?) => observe(({ next, error, complete }) => {
140 let _acc = initial;
141 return producer(fuse({
142 next: next !== noop ? (v: T) => next(_acc = accumulator(_acc, v)) : noop,
143 error,
144 complete
145 }));
146 })
147 });
@@ -0,0 +1,13
1 import * as t from "tap";
2 import { Baz } from "./mock/Baz";
3
4 t.comment("Declare tests");
5
6 const baz = new Baz();
7
8 const data: string[] = [];
9 baz.writeHello(data);
10 t.pass("Test complete");
11
12 // tslint:disable-next-line: no-console
13 t.comment(data.join("\n"));
@@ -0,0 +1,52
1 import { observe } from "./observable";
2 import * as t from "tap";
3
4 const subj1 = observe<number>(({ next, complete }) => {
5 next(1);
6 complete();
7 next(2);
8 });
9
10 const consumer1 = {
11 sum: 0,
12 next(v: number) {
13 this.sum += v;
14 }
15 }
16
17 subj1.subscribe(consumer1);
18 t.equal(consumer1.sum, 1, "Should get only one value");
19
20 subj1.subscribe(consumer1);
21 t.equal(consumer1.sum, 2, "Should get the value again");
22
23 const consumer2 = {
24 value: 0,
25 completed: false,
26 next(v: number) { this.value = v; },
27 complete() { this.completed = true; }
28 };
29
30 let maps = 0;
31
32 subj1
33 .map(v => {
34 t.comment("map1: " + v * 2);
35 maps++;
36 return v * 2;
37 })
38 .map (v => {
39 t.comment("map2: " + v * 2);
40 maps++;
41 return v * 2;
42 })
43 .map(v => {
44 t.comment("map3: " + v * 2);
45 maps++;
46 return v * 2
47 })
48 .subscribe(consumer2);
49
50 t.equal(consumer2.value, 8, "Should map");
51 t.equal(maps, 3, "The map chain should not be executed after completion");
52 t.ok(consumer2.completed, "The completion signal should pass through"); No newline at end of file
@@ -0,0 +1,1
1 symbols=local No newline at end of file
@@ -0,0 +1,126
1 plugins {
2 id "org.implab.gradle-typescript" version "1.3.4"
3 id "ivy-publish"
4 }
5
6 def container = "djx-playground"
7
8 configurations {
9 npmLocal
10 }
11
12 dependencies {
13 npmLocal project(":djx")
14 }
15
16 def bundleDir = fileTree(layout.buildDirectory.dir("bundle")) {
17 builtBy "bundle"
18 }
19
20 typescript {
21 compilerOptions {
22 lib = ["es5", "dom", "scripthost", "es2015.promise", "es2015.symbol", "es2015.iterable"]
23 // listFiles = true
24 strict = true
25 types = ["requirejs", "@implab/dojo-typings", "@implab/djx"]
26 module = "amd"
27 it.target = "es5"
28 experimentalDecorators = true
29 noUnusedLocals = false
30 jsx = "react"
31 jsxFactory = "createElement"
32 moduleResolution = "node"
33 // dojo-typings are sick
34 skipLibCheck = true
35 // traceResolution = true
36 // baseUrl = "./"
37 // paths = [ "*": [ "$projectDir/src/typings/*" ] ]
38 // baseUrl = "$projectDir/src/typings"
39 // typeRoots = ["$projectDir/src/typings"]
40 }
41 tscCmd = "$projectDir/node_modules/.bin/tsc"
42 tsLintCmd = "$projectDir/node_modules/.bin/tslint"
43 esLintCmd = "$projectDir/node_modules/.bin/eslint"
44 }
45
46 tasks.matching{ it.name =~ /^configureTs/ }.configureEach {
47 compilerOptions {
48 if (symbols != 'none') {
49 sourceMap = true
50 switch(symbols) {
51 case "local":
52 sourceRoot = ( isWindows ? "file:///" : "file://" ) + it.rootDir
53 break;
54 }
55 }
56 }
57 }
58
59 npmInstall {
60 //npmInstall.dependsOn it
61 dependsOn configurations.npmLocal
62
63 doFirst {
64 configurations.npmLocal.each { f ->
65 exec {
66 commandLine "npm", "install", f, "--save-dev"
67 }
68 }
69 }
70 }
71
72 clean {
73 doFirst {
74 delete "$buildDir/bundle"
75 }
76 }
77
78
79 task processResourcesBundle(type: Copy) {
80 from "src/bundle"
81 into layout.buildDirectory.dir("bundle")
82 }
83
84 task copyModules(type: Copy) {
85 dependsOn npmInstall
86 into layout.buildDirectory.dir("bundle/js");
87
88 def pack = { String jsmod ->
89 into(jsmod) {
90 from npm.module(jsmod)
91 }
92 }
93
94
95 pack("@implab/djx")
96 pack("@implab/core-amd")
97 pack("dojo")
98 pack("dijit")
99 into("rxjs") {
100 from(npm.module("rxjs/dist/bundles"))
101 }
102 from npm.module("requirejs/require.js")
103 }
104
105 task copyApp(type: Copy) {
106 dependsOn assemble
107 from typescript.assemblyDir
108 into layout.buildDirectory.dir("bundle/js/app")
109 }
110
111 task bundle {
112 dependsOn copyModules, processResourcesBundle, copyApp
113 }
114
115 task up(type: Exec) {
116 dependsOn bundle
117 commandLine "podman", "run", "--rm", "-d",
118 "--name", container,
119 "-p", "2078:80",
120 "-v", "$buildDir/bundle:/srv/www/htdocs",
121 "registry.implab.org/implab/apache2:latest"
122 }
123
124 task stop(type: Exec) {
125 commandLine "podman", "stop", container
126 } No newline at end of file
@@ -0,0 +1,170
1 {
2 "name": "@implab/djx-playground",
3 "lockfileVersion": 2,
4 "requires": true,
5 "packages": {
6 "": {
7 "name": "@implab/djx-playground",
8 "dependencies": {
9 "dijit": "1.17.3",
10 "dojo": "1.17.3",
11 "requirejs": "2.3.6",
12 "rxjs": "7.5.6"
13 },
14 "devDependencies": {
15 "@implab/core-amd": "1.4.6",
16 "@implab/djx": "file:../djx/build/npm/package",
17 "@implab/dojo-typings": "1.0.2",
18 "@types/requirejs": "2.1.34",
19 "typescript": "4.8.2"
20 }
21 },
22 "../djx/build/npm/package": {
23 "name": "@implab/djx",
24 "dev": true,
25 "license": "BSD-2-Clause",
26 "peerDependencies": {
27 "@implab/core-amd": "^1.4.0",
28 "dojo": "^1.10.0"
29 }
30 },
31 "node_modules/@implab/core-amd": {
32 "version": "1.4.6",
33 "resolved": "https://registry.npmjs.org/@implab/core-amd/-/core-amd-1.4.6.tgz",
34 "integrity": "sha512-I1RwUAxeiodePpiBzveoHaehMSAyk7NFPPPEvDqfphHBC8yXoXWAaUrp7EcOKEzjXAs7lJQVhNpmjCjIqoj6BQ==",
35 "dev": true,
36 "peerDependencies": {
37 "dojo": "^1.10.0"
38 }
39 },
40 "node_modules/@implab/djx": {
41 "resolved": "../djx/build/npm/package",
42 "link": true
43 },
44 "node_modules/@implab/dojo-typings": {
45 "version": "1.0.2",
46 "resolved": "https://registry.npmjs.org/@implab/dojo-typings/-/dojo-typings-1.0.2.tgz",
47 "integrity": "sha512-/lbcMCHdRoHJLKFcT8xdk1KbGazSlb1pGSDJ406io7iMenPm/XbJYcUti+VzXnn71zOJ8aYpGT12T5L0rfOZNA==",
48 "dev": true
49 },
50 "node_modules/@types/requirejs": {
51 "version": "2.1.34",
52 "resolved": "https://registry.npmjs.org/@types/requirejs/-/requirejs-2.1.34.tgz",
53 "integrity": "sha512-iQLGNE1DyIRYih60B47l/hI5X7J0wAnnRBL6Yn85GUYQg8Fm3wl8kvT6NRwncKroUOSx7/lbAagIFNV7y02DiQ==",
54 "dev": true
55 },
56 "node_modules/dijit": {
57 "version": "1.17.3",
58 "resolved": "https://registry.npmjs.org/dijit/-/dijit-1.17.3.tgz",
59 "integrity": "sha512-QS+1bNhPT+BF9E+iomQSi5qI+o3oUNSx1r5TF8WlGH4LybGZP+IIGJBOO5/41YduBPljVXhY7vaPsgrycxC6UQ==",
60 "dependencies": {
61 "dojo": "1.17.3"
62 }
63 },
64 "node_modules/dojo": {
65 "version": "1.17.3",
66 "resolved": "https://registry.npmjs.org/dojo/-/dojo-1.17.3.tgz",
67 "integrity": "sha512-iWDx1oSfCEDnIrs8cMW7Zh9Fbjgxu8iRagFz+Qi2eya3MXIAxFXKhv2A7dpi+bfpMpFozLwcsLV8URLw6BsHsA=="
68 },
69 "node_modules/requirejs": {
70 "version": "2.3.6",
71 "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz",
72 "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg==",
73 "bin": {
74 "r_js": "bin/r.js",
75 "r.js": "bin/r.js"
76 },
77 "engines": {
78 "node": ">=0.4.0"
79 }
80 },
81 "node_modules/rxjs": {
82 "version": "7.5.6",
83 "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz",
84 "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==",
85 "dependencies": {
86 "tslib": "^2.1.0"
87 }
88 },
89 "node_modules/tslib": {
90 "version": "2.4.0",
91 "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
92 "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
93 },
94 "node_modules/typescript": {
95 "version": "4.8.2",
96 "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz",
97 "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==",
98 "dev": true,
99 "bin": {
100 "tsc": "bin/tsc",
101 "tsserver": "bin/tsserver"
102 },
103 "engines": {
104 "node": ">=4.2.0"
105 }
106 }
107 },
108 "dependencies": {
109 "@implab/core-amd": {
110 "version": "1.4.6",
111 "resolved": "https://registry.npmjs.org/@implab/core-amd/-/core-amd-1.4.6.tgz",
112 "integrity": "sha512-I1RwUAxeiodePpiBzveoHaehMSAyk7NFPPPEvDqfphHBC8yXoXWAaUrp7EcOKEzjXAs7lJQVhNpmjCjIqoj6BQ==",
113 "dev": true,
114 "requires": {}
115 },
116 "@implab/djx": {
117 "version": "file:../djx/build/npm/package",
118 "requires": {}
119 },
120 "@implab/dojo-typings": {
121 "version": "1.0.2",
122 "resolved": "https://registry.npmjs.org/@implab/dojo-typings/-/dojo-typings-1.0.2.tgz",
123 "integrity": "sha512-/lbcMCHdRoHJLKFcT8xdk1KbGazSlb1pGSDJ406io7iMenPm/XbJYcUti+VzXnn71zOJ8aYpGT12T5L0rfOZNA==",
124 "dev": true
125 },
126 "@types/requirejs": {
127 "version": "2.1.34",
128 "resolved": "https://registry.npmjs.org/@types/requirejs/-/requirejs-2.1.34.tgz",
129 "integrity": "sha512-iQLGNE1DyIRYih60B47l/hI5X7J0wAnnRBL6Yn85GUYQg8Fm3wl8kvT6NRwncKroUOSx7/lbAagIFNV7y02DiQ==",
130 "dev": true
131 },
132 "dijit": {
133 "version": "1.17.3",
134 "resolved": "https://registry.npmjs.org/dijit/-/dijit-1.17.3.tgz",
135 "integrity": "sha512-QS+1bNhPT+BF9E+iomQSi5qI+o3oUNSx1r5TF8WlGH4LybGZP+IIGJBOO5/41YduBPljVXhY7vaPsgrycxC6UQ==",
136 "requires": {
137 "dojo": "1.17.3"
138 }
139 },
140 "dojo": {
141 "version": "1.17.3",
142 "resolved": "https://registry.npmjs.org/dojo/-/dojo-1.17.3.tgz",
143 "integrity": "sha512-iWDx1oSfCEDnIrs8cMW7Zh9Fbjgxu8iRagFz+Qi2eya3MXIAxFXKhv2A7dpi+bfpMpFozLwcsLV8URLw6BsHsA=="
144 },
145 "requirejs": {
146 "version": "2.3.6",
147 "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.6.tgz",
148 "integrity": "sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg=="
149 },
150 "rxjs": {
151 "version": "7.5.6",
152 "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz",
153 "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==",
154 "requires": {
155 "tslib": "^2.1.0"
156 }
157 },
158 "tslib": {
159 "version": "2.4.0",
160 "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
161 "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
162 },
163 "typescript": {
164 "version": "4.8.2",
165 "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz",
166 "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==",
167 "dev": true
168 }
169 }
170 }
@@ -0,0 +1,17
1 {
2 "name": "@implab/djx-playground",
3 "private": true,
4 "dependencies": {
5 "dijit": "1.17.3",
6 "dojo": "1.17.3",
7 "requirejs": "2.3.6",
8 "rxjs": "7.5.6"
9 },
10 "devDependencies": {
11 "@implab/core-amd": "1.4.6",
12 "@implab/djx": "file:../djx/build/npm/package",
13 "@implab/dojo-typings": "1.0.2",
14 "@types/requirejs": "2.1.34",
15 "typescript": "4.8.2"
16 }
17 }
@@ -0,0 +1,16
1 requirejs.config({
2 baseUrl: "js",
3 packages: [
4 "app",
5 "@implab/djx",
6 "@implab/core-amd",
7 "dojo",
8 "dijit",
9 {
10 name: "rxjs",
11 location: "rxjs",
12 main: "rxjs.umd.min"
13 }
14 ],
15 deps: ["app"]
16 });
@@ -0,0 +1,13
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset='utf-8'>
5 <meta http-equiv='X-UA-Compatible' content='IE=edge'>
6 <title>Djx playground</title>
7 <meta name='viewport' content='width=device-width, initial-scale=1'>
8 <script data-main="config.js" src='js/require.js'></script>
9 </head>
10 <body>
11
12 </body>
13 </html> No newline at end of file
@@ -0,0 +1,39
1 .progress-bar * {
2 transition: all 0.5s;
3 }
4
5 .progress-bar {
6
7 transition: all 0.5s;
8 height: 1em;
9 line-height: 0;
10 font-size: 10px;
11 white-space: nowrap;
12 }
13
14 .progress-track {
15 background-color: #888;
16 height: 0.2em;
17 box-shadow: 0 0 0.5em 0 #0004;
18 width: 70%;
19 display: inline-block;
20 vertical-align: middle;
21 }
22
23 .progress-indicator {
24 width: 30%;
25 background-color: #0ae;
26 height: 100%;
27 display: inline-block;
28 vertical-align: middle;
29 box-shadow: 0 0.6em 1em -0.4em #000;
30 }
31
32 .progress-bar:hover .progress-indicator {
33 box-shadow: 0 1em 2em -0.4em #000;
34 }
35
36 .progress-bar:hover .progress-track {
37 background-color: #0ae;
38 box-shadow: 0 0 0.5em 0 #0ae;
39 } No newline at end of file
@@ -0,0 +1,82
1 import { djbase, djclass } from "@implab/djx/declare";
2 import { DjxWidgetBase } from "@implab/djx/tsx/DjxWidgetBase";
3 import { createElement, watch, prop, attach, all, bind, toggleClass } from "@implab/djx/tsx";
4 import ProgressBar from "./ProgressBar";
5 import Button = require("dijit/form/Button");
6 import { interval } from "rxjs";
7
8 @djclass
9 export default class MainWidget extends djbase(DjxWidgetBase) {
10
11 titleNode?: HTMLHeadingElement;
12
13 progressBar?: ProgressBar;
14
15 count = 0;
16
17 showCounter = false;
18
19 counterNode?: HTMLInputElement;
20
21 paused = false;
22
23 render() {
24 const Counter = ({ children }: { children: unknown[] }) => <span>Counter: {children}</span>;
25
26 return <div className="tundra">
27 <h2 ref={attach(this, "titleNode")}>Hi!</h2>
28 <ProgressBar ref={attach(this, "progressBar")} />
29 <section style={{ padding: "10px" }}>
30 {watch(prop(this, "showCounter"), flag => flag &&
31 [
32 <Counter><input ref={all(
33 bind("value", prop(this, "count")
34 .map(x => x*10)
35 .map(String)
36 ),
37 attach(this, "counterNode")
38 )} /> <span>ms</span></Counter>,
39 " | ",
40 <span ref={bind("innerHTML", interval(1000))}></span>,
41 " | ",
42 <Button
43 ref={all(
44 bind("label", prop(this, "paused")
45 .map(x => x ? "Unpause" : "Pause")
46 ),
47 toggleClass("paused", prop(this,"paused"))
48 )}
49 onClick={this._onPauseClick}
50 />
51 ]
52
53 )}
54 </section>
55 <Button onClick={this._onToggleCounterClick}>Toggle counter</Button>
56 </div>;
57 }
58
59 postCreate(): void {
60 super.postCreate();
61
62 const h = setInterval(
63 () => {
64 this.set("count", this.count + 1);
65 },
66 10
67 );
68 this.own({
69 destroy: () => {
70 clearInterval(h);
71 }
72 });
73 }
74
75 private _onPauseClick = () => {
76 this.set("paused", !this.paused);
77 }
78
79 private _onToggleCounterClick = () => {
80 this.set("showCounter", !this.showCounter);
81 }
82 }
@@ -0,0 +1,19
1 import { djbase, djclass } from "@implab/djx/declare";
2 import { createElement } from "@implab/djx/tsx";
3 import { DjxWidgetBase } from "@implab/djx/tsx/DjxWidgetBase";
4 import "@implab/djx/css!./css/ProgressBar.css";
5
6 @djclass
7 export default class ProgressBar extends djbase(DjxWidgetBase) {
8
9 constructor(opts?: Partial<ProgressBar> & ThisType<ProgressBar>, refNode?: Node | string) {
10 super(opts, refNode);
11 }
12 render() {
13 return <div class="progress-bar">
14 <div class="progress-indicator"/>
15 <div class="progress-track"/>
16 </div>;
17 }
18
19 } No newline at end of file
@@ -0,0 +1,7
1 import MainWidget from "./MainWidget";
2 import "@implab/djx/css!dojo/resources/dojo.css"
3 import "@implab/djx/css!dijit/themes/dijit.css"
4 import "@implab/djx/css!dijit/themes/tundra/tundra.css"
5
6 const w = new MainWidget();
7 w.placeAt(document.body); No newline at end of file
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,16
1 {
2 "compilerOptions": {
3 "moduleResolution": "node",
4 "experimentalDecorators": true,
5 "module": "AMD",
6 "jsx": "react",
7 "jsxFactory": "createElement",
8 "strict": true,
9 "types": [
10 "requirejs",
11 "@implab/djx",
12 "@implab/dojo-typings"
13 ],
14 "skipLibCheck": true
15 }
16 } No newline at end of file
@@ -1,78 +1,104
1 plugins {
1 plugins {
2 id "org.implab.gradle-typescript" version "1.3.3"
2 id "org.implab.gradle-typescript" version "1.3.4"
3 id "ivy-publish"
3 id "ivy-publish"
4 }
4 }
5
5
6 configurations {
7 "default" {
8 canBeConsumed = true
9 canBeResolved = false
10 }
11 }
12
6 typescript {
13 typescript {
7 compilerOptions {
14 compilerOptions {
8 lib = ["es5", "dom", "scripthost", "es2015.promise", "es2015.symbol", "es2015.iterable"]
15 lib = ["es5", "dom", "scripthost", "es2015.promise", "es2015.symbol", "es2015.iterable"]
9 // listFiles = true
16 // listFiles = true
10 declaration = true
17 declaration = true
11 strict = true
18 strict = true
12 types = []
19 types = []
13 module = "amd"
20 module = "amd"
14 it.target = "es5"
21 it.target = "es5"
15 experimentalDecorators = true
22 experimentalDecorators = true
16 noUnusedLocals = false
23 noUnusedLocals = false
17 jsx = "react"
24 jsx = "react"
18 jsxFactory = "createElement"
25 jsxFactory = "createElement"
19 moduleResolution = "node"
26 moduleResolution = "node"
20 // dojo-typings are sick
27 // dojo-typings are sick
21 skipLibCheck = true
28 skipLibCheck = true
22 // traceResolution = true
29 // traceResolution = true
23 // baseUrl = "./"
30 // baseUrl = "./"
24 // paths = [ "*": [ "$projectDir/src/typings/*" ] ]
31 // paths = [ "*": [ "$projectDir/src/typings/*" ] ]
25 // baseUrl = "$projectDir/src/typings"
32 // baseUrl = "$projectDir/src/typings"
26 // typeRoots = ["$projectDir/src/typings"]
33 // typeRoots = ["$projectDir/src/typings"]
27 }
34 }
28
35
29 tscCmd = "$projectDir/node_modules/.bin/tsc"
36 tscCmd = "$projectDir/node_modules/.bin/tsc"
30 tsLintCmd = "$projectDir/node_modules/.bin/tslint"
37 tsLintCmd = "$projectDir/node_modules/.bin/tslint"
31 esLintCmd = "$projectDir/node_modules/.bin/eslint"
38 esLintCmd = "$projectDir/node_modules/.bin/eslint"
32 }
39 }
33
40
34 configureTsMain {
41 configureTsMain {
35 sourceFiles {
42 sourceFiles {
36 from sources.main.typings
43 from sources.main.typings
37 }
44 }
38 compilerOptions {
45 compilerOptions {
39 // baseUrl = "$projectDir/src"
46 // baseUrl = "$projectDir/src"
40 /*paths = [
47 /*paths = [
41 "dojo/*" : [ "typings/dojo/*" ],
48 "dojo/*" : [ "typings/dojo/*" ],
42 "dijit/*" : [ "typings/dijit/*" ]
49 "dijit/*" : [ "typings/dijit/*" ]
43 ]*/
50 ]*/
44 types = ["requirejs", "@implab/dojo-typings"]
51 types = ["requirejs", "@implab/dojo-typings"]
45 }
52 }
46 }
53 }
47
54
48 configureTsTest {
55 configureTsTest {
49 compilerOptions {
56 compilerOptions {
50 typeRoots = []
57 typeRoots = []
51 types = ["requirejs", sources.main.output.typingsDir.get().toString() ]
58 types = ["requirejs", sources.main.output.typingsDir.get().toString() ]
52 }
59 }
53 }
60 }
54
61
62 tasks.matching{ it.name =~ /^configureTs/ }.configureEach {
63 compilerOptions {
64 if (symbols != 'none') {
65 sourceMap = true
66 switch(symbols) {
67 case "local":
68 sourceRoot = ( isWindows ? "file:///" : "file://" ) + it.rootDir
69 break;
70 }
71 }
72 }
73 }
74
55 npmPackMeta {
75 npmPackMeta {
56 meta {
76 meta {
57 name = "@$npmScope/$project.name"
77 name = "@$npmScope/$project.name"
58 }
78 }
59 }
79 }
60
80
61 task npmPackTypings(type: Copy) {
81 task npmPackTypings(type: Copy) {
62 dependsOn typings
82 dependsOn typings
63
83
64 npmPackContents.dependsOn it
84 npmPackContents.dependsOn it
65
85
66 from typescript.typingsDir
86 from typescript.typingsDir
67 into npm.packageDir
87 into npm.packageDir
68 }
88 }
69
89
70 task printVersion {
90 task printVersion {
71 doLast {
91 doLast {
72 println "packageName: ${npmPackMeta.metadata.get().name}";
92 println "packageName: ${npmPackMeta.metadata.get().name}";
73 println "version: $version";
93 println "version: $version";
74 println "target: $typescript.compilerOptions.target";
94 println "target: $typescript.compilerOptions.target";
75 println "module: $typescript.compilerOptions.module";
95 println "module: $typescript.compilerOptions.module";
76 println "symbols: $symbols";
96 println "symbols: $symbols";
77 }
97 }
98 }
99
100 artifacts {
101 "default" (npm.packageDir) {
102 builtBy npmAssemblePackage
103 }
78 } No newline at end of file
104 }
@@ -1,8 +1,7
1 group=org.implab.implabjs
1 group=org.implab.implabjs
2 version=
2 version=
3 author=Implab team
3 author=Implab team
4 description=Create HyperText with Typescript, integrate Dojo1 widgets in your .tsx scripts.
4 description=Create HyperText with Typescript, integrate Dojo1 widgets in your .tsx scripts.
5 license=BSD-2-Clause
5 license=BSD-2-Clause
6 repository=http://hg.code.sf.net/p/implabjs/djx
6 repository=http://hg.code.sf.net/p/implabjs/djx
7 npmScope=implab
7 npmScope=implab
8 symbols=pack No newline at end of file
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,34 +1,36
1 {
1 {
2 "name": "@implab/djx",
2 "name": "@implab/djx",
3 "version": "0.0.1-dev",
3 "version": "0.0.1-dev",
4 "description": "Supports using dojo version 1 with typescript and .tsx files",
4 "description": "Supports using dojo version 1 with typescript and .tsx files",
5 "keywords": [
5 "keywords": [
6 "dojo",
6 "dojo",
7 "tsx",
7 "tsx",
8 "typescript",
8 "typescript",
9 "widgets"
9 "widgets"
10 ],
10 ],
11 "author": "Implab team",
11 "author": "Implab team",
12 "license": "BSD-2-Clause",
12 "license": "BSD-2-Clause",
13 "repository": "https://code.implab.org/implab/implabjs-djx",
13 "repository": "https://code.implab.org/implab/implabjs-djx",
14 "publishConfig": {
14 "publishConfig": {
15 "access": "public"
15 "access": "public"
16 },
16 },
17 "peerDependencies": {
17 "peerDependencies": {
18 "@implab/core-amd": "^1.4.0",
18 "@implab/core-amd": "^1.4.0",
19 "dojo": "^1.10.0"
19 "dojo": "^1.10.0"
20 },
20 },
21 "devDependencies": {
21 "devDependencies": {
22 "@implab/core-amd": "^1.4.0",
22 "@implab/core-amd": "^1.4.0",
23 "@types/chai": "4.1.3",
23 "@types/chai": "4.1.3",
24 "@types/requirejs": "2.1.31",
24 "@types/requirejs": "2.1.31",
25 "@types/yaml": "1.2.0",
25 "@types/yaml": "1.2.0",
26 "@types/tap": "15.0.7",
26 "dojo": "1.16.0",
27 "dojo": "1.16.0",
27 "@implab/dojo-typings": "1.0.0",
28 "@implab/dojo-typings": "1.0.0",
28 "eslint": "6.8.0",
29 "eslint": "6.8.0",
29 "requirejs": "2.3.6",
30 "requirejs": "2.3.6",
30 "tslint": "^6.1.3",
31 "tslint": "^6.1.3",
31 "typescript": "4.2.4",
32 "typescript": "4.8.2",
32 "yaml": "~1.7.2"
33 "yaml": "~1.7.2",
34 "tap": "16.3.0"
33 }
35 }
34 }
36 }
@@ -1,119 +1,176
1 import { Constructor, IDestroyable, IRemovable } from "@implab/core-amd/interfaces";
1 import { Constructor } from "@implab/core-amd/interfaces";
2 import { HtmlRendition } from "./tsx/HtmlRendition";
2 import { HtmlRendition } from "./tsx/HtmlRendition";
3 import { WidgetRendition } from "./tsx/WidgetRendition";
3 import { WidgetRendition } from "./tsx/WidgetRendition";
4 import { destroy, isWidgetConstructor, Rendition } from "./tsx/traits";
4 import { isElementNode, isWidget, isWidgetConstructor, Rendition } from "./tsx/traits";
5 import { FunctionRendition } from "./tsx/FunctionRendition";
5 import { FunctionRendition } from "./tsx/FunctionRendition";
6 import Stateful = require("dojo/Stateful");
6 import Stateful = require("dojo/Stateful");
7 import _WidgetBase = require("dijit/_WidgetBase");
7 import _WidgetBase = require("dijit/_WidgetBase");
8 import { DjxWidgetBase } from "./tsx/DjxWidgetBase";
8 import { DjxWidgetBase } from "./tsx/DjxWidgetBase";
9 import { WatchRendition } from "./tsx/WatchRendition";
10 import { Observable, observe, Subscribable } from "./observable";
11 import djAttr = require("dojo/dom-attr");
12 import djClass = require("dojo/dom-class");
9
13
10 export function createElement<T extends Constructor | string | ((props: any) => Element)>(elementType: T, ...args: any[]): Rendition {
14 export function createElement<T extends Constructor | string | ((props: any) => Element)>(elementType: T, ...args: any[]): Rendition {
11 if (typeof elementType === "string") {
15 if (typeof elementType === "string") {
12 const ctx = new HtmlRendition(elementType);
16 const ctx = new HtmlRendition(elementType);
13 if (args)
17 if (args)
14 args.forEach(x => ctx.visitNext(x));
18 args.forEach(x => ctx.visitNext(x));
15
19
16 return ctx;
20 return ctx;
17 } else if (isWidgetConstructor(elementType)) {
21 } else if (isWidgetConstructor(elementType)) {
18 const ctx = new WidgetRendition(elementType);
22 const ctx = new WidgetRendition(elementType);
19 if (args)
23 if (args)
20 args.forEach(x => ctx.visitNext(x));
24 args.forEach(x => ctx.visitNext(x));
21
25
22 return ctx;
26 return ctx;
23 } else if (typeof elementType === "function") {
27 } else if (typeof elementType === "function") {
24 const ctx = new FunctionRendition(elementType as (props: any) => Element);
28 const ctx = new FunctionRendition(elementType as (props: any) => Element);
25 if (args)
29 if (args)
26 args.forEach(x => ctx.visitNext(x));
30 args.forEach(x => ctx.visitNext(x));
27
31
28 return ctx;
32 return ctx;
29 } else {
33 } else {
30 throw new Error(`The element type '${elementType}' is unsupported`);
34 throw new Error(`The element type '${elementType}' is unsupported`);
31 }
35 }
32 }
36 }
33
37
34 export interface EventDetails<T = any> {
38 export interface EventDetails<T = any> {
35 detail: T;
39 detail: T;
36 }
40 }
37
41
38 export interface EventSelector {
42 export interface EventSelector {
39 selectorTarget: HTMLElement;
43 selectorTarget: HTMLElement;
40 target: HTMLElement;
44 target: HTMLElement;
41 }
45 }
42
46
43 export type DojoMouseEvent<T = any> = MouseEvent & EventSelector & EventDetails<T>;
47 export type DojoMouseEvent<T = any> = MouseEvent & EventSelector & EventDetails<T>;
44
48
45 type StatefulProps<T> = T extends Stateful<infer A> ? A : never;
49 type StatefulProps<T> = T extends Stateful<infer A> ? A :
50 T extends _WidgetBase ? T : never;
46
51
47 type CleanFn = (instance: IRemovable | IDestroyable) => void;
48
52
49 /**
53 /**
50 * Observers the property and calls render callback each change.
54 * Observers the property and calls render callback each change.
51 *
55 *
52 * @param target The target object which property will be observed.
56 * @param target The target object which property will be observed.
53 * @param prop The name of the property.
57 * @param prop The name of the property.
54 * @param render The callback which will be called every time the value is changed
58 * @param render The callback which will be called every time the value is changed
55 * @param cleanupOrOwner The object with method `own` or an callback to register lifecycle for the observer.
56 * @returns Rendition which is created instantly
59 * @returns Rendition which is created instantly
57 */
60 */
58 export function watch<W extends _WidgetBase, K extends keyof W>(
61 export function watch<W extends _WidgetBase, K extends keyof W>(
59 target: W,
62 target: W,
60 prop: K,
63 prop: K,
61 render: (model: W[K]) => any,
64 render: (model: W[K]) => any
62 cleanupOrOwner?: { own: CleanFn } | CleanFn
63 ): Rendition;
65 ): Rendition;
64 /**
66 /**
65 * Observers the property and calls render callback each change.
67 * Observers the property and calls render callback each change.
66 *
68 *
67 * @param target The target object which property will be observed.
69 * @param target The target object which property will be observed.
68 * @param prop The name of the property.
70 * @param prop The name of the property.
69 * @param render The callback which will be called every time the value is changed
71 * @param render The callback which will be called every time the value is changed
70 * @param cleanupOrOwner The object with method `own` or an callback to register lifecycle for the observer.
71 * @returns Rendition which is created instantly
72 * @returns Rendition which is created instantly
72 */
73 */
73 export function watch<T extends Stateful, K extends keyof StatefulProps<T>>(
74 export function watch<T extends Stateful, K extends keyof StatefulProps<T>>(
74 target: T,
75 target: T,
75 prop: K,
76 prop: K,
76 render: (model: StatefulProps<T>[K]) => any,
77 render: (model: StatefulProps<T>[K]) => any
77 cleanupOrOwner?: { own: CleanFn } | CleanFn
78 ): Rendition;
78 ): Rendition;
79 export function watch<T extends Stateful, K extends keyof StatefulProps<T> & string>(
79 export function watch<V>(subj: Subscribable<V>, render: (model: V) => unknown): Rendition;
80 target: T,
80 export function watch(
81 prop: K,
81 ...args: [Stateful, string, (model: unknown) => unknown] |
82 render: (model: StatefulProps<T>[K]) => any,
82 [Subscribable<unknown>, (model: unknown) => unknown]
83 cleanupOrOwner: { own: CleanFn } | CleanFn = () => { }
84 ) {
83 ) {
85 let rendition = new FunctionRendition(() => render(target.get(prop)));
84 if (args.length === 3) {
86 const _own = cleanupOrOwner instanceof Function ? cleanupOrOwner : (x: IRemovable) => cleanupOrOwner.own(x);
85 const [target, prop, render] = args;
87 _own(target.watch(prop, (_name, oldValue, newValue) => {
86 return new WatchRendition(
88 if (oldValue !== newValue) {
87 render,
89 const newRendition = new FunctionRendition(() => render(newValue));
88 observe(({next}) => {
90 newRendition.placeAt(rendition.getDomNode(), "replace");
89 const h = target.watch<any>(
91 destroy(rendition.getDomNode());
90 prop,
92 rendition = newRendition;
91 (_prop, oldValue, newValue) => oldValue !== newValue && next(newValue)
92 );
93 next(target.get(prop));
94 return () => h.remove();
95 })
96 );
97 } else {
98 const [subj, render] = args;
99 return new WatchRendition(render, subj);
100 }
93 }
101 }
94 }));
102
95 return rendition;
103 export const prop: {
104 <T extends Stateful, K extends string & keyof StatefulProps<T>>(target: T, name: K): Observable<StatefulProps<T>[K]>;
105 <T extends _WidgetBase, K extends keyof T>(target: T, name: K): Observable<T[K]>;
106 } = (target: Stateful, name: string) => {
107 return observe(({next}) => {
108 const h = target.watch(
109 name,
110 (_prop, oldValue, newValue) => oldValue !== newValue && next(newValue)
111 );
112 next(target.get(name));
113 return () => h.remove();
114 })
115 };
116
117 export const attach = <W extends DjxWidgetBase, K extends keyof W>(target: W, name: K) => (v: W[K]) => target.set(name, v);
118
119 export const bind = <K extends string, T>(attr: K, subj: Subscribable<T>) => {
120 let h = { unsubscribe() { } };
121
122 return <E extends (HTMLElement & { [p in K]: T }) | { set(name: K, value: T): void; }>(el: E | undefined) => {
123 if (el) {
124 if (isElementNode(el)) {
125 h = subj.subscribe({
126 next: value => djAttr.set(el, attr, value)
127 });
128 } else {
129 h = subj.subscribe({
130 next: value => el.set(attr, value)
131 });
96 }
132 }
133 } else {
134 h.unsubscribe();
135 }
136 }
137 };
138
139 export const toggleClass = (className: string, subj: Subscribable<boolean>) => {
140 let h = { unsubscribe() { } };
141 return (elOrWidget: HTMLElement | _WidgetBase | undefined) => {
142 const el = isWidget(elOrWidget) ? elOrWidget.domNode : elOrWidget;
143 if (el) {
144 h = subj.subscribe({
145 next: v => djClass.toggle(el, className, v)
146 });
147 } else {
148 h.unsubscribe();
149 }
150 }
151 }
152
153 export const all = <T, A extends JSX.Ref<T>[]>(...cbs: A): JSX.Ref<T> => (arg: T | undefined) => cbs.forEach(cb => cb(arg));
97
154
98 /** Decorates the method which will be registered as the handle for the specified event.
155 /** Decorates the method which will be registered as the handle for the specified event.
99 * This decorator can be applied to DjxWidgetBase subclass methods.
156 * This decorator can be applied to DjxWidgetBase subclass methods.
100 *
157 *
101 * ```
158 * ```
102 * @on("click")
159 * @on("click")
103 * _onClick(eventObj: MouseEvent) {
160 * _onClick(eventObj: MouseEvent) {
104 * // ...
161 * // ...
105 * }
162 * }
106 * ```
163 * ```
107 */
164 */
108 export const on = <E extends string>(...eventNames: E[]) =>
165 export const on = <E extends string>(...eventNames: E[]) =>
109 <K extends string,
166 <K extends string,
110 T extends DjxWidgetBase<any, { [p in E]: EV }>,
167 T extends DjxWidgetBase<any, { [p in E]: EV }>,
111 EV extends Event
168 EV extends Event
112 >(
169 >(
113 target: T,
170 target: T,
114 key: K,
171 key: K,
115 _descriptor: TypedPropertyDescriptor<(eventObj: EV) => void> | TypedPropertyDescriptor<() => void>
172 _descriptor: TypedPropertyDescriptor<(eventObj: EV) => void> | TypedPropertyDescriptor<() => void>
116 ): any => {
173 ): any => {
117 const handlers = eventNames.map(eventName => ({ eventName, handlerMethod: key }));
174 const handlers = eventNames.map(eventName => ({ eventName, handlerMethod: key }));
118 target._eventHandlers = target._eventHandlers ? target._eventHandlers.concat(handlers) : handlers;
175 target._eventHandlers = target._eventHandlers ? target._eventHandlers.concat(handlers) : handlers;
119 };
176 };
@@ -1,114 +1,128
1 import { djbase, djclass } from "../declare";
1 import { djbase, djclass } from "../declare";
2 import _WidgetBase = require("dijit/_WidgetBase");
2 import _WidgetBase = require("dijit/_WidgetBase");
3 import _AttachMixin = require("dijit/_AttachMixin");
3 import _AttachMixin = require("dijit/_AttachMixin");
4 import { Rendition, isNode } from "./traits";
4 import { Rendition, isNode, isElementNode } from "./traits";
5 import registry = require("dijit/registry");
5 import registry = require("dijit/registry");
6 import on = require("dojo/on");
6 import on = require("dojo/on");
7 import { Scope } from "./Scope";
8 import { render } from "./render";
7
9
8 // type Handle = dojo.Handle;
10 // type Handle = dojo.Handle;
9
11
10 export interface EventArgs {
12 export interface EventArgs {
11 bubbles?: boolean;
13 bubbles?: boolean;
12
14
13 cancelable?: boolean;
15 cancelable?: boolean;
14
16
15 composed?: boolean;
17 composed?: boolean;
16 }
18 }
17
19
18 export interface DjxWidgetBase<Attrs = {}, Events extends { [name in keyof Events]: Event } = {}> extends
20 export interface DjxWidgetBase<Attrs = {}, Events extends { [name in keyof Events]: Event } = {}> extends
19 _WidgetBase<Events> {
21 _WidgetBase<Events> {
20
22
21 /** This property is declared only for type inference to work, it is never assigned
23 /** This property is declared only for type inference to work, it is never assigned
22 * and should not be used.
24 * and should not be used.
23 */
25 */
24 readonly _eventMap: Events & GlobalEventHandlersEventMap;
26 readonly _eventMap: Events & GlobalEventHandlersEventMap;
25
27
26 /** The list of pairs of event and method names. When the widget is created all methods from
28 /** The list of pairs of event and method names. When the widget is created all methods from
27 * this list will be connected to corresponding events.
29 * this list will be connected to corresponding events.
28 *
30 *
29 * This property is maintained in the prototype
31 * This property is maintained in the prototype
30 */
32 */
31 _eventHandlers: Array<{
33 _eventHandlers: Array<{
32 eventName: string,
34 eventName: string,
33 handlerMethod: keyof any;
35 handlerMethod: keyof any;
34 }>;
36 }>;
35 }
37 }
36
38
37 type _super = {
39 type _super = {
38 startup(): void;
40 startup(): void;
41
42 destroy(preserveDom?: boolean): void;
39 };
43 };
40
44
41 @djclass
45 @djclass
42 export abstract class DjxWidgetBase<Attrs = {}, Events = {}> extends djbase<_super, _AttachMixin>(_WidgetBase, _AttachMixin) {
46 export abstract class DjxWidgetBase<Attrs = {}, Events = {}> extends djbase<_super, _AttachMixin>(_WidgetBase, _AttachMixin) {
47 private readonly _scope = new Scope();
43
48
44 buildRendering() {
49 buildRendering() {
45 this.domNode = this.render().getDomNode();
50 const node = render(this.render(), this._scope);
51 if (!isElementNode(node))
52 throw new Error("The render method must return a single DOM element");
53 this.domNode = node as HTMLElement;
54
46 super.buildRendering();
55 super.buildRendering();
47
56
48 // now we should get assigned data-dojo-attach-points
57 // now we should get assigned data-dojo-attach-points
49 // place the contents of the original srcNode to the containerNode
58 // place the contents of the original srcNode to the containerNode
50 const src = this.srcNodeRef;
59 const src = this.srcNodeRef;
51 const dest = this.containerNode;
60 const dest = this.containerNode;
52
61
53 // the donNode is constructed now we need to connect event handlers
62 // the donNode is constructed now we need to connect event handlers
54 this._connectEventHandlers();
63 this._connectEventHandlers();
55
64
56 if (src && dest) {
65 if (src && dest) {
57 while (src.firstChild)
66 while (src.firstChild)
58 dest.appendChild(src.firstChild);
67 dest.appendChild(src.firstChild);
59 }
68 }
60 }
69 }
61
70
62 abstract render(): Rendition<HTMLElement>;
71 abstract render(): Rendition<HTMLElement>;
63
72
64 private _connectEventHandlers() {
73 private _connectEventHandlers() {
65 if (this._eventHandlers)
74 if (this._eventHandlers)
66 this._eventHandlers.forEach(({ eventName, handlerMethod }) => {
75 this._eventHandlers.forEach(({ eventName, handlerMethod }) => {
67 const handler = this[handlerMethod as keyof this];
76 const handler = this[handlerMethod as keyof this];
68 if (typeof handler === "function")
77 if (typeof handler === "function")
69 on(this.domNode, eventName, handler.bind(this));
78 on(this.domNode, eventName, handler.bind(this));
70 });
79 });
71 }
80 }
72
81
73 _processTemplateNode<T extends (Element | Node | _WidgetBase)>(
82 _processTemplateNode<T extends (Element | Node | _WidgetBase)>(
74 baseNode: T,
83 baseNode: T,
75 getAttrFunc: (baseNode: T, attr: string) => any,
84 getAttrFunc: (baseNode: T, attr: string) => any,
76 // tslint:disable-next-line: ban-types
85 // tslint:disable-next-line: ban-types
77 attachFunc: (node: T, type: string, func?: Function) => dojo.Handle
86 attachFunc: (node: T, type: string, func?: Function) => dojo.Handle
78 ): boolean {
87 ): boolean {
79 if (isNode(baseNode)) {
88 if (isNode(baseNode)) {
80 const w = registry.byNode(baseNode);
89 const w = registry.byNode(baseNode);
81 if (w) {
90 if (w) {
82 // from dijit/_WidgetsInTemplateMixin
91 // from dijit/_WidgetsInTemplateMixin
83 this._processTemplateNode(w,
92 this._processTemplateNode(w,
84 (n, p) => n.get(p as any), // callback to get a property of a widget
93 (n, p) => n.get(p as any), // callback to get a property of a widget
85 (widget, type, callback) => {
94 (widget, type, callback) => {
86 if (!callback)
95 if (!callback)
87 throw new Error("The callback must be specified");
96 throw new Error("The callback must be specified");
88
97
89 // callback to do data-dojo-attach-event to a widget
98 // callback to do data-dojo-attach-event to a widget
90 if (type in widget) {
99 if (type in widget) {
91 // back-compat, remove for 2.0
100 // back-compat, remove for 2.0
92 return widget.connect(widget, type, callback as EventListener);
101 return widget.connect(widget, type, callback as EventListener);
93 } else {
102 } else {
94 // 1.x may never hit this branch, but it's the default for 2.0
103 // 1.x may never hit this branch, but it's the default for 2.0
95 return widget.on(type, callback);
104 return widget.on(type, callback);
96 }
105 }
97
106
98 });
107 });
99 // don't process widgets internals
108 // don't process widgets internals
100 return false;
109 return false;
101 }
110 }
102 }
111 }
103 return super._processTemplateNode(baseNode, getAttrFunc, attachFunc);
112 return super._processTemplateNode(baseNode, getAttrFunc, attachFunc);
104 }
113 }
105
114
106 /** Starts current widget and all its supporting widgets (placed outside
115 /** Starts current widget and all its supporting widgets (placed outside
107 * `containerNode`) and child widgets (placed inside `containerNode`)
116 * `containerNode`) and child widgets (placed inside `containerNode`)
108 */
117 */
109 startup() {
118 startup() {
110 // startup supporting widgets
119 // startup supporting widgets
111 registry.findWidgets(this.domNode, this.containerNode).forEach(w => w.startup());
120 registry.findWidgets(this.domNode, this.containerNode).forEach(w => w.startup());
112 super.startup();
121 super.startup();
113 }
122 }
123
124 destroy(preserveDom?: boolean) {
125 this._scope.destroy();
126 super.destroy(preserveDom);
114 }
127 }
128 }
@@ -1,30 +1,30
1 import { argumentNotNull } from "@implab/core-amd/safe";
1 import { argumentNotNull } from "@implab/core-amd/safe";
2 import { getItemDom } from "./render";
2 import { RenditionBase } from "./RenditionBase";
3 import { RenditionBase } from "./RenditionBase";
3
4
4 export class FunctionRendition extends RenditionBase<Node> {
5 export class FunctionRendition extends RenditionBase<Node> {
5 private _component: (...args: any[]) => any;
6 private _component: (...args: any[]) => any;
6
7
7 private _node: Node | undefined;
8 private _node: Node | undefined;
8
9
9 constructor(component: (...args: any[]) => any) {
10 constructor(component: (...args: any[]) => any) {
10 super();
11 super();
11 argumentNotNull(component, "component");
12 argumentNotNull(component, "component");
12
13
13 this._component = component;
14 this._component = component;
14 }
15 }
15
16
16 protected _create(attrs: object, children: any[]) {
17 protected _create(attrs: object, children: any[]) {
17 const _attrs: any = attrs || {};
18 const _attrs: any = attrs || {};
18 const _children = children.map(x => this.getItemDom(x));
19 const _children = children.map(x => getItemDom(x));
19 this._node = this.getItemDom(
20 this._node = getItemDom(
20 this._component.call(null, { ..._attrs, children: _children })
21 this._component.call(null, { ..._attrs, children: _children }));
21 );
22 }
22 }
23
23
24 protected _getDomNode() {
24 protected _getDomNode() {
25 if (!this._node)
25 if (!this._node)
26 throw new Error("The instance of the widget isn't created");
26 throw new Error("The instance of the widget isn't created");
27 return this._node;
27 return this._node;
28 }
28 }
29
29
30 }
30 }
@@ -1,36 +1,50
1 import dom = require("dojo/dom-construct");
1 import djDom = require("dojo/dom-construct");
2 import djAttr = require("dojo/dom-attr");
2 import { argumentNotEmptyString } from "@implab/core-amd/safe";
3 import { argumentNotEmptyString } from "@implab/core-amd/safe";
3 import { RenditionBase } from "./RenditionBase";
4 import { RenditionBase } from "./RenditionBase";
5 import { placeAt } from "./traits";
6 import { getItemDom, refHook } from "./render";
4
7
5 export class HtmlRendition extends RenditionBase<HTMLElement> {
8 export class HtmlRendition extends RenditionBase<Element> {
6 elementType: string;
9 elementType: string;
7
10
8 _element: HTMLElement | undefined;
11 _element: Element | undefined;
9
12
10 constructor(elementType: string) {
13 constructor(elementType: string) {
11 argumentNotEmptyString(elementType, "elementType");
14 argumentNotEmptyString(elementType, "elementType");
12 super();
15 super();
13
16
14 this.elementType = elementType;
17 this.elementType = elementType;
15 }
18 }
16
19
17 _addChild(child: any): void {
20 _addChild(child: unknown): void {
18 if (!this._element)
21 if (!this._element)
19 throw new Error("The HTML element isn't created");
22 throw new Error("The HTML element isn't created");
20 dom.place(this.getItemDom(child), this._element);
23 placeAt(getItemDom(child), this._element);
21 }
24 }
22
25
23 _create(attrs: object, children: any[]) {
26 _create({ xmlns, ref, ...attrs }: { xmlns?: string, ref?: JSX.Ref<Element> }, children: unknown[]) {
24 this._element = dom.create(this.elementType, attrs);
27
28 if (xmlns) {
29 this._element = document.createElementNS(xmlns, this.elementType);
30 djAttr.set(this._element, attrs);
31 } else {
32 this._element = djDom.create(this.elementType, attrs);
33 }
25
34
26 children.forEach(v => this._addChild(v));
35 children.forEach(v => this._addChild(v));
36
37 const element = this._element;
38
39 if (ref)
40 refHook(element, ref);
27 }
41 }
28
42
29 _getDomNode() {
43 _getDomNode() {
30 if (!this._element)
44 if (!this._element)
31 throw new Error("The HTML element isn't created");
45 throw new Error("The HTML element isn't created");
32
46
33 return this._element;
47 return this._element;
34 }
48 }
35
49
36 }
50 }
@@ -1,117 +1,70
1 import { isNull, mixin } from "@implab/core-amd/safe";
1 import { isPlainObject, DojoNodePosition, Rendition, isDocumentFragmentNode, placeAt, collectNodes, isMounted, startupWidgets } from "./traits";
2 import { isPlainObject, isNode, isRendition, DojoNodePosition, Rendition, isInPage, isWidget, isDocumentFragmentNode, startupWidgets } from "./traits";
3
4 import dom = require("dojo/dom-construct");
5 import registry = require("dijit/registry");
6
7
2
8 export abstract class RenditionBase<TNode extends Node> implements Rendition<TNode> {
3 export abstract class RenditionBase<TNode extends Node> implements Rendition<TNode> {
9 private _attrs = {};
4 private _attrs = {};
10
5
11 private _children = new Array();
6 private _children = new Array();
12
7
13 private _created: boolean = false;
8 private _created: boolean = false;
14
9
15 visitNext(v: any) {
10 visitNext(v: any) {
16 if (this._created)
11 if (this._created)
17 throw new Error("The Element is already created");
12 throw new Error("The Element is already created");
18
13
19 if (isNull(v) || typeof v === "boolean")
14 if (v === null || v === undefined || typeof v === "boolean")
20 // skip null, undefined, booleans ( this will work: {value && <span>{value}</span>} )
15 // skip null, undefined, booleans ( this will work: {value && <span>{value}</span>} )
21 return;
16 return;
22
17
23 if (isPlainObject(v)) {
18 if (isPlainObject(v)) {
24 mixin(this._attrs, v);
19 this._attrs = {... this._attrs, ...v};
25 } else if (v instanceof Array) {
20 } else if (v instanceof Array) {
26 v.forEach(x => this.visitNext(x));
21 v.forEach(x => this.visitNext(x));
27 } else {
22 } else {
28 this._children.push(v);
23 this._children.push(v);
29 }
24 }
30 }
25 }
31
26
32 /** Renders DOM element for different types of the argument. */
33 protected getItemDom(v: any) {
34 const tv = typeof v;
35
36 if (tv === "string" || tv === "number" || v instanceof RegExp || v instanceof Date) {
37 // primitive types converted to the text nodes
38 return document.createTextNode(v.toString());
39 } else if (isNode(v)) {
40 // nodes are kept as is
41 return v;
42 } else if (isRendition(v)) {
43 // renditions are instantiated
44 return v.getDomNode();
45 } else if (isWidget(v)) {
46 // widgets are converted to it's markup
47 return v.domNode;
48 } else if (tv === "boolean" || v === null || v === undefined) {
49 // null | undefined | boolean are removed, converted to comments
50 return document.createComment(`[${tv} ${String(v)}]`);
51 } else {
52 // bug: explicit error otherwise
53 throw new Error("Invalid parameter: " + v);
54 }
55 }
56
57 ensureCreated() {
27 ensureCreated() {
58 if (!this._created) {
28 if (!this._created) {
59 this._create(this._attrs, this._children);
29 this._create(this._attrs, this._children);
60 this._children = [];
30 this._children = [];
61 this._attrs = {};
31 this._attrs = {};
62 this._created = true;
32 this._created = true;
63 }
33 }
64 }
34 }
65
35
66 /** Is rendition was instantiated to the DOM node */
36 /** Is rendition was instantiated to the DOM node */
67 isCreated() {
37 isCreated() {
68 return this._created;
38 return this._created;
69 }
39 }
70
40
71 /** Creates DOM node if not created. No additional actions are taken. */
41 /** Creates DOM node if not created. No additional actions are taken. */
72 getDomNode() {
42 getDomNode() {
73 this.ensureCreated();
43 this.ensureCreated();
74 return this._getDomNode();
44 return this._getDomNode();
75 }
45 }
76
46
77 /** Creates DOM node if not created, places it to the specified position
47 /** Creates DOM node if not created, places it to the specified position
78 * and calls startup() method for all widgets contained by this node.
48 * and calls startup() method for all widgets contained by this node.
79 *
49 *
80 * @param {string | Node} refNode The reference node where the created
50 * @param {string | Node} refNode The reference node where the created
81 * DOM should be placed.
51 * DOM should be placed.
82 * @param {DojoNodePosition} position Optional parameter, specifies the
52 * @param {DojoNodePosition} position Optional parameter, specifies the
83 * position relative to refNode. Default is "last" (i.e. last child).
53 * position relative to refNode. Default is "last" (i.e. last child).
84 */
54 */
85 placeAt(refNode: string | Node, position?: DojoNodePosition) {
55 placeAt(refNode: string | Node, position: DojoNodePosition = "last") {
86 const domNode = this.getDomNode();
56 const domNode = this.getDomNode();
87
57
88 const collect = (collection: HTMLCollection) => {
58 const startupPending = isDocumentFragmentNode(domNode) ? collectNodes(domNode.childNodes) : [domNode];
89 const items = [];
90 for (let i = 0, n = collection.length; i < n; i++) {
91 items.push(collection[i]);
92 }
93 return items;
94 };
95
59
96 const startup = (node: Node) => {
60 placeAt(domNode, refNode, position);
97 if (node.parentNode) {
98 const parentWidget = registry.getEnclosingWidget(node.parentNode);
99 if (parentWidget && parentWidget._started)
100 return startupWidgets(node);
101 }
102 if (isInPage(node))
103 startupWidgets(node);
104 };
105
61
106 const startupPending = isDocumentFragmentNode(domNode) ? collect(domNode.children) : [domNode];
62 if (isMounted(startupPending[0]))
107
63 startupPending.forEach(n => startupWidgets(n));
108 dom.place(domNode, refNode, position);
109
110 startupPending.forEach(startup);
111
64
112 }
65 }
113
66
114 protected abstract _create(attrs: object, children: any[]): void;
67 protected abstract _create(attrs: object, children: unknown[]): void;
115
68
116 protected abstract _getDomNode(): TNode;
69 protected abstract _getDomNode(): TNode;
117 }
70 }
@@ -1,40 +1,43
1 import { IDestroyable, IRemovable } from "@implab/core-amd/interfaces";
1 import { IDestroyable, IRemovable } from "@implab/core-amd/interfaces";
2 import { isDestroyable, isRemovable } from "@implab/core-amd/safe";
2 import { isDestroyable, isRemovable } from "@implab/core-amd/safe";
3 import { isUnsubsribable, Unsubscribable } from "../observable";
3
4
4 export interface IScope {
5 export interface IScope {
5 own(target: (() => void) | IDestroyable | IRemovable): void;
6 own(target: (() => void) | IDestroyable | IRemovable | Unsubscribable): void;
6 }
7 }
7
8
8 export class Scope implements IDestroyable, IScope {
9 export class Scope implements IDestroyable, IScope {
9 private readonly _cleanup: (() => void)[] = [];
10 private readonly _cleanup: (() => void)[] = [];
10
11
11 static readonly dummy: IScope = { own() { } };
12 static readonly dummy: IScope = { own() { } };
12
13
13 own(target: (() => void) | IDestroyable | IRemovable) {
14 own(target: (() => void) | IDestroyable | IRemovable | Unsubscribable) {
14 if (target instanceof Function) {
15 if (target instanceof Function) {
15 this._cleanup.push(target);
16 this._cleanup.push(target);
16 } else if (isDestroyable(target)) {
17 } else if (isDestroyable(target)) {
17 this._cleanup.push(() => target.destroy());
18 this._cleanup.push(() => target.destroy());
18 } else if (isRemovable(target)) {
19 } else if (isRemovable(target)) {
19 this._cleanup.push(() => target.remove());
20 this._cleanup.push(() => target.remove());
21 } else if (isUnsubsribable(target)) {
22 this._cleanup.push(() => target.unsubscribe());
20 }
23 }
21 }
24 }
22
25
23 clean() {
26 clean() {
24 const guard = (cb: () => void) => {
27 const guard = (cb: () => void) => {
25 try {
28 try {
26 cb();
29 cb();
27 } catch {
30 } catch {
28 // guard
31 // guard
29 }
32 }
30 }
33 }
31
34
32 this._cleanup.forEach(guard);
35 this._cleanup.forEach(guard);
33 this._cleanup.length = 0;
36 this._cleanup.length = 0;
34 }
37 }
35
38
36 destroy() {
39 destroy() {
37 this.clean();
40 this.clean();
38 }
41 }
39
42
40 } No newline at end of file
43 }
@@ -1,58 +1,97
1 import { id as mid } from "module";
1 import { id as mid } from "module";
2 import { TraceSource } from "@implab/core-amd/log/TraceSource";
2 import { TraceSource } from "@implab/core-amd/log/TraceSource";
3 import { argumentNotNull } from "@implab/core-amd/safe";
3 import { argumentNotNull } from "@implab/core-amd/safe";
4 import { place } from "dojo/dom-construct";
4 import { getScope, render } from "./render";
5 import { getScope, render } from "./Renderer";
6 import { RenditionBase } from "./RenditionBase";
5 import { RenditionBase } from "./RenditionBase";
7 import { Scope } from "./Scope";
6 import { Scope } from "./Scope";
8 import { locateNode } from "./traits";
7 import { Subscribable } from "../observable";
8 import { Cancellation } from "@implab/core-amd/Cancellation";
9 import { collectNodes, destroy, isDocumentFragmentNode, isMounted, placeAt, startupWidgets } from "./traits";
9
10
10 const trace = TraceSource.get(mid);
11 const trace = TraceSource.get(mid);
11
12
12 export class WatchRendition<T> extends RenditionBase<Node> {
13 export class WatchRendition<T> extends RenditionBase<Node> {
13 private readonly _factory: (arg: T) => any;
14 private readonly _component: (arg: T) => unknown;
14
15
15 private _node: Node;
16 private readonly _node: Node;
16
17
17 private readonly _scope = new Scope();
18 private readonly _scope = new Scope();
18
19
19 constructor(component: (arg: T) => any, subject: any) {
20 private readonly _subject: Subscribable<T>;
21
22 private _renderJob?: { value: T };
23
24 private _ct = Cancellation.none;
25
26 constructor(component: (arg: T) => unknown, subject: Subscribable<T>) {
20 super();
27 super();
21 argumentNotNull(component, "component");
28 argumentNotNull(component, "component");
22
29
23 this._factory = component;
30 this._component = component;
31
32 this._subject = subject;
33
34 this._node = document.createComment("[Watch]");
35 }
24
36
25 this._node = document.createComment("WatchRendition placeholder");
37 protected _create() {
38 const scope = getScope();
39 scope.own(() => {
40 this._scope.destroy();
41 destroy(this._node);
42 });
43 scope.own(this._subject.subscribe({ next: this._onValue }));
44 this._ct = new Cancellation(cancel => scope.own(cancel));
26 }
45 }
27
46
28 protected _create(attrs: object, children: any[]) {
47 private _onValue = (value: T) => {
29 const _attrs: any = attrs || {};
48 if (!this._renderJob) {
30 const _children = children.map(x => this.getItemDom(x));
49 // schedule a new job
31 this._node = this.getItemDom(
50 this._renderJob = { value };
32 this._factory.call(null, { ..._attrs, children: _children })
51 this._render().catch(e => trace.error(e));
52 } else {
53 // update existing job
54 this._renderJob = { value };
55 }
56 }
57
58 private async _render() {
59 // fork
60 await Promise.resolve();
61 // don't render destroyed rendition
62 if (this._ct.isRequested())
63 return;
64
65 // remove all previous content
66 this._scope.clean();
67
68 // render the new node
69 const node = render(
70 this._renderJob ? this._component(this._renderJob.value) : undefined,
71 this._scope
33 );
72 );
34
73
35 const scope = getScope();
74 // get actual content
36 scope.own(this._scope);
75 const pending = isDocumentFragmentNode(node) ?
76 collectNodes(node.childNodes) :
77 [node];
37
78
38 // если отрендерили текст? или DocumentFragment
79 placeAt(node, this._node, "after");
39 }
40
80
41 private async _render(value: T) {
81 if (isMounted(this._node))
42 const [refNode, position] = locateNode(this._node);
82 pending.forEach(n => startupWidgets(n));
43 this._scope.clean();
44
83
45 this._node = await render(() => this._factory(value), this._scope);
84 if (pending.length)
85 this._scope.own(() => pending.forEach(destroy));
46
86
47 if (refNode)
87 this._renderJob = undefined;
48 place(this._node, refNode, position);
49 }
88 }
50
89
51 protected _getDomNode() {
90 protected _getDomNode() {
52 if (!this._node)
91 if (!this._node)
53 throw new Error("The instance of the widget isn't created");
92 throw new Error("The instance of the widget isn't created");
54 return this._node;
93 return this._node;
55 }
94 }
56
95
57
96
58 }
97 }
@@ -1,128 +1,130
1 import dom = require("dojo/dom-construct");
2 import { argumentNotNull } from "@implab/core-amd/safe";
1 import { argumentNotNull } from "@implab/core-amd/safe";
3 import { RenditionBase } from "./RenditionBase";
2 import { RenditionBase } from "./RenditionBase";
4 import { DojoNodePosition, isElementNode, isInPage, isWidget } from "./traits";
3 import { DojoNodePosition, isElementNode, isInPage, isWidget, placeAt } from "./traits";
5 import registry = require("dijit/registry");
4 import registry = require("dijit/registry");
6 import ContentPane = require("dijit/layout/ContentPane");
5 import ContentPane = require("dijit/layout/ContentPane");
6 import { getItemDom, refHook } from "./render";
7
7
8 // tslint:disable-next-line: class-name
8 // tslint:disable-next-line: class-name
9 export interface _Widget {
9 export interface _Widget {
10 domNode: Node;
10 domNode: Node;
11
11
12 containerNode?: Node;
12 containerNode?: Node;
13
13
14 placeAt?(refNode: string | Node, position?: DojoNodePosition): void;
14 placeAt?(refNode: string | Node, position?: DojoNodePosition): void;
15 startup?(): void;
15 startup?(): void;
16
16
17 addChild?(widget: any, index?: number): void;
17 addChild?(widget: unknown, index?: number): void;
18 }
18 }
19
19
20 export type _WidgetCtor = new (attrs: any, srcNode?: string | Node) => _Widget;
20 export type _WidgetCtor = new (attrs: {}, srcNode?: string | Node) => _Widget;
21
21
22 export class WidgetRendition extends RenditionBase<Node> {
22 export class WidgetRendition extends RenditionBase<Node> {
23 readonly widgetClass: _WidgetCtor;
23 readonly widgetClass: _WidgetCtor;
24
24
25 _instance: _Widget | undefined;
25 _instance: _Widget | undefined;
26
26
27 constructor(widgetClass: _WidgetCtor) {
27 constructor(widgetClass: _WidgetCtor) {
28 super();
28 super();
29 argumentNotNull(widgetClass, "widgetClass");
29 argumentNotNull(widgetClass, "widgetClass");
30
30
31 this.widgetClass = widgetClass;
31 this.widgetClass = widgetClass;
32 }
32 }
33
33
34 _addChild(child: any): void {
34 _addChild(child: unknown): void {
35 const instance = this._getInstance();
35 const instance = this._getInstance();
36
36
37 if (instance.addChild) {
37 if (instance.addChild) {
38 if (child instanceof WidgetRendition) {
38 if (child instanceof WidgetRendition) {
39 // layout containers add custom logic to addChild methods
39 // layout containers add custom logic to addChild methods
40 instance.addChild(child.getWidgetInstance());
40 instance.addChild(child.getWidgetInstance());
41 } else if (isWidget(child)) {
41 } else if (isWidget(child)) {
42 instance.addChild(child);
42 instance.addChild(child);
43 } else {
43 } else {
44 const childDom = this.getItemDom(child);
44 const childDom = getItemDom(child);
45 const w = isElementNode(childDom) ? registry.byNode(childDom) : undefined;
45 const w = isElementNode(childDom) ? registry.byNode(childDom) : undefined;
46
46
47 if (w) {
47 if (w) {
48 instance.addChild(w);
48 instance.addChild(w);
49 } else {
49 } else {
50 if (!instance.containerNode)
50 if (!instance.containerNode)
51 throw new Error("Failed to add DOM content. The widget doesn't have a containerNode");
51 throw new Error("Failed to add DOM content. The widget doesn't have a containerNode");
52
52
53 // the current widget isn't started, it's children shouldn't start too
53 // the current widget isn't started, it's children shouldn't start too
54 dom.place(this.getItemDom(child), instance.containerNode);
54 placeAt(getItemDom(child), instance.containerNode, "last");
55 }
55 }
56 }
56 }
57 } else {
57 } else {
58 if (!instance.containerNode)
58 if (!instance.containerNode)
59 throw new Error("The widget doesn't have neither addChild nor containerNode");
59 throw new Error("The widget doesn't have neither addChild nor containerNode");
60
60
61 // the current widget isn't started, it's children shouldn't start too
61 // the current widget isn't started, it's children shouldn't start too
62 dom.place(this.getItemDom(child), instance.containerNode);
62 placeAt(getItemDom(child), instance.containerNode, "last");
63 }
63 }
64 }
64 }
65
65
66 protected _create(attrs: any, children: any[]) {
66 protected _create({ref, ...attrs}: {ref?: JSX.Ref<_Widget>}, children: unknown[]) {
67 if (this.widgetClass.prototype instanceof ContentPane) {
67 if (this.widgetClass.prototype instanceof ContentPane) {
68 // a special case for the ContentPane this is for
68 // a special case for the ContentPane this is for
69 // the compatibility with this heavy widget, all
69 // compatibility with that heavy widget, all
70 // regular containers could be easily manipulated
70 // regular containers could be easily manipulated
71 // through `containerNode` property or `addChild` method.
71 // through `containerNode` property or `addChild` method.
72
72
73 // render children to the DocumentFragment
73 // render children to the DocumentFragment
74 const content = document.createDocumentFragment();
74 const content = document.createDocumentFragment();
75 children.forEach(child => content.appendChild(this.getItemDom(child)));
75 children.forEach(child => content.appendChild(getItemDom(child)));
76
76
77 // set the content property to the parameters of the widget
77 // set the content property to the parameters of the widget
78 const _attrs = { ...attrs, content };
78 const _attrs = { ...attrs, content };
79 this._instance = new this.widgetClass(_attrs);
79 this._instance = new this.widgetClass(_attrs);
80 } else {
80 } else {
81 this._instance = new this.widgetClass(attrs);
81 this._instance = new this.widgetClass(attrs);
82 children.forEach(x => this._addChild(x));
82 children.forEach(x => this._addChild(x));
83 }
83 }
84
84
85 if (ref)
86 refHook(this._instance, ref);
85 }
87 }
86
88
87 private _getInstance() {
89 private _getInstance() {
88 if (!this._instance)
90 if (!this._instance)
89 throw new Error("The instance of the widget isn't created");
91 throw new Error("The instance of the widget isn't created");
90 return this._instance;
92 return this._instance;
91 }
93 }
92
94
93 protected _getDomNode() {
95 protected _getDomNode() {
94 if (!this._instance)
96 if (!this._instance)
95 throw new Error("The instance of the widget isn't created");
97 throw new Error("The instance of the widget isn't created");
96 return this._instance.domNode;
98 return this._instance.domNode;
97 }
99 }
98
100
99 /** Overrides default placeAt implementation. Calls placeAt of the
101 /** Overrides default placeAt implementation. Calls placeAt of the
100 * widget and then starts it.
102 * widget and then starts it.
101 *
103 *
102 * @param refNode A node or id of the node where the widget should be placed.
104 * @param refNode A node or id of the node where the widget should be placed.
103 * @param position A position relative to refNode.
105 * @param position A position relative to refNode.
104 */
106 */
105 placeAt(refNode: string | Node, position?: DojoNodePosition) {
107 placeAt(refNode: string | Node, position?: DojoNodePosition) {
106 this.ensureCreated();
108 this.ensureCreated();
107 const instance = this._getInstance();
109 const instance = this._getInstance();
108 if (typeof instance.placeAt === "function") {
110 if (typeof instance.placeAt === "function") {
109 instance.placeAt(refNode, position);
111 instance.placeAt(refNode, position);
110
112
111 // fix the dojo startup behavior when the widget is placed
113 // fix the dojo startup behavior when the widget is placed
112 // directly to the document and doesn't have any enclosing widgets
114 // directly to the document and doesn't have any enclosing widgets
113 const parentWidget = instance.domNode.parentNode ?
115 const parentWidget = instance.domNode.parentNode ?
114 registry.getEnclosingWidget(instance.domNode.parentNode) : null;
116 registry.getEnclosingWidget(instance.domNode.parentNode) : null;
115 if (!parentWidget && isInPage(instance.domNode) && typeof instance.startup === "function")
117 if (!parentWidget && isInPage(instance.domNode) && typeof instance.startup === "function")
116 instance.startup();
118 instance.startup();
117 } else {
119 } else {
118 // the widget doesn't have a placeAt method, strange but whatever
120 // the widget doesn't have a placeAt method, strange but whatever
119 super.placeAt(refNode, position);
121 super.placeAt(refNode, position);
120 }
122 }
121 }
123 }
122
124
123 getWidgetInstance() {
125 getWidgetInstance() {
124 this.ensureCreated();
126 this.ensureCreated();
125 return this._getInstance();
127 return this._getInstance();
126 }
128 }
127
129
128 }
130 }
@@ -1,26 +1,112
1 import { Scope } from "./Scope";
1 import { TraceSource } from "@implab/core-amd/log/TraceSource";
2 import { destroy, Rendition } from "./traits";
2 import { isPromise } from "@implab/core-amd/safe";
3 import { id as mid } from "module";
4 import { IScope, Scope } from "./Scope";
5 import { isNode, isRendition, isWidget } from "./traits";
6
7 const trace = TraceSource.get(mid);
3
8
4 let _scope = Scope.dummy;
9 interface Context {
10 scope: IScope;
5
11
6 const beginRender = async () => {
12 hooks?: (() => void)[];
13 }
14
15 let _context: Context = {
16 scope: Scope.dummy
7 }
17 }
8
18
9 const endRender = () => {
19 const guard = (cb: () => unknown) => {
20 try {
21 const result = cb()
22 if (isPromise(result)) {
23 const warn = (ret: unknown) => trace.error("The callback {0} competed asynchronously. result = {1}", cb, ret);
24 result.then(warn, warn);
25 }
26 } catch (e) {
27 trace.error(e);
28 }
29 }
30
31 export const beginRender = (scope: IScope = getScope()) => {
32 const prev = _context;
33 _context = {
34 scope,
35 hooks: []
36 };
37 return endRender(prev);
38 }
39
40 /**
41 * Completes render operation
42 */
43 const endRender = (prev: Context) => () => {
44 const { hooks } = _context;
45 if (hooks)
46 hooks.forEach(guard);
47
48 _context = prev;
10 }
49 }
11
50
12 export const getScope = () => _scope;
51 export const renderHook = (hook: () => void) => {
52 const { hooks } = _context;
53 if (hooks)
54 hooks.push(hook);
55 else
56 guard(hook);
57 }
13
58
14 export const render = async (rendition: () => Rendition, scope = Scope.dummy) => {
59 export const refHook = <T>(value: T, ref: JSX.Ref<T>) => {
15 await beginRender();
60 const { hooks, scope } = _context;
16 const prev = _scope;
61 if (hooks)
17 _scope = scope;
62 hooks.push(() => ref(value));
63 else
64 guard(() => ref(value));
65
66 scope.own(() => ref(undefined));
67 }
68
69 /** Returns the current scope */
70 export const getScope = () => _context.scope;
71
72 /** Schedules the rendition to be rendered to the DOM Node
73 * @param rendition The rendition to be rendered
74 * @param scope The scope
75 */
76 export const render = (rendition: unknown, scope = Scope.dummy) => {
77 const complete = beginRender(scope);
18 try {
78 try {
19 const node = rendition().getDomNode();
79 return getItemDom(rendition);
20 scope.own(() => destroy(node));
21 return node;
22 } finally {
80 } finally {
23 _scope = prev;
81 complete();
24 endRender();
25 }
82 }
26 }
83 }
84
85 /** Renders DOM element for different types of the argument. */
86 export const getItemDom = (v: unknown) => {
87 if (typeof v === "string" || typeof v === "number" || v instanceof RegExp || v instanceof Date) {
88 // primitive types converted to the text nodes
89 return document.createTextNode(v.toString());
90 } else if (isNode(v)) {
91 // nodes are kept as is
92 return v;
93 } else if (isRendition(v)) {
94 // renditions are instantiated
95 return v.getDomNode();
96 } else if (isWidget(v)) {
97 // widgets are converted to it's markup
98 return v.domNode;
99 } else if (typeof v === "boolean" || v === null || v === undefined) {
100 // null | undefined | boolean are removed
101 return document.createDocumentFragment();
102 } else if (v instanceof Array) {
103 // arrays will be translated to document fragments
104 const fragment = document.createDocumentFragment();
105 v.map(item => getItemDom(item))
106 .forEach(node => fragment.appendChild(node));
107 return fragment;
108 } else {
109 // bug: explicit error otherwise
110 throw new Error("Invalid parameter: " + v);
111 }
112 }
@@ -1,161 +1,214
1 import { IDestroyable } from "@implab/core-amd/interfaces";
1 import { IDestroyable } from "@implab/core-amd/interfaces";
2 import { isDestroyable } from "@implab/core-amd/safe";
2 import { isDestroyable } from "@implab/core-amd/safe";
3 import _WidgetBase = require("dijit/_WidgetBase");
3 import _WidgetBase = require("dijit/_WidgetBase");
4 import registry = require("dijit/registry");
4 import registry = require("dijit/registry");
5 import dom = require("dojo/dom-construct");
6
5
7 type _WidgetBaseConstructor = typeof _WidgetBase;
6 interface _WidgetBaseConstructor {
7 new <A = {}, E extends { [k in keyof E]: Event } = {}>(params?: Partial<_WidgetBase<E> & A>, srcNodeRef?: dojo.NodeOrString): _WidgetBase<E> & dojo._base.DeclareCreatedObject;
8 prototype: _WidgetBase<any>;
9 }
8
10
9 export type DojoNodePosition = "first" | "after" | "before" | "last" | "replace" | "only" | number;
11 export type DojoNodePosition = "first" | "after" | "before" | "last" | "replace" | "only" | number;
10
12
11 export type DojoNodeLocation = [Node | null, DojoNodePosition];
13 export type DojoNodeLocation = [Node, DojoNodePosition];
12
14
13 export interface Rendition<TNode extends Node = Node> {
15 export interface Rendition<TNode extends Node = Node> {
14 getDomNode(): TNode;
16 getDomNode(): TNode;
15
17
16 placeAt(refNode: string | Node, position?: DojoNodePosition): void;
18 placeAt(refNode: string | Node, position?: DojoNodePosition): void;
17 }
19 }
18
20
19 /**
21 /**
20 * @deprecated use Rendition
22 * @deprecated use Rendition
21 */
23 */
22 export type BuildContext<TNode extends Node = Node> = Rendition<TNode>;
24 export type BuildContext<TNode extends Node = Node> = Rendition<TNode>;
23
25
24 export interface IRecursivelyDestroyable {
26 export interface IRecursivelyDestroyable {
25 destroyRecursive(): void;
27 destroyRecursive(): void;
26 }
28 }
27
29
28 export function isNode(el: any): el is Node {
30 export const isNode = (el: unknown): el is Node => !!(el && (el as Node).nodeName && (el as Node).nodeType);
29 return el && el.nodeName && el.nodeType;
30 }
31
31
32 export function isElementNode(el: any): el is Element {
32 export const isElementNode = (el: unknown): el is Element => isNode(el) && el.nodeType === 1;
33 return isNode(el) && el.nodeType === 1;
34 }
35
33
36 export function isTextNode(el: any): el is Text {
34 export const isTextNode = (el: unknown): el is Text => isNode(el) && el.nodeType === 3;
37 return isNode(el) && el.nodeType === 3;
38 }
39
35
40 export function isProcessingInstructionNode(el: any): el is ProcessingInstruction {
36 export const isProcessingInstructionNode = (el: unknown): el is ProcessingInstruction => isNode(el) && el.nodeType === 7;
41 return isNode(el) && el.nodeType === 7;
42 }
43
37
44 export function isCommentNode(el: any): el is Comment {
38 export const isCommentNode = (el: unknown): el is Comment => isNode(el) && el.nodeType === 8;
45 return isNode(el) && el.nodeType === 8;
46 }
47
39
48 export function isDocumentNode(el: any): el is Document {
40 export const isDocumentNode = (el: unknown): el is Document => isNode(el) && el.nodeType === 9;
49 return isNode(el) && el.nodeType === 9;
50 }
51
41
52 export function isDocumentTypeNode(el: any): el is DocumentType {
42 export const isDocumentTypeNode = (el: unknown): el is DocumentType => isNode(el) && el.nodeType === 10;
53 return isNode(el) && el.nodeType === 10;
54 }
55
43
56 export function isDocumentFragmentNode(el: any): el is DocumentFragment {
44 export const isDocumentFragmentNode = (el: any): el is DocumentFragment => isNode(el) && el.nodeType === 11;
57 return isNode(el) && el.nodeType === 11;
58 }
59
45
60 export function isWidget(v: any): v is _WidgetBase {
46 export const isWidget = (v: unknown): v is _WidgetBase => !!(v && "domNode" in (v as _WidgetBase));
61 return v && "domNode" in v;
62 }
63
47
64 export function isRendition(v: any): v is Rendition {
48 export const isRendition = (v: unknown): v is Rendition => !!(v && typeof (v as Rendition).getDomNode === "function");
65 return v && typeof v.getDomElement === "function";
66 }
67
49
68 /**
50 /**
69 * @deprecated use isRendition
51 * @deprecated use isRendition
70 */
52 */
71 export const isBuildContext = isRendition;
53 export const isBuildContext = isRendition;
72
54
73 export function isPlainObject(v: object) {
55 export const isPlainObject = (v: object) => {
74 if (typeof v !== "object")
56 if (typeof v !== "object")
75 return false;
57 return false;
76
58
77 const vp = Object.getPrototypeOf(v);
59 const vp = Object.getPrototypeOf(v);
78 return !vp || vp === Object.prototype;
60 return !vp || vp === Object.prototype;
79 }
61 }
80
62
81 export function isWidgetConstructor(v: any): v is _WidgetBaseConstructor {
63 export const isWidgetConstructor = (v: unknown): v is _WidgetBaseConstructor =>
82 return typeof v === "function" && v.prototype && (
64 typeof v === "function" && v.prototype && (
83 "domNode" in v.prototype ||
65 "domNode" in v.prototype ||
84 "buildRendering" in v.prototype
66 "buildRendering" in v.prototype
85 );
67 );
86 }
68
87
69
88 /** Tests whether the specified node is placed in visible dom.
70 /** Tests whether the specified node is placed in visible dom.
89 * @param {Node} node The node to test
71 * @param {Node} node The node to test
90 */
72 */
91 export function isInPage(node: Node) {
73 export const isInPage = (node: Node) => node === document.body ? false : document.body.contains(node);
92 return (node === document.body) ? false : document.body.contains(node);
93 }
94
74
95 export function isRecursivelyDestroyable(target: any): target is IRecursivelyDestroyable {
75 export const isRecursivelyDestroyable = (target: unknown): target is IRecursivelyDestroyable =>
96 return target && typeof target.destroyRecursive === "function";
76 !!(target && typeof (target as IRecursivelyDestroyable).destroyRecursive === "function");
97 }
77
98
78
99
79
100 /** Destroys DOM Node with all contained widgets.
80 /** Destroys DOM Node with all contained widgets.
101 * If the specified node is the root node of a widget, then the
81 * If the specified node is the root node of a widget, then the
102 * widget will be destroyed.
82 * widget will be destroyed.
103 *
83 *
104 * @param target DOM Node or widget to destroy
84 * @param target DOM Node or widget to destroy
105 */
85 */
106 export function destroy(target: Node | IDestroyable | IRecursivelyDestroyable) {
86 export const destroy = (target: Node | IDestroyable | IRecursivelyDestroyable) => {
107 if (isRecursivelyDestroyable(target)) {
87 if (isRecursivelyDestroyable(target)) {
108 target.destroyRecursive();
88 target.destroyRecursive();
109 } else if (isDestroyable(target)) {
89 } else if (isDestroyable(target)) {
110 target.destroy();
90 target.destroy();
111 } else if (isNode(target)) {
91 } else if (isNode(target)) {
112 if (isElementNode(target)) {
92 if (isElementNode(target)) {
113 const w = registry.byNode(target);
93 const w = registry.byNode(target);
114 if (w) {
94 if (w) {
115 w.destroyRecursive();
95 w.destroyRecursive();
96 return;
116 } else {
97 } else {
117 registry.findWidgets(target).forEach(destroy);
98 emptyNode(target);
118 dom.destroy(target);
119 }
99 }
120 }
100 }
101 const parent = target.parentNode;
102 if (parent)
103 parent.removeChild(target);
104
121 }
105 }
122 }
106 }
123
107
124 /** Empties a content of the specified node and destroys all contained widgets.
108 /** Empties a content of the specified node and destroys all contained widgets.
125 *
109 *
126 * @param target DOM node to .
110 * @param target DOM node to empty.
127 */
111 */
128 export function emptyNode(target: Node) {
112 export const emptyNode = (target: Node) => {
129 registry.findWidgets(target).forEach(destroy);
113 registry.findWidgets(target).forEach(destroy);
130 dom.empty(target);
114
115 for (let c; c = target.lastChild;) { // intentional assignment
116 target.removeChild(c);
117 }
131 }
118 }
132
119
133 /** This function starts all widgets inside the DOM node if the target is a node
120 /** This function starts all widgets inside the DOM node if the target is a node
134 * or starts widget itself if the target is the widget. If the specified node
121 * or starts widget itself if the target is the widget. If the specified node
135 * associated with the widget that widget will be started.
122 * associated with the widget that widget will be started.
136 *
123 *
137 * @param target DOM node to find and start widgets or the widget itself.
124 * @param target DOM node to find and start widgets or the widget itself.
138 */
125 */
139 export function startupWidgets(target: Node | _WidgetBase, skipNode?: Node) {
126 export const startupWidgets = (target: Node | _WidgetBase, skipNode?: Node) => {
140 if (isNode(target)) {
127 if (isNode(target)) {
141 if (isElementNode(target)) {
128 if (isElementNode(target)) {
142 const w = registry.byNode(target);
129 const w = registry.byNode(target);
143 if (w) {
130 if (w) {
144 if (w.startup)
131 if (w.startup)
145 w.startup();
132 w.startup();
146 } else {
133 } else {
147 registry.findWidgets(target, skipNode).forEach(x => x.startup());
134 registry.findWidgets(target, skipNode).forEach(x => x.startup());
148 }
135 }
149 }
136 }
150 } else {
137 } else {
151 if (target.startup)
138 if (target.startup)
152 target.startup();
139 target.startup();
153 }
140 }
154 }
141 }
155
142
156 export function locateNode(node: Node): DojoNodeLocation {
143 /** Places the specified DOM node at the specified location.
157 const next = node.nextSibling;
144 *
158 return next ?
145 * @param node The node which should be placed
159 [next, "before"] :
146 * @param refNodeOrId The reference node where the created
160 [node.parentNode, "last"];
147 * DOM should be placed.
161 } No newline at end of file
148 * @param position Optional parameter, specifies the
149 * position relative to refNode. Default is "last" (i.e. last child).
150 */
151 export const placeAt = (node: Node, refNodeOrId: string | Node, position: DojoNodePosition = "last") => {
152 const ref = typeof refNodeOrId == "string" ? document.getElementById(refNodeOrId) : refNodeOrId;
153 if (!ref)
154 return;
155
156 const parent = ref.parentNode;
157
158 if (typeof position == "number") {
159 if (ref.childNodes.length <= position) {
160 ref.appendChild(node);
161 } else {
162 ref.insertBefore(node, ref.childNodes[position]);
163 }
164 } else {
165 switch (position) {
166 case "before":
167 parent && parent.insertBefore(node, ref);
168 break;
169 case "after":
170 parent && parent.insertBefore(node, ref.nextSibling);
171 break;
172 case "first":
173 ref.insertBefore(node, ref.firstChild);
174 break;
175 case "last":
176 ref.appendChild(node);
177 break;
178 case "only":
179 emptyNode(ref);
180 ref.appendChild(node);
181 break;
182 case "replace":
183 if (parent)
184 parent.replaceChild(node, ref);
185 destroy(ref);
186 break;
187 }
188 }
189 }
190
191 /** Collects nodes from collection to an array.
192 *
193 * @param collection The collection of nodes.
194 * @returns The array of nodes.
195 */
196 export const collectNodes = (collection: NodeListOf<ChildNode>) => {
197 const items = [];
198 for (let i = 0, n = collection.length; i < n; i++) {
199 items.push(collection[i]);
200 }
201 return items;
202 };
203
204
205 export const isMounted = (node: Node) => {
206 if (node.parentNode) {
207 const parentWidget = registry.getEnclosingWidget(node.parentNode);
208 if (parentWidget && parentWidget._started)
209 return true;
210 }
211 if (isInPage(node))
212 return true;
213 return false;
214 }; No newline at end of file
1 NO CONTENT: file renamed from djx/src/main/typings/css.d.ts to djx/src/main/typings/css-plugin.d.ts
NO CONTENT: file renamed from djx/src/main/typings/css.d.ts to djx/src/main/typings/css-plugin.d.ts
@@ -1,68 +1,81
1 /// <reference path="./css.d.ts"/>
1 /// <reference path="./css-plugin.d.ts"/>
2
2
3 declare namespace JSX {
3 declare namespace JSX {
4
4
5 interface DjxIntrinsicAttributes {
5 type Ref<T> = ((value: T | undefined) => void);
6
7 interface DjxIntrinsicAttributes<E> {
6 /** alias for className */
8 /** alias for className */
7 class: string;
9 class: string;
8
10
9 /** specifies the name of the property in the widget where the the
11 /** specifies the name of the property in the widget where the the
10 * reference to the current object will be stored
12 * reference to the current object will be stored
11 */
13 */
12 "data-dojo-attach-point": string;
14 "data-dojo-attach-point": string;
13
15
14 /** specifies handlers map for the events */
16 /** specifies handlers map for the events */
15 "data-dojo-attach-event": string;
17 "data-dojo-attach-event": string;
16
18
19 ref: Ref<E>;
20
17 /** @deprecated */
21 /** @deprecated */
18 [attr: string]: any;
22 [attr: string]: any;
19 }
23 }
20
24
21 interface DjxIntrinsicElements {
25 interface DjxIntrinsicElements {
22 }
26 }
23
27
24 type RecursivePartial<T> = T extends string | number | boolean | null | undefined | Function ?
28 type RecursivePartial<T> = T extends string | number | boolean | null | undefined | Function ?
25 T :
29 T :
26 { [k in keyof T]?: RecursivePartial<T[k]> };
30 { [k in keyof T]?: RecursivePartial<T[k]> };
27
31
28 type MatchingMemberKeys<T, U> = {
32 type MatchingMemberKeys<T, U> = {
29 [K in keyof T]: T[K] extends U ? K : never;
33 [K in keyof T]: T[K] extends U ? K : never;
30 }[keyof T];
34 }[keyof T];
31 type NotMatchingMemberKeys<T, U> = {
35 type NotMatchingMemberKeys<T, U> = {
32 [K in keyof T]: T[K] extends U ? never : K;
36 [K in keyof T]: T[K] extends U ? never : K;
33 }[keyof T];
37 }[keyof T];
34
38
35 type ExtractMembers<T, U> = Pick<T, MatchingMemberKeys<T, U>>;
39 type ExtractMembers<T, U> = Pick<T, MatchingMemberKeys<T, U>>;
36
40
37 type ExcludeMembers<T, U> = Pick<T, NotMatchingMemberKeys<T, U>>;
41 type ExcludeMembers<T, U> = Pick<T, NotMatchingMemberKeys<T, U>>;
38
42
39 type ElementAttrNames<E> = NotMatchingMemberKeys<E, (...args: any[]) => any>;
43 type ElementAttrNames<E> = NotMatchingMemberKeys<E, (...args: any[]) => any>;
40
44
41 type ElementAttrType<E, K extends keyof any> = K extends keyof E ? RecursivePartial<E[K]> : string;
45 type ElementAttrType<E, K extends keyof any> = K extends keyof E ? RecursivePartial<E[K]> : string;
42
46
43
47
44 type ElementAttrNamesBlacklist = "children" | "getRootNode" | keyof EventTarget;
48 type ElementAttrNamesBlacklist = "children" | "getRootNode" | keyof EventTarget;
45
49
46 /** This type extracts keys of the specified parameter E by the following rule:
50 /** This type extracts keys of the specified parameter E by the following rule:
47 * 1. skips all ElementAttrNamesBlacklist
51 * 1. skips all ElementAttrNamesBlacklist
48 * 2. skips all methods except with the signature of event handlers
52 * 2. skips all methods except with the signature of event handlers
49 */
53 */
50 type AssignableElementAttrNames<E> = {
54 type AssignableElementAttrNames<E> = {
51 [K in keyof E]: K extends ElementAttrNamesBlacklist ? never :
55 [K in keyof E]: K extends ElementAttrNamesBlacklist ? never :
52 ((evt: Event) => any) extends E[K] ? K :
56 ((evt: Event) => any) extends E[K] ? K :
53 E[K] extends ((...args: any[]) => any) ? never :
57 E[K] extends ((...args: any[]) => any) ? never :
54 K;
58 K;
55 }[keyof E];
59 }[keyof E];
56
60
57 type LaxElement<E extends object> =
61 type LaxElement<E extends object> =
58 Pick<E, AssignableElementAttrNames<E>> &
62 Pick<E, AssignableElementAttrNames<E>> &
59 DjxIntrinsicAttributes;
63 DjxIntrinsicAttributes<E>;
60
64
61 type LaxIntrinsicElementsMap = {
65 type LaxIntrinsicElementsMap = {
62 [tag in keyof HTMLElementTagNameMap]: LaxElement<HTMLElementTagNameMap[tag]>
66 [tag in keyof HTMLElementTagNameMap]: LaxElement<HTMLElementTagNameMap[tag]>
63 } & DjxIntrinsicElements;
67 } & DjxIntrinsicElements;
64
68
65 type IntrinsicElements = {
69 type IntrinsicElements = {
66 [tag in keyof LaxIntrinsicElementsMap]: RecursivePartial<LaxIntrinsicElementsMap[tag]>;
70 [tag in keyof LaxIntrinsicElementsMap]: RecursivePartial<LaxIntrinsicElementsMap[tag]>;
67 }
71 }
72
73 interface ElementChildrenAttribute {
74 children: {};
68 }
75 }
76
77 interface IntrinsicClassAttributes<T> {
78 ref?: (value: T) => void;
79 children?: unknown;
80 }
81 }
@@ -1,1 +1,2
1 import "./DeclareTests"; No newline at end of file
1 import "./declare-tests";
2 import "./observable-tests"; No newline at end of file
@@ -1,70 +1,70
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, on } from "../tsx";
4 import { createElement, on } 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 detail: number;
14 detail: number;
15 };
15 };
16
16
17 "count-dec": Event & {
17 "count-dec": Event & {
18 detail: number;
18 detail: number;
19 };
19 };
20 }
20 }
21
21
22
22
23 @djclass
23 @djclass
24 export class MyWidget extends djbase(DjxWidgetBase as AbstractConstructor<DjxWidgetBase<MyWidgetAttrs, MyWidgetEvents>>) {
24 export class MyWidget extends djbase(DjxWidgetBase as AbstractConstructor<DjxWidgetBase<MyWidgetAttrs, MyWidgetEvents>>) {
25
25
26 @bind({ node: "titleNode", type: "innerHTML" })
26 @bind({ node: "titleNode", type: "innerHTML" })
27 title = "";
27 title = "";
28
28
29 @prototype()
29 @prototype()
30 counter = 0;
30 counter = 0;
31
31
32 render() {
32 render() {
33 const Frame = (props: any) => <div>{props.children}</div>;
33 const Frame = ({children, ref}: {ref: JSX.Ref<HTMLDivElement>, children: any[]}) => <div ref={ref} >{children}</div>;
34 return <div className="myWidget" onsubmit={e => this._onSubmit(e)} tabIndex={3} style={{ alignContent: "center", border: "1px solid" }} >
34 return <div className="myWidget" onsubmit={e => this._onSubmit(e)} tabIndex={3} style={{ alignContent: "center", border: "1px solid" }} >
35 <h1 data-dojo-attach-point="titleNode"></h1>
35 <h1 data-dojo-attach-point="titleNode"></h1>
36 <Frame>
36 <Frame ref={ v => {}}>
37 <span class="up-button" onclick={e => this._onIncClick(e)}>[+]</span>
37 <span class="up-button" onclick={e => this._onIncClick(e)}>[+]</span>
38 <span class="down-button" onclick={() => this._onDecClick()}>[-]</span>
38 <span class="down-button" onclick={() => this._onDecClick()}>[-]</span>
39 </Frame>
39 </Frame>
40 </div>;
40 </div>;
41 }
41 }
42
42
43 postCreate() {
43 postCreate() {
44 super.postCreate();
44 super.postCreate();
45
45
46 this.on("click", () => {});
46 this.on("click", () => {});
47 }
47 }
48
48
49 _onSubmit(e: Event) {
49 _onSubmit(e: Event) {
50 }
50 }
51
51
52 _onIncClick(e: MouseEvent) {
52 _onIncClick(e: MouseEvent) {
53 this.set("counter", this.counter + 1);
53 this.set("counter", this.counter + 1);
54
54
55 this.emit("count-inc", { bubbles: false });
55 this.emit("count-inc", { bubbles: false });
56 }
56 }
57
57
58 _onDecClick() {
58 _onDecClick() {
59 this.emit("count-dec", { bubbles: false, detail: this.counter });
59 this.emit("count-dec", { bubbles: false, detail: this.counter });
60 }
60 }
61
61
62 @on("count-inc")
62 @on("count-inc")
63 private _onCounterInc(evt: Event & { detail: number; x?: number; }) {
63 private _onCounterInc(evt: Event & { detail: number; x?: number; }) {
64 }
64 }
65
65
66 @on("click", "keydown")
66 @on("click", "keydown")
67 protected _onClick(event: MouseEvent | KeyboardEvent) {
67 protected _onClick(event: MouseEvent | KeyboardEvent) {
68
68
69 }
69 }
70 } No newline at end of file
70 }
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,5 +1,5
1 distributionBase=GRADLE_USER_HOME
1 distributionBase=GRADLE_USER_HOME
2 distributionPath=wrapper/dists
2 distributionPath=wrapper/dists
3 distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-bin.zip
3 distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
4 zipStoreBase=GRADLE_USER_HOME
4 zipStoreBase=GRADLE_USER_HOME
5 zipStorePath=wrapper/dists
5 zipStorePath=wrapper/dists
@@ -1,188 +1,183
1 #!/usr/bin/env sh
1 #!/usr/bin/env sh
2
2
3 #
3 #
4 # Copyright 2015 the original author or authors.
4 # Copyright 2015 the original author or authors.
5 #
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
8 # You may obtain a copy of the License at
9 #
9 #
10 # https://www.apache.org/licenses/LICENSE-2.0
10 # https://www.apache.org/licenses/LICENSE-2.0
11 #
11 #
12 # Unless required by applicable law or agreed to in writing, software
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
16 # limitations under the License.
17 #
17 #
18
18
19 ##############################################################################
19 ##############################################################################
20 ##
20 ##
21 ## Gradle start up script for UN*X
21 ## Gradle start up script for UN*X
22 ##
22 ##
23 ##############################################################################
23 ##############################################################################
24
24
25 # Attempt to set APP_HOME
25 # Attempt to set APP_HOME
26 # Resolve links: $0 may be a link
26 # Resolve links: $0 may be a link
27 PRG="$0"
27 PRG="$0"
28 # Need this for relative symlinks.
28 # Need this for relative symlinks.
29 while [ -h "$PRG" ] ; do
29 while [ -h "$PRG" ] ; do
30 ls=`ls -ld "$PRG"`
30 ls=`ls -ld "$PRG"`
31 link=`expr "$ls" : '.*-> \(.*\)$'`
31 link=`expr "$ls" : '.*-> \(.*\)$'`
32 if expr "$link" : '/.*' > /dev/null; then
32 if expr "$link" : '/.*' > /dev/null; then
33 PRG="$link"
33 PRG="$link"
34 else
34 else
35 PRG=`dirname "$PRG"`"/$link"
35 PRG=`dirname "$PRG"`"/$link"
36 fi
36 fi
37 done
37 done
38 SAVED="`pwd`"
38 SAVED="`pwd`"
39 cd "`dirname \"$PRG\"`/" >/dev/null
39 cd "`dirname \"$PRG\"`/" >/dev/null
40 APP_HOME="`pwd -P`"
40 APP_HOME="`pwd -P`"
41 cd "$SAVED" >/dev/null
41 cd "$SAVED" >/dev/null
42
42
43 APP_NAME="Gradle"
43 APP_NAME="Gradle"
44 APP_BASE_NAME=`basename "$0"`
44 APP_BASE_NAME=`basename "$0"`
45
45
46 # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
46 # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
47 DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48
48
49 # Use the maximum available, or set MAX_FD != -1 to use that value.
49 # Use the maximum available, or set MAX_FD != -1 to use that value.
50 MAX_FD="maximum"
50 MAX_FD="maximum"
51
51
52 warn () {
52 warn () {
53 echo "$*"
53 echo "$*"
54 }
54 }
55
55
56 die () {
56 die () {
57 echo
57 echo
58 echo "$*"
58 echo "$*"
59 echo
59 echo
60 exit 1
60 exit 1
61 }
61 }
62
62
63 # OS specific support (must be 'true' or 'false').
63 # OS specific support (must be 'true' or 'false').
64 cygwin=false
64 cygwin=false
65 msys=false
65 msys=false
66 darwin=false
66 darwin=false
67 nonstop=false
67 nonstop=false
68 case "`uname`" in
68 case "`uname`" in
69 CYGWIN* )
69 CYGWIN* )
70 cygwin=true
70 cygwin=true
71 ;;
71 ;;
72 Darwin* )
72 Darwin* )
73 darwin=true
73 darwin=true
74 ;;
74 ;;
75 MINGW* )
75 MINGW* )
76 msys=true
76 msys=true
77 ;;
77 ;;
78 NONSTOP* )
78 NONSTOP* )
79 nonstop=true
79 nonstop=true
80 ;;
80 ;;
81 esac
81 esac
82
82
83 CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
83 CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84
84
85 # Determine the Java command to use to start the JVM.
85 # Determine the Java command to use to start the JVM.
86 if [ -n "$JAVA_HOME" ] ; then
86 if [ -n "$JAVA_HOME" ] ; then
87 if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
87 if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 # IBM's JDK on AIX uses strange locations for the executables
88 # IBM's JDK on AIX uses strange locations for the executables
89 JAVACMD="$JAVA_HOME/jre/sh/java"
89 JAVACMD="$JAVA_HOME/jre/sh/java"
90 else
90 else
91 JAVACMD="$JAVA_HOME/bin/java"
91 JAVACMD="$JAVA_HOME/bin/java"
92 fi
92 fi
93 if [ ! -x "$JAVACMD" ] ; then
93 if [ ! -x "$JAVACMD" ] ; then
94 die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
94 die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95
95
96 Please set the JAVA_HOME variable in your environment to match the
96 Please set the JAVA_HOME variable in your environment to match the
97 location of your Java installation."
97 location of your Java installation."
98 fi
98 fi
99 else
99 else
100 JAVACMD="java"
100 JAVACMD="java"
101 which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
101 which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102
102
103 Please set the JAVA_HOME variable in your environment to match the
103 Please set the JAVA_HOME variable in your environment to match the
104 location of your Java installation."
104 location of your Java installation."
105 fi
105 fi
106
106
107 # Increase the maximum file descriptors if we can.
107 # Increase the maximum file descriptors if we can.
108 if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
108 if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 MAX_FD_LIMIT=`ulimit -H -n`
109 MAX_FD_LIMIT=`ulimit -H -n`
110 if [ $? -eq 0 ] ; then
110 if [ $? -eq 0 ] ; then
111 if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
111 if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 MAX_FD="$MAX_FD_LIMIT"
112 MAX_FD="$MAX_FD_LIMIT"
113 fi
113 fi
114 ulimit -n $MAX_FD
114 ulimit -n $MAX_FD
115 if [ $? -ne 0 ] ; then
115 if [ $? -ne 0 ] ; then
116 warn "Could not set maximum file descriptor limit: $MAX_FD"
116 warn "Could not set maximum file descriptor limit: $MAX_FD"
117 fi
117 fi
118 else
118 else
119 warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
119 warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 fi
120 fi
121 fi
121 fi
122
122
123 # For Darwin, add options to specify how the application appears in the dock
123 # For Darwin, add options to specify how the application appears in the dock
124 if $darwin; then
124 if $darwin; then
125 GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
125 GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 fi
126 fi
127
127
128 # For Cygwin or MSYS, switch paths to Windows format before running java
128 # For Cygwin or MSYS, switch paths to Windows format before running java
129 if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
129 if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
130 APP_HOME=`cygpath --path --mixed "$APP_HOME"`
130 APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
131 CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 JAVACMD=`cygpath --unix "$JAVACMD"`
132 JAVACMD=`cygpath --unix "$JAVACMD"`
133
133
134 # We build the pattern for arguments to be converted via cygpath
134 # We build the pattern for arguments to be converted via cygpath
135 ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
135 ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 SEP=""
136 SEP=""
137 for dir in $ROOTDIRSRAW ; do
137 for dir in $ROOTDIRSRAW ; do
138 ROOTDIRS="$ROOTDIRS$SEP$dir"
138 ROOTDIRS="$ROOTDIRS$SEP$dir"
139 SEP="|"
139 SEP="|"
140 done
140 done
141 OURCYGPATTERN="(^($ROOTDIRS))"
141 OURCYGPATTERN="(^($ROOTDIRS))"
142 # Add a user-defined pattern to the cygpath arguments
142 # Add a user-defined pattern to the cygpath arguments
143 if [ "$GRADLE_CYGPATTERN" != "" ] ; then
143 if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
144 OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 fi
145 fi
146 # Now convert the arguments - kludge to limit ourselves to /bin/sh
146 # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 i=0
147 i=0
148 for arg in "$@" ; do
148 for arg in "$@" ; do
149 CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
149 CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
150 CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151
151
152 if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
152 if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
153 eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 else
154 else
155 eval `echo args$i`="\"$arg\""
155 eval `echo args$i`="\"$arg\""
156 fi
156 fi
157 i=$((i+1))
157 i=`expr $i + 1`
158 done
158 done
159 case $i in
159 case $i in
160 (0) set -- ;;
160 0) set -- ;;
161 (1) set -- "$args0" ;;
161 1) set -- "$args0" ;;
162 (2) set -- "$args0" "$args1" ;;
162 2) set -- "$args0" "$args1" ;;
163 (3) set -- "$args0" "$args1" "$args2" ;;
163 3) set -- "$args0" "$args1" "$args2" ;;
164 (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
164 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
165 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
166 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
167 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
168 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
169 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 esac
170 esac
171 fi
171 fi
172
172
173 # Escape application args
173 # Escape application args
174 save () {
174 save () {
175 for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
175 for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 echo " "
176 echo " "
177 }
177 }
178 APP_ARGS=$(save "$@")
178 APP_ARGS=`save "$@"`
179
179
180 # Collect all arguments for the java command, following the shell quoting and substitution rules
180 # Collect all arguments for the java command, following the shell quoting and substitution rules
181 eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
181 eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182
182
183 # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
184 if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
185 cd "$(dirname "$0")"
186 fi
187
188 exec "$JAVACMD" "$@"
183 exec "$JAVACMD" "$@"
@@ -1,17 +1,18
1 /*
1 /*
2 * This settings file was generated by the Gradle 'init' task.
2 * This settings file was generated by the Gradle 'init' task.
3 *
3 *
4 * The settings file is used to specify which projects to include in your build.
4 * The settings file is used to specify which projects to include in your build.
5 * In a single project build this file can be empty or even removed.
5 * In a single project build this file can be empty or even removed.
6 *
6 *
7 * Detailed information about configuring a multi-project build in Gradle can be found
7 * Detailed information about configuring a multi-project build in Gradle can be found
8 * in the user guide at https://docs.gradle.org/3.5/userguide/multi_project_builds.html
8 * in the user guide at https://docs.gradle.org/3.5/userguide/multi_project_builds.html
9 */
9 */
10
10
11 // To declare projects as part of a multi-project build use the 'include' method
11 // To declare projects as part of a multi-project build use the 'include' method
12
12
13 //include 'sub-project-name'
13 //include 'sub-project-name'
14
14
15 rootProject.name = 'implabjs-djx'
15 rootProject.name = 'implabjs-djx'
16
16
17 include 'djx' No newline at end of file
17 include 'djx'
18 include 'playground' No newline at end of file
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now