UNPKG

watch-selector

Version:

Runs a function when a selector is added to dom

259 lines 16.6 kB
export type ElementFromSelector<S extends string> = S extends `input[type="text"]${string}` ? HTMLInputElement : S extends `input[type="email"]${string}` ? HTMLInputElement : S extends `input[type="password"]${string}` ? HTMLInputElement : S extends `input[type="search"]${string}` ? HTMLInputElement : S extends `input[type="tel"]${string}` ? HTMLInputElement : S extends `input[type="url"]${string}` ? HTMLInputElement : S extends `input[type="number"]${string}` ? HTMLInputElement : S extends `input[type="range"]${string}` ? HTMLInputElement : S extends `input[type="date"]${string}` ? HTMLInputElement : S extends `input[type="time"]${string}` ? HTMLInputElement : S extends `input[type="datetime-local"]${string}` ? HTMLInputElement : S extends `input[type="month"]${string}` ? HTMLInputElement : S extends `input[type="week"]${string}` ? HTMLInputElement : S extends `input[type="color"]${string}` ? HTMLInputElement : S extends `input[type="file"]${string}` ? HTMLInputElement : S extends `input[type="hidden"]${string}` ? HTMLInputElement : S extends `input[type="checkbox"]${string}` ? HTMLInputElement : S extends `input[type="radio"]${string}` ? HTMLInputElement : S extends `input[type="submit"]${string}` ? HTMLInputElement : S extends `input[type="reset"]${string}` ? HTMLInputElement : S extends `input[type="button"]${string}` ? HTMLInputElement : S extends `input[type="image"]${string}` ? HTMLInputElement : S extends `input${string}` ? HTMLInputElement : S extends `button${string}` ? HTMLButtonElement : S extends `form${string}` ? HTMLFormElement : S extends `a${string}` ? HTMLAnchorElement : S extends `img${string}` ? HTMLImageElement : S extends `select${string}` ? HTMLSelectElement : S extends `textarea${string}` ? HTMLTextAreaElement : S extends `div${string}` ? HTMLDivElement : S extends `span${string}` ? HTMLSpanElement : S extends `p${string}` ? HTMLParagraphElement : S extends `h1${string}` ? HTMLHeadingElement : S extends `h2${string}` ? HTMLHeadingElement : S extends `h3${string}` ? HTMLHeadingElement : S extends `h4${string}` ? HTMLHeadingElement : S extends `h5${string}` ? HTMLHeadingElement : S extends `h6${string}` ? HTMLHeadingElement : S extends `ul${string}` ? HTMLUListElement : S extends `ol${string}` ? HTMLOListElement : S extends `li${string}` ? HTMLLIElement : S extends `table${string}` ? HTMLTableElement : S extends `tr${string}` ? HTMLTableRowElement : S extends `td${string}` ? HTMLTableCellElement : S extends `th${string}` ? HTMLTableCellElement : S extends `thead${string}` ? HTMLTableSectionElement : S extends `tbody${string}` ? HTMLTableSectionElement : S extends `tfoot${string}` ? HTMLTableSectionElement : S extends `canvas${string}` ? HTMLCanvasElement : S extends `video${string}` ? HTMLVideoElement : S extends `audio${string}` ? HTMLAudioElement : S extends `iframe${string}` ? HTMLIFrameElement : S extends `script${string}` ? HTMLScriptElement : S extends `link${string}` ? HTMLLinkElement : S extends `style${string}` ? HTMLStyleElement : S extends `meta${string}` ? HTMLMetaElement : S extends `title${string}` ? HTMLTitleElement : S extends `head${string}` ? HTMLHeadElement : S extends `body${string}` ? HTMLBodyElement : S extends `html${string}` ? HTMLHtmlElement : HTMLElement; export type ElementHandler<El extends HTMLElement = HTMLElement> = (element: El) => void; export type ElementFn<El extends Element = HTMLElement, T = void> = (element: El) => T; export type Selector = string; export interface GeneratorContext<El extends HTMLElement = HTMLElement> { readonly element: El; readonly selector: string; readonly index: number; readonly array: readonly El[]; } export type ElementProxy<El extends HTMLElement = HTMLElement> = El & { <T extends HTMLElement = HTMLElement>(selector: string): T | null; all<T extends HTMLElement = HTMLElement>(selector: string): T[]; }; export type SelfFunction<El extends HTMLElement = HTMLElement> = () => El; export type GeneratorFunction<El extends HTMLElement = HTMLElement, T = void> = (() => Generator<ElementFn<El, any>, T, unknown>) | (() => AsyncGenerator<ElementFn<El, any>, T, unknown>); export type GeneratorYield<El extends HTMLElement = HTMLElement> = ElementFn<El, any> | Promise<ElementFn<El, any>> | Generator<ElementFn<El, any>, void, unknown> | AsyncGenerator<ElementFn<El, any>, void, unknown> | Promise<any>; export interface ParentContext<ParentEl extends HTMLElement = HTMLElement, ParentApi = any> { element: ParentEl; api: ParentApi; } export type ContextFunction<El extends HTMLElement = HTMLElement> = () => WatchContext<El>; export interface TypedGeneratorContext<El extends HTMLElement = HTMLElement> { self(): El; el<T extends HTMLElement = HTMLElement>(selector: string): T | null; all<T extends HTMLElement = HTMLElement>(selector: string): T[]; cleanup(fn: CleanupFunction): void; ctx(): WatchContext<El>; readonly element: El; readonly selector: string; readonly index: number; readonly array: readonly El[]; } export interface PreDefinedWatchContext<S extends string = string, El extends HTMLElement = ElementFromSelector<S>, Options extends Record<string, unknown> = Record<string, unknown>> { readonly selector: S; readonly elementType: El; readonly options: Options; readonly __brand: "PreDefinedWatchContext"; } export interface WatchContextOptions extends Record<string, unknown> { debounce?: number; throttle?: number; once?: boolean; data?: Record<string, unknown>; filter?: (element: HTMLElement) => boolean; priority?: number; } export type DualAPI<DirectArgs extends readonly unknown[], GeneratorArgs extends readonly unknown[], El extends HTMLElement = HTMLElement, ReturnType = void> = { (...args: [...DirectArgs, El]): ReturnType; (...args: GeneratorArgs): ElementFn<El, ReturnType>; }; export type ElementEventHandler<El extends HTMLElement = HTMLElement, K extends keyof HTMLElementEventMap = keyof HTMLElementEventMap> = (event: HTMLElementEventMap[K], element: El) => void; export type CustomEventHandler<El extends HTMLElement = HTMLElement, T = any> = (event: CustomEvent<T>, element: El) => void; export type EventHandler<El extends HTMLElement = HTMLElement, K extends keyof HTMLElementEventMap = keyof HTMLElementEventMap, T = any> = ElementEventHandler<El, K> | CustomEventHandler<El, T>; export interface WatchEventListenerOptions extends AddEventListenerOptions { /** Enable delegation - listen on parent and match against selector */ delegate?: string; /** Debounce the event handler (milliseconds) */ debounce?: number; /** Throttle the event handler (milliseconds) */ throttle?: number; /** Only handle events from specific elements */ filter?: (event: Event, element: HTMLElement) => boolean; } /** * The possible return types for an event handler, which can be a regular * function, an async function, or a synchronous/asynchronous generator. */ export type EventHandlerResult = void | Promise<void> | Generator<any, void, any> | AsyncGenerator<any, void, any>; /** * A generic event handler function type. * @template E - The specific Event type (e.g., MouseEvent, KeyboardEvent). */ export type EventHandler<E extends Event = Event> = (event: E) => EventHandlerResult; export type HybridEventHandler<El extends Element = HTMLElement, K extends keyof HTMLElementEventMap = keyof HTMLElementEventMap> = ((event: HTMLElementEventMap[K], element?: El) => void) | ((event: HTMLElementEventMap[K], element?: El) => Promise<void>) | ((event: HTMLElementEventMap[K], element?: El) => Generator<ElementFn<El>, void, unknown>) | ((event: HTMLElementEventMap[K], element?: El) => AsyncGenerator<ElementFn<El>, void, unknown>) | ((event: HTMLElementEventMap[K]) => void) | ((event: HTMLElementEventMap[K]) => Promise<void>) | ((event: HTMLElementEventMap[K]) => Generator<ElementFn<El>, void, unknown>) | ((event: HTMLElementEventMap[K]) => AsyncGenerator<ElementFn<El>, void, unknown>); export type HybridCustomEventHandler<El extends Element = HTMLElement, T = any> = ((event: CustomEvent<T>, element?: El) => void) | ((event: CustomEvent<T>, element?: El) => Promise<void>) | ((event: CustomEvent<T>, element?: El) => Generator<ElementFn<El>, void, unknown>) | ((event: CustomEvent<T>, element?: El) => AsyncGenerator<ElementFn<El>, void, unknown>) | ((event: CustomEvent<T>) => void) | ((event: CustomEvent<T>) => Promise<void>) | ((event: CustomEvent<T>) => Generator<ElementFn<El>, void, unknown>) | ((event: CustomEvent<T>) => AsyncGenerator<ElementFn<El>, void, unknown>); export interface DebounceOptions { /** Wait time in milliseconds */ wait: number; /** Execute on leading edge */ leading?: boolean; /** Execute on trailing edge (default: true) */ trailing?: boolean; } export interface ThrottleOptions { /** Limit in milliseconds */ limit: number; /** Execute on leading edge (default: true) */ leading?: boolean; /** Execute on trailing edge */ trailing?: boolean; } export interface HybridEventOptions extends Omit<AddEventListenerOptions, "signal"> { /** Enable delegation - listen on parent and match against selector */ delegate?: string; /** Delegation phase - bubble (default) or capture */ delegatePhase?: "bubble" | "capture"; /** Debounce configuration */ debounce?: number | DebounceOptions; /** Throttle configuration */ throttle?: number | ThrottleOptions; /** Filter function to conditionally handle events */ filter?: (event: Event, element: HTMLElement) => boolean; /** AbortSignal for cleanup */ signal?: AbortSignal; /** Queue concurrent async generators: 'latest' | 'all' | 'none' (default: 'all') */ queue?: "latest" | "all" | "none"; } export type FormElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement; export type Prettify<T> = { [K in keyof T]: T[K]; } & {}; export type CSSPropertyName = keyof CSSStyleDeclaration; export type AttributeName = string; export type DataAttributeKey = string; export type EventName<_El extends HTMLElement = HTMLElement> = keyof HTMLElementEventMap; export type ElementMatcher<El extends HTMLElement = HTMLElement> = (element: HTMLElement) => element is El; export interface TypedGeneratorContext<El extends HTMLElement = HTMLElement> { self: () => El; el: <T extends HTMLElement = HTMLElement>(selector: string) => T | null; all: <T extends HTMLElement = HTMLElement>(selector: string) => T[]; cleanup: (fn: CleanupFunction) => void; ctx: () => WatchContext<El>; readonly element: El; readonly selector: string; readonly index: number; readonly array: readonly El[]; } export type AdvancedMatcher = (element: HTMLElement) => "skip" | "observe" | "queue"; export type WatchTarget<El extends HTMLElement = HTMLElement> = string | El | El[] | NodeListOf<El> | ElementMatcher<El> | AdvancedMatcher | { parent: HTMLElement; childSelector: string; } | { parent: HTMLElement; selector: string; } | { parent: HTMLElement; matcher: ElementMatcher<any>; }; export interface AttributeChange { attributeName: string; oldValue: string | null; newValue: string | null; } export interface TextChange { oldText: string; newText: string; } export interface VisibilityChange { isVisible: boolean; intersectionRatio: number; boundingClientRect: DOMRectReadOnly; } export interface ResizeChange { contentRect: DOMRectReadOnly; borderBoxSize: readonly ResizeObserverSize[]; contentBoxSize: readonly ResizeObserverSize[]; devicePixelContentBoxSize?: readonly ResizeObserverSize[]; } export type MountHandler<El extends HTMLElement = HTMLElement> = (element: El) => void; export type UnmountHandler<El extends HTMLElement = HTMLElement> = (element: El) => void; export type CleanupFunction = () => void; export type SelectorRegistry = Map<string, Set<ElementHandler>>; export type UnmountRegistry = WeakMap<HTMLElement, Set<UnmountHandler>>; export type TypedState<T = any> = { get(): T; set(value: T): void; update(fn: (current: T) => T): void; init(value: T): void; }; export type Operation<TReturn, El extends HTMLElement = HTMLElement> = (context: WatchContext<El>) => TReturn | Promise<TReturn>; export type SyncWorkflow<TReturn = void> = Generator<Operation<any, any>, TReturn, any>; export type AsyncWorkflow<TReturn = void> = AsyncGenerator<Operation<any, any>, TReturn, any>; export type Workflow<TReturn = void> = SyncWorkflow<TReturn>; export interface EnhancedWatchContext<El extends HTMLElement = HTMLElement> extends Omit<WatchContext<El>, "state"> { readonly element: El; readonly selector: string; readonly index: number; readonly array: readonly El[]; readonly state: { get<T>(key: string, defaultValue?: T): T; set<T>(key: string, value: T): void; update<T>(key: string, updater: (current: T) => T): void; has(key: string): boolean; delete(key: string): boolean; watch<T>(key: string, callback: (newValue: T, oldValue: T) => void): () => void; }; readonly parentContext?: EnhancedWatchContext; readonly cleanup: (fn: () => void) => void; readonly addObserver: (observer: MutationObserver | IntersectionObserver | ResizeObserver) => void; } export type OperationContext<El extends HTMLElement = HTMLElement> = EnhancedWatchContext<El>; export type OperationResult<T = any> = T; export type WorkflowFunction<El extends HTMLElement = HTMLElement, T = void> = (context: OperationContext<El>) => SyncWorkflow<T>; export type OperationFactory<TArgs extends readonly unknown[], TReturn, El extends HTMLElement = HTMLElement> = (...args: TArgs) => Operation<TReturn, El>; export type WorkflowComposer<El extends HTMLElement = HTMLElement> = (...workflows: WorkflowFunction<El>[]) => WorkflowFunction<El>; /** * Represents a managed instance of a watched element, providing access to * its element and a snapshot of its state for introspection purposes. */ export interface ManagedInstance { readonly element: HTMLElement; getState: () => Readonly<Record<string, any>>; } /** * The controller object returned by the watch() function. It provides a handle * to the watch operation, enabling advanced control like behavior layering, * instance introspection, and manual destruction. * * For backward compatibility, controllers are callable as cleanup functions. */ export interface WatchController<El extends HTMLElement = HTMLElement> { /** The original subject (selector, element, etc.) that this controller manages. */ readonly subject: WatchTarget<El>; /** Returns a read-only Map of the current elements being managed by this watcher. */ getInstances(): ReadonlyMap<El, ManagedInstance>; /** * Adds a new behavior "layer" to the watched elements. This generator will be * executed on all current and future elements matched by this controller. */ layer(generator: (ctx: TypedGeneratorContext<El>) => Generator<ElementFn<El, any>, any, unknown> | AsyncGenerator<any, any, unknown>): void; /** * Destroys this watch operation entirely, cleaning up all its layered behaviors * and removing all listeners and observers for all managed instances. */ destroy(): void; /** * Backward compatibility: Allow controller to be called as a cleanup function */ (): void; } export interface WatchedInstance<El extends HTMLElement = HTMLElement> { element: El; state: Record<string, any>; observers: Set<MutationObserver | IntersectionObserver | ResizeObserver>; cleanupFns: (() => void)[]; } export interface WatchConfig<El extends HTMLElement = HTMLElement> { subject: WatchTarget<El>; parentScope: HTMLElement; mountFns: ((instance: WatchedInstance<El>) => void)[]; unmountFns: ((instance: WatchedInstance<El>) => void)[]; instances: Map<HTMLElement, WatchedInstance<El>>; globalObserver?: MutationObserver; } export interface WatchContext<El extends HTMLElement = HTMLElement> { element: El; selector: string; index: number; array: readonly El[]; state: Map<string, any>; observers: Set<MutationObserver | IntersectionObserver | ResizeObserver>; el: ElementProxy<El>; self: SelfFunction<El>; cleanup: (fn: CleanupFunction) => void; addObserver: (observer: MutationObserver | IntersectionObserver | ResizeObserver) => void; signal?: AbortSignal; } //# sourceMappingURL=types.d.ts.map