angular-signals
Version:
Advanced Angular signals library with deep equality checking and physics-based animations
469 lines (451 loc) • 14.7 kB
TypeScript
import * as _angular_core from '@angular/core';
import { WritableSignal, Signal } from '@angular/core';
declare function deepSignal<T>(value: T): WritableSignal<T>;
declare function deepComputed<T>(computation: () => T): _angular_core.Signal<T>;
interface SpringConfig {
stiffness?: number;
damping?: number;
precision?: number;
}
interface SpringSignals<T extends number | number[]> {
current: Signal<T>;
target: WritableSignal<T>;
}
/**
* Creates a spring animation that smoothly interpolates between values.
*
* @param initialValue - The initial value (number or array of numbers)
* @param config - Spring configuration with stiffness, damping, and precision
* @returns Object with `current` (readonly signal) and `target` (writable signal)
*
* @example
* ```ts
* const { current, target } = spring(0, { stiffness: 0.15, damping: 0.8 });
*
* // Update target to animate
* target.set(100);
*
* // Read current animated value
* console.log(current());
* ```
*/
declare function spring(initialValue: number, config?: SpringConfig): SpringSignals<number>;
declare function spring(initialValue: number[], config?: SpringConfig): SpringSignals<number[]>;
type InterpolateFunction<T> = (from: T, to: T) => (t: number) => T;
interface TweenOptions<T extends number | number[]> {
duration: number;
delay?: number;
easing?: (t: number) => number;
interpolate?: InterpolateFunction<T>;
}
interface TweenSignals<T extends number | number[]> {
current: Signal<T>;
target: WritableSignal<T>;
}
/**
* Creates a tween animation that smoothly interpolates between values over a fixed duration.
*
* @param initialValue - The initial value (number or array of numbers)
* @param options - Tween configuration with duration, delay, easing function, and interpolation
* @returns Object with `current` (readonly signal) and `target` (writable signal)
*
* @example
* ```ts
* // Basic usage
* const { current, target } = tween(0, {
* duration: 1000,
* delay: 500,
* easing: (t) => t * t
* });
*
* // With custom interpolation (curried function)
* const { current, target } = tween(0, {
* duration: 1000,
* interpolate: (from, to) => (t) => from + (to - from) * t
* });
*
* // Update target to animate
* target.set(100);
*
* // Read current animated value
* console.log(current());
* ```
*/
declare function tween(initialValue: number, options: TweenOptions<number>): TweenSignals<number>;
declare function tween(initialValue: number[], options: TweenOptions<number[]>): TweenSignals<number[]>;
/**
* Creates a signal that tracks the previous value of another signal.
*
* @param source - The signal to track
* @returns A signal containing the previous value (undefined on first read)
*
* @example
* ```ts
* const count = signal(0);
* const prevCount = usePrevious(count);
*
* console.log(prevCount()); // undefined
* count.set(1);
* console.log(prevCount()); // 0
* count.set(2);
* console.log(prevCount()); // 1
* ```
*/
declare function usePrevious<T>(source: Signal<T>): Signal<T | undefined>;
/**
* Return type for the useToggle function.
*/
interface ToggleReturn {
/** The current boolean value */
value: Signal<boolean>;
/** Toggle the value */
toggle: () => void;
/** Set value to true */
setTrue: () => void;
/** Set value to false */
setFalse: () => void;
}
/**
* Creates a boolean signal with convenient toggle methods.
*
* @param initial - Initial boolean value (default: false)
* @returns Object with value signal and toggle methods
*
* @example
* ```ts
* const modal = useToggle(false);
*
* console.log(modal.value()); // false
* modal.toggle();
* console.log(modal.value()); // true
* modal.setFalse();
* console.log(modal.value()); // false
* ```
*/
declare function useToggle(initial?: boolean): ToggleReturn;
/**
* Return type for the useCounter function.
*/
interface CounterReturn {
/** The current count value */
count: Signal<number>;
/** Increment the counter by step (default: 1) */
increment: (step?: number) => void;
/** Decrement the counter by step (default: 1) */
decrement: (step?: number) => void;
/** Reset the counter to initial or specified value */
reset: (value?: number) => void;
/** Set the counter to a specific value */
set: (value: number) => void;
}
/**
* Creates a counter signal with increment/decrement methods and optional bounds.
*
* @param initial - Initial count value (default: 0)
* @param min - Minimum allowed value (default: -Infinity)
* @param max - Maximum allowed value (default: Infinity)
* @returns Object with count signal and manipulation methods
*
* @example
* ```ts
* const counter = useCounter(0, 0, 10);
*
* console.log(counter.count()); // 0
* counter.increment();
* console.log(counter.count()); // 1
* counter.increment(5);
* console.log(counter.count()); // 6
* counter.increment(10);
* console.log(counter.count()); // 10 (clamped to max)
* counter.reset();
* console.log(counter.count()); // 0
* ```
*/
declare function useCounter(initial?: number, min?: number, max?: number): CounterReturn;
/**
* Return type for the useArray function.
*/
interface ArrayReturn<T> {
/** The current array value */
items: Signal<readonly T[]>;
/** Add items to the end of the array */
push: (...items: T[]) => void;
/** Remove and return the last item */
pop: () => T | undefined;
/** Remove and return the first item */
shift: () => T | undefined;
/** Add items to the beginning of the array */
unshift: (...items: T[]) => void;
/** Filter items based on predicate */
filter: (predicate: (item: T, index: number) => boolean) => void;
/** Map items to new values */
map: <U>(fn: (item: T, index: number) => U) => void;
/** Remove all items */
clear: () => void;
/** Remove item at specific index */
remove: (index: number) => void;
/** Insert item at specific index */
insert: (index: number, item: T) => void;
/** Replace the entire array */
set: (items: T[]) => void;
/** Find first item matching predicate */
find: (predicate: (item: T) => boolean) => T | undefined;
/** Find index of first item matching predicate */
findIndex: (predicate: (item: T) => boolean) => number;
}
/**
* Creates an array signal with convenient manipulation methods.
* All operations return new arrays (immutable).
*
* @param initial - Initial array value (default: [])
* @returns Object with items signal and array manipulation methods
*
* @example
* ```ts
* const todos = useArray<string>(['Buy milk', 'Walk dog']);
*
* console.log(todos.items()); // ['Buy milk', 'Walk dog']
* todos.push('Clean room');
* console.log(todos.items()); // ['Buy milk', 'Walk dog', 'Clean room']
* todos.remove(0);
* console.log(todos.items()); // ['Walk dog', 'Clean room']
* todos.clear();
* console.log(todos.items()); // []
* ```
*/
declare function useArray<T>(initial?: T[]): ArrayReturn<T>;
/**
* Configuration options for debouncing.
*/
interface DebounceOptions {
/** Delay in milliseconds */
delay: number;
/** Execute on the leading edge (default: false) */
leading?: boolean;
/** Execute on the trailing edge (default: true) */
trailing?: boolean;
}
/**
* Creates a debounced version of a signal that delays updates.
*
* @param source - The signal to debounce
* @param options - Debounce configuration
* @returns A debounced signal
*
* @example
* ```ts
* const search = signal('');
* const debouncedSearch = useDebounce(search, { delay: 300 });
*
* search.set('hello'); // debouncedSearch updates after 300ms
* ```
*/
declare function useDebounce<T>(source: Signal<T>, options: DebounceOptions): Signal<T>;
/**
* Configuration options for throttling.
*/
interface ThrottleOptions {
/** Delay in milliseconds */
delay: number;
/** Execute on the leading edge (default: true) */
leading?: boolean;
/** Execute on the trailing edge (default: false) */
trailing?: boolean;
}
/**
* Creates a throttled version of a signal that limits update frequency.
*
* @param source - The signal to throttle
* @param options - Throttle configuration
* @returns A throttled signal
*
* @example
* ```ts
* const scrollY = signal(0);
* const throttledScrollY = useThrottle(scrollY, { delay: 100 });
*
* // scrollY updates frequently, but throttledScrollY updates at most once per 100ms
* ```
*/
declare function useThrottle<T>(source: Signal<T>, options: ThrottleOptions): Signal<T>;
/**
* Return type for the useInterval function.
*/
interface IntervalReturn {
/** Number of times the interval has executed */
count: Signal<number>;
/** Whether the interval is currently active */
isActive: Signal<boolean>;
/** Pause the interval */
pause: () => void;
/** Resume the interval */
resume: () => void;
/** Reset the count and restart the interval */
reset: () => void;
}
/**
* Creates an interval that executes a callback repeatedly with pause/resume control.
*
* @param callback - Function to execute on each interval
* @param delay - Delay in milliseconds (or signal of delay)
* @returns Object with control methods and state signals
*
* @example
* ```ts
* const timer = useInterval(() => {
* console.log('Tick');
* }, 1000);
*
* console.log(timer.count()); // Number of ticks
* timer.pause(); // Pause the interval
* timer.resume(); // Resume the interval
* timer.reset(); // Reset count and restart
* ```
*/
declare function useInterval(callback: () => void, delay: number | Signal<number>): IntervalReturn;
/**
* Return type for the useTimeout function.
*/
interface TimeoutReturn {
/** Whether the timeout has completed */
isReady: Signal<boolean>;
/** Whether the timeout is currently pending */
isPending: Signal<boolean>;
/** Cancel the timeout */
cancel: () => void;
/** Reset and restart the timeout */
reset: () => void;
}
/**
* Creates a timeout that executes a callback after a delay with cancel/reset control.
*
* @param callback - Function to execute after timeout
* @param delay - Delay in milliseconds (or signal of delay)
* @returns Object with control methods and state signals
*
* @example
* ```ts
* const timeout = useTimeout(() => {
* console.log('Timeout completed!');
* }, 3000);
*
* effect(() => {
* if (timeout.isReady()) {
* console.log('Ready!');
* }
* });
*
* timeout.cancel(); // Cancel the timeout
* timeout.reset(); // Reset and restart
* ```
*/
declare function useTimeout(callback: () => void, delay: number | Signal<number>): TimeoutReturn;
/**
* Configuration options for useNow.
*/
interface NowOptions {
/** Update interval in milliseconds (default: 1000) */
interval?: number;
}
/**
* Creates a signal that contains the current Date, updating at a specified interval.
*
* @param options - Configuration options
* @returns A signal containing the current Date
*
* @example
* ```ts
* const now = useNow({ interval: 1000 });
*
* effect(() => {
* console.log('Current time:', now().toLocaleTimeString());
* });
* ```
*/
declare function useNow(options?: NowOptions): Signal<Date>;
/**
* Creates a signal that tracks whether a media query matches.
*
* @param query - CSS media query string or signal of query
* @returns A signal indicating if the media query matches
*
* @example
* ```ts
* const isMobile = useMediaQuery('(max-width: 768px)');
* const isDark = useMediaQuery('(prefers-color-scheme: dark)');
*
* effect(() => {
* console.log('Is mobile:', isMobile());
* console.log('Dark mode:', isDark());
* });
* ```
*/
declare function useMediaQuery(query: string | Signal<string>): Signal<boolean>;
/**
* Attaches an event listener to a target with automatic cleanup.
*
* @param target - Event target (Window, Document, HTMLElement, or signal of element)
* @param event - Event name
* @param handler - Event handler function
* @param options - AddEventListener options
*
* @example
* ```ts
* // Listen to window resize
* useEventListener(window, 'resize', () => {
* console.log('Window resized');
* });
*
* // Listen to element clicks
* const button = signal<HTMLElement | null>(null);
* useEventListener(button, 'click', (event) => {
* console.log('Button clicked', event);
* });
* ```
*/
declare function useEventListener<K extends keyof WindowEventMap>(target: Window, event: K, handler: (event: WindowEventMap[K]) => void, options?: AddEventListenerOptions): void;
declare function useEventListener<K extends keyof DocumentEventMap>(target: Document, event: K, handler: (event: DocumentEventMap[K]) => void, options?: AddEventListenerOptions): void;
declare function useEventListener<K extends keyof HTMLElementEventMap>(target: HTMLElement | Signal<HTMLElement | null>, event: K, handler: (event: HTMLElementEventMap[K]) => void, options?: AddEventListenerOptions): void;
/**
* Return type for storage functions.
*/
interface StorageReturn<T> {
/** The stored value */
value: WritableSignal<T>;
/** Remove the value from storage */
remove: () => void;
}
/**
* Creates a signal synced with localStorage.
*
* @param key - Storage key
* @param initialValue - Initial value if key doesn't exist
* @returns Object with value signal and remove method
*
* @example
* ```ts
* const theme = useLocalStorage('theme', 'light');
*
* theme.value.set('dark'); // Persists to localStorage
* console.log(theme.value()); // 'dark'
* theme.remove(); // Removes from localStorage
* ```
*/
declare function useLocalStorage<T>(key: string, initialValue: T): StorageReturn<T>;
/**
* Creates a signal synced with sessionStorage.
*
* @param key - Storage key
* @param initialValue - Initial value if key doesn't exist
* @returns Object with value signal and remove method
*
* @example
* ```ts
* const sessionData = useSessionStorage('user', { name: 'Guest' });
*
* sessionData.value.set({ name: 'John' }); // Persists to sessionStorage
* ```
*/
declare function useSessionStorage<T>(key: string, initialValue: T): StorageReturn<T>;
export { deepComputed, deepSignal, spring, tween, useArray, useCounter, useDebounce, useEventListener, useInterval, useLocalStorage, useMediaQuery, useNow, usePrevious, useSessionStorage, useThrottle, useTimeout, useToggle };
export type { ArrayReturn, CounterReturn, DebounceOptions, InterpolateFunction, IntervalReturn, NowOptions, SpringConfig, SpringSignals, StorageReturn, ThrottleOptions, TimeoutReturn, ToggleReturn, TweenOptions, TweenSignals };