@vielzeug/craftit 2.1.0 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. package/README.md +58 -124
  2. package/dist/controls/a11y-control.cjs +1 -1
  3. package/dist/controls/a11y-control.cjs.map +1 -1
  4. package/dist/controls/a11y-control.d.ts +1 -1
  5. package/dist/controls/a11y-control.d.ts.map +1 -1
  6. package/dist/controls/a11y-control.js +1 -1
  7. package/dist/controls/a11y-control.js.map +1 -1
  8. package/dist/controls/checkable-control.cjs +1 -1
  9. package/dist/controls/checkable-control.cjs.map +1 -1
  10. package/dist/controls/checkable-control.d.ts +7 -7
  11. package/dist/controls/checkable-control.d.ts.map +1 -1
  12. package/dist/controls/checkable-control.js +1 -1
  13. package/dist/controls/checkable-control.js.map +1 -1
  14. package/dist/controls/choice-field-control.cjs +2 -0
  15. package/dist/controls/choice-field-control.cjs.map +1 -0
  16. package/dist/controls/choice-field-control.d.ts +3 -0
  17. package/dist/controls/choice-field-control.d.ts.map +1 -0
  18. package/dist/controls/choice-field-control.js +2 -0
  19. package/dist/controls/choice-field-control.js.map +1 -0
  20. package/dist/controls/field-control.cjs +1 -1
  21. package/dist/controls/field-control.cjs.map +1 -1
  22. package/dist/controls/field-control.d.ts +28 -73
  23. package/dist/controls/field-control.d.ts.map +1 -1
  24. package/dist/controls/field-control.js +1 -1
  25. package/dist/controls/field-control.js.map +1 -1
  26. package/dist/controls/index.d.ts +11 -9
  27. package/dist/controls/index.d.ts.map +1 -1
  28. package/dist/controls/internal/control-state.cjs +1 -1
  29. package/dist/controls/internal/control-state.cjs.map +1 -1
  30. package/dist/controls/internal/control-state.d.ts +6 -4
  31. package/dist/controls/internal/control-state.d.ts.map +1 -1
  32. package/dist/controls/internal/control-state.js +1 -1
  33. package/dist/controls/internal/control-state.js.map +1 -1
  34. package/dist/controls/internal/keyboard-utils.cjs.map +1 -1
  35. package/dist/controls/internal/keyboard-utils.js.map +1 -1
  36. package/dist/controls/internal/number-utils.cjs.map +1 -1
  37. package/dist/controls/internal/number-utils.js.map +1 -1
  38. package/dist/controls/internal/validation-utils.cjs.map +1 -1
  39. package/dist/controls/internal/validation-utils.js.map +1 -1
  40. package/dist/controls/list-control.cjs +1 -1
  41. package/dist/controls/list-control.cjs.map +1 -1
  42. package/dist/controls/list-control.d.ts +10 -8
  43. package/dist/controls/list-control.d.ts.map +1 -1
  44. package/dist/controls/list-control.js +1 -1
  45. package/dist/controls/list-control.js.map +1 -1
  46. package/dist/controls/overlay-control.cjs +1 -1
  47. package/dist/controls/overlay-control.cjs.map +1 -1
  48. package/dist/controls/overlay-control.d.ts +17 -14
  49. package/dist/controls/overlay-control.d.ts.map +1 -1
  50. package/dist/controls/overlay-control.js +1 -1
  51. package/dist/controls/overlay-control.js.map +1 -1
  52. package/dist/controls/popup-list-control.cjs +2 -0
  53. package/dist/controls/popup-list-control.cjs.map +1 -0
  54. package/dist/controls/popup-list-control.d.ts +160 -0
  55. package/dist/controls/popup-list-control.d.ts.map +1 -0
  56. package/dist/controls/popup-list-control.js +2 -0
  57. package/dist/controls/popup-list-control.js.map +1 -0
  58. package/dist/controls/press-control.cjs.map +1 -1
  59. package/dist/controls/press-control.js.map +1 -1
  60. package/dist/controls/slider-control.cjs.map +1 -1
  61. package/dist/controls/slider-control.js.map +1 -1
  62. package/dist/controls/spinner-control.cjs.map +1 -1
  63. package/dist/controls/spinner-control.js.map +1 -1
  64. package/dist/controls/swipe-control.cjs +2 -0
  65. package/dist/controls/swipe-control.cjs.map +1 -0
  66. package/dist/controls/swipe-control.d.ts +32 -0
  67. package/dist/controls/swipe-control.d.ts.map +1 -0
  68. package/dist/controls/swipe-control.js +2 -0
  69. package/dist/controls/swipe-control.js.map +1 -0
  70. package/dist/controls/text-field-control.cjs +2 -0
  71. package/dist/controls/text-field-control.cjs.map +1 -0
  72. package/dist/controls/text-field-control.d.ts +3 -0
  73. package/dist/controls/text-field-control.d.ts.map +1 -0
  74. package/dist/controls/text-field-control.js +2 -0
  75. package/dist/controls/text-field-control.js.map +1 -0
  76. package/dist/controls.cjs +1 -1
  77. package/dist/controls.js +1 -1
  78. package/dist/craftit.cjs +1 -1
  79. package/dist/craftit.cjs.map +1 -1
  80. package/dist/craftit.js +1 -1
  81. package/dist/craftit.js.map +1 -1
  82. package/dist/directives/classMap.cjs +2 -0
  83. package/dist/directives/classMap.cjs.map +1 -0
  84. package/dist/directives/classMap.d.ts +19 -0
  85. package/dist/directives/classMap.d.ts.map +1 -0
  86. package/dist/directives/classMap.js +2 -0
  87. package/dist/directives/classMap.js.map +1 -0
  88. package/dist/directives/each.cjs +1 -1
  89. package/dist/directives/each.cjs.map +1 -1
  90. package/dist/directives/each.d.ts +5 -30
  91. package/dist/directives/each.d.ts.map +1 -1
  92. package/dist/directives/each.js +1 -1
  93. package/dist/directives/each.js.map +1 -1
  94. package/dist/directives/guard.cjs +2 -0
  95. package/dist/directives/guard.cjs.map +1 -0
  96. package/dist/directives/guard.d.ts +10 -0
  97. package/dist/directives/guard.d.ts.map +1 -0
  98. package/dist/directives/guard.js +2 -0
  99. package/dist/directives/guard.js.map +1 -0
  100. package/dist/directives/live.cjs +2 -0
  101. package/dist/directives/live.cjs.map +1 -0
  102. package/dist/directives/live.d.ts +23 -0
  103. package/dist/directives/live.d.ts.map +1 -0
  104. package/dist/directives/live.js +2 -0
  105. package/dist/directives/live.js.map +1 -0
  106. package/dist/directives/raw.cjs +1 -1
  107. package/dist/directives/raw.cjs.map +1 -1
  108. package/dist/directives/raw.d.ts +3 -5
  109. package/dist/directives/raw.d.ts.map +1 -1
  110. package/dist/directives/raw.js +1 -1
  111. package/dist/directives/raw.js.map +1 -1
  112. package/dist/directives/resource.cjs +2 -0
  113. package/dist/directives/resource.cjs.map +1 -0
  114. package/dist/directives/resource.d.ts +32 -0
  115. package/dist/directives/resource.d.ts.map +1 -0
  116. package/dist/directives/resource.js +2 -0
  117. package/dist/directives/resource.js.map +1 -0
  118. package/dist/directives/styleMap.cjs +2 -0
  119. package/dist/directives/styleMap.cjs.map +1 -0
  120. package/dist/directives/styleMap.d.ts +11 -0
  121. package/dist/directives/styleMap.d.ts.map +1 -0
  122. package/dist/directives/styleMap.js +2 -0
  123. package/dist/directives/styleMap.js.map +1 -0
  124. package/dist/directives/when.cjs +1 -1
  125. package/dist/directives/when.cjs.map +1 -1
  126. package/dist/directives/when.d.ts +6 -19
  127. package/dist/directives/when.d.ts.map +1 -1
  128. package/dist/directives/when.js +1 -1
  129. package/dist/directives/when.js.map +1 -1
  130. package/dist/errors.cjs +2 -0
  131. package/dist/errors.cjs.map +1 -0
  132. package/dist/errors.d.ts +12 -0
  133. package/dist/errors.d.ts.map +1 -0
  134. package/dist/errors.js +2 -0
  135. package/dist/errors.js.map +1 -0
  136. package/dist/form.cjs +1 -1
  137. package/dist/form.cjs.map +1 -1
  138. package/dist/form.d.ts +3 -17
  139. package/dist/form.d.ts.map +1 -1
  140. package/dist/form.js +1 -1
  141. package/dist/form.js.map +1 -1
  142. package/dist/host.cjs +1 -1
  143. package/dist/host.cjs.map +1 -1
  144. package/dist/host.d.ts +40 -37
  145. package/dist/host.d.ts.map +1 -1
  146. package/dist/host.js +1 -1
  147. package/dist/host.js.map +1 -1
  148. package/dist/index.cjs +1 -1
  149. package/dist/index.d.ts +16 -8
  150. package/dist/index.d.ts.map +1 -1
  151. package/dist/index.js +1 -1
  152. package/dist/internal.cjs +1 -1
  153. package/dist/internal.cjs.map +1 -1
  154. package/dist/internal.d.ts +60 -120
  155. package/dist/internal.d.ts.map +1 -1
  156. package/dist/internal.js +1 -1
  157. package/dist/internal.js.map +1 -1
  158. package/dist/observers/index.d.ts +1 -0
  159. package/dist/observers/index.d.ts.map +1 -1
  160. package/dist/observers/intersection-observe.cjs +1 -1
  161. package/dist/observers/intersection-observe.cjs.map +1 -1
  162. package/dist/observers/intersection-observe.d.ts +1 -1
  163. package/dist/observers/intersection-observe.js +1 -1
  164. package/dist/observers/intersection-observe.js.map +1 -1
  165. package/dist/observers/media-observe.cjs +1 -1
  166. package/dist/observers/media-observe.cjs.map +1 -1
  167. package/dist/observers/media-observe.d.ts +1 -1
  168. package/dist/observers/media-observe.js +1 -1
  169. package/dist/observers/media-observe.js.map +1 -1
  170. package/dist/observers/mutation-observe.cjs +2 -0
  171. package/dist/observers/mutation-observe.cjs.map +1 -0
  172. package/dist/observers/mutation-observe.d.ts +10 -0
  173. package/dist/observers/mutation-observe.d.ts.map +1 -0
  174. package/dist/observers/mutation-observe.js +2 -0
  175. package/dist/observers/mutation-observe.js.map +1 -0
  176. package/dist/observers/resize-observe.cjs +1 -1
  177. package/dist/observers/resize-observe.cjs.map +1 -1
  178. package/dist/observers/resize-observe.d.ts +1 -1
  179. package/dist/observers/resize-observe.js +1 -1
  180. package/dist/observers/resize-observe.js.map +1 -1
  181. package/dist/observers.cjs +1 -1
  182. package/dist/observers.js +1 -1
  183. package/dist/props.cjs +1 -1
  184. package/dist/props.cjs.map +1 -1
  185. package/dist/props.d.ts +18 -31
  186. package/dist/props.d.ts.map +1 -1
  187. package/dist/props.js +1 -1
  188. package/dist/props.js.map +1 -1
  189. package/dist/registration.cjs +1 -1
  190. package/dist/registration.cjs.map +1 -1
  191. package/dist/registration.d.ts +27 -7
  192. package/dist/registration.d.ts.map +1 -1
  193. package/dist/registration.js +1 -1
  194. package/dist/registration.js.map +1 -1
  195. package/dist/runtime.cjs +1 -1
  196. package/dist/runtime.cjs.map +1 -1
  197. package/dist/runtime.d.ts +29 -17
  198. package/dist/runtime.d.ts.map +1 -1
  199. package/dist/runtime.js +1 -1
  200. package/dist/runtime.js.map +1 -1
  201. package/dist/template-bindings.cjs +1 -1
  202. package/dist/template-bindings.cjs.map +1 -1
  203. package/dist/template-bindings.d.ts +10 -47
  204. package/dist/template-bindings.d.ts.map +1 -1
  205. package/dist/template-bindings.js +1 -1
  206. package/dist/template-bindings.js.map +1 -1
  207. package/dist/template-compiler.cjs +1 -1
  208. package/dist/template-compiler.cjs.map +1 -1
  209. package/dist/template-compiler.d.ts +1 -21
  210. package/dist/template-compiler.d.ts.map +1 -1
  211. package/dist/template-compiler.js +1 -1
  212. package/dist/template-compiler.js.map +1 -1
  213. package/dist/testing/testing.cjs +1 -1
  214. package/dist/testing/testing.cjs.map +1 -1
  215. package/dist/testing/testing.d.ts +12 -5
  216. package/dist/testing/testing.d.ts.map +1 -1
  217. package/dist/testing/testing.js +1 -1
  218. package/dist/testing/testing.js.map +1 -1
  219. package/package.json +6 -12
  220. package/dist/component.cjs +0 -2
  221. package/dist/component.cjs.map +0 -1
  222. package/dist/component.d.ts +0 -39
  223. package/dist/component.d.ts.map +0 -1
  224. package/dist/component.js +0 -2
  225. package/dist/component.js.map +0 -1
  226. package/dist/controls/list-key-control.cjs +0 -2
  227. package/dist/controls/list-key-control.cjs.map +0 -1
  228. package/dist/controls/list-key-control.d.ts +0 -14
  229. package/dist/controls/list-key-control.d.ts.map +0 -1
  230. package/dist/controls/list-key-control.js +0 -2
  231. package/dist/controls/list-key-control.js.map +0 -1
  232. package/dist/directives/attr.cjs +0 -2
  233. package/dist/directives/attr.cjs.map +0 -1
  234. package/dist/directives/attr.d.ts +0 -12
  235. package/dist/directives/attr.d.ts.map +0 -1
  236. package/dist/directives/attr.js +0 -2
  237. package/dist/directives/attr.js.map +0 -1
  238. package/dist/directives/bind.cjs +0 -2
  239. package/dist/directives/bind.cjs.map +0 -1
  240. package/dist/directives/bind.d.ts +0 -38
  241. package/dist/directives/bind.d.ts.map +0 -1
  242. package/dist/directives/bind.js +0 -2
  243. package/dist/directives/bind.js.map +0 -1
  244. package/dist/directives/choose.cjs +0 -2
  245. package/dist/directives/choose.cjs.map +0 -1
  246. package/dist/directives/choose.d.ts +0 -39
  247. package/dist/directives/choose.d.ts.map +0 -1
  248. package/dist/directives/choose.js +0 -2
  249. package/dist/directives/choose.js.map +0 -1
  250. package/dist/directives/classes.cjs +0 -2
  251. package/dist/directives/classes.cjs.map +0 -1
  252. package/dist/directives/classes.d.ts +0 -20
  253. package/dist/directives/classes.d.ts.map +0 -1
  254. package/dist/directives/classes.js +0 -2
  255. package/dist/directives/classes.js.map +0 -1
  256. package/dist/directives/index.d.ts +0 -13
  257. package/dist/directives/index.d.ts.map +0 -1
  258. package/dist/directives/memo.cjs +0 -2
  259. package/dist/directives/memo.cjs.map +0 -1
  260. package/dist/directives/memo.d.ts +0 -27
  261. package/dist/directives/memo.d.ts.map +0 -1
  262. package/dist/directives/memo.js +0 -2
  263. package/dist/directives/memo.js.map +0 -1
  264. package/dist/directives/on.cjs +0 -2
  265. package/dist/directives/on.cjs.map +0 -1
  266. package/dist/directives/on.d.ts +0 -25
  267. package/dist/directives/on.d.ts.map +0 -1
  268. package/dist/directives/on.js +0 -2
  269. package/dist/directives/on.js.map +0 -1
  270. package/dist/directives/spread.cjs +0 -2
  271. package/dist/directives/spread.cjs.map +0 -1
  272. package/dist/directives/spread.d.ts +0 -14
  273. package/dist/directives/spread.d.ts.map +0 -1
  274. package/dist/directives/spread.js +0 -2
  275. package/dist/directives/spread.js.map +0 -1
  276. package/dist/directives/style.cjs +0 -2
  277. package/dist/directives/style.cjs.map +0 -1
  278. package/dist/directives/style.d.ts +0 -22
  279. package/dist/directives/style.d.ts.map +0 -1
  280. package/dist/directives/style.js +0 -2
  281. package/dist/directives/style.js.map +0 -1
  282. package/dist/directives/until.cjs +0 -2
  283. package/dist/directives/until.cjs.map +0 -1
  284. package/dist/directives/until.d.ts +0 -26
  285. package/dist/directives/until.d.ts.map +0 -1
  286. package/dist/directives/until.js +0 -2
  287. package/dist/directives/until.js.map +0 -1
  288. package/dist/directives.cjs +0 -1
  289. package/dist/directives.js +0 -1
  290. package/dist/runtime-bindings.cjs +0 -2
  291. package/dist/runtime-bindings.cjs.map +0 -1
  292. package/dist/runtime-bindings.d.ts +0 -6
  293. package/dist/runtime-bindings.d.ts.map +0 -1
  294. package/dist/runtime-bindings.js +0 -2
  295. package/dist/runtime-bindings.js.map +0 -1
  296. package/dist/runtime-core.cjs +0 -2
  297. package/dist/runtime-core.cjs.map +0 -1
  298. package/dist/runtime-core.d.ts +0 -21
  299. package/dist/runtime-core.d.ts.map +0 -1
  300. package/dist/runtime-core.js +0 -2
  301. package/dist/runtime-core.js.map +0 -1
  302. package/dist/runtime-lifecycle.cjs +0 -2
  303. package/dist/runtime-lifecycle.cjs.map +0 -1
  304. package/dist/runtime-lifecycle.d.ts +0 -24
  305. package/dist/runtime-lifecycle.d.ts.map +0 -1
  306. package/dist/runtime-lifecycle.js +0 -2
  307. package/dist/runtime-lifecycle.js.map +0 -1
  308. package/dist/template-dom.cjs +0 -2
  309. package/dist/template-dom.cjs.map +0 -1
  310. package/dist/template-dom.d.ts +0 -13
  311. package/dist/template-dom.d.ts.map +0 -1
  312. package/dist/template-dom.js +0 -2
  313. package/dist/template-dom.js.map +0 -1
  314. package/dist/template-html.cjs +0 -2
  315. package/dist/template-html.cjs.map +0 -1
  316. package/dist/template-html.d.ts +0 -23
  317. package/dist/template-html.d.ts.map +0 -1
  318. package/dist/template-html.js +0 -2
  319. package/dist/template-html.js.map +0 -1
  320. package/dist/template.cjs +0 -2
  321. package/dist/template.cjs.map +0 -1
  322. package/dist/template.d.ts +0 -10
  323. package/dist/template.d.ts.map +0 -1
  324. package/dist/template.js +0 -2
  325. package/dist/template.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classMap.cjs","names":[],"sources":["../../src/directives/classMap.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\n/**\n * Produces a reactive string of class names from an object map.\n *\n * Each key is a class name. Its value may be:\n * - a static `boolean`\n * - a `Signal<boolean>`\n * - a getter `() => boolean`\n *\n * Returns a `ReadonlySignal<string>` that can be used directly in a template\n * class attribute:\n *\n * @example\n * ```ts\n * html`<div class=\"${classMap({ active: isActive, hidden: () => !isVisible.value })}\"></div>`\n * ```\n */\nexport const classMap = (\n map: Record<string, (() => boolean) | ReadonlySignal<boolean> | boolean>,\n): ReadonlySignal<string> => {\n return computed(() =>\n Object.entries(map)\n .filter(([, v]) => (typeof v === 'function' ? v() : isSignal(v) ? v.value : v))\n .map(([k]) => k)\n .join(' '),\n );\n};\n"],"mappings":"mCAkBA,IAAa,EACX,IAEA,EAAA,EAAA,cACE,OAAO,QAAQ,CAAG,EACf,QAAQ,EAAG,KAAQ,OAAO,GAAM,WAAa,EAAE,GAAA,EAAA,EAAA,UAAa,CAAC,EAAI,EAAE,MAAQ,CAAE,EAC7E,KAAK,CAAC,KAAO,CAAC,EACd,KAAK,GAAG,CACb"}
@@ -0,0 +1,19 @@
1
+ import { type ReadonlySignal } from '@vielzeug/stateit';
2
+ /**
3
+ * Produces a reactive string of class names from an object map.
4
+ *
5
+ * Each key is a class name. Its value may be:
6
+ * - a static `boolean`
7
+ * - a `Signal<boolean>`
8
+ * - a getter `() => boolean`
9
+ *
10
+ * Returns a `ReadonlySignal<string>` that can be used directly in a template
11
+ * class attribute:
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * html`<div class="${classMap({ active: isActive, hidden: () => !isVisible.value })}"></div>`
16
+ * ```
17
+ */
18
+ export declare const classMap: (map: Record<string, (() => boolean) | ReadonlySignal<boolean> | boolean>) => ReadonlySignal<string>;
19
+ //# sourceMappingURL=classMap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classMap.d.ts","sourceRoot":"","sources":["../../src/directives/classMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE5E;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,QAAQ,GACnB,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,OAAO,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,KACvE,cAAc,CAAC,MAAM,CAOvB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import{computed as e,isSignal as t}from"@vielzeug/stateit";var n=n=>e(()=>Object.entries(n).filter(([,e])=>typeof e==`function`?e():t(e)?e.value:e).map(([e])=>e).join(` `));export{n as classMap};
2
+ //# sourceMappingURL=classMap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classMap.js","names":[],"sources":["../../src/directives/classMap.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\n/**\n * Produces a reactive string of class names from an object map.\n *\n * Each key is a class name. Its value may be:\n * - a static `boolean`\n * - a `Signal<boolean>`\n * - a getter `() => boolean`\n *\n * Returns a `ReadonlySignal<string>` that can be used directly in a template\n * class attribute:\n *\n * @example\n * ```ts\n * html`<div class=\"${classMap({ active: isActive, hidden: () => !isVisible.value })}\"></div>`\n * ```\n */\nexport const classMap = (\n map: Record<string, (() => boolean) | ReadonlySignal<boolean> | boolean>,\n): ReadonlySignal<string> => {\n return computed(() =>\n Object.entries(map)\n .filter(([, v]) => (typeof v === 'function' ? v() : isSignal(v) ? v.value : v))\n .map(([k]) => k)\n .join(' '),\n );\n};\n"],"mappings":"2DAkBA,IAAa,EACX,GAEO,MACL,OAAO,QAAQ,CAAG,EACf,QAAQ,EAAG,KAAQ,OAAO,GAAM,WAAa,EAAE,EAAI,EAAS,CAAC,EAAI,EAAE,MAAQ,CAAE,EAC7E,KAAK,CAAC,KAAO,CAAC,EACd,KAAK,GAAG,CACb"}
@@ -1,2 +1,2 @@
1
- const e=require(`../internal.cjs`);let t=require(`@vielzeug/stateit`);var n=RegExp(`u="(\\d+)"`,`g`),r=e.htmlResult(``),i=[],a=(t,n)=>e.isHtmlResult(t)?s(t,n):{bindings:i,html:e.escapeHtml(t)},o=t=>e.isHtmlResult(t)?t:e.htmlResult(e.escapeHtml(t));function s(e,t){let r=new Map,i=[];for(let n of e.__bindings){let e=n.uid,a=r.get(e)??String(t.n++);r.has(e)||r.set(e,a),i.push({...n,uid:a})}return{bindings:i,html:e.__html.replace(n,(e,t)=>`u="${r.get(t)??t}"`).replace(/<!--(\d+)-->/g,(e,t)=>`<!--${r.get(t)??t}-->`)}}function c(e,t,n){let r=``,i=[],o=[],s=new Set,c=[],l={n:0};for(let u=0;u<e.length;u++){let d=n(e[u],u);if(s.has(d))throw Error(`[craftit:each] Duplicate key "${String(d)}" at index ${u}.`);s.add(d),o.push(d);let f=a(t(e[u],u),l);r+=f.html,i.push(...f.bindings),c.push(f)}return{bindings:i,html:r,keys:o,rendered:c}}function l(e,t){let n=``,r=[],i={n:0};for(let o=0;o<e.length;o++){let s=a(t(e[o],o),i);n+=s.html,r.push(...s.bindings)}return{bindings:r,html:n}}var u=e=>typeof e==`function`,d=e=>{if(!u(e.render))throw Error(`[craftit:each] options.render must be a function.`);if(e.key!==void 0&&!u(e.key))throw Error(`[craftit:each] options.key must be a function when provided.`);if(e.select!==void 0&&!u(e.select))throw Error(`[craftit:each] options.select must be a function when provided.`);if(e.fallback!==void 0&&!u(e.fallback))throw Error(`[craftit:each] options.fallback must be a function when provided.`)},f=e=>{if(Array.isArray(e))return e;throw Error(`[craftit:each] source must resolve to an array.`)};function p(n,i){d(i);let{fallback:a,key:s,render:u,select:p}=i;if(Array.isArray(n)){let t=p?n.filter(p):n;if(!t.length){if(a){let t=e.extractResult(o(a()));return e.htmlResult(t.html,t.bindings)}return r}let{bindings:i,html:s}=l(t,u);return e.htmlResult(s,i)}if(!s)throw Error(`[craftit:each] Reactive each() requires options.key for stable reconciliation.`);let m=(0,t.isSignal)(n)?()=>n.value:n;return{[e.EACH_SIGNAL]:(0,t.computed)(()=>{let t=f(m()),n=p?t.filter(p):t;if(!n.length){let t=a?o(a()):void 0;return t?{...e.extractResult(t),items:[],keys:[]}:{bindings:[],html:``,items:[],keys:[]}}let{bindings:r,html:i,keys:l,rendered:d}=c(n,u,s);return{bindings:r,html:i,items:d,keys:l}})}}exports.each=p;
1
+ const e=require(`../errors.cjs`),t=require(`../internal.cjs`),n=require(`../template-bindings.cjs`);let r=require(`@vielzeug/stateit`);var i=(e,n)=>t.isHtmlResult(e)?t.rekeyHtmlResult(e,n):{bindings:[],html:t.escapeHtml(e)},a=e=>t.isHtmlResult(e)?e:t.htmlResult(t.escapeHtml(e));function o(n,r,a){let o=``,s=[],c=[],l=new Set,u=[],d=t.createMarkerIdFactory();for(let t=0;t<n.length;t++){let f=a(n[t],t);if(l.has(f))throw Error(e.CRAFTIT_ERRORS.eachDuplicateKey(String(f),t));l.add(f),c.push(f);let p=i(r(n[t],t),d);o+=p.html,s.push(...p.bindings),u.push(p)}return{bindings:s,html:o,keys:c,rendered:u}}function s(e,i){let{fallback:s,key:c,render:l}=i;return{__craftitDirective:!0,mount:(i,u)=>{let d=new Map,f=[],p=[],m=()=>{t.removeNodes(f),f=[],t.runAll(p),p=[]},h=()=>{for(let[,e]of d)t.removeNodes(e.nodes),t.runAll(e.cleanups);d.clear()};u((0,r.effect)(()=>{let u=i.parentNode;u&&(0,r.batch)(()=>{let g=e.value;if(!g.length){if(h(),m(),!s)return;let e=t.extractResult(a(s())),r=n.parseHTML(e.html);f=Array.from(r.childNodes),i.after(r);let o=e=>p.push(e);n.applyBindingsWithTargets(e.bindings,o,n.indexBindingTargets(f),{onHtml:e=>n.applyHtmlBinding(u,e,o)});return}m();let{keys:_,rendered:v}=o(g,l,c),y=new Map,b=[];for(let e=0;e<_.length;e++){let r=_[e],i=v[e],a=d.get(r),o,s=[];if(a&&a.html===i.html)o=a.nodes,t.runAll(a.cleanups);else{a&&(t.removeNodes(a.nodes),t.runAll(a.cleanups));let e=n.parseHTML(i.html);o=Array.from(e.childNodes)}b.push({item:i,key:r,nodes:o}),y.set(r,{cleanups:s,html:i.html,nodes:o})}for(let[e,n]of d)y.has(e)||(t.removeNodes(n.nodes),t.runAll(n.cleanups));let x=i;for(let e of b){for(let t of e.nodes)u.insertBefore(t,x.nextSibling),x=t;let t=y.get(e.key);if(!t)continue;let i=e=>t.cleanups.push(e);(0,r.untrack)(()=>{n.applyBindingsWithTargets(e.item.bindings,i,n.indexBindingTargets(e.nodes),{onHtml:e=>n.applyHtmlBinding(u,e,i)})})}d=y})})),u(()=>{m(),h()})}}}exports.each=s;
2
2
  //# sourceMappingURL=each.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"each.cjs","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport {\n CF_ID_ATTR,\n EACH_SIGNAL,\n escapeHtml,\n extractResult,\n htmlResult,\n isHtmlResult,\n type Binding,\n type Directive,\n type HTMLResult,\n} from '../internal';\n\nconst ATTR_ID_RE = new RegExp(`${CF_ID_ATTR}=\"(\\\\d+)\"`, 'g');\n\n/* immutable — shared singleton for empty static lists */\nconst EMPTY = htmlResult('');\nconst NO_BINDINGS: Binding[] = [];\n\nconst toResultEntry = (value: string | HTMLResult, c: { n: number }): { bindings: Binding[]; html: string } =>\n isHtmlResult(value) ? renumber(value, c) : { bindings: NO_BINDINGS, html: escapeHtml(value) };\n\nconst toHtmlResult = (value: string | HTMLResult): HTMLResult =>\n isHtmlResult(value) ? value : htmlResult(escapeHtml(value));\n\nfunction renumber(res: HTMLResult, c: { n: number }): { bindings: Binding[]; html: string } {\n const map = new Map<string, string>();\n const nb: Binding[] = [];\n\n for (const b of res.__bindings) {\n const oldId = b.uid;\n const newId = map.get(oldId) ?? String(c.n++);\n\n if (!map.has(oldId)) map.set(oldId, newId);\n\n nb.push({ ...b, uid: newId });\n }\n\n return {\n bindings: nb,\n html: res.__html\n // Re-map element binding ids.\n .replace(ATTR_ID_RE, (_, id) => `${CF_ID_ATTR}=\"${map.get(id) ?? id}\"`)\n // Re-map numeric comment markers used by text/html placeholders.\n .replace(/<!--(\\d+)-->/g, (_, id) => `<!--${map.get(id) ?? id}-->`),\n };\n}\n\n/** Render loop used by the reactive path (keys + rendered metadata required for reconciliation). */\nfunction renderKeyed<T>(\n items: T[],\n template: (item: T, index: number) => string | HTMLResult,\n keyFn: (item: T, index: number) => string | number,\n): {\n bindings: Binding[];\n html: string;\n keys: (string | number)[];\n rendered: Array<{ bindings: Binding[]; html: string }>;\n} {\n let html = '';\n const allBindings: Binding[] = [];\n const keys: (string | number)[] = [];\n const seenKeys = new Set<string | number>();\n const rendered: Array<{ bindings: Binding[]; html: string }> = [];\n const c = { n: 0 };\n\n for (let i = 0; i < items.length; i++) {\n const nextKey = keyFn(items[i], i);\n\n if (seenKeys.has(nextKey)) {\n throw new Error(`[craftit:each] Duplicate key \"${String(nextKey)}\" at index ${i}.`);\n }\n\n seenKeys.add(nextKey);\n keys.push(nextKey);\n\n const entry = toResultEntry(template(items[i], i), c);\n\n html += entry.html;\n allBindings.push(...entry.bindings);\n rendered.push(entry);\n }\n\n return { bindings: allBindings, html, keys, rendered };\n}\n\n/** Render loop used by the static path — no key/reconciliation metadata needed. */\nfunction renderStatic<T>(\n items: T[],\n template: (item: T, index: number) => string | HTMLResult,\n): { bindings: Binding[]; html: string } {\n let html = '';\n const allBindings: Binding[] = [];\n const c = { n: 0 };\n\n for (let i = 0; i < items.length; i++) {\n const entry = toResultEntry(template(items[i], i), c);\n\n html += entry.html;\n allBindings.push(...entry.bindings);\n }\n\n return { bindings: allBindings, html };\n}\n\ntype ReactiveSource<T> = ReadonlySignal<T[]> | (() => T[]);\ntype EachSignalResult = Directive & {\n [EACH_SIGNAL]: ReadonlySignal<{\n bindings: Binding[];\n html: string;\n items?: Array<{ bindings: Binding[]; html: string }>;\n keys?: (string | number)[];\n }>;\n};\n\nexport interface EachOptions<T> {\n fallback?: () => string | HTMLResult;\n key?: (item: T, index: number) => string | number;\n render: (item: T, index: number) => string | HTMLResult;\n /**\n * Filter predicate. Receives the item and its original array index.\n * Only items for which `select` returns `true` are rendered.\n */\n select?: (item: T, index: number) => boolean;\n}\n\nconst isFunction = (value: unknown): value is (...args: any[]) => any => typeof value === 'function';\n\nconst validateEachOptions = <T>(options: EachOptions<T>): void => {\n if (!isFunction(options.render)) {\n throw new Error('[craftit:each] options.render must be a function.');\n }\n\n if (options.key !== undefined && !isFunction(options.key)) {\n throw new Error('[craftit:each] options.key must be a function when provided.');\n }\n\n if (options.select !== undefined && !isFunction(options.select)) {\n throw new Error('[craftit:each] options.select must be a function when provided.');\n }\n\n if (options.fallback !== undefined && !isFunction(options.fallback)) {\n throw new Error('[craftit:each] options.fallback must be a function when provided.');\n }\n};\n\nconst assertArrayResult = <T>(value: T[] | unknown): T[] => {\n if (Array.isArray(value)) return value as T[];\n\n throw new Error('[craftit:each] source must resolve to an array.');\n};\n\n/**\n * Renders a list with keyed DOM reconciliation for reactive sources.\n * Use inside `html` tagged templates.\n *\n * For reactive sources (Signal/getter), you must provide a stable `key` function.\n *\n * For dynamic lists with click handlers, prefer event delegation on a parent node\n * (`@click` + `closest(...)`) over per-item handlers inside `each()`.\n *\n * @example\n * import { each } from '@vielzeug/craftit/directives';\n *\n * // Static array (key optional):\n * html`${each([1, 2, 3], { render: (item) => html`<li>${item}</li>` })}`\n *\n * // Reactive source (key required):\n * html`${each(items, { key: item => item.id, render: (item) => html`<li>${item.name}</li>` })}`\n *\n * // Full example:\n * html`${each(items, {\n * fallback: () => html`<p>No items</p>`,\n * key: item => item.id,\n * render: (item) => html`<li>${item.name}</li>`,\n * select: item => item.active,\n * })}`\n */\nexport function each<T>(source: T[], options: EachOptions<T>): HTMLResult;\nexport function each<T>(\n source: ReactiveSource<T>,\n options: EachOptions<T> & { key: (item: T, index: number) => string | number },\n): EachSignalResult;\nexport function each<T>(source: T[] | ReactiveSource<T>, options: EachOptions<T>): HTMLResult | EachSignalResult {\n validateEachOptions(options);\n\n const { fallback, key, render, select } = options;\n\n if (Array.isArray(source)) {\n const filtered = select ? source.filter(select) : source;\n\n if (!filtered.length) {\n if (fallback) {\n const er = extractResult(toHtmlResult(fallback()));\n\n return htmlResult(er.html, er.bindings);\n }\n\n return EMPTY;\n }\n\n const { bindings, html } = renderStatic(filtered, render);\n\n return htmlResult(html, bindings);\n }\n\n if (!key) {\n throw new Error('[craftit:each] Reactive each() requires options.key for stable reconciliation.');\n }\n\n const getItems = isSignal(source) ? () => (source as ReadonlySignal<T[]>).value : (source as () => T[]);\n\n return {\n [EACH_SIGNAL]: computed(() => {\n const raw = assertArrayResult<T>(getItems());\n const filtered = select ? raw.filter(select) : raw;\n\n if (!filtered.length) {\n const er = fallback ? toHtmlResult(fallback()) : undefined;\n\n return er ? { ...extractResult(er), items: [], keys: [] } : { bindings: [], html: '', items: [], keys: [] };\n }\n\n const { bindings, html, keys, rendered } = renderKeyed(filtered, render, key);\n\n return { bindings, html, items: rendered, keys };\n }),\n };\n}\n"],"mappings":"sEAcA,IAAM,EAAiB,OAAO,aAA0B,IAAI,CAGtD,EAAQ,EAAA,WAAW,GAAG,CACtB,EAAyB,EAAE,CAE3B,GAAiB,EAA4B,IACjD,EAAA,aAAa,EAAM,CAAG,EAAS,EAAO,EAAE,CAAG,CAAE,SAAU,EAAa,KAAM,EAAA,WAAW,EAAM,CAAE,CAEzF,EAAgB,GACpB,EAAA,aAAa,EAAM,CAAG,EAAQ,EAAA,WAAW,EAAA,WAAW,EAAM,CAAC,CAE7D,SAAS,EAAS,EAAiB,EAAyD,CAC1F,IAAM,EAAM,IAAI,IACV,EAAgB,EAAE,CAExB,IAAK,IAAM,KAAK,EAAI,WAAY,CAC9B,IAAM,EAAQ,EAAE,IACV,EAAQ,EAAI,IAAI,EAAM,EAAI,OAAO,EAAE,IAAI,CAExC,EAAI,IAAI,EAAM,EAAE,EAAI,IAAI,EAAO,EAAM,CAE1C,EAAG,KAAK,CAAE,GAAG,EAAG,IAAK,EAAO,CAAC,CAG/B,MAAO,CACL,SAAU,EACV,KAAM,EAAI,OAEP,QAAQ,GAAa,EAAG,IAAO,MAAkB,EAAI,IAAI,EAAG,EAAI,EAAG,GAAG,CAEtE,QAAQ,iBAAkB,EAAG,IAAO,OAAO,EAAI,IAAI,EAAG,EAAI,EAAG,KAAK,CACtE,CAIH,SAAS,EACP,EACA,EACA,EAMA,CACA,IAAI,EAAO,GACL,EAAyB,EAAE,CAC3B,EAA4B,EAAE,CAC9B,EAAW,IAAI,IACf,EAAyD,EAAE,CAC3D,EAAI,CAAE,EAAG,EAAG,CAElB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACrC,IAAM,EAAU,EAAM,EAAM,GAAI,EAAE,CAElC,GAAI,EAAS,IAAI,EAAQ,CACvB,MAAU,MAAM,iCAAiC,OAAO,EAAQ,CAAC,aAAa,EAAE,GAAG,CAGrF,EAAS,IAAI,EAAQ,CACrB,EAAK,KAAK,EAAQ,CAElB,IAAM,EAAQ,EAAc,EAAS,EAAM,GAAI,EAAE,CAAE,EAAE,CAErD,GAAQ,EAAM,KACd,EAAY,KAAK,GAAG,EAAM,SAAS,CACnC,EAAS,KAAK,EAAM,CAGtB,MAAO,CAAE,SAAU,EAAa,OAAM,OAAM,WAAU,CAIxD,SAAS,EACP,EACA,EACuC,CACvC,IAAI,EAAO,GACL,EAAyB,EAAE,CAC3B,EAAI,CAAE,EAAG,EAAG,CAElB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAAK,CACrC,IAAM,EAAQ,EAAc,EAAS,EAAM,GAAI,EAAE,CAAE,EAAE,CAErD,GAAQ,EAAM,KACd,EAAY,KAAK,GAAG,EAAM,SAAS,CAGrC,MAAO,CAAE,SAAU,EAAa,OAAM,CAwBxC,IAAM,EAAc,GAAqD,OAAO,GAAU,WAEpF,EAA0B,GAAkC,CAChE,GAAI,CAAC,EAAW,EAAQ,OAAO,CAC7B,MAAU,MAAM,oDAAoD,CAGtE,GAAI,EAAQ,MAAQ,IAAA,IAAa,CAAC,EAAW,EAAQ,IAAI,CACvD,MAAU,MAAM,+DAA+D,CAGjF,GAAI,EAAQ,SAAW,IAAA,IAAa,CAAC,EAAW,EAAQ,OAAO,CAC7D,MAAU,MAAM,kEAAkE,CAGpF,GAAI,EAAQ,WAAa,IAAA,IAAa,CAAC,EAAW,EAAQ,SAAS,CACjE,MAAU,MAAM,oEAAoE,EAIlF,EAAwB,GAA8B,CAC1D,GAAI,MAAM,QAAQ,EAAM,CAAE,OAAO,EAEjC,MAAU,MAAM,kDAAkD,EAkCpE,SAAgB,EAAQ,EAAiC,EAAwD,CAC/G,EAAoB,EAAQ,CAE5B,GAAM,CAAE,WAAU,MAAK,SAAQ,UAAW,EAE1C,GAAI,MAAM,QAAQ,EAAO,CAAE,CACzB,IAAM,EAAW,EAAS,EAAO,OAAO,EAAO,CAAG,EAElD,GAAI,CAAC,EAAS,OAAQ,CACpB,GAAI,EAAU,CACZ,IAAM,EAAK,EAAA,cAAc,EAAa,GAAU,CAAC,CAAC,CAElD,OAAO,EAAA,WAAW,EAAG,KAAM,EAAG,SAAS,CAGzC,OAAO,EAGT,GAAM,CAAE,WAAU,QAAS,EAAa,EAAU,EAAO,CAEzD,OAAO,EAAA,WAAW,EAAM,EAAS,CAGnC,GAAI,CAAC,EACH,MAAU,MAAM,iFAAiF,CAGnG,IAAM,GAAA,EAAA,EAAA,UAAoB,EAAO,KAAU,EAA+B,MAAS,EAEnF,MAAO,EACJ,EAAA,cAAA,EAAA,EAAA,cAA6B,CAC5B,IAAM,EAAM,EAAqB,GAAU,CAAC,CACtC,EAAW,EAAS,EAAI,OAAO,EAAO,CAAG,EAE/C,GAAI,CAAC,EAAS,OAAQ,CACpB,IAAM,EAAK,EAAW,EAAa,GAAU,CAAC,CAAG,IAAA,GAEjD,OAAO,EAAK,CAAE,GAAG,EAAA,cAAc,EAAG,CAAE,MAAO,EAAE,CAAE,KAAM,EAAE,CAAE,CAAG,CAAE,SAAU,EAAE,CAAE,KAAM,GAAI,MAAO,EAAE,CAAE,KAAM,EAAE,CAAE,CAG7G,GAAM,CAAE,WAAU,OAAM,OAAM,YAAa,EAAY,EAAU,EAAQ,EAAI,CAE7E,MAAO,CAAE,WAAU,OAAM,MAAO,EAAU,OAAM,EAChD,CACH"}
1
+ {"version":3,"file":"each.cjs","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import { batch, effect as _effect, untrack, type CleanupFn, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport { CRAFTIT_ERRORS } from '../errors';\nimport {\n createMarkerIdFactory,\n escapeHtml,\n extractResult,\n htmlResult,\n isHtmlResult,\n removeNodes,\n rekeyHtmlResult,\n runAll,\n type Binding,\n type DirectiveResult,\n type HTMLResult,\n} from '../internal';\nimport { applyBindingsWithTargets, indexBindingTargets, parseHTML, type RegisterCleanup } from '../template-bindings';\nimport { applyHtmlBinding } from '../template-bindings';\n\nconst toResultEntry = (value: string | HTMLResult, getNextId: () => string): { bindings: Binding[]; html: string } =>\n isHtmlResult(value) ? rekeyHtmlResult(value, getNextId) : { bindings: [], html: escapeHtml(value) };\n\nconst toHtmlResult = (value: string | HTMLResult): HTMLResult =>\n isHtmlResult(value) ? value : htmlResult(escapeHtml(value));\n\n/** Render loop used by the reactive path (keys + rendered metadata required for reconciliation). */\nfunction renderKeyed<T>(\n items: T[],\n template: (item: T, index: number) => string | HTMLResult,\n keyFn: (item: T, index: number) => string | number,\n): {\n bindings: Binding[];\n html: string;\n keys: (string | number)[];\n rendered: Array<{ bindings: Binding[]; html: string }>;\n} {\n let html = '';\n const allBindings: Binding[] = [];\n const keys: (string | number)[] = [];\n const seenKeys = new Set<string | number>();\n const rendered: Array<{ bindings: Binding[]; html: string }> = [];\n const getNextId = createMarkerIdFactory();\n\n for (let i = 0; i < items.length; i++) {\n const nextKey = keyFn(items[i], i);\n\n if (seenKeys.has(nextKey)) {\n throw new Error(CRAFTIT_ERRORS.eachDuplicateKey(String(nextKey), i));\n }\n\n seenKeys.add(nextKey);\n keys.push(nextKey);\n\n const entry = toResultEntry(template(items[i], i), getNextId);\n\n html += entry.html;\n allBindings.push(...entry.bindings);\n rendered.push(entry);\n }\n\n return { bindings: allBindings, html, keys, rendered };\n}\n\nexport interface EachOptions<T> {\n fallback?: () => string | HTMLResult;\n key: (item: T, index: number) => string | number;\n render: (item: T, index: number) => string | HTMLResult;\n}\n\n/**\n * Renders a list with keyed DOM reconciliation for reactive sources.\n * Use inside `html` tagged templates.\n *\n * `each()` expects a reactive signal source and a stable `key` function.\n *\n * For dynamic lists with click handlers, prefer event delegation on a parent node\n * (`@click` + `closest(...)`) over per-item handlers inside `each()`.\n *\n * @example\n * import { each } from '@vielzeug/craftit';\n *\n * // Reactive source (key required):\n * html`${each(items, { key: item => item.id, render: (item) => html`<li>${item.name}</li>` })}`\n *\n * // Full example:\n * html`${each(items, {\n * fallback: () => html`<p>No items</p>`,\n * key: item => item.id,\n * render: (item) => html`<li>${item.name}</li>`,\n * })}`\n */\nexport function each<T>(source: ReadonlySignal<T[]>, options: EachOptions<T>): DirectiveResult;\nexport function each<T>(source: ReadonlySignal<T[]>, options: EachOptions<T>): DirectiveResult {\n const { fallback, key, render: renderItem } = options;\n\n const mount = (anchor: Comment, registerCleanup: RegisterCleanup): void => {\n type KeyedNode = {\n cleanups: CleanupFn[];\n html: string;\n nodes: Node[];\n };\n\n let keyedNodes = new Map<string | number, KeyedNode>();\n let fallbackNodes: Node[] = [];\n let fallbackCleanups: CleanupFn[] = [];\n\n const clearFallback = (): void => {\n removeNodes(fallbackNodes);\n fallbackNodes = [];\n runAll(fallbackCleanups);\n fallbackCleanups = [];\n };\n\n const clearKeyed = (): void => {\n for (const [, node] of keyedNodes) {\n removeNodes(node.nodes);\n runAll(node.cleanups);\n }\n\n keyedNodes.clear();\n };\n\n const stop = _effect(() => {\n const parent = anchor.parentNode;\n\n if (!parent) return;\n\n batch(() => {\n const raw = source.value;\n\n if (!raw.length) {\n clearKeyed();\n clearFallback();\n\n if (!fallback) return;\n\n const fallbackResult = extractResult(toHtmlResult(fallback()));\n const parsed = parseHTML(fallbackResult.html);\n\n fallbackNodes = Array.from(parsed.childNodes);\n anchor.after(parsed);\n\n const addFallbackCleanup: RegisterCleanup = (fn) => fallbackCleanups.push(fn);\n\n applyBindingsWithTargets(fallbackResult.bindings, addFallbackCleanup, indexBindingTargets(fallbackNodes), {\n onHtml: (binding) => applyHtmlBinding(parent as unknown as Node, binding, addFallbackCleanup),\n });\n\n return;\n }\n\n clearFallback();\n\n const { keys, rendered } = renderKeyed(raw, renderItem, key);\n const nextKeyed = new Map<string | number, KeyedNode>();\n const ordered: Array<{ item: { bindings: Binding[]; html: string }; key: string | number; nodes: Node[] }> = [];\n\n for (let i = 0; i < keys.length; i++) {\n const nextKey = keys[i];\n const item = rendered[i];\n const existing = keyedNodes.get(nextKey);\n let nodes: Node[];\n const cleanups: CleanupFn[] = [];\n\n if (existing && existing.html === item.html) {\n nodes = existing.nodes;\n runAll(existing.cleanups);\n } else {\n if (existing) {\n removeNodes(existing.nodes);\n runAll(existing.cleanups);\n }\n\n const parsed = parseHTML(item.html);\n\n nodes = Array.from(parsed.childNodes);\n }\n\n ordered.push({ item, key: nextKey, nodes });\n nextKeyed.set(nextKey, { cleanups, html: item.html, nodes });\n }\n\n for (const [oldKey, oldNode] of keyedNodes) {\n if (!nextKeyed.has(oldKey)) {\n removeNodes(oldNode.nodes);\n runAll(oldNode.cleanups);\n }\n }\n\n let cursor: Node = anchor;\n\n for (const entry of ordered) {\n for (const node of entry.nodes) {\n parent.insertBefore(node, cursor.nextSibling);\n cursor = node;\n }\n\n const targetNode = nextKeyed.get(entry.key);\n\n if (!targetNode) continue;\n\n const addItemCleanup: RegisterCleanup = (fn) => targetNode.cleanups.push(fn);\n\n untrack(() => {\n applyBindingsWithTargets(entry.item.bindings, addItemCleanup, indexBindingTargets(entry.nodes), {\n onHtml: (binding) => applyHtmlBinding(parent as unknown as Node, binding, addItemCleanup),\n });\n });\n }\n\n keyedNodes = nextKeyed;\n });\n });\n\n registerCleanup(stop);\n registerCleanup(() => {\n clearFallback();\n clearKeyed();\n });\n };\n\n return { __craftitDirective: true, mount };\n}\n"],"mappings":"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"}
@@ -1,41 +1,21 @@
1
1
  import { type ReadonlySignal } from '@vielzeug/stateit';
2
- import { EACH_SIGNAL, type Binding, type Directive, type HTMLResult } from '../internal';
3
- type ReactiveSource<T> = ReadonlySignal<T[]> | (() => T[]);
4
- type EachSignalResult = Directive & {
5
- [EACH_SIGNAL]: ReadonlySignal<{
6
- bindings: Binding[];
7
- html: string;
8
- items?: Array<{
9
- bindings: Binding[];
10
- html: string;
11
- }>;
12
- keys?: (string | number)[];
13
- }>;
14
- };
2
+ import { type DirectiveResult, type HTMLResult } from '../internal';
15
3
  export interface EachOptions<T> {
16
4
  fallback?: () => string | HTMLResult;
17
- key?: (item: T, index: number) => string | number;
5
+ key: (item: T, index: number) => string | number;
18
6
  render: (item: T, index: number) => string | HTMLResult;
19
- /**
20
- * Filter predicate. Receives the item and its original array index.
21
- * Only items for which `select` returns `true` are rendered.
22
- */
23
- select?: (item: T, index: number) => boolean;
24
7
  }
25
8
  /**
26
9
  * Renders a list with keyed DOM reconciliation for reactive sources.
27
10
  * Use inside `html` tagged templates.
28
11
  *
29
- * For reactive sources (Signal/getter), you must provide a stable `key` function.
12
+ * `each()` expects a reactive signal source and a stable `key` function.
30
13
  *
31
14
  * For dynamic lists with click handlers, prefer event delegation on a parent node
32
15
  * (`@click` + `closest(...)`) over per-item handlers inside `each()`.
33
16
  *
34
17
  * @example
35
- * import { each } from '@vielzeug/craftit/directives';
36
- *
37
- * // Static array (key optional):
38
- * html`${each([1, 2, 3], { render: (item) => html`<li>${item}</li>` })}`
18
+ * import { each } from '@vielzeug/craftit';
39
19
  *
40
20
  * // Reactive source (key required):
41
21
  * html`${each(items, { key: item => item.id, render: (item) => html`<li>${item.name}</li>` })}`
@@ -45,12 +25,7 @@ export interface EachOptions<T> {
45
25
  * fallback: () => html`<p>No items</p>`,
46
26
  * key: item => item.id,
47
27
  * render: (item) => html`<li>${item.name}</li>`,
48
- * select: item => item.active,
49
28
  * })}`
50
29
  */
51
- export declare function each<T>(source: T[], options: EachOptions<T>): HTMLResult;
52
- export declare function each<T>(source: ReactiveSource<T>, options: EachOptions<T> & {
53
- key: (item: T, index: number) => string | number;
54
- }): EachSignalResult;
55
- export {};
30
+ export declare function each<T>(source: ReadonlySignal<T[]>, options: EachOptions<T>): DirectiveResult;
56
31
  //# sourceMappingURL=each.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"each.d.ts","sourceRoot":"","sources":["../../src/directives/each.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE5E,OAAO,EAEL,WAAW,EAKX,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AA8FrB,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,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,MAAM,GAAG,UAAU,CAAC;IACrC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAAC;IAClD,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,UAAU,CAAC;IACxD;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;CAC9C;AA4BD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC;AAC1E,wBAAgB,IAAI,CAAC,CAAC,EACpB,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,EACzB,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"}
1
+ {"version":3,"file":"each.d.ts","sourceRoot":"","sources":["../../src/directives/each.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqD,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAG3G,OAAO,EAUL,KAAK,eAAe,EACpB,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AAgDrB,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,MAAM,GAAG,UAAU,CAAC;IACrC,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAAC;IACjD,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,UAAU,CAAC;CACzD;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC"}
@@ -1,2 +1,2 @@
1
- import{EACH_SIGNAL as e,escapeHtml as t,extractResult as n,htmlResult as r,isHtmlResult as i}from"../internal.js";import{computed as a,isSignal as o}from"@vielzeug/stateit";var s=RegExp(`u="(\\d+)"`,`g`),c=r(``),l=[],u=(e,n)=>i(e)?f(e,n):{bindings:l,html:t(e)},d=e=>i(e)?e:r(t(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=new Set,s=[],c={n:0};for(let l=0;l<e.length;l++){let d=n(e[l],l);if(o.has(d))throw Error(`[craftit:each] Duplicate key "${String(d)}" at index ${l}.`);o.add(d),a.push(d);let f=u(t(e[l],l),c);r+=f.html,i.push(...f.bindings),s.push(f)}return{bindings:i,html:r,keys:a,rendered:s}}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}}var h=e=>typeof e==`function`,g=e=>{if(!h(e.render))throw Error(`[craftit:each] options.render must be a function.`);if(e.key!==void 0&&!h(e.key))throw Error(`[craftit:each] options.key must be a function when provided.`);if(e.select!==void 0&&!h(e.select))throw Error(`[craftit:each] options.select must be a function when provided.`);if(e.fallback!==void 0&&!h(e.fallback))throw Error(`[craftit:each] options.fallback must be a function when provided.`)},_=e=>{if(Array.isArray(e))return e;throw Error(`[craftit:each] source must resolve to an array.`)};function v(t,i){g(i);let{fallback:s,key:l,render:u,select:f}=i;if(Array.isArray(t)){let e=f?t.filter(f):t;if(!e.length){if(s){let e=n(d(s()));return r(e.html,e.bindings)}return c}let{bindings:i,html:a}=m(e,u);return r(a,i)}if(!l)throw Error(`[craftit:each] Reactive each() requires options.key for stable reconciliation.`);let h=o(t)?()=>t.value:t;return{[e]:a(()=>{let e=_(h()),t=f?e.filter(f):e;if(!t.length){let e=s?d(s()):void 0;return e?{...n(e),items:[],keys:[]}:{bindings:[],html:``,items:[],keys:[]}}let{bindings:r,html:i,keys:a,rendered:o}=p(t,u,l);return{bindings:r,html:i,items:o,keys:a}})}}export{v as each};
1
+ import{CRAFTIT_ERRORS as e}from"../errors.js";import{createMarkerIdFactory as t,escapeHtml as n,extractResult as r,htmlResult as i,isHtmlResult as a,rekeyHtmlResult as o,removeNodes as s,runAll as c}from"../internal.js";import{applyBindingsWithTargets as l,applyHtmlBinding as u,indexBindingTargets as d,parseHTML as f}from"../template-bindings.js";import{batch as p,effect as m,untrack as h}from"@vielzeug/stateit";var g=(e,t)=>a(e)?o(e,t):{bindings:[],html:n(e)},_=e=>a(e)?e:i(n(e));function v(n,r,i){let a=``,o=[],s=[],c=new Set,l=[],u=t();for(let t=0;t<n.length;t++){let d=i(n[t],t);if(c.has(d))throw Error(e.eachDuplicateKey(String(d),t));c.add(d),s.push(d);let f=g(r(n[t],t),u);a+=f.html,o.push(...f.bindings),l.push(f)}return{bindings:o,html:a,keys:s,rendered:l}}function y(e,t){let{fallback:n,key:i,render:a}=t;return{__craftitDirective:!0,mount:(t,o)=>{let g=new Map,y=[],b=[],x=()=>{s(y),y=[],c(b),b=[]},S=()=>{for(let[,e]of g)s(e.nodes),c(e.cleanups);g.clear()};o(m(()=>{let o=t.parentNode;o&&p(()=>{let p=e.value;if(!p.length){if(S(),x(),!n)return;let e=r(_(n())),i=f(e.html);y=Array.from(i.childNodes),t.after(i);let a=e=>b.push(e);l(e.bindings,a,d(y),{onHtml:e=>u(o,e,a)});return}x();let{keys:m,rendered:C}=v(p,a,i),w=new Map,T=[];for(let e=0;e<m.length;e++){let t=m[e],n=C[e],r=g.get(t),i,a=[];if(r&&r.html===n.html)i=r.nodes,c(r.cleanups);else{r&&(s(r.nodes),c(r.cleanups));let e=f(n.html);i=Array.from(e.childNodes)}T.push({item:n,key:t,nodes:i}),w.set(t,{cleanups:a,html:n.html,nodes:i})}for(let[e,t]of g)w.has(e)||(s(t.nodes),c(t.cleanups));let E=t;for(let e of T){for(let t of e.nodes)o.insertBefore(t,E.nextSibling),E=t;let t=w.get(e.key);if(!t)continue;let n=e=>t.cleanups.push(e);h(()=>{l(e.item.bindings,n,d(e.nodes),{onHtml:e=>u(o,e,n)})})}g=w})})),o(()=>{x(),S()})}}}export{y as each};
2
2
  //# sourceMappingURL=each.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"each.js","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport {\n CF_ID_ATTR,\n EACH_SIGNAL,\n escapeHtml,\n extractResult,\n htmlResult,\n isHtmlResult,\n type Binding,\n type Directive,\n type HTMLResult,\n} from '../internal';\n\nconst ATTR_ID_RE = new RegExp(`${CF_ID_ATTR}=\"(\\\\d+)\"`, 'g');\n\n/* immutable — shared singleton for empty static lists */\nconst EMPTY = htmlResult('');\nconst NO_BINDINGS: Binding[] = [];\n\nconst toResultEntry = (value: string | HTMLResult, c: { n: number }): { bindings: Binding[]; html: string } =>\n isHtmlResult(value) ? renumber(value, c) : { bindings: NO_BINDINGS, html: escapeHtml(value) };\n\nconst toHtmlResult = (value: string | HTMLResult): HTMLResult =>\n isHtmlResult(value) ? value : htmlResult(escapeHtml(value));\n\nfunction renumber(res: HTMLResult, c: { n: number }): { bindings: Binding[]; html: string } {\n const map = new Map<string, string>();\n const nb: Binding[] = [];\n\n for (const b of res.__bindings) {\n const oldId = b.uid;\n const newId = map.get(oldId) ?? String(c.n++);\n\n if (!map.has(oldId)) map.set(oldId, newId);\n\n nb.push({ ...b, uid: newId });\n }\n\n return {\n bindings: nb,\n html: res.__html\n // Re-map element binding ids.\n .replace(ATTR_ID_RE, (_, id) => `${CF_ID_ATTR}=\"${map.get(id) ?? id}\"`)\n // Re-map numeric comment markers used by text/html placeholders.\n .replace(/<!--(\\d+)-->/g, (_, id) => `<!--${map.get(id) ?? id}-->`),\n };\n}\n\n/** Render loop used by the reactive path (keys + rendered metadata required for reconciliation). */\nfunction renderKeyed<T>(\n items: T[],\n template: (item: T, index: number) => string | HTMLResult,\n keyFn: (item: T, index: number) => string | number,\n): {\n bindings: Binding[];\n html: string;\n keys: (string | number)[];\n rendered: Array<{ bindings: Binding[]; html: string }>;\n} {\n let html = '';\n const allBindings: Binding[] = [];\n const keys: (string | number)[] = [];\n const seenKeys = new Set<string | number>();\n const rendered: Array<{ bindings: Binding[]; html: string }> = [];\n const c = { n: 0 };\n\n for (let i = 0; i < items.length; i++) {\n const nextKey = keyFn(items[i], i);\n\n if (seenKeys.has(nextKey)) {\n throw new Error(`[craftit:each] Duplicate key \"${String(nextKey)}\" at index ${i}.`);\n }\n\n seenKeys.add(nextKey);\n keys.push(nextKey);\n\n const entry = toResultEntry(template(items[i], i), c);\n\n html += entry.html;\n allBindings.push(...entry.bindings);\n rendered.push(entry);\n }\n\n return { bindings: allBindings, html, keys, rendered };\n}\n\n/** Render loop used by the static path — no key/reconciliation metadata needed. */\nfunction renderStatic<T>(\n items: T[],\n template: (item: T, index: number) => string | HTMLResult,\n): { bindings: Binding[]; html: string } {\n let html = '';\n const allBindings: Binding[] = [];\n const c = { n: 0 };\n\n for (let i = 0; i < items.length; i++) {\n const entry = toResultEntry(template(items[i], i), c);\n\n html += entry.html;\n allBindings.push(...entry.bindings);\n }\n\n return { bindings: allBindings, html };\n}\n\ntype ReactiveSource<T> = ReadonlySignal<T[]> | (() => T[]);\ntype EachSignalResult = Directive & {\n [EACH_SIGNAL]: ReadonlySignal<{\n bindings: Binding[];\n html: string;\n items?: Array<{ bindings: Binding[]; html: string }>;\n keys?: (string | number)[];\n }>;\n};\n\nexport interface EachOptions<T> {\n fallback?: () => string | HTMLResult;\n key?: (item: T, index: number) => string | number;\n render: (item: T, index: number) => string | HTMLResult;\n /**\n * Filter predicate. Receives the item and its original array index.\n * Only items for which `select` returns `true` are rendered.\n */\n select?: (item: T, index: number) => boolean;\n}\n\nconst isFunction = (value: unknown): value is (...args: any[]) => any => typeof value === 'function';\n\nconst validateEachOptions = <T>(options: EachOptions<T>): void => {\n if (!isFunction(options.render)) {\n throw new Error('[craftit:each] options.render must be a function.');\n }\n\n if (options.key !== undefined && !isFunction(options.key)) {\n throw new Error('[craftit:each] options.key must be a function when provided.');\n }\n\n if (options.select !== undefined && !isFunction(options.select)) {\n throw new Error('[craftit:each] options.select must be a function when provided.');\n }\n\n if (options.fallback !== undefined && !isFunction(options.fallback)) {\n throw new Error('[craftit:each] options.fallback must be a function when provided.');\n }\n};\n\nconst assertArrayResult = <T>(value: T[] | unknown): T[] => {\n if (Array.isArray(value)) return value as T[];\n\n throw new Error('[craftit:each] source must resolve to an array.');\n};\n\n/**\n * Renders a list with keyed DOM reconciliation for reactive sources.\n * Use inside `html` tagged templates.\n *\n * For reactive sources (Signal/getter), you must provide a stable `key` function.\n *\n * For dynamic lists with click handlers, prefer event delegation on a parent node\n * (`@click` + `closest(...)`) over per-item handlers inside `each()`.\n *\n * @example\n * import { each } from '@vielzeug/craftit/directives';\n *\n * // Static array (key optional):\n * html`${each([1, 2, 3], { render: (item) => html`<li>${item}</li>` })}`\n *\n * // Reactive source (key required):\n * html`${each(items, { key: item => item.id, render: (item) => html`<li>${item.name}</li>` })}`\n *\n * // Full example:\n * html`${each(items, {\n * fallback: () => html`<p>No items</p>`,\n * key: item => item.id,\n * render: (item) => html`<li>${item.name}</li>`,\n * select: item => item.active,\n * })}`\n */\nexport function each<T>(source: T[], options: EachOptions<T>): HTMLResult;\nexport function each<T>(\n source: ReactiveSource<T>,\n options: EachOptions<T> & { key: (item: T, index: number) => string | number },\n): EachSignalResult;\nexport function each<T>(source: T[] | ReactiveSource<T>, options: EachOptions<T>): HTMLResult | EachSignalResult {\n validateEachOptions(options);\n\n const { fallback, key, render, select } = options;\n\n if (Array.isArray(source)) {\n const filtered = select ? source.filter(select) : source;\n\n if (!filtered.length) {\n if (fallback) {\n const er = extractResult(toHtmlResult(fallback()));\n\n return htmlResult(er.html, er.bindings);\n }\n\n return EMPTY;\n }\n\n const { bindings, html } = renderStatic(filtered, render);\n\n return htmlResult(html, bindings);\n }\n\n if (!key) {\n throw new Error('[craftit:each] Reactive each() requires options.key for stable reconciliation.');\n }\n\n const getItems = isSignal(source) ? () => (source as ReadonlySignal<T[]>).value : (source as () => T[]);\n\n return {\n [EACH_SIGNAL]: computed(() => {\n const raw = assertArrayResult<T>(getItems());\n const filtered = select ? raw.filter(select) : raw;\n\n if (!filtered.length) {\n const er = fallback ? toHtmlResult(fallback()) : undefined;\n\n return er ? { ...extractResult(er), items: [], keys: [] } : { bindings: [], html: '', items: [], keys: [] };\n }\n\n const { bindings, html, keys, rendered } = renderKeyed(filtered, render, key);\n\n return { bindings, html, items: rendered, keys };\n }),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"each.js","names":[],"sources":["../../src/directives/each.ts"],"sourcesContent":["import { batch, effect as _effect, untrack, type CleanupFn, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport { CRAFTIT_ERRORS } from '../errors';\nimport {\n createMarkerIdFactory,\n escapeHtml,\n extractResult,\n htmlResult,\n isHtmlResult,\n removeNodes,\n rekeyHtmlResult,\n runAll,\n type Binding,\n type DirectiveResult,\n type HTMLResult,\n} from '../internal';\nimport { applyBindingsWithTargets, indexBindingTargets, parseHTML, type RegisterCleanup } from '../template-bindings';\nimport { applyHtmlBinding } from '../template-bindings';\n\nconst toResultEntry = (value: string | HTMLResult, getNextId: () => string): { bindings: Binding[]; html: string } =>\n isHtmlResult(value) ? rekeyHtmlResult(value, getNextId) : { bindings: [], html: escapeHtml(value) };\n\nconst toHtmlResult = (value: string | HTMLResult): HTMLResult =>\n isHtmlResult(value) ? value : htmlResult(escapeHtml(value));\n\n/** Render loop used by the reactive path (keys + rendered metadata required for reconciliation). */\nfunction renderKeyed<T>(\n items: T[],\n template: (item: T, index: number) => string | HTMLResult,\n keyFn: (item: T, index: number) => string | number,\n): {\n bindings: Binding[];\n html: string;\n keys: (string | number)[];\n rendered: Array<{ bindings: Binding[]; html: string }>;\n} {\n let html = '';\n const allBindings: Binding[] = [];\n const keys: (string | number)[] = [];\n const seenKeys = new Set<string | number>();\n const rendered: Array<{ bindings: Binding[]; html: string }> = [];\n const getNextId = createMarkerIdFactory();\n\n for (let i = 0; i < items.length; i++) {\n const nextKey = keyFn(items[i], i);\n\n if (seenKeys.has(nextKey)) {\n throw new Error(CRAFTIT_ERRORS.eachDuplicateKey(String(nextKey), i));\n }\n\n seenKeys.add(nextKey);\n keys.push(nextKey);\n\n const entry = toResultEntry(template(items[i], i), getNextId);\n\n html += entry.html;\n allBindings.push(...entry.bindings);\n rendered.push(entry);\n }\n\n return { bindings: allBindings, html, keys, rendered };\n}\n\nexport interface EachOptions<T> {\n fallback?: () => string | HTMLResult;\n key: (item: T, index: number) => string | number;\n render: (item: T, index: number) => string | HTMLResult;\n}\n\n/**\n * Renders a list with keyed DOM reconciliation for reactive sources.\n * Use inside `html` tagged templates.\n *\n * `each()` expects a reactive signal source and a stable `key` function.\n *\n * For dynamic lists with click handlers, prefer event delegation on a parent node\n * (`@click` + `closest(...)`) over per-item handlers inside `each()`.\n *\n * @example\n * import { each } from '@vielzeug/craftit';\n *\n * // Reactive source (key required):\n * html`${each(items, { key: item => item.id, render: (item) => html`<li>${item.name}</li>` })}`\n *\n * // Full example:\n * html`${each(items, {\n * fallback: () => html`<p>No items</p>`,\n * key: item => item.id,\n * render: (item) => html`<li>${item.name}</li>`,\n * })}`\n */\nexport function each<T>(source: ReadonlySignal<T[]>, options: EachOptions<T>): DirectiveResult;\nexport function each<T>(source: ReadonlySignal<T[]>, options: EachOptions<T>): DirectiveResult {\n const { fallback, key, render: renderItem } = options;\n\n const mount = (anchor: Comment, registerCleanup: RegisterCleanup): void => {\n type KeyedNode = {\n cleanups: CleanupFn[];\n html: string;\n nodes: Node[];\n };\n\n let keyedNodes = new Map<string | number, KeyedNode>();\n let fallbackNodes: Node[] = [];\n let fallbackCleanups: CleanupFn[] = [];\n\n const clearFallback = (): void => {\n removeNodes(fallbackNodes);\n fallbackNodes = [];\n runAll(fallbackCleanups);\n fallbackCleanups = [];\n };\n\n const clearKeyed = (): void => {\n for (const [, node] of keyedNodes) {\n removeNodes(node.nodes);\n runAll(node.cleanups);\n }\n\n keyedNodes.clear();\n };\n\n const stop = _effect(() => {\n const parent = anchor.parentNode;\n\n if (!parent) return;\n\n batch(() => {\n const raw = source.value;\n\n if (!raw.length) {\n clearKeyed();\n clearFallback();\n\n if (!fallback) return;\n\n const fallbackResult = extractResult(toHtmlResult(fallback()));\n const parsed = parseHTML(fallbackResult.html);\n\n fallbackNodes = Array.from(parsed.childNodes);\n anchor.after(parsed);\n\n const addFallbackCleanup: RegisterCleanup = (fn) => fallbackCleanups.push(fn);\n\n applyBindingsWithTargets(fallbackResult.bindings, addFallbackCleanup, indexBindingTargets(fallbackNodes), {\n onHtml: (binding) => applyHtmlBinding(parent as unknown as Node, binding, addFallbackCleanup),\n });\n\n return;\n }\n\n clearFallback();\n\n const { keys, rendered } = renderKeyed(raw, renderItem, key);\n const nextKeyed = new Map<string | number, KeyedNode>();\n const ordered: Array<{ item: { bindings: Binding[]; html: string }; key: string | number; nodes: Node[] }> = [];\n\n for (let i = 0; i < keys.length; i++) {\n const nextKey = keys[i];\n const item = rendered[i];\n const existing = keyedNodes.get(nextKey);\n let nodes: Node[];\n const cleanups: CleanupFn[] = [];\n\n if (existing && existing.html === item.html) {\n nodes = existing.nodes;\n runAll(existing.cleanups);\n } else {\n if (existing) {\n removeNodes(existing.nodes);\n runAll(existing.cleanups);\n }\n\n const parsed = parseHTML(item.html);\n\n nodes = Array.from(parsed.childNodes);\n }\n\n ordered.push({ item, key: nextKey, nodes });\n nextKeyed.set(nextKey, { cleanups, html: item.html, nodes });\n }\n\n for (const [oldKey, oldNode] of keyedNodes) {\n if (!nextKeyed.has(oldKey)) {\n removeNodes(oldNode.nodes);\n runAll(oldNode.cleanups);\n }\n }\n\n let cursor: Node = anchor;\n\n for (const entry of ordered) {\n for (const node of entry.nodes) {\n parent.insertBefore(node, cursor.nextSibling);\n cursor = node;\n }\n\n const targetNode = nextKeyed.get(entry.key);\n\n if (!targetNode) continue;\n\n const addItemCleanup: RegisterCleanup = (fn) => targetNode.cleanups.push(fn);\n\n untrack(() => {\n applyBindingsWithTargets(entry.item.bindings, addItemCleanup, indexBindingTargets(entry.nodes), {\n onHtml: (binding) => applyHtmlBinding(parent as unknown as Node, binding, addItemCleanup),\n });\n });\n }\n\n keyedNodes = nextKeyed;\n });\n });\n\n registerCleanup(stop);\n registerCleanup(() => {\n clearFallback();\n clearKeyed();\n });\n };\n\n return { __craftitDirective: true, mount };\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ let e=require(`@vielzeug/stateit`);var t=t=>typeof t==`function`?t():(0,e.isSignal)(t)?t.value:t,n=(e,t)=>{if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(!Object.is(e[n],t[n]))return!1;return!0};function r(r,i){let a=!1,o=[],s=``;return(0,e.computed)(()=>{let c=(Array.isArray(r)?r:[r]).map(e=>t(e));return(!a||!n(o,c))&&(s=(0,e.untrack)(i),o=c,a=!0),s})}exports.guard=r;
2
+ //# sourceMappingURL=guard.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.cjs","names":[],"sources":["../../src/directives/guard.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, untrack } from '@vielzeug/stateit';\n\nimport type { HTMLResult } from '../internal';\n\ntype GuardDep = unknown | (() => unknown) | ReadonlySignal<unknown>;\n\ntype GuardRenderable = string | HTMLResult;\n\nconst resolve = (value: GuardDep): unknown => {\n if (typeof value === 'function') return (value as () => unknown)();\n\n if (isSignal(value)) return value.value;\n\n return value;\n};\n\nconst depsEqual = (prev: unknown[], next: unknown[]): boolean => {\n if (prev.length !== next.length) return false;\n\n for (let index = 0; index < prev.length; index++) {\n if (!Object.is(prev[index], next[index])) return false;\n }\n\n return true;\n};\n\n/**\n * Memoizes rendering until the provided dependency tuple changes.\n */\nexport function guard(deps: GuardDep | GuardDep[], render: () => GuardRenderable): ReadonlySignal<GuardRenderable> {\n let initialized = false;\n let previousDeps: unknown[] = [];\n let cached: GuardRenderable = '';\n\n return computed(() => {\n const resolvedDeps = (Array.isArray(deps) ? deps : [deps]).map((dep) => resolve(dep));\n\n if (!initialized || !depsEqual(previousDeps, resolvedDeps)) {\n // untrack() prevents signals read inside render() from invalidating this\n // computed — only changes to the explicit dep array trigger re-renders.\n cached = untrack(render);\n previousDeps = resolvedDeps;\n initialized = true;\n }\n\n return cached;\n });\n}\n"],"mappings":"mCAQA,IAAM,EAAW,GACX,OAAO,GAAU,WAAoB,EAAwB,GAEjE,EAAA,EAAA,UAAa,CAAK,EAAU,EAAM,MAE3B,EAGH,GAAa,EAAiB,IAA6B,CAC/D,GAAI,EAAK,SAAW,EAAK,OAAQ,MAAO,GAExC,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAK,OAAQ,IACvC,GAAI,CAAC,OAAO,GAAG,EAAK,GAAQ,EAAK,EAAM,EAAG,MAAO,GAGnD,MAAO,EACT,EAKA,SAAgB,EAAM,EAA6B,EAAgE,CACjH,IAAI,EAAc,GACd,EAA0B,CAAC,EAC3B,EAA0B,GAE9B,OAAA,EAAA,EAAA,cAAsB,CACpB,IAAM,GAAgB,MAAM,QAAQ,CAAI,EAAI,EAAO,CAAC,CAAI,GAAG,IAAK,GAAQ,EAAQ,CAAG,CAAC,EAUpF,OARI,CAAC,GAAe,CAAC,EAAU,EAAc,CAAY,KAGvD,GAAA,EAAA,EAAA,SAAiB,CAAM,EACvB,EAAe,EACf,EAAc,IAGT,CACT,CAAC,CACH"}
@@ -0,0 +1,10 @@
1
+ import { type ReadonlySignal } from '@vielzeug/stateit';
2
+ import type { HTMLResult } from '../internal';
3
+ type GuardDep = unknown | (() => unknown) | ReadonlySignal<unknown>;
4
+ type GuardRenderable = string | HTMLResult;
5
+ /**
6
+ * Memoizes rendering until the provided dependency tuple changes.
7
+ */
8
+ export declare function guard(deps: GuardDep | GuardDep[], render: () => GuardRenderable): ReadonlySignal<GuardRenderable>;
9
+ export {};
10
+ //# sourceMappingURL=guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/directives/guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAAW,MAAM,mBAAmB,CAAC;AAErF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,KAAK,QAAQ,GAAG,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;AAEpE,KAAK,eAAe,GAAG,MAAM,GAAG,UAAU,CAAC;AAoB3C;;GAEG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,eAAe,GAAG,cAAc,CAAC,eAAe,CAAC,CAkBjH"}
@@ -0,0 +1,2 @@
1
+ import{computed as e,isSignal as t,untrack as n}from"@vielzeug/stateit";var r=e=>typeof e==`function`?e():t(e)?e.value:e,i=(e,t)=>{if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(!Object.is(e[n],t[n]))return!1;return!0};function a(t,a){let o=!1,s=[],c=``;return e(()=>{let e=(Array.isArray(t)?t:[t]).map(e=>r(e));return(!o||!i(s,e))&&(c=n(a),s=e,o=!0),c})}export{a as guard};
2
+ //# sourceMappingURL=guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.js","names":[],"sources":["../../src/directives/guard.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, untrack } from '@vielzeug/stateit';\n\nimport type { HTMLResult } from '../internal';\n\ntype GuardDep = unknown | (() => unknown) | ReadonlySignal<unknown>;\n\ntype GuardRenderable = string | HTMLResult;\n\nconst resolve = (value: GuardDep): unknown => {\n if (typeof value === 'function') return (value as () => unknown)();\n\n if (isSignal(value)) return value.value;\n\n return value;\n};\n\nconst depsEqual = (prev: unknown[], next: unknown[]): boolean => {\n if (prev.length !== next.length) return false;\n\n for (let index = 0; index < prev.length; index++) {\n if (!Object.is(prev[index], next[index])) return false;\n }\n\n return true;\n};\n\n/**\n * Memoizes rendering until the provided dependency tuple changes.\n */\nexport function guard(deps: GuardDep | GuardDep[], render: () => GuardRenderable): ReadonlySignal<GuardRenderable> {\n let initialized = false;\n let previousDeps: unknown[] = [];\n let cached: GuardRenderable = '';\n\n return computed(() => {\n const resolvedDeps = (Array.isArray(deps) ? deps : [deps]).map((dep) => resolve(dep));\n\n if (!initialized || !depsEqual(previousDeps, resolvedDeps)) {\n // untrack() prevents signals read inside render() from invalidating this\n // computed — only changes to the explicit dep array trigger re-renders.\n cached = untrack(render);\n previousDeps = resolvedDeps;\n initialized = true;\n }\n\n return cached;\n });\n}\n"],"mappings":"wEAQA,IAAM,EAAW,GACX,OAAO,GAAU,WAAoB,EAAwB,EAE7D,EAAS,CAAK,EAAU,EAAM,MAE3B,EAGH,GAAa,EAAiB,IAA6B,CAC/D,GAAI,EAAK,SAAW,EAAK,OAAQ,MAAO,GAExC,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAK,OAAQ,IACvC,GAAI,CAAC,OAAO,GAAG,EAAK,GAAQ,EAAK,EAAM,EAAG,MAAO,GAGnD,MAAO,EACT,EAKA,SAAgB,EAAM,EAA6B,EAAgE,CACjH,IAAI,EAAc,GACd,EAA0B,CAAC,EAC3B,EAA0B,GAE9B,OAAO,MAAe,CACpB,IAAM,GAAgB,MAAM,QAAQ,CAAI,EAAI,EAAO,CAAC,CAAI,GAAG,IAAK,GAAQ,EAAQ,CAAG,CAAC,EAUpF,OARI,CAAC,GAAe,CAAC,EAAU,EAAc,CAAY,KAGvD,EAAS,EAAQ,CAAM,EACvB,EAAe,EACf,EAAc,IAGT,CACT,CAAC,CACH"}
@@ -0,0 +1,2 @@
1
+ var e=e=>({__live:!0,get value(){return e.value}}),t=e=>typeof e==`object`&&!!e&&e.__live===!0;exports.isLiveSignal=t,exports.live=e;
2
+ //# sourceMappingURL=live.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live.cjs","names":[],"sources":["../../src/directives/live.ts"],"sourcesContent":["import type { ReadonlySignal } from '@vielzeug/stateit';\n\n/**\n * A branded signal that tells the attribute binding engine to skip writing when\n * the DOM value has diverged from the last programmatically-written value.\n * Created via live(signal) — pass a signal directly rather than wrapping a value.\n *\n * @example\n * html`<input :value=\"${live(model)}\" />`\n */\nexport type LiveSignal<T> = ReadonlySignal<T> & { readonly __live: true };\n\n/**\n * Marks a signal binding as \"live\" so stale app-state writes never clobber\n * in-progress user input.\n *\n * For form controls: if the current DOM value diverges from the last write made\n * by this binding, subsequent app-state writes are silently dropped until the\n * DOM value matches the incoming value or no prior write has been recorded.\n */\nexport const live = <T>(source: ReadonlySignal<T>): LiveSignal<T> =>\n ({\n __live: true as const,\n get value(): T {\n return source.value;\n },\n }) as LiveSignal<T>;\n\nexport const isLiveSignal = (value: unknown): value is LiveSignal<unknown> =>\n typeof value === 'object' && value !== null && (value as Record<string, unknown>).__live === true;\n"],"mappings":"AAoBA,IAAa,EAAW,IACrB,CACC,OAAQ,GACR,IAAI,OAAW,CACb,OAAO,EAAO,KAChB,CACF,GAEW,EAAgB,GAC3B,OAAO,GAAU,YAAY,GAAmB,EAAkC,SAAW"}
@@ -0,0 +1,23 @@
1
+ import type { ReadonlySignal } from '@vielzeug/stateit';
2
+ /**
3
+ * A branded signal that tells the attribute binding engine to skip writing when
4
+ * the DOM value has diverged from the last programmatically-written value.
5
+ * Created via live(signal) — pass a signal directly rather than wrapping a value.
6
+ *
7
+ * @example
8
+ * html`<input :value="${live(model)}" />`
9
+ */
10
+ export type LiveSignal<T> = ReadonlySignal<T> & {
11
+ readonly __live: true;
12
+ };
13
+ /**
14
+ * Marks a signal binding as "live" so stale app-state writes never clobber
15
+ * in-progress user input.
16
+ *
17
+ * For form controls: if the current DOM value diverges from the last write made
18
+ * by this binding, subsequent app-state writes are silently dropped until the
19
+ * DOM value matches the incoming value or no prior write has been recorded.
20
+ */
21
+ export declare const live: <T>(source: ReadonlySignal<T>) => LiveSignal<T>;
22
+ export declare const isLiveSignal: (value: unknown) => value is LiveSignal<unknown>;
23
+ //# sourceMappingURL=live.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../../src/directives/live.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,CAAC,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAA;CAAE,CAAC;AAE1E;;;;;;;GAOG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,EAAE,QAAQ,cAAc,CAAC,CAAC,CAAC,KAAG,UAAU,CAAC,CAAC,CAM3C,CAAC;AAEtB,eAAO,MAAM,YAAY,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,UAAU,CAAC,OAAO,CAC0B,CAAC"}
@@ -0,0 +1,2 @@
1
+ var e=e=>({__live:!0,get value(){return e.value}}),t=e=>typeof e==`object`&&!!e&&e.__live===!0;export{t as isLiveSignal,e as live};
2
+ //# sourceMappingURL=live.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live.js","names":[],"sources":["../../src/directives/live.ts"],"sourcesContent":["import type { ReadonlySignal } from '@vielzeug/stateit';\n\n/**\n * A branded signal that tells the attribute binding engine to skip writing when\n * the DOM value has diverged from the last programmatically-written value.\n * Created via live(signal) — pass a signal directly rather than wrapping a value.\n *\n * @example\n * html`<input :value=\"${live(model)}\" />`\n */\nexport type LiveSignal<T> = ReadonlySignal<T> & { readonly __live: true };\n\n/**\n * Marks a signal binding as \"live\" so stale app-state writes never clobber\n * in-progress user input.\n *\n * For form controls: if the current DOM value diverges from the last write made\n * by this binding, subsequent app-state writes are silently dropped until the\n * DOM value matches the incoming value or no prior write has been recorded.\n */\nexport const live = <T>(source: ReadonlySignal<T>): LiveSignal<T> =>\n ({\n __live: true as const,\n get value(): T {\n return source.value;\n },\n }) as LiveSignal<T>;\n\nexport const isLiveSignal = (value: unknown): value is LiveSignal<unknown> =>\n typeof value === 'object' && value !== null && (value as Record<string, unknown>).__live === true;\n"],"mappings":"AAoBA,IAAa,EAAW,IACrB,CACC,OAAQ,GACR,IAAI,OAAW,CACb,OAAO,EAAO,KAChB,CACF,GAEW,EAAgB,GAC3B,OAAO,GAAU,YAAY,GAAmB,EAAkC,SAAW"}
@@ -1,2 +1,2 @@
1
- const e=require(`../internal.cjs`);let t=require(`@vielzeug/stateit`);function n(n){return(0,t.isSignal)(n)?(0,t.computed)(()=>e.htmlResult(n.value)):typeof n==`function`?(0,t.computed)(()=>e.htmlResult(n())):e.htmlResult(n)}exports.raw=n;
1
+ const e=require(`../internal.cjs`);let t=require(`@vielzeug/stateit`);function n(n){return(0,t.isSignal)(n)?(0,t.computed)(()=>e.htmlResult(n.value)):e.htmlResult(n)}exports.raw=n;
2
2
  //# sourceMappingURL=raw.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"raw.cjs","names":[],"sources":["../../src/directives/raw.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport { htmlResult, type HTMLResult } from '../internal';\n\n/**\n * Renders a trusted HTML string without escaping.\n * **Only use with content you control** — passing user-supplied strings\n * directly is an XSS risk.\n *\n * Supports static strings, 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":"sEAyBA,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"}
1
+ {"version":3,"file":"raw.cjs","names":[],"sources":["../../src/directives/raw.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport { htmlResult, type HTMLResult } from '../internal';\n\n/**\n * Renders a trusted HTML string without escaping.\n * **Only use with content you control** — passing user-supplied strings\n * directly is an XSS risk.\n *\n * Supports static strings and signals.\n * When reactive, the DOM is updated in-place whenever the value changes.\n *\n * @example\n * import { raw } from '@vielzeug/craftit';\n *\n * // Static\n * html`<div>${raw('<strong>bold</strong>')}</div>`\n *\n * // Reactive signal\n * const content = signal('<em>hello</em>');\n * html`<div>${raw(content)}</div>`\n *\n */\nexport function raw(value: string | Signal<string> | ReadonlySignal<string>): HTMLResult | ReadonlySignal<HTMLResult> {\n if (isSignal(value)) {\n return computed(() => htmlResult((value as ReadonlySignal<string>).value));\n }\n\n return htmlResult(value);\n}\n"],"mappings":"sEAuBA,SAAgB,EAAI,EAAkG,CAKpH,OAJA,EAAA,EAAA,UAAa,CAAK,GAChB,EAAA,EAAA,cAAsB,EAAA,WAAY,EAAiC,KAAK,CAAC,EAGpE,EAAA,WAAW,CAAK,CACzB"}
@@ -5,11 +5,11 @@ import { type HTMLResult } from '../internal';
5
5
  * **Only use with content you control** — passing user-supplied strings
6
6
  * directly is an XSS risk.
7
7
  *
8
- * Supports static strings, writable Signals, and getter functions.
8
+ * Supports static strings and signals.
9
9
  * When reactive, the DOM is updated in-place whenever the value changes.
10
10
  *
11
11
  * @example
12
- * import { raw } from '@vielzeug/craftit/directives';
12
+ * import { raw } from '@vielzeug/craftit';
13
13
  *
14
14
  * // Static
15
15
  * html`<div>${raw('<strong>bold</strong>')}</div>`
@@ -18,8 +18,6 @@ import { type HTMLResult } from '../internal';
18
18
  * const content = signal('<em>hello</em>');
19
19
  * html`<div>${raw(content)}</div>`
20
20
  *
21
- * // Getter
22
- * html`<div>${raw(() => sanitize(props.body.value))}</div>`
23
21
  */
24
- export declare function raw(value: string | Signal<string> | ReadonlySignal<string> | (() => string)): HTMLResult | ReadonlySignal<HTMLResult>;
22
+ export declare function raw(value: string | Signal<string> | ReadonlySignal<string>): HTMLResult | ReadonlySignal<HTMLResult>;
25
23
  //# sourceMappingURL=raw.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"raw.d.ts","sourceRoot":"","sources":["../../src/directives/raw.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAAE,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEzF,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;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"}
1
+ {"version":3,"file":"raw.d.ts","sourceRoot":"","sources":["../../src/directives/raw.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAAE,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEzF,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,GAAG,UAAU,GAAG,cAAc,CAAC,UAAU,CAAC,CAMpH"}
@@ -1,2 +1,2 @@
1
- import{htmlResult as e}from"../internal.js";import{computed as t,isSignal as n}from"@vielzeug/stateit";function r(r){return n(r)?t(()=>e(r.value)):typeof r==`function`?t(()=>e(r())):e(r)}export{r as raw};
1
+ import{htmlResult as e}from"../internal.js";import{computed as t,isSignal as n}from"@vielzeug/stateit";function r(r){return n(r)?t(()=>e(r.value)):e(r)}export{r as raw};
2
2
  //# sourceMappingURL=raw.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"raw.js","names":[],"sources":["../../src/directives/raw.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport { htmlResult, type HTMLResult } from '../internal';\n\n/**\n * Renders a trusted HTML string without escaping.\n * **Only use with content you control** — passing user-supplied strings\n * directly is an XSS risk.\n *\n * Supports static strings, 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":"uGAyBA,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"}
1
+ {"version":3,"file":"raw.js","names":[],"sources":["../../src/directives/raw.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport { htmlResult, type HTMLResult } from '../internal';\n\n/**\n * Renders a trusted HTML string without escaping.\n * **Only use with content you control** — passing user-supplied strings\n * directly is an XSS risk.\n *\n * Supports static strings and signals.\n * When reactive, the DOM is updated in-place whenever the value changes.\n *\n * @example\n * import { raw } from '@vielzeug/craftit';\n *\n * // Static\n * html`<div>${raw('<strong>bold</strong>')}</div>`\n *\n * // Reactive signal\n * const content = signal('<em>hello</em>');\n * html`<div>${raw(content)}</div>`\n *\n */\nexport function raw(value: string | Signal<string> | ReadonlySignal<string>): HTMLResult | ReadonlySignal<HTMLResult> {\n if (isSignal(value)) {\n return computed(() => htmlResult((value as ReadonlySignal<string>).value));\n }\n\n return htmlResult(value);\n}\n"],"mappings":"uGAuBA,SAAgB,EAAI,EAAkG,CAKpH,OAJI,EAAS,CAAK,EACT,MAAe,EAAY,EAAiC,KAAK,CAAC,EAGpE,EAAW,CAAK,CACzB"}
@@ -0,0 +1,2 @@
1
+ const e=require(`../errors.cjs`),t=require(`../runtime.cjs`);let n=require(`@vielzeug/stateit`);function r(r,i){let a=(0,n.signal)({data:void 0,error:void 0,pending:!0}),o;if(!t.tryRegisterCleanup((0,n.effect)(()=>{let e=r(),t=new AbortController;return a.value={data:o,error:void 0,pending:!0},i(e,t.signal).then(e=>{t.signal.aborted||(o=e,a.value={data:e,error:void 0,pending:!1})}).catch(e=>{t.signal.aborted||(a.value={data:o,error:e,pending:!1})}),()=>t.abort()})))throw Error(e.CRAFTIT_ERRORS.lifecycleOutsideSetup);return a}exports.resource=r;
2
+ //# sourceMappingURL=resource.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.cjs","names":[],"sources":["../../src/directives/resource.ts"],"sourcesContent":["import { effect as signalEffect, type Signal, signal } from '@vielzeug/stateit';\n\nimport { CRAFTIT_ERRORS } from '../errors';\nimport { tryRegisterCleanup } from '../runtime';\n\nexport type ResourceState<T> = {\n /** Previous successful data, available while a reload is pending. */\n data: T | undefined;\n /** Rejection reason from the most recent failed fetch, or `undefined`. */\n error: unknown;\n /** `true` while the fetcher promise is in flight. */\n pending: boolean;\n};\n\n/**\n * Reactive async resource. Re-fetches whenever the `deps` getter returns a new value.\n * Previous data is preserved during reloads so the UI can show stale-while-revalidate UX.\n * In-flight requests are cancelled via `AbortController` when deps change.\n *\n * Must be called inside component `setup()` or another Craftit runtime scope.\n *\n * @example\n * ```ts\n * const user = resource(\n * () => props.userId.value,\n * (id, signal) => fetch(`/api/users/${id}`, { signal }).then(r => r.json()),\n * );\n *\n * return () => html`\n * ${when(() => user.value.pending, () => html`<p>Loading…</p>`)}\n * ${when(() => !!user.value.error, () => html`<p>Error</p>`)}\n * ${when(() => !!user.value.data, () => html`<p>${user.value.data?.name}</p>`)}\n * `;\n * ```\n */\nexport function resource<Deps, T>(deps: () => Deps, fetcher: (deps: Deps, signal: AbortSignal) => Promise<T>) {\n const state: Signal<ResourceState<T>> = signal({ data: undefined, error: undefined, pending: true });\n let previousData: T | undefined;\n\n // Use raw stateit effect and register cleanup explicitly.\n const dispose = signalEffect(() => {\n const currentDeps = deps();\n const controller = new AbortController();\n\n state.value = { data: previousData, error: undefined, pending: true };\n\n fetcher(currentDeps, controller.signal)\n .then((data) => {\n if (!controller.signal.aborted) {\n previousData = data;\n state.value = { data, error: undefined, pending: false };\n }\n })\n .catch((error: unknown) => {\n if (!controller.signal.aborted) {\n state.value = { data: previousData, error, pending: false };\n }\n });\n\n return () => controller.abort();\n });\n\n if (!tryRegisterCleanup(dispose)) throw new Error(CRAFTIT_ERRORS.lifecycleOutsideSetup);\n\n return state;\n}\n"],"mappings":"gGAmCA,SAAgB,EAAkB,EAAkB,EAA0D,CAC5G,IAAM,GAAA,EAAA,EAAA,QAAyC,CAAE,KAAM,IAAA,GAAW,MAAO,IAAA,GAAW,QAAS,EAAK,CAAC,EAC/F,EAyBJ,GAAI,CAAC,EAAA,oBAAA,EAAA,EAAA,YAtB8B,CACjC,IAAM,EAAc,EAAK,EACnB,EAAa,IAAI,gBAiBvB,MAfA,GAAM,MAAQ,CAAE,KAAM,EAAc,MAAO,IAAA,GAAW,QAAS,EAAK,EAEpE,EAAQ,EAAa,EAAW,MAAM,EACnC,KAAM,GAAS,CACT,EAAW,OAAO,UACrB,EAAe,EACf,EAAM,MAAQ,CAAE,OAAM,MAAO,IAAA,GAAW,QAAS,EAAM,EAE3D,CAAC,EACA,MAAO,GAAmB,CACpB,EAAW,OAAO,UACrB,EAAM,MAAQ,CAAE,KAAM,EAAc,QAAO,QAAS,EAAM,EAE9D,CAAC,MAEU,EAAW,MAAM,CAChC,CAEwB,CAAO,EAAG,MAAU,MAAM,EAAA,eAAe,qBAAqB,EAEtF,OAAO,CACT"}
@@ -0,0 +1,32 @@
1
+ import { type Signal } from '@vielzeug/stateit';
2
+ export type ResourceState<T> = {
3
+ /** Previous successful data, available while a reload is pending. */
4
+ data: T | undefined;
5
+ /** Rejection reason from the most recent failed fetch, or `undefined`. */
6
+ error: unknown;
7
+ /** `true` while the fetcher promise is in flight. */
8
+ pending: boolean;
9
+ };
10
+ /**
11
+ * Reactive async resource. Re-fetches whenever the `deps` getter returns a new value.
12
+ * Previous data is preserved during reloads so the UI can show stale-while-revalidate UX.
13
+ * In-flight requests are cancelled via `AbortController` when deps change.
14
+ *
15
+ * Must be called inside component `setup()` or another Craftit runtime scope.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const user = resource(
20
+ * () => props.userId.value,
21
+ * (id, signal) => fetch(`/api/users/${id}`, { signal }).then(r => r.json()),
22
+ * );
23
+ *
24
+ * return () => html`
25
+ * ${when(() => user.value.pending, () => html`<p>Loading…</p>`)}
26
+ * ${when(() => !!user.value.error, () => html`<p>Error</p>`)}
27
+ * ${when(() => !!user.value.data, () => html`<p>${user.value.data?.name}</p>`)}
28
+ * `;
29
+ * ```
30
+ */
31
+ export declare function resource<Deps, T>(deps: () => Deps, fetcher: (deps: Deps, signal: AbortSignal) => Promise<T>): Signal<ResourceState<T>>;
32
+ //# sourceMappingURL=resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/directives/resource.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,MAAM,EAAU,MAAM,mBAAmB,CAAC;AAKhF,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;IAC7B,qEAAqE;IACrE,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;IACpB,0EAA0E;IAC1E,KAAK,EAAE,OAAO,CAAC;IACf,qDAAqD;IACrD,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,4BA8B3G"}
@@ -0,0 +1,2 @@
1
+ import{CRAFTIT_ERRORS as e}from"../errors.js";import{tryRegisterCleanup as t}from"../runtime.js";import{effect as n,signal as r}from"@vielzeug/stateit";function i(i,a){let o=r({data:void 0,error:void 0,pending:!0}),s;if(!t(n(()=>{let e=i(),t=new AbortController;return o.value={data:s,error:void 0,pending:!0},a(e,t.signal).then(e=>{t.signal.aborted||(s=e,o.value={data:e,error:void 0,pending:!1})}).catch(e=>{t.signal.aborted||(o.value={data:s,error:e,pending:!1})}),()=>t.abort()})))throw Error(e.lifecycleOutsideSetup);return o}export{i as resource};
2
+ //# sourceMappingURL=resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.js","names":[],"sources":["../../src/directives/resource.ts"],"sourcesContent":["import { effect as signalEffect, type Signal, signal } from '@vielzeug/stateit';\n\nimport { CRAFTIT_ERRORS } from '../errors';\nimport { tryRegisterCleanup } from '../runtime';\n\nexport type ResourceState<T> = {\n /** Previous successful data, available while a reload is pending. */\n data: T | undefined;\n /** Rejection reason from the most recent failed fetch, or `undefined`. */\n error: unknown;\n /** `true` while the fetcher promise is in flight. */\n pending: boolean;\n};\n\n/**\n * Reactive async resource. Re-fetches whenever the `deps` getter returns a new value.\n * Previous data is preserved during reloads so the UI can show stale-while-revalidate UX.\n * In-flight requests are cancelled via `AbortController` when deps change.\n *\n * Must be called inside component `setup()` or another Craftit runtime scope.\n *\n * @example\n * ```ts\n * const user = resource(\n * () => props.userId.value,\n * (id, signal) => fetch(`/api/users/${id}`, { signal }).then(r => r.json()),\n * );\n *\n * return () => html`\n * ${when(() => user.value.pending, () => html`<p>Loading…</p>`)}\n * ${when(() => !!user.value.error, () => html`<p>Error</p>`)}\n * ${when(() => !!user.value.data, () => html`<p>${user.value.data?.name}</p>`)}\n * `;\n * ```\n */\nexport function resource<Deps, T>(deps: () => Deps, fetcher: (deps: Deps, signal: AbortSignal) => Promise<T>) {\n const state: Signal<ResourceState<T>> = signal({ data: undefined, error: undefined, pending: true });\n let previousData: T | undefined;\n\n // Use raw stateit effect and register cleanup explicitly.\n const dispose = signalEffect(() => {\n const currentDeps = deps();\n const controller = new AbortController();\n\n state.value = { data: previousData, error: undefined, pending: true };\n\n fetcher(currentDeps, controller.signal)\n .then((data) => {\n if (!controller.signal.aborted) {\n previousData = data;\n state.value = { data, error: undefined, pending: false };\n }\n })\n .catch((error: unknown) => {\n if (!controller.signal.aborted) {\n state.value = { data: previousData, error, pending: false };\n }\n });\n\n return () => controller.abort();\n });\n\n if (!tryRegisterCleanup(dispose)) throw new Error(CRAFTIT_ERRORS.lifecycleOutsideSetup);\n\n return state;\n}\n"],"mappings":"wJAmCA,SAAgB,EAAkB,EAAkB,EAA0D,CAC5G,IAAM,EAAkC,EAAO,CAAE,KAAM,IAAA,GAAW,MAAO,IAAA,GAAW,QAAS,EAAK,CAAC,EAC/F,EAyBJ,GAAI,CAAC,EAtBW,MAAmB,CACjC,IAAM,EAAc,EAAK,EACnB,EAAa,IAAI,gBAiBvB,MAfA,GAAM,MAAQ,CAAE,KAAM,EAAc,MAAO,IAAA,GAAW,QAAS,EAAK,EAEpE,EAAQ,EAAa,EAAW,MAAM,EACnC,KAAM,GAAS,CACT,EAAW,OAAO,UACrB,EAAe,EACf,EAAM,MAAQ,CAAE,OAAM,MAAO,IAAA,GAAW,QAAS,EAAM,EAE3D,CAAC,EACA,MAAO,GAAmB,CACpB,EAAW,OAAO,UACrB,EAAM,MAAQ,CAAE,KAAM,EAAc,QAAO,QAAS,EAAM,EAE9D,CAAC,MAEU,EAAW,MAAM,CAChC,CAEwB,CAAO,EAAG,MAAU,MAAM,EAAe,qBAAqB,EAEtF,OAAO,CACT"}
@@ -0,0 +1,2 @@
1
+ let e=require(`@vielzeug/stateit`);var t=t=>{let n=typeof t==`function`?t():(0,e.isSignal)(t)?t.value:t;return n==null||n===!1?``:String(n)},n=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),r=r=>(0,e.computed)(()=>{let e=[];for(let[i,a]of Object.entries(r)){let r=t(a);r&&e.push(`${n(i)}:${r}`)}return e.join(`;`)});exports.styleMap=r;
2
+ //# sourceMappingURL=styleMap.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styleMap.cjs","names":[],"sources":["../../src/directives/styleMap.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\ntype StyleInput =\n | string\n | number\n | null\n | undefined\n | false\n | (() => string | number | null | undefined | false)\n | ReadonlySignal<string | number | null | undefined | false>;\n\nconst toStyleValue = (value: StyleInput): string => {\n const resolved = typeof value === 'function' ? value() : isSignal(value) ? value.value : value;\n\n if (resolved == null || resolved === false) return '';\n\n return String(resolved);\n};\n\nconst toKebabCase = (name: string): string => name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);\n\n/**\n * Builds a reactive inline style string from a style object.\n *\n * @example\n * html`<div :style=${styleMap({ backgroundColor: color, width: () => `${size.value}px` })}></div>`\n */\nexport const styleMap = (record: Record<string, StyleInput>): ReadonlySignal<string> => {\n return computed(() => {\n const declarations: string[] = [];\n\n for (const [name, input] of Object.entries(record)) {\n const value = toStyleValue(input);\n\n if (!value) continue;\n\n declarations.push(`${toKebabCase(name)}:${value}`);\n }\n\n return declarations.join(';');\n });\n};\n"],"mappings":"mCAWA,IAAM,EAAgB,GAA8B,CAClD,IAAM,EAAW,OAAO,GAAU,WAAa,EAAM,GAAA,EAAA,EAAA,UAAa,CAAK,EAAI,EAAM,MAAQ,EAIzF,OAFI,GAAY,MAAQ,IAAa,GAAc,GAE5C,OAAO,CAAQ,CACxB,EAEM,EAAe,GAAyB,EAAK,QAAQ,SAAW,GAAS,IAAI,EAAK,YAAY,GAAG,EAQ1F,EAAY,IACvB,EAAA,EAAA,cAAsB,CACpB,IAAM,EAAyB,CAAC,EAEhC,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAM,EAAG,CAClD,IAAM,EAAQ,EAAa,CAAK,EAE3B,GAEL,EAAa,KAAK,GAAG,EAAY,CAAI,EAAE,GAAG,GAAO,CACnD,CAEA,OAAO,EAAa,KAAK,GAAG,CAC9B,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { type ReadonlySignal } from '@vielzeug/stateit';
2
+ type StyleInput = string | number | null | undefined | false | (() => string | number | null | undefined | false) | ReadonlySignal<string | number | null | undefined | false>;
3
+ /**
4
+ * Builds a reactive inline style string from a style object.
5
+ *
6
+ * @example
7
+ * html`<div :style=${styleMap({ backgroundColor: color, width: () => `${size.value}px` })}></div>`
8
+ */
9
+ export declare const styleMap: (record: Record<string, StyleInput>) => ReadonlySignal<string>;
10
+ export {};
11
+ //# sourceMappingURL=styleMap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styleMap.d.ts","sourceRoot":"","sources":["../../src/directives/styleMap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAE5E,KAAK,UAAU,GACX,MAAM,GACN,MAAM,GACN,IAAI,GACJ,SAAS,GACT,KAAK,GACL,CAAC,MAAM,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,CAAC,GAClD,cAAc,CAAC,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,CAAC,CAAC;AAY/D;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,GAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,KAAG,cAAc,CAAC,MAAM,CAclF,CAAC"}
@@ -0,0 +1,2 @@
1
+ import{computed as e,isSignal as t}from"@vielzeug/stateit";var n=e=>{let n=typeof e==`function`?e():t(e)?e.value:e;return n==null||n===!1?``:String(n)},r=e=>e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`),i=t=>e(()=>{let e=[];for(let[i,a]of Object.entries(t)){let t=n(a);t&&e.push(`${r(i)}:${t}`)}return e.join(`;`)});export{i as styleMap};
2
+ //# sourceMappingURL=styleMap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styleMap.js","names":[],"sources":["../../src/directives/styleMap.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\ntype StyleInput =\n | string\n | number\n | null\n | undefined\n | false\n | (() => string | number | null | undefined | false)\n | ReadonlySignal<string | number | null | undefined | false>;\n\nconst toStyleValue = (value: StyleInput): string => {\n const resolved = typeof value === 'function' ? value() : isSignal(value) ? value.value : value;\n\n if (resolved == null || resolved === false) return '';\n\n return String(resolved);\n};\n\nconst toKebabCase = (name: string): string => name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);\n\n/**\n * Builds a reactive inline style string from a style object.\n *\n * @example\n * html`<div :style=${styleMap({ backgroundColor: color, width: () => `${size.value}px` })}></div>`\n */\nexport const styleMap = (record: Record<string, StyleInput>): ReadonlySignal<string> => {\n return computed(() => {\n const declarations: string[] = [];\n\n for (const [name, input] of Object.entries(record)) {\n const value = toStyleValue(input);\n\n if (!value) continue;\n\n declarations.push(`${toKebabCase(name)}:${value}`);\n }\n\n return declarations.join(';');\n });\n};\n"],"mappings":"2DAWA,IAAM,EAAgB,GAA8B,CAClD,IAAM,EAAW,OAAO,GAAU,WAAa,EAAM,EAAI,EAAS,CAAK,EAAI,EAAM,MAAQ,EAIzF,OAFI,GAAY,MAAQ,IAAa,GAAc,GAE5C,OAAO,CAAQ,CACxB,EAEM,EAAe,GAAyB,EAAK,QAAQ,SAAW,GAAS,IAAI,EAAK,YAAY,GAAG,EAQ1F,EAAY,GAChB,MAAe,CACpB,IAAM,EAAyB,CAAC,EAEhC,IAAK,GAAM,CAAC,EAAM,KAAU,OAAO,QAAQ,CAAM,EAAG,CAClD,IAAM,EAAQ,EAAa,CAAK,EAE3B,GAEL,EAAa,KAAK,GAAG,EAAY,CAAI,EAAE,GAAG,GAAO,CACnD,CAEA,OAAO,EAAa,KAAK,GAAG,CAC9B,CAAC"}
@@ -1,2 +1,2 @@
1
- let e=require(`@vielzeug/stateit`);var t=e=>{if(e.then!==void 0&&e.then!==null&&typeof e.then!=`function`)throw Error(`[craftit:when] options.then must be a function when provided.`);if(e.else!==void 0&&e.else!==null&&typeof e.else!=`function`)throw Error(`[craftit:when] options.else must be a function when provided.`)};function n(n){t(n);let{condition:i}=n,{else:a,then:o}=n,s=()=>r(i)?o?.()??``:a?.()??``;return(0,e.isSignal)(i)||typeof i==`function`?{render:s}:s()}var r=t=>(0,e.isSignal)(t)?t.value:typeof t==`function`?t():t;exports.when=n;
1
+ let e=require(`@vielzeug/stateit`);var t=t=>typeof t==`function`?t():(0,e.isSignal)(t)?t.value:t;function n(n,r,i){return(0,e.computed)(()=>t(n)?r():i?i():``)}exports.when=n;
2
2
  //# sourceMappingURL=when.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"when.cjs","names":[],"sources":["../../src/directives/when.ts"],"sourcesContent":["import { isSignal, type ReadonlySignal, type Signal } from '@vielzeug/stateit';\n\nimport type { Directive, HTMLResult } from '../internal';\n\ntype TemplateFn<V extends string | HTMLResult = string | HTMLResult> = () => V;\n\nexport type WhenOptions<V extends string | HTMLResult = string | HTMLResult> = {\n condition: Signal<unknown> | ReadonlySignal<unknown> | (() => unknown) | unknown;\n else?: TemplateFn<V> | null;\n then?: TemplateFn<V> | null;\n};\n\nconst validateWhenOptions = <V extends string | HTMLResult>(options: WhenOptions<V>): void => {\n if (options.then !== undefined && options.then !== null && typeof options.then !== 'function') {\n throw new Error('[craftit:when] options.then must be a function when provided.');\n }\n\n if (options.else !== undefined && options.else !== null && typeof options.else !== 'function') {\n throw new Error('[craftit:when] options.else must be a function when provided.');\n }\n};\n\n/**\n * Conditionally renders one of two templates based on a condition.\n *\n * - **Signal or getter** — returns a reactive function the engine re-runs automatically.\n * - **Static value** — evaluated once at call time, returns the result directly.\n *\n * @example\n * import { when } from '@vielzeug/craftit/directives';\n *\n * html`${when({ condition: isLoggedIn, then: () => html`<user-panel>`, else: () => html`<login-form>` })}`\n * html`${when({ condition: () => count.value > 0, then: () => html`<span>${count}</span>` })}`\n */\nexport function when<V extends string | HTMLResult>(options: WhenOptions<V>): Directive | V | string {\n validateWhenOptions(options);\n\n const { condition } = options;\n const { else: resolvedElse, then: resolvedThen } = options;\n const renderResolved = (): V | string =>\n conditionValue(condition) ? (resolvedThen?.() ?? '') : (resolvedElse?.() ?? '');\n\n if (isSignal(condition) || typeof condition === 'function') {\n return {\n render: renderResolved,\n };\n }\n\n return renderResolved();\n}\n\nconst conditionValue = (condition: WhenOptions['condition']): unknown => {\n if (isSignal(condition)) return (condition as ReadonlySignal<unknown>).value;\n\n if (typeof condition === 'function') return (condition as () => unknown)();\n\n return condition;\n};\n"],"mappings":"mCAYA,IAAM,EAAsD,GAAkC,CAC5F,GAAI,EAAQ,OAAS,IAAA,IAAa,EAAQ,OAAS,MAAQ,OAAO,EAAQ,MAAS,WACjF,MAAU,MAAM,gEAAgE,CAGlF,GAAI,EAAQ,OAAS,IAAA,IAAa,EAAQ,OAAS,MAAQ,OAAO,EAAQ,MAAS,WACjF,MAAU,MAAM,gEAAgE,EAgBpF,SAAgB,EAAoC,EAAiD,CACnG,EAAoB,EAAQ,CAE5B,GAAM,CAAE,aAAc,EAChB,CAAE,KAAM,EAAc,KAAM,GAAiB,EAC7C,MACJ,EAAe,EAAU,CAAI,KAAgB,EAAI,GAAO,KAAgB,EAAI,GAQ9E,OANA,EAAA,EAAA,UAAa,EAAU,EAAI,OAAO,GAAc,WACvC,CACL,OAAQ,EACT,CAGI,GAAgB,CAGzB,IAAM,EAAkB,IACtB,EAAA,EAAA,UAAa,EAAU,CAAU,EAAsC,MAEnE,OAAO,GAAc,WAAoB,GAA6B,CAEnE"}
1
+ {"version":3,"file":"when.cjs","names":[],"sources":["../../src/directives/when.ts"],"sourcesContent":["import { computed, isSignal, type ReadonlySignal } from '@vielzeug/stateit';\n\nimport type { HTMLResult } from '../internal';\n\ntype MaybeReactive<T> = T | (() => T) | ReadonlySignal<T>;\n\ntype WhenRenderable = string | HTMLResult;\n\nconst resolve = <T>(value: MaybeReactive<T>): T => {\n if (typeof value === 'function') return (value as () => T)();\n\n if (isSignal(value)) return value.value;\n\n return value;\n};\n\n/**\n * Conditionally renders one of two branches.\n */\nexport function when(\n condition: MaybeReactive<boolean>,\n truthy: () => WhenRenderable,\n falsy?: () => WhenRenderable,\n): ReadonlySignal<WhenRenderable> {\n return computed(() => {\n if (resolve(condition)) return truthy();\n\n return falsy ? falsy() : '';\n });\n} // Note: when returns HtmlResult (not DirectiveResult)\n"],"mappings":"mCAQA,IAAM,EAAc,GACd,OAAO,GAAU,WAAoB,EAAkB,GAE3D,EAAA,EAAA,UAAa,CAAK,EAAU,EAAM,MAE3B,EAMT,SAAgB,EACd,EACA,EACA,EACgC,CAChC,OAAA,EAAA,EAAA,cACM,EAAQ,CAAS,EAAU,EAAO,EAE/B,EAAQ,EAAM,EAAI,EAC1B,CACH"}
@@ -1,23 +1,10 @@
1
- import { type ReadonlySignal, type Signal } from '@vielzeug/stateit';
2
- import type { Directive, HTMLResult } from '../internal';
3
- type TemplateFn<V extends string | HTMLResult = string | HTMLResult> = () => V;
4
- export type WhenOptions<V extends string | HTMLResult = string | HTMLResult> = {
5
- condition: Signal<unknown> | ReadonlySignal<unknown> | (() => unknown) | unknown;
6
- else?: TemplateFn<V> | null;
7
- then?: TemplateFn<V> | null;
8
- };
1
+ import { type ReadonlySignal } from '@vielzeug/stateit';
2
+ import type { HTMLResult } from '../internal';
3
+ type MaybeReactive<T> = T | (() => T) | ReadonlySignal<T>;
4
+ type WhenRenderable = string | HTMLResult;
9
5
  /**
10
- * Conditionally renders one of two templates based on a condition.
11
- *
12
- * - **Signal or getter** — returns a reactive function the engine re-runs automatically.
13
- * - **Static value** — evaluated once at call time, returns the result directly.
14
- *
15
- * @example
16
- * import { when } from '@vielzeug/craftit/directives';
17
- *
18
- * html`${when({ condition: isLoggedIn, then: () => html`<user-panel>`, else: () => html`<login-form>` })}`
19
- * html`${when({ condition: () => count.value > 0, then: () => html`<span>${count}</span>` })}`
6
+ * Conditionally renders one of two branches.
20
7
  */
21
- export declare function when<V extends string | HTMLResult>(options: WhenOptions<V>): Directive | V | string;
8
+ export declare function when(condition: MaybeReactive<boolean>, truthy: () => WhenRenderable, falsy?: () => WhenRenderable): ReadonlySignal<WhenRenderable>;
22
9
  export {};
23
10
  //# sourceMappingURL=when.d.ts.map