@zeix/le-truc 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (406) hide show
  1. package/.ai-context.md +234 -0
  2. package/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +111 -0
  3. package/.editorconfig +12 -0
  4. package/.github/copilot-instructions.md +62 -0
  5. package/.github/workflows/codeql.yml +108 -0
  6. package/.github/workflows/static.yml +43 -0
  7. package/.prettierrc +17 -0
  8. package/CLAUDE.md +215 -0
  9. package/CODE_OF_CONDUCT.md +128 -0
  10. package/CONTRIBUTING.md +160 -0
  11. package/LICENSE +21 -0
  12. package/README.md +474 -0
  13. package/biome.json +295 -0
  14. package/bun.lock +239 -0
  15. package/docs/about.html +105 -0
  16. package/docs/assets/main.css +1 -0
  17. package/docs/assets/main.js +10 -0
  18. package/docs/assets/main.js.map +66 -0
  19. package/docs/components.html +293 -0
  20. package/docs/data-flow.html +308 -0
  21. package/docs/examples/basic-button.html +367 -0
  22. package/docs/examples/basic-counter.html +188 -0
  23. package/docs/examples/basic-hello.html +138 -0
  24. package/docs/examples/basic-number.html +271 -0
  25. package/docs/examples/basic-pluralize.html +214 -0
  26. package/docs/examples/card-callout.html +152 -0
  27. package/docs/examples/card-mediaqueries.html +138 -0
  28. package/docs/examples/context-media.html +198 -0
  29. package/docs/examples/empty.html +37 -0
  30. package/docs/examples/form-checkbox.html +233 -0
  31. package/docs/examples/form-combobox.html +420 -0
  32. package/docs/examples/form-listbox.html +434 -0
  33. package/docs/examples/form-radiogroup.html +296 -0
  34. package/docs/examples/form-spinbutton.html +402 -0
  35. package/docs/examples/form-textbox.html +361 -0
  36. package/docs/examples/layout.html +67 -0
  37. package/docs/examples/module-carousel.html +552 -0
  38. package/docs/examples/module-catalog.html +241 -0
  39. package/docs/examples/module-codeblock.html +270 -0
  40. package/docs/examples/module-dialog.html +343 -0
  41. package/docs/examples/module-lazyload.html +289 -0
  42. package/docs/examples/module-list.html +197 -0
  43. package/docs/examples/module-pagination.html +283 -0
  44. package/docs/examples/module-scrollarea.html +447 -0
  45. package/docs/examples/module-tabgroup.html +526 -0
  46. package/docs/examples/module-todo.html +367 -0
  47. package/docs/examples/module-with-type.html +63 -0
  48. package/docs/examples/nested-components.html +88 -0
  49. package/docs/examples/recursive.html +56 -0
  50. package/docs/examples/simple-text.html +39 -0
  51. package/docs/examples/snippet.html +93 -0
  52. package/docs/examples/with-styles.html +75 -0
  53. package/docs/getting-started.html +143 -0
  54. package/docs/index.html +112 -0
  55. package/docs/sitemap.xml +28 -0
  56. package/docs/styling.html +160 -0
  57. package/docs/sw.js +112 -0
  58. package/docs-src/api/README.md +478 -0
  59. package/docs-src/api/_media/LICENSE +21 -0
  60. package/docs-src/api/classes/CircularDependencyError.md +299 -0
  61. package/docs-src/api/classes/CircularMutationError.md +301 -0
  62. package/docs-src/api/classes/ContextRequestEvent.md +590 -0
  63. package/docs-src/api/classes/DependencyTimeoutError.md +301 -0
  64. package/docs-src/api/classes/InvalidCallbackError.md +303 -0
  65. package/docs-src/api/classes/InvalidComponentNameError.md +295 -0
  66. package/docs-src/api/classes/InvalidCustomElementError.md +301 -0
  67. package/docs-src/api/classes/InvalidEffectsError.md +301 -0
  68. package/docs-src/api/classes/InvalidPropertyNameError.md +307 -0
  69. package/docs-src/api/classes/InvalidReactivesError.md +307 -0
  70. package/docs-src/api/classes/InvalidSignalValueError.md +303 -0
  71. package/docs-src/api/classes/MissingElementError.md +307 -0
  72. package/docs-src/api/classes/NullishSignalValueError.md +299 -0
  73. package/docs-src/api/classes/StoreKeyExistsError.md +303 -0
  74. package/docs-src/api/classes/StoreKeyRangeError.md +299 -0
  75. package/docs-src/api/classes/StoreKeyReadonlyError.md +303 -0
  76. package/docs-src/api/functions/asBoolean.md +21 -0
  77. package/docs-src/api/functions/asEnum.md +31 -0
  78. package/docs-src/api/functions/asInteger.md +39 -0
  79. package/docs-src/api/functions/asJSON.md +49 -0
  80. package/docs-src/api/functions/asNumber.md +37 -0
  81. package/docs-src/api/functions/asString.md +37 -0
  82. package/docs-src/api/functions/createCollection.md +83 -0
  83. package/docs-src/api/functions/createSensor.md +71 -0
  84. package/docs-src/api/functions/dangerouslySetInnerHTML.md +48 -0
  85. package/docs-src/api/functions/defineComponent.md +65 -0
  86. package/docs-src/api/functions/isCollection.md +37 -0
  87. package/docs-src/api/functions/isParser.md +41 -0
  88. package/docs-src/api/functions/match.md +47 -0
  89. package/docs-src/api/functions/on.md +58 -0
  90. package/docs-src/api/functions/pass.md +53 -0
  91. package/docs-src/api/functions/provideContexts.md +47 -0
  92. package/docs-src/api/functions/read.md +47 -0
  93. package/docs-src/api/functions/requestContext.md +51 -0
  94. package/docs-src/api/functions/resolve.md +40 -0
  95. package/docs-src/api/functions/runEffects.md +51 -0
  96. package/docs-src/api/functions/runElementEffects.md +57 -0
  97. package/docs-src/api/functions/schedule.md +33 -0
  98. package/docs-src/api/functions/setAttribute.md +48 -0
  99. package/docs-src/api/functions/setProperty.md +52 -0
  100. package/docs-src/api/functions/setStyle.md +48 -0
  101. package/docs-src/api/functions/setText.md +42 -0
  102. package/docs-src/api/functions/show.md +42 -0
  103. package/docs-src/api/functions/toSignal.md +37 -0
  104. package/docs-src/api/functions/toggleAttribute.md +48 -0
  105. package/docs-src/api/functions/toggleClass.md +48 -0
  106. package/docs-src/api/functions/updateElement.md +53 -0
  107. package/docs-src/api/globals.md +131 -0
  108. package/docs-src/api/type-aliases/Cleanup.md +27 -0
  109. package/docs-src/api/type-aliases/Collection.md +91 -0
  110. package/docs-src/api/type-aliases/CollectionListener.md +27 -0
  111. package/docs-src/api/type-aliases/Component.md +17 -0
  112. package/docs-src/api/type-aliases/ComponentProp.md +11 -0
  113. package/docs-src/api/type-aliases/ComponentProps.md +11 -0
  114. package/docs-src/api/type-aliases/ComponentSetup.md +31 -0
  115. package/docs-src/api/type-aliases/ComponentUI.md +27 -0
  116. package/docs-src/api/type-aliases/Computed.md +49 -0
  117. package/docs-src/api/type-aliases/ComputedCallback.md +29 -0
  118. package/docs-src/api/type-aliases/Context.md +33 -0
  119. package/docs-src/api/type-aliases/ContextType.md +19 -0
  120. package/docs-src/api/type-aliases/DangerouslySetInnerHTMLOptions.md +27 -0
  121. package/docs-src/api/type-aliases/DiffResult.md +61 -0
  122. package/docs-src/api/type-aliases/Effect.md +35 -0
  123. package/docs-src/api/type-aliases/EffectCallback.md +23 -0
  124. package/docs-src/api/type-aliases/Effects.md +21 -0
  125. package/docs-src/api/type-aliases/ElementEffects.md +21 -0
  126. package/docs-src/api/type-aliases/ElementFromKey.md +21 -0
  127. package/docs-src/api/type-aliases/ElementQueries.md +27 -0
  128. package/docs-src/api/type-aliases/ElementUpdater.md +131 -0
  129. package/docs-src/api/type-aliases/EventHandler.md +31 -0
  130. package/docs-src/api/type-aliases/EventType.md +17 -0
  131. package/docs-src/api/type-aliases/Fallback.md +21 -0
  132. package/docs-src/api/type-aliases/Initializers.md +21 -0
  133. package/docs-src/api/type-aliases/LooseReader.md +31 -0
  134. package/docs-src/api/type-aliases/MatchHandlers.md +77 -0
  135. package/docs-src/api/type-aliases/MaybeCleanup.md +23 -0
  136. package/docs-src/api/type-aliases/MaybeSignal.md +17 -0
  137. package/docs-src/api/type-aliases/Parser.md +39 -0
  138. package/docs-src/api/type-aliases/ParserOrFallback.md +21 -0
  139. package/docs-src/api/type-aliases/PassedProp.md +25 -0
  140. package/docs-src/api/type-aliases/PassedProps.md +21 -0
  141. package/docs-src/api/type-aliases/Reactive.md +25 -0
  142. package/docs-src/api/type-aliases/Reader.md +31 -0
  143. package/docs-src/api/type-aliases/ReservedWords.md +11 -0
  144. package/docs-src/api/type-aliases/ResolveResult.md +29 -0
  145. package/docs-src/api/type-aliases/SensorEvents.md +25 -0
  146. package/docs-src/api/type-aliases/Signal.md +41 -0
  147. package/docs-src/api/type-aliases/State.md +85 -0
  148. package/docs-src/api/type-aliases/Store.md +29 -0
  149. package/docs-src/api/type-aliases/UI.md +11 -0
  150. package/docs-src/api/type-aliases/UnknownContext.md +13 -0
  151. package/docs-src/api/variables/CONTEXT_REQUEST.md +11 -0
  152. package/docs-src/api/variables/UNSET.md +23 -0
  153. package/docs-src/api/variables/batch.md +25 -0
  154. package/docs-src/api/variables/createComputed.md +41 -0
  155. package/docs-src/api/variables/createEffect.md +35 -0
  156. package/docs-src/api/variables/createState.md +37 -0
  157. package/docs-src/api/variables/createStore.md +42 -0
  158. package/docs-src/api/variables/diff.md +43 -0
  159. package/docs-src/api/variables/isAbortError.md +33 -0
  160. package/docs-src/api/variables/isAsyncFunction.md +39 -0
  161. package/docs-src/api/variables/isComputed.md +37 -0
  162. package/docs-src/api/variables/isEqual.md +49 -0
  163. package/docs-src/api/variables/isFunction.md +39 -0
  164. package/docs-src/api/variables/isMutableSignal.md +37 -0
  165. package/docs-src/api/variables/isNumber.md +33 -0
  166. package/docs-src/api/variables/isRecord.md +39 -0
  167. package/docs-src/api/variables/isRecordOrArray.md +39 -0
  168. package/docs-src/api/variables/isSignal.md +37 -0
  169. package/docs-src/api/variables/isState.md +37 -0
  170. package/docs-src/api/variables/isStore.md +37 -0
  171. package/docs-src/api/variables/isString.md +33 -0
  172. package/docs-src/api/variables/isSymbol.md +33 -0
  173. package/docs-src/api/variables/toError.md +33 -0
  174. package/docs-src/api/variables/valueString.md +33 -0
  175. package/docs-src/includes/menu.html +44 -0
  176. package/docs-src/pages/about.md +89 -0
  177. package/docs-src/pages/components.md +437 -0
  178. package/docs-src/pages/data-flow.md +449 -0
  179. package/docs-src/pages/getting-started.md +170 -0
  180. package/docs-src/pages/index.md +98 -0
  181. package/docs-src/pages/styling.md +165 -0
  182. package/eslint.config.js +64 -0
  183. package/examples/_common/clear.ts +49 -0
  184. package/examples/_common/fetch.ts +160 -0
  185. package/examples/_common/focus.ts +45 -0
  186. package/examples/_common/highlight.ts +5 -0
  187. package/examples/_global.css +463 -0
  188. package/examples/basic-button/basic-button.css +176 -0
  189. package/examples/basic-button/basic-button.html +46 -0
  190. package/examples/basic-button/basic-button.spec.ts +160 -0
  191. package/examples/basic-button/basic-button.ts +45 -0
  192. package/examples/basic-button/copyToClipboard.ts +37 -0
  193. package/examples/basic-counter/basic-counter.css +21 -0
  194. package/examples/basic-counter/basic-counter.html +24 -0
  195. package/examples/basic-counter/basic-counter.spec.ts +85 -0
  196. package/examples/basic-counter/basic-counter.ts +43 -0
  197. package/examples/basic-hello/basic-hello.html +34 -0
  198. package/examples/basic-hello/basic-hello.spec.ts +110 -0
  199. package/examples/basic-hello/basic-hello.ts +36 -0
  200. package/examples/basic-number/basic-number.html +79 -0
  201. package/examples/basic-number/basic-number.spec.ts +175 -0
  202. package/examples/basic-number/basic-number.ts +124 -0
  203. package/examples/basic-pluralize/basic-pluralize.html +64 -0
  204. package/examples/basic-pluralize/basic-pluralize.spec.ts +258 -0
  205. package/examples/basic-pluralize/basic-pluralize.ts +82 -0
  206. package/examples/card-callout/card-callout.css +79 -0
  207. package/examples/card-callout/card-callout.html +5 -0
  208. package/examples/card-mediaqueries/card-mediaqueries.html +29 -0
  209. package/examples/card-mediaqueries/card-mediaqueries.spec.ts +300 -0
  210. package/examples/card-mediaqueries/card-mediaqueries.ts +41 -0
  211. package/examples/context-media/context-media.html +3 -0
  212. package/examples/context-media/context-media.ts +127 -0
  213. package/examples/form-checkbox/form-checkbox.css +70 -0
  214. package/examples/form-checkbox/form-checkbox.html +13 -0
  215. package/examples/form-checkbox/form-checkbox.spec.ts +357 -0
  216. package/examples/form-checkbox/form-checkbox.ts +50 -0
  217. package/examples/form-checkbox/vanilla-checkbox.ts +101 -0
  218. package/examples/form-combobox/form-combobox.css +118 -0
  219. package/examples/form-combobox/form-combobox.html +74 -0
  220. package/examples/form-combobox/form-combobox.spec.ts +977 -0
  221. package/examples/form-combobox/form-combobox.ts +128 -0
  222. package/examples/form-listbox/form-listbox.css +71 -0
  223. package/examples/form-listbox/form-listbox.html +67 -0
  224. package/examples/form-listbox/form-listbox.spec.ts +1050 -0
  225. package/examples/form-listbox/form-listbox.ts +196 -0
  226. package/examples/form-listbox/mocks/timezones.json +495 -0
  227. package/examples/form-radiogroup/form-radiogroup.css +87 -0
  228. package/examples/form-radiogroup/form-radiogroup.html +51 -0
  229. package/examples/form-radiogroup/form-radiogroup.spec.ts +515 -0
  230. package/examples/form-radiogroup/form-radiogroup.ts +58 -0
  231. package/examples/form-spinbutton/form-spinbutton.css +95 -0
  232. package/examples/form-spinbutton/form-spinbutton.html +96 -0
  233. package/examples/form-spinbutton/form-spinbutton.spec.ts +688 -0
  234. package/examples/form-spinbutton/form-spinbutton.ts +111 -0
  235. package/examples/form-textbox/form-textbox.css +104 -0
  236. package/examples/form-textbox/form-textbox.html +53 -0
  237. package/examples/form-textbox/form-textbox.spec.ts +542 -0
  238. package/examples/form-textbox/form-textbox.ts +104 -0
  239. package/examples/main.css +22 -0
  240. package/examples/main.ts +23 -0
  241. package/examples/module-carousel/module-carousel.css +113 -0
  242. package/examples/module-carousel/module-carousel.html +208 -0
  243. package/examples/module-carousel/module-carousel.spec.ts +523 -0
  244. package/examples/module-carousel/module-carousel.ts +131 -0
  245. package/examples/module-catalog/module-catalog.css +22 -0
  246. package/examples/module-catalog/module-catalog.html +82 -0
  247. package/examples/module-catalog/module-catalog.spec.ts +396 -0
  248. package/examples/module-catalog/module-catalog.ts +37 -0
  249. package/examples/module-codeblock/module-codeblock.css +95 -0
  250. package/examples/module-codeblock/module-codeblock.html +28 -0
  251. package/examples/module-codeblock/module-codeblock.ts +47 -0
  252. package/examples/module-demo/module-demo.css +13 -0
  253. package/examples/module-dialog/module-dialog.css +96 -0
  254. package/examples/module-dialog/module-dialog.html +66 -0
  255. package/examples/module-dialog/module-dialog.spec.ts +557 -0
  256. package/examples/module-dialog/module-dialog.ts +81 -0
  257. package/examples/module-lazyload/mocks/empty.html +1 -0
  258. package/examples/module-lazyload/mocks/module-with-type.html +27 -0
  259. package/examples/module-lazyload/mocks/nested-components.html +52 -0
  260. package/examples/module-lazyload/mocks/recursive.html +20 -0
  261. package/examples/module-lazyload/mocks/simple-text.html +3 -0
  262. package/examples/module-lazyload/mocks/snippet.html +57 -0
  263. package/examples/module-lazyload/mocks/with-styles.html +39 -0
  264. package/examples/module-lazyload/module-lazyload.html +132 -0
  265. package/examples/module-lazyload/module-lazyload.spec.ts +734 -0
  266. package/examples/module-lazyload/module-lazyload.ts +89 -0
  267. package/examples/module-list/module-list.html +30 -0
  268. package/examples/module-list/module-list.spec.ts +592 -0
  269. package/examples/module-list/module-list.ts +99 -0
  270. package/examples/module-pagination/module-pagination.css +79 -0
  271. package/examples/module-pagination/module-pagination.html +16 -0
  272. package/examples/module-pagination/module-pagination.spec.ts +701 -0
  273. package/examples/module-pagination/module-pagination.ts +88 -0
  274. package/examples/module-scrollarea/module-scrollarea.css +77 -0
  275. package/examples/module-scrollarea/module-scrollarea.html +189 -0
  276. package/examples/module-scrollarea/module-scrollarea.spec.ts +445 -0
  277. package/examples/module-scrollarea/module-scrollarea.ts +81 -0
  278. package/examples/module-tabgroup/module-tabgroup.css +55 -0
  279. package/examples/module-tabgroup/module-tabgroup.html +269 -0
  280. package/examples/module-tabgroup/module-tabgroup.spec.ts +631 -0
  281. package/examples/module-tabgroup/module-tabgroup.ts +102 -0
  282. package/examples/module-toc/module-toc.css +34 -0
  283. package/examples/module-todo/module-todo.css +84 -0
  284. package/examples/module-todo/module-todo.html +92 -0
  285. package/examples/module-todo/module-todo.spec.ts +528 -0
  286. package/examples/module-todo/module-todo.ts +91 -0
  287. package/examples/section-hero/section-hero.css +37 -0
  288. package/examples/section-menu/section-menu.css +81 -0
  289. package/examples/server.ts +95 -0
  290. package/examples/test-setup.md +314 -0
  291. package/index.dev.js +1688 -0
  292. package/index.dev.ts +127 -0
  293. package/index.js +3 -0
  294. package/index.js.map +42 -0
  295. package/index.ts +127 -0
  296. package/package.json +64 -0
  297. package/playwright.config.ts +31 -0
  298. package/server/BUILD_SYSTEM.md +428 -0
  299. package/server/SERVER.md +286 -0
  300. package/server/build.ts +91 -0
  301. package/server/config.ts +130 -0
  302. package/server/effects/api.ts +28 -0
  303. package/server/effects/css.ts +31 -0
  304. package/server/effects/examples.ts +109 -0
  305. package/server/effects/js.ts +32 -0
  306. package/server/effects/menu.ts +34 -0
  307. package/server/effects/pages.ts +178 -0
  308. package/server/effects/service-worker.ts +57 -0
  309. package/server/effects/sitemap.ts +27 -0
  310. package/server/file-signals.ts +361 -0
  311. package/server/file-watcher.ts +77 -0
  312. package/server/io.ts +174 -0
  313. package/server/layout-engine.ts +470 -0
  314. package/server/layout-utils.ts +615 -0
  315. package/server/layouts/api.html +76 -0
  316. package/server/layouts/base.html +37 -0
  317. package/server/layouts/blog.html +115 -0
  318. package/server/layouts/example.html +104 -0
  319. package/server/layouts/overview.html +165 -0
  320. package/server/layouts/page.html +36 -0
  321. package/server/layouts/test.html +24 -0
  322. package/server/markdoc-helpers.ts +217 -0
  323. package/server/markdoc.config.ts +29 -0
  324. package/server/schema/callout.markdoc.ts +17 -0
  325. package/server/schema/carousel.markdoc.ts +118 -0
  326. package/server/schema/demo.markdoc.ts +74 -0
  327. package/server/schema/fence.markdoc.ts +84 -0
  328. package/server/schema/heading.markdoc.ts +23 -0
  329. package/server/schema/hero.markdoc.ts +59 -0
  330. package/server/schema/section.markdoc.ts +10 -0
  331. package/server/schema/slide.markdoc.ts +17 -0
  332. package/server/schema/source.markdoc.ts +53 -0
  333. package/server/schema/tabgroup.markdoc.ts +102 -0
  334. package/server/serve.ts +635 -0
  335. package/server/templates/README.md +352 -0
  336. package/server/templates/constants.ts +236 -0
  337. package/server/templates/fragments.ts +159 -0
  338. package/server/templates/hmr.ts +269 -0
  339. package/server/templates/menu.ts +33 -0
  340. package/server/templates/performance-hints.ts +94 -0
  341. package/server/templates/service-worker.ts +403 -0
  342. package/server/templates/sitemap.ts +57 -0
  343. package/server/templates/toc.ts +41 -0
  344. package/server/templates/utils.ts +378 -0
  345. package/src/component.ts +215 -0
  346. package/src/context.ts +156 -0
  347. package/src/effects/attribute.ts +82 -0
  348. package/src/effects/class.ts +28 -0
  349. package/src/effects/event.ts +67 -0
  350. package/src/effects/html.ts +60 -0
  351. package/src/effects/method.ts +57 -0
  352. package/src/effects/pass.ts +103 -0
  353. package/src/effects/property.ts +57 -0
  354. package/src/effects/style.ts +34 -0
  355. package/src/effects/text.ts +28 -0
  356. package/src/effects.ts +412 -0
  357. package/src/errors.ts +160 -0
  358. package/src/parsers/boolean.ts +14 -0
  359. package/src/parsers/json.ts +33 -0
  360. package/src/parsers/number.ts +55 -0
  361. package/src/parsers/string.ts +32 -0
  362. package/src/parsers.ts +90 -0
  363. package/src/scheduler.ts +47 -0
  364. package/src/signals/collection.ts +253 -0
  365. package/src/signals/sensor.ts +131 -0
  366. package/src/ui.ts +236 -0
  367. package/src/util.ts +187 -0
  368. package/tsconfig.json +34 -0
  369. package/types/examples/basic-button/basic-button.d.ts +16 -0
  370. package/types/examples/basic-hello/basic-hello.d.ts +18 -0
  371. package/types/index.d.ts +27 -0
  372. package/types/index.dev.d.ts +27 -0
  373. package/types/src/collection.d.ts +27 -0
  374. package/types/src/component.d.ts +32 -0
  375. package/types/src/context.d.ts +85 -0
  376. package/types/src/effects/attribute.d.ts +23 -0
  377. package/types/src/effects/callMethod.d.ts +23 -0
  378. package/types/src/effects/class.d.ts +13 -0
  379. package/types/src/effects/dangerouslySetInnerHTML.d.ts +18 -0
  380. package/types/src/effects/event.d.ts +18 -0
  381. package/types/src/effects/html.d.ts +17 -0
  382. package/types/src/effects/method.d.ts +22 -0
  383. package/types/src/effects/pass.d.ts +18 -0
  384. package/types/src/effects/property.d.ts +22 -0
  385. package/types/src/effects/setAttribute.d.ts +24 -0
  386. package/types/src/effects/setProperty.d.ts +23 -0
  387. package/types/src/effects/setStyle.d.ts +14 -0
  388. package/types/src/effects/setText.d.ts +13 -0
  389. package/types/src/effects/style.d.ts +13 -0
  390. package/types/src/effects/text.d.ts +12 -0
  391. package/types/src/effects/toggleClass.d.ts +14 -0
  392. package/types/src/effects.d.ts +153 -0
  393. package/types/src/errors.d.ts +99 -0
  394. package/types/src/events.d.ts +27 -0
  395. package/types/src/extractors.d.ts +23 -0
  396. package/types/src/parsers/boolean.d.ts +10 -0
  397. package/types/src/parsers/json.d.ts +13 -0
  398. package/types/src/parsers/number.d.ts +21 -0
  399. package/types/src/parsers/string.d.ts +19 -0
  400. package/types/src/parsers.d.ts +41 -0
  401. package/types/src/scheduler.d.ts +11 -0
  402. package/types/src/sensor.d.ts +27 -0
  403. package/types/src/signals/collection.d.ts +32 -0
  404. package/types/src/signals/sensor.d.ts +27 -0
  405. package/types/src/ui.d.ts +37 -0
  406. package/types/src/util.d.ts +65 -0
package/src/context.ts ADDED
@@ -0,0 +1,156 @@
1
+ import {
2
+ type Cleanup,
3
+ type Computed,
4
+ createComputed,
5
+ isFunction,
6
+ isString,
7
+ } from '@zeix/cause-effect'
8
+
9
+ import type { Component, ComponentProps } from './component'
10
+ import { type Fallback, getFallback, type Reader } from './parsers'
11
+ import type { UI } from './ui'
12
+
13
+ /** @see https://github.com/webcomponents-cg/community-protocols/blob/main/proposals/context.md */
14
+
15
+ /* === Types === */
16
+
17
+ /**
18
+ * A context key.
19
+ *
20
+ * A context key can be any type of object, including strings and symbols. The
21
+ * Context type brands the key type with the `__context__` property that
22
+ * carries the type of the value the context references.
23
+ */
24
+ type Context<K, V> = K & { __context__: V }
25
+
26
+ /**
27
+ * An unknown context type
28
+ */
29
+ type UnknownContext = Context<unknown, unknown>
30
+
31
+ /**
32
+ * A helper type which can extract a Context value type from a Context type
33
+ */
34
+ type ContextType<T extends UnknownContext> =
35
+ T extends Context<infer _, infer V> ? V : never
36
+
37
+ /**
38
+ * A callback which is provided by a context requester and is called with the value satisfying the request.
39
+ * This callback can be called multiple times by context providers as the requested value is changed.
40
+ */
41
+ type ContextCallback<V> = (value: V, unsubscribe?: () => void) => void
42
+
43
+ declare global {
44
+ interface HTMLElementEventMap {
45
+ /**
46
+ * A 'context-request' event can be emitted by any element which desires
47
+ * a context value to be injected by an external provider.
48
+ */
49
+ 'context-request': ContextRequestEvent<UnknownContext>
50
+ }
51
+ }
52
+
53
+ /* === Constants === */
54
+
55
+ const CONTEXT_REQUEST = 'context-request'
56
+
57
+ /* === Exported class === */
58
+
59
+ /**
60
+ * Class for context-request events
61
+ *
62
+ * An event fired by a context requester to signal it desires a named context.
63
+ *
64
+ * A provider should inspect the `context` property of the event to determine if it has a value that can
65
+ * satisfy the request, calling the `callback` with the requested value if so.
66
+ *
67
+ * If the requested context event contains a truthy `subscribe` value, then a provider can call the callback
68
+ * multiple times if the value is changed, if this is the case the provider should pass an `unsubscribe`
69
+ * function to the callback which requesters can invoke to indicate they no longer wish to receive these updates.
70
+ *
71
+ * @class ContextRequestEvent
72
+ * @extends {Event}
73
+ *
74
+ * @property {T} context - context key
75
+ * @property {ContextCallback<ContextType<T>>} callback - callback function for value getter and unsubscribe function
76
+ * @property {boolean} [subscribe=false] - whether to subscribe to context changes
77
+ */
78
+ class ContextRequestEvent<T extends UnknownContext> extends Event {
79
+ readonly context: T
80
+ readonly callback: ContextCallback<ContextType<T>>
81
+ readonly subscribe: boolean
82
+
83
+ constructor(
84
+ context: T,
85
+ callback: ContextCallback<ContextType<T>>,
86
+ subscribe: boolean = false,
87
+ ) {
88
+ super(CONTEXT_REQUEST, {
89
+ bubbles: true,
90
+ composed: true,
91
+ })
92
+ this.context = context
93
+ this.callback = callback
94
+ this.subscribe = subscribe
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Provide a context for descendant component consumers
100
+ *
101
+ * @since 0.13.3
102
+ * @param {Context<string, P[K]>[]} contexts - Array of contexts to provide
103
+ * @returns {(host: Component<P>) => Cleanup} Function to add an event listener for ContextRequestEvent returning a cleanup function to remove the event listener
104
+ */
105
+ const provideContexts =
106
+ <P extends ComponentProps>(
107
+ contexts: Array<keyof P>,
108
+ ): ((host: Component<P>) => Cleanup) =>
109
+ (host: Component<P>) => {
110
+ const listener = (e: ContextRequestEvent<UnknownContext>) => {
111
+ const { context, callback } = e
112
+ if (
113
+ isString(context) &&
114
+ contexts.includes(context as unknown as Extract<keyof P, string>) &&
115
+ isFunction(callback)
116
+ ) {
117
+ e.stopImmediatePropagation()
118
+ callback(() => host[context])
119
+ }
120
+ }
121
+ host.addEventListener(CONTEXT_REQUEST, listener)
122
+ return () => host.removeEventListener(CONTEXT_REQUEST, listener)
123
+ }
124
+
125
+ /**
126
+ * Consume a context value for a component
127
+ *
128
+ * @since 0.15.0
129
+ * @param {Context<string, () => T>} context - Context key to consume
130
+ * @param {Fallback<T, U>} fallback - Fallback value or reader function for fallback
131
+ * @returns {Reader<Computed<T>, U>} Computed signal that returns the consumed context the fallback value
132
+ */
133
+ const requestContext =
134
+ <T extends {}, P extends ComponentProps, U extends UI>(
135
+ context: Context<string, () => T>,
136
+ fallback: Fallback<T, U & { host: Component<P> }>,
137
+ ): Reader<Computed<T>, U & { host: Component<P> }> =>
138
+ (ui: U & { host: Component<P> }) => {
139
+ let consumed = () => getFallback(ui, fallback)
140
+ ui.host.dispatchEvent(
141
+ new ContextRequestEvent(context, (getter: () => T) => {
142
+ consumed = getter
143
+ }),
144
+ )
145
+ return createComputed(consumed)
146
+ }
147
+
148
+ export {
149
+ type Context,
150
+ type UnknownContext,
151
+ type ContextType,
152
+ CONTEXT_REQUEST,
153
+ ContextRequestEvent,
154
+ provideContexts,
155
+ requestContext,
156
+ }
@@ -0,0 +1,82 @@
1
+ import type { ComponentProps } from '../component'
2
+ import { type Effect, type Reactive, updateElement } from '../effects'
3
+
4
+ /* === Internal Functions === */
5
+
6
+ const isSafeURL = (value: string): boolean => {
7
+ if (/^(mailto|tel):/i.test(value)) return true
8
+ if (value.includes('://')) {
9
+ try {
10
+ const url = new URL(value, window.location.origin)
11
+ return ['http:', 'https:', 'ftp:'].includes(url.protocol)
12
+ } catch {
13
+ return false
14
+ }
15
+ }
16
+ return true
17
+ }
18
+
19
+ const safeSetAttribute = (
20
+ element: Element,
21
+ attr: string,
22
+ value: string,
23
+ ): void => {
24
+ if (/^on/i.test(attr)) throw new Error(`Unsafe attribute: ${attr}`)
25
+ value = String(value).trim()
26
+ if (!isSafeURL(value)) throw new Error(`Unsafe URL for ${attr}: ${value}`)
27
+ element.setAttribute(attr, value)
28
+ }
29
+
30
+ /* === Exported Functions === */
31
+
32
+ /**
33
+ * Effect for setting an attribute on an element.
34
+ * Sets the specified attribute with security validation for unsafe values.
35
+ *
36
+ * @since 0.8.0
37
+ * @param {string} name - Name of the attribute to set
38
+ * @param {Reactive<string, P, E>} reactive - Reactive value bound to the attribute value (defaults to attribute name)
39
+ * @returns {Effect<P, E>} Effect function that sets the attribute on the element
40
+ */
41
+ const setAttribute = <P extends ComponentProps, E extends Element>(
42
+ name: string,
43
+ reactive: Reactive<string, P, E> = name as Reactive<string, P, E>,
44
+ ): Effect<P, E> =>
45
+ updateElement(reactive, {
46
+ op: 'a',
47
+ name,
48
+ read: el => el.getAttribute(name),
49
+ update: (el, value) => {
50
+ safeSetAttribute(el, name, value)
51
+ },
52
+ delete: el => {
53
+ el.removeAttribute(name)
54
+ },
55
+ })
56
+
57
+ /**
58
+ * Effect for toggling a boolean attribute on an element.
59
+ * When the reactive value is true, the attribute is present; when false, it's absent.
60
+ *
61
+ * @since 0.8.0
62
+ * @param {string} name - Name of the attribute to toggle
63
+ * @param {Reactive<boolean, P, E>} reactive - Reactive value bound to the attribute presence (defaults to attribute name)
64
+ * @returns {Effect<P, E>} Effect function that toggles the attribute on the element
65
+ */
66
+ const toggleAttribute = <
67
+ P extends ComponentProps,
68
+ E extends Element = HTMLElement,
69
+ >(
70
+ name: string,
71
+ reactive: Reactive<boolean, P, E> = name as Reactive<boolean, P, E>,
72
+ ): Effect<P, E> =>
73
+ updateElement(reactive, {
74
+ op: 'a',
75
+ name,
76
+ read: el => el.hasAttribute(name),
77
+ update: (el, value) => {
78
+ el.toggleAttribute(name, value)
79
+ },
80
+ })
81
+
82
+ export { setAttribute, toggleAttribute }
@@ -0,0 +1,28 @@
1
+ import type { ComponentProps } from '../component'
2
+ import { type Effect, type Reactive, updateElement } from '../effects'
3
+
4
+ /* === Exported Function === */
5
+
6
+ /**
7
+ * Effect for toggling a CSS class token on an element.
8
+ * When the reactive value is true, the class is added; when false, it's removed.
9
+ *
10
+ * @since 0.8.0
11
+ * @param {string} token - CSS class token to toggle
12
+ * @param {Reactive<boolean, P, E>} reactive - Reactive value bound to the class presence (defaults to class name)
13
+ * @returns {Effect<P, U, E>} Effect function that toggles the class on the element
14
+ */
15
+ const toggleClass = <P extends ComponentProps, E extends Element>(
16
+ token: string,
17
+ reactive: Reactive<boolean, P, E> = token as Reactive<boolean, P, E>,
18
+ ): Effect<P, E> =>
19
+ updateElement(reactive, {
20
+ op: 'c',
21
+ name: token,
22
+ read: el => el.classList.contains(token),
23
+ update: (el, value) => {
24
+ el.classList.toggle(token, value)
25
+ },
26
+ })
27
+
28
+ export { toggleClass }
@@ -0,0 +1,67 @@
1
+ import { batch, type Cleanup, isRecord } from '@zeix/cause-effect'
2
+ import type { ComponentProps } from '../component'
3
+ import { type Effect } from '../effects'
4
+ import { PASSIVE_EVENTS, schedule } from '../scheduler'
5
+ import { elementName, LOG_ERROR, log } from '../util'
6
+
7
+ /* === Types === */
8
+
9
+ type EventType<K extends string> = K extends keyof HTMLElementEventMap
10
+ ? HTMLElementEventMap[K]
11
+ : Event
12
+
13
+ type EventHandler<P extends ComponentProps, Evt extends Event> = (
14
+ event: Evt,
15
+ ) => { [K in keyof P]?: P[K] } | void | Promise<void>
16
+
17
+ /* === Exported Function === */
18
+
19
+ /**
20
+ * Effect for attaching an event listener to an element.
21
+ * Provides proper cleanup when the effect is disposed.
22
+ *
23
+ * @since 0.14.0
24
+ * @param {K} type - Event type
25
+ * @param {EventHandler<P, E, EventType<K>>} handler - Event handler function
26
+ * @param {AddEventListenerOptions | boolean} options - Event listener options
27
+ * @returns {Effect<ComponentProps, E>} Effect function that manages the event listener
28
+ */
29
+ const on =
30
+ <
31
+ K extends keyof HTMLElementEventMap | string,
32
+ P extends ComponentProps,
33
+ E extends Element = HTMLElement,
34
+ >(
35
+ type: K,
36
+ handler: EventHandler<P, EventType<K>>,
37
+ options: AddEventListenerOptions = {},
38
+ ): Effect<P, E> =>
39
+ (host, target): Cleanup => {
40
+ if (!('passive' in options))
41
+ options = { ...options, passive: PASSIVE_EVENTS.has(type) }
42
+ const listener = (e: Event) => {
43
+ const task = () => {
44
+ const result = handler(e as EventType<K>)
45
+ if (!isRecord(result)) return
46
+ batch(() => {
47
+ for (const [key, value] of Object.entries(result)) {
48
+ try {
49
+ host[key as keyof P] = value
50
+ } catch (error) {
51
+ log(
52
+ error,
53
+ `Reactive property "${key}" on ${elementName(host)} from event ${type} on ${elementName(target)} could not be set, because it is read-only.`,
54
+ LOG_ERROR,
55
+ )
56
+ }
57
+ }
58
+ })
59
+ }
60
+ if (options.passive) schedule(target, task)
61
+ else task()
62
+ }
63
+ target.addEventListener(type, listener, options)
64
+ return () => target.removeEventListener(type, listener)
65
+ }
66
+
67
+ export { type EventHandler, type EventType, on }
@@ -0,0 +1,60 @@
1
+ import type { ComponentProps } from '../component'
2
+ import { type Effect, type Reactive, updateElement } from '../effects'
3
+ import { schedule } from '../scheduler'
4
+
5
+ /* === Types === */
6
+
7
+ type DangerouslySetInnerHTMLOptions = {
8
+ shadowRootMode?: ShadowRootMode
9
+ allowScripts?: boolean
10
+ }
11
+
12
+ /* === Exported Function === */
13
+
14
+ /**
15
+ * Effect for setting the inner HTML of an element with optional Shadow DOM support.
16
+ * Provides security options for script execution and shadow root creation.
17
+ *
18
+ * @since 0.11.0
19
+ * @param {Reactive<string, P, E>} reactive - Reactive value bound to the inner HTML content
20
+ * @param {DangerouslySetInnerHTMLOptions} options - Configuration options: shadowRootMode, allowScripts
21
+ * @returns {Effect<P, E>} Effect function that sets the inner HTML of the element
22
+ */
23
+ const dangerouslySetInnerHTML = <P extends ComponentProps, E extends Element>(
24
+ reactive: Reactive<string, P, E>,
25
+ options: DangerouslySetInnerHTMLOptions = {},
26
+ ): Effect<P, E> =>
27
+ updateElement(reactive, {
28
+ op: 'h',
29
+ read: el =>
30
+ (el.shadowRoot || !options.shadowRootMode ? el : null)?.innerHTML ?? '',
31
+ update: (el, html) => {
32
+ const { shadowRootMode, allowScripts } = options
33
+ if (!html) {
34
+ if (el.shadowRoot) el.shadowRoot.innerHTML = '<slot></slot>'
35
+ return ''
36
+ }
37
+ if (shadowRootMode && !el.shadowRoot)
38
+ el.attachShadow({ mode: shadowRootMode })
39
+ const target = el.shadowRoot || el
40
+ schedule(el, () => {
41
+ target.innerHTML = html
42
+ if (allowScripts) {
43
+ target.querySelectorAll('script').forEach(script => {
44
+ const newScript = document.createElement('script')
45
+ newScript.appendChild(
46
+ document.createTextNode(script.textContent ?? ''),
47
+ )
48
+ // Safely copy only the type attribute to preserve module/MIME type info
49
+ const typeAttr = script.getAttribute('type')
50
+ if (typeAttr) newScript.setAttribute('type', typeAttr)
51
+ target.appendChild(newScript)
52
+ script.remove()
53
+ })
54
+ }
55
+ })
56
+ return allowScripts ? ' with scripts' : ''
57
+ },
58
+ })
59
+
60
+ export { type DangerouslySetInnerHTMLOptions, dangerouslySetInnerHTML }
@@ -0,0 +1,57 @@
1
+ import type { ComponentProps } from '../component'
2
+ import { type Effect, type Reactive, updateElement } from '../effects'
3
+ import { hasMethod } from '../util'
4
+
5
+ /* === Exported Functions === */
6
+
7
+ /**
8
+ * Effect for calling a method on an element.
9
+ *
10
+ * @since 0.13.3
11
+ * @param {K} methodName - Name of the method to call
12
+ * @param {Reactive<boolean, P, E>} reactive - Reactive value bound to the method call
13
+ * @param {unknown[]} args - Arguments to pass to the method
14
+ * @returns Effect function that calls the method on the element
15
+ */
16
+ const callMethod = <
17
+ P extends ComponentProps,
18
+ E extends HTMLElement,
19
+ K extends keyof E & string,
20
+ >(
21
+ methodName: K,
22
+ reactive: Reactive<boolean, P, E>,
23
+ args?: unknown[],
24
+ ): Effect<P, E> =>
25
+ updateElement(reactive, {
26
+ op: 'm',
27
+ name: String(methodName),
28
+ read: () => null,
29
+ update: (el, value) => {
30
+ if (value && hasMethod(el, methodName)) {
31
+ if (args) el[methodName](...args)
32
+ else el[methodName]()
33
+ }
34
+ },
35
+ })
36
+
37
+ /**
38
+ * Effect for controlling element focus by calling the 'focus()' method.
39
+ * If the reactive value is true, element will be focussed; when false, nothing happens.
40
+ *
41
+ * @since 0.13.3
42
+ * @param {Reactive<boolean, P, E>} reactive - Reactive value bound to the focus state
43
+ * @returns {Effect<P, E>} Effect function that sets element focus
44
+ */
45
+ const focus = <P extends ComponentProps, E extends HTMLElement>(
46
+ reactive: Reactive<boolean, P, E>,
47
+ ): Effect<P, E> =>
48
+ updateElement(reactive, {
49
+ op: 'm',
50
+ name: 'focus',
51
+ read: el => el === document.activeElement,
52
+ update: (el, value) => {
53
+ if (value && hasMethod(el, 'focus')) el.focus()
54
+ },
55
+ })
56
+
57
+ export { callMethod, focus }
@@ -0,0 +1,103 @@
1
+ import {
2
+ createComputed,
3
+ isComputedCallback,
4
+ isFunction,
5
+ isRecord,
6
+ isSignal,
7
+ isString,
8
+ type MaybeCleanup,
9
+ UNSET,
10
+ } from '@zeix/cause-effect'
11
+ import type { Component, ComponentProps } from '../component'
12
+ import type { Effect, Reactive } from '../effects'
13
+ import { InvalidCustomElementError, InvalidReactivesError } from '../errors'
14
+ import { elementName, isCustomElement } from '../util'
15
+
16
+ /* === Types === */
17
+
18
+ type PassedProp<T, P extends ComponentProps, E extends HTMLElement> =
19
+ | Reactive<T, P, E>
20
+ | [Reactive<T, P, E>, (value: T) => void]
21
+
22
+ type PassedProps<P extends ComponentProps, Q extends ComponentProps> = {
23
+ [K in keyof Q & string]?: PassedProp<Q[K], P, Component<Q>>
24
+ }
25
+
26
+ /* === Exported Function === */
27
+
28
+ /**
29
+ * Effect for passing reactive values to a descendant Le Truc component.
30
+ *
31
+ * @since 0.15.0
32
+ * @param {MutableReactives<Component<Q>, P>} props - Reactive values to pass
33
+ * @returns {Effect<P, Component<Q>>} Effect function that passes reactive values to the descendant component
34
+ * @throws {InvalidCustomElementError} When the target element is not a valid custom element
35
+ * @throws {InvalidReactivesError} When the provided reactives is not a record of signals, reactive property names or functions
36
+ * @throws {Error} When passing signals failed for some other reason
37
+ */
38
+ const pass =
39
+ <P extends ComponentProps, Q extends ComponentProps>(
40
+ props: PassedProps<P, Q> | ((target: Component<Q>) => PassedProps<P, Q>),
41
+ ): Effect<P, Component<Q>> =>
42
+ (host, target): MaybeCleanup => {
43
+ if (!isCustomElement(target))
44
+ throw new InvalidCustomElementError(
45
+ target,
46
+ `pass from ${elementName(host)}`,
47
+ )
48
+ const reactives = isFunction(props) ? props(target) : props
49
+ if (!isRecord(reactives))
50
+ throw new InvalidReactivesError(host, target, reactives)
51
+
52
+ const resetProperties: PropertyDescriptorMap = {}
53
+
54
+ // Return getter from signal, reactive property name or function
55
+ const getGetter = (value: unknown) => {
56
+ if (isSignal(value)) return value.get
57
+ const fn =
58
+ isString(value) && value in host
59
+ ? () => host[value as keyof typeof host]
60
+ : isComputedCallback(value)
61
+ ? value
62
+ : undefined
63
+ return fn ? createComputed(fn).get : undefined
64
+ }
65
+
66
+ // Iterate through reactives
67
+ for (const [prop, reactive] of Object.entries(reactives)) {
68
+ if (reactive == null) continue
69
+
70
+ // Ensure target has configurable property
71
+ const descriptor = Object.getOwnPropertyDescriptor(target, prop)
72
+ if (!(prop in target) || !descriptor?.configurable) continue
73
+
74
+ // Determine getter and setter
75
+ const applied =
76
+ isFunction(reactive) && reactive.length === 1
77
+ ? reactive(target)
78
+ : reactive
79
+ const isArray = Array.isArray(applied) && applied.length === 2
80
+ const getter = getGetter(isArray ? applied[0] : applied)
81
+ const setter = isArray && isFunction(applied[1]) ? applied[1] : undefined
82
+ if (!getter) continue
83
+
84
+ // Store original descriptor for reset and assign new descriptor
85
+ resetProperties[prop] = descriptor
86
+ Object.defineProperty(target, prop, {
87
+ configurable: true,
88
+ enumerable: true,
89
+ get: getter,
90
+ set: setter,
91
+ })
92
+
93
+ // Unset previous value so subscribers are notified
94
+ descriptor.set?.call(target, UNSET)
95
+ }
96
+
97
+ // Reset to original descriptors on cleanup
98
+ return () => {
99
+ Object.defineProperties(target, resetProperties)
100
+ }
101
+ }
102
+
103
+ export { type PassedProp, type PassedProps, pass }
@@ -0,0 +1,57 @@
1
+ import { UNSET } from '@zeix/cause-effect'
2
+ import type { ComponentProps } from '../component'
3
+ import { type Effect, type Reactive, updateElement } from '../effects'
4
+
5
+ /* === Exported Functions === */
6
+
7
+ /**
8
+ * Effect for setting a property on an element.
9
+ * Sets the specified property directly on the element object.
10
+ *
11
+ * @since 0.8.0
12
+ * @param {K} key - Name of the property to set
13
+ * @param {Reactive<E[K], P, E>} reactive - Reactive value bound to the property value (defaults to property name)
14
+ * @returns {Effect<P, E>} Effect function that sets the property on the element
15
+ */
16
+ const setProperty = <
17
+ P extends ComponentProps,
18
+ E extends Element,
19
+ K extends keyof E & string,
20
+ >(
21
+ key: K,
22
+ reactive: Reactive<E[K] & {}, P, E> = key as unknown as Reactive<
23
+ E[K] & {},
24
+ P,
25
+ E
26
+ >,
27
+ ): Effect<P, E> =>
28
+ updateElement(reactive, {
29
+ op: 'p',
30
+ name: key,
31
+ read: el => (key in el ? el[key] : UNSET),
32
+ update: (el, value) => {
33
+ el[key] = value
34
+ },
35
+ })
36
+
37
+ /**
38
+ * Effect for controlling element visibility by setting the 'hidden' property.
39
+ * When the reactive value is true, the element is shown; when false, it's hidden.
40
+ *
41
+ * @since 0.13.1
42
+ * @param {Reactive<boolean, P, E>} reactive - Reactive value bound to the visibility state
43
+ * @returns {Effect<P, E>} Effect function that controls element visibility
44
+ */
45
+ const show = <P extends ComponentProps, E extends HTMLElement = HTMLElement>(
46
+ reactive: Reactive<boolean, P, E>,
47
+ ): Effect<P, E> =>
48
+ updateElement(reactive, {
49
+ op: 'p',
50
+ name: 'hidden',
51
+ read: el => !el.hidden,
52
+ update: (el, value) => {
53
+ el.hidden = !value
54
+ },
55
+ })
56
+
57
+ export { setProperty, show }
@@ -0,0 +1,34 @@
1
+ import type { ComponentProps } from '../component'
2
+ import { type Effect, type Reactive, updateElement } from '../effects'
3
+
4
+ /* === Exported Function === */
5
+
6
+ /**
7
+ * Effect for setting a CSS style property on an element.
8
+ * Sets the specified style property with support for deletion via UNSET.
9
+ *
10
+ * @since 0.8.0
11
+ * @param {string} prop - Name of the CSS style property to set
12
+ * @param {Reactive<string, P, E>} reactive - Reactive value bound to the style property value (defaults to property name)
13
+ * @returns {Effect<P, E>} Effect function that sets the style property on the element
14
+ */
15
+ const setStyle = <
16
+ P extends ComponentProps,
17
+ E extends HTMLElement | SVGElement | MathMLElement,
18
+ >(
19
+ prop: string,
20
+ reactive: Reactive<string, P, E> = prop as Reactive<string, P, E>,
21
+ ): Effect<P, E> =>
22
+ updateElement(reactive, {
23
+ op: 's',
24
+ name: prop,
25
+ read: el => el.style.getPropertyValue(prop),
26
+ update: (el, value) => {
27
+ el.style.setProperty(prop, value)
28
+ },
29
+ delete: el => {
30
+ el.style.removeProperty(prop)
31
+ },
32
+ })
33
+
34
+ export { setStyle }
@@ -0,0 +1,28 @@
1
+ import type { ComponentProps } from '../component'
2
+ import { type Effect, type Reactive, updateElement } from '../effects'
3
+
4
+ /* === Exported Function === */
5
+
6
+ /**
7
+ * Effect for setting the text content of an element.
8
+ * Replaces all child nodes (except comments) with a single text node.
9
+ *
10
+ * @since 0.8.0
11
+ * @param {Reactive<string, P, E>} reactive - Reactive value bound to the text content
12
+ * @returns {Effect<P, E>} Effect function that sets the text content of the element
13
+ */
14
+ const setText = <P extends ComponentProps, E extends Element>(
15
+ reactive: Reactive<string, P, E>,
16
+ ): Effect<P, E> =>
17
+ updateElement(reactive, {
18
+ op: 't',
19
+ read: el => el.textContent,
20
+ update: (el, value) => {
21
+ Array.from(el.childNodes)
22
+ .filter(node => node.nodeType !== Node.COMMENT_NODE)
23
+ .forEach(node => node.remove())
24
+ el.append(document.createTextNode(value))
25
+ },
26
+ })
27
+
28
+ export { setText }