### 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 &&
Toggle }
```
--------------------------------
### 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"],
]
// =>
```
--------------------------------
### 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'],
],
]);
```