@tempots/dom
Version:
Fully-typed frontend framework alternative to React and Angular
264 lines (263 loc) • 10.5 kB
TypeScript
import { Providers, Renderable } from '../types/domain';
import { DOMContext } from '../dom/dom-context';
import { HeadlessPortal } from '../dom/headless-context';
import { DisposalScope, Value } from '@tempots/core';
/**
* Renders the given `renderable` with the provided `ctx` DOM context.
*
* Creates a DisposalScope for automatic signal disposal. All signals created
* during the renderable execution are tracked and disposed when the clear
* function is called.
*
* @param renderable - The renderable node to be rendered.
* @param ctx - The DOM context to be used for rendering.
* @returns A function that can be called to clear the rendered node.
* @public
*/
export declare const renderWithContext: (renderable: Renderable, ctx: DOMContext, onScope?: (scope: DisposalScope) => void) => (removeTree?: boolean) => void;
/**
* Options for rendering.
* @public
*/
export type RenderOptions = {
/**
* The document to render to. It is inferred from the parent element if not provided.
*/
doc?: Document;
/**
* Whether to clear the document before rendering. This is useful when the page has been pre-rendered on the server.
*/
clear?: boolean;
/**
* Whether to dispose the renderable when the parent element is removed from the DOM.
*/
disposeWithParent?: boolean;
/**
* The providers to use for the renderable.
*/
providers?: Providers;
/**
* Callback to capture the DisposalScope created during rendering.
* Used by HMR runtime for signal introspection.
*/
onScope?: (scope: DisposalScope) => void;
};
/**
* Renders a `Renderable` node into a specified parent element or selector.
*
* @param node - The `Renderable` node to render.
* @param parent - The parent element or selector where the node should be rendered.
* @param options - Optional rendering options.
* @returns The result of rendering the `Renderable` node.
* @throws Throws a `RenderingError` if the parent element cannot be found by the provided selector.
* @public
*/
export declare const render: (node: Renderable, parent: Node | string, { doc, clear, disposeWithParent, providers, onScope, }?: RenderOptions) => () => void;
/**
* Options for running a headless environment.
* @public
*/
export type HeadlessOptions = {
/**
* The initial URL for the headless environment.
*/
startUrl?: Value<string>;
/**
* The selector used to find the root element in the headless environment.
*/
selector: string;
/**
* The providers to use for the renderable.
*/
providers?: Providers;
};
/**
* Runs a renderable function in a headless environment.
*
* @param makeRenderable - A function that returns a Renderable to be rendered in the headless environment.
* @param options - Optional configuration for the headless environment.
* @param options.startUrl - The initial URL for the headless environment. Defaults to 'https://example.com'.
* @param options.selector - The selector used to find the root element in the headless environment. Defaults to ':root'.
* @returns An object containing the clear function, root element, and current URL Signal of the headless environment.
* @public
*/
export declare const runHeadless: (makeRenderable: () => Renderable, { startUrl, selector, providers, }?: HeadlessOptions) => {
clear: (removeTree?: boolean) => void;
root: HeadlessPortal;
currentURL: import('@tempots/core').Prop<string>;
};
/**
* Represents an error that occurs during rendering.
* @public
*/
export declare class RenderingError extends Error {
constructor(message: string);
}
/**
* @internal
*/
export declare const _NODE_PLACEHOLDER_ATTR = "data-tts-node";
export declare const CLASS_PLACEHOLDER_ATTR = "data-tts-class";
/**
* Represents an adapter for headless rendering environments.
* This class provides methods to interact with elements in a headless context.
*
* This class is used to adapt the HeadlesContext to whatever you want to use to render your final HTML.
* You can use libraries like cheerio to render your HTML.
*
* For cheerio an adapter could look like this:
*
* ```ts
* const renderWithCheerio = (html: string, root: HeadlessPortal) => {
* const $ = cheerio.load(html)
*
* const adapter = new HeadlessAdapter<cheerio.Cheerio<any>>({
* select: (selector: string): cheerio.Cheerio<any>[] => [$(selector)],
* getAttribute: (el, name: string) => el.attr(name) ?? null,
* setAttribute: (el, name: string, value: string | null) => {
* if (value === null) {
* el.removeAttr(name)
* } else {
* el.attr(name, value)
* }
* },
* getClass: el => el.attr('class') ?? '',
* setClass: (el, value: string | null) => {
* if (value === null) {
* el.removeAttr('class')
* } else {
* el.attr('class', value)
* }
* },
* getStyles: el => el.attr('style') ?? {},
* setStyles: (el, value: Record<string, string>) => {
* if (Object.keys(value).length === 0) {
* el.removeAttr('style')
* } else {
* el.css(value)
* }
* },
* appendHTML: (el, html) => el.append(html),
* getInnerHTML: el => el.html() ?? '',
* setInnerHTML: (el, html) => el.html(html),
* getInnerText: el => el.text() ?? '',
* setInnerText: (el, text) => el.text(text),
* })
*
* adapter.setFromRoot(root, true)
*
* return $.html()
* }
* ```
*
* This function will return the rendered HTML as a string.
*
* @typeParam EL - The type of elements in the headless environment.
* @public
*/
export declare class HeadlessAdapter<EL> {
/**
* Selects elements from the headless environment.
* @param selector - The selector to select elements from. The supported selectors are CSS selectors whose complexity depends on the adapter implementation.
* @returns An array of elements.
*/
readonly select: (selector: string) => EL[];
/**
* Gets the value of an attribute from an element.
* @param el - The element to get the attribute from.
* @param attr - The attribute to get the value from.
* @returns The value of the attribute or null if the attribute is not set.
*/
readonly getAttribute: (el: EL, attr: string) => string | null;
/**
* Sets the value of an attribute on an element.
* @param el - The element to set the attribute on.
* @param attr - The attribute to set the value of.
* @param value - The value to set the attribute to.
*/
readonly setAttribute: (el: EL, attr: string, value: string | null) => void;
/**
* Gets the class of an element.
* @param el - The element to get the class from.
* @returns The class of the element or an empty string if the class is not set.
*/
readonly getClass: (el: EL) => string | null;
/**
* Sets the class of an element.
* @param el - The element to set the class on.
* @param cls - The class to set.
*/
readonly setClass: (el: EL, cls: string | null) => void;
/**
* Gets the styles of an element.
* @param el - The element to get the styles from.
* @returns The styles of the element.
*/
readonly getStyles: (el: EL) => Record<string, string>;
/**
* Sets the styles of an element.
* @param el - The element to set the styles on.
*/
readonly setStyles: (el: EL, styles: Record<string, string>) => void;
/**
* Appends HTML to an element.
* @param el - The element to append the HTML to.
* @param html - The HTML to append.
*/
readonly appendHTML: (el: EL, html: string) => void;
/**
* Gets the inner HTML of an element.
* @param el - The element to get the inner HTML from.
* @returns The inner HTML of the element or an empty string if the inner HTML is not set.
*/
readonly getInnerHTML: (el: EL) => string | null;
/**
* Sets the inner HTML of an element.
* @param el - The element to set the inner HTML on.
* @param html - The inner HTML to set.
*/
readonly setInnerHTML: (el: EL, html: string) => void;
/**
* Gets the inner text of an element.
* @param el - The element to get the inner text from.
* @returns The inner text of the element or an empty string if the inner text is not set.
*/
readonly getInnerText: (el: EL) => string | null;
/**
* Sets the inner text of an element.
* @param el - The element to set the inner text on.
* @param text - The inner text to set.
*/
readonly setInnerText: (el: EL, text: string) => void;
constructor({ select, getAttribute, setAttribute, getClass, setClass, getStyles, setStyles, appendHTML, getInnerHTML, setInnerHTML, getInnerText, setInnerText, }: {
select: (selector: string) => EL[];
getAttribute: (el: EL, attr: string) => string | null;
setAttribute: (el: EL, attr: string, value: string | null) => void;
getClass: (el: EL) => string | null;
setClass: (el: EL, cls: string | null) => void;
getStyles: (el: EL) => Record<string, string>;
setStyles: (el: EL, styles: Record<string, string>) => void;
appendHTML: (el: EL, html: string) => void;
getInnerHTML: (el: EL) => string | null;
setInnerHTML: (el: EL, html: string) => void;
getInnerText: (el: EL) => string | null;
setInnerText: (el: EL, text: string) => void;
});
/**
* Sets the content of the root element from a HeadlessPortal. Generally this will be the same instance that is
* returned by `runHeadless`.
*
* @param root - The HeadlessPortal containing the content to set.
* @param setPlaceholders - Whether to set placeholders for the content. This allows you to restore the original content
* when you render on the server and then hydrate on the client.
*/
readonly setFromRoot: (root: HeadlessPortal, setPlaceholders: boolean) => void;
}
/**
* Restores all placeholders in the DOM. This function is useful when the HTML is rendered on the server and then
* hydrated on the client. It restores the original content that was replaced with placeholders during the initial
* render. When you render on the server side, make sure to call `HeadlessAdapter.setFromRoot` with the result of
* `runHeadless` and the second parameter `setPlaceholders` to `true`.
* @public
*/
export declare const restoreTempoPlaceholders: () => void;