| @@ -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 | 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 | |||
| @@ -150,3 +152,10 export function startupWidgets(target: N | |||||
| 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
                    
                