From 59e66152d58c10cca0b37201dc057ed363a83c4f Mon Sep 17 00:00:00 2001 From: Hunter Johnston Date: Tue, 17 Dec 2024 21:12:57 -0500 Subject: [PATCH 1/2] feat: `IsInViewport` --- .changeset/empty-candles-cough.md | 5 ++ .../IsInViewport/IsInViewport.svelte.ts | 38 ++++++++++++++ .../src/lib/utilities/IsInViewport/index.ts | 1 + packages/runed/src/lib/utilities/index.ts | 1 + .../src/content/utilities/is-in-viewport.md | 49 +++++++++++++++++++ .../components/demos/is-in-viewport.svelte | 26 ++++++++++ 6 files changed, 120 insertions(+) create mode 100644 .changeset/empty-candles-cough.md create mode 100644 packages/runed/src/lib/utilities/IsInViewport/IsInViewport.svelte.ts create mode 100644 packages/runed/src/lib/utilities/IsInViewport/index.ts create mode 100644 sites/docs/src/content/utilities/is-in-viewport.md create mode 100644 sites/docs/src/lib/components/demos/is-in-viewport.svelte diff --git a/.changeset/empty-candles-cough.md b/.changeset/empty-candles-cough.md new file mode 100644 index 00000000..b2ed50d0 --- /dev/null +++ b/.changeset/empty-candles-cough.md @@ -0,0 +1,5 @@ +--- +"runed": minor +--- + +feat: `IsInViewport` diff --git a/packages/runed/src/lib/utilities/IsInViewport/IsInViewport.svelte.ts b/packages/runed/src/lib/utilities/IsInViewport/IsInViewport.svelte.ts new file mode 100644 index 00000000..2051b82c --- /dev/null +++ b/packages/runed/src/lib/utilities/IsInViewport/IsInViewport.svelte.ts @@ -0,0 +1,38 @@ +import type { MaybeGetter } from "$lib/internal/types.js"; +import { + useIntersectionObserver, + type UseIntersectionObserverOptions, +} from "../useIntersectionObserver/useIntersectionObserver.svelte.js"; + +export type IsInViewportOptions = UseIntersectionObserverOptions; + +/** + * Tracks if an element is visible within the current viewport. + * + * @see {@link https://runed.dev/docs/utilities/is-in-viewport} + */ +export class IsInViewport { + #isInViewport = $state(false); + + constructor(node: MaybeGetter, options?: IsInViewportOptions) { + useIntersectionObserver( + node, + (intersectionObserverEntries) => { + let isIntersecting = this.#isInViewport; + let latestTime = 0; + for (const entry of intersectionObserverEntries) { + if (entry.time >= latestTime) { + latestTime = entry.time; + isIntersecting = entry.isIntersecting; + } + } + this.#isInViewport = isIntersecting; + }, + options + ); + } + + get current() { + return this.#isInViewport; + } +} diff --git a/packages/runed/src/lib/utilities/IsInViewport/index.ts b/packages/runed/src/lib/utilities/IsInViewport/index.ts new file mode 100644 index 00000000..03d0db36 --- /dev/null +++ b/packages/runed/src/lib/utilities/IsInViewport/index.ts @@ -0,0 +1 @@ +export * from "./IsInViewport.svelte.js"; \ No newline at end of file diff --git a/packages/runed/src/lib/utilities/index.ts b/packages/runed/src/lib/utilities/index.ts index c5cbd92f..45f5682d 100644 --- a/packages/runed/src/lib/utilities/index.ts +++ b/packages/runed/src/lib/utilities/index.ts @@ -23,3 +23,4 @@ export * from "./FiniteStateMachine/index.js"; export * from "./PersistedState/index.js"; export * from "./useGeolocation/index.js"; export * from "./Context/index.js"; +export * from "./IsInViewport/index.js"; diff --git a/sites/docs/src/content/utilities/is-in-viewport.md b/sites/docs/src/content/utilities/is-in-viewport.md new file mode 100644 index 00000000..5c56231f --- /dev/null +++ b/sites/docs/src/content/utilities/is-in-viewport.md @@ -0,0 +1,49 @@ +--- +title: IsInViewport +description: N/A +category: Utilities +--- + + + +`IsInViewport` uses the [`useIntersectionObserver`](/docs/utilities/use-intersection-observer) +utility to track if an element is visible within the current viewport. + +It accepts an element or getter that returns an element and an optional `options` object that aligns +with the [`useIntersectionObserver`](/docs/utilities/use-intersection-observer) utility options. + +## Demo + + + +## Usage + +```svelte + + +

Target node

+ +

Target node in viewport: {inViewport.current}

+``` + +## Type Definition + +```ts +import { type UseIntersectionObserverOptions } from "runed"; +export type IsInViewportOptions = UseIntersectionObserverOptions; + +export declare class IsInViewport { + constructor(node: MaybeGetter, options?: IsInViewportOptions); + get current(): boolean; +} +``` + + +
diff --git a/sites/docs/src/lib/components/demos/is-in-viewport.svelte b/sites/docs/src/lib/components/demos/is-in-viewport.svelte new file mode 100644 index 00000000..aac4d9e8 --- /dev/null +++ b/sites/docs/src/lib/components/demos/is-in-viewport.svelte @@ -0,0 +1,26 @@ + + + +

Target node

+

Scroll down to observe the behavior

+
+ +
+

+ Target node is + {inViewport.current ? " in " : " out of "} + + viewport +

+
From e32b09518298ee39c0318c7d55b7f88b07ff4784 Mon Sep 17 00:00:00 2001 From: Hunter Johnston Date: Tue, 17 Dec 2024 21:15:30 -0500 Subject: [PATCH 2/2] formatting --- packages/runed/src/lib/utilities/IsInViewport/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runed/src/lib/utilities/IsInViewport/index.ts b/packages/runed/src/lib/utilities/IsInViewport/index.ts index 03d0db36..359c2ec4 100644 --- a/packages/runed/src/lib/utilities/IsInViewport/index.ts +++ b/packages/runed/src/lib/utilities/IsInViewport/index.ts @@ -1 +1 @@ -export * from "./IsInViewport.svelte.js"; \ No newline at end of file +export * from "./IsInViewport.svelte.js";