Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added useEventListener #7

Merged
merged 9 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/lazy-windows-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"runed": patch
---

New Function: `useEventListener`
1 change: 1 addition & 0 deletions packages/runed/src/lib/functions/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./useActiveElement/index.js";
export * from "./useDebounce/index.js";
export * from "./useElementSize/index.js";
export * from "./useEventListener/index.js";
1 change: 1 addition & 0 deletions packages/runed/src/lib/functions/useEventListener/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./useEventListener.svelte.js";
Original file line number Diff line number Diff line change
@@ -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<TEvent extends keyof WindowEventMap>(
target: ValueOrGetter<Window>,
event: TEvent,
handler: (this: Window, event: WindowEventMap[TEvent]) => unknown,
options?: boolean | AddEventListenerOptions
): void;

export function useEventListener<TEvent extends keyof DocumentEventMap>(
target: ValueOrGetter<Document>,
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<TElement>,
event: TEvent,
handler: (this: TElement, event: HTMLElementEventMap[TEvent]) => unknown,
options?: boolean | AddEventListenerOptions
): void;

export function useEventListener(
target: ValueOrGetter<EventTarget>,
event: string,
handler: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions
): void;

export function useEventListener(
_target: ValueOrGetter<EventTarget>,
event: string,
handler: EventListenerOrEventListenerObject,
options?: boolean | AddEventListenerOptions
) {
const target = boxed(_target);

$effect(() => addEventListener(target.value, event, handler, options));
}
58 changes: 58 additions & 0 deletions packages/runed/src/lib/internal/utils/event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* Overloaded function signatures for addEventListener
*/
export function addEventListener<TEvent extends keyof WindowEventMap>(
target: Window,
event: TEvent,
handler: (this: Window, event: WindowEventMap[TEvent]) => unknown,
options?: boolean | AddEventListenerOptions
): VoidFunction;

export function addEventListener<TEvent extends keyof DocumentEventMap>(
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));
};
}
29 changes: 29 additions & 0 deletions sites/docs/content/functions/use-event-listener.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
title: UseEventListener
description: A function that attaches an automatically disposed event listener.
---

<script>
import { UseEventListenerDemo } from '$lib/components/demos';
</script>

## Demo

<UseEventListenerDemo />

## Usage

```svelte
<script lang="ts">
import { useEventListener } from "runed";

let count = $state(0);
function increment() {
count++;
}

useEventListener(() => document, "click", increment);
</script>

<p>You've clicked {count} {count === 1 ? "Time" : "Times"}</p>
```
1 change: 1 addition & 0 deletions sites/docs/src/lib/components/demos/index.ts
Original file line number Diff line number Diff line change
@@ -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";
14 changes: 14 additions & 0 deletions sites/docs/src/lib/components/demos/use-event-listener.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script lang="ts">
import { useEventListener } from "runed";

let count = $state(0);
function increment() {
count++;
}

useEventListener(() => document, "click", increment);
</script>

<div class="select-none rounded-md bg-card p-8">
<p>You've clicked {count} {count === 1 ? "Time" : "Times"}</p>
</div>
5 changes: 5 additions & 0 deletions sites/docs/src/lib/config/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ export const navigation: Navigation = {
href: "/docs/functions/use-element-size",
items: [],
},
{
title: "useEventListener",
href: "/docs/functions/use-event-listener",
items: [],
},
],
},
],
Expand Down
Loading