sygnal 5.3.7 → 6.0.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 (438) hide show
  1. package/CHANGELOG.md +646 -0
  2. package/README.md +77 -47
  3. package/dist/astro/client.cjs.js +33 -8114
  4. package/dist/astro/client.cjs.js.map +1 -0
  5. package/dist/astro/client.mjs +33 -8114
  6. package/dist/astro/client.mjs.map +1 -0
  7. package/dist/astro/index.cjs.js +1039 -9
  8. package/dist/astro/index.cjs.js.map +1 -0
  9. package/dist/astro/index.mjs +1038 -9
  10. package/dist/astro/index.mjs.map +1 -0
  11. package/dist/astro/server.cjs.js +3400 -166
  12. package/dist/astro/server.cjs.js.map +1 -0
  13. package/dist/astro/server.mjs +3400 -166
  14. package/dist/astro/server.mjs.map +1 -0
  15. package/dist/devtools.cjs.js +1431 -0
  16. package/dist/devtools.cjs.js.map +1 -0
  17. package/dist/devtools.esm.js +1419 -0
  18. package/dist/devtools.esm.js.map +1 -0
  19. package/dist/diagnostics.cjs.js +4033 -0
  20. package/dist/diagnostics.cjs.js.map +1 -0
  21. package/dist/diagnostics.esm.js +4003 -0
  22. package/dist/diagnostics.esm.js.map +1 -0
  23. package/dist/element.cjs.js +230 -0
  24. package/dist/element.cjs.js.map +1 -0
  25. package/dist/element.esm.js +228 -0
  26. package/dist/element.esm.js.map +1 -0
  27. package/dist/guide/accessibility.md +257 -0
  28. package/dist/guide/adapters.md +221 -0
  29. package/dist/guide/behaviors.md +312 -0
  30. package/dist/guide/browser-sources.md +203 -0
  31. package/dist/guide/drag-and-drop.md +342 -0
  32. package/dist/guide/element-commands.md +306 -0
  33. package/dist/guide/error-boundaries.md +169 -0
  34. package/dist/guide/forms-reference.md +381 -0
  35. package/dist/guide/forms.md +250 -0
  36. package/dist/guide/http.md +299 -0
  37. package/dist/guide/inputs.md +173 -0
  38. package/dist/guide/persistence.md +245 -0
  39. package/dist/guide/recipes/carousel.md +155 -0
  40. package/dist/guide/recipes/charts.md +177 -0
  41. package/dist/guide/recipes/code-editor.md +135 -0
  42. package/dist/guide/recipes/data-grid.md +155 -0
  43. package/dist/guide/recipes/data-table.md +196 -0
  44. package/dist/guide/recipes/i18n.md +222 -0
  45. package/dist/guide/recipes/icons.md +115 -0
  46. package/dist/guide/recipes/overview.md +33 -0
  47. package/dist/guide/recipes/rich-text.md +154 -0
  48. package/dist/guide/resources.md +471 -0
  49. package/dist/guide/ssr.md +255 -0
  50. package/dist/guide/timers.md +178 -0
  51. package/dist/guide/ui/accordion.md +93 -0
  52. package/dist/guide/ui/combobox.md +144 -0
  53. package/dist/guide/ui/dialog.md +124 -0
  54. package/dist/guide/ui/disclosure.md +60 -0
  55. package/dist/guide/ui/menu.md +125 -0
  56. package/dist/guide/ui/overview.md +166 -0
  57. package/dist/guide/ui/popover.md +101 -0
  58. package/dist/guide/ui/select.md +114 -0
  59. package/dist/guide/ui/tabs.md +161 -0
  60. package/dist/guide/ui/toaster.md +152 -0
  61. package/dist/guide/ui/tooltip.md +103 -0
  62. package/dist/guide/undo.md +178 -0
  63. package/dist/guide/virtual-collections.md +183 -0
  64. package/dist/guide/web-components.md +179 -0
  65. package/dist/guide/widgets.md +217 -0
  66. package/dist/index.cjs.js +15283 -6279
  67. package/dist/index.cjs.js.map +1 -0
  68. package/dist/index.d.ts +3739 -370
  69. package/dist/index.esm.js +15230 -6274
  70. package/dist/index.esm.js.map +1 -0
  71. package/dist/jsx-dev-runtime.cjs.js +27 -329
  72. package/dist/jsx-dev-runtime.cjs.js.map +1 -0
  73. package/dist/jsx-dev-runtime.esm.js +20 -322
  74. package/dist/jsx-dev-runtime.esm.js.map +1 -0
  75. package/dist/jsx-runtime.cjs.js +27 -329
  76. package/dist/jsx-runtime.cjs.js.map +1 -0
  77. package/dist/jsx-runtime.esm.js +20 -322
  78. package/dist/jsx-runtime.esm.js.map +1 -0
  79. package/dist/jsx.cjs.js +11 -309
  80. package/dist/jsx.cjs.js.map +1 -0
  81. package/dist/jsx.esm.js +9 -310
  82. package/dist/jsx.esm.js.map +1 -0
  83. package/dist/react.cjs.js +117 -0
  84. package/dist/react.cjs.js.map +1 -0
  85. package/dist/react.esm.js +115 -0
  86. package/dist/react.esm.js.map +1 -0
  87. package/dist/shims/globalthis.cjs +20 -0
  88. package/dist/sygnal.min.js +2 -1
  89. package/dist/sygnal.min.js.map +1 -0
  90. package/dist/ui-combobox.cjs.js +169 -0
  91. package/dist/ui-combobox.cjs.js.map +1 -0
  92. package/dist/ui-combobox.esm.js +148 -0
  93. package/dist/ui-combobox.esm.js.map +1 -0
  94. package/dist/ui-menu.cjs.js +87 -0
  95. package/dist/ui-menu.cjs.js.map +1 -0
  96. package/dist/ui-menu.esm.js +66 -0
  97. package/dist/ui-menu.esm.js.map +1 -0
  98. package/dist/ui-select.cjs.js +104 -0
  99. package/dist/ui-select.cjs.js.map +1 -0
  100. package/dist/ui-select.esm.js +83 -0
  101. package/dist/ui-select.esm.js.map +1 -0
  102. package/dist/ui.cjs.js +738 -0
  103. package/dist/ui.cjs.js.map +1 -0
  104. package/dist/ui.esm.js +727 -0
  105. package/dist/ui.esm.js.map +1 -0
  106. package/dist/vike/ClientOnly.cjs.js +5 -14
  107. package/dist/vike/ClientOnly.cjs.js.map +1 -0
  108. package/dist/vike/ClientOnly.mjs +5 -14
  109. package/dist/vike/ClientOnly.mjs.map +1 -0
  110. package/dist/vike/{+config.js → config/+config.js} +10 -1
  111. package/dist/vike/config/+config.js.map +1 -0
  112. package/dist/vike/config/package.json +6 -0
  113. package/dist/vike/onRenderClient.cjs.js +166 -95
  114. package/dist/vike/onRenderClient.cjs.js.map +1 -0
  115. package/dist/vike/onRenderClient.mjs +166 -95
  116. package/dist/vike/onRenderClient.mjs.map +1 -0
  117. package/dist/vike/onRenderHtml.cjs.js +60 -24
  118. package/dist/vike/onRenderHtml.cjs.js.map +1 -0
  119. package/dist/vike/onRenderHtml.mjs +61 -25
  120. package/dist/vike/onRenderHtml.mjs.map +1 -0
  121. package/dist/vite/plugin.cjs.js +926 -65
  122. package/dist/vite/plugin.cjs.js.map +1 -0
  123. package/dist/vite/plugin.mjs +925 -65
  124. package/dist/vite/plugin.mjs.map +1 -0
  125. package/dist/zag.cjs.js +408 -0
  126. package/dist/zag.cjs.js.map +1 -0
  127. package/dist/zag.esm.js +405 -0
  128. package/dist/zag.esm.js.map +1 -0
  129. package/llms.txt +313 -0
  130. package/package.json +109 -16
  131. package/src/astro/client.ts +40 -18
  132. package/src/astro/index.d.ts +48 -1
  133. package/src/astro/index.ts +106 -9
  134. package/src/astro/server.ts +11 -3
  135. package/src/collection.ts +6 -90
  136. package/src/core/actions.ts +134 -0
  137. package/src/core/cell.ts +202 -0
  138. package/src/core/debug.ts +23 -0
  139. package/src/core/define.ts +242 -0
  140. package/src/core/hooks.ts +314 -0
  141. package/src/core/hosts/collection.ts +325 -0
  142. package/src/core/hosts/switchable.ts +146 -0
  143. package/src/core/instance.ts +592 -0
  144. package/src/core/markers/clientonly.ts +13 -0
  145. package/src/core/markers/lazy.ts +40 -0
  146. package/src/core/markers/portal.ts +96 -0
  147. package/src/core/markers/suspense.ts +41 -0
  148. package/src/core/markers/transition.ts +70 -0
  149. package/src/core/registry.ts +25 -0
  150. package/src/core/runtime.ts +573 -0
  151. package/src/core/statics.ts +104 -0
  152. package/src/core/teardown.ts +60 -0
  153. package/src/core/view.ts +37 -0
  154. package/src/cycle/dom/DocumentDOMSource.ts +13 -8
  155. package/src/cycle/dom/ElementFinder.ts +7 -9
  156. package/src/cycle/dom/EventDelegator.ts +174 -223
  157. package/src/cycle/dom/IsolateModule.ts +65 -35
  158. package/src/cycle/dom/MainDOMSource.ts +13 -2
  159. package/src/cycle/dom/PriorityQueue.ts +4 -2
  160. package/src/cycle/dom/SymbolTree.ts +18 -47
  161. package/src/cycle/dom/classNameModule.ts +76 -0
  162. package/src/cycle/dom/controlledInputModule.ts +50 -0
  163. package/src/cycle/dom/enrichEventStream.ts +29 -45
  164. package/src/cycle/dom/fragment.ts +7 -0
  165. package/src/cycle/dom/isolate.ts +30 -27
  166. package/src/cycle/dom/makeDOMDriver.ts +115 -57
  167. package/src/cycle/dom/mockDOMSource.ts +83 -9
  168. package/src/cycle/dom/modules.ts +21 -4
  169. package/src/cycle/dom/propsModule.ts +65 -0
  170. package/src/cycle/dom/selectModule.ts +22 -17
  171. package/src/cycle/dom/snabbdom.ts +1 -6
  172. package/src/cycle/dom/thunk.ts +3 -0
  173. package/src/cycle/dom/utils.ts +98 -0
  174. package/src/cycle/dom/viewTransition.ts +43 -0
  175. package/src/cycle/state/StateSource.ts +20 -4
  176. package/src/cycle/state/objIsEqual.ts +50 -0
  177. package/src/cycle/state/types.ts +0 -10
  178. package/src/defineComponent.ts +34 -0
  179. package/src/devtools.d.ts +151 -0
  180. package/src/devtools.ts +34 -0
  181. package/src/element.d.ts +59 -0
  182. package/src/element.ts +235 -0
  183. package/src/extra/backoff.ts +9 -0
  184. package/src/extra/behaviors.ts +169 -0
  185. package/src/extra/browserSignals.ts +23 -0
  186. package/src/extra/browserSources.ts +326 -0
  187. package/src/extra/command.ts +3 -3
  188. package/src/extra/controls.ts +47 -0
  189. package/src/extra/copyAsTest.ts +286 -0
  190. package/src/extra/devtools.ts +109 -31
  191. package/src/extra/devtoolsActions.ts +379 -0
  192. package/src/extra/devtoolsHook.ts +9 -0
  193. package/src/extra/devtoolsNext.ts +83 -0
  194. package/src/extra/diagnostics/checks/actionLog.ts +108 -0
  195. package/src/extra/diagnostics/checks/behaviors.ts +49 -0
  196. package/src/extra/diagnostics/checks/browserSources.ts +96 -0
  197. package/src/extra/diagnostics/checks/collections.ts +67 -0
  198. package/src/extra/diagnostics/checks/controls.ts +148 -0
  199. package/src/extra/diagnostics/checks/dataset.ts +57 -0
  200. package/src/extra/diagnostics/checks/dom.ts +250 -0
  201. package/src/extra/diagnostics/checks/elementCommands.ts +175 -0
  202. package/src/extra/diagnostics/checks/events.ts +83 -0
  203. package/src/extra/diagnostics/checks/fetch.ts +135 -0
  204. package/src/extra/diagnostics/checks/forms.ts +163 -0
  205. package/src/extra/diagnostics/checks/index.ts +202 -0
  206. package/src/extra/diagnostics/checks/inspect.ts +427 -0
  207. package/src/extra/diagnostics/checks/next.ts +417 -0
  208. package/src/extra/diagnostics/checks/persist.ts +54 -0
  209. package/src/extra/diagnostics/checks/props.ts +56 -0
  210. package/src/extra/diagnostics/checks/public.d.ts +302 -0
  211. package/src/extra/diagnostics/checks/replies.ts +128 -0
  212. package/src/extra/diagnostics/checks/router.ts +100 -0
  213. package/src/extra/diagnostics/checks/rxjsHints.ts +88 -0
  214. package/src/extra/diagnostics/checks/shared.ts +203 -0
  215. package/src/extra/diagnostics/checks/shorthand.ts +115 -0
  216. package/src/extra/diagnostics/checks/sortable.ts +129 -0
  217. package/src/extra/diagnostics/checks/state.ts +148 -0
  218. package/src/extra/diagnostics/checks/statics.ts +48 -0
  219. package/src/extra/diagnostics/checks/strict.ts +55 -0
  220. package/src/extra/diagnostics/checks/timers.ts +80 -0
  221. package/src/extra/diagnostics/checks/viewTransitions.ts +47 -0
  222. package/src/extra/diagnostics/checks/virtual.ts +73 -0
  223. package/src/extra/diagnostics/checks/widgets.ts +109 -0
  224. package/src/extra/diagnostics/checks/wiring.ts +121 -0
  225. package/src/extra/diagnostics/codes.ts +477 -0
  226. package/src/extra/diagnostics/index.ts +353 -0
  227. package/src/extra/diagnostics/legacy.ts +76 -0
  228. package/src/extra/driverFactories.ts +172 -60
  229. package/src/extra/elementCommands.ts +53 -0
  230. package/src/extra/eventDriver.ts +4 -0
  231. package/src/extra/fetchDriver.ts +519 -0
  232. package/src/extra/flatten.ts +75 -0
  233. package/src/extra/focusWithin.ts +27 -0
  234. package/src/extra/form.ts +268 -0
  235. package/src/extra/formHelpers.ts +139 -0
  236. package/src/extra/head.ts +106 -0
  237. package/src/extra/hmr.ts +2 -3
  238. package/src/extra/owned.ts +24 -0
  239. package/src/extra/pager.ts +50 -0
  240. package/src/extra/persist.ts +133 -0
  241. package/src/extra/pwa.ts +1 -1
  242. package/src/extra/queryCache.ts +107 -0
  243. package/src/extra/reducers.ts +13 -6
  244. package/src/extra/reduxDevtools.ts +92 -0
  245. package/src/extra/replies.ts +54 -0
  246. package/src/extra/router.ts +323 -0
  247. package/src/extra/run.ts +94 -210
  248. package/src/extra/selection.ts +76 -0
  249. package/src/extra/socketDriver.ts +265 -0
  250. package/src/extra/sortable.ts +377 -0
  251. package/src/extra/ssr.ts +427 -160
  252. package/src/extra/standardSchema.ts +27 -0
  253. package/src/extra/testing.ts +3158 -190
  254. package/src/extra/timers.ts +95 -0
  255. package/src/extra/undo.ts +261 -0
  256. package/src/extra/viewTransitions.ts +38 -0
  257. package/src/extra/virtual.ts +513 -0
  258. package/src/extra/widget.ts +201 -0
  259. package/src/extra/xstreamExtras.ts +269 -0
  260. package/src/index.d.ts +3012 -87
  261. package/src/index.ts +32 -10
  262. package/src/jsx-runtime.ts +15 -1
  263. package/src/jsx.ts +2 -1
  264. package/src/lazy.ts +50 -7
  265. package/src/portal.ts +3 -1
  266. package/src/pragma/index.ts +262 -134
  267. package/src/react-peers.d.ts +5 -0
  268. package/src/react.d.ts +43 -0
  269. package/src/react.ts +110 -0
  270. package/src/shared.ts +48 -0
  271. package/src/slot.ts +1 -1
  272. package/src/suspense.ts +3 -1
  273. package/src/switchable.ts +6 -119
  274. package/src/transition.ts +3 -1
  275. package/src/ui/accordion.ts +64 -0
  276. package/src/ui/dialog.ts +129 -0
  277. package/src/ui/disclosure.ts +35 -0
  278. package/src/ui/popover.ts +45 -0
  279. package/src/ui/shared.ts +94 -0
  280. package/src/ui/tabs.ts +82 -0
  281. package/src/ui/toaster.ts +198 -0
  282. package/src/ui/tooltip.ts +70 -0
  283. package/src/ui/zag/combobox.ts +115 -0
  284. package/src/ui/zag/menu.ts +40 -0
  285. package/src/ui/zag/select.ts +54 -0
  286. package/src/ui/zag/shared.ts +43 -0
  287. package/src/ui-combobox.d.ts +20 -0
  288. package/src/ui-combobox.ts +6 -0
  289. package/src/ui-menu.d.ts +30 -0
  290. package/src/ui-menu.ts +6 -0
  291. package/src/ui-select.d.ts +17 -0
  292. package/src/ui-select.ts +6 -0
  293. package/src/ui-zag-types.d.ts +42 -0
  294. package/src/ui.d.ts +208 -0
  295. package/src/ui.ts +15 -0
  296. package/src/vike/+config.ts +9 -1
  297. package/src/vike/ClientOnly.ts +1 -1
  298. package/src/vike/onRenderClient.ts +144 -96
  299. package/src/vike/onRenderHtml.ts +69 -26
  300. package/src/vike/types.ts +27 -4
  301. package/src/vite/globalthis-shim.ts +18 -0
  302. package/src/vite/globalthis.ts +56 -0
  303. package/src/vite/plugin.d.ts +114 -2
  304. package/src/vite/plugin.ts +920 -56
  305. package/src/zag.d.ts +64 -0
  306. package/src/zag.ts +238 -0
  307. package/dist/astro/astro/client.d.ts +0 -5
  308. package/dist/astro/astro/index.d.ts +0 -16
  309. package/dist/astro/astro/server.d.ts +0 -12
  310. package/dist/astro/client.d.ts +0 -5
  311. package/dist/astro/client.esm.js +0 -4665
  312. package/dist/astro/collection.d.ts +0 -12
  313. package/dist/astro/component.d.ts +0 -22
  314. package/dist/astro/cycle/dom/BodyDOMSource.d.ts +0 -10
  315. package/dist/astro/cycle/dom/DOMSource.d.ts +0 -11
  316. package/dist/astro/cycle/dom/DocumentDOMSource.d.ts +0 -12
  317. package/dist/astro/cycle/dom/ElementFinder.d.ts +0 -8
  318. package/dist/astro/cycle/dom/EventDelegator.d.ts +0 -34
  319. package/dist/astro/cycle/dom/IsolateModule.d.ts +0 -23
  320. package/dist/astro/cycle/dom/MainDOMSource.d.ts +0 -32
  321. package/dist/astro/cycle/dom/PriorityQueue.d.ts +0 -7
  322. package/dist/astro/cycle/dom/ScopeChecker.d.ts +0 -9
  323. package/dist/astro/cycle/dom/SymbolTree.d.ts +0 -9
  324. package/dist/astro/cycle/dom/VNodeWrapper.d.ts +0 -8
  325. package/dist/astro/cycle/dom/enrichEventStream.d.ts +0 -24
  326. package/dist/astro/cycle/dom/fromEvent.d.ts +0 -6
  327. package/dist/astro/cycle/dom/index.d.ts +0 -8
  328. package/dist/astro/cycle/dom/isolate.d.ts +0 -9
  329. package/dist/astro/cycle/dom/makeDOMDriver.d.ts +0 -11
  330. package/dist/astro/cycle/dom/mockDOMSource.d.ts +0 -17
  331. package/dist/astro/cycle/dom/modules.d.ts +0 -5
  332. package/dist/astro/cycle/dom/snabbdom.d.ts +0 -22
  333. package/dist/astro/cycle/dom/styleModule.d.ts +0 -13
  334. package/dist/astro/cycle/dom/thunk.d.ts +0 -11
  335. package/dist/astro/cycle/dom/utils.d.ts +0 -8
  336. package/dist/astro/cycle/isolate/index.d.ts +0 -42
  337. package/dist/astro/cycle/run/adapt.d.ts +0 -6
  338. package/dist/astro/cycle/run/index.d.ts +0 -36
  339. package/dist/astro/cycle/run/internals.d.ts +0 -8
  340. package/dist/astro/cycle/run/types.d.ts +0 -49
  341. package/dist/astro/cycle/state/Collection.d.ts +0 -29
  342. package/dist/astro/cycle/state/StateSource.d.ts +0 -20
  343. package/dist/astro/cycle/state/index.d.ts +0 -5
  344. package/dist/astro/cycle/state/pickCombine.d.ts +0 -3
  345. package/dist/astro/cycle/state/pickMerge.d.ts +0 -3
  346. package/dist/astro/cycle/state/types.d.ts +0 -18
  347. package/dist/astro/cycle/state/withState.d.ts +0 -17
  348. package/dist/astro/extra/classes.d.ts +0 -7
  349. package/dist/astro/extra/devtools.d.ts +0 -78
  350. package/dist/astro/extra/dragDriver.d.ts +0 -20
  351. package/dist/astro/extra/driverFactories.d.ts +0 -12
  352. package/dist/astro/extra/eventDriver.d.ts +0 -9
  353. package/dist/astro/extra/exactState.d.ts +0 -1
  354. package/dist/astro/extra/hmr.d.ts +0 -17
  355. package/dist/astro/extra/logDriver.d.ts +0 -2
  356. package/dist/astro/extra/processDrag.d.ts +0 -19
  357. package/dist/astro/extra/processForm.d.ts +0 -15
  358. package/dist/astro/extra/run.d.ts +0 -13
  359. package/dist/astro/extra/xstreamCompat.d.ts +0 -5
  360. package/dist/astro/index.d.ts +0 -19
  361. package/dist/astro/index.esm.js +0 -27
  362. package/dist/astro/jsx-dev-runtime.d.ts +0 -1
  363. package/dist/astro/jsx-runtime.d.ts +0 -4
  364. package/dist/astro/jsx.d.ts +0 -2
  365. package/dist/astro/pragma/fn.d.ts +0 -7
  366. package/dist/astro/pragma/index.d.ts +0 -7
  367. package/dist/astro/pragma/is.d.ts +0 -10
  368. package/dist/astro/server.d.ts +0 -12
  369. package/dist/astro/server.esm.js +0 -25
  370. package/dist/astro/switchable.d.ts +0 -7
  371. package/dist/collection.d.ts +0 -12
  372. package/dist/component.d.ts +0 -22
  373. package/dist/cycle/dom/BodyDOMSource.d.ts +0 -10
  374. package/dist/cycle/dom/DOMSource.d.ts +0 -11
  375. package/dist/cycle/dom/DocumentDOMSource.d.ts +0 -12
  376. package/dist/cycle/dom/ElementFinder.d.ts +0 -8
  377. package/dist/cycle/dom/EventDelegator.d.ts +0 -34
  378. package/dist/cycle/dom/IsolateModule.d.ts +0 -23
  379. package/dist/cycle/dom/MainDOMSource.d.ts +0 -32
  380. package/dist/cycle/dom/PriorityQueue.d.ts +0 -7
  381. package/dist/cycle/dom/ScopeChecker.d.ts +0 -9
  382. package/dist/cycle/dom/SymbolTree.d.ts +0 -9
  383. package/dist/cycle/dom/VNodeWrapper.d.ts +0 -8
  384. package/dist/cycle/dom/enrichEventStream.d.ts +0 -24
  385. package/dist/cycle/dom/fromEvent.d.ts +0 -6
  386. package/dist/cycle/dom/index.d.ts +0 -8
  387. package/dist/cycle/dom/isolate.d.ts +0 -9
  388. package/dist/cycle/dom/makeDOMDriver.d.ts +0 -11
  389. package/dist/cycle/dom/mockDOMSource.d.ts +0 -17
  390. package/dist/cycle/dom/modules.d.ts +0 -5
  391. package/dist/cycle/dom/snabbdom.d.ts +0 -22
  392. package/dist/cycle/dom/styleModule.d.ts +0 -13
  393. package/dist/cycle/dom/thunk.d.ts +0 -11
  394. package/dist/cycle/dom/utils.d.ts +0 -8
  395. package/dist/cycle/isolate/index.d.ts +0 -42
  396. package/dist/cycle/run/adapt.d.ts +0 -6
  397. package/dist/cycle/run/index.d.ts +0 -36
  398. package/dist/cycle/run/internals.d.ts +0 -8
  399. package/dist/cycle/run/types.d.ts +0 -49
  400. package/dist/cycle/state/Collection.d.ts +0 -29
  401. package/dist/cycle/state/StateSource.d.ts +0 -20
  402. package/dist/cycle/state/index.d.ts +0 -5
  403. package/dist/cycle/state/pickCombine.d.ts +0 -3
  404. package/dist/cycle/state/pickMerge.d.ts +0 -3
  405. package/dist/cycle/state/types.d.ts +0 -18
  406. package/dist/cycle/state/withState.d.ts +0 -17
  407. package/dist/extra/classes.d.ts +0 -7
  408. package/dist/extra/devtools.d.ts +0 -78
  409. package/dist/extra/dragDriver.d.ts +0 -20
  410. package/dist/extra/driverFactories.d.ts +0 -12
  411. package/dist/extra/eventDriver.d.ts +0 -9
  412. package/dist/extra/exactState.d.ts +0 -1
  413. package/dist/extra/hmr.d.ts +0 -17
  414. package/dist/extra/logDriver.d.ts +0 -2
  415. package/dist/extra/processDrag.d.ts +0 -19
  416. package/dist/extra/processForm.d.ts +0 -15
  417. package/dist/extra/run.d.ts +0 -13
  418. package/dist/extra/xstreamCompat.d.ts +0 -5
  419. package/dist/jsx-dev-runtime.d.ts +0 -1
  420. package/dist/jsx-dev-runtime.js +0 -224
  421. package/dist/jsx-runtime.d.ts +0 -4
  422. package/dist/jsx-runtime.js +0 -224
  423. package/dist/jsx.d.ts +0 -2
  424. package/dist/pragma/fn.d.ts +0 -7
  425. package/dist/pragma/index.d.ts +0 -7
  426. package/dist/pragma/is.d.ts +0 -10
  427. package/dist/switchable.d.ts +0 -7
  428. package/dist/vike/+config.cjs.js +0 -65
  429. package/src/component.ts +0 -2256
  430. package/src/cycle/isolate/index.ts +0 -196
  431. package/src/cycle/run/index.ts +0 -151
  432. package/src/cycle/run/internals.ts +0 -143
  433. package/src/cycle/state/Collection.ts +0 -174
  434. package/src/cycle/state/index.ts +0 -5
  435. package/src/cycle/state/pickCombine.ts +0 -167
  436. package/src/cycle/state/pickMerge.ts +0 -122
  437. package/src/cycle/state/withState.ts +0 -47
  438. package/src/pragma/fn.ts +0 -55
@@ -0,0 +1,257 @@
1
+ <!-- Generated from docs/src/content/docs/guide by scripts/copy-guides.mjs; online: https://sygnal.js.org/guide/ -->
2
+ # Accessibility
3
+
4
+ `sygnal-check` has an accessibility lane, SYG701 to SYG708 and SYG722. It finds the markup mistakes that lock keyboard and screen-reader users out without any visible sign: a clickable `<div>`, a field without a label, an icon button with no name. Like the other checks, it reads your source, so it works the same in the editor, in CI and in the dev server.
5
+
6
+ ## Severity
7
+
8
+ - **Warning** by default: `npx --no-install sygnal-check` prints the findings and exits with code 1, like any other warning.
9
+ - **Still a warning under `--strict`.** Strict mode checks the canonical forms; it doesn't turn markup that already shipped into errors when you upgrade.
10
+ - **Error** when you ask for it: `sygnal-check --a11y=error`, `check(paths, { a11y: 'error' })`, or `check: { a11y: 'error' }` in the [Vite plugin](https://sygnal.js.org/integration/bundler-config/#plugin-options), where errors also open Vite's error overlay. Use it in CI once an app is clean.
11
+ - **On in the dev server**: the Vite plugin runs `sygnal-check` when it is installed, so the findings appear in the terminal and the browser console as you edit.
12
+
13
+ The checks are static only: markup that is built at runtime (spread props, a dynamic `type` or `role`) is skipped rather than guessed at.
14
+
15
+ To keep one finding on purpose, put `// sygnal-ignore SYG70x` on the line above it, or at the end of the line (see [Suppressing a finding](#suppressing-a-finding)).
16
+
17
+ ## The rules
18
+
19
+ Each example shows the flagged form as a comment, then the fix.
20
+
21
+ ### SYG701: click listener on a non-interactive element
22
+
23
+ The intent listens for clicks on a `<div>`, `<span>`, `<li>`, `<p>`, `<img>` or similar without a `role` and `tabIndex`. Mouse users can click it; keyboard users can't reach it, and screen readers don't announce it as something to activate. Render a button instead:
24
+
25
+ ```jsx
26
+ // Flagged:
27
+ // function Card({ state }) { return <div className="card">{state.title}</div> }
28
+
29
+ function Card({ state }) {
30
+ return <button type="button" className="card">{state.title}</button>
31
+ }
32
+
33
+ Card.intent = ({ DOM }) => ({ OPEN: DOM.click('.card') })
34
+ Card.model = { OPEN: (state) => ({ ...state, open: true }) }
35
+ ```
36
+
37
+ If the element must stay a `<div>`, give it `role="button"` and `tabIndex={0}`, and handle Enter and Space too:
38
+
39
+ ```jsx
40
+ import { xs } from 'sygnal'
41
+
42
+ function Card({ state }) {
43
+ return <div className="card" role="button" tabIndex={0}>{state.title}</div>
44
+ }
45
+
46
+ Card.intent = ({ DOM }) => ({
47
+ OPEN: xs.merge(
48
+ DOM.click('.card'),
49
+ DOM.keydown('.card').filter(e => e.key === 'Enter' || e.key === ' '),
50
+ ),
51
+ })
52
+ Card.model = { OPEN: (state) => ({ ...state, open: true }) }
53
+ ```
54
+
55
+ The rule applies to a [control](https://sygnal.js.org/guide/controls/)'s element too (`controls({ Card: 'div' })` listened to for clicks: declare it as `'button'`). An element that contains a button, link or form field isn't flagged, since a click on those bubbles up to the listener.
56
+
57
+ ### SYG702: form field without a label
58
+
59
+ An `<input>`, `<select>` or `<textarea>` has no accessible name, so a screen reader announces only "edit text". Wrap it in a `<label>`, link a `<label for>` to its `id` (with [`uid()`](#ids-with-uid), below), or, for a field with no visible label, add `aria-label`:
60
+
61
+ ```jsx
62
+ // Flagged: <input className="search" type="search" value={state.query} />
63
+
64
+ function Search({ state }) {
65
+ return <input className="search" type="search" aria-label="Search tasks" value={state.query} />
66
+ }
67
+
68
+ Search.initialState = { query: '' }
69
+ Search.intent = ({ DOM }) => ({ QUERY: DOM.input('.search').value() })
70
+ Search.model = { QUERY: (state, query) => ({ ...state, query }) }
71
+ ```
72
+
73
+ A `placeholder` counts as a last-resort name, but it disappears as soon as the user types; prefer a visible label.
74
+
75
+ ### SYG703: image without alt text
76
+
77
+ Every `<img>` needs `alt`. Describe what the image shows, or use `alt=""` for a decorative image, which screen readers then skip:
78
+
79
+ ```jsx
80
+ // Flagged: <img src={state.avatarUrl} />
81
+
82
+ function Profile({ state }) {
83
+ return (
84
+ <div>
85
+ <img src={state.avatarUrl} alt={state.name} />
86
+ <img src="/divider.svg" alt="" />
87
+ </div>
88
+ )
89
+ }
90
+ ```
91
+
92
+ ### SYG704: link without href used as a button
93
+
94
+ An `<a>` without `href` is not focusable and is announced as plain text. Use a button for an action, and keep `<a href>` for navigation:
95
+
96
+ ```jsx
97
+ // Flagged: <a className="more">Show more</a> with DOM.click('.more')
98
+
99
+ function Comments({ state }) {
100
+ return (
101
+ <div>
102
+ <p>{state.shown} of {state.total} comments</p>
103
+ <button type="button" className="more">Show more</button>
104
+ </div>
105
+ )
106
+ }
107
+
108
+ Comments.intent = ({ DOM }) => ({ MORE: DOM.click('.more') })
109
+ Comments.model = { MORE: (state) => ({ ...state, shown: Math.min(state.total, state.shown + 10) }) }
110
+ ```
111
+
112
+ ### SYG705: button without an accessible name
113
+
114
+ A button whose only content is an icon is announced as just "button". Add `aria-label`, and hide the icon from screen readers:
115
+
116
+ ```jsx
117
+ // Flagged: <button type="button" className="close"><span>×</span></button>, no name for "×"...
118
+ // ...and an icon with no text: <button type="button" className="close"><i className="icon-x" /></button>
119
+
120
+ function Banner({ state }) {
121
+ return (
122
+ <div role="status">
123
+ {state.message}
124
+ <button type="button" className="close" aria-label="Dismiss">
125
+ <i className="icon-x" aria-hidden="true" />
126
+ </button>
127
+ </div>
128
+ )
129
+ }
130
+
131
+ Banner.intent = ({ DOM }) => ({ DISMISS: DOM.click('.close') })
132
+ Banner.model = { DISMISS: (state) => ({ ...state, message: '' }) }
133
+ ```
134
+
135
+ Only literal content is checked: a button whose children include an expression (`{state.label}`) or a component isn't flagged.
136
+
137
+ ### SYG706: positive tabIndex
138
+
139
+ `tabIndex={1}` and higher move the element ahead of everything else in the Tab order. Use `tabIndex={0}` (focusable in document order) or `tabIndex={-1}` (focusable only from code), and change the markup order to change the Tab order:
140
+
141
+ ```jsx
142
+ // Flagged: <div className="preview" tabIndex={1}>…</div>
143
+
144
+ function Preview({ state }) {
145
+ return <div className="preview" tabIndex={0} aria-label="Preview">{state.html}</div>
146
+ }
147
+ ```
148
+
149
+ ### SYG707: unknown ARIA attribute or invalid role
150
+
151
+ A misspelled `aria-*` attribute, or a `role` that isn't a WAI-ARIA role (including abstract roles such as `widget`), is ignored by assistive technology. The message suggests the closest valid name:
152
+
153
+ ```jsx
154
+ // Flagged: aria-labeledby (a typo), role="widget" (abstract)
155
+
156
+ function Tabs({ state }) {
157
+ return (
158
+ <div>
159
+ <h2 id="tabs-title">Settings</h2>
160
+ <div role="tablist" aria-labelledby="tabs-title">
161
+ {state.tabs.map(tab => <button type="button" role="tab" aria-selected={String(tab === state.current)}>{tab}</button>)}
162
+ </div>
163
+ </div>
164
+ )
165
+ }
166
+ ```
167
+
168
+ ### SYG708: label or ARIA reference to an id that isn't rendered
169
+
170
+ A `<label for>`, `aria-describedby` or `aria-labelledby` names an id that no element has, so the label is attached to nothing. This usually comes from an id renamed on one side only. Inside a component, use `uid()` on both sides:
171
+
172
+ ```jsx
173
+ // Flagged: <input id="email" … /> with <label for="e-mail">
174
+
175
+ function EmailField({ state, uid }) {
176
+ return (
177
+ <p>
178
+ <label for={uid('email')}>Email</label>
179
+ <input id={uid('email')} type="email" className="email" value={state.email} aria-describedby={uid('email-error')} />
180
+ {state.error && <span id={uid('email-error')}>{state.error}</span>}
181
+ </p>
182
+ )
183
+ }
184
+
185
+ EmailField.initialState = { email: '', error: '' }
186
+ EmailField.intent = ({ DOM }) => ({ EMAIL: DOM.input('.email').value() })
187
+ EmailField.model = { EMAIL: (state, email) => ({ ...state, email, error: email.includes('@') ? '' : 'Enter an email address' }) }
188
+ ```
189
+
190
+ A `uid('x')` reference needs an element in the same component with `id={uid('x')}`; a literal id may be anywhere in the checked files.
191
+
192
+ ### SYG722: Menu, Select or Combobox without an accessible name
193
+
194
+ A [Menu](./ui/menu.md), [Select](./ui/select.md) or [Combobox](./ui/combobox.md) without `label`, `aria-label` or `aria-labelledby`. The part names its control from them (Menu's trigger text is its `label`), so without one the menu button or combobox is announced without a name. Give it a visible `label`; when the label is elsewhere, point at it with `aria-labelledby`; for an icon-only menu trigger, use `aria-label`:
195
+
196
+ ```jsx
197
+ import { Select } from 'sygnal/ui/select'
198
+ import { Menu } from 'sygnal/ui/menu'
199
+
200
+ // Flagged: <Select className="size" items={SIZES} />
201
+
202
+ function Sizes({ state }) {
203
+ return (
204
+ <div>
205
+ <Select className="size" label="Size" items={['S', 'M', 'L']} value={state.size} />
206
+ <Menu className="more" label="⋯" aria-label="More actions" items={['Duplicate', 'Delete']} />
207
+ </div>
208
+ )
209
+ }
210
+
211
+ Sizes.initialState = { size: null }
212
+ Sizes.intent = ({ DOM }) => ({ SIZE: DOM.select('.size').events('value-change').detail() })
213
+ Sizes.model = { SIZE: (state, size) => ({ ...state, size }) }
214
+ ```
215
+
216
+ ## ARIA values in JSX
217
+
218
+ An `aria-*` attribute set to `true` renders `"true"`. Set to `false`, it renders `"false"` on the ARIA states that have a false value (`aria-expanded`, `aria-hidden`, `aria-pressed`, `aria-checked`, `aria-selected`, `aria-disabled`, `aria-invalid`, `aria-current`, `aria-haspopup`, `aria-busy`, `aria-modal`, `aria-required`, `aria-readonly`, `aria-multiline`, `aria-multiselectable`, `aria-atomic`, `aria-grabbed`); on any other `aria-*` (a label, an id reference, a number) `false` removes the attribute, so `aria-describedby={hasError && uid('error')}` doesn't announce "false". `null` and `undefined` remove it. Passed to a component, `role`, `for`, `tabindex` and `aria-*` are ordinary props: the component gets the value as written.
219
+
220
+ ## Ids with uid()
221
+
222
+ Labels and ARIA references need ids, and ids must be unique on the page, which a literal id isn't once the component is rendered twice. Every view gets a `uid` prop: `uid()` is an id for this component instance, and `uid('email')` an id derived from it. They are stable across renders and the same on the server and the client, so they are safe with [SSR](./ssr.md#stable-ids-uid). See [Forms](./inputs.md#labels-and-ids-uid) for a full example. The checks understand `uid()`: SYG702 and SYG708 match `for={uid('email')}` with `id={uid('email')}`.
223
+
224
+ ## Suppressing a finding
225
+
226
+ A comment on the line above (or at the end of the line) suppresses one finding:
227
+
228
+ ```jsx
229
+ import { xs } from 'sygnal'
230
+
231
+ function Dialog({ state }) {
232
+ return (
233
+ <div>
234
+ <div className="backdrop" />
235
+ <div role="dialog" aria-modal="true" aria-label="Settings">{state.body}</div>
236
+ </div>
237
+ )
238
+ }
239
+
240
+ Dialog.intent = ({ DOM }) => ({
241
+ CLOSE: xs.merge(
242
+ // a mouse shortcut: keyboard users close the dialog with Escape
243
+ // sygnal-ignore SYG701
244
+ DOM.click('.backdrop'),
245
+ DOM.keydown('document').key().filter(key => key === 'Escape'),
246
+ ),
247
+ })
248
+ Dialog.model = { CLOSE: { PARENT: () => 'closed' } }
249
+ ```
250
+
251
+ A backdrop that closes a dialog on click is a common case: the dialog itself is reachable, and keyboard users close it with Escape. Without codes, `// sygnal-ignore` suppresses every code on that line. To turn a rule off for the dev server, add it to the Vite plugin's ignore list: `sygnal({ check: { ignore: ['SYG705'] } })`.
252
+
253
+ ## Related
254
+
255
+ - [Error Reference](https://sygnal.js.org/reference/errors/#syg701): every SYG7xx code, its cause and its fix
256
+ - [Diagnostics](https://sygnal.js.org/guide/diagnostics/#the-static-checker): running `sygnal-check`
257
+ - [Forms](./forms.md): validation and server errors; [Inputs, Labels and Focus](./inputs.md): labels, controlled inputs and focus
@@ -0,0 +1,221 @@
1
+ <!-- Generated from docs/src/content/docs/guide by scripts/copy-guides.mjs; online: https://sygnal.js.org/guide/ -->
2
+ # Adapters
3
+
4
+ An adapter turns code written for another model into a Sygnal [widget](./widgets.md) tag: you render it with a class, read its events in the intent with `.detail()`, and send it [element commands](./element-commands.md), as with any widget.
5
+
6
+ - **`fromZag`** (`sygnal/zag`) runs a [Zag.js](https://zagjs.com) state machine and renders its parts with Sygnal JSX. It is what [Menu](./ui/menu.md), [Select](./ui/select.md) and [Combobox](./ui/combobox.md) are built on.
7
+ - **`fromReact`** (`sygnal/react`) renders a React component, or a Preact one through `preact/compat`.
8
+
9
+ **Adapters are an escape hatch.** Reach for one for the component you can't replace, not as a way to build the app. First look for a native answer (`<dialog>`, the Popover API, [`sygnal/ui`](./ui/overview.md)), a [web component](./web-components.md), or a framework-agnostic library through [`defineWidget`](./widgets.md). A React component in a Sygnal app:
10
+
11
+ - **brings its runtime**: React and react-dom add about 68 KB gzipped (Preact through `preact/compat` about 8 KB);
12
+ - **doesn't see your context or providers**: React context, a theme provider, a router or a query client from a React tree don't cross the boundary, and Sygnal's `.context` doesn't reach the component. Pass what it needs as props;
13
+ - **can render outside its host**: a React portal (a modal, a tooltip) goes where React puts it, so Sygnal's `DOM.select` doesn't see events from it; use the component's callbacks instead;
14
+ - **doesn't run in the mock DOM**: test it with `renderComponent(App, { dom: 'real' })` (jsdom or a browser).
15
+
16
+ The libraries are optional peer dependencies of `sygnal`: an app that doesn't import an adapter never installs or ships them. When one is missing, `sygnal/vite` names it ([SYG666](https://sygnal.js.org/reference/errors/#syg666)).
17
+
18
+ ## React components: fromReact
19
+
20
+ ```bash
21
+ npm install react react-dom
22
+ ```
23
+
24
+ The React component here is a small one of our own, written with React's `createElement` (the JSX on these pages is Sygnal's); a component from npm is wrapped the same way:
25
+
26
+ ```js live-file=./Rating.js
27
+ // Rating.js: a React component
28
+ import { createElement as h } from 'react'
29
+
30
+ export function Rating({ value, max, onChange }) {
31
+ return h('span', { role: 'group', 'aria-label': 'Rating' },
32
+ Array.from({ length: max }, (_, i) => h('button', {
33
+ key: i, type: 'button', 'aria-label': `${i + 1} of ${max}`, 'aria-pressed': i < value, onClick: () => onChange(i + 1),
34
+ }, i < value ? '★' : '☆')))
35
+ }
36
+ ```
37
+
38
+ ```jsx live-file=./Stars.js
39
+ // Stars.js
40
+ import { fromReact } from 'sygnal/react'
41
+ import { Rating } from './Rating.js'
42
+
43
+ export const Stars = fromReact(Rating, { events: { rate: 'onChange' } })
44
+ ```
45
+
46
+ ```jsx live
47
+ // Review.jsx
48
+ import { Stars } from './Stars.js'
49
+
50
+ export function Review({ state }) {
51
+ return (
52
+ <div>
53
+ <Stars className="rating" value={state.rating} max={5} />
54
+ <p>{state.rating} of 5</p>
55
+ </div>
56
+ )
57
+ }
58
+
59
+ Review.initialState = { rating: 0 }
60
+ Review.intent = ({ DOM }) => ({
61
+ RATE: DOM.select('.rating').events('rate').detail(),
62
+ })
63
+ Review.model = {
64
+ RATE: (state, rating) => ({ ...state, rating }),
65
+ }
66
+ ```
67
+
68
+ The tag renders a host `<div class="rating">` and a React root inside it. Every render passes the newest props to the component (`value`, `max`); the props are routed between the host and the component:
69
+
70
+ | Props | Go to |
71
+ |---|---|
72
+ | `className`, `class`, `id`, `style`, `attrs`, `tabIndex`, `hidden` | the host only (one tab stop) |
73
+ | `aria-*`, `role`, `title` | the component only: a React control names itself, so `<IconButton aria-label="Delete" />` labels the component's `<button>`, not the generic host `<div>` |
74
+ | `data-*` | both |
75
+ | everything else | the component (`name`, `placeholder`, `lang` and `dir` also go on the host, as for any widget) |
76
+
77
+ List a host prop in `ownProps` to send it to the component instead, or a component-only one in `hostProps` to put it on the host too. A React component that doesn't pass `aria-*` and `role` on to its element (it takes only the props it names) gets no accessible name this way: fix the component to spread them, or list them in `hostProps` (`hostProps: ['role', 'aria-label']`) so the host carries the role and the name. When the component calls `onChange(4)`, the adapter dispatches a `rate` event on the host with `4` as its detail.
78
+
79
+ When the host leaves the page (also inside a shadow root, as with `sygnal/element`'s `shadow: true`), the React root unmounts. A host can also be destroyed while its element stays in the page, for example during a `<Transition>` leave that never gets its `transitionend`. The React root then stays mounted until the element is removed, and for at most 10 seconds, so no root or observer is leaked. A component's cleanup (`useEffect` returns) can therefore run up to 10 seconds after the host was destroyed.
80
+
81
+ | Option | |
82
+ |---|---|
83
+ | `events` | `{ eventName: 'onCallback' }`: each callback prop becomes a DOM event with that name. An array (`['onChange']`) uses the callback's own name as the event name. The detail is the callback's argument, or an array of them when it gets several. A callback you pass as a prop still runs, first |
84
+ | `props` | `(props) => componentProps`, when the component's props differ from the tag's |
85
+ | `commands` | Element commands, called with `{ root, el, props }` |
86
+ | `tag`, `name`, `fallback`, `hostProps`, `ownProps` | As in [`defineWidget`](./widgets.md#the-definition) |
87
+
88
+ Prefer event names of your own (`'rate'`) to the browser's (`'change'`): the component's own `<input>`s fire native `change` events that bubble out of the host too.
89
+
90
+ ### Preact
91
+
92
+ The adapter uses only `createElement` (from `react`), `createRoot` (from `react-dom/client`) and `flushSync` (from `react-dom`), which `preact/compat` provides. Alias the React packages in the bundler, and the same adapter and the same component code run on Preact:
93
+
94
+ ```js
95
+ // vite.config.js
96
+ import { defineConfig } from 'vite'
97
+ import sygnal from 'sygnal/vite'
98
+
99
+ export default defineConfig({
100
+ plugins: [sygnal()],
101
+ resolve: {
102
+ alias: {
103
+ react: 'preact/compat',
104
+ 'react-dom/client': 'preact/compat/client',
105
+ 'react-dom': 'preact/compat',
106
+ },
107
+ },
108
+ })
109
+ ```
110
+
111
+ ```bash
112
+ npm install preact
113
+ ```
114
+
115
+ ## Zag machines: fromZag
116
+
117
+ [Zag.js](https://zagjs.com) implements UI patterns (menus, comboboxes, date pickers, sliders) as framework-agnostic state machines, with the keyboard and ARIA work done. `fromZag(zag, render, options)` runs one on a widget host:
118
+
119
+ ```bash
120
+ npm install @zag-js/vanilla@~1.45.0 @zag-js/menu@~1.45.0
121
+ ```
122
+
123
+ ```jsx live-file=./Actions.jsx
124
+ // Actions.jsx
125
+ import { fromZag } from 'sygnal/zag'
126
+ import * as menu from '@zag-js/menu'
127
+
128
+ export const Actions = fromZag(menu, (api, props) => (
129
+ <div>
130
+ <button {...api.getTriggerProps()}>{props.label}</button>
131
+ <div {...api.getPositionerProps()}>
132
+ <ul {...api.getContentProps()}>
133
+ {props.items.map((item) => <li {...api.getItemProps({ value: item })}>{item}</li>)}
134
+ </ul>
135
+ </div>
136
+ </div>
137
+ ), {
138
+ name: 'Actions',
139
+ events: { pick: ['onSelect', (details) => details.value] },
140
+ commands: { open: (api) => api.setOpen(true) },
141
+ })
142
+ ```
143
+
144
+ ```jsx live live-height=170
145
+ // Toolbar.jsx
146
+ import { Actions } from './Actions.jsx'
147
+
148
+ export function Toolbar({ state }) {
149
+ return <div><Actions className="actions" label="Edit" items={['cut', 'copy', 'paste']} /><p>{state.last}</p></div>
150
+ }
151
+
152
+ Toolbar.initialState = { last: '' }
153
+ Toolbar.intent = ({ DOM }) => ({
154
+ PICK: DOM.select('.actions').events('pick').detail(),
155
+ })
156
+ Toolbar.model = {
157
+ PICK: (state, last) => ({ ...state, last }),
158
+ }
159
+ ```
160
+
161
+ Zag's parts come unstyled. Style them by the `data-scope` and `data-part` attributes the prop getters put on them:
162
+
163
+ ```css live
164
+ [data-scope="menu"][data-part="content"] { margin: 0; padding: 0.25rem 0; list-style: none; background: Canvas; border: 1px solid GrayText; z-index: 1; }
165
+ [data-scope="menu"][data-part="item"] { padding: 0.25rem 0.75rem; cursor: pointer; }
166
+ [data-scope="menu"][data-part="item"][data-highlighted] { background: Highlight; color: HighlightText; }
167
+ ```
168
+
169
+ - **`zag`** is the machine package as a namespace (`import * as menu from '@zag-js/menu'`): its `machine` and `connect`.
170
+ - **`render(api, props, instance)`** returns one element. `api` is the machine's connected API: spread its prop getters on your elements (`{...api.getTriggerProps()}`); they carry the ARIA attributes, the ids and the event handlers. It runs again whenever the machine's state changes and when the tag gets new props, and the result is patched into the host. Render **plain elements only**: the adapter patches the result itself, outside the component tree, so a Sygnal component, a widget tag or special JSX (`<Transition>`, `<Portal>`, `<Collection>`…) inside it doesn't run. Pass data in through the tag's props, and keep components and widgets around the tag in the view ([SYG669](https://sygnal.js.org/reference/errors/#syg669) in dev).
171
+ - **Props** go to the machine as they are (`open`, `value`, `positioning`...), always the newest ones: a controlled prop such as `open={state.menuOpen}` drives the machine. `options.props(props, instance)` maps them when they differ (Select turns `value: 'm'` into Zag's `['m']`).
172
+ - **`events`**: `{ eventName: 'onCallback' }` dispatches the callback's details object; `{ eventName: ['onCallback', (details) => detail] }` dispatches what the function returns.
173
+ - **`commands`**: `{ name: (api, options, instance) => … }` for `ELEMENT`.
174
+
175
+ Pin Zag's version (`~1.45.0`, all packages the same): the adapter uses `@zag-js/vanilla`'s machine runtime, whose API changes in Zag 2.0.
176
+
177
+ ## Testing
178
+
179
+ In the default mock DOM, an adapter's tag renders its host and nothing runs inside it. `t.widget(selector)` gives the props the view passed and dispatches the adapter's events, so the component's logic can be tested without React or Zag:
180
+
181
+ ```jsx
182
+ import { it, expect } from 'vitest'
183
+ import { renderComponent } from 'sygnal'
184
+ import { Review } from './Review.jsx'
185
+
186
+ it('stores the rating', async () => {
187
+ const t = renderComponent(Review)
188
+ await t.ready()
189
+ t.widget('.rating').dispatch('rate', 4)
190
+ await t.next((state) => state.rating === 4)
191
+ expect(t.widget('.rating').props.value).toBe(4)
192
+ t.dispose()
193
+ })
194
+ ```
195
+
196
+ To run the React component or the machine itself, use `renderComponent(Review, { dom: 'real' })` (in a jsdom test environment), click with `t.query(...)`, and wait for the state with `t.next`. Zag's positioning and list scrolling use `ResizeObserver`, `CSS.escape` and `Element.prototype.scrollTo`, which jsdom lacks: while a `dom: 'real'` test runs, `renderComponent` adds the missing ones (a `ResizeObserver` that observes nothing, a `scrollTo` that does nothing) and removes them when the last test instance is disposed. An implementation the environment already has is kept. Outside `renderComponent` (a test that calls `run()` itself), stub them in a setup file.
197
+
198
+ ## Server rendering
199
+
200
+ `renderToString` renders an adapter's host with its `fallback` inside, like any widget; React and Zag run only in the browser. The client's first render replaces the fallback.
201
+
202
+ ## Size
203
+
204
+ Gzipped, added to a small app:
205
+
206
+ | | Adds |
207
+ |---|---|
208
+ | `fromReact` with React 19 + react-dom | 68 KB (the adapter itself: 0.6 KB) |
209
+ | `fromReact` with `preact/compat` | 8 KB |
210
+ | `fromZag` with Zag's dialog machine | 20 KB (the adapter itself: 2 KB) |
211
+ | [Menu](./ui/menu.md), [Select](./ui/select.md), [Combobox](./ui/combobox.md) | 33–34 KB each, 47 KB for the three |
212
+
213
+ ## Diagnostics
214
+
215
+ | Code | When |
216
+ |---|---|
217
+ | [SYG666](https://sygnal.js.org/reference/errors/#syg666) | An adapter entry is imported, but a package it needs isn't installed (`sygnal/vite`) |
218
+ | [SYG667](https://sygnal.js.org/reference/errors/#syg667) | `fromZag` or `fromReact` got something that isn't a machine package or a component |
219
+ | [SYG669](https://sygnal.js.org/reference/errors/#syg669) | A `fromZag` render returned a Sygnal component, a widget tag or special JSX, which can't run there (dev) |
220
+
221
+ The widget codes ([SYG140–144](./widgets.md#diagnostics), [SYG660–662](https://sygnal.js.org/reference/errors/#syg660)) apply to adapters as to any widget.