@vielzeug/craftit 1.0.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -401
- package/dist/core/component.cjs +2 -0
- package/dist/core/component.cjs.map +1 -0
- package/dist/core/component.d.ts +172 -0
- package/dist/core/component.d.ts.map +1 -0
- package/dist/core/component.js +2 -0
- package/dist/core/component.js.map +1 -0
- package/dist/core/host.cjs +2 -0
- package/dist/core/host.cjs.map +1 -0
- package/dist/core/host.d.ts +77 -0
- package/dist/core/host.d.ts.map +1 -0
- package/dist/core/host.js +2 -0
- package/dist/core/host.js.map +1 -0
- package/dist/core/internal.cjs +2 -0
- package/dist/core/internal.cjs.map +1 -0
- package/dist/core/internal.d.ts +107 -0
- package/dist/core/internal.d.ts.map +1 -0
- package/dist/core/internal.js +2 -0
- package/dist/core/internal.js.map +1 -0
- package/dist/core/runtime-bindings.cjs +2 -0
- package/dist/core/runtime-bindings.cjs.map +1 -0
- package/dist/core/runtime-bindings.d.ts +6 -0
- package/dist/core/runtime-bindings.d.ts.map +1 -0
- package/dist/core/runtime-bindings.js +2 -0
- package/dist/core/runtime-bindings.js.map +1 -0
- package/dist/core/runtime-lifecycle.cjs +2 -0
- package/dist/core/runtime-lifecycle.cjs.map +1 -0
- package/dist/core/runtime-lifecycle.d.ts +116 -0
- package/dist/core/runtime-lifecycle.d.ts.map +1 -0
- package/dist/core/runtime-lifecycle.js +2 -0
- package/dist/core/runtime-lifecycle.js.map +1 -0
- package/dist/core/runtime.cjs +1 -0
- package/dist/core/runtime.d.ts +3 -0
- package/dist/core/runtime.d.ts.map +1 -0
- package/dist/core/runtime.js +1 -0
- package/dist/core/template-bindings.cjs +2 -0
- package/dist/core/template-bindings.cjs.map +1 -0
- package/dist/core/template-bindings.d.ts +59 -0
- package/dist/core/template-bindings.d.ts.map +1 -0
- package/dist/core/template-bindings.js +2 -0
- package/dist/core/template-bindings.js.map +1 -0
- package/dist/core/template-compiler.cjs +2 -0
- package/dist/core/template-compiler.cjs.map +1 -0
- package/dist/core/template-compiler.d.ts +25 -0
- package/dist/core/template-compiler.d.ts.map +1 -0
- package/dist/core/template-compiler.js +2 -0
- package/dist/core/template-compiler.js.map +1 -0
- package/dist/core/template-dom.cjs +2 -0
- package/dist/core/template-dom.cjs.map +1 -0
- package/dist/core/template-dom.d.ts +13 -0
- package/dist/core/template-dom.d.ts.map +1 -0
- package/dist/core/template-dom.js +2 -0
- package/dist/core/template-dom.js.map +1 -0
- package/dist/core/template-html.cjs +2 -0
- package/dist/core/template-html.cjs.map +1 -0
- package/dist/core/template-html.d.ts +26 -0
- package/dist/core/template-html.d.ts.map +1 -0
- package/dist/core/template-html.js +2 -0
- package/dist/core/template-html.js.map +1 -0
- package/dist/core/template.cjs +2 -0
- package/dist/core/template.cjs.map +1 -0
- package/dist/core/template.d.ts +11 -0
- package/dist/core/template.d.ts.map +1 -0
- package/dist/core/template.js +2 -0
- package/dist/core/template.js.map +1 -0
- package/dist/core/utilities.cjs +2 -0
- package/dist/core/utilities.cjs.map +1 -0
- package/dist/core/utilities.d.ts +68 -0
- package/dist/core/utilities.d.ts.map +1 -0
- package/dist/core/utilities.js +2 -0
- package/dist/core/utilities.js.map +1 -0
- package/dist/craftit.cjs +2 -18
- package/dist/craftit.cjs.map +1 -1
- package/dist/craftit.js +2 -580
- package/dist/craftit.js.map +1 -1
- package/dist/directives/attr.cjs +2 -0
- package/dist/directives/attr.cjs.map +1 -0
- package/dist/directives/attr.d.ts +14 -0
- package/dist/directives/attr.d.ts.map +1 -0
- package/dist/directives/attr.js +2 -0
- package/dist/directives/attr.js.map +1 -0
- package/dist/directives/bind.cjs +2 -0
- package/dist/directives/bind.cjs.map +1 -0
- package/dist/directives/bind.d.ts +30 -0
- package/dist/directives/bind.d.ts.map +1 -0
- package/dist/directives/bind.js +2 -0
- package/dist/directives/bind.js.map +1 -0
- package/dist/directives/choose.cjs +2 -0
- package/dist/directives/choose.cjs.map +1 -0
- package/dist/directives/choose.d.ts +34 -0
- package/dist/directives/choose.d.ts.map +1 -0
- package/dist/directives/choose.js +2 -0
- package/dist/directives/choose.js.map +1 -0
- package/dist/directives/classes.cjs +2 -0
- package/dist/directives/classes.cjs.map +1 -0
- package/dist/directives/classes.d.ts +20 -0
- package/dist/directives/classes.d.ts.map +1 -0
- package/dist/directives/classes.js +2 -0
- package/dist/directives/classes.js.map +1 -0
- package/dist/directives/each.cjs +2 -0
- package/dist/directives/each.cjs.map +1 -0
- package/dist/directives/each.d.ts +68 -0
- package/dist/directives/each.d.ts.map +1 -0
- package/dist/directives/each.js +2 -0
- package/dist/directives/each.js.map +1 -0
- package/dist/directives/index.cjs +1 -0
- package/dist/directives/index.d.ts +14 -0
- package/dist/directives/index.d.ts.map +1 -0
- package/dist/directives/index.js +1 -0
- package/dist/directives/match.cjs +2 -0
- package/dist/directives/match.cjs.map +1 -0
- package/dist/directives/match.d.ts +31 -0
- package/dist/directives/match.d.ts.map +1 -0
- package/dist/directives/match.js +2 -0
- package/dist/directives/match.js.map +1 -0
- package/dist/directives/memo.cjs +2 -0
- package/dist/directives/memo.cjs.map +1 -0
- package/dist/directives/memo.d.ts +23 -0
- package/dist/directives/memo.d.ts.map +1 -0
- package/dist/directives/memo.js +2 -0
- package/dist/directives/memo.js.map +1 -0
- package/dist/directives/on.cjs +2 -0
- package/dist/directives/on.cjs.map +1 -0
- package/dist/directives/on.d.ts +25 -0
- package/dist/directives/on.d.ts.map +1 -0
- package/dist/directives/on.js +2 -0
- package/dist/directives/on.js.map +1 -0
- package/dist/directives/raw.cjs +2 -0
- package/dist/directives/raw.cjs.map +1 -0
- package/dist/directives/raw.d.ts +25 -0
- package/dist/directives/raw.d.ts.map +1 -0
- package/dist/directives/raw.js +2 -0
- package/dist/directives/raw.js.map +1 -0
- package/dist/directives/spread.cjs +2 -0
- package/dist/directives/spread.cjs.map +1 -0
- package/dist/directives/spread.d.ts +14 -0
- package/dist/directives/spread.d.ts.map +1 -0
- package/dist/directives/spread.js +2 -0
- package/dist/directives/spread.js.map +1 -0
- package/dist/directives/style.cjs +2 -0
- package/dist/directives/style.cjs.map +1 -0
- package/dist/directives/style.d.ts +22 -0
- package/dist/directives/style.d.ts.map +1 -0
- package/dist/directives/style.js +2 -0
- package/dist/directives/style.js.map +1 -0
- package/dist/directives/until.cjs +2 -0
- package/dist/directives/until.cjs.map +1 -0
- package/dist/directives/until.d.ts +26 -0
- package/dist/directives/until.d.ts.map +1 -0
- package/dist/directives/until.js +2 -0
- package/dist/directives/until.js.map +1 -0
- package/dist/directives/when.cjs +2 -0
- package/dist/directives/when.cjs.map +1 -0
- package/dist/directives/when.d.ts +17 -0
- package/dist/directives/when.d.ts.map +1 -0
- package/dist/directives/when.js +2 -0
- package/dist/directives/when.js.map +1 -0
- package/dist/index.cjs +1 -2
- package/dist/index.d.ts +10 -265
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -13
- package/dist/labs/a11y.cjs +2 -0
- package/dist/labs/a11y.cjs.map +1 -0
- package/dist/labs/a11y.d.ts +61 -0
- package/dist/labs/a11y.d.ts.map +1 -0
- package/dist/labs/a11y.js +2 -0
- package/dist/labs/a11y.js.map +1 -0
- package/dist/labs/index.d.ts +8 -0
- package/dist/labs/index.d.ts.map +1 -0
- package/dist/labs/list.cjs +2 -0
- package/dist/labs/list.cjs.map +1 -0
- package/dist/labs/list.d.ts +26 -0
- package/dist/labs/list.d.ts.map +1 -0
- package/dist/labs/list.js +2 -0
- package/dist/labs/list.js.map +1 -0
- package/dist/labs/observers.cjs +2 -0
- package/dist/labs/observers.cjs.map +1 -0
- package/dist/labs/observers.d.ts +42 -0
- package/dist/labs/observers.d.ts.map +1 -0
- package/dist/labs/observers.js +2 -0
- package/dist/labs/observers.js.map +1 -0
- package/dist/labs/overlay.cjs +2 -0
- package/dist/labs/overlay.cjs.map +1 -0
- package/dist/labs/overlay.d.ts +35 -0
- package/dist/labs/overlay.d.ts.map +1 -0
- package/dist/labs/overlay.js +2 -0
- package/dist/labs/overlay.js.map +1 -0
- package/dist/labs/selectable.cjs +2 -0
- package/dist/labs/selectable.cjs.map +1 -0
- package/dist/labs/selectable.d.ts +70 -0
- package/dist/labs/selectable.d.ts.map +1 -0
- package/dist/labs/selectable.js +2 -0
- package/dist/labs/selectable.js.map +1 -0
- package/dist/labs/selection.cjs +2 -0
- package/dist/labs/selection.cjs.map +1 -0
- package/dist/labs/selection.d.ts +68 -0
- package/dist/labs/selection.d.ts.map +1 -0
- package/dist/labs/selection.js +2 -0
- package/dist/labs/selection.js.map +1 -0
- package/dist/labs.cjs +1 -0
- package/dist/labs.js +1 -0
- package/dist/test/index.d.ts +2 -0
- package/dist/test/index.d.ts.map +1 -0
- package/dist/test/test.cjs +2 -0
- package/dist/test/test.cjs.map +1 -0
- package/dist/test/test.d.ts +198 -0
- package/dist/test/test.d.ts.map +1 -0
- package/dist/test/test.js +2 -0
- package/dist/test/test.js.map +1 -0
- package/dist/test.cjs +1 -0
- package/dist/test.js +1 -0
- package/package.json +37 -9
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"choose.js","names":[],"sources":["../../src/directives/choose.ts"],"sourcesContent":["import { isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport type { HTMLResult } from '../core/internal';\n\ntype TemplateFn = () => string | HTMLResult;\ntype CaseEntry<T> = readonly [T, TemplateFn];\n\n/**\n * Renders the template matching the current value — like a type-safe `switch`.\n * Cases are `[value, templateFn]` pairs evaluated with strict equality (`===`).\n *\n * The discriminant can be a static value, a reactive Signal, or a getter function.\n * When reactive, the rendered output updates automatically when the value changes.\n *\n * @example\n * import { choose } from '@vielzeug/craftit/directives';\n *\n * // Static\n * html`${choose(section, [\n * ['home', () => html`<h1>Home</h1>`],\n * ['about', () => html`<h1>About</h1>`],\n * ], () => html`<h1>Not found</h1>`)}`\n *\n * // Reactive signal — updates on change\n * const tab = signal('home');\n * html`${choose(tab, [\n * ['home', () => html`<home-panel>`],\n * ['about', () => html`<about-panel>`],\n * ])}`\n *\n * // Reactive getter\n * html`${choose(() => props.view.value, cases)}`\n */\nexport function choose<T extends PropertyKey>(\n value: ReadonlySignal<T> | (() => T),\n cases: ReadonlyArray<CaseEntry<T>>,\n defaultFn?: TemplateFn,\n): TemplateFn;\nexport function choose<T extends PropertyKey>(\n value: T,\n cases: ReadonlyArray<CaseEntry<T>>,\n defaultFn?: TemplateFn,\n): string | HTMLResult;\nexport function choose<T extends PropertyKey>(\n value: T | ReadonlySignal<T> | (() => T),\n cases: ReadonlyArray<CaseEntry<T>>,\n defaultFn?: TemplateFn,\n): string | HTMLResult | TemplateFn {\n const pick = (v: T): string | HTMLResult => {\n const entry = cases.find(([key]) => key === v);\n\n return entry ? entry[1]() : (defaultFn?.() ?? '');\n };\n\n if (isSignal(value)) {\n return () => pick((value as ReadonlySignal<T>).value);\n }\n\n if (typeof value === 'function') {\n return () => pick((value as () => T)());\n }\n\n return pick(value as T);\n}\n"],"mappings":"6CA2CA,SAAgB,EACd,EACA,EACA,EACkC,CAClC,IAAM,EAAQ,GAA8B,CAC1C,IAAM,EAAQ,EAAM,MAAM,CAAC,KAAS,IAAQ,EAAE,CAE9C,OAAO,EAAQ,EAAM,IAAI,CAAI,KAAa,EAAI,IAWhD,OARI,EAAS,EAAM,KACJ,EAAM,EAA4B,MAAM,CAGnD,OAAO,GAAU,eACN,EAAM,GAAmB,CAAC,CAGlC,EAAK,EAAW"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require(`@vielzeug/stateit`);function t(t){let n=Object.entries(t),r=n.some(([,t])=>(0,e.isSignal)(t)||typeof t==`function`),i=()=>n.filter(([,t])=>(0,e.isSignal)(t)?t.value:typeof t==`function`?!!t():!!t).map(([e])=>e).join(` `);return r?(0,e.computed)(i):i()}exports.classes=t;
|
|
2
|
+
//# sourceMappingURL=classes.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classes.cjs","names":[],"sources":["../../src/directives/classes.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\ntype ClassValue = boolean | undefined | null | Signal<boolean> | ReadonlySignal<boolean> | (() => boolean);\n\n/**\n * Build a dynamic class string from an object map of class names to conditions.\n * Conditions can be static booleans, reactive Signals, or getter functions.\n * When any condition is reactive the returned value is a `ReadonlySignal<string>`\n * so it can be bound directly to a `class` attribute.\n *\n * @example\n * import { classes } from '@vielzeug/craftit/directives';\n *\n * // Static — returns a plain string\n * html`<div class=${classes({ foo: true, bar: false })}></div>`\n *\n * // Reactive — returns a signal; no arrow-function wrapper needed\n * html`<div class=${classes({ active: isActive, disabled: isDisabled })}></div>`\n */\nexport function classes(map: Record<string, ClassValue>): string | ReadonlySignal<string> {\n const entries = Object.entries(map);\n const hasReactive = entries.some(([, v]) => isSignal(v) || typeof v === 'function');\n\n const build = (): string =>\n entries\n .filter(([, v]) => {\n if (isSignal(v)) return (v as ReadonlySignal<boolean>).value;\n\n if (typeof v === 'function') return !!(v as () => boolean)();\n\n return !!v;\n })\n .map(([k]) => k)\n .join(' ');\n\n return hasReactive ? computed(build) : build();\n}\n"],"mappings":"mCAmBA,SAAgB,EAAQ,EAAkE,CACxF,IAAM,EAAU,OAAO,QAAQ,EAAI,CAC7B,EAAc,EAAQ,MAAM,EAAG,MAAA,EAAA,EAAA,UAAgB,EAAE,EAAI,OAAO,GAAM,WAAW,CAE7E,MACJ,EACG,QAAQ,EAAG,MACV,EAAA,EAAA,UAAa,EAAE,CAAU,EAA8B,MAEnD,OAAO,GAAM,WAAmB,CAAC,CAAE,GAAqB,CAErD,CAAC,CAAC,EACT,CACD,KAAK,CAAC,KAAO,EAAE,CACf,KAAK,IAAI,CAEd,OAAO,GAAA,EAAA,EAAA,UAAuB,EAAM,CAAG,GAAO"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ReadonlySignal, type Signal } from '@vielzeug/stateit';
|
|
2
|
+
type ClassValue = boolean | undefined | null | Signal<boolean> | ReadonlySignal<boolean> | (() => boolean);
|
|
3
|
+
/**
|
|
4
|
+
* Build a dynamic class string from an object map of class names to conditions.
|
|
5
|
+
* Conditions can be static booleans, reactive Signals, or getter functions.
|
|
6
|
+
* When any condition is reactive the returned value is a `ReadonlySignal<string>`
|
|
7
|
+
* so it can be bound directly to a `class` attribute.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* import { classes } from '@vielzeug/craftit/directives';
|
|
11
|
+
*
|
|
12
|
+
* // Static — returns a plain string
|
|
13
|
+
* html`<div class=${classes({ foo: true, bar: false })}></div>`
|
|
14
|
+
*
|
|
15
|
+
* // Reactive — returns a signal; no arrow-function wrapper needed
|
|
16
|
+
* html`<div class=${classes({ active: isActive, disabled: isDisabled })}></div>`
|
|
17
|
+
*/
|
|
18
|
+
export declare function classes(map: Record<string, ClassValue>): string | ReadonlySignal<string>;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=classes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classes.d.ts","sourceRoot":"","sources":["../../src/directives/classes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAAE,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEzF,KAAK,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;AAE3G;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAiBxF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{computed as e,isSignal as t}from"@vielzeug/stateit";function n(n){let r=Object.entries(n),i=r.some(([,e])=>t(e)||typeof e==`function`),a=()=>r.filter(([,e])=>t(e)?e.value:typeof e==`function`?!!e():!!e).map(([e])=>e).join(` `);return i?e(a):a()}export{n as classes};
|
|
2
|
+
//# sourceMappingURL=classes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classes.js","names":[],"sources":["../../src/directives/classes.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\ntype ClassValue = boolean | undefined | null | Signal<boolean> | ReadonlySignal<boolean> | (() => boolean);\n\n/**\n * Build a dynamic class string from an object map of class names to conditions.\n * Conditions can be static booleans, reactive Signals, or getter functions.\n * When any condition is reactive the returned value is a `ReadonlySignal<string>`\n * so it can be bound directly to a `class` attribute.\n *\n * @example\n * import { classes } from '@vielzeug/craftit/directives';\n *\n * // Static — returns a plain string\n * html`<div class=${classes({ foo: true, bar: false })}></div>`\n *\n * // Reactive — returns a signal; no arrow-function wrapper needed\n * html`<div class=${classes({ active: isActive, disabled: isDisabled })}></div>`\n */\nexport function classes(map: Record<string, ClassValue>): string | ReadonlySignal<string> {\n const entries = Object.entries(map);\n const hasReactive = entries.some(([, v]) => isSignal(v) || typeof v === 'function');\n\n const build = (): string =>\n entries\n .filter(([, v]) => {\n if (isSignal(v)) return (v as ReadonlySignal<boolean>).value;\n\n if (typeof v === 'function') return !!(v as () => boolean)();\n\n return !!v;\n })\n .map(([k]) => k)\n .join(' ');\n\n return hasReactive ? computed(build) : build();\n}\n"],"mappings":"2DAmBA,SAAgB,EAAQ,EAAkE,CACxF,IAAM,EAAU,OAAO,QAAQ,EAAI,CAC7B,EAAc,EAAQ,MAAM,EAAG,KAAO,EAAS,EAAE,EAAI,OAAO,GAAM,WAAW,CAE7E,MACJ,EACG,QAAQ,EAAG,KACN,EAAS,EAAE,CAAU,EAA8B,MAEnD,OAAO,GAAM,WAAmB,CAAC,CAAE,GAAqB,CAErD,CAAC,CAAC,EACT,CACD,KAAK,CAAC,KAAO,EAAE,CACf,KAAK,IAAI,CAEd,OAAO,EAAc,EAAS,EAAM,CAAG,GAAO"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`../core/internal.cjs`),t=require(`../core/utilities.cjs`);let n=require(`@vielzeug/stateit`);var r=RegExp(`u="(\\d+)"`,`g`),i=e.htmlResult(``),a=[],o=(n,r)=>e.isHtmlResult(n)?c(n,r):{bindings:a,html:t.escapeHtml(n)},s=n=>e.isHtmlResult(n)?n:e.htmlResult(t.escapeHtml(n));function c(e,t){let n=new Map,i=[];for(let r of e.__bindings){let e=r.uid,a=n.get(e)??String(t.n++);n.has(e)||n.set(e,a),i.push({...r,uid:a})}return{bindings:i,html:e.__html.replace(r,(e,t)=>`u="${n.get(t)??t}"`).replace(/<!--(\d+)-->/g,(e,t)=>`<!--${n.get(t)??t}-->`)}}function l(e,t,n){let r=``,i=[],a=[],s=[],c={n:0};for(let l=0;l<e.length;l++){a.push(n(e[l],l));let u=o(t(e[l],l),c);r+=u.html,i.push(...u.bindings),s.push(u)}return{bindings:i,html:r,keys:a,rendered:s}}function u(e,t){let n=``,r=[],i={n:0};for(let a=0;a<e.length;a++){let s=o(t(e[a],a),i);n+=s.html,r.push(...s.bindings)}return{bindings:r,html:n}}function d(t,r,a,o){let c=typeof a==`function`,d=c?a:void 0,f=(c?o:o??a)??{},p=f.select;if(Array.isArray(t)){let n=p?t.filter(p):t;if(!n.length){if(d){let t=e.extractResult(s(d()));return e.htmlResult(t.html,t.bindings)}return i}let{bindings:a,html:o}=u(n,r);return e.htmlResult(o,a)}let m=f.key;if(!m)throw Error(`[craftit:each] Reactive each() requires options.key for stable reconciliation.`);let h=(0,n.isSignal)(t)?()=>t.value:t;return{[e.EACH_SIGNAL]:(0,n.computed)(()=>{let t=h(),n=p?t.filter(p):t;if(!n.length){let t=d?s(d()):void 0;return t?{...e.extractResult(t),items:[],keys:[]}:{bindings:[],html:``,items:[],keys:[]}}let{bindings:i,html:a,keys:o,rendered:c}=l(n,r,m);return{bindings:i,html:a,items:c,keys:o}})}}exports.each=d;
|
|
2
|
+
//# sourceMappingURL=each.cjs.map
|
|
@@ -0,0 +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 extractResult,\n htmlResult,\n isHtmlResult,\n type Binding,\n type Directive,\n type HTMLResult,\n} from '../core/internal';\nimport { escapeHtml } from '../core/utilities';\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 rendered: Array<{ bindings: Binding[]; html: string }> = [];\n const c = { n: 0 };\n\n for (let i = 0; i < items.length; i++) {\n keys.push(keyFn(items[i], i));\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\n/**\n * Options accepted by `each()`.\n */\nexport interface EachOptions<T> {\n /**\n * Key extractor for stable DOM reconciliation.\n * Receives the item and its **filtered** array index (after `select` is applied).\n *\n * Required when `source` is reactive (Signal/getter).\n * Optional for static arrays (render-once path).\n */\n key?: (item: T, index: number) => string | number;\n /**\n * Filter predicate. Receives the item and its **original** array index.\n * Only items for which `select` returns `true` are rendered.\n *\n * @example\n * each(items, item => html`<li>${item.name}</li>`, undefined, { select: item => item.active })\n */\n select?: (item: T, index: number) => boolean;\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\n/**\n * Renders a reactive list with keyed DOM reconciliation for efficient updates.\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], item => html`<li>${item}</li>`)}`\n *\n * // Reactive source (key required):\n * html`${each(items, item => html`<li>${item.name}</li>`, undefined, { key: item => item.id })}`\n *\n * // Full example:\n * html`${each(items, item => html`<li>${item.name}</li>`, () => html`<p>No items</p>`, {\n * key: item => item.id,\n * select: item => item.active,\n * })}`\n */\nexport function each<T>(\n source: T[],\n template: (item: T, index: number) => string | HTMLResult,\n empty?: () => string | HTMLResult,\n options?: EachOptions<T>,\n): HTMLResult;\nexport function each<T>(\n source: ReactiveSource<T>,\n template: (item: T, index: number) => string | HTMLResult,\n options: EachOptions<T> & { key: (item: T, index: number) => string | number },\n): EachSignalResult;\nexport function each<T>(\n source: ReactiveSource<T>,\n template: (item: T, index: number) => string | HTMLResult,\n empty: (() => string | HTMLResult) | undefined,\n options: EachOptions<T> & { key: (item: T, index: number) => string | number },\n): EachSignalResult;\nexport function each<T>(\n source: T[] | ReactiveSource<T>,\n template: (item: T, index: number) => string | HTMLResult,\n emptyOrOptions?: (() => string | HTMLResult) | EachOptions<T>,\n options?: EachOptions<T>,\n): HTMLResult | EachSignalResult {\n const hasEmptyFn = typeof emptyOrOptions === 'function';\n const empty = hasEmptyFn ? emptyOrOptions : undefined;\n const resolvedOptions = (hasEmptyFn ? options : (options ?? emptyOrOptions)) ?? {};\n const select = resolvedOptions.select;\n\n if (Array.isArray(source)) {\n const filtered = select ? source.filter(select) : source;\n\n if (!filtered.length) {\n if (empty) {\n const er = extractResult(toHtmlResult(empty()));\n\n return htmlResult(er.html, er.bindings);\n }\n\n return EMPTY;\n }\n\n const { bindings, html } = renderStatic(filtered, template);\n\n return htmlResult(html, bindings);\n }\n\n const keyFn = resolvedOptions.key;\n\n if (!keyFn) {\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 = getItems();\n const filtered = select ? raw.filter(select) : raw;\n\n if (!filtered.length) {\n const er = empty ? toHtmlResult(empty()) : undefined;\n\n return er ? { ...extractResult(er), items: [], keys: [] } : { bindings: [], html: '', items: [], keys: [] };\n }\n\n const { bindings, html, keys, rendered } = renderKeyed(filtered, template, keyFn);\n\n return { bindings, html, items: rendered, keys };\n }),\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type ReadonlySignal } from '@vielzeug/stateit';
|
|
2
|
+
import { EACH_SIGNAL, type Binding, type Directive, type HTMLResult } from '../core/internal';
|
|
3
|
+
/**
|
|
4
|
+
* Options accepted by `each()`.
|
|
5
|
+
*/
|
|
6
|
+
export interface EachOptions<T> {
|
|
7
|
+
/**
|
|
8
|
+
* Key extractor for stable DOM reconciliation.
|
|
9
|
+
* Receives the item and its **filtered** array index (after `select` is applied).
|
|
10
|
+
*
|
|
11
|
+
* Required when `source` is reactive (Signal/getter).
|
|
12
|
+
* Optional for static arrays (render-once path).
|
|
13
|
+
*/
|
|
14
|
+
key?: (item: T, index: number) => string | number;
|
|
15
|
+
/**
|
|
16
|
+
* Filter predicate. Receives the item and its **original** array index.
|
|
17
|
+
* Only items for which `select` returns `true` are rendered.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* each(items, item => html`<li>${item.name}</li>`, undefined, { select: item => item.active })
|
|
21
|
+
*/
|
|
22
|
+
select?: (item: T, index: number) => boolean;
|
|
23
|
+
}
|
|
24
|
+
type ReactiveSource<T> = ReadonlySignal<T[]> | (() => T[]);
|
|
25
|
+
type EachSignalResult = Directive & {
|
|
26
|
+
[EACH_SIGNAL]: ReadonlySignal<{
|
|
27
|
+
bindings: Binding[];
|
|
28
|
+
html: string;
|
|
29
|
+
items?: Array<{
|
|
30
|
+
bindings: Binding[];
|
|
31
|
+
html: string;
|
|
32
|
+
}>;
|
|
33
|
+
keys?: (string | number)[];
|
|
34
|
+
}>;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Renders a reactive list with keyed DOM reconciliation for efficient updates.
|
|
38
|
+
* Use inside `html` tagged templates.
|
|
39
|
+
*
|
|
40
|
+
* For reactive sources (Signal/getter), you must provide a stable `key` function.
|
|
41
|
+
*
|
|
42
|
+
* For dynamic lists with click handlers, prefer event delegation on a parent node
|
|
43
|
+
* (`@click` + `closest(...)`) over per-item handlers inside `each()`.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* import { each } from '@vielzeug/craftit/directives';
|
|
47
|
+
*
|
|
48
|
+
* // Static array (key optional):
|
|
49
|
+
* html`${each([1, 2, 3], item => html`<li>${item}</li>`)}`
|
|
50
|
+
*
|
|
51
|
+
* // Reactive source (key required):
|
|
52
|
+
* html`${each(items, item => html`<li>${item.name}</li>`, undefined, { key: item => item.id })}`
|
|
53
|
+
*
|
|
54
|
+
* // Full example:
|
|
55
|
+
* html`${each(items, item => html`<li>${item.name}</li>`, () => html`<p>No items</p>`, {
|
|
56
|
+
* key: item => item.id,
|
|
57
|
+
* select: item => item.active,
|
|
58
|
+
* })}`
|
|
59
|
+
*/
|
|
60
|
+
export declare function each<T>(source: T[], template: (item: T, index: number) => string | HTMLResult, empty?: () => string | HTMLResult, options?: EachOptions<T>): HTMLResult;
|
|
61
|
+
export declare function each<T>(source: ReactiveSource<T>, template: (item: T, index: number) => string | HTMLResult, options: EachOptions<T> & {
|
|
62
|
+
key: (item: T, index: number) => string | number;
|
|
63
|
+
}): EachSignalResult;
|
|
64
|
+
export declare function each<T>(source: ReactiveSource<T>, template: (item: T, index: number) => string | HTMLResult, empty: (() => string | HTMLResult) | undefined, options: EachOptions<T> & {
|
|
65
|
+
key: (item: T, index: number) => string | number;
|
|
66
|
+
}): EachSignalResult;
|
|
67
|
+
export {};
|
|
68
|
+
//# sourceMappingURL=each.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"each.d.ts","sourceRoot":"","sources":["../../src/directives/each.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE5E,OAAO,EAEL,WAAW,EAIX,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,UAAU,EAChB,MAAM,kBAAkB,CAAC;AAuF1B;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAAC;IAClD;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;CAC9C;AAED,KAAK,cAAc,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAC3D,KAAK,gBAAgB,GAAG,SAAS,GAAG;IAClC,CAAC,WAAW,CAAC,EAAE,cAAc,CAAC;QAC5B,QAAQ,EAAE,OAAO,EAAE,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,KAAK,CAAC;YAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACrD,IAAI,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;KAC5B,CAAC,CAAC;CACJ,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,IAAI,CAAC,CAAC,EACpB,MAAM,EAAE,CAAC,EAAE,EACX,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,UAAU,EACzD,KAAK,CAAC,EAAE,MAAM,MAAM,GAAG,UAAU,EACjC,OAAO,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GACvB,UAAU,CAAC;AACd,wBAAgB,IAAI,CAAC,CAAC,EACpB,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,EACzB,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,UAAU,EACzD,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG;IAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAAA;CAAE,GAC7E,gBAAgB,CAAC;AACpB,wBAAgB,IAAI,CAAC,CAAC,EACpB,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,EACzB,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,UAAU,EACzD,KAAK,EAAE,CAAC,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,SAAS,EAC9C,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG;IAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAAA;CAAE,GAC7E,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{EACH_SIGNAL as e,extractResult as t,htmlResult as n,isHtmlResult as r}from"../core/internal.js";import{escapeHtml as i}from"../core/utilities.js";import{computed as a,isSignal as o}from"@vielzeug/stateit";var s=RegExp(`u="(\\d+)"`,`g`),c=n(``),l=[],u=(e,t)=>r(e)?f(e,t):{bindings:l,html:i(e)},d=e=>r(e)?e:n(i(e));function f(e,t){let n=new Map,r=[];for(let i of e.__bindings){let e=i.uid,a=n.get(e)??String(t.n++);n.has(e)||n.set(e,a),r.push({...i,uid:a})}return{bindings:r,html:e.__html.replace(s,(e,t)=>`u="${n.get(t)??t}"`).replace(/<!--(\d+)-->/g,(e,t)=>`<!--${n.get(t)??t}-->`)}}function p(e,t,n){let r=``,i=[],a=[],o=[],s={n:0};for(let c=0;c<e.length;c++){a.push(n(e[c],c));let l=u(t(e[c],c),s);r+=l.html,i.push(...l.bindings),o.push(l)}return{bindings:i,html:r,keys:a,rendered:o}}function m(e,t){let n=``,r=[],i={n:0};for(let a=0;a<e.length;a++){let o=u(t(e[a],a),i);n+=o.html,r.push(...o.bindings)}return{bindings:r,html:n}}function h(r,i,s,l){let u=typeof s==`function`,f=u?s:void 0,h=(u?l:l??s)??{},g=h.select;if(Array.isArray(r)){let e=g?r.filter(g):r;if(!e.length){if(f){let e=t(d(f()));return n(e.html,e.bindings)}return c}let{bindings:a,html:o}=m(e,i);return n(o,a)}let _=h.key;if(!_)throw Error(`[craftit:each] Reactive each() requires options.key for stable reconciliation.`);let v=o(r)?()=>r.value:r;return{[e]:a(()=>{let e=v(),n=g?e.filter(g):e;if(!n.length){let e=f?d(f()):void 0;return e?{...t(e),items:[],keys:[]}:{bindings:[],html:``,items:[],keys:[]}}let{bindings:r,html:a,keys:o,rendered:s}=p(n,i,_);return{bindings:r,html:a,items:s,keys:o}})}}export{h as each};
|
|
2
|
+
//# sourceMappingURL=each.js.map
|
|
@@ -0,0 +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 extractResult,\n htmlResult,\n isHtmlResult,\n type Binding,\n type Directive,\n type HTMLResult,\n} from '../core/internal';\nimport { escapeHtml } from '../core/utilities';\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 rendered: Array<{ bindings: Binding[]; html: string }> = [];\n const c = { n: 0 };\n\n for (let i = 0; i < items.length; i++) {\n keys.push(keyFn(items[i], i));\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\n/**\n * Options accepted by `each()`.\n */\nexport interface EachOptions<T> {\n /**\n * Key extractor for stable DOM reconciliation.\n * Receives the item and its **filtered** array index (after `select` is applied).\n *\n * Required when `source` is reactive (Signal/getter).\n * Optional for static arrays (render-once path).\n */\n key?: (item: T, index: number) => string | number;\n /**\n * Filter predicate. Receives the item and its **original** array index.\n * Only items for which `select` returns `true` are rendered.\n *\n * @example\n * each(items, item => html`<li>${item.name}</li>`, undefined, { select: item => item.active })\n */\n select?: (item: T, index: number) => boolean;\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\n/**\n * Renders a reactive list with keyed DOM reconciliation for efficient updates.\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], item => html`<li>${item}</li>`)}`\n *\n * // Reactive source (key required):\n * html`${each(items, item => html`<li>${item.name}</li>`, undefined, { key: item => item.id })}`\n *\n * // Full example:\n * html`${each(items, item => html`<li>${item.name}</li>`, () => html`<p>No items</p>`, {\n * key: item => item.id,\n * select: item => item.active,\n * })}`\n */\nexport function each<T>(\n source: T[],\n template: (item: T, index: number) => string | HTMLResult,\n empty?: () => string | HTMLResult,\n options?: EachOptions<T>,\n): HTMLResult;\nexport function each<T>(\n source: ReactiveSource<T>,\n template: (item: T, index: number) => string | HTMLResult,\n options: EachOptions<T> & { key: (item: T, index: number) => string | number },\n): EachSignalResult;\nexport function each<T>(\n source: ReactiveSource<T>,\n template: (item: T, index: number) => string | HTMLResult,\n empty: (() => string | HTMLResult) | undefined,\n options: EachOptions<T> & { key: (item: T, index: number) => string | number },\n): EachSignalResult;\nexport function each<T>(\n source: T[] | ReactiveSource<T>,\n template: (item: T, index: number) => string | HTMLResult,\n emptyOrOptions?: (() => string | HTMLResult) | EachOptions<T>,\n options?: EachOptions<T>,\n): HTMLResult | EachSignalResult {\n const hasEmptyFn = typeof emptyOrOptions === 'function';\n const empty = hasEmptyFn ? emptyOrOptions : undefined;\n const resolvedOptions = (hasEmptyFn ? options : (options ?? emptyOrOptions)) ?? {};\n const select = resolvedOptions.select;\n\n if (Array.isArray(source)) {\n const filtered = select ? source.filter(select) : source;\n\n if (!filtered.length) {\n if (empty) {\n const er = extractResult(toHtmlResult(empty()));\n\n return htmlResult(er.html, er.bindings);\n }\n\n return EMPTY;\n }\n\n const { bindings, html } = renderStatic(filtered, template);\n\n return htmlResult(html, bindings);\n }\n\n const keyFn = resolvedOptions.key;\n\n if (!keyFn) {\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 = getItems();\n const filtered = select ? raw.filter(select) : raw;\n\n if (!filtered.length) {\n const er = empty ? toHtmlResult(empty()) : undefined;\n\n return er ? { ...extractResult(er), items: [], keys: [] } : { bindings: [], html: '', items: [], keys: [] };\n }\n\n const { bindings, html, keys, rendered } = renderKeyed(filtered, template, keyFn);\n\n return { bindings, html, items: rendered, keys };\n }),\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./spread.cjs`),t=require(`./attr.cjs`),n=require(`./bind.cjs`),r=require(`./choose.cjs`),i=require(`./classes.cjs`),a=require(`./each.cjs`),o=require(`./match.cjs`),s=require(`./memo.cjs`),c=require(`./on.cjs`),l=require(`./raw.cjs`),u=require(`./style.cjs`),d=require(`./until.cjs`),f=require(`./when.cjs`);exports.attr=t.attr,exports.bind=n.bind,exports.choose=r.choose,exports.classes=i.classes,exports.each=a.each,exports.match=o.match,exports.memo=s.memo,exports.on=c.on,exports.raw=l.raw,exports.spread=e.spread,exports.style=u.style,exports.until=d.until,exports.when=f.when;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { attr } from './attr';
|
|
2
|
+
export { bind } from './bind';
|
|
3
|
+
export { choose } from './choose';
|
|
4
|
+
export { classes } from './classes';
|
|
5
|
+
export { each } from './each';
|
|
6
|
+
export { match } from './match';
|
|
7
|
+
export { memo } from './memo';
|
|
8
|
+
export { on } from './on';
|
|
9
|
+
export { raw } from './raw';
|
|
10
|
+
export { spread } from './spread';
|
|
11
|
+
export { style } from './style';
|
|
12
|
+
export { until } from './until';
|
|
13
|
+
export { when } from './when';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/directives/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{spread as e}from"./spread.js";import{attr as t}from"./attr.js";import{bind as n}from"./bind.js";import{choose as r}from"./choose.js";import{classes as i}from"./classes.js";import{each as a}from"./each.js";import{match as o}from"./match.js";import{memo as s}from"./memo.js";import{on as c}from"./on.js";import{raw as l}from"./raw.js";import{style as u}from"./style.js";import{until as d}from"./until.js";import{when as f}from"./when.js";export{t as attr,n as bind,r as choose,i as classes,a as each,o as match,s as memo,c as on,l as raw,e as spread,u as style,d as until,f as when};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require(`@vielzeug/stateit`);function t(...t){let n=t[t.length-1],r=typeof n==`function`&&!Array.isArray(n),i=r?t.slice(0,-1):t,a=r?n:void 0,o=t=>(0,e.isSignal)(t)?!!t.value:typeof t==`function`?!!t():!!t,s=()=>{for(let[e,t]of i)if(o(e))return t();return a?.()??``};return i.some(([t])=>(0,e.isSignal)(t)||typeof t==`function`)?s:s()}exports.match=t;
|
|
2
|
+
//# sourceMappingURL=match.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"match.cjs","names":[],"sources":["../../src/directives/match.ts"],"sourcesContent":["import { isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport type { HTMLResult } from '../core/internal';\n\ntype TemplateFn = () => string | HTMLResult;\ntype Branch = readonly [unknown, TemplateFn];\n\n/**\n * Multi-branch conditional rendering. Pass branches as individual `[condition, templateFn]`\n * tuples (variadic), with an optional trailing `fallback` function.\n * Renders the first truthy branch, or the fallback when no branch matches.\n *\n * Reactive when any condition is a Signal or getter function — the output updates\n * automatically whenever a reactive condition changes.\n *\n * @example\n * import { match } from '@vielzeug/craftit/directives';\n *\n * // Static — evaluated once\n * html`${match(\n * [isAdmin, () => html`<admin-panel>`],\n * [isModerator, () => html`<mod-panel>`],\n * () => html`<user-panel>`,\n * )}`\n *\n * // Reactive Signal conditions\n * html`${match(\n * [isAdmin, () => html`<admin-panel>`],\n * [isLoggedIn, () => html`<user-panel>`],\n * () => html`<guest-panel>`,\n * )}`\n */\nexport function match(...args: (Branch | TemplateFn)[]): TemplateFn | string | HTMLResult {\n // Separate branches from optional trailing fallback function.\n // A Branch is a tuple [condition, fn] (an array); a TemplateFn is a bare () => ... function.\n const lastArg = args[args.length - 1];\n const hasFallback = typeof lastArg === 'function' && !Array.isArray(lastArg);\n const branches = (hasFallback ? args.slice(0, -1) : args) as Branch[];\n const fallback = hasFallback ? (lastArg as TemplateFn) : undefined;\n\n const resolve = (cond: unknown): boolean => {\n if (isSignal(cond)) return !!(cond as ReadonlySignal<unknown>).value;\n\n if (typeof cond === 'function') return !!(cond as () => unknown)();\n\n return !!cond;\n };\n\n const evaluate = (): string | HTMLResult => {\n for (const [cond, fn] of branches) {\n if (resolve(cond)) return fn();\n }\n\n return fallback?.() ?? '';\n };\n\n const reactive = branches.some(([cond]) => isSignal(cond) || typeof cond === 'function');\n\n return reactive ? evaluate : evaluate();\n}\n"],"mappings":"mCAgCA,SAAgB,EAAM,GAAG,EAAiE,CAGxF,IAAM,EAAU,EAAK,EAAK,OAAS,GAC7B,EAAc,OAAO,GAAY,YAAc,CAAC,MAAM,QAAQ,EAAQ,CACtE,EAAY,EAAc,EAAK,MAAM,EAAG,GAAG,CAAG,EAC9C,EAAW,EAAe,EAAyB,IAAA,GAEnD,EAAW,IACf,EAAA,EAAA,UAAa,EAAK,CAAS,CAAC,CAAE,EAAiC,MAE3D,OAAO,GAAS,WAAmB,CAAC,CAAE,GAAwB,CAE3D,CAAC,CAAC,EAGL,MAAsC,CAC1C,IAAK,GAAM,CAAC,EAAM,KAAO,EACvB,GAAI,EAAQ,EAAK,CAAE,OAAO,GAAI,CAGhC,OAAO,KAAY,EAAI,IAKzB,OAFiB,EAAS,MAAM,CAAC,MAAA,EAAA,EAAA,UAAmB,EAAK,EAAI,OAAO,GAAS,WAAW,CAEtE,EAAW,GAAU"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { HTMLResult } from '../core/internal';
|
|
2
|
+
type TemplateFn = () => string | HTMLResult;
|
|
3
|
+
type Branch = readonly [unknown, TemplateFn];
|
|
4
|
+
/**
|
|
5
|
+
* Multi-branch conditional rendering. Pass branches as individual `[condition, templateFn]`
|
|
6
|
+
* tuples (variadic), with an optional trailing `fallback` function.
|
|
7
|
+
* Renders the first truthy branch, or the fallback when no branch matches.
|
|
8
|
+
*
|
|
9
|
+
* Reactive when any condition is a Signal or getter function — the output updates
|
|
10
|
+
* automatically whenever a reactive condition changes.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* import { match } from '@vielzeug/craftit/directives';
|
|
14
|
+
*
|
|
15
|
+
* // Static — evaluated once
|
|
16
|
+
* html`${match(
|
|
17
|
+
* [isAdmin, () => html`<admin-panel>`],
|
|
18
|
+
* [isModerator, () => html`<mod-panel>`],
|
|
19
|
+
* () => html`<user-panel>`,
|
|
20
|
+
* )}`
|
|
21
|
+
*
|
|
22
|
+
* // Reactive Signal conditions
|
|
23
|
+
* html`${match(
|
|
24
|
+
* [isAdmin, () => html`<admin-panel>`],
|
|
25
|
+
* [isLoggedIn, () => html`<user-panel>`],
|
|
26
|
+
* () => html`<guest-panel>`,
|
|
27
|
+
* )}`
|
|
28
|
+
*/
|
|
29
|
+
export declare function match(...args: (Branch | TemplateFn)[]): TemplateFn | string | HTMLResult;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=match.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"match.d.ts","sourceRoot":"","sources":["../../src/directives/match.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,KAAK,UAAU,GAAG,MAAM,MAAM,GAAG,UAAU,CAAC;AAC5C,KAAK,MAAM,GAAG,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,KAAK,CAAC,GAAG,IAAI,EAAE,CAAC,MAAM,GAAG,UAAU,CAAC,EAAE,GAAG,UAAU,GAAG,MAAM,GAAG,UAAU,CA2BxF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{isSignal as e}from"@vielzeug/stateit";function t(...t){let n=t[t.length-1],r=typeof n==`function`&&!Array.isArray(n),i=r?t.slice(0,-1):t,a=r?n:void 0,o=t=>e(t)?!!t.value:typeof t==`function`?!!t():!!t,s=()=>{for(let[e,t]of i)if(o(e))return t();return a?.()??``};return i.some(([t])=>e(t)||typeof t==`function`)?s:s()}export{t as match};
|
|
2
|
+
//# sourceMappingURL=match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"match.js","names":[],"sources":["../../src/directives/match.ts"],"sourcesContent":["import { isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport type { HTMLResult } from '../core/internal';\n\ntype TemplateFn = () => string | HTMLResult;\ntype Branch = readonly [unknown, TemplateFn];\n\n/**\n * Multi-branch conditional rendering. Pass branches as individual `[condition, templateFn]`\n * tuples (variadic), with an optional trailing `fallback` function.\n * Renders the first truthy branch, or the fallback when no branch matches.\n *\n * Reactive when any condition is a Signal or getter function — the output updates\n * automatically whenever a reactive condition changes.\n *\n * @example\n * import { match } from '@vielzeug/craftit/directives';\n *\n * // Static — evaluated once\n * html`${match(\n * [isAdmin, () => html`<admin-panel>`],\n * [isModerator, () => html`<mod-panel>`],\n * () => html`<user-panel>`,\n * )}`\n *\n * // Reactive Signal conditions\n * html`${match(\n * [isAdmin, () => html`<admin-panel>`],\n * [isLoggedIn, () => html`<user-panel>`],\n * () => html`<guest-panel>`,\n * )}`\n */\nexport function match(...args: (Branch | TemplateFn)[]): TemplateFn | string | HTMLResult {\n // Separate branches from optional trailing fallback function.\n // A Branch is a tuple [condition, fn] (an array); a TemplateFn is a bare () => ... function.\n const lastArg = args[args.length - 1];\n const hasFallback = typeof lastArg === 'function' && !Array.isArray(lastArg);\n const branches = (hasFallback ? args.slice(0, -1) : args) as Branch[];\n const fallback = hasFallback ? (lastArg as TemplateFn) : undefined;\n\n const resolve = (cond: unknown): boolean => {\n if (isSignal(cond)) return !!(cond as ReadonlySignal<unknown>).value;\n\n if (typeof cond === 'function') return !!(cond as () => unknown)();\n\n return !!cond;\n };\n\n const evaluate = (): string | HTMLResult => {\n for (const [cond, fn] of branches) {\n if (resolve(cond)) return fn();\n }\n\n return fallback?.() ?? '';\n };\n\n const reactive = branches.some(([cond]) => isSignal(cond) || typeof cond === 'function');\n\n return reactive ? evaluate : evaluate();\n}\n"],"mappings":"6CAgCA,SAAgB,EAAM,GAAG,EAAiE,CAGxF,IAAM,EAAU,EAAK,EAAK,OAAS,GAC7B,EAAc,OAAO,GAAY,YAAc,CAAC,MAAM,QAAQ,EAAQ,CACtE,EAAY,EAAc,EAAK,MAAM,EAAG,GAAG,CAAG,EAC9C,EAAW,EAAe,EAAyB,IAAA,GAEnD,EAAW,GACX,EAAS,EAAK,CAAS,CAAC,CAAE,EAAiC,MAE3D,OAAO,GAAS,WAAmB,CAAC,CAAE,GAAwB,CAE3D,CAAC,CAAC,EAGL,MAAsC,CAC1C,IAAK,GAAM,CAAC,EAAM,KAAO,EACvB,GAAI,EAAQ,EAAK,CAAE,OAAO,GAAI,CAGhC,OAAO,KAAY,EAAI,IAKzB,OAFiB,EAAS,MAAM,CAAC,KAAU,EAAS,EAAK,EAAI,OAAO,GAAS,WAAW,CAEtE,EAAW,GAAU"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memo.cjs","names":[],"sources":["../../src/directives/memo.ts"],"sourcesContent":["import { isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport type { HTMLResult } from '../core/internal';\n\ntype Dep = unknown | ReadonlySignal<unknown>;\n\n/**\n * Memoizes a template fragment — `templateFn` is only re-called when at least\n * one entry in `deps` has changed (compared with `Object.is`).\n *\n * Signal values in `deps` are automatically unwrapped and tracked, so changing a\n * dep signal triggers a re-evaluation. This is useful for skipping expensive\n * sub-tree renders when only unrelated state changes.\n *\n * @example\n * import { memo } from '@vielzeug/craftit/directives';\n *\n * // Only re-renders the table when `rows` actually changes\n * html`${memo([rows], () => html`<big-table :data=${rows}></big-table>`)}`\n *\n * // Multiple deps — re-renders when either changes\n * html`${memo([locale, theme], () => html`<themed-chart :locale=${locale}></themed-chart>`)}`\n */\nexport function memo(deps: ReadonlyArray<Dep>, templateFn: () => string | HTMLResult): () => string | HTMLResult {\n let cached: string | HTMLResult = '';\n let lastDeps: unknown[] = [];\n let initialized = false;\n\n return (): string | HTMLResult => {\n const current = deps.map((d) => (isSignal(d) ? (d as ReadonlySignal<unknown>).value : d));\n const changed =\n !initialized || current.length !== lastDeps.length || current.some((v, i) => !Object.is(v, lastDeps[i]));\n\n if (changed) {\n cached = templateFn();\n lastDeps = current;\n initialized = true;\n }\n\n return cached;\n };\n}\n"],"mappings":"mCAuBA,SAAgB,EAAK,EAA0B,EAAkE,CAC/G,IAAI,EAA8B,GAC9B,EAAsB,EAAE,CACxB,EAAc,GAElB,UAAkC,CAChC,IAAM,EAAU,EAAK,IAAK,IAAA,EAAA,EAAA,UAAgB,EAAE,CAAI,EAA8B,MAAQ,EAAG,CAUzF,OARE,CAAC,GAAe,EAAQ,SAAW,EAAS,QAAU,EAAQ,MAAM,EAAG,IAAM,CAAC,OAAO,GAAG,EAAG,EAAS,GAAG,CAAC,IAGxG,EAAS,GAAY,CACrB,EAAW,EACX,EAAc,IAGT"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ReadonlySignal } from '@vielzeug/stateit';
|
|
2
|
+
import type { HTMLResult } from '../core/internal';
|
|
3
|
+
type Dep = unknown | ReadonlySignal<unknown>;
|
|
4
|
+
/**
|
|
5
|
+
* Memoizes a template fragment — `templateFn` is only re-called when at least
|
|
6
|
+
* one entry in `deps` has changed (compared with `Object.is`).
|
|
7
|
+
*
|
|
8
|
+
* Signal values in `deps` are automatically unwrapped and tracked, so changing a
|
|
9
|
+
* dep signal triggers a re-evaluation. This is useful for skipping expensive
|
|
10
|
+
* sub-tree renders when only unrelated state changes.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* import { memo } from '@vielzeug/craftit/directives';
|
|
14
|
+
*
|
|
15
|
+
* // Only re-renders the table when `rows` actually changes
|
|
16
|
+
* html`${memo([rows], () => html`<big-table :data=${rows}></big-table>`)}`
|
|
17
|
+
*
|
|
18
|
+
* // Multiple deps — re-renders when either changes
|
|
19
|
+
* html`${memo([locale, theme], () => html`<themed-chart :locale=${locale}></themed-chart>`)}`
|
|
20
|
+
*/
|
|
21
|
+
export declare function memo(deps: ReadonlyArray<Dep>, templateFn: () => string | HTMLResult): () => string | HTMLResult;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=memo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memo.d.ts","sourceRoot":"","sources":["../../src/directives/memo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAElE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,KAAK,GAAG,GAAG,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;AAE7C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,MAAM,MAAM,GAAG,UAAU,GAAG,MAAM,MAAM,GAAG,UAAU,CAkB/G"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memo.js","names":[],"sources":["../../src/directives/memo.ts"],"sourcesContent":["import { isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport type { HTMLResult } from '../core/internal';\n\ntype Dep = unknown | ReadonlySignal<unknown>;\n\n/**\n * Memoizes a template fragment — `templateFn` is only re-called when at least\n * one entry in `deps` has changed (compared with `Object.is`).\n *\n * Signal values in `deps` are automatically unwrapped and tracked, so changing a\n * dep signal triggers a re-evaluation. This is useful for skipping expensive\n * sub-tree renders when only unrelated state changes.\n *\n * @example\n * import { memo } from '@vielzeug/craftit/directives';\n *\n * // Only re-renders the table when `rows` actually changes\n * html`${memo([rows], () => html`<big-table :data=${rows}></big-table>`)}`\n *\n * // Multiple deps — re-renders when either changes\n * html`${memo([locale, theme], () => html`<themed-chart :locale=${locale}></themed-chart>`)}`\n */\nexport function memo(deps: ReadonlyArray<Dep>, templateFn: () => string | HTMLResult): () => string | HTMLResult {\n let cached: string | HTMLResult = '';\n let lastDeps: unknown[] = [];\n let initialized = false;\n\n return (): string | HTMLResult => {\n const current = deps.map((d) => (isSignal(d) ? (d as ReadonlySignal<unknown>).value : d));\n const changed =\n !initialized || current.length !== lastDeps.length || current.some((v, i) => !Object.is(v, lastDeps[i]));\n\n if (changed) {\n cached = templateFn();\n lastDeps = current;\n initialized = true;\n }\n\n return cached;\n };\n}\n"],"mappings":"6CAuBA,SAAgB,EAAK,EAA0B,EAAkE,CAC/G,IAAI,EAA8B,GAC9B,EAAsB,EAAE,CACxB,EAAc,GAElB,UAAkC,CAChC,IAAM,EAAU,EAAK,IAAK,GAAO,EAAS,EAAE,CAAI,EAA8B,MAAQ,EAAG,CAUzF,OARE,CAAC,GAAe,EAAQ,SAAW,EAAS,QAAU,EAAQ,MAAM,EAAG,IAAM,CAAC,OAAO,GAAG,EAAG,EAAS,GAAG,CAAC,IAGxG,EAAS,GAAY,CACrB,EAAW,EACX,EAAc,IAGT"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`../core/utilities.cjs`);function t(t,n,r){return{mount(i,{registerCleanup:a}){if(t===`clickOutside`){let t=i.ownerDocument??document;a(e.listen(t,`click`,e=>{e.composedPath().includes(i)||n(e)},r))}else a(e.listen(i,t,n,r))}}}exports.on=t;
|
|
2
|
+
//# sourceMappingURL=on.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"on.cjs","names":[],"sources":["../../src/directives/on.ts"],"sourcesContent":["import type { Directive } from '../core/internal';\n\nimport { listen } from '../core/utilities';\n\n/**\n * Attaches an event listener to an element as a spread directive, supporting\n * full `AddEventListenerOptions` (e.g. `passive`, `once`, `capture`).\n *\n * **Synthetic event: `'clickOutside'`**\n * When the event name is `'clickOutside'`, the listener is attached to `document`\n * and fires whenever a click occurs *outside* the host element — useful for\n * closing dropdowns, modals, and popovers.\n *\n * @example\n * import { on } from '@vielzeug/craftit/directives';\n *\n * // Passive wheel listener (not possible with @wheel= template syntax)\n * html`<div ${on('wheel', onScroll, { passive: true })}></div>`\n *\n * // Typed handler — `e` inferred as `MouseEvent`\n * html`<button ${on('click', (e) => e.clientX)}></button>`\n *\n * // Click-outside (closes a dropdown when the user clicks away)\n * html`<div ${on('clickOutside', () => open.value = false)}></div>`\n */\nexport function on(\n event: 'clickOutside',\n handler: (e: MouseEvent) => void,\n options?: AddEventListenerOptions,\n): Directive;\nexport function on<K extends keyof HTMLElementEventMap>(\n event: K,\n handler: (e: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n): Directive;\nexport function on(event: string, handler: (e: any) => void, options?: AddEventListenerOptions): Directive {\n return {\n mount(el, { registerCleanup }) {\n if (event === 'clickOutside') {\n const doc = el.ownerDocument ?? document;\n const docHandler = (e: Event) => {\n if (!e.composedPath().includes(el)) handler(e as MouseEvent);\n };\n\n registerCleanup(listen(doc, 'click', docHandler, options));\n } else {\n registerCleanup(listen(el, event, handler, options));\n }\n },\n };\n}\n"],"mappings":"yCAmCA,SAAgB,EAAG,EAAe,EAA2B,EAA8C,CACzG,MAAO,CACL,MAAM,EAAI,CAAE,mBAAmB,CAC7B,GAAI,IAAU,eAAgB,CAC5B,IAAM,EAAM,EAAG,eAAiB,SAKhC,EAAgB,EAAA,OAAO,EAAK,QAJR,GAAa,CAC1B,EAAE,cAAc,CAAC,SAAS,EAAG,EAAE,EAAQ,EAAgB,EAGb,EAAQ,CAAC,MAE1D,EAAgB,EAAA,OAAO,EAAI,EAAO,EAAS,EAAQ,CAAC,EAGzD"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Directive } from '../core/internal';
|
|
2
|
+
/**
|
|
3
|
+
* Attaches an event listener to an element as a spread directive, supporting
|
|
4
|
+
* full `AddEventListenerOptions` (e.g. `passive`, `once`, `capture`).
|
|
5
|
+
*
|
|
6
|
+
* **Synthetic event: `'clickOutside'`**
|
|
7
|
+
* When the event name is `'clickOutside'`, the listener is attached to `document`
|
|
8
|
+
* and fires whenever a click occurs *outside* the host element — useful for
|
|
9
|
+
* closing dropdowns, modals, and popovers.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* import { on } from '@vielzeug/craftit/directives';
|
|
13
|
+
*
|
|
14
|
+
* // Passive wheel listener (not possible with @wheel= template syntax)
|
|
15
|
+
* html`<div ${on('wheel', onScroll, { passive: true })}></div>`
|
|
16
|
+
*
|
|
17
|
+
* // Typed handler — `e` inferred as `MouseEvent`
|
|
18
|
+
* html`<button ${on('click', (e) => e.clientX)}></button>`
|
|
19
|
+
*
|
|
20
|
+
* // Click-outside (closes a dropdown when the user clicks away)
|
|
21
|
+
* html`<div ${on('clickOutside', () => open.value = false)}></div>`
|
|
22
|
+
*/
|
|
23
|
+
export declare function on(event: 'clickOutside', handler: (e: MouseEvent) => void, options?: AddEventListenerOptions): Directive;
|
|
24
|
+
export declare function on<K extends keyof HTMLElementEventMap>(event: K, handler: (e: HTMLElementEventMap[K]) => void, options?: AddEventListenerOptions): Directive;
|
|
25
|
+
//# sourceMappingURL=on.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"on.d.ts","sourceRoot":"","sources":["../../src/directives/on.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAIlD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,EAAE,CAChB,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,EAChC,OAAO,CAAC,EAAE,uBAAuB,GAChC,SAAS,CAAC;AACb,wBAAgB,EAAE,CAAC,CAAC,SAAS,MAAM,mBAAmB,EACpD,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC,KAAK,IAAI,EAC5C,OAAO,CAAC,EAAE,uBAAuB,GAChC,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"on.js","names":[],"sources":["../../src/directives/on.ts"],"sourcesContent":["import type { Directive } from '../core/internal';\n\nimport { listen } from '../core/utilities';\n\n/**\n * Attaches an event listener to an element as a spread directive, supporting\n * full `AddEventListenerOptions` (e.g. `passive`, `once`, `capture`).\n *\n * **Synthetic event: `'clickOutside'`**\n * When the event name is `'clickOutside'`, the listener is attached to `document`\n * and fires whenever a click occurs *outside* the host element — useful for\n * closing dropdowns, modals, and popovers.\n *\n * @example\n * import { on } from '@vielzeug/craftit/directives';\n *\n * // Passive wheel listener (not possible with @wheel= template syntax)\n * html`<div ${on('wheel', onScroll, { passive: true })}></div>`\n *\n * // Typed handler — `e` inferred as `MouseEvent`\n * html`<button ${on('click', (e) => e.clientX)}></button>`\n *\n * // Click-outside (closes a dropdown when the user clicks away)\n * html`<div ${on('clickOutside', () => open.value = false)}></div>`\n */\nexport function on(\n event: 'clickOutside',\n handler: (e: MouseEvent) => void,\n options?: AddEventListenerOptions,\n): Directive;\nexport function on<K extends keyof HTMLElementEventMap>(\n event: K,\n handler: (e: HTMLElementEventMap[K]) => void,\n options?: AddEventListenerOptions,\n): Directive;\nexport function on(event: string, handler: (e: any) => void, options?: AddEventListenerOptions): Directive {\n return {\n mount(el, { registerCleanup }) {\n if (event === 'clickOutside') {\n const doc = el.ownerDocument ?? document;\n const docHandler = (e: Event) => {\n if (!e.composedPath().includes(el)) handler(e as MouseEvent);\n };\n\n registerCleanup(listen(doc, 'click', docHandler, options));\n } else {\n registerCleanup(listen(el, event, handler, options));\n }\n },\n };\n}\n"],"mappings":"8CAmCA,SAAgB,EAAG,EAAe,EAA2B,EAA8C,CACzG,MAAO,CACL,MAAM,EAAI,CAAE,mBAAmB,CAO3B,EANE,IAAU,eAMI,EALJ,EAAG,eAAiB,SAKJ,QAJR,GAAa,CAC1B,EAAE,cAAc,CAAC,SAAS,EAAG,EAAE,EAAQ,EAAgB,EAGb,EAAQ,CAEzC,EAAO,EAAI,EAAO,EAAS,EAAQ,CAAC,EAGzD"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`../core/internal.cjs`);let t=require(`@vielzeug/stateit`);function n(n){return(0,t.isSignal)(n)?(0,t.computed)(()=>e.htmlResult(n.value)):typeof n==`function`?(0,t.computed)(()=>e.htmlResult(n())):e.htmlResult(n)}exports.raw=n;
|
|
2
|
+
//# sourceMappingURL=raw.cjs.map
|
|
@@ -0,0 +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 '../core/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, writable Signals, and getter functions.\n * When reactive, the DOM is updated in-place whenever the value changes.\n *\n * @example\n * import { raw } from '@vielzeug/craftit/directives';\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 * // Getter\n * html`<div>${raw(() => sanitize(props.body.value))}</div>`\n */\nexport function raw(\n value: string | Signal<string> | ReadonlySignal<string> | (() => string),\n): HTMLResult | ReadonlySignal<HTMLResult> {\n if (isSignal(value)) {\n return computed(() => htmlResult((value as ReadonlySignal<string>).value));\n }\n\n if (typeof value === 'function') {\n return computed(() => htmlResult((value as () => string)()));\n }\n\n return htmlResult(value);\n}\n"],"mappings":"2EAyBA,SAAgB,EACd,EACyC,CASzC,OARA,EAAA,EAAA,UAAa,EAAM,EACjB,EAAA,EAAA,cAAsB,EAAA,WAAY,EAAiC,MAAM,CAAC,CAGxE,OAAO,GAAU,YACnB,EAAA,EAAA,cAAsB,EAAA,WAAY,GAAwB,CAAC,CAAC,CAGvD,EAAA,WAAW,EAAM"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ReadonlySignal, type Signal } from '@vielzeug/stateit';
|
|
2
|
+
import { type HTMLResult } from '../core/internal';
|
|
3
|
+
/**
|
|
4
|
+
* Renders a trusted HTML string without escaping.
|
|
5
|
+
* **Only use with content you control** — passing user-supplied strings
|
|
6
|
+
* directly is an XSS risk.
|
|
7
|
+
*
|
|
8
|
+
* Supports static strings, writable Signals, and getter functions.
|
|
9
|
+
* When reactive, the DOM is updated in-place whenever the value changes.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* import { raw } from '@vielzeug/craftit/directives';
|
|
13
|
+
*
|
|
14
|
+
* // Static
|
|
15
|
+
* html`<div>${raw('<strong>bold</strong>')}</div>`
|
|
16
|
+
*
|
|
17
|
+
* // Reactive signal
|
|
18
|
+
* const content = signal('<em>hello</em>');
|
|
19
|
+
* html`<div>${raw(content)}</div>`
|
|
20
|
+
*
|
|
21
|
+
* // Getter
|
|
22
|
+
* html`<div>${raw(() => sanitize(props.body.value))}</div>`
|
|
23
|
+
*/
|
|
24
|
+
export declare function raw(value: string | Signal<string> | ReadonlySignal<string> | (() => string)): HTMLResult | ReadonlySignal<HTMLResult>;
|
|
25
|
+
//# sourceMappingURL=raw.d.ts.map
|
|
@@ -0,0 +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,kBAAkB,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,GAAG,CACjB,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,GACvE,UAAU,GAAG,cAAc,CAAC,UAAU,CAAC,CAUzC"}
|
|
@@ -0,0 +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 '../core/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, writable Signals, and getter functions.\n * When reactive, the DOM is updated in-place whenever the value changes.\n *\n * @example\n * import { raw } from '@vielzeug/craftit/directives';\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 * // Getter\n * html`<div>${raw(() => sanitize(props.body.value))}</div>`\n */\nexport function raw(\n value: string | Signal<string> | ReadonlySignal<string> | (() => string),\n): HTMLResult | ReadonlySignal<HTMLResult> {\n if (isSignal(value)) {\n return computed(() => htmlResult((value as ReadonlySignal<string>).value));\n }\n\n if (typeof value === 'function') {\n return computed(() => htmlResult((value as () => string)()));\n }\n\n return htmlResult(value);\n}\n"],"mappings":"4GAyBA,SAAgB,EACd,EACyC,CASzC,OARI,EAAS,EAAM,CACV,MAAe,EAAY,EAAiC,MAAM,CAAC,CAGxE,OAAO,GAAU,WACZ,MAAe,EAAY,GAAwB,CAAC,CAAC,CAGvD,EAAW,EAAM"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
require(`../core/internal.cjs`);const e=require(`../core/utilities.cjs`),t=require(`../core/runtime-bindings.cjs`);let n=require(`@vielzeug/stateit`);var r=(e,t,r)=>e?(r((0,n.effect)(()=>{t(e.value)})),!0):!1,i=(t,n,r,i)=>n.startsWith(`@`)?(typeof r==`function`&&i(e.listen(t,n.slice(1),r)),!0):!1,a=(e,n,i,a)=>{if(!n.startsWith(`.`))return!1;let o=n.slice(1),s=t.hasWritableValueSetter(i)?i:void 0;return r(t.toReactiveBindingSource(i),t=>e[o]=t,a)||(e[o]=i),t.bindPropertyModel(e,o,s,a),!0},o=(e,n,i,a)=>{if(!n.startsWith(`?`))return!1;let o=n.slice(1);return r(t.toReactiveBindingSource(i),t=>e.toggleAttribute(o,!!t),a)||e.toggleAttribute(o,!!i),!0},s=(n,i,a,o)=>{r(t.toReactiveBindingSource(a),t=>e.setAttr(n,i,t),o)||e.setAttr(n,i,a)},c=(e,t,n,r)=>{n!==void 0&&(i(e,t,n,r)||a(e,t,n,r)||o(e,t,n,r)||s(e,t,n,r))};function l(e){return{mount(t,{registerCleanup:n}){for(let[r,i]of Object.entries(e))c(t,r,i,n)}}}exports.spread=l;
|
|
2
|
+
//# sourceMappingURL=spread.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spread.cjs","names":[],"sources":["../../src/directives/spread.ts"],"sourcesContent":["import { effect, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport { type Directive } from '../core/internal';\nimport { bindPropertyModel, hasWritableValueSetter, toReactiveBindingSource } from '../core/runtime-bindings';\nimport { listen, setAttr } from '../core/utilities';\n\nexport type SpreadValue =\n | string\n | number\n | boolean\n | null\n | undefined\n | ReadonlySignal<string | number | boolean | null | undefined>\n | (() => string | number | boolean | null | undefined)\n | ((event: Event) => void);\n\ntype RegisterCleanup = (fn: () => void) => void;\n\nconst applyReactiveValue = (\n source: ReadonlySignal<unknown> | undefined,\n apply: (value: unknown) => void,\n registerCleanup: RegisterCleanup,\n): boolean => {\n if (!source) return false;\n\n registerCleanup(\n effect(() => {\n apply(source.value);\n }),\n );\n\n return true;\n};\n\nconst applyEventEntry = (\n el: HTMLElement,\n rawKey: string,\n rawValue: SpreadValue,\n registerCleanup: RegisterCleanup,\n): boolean => {\n if (!rawKey.startsWith('@')) return false;\n\n if (typeof rawValue === 'function') {\n registerCleanup(listen(el, rawKey.slice(1), rawValue as (event: Event) => void));\n }\n\n return true;\n};\n\nconst applyPropertyEntry = (\n el: HTMLElement,\n rawKey: string,\n rawValue: SpreadValue,\n registerCleanup: RegisterCleanup,\n): boolean => {\n if (!rawKey.startsWith('.')) return false;\n\n const key = rawKey.slice(1);\n const writableModel = hasWritableValueSetter(rawValue) ? rawValue : undefined;\n const source = toReactiveBindingSource(rawValue);\n\n if (!applyReactiveValue(source, (value) => ((el as any)[key] = value), registerCleanup)) {\n (el as any)[key] = rawValue;\n }\n\n bindPropertyModel(el, key, writableModel, registerCleanup);\n\n return true;\n};\n\nconst applyBooleanAttributeEntry = (\n el: HTMLElement,\n rawKey: string,\n rawValue: SpreadValue,\n registerCleanup: RegisterCleanup,\n): boolean => {\n if (!rawKey.startsWith('?')) return false;\n\n const key = rawKey.slice(1);\n const source = toReactiveBindingSource(rawValue);\n\n if (!applyReactiveValue(source, (value) => el.toggleAttribute(key, Boolean(value)), registerCleanup)) {\n el.toggleAttribute(key, Boolean(rawValue));\n }\n\n return true;\n};\n\nconst applyAttributeEntry = (\n el: HTMLElement,\n rawKey: string,\n rawValue: SpreadValue,\n registerCleanup: RegisterCleanup,\n): void => {\n const source = toReactiveBindingSource(rawValue);\n\n if (!applyReactiveValue(source, (value) => setAttr(el, rawKey, value), registerCleanup)) {\n setAttr(el, rawKey, rawValue);\n }\n};\n\nconst applyEntry = (el: HTMLElement, rawKey: string, rawValue: SpreadValue, registerCleanup: RegisterCleanup): void => {\n if (rawValue === undefined) return;\n\n if (applyEventEntry(el, rawKey, rawValue, registerCleanup)) return;\n\n if (applyPropertyEntry(el, rawKey, rawValue, registerCleanup)) return;\n\n if (applyBooleanAttributeEntry(el, rawKey, rawValue, registerCleanup)) return;\n\n applyAttributeEntry(el, rawKey, rawValue, registerCleanup);\n};\n\n/**\n * Unified spread directive for attributes, properties, and events.\n *\n * Key prefixes:\n * - `name` -> attribute\n * - `?name` -> boolean attribute\n * - `.name` -> DOM property\n * - `@name` -> event listener\n */\nexport function spread(map: Record<string, SpreadValue>): Directive {\n return {\n mount(el, { registerCleanup }) {\n for (const [key, value] of Object.entries(map)) {\n applyEntry(el, key, value, registerCleanup);\n }\n },\n };\n}\n"],"mappings":"sJAkBA,IAAM,GACJ,EACA,EACA,IAEK,GAEL,GAAA,EAAA,EAAA,YACe,CACX,EAAM,EAAO,MAAM,EACnB,CACH,CAEM,IARa,GAWhB,GACJ,EACA,EACA,EACA,IAEK,EAAO,WAAW,IAAI,EAEvB,OAAO,GAAa,YACtB,EAAgB,EAAA,OAAO,EAAI,EAAO,MAAM,EAAE,CAAE,EAAmC,CAAC,CAG3E,IAN6B,GAShC,GACJ,EACA,EACA,EACA,IACY,CACZ,GAAI,CAAC,EAAO,WAAW,IAAI,CAAE,MAAO,GAEpC,IAAM,EAAM,EAAO,MAAM,EAAE,CACrB,EAAgB,EAAA,uBAAuB,EAAS,CAAG,EAAW,IAAA,GASpE,OANK,EAFU,EAAA,wBAAwB,EAAS,CAEf,GAAW,EAAY,GAAO,EAAQ,EAAgB,GACpF,EAAW,GAAO,GAGrB,EAAA,kBAAkB,EAAI,EAAK,EAAe,EAAgB,CAEnD,IAGH,GACJ,EACA,EACA,EACA,IACY,CACZ,GAAI,CAAC,EAAO,WAAW,IAAI,CAAE,MAAO,GAEpC,IAAM,EAAM,EAAO,MAAM,EAAE,CAO3B,OAJK,EAFU,EAAA,wBAAwB,EAAS,CAEf,GAAU,EAAG,gBAAgB,EAAK,EAAQ,EAAO,CAAE,EAAgB,EAClG,EAAG,gBAAgB,EAAK,EAAQ,EAAU,CAGrC,IAGH,GACJ,EACA,EACA,EACA,IACS,CAGJ,EAFU,EAAA,wBAAwB,EAAS,CAEf,GAAU,EAAA,QAAQ,EAAI,EAAQ,EAAM,CAAE,EAAgB,EACrF,EAAA,QAAQ,EAAI,EAAQ,EAAS,EAI3B,GAAc,EAAiB,EAAgB,EAAuB,IAA2C,CACjH,IAAa,IAAA,KAEb,EAAgB,EAAI,EAAQ,EAAU,EAAgB,EAEtD,EAAmB,EAAI,EAAQ,EAAU,EAAgB,EAEzD,EAA2B,EAAI,EAAQ,EAAU,EAAgB,EAErE,EAAoB,EAAI,EAAQ,EAAU,EAAgB,GAY5D,SAAgB,EAAO,EAA6C,CAClE,MAAO,CACL,MAAM,EAAI,CAAE,mBAAmB,CAC7B,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAI,CAC5C,EAAW,EAAI,EAAK,EAAO,EAAgB,EAGhD"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ReadonlySignal } from '@vielzeug/stateit';
|
|
2
|
+
import { type Directive } from '../core/internal';
|
|
3
|
+
export type SpreadValue = string | number | boolean | null | undefined | ReadonlySignal<string | number | boolean | null | undefined> | (() => string | number | boolean | null | undefined) | ((event: Event) => void);
|
|
4
|
+
/**
|
|
5
|
+
* Unified spread directive for attributes, properties, and events.
|
|
6
|
+
*
|
|
7
|
+
* Key prefixes:
|
|
8
|
+
* - `name` -> attribute
|
|
9
|
+
* - `?name` -> boolean attribute
|
|
10
|
+
* - `.name` -> DOM property
|
|
11
|
+
* - `@name` -> event listener
|
|
12
|
+
*/
|
|
13
|
+
export declare function spread(map: Record<string, SpreadValue>): Directive;
|
|
14
|
+
//# sourceMappingURL=spread.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spread.d.ts","sourceRoot":"","sources":["../../src/directives/spread.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEhE,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAIlD,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,GACT,cAAc,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,GAC5D,CAAC,MAAM,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,GACpD,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,CAAC;AAmG7B;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,SAAS,CAQlE"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import"../core/internal.js";import{listen as e,setAttr as t}from"../core/utilities.js";import{bindPropertyModel as n,hasWritableValueSetter as r,toReactiveBindingSource as i}from"../core/runtime-bindings.js";import{effect as a}from"@vielzeug/stateit";var o=(e,t,n)=>e?(n(a(()=>{t(e.value)})),!0):!1,s=(t,n,r,i)=>n.startsWith(`@`)?(typeof r==`function`&&i(e(t,n.slice(1),r)),!0):!1,c=(e,t,a,s)=>{if(!t.startsWith(`.`))return!1;let c=t.slice(1),l=r(a)?a:void 0;return o(i(a),t=>e[c]=t,s)||(e[c]=a),n(e,c,l,s),!0},l=(e,t,n,r)=>{if(!t.startsWith(`?`))return!1;let a=t.slice(1);return o(i(n),t=>e.toggleAttribute(a,!!t),r)||e.toggleAttribute(a,!!n),!0},u=(e,n,r,a)=>{o(i(r),r=>t(e,n,r),a)||t(e,n,r)},d=(e,t,n,r)=>{n!==void 0&&(s(e,t,n,r)||c(e,t,n,r)||l(e,t,n,r)||u(e,t,n,r))};function f(e){return{mount(t,{registerCleanup:n}){for(let[r,i]of Object.entries(e))d(t,r,i,n)}}}export{f as spread};
|
|
2
|
+
//# sourceMappingURL=spread.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spread.js","names":[],"sources":["../../src/directives/spread.ts"],"sourcesContent":["import { effect, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport { type Directive } from '../core/internal';\nimport { bindPropertyModel, hasWritableValueSetter, toReactiveBindingSource } from '../core/runtime-bindings';\nimport { listen, setAttr } from '../core/utilities';\n\nexport type SpreadValue =\n | string\n | number\n | boolean\n | null\n | undefined\n | ReadonlySignal<string | number | boolean | null | undefined>\n | (() => string | number | boolean | null | undefined)\n | ((event: Event) => void);\n\ntype RegisterCleanup = (fn: () => void) => void;\n\nconst applyReactiveValue = (\n source: ReadonlySignal<unknown> | undefined,\n apply: (value: unknown) => void,\n registerCleanup: RegisterCleanup,\n): boolean => {\n if (!source) return false;\n\n registerCleanup(\n effect(() => {\n apply(source.value);\n }),\n );\n\n return true;\n};\n\nconst applyEventEntry = (\n el: HTMLElement,\n rawKey: string,\n rawValue: SpreadValue,\n registerCleanup: RegisterCleanup,\n): boolean => {\n if (!rawKey.startsWith('@')) return false;\n\n if (typeof rawValue === 'function') {\n registerCleanup(listen(el, rawKey.slice(1), rawValue as (event: Event) => void));\n }\n\n return true;\n};\n\nconst applyPropertyEntry = (\n el: HTMLElement,\n rawKey: string,\n rawValue: SpreadValue,\n registerCleanup: RegisterCleanup,\n): boolean => {\n if (!rawKey.startsWith('.')) return false;\n\n const key = rawKey.slice(1);\n const writableModel = hasWritableValueSetter(rawValue) ? rawValue : undefined;\n const source = toReactiveBindingSource(rawValue);\n\n if (!applyReactiveValue(source, (value) => ((el as any)[key] = value), registerCleanup)) {\n (el as any)[key] = rawValue;\n }\n\n bindPropertyModel(el, key, writableModel, registerCleanup);\n\n return true;\n};\n\nconst applyBooleanAttributeEntry = (\n el: HTMLElement,\n rawKey: string,\n rawValue: SpreadValue,\n registerCleanup: RegisterCleanup,\n): boolean => {\n if (!rawKey.startsWith('?')) return false;\n\n const key = rawKey.slice(1);\n const source = toReactiveBindingSource(rawValue);\n\n if (!applyReactiveValue(source, (value) => el.toggleAttribute(key, Boolean(value)), registerCleanup)) {\n el.toggleAttribute(key, Boolean(rawValue));\n }\n\n return true;\n};\n\nconst applyAttributeEntry = (\n el: HTMLElement,\n rawKey: string,\n rawValue: SpreadValue,\n registerCleanup: RegisterCleanup,\n): void => {\n const source = toReactiveBindingSource(rawValue);\n\n if (!applyReactiveValue(source, (value) => setAttr(el, rawKey, value), registerCleanup)) {\n setAttr(el, rawKey, rawValue);\n }\n};\n\nconst applyEntry = (el: HTMLElement, rawKey: string, rawValue: SpreadValue, registerCleanup: RegisterCleanup): void => {\n if (rawValue === undefined) return;\n\n if (applyEventEntry(el, rawKey, rawValue, registerCleanup)) return;\n\n if (applyPropertyEntry(el, rawKey, rawValue, registerCleanup)) return;\n\n if (applyBooleanAttributeEntry(el, rawKey, rawValue, registerCleanup)) return;\n\n applyAttributeEntry(el, rawKey, rawValue, registerCleanup);\n};\n\n/**\n * Unified spread directive for attributes, properties, and events.\n *\n * Key prefixes:\n * - `name` -> attribute\n * - `?name` -> boolean attribute\n * - `.name` -> DOM property\n * - `@name` -> event listener\n */\nexport function spread(map: Record<string, SpreadValue>): Directive {\n return {\n mount(el, { registerCleanup }) {\n for (const [key, value] of Object.entries(map)) {\n applyEntry(el, key, value, registerCleanup);\n }\n },\n };\n}\n"],"mappings":"2PAkBA,IAAM,GACJ,EACA,EACA,IAEK,GAEL,EACE,MAAa,CACX,EAAM,EAAO,MAAM,EACnB,CACH,CAEM,IARa,GAWhB,GACJ,EACA,EACA,EACA,IAEK,EAAO,WAAW,IAAI,EAEvB,OAAO,GAAa,YACtB,EAAgB,EAAO,EAAI,EAAO,MAAM,EAAE,CAAE,EAAmC,CAAC,CAG3E,IAN6B,GAShC,GACJ,EACA,EACA,EACA,IACY,CACZ,GAAI,CAAC,EAAO,WAAW,IAAI,CAAE,MAAO,GAEpC,IAAM,EAAM,EAAO,MAAM,EAAE,CACrB,EAAgB,EAAuB,EAAS,CAAG,EAAW,IAAA,GASpE,OANK,EAFU,EAAwB,EAAS,CAEf,GAAW,EAAY,GAAO,EAAQ,EAAgB,GACpF,EAAW,GAAO,GAGrB,EAAkB,EAAI,EAAK,EAAe,EAAgB,CAEnD,IAGH,GACJ,EACA,EACA,EACA,IACY,CACZ,GAAI,CAAC,EAAO,WAAW,IAAI,CAAE,MAAO,GAEpC,IAAM,EAAM,EAAO,MAAM,EAAE,CAO3B,OAJK,EAFU,EAAwB,EAAS,CAEf,GAAU,EAAG,gBAAgB,EAAK,EAAQ,EAAO,CAAE,EAAgB,EAClG,EAAG,gBAAgB,EAAK,EAAQ,EAAU,CAGrC,IAGH,GACJ,EACA,EACA,EACA,IACS,CAGJ,EAFU,EAAwB,EAAS,CAEf,GAAU,EAAQ,EAAI,EAAQ,EAAM,CAAE,EAAgB,EACrF,EAAQ,EAAI,EAAQ,EAAS,EAI3B,GAAc,EAAiB,EAAgB,EAAuB,IAA2C,CACjH,IAAa,IAAA,KAEb,EAAgB,EAAI,EAAQ,EAAU,EAAgB,EAEtD,EAAmB,EAAI,EAAQ,EAAU,EAAgB,EAEzD,EAA2B,EAAI,EAAQ,EAAU,EAAgB,EAErE,EAAoB,EAAI,EAAQ,EAAU,EAAgB,GAY5D,SAAgB,EAAO,EAA6C,CAClE,MAAO,CACL,MAAM,EAAI,CAAE,mBAAmB,CAC7B,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAI,CAC5C,EAAW,EAAI,EAAK,EAAO,EAAgB,EAGhD"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`../core/utilities.cjs`);let t=require(`@vielzeug/stateit`);var n=new Set([`animationIterationCount`,`columnCount`,`flex`,`flexGrow`,`flexShrink`,`fontWeight`,`gridColumn`,`gridRow`,`lineHeight`,`opacity`,`order`,`orphans`,`tabSize`,`widows`,`zIndex`]),r=(e,t)=>typeof t==`number`&&!n.has(e)?`${t}px`:String(t);function i(n){let i=Object.entries(n),a=i.some(([,e])=>(0,t.isSignal)(e)||typeof e==`function`),o=()=>{let n=[];for(let[a,o]of i){let i=(0,t.isSignal)(o)?o.value:typeof o==`function`?o():o;i!=null&&i!==``&&n.push(`${e.toKebab(a)}:${r(a,i)}`)}return n.join(`;`)};return a?(0,t.computed)(o):o()}exports.style=i;
|
|
2
|
+
//# sourceMappingURL=style.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style.cjs","names":[],"sources":["../../src/directives/style.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport { toKebab } from '../core/utilities';\n\ntype StyleValue =\n | string\n | number\n | undefined\n | null\n | Signal<string | number>\n | ReadonlySignal<string | number>\n | (() => string | number | null | undefined);\n\n/** Properties that are unitless — numbers are NOT suffixed with 'px'. */\nconst UNITLESS = new Set([\n 'animationIterationCount',\n 'columnCount',\n 'flex',\n 'flexGrow',\n 'flexShrink',\n 'fontWeight',\n 'gridColumn',\n 'gridRow',\n 'lineHeight',\n 'opacity',\n 'order',\n 'orphans',\n 'tabSize',\n 'widows',\n 'zIndex',\n]);\n\nconst toCssValue = (prop: string, val: string | number): string => {\n if (typeof val === 'number' && !UNITLESS.has(prop)) return `${val}px`;\n\n return String(val);\n};\n\n/**\n * Build a dynamic inline style string from an object map of CSS properties to values.\n * Supports camelCase property names (auto-converted to kebab-case). Number values\n * automatically get a `px` suffix except for unitless properties (opacity, zIndex, etc.).\n *\n * When any value is a reactive Signal or getter function the returned value is a\n * `ReadonlySignal<string>` that updates automatically — no arrow-function wrapper needed.\n *\n * @example\n * import { style } from '@vielzeug/craftit/directives';\n *\n * // Static — returns a plain string\n * html`<div style=${style({ color: 'red', fontSize: 16 })}></div>`\n *\n * // Reactive — returns a signal\n * html`<div style=${style({ fontSize: size, color: theme })}></div>`\n */\nexport function style(map: Record<string, StyleValue>): string | ReadonlySignal<string> {\n const entries = Object.entries(map);\n const hasReactive = entries.some(([, v]) => isSignal(v) || typeof v === 'function');\n\n const build = (): string => {\n const parts: string[] = [];\n\n for (const [k, v] of entries) {\n const raw = isSignal(v)\n ? (v as ReadonlySignal<string | number>).value\n : typeof v === 'function'\n ? (v as () => string | number | null | undefined)()\n : v;\n\n if (raw != null && raw !== '') parts.push(`${toKebab(k)}:${toCssValue(k, raw as string | number)}`);\n }\n\n return parts.join(';');\n };\n\n return hasReactive ? computed(build) : build();\n}\n"],"mappings":"4EAcA,IAAM,EAAW,IAAI,IAAI,CACvB,0BACA,cACA,OACA,WACA,aACA,aACA,aACA,UACA,aACA,UACA,QACA,UACA,UACA,SACA,SACD,CAAC,CAEI,GAAc,EAAc,IAC5B,OAAO,GAAQ,UAAY,CAAC,EAAS,IAAI,EAAK,CAAS,GAAG,EAAI,IAE3D,OAAO,EAAI,CAoBpB,SAAgB,EAAM,EAAkE,CACtF,IAAM,EAAU,OAAO,QAAQ,EAAI,CAC7B,EAAc,EAAQ,MAAM,EAAG,MAAA,EAAA,EAAA,UAAgB,EAAE,EAAI,OAAO,GAAM,WAAW,CAE7E,MAAsB,CAC1B,IAAM,EAAkB,EAAE,CAE1B,IAAK,GAAM,CAAC,EAAG,KAAM,EAAS,CAC5B,IAAM,GAAA,EAAA,EAAA,UAAe,EAAE,CAClB,EAAsC,MACvC,OAAO,GAAM,WACV,GAAgD,CACjD,EAEF,GAAO,MAAQ,IAAQ,IAAI,EAAM,KAAK,GAAG,EAAA,QAAQ,EAAE,CAAC,GAAG,EAAW,EAAG,EAAuB,GAAG,CAGrG,OAAO,EAAM,KAAK,IAAI,EAGxB,OAAO,GAAA,EAAA,EAAA,UAAuB,EAAM,CAAG,GAAO"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ReadonlySignal, type Signal } from '@vielzeug/stateit';
|
|
2
|
+
type StyleValue = string | number | undefined | null | Signal<string | number> | ReadonlySignal<string | number> | (() => string | number | null | undefined);
|
|
3
|
+
/**
|
|
4
|
+
* Build a dynamic inline style string from an object map of CSS properties to values.
|
|
5
|
+
* Supports camelCase property names (auto-converted to kebab-case). Number values
|
|
6
|
+
* automatically get a `px` suffix except for unitless properties (opacity, zIndex, etc.).
|
|
7
|
+
*
|
|
8
|
+
* When any value is a reactive Signal or getter function the returned value is a
|
|
9
|
+
* `ReadonlySignal<string>` that updates automatically — no arrow-function wrapper needed.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* import { style } from '@vielzeug/craftit/directives';
|
|
13
|
+
*
|
|
14
|
+
* // Static — returns a plain string
|
|
15
|
+
* html`<div style=${style({ color: 'red', fontSize: 16 })}></div>`
|
|
16
|
+
*
|
|
17
|
+
* // Reactive — returns a signal
|
|
18
|
+
* html`<div style=${style({ fontSize: size, color: theme })}></div>`
|
|
19
|
+
*/
|
|
20
|
+
export declare function style(map: Record<string, StyleValue>): string | ReadonlySignal<string>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=style.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../../src/directives/style.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAAE,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAIzF,KAAK,UAAU,GACX,MAAM,GACN,MAAM,GACN,SAAS,GACT,IAAI,GACJ,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,GACvB,cAAc,CAAC,MAAM,GAAG,MAAM,CAAC,GAC/B,CAAC,MAAM,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AA2B/C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAqBtF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{toKebab as e}from"../core/utilities.js";import{computed as t,isSignal as n}from"@vielzeug/stateit";var r=new Set([`animationIterationCount`,`columnCount`,`flex`,`flexGrow`,`flexShrink`,`fontWeight`,`gridColumn`,`gridRow`,`lineHeight`,`opacity`,`order`,`orphans`,`tabSize`,`widows`,`zIndex`]),i=(e,t)=>typeof t==`number`&&!r.has(e)?`${t}px`:String(t);function a(r){let a=Object.entries(r),o=a.some(([,e])=>n(e)||typeof e==`function`),s=()=>{let t=[];for(let[r,o]of a){let a=n(o)?o.value:typeof o==`function`?o():o;a!=null&&a!==``&&t.push(`${e(r)}:${i(r,a)}`)}return t.join(`;`)};return o?t(s):s()}export{a as style};
|
|
2
|
+
//# sourceMappingURL=style.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style.js","names":[],"sources":["../../src/directives/style.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport { toKebab } from '../core/utilities';\n\ntype StyleValue =\n | string\n | number\n | undefined\n | null\n | Signal<string | number>\n | ReadonlySignal<string | number>\n | (() => string | number | null | undefined);\n\n/** Properties that are unitless — numbers are NOT suffixed with 'px'. */\nconst UNITLESS = new Set([\n 'animationIterationCount',\n 'columnCount',\n 'flex',\n 'flexGrow',\n 'flexShrink',\n 'fontWeight',\n 'gridColumn',\n 'gridRow',\n 'lineHeight',\n 'opacity',\n 'order',\n 'orphans',\n 'tabSize',\n 'widows',\n 'zIndex',\n]);\n\nconst toCssValue = (prop: string, val: string | number): string => {\n if (typeof val === 'number' && !UNITLESS.has(prop)) return `${val}px`;\n\n return String(val);\n};\n\n/**\n * Build a dynamic inline style string from an object map of CSS properties to values.\n * Supports camelCase property names (auto-converted to kebab-case). Number values\n * automatically get a `px` suffix except for unitless properties (opacity, zIndex, etc.).\n *\n * When any value is a reactive Signal or getter function the returned value is a\n * `ReadonlySignal<string>` that updates automatically — no arrow-function wrapper needed.\n *\n * @example\n * import { style } from '@vielzeug/craftit/directives';\n *\n * // Static — returns a plain string\n * html`<div style=${style({ color: 'red', fontSize: 16 })}></div>`\n *\n * // Reactive — returns a signal\n * html`<div style=${style({ fontSize: size, color: theme })}></div>`\n */\nexport function style(map: Record<string, StyleValue>): string | ReadonlySignal<string> {\n const entries = Object.entries(map);\n const hasReactive = entries.some(([, v]) => isSignal(v) || typeof v === 'function');\n\n const build = (): string => {\n const parts: string[] = [];\n\n for (const [k, v] of entries) {\n const raw = isSignal(v)\n ? (v as ReadonlySignal<string | number>).value\n : typeof v === 'function'\n ? (v as () => string | number | null | undefined)()\n : v;\n\n if (raw != null && raw !== '') parts.push(`${toKebab(k)}:${toCssValue(k, raw as string | number)}`);\n }\n\n return parts.join(';');\n };\n\n return hasReactive ? computed(build) : build();\n}\n"],"mappings":"0GAcA,IAAM,EAAW,IAAI,IAAI,CACvB,0BACA,cACA,OACA,WACA,aACA,aACA,aACA,UACA,aACA,UACA,QACA,UACA,UACA,SACA,SACD,CAAC,CAEI,GAAc,EAAc,IAC5B,OAAO,GAAQ,UAAY,CAAC,EAAS,IAAI,EAAK,CAAS,GAAG,EAAI,IAE3D,OAAO,EAAI,CAoBpB,SAAgB,EAAM,EAAkE,CACtF,IAAM,EAAU,OAAO,QAAQ,EAAI,CAC7B,EAAc,EAAQ,MAAM,EAAG,KAAO,EAAS,EAAE,EAAI,OAAO,GAAM,WAAW,CAE7E,MAAsB,CAC1B,IAAM,EAAkB,EAAE,CAE1B,IAAK,GAAM,CAAC,EAAG,KAAM,EAAS,CAC5B,IAAM,EAAM,EAAS,EAAE,CAClB,EAAsC,MACvC,OAAO,GAAM,WACV,GAAgD,CACjD,EAEF,GAAO,MAAQ,IAAQ,IAAI,EAAM,KAAK,GAAG,EAAQ,EAAE,CAAC,GAAG,EAAW,EAAG,EAAuB,GAAG,CAGrG,OAAO,EAAM,KAAK,IAAI,EAGxB,OAAO,EAAc,EAAS,EAAM,CAAG,GAAO"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require(`@vielzeug/stateit`);function t(t,n,r){let i=(0,e.signal)({done:!1});return t.then(e=>{i.value={done:!0,value:e}},e=>{r?i.value={done:!0,value:r(e)}:i.value={done:!0,value:`Error: ${String(e)}`}}),()=>i.value.done?i.value.value:n?.()??``}exports.until=t;
|
|
2
|
+
//# sourceMappingURL=until.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"until.cjs","names":[],"sources":["../../src/directives/until.ts"],"sourcesContent":["import { signal } from '@vielzeug/stateit';\n\nimport type { HTMLResult } from '../core/internal';\n\ntype State = { done: false } | { done: true; value: string | HTMLResult };\n\n/**\n * Renders `pendingFn` while a Promise is pending, then switches to the resolved\n * result. Lighter-weight alternative to `suspense()` for one-shot data loading\n * with no error/retry UI.\n *\n * Returns a reactive getter the engine tracks automatically — no manual signal\n * management needed at the call site.\n *\n * @param promise The promise to await. Should already resolve to a renderable value.\n * @param pendingFn Optional function called while the promise is pending.\n * @param onError Optional function called when the promise rejects. Receives the rejection reason.\n *\n * @example\n * import { until } from '@vielzeug/craftit/directives';\n *\n * const data = fetch('/api/user').then(r => r.json());\n *\n * html`${until(\n * data.then(u => html`<p>Hello, ${u.name}!</p>`),\n * () => html`<p>Loading…</p>`,\n * (err) => html`<p>Error: ${String(err)}</p>`,\n * )}`\n */\nexport function until(\n promise: Promise<string | HTMLResult>,\n pendingFn?: () => string | HTMLResult,\n onError?: (err: unknown) => string | HTMLResult,\n): () => string | HTMLResult {\n const state = signal<State>({ done: false });\n\n promise.then(\n (val) => {\n state.value = { done: true, value: val };\n },\n (err) => {\n if (onError) {\n state.value = { done: true, value: onError(err) };\n } else {\n state.value = { done: true, value: `Error: ${String(err)}` };\n }\n },\n );\n\n return () => (state.value.done ? state.value.value : (pendingFn?.() ?? ''));\n}\n"],"mappings":"mCA6BA,SAAgB,EACd,EACA,EACA,EAC2B,CAC3B,IAAM,GAAA,EAAA,EAAA,QAAsB,CAAE,KAAM,GAAO,CAAC,CAe5C,OAbA,EAAQ,KACL,GAAQ,CACP,EAAM,MAAQ,CAAE,KAAM,GAAM,MAAO,EAAK,EAEzC,GAAQ,CACH,EACF,EAAM,MAAQ,CAAE,KAAM,GAAM,MAAO,EAAQ,EAAI,CAAE,CAEjD,EAAM,MAAQ,CAAE,KAAM,GAAM,MAAO,UAAU,OAAO,EAAI,GAAI,EAGjE,KAEa,EAAM,MAAM,KAAO,EAAM,MAAM,MAAS,KAAa,EAAI"}
|