/** * Type definitions for loader-buttons */ export type LoaderKind = "webgl" | "canvas" | "svg" | "dom"; /** A mounted animation instance, as returned by `LoaderDesign.mount`. */ export interface LoaderVisual { render(timeSeconds: number, deltaSeconds: number): void; resize?(widthCssPixels: number, heightCssPixels: number, devicePixelRatio: number): void; reset?(): void; destroy?(): void; } /** One loader animation module. */ export interface LoaderDesign { id: string; name: string; kind: LoaderKind; technique: string; label?: string; phaseOffset?: number; mount(container: Element): LoaderVisual; } /** Anything accepted where a design is expected. */ export type DesignInput = string | number | LoaderDesign; export declare const designs: readonly LoaderDesign[]; export declare const designsById: Readonly>; export declare const designIds: readonly string[]; export declare function designsByKind(kind: LoaderKind): LoaderDesign[]; export declare function getDesign(id: string): LoaderDesign | undefined; export declare function resolveDesign(design: DesignInput): LoaderDesign; /* -------------------------------------------------------------------------- * * Clock * -------------------------------------------------------------------------- */ export interface ClockOptions { /** Frames per second, clamped to 1-120. Defaults to 30. */ fps?: number; /** Stop rendering while the tab is hidden. Defaults to `true`. */ pauseWhenHidden?: boolean; /** Start as soon as the first subscriber arrives. Defaults to `true`. */ autoStart?: boolean; } export interface LoaderClock { subscribe(callback: (deltaSeconds: number, frameTime: number) => void): () => void; start(): void; stop(): void; setFps(fps: number): void; readonly fps: number; readonly running: boolean; readonly size: number; destroy(): void; } export declare const DEFAULT_FPS: number; export declare function createClock(options?: ClockOptions): LoaderClock; export declare function getSharedClock(fps?: number): LoaderClock; /* -------------------------------------------------------------------------- * * Headless loader * -------------------------------------------------------------------------- */ export interface LoaderErrorContext { design: LoaderDesign; stage: "mount" | "render" | "resize" | "reset"; } export interface MountLoaderOptions { /** Which animation to run. Required. */ design: DesignInput; /** Share an explicit clock instead of the shared one for this fps. */ clock?: LoaderClock; /** Frame budget when no `clock` is given. Defaults to 30. */ fps?: number; /** Time multiplier. `0` freezes, `2` runs at double speed. Defaults to 1. */ speed?: number; /** Seconds the animation starts (and restarts) from. Defaults to 0. */ phaseOffset?: number; /** Start frozen. Defaults to `false`. */ paused?: boolean; /** Upper bound on rendered pixel density. Defaults to 3. */ maxDpr?: number; /** Size assumed when the host element has not been laid out yet. Defaults to 30. */ size?: number; /** Freeze while scrolled out of view. Defaults to `true`. */ pauseOffscreen?: boolean; /** Freeze for visitors who ask for reduced motion. Defaults to `true`. */ respectReducedMotion?: boolean; /** Free WebGL programs while off-screen. Defaults to `true`. */ releaseWebGLOffscreen?: boolean; /** Milliseconds off-screen before WebGL resources are freed. Defaults to 1200. */ releaseDelay?: number; /** IntersectionObserver root margin. Defaults to `"120px 0px"`. */ rootMargin?: string; /** IntersectionObserver threshold. Defaults to 0.01. */ threshold?: number; onError?(error: unknown, context: LoaderErrorContext): void; onFrame?(timeSeconds: number, deltaSeconds: number): void; } export interface LoaderInstance { readonly element: Element; readonly design: LoaderDesign; readonly time: number; readonly speed: number; readonly paused: boolean; readonly visible: boolean; readonly animating: boolean; readonly reducedMotion: boolean; readonly failed: boolean; readonly destroyed: boolean; readonly clock: LoaderClock; play(): LoaderInstance; pause(): LoaderInstance; toggle(): LoaderInstance; reset(): LoaderInstance; seek(seconds: number): LoaderInstance; renderFrame(): LoaderInstance; setSpeed(speed: number): LoaderInstance; setPhaseOffset(seconds: number): LoaderInstance; setMaxDpr(maxDpr: number): LoaderInstance; setDesign(design: DesignInput): LoaderInstance; refresh(): LoaderInstance; destroy(): void; } export declare const loaderDefaults: Readonly>>; export declare function mountLoader( element: Element, options: MountLoaderOptions, ): LoaderInstance; /* -------------------------------------------------------------------------- * * Styled button * -------------------------------------------------------------------------- */ export type LoaderButtonSize = "sm" | "md" | "lg"; export type LoaderButtonVariant = "glass" | "solid" | "outline" | "ghost"; export type LoaderButtonTheme = "light" | "dark" | "auto"; export type LoaderButtonShape = "pill" | "rounded" | "square"; export type LoaderIconPosition = "start" | "end"; /** Design tokens. Numbers are read as pixels where a length is expected. */ export interface LoaderButtonTokens { width?: string | number; height?: string | number; radius?: string | number; slotSize?: string | number; gap?: string | number; paddingInline?: string | number; fontFamily?: string; fontSize?: string | number; fontWeight?: string | number; letterSpacing?: string | number; lineHeight?: string | number; /** Colour the animation draws with. */ ink?: string; labelColor?: string; surface?: string; borderColor?: string; borderWidth?: string | number; blur?: string | number; shadow?: string; focusRing?: string; pressScale?: string | number; tiltAngle?: string; ease?: string; duration?: string; } export interface CreateLoaderButtonOptions extends MountLoaderOptions, LoaderButtonTokens { /** Text shown while loading. Defaults to `"Working"`. */ label?: string; /** Text shown when `loading` is `false`. Falls back to `label`. */ idleLabel?: string; /** Show the animation and freeze it when `false`. Defaults to `true`. */ loading?: boolean; size?: LoaderButtonSize; variant?: LoaderButtonVariant; theme?: LoaderButtonTheme; shape?: LoaderButtonShape; iconPosition?: LoaderIconPosition; type?: "button" | "submit" | "reset"; /** 3D press tilt. Defaults to `true`. */ tilt?: boolean; /** Diagonal highlight sweep on hover. Defaults to `true`. */ sheen?: boolean; /** Conic rim light. Defaults to `true`. */ rim?: boolean; /** Scale down on press. Defaults to `true`. */ press?: boolean; /** Shrink one size step below 690px. Defaults to `true`. */ responsive?: boolean; fullWidth?: boolean; disabled?: boolean; /** Restart the animation on click. Defaults to `true`. */ resetOnClick?: boolean; /** Restart the animation when `loading` flips back on. Defaults to `true`. */ resetOnLoading?: boolean; /** Milliseconds the press animation lasts. Defaults to 140. */ replayDuration?: number; className?: string; id?: string; style?: Record; ariaLabel?: string; /** How to attach to the target element. Defaults to `"append"`. */ mount?: "append" | "prepend" | "replace"; onClick?(event: MouseEvent, button: LoaderButtonInstance): void; } export interface LoaderButtonInstance { readonly element: HTMLDivElement; readonly button: HTMLButtonElement; readonly slot: HTMLSpanElement; readonly labelElement: HTMLSpanElement; readonly loader: LoaderInstance | null; readonly design: LoaderDesign; readonly loading: boolean; readonly disabled: boolean; readonly options: CreateLoaderButtonOptions; readonly destroyed: boolean; update(options: Partial): LoaderButtonInstance; setDesign(design: DesignInput): LoaderButtonInstance; setLabel(text: string): LoaderButtonInstance; setLoading(loading: boolean): LoaderButtonInstance; setDisabled(disabled: boolean): LoaderButtonInstance; setVariant(variant: LoaderButtonVariant): LoaderButtonInstance; setSize(size: LoaderButtonSize): LoaderButtonInstance; setTheme(theme: LoaderButtonTheme): LoaderButtonInstance; setSpeed(speed: number): LoaderButtonInstance; reset(): LoaderButtonInstance; play(): LoaderButtonInstance; pause(): LoaderButtonInstance; destroy(): void; } export declare const buttonDefaults: Readonly>; export declare function createLoaderButton( target: Element, options: CreateLoaderButtonOptions, ): LoaderButtonInstance; export declare function createLoaderButton( options: CreateLoaderButtonOptions, ): LoaderButtonInstance; /* -------------------------------------------------------------------------- * * Authoring helpers and environment * -------------------------------------------------------------------------- */ export declare function createCanvasVisual( container: Element, draw: ( context: CanvasRenderingContext2D, timeSeconds: number, deltaSeconds: number, width: number, height: number, ) => void, options?: { maxDpr?: number }, ): LoaderVisual; export declare function createShaderVisual( container: Element, config: { name?: string; seed?: number; fragment: string; fallback?: ( context: CanvasRenderingContext2D, timeSeconds: number, deltaSeconds: number, width: number, height: number, ) => void; maxDpr?: number; }, ): LoaderVisual; export declare const math: { TAU: number; clamp(value: number, minimum?: number, maximum?: number): number; lerp(start: number, end: number, amount: number): number; inverseLerp(start: number, end: number, value: number): number; smoothstep(edge0: number, edge1: number, value: number): number; easeInOutCubic(value: number): number; easeOutBack(value: number, overshoot?: number): number; fract(value: number): number; pingPong(value: number): number; }; export declare const cssVariableOptions: Readonly>; export declare function buildStyleObject( options?: LoaderButtonTokens, style?: Record | null, ): Record; export declare function isBrowser(): boolean; export declare function prefersReducedMotion(): boolean; export declare function onReducedMotionChange(listener: (matches: boolean) => void): () => void; export declare function currentDevicePixelRatio(): number; export declare function onDevicePixelRatioChange(listener: (ratio: number) => void): () => void;