# Vnode

Import these from `@grafloria/renderer`.

## Functions

### `camelToKebab`

camelCase → kebab-case (`strokeWidth` → `stroke-width`).

```ts
function camelToKebab(str: string): string
```

### `createDomElement`

Build a fresh detached DOM tree for `vnode` using the default patcher.

```ts
function createDomElement(vnode: VNode, namespace: string = SVG_NS): Element
```

### `createForeignObject`

Create a foreignObject VNode

Creates a VNode representing an SVG foreignObject element, which allows
embedding HTML content inside SVG. Automatically generates a container ID
if not provided, and includes a default XHTML div wrapper.

```ts
function createForeignObject(options: ForeignObjectOptions): VNode
```

**Parameters**

- `options`: Configuration options for the foreignObject

**Returns** A VNode of type 'foreignObject'

**Example**

```typescript
const vnode = createForeignObject({
  nodeId: 'node-1',
  x: 10,
  y: 20,
  width: 200,
  height: 150
});
// Returns: { type: 'foreignObject', props: { x: 10, y: 20, ... }, children: [...] }
```

**Example**

 With custom container ID and children
```typescript
const vnode = createForeignObject({
  nodeId: 'node-1',
  x: 10,
  y: 20,
  width: 200,
  height: 150,
  containerId: 'my-custom-id',
  children: [
    { type: 'div', props: { className: 'custom-content' } }
  ]
});
```

### `getContainerId`

Get the container ID from a foreignObject VNode

Extracts the container ID from a foreignObject VNode's props. Returns undefined if the VNode is not a foreignObject or doesn't have a container ID.

```ts
function getContainerId(vnode: VNode): string | undefined
```

**Parameters**

- `vnode`: The VNode to extract the container ID from

**Returns** The container ID if the VNode is a foreignObject, undefined otherwise

**Example**

```typescript
const vnode = createForeignObject({
  nodeId: 'node-1',
  x: 0, y: 0, width: 100, height: 100
});

const containerId = getContainerId(vnode);
// Returns: 'fo-node-1-1'
```

**Example**

 Non-foreignObject returns undefined
```typescript
const rectNode = { type: 'rect', props: { ... } };
const containerId = getContainerId(rectNode);
// Returns: undefined
```

### `isForeignObject`

Check if a VNode is a foreignObject element

Type guard function that checks if the given VNode represents
a foreignObject element.

```ts
function isForeignObject(vnode: VNode): boolean
```

**Parameters**

- `vnode`: The VNode to check

**Returns** True if the VNode is a foreignObject, false otherwise

**Example**

```typescript
const foNode = createForeignObject({ ... });
const rectNode = { type: 'rect', props: { ... } };

isForeignObject(foNode);   // true
isForeignObject(rectNode); // false
```

### `isOpaqueVNode`

foreignObject subtrees embed live HTML (framework components, form controls,
media). They are OPAQUE to the diff: props are patched, children are left
exactly as they are. Diffing into them would wipe whatever was mounted there.

```ts
function isOpaqueVNode(vnode: VNode): boolean
```

### `reconcile`

Reconcile `vnode` into `container` using the default patcher.

```ts
function reconcile(container: Element, vnode: VNode): Element
```

### `serializeStyle`

Serialize a style prop. Accepts the object form the renderer emits
(`{ cursor: 'move' }`) as well as a plain string. Returns '' for empty/absent styles.

```ts
function serializeStyle(style: unknown): string
```

## Classes

### `ContainerIdGenerator`

Generate unique container IDs for foreignObject elements

This class provides static methods to generate and validate container IDs
used to link SVG foreignObject elements with their Angular component content.

```ts
class ContainerIdGenerator
```

**Methods**

- `static generate(nodeId: string): string` (static) — Generate a unique container ID for a foreignObject element
- `static isContainerId(id: string): boolean` (static) — Check if a given ID is a valid container ID

Container IDs must start with 'fo-' prefix. This is a lightweight check that only validates the prefix.
- `static getNodeId(containerId: string): string | null` (static) — Extract the node ID from a container ID

Parses a container ID and returns the original node ID. Container IDs must follow the format: `fo-{nodeId}-{counter}`
- `static reset(): void` (static) — Reset the internal counter to zero

This method is primarily for testing purposes to ensure
predictable ID generation in test suites.

### `VNodePatcher`

Keyed VNode → DOM reconciler.

```ts
const patcher = new VNodePatcher();
patcher.reconcile(container, vnodeTree);   // first call: mount
patcher.reconcile(container, nextTree);    // later calls: diff + patch in place
```

```ts
class VNodePatcher
```

**Methods**

- `constructor(options: VNodePatcherOptions = {})`
- `get stats(): Readonly<PatchStats>` — Work done during the most recent `reconcile()` call.
- `reconcile(container: Element, vnode: VNode): Element` — Diff `vnode` against whatever this patcher last rendered into `container`
and patch the existing DOM in place. First call (or a lost root) mounts a
fresh tree.
- `hydrate(container: Element, vnode: VNode): Element` — ADOPT the DOM already inside `container` as the materialization of `vnode`,
without creating, moving or removing a single node.

This is the client half of SSR hydration. The server emitted the
serialization of this exact VNode tree into the HTML; re-mounting it would
mean tearing that DOM down and rebuilding it — the "hydration flash" every
flow library ships with. Instead we simply register the existing root as our
mount, so the NEXT `reconcile()` diffs against `vnode` and touches only what
genuinely changed.

The adopt is only taken when the existing root plausibly IS this tree (same
tag name); otherwise we fall back to a normal mount, which is always correct
(just not flash-free). Assert with `stats`: a real hydration reports
`created: 0, removed: 0`.
- `getMountedElement(container: Element): Element | undefined` — The root element currently mounted in `container`, if any.
- `unmount(container: Element): void` — Remove the mounted tree and forget the container.
- `createElement(vnode: VNode, namespace: string = SVG_NS): Element` — Build a fresh detached DOM element (deep) for a VNode.
- `patchElement(el: Element, oldVNode: VNode, newVNode: VNode): Element` — Diff two VNodes onto an existing element.
- `patchProps( el: Element, oldProps: Record<string, any>, newProps: Record<string, any> ): void` — Apply a prop delta to an element (no children touched).

## Constants

### `defaultPatcher`

Process-wide default patcher — convenient for the common "one DOM, one tree"
case. Instantiate `VNodePatcher` directly for isolated instances.

```ts
const defaultPatcher: VNodePatcher
```

### `SVG_NS`

SVG namespace — everything outside a foreignObject is created here.

```ts
const SVG_NS: "http://www.w3.org/2000/svg"
```

### `XHTML_NS`

XHTML namespace — foreignObject children are HTML, not SVG.

```ts
const XHTML_NS: "http://www.w3.org/1999/xhtml"
```

## Interfaces

### `ForeignObjectOptions`

Options for creating a foreignObject VNode

```ts
interface ForeignObjectOptions
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `nodeId` | `string` |  | Node ID - used to generate container ID |
| `x` | `number` |  | X coordinate (top-left corner) |
| `y` | `number` |  | Y coordinate (top-left corner) |
| `width` | `number` |  | Width in pixels |
| `height` | `number` |  | Height in pixels |
| `containerId?` | `string` |  | Optional custom container ID If not provided, will be auto-generated using ContainerIdGenerator |
| `children?` | `VNode[]` |  | Optional children VNodes If not provided, creates a default XHTML div wrapper |
| `key?` | `string` |  | Optional key for React/Angular diffing optimization |

### `PatchStats`

Per-reconcile work counters. Reset at the top of every `reconcile()` call. Useful as a cheap regression guard: a steady-state frame should create ~0
elements ("no teardown-and-rebuild").

```ts
interface PatchStats
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `created` | `number` |  | DOM nodes created from scratch. |
| `reused` | `number` |  | DOM nodes reused in place (patched, not recreated). |
| `moved` | `number` |  | Reused DOM nodes that had to move to a new sibling index. |
| `removed` | `number` |  | DOM nodes removed because their VNode disappeared. |
| `skipped` | `number` |  | Subtrees skipped entirely because the VNode object was identical. |

### `VNodePatcherOptions`

Options for a patcher instance.

```ts
interface VNodePatcherOptions
```

**Properties**

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `document?` | `Document` |  | Document used to create nodes. Defaults to the ambient `globalThis.document`. Pass one explicitly for headless / SSR / multi-document use. |

## Types

### `VNodeChild`

A child slot in a VNode tree. Strings/numbers materialise as text nodes.

```ts
type VNodeChild = VNode | string | number | null | undefined;
```

**Members**

- `toString(): string` — Returns a string representation of a string.
- `valueOf(): string` — Returns the primitive value of the specified object.
- `toLocaleString(): string` — Returns a date converted to a string using the current locale.
