@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.
Files changed (214) hide show
  1. package/README.md +112 -401
  2. package/dist/core/component.cjs +2 -0
  3. package/dist/core/component.cjs.map +1 -0
  4. package/dist/core/component.d.ts +172 -0
  5. package/dist/core/component.d.ts.map +1 -0
  6. package/dist/core/component.js +2 -0
  7. package/dist/core/component.js.map +1 -0
  8. package/dist/core/host.cjs +2 -0
  9. package/dist/core/host.cjs.map +1 -0
  10. package/dist/core/host.d.ts +77 -0
  11. package/dist/core/host.d.ts.map +1 -0
  12. package/dist/core/host.js +2 -0
  13. package/dist/core/host.js.map +1 -0
  14. package/dist/core/internal.cjs +2 -0
  15. package/dist/core/internal.cjs.map +1 -0
  16. package/dist/core/internal.d.ts +107 -0
  17. package/dist/core/internal.d.ts.map +1 -0
  18. package/dist/core/internal.js +2 -0
  19. package/dist/core/internal.js.map +1 -0
  20. package/dist/core/runtime-bindings.cjs +2 -0
  21. package/dist/core/runtime-bindings.cjs.map +1 -0
  22. package/dist/core/runtime-bindings.d.ts +6 -0
  23. package/dist/core/runtime-bindings.d.ts.map +1 -0
  24. package/dist/core/runtime-bindings.js +2 -0
  25. package/dist/core/runtime-bindings.js.map +1 -0
  26. package/dist/core/runtime-lifecycle.cjs +2 -0
  27. package/dist/core/runtime-lifecycle.cjs.map +1 -0
  28. package/dist/core/runtime-lifecycle.d.ts +116 -0
  29. package/dist/core/runtime-lifecycle.d.ts.map +1 -0
  30. package/dist/core/runtime-lifecycle.js +2 -0
  31. package/dist/core/runtime-lifecycle.js.map +1 -0
  32. package/dist/core/runtime.cjs +1 -0
  33. package/dist/core/runtime.d.ts +3 -0
  34. package/dist/core/runtime.d.ts.map +1 -0
  35. package/dist/core/runtime.js +1 -0
  36. package/dist/core/template-bindings.cjs +2 -0
  37. package/dist/core/template-bindings.cjs.map +1 -0
  38. package/dist/core/template-bindings.d.ts +59 -0
  39. package/dist/core/template-bindings.d.ts.map +1 -0
  40. package/dist/core/template-bindings.js +2 -0
  41. package/dist/core/template-bindings.js.map +1 -0
  42. package/dist/core/template-compiler.cjs +2 -0
  43. package/dist/core/template-compiler.cjs.map +1 -0
  44. package/dist/core/template-compiler.d.ts +25 -0
  45. package/dist/core/template-compiler.d.ts.map +1 -0
  46. package/dist/core/template-compiler.js +2 -0
  47. package/dist/core/template-compiler.js.map +1 -0
  48. package/dist/core/template-dom.cjs +2 -0
  49. package/dist/core/template-dom.cjs.map +1 -0
  50. package/dist/core/template-dom.d.ts +13 -0
  51. package/dist/core/template-dom.d.ts.map +1 -0
  52. package/dist/core/template-dom.js +2 -0
  53. package/dist/core/template-dom.js.map +1 -0
  54. package/dist/core/template-html.cjs +2 -0
  55. package/dist/core/template-html.cjs.map +1 -0
  56. package/dist/core/template-html.d.ts +26 -0
  57. package/dist/core/template-html.d.ts.map +1 -0
  58. package/dist/core/template-html.js +2 -0
  59. package/dist/core/template-html.js.map +1 -0
  60. package/dist/core/template.cjs +2 -0
  61. package/dist/core/template.cjs.map +1 -0
  62. package/dist/core/template.d.ts +11 -0
  63. package/dist/core/template.d.ts.map +1 -0
  64. package/dist/core/template.js +2 -0
  65. package/dist/core/template.js.map +1 -0
  66. package/dist/core/utilities.cjs +2 -0
  67. package/dist/core/utilities.cjs.map +1 -0
  68. package/dist/core/utilities.d.ts +68 -0
  69. package/dist/core/utilities.d.ts.map +1 -0
  70. package/dist/core/utilities.js +2 -0
  71. package/dist/core/utilities.js.map +1 -0
  72. package/dist/craftit.cjs +2 -18
  73. package/dist/craftit.cjs.map +1 -1
  74. package/dist/craftit.js +2 -580
  75. package/dist/craftit.js.map +1 -1
  76. package/dist/directives/attr.cjs +2 -0
  77. package/dist/directives/attr.cjs.map +1 -0
  78. package/dist/directives/attr.d.ts +14 -0
  79. package/dist/directives/attr.d.ts.map +1 -0
  80. package/dist/directives/attr.js +2 -0
  81. package/dist/directives/attr.js.map +1 -0
  82. package/dist/directives/bind.cjs +2 -0
  83. package/dist/directives/bind.cjs.map +1 -0
  84. package/dist/directives/bind.d.ts +30 -0
  85. package/dist/directives/bind.d.ts.map +1 -0
  86. package/dist/directives/bind.js +2 -0
  87. package/dist/directives/bind.js.map +1 -0
  88. package/dist/directives/choose.cjs +2 -0
  89. package/dist/directives/choose.cjs.map +1 -0
  90. package/dist/directives/choose.d.ts +34 -0
  91. package/dist/directives/choose.d.ts.map +1 -0
  92. package/dist/directives/choose.js +2 -0
  93. package/dist/directives/choose.js.map +1 -0
  94. package/dist/directives/classes.cjs +2 -0
  95. package/dist/directives/classes.cjs.map +1 -0
  96. package/dist/directives/classes.d.ts +20 -0
  97. package/dist/directives/classes.d.ts.map +1 -0
  98. package/dist/directives/classes.js +2 -0
  99. package/dist/directives/classes.js.map +1 -0
  100. package/dist/directives/each.cjs +2 -0
  101. package/dist/directives/each.cjs.map +1 -0
  102. package/dist/directives/each.d.ts +68 -0
  103. package/dist/directives/each.d.ts.map +1 -0
  104. package/dist/directives/each.js +2 -0
  105. package/dist/directives/each.js.map +1 -0
  106. package/dist/directives/index.cjs +1 -0
  107. package/dist/directives/index.d.ts +14 -0
  108. package/dist/directives/index.d.ts.map +1 -0
  109. package/dist/directives/index.js +1 -0
  110. package/dist/directives/match.cjs +2 -0
  111. package/dist/directives/match.cjs.map +1 -0
  112. package/dist/directives/match.d.ts +31 -0
  113. package/dist/directives/match.d.ts.map +1 -0
  114. package/dist/directives/match.js +2 -0
  115. package/dist/directives/match.js.map +1 -0
  116. package/dist/directives/memo.cjs +2 -0
  117. package/dist/directives/memo.cjs.map +1 -0
  118. package/dist/directives/memo.d.ts +23 -0
  119. package/dist/directives/memo.d.ts.map +1 -0
  120. package/dist/directives/memo.js +2 -0
  121. package/dist/directives/memo.js.map +1 -0
  122. package/dist/directives/on.cjs +2 -0
  123. package/dist/directives/on.cjs.map +1 -0
  124. package/dist/directives/on.d.ts +25 -0
  125. package/dist/directives/on.d.ts.map +1 -0
  126. package/dist/directives/on.js +2 -0
  127. package/dist/directives/on.js.map +1 -0
  128. package/dist/directives/raw.cjs +2 -0
  129. package/dist/directives/raw.cjs.map +1 -0
  130. package/dist/directives/raw.d.ts +25 -0
  131. package/dist/directives/raw.d.ts.map +1 -0
  132. package/dist/directives/raw.js +2 -0
  133. package/dist/directives/raw.js.map +1 -0
  134. package/dist/directives/spread.cjs +2 -0
  135. package/dist/directives/spread.cjs.map +1 -0
  136. package/dist/directives/spread.d.ts +14 -0
  137. package/dist/directives/spread.d.ts.map +1 -0
  138. package/dist/directives/spread.js +2 -0
  139. package/dist/directives/spread.js.map +1 -0
  140. package/dist/directives/style.cjs +2 -0
  141. package/dist/directives/style.cjs.map +1 -0
  142. package/dist/directives/style.d.ts +22 -0
  143. package/dist/directives/style.d.ts.map +1 -0
  144. package/dist/directives/style.js +2 -0
  145. package/dist/directives/style.js.map +1 -0
  146. package/dist/directives/until.cjs +2 -0
  147. package/dist/directives/until.cjs.map +1 -0
  148. package/dist/directives/until.d.ts +26 -0
  149. package/dist/directives/until.d.ts.map +1 -0
  150. package/dist/directives/until.js +2 -0
  151. package/dist/directives/until.js.map +1 -0
  152. package/dist/directives/when.cjs +2 -0
  153. package/dist/directives/when.cjs.map +1 -0
  154. package/dist/directives/when.d.ts +17 -0
  155. package/dist/directives/when.d.ts.map +1 -0
  156. package/dist/directives/when.js +2 -0
  157. package/dist/directives/when.js.map +1 -0
  158. package/dist/index.cjs +1 -2
  159. package/dist/index.d.ts +10 -265
  160. package/dist/index.d.ts.map +1 -0
  161. package/dist/index.js +1 -13
  162. package/dist/labs/a11y.cjs +2 -0
  163. package/dist/labs/a11y.cjs.map +1 -0
  164. package/dist/labs/a11y.d.ts +61 -0
  165. package/dist/labs/a11y.d.ts.map +1 -0
  166. package/dist/labs/a11y.js +2 -0
  167. package/dist/labs/a11y.js.map +1 -0
  168. package/dist/labs/index.d.ts +8 -0
  169. package/dist/labs/index.d.ts.map +1 -0
  170. package/dist/labs/list.cjs +2 -0
  171. package/dist/labs/list.cjs.map +1 -0
  172. package/dist/labs/list.d.ts +26 -0
  173. package/dist/labs/list.d.ts.map +1 -0
  174. package/dist/labs/list.js +2 -0
  175. package/dist/labs/list.js.map +1 -0
  176. package/dist/labs/observers.cjs +2 -0
  177. package/dist/labs/observers.cjs.map +1 -0
  178. package/dist/labs/observers.d.ts +42 -0
  179. package/dist/labs/observers.d.ts.map +1 -0
  180. package/dist/labs/observers.js +2 -0
  181. package/dist/labs/observers.js.map +1 -0
  182. package/dist/labs/overlay.cjs +2 -0
  183. package/dist/labs/overlay.cjs.map +1 -0
  184. package/dist/labs/overlay.d.ts +35 -0
  185. package/dist/labs/overlay.d.ts.map +1 -0
  186. package/dist/labs/overlay.js +2 -0
  187. package/dist/labs/overlay.js.map +1 -0
  188. package/dist/labs/selectable.cjs +2 -0
  189. package/dist/labs/selectable.cjs.map +1 -0
  190. package/dist/labs/selectable.d.ts +70 -0
  191. package/dist/labs/selectable.d.ts.map +1 -0
  192. package/dist/labs/selectable.js +2 -0
  193. package/dist/labs/selectable.js.map +1 -0
  194. package/dist/labs/selection.cjs +2 -0
  195. package/dist/labs/selection.cjs.map +1 -0
  196. package/dist/labs/selection.d.ts +68 -0
  197. package/dist/labs/selection.d.ts.map +1 -0
  198. package/dist/labs/selection.js +2 -0
  199. package/dist/labs/selection.js.map +1 -0
  200. package/dist/labs.cjs +1 -0
  201. package/dist/labs.js +1 -0
  202. package/dist/test/index.d.ts +2 -0
  203. package/dist/test/index.d.ts.map +1 -0
  204. package/dist/test/test.cjs +2 -0
  205. package/dist/test/test.cjs.map +1 -0
  206. package/dist/test/test.d.ts +198 -0
  207. package/dist/test/test.d.ts.map +1 -0
  208. package/dist/test/test.js +2 -0
  209. package/dist/test/test.js.map +1 -0
  210. package/dist/test.cjs +1 -0
  211. package/dist/test.js +1 -0
  212. package/package.json +37 -9
  213. package/dist/index.cjs.map +0 -1
  214. 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,2 @@
1
+ let e=require(`@vielzeug/stateit`);function t(t,n){let r=``,i=[],a=!1;return()=>{let o=t.map(t=>(0,e.isSignal)(t)?t.value:t);return(!a||o.length!==i.length||o.some((e,t)=>!Object.is(e,i[t])))&&(r=n(),i=o,a=!0),r}}exports.memo=t;
2
+ //# sourceMappingURL=memo.cjs.map
@@ -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,2 @@
1
+ import{isSignal as e}from"@vielzeug/stateit";function t(t,n){let r=``,i=[],a=!1;return()=>{let o=t.map(t=>e(t)?t.value:t);return(!a||o.length!==i.length||o.some((e,t)=>!Object.is(e,i[t])))&&(r=n(),i=o,a=!0),r}}export{t as memo};
2
+ //# sourceMappingURL=memo.js.map
@@ -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,2 @@
1
+ import{listen as e}from"../core/utilities.js";function t(t,n,r){return{mount(i,{registerCleanup:a}){a(t===`clickOutside`?e(i.ownerDocument??document,`click`,e=>{e.composedPath().includes(i)||n(e)},r):e(i,t,n,r))}}}export{t as on};
2
+ //# sourceMappingURL=on.js.map
@@ -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,2 @@
1
+ import{htmlResult as e}from"../core/internal.js";import{computed as t,isSignal as n}from"@vielzeug/stateit";function r(r){return n(r)?t(()=>e(r.value)):typeof r==`function`?t(()=>e(r())):e(r)}export{r as raw};
2
+ //# sourceMappingURL=raw.js.map
@@ -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"}