# Svelte Toolbelt Utilities for Svelte 5 that I find useful and will use in the various projects I work on. It's maintained by me, for me. For more robust and feature-rich utilities, I recommend checking out/using [runed](https://runed.dev). ## Installation ```bash npm install svelte-toolbelt ``` ## Box ### `box` Initializes a writable boxed state. ```svelte ``` ### `box.with` Creates reactive state using getter and setter functions. If a setter function is provided, the box is writable. If not, the box is readonly. Useful for passing synced reactive values across boundaries. ```svelte ``` ### `box.from` Creates a box from an existing box, a getter function, or a static value. Useful for receiving arguments that may or may not be reactive. ```svelte ``` ### `box.flatten` Transforms any boxes within an object to reactive properties, removing the need to access each property with `.current`. ```ts const count = box(1); const flat = box.flatten({ count, double: box.with(() => count.current * 2), increment() { count.current++; } }); console.log(flat.count); // 1 console.log(flat.double); // 2 flat.increment(); console.log(flat.count); // 2 ``` ### `box.readonly` Creates a readonly box from a writable box that remains in sync with the original box. ```ts const count = box(1); const readonlyCount = box.readonly(count); console.log(readonlyCount.current); // 1 count.current++; console.log(readonlyCount.current); // 2 readonlyCount.current = 3; // Error: Cannot assign to read only property 'value' of object ``` ### `box.isBox` Checks if a value is a `Box`. ```ts const count = box(1); console.log(box.isBox(count)); // true console.log(box.isBox(1)); // false ``` ### `box.isWritableBox` Checks if a value is a `WritableBox`. ```ts const count = box(1); const double = box.with(() => count.current * 2); console.log(box.isWritableBox(count)); // true console.log(box.isWritableBox(double)); // false ``` ### `unbox` Unboxes the value from a box. ```ts const count = box(1); const double = box.with(() => count.current * 2); console.log(unbox(double)); // 2 ``` ## Utils ### `afterSleep` Executes a callback after a specified number of milliseconds. ```ts afterSleep(1000, () => console.log("Hello, world!")); ``` ### `afterTick` Executes a callback after the next tick. ```ts afterTick(() => console.log("Hello, world!")); ``` ### `composeHandlers` Composes event handlers into a single function that can be called with an event. If the previous handler cancels the event using `event.preventDefault()`, the handlers that follow will not be called. ```ts import { composeHandlers } from "svelte-toolbelt"; const handler1 = () => console.log("Handler 1"); const handler2 = () => console.log("Handler 2"); const composedHandler = composeHandlers(handler1, handler2); const event = new MouseEvent("click", { cancelable: true }); console.log(composedHandler(event)); // Handler 1, Handler 2 ``` ### `cssToStyleObj` Converts a CSS string to a style object. ```ts const css = "color: red; font-size: 16px;"; const styleObj = cssToStyleObj(css); console.log(styleObj); // { color: "red", fontSize: "16px" } ``` ### `executeCallbacks` Executes an array of callback functions with the same arguments. ```ts const callback1 = () => console.log("Callback 1"); const callback2 = () => console.log("Callback 2"); console.log(executeCallbacks(callback1, callback2)); // Callback 1, Callback 2 ``` ### `addEventListener` Adds an event listener to the specified target element(s) for the given event(s), and returns a function to remove it. ```ts import { addEventListener } from "svelte-toolbelt"; const target = document.getElementById("my-element"); const event = "click"; const handler = () => console.log("Clicked!"); const removeListener = addEventListener(target, event, handler); // Later, remove the listener removeListener(); ``` ### `mergeProps` Merges props into a single object. ```ts import { mergeProps } from "svelte-toolbelt"; const props1 = { a: 1 }; const props2 = { b: 2 }; const result = mergeProps(props1, props2); console.log(result); // { a: 1, b: 2 } ``` #### Event Handlers Event handlers are chained in the order they're passed. If a handler calls `event.preventDefault()`, subsequent handlers in the chain are not executed. ```ts const props1 = { onclick: (e: MouseEvent) => console.log("First click") }; const props2 = { onclick: (e: MouseEvent) => console.log("Second click") }; const mergedProps = mergeProps(props1, props2); mergedProps.onclick(new MouseEvent("click")); // Logs: "First click" then "Second click" ``` If `preventDefault()` is called: ```ts const props1 = { onclick: (e: MouseEvent) => console.log("First click") }; const props2 = { onclick: (e: MouseEvent) => { console.log("Second click"); e.preventDefault(); } }; const props3 = { onclick: (e: MouseEvent) => console.log("Third click") }; const mergedProps = mergeProps(props1, props2, props3); mergedProps.onclick(new MouseEvent("click")); // Logs: "First click" then "Second click" only ``` Since `props2` called `event.preventDefault()`, `props3`'s `onclick` handler will not be called. #### Non-Event Handler Functions Non-event handler functions are also chained, but without the ability to prevent subsequent functions from executing: ```ts const props1 = { doSomething: () => console.log("Action 1") }; const props2 = { doSomething: () => console.log("Action 2") }; const mergedProps = mergeProps(props1, props2); mergedProps.doSomething(); // Logs: "Action 1" then "Action 2" ``` #### Classes Class names are merged using [`clsx`](https://www.npmjs.com/package/clsx): ```ts const props1 = { class: "text-lg font-bold" }; const props2 = { class: ["bg-blue-500", "hover:bg-blue-600"] }; const mergedProps = mergeProps(props1, props2); console.log(mergedProps.class); // "text-lg font-bold bg-blue-500 hover:bg-blue-600" ``` #### Styles Style objects and strings are merged, with later properties overriding earlier ones: ```ts const props1 = { style: { color: "red", fontSize: "16px" } }; const props2 = { style: "background-color: blue; font-weight: bold;" }; const mergedProps = mergeProps(props1, props2); console.log(mergedProps.style); // "color: red; font-size: 16px; background-color: blue; font-weight: bold;" ``` ```ts import { mergeProps } from "bits-ui"; const props1 = { style: "--foo: red" }; const props2 = { style: { "--foo": "green", color: "blue" } }; const mergedProps = mergeProps(props1, props2); console.log(mergedProps.style); // "--foo: green; color: blue;" ``` ### `onDestroyEffect` Executes a callback when a component is destroyed. ### `onMountEffect` Executes a callback when a component is mounted. ### `styleToString` Converts a style object to a CSS string. ```ts const style = { color: "red", fontSize: "16px" }; const css = styleToString(style); console.log(css); // "color: red; font-size: 16px;" ``` ### `srOnlyStyles` An object of styles that can be used to hide content from the DOM but still be accessible to screen readers. ```ts export const srOnlyStyles: StyleProperties = { position: "absolute", width: "1px", height: "1px", padding: "0", margin: "-1px", overflow: "hidden", clip: "rect(0, 0, 0, 0)", whiteSpace: "nowrap", borderWidth: "0", transform: "translateX(-100%)" }; ``` ### `srOnlyStylesString` A string representation of `srOnlyStyles`. ### `useRefById` Finds the node with the given boxed `id` and sets it to the boxed `ref`. Reactive using `$effect` to ensure when the `id` or `deps` change, an update is triggered and the node is re-found. #### Props ```ts type UseRefByIdProps = { /** * The ID of the node to find. */ id: Box; /** * The ref to set the node to. */ ref: WritableBox; /** * A reactive condition that will cause the node to be set. */ deps?: Getter; /** * A callback fired when the ref changes. */ onRefChange?: (node: HTMLElement | null) => void; /** * A function that returns the root node to search for the element by ID. * Defaults to `() => (typeof document !== "undefined" ? document : undefined) */ getRootNode?: Getter; }; ``` ### `useOnChange` A simple helper function to react to changes to reactive state. This is useful for syncing a read-only dependency that may change with some writable state in your app. ```svelte ``` ### `attachRef` Creates a Svelte attachment that attaches a DOM element to a ref. ```svelte
Content
``` or ```svelte
(ref = node))}>Content
```