##// 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 import { IDestroyable } from "@implab/core-amd/interfaces";
1 import { IDestroyable } from "@implab/core-amd/interfaces";
2 import { isDestroyable } from "@implab/core-amd/safe";
2 import { isDestroyable } from "@implab/core-amd/safe";
3 import _WidgetBase = require("dijit/_WidgetBase");
3 import _WidgetBase = require("dijit/_WidgetBase");
4 import registry = require("dijit/registry");
4 import registry = require("dijit/registry");
5 import dom = require("dojo/dom-construct");
5 import dom = require("dojo/dom-construct");
6
6
7 type _WidgetBaseConstructor = typeof _WidgetBase;
7 type _WidgetBaseConstructor = typeof _WidgetBase;
8
8
9 export type DojoNodePosition = "first" | "after" | "before" | "last" | "replace" | "only" | number;
9 export type DojoNodePosition = "first" | "after" | "before" | "last" | "replace" | "only" | number;
10
10
11 export type DojoNodeLocation = [Node | null, DojoNodePosition];
12
11 export interface Rendition<TNode extends Node = Node> {
13 export interface Rendition<TNode extends Node = Node> {
12 getDomNode(): TNode;
14 getDomNode(): TNode;
13
15
14 placeAt(refNode: string | Node, position?: DojoNodePosition): void;
16 placeAt(refNode: string | Node, position?: DojoNodePosition): void;
15 }
17 }
16
18
17 /**
19 /**
18 * @deprecated use Rendition
20 * @deprecated use Rendition
19 */
21 */
20 export type BuildContext<TNode extends Node = Node> = Rendition<TNode>;
22 export type BuildContext<TNode extends Node = Node> = Rendition<TNode>;
21
23
22 export interface IRecursivelyDestroyable {
24 export interface IRecursivelyDestroyable {
23 destroyRecursive(): void;
25 destroyRecursive(): void;
24 }
26 }
25
27
26 export function isNode(el: any): el is Node {
28 export function isNode(el: any): el is Node {
27 return el && el.nodeName && el.nodeType;
29 return el && el.nodeName && el.nodeType;
28 }
30 }
29
31
30 export function isElementNode(el: any): el is Element {
32 export function isElementNode(el: any): el is Element {
31 return isNode(el) && el.nodeType === 1;
33 return isNode(el) && el.nodeType === 1;
32 }
34 }
33
35
34 export function isTextNode(el: any): el is Text {
36 export function isTextNode(el: any): el is Text {
35 return isNode(el) && el.nodeType === 3;
37 return isNode(el) && el.nodeType === 3;
36 }
38 }
37
39
38 export function isProcessingInstructionNode(el: any): el is ProcessingInstruction {
40 export function isProcessingInstructionNode(el: any): el is ProcessingInstruction {
39 return isNode(el) && el.nodeType === 7;
41 return isNode(el) && el.nodeType === 7;
40 }
42 }
41
43
42 export function isCommentNode(el: any): el is Comment {
44 export function isCommentNode(el: any): el is Comment {
43 return isNode(el) && el.nodeType === 8;
45 return isNode(el) && el.nodeType === 8;
44 }
46 }
45
47
46 export function isDocumentNode(el: any): el is Document {
48 export function isDocumentNode(el: any): el is Document {
47 return isNode(el) && el.nodeType === 9;
49 return isNode(el) && el.nodeType === 9;
48 }
50 }
49
51
50 export function isDocumentTypeNode(el: any): el is DocumentType {
52 export function isDocumentTypeNode(el: any): el is DocumentType {
51 return isNode(el) && el.nodeType === 10;
53 return isNode(el) && el.nodeType === 10;
52 }
54 }
53
55
54 export function isDocumentFragmentNode(el: any): el is DocumentFragment {
56 export function isDocumentFragmentNode(el: any): el is DocumentFragment {
55 return isNode(el) && el.nodeType === 11;
57 return isNode(el) && el.nodeType === 11;
56 }
58 }
57
59
58 export function isWidget(v: any): v is _WidgetBase {
60 export function isWidget(v: any): v is _WidgetBase {
59 return v && "domNode" in v;
61 return v && "domNode" in v;
60 }
62 }
61
63
62 export function isRendition(v: any): v is Rendition {
64 export function isRendition(v: any): v is Rendition {
63 return v && typeof v.getDomElement === "function";
65 return v && typeof v.getDomElement === "function";
64 }
66 }
65
67
66 /**
68 /**
67 * @deprecated use isRendition
69 * @deprecated use isRendition
68 */
70 */
69 export const isBuildContext = isRendition;
71 export const isBuildContext = isRendition;
70
72
71 export function isPlainObject(v: object) {
73 export function isPlainObject(v: object) {
72 if (typeof v !== "object")
74 if (typeof v !== "object")
73 return false;
75 return false;
74
76
75 const vp = Object.getPrototypeOf(v);
77 const vp = Object.getPrototypeOf(v);
76 return !vp || vp === Object.prototype;
78 return !vp || vp === Object.prototype;
77 }
79 }
78
80
79 export function isWidgetConstructor(v: any): v is _WidgetBaseConstructor {
81 export function isWidgetConstructor(v: any): v is _WidgetBaseConstructor {
80 return typeof v === "function" && v.prototype && (
82 return typeof v === "function" && v.prototype && (
81 "domNode" in v.prototype ||
83 "domNode" in v.prototype ||
82 "buildRendering" in v.prototype
84 "buildRendering" in v.prototype
83 );
85 );
84 }
86 }
85
87
86 /** Tests whether the specified node is placed in visible dom.
88 /** Tests whether the specified node is placed in visible dom.
87 * @param {Node} node The node to test
89 * @param {Node} node The node to test
88 */
90 */
89 export function isInPage(node: Node) {
91 export function isInPage(node: Node) {
90 return (node === document.body) ? false : document.body.contains(node);
92 return (node === document.body) ? false : document.body.contains(node);
91 }
93 }
92
94
93 export function isRecursivelyDestroyable(target: any): target is IRecursivelyDestroyable {
95 export function isRecursivelyDestroyable(target: any): target is IRecursivelyDestroyable {
94 return target && typeof target.destroyRecursive === "function";
96 return target && typeof target.destroyRecursive === "function";
95 }
97 }
96
98
97
99
98 /** Destroys DOM Node with all contained widgets.
100 /** Destroys DOM Node with all contained widgets.
99 * If the specified node is the root node of a widget, then the
101 * If the specified node is the root node of a widget, then the
100 * widget will be destroyed.
102 * widget will be destroyed.
101 *
103 *
102 * @param target DOM Node or widget to destroy
104 * @param target DOM Node or widget to destroy
103 */
105 */
104 export function destroy(target: Node | IDestroyable | IRecursivelyDestroyable) {
106 export function destroy(target: Node | IDestroyable | IRecursivelyDestroyable) {
105 if (isRecursivelyDestroyable(target)) {
107 if (isRecursivelyDestroyable(target)) {
106 target.destroyRecursive();
108 target.destroyRecursive();
107 } else if (isDestroyable(target)) {
109 } else if (isDestroyable(target)) {
108 target.destroy();
110 target.destroy();
109 } else if (isNode(target)) {
111 } else if (isNode(target)) {
110 if (isElementNode(target)) {
112 if (isElementNode(target)) {
111 const w = registry.byNode(target);
113 const w = registry.byNode(target);
112 if (w) {
114 if (w) {
113 w.destroyRecursive();
115 w.destroyRecursive();
114 } else {
116 } else {
115 registry.findWidgets(target).forEach(destroy);
117 registry.findWidgets(target).forEach(destroy);
116 dom.destroy(target);
118 dom.destroy(target);
117 }
119 }
118 }
120 }
119 }
121 }
120 }
122 }
121
123
122 /** Empties a content of the specified node and destroys all contained widgets.
124 /** Empties a content of the specified node and destroys all contained widgets.
123 *
125 *
124 * @param target DOM node to .
126 * @param target DOM node to .
125 */
127 */
126 export function emptyNode(target: Node) {
128 export function emptyNode(target: Node) {
127 registry.findWidgets(target).forEach(destroy);
129 registry.findWidgets(target).forEach(destroy);
128 dom.empty(target);
130 dom.empty(target);
129 }
131 }
130
132
131 /** This function starts all widgets inside the DOM node if the target is a node
133 /** This function starts all widgets inside the DOM node if the target is a node
132 * or starts widget itself if the target is the widget. If the specified node
134 * or starts widget itself if the target is the widget. If the specified node
133 * associated with the widget that widget will be started.
135 * associated with the widget that widget will be started.
134 *
136 *
135 * @param target DOM node to find and start widgets or the widget itself.
137 * @param target DOM node to find and start widgets or the widget itself.
136 */
138 */
137 export function startupWidgets(target: Node | _WidgetBase, skipNode?: Node) {
139 export function startupWidgets(target: Node | _WidgetBase, skipNode?: Node) {
138 if (isNode(target)) {
140 if (isNode(target)) {
139 if (isElementNode(target)) {
141 if (isElementNode(target)) {
140 const w = registry.byNode(target);
142 const w = registry.byNode(target);
141 if (w) {
143 if (w) {
142 if (w.startup)
144 if (w.startup)
143 w.startup();
145 w.startup();
144 } else {
146 } else {
145 registry.findWidgets(target, skipNode).forEach(x => x.startup());
147 registry.findWidgets(target, skipNode).forEach(x => x.startup());
146 }
148 }
147 }
149 }
148 } else {
150 } else {
149 if (target.startup)
151 if (target.startup)
150 target.startup();
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