tsx.ts
196 lines
| 6.9 KiB
| video/mp2t
|
TypeScriptLexer
|
|
r96 | import { Constructor } from "@implab/core-amd/interfaces"; | ||
|
|
r65 | import { HtmlRendition } from "./tsx/HtmlRendition"; | ||
| import { WidgetRendition } from "./tsx/WidgetRendition"; | ||||
|
|
r102 | import { isElementNode, isWidget, isWidgetConstructor, Rendition } from "./tsx/traits"; | ||
|
|
r65 | import { FunctionRendition } from "./tsx/FunctionRendition"; | ||
|
|
r89 | import Stateful = require("dojo/Stateful"); | ||
| import _WidgetBase = require("dijit/_WidgetBase"); | ||||
| import { DjxWidgetBase } from "./tsx/DjxWidgetBase"; | ||||
|
|
r96 | import { WatchRendition } from "./tsx/WatchRendition"; | ||
|
|
r116 | import { Observable, observe, Subscribable } from "./observable"; | ||
|
|
r102 | import djAttr = require("dojo/dom-attr"); | ||
| import djClass = require("dojo/dom-class"); | ||||
|
|
r107 | import { AnimationAttrs, WatchForRendition } from "./tsx/WatchForRendition"; | ||
|
|
r116 | import { OrderedUpdate } from "./store"; | ||
|
|
r65 | |||
|
|
r109 | export function createElement<T extends Constructor | string | ((props: object) => Element)>(elementType: T, ...args: unknown[]): Rendition { | ||
|
|
r65 | 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") { | ||||
|
|
r109 | const ctx = new FunctionRendition(elementType as (props: unknown) => Element); | ||
|
|
r65 | if (args) | ||
| args.forEach(x => ctx.visitNext(x)); | ||||
| return ctx; | ||||
| } else { | ||||
|
|
r109 | throw new Error(`The element type '${String(elementType)}' is unsupported`); | ||
|
|
r65 | } | ||
| } | ||||
|
|
r109 | export interface EventDetails<T = unknown> { | ||
|
|
r65 | detail: T; | ||
| } | ||||
| export interface EventSelector { | ||||
| selectorTarget: HTMLElement; | ||||
| target: HTMLElement; | ||||
| } | ||||
|
|
r109 | export type DojoMouseEvent<T = unknown> = MouseEvent & EventSelector & EventDetails<T>; | ||
|
|
r89 | |||
|
|
r102 | type StatefulProps<T> = T extends Stateful<infer A> ? A : | ||
| T extends _WidgetBase ? T : never; | ||||
|
|
r89 | |||
| /** | ||||
| * 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, | ||||
|
|
r109 | render: (model: W[K]) => unknown | ||
|
|
r89 | ): 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, | ||||
|
|
r109 | render: (model: StatefulProps<T>[K]) => unknown | ||
|
|
r89 | ): Rendition; | ||
|
|
r102 | 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] | ||||
|
|
r89 | ) { | ||
|
|
r102 | if (args.length === 3) { | ||
| const [target, prop, render] = args; | ||||
| return new WatchRendition( | ||||
| render, | ||||
| observe(({next}) => { | ||||
|
|
r109 | const h = target.watch( | ||
|
|
r102 | 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); | ||||
| } | ||||
| } | ||||
|
|
r118 | export const watchFor = <T>(source: T[] | Subscribable<OrderedUpdate<T>> | null | undefined, render: (item: T, index: number) => unknown, opts: AnimationAttrs = {}) => { | ||
|
|
r107 | return new WatchForRendition({ | ||
| ...opts, | ||||
| subject: source, | ||||
| component: render | ||||
| }); | ||||
|
|
r109 | }; | ||
|
|
r107 | |||
|
|
r102 | 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(); | ||||
|
|
r109 | }); | ||
|
|
r102 | }; | ||
|
|
r101 | |||
|
|
r102 | 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() { } }; | ||||
|
|
r134 | return (el: Element | { set(name: K, value: T, priority?: boolean): void; } | undefined) => { | ||
|
|
r102 | if (el) { | ||
| if (isElementNode(el)) { | ||||
| h = subj.subscribe({ | ||||
| next: value => djAttr.set(el, attr, value) | ||||
| }); | ||||
| } else { | ||||
| h = subj.subscribe({ | ||||
|
|
r134 | next: value => el.set(attr, value, false) | ||
|
|
r102 | }); | ||
| } | ||||
| } else { | ||||
| h.unsubscribe(); | ||||
| } | ||||
|
|
r109 | }; | ||
|
|
r102 | }; | ||
|
|
r120 | /** 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 | ||||
| */ | ||||
|
|
r102 | 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({ | ||||
|
|
r120 | next: v => djClass.toggle(el, className, v || false) | ||
|
|
r102 | }); | ||
| } else { | ||||
| h.unsubscribe(); | ||||
| } | ||||
|
|
r109 | }; | ||
| }; | ||||
|
|
r89 | |||
|
|
r120 | /** Combines multiple hooks to the single one */ | ||
|
|
r102 | export const all = <T, A extends JSX.Ref<T>[]>(...cbs: A): JSX.Ref<T> => (arg: T | undefined) => cbs.forEach(cb => cb(arg)); | ||
|
|
r89 | /** 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, | ||||
|
|
r109 | T extends DjxWidgetBase<object, { [p in E]: EV }>, | ||
|
|
r89 | EV extends Event | ||
| >( | ||||
| target: T, | ||||
| key: K, | ||||
|
|
r109 | // eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
|
|
r89 | _descriptor: TypedPropertyDescriptor<(eventObj: EV) => void> | TypedPropertyDescriptor<() => void> | ||
|
|
r109 | ) => { | ||
|
|
r93 | const handlers = eventNames.map(eventName => ({ eventName, handlerMethod: key })); | ||
|
|
r89 | target._eventHandlers = target._eventHandlers ? target._eventHandlers.concat(handlers) : handlers; | ||
| }; | ||||
