@@ -0,0 +1,1 | |||||
|
1 | .eslintrc.js No newline at end of file |
@@ -0,0 +1,39 | |||||
|
1 | module.exports = { | |||
|
2 | root: true, | |||
|
3 | extends: [ | |||
|
4 | "plugin:react/recommended", | |||
|
5 | "eslint:recommended", | |||
|
6 | "plugin:@typescript-eslint/eslint-recommended", | |||
|
7 | "plugin:@typescript-eslint/recommended", | |||
|
8 | "plugin:@typescript-eslint/recommended-requiring-type-checking" | |||
|
9 | ], | |||
|
10 | parser: "@typescript-eslint/parser", | |||
|
11 | parserOptions: { | |||
|
12 | ecmaFeatures: { | |||
|
13 | jsx: true | |||
|
14 | }, | |||
|
15 | ecmaVersion: 5, | |||
|
16 | tsconfigRootDir: __dirname + "/src", | |||
|
17 | project: [ | |||
|
18 | "tsconfig.eslint.json", | |||
|
19 | "*/tsconfig.json" | |||
|
20 | ] | |||
|
21 | }, | |||
|
22 | plugins: [ | |||
|
23 | "react", | |||
|
24 | "@typescript-eslint" | |||
|
25 | ], | |||
|
26 | rules: { | |||
|
27 | "react/react-in-jsx-scope": "off", | |||
|
28 | "react/no-unknown-property": "off", | |||
|
29 | "@typescript-eslint/no-empty-function": "off", | |||
|
30 | "max-classes-per-file": [ | |||
|
31 | "error", | |||
|
32 | { "ignoreExpressions": true, "max": 1 } | |||
|
33 | ], | |||
|
34 | "@typescript-eslint/prefer-readonly": ["error"], | |||
|
35 | "semi": "off", | |||
|
36 | "@typescript-eslint/semi": ["error"] | |||
|
37 | ||||
|
38 | } | |||
|
39 | } |
@@ -0,0 +1,39 | |||||
|
1 | module.exports = { | |||
|
2 | root: true, | |||
|
3 | extends: [ | |||
|
4 | "plugin:react/recommended", | |||
|
5 | "eslint:recommended", | |||
|
6 | "plugin:@typescript-eslint/eslint-recommended", | |||
|
7 | "plugin:@typescript-eslint/recommended", | |||
|
8 | "plugin:@typescript-eslint/recommended-requiring-type-checking" | |||
|
9 | ], | |||
|
10 | parser: "@typescript-eslint/parser", | |||
|
11 | parserOptions: { | |||
|
12 | ecmaFeatures: { | |||
|
13 | jsx: true | |||
|
14 | }, | |||
|
15 | ecmaVersion: 5, | |||
|
16 | tsconfigRootDir: __dirname + "/src", | |||
|
17 | project: [ | |||
|
18 | "tsconfig.eslint.json", | |||
|
19 | "*/tsconfig.json" | |||
|
20 | ] | |||
|
21 | }, | |||
|
22 | plugins: [ | |||
|
23 | "react", | |||
|
24 | "@typescript-eslint" | |||
|
25 | ], | |||
|
26 | rules: { | |||
|
27 | "react/react-in-jsx-scope": "off", | |||
|
28 | "react/no-unknown-property": "off", | |||
|
29 | "@typescript-eslint/no-empty-function": "off", | |||
|
30 | "max-classes-per-file": [ | |||
|
31 | "error", | |||
|
32 | { "ignoreExpressions": true, "max": 1 } | |||
|
33 | ], | |||
|
34 | "@typescript-eslint/prefer-readonly": ["error"], | |||
|
35 | "semi": "off", | |||
|
36 | "@typescript-eslint/semi": ["error"], | |||
|
37 | "react/jsx-key": "off" | |||
|
38 | } | |||
|
39 | } |
@@ -10,7 +10,7 | |||||
10 | "license": "BSD-2-Clause", |
|
10 | "license": "BSD-2-Clause", | |
11 | "devDependencies": { |
|
11 | "devDependencies": { | |
12 | "@implab/core-amd": "^1.4.0", |
|
12 | "@implab/core-amd": "^1.4.0", | |
13 |
"@implab/dojo-typings": "1.0. |
|
13 | "@implab/dojo-typings": "1.0.3", | |
14 | "@types/chai": "4.1.3", |
|
14 | "@types/chai": "4.1.3", | |
15 | "@types/requirejs": "2.1.31", |
|
15 | "@types/requirejs": "2.1.31", | |
16 | "@types/tap": "15.0.7", |
|
16 | "@types/tap": "15.0.7", | |
@@ -458,9 +458,9 | |||||
458 | "dev": true |
|
458 | "dev": true | |
459 | }, |
|
459 | }, | |
460 | "node_modules/@implab/dojo-typings": { |
|
460 | "node_modules/@implab/dojo-typings": { | |
461 |
"version": "1.0. |
|
461 | "version": "1.0.3", | |
462 |
"resolved": "https://registry.npmjs.org/@implab/dojo-typings/-/dojo-typings-1.0. |
|
462 | "resolved": "https://registry.npmjs.org/@implab/dojo-typings/-/dojo-typings-1.0.3.tgz", | |
463 | "integrity": "sha512-B2kvlKJgvyIQAl/k1pVyNmtp4ADvBDCs4Lw/qfBC+Sz/ft4c7EuRXmN/+2dhWrd6A5SMjEgex5oeq6Ja2ntrig==", |
|
463 | "integrity": "sha512-oyCiuU5ay9MfvdQtZNJSeV30jKufdiLBAcq6rn360pww2hzdqvWEeoU9/New8fMzyNiaEumOlgbcS11EVIH+Jg==", | |
464 | "dev": true |
|
464 | "dev": true | |
465 | }, |
|
465 | }, | |
466 | "node_modules/@istanbuljs/load-nyc-config": { |
|
466 | "node_modules/@istanbuljs/load-nyc-config": { | |
@@ -7030,9 +7030,9 | |||||
7030 | "dev": true |
|
7030 | "dev": true | |
7031 | }, |
|
7031 | }, | |
7032 | "@implab/dojo-typings": { |
|
7032 | "@implab/dojo-typings": { | |
7033 |
"version": "1.0. |
|
7033 | "version": "1.0.3", | |
7034 |
"resolved": "https://registry.npmjs.org/@implab/dojo-typings/-/dojo-typings-1.0. |
|
7034 | "resolved": "https://registry.npmjs.org/@implab/dojo-typings/-/dojo-typings-1.0.3.tgz", | |
7035 | "integrity": "sha512-B2kvlKJgvyIQAl/k1pVyNmtp4ADvBDCs4Lw/qfBC+Sz/ft4c7EuRXmN/+2dhWrd6A5SMjEgex5oeq6Ja2ntrig==", |
|
7035 | "integrity": "sha512-oyCiuU5ay9MfvdQtZNJSeV30jKufdiLBAcq6rn360pww2hzdqvWEeoU9/New8fMzyNiaEumOlgbcS11EVIH+Jg==", | |
7036 | "dev": true |
|
7036 | "dev": true | |
7037 | }, |
|
7037 | }, | |
7038 | "@istanbuljs/load-nyc-config": { |
|
7038 | "@istanbuljs/load-nyc-config": { |
@@ -25,7 +25,7 | |||||
25 | "@types/yaml": "1.2.0", |
|
25 | "@types/yaml": "1.2.0", | |
26 | "@types/tap": "15.0.7", |
|
26 | "@types/tap": "15.0.7", | |
27 | "dojo": "1.16.0", |
|
27 | "dojo": "1.16.0", | |
28 |
"@implab/dojo-typings": "1.0. |
|
28 | "@implab/dojo-typings": "1.0.3", | |
29 | "@typescript-eslint/eslint-plugin": "^5.23.0", |
|
29 | "@typescript-eslint/eslint-plugin": "^5.23.0", | |
30 | "@typescript-eslint/parser": "^5.23.0", |
|
30 | "@typescript-eslint/parser": "^5.23.0", | |
31 | "eslint": "^8.23.0", |
|
31 | "eslint": "^8.23.0", |
@@ -3,31 +3,18 import { argumentNotEmptyString, isPromi | |||||
3 |
|
3 | |||
4 | export type LocaleProvider<T> = () => PromiseOrValue<T | { default: T }>; |
|
4 | export type LocaleProvider<T> = () => PromiseOrValue<T | { default: T }>; | |
5 |
|
5 | |||
6 | type ResolveCallback<T> = () => PromiseOrValue<T>; |
|
|||
7 |
|
||||
8 | function when<T, T2>(value: PromiseOrValue<T>, cb: (v: T) => PromiseOrValue<T2>): PromiseOrValue<T2> { |
|
6 | function when<T, T2>(value: PromiseOrValue<T>, cb: (v: T) => PromiseOrValue<T2>): PromiseOrValue<T2> { | |
9 | return isPromise(value) ? |
|
7 | return isPromise(value) ? | |
10 | value.then(cb) : |
|
8 | value.then(cb) : | |
11 | cb(value); |
|
9 | cb(value); | |
12 | } |
|
10 | } | |
13 |
|
11 | |||
14 | function isCallback<T>(v: ResolveCallback<T> | PromiseOrValue<T>): v is ResolveCallback<T> { |
|
12 | const chainObjects = <T extends object, T2 extends object>(o1: T, o2: T2) => | |
15 | return typeof v === "function"; |
|
13 | mixin(Object.create(o1) as T, o2); | |
16 | } |
|
14 | export class NlsBundle<T extends object> { | |
|
15 | private readonly _locales: MapOf<LocaleProvider<Partial<T>>>; | |||
17 |
|
16 | |||
18 | function chainObjects<T extends object>(o1: T, o2: T) { |
|
17 | private readonly _default: T; | |
19 | if (!o1) |
|
|||
20 | return o2; |
|
|||
21 | if (!o2) |
|
|||
22 | return o1; |
|
|||
23 |
|
||||
24 | return mixin(Object.create(o1) as T, o2); |
|
|||
25 | } |
|
|||
26 |
|
||||
27 | export class NlsBundle<T extends object> { |
|
|||
28 | private _locales: MapOf<LocaleProvider<Partial<T>>>; |
|
|||
29 |
|
||||
30 | private _default: T; |
|
|||
31 |
|
18 | |||
32 | private _cache: MapOf<PromiseOrValue<T>>; |
|
19 | private _cache: MapOf<PromiseOrValue<T>>; | |
33 |
|
20 | |||
@@ -65,9 +52,7 export class NlsBundle<T extends object> | |||||
65 | }); |
|
52 | }); | |
66 | } |
|
53 | } | |
67 |
|
54 | |||
68 |
_loadPackage(localeData: |
|
55 | _loadPackage(localeData: LocaleProvider<Partial<T>>) { | |
69 | if (isCallback(localeData)) |
|
56 | return when(localeData(), data => data && "default" in data ? data.default : data); | |
70 | return when(localeData(), data => data && "default" in data ? data.default : data); |
|
|||
71 | return localeData; |
|
|||
72 | } |
|
57 | } | |
73 | } |
|
58 | } |
@@ -1,8 +1,8 | |||||
1 | import inject from "./dom-inject"; |
|
1 | import inject from "./dom-inject"; | |
2 |
|
2 | |||
3 | interface OnLoad { |
|
3 | interface OnLoad { | |
4 |
(result?: |
|
4 | (result?: unknown): void; | |
5 |
error(err: |
|
5 | error(err: unknown): void; | |
6 | } |
|
6 | } | |
7 |
|
7 | |||
8 | const plugin = { |
|
8 | const plugin = { |
@@ -137,9 +137,10 export function djclass<T extends Abstra | |||||
137 |
|
137 | |||
138 | // proxy - фабрика для создания прокси-методов, которые внутри |
|
138 | // proxy - фабрика для создания прокси-методов, которые внутри | |
139 | // себя вызовут this.inherited с правильными параметрами. |
|
139 | // себя вызовут this.inherited с правильными параметрами. | |
140 |
const proxy = (m: (...args: unknown[]) => unknown) => function (this: dojo._base.DeclareCreatedObject |
|
140 | const proxy = (m: (...args: unknown[]) => unknown) => function (this: dojo._base.DeclareCreatedObject) { | |
141 |
const f = this.getInherited({ callee: m |
|
141 | const f = this.getInherited({ callee: m } as unknown as IArguments); | |
142 | return f ? f.apply(this, args) as unknown : undefined; |
|
142 | // eslint-disable-next-line prefer-rest-params | |
|
143 | return f ? f.apply(this, arguments) as unknown : undefined; | |||
143 |
|
144 | |||
144 | // так сделать можно только dojo 1.15+ |
|
145 | // так сделать можно только dojo 1.15+ | |
145 | // return this.inherited(m, arguments); |
|
146 | // return this.inherited(m, arguments); | |
@@ -153,7 +154,7 export function djclass<T extends Abstra | |||||
153 | each(target.prototype, (m: unknown, p: string) => { |
|
154 | each(target.prototype, (m: unknown, p: string) => { | |
154 | if (typeof m === "function" && |
|
155 | if (typeof m === "function" && | |
155 | p !== "constructor" && |
|
156 | p !== "constructor" && | |
156 | Object.prototype.hasOwnProperty.call(target, p) |
|
157 | Object.prototype.hasOwnProperty.call(target.prototype, p) | |
157 | ) { |
|
158 | ) { | |
158 | nbp[p] = proxy(m as (...args: unknown[]) => unknown); |
|
159 | nbp[p] = proxy(m as (...args: unknown[]) => unknown); | |
159 | } |
|
160 | } | |
@@ -224,18 +225,18 export function bind(params: NodeBindSpe | |||||
224 | target[makeSetterName(name) as K /** hack to not go insane) */] = params; |
|
225 | target[makeSetterName(name) as K /** hack to not go insane) */] = params; | |
225 | }; |
|
226 | }; | |
226 | } else { |
|
227 | } else { | |
227 | return <K extends string, |
|
228 | return <K extends string>(target: Record<K | "_set", unknown>, name: K) => { | |
228 | T extends { [p in K]: p extends "_set" ? (name: p, v: unknown) => void : unknown; }> (target: T, name: K) => { |
|
229 | target[name] = undefined; | |
229 | target[name] = undefined as T[K]; |
|
230 | ||
230 |
target[makeSetterName(name) as K] = function (this: |
|
231 | target[makeSetterName(name) as K] = function (this: typeof target, v: unknown) { | |
231 | this._set(name, v); |
|
232 | (this._set as (n: K, v: unknown) => void)(name, v); | |
232 | const inner = this[params.member] as Record<string, unknown>; |
|
233 | const inner = this[params.member as K] as Record<string, unknown>; | |
233 | if (typeof inner.set === "function") |
|
234 | if (typeof inner.set === "function") | |
234 | inner.set(params.property, v); |
|
235 | inner.set(params.property, v); | |
235 |
} |
|
236 | }; | |
236 | if (params.getter) |
|
237 | if (params.getter) | |
237 | target[makeGetterName(name)] = function () { |
|
238 | target[makeGetterName(name) as K] = function (this: typeof target) { | |
238 | const inner = this[params.member] as Record<string, unknown>; |
|
239 | const inner = this[params.member as K] as Record<string, unknown>; | |
239 | if (typeof inner.get === "function") |
|
240 | if (typeof inner.get === "function") | |
240 | return inner.get(params.property) as unknown; |
|
241 | return inner.get(params.property) as unknown; | |
241 | }; |
|
242 | }; |
@@ -6,7 +6,7 import { mixin } from "@implab/core-amd/ | |||||
6 | const trace = TraceSource.get(mid); |
|
6 | const trace = TraceSource.get(mid); | |
7 |
|
7 | |||
8 |
|
8 | |||
9 |
function on<T extends keyof HTMLElementEventMap>(node: HTMLElement, eventName: T, handler: (this: HTMLElement, ev: HTMLElementEventMap[T]) => |
|
9 | function on<T extends keyof HTMLElementEventMap>(node: HTMLElement, eventName: T, handler: (this: HTMLElement, ev: HTMLElementEventMap[T]) => unknown): () => void { | |
10 | // Add an event listener to a DOM node |
|
10 | // Add an event listener to a DOM node | |
11 | node.addEventListener(eventName, handler, false); |
|
11 | node.addEventListener(eventName, handler, false); | |
12 |
|
12 | |||
@@ -57,7 +57,7 class DomInject { | |||||
57 |
|
57 | |||
58 | async injectScript(url: string) { |
|
58 | async injectScript(url: string) { | |
59 | let d = this._map[url]; |
|
59 | let d = this._map[url]; | |
60 |
if ( |
|
60 | if (d === undefined) { | |
61 | trace.log("js {0}", url); |
|
61 | trace.log("js {0}", url); | |
62 | d = this._inject("script", { |
|
62 | d = this._inject("script", { | |
63 | type: "text/javascript", |
|
63 | type: "text/javascript", | |
@@ -77,7 +77,7 class DomInject { | |||||
77 |
|
77 | |||
78 | async injectStylesheet(url: string) { |
|
78 | async injectStylesheet(url: string) { | |
79 | let d = this._map[url]; |
|
79 | let d = this._map[url]; | |
80 |
if ( |
|
80 | if (d === undefined) { | |
81 | trace.log("js {0}", url); |
|
81 | trace.log("js {0}", url); | |
82 | d = this._inject("link", { |
|
82 | d = this._inject("link", { | |
83 | type: "text/css", |
|
83 | type: "text/css", |
@@ -1,6 +1,6 | |||||
1 | import { id as mid} from "module"; |
|
1 | import { id as mid} from "module"; | |
2 | import { MapOf } from "@implab/core-amd/interfaces"; |
|
2 | import { MapOf } from "@implab/core-amd/interfaces"; | |
3 | import { NlsBundle } from "./NlsBundle"; |
|
3 | import { LocaleProvider, NlsBundle } from "./NlsBundle"; | |
4 | import { isPromise } from "@implab/core-amd/safe"; |
|
4 | import { isPromise } from "@implab/core-amd/safe"; | |
5 | import { locale as sysLocale } from "dojo/_base/kernel"; |
|
5 | import { locale as sysLocale } from "dojo/_base/kernel"; | |
6 | import { TraceSource } from "@implab/core-amd/log/TraceSource"; |
|
6 | import { TraceSource } from "@implab/core-amd/log/TraceSource"; | |
@@ -10,11 +10,11 const trace = TraceSource.get(mid); | |||||
10 | trace.debug("Current sysLocale: {0}", sysLocale); |
|
10 | trace.debug("Current sysLocale: {0}", sysLocale); | |
11 |
|
11 | |||
12 | export interface OnLoad { |
|
12 | export interface OnLoad { | |
13 |
(result?: |
|
13 | (result?: unknown): void; | |
14 |
error(err: |
|
14 | error(err: unknown): void; | |
15 | } |
|
15 | } | |
16 |
|
16 | |||
17 |
export function bundle<T extends object>(nls: T, locales?: MapOf< |
|
17 | export function bundle<T extends object>(nls: T, locales?: MapOf<LocaleProvider<object>>) { | |
18 | const nlsBundle = new NlsBundle(nls, locales); |
|
18 | const nlsBundle = new NlsBundle(nls, locales); | |
19 |
|
19 | |||
20 | const fn = (_locale?: string) => { |
|
20 | const fn = (_locale?: string) => { | |
@@ -28,7 +28,7 export function bundle<T extends object> | |||||
28 | }; |
|
28 | }; | |
29 |
|
29 | |||
30 | fn.define = (pack: Partial<T>) => pack; |
|
30 | fn.define = (pack: Partial<T>) => pack; | |
31 |
fn.load = async (id: string, require: Require, cb: OnLoad, config: |
|
31 | fn.load = async (id: string, require: Require, cb: OnLoad, config: {isBuild?: boolean}) => { | |
32 | const locale = id || sysLocale; |
|
32 | const locale = id || sysLocale; | |
33 | if (config && config.isBuild) { |
|
33 | if (config && config.isBuild) { | |
34 | cb(); |
|
34 | cb(); |
@@ -74,17 +74,17 const sink = <T>(consumer: Partial<Obser | |||||
74 | next: next ? next.bind(consumer) : noop, |
|
74 | next: next ? next.bind(consumer) : noop, | |
75 | error: error ? error.bind(consumer) : noop, |
|
75 | error: error ? error.bind(consumer) : noop, | |
76 | complete: complete ? complete.bind(consumer) : noop |
|
76 | complete: complete ? complete.bind(consumer) : noop | |
77 | } |
|
77 | }; | |
78 | }; |
|
78 | }; | |
79 |
|
79 | |||
80 | const fuse = <T>({ next, error, complete }: Sink<T>) => { |
|
80 | const fuse = <T>({ next, error, complete }: Sink<T>) => { | |
81 | let done = false; |
|
81 | let done = false; | |
82 | return { |
|
82 | return { | |
83 | next: (value: T) => { !done && next(value) }, |
|
83 | next: (value: T) => { !done && next(value); }, | |
84 | error: (e: unknown) => { !done && (done = true, error(e)) }, |
|
84 | error: (e: unknown) => { !done && (done = true, error(e)); }, | |
85 | complete: () => { !done && (done = true, complete()) } |
|
85 | complete: () => { !done && (done = true, complete()); } | |
86 | } |
|
86 | }; | |
87 | } |
|
87 | }; | |
88 |
|
88 | |||
89 | const _observe = <T>(producer: Producer<T>): Observable<T> => ({ |
|
89 | const _observe = <T>(producer: Producer<T>): Observable<T> => ({ | |
90 | subscribe: (consumer: Partial<Observer<T>>) => ({ |
|
90 | subscribe: (consumer: Partial<Observer<T>>) => ({ |
@@ -12,7 +12,7 import djAttr = require("dojo/dom-attr") | |||||
12 | import djClass = require("dojo/dom-class"); |
|
12 | import djClass = require("dojo/dom-class"); | |
13 | import { AnimationAttrs, WatchForRendition } from "./tsx/WatchForRendition"; |
|
13 | import { AnimationAttrs, WatchForRendition } from "./tsx/WatchForRendition"; | |
14 |
|
14 | |||
15 |
export function createElement<T extends Constructor | string | ((props: |
|
15 | export function createElement<T extends Constructor | string | ((props: object) => Element)>(elementType: T, ...args: unknown[]): Rendition { | |
16 | if (typeof elementType === "string") { |
|
16 | if (typeof elementType === "string") { | |
17 | const ctx = new HtmlRendition(elementType); |
|
17 | const ctx = new HtmlRendition(elementType); | |
18 | if (args) |
|
18 | if (args) | |
@@ -26,17 +26,17 export function createElement<T extends | |||||
26 |
|
26 | |||
27 | return ctx; |
|
27 | return ctx; | |
28 | } else if (typeof elementType === "function") { |
|
28 | } else if (typeof elementType === "function") { | |
29 |
const ctx = new FunctionRendition(elementType as (props: |
|
29 | const ctx = new FunctionRendition(elementType as (props: unknown) => Element); | |
30 | if (args) |
|
30 | if (args) | |
31 | args.forEach(x => ctx.visitNext(x)); |
|
31 | args.forEach(x => ctx.visitNext(x)); | |
32 |
|
32 | |||
33 | return ctx; |
|
33 | return ctx; | |
34 | } else { |
|
34 | } else { | |
35 | throw new Error(`The element type '${elementType}' is unsupported`); |
|
35 | throw new Error(`The element type '${String(elementType)}' is unsupported`); | |
36 | } |
|
36 | } | |
37 | } |
|
37 | } | |
38 |
|
38 | |||
39 |
export interface EventDetails<T = |
|
39 | export interface EventDetails<T = unknown> { | |
40 | detail: T; |
|
40 | detail: T; | |
41 | } |
|
41 | } | |
42 |
|
42 | |||
@@ -56,7 +56,7 export interface QueryResultUpdate<T> { | |||||
56 | newIndex: number; |
|
56 | newIndex: number; | |
57 | } |
|
57 | } | |
58 |
|
58 | |||
59 |
export type DojoMouseEvent<T = |
|
59 | export type DojoMouseEvent<T = unknown> = MouseEvent & EventSelector & EventDetails<T>; | |
60 |
|
60 | |||
61 | type StatefulProps<T> = T extends Stateful<infer A> ? A : |
|
61 | type StatefulProps<T> = T extends Stateful<infer A> ? A : | |
62 | T extends _WidgetBase ? T : never; |
|
62 | T extends _WidgetBase ? T : never; | |
@@ -73,7 +73,7 type StatefulProps<T> = T extends Statef | |||||
73 | export function watch<W extends _WidgetBase, K extends keyof W>( |
|
73 | export function watch<W extends _WidgetBase, K extends keyof W>( | |
74 | target: W, |
|
74 | target: W, | |
75 | prop: K, |
|
75 | prop: K, | |
76 |
render: (model: W[K]) => |
|
76 | render: (model: W[K]) => unknown | |
77 | ): Rendition; |
|
77 | ): Rendition; | |
78 | /** |
|
78 | /** | |
79 | * Observers the property and calls render callback each change. |
|
79 | * Observers the property and calls render callback each change. | |
@@ -86,7 +86,7 export function watch<W extends _WidgetB | |||||
86 | export function watch<T extends Stateful, K extends keyof StatefulProps<T>>( |
|
86 | export function watch<T extends Stateful, K extends keyof StatefulProps<T>>( | |
87 | target: T, |
|
87 | target: T, | |
88 | prop: K, |
|
88 | prop: K, | |
89 |
render: (model: StatefulProps<T>[K]) => |
|
89 | render: (model: StatefulProps<T>[K]) => unknown | |
90 | ): Rendition; |
|
90 | ): Rendition; | |
91 | export function watch<V>(subj: Subscribable<V>, render: (model: V) => unknown): Rendition; |
|
91 | export function watch<V>(subj: Subscribable<V>, render: (model: V) => unknown): Rendition; | |
92 | export function watch( |
|
92 | export function watch( | |
@@ -98,7 +98,7 export function watch( | |||||
98 | return new WatchRendition( |
|
98 | return new WatchRendition( | |
99 | render, |
|
99 | render, | |
100 | observe(({next}) => { |
|
100 | observe(({next}) => { | |
101 |
const h = target.watch |
|
101 | const h = target.watch( | |
102 | prop, |
|
102 | prop, | |
103 | (_prop, oldValue, newValue) => oldValue !== newValue && next(newValue) |
|
103 | (_prop, oldValue, newValue) => oldValue !== newValue && next(newValue) | |
104 | ); |
|
104 | ); | |
@@ -118,7 +118,7 export const watchFor = <T>(source: T[] | |||||
118 | subject: source, |
|
118 | subject: source, | |
119 | component: render |
|
119 | component: render | |
120 | }); |
|
120 | }); | |
121 | } |
|
121 | }; | |
122 |
|
122 | |||
123 |
|
123 | |||
124 | export const prop: { |
|
124 | export const prop: { | |
@@ -132,7 +132,7 export const prop: { | |||||
132 | ); |
|
132 | ); | |
133 | next(target.get(name)); |
|
133 | next(target.get(name)); | |
134 | return () => h.remove(); |
|
134 | return () => h.remove(); | |
135 | }) |
|
135 | }); | |
136 | }; |
|
136 | }; | |
137 |
|
137 | |||
138 | export const attach = <W extends DjxWidgetBase, K extends keyof W>(target: W, name: K) => (v: W[K]) => target.set(name, v); |
|
138 | export const attach = <W extends DjxWidgetBase, K extends keyof W>(target: W, name: K) => (v: W[K]) => target.set(name, v); | |
@@ -140,7 +140,7 export const attach = <W extends DjxWidg | |||||
140 | export const bind = <K extends string, T>(attr: K, subj: Subscribable<T>) => { |
|
140 | export const bind = <K extends string, T>(attr: K, subj: Subscribable<T>) => { | |
141 | let h = { unsubscribe() { } }; |
|
141 | let h = { unsubscribe() { } }; | |
142 |
|
142 | |||
143 |
return |
|
143 | return (el: Element | { set(name: K, value: T): void; } | undefined) => { | |
144 | if (el) { |
|
144 | if (el) { | |
145 | if (isElementNode(el)) { |
|
145 | if (isElementNode(el)) { | |
146 | h = subj.subscribe({ |
|
146 | h = subj.subscribe({ | |
@@ -154,7 +154,7 export const bind = <K extends string, T | |||||
154 | } else { |
|
154 | } else { | |
155 | h.unsubscribe(); |
|
155 | h.unsubscribe(); | |
156 | } |
|
156 | } | |
157 | } |
|
157 | }; | |
158 | }; |
|
158 | }; | |
159 |
|
159 | |||
160 | export const toggleClass = (className: string, subj: Subscribable<boolean>) => { |
|
160 | export const toggleClass = (className: string, subj: Subscribable<boolean>) => { | |
@@ -168,8 +168,8 export const toggleClass = (className: s | |||||
168 | } else { |
|
168 | } else { | |
169 | h.unsubscribe(); |
|
169 | h.unsubscribe(); | |
170 | } |
|
170 | } | |
171 | } |
|
171 | }; | |
172 | } |
|
172 | }; | |
173 |
|
173 | |||
174 | export const all = <T, A extends JSX.Ref<T>[]>(...cbs: A): JSX.Ref<T> => (arg: T | undefined) => cbs.forEach(cb => cb(arg)); |
|
174 | export const all = <T, A extends JSX.Ref<T>[]>(...cbs: A): JSX.Ref<T> => (arg: T | undefined) => cbs.forEach(cb => cb(arg)); | |
175 |
|
175 | |||
@@ -185,13 +185,14 export const all = <T, A extends JSX.Ref | |||||
185 | */ |
|
185 | */ | |
186 | export const on = <E extends string>(...eventNames: E[]) => |
|
186 | export const on = <E extends string>(...eventNames: E[]) => | |
187 | <K extends string, |
|
187 | <K extends string, | |
188 |
T extends DjxWidgetBase< |
|
188 | T extends DjxWidgetBase<object, { [p in E]: EV }>, | |
189 | EV extends Event |
|
189 | EV extends Event | |
190 | >( |
|
190 | >( | |
191 | target: T, |
|
191 | target: T, | |
192 | key: K, |
|
192 | key: K, | |
|
193 | // eslint-disable-next-line @typescript-eslint/no-unused-vars | |||
193 | _descriptor: TypedPropertyDescriptor<(eventObj: EV) => void> | TypedPropertyDescriptor<() => void> |
|
194 | _descriptor: TypedPropertyDescriptor<(eventObj: EV) => void> | TypedPropertyDescriptor<() => void> | |
194 |
) |
|
195 | ) => { | |
195 | const handlers = eventNames.map(eventName => ({ eventName, handlerMethod: key })); |
|
196 | const handlers = eventNames.map(eventName => ({ eventName, handlerMethod: key })); | |
196 | target._eventHandlers = target._eventHandlers ? target._eventHandlers.concat(handlers) : handlers; |
|
197 | target._eventHandlers = target._eventHandlers ? target._eventHandlers.concat(handlers) : handlers; | |
197 | }; |
|
198 | }; |
@@ -17,7 +17,8 export interface EventArgs { | |||||
17 | composed?: boolean; |
|
17 | composed?: boolean; | |
18 | } |
|
18 | } | |
19 |
|
19 | |||
20 | export interface DjxWidgetBase<Attrs = {}, Events extends { [name in keyof Events]: Event } = {}> extends |
|
20 | // eslint-disable-next-line @typescript-eslint/no-unused-vars | |
|
21 | export interface DjxWidgetBase<Attrs = object, Events extends { [name in keyof Events]: Event } = object> extends | |||
21 | _WidgetBase<Events> { |
|
22 | _WidgetBase<Events> { | |
22 |
|
23 | |||
23 | /** This property is declared only for type inference to work, it is never assigned |
|
24 | /** This property is declared only for type inference to work, it is never assigned | |
@@ -32,7 +33,7 export interface DjxWidgetBase<Attrs = { | |||||
32 | */ |
|
33 | */ | |
33 | _eventHandlers: Array<{ |
|
34 | _eventHandlers: Array<{ | |
34 | eventName: string, |
|
35 | eventName: string, | |
35 |
handlerMethod: |
|
36 | handlerMethod: string; | |
36 | }>; |
|
37 | }>; | |
37 | } |
|
38 | } | |
38 |
|
39 | |||
@@ -43,7 +44,8 type _super = { | |||||
43 | }; |
|
44 | }; | |
44 |
|
45 | |||
45 | @djclass |
|
46 | @djclass | |
46 | export abstract class DjxWidgetBase<Attrs = {}, Events = {}> extends djbase<_super, _AttachMixin>(_WidgetBase, _AttachMixin) { |
|
47 | // eslint-disable-next-line @typescript-eslint/no-unused-vars | |
|
48 | export abstract class DjxWidgetBase<Attrs = object, Events = object> extends djbase<_super, _AttachMixin>(_WidgetBase, _AttachMixin) { | |||
47 | private readonly _scope = new Scope(); |
|
49 | private readonly _scope = new Scope(); | |
48 |
|
50 | |||
49 | buildRendering() { |
|
51 | buildRendering() { | |
@@ -68,29 +70,29 export abstract class DjxWidgetBase<Attr | |||||
68 | } |
|
70 | } | |
69 | } |
|
71 | } | |
70 |
|
72 | |||
71 |
abstract render(): Rendition |
|
73 | abstract render(): Rendition; | |
72 |
|
74 | |||
73 | private _connectEventHandlers() { |
|
75 | private _connectEventHandlers() { | |
74 | if (this._eventHandlers) |
|
76 | if (this._eventHandlers) | |
75 | this._eventHandlers.forEach(({ eventName, handlerMethod }) => { |
|
77 | this._eventHandlers.forEach(({ eventName, handlerMethod }) => { | |
76 | const handler = this[handlerMethod as keyof this]; |
|
78 | const handler = this[handlerMethod as keyof this]; | |
77 | if (typeof handler === "function") |
|
79 | if (typeof handler === "function") | |
78 | on(this.domNode, eventName, handler.bind(this)); |
|
80 | on(this.domNode, eventName, handler.bind(this) as (...args: unknown[]) => unknown); | |
79 | }); |
|
81 | }); | |
80 | } |
|
82 | } | |
81 |
|
83 | |||
82 | _processTemplateNode<T extends (Element | Node | _WidgetBase)>( |
|
84 | _processTemplateNode<T extends (Element | Node | _WidgetBase)>( | |
83 | baseNode: T, |
|
85 | baseNode: T, | |
84 |
getAttrFunc: (baseNode: T, attr: string) => |
|
86 | getAttrFunc: (baseNode: T, attr: string) => string, | |
85 | // tslint:disable-next-line: ban-types |
|
87 | // tslint:disable-next-line: ban-types | |
86 |
attachFunc: (node: T, type: string, func?: |
|
88 | attachFunc: (node: T, type: string, func?: (...args: unknown[]) => unknown) => dojo.Handle | |
87 | ): boolean { |
|
89 | ): boolean { | |
88 | if (isNode(baseNode)) { |
|
90 | if (isNode(baseNode)) { | |
89 | const w = registry.byNode(baseNode); |
|
91 | const w = registry.byNode(baseNode); | |
90 | if (w) { |
|
92 | if (w) { | |
91 | // from dijit/_WidgetsInTemplateMixin |
|
93 | // from dijit/_WidgetsInTemplateMixin | |
92 | this._processTemplateNode(w, |
|
94 | this._processTemplateNode(w, | |
93 |
(n, p) => n.get(p as |
|
95 | (n, p) => String(n.get(p as keyof typeof n)), // callback to get a property of a widget | |
94 | (widget, type, callback) => { |
|
96 | (widget, type, callback) => { | |
95 | if (!callback) |
|
97 | if (!callback) | |
96 | throw new Error("The callback must be specified"); |
|
98 | throw new Error("The callback must be specified"); | |
@@ -101,7 +103,7 export abstract class DjxWidgetBase<Attr | |||||
101 | return widget.connect(widget, type, callback as EventListener); |
|
103 | return widget.connect(widget, type, callback as EventListener); | |
102 | } else { |
|
104 | } else { | |
103 | // 1.x may never hit this branch, but it's the default for 2.0 |
|
105 | // 1.x may never hit this branch, but it's the default for 2.0 | |
104 | return widget.on(type, callback); |
|
106 | return widget.on(type as keyof GlobalEventHandlersEventMap, callback); | |
105 | } |
|
107 | } | |
106 |
|
108 | |||
107 | }); |
|
109 | }); | |
@@ -109,7 +111,8 export abstract class DjxWidgetBase<Attr | |||||
109 | return false; |
|
111 | return false; | |
110 | } |
|
112 | } | |
111 | } |
|
113 | } | |
112 | return super._processTemplateNode(baseNode, getAttrFunc, attachFunc); |
|
114 | // eslint-disable-next-line @typescript-eslint/ban-types | |
|
115 | return super._processTemplateNode(baseNode, getAttrFunc, attachFunc as (node: T, type: string, func?: Function) => dojo.Handle); | |||
113 | } |
|
116 | } | |
114 |
|
117 | |||
115 | /** Starts current widget and all its supporting widgets (placed outside |
|
118 | /** Starts current widget and all its supporting widgets (placed outside |
@@ -3,19 +3,19 import { getItemDom } from "./render"; | |||||
3 | import { RenditionBase } from "./RenditionBase"; |
|
3 | import { RenditionBase } from "./RenditionBase"; | |
4 |
|
4 | |||
5 | export class FunctionRendition extends RenditionBase<Node> { |
|
5 | export class FunctionRendition extends RenditionBase<Node> { | |
6 |
private _component: (...args: |
|
6 | private readonly _component: (...args: unknown[]) => unknown; | |
7 |
|
7 | |||
8 | private _node: Node | undefined; |
|
8 | private _node: Node | undefined; | |
9 |
|
9 | |||
10 |
constructor(component: (...args: |
|
10 | constructor(component: (...args: unknown[]) => unknown) { | |
11 | super(); |
|
11 | super(); | |
12 | argumentNotNull(component, "component"); |
|
12 | argumentNotNull(component, "component"); | |
13 |
|
13 | |||
14 | this._component = component; |
|
14 | this._component = component; | |
15 | } |
|
15 | } | |
16 |
|
16 | |||
17 |
protected _create(attrs: object, children: |
|
17 | protected _create(attrs: object, children: unknown[]) { | |
18 |
const _attrs |
|
18 | const _attrs = attrs || {}; | |
19 | const _children = children.map(x => getItemDom(x)); |
|
19 | const _children = children.map(x => getItemDom(x)); | |
20 | this._node = getItemDom( |
|
20 | this._node = getItemDom( | |
21 | this._component.call(null, { ..._attrs, children: _children })); |
|
21 | this._component.call(null, { ..._attrs, children: _children })); |
@@ -3,11 +3,11 import { isPlainObject, DojoNodePosition | |||||
3 | export abstract class RenditionBase<TNode extends Node> implements Rendition<TNode> { |
|
3 | export abstract class RenditionBase<TNode extends Node> implements Rendition<TNode> { | |
4 | private _attrs = {}; |
|
4 | private _attrs = {}; | |
5 |
|
5 | |||
6 |
private _children = |
|
6 | private _children: unknown[] = []; | |
7 |
|
7 | |||
8 |
private _created |
|
8 | private _created = false; | |
9 |
|
9 | |||
10 |
visitNext(v: |
|
10 | visitNext(v: unknown) { | |
11 | if (this._created) |
|
11 | if (this._created) | |
12 | throw new Error("The Element is already created"); |
|
12 | throw new Error("The Element is already created"); | |
13 |
|
13 |
@@ -30,7 +30,7 export class Scope implements IDestroyab | |||||
30 | } catch { |
|
30 | } catch { | |
31 | // guard |
|
31 | // guard | |
32 | } |
|
32 | } | |
33 | } |
|
33 | }; | |
34 |
|
34 | |||
35 | this._cleanup.forEach(guard); |
|
35 | this._cleanup.forEach(guard); | |
36 | this._cleanup.length = 0; |
|
36 | this._cleanup.length = 0; |
@@ -49,10 +49,10 export interface WatchForRenditionAttrs< | |||||
49 | component: (arg: T, index: number) => unknown; |
|
49 | component: (arg: T, index: number) => unknown; | |
50 | } |
|
50 | } | |
51 |
|
51 | |||
52 |
const isObservable = <T>(v: |
|
52 | const isObservable = <T>(v: ArrayLike<T>): v is ArrayLike<T> & ObservableResults<T> => | |
53 |
v && (typeof (v as |
|
53 | v && (typeof (v as { observe?: unknown; }).observe === "function"); | |
54 |
|
54 | |||
55 | const noop = () => {}; |
|
55 | const noop = () => { }; | |
56 |
|
56 | |||
57 | const fadeIn = (nodes: Node[]) => Promise.all(nodes |
|
57 | const fadeIn = (nodes: Node[]) => Promise.all(nodes | |
58 | .filter(isElementNode) |
|
58 | .filter(isElementNode) | |
@@ -112,7 +112,7 export class WatchForRendition<T> extend | |||||
112 | if (isSubsribable<QueryResultUpdate<T>>(result)) { |
|
112 | if (isSubsribable<QueryResultUpdate<T>>(result)) { | |
113 | let animate = false; |
|
113 | let animate = false; | |
114 | const subscription = result.subscribe({ |
|
114 | const subscription = result.subscribe({ | |
115 | next: ({item, prevIndex, newIndex}) => this._onItemUpdated({ item, prevIndex, newIndex, animate }) |
|
115 | next: ({ item, prevIndex, newIndex }) => this._onItemUpdated({ item, prevIndex, newIndex, animate }) | |
116 | }); |
|
116 | }); | |
117 | scope.own(subscription); |
|
117 | scope.own(subscription); | |
118 | animate = this._animate; |
|
118 | animate = this._animate; | |
@@ -127,7 +127,7 export class WatchForRendition<T> extend | |||||
127 | this._ct = new Cancellation(cancel => scope.own(cancel)); |
|
127 | this._ct = new Cancellation(cancel => scope.own(cancel)); | |
128 | } |
|
128 | } | |
129 |
|
129 | |||
130 | private _onItemUpdated = (item: RenderTask<T>) => { |
|
130 | private readonly _onItemUpdated = (item: RenderTask<T>) => { | |
131 | if (!this._renderTasks.length) { |
|
131 | if (!this._renderTasks.length) { | |
132 | // schedule a new job |
|
132 | // schedule a new job | |
133 | this._renderTasks.push(item); |
|
133 | this._renderTasks.push(item); | |
@@ -136,7 +136,7 export class WatchForRendition<T> extend | |||||
136 | // update existing job |
|
136 | // update existing job | |
137 | this._renderTasks.push(item); |
|
137 | this._renderTasks.push(item); | |
138 | } |
|
138 | } | |
139 | } |
|
139 | }; | |
140 |
|
140 | |||
141 | private async _render() { |
|
141 | private async _render() { | |
142 | // fork |
|
142 | // fork | |
@@ -149,7 +149,7 export class WatchForRendition<T> extend | |||||
149 | this._renderTasks.length = 0; |
|
149 | this._renderTasks.length = 0; | |
150 | } |
|
150 | } | |
151 |
|
151 | |||
152 | _onRenderItem = ({ item, newIndex, prevIndex, animate: _animate }: RenderTask<T>) => { |
|
152 | private readonly _onRenderItem = ({ item, newIndex, prevIndex, animate: _animate }: RenderTask<T>) => { | |
153 | const animate = _animate && prevIndex !== newIndex; |
|
153 | const animate = _animate && prevIndex !== newIndex; | |
154 |
|
154 | |||
155 | if (prevIndex > -1) { |
|
155 | if (prevIndex > -1) { | |
@@ -205,7 +205,7 export class WatchForRendition<T> extend | |||||
205 | if (animate) |
|
205 | if (animate) | |
206 | this._animateIn(nodes).catch(e => trace.error(e)); |
|
206 | this._animateIn(nodes).catch(e => trace.error(e)); | |
207 | } |
|
207 | } | |
208 | } |
|
208 | }; | |
209 |
|
209 | |||
210 | protected _getDomNode() { |
|
210 | protected _getDomNode() { | |
211 | if (!this._node) |
|
211 | if (!this._node) |
@@ -17,7 +17,7 export interface _Widget { | |||||
17 | addChild?(widget: unknown, index?: number): void; |
|
17 | addChild?(widget: unknown, index?: number): void; | |
18 | } |
|
18 | } | |
19 |
|
19 | |||
20 |
export type _WidgetCtor = new (attrs: |
|
20 | export type _WidgetCtor = new (attrs: object, 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; |
@@ -14,11 +14,11 interface Context { | |||||
14 |
|
14 | |||
15 | let _context: Context = { |
|
15 | let _context: Context = { | |
16 | scope: Scope.dummy |
|
16 | scope: Scope.dummy | |
17 | } |
|
17 | }; | |
18 |
|
18 | |||
19 | const guard = (cb: () => unknown) => { |
|
19 | const guard = (cb: () => unknown) => { | |
20 | try { |
|
20 | try { | |
21 | const result = cb() |
|
21 | const result = cb(); | |
22 | if (isPromise(result)) { |
|
22 | if (isPromise(result)) { | |
23 | const warn = (ret: unknown) => trace.error("The callback {0} competed asynchronously. result = {1}", cb, ret); |
|
23 | const warn = (ret: unknown) => trace.error("The callback {0} competed asynchronously. result = {1}", cb, ret); | |
24 | result.then(warn, warn); |
|
24 | result.then(warn, warn); | |
@@ -26,7 +26,7 const guard = (cb: () => unknown) => { | |||||
26 | } catch (e) { |
|
26 | } catch (e) { | |
27 | trace.error(e); |
|
27 | trace.error(e); | |
28 | } |
|
28 | } | |
29 | } |
|
29 | }; | |
30 |
|
30 | |||
31 | export const beginRender = (scope: IScope = getScope()) => { |
|
31 | export const beginRender = (scope: IScope = getScope()) => { | |
32 | const prev = _context; |
|
32 | const prev = _context; | |
@@ -35,7 +35,7 export const beginRender = (scope: IScop | |||||
35 | hooks: [] |
|
35 | hooks: [] | |
36 | }; |
|
36 | }; | |
37 | return endRender(prev); |
|
37 | return endRender(prev); | |
38 | } |
|
38 | }; | |
39 |
|
39 | |||
40 |
|
|
40 | /** | |
41 | * Completes render operation |
|
41 | * Completes render operation | |
@@ -46,7 +46,7 const endRender = (prev: Context) => () | |||||
46 | hooks.forEach(guard); |
|
46 | hooks.forEach(guard); | |
47 |
|
47 | |||
48 | _context = prev; |
|
48 | _context = prev; | |
49 | } |
|
49 | }; | |
50 |
|
50 | |||
51 | export const renderHook = (hook: () => void) => { |
|
51 | export const renderHook = (hook: () => void) => { | |
52 | const { hooks } = _context; |
|
52 | const { hooks } = _context; | |
@@ -54,7 +54,7 export const renderHook = (hook: () => v | |||||
54 | hooks.push(hook); |
|
54 | hooks.push(hook); | |
55 | else |
|
55 | else | |
56 | guard(hook); |
|
56 | guard(hook); | |
57 | } |
|
57 | }; | |
58 |
|
58 | |||
59 | export const refHook = <T>(value: T, ref: JSX.Ref<T>) => { |
|
59 | export const refHook = <T>(value: T, ref: JSX.Ref<T>) => { | |
60 | const { hooks, scope } = _context; |
|
60 | const { hooks, scope } = _context; | |
@@ -64,7 +64,7 export const refHook = <T>(value: T, ref | |||||
64 | guard(() => ref(value)); |
|
64 | guard(() => ref(value)); | |
65 |
|
65 | |||
66 | scope.own(() => ref(undefined)); |
|
66 | scope.own(() => ref(undefined)); | |
67 | } |
|
67 | }; | |
68 |
|
68 | |||
69 |
|
|
69 | /** Returns the current scope */ | |
70 | export const getScope = () => _context.scope; |
|
70 | export const getScope = () => _context.scope; | |
@@ -80,7 +80,7 export const render = (rendition: unknow | |||||
80 | } finally { |
|
80 | } finally { | |
81 | complete(); |
|
81 | complete(); | |
82 | } |
|
82 | } | |
83 | } |
|
83 | }; | |
84 |
|
84 | |||
85 |
|
|
85 | /** Renders DOM element for different types of the argument. */ | |
86 | export const getItemDom = (v: unknown) => { |
|
86 | export const getItemDom = (v: unknown) => { | |
@@ -107,6 +107,6 export const getItemDom = (v: unknown) = | |||||
107 | return fragment; |
|
107 | return fragment; | |
108 | } else { |
|
108 | } else { | |
109 | // bug: explicit error otherwise |
|
109 | // bug: explicit error otherwise | |
110 |
throw new Error( |
|
110 | throw new Error(`Invalid parameter: ${String(v)}`); | |
111 | } |
|
111 | } | |
112 | } |
|
112 | }; |
@@ -1,81 +1,89 | |||||
|
1 | // eslint-disable-next-line @typescript-eslint/triple-slash-reference | |||
1 | /// <reference path="./css-plugin.d.ts"/> |
|
2 | /// <reference path="./css-plugin.d.ts"/> | |
2 |
|
3 | |||
3 | declare namespace JSX { |
|
4 | import { Rendition } from "./tsx/traits"; | |
4 |
|
||||
5 | type Ref<T> = ((value: T | undefined) => void); |
|
|||
6 |
|
5 | |||
7 | interface DjxIntrinsicAttributes<E> { |
|
6 | declare global { | |
8 | /** alias for className */ |
|
7 | namespace JSX { | |
9 | class: string; |
|
8 | ||
|
9 | type Ref<T> = ((value: T | undefined) => void); | |||
|
10 | ||||
|
11 | type Element = Rendition; | |||
10 |
|
12 | |||
11 | /** specifies the name of the property in the widget where the the |
|
13 | interface DjxIntrinsicAttributes<E> { | |
12 | * reference to the current object will be stored |
|
14 | /** alias for className */ | |
13 | */ |
|
15 | class?: string; | |
14 | "data-dojo-attach-point": string; |
|
|||
15 |
|
16 | |||
16 | /** specifies handlers map for the events */ |
|
17 | /** specifies the name of the property in the widget where the the | |
17 | "data-dojo-attach-event": string; |
|
18 | * reference to the current object will be stored | |
|
19 | */ | |||
|
20 | "data-dojo-attach-point"?: string; | |||
18 |
|
21 | |||
19 | ref: Ref<E>; |
|
22 | /** specifies handlers map for the events */ | |
|
23 | "data-dojo-attach-event"?: string; | |||
20 |
|
24 | |||
21 | /** @deprecated */ |
|
25 | ref?: Ref<E>; | |
22 | [attr: string]: any; |
|
|||
23 | } |
|
|||
24 |
|
26 | |||
25 | interface DjxIntrinsicElements { |
|
27 | /** @deprecated */ | |
26 | } |
|
28 | [attr: string]: unknown; | |
|
29 | } | |||
27 |
|
30 | |||
28 | type RecursivePartial<T> = T extends string | number | boolean | null | undefined | Function ? |
|
31 | // eslint-disable-next-line @typescript-eslint/no-empty-interface | |
29 | T : |
|
32 | interface DjxIntrinsicElements { | |
30 | { [k in keyof T]?: RecursivePartial<T[k]> }; |
|
33 | } | |
|
34 | ||||
|
35 | type RecursivePartial<T> = T extends string | number | boolean | null | undefined | ((...args: unknown[]) => unknown) ? | |||
|
36 | T : | |||
|
37 | { [k in keyof T]?: RecursivePartial<T[k]> }; | |||
31 |
|
38 | |||
32 | type MatchingMemberKeys<T, U> = { |
|
39 | type MatchingMemberKeys<T, U> = { | |
33 | [K in keyof T]: T[K] extends U ? K : never; |
|
40 | [K in keyof T]: T[K] extends U ? K : never; | |
34 | }[keyof T]; |
|
41 | }[keyof T]; | |
35 | type NotMatchingMemberKeys<T, U> = { |
|
42 | type NotMatchingMemberKeys<T, U> = { | |
36 | [K in keyof T]: T[K] extends U ? never : K; |
|
43 | [K in keyof T]: T[K] extends U ? never : K; | |
37 | }[keyof T]; |
|
44 | }[keyof T]; | |
38 |
|
45 | |||
39 | type ExtractMembers<T, U> = Pick<T, MatchingMemberKeys<T, U>>; |
|
46 | type ExtractMembers<T, U> = Pick<T, MatchingMemberKeys<T, U>>; | |
40 |
|
47 | |||
41 | type ExcludeMembers<T, U> = Pick<T, NotMatchingMemberKeys<T, U>>; |
|
48 | type ExcludeMembers<T, U> = Pick<T, NotMatchingMemberKeys<T, U>>; | |
42 |
|
49 | |||
43 |
type ElementAttrNames<E> = NotMatchingMemberKeys<E, (...args: |
|
50 | type ElementAttrNames<E> = NotMatchingMemberKeys<E, (...args: unknown[]) => unknown>; | |
44 |
|
51 | |||
45 |
type ElementAttrType<E, K extends |
|
52 | type ElementAttrType<E, K extends string | symbol> = K extends keyof E ? RecursivePartial<E[K]> : string; | |
46 |
|
53 | |||
47 |
|
54 | |||
48 | type ElementAttrNamesBlacklist = "children" | "getRootNode" | keyof EventTarget; |
|
55 | type ElementAttrNamesBlacklist = "children" | "getRootNode" | keyof EventTarget; | |
49 |
|
56 | |||
50 | /** This type extracts keys of the specified parameter E by the following rule: |
|
57 | /** This type extracts keys of the specified parameter E by the following rule: | |
51 | * 1. skips all ElementAttrNamesBlacklist |
|
58 | * 1. skips all ElementAttrNamesBlacklist | |
52 | * 2. skips all methods except with the signature of event handlers |
|
59 | * 2. skips all methods except with the signature of event handlers | |
53 | */ |
|
60 | */ | |
54 | type AssignableElementAttrNames<E> = { |
|
61 | type AssignableElementAttrNames<E> = { | |
55 | [K in keyof E]: K extends ElementAttrNamesBlacklist ? never : |
|
62 | [K in keyof E]: K extends ElementAttrNamesBlacklist ? never : | |
56 |
((evt: Event) => |
|
63 | ((evt: Event) => unknown) extends E[K] ? K : | |
57 |
E[K] extends ((...args: |
|
64 | E[K] extends ((...args: unknown[]) => unknown) ? never : | |
58 | K; |
|
65 | K; | |
59 | }[keyof E]; |
|
66 | }[keyof E]; | |
60 |
|
67 | |||
61 | type LaxElement<E extends object> = |
|
68 | type LaxElement<E extends object> = | |
62 | Pick<E, AssignableElementAttrNames<E>> & |
|
69 | RecursivePartial<Pick<E, AssignableElementAttrNames<E>>> & | |
63 | DjxIntrinsicAttributes<E>; |
|
70 | DjxIntrinsicAttributes<E>; | |
64 |
|
71 | |||
65 | type LaxIntrinsicElementsMap = { |
|
72 | type LaxIntrinsicElementsMap = { | |
66 | [tag in keyof HTMLElementTagNameMap]: LaxElement<HTMLElementTagNameMap[tag]> |
|
73 | [tag in keyof HTMLElementTagNameMap]: LaxElement<HTMLElementTagNameMap[tag]> | |
67 | } & DjxIntrinsicElements; |
|
74 | } & DjxIntrinsicElements; | |
68 |
|
75 | |||
69 | type IntrinsicElements = { |
|
76 | type IntrinsicElements = { | |
70 |
[tag in keyof LaxIntrinsicElementsMap]: |
|
77 | [tag in keyof LaxIntrinsicElementsMap]: LaxIntrinsicElementsMap[tag]; | |
71 | } |
|
78 | }; | |
72 |
|
79 | |||
73 | interface ElementChildrenAttribute { |
|
80 | interface ElementChildrenAttribute { | |
74 |
children: |
|
81 | children: unknown; | |
75 | } |
|
82 | } | |
76 |
|
83 | |||
77 | interface IntrinsicClassAttributes<T> { |
|
84 | interface IntrinsicClassAttributes<T> { | |
78 |
ref? |
|
85 | ref?: Ref<T>; | |
79 | children?: unknown; |
|
86 | children?: unknown; | |
|
87 | } | |||
80 | } |
|
88 | } | |
81 |
} |
|
89 | } No newline at end of file |
@@ -2,6 +2,7 import { djbase, djclass, bind, prototyp | |||||
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 | import { argumentNotNull } from "@implab/core-amd/safe"; | |||
5 |
|
6 | |||
6 | interface MyWidgetAttrs { |
|
7 | interface MyWidgetAttrs { | |
7 | title: string; |
|
8 | title: string; | |
@@ -19,6 +20,12 interface MyWidgetEvents { | |||||
19 | }; |
|
20 | }; | |
20 | } |
|
21 | } | |
21 |
|
22 | |||
|
23 | interface FrameProps { | |||
|
24 | ref?: JSX.Ref<HTMLDivElement>; | |||
|
25 | children?: unknown[]; | |||
|
26 | } | |||
|
27 | ||||
|
28 | const Frame = ({children, ref}: FrameProps) => <div ref={ref} >{children}</div>; | |||
22 |
|
29 | |||
23 | @djclass |
|
30 | @djclass | |
24 | export class MyWidget extends djbase(DjxWidgetBase as AbstractConstructor<DjxWidgetBase<MyWidgetAttrs, MyWidgetEvents>>) { |
|
31 | export class MyWidget extends djbase(DjxWidgetBase as AbstractConstructor<DjxWidgetBase<MyWidgetAttrs, MyWidgetEvents>>) { | |
@@ -29,31 +36,40 export class MyWidget extends djbase(Djx | |||||
29 | @prototype(0) |
|
36 | @prototype(0) | |
30 | counter = 0; |
|
37 | counter = 0; | |
31 |
|
38 | |||
|
39 | frameNode?: HTMLDivElement; | |||
|
40 | ||||
32 | render() { |
|
41 | render() { | |
33 | const Frame = ({children, ref}: {ref: JSX.Ref<HTMLDivElement>, children: unknown[]}) => <div ref={ref} >{children}</div>; |
|
42 | ||
34 |
|
|
43 | return <div className="myWidget" onsubmit={this._onSubmit} tabIndex={3} style={{ alignContent: "center", border: "1px solid" }} > | |
35 | <h1 data-dojo-attach-point="titleNode"></h1> |
|
44 | <h1 data-dojo-attach-point="titleNode"></h1> | |
36 |
<Frame ref={ |
|
45 | <Frame ref={this._setFrameElement}> | |
37 |
<span class="up-button" onclick={ |
|
46 | <span class="up-button" onclick={this._onIncClick}>[+]</span> | |
38 | <span class="down-button" onclick={() => this._onDecClick()}>[-]</span> |
|
47 | <span class="down-button" onclick={() => this._onDecClick()}>[-]</span> | |
39 | </Frame> |
|
48 | </Frame> | |
40 | </div>; |
|
49 | </div>; | |
41 | } |
|
50 | } | |
42 |
|
51 | |||
|
52 | private readonly _setFrameElement = (node?: HTMLDivElement) => { | |||
|
53 | this.frameNode = node; | |||
|
54 | }; | |||
|
55 | ||||
43 | postCreate() { |
|
56 | postCreate() { | |
44 | super.postCreate(); |
|
57 | super.postCreate(); | |
45 |
|
58 | |||
46 | this.on("click", () => {}); |
|
59 | this.on("click", () => {}); | |
47 | } |
|
60 | } | |
48 |
|
61 | |||
49 | _onSubmit(e: Event) { |
|
62 | private readonly _onSubmit = (evt: Event) => { | |
50 | } |
|
63 | argumentNotNull(evt, "evt"); | |
|
64 | }; | |||
51 |
|
65 | |||
52 | _onIncClick(e: MouseEvent) { |
|
66 | private readonly _onIncClick = (evt: MouseEvent) => { | |
|
67 | argumentNotNull(evt, "evt"); | |||
|
68 | ||||
53 | this.set("counter", this.counter + 1); |
|
69 | this.set("counter", this.counter + 1); | |
54 |
|
70 | |||
55 | this.emit("count-inc", { bubbles: false }); |
|
71 | this.emit("count-inc", { bubbles: false }); | |
56 | } |
|
72 | }; | |
57 |
|
73 | |||
58 | _onDecClick() { |
|
74 | _onDecClick() { | |
59 | this.emit("count-dec", { bubbles: false, detail: this.counter }); |
|
75 | this.emit("count-dec", { bubbles: false, detail: this.counter }); | |
@@ -61,10 +77,11 export class MyWidget extends djbase(Djx | |||||
61 |
|
77 | |||
62 | @on("count-inc") |
|
78 | @on("count-inc") | |
63 | private _onCounterInc(evt: Event & { detail: number; x?: number; }) { |
|
79 | private _onCounterInc(evt: Event & { detail: number; x?: number; }) { | |
|
80 | argumentNotNull(evt, "evt"); | |||
64 | } |
|
81 | } | |
65 |
|
82 | |||
66 | @on("click", "keydown") |
|
83 | @on("click", "keydown") | |
67 |
protected _onClick(ev |
|
84 | protected _onClick(evt: MouseEvent | KeyboardEvent) { | |
68 |
|
85 | argumentNotNull(evt, "evt"); | ||
69 | } |
|
86 | } | |
70 | } No newline at end of file |
|
87 | } |
@@ -1,6 +1,7 | |||||
1 | { |
|
1 | { | |
2 | "extends": "../tsconfig", |
|
2 | "extends": "../tsconfig", | |
3 | "compilerOptions": { |
|
3 | "compilerOptions": { | |
|
4 | "baseUrl": ".", | |||
4 | "noUnusedLocals": false, |
|
5 | "noUnusedLocals": false, | |
5 | //"rootDir": "ts", |
|
6 | //"rootDir": "ts", | |
6 | "rootDirs": [ |
|
7 | "rootDirs": [ | |
@@ -9,6 +10,11 | |||||
9 | "../main/ts", |
|
10 | "../main/ts", | |
10 | "../main/typings" |
|
11 | "../main/typings" | |
11 | ], |
|
12 | ], | |
12 | "types": ["requirejs", "../main/typings", "@implab/dojo-typings"] |
|
13 | "types": [ | |
|
14 | "requirejs", | |||
|
15 | "../main/typings", | |||
|
16 | "@implab/dojo-typings", | |||
|
17 | "@implab/dojo-typings/dojo/NodeList-fx" | |||
|
18 | ] | |||
13 | } |
|
19 | } | |
14 | } No newline at end of file |
|
20 | } |
@@ -1,10 +1,9 | |||||
1 | { |
|
1 | { | |
2 | "extends": "./tsconfig.json", |
|
2 | //"extends": "./tsconfig.json", | |
3 | "compilerOptions": { |
|
3 | "compilerOptions": { | |
4 | // ensure that nobody can accidentally use this config for a build |
|
4 | // ensure that nobody can accidentally use this config for a build | |
5 | "noEmit": true |
|
5 | "noEmit": true, | |
6 | }, |
|
6 | }, | |
7 | "include": [ |
|
7 | "include": [ | |
8 | "ts" |
|
|||
9 | ] |
|
8 | ] | |
10 | } No newline at end of file |
|
9 | } |
@@ -1,7 +1,7 | |||||
1 | import MainWidget from "./view/MainWidget"; |
|
1 | import MainWidget from "./view/MainWidget"; | |
2 | import "@implab/djx/css!dojo/resources/dojo.css" |
|
2 | import "@implab/djx/css!dojo/resources/dojo.css"; | |
3 | import "@implab/djx/css!dijit/themes/dijit.css" |
|
3 | import "@implab/djx/css!dijit/themes/dijit.css"; | |
4 | import "@implab/djx/css!dijit/themes/tundra/tundra.css" |
|
4 | import "@implab/djx/css!dijit/themes/tundra/tundra.css"; | |
5 |
|
5 | |||
6 | const w = new MainWidget(); |
|
6 | const w = new MainWidget(); | |
7 | w.placeAt(document.body); No newline at end of file |
|
7 | w.placeAt(document.body); |
@@ -5,6 +5,7 import { Contact } from "./Contact"; | |||||
5 | import { Uuid } from "@implab/core-amd/Uuid"; |
|
5 | import { Uuid } from "@implab/core-amd/Uuid"; | |
6 | import { Observable as RxjsObservable } from "rxjs"; |
|
6 | import { Observable as RxjsObservable } from "rxjs"; | |
7 | import { QueryResultUpdate } from "@implab/djx/tsx"; |
|
7 | import { QueryResultUpdate } from "@implab/djx/tsx"; | |
|
8 | import {isPromise} from "@implab/core-amd/safe"; | |||
8 |
|
9 | |||
9 | type AppointmentRecord = Omit<Appointment, "getMembers"> & {id: string}; |
|
10 | type AppointmentRecord = Omit<Appointment, "getMembers"> & {id: string}; | |
10 |
|
11 | |||
@@ -22,13 +23,13 export interface ObservableResults<T> { | |||||
22 | } |
|
23 | } | |
23 |
|
24 | |||
24 |
|
25 | |||
25 |
export function isObservable<T>(v: |
|
26 | export function isObservable<T>(v: unknown): v is ObservableResults<T> { | |
26 |
return v && (typeof (v as |
|
27 | return !!v && (typeof (v as {observe?: unknown}).observe === "function"); | |
27 | } |
|
28 | } | |
28 |
|
29 | |||
29 | export function observe<T>(results: T[], includeObjectUpdates?: boolean): RxjsObservable<QueryResultUpdate<T>>; |
|
30 | export function observe<T>(results: T[], includeObjectUpdates?: boolean): RxjsObservable<QueryResultUpdate<T>>; | |
30 | export function observe<T>(results: PromiseLike<T[]>, includeObjectUpdates?: boolean): PromiseLike<RxjsObservable<QueryResultUpdate<T>>>; |
|
31 | export function observe<T>(results: PromiseLike<T[]>, includeObjectUpdates?: boolean): PromiseLike<RxjsObservable<QueryResultUpdate<T>>>; | |
31 |
export function observe(results: |
|
32 | export function observe(results: unknown[] | PromiseLike<unknown[]>, includeObjectUpdates = true) { | |
32 | // results может быть асинхронным, т.е. до завершения |
|
33 | // results может быть асинхронным, т.е. до завершения | |
33 | // получения результатов store может быть обновлен. В любом |
|
34 | // получения результатов store может быть обновлен. В любом | |
34 | // случае, если между подключением хотя бы одного наблюдателя |
|
35 | // случае, если между подключением хотя бы одного наблюдателя | |
@@ -41,7 +42,7 export function observe(results: any, in | |||||
41 | // о необработанной ошибке в Promise при обращении к методам |
|
42 | // о необработанной ошибке в Promise при обращении к методам | |
42 | // обновления (add,put,remove) |
|
43 | // обновления (add,put,remove) | |
43 |
|
44 | |||
44 |
const _subscribe = (items: |
|
45 | const _subscribe = (items: unknown[]) => new RxjsObservable<QueryResultUpdate<unknown>>(subscriber => { | |
45 | items |
|
46 | items | |
46 | .forEach((value, newIndex) => subscriber.next({ item: value, newIndex, prevIndex: -1})); |
|
47 | .forEach((value, newIndex) => subscriber.next({ item: value, newIndex, prevIndex: -1})); | |
47 |
|
48 | |||
@@ -72,11 +73,11 export function observe(results: any, in | |||||
72 |
|
73 | |||
73 |
|
74 | |||
74 | export class MainContext { |
|
75 | export class MainContext { | |
75 | private _appointments = new Observable(new Memory<AppointmentRecord>()); |
|
76 | private readonly _appointments = new Observable(new Memory<AppointmentRecord>()); | |
76 |
|
77 | |||
77 | private _contacts = new Observable(new Memory<ContactRecord>()); |
|
78 | private readonly _contacts = new Observable(new Memory<ContactRecord>()); | |
78 |
|
79 | |||
79 | private _members = new Observable(new Memory<MemberRecord>()); |
|
80 | private readonly _members = new Observable(new Memory<MemberRecord>()); | |
80 |
|
81 | |||
81 | createAppointment(title: string, startAt: Date, duration: number, members: Member[]) { |
|
82 | createAppointment(title: string, startAt: Date, duration: number, members: Member[]) { | |
82 | const id = Uuid(); |
|
83 | const id = Uuid(); | |
@@ -91,16 +92,16 export class MainContext { | |||||
91 | this._members.add({ |
|
92 | this._members.add({ | |
92 | appointmentId: id, |
|
93 | appointmentId: id, | |
93 | ...member |
|
94 | ...member | |
94 | }, {id: Uuid()}) |
|
95 | }, {id: Uuid()}) as void | |
95 | ); |
|
96 | ); | |
96 | } |
|
97 | } | |
97 |
|
98 | |||
98 | queryAppointments(dateFrom: Date, dateTo: Date) { |
|
99 | queryAppointments(dateFrom: Date, dateTo: Date) { | |
99 | this._appointments.query().map() |
|
100 | //this._appointments.query().map() | |
100 | } |
|
101 | } | |
101 |
|
102 | |||
102 | private _mapAppointment = ({startAt, title, duration, id}: AppointmentRecord) => ({ |
|
103 | private readonly _mapAppointment = ({startAt, title, duration, id}: AppointmentRecord) => ({ | |
103 |
|
104 | |||
104 | }) |
|
105 | }); | |
105 |
|
106 | |||
106 | } |
|
107 | } |
@@ -5,6 +5,8 import ProgressBar from "./ProgressBar"; | |||||
5 | import Button = require("dijit/form/Button"); |
|
5 | import Button = require("dijit/form/Button"); | |
6 | import { interval } from "rxjs"; |
|
6 | import { interval } from "rxjs"; | |
7 |
|
7 | |||
|
8 | const Counter = ({ children }: { children: unknown[] }) => <span>Counter: {children}</span>; | |||
|
9 | ||||
8 | @djclass |
|
10 | @djclass | |
9 | export default class MainWidget extends djbase(DjxWidgetBase) { |
|
11 | export default class MainWidget extends djbase(DjxWidgetBase) { | |
10 |
|
12 | |||
@@ -21,7 +23,6 export default class MainWidget extends | |||||
21 | paused = false; |
|
23 | paused = false; | |
22 |
|
24 | |||
23 | render() { |
|
25 | render() { | |
24 | const Counter = ({ children }: { children: unknown[] }) => <span>Counter: {children}</span>; |
|
|||
25 |
|
|
26 | ||
26 | return <div className="tundra"> |
|
27 | return <div className="tundra"> | |
27 | <h2 ref={attach(this, "titleNode")}>Hi!</h2> |
|
28 | <h2 ref={attach(this, "titleNode")}>Hi!</h2> | |
@@ -31,7 +32,6 export default class MainWidget extends | |||||
31 | <Counter><input ref={all( |
|
32 | <Counter><input ref={all( | |
32 | bind("value", prop(this, "count") |
|
33 | bind("value", prop(this, "count") | |
33 | .map(x => x/10) |
|
34 | .map(x => x/10) | |
34 | .map(String) |
|
|||
35 | ), |
|
35 | ), | |
36 | attach(this, "counterNode") |
|
36 | attach(this, "counterNode") | |
37 | )} /> <span>s</span></Counter>, |
|
37 | )} /> <span>s</span></Counter>, | |
@@ -71,11 +71,11 export default class MainWidget extends | |||||
71 | }); |
|
71 | }); | |
72 | } |
|
72 | } | |
73 |
|
73 | |||
74 | private _onPauseClick = () => { |
|
74 | private readonly _onPauseClick = () => { | |
75 | this.set("paused", !this.paused); |
|
75 | this.set("paused", !this.paused); | |
76 | } |
|
76 | }; | |
77 |
|
77 | |||
78 | private _onToggleCounterClick = () => { |
|
78 | private readonly _onToggleCounterClick = () => { | |
79 | this.set("showCounter", !this.showCounter); |
|
79 | this.set("showCounter", !this.showCounter); | |
80 | } |
|
80 | }; | |
81 | } |
|
81 | } |
@@ -0,0 +1,1 | |||||
|
1 | /* noop */ No newline at end of file |
@@ -5,6 +5,5 | |||||
5 | "noEmit": true |
|
5 | "noEmit": true | |
6 | }, |
|
6 | }, | |
7 | "include": [ |
|
7 | "include": [ | |
8 | "ts" |
|
|||
9 | ] |
|
8 | ] | |
10 | } No newline at end of file |
|
9 | } |
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