##// END OF EJS Templates
Added tag v1.6.1 for changeset e07418577cbc
Added tag v1.6.1 for changeset e07418577cbc

File last commit:

r118:e07418577cbc v1.6.1 default
r119:289f4698a954 default
Show More
render.ts
195 lines | 5.1 KiB | video/mp2t | TypeScriptLexer
cin
Testing nested watch, release candidate
r101 import { TraceSource } from "@implab/core-amd/log/TraceSource";
import { isPromise } from "@implab/core-amd/safe";
import { id as mid } from "module";
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102 import { IScope, Scope } from "./Scope";
import { isNode, isRendition, isWidget } from "./traits";
cin
Testing nested watch, release candidate
r101
const trace = TraceSource.get(mid);
cin
file rename
r98
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102 interface Context {
cin
added whenRendered() method to wait for pending oprations to complete
r118 readonly scope: IScope;
cin
file rename
r98
cin
added whenRendered() method to wait for pending oprations to complete
r118 readonly hooks?: (() => void)[];
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102 }
cin
Testing nested watch, release candidate
r101
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102 let _context: Context = {
scope: Scope.dummy
cin
linting
r109 };
cin
Testing nested watch, release candidate
r101
cin
added whenRendered() method to wait for pending oprations to complete
r118 let _renderCount = 0;
let _renderId = 1;
let _renderedHooks: (() => void)[] = [];
cin
Testing nested watch, release candidate
r101 const guard = (cb: () => unknown) => {
try {
cin
linting
r109 const result = cb();
cin
Testing nested watch, release candidate
r101 if (isPromise(result)) {
const warn = (ret: unknown) => trace.error("The callback {0} competed asynchronously. result = {1}", cb, ret);
result.then(warn, warn);
}
} catch (e) {
trace.error(e);
}
cin
linting
r109 };
cin
file rename
r98
cin
added whenRendered() method to wait for pending oprations to complete
r118 /**
*
* @param scope
* @returns
*/
export const beginRender = (scope = getScope()) => {
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102 const prev = _context;
cin
added whenRendered() method to wait for pending oprations to complete
r118 _renderCount++;
const renderId = _renderId++;
trace.debug("beginRender [{0}], pending = {1}", renderId, _renderCount);
if (_renderCount === 1)
onRendering();
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102 _context = {
scope,
hooks: []
};
cin
added whenRendered() method to wait for pending oprations to complete
r118 return endRender(prev, _context, renderId);
};
/**
* Method for a deferred rendering. Returns a promise with `beginRender()` function.
* Call to `scheduleRender` will save the current context, and will increment pending
* operations counter.
*
* @example
*
* const begin = await scheduleRender();
* const end = begin();
* try {
* // do some DOM manipulations
* } finally {
* end();
* }
*
* @param scope
* @returns
*/
export const scheduleRender = async (scope = getScope()) => {
const prev = _context;
_renderCount++;
const renderId = _renderId ++;
trace.debug("scheduleRender [{0}], pending = {1}", renderId, _renderCount);
if (_renderCount === 1)
onRendering();
await Promise.resolve();
return () => {
trace.debug("beginRender [{0}], pending = {1}", renderId, _renderCount);
_context = {
scope,
hooks: []
};
return endRender(prev, _context, renderId);
};
cin
linting
r109 };
cin
Testing nested watch, release candidate
r101
/**
* Completes render operation
*/
cin
added whenRendered() method to wait for pending oprations to complete
r118 const endRender = (prev: Context, current: Context, renderId: number) => () => {
if (_context !== current)
trace.error("endRender mismatched beginRender call");
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102 const { hooks } = _context;
if (hooks)
cin
Testing nested watch, release candidate
r101 hooks.forEach(guard);
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102
cin
added whenRendered() method to wait for pending oprations to complete
r118 _renderCount--;
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102 _context = prev;
cin
added whenRendered() method to wait for pending oprations to complete
r118
trace.debug("endRender [{0}], pending = {1}", renderId, _renderCount);
if (_renderCount === 0)
onRendered();
cin
linting
r109 };
cin
Testing nested watch, release candidate
r101
cin
added whenRendered() method to wait for pending oprations to complete
r118 // called when the first beginRender is called for this iteration
const onRendering = () => {
setTimeout(() => {
if (_renderCount !== 0)
trace.error("Rendering tasks aren't finished, currently running = {0}", _renderCount);
});
};
// called when all render operations are complete
const onRendered = () => {
_renderedHooks.forEach(guard);
_renderedHooks = [];
};
export const whenRendered = () => new Promise<void>((resolve) => {
if (_renderCount)
_renderedHooks.push(resolve);
else
resolve();
});
cin
Testing nested watch, release candidate
r101 export const renderHook = (hook: () => void) => {
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102 const { hooks } = _context;
if (hooks)
cin
Testing nested watch, release candidate
r101 hooks.push(hook);
else
guard(hook);
cin
linting
r109 };
cin
file rename
r98
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102 export const refHook = <T>(value: T, ref: JSX.Ref<T>) => {
const { hooks, scope } = _context;
if (hooks)
hooks.push(() => ref(value));
else
guard(() => ref(value));
scope.own(() => ref(undefined));
cin
linting
r109 };
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102
cin
file rename
r98 /** Returns the current scope */
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102 export const getScope = () => _context.scope;
cin
file rename
r98
/** Schedules the rendition to be rendered to the DOM Node
* @param rendition The rendition to be rendered
* @param scope The scope
*/
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102 export const render = (rendition: unknown, scope = Scope.dummy) => {
const complete = beginRender(scope);
cin
file rename
r98 try {
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102 return getItemDom(rendition);
cin
file rename
r98 } finally {
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102 complete();
cin
file rename
r98 }
cin
linting
r109 };
cin
file rename
r98
/** Renders DOM element for different types of the argument. */
cin
Testing nested watch, release candidate
r101 export const getItemDom = (v: unknown) => {
cin
file rename
r98 if (typeof v === "string" || typeof v === "number" || v instanceof RegExp || v instanceof Date) {
// primitive types converted to the text nodes
return document.createTextNode(v.toString());
} else if (isNode(v)) {
// nodes are kept as is
return v;
} else if (isRendition(v)) {
// renditions are instantiated
cin
Testing nested watch, release candidate
r101 return v.getDomNode();
cin
file rename
r98 } else if (isWidget(v)) {
// widgets are converted to it's markup
return v.domNode;
} else if (typeof v === "boolean" || v === null || v === undefined) {
cin
`Subscribable` is made compatible with rxjs, added map, filter and scan...
r102 // null | undefined | boolean are removed
return document.createDocumentFragment();
cin
file rename
r98 } else if (v instanceof Array) {
// arrays will be translated to document fragments
const fragment = document.createDocumentFragment();
cin
Testing nested watch, release candidate
r101 v.map(item => getItemDom(item))
cin
file rename
r98 .forEach(node => fragment.appendChild(node));
return fragment;
} else {
// bug: explicit error otherwise
cin
linting
r109 throw new Error(`Invalid parameter: ${String(v)}`);
cin
file rename
r98 }
cin
linting
r109 };