diff --git a/.changeset/lazy-windows-help.md b/.changeset/lazy-windows-help.md new file mode 100644 index 00000000..26c733d4 --- /dev/null +++ b/.changeset/lazy-windows-help.md @@ -0,0 +1,5 @@ +--- +"runed": patch +--- + +New Function: `useEventListener` diff --git a/packages/runed/src/lib/functions/index.ts b/packages/runed/src/lib/functions/index.ts index fcd4a256..c76ccef0 100644 --- a/packages/runed/src/lib/functions/index.ts +++ b/packages/runed/src/lib/functions/index.ts @@ -1,3 +1,4 @@ export * from "./useActiveElement/index.js"; export * from "./useDebounce/index.js"; export * from "./useElementSize/index.js"; +export * from "./useEventListener/index.js"; diff --git a/packages/runed/src/lib/functions/useEventListener/index.ts b/packages/runed/src/lib/functions/useEventListener/index.ts new file mode 100644 index 00000000..4455e9d1 --- /dev/null +++ b/packages/runed/src/lib/functions/useEventListener/index.ts @@ -0,0 +1 @@ +export * from "./useEventListener.svelte.js"; diff --git a/packages/runed/src/lib/functions/useEventListener/useEventListener.svelte.ts b/packages/runed/src/lib/functions/useEventListener/useEventListener.svelte.ts new file mode 100644 index 00000000..36150e53 --- /dev/null +++ b/packages/runed/src/lib/functions/useEventListener/useEventListener.svelte.ts @@ -0,0 +1,45 @@ +import type { ValueOrGetter } from "$lib/internal/types.js"; +import { boxed } from "$lib/internal/utils/boxed.svelte.js"; +import { addEventListener } from "$lib/internal/utils/event.js"; + +export function useEventListener( + target: ValueOrGetter, + event: TEvent, + handler: (this: Window, event: WindowEventMap[TEvent]) => unknown, + options?: boolean | AddEventListenerOptions +): void; + +export function useEventListener( + target: ValueOrGetter, + event: TEvent, + handler: (this: Document, event: DocumentEventMap[TEvent]) => unknown, + options?: boolean | AddEventListenerOptions +): void; + +export function useEventListener< + TElement extends HTMLElement, + TEvent extends keyof HTMLElementEventMap, +>( + target: ValueOrGetter, + event: TEvent, + handler: (this: TElement, event: HTMLElementEventMap[TEvent]) => unknown, + options?: boolean | AddEventListenerOptions +): void; + +export function useEventListener( + target: ValueOrGetter, + event: string, + handler: EventListenerOrEventListenerObject, + options?: boolean | AddEventListenerOptions +): void; + +export function useEventListener( + _target: ValueOrGetter, + event: string, + handler: EventListenerOrEventListenerObject, + options?: boolean | AddEventListenerOptions +) { + const target = boxed(_target); + + $effect(() => addEventListener(target.value, event, handler, options)); +} diff --git a/packages/runed/src/lib/internal/utils/event.ts b/packages/runed/src/lib/internal/utils/event.ts new file mode 100644 index 00000000..fa3f5f79 --- /dev/null +++ b/packages/runed/src/lib/internal/utils/event.ts @@ -0,0 +1,58 @@ +/** + * Overloaded function signatures for addEventListener + */ +export function addEventListener( + target: Window, + event: TEvent, + handler: (this: Window, event: WindowEventMap[TEvent]) => unknown, + options?: boolean | AddEventListenerOptions +): VoidFunction; + +export function addEventListener( + target: Document, + event: TEvent, + handler: (this: Document, event: DocumentEventMap[TEvent]) => unknown, + options?: boolean | AddEventListenerOptions +): VoidFunction; + +export function addEventListener< + TElement extends HTMLElement, + TEvent extends keyof HTMLElementEventMap, +>( + target: TElement, + event: TEvent, + handler: (this: TElement, event: HTMLElementEventMap[TEvent]) => unknown, + options?: boolean | AddEventListenerOptions +): VoidFunction; + +export function addEventListener( + target: EventTarget, + event: string, + handler: EventListenerOrEventListenerObject, + options?: boolean | AddEventListenerOptions +): VoidFunction; + +/** + * Adds an event listener to the specified target element(s) for the given event(s), and returns a function to remove it. + * @param target The target element(s) to add the event listener to. + * @param event The event(s) to listen for. + * @param handler The function to be called when the event is triggered. + * @param options An optional object that specifies characteristics about the event listener. + * @returns A function that removes the event listener from the target element(s). + */ +export function addEventListener( + target: Window | Document | EventTarget, + event: string | string[], + handler: EventListenerOrEventListenerObject, + options?: boolean | AddEventListenerOptions +) { + const events = Array.isArray(event) ? event : [event]; + + // Add the event listener to each specified event for the target element(s). + events.forEach((_event) => target.addEventListener(_event, handler, options)); + + // Return a function that removes the event listener from the target element(s). + return () => { + events.forEach((_event) => target.removeEventListener(_event, handler, options)); + }; +} diff --git a/sites/docs/content/functions/use-event-listener.md b/sites/docs/content/functions/use-event-listener.md new file mode 100644 index 00000000..aea64e15 --- /dev/null +++ b/sites/docs/content/functions/use-event-listener.md @@ -0,0 +1,29 @@ +--- +title: UseEventListener +description: A function that attaches an automatically disposed event listener. +--- + + + +## Demo + + + +## Usage + +```svelte + + +

You've clicked {count} {count === 1 ? "Time" : "Times"}

+``` diff --git a/sites/docs/src/lib/components/demos/index.ts b/sites/docs/src/lib/components/demos/index.ts index 7a673edf..bb2f1433 100644 --- a/sites/docs/src/lib/components/demos/index.ts +++ b/sites/docs/src/lib/components/demos/index.ts @@ -1,3 +1,4 @@ export { default as UseActiveElementDemo } from "./use-active-element.svelte"; export { default as UseDebounceDemo } from "./use-debounce.svelte"; export { default as UseElementSizeDemo } from "./use-element-size.svelte"; +export { default as UseEventListenerDemo } from "./use-event-listener.svelte"; diff --git a/sites/docs/src/lib/components/demos/use-event-listener.svelte b/sites/docs/src/lib/components/demos/use-event-listener.svelte new file mode 100644 index 00000000..9bc5543c --- /dev/null +++ b/sites/docs/src/lib/components/demos/use-event-listener.svelte @@ -0,0 +1,14 @@ + + +
+

You've clicked {count} {count === 1 ? "Time" : "Times"}

+
diff --git a/sites/docs/src/lib/config/navigation.ts b/sites/docs/src/lib/config/navigation.ts index 2215e9d1..b6e34c81 100644 --- a/sites/docs/src/lib/config/navigation.ts +++ b/sites/docs/src/lib/config/navigation.ts @@ -76,6 +76,11 @@ export const navigation: Navigation = { href: "/docs/functions/use-element-size", items: [], }, + { + title: "useEventListener", + href: "/docs/functions/use-event-listener", + items: [], + }, ], }, ],