### Vode NPM Integration - HTML Setup Source: https://github.com/ryupold/vode/blob/main/README.md Basic HTML structure for an application using Vode installed via NPM. It includes a script tag to load the main JavaScript module. ```html Vode NPM Example
``` -------------------------------- ### Install Vode with NPM Source: https://github.com/ryupold/vode/blob/main/_autodocs/quick-start.md Install the Vode package using npm. This is the recommended method for most projects. ```bash npm install @ryupold/vode ``` -------------------------------- ### Vode NPM Integration - TypeScript Main File Source: https://github.com/ryupold/vode/blob/main/README.md Example TypeScript file for an application using Vode installed via NPM. It demonstrates importing Vode components and setting up a simple counter application. ```typescript import { app, createState, BR, DIV, INPUT, SPAN } from '@ryupold/vode'; const state = createState({ counter: 0, }); const appNode = document.getElementById('app')!; app(appNode, state, (s) => [DIV, [INPUT, { type: 'button', onclick: { counter: s.counter + 1 }, value: 'Click me', }], [BR], [SPAN, { style: { color: 'red' } }, `${s.counter}`], ] ); ``` -------------------------------- ### ClassProp Examples Source: https://github.com/ryupold/vode/blob/main/_autodocs/types.md Demonstrates various formats for specifying CSS classes using the ClassProp type. ```typescript class: "container dark" // string format ``` ```typescript class: ["container", "dark"] // array format ``` ```typescript class: { container: true, dark: false } // object format ``` ```typescript class: null // no classes ``` ```typescript class: { active: s.isActive } // conditional ``` -------------------------------- ### StyleProp Examples Source: https://github.com/ryupold/vode/blob/main/_autodocs/types.md Shows different ways to define inline CSS styles using the StyleProp type. ```typescript style: "color: red; font-size: 16px;" // string format ``` ```typescript style: { color: "red", fontSize: "16px" } // camelCase object ``` ```typescript style: { color: "red" } // partial object ``` ```typescript style: null // no styles ``` -------------------------------- ### Vode Variant Examples Source: https://github.com/ryupold/vode/blob/main/_autodocs/types.md Illustrates different ways to construct Vode instances, from just a tag to tag with props and children. ```typescript // Just a tag [DIV] // =>
``` ```typescript // Tag with props [DIV, { class: 'container' }] // =>
``` ```typescript // Tag with props and children [DIV, { id: 'main' }, 'text', [SPAN, 'nested']] // =>
textnested
``` ```typescript // Tag with children, no props [DIV, 'text', [SPAN, 'nested']] // =>
textnested
``` -------------------------------- ### Vode Tag Usage Example Source: https://github.com/ryupold/vode/blob/main/_autodocs/api-reference-utility-functions.md Demonstrates how to import and use Vode tag constants to create elements and SVG structures. ```typescript import { DIV, SPAN, BUTTON, SVG, CIRCLE } from '@ryupold/vode'; const element = [DIV, [SPAN, "Hello"], [BUTTON, "Click"]]; const svg = [SVG, { width: 100, height: 100 }, [CIRCLE, { cx: 50, cy: 50, r: 40 }] ]; ``` -------------------------------- ### Basic Counter Example Implementation Source: https://github.com/ryupold/vode/blob/main/_autodocs/quick-start.md A simple counter application using Vode. It initializes state and renders a button to increment a counter and a span to display its value. ```typescript import { app, DIV, BUTTON, SPAN, BR } from '@ryupold/vode'; const container = document.getElementById('app')!; const state = { counter: 0 }; app(container, state, (s) => [DIV, [BUTTON, { onclick: (s, evt) => ({ counter: s.counter + 1 }), }, "Increment"], [BR], [SPAN, `Count: ${s.counter}`], ]); ``` -------------------------------- ### Vode ESM Usage Example Source: https://github.com/ryupold/vode/blob/main/README.md Demonstrates how to use Vode with an ESM import in a standard HTML file. Ensure the script type is 'module' and import from the minified ESM bundle. ```html Vode ESM Example
``` -------------------------------- ### Create and Use Settings Context Source: https://github.com/ryupold/vode/blob/main/README.md Demonstrates creating a type-safe context for nested state and using it in a form component to get and patch settings. ```typescript import { app, context, createState, SubContext, Vode, DIV, FORM, H1, OPTION, P, SELECT } from "@ryupold/vode"; type Settings = { theme: string, lang: string }; type StateType = { user: { profile: { settings: Settings } } }; const state = createState({ user: { profile: { settings: { theme: 'dark', lang: 'es' } } } }); // Create a context for the nested settings const settingsCtx = context(state).user.profile.settings; const element = document.getElementById('app')!; app(element, state, (s) => [DIV, [H1, "Settings"], SettingsForm(settingsCtx), ] ); function SettingsForm(ctx: SubContext) { const settings = ctx.get(); // { theme: 'dark', lang: 'es' } return [FORM, [P, "current theme:", settings.theme], [SELECT, { class: 'theme-select', onchange: (_: unknown, e: Event) => ctx.patch({ theme: (e.target).value }), value: settings.theme, }, [OPTION, { value: 'light', selected: settings.theme === 'light' }, 'light'], [OPTION, { value: 'dark', selected: settings.theme === 'dark' }, 'dark'], ], [P, "current lang:", settings.lang], [SELECT, { class: 'lang-select', onchange: (_: unknown, e: Event) => ctx.patch({ lang: (e.target).value }), value: settings.lang, }, [OPTION, { value: 'en', selected: settings.lang === 'en' }, 'en'], [OPTION, { value: 'de', selected: settings.lang === 'de' }, 'de'], [OPTION, { value: 'es', selected: settings.lang === 'es' }, 'es'], [OPTION, { value: 'fr', selected: settings.lang === 'fr' }, 'fr'], ], ]; } ``` -------------------------------- ### Vode Classic (IIFE) Usage Example Source: https://github.com/ryupold/vode/blob/main/README.md Shows how to use Vode via a classic IIFE script tag, which binds the library to the global 'V' variable. This method is suitable for older environments or when module systems are not in use. ```html Vode ES5 (IIFE) Script Example
``` -------------------------------- ### JSX Example for Comparison Source: https://github.com/ryupold/vode/blob/main/_autodocs/virtual-dom-structure.md A typical JSX structure used in React-like frameworks, serving as a comparison point to Vode's array-based approach. ```jsx

{s.title}

{s.content}

{s.isActive && }
``` -------------------------------- ### State Patching Examples Source: https://github.com/ryupold/vode/blob/main/_autodocs/README.md Demonstrates how to update state using synchronous and asynchronous patches. State is a plain object that can be modified via the `patch` method. ```typescript const state = { count: 0 }; state.patch({ count: 5 }); // synchronous ``` ```typescript state.patch(async (s) => ({ count: s.count + 1 })); // async ``` -------------------------------- ### EventProp Examples Source: https://github.com/ryupold/vode/blob/main/_autodocs/types.md Illustrates how to define event properties in Vode, supporting either a function handler or a direct patch object. Conditional event handling is also shown. ```typescript // Function handler onclick: (s, evt) => ({ count: s.count + 1 }) // Direct patch onclick: { count: 5 } // Conditional event onclick: s.isActive && ((s, evt) => ({ active: false })) ``` -------------------------------- ### Vode Array-Based Equivalent to JSX Source: https://github.com/ryupold/vode/blob/main/_autodocs/virtual-dom-structure.md The Vode equivalent of the provided JSX example, demonstrating how to represent the same UI structure using plain JavaScript arrays. ```typescript [DIV, { class: "card" }, [H1, s.title], [P, s.content], s.isActive && [BUTTON, { onclick: toggle }, "Toggle"], ] ``` -------------------------------- ### Setting Standard HTML Attributes Source: https://github.com/ryupold/vode/blob/main/_autodocs/props-and-attributes.md Examples of how to set common HTML attributes for various elements like INPUT, IMG, A, and FORM using Vode's declarative syntax. ```typescript [INPUT, { type: "email", name: "email", placeholder: "Enter email", required: true, disabled: false, value: "user@example.com", }] ``` ```typescript [IMG, { src: "image.png", alt: "Description", width: 100, height: 100, }] ``` ```typescript [A, { href: "https://example.com", target: "_blank", rel: "noopener", }] ``` ```typescript [FORM, { method: "POST", action: "/submit", enctype: "multipart/form-data", }] ``` -------------------------------- ### TypeScript Inference Example Source: https://github.com/ryupold/vode/blob/main/_autodocs/README.md Demonstrates full TypeScript inference without requiring explicit type annotations when using the `app` function with a state object. ```typescript const state = { count: 0 }; const patch = app(container, state, (s) => { // s is inferred as { count: number } // s.count is inferred as number return [DIV, s.count]; }); ``` -------------------------------- ### Explicit State Type Declaration for Vode App Source: https://github.com/ryupold/vode/blob/main/README.md This example shows how to explicitly define the state type when initializing a Vode app, offering more control over type safety. ```typescript type State = typeof state; app(appNode, state, (s: State) => ...); ``` -------------------------------- ### Basic Counter Example HTML Structure Source: https://github.com/ryupold/vode/blob/main/_autodocs/quick-start.md The basic HTML structure required for the Vode counter application. It includes a div with id="app" to mount the application and a script tag to load the main JavaScript file. ```html Counter App
``` -------------------------------- ### Vode Structure Example Source: https://github.com/ryupold/vode/blob/main/README.md This snippet shows how an HTML structure is represented as a Vode tuple. Vode uses an array format: [TAG, PROPS?, CHILDREN...]. ```typescript [DIV, { class: 'card' }, [DIV, { class: 'card-image' }, [FIGURE, { class: 'image is-4by3' }, [IMG, { src: 'placeholders/1280x960.png', alt: 'Placeholder image' }] ] ], [DIV, { class: 'card-content' }, [DIV, { class: 'media' }, [DIV, { class: 'media-left' }, [FIGURE, { class: 'image is-48x48' }, [IMG, { src: 'placeholders/96x96.png', alt: 'Placeholder image' }] ] ], [DIV, { class: 'media-content' }, [P, { class: 'title is-4' }, 'John Smith'], [P, { class: 'subtitle is-6' }, '@johnsmith'] ] ], [DIV, { class: 'content' }, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', [A, { href: '?post=vode' }, 'vode'], '. ', [A, { href: '#' }, '#css'], [A, { href: '#' }, '#responsive'], [BR], [TIME, { datetime: '2025-09-24' }, '10:09 PM - 24 Sep 2025'] ] ] ] ``` -------------------------------- ### Component with State Access and Manipulation Source: https://github.com/ryupold/vode/blob/main/_autodocs/quick-start.md Build a component that displays state and allows direct manipulation of that state through buttons. This example shows a simple counter. ```typescript const Counter = (s) => [DIV, [P, `Count: ${s.count}`], [BUTTON, { onclick: (s) => ({ count: s.count + 1 }) }, "+"], [BUTTON, { onclick: (s) => ({ count: s.count - 1 }) }, "-"], ]; // Use (s) => [DIV, Counter, ] ``` -------------------------------- ### Use Lifecycle Hooks for Component Initialization and Cleanup Source: https://github.com/ryupold/vode/blob/main/_autodocs/quick-start.md Leverage lifecycle hooks like `onMount` and `onUnmount` to perform actions when a component is added to or removed from the DOM. This example focuses on input element focus. ```typescript [INPUT, { type: "text", onMount: (s, element) => { element.focus(); // Focus on mount return { inputReady: true }; }, onUnmount: (s, element) => { return { inputReady: false }; } }] ``` -------------------------------- ### Override View Transitions Renderer Source: https://github.com/ryupold/vode/blob/main/_autodocs/advanced-features.md This example demonstrates how to override the default animation behavior for both synchronous and asynchronous renderers. You can implement custom animation logic or timing. ```typescript container._vode.syncRenderer = (cb) => { // Use custom timing function instead of requestAnimationFrame setTimeout(cb, 100); }; container._vode.asyncRenderer = (cb) => { // Custom animation implementation console.log("Custom animation starting"); return document.startViewTransition(cb); }; ``` -------------------------------- ### Dynamic Attributes with Conditional Classes and Styles Source: https://github.com/ryupold/vode/blob/main/_autodocs/virtual-dom-structure.md Apply dynamic classes and inline styles to elements based on component state. This example shows how to conditionally set 'class' and 'style' properties. ```typescript // Conditional classes [DIV, { class: s.isActive ? "active" : "inactive", style: { color: s.color, opacity: s.isVisible ? 1 : 0, } }] ``` -------------------------------- ### Vode Patching: Discouraged Usage and View Transitions Source: https://github.com/ryupold/vode/blob/main/README.md Highlights the discouragement of patching within a render step. It also introduces experimental support for view transitions using array-based patches, including skipping transitions or starting new ones. ```javascript // ❌ it is discouraged to patch inside the render step 💩 const ComponentEwww = (s) => { if(!s.isLoading) s.patch(() => startLoading()); return [DIV, s.isLoading ? [PROGRESS] : s.title]; } // ✨ experimental view transitions support ✨ // patch with a render via view transition s.patch([{}, (s) => {/*...*/}]); // all given patches will be part of a view transition // an empty array tells vode to skip the current view transition // and set the queued animated patches until now as current state with a sync patch s.patch([]); // skip current view transition and start this view transition instead s.patch([[], { loading: true }]); ``` -------------------------------- ### App Initialization with Async Effects Source: https://github.com/ryupold/vode/blob/main/_autodocs/api-reference-app.md Initializes a Vode application with initial state, UI, and an asynchronous effect to fetch data. Handles loading states. ```typescript const state = { data: null, loading: false }; app(container, state, (s) => [DIV, s.loading ? [P, 'Loading...'] : [P, s.data], ], async (s) => { const res = await fetch('/api/data'); return { data: await res.json(), loading: false }; } ); ``` -------------------------------- ### Creating State Before App Initialization Source: https://github.com/ryupold/vode/blob/main/_autodocs/state-management.md Shows how to use `createState` to define initial state and queue patches before the Vode application is mounted. All queued patches are applied immediately after initialization. ```typescript import { createState, app } from '@ryupold/vode'; const state = createState({ data: null, loading: true, }); // Queue patches before app() is called state.patch({ data: "initial data" }); state.patch(async (s) => { const response = await fetch('/api/config'); return { data: await response.json(), loading: false, }; }); const container = document.getElementById('app')!; // All queued patches apply after first render app(container, state, (s) => [DIV, s.data]); ``` -------------------------------- ### SubContext API Source: https://github.com/ryupold/vode/blob/main/_autodocs/api-reference-utility-functions.md The SubContext interface provides methods for getting, putting, and patching sub-state values within the context. ```APIDOC ## SubContext API ### Description The `SubContext` interface provides three methods for interacting with a specific slice of the state. ### Interface ```typescript interface SubContext { get(): SubState; put(value: SubState): void; patch(value: SubState | Partial | DeepPartial, animated?: boolean): void; } ``` ### Methods - **get()**: Returns the current value of the sub-state. - **put(value)**: Assigns a new value to the sub-state without triggering a render (silent mutation). - **patch(value, animated?)**: Applies partial or full updates to the sub-state and triggers a render. The `animated` parameter can be used to indicate if the update should be animated. ### Examples ```typescript import { context, createState, app, DIV, SELECT, OPTION, FORM } from '@ryupold/vode'; const state = createState({ settings: { theme: 'light', lang: 'en' } }); const settingsCtx = context(state).settings; function SettingsForm(ctx: SubContext<{ theme: string; lang: string }>) { const settings = ctx.get(); return [FORM, [SELECT, { value: settings.theme, onchange: (_, e) => ctx.patch({ theme: e.target.value }) }, [OPTION, { value: 'light' }, 'Light'], [OPTION, { value: 'dark' }, 'Dark'] ], [SELECT, { value: settings.lang, onchange: (_, e) => ctx.patch({ lang: e.target.value }) }, [OPTION, { value: 'en' }, 'English'], [OPTION, { value: 'de' }, 'Deutsch'] ] ]; } app(container, state, (s) => [DIV, SettingsForm(settingsCtx) ]); ``` ``` -------------------------------- ### Basic Counter App Initialization Source: https://github.com/ryupold/vode/blob/main/_autodocs/api-reference-app.md Initializes a Vode application with a simple counter state and UI. Demonstrates basic state updates via button clicks. ```typescript import { app, DIV, INPUT, BR, SPAN } from '@ryupold/vode'; const container = document.getElementById('app')!; const state = { counter: 0 }; app(container, state, (s) => [DIV, [INPUT, { type: 'button', onclick: { counter: s.counter + 1 }, value: 'Increment', }], [BR], [SPAN, `Count: ${s.counter}`], ]); // Later, update state state.patch({ counter: 5 }); ``` -------------------------------- ### Update State from Button Click Event Source: https://github.com/ryupold/vode/blob/main/_autodocs/quick-start.md Use an event handler to update the component's state. This example increments a counter. ```typescript [BUTTON, { onclick: (s, evt) => ({ count: s.count + 1, }), }] ``` -------------------------------- ### Get Vode Properties Source: https://github.com/ryupold/vode/blob/main/_autodocs/api-reference-core-functions.md Use the `props` function to retrieve the properties object from a Vode. Returns undefined if the Vode has no associated properties. ```typescript import { props } from '@ryupold/vode'; const v1 = [DIV, { class: "box", id: "main" }, "content"]; props(v1); // { class: "box", id: "main" } const v2 = [SPAN, "text"]; props(v2); // undefined const v3 = [BR]; props(v3); // undefined ``` -------------------------------- ### App Initialization Source: https://github.com/ryupold/vode/blob/main/_autodocs/README.md Initializes the Vode application within a specified container element, using the provided state and a render function. The render function defines the UI based on the current state. ```typescript app(container, state, (s) => [DIV, s.count]); ``` -------------------------------- ### Vode State Initialization and Direct Update Source: https://github.com/ryupold/vode/blob/main/README.md Initializes state and demonstrates direct property updates on the state object after `app` initialization. Direct updates are silent patches and do not trigger a re-render. ```javascript const s = { counter: 0, pointing: false, loading: false, title: 'foo', body: '', }; app(appNode, s, s => AppView(s)); // after calling app(), the state object is bound to the appNode // update state directly as it is a singleton (silent patch, no render) s.title = 'Hello World'; ``` -------------------------------- ### Basic Vode Structure and Rendering Source: https://github.com/ryupold/vode/blob/main/README.md Illustrates the structure of a full Vode, including tags, properties, and children. Demonstrates rendering strings, self-closing tags, and conditional elements. ```typescript // A full vode has a tag, properties, and children. props and children are optional. const CompFoo = (s) => [SPAN, { class: "foo" }, s.isAuthenticated ? "foo" : "bar"]; const CompBar = (s) => [DIV, { class: "container" }, // a child vode can be a string, which results in a text node [H1, "Hello World"], // a vode can also be a self-closing tag [HR], // conditional rendering s.isAuthenticated ? [STRONG, `and also hello ${s.user}`] : [FORM, [INPUT, { type: "email", name: "email" }], [INPUT, { type: "password", name: "pw" }], [INPUT, { type: "submit" }], ], // a child-vode of false, undefined or null is not rendered !s.isAuthenticated && [HR], // style object maps directly to the HTML style attribute [P, { style: { color: "red", fontWeight: "bold" } }, "This is a paragraph."], [P, { style: "color: red; font-weight: bold;" }, "This is also a paragraph."], // class property has multiple forms [UL, [LI, {class: "class1 class2"}, "as string"], [LI, {class: ["class1", "class2"]}, "as array"], [LI, {class: {class1: true, class2: false}}, "as Record"], ], // events get the state object as first argument // and the HTML event object as second argument [BUTTON, { // all on* events accept `Patch` onclick: (s, evt) => { // objects returned by events are patched automatically return { counter: s.counter + 1 }; }, // you can set the patch object directly for events onmouseenter: { pointing: true }, onmouseleave: { pointing: false }, // a patch can be an async function onmouseup: async (s, evt) => { s.patch({ loading: true }); const result = await apiCall(); return { title: result.data.title, loading: false }; }, // you can also use a generator function that yields patches onmousedown: async function* (s, evt) { yield { loading: true }; const result = await apiCall(); yield { body: result.data.body, }; return { loading: false }; }, // events can be attached conditionally ondblclick: s.counter > 20 && ((s, evt) => { return { counter: s.counter * 2 }; }), class: { bar: s.pointing } }, "Click me!"], // components can be used as child-vodes, they are called lazily on render CompFoo, // or this way CompFoo(s), ]; ``` -------------------------------- ### Initializing State and App Source: https://github.com/ryupold/vode/blob/main/_autodocs/state-management.md Define a plain JavaScript object for state and pass it to the app function along with the container and a render function. ```typescript const state = { counter: 0, user: { name: "Alice", email: "alice@example.com" }, items: ["todo1", "todo2"], loading: false, }; app(container, state, (s) => [DIV, s.counter]); ``` -------------------------------- ### Vode Component with onMount and onUnmount Source: https://github.com/ryupold/vode/blob/main/README.md Demonstrates the usage of `onMount` and `onUnmount` for input focus and timer lifecycle events within a Vode component. Shows how returning a patch object from these hooks updates the state. ```typescript const container = document.getElementById('app')!; const state = createState({ startTime: 0, inputReady: false, showInput: true, showTimer: true }); const patch = app(container, state, (s) => [DIV, s.showInput && [INPUT, { type: 'text', placeholder: 'Auto-focused on mount', onMount: (s: typeof state, ele: HTMLElement) => { console.log('Input mounted'); (ele as HTMLInputElement).focus(); return { inputReady: true }; }, onUnmount: (s: typeof state, ele: HTMLElement) => { console.log('Input removed'); return { inputReady: false }; } }], s.showTimer && [P, { onMount: (s: typeof state, ele: HTMLElement) => { console.log('Timer started'); s.patch({ startTime: Date.now() }); }, onUnmount: (s: typeof state, ele: HTMLElement) => { console.log('Timer stopped after', Date.now() - s.startTime, 'ms'); } }, 'Mount/unmount lifecycle demo'] ] ); // OUTPUT: // 1. Input mounted // 2. Timer started patch({ showInput: false }); // 3. Input removed patch({ showTimer: false }); // 4. Timer stopped after XY ms ``` -------------------------------- ### Create a Counter with Vode Source: https://github.com/ryupold/vode/blob/main/_autodocs/README.md Initialize a Vode application with a counter state and buttons to increment it. Requires importing necessary components from '@ryupold/vode'. ```typescript import { app, DIV, BUTTON, BR } from '@ryupold/vode'; app(document.getElementById('app')!, { count: 0 }, (s) => [DIV, [BUTTON, { onclick: (s) => ({ count: s.count + 1 }) }, "+"], [BR], [DIV, `Count: ${s.count}`], ]); ``` -------------------------------- ### Hierarchical Error Propagation Source: https://github.com/ryupold/vode/blob/main/_autodocs/advanced-features.md Demonstrates how errors propagate up the component tree until caught by the nearest ancestor with a `catch` handler. This example shows an error being caught by an inner boundary. ```typescript [DIV, { catch: (s, err) => [P, "Outer catch"] }, [DIV, { catch: (s, err) => [P, "Inner catch"] }, ThrowingComponent, // Error caught by inner catch ], ] ``` -------------------------------- ### Get Vode Tag Name Source: https://github.com/ryupold/vode/blob/main/_autodocs/api-reference-core-functions.md Use the `tag` function to extract the HTML/SVG/MathML tag name from a Vode structure. It returns undefined for text nodes or falsy values. ```typescript import { tag } from '@ryupold/vode'; tag([DIV, "content"]) // "div" tag([SPAN, { class: "x" }]) // "span" tag("text string") // undefined tag(null) // undefined tag(undefined) // undefined ``` -------------------------------- ### Basic UI Rendering Source: https://github.com/ryupold/vode/blob/main/_autodocs/quick-start.md Renders a basic application structure with a header, paragraph, and a button within a container. ```typescript app(container, state, (s) => [DIV, { class: "app" }, [H1, "My App"], [P, "Hello World"], [BUTTON, "Click me"], ]); ``` -------------------------------- ### Define and Use a Simple Header Component Source: https://github.com/ryupold/vode/blob/main/_autodocs/quick-start.md Create a basic component that renders a header with a title and navigation. Components are automatically invoked when used within the render function. ```typescript const Header = (s) => [HEADER, [H1, s.title], [NAV, /* ... */], ]; // Use in render (s) => [DIV, Header, // Invoked automatically ] ``` -------------------------------- ### Accessing Globals and View Transitions API Source: https://github.com/ryupold/vode/blob/main/_autodocs/advanced-features.md Demonstrates how to access internal global configurations, check for View Transitions API support, access the current view transition, and override the requestAnimationFrame function. ```typescript import { globals } from '@ryupold/vode'; // Check view transitions support if (globals.startViewTransition) { console.log("View Transitions API supported"); } // Access current view transition if (globals.currentViewTransition) { await globals.currentViewTransition.updateCallbackDone; } // Override request animation frame globals.requestAnimationFrame = (cb) => { console.log("Custom RAF called"); setTimeout(cb, 16); }; ``` -------------------------------- ### Get Child Vodes Source: https://github.com/ryupold/vode/blob/main/_autodocs/api-reference-core-functions.md Retrieves an array of child Vode structures from a given Vode. Returns undefined if no children are present. Useful for iterating over or inspecting the direct children of a Vode. ```typescript import { children } from '@ryupold/vode'; const v = [DIV, [SPAN, "A"], [SPAN, "B"]]; children(v); // [[SPAN, "A"], [SPAN, "B"]] const v2 = [DIV, { class: "box" }, "text"]; children(v2); // ["text"] const v3 = [BR]; children(v3); // undefined ``` -------------------------------- ### Extract Tag from a Node Source: https://github.com/ryupold/vode/blob/main/_autodocs/virtual-dom-structure.md The tag() helper gets the element type (tag name) from a Vode node. Returns undefined for non-node types like plain text or null. ```typescript import { tag } from '@ryupold/vode'; tag([DIV, { class: "box" }]); // "div" tag([SPAN, "text"]); // "span" tag("plain text"); // undefined tag(null); // undefined ``` -------------------------------- ### Run Multiple Independent App Instances Source: https://github.com/ryupold/vode/blob/main/_autodocs/advanced-features.md Instantiate multiple Vode applications on the same page. Each app is isolated with its own state and update mechanisms. ```typescript import { app, createState } from '@ryupold/vode'; // App 1 const state1 = createState({ count: 0 }); const patch1 = app( document.getElementById('app1')!, state1, (s) => [DIV, `Count: ${s.count}`], ); // App 2 const state2 = createState({ name: "Test" }); const patch2 = app( document.getElementById('app2')!, state2, (s) => [DIV, `Name: ${s.name}`], ); // Each app is isolated patch1({ count: 5 }); patch2({ name: "Updated" }); ``` -------------------------------- ### Managing Nested State in Vode Source: https://github.com/ryupold/vode/blob/main/_autodocs/README.md Access and update nested state properties using Vode's context API. This example demonstrates updating a theme setting in a select element. ```typescript const settingsCtx = context(state).user.profile.settings; [SELECT, { value: settingsCtx.get().theme, onchange: (_, e) => settingsCtx.patch({ theme: e.target.value }), }] ``` -------------------------------- ### Create and Use State Context with Property Chaining Source: https://github.com/ryupold/vode/blob/main/_autodocs/api-reference-utility-functions.md Demonstrates creating a state context and accessing nested properties using property chaining. Use `put` for silent updates and `patch` to trigger renders. ```typescript import { context, app } from '@ryupold/vode'; type State = { user: { profile: { settings: { theme: 'light' | 'dark'; lang: string } } } }; const state = createState({ user: { profile: { settings: { theme: 'dark', lang: 'en' } } } }); // Property chaining const settingsCtx = context(state).user.profile.settings; settingsCtx.get(); // { theme: 'dark', lang: 'en' } settingsCtx.put({ theme: 'light', lang: 'de' }); // silent update settingsCtx.patch({ theme: 'light' }); // triggers render ``` -------------------------------- ### Conditional Rendering in Vode Source: https://github.com/ryupold/vode/blob/main/_autodocs/README.md Conditionally render different UI elements based on the application's state, such as user login status. This example shows rendering user info or a form. ```typescript (s) => [DIV, s.isLoggedIn ? [P, `Hello, ${s.user}`] : [FORM, /* ... */], ] ``` -------------------------------- ### Basic View Transitions API Usage Source: https://github.com/ryupold/vode/blob/main/_autodocs/advanced-features.md This snippet shows how to enable animated page transitions using the View Transitions API. Ensure `asyncRenderer` is available for view transitions to work. ```typescript import { app } from '@ryupold/vode'; const state = createState({ page: "home" }); app(container, state, (s) => [DIV, s.page === "home" && renderHome(s), s.page === "about" && renderAbout(s), ]); // Animated page transition state.patch([ { page: "about" }, ]); ``` -------------------------------- ### Access Nested State with Context Source: https://github.com/ryupold/vode/blob/main/_autodocs/quick-start.md Utilize context to access and modify nested state properties. Create a context for a specific part of the state and use its methods to get or patch values. ```typescript import { context } from '@ryupold/vode'; const state = { user: { name: "Alice", email: "..." } }; const userCtx = context(state).user; userCtx.get(); // { name: "Alice", ... } userCtx.patch({ name: "Bob" }); // Patch user, triggers render ``` -------------------------------- ### Vode Patching: Basic and Function-Based Updates Source: https://github.com/ryupold/vode/blob/main/README.md Demonstrates triggering a re-render using `s.patch({})` and updating state properties with `s.patch({ title: 'bar' })`. It also shows how to use a function within `patch` to update state based on its current value. ```javascript // render patch s.patch({}); // render patch with a change that is applied to the state s.patch({ title: 'bar' }); // patch with a function that receives the state s.patch((s) => ({body: s.body + ' baz'})); ``` -------------------------------- ### Accessing a Child Vode Element Source: https://github.com/ryupold/vode/blob/main/_autodocs/api-reference-core-functions.md Use the 'child' function to get a specific child Vode element from a parent Vode structure using its zero-based index. Returns undefined if the index is out of bounds. ```typescript import { child } from '@ryupold/vode'; const v = [DIV, [SPAN, "A"], [SPAN, "B"]]; child(v, 0); // [SPAN, "A"] child(v, 1); // [SPAN, "B"] child(v, 2); // undefined ``` -------------------------------- ### Skip View Transition with Empty Array Patch Source: https://github.com/ryupold/vode/blob/main/_autodocs/state-management.md Use an empty array patch to skip the current view transition and merge queued patches, or to skip and start a new transition with subsequent patches. ```typescript // Skip current view transition and merge queued patches state.patch([]); // Or skip and start a new transition state.patch([[], { page: 2 }]); ``` -------------------------------- ### Rendering a MathML Element with Vode Source: https://github.com/ryupold/vode/blob/main/README.md Illustrates how to define and render a MathML component using Vode. Requires specifying the MathML namespace in the properties. ```typescript import { MATH, MSUP, MI, MN } from '@ryupold/vode'; const CompMathML = (s) => [MATH, { xmlns: 'http://www.w3.org/1998/Math/MathML' }, [MSUP, [MI, 'x'], [MN, '2'] ] ]; ``` -------------------------------- ### Resolving Property Conflicts with Path Producer Source: https://github.com/ryupold/vode/blob/main/_autodocs/advanced-features.md When state property names conflict with context methods like 'get' or 'patch', use a path producer function as the second argument to `context()` to specify the desired property. ```typescript type State = { get: { value: number }, // 'get' property patch: { enabled: boolean }, // 'patch' property }; const state = createState({ /* ... */ }); // Property chaining would conflict, use path producer const getCtx = context(state, s => s.get); const patchCtx = context(state, s => s.patch); getCtx.get(); // returns { value: number } patchCtx.get(); // returns { enabled: boolean } ``` -------------------------------- ### createState Source: https://github.com/ryupold/vode/blob/main/_autodocs/api-reference-core-functions.md Creates a patchable state object that can queue patches before the app is initialized. This is useful for pre-configuring state before calling `app()`. ```APIDOC ## createState Creates a patchable state object that can queue patches before the app is initialized. Useful for pre-configuring state before calling `app()`. ### Signature ```typescript export function createState(state: S): PatchableState; ``` ### Parameters #### Parameters - **state** (S) - Required - Plain object to become patchable. A `patch` property will be added ### Return `PatchableState` — the same object with a `patch` method added ### Behavior - Adds a non-enumerable `patch` property to the state object - Before `app()` is called, patches are queued in `state.patch.initialPatches` - After `app()` is called, patches are applied immediately and trigger renders - Safe to call `patch()` multiple times before `app()` initialization ### Example ```typescript import { createState, app } from '@ryupold/vode'; const state = createState({ userId: null, userData: null, loading: false, }); // Queue patches before app initialization state.patch({ loading: true }); state.patch(async (s) => { const user = await fetch('/api/user').then(r => r.json()); return { userData: user, loading: false }; }); // Pass to app; queued patches will be applied after first render app(container, state, (s) => [DIV, s.userData?.name]); ``` ``` -------------------------------- ### State Context with Path Producer for Conflicting Property Names Source: https://github.com/ryupold/vode/blob/main/_autodocs/api-reference-utility-functions.md Illustrates using a path producer function with `context` to safely access nested state when property names might conflict with the context API (e.g., 'get', 'put', 'patch'). ```typescript import { context, app } from '@ryupold/vode'; type State = { user: { profile: { settings: { theme: 'light' | 'dark'; lang: string } } } }; const state = createState({ user: { profile: { settings: { theme: 'dark', lang: 'en' } } } }); // Property chaining const settingsCtx = context(state).user.profile.settings; settingsCtx.get(); // { theme: 'dark', lang: 'en' } settingsCtx.put({ theme: 'light', lang: 'de' }); // silent update settingsCtx.patch({ theme: 'light' }); // triggers render // Path producer (for properties named get/put/patch) type State2 = { get: { value: number } // 'get' property name conflicts }; const ctx = context(state, s => s.get); // Now safe to access the 'get' property without conflicting with context API ``` -------------------------------- ### Vendor Prefix Handling Source: https://github.com/ryupold/vode/blob/main/_autodocs/api-reference-utility-functions.md Shows how `mergeStyle` automatically handles vendor prefixes for CSS properties. ```typescript import { mergeStyle } from '@ryupold/vode'; // Vendor prefix mergeStyle({ WebkitTransform: 'rotate(45deg)' }); // "-webkit-transform: rotate(45deg);" ``` -------------------------------- ### Vode Patching: Asynchronous Updates Source: https://github.com/ryupold/vode/blob/main/README.md Illustrates how to use asynchronous functions and async generator functions with `s.patch` to handle operations like API calls and yield intermediate state updates before returning a final state. ```javascript // patch with an async function that receives the state s.patch(async (s) => { s.loading = true; // sometimes it is easier to combine a silent patch s.patch({}); // with an empty render patch const result = await apiCall(); return { title: result.title, body: result.body, loading: false }; }); // can be awaited to wait for execution // patch with an async generator function that yields patches s.patch(async function*(s){ yield { loading: true }; const result = await apiCall(); yield { title: result.title, body: result.body }; return { loading: false }; }); // can be awaited to wait for execution ``` -------------------------------- ### Minimal Virtual DOM Nodes Source: https://github.com/ryupold/vode/blob/main/_autodocs/virtual-dom-structure.md Illustrates the basic array structure for creating virtual DOM nodes with tags, attributes, text content, and nested elements. ```typescript // Just a tag [DIV] // =>
// With attributes [DIV, { id: "main", class: "container" }] // =>
// With text content [P, "Hello World"] // =>

Hello World

// With nested elements [DIV, [H1, "Title"], [P, "Paragraph"], ] // =>

Title

Paragraph

``` -------------------------------- ### Create Nodes with vode() Source: https://github.com/ryupold/vode/blob/main/_autodocs/virtual-dom-structure.md Use the vode() function for a verbose way to create nodes, or use the array shorthand. It also passes through existing arrays. ```typescript import { vode, DIV, SPAN } from '@ryupold/vode'; // Explicit form const node = vode(DIV, { class: "box" }, vode(SPAN, "content") ); // Equivalent to const node = [DIV, { class: "box" }, [SPAN, "content"]]; // Identity passthrough const existing = [DIV, "hello"]; vode(existing) // returns the same array ``` -------------------------------- ### Event-Driven State Patches with Input Change Source: https://github.com/ryupold/vode/blob/main/_autodocs/state-management.md Demonstrates using an `onchange` event handler for an input element to return a patch object, updating the state with the input's current value. ```typescript [INPUT, { onchange: (s, evt) => ({ value: evt.target.value, }), }] ``` -------------------------------- ### Re-exporting from Root Entry Point Source: https://github.com/ryupold/vode/blob/main/_autodocs/module-exports.md All public exports are re-exported from the main index.ts file for convenient importing into your project. ```typescript export * from "./src/vode"; export * from "./src/vode-tags"; export * from "./src/merge-class"; export * from "./src/merge-style"; export * from "./src/merge-props"; export * from "./src/state-context"; ``` -------------------------------- ### Include Vode via CDN (IIFE/Classic) Source: https://github.com/ryupold/vode/blob/main/_autodocs/quick-start.md Include Vode in your HTML using script tags to load the IIFE/Classic version from a CDN. The Vode object will be available globally as 'V'. ```html ``` -------------------------------- ### Lifecycle Hooks for Resource Management Source: https://github.com/ryupold/vode/blob/main/_autodocs/advanced-features.md Implement `onMount` and `onUnmount` lifecycle hooks to manage resources like timers. `onMount` is called when the component is added to the DOM, and `onUnmount` is called when it's removed. ```typescript [DIV, { onMount: (s, element) => { const timerId = setInterval(() => { console.log("Tick"); }, 1000); // Store timer ID in state for cleanup return { timerId }; }, onUnmount: (s, element) => { clearInterval(s.timerId); return { timerId: null }; }, }] ``` -------------------------------- ### context Source: https://github.com/ryupold/vode/blob/main/_autodocs/module-exports.md Creates type-safe contexts for nested state access and manipulation. ```APIDOC ## context ### Description Creates type-safe contexts for nested state access and manipulation. ### Signatures `export function context(state: S): ProxyStateContext;` `export function context(state: S, producePath: (ctx: ProxyState) => ProxyState): ProxyStateContext;` ``` -------------------------------- ### Conditional Rendering and Event Handlers Source: https://github.com/ryupold/vode/blob/main/_autodocs/api-reference-app.md Demonstrates conditional rendering based on application state and handling user interactions with event listeners like `onclick` and `onchange`. ```typescript app(container, state, (s) => [DIV, [H1, 'Welcome'], s.isLoggedIn ? [DIV, [P, `Hello, ${s.user}`], [BUTTON, { onclick: { isLoggedIn: false } }, 'Logout'], ] : [FORM, [INPUT, { type: 'email', value: s.email, onchange: (s, e) => ({ email: e.target.value }) }], [BUTTON, { onclick: { isLoggedIn: true, user: 'Guest' } }, 'Login'], ], ]); ```