@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
@@ -0,0 +1,82 @@
1
+ import {
2
+ asInteger,
3
+ type Component,
4
+ defineComponent,
5
+ setText,
6
+ show,
7
+ UI,
8
+ } from '../..'
9
+
10
+ export type BasicPluralizeProps = {
11
+ count: number
12
+ }
13
+
14
+ type BasicPluralizeUI = Partial<
15
+ Record<
16
+ | 'count'
17
+ | 'none'
18
+ | 'some'
19
+ | 'zero'
20
+ | 'one'
21
+ | 'two'
22
+ | 'few'
23
+ | 'many'
24
+ | 'other',
25
+ HTMLElement
26
+ >
27
+ >
28
+
29
+ declare global {
30
+ interface HTMLElementTagNameMap {
31
+ 'basic-pluralize': Component<BasicPluralizeProps>
32
+ }
33
+ }
34
+
35
+ const FALLBACK_LOCALE = 'en'
36
+
37
+ /**
38
+ * Parse a string as a positive integer (>= 0), falling back to 0 for negative values
39
+ */
40
+ const asPositiveInteger =
41
+ () =>
42
+ <U extends UI>(ui: U, value: string | null | undefined) => {
43
+ const parsed = asInteger()(ui, value)
44
+ return parsed < 0 ? 0 : parsed
45
+ }
46
+
47
+ export default defineComponent<BasicPluralizeProps, BasicPluralizeUI>(
48
+ 'basic-pluralize',
49
+ {
50
+ count: asPositiveInteger(),
51
+ },
52
+ ({ first }) => ({
53
+ count: first('.count'),
54
+ none: first('.none'),
55
+ some: first('.some'),
56
+ zero: first('.zero'),
57
+ one: first('.one'),
58
+ two: first('.two'),
59
+ few: first('.few'),
60
+ many: first('.many'),
61
+ other: first('.other'),
62
+ }),
63
+ ({ host }) => {
64
+ const pluralizer = new Intl.PluralRules(
65
+ host.closest('[lang]')?.getAttribute('lang') || FALLBACK_LOCALE,
66
+ host.hasAttribute('ordinal') ? { type: 'ordinal' } : undefined,
67
+ )
68
+
69
+ // Basic effects
70
+ const effects = {
71
+ count: setText(() => String(host.count)),
72
+ none: show(() => host.count === 0),
73
+ some: show(() => host.count > 0),
74
+ }
75
+
76
+ // Subset of plural categories for applicable pluralizer: ['zero', 'one', 'two', 'few', 'many', 'other']
77
+ const categories = pluralizer.resolvedOptions().pluralCategories
78
+ for (const category of categories)
79
+ effects[category] = show(() => pluralizer.select(host.count) === category)
80
+ return effects
81
+ },
82
+ )
@@ -0,0 +1,79 @@
1
+ card-callout {
2
+ display: block;
3
+ position: relative;
4
+ padding: var(--space-s) var(--space-s) var(--space-s) var(--space-xl);
5
+ margin: 0 0 var(--space-l);
6
+ font-size: var(--font-size-m);
7
+ line-height: var(--line-height-l);
8
+ background: var(--color-blue-20);
9
+ border-left: var(--space-xxs) solid var(--color-blue-50);
10
+ border-radius: var(--space-s);
11
+
12
+ &::before {
13
+ position: absolute;
14
+ content: "ℹ️";
15
+ left: var(--space-s);
16
+ }
17
+
18
+ > *:last-child {
19
+ margin-bottom: 0;
20
+ }
21
+
22
+ &.tip {
23
+ background: var(--color-green-20);
24
+ border-color: var(--color-green-50);
25
+
26
+ &::before {
27
+ content: "💡";
28
+ }
29
+ }
30
+
31
+ &.note {
32
+ background: var(--color-neutral-20);
33
+ border-color: var(--color-neutral-50);
34
+
35
+ &::before {
36
+ content: "💬";
37
+ }
38
+ }
39
+
40
+ &.caution {
41
+ background: var(--color-orange-20);
42
+ border-color: var(--color-orange-50);
43
+
44
+ &::before {
45
+ content: "⚠️";
46
+ }
47
+ }
48
+
49
+ &.danger {
50
+ background: var(--color-pink-20);
51
+ border-color: var(--color-pink-50);
52
+
53
+ &::before {
54
+ content: "🚨";
55
+ }
56
+ }
57
+ }
58
+
59
+ @media (prefers-color-scheme: dark) {
60
+ card-callout {
61
+ background: var(--color-blue-80);
62
+
63
+ &.tip {
64
+ background: var(--color-green-80);
65
+ }
66
+
67
+ &.note {
68
+ background: var(--color-neutral-80);
69
+ }
70
+
71
+ &.caution {
72
+ background: var(--color-orange-80);
73
+ }
74
+
75
+ &.danger {
76
+ background: var(--color-pink-80);
77
+ }
78
+ }
79
+ }
@@ -0,0 +1,5 @@
1
+ <card-callout>This is an informational message.</card-callout>
2
+ <card-callout class="tip">Remember to hydrate while coding!</card-callout>
3
+ <card-callout class="caution">Be careful with this operation.</card-callout>
4
+ <card-callout class="danger">This action is irreversible!</card-callout>
5
+ <card-callout class="note">This is just a side note.</card-callout>
@@ -0,0 +1,29 @@
1
+ <card-mediaqueries>
2
+ <h2>Without Context</h2>
3
+ <dl>
4
+ <dt>Motion Preference:</dt>
5
+ <dd class="motion"></dd>
6
+ <dt>Theme Preference</dt>
7
+ <dd class="theme"></dd>
8
+ <dt>Device Viewport:</dt>
9
+ <dd class="viewport"></dd>
10
+ <dt>Device Orientation:</dt>
11
+ <dd class="orientation"></dd>
12
+ </dl>
13
+ </card-mediaqueries>
14
+
15
+ <context-media>
16
+ <card-mediaqueries>
17
+ <h2>With Context</h2>
18
+ <dl>
19
+ <dt>Motion Preference:</dt>
20
+ <dd class="motion"></dd>
21
+ <dt>Theme Preference:</dt>
22
+ <dd class="theme"></dd>
23
+ <dt>Device Viewport:</dt>
24
+ <dd class="viewport"></dd>
25
+ <dt>Device Orientation:</dt>
26
+ <dd class="orientation"></dd>
27
+ </dl>
28
+ </card-mediaqueries>
29
+ </context-media>
@@ -0,0 +1,300 @@
1
+ import { expect, test } from '@playwright/test'
2
+
3
+ test.describe('card-mediaqueries component', () => {
4
+ test.beforeEach(async ({ page }) => {
5
+ await page.goto('http://localhost:3000/test/card-mediaqueries.html')
6
+ await page.waitForSelector('card-mediaqueries')
7
+ })
8
+
9
+ test('renders component elements correctly', async ({ page }) => {
10
+ // Check both instances exist
11
+ const componentWithoutContext = page.locator('card-mediaqueries').first()
12
+ const componentWithContext = page.locator('context-media card-mediaqueries')
13
+
14
+ await expect(componentWithoutContext).toBeVisible()
15
+ await expect(componentWithContext).toBeVisible()
16
+
17
+ // Check that each has the expected structure
18
+ for (const component of [componentWithoutContext, componentWithContext]) {
19
+ await expect(component.locator('h2')).toBeVisible()
20
+ await expect(component.locator('.motion')).toBeVisible()
21
+ await expect(component.locator('.theme')).toBeVisible()
22
+ await expect(component.locator('.viewport')).toBeVisible()
23
+ await expect(component.locator('.orientation')).toBeVisible()
24
+ }
25
+ })
26
+
27
+ test('shows fallback values without context provider', async ({ page }) => {
28
+ const componentWithoutContext = page.locator('card-mediaqueries').first()
29
+
30
+ // All values should show 'unknown' fallback
31
+ await expect(componentWithoutContext.locator('.motion')).toHaveText(
32
+ 'unknown',
33
+ )
34
+ await expect(componentWithoutContext.locator('.theme')).toHaveText(
35
+ 'unknown',
36
+ )
37
+ await expect(componentWithoutContext.locator('.viewport')).toHaveText(
38
+ 'unknown',
39
+ )
40
+ await expect(componentWithoutContext.locator('.orientation')).toHaveText(
41
+ 'unknown',
42
+ )
43
+ })
44
+
45
+ test('receives context values from provider', async ({ page }) => {
46
+ const componentWithContext = page.locator('context-media card-mediaqueries')
47
+
48
+ // Motion preference should be detected
49
+ const motionText = await componentWithContext
50
+ .locator('.motion')
51
+ .textContent()
52
+ expect(['no-preference', 'reduce']).toContain(motionText)
53
+
54
+ // Theme preference should be detected
55
+ const themeText = await componentWithContext.locator('.theme').textContent()
56
+ expect(['light', 'dark']).toContain(themeText)
57
+
58
+ // Viewport should be detected based on current window size
59
+ const viewportText = await componentWithContext
60
+ .locator('.viewport')
61
+ .textContent()
62
+ expect(['xs', 'sm', 'md', 'lg', 'xl']).toContain(viewportText)
63
+
64
+ // Orientation should be detected
65
+ const orientationText = await componentWithContext
66
+ .locator('.orientation')
67
+ .textContent()
68
+ expect(['portrait', 'landscape']).toContain(orientationText)
69
+ })
70
+
71
+ test('context values differ from fallback values', async ({ page }) => {
72
+ const componentWithoutContext = page.locator('card-mediaqueries').first()
73
+ const componentWithContext = page.locator('context-media card-mediaqueries')
74
+
75
+ // Get values from both components
76
+ const [
77
+ motionWithoutContext,
78
+ motionWithContext,
79
+ themeWithoutContext,
80
+ themeWithContext,
81
+ viewportWithoutContext,
82
+ viewportWithContext,
83
+ orientationWithoutContext,
84
+ orientationWithContext,
85
+ ] = await Promise.all([
86
+ componentWithoutContext.locator('.motion').textContent(),
87
+ componentWithContext.locator('.motion').textContent(),
88
+ componentWithoutContext.locator('.theme').textContent(),
89
+ componentWithContext.locator('.theme').textContent(),
90
+ componentWithoutContext.locator('.viewport').textContent(),
91
+ componentWithContext.locator('.viewport').textContent(),
92
+ componentWithoutContext.locator('.orientation').textContent(),
93
+ componentWithContext.locator('.orientation').textContent(),
94
+ ])
95
+
96
+ // Without context should always be 'unknown'
97
+ expect(motionWithoutContext).toBe('unknown')
98
+ expect(themeWithoutContext).toBe('unknown')
99
+ expect(viewportWithoutContext).toBe('unknown')
100
+ expect(orientationWithoutContext).toBe('unknown')
101
+
102
+ // With context should be actual detected values (not 'unknown')
103
+ expect(motionWithContext).not.toBe('unknown')
104
+ expect(themeWithContext).not.toBe('unknown')
105
+ expect(viewportWithContext).not.toBe('unknown')
106
+ expect(orientationWithContext).not.toBe('unknown')
107
+ })
108
+
109
+ test('properties reflect context values', async ({ page }) => {
110
+ const componentWithContext = page.locator('context-media card-mediaqueries')
111
+
112
+ // Check that component properties are accessible and match displayed values
113
+ const [
114
+ displayedMotion,
115
+ displayedTheme,
116
+ displayedViewport,
117
+ displayedOrientation,
118
+ ] = await Promise.all([
119
+ componentWithContext.locator('.motion').textContent(),
120
+ componentWithContext.locator('.theme').textContent(),
121
+ componentWithContext.locator('.viewport').textContent(),
122
+ componentWithContext.locator('.orientation').textContent(),
123
+ ])
124
+
125
+ const [
126
+ propertyMotion,
127
+ propertyTheme,
128
+ propertyViewport,
129
+ propertyOrientation,
130
+ ] = await page.evaluate(() => {
131
+ const element = document.querySelector(
132
+ 'context-media card-mediaqueries',
133
+ ) as any
134
+ return [
135
+ element.motion,
136
+ element.theme,
137
+ element.viewport,
138
+ element.orientation,
139
+ ]
140
+ })
141
+
142
+ expect(propertyMotion).toBe(displayedMotion)
143
+ expect(propertyTheme).toBe(displayedTheme)
144
+ expect(propertyViewport).toBe(displayedViewport)
145
+ expect(propertyOrientation).toBe(displayedOrientation)
146
+ })
147
+
148
+ test('responds to media query changes', async ({ page, isMobile }) => {
149
+ const componentWithContext = page.locator('context-media card-mediaqueries')
150
+
151
+ // Change viewport size to trigger media query changes
152
+ if (!isMobile) {
153
+ // Test desktop -> mobile transition
154
+ await page.setViewportSize({ width: 400, height: 600 })
155
+ await page.waitForTimeout(100) // Allow time for media query listeners
156
+
157
+ const mobileViewport = await componentWithContext
158
+ .locator('.viewport')
159
+ .textContent()
160
+
161
+ // Should show mobile viewport size
162
+ expect(['xs', 'sm']).toContain(mobileViewport)
163
+
164
+ // Change back to desktop
165
+ await page.setViewportSize({ width: 1200, height: 800 })
166
+ await page.waitForTimeout(100)
167
+
168
+ const desktopViewport = await componentWithContext
169
+ .locator('.viewport')
170
+ .textContent()
171
+
172
+ // Should show larger viewport size
173
+ expect(['md', 'lg', 'xl']).toContain(desktopViewport)
174
+
175
+ // Values should have changed
176
+ expect(mobileViewport).not.toBe(desktopViewport)
177
+ }
178
+ })
179
+
180
+ test('context provider supports custom breakpoints via attributes', async ({
181
+ page,
182
+ }) => {
183
+ // Add custom breakpoint attributes to the context-media element
184
+ await page.evaluate(() => {
185
+ const contextMedia = document.querySelector('context-media')
186
+ if (contextMedia) {
187
+ contextMedia.setAttribute('sm', '40em')
188
+ contextMedia.setAttribute('md', '60em')
189
+ contextMedia.setAttribute('lg', '80em')
190
+ contextMedia.setAttribute('xl', '120em')
191
+ }
192
+ })
193
+
194
+ // Wait a moment for the changes to take effect
195
+ await page.waitForTimeout(100)
196
+
197
+ const componentWithContext = page.locator('context-media card-mediaqueries')
198
+
199
+ // Viewport should still be a valid value
200
+ const viewportText = await componentWithContext
201
+ .locator('.viewport')
202
+ .textContent()
203
+ expect(['xs', 'sm', 'md', 'lg', 'xl']).toContain(viewportText)
204
+ })
205
+
206
+ test('multiple components receive same context values', async ({ page }) => {
207
+ // Add another card-mediaqueries component inside the context provider
208
+ await page.evaluate(() => {
209
+ const contextMedia = document.querySelector('context-media')
210
+ if (contextMedia) {
211
+ const newCard = document.createElement('card-mediaqueries')
212
+ newCard.innerHTML = `
213
+ <h2>Additional Card</h2>
214
+ <dl>
215
+ <dt>Motion:</dt><dd class="motion"></dd>
216
+ <dt>Theme:</dt><dd class="theme"></dd>
217
+ <dt>Viewport:</dt><dd class="viewport"></dd>
218
+ <dt>Orientation:</dt><dd class="orientation"></dd>
219
+ </dl>
220
+ `
221
+ contextMedia.appendChild(newCard)
222
+ }
223
+ })
224
+
225
+ const firstCard = page.locator('context-media card-mediaqueries').first()
226
+ const secondCard = page.locator('context-media card-mediaqueries').nth(1)
227
+
228
+ await expect(secondCard).toBeVisible()
229
+
230
+ // Both cards should receive the same context values
231
+ const [
232
+ firstMotion,
233
+ firstTheme,
234
+ firstViewport,
235
+ firstOrientation,
236
+ secondMotion,
237
+ secondTheme,
238
+ secondViewport,
239
+ secondOrientation,
240
+ ] = await Promise.all([
241
+ firstCard.locator('.motion').textContent(),
242
+ firstCard.locator('.theme').textContent(),
243
+ firstCard.locator('.viewport').textContent(),
244
+ firstCard.locator('.orientation').textContent(),
245
+ secondCard.locator('.motion').textContent(),
246
+ secondCard.locator('.theme').textContent(),
247
+ secondCard.locator('.viewport').textContent(),
248
+ secondCard.locator('.orientation').textContent(),
249
+ ])
250
+
251
+ expect(firstMotion).toBe(secondMotion)
252
+ expect(firstTheme).toBe(secondTheme)
253
+ expect(firstViewport).toBe(secondViewport)
254
+ expect(firstOrientation).toBe(secondOrientation)
255
+ })
256
+
257
+ // TODO: Re-enable this test when context-theme component is implemented
258
+ // This test doesn't make sense with context-media since it always returns
259
+ // the same browser/system values regardless of nesting
260
+ test.skip('context isolation - nested providers override parent values', async ({
261
+ page,
262
+ }) => {
263
+ // Create a nested context structure where inner context might override values
264
+ await page.evaluate(() => {
265
+ const outerContext = document.querySelector('context-media')
266
+ if (outerContext) {
267
+ const innerContext = document.createElement('context-media')
268
+ const nestedCard = document.createElement('card-mediaqueries')
269
+ nestedCard.innerHTML = `
270
+ <h2>Nested Card</h2>
271
+ <dl>
272
+ <dt>Motion:</dt><dd class="motion"></dd>
273
+ <dt>Theme:</dt><dd class="theme"></dd>
274
+ <dt>Viewport:</dt><dd class="viewport"></dd>
275
+ <dt>Orientation:</dt><dd class="orientation"></dd>
276
+ </dl>
277
+ `
278
+ innerContext.appendChild(nestedCard)
279
+ outerContext.appendChild(innerContext)
280
+ }
281
+ })
282
+
283
+ const outerCard = page.locator('context-media > card-mediaqueries')
284
+ const nestedCard = page.locator(
285
+ 'context-media context-media card-mediaqueries',
286
+ )
287
+
288
+ await expect(nestedCard).toBeVisible()
289
+
290
+ // Both should receive context values (same source since it's media queries)
291
+ const [outerMotion, nestedMotion] = await Promise.all([
292
+ outerCard.locator('.motion').textContent(),
293
+ nestedCard.locator('.motion').textContent(),
294
+ ])
295
+
296
+ // Should both be valid motion preferences (not 'unknown')
297
+ expect(['no-preference', 'reduce']).toContain(outerMotion)
298
+ expect(['no-preference', 'reduce']).toContain(nestedMotion)
299
+ })
300
+ })
@@ -0,0 +1,41 @@
1
+ import { Component, defineComponent, requestContext, setText } from '../..'
2
+ import {
3
+ MEDIA_MOTION,
4
+ MEDIA_ORIENTATION,
5
+ MEDIA_THEME,
6
+ MEDIA_VIEWPORT,
7
+ } from '../context-media/context-media'
8
+
9
+ type CardMediaqueriesPropKeys = 'motion' | 'theme' | 'viewport' | 'orientation'
10
+
11
+ export type CardMediaqueriesProps = Record<CardMediaqueriesPropKeys, string>
12
+
13
+ type CardMediaqueriesUI = Partial<Record<CardMediaqueriesPropKeys, HTMLElement>>
14
+
15
+ declare global {
16
+ interface HTMLElementTagNameMap {
17
+ 'card-mediaqueries': Component<CardMediaqueriesProps>
18
+ }
19
+ }
20
+
21
+ export default defineComponent<CardMediaqueriesProps, CardMediaqueriesUI>(
22
+ 'card-mediaqueries',
23
+ {
24
+ motion: requestContext(MEDIA_MOTION, 'unknown'),
25
+ theme: requestContext(MEDIA_THEME, 'unknown'),
26
+ viewport: requestContext(MEDIA_VIEWPORT, 'unknown'),
27
+ orientation: requestContext(MEDIA_ORIENTATION, 'unknown'),
28
+ },
29
+ ({ first }) => ({
30
+ motion: first('.motion'),
31
+ theme: first('.theme'),
32
+ viewport: first('.viewport'),
33
+ orientation: first('.orientation'),
34
+ }),
35
+ () => ({
36
+ motion: setText('motion'),
37
+ theme: setText('theme'),
38
+ viewport: setText('viewport'),
39
+ orientation: setText('orientation'),
40
+ }),
41
+ )
@@ -0,0 +1,3 @@
1
+ <context-media>
2
+ <!-- Arbitrarily nested HTML with one or many context consumers -->
3
+ </context-media>
@@ -0,0 +1,127 @@
1
+ import {
2
+ type Component,
3
+ type Context,
4
+ createState,
5
+ defineComponent,
6
+ provideContexts,
7
+ } from '../..'
8
+
9
+ export type ContextMediaMotion = 'no-preference' | 'reduce'
10
+ export type ContextMediaTheme = 'light' | 'dark'
11
+ export type ContextMediaViewport = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
12
+ export type ContextMediaOrientation = 'portrait' | 'landscape'
13
+
14
+ export type ContextMediaProps = {
15
+ readonly 'media-motion': ContextMediaMotion
16
+ readonly 'media-theme': ContextMediaTheme
17
+ readonly 'media-viewport': ContextMediaViewport
18
+ readonly 'media-orientation': ContextMediaOrientation
19
+ }
20
+
21
+ declare global {
22
+ interface HTMLElementTagNameMap {
23
+ 'context-media': Component<ContextMediaProps>
24
+ }
25
+ }
26
+
27
+ /* === Exported Contexts === */
28
+
29
+ export const MEDIA_MOTION = 'media-motion' as Context<
30
+ 'media-motion',
31
+ () => ContextMediaMotion
32
+ >
33
+ export const MEDIA_THEME = 'media-theme' as Context<
34
+ 'media-theme',
35
+ () => ContextMediaTheme
36
+ >
37
+ export const MEDIA_VIEWPORT = 'media-viewport' as Context<
38
+ 'media-viewport',
39
+ () => ContextMediaViewport
40
+ >
41
+ export const MEDIA_ORIENTATION = 'media-orientation' as Context<
42
+ 'media-orientation',
43
+ () => ContextMediaOrientation
44
+ >
45
+
46
+ /* === Component === */
47
+
48
+ export default defineComponent<ContextMediaProps>(
49
+ 'context-media',
50
+ {
51
+ // Context for motion preference; true for no-preference, false for reduce
52
+ [MEDIA_MOTION]: () => {
53
+ const mql = matchMedia('(prefers-reduced-motion: reduce)')
54
+ const motion = createState(mql.matches ? 'reduce' : 'no-preference')
55
+ mql.addEventListener('change', e => {
56
+ motion.set(e.matches ? 'reduce' : 'no-preference')
57
+ })
58
+ return motion
59
+ },
60
+
61
+ // Context for preferred color scheme
62
+ [MEDIA_THEME]: () => {
63
+ const mql = matchMedia('(prefers-color-scheme: dark)')
64
+ const theme = createState(mql.matches ? 'dark' : 'light')
65
+ mql.addEventListener('change', e => {
66
+ theme.set(e.matches ? 'dark' : 'light')
67
+ })
68
+ return theme
69
+ },
70
+
71
+ // Context for screen viewport size
72
+ [MEDIA_VIEWPORT]: (ui: { host: HTMLElement }) => {
73
+ const getBreakpoint = (attr: string, fallback: string) => {
74
+ const value = ui.host.getAttribute(attr)
75
+ const trimmed = value?.trim()
76
+ if (!trimmed) return fallback
77
+ const unit = trimmed.match(/em$/) ? 'em' : 'px'
78
+ const v = parseFloat(trimmed)
79
+ return Number.isFinite(v) ? v + unit : fallback
80
+ }
81
+ const mqlSM = matchMedia(`(min-width: ${getBreakpoint('sm', '32em')})`)
82
+ const mqlMD = matchMedia(`(min-width: ${getBreakpoint('md', '48em')})`)
83
+ const mqlLG = matchMedia(`(min-width: ${getBreakpoint('lg', '72em')})`)
84
+ const mqlXL = matchMedia(`(min-width: ${getBreakpoint('xl', '104em')})`)
85
+ const getViewport = () => {
86
+ if (mqlXL.matches) return 'xl'
87
+ if (mqlLG.matches) return 'lg'
88
+ if (mqlMD.matches) return 'md'
89
+ if (mqlSM.matches) return 'sm'
90
+ return 'xs'
91
+ }
92
+ const viewport = createState(getViewport())
93
+ mqlSM.addEventListener('change', () => {
94
+ viewport.set(getViewport())
95
+ })
96
+ mqlMD.addEventListener('change', () => {
97
+ viewport.set(getViewport())
98
+ })
99
+ mqlLG.addEventListener('change', () => {
100
+ viewport.set(getViewport())
101
+ })
102
+ mqlXL.addEventListener('change', () => {
103
+ viewport.set(getViewport())
104
+ })
105
+ return viewport
106
+ },
107
+
108
+ // Context for screen orientation
109
+ [MEDIA_ORIENTATION]: () => {
110
+ const mql = matchMedia('(orientation: landscape)')
111
+ const orientation = createState(mql.matches ? 'landscape' : 'portrait')
112
+ mql.addEventListener('change', e => {
113
+ orientation.set(e.matches ? 'landscape' : 'portrait')
114
+ })
115
+ return orientation
116
+ },
117
+ },
118
+ undefined,
119
+ () => ({
120
+ host: provideContexts([
121
+ MEDIA_MOTION,
122
+ MEDIA_THEME,
123
+ MEDIA_VIEWPORT,
124
+ MEDIA_ORIENTATION,
125
+ ]),
126
+ }),
127
+ )