##// END OF EJS Templates
Corrected Scope.own() to cleanup the supplied object immediately when the scope is disposed already
Corrected Scope.own() to cleanup the supplied object immediately when the scope is disposed already

File last commit:

r112:2ccfaae984e9 v1.4.4 default
r131:c7d9ad82b374 v1.8.1 default
Show More
DjxWidgetBase.ts
135 lines | 4.9 KiB | video/mp2t | TypeScriptLexer
/ djx / src / main / ts / tsx / DjxWidgetBase.ts
import { djbase, djclass } from "../declare";
import _WidgetBase = require("dijit/_WidgetBase");
import _AttachMixin = require("dijit/_AttachMixin");
import { isNode, isElementNode } from "./traits";
import registry = require("dijit/registry");
import on = require("dojo/on");
import { Scope } from "./Scope";
import { render } from "./render";
import { isNull } from "@implab/core-amd/safe";
// type Handle = dojo.Handle;
export interface EventArgs {
bubbles?: boolean;
cancelable?: boolean;
composed?: boolean;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export interface DjxWidgetBase<Attrs = object, Events extends { [name in keyof Events]: Event } = object> extends
_WidgetBase<Events> {
/** This property is declared only for type inference to work, it is never assigned
* and should not be used.
*/
readonly _eventMap: Events & GlobalEventHandlersEventMap;
/** The list of pairs of event and method names. When the widget is created all methods from
* this list will be connected to corresponding events.
*
* This property is maintained in the prototype
*/
_eventHandlers: Array<{
eventName: string,
handlerMethod: string;
}>;
}
type _super = {
startup(): void;
destroy(preserveDom?: boolean): void;
};
@djclass
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export abstract class DjxWidgetBase<Attrs = object, Events = object> extends djbase<_super, _AttachMixin>(_WidgetBase, _AttachMixin) {
private readonly _scope = new Scope();
buildRendering() {
const node = render(this.render(), this._scope);
if (!isElementNode(node))
throw new Error("The render method must return a single DOM element");
this.domNode = node as HTMLElement;
super.buildRendering();
// now we should get assigned data-dojo-attach-points
// place the contents of the original srcNode to the containerNode
const src = this.srcNodeRef;
const dest = this.containerNode;
// the donNode is constructed now we need to connect event handlers
this._connectEventHandlers();
if (src && dest) {
while (src.firstChild)
dest.appendChild(src.firstChild);
}
}
abstract render(): JSX.Element;
private _connectEventHandlers() {
if (this._eventHandlers)
this._eventHandlers.forEach(({ eventName, handlerMethod }) => {
const handler = this[handlerMethod as keyof this];
if (typeof handler === "function")
on(this.domNode, eventName, handler.bind(this) as (...args: unknown[]) => unknown);
});
}
_processTemplateNode<T extends (Element | Node | _WidgetBase)>(
baseNode: T,
getAttrFunc: (baseNode: T, attr: string) => string | undefined,
// tslint:disable-next-line: ban-types
attachFunc: (node: T, type: string, func?: (...args: unknown[]) => unknown) => dojo.Handle
): boolean {
if (isNode(baseNode)) {
const w = registry.byNode(baseNode);
if (w) {
// from dijit/_WidgetsInTemplateMixin
this._processTemplateNode(w,
(n, p) => {
const v = n.get(p as keyof typeof n);
return isNull(v) ? undefined : String(v);
}, // callback to get a property of a widget
(widget, type, callback) => {
if (!callback)
throw new Error("The callback must be specified");
// callback to do data-dojo-attach-event to a widget
if (type in widget) {
// back-compat, remove for 2.0
return widget.connect(widget, type, callback as EventListener);
} else {
// 1.x may never hit this branch, but it's the default for 2.0
return widget.on(type as keyof GlobalEventHandlersEventMap, callback);
}
});
// don't process widgets internals
return false;
}
}
// eslint-disable-next-line @typescript-eslint/ban-types
return super._processTemplateNode(baseNode, getAttrFunc as (baseNode: T, attr: string) => string, attachFunc as (node: T, type: string, func?: Function) => dojo.Handle);
}
/** Starts current widget and all its supporting widgets (placed outside
* `containerNode`) and child widgets (placed inside `containerNode`)
*/
startup() {
// startup supporting widgets
registry.findWidgets(this.domNode, this.containerNode).forEach(w => w.startup());
super.startup();
}
destroy(preserveDom?: boolean) {
this._scope.destroy();
super.destroy(preserveDom);
}
}