diff --git a/.prettierignore b/.prettierignore
index f41062c8..4a43abf9 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -19,3 +19,4 @@ yarn.lock
CHANGELOG.md
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
+sites/docs/src/routes/api/search.json/**/*
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9aa73b68..ea92e65c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -133,8 +133,8 @@ importers:
sites/docs:
devDependencies:
'@svecodocs/kit':
- specifier: ^0.0.4
- version: 0.0.4(@sveltejs/kit@2.9.0(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.3.1)(vite@5.4.11(@types/node@20.17.9)(lightningcss@1.28.2)(terser@5.36.0)))(svelte@5.3.1)(vite@5.4.11(@types/node@20.17.9)(lightningcss@1.28.2)(terser@5.36.0)))(bits-ui@1.0.0-next.65(svelte@5.3.1))(svelte@5.3.1)(tailwindcss@4.0.0-beta.4)(vite@5.4.11(@types/node@20.17.9)(lightningcss@1.28.2)(terser@5.36.0))
+ specifier: ^0.0.5
+ version: 0.0.5(@sveltejs/kit@2.9.0(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.3.1)(vite@5.4.11(@types/node@20.17.9)(lightningcss@1.28.2)(terser@5.36.0)))(svelte@5.3.1)(vite@5.4.11(@types/node@20.17.9)(lightningcss@1.28.2)(terser@5.36.0)))(bits-ui@1.0.0-next.65(svelte@5.3.1))(svelte@5.3.1)(tailwindcss@4.0.0-beta.4)(vite@5.4.11(@types/node@20.17.9)(lightningcss@1.28.2)(terser@5.36.0))
'@sveltejs/adapter-cloudflare':
specifier: ^4.8.0
version: 4.8.0(@sveltejs/kit@2.9.0(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.3.1)(vite@5.4.11(@types/node@20.17.9)(lightningcss@1.28.2)(terser@5.36.0)))(svelte@5.3.1)(vite@5.4.11(@types/node@20.17.9)(lightningcss@1.28.2)(terser@5.36.0)))(wrangler@3.91.0(@cloudflare/workers-types@4.20241127.0))
@@ -1084,8 +1084,8 @@ packages:
'@sinclair/typebox@0.27.8':
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
- '@svecodocs/kit@0.0.4':
- resolution: {integrity: sha512-thJKW8XcWFngXdhIEJ3LgTbj2aTv6pIAraTj7CZzh0YGRQ28DKns9yWJ0jTgsCove1xLrEhTlqgfL7HlQOA4fg==}
+ '@svecodocs/kit@0.0.5':
+ resolution: {integrity: sha512-DrwREJDQBDZTm6UOHg27CqpObFMUBcgYH9P94PSVsdJtaYOAv5GF33cjxwxvwNgczKgcpeZs4xWu3/HdFI5JKA==}
peerDependencies:
'@sveltejs/kit': ^2.0.0
bits-ui: 1.0.0-next.65
@@ -4468,7 +4468,7 @@ snapshots:
'@sinclair/typebox@0.27.8': {}
- '@svecodocs/kit@0.0.4(@sveltejs/kit@2.9.0(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.3.1)(vite@5.4.11(@types/node@20.17.9)(lightningcss@1.28.2)(terser@5.36.0)))(svelte@5.3.1)(vite@5.4.11(@types/node@20.17.9)(lightningcss@1.28.2)(terser@5.36.0)))(bits-ui@1.0.0-next.65(svelte@5.3.1))(svelte@5.3.1)(tailwindcss@4.0.0-beta.4)(vite@5.4.11(@types/node@20.17.9)(lightningcss@1.28.2)(terser@5.36.0))':
+ '@svecodocs/kit@0.0.5(@sveltejs/kit@2.9.0(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.3.1)(vite@5.4.11(@types/node@20.17.9)(lightningcss@1.28.2)(terser@5.36.0)))(svelte@5.3.1)(vite@5.4.11(@types/node@20.17.9)(lightningcss@1.28.2)(terser@5.36.0)))(bits-ui@1.0.0-next.65(svelte@5.3.1))(svelte@5.3.1)(tailwindcss@4.0.0-beta.4)(vite@5.4.11(@types/node@20.17.9)(lightningcss@1.28.2)(terser@5.36.0))':
dependencies:
'@sveltejs/kit': 2.9.0(@sveltejs/vite-plugin-svelte@4.0.2(svelte@5.3.1)(vite@5.4.11(@types/node@20.17.9)(lightningcss@1.28.2)(terser@5.36.0)))(svelte@5.3.1)(vite@5.4.11(@types/node@20.17.9)(lightningcss@1.28.2)(terser@5.36.0))
bits-ui: 1.0.0-next.65(svelte@5.3.1)
diff --git a/scripts/add-utility.mjs b/scripts/add-utility.mjs
index fba5fbe4..0f32157c 100644
--- a/scripts/add-utility.mjs
+++ b/scripts/add-utility.mjs
@@ -7,7 +7,7 @@ function toKebabCase(str) {
}
const utilsDir = "./packages/runed/src/lib/utilities";
-const contentDir = "./sites/docs/content/utilities";
+const contentDir = "./sites/docs/src/content/utilities";
const demosDir = "./sites/docs/src/lib/components/demos";
const utilName = readlineSync.question("What is the name of the utility? ");
@@ -50,8 +50,8 @@ fs.writeFileSync(
demoFile,
`
diff --git a/sites/docs/package.json b/sites/docs/package.json
index 7731d1ab..9678555b 100644
--- a/sites/docs/package.json
+++ b/sites/docs/package.json
@@ -29,7 +29,7 @@
"url": "https://github.com/svecosystem/runed.git"
},
"devDependencies": {
- "@svecodocs/kit": "^0.0.4",
+ "@svecodocs/kit": "^0.0.5",
"@sveltejs/adapter-cloudflare": "^4.8.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0",
diff --git a/sites/docs/src/routes/api/search.json/search.json b/sites/docs/src/routes/api/search.json/search.json
index d0040406..cd4a2968 100644
--- a/sites/docs/src/routes/api/search.json/search.json
+++ b/sites/docs/src/routes/api/search.json/search.json
@@ -1,146 +1 @@
-[
- {
- "title": "Getting Started",
- "href": "/docs/getting-started",
- "description": "Learn how to install and use Runed in your projects.",
- "content": "Installation Install Runed using your favorite package manager: npm install runed Usage Import one of the utilities you need to either a .svelte or .svelte.js|ts file and start using it: import { activeElement } from \"runed\"; let inputElement = $state(); {#if activeElement.current === inputElement} The input element is active! {/if} or import { activeElement } from \"runed\"; function logActiveElement() { $effect(() => { console.log(\"Active element is \", activeElement.current); }); } logActiveElement(); `"
- },
- {
- "title": "Introduction",
- "href": "/docs/index",
- "description": "Runes are magic, but what good is magic if you don't have a wand?",
- "content": "Runed is a collection of utilities for Svelte 5 that make composing powerful applications and libraries a breeze, leveraging the power of $2. Why Runed? Svelte 5 Runes unlock immense power by providing a set of primitives that allow us to build impressive applications and libraries with ease. However, building complex applications often requires more than just the primitives provided by Svelte Runes. Runed takes those primitives to the next level by providing: Powerful Utilities**: A set of carefully crafted utility functions and classes that simplify common tasks and reduce boilerplate. Collective Efforts**: We often find ourselves writing the same utility functions over and over again. Runed aims to provide a single source of truth for these utilities, allowing the community to contribute, test, and benefit from them. Consistency**: A consistent set of APIs and behaviors across all utilities, so you can focus on building your projects instead of constantly learning new APIs. Reactivity First**: Powered by Svelte 5's new reactivity system, Runed utilities are designed to handle reactive state and side effects with ease. Type Safety**: Full TypeScript support to catch errors early and provide a better developer experience. Ideas and Principles Embrace the Magic of Runes Svelte Runes are a powerful new paradigm. Runed fully embraces this concept and explores its potential. Our goal is to make working with Runes feel as natural and intuitive as possible. Enhance, Don't Replace Runed is not here to replace Svelte's core functionality, but to enhance and extend it. Our utilities should feel like a natural extension of Svelte, not a separate framework. Progressive Complexity Simple things should be simple, complex things should be possible. Runed provides easy-to-use defaults while allowing for advanced customization when needed. Open Source and Community Collaboration Runed is an open-source, MIT licensed project that welcomes all forms of contributions from the community. Whether it's bug reports, feature requests, or code contributions, your input will help make Runed the best it can be."
- },
- {
- "title": "activeElement",
- "href": "/docs/utilities/active-element",
- "description": "An object holding the currently active element.",
- "content": " import Demo from '$lib/components/demos/active-element.svelte'; Demo Usage import { activeElement } from \"runed\"; Currently active element: {activeElement.current?.localName ?? \"No active element found\"} `"
- },
- {
- "title": "AnimationFrames",
- "href": "/docs/utilities/animation-frames",
- "description": "A wrapper over `requestAnimationFrame`, with controls for limiting FPS, and information about the current frame.",
- "content": " import Demo from '$lib/components/demos/animation-frames.svelte'; Demo Description AnimationFrames wraps over $2. While it is not necessary to use it to use requestAnimationFrame, it removes some of the boilerplate, and adds common utilities for it. Automatically interrupts the requestAnimationFrame loop once the component is unmounted Lets you set an FPS limit Lets you get information about the current frame, such as its current timestamp, and the difference in ms between the last frame and the current one Returns information about current FPS Usage import { AnimationFrames } from \"runed\"; import { Slider } from \"../ui/slider\"; // Check out shadcn-svelte! let frames = $state(0); let fpsLimit = $state(10); let delta = $state(0); const animation = new AnimationFrames( (args) => { frames++; delta = args.delta; }, { fpsLimit: () => fpsLimit } ); const stats = $derived( Frames: ${frames}\\nFPS: ${animation.fps.toFixed(0)}\\nDelta: ${delta.toFixed(0)}ms ); {stats} {animation.running ? \"Stop\" : \"Start\"} FPS limit: {fpsLimit}{fpsLimit === 0 ? \" (not limited)\" : \"\"} (fpsLimit = value[0] ?? 0)} min={0} max={144} /> `"
- },
- {
- "title": "Debounced",
- "href": "/docs/utilities/debounced",
- "description": "A wrapper over `useDebounce` that returns a debounced state.",
- "content": " import Demo from '$lib/components/demos/debounced.svelte'; Demo Usage This is a simple wrapper over $2 that returns a debounced state. import { Debounced } from \"runed\"; let search = $state(\"\"); const debounced = new Debounced(() => search, 500); You searched for: {debounced.current} You may cancel the pending update, or set a new value immediately. Setting immediately also cancels any pending updates. let count = $state(0); const debounced = new Debounced(() => count, 500); count = 1; debounced.cancel(); // after a while... console.log(debounced.current); // Still 0! count = 2; console.log(debounced.current); // Still 0! debounced.setImmediately(count); console.log(debounced.current); // 2 `"
- },
- {
- "title": "ElementRect",
- "href": "/docs/utilities/element-rect",
- "description": "A function that returns the size of an element.",
- "content": " import Demo from '$lib/components/demos/element-rect.svelte'; Demo Usage With a reference to an element, you can use the ElementRect utility to get the bounding rectangle of the element. import { ElementRect } from \"runed\"; let el = $state(); const rect = new ElementRect(() => el); Width: {rect.width} Height: {rect.height} {JSON.stringify(rect.current, null, 2)} `"
- },
- {
- "title": "ElementSize",
- "href": "/docs/utilities/element-size",
- "description": "A function that returns the size of an element.",
- "content": " import Demo from '$lib/components/demos/element-size.svelte'; Demo Usage import { ElementSize } from \"runed\"; let el = $state() as HTMLElement; const size = new ElementSize(() => el); Width: {size.width} Height: {size.height} `"
- },
- {
- "title": "FiniteStateMachine",
- "href": "/docs/utilities/finite-state-machine",
- "description": "Defines a strongly-typed finite state machine.",
- "content": " import Demo from '$lib/components/demos/finite-state-machine.svelte'; Demo type MyStates = \"disabled\" | \"idle\" | \"running\"; type MyEvents = \"toggleEnabled\" | \"start\" | \"stop\"; const f = new FiniteStateMachine(\"disabled\", { disabled: { toggleEnabled: \"idle\" }, idle: { toggleEnabled: \"disabled\", start: \"running\" }, running: { _enter: () => { f.debounce(2000, \"stop\"); }, stop: \"idle\", toggleEnabled: \"disabled\" } }); Usage Finite state machines (often abbreviated as \"FSMs\") are useful for tracking and manipulating something that could be in one of many different states. It centralizes the definition of every possible state and the events that might trigger a transition from one state to another. Here is a state machine describing a simple toggle switch: import { FiniteStateMachine } from \"runed\"; type MyStates = \"on\" | \"off\"; type MyEvents = \"toggle\"; const f = new FiniteStateMachine(\"off\", { off: { toggle: \"on\" }, on: { toggle: \"off\" } }); The first argument to the FiniteStateMachine constructor is the initial state. The second argument is an object with one key for each state. Each state then describes which events are valid for that state, and which state that event should lead to. In the above example of a simple switch, there are two states (on and off). The toggle event in either state leads to the other state. You send events to the FSM using f.send. To send the toggle event, invoke f.send('toggle'). Actions Maybe you want fancier logic for an event handler, or you want to conditionally transition into another state. Instead of strings, you can use actions. An action is a function that returns a state. An action can receive parameters, and it can use those parameters to dynamically choose which state should come next. It can also prevent a state transition by returning nothing. type MyStates = \"on\" | \"off\" | \"cooldown\"; const f = new FiniteStateMachine(\"off\", { off: { toggle: () => { if (isTuesday) { // Switch can only turn on during Tuesdays return \"on\"; } // All other days, nothing is returned and state is unchanged. } }, on: { toggle: (heldMillis: number) => { // You can also dynamically return the next state! // Only turn off if switch is depressed for 3 seconds if (heldMillis > 3000) { return \"off\"; } } } }); Lifecycle methods You can define special handlers that are invoked whenever a state is entered or exited: const f = new FiniteStateMachine('off', { off: { toggle: 'on' _enter: (meta) => { console.log('switch is off') } _exit: (meta) => { console.log('switch is no longer off') } }, on: { toggle: 'off' _enter: (meta) => { console.log('switch is on') } _exit: (meta) => { console.log('switch is no longer on') } } }); The lifecycle methods are invoked with a metadata object containing some useful information: from: the name of the event that is being exited to: the name of the event that is being entered event: the name of the event which has triggered the transition args: (optional) you may pass additional metadata when invoking an action with f.send('theAction', additional, params, as, args) The _enter handler for the initial state is called upon creation of the FSM. It is invoked with both the from and event fields set to null. Wildcard handlers There is one special state used as a fallback: *. If you have the fallback state, and you attempt to send() an event that is not handled by the current state, then it will try to find a handler for that event on the * state before discarding the event: const f = new FiniteStateMachine('off', { off: { toggle: 'on' }, on: { toggle: 'off' } '*': { emergency: 'off' } }); // will always result in the switch turning off. f.send('emergency'); Debouncing Frequently, you want to transition to another state after some time has elapsed. To do this, use the debounce method: f.send(\"toggle\"); // turn on immediately f.debounce(5000, \"toggle\"); // turn off in 5000 milliseconds If you re-invoke debounce with the same event, it will cancel the existing timer and start the countdown over: // schedule a toggle in five seconds f.debounce(5000, \"toggle\"); // ... less than 5000ms elapses ... f.debounce(5000, \"toggle\"); // The second call cancels the original timer, and starts a new one You can also use debounce in both actions and lifecycle methods. In both of the following examples, the lightswitch will turn itself off five seconds after it was turned on: const f = new FiniteStateMachine(\"off\", { off: { toggle: () => { f.debounce(5000, \"toggle\"); return \"on\"; } }, on: { toggle: \"off\" } }); const f = new FiniteStateMachine(\"off\", { off: { toggle: \"on\" }, on: { toggle: \"off\", _enter: () => { f.debounce(5000, \"toggle\"); } } }); Notes FiniteStateMachine is a loving rewrite of $2. FSMs are ideal for representing many different kinds of systems and interaction patterns. FiniteStateMachine is an intentionally minimalistic implementation. If you're looking for a more powerful FSM library, $2 is an excellent library with more features — and a steeper learning curve."
- },
- {
- "title": "IsFocusWithin",
- "href": "/docs/utilities/is-focus-within",
- "description": "Determine if focus is within a specific element.",
- "content": " import Demo from '$lib/components/demos/is-focus-within.svelte'; Demo Usage import { IsFocusWithin } from \"runed\"; let formElement = $state(); const focusWithinForm = new IsFocusWithin(() => formElement); Focus within form: {focusWithinForm.current} Submit `"
- },
- {
- "title": "IsIdle",
- "href": "/docs/utilities/is-idle",
- "description": "Determine if a user is idle, and when was the last time they were active.",
- "content": " import Demo from '$lib/components/demos/is-idle.svelte'; Demo Usage import { AnimationFrames, IsIdle } from \"runed\"; const idle = new IsIdle({ timeout: 1000 }); Idle: {idle.current} Last active: {new Date(idle.lastActive).toLocaleTimeString()} `"
- },
- {
- "title": "IsMounted",
- "href": "/docs/utilities/is-mounted",
- "description": "A class that returns the mounted state of the component it's called in.",
- "content": " import Demo from '$lib/components/demos/is-mounted.svelte'; Demo Usage import { IsMounted } from \"runed\"; const isMounted = new IsMounted(); Which is a shorthand for one of the following: import { onMount } from \"svelte\"; const isMounted = $state({ current: false }); onMount(() => { isMounted.current = true; }); or import { untrack } from \"svelte\"; const isMounted = $state({ current: false }); $effect(() => { untrack(() => (isMounted.current = true)); }); `"
- },
- {
- "title": "IsSupported",
- "href": "/docs/utilities/is-supported",
- "description": "Determine if a feature is supported by the environment before using it.",
- "content": "Usage import { IsSupported } from \"runed\"; const isSupported = new IsSupported(() => navigator && \"geolocation\" in navigator); if (isSupported.current) { // Do something with the geolocation API } `"
- },
- {
- "title": "MediaQuery",
- "href": "/docs/utilities/media-query",
- "description": "Take a media query (or a function that returns one if you want reactivity) as input and you can check if it currently matches doing `instance.match`",
- "content": " import Demo from '$lib/components/demos/media-query.svelte'; Demo Usage The simplest way of using this utility is just by passing a string with a valid media query. import { MediaQuery } from \"runed\"; const screen = new MediaQuery(\"(min-width: 640px)\"); {#if screen.matches} Your screen is less than 640px {:else} Your screen is more than 640px {/if} You can also pass a getter that returns a string. import { MediaQuery } from \"runed\"; let media = $state(\"(min-width: 640px)\"); const query = new MediaQuery(() => media); Media query {media} is currently {screen.matches} 640px 320px `"
- },
- {
- "title": "PersistedState",
- "href": "/docs/utilities/persisted-state",
- "description": "Create reactive state that is persisted and synchronized across browser sessions and tabs using Web Storage.",
- "content": " import Demo from '$lib/components/demos/persisted-state.svelte'; import { Callout } from '@svecodocs/kit' Demo You can refresh this page and/or open it in another tab to see the count state being persisted and synchronized across sessions and tabs. Usage PersistedState allows for syncing and persisting state across browser sessions using localStorage or sessionStorage. Initialize PersistedState by providing a unique key and an initial value for the state. import { PersistedState } from \"runed\"; const count = new PersistedState(\"count\", 0); count.current++}>Increment count.current--}>Decrement (count.current = 0)}>Reset Count: {count.current} PersistedState also includes an options object. { storage: 'session', // Specifies whether to use local or session storage. Default is 'local'. syncTabs: false, // Indicates if changes should sync across tabs. Default is true. serializer: { serialize: superjson.stringify, // Custom serialization function. Default is JSON.stringify. deserialize: superjson.parse // Custom deserialization function. Default is JSON.parse. } } `"
- },
- {
- "title": "PressedKeys",
- "href": "/docs/utilities/pressed-keys",
- "description": "Tracks which keys are currently pressed",
- "content": " import Demo from '$lib/components/demos/pressed-keys.svelte'; Demo Usage With an instance of PressedKeys, you can use the has method. const keys = new PressedKeys(); const isArrowDownPressed = $derived(keys.has(\"ArrowDown\")); const isCtrlAPressed = $derived(keys.has(\"Control\", \"a\")); Or get all of the currently pressed keys: const keys = new PressedKeys(); console.log(keys.all()); `"
- },
- {
- "title": "Previous",
- "href": "/docs/utilities/previous",
- "description": "Holds the previous value of a getter.",
- "content": " import Demo from '$lib/components/demos/previous.svelte'; Demo Usage import { Previous } from \"runed\"; let count = $state(0); const previous = new Previous(() => count); count++}>Count: {count} Previous: {${previous.current}} `"
- },
- {
- "title": "StateHistory",
- "href": "/docs/utilities/state-history",
- "description": "Track the change history of a value, providing undo and redo functionality",
- "content": " import Demo from '$lib/components/demos/state-history.svelte'; Demo Usage StateHistory tracks a getter's return value, logging each change into an array. A setter is also required to use the undo and redo functions. import { StateHistory } from \"runed\"; let count = $state(0); const history = new StateHistory(() => count, (c) => (count = c)); history.log[0]; // { snapshot: 0, timestamp: ... } Besides log, the returned object contains undo and redo functionality. import { useStateHistory } from \"runed\"; let count = $state(0); const history = new StateHistory(() => count, (c) => (count = c)); function format(ts: number) { return new Date(ts).toLocaleString(); } {count} count++}>Increment count--}>Decrement Undo Redo `"
- },
- {
- "title": "Store",
- "href": "/docs/utilities/store",
- "description": "Convert a legacy svelte store into rune-powered runeStore state.",
- "content": " import Demo from '$lib/components/demos/store.svelte'; Demo Usage import { Store } from \"runed\"; import { writable } from \"svelte/store\"; const store = writable(0); const runeStore = new Store(store); Rune: {runeStore.current} runeStore.current++}>Increment via runeStore Store: {$store} ($store = $store + 1)}>Increment via store `"
- },
- {
- "title": "useDebounce",
- "href": "/docs/utilities/use-debounce",
- "description": "A higher-order function that debounces the execution of a function.",
- "content": " import Demo from '$lib/components/demos/use-debounce.svelte'; Demo Usage import { useDebounce } from \"runed\"; let count = $state(0); let logged = $state(\"\"); let isFirstTime = $state(true); let debounceDuration = $state(1000); const logCount = useDebounce( () => { if (isFirstTime) { isFirstTime = false; logged = You pressed the button ${count} times!; } else { logged = You pressed the button ${count} times since last time!; } count = 0; }, () => debounceDuration ); function ding() { count++; logCount(); } DING DING DING Cancel message {logged || \"Press the button!\"} `"
- },
- {
- "title": "useEventListener",
- "href": "/docs/utilities/use-event-listener",
- "description": "A function that attaches an automatically disposed event listener.",
- "content": " import Demo from '$lib/components/demos/use-event-listener.svelte'; Demo Usage The useEventListener function is particularly useful for attaching event listeners to elements you don't directly control. For instance, if you need to listen for events on the document body or window and can't use ``, or if you receive an element reference from a parent component. Example: Tracking Clicks on the Document // ClickLogger.ts import { useEventListener } from \"runed\"; export class ClickLogger { #clicks = $state(0); constructor() { useEventListener( () => document.body, \"click\", () => this.#clicks++ ); } get clicks() { return this.#clicks; } } This ClickLogger class tracks the number of clicks on the document body using the useEventListener function. Each time a click occurs, the internal counter increments. Svelte Component Usage import { ClickLogger } from \"./ClickLogger.ts\"; const logger = new ClickLogger(); You've clicked the document {logger.clicks} {logger.clicks === 1 ? \"time\" : \"times\"} In the component above, we create an instance of the ClickLogger class to monitor clicks on the document. The displayed text updates dynamically based on the recorded click count. Key Points Automatic Cleanup:** The event listener is removed automatically when the component is destroyed or when the element reference changes. Lazy Initialization:** The target element can be defined using a function, enabling flexible and dynamic behavior. Convenient for Global Listeners:** Ideal for scenarios where attaching event listeners directly to the DOM elements is cumbersome or impractical."
- },
- {
- "title": "useIntersectionObserver",
- "href": "/docs/utilities/use-intersection-observer",
- "description": "Watch for intersection changes of a target element.",
- "content": " import Demo from '$lib/components/demos/use-intersection-observer.svelte'; import { Callout } from '@svecodocs/kit' Demo Usage With a reference to an element, you can use the useIntersectionObserver utility to watch for intersection changes of the target element. import { useIntersectionObserver } from \"runed\"; let target = $state(null); let root = $state(null); let isIntersecting = $state(false); useIntersectionObserver( () => target, (entries) => { const entry = entries[0]; if (!entry) return; isIntersecting = entry.isIntersecting; }, { root: () => root } ); {#if isIntersecting} Target is intersecting {:else} Target is not intersecting {/if} Pause You can pause the intersection observer at any point by calling the pause method. const observer = useIntersectionObserver(/* ... */); observer.pause(); Resume You can resume the intersection observer at any point by calling the resume method. const observer = useIntersectionObserver(/* ... */); observer.resume(); Stop You can stop the intersection observer at any point by calling the stop method. const observer = useIntersectionObserver(/* ... */); observer.stop(); isActive You can check if the intersection observer is active by checking the isActive property. This property cannot be destructured as it is a getter. You must access it directly from the observer. const observer = useIntersectionObserver(/* ... */); if (observer.isActive) { // do something } `"
- },
- {
- "title": "useMutationObserver",
- "href": "/docs/utilities/use-mutation-observer",
- "description": "Observe changes in an element",
- "content": " import Demo from '$lib/components/demos/use-mutation-observer.svelte'; Demo Usage With a reference to an element, you can use the useMutationObserver hook to observe changes in the element. import { useMutationObserver } from \"runed\"; let el = $state(null); const messages = $state([]); let className = $state(\"\"); let style = $state(\"\"); useMutationObserver( () => el, (mutations) => { const mutation = mutations[0]; if (!mutation) return; messages.push(mutation.attributeName!); }, { attributes: true } ); setTimeout(() => { className = \"text-brand\"; }, 1000); setTimeout(() => { style = \"font-style: italic;\"; }, 1500); {#each messages as text} Mutation Attribute: {text} {:else} No mutations yet {/each} You can stop the mutation observer at any point by calling the stop method. const { stop } = useMutationObserver(/* ... */); stop(); `"
- },
- {
- "title": "useResizeObserver",
- "href": "/docs/utilities/use-resize-observer",
- "description": "Detects changes in the size of an element",
- "content": " import Demo from '$lib/components/demos/use-resize-observer.svelte'; Demo Usage With a reference to an element, you can use the useResizeObserver utility to detect changes in the size of an element. import { useResizeObserver } from \"runed\"; let el = $state(null); let text = $state(\"\"); useResizeObserver( () => el, (entries) => { const entry = entries[0]; if (!entry) return; const { width, height } = entry.contentRect; text = width: ${width};\\nheight: ${height};; } ); You can stop the resize observer at any point by calling the stop method. const { stop } = useResizeObserver(/* ... */); stop(); `"
- },
- {
- "title": "watch",
- "href": "/docs/utilities/watch",
- "description": "Watch for changes and run a callback",
- "content": "Runes provide a handy way of running a callback when reactive values change: $2. It automatically detects when inner values change, and re-runs the callback. $effect is great, but sometimes you want to manually specify which values should trigger the callback. Svelte provides an untrack function, allowing you to specify that a dependency shouldn't be tracked, but it doesn't provide a way to say that only certain values should be tracked. watch does exactly that. It accepts a getter function, which returns the dependencies of the effect callback. Usage watch Runs a callback whenever one of the sources change. import { watch } from \"runed\"; let count = $state(0); watch(() => count, () => { console.log(count); } ); The callback receives two arguments: The current value of the sources, and the previous value. let count = $state(0); watch(() => count, (curr, prev) => { console.log(count is ${curr}, was ${prev}); } ); You can also send in an array of sources: let age = $state(20); let name = $state(\"bob\"); watch([() => age, () => name], ([age, name], [prevAge, prevName]) => { // ... } watch also accepts an options object. watch(sources, callback, { // First run will only happen after sources change when set to true. // By default, its false. lazy: true }); watch.pre watch.pre is similar to watch, but it uses $2 under the hood. watchOnce In case you want to run the callback only once, you can use watchOnce and watchOnce.pre. It functions identically to the watch and watch.pre otherwise, but it does not accept any options object."
- }
-]
+[{"title":"Getting Started","href":"/docs/getting-started","description":"Learn how to install and use Runed in your projects.","content":"Installation Install Runed using your favorite package manager: npm install runed Usage Import one of the utilities you need to either a .svelte or .svelte.js|ts file and start using it: import { activeElement } from \"runed\"; let inputElement = $state(); {#if activeElement.current === inputElement} The input element is active! {/if} or import { activeElement } from \"runed\"; function logActiveElement() { $effect(() => { console.log(\"Active element is \", activeElement.current); }); } logActiveElement(); `"},{"title":"Introduction","href":"/docs/index","description":"Runes are magic, but what good is magic if you don't have a wand?","content":"Runed is a collection of utilities for Svelte 5 that make composing powerful applications and libraries a breeze, leveraging the power of $2. Why Runed? Svelte 5 Runes unlock immense power by providing a set of primitives that allow us to build impressive applications and libraries with ease. However, building complex applications often requires more than just the primitives provided by Svelte Runes. Runed takes those primitives to the next level by providing: Powerful Utilities**: A set of carefully crafted utility functions and classes that simplify common tasks and reduce boilerplate. Collective Efforts**: We often find ourselves writing the same utility functions over and over again. Runed aims to provide a single source of truth for these utilities, allowing the community to contribute, test, and benefit from them. Consistency**: A consistent set of APIs and behaviors across all utilities, so you can focus on building your projects instead of constantly learning new APIs. Reactivity First**: Powered by Svelte 5's new reactivity system, Runed utilities are designed to handle reactive state and side effects with ease. Type Safety**: Full TypeScript support to catch errors early and provide a better developer experience. Ideas and Principles Embrace the Magic of Runes Svelte Runes are a powerful new paradigm. Runed fully embraces this concept and explores its potential. Our goal is to make working with Runes feel as natural and intuitive as possible. Enhance, Don't Replace Runed is not here to replace Svelte's core functionality, but to enhance and extend it. Our utilities should feel like a natural extension of Svelte, not a separate framework. Progressive Complexity Simple things should be simple, complex things should be possible. Runed provides easy-to-use defaults while allowing for advanced customization when needed. Open Source and Community Collaboration Runed is an open-source, MIT licensed project that welcomes all forms of contributions from the community. Whether it's bug reports, feature requests, or code contributions, your input will help make Runed the best it can be."},{"title":"activeElement","href":"/docs/utilities/active-element","description":"An object holding the currently active element.","content":" import Demo from '$lib/components/demos/active-element.svelte'; Demo Usage import { activeElement } from \"runed\"; Currently active element: {activeElement.current?.localName ?? \"No active element found\"} `"},{"title":"AnimationFrames","href":"/docs/utilities/animation-frames","description":"A wrapper over `requestAnimationFrame`, with controls for limiting FPS, and information about the current frame.","content":" import Demo from '$lib/components/demos/animation-frames.svelte'; Demo Description AnimationFrames wraps over $2. While it is not necessary to use it to use requestAnimationFrame, it removes some of the boilerplate, and adds common utilities for it. Automatically interrupts the requestAnimationFrame loop once the component is unmounted Lets you set an FPS limit Lets you get information about the current frame, such as its current timestamp, and the difference in ms between the last frame and the current one Returns information about current FPS Usage import { AnimationFrames } from \"runed\"; import { Slider } from \"../ui/slider\"; // Check out shadcn-svelte! let frames = $state(0); let fpsLimit = $state(10); let delta = $state(0); const animation = new AnimationFrames( (args) => { frames++; delta = args.delta; }, { fpsLimit: () => fpsLimit } ); const stats = $derived( Frames: ${frames}\\nFPS: ${animation.fps.toFixed(0)}\\nDelta: ${delta.toFixed(0)}ms ); {stats} {animation.running ? \"Stop\" : \"Start\"} FPS limit: {fpsLimit}{fpsLimit === 0 ? \" (not limited)\" : \"\"} (fpsLimit = value[0] ?? 0)} min={0} max={144} /> `"},{"title":"Debounced","href":"/docs/utilities/debounced","description":"A wrapper over `useDebounce` that returns a debounced state.","content":" import Demo from '$lib/components/demos/debounced.svelte'; Demo Usage This is a simple wrapper over $2 that returns a debounced state. import { Debounced } from \"runed\"; let search = $state(\"\"); const debounced = new Debounced(() => search, 500); You searched for: {debounced.current} You may cancel the pending update, or set a new value immediately. Setting immediately also cancels any pending updates. let count = $state(0); const debounced = new Debounced(() => count, 500); count = 1; debounced.cancel(); // after a while... console.log(debounced.current); // Still 0! count = 2; console.log(debounced.current); // Still 0! debounced.setImmediately(count); console.log(debounced.current); // 2 `"},{"title":"ElementRect","href":"/docs/utilities/element-rect","description":"A function that returns the size of an element.","content":" import Demo from '$lib/components/demos/element-rect.svelte'; Demo Usage With a reference to an element, you can use the ElementRect utility to get the bounding rectangle of the element. import { ElementRect } from \"runed\"; let el = $state(); const rect = new ElementRect(() => el); Width: {rect.width} Height: {rect.height} {JSON.stringify(rect.current, null, 2)} `"},{"title":"ElementSize","href":"/docs/utilities/element-size","description":"A function that returns the size of an element.","content":" import Demo from '$lib/components/demos/element-size.svelte'; Demo Usage import { ElementSize } from \"runed\"; let el = $state() as HTMLElement; const size = new ElementSize(() => el); Width: {size.width} Height: {size.height} `"},{"title":"FiniteStateMachine","href":"/docs/utilities/finite-state-machine","description":"Defines a strongly-typed finite state machine.","content":" import Demo from '$lib/components/demos/finite-state-machine.svelte'; Demo type MyStates = \"disabled\" | \"idle\" | \"running\"; type MyEvents = \"toggleEnabled\" | \"start\" | \"stop\"; const f = new FiniteStateMachine(\"disabled\", { disabled: { toggleEnabled: \"idle\" }, idle: { toggleEnabled: \"disabled\", start: \"running\" }, running: { _enter: () => { f.debounce(2000, \"stop\"); }, stop: \"idle\", toggleEnabled: \"disabled\" } }); Usage Finite state machines (often abbreviated as \"FSMs\") are useful for tracking and manipulating something that could be in one of many different states. It centralizes the definition of every possible state and the events that might trigger a transition from one state to another. Here is a state machine describing a simple toggle switch: import { FiniteStateMachine } from \"runed\"; type MyStates = \"on\" | \"off\"; type MyEvents = \"toggle\"; const f = new FiniteStateMachine(\"off\", { off: { toggle: \"on\" }, on: { toggle: \"off\" } }); The first argument to the FiniteStateMachine constructor is the initial state. The second argument is an object with one key for each state. Each state then describes which events are valid for that state, and which state that event should lead to. In the above example of a simple switch, there are two states (on and off). The toggle event in either state leads to the other state. You send events to the FSM using f.send. To send the toggle event, invoke f.send('toggle'). Actions Maybe you want fancier logic for an event handler, or you want to conditionally transition into another state. Instead of strings, you can use actions. An action is a function that returns a state. An action can receive parameters, and it can use those parameters to dynamically choose which state should come next. It can also prevent a state transition by returning nothing. type MyStates = \"on\" | \"off\" | \"cooldown\"; const f = new FiniteStateMachine(\"off\", { off: { toggle: () => { if (isTuesday) { // Switch can only turn on during Tuesdays return \"on\"; } // All other days, nothing is returned and state is unchanged. } }, on: { toggle: (heldMillis: number) => { // You can also dynamically return the next state! // Only turn off if switch is depressed for 3 seconds if (heldMillis > 3000) { return \"off\"; } } } }); Lifecycle methods You can define special handlers that are invoked whenever a state is entered or exited: const f = new FiniteStateMachine('off', { off: { toggle: 'on' _enter: (meta) => { console.log('switch is off') } _exit: (meta) => { console.log('switch is no longer off') } }, on: { toggle: 'off' _enter: (meta) => { console.log('switch is on') } _exit: (meta) => { console.log('switch is no longer on') } } }); The lifecycle methods are invoked with a metadata object containing some useful information: from: the name of the event that is being exited to: the name of the event that is being entered event: the name of the event which has triggered the transition args: (optional) you may pass additional metadata when invoking an action with f.send('theAction', additional, params, as, args) The _enter handler for the initial state is called upon creation of the FSM. It is invoked with both the from and event fields set to null. Wildcard handlers There is one special state used as a fallback: *. If you have the fallback state, and you attempt to send() an event that is not handled by the current state, then it will try to find a handler for that event on the * state before discarding the event: const f = new FiniteStateMachine('off', { off: { toggle: 'on' }, on: { toggle: 'off' } '*': { emergency: 'off' } }); // will always result in the switch turning off. f.send('emergency'); Debouncing Frequently, you want to transition to another state after some time has elapsed. To do this, use the debounce method: f.send(\"toggle\"); // turn on immediately f.debounce(5000, \"toggle\"); // turn off in 5000 milliseconds If you re-invoke debounce with the same event, it will cancel the existing timer and start the countdown over: // schedule a toggle in five seconds f.debounce(5000, \"toggle\"); // ... less than 5000ms elapses ... f.debounce(5000, \"toggle\"); // The second call cancels the original timer, and starts a new one You can also use debounce in both actions and lifecycle methods. In both of the following examples, the lightswitch will turn itself off five seconds after it was turned on: const f = new FiniteStateMachine(\"off\", { off: { toggle: () => { f.debounce(5000, \"toggle\"); return \"on\"; } }, on: { toggle: \"off\" } }); const f = new FiniteStateMachine(\"off\", { off: { toggle: \"on\" }, on: { toggle: \"off\", _enter: () => { f.debounce(5000, \"toggle\"); } } }); Notes FiniteStateMachine is a loving rewrite of $2. FSMs are ideal for representing many different kinds of systems and interaction patterns. FiniteStateMachine is an intentionally minimalistic implementation. If you're looking for a more powerful FSM library, $2 is an excellent library with more features — and a steeper learning curve."},{"title":"IsFocusWithin","href":"/docs/utilities/is-focus-within","description":"Determine if focus is within a specific element.","content":" import Demo from '$lib/components/demos/is-focus-within.svelte'; Demo Usage import { IsFocusWithin } from \"runed\"; let formElement = $state(); const focusWithinForm = new IsFocusWithin(() => formElement); Focus within form: {focusWithinForm.current} Submit `"},{"title":"IsIdle","href":"/docs/utilities/is-idle","description":"Determine if a user is idle, and when was the last time they were active.","content":" import Demo from '$lib/components/demos/is-idle.svelte'; Demo Usage import { AnimationFrames, IsIdle } from \"runed\"; const idle = new IsIdle({ timeout: 1000 }); Idle: {idle.current} Last active: {new Date(idle.lastActive).toLocaleTimeString()} `"},{"title":"IsMounted","href":"/docs/utilities/is-mounted","description":"A class that returns the mounted state of the component it's called in.","content":" import Demo from '$lib/components/demos/is-mounted.svelte'; Demo Usage import { IsMounted } from \"runed\"; const isMounted = new IsMounted(); Which is a shorthand for one of the following: import { onMount } from \"svelte\"; const isMounted = $state({ current: false }); onMount(() => { isMounted.current = true; }); or import { untrack } from \"svelte\"; const isMounted = $state({ current: false }); $effect(() => { untrack(() => (isMounted.current = true)); }); `"},{"title":"IsSupported","href":"/docs/utilities/is-supported","description":"Determine if a feature is supported by the environment before using it.","content":"Usage import { IsSupported } from \"runed\"; const isSupported = new IsSupported(() => navigator && \"geolocation\" in navigator); if (isSupported.current) { // Do something with the geolocation API } `"},{"title":"MediaQuery","href":"/docs/utilities/media-query","description":"Take a media query (or a function that returns one if you want reactivity) as input and you can check if it currently matches doing `instance.match`","content":" import Demo from '$lib/components/demos/media-query.svelte'; Demo Usage The simplest way of using this utility is just by passing a string with a valid media query. import { MediaQuery } from \"runed\"; const screen = new MediaQuery(\"(min-width: 640px)\"); {#if screen.matches} Your screen is less than 640px {:else} Your screen is more than 640px {/if} You can also pass a getter that returns a string. import { MediaQuery } from \"runed\"; let media = $state(\"(min-width: 640px)\"); const query = new MediaQuery(() => media); Media query {media} is currently {screen.matches} 640px 320px `"},{"title":"PersistedState","href":"/docs/utilities/persisted-state","description":"Create reactive state that is persisted and synchronized across browser sessions and tabs using Web Storage.","content":" import Demo from '$lib/components/demos/persisted-state.svelte'; import { Callout } from '@svecodocs/kit' Demo You can refresh this page and/or open it in another tab to see the count state being persisted and synchronized across sessions and tabs. Usage PersistedState allows for syncing and persisting state across browser sessions using localStorage or sessionStorage. Initialize PersistedState by providing a unique key and an initial value for the state. import { PersistedState } from \"runed\"; const count = new PersistedState(\"count\", 0); count.current++}>Increment count.current--}>Decrement (count.current = 0)}>Reset Count: {count.current} PersistedState also includes an options object. { storage: 'session', // Specifies whether to use local or session storage. Default is 'local'. syncTabs: false, // Indicates if changes should sync across tabs. Default is true. serializer: { serialize: superjson.stringify, // Custom serialization function. Default is JSON.stringify. deserialize: superjson.parse // Custom deserialization function. Default is JSON.parse. } } `"},{"title":"PressedKeys","href":"/docs/utilities/pressed-keys","description":"Tracks which keys are currently pressed","content":" import Demo from '$lib/components/demos/pressed-keys.svelte'; Demo Usage With an instance of PressedKeys, you can use the has method. const keys = new PressedKeys(); const isArrowDownPressed = $derived(keys.has(\"ArrowDown\")); const isCtrlAPressed = $derived(keys.has(\"Control\", \"a\")); Or get all of the currently pressed keys: const keys = new PressedKeys(); console.log(keys.all()); `"},{"title":"Previous","href":"/docs/utilities/previous","description":"Holds the previous value of a getter.","content":" import Demo from '$lib/components/demos/previous.svelte'; Demo Usage import { Previous } from \"runed\"; let count = $state(0); const previous = new Previous(() => count); count++}>Count: {count} Previous: {${previous.current}} `"},{"title":"StateHistory","href":"/docs/utilities/state-history","description":"Track the change history of a value, providing undo and redo functionality","content":" import Demo from '$lib/components/demos/state-history.svelte'; Demo Usage StateHistory tracks a getter's return value, logging each change into an array. A setter is also required to use the undo and redo functions. import { StateHistory } from \"runed\"; let count = $state(0); const history = new StateHistory(() => count, (c) => (count = c)); history.log[0]; // { snapshot: 0, timestamp: ... } Besides log, the returned object contains undo and redo functionality. import { useStateHistory } from \"runed\"; let count = $state(0); const history = new StateHistory(() => count, (c) => (count = c)); function format(ts: number) { return new Date(ts).toLocaleString(); } {count} count++}>Increment count--}>Decrement Undo Redo `"},{"title":"Store","href":"/docs/utilities/store","description":"Convert a legacy svelte store into rune-powered runeStore state.","content":" import Demo from '$lib/components/demos/store.svelte'; Demo Usage import { Store } from \"runed\"; import { writable } from \"svelte/store\"; const store = writable(0); const runeStore = new Store(store); Rune: {runeStore.current} runeStore.current++}>Increment via runeStore Store: {$store} ($store = $store + 1)}>Increment via store `"},{"title":"useDebounce","href":"/docs/utilities/use-debounce","description":"A higher-order function that debounces the execution of a function.","content":" import Demo from '$lib/components/demos/use-debounce.svelte'; Demo Usage import { useDebounce } from \"runed\"; let count = $state(0); let logged = $state(\"\"); let isFirstTime = $state(true); let debounceDuration = $state(1000); const logCount = useDebounce( () => { if (isFirstTime) { isFirstTime = false; logged = You pressed the button ${count} times!; } else { logged = You pressed the button ${count} times since last time!; } count = 0; }, () => debounceDuration ); function ding() { count++; logCount(); } DING DING DING Cancel message {logged || \"Press the button!\"} `"},{"title":"useEventListener","href":"/docs/utilities/use-event-listener","description":"A function that attaches an automatically disposed event listener.","content":" import Demo from '$lib/components/demos/use-event-listener.svelte'; Demo Usage The useEventListener function is particularly useful for attaching event listeners to elements you don't directly control. For instance, if you need to listen for events on the document body or window and can't use ``, or if you receive an element reference from a parent component. Example: Tracking Clicks on the Document // ClickLogger.ts import { useEventListener } from \"runed\"; export class ClickLogger { #clicks = $state(0); constructor() { useEventListener( () => document.body, \"click\", () => this.#clicks++ ); } get clicks() { return this.#clicks; } } This ClickLogger class tracks the number of clicks on the document body using the useEventListener function. Each time a click occurs, the internal counter increments. Svelte Component Usage import { ClickLogger } from \"./ClickLogger.ts\"; const logger = new ClickLogger(); You've clicked the document {logger.clicks} {logger.clicks === 1 ? \"time\" : \"times\"} In the component above, we create an instance of the ClickLogger class to monitor clicks on the document. The displayed text updates dynamically based on the recorded click count. Key Points Automatic Cleanup:** The event listener is removed automatically when the component is destroyed or when the element reference changes. Lazy Initialization:** The target element can be defined using a function, enabling flexible and dynamic behavior. Convenient for Global Listeners:** Ideal for scenarios where attaching event listeners directly to the DOM elements is cumbersome or impractical."},{"title":"useIntersectionObserver","href":"/docs/utilities/use-intersection-observer","description":"Watch for intersection changes of a target element.","content":" import Demo from '$lib/components/demos/use-intersection-observer.svelte'; import { Callout } from '@svecodocs/kit' Demo Usage With a reference to an element, you can use the useIntersectionObserver utility to watch for intersection changes of the target element. import { useIntersectionObserver } from \"runed\"; let target = $state(null); let root = $state(null); let isIntersecting = $state(false); useIntersectionObserver( () => target, (entries) => { const entry = entries[0]; if (!entry) return; isIntersecting = entry.isIntersecting; }, { root: () => root } ); {#if isIntersecting} Target is intersecting {:else} Target is not intersecting {/if} Pause You can pause the intersection observer at any point by calling the pause method. const observer = useIntersectionObserver(/* ... */); observer.pause(); Resume You can resume the intersection observer at any point by calling the resume method. const observer = useIntersectionObserver(/* ... */); observer.resume(); Stop You can stop the intersection observer at any point by calling the stop method. const observer = useIntersectionObserver(/* ... */); observer.stop(); isActive You can check if the intersection observer is active by checking the isActive property. This property cannot be destructured as it is a getter. You must access it directly from the observer. const observer = useIntersectionObserver(/* ... */); if (observer.isActive) { // do something } `"},{"title":"useMutationObserver","href":"/docs/utilities/use-mutation-observer","description":"Observe changes in an element","content":" import Demo from '$lib/components/demos/use-mutation-observer.svelte'; Demo Usage With a reference to an element, you can use the useMutationObserver hook to observe changes in the element. import { useMutationObserver } from \"runed\"; let el = $state(null); const messages = $state([]); let className = $state(\"\"); let style = $state(\"\"); useMutationObserver( () => el, (mutations) => { const mutation = mutations[0]; if (!mutation) return; messages.push(mutation.attributeName!); }, { attributes: true } ); setTimeout(() => { className = \"text-brand\"; }, 1000); setTimeout(() => { style = \"font-style: italic;\"; }, 1500); {#each messages as text} Mutation Attribute: {text} {:else} No mutations yet {/each} You can stop the mutation observer at any point by calling the stop method. const { stop } = useMutationObserver(/* ... */); stop(); `"},{"title":"useResizeObserver","href":"/docs/utilities/use-resize-observer","description":"Detects changes in the size of an element","content":" import Demo from '$lib/components/demos/use-resize-observer.svelte'; Demo Usage With a reference to an element, you can use the useResizeObserver utility to detect changes in the size of an element. import { useResizeObserver } from \"runed\"; let el = $state(null); let text = $state(\"\"); useResizeObserver( () => el, (entries) => { const entry = entries[0]; if (!entry) return; const { width, height } = entry.contentRect; text = width: ${width};\\nheight: ${height};; } ); You can stop the resize observer at any point by calling the stop method. const { stop } = useResizeObserver(/* ... */); stop(); `"},{"title":"watch","href":"/docs/utilities/watch","description":"Watch for changes and run a callback","content":"Runes provide a handy way of running a callback when reactive values change: $2. It automatically detects when inner values change, and re-runs the callback. $effect is great, but sometimes you want to manually specify which values should trigger the callback. Svelte provides an untrack function, allowing you to specify that a dependency shouldn't be tracked, but it doesn't provide a way to say that only certain values should be tracked. watch does exactly that. It accepts a getter function, which returns the dependencies of the effect callback. Usage watch Runs a callback whenever one of the sources change. import { watch } from \"runed\"; let count = $state(0); watch(() => count, () => { console.log(count); } ); The callback receives two arguments: The current value of the sources, and the previous value. let count = $state(0); watch(() => count, (curr, prev) => { console.log(count is ${curr}, was ${prev}); } ); You can also send in an array of sources: let age = $state(20); let name = $state(\"bob\"); watch([() => age, () => name], ([age, name], [prevAge, prevName]) => { // ... } watch also accepts an options object. watch(sources, callback, { // First run will only happen after sources change when set to true. // By default, its false. lazy: true }); watch.pre watch.pre is similar to watch, but it uses $2 under the hood. watchOnce In case you want to run the callback only once, you can use watchOnce and watchOnce.pre. It functions identically to the watch and watch.pre otherwise, but it does not accept any options object."}]
\ No newline at end of file