@vielzeug/ore 1.2.3 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -8
- package/dist/_component-class.cjs +2 -0
- package/dist/_component-class.cjs.map +1 -0
- package/dist/_component-class.d.ts +2 -0
- package/dist/_component-class.d.ts.map +1 -0
- package/dist/_component-class.js +2 -0
- package/dist/_component-class.js.map +1 -0
- package/dist/_dev.cjs +1 -1
- package/dist/_dev.cjs.map +1 -1
- package/dist/_dev.js +1 -1
- package/dist/_dev.js.map +1 -1
- package/dist/base-element.cjs +1 -1
- package/dist/base-element.cjs.map +1 -1
- package/dist/base-element.d.ts +9 -15
- 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 +3 -14
- package/dist/component-types.d.ts.map +1 -1
- package/dist/context.cjs +1 -1
- package/dist/context.cjs.map +1 -1
- package/dist/context.d.ts +13 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +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 +6 -5
- 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 +1 -1
- package/dist/directives/classMap.cjs.map +1 -1
- package/dist/directives/classMap.d.ts.map +1 -1
- package/dist/directives/classMap.js +1 -1
- package/dist/directives/classMap.js.map +1 -1
- package/dist/directives/each.cjs +1 -1
- package/dist/directives/each.cjs.map +1 -1
- package/dist/directives/each.d.ts +9 -2
- package/dist/directives/each.d.ts.map +1 -1
- package/dist/directives/each.js +1 -1
- package/dist/directives/each.js.map +1 -1
- package/dist/directives/live.cjs +1 -1
- package/dist/directives/live.cjs.map +1 -1
- package/dist/directives/live.d.ts +10 -12
- package/dist/directives/live.d.ts.map +1 -1
- package/dist/directives/live.js +1 -1
- package/dist/directives/live.js.map +1 -1
- package/dist/directives/styleMap.cjs +1 -1
- package/dist/directives/styleMap.cjs.map +1 -1
- package/dist/directives/styleMap.d.ts +1 -1
- package/dist/directives/styleMap.d.ts.map +1 -1
- package/dist/directives/styleMap.js +1 -1
- package/dist/directives/styleMap.js.map +1 -1
- package/dist/directives/unsafe-html.cjs +2 -0
- package/dist/directives/unsafe-html.cjs.map +1 -0
- package/dist/directives/unsafe-html.d.ts +14 -0
- package/dist/directives/unsafe-html.d.ts.map +1 -0
- package/dist/directives/unsafe-html.js +2 -0
- package/dist/directives/unsafe-html.js.map +1 -0
- package/dist/directives/when.cjs +1 -1
- package/dist/directives/when.cjs.map +1 -1
- package/dist/directives/when.d.ts +1 -1
- package/dist/directives/when.d.ts.map +1 -1
- package/dist/directives/when.js +1 -1
- package/dist/directives/when.js.map +1 -1
- package/dist/errors.cjs +2 -2
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +21 -6
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -2
- package/dist/errors.js.map +1 -1
- package/dist/forms/field.cjs +1 -1
- package/dist/forms/field.cjs.map +1 -1
- package/dist/forms/field.d.ts +5 -1
- package/dist/forms/field.d.ts.map +1 -1
- package/dist/forms/field.js +1 -1
- package/dist/forms/field.js.map +1 -1
- package/dist/host-bind.cjs +1 -1
- package/dist/host-bind.cjs.map +1 -1
- package/dist/host-bind.d.ts +6 -0
- package/dist/host-bind.d.ts.map +1 -1
- package/dist/host-bind.js +1 -1
- package/dist/host-bind.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +15 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/observers/intersection-observe.cjs +1 -1
- package/dist/observers/intersection-observe.cjs.map +1 -1
- package/dist/observers/intersection-observe.d.ts.map +1 -1
- package/dist/observers/intersection-observe.js +1 -1
- package/dist/observers/intersection-observe.js.map +1 -1
- package/dist/observers/media-observe.cjs +1 -1
- package/dist/observers/media-observe.cjs.map +1 -1
- package/dist/observers/media-observe.d.ts.map +1 -1
- package/dist/observers/media-observe.js +1 -1
- package/dist/observers/media-observe.js.map +1 -1
- package/dist/observers/mutation-observe.cjs +1 -1
- package/dist/observers/mutation-observe.cjs.map +1 -1
- package/dist/observers/mutation-observe.d.ts.map +1 -1
- package/dist/observers/mutation-observe.js +1 -1
- package/dist/observers/mutation-observe.js.map +1 -1
- package/dist/observers/resize-observe.cjs +1 -1
- package/dist/observers/resize-observe.cjs.map +1 -1
- package/dist/observers/resize-observe.d.ts.map +1 -1
- package/dist/observers/resize-observe.js +1 -1
- package/dist/observers/resize-observe.js.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 +1 -1
- package/dist/props.cjs.map +1 -1
- package/dist/props.d.ts +1 -6
- package/dist/props.d.ts.map +1 -1
- package/dist/props.js +1 -1
- package/dist/props.js.map +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.ts +7 -10
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/slots.cjs +1 -1
- package/dist/slots.cjs.map +1 -1
- package/dist/slots.d.ts.map +1 -1
- package/dist/slots.js +1 -1
- package/dist/slots.js.map +1 -1
- package/dist/template/binding-types.cjs +1 -0
- package/dist/template/binding-types.d.ts +59 -0
- package/dist/template/binding-types.d.ts.map +1 -0
- package/dist/template/binding-types.js +1 -0
- package/dist/template/bindings.cjs +1 -1
- package/dist/template/bindings.cjs.map +1 -1
- package/dist/template/bindings.d.ts +18 -3
- package/dist/template/bindings.d.ts.map +1 -1
- package/dist/template/bindings.js +1 -1
- package/dist/template/bindings.js.map +1 -1
- package/dist/template/compiler.cjs +1 -1
- package/dist/template/compiler.cjs.map +1 -1
- package/dist/template/compiler.d.ts +0 -4
- package/dist/template/compiler.d.ts.map +1 -1
- package/dist/template/compiler.js +1 -1
- package/dist/template/compiler.js.map +1 -1
- package/dist/template/instantiator.cjs +1 -1
- package/dist/template/instantiator.cjs.map +1 -1
- package/dist/template/instantiator.d.ts +6 -1
- package/dist/template/instantiator.d.ts.map +1 -1
- package/dist/template/instantiator.js +1 -1
- package/dist/template/instantiator.js.map +1 -1
- package/dist/template/result.cjs +2 -0
- package/dist/template/result.cjs.map +1 -0
- package/dist/template/result.d.ts +42 -0
- package/dist/template/result.d.ts.map +1 -0
- package/dist/template/result.js +2 -0
- package/dist/template/result.js.map +1 -0
- package/dist/testing/flush.cjs +1 -1
- package/dist/testing/flush.cjs.map +1 -1
- package/dist/testing/flush.d.ts +30 -26
- package/dist/testing/flush.d.ts.map +1 -1
- package/dist/testing/flush.js +1 -1
- package/dist/testing/flush.js.map +1 -1
- package/dist/testing/form-internals-polyfill.cjs +1 -1
- package/dist/testing/form-internals-polyfill.cjs.map +1 -1
- package/dist/testing/form-internals-polyfill.d.ts +9 -7
- package/dist/testing/form-internals-polyfill.d.ts.map +1 -1
- package/dist/testing/form-internals-polyfill.js +1 -1
- package/dist/testing/form-internals-polyfill.js.map +1 -1
- package/dist/testing/index.d.ts +35 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/mount.cjs +1 -1
- package/dist/testing/mount.cjs.map +1 -1
- package/dist/testing/mount.d.ts +10 -19
- package/dist/testing/mount.d.ts.map +1 -1
- package/dist/testing/mount.js +1 -1
- package/dist/testing/mount.js.map +1 -1
- package/dist/testing/render-hook.cjs +1 -1
- package/dist/testing/render-hook.cjs.map +1 -1
- package/dist/testing/render-hook.js +1 -1
- package/dist/testing/render-hook.js.map +1 -1
- package/dist/testing/reset.cjs +2 -0
- package/dist/testing/reset.cjs.map +1 -0
- package/dist/testing/reset.d.ts +12 -0
- package/dist/testing/reset.d.ts.map +1 -0
- package/dist/testing/reset.js +2 -0
- package/dist/testing/reset.js.map +1 -0
- package/dist/testing.cjs +2 -1
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.js +2 -1
- package/dist/testing.js.map +1 -0
- package/dist/utils/aria.cjs.map +1 -1
- package/dist/utils/aria.d.ts +1 -1
- package/dist/utils/aria.js.map +1 -1
- package/dist/utils/brand.cjs +2 -0
- package/dist/utils/brand.cjs.map +1 -0
- package/dist/utils/brand.d.ts +16 -0
- package/dist/utils/brand.d.ts.map +1 -0
- package/dist/utils/brand.js +2 -0
- package/dist/utils/brand.js.map +1 -0
- package/dist/utils/css.cjs +1 -1
- package/dist/utils/css.cjs.map +1 -1
- package/dist/utils/css.d.ts.map +1 -1
- package/dist/utils/css.js +1 -1
- package/dist/utils/css.js.map +1 -1
- package/dist/utils/dom.cjs +1 -1
- package/dist/utils/dom.cjs.map +1 -1
- package/dist/utils/dom.d.ts +34 -0
- package/dist/utils/dom.d.ts.map +1 -1
- package/dist/utils/dom.js +1 -1
- package/dist/utils/dom.js.map +1 -1
- package/dist/utils/emit.cjs.map +1 -1
- package/dist/utils/emit.d.ts +10 -3
- package/dist/utils/emit.d.ts.map +1 -1
- package/dist/utils/emit.js.map +1 -1
- package/dist/utils/id.cjs +1 -1
- package/dist/utils/id.cjs.map +1 -1
- package/dist/utils/id.d.ts +11 -2
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +1 -1
- package/dist/utils/id.js.map +1 -1
- package/package.json +15 -30
- package/dist/aria.cjs +0 -2
- package/dist/aria.cjs.map +0 -1
- package/dist/aria.d.ts +0 -20
- package/dist/aria.d.ts.map +0 -1
- package/dist/aria.js +0 -2
- package/dist/aria.js.map +0 -1
- package/dist/devtools.cjs +0 -2
- package/dist/devtools.cjs.map +0 -1
- package/dist/devtools.d.ts +0 -32
- package/dist/devtools.d.ts.map +0 -1
- package/dist/devtools.js +0 -2
- package/dist/devtools.js.map +0 -1
- package/dist/directives/index.d.ts +0 -8
- package/dist/directives/index.d.ts.map +0 -1
- package/dist/directives/model.cjs +0 -2
- package/dist/directives/model.cjs.map +0 -1
- package/dist/directives/model.d.ts +0 -28
- package/dist/directives/model.d.ts.map +0 -1
- package/dist/directives/model.js +0 -2
- package/dist/directives/model.js.map +0 -1
- package/dist/directives/raw.cjs +0 -2
- package/dist/directives/raw.cjs.map +0 -1
- package/dist/directives/raw.d.ts +0 -16
- package/dist/directives/raw.d.ts.map +0 -1
- package/dist/directives/raw.js +0 -2
- package/dist/directives/raw.js.map +0 -1
- package/dist/directives.cjs +0 -1
- package/dist/directives.js +0 -1
- package/dist/forms/context.cjs +0 -2
- package/dist/forms/context.cjs.map +0 -1
- package/dist/forms/context.d.ts +0 -56
- package/dist/forms/context.d.ts.map +0 -1
- package/dist/forms/context.js +0 -2
- package/dist/forms/context.js.map +0 -1
- package/dist/forms/index.d.ts +0 -11
- package/dist/forms/index.d.ts.map +0 -1
- package/dist/forms.cjs +0 -1
- package/dist/forms.js +0 -1
- package/dist/observers/index.d.ts +0 -5
- package/dist/observers/index.d.ts.map +0 -1
- package/dist/observers.cjs +0 -1
- package/dist/observers.js +0 -1
- package/dist/template.cjs +0 -1
- package/dist/template.d.ts +0 -4
- package/dist/template.d.ts.map +0 -1
- package/dist/template.js +0 -1
- package/dist/testing/events.cjs +0 -2
- package/dist/testing/events.cjs.map +0 -1
- package/dist/testing/events.d.ts +0 -37
- package/dist/testing/events.d.ts.map +0 -1
- package/dist/testing/events.js +0 -2
- package/dist/testing/events.js.map +0 -1
- package/dist/testing/interactions.cjs +0 -2
- package/dist/testing/interactions.cjs.map +0 -1
- package/dist/testing/interactions.d.ts +0 -27
- package/dist/testing/interactions.d.ts.map +0 -1
- package/dist/testing/interactions.js +0 -2
- package/dist/testing/interactions.js.map +0 -1
- package/dist/testing/query.cjs +0 -2
- package/dist/testing/query.cjs.map +0 -1
- package/dist/testing/query.d.ts +0 -24
- package/dist/testing/query.d.ts.map +0 -1
- package/dist/testing/query.js +0 -2
- package/dist/testing/query.js.map +0 -1
- package/dist/testing/testing.cjs +0 -2
- package/dist/testing/testing.cjs.map +0 -1
- package/dist/testing/testing.d.ts +0 -32
- package/dist/testing/testing.d.ts.map +0 -1
- package/dist/testing/testing.js +0 -2
- package/dist/testing/testing.js.map +0 -1
- package/dist/testing/wait.cjs +0 -2
- package/dist/testing/wait.cjs.map +0 -1
- package/dist/testing/wait.d.ts +0 -35
- package/dist/testing/wait.d.ts.map +0 -1
- package/dist/testing/wait.js +0 -2
- package/dist/testing/wait.js.map +0 -1
- package/dist/types/bindings.cjs +0 -2
- package/dist/types/bindings.cjs.map +0 -1
- package/dist/types/bindings.d.ts +0 -125
- package/dist/types/bindings.d.ts.map +0 -1
- package/dist/types/bindings.js +0 -2
- package/dist/types/bindings.js.map +0 -1
- package/dist/types.cjs +0 -2
- package/dist/types.cjs.map +0 -1
- package/dist/types.d.ts +0 -19
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
- package/dist/utils/event-modifiers.cjs +0 -2
- package/dist/utils/event-modifiers.cjs.map +0 -1
- package/dist/utils/event-modifiers.d.ts +0 -11
- package/dist/utils/event-modifiers.d.ts.map +0 -1
- package/dist/utils/event-modifiers.js +0 -2
- package/dist/utils/event-modifiers.js.map +0 -1
package/dist/define.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("./errors.cjs"),t=require("./
|
|
1
|
+
const e=require("./errors.cjs"),t=require("./props.cjs"),n=require("./_component-class.cjs");function r(t,r){if(!t)throw new e.OreApiError(e.ORE_ERRORS.defineRequiresTag);if(customElements.get(t))throw new e.OreApiError(e.ORE_ERRORS.defineDuplicate(t));let i=n.createComponentClass(t,r);Object.defineProperty(i,"name",{value:t}),customElements.define(t,i)}exports.define=r,exports.prop=t.prop;
|
|
2
2
|
//# sourceMappingURL=define.cjs.map
|
package/dist/define.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define.cjs","names":[],"sources":["../src/define.ts"],"sourcesContent":["import type { ComponentDefinition } from './component-types';\n\nimport {
|
|
1
|
+
{"version":3,"file":"define.cjs","names":[],"sources":["../src/define.ts"],"sourcesContent":["import type { ComponentDefinition } from './component-types';\n\nimport { createComponentClass } from './_component-class';\nimport { OreApiError, ORE_ERRORS } from './errors';\nimport { prop, type PropDef } from './props';\n\nexport { prop };\nexport type { PropDef };\nexport type { HostBindFn } from './host-bind';\n\n/**\n * Define and register a web component.\n *\n * The `setup` function runs for each connection and returns an `HTMLResult`.\n * Disconnecting disposes its state; reconnecting rebuilds it. All reactive\n * behaviour within a connection is expressed through directives inside the\n * template — not by re-evaluating setup itself.\n *\n * Everything besides `props` — lifecycle hooks, host bindings, context, slots,\n * emit — is a free function imported from `@vielzeug/ore` and called directly\n * from inside `setup()` (or from a composable it calls):\n *\n * ```ts\n * import { define, html, prop, onMounted, useEmit, useSlots } from '@vielzeug/ore';\n *\n * define<{ count?: number }>('my-counter', {\n * props: { count: prop.number(0) },\n * setup(props) {\n * const emit = useEmit<{ increment: number }>();\n * const slots = useSlots<'header' | 'footer'>();\n *\n * onMounted(() => console.log('mounted'));\n *\n * return html`<button @click=${() => emit('increment', props.count.value + 1)}>${props.count}</button>`;\n * },\n * });\n * ```\n */\nexport function define<Props extends Record<string, unknown> = Record<never, never>>(\n tag: string,\n definition: ComponentDefinition<Props>,\n): void {\n if (!tag) throw new OreApiError(ORE_ERRORS.defineRequiresTag);\n\n if (customElements.get(tag)) throw new OreApiError(ORE_ERRORS.defineDuplicate(tag));\n\n const ComponentClass = createComponentClass(tag, definition);\n\n // Registration is intentionally the sole global side effect of define().\n Object.defineProperty(ComponentClass, 'name', { value: tag });\n customElements.define(tag, ComponentClass);\n}\n"],"mappings":"6FAsCA,SAAgB,EACd,EACA,EACM,CACN,GAAI,CAAC,EAAK,MAAM,IAAI,EAAA,YAAY,EAAA,WAAW,iBAAiB,EAE5D,GAAI,eAAe,IAAI,CAAG,EAAG,MAAM,IAAI,EAAA,YAAY,EAAA,WAAW,gBAAgB,CAAG,CAAC,EAElF,IAAM,EAAiB,EAAA,qBAAqB,EAAK,CAAU,EAG3D,OAAO,eAAe,EAAgB,OAAQ,CAAE,MAAO,CAAI,CAAC,EAC5D,eAAe,OAAO,EAAK,CAAc,CAC3C"}
|
package/dist/define.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import type { ComponentDefinition } from './component-types';
|
|
2
|
-
import {
|
|
2
|
+
import { prop, type PropDef } from './props';
|
|
3
3
|
export { prop };
|
|
4
|
-
export type {
|
|
4
|
+
export type { PropDef };
|
|
5
5
|
export type { HostBindFn } from './host-bind';
|
|
6
6
|
/**
|
|
7
7
|
* Define and register a web component.
|
|
8
8
|
*
|
|
9
|
-
* The `setup` function runs
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* The `setup` function runs for each connection and returns an `HTMLResult`.
|
|
10
|
+
* Disconnecting disposes its state; reconnecting rebuilds it. All reactive
|
|
11
|
+
* behaviour within a connection is expressed through directives inside the
|
|
12
|
+
* template — not by re-evaluating setup itself.
|
|
12
13
|
*
|
|
13
14
|
* Everything besides `props` — lifecycle hooks, host bindings, context, slots,
|
|
14
15
|
* emit — is a free function imported from `@vielzeug/ore` and called directly
|
package/dist/define.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAI7D,OAAO,
|
|
1
|
+
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAI7D,OAAO,EAAE,IAAI,EAAE,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AAE7C,OAAO,EAAE,IAAI,EAAE,CAAC;AAChB,YAAY,EAAE,OAAO,EAAE,CAAC;AACxB,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,MAAM,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EACjF,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,mBAAmB,CAAC,KAAK,CAAC,GACrC,IAAI,CAUN"}
|
package/dist/define.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ORE_ERRORS as e,OreApiError as t}from"./errors.js";import{
|
|
1
|
+
import{ORE_ERRORS as e,OreApiError as t}from"./errors.js";import{prop as n}from"./props.js";import{createComponentClass as r}from"./_component-class.js";function i(n,i){if(!n)throw new t(e.defineRequiresTag);if(customElements.get(n))throw new t(e.defineDuplicate(n));let a=r(n,i);Object.defineProperty(a,"name",{value:n}),customElements.define(n,a)}export{i as define,n as prop};
|
|
2
2
|
//# sourceMappingURL=define.js.map
|
package/dist/define.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define.js","names":[],"sources":["../src/define.ts"],"sourcesContent":["import type { ComponentDefinition } from './component-types';\n\nimport {
|
|
1
|
+
{"version":3,"file":"define.js","names":[],"sources":["../src/define.ts"],"sourcesContent":["import type { ComponentDefinition } from './component-types';\n\nimport { createComponentClass } from './_component-class';\nimport { OreApiError, ORE_ERRORS } from './errors';\nimport { prop, type PropDef } from './props';\n\nexport { prop };\nexport type { PropDef };\nexport type { HostBindFn } from './host-bind';\n\n/**\n * Define and register a web component.\n *\n * The `setup` function runs for each connection and returns an `HTMLResult`.\n * Disconnecting disposes its state; reconnecting rebuilds it. All reactive\n * behaviour within a connection is expressed through directives inside the\n * template — not by re-evaluating setup itself.\n *\n * Everything besides `props` — lifecycle hooks, host bindings, context, slots,\n * emit — is a free function imported from `@vielzeug/ore` and called directly\n * from inside `setup()` (or from a composable it calls):\n *\n * ```ts\n * import { define, html, prop, onMounted, useEmit, useSlots } from '@vielzeug/ore';\n *\n * define<{ count?: number }>('my-counter', {\n * props: { count: prop.number(0) },\n * setup(props) {\n * const emit = useEmit<{ increment: number }>();\n * const slots = useSlots<'header' | 'footer'>();\n *\n * onMounted(() => console.log('mounted'));\n *\n * return html`<button @click=${() => emit('increment', props.count.value + 1)}>${props.count}</button>`;\n * },\n * });\n * ```\n */\nexport function define<Props extends Record<string, unknown> = Record<never, never>>(\n tag: string,\n definition: ComponentDefinition<Props>,\n): void {\n if (!tag) throw new OreApiError(ORE_ERRORS.defineRequiresTag);\n\n if (customElements.get(tag)) throw new OreApiError(ORE_ERRORS.defineDuplicate(tag));\n\n const ComponentClass = createComponentClass(tag, definition);\n\n // Registration is intentionally the sole global side effect of define().\n Object.defineProperty(ComponentClass, 'name', { value: tag });\n customElements.define(tag, ComponentClass);\n}\n"],"mappings":"yJAsCA,SAAgB,EACd,EACA,EACM,CACN,GAAI,CAAC,EAAK,MAAM,IAAI,EAAY,EAAW,iBAAiB,EAE5D,GAAI,eAAe,IAAI,CAAG,EAAG,MAAM,IAAI,EAAY,EAAW,gBAAgB,CAAG,CAAC,EAElF,IAAM,EAAiB,EAAqB,EAAK,CAAU,EAG3D,OAAO,eAAe,EAAgB,OAAQ,CAAE,MAAO,CAAI,CAAC,EAC5D,eAAe,OAAO,EAAK,CAAc,CAC3C"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
const e=require("../utils/dom.cjs");let t=require("@vielzeug/ripple");var n=n=>(0,t.computed)(()=>Object.entries(n).filter(([,t])=>e.resolveMaybeReactive(t)).map(([e])=>e.replace(/\s+/g,``)).filter(Boolean).join(` `));exports.classMap=n;
|
|
2
2
|
//# sourceMappingURL=classMap.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classMap.cjs","names":[],"sources":["../../src/directives/classMap.ts"],"sourcesContent":["import { computed,
|
|
1
|
+
{"version":3,"file":"classMap.cjs","names":[],"sources":["../../src/directives/classMap.ts"],"sourcesContent":["import { computed, type Readable } from '@vielzeug/ripple';\n\nimport { resolveMaybeReactive } from '../utils/dom';\n\n/**\n * Produces a reactive string of class names from an object map.\n *\n * Each key is a class name. Its value may be:\n * - a static `boolean`\n * - a `Signal<boolean>`\n * - a getter `() => boolean`\n *\n * Returns a `Reactive<string>` that can be used directly in a template\n * class attribute:\n *\n * @example\n * ```ts\n * html`<div class=\"${classMap({ active: isActive, hidden: () => !isVisible.value })}\"></div>`\n * ```\n */\nexport const classMap = (map: Record<string, (() => boolean) | Readable<boolean> | boolean>): Readable<string> => {\n return computed(() =>\n Object.entries(map)\n .filter(([, v]) => resolveMaybeReactive(v))\n // Strip whitespace from each key — spaces would inject extra class tokens.\n .map(([k]) => k.replace(/\\s+/g, ''))\n .filter(Boolean)\n .join(' '),\n );\n};\n"],"mappings":"sEAoBA,IAAa,EAAY,IACvB,EAAO,EAAA,SAAA,KACL,OAAO,QAAQ,CAAG,CAAC,CAChB,QAAQ,EAAG,KAAO,EAAA,qBAAqB,CAAC,CAAC,CAAC,CAE1C,KAAK,CAAC,KAAO,EAAE,QAAQ,OAAQ,EAAE,CAAC,CAAC,CACnC,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,CACb"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classMap.d.ts","sourceRoot":"","sources":["../../src/directives/classMap.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"classMap.d.ts","sourceRoot":"","sources":["../../src/directives/classMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAI3D;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,OAAO,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,KAAG,QAAQ,CAAC,MAAM,CAS5G,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{resolveMaybeReactive as e}from"../utils/dom.js";import{computed as t}from"@vielzeug/ripple";var n=n=>t(()=>Object.entries(n).filter(([,t])=>e(t)).map(([e])=>e.replace(/\s+/g,``)).filter(Boolean).join(` `));export{n as classMap};
|
|
2
2
|
//# sourceMappingURL=classMap.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classMap.js","names":[],"sources":["../../src/directives/classMap.ts"],"sourcesContent":["import { computed,
|
|
1
|
+
{"version":3,"file":"classMap.js","names":[],"sources":["../../src/directives/classMap.ts"],"sourcesContent":["import { computed, type Readable } from '@vielzeug/ripple';\n\nimport { resolveMaybeReactive } from '../utils/dom';\n\n/**\n * Produces a reactive string of class names from an object map.\n *\n * Each key is a class name. Its value may be:\n * - a static `boolean`\n * - a `Signal<boolean>`\n * - a getter `() => boolean`\n *\n * Returns a `Reactive<string>` that can be used directly in a template\n * class attribute:\n *\n * @example\n * ```ts\n * html`<div class=\"${classMap({ active: isActive, hidden: () => !isVisible.value })}\"></div>`\n * ```\n */\nexport const classMap = (map: Record<string, (() => boolean) | Readable<boolean> | boolean>): Readable<string> => {\n return computed(() =>\n Object.entries(map)\n .filter(([, v]) => resolveMaybeReactive(v))\n // Strip whitespace from each key — spaces would inject extra class tokens.\n .map(([k]) => k.replace(/\\s+/g, ''))\n .filter(Boolean)\n .join(' '),\n );\n};\n"],"mappings":"mGAoBA,IAAa,EAAY,GAChB,MACL,OAAO,QAAQ,CAAG,CAAC,CAChB,QAAQ,EAAG,KAAO,EAAqB,CAAC,CAAC,CAAC,CAE1C,KAAK,CAAC,KAAO,EAAE,QAAQ,OAAQ,EAAE,CAAC,CAAC,CACnC,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,CACb"}
|
package/dist/directives/each.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../
|
|
1
|
+
const e=require("../errors.cjs"),t=require("../utils/dom.cjs"),n=require("../template/result.cjs");let r=require("@vielzeug/ripple");var i=(e,t,n,i,a)=>{let o=(0,r.signal)(e),s=(0,r.signal)(t),c=(0,r.createScope)(),l=[],u=[];return c.run(()=>{u=n(o,s).mount(i,a,e=>l.push(e))}),{cleanups:l,data:o,index:s,key:``,nodes:u,scope:c}},a=e=>{e.scope.dispose(),t.runAll(e.cleanups),t.removeNodes(e.nodes)},o=(t,n,o,s,c,l)=>{let u=[],d=new Set;for(let t=0;t<n.length;t++){let r=String(o(n[t],t));if(d.has(r))throw new e.OreApiError(e.ORE_ERRORS.eachDuplicateKey(r,t));d.add(r),u.push(r)}for(let[e,n]of t)d.has(e)||(a(n),t.delete(e));let f=[];for(let e=0;e<n.length;e++){let a=u[e],o=t.get(a);if(o)(0,r.batch)(()=>{o.data.value=n[e],o.index.value=e}),f.push(o);else{let o=(0,r.untrack)(()=>i(n[e],e,s,c,l));o.key=a,t.set(a,o),f.push(o)}}let p=l;for(let e=f.length-1;e>=0;e--){let t=f[e],n=t.nodes[0];if(n&&n!==p.previousSibling)for(let e of t.nodes)c.insertBefore(e,p);p=n??p}return f};function s(i,s,c,l){let u=Array.isArray(i)?(0,r.signal)(i):typeof i==`function`?(0,r.computed)(i):i;return n.createDirectiveResult((n,i)=>{let d=n.parentNode;e.invariant(d,`each() anchor comment has no parent node`);let f=document.createComment(`each/end`);d.insertBefore(f,n.nextSibling);let p=new Map,m=[],h=null,g=[],_=()=>{l&&(h=l().mount(d,f,e=>g.push(e)))},v=()=>{h&&(t.runAll(g),t.removeNodes(h),h=null,g=[])},y=(0,r.effect)(()=>{let t=u.value??[];if(t.length===0){for(let e of(0,r.untrack)(()=>m))a(e);p=new Map,m=[],h||(0,r.untrack)(_);return}v();try{m=(0,r.untrack)(()=>o(p,t,s,c,d,f))}catch(t){let r=t instanceof Error?t:Error(String(t));e.reportRuntimeError(new e.OreLifecycleError(`each() failed to reconcile a list update: ${r.message}`,{cause:r,component:`each()`,phase:`each-reconcile`}),n);for(let e of p.values())a(e);p=new Map,m=[]}});i(()=>y.dispose()),i(()=>{v();for(let e of m)a(e);f.remove()})})}exports.each=s;
|
|
2
2
|
//# sourceMappingURL=each.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"each.cjs","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import { batch, computed, effect as rawEffect, type Readable, signal, type Signal, untrack } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport { invariant, OreApiError, ORE_ERRORS } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult } from '../types/bindings';\nimport { removeNodes, runAll } from '../utils/dom';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype MaybeReactiveArray<T> = Readable<T[]> | (() => T[]) | T[];\n\ntype ItemEntry<T> = {\n cleanups: (() => void)[];\n data: Signal<T>;\n index: Signal<number>;\n /** The key used to identify this entry. */\n key: string;\n nodes: Node[];\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\n const result = render(dataSignal, indexSignal);\n const cleanups: (() => void)[] = [];\n const nodes = result.mount(parent, insertBefore, (fn) => cleanups.push(fn));\n\n return { cleanups, data: dataSignal, index: indexSignal, key: '', nodes };\n};\n\nconst removeItem = <T>(entry: ItemEntry<T>): void => {\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<string, ItemEntry<T>>,\n next: T[],\n keyFn: (item: T, index: number) => string | number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n endMarker: Node,\n): ItemEntry<T>[] => {\n const nextKeys: string[] = [];\n const nextKeySet = new Set<string>();\n\n for (let i = 0; i < next.length; i++) {\n const key = String(keyFn(next[i], i));\n\n if (nextKeySet.has(key)) throw new OreApiError(ORE_ERRORS.eachDuplicateKey(key, i));\n\n nextKeySet.add(key);\n nextKeys.push(key);\n }\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 entry.key = key;\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 // 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\n if (firstNode && firstNode !== 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\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. Duplicate keys within one render throw immediately (see `eachDuplicateKey`).\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<string, 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 clearFallback();\n\n try {\n itemsOrdered = untrack(() => reconcileItems(itemsMap, nextList, keyFn, render, parent, endMarker));\n } catch (err) {\n warn(`each() reconciliation error: ${err instanceof Error ? err.message : String(err)}`);\n\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.cjs","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import {\n batch,\n computed,\n createScope,\n effect as rawEffect,\n type Readable,\n type Scope,\n signal,\n type Signal,\n untrack,\n} from '@vielzeug/ripple';\n\nimport { invariant, OreApiError, ORE_ERRORS, 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[];\n\ntype ItemEntry<T> = {\n cleanups: (() => void)[];\n data: Signal<T>;\n index: Signal<number>;\n /** The key used to identify this entry. */\n key: string;\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, key: '', 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<string, ItemEntry<T>>,\n next: T[],\n keyFn: (item: T, index: number) => string | number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n endMarker: Node,\n): ItemEntry<T>[] => {\n const nextKeys: string[] = [];\n const nextKeySet = new Set<string>();\n\n for (let i = 0; i < next.length; i++) {\n const key = String(keyFn(next[i], i));\n\n if (nextKeySet.has(key)) throw new OreApiError(ORE_ERRORS.eachDuplicateKey(key, i));\n\n nextKeySet.add(key);\n nextKeys.push(key);\n }\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 entry.key = key;\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 // 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\n if (firstNode && firstNode !== 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\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 list is cleared and the failure is reported via the `ore:error` DOM event (see\n * `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<string, 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 clearFallback();\n\n try {\n itemsOrdered = untrack(() => reconcileItems(itemsMap, nextList, keyFn, render, parent, endMarker));\n } catch (err) {\n const cause = err instanceof Error ? err : new Error(String(err));\n\n // Dispatched on the anchor comment (always a live DOM node) rather than the enclosing\n // component's host element, which each() has no direct reference to — the event still\n // bubbles/composes up to any ancestor listener, including a global one on document.\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 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,5 +1,5 @@
|
|
|
1
1
|
import { type Readable } from '@vielzeug/ripple';
|
|
2
|
-
import { type DirectiveResult, type HTMLResult } from '../
|
|
2
|
+
import { type DirectiveResult, type HTMLResult } from '../template/result';
|
|
3
3
|
type MaybeReactiveArray<T> = Readable<T[]> | (() => T[]) | T[];
|
|
4
4
|
/**
|
|
5
5
|
* Renders a keyed list of items as a `DirectiveResult`.
|
|
@@ -24,7 +24,14 @@ type MaybeReactiveArray<T> = Readable<T[]> | (() => T[]) | T[];
|
|
|
24
24
|
* array index — an index-based key reassigns to a different item whenever the
|
|
25
25
|
* list is reordered or an item is inserted/removed before it, causing full
|
|
26
26
|
* item teardown/recreation instead of the in-place update `each()` is built
|
|
27
|
-
* for.
|
|
27
|
+
* for.
|
|
28
|
+
*
|
|
29
|
+
* **Duplicate keys:** a reconciliation failure (e.g. duplicate keys, see `eachDuplicateKey`)
|
|
30
|
+
* does not throw past this function — an uncaught exception inside the reactive effect that
|
|
31
|
+
* drives `each()` would risk corrupting unrelated effects scheduled in the same update batch.
|
|
32
|
+
* Instead the list is cleared and the failure is reported via the `ore:error` DOM event (see
|
|
33
|
+
* `OreLifecycleError`, phase `'each-reconcile'`) plus a dev-only console log — listen for
|
|
34
|
+
* `ore:error` on `document`/`window` to observe this in every build, including production.
|
|
28
35
|
*/
|
|
29
36
|
export declare function each<T>(list: MaybeReactiveArray<T>, keyFn: (item: T, index: number) => string | number, render: (item: Readable<T>, index: Readable<number>) => HTMLResult, fallback?: () => HTMLResult): DirectiveResult;
|
|
30
37
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"each.d.ts","sourceRoot":"","sources":["../../src/directives/each.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"each.d.ts","sourceRoot":"","sources":["../../src/directives/each.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,QAAQ,EAKd,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAyB,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAKlG,KAAK,kBAAkB,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;AAyH/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,IAAI,CAAC,CAAC,EACpB,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC3B,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,EAClD,MAAM,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,UAAU,EAClE,QAAQ,CAAC,EAAE,MAAM,UAAU,GAC1B,eAAe,CAmFjB"}
|
package/dist/directives/each.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{ORE_ERRORS as e,OreApiError as t,OreLifecycleError as n,invariant as r,reportRuntimeError as i}from"../errors.js";import{removeNodes as a,runAll as o}from"../utils/dom.js";import{createDirectiveResult as s}from"../template/result.js";import{batch as c,computed as l,createScope as u,effect as d,signal as f,untrack as p}from"@vielzeug/ripple";var m=(e,t,n,r,i)=>{let a=f(e),o=f(t),s=u(),c=[],l=[];return s.run(()=>{l=n(a,o).mount(r,i,e=>c.push(e))}),{cleanups:c,data:a,index:o,key:``,nodes:l,scope:s}},h=e=>{e.scope.dispose(),o(e.cleanups),a(e.nodes)},g=(n,r,i,a,o,s)=>{let l=[],u=new Set;for(let n=0;n<r.length;n++){let a=String(i(r[n],n));if(u.has(a))throw new t(e.eachDuplicateKey(a,n));u.add(a),l.push(a)}for(let[e,t]of n)u.has(e)||(h(t),n.delete(e));let d=[];for(let e=0;e<r.length;e++){let t=l[e],i=n.get(t);if(i)c(()=>{i.data.value=r[e],i.index.value=e}),d.push(i);else{let i=p(()=>m(r[e],e,a,o,s));i.key=t,n.set(t,i),d.push(i)}}let f=s;for(let e=d.length-1;e>=0;e--){let t=d[e],n=t.nodes[0];if(n&&n!==f.previousSibling)for(let e of t.nodes)o.insertBefore(e,f);f=n??f}return d};function _(e,t,c,u){let m=Array.isArray(e)?f(e):typeof e==`function`?l(e):e;return s((e,s)=>{let l=e.parentNode;r(l,`each() anchor comment has no parent node`);let f=document.createComment(`each/end`);l.insertBefore(f,e.nextSibling);let _=new Map,v=[],y=null,b=[],x=()=>{u&&(y=u().mount(l,f,e=>b.push(e)))},S=()=>{y&&(o(b),a(y),y=null,b=[])},C=d(()=>{let r=m.value??[];if(r.length===0){for(let e of p(()=>v))h(e);_=new Map,v=[],y||p(x);return}S();try{v=p(()=>g(_,r,t,c,l,f))}catch(t){let r=t instanceof Error?t:Error(String(t));i(new n(`each() failed to reconcile a list update: ${r.message}`,{cause:r,component:`each()`,phase:`each-reconcile`}),e);for(let e of _.values())h(e);_=new Map,v=[]}});s(()=>C.dispose()),s(()=>{S();for(let e of v)h(e);f.remove()})})}export{_ as each};
|
|
2
2
|
//# sourceMappingURL=each.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"each.js","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import { batch, computed, effect as rawEffect, type Readable, signal, type Signal, untrack } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\nimport { invariant, OreApiError, ORE_ERRORS } from '../errors';\nimport { createDirectiveResult, type DirectiveResult, type HTMLResult } from '../types/bindings';\nimport { removeNodes, runAll } from '../utils/dom';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype MaybeReactiveArray<T> = Readable<T[]> | (() => T[]) | T[];\n\ntype ItemEntry<T> = {\n cleanups: (() => void)[];\n data: Signal<T>;\n index: Signal<number>;\n /** The key used to identify this entry. */\n key: string;\n nodes: Node[];\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\n const result = render(dataSignal, indexSignal);\n const cleanups: (() => void)[] = [];\n const nodes = result.mount(parent, insertBefore, (fn) => cleanups.push(fn));\n\n return { cleanups, data: dataSignal, index: indexSignal, key: '', nodes };\n};\n\nconst removeItem = <T>(entry: ItemEntry<T>): void => {\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<string, ItemEntry<T>>,\n next: T[],\n keyFn: (item: T, index: number) => string | number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n endMarker: Node,\n): ItemEntry<T>[] => {\n const nextKeys: string[] = [];\n const nextKeySet = new Set<string>();\n\n for (let i = 0; i < next.length; i++) {\n const key = String(keyFn(next[i], i));\n\n if (nextKeySet.has(key)) throw new OreApiError(ORE_ERRORS.eachDuplicateKey(key, i));\n\n nextKeySet.add(key);\n nextKeys.push(key);\n }\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 entry.key = key;\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 // 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\n if (firstNode && firstNode !== 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\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. Duplicate keys within one render throw immediately (see `eachDuplicateKey`).\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<string, 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 clearFallback();\n\n try {\n itemsOrdered = untrack(() => reconcileItems(itemsMap, nextList, keyFn, render, parent, endMarker));\n } catch (err) {\n warn(`each() reconciliation error: ${err instanceof Error ? err.message : String(err)}`);\n\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":"+TAsBA,IAAM,GACJ,EACA,EACA,EACA,EACA,IACiB,CACjB,IAAM,EAAwB,EAAO,CAAI,EACnC,EAA8B,EAAO,CAAK,EAE1C,EAAS,EAAO,EAAY,CAAW,EACvC,EAA2B,CAAC,EAGlC,MAAO,CAAE,WAAU,KAAM,EAAY,MAAO,EAAa,IAAK,GAAI,MAFpD,EAAO,MAAM,EAAQ,EAAe,GAAO,EAAS,KAAK,CAAE,CAEP,CAAM,CAC1E,EAEM,EAAiB,GAA8B,CACnD,EAAO,EAAM,QAAQ,EACrB,EAAY,EAAM,KAAK,CACzB,EAUM,GACJ,EACA,EACA,EACA,EACA,EACA,IACmB,CACnB,IAAM,EAAqB,CAAC,EACtB,EAAa,IAAI,IAEvB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAAK,CACpC,IAAM,EAAM,OAAO,EAAM,EAAK,GAAI,CAAC,CAAC,EAEpC,GAAI,EAAW,IAAI,CAAG,EAAG,MAAM,IAAI,EAAY,EAAW,iBAAiB,EAAK,CAAC,CAAC,EAElF,EAAW,IAAI,CAAG,EAClB,EAAS,KAAK,CAAG,CACnB,CAGA,IAAK,GAAM,CAAC,EAAK,KAAU,EACpB,EAAW,IAAI,CAAG,IACrB,EAAW,CAAK,EAChB,EAAS,OAAO,CAAG,GAKvB,IAAM,EAA8B,CAAC,EAErC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAAK,CACpC,IAAM,EAAM,EAAS,GACf,EAAW,EAAS,IAAI,CAAG,EAEjC,GAAI,EACF,MAAY,CACV,EAAS,KAAK,MAAQ,EAAK,GAC3B,EAAS,MAAM,MAAQ,CACzB,CAAC,EACD,EAAY,KAAK,CAAQ,MACpB,CACL,IAAM,EAAQ,MAAc,EAAW,EAAK,GAAI,EAAG,EAAQ,EAAQ,CAAS,CAAC,EAE7E,EAAM,IAAM,EACZ,EAAS,IAAI,EAAK,CAAK,EACvB,EAAY,KAAK,CAAK,CACxB,CACF,CAKA,IAAI,EAAe,EAEnB,IAAK,IAAI,EAAI,EAAY,OAAS,EAAG,GAAK,EAAG,IAAK,CAChD,IAAM,EAAQ,EAAY,GACpB,EAAY,EAAM,MAAM,GAE9B,GAAI,GAAa,IAAc,EAAO,gBACpC,IAAK,IAAM,KAAQ,EAAM,MAAO,EAAO,aAAa,EAAM,CAAM,EAGlE,EAAS,GAAa,CACxB,CAEA,OAAO,CACT,EA6BA,SAAgB,EACd,EACA,EACA,EACA,EACiB,CACjB,IAAM,EAAa,MAAM,QAAQ,CAAI,EACjC,EAAO,CAAW,EAClB,OAAO,GAAS,WACd,EAAS,CAAiB,EAC1B,EAEN,OAAO,GAAuB,EAAQ,IAAoB,CACxD,IAAM,EAAS,EAAO,WAEtB,EAAU,EAAQ,0CAA0C,EAE5D,IAAM,EAAY,SAAS,cAAc,UAAU,EAEnD,EAAO,aAAa,EAAW,EAAO,WAAW,EAEjD,IAAI,EAAW,IAAI,IACf,EAA+B,CAAC,EAChC,EAA+B,KAC/B,EAAmC,CAAC,EAElC,MAA4B,CAC3B,IAIL,EAFe,EAEC,CAAA,CAAO,MAAM,EAAQ,EAAY,GAAO,EAAiB,KAAK,CAAE,CAAC,EACnF,EAEM,MAA4B,CAC5B,IACF,EAAO,CAAgB,EACvB,EAAY,CAAa,EACzB,EAAgB,KAChB,EAAmB,CAAC,EAExB,EAEM,EAAM,MAAgB,CAC1B,IAAM,EAAW,EAAW,OAAS,CAAC,EAEtC,GAAI,EAAS,SAAW,EAAG,CACzB,IAAK,IAAM,KAAS,MAAc,CAAY,EAAG,EAAW,CAAK,EACjE,EAAW,IAAI,IACf,EAAe,CAAC,EAEX,GAAe,EAAQ,CAAa,EAEzC,MACF,CAEA,EAAc,EAEd,GAAI,CACF,EAAe,MAAc,EAAe,EAAU,EAAU,EAAO,EAAQ,EAAQ,CAAS,CAAC,CACnG,OAAS,EAAK,CACZ,EAAK,gCAAgC,aAAe,MAAQ,EAAI,QAAU,OAAO,CAAG,GAAG,EAEvF,IAAK,IAAM,KAAS,EAAS,OAAO,EAAG,EAAW,CAAK,EACvD,EAAW,IAAI,IACf,EAAe,CAAC,CAClB,CACF,CAAC,EAED,MAAsB,EAAI,QAAQ,CAAC,EACnC,MAAsB,CACpB,EAAc,EACd,IAAK,IAAM,KAAS,EAAc,EAAW,CAAK,EAClD,EAAU,OAAO,CACnB,CAAC,CACH,CAAC,CACH"}
|
|
1
|
+
{"version":3,"file":"each.js","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import {\n batch,\n computed,\n createScope,\n effect as rawEffect,\n type Readable,\n type Scope,\n signal,\n type Signal,\n untrack,\n} from '@vielzeug/ripple';\n\nimport { invariant, OreApiError, ORE_ERRORS, 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[];\n\ntype ItemEntry<T> = {\n cleanups: (() => void)[];\n data: Signal<T>;\n index: Signal<number>;\n /** The key used to identify this entry. */\n key: string;\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, key: '', 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<string, ItemEntry<T>>,\n next: T[],\n keyFn: (item: T, index: number) => string | number,\n render: (item: Readable<T>, index: Readable<number>) => HTMLResult,\n parent: ParentNode,\n endMarker: Node,\n): ItemEntry<T>[] => {\n const nextKeys: string[] = [];\n const nextKeySet = new Set<string>();\n\n for (let i = 0; i < next.length; i++) {\n const key = String(keyFn(next[i], i));\n\n if (nextKeySet.has(key)) throw new OreApiError(ORE_ERRORS.eachDuplicateKey(key, i));\n\n nextKeySet.add(key);\n nextKeys.push(key);\n }\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 entry.key = key;\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 // 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\n if (firstNode && firstNode !== 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\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 list is cleared and the failure is reported via the `ore:error` DOM event (see\n * `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<string, 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 clearFallback();\n\n try {\n itemsOrdered = untrack(() => reconcileItems(itemsMap, nextList, keyFn, render, parent, endMarker));\n } catch (err) {\n const cause = err instanceof Error ? err : new Error(String(err));\n\n // Dispatched on the anchor comment (always a live DOM node) rather than the enclosing\n // component's host element, which each() has no direct reference to — the event still\n // bubbles/composes up to any ancestor listener, including a global one on document.\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 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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"}
|
package/dist/directives/live.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=
|
|
1
|
+
var e=require("../utils/brand.cjs").makeBrand(`ore:live`),t=t=>e.stamp({source:t}),n=e.is;exports.isLiveBinding=n,exports.live=t;
|
|
2
2
|
//# sourceMappingURL=live.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live.cjs","names":[],"sources":["../../src/directives/live.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\
|
|
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,25 +1,23 @@
|
|
|
1
1
|
import type { Readable } from '@vielzeug/ripple';
|
|
2
|
-
declare const LIVE_BRAND: unique symbol;
|
|
3
2
|
/**
|
|
4
|
-
* A
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* A per-binding marker produced by `live()`. Wraps the source signal — the
|
|
4
|
+
* "live" flag belongs to this one binding site, never to the signal itself,
|
|
5
|
+
* so other bindings of the same signal are unaffected.
|
|
7
6
|
*
|
|
8
7
|
* @example
|
|
9
|
-
* html`<input
|
|
8
|
+
* html`<input value="${live(model)}" />`
|
|
10
9
|
*/
|
|
11
|
-
export type
|
|
12
|
-
readonly
|
|
10
|
+
export type LiveBinding<T> = {
|
|
11
|
+
readonly source: Readable<T>;
|
|
13
12
|
};
|
|
14
13
|
/**
|
|
15
|
-
* Marks
|
|
16
|
-
* in-progress user input.
|
|
14
|
+
* Marks one attribute binding as "live" so stale app-state writes never clobber
|
|
15
|
+
* in-progress user input at that binding site.
|
|
17
16
|
*
|
|
18
17
|
* For form controls: if the current DOM value diverges from the last write made
|
|
19
18
|
* by this binding, subsequent app-state writes are silently dropped until the
|
|
20
19
|
* DOM value matches the incoming value or no prior write has been recorded.
|
|
21
20
|
*/
|
|
22
|
-
export declare const live: <T>(source: Readable<T>) =>
|
|
23
|
-
export declare const
|
|
24
|
-
export {};
|
|
21
|
+
export declare const live: <T>(source: Readable<T>) => LiveBinding<T>;
|
|
22
|
+
export declare const isLiveBinding: (value: unknown) => value is LiveBinding<unknown>;
|
|
25
23
|
//# sourceMappingURL=live.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../../src/directives/live.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../../src/directives/live.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAIjD;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAA;CAAE,CAAC;AAI9D;;;;;;;GAOG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,EAAE,QAAQ,QAAQ,CAAC,CAAC,CAAC,KAAG,WAAW,CAAC,CAAC,CAAkD,CAAC;AAE9G,eAAO,MAAM,aAAa,mDAAe,CAAC"}
|
package/dist/directives/live.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import{makeBrand as e}from"../utils/brand.js";var t=e(`ore:live`),n=e=>t.stamp({source:e}),r=t.is;export{r as isLiveBinding,n as live};
|
|
2
2
|
//# sourceMappingURL=live.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live.js","names":[],"sources":["../../src/directives/live.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\
|
|
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,2 +1,2 @@
|
|
|
1
|
-
const e=require("../utils/dom.cjs");let t=require("@vielzeug/ripple");var n=
|
|
1
|
+
const e=require("../utils/dom.cjs");let t=require("@vielzeug/ripple");var n=t=>{let n=e.resolveMaybeReactive(t);return n==null||n===!1?``:e.sanitizeCssToken(String(n))},r=r=>(0,t.computed)(()=>{let t=[];for(let[i,a]of Object.entries(r)){let r=n(a);if(!r)continue;let o=e.sanitizeCssToken(e.toKebab(i));o&&t.push(`${o}:${r}`)}return t.join(`;`)});exports.styleMap=r;
|
|
2
2
|
//# sourceMappingURL=styleMap.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styleMap.cjs","names":[],"sources":["../../src/directives/styleMap.ts"],"sourcesContent":["import { computed,
|
|
1
|
+
{"version":3,"file":"styleMap.cjs","names":[],"sources":["../../src/directives/styleMap.ts"],"sourcesContent":["import { computed, type Readable } from '@vielzeug/ripple';\n\nimport { resolveMaybeReactive, sanitizeCssToken, toKebab } from '../utils/dom';\n\ntype StyleInput =\n | string\n | number\n | null\n | undefined\n | false\n | (() => string | number | null | undefined | false)\n | Readable<string | number | null | undefined | false>;\n\nconst toStyleValue = (value: StyleInput): string => {\n const resolved = resolveMaybeReactive(value);\n\n if (resolved == null || resolved === false) return '';\n\n return sanitizeCssToken(String(resolved));\n};\n\n/**\n * Builds a reactive inline style string from a style object.\n *\n * Takes a record of CSS property names (camelCase) with values that can be:\n * - Static strings/numbers\n * - Functions that return strings/numbers\n * - Signals for reactive updates\n *\n * @example\n * ```ts\n * const color = signal('red');\n * const size = signal(16);\n *\n * html`<div style=${styleMap({\n * backgroundColor: color,\n * width: () => `${size.value}px`,\n * padding: '10px'\n * })}></div>`\n * ```\n */\nexport const styleMap = (record: Record<string, StyleInput>): Readable<string> => {\n return computed(() => {\n const declarations: string[] = [];\n\n for (const [name, input] of Object.entries(record)) {\n const value = toStyleValue(input);\n\n if (!value) continue;\n\n const safeName = sanitizeCssToken(toKebab(name));\n\n if (!safeName) continue;\n\n declarations.push(`${safeName}:${value}`);\n }\n\n return declarations.join(';');\n });\n};\n"],"mappings":"sEAaA,IAAM,EAAgB,GAA8B,CAClD,IAAM,EAAW,EAAA,qBAAqB,CAAK,EAI3C,OAFI,GAAY,MAAQ,IAAa,GAAc,GAE5C,EAAA,iBAAiB,OAAO,CAAQ,CAAC,CAC1C,EAsBa,EAAY,IACvB,EAAO,EAAA,SAAA,KAAe,CACpB,IAAM,EAAyB,CAAC,EAEhC,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAM,EAAG,CAClD,IAAM,EAAQ,EAAa,CAAK,EAEhC,GAAI,CAAC,EAAO,SAEZ,IAAM,EAAW,EAAA,iBAAiB,EAAA,QAAQ,CAAI,CAAC,EAE1C,GAEL,EAAa,KAAK,GAAG,EAAS,GAAG,GAAO,CAC1C,CAEA,OAAO,EAAa,KAAK,GAAG,CAC9B,CAAC"}
|
|
@@ -13,7 +13,7 @@ type StyleInput = string | number | null | undefined | false | (() => string | n
|
|
|
13
13
|
* const color = signal('red');
|
|
14
14
|
* const size = signal(16);
|
|
15
15
|
*
|
|
16
|
-
* html`<div
|
|
16
|
+
* html`<div style=${styleMap({
|
|
17
17
|
* backgroundColor: color,
|
|
18
18
|
* width: () => `${size.value}px`,
|
|
19
19
|
* padding: '10px'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styleMap.d.ts","sourceRoot":"","sources":["../../src/directives/styleMap.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"styleMap.d.ts","sourceRoot":"","sources":["../../src/directives/styleMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAI3D,KAAK,UAAU,GACX,MAAM,GACN,MAAM,GACN,IAAI,GACJ,SAAS,GACT,KAAK,GACL,CAAC,MAAM,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,CAAC,GAClD,QAAQ,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,CAAC,CAAC;AAUzD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,QAAQ,GAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,KAAG,QAAQ,CAAC,MAAM,CAkB5E,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{toKebab as
|
|
1
|
+
import{resolveMaybeReactive as e,sanitizeCssToken as t,toKebab as n}from"../utils/dom.js";import{computed as r}from"@vielzeug/ripple";var i=n=>{let r=e(n);return r==null||r===!1?``:t(String(r))},a=e=>r(()=>{let r=[];for(let[a,o]of Object.entries(e)){let e=i(o);if(!e)continue;let s=t(n(a));s&&r.push(`${s}:${e}`)}return r.join(`;`)});export{a as styleMap};
|
|
2
2
|
//# sourceMappingURL=styleMap.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styleMap.js","names":[],"sources":["../../src/directives/styleMap.ts"],"sourcesContent":["import { computed,
|
|
1
|
+
{"version":3,"file":"styleMap.js","names":[],"sources":["../../src/directives/styleMap.ts"],"sourcesContent":["import { computed, type Readable } from '@vielzeug/ripple';\n\nimport { resolveMaybeReactive, sanitizeCssToken, toKebab } from '../utils/dom';\n\ntype StyleInput =\n | string\n | number\n | null\n | undefined\n | false\n | (() => string | number | null | undefined | false)\n | Readable<string | number | null | undefined | false>;\n\nconst toStyleValue = (value: StyleInput): string => {\n const resolved = resolveMaybeReactive(value);\n\n if (resolved == null || resolved === false) return '';\n\n return sanitizeCssToken(String(resolved));\n};\n\n/**\n * Builds a reactive inline style string from a style object.\n *\n * Takes a record of CSS property names (camelCase) with values that can be:\n * - Static strings/numbers\n * - Functions that return strings/numbers\n * - Signals for reactive updates\n *\n * @example\n * ```ts\n * const color = signal('red');\n * const size = signal(16);\n *\n * html`<div style=${styleMap({\n * backgroundColor: color,\n * width: () => `${size.value}px`,\n * padding: '10px'\n * })}></div>`\n * ```\n */\nexport const styleMap = (record: Record<string, StyleInput>): Readable<string> => {\n return computed(() => {\n const declarations: string[] = [];\n\n for (const [name, input] of Object.entries(record)) {\n const value = toStyleValue(input);\n\n if (!value) continue;\n\n const safeName = sanitizeCssToken(toKebab(name));\n\n if (!safeName) continue;\n\n declarations.push(`${safeName}:${value}`);\n }\n\n return declarations.join(';');\n });\n};\n"],"mappings":"sIAaA,IAAM,EAAgB,GAA8B,CAClD,IAAM,EAAW,EAAqB,CAAK,EAI3C,OAFI,GAAY,MAAQ,IAAa,GAAc,GAE5C,EAAiB,OAAO,CAAQ,CAAC,CAC1C,EAsBa,EAAY,GAChB,MAAe,CACpB,IAAM,EAAyB,CAAC,EAEhC,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAM,EAAG,CAClD,IAAM,EAAQ,EAAa,CAAK,EAEhC,GAAI,CAAC,EAAO,SAEZ,IAAM,EAAW,EAAiB,EAAQ,CAAI,CAAC,EAE1C,GAEL,EAAa,KAAK,GAAG,EAAS,GAAG,GAAO,CAC1C,CAEA,OAAO,EAAa,KAAK,GAAG,CAC9B,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../errors.cjs"),t=require("../utils/dom.cjs"),n=require("../template/result.cjs");let r=require("@vielzeug/ripple");var i=(e,t,n)=>{let r=document.createElement(`template`);r.innerHTML=e;let i=Array.from(r.content.cloneNode(!0).childNodes);for(let e of i)t.insertBefore(e,n);return i};function a(o){if(typeof o==`function`){let e=(0,r.computed)(o);return n.createDirectiveResult((t,n)=>{a(e).mount(t,n)})}return n.createDirectiveResult((n,a)=>{let s=n.parentNode;e.invariant(s,`unsafeHtml() anchor comment has no parent node`);let c=document.createComment(`unsafe-html/end`);if(s.insertBefore(c,n.nextSibling),(0,r.isReactive)(o)){let e=t.createReplaceableSlot(),n=o,l=(0,r.effect)(()=>{e.clear(),e.setNodes(i(n.value,s,c))});a(()=>l.dispose()),a(()=>{e.clear(),c.remove()})}else i(o,s,c),a(()=>c.remove())})}exports.unsafeHtml=a;
|
|
2
|
+
//# sourceMappingURL=unsafe-html.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unsafe-html.cjs","names":[],"sources":["../../src/directives/unsafe-html.ts"],"sourcesContent":["import { computed, effect as rawEffect, isReactive, type Readable, type Signal } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { createDirectiveResult, type DirectiveResult } from '../template/result';\nimport { createReplaceableSlot } from '../utils/dom';\n\nconst parseHtml = (html: string, parent: ParentNode, insertBefore: Node): Node[] => {\n const tpl = document.createElement('template');\n\n tpl.innerHTML = html;\n\n const nodes = Array.from(tpl.content.cloneNode(true).childNodes);\n\n for (const node of nodes) parent.insertBefore(node, insertBefore);\n\n return nodes;\n};\n\n/**\n * Renders HTML without escaping as a DirectiveResult.\n *\n * This is intentionally named `unsafeHtml`: sanitization is an application\n * boundary, not mutable process-wide framework configuration. Sanitize\n * untrusted content before passing it here.\n *\n * Supports static strings, signals, and getter functions `() => string`.\n * When reactive, the DOM is updated in-place whenever the value changes.\n */\nexport function unsafeHtml(value: (() => string) | string | Signal<string> | Readable<string>): DirectiveResult {\n if (typeof value === 'function') {\n const c = computed(value);\n\n return createDirectiveResult((anchor, registerCleanup) => {\n unsafeHtml(c).mount(anchor, registerCleanup);\n });\n }\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const parent = anchor.parentNode;\n\n invariant(parent, 'unsafeHtml() anchor comment has no parent node');\n\n const endMarker = document.createComment('unsafe-html/end');\n\n parent.insertBefore(endMarker, anchor.nextSibling);\n\n if (isReactive(value)) {\n const slot = createReplaceableSlot();\n const src = value as Readable<string>;\n\n const stop = rawEffect(() => {\n slot.clear();\n slot.setNodes(parseHtml(src.value, parent, endMarker));\n });\n\n registerCleanup(() => stop.dispose());\n registerCleanup(() => {\n slot.clear();\n endMarker.remove();\n });\n } else {\n parseHtml(value, parent, endMarker);\n\n registerCleanup(() => endMarker.remove());\n }\n });\n}\n"],"mappings":"qIAMA,IAAM,GAAa,EAAc,EAAoB,IAA+B,CAClF,IAAM,EAAM,SAAS,cAAc,UAAU,EAE7C,EAAI,UAAY,EAEhB,IAAM,EAAQ,MAAM,KAAK,EAAI,QAAQ,UAAU,EAAI,CAAC,CAAC,UAAU,EAE/D,IAAK,IAAM,KAAQ,EAAO,EAAO,aAAa,EAAM,CAAY,EAEhE,OAAO,CACT,EAYA,SAAgB,EAAW,EAAqF,CAC9G,GAAI,OAAO,GAAU,WAAY,CAC/B,IAAM,GAAA,EAAI,EAAA,SAAA,CAAS,CAAK,EAExB,OAAO,EAAA,uBAAuB,EAAQ,IAAoB,CACxD,EAAW,CAAC,CAAC,CAAC,MAAM,EAAQ,CAAe,CAC7C,CAAC,CACH,CAEA,OAAO,EAAA,uBAAuB,EAAQ,IAAoB,CACxD,IAAM,EAAS,EAAO,WAEtB,EAAA,UAAU,EAAQ,gDAAgD,EAElE,IAAM,EAAY,SAAS,cAAc,iBAAiB,EAI1D,GAFA,EAAO,aAAa,EAAW,EAAO,WAAW,GAEjD,EAAI,EAAA,WAAA,CAAW,CAAK,EAAG,CACrB,IAAM,EAAO,EAAA,sBAAsB,EAC7B,EAAM,EAEN,GAAA,EAAO,EAAA,OAAA,KAAgB,CAC3B,EAAK,MAAM,EACX,EAAK,SAAS,EAAU,EAAI,MAAO,EAAQ,CAAS,CAAC,CACvD,CAAC,EAED,MAAsB,EAAK,QAAQ,CAAC,EACpC,MAAsB,CACpB,EAAK,MAAM,EACX,EAAU,OAAO,CACnB,CAAC,CACH,MACE,EAAU,EAAO,EAAQ,CAAS,EAElC,MAAsB,EAAU,OAAO,CAAC,CAE5C,CAAC,CACH"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Readable, type Signal } from '@vielzeug/ripple';
|
|
2
|
+
import { type DirectiveResult } from '../template/result';
|
|
3
|
+
/**
|
|
4
|
+
* Renders HTML without escaping as a DirectiveResult.
|
|
5
|
+
*
|
|
6
|
+
* This is intentionally named `unsafeHtml`: sanitization is an application
|
|
7
|
+
* boundary, not mutable process-wide framework configuration. Sanitize
|
|
8
|
+
* untrusted content before passing it here.
|
|
9
|
+
*
|
|
10
|
+
* Supports static strings, signals, and getter functions `() => string`.
|
|
11
|
+
* When reactive, the DOM is updated in-place whenever the value changes.
|
|
12
|
+
*/
|
|
13
|
+
export declare function unsafeHtml(value: (() => string) | string | Signal<string> | Readable<string>): DirectiveResult;
|
|
14
|
+
//# sourceMappingURL=unsafe-html.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unsafe-html.d.ts","sourceRoot":"","sources":["../../src/directives/unsafe-html.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6C,KAAK,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAGzG,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAejF;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,eAAe,CAsC9G"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{invariant as e}from"../errors.js";import{createReplaceableSlot as t}from"../utils/dom.js";import{createDirectiveResult as n}from"../template/result.js";import{computed as r,effect as i,isReactive as a}from"@vielzeug/ripple";var o=(e,t,n)=>{let r=document.createElement(`template`);r.innerHTML=e;let i=Array.from(r.content.cloneNode(!0).childNodes);for(let e of i)t.insertBefore(e,n);return i};function s(c){if(typeof c==`function`){let e=r(c);return n((t,n)=>{s(e).mount(t,n)})}return n((n,r)=>{let s=n.parentNode;e(s,`unsafeHtml() anchor comment has no parent node`);let l=document.createComment(`unsafe-html/end`);if(s.insertBefore(l,n.nextSibling),a(c)){let e=t(),n=c,a=i(()=>{e.clear(),e.setNodes(o(n.value,s,l))});r(()=>a.dispose()),r(()=>{e.clear(),l.remove()})}else o(c,s,l),r(()=>l.remove())})}export{s as unsafeHtml};
|
|
2
|
+
//# sourceMappingURL=unsafe-html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unsafe-html.js","names":[],"sources":["../../src/directives/unsafe-html.ts"],"sourcesContent":["import { computed, effect as rawEffect, isReactive, type Readable, type Signal } from '@vielzeug/ripple';\n\nimport { invariant } from '../errors';\nimport { createDirectiveResult, type DirectiveResult } from '../template/result';\nimport { createReplaceableSlot } from '../utils/dom';\n\nconst parseHtml = (html: string, parent: ParentNode, insertBefore: Node): Node[] => {\n const tpl = document.createElement('template');\n\n tpl.innerHTML = html;\n\n const nodes = Array.from(tpl.content.cloneNode(true).childNodes);\n\n for (const node of nodes) parent.insertBefore(node, insertBefore);\n\n return nodes;\n};\n\n/**\n * Renders HTML without escaping as a DirectiveResult.\n *\n * This is intentionally named `unsafeHtml`: sanitization is an application\n * boundary, not mutable process-wide framework configuration. Sanitize\n * untrusted content before passing it here.\n *\n * Supports static strings, signals, and getter functions `() => string`.\n * When reactive, the DOM is updated in-place whenever the value changes.\n */\nexport function unsafeHtml(value: (() => string) | string | Signal<string> | Readable<string>): DirectiveResult {\n if (typeof value === 'function') {\n const c = computed(value);\n\n return createDirectiveResult((anchor, registerCleanup) => {\n unsafeHtml(c).mount(anchor, registerCleanup);\n });\n }\n\n return createDirectiveResult((anchor, registerCleanup) => {\n const parent = anchor.parentNode;\n\n invariant(parent, 'unsafeHtml() anchor comment has no parent node');\n\n const endMarker = document.createComment('unsafe-html/end');\n\n parent.insertBefore(endMarker, anchor.nextSibling);\n\n if (isReactive(value)) {\n const slot = createReplaceableSlot();\n const src = value as Readable<string>;\n\n const stop = rawEffect(() => {\n slot.clear();\n slot.setNodes(parseHtml(src.value, parent, endMarker));\n });\n\n registerCleanup(() => stop.dispose());\n registerCleanup(() => {\n slot.clear();\n endMarker.remove();\n });\n } else {\n parseHtml(value, parent, endMarker);\n\n registerCleanup(() => endMarker.remove());\n }\n });\n}\n"],"mappings":"uOAMA,IAAM,GAAa,EAAc,EAAoB,IAA+B,CAClF,IAAM,EAAM,SAAS,cAAc,UAAU,EAE7C,EAAI,UAAY,EAEhB,IAAM,EAAQ,MAAM,KAAK,EAAI,QAAQ,UAAU,EAAI,CAAC,CAAC,UAAU,EAE/D,IAAK,IAAM,KAAQ,EAAO,EAAO,aAAa,EAAM,CAAY,EAEhE,OAAO,CACT,EAYA,SAAgB,EAAW,EAAqF,CAC9G,GAAI,OAAO,GAAU,WAAY,CAC/B,IAAM,EAAI,EAAS,CAAK,EAExB,OAAO,GAAuB,EAAQ,IAAoB,CACxD,EAAW,CAAC,CAAC,CAAC,MAAM,EAAQ,CAAe,CAC7C,CAAC,CACH,CAEA,OAAO,GAAuB,EAAQ,IAAoB,CACxD,IAAM,EAAS,EAAO,WAEtB,EAAU,EAAQ,gDAAgD,EAElE,IAAM,EAAY,SAAS,cAAc,iBAAiB,EAI1D,GAFA,EAAO,aAAa,EAAW,EAAO,WAAW,EAE7C,EAAW,CAAK,EAAG,CACrB,IAAM,EAAO,EAAsB,EAC7B,EAAM,EAEN,EAAO,MAAgB,CAC3B,EAAK,MAAM,EACX,EAAK,SAAS,EAAU,EAAI,MAAO,EAAQ,CAAS,CAAC,CACvD,CAAC,EAED,MAAsB,EAAK,QAAQ,CAAC,EACpC,MAAsB,CACpB,EAAK,MAAM,EACX,EAAU,OAAO,CACnB,CAAC,CACH,MACE,EAAU,EAAO,EAAQ,CAAS,EAElC,MAAsB,EAAU,OAAO,CAAC,CAE5C,CAAC,CACH"}
|
package/dist/directives/when.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../errors.cjs"),t=require("../
|
|
1
|
+
const e=require("../errors.cjs"),t=require("../utils/dom.cjs"),n=require("../template/result.cjs");let r=require("@vielzeug/ripple");var i=`when() anchor comment has no parent node`;function a(a,o,s){return typeof a!=`function`&&!(0,r.isReactive)(a)?n.createDirectiveResult((r,c)=>{let l=a?o():s?s():null;if(!l||!n.isHtmlResult(l))return;let u=r.parentNode;e.invariant(u,i);let d=l.mount(u,r,c);c(()=>t.removeNodes(d))}):n.createDirectiveResult((c,l)=>{let u=(typeof a==`function`?(0,r.computed)(a):null)??a,d=c.parentNode;e.invariant(d,i);let f=document.createComment(`when/end`);d.insertBefore(f,c.nextSibling);let p=t.createReplaceableSlot(),m=(0,r.effect)(()=>{let e=u.value;p.clear();let t=e?o():s?s():null;!t||!n.isHtmlResult(t)||p.setNodes((0,r.untrack)(()=>t.mount(d,f,p.registerCleanup)))});l(()=>m.dispose()),l(()=>{p.clear(),f.remove()})})}exports.when=a;
|
|
2
2
|
//# sourceMappingURL=when.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"when.cjs","names":[],"sources":["../../src/directives/when.ts"],"sourcesContent":["import { computed, effect as rawEffect, isReactive, type Readable, untrack } from '@vielzeug/ripple';\n\nimport
|
|
1
|
+
{"version":3,"file":"when.cjs","names":[],"sources":["../../src/directives/when.ts"],"sourcesContent":["import { computed, effect as rawEffect, isReactive, type Readable, 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 MaybeReactive<T> = T | (() => T) | Readable<T>;\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":"qIAUA,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,KAE/C,CAAC,GAAU,CAAC,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,5 +1,5 @@
|
|
|
1
1
|
import { type Readable } from '@vielzeug/ripple';
|
|
2
|
-
import { type DirectiveResult, type HTMLResult } from '../
|
|
2
|
+
import { type DirectiveResult, type HTMLResult } from '../template/result';
|
|
3
3
|
type MaybeReactive<T> = T | (() => T) | Readable<T>;
|
|
4
4
|
type WhenRenderable = HTMLResult | null | undefined | false;
|
|
5
5
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"when.d.ts","sourceRoot":"","sources":["../../src/directives/when.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6C,KAAK,QAAQ,EAAW,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"when.d.ts","sourceRoot":"","sources":["../../src/directives/when.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6C,KAAK,QAAQ,EAAW,MAAM,kBAAkB,CAAC;AAGrG,OAAO,EAAyB,KAAK,eAAe,EAAE,KAAK,UAAU,EAAgB,MAAM,oBAAoB,CAAC;AAGhH,KAAK,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;AAEpD,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"}
|