|
|
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 { queueRenderTask, getPriority, 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();
|
|
|
|
|
|
private readonly _priority = getPriority() + 1;
|
|
|
|
|
|
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);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/** Schedules a new deferred rendition within the scope of the widget */
|
|
|
scheduleRender(task: () => void) {
|
|
|
return queueRenderTask(task, this._scope, this._priority);
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
}
|
|
|
}
|
|
|
|