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

r122:fb2ea4d6aaba v1.6.3 default
r131:c7d9ad82b374 v1.8.1 default
Show More
render.ts
198 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()) => {
_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);
cin
Fixed scheduleRender context restoration
r122 const prev = _context;
cin
added whenRendered() method to wait for pending oprations to complete
r118 _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
cin
type fixes for i18n, store
r120 const emptyFragment = document.createDocumentFragment();
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
cin
type fixes for i18n, store
r120 return emptyFragment;
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 };