| @@ -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 | } | |
| @@ -8,6 +8,8 type _WidgetBaseConstructor = typeof _Wi | |||
| 
             | 
        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 | |
| @@ -150,3 +152,10 export function startupWidgets(target: N | |||
| 
             | 
        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
                    
                