@frontmeans/drek
Version:
Document render kit
695 lines (677 loc) • 26.5 kB
TypeScript
/// <reference lib="dom" />
/// <reference lib="es2019" />
declare module "@frontmeans/drek" {
/**
* A rendered content status.
*/
export interface DrekContentStatus {
/**
* Whether the content connected to the document.
*
* This is always `true` for document content, but may be `false` e.g. until a fragment is {@link DrekFragment.render
* rendered}.
*/
readonly connected: boolean;
readonly withinFragment?: unknown | undefined;
}
}
declare module "@frontmeans/drek" {
import { AfterEvent, AfterEvent__symbol, EventKeeper, OnEvent } from "@proc7ts/fun-events";
/**
* A rendered content placement.
*
* @typeParam TStatus - A type of the tuple containing a rendered content status as its first element.
*/
export abstract class DrekPlacement<TStatus extends [
DrekContentStatus
] = [
DrekContentStatus
]> implements EventKeeper<TStatus> {
/**
* An `AfterEvent` keeper of content placement status.
*/
abstract readonly readStatus: AfterEvent<TStatus>;
constructor();
/**
* A {@link DrekFragment fragment} the content is placed to, if any.
*/
abstract readonly fragment: DrekFragment | undefined;
/**
* An alias of {@link readStatus}.
*
* @returns An `AfterEvent` keeper of content placement status.
*/
[AfterEvent__symbol](): AfterEvent<TStatus>;
/**
* An `OnEvent` sender of placed content connection event.
*
* The registered receiver is called when placed content is {@link DrekContentStatus.connected connected}.
* If connected already the receiver is called immediately.
*/
get onceConnected(): OnEvent<TStatus>;
/**
* An `OnEvent` sender of single placed content connection event.
*
* The registered receiver is called when placed content is {@link DrekContentStatus.connected connected}.
* If connected already the receiver is called immediately.
*
* In contrast to {@link onceConnected}, cuts off the event supply after sending the first event.
*/
get whenConnected(): OnEvent<TStatus>;
}
}
declare module "@frontmeans/drek" {
/**
* Obtains an updatable rendering context of the given document.
*
* @param document - Target document.
*
* @returns Updatable document rendering context.
*/
export function drekContextOf(document: Document): DrekContext.Updatable;
/**
* Obtains a rendering context of the given node.
*
* If the node is connected to document, then the rendering context of that document is returned. Otherwise, if the node
* belongs to the {@link DrekFragment.content content} of the rendered fragment, then the context
* {@link DrekFragment.innerContext provided} by that fragment is returned. Otherwise, an unrooted context is created
* and attached to the [root node] of the target `node`.
*
* [root node]: https://developer.mozilla.org/en-US/docs/Web/API/Node/getRootNode
*
* @param node - Target node.
*
* @returns Target node rendering context.
*/
export function drekContextOf(node: Node): DrekContext;
}
declare module "@frontmeans/drek" {
/**
* Rendering target.
*
* Represents a part of the DOM tree to place the rendered content to.
*
* @typeParam TStatus - A tuple type reflecting a content {@link DrekContentStatus placement status}.
*/
export interface DrekTarget<TStatus extends [
DrekContentStatus
] = [
DrekContentStatus
]> {
/**
* Rendering context of this rendering target.
*/
readonly context: DrekContext;
/**
* A DOM node that serves as a host of {@link placeContent placed content}.
*/
readonly host: Node;
/**
* Places rendered content to this target.
*
* It is up to the implementation to decide how the content is placed. E.g. some implementations append the content,
* while the others replace it.
*
* It is expected that this operation is performed within a render scheduler the user chooses, probably the one of
* rendering {@link context}.
*
* @param content - Rendered DOM node to place.
*
* @returns Rendered content placement status.
*/
placeContent(content: Node): DrekPlacement<TStatus>;
}
}
declare module "@frontmeans/drek" {
/**
* Creates a rendering target that appends content to parent node.
*
* @param host - A node to append content to.
* @param context - Custom rendering context. Defaults to `host` node context.
*
* @returns Rendering target.
*/
export function drekAppender(host: Node, context?: DrekContext): DrekTarget;
}
declare module "@frontmeans/drek" {
/**
* Creates a rendering target that charges rendered content prior to placing it to another target.
*
* @typeParam TStatus - A tuple type reflecting a content {@link DrekContentStatus placement status}.
* @param target - Rendering target of charged content.
* @param spec - Content charging options.
*
* @returns Rendering target.
*/
export function drekCharger<TStatus extends [
DrekContentStatus
] = [
DrekContentStatus
]>(target: DrekTarget<TStatus>, spec?: DrekCharger.Spec<TStatus>): DrekTarget;
export namespace DrekCharger {
/**
* Rendered content charger specifier.
*
* Can be one of:
*
* - An arbitrary string containing a text for enclosing comments.
* - A {@link Custom custom charger}.
* - A {@link Factory charger factory} function.
* - `null`/`undefined` to enclose the rendered contents in comments with random text.
*
* @typeParam TStatus - A tuple type reflecting a content {@link DrekContentStatus placement status}.
*/
type Spec<TStatus extends [
DrekContentStatus
] = [
DrekContentStatus
]> = string | Custom | Factory<TStatus> | null | undefined;
/**
* Custom rendered content charger.
*/
interface Custom {
/**
* Charges rendered content by representing it as another DOM node.
*
* @typeParam TStatus - A tuple type reflecting a content {@link DrekContentStatus placement status}.
* @param content - Rendered content to charge.
* @param target - Rendering target to place the charged content to.
*
* @returns Charged content placement status.
*/
charge<TStatus extends [
DrekContentStatus
]>(content: Node, target: DrekTarget<TStatus>): DrekPlacement<TStatus>;
}
/**
* Rendered content charger factory signature.
*
* @typeParam TStatus - A tuple type reflecting a content {@link DrekContentStatus placement status}.
*/
type Factory<TStatus extends [
DrekContentStatus
] = [
DrekContentStatus
]> =
/**
* @param target - A target to render the charged content to.
*
* @returns Rendered content charger specifier.
*/
(this: void, target: DrekTarget<TStatus>) => Spec;
}
}
declare module "@frontmeans/drek" {
/**
* Creates a rendering target that inserts content to parent node at particular position.
*
* @param host - A node to insert content to.
* @param before - A child node of `host` one to insert the content before, or `null` to append it as the last child
* of `host` node.
* @param context - Custom rendering context. Defaults to `host` node context.
*
* @returns Rendering target.
*/
export function drekInserter(host: Node, before: Node | null, context?: DrekContext): DrekTarget;
}
declare module "@frontmeans/drek" {
/**
* Creates a rendering target that replaces content of the `host` node.
*
* @param host - A node to replace the content of.
* @param context - Custom rendering context. Defaults to `host` node context.
*
* @returns Rendering target.
*/
export function drekReplacer(host: Node, context?: DrekContext): DrekTarget;
}
declare module "@frontmeans/drek" {
import { RenderExecution, RenderSchedule, RenderScheduleOptions } from "@frontmeans/render-scheduler";
/**
* Fragment render scheduler signature.
*
* @typeParam TStatus - A type of the tuple containing a rendered content status as its first element.
*/
export type DrekFragmentRenderScheduler<TStatus extends [
DrekContentStatus
] = [
DrekContentStatus
]> =
/**
* @param options - Options of constructed render schedule.
*
* @returns New render schedule.
*/
(this: void, options?: RenderScheduleOptions) => RenderSchedule<DrekFragmentRenderExecution<TStatus>>;
/**
* Fragment render shot execution context.
*
* @typeParam TStatus - A type of the tuple containing a rendered content status as its first element.
*/
export interface DrekFragmentRenderExecution<TStatus extends [
DrekContentStatus
] = [
DrekContentStatus
]> extends RenderExecution {
/**
* Rendered fragment instance.
*/
readonly fragment: DrekFragment<TStatus>;
/**
* The content of the rendered fragment.
*/
readonly content: DocumentFragment;
}
}
declare module "@frontmeans/drek" {
import { NamespaceAliaser } from "@frontmeans/namespace-aliaser";
import { RenderScheduler } from "@frontmeans/render-scheduler";
import { OnEvent } from "@proc7ts/fun-events";
/**
* A fragment of DOM tree, which content is to be {@link DrekTarget#placeContent placed} to the document once rendered.
*
* Provides separate {@link DrekContext rendering context} for its nodes.
*
* @typeParam TStatus - A type of the tuple containing a rendered content status as its first element.
*/
export class DrekFragment<TStatus extends [
DrekContentStatus
] = [
DrekContentStatus
]> {
/**
* Rendering target.
*
* When the fragment is {@link render rendered}, the rendered content is placed to this target.
*/
get target(): DrekTarget;
/**
* Inner rendering context of the fragment.
*
* This context as available to the {@link content} nodes.
*
* This context updated each time the fragment is {@link render rendered}.
*/
get innerContext(): DrekFragment.InnerContext<TStatus>;
/**
* The content of the fragment.
*/
get content(): DocumentFragment;
/**
* An `OnEvent` sender of fragment rendering event.
*
* Sends a fragment content {@link DrekTarget#placeContent placement} to {@link target} when the fragment is actually
* {@link render rendered}.
*
* Cuts off the event supply after sending the first event.
*/
get whenRendered(): OnEvent<[
DrekPlacement<DrekFragment.Status<TStatus>>
]>;
/**
* Construct rendered fragment.
*
* @param target - Rendering target to place the
* @param options - Fragment rendering options.
*/
constructor(target: DrekTarget<TStatus>, options?: DrekFragment.Options);
/**
* Settles previously rendered content.
*
* A {@link DrekContext#whenSettled} event sender notifies its receivers once settled.
*
* @returns `this` instance.
*/
settle(): this;
/**
* Renders this fragment by {@link DrekTarget#placeContent placing} its {@link DrekFragmentRenderExecution#content
* content} to {@link target rendering target}.
*
* Once rendered the fragment {@link content} becomes empty and can be reused. Its rendering context is updated.
*
* @returns Content {@link DrekTarget#placeContent placement} to {@link target}.
*/
render(): DrekPlacement<DrekFragment.Status<TStatus>>;
}
export namespace DrekFragment {
/**
* Rendering context provided by fragment to its content nodes.
*
* @typeParam TStatus - A type of the tuple containing a rendered content status as its first element.
*/
interface InnerContext<TStatus extends [
DrekContentStatus
]> extends DrekContext<Status<TStatus>> {
readonly scheduler: DrekFragmentRenderScheduler<TStatus>;
/**
* Tries to lift this rendering context to {@link DrekFragment#target target} one.
*
* @returns The {@link DrekFragment#target target's} context when the fragment is rendered, or `this` instance
* otherwise.
*/
lift(): DrekContext;
}
/**
* A status of rendered fragment content.
*/
type Status<TStatus extends [
DrekContentStatus
]> = [
OwnStatus
] | TStatus;
/**
* A status of rendered fragment content.
*
* This status is replaced by the target one
*/
interface OwnStatus extends DrekContentStatus {
readonly connected: false;
/**
* A status of the content within a fragment.
*
* Can be one of:
*
* - `'added'` - when the content is added to the fragment, but not yet rendered.
* - `'rendered'` - while the content is being rendered, but not yet placed to {@link DrekFragment#target target}.
*/
readonly withinFragment: 'added' | 'rendered';
}
/**
* Rendered fragment construction options.
*/
interface Options {
/**
* Namespace aliaser to use by content nodes.
*
* The one from the {@link DrekTarget#context target context} is used when omitted.
*/
readonly nsAlias?: NamespaceAliaser | undefined;
/**
* Render scheduler to use by content nodes.
*
* A `queuedRenderScheduler` is used when omitted.
*/
readonly scheduler?: RenderScheduler | undefined;
/**
* The content of constructed fragment.
*
* A new document fragment will be created when omitted.
*/
readonly content?: DocumentFragment | undefined;
}
}
}
declare module "@frontmeans/drek" {
import type { NamespaceAliaser } from "@frontmeans/namespace-aliaser";
import type { RenderScheduler } from "@frontmeans/render-scheduler";
import { OnEvent } from "@proc7ts/fun-events";
/**
* Document rendering context.
*
* Can be obtained by {@link drekContextOf} function, or {@link DrekFragment#innerContext provided} by rendered
* fragment.
*
* There are three kinds of rendering contexts:
*
* 1. Document rendering context.
*
* Such context is always available in document and returned by {@link drekContextOf} function for any DOM node
* connected to the document.
*
* 2. Fragment content rendering context.
*
* It is created for each rendered fragment and is available via {@link DrekFragment#innerContext} property.
* The {@link drekContextOf} function returns this context for fragment's {@link DrekFragment#content content},
* as well as for each DOM node added to it.
*
* 3. Unrooted rendering context.
*
* When a DOM node is neither connected to a document, nor part of a rendered fragment's
* {@link DrekFragment#content content}, the {@link drekContextOf} function creates an unrooted context for the
* [root node] of that node.
*
* Unrooted context tracks a {@link DrekPlacement#whenConnected document connection} and
* {@link DrekContext#whenSettled settlement} semi-automatically. A {@link DrekContext#lift} method can be used
* to forcibly update them.
*
* Semi-automatic tracking means that each time an unrooted context {@link drekContextOf created}, it is registered
* for automatic lifting. The lifting happens either asynchronously, or synchronously right before the
* {@link drekBuild} function exit.
*
* Alternatively, a {@link drekLift} function can be used to lift a context of the [root node] after adding it to
* another one.
*
* [root node]: https://developer.mozilla.org/en-US/docs/Web/API/Node/getRootNode
*
* @typeParam TStatus - A type of the tuple containing a context content status as its first element.
*/
export abstract class DrekContext<TStatus extends [
DrekContentStatus
] = [
DrekContentStatus
]> extends DrekPlacement<TStatus> {
/**
* A rendered {@link DrekFragment fragment} this context is provided by, if any.
*/
abstract readonly fragment: DrekFragment | undefined;
/**
* The window this context belongs to.
*/
abstract readonly window: Window;
/**
* The document this context belongs to.
*/
abstract readonly document: Document;
/**
* Namespace aliaser to use.
*/
abstract readonly nsAlias: NamespaceAliaser;
/**
* Render scheduler to use.
*/
abstract readonly scheduler: RenderScheduler;
/**
* An `OnEvent` sender of a settlement event.
*
* Such event can be sent by {@link DrekFragment.settle rendered fragment}.
*
* The same as {@link whenConnected} by default.
*
* Cuts off the event supply after sending the first event.
*/
get whenSettled(): OnEvent<TStatus>;
/**
* Tries to lift this rendering context to enclosing one.
*
* Tries to find a new root node. If the new root differs from current one, then {@link drekContextOf finds} a context
* of that new root and connects the status of this context to the found one. After successful lifting the context
* becomes a proxy accessor of the context it is lifted to, so the latter can be used instead.
*
* This has effect for unrooted contexts only.
*
* @returns Either a rendering context of the new root node, or this one.
*/
abstract lift(): DrekContext;
}
export namespace DrekContext {
/**
* Updatable document rendering context.
*/
interface Updatable extends DrekContext {
/**
* Updates this context.
*
* @param update - An update to apply to this context.
*
* @returns `this` instance.
*/
update(update?: Update): this;
}
/**
* An update to rendering context.
*/
interface Update {
/**
* Namespace aliaser to use.
*
* The aliaser is not updated when omitted.
*/
readonly nsAlias?: NamespaceAliaser | undefined;
/**
* Render scheduler to use.
*
* The scheduler is not updated when omitted.
*/
readonly scheduler?: RenderScheduler | undefined;
}
}
}
declare module "@frontmeans/drek" {
/**
* Executes a DOM builder function and then {@link DrekContext.lift lifts} all unrooted rendering contexts created by
* it.
*
* This helps to track a {@link DrekContext.whenConnected document connection} or {@link DrekContext.whenSettled
* settlement} of any unrooted rendering contexts that created before its node added to document or
* {@link DrekFragment rendered fragment}. This may happen e.g. when the rendering context {@link drekContextOf
* accessed} from inside a custom element constructor when calling `document.createElement('custom-element')`.
*
* @typeParam TResult - DOM builder result type.
* @param builder - A DOM builder function to call.
*
* @returns The value returned from DOM `builder` function.
*/
export function drekBuild<TResult>(builder: (this: void) => TResult): TResult;
}
declare module "@frontmeans/drek" {
import { QualifiedName } from "@frontmeans/namespace-aliaser";
import { Supply, SupplyPeer } from "@proc7ts/supply";
/**
* An accessor to CSS classes of some element.
*
* Can be obtained by {@link drekCssClassesOf} function.
*/
export interface DrekCssClasses {
/**
* Adds CSS class to target element.
*
* The same CSS class can be supplied multiple times. In this case the class would be removed when no more suppliers
* left.
*
* Utilizes a {@link DrekContext.nsAlias namespace aliaser} of element rendering context for resolving class names.
*
* {@link DrekContext.scheduler Schedules} element CSS updates via element rendering context.
*
* @param className - CSS class name to add. Either a string or qualified one.
* @param user - A supply peer of the CSS class. When specified, its supply us returned from the method call.
*
* @returns Added CSS class supply that removes the class once cut off, unless there are other supplies of the same
* class.
*/
add(className: QualifiedName, user?: SupplyPeer): Supply;
/**
* Checks whether the target element has the given CSS class.
*
* @param className - CSS class name to check. Either a string or qualified one.
*
* @returns `true` if the target element has this class, or `false` otherwise.
*/
has(className: QualifiedName): boolean;
/**
* Obtains CSS classes accessor using different rendering context.,
*
* @param context - A rendering context to use instead of the {@link drekContextOf default one}.
*
* @returns Either new CSS classes accessor instance, or `this` one if context is the same.
*/
renderIn(context: DrekContext): DrekCssClasses;
}
/**
* Obtains CSS classes of the given element.
*
* @param element - Target element.
*
* @returns CSS classes accessor, either already existing or newly created one.
*/
export function drekCssClassesOf(element: Element): DrekCssClasses;
}
declare module "@frontmeans/drek" {
/**
* Creates a rendering context based on another one.
*
* @typeParam TStatus - A type of the tuple containing a context content status as its first element.
* @param base - Base rendering context.
* @param update - Context update.
*
* @returns Updated rendering context, or the `base` one if nothing to update.
*/
export function deriveDrekContext<TStatus extends [
DrekContentStatus
] = [
DrekContentStatus
]>(base: DrekContext<TStatus>, update?: DrekContext.Update): DrekContext<TStatus>;
}
declare module "@frontmeans/drek" {
/**
* Finds a host element of the given DOM node with respect to rendering targets.
*
* Crosses shadow DOM and {@link DrekFragment rendered fragment} bounds. In the latter case returns a
* {@link DrekTarget.host rendering target host} instead of the document fragment.
*
* @param node - Target DOM element.
*
* @returns Either parent element of the given node, or `undefined` when not found.
*/
export function drekHost(node: Node): Element | undefined;
}
declare module "@frontmeans/drek" {
/**
* Tries to {@link DrekContext.lift lift} the rendering context of the given DOM `node` to enclosing one.
*
* It may be useful to call this method e.g. on a custom element after appending it to {@link DrekFragment fragment}.
* The former may access its rendering context in constructor. Calling this method would lift that context to fragment's
* one, so the custom element would be notified on its {@link DrekContext.whenSettled settlement}.
*
* Does nothing if the given node has no rendering context attached to it.
*
* @param node - A DOM node with rendering context to lift.
*
* @returns The `node` itself.
*/
export function drekLift<TNode extends Node>(node: TNode): TNode;
}
declare module "@frontmeans/drek" {
import { CxEntry } from "@proc7ts/context-values";
/**
* Document render kit instance.
*/
export interface DocumentRenderKit {
/**
* Obtains a rendering context of the given DOM node.
*
* Does the same as {@link drekContextOf} function, and also makes sure that the rendering context for the document
* is initialized with global `RenderScheduler` and `NamespaceAliaser`.
*
* @param node - Target DOM node.
*
* @returns Target node rendering context.
*/
contextOf(node: Node): DrekContext;
}
/**
* Context entry containing {@link DocumentRenderKit} instance.
*
* Initiated lazily. So the replacement should be provided before the kit used for the first time.
*
* Constructs global render kit instance by default.
*/
export const DocumentRenderKit: CxEntry<DocumentRenderKit>;
}
declare module "@frontmeans/drek" {
import { NamespaceDef } from "@frontmeans/namespace-aliaser";
/**
* Default Drek namespace definition.
*/
export const Drek__NS: NamespaceDef;
}
//# sourceMappingURL=drek.d.ts.map