@vielzeug/craftit 2.1.0 → 3.0.3
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 +58 -124
- package/dist/controls/a11y-control.cjs +1 -1
- package/dist/controls/a11y-control.cjs.map +1 -1
- package/dist/controls/a11y-control.d.ts +1 -1
- package/dist/controls/a11y-control.d.ts.map +1 -1
- package/dist/controls/a11y-control.js +1 -1
- package/dist/controls/a11y-control.js.map +1 -1
- package/dist/controls/checkable-control.cjs +1 -1
- package/dist/controls/checkable-control.cjs.map +1 -1
- package/dist/controls/checkable-control.d.ts +7 -7
- package/dist/controls/checkable-control.d.ts.map +1 -1
- package/dist/controls/checkable-control.js +1 -1
- package/dist/controls/checkable-control.js.map +1 -1
- package/dist/controls/choice-field-control.cjs +2 -0
- package/dist/controls/choice-field-control.cjs.map +1 -0
- package/dist/controls/choice-field-control.d.ts +3 -0
- package/dist/controls/choice-field-control.d.ts.map +1 -0
- package/dist/controls/choice-field-control.js +2 -0
- package/dist/controls/choice-field-control.js.map +1 -0
- package/dist/controls/field-control.cjs +1 -1
- package/dist/controls/field-control.cjs.map +1 -1
- package/dist/controls/field-control.d.ts +28 -73
- package/dist/controls/field-control.d.ts.map +1 -1
- package/dist/controls/field-control.js +1 -1
- package/dist/controls/field-control.js.map +1 -1
- package/dist/controls/index.d.ts +11 -9
- package/dist/controls/index.d.ts.map +1 -1
- package/dist/controls/internal/control-state.cjs +1 -1
- package/dist/controls/internal/control-state.cjs.map +1 -1
- package/dist/controls/internal/control-state.d.ts +6 -4
- package/dist/controls/internal/control-state.d.ts.map +1 -1
- package/dist/controls/internal/control-state.js +1 -1
- package/dist/controls/internal/control-state.js.map +1 -1
- package/dist/controls/internal/keyboard-utils.cjs.map +1 -1
- package/dist/controls/internal/keyboard-utils.js.map +1 -1
- package/dist/controls/internal/number-utils.cjs.map +1 -1
- package/dist/controls/internal/number-utils.js.map +1 -1
- package/dist/controls/internal/validation-utils.cjs.map +1 -1
- package/dist/controls/internal/validation-utils.js.map +1 -1
- package/dist/controls/list-control.cjs +1 -1
- package/dist/controls/list-control.cjs.map +1 -1
- package/dist/controls/list-control.d.ts +10 -8
- package/dist/controls/list-control.d.ts.map +1 -1
- package/dist/controls/list-control.js +1 -1
- package/dist/controls/list-control.js.map +1 -1
- package/dist/controls/overlay-control.cjs +1 -1
- package/dist/controls/overlay-control.cjs.map +1 -1
- package/dist/controls/overlay-control.d.ts +17 -14
- package/dist/controls/overlay-control.d.ts.map +1 -1
- package/dist/controls/overlay-control.js +1 -1
- package/dist/controls/overlay-control.js.map +1 -1
- package/dist/controls/popup-list-control.cjs +2 -0
- package/dist/controls/popup-list-control.cjs.map +1 -0
- package/dist/controls/popup-list-control.d.ts +160 -0
- package/dist/controls/popup-list-control.d.ts.map +1 -0
- package/dist/controls/popup-list-control.js +2 -0
- package/dist/controls/popup-list-control.js.map +1 -0
- package/dist/controls/press-control.cjs.map +1 -1
- package/dist/controls/press-control.js.map +1 -1
- package/dist/controls/slider-control.cjs.map +1 -1
- package/dist/controls/slider-control.js.map +1 -1
- package/dist/controls/spinner-control.cjs.map +1 -1
- package/dist/controls/spinner-control.js.map +1 -1
- package/dist/controls/swipe-control.cjs +2 -0
- package/dist/controls/swipe-control.cjs.map +1 -0
- package/dist/controls/swipe-control.d.ts +32 -0
- package/dist/controls/swipe-control.d.ts.map +1 -0
- package/dist/controls/swipe-control.js +2 -0
- package/dist/controls/swipe-control.js.map +1 -0
- package/dist/controls/text-field-control.cjs +2 -0
- package/dist/controls/text-field-control.cjs.map +1 -0
- package/dist/controls/text-field-control.d.ts +3 -0
- package/dist/controls/text-field-control.d.ts.map +1 -0
- package/dist/controls/text-field-control.js +2 -0
- package/dist/controls/text-field-control.js.map +1 -0
- package/dist/controls.cjs +1 -1
- package/dist/controls.js +1 -1
- package/dist/craftit.cjs +1 -1
- package/dist/craftit.cjs.map +1 -1
- package/dist/craftit.js +1 -1
- package/dist/craftit.js.map +1 -1
- package/dist/directives/classMap.cjs +2 -0
- package/dist/directives/classMap.cjs.map +1 -0
- package/dist/directives/classMap.d.ts +19 -0
- package/dist/directives/classMap.d.ts.map +1 -0
- package/dist/directives/classMap.js +2 -0
- package/dist/directives/classMap.js.map +1 -0
- package/dist/directives/each.cjs +1 -1
- package/dist/directives/each.cjs.map +1 -1
- package/dist/directives/each.d.ts +5 -30
- 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/guard.cjs +2 -0
- package/dist/directives/guard.cjs.map +1 -0
- package/dist/directives/guard.d.ts +10 -0
- package/dist/directives/guard.d.ts.map +1 -0
- package/dist/directives/guard.js +2 -0
- package/dist/directives/guard.js.map +1 -0
- package/dist/directives/live.cjs +2 -0
- package/dist/directives/live.cjs.map +1 -0
- package/dist/directives/live.d.ts +23 -0
- package/dist/directives/live.d.ts.map +1 -0
- package/dist/directives/live.js +2 -0
- package/dist/directives/live.js.map +1 -0
- package/dist/directives/raw.cjs +1 -1
- package/dist/directives/raw.cjs.map +1 -1
- package/dist/directives/raw.d.ts +3 -5
- package/dist/directives/raw.d.ts.map +1 -1
- package/dist/directives/raw.js +1 -1
- package/dist/directives/raw.js.map +1 -1
- package/dist/directives/resource.cjs +2 -0
- package/dist/directives/resource.cjs.map +1 -0
- package/dist/directives/resource.d.ts +32 -0
- package/dist/directives/resource.d.ts.map +1 -0
- package/dist/directives/resource.js +2 -0
- package/dist/directives/resource.js.map +1 -0
- package/dist/directives/styleMap.cjs +2 -0
- package/dist/directives/styleMap.cjs.map +1 -0
- package/dist/directives/styleMap.d.ts +11 -0
- package/dist/directives/styleMap.d.ts.map +1 -0
- package/dist/directives/styleMap.js +2 -0
- package/dist/directives/styleMap.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 +6 -19
- 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 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +2 -0
- package/dist/errors.js.map +1 -0
- package/dist/form.cjs +1 -1
- package/dist/form.cjs.map +1 -1
- package/dist/form.d.ts +3 -17
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +1 -1
- package/dist/form.js.map +1 -1
- package/dist/host.cjs +1 -1
- package/dist/host.cjs.map +1 -1
- package/dist/host.d.ts +40 -37
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +1 -1
- package/dist/host.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +16 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/internal.cjs +1 -1
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.ts +60 -120
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -1
- package/dist/internal.js.map +1 -1
- package/dist/observers/index.d.ts +1 -0
- package/dist/observers/index.d.ts.map +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 +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 +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 +2 -0
- package/dist/observers/mutation-observe.cjs.map +1 -0
- package/dist/observers/mutation-observe.d.ts +10 -0
- package/dist/observers/mutation-observe.d.ts.map +1 -0
- package/dist/observers/mutation-observe.js +2 -0
- package/dist/observers/mutation-observe.js.map +1 -0
- package/dist/observers/resize-observe.cjs +1 -1
- package/dist/observers/resize-observe.cjs.map +1 -1
- package/dist/observers/resize-observe.d.ts +1 -1
- package/dist/observers/resize-observe.js +1 -1
- package/dist/observers/resize-observe.js.map +1 -1
- package/dist/observers.cjs +1 -1
- package/dist/observers.js +1 -1
- package/dist/props.cjs +1 -1
- package/dist/props.cjs.map +1 -1
- package/dist/props.d.ts +18 -31
- package/dist/props.d.ts.map +1 -1
- package/dist/props.js +1 -1
- package/dist/props.js.map +1 -1
- package/dist/registration.cjs +1 -1
- package/dist/registration.cjs.map +1 -1
- package/dist/registration.d.ts +27 -7
- package/dist/registration.d.ts.map +1 -1
- package/dist/registration.js +1 -1
- package/dist/registration.js.map +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.ts +29 -17
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/template-bindings.cjs +1 -1
- package/dist/template-bindings.cjs.map +1 -1
- package/dist/template-bindings.d.ts +10 -47
- 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 +1 -21
- 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/testing/testing.cjs +1 -1
- package/dist/testing/testing.cjs.map +1 -1
- package/dist/testing/testing.d.ts +12 -5
- package/dist/testing/testing.d.ts.map +1 -1
- package/dist/testing/testing.js +1 -1
- package/dist/testing/testing.js.map +1 -1
- package/package.json +6 -12
- package/dist/component.cjs +0 -2
- package/dist/component.cjs.map +0 -1
- package/dist/component.d.ts +0 -39
- package/dist/component.d.ts.map +0 -1
- package/dist/component.js +0 -2
- package/dist/component.js.map +0 -1
- package/dist/controls/list-key-control.cjs +0 -2
- package/dist/controls/list-key-control.cjs.map +0 -1
- package/dist/controls/list-key-control.d.ts +0 -14
- package/dist/controls/list-key-control.d.ts.map +0 -1
- package/dist/controls/list-key-control.js +0 -2
- package/dist/controls/list-key-control.js.map +0 -1
- package/dist/directives/attr.cjs +0 -2
- package/dist/directives/attr.cjs.map +0 -1
- package/dist/directives/attr.d.ts +0 -12
- package/dist/directives/attr.d.ts.map +0 -1
- package/dist/directives/attr.js +0 -2
- package/dist/directives/attr.js.map +0 -1
- package/dist/directives/bind.cjs +0 -2
- package/dist/directives/bind.cjs.map +0 -1
- package/dist/directives/bind.d.ts +0 -38
- package/dist/directives/bind.d.ts.map +0 -1
- package/dist/directives/bind.js +0 -2
- package/dist/directives/bind.js.map +0 -1
- package/dist/directives/choose.cjs +0 -2
- package/dist/directives/choose.cjs.map +0 -1
- package/dist/directives/choose.d.ts +0 -39
- package/dist/directives/choose.d.ts.map +0 -1
- package/dist/directives/choose.js +0 -2
- package/dist/directives/choose.js.map +0 -1
- package/dist/directives/classes.cjs +0 -2
- package/dist/directives/classes.cjs.map +0 -1
- package/dist/directives/classes.d.ts +0 -20
- package/dist/directives/classes.d.ts.map +0 -1
- package/dist/directives/classes.js +0 -2
- package/dist/directives/classes.js.map +0 -1
- package/dist/directives/index.d.ts +0 -13
- package/dist/directives/index.d.ts.map +0 -1
- package/dist/directives/memo.cjs +0 -2
- package/dist/directives/memo.cjs.map +0 -1
- package/dist/directives/memo.d.ts +0 -27
- package/dist/directives/memo.d.ts.map +0 -1
- package/dist/directives/memo.js +0 -2
- package/dist/directives/memo.js.map +0 -1
- package/dist/directives/on.cjs +0 -2
- package/dist/directives/on.cjs.map +0 -1
- package/dist/directives/on.d.ts +0 -25
- package/dist/directives/on.d.ts.map +0 -1
- package/dist/directives/on.js +0 -2
- package/dist/directives/on.js.map +0 -1
- package/dist/directives/spread.cjs +0 -2
- package/dist/directives/spread.cjs.map +0 -1
- package/dist/directives/spread.d.ts +0 -14
- package/dist/directives/spread.d.ts.map +0 -1
- package/dist/directives/spread.js +0 -2
- package/dist/directives/spread.js.map +0 -1
- package/dist/directives/style.cjs +0 -2
- package/dist/directives/style.cjs.map +0 -1
- package/dist/directives/style.d.ts +0 -22
- package/dist/directives/style.d.ts.map +0 -1
- package/dist/directives/style.js +0 -2
- package/dist/directives/style.js.map +0 -1
- package/dist/directives/until.cjs +0 -2
- package/dist/directives/until.cjs.map +0 -1
- package/dist/directives/until.d.ts +0 -26
- package/dist/directives/until.d.ts.map +0 -1
- package/dist/directives/until.js +0 -2
- package/dist/directives/until.js.map +0 -1
- package/dist/directives.cjs +0 -1
- package/dist/directives.js +0 -1
- package/dist/runtime-bindings.cjs +0 -2
- package/dist/runtime-bindings.cjs.map +0 -1
- package/dist/runtime-bindings.d.ts +0 -6
- package/dist/runtime-bindings.d.ts.map +0 -1
- package/dist/runtime-bindings.js +0 -2
- package/dist/runtime-bindings.js.map +0 -1
- package/dist/runtime-core.cjs +0 -2
- package/dist/runtime-core.cjs.map +0 -1
- package/dist/runtime-core.d.ts +0 -21
- package/dist/runtime-core.d.ts.map +0 -1
- package/dist/runtime-core.js +0 -2
- package/dist/runtime-core.js.map +0 -1
- package/dist/runtime-lifecycle.cjs +0 -2
- package/dist/runtime-lifecycle.cjs.map +0 -1
- package/dist/runtime-lifecycle.d.ts +0 -24
- package/dist/runtime-lifecycle.d.ts.map +0 -1
- package/dist/runtime-lifecycle.js +0 -2
- package/dist/runtime-lifecycle.js.map +0 -1
- package/dist/template-dom.cjs +0 -2
- package/dist/template-dom.cjs.map +0 -1
- package/dist/template-dom.d.ts +0 -13
- package/dist/template-dom.d.ts.map +0 -1
- package/dist/template-dom.js +0 -2
- package/dist/template-dom.js.map +0 -1
- package/dist/template-html.cjs +0 -2
- package/dist/template-html.cjs.map +0 -1
- package/dist/template-html.d.ts +0 -23
- package/dist/template-html.d.ts.map +0 -1
- package/dist/template-html.js +0 -2
- package/dist/template-html.js.map +0 -1
- package/dist/template.cjs +0 -2
- package/dist/template.cjs.map +0 -1
- package/dist/template.d.ts +0 -10
- package/dist/template.d.ts.map +0 -1
- package/dist/template.js +0 -2
- package/dist/template.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classMap.cjs","names":[],"sources":["../../src/directives/classMap.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal } from '@vielzeug/stateit';\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 `ReadonlySignal<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 = (\n map: Record<string, (() => boolean) | ReadonlySignal<boolean> | boolean>,\n): ReadonlySignal<string> => {\n return computed(() =>\n Object.entries(map)\n .filter(([, v]) => (typeof v === 'function' ? v() : isSignal(v) ? v.value : v))\n .map(([k]) => k)\n .join(' '),\n );\n};\n"],"mappings":"mCAkBA,IAAa,EACX,IAEA,EAAA,EAAA,cACE,OAAO,QAAQ,CAAG,EACf,QAAQ,EAAG,KAAQ,OAAO,GAAM,WAAa,EAAE,GAAA,EAAA,EAAA,UAAa,CAAC,EAAI,EAAE,MAAQ,CAAE,EAC7E,KAAK,CAAC,KAAO,CAAC,EACd,KAAK,GAAG,CACb"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ReadonlySignal } from '@vielzeug/stateit';
|
|
2
|
+
/**
|
|
3
|
+
* Produces a reactive string of class names from an object map.
|
|
4
|
+
*
|
|
5
|
+
* Each key is a class name. Its value may be:
|
|
6
|
+
* - a static `boolean`
|
|
7
|
+
* - a `Signal<boolean>`
|
|
8
|
+
* - a getter `() => boolean`
|
|
9
|
+
*
|
|
10
|
+
* Returns a `ReadonlySignal<string>` that can be used directly in a template
|
|
11
|
+
* class attribute:
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* html`<div class="${classMap({ active: isActive, hidden: () => !isVisible.value })}"></div>`
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare const classMap: (map: Record<string, (() => boolean) | ReadonlySignal<boolean> | boolean>) => ReadonlySignal<string>;
|
|
19
|
+
//# sourceMappingURL=classMap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classMap.d.ts","sourceRoot":"","sources":["../../src/directives/classMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE5E;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,QAAQ,GACnB,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,OAAO,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,KACvE,cAAc,CAAC,MAAM,CAOvB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classMap.js","names":[],"sources":["../../src/directives/classMap.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal } from '@vielzeug/stateit';\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 `ReadonlySignal<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 = (\n map: Record<string, (() => boolean) | ReadonlySignal<boolean> | boolean>,\n): ReadonlySignal<string> => {\n return computed(() =>\n Object.entries(map)\n .filter(([, v]) => (typeof v === 'function' ? v() : isSignal(v) ? v.value : v))\n .map(([k]) => k)\n .join(' '),\n );\n};\n"],"mappings":"2DAkBA,IAAa,EACX,GAEO,MACL,OAAO,QAAQ,CAAG,EACf,QAAQ,EAAG,KAAQ,OAAO,GAAM,WAAa,EAAE,EAAI,EAAS,CAAC,EAAI,EAAE,MAAQ,CAAE,EAC7E,KAAK,CAAC,KAAO,CAAC,EACd,KAAK,GAAG,CACb"}
|
package/dist/directives/each.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`../internal.cjs`);let
|
|
1
|
+
const e=require(`../errors.cjs`),t=require(`../internal.cjs`),n=require(`../template-bindings.cjs`);let r=require(`@vielzeug/stateit`);var i=(e,n)=>t.isHtmlResult(e)?t.rekeyHtmlResult(e,n):{bindings:[],html:t.escapeHtml(e)},a=e=>t.isHtmlResult(e)?e:t.htmlResult(t.escapeHtml(e));function o(n,r,a){let o=``,s=[],c=[],l=new Set,u=[],d=t.createMarkerIdFactory();for(let t=0;t<n.length;t++){let f=a(n[t],t);if(l.has(f))throw Error(e.CRAFTIT_ERRORS.eachDuplicateKey(String(f),t));l.add(f),c.push(f);let p=i(r(n[t],t),d);o+=p.html,s.push(...p.bindings),u.push(p)}return{bindings:s,html:o,keys:c,rendered:u}}function s(e,i){let{fallback:s,key:c,render:l}=i;return{__craftitDirective:!0,mount:(i,u)=>{let d=new Map,f=[],p=[],m=()=>{t.removeNodes(f),f=[],t.runAll(p),p=[]},h=()=>{for(let[,e]of d)t.removeNodes(e.nodes),t.runAll(e.cleanups);d.clear()};u((0,r.effect)(()=>{let u=i.parentNode;u&&(0,r.batch)(()=>{let g=e.value;if(!g.length){if(h(),m(),!s)return;let e=t.extractResult(a(s())),r=n.parseHTML(e.html);f=Array.from(r.childNodes),i.after(r);let o=e=>p.push(e);n.applyBindingsWithTargets(e.bindings,o,n.indexBindingTargets(f),{onHtml:e=>n.applyHtmlBinding(u,e,o)});return}m();let{keys:_,rendered:v}=o(g,l,c),y=new Map,b=[];for(let e=0;e<_.length;e++){let r=_[e],i=v[e],a=d.get(r),o,s=[];if(a&&a.html===i.html)o=a.nodes,t.runAll(a.cleanups);else{a&&(t.removeNodes(a.nodes),t.runAll(a.cleanups));let e=n.parseHTML(i.html);o=Array.from(e.childNodes)}b.push({item:i,key:r,nodes:o}),y.set(r,{cleanups:s,html:i.html,nodes:o})}for(let[e,n]of d)y.has(e)||(t.removeNodes(n.nodes),t.runAll(n.cleanups));let x=i;for(let e of b){for(let t of e.nodes)u.insertBefore(t,x.nextSibling),x=t;let t=y.get(e.key);if(!t)continue;let i=e=>t.cleanups.push(e);(0,r.untrack)(()=>{n.applyBindingsWithTargets(e.item.bindings,i,n.indexBindingTargets(e.nodes),{onHtml:e=>n.applyHtmlBinding(u,e,i)})})}d=y})})),u(()=>{m(),h()})}}}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 { computed, isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport {\n CF_ID_ATTR,\n EACH_SIGNAL,\n escapeHtml,\n extractResult,\n htmlResult,\n isHtmlResult,\n type Binding,\n type Directive,\n type HTMLResult,\n} from '../internal';\n\nconst ATTR_ID_RE = new RegExp(`${CF_ID_ATTR}=\"(\\\\d+)\"`, 'g');\n\n/* immutable — shared singleton for empty static lists */\nconst EMPTY = htmlResult('');\nconst NO_BINDINGS: Binding[] = [];\n\nconst toResultEntry = (value: string | HTMLResult, c: { n: number }): { bindings: Binding[]; html: string } =>\n isHtmlResult(value) ? renumber(value, c) : { bindings: NO_BINDINGS, html: escapeHtml(value) };\n\nconst toHtmlResult = (value: string | HTMLResult): HTMLResult =>\n isHtmlResult(value) ? value : htmlResult(escapeHtml(value));\n\nfunction renumber(res: HTMLResult, c: { n: number }): { bindings: Binding[]; html: string } {\n const map = new Map<string, string>();\n const nb: Binding[] = [];\n\n for (const b of res.__bindings) {\n const oldId = b.uid;\n const newId = map.get(oldId) ?? String(c.n++);\n\n if (!map.has(oldId)) map.set(oldId, newId);\n\n nb.push({ ...b, uid: newId });\n }\n\n return {\n bindings: nb,\n html: res.__html\n // Re-map element binding ids.\n .replace(ATTR_ID_RE, (_, id) => `${CF_ID_ATTR}=\"${map.get(id) ?? id}\"`)\n // Re-map numeric comment markers used by text/html placeholders.\n .replace(/<!--(\\d+)-->/g, (_, id) => `<!--${map.get(id) ?? id}-->`),\n };\n}\n\n/** Render loop used by the reactive path (keys + rendered metadata required for reconciliation). */\nfunction renderKeyed<T>(\n items: T[],\n template: (item: T, index: number) => string | HTMLResult,\n keyFn: (item: T, index: number) => string | number,\n): {\n bindings: Binding[];\n html: string;\n keys: (string | number)[];\n rendered: Array<{ bindings: Binding[]; html: string }>;\n} {\n let html = '';\n const allBindings: Binding[] = [];\n const keys: (string | number)[] = [];\n const seenKeys = new Set<string | number>();\n const rendered: Array<{ bindings: Binding[]; html: string }> = [];\n const c = { n: 0 };\n\n for (let i = 0; i < items.length; i++) {\n const nextKey = keyFn(items[i], i);\n\n if (seenKeys.has(nextKey)) {\n throw new Error(`[craftit:each] Duplicate key \"${String(nextKey)}\" at index ${i}.`);\n }\n\n seenKeys.add(nextKey);\n keys.push(nextKey);\n\n const entry = toResultEntry(template(items[i], i), c);\n\n html += entry.html;\n allBindings.push(...entry.bindings);\n rendered.push(entry);\n }\n\n return { bindings: allBindings, html, keys, rendered };\n}\n\n/** Render loop used by the static path — no key/reconciliation metadata needed. */\nfunction renderStatic<T>(\n items: T[],\n template: (item: T, index: number) => string | HTMLResult,\n): { bindings: Binding[]; html: string } {\n let html = '';\n const allBindings: Binding[] = [];\n const c = { n: 0 };\n\n for (let i = 0; i < items.length; i++) {\n const entry = toResultEntry(template(items[i], i), c);\n\n html += entry.html;\n allBindings.push(...entry.bindings);\n }\n\n return { bindings: allBindings, html };\n}\n\ntype ReactiveSource<T> = ReadonlySignal<T[]> | (() => T[]);\ntype EachSignalResult = Directive & {\n [EACH_SIGNAL]: ReadonlySignal<{\n bindings: Binding[];\n html: string;\n items?: Array<{ bindings: Binding[]; html: string }>;\n keys?: (string | number)[];\n }>;\n};\n\nexport interface EachOptions<T> {\n fallback?: () => string | HTMLResult;\n key?: (item: T, index: number) => string | number;\n render: (item: T, index: number) => string | HTMLResult;\n /**\n * Filter predicate. Receives the item and its original array index.\n * Only items for which `select` returns `true` are rendered.\n */\n select?: (item: T, index: number) => boolean;\n}\n\nconst isFunction = (value: unknown): value is (...args: any[]) => any => typeof value === 'function';\n\nconst validateEachOptions = <T>(options: EachOptions<T>): void => {\n if (!isFunction(options.render)) {\n throw new Error('[craftit:each] options.render must be a function.');\n }\n\n if (options.key !== undefined && !isFunction(options.key)) {\n throw new Error('[craftit:each] options.key must be a function when provided.');\n }\n\n if (options.select !== undefined && !isFunction(options.select)) {\n throw new Error('[craftit:each] options.select must be a function when provided.');\n }\n\n if (options.fallback !== undefined && !isFunction(options.fallback)) {\n throw new Error('[craftit:each] options.fallback must be a function when provided.');\n }\n};\n\nconst assertArrayResult = <T>(value: T[] | unknown): T[] => {\n if (Array.isArray(value)) return value as T[];\n\n throw new Error('[craftit:each] source must resolve to an array.');\n};\n\n/**\n * Renders a list with keyed DOM reconciliation for reactive sources.\n * Use inside `html` tagged templates.\n *\n * For reactive sources (Signal/getter), you must provide a stable `key` function.\n *\n * For dynamic lists with click handlers, prefer event delegation on a parent node\n * (`@click` + `closest(...)`) over per-item handlers inside `each()`.\n *\n * @example\n * import { each } from '@vielzeug/craftit/directives';\n *\n * // Static array (key optional):\n * html`${each([1, 2, 3], { render: (item) => html`<li>${item}</li>` })}`\n *\n * // Reactive source (key required):\n * html`${each(items, { key: item => item.id, render: (item) => html`<li>${item.name}</li>` })}`\n *\n * // Full example:\n * html`${each(items, {\n * fallback: () => html`<p>No items</p>`,\n * key: item => item.id,\n * render: (item) => html`<li>${item.name}</li>`,\n * select: item => item.active,\n * })}`\n */\nexport function each<T>(source: T[], options: EachOptions<T>): HTMLResult;\nexport function each<T>(\n source: ReactiveSource<T>,\n options: EachOptions<T> & { key: (item: T, index: number) => string | number },\n): EachSignalResult;\nexport function each<T>(source: T[] | ReactiveSource<T>, options: EachOptions<T>): HTMLResult | EachSignalResult {\n validateEachOptions(options);\n\n const { fallback, key, render, select } = options;\n\n if (Array.isArray(source)) {\n const filtered = select ? source.filter(select) : source;\n\n if (!filtered.length) {\n if (fallback) {\n const er = extractResult(toHtmlResult(fallback()));\n\n return htmlResult(er.html, er.bindings);\n }\n\n return EMPTY;\n }\n\n const { bindings, html } = renderStatic(filtered, render);\n\n return htmlResult(html, bindings);\n }\n\n if (!key) {\n throw new Error('[craftit:each] Reactive each() requires options.key for stable reconciliation.');\n }\n\n const getItems = isSignal(source) ? () => (source as ReadonlySignal<T[]>).value : (source as () => T[]);\n\n return {\n [EACH_SIGNAL]: computed(() => {\n const raw = assertArrayResult<T>(getItems());\n const filtered = select ? raw.filter(select) : raw;\n\n if (!filtered.length) {\n const er = fallback ? toHtmlResult(fallback()) : undefined;\n\n return er ? { ...extractResult(er), items: [], keys: [] } : { bindings: [], html: '', items: [], keys: [] };\n }\n\n const { bindings, html, keys, rendered } = renderKeyed(filtered, render, key);\n\n return { bindings, html, items: rendered, keys };\n }),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"each.cjs","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import { batch, effect as _effect, untrack, type CleanupFn, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport { CRAFTIT_ERRORS } from '../errors';\nimport {\n createMarkerIdFactory,\n escapeHtml,\n extractResult,\n htmlResult,\n isHtmlResult,\n removeNodes,\n rekeyHtmlResult,\n runAll,\n type Binding,\n type DirectiveResult,\n type HTMLResult,\n} from '../internal';\nimport { applyBindingsWithTargets, indexBindingTargets, parseHTML, type RegisterCleanup } from '../template-bindings';\nimport { applyHtmlBinding } from '../template-bindings';\n\nconst toResultEntry = (value: string | HTMLResult, getNextId: () => string): { bindings: Binding[]; html: string } =>\n isHtmlResult(value) ? rekeyHtmlResult(value, getNextId) : { bindings: [], html: escapeHtml(value) };\n\nconst toHtmlResult = (value: string | HTMLResult): HTMLResult =>\n isHtmlResult(value) ? value : htmlResult(escapeHtml(value));\n\n/** Render loop used by the reactive path (keys + rendered metadata required for reconciliation). */\nfunction renderKeyed<T>(\n items: T[],\n template: (item: T, index: number) => string | HTMLResult,\n keyFn: (item: T, index: number) => string | number,\n): {\n bindings: Binding[];\n html: string;\n keys: (string | number)[];\n rendered: Array<{ bindings: Binding[]; html: string }>;\n} {\n let html = '';\n const allBindings: Binding[] = [];\n const keys: (string | number)[] = [];\n const seenKeys = new Set<string | number>();\n const rendered: Array<{ bindings: Binding[]; html: string }> = [];\n const getNextId = createMarkerIdFactory();\n\n for (let i = 0; i < items.length; i++) {\n const nextKey = keyFn(items[i], i);\n\n if (seenKeys.has(nextKey)) {\n throw new Error(CRAFTIT_ERRORS.eachDuplicateKey(String(nextKey), i));\n }\n\n seenKeys.add(nextKey);\n keys.push(nextKey);\n\n const entry = toResultEntry(template(items[i], i), getNextId);\n\n html += entry.html;\n allBindings.push(...entry.bindings);\n rendered.push(entry);\n }\n\n return { bindings: allBindings, html, keys, rendered };\n}\n\nexport interface EachOptions<T> {\n fallback?: () => string | HTMLResult;\n key: (item: T, index: number) => string | number;\n render: (item: T, index: number) => string | HTMLResult;\n}\n\n/**\n * Renders a list with keyed DOM reconciliation for reactive sources.\n * Use inside `html` tagged templates.\n *\n * `each()` expects a reactive signal source and a stable `key` function.\n *\n * For dynamic lists with click handlers, prefer event delegation on a parent node\n * (`@click` + `closest(...)`) over per-item handlers inside `each()`.\n *\n * @example\n * import { each } from '@vielzeug/craftit';\n *\n * // Reactive source (key required):\n * html`${each(items, { key: item => item.id, render: (item) => html`<li>${item.name}</li>` })}`\n *\n * // Full example:\n * html`${each(items, {\n * fallback: () => html`<p>No items</p>`,\n * key: item => item.id,\n * render: (item) => html`<li>${item.name}</li>`,\n * })}`\n */\nexport function each<T>(source: ReadonlySignal<T[]>, options: EachOptions<T>): DirectiveResult;\nexport function each<T>(source: ReadonlySignal<T[]>, options: EachOptions<T>): DirectiveResult {\n const { fallback, key, render: renderItem } = options;\n\n const mount = (anchor: Comment, registerCleanup: RegisterCleanup): void => {\n type KeyedNode = {\n cleanups: CleanupFn[];\n html: string;\n nodes: Node[];\n };\n\n let keyedNodes = new Map<string | number, KeyedNode>();\n let fallbackNodes: Node[] = [];\n let fallbackCleanups: CleanupFn[] = [];\n\n const clearFallback = (): void => {\n removeNodes(fallbackNodes);\n fallbackNodes = [];\n runAll(fallbackCleanups);\n fallbackCleanups = [];\n };\n\n const clearKeyed = (): void => {\n for (const [, node] of keyedNodes) {\n removeNodes(node.nodes);\n runAll(node.cleanups);\n }\n\n keyedNodes.clear();\n };\n\n const stop = _effect(() => {\n const parent = anchor.parentNode;\n\n if (!parent) return;\n\n batch(() => {\n const raw = source.value;\n\n if (!raw.length) {\n clearKeyed();\n clearFallback();\n\n if (!fallback) return;\n\n const fallbackResult = extractResult(toHtmlResult(fallback()));\n const parsed = parseHTML(fallbackResult.html);\n\n fallbackNodes = Array.from(parsed.childNodes);\n anchor.after(parsed);\n\n const addFallbackCleanup: RegisterCleanup = (fn) => fallbackCleanups.push(fn);\n\n applyBindingsWithTargets(fallbackResult.bindings, addFallbackCleanup, indexBindingTargets(fallbackNodes), {\n onHtml: (binding) => applyHtmlBinding(parent as unknown as Node, binding, addFallbackCleanup),\n });\n\n return;\n }\n\n clearFallback();\n\n const { keys, rendered } = renderKeyed(raw, renderItem, key);\n const nextKeyed = new Map<string | number, KeyedNode>();\n const ordered: Array<{ item: { bindings: Binding[]; html: string }; key: string | number; nodes: Node[] }> = [];\n\n for (let i = 0; i < keys.length; i++) {\n const nextKey = keys[i];\n const item = rendered[i];\n const existing = keyedNodes.get(nextKey);\n let nodes: Node[];\n const cleanups: CleanupFn[] = [];\n\n if (existing && existing.html === item.html) {\n nodes = existing.nodes;\n runAll(existing.cleanups);\n } else {\n if (existing) {\n removeNodes(existing.nodes);\n runAll(existing.cleanups);\n }\n\n const parsed = parseHTML(item.html);\n\n nodes = Array.from(parsed.childNodes);\n }\n\n ordered.push({ item, key: nextKey, nodes });\n nextKeyed.set(nextKey, { cleanups, html: item.html, nodes });\n }\n\n for (const [oldKey, oldNode] of keyedNodes) {\n if (!nextKeyed.has(oldKey)) {\n removeNodes(oldNode.nodes);\n runAll(oldNode.cleanups);\n }\n }\n\n let cursor: Node = anchor;\n\n for (const entry of ordered) {\n for (const node of entry.nodes) {\n parent.insertBefore(node, cursor.nextSibling);\n cursor = node;\n }\n\n const targetNode = nextKeyed.get(entry.key);\n\n if (!targetNode) continue;\n\n const addItemCleanup: RegisterCleanup = (fn) => targetNode.cleanups.push(fn);\n\n untrack(() => {\n applyBindingsWithTargets(entry.item.bindings, addItemCleanup, indexBindingTargets(entry.nodes), {\n onHtml: (binding) => applyHtmlBinding(parent as unknown as Node, binding, addItemCleanup),\n });\n });\n }\n\n keyedNodes = nextKeyed;\n });\n });\n\n registerCleanup(stop);\n registerCleanup(() => {\n clearFallback();\n clearKeyed();\n });\n };\n\n return { __craftitDirective: true, mount };\n}\n"],"mappings":"uIAmBA,IAAM,GAAiB,EAA4B,IACjD,EAAA,aAAa,CAAK,EAAI,EAAA,gBAAgB,EAAO,CAAS,EAAI,CAAE,SAAU,CAAC,EAAG,KAAM,EAAA,WAAW,CAAK,CAAE,EAE9F,EAAgB,GACpB,EAAA,aAAa,CAAK,EAAI,EAAQ,EAAA,WAAW,EAAA,WAAW,CAAK,CAAC,EAG5D,SAAS,EACP,EACA,EACA,EAMA,CACA,IAAI,EAAO,GACL,EAAyB,CAAC,EAC1B,EAA4B,CAAC,EAC7B,EAAW,IAAI,IACf,EAAyD,CAAC,EAC1D,EAAY,EAAA,sBAAsB,EAExC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACrC,IAAM,EAAU,EAAM,EAAM,GAAI,CAAC,EAEjC,GAAI,EAAS,IAAI,CAAO,EACtB,MAAU,MAAM,EAAA,eAAe,iBAAiB,OAAO,CAAO,EAAG,CAAC,CAAC,EAGrE,EAAS,IAAI,CAAO,EACpB,EAAK,KAAK,CAAO,EAEjB,IAAM,EAAQ,EAAc,EAAS,EAAM,GAAI,CAAC,EAAG,CAAS,EAE5D,GAAQ,EAAM,KACd,EAAY,KAAK,GAAG,EAAM,QAAQ,EAClC,EAAS,KAAK,CAAK,CACrB,CAEA,MAAO,CAAE,SAAU,EAAa,OAAM,OAAM,UAAS,CACvD,CA+BA,SAAgB,EAAQ,EAA6B,EAA0C,CAC7F,GAAM,CAAE,WAAU,MAAK,OAAQ,GAAe,EAgI9C,MAAO,CAAE,mBAAoB,GAAM,OA9HpB,EAAiB,IAA2C,CAOzE,IAAI,EAAa,IAAI,IACjB,EAAwB,CAAC,EACzB,EAAgC,CAAC,EAE/B,MAA4B,CAChC,EAAA,YAAY,CAAa,EACzB,EAAgB,CAAC,EACjB,EAAA,OAAO,CAAgB,EACvB,EAAmB,CAAC,CACtB,EAEM,MAAyB,CAC7B,IAAK,GAAM,EAAG,KAAS,EACrB,EAAA,YAAY,EAAK,KAAK,EACtB,EAAA,OAAO,EAAK,QAAQ,EAGtB,EAAW,MAAM,CACnB,EA8FA,GAAA,EAAA,EAAA,YA5F2B,CACzB,IAAM,EAAS,EAAO,WAEjB,IAEL,EAAA,EAAA,WAAY,CACV,IAAM,EAAM,EAAO,MAEnB,GAAI,CAAC,EAAI,OAAQ,CAIf,GAHA,EAAW,EACX,EAAc,EAEV,CAAC,EAAU,OAEf,IAAM,EAAiB,EAAA,cAAc,EAAa,EAAS,CAAC,CAAC,EACvD,EAAS,EAAA,UAAU,EAAe,IAAI,EAE5C,EAAgB,MAAM,KAAK,EAAO,UAAU,EAC5C,EAAO,MAAM,CAAM,EAEnB,IAAM,EAAuC,GAAO,EAAiB,KAAK,CAAE,EAE5E,EAAA,yBAAyB,EAAe,SAAU,EAAoB,EAAA,oBAAoB,CAAa,EAAG,CACxG,OAAS,GAAY,EAAA,iBAAiB,EAA2B,EAAS,CAAkB,CAC9F,CAAC,EAED,MACF,CAEA,EAAc,EAEd,GAAM,CAAE,OAAM,YAAa,EAAY,EAAK,EAAY,CAAG,EACrD,EAAY,IAAI,IAChB,EAAuG,CAAC,EAE9G,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,IAAK,CACpC,IAAM,EAAU,EAAK,GACf,EAAO,EAAS,GAChB,EAAW,EAAW,IAAI,CAAO,EACnC,EACE,EAAwB,CAAC,EAE/B,GAAI,GAAY,EAAS,OAAS,EAAK,KACrC,EAAQ,EAAS,MACjB,EAAA,OAAO,EAAS,QAAQ,MACnB,CACD,IACF,EAAA,YAAY,EAAS,KAAK,EAC1B,EAAA,OAAO,EAAS,QAAQ,GAG1B,IAAM,EAAS,EAAA,UAAU,EAAK,IAAI,EAElC,EAAQ,MAAM,KAAK,EAAO,UAAU,CACtC,CAEA,EAAQ,KAAK,CAAE,OAAM,IAAK,EAAS,OAAM,CAAC,EAC1C,EAAU,IAAI,EAAS,CAAE,WAAU,KAAM,EAAK,KAAM,OAAM,CAAC,CAC7D,CAEA,IAAK,GAAM,CAAC,EAAQ,KAAY,EACzB,EAAU,IAAI,CAAM,IACvB,EAAA,YAAY,EAAQ,KAAK,EACzB,EAAA,OAAO,EAAQ,QAAQ,GAI3B,IAAI,EAAe,EAEnB,IAAK,IAAM,KAAS,EAAS,CAC3B,IAAK,IAAM,KAAQ,EAAM,MACvB,EAAO,aAAa,EAAM,EAAO,WAAW,EAC5C,EAAS,EAGX,IAAM,EAAa,EAAU,IAAI,EAAM,GAAG,EAE1C,GAAI,CAAC,EAAY,SAEjB,IAAM,EAAmC,GAAO,EAAW,SAAS,KAAK,CAAE,GAE3E,EAAA,EAAA,aAAc,CACZ,EAAA,yBAAyB,EAAM,KAAK,SAAU,EAAgB,EAAA,oBAAoB,EAAM,KAAK,EAAG,CAC9F,OAAS,GAAY,EAAA,iBAAiB,EAA2B,EAAS,CAAc,CAC1F,CAAC,CACH,CAAC,CACH,CAEA,EAAa,CACf,CAAC,CACH,CAEgB,CAAI,EACpB,MAAsB,CACpB,EAAc,EACd,EAAW,CACb,CAAC,CACH,CAEyC,CAC3C"}
|
|
@@ -1,41 +1,21 @@
|
|
|
1
1
|
import { type ReadonlySignal } from '@vielzeug/stateit';
|
|
2
|
-
import {
|
|
3
|
-
type ReactiveSource<T> = ReadonlySignal<T[]> | (() => T[]);
|
|
4
|
-
type EachSignalResult = Directive & {
|
|
5
|
-
[EACH_SIGNAL]: ReadonlySignal<{
|
|
6
|
-
bindings: Binding[];
|
|
7
|
-
html: string;
|
|
8
|
-
items?: Array<{
|
|
9
|
-
bindings: Binding[];
|
|
10
|
-
html: string;
|
|
11
|
-
}>;
|
|
12
|
-
keys?: (string | number)[];
|
|
13
|
-
}>;
|
|
14
|
-
};
|
|
2
|
+
import { type DirectiveResult, type HTMLResult } from '../internal';
|
|
15
3
|
export interface EachOptions<T> {
|
|
16
4
|
fallback?: () => string | HTMLResult;
|
|
17
|
-
key
|
|
5
|
+
key: (item: T, index: number) => string | number;
|
|
18
6
|
render: (item: T, index: number) => string | HTMLResult;
|
|
19
|
-
/**
|
|
20
|
-
* Filter predicate. Receives the item and its original array index.
|
|
21
|
-
* Only items for which `select` returns `true` are rendered.
|
|
22
|
-
*/
|
|
23
|
-
select?: (item: T, index: number) => boolean;
|
|
24
7
|
}
|
|
25
8
|
/**
|
|
26
9
|
* Renders a list with keyed DOM reconciliation for reactive sources.
|
|
27
10
|
* Use inside `html` tagged templates.
|
|
28
11
|
*
|
|
29
|
-
*
|
|
12
|
+
* `each()` expects a reactive signal source and a stable `key` function.
|
|
30
13
|
*
|
|
31
14
|
* For dynamic lists with click handlers, prefer event delegation on a parent node
|
|
32
15
|
* (`@click` + `closest(...)`) over per-item handlers inside `each()`.
|
|
33
16
|
*
|
|
34
17
|
* @example
|
|
35
|
-
* import { each } from '@vielzeug/craftit
|
|
36
|
-
*
|
|
37
|
-
* // Static array (key optional):
|
|
38
|
-
* html`${each([1, 2, 3], { render: (item) => html`<li>${item}</li>` })}`
|
|
18
|
+
* import { each } from '@vielzeug/craftit';
|
|
39
19
|
*
|
|
40
20
|
* // Reactive source (key required):
|
|
41
21
|
* html`${each(items, { key: item => item.id, render: (item) => html`<li>${item.name}</li>` })}`
|
|
@@ -45,12 +25,7 @@ export interface EachOptions<T> {
|
|
|
45
25
|
* fallback: () => html`<p>No items</p>`,
|
|
46
26
|
* key: item => item.id,
|
|
47
27
|
* render: (item) => html`<li>${item.name}</li>`,
|
|
48
|
-
* select: item => item.active,
|
|
49
28
|
* })}`
|
|
50
29
|
*/
|
|
51
|
-
export declare function each<T>(source: T[]
|
|
52
|
-
export declare function each<T>(source: ReactiveSource<T>, options: EachOptions<T> & {
|
|
53
|
-
key: (item: T, index: number) => string | number;
|
|
54
|
-
}): EachSignalResult;
|
|
55
|
-
export {};
|
|
30
|
+
export declare function each<T>(source: ReadonlySignal<T[]>, options: EachOptions<T>): DirectiveResult;
|
|
56
31
|
//# sourceMappingURL=each.d.ts.map
|
|
@@ -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,EAAqD,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAG3G,OAAO,EAUL,KAAK,eAAe,EACpB,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AAgDrB,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,MAAM,GAAG,UAAU,CAAC;IACrC,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAAC;IACjD,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,UAAU,CAAC;CACzD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC"}
|
package/dist/directives/each.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{CRAFTIT_ERRORS as e}from"../errors.js";import{createMarkerIdFactory as t,escapeHtml as n,extractResult as r,htmlResult as i,isHtmlResult as a,rekeyHtmlResult as o,removeNodes as s,runAll as c}from"../internal.js";import{applyBindingsWithTargets as l,applyHtmlBinding as u,indexBindingTargets as d,parseHTML as f}from"../template-bindings.js";import{batch as p,effect as m,untrack as h}from"@vielzeug/stateit";var g=(e,t)=>a(e)?o(e,t):{bindings:[],html:n(e)},_=e=>a(e)?e:i(n(e));function v(n,r,i){let a=``,o=[],s=[],c=new Set,l=[],u=t();for(let t=0;t<n.length;t++){let d=i(n[t],t);if(c.has(d))throw Error(e.eachDuplicateKey(String(d),t));c.add(d),s.push(d);let f=g(r(n[t],t),u);a+=f.html,o.push(...f.bindings),l.push(f)}return{bindings:o,html:a,keys:s,rendered:l}}function y(e,t){let{fallback:n,key:i,render:a}=t;return{__craftitDirective:!0,mount:(t,o)=>{let g=new Map,y=[],b=[],x=()=>{s(y),y=[],c(b),b=[]},S=()=>{for(let[,e]of g)s(e.nodes),c(e.cleanups);g.clear()};o(m(()=>{let o=t.parentNode;o&&p(()=>{let p=e.value;if(!p.length){if(S(),x(),!n)return;let e=r(_(n())),i=f(e.html);y=Array.from(i.childNodes),t.after(i);let a=e=>b.push(e);l(e.bindings,a,d(y),{onHtml:e=>u(o,e,a)});return}x();let{keys:m,rendered:C}=v(p,a,i),w=new Map,T=[];for(let e=0;e<m.length;e++){let t=m[e],n=C[e],r=g.get(t),i,a=[];if(r&&r.html===n.html)i=r.nodes,c(r.cleanups);else{r&&(s(r.nodes),c(r.cleanups));let e=f(n.html);i=Array.from(e.childNodes)}T.push({item:n,key:t,nodes:i}),w.set(t,{cleanups:a,html:n.html,nodes:i})}for(let[e,t]of g)w.has(e)||(s(t.nodes),c(t.cleanups));let E=t;for(let e of T){for(let t of e.nodes)o.insertBefore(t,E.nextSibling),E=t;let t=w.get(e.key);if(!t)continue;let n=e=>t.cleanups.push(e);h(()=>{l(e.item.bindings,n,d(e.nodes),{onHtml:e=>u(o,e,n)})})}g=w})})),o(()=>{x(),S()})}}}export{y as each};
|
|
2
2
|
//# sourceMappingURL=each.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"each.js","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport {\n CF_ID_ATTR,\n EACH_SIGNAL,\n escapeHtml,\n extractResult,\n htmlResult,\n isHtmlResult,\n type Binding,\n type Directive,\n type HTMLResult,\n} from '../internal';\n\nconst ATTR_ID_RE = new RegExp(`${CF_ID_ATTR}=\"(\\\\d+)\"`, 'g');\n\n/* immutable — shared singleton for empty static lists */\nconst EMPTY = htmlResult('');\nconst NO_BINDINGS: Binding[] = [];\n\nconst toResultEntry = (value: string | HTMLResult, c: { n: number }): { bindings: Binding[]; html: string } =>\n isHtmlResult(value) ? renumber(value, c) : { bindings: NO_BINDINGS, html: escapeHtml(value) };\n\nconst toHtmlResult = (value: string | HTMLResult): HTMLResult =>\n isHtmlResult(value) ? value : htmlResult(escapeHtml(value));\n\nfunction renumber(res: HTMLResult, c: { n: number }): { bindings: Binding[]; html: string } {\n const map = new Map<string, string>();\n const nb: Binding[] = [];\n\n for (const b of res.__bindings) {\n const oldId = b.uid;\n const newId = map.get(oldId) ?? String(c.n++);\n\n if (!map.has(oldId)) map.set(oldId, newId);\n\n nb.push({ ...b, uid: newId });\n }\n\n return {\n bindings: nb,\n html: res.__html\n // Re-map element binding ids.\n .replace(ATTR_ID_RE, (_, id) => `${CF_ID_ATTR}=\"${map.get(id) ?? id}\"`)\n // Re-map numeric comment markers used by text/html placeholders.\n .replace(/<!--(\\d+)-->/g, (_, id) => `<!--${map.get(id) ?? id}-->`),\n };\n}\n\n/** Render loop used by the reactive path (keys + rendered metadata required for reconciliation). */\nfunction renderKeyed<T>(\n items: T[],\n template: (item: T, index: number) => string | HTMLResult,\n keyFn: (item: T, index: number) => string | number,\n): {\n bindings: Binding[];\n html: string;\n keys: (string | number)[];\n rendered: Array<{ bindings: Binding[]; html: string }>;\n} {\n let html = '';\n const allBindings: Binding[] = [];\n const keys: (string | number)[] = [];\n const seenKeys = new Set<string | number>();\n const rendered: Array<{ bindings: Binding[]; html: string }> = [];\n const c = { n: 0 };\n\n for (let i = 0; i < items.length; i++) {\n const nextKey = keyFn(items[i], i);\n\n if (seenKeys.has(nextKey)) {\n throw new Error(`[craftit:each] Duplicate key \"${String(nextKey)}\" at index ${i}.`);\n }\n\n seenKeys.add(nextKey);\n keys.push(nextKey);\n\n const entry = toResultEntry(template(items[i], i), c);\n\n html += entry.html;\n allBindings.push(...entry.bindings);\n rendered.push(entry);\n }\n\n return { bindings: allBindings, html, keys, rendered };\n}\n\n/** Render loop used by the static path — no key/reconciliation metadata needed. */\nfunction renderStatic<T>(\n items: T[],\n template: (item: T, index: number) => string | HTMLResult,\n): { bindings: Binding[]; html: string } {\n let html = '';\n const allBindings: Binding[] = [];\n const c = { n: 0 };\n\n for (let i = 0; i < items.length; i++) {\n const entry = toResultEntry(template(items[i], i), c);\n\n html += entry.html;\n allBindings.push(...entry.bindings);\n }\n\n return { bindings: allBindings, html };\n}\n\ntype ReactiveSource<T> = ReadonlySignal<T[]> | (() => T[]);\ntype EachSignalResult = Directive & {\n [EACH_SIGNAL]: ReadonlySignal<{\n bindings: Binding[];\n html: string;\n items?: Array<{ bindings: Binding[]; html: string }>;\n keys?: (string | number)[];\n }>;\n};\n\nexport interface EachOptions<T> {\n fallback?: () => string | HTMLResult;\n key?: (item: T, index: number) => string | number;\n render: (item: T, index: number) => string | HTMLResult;\n /**\n * Filter predicate. Receives the item and its original array index.\n * Only items for which `select` returns `true` are rendered.\n */\n select?: (item: T, index: number) => boolean;\n}\n\nconst isFunction = (value: unknown): value is (...args: any[]) => any => typeof value === 'function';\n\nconst validateEachOptions = <T>(options: EachOptions<T>): void => {\n if (!isFunction(options.render)) {\n throw new Error('[craftit:each] options.render must be a function.');\n }\n\n if (options.key !== undefined && !isFunction(options.key)) {\n throw new Error('[craftit:each] options.key must be a function when provided.');\n }\n\n if (options.select !== undefined && !isFunction(options.select)) {\n throw new Error('[craftit:each] options.select must be a function when provided.');\n }\n\n if (options.fallback !== undefined && !isFunction(options.fallback)) {\n throw new Error('[craftit:each] options.fallback must be a function when provided.');\n }\n};\n\nconst assertArrayResult = <T>(value: T[] | unknown): T[] => {\n if (Array.isArray(value)) return value as T[];\n\n throw new Error('[craftit:each] source must resolve to an array.');\n};\n\n/**\n * Renders a list with keyed DOM reconciliation for reactive sources.\n * Use inside `html` tagged templates.\n *\n * For reactive sources (Signal/getter), you must provide a stable `key` function.\n *\n * For dynamic lists with click handlers, prefer event delegation on a parent node\n * (`@click` + `closest(...)`) over per-item handlers inside `each()`.\n *\n * @example\n * import { each } from '@vielzeug/craftit/directives';\n *\n * // Static array (key optional):\n * html`${each([1, 2, 3], { render: (item) => html`<li>${item}</li>` })}`\n *\n * // Reactive source (key required):\n * html`${each(items, { key: item => item.id, render: (item) => html`<li>${item.name}</li>` })}`\n *\n * // Full example:\n * html`${each(items, {\n * fallback: () => html`<p>No items</p>`,\n * key: item => item.id,\n * render: (item) => html`<li>${item.name}</li>`,\n * select: item => item.active,\n * })}`\n */\nexport function each<T>(source: T[], options: EachOptions<T>): HTMLResult;\nexport function each<T>(\n source: ReactiveSource<T>,\n options: EachOptions<T> & { key: (item: T, index: number) => string | number },\n): EachSignalResult;\nexport function each<T>(source: T[] | ReactiveSource<T>, options: EachOptions<T>): HTMLResult | EachSignalResult {\n validateEachOptions(options);\n\n const { fallback, key, render, select } = options;\n\n if (Array.isArray(source)) {\n const filtered = select ? source.filter(select) : source;\n\n if (!filtered.length) {\n if (fallback) {\n const er = extractResult(toHtmlResult(fallback()));\n\n return htmlResult(er.html, er.bindings);\n }\n\n return EMPTY;\n }\n\n const { bindings, html } = renderStatic(filtered, render);\n\n return htmlResult(html, bindings);\n }\n\n if (!key) {\n throw new Error('[craftit:each] Reactive each() requires options.key for stable reconciliation.');\n }\n\n const getItems = isSignal(source) ? () => (source as ReadonlySignal<T[]>).value : (source as () => T[]);\n\n return {\n [EACH_SIGNAL]: computed(() => {\n const raw = assertArrayResult<T>(getItems());\n const filtered = select ? raw.filter(select) : raw;\n\n if (!filtered.length) {\n const er = fallback ? toHtmlResult(fallback()) : undefined;\n\n return er ? { ...extractResult(er), items: [], keys: [] } : { bindings: [], html: '', items: [], keys: [] };\n }\n\n const { bindings, html, keys, rendered } = renderKeyed(filtered, render, key);\n\n return { bindings, html, items: rendered, keys };\n }),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"each.js","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import { batch, effect as _effect, untrack, type CleanupFn, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport { CRAFTIT_ERRORS } from '../errors';\nimport {\n createMarkerIdFactory,\n escapeHtml,\n extractResult,\n htmlResult,\n isHtmlResult,\n removeNodes,\n rekeyHtmlResult,\n runAll,\n type Binding,\n type DirectiveResult,\n type HTMLResult,\n} from '../internal';\nimport { applyBindingsWithTargets, indexBindingTargets, parseHTML, type RegisterCleanup } from '../template-bindings';\nimport { applyHtmlBinding } from '../template-bindings';\n\nconst toResultEntry = (value: string | HTMLResult, getNextId: () => string): { bindings: Binding[]; html: string } =>\n isHtmlResult(value) ? rekeyHtmlResult(value, getNextId) : { bindings: [], html: escapeHtml(value) };\n\nconst toHtmlResult = (value: string | HTMLResult): HTMLResult =>\n isHtmlResult(value) ? value : htmlResult(escapeHtml(value));\n\n/** Render loop used by the reactive path (keys + rendered metadata required for reconciliation). */\nfunction renderKeyed<T>(\n items: T[],\n template: (item: T, index: number) => string | HTMLResult,\n keyFn: (item: T, index: number) => string | number,\n): {\n bindings: Binding[];\n html: string;\n keys: (string | number)[];\n rendered: Array<{ bindings: Binding[]; html: string }>;\n} {\n let html = '';\n const allBindings: Binding[] = [];\n const keys: (string | number)[] = [];\n const seenKeys = new Set<string | number>();\n const rendered: Array<{ bindings: Binding[]; html: string }> = [];\n const getNextId = createMarkerIdFactory();\n\n for (let i = 0; i < items.length; i++) {\n const nextKey = keyFn(items[i], i);\n\n if (seenKeys.has(nextKey)) {\n throw new Error(CRAFTIT_ERRORS.eachDuplicateKey(String(nextKey), i));\n }\n\n seenKeys.add(nextKey);\n keys.push(nextKey);\n\n const entry = toResultEntry(template(items[i], i), getNextId);\n\n html += entry.html;\n allBindings.push(...entry.bindings);\n rendered.push(entry);\n }\n\n return { bindings: allBindings, html, keys, rendered };\n}\n\nexport interface EachOptions<T> {\n fallback?: () => string | HTMLResult;\n key: (item: T, index: number) => string | number;\n render: (item: T, index: number) => string | HTMLResult;\n}\n\n/**\n * Renders a list with keyed DOM reconciliation for reactive sources.\n * Use inside `html` tagged templates.\n *\n * `each()` expects a reactive signal source and a stable `key` function.\n *\n * For dynamic lists with click handlers, prefer event delegation on a parent node\n * (`@click` + `closest(...)`) over per-item handlers inside `each()`.\n *\n * @example\n * import { each } from '@vielzeug/craftit';\n *\n * // Reactive source (key required):\n * html`${each(items, { key: item => item.id, render: (item) => html`<li>${item.name}</li>` })}`\n *\n * // Full example:\n * html`${each(items, {\n * fallback: () => html`<p>No items</p>`,\n * key: item => item.id,\n * render: (item) => html`<li>${item.name}</li>`,\n * })}`\n */\nexport function each<T>(source: ReadonlySignal<T[]>, options: EachOptions<T>): DirectiveResult;\nexport function each<T>(source: ReadonlySignal<T[]>, options: EachOptions<T>): DirectiveResult {\n const { fallback, key, render: renderItem } = options;\n\n const mount = (anchor: Comment, registerCleanup: RegisterCleanup): void => {\n type KeyedNode = {\n cleanups: CleanupFn[];\n html: string;\n nodes: Node[];\n };\n\n let keyedNodes = new Map<string | number, KeyedNode>();\n let fallbackNodes: Node[] = [];\n let fallbackCleanups: CleanupFn[] = [];\n\n const clearFallback = (): void => {\n removeNodes(fallbackNodes);\n fallbackNodes = [];\n runAll(fallbackCleanups);\n fallbackCleanups = [];\n };\n\n const clearKeyed = (): void => {\n for (const [, node] of keyedNodes) {\n removeNodes(node.nodes);\n runAll(node.cleanups);\n }\n\n keyedNodes.clear();\n };\n\n const stop = _effect(() => {\n const parent = anchor.parentNode;\n\n if (!parent) return;\n\n batch(() => {\n const raw = source.value;\n\n if (!raw.length) {\n clearKeyed();\n clearFallback();\n\n if (!fallback) return;\n\n const fallbackResult = extractResult(toHtmlResult(fallback()));\n const parsed = parseHTML(fallbackResult.html);\n\n fallbackNodes = Array.from(parsed.childNodes);\n anchor.after(parsed);\n\n const addFallbackCleanup: RegisterCleanup = (fn) => fallbackCleanups.push(fn);\n\n applyBindingsWithTargets(fallbackResult.bindings, addFallbackCleanup, indexBindingTargets(fallbackNodes), {\n onHtml: (binding) => applyHtmlBinding(parent as unknown as Node, binding, addFallbackCleanup),\n });\n\n return;\n }\n\n clearFallback();\n\n const { keys, rendered } = renderKeyed(raw, renderItem, key);\n const nextKeyed = new Map<string | number, KeyedNode>();\n const ordered: Array<{ item: { bindings: Binding[]; html: string }; key: string | number; nodes: Node[] }> = [];\n\n for (let i = 0; i < keys.length; i++) {\n const nextKey = keys[i];\n const item = rendered[i];\n const existing = keyedNodes.get(nextKey);\n let nodes: Node[];\n const cleanups: CleanupFn[] = [];\n\n if (existing && existing.html === item.html) {\n nodes = existing.nodes;\n runAll(existing.cleanups);\n } else {\n if (existing) {\n removeNodes(existing.nodes);\n runAll(existing.cleanups);\n }\n\n const parsed = parseHTML(item.html);\n\n nodes = Array.from(parsed.childNodes);\n }\n\n ordered.push({ item, key: nextKey, nodes });\n nextKeyed.set(nextKey, { cleanups, html: item.html, nodes });\n }\n\n for (const [oldKey, oldNode] of keyedNodes) {\n if (!nextKeyed.has(oldKey)) {\n removeNodes(oldNode.nodes);\n runAll(oldNode.cleanups);\n }\n }\n\n let cursor: Node = anchor;\n\n for (const entry of ordered) {\n for (const node of entry.nodes) {\n parent.insertBefore(node, cursor.nextSibling);\n cursor = node;\n }\n\n const targetNode = nextKeyed.get(entry.key);\n\n if (!targetNode) continue;\n\n const addItemCleanup: RegisterCleanup = (fn) => targetNode.cleanups.push(fn);\n\n untrack(() => {\n applyBindingsWithTargets(entry.item.bindings, addItemCleanup, indexBindingTargets(entry.nodes), {\n onHtml: (binding) => applyHtmlBinding(parent as unknown as Node, binding, addItemCleanup),\n });\n });\n }\n\n keyedNodes = nextKeyed;\n });\n });\n\n registerCleanup(stop);\n registerCleanup(() => {\n clearFallback();\n clearKeyed();\n });\n };\n\n return { __craftitDirective: true, mount };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require(`@vielzeug/stateit`);var t=t=>typeof t==`function`?t():(0,e.isSignal)(t)?t.value:t,n=(e,t)=>{if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(!Object.is(e[n],t[n]))return!1;return!0};function r(r,i){let a=!1,o=[],s=``;return(0,e.computed)(()=>{let c=(Array.isArray(r)?r:[r]).map(e=>t(e));return(!a||!n(o,c))&&(s=(0,e.untrack)(i),o=c,a=!0),s})}exports.guard=r;
|
|
2
|
+
//# sourceMappingURL=guard.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.cjs","names":[],"sources":["../../src/directives/guard.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, untrack } from '@vielzeug/stateit';\n\nimport type { HTMLResult } from '../internal';\n\ntype GuardDep = unknown | (() => unknown) | ReadonlySignal<unknown>;\n\ntype GuardRenderable = string | HTMLResult;\n\nconst resolve = (value: GuardDep): unknown => {\n if (typeof value === 'function') return (value as () => unknown)();\n\n if (isSignal(value)) return value.value;\n\n return value;\n};\n\nconst depsEqual = (prev: unknown[], next: unknown[]): boolean => {\n if (prev.length !== next.length) return false;\n\n for (let index = 0; index < prev.length; index++) {\n if (!Object.is(prev[index], next[index])) return false;\n }\n\n return true;\n};\n\n/**\n * Memoizes rendering until the provided dependency tuple changes.\n */\nexport function guard(deps: GuardDep | GuardDep[], render: () => GuardRenderable): ReadonlySignal<GuardRenderable> {\n let initialized = false;\n let previousDeps: unknown[] = [];\n let cached: GuardRenderable = '';\n\n return computed(() => {\n const resolvedDeps = (Array.isArray(deps) ? deps : [deps]).map((dep) => resolve(dep));\n\n if (!initialized || !depsEqual(previousDeps, resolvedDeps)) {\n // untrack() prevents signals read inside render() from invalidating this\n // computed — only changes to the explicit dep array trigger re-renders.\n cached = untrack(render);\n previousDeps = resolvedDeps;\n initialized = true;\n }\n\n return cached;\n });\n}\n"],"mappings":"mCAQA,IAAM,EAAW,GACX,OAAO,GAAU,WAAoB,EAAwB,GAEjE,EAAA,EAAA,UAAa,CAAK,EAAU,EAAM,MAE3B,EAGH,GAAa,EAAiB,IAA6B,CAC/D,GAAI,EAAK,SAAW,EAAK,OAAQ,MAAO,GAExC,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAK,OAAQ,IACvC,GAAI,CAAC,OAAO,GAAG,EAAK,GAAQ,EAAK,EAAM,EAAG,MAAO,GAGnD,MAAO,EACT,EAKA,SAAgB,EAAM,EAA6B,EAAgE,CACjH,IAAI,EAAc,GACd,EAA0B,CAAC,EAC3B,EAA0B,GAE9B,OAAA,EAAA,EAAA,cAAsB,CACpB,IAAM,GAAgB,MAAM,QAAQ,CAAI,EAAI,EAAO,CAAC,CAAI,GAAG,IAAK,GAAQ,EAAQ,CAAG,CAAC,EAUpF,OARI,CAAC,GAAe,CAAC,EAAU,EAAc,CAAY,KAGvD,GAAA,EAAA,EAAA,SAAiB,CAAM,EACvB,EAAe,EACf,EAAc,IAGT,CACT,CAAC,CACH"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ReadonlySignal } from '@vielzeug/stateit';
|
|
2
|
+
import type { HTMLResult } from '../internal';
|
|
3
|
+
type GuardDep = unknown | (() => unknown) | ReadonlySignal<unknown>;
|
|
4
|
+
type GuardRenderable = string | HTMLResult;
|
|
5
|
+
/**
|
|
6
|
+
* Memoizes rendering until the provided dependency tuple changes.
|
|
7
|
+
*/
|
|
8
|
+
export declare function guard(deps: GuardDep | GuardDep[], render: () => GuardRenderable): ReadonlySignal<GuardRenderable>;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/directives/guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAAW,MAAM,mBAAmB,CAAC;AAErF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,KAAK,QAAQ,GAAG,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;AAEpE,KAAK,eAAe,GAAG,MAAM,GAAG,UAAU,CAAC;AAoB3C;;GAEG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,eAAe,GAAG,cAAc,CAAC,eAAe,CAAC,CAkBjH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{computed as e,isSignal as t,untrack as n}from"@vielzeug/stateit";var r=e=>typeof e==`function`?e():t(e)?e.value:e,i=(e,t)=>{if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(!Object.is(e[n],t[n]))return!1;return!0};function a(t,a){let o=!1,s=[],c=``;return e(()=>{let e=(Array.isArray(t)?t:[t]).map(e=>r(e));return(!o||!i(s,e))&&(c=n(a),s=e,o=!0),c})}export{a as guard};
|
|
2
|
+
//# sourceMappingURL=guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.js","names":[],"sources":["../../src/directives/guard.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, untrack } from '@vielzeug/stateit';\n\nimport type { HTMLResult } from '../internal';\n\ntype GuardDep = unknown | (() => unknown) | ReadonlySignal<unknown>;\n\ntype GuardRenderable = string | HTMLResult;\n\nconst resolve = (value: GuardDep): unknown => {\n if (typeof value === 'function') return (value as () => unknown)();\n\n if (isSignal(value)) return value.value;\n\n return value;\n};\n\nconst depsEqual = (prev: unknown[], next: unknown[]): boolean => {\n if (prev.length !== next.length) return false;\n\n for (let index = 0; index < prev.length; index++) {\n if (!Object.is(prev[index], next[index])) return false;\n }\n\n return true;\n};\n\n/**\n * Memoizes rendering until the provided dependency tuple changes.\n */\nexport function guard(deps: GuardDep | GuardDep[], render: () => GuardRenderable): ReadonlySignal<GuardRenderable> {\n let initialized = false;\n let previousDeps: unknown[] = [];\n let cached: GuardRenderable = '';\n\n return computed(() => {\n const resolvedDeps = (Array.isArray(deps) ? deps : [deps]).map((dep) => resolve(dep));\n\n if (!initialized || !depsEqual(previousDeps, resolvedDeps)) {\n // untrack() prevents signals read inside render() from invalidating this\n // computed — only changes to the explicit dep array trigger re-renders.\n cached = untrack(render);\n previousDeps = resolvedDeps;\n initialized = true;\n }\n\n return cached;\n });\n}\n"],"mappings":"wEAQA,IAAM,EAAW,GACX,OAAO,GAAU,WAAoB,EAAwB,EAE7D,EAAS,CAAK,EAAU,EAAM,MAE3B,EAGH,GAAa,EAAiB,IAA6B,CAC/D,GAAI,EAAK,SAAW,EAAK,OAAQ,MAAO,GAExC,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAK,OAAQ,IACvC,GAAI,CAAC,OAAO,GAAG,EAAK,GAAQ,EAAK,EAAM,EAAG,MAAO,GAGnD,MAAO,EACT,EAKA,SAAgB,EAAM,EAA6B,EAAgE,CACjH,IAAI,EAAc,GACd,EAA0B,CAAC,EAC3B,EAA0B,GAE9B,OAAO,MAAe,CACpB,IAAM,GAAgB,MAAM,QAAQ,CAAI,EAAI,EAAO,CAAC,CAAI,GAAG,IAAK,GAAQ,EAAQ,CAAG,CAAC,EAUpF,OARI,CAAC,GAAe,CAAC,EAAU,EAAc,CAAY,KAGvD,EAAS,EAAQ,CAAM,EACvB,EAAe,EACf,EAAc,IAGT,CACT,CAAC,CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live.cjs","names":[],"sources":["../../src/directives/live.ts"],"sourcesContent":["import type { ReadonlySignal } from '@vielzeug/stateit';\n\n/**\n * A branded signal that tells the attribute binding engine to skip writing when\n * the DOM value has diverged from the last programmatically-written value.\n * Created via live(signal) — pass a signal directly rather than wrapping a value.\n *\n * @example\n * html`<input :value=\"${live(model)}\" />`\n */\nexport type LiveSignal<T> = ReadonlySignal<T> & { readonly __live: true };\n\n/**\n * Marks a signal binding as \"live\" so stale app-state writes never clobber\n * in-progress user input.\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: ReadonlySignal<T>): LiveSignal<T> =>\n ({\n __live: true as const,\n get value(): T {\n return source.value;\n },\n }) as LiveSignal<T>;\n\nexport const isLiveSignal = (value: unknown): value is LiveSignal<unknown> =>\n typeof value === 'object' && value !== null && (value as Record<string, unknown>).__live === true;\n"],"mappings":"AAoBA,IAAa,EAAW,IACrB,CACC,OAAQ,GACR,IAAI,OAAW,CACb,OAAO,EAAO,KAChB,CACF,GAEW,EAAgB,GAC3B,OAAO,GAAU,YAAY,GAAmB,EAAkC,SAAW"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ReadonlySignal } from '@vielzeug/stateit';
|
|
2
|
+
/**
|
|
3
|
+
* A branded signal that tells the attribute binding engine to skip writing when
|
|
4
|
+
* the DOM value has diverged from the last programmatically-written value.
|
|
5
|
+
* Created via live(signal) — pass a signal directly rather than wrapping a value.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* html`<input :value="${live(model)}" />`
|
|
9
|
+
*/
|
|
10
|
+
export type LiveSignal<T> = ReadonlySignal<T> & {
|
|
11
|
+
readonly __live: true;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Marks a signal binding as "live" so stale app-state writes never clobber
|
|
15
|
+
* in-progress user input.
|
|
16
|
+
*
|
|
17
|
+
* For form controls: if the current DOM value diverges from the last write made
|
|
18
|
+
* by this binding, subsequent app-state writes are silently dropped until the
|
|
19
|
+
* DOM value matches the incoming value or no prior write has been recorded.
|
|
20
|
+
*/
|
|
21
|
+
export declare const live: <T>(source: ReadonlySignal<T>) => LiveSignal<T>;
|
|
22
|
+
export declare const isLiveSignal: (value: unknown) => value is LiveSignal<unknown>;
|
|
23
|
+
//# sourceMappingURL=live.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../../src/directives/live.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,CAAC,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAA;CAAE,CAAC;AAE1E;;;;;;;GAOG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,EAAE,QAAQ,cAAc,CAAC,CAAC,CAAC,KAAG,UAAU,CAAC,CAAC,CAM3C,CAAC;AAEtB,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,UAAU,CAAC,OAAO,CAC0B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"live.js","names":[],"sources":["../../src/directives/live.ts"],"sourcesContent":["import type { ReadonlySignal } from '@vielzeug/stateit';\n\n/**\n * A branded signal that tells the attribute binding engine to skip writing when\n * the DOM value has diverged from the last programmatically-written value.\n * Created via live(signal) — pass a signal directly rather than wrapping a value.\n *\n * @example\n * html`<input :value=\"${live(model)}\" />`\n */\nexport type LiveSignal<T> = ReadonlySignal<T> & { readonly __live: true };\n\n/**\n * Marks a signal binding as \"live\" so stale app-state writes never clobber\n * in-progress user input.\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: ReadonlySignal<T>): LiveSignal<T> =>\n ({\n __live: true as const,\n get value(): T {\n return source.value;\n },\n }) as LiveSignal<T>;\n\nexport const isLiveSignal = (value: unknown): value is LiveSignal<unknown> =>\n typeof value === 'object' && value !== null && (value as Record<string, unknown>).__live === true;\n"],"mappings":"AAoBA,IAAa,EAAW,IACrB,CACC,OAAQ,GACR,IAAI,OAAW,CACb,OAAO,EAAO,KAChB,CACF,GAEW,EAAgB,GAC3B,OAAO,GAAU,YAAY,GAAmB,EAAkC,SAAW"}
|
package/dist/directives/raw.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`../internal.cjs`);let t=require(`@vielzeug/stateit`);function n(n){return(0,t.isSignal)(n)?(0,t.computed)(()=>e.htmlResult(n.value)):
|
|
1
|
+
const e=require(`../internal.cjs`);let t=require(`@vielzeug/stateit`);function n(n){return(0,t.isSignal)(n)?(0,t.computed)(()=>e.htmlResult(n.value)):e.htmlResult(n)}exports.raw=n;
|
|
2
2
|
//# sourceMappingURL=raw.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"raw.cjs","names":[],"sources":["../../src/directives/raw.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport { htmlResult, type HTMLResult } from '../internal';\n\n/**\n * Renders a trusted HTML string without escaping.\n * **Only use with content you control** — passing user-supplied strings\n * directly is an XSS risk.\n *\n * Supports static strings
|
|
1
|
+
{"version":3,"file":"raw.cjs","names":[],"sources":["../../src/directives/raw.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport { htmlResult, type HTMLResult } from '../internal';\n\n/**\n * Renders a trusted HTML string without escaping.\n * **Only use with content you control** — passing user-supplied strings\n * directly is an XSS risk.\n *\n * Supports static strings and signals.\n * When reactive, the DOM is updated in-place whenever the value changes.\n *\n * @example\n * import { raw } from '@vielzeug/craftit';\n *\n * // Static\n * html`<div>${raw('<strong>bold</strong>')}</div>`\n *\n * // Reactive signal\n * const content = signal('<em>hello</em>');\n * html`<div>${raw(content)}</div>`\n *\n */\nexport function raw(value: string | Signal<string> | ReadonlySignal<string>): HTMLResult | ReadonlySignal<HTMLResult> {\n if (isSignal(value)) {\n return computed(() => htmlResult((value as ReadonlySignal<string>).value));\n }\n\n return htmlResult(value);\n}\n"],"mappings":"sEAuBA,SAAgB,EAAI,EAAkG,CAKpH,OAJA,EAAA,EAAA,UAAa,CAAK,GAChB,EAAA,EAAA,cAAsB,EAAA,WAAY,EAAiC,KAAK,CAAC,EAGpE,EAAA,WAAW,CAAK,CACzB"}
|
package/dist/directives/raw.d.ts
CHANGED
|
@@ -5,11 +5,11 @@ import { type HTMLResult } from '../internal';
|
|
|
5
5
|
* **Only use with content you control** — passing user-supplied strings
|
|
6
6
|
* directly is an XSS risk.
|
|
7
7
|
*
|
|
8
|
-
* Supports static strings
|
|
8
|
+
* Supports static strings and signals.
|
|
9
9
|
* When reactive, the DOM is updated in-place whenever the value changes.
|
|
10
10
|
*
|
|
11
11
|
* @example
|
|
12
|
-
* import { raw } from '@vielzeug/craftit
|
|
12
|
+
* import { raw } from '@vielzeug/craftit';
|
|
13
13
|
*
|
|
14
14
|
* // Static
|
|
15
15
|
* html`<div>${raw('<strong>bold</strong>')}</div>`
|
|
@@ -18,8 +18,6 @@ import { type HTMLResult } from '../internal';
|
|
|
18
18
|
* const content = signal('<em>hello</em>');
|
|
19
19
|
* html`<div>${raw(content)}</div>`
|
|
20
20
|
*
|
|
21
|
-
* // Getter
|
|
22
|
-
* html`<div>${raw(() => sanitize(props.body.value))}</div>`
|
|
23
21
|
*/
|
|
24
|
-
export declare function raw(value: string | Signal<string> | ReadonlySignal<string>
|
|
22
|
+
export declare function raw(value: string | Signal<string> | ReadonlySignal<string>): HTMLResult | ReadonlySignal<HTMLResult>;
|
|
25
23
|
//# sourceMappingURL=raw.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"raw.d.ts","sourceRoot":"","sources":["../../src/directives/raw.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAAE,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEzF,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1D
|
|
1
|
+
{"version":3,"file":"raw.d.ts","sourceRoot":"","sources":["../../src/directives/raw.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAAE,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEzF,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,GAAG,UAAU,GAAG,cAAc,CAAC,UAAU,CAAC,CAMpH"}
|
package/dist/directives/raw.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{htmlResult as e}from"../internal.js";import{computed as t,isSignal as n}from"@vielzeug/stateit";function r(r){return n(r)?t(()=>e(r.value)):
|
|
1
|
+
import{htmlResult as e}from"../internal.js";import{computed as t,isSignal as n}from"@vielzeug/stateit";function r(r){return n(r)?t(()=>e(r.value)):e(r)}export{r as raw};
|
|
2
2
|
//# sourceMappingURL=raw.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"raw.js","names":[],"sources":["../../src/directives/raw.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport { htmlResult, type HTMLResult } from '../internal';\n\n/**\n * Renders a trusted HTML string without escaping.\n * **Only use with content you control** — passing user-supplied strings\n * directly is an XSS risk.\n *\n * Supports static strings
|
|
1
|
+
{"version":3,"file":"raw.js","names":[],"sources":["../../src/directives/raw.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport { htmlResult, type HTMLResult } from '../internal';\n\n/**\n * Renders a trusted HTML string without escaping.\n * **Only use with content you control** — passing user-supplied strings\n * directly is an XSS risk.\n *\n * Supports static strings and signals.\n * When reactive, the DOM is updated in-place whenever the value changes.\n *\n * @example\n * import { raw } from '@vielzeug/craftit';\n *\n * // Static\n * html`<div>${raw('<strong>bold</strong>')}</div>`\n *\n * // Reactive signal\n * const content = signal('<em>hello</em>');\n * html`<div>${raw(content)}</div>`\n *\n */\nexport function raw(value: string | Signal<string> | ReadonlySignal<string>): HTMLResult | ReadonlySignal<HTMLResult> {\n if (isSignal(value)) {\n return computed(() => htmlResult((value as ReadonlySignal<string>).value));\n }\n\n return htmlResult(value);\n}\n"],"mappings":"uGAuBA,SAAgB,EAAI,EAAkG,CAKpH,OAJI,EAAS,CAAK,EACT,MAAe,EAAY,EAAiC,KAAK,CAAC,EAGpE,EAAW,CAAK,CACzB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`../errors.cjs`),t=require(`../runtime.cjs`);let n=require(`@vielzeug/stateit`);function r(r,i){let a=(0,n.signal)({data:void 0,error:void 0,pending:!0}),o;if(!t.tryRegisterCleanup((0,n.effect)(()=>{let e=r(),t=new AbortController;return a.value={data:o,error:void 0,pending:!0},i(e,t.signal).then(e=>{t.signal.aborted||(o=e,a.value={data:e,error:void 0,pending:!1})}).catch(e=>{t.signal.aborted||(a.value={data:o,error:e,pending:!1})}),()=>t.abort()})))throw Error(e.CRAFTIT_ERRORS.lifecycleOutsideSetup);return a}exports.resource=r;
|
|
2
|
+
//# sourceMappingURL=resource.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.cjs","names":[],"sources":["../../src/directives/resource.ts"],"sourcesContent":["import { effect as signalEffect, type Signal, signal } from '@vielzeug/stateit';\n\nimport { CRAFTIT_ERRORS } from '../errors';\nimport { tryRegisterCleanup } from '../runtime';\n\nexport type ResourceState<T> = {\n /** Previous successful data, available while a reload is pending. */\n data: T | undefined;\n /** Rejection reason from the most recent failed fetch, or `undefined`. */\n error: unknown;\n /** `true` while the fetcher promise is in flight. */\n pending: boolean;\n};\n\n/**\n * Reactive async resource. Re-fetches whenever the `deps` getter returns a new value.\n * Previous data is preserved during reloads so the UI can show stale-while-revalidate UX.\n * In-flight requests are cancelled via `AbortController` when deps change.\n *\n * Must be called inside component `setup()` or another Craftit runtime scope.\n *\n * @example\n * ```ts\n * const user = resource(\n * () => props.userId.value,\n * (id, signal) => fetch(`/api/users/${id}`, { signal }).then(r => r.json()),\n * );\n *\n * return () => html`\n * ${when(() => user.value.pending, () => html`<p>Loading…</p>`)}\n * ${when(() => !!user.value.error, () => html`<p>Error</p>`)}\n * ${when(() => !!user.value.data, () => html`<p>${user.value.data?.name}</p>`)}\n * `;\n * ```\n */\nexport function resource<Deps, T>(deps: () => Deps, fetcher: (deps: Deps, signal: AbortSignal) => Promise<T>) {\n const state: Signal<ResourceState<T>> = signal({ data: undefined, error: undefined, pending: true });\n let previousData: T | undefined;\n\n // Use raw stateit effect and register cleanup explicitly.\n const dispose = signalEffect(() => {\n const currentDeps = deps();\n const controller = new AbortController();\n\n state.value = { data: previousData, error: undefined, pending: true };\n\n fetcher(currentDeps, controller.signal)\n .then((data) => {\n if (!controller.signal.aborted) {\n previousData = data;\n state.value = { data, error: undefined, pending: false };\n }\n })\n .catch((error: unknown) => {\n if (!controller.signal.aborted) {\n state.value = { data: previousData, error, pending: false };\n }\n });\n\n return () => controller.abort();\n });\n\n if (!tryRegisterCleanup(dispose)) throw new Error(CRAFTIT_ERRORS.lifecycleOutsideSetup);\n\n return state;\n}\n"],"mappings":"gGAmCA,SAAgB,EAAkB,EAAkB,EAA0D,CAC5G,IAAM,GAAA,EAAA,EAAA,QAAyC,CAAE,KAAM,IAAA,GAAW,MAAO,IAAA,GAAW,QAAS,EAAK,CAAC,EAC/F,EAyBJ,GAAI,CAAC,EAAA,oBAAA,EAAA,EAAA,YAtB8B,CACjC,IAAM,EAAc,EAAK,EACnB,EAAa,IAAI,gBAiBvB,MAfA,GAAM,MAAQ,CAAE,KAAM,EAAc,MAAO,IAAA,GAAW,QAAS,EAAK,EAEpE,EAAQ,EAAa,EAAW,MAAM,EACnC,KAAM,GAAS,CACT,EAAW,OAAO,UACrB,EAAe,EACf,EAAM,MAAQ,CAAE,OAAM,MAAO,IAAA,GAAW,QAAS,EAAM,EAE3D,CAAC,EACA,MAAO,GAAmB,CACpB,EAAW,OAAO,UACrB,EAAM,MAAQ,CAAE,KAAM,EAAc,QAAO,QAAS,EAAM,EAE9D,CAAC,MAEU,EAAW,MAAM,CAChC,CAEwB,CAAO,EAAG,MAAU,MAAM,EAAA,eAAe,qBAAqB,EAEtF,OAAO,CACT"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Signal } from '@vielzeug/stateit';
|
|
2
|
+
export type ResourceState<T> = {
|
|
3
|
+
/** Previous successful data, available while a reload is pending. */
|
|
4
|
+
data: T | undefined;
|
|
5
|
+
/** Rejection reason from the most recent failed fetch, or `undefined`. */
|
|
6
|
+
error: unknown;
|
|
7
|
+
/** `true` while the fetcher promise is in flight. */
|
|
8
|
+
pending: boolean;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Reactive async resource. Re-fetches whenever the `deps` getter returns a new value.
|
|
12
|
+
* Previous data is preserved during reloads so the UI can show stale-while-revalidate UX.
|
|
13
|
+
* In-flight requests are cancelled via `AbortController` when deps change.
|
|
14
|
+
*
|
|
15
|
+
* Must be called inside component `setup()` or another Craftit runtime scope.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const user = resource(
|
|
20
|
+
* () => props.userId.value,
|
|
21
|
+
* (id, signal) => fetch(`/api/users/${id}`, { signal }).then(r => r.json()),
|
|
22
|
+
* );
|
|
23
|
+
*
|
|
24
|
+
* return () => html`
|
|
25
|
+
* ${when(() => user.value.pending, () => html`<p>Loading…</p>`)}
|
|
26
|
+
* ${when(() => !!user.value.error, () => html`<p>Error</p>`)}
|
|
27
|
+
* ${when(() => !!user.value.data, () => html`<p>${user.value.data?.name}</p>`)}
|
|
28
|
+
* `;
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export declare function resource<Deps, T>(deps: () => Deps, fetcher: (deps: Deps, signal: AbortSignal) => Promise<T>): Signal<ResourceState<T>>;
|
|
32
|
+
//# sourceMappingURL=resource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/directives/resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,MAAM,EAAU,MAAM,mBAAmB,CAAC;AAKhF,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;IAC7B,qEAAqE;IACrE,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;IACpB,0EAA0E;IAC1E,KAAK,EAAE,OAAO,CAAC;IACf,qDAAqD;IACrD,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,4BA8B3G"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{CRAFTIT_ERRORS as e}from"../errors.js";import{tryRegisterCleanup as t}from"../runtime.js";import{effect as n,signal as r}from"@vielzeug/stateit";function i(i,a){let o=r({data:void 0,error:void 0,pending:!0}),s;if(!t(n(()=>{let e=i(),t=new AbortController;return o.value={data:s,error:void 0,pending:!0},a(e,t.signal).then(e=>{t.signal.aborted||(s=e,o.value={data:e,error:void 0,pending:!1})}).catch(e=>{t.signal.aborted||(o.value={data:s,error:e,pending:!1})}),()=>t.abort()})))throw Error(e.lifecycleOutsideSetup);return o}export{i as resource};
|
|
2
|
+
//# sourceMappingURL=resource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.js","names":[],"sources":["../../src/directives/resource.ts"],"sourcesContent":["import { effect as signalEffect, type Signal, signal } from '@vielzeug/stateit';\n\nimport { CRAFTIT_ERRORS } from '../errors';\nimport { tryRegisterCleanup } from '../runtime';\n\nexport type ResourceState<T> = {\n /** Previous successful data, available while a reload is pending. */\n data: T | undefined;\n /** Rejection reason from the most recent failed fetch, or `undefined`. */\n error: unknown;\n /** `true` while the fetcher promise is in flight. */\n pending: boolean;\n};\n\n/**\n * Reactive async resource. Re-fetches whenever the `deps` getter returns a new value.\n * Previous data is preserved during reloads so the UI can show stale-while-revalidate UX.\n * In-flight requests are cancelled via `AbortController` when deps change.\n *\n * Must be called inside component `setup()` or another Craftit runtime scope.\n *\n * @example\n * ```ts\n * const user = resource(\n * () => props.userId.value,\n * (id, signal) => fetch(`/api/users/${id}`, { signal }).then(r => r.json()),\n * );\n *\n * return () => html`\n * ${when(() => user.value.pending, () => html`<p>Loading…</p>`)}\n * ${when(() => !!user.value.error, () => html`<p>Error</p>`)}\n * ${when(() => !!user.value.data, () => html`<p>${user.value.data?.name}</p>`)}\n * `;\n * ```\n */\nexport function resource<Deps, T>(deps: () => Deps, fetcher: (deps: Deps, signal: AbortSignal) => Promise<T>) {\n const state: Signal<ResourceState<T>> = signal({ data: undefined, error: undefined, pending: true });\n let previousData: T | undefined;\n\n // Use raw stateit effect and register cleanup explicitly.\n const dispose = signalEffect(() => {\n const currentDeps = deps();\n const controller = new AbortController();\n\n state.value = { data: previousData, error: undefined, pending: true };\n\n fetcher(currentDeps, controller.signal)\n .then((data) => {\n if (!controller.signal.aborted) {\n previousData = data;\n state.value = { data, error: undefined, pending: false };\n }\n })\n .catch((error: unknown) => {\n if (!controller.signal.aborted) {\n state.value = { data: previousData, error, pending: false };\n }\n });\n\n return () => controller.abort();\n });\n\n if (!tryRegisterCleanup(dispose)) throw new Error(CRAFTIT_ERRORS.lifecycleOutsideSetup);\n\n return state;\n}\n"],"mappings":"wJAmCA,SAAgB,EAAkB,EAAkB,EAA0D,CAC5G,IAAM,EAAkC,EAAO,CAAE,KAAM,IAAA,GAAW,MAAO,IAAA,GAAW,QAAS,EAAK,CAAC,EAC/F,EAyBJ,GAAI,CAAC,EAtBW,MAAmB,CACjC,IAAM,EAAc,EAAK,EACnB,EAAa,IAAI,gBAiBvB,MAfA,GAAM,MAAQ,CAAE,KAAM,EAAc,MAAO,IAAA,GAAW,QAAS,EAAK,EAEpE,EAAQ,EAAa,EAAW,MAAM,EACnC,KAAM,GAAS,CACT,EAAW,OAAO,UACrB,EAAe,EACf,EAAM,MAAQ,CAAE,OAAM,MAAO,IAAA,GAAW,QAAS,EAAM,EAE3D,CAAC,EACA,MAAO,GAAmB,CACpB,EAAW,OAAO,UACrB,EAAM,MAAQ,CAAE,KAAM,EAAc,QAAO,QAAS,EAAM,EAE9D,CAAC,MAEU,EAAW,MAAM,CAChC,CAEwB,CAAO,EAAG,MAAU,MAAM,EAAe,qBAAqB,EAEtF,OAAO,CACT"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require(`@vielzeug/stateit`);var t=t=>{let n=typeof t==`function`?t():(0,e.isSignal)(t)?t.value:t;return n==null||n===!1?``:String(n)},n=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),r=r=>(0,e.computed)(()=>{let e=[];for(let[i,a]of Object.entries(r)){let r=t(a);r&&e.push(`${n(i)}:${r}`)}return e.join(`;`)});exports.styleMap=r;
|
|
2
|
+
//# sourceMappingURL=styleMap.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styleMap.cjs","names":[],"sources":["../../src/directives/styleMap.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\ntype StyleInput =\n | string\n | number\n | null\n | undefined\n | false\n | (() => string | number | null | undefined | false)\n | ReadonlySignal<string | number | null | undefined | false>;\n\nconst toStyleValue = (value: StyleInput): string => {\n const resolved = typeof value === 'function' ? value() : isSignal(value) ? value.value : value;\n\n if (resolved == null || resolved === false) return '';\n\n return String(resolved);\n};\n\nconst toKebabCase = (name: string): string => name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);\n\n/**\n * Builds a reactive inline style string from a style object.\n *\n * @example\n * html`<div :style=${styleMap({ backgroundColor: color, width: () => `${size.value}px` })}></div>`\n */\nexport const styleMap = (record: Record<string, StyleInput>): ReadonlySignal<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 declarations.push(`${toKebabCase(name)}:${value}`);\n }\n\n return declarations.join(';');\n });\n};\n"],"mappings":"mCAWA,IAAM,EAAgB,GAA8B,CAClD,IAAM,EAAW,OAAO,GAAU,WAAa,EAAM,GAAA,EAAA,EAAA,UAAa,CAAK,EAAI,EAAM,MAAQ,EAIzF,OAFI,GAAY,MAAQ,IAAa,GAAc,GAE5C,OAAO,CAAQ,CACxB,EAEM,EAAe,GAAyB,EAAK,QAAQ,SAAW,GAAS,IAAI,EAAK,YAAY,GAAG,EAQ1F,EAAY,IACvB,EAAA,EAAA,cAAsB,CACpB,IAAM,EAAyB,CAAC,EAEhC,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAM,EAAG,CAClD,IAAM,EAAQ,EAAa,CAAK,EAE3B,GAEL,EAAa,KAAK,GAAG,EAAY,CAAI,EAAE,GAAG,GAAO,CACnD,CAEA,OAAO,EAAa,KAAK,GAAG,CAC9B,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ReadonlySignal } from '@vielzeug/stateit';
|
|
2
|
+
type StyleInput = string | number | null | undefined | false | (() => string | number | null | undefined | false) | ReadonlySignal<string | number | null | undefined | false>;
|
|
3
|
+
/**
|
|
4
|
+
* Builds a reactive inline style string from a style object.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* html`<div :style=${styleMap({ backgroundColor: color, width: () => `${size.value}px` })}></div>`
|
|
8
|
+
*/
|
|
9
|
+
export declare const styleMap: (record: Record<string, StyleInput>) => ReadonlySignal<string>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=styleMap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styleMap.d.ts","sourceRoot":"","sources":["../../src/directives/styleMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE5E,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,cAAc,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,CAAC,CAAC;AAY/D;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,GAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,KAAG,cAAc,CAAC,MAAM,CAclF,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{computed as e,isSignal as t}from"@vielzeug/stateit";var n=e=>{let n=typeof e==`function`?e():t(e)?e.value:e;return n==null||n===!1?``:String(n)},r=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),i=t=>e(()=>{let e=[];for(let[i,a]of Object.entries(t)){let t=n(a);t&&e.push(`${r(i)}:${t}`)}return e.join(`;`)});export{i as styleMap};
|
|
2
|
+
//# sourceMappingURL=styleMap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styleMap.js","names":[],"sources":["../../src/directives/styleMap.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\ntype StyleInput =\n | string\n | number\n | null\n | undefined\n | false\n | (() => string | number | null | undefined | false)\n | ReadonlySignal<string | number | null | undefined | false>;\n\nconst toStyleValue = (value: StyleInput): string => {\n const resolved = typeof value === 'function' ? value() : isSignal(value) ? value.value : value;\n\n if (resolved == null || resolved === false) return '';\n\n return String(resolved);\n};\n\nconst toKebabCase = (name: string): string => name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);\n\n/**\n * Builds a reactive inline style string from a style object.\n *\n * @example\n * html`<div :style=${styleMap({ backgroundColor: color, width: () => `${size.value}px` })}></div>`\n */\nexport const styleMap = (record: Record<string, StyleInput>): ReadonlySignal<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 declarations.push(`${toKebabCase(name)}:${value}`);\n }\n\n return declarations.join(';');\n });\n};\n"],"mappings":"2DAWA,IAAM,EAAgB,GAA8B,CAClD,IAAM,EAAW,OAAO,GAAU,WAAa,EAAM,EAAI,EAAS,CAAK,EAAI,EAAM,MAAQ,EAIzF,OAFI,GAAY,MAAQ,IAAa,GAAc,GAE5C,OAAO,CAAQ,CACxB,EAEM,EAAe,GAAyB,EAAK,QAAQ,SAAW,GAAS,IAAI,EAAK,YAAY,GAAG,EAQ1F,EAAY,GAChB,MAAe,CACpB,IAAM,EAAyB,CAAC,EAEhC,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAM,EAAG,CAClD,IAAM,EAAQ,EAAa,CAAK,EAE3B,GAEL,EAAa,KAAK,GAAG,EAAY,CAAI,EAAE,GAAG,GAAO,CACnD,CAEA,OAAO,EAAa,KAAK,GAAG,CAC9B,CAAC"}
|
package/dist/directives/when.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
let e=require(`@vielzeug/stateit`);var t=
|
|
1
|
+
let e=require(`@vielzeug/stateit`);var t=t=>typeof t==`function`?t():(0,e.isSignal)(t)?t.value:t;function n(n,r,i){return(0,e.computed)(()=>t(n)?r():i?i():``)}exports.when=n;
|
|
2
2
|
//# sourceMappingURL=when.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"when.cjs","names":[],"sources":["../../src/directives/when.ts"],"sourcesContent":["import { isSignal, type ReadonlySignal
|
|
1
|
+
{"version":3,"file":"when.cjs","names":[],"sources":["../../src/directives/when.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport type { HTMLResult } from '../internal';\n\ntype MaybeReactive<T> = T | (() => T) | ReadonlySignal<T>;\n\ntype WhenRenderable = string | HTMLResult;\n\nconst resolve = <T>(value: MaybeReactive<T>): T => {\n if (typeof value === 'function') return (value as () => T)();\n\n if (isSignal(value)) return value.value;\n\n return value;\n};\n\n/**\n * Conditionally renders one of two branches.\n */\nexport function when(\n condition: MaybeReactive<boolean>,\n truthy: () => WhenRenderable,\n falsy?: () => WhenRenderable,\n): ReadonlySignal<WhenRenderable> {\n return computed(() => {\n if (resolve(condition)) return truthy();\n\n return falsy ? falsy() : '';\n });\n} // Note: when returns HtmlResult (not DirectiveResult)\n"],"mappings":"mCAQA,IAAM,EAAc,GACd,OAAO,GAAU,WAAoB,EAAkB,GAE3D,EAAA,EAAA,UAAa,CAAK,EAAU,EAAM,MAE3B,EAMT,SAAgB,EACd,EACA,EACA,EACgC,CAChC,OAAA,EAAA,EAAA,cACM,EAAQ,CAAS,EAAU,EAAO,EAE/B,EAAQ,EAAM,EAAI,EAC1B,CACH"}
|
|
@@ -1,23 +1,10 @@
|
|
|
1
|
-
import { type ReadonlySignal
|
|
2
|
-
import type {
|
|
3
|
-
type
|
|
4
|
-
|
|
5
|
-
condition: Signal<unknown> | ReadonlySignal<unknown> | (() => unknown) | unknown;
|
|
6
|
-
else?: TemplateFn<V> | null;
|
|
7
|
-
then?: TemplateFn<V> | null;
|
|
8
|
-
};
|
|
1
|
+
import { type ReadonlySignal } from '@vielzeug/stateit';
|
|
2
|
+
import type { HTMLResult } from '../internal';
|
|
3
|
+
type MaybeReactive<T> = T | (() => T) | ReadonlySignal<T>;
|
|
4
|
+
type WhenRenderable = string | HTMLResult;
|
|
9
5
|
/**
|
|
10
|
-
* Conditionally renders one of two
|
|
11
|
-
*
|
|
12
|
-
* - **Signal or getter** — returns a reactive function the engine re-runs automatically.
|
|
13
|
-
* - **Static value** — evaluated once at call time, returns the result directly.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* import { when } from '@vielzeug/craftit/directives';
|
|
17
|
-
*
|
|
18
|
-
* html`${when({ condition: isLoggedIn, then: () => html`<user-panel>`, else: () => html`<login-form>` })}`
|
|
19
|
-
* html`${when({ condition: () => count.value > 0, then: () => html`<span>${count}</span>` })}`
|
|
6
|
+
* Conditionally renders one of two branches.
|
|
20
7
|
*/
|
|
21
|
-
export declare function when<
|
|
8
|
+
export declare function when(condition: MaybeReactive<boolean>, truthy: () => WhenRenderable, falsy?: () => WhenRenderable): ReadonlySignal<WhenRenderable>;
|
|
22
9
|
export {};
|
|
23
10
|
//# sourceMappingURL=when.d.ts.map
|