@vielzeug/codex 1.0.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -107
- package/data/catalog.json +1679 -0
- package/data/llms-full.txt +18826 -31876
- package/data/llms.txt +32 -114
- package/data/manifest.json +8 -0
- package/data/packages/arsenal.json +210 -0
- package/data/packages/assay.json +40 -0
- package/data/packages/clockwork.json +67 -0
- package/data/packages/codex.json +43 -0
- package/data/packages/coins.json +103 -0
- package/data/packages/conduit.json +60 -0
- package/data/packages/courier.json +58 -0
- package/data/packages/dnd.json +75 -0
- package/data/packages/familiar.json +30 -0
- package/data/packages/flux.json +93 -0
- package/data/packages/forge.json +84 -0
- package/data/packages/herald.json +122 -0
- package/data/packages/keymap.json +65 -0
- package/data/packages/ledger.json +54 -0
- package/data/packages/lingua.json +66 -0
- package/data/packages/orbit.json +112 -0
- package/data/packages/ore.json +73 -0
- package/data/packages/prism.json +70 -0
- package/data/packages/pulse.json +58 -0
- package/data/packages/refine.json +12 -0
- package/data/packages/ripple.json +79 -0
- package/data/packages/rune.json +81 -0
- package/data/packages/sandbox.json +39 -0
- package/data/packages/scout.json +60 -0
- package/data/packages/scroll.json +113 -0
- package/data/packages/sourcerer.json +74 -0
- package/data/packages/spell.json +134 -0
- package/data/packages/tempo.json +113 -0
- package/data/packages/vault.json +90 -0
- package/data/packages/ward.json +125 -0
- package/data/packages/wayfinder.json +113 -0
- package/data/refine.json +11752 -0
- package/data/search.json +1437 -0
- package/dist/catalog.js +149 -0
- package/dist/catalog.js.map +1 -0
- package/dist/cli.js +33 -59
- package/dist/cli.js.map +1 -1
- package/dist/errors.js +0 -14
- package/dist/errors.js.map +1 -1
- package/dist/http.js +54 -96
- package/dist/http.js.map +1 -1
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/server.js +4 -9
- package/dist/server.js.map +1 -1
- package/dist/snapshot.js +233 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/tools/index.js +21 -42
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/packages.js +67 -166
- package/dist/tools/packages.js.map +1 -1
- package/dist/tools/refine.js +99 -305
- package/dist/tools/refine.js.map +1 -1
- package/dist/tools/schema.js +8 -8
- package/dist/tools/schema.js.map +1 -1
- package/dist/tools/shared.js +1 -26
- package/dist/tools/shared.js.map +1 -1
- package/dist/types.js +1 -2
- package/dist/types.js.map +1 -1
- package/mcp-setup.json +10 -0
- package/package.json +7 -7
- package/data/.cache.json +0 -34
- package/data/vielzeug-data.json +0 -16118
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"apiSource": "// Public API — all exports for @vielzeug/prism\n\nexport type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';\n\n// Error classes\nexport { PrismDisposedError, PrismError, PrismRenderError } from './errors';\n\n// Chart factories\nexport { createAreaChart } from './charts/area';\nexport { createBarChart } from './charts/bar';\nexport { createLineChart } from './charts/line';\nexport { createPieChart } from './charts/pie';\nexport { createSparkline } from './charts/sparkline';\n\n// Scale factories\nexport { bandScale } from './scales/band';\nexport { linearScale } from './scales/linear';\nexport { timeScale } from './scales/time';\n\n// Animation utilities (for plugin authors)\nexport { animate } from './animation/transition';\nexport type { AnimationTarget } from './animation/transition';\nexport type { EasingFn } from './animation/easing';\n\n// Interaction types (useful for plugin authors)\nexport type { LegendState } from './interaction/legend';\nexport type { TooltipState } from './interaction/tooltip';\n\n// SVG primitives (for plugin authors)\nexport type { Point } from './svg/path';\n\n// Scaffold types (for authors building a custom chart factory on `createChartScaffold`/\n// `createRadialScaffold` — not the type passed to `ChartPlugin.install()`, see `ChartPluginContext` for that)\nexport type { ChartEventHandlers, RadialScaffoldContext, ScaffoldContext, ScaffoldGroups } from './core/chart-scaffold';\n\n// Theme utilities\nexport { resetTheme, seriesColor, setTheme } from './theme';\n",
|
|
3
|
+
"docs": {
|
|
4
|
+
"index": "---\ntitle: Prism — Reactive SVG data visualization\ndescription: Reactive SVG charting library — line, bar, and area charts. Signal-driven updates, CSS-themeable, accessible.\npackage: prism\ncategory: ui\nkeywords: [chart, svg, visualization, reactive, line-chart, bar-chart, area-chart, signals, typescript]\nrelated: [ripple, refine, orbit]\nexports:\n [\n createLineChart,\n createBarChart,\n createAreaChart,\n createPieChart,\n createSparkline,\n linearScale,\n timeScale,\n bandScale,\n seriesColor,\n setTheme,\n resetTheme,\n animate,\n PrismError,\n AnimationTarget,\n EasingFn,\n LegendState,\n TooltipState,\n ChartPluginContext,\n Point,\n ScaffoldContext,\n ScaffoldGroups,\n ChartEventHandlers,\n StackSegment,\n ]\nenvironments: [browser]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"prism\" />\n\n## Why Prism?\n\nCharting libraries typically require a framework binding, bundle heavy dependencies, or force canvas rendering that can't be styled with CSS. Prism takes a different approach:\n\n```ts\n// Before — Chart.js, imperative setup with a canvas you can't CSS-theme\nimport Chart from 'chart.js/auto';\nconst ctx = document.getElementById('myChart') as HTMLCanvasElement;\nnew Chart(ctx, {\n type: 'line',\n data: { labels, datasets: [{ data: values }] },\n // re-render manually when data changes, no signals, canvas not CSS-styleable\n});\n\n// After — Prism, declarative SVG chart driven by a signal\nimport { createLineChart } from '@vielzeug/prism';\nimport { signal } from '@vielzeug/ripple';\n\nconst data = signal([\n { key: 1, value: 12 },\n { key: 2, value: 40 },\n { key: 3, value: 28 },\n]);\nconst chart = createLineChart(document.getElementById('chart')!, {\n series: [{ name: 'Users', data }],\n tooltip: true,\n});\n// chart auto-updates when data.value changes — no manual re-render\ndata.value = [...data.value, { key: 4, value: 65 }];\n```\n\n| Feature | Prism | Chart.js | Lightweight Charts | D3 |\n| ------------------ | -------------------------------------------- | ---------------------------------------- | -------------------------------------------- | -------------------------------------------- |\n| Bundle size | <PackageInfo package=\"prism\" type=\"size\" /> | ~60 kB | ~45 kB | ~30 kB (core) |\n| Renderer | SVG | Canvas | Canvas | SVG/Canvas |\n| Zero dependencies | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| CSS themeable | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | Limited | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| Reactive (signals) | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Accessible SVG | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | Manual |\n| TypeScript-first | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Partial | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Types available |\n\n<div class=\"decision-callout\">\n\n**Use Prism when** you need lightweight, reactive charts that integrate with signal-based state and can be styled purely with CSS. Ideal for dashboards, admin panels, and data-heavy applications using Vielzeug.\n\n**Consider alternatives when** you need 50+ chart types (ECharts), financial trading charts (Lightweight Charts), or low-level visualization grammar (D3).\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/prism\n```\n\n```sh [npm]\nnpm install @vielzeug/prism\n```\n\n```sh [yarn]\nyarn add @vielzeug/prism\n```\n\n:::\n\n## Quick Start\n\n```ts\nimport { createLineChart } from '@vielzeug/prism';\nimport { signal } from '@vielzeug/ripple';\nimport '@vielzeug/prism/theme';\n\nconst data = signal([\n { key: 1, value: 10 },\n { key: 2, value: 25 },\n { key: 3, value: 18 },\n { key: 4, value: 32 },\n]);\n\nconst chart = createLineChart(document.getElementById('chart')!, {\n series: [{ name: 'Revenue', data, color: '#3b82f6' }],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n tooltip: true,\n crosshair: true,\n onHover: (event) => console.log(event?.datum),\n});\n\n// Update data → chart re-renders automatically\ndata.value = [...data.value, { key: 5, value: 28 }];\n\n// Cleanup when done\nchart.dispose();\n```\n\n## Features\n\n<div class=\"features-grid\">\n\n- **`createLineChart(container, config)`** — line chart with linear, monotone, or step interpolation\n- **`createBarChart(container, config)`** — bar chart with four layout variants: grouped, stacked, grouped-horizontal, stacked-horizontal\n- **`createAreaChart(container, config)`** — filled area with configurable opacity\n- **`createSparkline(container, config)`** — minimal inline sparkline (line, area, or bar variant)\n- **`createPieChart(container, config)`** — pie, donut, or semi-circle donut chart\n- **`linearScale(config)`** — continuous numeric scale with nice tick generation\n- **`timeScale(config)`** — date/time scale with interval-based ticks\n- **`bandScale(config)`** — categorical scale for bar charts\n- **`MaybeSignal<T>`** — pass plain values or `@vielzeug/ripple` signals; both work seamlessly\n- **`seriesColor(index, override?)`** — resolve CSS palette color by series index\n- **`setTheme(theme)` / `resetTheme()`** — apply or clear custom colors, font, and grid tokens at runtime\n- **Event hooks** — `onClick` and `onHover` callbacks on every chart\n- **Plugin system** — extend charts with `ChartPlugin` (`install()`/`dispose()` lifecycle, each isolated from the other's failures); supported by all chart types including `createPieChart`\n- **Devtools** — `debugChart()` from `@vielzeug/prism/devtools` logs mount/resize/dispose to `console.debug`; tree-shaken from production unless imported\n- **CSS custom properties** — full theme control via `--prism-*` tokens\n- **Responsive** — auto-resizes via `ResizeObserver`\n- **Accessible** — ARIA labels and semantic SVG structure\n- **`Symbol.dispose`** — explicit resource management following TC39 proposal\n\n</div>\n\n## Sub-paths\n\n| Import | Purpose |\n| -------------------------- | ------------------------------------------------------------------------------------ |\n| `@vielzeug/prism` | All chart factories, scales, and types |\n| `@vielzeug/prism/theme` | Default CSS (custom properties + dark mode) |\n| `@vielzeug/prism/devtools` | `debugChart()` — opt-in `console.debug` lifecycle logging, tree-shaken in production |\n\n## Documentation\n\n<div class=\"doc-links\">\n\n- [Usage Guide](./usage.md)\n- [API Reference](./api.md)\n- [Examples](./examples.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Ripple](/ripple/) — reactive signals that power Prism's auto-updating charts\n- [Refine](/refine/) — accessible web components that pair well with Prism for dashboards\n- [Orbit](/orbit/) — floating element positioning for chart tooltips and popovers\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
|
|
5
|
+
"api": "---\ntitle: Prism — API Reference\ndescription: Complete type signatures, parameter docs, and return values for every export in @vielzeug/prism.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Returns |\n| -------------------- | ----------------------------------------------------- | ------------------------------ |\n| `createLineChart()` | Reactive line chart with curves and interpolation | `ChartHandle` |\n| `createBarChart()` | Bar chart: grouped, stacked, horizontal variants | `ChartHandle` |\n| `createAreaChart()` | Filled area chart | `ChartHandle` |\n| `linearScale()` | Continuous numeric → pixel scale | `Scale<number>` |\n| `timeScale()` | Date → pixel scale | `Scale<Date>` |\n| `bandScale()` | Categorical → pixel band scale | `BandScale` |\n| `createSparkline()` | Minimal inline sparkline (line/area/bar) | `ChartHandle` |\n| `createPieChart()` | Pie, donut, or semi-circle donut chart | `ChartHandle` |\n| `seriesColor()` | CSS variable color for series index | `string` |\n| `setTheme()` | Apply custom palette / CSS tokens at runtime | `void` |\n| `resetTheme()` | Clear all custom theme overrides back to defaults | `void` |\n| `animate()` | Animate SVG element attributes via RAF | `() => void` (cancel function) |\n| `debugChart()` | Wrap a `ChartHandle` with lifecycle logging (`/devtools` subpath) | `ChartHandle` |\n| `PrismError` | Base class for all prism-originated errors | class |\n| `LegendState` | Live legend state object (plugin API) | type |\n| `TooltipState` | Live tooltip state object (plugin API) | type |\n| `ChartPluginContext` | Context object passed to `ChartPlugin.install()` | type |\n\n## Package Entry Points\n\n| Import | Purpose |\n| -------------------------- | --------------------------------------------------------------------------- |\n| `@vielzeug/prism` | All chart factories, scales, types, and utilities |\n| `@vielzeug/prism/theme` | Default CSS custom properties (light + dark) |\n| `@vielzeug/prism/devtools` | `debugChart()` — opt-in `console.debug` lifecycle logging, tree-shaken in production |\n\n---\n\n## Chart Factories\n\n### `createLineChart`\n\n```ts\nfunction createLineChart(container: HTMLElement, config: LineChartConfig): ChartHandle;\n```\n\nCreates a reactive line chart. Supports multiple series, curve interpolation, tooltips, crosshair, and event hooks.\n\n| Parameter | Type | Description |\n| ----------- | ----------------- | --------------------------------------------------- |\n| `container` | `HTMLElement` | DOM element to render into (must have width/height) |\n| `config` | `LineChartConfig` | Chart configuration |\n\n**Returns** — [`ChartHandle`](#charthandle)\n\n---\n\n### `createBarChart`\n\n```ts\nfunction createBarChart(container: HTMLElement, config: BarChartConfig): ChartHandle;\n```\n\nCreates a reactive bar chart. Use `variant` to switch between grouped, stacked, horizontal variants.\n\n| Parameter | Type | Description |\n| ----------- | ---------------- | -------------------------- |\n| `container` | `HTMLElement` | DOM element to render into |\n| `config` | `BarChartConfig` | Chart configuration |\n\n**Returns** — [`ChartHandle`](#charthandle)\n\n---\n\n### `createAreaChart`\n\n```ts\nfunction createAreaChart(container: HTMLElement, config: AreaChartConfig): ChartHandle;\n```\n\nCreates a reactive filled area chart with configurable opacity, curve, and event hooks.\n\n| Parameter | Type | Description |\n| ----------- | ----------------- | -------------------------- |\n| `container` | `HTMLElement` | DOM element to render into |\n| `config` | `AreaChartConfig` | Chart configuration |\n\n**Returns** — [`ChartHandle`](#charthandle)\n\n---\n\n### `createPieChart`\n\n```ts\nfunction createPieChart(container: HTMLElement, config: PieChartConfig): ChartHandle;\n```\n\nCreates a pie, donut, or semi-circle donut chart. All three variants share the same `PieChartConfig` — select via `variant`.\n\n| Parameter | Type | Description |\n| ----------- | ---------------- | ----------------------------------------- |\n| `container` | `HTMLElement` | DOM element to render into (sized by CSS) |\n| `config` | `PieChartConfig` | Chart configuration |\n\n**Returns** — [`ChartHandle`](#charthandle)\n\n---\n\n### `createSparkline`\n\n```ts\nfunction createSparkline(container: HTMLElement, config: SparklineConfig): ChartHandle;\n```\n\nCreates a minimal inline chart with no axes, no legend, and no margin. Designed for use in tables, cards, and inline data contexts.\n\n| Parameter | Type | Description |\n| ----------- | ----------------- | ----------------------------------------- |\n| `container` | `HTMLElement` | DOM element to render into (sized by CSS) |\n| `config` | `SparklineConfig` | Sparkline configuration |\n\n**Returns** — [`ChartHandle`](#charthandle)\n\n---\n\n## Scale Factories\n\n### `linearScale`\n\n```ts\nfunction linearScale(config: LinearScaleConfig): Scale<number>;\n```\n\nContinuous linear scale mapping a numeric domain to a pixel range. Unlike chart config fields, scale factory config is not `MaybeSignal` — pass plain values and call `linearScale()` again if the domain/range changes.\n\n| Field | Type | Default | Description |\n| --------------- | ------------------ | ------- | ----------------------------------------------------------------- |\n| `config.domain` | `[number, number]` | — | Input data range `[min, max]`. A reversed domain (`min > max`) is supported for inverted axes. |\n| `config.range` | `[number, number]` | — | Output pixel range `[min, max]` |\n| `config.nice` | `boolean` | `true` | Extend domain to nice round numbers |\n| `config.clamp` | `boolean` | `false` | Clamp output to range bounds |\n\n---\n\n### `timeScale`\n\n```ts\nfunction timeScale(config: TimeScaleConfig): Scale<Date>;\n```\n\nTime scale mapping `Date` values to pixels. Automatically selects tick intervals (seconds → years).\n\n| Field | Type | Default | Description |\n| --------------- | ----------------- | ------- | -------------------------------- |\n| `config.domain` | `[Date, Date]` | — | Input date range `[start, end]` |\n| `config.range` | `[number, number]` | — | Output pixel range |\n| `config.nice` | `boolean` | `true` | Extend domain to nice boundaries |\n\n---\n\n### `bandScale`\n\n```ts\nfunction bandScale(config: BandScaleConfig): BandScale;\n```\n\nCategorical scale dividing the range into equal bands with configurable padding.\n\n| Field | Type | Default | Description |\n| --------------------- | ------------------ | ----------------- | ------------------------- |\n| `config.domain` | `string[]` | — | Category names |\n| `config.range` | `[number, number]` | — | Output pixel range |\n| `config.padding` | `number` | `0.1` | Inner padding ratio (0–1) |\n| `config.paddingOuter` | `number` | same as `padding` | Outer edge padding ratio |\n\n---\n\n## Types\n\n### `ChartHandle`\n\nReturned by all chart factories.\n\n```ts\ninterface ChartHandle {\n readonly disposalSignal: AbortSignal;\n readonly disposed: boolean;\n readonly el: SVGSVGElement;\n dispose(): void;\n [Symbol.dispose](): void;\n}\n```\n\n| Member | Description |\n| -------------------- | ----------------------------------------------------------------------------------------------- |\n| `el` | The root `SVGSVGElement` (for styling or external manipulation) |\n| `disposed` | `true` once `dispose()` has run; useful for guarding late callbacks |\n| `disposalSignal` | Aborted when the chart is disposed — tie your own cleanup (RAF loops, observers) to this instead of overriding `dispose()` |\n| `dispose()` | Tear down all effects, observers, DOM nodes, tooltip, and legend. Calling it more than once is a no-op |\n| `[Symbol.dispose]()` | Same as `dispose()` — for TC39 `using` declarations |\n\n> **Note:** Charts re-render automatically when signal data changes. There is no `update()` method — reactivity is fully automatic.\n\n---\n\n### `ChartEvent`\n\nPassed to `onClick` and `onHover` callbacks.\n\n```ts\ninterface ChartEvent {\n datum: Datum;\n originalEvent: MouseEvent;\n series: Series;\n}\n```\n\n---\n\n### `ChartPlugin`\n\nInterface for extending charts with custom behavior. Plugins are installed after the chart is mounted and torn down on `dispose()`.\n\n```ts\ninterface ChartPlugin {\n install(ctx: ChartPluginContext): void;\n dispose(): void;\n}\n```\n\nSee [`ChartPluginContext`](#chartplugincontext) for the object passed to `install()`.\n\n---\n\n### `BaseChartConfig`\n\nShared configuration inherited by all chart config types.\n\n```ts\ninterface BaseChartConfig {\n ariaLabel?: string;\n legend?: boolean | LegendConfig;\n margin?: Partial<ChartMargin>;\n onClick?: (event: ChartEvent) => void;\n onHover?: (event: ChartEvent | null) => void;\n plugins?: ChartPlugin[];\n tooltip?: boolean | TooltipConfig;\n transition?: TransitionConfig;\n xAxis?: AxisConfig;\n yAxis?: AxisConfig;\n}\n```\n\n| Field | Type | Description |\n| ------------ | ------------------------------------- | --------------------------------------- |\n| `ariaLabel` | `string` | Accessible label on the SVG element |\n| `legend` | `boolean \\| LegendConfig` | Show a series legend |\n| `margin` | `Partial<ChartMargin>` | Override chart margins |\n| `onClick` | `(event: ChartEvent) => void` | Fired when a data point is clicked |\n| `onHover` | `(event: ChartEvent \\| null) => void` | Fired on mousemove (null on mouseleave) |\n| `plugins` | `ChartPlugin[]` | Extension plugins installed at mount |\n| `tooltip` | `boolean \\| TooltipConfig` | Hover tooltip |\n| `transition` | `TransitionConfig` | Enter/update animation |\n| `xAxis` | `AxisConfig` | X-axis configuration |\n| `yAxis` | `AxisConfig` | Y-axis configuration |\n\n---\n\n### `MaybeSignal<T>`\n\n```ts\ntype MaybeSignal<T> = Readable<T> | T;\n```\n\nAccepts either a plain value or a `@vielzeug/ripple` `Readable<T>` signal (e.g. one created with `signal()`). Used for `series`/`data` fields on chart configs — when a signal is passed, the chart re-renders automatically on `.value` changes. Not used by the scale factories (`linearScale`/`timeScale`/`bandScale`), whose config fields are always plain values.\n\n---\n\n### `Scale<T>`\n\n```ts\ninterface Scale<T> {\n readonly domain: readonly [T, T];\n readonly range: readonly [number, number];\n map(value: T): number;\n invert(pixel: number): T;\n ticks(count?: number): T[];\n}\n```\n\n| Member | Description |\n| --------------- | --------------------------------------------------- |\n| `domain` | Input domain `[min, max]` — readonly computed tuple |\n| `range` | Output pixel range — readonly computed tuple |\n| `map(value)` | Domain value → pixel position |\n| `invert(pixel)` | Pixel position → domain value |\n| `ticks(count?)` | Nicely-spaced tick values (default: 10) |\n\n---\n\n### `BandScale`\n\n```ts\ninterface BandScale {\n readonly domain: readonly string[];\n readonly range: readonly [number, number];\n map(value: string): number;\n bandwidth(): number;\n gap(): number;\n ticks(count?: number): string[];\n}\n```\n\n| Member | Description |\n| --------------- | --------------------------------------------------------------- |\n| `map(value)` | Left edge pixel position of a category's band |\n| `bandwidth()` | Width of each band in pixels |\n| `gap()` | Pixel gap between adjacent bands (`bandwidth × padding`) |\n| `ticks(count?)` | All domain categories, or at most `count` evenly sampled values |\n\n---\n\n### `Point`\n\n```ts\ninterface Point {\n x: number;\n y: number;\n}\n```\n\nA pixel-space 2D point used by path builders and area renderers. Exported for plugin authors who build custom SVG paths.\n\n---\n\n### `Datum`\n\nA single data point in a cartesian chart series.\n\n```ts\ninterface Datum {\n key: Date | number | string;\n value: number;\n meta?: Record<string, unknown>;\n}\n```\n\n| Field | Type | Description |\n| ------- | -------------------------- | ----------------------------------------------------------------------------------------- |\n| `key` | `Date \\| number \\| string` | X-axis identity. Use `number` or `Date` for line/area charts; `string` for bar categories |\n| `value` | `number` | Y-axis measured quantity |\n| `meta` | `Record<string, unknown>` | Optional arbitrary metadata (available in tooltip `render` callbacks) |\n\n---\n\n### `Series`\n\n```ts\ninterface Series {\n name: string;\n data: MaybeSignal<Datum[]>;\n color?: string;\n}\n```\n\n---\n\n### `ScaffoldContext`\n\nPassed to `renderFn` inside `createChartScaffold` — the internal building block behind `createLineChart`/`createBarChart`/`createAreaChart`. Relevant only if you're building a custom cartesian chart type on top of prism's scaffold, not to `ChartPlugin.install()` (see [`ChartPluginContext`](#chartplugincontext) for that).\n\n```ts\ninterface ScaffoldContext {\n chartArea: SVGGElement;\n container: HTMLElement;\n dimensions: Readable<ChartDimensions>;\n disposalSignal: AbortSignal;\n groups: ScaffoldGroups;\n legend: LegendState | null;\n svg: SVGSVGElement;\n tooltip: TooltipState | null;\n}\n```\n\n---\n\n### `RadialScaffoldContext`\n\nThe `createRadialScaffold` counterpart to `ScaffoldContext`, for chart types with no cartesian axis groups (pie, donut, semi). Backs `createPieChart`.\n\n```ts\ninterface RadialScaffoldContext {\n container: HTMLElement;\n dimensions: Readable<ChartDimensions>;\n disposalSignal: AbortSignal;\n legend: LegendState | null;\n svg: SVGSVGElement;\n tooltip: TooltipState | null;\n}\n```\n\n---\n\n### `ScaffoldGroups`\n\n```ts\ninterface ScaffoldGroups {\n grid: SVGGElement;\n series: SVGGElement;\n xAxis: SVGGElement;\n yAxis: SVGGElement;\n}\n```\n\nSVG `<g>` elements created by `createChartScaffold`. Children of `chartArea`, appended in render order: `grid` → `xAxis` → `yAxis` → `series`.\n\n---\n\n### `ChartEventHandlers`\n\n```ts\ninterface ChartEventHandlers {\n onClick?: (event: MouseEvent) => void;\n onMouseLeave?: (event: MouseEvent) => void;\n onMouseMove?: (event: MouseEvent) => void;\n}\n```\n\nReturned by the `renderFn` passed to `createChartScaffold`. The scaffold attaches and tears down these listeners automatically before each re-render.\n\n---\n\n### `AnimationTarget`\n\n```ts\ninterface AnimationTarget {\n attrs: Record<string, { from: number; to: number }>;\n el: SVGElement;\n}\n```\n\nOne element + attribute map for use with `animate()`. Each attribute entry specifies the start (`from`) and end (`to`) pixel value.\n\n---\n\n## Pie / Donut Types\n\n### `PieChartConfig`\n\nExtends [`BaseChartConfig`](#basechartconfig) (inherits `ariaLabel`, `legend`, `margin`, `plugins`, `tooltip`, `transition`). Overrides `onClick`/`onHover` with pie-specific slice signatures.\n\n```ts\ninterface PieChartConfig extends Omit<BaseChartConfig, 'onClick' | 'onHover' | 'xAxis' | 'yAxis'> {\n cornerRadius?: number;\n data: MaybeSignal<PieSliceConfig[]>;\n innerRadius?: number;\n onClick?: (slice: PieSliceConfig, index: number) => void;\n onHover?: (slice: PieSliceConfig | null, index: number | null) => void;\n padPixels?: number;\n variant?: PieVariant;\n}\n```\n\n| Field | Type | Default | Description |\n| -------------- | ------------------------------------ | -------------------------------------- | ------------------------------------------------------- |\n| `data` | `MaybeSignal<PieSliceConfig[]>` | — | Slice definitions |\n| `variant` | `PieVariant` | `'pie'` | Chart style: `'pie'`, `'donut'`, or `'semi'` |\n| `innerRadius` | `number` | `55%` of outer (donut/semi), `0` (pie) | Inner hole radius in pixels |\n| `padPixels` | `number` | `0` (pie), `8` (donut/semi) | Pixel gap between slices (uniform across arc thickness) |\n| `cornerRadius` | `number` | `0` (pie), `8` (donut/semi) | Rounded arc corners (pixels) |\n| `onClick` | `(slice, index) => void` | — | Fired on slice click |\n| `onHover` | `(slice\\|null, index\\|null) => void` | — | Fired on hover; `null` on mouseleave |\n\n> Inherited `BaseChartConfig` fields (`tooltip`, `transition`, `legend`, `margin`, `ariaLabel`, `plugins`) behave identically to other chart types.\n\n### `PieSliceConfig`\n\n```ts\ninterface PieSliceConfig {\n color?: string;\n label?: string;\n value: number;\n}\n```\n\n| Field | Type | Description |\n| ------- | -------- | ------------------------------------------------- |\n| `value` | `number` | Numeric weight of the slice |\n| `color` | `string` | Slice fill color; defaults to `--prism-color-{n}` |\n| `label` | `string` | Optional text rendered at the arc centroid |\n\n### `PieVariant`\n\n```ts\ntype PieVariant = 'donut' | 'pie' | 'semi';\n```\n\n- **`pie`** — full circle, no hole\n- **`donut`** — full circle with inner hole (~55% of outer radius by default)\n- **`semi`** — top-half semicircle with inner hole (useful for gauges/progress)\n\n---\n\n## Sparkline Types\n\n### `SparklineConfig`\n\n```ts\ninterface SparklineConfig {\n ariaLabel?: string;\n color?: string;\n cornerRadius?: number;\n curve?: 'linear' | 'monotone' | 'step';\n data: MaybeSignal<number[] | StackSegment[]>;\n fillOpacity?: number;\n onClick?: (index: number, value: number) => void;\n onHover?: (index: number | null, value: number | null) => void;\n padPixels?: number;\n strokeWidth?: number;\n transition?: TransitionConfig;\n variant?: SparklineVariant;\n}\n```\n\n| Field | Type | Default | Description |\n| -------------- | ----------------------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------- |\n| `data` | `MaybeSignal<number[] \\| StackSegment[]>` | — | Numeric values, or `StackSegment[]` for `'stack'` variant |\n| `variant` | `SparklineVariant` | `'line'` | Chart style |\n| `color` | `string` | `var(--prism-color-1)` | Stroke/fill color (line/area/bar only) |\n| `curve` | `'linear' \\| 'monotone' \\| 'step'` | `'linear'` | Line interpolation (line/area only) |\n| `strokeWidth` | `number` | `1.5` | Line stroke width (line/area only) |\n| `fillOpacity` | `number` | `0.2` | Fill opacity (area only) |\n| `cornerRadius` | `number` | `4` | Rounded corners for stack segments in pixels. Stack variant only — no effect on line/area/bar |\n| `padPixels` | `number` | `0` | Gap between stack segments in pixels. Stack variant only — no effect on line/area/bar |\n| `ariaLabel` | `string` | — | Accessible label; sets `role=\"img\"` on the SVG. If omitted the SVG is marked `aria-hidden=\"true\"` (decorative) |\n| `transition` | `TransitionConfig` | — | Enter animation (bar/stack only; line/area use RAF interpolation) |\n| `onClick` | `(index, value) => void` | — | Called on click with nearest data index. Not fired for 0- or 1-point data |\n| `onHover` | `(index\\|null, value\\|null) => void` | — | Called on mousemove; `null` on mouseleave. Not fired for 0- or 1-point data |\n\n### `SparklineVariant`\n\n```ts\ntype SparklineVariant = 'area' | 'bar' | 'line' | 'stack';\n```\n\n- **`line`** — polyline path (default)\n- **`area`** — filled area + line overlay\n- **`bar`** — vertical bar per data point\n- **`stack`** — horizontal proportional segments; use `StackSegment[]` for `data` with per-segment colors\n\n### `StackSegment`\n\n```ts\ninterface StackSegment {\n color?: string;\n label?: string;\n value: number;\n}\n```\n\n> **Accessibility:** Without `ariaLabel` the SVG is marked `aria-hidden=\"true\"` (decorative). Set `ariaLabel` to expose the chart to assistive technology — the SVG will carry `role=\"img\"` and the provided label.\n\n---\n\n## Chart Config Types\n\n### `LineChartConfig`\n\nExtends [`BaseChartConfig`](#basechartconfig).\n\n```ts\ninterface LineChartConfig extends BaseChartConfig {\n series: MaybeSignal<LineSeriesConfig[]>;\n crosshair?: boolean | CrosshairConfig;\n}\n```\n\n### `LineSeriesConfig`\n\n```ts\ninterface LineSeriesConfig extends Series {\n curve?: 'linear' | 'monotone' | 'step'; // default: 'linear'\n strokeWidth?: number; // default: 2\n showPoints?: boolean; // default: false\n pointRadius?: number; // default: 3\n}\n```\n\n---\n\n### `BarChartConfig`\n\nExtends [`BaseChartConfig`](#basechartconfig).\n\n```ts\ntype BarVariant =\n | 'grouped' // vertical grouped (default)\n | 'stacked' // vertical stacked\n | 'grouped-horizontal' // horizontal grouped\n | 'stacked-horizontal'; // horizontal stacked\n\ninterface BarChartConfig extends BaseChartConfig {\n series: MaybeSignal<BarSeriesConfig[]>;\n variant?: BarVariant; // default: 'grouped'\n}\n```\n\n### `BarSeriesConfig`\n\n```ts\ninterface BarSeriesConfig extends Series {\n borderRadius?: number; // default: 0\n}\n```\n\n---\n\n### `AreaChartConfig`\n\nExtends [`BaseChartConfig`](#basechartconfig).\n\n```ts\ninterface AreaChartConfig extends BaseChartConfig {\n series: MaybeSignal<AreaSeriesConfig[]>;\n crosshair?: boolean | CrosshairConfig;\n}\n```\n\n### `AreaSeriesConfig`\n\n```ts\ninterface AreaSeriesConfig extends Series {\n curve?: 'linear' | 'monotone' | 'step'; // default: 'linear'\n fillOpacity?: number; // default: 0.3\n showLine?: boolean; // default: true\n}\n```\n\n---\n\n## Shared Config Types\n\n### `AxisConfig`\n\n```ts\ninterface AxisConfig {\n position: 'top' | 'bottom' | 'left' | 'right';\n tickCount?: number;\n tickFormat?: (value: Date | number | string) => string;\n label?: string;\n grid?: boolean | GridConfig;\n}\n```\n\n### `GridConfig`\n\n```ts\ninterface GridConfig {\n color?: string;\n dash?: string; // SVG stroke-dasharray value, e.g. '4 2'\n}\n```\n\n### `TooltipConfig`\n\n```ts\ninterface TooltipConfig {\n offset?: number; // default: 8\n render?: (datum: Datum, series: Series) => string; // returns HTML string\n sanitize?: (html: string) => string; // applied before innerHTML injection\n}\n```\n\nThe tooltip is appended inside the chart container (not `document.body`), so it is automatically scoped and cleaned up on `dispose()`.\n\n> ⚠️ **Security:** The string returned by `render` is injected via `innerHTML`. Pass `sanitize` to apply a sanitizer (e.g. DOMPurify) before injection, or ensure all user-supplied values are escaped before interpolation. A `warn` is emitted in development when `render` is set without `sanitize`.\n\n### `CrosshairConfig`\n\n```ts\ninterface CrosshairConfig {\n vertical?: boolean; // default: true\n horizontal?: boolean; // default: false\n snap?: boolean; // default: true\n}\n```\n\n### `LegendConfig`\n\n```ts\ninterface LegendConfig {\n position?: 'top' | 'bottom' | 'left' | 'right'; // default: 'bottom'\n}\n```\n\n### `TransitionConfig`\n\n```ts\ninterface TransitionConfig {\n duration?: number; // ms, default: 300\n easing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out' | ((t: number) => number);\n stagger?: number; // ms delay between bar enter animations, default: 0\n}\n```\n\n> **`stagger`** applies only to bar chart enter animations — new bars grow in sequence with a `stagger`ms delay between each one.\n\n### `ChartMargin`\n\n```ts\ninterface ChartMargin {\n top: number; // default: 20\n right: number; // default: 20\n bottom: number; // default: 40\n left: number; // default: 50\n}\n```\n\n---\n\n## Utilities\n\n### `seriesColor`\n\n```ts\nfunction seriesColor(index: number, override?: string): string;\n```\n\nReturns the CSS variable reference for palette color at `index` (wraps at 8). If `override` is provided it is returned as-is. Used internally by all chart factories.\n\n```ts\nimport { seriesColor } from '@vielzeug/prism';\n\nseriesColor(0); // 'var(--prism-color-1)'\nseriesColor(0, '#ff0'); // '#ff0'\n```\n\n### `setTheme`\n\n```ts\ninterface PrismTheme {\n colors?: string[]; // replaces --prism-color-1 … -8\n fontFamily?: string; // sets --prism-font-family\n gridColor?: string; // sets --prism-grid-color\n gridOpacity?: number; // sets --prism-grid-opacity\n}\n\nfunction setTheme(theme: PrismTheme): void;\n```\n\nApplies CSS custom properties to `document.documentElement`. Call once at app startup before mounting charts. Setting `colors` clears any unset color slots left over from a previous `setTheme()` call, so a theme with fewer colors than the last one doesn't leave stale high-index colors behind.\n\n```ts\nimport { setTheme } from '@vielzeug/prism';\n\nsetTheme({ colors: ['#6366f1', '#22d3ee', '#f59e0b', '#10b981'] });\n```\n\n### `resetTheme`\n\n```ts\nfunction resetTheme(): void;\n```\n\nClears every CSS custom property `setTheme()` can set, restoring prism's default theme (from `@vielzeug/prism/theme`). Useful for test teardown or a theme-switcher's \"reset to default\" action.\n\n```ts\nimport { resetTheme, setTheme } from '@vielzeug/prism';\n\nsetTheme({ colors: ['#6366f1'] });\nresetTheme(); // back to the default palette\n```\n\n> `seriesColor`, `setTheme`, and `resetTheme` are all exported from `@vielzeug/prism` (not from the `/theme` CSS subpath).\n\n---\n\n## Interaction Types\n\n> Exported from `@vielzeug/prism` for use in plugins and custom chart extensions. Both types reflect the live state object created internally; `el` is `null` when no legend/tooltip is configured.\n\n### `LegendState`\n\n```ts\ninterface LegendState {\n dispose(): void;\n [Symbol.dispose](): void;\n el: HTMLDivElement | null;\n update(series: { color: string; name: string }[]): void;\n}\n```\n\nThe live legend object available on `ctx.legend` inside `ChartPlugin.install`. Call `update()` to re-render legend items, `dispose()` to remove the element.\n\n### `TooltipState`\n\n```ts\ninterface TooltipState {\n dispose(): void;\n [Symbol.dispose](): void;\n el: HTMLElement | null;\n hide(): void;\n show(x: number, y: number, datum: Datum, series: Series): void;\n}\n```\n\nThe live tooltip object available on `ctx.tooltip` inside `ChartPlugin.install`. `x`/`y` are pixel coordinates relative to the chart area; `show()` positions and renders the tooltip.\n\n---\n\n### `ChartPluginContext`\n\n```ts\ninterface ChartPluginContext {\n container: HTMLElement;\n dimensions: Readable<ChartDimensions>;\n disposalSignal: AbortSignal;\n svg: SVGSVGElement;\n}\n```\n\nPassed to `ChartPlugin.install(ctx)`. Gives plugins access to the reactive `dimensions` signal, the host `container`, the root `svg` element, and a `disposalSignal` aborted when the chart is torn down.\n\n```ts\nimport type { ChartPlugin } from '@vielzeug/prism';\nimport { effect } from '@vielzeug/ripple';\n\nconst watermarkPlugin: ChartPlugin = {\n dispose() {},\n install(ctx) {\n // React to size changes\n effect(() => {\n const { width, height } = ctx.dimensions.value;\n /* re-layout watermark */\n });\n },\n};\n```\n\n> **Note:** To observe future resize events use `effect(() => { ctx.dimensions.value; })` from `@vielzeug/ripple` within a reactive scope. To run cleanup when the chart is disposed without relying on your own `dispose()` implementation being called, add a listener to `ctx.disposalSignal` instead: `ctx.disposalSignal.addEventListener('abort', cleanup)`.\n>\n> **Error isolation:** if a plugin's `install()` or `dispose()` throws, the error is logged (dev builds only) and the rest of the chart — and any other installed plugins — continues to work. A throwing plugin never aborts chart creation or teardown.\n\n---\n\n## Animation Utilities\n\n> Exported from `@vielzeug/prism` for use in plugins and custom chart extensions.\n\n### `animate`\n\n```ts\nfunction animate(\n targets: AnimationTarget[],\n config?: TransitionConfig,\n onComplete?: () => void,\n signal?: AbortSignal,\n): () => void;\n```\n\nAnimates SVG element attributes from `from` to `to` values over the given `TransitionConfig` duration. Calls `onComplete` when all animations finish. Returns a cancel function — call it to stop the in-flight animation early (its `requestAnimationFrame` loop is cancelled and `onComplete` is not called).\n\n- **Empty targets or `duration: 0`** — attributes are set immediately and `onComplete` is called synchronously; no RAF is scheduled. The returned cancel function is a no-op in this case.\n- **Negative `stagger`** — clamped to `0`; all elements animate in parallel.\n- **`signal`** — if provided and already aborted (or aborted mid-animation), the RAF loop stops rescheduling itself on its next frame, same effect as calling the returned cancel function.\n\n**Parameters — `AnimationTarget`:**\n\n| Field | Type | Description |\n| ------- | ---------------------------------------------- | --------------------------------- |\n| `el` | `SVGElement` | Target element |\n| `attrs` | `Record<string, { from: number; to: number }>` | Attribute name → start/end values |\n\n```ts\nimport { animate } from '@vielzeug/prism';\n\nconst cancel = animate([{ attrs: { opacity: { from: 0, to: 1 } }, el: rect }], { duration: 300, easing: 'ease-out' });\n\n// Stop early if the element is removed before the animation completes:\ncancel();\n```\n\n### `EasingFn`\n\n```ts\ntype EasingFn = (t: number) => number;\n```\n\nA custom easing function. Receives a normalised time value `t ∈ [0, 1]` and returns a progress value (also typically `[0, 1]`). Pass as `TransitionConfig.easing`. Unknown or invalid easing name strings fall back to `'ease-out'` rather than throwing.\n\n---\n\n## Devtools\n\n> **Import:** `@vielzeug/prism/devtools`\n\nOpt-in debug logging, separate from the internal dev-mode validation warnings in `_dev.ts` (those run automatically and need no import). Tree-shaken from production bundles when this sub-path isn't imported — there is no environment gate to configure.\n\n### `debugChart`\n\n```ts\ninterface DebugChartOptions {\n label?: string; // defaults to 'chart', producing log prefixes like [prism:chart]\n}\n\nfunction debugChart<T extends ChartHandle>(handle: T, options?: DebugChartOptions): T;\n```\n\nWraps an already-created `ChartHandle` with lifecycle logging to `console.debug`. Logs the chart's mount, every resize (via its own `ResizeObserver` on `handle.el`, independent of the chart's internal one), and disposal — each prefixed with `[prism:<label>]`. Returns the same handle unchanged, so it can wrap any `create*Chart()` call in place.\n\n```ts\nimport { createLineChart } from '@vielzeug/prism';\nimport { debugChart } from '@vielzeug/prism/devtools';\n\nconst chart = debugChart(createLineChart(container, config), { label: 'revenue' });\n// [prism:revenue] mounted\n// [prism:revenue] resized 600×300\nchart.dispose();\n// [prism:revenue] disposed\n```\n\n---\n\n## Errors\n\n### `PrismError`\n\nBase class for all prism errors. Use `instanceof PrismError` or `PrismError.is()` to catch any prism-originated error.\n\n```ts\nclass PrismError extends Error {\n static is(err: unknown): err is PrismError;\n}\n```\n\n**Named subclasses**\n\n| Class | Thrown when |\n| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `PrismRenderError` | A chart is given a structurally invalid configuration it cannot render at all (e.g. a non-`Element` `container`). Recoverable issues like empty or malformed data emit a dev-mode warning instead — they do not throw. |\n| `PrismDisposedError` | Reserved for future disposal-sensitive APIs on `ChartHandle`. No code path throws this yet — calling `dispose()` more than once is currently a documented no-op, not an error. |\n",
|
|
6
|
+
"usage": "---\ntitle: Prism — Usage Guide\ndescription: Concepts, patterns, and best practices for @vielzeug/prism — reactive SVG charts.\n---\n\n[[toc]]\n\n## Basic Usage\n\nEvery chart needs a container element with defined dimensions and the theme CSS:\n\n```ts\nimport { createLineChart } from '@vielzeug/prism';\nimport '@vielzeug/prism/theme';\n\nconst container = document.querySelector<HTMLElement>('#chart')!;\nconst chart = createLineChart(container, {\n series: [\n {\n name: 'Revenue',\n data: [\n { key: 1, value: 10 },\n { key: 2, value: 16 },\n ],\n },\n ],\n});\n\nchart.dispose();\n```\n\n```html\n<div id=\"chart\" style=\"width: 100%; height: 300px;\"></div>\n```\n\nPrism observes the container size via `ResizeObserver` and re-renders automatically on resize. If the container has zero dimensions at mount time, a `warn` is emitted in development — ensure the container has layout before calling the chart factory.\n\n## Reactivity with Signals\n\nPrism accepts both plain values and `@vielzeug/ripple` signals for any data property. When a signal changes, the chart re-renders automatically in the next animation frame.\n\n### Static Data\n\n```ts\nimport { createLineChart } from '@vielzeug/prism';\n\nconst chart = createLineChart(container, {\n series: [\n {\n name: 'Static',\n data: [\n { key: 1, value: 10 },\n { key: 2, value: 20 },\n ],\n },\n ],\n});\n```\n\n### Reactive Data\n\n```ts\nimport { createLineChart } from '@vielzeug/prism';\nimport { signal } from '@vielzeug/ripple';\n\nconst data = signal([\n { key: 1, value: 10 },\n { key: 2, value: 20 },\n]);\n\nconst chart = createLineChart(container, {\n series: [{ name: 'Live', data }],\n});\n\n// Later — chart updates automatically\ndata.value = [...data.value, { key: 3, value: 30 }];\n```\n\n### The `MaybeSignal<T>` Pattern\n\nAll data-bearing config fields use the `MaybeSignal<T>` type:\n\n```ts\ntype MaybeSignal<T> = Readable<T> | T;\n```\n\nPass a plain value when data is fixed, or a `@vielzeug/ripple` signal when it changes dynamically. The chart handles both identically.\n\n## Line Charts\n\n```ts\nimport { createLineChart } from '@vielzeug/prism';\n\nconst chart = createLineChart(container, {\n series: [\n {\n name: 'Revenue',\n data: [\n { key: 1, value: 100 },\n { key: 2, value: 150 },\n { key: 3, value: 130 },\n ],\n color: '#3b82f6',\n curve: 'monotone', // 'linear' | 'monotone' | 'step'\n strokeWidth: 2,\n showPoints: true,\n pointRadius: 4,\n },\n ],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n tooltip: true,\n crosshair: true,\n});\n```\n\n### Multiple Series\n\n```ts\nconst chart = createLineChart(container, {\n series: [\n { name: 'Revenue', data: revenueData, color: '#3b82f6' },\n { name: 'Expenses', data: expenseData, color: '#ef4444' },\n ],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n});\n```\n\n### Time-based X Axis\n\nWhen data points use `Date` objects for `key`, Prism automatically applies a time scale:\n\n```ts\nconst chart = createLineChart(container, {\n series: [\n {\n name: 'Signups',\n data: [\n { key: new Date('2024-01-01'), value: 50 },\n { key: new Date('2024-02-01'), value: 80 },\n { key: new Date('2024-03-01'), value: 120 },\n ],\n },\n ],\n xAxis: { position: 'bottom', tickFormat: (d) => (d as Date).toLocaleDateString() },\n yAxis: { position: 'left' },\n});\n```\n\n## Bar Charts\n\n```ts\nimport { createBarChart } from '@vielzeug/prism';\n\nconst chart = createBarChart(container, {\n series: [\n {\n name: 'Sales',\n data: [\n { key: 'Q1', value: 200 },\n { key: 'Q2', value: 350 },\n { key: 'Q3', value: 280 },\n { key: 'Q4', value: 400 },\n ],\n borderRadius: 4,\n },\n ],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n tooltip: true,\n});\n```\n\n### Variants\n\nSelect the bar layout with `variant`:\n\n| Value | Layout |\n| ---------------------- | -------------------------- |\n| `'grouped'` | Vertical grouped (default) |\n| `'stacked'` | Vertical stacked |\n| `'grouped-horizontal'` | Horizontal grouped |\n| `'stacked-horizontal'` | Horizontal stacked |\n\n> **Note:** `tooltip` and `legend` are always available on the scaffold — omitting them uses a no-op null-object internally, so no conditional checks are needed in plugins or custom render logic.\n\n```ts\nconst chart = createBarChart(container, {\n variant: 'stacked',\n series: [\n { name: 'Mobile', data: mobileData, color: '#3b82f6', borderRadius: 0 },\n { name: 'Desktop', data: desktopData, color: '#10b981', borderRadius: 0 },\n ],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n tooltip: true,\n legend: true,\n});\n```\n\nFor horizontal layouts, categories appear on the Y axis and values on the X axis:\n\n```ts\nconst chart = createBarChart(container, {\n variant: 'grouped-horizontal',\n series: [{ name: 'Revenue', data, color: '#3b82f6' }],\n xAxis: { position: 'bottom', grid: true },\n yAxis: { position: 'left' },\n});\n```\n\n### Grouped Bars\n\nMultiple series with `variant: 'grouped'` (default) render side-by-side:\n\n```ts\nconst chart = createBarChart(container, {\n series: [\n { name: '2023', data: lastYearData, color: '#94a3b8' },\n { name: '2024', data: thisYearData, color: '#3b82f6' },\n ],\n});\n```\n\n## Area Charts\n\n```ts\nimport { createAreaChart } from '@vielzeug/prism';\n\nconst chart = createAreaChart(container, {\n series: [\n {\n name: 'Users',\n data: userData,\n curve: 'monotone',\n fillOpacity: 0.2,\n showLine: true,\n },\n ],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n crosshair: true,\n});\n```\n\n## Pie, Donut, and Semi-circle Charts\n\nAll three variants use `createPieChart` with the `variant` field:\n\n```ts\nimport { createPieChart } from '@vielzeug/prism';\n\nconst chart = createPieChart(container, {\n data: [\n { label: 'Direct', value: 42, color: '#3b82f6' },\n { label: 'Organic', value: 28, color: '#10b981' },\n { label: 'Referral', value: 18, color: '#f59e0b' },\n { label: 'Social', value: 12, color: '#8b5cf6' },\n ],\n variant: 'donut', // 'pie' | 'donut' | 'semi'\n tooltip: true,\n transition: { duration: 400, easing: 'ease-out' },\n});\n```\n\n### Variants\n\n| Value | Shape |\n| --------- | ------------------------------------------------------- |\n| `'pie'` | Full circle, no hole |\n| `'donut'` | Full circle with inner hole (~55% of outer by default) |\n| `'semi'` | Top-half semicircle with inner hole — useful for gauges |\n\n### Inner Radius\n\n`innerRadius` overrides the automatic calculation:\n\n```ts\ncreatePieChart(container, {\n data,\n variant: 'donut',\n innerRadius: 60, // explicit pixels\n});\n```\n\n### Slice Labels\n\nSet `label` on each `PieSliceConfig` to render text at the arc centroid:\n\n```ts\n{ value: 42, label: '42%' }\n```\n\nStyle labels via CSS:\n\n```css\n:root {\n --prism-pie-label-color: #fff;\n --prism-pie-label-size: 11px;\n}\n```\n\n### Reactive Data\n\n```ts\nimport { signal } from '@vielzeug/ripple';\n\nconst data = signal([\n { label: 'A', value: 40 },\n { label: 'B', value: 60 },\n]);\n\nconst chart = createPieChart(container, { data, variant: 'donut' });\n\ndata.value = [\n { label: 'A', value: 55 },\n { label: 'B', value: 45 },\n];\n```\n\n### Event Hooks\n\n```ts\ncreatePieChart(container, {\n data,\n onHover: (slice, index) => {\n // slice/index are null on mouseleave\n if (slice) console.log(slice.label, slice.value);\n },\n onClick: (slice, index) => {\n console.log('clicked', slice.label);\n },\n});\n```\n\n## Sparklines\n\nSparklines are minimal inline charts with no axes, no legend, and no margin — designed to live inline with text or inside table cells.\n\n```ts\nimport { createSparkline } from '@vielzeug/prism';\n\nconst spark = createSparkline(container, {\n data: [12, 18, 14, 22, 19, 28],\n variant: 'line', // 'line' | 'area' | 'bar' (default: 'line')\n color: '#3b82f6',\n curve: 'monotone',\n strokeWidth: 1.5,\n});\n\nspark.dispose();\n```\n\n### Variants\n\n- **`line`** — simple polyline path (default)\n- **`area`** — filled area + line overlay\n- **`bar`** — vertical bar for each data point\n- **`stack`** — horizontal proportional segments; use `StackSegment[]` for `data` with per-segment colors\n\n### Reactive Data\n\n```ts\nimport { signal } from '@vielzeug/ripple';\n\nconst data = signal([12, 18, 14, 22]);\n\nconst spark = createSparkline(container, { data, variant: 'area' });\n\ndata.value = [...data.value, 30]; // re-renders automatically\n```\n\n### Event Hooks\n\nSparklines use simplified hooks — index-based rather than full `ChartEvent`:\n\n```ts\nconst spark = createSparkline(container, {\n data: [10, 20, 30],\n onHover: (index, value) => {\n // index/value are null on mouseleave\n if (index !== null) console.log(`Hovering point ${index}: ${value}`);\n },\n onClick: (index, value) => {\n console.log(`Clicked point ${index}: ${value}`);\n },\n});\n```\n\n> **Note:** Sparkline SVGs are marked `aria-hidden=\"true\"` since they are decorative. Provide meaningful surrounding text context for accessibility.\n\n## Axes and Grid\n\n```ts\n{\n xAxis: {\n position: 'bottom', // 'top' | 'bottom'\n tickCount: 5,\n tickFormat: (v) => `$${v}`,\n label: 'Month',\n grid: true, // or { color: '#ddd', dash: '4 2' }\n },\n yAxis: {\n position: 'left', // 'left' | 'right'\n grid: { color: '#f0f0f0' },\n label: 'Revenue ($)',\n },\n}\n```\n\n## Tooltips\n\nEnable with `tooltip: true` for default rendering, or provide a custom `render` function returning an HTML string:\n\n```ts\n{\n tooltip: {\n offset: 12,\n render: (datum, series) => `\n <strong>${series.name}</strong><br/>\n Value: ${datum.value.toLocaleString()}\n `,\n },\n}\n```\n\nThe `render` output is injected via `innerHTML`. If you interpolate user-supplied data, pass a `sanitize` function to guard against XSS:\n\n```ts\nimport DOMPurify from 'dompurify';\n\n{\n tooltip: {\n render: (datum, series) => `<b>${series.name}</b>: ${datum.value}`,\n sanitize: (html) => DOMPurify.sanitize(html),\n },\n}\n```\n\nThe tooltip element is scoped inside the chart container (not `document.body`) and is removed automatically on `dispose()`.\n\n## Crosshair\n\nA vertical guide that snaps to the nearest data point:\n\n```ts\n{\n crosshair: true,\n // or configure:\n crosshair: { vertical: true, horizontal: true, snap: true },\n}\n```\n\n## Legend\n\nEnable with `legend: true` (defaults to `bottom`) or configure position:\n\n```ts\n{\n legend: true,\n // or:\n legend: { position: 'top' }, // 'top' | 'bottom' | 'left' | 'right'\n}\n```\n\nThe legend renders as a `div` placed outside the SVG. Each item shows a color swatch and the series `name`. Customize via CSS:\n\n```css\n:root {\n --prism-legend-gap: 1rem;\n --prism-legend-dot-size: 0.5rem;\n --prism-legend-font-size: 0.75rem;\n}\n```\n\n## Event Hooks\n\nAll charts expose `onClick` and `onHover` callbacks on the config:\n\n```ts\nconst chart = createLineChart(container, {\n series: [{ name: 'Revenue', data }],\n onHover: (event) => {\n // event is ChartEvent | null (null on mouseleave)\n if (event) console.log(event.datum, event.series);\n },\n onClick: (event) => {\n console.log('clicked', event.datum);\n },\n});\n```\n\n`ChartEvent` provides:\n\n- `datum` — the nearest `Datum`\n- `series` — the corresponding `Series` config\n- `originalEvent` — the raw `MouseEvent`\n\n> **Pie chart events differ** — `onHover` and `onClick` receive `(slice: PieSliceConfig, index: number)` instead of `ChartEvent`. See [`PieChartConfig`](./api.md#piechartconfig) for details.\n\n## Plugins\n\nExtend any chart with custom behavior using the `ChartPlugin` interface. All chart types — including `createPieChart` — support `plugins`.\n\n```ts\nimport type { ChartPlugin } from '@vielzeug/prism';\n\nfunction createClickLogger(): ChartPlugin {\n const handler = (e: MouseEvent) => console.log('chart clicked', e);\n // `dispose()` receives no arguments, so capture whatever `install()` needs\n // to clean up (here, the svg it attached the listener to) in this closure.\n let svg: SVGSVGElement | undefined;\n\n return {\n install(ctx) {\n svg = ctx.svg;\n svg.addEventListener('click', handler);\n },\n dispose() {\n svg?.removeEventListener('click', handler);\n },\n };\n}\n\nconst chart = createLineChart(container, {\n series: [{ name: 'Revenue', data }],\n plugins: [createClickLogger()],\n});\n\n// Works for pie charts too:\nconst pie = createPieChart(container, {\n data,\n plugins: [createClickLogger()],\n});\n```\n\n> **Alternative to `dispose()`:** `install(ctx)` can instead listen for `ctx.disposalSignal`'s `abort` event to run cleanup, without needing to capture anything for a separate `dispose()` implementation: `ctx.disposalSignal.addEventListener('abort', () => svg.removeEventListener('click', handler))`.\n>\n> **Error isolation:** if a plugin's `install()` or `dispose()` throws, the error is logged in development and the rest of the chart — plus any other installed plugins — keeps working. A throwing plugin never aborts chart creation or teardown.\n\n## Animations\n\nPass a `transition` config to animate enter and update transitions:\n\n```ts\n{\n transition: {\n duration: 400,\n easing: 'ease-out',\n stagger: 30, // bar charts only: ms delay between each bar's enter animation\n },\n}\n```\n\nAll chart types use requestAnimationFrame-based interpolation. Bar charts additionally support `stagger` — a per-bar delay that creates a cascade effect on first render.\n\n## Theming\n\nImport the default theme:\n\n```ts\nimport '@vielzeug/prism/theme';\n```\n\n### Programmatic Theme with `setTheme`\n\nCall `setTheme` once at app startup to apply custom tokens programmatically:\n\n```ts\nimport { setTheme } from '@vielzeug/prism';\n\nsetTheme({\n colors: ['#6366f1', '#22d3ee', '#f59e0b', '#10b981'], // replaces --prism-color-1 through -4\n fontFamily: 'Inter, system-ui, sans-serif', // sets --prism-font-family\n gridColor: '#e2e8f0', // sets --prism-grid-color\n gridOpacity: 0.6, // sets --prism-grid-opacity\n});\n```\n\n`setTheme` writes to `document.documentElement` style, so it takes precedence over CSS file defaults. Call `resetTheme()` to clear every custom property `setTheme` can set and restore the default theme — useful for a theme-switcher's \"reset\" action or test teardown:\n\n```ts\nimport { resetTheme } from '@vielzeug/prism';\n\nresetTheme();\n```\n\n### Custom Theme (CSS)\n\n```css\n:root {\n --prism-color-1: #6366f1;\n --prism-color-2: #22c55e;\n --prism-axis-color: #71717a;\n --prism-grid-color: #f4f4f5;\n --prism-text-color: #18181b;\n --prism-tooltip-bg: #27272a;\n --prism-font-family: 'Inter', system-ui, sans-serif;\n}\n```\n\n### Scoped Themes\n\nApply tokens to a specific container:\n\n```css\n.dark-dashboard {\n --prism-axis-color: #64748b;\n --prism-grid-color: #334155;\n --prism-text-color: #e2e8f0;\n}\n```\n\n### Available Tokens\n\n| Token | Default | Description |\n| ------------------------- | ---------------- | ---------------------- |\n| `--prism-color-{1-8}` | Tailwind palette | Series color palette |\n| `--prism-bg` | `transparent` | Chart background |\n| `--prism-axis-color` | `#94a3b8` | Axis lines and ticks |\n| `--prism-grid-color` | `#e2e8f0` | Grid lines |\n| `--prism-text-color` | `#334155` | Axis labels and text |\n| `--prism-font-family` | `system-ui` | Chart font |\n| `--prism-font-size` | `12px` | Label font size |\n| `--prism-tooltip-bg` | `#1e293b` | Tooltip background |\n| `--prism-tooltip-color` | `#f8fafc` | Tooltip text |\n| `--prism-tooltip-radius` | `6px` | Tooltip border radius |\n| `--prism-crosshair-color` | `#64748b` | Crosshair line |\n| `--prism-crosshair-dash` | `4 2` | Crosshair dash pattern |\n\n## Scales (Standalone)\n\nScales can be used independently for custom visualizations:\n\n```ts\nimport { linearScale, timeScale, bandScale } from '@vielzeug/prism';\n\nconst y = linearScale({ domain: [0, 100], range: [300, 0] });\ny.map(50); // → 150\ny.invert(150); // → 50\ny.ticks(5); // → [0, 20, 40, 60, 80, 100]\n\nconst x = bandScale({ domain: ['A', 'B', 'C'], range: [0, 300] });\nx.map('B'); // → pixel left edge of band B\nx.bandwidth(); // → width of each band\n```\n\n## Lifecycle and Cleanup\n\nEvery chart returns a `ChartHandle`. Always call `dispose()` when removing a chart:\n\n```ts\nconst chart = createLineChart(container, config);\n\n// When done:\nchart.dispose();\n\n// Or with TC39 explicit resource management:\n{\n using chart = createLineChart(container, config);\n // auto-disposed at block end\n}\n```\n\nCalling `dispose()`:\n\n- Cancels all reactive signal effects\n- Disconnects the `ResizeObserver`\n- Removes the SVG element, tooltip, and legend from the DOM\n- Calls `dispose()` on all plugins (a plugin that throws is logged and skipped — it never blocks the rest of teardown)\n- Is idempotent — safe to call multiple times\n\n> **Reactivity is automatic** — charts re-render whenever signal data changes. There is no manual `update()` call needed.\n\n## Responsive Behavior\n\nCharts resize automatically when the container dimensions change. Prism uses `ResizeObserver` internally — no manual `resize()` call is needed.\n\n## Devtools\n\nImport `debugChart()` from the `/devtools` subpath to log a chart's mount, resize, and dispose events to `console.debug`. It's separate from prism's internal validation warnings (those run automatically in development, no import needed) and is tree-shaken from production bundles when this subpath isn't imported.\n\n```ts\nimport { createLineChart } from '@vielzeug/prism';\nimport { debugChart } from '@vielzeug/prism/devtools';\n\nconst chart = debugChart(createLineChart(container, config), { label: 'revenue' });\n// [prism:revenue] mounted\n// [prism:revenue] resized 600×300\nchart.dispose();\n// [prism:revenue] disposed\n```\n\n> `debugChart()` wraps and returns the same `ChartHandle` unchanged, so it drops into any `create*Chart()` call without restructuring your code.\n\n## Framework Integration\n\nPrism renders into a plain DOM element. Attach charts inside mount/unmount lifecycle hooks for any framework.\n\n::: code-group\n\n```tsx [React]\nimport { useEffect, useRef } from 'react';\nimport { createLineChart, type Datum } from '@vielzeug/prism';\n\nfunction LineChart({ data }: { data: Datum[] }) {\n const containerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const chart = createLineChart(containerRef.current!, {\n series: [{ data, name: 'Series' }],\n });\n return () => chart.dispose();\n }, [data]);\n\n return <div ref={containerRef} style={{ width: '100%', height: 300 }} />;\n}\n```\n\n```ts [Vue 3]\nimport { onMounted, onUnmounted, ref } from 'vue';\nimport { createLineChart, type ChartHandle, type Datum } from '@vielzeug/prism';\n\nfunction useLineChart(data: Datum[]) {\n const containerRef = ref<HTMLElement | null>(null);\n let chart: ChartHandle | null = null;\n\n onMounted(() => {\n chart = createLineChart(containerRef.value!, { series: [{ data, name: 'Series' }] });\n });\n\n onUnmounted(() => chart?.dispose());\n return { containerRef };\n}\n```\n\n```svelte [Svelte]\n<script lang=\"ts\">\n import { onMount } from 'svelte';\n import { createLineChart, type Datum } from '@vielzeug/prism';\n\n export let data: Datum[] = [];\n let container: HTMLDivElement;\n\n onMount(() => {\n const chart = createLineChart(container, { series: [{ data, name: 'Series' }] });\n return () => chart.dispose();\n });\n</script>\n\n<div bind:this={container} style=\"width:100%;height:300px\"></div>\n```\n\n:::\n\n## Working with Other Vielzeug Libraries\n\n### With Ripple\n\nPass Ripple signals as chart data properties. Prism re-renders automatically when a signal changes.\n\n```ts\nimport { signal } from '@vielzeug/ripple';\nimport { createLineChart } from '@vielzeug/prism';\n\nconst data = signal([\n { key: 1, value: 10 },\n { key: 2, value: 20 },\n]);\n\nconst chart = createLineChart(container, {\n series: [{ data, name: 'Series' }], // signal passed directly\n});\n\n// Updating the signal triggers an automatic re-render:\ndata.value = [\n { key: 1, value: 15 },\n { key: 2, value: 25 },\n];\n```\n\n### With Sourcerer\n\nBind chart data to a Sourcerer remote source so charts update whenever the list refreshes.\n\n```ts\nimport { createPageSource } from '@vielzeug/sourcerer';\nimport { computed, signal } from '@vielzeug/ripple';\nimport { createBarChart } from '@vielzeug/prism';\n\nconst source = createPageSource({ load: ({ query, signal }) => api.stats.list(query, { signal }) });\nconst snapshot = signal(source.snapshot);\nsource.subscribe((next) => (snapshot.value = next));\n\nconst chartData = computed(() => snapshot.value.data.map((item) => ({ key: item.label, value: item.count })));\n\nconst chart = createBarChart(container, {\n series: [{ data: chartData, name: 'Series' }],\n});\n```\n\n## Best Practices\n\n- Ensure the container element has explicit dimensions before calling a chart factory — `ResizeObserver` needs a non-zero layout size to trigger the first render.\n- Call `chart.dispose()` in your framework's unmount/cleanup phase to cancel signal effects and remove DOM nodes.\n- Prefer `signal()` from Ripple for mutable data properties — charts re-render automatically when signals change, with no manual `update()` call.\n- Wrap a chart with `debugChart()` from the `/devtools` subpath only in development code paths; it is tree-shaken in production.\n- For SSR, skip chart creation server-side — Prism depends on DOM APIs and `ResizeObserver`. Render charts only after hydration in a `onMounted`/`useEffect` callback.\n",
|
|
7
|
+
"examples": "---\ntitle: Prism — Examples\ndescription: Interactive code examples for @vielzeug/prism charts.\n---\n\n[[toc]]\n\n## Line Chart\n\nBasic line chart with tooltip and crosshair:\n\n<ComponentPreview vertical height=\"320px\">\n\n```html\n<div id=\"ex-line\" style=\"width:100%;height:280px;\"></div>\n<script>\n const { createLineChart } = Prism;\n createLineChart(document.getElementById('ex-line'), {\n series: [\n {\n name: 'Revenue',\n data: [\n { key: 1, value: 120 },\n { key: 2, value: 180 },\n { key: 3, value: 150 },\n { key: 4, value: 220 },\n { key: 5, value: 195 },\n { key: 6, value: 280 },\n ],\n color: '#3b82f6',\n curve: 'monotone',\n strokeWidth: 2,\n showPoints: true,\n },\n ],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n tooltip: true,\n crosshair: true,\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Multi-series Line Chart\n\nMultiple lines with different curves:\n\n<ComponentPreview vertical height=\"320px\">\n\n```html\n<div id=\"ex-multi-line\" style=\"width:100%;height:280px;\"></div>\n<script>\n const { createLineChart } = Prism;\n createLineChart(document.getElementById('ex-multi-line'), {\n series: [\n {\n name: 'Product A',\n data: [\n { key: 1, value: 40 },\n { key: 2, value: 65 },\n { key: 3, value: 55 },\n { key: 4, value: 80 },\n { key: 5, value: 72 },\n ],\n color: '#3b82f6',\n curve: 'monotone',\n },\n {\n name: 'Product B',\n data: [\n { key: 1, value: 20 },\n { key: 2, value: 35 },\n { key: 3, value: 60 },\n { key: 4, value: 45 },\n { key: 5, value: 90 },\n ],\n color: '#10b981',\n curve: 'monotone',\n },\n ],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n crosshair: true,\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Legend — Line Chart\n\nAdd `legend: true` to label each series below the chart:\n\n<ComponentPreview vertical height=\"360px\">\n\n```html\n<div id=\"ex-legend-line\" style=\"width:100%;height:280px;\"></div>\n<script>\n const { createLineChart } = Prism;\n createLineChart(document.getElementById('ex-legend-line'), {\n series: [\n {\n name: 'Revenue',\n data: [\n { key: 1, value: 120 },\n { key: 2, value: 180 },\n { key: 3, value: 150 },\n { key: 4, value: 220 },\n { key: 5, value: 195 },\n ],\n color: '#3b82f6',\n curve: 'monotone',\n },\n {\n name: 'Expenses',\n data: [\n { key: 1, value: 80 },\n { key: 2, value: 95 },\n { key: 3, value: 110 },\n { key: 4, value: 130 },\n { key: 5, value: 125 },\n ],\n color: '#ef4444',\n curve: 'monotone',\n },\n ],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n tooltip: true,\n crosshair: true,\n legend: true,\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Bar Chart\n\nGrouped bar chart comparing categories:\n\n<ComponentPreview vertical height=\"320px\">\n\n```html\n<div id=\"ex-bar\" style=\"width:100%;height:280px;\"></div>\n<script>\n const { createBarChart } = Prism;\n createBarChart(document.getElementById('ex-bar'), {\n series: [\n {\n name: '2023',\n data: [\n { key: 'Q1', value: 120 },\n { key: 'Q2', value: 180 },\n { key: 'Q3', value: 150 },\n { key: 'Q4', value: 210 },\n ],\n color: '#94a3b8',\n borderRadius: 4,\n },\n {\n name: '2024',\n data: [\n { key: 'Q1', value: 150 },\n { key: 'Q2', value: 220 },\n { key: 'Q3', value: 190 },\n { key: 'Q4', value: 280 },\n ],\n color: '#3b82f6',\n borderRadius: 4,\n },\n ],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n tooltip: true,\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Stacked Bar Chart\n\nBar chart with `variant: 'stacked'` — series stack vertically per category:\n\n<ComponentPreview vertical height=\"320px\">\n\n```html\n<div id=\"ex-bar-stacked\" style=\"width:100%;height:280px;\"></div>\n<script>\n const { createBarChart } = Prism;\n createBarChart(document.getElementById('ex-bar-stacked'), {\n series: [\n {\n name: 'Mobile',\n data: [\n { key: 'Q1', value: 80 },\n { key: 'Q2', value: 110 },\n { key: 'Q3', value: 95 },\n { key: 'Q4', value: 130 },\n ],\n color: '#3b82f6',\n borderRadius: 0,\n },\n {\n name: 'Desktop',\n data: [\n { key: 'Q1', value: 60 },\n { key: 'Q2', value: 90 },\n { key: 'Q3', value: 75 },\n { key: 'Q4', value: 100 },\n ],\n color: '#10b981',\n borderRadius: 0,\n },\n {\n name: 'Tablet',\n data: [\n { key: 'Q1', value: 20 },\n { key: 'Q2', value: 30 },\n { key: 'Q3', value: 25 },\n { key: 'Q4', value: 35 },\n ],\n color: '#f59e0b',\n borderRadius: 0,\n },\n ],\n variant: 'stacked',\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n tooltip: true,\n legend: true,\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Horizontal Bar Chart\n\nBar chart with `variant: 'grouped-horizontal'` — categories on the Y axis, values on the X axis:\n\n<ComponentPreview vertical height=\"320px\">\n\n```html\n<div id=\"ex-bar-horizontal\" style=\"width:100%;height:280px;\"></div>\n<script>\n const { createBarChart } = Prism;\n createBarChart(document.getElementById('ex-bar-horizontal'), {\n variant: 'grouped-horizontal',\n series: [\n {\n name: 'Revenue',\n data: [\n { key: 'Q1', value: 80 },\n { key: 'Q2', value: 110 },\n { key: 'Q3', value: 95 },\n { key: 'Q4', value: 130 },\n ],\n color: '#3b82f6',\n },\n ],\n xAxis: { position: 'bottom', grid: true },\n yAxis: { position: 'left' },\n tooltip: true,\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Horizontal Stacked Bar Chart\n\nUse `variant: 'stacked-horizontal'` — horizontal bars stacked per category:\n\n<ComponentPreview vertical height=\"320px\">\n\n```html\n<div id=\"ex-bar-h-stacked\" style=\"width:100%;height:280px;\"></div>\n<script>\n const { createBarChart } = Prism;\n createBarChart(document.getElementById('ex-bar-h-stacked'), {\n variant: 'stacked-horizontal',\n series: [\n {\n name: 'Mobile',\n data: [\n { key: 'Q1', value: 80 },\n { key: 'Q2', value: 110 },\n { key: 'Q3', value: 95 },\n { key: 'Q4', value: 130 },\n ],\n color: '#3b82f6',\n borderRadius: 0,\n },\n {\n name: 'Desktop',\n data: [\n { key: 'Q1', value: 60 },\n { key: 'Q2', value: 90 },\n { key: 'Q3', value: 75 },\n { key: 'Q4', value: 100 },\n ],\n color: '#10b981',\n borderRadius: 0,\n },\n ],\n xAxis: { position: 'bottom', grid: true },\n yAxis: { position: 'left' },\n tooltip: true,\n legend: true,\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Legend — Bar Chart\n\nGrouped bar chart with a legend positioned at the top:\n\n<ComponentPreview vertical height=\"360px\">\n\n```html\n<div id=\"ex-legend-bar\" style=\"width:100%;height:280px;\"></div>\n<script>\n const { createBarChart } = Prism;\n createBarChart(document.getElementById('ex-legend-bar'), {\n series: [\n {\n name: '2023',\n data: [\n { key: 'Q1', value: 120 },\n { key: 'Q2', value: 180 },\n { key: 'Q3', value: 150 },\n { key: 'Q4', value: 210 },\n ],\n color: '#94a3b8',\n borderRadius: 4,\n },\n {\n name: '2024',\n data: [\n { key: 'Q1', value: 150 },\n { key: 'Q2', value: 220 },\n { key: 'Q3', value: 190 },\n { key: 'Q4', value: 280 },\n ],\n color: '#3b82f6',\n borderRadius: 4,\n },\n ],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n tooltip: true,\n legend: { position: 'top' },\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Area Chart\n\nFilled area with monotone curve and low opacity:\n\n<ComponentPreview vertical height=\"320px\">\n\n```html\n<div id=\"ex-area\" style=\"width:100%;height:280px;\"></div>\n<script>\n const { createAreaChart } = Prism;\n createAreaChart(document.getElementById('ex-area'), {\n series: [\n {\n name: 'Signups',\n data: [\n { key: 1, value: 500 },\n { key: 2, value: 650 },\n { key: 3, value: 800 },\n { key: 4, value: 720 },\n { key: 5, value: 900 },\n { key: 6, value: 1100 },\n ],\n color: '#8b5cf6',\n curve: 'monotone',\n fillOpacity: 0.2,\n showLine: true,\n },\n ],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: { color: '#f1f5f9' } },\n crosshair: { vertical: true },\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Legend — Area Chart\n\nMulti-series area chart with a bottom legend:\n\n<ComponentPreview vertical height=\"360px\">\n\n```html\n<div id=\"ex-legend-area\" style=\"width:100%;height:280px;\"></div>\n<script>\n const { createAreaChart } = Prism;\n createAreaChart(document.getElementById('ex-legend-area'), {\n series: [\n {\n name: 'Mobile',\n data: [\n { key: 1, value: 300 },\n { key: 2, value: 420 },\n { key: 3, value: 510 },\n { key: 4, value: 480 },\n { key: 5, value: 620 },\n { key: 6, value: 750 },\n ],\n color: '#8b5cf6',\n curve: 'monotone',\n fillOpacity: 0.25,\n },\n {\n name: 'Desktop',\n data: [\n { key: 1, value: 200 },\n { key: 2, value: 230 },\n { key: 3, value: 290 },\n { key: 4, value: 240 },\n { key: 5, value: 280 },\n { key: 6, value: 350 },\n ],\n color: '#06b6d4',\n curve: 'monotone',\n fillOpacity: 0.25,\n },\n ],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n crosshair: true,\n legend: true,\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Step Line Chart\n\nLine chart with step interpolation:\n\n<ComponentPreview vertical height=\"320px\">\n\n```html\n<div id=\"ex-step\" style=\"width:100%;height:280px;\"></div>\n<script>\n const { createLineChart } = Prism;\n createLineChart(document.getElementById('ex-step'), {\n series: [\n {\n name: 'Status',\n data: [\n { key: 1, value: 0 },\n { key: 2, value: 1 },\n { key: 3, value: 1 },\n { key: 4, value: 0 },\n { key: 5, value: 1 },\n { key: 6, value: 0 },\n ],\n color: '#f59e0b',\n curve: 'step',\n strokeWidth: 3,\n },\n ],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left' },\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Reactive Chart\n\nChart that updates when signal data changes:\n\n<ComponentPreview vertical height=\"320px\">\n\n```html\n<div style=\"margin-bottom:8px;\">\n <button id=\"ex-reactive-btn\" style=\"padding:4px 12px;border:1px solid #e2e8f0;border-radius:4px;cursor:pointer;\">\n Add Data Point\n </button>\n</div>\n<div id=\"ex-reactive\" style=\"width:100%;height:250px;\"></div>\n<script>\n const { createLineChart } = Prism;\n const { signal } = Ripple;\n\n const data = signal([\n { key: 1, value: 20 },\n { key: 2, value: 35 },\n { key: 3, value: 28 },\n { key: 4, value: 45 },\n ]);\n\n createLineChart(document.getElementById('ex-reactive'), {\n series: [{ name: 'Live', data, color: '#10b981', curve: 'monotone', showPoints: true }],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n crosshair: true,\n transition: { duration: 400, easing: 'ease-out' },\n });\n\n document.getElementById('ex-reactive-btn').addEventListener('click', function () {\n var prev = data.value;\n var nextX = prev.length + 1;\n var nextY = 20 + Math.floor(Math.random() * 40);\n data.value = prev.concat([{ key: nextX, value: nextY }]);\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Reactive Bar Chart\n\nBar chart that updates when signal data changes, with stagger animation on new bars:\n\n<ComponentPreview vertical height=\"320px\">\n\n```html\n<div style=\"margin-bottom:8px;\">\n <button id=\"ex-reactive-bar-btn\" style=\"padding:4px 12px;border:1px solid #e2e8f0;border-radius:4px;cursor:pointer;\">\n Add Category\n </button>\n</div>\n<div id=\"ex-reactive-bar\" style=\"width:100%;height:250px;\"></div>\n<script>\n const { createBarChart } = Prism;\n const { signal } = Ripple;\n\n const barData = signal([\n { key: 'Q1', value: 120 },\n { key: 'Q2', value: 180 },\n { key: 'Q3', value: 150 },\n { key: 'Q4', value: 210 },\n ]);\n\n createBarChart(document.getElementById('ex-reactive-bar'), {\n series: [{ name: 'Revenue', data: barData, color: '#6366f1', borderRadius: 4 }],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n tooltip: true,\n transition: { duration: 400, easing: 'ease-out', stagger: 40 },\n });\n\n var quarters = ['Q5', 'Q6', 'Q7', 'Q8'];\n var qIdx = 0;\n document.getElementById('ex-reactive-bar-btn').addEventListener('click', function () {\n if (qIdx >= quarters.length) return;\n var nextY = 150 + Math.floor(Math.random() * 120);\n barData.value = barData.value.concat([{ key: quarters[qIdx++], value: nextY }]);\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Event Hooks\n\nUsing `onHover` and `onClick` to react to chart interactions:\n\n<ComponentPreview vertical height=\"360px\">\n\n```html\n<div id=\"ex-events-info\" style=\"margin-bottom:8px;font-size:13px;color:#64748b;min-height:20px;\"></div>\n<div id=\"ex-events\" style=\"width:100%;height:280px;\"></div>\n<script>\n const { createLineChart } = Prism;\n\n const info = document.getElementById('ex-events-info');\n\n createLineChart(document.getElementById('ex-events'), {\n series: [\n {\n name: 'Revenue',\n data: [\n { key: 1, value: 120 },\n { key: 2, value: 180 },\n { key: 3, value: 150 },\n { key: 4, value: 220 },\n { key: 5, value: 195 },\n { key: 6, value: 280 },\n ],\n color: '#3b82f6',\n curve: 'monotone',\n showPoints: true,\n },\n ],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n onHover: function (event) {\n info.textContent = event ? 'Hovering key=' + event.datum.key + ' value=' + event.datum.value : '';\n },\n onClick: function (event) {\n info.textContent = 'Clicked key=' + event.datum.key + ' value=' + event.datum.value;\n },\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Pie Chart\n\nBasic pie chart with labeled slices:\n\n<ComponentPreview vertical height=\"340px\">\n\n```html\n<div id=\"ex-pie\" style=\"width:300px;height:300px;\"></div>\n<script>\n const { createPieChart } = Prism;\n createPieChart(document.getElementById('ex-pie'), {\n data: [\n { label: 'Direct', value: 42, color: '#3b82f6' },\n { label: 'Organic', value: 28, color: '#10b981' },\n { label: 'Referral', value: 18, color: '#f59e0b' },\n { label: 'Social', value: 12, color: '#8b5cf6' },\n ],\n variant: 'pie',\n transition: { duration: 600, easing: 'ease-out' },\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Donut Chart\n\nDonut chart with tooltip:\n\n<ComponentPreview vertical height=\"340px\">\n\n```html\n<div id=\"ex-donut\" style=\"width:300px;height:300px;\"></div>\n<script>\n const { createPieChart } = Prism;\n createPieChart(document.getElementById('ex-donut'), {\n data: [\n { label: 'Direct', value: 42, color: '#3b82f6' },\n { label: 'Organic', value: 28, color: '#10b981' },\n { label: 'Referral', value: 18, color: '#f59e0b' },\n { label: 'Social', value: 12, color: '#8b5cf6' },\n ],\n variant: 'donut',\n tooltip: true,\n transition: { duration: 600, easing: 'ease-out' },\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Semi-circle Donut\n\nSemicircle donut — useful for gauges and progress indicators:\n\n<ComponentPreview vertical height=\"220px\">\n\n```html\n<div id=\"ex-semi\" style=\"width:300px;height:180px;\"></div>\n<script>\n const { createPieChart } = Prism;\n createPieChart(document.getElementById('ex-semi'), {\n data: [\n { label: 'Used', value: 68, color: '#3b82f6' },\n { label: 'Free', value: 32, color: '#e2e8f0' },\n ],\n variant: 'semi',\n transition: { duration: 800, easing: 'ease-out' },\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Sparkline — Line\n\nMinimal inline sparkline inside a table cell or card:\n\n<ComponentPreview vertical height=\"80px\">\n\n```html\n<div id=\"ex-spark-line\" style=\"width:200px;height:40px;\"></div>\n<script>\n const { createSparkline } = Prism;\n createSparkline(document.getElementById('ex-spark-line'), {\n data: [12, 18, 14, 22, 19, 28, 24, 32],\n variant: 'line',\n color: '#3b82f6',\n curve: 'monotone',\n strokeWidth: 1.5,\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Sparkline — Area\n\nArea variant with fill:\n\n<ComponentPreview vertical height=\"80px\">\n\n```html\n<div id=\"ex-spark-area\" style=\"width:200px;height:40px;\"></div>\n<script>\n const { createSparkline } = Prism;\n createSparkline(document.getElementById('ex-spark-area'), {\n data: [12, 18, 14, 22, 19, 28, 24, 32],\n variant: 'area',\n color: '#8b5cf6',\n curve: 'monotone',\n fillOpacity: 0.25,\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Sparkline — Bar\n\nBar variant — one rect per value:\n\n<ComponentPreview vertical height=\"80px\">\n\n```html\n<div id=\"ex-spark-bar\" style=\"width:200px;height:40px;\"></div>\n<script>\n const { createSparkline } = Prism;\n createSparkline(document.getElementById('ex-spark-bar'), {\n data: [12, 18, 14, 22, 19, 28, 24, 32],\n variant: 'bar',\n color: '#10b981',\n transition: { duration: 400, easing: 'ease-out', stagger: 30 },\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Sparkline — Reactive\n\nSparkline that updates when signal data changes:\n\n<ComponentPreview vertical height=\"120px\">\n\n```html\n<div style=\"margin-bottom:8px;\">\n <button id=\"ex-spark-btn\" style=\"padding:4px 12px;border:1px solid #e2e8f0;border-radius:4px;cursor:pointer;\">\n Add Point\n </button>\n</div>\n<div id=\"ex-spark-reactive\" style=\"width:200px;height:40px;\"></div>\n<script>\n const { createSparkline } = Prism;\n const { signal } = Ripple;\n\n const sparkData = signal([10, 15, 12, 18, 14]);\n\n createSparkline(document.getElementById('ex-spark-reactive'), {\n data: sparkData,\n variant: 'area',\n color: '#f59e0b',\n curve: 'monotone',\n fillOpacity: 0.2,\n transition: { duration: 300, easing: 'ease-out' },\n });\n\n document.getElementById('ex-spark-btn').addEventListener('click', function () {\n sparkData.value = sparkData.value.concat([10 + Math.floor(Math.random() * 25)]);\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Sparkline — Stack\n\nHorizontal stacked bar — proportional segments with per-segment colors:\n\n<ComponentPreview vertical height=\"80px\">\n\n```html\n<div id=\"ex-spark-stack\" style=\"width:200px;height:40px;\"></div>\n<script>\n const { createSparkline } = Prism;\n createSparkline(document.getElementById('ex-spark-stack'), {\n variant: 'stack',\n data: [\n { label: 'Chrome', value: 341, color: '#3b82f6' },\n { label: 'Safari', value: 217, color: '#06b6d4' },\n { label: 'Firefox', value: 124, color: '#10b981' },\n { label: 'Edge', value: 53, color: '#f59e0b' },\n ],\n cornerRadius: 4,\n padPixels: 4,\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Custom Tooltip\n\nRich HTML tooltip with custom formatting:\n\n<ComponentPreview vertical height=\"320px\">\n\n```html\n<div id=\"ex-tooltip\" style=\"width:100%;height:280px;\"></div>\n<script>\n const { createBarChart } = Prism;\n createBarChart(document.getElementById('ex-tooltip'), {\n series: [\n {\n name: 'Revenue',\n data: [\n { key: 'Jan', value: 4200 },\n { key: 'Feb', value: 5100 },\n { key: 'Mar', value: 4800 },\n { key: 'Apr', value: 6300 },\n { key: 'May', value: 5900 },\n { key: 'Jun', value: 7200 },\n ],\n color: '#6366f1',\n borderRadius: 6,\n },\n ],\n xAxis: { position: 'bottom' },\n yAxis: { position: 'left', grid: true },\n tooltip: {\n render: function (datum, series) {\n return (\n '<div style=\"font-weight:600\">' +\n series.name +\n '</div>' +\n '<div style=\"opacity:0.7;font-size:11px\">' +\n datum.key +\n '</div>' +\n '<div style=\"font-size:14px;margin-top:2px\">$' +\n datum.value.toLocaleString() +\n '</div>'\n );\n },\n },\n });\n</script>\n```\n\n</ComponentPreview>\n\n## Pie Chart with Plugin\n\nA donut chart that installs a custom plugin to draw a total count label in the center hole:\n\n<ComponentPreview vertical height=\"320px\">\n\n```html\n<div id=\"ex-pie-plugin\" style=\"width:100%;height:280px;\"></div>\n<script>\n const { createPieChart } = Prism;\n\n const data = [\n { label: 'Direct', value: 42, color: '#6366f1' },\n { label: 'Organic', value: 28, color: '#10b981' },\n { label: 'Social', value: 18, color: '#f59e0b' },\n { label: 'Referral', value: 12, color: '#8b5cf6' },\n ];\n\n const total = data.reduce((s, d) => s + d.value, 0);\n let centerLabel;\n\n const centerPlugin = {\n install(ctx) {\n const ns = 'http://www.w3.org/2000/svg';\n centerLabel = document.createElementNS(ns, 'text');\n centerLabel.setAttribute('text-anchor', 'middle');\n centerLabel.setAttribute('dominant-baseline', 'middle');\n centerLabel.setAttribute('font-size', '20');\n centerLabel.setAttribute('font-weight', '600');\n centerLabel.setAttribute('fill', 'var(--prism-text-color, #334155)');\n centerLabel.textContent = total;\n ctx.svg.appendChild(centerLabel);\n // Position at SVG center once dimensions are available\n requestAnimationFrame(() => {\n const { width, height } = ctx.dimensions.value;\n if (width && height) {\n centerLabel.setAttribute('x', String(width / 2));\n centerLabel.setAttribute('y', String(height / 2));\n }\n });\n },\n dispose() {\n centerLabel?.remove();\n },\n };\n\n createPieChart(document.getElementById('ex-pie-plugin'), {\n data,\n variant: 'donut',\n tooltip: true,\n transition: { duration: 400, easing: 'ease-out' },\n plugins: [centerPlugin],\n });\n</script>\n```\n\n</ComponentPreview>\n"
|
|
8
|
+
},
|
|
9
|
+
"examples": [],
|
|
10
|
+
"typeSignatures": {
|
|
11
|
+
"AreaChartConfig": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
12
|
+
"AreaSeriesConfig": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
13
|
+
"AxisConfig": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
14
|
+
"AxisPosition": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
15
|
+
"BandScale": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
16
|
+
"BarChartConfig": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
17
|
+
"BarSeriesConfig": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
18
|
+
"BarVariant": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
19
|
+
"BaseChartConfig": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
20
|
+
"ChartDimensions": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
21
|
+
"ChartEvent": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
22
|
+
"ChartHandle": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
23
|
+
"ChartMargin": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
24
|
+
"ChartPlugin": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
25
|
+
"ChartPluginContext": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
26
|
+
"CrosshairConfig": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
27
|
+
"Datum": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
28
|
+
"GridConfig": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
29
|
+
"LegendConfig": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
30
|
+
"LegendPosition": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
31
|
+
"LineChartConfig": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
32
|
+
"LineSeriesConfig": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
33
|
+
"MaybeSignal": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
34
|
+
"PieChartConfig": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
35
|
+
"PieSliceConfig": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
36
|
+
"PieVariant": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
37
|
+
"PrismTheme": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
38
|
+
"Scale": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
39
|
+
"Series": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
40
|
+
"SparklineConfig": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
41
|
+
"SparklineVariant": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
42
|
+
"StackSegment": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
43
|
+
"TooltipConfig": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
44
|
+
"TransitionConfig": "export type {\n AreaChartConfig,\n AreaSeriesConfig,\n AxisConfig,\n AxisPosition,\n BandScale,\n BarChartConfig,\n BarSeriesConfig,\n BarVariant,\n BaseChartConfig,\n ChartDimensions,\n ChartEvent,\n ChartHandle,\n ChartMargin,\n ChartPlugin,\n ChartPluginContext,\n CrosshairConfig,\n Datum,\n GridConfig,\n LegendConfig,\n LegendPosition,\n LineChartConfig,\n LineSeriesConfig,\n MaybeSignal,\n PieChartConfig,\n PieSliceConfig,\n PieVariant,\n PrismTheme,\n Scale,\n Series,\n SparklineConfig,\n SparklineVariant,\n StackSegment,\n TooltipConfig,\n TransitionConfig,\n} from './types';",
|
|
45
|
+
"PrismDisposedError": "export { PrismDisposedError, PrismError, PrismRenderError } from './errors';",
|
|
46
|
+
"PrismError": "export { PrismDisposedError, PrismError, PrismRenderError } from './errors';",
|
|
47
|
+
"PrismRenderError": "export { PrismDisposedError, PrismError, PrismRenderError } from './errors';",
|
|
48
|
+
"createAreaChart": "export { createAreaChart } from './charts/area';",
|
|
49
|
+
"createBarChart": "export { createBarChart } from './charts/bar';",
|
|
50
|
+
"createLineChart": "export { createLineChart } from './charts/line';",
|
|
51
|
+
"createPieChart": "export { createPieChart } from './charts/pie';",
|
|
52
|
+
"createSparkline": "export { createSparkline } from './charts/sparkline';",
|
|
53
|
+
"bandScale": "export { bandScale } from './scales/band';",
|
|
54
|
+
"linearScale": "export { linearScale } from './scales/linear';",
|
|
55
|
+
"timeScale": "export { timeScale } from './scales/time';",
|
|
56
|
+
"animate": "export { animate } from './animation/transition';",
|
|
57
|
+
"AnimationTarget": "export type { AnimationTarget } from './animation/transition';",
|
|
58
|
+
"EasingFn": "export type { EasingFn } from './animation/easing';",
|
|
59
|
+
"LegendState": "export type { LegendState } from './interaction/legend';",
|
|
60
|
+
"TooltipState": "export type { TooltipState } from './interaction/tooltip';",
|
|
61
|
+
"Point": "export type { Point } from './svg/path';",
|
|
62
|
+
"ChartEventHandlers": "export type { ChartEventHandlers, RadialScaffoldContext, ScaffoldContext, ScaffoldGroups } from './core/chart-scaffold';",
|
|
63
|
+
"RadialScaffoldContext": "export type { ChartEventHandlers, RadialScaffoldContext, ScaffoldContext, ScaffoldGroups } from './core/chart-scaffold';",
|
|
64
|
+
"ScaffoldContext": "export type { ChartEventHandlers, RadialScaffoldContext, ScaffoldContext, ScaffoldGroups } from './core/chart-scaffold';",
|
|
65
|
+
"ScaffoldGroups": "export type { ChartEventHandlers, RadialScaffoldContext, ScaffoldContext, ScaffoldGroups } from './core/chart-scaffold';",
|
|
66
|
+
"resetTheme": "export { resetTheme, seriesColor, setTheme } from './theme';",
|
|
67
|
+
"seriesColor": "export { resetTheme, seriesColor, setTheme } from './theme';",
|
|
68
|
+
"setTheme": "export { resetTheme, seriesColor, setTheme } from './theme';"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"apiSource": "export type {\n BufferOptions,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n Middleware,\n PresenceChannel,\n Pulse,\n PulseChannel,\n PulseOptions,\n PulseStatus,\n ReadonlyMap,\n ReconnectOptions,\n Unsubscribe,\n} from './types';\n\nexport {\n PulseAbortError,\n PulseConnectionError,\n PulseDisposedError,\n PulseError,\n PulseProtocolError,\n PulseTimeoutError,\n} from './errors';\n\nexport { createPulse } from './pulse';\n",
|
|
3
|
+
"docs": {
|
|
4
|
+
"index": "---\ntitle: Pulse — Typed WebSocket client with channels, rooms, and presence\ndescription: Full-featured WebSocket client with typed messaging, channel multiplexing, room management, reactive presence, auto-reconnect, and heartbeat — built on ripple signals.\npackage: pulse\ncategory: websockets\nkeywords: [websocket, realtime, channels, presence, reconnect, heartbeat, typed-messaging, ripple]\nrelated: [herald, ripple, courier, clockwork]\nexports:\n [\n createPulse,\n Pulse,\n PulseChannel,\n PresenceChannel,\n PulseOptions,\n BufferOptions,\n PulseError,\n PulseConnectionError,\n PulseTimeoutError,\n PulseAbortError,\n PulseDisposedError,\n PulseProtocolError,\n ]\nenvironments: [browser, node]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"pulse\" />\n\n## Why Pulse?\n\nRaw WebSocket gives you an untyped message stream — no event routing, no reconnection, no presence, no lifecycle management. Building those primitives for every project is repetitive and error-prone.\n\n```ts\n// Before — raw WebSocket\nconst ws = new WebSocket('wss://api.example.com/ws');\nws.addEventListener('message', (ev) => {\n const { type, payload } = JSON.parse(ev.data); // untyped\n if (type === 'chat:message') renderMessage(payload); // manual routing\n});\nws.addEventListener('close', () => setTimeout(reconnect, 3_000)); // manual reconnect\n// No channels, no presence, no heartbeat, no disposal\n\n// After — Pulse\nconst pulse = createPulse<ServerEvents, ClientEvents>('wss://api.example.com/ws', {\n reconnect: { maxAttempts: 5 },\n heartbeat: true,\n});\npulse.on('chat:message', ({ user, text }) => renderMessage({ user, text })); // fully typed\npulse.send('chat:send', { text: 'Hello!' });\neffect(() => console.log('status:', pulse.status.value)); // reactive via ripple\n```\n\n| Feature | Pulse | Native WebSocket | socket.io-client |\n| --------------------- | ---------------------------------------------------------- | ----------------------------------------------- | ---------------------------------------------------------- |\n| Bundle size | <PackageInfo package=\"pulse\" type=\"size\" /> | 0 B (native) | ~44 kB gzip |\n| TypeScript inference | <ore-icon name=\"check\" size=\"16\"></ore-icon> Full | <ore-icon name=\"x\" size=\"16\"></ore-icon> None | <ore-icon name=\"triangle-alert\" size=\"16\"></ore-icon> Basic |\n| Auto-reconnect | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| Heartbeat (ping/pong) | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| Channel multiplexing | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n| Reactive presence | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"triangle-alert\" size=\"16\"></ore-icon> Manual |\n| Reactive status | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Server lock-in | <ore-icon name=\"check\" size=\"16\"></ore-icon> None | <ore-icon name=\"check\" size=\"16\"></ore-icon> None | <ore-icon name=\"x\" size=\"16\"></ore-icon> Required |\n| Zero dependencies | <ore-icon name=\"triangle-alert\" size=\"16\"></ore-icon> ripple | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n\n<div class=\"decision-callout\">\n\n**Use Pulse when** you need typed, multiplexed real-time messaging with reactive state and a clean disposal lifecycle — without being locked to a specific server stack.\n\n**Consider native WebSocket when** you need the absolute minimum footprint and are building a one-off, untyped connection with no reuse patterns.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/pulse @vielzeug/ripple\n```\n\n```sh [npm]\nnpm install @vielzeug/pulse @vielzeug/ripple\n```\n\n```sh [yarn]\nyarn add @vielzeug/pulse @vielzeug/ripple\n```\n\n:::\n\n## Quick Start\n\n```ts\nimport { createPulse } from '@vielzeug/pulse';\nimport { effect } from '@vielzeug/ripple';\n\ntype ServerEvents = {\n 'chat:message': { user: string; text: string };\n 'user:joined': { userId: string };\n};\n\ntype ClientEvents = {\n 'chat:send': { text: string };\n};\n\nconst pulse = createPulse<ServerEvents, ClientEvents>('wss://api.example.com/ws', {\n reconnect: { maxAttempts: 5, delay: (n) => Math.min(1000 * 2 ** n, 30_000) },\n heartbeat: true,\n});\n\n// Reactive status via ripple signal\neffect(() => console.log('connection:', pulse.status.value));\n\n// Typed server events\npulse.on('chat:message', ({ user, text }) => console.log(`${user}: ${text}`));\n\n// Typed client messages\npulse.send('chat:send', { text: 'Hello!' });\n\n// Isolated channel namespace\nconst notif = pulse.channel<{ alert: { level: string; msg: string } }>('notifications');\nnotif.on('alert', ({ level, msg }) => showNotification(level, msg));\n\n// Reactive presence tracking\nconst lobby = pulse.presence<{ name: string; status: string }>('lobby');\neffect(() => console.log('online:', [...lobby.state.value.keys()]));\nlobby.update({ name: 'Alice', status: 'active' });\n\n// Clean disposal\nusing _ = pulse;\n```\n\n## Features\n\n<div class=\"features-grid\">\n\n- **Typed event maps** — `TServer` and `TClient` generics enforce payload types on both sides of the wire\n- **`on()` / `once()` / `wait()`** — persistent, one-shot, and async-await event subscriptions\n- **`channel()`** — isolated namespaces multiplexed over the shared connection; **same name returns the same object** (memoized); auto-resubscribed on reconnect; `dispose()` sends an `unsubscribe` frame\n- **`join()` / `leave()`** — room membership with server-confirmation promises; optional `timeout` and `AbortSignal` support\n- **`presence()`** — reactive `Signal<Map<memberId, T>>` state, with `onJoin`/`onLeave` callbacks and `update()` for broadcasting state\n- **Middleware pipeline** — intercept every outgoing `send()` call; omit `next()` to suppress\n- **Auto-reconnect** — exponential backoff (full-jitter by default), configurable `maxAttempts`, custom `delay` function, and `onReconnect` callback\n- **Heartbeat** — configurable ping/pong keep-alive with dead-connection detection and automatic reconnect trigger\n- **Reactive `status` signal** — `'connecting' | 'open' | 'reconnecting' | 'closed'` exposed as a ripple `Readable`\n- **Reactive `rooms` signal** — current room membership as a `Readable<ReadonlySet<string>>`\n- **`disposalSignal`** — `AbortSignal` that fires on `dispose()`; ties external cleanup to the connection lifetime\n- **`dispose()` and `[Symbol.dispose]`** — deterministic teardown; closes the socket, clears all listeners, aborts pending `wait()` calls\n- **Message buffering** — `buffer: true` queues outgoing frames while disconnected and flushes on reconnect; configurable `maxSize`\n- **Lazy connection** — `lazy: true` defers the initial connection until `connect()` is called explicitly\n- **Protocol-agnostic** — works with any WebSocket server that speaks the Pulse JSON frame format\n- **Single dependency** — only requires `@vielzeug/ripple` for reactive state\n\n</div>\n\n## Documentation\n\n<div class=\"doc-links\">\n\n- [Usage Guide](./usage.md)\n- [API Reference](./api.md)\n- [Examples](./examples.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Ripple](/ripple/) — the reactive signal library powering `pulse.status`, `pulse.rooms`, and `presence.state`\n- [Herald](/herald/) — typed in-process event bus; complement Pulse by bridging incoming WebSocket events to application-wide bus dispatches\n- [Courier](/courier/) — typed HTTP client for the request/response traffic that runs alongside your WebSocket connection\n- [Clockwork](/clockwork/) — finite state machine; model complex reconnection or auth-handshake logic as a proper state machine\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
|
|
5
|
+
"api": "---\ntitle: Pulse — API Reference\ndescription: Complete API reference for @vielzeug/pulse.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| -------------------- | --------------------------------------------- | -------------- | ------------------------------------------------------------------------- |\n| `createPulse()` | Create a typed WebSocket client instance | Sync | Connects immediately by default; pass `lazy: true` to defer |\n| `pulse.on()` | Subscribe to a typed server event | Sync | Returns an `Unsubscribe`; always call it on component teardown |\n| `pulse.once()` | One-shot server event subscription | Sync | Listener auto-removes after first fire |\n| `pulse.send()` | Send a typed client event | Sync | Buffered when `buffer: true`; dropped (dev warn) otherwise |\n| `pulse.wait()` | Await the next server event | Async | Rejects with `PulseAbortError` on disposal; use `timeout` for a deadline |\n| `pulse.connect()` | Open the connection explicitly | Async | Required when `lazy: true`; otherwise called automatically on creation |\n| `pulse.disconnect()` | Close without triggering reconnect | Sync | Pass code `1000` for a clean close |\n| `pulse.join()` | Join a room; resolves on server confirmation | Async | Rejects with `PulseAbortError` if pulse is disposed before server replies |\n| `pulse.leave()` | Leave a room; resolves on server confirmation | Async | Room is removed from `pulse.rooms` only after server confirms |\n| `pulse.channel()` | Create an isolated channel namespace | Sync | Same name returns the **same** object; `dispose()` sends unsubscribe |\n| `pulse.presence()` | Reactive presence channel for a room | Sync | Implicitly joins the room; `dispose()` to stop tracking |\n| `pulse.dispose()` | Permanently close and release all resources | Sync | Idempotent; also aborts `disposalSignal` |\n\n## Package Entry Point\n\n| Import | Purpose |\n| ----------------- | ---------------------------- |\n| `@vielzeug/pulse` | All public exports and types |\n\n## `createPulse()`\n\n```ts\ncreatePulse<TServer extends MessageMap = MessageMap, TClient extends MessageMap = MessageMap>(\n url: string,\n opts?: PulseOptions,\n): Pulse<TServer, TClient>\n```\n\nCreates and returns a new `Pulse<TServer, TClient>` instance. The WebSocket connection opens immediately on creation.\n\n**Parameters:**\n\n| Parameter | Type | Description |\n| --------- | -------------- | ---------------------------------- |\n| `url` | `string` | WebSocket server URL (`wss://…`) |\n| `opts` | `PulseOptions` | Optional configuration (see below) |\n\n**Parameters — `PulseOptions`:**\n\n| Option | Type | Default | Description |\n| ------------- | ---------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------ |\n| `buffer` | `boolean \\| BufferOptions` | `false` | `true` uses defaults (`maxSize: 50`); buffers outgoing frames while disconnected, flushes on reconnect |\n| `heartbeat` | `boolean \\| HeartbeatOptions` | `false` | `true` uses defaults; `false` disables; object for custom interval/timeout |\n| `lazy` | `boolean` | `false` | `true` defers the initial connection until `connect()` is called explicitly |\n| `middleware` | `readonly Middleware[]` | `[]` | Functions run on every outgoing `send()` before the message is written to the socket |\n| `onClose` | `(code: number, reason: string) => void` | — | Called when the connection is closed by either side |\n| `onError` | `(error: Error) => void` | — | Called on a WebSocket error event; errors almost always precede a close |\n| `onMessage` | `(event: MessageEvent) => void` | — | Called with every raw `MessageEvent` before parsing; useful for low-level debugging |\n| `onOpen` | `() => void` | — | Called when the connection is established or re-established |\n| `onReconnect` | `(attempt: number) => void` | — | Called at the start of each reconnect attempt; `attempt` is 1-based |\n| `protocols` | `string \\| string[]` | — | Sub-protocols passed to the `WebSocket` constructor |\n| `reconnect` | `boolean \\| ReconnectOptions` | `false` | `true` uses defaults; `false` disables; object for custom delay/maxAttempts |\n\n**Returns:** `Pulse<TServer, TClient>`\n\n**Example:**\n\n```ts\nimport { createPulse } from '@vielzeug/pulse';\n\ntype ServerEvents = { 'chat:message': { user: string; text: string } };\ntype ClientEvents = { 'chat:send': { text: string } };\n\nconst pulse = createPulse<ServerEvents, ClientEvents>('wss://api.example.com/ws', {\n reconnect: { maxAttempts: 5 },\n heartbeat: true,\n onOpen: () => console.log('connected'),\n onClose: (code, reason) => console.log('closed', code, reason),\n});\n```\n\n## Pulse Interface\n\n### `pulse.status`\n\nType: `Readable<PulseStatus>`\n\nReactive connection status. Subscribe with ripple `effect()` to react to status changes.\n\n```ts\nimport { effect } from '@vielzeug/ripple';\n\neffect(() => updateStatusBadge(pulse.status.value));\n\n// Read without subscribing\nconsole.log(pulse.status.value); // 'connecting' | 'open' | 'reconnecting' | 'closed'\n```\n\n---\n\n### `pulse.rooms`\n\nType: `Readable<ReadonlySet<string>>`\n\nReactive set of rooms the client is currently a confirmed member of.\n\n```ts\nimport { computed } from '@vielzeug/ripple';\n\nconst roomCount = computed(() => pulse.rooms.value.size);\n```\n\n---\n\n### `pulse.disposed`\n\nType: `readonly boolean`\n\n`true` after `dispose()` has been called.\n\n---\n\n### `pulse.disposalSignal`\n\nType: `readonly AbortSignal`\n\nAn `AbortSignal` that aborts when `dispose()` is called. Use it to tie external lifetimes to the connection.\n\n```ts\n// Cancel a fetch when the pulse is disposed\nfetch('/api/stream', { signal: pulse.disposalSignal });\n```\n\n---\n\n### `pulse.on()`\n\n```ts\non<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe\n```\n\nSubscribe to a typed server event. Returns an `Unsubscribe` function; call it to remove the listener.\n\n| Parameter | Type | Description |\n| --------- | ------------------------------- | -------------------------- |\n| `event` | `K` (EventKey of TServer) | Server event name |\n| `handler` | `(payload: TServer[K]) => void` | Callback for each delivery |\n\n**Returns:** `Unsubscribe`\n\n```ts\nconst unsub = pulse.on('chat:message', ({ user, text }) => appendToLog(user, text));\nunsub(); // remove when done\n```\n\n---\n\n### `pulse.once()`\n\n```ts\nonce<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe\n```\n\nRegisters a listener that fires exactly once, then removes itself. Returns an `Unsubscribe` for early cancellation.\n\n```ts\npulse.once('user:joined', ({ userId }) => showWelcome(userId));\n```\n\n---\n\n### `pulse.send()`\n\n```ts\nsend<K extends EventKey<TClient>>(event: K, payload: TClient[K]): void\n```\n\nSend a typed event to the server. When the connection is not open:\n\n- If `buffer: true` is set, the message is queued and flushed on the next successful open.\n- Otherwise the message is dropped and a dev warning is emitted.\n\n```ts\npulse.send('chat:send', { text: 'Hello!' });\n```\n\n---\n\n### `pulse.wait()`\n\n```ts\nwait<K extends EventKey<TServer>>(event: K, opts?: { signal?: AbortSignal; timeout?: number }): Promise<TServer[K]>\n```\n\nReturns a promise that resolves with the payload of the next server emission of `event`.\n\n| Parameter | Type | Description |\n| -------------- | ------------- | ------------------------------------------------- |\n| `event` | `K` | Server event name to await |\n| `opts.signal` | `AbortSignal` | Optional; rejects with `PulseAbortError` when it fires |\n| `opts.timeout` | `number` | Optional; rejects with `PulseTimeoutError` after ms |\n\n**Rejects when:**\n\n- `opts.signal` fires — rejects with `PulseAbortError`\n- `opts.timeout` elapses — rejects with `PulseTimeoutError`\n- The pulse is disposed — rejects with `PulseAbortError`\n\n```ts\nconst msg = await pulse.wait('chat:message', { timeout: 5_000 });\n```\n\n---\n\n### `pulse.connect()`\n\n```ts\nconnect(): Promise<void>\n```\n\nOpens the WebSocket connection. Resolves when the connection is open. Returns immediately if already open.\n\n> **Note:** When `lazy: false` (default) the connection opens automatically on construction. Use `lazy: true` to defer and call `connect()` explicitly.\n\n**Rejects when:**\n\n- Already disposed — `PulseDisposedError`\n- Socket closes before it opens — `PulseConnectionError`\n- Socket error — `PulseConnectionError`\n\n```ts\nawait pulse.connect();\n```\n\n---\n\n### `pulse.disconnect()`\n\n```ts\ndisconnect(code?: number, reason?: string): void\n```\n\nCloses the WebSocket without triggering auto-reconnect. Status transitions to `'closed'`.\n\n| Parameter | Type | Default | Description |\n| --------- | -------- | ------- | --------------------- |\n| `code` | `number` | `1000` | WebSocket close code |\n| `reason` | `string` | `''` | Human-readable reason |\n\n```ts\npulse.disconnect(1000, 'user signed out');\n```\n\n---\n\n### `pulse.join()`\n\n```ts\njoin(room: string, opts?: { signal?: AbortSignal; timeout?: number }): Promise<void>\n```\n\nRequests to join a room. Resolves when the server confirms with a `joined` frame. The room is added to `pulse.rooms` on confirmation.\n\n| Parameter | Type | Description |\n| -------------- | ------------- | ------------------------------------------------- |\n| `room` | `string` | Room name |\n| `opts.signal` | `AbortSignal` | Optional; rejects with `PulseAbortError` on fire |\n| `opts.timeout` | `number` | Optional; rejects with `PulseTimeoutError` after ms |\n\n**Rejects when:**\n\n- Already disposed — `PulseDisposedError`\n- The signal fires — `PulseAbortError`\n- `opts.timeout` elapses — `PulseTimeoutError`\n- The pulse is disposed before confirmation — `PulseAbortError`\n\n```ts\nawait pulse.join('lobby', { timeout: 5_000 });\n```\n\n---\n\n### `pulse.leave()`\n\n```ts\nleave(room: string, opts?: { signal?: AbortSignal; timeout?: number }): Promise<void>\n```\n\nRequests to leave a room. Resolves when the server confirms with a `left` frame. The room is removed from `pulse.rooms` on confirmation.\n\nIf the socket is not open, `leave()` connects first (mirroring `join()` behaviour).\n\n**Rejects when:**\n\n- Already disposed — `PulseDisposedError`\n- The signal fires — `PulseAbortError`\n- `opts.timeout` elapses — `PulseTimeoutError`\n- Connection fails — `PulseConnectionError`\n\n```ts\nawait pulse.leave('lobby');\n```\n\n---\n\n### `pulse.channel()`\n\n```ts\nchannel<TChServer extends MessageMap = TServer, TChClient extends MessageMap = TClient>(\n name: string,\n): PulseChannel<TChServer, TChClient>\n```\n\nReturns a `PulseChannel` scoped to `name`. Multiple calls with the **same name return the same object** — the channel is memoized. The subscription is automatically re-sent on reconnect. Disposing the channel sends an `unsubscribe` frame and evicts it from the cache.\n\n```ts\nconst chat = pulse.channel<ChatServer, ChatClient>('chat');\nconst same = pulse.channel<ChatServer, ChatClient>('chat');\nconsole.log(chat === same); // true\n```\n\n---\n\n### `pulse.presence()`\n\n```ts\npresence<T>(room: string): PresenceChannel<T>\n```\n\nReturns a `PresenceChannel<T>` that tracks all members' state in `room`. Implicitly joins the room.\n\n```ts\nconst lobby = pulse.presence<{ name: string }>('lobby');\n```\n\n---\n\n### `pulse.dispose()`\n\n```ts\ndispose(): void\n```\n\nPermanently closes the connection and releases all resources:\n\n- Closes the WebSocket with code `1000`\n- Clears all listeners\n- Rejects all pending `wait()`, `join()`, and `leave()` promises with `PulseDisposedError`\n- Rejects any in-flight `connect()` with `PulseDisposedError`\n- Aborts `disposalSignal`\n\nIdempotent — safe to call multiple times.\n\n---\n\n### `pulse[Symbol.dispose]()`\n\n```ts\n[Symbol.dispose](): void\n```\n\nAlias for `dispose()`. Enables the `using` keyword:\n\n```ts\n{\n using pulse = createPulse('wss://api.example.com/ws');\n // ...\n} // dispose() called automatically\n```\n\n## PulseChannel Interface\n\nObtain via `pulse.channel(name)`.\n\n### `channel.on()`\n\n```ts\non<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe\n```\n\nSubscribe to a server event scoped to this channel. Listeners are auto-removed on `channel.dispose()`.\n\n---\n\n### `channel.once()`\n\n```ts\nonce<K extends EventKey<TServer>>(event: K, handler: (payload: TServer[K]) => void): Unsubscribe\n```\n\nOne-shot subscription scoped to this channel.\n\n---\n\n### `channel.send()`\n\n```ts\nsend<K extends EventKey<TClient>>(event: K, payload: TClient[K]): void\n```\n\nSend a typed message to the server scoped to this channel. No-op if the pulse connection is not open.\n\n---\n\n### `channel.wait()`\n\n```ts\nwait<K extends EventKey<TServer>>(event: K, opts?: { signal?: AbortSignal; timeout?: number }): Promise<TServer[K]>\n```\n\nResolves on the next emission of the given event within this channel. Rejects when:\n\n- `opts.signal` fires — `PulseAbortError`\n- `opts.timeout` elapses — `PulseTimeoutError`\n- The channel is disposed — `PulseAbortError`\n\n---\n\n### `channel.dispose()`\n\n```ts\ndispose(): void\n```\n\nRemoves all channel listeners, sends an `unsubscribe` frame, and evicts the channel from the memoization cache. The underlying connection is unaffected.\n\n---\n\n### `channel.disposed`\n\nType: `readonly boolean`\n\n`true` after `dispose()` has been called.\n\n---\n\n### `channel.disposalSignal`\n\nType: `readonly AbortSignal`\n\nAn `AbortSignal` that aborts when `dispose()` is called.\n\n```ts\nfetch('/api', { signal: channel.disposalSignal });\n```\n\n---\n\n### `channel.name`\n\nType: `readonly string`\n\nThe channel name passed to `pulse.channel()`.\n\n---\n\n### `channel[Symbol.dispose]()`\n\nAlias for `dispose()`. Enables `using` declarations.\n\n## PresenceChannel Interface\n\nObtain via `pulse.presence(room)`.\n\n### `presence.state`\n\nType: `Readable<ReadonlyMap<string, T>>`\n\nReactive map of `memberId → state`. Updates whenever any member joins, leaves, or changes state.\n\n```ts\nimport { effect } from '@vielzeug/ripple';\n\neffect(() => {\n for (const [id, state] of lobby.state.value) {\n renderAvatar(id, state);\n }\n});\n```\n\n---\n\n### `presence.onJoin()`\n\n```ts\nonJoin(handler: (memberId: string, state: T) => void): Unsubscribe\n```\n\nRegisters a callback fired whenever a new member joins with their initial state. Returns an `Unsubscribe`.\n\n---\n\n### `presence.onLeave()`\n\n```ts\nonLeave(handler: (memberId: string) => void): Unsubscribe\n```\n\nRegisters a callback fired whenever a member leaves. Returns an `Unsubscribe`.\n\n---\n\n### `presence.update()`\n\n```ts\nupdate(state: T): void\n```\n\nBroadcasts this client's presence state to all room members. Also serves as an implicit join if not already in the room.\n\n---\n\n### `presence.room`\n\nType: `readonly string`\n\nThe room name passed to `pulse.presence()`.\n\n---\n\n### `presence.disposed`\n\nType: `readonly boolean`\n\n`true` after `dispose()` has been called.\n\n---\n\n### `presence.dispose()`\n\n```ts\ndispose(): void\n```\n\nStops tracking the room, removes all join/leave callbacks, and sends a `leave` frame to the server.\n\n---\n\n### `presence.disposalSignal`\n\nType: `readonly AbortSignal`\n\nAn `AbortSignal` that aborts when `dispose()` is called.\n\n---\n\n### `presence[Symbol.dispose]()`\n\nAlias for `dispose()`. Enables `using` declarations.\n\n## Types\n\n```ts\n/** A map of event name → payload type. */\ntype MessageMap = Record<string, unknown>;\n\n/** Extract valid event key strings from a MessageMap. */\ntype EventKey<T extends MessageMap> = keyof T & string;\n\n/** A function that removes a listener subscription. */\ntype Unsubscribe = () => void;\n\n/** Lifecycle state of a Pulse connection. */\ntype PulseStatus = 'connecting' | 'open' | 'reconnecting' | 'closed';\n\n/** A read-only view of a Map — callers cannot mutate the entries. */\ntype ReadonlyMap<K, V> = Omit<Map<K, V>, 'clear' | 'delete' | 'set'>;\n```\n\n```ts\ntype ReconnectOptions = {\n /**\n * Delay strategy between attempts (ms).\n * number = fixed delay; function = (attempt: number) => ms.\n * Defaults to full-jitter exponential backoff capped at 30 s.\n */\n delay?: number | ((attempt: number) => number);\n /** Maximum reconnect attempts. Default: 5. */\n maxAttempts?: number;\n};\n```\n\n```ts\ntype HeartbeatOptions = {\n /** Interval between pings in ms. Default: 30_000. */\n interval?: number;\n /** How long to wait for a pong before treating the connection as dead. Default: 5_000. */\n timeout?: number;\n};\n```\n\n```ts\n/**\n * Intercepts outgoing messages. Call next() to allow; omit to suppress.\n */\ntype Middleware = (event: string, payload: unknown, next: () => void) => void;\n```\n\n```ts\ntype BufferOptions = {\n /** Maximum number of frames to buffer. Oldest evicted when full. Default: 50. */\n maxSize?: number;\n};\n```\n\n```ts\ntype PulseOptions = {\n buffer?: boolean | BufferOptions;\n heartbeat?: boolean | HeartbeatOptions;\n lazy?: boolean;\n middleware?: readonly Middleware[];\n onClose?: (code: number, reason: string) => void;\n onError?: (error: Error) => void;\n onMessage?: (event: MessageEvent) => void;\n onOpen?: () => void;\n onReconnect?: (attempt: number) => void;\n protocols?: string | string[];\n reconnect?: boolean | ReconnectOptions;\n};\n```\n\n## Errors\n\nAll errors extend `PulseError`. Use `instanceof PulseError` to catch any pulse-originated error in one branch.\n\nAll error constructors accept a trailing `opts?: ErrorOptions`, so you can chain a `cause`: `new PulseTimeoutError('chat:message', { cause })`.\n\n| Class | Extends | Triggers when | Notable properties |\n| ---------------------- | ------------ | --------------------------------------------------------------------------- | ------------------ |\n| `PulseError` | `Error` | Base class — never thrown directly | — |\n| `PulseConnectionError` | `PulseError` | Connection cannot be established or is lost with reconnect budget exhausted | `url: string` |\n| `PulseTimeoutError` | `PulseError` | `wait()` `timeout` elapses before the event arrives | `event: string` |\n| `PulseAbortError` | `PulseError` | `wait()`, `join()`, or `leave()` is aborted via signal or pulse disposal | — |\n| `PulseDisposedError` | `PulseError` | A method is called on a disposed instance or channel | — |\n| `PulseProtocolError` | `PulseError` | The server sends a frame that cannot be parsed or has no `type` field | `raw: unknown` |\n\n```ts\nimport { PulseAbortError, PulseError, PulseTimeoutError } from '@vielzeug/pulse';\n\ntry {\n await pulse.wait('chat:message', { timeout: 5_000 });\n} catch (err) {\n if (err instanceof PulseTimeoutError) {\n console.warn('no message in 5 s, event:', err.event);\n } else if (err instanceof PulseAbortError) {\n console.log('aborted or pulse disposed');\n } else if (err instanceof PulseError) {\n console.error('unexpected pulse error', err);\n }\n}\n```\n",
|
|
6
|
+
"usage": "---\ntitle: Pulse — Usage Guide\ndescription: Connection management, typed messaging, channels, rooms, presence, middleware, reconnect, and heartbeat for @vielzeug/pulse.\n---\n\n[[toc]]\n\n::: tip New to Pulse?\nStart with the [Overview](./index.md) for installation and a quick start, then return here for in-depth usage patterns.\n:::\n\n## Basic Usage\n\nA message map is a plain TypeScript type where each key is an event name and each value is the payload type. Define separate maps for server-to-client and client-to-server traffic.\n\n```ts\nimport { createPulse } from '@vielzeug/pulse';\n\ntype ServerEvents = {\n 'chat:message': { user: string; text: string };\n 'user:joined': { userId: string };\n};\n\ntype ClientEvents = {\n 'chat:send': { text: string };\n};\n\nconst pulse = createPulse<ServerEvents, ClientEvents>('wss://api.example.com/ws');\n\npulse.on('chat:message', ({ user, text }) => {\n console.log(`${user}: ${text}`); // payload is fully typed\n});\n\npulse.send('chat:send', { text: 'Hello!' });\n```\n\n## Connection Management\n\n### Reactive status\n\n`pulse.status` is a ripple `Reactive<PulseStatus>`. Use `effect()` to react to connection state changes.\n\n```ts\nimport { effect } from '@vielzeug/ripple';\n\n// 'connecting' | 'open' | 'reconnecting' | 'closed'\neffect(() => {\n document.title = pulse.status.value === 'open' ? 'Live' : 'Reconnecting…';\n});\n```\n\n### Explicit connect and disconnect\n\nBy default the connection opens as soon as `createPulse` is called. Pass `lazy: true` to defer it until you call `connect()` explicitly — useful when the connection should not open until after a user gesture or auth check.\n\n```ts\n// Default — connects immediately\nconst pulse = createPulse('wss://api.example.com/ws');\n\n// Lazy — deferred until connect() is called\nconst pulse = createPulse('wss://api.example.com/ws', { lazy: true });\nawait pulse.connect(); // resolves when the socket is open\n\npulse.disconnect(1000, 'user logged out'); // clean close, no reconnect\n```\n\n`disconnect()` closes the socket and sets status to `'closed'` without triggering auto-reconnect.\n\n## Subscribing to Server Events\n\n### `on()` — Persistent listener\n\n`on()` subscribes to every future emission of an event. It returns an `Unsubscribe` function.\n\n```ts\nconst unsub = pulse.on('chat:message', ({ user, text }) => {\n appendToChat(user, text);\n});\n\n// Remove the listener when no longer needed\nunsub();\n```\n\n### `once()` — One-shot listener\n\n`once()` fires exactly once, then removes itself.\n\n```ts\npulse.once('user:joined', ({ userId }) => {\n showWelcomeBanner(userId);\n});\n```\n\n### `wait()` — Async one-shot\n\n`wait()` returns a promise that resolves with the next emitted payload. Pass `signal` or `timeout` to add a deadline.\n\n```ts\n// Wait for the next server-push notification\nconst msg = await pulse.wait('chat:message');\n\n// With a timeout (ms)\nconst msg = await pulse.wait('chat:message', { timeout: 5_000 });\n\n// With an AbortSignal\nconst msg = await pulse.wait('chat:message', { signal: AbortSignal.timeout(5_000) });\n```\n\n`wait()` rejects with `PulseTimeoutError` when `timeout` elapses, with `PulseAbortError` when the signal fires, and with `PulseAbortError` when the pulse is disposed before the event arrives.\n\n## Channels\n\nA channel is an isolated message namespace multiplexed over the same WebSocket connection. Use separate channels to scope events to logical subsystems.\n\n```ts\n// Separate type maps per channel\ntype NotifServer = { alert: { level: 'info' | 'warn' | 'error'; msg: string } };\ntype ChatServer = { message: { user: string; text: string } };\ntype ChatClient = { send: { text: string } };\n\nconst notif = pulse.channel<NotifServer>('notifications');\nconst chat = pulse.channel<ChatServer, ChatClient>('chat');\n\nnotif.on('alert', ({ level, msg }) => showToast(level, msg));\n\nchat.on('message', ({ user, text }) => appendToLog(user, text));\nchat.send('send', { text: 'hi' });\n```\n\nMultiple calls with the same name return the **same channel object** — the channel is memoized. Dispose it once to fully remove the subscription and send an `unsubscribe` frame. After disposal, calling `pulse.channel()` with the same name creates a fresh channel.\n\n### Channel disposal\n\nDisposing a channel removes all its listeners, sends an `unsubscribe` frame to the server, and evicts it from the cache. The underlying connection is unaffected.\n\n```ts\nusing chat = pulse.channel<ChatServer, ChatClient>('chat');\n\n// — or manually:\nchat.dispose();\nchat.disposed; // true\n// pulse.channel('chat') now returns a fresh channel\n```\n\n## Rooms\n\n`join()` requests membership in a named room. It resolves when the server confirms with a `joined` frame.\n\n```ts\nawait pulse.join('lobby');\nconsole.log(pulse.rooms.value.has('lobby')); // true — reactive signal\n\nawait pulse.leave('lobby');\nconsole.log(pulse.rooms.value.has('lobby')); // false\n```\n\nPass a `timeout` or `AbortSignal` to bound the wait:\n\n```ts\n// Reject with PulseTimeoutError if server doesn't confirm within 5 s\nawait pulse.join('lobby', { timeout: 5_000 });\n\n// Or cancel with an AbortSignal\nconst ctrl = new AbortController();\nconst joinP = pulse.join('arena', { signal: ctrl.signal });\nctrl.abort(); // rejects joinP with PulseAbortError\n```\n\n`pulse.rooms` is a `Readable<ReadonlySet<string>>`. Derive computed views with ripple:\n\n```ts\nimport { computed } from '@vielzeug/ripple';\n\nconst roomCount = computed(() => pulse.rooms.value.size);\n```\n\n## Presence\n\n`presence()` returns a presence channel for a room. It implicitly joins the room and begins tracking members.\n\n```ts\ntype MemberState = { name: string; status: 'online' | 'away' };\n\nconst lobby = pulse.presence<MemberState>('lobby');\n\n// Reactive member map — updates on every join, leave, or state change\nimport { effect } from '@vielzeug/ripple';\neffect(() => {\n for (const [id, state] of lobby.state.value) {\n console.log(id, state.name, state.status);\n }\n});\n\n// React to membership events\nlobby.onJoin((memberId, state) => showJoinBanner(state.name));\nlobby.onLeave((memberId) => removeAvatarFromList(memberId));\n\n// Broadcast your own state (also serves as join confirmation)\nlobby.update({ name: 'Alice', status: 'online' });\n```\n\n### Presence disposal\n\nDisposing a presence channel stops tracking, removes all join/leave callbacks, and sends a `leave` frame to the server.\n\n```ts\nusing _ = lobby;\n// — or —\nlobby.dispose(); // also sends 'leave' frame\n```\n\n## Middleware\n\nMiddleware intercepts every outgoing `send()` before the message hits the socket. Call `next()` to allow the send; omit it to suppress.\n\n```ts\nconst pulse = createPulse<ServerEvents, ClientEvents>('wss://api.example.com/ws', {\n middleware: [\n // Logging middleware\n (event, payload, next) => {\n console.debug('[ws out]', event, payload);\n next();\n },\n // Rate-limiting middleware\n (event, _payload, next) => {\n if (rateLimiter.allow(event)) next();\n // omit next() to drop the message\n },\n ],\n});\n```\n\nMiddleware only applies to application messages sent via `send()`. Internal frames (ping, join, subscribe) bypass the pipeline.\n\n## Reconnect & Heartbeat\n\n### Auto-reconnect\n\nEnable reconnect with `true` (uses defaults) or a `ReconnectOptions` object.\n\n```ts\nconst pulse = createPulse('wss://api.example.com/ws', {\n reconnect: {\n maxAttempts: 10, // default: 5\n delay: 1_000, // fixed 1 s delay — or a function:\n // delay: (n) => Math.min(500 * 2 ** n, 30_000)\n },\n});\n```\n\nWhen reconnect is enabled, an unexpected close transitions `status` to `'reconnecting'`. After the budget is exhausted without success, `status` moves to `'closed'`.\n\nThe default `delay` is full-jitter exponential backoff: `Math.random() * Math.min(1000 * 2^n, 30_000)`.\n\n### Heartbeat\n\nEnable heartbeat with `true` (uses defaults) or a `HeartbeatOptions` object.\n\n```ts\nconst pulse = createPulse('wss://api.example.com/ws', {\n heartbeat: {\n interval: 30_000, // ms between pings — default: 30_000\n timeout: 5_000, // ms to wait for pong before treating connection as dead — default: 5_000\n },\n});\n```\n\nWhen a pong is not received within `timeout` ms, the socket is closed and — if reconnect is enabled — a reconnect attempt is triggered.\n\n## Disposal\n\nDisposing a pulse instance closes the WebSocket, clears all listeners, rejects pending `wait()` / `join()` / `leave()` promises, and aborts the `disposalSignal`.\n\n```ts\n// using declaration — dispose() called automatically at block exit\nusing pulse = createPulse('wss://api.example.com/ws');\n\n// — or manually:\npulse.dispose();\npulse.disposed; // true\n```\n\n### `disposalSignal`\n\n`pulse.disposalSignal` is an `AbortSignal` that fires when `dispose()` is called. Use it to tie external cleanup to the connection lifetime.\n\n```ts\n// Automatically cancel a fetch when the pulse is disposed\nfetch('/api/init', { signal: pulse.disposalSignal });\n\n// Unsubscribe from another system when pulse tears down\nexternalBus.on('theme', applyTheme, { signal: pulse.disposalSignal });\n```\n\n## Framework Integration\n\n::: code-group\n\n```ts [React]\nimport { createPulse } from '@vielzeug/pulse';\nimport { useEffect, useSyncExternalStore } from 'react';\n\nfunction usePulseStatus(pulse: ReturnType<typeof createPulse>) {\n return useSyncExternalStore(\n (cb) => {\n const unsub = pulse.status.subscribe(cb);\n return unsub;\n },\n () => pulse.status.value,\n );\n}\n\nfunction Chat() {\n const status = usePulseStatus(pulse);\n\n useEffect(() => {\n const unsub = pulse.on('chat:message', ({ user, text }) => {\n appendToLog(user, text);\n });\n return unsub;\n }, []);\n\n return <div>Status: {status}</div>;\n}\n```\n\n```ts [Vue 3]\nimport { createPulse } from '@vielzeug/pulse';\nimport { onUnmounted, ref, watchEffect } from 'vue';\n\nexport function usePulse(url: string) {\n const pulse = createPulse(url, { reconnect: true });\n const status = ref(pulse.status.value);\n\n const unsub = pulse.status.subscribe((s) => {\n status.value = s;\n });\n\n onUnmounted(() => pulse.dispose());\n\n return { pulse, status };\n}\n```\n\n```ts [Svelte]\nimport { createPulse } from '@vielzeug/pulse';\nimport { onDestroy } from 'svelte';\nimport { readable } from 'svelte/store';\n\nconst pulse = createPulse('wss://api.example.com/ws', { reconnect: true });\n\n// Wrap ripple signal in a Svelte readable store\nconst status = readable(pulse.status.value, (set) => {\n return pulse.status.subscribe(set);\n});\n\nonDestroy(() => pulse.dispose());\n```\n\n:::\n\n## Working with Other Vielzeug Libraries\n\n### Herald — bridge WebSocket events to an app bus\n\nRoute incoming server events through a Herald bus so the rest of your application doesn't need to know about the WebSocket.\n\n```ts\nimport { createPulse } from '@vielzeug/pulse';\nimport { createBus } from '@vielzeug/herald';\n\ntype AppEvents = {\n 'chat:message': { user: string; text: string };\n};\n\nconst pulse = createPulse<AppEvents>('wss://api.example.com/ws');\nconst bus = createBus<AppEvents>();\n\n// Forward all WebSocket events to the Herald bus\npulse.on('chat:message', (payload) => bus.emit('chat:message', payload));\n\n// The rest of the app only knows about the bus\nbus.on('chat:message', ({ user, text }) => appendToLog(user, text));\n\n// Dispose both together\npulse.disposalSignal.addEventListener('abort', () => bus.dispose(), { once: true });\n```\n\n### Ripple — derive computed views from reactive signals\n\n```ts\nimport { computed, effect } from '@vielzeug/ripple';\n\nconst pulse = createPulse<ServerEvents, ClientEvents>('wss://api.example.com/ws');\nconst lobby = pulse.presence<{ name: string }>('lobby');\n\nconst memberCount = computed(() => lobby.state.value.size);\nconst memberNames = computed(() => [...lobby.state.value.values()].map((s) => s.name));\n\neffect(() => {\n document.querySelector('#count')!.textContent = String(memberCount.value);\n});\n```\n\n## Message Buffering\n\nEnable `buffer: true` to queue outgoing frames while the connection is not open. The queue is flushed automatically on the next successful open.\n\n```ts\nconst pulse = createPulse('wss://api.example.com/ws', {\n reconnect: true,\n buffer: true, // queue up to 50 frames (default)\n // buffer: { maxSize: 20 }, // or a custom limit\n});\n\n// Messages sent during reconnect are queued, not dropped\npulse.send('chat:send', { text: 'Queued while offline' });\n```\n\nWhen the buffer is full, the **oldest** frame is evicted to make room for the new one. With buffering disabled (default), a `send()` while disconnected is a no-op and emits a dev-mode warning.\n\n## Best Practices\n\n- **Define message maps upfront.** Separate `ServerEvents` and `ClientEvents` types make protocol changes a compile error, not a runtime surprise.\n- **One `createPulse` instance per connection.** Multiple instances to the same URL open multiple sockets. Share a single instance across your application.\n- **Always dispose.** Call `pulse.dispose()` or use `using` to prevent socket and listener leaks in component/module teardown.\n- **Use `disposalSignal` to chain cleanups.** Pass `pulse.disposalSignal` to any external subscription or fetch so teardown is automatic.\n- **Enable reconnect for production.** `reconnect: true` uses sensible defaults; override `maxAttempts` and `delay` only when you have measured the right values.\n- **Scope messages with channels.** Use `channel()` when building features that own a domain namespace — it keeps listener cleanup isolated.\n- **Enable buffering when ordering matters.** Use `buffer: true` with reconnect so messages sent during brief disconnects are not silently dropped.\n",
|
|
7
|
+
"examples": "---\ntitle: Pulse — Examples\ndescription: Practical examples and recipes for @vielzeug/pulse.\n---\n\n## Examples\n\n- [Basic Connection](./examples/basic-connection.md)\n- [Channel Multiplexing](./examples/channels.md)\n- [Rooms and Presence](./examples/rooms-and-presence.md)\n- [Reconnect and Heartbeat](./examples/reconnect-and-heartbeat.md)\n- [Outgoing Middleware](./examples/middleware.md)\n"
|
|
8
|
+
},
|
|
9
|
+
"examples": [
|
|
10
|
+
{
|
|
11
|
+
"id": "channels",
|
|
12
|
+
"code": "import { createPulse } from '@vielzeug/pulse'\n\n// Isolated channel namespace — listeners and sends are scoped to 'chat'\nconst pulse = createPulse('wss://api.example.com/ws')\nconst chat = pulse.channel('chat')\n\n// Listeners scoped to the channel\nchat.on('message', ({ from, text }) => {\n console.log('[chat] ' + from + ': ' + text)\n})\n\n// Send scoped to the channel\nchat.send('send', { text: 'hey!' })\n\n// Wait with a per-event timeout\ntry {\n const msg = await chat.wait('message', { timeout: 3_000 })\n console.log('got:', msg.text)\n} catch (err) {\n console.log('channel wait timed out:', err.message)\n}\n\n// Disposing the channel removes all its listeners\n// but the underlying pulse connection stays open\nchat.dispose()\nconsole.log('channel disposed, pulse still open:', pulse.status.value)\n\npulse.dispose()",
|
|
13
|
+
"name": "Typed Channels"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "connect-and-send",
|
|
17
|
+
"code": "import { createPulse } from '@vielzeug/pulse'\n\n// Typed WebSocket client: on(), once(), send(), wait()\nconst pulse = createPulse('wss://api.example.com/ws', {\n reconnect: { maxAttempts: 5 },\n})\n\n// Subscribe before connecting — listeners are synchronous\nconst unsub = pulse.on('chat:message', ({ from, text }) => {\n console.log('[' + from + '] ' + text)\n})\n\n// One-shot listener: fires once and auto-removes\npulse.once('chat:message', (msg) => {\n console.log('first message:', msg.text)\n})\n\n// Connect; send when open\ntry {\n await pulse.connect()\n pulse.send('chat:send', { text: 'Hello, world!' })\n} catch (err) {\n console.log('connect failed:', err.message)\n}\n\n// Await next server event with a 5 s deadline\ntry {\n const msg = await pulse.wait('chat:message', { timeout: 500 })\n console.log('received:', msg.text)\n} catch (err) {\n console.log('wait ended:', err.message)\n}\n\nunsub()\npulse.dispose()",
|
|
18
|
+
"name": "Connect & Send"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "lifecycle",
|
|
22
|
+
"code": "import { createPulse, PulseDisposedError } from '@vielzeug/pulse'\n\n// Status signal, disposalSignal, and error handling on dispose\nconst pulse = createPulse('wss://api.example.com/ws', {\n reconnect: { delay: 1_000, maxAttempts: 3 },\n heartbeat: { interval: 30_000, timeout: 5_000 },\n onOpen: () => console.log('connected'),\n onClose: (code, reason) => console.log('closed', code, reason),\n})\n\n// status is a reactive signal: 'connecting' | 'open' | 'reconnecting' | 'closed'\nconsole.log('initial status:', pulse.status.value)\n\n// disposalSignal aborts when dispose() is called\npulse.disposalSignal.addEventListener('abort', () => {\n console.log('disposal signal fired')\n})\n\n// dispose() is idempotent — safe to call multiple times\npulse.dispose()\npulse.dispose()\nconsole.log('disposed:', pulse.disposed)\n\n// Methods reject with PulseDisposedError after dispose\ntry {\n await pulse.connect()\n} catch (err) {\n if (err instanceof PulseDisposedError) {\n console.log('connect() rejected with PulseDisposedError — correct')\n }\n}",
|
|
23
|
+
"name": "Lifecycle & Disposal"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "reconnect",
|
|
27
|
+
"code": "import { createPulse, PulseConnectionError } from '@vielzeug/pulse'\n\n// onReconnect fires at the start of each reconnect attempt (1-based)\n// Channels are automatically re-subscribed when the socket reopens.\nconst pulse = createPulse('wss://api.example.com/ws', {\n reconnect: { delay: 500, maxAttempts: 3 },\n onReconnect: (attempt) => {\n console.log('reconnect attempt #' + attempt)\n },\n onOpen: () => console.log('open — status:', pulse.status.value),\n onClose: (code) => console.log('closed, code:', code),\n})\n\n// Channel is tracked: re-subscribed automatically after every reconnect\nconst chat = pulse.channel('chat')\nchat.on('message', ({ from, text }) => console.log(from + ': ' + text))\n\n// Connect explicitly to observe the status\ntry {\n await pulse.connect()\n console.log('connected, status:', pulse.status.value)\n} catch (err) {\n if (err instanceof PulseConnectionError) {\n console.log('connection failed:', err.message)\n }\n}\n\nconsole.log('channel name:', chat.name)\nconsole.log('channel disposed?', chat.disposed)\n\n// Disposing a channel removes it from re-subscription tracking\nchat.dispose()\nconsole.log('channel disposed, pulse still running:', !pulse.disposed)\n\npulse.dispose()",
|
|
28
|
+
"name": "Reconnect & onReconnect"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": "rooms-presence",
|
|
32
|
+
"code": "import { createPulse } from '@vielzeug/pulse'\n\n// Reactive presence channel — implicitly joins 'lobby'\nconst pulse = createPulse('wss://api.example.com/ws')\nconst lobby = pulse.presence('lobby')\n\n// Subscribe to state changes manually (state.value is a ReadonlyMap)\nconst printMembers = () => {\n for (const [id, state] of lobby.state.value) {\n console.log(' ' + id + ': ' + state.name + ' (' + state.status + ')')\n }\n}\n\n// React to individual joins and leaves\nlobby.onJoin((id, state) => console.log(state.name + ' joined'))\nlobby.onLeave((id) => console.log(id + ' left'))\n\n// Broadcast our own presence\nlobby.update({ avatar: '/me.png', name: 'Alice', status: 'online' })\n\n// Explicit room management (join resolves on server confirmation)\ntry {\n await pulse.join('game-room')\n console.log('rooms:', [...pulse.rooms.value])\n await pulse.leave('game-room')\n console.log('rooms after leave:', [...pulse.rooms.value])\n} catch (err) {\n console.log('room op failed:', err.message)\n}\n\nlobby.dispose()\npulse.dispose()",
|
|
33
|
+
"name": "Rooms & Presence"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"typeSignatures": {
|
|
37
|
+
"BufferOptions": "export type {\n BufferOptions,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n Middleware,\n PresenceChannel,\n Pulse,\n PulseChannel,\n PulseOptions,\n PulseStatus,\n ReadonlyMap,\n ReconnectOptions,\n Unsubscribe,\n} from './types';",
|
|
38
|
+
"EventKey": "export type {\n BufferOptions,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n Middleware,\n PresenceChannel,\n Pulse,\n PulseChannel,\n PulseOptions,\n PulseStatus,\n ReadonlyMap,\n ReconnectOptions,\n Unsubscribe,\n} from './types';",
|
|
39
|
+
"HeartbeatOptions": "export type {\n BufferOptions,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n Middleware,\n PresenceChannel,\n Pulse,\n PulseChannel,\n PulseOptions,\n PulseStatus,\n ReadonlyMap,\n ReconnectOptions,\n Unsubscribe,\n} from './types';",
|
|
40
|
+
"MessageMap": "export type {\n BufferOptions,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n Middleware,\n PresenceChannel,\n Pulse,\n PulseChannel,\n PulseOptions,\n PulseStatus,\n ReadonlyMap,\n ReconnectOptions,\n Unsubscribe,\n} from './types';",
|
|
41
|
+
"Middleware": "export type {\n BufferOptions,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n Middleware,\n PresenceChannel,\n Pulse,\n PulseChannel,\n PulseOptions,\n PulseStatus,\n ReadonlyMap,\n ReconnectOptions,\n Unsubscribe,\n} from './types';",
|
|
42
|
+
"PresenceChannel": "export type {\n BufferOptions,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n Middleware,\n PresenceChannel,\n Pulse,\n PulseChannel,\n PulseOptions,\n PulseStatus,\n ReadonlyMap,\n ReconnectOptions,\n Unsubscribe,\n} from './types';",
|
|
43
|
+
"Pulse": "export type {\n BufferOptions,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n Middleware,\n PresenceChannel,\n Pulse,\n PulseChannel,\n PulseOptions,\n PulseStatus,\n ReadonlyMap,\n ReconnectOptions,\n Unsubscribe,\n} from './types';",
|
|
44
|
+
"PulseChannel": "export type {\n BufferOptions,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n Middleware,\n PresenceChannel,\n Pulse,\n PulseChannel,\n PulseOptions,\n PulseStatus,\n ReadonlyMap,\n ReconnectOptions,\n Unsubscribe,\n} from './types';",
|
|
45
|
+
"PulseOptions": "export type {\n BufferOptions,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n Middleware,\n PresenceChannel,\n Pulse,\n PulseChannel,\n PulseOptions,\n PulseStatus,\n ReadonlyMap,\n ReconnectOptions,\n Unsubscribe,\n} from './types';",
|
|
46
|
+
"PulseStatus": "export type {\n BufferOptions,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n Middleware,\n PresenceChannel,\n Pulse,\n PulseChannel,\n PulseOptions,\n PulseStatus,\n ReadonlyMap,\n ReconnectOptions,\n Unsubscribe,\n} from './types';",
|
|
47
|
+
"ReadonlyMap": "export type {\n BufferOptions,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n Middleware,\n PresenceChannel,\n Pulse,\n PulseChannel,\n PulseOptions,\n PulseStatus,\n ReadonlyMap,\n ReconnectOptions,\n Unsubscribe,\n} from './types';",
|
|
48
|
+
"ReconnectOptions": "export type {\n BufferOptions,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n Middleware,\n PresenceChannel,\n Pulse,\n PulseChannel,\n PulseOptions,\n PulseStatus,\n ReadonlyMap,\n ReconnectOptions,\n Unsubscribe,\n} from './types';",
|
|
49
|
+
"Unsubscribe": "export type {\n BufferOptions,\n EventKey,\n HeartbeatOptions,\n MessageMap,\n Middleware,\n PresenceChannel,\n Pulse,\n PulseChannel,\n PulseOptions,\n PulseStatus,\n ReadonlyMap,\n ReconnectOptions,\n Unsubscribe,\n} from './types';",
|
|
50
|
+
"PulseAbortError": "export {\n PulseAbortError,\n PulseConnectionError,\n PulseDisposedError,\n PulseError,\n PulseProtocolError,\n PulseTimeoutError,\n} from './errors';",
|
|
51
|
+
"PulseConnectionError": "export {\n PulseAbortError,\n PulseConnectionError,\n PulseDisposedError,\n PulseError,\n PulseProtocolError,\n PulseTimeoutError,\n} from './errors';",
|
|
52
|
+
"PulseDisposedError": "export {\n PulseAbortError,\n PulseConnectionError,\n PulseDisposedError,\n PulseError,\n PulseProtocolError,\n PulseTimeoutError,\n} from './errors';",
|
|
53
|
+
"PulseError": "export {\n PulseAbortError,\n PulseConnectionError,\n PulseDisposedError,\n PulseError,\n PulseProtocolError,\n PulseTimeoutError,\n} from './errors';",
|
|
54
|
+
"PulseProtocolError": "export {\n PulseAbortError,\n PulseConnectionError,\n PulseDisposedError,\n PulseError,\n PulseProtocolError,\n PulseTimeoutError,\n} from './errors';",
|
|
55
|
+
"PulseTimeoutError": "export {\n PulseAbortError,\n PulseConnectionError,\n PulseDisposedError,\n PulseError,\n PulseProtocolError,\n PulseTimeoutError,\n} from './errors';",
|
|
56
|
+
"createPulse": "export { createPulse } from './pulse';"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"apiSource": "/**\n * Refine components register through their explicit component entry points.\n *\n * Keeping the package root free of registration side effects makes dependency\n * ownership and bundle contents obvious to application code.\n */\nexport { RefineError } from './errors';\n",
|
|
3
|
+
"docs": {
|
|
4
|
+
"index": "---\ntitle: Refine — Web component library\ndescription: Accessible, themeable web components built with Ore for framework and vanilla DOM apps.\npackage: refine\ncategory: ui-components\nkeywords: [web-components, accessible, themeable, ui, components, design-system]\nrelated: [ore, orbit, forge, keymap]\nexports:\n [\n ore-accordion,\n ore-accordion-item,\n ore-alert,\n ore-async,\n ore-avatar,\n ore-avatar-group,\n ore-badge,\n ore-box,\n ore-breadcrumb,\n ore-breadcrumb-item,\n ore-button,\n ore-button-group,\n ore-calendar,\n ore-card,\n ore-carousel,\n ore-chat-message,\n ore-checkbox,\n ore-checkbox-group,\n ore-chip,\n ore-combobox,\n ore-command-palette,\n ore-command-palette-item,\n ore-datagrid,\n ore-date-picker,\n ore-dialog,\n ore-drawer,\n ore-file-input,\n ore-grid,\n ore-grid-item,\n ore-icon,\n ore-input,\n ore-list,\n ore-list-item,\n ore-menu,\n ore-menu-item,\n ore-menu-separator,\n ore-message-composer,\n ore-navbar,\n ore-navbar-item,\n ore-number-input,\n ore-otp-input,\n ore-pagination,\n ore-password-strength,\n ore-popover,\n ore-progress,\n ore-radio,\n ore-radio-group,\n ore-rating,\n ore-select,\n ore-separator,\n ore-sidebar,\n ore-sidebar-group,\n ore-sidebar-item,\n ore-skeleton,\n ore-slider,\n ore-step,\n ore-stepper,\n ore-switch,\n ore-tab-item,\n ore-tab-panel,\n ore-table,\n ore-tabs,\n ore-text,\n ore-textarea,\n ore-time-picker,\n ore-toast,\n ore-tooltip,\n ore-typing-indicator,\n ]\nenvironments: [browser]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"refine\" />\n\n## Why Refine?\n\nEvery project needs UI primitives. Refine provides accessible web components that work natively anywhere HTML is rendered—no framework required.\n\n```html\n<!-- Before — roll your own button with ARIA -->\n<button class=\"btn btn-primary\" role=\"button\" aria-pressed=\"false\" tabindex=\"0\">\n <span class=\"btn-spinner\" aria-hidden=\"true\"></span>\n Save\n</button>\n\n<!-- After — Refine -->\n<ore-button variant=\"primary\" loading>Save</ore-button>\n```\n\n| Feature | Refine | Shoelace | Material Web |\n| ------------------ | ------------------------------------------- | ------------------------------------------ | ------------------------------------------ |\n| Bundle size | <PackageInfo package=\"refine\" type=\"size\" /> | ~145 kB | ~200 kB |\n| Built with | Ore | Lit | Lit |\n| Accessible | WCAG AA | WCAG AA | WCAG AA |\n| Framework agnostic | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> |\n\n<div class=\"decision-callout\">\n\n**Use Refine when** you want accessible web components that match the Vielzeug design system without a heavy framework dependency.\n\n**Consider Shoelace or Material Web** if your team is already standardized on those ecosystems and you need their established component catalogs.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/refine\n```\n\n```sh [npm]\nnpm install @vielzeug/refine\n```\n\n```sh [yarn]\nyarn add @vielzeug/refine\n```\n\n:::\n\n## Quick Start\n\n```ts\n// 1. Import global styles once\nimport '@vielzeug/refine/tokens.css';\n\n// 2. Register only the elements you need\nimport '@vielzeug/refine/button';\nimport '@vielzeug/refine/input';\nimport '@vielzeug/refine/card';\n```\n\n```html\n<ore-button variant=\"solid\" color=\"primary\">Save</ore-button>\n<ore-input label=\"Email\" type=\"email\" required></ore-input>\n<ore-card padding=\"lg\">\n <span slot=\"header\">Account</span>\n <p>Card content goes here.</p>\n</ore-card>\n```\n\n```ts\n```\n\n### CDN / Vanilla HTML\n\nUse the self-contained IIFE bundle to load Refine directly from a CDN in any HTML page — no build step required:\n\n```html\n<!-- 1. Styles -->\n<link rel=\"stylesheet\" href=\"https://unpkg.com/@vielzeug/refine/dist/styles/tokens.css\" />\n\n<!-- 2. All components (IIFE — registers global Refine namespace) -->\n<script src=\"https://unpkg.com/@vielzeug/refine/dist/refine.iife.js\"></script>\n```\n\nFor bundler-based projects that still want a CDN URL, use the ESM bundle via an import map:\n\n```html\n<script type=\"importmap\">\n {\n \"imports\": {\n \"@vielzeug/refine\": \"https://esm.sh/@vielzeug/refine\",\n \"@vielzeug/refine/button\": \"https://esm.sh/@vielzeug/refine/button\",\n \"@vielzeug/refine/input\": \"https://esm.sh/@vielzeug/refine/input\"\n }\n }\n</script>\n\n<script type=\"module\">\n import '@vielzeug/refine/button';\n import '@vielzeug/refine/input';\n</script>\n```\n\n### Package Entry Points\n\n| Import | Purpose |\n| ------------------------ | ----------------------------------------- |\n| `@vielzeug/refine/tokens.css` | Global design tokens and cascade layers |\n| `@vielzeug/refine/styles/preflight.css` | Optional browser-default reset |\n\nComponent registration happens through side-effect imports such as `@vielzeug/refine/button` and `@vielzeug/refine/dialog`.\n\n### Components\n\n**Content:** `ore-avatar`, `ore-avatar-group`, `ore-breadcrumb`, `ore-card`, `ore-carousel`, `ore-carousel-slide`, `ore-chat-message`, `ore-icon`, `ore-list`, `ore-list-item`, `ore-marquee`, `ore-pagination`, `ore-separator`, `ore-step`, `ore-stepper`, `ore-table`, `ore-text`\n\n**Disclosure:** `ore-accordion`, `ore-accordion-item`, `ore-tabs`, `ore-tab-item`, `ore-tab-panel`\n\n**Feedback:** `ore-alert`, `ore-async`, `ore-badge`, `ore-chip`, `ore-password-strength`, `ore-progress`, `ore-skeleton`, `ore-toast`, `ore-typing-indicator`\n\n**Inputs:** `ore-button`, `ore-button-group`, `ore-calendar`, `ore-checkbox`, `ore-checkbox-group`, `ore-column`, `ore-combobox`, `ore-datagrid`, `ore-date-picker`, `ore-file-input`, `ore-input`, `ore-message-composer`, `ore-number-input`, `ore-otp-input`, `ore-radio`, `ore-radio-group`, `ore-rating`, `ore-select`, `ore-slider`, `ore-switch`, `ore-textarea`, `ore-time-picker`\n\n**Layout:** `ore-box`, `ore-grid`, `ore-grid-item`, `ore-navbar`, `ore-sidebar`\n\n**Overlay:** `ore-command-palette`, `ore-command-palette-item`, `ore-dialog`, `ore-drawer`, `ore-menu`, `ore-popover`, `ore-tooltip`\n\n## Features\n\n<div class=\"features-grid\">\n\n- **Accessible** — keyboard navigation, ARIA wiring, and focus management across interactive components\n- **Themeable** — global tokens plus component-level CSS custom properties\n- **Framework agnostic** — works anywhere HTML can be rendered\n- **Tree-shakeable** — import only the component entry points you register\n- **Comprehensive surface** — inputs, content, disclosure, feedback, layout, and overlay primitives\n- **Zero runtime deps** — <PackageInfo package=\"refine\" type=\"size\" /> gzipped\n\n</div>\n\n### Prerequisites\n\n- Browser runtime with Custom Elements support.\n- Import `@vielzeug/refine/tokens.css` before rendering components.\n- For SSR, render placeholders server-side and hydrate components only on the client.\n\n## Documentation\n\n<div class=\"doc-links\">\n\n- [Usage Guide](./usage.md)\n- [API Reference](./api.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Ore](/ore/) — Web component runtime that powers Refine\n- [Orbit](/orbit/) — Floating UI positioning used in Refine's overlays\n- [Forge](/forge/) — Form state management for use with Refine inputs\n- [Keymap](/keymap/) — Keyboard shortcut manager that powers the command palette's global trigger\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
|
|
5
|
+
"api": "---\ntitle: Refine — API Reference\ndescription: Published component registration and stylesheet entry points for @vielzeug/refine.\n---\n\n# API Reference\n\n[[toc]]\n\nRefine deliberately publishes components, not a second headless framework. Register each element through its component\nsubpath and import its types from the same path.\n\n## Styles\n\n```ts\nimport '@vielzeug/refine/tokens.css';\nimport '@vielzeug/refine/styles/preflight.css'; // Optional: normalizes browser defaults.\n```\n\n`tokens.css` defines Refine's design tokens, animations, and cascade-layer order without modifying global element\ndefaults. `preflight.css` is a separate opt-in reset.\n\nDirect CSS entry points are also available when needed:\n\n| Import path | Purpose |\n| --- | --- |\n| `@vielzeug/refine/tokens.css` | Tokens, animation helpers, and cascade layers |\n| `@vielzeug/refine/styles/theme.css` | Theme token declarations |\n| `@vielzeug/refine/styles/animation.css` | Animation helpers |\n| `@vielzeug/refine/styles/layers.css` | Cascade layer declarations |\n| `@vielzeug/refine/styles/preflight.css` | Optional browser-default reset |\n\n## Components\n\nEach component has a single registration and type entry point:\n\n```ts\nimport '@vielzeug/refine/button';\nimport type { OreButtonEvents, OreButtonProps } from '@vielzeug/refine/button';\n```\n\nThe package root only exports `RefineError`; it does not register elements. This keeps component ownership and bundle\ncontents explicit.\n\n| Area | Components |\n| --- | --- |\n| Content | `accordion`, `accordion-item`, `avatar`, `avatar-group`, `badge`, `breadcrumb`, `card`, `carousel`, `chat-message`, `code-window`, `copy-command`, `icon`, `list`, `list-item`, `marquee`, `pagination`, `separator`, `step`, `stepper`, `table`, `text` |\n| Feedback | `alert`, `async`, `chip`, `password-strength`, `progress`, `skeleton`, `toast`, `typing-indicator` |\n| Inputs | `button`, `button-group`, `calendar`, `checkbox`, `checkbox-group`, `combobox`, `datagrid`, `date-picker`, `file-input`, `input`, `message-composer`, `number-input`, `otp-input`, `radio`, `radio-group`, `rating`, `select`, `slider`, `switch`, `textarea`, `time-picker` |\n| Layout | `box`, `grid`, `grid-item`, `navbar`, `sidebar` |\n| Overlays | `command-palette`, `dialog`, `drawer`, `menu`, `popover`, `tooltip` |\n\nEach component's documentation page describes its attributes, properties, events, slots, parts, and custom properties.\n\n## Events and Form Controls\n\nForm controls expose their current `.value` or `.checked` property and dispatch standard `input` and `change` events.\nRead the property from `event.currentTarget`; do not rely on framework-specific custom-event casts.\n\nStateful overlays expose `open` and `default-open` properties/attributes and dispatch `open-change` with\n`{ open, reason }` detail. The per-component pages describe valid reasons and focus behavior.\n",
|
|
6
|
+
"usage": "---\ntitle: Refine — Usage Guide\ndescription: Installation, attributes, events, slots, and ecosystem integration for Refine components.\n---\n\n# Usage Guide\n\n[[toc]]\n\nRefine components are native Web Components. Once imported, they behave like regular HTML elements — set attributes, listen to DOM events, use slots for content projection.\n\n## Installation\n\nImport the global styles first, then register only the components you need:\n\n```ts\nimport '@vielzeug/refine/tokens.css';\nimport '@vielzeug/refine/button';\nimport '@vielzeug/refine/input';\nimport '@vielzeug/refine/dialog';\n```\n\nThe token stylesheet supplies Refine's design tokens and cascade layers without changing browser defaults. Add the reset only when your application explicitly wants it:\n\n```ts\nimport '@vielzeug/refine/styles/preflight.css';\n```\n\n## Attributes and Events\n\nSet attributes directly on the element. Attributes map to component props:\n\n```html\n<ore-button variant=\"outline\" color=\"secondary\" size=\"lg\" disabled>\n Large Outline Button\n</ore-button>\n```\n\nComponents emit standard DOM events. Common event names: `click`, `input`, `change`, and `open-change`. Custom events carry a `detail` object:\n\n```javascript\nconst input = document.querySelector('ore-input');\n\ninput.addEventListener('input', () => {\n console.log(input.value);\n});\n```\n\nNative browser events (`click`, `focus`, `blur`) work as normal. Custom events with `event.detail` require `addEventListener` in React 18 and earlier — see the [Framework Integration](./frameworks.md) guide.\n\n## Slots\n\nSlots let you pass HTML into named regions of a component without JavaScript.\n\nContent placed directly inside the element fills the default slot:\n\n```html\n<ore-button>Save Changes</ore-button>\n<ore-card>Any HTML content here</ore-card>\n```\n\nComponents with distinct regions expose named slots:\n\n```html\n<ore-card>\n <span slot=\"header\">Card Heading</span>\n <p>Main body content fills the default slot.</p>\n <div slot=\"footer\">\n <ore-button size=\"sm\" variant=\"outline\">Cancel</ore-button>\n <ore-button size=\"sm\">Confirm</ore-button>\n </div>\n</ore-card>\n```\n\nMany input components expose `prefix` and `suffix` slots for icons or actions:\n\n```html\n<ore-button>\n <ore-icon slot=\"prefix\" name=\"arrow-left\" size=\"18\"></ore-icon>\n Back\n</ore-button>\n\n<ore-input label=\"Search\">\n <ore-icon slot=\"suffix\" name=\"search\" size=\"18\" aria-hidden=\"true\"></ore-icon>\n</ore-input>\n```\n\nEach component's available slots are listed in its API Reference table.\n\n## Composing with Ore and Ripple\n\nRefine components are plain HTML elements — they compose naturally with [Ore](/ore/) custom elements and [Ripple](/ripple/) signals.\n\n**Build a custom component that wraps Refine elements:**\n\n```ts\nimport '@vielzeug/refine/button';\nimport '@vielzeug/refine/input';\nimport { define, html } from '@vielzeug/ore';\nimport { signal } from '@vielzeug/ripple';\n\ndefine('my-search-bar', () => {\n const query = signal('');\n return html`\n <ore-input\n .value=${query}\n @input=${(e) => (query.value = e.currentTarget.value)}\n label=\"Search\"\n />\n <ore-button @click=${() => search(query.value)} variant=\"solid\" color=\"primary\">\n Search\n </ore-button>\n `;\n});\n```\n\n**Drive component state from reactive signals:**\n\n```ts\nimport { signal, effect } from '@vielzeug/ripple';\n\nconst isLoading = signal(false);\nconst btn = document.querySelector('ore-button');\n\neffect(() => {\n btn.loading = isLoading.value;\n});\n```\n\n## Framework Integration\n\nFor React, Vue, Svelte, and Angular wiring — including event handling, TypeScript declarations, Vite setup, and SSR guards — see the [Framework Integration](./frameworks.md) guide.\n\n## Accessibility\n\nAll Refine components target WCAG 2.1 AA. ARIA roles and states are managed automatically. For the full compliance contract, per-component coverage, and testing strategy, see the [Accessibility](./accessibility.md) page.\n\nThe two things you always control:\n\n- **Icon-only buttons** require a `label` attribute — it becomes `aria-label`.\n- **Decorative icons** should have `aria-hidden=\"true\"` so screen readers skip them.\n"
|
|
7
|
+
},
|
|
8
|
+
"examples": [],
|
|
9
|
+
"typeSignatures": {
|
|
10
|
+
"RefineError": "export { RefineError } from './errors';"
|
|
11
|
+
}
|
|
12
|
+
}
|