UNPKG

@ddtmm/angular-signal-generators

Version:

Specialized Angular signals to help with frequently encountered situations.

878 lines (848 loc) 47.5 kB
import { Signal, CreateSignalOptions, Injector, ValueEqualityFn, WritableSignal, ElementRef, CreateEffectOptions, EffectCleanupRegisterFn, EffectRef } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; /** A function used for updating animations. Meant for requestAnimationFrame or some substitution. */ type AnimationFrameFn = (callback: (timeStamp: number) => void) => unknown; /** * Could be a function used in a {@link https://angular.dev/api/core/computed | computed }, * a type compatible with {@link https://angular.dev/api/core/rxjs-interop/toSignal | toSignal}, * or just a {@link https://angular.dev/api/core/signal | signal}. */ type ReactiveSource<T> = (() => T) | ToSignalInput<T> | Signal<T>; /** Extracts the emitted value of a {@link ReactiveSource} */ type ReactiveValue<S extends ReactiveSource<unknown>> = S extends ReactiveSource<infer R> ? R : never; /** Extracts what the signal would be from a {@link ReactiveSource} */ type ReactiveSignal<S extends ReactiveSource<unknown>> = S extends Signal<unknown> ? S : S extends ReactiveSource<infer R> ? Signal<R> : never; /** A type that can be converted to a signal with {@link https://angular.dev/api/core/rxjs-interop/toSignal | toSignal}. */ type ToSignalInput<T> = Parameters<typeof toSignal<T>>[0]; /** * A constant value or a {@link ReactiveSource}. * @typeParam T The type of the value or the type emitted by the {@link ReactiveSource}. */ type ValueSource<T> = T | ReactiveSource<T>; /** Extracts the value of the {@link ValueSource}. */ type ValueSourceValue<V extends ValueSource<unknown>> = V extends ReactiveSource<unknown> ? ReactiveValue<V> : V; /** Returns a value at a time *roughly* between 0 and 1. */ type InterpolateStepFn<T> = (progress: number) => T; /** Returns a function that will return an interpolated value at an point in time. */ type InterpolateFactoryFn<T> = (a: T, b: T) => InterpolateStepFn<T>; /** Either a number, array of numbers or a record of numbers. */ type NumericValues = number | number[] | Record<string | number | symbol, number>; /** Creates an interpolator using an initial {@link NumericValues} to determine the type of function to use when interpolating future values.. */ declare function createInterpolator<T extends NumericValues>(currentValue: T): InterpolateFactoryFn<any>; /** * Options that can be used to overwrite default options when calling {@link AnimatedSignal.setOptions}, * {@link WritableAnimatedSignal.set}, or {@link WritableAnimatedSignal.update}. */ interface AnimationOptions { /** A delay before starting. */ delay: number; } type AnimationOptionsWithInterpolator<TVal, TOpt extends AnimationOptions> = TOpt & { interpolator: InterpolateFactoryFn<TVal>; }; /** Options for an {@link AnimatedSignal}. */ type AnimatedSignalOptions<TVal, TOpt extends AnimationOptions> = Partial<TOpt> & Pick<CreateSignalOptions<TVal>, 'debugName'> & { /** This is only used if a signal is created from an observable. */ injector?: Injector; /** The interpolator is required unless numeric values are used. */ interpolator: InterpolateFactoryFn<TVal>; /** A function to get the next animation frame. Defaults to window.requestAnimationFrame in browser context. */ raf?: AnimationFrameFn; }; interface AnimatedSignal<TVal, TOpt extends AnimationOptions> extends Signal<TVal> { /** Sets the default animation parameters for the signal. */ setOptions(options: Partial<AnimationOptionsWithInterpolator<TVal, TOpt>>): void; } interface WritableAnimatedSignal<TVal, TOpt extends AnimationOptions> extends AnimatedSignal<TVal, TOpt> { /** Sets the value of signal with optional animation options during the next transition. */ set(value: TVal, oneTimeOptions?: Partial<AnimationOptionsWithInterpolator<TVal, TOpt>>): void; /** Update the value of the signal based on its current value, with optional animation options used during the next transition. */ update(updateFn: (value: TVal) => TVal, oneTimeOptions?: Partial<AnimationOptionsWithInterpolator<TVal, TOpt>>): void; /** Returns a readonly version of this signal */ asReadonly(): Signal<TVal>; } /** * Options that can be used to overwrite default options when calling {@link SpringSignal.setOptions}, * {@link WritableSpringSignal.set}, or {@link WritableSpringSignal.update}. */ interface SpringOptions extends AnimationOptions { /** If true, will stay with the bounds of the starting and ending value during transitions. */ clamp: boolean; /** The degree to suppress spring oscillation. The higher the value, the less movement.*/ damping: number; /** How close the velocity must be to 0 and progress the final value before the animation is considered done. */ precision: number; /** * Effects how quickly the animation changes direction. * The higher the value, the sooner the spring will reach the end and bounce back. */ stiffness: number; } /** Options for {@link springSignal}. */ type SpringSignalOptions<T> = AnimatedSignalOptions<T, SpringOptions>; /** Same as regular {@link SpringSignalOptions}, but interpolator is not required. */ type SpringNumericSignalOptions<T extends NumericValues> = Omit<SpringSignalOptions<T>, 'interpolator'> & Partial<Pick<SpringSignalOptions<T>, 'interpolator'>>; /** A signal with a function to set animation parameters returned from {@link springSignal}. */ type SpringSignal<T> = AnimatedSignal<T, SpringOptions>; /** Returned from returned from {@link springSignal}. It's like a writable a signal, but with extended options when setting and updating. */ type WritableSpringSignal<T> = WritableAnimatedSignal<T, SpringOptions>; declare function springSignal<V extends ValueSource<number>>(source: V, options?: SpringNumericSignalOptions<number>): V extends ReactiveSource<number> ? SpringSignal<number> : WritableSpringSignal<number>; declare function springSignal<T extends NumericValues>(source: ValueSource<T>, options?: SpringNumericSignalOptions<T>): typeof source extends ReactiveSource<T> ? SpringSignal<T> : WritableSpringSignal<T>; declare function springSignal<T>(source: ValueSource<T>, options: SpringSignalOptions<T>): typeof source extends ReactiveSource<T> ? SpringSignal<T> : WritableSpringSignal<T>; /** A function that alters progress between 0 and 1. */ type EasingFn = ((progress: number) => number); /** * Options that can be used to overwrite default options when calling {@link TweenSignal.setOptions}, * {@link WritableTweenSignal.set}, or {@link WritableTweenSignal.update}. */ interface TweenOptions extends AnimationOptions { /** How long the animation should last. */ duration: number; /** An easing function that distorts progress. */ easing: EasingFn; } /** Options for {@link tweenSignal}. */ type TweenSignalOptions<T> = AnimatedSignalOptions<T, TweenOptions>; /** Same as regular {@link SpringSignalOptions}, but interpolator is not required. */ type TweenNumericSignalOptions<T extends NumericValues> = Omit<TweenSignalOptions<T>, 'interpolator'> & Partial<Pick<TweenSignalOptions<T>, 'interpolator'>>; /** A signal with a function to set animation parameters returned from {@link tweenSignal}. */ type TweenSignal<T> = AnimatedSignal<T, TweenOptions>; /** Returned from returned from {@link tweenSignal}. It's like a writable a signal, but with extended options when setting and updating. */ type WritableTweenSignal<T> = WritableAnimatedSignal<T, TweenOptions>; declare function tweenSignal<V extends ValueSource<number>>(source: V, options?: TweenNumericSignalOptions<number>): V extends ReactiveSource<number> ? TweenSignal<number> : WritableTweenSignal<number>; declare function tweenSignal<T extends NumericValues>(source: ValueSource<T>, options?: TweenNumericSignalOptions<T>): typeof source extends ReactiveSource<T> ? TweenSignal<T> : WritableTweenSignal<T>; declare function tweenSignal<T>(source: ValueSource<T>, options: TweenSignalOptions<T>): typeof source extends ReactiveSource<T> ? TweenSignal<T> : WritableTweenSignal<T>; /** * This is like a writable signal, but the inputs and outputs are different types. * @typeParam TIn The input type used in set and update. * @typeParam TOut The output type used in asReadonly or when this is called as a signal. */ interface TransformedSignal<TIn, TOut> extends Signal<TOut> { /** Returns the output signal as a readonly. */ asReadonly(): Signal<TOut>; /** Sets the input value of the signal. */ set(value: TIn): void; /** Updates the input value of the signal. */ update(updateFn: (value: TIn) => TIn): void; } /** Either something with a .subscribe() function or a promise. */ type AsyncSource<T> = ToSignalInput<T> | Promise<T>; /** Options for {@link asyncSignal}. */ interface AsyncSignalOptions<T> extends Pick<CreateSignalOptions<T>, 'debugName'> { /** The default value before the first emission. */ defaultValue?: T; /** Equal functions run on values emitted from async sources. */ equal?: ValueEqualityFn<T>; /** This is only used if a signal is created from an observable. */ injector?: Injector; /** * If true, then the passed value will eagerly be read and it will throw an error if it hasn't been set. * You shouldn't use defaultValue in this case. */ requireSync?: boolean; } /** An signal that returns values from an async source that can change. */ type AsyncSignal<T> = TransformedSignal<AsyncSource<T>, T>; declare function asyncSignal<T>(valueSource: AsyncSource<T>, options: AsyncSignalOptions<T> & ({ defaultValue: T; requireSync?: false; } | { defaultValue?: undefined; requireSync: true; })): AsyncSignal<T>; declare function asyncSignal<T>(valueSource: AsyncSource<T>, options?: AsyncSignalOptions<T | undefined> & { defaultValue?: undefined; requireSync?: false; }): AsyncSignal<T | undefined>; declare function asyncSignal<T>(valueSource: Signal<AsyncSource<T>> | (() => AsyncSource<T>), options: AsyncSignalOptions<T> & ({ defaultValue: T; requireSync?: false; } | { defaultValue?: undefined; requireSync: true; })): Signal<T>; declare function asyncSignal<T>(valueSource: Signal<AsyncSource<T>> | (() => AsyncSource<T>), options?: AsyncSignalOptions<T | undefined> & { defaultValue?: undefined; requireSync?: false; }): Signal<T | undefined>; interface DebounceSignalOptions { /** pass injector if this is not created in Injection Context */ injector?: Injector; } /** * A writable signal whose value changes are debounced by a configured time period. * @privateRemarks * This could have been just a WriteableSignal, but I'm concerned about any confusion created by the fact * that the SIGNAL symbol comes from a different signal than what the write functions are writing to. */ type DebouncedSignal<T> = Signal<T> & Pick<WritableSignal<T>, 'set' | 'update' | 'asReadonly'>; /** * Creates a signal which emits the debounced changes from another signal. * See the other overload if you want to create a writable signal that is debounced. * @param source The signal like object whose values are debounced. * @param debounceTime The time from last change before the value is emitted. Can be signal like. * @param options Options for the signal. * @example * ```ts * const original = signal('unchanged'); * const debounced = debounceSignal(original, 500); * * original.set('changed'); * console.log(original(), debounced()) // changed, unchanged * * setTimeout(() => console.log(original(), debounced()), 500) // changed, changed. * ``` */ declare function debounceSignal<T>(source: ReactiveSource<T>, debounceTime: ValueSource<number>, options?: DebounceSignalOptions & Pick<CreateSignalOptions<T>, 'debugName'>): Signal<T>; /** * Creates a signal whose changes are debounced after a period of time from when the signal was updated. * @param initialValue The initial value like a regular signal. * @param debounceTime The time from last change before the value is emitted. Can be signal like. * @param options Options for the signal. * * @example * ```ts * const debounced = debounceSignal('unchanged', 500); * * debounced.set('changed'); * console.log(debounced()) // unchanged * * setTimeout(() => console.log(debounced()), 500) // changed * ``` */ declare function debounceSignal<T>(initialValue: T, debounceTime: ValueSource<number>, options?: DebounceSignalOptions & CreateSignalOptions<T>): DebouncedSignal<T>; /** * Optional injector reference if created outside injector context and IntersectionObserver options. * If no IntersectionObserver options are passed then only all attributes are observed. */ interface IntersectionObserverOptions extends Omit<IntersectionObserverInit, 'root'>, Pick<CreateSignalOptions<unknown>, 'debugName'> { /** This signal must either be created in an injection context or passed an injector. */ injector?: Injector; /** The root element where intersections will be observed. */ root?: Document | ElementRef | Element | null | undefined; } type IntersectionSignalValue = ElementRef | Element | null | undefined; /** A signal that watches when elements are resized. */ type IntersectionSignal = Signal<IntersectionObserverEntry[]> & { /** Returns the output signal as a readonly. */ asReadonly(): Signal<IntersectionObserverEntry[]>; /** Sets the new Element to watch. */ set(value: IntersectionSignalValue): void; /** Updates the new Node or Element to watch from the existing value. */ update(updateFn: (value: IntersectionSignalValue) => IntersectionSignalValue): void; }; declare function intersectionSignal(source: IntersectionSignalValue, options?: IntersectionObserverOptions): IntersectionSignal; declare function intersectionSignal(source: ReactiveSource<IntersectionSignalValue>, options?: IntersectionObserverOptions): Signal<IntersectionObserverEntry[]>; /** * Optional injector reference if created outside injector context and MutationObserver options. * If no MutationObserver options are passed then only all attributes are observed. */ interface MutationSignalOptions extends MutationObserverInit, Pick<CreateSignalOptions<unknown>, 'debugName'> { /** This signal must either be created in an injection context or passed an injector. */ injector?: Injector; } type MutationSignalValue = ElementRef | Node | null | undefined; /** A signal that watches changes to nodes or elements. */ type MutationSignal = Signal<MutationRecord[]> & { /** Returns the output signal as a readonly. */ asReadonly(): Signal<MutationRecord[]>; /** Sets the new Node or ElementRef to watch. Can optionally update options. */ set(value: MutationSignalValue, options?: MutationObserverInit): void; /** Updates the new Node or ElementRef to watch from the existing value. */ update(updateFn: (value: MutationSignalValue) => MutationSignalValue): void; }; declare function mutationSignal(source: MutationSignalValue, options?: MutationSignalOptions): MutationSignal; declare function mutationSignal(source: ReactiveSource<MutationSignalValue>, options?: MutationSignalOptions): Signal<MutationRecord[]>; /** * Optional injector reference if created outside injector context and MutationObserver options. * If no MutationObserver options are passed then only all attributes are observed. */ interface ResizeSignalOptions extends ResizeObserverOptions, Pick<CreateSignalOptions<unknown>, 'debugName'> { /** This signal must either be created in an injection context or passed an injector. */ injector?: Injector; } type ResizeSignalValue = ElementRef | Element | null | undefined; /** A signal that watches when elements are resized. */ type ResizeSignal = Signal<ResizeObserverEntry[]> & { /** Returns the output signal as a readonly. */ asReadonly(): Signal<ResizeObserverEntry[]>; /** Sets the new ElementRef to watch and optionally can set options too. */ set(value: ResizeSignalValue, options?: ResizeObserverOptions): void; /** Updates the new Node or ElementRef to watch from the existing value. */ update(updateFn: (value: ResizeSignalValue) => ResizeSignalValue): void; }; declare function resizeSignal(source: ResizeSignalValue, options?: ResizeSignalOptions): ResizeSignal; declare function resizeSignal(source: ReactiveSource<ResizeSignalValue>, options?: ResizeSignalOptions): Signal<ResizeObserverEntry[]>; /** Options for {@link eventSignal}. */ interface EventSignalOptions<T> extends Pick<CreateSignalOptions<T>, 'debugName'> { /** An equal function put on the selector result. */ equal?: ValueEqualityFn<T | undefined>; /** The initial value until the first emission. */ initialValue?: unknown; /** Needed if not created in injection context. */ injector?: Injector; } /** Anything Renderer2 can listen to plus ElementRef. */ type EventSignalTarget = 'window' | 'document' | 'body' | ElementRef | any; /** * Creates a signal that listens to a DOM object event and maps the event to a value, initially returning undefined if default is not set. * Requires being in an injection context. * @example * ```ts * const $viewElem = viewChild('div'); * const $bodyEvent = eventSignal('body', 'click', (event) => event.clientX); * const $divEvent = eventSignal($viewElem, 'click', (event) => event.clientX); * effect(() => console.log(`body: ${$bodyEvent()}`)); // initially undefined. * effect(() => console.log(`div: ${$divEvent()}`)); // initially undefined. * ``` * @param T The type of the return value from the signal which comes from the selector or initialValue if provided. * @param source The source the contains the target to listen to. Can be window, document, body, or an element ref, or anything else Renderer2 can listen to. * @param eventName The name of the event to listen to. * @param selector The selector to run when the event is emitted that will be output by the signal. * @param options Options used when creating the signal. */ declare function eventSignal<T>(source: ValueSource<EventSignalTarget>, eventName: string, selector: (event: any) => T, options?: EventSignalOptions<T> & { initialValue?: undefined; }): Signal<T | undefined>; /** * Creates a signal that listens to a DOM object event and maps the event to a value. * Requires being in an injection context. * @example * ```ts * const $viewElem = viewChild('div'); * const $bodyEvent = eventSignal('body', 'click', (event) => event.clientX, { initialValue: 0 }); * const $divEvent = eventSignal($viewElem, 'click', (event) => event.clientX, { initialValue: 0 }); * effect(() => console.log(`body: ${$bodyEvent()}`)); // initially 0. * effect(() => console.log(`div: ${$divEvent()}`)); // initially 0. * ``` * @param T The type of the return value from the signal which comes from the selector or initialValue. * @param source The source the contains the target to listen to. Can be window, document, body, or an element ref, or anything else Renderer2 can listen to. * @param eventName The name of the event to listen to. * @param selector The selector to run when the event is emitted that will be output by the signal. * @param options Options used when creating the signal. */ declare function eventSignal<T>(source: ValueSource<EventSignalTarget>, eventName: string, selector: (event: any) => T, options: EventSignalOptions<T> & { initialValue: T; }): Signal<T>; /** * Creates a signal that listens to a DOM object event and returns that event. * Requires being in an injection context. * @example * ```ts * const $viewElem = viewChild('div'); * const $bodyEvent = eventSignal('body', 'click'); * const $divEvent = eventSignal($viewElem, 'click'); * // The following emit HTML events: * effect(() => console.log(`body: ${$bodyEvent()}`)); * effect(() => console.log(`div: ${$divEvent()}`)); * ``` * @param source The source the contains the target to listen to. Can be window, document, body, or an element ref, or anything else Renderer2 can listen to. * @param eventName The name of the event to listen to. * @param options Options used when creating the signal. */ declare function eventSignal(source: ValueSource<EventSignalTarget>, eventName: string, options?: EventSignalOptions<any> & { initialValue?: any; }): Signal<any>; /** The signal returned from {@link filterSignal}. */ interface FilterSignal<T, O = T> extends Signal<O> { /** Returns the output signal as a readonly. */ asReadonly(): Signal<O>; /** Sets the new value IF it is compatible with the filter function. */ set(value: T): void; /** Updates the signal's value IF it is compatible with the filter function. */ update(updateFn: (value: O) => T): void; } declare function filterSignal<T, O extends T>(initialValue: O, filterFn: (x: T) => x is O, options?: CreateSignalOptions<O>): FilterSignal<T, O>; declare function filterSignal<O>(initialValue: O, filterFn: (x: O) => boolean, options?: CreateSignalOptions<O>): FilterSignal<O>; /** Options for {@link liftSignal}. */ interface LiftSignalOptions<T> { /** * Because signals only place nice with mutable objects, all mutations work by first cloning. * There is a default clone function present, but if there are problems with it, you can provide your own. */ cloneFn?: (source: T) => T; /** * A debug name for the signal. Used in Angular DevTools to identify the signal. * Only used if a {@link https://angular.dev/api/core/WritableSignal WritableSignal} is NOT passed as the first argument. */ debugName?: string; /** * Custom equality function. * Only used if a value and not a {@link https://angular.dev/api/core/WritableSignal WritableSignal} is passed as the first argument. */ equal?: ValueEqualityFn<T>; } type MethodKey<T> = keyof { [K in keyof T as T[K] extends (...args: any[]) => unknown ? K : never]: K; } & keyof T; type UpdaterKey<T> = keyof { [K in keyof T as T[K] extends (...args: any[]) => T ? K : never]: K; } & keyof T; type MethodParameters<T, K extends MethodKey<T> | UpdaterKey<T>> = T[K] extends (...args: infer P) => unknown ? P : never; type BoundMethods<T, K extends readonly (MethodKey<T> | UpdaterKey<T>)[]> = { [Key in K[number]]: (...args: MethodParameters<T, Key>) => void; }; /** * Lifts methods from the signal's value to the signal itself. * @example * ```ts * const awesomeArray = liftSignal([1, 2, 3, 4], ['filter'], ['push', 'pop']); * awesomeArray.push(5); * console.log(awesomeArray()); //[1, 2, 3, 4, 5]; * awesomeArray.pop(); * console.log(awesomeArray()); //[1, 2, 3, 4]; * awesomeArray.filter(x => x % 2 === 0); * console.log(awesomeArray()); //[2, 4]; * ``` * @param valueSource Either a value or a Writable signal. * @param updaters A tuple that contains the names that will return a new value. * @param mutators A tuple that contains the names that will modify the signal's value directly. * To guarantee this will return a new value, structuredClone or object.assign is used to create a brand new object, * so use with caution. * @typeParam T the type of the signal's value as well as the type where the functions are lifted from. * @typeParam U A tuple that contains the names of methods appropriate for updating. * @typeParam M A tuple that contains the names of methods appropriate for mutating. */ declare function liftSignal<T extends NonNullable<unknown>, const U extends readonly UpdaterKey<T>[] | null | undefined, const M extends readonly MethodKey<T>[] | null | undefined = null>(valueSource: Exclude<T, Signal<unknown>> | WritableSignal<T>, updaters: U, mutators?: M, options?: LiftSignalOptions<T>): WritableSignal<T> & BoundMethods<T, NonNullable<M>> & BoundMethods<T, NonNullable<U>>; /** Options for {@link mapSignal}. */ interface MapSignalOptions<R> extends Pick<CreateSignalOptions<R>, 'debugName'> { /** An equal function put on the selector result. */ equal?: ValueEqualityFn<R>; /** This is only used if toSignal is needed to convert to a signal OR to get destroyedRef. */ injector?: Injector; } /** A signal that is updated with TIn, but emits TOut due to a selector specified at creation. */ interface MapSignal<TIn, TOut> extends TransformedSignal<TIn, TOut> { /** * Contains the values that are input to the signal. * Calling set or update on this will have the same behavior as calling the main set or update methods * and is exposed to make it easier for binding. */ input: WritableSignal<TIn>; } /** Used for when one or more signals passed as parameters */ type FromReactiveTupleType<T = unknown> = readonly ReactiveSource<T>[]; /** Extracts the values from a tuple of signal and converts them into a tuple of their own. */ type FromReactiveValues<T extends FromReactiveTupleType> = { [I in keyof T]: ReactiveValue<T[I]>; }; /** Creates a selector function type that uses the tuple of values as parameters */ type FromReactiveSelector<T extends FromReactiveTupleType, R> = (...x: FromReactiveValues<T>) => R; /** The function parameters if signals are being passed without options. */ type FromReactiveParameters<T extends FromReactiveTupleType, R> = readonly [...inputs: T, selector: FromReactiveSelector<T, R>]; /** The function parameters if signals are being passed with options. Using an optional tuple member produced weird results. */ type FromReactiveParametersWithOptions<T extends FromReactiveTupleType, R> = readonly [ ...inputs: T, selector: FromReactiveSelector<T, R>, options: MapSignalOptions<R> ]; /** * Creates a signal from one or more signals that are mapped using the selector function. * This is slightly different from compute as all values will be recalculated even if logic in the selector only uses some. * @typeParam T The type of the signal tuple portion of params. * @typeParam R The type of the value output by the selector function * @param params One or more signals, then the selector. * @returns A readonly signal emitting the selector value * @example * ```ts * const num1 = signal(0); * const num2 = signal(1); * const num3 = signal(2); * const mapped = mapSignal(num1, num2, num3, (a, b, c) => a + b + c); * console.log(mapped()); // 3 * num1.set(5); * console.log(mapped()); // 8 * ``` */ declare function mapSignal<const T extends FromReactiveTupleType, R>(...params: FromReactiveParameters<T, R> | FromReactiveParametersWithOptions<T, R>): Signal<R>; /** * Creates a signal whose input value is immediately mapped to a different value based on a selector. * The selector can contain signals and will react to changes in those signals. * @typeParam T The type of the initial value * @typeParam R The type of the value output by the selector function * @param initialValue The initial value that will be run * @param selector A selector that is run after the value of the signal is changed. * @param options Can see equality function or if the selector or injector since this uses a computed function. * @returns A writable signal whose output value is mapped with the selector. * @example * ```ts * const addOne = mapSignal(1, x => x + 1); * console.log(addOne()); // 2 * * const addOnePlusOne = mapSignal(1, x => x + addOne()); * console.log(addOnePlusOne()); // 3 * ``` */ declare function mapSignal<T, R>(initialValue: T, selector: (x: T) => R, options?: MapSignalOptions<R>): MapSignal<T, R>; /** * A signal that can be destroyed, releasing underlying resources and preventing future emissions. * If created in an injector context, then it should be destroyed automatically, and calling {@link destroy} * is only if for cases when it is desirable to stop future emissions early. * For signals created outside an injector context, this is the only way to clean up resources such as event listeners. */ interface DestroyableSignal<T> extends Signal<T> { /** * This signal relies on resources that must be cleaned up. * If it was created in an injector context then this doesn't have to be called, but is safe to call early. * if it was created outside of an injector context then this should be called when it is no longer needed. */ destroy(): void; } /** The latest state of a MediaQueryList. */ type MediaQueryState = Pick<MediaQueryList, 'matches' | 'media'>; /** Options for {@link mediaQuerySignal} when generating values from another reactive source. */ interface MediaQuerySignalOptions extends Pick<CreateSignalOptions<unknown>, 'debugName'> { /** When passed another reactive source then this needs to be passed if outside injector context since this signal relies on effect. */ injector?: Injector; } /** Options for {@link mediaQuerySignal} when using writeable overload. */ interface MediaQueryWriteableSignalOptions extends Pick<CreateSignalOptions<unknown>, 'debugName'> { /** This is only necessary if called outside injector context and {@link manualDestroy} is not true. */ injector?: Injector; /** * Whether this will be destroyed manually instead of when the injection context is destroyed. * If true, this signal does not have to be created in an injection context, * and {@link MediaQuerySignal.destroy} should be called when or if it is time to release resources. */ manualDestroy?: boolean; } /** A writeable signal created from {@link MediaQuerySignal}. */ interface MediaQuerySignal extends TransformedSignal<string, MediaQueryState>, DestroyableSignal<MediaQueryState> { } declare function mediaQuerySignal(querySource: ReactiveSource<string>, options?: MediaQuerySignalOptions): DestroyableSignal<MediaQueryState>; declare function mediaQuerySignal(querySource: string, options?: MediaQueryWriteableSignalOptions): MediaQuerySignal; /** Options for {@link nestSignal}. */ interface NestSignalOptions<T> extends CreateSignalOptions<NestSignalValue<T>> { /** If true, errors will be caught, and the value will be set to undefined. */ ignoreErrors?: boolean; /** Needed if not created in injection context and an Subscribable is passed as source. */ injector?: Injector; } /** The value returned by {@link nestSignal} that recursively replaces signals with their value types. */ type NestSignalValue<T> = T extends Signal<infer R> ? NestSignalValue<R> : T extends [] ? NestSignalValue<T[number]>[] : T extends Set<infer R> ? Iterable<NestSignalValue<R>> : T extends Map<infer K, infer V> ? Iterable<NestSignalValue<K>, NestSignalValue<V>> : T extends Date ? T : T extends object ? { [K in keyof T]: NestSignalValue<T[K]>; } : T; declare function nestSignal<T>(source: ReactiveSource<T>, options?: NestSignalOptions<T>): Signal<NestSignalValue<T>>; declare function nestSignal<T>(initialValue: T, options?: NestSignalOptions<T>): TransformedSignal<T, NestSignalValue<T>>; interface ReduceSignal<T, U> extends Signal<T> { asReadonly(): Signal<T>; set(value: U): void; update(updateFn: (prior: T) => U): void; } declare function reduceSignal<T>(initialValue: T, callbackFn: (prior: T, current: T) => T, options?: CreateSignalOptions<T>): ReduceSignal<T, T>; declare function reduceSignal<T, U>(initialValue: T, callbackFn: (prior: T, current: U) => T, options?: CreateSignalOptions<T>): ReduceSignal<T, U>; /** A result from a {@link Cursor}. */ type CursorResult<T> = { /** If false there was no value, possibly because it went past a boundary or has no elements. */ hasValue: false; /** The value will be undefined if hasValue is false. */ value?: unknown; } | { /** If true, then the value property should be set. */ hasValue: true; /** The current value after last move. */ value: T; }; /** * Like an iterator, but with a reset method. * @typeParam TVal The type of the value being iterated. */ interface Cursor<TVal> { /** * Performs a forward search from current position. * If nothing is found, and autoReset is true, then it will resume search from the beginning. * Will set position past end if nothing is found. * * @param value The value to search for. * @returns The current cursor result after search is conducted. */ moveTo(value: TVal): CursorResult<TVal>; /** * Should move to the next element if relativeChange isn't passed, * otherwise it should move the amount of relativeChange. * * @param relativeChange The amount to move. If no value is passed then it will move forward 1. * @returns The current cursor state after movement. */ next(relativeChange?: number): CursorResult<TVal>; /** Should reset to right before the beginning. */ reset(): void; } declare const ERR_BOUNDS_EXCEEDED = "Sequence cursor has exceeded bounds."; declare const ERR_ELEMENT_NOT_PRESENT = "Element was not present in sequence."; declare const ERR_NO_ELEMENTS = "Sequence contains no elements."; declare const ERR_INVALID_SOURCE = "Invalid source type passed to sequence signal."; /** Types that can be used in conjunction with {@link sequenceSignal}. */ type SequenceSource<T> = ArrayLike<T> | Cursor<T> | Iterable<T>; interface SequenceSignalOptions extends Pick<CreateSignalOptions<unknown>, 'debugName'> { /** * If true, then the sequence will not loop and restart needs to be called. * This is used when creating a cursor internally. If a cursor is provided as a source, then this will be ignored. */ disableAutoReset?: boolean; /** injector should only be necessary if passing in an observable outside injector context. */ injector?: Injector; } interface SequenceSignal<T> extends Signal<T> { /** * Updates the signal to the next item in sequence. * If at end and the option {@link SequenceSignalOptions.disableAutoReset} was passed as true, then it will move to the start. */ next(relativeChange?: number): void; /** Updates the signal to the first item in sequence. */ reset(): void; /** Moves the sequence to the next element that matches the passed value, ignoring the current element. Will throw if not found. */ set(value: T): void; /** Moves the sequence to the next element that matches the passed value, ignoring the current element. Will throw if not found. */ update(updateFn: (value: T) => T): void; } /** * This creates a signal that will cycle through a source of values, returning the the start after each call of next. * @param sequence The source of sequence values. * Could be an ArrayLike object or a "Cursor" which is like an iterator, but with a reset method. * @param options Options for creating the sequence signal. * @returns The sequence signal. * @example * ```ts * const boolSeq = sequenceSignal([true, false]); * console.log(boolSeq()) // true * boolSeq.next(); * console.log(boolSeq()) // false * boolSeq.next(); * console.log(boolSeq()) // true * * const lettersSeq = sequenceSignal('ABCDEFG'); * console.log(letterSeq()) // A; * boolSeq.next(2); * console.log(letterSeq()) // C; * * // pass a "Cursor" to generate a sequence. * sequenceSignal((() => { * let values = [1, 2]; * return { * next: (relativeChange: number) => { * for (let i = 0; i < relativeChange; i++) { * values = [values[1], values[0] + values[1]]; * } * for (let i = relativeChange; i < 0; i++) { * values = [Math.max(1, values[1] - values[0]), Math.max(values[0], 2)]; * } * return { hasValue: true, value: values[0] }; * }, * reset: () => values = [1, 2] * }; * })()); * console.log(fibSeq()); // 1; * fibSeq.next(); * console.log(fibSeq()); // 2; * ``` */ declare function sequenceSignal<T>(sequence: ValueSource<SequenceSource<T>>, options?: SequenceSignalOptions): SequenceSignal<T>; /** A simple provider of persistent storage for {@link storageSignal}. */ interface StorageSignalStore<T> { get(key: string): T | undefined; set(key: string, value: T): void; } /** * Creates a signal that will sync changes to some sort of storage. * The next time a signal with the same key is read, an alternative value to the initial value will be used. * It's probably better to use {@link sessionStorageSignal} or {@link localStorageSignal} instead of this. * @param initialValue The initialValue for the signal if it isn't in storage. * @param key The key to use for storage. This should be unique to avoid conflicts when deserializing values. * @param storageProvider The provider of storage. * @param options Standard create signal options. * @typeParam T The type of the value that should be stored and deserialized. * @returns A writable signal * @example * ```ts * const storageProvider = Map<string, number>(); * const signal1 = storageSignal(1, 'someKey', storageProvider); * signal1.set(100); * const signal2 = storageSignal(1, 'someKey', storageProvider); * console.log(signal1(), signal2()); // [LOG]: 100, 100 * ``` */ declare function storageSignal<T>(initialValue: T, key: string, storageProvider: StorageSignalStore<T>, options?: CreateSignalOptions<T>): WritableSignal<T>; /** * Options for {@link localStorageSignal} and {@link sessionStorageSignal}. * @typeParam T The type of the value that should be stored and deserialized. */ interface WebStorageOptions<T> extends CreateSignalOptions<T> { /** An optional function to use when serializing a value with JSON.parse. */ replacer?: (key: string, value: unknown) => unknown; /** An optional function to use when deserializing a value with JSON.parse. */ reviver?: (key: string, value: unknown) => unknown; } /** * Generates a signal using localStorage as the store. A shared Map is used if session storage is not supported. * * @param initialValue the initial value for the signal * @param key the key to use in localStorage * @param options optional options to configure the signal and underlying storage. * @typeParam T The type of the value that should be stored and deserialized. * @returns the writable signal generated from storageSignal. * @example * ```ts * const signal1 = localStorageSignal(1, 'someKey'); * console.log(signal1()); // This MIGHT not be 1 depending on what was stored in localStorage for "someKey". * signal1.set(100); * console.log(signal1()); // 100 ("someKey" is now 100 in localStorage) * ``` * @see {@link storageSignal} */ declare function localStorageSignal<T>(initialValue: T, key: string, options?: WebStorageOptions<T>): WritableSignal<T>; /** * Generates a signal using sessionStorage as the store. A shared Map is used if session storage is not supported. * * @param initialValue the initial value for the signal * @param key the key to use in sessionStorage * @param options optional options to configure the signal and underlying storage. * @typeParam T The type of the value that should be stored and deserialized. * @returns the writable signal generated from storageSignal. * @example * ```ts * const signal1 = sessionStorageSignal(1, 'someKey'); * console.log(signal1()); // This MIGHT not be 1 depending on what was stored in sessionStorage for "someKey". * signal1.set(100); * console.log(signal1()); // 100 ("someKey" is now 100 in sessionStorage) * ``` * @see {@link storageSignal} */ declare function sessionStorageSignal<T>(initialValue: T, key: string, options?: WebStorageOptions<T>): WritableSignal<T>; /** The state of the timer. */ type TimerSignalStatus = 'running' | 'paused' | 'stopped' | 'destroyed'; /** Options for {@link timerSignal}. */ interface TimerSignalOptions<T = number> extends Pick<CreateSignalOptions<T>, 'debugName'> { /** pass injector if this is not created in Injection Context */ injector?: Injector; /** * If true, the timer isn't running at start. * When running in a non-browser environment, the signal always begins in a stopped state by default. */ stopped?: boolean; /** * A selector function that receives the tick count and returns the value to emit from the signal. * If not provided, the tick count (number) is emitted. */ selector?: (tickCount: number) => T; } /** A readonly signal with methods to affect execution created from {@link timerSignal}. */ interface TimerSignal<T = number> extends Signal<T> { /** Pauses the timer. */ pause(): void; /** Restarts the timer if it is an interval, or incomplete "one-time" timer. */ restart(): void; /** Resumes the timer if paused using the remaining time when paused. */ resume(): void; /** The status of the timer as a signal. */ state: Signal<TimerSignalStatus>; } declare function timerSignal<T>(timerTime: ValueSource<number>, intervalTime: ValueSource<number> | null | undefined, options: TimerSignalOptions<T> & { selector: (tickCount: number) => T; }): TimerSignal<T>; declare function timerSignal(timerTime: ValueSource<number>, intervalTime?: ValueSource<number> | null, options?: TimerSignalOptions): TimerSignal<number>; /** Options that control the behavior of {@link gatedEffect}. */ interface GatedEffectOptions extends CreateEffectOptions { /** Prevents execution of the effect if this returns false. */ filter?: () => boolean; /** * A condition that will prevent the effect from running until it is true. * Once it is true once, it will not be checked again. */ start?: () => boolean; /** * The number of times the effect should run. * Only increments if the effect function is run, so occasions where {@link filter} or {@link start} return false aren't counted. * If {@link until} is also set, then whichever comes first will terminate the effect. */ times?: number; /** * A condition that will terminate the effect. * If {@link times} is also set, then whichever comes first will terminate the effect. */ until?: () => boolean; } /** * An effect that will start, stop, or conditionally run based on conditions set in the {@link GatedEffectOptions}. * This is most effective for stopping an effect as it will properly clean up after itself and not track signals unnecessarily. * * @param effectFn The function to be executed whenever all gate conditions are met. * @param options Standard effect options, plus gate conditions filter (condition that prevents running), * start (start running effect), times (number of times to execute), and until (stop running effect) * @example * ```ts * const $userInfo = signal<UserInfo | undefined>(undefined); * gatedEffect(() => doSomethingWithRequiredUserInfo($userInfo()!), { start: () => $userInfo() !== undefined, times: 1 }); * ``` */ declare function gatedEffect(effectFn: (onCleanup: EffectCleanupRegisterFn) => void, options?: GatedEffectOptions): EffectRef; /** Options for {@link signalToIterator}. */ interface SignalToIteratorOptions { /** pass injector if this is not created in Injection Context */ injector?: Injector; } /** * Creates an async iterator for a signal. * @param source The signal to create an async iterator for. * @param options Options for the signal. * @example * ```ts * // assumes this is within a component * readonly source = signal('initial'); * constructor() { * for await (const item of signalToIterator(this.source)) { * console.log(item); // 'initial', 'next'; * } * this.source.set('next'); * } * ``` */ declare function signalToIterator<T>(source: Signal<T>, options?: SignalToIteratorOptions): Required<AsyncIterableIterator<T>>; /** * Options that control the behavior of {@link inspect}. * Options specific to inspect can be set globally from {@link INSPECT_DEFAULTS}. */ interface InspectOptions<T> extends NestSignalOptions<T> { /** Needed if not created in an injection context. */ injector?: Injector; /** Overrides the default reporter.*/ reporter?: (value: NestSignalValue<T>) => void; /** By default inspect will only work in dev mode. Set to true to run in prod mode. */ runInProdMode?: boolean; /** * If true, then the first output is not reported. * This is dependent on one when the first effect actually executes. */ skipInitial?: boolean; } /** * Given that {@link inspect} should have no side-effects and does nothing in production mode, * this is provided as a simple way to set the default behavior without having to get into dependency injection. * You can modify this from wherever you want. */ declare const INSPECT_DEFAULTS: { /** This is the default reporter for inspect. */ reporter: (value: unknown) => void; /** If true, this will run even if in prod mode is enabled. */ runInProdMode: boolean; /** If true, the initial value will not be reported. */ skipInitial: boolean; }; /** * Reports the latest state of a subject by resolving all the values deeply contained within by using {@link nestSignal}. * By default the output is just a console.log, but this can be changed with the {@link InspectOptions<T>.reporter} option. * Subject can be anything, but to be effective it should be a signal, an object that contains signals, or an array of signals. * * @param subject This can be anything, but ideally it is some object that contains signals somewhere. * @param options Options that control the behavior of inspect. Globally, options can be changed from {@link INSPECT_DEFAULTS}. * @example * ```ts * const $a = signal(1); * const $b = signal(2); * const $c = signal({ a: $a, b: $b }); * inspect([$c]); // log: [{ a: 1, b: 2 }]; * $b.set(3) // log: [{ a: 1, b: 3 }]; * ``` */ declare function inspect<T>(subject: T, options?: InspectOptions<T>): void; export { ERR_BOUNDS_EXCEEDED, ERR_ELEMENT_NOT_PRESENT, ERR_INVALID_SOURCE, ERR_NO_ELEMENTS, INSPECT_DEFAULTS, asyncSignal, createInterpolator, debounceSignal, eventSignal, filterSignal, gatedEffect, inspect, intersectionSignal, liftSignal, localStorageSignal, mapSignal, mediaQuerySignal, mutationSignal, nestSignal, reduceSignal, resizeSignal, sequenceSignal, sessionStorageSignal, signalToIterator, springSignal, storageSignal, timerSignal, tweenSignal }; export type { AnimatedSignal, AnimatedSignalOptions, AnimationFrameFn, AnimationOptions, AsyncSignal, AsyncSignalOptions, AsyncSource, BoundMethods, Cursor, CursorResult, DebounceSignalOptions, DebouncedSignal, DestroyableSignal, EasingFn, EventSignalOptions, EventSignalTarget, FilterSignal, FromReactiveSelector, FromReactiveTupleType, FromReactiveValues, GatedEffectOptions, InspectOptions, InterpolateFactoryFn, InterpolateStepFn, IntersectionObserverOptions, IntersectionSignal, IntersectionSignalValue, LiftSignalOptions, MapSignal, MapSignalOptions, MediaQuerySignal, MediaQuerySignalOptions, MediaQueryState, MediaQueryWriteableSignalOptions, MethodKey, MethodParameters, MutationSignal, MutationSignalOptions, MutationSignalValue, NestSignalOptions, NestSignalValue, NumericValues, ReactiveSignal, ReactiveSource, ReactiveValue, ReduceSignal, ResizeSignal, ResizeSignalOptions, ResizeSignalValue, SequenceSignal, SequenceSignalOptions, SequenceSource, SignalToIteratorOptions, SpringNumericSignalOptions, SpringOptions, SpringSignal, SpringSignalOptions, StorageSignalStore, TimerSignal, TimerSignalOptions, TimerSignalStatus, ToSignalInput, TransformedSignal, TweenNumericSignalOptions, TweenOptions, TweenSignal, TweenSignalOptions, UpdaterKey, ValueSource, ValueSourceValue, WebStorageOptions, WritableAnimatedSignal, WritableSpringSignal, WritableTweenSignal };