diff --git a/src/main/ts/dom-inject.ts b/src/main/ts/dom-inject.ts --- a/src/main/ts/dom-inject.ts +++ b/src/main/ts/dom-inject.ts @@ -19,7 +19,7 @@ interface NodeLoadResult { class DomInject { injectionPoint?: HTMLElement; - injectBefore?: HTMLElement; + injectAfter?: HTMLElement; _map: MapOf> = {}; @@ -49,7 +49,7 @@ class DomInject { mixin(node, attr); const _injectionPoint = this.injectionPoint || document.head; - const _injectBefore = this.injectBefore || _injectionPoint.firstChild; + const _injectBefore = this.injectAfter ? this.injectAfter.nextSibling : null; _injectionPoint.insertBefore(node, _injectBefore); }); diff --git a/src/main/ts/tsx/BuildContextBase.ts b/src/main/ts/tsx/BuildContextBase.ts --- a/src/main/ts/tsx/BuildContextBase.ts +++ b/src/main/ts/tsx/BuildContextBase.ts @@ -2,6 +2,7 @@ import { isNull, mixin } from "@implab/c import { isPlainObject, isNode, isBuildContext, DojoNodePosition, BuildContext } from "./traits"; import dom = require("dojo/dom-construct"); +import registry = require("dijit/registry"); export abstract class BuildContextBase implements BuildContext { private _attrs = {}; @@ -39,8 +40,6 @@ export abstract class BuildContextBase w.startup()); } abstract _create(attrs: object, children: any[]): void; + + abstract _getDomNode(): TNode; } diff --git a/src/main/ts/tsx/DjxFragment.ts b/src/main/ts/tsx/DjxFragment.ts --- a/src/main/ts/tsx/DjxFragment.ts +++ b/src/main/ts/tsx/DjxFragment.ts @@ -5,7 +5,6 @@ import { prototype } from "../declare"; /** Special widget used to create a document fragment */ export class DjxFragment implements _Widget { - @prototype() domNode: Node; containerNode?: Node; @@ -13,13 +12,7 @@ export class DjxFragment implements _Wid constructor() { this.domNode = this.containerNode = document.createDocumentFragment(); } - - get(attr: string) { - return undefined; - } - set(attr: string, value: any): void; - set(attrs: MapOf): void; - set() { - /* do nothing */ + buildRendering() { + // this function marks this class as _Widget } } \ No newline at end of file diff --git a/src/main/ts/tsx/WidgetContext.ts b/src/main/ts/tsx/WidgetContext.ts --- a/src/main/ts/tsx/WidgetContext.ts +++ b/src/main/ts/tsx/WidgetContext.ts @@ -1,24 +1,24 @@ import dom = require("dojo/dom-construct"); import { argumentNotNull } from "@implab/core-amd/safe"; import { BuildContextBase } from "./BuildContextBase"; -import { MapOf } from "@implab/core-amd/interfaces"; +import { DojoNodePosition, isWidget } from "./traits"; // tslint:disable-next-line: class-name export interface _Widget { domNode: Node; - get(attr: string): any; + containerNode?: Node; - set(attr: string, value: any): void; - set(attrs: MapOf): void; + placeAt?(refNode: string | Node, position?: DojoNodePosition): void; + startup?(): void; - containerNode?: Node + addChild?(widget: any, index?: number): void; } export type _WidgetCtor = new (attrs: any, srcNode?: string | Node) => _Widget; export class WidgetContext extends BuildContextBase { - widgetClass: _WidgetCtor; + readonly widgetClass: _WidgetCtor; _instance: _Widget | undefined; @@ -30,10 +30,23 @@ export class WidgetContext extends Build } _addChild(child: any): void { - if (!this._instance || !this._instance.containerNode) - throw new Error("Widget doesn't support adding children"); + const instance = this._getInstance(); - dom.place(this.getChildDom(child), this._instance.containerNode); + if (instance.addChild) { + if (child instanceof WidgetContext) + // layout containers add custom logic to addChild methods + instance.addChild(child.getWidgetInstance()); + else if (isWidget(child)) + instance.addChild(child); + else + instance.addChild(this.getChildDom(child)); + } else { + if (!instance.containerNode) + throw new Error("The widget doesn't have neither addChild nor containerNode"); + + // the current widget isn't started, it's children shouldn't start too + dom.place(this.getChildDom(child), instance.containerNode); + } } _create(attrs: object, children: any[]) { @@ -42,10 +55,41 @@ export class WidgetContext extends Build children.forEach(x => this._addChild(x)); } + private _getInstance() { + if (!this._instance) + throw new Error("The instance of the widget isn't created"); + return this._instance; + } + _getDomNode() { if (!this._instance) throw new Error("The instance of the widget isn't created"); return this._instance.domNode; } + /** Overrides default placeAt implementation. Calls placeAt of the + * widget and then starts it. + * + * @param refNode A node or id of the node where the widget should be placed. + * @param position A position relative to refNode. + */ + placeAt(refNode: string | Node, position?: DojoNodePosition) { + this.ensureCreated(); + const instance = this._getInstance(); + if (typeof instance.placeAt === "function") { + instance.placeAt(refNode, position); + + // do we need to force widget startup? + if (typeof instance.startup === "function") + instance.startup(); + } else { + super.placeAt(refNode, position); + } + } + + getWidgetInstance() { + this.ensureCreated(); + return this._getInstance(); + } + } diff --git a/src/main/ts/tsx/traits.ts b/src/main/ts/tsx/traits.ts --- a/src/main/ts/tsx/traits.ts +++ b/src/main/ts/tsx/traits.ts @@ -59,5 +59,8 @@ export function isPlainObject(v: object) } export function isWidgetConstructor(v: any): v is _WidgetBaseConstructor { - return typeof v === "function" && v.prototype && "domNode" in v.prototype; + return typeof v === "function" && v.prototype && ( + "domNode" in v.prototype || + "buildRendering" in v.prototype + ); }