##// END OF EJS Templates
Working on WatchForRendition
cin -
r107:e59104632d14 default
parent child
Show More

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

@@ -0,0 +1,16
1 import aspect = require("dojo/aspect");
2 import { Cancellation } from "@implab/core-amd/Cancellation";
3
4 export const play = (ani: dojo._base.Animation, ct = Cancellation.none) => {
5 if (ct.isSupported())
6 ct.register(() => ani.stop());
7
8 return new Promise<void>(resolve => {
9 aspect.after(ani, "onEnd", x => {
10 resolve();
11 return x;
12 });
13 ani.play();
14 });
15
16 };
@@ -0,0 +1,3
1 export default class Observable<S extends dojo.store.api.Store<object>> {
2
3 } No newline at end of file
@@ -0,0 +1,215
1 import { id as mid } from "module";
2 import { TraceSource } from "@implab/core-amd/log/TraceSource";
3 import { argumentNotNull } from "@implab/core-amd/safe";
4 import { getScope, render } from "./render";
5 import { RenditionBase } from "./RenditionBase";
6 import { Scope } from "./Scope";
7 import { Cancellation } from "@implab/core-amd/Cancellation";
8 import { collectNodes, destroy as safeDestroy, isDocumentFragmentNode, isElementNode, isMounted, placeAt, startupWidgets } from "./traits";
9 import { IDestroyable } from "@implab/core-amd/interfaces";
10 import { play } from "../play";
11 import * as fx from "dojo/fx";
12 import { isSubsribable, Subscribable } from "../observable";
13 import { QueryResultUpdate } from "../tsx";
14
15 const trace = TraceSource.get(mid);
16
17 interface ItemRendition {
18 nodes: Node[];
19
20 scope: IDestroyable;
21
22 destroy(): void;
23 }
24
25 interface ObservableResults<T> {
26 /**
27 * Allows observation of results
28 */
29 observe(listener: (object: T, previousIndex: number, newIndex: number) => void, includeUpdates?: boolean): {
30 remove(): void;
31 };
32 }
33
34 interface RenderTask<T> extends QueryResultUpdate<T> {
35 animate: boolean;
36 }
37
38 export interface AnimationAttrs {
39 animate?: boolean;
40
41 animateIn?: (nodes: Node[]) => Promise<void>;
42
43 animateOut?: (nodes: Node[]) => Promise<void>;
44 }
45
46 export interface WatchForRenditionAttrs<T> extends AnimationAttrs {
47 subject: T[] | Subscribable<QueryResultUpdate<T>>;
48
49 component: (arg: T, index: number) => unknown;
50 }
51
52 const isObservable = <T>(v: PromiseLike<ArrayLike<T>> | ArrayLike<T>): v is ArrayLike<T> & ObservableResults<T> =>
53 v && (typeof (v as any).observe === "function");
54
55 const noop = () => {};
56
57 const fadeIn = (nodes: Node[]) => Promise.all(nodes
58 .filter(isElementNode)
59 .map(el => play(fx.fadeIn({ node: el as HTMLElement })))
60 ).then(noop);
61
62 const fadeOut = (nodes: Node[]) => Promise.all(nodes
63 .filter(isElementNode)
64 .map(el => play(fx.fadeOut({ node: el as HTMLElement })))
65 ).then(noop);
66
67
68 export class WatchForRendition<T> extends RenditionBase<Node> {
69 private readonly _component: (arg: T, index: number) => unknown;
70
71 private readonly _node: Node;
72
73 private readonly _itemRenditions: ItemRendition[] = [];
74
75 private readonly _subject: T[] | Subscribable<QueryResultUpdate<T>>;
76
77 private readonly _renderTasks: RenderTask<T>[] = [];
78
79 private readonly _animate: boolean;
80
81 private readonly _animateIn: (nodes: Node[]) => Promise<void>;
82
83 private readonly _animateOut: (nodes: Node[]) => Promise<void>;
84
85 private _ct = Cancellation.none;
86
87 constructor({ subject, component, animate, animateIn, animateOut }: WatchForRenditionAttrs<T>) {
88 super();
89 argumentNotNull(component, "component");
90 argumentNotNull(subject, "component");
91
92 this._component = component;
93
94 this._subject = subject;
95
96 this._node = document.createComment("[WatchFor]");
97 this._animate = !!animate;
98 this._animateIn = animateIn ?? fadeIn;
99 this._animateOut = animateOut ?? fadeOut;
100 }
101
102 protected _create() {
103 const scope = getScope();
104 scope.own(() => {
105 this._itemRenditions.forEach(safeDestroy);
106 safeDestroy(this._node);
107 });
108
109 const result = this._subject;
110
111 if (result) {
112 if (isSubsribable<QueryResultUpdate<T>>(result)) {
113 let animate = false;
114 const subscription = result.subscribe({
115 next: ({item, prevIndex, newIndex}) => this._onItemUpdated({ item, prevIndex, newIndex, animate })
116 });
117 scope.own(subscription);
118 animate = this._animate;
119 } else {
120 if (isObservable(result))
121 scope.own(result.observe((item, prevIndex, newIndex) => this._onItemUpdated({ item, prevIndex, newIndex, animate: false }), true));
122
123 for (let i = 0, n = result.length; i < n; i++)
124 this._onItemUpdated({ item: result[i], prevIndex: -1, newIndex: i, animate: this._animate });
125 }
126 }
127 this._ct = new Cancellation(cancel => scope.own(cancel));
128 }
129
130 private _onItemUpdated = (item: RenderTask<T>) => {
131 if (!this._renderTasks.length) {
132 // schedule a new job
133 this._renderTasks.push(item);
134 this._render().catch(e => trace.error(e));
135 } else {
136 // update existing job
137 this._renderTasks.push(item);
138 }
139 }
140
141 private async _render() {
142 // fork
143 await Promise.resolve();
144 // don't render destroyed rendition
145 if (this._ct.isRequested())
146 return;
147
148 this._renderTasks.forEach(this._onRenderItem);
149 this._renderTasks.length = 0;
150 }
151
152 _onRenderItem = ({ item, newIndex, prevIndex, animate: _animate }: RenderTask<T>) => {
153 const animate = _animate && prevIndex !== newIndex;
154
155 if (prevIndex > -1) {
156 // if we need to delete previous rendition
157 const [{ nodes, destroy }] = this._itemRenditions.splice(prevIndex, 1);
158 if (animate) {
159 this._animateOut(nodes)
160 .then(destroy)
161 .catch(e => trace.error(e));
162 } else {
163 destroy();
164 }
165 }
166
167 if (newIndex > -1) {
168 // if we need to create the new rendition
169
170 // 1. create a new scope for rendering a content
171 const scope = new Scope();
172
173 // 2. render the content
174 const itemNode = render(this._component(item, newIndex), scope);
175
176 // 3. track nodes
177 const nodes = isDocumentFragmentNode(itemNode) ?
178 collectNodes(itemNode.childNodes) :
179 [itemNode];
180
181 // 5. insert node at the correct position
182
183 const { nodes: [beforeNode] } = this._itemRenditions[newIndex] ?? { nodes: [] };
184
185 if (beforeNode)
186 placeAt(itemNode, beforeNode, "before");
187 else
188 placeAt(itemNode, this._node, "before");
189
190 // 6. store information about rendition
191 this._itemRenditions.splice(newIndex, 0, {
192 scope,
193 nodes,
194 destroy: () => {
195 scope.destroy();
196 nodes.forEach(safeDestroy);
197 }
198 });
199
200 // 7. startup widgets if needed
201 if (isMounted(this._node))
202 nodes.forEach(n => startupWidgets(n));
203
204 // 8. optionally play the animation
205 if (animate)
206 this._animateIn(nodes).catch(e => trace.error(e));
207 }
208 }
209
210 protected _getDomNode() {
211 if (!this._node)
212 throw new Error("The instance of the widget isn't created");
213 return this._node;
214 }
215 }
@@ -0,0 +1,10
1 {
2 "extends": "./tsconfig.json",
3 "compilerOptions": {
4 // ensure that nobody can accidentally use this config for a build
5 "noEmit": true
6 },
7 "include": [
8 "ts"
9 ]
10 } No newline at end of file
@@ -0,0 +1,33
1 {
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": "src",
17 "project": ["tsconfig.eslint.json", "*/tsconfig.json"]
18 },
19 "plugins": [
20 "@typescript-eslint"
21 ],
22 "rules": {
23 "@typescript-eslint/no-empty-function": "off",
24 "max-classes-per-file": [
25 "error",
26 { "ignoreExpressions": true, "max": 1 }
27 ],
28 "@typescript-eslint/prefer-readonly": ["error"],
29 "semi": "off",
30 "@typescript-eslint/semi": ["error"]
31
32 }
33 }
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,25 +1,33
1 1 {
2 "env": {
3 "browser": true,
4 "amd": true
5 },
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",
6 11 "parserOptions": {
7 12 "ecmaFeatures": {
8 13 "jsx": true
9 14 },
10 "sourceType": "script"
15 "ecmaVersion": 5,
16 "tsconfigRootDir": "djx/src",
17 "project": ["tsconfig.eslint.json", "*/tsconfig.json"]
11 18 },
12 "extends": "eslint:recommended",
19 "plugins": [
20 "@typescript-eslint"
21 ],
13 22 "rules": {
14 "no-const-assign": "warn",
15 "no-this-before-super": "warn",
16 "no-undef": "error",
17 "no-unreachable": "warn",
18 "no-unused-vars": "warn",
19 "constructor-super": "warn",
20 "valid-typeof": "warn",
21 "semi" : "warn",
22 "no-invalid-this" : "error",
23 "no-console": "off"
23 "@typescript-eslint/no-empty-function": "off",
24 "max-classes-per-file": [
25 "error",
26 { "ignoreExpressions": true, "max": 1 }
27 ],
28 "@typescript-eslint/prefer-readonly": ["error"],
29 "semi": "off",
30 "@typescript-eslint/semi": ["error"]
31
24 32 }
25 } No newline at end of file
33 }
@@ -48,7 +48,7 configureTsMain {
48 48 "dojo/*" : [ "typings/dojo/*" ],
49 49 "dijit/*" : [ "typings/dijit/*" ]
50 50 ]*/
51 types = ["requirejs", "@implab/dojo-typings"]
51 types = ["requirejs", "@implab/dojo-typings", "@implab/dojo-typings/dojo/NodeList-fx"]
52 52 }
53 53 }
54 54
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -26,10 +26,16
26 26 "@types/tap": "15.0.7",
27 27 "dojo": "1.16.0",
28 28 "@implab/dojo-typings": "1.0.0",
29 "eslint": "6.8.0",
29 "@typescript-eslint/eslint-plugin": "^5.23.0",
30 "@typescript-eslint/parser": "^5.23.0",
31 "eslint": "^8.23.0",
32 "eslint-config-standard": "^17.0.0",
33 "eslint-plugin-import": "^2.26.0",
34 "eslint-plugin-n": "^15.2.0",
35 "eslint-plugin-promise": "^6.0.0",
36 "eslint-plugin-react": "^7.29.4",
30 37 "requirejs": "2.3.6",
31 "tslint": "^6.1.3",
32 "typescript": "4.8.2",
38 "typescript": "4.8.3",
33 39 "yaml": "~1.7.2",
34 40 "tap": "16.3.0"
35 41 }
@@ -6,18 +6,19 import { Constructor } from "@implab/cor
6 6
7 7 type DeclareConstructor<T> = dojo._base.DeclareConstructor<T>;
8 8
9 export interface AbstractConstructor<T = {}> {
9 export interface AbstractConstructor<T = object> {
10 10 prototype: T;
11 11 }
12 12
13 interface DjMockConstructor<T = {}> {
14 new(...args: any[]): T;
13 interface DjMockConstructor<T = object> {
14 new(...args: unknown[]): T;
15 15 mock: boolean;
16 16 bases: AbstractConstructor[];
17 17 }
18 18
19 export function djbase<T>(): DeclareConstructor<T>;
19 20 export function djbase<T>(
20 b0?: AbstractConstructor<T>
21 b0: AbstractConstructor<T>
21 22 ): DeclareConstructor<T>;
22 23
23 24 export function djbase<T0, T1>(
@@ -91,7 +92,7 export function djbase<T0, T1, T2, T3, T
91 92 * новый класс.
92 93 *
93 94 */
94 export function djbase(...bases: any[]): Constructor {
95 export function djbase(...bases: AbstractConstructor[]): Constructor {
95 96
96 97 const t = class {
97 98 static mock: boolean;
@@ -101,10 +102,10 export function djbase(...bases: any[]):
101 102 t.mock = true;
102 103 t.bases = bases;
103 104
104 return t as any;
105 return t as Constructor;
105 106 }
106 107
107 function isMockConstructor<T extends {}>(v: AbstractConstructor<T>): v is DjMockConstructor<T> {
108 function isMockConstructor<T extends object>(v: AbstractConstructor<T>): v is DjMockConstructor<T> {
108 109 return v && "mock" in v;
109 110 }
110 111
@@ -115,30 +116,30 function isMockConstructor<T extends {}>
115 116 */
116 117 export function djclass<T extends AbstractConstructor>(target: T): T {
117 118 // получаем базовый конструктор и его прототип
118 let bp = target && target.prototype && Object.getPrototypeOf(target.prototype);
119 const bp = target && !!target.prototype && Object.getPrototypeOf(target.prototype) as object;
119 120 const bc = bp && bp.constructor;
120 121
121 122 // проверка того, что класс унаследован от специальной заглушки
122 123 if (isMockConstructor(bc)) {
123 124 // bc.bases - базовый класс, объявленный при помощи dojo/_base/declare
124 const cls = declare<any>(bc.bases, target.prototype);
125 const cls = declare(bc.bases, target.prototype) as unknown as T;
125 126
126 127 // bc - базовый класс, bc.prototype используется как super
127 128 // при вызове базовых методов. Нужно создать bc.prototype
128 129 // таким образом, чтобы он вызывал this.inherited().
129 130
130 // создаем новый порототип, он не в цепочке прототипов у текущего
131 // создаем новый прототип, он не в цепочке прототипов у текущего
131 132 // класса, но super.some_method будет использовать именно его.
132 133 // в этом объекте будут размещены прокси для переопределенных
133 134 // методов.
134 bp = bc.prototype = Object.create(cls.prototype);
135 bp.constructor = bc;
135 const nbp = bc.prototype = Object.create(cls.prototype) as Record<string, unknown>;
136 nbp.constructor = bc;
136 137
137 138 // proxy - фабрика для создания прокси-методов, которые внутри
138 139 // себя вызовут this.inherited с правильными параметрами.
139 const proxy = (m: (...args: any[]) => any) => function (this: any) {
140 const f = this.getInherited({ callee: m });
141 return f && f.apply(this, arguments);
140 const proxy = (m: (...args: unknown[]) => unknown) => function (this: dojo._base.DeclareCreatedObject, ...args: unknown[]) {
141 const f = this.getInherited({ callee: m, ...args, length: args.length});
142 return f ? f.apply(this, args) as unknown : undefined;
142 143
143 144 // так сделать можно только dojo 1.15+
144 145 // return this.inherited(m, arguments);
@@ -149,19 +150,19 export function djclass<T extends Abstra
149 150 // создать для них прокси.
150 151 // При этом только те, методы, которые есть в базовых классах
151 152 // могут быть переопределены.
152 each(target.prototype, (m: any, p: string | number | symbol) => {
153 each(target.prototype, (m: unknown, p: string) => {
153 154 if (typeof m === "function" &&
154 155 p !== "constructor" &&
155 target.prototype.hasOwnProperty(p)
156 Object.prototype.hasOwnProperty.call(target, p)
156 157 ) {
157 bp[p] = proxy(m);
158 nbp[p] = proxy(m as (...args: unknown[]) => unknown);
158 159 }
159 160 });
160 161
161 162 // TODO mixin static members
162 return cls as any;
163 return cls;
163 164 } else {
164 return target as any;
165 return target;
165 166 }
166 167 }
167 168
@@ -208,7 +209,7 interface MemberBindSpec {
208 209 getter?: boolean;
209 210 }
210 211
211 function isNodeBindSpec(v: any): v is NodeBindSpec {
212 function isNodeBindSpec(v: object): v is NodeBindSpec {
212 213 return "node" in v;
213 214 }
214 215
@@ -219,19 +220,23 function isNodeBindSpec(v: any): v is No
219 220 */
220 221 export function bind(params: NodeBindSpec | MemberBindSpec) {
221 222 if (isNodeBindSpec(params)) {
222 return (target: any, name: string) => {
223 return (target: Record<string, unknown>, name: string) => {
223 224 target[makeSetterName(name)] = params;
224 225 };
225 226 } else {
226 return (target: any, name: string) => {
227 return (target: Record<string, unknown> & { _set(name: string, v: unknown): void }, name: string) => {
227 228 target[name] = null;
228 target[makeSetterName(name)] = function (v: any) {
229 target[makeSetterName(name)] = function (v: unknown) {
229 230 this._set(name, v);
230 this[params.member].set(params.property, v);
231 const inner = this[params.member] as Record<string, unknown>;
232 if (typeof inner.set === "function")
233 inner.set(params.property, v);
231 234 };
232 235 if (params.getter)
233 236 target[makeGetterName(name)] = function () {
234 return this[params.member].get(params.property);
237 const inner = this[params.member] as Record<string, unknown>;
238 if (typeof inner.get === "function")
239 return inner.get(params.property) as unknown;
235 240 };
236 241 };
237 242 }
@@ -241,7 +246,7 export function bind(params: NodeBindSpe
241 246 * декоратор следует использовать для свойств, у которых нет значения по-умолчанию
242 247 * и они не могут быть `null | undefined`
243 248 */
244 export function prototype(): (p: any, name: string) => void;
249 export function prototype(): (p: object, name: string) => void;
245 250 /** Создает в прототипе свойство с указанным значением.
246 251 * @param value Значение, которое будет указано в прототипе
247 252 */
@@ -35,7 +35,7 export interface Unsubscribable {
35 35 export const isUnsubsribable = (v: unknown): v is Unsubscribable =>
36 36 v !== null && v !== undefined && typeof (v as Unsubscribable).unsubscribe === "function";
37 37
38 export const isSubsribable = (v: unknown): v is Subscribable<unknown> =>
38 export const isSubsribable = <T = unknown>(v: unknown): v is Subscribable<T> =>
39 39 v !== null && v !== undefined && typeof (v as Subscribable<unknown>).subscribe === "function";
40 40
41 41 export interface Subscribable<T> {
@@ -100,7 +100,7 const _observe = <T>(producer: Producer<
100 100 filter: (predicate) => _observe(({ next, error, complete }) =>
101 101 producer({
102 102 next: next !== noop ?
103 (v: T) => predicate(v) ? next(v) : void(0) : noop,
103 (v: T) => predicate(v) ? next(v) : void (0) : noop,
104 104 error,
105 105 complete
106 106 })
@@ -136,7 +136,7 export const observe = <T>(producer: Pro
136 136 complete
137 137 }))
138 138 ),
139 scan: (accumulator, initial?) => observe(({ next, error, complete }) => {
139 scan: (accumulator, initial) => observe(({ next, error, complete }) => {
140 140 let _acc = initial;
141 141 return producer(fuse({
142 142 next: next !== noop ? (v: T) => next(_acc = accumulator(_acc, v)) : noop,
@@ -10,6 +10,7 import { WatchRendition } from "./tsx/Wa
10 10 import { Observable, observe, Subscribable } from "./observable";
11 11 import djAttr = require("dojo/dom-attr");
12 12 import djClass = require("dojo/dom-class");
13 import { AnimationAttrs, WatchForRendition } from "./tsx/WatchForRendition";
13 14
14 15 export function createElement<T extends Constructor | string | ((props: any) => Element)>(elementType: T, ...args: any[]): Rendition {
15 16 if (typeof elementType === "string") {
@@ -44,6 +45,17 export interface EventSelector {
44 45 target: HTMLElement;
45 46 }
46 47
48 export interface QueryResultUpdate<T> {
49 /** The item is being updated */
50 item: T;
51
52 /** The previous index of the item, -1 in case it is inserted */
53 prevIndex: number;
54
55 /** The new index of the item, -1 in case it is deleted */
56 newIndex: number;
57 }
58
47 59 export type DojoMouseEvent<T = any> = MouseEvent & EventSelector & EventDetails<T>;
48 60
49 61 type StatefulProps<T> = T extends Stateful<infer A> ? A :
@@ -100,6 +112,15 export function watch(
100 112 }
101 113 }
102 114
115 export const watchFor = <T>(source: T[] | Subscribable<QueryResultUpdate<T>>, render: (item: T, index: number) => unknown, opts: AnimationAttrs = {}) => {
116 return new WatchForRendition({
117 ...opts,
118 subject: source,
119 component: render
120 });
121 }
122
123
103 124 export const prop: {
104 125 <T extends Stateful, K extends string & keyof StatefulProps<T>>(target: T, name: K): Observable<StatefulProps<T>[K]>;
105 126 <T extends _WidgetBase, K extends keyof T>(target: T, name: K): Observable<T[K]>;
@@ -44,7 +44,7 export class WatchRendition<T> extends R
44 44 this._ct = new Cancellation(cancel => scope.own(cancel));
45 45 }
46 46
47 private _onValue = (value: T) => {
47 private readonly _onValue = (value: T) => {
48 48 if (!this._renderJob) {
49 49 // schedule a new job
50 50 this._renderJob = { value };
@@ -53,7 +53,7 export class WatchRendition<T> extends R
53 53 // update existing job
54 54 this._renderJob = { value };
55 55 }
56 }
56 };
57 57
58 58 private async _render() {
59 59 // fork
@@ -4,8 +4,8 import _WidgetBase = require("dijit/_Wid
4 4 import registry = require("dijit/registry");
5 5
6 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>;
7 new <E extends { [k in keyof E]: Event } = {}>(params?: Partial<_WidgetBase<E>> & ThisType<_WidgetBase<E>>, srcNodeRef?: string | Node): _WidgetBase<E> & dojo._base.DeclareCreatedObject;
8 prototype: _WidgetBase;
9 9 }
10 10
11 11 export type DojoNodePosition = "first" | "after" | "before" | "last" | "replace" | "only" | number;
@@ -41,7 +41,7 export const isDocumentNode = (el: unkno
41 41
42 42 export const isDocumentTypeNode = (el: unknown): el is DocumentType => isNode(el) && el.nodeType === 10;
43 43
44 export const isDocumentFragmentNode = (el: any): el is DocumentFragment => isNode(el) && el.nodeType === 11;
44 export const isDocumentFragmentNode = (el: unknown): el is DocumentFragment => isNode(el) && el.nodeType === 11;
45 45
46 46 export const isWidget = (v: unknown): v is _WidgetBase => !!(v && "domNode" in (v as _WidgetBase));
47 47
@@ -56,12 +56,12 export const isPlainObject = (v: object)
56 56 if (typeof v !== "object")
57 57 return false;
58 58
59 const vp = Object.getPrototypeOf(v);
59 const vp = Object.getPrototypeOf(v) as object;
60 60 return !vp || vp === Object.prototype;
61 }
61 };
62 62
63 63 export const isWidgetConstructor = (v: unknown): v is _WidgetBaseConstructor =>
64 typeof v === "function" && v.prototype && (
64 typeof v === "function" && !!v.prototype && (
65 65 "domNode" in v.prototype ||
66 66 "buildRendering" in v.prototype
67 67 );
@@ -103,7 +103,7 export const destroy = (target: Node | I
103 103 parent.removeChild(target);
104 104
105 105 }
106 }
106 };
107 107
108 108 /** Empties a content of the specified node and destroys all contained widgets.
109 109 *
@@ -112,10 +112,11 export const destroy = (target: Node | I
112 112 export const emptyNode = (target: Node) => {
113 113 registry.findWidgets(target).forEach(destroy);
114 114
115 // eslint-disable-next-line no-cond-assign
115 116 for (let c; c = target.lastChild;) { // intentional assignment
116 117 target.removeChild(c);
117 118 }
118 }
119 };
119 120
120 121 /** This function starts all widgets inside the DOM node if the target is a node
121 122 * or starts widget itself if the target is the widget. If the specified node
@@ -138,7 +139,7 export const startupWidgets = (target: N
138 139 if (target.startup)
139 140 target.startup();
140 141 }
141 }
142 };
142 143
143 144 /** Places the specified DOM node at the specified location.
144 145 *
@@ -186,7 +187,7 export const placeAt = (node: Node, refN
186 187 break;
187 188 }
188 189 }
189 }
190 };
190 191
191 192 /** Collects nodes from collection to an array.
192 193 *
@@ -7,7 +7,10
7 7 "typings"
8 8 ],
9 9 "types": [
10 "requirejs", "./typings", "@implab/dojo-typings"
10 "requirejs",
11 "./typings",
12 "@implab/dojo-typings",
13 "@implab/dojo-typings/dojo/NodeList-fx"
11 14 ]
12 15 }
13 16 }
@@ -7,7 +7,6
7 7 "experimentalDecorators": true,
8 8 "jsxFactory": "createElement",
9 9 "target": "ES5",
10 //"skipLibCheck": true,
11 10 "jsx": "react",
12 11 "lib": ["es5", "es2015.promise", "es2015.symbol", "es2015.iterable", "dom", "scripthost"],
13 12 "noUnusedLocals": true
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file renamed from playground/src/main/ts/ProgressBar.tsx to playground/src/main/ts/view/ProgressBar.tsx
1 NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now