alien-dom
Version:
Next-generation JSX client renderer with observable data primitives, immediate DOM references, and more.
1,039 lines (969 loc) • 61.7 kB
TypeScript
import { g as ArrayRef, h as ArrayOperation, J as JSX, i as Disposable, E as EffectResult, C as ComputedRef, j as ForwardedContext, k as Context, V as VarArgs, l as DisposablePromise, m as EffectCallback, n as AnyElement, R as Ref, o as ElementProxy, p as ReadonlyRef, q as HTMLOrSVGElement, r as SpringAnimation, s as CSSAttributes, t as HTMLTagName, u as SVGTagName, f as FunctionComponent, P as Promisable, v as AnimatedProps, U as Unref, w as UnresolvedChild, x as ContextStore, y as AlienBoundEffect, z as AlienNode, B as ChildrenFragment, G as AlienEffectType } from './animate-b617067e.js';
export { aS as $, aU as $$, a2 as AlienEffect, a3 as AlienEffects, c as AlienElement, aK as AlienElementIterator, A as AlienElementList, aL as AlienEvent, a4 as AlienMountEffects, aT as AlienSelectable, X as AnimatedProp, bc as AnimationEventHandler, d as AnimationsParam, aV as AriaAttributes, aW as AriaRole, ag as ArrayObserver, aY as CSSAngle, aX as CSSLength, aZ as CSSTransformAttributes, b5 as ChangeEventHandler, b0 as ClipboardEventHandler, b1 as CompositionEventHandler, ax as ComputedInput, a_ as DOMAttributes, b2 as DragEventHandler, aD as EffectContext, a$ as EventHandler, aA as FlatReadonlyRef, b3 as FocusEventHandler, b4 as FormEventHandler, L as FrameCallback, F as FromElementProxy, bm as HTML, T as HTMLAnimatedProps, bl as HTMLAttributes, H as HTMLAttributesByTagName, be as HTMLClassArrayAttribute, bh as HTMLClassAttribute, bf as HTMLClassObjectAttribute, bg as HTMLClassPrimitiveAttribute, bk as HTMLDatasetAttribute, bi as HTMLStyleArrayAttribute, bj as HTMLStyleAttribute, b6 as KeyboardEventHandler, ah as LensRef, b7 as MouseEventHandler, aa as ObservableHooks, af as Observer, aR as OpenPromise, b9 as PointerEventHandler, ac as ReadonlyArrayRef, ae as RefMap, bp as SVG, W as SVGAnimatedProps, bn as SVGAttributes, S as SVGAttributesByTagName, bo as SVGElementLike, aI as ShadowRootNode, Q as SpringConfig, O as SpringConfigOption, I as SpringDelay, K as SpringDelayFn, N as StepAnimation, M as StepAnimationFn, aJ as TemplateNode, b8 as TouchEventHandler, bd as TransitionEventHandler, ba as UIEventHandler, bb as WheelEventHandler, Y as animate, ad as arrayRef, _ as attachDisposer, ai as collectAccessedRefs, al as computed, am as computedEvery, an as computedSome, a7 as createAsyncEffect, a0 as createDisposable, a5 as createEffect, aM as createElementProxy, a6 as createOnceEffect, Z as defineContext, a8 as defineEffectType, ay as evaluateInput, a9 as getCurrentEffect, au as guardRef, at as isArrayRef, aC as isChildrenFragment, $ as isDisposable, aN as isElementProxy, ar as isReadonlyRef, as as isRef, aG as isShadowRoot, aH as isTemplateNode, ao as lens, a1 as mergeDisposables, ap as observe, aq as observeArrayOperations, av as peek, aO as promiseEvent, aQ as promiseRace, aP as promiseTimeout, aj as ref, ak as refMap, ab as setObservableHooks, aw as unref, aB as useChildren, aE as useEffect, aF as useWrappedEffect, az as when } from './animate-b617067e.js';
import { A as AlienComponent, N as NodeStore } from './helpers-eb468bd5.js';
import { Falsy, AllKeys, Remap, Intersect } from '@alloc/types';
export { F as Fragment, S as SVGNamespace, c as createElement, a as createFragment } from './jsx-runtime-1947324d.js';
import { Key } from 'ts-key-enum';
export { Color, mixColor, parseColor } from 'linear-color';
import 'csstype';
/**
* Returns a new `AbortController` instance. When the `deps` argument changes,
* the previous `AbortController` instance is aborted and a new one is created.
*/
declare function useAbortController(deps: readonly any[]): AbortController;
/**
* The `signal` argument has its `abort `event propagated to the returned
* `AbortController` instance. When the `deps` argument changes, the
* `AbortController` instance is aborted and a new one is created.
*/
declare function useAbortController(signal: AbortSignal | undefined, deps: readonly any[]): AbortController;
/**
* The `signal` argument has its `abort `event propagated to the returned
* `AbortController` instance.
*
* Note: The `signal` argument is captured on first render. It won't be updated
* unless you use a dependency array.
*/
declare function useAbortController(signal?: AbortSignal): AbortController;
/**
* Observe the fine-grained changes to an `ArrayRef` object.
*
* 🪝 This hook adds 2 to the hook offset.
*/
declare function useArrayObserver<T>(arrayRef: ArrayRef<T> | Falsy, handler: ArrayOperation.Handler<T>): void;
declare const useArrayRef: <T>(init?: readonly T[] | undefined, debugId?: string | number) => ArrayRef<T>;
type ArrayViewRenderFn<T = any> = (item: T, key: JSX.ElementKey) => JSX.Children;
interface ArrayViewOptions<T = any> {
getItemKey?: (item: T) => JSX.ElementKey;
}
declare function useArrayView<T>(array: ArrayRef<T> | Falsy, render: ArrayViewRenderFn<T>, deps?: readonly any[]): JSX.Element | null;
declare function useArrayView<T>(array: ArrayRef<T> | Falsy, options: ArrayViewOptions<T> | Falsy, render: ArrayViewRenderFn<T>, deps?: readonly any[]): JSX.Element | null;
/**
* Apply the properties of the `source` object to the `target` object. If a
* `source` property has an undefined value, the initial value of the `target`
* object is used instead.
*
* The `target` object is updated within a `useEffect` callback.
*
* Every possible property of the `target` object must exist. In other words,
* you must explicitly define each property, even if `undefined` is used as the
* initial value. This requirement allows the hook to “unset” a property when
* it's undefined in the `source` object. It's also good for performance.
*
* 🪝 This hook adds 2 to the hook offset.
*/
declare function useAssign<T extends object>(target: T, source: Partial<T>): {} & T;
type UseAsyncFn<T> = (state: UseAsync<T>) => PromiseLike<T> | T;
declare function useAsync<T>(get: UseAsyncFn<UseAsyncAwaited<T>> | Falsy, deps?: readonly any[]): UseAsync<UseAsyncAwaited<T>>;
declare class UseAsync<T> {
private state;
private abortCtrl;
timestamp: number | null;
promise: Promise<void> | null;
effects: Disposable[];
numAttempts: number;
stale: boolean;
deps?: readonly any[];
/** @observable */
get status(): "error" | "loaded" | "idle" | "loading";
/** @observable */
get result(): T | undefined;
/** @observable */
get error(): any;
get abort(): () => void;
get aborted(): boolean;
get abortSignal(): AbortSignal;
get retry(): (reset?: boolean) => void;
markAttempt(): void;
setPromise(promise: Promise<any>): void;
/**
* Track a disposable effect that will be disposed when the component unmounts
* or the async getter is called again.
*/
get track(): {
(effect: () => void): () => void;
<E extends {
dispose(): void;
}>(effect: E): E;
};
}
/**
* The result of a `useAsync` callback is processed such that an array
* of promises, an array of objects with promise values, or an object
* with promise values is awaited. If the callback's return value is a
* promise, any resulting array or object **won't** be processed.
*/
type UseAsyncAwaited<T> = T extends readonly (infer Item)[] ? UseAsyncAwaitedItem<Item>[] : UseAsyncAwaitedItem<T>;
type UseAsyncAwaitedItem<T> = T extends PromiseLike<infer Item> ? UseAsyncAwaited<Item> : T extends (...args: any[]) => any ? T : T extends object ? {
[K in keyof T]: Awaited<T[K]>;
} : T;
declare function useAutoBind<T extends object>(target: T): Readonly<T>;
declare function useClickOutside(handler: () => void): {
setElement: (element: HTMLElement | null) => void;
enabled: boolean;
enable: (target: HTMLElement | Document) => EffectResult;
dispose?: (() => void) | undefined;
};
/**
* Creates a `ComputedRef` that is updated when the dependencies change.
*
* Like `useMemo`, the ref is recreated when the component is hot reloaded.
*
* 🪝 This hook adds 2 to the hook offset.
*/
declare function useComputed<T>(get: () => T, deps?: readonly any[], debugId?: string | number): ComputedRef<T> & [value: T];
/**
* Create a piece of state that persists between renders. The state is recreated
* when its `params` change between renders. If the only argument is a plain
* object, its properties will be used for dependency tracking.
*
* Use this over `useMemo` for state that needs to persist between hot reloads.
* Note that if you define a `dispose` method on the state, it won't be
* persisted between hot reloads anymore and the `dispose` method will be called
* before the state is recreated (when its `params` change).
*
* 🪝 This hook adds 2 to the hook offset.
*/
declare function useConst<State extends object, Params extends any[]>(init: new (...params: Params) => State, ...params: Params): State;
declare function useConst<State, Params extends any[]>(init: (...params: Params) => State, ...params: Params): State;
/**
* Capture the current context and return a Provider component that can
* forward it to other components asynchronously.
*/
declare function useContext(): ForwardedContext;
/**
* Access the current value of the given Context type.
*/
declare function useContext<T>(context: Context<T>): T;
/**
* Set a timeout to run an effect after a delay, with proper cleanup on unmount.
*
* If no dependency array is given, the effect runs once (even if the `delay`
* argument value is changed in a future render).
*
* 🪝 This hook adds 1 to the hook offset.
*/
declare function useDelayedEffect(delay: VarArgs<number | DisposablePromise<any>>, effect: EffectCallback | Falsy, deps?: readonly any[]): void;
/**
* Returns the result of `depsHaveChanged` with the current `deps` and the
* previous `deps` from the last successful render.
*
* 🪝 This hook adds 1 to the hook offset.
*/
declare function useDepsArray(deps: readonly any[] | undefined): boolean;
/**
* An `ElementRef` is attached to a DOM element through a JSX element‘s `ref`
* prop. It‘s most often used to enact side effects that are tied to the
* lifecycle of the DOM element (i.e. the side effects will be active from the
* time the DOM element is mounted to the time it is unmounted).
*
* Because the JSX `ref` prop accepts multiple “element refs” in the shape of an
* array, they can be useful for component hooks that need to attach side
* effects to DOM elements, removing the need to hoist your JSX elements just to
* pass them into those hooks.
*/
declare class ElementRef<Element extends AnyElement = AnyElement> implements JSX.ElementRef {
readonly element: Element | null;
protected dispose: (() => void) | void;
setElement(element: Element | null): void;
/**
* When this method is called, it means the “element ref” was passed into a JSX
* element through its `ref` prop and that JSX element is now mounted to the
* document.
*/
protected attach?(element: Element): (() => void) | void;
/**
* When this method is called, it means the DOM element that was attached to
* this “element ref” is no longer in the document.
*/
protected detach?(element: Element): void;
}
/**
* Same as `ElementRef` except a delegate (provided to the constructor) is
* called for the `attach` and `detach` events.
*/
declare class DelegatedElementRef<Element extends AnyElement = AnyElement, Key = any> extends ElementRef<Element> {
delegate: ElementRefDelegate<Element, Key>;
key: Key;
constructor(delegate: ElementRefDelegate<Element, Key>, key: Key);
protected attach(element: Element): (() => void) | void;
protected detach(element: Element): void;
}
/**
* A delegate for `DelegatedElementRef`.
*/
interface ElementRefDelegate<Element extends AnyElement, Key> {
/**
* When this method is called, it means the “element ref” was passed into a JSX
* element through its `ref` prop and that JSX element is now mounted to the
* document.
*/
attach?(element: Element, ref: DelegatedElementRef<Element, Key>): (() => void) | void;
/**
* When this method is called, it means the DOM element that was attached to
* this “element ref” is no longer in the document.
*/
detach?(element: Element, ref: DelegatedElementRef<Element, Key>): void;
}
declare const useElementArray: <T extends Element>(deps?: readonly any[]) => {
new (arrayLength: number): ElementArray<T>;
new (...items: (ElementRef<T> | undefined)[]): ElementArray<T>;
isArray(arg: any): arg is any[];
from<T_1>(arrayLike: ArrayLike<T_1>): T_1[];
from<T_2, U>(arrayLike: ArrayLike<T_2>, mapfn: (v: T_2, k: number) => U, thisArg?: any): U[];
from<T_3>(iterable: Iterable<T_3> | ArrayLike<T_3>): T_3[];
from<T_4, U_1>(iterable: Iterable<T_4> | ArrayLike<T_4>, mapfn: (v: T_4, k: number) => U_1, thisArg?: any): U_1[];
of<T_5>(...items: T_5[]): T_5[];
readonly [Symbol.species]: ArrayConstructor;
};
declare class ElementArray<T extends AnyElement = AnyElement> extends Array<ElementRef<T> | undefined> {
/**
* Get the element at the given index. Negative indices are allowed.
*/
get(index: number): T | null;
/**
* Get an element ref to hold an element for the given index. Pass the result
* as the `ref` prop of the JSX element whose DOM node you need a reference
* to.
*/
bind(index: number): ElementRef<T>;
protected attach(_element: T, ref: DelegatedElementRef<T, number>): void;
protected detach(_element: T, ref: DelegatedElementRef<T, number>): void;
}
/**
* An observable object that tracks the bounds of an element.
*/
declare class ElementBounds implements JSX.ElementRef {
constructor(element?: AnyElement | null);
protected rectRef: Ref<DOMRectReadOnly | null>;
protected resizeEffect: Disposable | undefined;
protected observedElement: AnyElement | null;
protected observer: ResizeObserver | null;
protected locked: boolean;
protected topRef: ComputedRef<number> | undefined;
protected rightRef: ComputedRef<number> | undefined;
protected bottomRef: ComputedRef<number> | undefined;
protected leftRef: ComputedRef<number> | undefined;
protected widthRef: ComputedRef<number> | undefined;
protected heightRef: ComputedRef<number> | undefined;
get x(): number;
get y(): number;
get top(): number;
get right(): number;
get bottom(): number;
get left(): number;
get width(): number;
get height(): number;
toJSON(): any;
observe(key: Exclude<keyof DOMRectReadOnly, 'toJSON' | 'x' | 'y'>): ComputedRef<number>;
setElement(element: AnyElement | null): void;
/**
* While locked, the current bounds won't be updated when the target element
* changes.
*/
lock(flag: boolean): void;
dispose(): void;
protected setupResizeEffect(element: AnyElement): void;
}
declare function useElementBounds(deps?: readonly any[]): typeof ElementBounds;
declare const useElementMap: <K, T extends Element = Element>(deps?: readonly any[]) => {
new (delegate?: ElementRefDelegate<T, K> | undefined): ElementMap<K, T>;
};
declare class ElementMap<Key, Element extends AnyElement = AnyElement> implements Iterable<[Key, Element]>, ElementRefDelegate<Element, Key> {
delegate?: ElementRefDelegate<Element, Key> | undefined;
private map;
constructor(delegate?: ElementRefDelegate<Element, Key> | undefined);
/**
* Get the element at the given key. Returns `null` if the key is not found.
*/
get(key: Key): Element | null;
/**
* Get an element ref to hold an element for the given key. Pass the result as
* the `ref` prop of the JSX element whose DOM node you need a reference to.
*/
bind(key: Key): ElementRef<Element>;
/** @internal */
attach(element: Element, ref: DelegatedElementRef<Element>): void | (() => void) | undefined;
/** @internal */
detach(element: Element, ref: DelegatedElementRef<Element>): void;
[Symbol.iterator](): Iterator<[Key, Element]>;
}
/**
* Create a `Proxy` that forwards access/invocations to the DOM node of the JSX
* element that receives this proxy in its `ref` prop.
*
* 🪝 This hook adds 3 to the hook offset.
*/
declare function useElementProxy<T extends Element>(effect?: (element: T) => EffectResult, deps?: readonly any[]): ElementProxy<T>;
type EventTargetEffect<Target extends EventTarget = EventTarget> = (target: Target | Document) => EffectResult;
/**
* Create an element ref that attaches an effect when the element is set.
*
* If the ref is not attached to a JSX element at render time, the effect will
* be attached to the `document` instead.
*
* 🪝 This hook adds 2 to the hook offset.
*/
declare function useEventTarget<Target extends EventTarget>(effect: EventTargetEffect<Target>): {
setElement: (element: HTMLElement | null) => void;
enabled: boolean;
enable: (target: HTMLElement | Document) => EffectResult;
dispose?: (() => void) | undefined;
};
/**
* Create a function that forces the current component to rerender.
*/
declare function useForceUpdate(): any;
/**
* By default, this hook returns a stable guid. If the `reset` argument is true,
* the guid will change on every render. If the `reset` argument is later
* changed to false, the last used guid will remain stable.
*
* It can be used to conditionally bust caches (i.e. for debugging or
* development purposes), but it's also good for generating a stable guid.
*
* The guid is guaranteed to be truthy. To avoid overflow issues, the guid
* starts at `Number.MIN_SAFE_INTEGER` instead of 1, but 0 is always skipped. If
* this approach isn't good enough, you can pass a custom `generateId` function.
*/
declare function useGlobalId(reset?: boolean): number;
declare function useGlobalId<Id extends string | number = number>(reset: boolean | null | undefined, generateId: () => Id): Id;
/**
* For conditional hooks, the `useHookOffset` function can be used in the
* alternate branch to ensure that the hook index is consistent between the two
* branches.
*
* Note: This approach is **fragile** and should be used sparingly, but it's
* very useful for custom hooks whose behavior is flexible based on input
* parameters.
*
* Built-in hooks like `useConst`, `useMemo`, and `useComputed` require an
* offset of 1, while `useRef`, `useEffect`, and `useObserver` require an offset
* of 2. For other hooks, you must look at their implementation to calculate the
* required offset. Statements like `component.nextHookIndex++` also increase
* the required offset (see `useConst` for an example).
*/
declare function useHookOffset(offset: number): void;
/**
* Set up an effect that repeats at a fixed time interval.
*
* 🪝 This hook adds 1 to the hook offset.
*/
declare const useInterval: (callback: () => void, delay: number | null, deps?: readonly any[]) => void;
interface KeyBindingEvent<Target extends Document | HTMLElement = any> {
target: Target;
repeat: boolean;
stopPropagation(): void;
preventDefault(): void;
}
type Split<T extends string> = T extends `${infer First}${infer Rest}` ? First | Split<Rest> : never;
type LetterKey = Split<'ABCDEFGHIJKLMNOPQRSTUVWXYZ'>;
type DigitKey = Split<'0123456789'>;
type SymbolKey = Split<'!@#$%^&*()_+-=[]{}|\\;:,.?<>/\'" '>;
type KeyCombo = readonly KeyCombo[] | `${Key}` | LetterKey | DigitKey | SymbolKey | false | null | undefined;
declare function useKeyBinding<Target extends Document | HTMLElement>(combo: KeyCombo, onKeyDown?: (event: KeyBindingEvent<Target>) => EffectResult, options?: AddEventListenerOptions): {
/**
* Equals true when the key binding is activated.
* @observable
*/
isActive: boolean;
effect: {
dispose(): void;
} | null;
combo: Set<string>;
options: AddEventListenerOptions | undefined;
onKeyDown: ((event: KeyBindingEvent<any>) => EffectResult) | undefined;
onKeyUp: EffectResult | undefined;
setElement: (element: HTMLElement | null) => void;
enable: (target: HTMLElement | Document, options?: AddEventListenerOptions | undefined) => EffectResult;
};
type KeyBinding = ReturnType<typeof initKeyBinding>;
declare const initKeyBinding: () => {
/**
* Equals true when the key binding is activated.
* @observable
*/
isActive: boolean;
effect: Disposable | null;
combo: Set<string>;
options: AddEventListenerOptions | undefined;
onKeyDown: ((event: KeyBindingEvent) => EffectResult) | undefined;
onKeyUp: EffectResult | undefined;
setElement: (element: HTMLElement | null) => void;
enable: (target: Document | HTMLElement, options?: AddEventListenerOptions) => EffectResult;
};
/**
* @experimental
*/
declare function defineMachine<T extends MachineType>(setup: (params: Readonly<MachineParams<T>>, update: MachineUpdater<T>, machine: Machine<T>) => MachineState<T>): MachineClass<T>;
interface MachineClass<T extends MachineType = any> {
new (params: Readonly<MachineParams<T>>, onChange?: MachineCallback<T>): Machine<T>;
}
type MachineType<Params extends object | void = any, State extends {
value: string;
} = any> = (params: Params) => State;
type MachineState<T extends MachineType, S extends MachineValue<T> = any> = T extends MachineType<any, infer State> ? Extract<State, {
value: S;
}> : never;
type MachineValue<T extends MachineType> = T extends MachineType<any, infer State> ? State['value'] : never;
type MachineValueWithKey<T extends MachineType, Key extends keyof any> = Extract<MachineState<T>, {
[K in Key]: any;
}>['value'];
type MachineParams<T extends MachineType> = T extends MachineType<infer Params> ? Params : never;
type MachineCallback<T extends MachineType> = (state: Readonly<MachineState<T>>, self: Machine<T>) => void;
type VoidMachineValue<T extends MachineType> = MachineState<T> extends infer State ? State extends {
value: MachineValue<T>;
} ? {
value: any;
} extends State ? State['value'] : never : never : never;
type MachineUpdater<T extends MachineType> = {
<State extends MachineState<T>>(newState: State): State;
<Value extends VoidMachineValue<T>>(value: Value): Extract<MachineState<T>, {
value: Value;
}>;
<Value extends MachineValue<T>, State extends Extract<MachineState<T>, {
value: Value;
}>>(value: Value, newState: Omit<State, 'value'>): State;
};
type MachineProxy<T extends MachineType, State extends MachineValue<T> = any> = ProxiedMachine<T, State> & Readonly<AssumedState<MachineState<T>>>;
interface ProxiedMachine<T extends MachineType, State extends MachineValue<T>> extends Machine<T, State> {
is<Value extends MachineValue<T>>(value: Value | Value[]): this is MachineProxy<T, Value>;
has<Key extends AllKeys<MachineState<T>>>(key: Key): this is MachineProxy<T, MachineValueWithKey<T, Key>>;
}
type OmitValue<T> = T extends any ? Remap<Omit<T, 'value'>> : never;
type AssumedState<T> = Remap<Intersect<[
T
] extends [{
value: infer V;
}] ? {
value: V;
} & OmitValue<T> : never>>;
declare class Machine<T extends MachineType, State extends MachineValue<T> = any> {
readonly params: Readonly<MachineParams<T>>;
readonly stateRef: ReadonlyRef<MachineState<T, State>>;
constructor(params: Readonly<MachineParams<T>>, stateRef: ReadonlyRef<MachineState<T, State>>);
get state(): Readonly<MachineState<T, State>>;
get value(): Extract<MachineValue<T>, State>;
peek(): any;
is<Value extends MachineValue<T>>(value: Value | Value[]): this is Machine<T, Value>;
has<Key extends AllKeys<MachineState<T>>>(key: Key): this is Machine<T, MachineValueWithKey<T, Key>>;
assert<Value extends MachineValue<T>>(value: Value): Readonly<MachineState<T, Value>>;
}
declare function toMachineProxy<T extends MachineType>(machine: Machine<T>): MachineProxy<T>;
declare function useMachineProxy<T extends MachineType<void>>(constructor: MachineClass<T>, onChange?: MachineCallback<T>): MachineProxy<T>;
declare function useMachineProxy<T extends MachineType>(constructor: MachineClass<T>, params: MachineParams<T>, onChange?: MachineCallback<T>): MachineProxy<T>;
/**
* Save a value until its dependencies change. If a function is passed, it‘s
* called with `peek()` and its result is saved as the value.
*
* The value is discarded when the component is hot-reloaded.
*
* 🪝 This hook adds 1 to the hook offset.
*/
declare function useMemo<T>(arg: T | (() => T), deps?: readonly any[]): T;
/**
* Return the same object reference unless its properties have changed. Strict
* equality is used to determine if a property has changed. Property order does
* not matter. Undefined values are identical to omitted properties.
*/
declare function useObjectMemo<T extends object>(o: T): T;
/**
* Observe a single ref.
*
* 🪝 This hook adds 2 to the hook offset.
*/
declare function useObserver<T>(ref: ReadonlyRef<T> | Falsy, onChange: (value: T, oldValue: T) => void): void;
/**
* Observe any refs accessed by the effect.
*
* 🪝 This hook adds 2 to the hook offset.
*/
declare function useObserver(effect: EffectCallback | Falsy, deps: readonly any[]): void;
/**
* Save the given `value` for the next render.
*
* 🪝 This hook adds 1 to the hook offset.
*/
declare function usePrevious<T>(value: T, deps?: readonly any[]): T | undefined;
/**
* Create an observable ref that persists between renders. Unlike the `useMemo`
* hook, the ref is not recreated when the component is hot-reloaded.
*
* You may destructure the ref into a `[value, setValue]` tuple.
*
* 🪝 This hook adds 2 to the hook offset.
*/
declare function useRef<T>(): Ref<T | undefined> & [
value: T | undefined,
set: Ref<T | undefined>[1]
];
declare function useRef<T>(init: T | (() => T)): Ref<T> & [value: T, set: Ref<T>[1]];
declare function useRef<T>(init: T | (() => T), deps: readonly any[]): Ref<T> & [value: T, set: Ref<T>[1]];
/**
* An object with observable properties.
*
* The result type of `makeObjectObservable`.
*/
type Observable<T extends object> = T & {
/**
* Get the underlying `Ref` of an observable property.
*
* This method is useful for binding a property to a JSX attribute.
*/
bind<K extends keyof T>(key: K): Ref<T[K]>;
};
/**
* Make every property in a plain object observable.
*/
declare function makeObjectObservable<T extends object>(object: T): Observable<T>;
/**
* Create a plain object with observable properties.
*
* 🪝 This hook adds 2 to the hook offset.
*/
declare function useRefs<T extends object>(init: T | (() => T), deps?: readonly any[]): Observable<T>;
/**
* This hook is useful for generating a guid that changes on each render
* where a particular condition is true or a dependency array has
* changed.
*
* 🪝 This hook adds 4 to the hook offset.
*/
declare function useResetId(reset: ResetOption): number;
declare function useResetId(reset: ResetOption | undefined): number | false;
declare function useResetId(reset: string): string;
declare function useResetId(reset: string | undefined): string | false;
declare function useResetId(reset: string | ResetOption | undefined): string | number | false;
declare function useResetId(compute: () => ResetOption, deps: readonly any[]): ReadonlyRef<number>;
type ResetOption = boolean | readonly any[];
declare function useScrollStart(handler: (event: Event) => void): {
setElement: (element: HTMLElement | null) => void;
enabled: boolean;
enable: (target: HTMLElement | Document) => EffectResult;
dispose?: (() => void) | undefined;
};
declare function useQuerySelector<Element extends AnyElement>(selector: string, context?: AnyElement): ReadonlyRef<Element | null> & Iterable<Element | null>;
declare function useQuerySelectorAll<Element extends AnyElement>(selector: string, context?: HTMLElement): ReadonlyRef<Set<Element>> & Iterable<Set<Element>>;
declare function useSpring<Element extends HTMLOrSVGElement>(element: Element, animations: SpringAnimation<Element> | readonly SpringAnimation<Element>[], shouldRun?: boolean | null): void;
/**
* This creates a stable callback (i.e. its reference never changes) whose
* implementation is updated on every render.
*
* This is most beneficial for callbacks used in run-once effects.
*
* 🪝 This hook adds 1 to the hook offset.
*/
declare function useStableCallback<T extends (...args: any[]) => any>(callback: T): T;
declare function useStableCallback<T extends (...args: any[]) => void>(callback: T | Falsy): T;
type SetState<T> = Ref<T>[1];
/**
* Identical to `useState` in React. The idiomatic way to declare UI state in
* AlienDOM is through the `useRef` hook, which you can destructure into a tuple
* just like `useState`. For this reason, you should avoid `useState` unless
* you're migrating a project from React.
*
* 🪝 This hook adds 2 to the hook offset.
*/
declare const useState: {
<T>(): [value: T | undefined, set: (arg: T | ((value: T | undefined) => T | undefined) | undefined) => T | undefined];
<T_1>(init: T_1 | (() => T_1), deps?: readonly any[]): [value: T_1, set: (arg: T_1 | ((value: T_1) => T_1)) => T_1];
};
/**
* Update the style of an element during render. This hook is preferred
* to calling `element.css` directly, because it updates the newest
* version of the `element`. The style is applied through morphdom,
* which means it won't interfere with animations.
*
* 🪝 This hook adds 1 to the hook offset.
*/
declare function useStyle(element: HTMLOrSVGElement | readonly HTMLOrSVGElement[], style: CSSAttributes | Falsy, deps?: readonly any[]): void;
/**
* Observable access within the useStyle callback does not trigger a
* re-render. Instead, the element is updated directly. This is useful
* for performance reasons when the style is updated frequently or the
* component is expensive to re-render.
*
* 🪝 This hook adds 1 to the hook offset.
*/
declare function useStyle(element: HTMLOrSVGElement | readonly HTMLOrSVGElement[], style: () => CSSAttributes | Falsy, deps: readonly any[]): void;
/**
* Shorthand for `useEffect` with a `!isFirstRun` check inside the effect.
*/
declare function useUpdateEffect<State = {}>(effect: EffectCallback<State> | Falsy, deps?: readonly any[]): void;
/**
* This hook is useful whenever you have an externally managed DOM node (or a
* collection of nodes) that need to be mounted and unmounted to/from a JSX
* parent element. One example is `useArrayView` which uses this hook to
* mount/unmount the nodes derived from the array to/from a parent element.
*
* Create a DOM node that notifies when it's mounted and unmounted. The given
* `view` function is called whenever the node is mounted to the DOM, and the
* function it returns is called whenever the node is unmounted. The node may be
* mounted/unmounted multiple times and the `view` function will be called each
* time. When the `deps` change, a new node is created and the old node is
* unmounted.
*
* ⚠️ To receive an unmount notification, you need to use the `unmount` function
* provided by AlienDOM (i.e. call `unmount` on the returned node or one of its
* ancestors).
*
* 🪝 This hook adds 2 to the hook offset.
*/
declare function useView(view: (parentNode: ParentNode) => () => void, deps: readonly any[]): ChildNode;
type UseVisibilityOptions = {
root?: Element | ElementProxy | Document | null;
rootMargin?: string;
threshold?: number | number[];
onChange?: (visible: boolean, entry: IntersectionObserverEntry) => void;
};
declare function useVisibility(target: HTMLOrSVGElement | ElementProxy, options?: UseVisibilityOptions, deps?: readonly any[]): ReadonlyRef<boolean>;
type Yield = VarArgs<DisposablePromise<any> | Promise<any> | (() => void) | Falsy>;
declare function useYieldingEffect(effect: () => Generator<any, EffectResult, Yield>, deps: readonly any[]): void;
/**
* Replace the children of the given `parent` node with the given `node`.
*
* ⚠️ You must use this in place of equivalent DOM methods, or else components
* won't know when to run their side effects.
*/
declare function mount(parent: ParentNode, node: Node): void;
/**
* Append the given `node` to the end of the given `parent` node.
*
* ⚠️ You must use this in place of equivalent DOM methods, or else components
* won't know when to run their side effects.
*/
declare function mountLastChild(parent: ParentNode, node: Node): void;
/**
* Insert the given `node` at the beginning of the given `parent` node.
*
* ⚠️ You must use this in place of equivalent DOM methods, or else components
* won't know when to run their side effects.
*/
declare function mountFirstChild(parent: ParentNode, node: Node): void;
/**
* Insert the given `node` before the given `sibling` node.
*
* ⚠️ You must use this in place of equivalent DOM methods, or else components
* won't know when to run their side effects.
*/
declare function mountBeforeNode(sibling: ChildNode, node: Node): void;
/**
* Insert the given `node` after the given `sibling` node.
*
* ⚠️ You must use this in place of equivalent DOM methods, or else components
* won't know when to run their side effects.
*/
declare function mountAfterNode(sibling: ChildNode, node: Node): void;
/**
* Replace the given `oldNode` with the given `newNode`.
*
* ⚠️ You must use this in place of equivalent DOM methods, or else components won't know when to run their side effects.
*/
declare function mountReplacementNode(oldNode: ChildNode, newNode: Node): void;
/**
* Declare a template node, which can be used as a JSX element type. The JSX
* runtime will use `cloneNode(true)` on the template.
*/
declare function template<T extends HTMLTagName | SVGTagName>(template: HTMLOrSVGElement): FunctionComponent<JSX.InferProps<T>>;
/**
* Any JSX element created outside of a component must be removed from the DOM
* with this function if you don't plan to reuse it. If you only want to reuse
* some or all of its descendants, be sure to remove those descendants (with
* `node.remove()` not this method) before calling this.
*/
declare function unmount(node: ChildNode | DocumentFragment | null | undefined, skipRemove?: boolean, keepComponent?: AlienComponent | null): void;
interface ArrayViewProps<T> extends ArrayViewOptions<T> {
array: ArrayRef<T>;
children: ArrayViewRenderFn<T>;
/**
* By default, the array items are re-rendered only if the `children` function
* is changed. For explicit control of the re-rendering, you can provide a
* `deps` array.
*/
deps?: readonly any[];
}
declare function ArrayView<T>({ array, children: render, deps, ...options }: ArrayViewProps<T>): HTMLElement | null;
interface ShadowRootProps extends ShadowRootInit {
children: JSX.ChildrenProp;
}
declare function ShadowRoot(props: ShadowRootProps): JSX.Element;
type TransitionData<Id> = {
id: Id;
element: JSX.Element;
relatedId: Id | undefined;
};
type TransitionDependency = PromiseLike<any> & {
stop(): void;
};
type TransitionAnimation = SpringAnimation<JSX.Element> | SpringAnimation<JSX.Element>[] | TransitionDependency;
type TransitionProp<Id, Data = {}> = ((data: TransitionData<Id> & Data) => TransitionAnimation | Falsy) | TransitionAnimation | Falsy;
type TransitionProps<Id> = {
/** The unique identifier for the current entered element. */
id: Id;
beforeEnter?: (data: TransitionData<Id> & {
initial: boolean;
}) => Promisable<any>;
initial?: AnimatedProps<JSX.Element> | boolean;
enter?: TransitionProp<Id, {
initial: boolean;
}>;
leave?: TransitionProp<Id>;
leaveClass?: JSX.HTMLClassProp;
/** The element to be animated can be selected from the direct child. */
selector?: string;
children: JSX.ChildrenProp;
};
declare function Transition<Id>(props: TransitionProps<Id>): HTMLElement;
declare const attachRef: (props: object, key: keyof any, ref: ReadonlyRef, didSet?: ((key: keyof any, newValue: any, oldValue: any) => void) | undefined) => ReadonlyRef<any>;
/**
* Attaches refs to an object.
*
* The `refs` object can override or extend the keys of `object`. The property
* values of `refs` must be either an observable ref or undefined.
*/
declare const attachRefs: <T extends object, R extends object & { [K in keyof T]?: ReadonlyRef<T[K]> | undefined; }>(object: T, refs: R & Record<string, ReadonlyRef<unknown> | undefined>, didSet?: ((key: string, newValue: any, oldValue: any) => void) | undefined) => T & { [K_1 in Exclude<keyof R, keyof T>]: Unref<R[K_1]>; };
type Id<T> = T;
/**
* The return type of `attachRefs`.
*/
type AttachRefs<T extends object, R extends Record<string, ReadonlyRef | undefined> & {
[K in keyof T]?: ReadonlyRef<T[K]>;
}> = Id<T & {
[K in Exclude<keyof R, keyof T>]: Unref<R[K]>;
}>;
/**
* Returns `true` when the two dependency arrays have differing values,
* according to `Object.is()`.
*
* ⚠️ If either argument is `undefined`, then `true` is returned. Even if both
* are undefined.
*/
declare function depsHaveChanged(deps: readonly any[] | undefined, prevDeps: readonly any[] | undefined): boolean;
declare function editClassList(ref: Ref<JSX.HTMLClassProp>, editor: (classList: DOMTokenList) => void): void;
/**
* If no element key was explicitly defined by user code and the compiler
* couldn't assign a statically defined key, then the element's JSX position is
* used, which might also be undefined if the element was added to the DOM
* through a native DOM API.
*/
declare function getElementIdentity(element: object): JSX.ElementKey | undefined;
/**
* This is the exact same function used by JSX components to resolve their
* return value into a single DOM node and apply updates across renders.
*/
declare function morphRootNode(rootNode: ChildNode | DocumentFragment | null, newRootNode: UnresolvedChild, rootKey: JSX.ElementKey | undefined, context?: ContextStore, nodeStore?: NodeStore | null): ChildNode | DocumentFragment;
/**
* Return a flat array of keys and values from an object, sorted by key, to be
* used as a dependency array. Properties with an undefined value are skipped,
* so that they're identical to omitted properties.
*/
declare function objectToDeps(object: object): any[];
/**
* A decorator for class fields that makes the field observable.
*/
declare function observable<This, Value>(target: undefined, field: ClassFieldDecoratorContext<This, Value>): (this: This, value: Value) => Value;
/**
* A decorator for class getters that makes the getter observable. When
* observed, the getter is only called when a dependency is changed.
*/
declare function observable<This, Return>(target: () => Return, field: ClassGetterDecoratorContext<This, Return>): (this: This) => Return;
/**
* A decorator for classes that makes every property observable.
*
* It also adds a `bind` method to the class, which can be used to bind a
* property to a JSX attribute. To expose this in TypeScript, you need to extend
* your class with this interface:
*
* import { Ref } from 'alien-dom'
* interface MyClass {
* bind<K extends keyof this>(key: K): Ref<this[K]>
* }
*/
declare function observable<Class extends abstract new (...args: any) => any>(target: Class, context: ClassDecoratorContext<Class>): typeof target;
/**
* Like `observe` but with a `target` argument that can be retargeted later.
*/
declare const observeAs: <T extends void | object>(target: T, action: (target: T) => void) => Disposable<AlienBoundEffect<T, [action: (target: T) => void], boolean>>;
interface ComponentNode<Props extends object = any> {
get tag(): FunctionComponent<Props>;
get props(): Readonly<Props>;
get rootNode(): ChildNode | DocumentFragment;
get firstChild(): ChildNode;
get firstElementChild(): JSX.Element | null;
get lastChild(): ChildNode;
get lastElementChild(): JSX.Element | null;
get childNodes(): readonly ChildNode[];
get ownerDocument(): Document | null;
/**
* Update the value of the given props. It's a partial update, so any props
* not defined in `newProps` are left unchanged. Any object props are replaced
* by any new object, rather than being merged.
*/
patchProps(newProps: Partial<Props>): void;
/**
* Same as `updateProps`, but any props which are not defined in `newProps`
* are set to undefined.
*/
replaceProps(newProps: Props): void;
}
/**
* Similar to rendering a component with JSX, but you get a `ComponentNode`
* back, which keeps a reference to the root node of the component instance,
* even if it gets replaced.
*/
declare function renderComponent<Props extends object = {}>(tag: FunctionComponent<Props>, initialProps?: Props): ComponentNode<Props>;
/**
* Restore a previous HTML element within a component's node references, so a
* JSX element using the same key will be reuse the DOM node instead of
* generating a new one.
*
* This function is meant to support JSX updates of DOM nodes that are removed
* from or moved within the DOM tree, and then subsequently re-added to it.
*/
declare function restoreNodeReferences(node: ChildNode | DocumentFragment): void;
declare function toElements<Element extends HTMLOrSVGElement>(node: Exclude<JSX.ElementLike, AlienNode | ChildrenFragment>): Element[];
declare function isNode(val: any): val is Node | ChildNode | ParentNode;
type PossibleNode = object & {
nodeType?: number;
};
declare function isElement(node: PossibleNode): node is HTMLOrSVGElement;
declare function isFragment(node: PossibleNode): node is DocumentFragment;
declare function isTextNode(node: PossibleNode): node is Text;
declare function isComment(node: PossibleNode): node is Comment;
declare function isDocument(node: PossibleNode): node is Document;
/**
* ⚠️ This returns true for functions due to the possibility of element
* thunking.
*/
declare function isJSXChild(value: any): value is JSX.Element | DocumentFragment | Comment;
/**
* Channels are strongly typed event buses.
*
* When an element is passed as the first argument, the channel will only send
* messages to receivers that are bound to that element or to one of its
* ancestors.
*
* The `Data` type must be a plain object. Use the `{}` type to represent a
* message with no custom metadata.
*/
type Channel<Data extends object = Record<string, any>, Target extends object | void = any> = Channel.Signature<Data, Target> & Channel.FunctionTuple<Data, Target>;
declare namespace Channel {
interface Message<T extends Signature = Signature, Target extends InferTarget<T> = InferTarget<T>> {
readonly target?: Target;
currentTarget?: InferTarget<T>;
stopPropagation(): void;
stopImmediatePropagation(): void;
}
/**
* A message that is sent to the target and its ancestors. This propagation
* can be interrupted by `stopPropagation` or `stopImmediatePropagation`.
*/
interface BubblingMessage<T extends Signature = Signature, Target extends InferTarget<T> = InferTarget<T>> extends Message<T, Target> {
readonly target: Target;
currentTarget: InferTarget<T>;
}
/** A receiver is a function that receives messages from a channel. */
type Receiver<T extends Signature = Signature, Target extends InferTarget<T> | void = InferTarget<T>> = (message: (Target extends void ? Message<T, InferTarget<T>> : BubblingMessage<T, InferTarget<T>>) & InferData<T>, connection: Connection<Target>) => boolean | void;
/** The signature of a channel. */
interface Signature<Data extends object = Record<string, any>, Target extends object | void = any> extends Send<Signature<Data, Target>>, Connect<Signature<Data, Target>> {
}
/**
* Every channel returned by `defineChannel` can be divided into two functions
* (`send` and `connect`) via array destructuring.
*/
type FunctionTuple<Data extends object = Record<string, any>, Target extends object | void = any> = [
send: Send<Signature<Data, Target>>,
connect: Connect<Signature<Data, Target>>
];
/** The function that sends a message to a channel. */
type Send<T extends Signature = Signature> = {
<Target extends InferTarget<T>>(target: Target, message: VoidIfEmpty<InferData<T>>): boolean;
(message: VoidIfEmpty<InferData<T>>): boolean;
};
/** The function that connects a receiver to a channel. */
type Connect<T extends Signature = Signature> = {
<Target extends InferTarget<T>>(target: Target, receiver: Receiver<T>): Connection<Target>;
(receiver: Receiver<Signature<InferData<T>, void>>): Connection<void>;
};
/** A disposable connection of a receiver to a channel. */
type Connection<Target extends object | void = any> = Disposable<AlienBoundEffect<Target>>;
}
type InferData<T extends Channel.Signature> = T extends Channel.Signature<infer Data> ? Data : never;
type InferTarget<T extends Channel.Signature> = T extends Channel.Signature<any, infer Target> ? Target : never;
type VoidIfEmpty<Data extends object> = Data extends any ? ({} extends Data ? void : never) | ({} extends Required<Data> ? never : Data) : never;
/**
* Channels are strongly typed event buses.
*
* When an element is passed as the first argument, the channel will only send
* messages to receivers that are bound to that element or to one of its
* ancestors.
*
* The `Data` type must be a plain object. Use the `{}` type to represent a
* message with no custom metadata.
*/
declare function defineChannel<Data extends object = {}, Target extends object = Node>({ isTarget, bubblingKey, }?: {
isTarget?(node: any): node is Target;
bubblingKey?: Extract<keyof Target, string> | false;
}): Channel<Data, Target>;
type Fn = (...args: any[]) => any;
type FnPropertyOf<T extends object> = {
[K in keyof T]: T[K] extends Fn ? K : never;
}[keyof T];
declare class Controller<State extends object = any, Key = any> {
protected singleton: boolean;
protected instances: Map<any, any> | undefined;
constructor(singleton?: boolean);
exists(key: Key): boolean;
/**
* Set the entire state of an instance. Any properties not provided will be
* set to `undefined`.
*/
set(arg1: [Key] extends [void] ? State : Key, arg2: [Key] extends [void] ? void : State): void;
/**
* Patch the state of an instance. Any properties not provided will be left
* unchanged.
*/
patch(arg1: [Key] extends [void] ? Partial<State> : Key, arg2: [Key] extends [void] ? void : Partial<State>): void;
protected call<P extends FnPropertyOf<State>>(key: Key, method: P, ...args: Parameters<Extract<State[P], Fn>>): ReturnType<Extract<State[P], Fn>>;
}
type ControllerProxy<State extends object, Key> = unknown & Controller<State, Key> & {
[P in FnPropertyOf<State>]-?: (...args: Parameters<Extract<State[P], Fn>>) => ReturnType<Extract<State[P], Fn>>;
};
declare function defineController<State extends object>(singleton: true): ControllerProxy<State, void>;
declare function defineController<Key, State extends object>(singleton?: false): ControllerProxy<State, Key>;
/**
* Create the instance of a singleton controller.
*/
declare function useController<State extends object, Params extends any[]>(ctrl: Controller<State, void>, init: new (...params: Params) => State, ...params: Params): Observable<State>;
/**
* Create an instance of a controller for the given `key`.
*/
declare function useController<Key, State extends object, Params extends any[]>(ctrl: Controller<State, Key>, key: Key, init: new (...params: Params) => State, ...params: Params): Observable<State>;
/**
* Create the instance of a singleton controller.
*/
declare function useController<State extends object, Params extends any[]>(ctrl: Controller<State, void>, init: (...params: Params) => State, ...params: Params): Observable<State>;
/**
* Create an instance of a controller for the given `key`.
*/
declare function useController<Key, State extends object, Params extends any[]>(ctrl: Controller<State, Key>, key: Key, init: (...params: Params) => State, ...params: Params): Observable<State>;
type NodeCallback = (node: ChildNode) => void;
declare function matchDescendants<E extends Element>(target: Node, selector: string, effect: (node: E) => void): Disposable<AlienBoundEffect<Node, [callback: NodeCallback], boolean>>;
declare function observeNewChildren(target: Node, listener: (childNode: ChildNode) => void): Disposable<AlienBoundEffect<Node, [callback: NodeCallback], boolean>>;
declare function observeRemovedChildren(target: Node, listener: (childNode: ChildNode) => void): Disposable<AlienBoundEffect<Node, [callback: NodeCallback], boolean>>;
declare const observeNewDescendants: AlienEffectType<[target: Node, callback: NodeCallback]>;
declare const observeRemovedDescendants: AlienEffectType<[target: Node, callback: NodeCallback]>;
/**
* Runs the effect when the given target is mounted, then stops
* observing the document.
*/
declare const onMount: (target: ChildNode, effect: () => void, rootNode?: Node) => Disposable<AlienBoundEffect<ChildNode, [key: "onAdded" | "onRemoved", effect: () => void, rootNode: Node], boolean>>;
/**
* Runs the effect when the given target is unmounted, then stops
* observing the document.
*/
declare const onUnmount: (target: ChildNode, effect: () => void, rootNode?: Node) => Disposable<AlienBoundEffect<ChildNode, [key: "onAdded" | "onRemoved", effect: () => void, rootNode: Node], boolean>>;
declare function patchAttributes<T extends HTMLOrSVGElement>(context: T, attributes: JSX.InferAttributes<T>): void;
declare function hasClass(context: HTMLElement | SVGSVGElement, className: string): boolean;
declare function addClass(context: HTMLElement | SVGSVGElement, classes: string | string[]): void;
declare function removeClass(context: HTMLElement | SVGSVGElement, classes: string | string[]):