##// END OF EJS Templates
Added Renderer, WatchRendition
cin -
r94:131e369d1143 default
parent child
Show More
@@ -0,0 +1,26
1 import { Scope } from "./Scope";
2 import { destroy, Rendition } from "./traits";
3
4 let _scope = Scope.dummy;
5
6 const beginRender = async () => {
7 }
8
9 const endRender = () => {
10 }
11
12 export const getScope = () => _scope;
13
14 export const render = async (rendition: () => Rendition, scope = Scope.dummy) => {
15 await beginRender();
16 const prev = _scope;
17 _scope = scope;
18 try {
19 const node = rendition().getDomNode();
20 scope.own(() => destroy(node));
21 return node;
22 } finally {
23 _scope = prev;
24 endRender();
25 }
26 }
@@ -0,0 +1,40
1 import { IDestroyable, IRemovable } from "@implab/core-amd/interfaces";
2 import { isDestroyable, isRemovable } from "@implab/core-amd/safe";
3
4 export interface IScope {
5 own(target: (() => void) | IDestroyable | IRemovable): void;
6 }
7
8 export class Scope implements IDestroyable, IScope {
9 private readonly _cleanup: (() => void)[] = [];
10
11 static readonly dummy: IScope = { own() { } };
12
13 own(target: (() => void) | IDestroyable | IRemovable) {
14 if (target instanceof Function) {
15 this._cleanup.push(target);
16 } else if (isDestroyable(target)) {
17 this._cleanup.push(() => target.destroy());
18 } else if (isRemovable(target)) {
19 this._cleanup.push(() => target.remove());
20 }
21 }
22
23 clean() {
24 const guard = (cb: () => void) => {
25 try {
26 cb();
27 } catch {
28 // guard
29 }
30 }
31
32 this._cleanup.forEach(guard);
33 this._cleanup.length = 0;
34 }
35
36 destroy() {
37 this.clean();
38 }
39
40 } No newline at end of file
@@ -0,0 +1,58
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 { place } from "dojo/dom-construct";
5 import { getScope, render } from "./Renderer";
6 import { RenditionBase } from "./RenditionBase";
7 import { Scope } from "./Scope";
8 import { locateNode } from "./traits";
9
10 const trace = TraceSource.get(mid);
11
12 export class WatchRendition<T> extends RenditionBase<Node> {
13 private readonly _factory: (arg: T) => any;
14
15 private _node: Node;
16
17 private readonly _scope = new Scope();
18
19 constructor(component: (arg: T) => any, subject: any) {
20 super();
21 argumentNotNull(component, "component");
22
23 this._factory = component;
24
25 this._node = document.createComment("WatchRendition placeholder");
26 }
27
28 protected _create(attrs: object, children: any[]) {
29 const _attrs: any = attrs || {};
30 const _children = children.map(x => this.getItemDom(x));
31 this._node = this.getItemDom(
32 this._factory.call(null, { ..._attrs, children: _children })
33 );
34
35 const scope = getScope();
36 scope.own(this._scope);
37
38 // Ссли ΠΎΡ‚Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ»ΠΈ тСкст? ΠΈΠ»ΠΈ DocumentFragment
39 }
40
41 private async _render(value: T) {
42 const [refNode, position] = locateNode(this._node);
43 this._scope.clean();
44
45 this._node = await render(() => this._factory(value), this._scope);
46
47 if (refNode)
48 place(this._node, refNode, position);
49 }
50
51 protected _getDomNode() {
52 if (!this._node)
53 throw new Error("The instance of the widget isn't created");
54 return this._node;
55 }
56
57
58 }
@@ -1,152 +1,161
1 1 import { IDestroyable } from "@implab/core-amd/interfaces";
2 2 import { isDestroyable } from "@implab/core-amd/safe";
3 3 import _WidgetBase = require("dijit/_WidgetBase");
4 4 import registry = require("dijit/registry");
5 5 import dom = require("dojo/dom-construct");
6 6
7 7 type _WidgetBaseConstructor = typeof _WidgetBase;
8 8
9 9 export type DojoNodePosition = "first" | "after" | "before" | "last" | "replace" | "only" | number;
10 10
11 export type DojoNodeLocation = [Node | null, DojoNodePosition];
12
11 13 export interface Rendition<TNode extends Node = Node> {
12 14 getDomNode(): TNode;
13 15
14 16 placeAt(refNode: string | Node, position?: DojoNodePosition): void;
15 17 }
16 18
17 19 /**
18 20 * @deprecated use Rendition
19 21 */
20 22 export type BuildContext<TNode extends Node = Node> = Rendition<TNode>;
21 23
22 24 export interface IRecursivelyDestroyable {
23 25 destroyRecursive(): void;
24 26 }
25 27
26 28 export function isNode(el: any): el is Node {
27 29 return el && el.nodeName && el.nodeType;
28 30 }
29 31
30 32 export function isElementNode(el: any): el is Element {
31 33 return isNode(el) && el.nodeType === 1;
32 34 }
33 35
34 36 export function isTextNode(el: any): el is Text {
35 37 return isNode(el) && el.nodeType === 3;
36 38 }
37 39
38 40 export function isProcessingInstructionNode(el: any): el is ProcessingInstruction {
39 41 return isNode(el) && el.nodeType === 7;
40 42 }
41 43
42 44 export function isCommentNode(el: any): el is Comment {
43 45 return isNode(el) && el.nodeType === 8;
44 46 }
45 47
46 48 export function isDocumentNode(el: any): el is Document {
47 49 return isNode(el) && el.nodeType === 9;
48 50 }
49 51
50 52 export function isDocumentTypeNode(el: any): el is DocumentType {
51 53 return isNode(el) && el.nodeType === 10;
52 54 }
53 55
54 56 export function isDocumentFragmentNode(el: any): el is DocumentFragment {
55 57 return isNode(el) && el.nodeType === 11;
56 58 }
57 59
58 60 export function isWidget(v: any): v is _WidgetBase {
59 61 return v && "domNode" in v;
60 62 }
61 63
62 64 export function isRendition(v: any): v is Rendition {
63 65 return v && typeof v.getDomElement === "function";
64 66 }
65 67
66 68 /**
67 69 * @deprecated use isRendition
68 70 */
69 71 export const isBuildContext = isRendition;
70 72
71 73 export function isPlainObject(v: object) {
72 74 if (typeof v !== "object")
73 75 return false;
74 76
75 77 const vp = Object.getPrototypeOf(v);
76 78 return !vp || vp === Object.prototype;
77 79 }
78 80
79 81 export function isWidgetConstructor(v: any): v is _WidgetBaseConstructor {
80 82 return typeof v === "function" && v.prototype && (
81 83 "domNode" in v.prototype ||
82 84 "buildRendering" in v.prototype
83 85 );
84 86 }
85 87
86 88 /** Tests whether the specified node is placed in visible dom.
87 89 * @param {Node} node The node to test
88 90 */
89 91 export function isInPage(node: Node) {
90 92 return (node === document.body) ? false : document.body.contains(node);
91 93 }
92 94
93 95 export function isRecursivelyDestroyable(target: any): target is IRecursivelyDestroyable {
94 96 return target && typeof target.destroyRecursive === "function";
95 97 }
96 98
97 99
98 100 /** Destroys DOM Node with all contained widgets.
99 101 * If the specified node is the root node of a widget, then the
100 102 * widget will be destroyed.
101 103 *
102 104 * @param target DOM Node or widget to destroy
103 105 */
104 106 export function destroy(target: Node | IDestroyable | IRecursivelyDestroyable) {
105 107 if (isRecursivelyDestroyable(target)) {
106 108 target.destroyRecursive();
107 109 } else if (isDestroyable(target)) {
108 110 target.destroy();
109 111 } else if (isNode(target)) {
110 112 if (isElementNode(target)) {
111 113 const w = registry.byNode(target);
112 114 if (w) {
113 115 w.destroyRecursive();
114 116 } else {
115 117 registry.findWidgets(target).forEach(destroy);
116 118 dom.destroy(target);
117 119 }
118 120 }
119 121 }
120 122 }
121 123
122 124 /** Empties a content of the specified node and destroys all contained widgets.
123 125 *
124 126 * @param target DOM node to .
125 127 */
126 128 export function emptyNode(target: Node) {
127 129 registry.findWidgets(target).forEach(destroy);
128 130 dom.empty(target);
129 131 }
130 132
131 133 /** This function starts all widgets inside the DOM node if the target is a node
132 134 * or starts widget itself if the target is the widget. If the specified node
133 135 * associated with the widget that widget will be started.
134 136 *
135 137 * @param target DOM node to find and start widgets or the widget itself.
136 138 */
137 139 export function startupWidgets(target: Node | _WidgetBase, skipNode?: Node) {
138 140 if (isNode(target)) {
139 141 if (isElementNode(target)) {
140 142 const w = registry.byNode(target);
141 143 if (w) {
142 144 if (w.startup)
143 145 w.startup();
144 146 } else {
145 147 registry.findWidgets(target, skipNode).forEach(x => x.startup());
146 148 }
147 149 }
148 150 } else {
149 151 if (target.startup)
150 152 target.startup();
151 153 }
152 154 }
155
156 export function locateNode(node: Node): DojoNodeLocation {
157 const next = node.nextSibling;
158 return next ?
159 [next, "before"] :
160 [node.parentNode, "last"];
161 } No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now