traits.ts
228 lines
| 7.3 KiB
| video/mp2t
|
TypeScriptLexer
cin
|
r71 | import { IDestroyable, IRemovable } from "@implab/core-amd/interfaces"; | ||
cin
|
r65 | import { isDestroyable } from "@implab/core-amd/safe"; | ||
import _WidgetBase = require("dijit/_WidgetBase"); | ||||
import registry = require("dijit/registry"); | ||||
import dom = require("dojo/dom-construct"); | ||||
cin
|
r71 | import Stateful = require("dojo/Stateful"); | ||
import { FunctionRendition } from "./FunctionRendition"; | ||||
cin
|
r72 | import { DjxWidgetBase } from "./DjxWidgetBase"; | ||
cin
|
r65 | |||
type _WidgetBaseConstructor = typeof _WidgetBase; | ||||
export type DojoNodePosition = "first" | "after" | "before" | "last" | "replace" | "only" | number; | ||||
export interface Rendition<TNode extends Node = Node> { | ||||
getDomNode(): TNode; | ||||
placeAt(refNode: string | Node, position?: DojoNodePosition): void; | ||||
} | ||||
cin
|
r72 | /** | ||
cin
|
r65 | * @deprecated use Rendition | ||
*/ | ||||
export type BuildContext<TNode extends Node = Node> = Rendition<TNode>; | ||||
export interface IRecursivelyDestroyable { | ||||
destroyRecursive(): void; | ||||
} | ||||
export function isNode(el: any): el is Node { | ||||
return el && el.nodeName && el.nodeType; | ||||
} | ||||
export function isElementNode(el: any): el is Element { | ||||
return isNode(el) && el.nodeType === 1; | ||||
} | ||||
export function isTextNode(el: any): el is Text { | ||||
return isNode(el) && el.nodeType === 3; | ||||
} | ||||
export function isProcessingInstructionNode(el: any): el is ProcessingInstruction { | ||||
return isNode(el) && el.nodeType === 7; | ||||
} | ||||
export function isCommentNode(el: any): el is Comment { | ||||
return isNode(el) && el.nodeType === 8; | ||||
} | ||||
export function isDocumentNode(el: any): el is Document { | ||||
return isNode(el) && el.nodeType === 9; | ||||
} | ||||
export function isDocumentTypeNode(el: any): el is DocumentType { | ||||
return isNode(el) && el.nodeType === 10; | ||||
} | ||||
export function isDocumentFragmentNode(el: any): el is DocumentFragment { | ||||
return isNode(el) && el.nodeType === 11; | ||||
} | ||||
export function isWidget(v: any): v is _WidgetBase { | ||||
return v && "domNode" in v; | ||||
} | ||||
export function isRendition(v: any): v is Rendition { | ||||
cin
|
r87 | return v && typeof v.getDomElement === "function"; | ||
cin
|
r65 | } | ||
/** | ||||
* @deprecated use isRendition | ||||
*/ | ||||
export const isBuildContext = isRendition; | ||||
export function isPlainObject(v: object) { | ||||
if (typeof v !== "object") | ||||
return false; | ||||
const vp = Object.getPrototypeOf(v); | ||||
return !vp || vp === Object.prototype; | ||||
} | ||||
export function isWidgetConstructor(v: any): v is _WidgetBaseConstructor { | ||||
return typeof v === "function" && v.prototype && ( | ||||
"domNode" in v.prototype || | ||||
"buildRendering" in v.prototype | ||||
); | ||||
} | ||||
/** Tests whether the specified node is placed in visible dom. | ||||
* @param {Node} node The node to test | ||||
*/ | ||||
export function isInPage(node: Node) { | ||||
return (node === document.body) ? false : document.body.contains(node); | ||||
} | ||||
export function isRecursivelyDestroyable(target: any): target is IRecursivelyDestroyable { | ||||
return target && typeof target.destroyRecursive === "function"; | ||||
} | ||||
/** Destroys DOM Node with all contained widgets. | ||||
* If the specified node is the root node of a widget, then the | ||||
* widget will be destroyed. | ||||
* | ||||
* @param target DOM Node or widget to destroy | ||||
*/ | ||||
export function destroy(target: Node | IDestroyable | IRecursivelyDestroyable) { | ||||
if (isRecursivelyDestroyable(target)) { | ||||
target.destroyRecursive(); | ||||
} else if (isDestroyable(target)) { | ||||
target.destroy(); | ||||
} else if (isNode(target)) { | ||||
cin
|
r75 | const w = isElementNode(target) ? registry.byNode(target) : undefined; | ||
if (w) { | ||||
w.destroyRecursive(); | ||||
cin
|
r65 | } else { | ||
registry.findWidgets(target).forEach(destroy); | ||||
dom.destroy(target); | ||||
} | ||||
} | ||||
} | ||||
/** Empties a content of the specified node and destroys all contained widgets. | ||||
* | ||||
* @param target DOM node to . | ||||
*/ | ||||
export function emptyNode(target: Node) { | ||||
registry.findWidgets(target).forEach(destroy); | ||||
dom.empty(target); | ||||
} | ||||
/** This function starts all widgets inside the DOM node if the target is a node | ||||
* or starts widget itself if the target is the widget. If the specified node | ||||
* associated with the widget that widget will be started. | ||||
cin
|
r72 | * | ||
cin
|
r65 | * @param target DOM node to find and start widgets or the widget itself. | ||
*/ | ||||
export function startupWidgets(target: Node | _WidgetBase, skipNode?: Node) { | ||||
if (isNode(target)) { | ||||
cin
|
r69 | const w = isElementNode(target) ? registry.byNode(target) : undefined; | ||
cin
|
r65 | if (w) { | ||
cin
|
r69 | if (w.startup) | ||
w.startup(); | ||||
cin
|
r65 | } else { | ||
cin
|
r69 | registry.findWidgets(target, skipNode).forEach(x => x.startup()); | ||
cin
|
r65 | } | ||
} else { | ||||
cin
|
r72 | if (target.startup) | ||
cin
|
r69 | target.startup(); | ||
cin
|
r65 | } | ||
cin
|
r71 | } | ||
type StatefulProps<T> = T extends Stateful<infer A> ? A : never; | ||||
type CleanFn = (instance: IRemovable | IDestroyable) => void; | ||||
/** | ||||
* Observers the property and calls render callback each change. | ||||
* | ||||
* @param target The target object which property will be observed. | ||||
* @param prop The name of the property. | ||||
* @param render The callback which will be called every time the value is changed | ||||
* @param cleanupOrOwner The object with method `own` or an callback to register lifecycle for the observer. | ||||
* @returns Rendition which is created instantly | ||||
*/ | ||||
cin
|
r72 | export function watch<W extends _WidgetBase, K extends keyof W>( | ||
cin
|
r71 | target: W, | ||
prop: K, | ||||
render: (model: W[K]) => any, | ||||
cin
|
r72 | cleanupOrOwner?: { own: CleanFn } | CleanFn | ||
cin
|
r71 | ): Rendition; | ||
/** | ||||
* Observers the property and calls render callback each change. | ||||
* | ||||
* @param target The target object which property will be observed. | ||||
* @param prop The name of the property. | ||||
* @param render The callback which will be called every time the value is changed | ||||
* @param cleanupOrOwner The object with method `own` or an callback to register lifecycle for the observer. | ||||
* @returns Rendition which is created instantly | ||||
*/ | ||||
export function watch<T extends Stateful, K extends keyof StatefulProps<T>>( | ||||
target: T, | ||||
prop: K, | ||||
render: (model: StatefulProps<T>[K]) => any, | ||||
cin
|
r72 | cleanupOrOwner?: { own: CleanFn } | CleanFn | ||
cin
|
r71 | ): Rendition; | ||
export function watch<T extends Stateful, K extends keyof StatefulProps<T> & string>( | ||||
target: T, | ||||
prop: K, | ||||
cin
|
r72 | render: (model: StatefulProps<T>[K]) => any, | ||
cleanupOrOwner: { own: CleanFn } | CleanFn = () => { } | ||||
cin
|
r71 | ) { | ||
let rendition = new FunctionRendition(() => render(target.get(prop))); | ||||
cin
|
r79 | const _own = cleanupOrOwner instanceof Function ? cleanupOrOwner : (x: IRemovable) => cleanupOrOwner.own(x); | ||
cin
|
r71 | _own(target.watch(prop, (_name, oldValue, newValue) => { | ||
if (oldValue !== newValue) { | ||||
const newRendition = new FunctionRendition(() => render(newValue)); | ||||
newRendition.placeAt(rendition.getDomNode(), "replace"); | ||||
destroy(rendition.getDomNode()); | ||||
rendition = newRendition; | ||||
} | ||||
})); | ||||
return rendition; | ||||
} | ||||
cin
|
r72 | |||
cin
|
r73 | /** Decorates the method which will be registered as the handle for the specified event. | ||
* This decorator can be applied to DjxWidgetBase subclass methods. | ||||
* | ||||
* ``` | ||||
* @on("click") | ||||
* _onClick(eventObj: MouseEvent) { | ||||
* // ... | ||||
* } | ||||
* ``` | ||||
*/ | ||||
cin
|
r85 | export const on = <E extends string>(...eventNames: E[]) => | ||
cin
|
r79 | <K extends string, | ||
cin
|
r73 | T extends DjxWidgetBase<any, { [p in E]: EV }>, | ||
EV extends Event | ||||
cin
|
r72 | >( | ||
target: T, | ||||
key: K, | ||||
cin
|
r79 | _descriptor: TypedPropertyDescriptor<(eventObj: EV) => void> | TypedPropertyDescriptor<() => void> | ||
cin
|
r72 | ): any => { | ||
cin
|
r85 | const handlers = eventNames.map(eventName => ({ eventName, handlerMethod: key })) | ||
target._eventHandlers = target._eventHandlers ? target._eventHandlers.concat(handlers) : handlers; | ||||
cin
|
r72 | }; | ||