@vielzeug/codex 3.0.2 → 3.0.4
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/data/catalog.json +1899 -0
- package/data/llms-full.txt +30099 -0
- package/data/llms.txt +45 -0
- package/data/manifest.json +8 -0
- package/data/packages/arsenal.json +216 -0
- package/data/packages/assay.json +46 -0
- package/data/packages/clockwork.json +66 -0
- package/data/packages/codex.json +29 -0
- package/data/packages/coins.json +101 -0
- package/data/packages/conduit.json +68 -0
- package/data/packages/courier.json +55 -0
- package/data/packages/dnd.json +60 -0
- package/data/packages/familiar.json +17 -0
- package/data/packages/flux.json +94 -0
- package/data/packages/focus.json +38 -0
- package/data/packages/forge.json +59 -0
- package/data/packages/gesture.json +37 -0
- package/data/packages/herald.json +81 -0
- package/data/packages/illusionist.json +132 -0
- package/data/packages/keymap.json +51 -0
- package/data/packages/ledger.json +60 -0
- package/data/packages/lingua.json +70 -0
- package/data/packages/necromancer.json +50 -0
- package/data/packages/orbit.json +99 -0
- package/data/packages/ore.json +65 -0
- package/data/packages/postmaster.json +49 -0
- package/data/packages/prism.json +66 -0
- package/data/packages/pulse.json +71 -0
- package/data/packages/refine.json +12 -0
- package/data/packages/ripple.json +88 -0
- package/data/packages/rune.json +78 -0
- package/data/packages/sandbox.json +26 -0
- package/data/packages/scout.json +63 -0
- package/data/packages/scroll.json +110 -0
- package/data/packages/sentinel.json +35 -0
- package/data/packages/sourcerer.json +70 -0
- package/data/packages/spell.json +128 -0
- package/data/packages/tempo.json +81 -0
- package/data/packages/vault.json +93 -0
- package/data/packages/ward.json +80 -0
- package/data/packages/wayfinder.json +114 -0
- package/data/refine.json +12235 -0
- package/data/search.json +1534 -0
- package/package.json +4 -5
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"apiSource": "export { catalogKeys } from './catalog';\nexport {\n LinguaDisposedError,\n LinguaError,\n LinguaInvalidCatalogError,\n LinguaInvalidLocaleError,\n LinguaInvalidPluralCountError,\n LinguaInvalidStateError,\n LinguaMissingCatalogError,\n LinguaMissingKeyError,\n LinguaMissingValueError,\n} from './errors';\nexport { createI18n, type I18n, type I18nSnapshot } from './i18n';\nexport { createTranslator, type Translator } from './translator';\nexport type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';\n",
|
|
3
|
+
"docs": {
|
|
4
|
+
"index": "---\ntitle: Lingua — Explicit localization for TypeScript\ndescription: Framework-neutral locale catalogs, typed translations, and explicit plural messages.\npackage: lingua\ncategory: i18n\nkeywords: [internationalization, translations, pluralization, locale, i18n, catalog-loading]\nrelated: [ripple, wayfinder, courier]\nexports: [createI18n, createTranslator, LinguaError, LinguaDisposedError, LinguaInvalidCatalogError, LinguaInvalidLocaleError, LinguaInvalidPluralCountError, LinguaInvalidStateError, LinguaMissingCatalogError, LinguaMissingKeyError, LinguaMissingValueError]\nenvironments: [browser, node, ssr, deno]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"lingua\" />\n\n## Why Lingua?\n\nLingua separates immutable translation from mutable locale state. Use `createTranslator` for one fixed-locale catalog, or `createI18n` when locale changes at runtime.\n\n```ts\n// Before\nconst message = catalogs[locale]?.inbox?.[count === 1 ? 'one' : 'other'] ?? 'inbox';\n\n// After\nconst output = i18n.translate('inbox', { count });\n```\n\n| Feature | Lingua | i18next | FormatJS |\n| --- | --- | --- | --- |\n| Bundle size | <PackageInfo package=\"lingua\" type=\"size\" /> | Varies by selected modules | Varies by selected modules |\n| Zero runtime dependencies | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"triangle-alert\" size=\"16\"></ore-icon> | <ore-icon name=\"triangle-alert\" size=\"16\"></ore-icon> |\n| Explicit plural catalog nodes | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Convention/config dependent | ICU-message dependent |\n| Static and lazy locale catalogs | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Plugin/config dependent | Application-defined |\n| Immutable locale snapshots | <ore-icon name=\"check\" size=\"16\"></ore-icon> | Application-defined | Application-defined |\n\n<div class=\"decision-callout\">\n\n**Use Lingua when** you need a compact TypeScript runtime with explicit catalog structure, deterministic fallback, and framework-neutral subscriptions.\n\n**Consider i18next or FormatJS when** you need their plugin ecosystems, message extraction pipelines, or framework-specific integrations.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/lingua\n```\n\n```sh [npm]\nnpm install @vielzeug/lingua\n```\n\n```sh [yarn]\nyarn add @vielzeug/lingua\n```\n\n:::\n\n## Quick Start\n\nCreate an i18n instance with an eager default catalog and a loader for other locales.\n\n```ts\nimport { createI18n } from '@vielzeug/lingua';\n\nconst en = { inbox: { plural: { one: 'One message', other: '{count} messages' } } };\nconst i18n = createI18n({\n catalogs: { en },\n locale: 'en',\n loadCatalog: (locale) => import(`./locales/${locale}.ts`).then((m) => m.default),\n});\n\ntry {\n console.log(i18n.translate('inbox', { count: 3 }));\n await i18n.setLocale('de');\n console.log(i18n.translate('inbox', { count: 1 }));\n} finally {\n i18n.dispose();\n}\n```\n\n## Features\n\n<div class=\"features-grid\">\n\n- `createTranslator()` compiles one immutable fixed-locale catalog.\n- `createI18n()` manages eager catalogs, locale changes, and optional lazy loading.\n- `translate()` renders text and plural messages through explicit catalog nodes.\n- `translateDynamic()` makes runtime-key lookup explicit.\n- `parts()` returns typed discriminated parts for framework content.\n- `load()` deduplicates loading and resolves the active locale plus configured fallbacks.\n- `getSnapshot()` and `subscribe()` expose immutable translator revisions.\n- `serialize()` and `state` option transfer resolved SSR catalogs.\n- `missing: 'throw' | 'key' | handler` controls failure behaviour.\n- `validateCatalog()` remains an isolated subpath tool.\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- [Migration Guide](./migration.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Ripple](../ripple/index.md) adapts Lingua snapshots into reactive application state.\n- [Courier](../courier/index.md) can fetch locale catalogs before passing them to Lingua loaders.\n- [Wayfinder](../wayfinder/index.md) can drive locale selection from route state.\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
|
|
5
|
+
"api": "---\ntitle: Lingua — API Reference\ndescription: Complete API reference for @vielzeug/lingua.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| --- | --- | --- | --- |\n| `createTranslator()` | Compile one immutable locale catalog | Sync | No fallback locales |\n| `createI18n()` | Create mutable locale and catalog store | Sync | Provide catalogs, serialized state, or a loader |\n| `createFormatter()` | Create a cached Intl facade from `/format` | Sync | Pass a locale getter for dynamic locale changes |\n| `catalogKeys()` | Enumerate message keys as dotted paths | Sync | Accepts i18n instance (current locale) or raw catalog; traverse subtrees for group-scoped keys |\n| `validateCatalog()` | Check explicit plural forms from `/validate` | Sync | Import from subpath |\n| `compareCatalogs()` | Compare key parity across locales from `/validate` | Sync | First locale is the base; import from subpath |\n| `LinguaError` | Base class for Lingua errors | Sync | Use `instanceof LinguaError` for broad narrowing |\n\n## Package Entry Point\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/lingua` | Translation factories, state types, and Lingua errors |\n| `@vielzeug/lingua/format` | Standalone cached Intl formatter factory |\n| `@vielzeug/lingua/validate` | `validateCatalog()`, `compareCatalogs()`, and `ValidationIssue` |\n\n## Translation Factories\n\n### createTranslator\n\n```ts\nfunction createTranslator<C extends Catalog>(catalog: C, options?: TranslatorOptions): Translator<C>;\n```\n\nCompiles one catalog and returns an immutable fixed-locale translator. Locale defaults to `en` and controls plural selection and diagnostics.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `catalog` | `C` | One catalog containing only messages and grouping objects |\n| `options` | `TranslatorOptions` | Locale and missing-message strategy |\n\n**Returns:** `Translator<C>`.\n\n**Example:**\n\n```ts\nimport { createTranslator } from '@vielzeug/lingua';\n\nconst translator = createTranslator(\n { save: 'Enregistrer' },\n { locale: 'fr' },\n);\n\ntranslator.translate('save');\n```\n\n| Method | Signature | Returns |\n| --- | --- | --- |\n| `translate` | `(textKey, options?)` or `(pluralKey, { count, ordinal?, values? })` | Rendered string |\n| `translateDynamic` | `(key, options?)` | Rendered string for runtime key |\n| `parts` | `(textKey, { values })` or `(pluralKey, { count, ordinal?, values? })` | Typed discriminated parts |\n| `partsDynamic` | `(key, options)` | Parts for runtime key |\n| `locale` | `Locale` | Resolved active locale |\n\n---\n\n### createI18n\n\n```ts\nfunction createI18n<C extends Catalog>(options: I18nOptions<C>): I18n<C>;\n```\n\nCreates catalog store, current locale state, and immutable translator snapshots.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `options.catalogs` | `Catalogs<C>` | Eager locale catalogs; mutually exclusive with `state` |\n| `options.locale` | `Locale` | Initial locale; defaults to serialized locale, then `en` |\n| `options.loadCatalog` | `(locale) => Promise<C> \\| C` | Optional loader for catalogs not supplied eagerly |\n| `options.fallback` | `Locale \\| readonly Locale[]` | Fallback locales loaded with the selected locale |\n| `options.missing` | `MissingStrategy` | Missing-key/value strategy |\n| `options.state` | `TranslationState<C>` | Eager serialized state; mutually exclusive with `catalogs` |\n\nProvide `catalogs`, `state`, or `loadCatalog`. You may combine eager `catalogs` or hydrated `state` with `loadCatalog` for later locales.\n\n**Returns:** `I18n<C>`, with every `Translator<C>` method plus lifecycle methods.\n\n**Example:**\n\n```ts\nimport { createI18n } from '@vielzeug/lingua';\n\nconst i18n = createI18n({\n catalogs: { en: { title: 'Home' } },\n locale: 'en',\n});\ni18n.translate('title');\n```\n\n| Method or property | Signature | Returns |\n| --- | --- | --- |\n| `translate` | Translator method | Rendered string |\n| `parts` | Translator method | Typed discriminated parts |\n| `load` | `({ locale? })` | `Promise<void>` after the locale and configured fallbacks load |\n| `setLocale` | `(locale)` | `Promise<void>` after an atomic loaded-locale commit |\n| `isLoaded` | `({ locale? })` | `boolean` |\n| `getSnapshot` | `()` | `I18nSnapshot<C>` |\n| `subscribe` | `(listener, { immediate?, signal? })` | Unsubscribe function |\n| `serialize` | `()` | Loader-free `TranslationState<C>` |\n| `dispose` | `()` | `void` |\n| `locale` | `Locale` | Current canonical locale |\n| `disposed` | `boolean` | Disposal state |\n| `disposalSignal` | `AbortSignal` | Aborts on disposal |\n| `[Symbol.dispose]` | `()` | Delegates to `dispose()` |\n\n---\n\n## Catalog Utilities\n\n### catalogKeys\n\n```ts\nfunction catalogKeys<C extends Catalog>(source: I18n<C> | C): ReadonlyArray<MessageKey<C>>;\n```\n\nEnumerates every message key as a dotted path. Traverses nested grouping objects and explicit `{ plural: ... }` messages, producing the same paths that `TextKey<C>` represents at the type level. Pass an `I18n` instance to read from its current locale catalog; pass a raw catalog object to enumerate directly.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `source` | `I18n<C> \\| C` | I18n instance (uses current locale) or raw catalog object |\n\n**Returns:** `ReadonlyArray<MessageKey<C>>` — dotted paths to every text and plural message.\n\n```ts\nimport { catalogKeys, createI18n } from '@vielzeug/lingua';\n\nconst i18n = createI18n({\n catalogs: { en: { nav: { home: 'Home', settings: 'Settings' } } },\n locale: 'en',\n});\n\nconst allKeys = catalogKeys(i18n); // ['nav.home', 'nav.settings']\nconst navKeys = catalogKeys(i18n.serialize().catalogs.en.nav); // ['home', 'settings']\n```\n\n---\n\n## Intl formatting\n\n### createFormatter\n\n```ts\nimport { createFormatter } from '@vielzeug/lingua/format';\n\nconst format = createFormatter(() => i18n.locale);\nformat.number(1_234.5);\nformat.currency(19.99, 'USD');\nformat.date(new Date(), { dateStyle: 'medium' });\n```\n\nThe formatter caches bounded `Intl` instances and resolves a locale getter for every call.\n\n## Validation\n\n### validateCatalog\n\n```ts\nfunction validateCatalog(catalog: Catalog, locale: Locale): ValidationIssue[];\n```\n\nValidates explicit plural messages against locale plural categories after catalog structural validation.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `catalog` | `Catalog` | Explicit catalog to validate |\n| `locale` | `Locale` | BCP 47 locale tag |\n\n**Returns:** `ValidationIssue[]`.\n\n**Example:**\n\n```ts\nimport { validateCatalog } from '@vielzeug/lingua/validate';\n\nvalidateCatalog({ inbox: { plural: { one: 'One message' } } }, 'en');\n```\n\n### compareCatalogs\n\n```ts\nfunction compareCatalogs<C extends Catalog>(catalogs: Catalogs<C>): CatalogComparison;\n```\n\nCompares key sets across locales. First locale is the base — reports keys missing in each target and keys present in targets but absent from base. Validates each catalog structurally.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `catalogs` | `Catalogs<C>` | Locale-keyed catalogs to compare |\n\n**Returns:** `CatalogComparison` with `missing` and `extra` arrays.\n\n```ts\nimport { compareCatalogs } from '@vielzeug/lingua/validate';\n\nconst result = compareCatalogs({\n en: { greeting: 'Hello', farewell: 'Goodbye' },\n de: { greeting: 'Hallo' },\n});\n// { missing: [{ key: 'farewell', locale: 'de' }], extra: [] }\n```\n\n## Types\n\n```ts\ntype Locale = string;\ntype PluralCategory = Intl.LDMLPluralRule;\ntype PluralMessage = { readonly plural: Partial<Record<PluralCategory, string>> };\ntype CatalogNode = Catalog | PluralMessage | string;\ntype Catalog = { readonly [key: string]: CatalogNode };\ntype Catalogs<C extends Catalog = Catalog> = Record<Locale, C>;\n\ntype TextPart = { readonly type: 'text'; readonly value: string };\ntype ValuePart<V> = { readonly type: 'value'; readonly value: V };\ntype Part<V> = TextPart | ValuePart<V>;\n\ntype MissingInfo = { readonly key: string; readonly locale: Locale; readonly name?: string };\ntype MissingHandler = (info: MissingInfo) => string;\ntype MissingStrategy = 'throw' | 'key' | MissingHandler;\n\ntype TranslatorOptions = { readonly locale?: Locale; readonly missing?: MissingStrategy };\n\ntype I18nOptions<C extends Catalog = Catalog> = {\n readonly fallback?: Locale | readonly Locale[];\n readonly locale?: Locale;\n readonly missing?: MissingStrategy;\n} & (\n | { readonly catalogs: Catalogs<C>; readonly loadCatalog?: CatalogLoader<C>; readonly state?: never }\n | { readonly catalogs?: never; readonly loadCatalog?: CatalogLoader<C>; readonly state: TranslationState<C> }\n | { readonly catalogs?: never; readonly loadCatalog: CatalogLoader<C>; readonly state?: never }\n);\n\ntype CatalogLoader<C extends Catalog> = (locale: Locale) => Promise<C> | C;\n\ntype TranslationState<C extends Catalog = Catalog> = {\n readonly catalogs: Catalogs<C>;\n readonly locale: Locale;\n readonly version: 4;\n};\n\ntype I18nSnapshot<C extends Catalog = Catalog> = {\n readonly locale: Locale;\n readonly revision: number;\n readonly translator: Translator<C>;\n};\n\ntype I18n<C extends Catalog = Catalog> = Translator<C> & {\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n getSnapshot(): I18nSnapshot<C>;\n isLoaded(options?: { locale?: Locale }): boolean;\n load(options?: { locale?: Locale }): Promise<void>;\n serialize(): TranslationState<C>;\n setLocale(locale: Locale): Promise<void>;\n subscribe(listener: (snapshot: I18nSnapshot<C>) => void, options?: SubscribeOptions): () => void;\n [Symbol.dispose](): void;\n};\n\ntype Translator<C extends Catalog = Catalog> = {\n readonly locale: Locale;\n parts<V>(key: TextKey<C>, options: TranslateOptions & { values: Record<string, V> }): Array<Part<V>>;\n parts<V>(key: PluralKey<C>, options: PluralOptions & { values?: Record<string, V> }): Array<Part<number | V>>;\n partsDynamic<V>(\n key: string,\n options: (TranslateOptions | PluralOptions) & { values?: Record<string, V> },\n ): Array<Part<number | V>>;\n translate(key: TextKey<C>, options?: TranslateOptions): string;\n translate(key: PluralKey<C>, options: PluralOptions): string;\n translateDynamic(key: string, options?: TranslateOptions | PluralOptions): string;\n};\n```\n\n```ts\ntype Values = Record<string, unknown>;\ntype TranslateOptions = { values?: Values };\ntype PluralOptions = TranslateOptions & { count: number; ordinal?: boolean };\ntype SubscribeOptions = { immediate?: boolean; signal?: AbortSignal };\n\ntype MessageKey<\n C,\n Prefix extends string = '',\n Depth extends readonly unknown[] = readonly [1, 1, 1, 1, 1, 1],\n> = Depth extends readonly [unknown, ...infer Rest]\n ? C extends string | PluralMessage\n ? Prefix\n : C extends Catalog\n ? {\n [K in string & keyof C]: MessageKey<C[K], Prefix extends '' ? K : `${Prefix}.${K}`, Rest>;\n }[string & keyof C]\n : never\n : never;\n\ntype TextKey<\n C,\n Prefix extends string = '',\n Depth extends readonly unknown[] = readonly [1, 1, 1, 1, 1, 1],\n> = Depth extends readonly [unknown, ...infer Rest]\n ? C extends string\n ? Prefix\n : C extends Catalog\n ? {\n [K in string & keyof C]: TextKey<C[K], Prefix extends '' ? K : `${Prefix}.${K}`, Rest>;\n }[string & keyof C]\n : never\n : never;\n\ntype PluralKey<\n C,\n Prefix extends string = '',\n Depth extends readonly unknown[] = readonly [1, 1, 1, 1, 1, 1],\n> = Depth extends readonly [unknown, ...infer Rest]\n ? C extends PluralMessage\n ? Prefix\n : C extends Catalog\n ? {\n [K in string & keyof C]: PluralKey<C[K], Prefix extends '' ? K : `${Prefix}.${K}`, Rest>;\n }[string & keyof C]\n : never\n : never;\n\ntype ValidationIssue = { key: string; locale: Locale; missing: Intl.LDMLPluralRule };\ntype CatalogComparison = {\n readonly missing: ReadonlyArray<{ key: string; locale: Locale }>;\n readonly extra: ReadonlyArray<{ key: string; locale: Locale }>;\n};\n```\n\n## Errors\n\n| Error | Trigger |\n| --- | --- |\n| `LinguaDisposedError` | State mutation or subscription after `dispose()` |\n| `LinguaInvalidCatalogError` | Invalid catalog node or reserved key |\n| `LinguaInvalidLocaleError` | Invalid BCP 47 locale tag |\n| `LinguaInvalidPluralCountError` | Non-finite plural count |\n| `LinguaInvalidStateError` | Unsupported serialized state version |\n| `LinguaMissingCatalogError` | No `loadCatalog` function or catalog source unavailable |\n| `LinguaMissingKeyError` | Missing translation key when `missing: 'throw'` |\n| `LinguaMissingValueError` | Missing interpolation value when `missing: 'throw'` |\n",
|
|
6
|
+
"usage": "---\ntitle: Lingua — Usage Guide\ndescription: Translate explicit catalogs, load lazy locales, and connect locale snapshots to UI state.\n---\n\n[[toc]]\n\n## Basic Usage\n\nCreate an i18n instance with eager locale catalogs. Strings are text messages; plural messages use `{ plural: ... }`.\n\n```ts\nimport { createI18n } from '@vielzeug/lingua';\n\nconst i18n = createI18n({\n catalogs: {\n en: {\n greeting: 'Hello, {name}!',\n inbox: { plural: { one: 'One message', other: '{count} messages' } },\n },\n },\n locale: 'en',\n});\n\nconsole.log(i18n.translate('greeting', { values: { name: 'Ada' } }));\nconsole.log(i18n.translate('inbox', { count: 3 }));\n```\n\nCall `dispose()` when the instance belongs to a temporary request, test, or route owner.\n\n## Define Explicit Catalogs\n\nUse nested objects only to group keys. A plural message always has `plural`, so regular objects containing `one` or `other` remain groups.\n\n```ts\nconst catalog = {\n account: {\n greeting: 'Hello, {name}!',\n unread: { plural: { one: 'One unread message', other: '{count} unread messages' } },\n },\n};\n```\n\nUse `{ values }` for text replacements. Pass `count` at top level for plural selection; Lingua injects it into selected template.\n\nCatalogs contain strings, grouping objects, and explicit `{ plural: ... }` messages only. Keep application data outside catalog, then translate display labels while constructing it.\n\n```ts\nimport { createTranslator } from '@vielzeug/lingua';\n\nconst messages = {\n status: { blocked: 'Blocked', done: 'Done', inProgress: 'In progress' },\n};\nconst statusDefinitions = [\n { labelKey: 'status.inProgress', value: 'in-progress' },\n { labelKey: 'status.blocked', value: 'blocked' },\n { labelKey: 'status.done', value: 'done' },\n] as const;\nconst translator = createTranslator(messages, { locale: 'en' });\nconst statusOptions = statusDefinitions.map(({ labelKey, value }) => ({ label: translator.translate(labelKey), value }));\n```\n\n## Missing Keys and Values\n\nMissing keys and values do not silently echo placeholders by default — Lingua warns in development and returns the key or `{name}`. Control failure behaviour with the `missing` option:\n\n| `missing` | Behaviour |\n| --- | --- |\n| `undefined` (default) | Warn in development; return key / `{name}` |\n| `'key'` | Return key / `{name}` silently |\n| `'throw'` | Throw `LinguaMissingKeyError` / `LinguaMissingValueError` |\n| `handler` | Call `({ key, locale, name? }) => string` |\n\n```ts\nconst translator = createTranslator(messages, { locale: 'en', missing: 'throw' });\n```\n\n## Enumerate Catalog Keys\n\nUse `catalogKeys()` to derive key arrays from the catalog itself instead of maintaining a parallel list that can go stale. It traverses nested grouping objects and explicit `{ plural: ... }` messages, returning the same dotted paths that `MessageKey<C>` represents at the type level.\n\nPass an `I18n` instance to enumerate keys from its current locale catalog without specifying a locale explicitly.\n\n```ts\nimport { catalogKeys, createI18n } from '@vielzeug/lingua';\n\nconst i18n = createI18n({\n catalogs: {\n en: {\n greeting: 'Hello, {name}!',\n inbox: { plural: { one: 'One message', other: '{count} messages' } },\n nav: { home: 'Home', settings: 'Settings' },\n },\n },\n locale: 'en',\n});\n\nconst allKeys = catalogKeys(i18n);\n// ['greeting', 'inbox', 'nav.home', 'nav.settings']\n```\n\nPass a raw catalog object to enumerate keys directly. Call `catalogKeys()` on a nested subtree to get exactly the keys in that group — no filtering, no casts.\n\n```ts\nimport { catalogKeys } from '@vielzeug/lingua';\n\nconst messages = {\n nav: { home: 'Home', settings: 'Settings' },\n} as const;\n\nconst allKeys = catalogKeys(messages);\n// ['nav.home', 'nav.settings']\n\nconst navKeys = catalogKeys(messages.nav);\n// ['home', 'settings']\n```\n\n## Render Framework Content\n\nUse `parts()` when replacements are framework nodes, links, or other values that must not be stringified. `parts()` returns a discriminated union: `{ type: 'text', value: string }` for literal text and `{ type: 'value', value: V }` for interpolated values.\n\n```ts\nimport { createTranslator } from '@vielzeug/lingua';\n\nconst translator = createTranslator({ error: 'Try {retry} or {support}.' }, { locale: 'en' });\n\nconst retry = { href: '/retry', label: 'retry' };\nconst support = { href: '/support', label: 'support' };\n\nconst result = translator.parts('error', { values: { retry, support } });\n// [\n// { type: 'text', value: 'Try ' },\n// { type: 'value', value: { href: '/retry', label: 'retry' } },\n// { type: 'text', value: ' or ' },\n// { type: 'value', value: { href: '/support', label: 'support' } },\n// { type: 'text', value: '.' },\n// ]\n```\n\nRender returned array with framework fragment or list primitive. Give UI values consumer-owned keys before passing them to `parts()`; Lingua preserves value identity and never clones or mutates them.\n\n## Use a Fixed-Locale Translator\n\nUse `createTranslator()` when one catalog and locale stay fixed for the translator's lifetime. It defaults locale to `en`; pass `locale` when plural rules or diagnostics need another locale. Lingua snapshots catalog messages during construction. Do not mutate source catalog objects afterward.\n\n```ts\nimport { createTranslator } from '@vielzeug/lingua';\n\nconst translator = createTranslator(\n { save: 'Enregistrer' },\n { locale: 'fr' },\n);\n\nconsole.log(translator.translate('save'));\n```\n\n## Load Catalogs and Switch Locales\n\nProvide eager catalogs for translations needed immediately and a `loadCatalog` function for other locales. `setLocale()` loads the selected locale and configured fallbacks before it commits the change.\n\n```ts\nimport { createI18n } from '@vielzeug/lingua';\n\nconst i18n = createI18n({\n catalogs: { en: { navigation: { settings: 'Settings' } } },\n fallback: 'en',\n locale: 'en',\n loadCatalog: (locale) => import(`./locales/${locale}.ts`).then((m) => m.default),\n});\n\nawait i18n.setLocale('fr');\nconsole.log(i18n.translate('navigation.settings'));\n```\n\nUse loader-only configuration when every locale is lazy, and call `load()` before the first translation. Concurrent loads for the same locale share work. A failed or superseded locale selection never changes the current snapshot.\n\n## Subscribe to Immutable Snapshots\n\nSubscribe when UI state must change with locale or loaded active/fallback catalog. Every callback receives a snapshot containing the translator for that revision.\n\n```ts\nconst unsubscribe = i18n.subscribe(\n ({ locale, translator }) => {\n console.log(locale, translator.translate('navigation.settings'));\n },\n { immediate: true },\n);\n\nunsubscribe();\n```\n\nPass `{ signal }` when an `AbortController` owns subscription lifetime.\n\n## SSR State\n\nSerialize resolved catalogs on the server, then hydrate a client instance from the same payload via the `state` option. `getSnapshot()` stays referentially stable until the store revision changes, so use the same hydrated instance throughout initial client render.\n\n```ts\nimport { createI18n } from '@vielzeug/lingua';\n\nconst serverI18n = createI18n({\n catalogs: { en: { title: 'Server title' } },\n locale: 'en',\n});\n\nconst state = serverI18n.serialize();\nconst clientI18n = createI18n({ state });\n\nconsole.log(clientI18n.translate('title'));\nserverI18n.dispose();\nclientI18n.dispose();\n```\n\nState contains raw loaded catalogs and the active locale. It never contains loader functions. Pass an explicit `locale` only when the client should override the serialized locale.\n\n## Validation\n\nImport catalog validation from the dedicated subpath to keep translation state focused.\n\n```ts\nimport { compareCatalogs, validateCatalog } from '@vielzeug/lingua/validate';\n\nconst catalog = { inbox: { plural: { one: 'One message', other: '{count} messages' } } };\n\nconsole.log(validateCatalog(catalog, 'en'));\n```\n\nUse `compareCatalogs()` to catch missing or extra keys across locales — the most common i18n defect. First locale is the base.\n\n```ts\nimport { compareCatalogs } from '@vielzeug/lingua/validate';\n\nconst result = compareCatalogs({\n en: { greeting: 'Hello', farewell: 'Goodbye' },\n de: { greeting: 'Hallo' },\n});\n// { missing: [{ key: 'farewell', locale: 'de' }], extra: [] }\n```\n\n## Framework Integration\n\nPass stable `getSnapshot()` and `subscribe()` methods to framework state primitives. For SSR, create client instance from the same serialized state used by the server before calling `useSyncExternalStore`.\n\n::: code-group\n\n```ts [React]\nimport { useSyncExternalStore } from 'react';\n\nimport type { I18n } from '@vielzeug/lingua';\n\nexport function useTranslator(i18n: I18n) {\n const snapshot = useSyncExternalStore(i18n.subscribe, i18n.getSnapshot, i18n.getSnapshot);\n\n return snapshot.translator;\n}\n```\n\n```ts [Vue 3]\nimport { onUnmounted, shallowRef } from 'vue';\n\nimport type { I18n } from '@vielzeug/lingua';\n\nexport function useTranslator(i18n: I18n) {\n const snapshot = shallowRef(i18n.getSnapshot());\n const unsubscribe = i18n.subscribe((next) => {\n snapshot.value = next;\n });\n\n onUnmounted(unsubscribe);\n return snapshot;\n}\n```\n\n```ts [Svelte]\nimport { readable } from 'svelte/store';\n\nimport type { I18n } from '@vielzeug/lingua';\n\nexport function translatorStore(i18n: I18n) {\n return readable(i18n.getSnapshot().translator, (set) => i18n.subscribe(({ translator }) => set(translator)));\n}\n```\n\n:::\n\n## Working with Other Vielzeug Libraries\n\nBridge Lingua's structural snapshot directly when templates need reactive locale reads.\n\n```ts\nimport { createRipple } from '@vielzeug/ripple';\n\nconst ripple = createRipple();\nconst i18nState = ripple.fromSubscribable(i18n);\nexport const locale = ripple.computed(() => i18nState.value.locale);\n\nexport const disposeLocaleBridge = () => ripple.dispose();\n```\n\nUse Courier loaders when locale catalogs come from HTTP rather than bundled modules; pass each loader to `loadCatalog`.\n\n## Best Practices\n\n- Define plural messages with `{ plural: ... }` and no sibling metadata.\n- Keep arrays and application metadata outside catalogs.\n- Treat source catalog objects as immutable after construction.\n- Use `translateDynamic()` only for runtime-generated keys.\n- Provide eager catalogs for translations required during initial render.\n- Give UI values keys before passing them to `parts()`.\n- Keep loader functions out of SSR payloads.\n- Dispose temporary instances after requests, tests, and route lifetimes.\n- Use `missing: 'throw'` in development to catch missing keys and values early.\n",
|
|
7
|
+
"examples": "---\ntitle: Lingua — Examples\ndescription: Focused examples for explicit catalogs and locale resources.\n---\n\n- [Static Translator](./examples/static-translator.md)\n- [Lazy Locale Catalog](./examples/feature-resources.md)\n- [SSR Hydration](./examples/ssr-hydration.md)\n"
|
|
8
|
+
},
|
|
9
|
+
"examples": [
|
|
10
|
+
{
|
|
11
|
+
"id": "feature-resources",
|
|
12
|
+
"code": "import { createI18n } from '@vielzeug/lingua'\n\nconst frCatalog = { home: 'Accueil' }\n\nconst i18n = createI18n({\n catalogs: { en: { home: 'Home' } },\n locale: 'en',\n loadCatalog: () => frCatalog,\n})\n\nconsole.log(i18n.translate('home'))\n\nawait i18n.setLocale('fr')\nconsole.log(i18n.translate('home'))",
|
|
13
|
+
"name": "Lazy Locale Catalog"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "rich-segments",
|
|
17
|
+
"code": "import { createTranslator } from '@vielzeug/lingua'\n\n// parts() preserves components, nodes, or other non-string replacements\n// as a typed discriminated union: { type: 'text', value } | { type: 'value', value }.\nconst translator = createTranslator({\n error: 'Try {retry} or {support}.',\n})\n\nconst retry = { label: 'retry', href: '/retry' }\nconst support = { label: 'support', href: '/support' }\nconst result = translator.parts('error', { values: { retry, support } })\n\nconsole.log(result)\nconsole.log(result.map((part) => part.type === 'text' ? part.value : part.value.label).join(''))",
|
|
18
|
+
"name": "Rich Parts"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "static-translator",
|
|
22
|
+
"code": "import { createTranslator } from '@vielzeug/lingua'\n\n// Immutable translator: explicit text and plural catalog nodes.\nconst translator = createTranslator({\n greeting: 'Bonjour, {name} !',\n inbox: { plural: { one: 'Un message', other: '{count} messages' } },\n}, { locale: 'fr' })\n\nconsole.log(translator.translate('greeting', { values: { name: 'Ada' } }))\nconsole.log(translator.translate('inbox', { count: 3 }))",
|
|
23
|
+
"name": "Static Translator"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "store",
|
|
27
|
+
"code": "import { createI18n } from '@vielzeug/lingua'\n\nconst catalogs: Record<string, Record<string, string>> = {\n en: { save: 'Save' },\n fr: { save: 'Enregistrer' },\n}\n\nconst i18n = createI18n({\n catalogs,\n locale: 'en',\n})\n\ni18n.subscribe(({ locale, translator }) => {\n console.log(locale, translator.translate('save'))\n}, { immediate: true })\n\nawait i18n.setLocale('fr')",
|
|
28
|
+
"name": "Reactive Locale Store"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"typeSignatures": {
|
|
32
|
+
"catalogKeys": "export { catalogKeys } from './catalog';",
|
|
33
|
+
"LinguaDisposedError": "export {\n LinguaDisposedError,\n LinguaError,\n LinguaInvalidCatalogError,\n LinguaInvalidLocaleError,\n LinguaInvalidPluralCountError,\n LinguaInvalidStateError,\n LinguaMissingCatalogError,\n LinguaMissingKeyError,\n LinguaMissingValueError,\n} from './errors';",
|
|
34
|
+
"LinguaError": "export {\n LinguaDisposedError,\n LinguaError,\n LinguaInvalidCatalogError,\n LinguaInvalidLocaleError,\n LinguaInvalidPluralCountError,\n LinguaInvalidStateError,\n LinguaMissingCatalogError,\n LinguaMissingKeyError,\n LinguaMissingValueError,\n} from './errors';",
|
|
35
|
+
"LinguaInvalidCatalogError": "export {\n LinguaDisposedError,\n LinguaError,\n LinguaInvalidCatalogError,\n LinguaInvalidLocaleError,\n LinguaInvalidPluralCountError,\n LinguaInvalidStateError,\n LinguaMissingCatalogError,\n LinguaMissingKeyError,\n LinguaMissingValueError,\n} from './errors';",
|
|
36
|
+
"LinguaInvalidLocaleError": "export {\n LinguaDisposedError,\n LinguaError,\n LinguaInvalidCatalogError,\n LinguaInvalidLocaleError,\n LinguaInvalidPluralCountError,\n LinguaInvalidStateError,\n LinguaMissingCatalogError,\n LinguaMissingKeyError,\n LinguaMissingValueError,\n} from './errors';",
|
|
37
|
+
"LinguaInvalidPluralCountError": "export {\n LinguaDisposedError,\n LinguaError,\n LinguaInvalidCatalogError,\n LinguaInvalidLocaleError,\n LinguaInvalidPluralCountError,\n LinguaInvalidStateError,\n LinguaMissingCatalogError,\n LinguaMissingKeyError,\n LinguaMissingValueError,\n} from './errors';",
|
|
38
|
+
"LinguaInvalidStateError": "export {\n LinguaDisposedError,\n LinguaError,\n LinguaInvalidCatalogError,\n LinguaInvalidLocaleError,\n LinguaInvalidPluralCountError,\n LinguaInvalidStateError,\n LinguaMissingCatalogError,\n LinguaMissingKeyError,\n LinguaMissingValueError,\n} from './errors';",
|
|
39
|
+
"LinguaMissingCatalogError": "export {\n LinguaDisposedError,\n LinguaError,\n LinguaInvalidCatalogError,\n LinguaInvalidLocaleError,\n LinguaInvalidPluralCountError,\n LinguaInvalidStateError,\n LinguaMissingCatalogError,\n LinguaMissingKeyError,\n LinguaMissingValueError,\n} from './errors';",
|
|
40
|
+
"LinguaMissingKeyError": "export {\n LinguaDisposedError,\n LinguaError,\n LinguaInvalidCatalogError,\n LinguaInvalidLocaleError,\n LinguaInvalidPluralCountError,\n LinguaInvalidStateError,\n LinguaMissingCatalogError,\n LinguaMissingKeyError,\n LinguaMissingValueError,\n} from './errors';",
|
|
41
|
+
"LinguaMissingValueError": "export {\n LinguaDisposedError,\n LinguaError,\n LinguaInvalidCatalogError,\n LinguaInvalidLocaleError,\n LinguaInvalidPluralCountError,\n LinguaInvalidStateError,\n LinguaMissingCatalogError,\n LinguaMissingKeyError,\n LinguaMissingValueError,\n} from './errors';",
|
|
42
|
+
"createI18n": "export { createI18n, type I18n, type I18nSnapshot } from './i18n';",
|
|
43
|
+
"I18n": "export { createI18n, type I18n, type I18nSnapshot } from './i18n';",
|
|
44
|
+
"I18nSnapshot": "export { createI18n, type I18n, type I18nSnapshot } from './i18n';",
|
|
45
|
+
"createTranslator": "export { createTranslator, type Translator } from './translator';",
|
|
46
|
+
"Translator": "export { createTranslator, type Translator } from './translator';",
|
|
47
|
+
"Catalog": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
48
|
+
"CatalogNode": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
49
|
+
"Catalogs": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
50
|
+
"I18nOptions": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
51
|
+
"Locale": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
52
|
+
"MessageKey": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
53
|
+
"MissingHandler": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
54
|
+
"MissingInfo": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
55
|
+
"MissingStrategy": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
56
|
+
"Part": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
57
|
+
"PluralCategory": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
58
|
+
"PluralKey": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
59
|
+
"PluralMessage": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
60
|
+
"PluralOptions": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
61
|
+
"SubscribeOptions": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
62
|
+
"TextKey": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
63
|
+
"TextPart": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
64
|
+
"TranslateOptions": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
65
|
+
"TranslationState": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
66
|
+
"TranslatorOptions": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
67
|
+
"ValuePart": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';",
|
|
68
|
+
"Values": "export type {\n Catalog,\n CatalogNode,\n Catalogs,\n I18nOptions,\n Locale,\n MessageKey,\n MissingHandler,\n MissingInfo,\n MissingStrategy,\n Part,\n PluralCategory,\n PluralKey,\n PluralMessage,\n PluralOptions,\n SubscribeOptions,\n TextKey,\n TextPart,\n TranslateOptions,\n TranslationState,\n TranslatorOptions,\n ValuePart,\n Values,\n} from './types';"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"apiSource": "export { animate } from './animate';\nexport { animateEach } from './animate-each';\nexport { NecromancerConfigError, NecromancerError, NecromancerUnsupportedError } from './errors';\nexport { captureLayout } from './layout';\nexport type {\n AnimateEachOptions,\n AnimateOptions,\n AnimationGroup,\n AnimationHandle,\n AnimationResult,\n KeyframeFactory,\n Keyframes,\n LayoutAnimationOptions,\n LayoutCaptureOptions,\n LayoutTransition,\n MotionMode,\n} from './types';\n",
|
|
3
|
+
"docs": {
|
|
4
|
+
"index": "---\ntitle: Necromancer — Lifecycle-owned DOM animations\ndescription: Lifecycle-owned Web Animations API primitives for native playback, groups, and additive FLIP transitions.\npackage: necromancer\ncategory: ui\nkeywords: [animation, web-animations-api, waapi, flip, stagger, reduced-motion]\nrelated: [orbit, ore]\nexports: [animate, animateEach, captureLayout]\nenvironments: [browser]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"necromancer\" />\n\n## Why Necromancer?\n\nNative Web Animations API calls do not provide lifecycle ownership, reduced-motion policy, grouped playback, or layout transitions. Necromancer retains native keyframes and timing semantics while making ownership explicit for a component or DOM feature.\n\n```ts\n// Before\nconst animation = element.animate(keyframes, { duration: 180 });\nanimation.addEventListener('cancel', removeListeners);\n\n// After\nconst animation = animate(element, keyframes, { duration: 180 });\nanimation.dispose();\n```\n\n| Feature | Native WAAPI | Necromancer | Motion One |\n| --- | --- | --- | --- |\n| Bundle size | 0 B | <PackageInfo package=\"necromancer\" type=\"size\" /> | ~18 kB |\n| Root dependencies | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"check\" size=\"16\"></ore-icon> | <ore-icon name=\"x\" size=\"16\"></ore-icon> |\n| Lifecycle handle | Manual | `dispose()` | Library-specific controls |\n| Reduced motion | Manual | `motion: 'system'` default | Configuration required |\n| Layout transitions | Manual FLIP math | `captureLayout().animate()` | Separate API |\n\n<div class=\"decision-callout\">\n\n**Use Necromancer when** you need native browser animations with explicit cancellation, reduced-motion behavior, staggered groups, or positional FLIP transitions.\n\n**Consider CSS transitions when** a static style change needs no playback control, cleanup, or layout measurement.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/necromancer\n```\n\n```sh [npm]\nnpm install @vielzeug/necromancer\n```\n\n```sh [yarn]\nyarn add @vielzeug/necromancer\n```\n\n:::\n\n## Quick Start\n\nStart the animation after its DOM element mounts and release it when its UI owner is removed.\n\n```ts\nimport { animate } from '@vielzeug/necromancer';\n\nconst notice = document.createElement('p');\nnotice.textContent = 'Saved';\ndocument.body.append(notice);\n\nconst animation = animate(\n notice,\n [{ opacity: 0, transform: 'translateY(8px)' }, { opacity: 1, transform: 'translateY(0)' }],\n { duration: 180, easing: 'ease-out' },\n);\n\nawait animation.result;\nanimation.dispose();\n```\n\n## Features\n\n<div class=\"features-grid\">\n\n- `animate()` — Native element animation with lifecycle ownership and direct native access\n- `animateEach()` — Group ownership with stable keyframe factories and `stagger`\n- `captureLayout()` — One-shot FLIP transition with additive `translate` (position) and `scale` (size)\n- `motion` — `'system'` reduced-motion support with explicit reduced outcomes\n- Native timing — Omitted timing fields retain Web Animations API behavior\n- `signal` — Abort a handle from its parent lifecycle\n- `dispose()` — Idempotent cleanup with `[Symbol.dispose]()`\n\n</div>\n\n## Deliberate Scope\n\nNecromancer owns explicit WAAPI keyframes. It does not generate CSS keyframes, observe CSS transitions, watch mutations, simulate springs, interpolate SVG paths, or run a JavaScript tween loop. Use CSS for declarative style changes and choose a dedicated tool when those capabilities are required.\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- [3.0 Migration](./migration.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Orbit](/orbit/) — Position floating UI before animating its appearance.\n- [Ore](/ore/) — Own Necromancer handles in a custom element's mount and disposal lifecycle.\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
|
|
5
|
+
"api": "---\ntitle: Necromancer — API Reference\ndescription: API reference for @vielzeug/necromancer animation ownership, groups, reduced motion, and FLIP transitions.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| --- | --- | --- | --- |\n| `animate()` | Animate one element | Sync | Omitted timing fields use native WAAPI behavior |\n| `animateEach()` | Animate a unique element group | Sync | Non-zero `stagger` needs numeric `delay` |\n| `captureLayout()` | Capture positions and create a one-shot FLIP transition | Sync | Capture before changing layout |\n| `NecromancerError` | Base package error | Sync | Use `instanceof NecromancerError` to narrow unknown errors |\n\n## Package Entry Point\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/necromancer` | Animation functions, types, and errors |\n| `@vielzeug/necromancer/testing` | jsdom test fakes for `Element.animate()` and `getBoundingClientRect()` |\n\n## Animation Functions\n\n### `animate()`\n\n```ts\nfunction animate(element: Element, keyframes: Keyframes, options?: AnimateOptions): AnimationHandle;\n```\n\nStarts a lifecycle-owned native Web Animation. Native timing options, including an omitted or zero `duration`, are preserved.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `element` | `Element` | Element animated through its native `animate()` method. |\n| `keyframes` | `Keyframes` | Native keyframe array or property-indexed keyframes. |\n| `options` | `AnimateOptions` | Native timing plus motion and cancellation ownership. |\n\n**Returns:** An `AnimationHandle` with native playback access and explicit disposal.\n\n**Example:**\n\n```ts\nimport { animate } from '@vielzeug/necromancer';\n\nconst handle = animate(element, [{ opacity: 0 }, { opacity: 1 }], { duration: 180 });\nhandle.animation.pause();\nconst result = await handle.result;\nhandle.dispose();\n```\n\n---\n\n### `animateEach()`\n\n```ts\nfunction animateEach<ElementType extends Element>(\n elements: Iterable<ElementType>,\n keyframes: Keyframes | KeyframeFactory<ElementType>,\n options?: AnimateEachOptions,\n): AnimationGroup;\n```\n\nStarts animations for unique elements in first-seen order. Necromancer resolves every keyframe factory before starting the first native animation.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `elements` | `Iterable<ElementType>` | Elements animated once in first-seen order. |\n| `keyframes` | `Keyframes \\| KeyframeFactory<ElementType>` | Shared keyframes or a subtype-preserving factory. |\n| `options` | `AnimateEachOptions` | Animation options plus a stagger interval. |\n\n**Returns:** An `AnimationGroup` containing child handles and ordered results.\n\n**Example:**\n\n```ts\nimport { animateEach } from '@vielzeug/necromancer';\n\nconst group = animateEach(document.querySelectorAll<HTMLElement>('.item'), [{ opacity: 0 }, { opacity: 1 }], {\n duration: 180,\n stagger: 30,\n});\nawait group.results;\n```\n\n## Layout Functions\n\n### `captureLayout()`\n\n```ts\nfunction captureLayout<ElementType extends Element>(\n elements: Iterable<ElementType>,\n options?: LayoutCaptureOptions<ElementType>,\n): LayoutTransition<ElementType>;\n```\n\nCaptures unique elements' positions and sizes and returns a one-shot transition. Rotation and other transforms are not captured or compensated.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `elements` | `Iterable<ElementType>` | Elements measured before a layout change. |\n| `options` | `LayoutCaptureOptions<ElementType>` | Optional stable key mapping for replacement nodes. |\n\n**Returns:** A `LayoutTransition<ElementType>` that measures committed layout once and creates an `AnimationGroup`.\n\n**Example:**\n\n```ts\nimport { captureLayout } from '@vielzeug/necromancer';\n\nconst transition = captureLayout(beforeItems, {\n getKey: (element) => element.getAttribute('data-id')!,\n});\n\nrenderReorderedItems();\n\nconst group = transition.animate({\n duration: 220,\n easing: 'ease-out',\n elements: afterItems,\n});\n```\n\nCalling `animate()` twice on the same transition throws `NecromancerConfigError`.\n\n## Types\n\n### `MotionMode`\n\n```ts\ntype MotionMode = 'full' | 'reduced' | 'system';\n```\n\n`'system'` is the default. Reduced motion preserves the supplied keyframes while normalizing delay, duration, and end delay to `0`, and iterations to `1`.\n\n### `AnimationResult`\n\n```ts\ntype AnimationResult =\n | { readonly status: 'finished' }\n | { readonly status: 'reduced' }\n | { readonly reason?: unknown; readonly status: 'cancelled' };\n```\n\n`cancelled` describes native cancellation and includes its native rejection reason. A reason passed to `dispose()` or an abort signal takes precedence. The independent `disposed` property becomes `true` only when the lifecycle owner is explicitly disposed.\n\n### `AnimateOptions`\n\n```ts\ntype AnimateOptions = KeyframeAnimationOptions & {\n readonly motion?: MotionMode;\n readonly signal?: AbortSignal;\n};\n```\n\nNative timing fields pass through unchanged unless reduced motion normalizes timing. `signal` disposes the returned handle when aborted.\n\n### `AnimateEachOptions`\n\n```ts\ntype AnimateEachOptions = AnimateOptions & {\n readonly stagger?: number;\n};\n```\n\n`stagger` is a finite, non-negative millisecond offset.\n\n### `LayoutCaptureOptions`\n\n```ts\ninterface LayoutCaptureOptions<ElementType extends Element = Element> {\n readonly getKey?: (element: ElementType) => string;\n}\n```\n\n`getKey` maps a captured element and its committed replacement to the same stable, non-empty string. Duplicate or empty keys throw `NecromancerConfigError`.\n\n### `LayoutAnimationOptions`\n\n```ts\ntype LayoutAnimationOptions<ElementType extends Element = Element> = AnimateEachOptions & {\n readonly elements?: Iterable<ElementType>;\n};\n```\n\n`elements` is the collection in its committed layout. Omit it to animate the same captured elements. With `getKey`, replacement elements animate from the positions of their captured predecessors. Unmatched, removed, and newly entered elements are ignored.\n\n### `Keyframes` and `KeyframeFactory`\n\n```ts\ntype Keyframes = readonly Keyframe[] | PropertyIndexedKeyframes;\ntype KeyframeFactory<ElementType extends Element = Element> = (\n element: ElementType,\n index: number,\n total: number,\n) => Keyframes;\n```\n\nAccepts a `readonly` array so a reusable `as const` keyframe list can be passed without a cast.\n\n### `AnimationHandle`\n\n```ts\ninterface AnimationHandle {\n readonly animation: Animation;\n readonly result: Promise<AnimationResult>;\n readonly disposed: boolean;\n dispose(reason?: unknown): void;\n [Symbol.dispose](): void;\n}\n```\n\n| Member | Description |\n| --- | --- |\n| `animation` | Owned native animation used for playback control. |\n| `result` | Resolves with the first terminal outcome. |\n| `disposed` | Reports explicit owner disposal. |\n| `dispose(reason?)` | Cancels the animation and releases its abort listener. |\n| `[Symbol.dispose]()` | Delegates to `dispose()`. |\n\n### `AnimationGroup`\n\n```ts\ninterface AnimationGroup {\n readonly handles: readonly AnimationHandle[];\n readonly results: Promise<readonly AnimationResult[]>;\n readonly disposed: boolean;\n dispose(reason?: unknown): void;\n [Symbol.dispose](): void;\n}\n```\n\n| Member | Description |\n| --- | --- |\n| `handles` | Child handles in input order. |\n| `results` | Resolves with every child outcome in handle order. |\n| `disposed` | Reports explicit group disposal. |\n| `dispose(reason?)` | Disposes every child. |\n| `[Symbol.dispose]()` | Delegates to `dispose()`. |\n\n### `LayoutTransition`\n\n```ts\ninterface LayoutTransition<ElementType extends Element = Element> {\n animate(options?: LayoutAnimationOptions<ElementType>): AnimationGroup;\n}\n```\n\n| Method | Description |\n| --- | --- |\n| `animate(options?)` | Measures committed elements and creates the one allowed layout animation group. |\n\n## Errors\n\n| Error | Trigger |\n| --- | --- |\n| `NecromancerError` | Base class for package errors |\n| `NecromancerConfigError` | Invalid stagger, incompatible delay, or reused layout transition |\n| `NecromancerUnsupportedError` | `Element.animate()` is unavailable |\n\n## Testing (`@vielzeug/necromancer/testing`)\n\njsdom (and most non-browser DOM environments) do not implement `Element.animate()`. Import these from the `/testing` sub-path, not the root entry point.\n\n### `AnimationCall`\n\n```ts\ntype AnimationCall = {\n readonly animation: FakeAnimation;\n readonly keyframes: Keyframe[] | PropertyIndexedKeyframes;\n readonly options?: KeyframeAnimationOptions;\n};\n```\n\nOne recorded invocation of `Element.prototype.animate` from `installFakeAnimations()`.\n\n### `installFakeAnimations()`\n\n```ts\nfunction installFakeAnimations(): {\n readonly calls: readonly AnimationCall[];\n restore(): void;\n [Symbol.dispose](): void;\n};\n```\n\nReplaces `Element.prototype.animate` with a deterministic lifecycle fake for the duration of a test. `calls` records every invocation in order. Call `restore()` or use an explicit resource-management `using` declaration to put the original implementation back. The fake does not implement native playback controls such as `pause()` or `reverse()`.\n\n```ts\nimport { installFakeAnimations } from '@vielzeug/necromancer/testing';\n\nusing animations = installFakeAnimations();\nconst handle = animate(element, [{ opacity: 0 }, { opacity: 1 }]);\n\nanimations.calls[0]?.animation.finish();\nawait handle.result; // { status: 'finished' }\n```\n\n### `FakeAnimation`\n\n```ts\nclass FakeAnimation {\n cancelCallCount: number;\n finishCallCount: number;\n finished: Promise<void>;\n cancel(): void;\n finish(): void;\n}\n```\n\nA minimal `Animation` stand-in. `cancel()` rejects `finished` with an `AbortError`; `finish()` resolves it. `cancelCallCount`/`finishCallCount` track how many times each was called, in place of a test-runner-specific spy.\n\n### `createRect()`\n\n```ts\nfunction createRect(x: number, y: number, width?: number, height?: number): DOMRect;\n```\n\nBuilds a `DOMRect` for mocking `Element.getBoundingClientRect()` in `captureLayout()` tests. `width`/`height` default to `20`.\n",
|
|
6
|
+
"usage": "---\ntitle: Necromancer — Usage Guide\ndescription: Animate DOM elements, coordinate groups, and create FLIP transitions with @vielzeug/necromancer.\n---\n\n[[toc]]\n\n## Basic Usage\n\nCreate an animation after its element mounts, control playback through the native `Animation`, and dispose its owner with the UI lifecycle.\n\n```ts\nimport { animate } from '@vielzeug/necromancer';\n\nconst handle = animate(\n element,\n [{ opacity: 0, transform: 'translateY(8px)' }, { opacity: 1, transform: 'translateY(0)' }],\n { duration: 180, easing: 'ease-out', fill: 'both' },\n);\n\nhandle.animation.reverse();\nconst result = await handle.result;\nhandle.dispose();\n```\n\n`result` distinguishes natural completion, reduced timing, and cancellation. `disposed` reports only whether the owner was explicitly disposed.\n\nOmitted timing fields retain native Web Animations API behavior. Pass an explicit `duration` when the animation should run over time.\n\n## Motion Preferences\n\nUse `motion` to select how the animation responds to the operating system preference.\n\n```ts\nconst handle = animate(element, [{ opacity: 0 }, { opacity: 1 }], {\n duration: 200,\n motion: 'system',\n});\n\nconst result = await handle.result;\n```\n\n`'system'` is the default and reduces movement when `prefers-reduced-motion: reduce` matches. `'full'` preserves the requested timing, while `'reduced'` always reduces it.\n\nReduced motion keeps the supplied keyframes but normalizes delay, duration, and end delay to zero and iterations to one. The result is `{ status: 'reduced' }`, and `handle.animation` still represents the requested visual transition.\n\n## Parent Cancellation\n\nPass a parent `AbortSignal` to release an animation when its owning work is cancelled.\n\n```ts\nconst controller = new AbortController();\nconst handle = animate(element, [{ scale: 0.96 }, { scale: 1 }], {\n duration: 160,\n signal: controller.signal,\n});\n\ncontroller.abort('route changed');\nconst result = await handle.result;\n// { status: 'cancelled', reason: 'route changed' }\n```\n\nAn already-aborted signal throws its reason before an animation starts.\n\n## Staggering a Group\n\nPass an iterable of elements to `animateEach()`. Duplicate elements are animated once in first-seen order.\n\n```ts\nimport { animateEach } from '@vielzeug/necromancer';\n\nconst group = animateEach(\n document.querySelectorAll('.card'),\n (_card, index) => [\n { opacity: 0, transform: `translateY(${12 + index * 2}px)` },\n { opacity: 1, transform: 'translateY(0)' },\n ],\n { duration: 220, easing: 'ease-out', stagger: 45 },\n);\n\nconst results = await group.results;\ngroup.dispose();\n```\n\nA group owns child lifecycles only. `results` preserves every child result in handle order; use `group.handles` when native playback control is required.\n\n## Serial Application Flow\n\nJavaScript control flow is the clearest way to express serial, conditional, or branching animations:\n\n```ts\nfor (const step of steps) {\n const handle = animate(step.element, step.keyframes, {\n ...step.options,\n signal: controller.signal,\n });\n const result = await handle.result;\n\n if (result.status === 'cancelled') break;\n}\n```\n\nOne parent `AbortSignal` cancels the active step without introducing a separate timeline abstraction.\n\n## Animating a Reorder with FLIP\n\nCapture positions before changing layout, then animate through the returned one-shot transition.\n\n```ts\nimport { captureLayout } from '@vielzeug/necromancer';\n\nconst transition = captureLayout(items);\nlist.prepend(items[2]!);\n\nconst group = transition.animate({ duration: 220, easing: 'ease-out' });\nawait group.results;\ngroup.dispose();\n```\n\nThe transition only animates changed, connected elements and can be animated once. It additively composes the individual CSS `translate` and `scale` properties, preserving authored `transform`, `translate`, and `scale`. A resized element (for example a list item whose content changed) animates from its captured size as well as its captured position.\n\n### Replacing rendered elements\n\nWhen a framework replaces list nodes rather than reorders the captured elements, give `captureLayout()` a stable key and pass the committed nodes to `animate()`. Capture before updating state, then call `animate()` only after the renderer has committed the new DOM.\n\n```ts\nconst transition = captureLayout(beforeItems, {\n getKey: (element) => element.getAttribute('data-id')!,\n});\n\nrenderReorderedItems();\n\ntransition.animate({\n duration: 220,\n easing: 'ease-out',\n elements: afterItems,\n});\n```\n\nKeys must be unique, non-empty strings in both collections. Items with no matching predecessor are not enter animations; animate those explicitly with `animate()` or `animateEach()`.\n\nFor sortable lists, DnD exposes its pre-commit layout seam through `onBeforeReorder`; see the [DnD optimistic-reorder recipe](/dnd/examples/optimistic-reorder-with-revert.md).\n\n## Scope\n\nNecromancer creates and owns explicit Web Animations API work. It does not observe CSS-authored transitions or animations, inject `@keyframes`, watch DOM mutations, generate springs, interpolate SVG geometry, or provide a JavaScript tween fallback. Keep CSS as the owner of declarative component styling and use a dedicated charting or tweening tool when the animation needs capabilities beyond WAAPI keyframes.\n\n## Testing\n\njsdom does not implement `Element.animate()`, so lifecycle tests need a fake. `@vielzeug/necromancer/testing` has no test-runner import and supports explicit resource management.\n\n```ts\nimport { animate } from '@vielzeug/necromancer';\nimport { installFakeAnimations } from '@vielzeug/necromancer/testing';\n\nusing animations = installFakeAnimations();\nconst handle = animate(element, [{ opacity: 0 }, { opacity: 1 }]);\n\nanimations.calls[0]?.animation.finish();\nawait handle.result; // { status: 'finished' }\n```\n\nUse `restore()` when `using` is unavailable. The lifecycle fake supports `finish()`, `cancel()`, and `finished`; use browser tests for native playback controls. Use `createRect()` to mock `Element.getBoundingClientRect()` when testing code that calls `captureLayout()`.\n\n## Framework Integration\n\nCreate handles in a client mount lifecycle and dispose them during unmount. The same composition works with reactive effect systems: start the animation in the effect and return `handle.dispose()` as its cleanup.\n\n```tsx\nimport { useEffect, useRef } from 'react';\nimport { animate } from '@vielzeug/necromancer';\n\nexport function Notice() {\n const elementRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const element = elementRef.current;\n if (!element) return;\n\n const handle = animate(element, [{ opacity: 0 }, { opacity: 1 }], { duration: 180 });\n return () => handle.dispose();\n }, []);\n\n return <div ref={elementRef}>Saved</div>;\n}\n```\n\n## Working with Other Vielzeug Libraries\n\nUse Dnd's `onBeforeReorder` hook to capture item layout before a sortable commits its DOM update. Animate the captured transition after `onReorder`; see the [optimistic reorder recipe](/dnd/examples/optimistic-reorder-with-revert.md).\n\n## Best Practices\n\n- Start animations only after their elements mount in the browser.\n- Set animation timing explicitly when native zero-duration behavior is not intended.\n- Dispose each handle or group with its UI owner.\n- Use native `Animation` objects for playback control.\n- Respect the default `'system'` motion setting unless movement is essential.\n- Use a parent `AbortSignal` for cancellable application flow.\n- Keep `delay` numeric when combining it with non-zero `stagger`.\n- Capture layout before mutation and animate each transition exactly once.\n",
|
|
7
|
+
"examples": "---\ntitle: Necromancer — Examples\ndescription: Practical animation and FLIP layout recipes for @vielzeug/necromancer.\n---\n\n## Examples\n\n- [Animate on Mount](./examples/animate-on-mount.md)\n- [Stagger a List](./examples/stagger-a-list.md)\n- [Animate a Reorder](./examples/animate-a-reorder.md)\n\n"
|
|
8
|
+
},
|
|
9
|
+
"examples": [
|
|
10
|
+
{
|
|
11
|
+
"id": "flip",
|
|
12
|
+
"code": "import { captureLayout } from '@vielzeug/necromancer'\n\nconst panel = document.createElement('section')\npanel.style.cssText = 'display: grid; gap: 12px; max-width: 320px; padding: 20px; border: 1px solid #cbd5e1; border-radius: 12px; background: #fff;'\n\nconst reorder = document.createElement('button')\nreorder.textContent = 'Move last item to top'\n\nconst list = document.createElement('div')\nlist.style.cssText = 'display: grid; gap: 8px;'\n\nconst items = ['Alpha', 'Beta', 'Gamma'].map((label) => {\n const wrapper = document.createElement('div')\n const content = document.createElement('div')\n content.textContent = label\n content.style.cssText = 'padding: 12px; border-radius: 8px; color: #fff; background: #ea580c; font: 600 14px system-ui;'\n wrapper.dataset.id = label\n wrapper.appendChild(content)\n list.appendChild(wrapper)\n return wrapper\n})\n\nconst status = document.createElement('output')\nstatus.textContent = 'Ready to reorder'\n\npanel.append(reorder, list, status)\ndocument.body.appendChild(panel)\n\nreorder.addEventListener('click', () => {\n const transition = captureLayout(items, {\n getKey: (item) => item.dataset.id!,\n })\n const last = items.pop()\n if (!last) return\n\n items.unshift(last)\n const replacements = items.map((item) => item.cloneNode(true) as HTMLDivElement)\n list.replaceChildren(...replacements)\n items.splice(0, items.length, ...replacements)\n\n const group = transition.animate({\n duration: 260,\n easing: 'ease-out',\n elements: replacements,\n })\n status.textContent = 'Animating layout change'\n group.results.then(() => {\n status.textContent = 'FLIP animation finished'\n })\n})",
|
|
13
|
+
"name": "captureLayout() - FLIP Reorder"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "lifecycle",
|
|
17
|
+
"code": "import { animate } from '@vielzeug/necromancer'\n\nconst panel = document.createElement('section')\npanel.style.cssText = 'display: grid; gap: 12px; max-width: 320px; padding: 20px; border: 1px solid #cbd5e1; border-radius: 12px; background: #fff;'\n\nconst card = document.createElement('div')\ncard.textContent = 'Lifecycle-owned animation'\ncard.style.cssText = 'padding: 18px; border-radius: 8px; color: #fff; background: #2563eb; font: 600 16px system-ui;'\n\nconst replay = document.createElement('button')\nreplay.textContent = 'Replay animation'\n\nconst status = document.createElement('output')\nstatus.textContent = 'Ready'\n\npanel.append(card, replay, status)\ndocument.body.appendChild(panel)\n\nlet current\n\nfunction run() {\n current?.dispose('replayed')\n current = animate(\n card,\n [\n { opacity: 0, transform: 'translateY(14px) scale(.96)' },\n { opacity: 1, transform: 'translateY(0) scale(1)' },\n ],\n { duration: 280, easing: 'ease-out', fill: 'both' },\n )\n status.textContent = 'Animating…'\n current.result.then((result) => {\n status.textContent = result.status === 'finished' ? 'Finished — handle remains disposable' : result.status === 'reduced' ? 'Finished with reduced timing' : 'Cancelled'\n })\n}\n\nreplay.addEventListener('click', run)\n",
|
|
18
|
+
"name": "animate() - Lifecycle Handle"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "reduced-motion",
|
|
22
|
+
"code": "import { animate } from '@vielzeug/necromancer'\n\nconst panel = document.createElement('section')\npanel.style.cssText = 'display: grid; gap: 12px; max-width: 320px; padding: 20px; border: 1px solid #cbd5e1; border-radius: 12px; background: #fff;'\n\nconst card = document.createElement('div')\ncard.textContent = 'Motion preference'\ncard.style.cssText = 'padding: 18px; border-radius: 8px; color: #fff; background: #7c3aed; font: 600 16px system-ui;'\n\nconst systemButton = document.createElement('button')\nsystemButton.textContent = 'Animate with system preference'\n\nconst skipButton = document.createElement('button')\nskipButton.textContent = 'Use reduced motion'\n\nconst status = document.createElement('output')\nstatus.textContent = 'Choose a mode'\n\npanel.append(card, systemButton, skipButton, status)\ndocument.body.appendChild(panel)\n\nfunction run(motion) {\n const handle = animate(\n card,\n [\n { opacity: 0, transform: 'translateX(-18px)' },\n { opacity: 1, transform: 'translateX(0)' },\n ],\n { duration: 320, easing: 'ease-out', motion, fill: 'both' },\n )\n status.textContent = 'Running with motion: ' + motion\n handle.result.then((result) => {\n status.textContent = result.status === 'reduced' ? 'Keyframes finished with reduced timing' : 'Finished'\n })\n}\n\nsystemButton.addEventListener('click', () => run('system'))\nskipButton.addEventListener('click', () => run('reduced'))",
|
|
23
|
+
"name": "motion - Reduced Motion"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "stagger",
|
|
27
|
+
"code": "import { animateEach } from '@vielzeug/necromancer'\n\nconst panel = document.createElement('section')\npanel.style.cssText = 'display: grid; gap: 12px; max-width: 320px; padding: 20px; border: 1px solid #cbd5e1; border-radius: 12px; background: #fff;'\n\nconst replay = document.createElement('button')\nreplay.textContent = 'Stagger cards'\n\nconst stack = document.createElement('div')\nstack.style.cssText = 'display: grid; gap: 8px;'\n\nconst cards = ['First', 'Second', 'Third', 'Fourth'].map((label) => {\n const card = document.createElement('div')\n card.textContent = label\n card.style.cssText = 'padding: 12px; border-radius: 8px; color: #fff; background: #0891b2; font: 600 14px system-ui;'\n stack.appendChild(card)\n return card\n})\n\nconst status = document.createElement('output')\nstatus.textContent = 'Ready'\n\npanel.append(replay, stack, status)\ndocument.body.appendChild(panel)\n\nfunction run() {\n const group = animateEach(\n cards,\n (_card, index) => [\n { opacity: 0, transform: 'translateY(' + (16 + index * 2) + 'px)' },\n { opacity: 1, transform: 'translateY(0)' },\n ],\n { duration: 240, easing: 'ease-out', stagger: 70, fill: 'both' },\n )\n status.textContent = 'Staggering ' + group.handles.length + ' cards'\n group.results.then(() => {\n status.textContent = 'All cards settled'\n })\n}\n\nreplay.addEventListener('click', run)\n",
|
|
28
|
+
"name": "animateEach() - Stagger a Group"
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"typeSignatures": {
|
|
32
|
+
"animate": "export { animate } from './animate';",
|
|
33
|
+
"animateEach": "export { animateEach } from './animate-each';",
|
|
34
|
+
"NecromancerConfigError": "export { NecromancerConfigError, NecromancerError, NecromancerUnsupportedError } from './errors';",
|
|
35
|
+
"NecromancerError": "export { NecromancerConfigError, NecromancerError, NecromancerUnsupportedError } from './errors';",
|
|
36
|
+
"NecromancerUnsupportedError": "export { NecromancerConfigError, NecromancerError, NecromancerUnsupportedError } from './errors';",
|
|
37
|
+
"captureLayout": "export { captureLayout } from './layout';",
|
|
38
|
+
"AnimateEachOptions": "export type {\n AnimateEachOptions,\n AnimateOptions,\n AnimationGroup,\n AnimationHandle,\n AnimationResult,\n KeyframeFactory,\n Keyframes,\n LayoutAnimationOptions,\n LayoutCaptureOptions,\n LayoutTransition,\n MotionMode,\n} from './types';",
|
|
39
|
+
"AnimateOptions": "export type {\n AnimateEachOptions,\n AnimateOptions,\n AnimationGroup,\n AnimationHandle,\n AnimationResult,\n KeyframeFactory,\n Keyframes,\n LayoutAnimationOptions,\n LayoutCaptureOptions,\n LayoutTransition,\n MotionMode,\n} from './types';",
|
|
40
|
+
"AnimationGroup": "export type {\n AnimateEachOptions,\n AnimateOptions,\n AnimationGroup,\n AnimationHandle,\n AnimationResult,\n KeyframeFactory,\n Keyframes,\n LayoutAnimationOptions,\n LayoutCaptureOptions,\n LayoutTransition,\n MotionMode,\n} from './types';",
|
|
41
|
+
"AnimationHandle": "export type {\n AnimateEachOptions,\n AnimateOptions,\n AnimationGroup,\n AnimationHandle,\n AnimationResult,\n KeyframeFactory,\n Keyframes,\n LayoutAnimationOptions,\n LayoutCaptureOptions,\n LayoutTransition,\n MotionMode,\n} from './types';",
|
|
42
|
+
"AnimationResult": "export type {\n AnimateEachOptions,\n AnimateOptions,\n AnimationGroup,\n AnimationHandle,\n AnimationResult,\n KeyframeFactory,\n Keyframes,\n LayoutAnimationOptions,\n LayoutCaptureOptions,\n LayoutTransition,\n MotionMode,\n} from './types';",
|
|
43
|
+
"KeyframeFactory": "export type {\n AnimateEachOptions,\n AnimateOptions,\n AnimationGroup,\n AnimationHandle,\n AnimationResult,\n KeyframeFactory,\n Keyframes,\n LayoutAnimationOptions,\n LayoutCaptureOptions,\n LayoutTransition,\n MotionMode,\n} from './types';",
|
|
44
|
+
"Keyframes": "export type {\n AnimateEachOptions,\n AnimateOptions,\n AnimationGroup,\n AnimationHandle,\n AnimationResult,\n KeyframeFactory,\n Keyframes,\n LayoutAnimationOptions,\n LayoutCaptureOptions,\n LayoutTransition,\n MotionMode,\n} from './types';",
|
|
45
|
+
"LayoutAnimationOptions": "export type {\n AnimateEachOptions,\n AnimateOptions,\n AnimationGroup,\n AnimationHandle,\n AnimationResult,\n KeyframeFactory,\n Keyframes,\n LayoutAnimationOptions,\n LayoutCaptureOptions,\n LayoutTransition,\n MotionMode,\n} from './types';",
|
|
46
|
+
"LayoutCaptureOptions": "export type {\n AnimateEachOptions,\n AnimateOptions,\n AnimationGroup,\n AnimationHandle,\n AnimationResult,\n KeyframeFactory,\n Keyframes,\n LayoutAnimationOptions,\n LayoutCaptureOptions,\n LayoutTransition,\n MotionMode,\n} from './types';",
|
|
47
|
+
"LayoutTransition": "export type {\n AnimateEachOptions,\n AnimateOptions,\n AnimationGroup,\n AnimationHandle,\n AnimationResult,\n KeyframeFactory,\n Keyframes,\n LayoutAnimationOptions,\n LayoutCaptureOptions,\n LayoutTransition,\n MotionMode,\n} from './types';",
|
|
48
|
+
"MotionMode": "export type {\n AnimateEachOptions,\n AnimateOptions,\n AnimationGroup,\n AnimationHandle,\n AnimationResult,\n KeyframeFactory,\n Keyframes,\n LayoutAnimationOptions,\n LayoutCaptureOptions,\n LayoutTransition,\n MotionMode,\n} from './types';"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"apiSource": "// Auto-update\nexport type { AutoUpdateOptions } from './auto-update';\nexport { autoUpdate } from './auto-update';\n// Core engine\nexport { computePosition } from './core';\n// Errors\nexport { OrbitConfigError, OrbitError } from './errors';\n// High-level API\nexport type { Positioner, PositionerOptions, PositionStrategy } from './float';\nexport { createPositioner } from './float';\n// Inline middleware\nexport type { InlineOptions } from './inline';\nexport { inline } from './inline';\n// Middleware\nexport type { ArrowOptions } from './middleware/arrow';\nexport { arrow } from './middleware/arrow';\nexport type { AutoPlacementOptions } from './middleware/auto-placement';\nexport { autoPlacement } from './middleware/auto-placement';\nexport type { FlipOptions } from './middleware/flip';\nexport { flip } from './middleware/flip';\nexport type { HideOptions } from './middleware/hide';\nexport { hide } from './middleware/hide';\nexport type { OffsetConfig, OffsetValue } from './middleware/offset';\nexport { offset } from './middleware/offset';\nexport type { LimitShiftOptions, ShiftLimiter, ShiftOptions } from './middleware/shift';\nexport { limitShift, shift } from './middleware/shift';\nexport type { SizeOptions } from './middleware/size';\nexport { size } from './middleware/size';\n// Overflow helpers\nexport { detectOverflow, getClippingAncestorRect } from './overflow';\n// Preset types (functions live on the @vielzeug/orbit/presets sub-path)\nexport type { PositioningPreset, PresetOptions } from './presets';\n// Types\nexport type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';\n// Public utilities\nexport { getAlignment, getSide } from './utils';\n",
|
|
3
|
+
"docs": {
|
|
4
|
+
"index": "---\ntitle: Orbit — Floating UI positioning\ndescription: Floating positioning with lifecycle-owned geometry and middleware.\npackage: orbit\ncategory: ui\nkeywords: [positioning, tooltip, popover, dropdown, middleware, floating-ui]\nexports: [autoUpdate, computePosition, createPositioner]\nrelated: [ore, refine, prism]\nenvironments: [browser]\n---\n\n<!-- markdownlint-disable MD025 MD033 MD060 -->\n\n<PackageHero package=\"orbit\" />\n\n## Why Orbit?\n\nFloating UI needs one owner for CSS coordinates, clipping boundaries, updates, and cleanup. Orbit provides a lifecycle positioner for normal UI and a pure computation API for advanced integrations.\n\n```ts\n// Before\nconst { x, y } = computeSomehow(trigger, panel);\npanel.style.left = `${x}px`;\npanel.style.top = `${y}px`;\n\n// After\nconst positioner = createPositioner(trigger, panel);\n```\n\n| Feature | Manual DOM positioning | Orbit |\n| --- | --- | --- |\n| Bundle size | 0 B | <PackageInfo package=\"orbit\" type=\"size\" /> |\n| Root dependencies | Application-defined | `@vielzeug/arsenal` |\n| Clipping boundary | Manual geometry | `clippingAncestors` default |\n| Coordinate strategy | Consumer logic | `fixed` / `absolute` |\n| Cleanup | Manual listeners | `dispose()` |\n\n<div class=\"decision-callout\">\n\n**Use Orbit when** floating UI needs robust placement, collision handling, or reactive updates.\n\n**Consider direct CSS when** placement is static and never depends on element geometry.\n\n</div>\n\n## Installation\n\n::: code-group\n\n```sh [pnpm]\npnpm add @vielzeug/orbit\n```\n\n```sh [npm]\nnpm install @vielzeug/orbit\n```\n\n```sh [yarn]\nyarn add @vielzeug/orbit\n```\n\n:::\n\n## Quick Start\n\nCreate a positioner only after its reference and floating elements mount; positioning starts immediately.\n\n```ts\nimport { createPositioner, flip, offset, shift } from '@vielzeug/orbit';\n\nconst positioner = createPositioner(trigger, tooltip, {\n middleware: [offset(8), flip(), shift({ padding: 6 })],\n placement: 'top',\n});\n\npositioner.dispose();\n```\n\n## Features\n\n<div class=\"features-grid\">\n\n- `createPositioner()` — Lifecycle-owned floating positioning\n- `computePosition()` — Low-level calculation for advanced integrations\n- `autoUpdate()` — Scroll, viewport, resize, and animation-frame updates\n- Middleware — Offset, flip, shift, size, hide, arrow, inline, auto-placement\n- `strategy` — Explicit `fixed` or `absolute` coordinate behavior\n- `/reactive` — Optional Ripple position readable\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- [Migration Guide](./migration.md)\n\n</div>\n\n## See Also\n\n<div class=\"see-also\">\n\n- [Refine](/refine/) — Accessible components using floating UI behavior.\n- [Ore](/ore/) — Lifecycle ownership for custom-element positioning.\n- [Prism](/prism/) — Chart tooltips positioned from virtual references.\n\n</div>\n\n<!-- markdownlint-enable MD025 MD033 MD060 -->\n",
|
|
5
|
+
"api": "---\ntitle: Orbit — API Reference\ndescription: API reference for @vielzeug/orbit positioners, computation, updates, middleware, and optional reactive integration.\n---\n\n[[toc]]\n\n## API Overview\n\n| Symbol | Purpose | Execution mode | Common gotcha |\n| --- | --- | --- | --- |\n| `createPositioner()` | Lifecycle-owned floating positioning | Sync | Starts immediately; create after mount |\n| `computePosition()` | Low-level geometry computation | Sync | Caller owns CSS application |\n| `autoUpdate()` | Listen for geometry changes | Sync | Call returned cleanup |\n| `createReactivePositioner()` | Optional Ripple position readable | Sync | Requires `@vielzeug/ripple` |\n| Middleware factories | Adjust placement and size | Sync | Order is explicit |\n\n## Package Entry Points\n\n| Import | Purpose |\n| --- | --- |\n| `@vielzeug/orbit` | Positioner, computation, updates, middleware, and types. |\n| `@vielzeug/orbit/reactive` | Optional Ripple position adapter. |\n| `@vielzeug/orbit/presets` | Preset placement and middleware options. |\n| `@vielzeug/orbit/devtools` | Development overlay. |\n\n## Core Functions\n\n### `createPositioner()`\n\n```ts\nfunction createPositioner(\n reference: ReferenceElement,\n floating: HTMLElement,\n options?: PositionerOptions,\n): Positioner;\n```\n\nCreates an active positioner and applies its initial position before returning.\n\n| Parameter | Type | Description |\n| --- | --- | --- |\n| `reference` | `ReferenceElement` | DOM or virtual anchor. |\n| `floating` | `HTMLElement` | Positioned element. |\n| `options` | `PositionerOptions` | Strategy, clipping, middleware, updates, and application callback. |\n\n**Returns:** `Positioner`.\n\n```ts\nimport { createPositioner } from '@vielzeug/orbit';\n\nconst positioner = createPositioner(trigger, tooltip);\npositioner.dispose();\n```\n\n| Member | Return | Contract |\n| --- | --- | --- |\n| `update()` | `void` | Recomputes and applies position. |\n| `getPosition()` | `ComputePositionResult` | Latest applied result. |\n| `dispose()` | `void` | Stops updates and aborts disposal signal. |\n\n### `computePosition()`\n\n```ts\nfunction computePosition(\n reference: ReferenceElement,\n floating: HTMLElement,\n options?: ComputePositionOptions,\n): ComputePositionResult;\n```\n\nCalculates position without applying DOM styles or creating listeners.\n\n**Returns:** `ComputePositionResult`.\n\n### `autoUpdate()`\n\n```ts\nfunction autoUpdate(\n reference: ReferenceElement,\n floating: HTMLElement,\n update: () => void,\n options?: AutoUpdateOptions,\n): () => void;\n```\n\nCalls `update` immediately, then on relevant scroll, viewport, resize, and optional animation-frame changes.\n\n**Returns:** cleanup callback.\n\n## Middleware\n\n```ts\ntype Middleware = (state: MiddlewareState) => MiddlewareResult | undefined;\n```\n\nBuilt-in factories: `arrow`, `autoPlacement`, `flip`, `hide`, `inline`, `offset`, `shift`, `limitShift`, and `size`.\n\n```ts\nconst middleware = [offset(8), flip(), shift({ padding: 6 }), size()];\n```\n\n`middlewareData` is `MiddlewareData`; narrow custom data at the consuming boundary.\n\n## Reactive Adapter\n\n```ts\nfunction createReactivePositioner(\n reference: ReferenceElement,\n floating: HTMLElement,\n options?: Omit<PositionerOptions, 'apply'>,\n): ReactivePositioner;\n```\n\n`ReactivePositioner.position` is `Readable<ComputePositionResult>`. Imported from `@vielzeug/orbit/reactive`.\n\n## Types\n\n```ts\ntype Side = 'top' | 'bottom' | 'left' | 'right';\ntype Alignment = 'start' | 'end';\ntype Placement = Side | `${Side}-${Alignment}`;\n\ninterface Rect {\n height: number;\n width: number;\n x: number;\n y: number;\n}\n\ninterface VirtualReference {\n getBoundingClientRect: () => DOMRect | Rect;\n getClientRects?: () => DOMRectList | DOMRect[];\n}\n\ntype ReferenceElement = Element | VirtualReference;\n\ninterface SideObject {\n bottom: number;\n left: number;\n right: number;\n top: number;\n}\n\ntype Padding = number | Partial<SideObject>;\n\ninterface ArrowData {\n centerOffset: number;\n constrained: boolean;\n x?: number;\n y?: number;\n}\n\ninterface FlipData {\n skippedPlacements: Placement[];\n}\n\ninterface ShiftData {\n x: number;\n y: number;\n}\n\ninterface HideData {\n escaped?: boolean;\n escapedOffsets?: SideObject;\n referenceHidden?: boolean;\n referenceHiddenOffsets?: SideObject;\n}\n\ninterface SizeData {\n availableHeight: number;\n availableWidth: number;\n}\n\ninterface MiddlewareData {\n arrow?: ArrowData;\n flip?: FlipData;\n hide?: HideData;\n shift?: ShiftData;\n size?: SizeData;\n [key: string]: unknown;\n}\n\ninterface MiddlewareState {\n boundary?: Element | Rect;\n elements: { floating: HTMLElement; reference: ReferenceElement };\n initialPlacement: Placement;\n middlewareData: MiddlewareData;\n padding?: Padding;\n placement: Placement;\n rects: { floating: Rect; reference: Rect };\n x: number;\n y: number;\n}\n\ntype MiddlewareReset = {\n placement?: Placement;\n rects?: MiddlewareState['rects'];\n remeasure?: boolean;\n};\n\ninterface MiddlewareResult {\n data?: MiddlewareData;\n placement?: Placement;\n reset?: MiddlewareReset;\n x?: number;\n y?: number;\n}\n\ntype Middleware = (state: MiddlewareState) => MiddlewareResult | undefined;\n\ninterface ComputePositionResult {\n middlewareData: MiddlewareData;\n placement: Placement;\n x: number;\n y: number;\n}\n\ninterface ComputePositionOptions {\n boundary?: Element | Rect;\n containingBlock?: Element | null;\n middleware?: readonly Middleware[];\n padding?: Padding;\n placement?: Placement;\n}\n\ninterface DetectOverflowOptions {\n boundary?: Element | Rect;\n padding?: Padding;\n}\n\ntype PositionStrategy = 'absolute' | 'fixed';\n\ninterface PositionerOptions extends Omit<ComputePositionOptions, 'boundary' | 'containingBlock'> {\n apply?: (result: ComputePositionResult) => void;\n autoUpdate?: AutoUpdateOptions | false;\n boundary?: ComputePositionOptions['boundary'] | 'clippingAncestors';\n strategy?: PositionStrategy;\n}\n\ninterface Positioner {\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n getPosition(): ComputePositionResult;\n update(): void;\n [Symbol.dispose](): void;\n}\n\ninterface AutoUpdateOptions {\n animationFrame?: boolean;\n observeAncestors?: boolean;\n observeFloating?: boolean;\n observeVisualViewport?: boolean;\n pauseWhenHidden?: boolean;\n throttle?: number;\n}\n\ninterface ReactivePositioner extends Positioner {\n readonly position: Readable<ComputePositionResult>;\n}\n\ninterface ArrowOptions {\n element: HTMLElement;\n padding?: Padding;\n}\n\ninterface AutoPlacementOptions extends DetectOverflowOptions {\n alignment?: Alignment | null;\n allowedPlacements?: Placement[];\n}\n\ninterface FlipOptions extends DetectOverflowOptions {\n fallbackPlacements?: Placement[];\n}\n\ninterface HideOptions extends DetectOverflowOptions {\n strategy?: 'referenceHidden' | 'escaped' | 'both';\n}\n\ntype OffsetConfig = {\n crossAxis?: number;\n mainAxis?: number;\n};\n\ntype OffsetValue = number | OffsetConfig | ((state: MiddlewareState) => number | OffsetConfig);\n\ntype ShiftLimiter = (\n state: MiddlewareState,\n correction: { crossAxis: number; mainAxis: number },\n) => { crossAxis: number; mainAxis: number };\n\ninterface LimitShiftOptions {\n offset?: number | ((state: MiddlewareState) => number);\n}\n\ninterface ShiftOptions extends DetectOverflowOptions {\n crossAxis?: boolean;\n limiter?: ShiftLimiter;\n}\n\ninterface InlineOptions {\n padding?: Padding;\n x?: number;\n y?: number;\n}\n\ntype SizeOptions = DetectOverflowOptions;\n\ninterface PositioningPreset {\n middleware: Middleware[];\n placement: Placement;\n}\n\ninterface PresetOptions {\n offset?: number;\n padding?: number;\n placement?: Placement;\n}\n```\n\n## Errors\n\n| Error | Trigger | Notable properties |\n| --- | --- | --- |\n| `OrbitConfigError` | Invalid middleware reset configuration | Extends `OrbitError` |\n| `OrbitError` | Base Orbit error | `instanceof OrbitError` narrows Orbit errors |\n",
|
|
6
|
+
"usage": "---\ntitle: Orbit — Usage Guide\ndescription: Position floating UI with lifecycle ownership, explicit coordinate strategy, middleware, and optional reactive state.\n---\n\n[[toc]]\n\n## Basic Usage\n\nCreate a positioner after both elements mount, then dispose it with their owner.\n\n```ts\nimport { createPositioner, flip, offset, shift } from '@vielzeug/orbit';\n\nconst positioner = createPositioner(trigger, tooltip, {\n middleware: [offset(8), flip(), shift({ padding: 6 })],\n placement: 'top',\n});\n\npositioner.dispose();\n```\n\n`createPositioner()` applies the initial position before returning and owns clipping-boundary resolution, updates, CSS strategy, and cleanup.\n\n## Coordinate Strategy\n\nUse `fixed` for viewport-positioned overlays. Use `absolute` when the floating element should position within its offset parent.\n\n```ts\nconst positioner = createPositioner(trigger, dropdown, {\n placement: 'bottom-start',\n strategy: 'absolute',\n});\n\n```\n\nOrbit resolves clipping ancestors by default. Pass an explicit `boundary` when your application owns a different visible region.\n\n## Middleware\n\nPass middleware in the exact order it should execute.\n\n```ts\nconst positioner = createPositioner(trigger, panel, {\n middleware: [\n offset(8),\n flip(),\n shift({ padding: 8 }),\n size(),\n arrow({ element: arrowElement }),\n ],\n});\n```\n\nUse either `flip()` or `autoPlacement()` for one positioner. Custom middleware writes data into `result.middlewareData`.\n\n## Virtual References\n\nUse a virtual reference for cursor-anchored UI.\n\n```ts\nconst reference = {\n getBoundingClientRect: () => ({ height: 0, width: 0, x: event.clientX, y: event.clientY }),\n};\n\nconst positioner = createPositioner(reference, menu, { placement: 'bottom-start' });\n```\n\n## Manual Positioning\n\nUse `computePosition()` only when your application owns CSS application and lifecycle itself.\n\n```ts\nimport { computePosition, offset } from '@vielzeug/orbit';\n\nconst result = computePosition(reference, floating, { middleware: [offset(8)] });\nfloating.style.left = `${result.x}px`;\nfloating.style.top = `${result.y}px`;\n```\n\n## Reactive Adapter\n\nInstall Ripple and import the optional adapter only when your UI needs a reactive position value.\n\n```ts\nimport { createReactivePositioner } from '@vielzeug/orbit/reactive';\nimport { effect } from '@vielzeug/ripple';\n\nconst positioner = createReactivePositioner(trigger, tooltip);\n\neffect(() => {\n const position = positioner.position.value;\n\n tooltip.style.left = `${position.x}px`;\n tooltip.style.top = `${position.y}px`;\n});\n```\n\n## Client Lifecycle\n\nOrbit root imports are server-safe. Invoke geometry APIs only from a client mount lifecycle, where DOM elements exist.\n\n```ts\nonMounted(() => {\n const positioner = createPositioner(trigger, panel);\n onCleanup(() => positioner.dispose());\n});\n```\n\n## Framework Integration\n\nCreate and dispose positioners with component lifecycle.\n\n::: code-group\n\n```tsx [React]\nuseEffect(() => {\n const positioner = createPositioner(trigger, panel);\n\n return () => positioner.dispose();\n}, [trigger, panel]);\n```\n\n```vue [Vue 3]\n<script setup lang=\"ts\">\nonMounted(() => {\n const positioner = createPositioner(trigger.value!, panel.value!);\n onUnmounted(() => positioner.dispose());\n});\n</script>\n```\n\n```ts [Svelte]\nonMount(() => {\n const positioner = createPositioner(trigger, panel);\n\n return () => positioner.dispose();\n});\n```\n\n:::\n\n## Working with Other Vielzeug Libraries\n\n### Orbit + Prism\n\nUse `strategy: 'absolute'` for a tooltip rendered inside a chart container.\n\n```ts\nconst positioner = createPositioner(cursorReference, tooltip, {\n autoUpdate: false,\n strategy: 'absolute',\n});\n\npositioner.dispose();\n```\n\n## Best Practices\n\n- **Create** a positioner after both DOM elements mount; it starts immediately.\n- **Dispose** it with its UI owner.\n- **Choose** `fixed` or `absolute` intentionally.\n- **Keep** middleware order explicit.\n- **Use** `computePosition()` only for advanced platform-managed paths.\n- **Install** Ripple only when importing `/reactive`.\n- **Invoke** geometry APIs only on the client.\n",
|
|
7
|
+
"examples": "---\ntitle: Orbit — Examples\ndescription: Worked examples for @vielzeug/orbit.\n---\n\n## Examples\n\n- [Context Menu](./examples/context-menu.md)\n- [Custom Middleware](./examples/custom-middleware.md)\n- [Dropdown Select](./examples/dropdown-select.md)\n- [Popover with Arrow](./examples/popover-with-arrow.md)\n- [Reactive Adapter](./examples/reactive-adapter.md)\n- [Tooltip](./examples/tooltip.md)\n- [Using Presets](./examples/using-presets.md)\n- [With Ore Component](./examples/with-ore-component.md)\n"
|
|
8
|
+
},
|
|
9
|
+
"examples": [
|
|
10
|
+
{
|
|
11
|
+
"id": "auto-update",
|
|
12
|
+
"code": "import { autoUpdate, computePosition, flip, offset, shift } from '@vielzeug/orbit'\n\nconst button = document.createElement('button')\nbutton.textContent = 'Reference'\nbutton.style.cssText = 'position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); padding: 8px 16px;'\ndocument.body.appendChild(button)\n\nconst dropdown = document.createElement('div')\ndropdown.textContent = 'Dropdown'\n// position: fixed with left: 0; top: 0 so left/top writes are absolute viewport coords\ndropdown.style.cssText = 'position: fixed; left: 0; top: 0; background: #fff; border: 1px solid #e5e5e5; border-radius: 6px; padding: 12px 16px; box-shadow: 0 4px 12px rgba(0,0,0,.1); z-index: 1000;'\ndocument.body.appendChild(dropdown)\n\nconst middleware = [offset(4), flip(), shift({ padding: 8 })]\n\nfunction update() {\n const { x, y, placement } = computePosition(button, dropdown, {\n placement: 'bottom-start',\n middleware,\n })\n dropdown.style.left = x + 'px'\n dropdown.style.top = y + 'px'\n dropdown.dataset.placement = placement\n console.log('Positioned:', placement)\n}\n\n// autoUpdate calls update immediately then re-calls on scroll/resize/mutation\nconst cleanup = autoUpdate(button, dropdown, update)\n\nconsole.log('autoUpdate running — try resizing the window')\nconsole.log('cleanup type (call to stop):', typeof cleanup)",
|
|
13
|
+
"name": "autoUpdate - Track on Scroll/Resize"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "inline-middleware",
|
|
17
|
+
"code": "import { computePosition, flip, inline, shift } from '@vielzeug/orbit'\n\n// inline() corrects the reference rect for multi-line inline elements.\n// It picks the client rect closest to the cursor (or floating element).\nconst span = document.createElement('span')\nspan.textContent = 'Hover to reveal tooltip — this is a long inline element that may wrap'\nspan.style.cssText = 'line-height: 1.8; cursor: pointer; background: #f0f0f0; padding: 2px 4px; border-radius: 3px;'\ndocument.body.appendChild(span)\n\nconst tooltip = document.createElement('div')\ntooltip.textContent = 'inline() picks the nearest client rect'\ntooltip.style.cssText = 'position: fixed; left: 0; top: 0; background: #1a1a2e; color: #e0e0ff; padding: 6px 10px; border-radius: 5px; font-size: 12px; pointer-events: none; z-index: 1000;'\ntooltip.hidden = true\ndocument.body.appendChild(tooltip)\n\nlet cursorX = 0\nlet cursorY = 0\n\nspan.addEventListener('mousemove', (e) => {\n cursorX = e.clientX\n cursorY = e.clientY\n tooltip.hidden = false\n\n const { x, y } = computePosition(span, tooltip, {\n placement: 'top',\n middleware: [inline({ x: cursorX, y: cursorY }), flip(), shift({ padding: 4 })],\n })\n tooltip.style.left = x + 'px'\n tooltip.style.top = y + 'px'\n})\n\nspan.addEventListener('mouseleave', () => { tooltip.hidden = true })\n\nconsole.log('Hover over the span to see inline() in action')",
|
|
18
|
+
"name": "inline - Multi-line Inline Reference"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "position-basic",
|
|
22
|
+
"code": "import { computePosition } from '@vielzeug/orbit'\n\n// Create reference and floating elements\nconst button = document.createElement('button')\nbutton.textContent = 'Anchor'\nbutton.style.cssText = 'padding: 8px 16px; margin: 50px;'\ndocument.body.appendChild(button)\n\nconst tooltip = document.createElement('div')\ntooltip.textContent = 'Tooltip'\ntooltip.style.cssText = 'position: fixed; background: #333; color: #fff; padding: 8px 12px; border-radius: 4px; font-size: 12px; z-index: 1000;'\ndocument.body.appendChild(tooltip)\n\n// computePosition is sync and returns x/y/placement/middlewareData\nfunction updatePosition() {\n const { x, y, placement } = computePosition(button, tooltip, {\n placement: 'top',\n })\n tooltip.style.left = x + 'px'\n tooltip.style.top = y + 'px'\n console.log(`Positioned at ${placement}: (${Math.round(x)}, ${Math.round(y)})`)\n}\n\nbutton.addEventListener('click', updatePosition)\nupdatePosition()\nconsole.log('Tooltip positioned relative to button')",
|
|
23
|
+
"name": "computePosition - Basic"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "position-float",
|
|
27
|
+
"code": "import { createPositioner, offset, flip, shift } from '@vielzeug/orbit'\n\nconst button = document.createElement('button')\nbutton.textContent = 'Hover me'\nbutton.style.cssText = 'margin: 100px; padding: 8px 16px;'\ndocument.body.appendChild(button)\n\nconst tooltip = document.createElement('div')\ntooltip.textContent = 'Tooltip with middleware'\ntooltip.style.cssText = 'position: fixed; background: #1e293b; color: #fff; padding: 8px 12px; border-radius: 6px; font-size: 13px; pointer-events: none; display: none;'\ndocument.body.appendChild(tooltip)\n\nlet positioner = null\n\nfunction show() {\n tooltip.style.display = 'block'\n positioner?.dispose()\n positioner = createPositioner(button, tooltip, {\n middleware: [offset(8), flip(), shift({ padding: 8 })],\n placement: 'top',\n })\n console.log('Placement:', positioner.getPosition().placement)\n}\n\nfunction hide() {\n tooltip.style.display = 'none'\n positioner?.dispose()\n positioner = null\n}\n\nbutton.addEventListener('mouseenter', show)\nbutton.addEventListener('mouseleave', hide)\n\nconsole.log('Hover the button to position the tooltip')",
|
|
28
|
+
"name": "createPositioner - With Middleware"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": "presets",
|
|
32
|
+
"code": "import { createPositioner } from '@vielzeug/orbit'\nimport { tooltip, dropdown, popover, contextMenu } from '@vielzeug/orbit/presets'\n\n// Presets are pre-configured middleware stacks for common UI patterns.\n// Each factory returns { placement, middleware } — spread into createPositioner().\n\n// --- tooltip() ---\nconst tooltipPreset = tooltip()\nconsole.log('tooltip placement:', tooltipPreset.placement)\nconsole.log('tooltip middleware count:', tooltipPreset.middleware.length)\n\n// Customise placement and offset:\nconst topTooltip = tooltip({ placement: 'top', offset: 12 })\nconsole.log('custom tooltip placement:', topTooltip.placement)\n\n// --- dropdown() ---\nconst dropdownPreset = dropdown()\nconsole.log('dropdown placement:', dropdownPreset.placement)\n\nconst wideDropdown = dropdown({ offset: 8, padding: 6 })\nconsole.log('wide dropdown middleware count:', wideDropdown.middleware.length)\n\n// --- popover() ---\nconst popoverPreset = popover()\nconsole.log('popover placement:', popoverPreset.placement)\n\n// --- contextMenu() ---\nconst menuPreset = contextMenu()\nconsole.log('contextMenu placement:', menuPreset.placement)\n\nconst menuTopStart = contextMenu({ placement: 'top-start' })\nconsole.log('contextMenu custom placement:', menuTopStart.placement)\n\n// --- Spread a preset into createPositioner() ---\nconst trigger = document.createElement('button')\ntrigger.textContent = 'Hover me'\ntrigger.style.cssText = 'margin: 80px; padding: 8px 16px;'\ndocument.body.appendChild(trigger)\n\nconst tip = document.createElement('div')\ntip.textContent = 'tooltip()'\ntip.style.cssText = 'position: fixed; background: #1e293b; color: #fff; padding: 6px 10px; border-radius: 6px; font-size: 13px; display: none;'\ndocument.body.appendChild(tip)\n\nlet positioner = null\n\ntrigger.addEventListener('mouseenter', () => {\n tip.style.display = 'block'\n positioner = createPositioner(trigger, tip, tooltip())\n})\ntrigger.addEventListener('mouseleave', () => {\n tip.style.display = 'none'\n positioner?.dispose()\n positioner = null\n})\n\nconsole.log('Hover the button to see tooltip() in action')",
|
|
33
|
+
"name": "presets - Ready-made Middleware Stacks"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "size-middleware",
|
|
37
|
+
"code": "import { createPositioner, offset, flip, size } from '@vielzeug/orbit'\n\nconst button = document.createElement('button')\nbutton.textContent = 'Open dropdown'\nbutton.style.cssText = 'margin:50px;padding:8px 16px;'\ndocument.body.appendChild(button)\n\nconst dropdown = document.createElement('div')\ndropdown.style.cssText = 'position:fixed;left:0;top:0;background:#fff;border:1px solid #e5e5e5;border-radius:6px;overflow-y:auto;box-shadow:0 4px 12px rgba(0,0,0,.1);'\n// Populate dropdown with many items\nfor (let i = 1; i <= 20; i++) {\n const item = document.createElement('div')\n item.textContent = 'Option ' + i\n item.style.cssText = 'padding:8px 16px;cursor:pointer;'\n dropdown.appendChild(item)\n}\ndocument.body.appendChild(dropdown)\n\n// size() writes availableHeight/availableWidth to middlewareData.size.\n// Read it in the positioner apply callback to constrain the floating element.\nconst positioner = createPositioner(button, dropdown, {\n placement: 'bottom-start',\n middleware: [offset(4), flip(), size({ padding: 8 })],\n apply(result) {\n const sizeData = result.middlewareData.size\n if (sizeData) {\n dropdown.style.maxHeight = Math.min(sizeData.availableHeight, 300) + 'px'\n console.log('Available height:', sizeData.availableHeight)\n }\n dropdown.style.left = result.x + 'px'\n dropdown.style.top = result.y + 'px'\n console.log('Resolved placement:', result.placement)\n },\n})\n\nconsole.log('size() constrains dropdown height to available space')",
|
|
38
|
+
"name": "size() - Constrain Height"
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"typeSignatures": {
|
|
42
|
+
"AutoUpdateOptions": "export type { AutoUpdateOptions } from './auto-update';",
|
|
43
|
+
"autoUpdate": "export { autoUpdate } from './auto-update';",
|
|
44
|
+
"computePosition": "export { computePosition } from './core';",
|
|
45
|
+
"OrbitConfigError": "export { OrbitConfigError, OrbitError } from './errors';",
|
|
46
|
+
"OrbitError": "export { OrbitConfigError, OrbitError } from './errors';",
|
|
47
|
+
"Positioner": "export type { Positioner, PositionerOptions, PositionStrategy } from './float';",
|
|
48
|
+
"PositionerOptions": "export type { Positioner, PositionerOptions, PositionStrategy } from './float';",
|
|
49
|
+
"PositionStrategy": "export type { Positioner, PositionerOptions, PositionStrategy } from './float';",
|
|
50
|
+
"createPositioner": "export { createPositioner } from './float';",
|
|
51
|
+
"InlineOptions": "export type { InlineOptions } from './inline';",
|
|
52
|
+
"inline": "export { inline } from './inline';",
|
|
53
|
+
"ArrowOptions": "export type { ArrowOptions } from './middleware/arrow';",
|
|
54
|
+
"arrow": "export { arrow } from './middleware/arrow';",
|
|
55
|
+
"AutoPlacementOptions": "export type { AutoPlacementOptions } from './middleware/auto-placement';",
|
|
56
|
+
"autoPlacement": "export { autoPlacement } from './middleware/auto-placement';",
|
|
57
|
+
"FlipOptions": "export type { FlipOptions } from './middleware/flip';",
|
|
58
|
+
"flip": "export { flip } from './middleware/flip';",
|
|
59
|
+
"HideOptions": "export type { HideOptions } from './middleware/hide';",
|
|
60
|
+
"hide": "export { hide } from './middleware/hide';",
|
|
61
|
+
"OffsetConfig": "export type { OffsetConfig, OffsetValue } from './middleware/offset';",
|
|
62
|
+
"OffsetValue": "export type { OffsetConfig, OffsetValue } from './middleware/offset';",
|
|
63
|
+
"offset": "export { offset } from './middleware/offset';",
|
|
64
|
+
"LimitShiftOptions": "export type { LimitShiftOptions, ShiftLimiter, ShiftOptions } from './middleware/shift';",
|
|
65
|
+
"ShiftLimiter": "export type { LimitShiftOptions, ShiftLimiter, ShiftOptions } from './middleware/shift';",
|
|
66
|
+
"ShiftOptions": "export type { LimitShiftOptions, ShiftLimiter, ShiftOptions } from './middleware/shift';",
|
|
67
|
+
"limitShift": "export { limitShift, shift } from './middleware/shift';",
|
|
68
|
+
"shift": "export { limitShift, shift } from './middleware/shift';",
|
|
69
|
+
"SizeOptions": "export type { SizeOptions } from './middleware/size';",
|
|
70
|
+
"size": "export { size } from './middleware/size';",
|
|
71
|
+
"detectOverflow": "export { detectOverflow, getClippingAncestorRect } from './overflow';",
|
|
72
|
+
"getClippingAncestorRect": "export { detectOverflow, getClippingAncestorRect } from './overflow';",
|
|
73
|
+
"PositioningPreset": "export type { PositioningPreset, PresetOptions } from './presets';",
|
|
74
|
+
"PresetOptions": "export type { PositioningPreset, PresetOptions } from './presets';",
|
|
75
|
+
"Alignment": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
76
|
+
"ArrowData": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
77
|
+
"ComputePositionOptions": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
78
|
+
"ComputePositionResult": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
79
|
+
"DetectOverflowOptions": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
80
|
+
"FlipData": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
81
|
+
"HideData": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
82
|
+
"Middleware": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
83
|
+
"MiddlewareData": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
84
|
+
"MiddlewareReset": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
85
|
+
"MiddlewareResult": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
86
|
+
"MiddlewareState": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
87
|
+
"Padding": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
88
|
+
"Placement": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
89
|
+
"Rect": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
90
|
+
"ReferenceElement": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
91
|
+
"ShiftData": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
92
|
+
"Side": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
93
|
+
"SideObject": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
94
|
+
"SizeData": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
95
|
+
"VirtualReference": "export type {\n Alignment,\n ArrowData,\n ComputePositionOptions,\n ComputePositionResult,\n DetectOverflowOptions,\n FlipData,\n HideData,\n Middleware,\n MiddlewareData,\n MiddlewareReset,\n MiddlewareResult,\n MiddlewareState,\n Padding,\n Placement,\n Rect,\n ReferenceElement,\n ShiftData,\n Side,\n SideObject,\n SizeData,\n VirtualReference,\n} from './types';",
|
|
96
|
+
"getAlignment": "export { getAlignment, getSide } from './utils';",
|
|
97
|
+
"getSide": "export { getAlignment, getSide } from './utils';"
|
|
98
|
+
}
|
|
99
|
+
}
|