import { Expression } from "./signals.js";
import type { Component, Content, Falsy } from "./types.js";
/**
 * Render arbitrary content.
 *
 * Supported content types are:
 * + Null and undefined (not displayed).
 * + Arbitrarily nested arrays/fragments of content.
 * + DOM nodes. Children will be removed from document fragments.
 * + {@link View Views}.
 * + Anything created with rvx's jsx runtime.
 * + Anything else is displayed as text.
 *
 * @param content The content to render.
 * @returns A view instance or the `content` parameter itself if its already a view.
 *
 * @example
 * ```tsx
 * import { $, render } from "rvx";
 *
 * // Not displayed:
 * render(null);
 * render(undefined);
 *
 * // Arbitrarily nested arrays/fragments of content:
 * render([["Hello"], " World!"]);
 * render(<>{<>"Hello"</>}{" World!"}</>);
 *
 * // DOM nodes:
 * render(<h1>Hello World!</h1>);
 * render(document.createElement("input"));
 * render(document.createTextNode("Hello World!"));
 * render(someTemplate.content.cloneNode(true));
 *
 * // Views:
 * render(render("Hello World!"));
 * render(when(true, () => "Hello World!"));
 * render(<Show when={true}>{() => "Hello World!"}</Show>);
 *
 * // Text:
 * render("Hello World!");
 * render(() => "Hello World!");
 * render(42);
 * render($(42));
 * ```
 */
export declare function render(content: Content): View;
/**
 * Render arbitrary content and append it to the specified parent until the current lifecycle is disposed.
 *
 * @param parent The parent node.
 * @param content The content to render. See {@link render} for supported types.
 * @returns The view instance.
 *
 * @example
 * ```tsx
 * import { mount } from "rvx";
 *
 * mount(
 *   document.body,
 *   <h1>Hello World!</h1>
 * );
 * ```
 *
 * Since the content is removed when the current lifecycle is disposed, this can also be used to temporarily append
 * content to different elements while some component is rendered:
 * ```tsx
 * import { mount, Content } from "rvx";
 *
 * function Popover(props: { text: Content, children: Content }) {
 *   const visible = $(false);
 *
 *   mount(
 *     document.body,
 *     <Show when={visible}>
 *       {props.children}
 *     </Show>
 *   );
 *
 *   return <button on:click={() => { visible.value = !visible.value; }}>
 *     {props.text}
 *   </button>;
 * }
 *
 * mount(
 *   document.body,
 *   <Popover text="Click me!">
 *     Hello World!
 *   </Popover>
 * );
 * ```
 */
export declare function mount(parent: Node, content: Content): View;
/**
 * A function that is called when the view boundary may have been changed.
 */
export interface ViewBoundaryOwner {
    /**
     * @param first The current first node.
     * @param last The current last node.
     */
    (first: Node, last: Node): void;
}
/**
 * A function that must be called after the view boundary has been changed.
 */
export interface ViewSetBoundaryFn {
    /**
     * @param first The first node if changed.
     * @param last The last node if changed.
     */
    (first: Node | undefined, last: Node | undefined): void;
}
interface UninitViewProps {
    /**
     * The current first node of this view.
     *
     * + This may be undefined while the view is not fully initialized.
     * + This property is not reactive.
     */
    get first(): Node | undefined;
    /**
     * The current last node of this view.
     *
     * + This may be undefined while the view is not fully initialized.
     * + This property is not reactive.
     */
    get last(): Node | undefined;
}
export type UninitView = UninitViewProps & Omit<View, keyof UninitViewProps>;
/**
 * A function that is called once to initialize a view instance.
 *
 * View creation will fail if no first or last node has been set during initialization.
 */
export interface ViewInitFn {
    /**
     * @param setBoundary A function that must be called after the view boundary has been changed.
     * @param self The current view itself. This can be used to keep track of the current boundary and parent nodes.
     */
    (setBoundary: ViewSetBoundaryFn, self: UninitView): void;
}
/**
 * Represents a sequence of at least one DOM node.
 *
 * Consumers of the view API need to guarantee that:
 * + The sequence of nodes is not modified from the outside.
 * + If there are multiple nodes, all nodes must have a common parent node at all time.
 */
export declare class View {
    #private;
    /**
     * Create a new view.
     *
     * View implementations need to guarantee that:
     * + The view doesn't break when the parent node is replaced or when a view consisting of only a single node is detached from its parent.
     * + The boundary is updated immediately after the first or last node has been updated.
     * + If there are multiple nodes, all nodes remain in the current parent.
     * + If there are multiple nodes, the initial nodes must have a common parent.
     */
    constructor(init: ViewInitFn);
    /**
     * The current first node of this view.
     *
     * Note, that this property is not reactive.
     */
    get first(): Node;
    /**
     * The current last node of this view.
     *
     * Note, that this property is not reactive.
     */
    get last(): Node;
    /**
     * The current parent node or undefined if there is none.
     *
     * Note, that this property is not reactive.
     */
    get parent(): Node | undefined;
    /**
     * Set the boundary owner for this view until the current lifecycle is disposed.
     *
     * @throws An error if there currently is a boundary owner.
     */
    setBoundaryOwner(owner: ViewBoundaryOwner): void;
    /**
     * Append all nodes of this view to the specified parent.
     *
     * @param parent The parent to append to.
     */
    appendTo(parent: Node): void;
    /**
     * Insert all nodes of this view before a reference child of the specified parent.
     *
     * @param parent The parent to insert into.
     * @param ref The reference child to insert before. If this is null, the nodes are appended to the parent.
     */
    insertBefore(parent: Node, ref: Node | null): void;
    /**
     * Detach all nodes of this view from the current parent if there is one.
     *
     * If there are multiple nodes, they are moved into a new document fragment to allow the view implementation to stay alive.
     *
     * @returns The single removed node or the document fragment they have been moved into.
     */
    detach(): Node | DocumentFragment;
}
/**
 * Get an iterator over all current top level nodes of a view.
 *
 * @param view The view.
 * @returns The iterator.
 *
 * @example
 * ```tsx
 * import { render, viewNodes } from "rvx";
 *
 * const view = render(<>
 *   <h1>Hello World!</h1>
 * </>);
 *
 * for (const node of viewNodes(view)) {
 *   console.log(node);
 * }
 * ```
 */
export declare function viewNodes(view: View): IterableIterator<Node>;
/**
 * Watch an expression and render content from its result.
 *
 * + If an error is thrown during initialization, the error is re-thrown.
 * + If an error is thrown during a signal update, the previously rendered content is kept in place and the error is re-thrown.
 * + Content returned from the component can be directly reused within the same `nest` instance.
 *
 * See {@link Nest `<Nest>`} when using JSX.
 *
 * @param expr The expression to watch.
 * @param component The component to render with the expression result. If the expression returns a component, null or undefined, this can be omitted.
 *
 * @example
 * ```tsx
 * import { $, nest, e } from "rvx";
 *
 * const count = $(0);
 *
 * // Using the expression result in a component:
 * nest(count, count => {
 *   switch (count) {
 *     case 0: return e("h1").append("Hello World!");
 *     case 1: return "Something else...";
 *   }
 * })
 *
 * // Or directly returning a component from the expression:
 * nest(() => {
 *   switch (count.value) {
 *     case 0: return () => e("h1").append("Hello World!");
 *     case 1: return () => "Something else...";
 *   }
 * })
 * ```
 */
export declare function nest(expr: Expression<Component | null | undefined>): View;
export declare function nest<T>(expr: Expression<T>, component: Component<T>): View;
/**
 * Render conditional content.
 *
 * + Content is only re-rendered if the expression result is not strictly equal to the previous one. If this behavior is undesired, use {@link nest} instead.
 * + If an error is thrown by the expression or component during initialization, the error is re-thrown.
 * + If an error is thrown by the expression or component during a signal update, the previously rendered content is kept and the error is re-thrown.
 *
 * See {@link Show `<Show>`} when using JSX.
 *
 * @param condition The condition to watch.
 * @param truthy The component to render when the condition result is truthy.
 * @param falsy An optional component to render when the condition is falsy.
 *
 * @example
 * ```tsx
 * import { $, when, e } from "rvx";
 *
 * const message = $<null | string>("Hello World!");
 *
 * when(message, value => e("h1").append(value), () => "No message...")
 * ```
 */
export declare function when<T>(condition: Expression<T | Falsy>, truthy: Component<T>, falsy?: Component): View;
export interface ForContentFn<T> {
    /**
     * @param value The value.
     * @param index An expression to get the current index.
     * @returns The content.
     */
    (value: T, index: () => number): Content;
}
/**
 * Render content for each value in an iterable.
 *
 * Errors thrown by the component or while updating an index result in undefined behavior.
 *
 * See {@link For `<For>`} for use with JSX.
 *
 * @param each The expression to watch. Note, that signals accessed during iteration will also trigger updates.
 * @param component The component to render for each value.
 *
 * @example
 * ```tsx
 * import { $, forEach, e } from "rvx";
 *
 * const items = $([1, 2, 3]);
 *
 * forEach(items, value => e("li").append(value))
 * ```
 */
export declare function forEach<T>(each: Expression<Iterable<T>>, component: ForContentFn<T>): View;
export interface IndexContentFn<T> {
    /**
     * @param value An expression to get the current value.
     * @param index The index.
     * @returns The content.
     */
    (value: () => T, index: number): Content;
}
/**
 * Render content for each index in an iterable.
 *
 * Errors thrown by the component or while updating a value result in undefined behavior.
 *
 * See {@link Index `<Index>`} when using JSX.
 *
 * @param each The expression to watch. Note, that signals accessed during iteration will also trigger updates.
 * @param component The component to render for each index.
 *
 * @example
 * ```tsx
 * import { $, indexEach, e } from "rvx";
 *
 * const items = $([1, 2, 3]);
 *
 * indexEach(items, value => e("li").append(value))
 * ```
 */
export declare function indexEach<T>(each: Expression<Iterable<T>>, component: IndexContentFn<T>): View;
/**
 * A wrapper that can be used for moving and reusing views.
 */
export declare class MovableView {
    #private;
    constructor(view: View);
    /**
     * Create a new view that contains the wrapped view until it is moved again or detached.
     *
     * If the lifecycle in which `move` is called is disposed, the created view no longer updates its boundary and nodes may be silently removed.
     */
    move: Component<void, View>;
    /**
     * Detach content from the currently active view.
     */
    detach(): void;
}
/**
 * Render and wrap arbitrary content so that it can be moved and reused.
 */
export declare function movable(content: Content): MovableView;
/**
 * Attach or detach content depending on an expression.
 *
 * Content is kept alive when detached.
 *
 * See {@link Attach `<Attach>`} when using JSX.
 *
 * @param condition The condition to watch.
 * @param content The content to attach when the condition result is truthy.
 *
 * @example
 * ```tsx
 * import { $, attach } from "rvx";
 *
 * const showMessage = $(true);
 *
 * attachWhen(showMessage, e("h1").append("Hello World!"))
 * ```
 */
export declare function attachWhen(condition: Expression<boolean>, content: Content): View;
export {};
//# sourceMappingURL=view.d.ts.map