|
|
import { argumentNotNull } from "@implab/core-amd/safe";
|
|
|
import { RenditionBase } from "./RenditionBase";
|
|
|
import { DojoNodePosition, isElementNode, isInPage, isWidget, placeAt } from "./traits";
|
|
|
import registry = require("dijit/registry");
|
|
|
import ContentPane = require("dijit/layout/ContentPane");
|
|
|
import { IScope } from "./Scope";
|
|
|
import { getItemDom, getScope } from "./render";
|
|
|
|
|
|
// tslint:disable-next-line: class-name
|
|
|
export interface _Widget {
|
|
|
domNode: Node;
|
|
|
|
|
|
containerNode?: Node;
|
|
|
|
|
|
placeAt?(refNode: string | Node, position?: DojoNodePosition): void;
|
|
|
startup?(): void;
|
|
|
|
|
|
addChild?(widget: any, index?: number): void;
|
|
|
}
|
|
|
|
|
|
export type _WidgetCtor = new (attrs: any, srcNode?: string | Node) => _Widget;
|
|
|
|
|
|
export class WidgetRendition extends RenditionBase<Node> {
|
|
|
readonly widgetClass: _WidgetCtor;
|
|
|
|
|
|
_instance: _Widget | undefined;
|
|
|
|
|
|
constructor(widgetClass: _WidgetCtor) {
|
|
|
super();
|
|
|
argumentNotNull(widgetClass, "widgetClass");
|
|
|
|
|
|
this.widgetClass = widgetClass;
|
|
|
}
|
|
|
|
|
|
_addChild(child: any, scope: IScope): void {
|
|
|
const instance = this._getInstance();
|
|
|
|
|
|
if (instance.addChild) {
|
|
|
if (child instanceof WidgetRendition) {
|
|
|
// layout containers add custom logic to addChild methods
|
|
|
instance.addChild(child.getWidgetInstance(scope));
|
|
|
} else if (isWidget(child)) {
|
|
|
instance.addChild(child);
|
|
|
} else {
|
|
|
const childDom = getItemDom(child, scope);
|
|
|
const w = isElementNode(childDom) ? registry.byNode(childDom) : undefined;
|
|
|
|
|
|
if (w) {
|
|
|
instance.addChild(w);
|
|
|
} else {
|
|
|
if (!instance.containerNode)
|
|
|
throw new Error("Failed to add DOM content. The widget doesn't have a containerNode");
|
|
|
|
|
|
// the current widget isn't started, it's children shouldn't start too
|
|
|
placeAt(getItemDom(child,scope), instance.containerNode, "last");
|
|
|
}
|
|
|
}
|
|
|
} 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
|
|
|
placeAt(getItemDom(child, scope), instance.containerNode, "last");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
protected _create(attrs: any, children: any[], scope: IScope) {
|
|
|
if (this.widgetClass.prototype instanceof ContentPane) {
|
|
|
// a special case for the ContentPane this is for
|
|
|
// the compatibility with this heavy widget, all
|
|
|
// regular containers could be easily manipulated
|
|
|
// through `containerNode` property or `addChild` method.
|
|
|
|
|
|
// render children to the DocumentFragment
|
|
|
const content = document.createDocumentFragment();
|
|
|
children.forEach(child => content.appendChild(getItemDom(child, scope)));
|
|
|
|
|
|
// set the content property to the parameters of the widget
|
|
|
const _attrs = { ...attrs, content };
|
|
|
this._instance = new this.widgetClass(_attrs);
|
|
|
} else {
|
|
|
this._instance = new this.widgetClass(attrs);
|
|
|
children.forEach(x => this._addChild(x, scope));
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
private _getInstance() {
|
|
|
if (!this._instance)
|
|
|
throw new Error("The instance of the widget isn't created");
|
|
|
return this._instance;
|
|
|
}
|
|
|
|
|
|
protected _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(getScope());
|
|
|
const instance = this._getInstance();
|
|
|
if (typeof instance.placeAt === "function") {
|
|
|
instance.placeAt(refNode, position);
|
|
|
|
|
|
// fix the dojo startup behavior when the widget is placed
|
|
|
// directly to the document and doesn't have any enclosing widgets
|
|
|
const parentWidget = instance.domNode.parentNode ?
|
|
|
registry.getEnclosingWidget(instance.domNode.parentNode) : null;
|
|
|
if (!parentWidget && isInPage(instance.domNode) && typeof instance.startup === "function")
|
|
|
instance.startup();
|
|
|
} else {
|
|
|
// the widget doesn't have a placeAt method, strange but whatever
|
|
|
super.placeAt(refNode, position);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
getWidgetInstance(scope?: IScope) {
|
|
|
this.ensureCreated(scope || getScope());
|
|
|
return this._getInstance();
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|