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
@@ -1,11 +1,9 @@
1
1
  import {Driver} from '../run/types';
2
- import {init, Module, Options as SnabbdomOptions, VNode, toVNode} from './snabbdom';
2
+ import {init, Module, Options as SnabbdomOptions, VNode} from './snabbdom';
3
3
  import xs, {Stream, Listener} from 'xstream';
4
- import concat from 'xstream/extra/concat.js';
5
- import sampleCombine from 'xstream/extra/sampleCombine.js';
6
4
  import {MainDOMSource} from './MainDOMSource';
7
5
  import {VNodeWrapper} from './VNodeWrapper';
8
- import {getValidNode, checkValidContainer} from './utils';
6
+ import {getValidNode, checkValidContainer, POKE, flat} from './utils';
9
7
  import defaultModules from './modules';
10
8
  import {IsolateModule} from './IsolateModule';
11
9
  import {EventDelegator} from './EventDelegator';
@@ -37,17 +35,11 @@ export interface DOMDriverOptions {
37
35
  snabbdomOptions?: SnabbdomOptions;
38
36
  }
39
37
 
40
- function dropCompletion<T>(input: Stream<T>): Stream<T> {
41
- return xs.merge(input, xs.never());
42
- }
43
-
44
38
  function unwrapElementFromVNode(vnode: VNode): Element {
45
39
  return vnode.elm as Element;
46
40
  }
47
41
 
48
- function defaultReportSnabbdomError(err: any): void {
49
- (console.error || console.log)(err);
50
- }
42
+ const ERR = 'data-sygnal-error';
51
43
 
52
44
  function makeDOMReady$(): Stream<null> {
53
45
  return xs.create<null>({
@@ -69,11 +61,64 @@ function makeDOMReady$(): Stream<null> {
69
61
  });
70
62
  }
71
63
 
72
- function addRootScope(vnode: VNode): VNode {
73
- vnode.data = vnode.data || {};
74
- (vnode.data as any).isolate = [];
75
- return vnode;
76
- }
64
+ // G-456 (D217): the app's first patch adopts markup already in the mount point (server HTML, an
65
+ // Astro island, a Vike page). Its old vnode is built from the DOM with the client's vnode as the
66
+ // template, by position (no toVNode): an element with the client's tag takes the client's
67
+ // selector and key (so component roots and Collection items match), and its data is what the
68
+ // modules need to reach the client's from what the server wrote: the dataset (stale keys go:
69
+ // data-sygnal-ssr), the class as the className prop (the className module drops stale classes),
70
+ // and each attribute the client sets as an attribute or a prop, with the server's value (nothing
71
+ // is written again: an iframe's src isn't reloaded). Any other attribute is removed now; so is a
72
+ // style (G-483: the style module writes the client's declarations, so none of the server's
73
+ // stays). A single text child keeps its text node. Whitespace and comments where the client has
74
+ // no text go; any other node that doesn't match stays as a placeholder that snabbdom replaces
75
+ // with the client's node, in place (the lists stay aligned, so the nodes after it are still
76
+ // adopted). Made again that way, as a fresh render makes them: an element whose selector has a
77
+ // class or id (G-486: `h('p.card')`, the Portal placeholder; patching never corrects them), and
78
+ // one whose hook expects a new element (G-485): a create or init hook (a thunk), an insert hook
79
+ // without a postpatch (a Transition's enter, a measured row, the toaster region, a lazy()
80
+ // placeholder), and any insert hook of the user's own (G-521: with a postpatch too; `u` when a
81
+ // ref, autoFocus or widget chained theirs after it; `s` marks a function chainHooks made)
82
+ const adopt = (e: any, v: any): any => {
83
+ // (a hole, `{cond && <X/>}`, has no node: snabbdom skips it. G-481: no fragment either, they
84
+ // are flat by now: their nodes pair with their children in their parent's run)
85
+ const c = (v.children || []).filter((z: any) => z), out: any[] = [];
86
+ for (let j = 0, x = e.firstChild, y: any; x; x = y) {
87
+ const w = c[j], t = x.nodeType, d = w?.data || {}, k = d.hook;
88
+ // a node that doesn't match stays in its place under a selector no vnode has: snabbdom makes
89
+ // the client's node before it and removes it (the lists stay aligned: no other node is paired)
90
+ let n: any = {sel: '', data: {}, elm: x};
91
+ // G-520: client text vnodes next to each other (`Hello, {name}!`) are one server text node:
92
+ // it is split by the client's text when that is its start, so the nodes after stay paired
93
+ t == 3 && c[j + 1]?.text != null && !c[j + 1].sel && w?.text && x.data.startsWith(w.text) && x.splitText(w.text.length);
94
+ y = x.nextSibling;
95
+ if (t == 3 && w && !w.sel && w.text != null) n = {text: x.data, elm: x};
96
+ else if (t == 1 && x.localName == w?.sel && !(k && (k.create || k.init || k.u || k.insert && !(k.postpatch && k.insert.s)))) {
97
+ const p = d.props || {}, a = d.attrs || {}, o: any = {}, at: any = {}, pr: any = {}, f = x.firstChild;
98
+ o.class = 'className';
99
+ for (const m in p) o[m == 'htmlFor' ? 'for' : m.toLowerCase()] = m;
100
+ // G-482: a data-* attribute the client sets as an attribute isn't in the dataset (the
101
+ // dataset module would remove it): it goes, and the attributes module writes it again.
102
+ // G-487: `open` (a <details> the user opened) stays when the client doesn't render it
103
+ for (const {name: m, value: y} of [...x.attributes])
104
+ /^data-/.test(m) ? m in a && x.removeAttribute(m)
105
+ : m in a ? at[m] = y
106
+ : m == 'open' ? 0
107
+ : m in o && !d.ns ? pr[o[m]] = y
108
+ : x.removeAttribute(m);
109
+ // G-484: a textarea's value no longer follows its text, which goes
110
+ x.localName == 'textarea' && (x.value = x.value);
111
+ const s = w.text != null && f?.nodeType == 3 && !f.nextSibling;
112
+ n = {sel: w.sel, key: w.key, data: {dataset: {...x.dataset}, attrs: at, props: pr}, children: s ? undefined : adopt(x, w), text: s ? f.data : undefined, elm: x};
113
+ } else if (t != 1 && (t != 3 || !/\S/.test(x.data))) {
114
+ x.remove();
115
+ continue;
116
+ }
117
+ out.push(n);
118
+ j++;
119
+ }
120
+ return out;
121
+ };
77
122
 
78
123
  function makeDOMDriver(
79
124
  container: string | Element | DocumentFragment,
@@ -87,66 +132,79 @@ function makeDOMDriver(
87
132
  const patch = init([isolateModule.createModule() as Partial<Module>].concat(modules), undefined, snabbdomOptions);
88
133
  const domReady$ = makeDOMReady$();
89
134
  let vnodeWrapper: VNodeWrapper;
90
- let mutationObserver: MutationObserver;
91
- const mutationConfirmed$ = xs.create<null>({
92
- start(listener) {
93
- mutationObserver = new MutationObserver(() => listener.next(null));
94
- },
95
- stop() {
96
- mutationObserver.disconnect();
97
- },
98
- });
99
135
 
100
136
  function DOMDriver(vnode$: Stream<VNode>, name = 'DOM'): MainDOMSource {
101
137
  domDriverInputGuard(vnode$);
102
138
  const sanitation$ = xs.create<null>();
103
139
 
140
+ let r: any;
104
141
  const firstRoot$ = domReady$.map(() => {
105
142
  const firstRoot = getValidNode(container) || document.body;
106
143
  vnodeWrapper = new VNodeWrapper(firstRoot);
144
+ r = {sel: '', data: {isolate: []}, elm: firstRoot};
107
145
  return firstRoot;
108
146
  });
109
147
 
110
148
  const rememberedVNode$ = vnode$.remember();
111
149
  rememberedVNode$.addListener({});
112
150
 
113
- mutationConfirmed$.addListener({});
114
-
115
- const elementAfterPatch$ = firstRoot$
151
+ // P45-C (rec 9, D146): the root element, when the DOM is ready and then after each patch
152
+ // (it was a MutationObserver on the root's subtree: DOM changes made outside a patch no
153
+ // longer emit)
154
+ // G-261: and when Sygnal changes the DOM outside a patch (a Transition's leave, a Portal
155
+ // mounted late): an event that bubbles up to the root. G-276: on the current root (a patch
156
+ // can replace the first one). G-277: the innermost app's root takes it (a nested app or
157
+ // custom element doesn't re-emit the outer app's DOM source)
158
+ let pl: any, cur: any;
159
+ const poke = (e: Event) => pl && (e.stopPropagation(), pl.next(cur));
160
+ const off = () => cur?.removeEventListener(POKE, poke);
161
+ // G-545: run() gives any DOM driver its reporter (onError, phase 'patch') on the isolate
162
+ // module; an explicit reportSnabbdomError comes first
163
+ const rep = (e: any) => (options.reportSnabbdomError || (isolateModule as any).rep || console.error)(e);
164
+ let dead = 0, end = 0;
165
+ const rootElement$ = firstRoot$
116
166
  .map(
117
- firstRoot =>
118
- xs
119
- .merge(rememberedVNode$.endWhen(sanitation$), sanitation$)
120
- .map(vnode => vnodeWrapper.call(vnode))
121
- .startWith(addRootScope(toVNode(firstRoot)))
122
- .fold(patch, toVNode(firstRoot))
123
- .drop(1)
124
- .map(unwrapElementFromVNode)
125
- .startWith(firstRoot as any)
126
- .map(el => {
127
- mutationObserver.observe(el, {
128
- childList: true,
129
- attributes: true,
130
- characterData: true,
131
- subtree: true,
132
- attributeOldValue: true,
133
- characterDataOldValue: true,
134
- });
135
- return el;
136
- })
137
- .compose(dropCompletion)
167
+ (firstRoot, P = (o: any, v: any) => (v = flat(v), patch(o == r ? (cur.removeAttribute?.(ERR), {...r, sel: v.sel, key: v.key, children: adopt(firstRoot, v)}) : o, v))) =>
168
+ xs.merge(
169
+ xs
170
+ .merge(rememberedVNode$.endWhen(sanitation$), sanitation$.map(() => (end = 1, null)))
171
+ .map(vnode => vnodeWrapper.call(vnode))
172
+ // the first step gives the root its scope; the second, the app's first patch,
173
+ // adopts the markup in it (G-456). The root keeps its own attributes (G-466)
174
+ .startWith(r)
175
+ // G-540: a patch that throws (a hook, a module) is reported once, and the app's DOM
176
+ // stops updating: snabbdom stopped half way (some elements patched, created or
177
+ // removed, the rest not; insert hooks not run), and no vnode tree matches that DOM
178
+ // (patching on from either tree, or adopting the DOM again, left wrong or duplicated
179
+ // DOM). The stream doesn't end (its events, the root element), and a patch to no
180
+ // children (dispose) still runs, so what the last good tree mounted (a Portal's
181
+ // content) is removed; its errors aren't reported again. G-549: only dispose (`end`:
182
+ // the sanitation signal), not a live render that has no children. G-543 (D224): the
183
+ // root element is marked data-sygnal-error="patch" while its DOM is stopped (CSS can
184
+ // show it; dispose removes it). G-550: marked before the error is reported (onError
185
+ // can read it); the first patch clears a mark a failed, undisposed app left in the
186
+ // container, and dispose removes it only when this app set it; a throw while
187
+ // flattening the tree (a key JSON.stringify can't take) is a patch error too (P
188
+ // flattens). A DocumentFragment mount point has no attributes: no mark
189
+ .fold((o: any, v: any) => {
190
+ if (!dead || end) try { o = P(o, v) } catch (e) { dead++ || (cur.setAttribute?.(ERR, 'patch'), rep(e)) }
191
+ end && dead && cur.removeAttribute?.(ERR)
192
+ return o
193
+ }, {...r, data: {}})
194
+ .drop(1)
195
+ .map(unwrapElementFromVNode)
196
+ .startWith(firstRoot as any)
197
+ .map((el: any) => (off(), el.addEventListener(POKE, poke), cur = el)),
198
+ // never completes (nor does the root element). G-285: the listener moves with the
199
+ // root and goes when the app stops (a disposed app's driver isn't kept by its root)
200
+ xs.create<any>({start: l => pl = l, stop: () => (pl = 0, off())})
201
+ )
138
202
  )
139
- .flatten();
140
-
141
- const rootElement$ = concat(domReady$, mutationConfirmed$)
203
+ .flatten()
142
204
  .endWhen(sanitation$)
143
- .compose(sampleCombine(elementAfterPatch$))
144
- .map(arr => arr[1])
145
205
  .remember();
146
206
 
147
- rootElement$.addListener({
148
- error: options.reportSnabbdomError || defaultReportSnabbdomError,
149
- });
207
+ rootElement$.addListener({error: rep});
150
208
 
151
209
  const delegator = new EventDelegator(rootElement$, isolateModule);
152
210
 
@@ -3,6 +3,22 @@ import {DevToolEnabledSource, FantasyObservable} from '../run/types';
3
3
  import {VNode} from './snabbdom';
4
4
  import {EventsFnOptions} from './DOMSource';
5
5
  import {adapt} from '../run/adapt';
6
+ import {enrichEventStream} from './enrichEventStream';
7
+
8
+ /**
9
+ * Optional simulated-event hub (used by renderComponent's simulateEvent): a
10
+ * stream of `{type, event, match(path)}`; each source's events(type) also emits
11
+ * the hub events whose `match` accepts its selector path (isolation scopes
12
+ * included as '.___scope' segments).
13
+ */
14
+ export type MockEventHub = Stream<{type: string; event: any; match: (path: string[]) => boolean}>;
15
+
16
+ /**
17
+ * Optional listener callback (renderComponent): called with `live` undefined when events() is
18
+ * called (SYG103/104 checks), then with true / false when that hub listener is subscribed /
19
+ * unsubscribed (simulateEvent waits for a just-mounted child's listeners, G-039).
20
+ */
21
+ export type MockOnEvents = (path: string[], eventType: string, live?: boolean) => void;
6
22
 
7
23
  export type MockConfig = {
8
24
  [name: string]: FantasyObservable<any> | MockConfig;
@@ -13,7 +29,12 @@ const SCOPE_PREFIX = '___';
13
29
  export class MockedDOMSource {
14
30
  private _elements: FantasyObservable<any>;
15
31
 
16
- constructor(private _mockConfig: MockConfig) {
32
+ constructor(
33
+ private _mockConfig: MockConfig,
34
+ private _hub?: MockEventHub,
35
+ public _path: string[] = [],
36
+ private _onEvents?: MockOnEvents
37
+ ) {
17
38
  if (_mockConfig.elements) {
18
39
  this._elements = _mockConfig.elements as FantasyObservable<any>;
19
40
  } else {
@@ -43,20 +64,54 @@ export class MockedDOMSource {
43
64
  options?: EventsFnOptions,
44
65
  bubbles?: boolean
45
66
  ): any {
46
- const streamForEventType = this._mockConfig[eventType] as any;
47
- const out: DevToolEnabledSource & FantasyObservable<any> = adapt(
48
- streamForEventType || xs.empty()
49
- );
67
+ const configured = this._mockConfig[eventType] as any;
68
+ const {_hub: hub, _path: path, _onEvents: on} = this;
69
+ if (on) on(path, eventType);
70
+ let hub$: Stream<any> | undefined;
71
+ if (hub) {
72
+ const ev$ = hub.filter(e => e.type === eventType && e.match(path)).map(e => e.event);
73
+ let l: any;
74
+ hub$ = on
75
+ ? xs.create({
76
+ start: (x: any) => {
77
+ ev$.addListener(l = {next: (v: any) => x.next(v), error: (e: any) => x.error(e), complete: () => x.complete()});
78
+ on(path, eventType, true);
79
+ },
80
+ stop: () => {
81
+ ev$.removeListener(l);
82
+ on(path, eventType, false);
83
+ },
84
+ })
85
+ : ev$;
86
+ }
87
+ const out: DevToolEnabledSource & FantasyObservable<any> = enrichEventStream(adapt(
88
+ hub$
89
+ ? xs.merge(configured ? xs.fromObservable(configured) : xs.empty(), hub$)
90
+ : configured || xs.empty()
91
+ ));
50
92
 
51
93
  out._isCycleSource = 'MockedDOM';
52
94
 
53
95
  return out;
54
96
  }
55
97
 
56
- public select(selector: string): MockedDOMSource {
98
+ public select(selector: any): MockedDOMSource {
99
+ // CT-1: a control selects by its marker; a component (any other function) is rejected
100
+ // like the real DOM source does (SYG124 in the dev entry names the fixes)
101
+ if (typeof selector == 'function') {
102
+ if (!selector.__sygnalControl) {
103
+ throw new Error(`DOM driver's select() expects the argument to be a string as a CSS selector`);
104
+ }
105
+ selector = '' + selector;
106
+ }
57
107
  const mockConfigForSelector = this._mockConfig[selector] || {};
58
108
 
59
- return new MockedDOMSource(mockConfigForSelector as MockConfig);
109
+ return new MockedDOMSource(
110
+ mockConfigForSelector as MockConfig,
111
+ this._hub,
112
+ this._path.concat(selector),
113
+ this._onEvents
114
+ );
60
115
  }
61
116
 
62
117
  public isolateSource(
@@ -66,9 +121,24 @@ export class MockedDOMSource {
66
121
  return source.select('.' + SCOPE_PREFIX + scope);
67
122
  }
68
123
 
124
+ /**
125
+ * PLAN-4.6: isolateSink for one vnode (a copy, with the scope class). 4-I G-558: a fragment (a
126
+ * Collection's, `<>…</>`) has no element: each of its top-level elements gets the class (as the
127
+ * real driver's scoped()); a text vnode is left as it is
128
+ */
129
+ public isolateValue(vnode: any, scope: string): any {
130
+ // G-305: the scope's class token exactly (scope s1 is not s14)
131
+ return !vnode || typeof vnode != 'object' ? vnode
132
+ : !vnode.sel ? (vnode.children ? {...vnode, children: vnode.children.map((c: any) => this.isolateValue(c, scope))} : vnode)
133
+ : vnode.sel.split('.').indexOf(SCOPE_PREFIX + scope) > 0 ? vnode : {...vnode, sel: vnode.sel + `.${SCOPE_PREFIX}${scope}`};
134
+ }
135
+
69
136
  public isolateSink(sink: any, scope: string): any {
70
137
  return adapt(
71
138
  xs.fromObservable<any>(sink).map((vnode: VNode) => {
139
+ // B-025: a Switchable's DOM sink starts with undefined (nothing rendered yet); the
140
+ // real DOM driver's isolateSink passes it through too
141
+ if (!vnode) return vnode;
72
142
  if (vnode.sel && vnode.sel.indexOf(SCOPE_PREFIX + scope) !== -1) {
73
143
  return vnode;
74
144
  } else {
@@ -80,6 +150,10 @@ export class MockedDOMSource {
80
150
  }
81
151
  }
82
152
 
83
- export function mockDOMSource(mockConfig: MockConfig): MockedDOMSource {
84
- return new MockedDOMSource(mockConfig);
153
+ export function mockDOMSource(
154
+ mockConfig: MockConfig,
155
+ hub?: MockEventHub,
156
+ onEvents?: MockOnEvents
157
+ ): MockedDOMSource {
158
+ return new MockedDOMSource(mockConfig, hub, [], onEvents);
85
159
  }
@@ -1,16 +1,33 @@
1
- import {Module, classModule, propsModule, attributesModule, datasetModule} from './snabbdom';
1
+ import {Module, classModule, attributesModule, datasetModule} from './snabbdom';
2
2
  import {styleModule} from './styleModule';
3
3
  import {selectModule} from './selectModule';
4
+ import {controlledInputModule} from './controlledInputModule';
5
+ import {classNameModule} from './classNameModule';
6
+ import {propsModule} from './propsModule';
7
+ import {isField} from './controlledInputModule';
8
+ import {sameData} from './utils';
4
9
 
5
- const modules: Array<Module> = [
10
+ const list: Array<Module> = [
6
11
  styleModule,
7
12
  classModule,
8
- propsModule,
13
+ propsModule, // Sygnal's: also clears removed and nullish props (B-015, G-109)
14
+ classNameModule, // after propsModule (B-012)
9
15
  attributesModule,
10
16
  datasetModule,
11
17
  selectModule,
18
+ controlledInputModule,
12
19
  ];
20
+ // P46-P: one update hook for these modules, in their order. A vnode whose data is the same as
21
+ // last time (each bucket shallow-equal) needs none of them, unless it is a form field (the
22
+ // controlled-input and select modules re-sync it every patch)
23
+ const ups = list.map(m => m.update!);
24
+ const modules: Array<Module> = list.map(({update, ...m}) => m as Module).concat({
25
+ update(o: any, v: any) {
26
+ if (!isField(v) && sameData(o.data, v.data)) return;
27
+ for (let i = 0; i < ups.length; i++) ups[i](o, v);
28
+ },
29
+ } as Module);
13
30
 
14
- export {styleModule, classModule, propsModule, attributesModule, datasetModule, selectModule};
31
+ export {styleModule, classModule, propsModule, attributesModule, datasetModule, selectModule, controlledInputModule, classNameModule};
15
32
 
16
33
  export default modules;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Sygnal's props module: snabbdom's propsModule, plus clearing removed and
3
+ * nullish props (B-015, G-109).
4
+ *
5
+ * Problem: snabbdom's propsModule writes new and changed props but never
6
+ * unsets a prop that disappears, so a reused element keeps `title`, `disabled`,
7
+ * `href`, ... from the previous render. A nullish prop is written as the
8
+ * string "undefined"/"null" (`title={cond ? 'x' : undefined}`, `src={null}`).
9
+ *
10
+ * Fix: write new and changed props except nullish ones (G-109: nothing is ever
11
+ * written as "null"). For each prop that is gone (or became nullish), remove
12
+ * the reflected attribute, which resets the property (numbers such as
13
+ * maxLength fall back to their default too). Without such an attribute, reset
14
+ * the property by its current type (boolean -> false, string -> '', other
15
+ * non-number -> undefined/null) if it differs (G-095). `className` is left
16
+ * to classNameModule (B-012). On form fields a nullish `value`/`checked` clears
17
+ * the field ('' / false, D49); a removed one is left alone (the controlled-input
18
+ * module owns them; removing them hands control back to the user, as in React).
19
+ */
20
+
21
+ import type {VNode} from 'snabbdom/build/vnode.js';
22
+
23
+ function clearProp(elm: any, key: string, nullish?: boolean): void {
24
+ if (key === 'className') return;
25
+ if (/^(value|checked)$/.test(key) && /^(INPUT|TEXTAREA|SELECT)$/.test(elm.tagName)) {
26
+ const empty = key === 'value' ? '' : false;
27
+ if (nullish && elm[key] !== empty) elm[key] = empty;
28
+ return;
29
+ }
30
+ const attr = key === 'htmlFor' ? 'for' : key.replace(/^aria(?=[A-Z])/, 'aria-').toLowerCase();
31
+ // G-095: a reflected attribute is just removed, which resets the property. Writing it first
32
+ // (src = '') would queue an img/video error event or navigate an iframe to about:blank.
33
+ if (elm.hasAttribute?.(attr)) return elm.removeAttribute(attr);
34
+ const cur = elm[key];
35
+ const t = typeof cur;
36
+ const reset = t === 'boolean' ? false : t === 'string' ? '' : cur === null || t === 'function' ? null : undefined;
37
+ try {
38
+ // numbers (maxLength, ...) are left to their default; unchanged values aren't rewritten
39
+ if (t !== 'number' && cur !== reset) elm[key] = reset;
40
+ } catch (_) {
41
+ // e.g. contentEditable rejects ''
42
+ }
43
+ }
44
+
45
+ function updateProps(oldVnode: VNode, vnode: VNode): void {
46
+ const elm: any = vnode.elm;
47
+ const props: any = vnode.data?.props || {};
48
+ const oldProps: any = oldVnode.data?.props || {};
49
+ if (!elm || oldProps === props) return;
50
+ for (const key in oldProps) {
51
+ if (!(key in props)) clearProp(elm, key);
52
+ }
53
+ for (const key in props) {
54
+ const cur = props[key];
55
+ if (cur === oldProps[key]) continue;
56
+ // a nullish className is left to classNameModule, which rebuilds the attribute (B-012)
57
+ if (cur == null) clearProp(elm, key, true);
58
+ // G-553: on create (no old sel) `value` is always written: the create hooks run before the
59
+ // children are appended, so an <option>'s value reads '' (no text yet) and value="" would be
60
+ // skipped, leaving no attribute (the option then submits its label)
61
+ else if (key !== 'value' || elm[key] !== cur || !oldVnode.sel) elm[key] = cur;
62
+ }
63
+ }
64
+
65
+ export const propsModule = {create: updateProps, update: updateProps};
@@ -1,33 +1,38 @@
1
1
  /**
2
- * Snabbdom module that fixes <select> value assignment.
2
+ * Snabbdom module that applies a <select>'s `value` after its <option>s exist.
3
3
  *
4
- * Problem: snabbdom's createElm() fires the propsModule create hook
5
- * (which sets elm.value) BEFORE appending child <option> elements.
6
- * The browser silently ignores the value assignment because no matching
7
- * option exists yet, so <select> always shows the first option.
4
+ * Problem: snabbdom runs the module create/update hooks (propsModule sets
5
+ * elm.value) BEFORE it creates or patches the element's children. When the
6
+ * matching <option> is new in this patch, the browser ignores the value and
7
+ * the <select> shows the first option. On create that is every <select>; on
8
+ * update it is a value change together with an options change (B-017).
8
9
  *
9
- * Fix: collect <select> elements that have a value prop during create,
10
- * then re-apply the value in the post hook (after all elements and
11
- * children have been inserted).
10
+ * Fix: collect <select> elements with a value prop during create and update,
11
+ * then re-apply the value in the post hook, after all children are patched.
12
12
  */
13
13
 
14
14
  import type {VNode} from 'snabbdom/build/vnode.js';
15
+ import {isField} from './controlledInputModule';
15
16
 
16
17
  let pendingSelects: Array<{elm: HTMLSelectElement; value: any}> = [];
17
18
 
18
- function createSelect(_oldVnode: VNode, vnode: VNode): void {
19
- const elm = vnode.elm as Element;
20
- if (elm && elm.tagName === 'SELECT' && vnode.data?.props?.value !== undefined) {
21
- pendingSelects.push({elm: elm as HTMLSelectElement, value: vnode.data.props.value});
19
+ function queueSelect(_oldVnode: VNode, vnode: VNode): void {
20
+ // P45-A: a form field by the vnode's tag first; most vnodes aren't
21
+ if (!isField(vnode)) return;
22
+ const elm = vnode.elm as HTMLSelectElement;
23
+ const value = vnode.data?.props?.value;
24
+ if (elm && elm.tagName === 'SELECT' && value != null) {
25
+ pendingSelects.push({elm, value});
22
26
  }
23
27
  }
24
28
 
25
29
  function postPatch(): void {
26
- for (let i = 0; i < pendingSelects.length; i++) {
27
- const {elm, value} = pendingSelects[i];
28
- elm.value = value;
29
- }
30
+ const list = pendingSelects;
30
31
  pendingSelects = [];
32
+ for (let i = 0; i < list.length; i++) {
33
+ const {elm, value} = list[i];
34
+ if (elm.value !== String(value)) elm.value = value;
35
+ }
31
36
  }
32
37
 
33
- export const selectModule = {create: createSelect, post: postPatch};
38
+ export const selectModule = {create: queueSelect, update: queueSelect, post: postPatch};
@@ -13,14 +13,9 @@
13
13
  export {h, fragment as HFragment} from 'snabbdom/build/h.js';
14
14
  export {init} from 'snabbdom/build/init.js';
15
15
  export type {Options} from 'snabbdom/build/init.js';
16
- export {toVNode} from 'snabbdom/build/tovnode.js';
17
16
  export {vnode} from 'snabbdom/build/vnode.js';
18
17
  export type {VNode, VNodeData} from 'snabbdom/build/vnode.js';
19
- import {Fragment as _Fragment} from 'snabbdom/build/jsx.js';
20
-
21
- // Tag Fragment so we can identify it even after minification mangles Function.name
22
- (_Fragment as any).__sygnalFragment = true;
23
- export const Fragment = _Fragment;
18
+ export {Fragment} from './fragment';
24
19
 
25
20
  // Module type
26
21
  export type {Module} from 'snabbdom/build/modules/module.js';
@@ -1,4 +1,5 @@
1
1
  import {h, VNode, VNodeData} from './snabbdom';
2
+ import {flat} from './utils';
2
3
 
3
4
  export interface ThunkData extends VNodeData {
4
5
  fn(): VNode;
@@ -10,6 +11,8 @@ export interface Thunk extends VNode {
10
11
  }
11
12
 
12
13
  function copyToThunk(vnode: VNode, thunkVNode: Thunk): void {
14
+ // (rendered during the patch: its fragments are flattened here, as the DOM driver does)
15
+ vnode = flat(vnode);
13
16
  thunkVNode.elm = vnode.elm;
14
17
  (vnode.data as ThunkData).fn = thunkVNode.data.fn;
15
18
  (vnode.data as ThunkData).args = thunkVNode.data.args;
@@ -86,3 +86,101 @@ export function makeInsert(
86
86
  }
87
87
  };
88
88
  }
89
+
90
+ // G-261: a DOM change Sygnal makes outside a patch (a Transition's leave, a Portal mounted late)
91
+ // re-emits the DOM source of the app whose root contains `el` (makeDOMDriver listens on its root)
92
+ export const POKE = 'sygnal-dom';
93
+ export const pokeDOM = (el: any): any => el?.dispatchEvent(new Event(POKE, {bubbles: true}));
94
+
95
+ const obj = (o: any) => o !== null && typeof o == 'object' && !Array.isArray(o);
96
+ /**
97
+ * P46-P: two vnodes' data are the same for the DOM modules: the same keys, each value identical
98
+ * or a shallow-equal object (a module bucket: props, attrs, class, dataset, style, ...)
99
+ */
100
+ export function sameData(a: any, b: any): boolean {
101
+ if (a === b) return true;
102
+ if (!a || !b) return false;
103
+ let n = 0;
104
+ for (const k in a) {
105
+ n++;
106
+ const x = a[k], y = b[k];
107
+ if (x !== y) {
108
+ if (!obj(x) || !obj(y)) return false;
109
+ let m = 0;
110
+ for (const j in x) { m++; if (x[j] !== y[j]) return false; }
111
+ for (const _ in y) m--;
112
+ if (m) return false;
113
+ }
114
+ }
115
+ for (const _ in b) n--;
116
+ return !n;
117
+ }
118
+
119
+ // G-517 / G-518 / G-522: snabbdom never sees a fragment. Before each patch, a fragment's children
120
+ // (through nested ones) are spliced into its parent's children, so they are diffed, moved and
121
+ // adopted at hydration as the parent's own (snabbdom's fragment bounds went stale: an insert into
122
+ // one landed at the parent's end, a keyed one never moved, a later patch threw NotFoundError).
123
+ // The children of a keyed fragment (a Collection item, a component's root) take its key and their
124
+ // own key, else their tag and its count among them: they move with it and stay apart from another
125
+ // item's. G-541: no two paths give the same key (a '/' separator did: fragment "src" + "a/b.ts" and
126
+ // fragment "src/a" + "b.ts"). 3-W G-544: each segment is a JSON string (the key as a string) or
127
+ // array ([tag, count], always last), which ends where it ends, appended to the prefix
128
+ // (`"src""a/b.ts"`): a key grows linearly with the nesting (JSON in JSON doubled at each level),
129
+ // and any key String() takes is fine (G-548: a BigInt; two Symbols with the same description share
130
+ // a key; a number key and its string are one key, as in snabbdom). A vnode with no fragment below is kept by identity, as is one already
131
+ // flattened (a cached subtree), so snabbdom still skips unchanged subtrees; so are a keyed
132
+ // fragment's copies while the fragment and its prefix are the same (G-539; a hit compares the
133
+ // parent's prefix, the fragment's own is made on a miss). A hoisted keyed fragment rendered in two
134
+ // places shares its copies, as any hoisted vnode shares its object (render it once). A copy (a
135
+ // parent with new children, a child with a new key) has its vnode's fields and writes its
136
+ // element there too, so the app's vnodes get their `elm` as before (testing and diagnostics read
137
+ // it); it keeps its own (a vnode object reused under another key: the old copy removes its own).
138
+ const flatOf = new WeakMap<any, any>();
139
+ const cpOf = new WeakMap<any, any>();
140
+ // 4-I G-561: a copy is an instance of one class (the vnode's own fields, `elm` an accessor on its
141
+ // prototype), not Object.create(y) with an own accessor: snabbdom's patch loop read each field
142
+ // through the prototype chain and called a per-object accessor, ~3x slower over a 1k Collection.
143
+ // (y: set again after the fields, as a copy's own y is its vnode's vnode; m starts as the vnode's
144
+ // elm, which snabbdom replaces before reading it)
145
+ class Copy {
146
+ y: any; m: any
147
+ constructor(y: any, o: any) { this.y = y; Object.assign(this, y, o).y = y }
148
+ get elm() { return this.m }
149
+ set elm(e: any) { this.y.elm = this.m = e }
150
+ }
151
+ const seg = (k: any, s: any) => (k || '') + JSON.stringify(typeof s == 'object' ? s : String(s));
152
+ export const flat = (v: any): any => {
153
+ if (!v || v.$p || !v.children) return v;
154
+ let r = flatOf.get(v);
155
+ if (!r) {
156
+ const o: any[] = [];
157
+ let d = 0;
158
+ // (m, s: locals of each call)
159
+ const put = (c: any[], k?: string, n: any = {}, m?: any, s?: any) => {
160
+ for (const x of c) {
161
+ // G-539: a keyed fragment's copies are kept per (fragment, prefix), in flatOf (flat() never
162
+ // takes a fragment): an unchanged one (a Collection item that didn't render again) gives
163
+ // snabbdom the same vnodes
164
+ if (x && !x.sel && x.children) d = 1, x.key == null ? put(x.children, k, n)
165
+ : (m = flatOf.get(x)) && m[0] === k ? o.push(...m[1])
166
+ : (s = o.length, put(x.children, seg(k, x.key)), flatOf.set(x, [k, o.slice(s)]));
167
+ else {
168
+ let y = flat(x);
169
+ y !== x && (d = 1);
170
+ if (k && y) {
171
+ // 4-H: the copy of an unchanged child under the same key is kept (the fragment is new
172
+ // when one of its children is: a Collection's), so snabbdom skips the others. 4-I
173
+ // G-561: a hit compares the prefix and the child's own part (its key, else its count),
174
+ // so the key is only built on a miss
175
+ const s = y.key ?? (n[y.sel] = -~n[y.sel]), c = cpOf.get(y);
176
+ y = c && c[0] === k && c[1] === s ? c[2] : (cpOf.set(y, [k, s, y = new Copy(y, {key: seg(k, y.key ?? [y.sel, s])})]), y);
177
+ }
178
+ o.push(y);
179
+ }
180
+ }
181
+ };
182
+ put(v.children);
183
+ flatOf.set(v, r = d ? new Copy(v, {children: o}) : v);
184
+ }
185
+ return r;
186
+ };