##// 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:

r120:bc1b4dd8ca1a v1.6.2 default
r131:c7d9ad82b374 v1.8.1 default
Show More
tsx.ts
196 lines | 6.9 KiB | video/mp2t | TypeScriptLexer
import { Constructor } from "@implab/core-amd/interfaces";
import { HtmlRendition } from "./tsx/HtmlRendition";
import { WidgetRendition } from "./tsx/WidgetRendition";
import { isElementNode, isWidget, isWidgetConstructor, Rendition } from "./tsx/traits";
import { FunctionRendition } from "./tsx/FunctionRendition";
import Stateful = require("dojo/Stateful");
import _WidgetBase = require("dijit/_WidgetBase");
import { DjxWidgetBase } from "./tsx/DjxWidgetBase";
import { WatchRendition } from "./tsx/WatchRendition";
import { Observable, observe, Subscribable } from "./observable";
import djAttr = require("dojo/dom-attr");
import djClass = require("dojo/dom-class");
import { AnimationAttrs, WatchForRendition } from "./tsx/WatchForRendition";
import { OrderedUpdate } from "./store";
export function createElement<T extends Constructor | string | ((props: object) => Element)>(elementType: T, ...args: unknown[]): Rendition {
if (typeof elementType === "string") {
const ctx = new HtmlRendition(elementType);
if (args)
args.forEach(x => ctx.visitNext(x));
return ctx;
} else if (isWidgetConstructor(elementType)) {
const ctx = new WidgetRendition(elementType);
if (args)
args.forEach(x => ctx.visitNext(x));
return ctx;
} else if (typeof elementType === "function") {
const ctx = new FunctionRendition(elementType as (props: unknown) => Element);
if (args)
args.forEach(x => ctx.visitNext(x));
return ctx;
} else {
throw new Error(`The element type '${String(elementType)}' is unsupported`);
}
}
export interface EventDetails<T = unknown> {
detail: T;
}
export interface EventSelector {
selectorTarget: HTMLElement;
target: HTMLElement;
}
export type DojoMouseEvent<T = unknown> = MouseEvent & EventSelector & EventDetails<T>;
type StatefulProps<T> = T extends Stateful<infer A> ? A :
T extends _WidgetBase ? T : never;
/**
* 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
* @returns Rendition which is created instantly
*/
export function watch<W extends _WidgetBase, K extends keyof W>(
target: W,
prop: K,
render: (model: W[K]) => unknown
): 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
* @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]) => unknown
): Rendition;
export function watch<V>(subj: Subscribable<V>, render: (model: V) => unknown): Rendition;
export function watch(
...args: [Stateful, string, (model: unknown) => unknown] |
[Subscribable<unknown>, (model: unknown) => unknown]
) {
if (args.length === 3) {
const [target, prop, render] = args;
return new WatchRendition(
render,
observe(({next}) => {
const h = target.watch(
prop,
(_prop, oldValue, newValue) => oldValue !== newValue && next(newValue)
);
next(target.get(prop));
return () => h.remove();
})
);
} else {
const [subj, render] = args;
return new WatchRendition(render, subj);
}
}
export const watchFor = <T>(source: T[] | Subscribable<OrderedUpdate<T>> | null | undefined, render: (item: T, index: number) => unknown, opts: AnimationAttrs = {}) => {
return new WatchForRendition({
...opts,
subject: source,
component: render
});
};
export const prop: {
<T extends Stateful, K extends string & keyof StatefulProps<T>>(target: T, name: K): Observable<StatefulProps<T>[K]>;
<T extends _WidgetBase, K extends keyof T>(target: T, name: K): Observable<T[K]>;
} = (target: Stateful, name: string) => {
return observe(({next}) => {
const h = target.watch(
name,
(_prop, oldValue, newValue) => oldValue !== newValue && next(newValue)
);
next(target.get(name));
return () => h.remove();
});
};
export const attach = <W extends DjxWidgetBase, K extends keyof W>(target: W, name: K) => (v: W[K]) => target.set(name, v);
export const bind = <K extends string, T>(attr: K, subj: Subscribable<T>) => {
let h = { unsubscribe() { } };
return (el: Element | { set(name: K, value: T): void; } | undefined) => {
if (el) {
if (isElementNode(el)) {
h = subj.subscribe({
next: value => djAttr.set(el, attr, value)
});
} else {
h = subj.subscribe({
next: value => el.set(attr, value)
});
}
} else {
h.unsubscribe();
}
};
};
/** Creates refHook to toggle the specified css class on the target element
*
* @param className
* @param subj a boolean observable value. When the value is false the className
* is removed, when the value is true, the className is added to the target element
* @returns refHook
*/
export const toggleClass = (className: string, subj: Subscribable<boolean>) => {
let h = { unsubscribe() { } };
return (elOrWidget: HTMLElement | _WidgetBase | undefined) => {
const el = isWidget(elOrWidget) ? elOrWidget.domNode : elOrWidget;
if (el) {
h = subj.subscribe({
next: v => djClass.toggle(el, className, v || false)
});
} else {
h.unsubscribe();
}
};
};
/** Combines multiple hooks to the single one */
export const all = <T, A extends JSX.Ref<T>[]>(...cbs: A): JSX.Ref<T> => (arg: T | undefined) => cbs.forEach(cb => cb(arg));
/** 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) {
* // ...
* }
* ```
*/
export const on = <E extends string>(...eventNames: E[]) =>
<K extends string,
T extends DjxWidgetBase<object, { [p in E]: EV }>,
EV extends Event
>(
target: T,
key: K,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_descriptor: TypedPropertyDescriptor<(eventObj: EV) => void> | TypedPropertyDescriptor<() => void>
) => {
const handlers = eventNames.map(eventName => ({ eventName, handlerMethod: key }));
target._eventHandlers = target._eventHandlers ? target._eventHandlers.concat(handlers) : handlers;
};