@vielzeug/ore 3.0.1 → 26.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/base-element.cjs +1 -1
- package/dist/base-element.cjs.map +1 -1
- package/dist/base-element.d.ts +2 -2
- package/dist/base-element.d.ts.map +1 -1
- package/dist/base-element.js +1 -1
- package/dist/base-element.js.map +1 -1
- package/dist/component-types.d.ts +2 -2
- package/dist/context.cjs.map +1 -1
- package/dist/context.d.ts +2 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js.map +1 -1
- package/dist/define.cjs +1 -1
- package/dist/define.cjs.map +1 -1
- package/dist/define.d.ts +7 -4
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +1 -1
- package/dist/define.js.map +1 -1
- package/dist/directives/classMap.cjs.map +1 -1
- package/dist/directives/classMap.d.ts +2 -1
- package/dist/directives/classMap.d.ts.map +1 -1
- package/dist/directives/classMap.js.map +1 -1
- package/dist/directives/each.cjs.map +1 -1
- package/dist/directives/each.d.ts +5 -4
- package/dist/directives/each.d.ts.map +1 -1
- package/dist/directives/each.js.map +1 -1
- package/dist/directives/live.cjs.map +1 -1
- package/dist/directives/live.d.ts +1 -1
- package/dist/directives/live.js.map +1 -1
- package/dist/directives/when.cjs.map +1 -1
- package/dist/directives/when.d.ts +1 -2
- package/dist/directives/when.d.ts.map +1 -1
- package/dist/directives/when.js.map +1 -1
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +10 -10
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/forms/field.cjs.map +1 -1
- package/dist/forms/field.d.ts +4 -4
- package/dist/forms/field.js.map +1 -1
- package/dist/host-bind.cjs.map +1 -1
- package/dist/host-bind.d.ts +4 -4
- package/dist/host-bind.d.ts.map +1 -1
- package/dist/host-bind.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/ore.cjs +2 -2
- package/dist/ore.cjs.map +1 -1
- package/dist/ore.iife.js +2 -2
- package/dist/ore.iife.js.map +1 -1
- package/dist/ore.js +2 -2
- package/dist/ore.js.map +1 -1
- package/dist/props.cjs.map +1 -1
- package/dist/props.d.ts +3 -3
- package/dist/props.js.map +1 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.ts +5 -5
- package/dist/runtime.js.map +1 -1
- package/dist/slots.cjs.map +1 -1
- package/dist/slots.d.ts +3 -3
- package/dist/slots.js.map +1 -1
- package/dist/template/binding-types.d.ts +2 -2
- package/dist/template/bindings.cjs.map +1 -1
- package/dist/template/bindings.d.ts +2 -2
- package/dist/template/bindings.js.map +1 -1
- package/dist/template/compiler.cjs.map +1 -1
- package/dist/template/compiler.d.ts +1 -1
- package/dist/template/compiler.js.map +1 -1
- package/dist/template/instantiator.cjs.map +1 -1
- package/dist/template/instantiator.d.ts +1 -1
- package/dist/template/instantiator.js.map +1 -1
- package/dist/template/result.cjs.map +1 -1
- package/dist/template/result.d.ts +4 -4
- package/dist/template/result.js.map +1 -1
- package/dist/testing/dom.cjs.map +1 -1
- package/dist/testing/dom.d.ts +3 -3
- package/dist/testing/dom.js.map +1 -1
- package/dist/testing/flush.cjs +1 -1
- package/dist/testing/flush.cjs.map +1 -1
- package/dist/testing/flush.d.ts +4 -4
- package/dist/testing/flush.js +1 -1
- package/dist/testing/flush.js.map +1 -1
- package/dist/testing/form-internals-polyfill.cjs.map +1 -1
- package/dist/testing/form-internals-polyfill.d.ts +2 -2
- package/dist/testing/form-internals-polyfill.js.map +1 -1
- package/dist/testing/mount.cjs.map +1 -1
- package/dist/testing/mount.d.ts +3 -3
- package/dist/testing/mount.d.ts.map +1 -1
- package/dist/testing/mount.js.map +1 -1
- package/dist/testing/render-hook.cjs.map +1 -1
- package/dist/testing/render-hook.d.ts +1 -1
- package/dist/testing/render-hook.js.map +1 -1
- package/dist/testing/reset.cjs.map +1 -1
- package/dist/testing/reset.d.ts +2 -2
- package/dist/testing/reset.js.map +1 -1
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/utils/brand.cjs.map +1 -1
- package/dist/utils/brand.d.ts +2 -2
- package/dist/utils/brand.js.map +1 -1
- package/dist/utils/dom.cjs.map +1 -1
- package/dist/utils/dom.d.ts +11 -5
- package/dist/utils/dom.d.ts.map +1 -1
- package/dist/utils/dom.js.map +1 -1
- package/dist/utils/emit.cjs +1 -1
- package/dist/utils/emit.cjs.map +1 -1
- package/dist/utils/emit.d.ts +9 -6
- package/dist/utils/emit.d.ts.map +1 -1
- package/dist/utils/emit.js +1 -1
- package/dist/utils/emit.js.map +1 -1
- package/package.json +9 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"each.js","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import {\n batch,\n computed,\n createScope,\n type Readable,\n effect as rawEffect,\n type Scope,\n type Signal,\n signal,\n untrack,\n} from '@vielzeug/ripple';\n\nimport { invariant, ORE_ERRORS, OreApiError, OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult } from '../template/result';\nimport { removeNodes, runAll } from '../utils/dom';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype MaybeReactiveArray<T> = Readable<T[]> | (() => T[]) | T[];\ntype ItemKey = number | string;\n\ntype ItemEntry<T> = {\n cleanups: (() => void)[];\n data: Signal<T>;\n index: Signal<number>;\n nodes: Node[];\n scope: Scope;\n};\n\n// ─── Item lifecycle ───────────────────────────────────────────────────────────\n\nconst createItem = <T>(\n item: T,\n index: number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n insertBefore: Node,\n): ItemEntry<T> => {\n const dataSignal: Signal<T> = signal(item);\n const indexSignal: Signal<number> = signal(index);\n const scope = createScope();\n const cleanups: (() => void)[] = [];\n let nodes: Node[] = [];\n\n scope.run(() => {\n const result = render(dataSignal, indexSignal);\n\n nodes = result.mount(parent, insertBefore, (fn) => cleanups.push(fn));\n });\n\n return { cleanups, data: dataSignal, index: indexSignal, nodes, scope };\n};\n\nconst removeItem = <T>(entry: ItemEntry<T>): void => {\n entry.scope.dispose();\n runAll(entry.cleanups);\n removeNodes(entry.nodes);\n};\n\n// ─── Reconciler ───────────────────────────────────────────────────────────────\n\n/**\n * Reconciles the live item map (mutated in-place) against the next array.\n * Stale entries are removed and destroyed; new entries are created and inserted.\n * Existing entries are updated in-place via signal writes.\n * Returns the ordered list of entries matching nextList.\n */\nconst reconcileItems = <T>(\n itemsMap: Map<ItemKey, ItemEntry<T>>,\n next: T[],\n nextKeys: ItemKey[],\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n endMarker: Node,\n): ItemEntry<T>[] => {\n const nextKeySet = new Set(nextKeys);\n\n // Remove stale entries from the map\n for (const [key, entry] of itemsMap) {\n if (!nextKeySet.has(key)) {\n removeItem(entry);\n itemsMap.delete(key);\n }\n }\n\n // Build the next ordered list: update existing items, create new ones\n const nextOrdered: ItemEntry<T>[] = [];\n\n for (let i = 0; i < next.length; i++) {\n const key = nextKeys[i];\n const existing = itemsMap.get(key);\n\n if (existing) {\n batch(() => {\n existing.data.value = next[i];\n existing.index.value = i;\n });\n nextOrdered.push(existing);\n } else {\n const entry = untrack(() => createItem(next[i], i, render, parent, endMarker));\n\n itemsMap.set(key, entry);\n nextOrdered.push(entry);\n }\n }\n\n // DOM ordering: right-to-left pass — move any item not already adjacent to cursor.\n // An item is in place when its last node directly precedes the cursor; items may\n // span several nodes (whitespace text around an element), so compare the last one.\n // Moving an already-placed item would disconnect and reconnect its custom elements.\n // O(n) DOM operations in the worst case; optimal for the typical small list sizes\n // encountered in UI components (tabs, options, menu items).\n let cursor: Node = endMarker;\n\n for (let j = nextOrdered.length - 1; j >= 0; j--) {\n const entry = nextOrdered[j];\n const firstNode = entry.nodes[0];\n const lastNode = entry.nodes[entry.nodes.length - 1];\n\n if (firstNode && lastNode !== cursor.previousSibling) {\n for (const node of entry.nodes) parent.insertBefore(node, cursor);\n }\n\n cursor = firstNode ?? cursor;\n }\n\n return nextOrdered;\n};\n\nconst collectKeys = <T>(next: T[], keyFn: (item: T, index: number) => ItemKey): ItemKey[] => {\n const keys: ItemKey[] = [];\n const seen = new Set<ItemKey>();\n\n for (let i = 0; i < next.length; i++) {\n const key = keyFn(next[i], i);\n\n if (seen.has(key)) throw new OreApiError(ORE_ERRORS.eachDuplicateKey(String(key), i));\n\n seen.add(key);\n keys.push(key);\n }\n\n return keys;\n};\n\nconst reportEachError = (error: unknown, anchor: Comment): void => {\n const cause = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`each() failed to reconcile a list update: ${cause.message}`, {\n cause,\n component: 'each()',\n phase: 'each-reconcile',\n }),\n anchor,\n );\n};\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Renders a keyed list of items as a `DirectiveResult`.\n *\n * Each item is rendered by the provided render function.\n * Items are reused by key when the list changes; only stale items are destroyed.\n *\n * The render function receives a `Readable<T>` signal and a `Readable<number>` index\n * signal. Use `item.value` to read the current item inside reactive expressions:\n *\n * ```ts\n * html`${each(items, (item) => item.id, (item) => html`<li>${() => item.value.name}</li>`)}`\n * ```\n *\n * **Plain array:** when a plain `T[]` is passed (not a signal or getter), it is\n * treated as a one-time static render. Mutations to the original array are not\n * tracked. Pass a `Signal<T[]>` or `() => T[]` for reactive lists.\n *\n * **Optional fallback:** the fourth argument renders when the list is empty.\n *\n * **Key choice:** pass a stable item identifier (e.g. `item.id`), never the\n * array index — an index-based key reassigns to a different item whenever the\n * list is reordered or an item is inserted/removed before it, causing full\n * item teardown/recreation instead of the in-place update `each()` is built\n * for.\n *\n * **Duplicate keys:** a reconciliation failure (e.g. duplicate keys, see `eachDuplicateKey`)\n * does not throw past this function — an uncaught exception inside the reactive effect that\n * drives `each()` would risk corrupting unrelated effects scheduled in the same update batch.\n * Instead the last valid list is preserved and the failure is reported via the `ore:error` DOM event\n * (see `OreLifecycleError`, phase `'each-reconcile'`) plus a dev-only console log — listen for\n * `ore:error` on `document`/`window` to observe this in every build, including production.\n */\nexport function each<T>(\n list: MaybeReactiveArray<T>,\n keyFn: (item: T, index: number) => string | number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n fallback?: () => HTMLResult,\n): DirectiveResult {\n const listSignal = Array.isArray(list)\n ? signal(list as T[])\n : typeof list === 'function'\n ? computed(list as () => T[])\n : list;\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const parent = anchor.parentNode;\n\n invariant(parent, 'each() anchor comment has no parent node');\n\n const endMarker = document.createComment('each/end');\n\n parent.insertBefore(endMarker, anchor.nextSibling);\n\n let itemsMap = new Map<ItemKey, ItemEntry<T>>();\n let itemsOrdered: ItemEntry<T>[] = [];\n let fallbackNodes: Node[] | null = null;\n let fallbackCleanups: (() => void)[] = [];\n\n const mountFallback = (): void => {\n if (!fallback) return;\n\n const result = fallback();\n\n fallbackNodes = result.mount(parent, endMarker, (fn) => fallbackCleanups.push(fn));\n };\n\n const clearFallback = (): void => {\n if (fallbackNodes) {\n runAll(fallbackCleanups);\n removeNodes(fallbackNodes);\n fallbackNodes = null;\n fallbackCleanups = [];\n }\n };\n\n const sub = rawEffect(() => {\n const nextList = listSignal.value ?? [];\n\n if (nextList.length === 0) {\n for (const entry of untrack(() => itemsOrdered)) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n\n if (!fallbackNodes) untrack(mountFallback);\n\n return;\n }\n\n let nextKeys: ItemKey[];\n\n try {\n nextKeys = untrack(() => collectKeys(nextList, keyFn));\n } catch (error) {\n reportEachError(error, anchor);\n return;\n }\n\n clearFallback();\n\n try {\n itemsOrdered = untrack(() => reconcileItems(itemsMap, nextList, nextKeys, render, parent, endMarker));\n } catch (error) {\n // Rendering may already have changed the live map, so clear it to avoid retaining partial DOM.\n reportEachError(error, anchor);\n for (const entry of itemsMap.values()) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n }\n });\n\n registerCleanup(() => sub.dispose());\n registerCleanup(() => {\n clearFallback();\n for (const entry of itemsOrdered) removeItem(entry);\n endMarker.remove();\n });\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"each.js","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import {\n batch,\n computed,\n createScope,\n type Readable,\n effect as rawEffect,\n type Scope,\n type Signal,\n signal,\n untrack,\n} from '@vielzeug/ripple';\n\nimport { invariant, ORE_ERRORS, OreApiError, OreLifecycleError, reportRuntimeError } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult } from '../template/result';\nimport { type MaybeReactive, removeNodes, runAll } from '../utils/dom';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype MaybeReactiveArray<T> = MaybeReactive<T[]>;\ntype ItemKey = number | string;\n\ntype ItemEntry<T> = {\n cleanups: (() => void)[];\n data: Signal<T>;\n index: Signal<number>;\n nodes: Node[];\n scope: Scope;\n};\n\n// ─── Item lifecycle ───────────────────────────────────────────────────────────\n\nconst createItem = <T>(\n item: T,\n index: number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n insertBefore: Node,\n): ItemEntry<T> => {\n const dataSignal: Signal<T> = signal(item);\n const indexSignal: Signal<number> = signal(index);\n const scope = createScope();\n const cleanups: (() => void)[] = [];\n let nodes: Node[] = [];\n\n scope.run(() => {\n const result = render(dataSignal, indexSignal);\n\n nodes = result.mount(parent, insertBefore, (fn) => cleanups.push(fn));\n });\n\n return { cleanups, data: dataSignal, index: indexSignal, nodes, scope };\n};\n\nconst removeItem = <T>(entry: ItemEntry<T>): void => {\n entry.scope.dispose();\n runAll(entry.cleanups);\n removeNodes(entry.nodes);\n};\n\n// ─── Reconciler ───────────────────────────────────────────────────────────────\n\n/**\n * Reconciles the live item map (mutated in-place) against the next array.\n * Stale entries are removed and destroyed; new entries are created and inserted.\n * Existing entries are updated in-place via signal writes.\n * Returns the ordered list of entries matching nextList.\n */\nconst reconcileItems = <T>(\n itemsMap: Map<ItemKey, ItemEntry<T>>,\n next: T[],\n nextKeys: ItemKey[],\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n endMarker: Node,\n): ItemEntry<T>[] => {\n const nextKeySet = new Set(nextKeys);\n\n // Remove stale entries from the map\n for (const [key, entry] of itemsMap) {\n if (!nextKeySet.has(key)) {\n removeItem(entry);\n itemsMap.delete(key);\n }\n }\n\n // Build the next ordered list: update existing items, create new ones\n const nextOrdered: ItemEntry<T>[] = [];\n\n for (let i = 0; i < next.length; i++) {\n const key = nextKeys[i];\n const existing = itemsMap.get(key);\n\n if (existing) {\n batch(() => {\n existing.data.value = next[i];\n existing.index.value = i;\n });\n nextOrdered.push(existing);\n } else {\n const entry = untrack(() => createItem(next[i], i, render, parent, endMarker));\n\n itemsMap.set(key, entry);\n nextOrdered.push(entry);\n }\n }\n\n // DOM ordering: right-to-left pass: move any item not already adjacent to cursor.\n // An item is in place when its last node directly precedes the cursor; items may\n // span several nodes (whitespace text around an element), so compare the last one.\n // Moving an already-placed item would disconnect and reconnect its custom elements.\n // O(n) DOM operations in the worst case; optimal for the typical small list sizes\n // encountered in UI components (tabs, options, menu items).\n let cursor: Node = endMarker;\n\n for (let j = nextOrdered.length - 1; j >= 0; j--) {\n const entry = nextOrdered[j];\n const firstNode = entry.nodes[0];\n const lastNode = entry.nodes[entry.nodes.length - 1];\n\n if (firstNode && lastNode !== cursor.previousSibling) {\n for (const node of entry.nodes) parent.insertBefore(node, cursor);\n }\n\n cursor = firstNode ?? cursor;\n }\n\n return nextOrdered;\n};\n\nconst collectKeys = <T>(next: T[], keyFn: (item: T, index: number) => ItemKey): ItemKey[] => {\n const keys: ItemKey[] = [];\n const seen = new Set<ItemKey>();\n\n for (let i = 0; i < next.length; i++) {\n const key = keyFn(next[i], i);\n\n if (seen.has(key)) throw new OreApiError(ORE_ERRORS.eachDuplicateKey(String(key), i));\n\n seen.add(key);\n keys.push(key);\n }\n\n return keys;\n};\n\nconst reportEachError = (error: unknown, anchor: Comment): void => {\n const cause = error instanceof Error ? error : new Error(String(error));\n\n reportRuntimeError(\n new OreLifecycleError(`each() failed to reconcile a list update: ${cause.message}`, {\n cause,\n component: 'each()',\n phase: 'each-reconcile',\n }),\n anchor,\n );\n};\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Renders a keyed list of items as a `DirectiveResult`.\n *\n * Each item is rendered by the provided render function.\n * Items are reused by key when the list changes; only stale items are destroyed.\n *\n * The render function receives a `Readable<T>` signal and a `Readable<number>` index\n * signal. Use `item.value` to read the current item inside reactive expressions:\n *\n * ```ts\n * html`${each(items, (item) => item.id, (item) => html`<li>${() => item.value.name}</li>`)}`\n * ```\n *\n * **Plain array:** when a plain `T[]` is passed (not a signal or getter), it is\n * treated as a one-time static render. Mutations to the original array are not\n * tracked. Pass a `Signal<T[]>` or `() => T[]` for reactive lists.\n *\n * **Optional fallback:** the fourth argument renders when the list is empty.\n *\n * **Key choice:** pass a stable item identifier (e.g. `item.id`), never the\n * array index: an index-based key reassigns to a different item whenever the\n * list is reordered or an item is inserted/removed before it, causing full\n * item teardown/recreation instead of the in-place update `each()` is built\n * for.\n *\n * **Duplicate keys:** a reconciliation failure (e.g. duplicate keys, see `eachDuplicateKey`)\n * does not throw past this function: an uncaught exception inside the reactive effect that\n * drives `each()` would risk corrupting unrelated effects scheduled in the same update batch.\n * Instead the last valid list is preserved and the failure is reported via the `ore:error` DOM event\n * (see `OreLifecycleError`, phase `'each-reconcile'`) plus a dev-only console log: listen for\n * `ore:error` on `document`/`window` to observe this in every build, including production.\n */\nexport function each<T>(\n list: MaybeReactiveArray<T>,\n keyFn: (item: T, index: number) => string | number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n fallback?: () => HTMLResult,\n): DirectiveResult {\n const listSignal = Array.isArray(list)\n ? signal(list as T[])\n : typeof list === 'function'\n ? computed(list as () => T[])\n : list;\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const parent = anchor.parentNode;\n\n invariant(parent, 'each() anchor comment has no parent node');\n\n const endMarker = document.createComment('each/end');\n\n parent.insertBefore(endMarker, anchor.nextSibling);\n\n let itemsMap = new Map<ItemKey, ItemEntry<T>>();\n let itemsOrdered: ItemEntry<T>[] = [];\n let fallbackNodes: Node[] | null = null;\n let fallbackCleanups: (() => void)[] = [];\n\n const mountFallback = (): void => {\n if (!fallback) return;\n\n const result = fallback();\n\n fallbackNodes = result.mount(parent, endMarker, (fn) => fallbackCleanups.push(fn));\n };\n\n const clearFallback = (): void => {\n if (fallbackNodes) {\n runAll(fallbackCleanups);\n removeNodes(fallbackNodes);\n fallbackNodes = null;\n fallbackCleanups = [];\n }\n };\n\n const sub = rawEffect(() => {\n const nextList = listSignal.value ?? [];\n\n if (nextList.length === 0) {\n for (const entry of untrack(() => itemsOrdered)) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n\n if (!fallbackNodes) untrack(mountFallback);\n\n return;\n }\n\n let nextKeys: ItemKey[];\n\n try {\n nextKeys = untrack(() => collectKeys(nextList, keyFn));\n } catch (error) {\n reportEachError(error, anchor);\n return;\n }\n\n clearFallback();\n\n try {\n itemsOrdered = untrack(() => reconcileItems(itemsMap, nextList, nextKeys, render, parent, endMarker));\n } catch (error) {\n // Rendering may already have changed the live map, so clear it to avoid retaining partial DOM.\n reportEachError(error, anchor);\n for (const entry of itemsMap.values()) removeItem(entry);\n itemsMap = new Map();\n itemsOrdered = [];\n }\n });\n\n registerCleanup(() => sub.dispose());\n registerCleanup(() => {\n clearFallback();\n for (const entry of itemsOrdered) removeItem(entry);\n endMarker.remove();\n });\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live.cjs","names":[],"sources":["../../src/directives/live.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nimport { makeBrand } from '../utils/brand';\n\n/**\n * A per-binding marker produced by `live()`. Wraps the source signal
|
|
1
|
+
{"version":3,"file":"live.cjs","names":[],"sources":["../../src/directives/live.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nimport { makeBrand } from '../utils/brand';\n\n/**\n * A per-binding marker produced by `live()`. Wraps the source signal: the\n * \"live\" flag belongs to this one binding site, never to the signal itself,\n * so other bindings of the same signal are unaffected.\n *\n * @example\n * html`<input value=\"${live(model)}\" />`\n */\nexport type LiveBinding<T> = { readonly source: Readable<T> };\n\nconst liveBrand = makeBrand<LiveBinding<unknown>>('ore:live');\n\n/**\n * Marks one attribute binding as \"live\" so stale app-state writes never clobber\n * in-progress user input at that binding site.\n *\n * For form controls: if the current DOM value diverges from the last write made\n * by this binding, subsequent app-state writes are silently dropped until the\n * DOM value matches the incoming value or no prior write has been recorded.\n */\nexport const live = <T>(source: Readable<T>): LiveBinding<T> => liveBrand.stamp({ source }) as LiveBinding<T>;\n\nexport const isLiveBinding = liveBrand.is;\n"],"mappings":"AAcA,IAAM,8BAAY,CAAA,CAAA,UAAgC,UAAU,EAU/C,EAAW,GAAwC,EAAU,MAAM,CAAE,QAAO,CAAC,EAE7E,EAAgB,EAAU"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Readable } from '@vielzeug/ripple';
|
|
2
2
|
/**
|
|
3
|
-
* A per-binding marker produced by `live()`. Wraps the source signal
|
|
3
|
+
* A per-binding marker produced by `live()`. Wraps the source signal: the
|
|
4
4
|
* "live" flag belongs to this one binding site, never to the signal itself,
|
|
5
5
|
* so other bindings of the same signal are unaffected.
|
|
6
6
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live.js","names":[],"sources":["../../src/directives/live.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nimport { makeBrand } from '../utils/brand';\n\n/**\n * A per-binding marker produced by `live()`. Wraps the source signal
|
|
1
|
+
{"version":3,"file":"live.js","names":[],"sources":["../../src/directives/live.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\nimport { makeBrand } from '../utils/brand';\n\n/**\n * A per-binding marker produced by `live()`. Wraps the source signal: the\n * \"live\" flag belongs to this one binding site, never to the signal itself,\n * so other bindings of the same signal are unaffected.\n *\n * @example\n * html`<input value=\"${live(model)}\" />`\n */\nexport type LiveBinding<T> = { readonly source: Readable<T> };\n\nconst liveBrand = makeBrand<LiveBinding<unknown>>('ore:live');\n\n/**\n * Marks one attribute binding as \"live\" so stale app-state writes never clobber\n * in-progress user input at that binding site.\n *\n * For form controls: if the current DOM value diverges from the last write made\n * by this binding, subsequent app-state writes are silently dropped until the\n * DOM value matches the incoming value or no prior write has been recorded.\n */\nexport const live = <T>(source: Readable<T>): LiveBinding<T> => liveBrand.stamp({ source }) as LiveBinding<T>;\n\nexport const isLiveBinding = liveBrand.is;\n"],"mappings":"8CAcA,IAAM,EAAY,EAAgC,UAAU,EAU/C,EAAW,GAAwC,EAAU,MAAM,CAAE,QAAO,CAAC,EAE7E,EAAgB,EAAU"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"when.cjs","names":[],"sources":["../../src/directives/when.ts"],"sourcesContent":["import { computed, isReactive, type Readable, effect as rawEffect, untrack } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult, isHtmlResult } from '../template/result';\nimport { createReplaceableSlot, removeNodes } from '../utils/dom';\n\ntype
|
|
1
|
+
{"version":3,"file":"when.cjs","names":[],"sources":["../../src/directives/when.ts"],"sourcesContent":["import { computed, isReactive, type Readable, effect as rawEffect, untrack } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult, isHtmlResult } from '../template/result';\nimport { createReplaceableSlot, type MaybeReactive, removeNodes } from '../utils/dom';\n\ntype WhenRenderable = HTMLResult | null | undefined | false;\n\nconst NO_PARENT_MSG = 'when() anchor comment has no parent node';\n\n/**\n * Conditionally renders one of two branches as a `DirectiveResult`.\n *\n * The condition may be a boolean, signal, or getter function.\n * When the condition changes, the current branch is cleaned up and\n * the new branch is mounted in its place.\n *\n * @example\n * ```ts\n * html`${when(isLoggedIn, () => html`<p>Welcome</p>`, () => html`<p>Log in</p>`)}`\n * ```\n */\nexport function when(\n condition: MaybeReactive<boolean>,\n truthy: () => WhenRenderable,\n falsy?: () => WhenRenderable,\n): DirectiveResult {\n if (typeof condition !== 'function' && !isReactive(condition)) {\n return createDirectiveResult((anchor, registerCleanup) => {\n const branch = condition ? truthy() : falsy ? falsy() : null;\n\n if (!branch || !isHtmlResult(branch)) return;\n\n const parent = anchor.parentNode;\n\n invariant(parent, NO_PARENT_MSG);\n\n const nodes = branch.mount(parent, anchor, registerCleanup);\n\n registerCleanup(() => removeNodes(nodes));\n });\n }\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const ownedComputed = typeof condition === 'function' ? computed(condition as () => boolean) : null;\n const conditionSignal = ownedComputed ?? (condition as Readable<boolean>);\n\n const parent = anchor.parentNode;\n\n invariant(parent, NO_PARENT_MSG);\n\n const endMarker = document.createComment('when/end');\n\n parent.insertBefore(endMarker, anchor.nextSibling);\n\n const slot = createReplaceableSlot();\n\n const sub = rawEffect(() => {\n const next = conditionSignal.value;\n\n slot.clear();\n\n const branch = next ? truthy() : falsy ? falsy() : null;\n\n if (!branch || !isHtmlResult(branch)) return;\n\n slot.setNodes(untrack(() => branch.mount(parent, endMarker, slot.registerCleanup)));\n });\n\n registerCleanup(() => sub.dispose());\n registerCleanup(() => {\n slot.clear();\n endMarker.remove();\n });\n });\n}\n"],"mappings":"qIAQA,IAAM,EAAgB,2CActB,SAAgB,EACd,EACA,EACA,EACiB,CAiBjB,OAhBI,OAAO,GAAc,YAAc,EAAA,EAAC,EAAA,WAAA,CAAW,CAAS,EACnD,EAAA,uBAAuB,EAAQ,IAAoB,CACxD,IAAM,EAAS,EAAY,EAAO,EAAI,EAAQ,EAAM,EAAI,KAExD,GAAI,CAAC,GAAU,CAAC,EAAA,aAAa,CAAM,EAAG,OAEtC,IAAM,EAAS,EAAO,WAEtB,EAAA,UAAU,EAAQ,CAAa,EAE/B,IAAM,EAAQ,EAAO,MAAM,EAAQ,EAAQ,CAAe,EAE1D,MAAsB,EAAA,YAAY,CAAK,CAAC,CAC1C,CAAC,EAGI,EAAA,uBAAuB,EAAQ,IAAoB,CAExD,IAAM,GADgB,OAAO,GAAc,YAAA,EAAa,EAAA,SAAA,CAAS,CAA0B,EAAI,OACrD,EAEpC,EAAS,EAAO,WAEtB,EAAA,UAAU,EAAQ,CAAa,EAE/B,IAAM,EAAY,SAAS,cAAc,UAAU,EAEnD,EAAO,aAAa,EAAW,EAAO,WAAW,EAEjD,IAAM,EAAO,EAAA,sBAAsB,EAE7B,GAAA,EAAM,EAAA,OAAA,KAAgB,CAC1B,IAAM,EAAO,EAAgB,MAE7B,EAAK,MAAM,EAEX,IAAM,EAAS,EAAO,EAAO,EAAI,EAAQ,EAAM,EAAI,KAE9C,GAAW,EAAA,aAAa,CAAM,GAEnC,EAAK,UAAA,EAAS,EAAA,QAAA,KAAc,EAAO,MAAM,EAAQ,EAAW,EAAK,eAAe,CAAC,CAAC,CACpF,CAAC,EAED,MAAsB,EAAI,QAAQ,CAAC,EACnC,MAAsB,CACpB,EAAK,MAAM,EACX,EAAU,OAAO,CACnB,CAAC,CACH,CAAC,CACH"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { type Readable } from '@vielzeug/ripple';
|
|
2
1
|
import { type DirectiveResult, type HTMLResult } from '../template/result';
|
|
3
|
-
type MaybeReactive
|
|
2
|
+
import { type MaybeReactive } from '../utils/dom';
|
|
4
3
|
type WhenRenderable = HTMLResult | null | undefined | false;
|
|
5
4
|
/**
|
|
6
5
|
* Conditionally renders one of two branches as a `DirectiveResult`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"when.d.ts","sourceRoot":"","sources":["../../src/directives/when.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"when.d.ts","sourceRoot":"","sources":["../../src/directives/when.ts"],"names":[],"mappings":"AAGA,OAAO,EAAyB,KAAK,eAAe,EAAE,KAAK,UAAU,EAAgB,MAAM,oBAAoB,CAAC;AAChH,OAAO,EAAyB,KAAK,aAAa,EAAe,MAAM,cAAc,CAAC;AAEtF,KAAK,cAAc,GAAG,UAAU,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,CAAC;AAI5D;;;;;;;;;;;GAWG;AACH,wBAAgB,IAAI,CAClB,SAAS,EAAE,aAAa,CAAC,OAAO,CAAC,EACjC,MAAM,EAAE,MAAM,cAAc,EAC5B,KAAK,CAAC,EAAE,MAAM,cAAc,GAC3B,eAAe,CAiDjB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"when.js","names":[],"sources":["../../src/directives/when.ts"],"sourcesContent":["import { computed, isReactive, type Readable, effect as rawEffect, untrack } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult, isHtmlResult } from '../template/result';\nimport { createReplaceableSlot, removeNodes } from '../utils/dom';\n\ntype
|
|
1
|
+
{"version":3,"file":"when.js","names":[],"sources":["../../src/directives/when.ts"],"sourcesContent":["import { computed, isReactive, type Readable, effect as rawEffect, untrack } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult, isHtmlResult } from '../template/result';\nimport { createReplaceableSlot, type MaybeReactive, removeNodes } from '../utils/dom';\n\ntype WhenRenderable = HTMLResult | null | undefined | false;\n\nconst NO_PARENT_MSG = 'when() anchor comment has no parent node';\n\n/**\n * Conditionally renders one of two branches as a `DirectiveResult`.\n *\n * The condition may be a boolean, signal, or getter function.\n * When the condition changes, the current branch is cleaned up and\n * the new branch is mounted in its place.\n *\n * @example\n * ```ts\n * html`${when(isLoggedIn, () => html`<p>Welcome</p>`, () => html`<p>Log in</p>`)}`\n * ```\n */\nexport function when(\n condition: MaybeReactive<boolean>,\n truthy: () => WhenRenderable,\n falsy?: () => WhenRenderable,\n): DirectiveResult {\n if (typeof condition !== 'function' && !isReactive(condition)) {\n return createDirectiveResult((anchor, registerCleanup) => {\n const branch = condition ? truthy() : falsy ? falsy() : null;\n\n if (!branch || !isHtmlResult(branch)) return;\n\n const parent = anchor.parentNode;\n\n invariant(parent, NO_PARENT_MSG);\n\n const nodes = branch.mount(parent, anchor, registerCleanup);\n\n registerCleanup(() => removeNodes(nodes));\n });\n }\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const ownedComputed = typeof condition === 'function' ? computed(condition as () => boolean) : null;\n const conditionSignal = ownedComputed ?? (condition as Readable<boolean>);\n\n const parent = anchor.parentNode;\n\n invariant(parent, NO_PARENT_MSG);\n\n const endMarker = document.createComment('when/end');\n\n parent.insertBefore(endMarker, anchor.nextSibling);\n\n const slot = createReplaceableSlot();\n\n const sub = rawEffect(() => {\n const next = conditionSignal.value;\n\n slot.clear();\n\n const branch = next ? truthy() : falsy ? falsy() : null;\n\n if (!branch || !isHtmlResult(branch)) return;\n\n slot.setNodes(untrack(() => branch.mount(parent, endMarker, slot.registerCleanup)));\n });\n\n registerCleanup(() => sub.dispose());\n registerCleanup(() => {\n slot.clear();\n endMarker.remove();\n });\n });\n}\n"],"mappings":"uRAQA,IAAM,EAAgB,2CActB,SAAgB,EACd,EACA,EACA,EACiB,CAiBjB,OAhBI,OAAO,GAAc,YAAc,CAAC,EAAW,CAAS,EACnD,GAAuB,EAAQ,IAAoB,CACxD,IAAM,EAAS,EAAY,EAAO,EAAI,EAAQ,EAAM,EAAI,KAExD,GAAI,CAAC,GAAU,CAAC,EAAa,CAAM,EAAG,OAEtC,IAAM,EAAS,EAAO,WAEtB,EAAU,EAAQ,CAAa,EAE/B,IAAM,EAAQ,EAAO,MAAM,EAAQ,EAAQ,CAAe,EAE1D,MAAsB,EAAY,CAAK,CAAC,CAC1C,CAAC,EAGI,GAAuB,EAAQ,IAAoB,CAExD,IAAM,GADgB,OAAO,GAAc,WAAa,EAAS,CAA0B,EAAI,OACrD,EAEpC,EAAS,EAAO,WAEtB,EAAU,EAAQ,CAAa,EAE/B,IAAM,EAAY,SAAS,cAAc,UAAU,EAEnD,EAAO,aAAa,EAAW,EAAO,WAAW,EAEjD,IAAM,EAAO,EAAsB,EAE7B,EAAM,MAAgB,CAC1B,IAAM,EAAO,EAAgB,MAE7B,EAAK,MAAM,EAEX,IAAM,EAAS,EAAO,EAAO,EAAI,EAAQ,EAAM,EAAI,KAE9C,GAAW,EAAa,CAAM,GAEnC,EAAK,SAAS,MAAc,EAAO,MAAM,EAAQ,EAAW,EAAK,eAAe,CAAC,CAAC,CACpF,CAAC,EAED,MAAsB,EAAI,QAAQ,CAAC,EACnC,MAAsB,CACpB,EAAK,MAAM,EACX,EAAU,OAAO,CACnB,CAAC,CACH,CAAC,CACH"}
|
package/dist/errors.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
require("./_dev.cjs");var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{},n=class extends e{},r=class extends e{component;phase;constructor(e,t){super(e,{cause:t.cause}),this.component=t.component,this.phase=t.phase}};function i(e,t){`${e.component}${e.phase}`,e.cause,t.dispatchEvent(new CustomEvent(`ore:error`,{bubbles:!0,composed:!0,detail:e}))}var a={asyncSetupUnsupported:`setup() must return an HTMLResult or null; use reactive state for asynchronous work`,defineDuplicate:e=>`define('${e}') called twice
|
|
1
|
+
require("./_dev.cjs");var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{},n=class extends e{},r=class extends e{component;phase;constructor(e,t){super(e,{cause:t.cause}),this.component=t.component,this.phase=t.phase}};function i(e,t){`${e.component}${e.phase}`,e.cause,t.dispatchEvent(new CustomEvent(`ore:error`,{bubbles:!0,composed:!0,detail:e}))}var a={asyncSetupUnsupported:`setup() must return an HTMLResult or null; use reactive state for asynchronous work`,defineDuplicate:e=>`define('${e}') called twice: custom element already registered`,defineFieldRequiresFormAssociated:e=>`useField() requires define('${e}', { formAssociated: true })`,defineRequiresTag:`define() requires a tag name`,eachDuplicateKey:(e,t)=>`each() received duplicate key "${e}" at index ${t}`,eventModifiersUnsupported:e=>`@${e}: event modifiers are unsupported; call native event methods in the handler instead`,injectStrictFailed:(e,t)=>`injectStrict() could not resolve key "${e}" in <${t}>`,invariantViolated:e=>`invariant violated: ${e}`,lifecycleOutsideSetup:`Lifecycle hooks must be called during component setup`,listenNullTarget:e=>`listen() called with a null/undefined target for event "${e}": listener not attached`,propInvalidReflect:`Structured props cannot use reflect:true: use prop.json() with reflect:false`,templateInterpolationInTag:"html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs",useFieldAlreadyCalled:e=>`useField() was already called on <${e}>. Call it only once per component.`,validationFailed:(e,t)=>`Validation failed for <${e}>:\n${t.join(`
|
|
2
2
|
`)}`};function o(e,t){if(!e)throw new n(a.invariantViolated(t))}exports.ORE_ERRORS=a,exports.OreApiError=t,exports.OreError=e,exports.OreInternalError=n,exports.OreLifecycleError=r,exports.invariant=o,exports.reportRuntimeError=i;
|
|
3
3
|
//# sourceMappingURL=errors.cjs.map
|
package/dist/errors.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import { error as logError } from './_dev';\n\n// ─── Error policy ─────────────────────────────────────────────────────────────\n// One rule for the whole package
|
|
1
|
+
{"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import { error as logError } from './_dev';\n\n// ─── Error policy ─────────────────────────────────────────────────────────────\n// One rule for the whole package: decide by whose code failed and whether it\n// can continue, never ad hoc per call site:\n//\n// API misuse (wrong arguments, hook outside setup, duplicate define)\n// → throw `OreApiError`, immediately, every build.\n// User-authored code failing inside ore's execution (setup, onMounted,\n// onFormReset, each() reconciliation)\n// → wrap in `OreLifecycleError` and report via `reportRuntimeError()`\n// (`ore:error` DOM event + dev console) so other callbacks keep running.\n// Recoverable oddity (overwrite warnings, blocked attribute writes)\n// → dev `warn()`/`error()` and continue. Never swallow silently.\n// Internal impossibility (compiled template metadata out of sync)\n// → `invariant()` throws `OreInternalError`, every build.\n\n// ─── Structured error types ───────────────────────────────────────────────────\n\n/** Base class for all Ore errors. Use `instanceof OreError` to catch any Ore-originated error. */\nexport class OreError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when Ore API is called incorrectly (e.g. outside setup, duplicate define, invalid prop). */\nexport class OreApiError extends OreError {}\n\n/**\n * Thrown when an internal invariant fails: e.g. compiled template metadata no\n * longer matching the DOM it was cloned from. Distinct from `OreApiError`: this\n * is never the caller's fault, it signals a bug in ore itself. See `invariant()`.\n */\nexport class OreInternalError extends OreError {}\n\n/**\n * The phase in which a component error occurred.\n * - `'setup'`: synchronous setup() threw\n * - `'mounted'`: an onMounted callback threw\n * - `'form-reset'`: an onFormReset callback threw\n * - `'each-reconcile'`: `each()` failed to reconcile a list update (e.g. duplicate keys)\n */\nexport type OreErrorPhase = 'each-reconcile' | 'form-reset' | 'mounted' | 'setup';\n\n/**\n * Structured error thrown by the Ore runtime when component setup fails.\n * Provides component name and original cause for debugging.\n */\nexport class OreLifecycleError extends OreError {\n readonly component: string;\n readonly phase: OreErrorPhase;\n\n constructor(message: string, options: { cause: Error; component: string; phase: OreErrorPhase }) {\n super(message, { cause: options.cause });\n this.component = options.component;\n this.phase = options.phase;\n }\n}\n\n/**\n * Report a runtime error via the ore:error event and console.\n *\n * `target` only needs to be an `EventTarget` (not specifically an `HTMLElement`): component\n * lifecycle errors dispatch on the host element, but non-lifecycle failures (e.g. `each()`\n * reconciliation, which has no single \"component\" to attribute the error to) dispatch on\n * whatever live DOM node is available, such as the directive's own anchor `Comment`. Either way\n * the event still bubbles and crosses shadow boundaries (`composed: true`), so a listener on\n * `document`/`window` observes every report regardless of where it originated.\n *\n * The console log (via `_dev.ts`'s `error()`) is still dev-gated like the rest of the package's\n * console diagnostics, but the `ore:error` DOM event dispatch below is **not**: it fires in\n * every build, so consumers always have a way to observe runtime failures programmatically even\n * when console output is stripped in production.\n */\nexport function reportRuntimeError(error: OreLifecycleError, target: EventTarget): void {\n logError(`<${error.component}> lifecycle error (phase: ${error.phase}):`, error.cause);\n\n target.dispatchEvent(\n new CustomEvent('ore:error', {\n bubbles: true,\n composed: true,\n detail: error,\n }),\n );\n}\n\n// ─── Error message constants ─────────────────────────────────────────────────\n\nexport const ORE_ERRORS = {\n asyncSetupUnsupported: 'setup() must return an HTMLResult or null; use reactive state for asynchronous work',\n defineDuplicate: (tag: string): string => `define('${tag}') called twice: custom element already registered`,\n defineFieldRequiresFormAssociated: (tag: string): string =>\n `useField() requires define('${tag}', { formAssociated: true })`,\n defineRequiresTag: 'define() requires a tag name',\n eachDuplicateKey: (key: string, index: number): string => `each() received duplicate key \"${key}\" at index ${index}`,\n eventModifiersUnsupported: (eventName: string): string =>\n `@${eventName}: event modifiers are unsupported; call native event methods in the handler instead`,\n injectStrictFailed: (key: string, tag: string): string => `injectStrict() could not resolve key \"${key}\" in <${tag}>`,\n invariantViolated: (message: string): string => `invariant violated: ${message}`,\n lifecycleOutsideSetup: 'Lifecycle hooks must be called during component setup',\n listenNullTarget: (eventName: string): string =>\n `listen() called with a null/undefined target for event \"${eventName}\": listener not attached`,\n propInvalidReflect: 'Structured props cannot use reflect:true: use prop.json() with reflect:false',\n templateInterpolationInTag:\n 'html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs',\n useFieldAlreadyCalled: (tag: string): string =>\n `useField() was already called on <${tag}>. Call it only once per component.`,\n validationFailed: (tag: string, errors: string[]): string => `Validation failed for <${tag}>:\\n${errors.join('\\n')}`,\n} as const;\n\n/**\n * Assert an internal invariant that must always hold: e.g. compiled template\n * metadata staying in sync with the DOM it was cloned from. A failed invariant\n * means a bug in ore itself, never user input, so it throws `OreInternalError`\n * unconditionally (every build, never gated like `_dev.ts`'s `warn()`).\n *\n * Narrowing caveat: `asserts condition` only narrows the exact expression\n * passed in. Assign to a local `const` first: `invariant(el.parentNode, msg)`\n * does not narrow later reads of `el.parentNode`.\n */\nexport function invariant(condition: unknown, message: string): asserts condition {\n if (!condition) throw new OreInternalError(ORE_ERRORS.invariantViolated(message));\n}\n"],"mappings":"sBAoBA,IAAa,EAAb,cAA8B,KAAM,CAClC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,EAAb,cAAiC,CAAS,CAAC,EAO9B,EAAb,cAAsC,CAAS,CAAC,EAenC,EAAb,cAAuC,CAAS,CAC9C,UACA,MAEA,YAAY,EAAiB,EAAoE,CAC/F,MAAM,EAAS,CAAE,MAAO,EAAQ,KAAM,CAAC,EACvC,KAAK,UAAY,EAAQ,UACzB,KAAK,MAAQ,EAAQ,KACvB,CACF,EAiBA,SAAgB,EAAmB,EAA0B,EAA2B,CAC7E,GAAI,EAAM,UAAV,EAAgD,EAAM,MAAtD,EAAiE,EAAM,MAEhF,EAAO,cACL,IAAI,YAAY,YAAa,CAC3B,QAAS,GACT,SAAU,GACV,OAAQ,CACV,CAAC,CACH,CACF,CAIA,IAAa,EAAa,CACxB,sBAAuB,sFACvB,gBAAkB,GAAwB,WAAW,EAAI,oDACzD,kCAAoC,GAClC,+BAA+B,EAAI,8BACrC,kBAAmB,+BACnB,kBAAmB,EAAa,IAA0B,kCAAkC,EAAI,aAAa,IAC7G,0BAA4B,GAC1B,IAAI,EAAU,qFAChB,oBAAqB,EAAa,IAAwB,yCAAyC,EAAI,QAAQ,EAAI,GACnH,kBAAoB,GAA4B,uBAAuB,IACvE,sBAAuB,wDACvB,iBAAmB,GACjB,2DAA2D,EAAU,0BACvE,mBAAoB,+EACpB,2BACE,uGACF,sBAAwB,GACtB,qCAAqC,EAAI,qCAC3C,kBAAmB,EAAa,IAA6B,0BAA0B,EAAI,MAAM,EAAO,KAAK;CAAI,GACnH,EAYA,SAAgB,EAAU,EAAoB,EAAoC,CAChF,GAAI,CAAC,EAAW,MAAM,IAAI,EAAiB,EAAW,kBAAkB,CAAO,CAAC,CAClF"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export declare class OreError extends Error {
|
|
|
6
6
|
export declare class OreApiError extends OreError {
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
|
-
* Thrown when an internal invariant fails
|
|
9
|
+
* Thrown when an internal invariant fails: e.g. compiled template metadata no
|
|
10
10
|
* longer matching the DOM it was cloned from. Distinct from `OreApiError`: this
|
|
11
11
|
* is never the caller's fault, it signals a bug in ore itself. See `invariant()`.
|
|
12
12
|
*/
|
|
@@ -14,10 +14,10 @@ export declare class OreInternalError extends OreError {
|
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* The phase in which a component error occurred.
|
|
17
|
-
* - `'setup'
|
|
18
|
-
* - `'mounted'
|
|
19
|
-
* - `'form-reset'
|
|
20
|
-
* - `'each-reconcile'
|
|
17
|
+
* - `'setup'`: synchronous setup() threw
|
|
18
|
+
* - `'mounted'`: an onMounted callback threw
|
|
19
|
+
* - `'form-reset'`: an onFormReset callback threw
|
|
20
|
+
* - `'each-reconcile'`: `each()` failed to reconcile a list update (e.g. duplicate keys)
|
|
21
21
|
*/
|
|
22
22
|
export type OreErrorPhase = 'each-reconcile' | 'form-reset' | 'mounted' | 'setup';
|
|
23
23
|
/**
|
|
@@ -36,7 +36,7 @@ export declare class OreLifecycleError extends OreError {
|
|
|
36
36
|
/**
|
|
37
37
|
* Report a runtime error via the ore:error event and console.
|
|
38
38
|
*
|
|
39
|
-
* `target` only needs to be an `EventTarget` (not specifically an `HTMLElement`)
|
|
39
|
+
* `target` only needs to be an `EventTarget` (not specifically an `HTMLElement`): component
|
|
40
40
|
* lifecycle errors dispatch on the host element, but non-lifecycle failures (e.g. `each()`
|
|
41
41
|
* reconciliation, which has no single "component" to attribute the error to) dispatch on
|
|
42
42
|
* whatever live DOM node is available, such as the directive's own anchor `Comment`. Either way
|
|
@@ -44,7 +44,7 @@ export declare class OreLifecycleError extends OreError {
|
|
|
44
44
|
* `document`/`window` observes every report regardless of where it originated.
|
|
45
45
|
*
|
|
46
46
|
* The console log (via `_dev.ts`'s `error()`) is still dev-gated like the rest of the package's
|
|
47
|
-
* console diagnostics, but the `ore:error` DOM event dispatch below is **not
|
|
47
|
+
* console diagnostics, but the `ore:error` DOM event dispatch below is **not**: it fires in
|
|
48
48
|
* every build, so consumers always have a way to observe runtime failures programmatically even
|
|
49
49
|
* when console output is stripped in production.
|
|
50
50
|
*/
|
|
@@ -60,19 +60,19 @@ export declare const ORE_ERRORS: {
|
|
|
60
60
|
readonly invariantViolated: (message: string) => string;
|
|
61
61
|
readonly lifecycleOutsideSetup: "Lifecycle hooks must be called during component setup";
|
|
62
62
|
readonly listenNullTarget: (eventName: string) => string;
|
|
63
|
-
readonly propInvalidReflect: "Structured props cannot use reflect:true
|
|
63
|
+
readonly propInvalidReflect: "Structured props cannot use reflect:true: use prop.json() with reflect:false";
|
|
64
64
|
readonly templateInterpolationInTag: "html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs";
|
|
65
65
|
readonly useFieldAlreadyCalled: (tag: string) => string;
|
|
66
66
|
readonly validationFailed: (tag: string, errors: string[]) => string;
|
|
67
67
|
};
|
|
68
68
|
/**
|
|
69
|
-
* Assert an internal invariant that must always hold
|
|
69
|
+
* Assert an internal invariant that must always hold: e.g. compiled template
|
|
70
70
|
* metadata staying in sync with the DOM it was cloned from. A failed invariant
|
|
71
71
|
* means a bug in ore itself, never user input, so it throws `OreInternalError`
|
|
72
72
|
* unconditionally (every build, never gated like `_dev.ts`'s `warn()`).
|
|
73
73
|
*
|
|
74
74
|
* Narrowing caveat: `asserts condition` only narrows the exact expression
|
|
75
|
-
* passed in. Assign to a local `const` first
|
|
75
|
+
* passed in. Assign to a local `const` first: `invariant(el.parentNode, msg)`
|
|
76
76
|
* does not narrow later reads of `el.parentNode`.
|
|
77
77
|
*/
|
|
78
78
|
export declare function invariant(condition: unknown, message: string): asserts condition;
|
package/dist/errors.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import"./_dev.js";var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{},n=class extends e{},r=class extends e{component;phase;constructor(e,t){super(e,{cause:t.cause}),this.component=t.component,this.phase=t.phase}};function i(e,t){`${e.component}${e.phase}`,e.cause,t.dispatchEvent(new CustomEvent(`ore:error`,{bubbles:!0,composed:!0,detail:e}))}var a={asyncSetupUnsupported:`setup() must return an HTMLResult or null; use reactive state for asynchronous work`,defineDuplicate:e=>`define('${e}') called twice
|
|
1
|
+
import"./_dev.js";var e=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{},n=class extends e{},r=class extends e{component;phase;constructor(e,t){super(e,{cause:t.cause}),this.component=t.component,this.phase=t.phase}};function i(e,t){`${e.component}${e.phase}`,e.cause,t.dispatchEvent(new CustomEvent(`ore:error`,{bubbles:!0,composed:!0,detail:e}))}var a={asyncSetupUnsupported:`setup() must return an HTMLResult or null; use reactive state for asynchronous work`,defineDuplicate:e=>`define('${e}') called twice: custom element already registered`,defineFieldRequiresFormAssociated:e=>`useField() requires define('${e}', { formAssociated: true })`,defineRequiresTag:`define() requires a tag name`,eachDuplicateKey:(e,t)=>`each() received duplicate key "${e}" at index ${t}`,eventModifiersUnsupported:e=>`@${e}: event modifiers are unsupported; call native event methods in the handler instead`,injectStrictFailed:(e,t)=>`injectStrict() could not resolve key "${e}" in <${t}>`,invariantViolated:e=>`invariant violated: ${e}`,lifecycleOutsideSetup:`Lifecycle hooks must be called during component setup`,listenNullTarget:e=>`listen() called with a null/undefined target for event "${e}": listener not attached`,propInvalidReflect:`Structured props cannot use reflect:true: use prop.json() with reflect:false`,templateInterpolationInTag:"html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs",useFieldAlreadyCalled:e=>`useField() was already called on <${e}>. Call it only once per component.`,validationFailed:(e,t)=>`Validation failed for <${e}>:\n${t.join(`
|
|
2
2
|
`)}`};function o(e,t){if(!e)throw new n(a.invariantViolated(t))}export{a as ORE_ERRORS,t as OreApiError,e as OreError,n as OreInternalError,r as OreLifecycleError,o as invariant,i as reportRuntimeError};
|
|
3
3
|
//# sourceMappingURL=errors.js.map
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import { error as logError } from './_dev';\n\n// ─── Error policy ─────────────────────────────────────────────────────────────\n// One rule for the whole package
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import { error as logError } from './_dev';\n\n// ─── Error policy ─────────────────────────────────────────────────────────────\n// One rule for the whole package: decide by whose code failed and whether it\n// can continue, never ad hoc per call site:\n//\n// API misuse (wrong arguments, hook outside setup, duplicate define)\n// → throw `OreApiError`, immediately, every build.\n// User-authored code failing inside ore's execution (setup, onMounted,\n// onFormReset, each() reconciliation)\n// → wrap in `OreLifecycleError` and report via `reportRuntimeError()`\n// (`ore:error` DOM event + dev console) so other callbacks keep running.\n// Recoverable oddity (overwrite warnings, blocked attribute writes)\n// → dev `warn()`/`error()` and continue. Never swallow silently.\n// Internal impossibility (compiled template metadata out of sync)\n// → `invariant()` throws `OreInternalError`, every build.\n\n// ─── Structured error types ───────────────────────────────────────────────────\n\n/** Base class for all Ore errors. Use `instanceof OreError` to catch any Ore-originated error. */\nexport class OreError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when Ore API is called incorrectly (e.g. outside setup, duplicate define, invalid prop). */\nexport class OreApiError extends OreError {}\n\n/**\n * Thrown when an internal invariant fails: e.g. compiled template metadata no\n * longer matching the DOM it was cloned from. Distinct from `OreApiError`: this\n * is never the caller's fault, it signals a bug in ore itself. See `invariant()`.\n */\nexport class OreInternalError extends OreError {}\n\n/**\n * The phase in which a component error occurred.\n * - `'setup'`: synchronous setup() threw\n * - `'mounted'`: an onMounted callback threw\n * - `'form-reset'`: an onFormReset callback threw\n * - `'each-reconcile'`: `each()` failed to reconcile a list update (e.g. duplicate keys)\n */\nexport type OreErrorPhase = 'each-reconcile' | 'form-reset' | 'mounted' | 'setup';\n\n/**\n * Structured error thrown by the Ore runtime when component setup fails.\n * Provides component name and original cause for debugging.\n */\nexport class OreLifecycleError extends OreError {\n readonly component: string;\n readonly phase: OreErrorPhase;\n\n constructor(message: string, options: { cause: Error; component: string; phase: OreErrorPhase }) {\n super(message, { cause: options.cause });\n this.component = options.component;\n this.phase = options.phase;\n }\n}\n\n/**\n * Report a runtime error via the ore:error event and console.\n *\n * `target` only needs to be an `EventTarget` (not specifically an `HTMLElement`): component\n * lifecycle errors dispatch on the host element, but non-lifecycle failures (e.g. `each()`\n * reconciliation, which has no single \"component\" to attribute the error to) dispatch on\n * whatever live DOM node is available, such as the directive's own anchor `Comment`. Either way\n * the event still bubbles and crosses shadow boundaries (`composed: true`), so a listener on\n * `document`/`window` observes every report regardless of where it originated.\n *\n * The console log (via `_dev.ts`'s `error()`) is still dev-gated like the rest of the package's\n * console diagnostics, but the `ore:error` DOM event dispatch below is **not**: it fires in\n * every build, so consumers always have a way to observe runtime failures programmatically even\n * when console output is stripped in production.\n */\nexport function reportRuntimeError(error: OreLifecycleError, target: EventTarget): void {\n logError(`<${error.component}> lifecycle error (phase: ${error.phase}):`, error.cause);\n\n target.dispatchEvent(\n new CustomEvent('ore:error', {\n bubbles: true,\n composed: true,\n detail: error,\n }),\n );\n}\n\n// ─── Error message constants ─────────────────────────────────────────────────\n\nexport const ORE_ERRORS = {\n asyncSetupUnsupported: 'setup() must return an HTMLResult or null; use reactive state for asynchronous work',\n defineDuplicate: (tag: string): string => `define('${tag}') called twice: custom element already registered`,\n defineFieldRequiresFormAssociated: (tag: string): string =>\n `useField() requires define('${tag}', { formAssociated: true })`,\n defineRequiresTag: 'define() requires a tag name',\n eachDuplicateKey: (key: string, index: number): string => `each() received duplicate key \"${key}\" at index ${index}`,\n eventModifiersUnsupported: (eventName: string): string =>\n `@${eventName}: event modifiers are unsupported; call native event methods in the handler instead`,\n injectStrictFailed: (key: string, tag: string): string => `injectStrict() could not resolve key \"${key}\" in <${tag}>`,\n invariantViolated: (message: string): string => `invariant violated: ${message}`,\n lifecycleOutsideSetup: 'Lifecycle hooks must be called during component setup',\n listenNullTarget: (eventName: string): string =>\n `listen() called with a null/undefined target for event \"${eventName}\": listener not attached`,\n propInvalidReflect: 'Structured props cannot use reflect:true: use prop.json() with reflect:false',\n templateInterpolationInTag:\n 'html`...`: interpolations inside a tag must be named attributes, boolean attributes, events, or refs',\n useFieldAlreadyCalled: (tag: string): string =>\n `useField() was already called on <${tag}>. Call it only once per component.`,\n validationFailed: (tag: string, errors: string[]): string => `Validation failed for <${tag}>:\\n${errors.join('\\n')}`,\n} as const;\n\n/**\n * Assert an internal invariant that must always hold: e.g. compiled template\n * metadata staying in sync with the DOM it was cloned from. A failed invariant\n * means a bug in ore itself, never user input, so it throws `OreInternalError`\n * unconditionally (every build, never gated like `_dev.ts`'s `warn()`).\n *\n * Narrowing caveat: `asserts condition` only narrows the exact expression\n * passed in. Assign to a local `const` first: `invariant(el.parentNode, msg)`\n * does not narrow later reads of `el.parentNode`.\n */\nexport function invariant(condition: unknown, message: string): asserts condition {\n if (!condition) throw new OreInternalError(ORE_ERRORS.invariantViolated(message));\n}\n"],"mappings":"kBAoBA,IAAa,EAAb,cAA8B,KAAM,CAClC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAGa,EAAb,cAAiC,CAAS,CAAC,EAO9B,EAAb,cAAsC,CAAS,CAAC,EAenC,EAAb,cAAuC,CAAS,CAC9C,UACA,MAEA,YAAY,EAAiB,EAAoE,CAC/F,MAAM,EAAS,CAAE,MAAO,EAAQ,KAAM,CAAC,EACvC,KAAK,UAAY,EAAQ,UACzB,KAAK,MAAQ,EAAQ,KACvB,CACF,EAiBA,SAAgB,EAAmB,EAA0B,EAA2B,CAC7E,GAAI,EAAM,UAAV,EAAgD,EAAM,MAAtD,EAAiE,EAAM,MAEhF,EAAO,cACL,IAAI,YAAY,YAAa,CAC3B,QAAS,GACT,SAAU,GACV,OAAQ,CACV,CAAC,CACH,CACF,CAIA,IAAa,EAAa,CACxB,sBAAuB,sFACvB,gBAAkB,GAAwB,WAAW,EAAI,oDACzD,kCAAoC,GAClC,+BAA+B,EAAI,8BACrC,kBAAmB,+BACnB,kBAAmB,EAAa,IAA0B,kCAAkC,EAAI,aAAa,IAC7G,0BAA4B,GAC1B,IAAI,EAAU,qFAChB,oBAAqB,EAAa,IAAwB,yCAAyC,EAAI,QAAQ,EAAI,GACnH,kBAAoB,GAA4B,uBAAuB,IACvE,sBAAuB,wDACvB,iBAAmB,GACjB,2DAA2D,EAAU,0BACvE,mBAAoB,+EACpB,2BACE,uGACF,sBAAwB,GACtB,qCAAqC,EAAI,qCAC3C,kBAAmB,EAAa,IAA6B,0BAA0B,EAAI,MAAM,EAAO,KAAK;CAAI,GACnH,EAYA,SAAgB,EAAU,EAAoB,EAAoC,CAChF,GAAI,CAAC,EAAW,MAAM,IAAI,EAAiB,EAAW,kBAAkB,CAAO,CAAC,CAClF"}
|
package/dist/forms/field.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field.cjs","names":[],"sources":["../../src/forms/field.ts"],"sourcesContent":["import type { Readable, Signal } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport { ORE_ERRORS, OreApiError } from '../errors';\nimport { getHost, onCleanup, onFormReset, watchEffect } from '../runtime';\n\n/** @internal */\nconst internalsRegistry = new WeakMap<HTMLElement, ElementInternals>();\n/** @internal */\nconst activeFieldRegistry = new WeakSet<HTMLElement>();\n\nexport type FormFieldOptions<T = unknown> = {\n disabled?: Readable<boolean>;\n /**\n * When `true`, a `null` or `undefined` value is submitted as an empty string\n * (`''`) instead of `null`. This keeps the field's key present in `FormData`\n * even when the value is absent
|
|
1
|
+
{"version":3,"file":"field.cjs","names":[],"sources":["../../src/forms/field.ts"],"sourcesContent":["import type { Readable, Signal } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport { ORE_ERRORS, OreApiError } from '../errors';\nimport { getHost, onCleanup, onFormReset, watchEffect } from '../runtime';\n\n/** @internal */\nconst internalsRegistry = new WeakMap<HTMLElement, ElementInternals>();\n/** @internal */\nconst activeFieldRegistry = new WeakSet<HTMLElement>();\n\nexport type FormFieldOptions<T = unknown> = {\n disabled?: Readable<boolean>;\n /**\n * When `true`, a `null` or `undefined` value is submitted as an empty string\n * (`''`) instead of `null`. This keeps the field's key present in `FormData`\n * even when the value is absent: useful when the server expects the field to\n * always be included.\n *\n * @default false\n */\n emptyStringForNull?: boolean;\n /**\n * Called when the ancestor `<form>` is reset (see `onFormReset`). Use to restore\n * whatever local state backs `value`: `useField` itself owns no field state to reset.\n */\n onReset?: () => void;\n toFormValue?: (value: T) => File | FormData | string | null;\n /**\n * Recomputed reactively and passed straight to `internals.setValidity()`: `null`\n * (or omitting `validity` entirely) means always valid. Pair with `validationMessage`.\n *\n * @example\n * ```ts\n * validity: computed(() => (required.value && isBlank(value.value)) ? { valueMissing: true } : null),\n * validationMessage: computed(() => (required.value && isBlank(value.value)) ? 'Required.' : ''),\n * ```\n */\n validationMessage?: Readable<string>;\n validity?: Readable<ValidityStateFlags | null>;\n value: Signal<T> | Readable<T>;\n};\n\nexport type FormFieldHandle = {\n checkValidity: () => boolean;\n readonly internals: ElementInternals;\n reportValidity: () => boolean;\n /**\n * Set (non-empty message) or clear (empty string) a custom validity error.\n * Guards the platform's throw-on-empty-message contract: prefer this over\n * calling `internals.setValidity` directly.\n */\n setCustomValidity: (message: string) => void;\n};\n\nexport const useField = <T = unknown>(options: FormFieldOptions<T>): FormFieldHandle => {\n const host = getHost();\n const ctor = host.constructor as typeof HTMLElement & { formAssociated?: boolean };\n\n if (!ctor.formAssociated) {\n throw new OreApiError(ORE_ERRORS.defineFieldRequiresFormAssociated(host.localName));\n }\n\n if (activeFieldRegistry.has(host)) {\n throw new OreApiError(ORE_ERRORS.useFieldAlreadyCalled(host.localName));\n }\n\n // ElementInternals may only be attached once for an element's lifetime, while setup\n // legitimately runs again when a custom element is reconnected.\n const internals = internalsRegistry.get(host) ?? host.attachInternals();\n\n internalsRegistry.set(host, internals);\n activeFieldRegistry.add(host);\n onCleanup(() => activeFieldRegistry.delete(host));\n\n const toFormValue =\n options.toFormValue ??\n ((v: T): File | FormData | string | null => {\n if (v == null) return options.emptyStringForNull ? '' : null;\n\n if (v instanceof File || v instanceof FormData) return v;\n\n return String(v);\n });\n\n watchEffect(() => {\n internals.setFormValue(toFormValue(options.value.value));\n });\n\n const disabled = options.disabled;\n\n if (disabled) {\n if (!('states' in internals)) {\n warn(\n 'useField(): ElementInternals.states (CustomStateSet) is not available in this environment: disabled state tracking skipped.',\n );\n } else {\n const states = internals.states as CustomStateSet;\n\n watchEffect(() => {\n if (disabled.value) states.add('disabled');\n else states.delete('disabled');\n });\n }\n }\n\n if (options.validity) {\n watchEffect(() => {\n const flags = options.validity?.value ?? {};\n // Per spec, ElementInternals.setValidity() throws if any flag is true and message is\n // empty: a caller-supplied `validity` with no matching `validationMessage` would crash\n // this effect in a real browser. Fail safe with a generic message instead of propagating\n // that crash, and say so loudly in dev so the real fix (pass validationMessage) gets made.\n const hasFlag = Object.values(flags).some(Boolean);\n const message = options.validationMessage?.value ?? '';\n\n if (hasFlag && !message) {\n warn(\n 'useField(): `validity` has a truthy flag but `validationMessage` is empty: internals.setValidity() ' +\n 'requires a non-empty message whenever any flag is true. Falling back to a generic message; pass ' +\n '`validationMessage` to customize it.',\n );\n internals.setValidity(flags, 'Invalid value.');\n\n return;\n }\n\n internals.setValidity(flags, message);\n });\n }\n\n if (options.onReset) onFormReset(options.onReset);\n\n const checkValidity = () => internals.checkValidity();\n const reportValidity = () => internals.reportValidity();\n const setCustomValidity = (message: string) =>\n message ? internals.setValidity({ customError: true }, message) : internals.setValidity({});\n\n return {\n checkValidity,\n internals,\n reportValidity,\n setCustomValidity,\n };\n};\n"],"mappings":"oFAOA,IAAM,EAAoB,IAAI,QAExB,EAAsB,IAAI,QA8CnB,EAAyB,GAAkD,CACtF,IAAM,EAAO,EAAA,QAAQ,EAGrB,GAAI,CAFS,EAAK,YAER,eACR,MAAM,IAAI,EAAA,YAAY,EAAA,WAAW,kCAAkC,EAAK,SAAS,CAAC,EAGpF,GAAI,EAAoB,IAAI,CAAI,EAC9B,MAAM,IAAI,EAAA,YAAY,EAAA,WAAW,sBAAsB,EAAK,SAAS,CAAC,EAKxE,IAAM,EAAY,EAAkB,IAAI,CAAI,GAAK,EAAK,gBAAgB,EAEtE,EAAkB,IAAI,EAAM,CAAS,EACrC,EAAoB,IAAI,CAAI,EAC5B,EAAA,cAAgB,EAAoB,OAAO,CAAI,CAAC,EAEhD,IAAM,EACJ,EAAQ,cACN,GACI,GAAK,KAAa,EAAQ,mBAAqB,GAAK,KAEpD,aAAa,MAAQ,aAAa,SAAiB,EAEhD,OAAO,CAAC,GAGnB,EAAA,gBAAkB,CAChB,EAAU,aAAa,EAAY,EAAQ,MAAM,KAAK,CAAC,CACzD,CAAC,EAED,IAAM,EAAW,EAAQ,SAEzB,GAAI,GACI,WAAY,EAIX,CACL,IAAM,EAAS,EAAU,OAEzB,EAAA,gBAAkB,CACZ,EAAS,MAAO,EAAO,IAAI,UAAU,EACpC,EAAO,OAAO,UAAU,CAC/B,CAAC,CACH,CAmCF,OAhCI,EAAQ,UACV,EAAA,gBAAkB,CAChB,IAAM,EAAQ,EAAQ,UAAU,OAAS,CAAC,EAKpC,EAAU,OAAO,OAAO,CAAK,CAAC,CAAC,KAAK,OAAO,EAC3C,EAAU,EAAQ,mBAAmB,OAAS,GAEpD,GAAI,GAAW,CAAC,EAAS,CAMvB,EAAU,YAAY,EAAO,gBAAgB,EAE7C,MACF,CAEA,EAAU,YAAY,EAAO,CAAO,CACtC,CAAC,EAGC,EAAQ,SAAS,EAAA,YAAY,EAAQ,OAAO,EAOzC,CACL,kBAN0B,EAAU,cAAc,EAOlD,YACA,mBAP2B,EAAU,eAAe,EAQpD,kBAPyB,GACzB,EAAU,EAAU,YAAY,CAAE,YAAa,EAAK,EAAG,CAAO,EAAI,EAAU,YAAY,CAAC,CAAC,CAO5F,CACF"}
|
package/dist/forms/field.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export type FormFieldOptions<T = unknown> = {
|
|
|
4
4
|
/**
|
|
5
5
|
* When `true`, a `null` or `undefined` value is submitted as an empty string
|
|
6
6
|
* (`''`) instead of `null`. This keeps the field's key present in `FormData`
|
|
7
|
-
* even when the value is absent
|
|
7
|
+
* even when the value is absent: useful when the server expects the field to
|
|
8
8
|
* always be included.
|
|
9
9
|
*
|
|
10
10
|
* @default false
|
|
@@ -12,12 +12,12 @@ export type FormFieldOptions<T = unknown> = {
|
|
|
12
12
|
emptyStringForNull?: boolean;
|
|
13
13
|
/**
|
|
14
14
|
* Called when the ancestor `<form>` is reset (see `onFormReset`). Use to restore
|
|
15
|
-
* whatever local state backs `value
|
|
15
|
+
* whatever local state backs `value`: `useField` itself owns no field state to reset.
|
|
16
16
|
*/
|
|
17
17
|
onReset?: () => void;
|
|
18
18
|
toFormValue?: (value: T) => File | FormData | string | null;
|
|
19
19
|
/**
|
|
20
|
-
* Recomputed reactively and passed straight to `internals.setValidity()
|
|
20
|
+
* Recomputed reactively and passed straight to `internals.setValidity()`: `null`
|
|
21
21
|
* (or omitting `validity` entirely) means always valid. Pair with `validationMessage`.
|
|
22
22
|
*
|
|
23
23
|
* @example
|
|
@@ -36,7 +36,7 @@ export type FormFieldHandle = {
|
|
|
36
36
|
reportValidity: () => boolean;
|
|
37
37
|
/**
|
|
38
38
|
* Set (non-empty message) or clear (empty string) a custom validity error.
|
|
39
|
-
* Guards the platform's throw-on-empty-message contract
|
|
39
|
+
* Guards the platform's throw-on-empty-message contract: prefer this over
|
|
40
40
|
* calling `internals.setValidity` directly.
|
|
41
41
|
*/
|
|
42
42
|
setCustomValidity: (message: string) => void;
|
package/dist/forms/field.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field.js","names":[],"sources":["../../src/forms/field.ts"],"sourcesContent":["import type { Readable, Signal } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport { ORE_ERRORS, OreApiError } from '../errors';\nimport { getHost, onCleanup, onFormReset, watchEffect } from '../runtime';\n\n/** @internal */\nconst internalsRegistry = new WeakMap<HTMLElement, ElementInternals>();\n/** @internal */\nconst activeFieldRegistry = new WeakSet<HTMLElement>();\n\nexport type FormFieldOptions<T = unknown> = {\n disabled?: Readable<boolean>;\n /**\n * When `true`, a `null` or `undefined` value is submitted as an empty string\n * (`''`) instead of `null`. This keeps the field's key present in `FormData`\n * even when the value is absent
|
|
1
|
+
{"version":3,"file":"field.js","names":[],"sources":["../../src/forms/field.ts"],"sourcesContent":["import type { Readable, Signal } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport { ORE_ERRORS, OreApiError } from '../errors';\nimport { getHost, onCleanup, onFormReset, watchEffect } from '../runtime';\n\n/** @internal */\nconst internalsRegistry = new WeakMap<HTMLElement, ElementInternals>();\n/** @internal */\nconst activeFieldRegistry = new WeakSet<HTMLElement>();\n\nexport type FormFieldOptions<T = unknown> = {\n disabled?: Readable<boolean>;\n /**\n * When `true`, a `null` or `undefined` value is submitted as an empty string\n * (`''`) instead of `null`. This keeps the field's key present in `FormData`\n * even when the value is absent: useful when the server expects the field to\n * always be included.\n *\n * @default false\n */\n emptyStringForNull?: boolean;\n /**\n * Called when the ancestor `<form>` is reset (see `onFormReset`). Use to restore\n * whatever local state backs `value`: `useField` itself owns no field state to reset.\n */\n onReset?: () => void;\n toFormValue?: (value: T) => File | FormData | string | null;\n /**\n * Recomputed reactively and passed straight to `internals.setValidity()`: `null`\n * (or omitting `validity` entirely) means always valid. Pair with `validationMessage`.\n *\n * @example\n * ```ts\n * validity: computed(() => (required.value && isBlank(value.value)) ? { valueMissing: true } : null),\n * validationMessage: computed(() => (required.value && isBlank(value.value)) ? 'Required.' : ''),\n * ```\n */\n validationMessage?: Readable<string>;\n validity?: Readable<ValidityStateFlags | null>;\n value: Signal<T> | Readable<T>;\n};\n\nexport type FormFieldHandle = {\n checkValidity: () => boolean;\n readonly internals: ElementInternals;\n reportValidity: () => boolean;\n /**\n * Set (non-empty message) or clear (empty string) a custom validity error.\n * Guards the platform's throw-on-empty-message contract: prefer this over\n * calling `internals.setValidity` directly.\n */\n setCustomValidity: (message: string) => void;\n};\n\nexport const useField = <T = unknown>(options: FormFieldOptions<T>): FormFieldHandle => {\n const host = getHost();\n const ctor = host.constructor as typeof HTMLElement & { formAssociated?: boolean };\n\n if (!ctor.formAssociated) {\n throw new OreApiError(ORE_ERRORS.defineFieldRequiresFormAssociated(host.localName));\n }\n\n if (activeFieldRegistry.has(host)) {\n throw new OreApiError(ORE_ERRORS.useFieldAlreadyCalled(host.localName));\n }\n\n // ElementInternals may only be attached once for an element's lifetime, while setup\n // legitimately runs again when a custom element is reconnected.\n const internals = internalsRegistry.get(host) ?? host.attachInternals();\n\n internalsRegistry.set(host, internals);\n activeFieldRegistry.add(host);\n onCleanup(() => activeFieldRegistry.delete(host));\n\n const toFormValue =\n options.toFormValue ??\n ((v: T): File | FormData | string | null => {\n if (v == null) return options.emptyStringForNull ? '' : null;\n\n if (v instanceof File || v instanceof FormData) return v;\n\n return String(v);\n });\n\n watchEffect(() => {\n internals.setFormValue(toFormValue(options.value.value));\n });\n\n const disabled = options.disabled;\n\n if (disabled) {\n if (!('states' in internals)) {\n warn(\n 'useField(): ElementInternals.states (CustomStateSet) is not available in this environment: disabled state tracking skipped.',\n );\n } else {\n const states = internals.states as CustomStateSet;\n\n watchEffect(() => {\n if (disabled.value) states.add('disabled');\n else states.delete('disabled');\n });\n }\n }\n\n if (options.validity) {\n watchEffect(() => {\n const flags = options.validity?.value ?? {};\n // Per spec, ElementInternals.setValidity() throws if any flag is true and message is\n // empty: a caller-supplied `validity` with no matching `validationMessage` would crash\n // this effect in a real browser. Fail safe with a generic message instead of propagating\n // that crash, and say so loudly in dev so the real fix (pass validationMessage) gets made.\n const hasFlag = Object.values(flags).some(Boolean);\n const message = options.validationMessage?.value ?? '';\n\n if (hasFlag && !message) {\n warn(\n 'useField(): `validity` has a truthy flag but `validationMessage` is empty: internals.setValidity() ' +\n 'requires a non-empty message whenever any flag is true. Falling back to a generic message; pass ' +\n '`validationMessage` to customize it.',\n );\n internals.setValidity(flags, 'Invalid value.');\n\n return;\n }\n\n internals.setValidity(flags, message);\n });\n }\n\n if (options.onReset) onFormReset(options.onReset);\n\n const checkValidity = () => internals.checkValidity();\n const reportValidity = () => internals.reportValidity();\n const setCustomValidity = (message: string) =>\n message ? internals.setValidity({ customError: true }, message) : internals.setValidity({});\n\n return {\n checkValidity,\n internals,\n reportValidity,\n setCustomValidity,\n };\n};\n"],"mappings":"uKAOA,IAAM,EAAoB,IAAI,QAExB,EAAsB,IAAI,QA8CnB,EAAyB,GAAkD,CACtF,IAAM,EAAO,EAAQ,EAGrB,GAAI,CAFS,EAAK,YAER,eACR,MAAM,IAAI,EAAY,EAAW,kCAAkC,EAAK,SAAS,CAAC,EAGpF,GAAI,EAAoB,IAAI,CAAI,EAC9B,MAAM,IAAI,EAAY,EAAW,sBAAsB,EAAK,SAAS,CAAC,EAKxE,IAAM,EAAY,EAAkB,IAAI,CAAI,GAAK,EAAK,gBAAgB,EAEtE,EAAkB,IAAI,EAAM,CAAS,EACrC,EAAoB,IAAI,CAAI,EAC5B,MAAgB,EAAoB,OAAO,CAAI,CAAC,EAEhD,IAAM,EACJ,EAAQ,cACN,GACI,GAAK,KAAa,EAAQ,mBAAqB,GAAK,KAEpD,aAAa,MAAQ,aAAa,SAAiB,EAEhD,OAAO,CAAC,GAGnB,MAAkB,CAChB,EAAU,aAAa,EAAY,EAAQ,MAAM,KAAK,CAAC,CACzD,CAAC,EAED,IAAM,EAAW,EAAQ,SAEzB,GAAI,GACI,WAAY,EAIX,CACL,IAAM,EAAS,EAAU,OAEzB,MAAkB,CACZ,EAAS,MAAO,EAAO,IAAI,UAAU,EACpC,EAAO,OAAO,UAAU,CAC/B,CAAC,CACH,CAmCF,OAhCI,EAAQ,UACV,MAAkB,CAChB,IAAM,EAAQ,EAAQ,UAAU,OAAS,CAAC,EAKpC,EAAU,OAAO,OAAO,CAAK,CAAC,CAAC,KAAK,OAAO,EAC3C,EAAU,EAAQ,mBAAmB,OAAS,GAEpD,GAAI,GAAW,CAAC,EAAS,CAMvB,EAAU,YAAY,EAAO,gBAAgB,EAE7C,MACF,CAEA,EAAU,YAAY,EAAO,CAAO,CACtC,CAAC,EAGC,EAAQ,SAAS,EAAY,EAAQ,OAAO,EAOzC,CACL,kBAN0B,EAAU,cAAc,EAOlD,YACA,mBAP2B,EAAU,eAAe,EAQpD,kBAPyB,GACzB,EAAU,EAAU,YAAY,CAAE,YAAa,EAAK,EAAG,CAAO,EAAI,EAAU,YAAY,CAAC,CAAC,CAO5F,CACF"}
|
package/dist/host-bind.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-bind.cjs","names":[],"sources":["../src/host-bind.ts"],"sourcesContent":["/**\n * Host element binding API
|
|
1
|
+
{"version":3,"file":"host-bind.cjs","names":[],"sources":["../src/host-bind.ts"],"sourcesContent":["/**\n * Host element binding API: reactive attr, class, style, and event bindings\n * applied directly to the component's host element or any target element.\n */\n\nimport { isReactive } from '@vielzeug/ripple';\n\nimport { getHost, tryRegisterCleanup, watchEffect } from './runtime';\nimport { normalizeHostAttrKey } from './utils/aria';\nimport { listen, type MaybeReactive, resolveMaybeReactive, sanitizeCssToken, setAttr, toKebab } from './utils/dom';\n\n/**\n * Describes a reactive or static host binding value.\n */\nexport type HostBindingValue = MaybeReactive<string | number | boolean | null | undefined>;\n\n/**\n * Configuration for host attribute bindings.\n */\nexport type AttributeBindings = Record<string, HostBindingValue>;\n\ntype HostClassBindingValue = MaybeReactive<boolean>;\n// Bivariant callback allows consumers to use narrower event types.\ntype HostEventListener = { bivarianceHack(event: Event): void }['bivarianceHack'];\n\nexport type HostBindConfig = {\n attr?: AttributeBindings;\n class?: (() => Record<string, boolean>) | Record<string, HostClassBindingValue>;\n on?: Record<string, HostEventListener | undefined>;\n style?: Record<string, HostBindingValue>;\n};\n\nexport type BindOptions = AddEventListenerOptions & {\n /**\n * Target element to bind to. Defaults to the host element when called\n * via `bind()`. Pass an explicit element to bind to any other element\n * (e.g. a slotted trigger or an internally-referenced child element).\n * When a target is provided, cleanup is always auto-registered with the\n * component scope if one is active.\n */\n target?: Element;\n};\n\n/**\n * Apply reactive or static bindings to an element's attributes, classes, styles,\n * and events. Defaults to the current component's host element; pass\n * `options.target` to bind to any other element (e.g. a slotted trigger, an\n * internally-referenced child).\n */\nexport const bind = (config: HostBindConfig, options?: BindOptions): (() => void) => {\n const el = (options?.target as HTMLElement | undefined) ?? getHost();\n const disposers: Array<() => void> = [];\n\n if (config.attr) {\n for (const [key, value] of Object.entries(config.attr)) {\n const name = toHostAttr(key);\n const dispose = applyAttribute(el, name, value);\n\n if (dispose) disposers.push(dispose);\n }\n }\n\n if (config.class) {\n disposers.push(applyClassMap(el, config.class));\n }\n\n if (config.style) {\n for (const [key, value] of Object.entries(config.style)) {\n const dispose = applyStyle(el, key, value);\n\n if (dispose) disposers.push(dispose);\n }\n }\n\n if (config.on) {\n const { target: _t, ...listenerOptions } = options ?? {};\n\n for (const event of Object.keys(config.on) as Array<keyof typeof config.on>) {\n const listener = config.on[event];\n\n if (!listener) continue;\n\n disposers.push(listen(el, event as string, listener as EventListener, listenerOptions));\n }\n }\n\n const cleanup = (): void => {\n for (const dispose of disposers) dispose();\n };\n\n tryRegisterCleanup(cleanup);\n\n return cleanup;\n};\n\nconst toHostAttr = normalizeHostAttrKey;\n\nconst applyReactiveBinding = (\n value: HostBindingValue,\n updater: (next: string | number | boolean | null | undefined) => void,\n): (() => void) | undefined => {\n if (typeof value === 'function') {\n return watchEffect(() => {\n updater(value());\n return undefined;\n });\n }\n\n if (isReactive(value)) {\n return watchEffect(() => {\n updater(value.value);\n return undefined;\n });\n }\n\n updater(value);\n};\n\nfunction applyAttribute(host: HTMLElement, name: string, value: HostBindingValue): (() => void) | undefined {\n return applyReactiveBinding(value, (next) => setAttr(host, name, next));\n}\n\nfunction applyStyle(host: HTMLElement, name: string, value: HostBindingValue): (() => void) | undefined {\n const cssName = sanitizeCssToken(name.startsWith('--') ? name : toKebab(name));\n\n if (!cssName) return;\n\n let owned = false;\n const setStyle = (v: string | number | boolean | null | undefined): void => {\n if (v != null && v !== '') {\n owned = true;\n host.style.setProperty(cssName, sanitizeCssToken(String(v)));\n } else if (owned) host.style.removeProperty(cssName);\n };\n\n return applyReactiveBinding(value, setStyle);\n}\n\nfunction applyClassMap(\n host: HTMLElement,\n value: (() => Record<string, boolean>) | Record<string, HostClassBindingValue>,\n): () => void {\n const getMap =\n typeof value === 'function'\n ? value\n : (): Record<string, boolean> => {\n const result: Record<string, boolean> = {};\n\n for (const [cls, entry] of Object.entries(value)) {\n result[cls] = resolveMaybeReactive(entry);\n }\n\n return result;\n };\n\n let prev = new Set<string>();\n\n const sub = watchEffect(() => {\n const next = new Set<string>();\n\n for (const [cls, active] of Object.entries(getMap())) {\n if (!active) continue;\n\n next.add(cls);\n\n if (!prev.has(cls)) host.classList.add(cls);\n }\n for (const cls of prev) {\n if (!next.has(cls)) host.classList.remove(cls);\n }\n prev = next;\n });\n\n return sub;\n}\n"],"mappings":"8HAiDA,IAAa,GAAQ,EAAwB,IAAwC,CACnF,IAAM,EAAM,GAAS,QAAsC,EAAA,QAAQ,EAC7D,EAA+B,CAAC,EAEtC,GAAI,EAAO,KACT,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAO,IAAI,EAAG,CAEtD,IAAM,EAAU,EAAe,EADlB,EAAW,CACW,EAAM,CAAK,EAE1C,GAAS,EAAU,KAAK,CAAO,CACrC,CAOF,GAJI,EAAO,OACT,EAAU,KAAK,EAAc,EAAI,EAAO,KAAK,CAAC,EAG5C,EAAO,MACT,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAO,KAAK,EAAG,CACvD,IAAM,EAAU,EAAW,EAAI,EAAK,CAAK,EAErC,GAAS,EAAU,KAAK,CAAO,CACrC,CAGF,GAAI,EAAO,GAAI,CACb,GAAM,CAAE,OAAQ,EAAI,GAAG,GAAoB,GAAW,CAAC,EAEvD,IAAK,IAAM,KAAS,OAAO,KAAK,EAAO,EAAE,EAAoC,CAC3E,IAAM,EAAW,EAAO,GAAG,GAEtB,GAEL,EAAU,KAAK,EAAA,OAAO,EAAI,EAAiB,EAA2B,CAAe,CAAC,CACxF,CACF,CAEA,IAAM,MAAsB,CAC1B,IAAK,IAAM,KAAW,EAAW,EAAQ,CAC3C,EAIA,OAFA,EAAA,mBAAmB,CAAO,EAEnB,CACT,EAEM,EAAa,EAAA,qBAEb,GACJ,EACA,IAC6B,CAC7B,GAAI,OAAO,GAAU,WACnB,OAAO,EAAA,gBAAkB,CACvB,EAAQ,EAAM,CAAC,CAEjB,CAAC,EAGH,IAAA,EAAI,EAAA,WAAA,CAAW,CAAK,EAClB,OAAO,EAAA,gBAAkB,CACvB,EAAQ,EAAM,KAAK,CAErB,CAAC,EAGH,EAAQ,CAAK,CACf,EAEA,SAAS,EAAe,EAAmB,EAAc,EAAmD,CAC1G,OAAO,EAAqB,EAAQ,GAAS,EAAA,QAAQ,EAAM,EAAM,CAAI,CAAC,CACxE,CAEA,SAAS,EAAW,EAAmB,EAAc,EAAmD,CACtG,IAAM,EAAU,EAAA,iBAAiB,EAAK,WAAW,IAAI,EAAI,EAAO,EAAA,QAAQ,CAAI,CAAC,EAE7E,GAAI,CAAC,EAAS,OAEd,IAAI,EAAQ,GAQZ,OAAO,EAAqB,EAPV,GAA0D,CACtE,GAAK,MAAQ,IAAM,IACrB,EAAQ,GACR,EAAK,MAAM,YAAY,EAAS,EAAA,iBAAiB,OAAO,CAAC,CAAC,CAAC,GAClD,GAAO,EAAK,MAAM,eAAe,CAAO,CACrD,CAE2C,CAC7C,CAEA,SAAS,EACP,EACA,EACY,CACZ,IAAM,EACJ,OAAO,GAAU,WACb,MAC+B,CAC7B,IAAM,EAAkC,CAAC,EAEzC,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAAK,EAC7C,EAAO,GAAO,EAAA,qBAAqB,CAAK,EAG1C,OAAO,CACT,EAEF,EAAO,IAAI,IAkBf,OAhBY,EAAA,gBAAkB,CAC5B,IAAM,EAAO,IAAI,IAEjB,IAAK,GAAM,CAAC,EAAK,KAAW,OAAO,QAAQ,EAAO,CAAC,EAC5C,IAEL,EAAK,IAAI,CAAG,EAEP,EAAK,IAAI,CAAG,GAAG,EAAK,UAAU,IAAI,CAAG,GAE5C,IAAK,IAAM,KAAO,EACX,EAAK,IAAI,CAAG,GAAG,EAAK,UAAU,OAAO,CAAG,EAE/C,EAAO,CACT,CAEO,CACT"}
|
package/dist/host-bind.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Host element binding API
|
|
2
|
+
* Host element binding API: reactive attr, class, style, and event bindings
|
|
3
3
|
* applied directly to the component's host element or any target element.
|
|
4
4
|
*/
|
|
5
|
-
import { type
|
|
5
|
+
import { type MaybeReactive } from './utils/dom';
|
|
6
6
|
/**
|
|
7
7
|
* Describes a reactive or static host binding value.
|
|
8
8
|
*/
|
|
9
|
-
export type HostBindingValue =
|
|
9
|
+
export type HostBindingValue = MaybeReactive<string | number | boolean | null | undefined>;
|
|
10
10
|
/**
|
|
11
11
|
* Configuration for host attribute bindings.
|
|
12
12
|
*/
|
|
13
13
|
export type AttributeBindings = Record<string, HostBindingValue>;
|
|
14
|
-
type HostClassBindingValue =
|
|
14
|
+
type HostClassBindingValue = MaybeReactive<boolean>;
|
|
15
15
|
type HostEventListener = {
|
|
16
16
|
bivarianceHack(event: Event): void;
|
|
17
17
|
}['bivarianceHack'];
|
package/dist/host-bind.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-bind.d.ts","sourceRoot":"","sources":["../src/host-bind.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"host-bind.d.ts","sourceRoot":"","sources":["../src/host-bind.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EAAU,KAAK,aAAa,EAA4D,MAAM,aAAa,CAAC;AAEnH;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,aAAa,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAE3F;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAEjE,KAAK,qBAAqB,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;AAEpD,KAAK,iBAAiB,GAAG;IAAE,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CAAE,CAAC,gBAAgB,CAAC,CAAC;AAElF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB,KAAK,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IAChF,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAC,CAAC;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,uBAAuB,GAAG;IAClD;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,IAAI,GAAI,QAAQ,cAAc,EAAE,UAAU,WAAW,KAAG,CAAC,MAAM,IAAI,CA4C/E,CAAC"}
|
package/dist/host-bind.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-bind.js","names":[],"sources":["../src/host-bind.ts"],"sourcesContent":["/**\n * Host element binding API
|
|
1
|
+
{"version":3,"file":"host-bind.js","names":[],"sources":["../src/host-bind.ts"],"sourcesContent":["/**\n * Host element binding API: reactive attr, class, style, and event bindings\n * applied directly to the component's host element or any target element.\n */\n\nimport { isReactive } from '@vielzeug/ripple';\n\nimport { getHost, tryRegisterCleanup, watchEffect } from './runtime';\nimport { normalizeHostAttrKey } from './utils/aria';\nimport { listen, type MaybeReactive, resolveMaybeReactive, sanitizeCssToken, setAttr, toKebab } from './utils/dom';\n\n/**\n * Describes a reactive or static host binding value.\n */\nexport type HostBindingValue = MaybeReactive<string | number | boolean | null | undefined>;\n\n/**\n * Configuration for host attribute bindings.\n */\nexport type AttributeBindings = Record<string, HostBindingValue>;\n\ntype HostClassBindingValue = MaybeReactive<boolean>;\n// Bivariant callback allows consumers to use narrower event types.\ntype HostEventListener = { bivarianceHack(event: Event): void }['bivarianceHack'];\n\nexport type HostBindConfig = {\n attr?: AttributeBindings;\n class?: (() => Record<string, boolean>) | Record<string, HostClassBindingValue>;\n on?: Record<string, HostEventListener | undefined>;\n style?: Record<string, HostBindingValue>;\n};\n\nexport type BindOptions = AddEventListenerOptions & {\n /**\n * Target element to bind to. Defaults to the host element when called\n * via `bind()`. Pass an explicit element to bind to any other element\n * (e.g. a slotted trigger or an internally-referenced child element).\n * When a target is provided, cleanup is always auto-registered with the\n * component scope if one is active.\n */\n target?: Element;\n};\n\n/**\n * Apply reactive or static bindings to an element's attributes, classes, styles,\n * and events. Defaults to the current component's host element; pass\n * `options.target` to bind to any other element (e.g. a slotted trigger, an\n * internally-referenced child).\n */\nexport const bind = (config: HostBindConfig, options?: BindOptions): (() => void) => {\n const el = (options?.target as HTMLElement | undefined) ?? getHost();\n const disposers: Array<() => void> = [];\n\n if (config.attr) {\n for (const [key, value] of Object.entries(config.attr)) {\n const name = toHostAttr(key);\n const dispose = applyAttribute(el, name, value);\n\n if (dispose) disposers.push(dispose);\n }\n }\n\n if (config.class) {\n disposers.push(applyClassMap(el, config.class));\n }\n\n if (config.style) {\n for (const [key, value] of Object.entries(config.style)) {\n const dispose = applyStyle(el, key, value);\n\n if (dispose) disposers.push(dispose);\n }\n }\n\n if (config.on) {\n const { target: _t, ...listenerOptions } = options ?? {};\n\n for (const event of Object.keys(config.on) as Array<keyof typeof config.on>) {\n const listener = config.on[event];\n\n if (!listener) continue;\n\n disposers.push(listen(el, event as string, listener as EventListener, listenerOptions));\n }\n }\n\n const cleanup = (): void => {\n for (const dispose of disposers) dispose();\n };\n\n tryRegisterCleanup(cleanup);\n\n return cleanup;\n};\n\nconst toHostAttr = normalizeHostAttrKey;\n\nconst applyReactiveBinding = (\n value: HostBindingValue,\n updater: (next: string | number | boolean | null | undefined) => void,\n): (() => void) | undefined => {\n if (typeof value === 'function') {\n return watchEffect(() => {\n updater(value());\n return undefined;\n });\n }\n\n if (isReactive(value)) {\n return watchEffect(() => {\n updater(value.value);\n return undefined;\n });\n }\n\n updater(value);\n};\n\nfunction applyAttribute(host: HTMLElement, name: string, value: HostBindingValue): (() => void) | undefined {\n return applyReactiveBinding(value, (next) => setAttr(host, name, next));\n}\n\nfunction applyStyle(host: HTMLElement, name: string, value: HostBindingValue): (() => void) | undefined {\n const cssName = sanitizeCssToken(name.startsWith('--') ? name : toKebab(name));\n\n if (!cssName) return;\n\n let owned = false;\n const setStyle = (v: string | number | boolean | null | undefined): void => {\n if (v != null && v !== '') {\n owned = true;\n host.style.setProperty(cssName, sanitizeCssToken(String(v)));\n } else if (owned) host.style.removeProperty(cssName);\n };\n\n return applyReactiveBinding(value, setStyle);\n}\n\nfunction applyClassMap(\n host: HTMLElement,\n value: (() => Record<string, boolean>) | Record<string, HostClassBindingValue>,\n): () => void {\n const getMap =\n typeof value === 'function'\n ? value\n : (): Record<string, boolean> => {\n const result: Record<string, boolean> = {};\n\n for (const [cls, entry] of Object.entries(value)) {\n result[cls] = resolveMaybeReactive(entry);\n }\n\n return result;\n };\n\n let prev = new Set<string>();\n\n const sub = watchEffect(() => {\n const next = new Set<string>();\n\n for (const [cls, active] of Object.entries(getMap())) {\n if (!active) continue;\n\n next.add(cls);\n\n if (!prev.has(cls)) host.classList.add(cls);\n }\n for (const cls of prev) {\n if (!next.has(cls)) host.classList.remove(cls);\n }\n prev = next;\n });\n\n return sub;\n}\n"],"mappings":"uSAiDA,IAAa,GAAQ,EAAwB,IAAwC,CACnF,IAAM,EAAM,GAAS,QAAsC,EAAQ,EAC7D,EAA+B,CAAC,EAEtC,GAAI,EAAO,KACT,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAO,IAAI,EAAG,CAEtD,IAAM,EAAU,EAAe,EADlB,EAAW,CACW,EAAM,CAAK,EAE1C,GAAS,EAAU,KAAK,CAAO,CACrC,CAOF,GAJI,EAAO,OACT,EAAU,KAAK,EAAc,EAAI,EAAO,KAAK,CAAC,EAG5C,EAAO,MACT,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAO,KAAK,EAAG,CACvD,IAAM,EAAU,EAAW,EAAI,EAAK,CAAK,EAErC,GAAS,EAAU,KAAK,CAAO,CACrC,CAGF,GAAI,EAAO,GAAI,CACb,GAAM,CAAE,OAAQ,EAAI,GAAG,GAAoB,GAAW,CAAC,EAEvD,IAAK,IAAM,KAAS,OAAO,KAAK,EAAO,EAAE,EAAoC,CAC3E,IAAM,EAAW,EAAO,GAAG,GAEtB,GAEL,EAAU,KAAK,EAAO,EAAI,EAAiB,EAA2B,CAAe,CAAC,CACxF,CACF,CAEA,IAAM,MAAsB,CAC1B,IAAK,IAAM,KAAW,EAAW,EAAQ,CAC3C,EAIA,OAFA,EAAmB,CAAO,EAEnB,CACT,EAEM,EAAa,EAEb,GACJ,EACA,IAC6B,CAC7B,GAAI,OAAO,GAAU,WACnB,OAAO,MAAkB,CACvB,EAAQ,EAAM,CAAC,CAEjB,CAAC,EAGH,GAAI,EAAW,CAAK,EAClB,OAAO,MAAkB,CACvB,EAAQ,EAAM,KAAK,CAErB,CAAC,EAGH,EAAQ,CAAK,CACf,EAEA,SAAS,EAAe,EAAmB,EAAc,EAAmD,CAC1G,OAAO,EAAqB,EAAQ,GAAS,EAAQ,EAAM,EAAM,CAAI,CAAC,CACxE,CAEA,SAAS,EAAW,EAAmB,EAAc,EAAmD,CACtG,IAAM,EAAU,EAAiB,EAAK,WAAW,IAAI,EAAI,EAAO,EAAQ,CAAI,CAAC,EAE7E,GAAI,CAAC,EAAS,OAEd,IAAI,EAAQ,GAQZ,OAAO,EAAqB,EAPV,GAA0D,CACtE,GAAK,MAAQ,IAAM,IACrB,EAAQ,GACR,EAAK,MAAM,YAAY,EAAS,EAAiB,OAAO,CAAC,CAAC,CAAC,GAClD,GAAO,EAAK,MAAM,eAAe,CAAO,CACrD,CAE2C,CAC7C,CAEA,SAAS,EACP,EACA,EACY,CACZ,IAAM,EACJ,OAAO,GAAU,WACb,MAC+B,CAC7B,IAAM,EAAkC,CAAC,EAEzC,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,CAAK,EAC7C,EAAO,GAAO,EAAqB,CAAK,EAG1C,OAAO,CACT,EAEF,EAAO,IAAI,IAkBf,OAhBY,MAAkB,CAC5B,IAAM,EAAO,IAAI,IAEjB,IAAK,GAAM,CAAC,EAAK,KAAW,OAAO,QAAQ,EAAO,CAAC,EAC5C,IAEL,EAAK,IAAI,CAAG,EAEP,EAAK,IAAI,CAAG,GAAG,EAAK,UAAU,IAAI,CAAG,GAE5C,IAAK,IAAM,KAAO,EACX,EAAK,IAAI,CAAG,GAAG,EAAK,UAAU,OAAO,CAAG,EAE/C,EAAO,CACT,CAEO,CACT"}
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export { type ComponentSlots, useSlots } from './slots';
|
|
|
17
17
|
export { html } from './template/instantiator';
|
|
18
18
|
export { type HTMLResult, type Ref, type RefCallback, ref } from './template/result';
|
|
19
19
|
export { type CSSResult, css } from './utils/css';
|
|
20
|
+
export type { MaybeReactive } from './utils/dom';
|
|
20
21
|
export { type EmitFn, useEmit } from './utils/emit';
|
|
21
22
|
export { createId } from './utils/id';
|
|
22
23
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC5F,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAOlC,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,KAAK,WAAW,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC1G,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACtF,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,IAAI,EACJ,KAAK,cAAc,EACnB,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,OAAO,EACL,OAAO,EACP,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,SAAS,EACT,SAAS,EACT,OAAO,EACP,WAAW,EACX,SAAS,EACT,WAAW,GACZ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,KAAK,cAAc,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,GAAG,EAAE,KAAK,WAAW,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EAAE,KAAK,SAAS,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,KAAK,MAAM,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC5F,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAOlC,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,KAAK,WAAW,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC1G,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,gBAAgB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACtF,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,IAAI,EACJ,KAAK,cAAc,EACnB,KAAK,gBAAgB,GACtB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,OAAO,EACL,OAAO,EACP,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,SAAS,EACT,SAAS,EACT,OAAO,EACP,WAAW,EACX,SAAS,EACT,WAAW,GACZ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,KAAK,cAAc,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,GAAG,EAAE,KAAK,WAAW,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACrF,OAAO,EAAE,KAAK,SAAS,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClD,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,KAAK,MAAM,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
|