neo.mjs 10.0.0-beta.5 → 10.0.0-beta.6

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 (468) hide show
  1. package/.github/RELEASE_NOTES/v10.0.0-beta.5.md +70 -0
  2. package/.github/RELEASE_NOTES/v10.0.0-beta.6.md +48 -0
  3. package/.github/epic-functional-components.md +498 -0
  4. package/.github/ticket-asymmetric-vdom-updates.md +122 -0
  5. package/README.md +0 -3
  6. package/ServiceWorker.mjs +2 -2
  7. package/apps/colors/store/Colors.mjs +1 -0
  8. package/apps/colors/view/GridContainer.mjs +3 -0
  9. package/apps/colors/view/HeaderToolbar.mjs +2 -0
  10. package/apps/colors/view/Viewport.mjs +3 -0
  11. package/apps/covid/view/FooterContainer.mjs +3 -0
  12. package/apps/covid/view/GalleryContainer.mjs +2 -0
  13. package/apps/covid/view/GalleryContainerController.mjs +1 -0
  14. package/apps/covid/view/HeaderContainer.mjs +2 -0
  15. package/apps/covid/view/HelixContainer.mjs +2 -0
  16. package/apps/covid/view/HelixContainerController.mjs +1 -0
  17. package/apps/covid/view/MainContainer.mjs +3 -0
  18. package/apps/covid/view/TableContainer.mjs +3 -0
  19. package/apps/covid/view/TableContainerController.mjs +1 -0
  20. package/apps/covid/view/WorldMapContainer.mjs +2 -0
  21. package/apps/covid/view/country/Gallery.mjs +3 -0
  22. package/apps/covid/view/country/Helix.mjs +8 -0
  23. package/apps/covid/view/country/HistoricalDataTable.mjs +1 -0
  24. package/apps/covid/view/country/Table.mjs +2 -0
  25. package/apps/covid/view/mapboxGl/Component.mjs +1 -0
  26. package/apps/covid/view/mapboxGl/Container.mjs +2 -0
  27. package/apps/email/EPIC_PLAN.md +58 -0
  28. package/apps/email/neo-config.json +2 -2
  29. package/apps/email/store/Emails.mjs +11 -1
  30. package/apps/email/view/ComposeView.mjs +44 -0
  31. package/apps/email/view/MainView.mjs +89 -0
  32. package/apps/email/view/Viewport.mjs +4 -33
  33. package/apps/email/view/ViewportStateProvider.mjs +3 -3
  34. package/apps/form/store/SideNav.mjs +1 -0
  35. package/apps/form/view/FormContainer.mjs +1 -0
  36. package/apps/form/view/FormPageContainer.mjs +2 -0
  37. package/apps/form/view/SideNavList.mjs +1 -0
  38. package/apps/form/view/Viewport.mjs +3 -0
  39. package/apps/portal/childapps/preview/MainContainer.mjs +1 -0
  40. package/apps/portal/index.html +1 -1
  41. package/apps/portal/store/BlogPosts.mjs +2 -0
  42. package/apps/portal/store/Content.mjs +1 -0
  43. package/apps/portal/store/ContentSections.mjs +1 -0
  44. package/apps/portal/store/Examples.mjs +1 -0
  45. package/apps/portal/view/HeaderToolbar.mjs +1 -0
  46. package/apps/portal/view/Viewport.mjs +5 -0
  47. package/apps/portal/view/ViewportController.mjs +8 -2
  48. package/apps/portal/view/about/Container.mjs +2 -0
  49. package/apps/portal/view/about/MemberContainer.mjs +7 -0
  50. package/apps/portal/view/blog/Container.mjs +2 -0
  51. package/apps/portal/view/blog/List.mjs +2 -0
  52. package/apps/portal/view/examples/List.mjs +1 -0
  53. package/apps/portal/view/examples/TabContainer.mjs +4 -0
  54. package/apps/portal/view/home/ContentBox.mjs +3 -0
  55. package/apps/portal/view/home/FeatureSection.mjs +8 -0
  56. package/apps/portal/view/home/FooterContainer.mjs +4 -1
  57. package/apps/portal/view/home/MainContainer.mjs +2 -0
  58. package/apps/portal/view/home/parts/AfterMath.mjs +2 -0
  59. package/apps/portal/view/home/parts/BaseContainer.mjs +1 -0
  60. package/apps/portal/view/home/parts/Colors.mjs +4 -0
  61. package/apps/portal/view/home/parts/Features.mjs +2 -0
  62. package/apps/portal/view/home/parts/Helix.mjs +5 -0
  63. package/apps/portal/view/home/parts/How.mjs +4 -0
  64. package/apps/portal/view/home/parts/MainNeo.mjs +1 -0
  65. package/apps/portal/view/home/parts/References.mjs +2 -0
  66. package/apps/portal/view/learn/ContentComponent.mjs +11 -5
  67. package/apps/portal/view/learn/ContentTreeList.mjs +2 -0
  68. package/apps/portal/view/learn/CubeLayoutButton.mjs +1 -0
  69. package/apps/portal/view/learn/MainContainer.mjs +4 -0
  70. package/apps/portal/view/learn/PageContainer.mjs +2 -0
  71. package/apps/portal/view/learn/PageSectionsContainer.mjs +3 -0
  72. package/apps/portal/view/learn/PageSectionsList.mjs +1 -0
  73. package/apps/portal/view/services/Component.mjs +1 -0
  74. package/apps/realworld/api/Base.mjs +1 -0
  75. package/apps/realworld/view/HeaderComponent.mjs +4 -0
  76. package/apps/realworld/view/HomeComponent.mjs +7 -0
  77. package/apps/realworld/view/MainContainer.mjs +2 -0
  78. package/apps/realworld/view/MainContainerController.mjs +2 -0
  79. package/apps/realworld/view/article/CommentComponent.mjs +3 -0
  80. package/apps/realworld/view/article/Component.mjs +17 -10
  81. package/apps/realworld/view/article/CreateCommentComponent.mjs +2 -0
  82. package/apps/realworld/view/article/CreateComponent.mjs +5 -0
  83. package/apps/realworld/view/article/PreviewComponent.mjs +9 -0
  84. package/apps/realworld/view/article/TagListComponent.mjs +2 -0
  85. package/apps/realworld/view/user/ProfileComponent.mjs +7 -0
  86. package/apps/realworld/view/user/SettingsComponent.mjs +5 -0
  87. package/apps/realworld/view/user/SignUpComponent.mjs +3 -0
  88. package/apps/realworld2/api/Base.mjs +1 -0
  89. package/apps/realworld2/view/FooterComponent.mjs +1 -0
  90. package/apps/realworld2/view/HeaderToolbar.mjs +3 -0
  91. package/apps/realworld2/view/HomeContainer.mjs +1 -0
  92. package/apps/realworld2/view/MainContainer.mjs +2 -0
  93. package/apps/realworld2/view/MainContainerController.mjs +1 -0
  94. package/apps/realworld2/view/article/Helix.mjs +1 -0
  95. package/apps/realworld2/view/article/PreviewComponent.mjs +9 -0
  96. package/apps/realworld2/view/article/PreviewList.mjs +1 -0
  97. package/apps/realworld2/view/article/TagListComponent.mjs +2 -0
  98. package/apps/route/view/CenterContainer.mjs +1 -0
  99. package/apps/route/view/MainView.mjs +1 -0
  100. package/apps/sharedcovid/childapps/sharedcovidchart/MainContainer.mjs +1 -0
  101. package/apps/sharedcovid/childapps/sharedcovidgallery/MainContainer.mjs +1 -0
  102. package/apps/sharedcovid/childapps/sharedcovidhelix/MainContainer.mjs +1 -0
  103. package/apps/sharedcovid/childapps/sharedcovidmap/MainContainer.mjs +1 -0
  104. package/apps/sharedcovid/view/FooterContainer.mjs +3 -0
  105. package/apps/sharedcovid/view/GalleryContainer.mjs +2 -0
  106. package/apps/sharedcovid/view/GalleryContainerController.mjs +1 -0
  107. package/apps/sharedcovid/view/HeaderContainer.mjs +2 -0
  108. package/apps/sharedcovid/view/HelixContainer.mjs +2 -0
  109. package/apps/sharedcovid/view/HelixContainerController.mjs +1 -0
  110. package/apps/sharedcovid/view/MainContainer.mjs +3 -0
  111. package/apps/sharedcovid/view/TableContainer.mjs +3 -0
  112. package/apps/sharedcovid/view/TableContainerController.mjs +1 -0
  113. package/apps/sharedcovid/view/WorldMapContainer.mjs +2 -0
  114. package/apps/sharedcovid/view/country/Gallery.mjs +3 -0
  115. package/apps/sharedcovid/view/country/Helix.mjs +8 -0
  116. package/apps/sharedcovid/view/country/HistoricalDataTable.mjs +1 -0
  117. package/apps/sharedcovid/view/country/Table.mjs +2 -0
  118. package/apps/sharedcovid/view/mapboxGl/Component.mjs +1 -0
  119. package/apps/sharedcovid/view/mapboxGl/Container.mjs +2 -0
  120. package/apps/shareddialog/childapps/shareddialog2/view/MainContainer.mjs +2 -0
  121. package/apps/shareddialog/view/DemoDialog.mjs +2 -0
  122. package/apps/shareddialog/view/MainContainer.mjs +2 -0
  123. package/apps/shareddialog/view/MainContainerController.mjs +1 -0
  124. package/buildScripts/addReactiveTags.mjs +191 -0
  125. package/buildScripts/checkReactiveTags.mjs +160 -0
  126. package/docs/app/store/Api.mjs +1 -0
  127. package/docs/app/store/Examples.mjs +1 -0
  128. package/docs/app/view/ApiTreeList.mjs +1 -0
  129. package/docs/app/view/ContentTabContainer.mjs +2 -0
  130. package/docs/app/view/ExamplesTreeList.mjs +2 -0
  131. package/docs/app/view/HeaderContainer.mjs +3 -0
  132. package/docs/app/view/MainContainer.mjs +5 -0
  133. package/docs/app/view/classdetails/HeaderComponent.mjs +1 -0
  134. package/docs/app/view/classdetails/MainContainer.mjs +3 -0
  135. package/docs/app/view/classdetails/MembersList.mjs +5 -0
  136. package/docs/app/view/classdetails/SourceViewComponent.mjs +2 -0
  137. package/examples/ConfigurationViewport.mjs +14 -8
  138. package/examples/calendar/weekview/MainContainer.mjs +4 -0
  139. package/examples/component/coronaGallery/CountryGallery.mjs +2 -0
  140. package/examples/component/coronaGallery/CountryStore.mjs +1 -0
  141. package/examples/component/coronaGallery/Viewport.mjs +3 -0
  142. package/examples/component/coronaGallery/ViewportController.mjs +1 -0
  143. package/examples/component/coronaHelix/CountryHelix.mjs +7 -0
  144. package/examples/component/coronaHelix/MainContainer.mjs +1 -0
  145. package/examples/component/gallery/ImageStore.mjs +1 -0
  146. package/examples/component/helix/ImageStore.mjs +1 -0
  147. package/examples/component/helix/Viewport.mjs +3 -0
  148. package/examples/component/helix/ViewportController.mjs +1 -0
  149. package/examples/component/multiWindowCoronaGallery/childapp/Viewport.mjs +1 -0
  150. package/examples/component/multiWindowHelix/childapp/Viewport.mjs +1 -0
  151. package/examples/component/wrapper/googleMaps/MapComponent.mjs +2 -0
  152. package/examples/core/config/MainContainer.mjs +2 -0
  153. package/examples/dialog/DemoDialog.mjs +2 -0
  154. package/examples/dialog/MainContainer.mjs +1 -0
  155. package/examples/form/field/color/MainStore.mjs +1 -0
  156. package/examples/functional/defineComponent/Component.mjs +18 -0
  157. package/examples/functional/defineComponent/MainContainer.mjs +41 -0
  158. package/examples/functional/defineComponent/app.mjs +6 -0
  159. package/examples/functional/defineComponent/index.html +11 -0
  160. package/examples/functional/defineComponent/neo-config.json +6 -0
  161. package/examples/functional/hostComponent/Component.mjs +32 -0
  162. package/examples/functional/hostComponent/MainContainer.mjs +48 -0
  163. package/examples/functional/hostComponent/app.mjs +6 -0
  164. package/examples/functional/hostComponent/index.html +11 -0
  165. package/examples/functional/hostComponent/neo-config.json +6 -0
  166. package/examples/grid/animatedRowSorting/Viewport.mjs +1 -1
  167. package/examples/grid/bigData/ControlsContainer.mjs +3 -0
  168. package/examples/grid/bigData/GridContainer.mjs +4 -2
  169. package/examples/grid/bigData/MainContainer.mjs +2 -0
  170. package/examples/grid/bigData/MainModel.mjs +1 -0
  171. package/examples/grid/bigData/MainStore.mjs +3 -0
  172. package/examples/grid/cellEditing/MainContainer.mjs +1 -1
  173. package/examples/grid/container/MainContainer.mjs +1 -1
  174. package/examples/grid/covid/GridContainer.mjs +3 -0
  175. package/examples/grid/covid/MainContainer.mjs +2 -0
  176. package/examples/grid/covid/Store.mjs +1 -0
  177. package/examples/grid/nestedRecordFields/EditUserDialog.mjs +3 -0
  178. package/examples/grid/nestedRecordFields/Viewport.mjs +3 -1
  179. package/examples/list/animate/List.mjs +4 -0
  180. package/examples/list/animate/MainContainer.mjs +2 -0
  181. package/examples/list/circle/MainStore.mjs +1 -0
  182. package/examples/list/color/MainStore.mjs +1 -0
  183. package/examples/preloadingAssets/view/MainContainer.mjs +2 -0
  184. package/examples/stateProvider/advanced/MainContainer.mjs +1 -0
  185. package/examples/stateProvider/dialog/EditUserDialog.mjs +2 -0
  186. package/examples/stateProvider/dialog/MainContainer.mjs +1 -0
  187. package/examples/stateProvider/extendedClass/MainContainer.mjs +2 -0
  188. package/examples/stateProvider/inline/MainContainer.mjs +1 -0
  189. package/examples/stateProvider/inlineNoStateProvider/MainContainer.mjs +1 -0
  190. package/examples/stateProvider/inlineNoStateProvider/MainContainerController.mjs +2 -0
  191. package/examples/stateProvider/multiWindow/EditUserDialog.mjs +3 -0
  192. package/examples/stateProvider/multiWindow/MainContainer.mjs +1 -0
  193. package/examples/stateProvider/multiWindow/Viewport.mjs +1 -0
  194. package/examples/stateProvider/nestedData/MainContainer.mjs +1 -0
  195. package/examples/stateProvider/table/MainContainer.mjs +1 -0
  196. package/examples/table/covid/MainContainer.mjs +2 -0
  197. package/examples/table/covid/Store.mjs +1 -0
  198. package/examples/table/covid/TableContainer.mjs +3 -0
  199. package/examples/table/nestedRecordFields/EditUserDialog.mjs +3 -0
  200. package/examples/table/nestedRecordFields/Viewport.mjs +1 -0
  201. package/examples/todoList/version1/MainComponent.mjs +1 -1
  202. package/examples/toolbar/breadcrumb/view/MainContainer.mjs +2 -0
  203. package/examples/toolbar/paging/store/Users.mjs +1 -0
  204. package/examples/toolbar/paging/view/AddUserDialog.mjs +3 -0
  205. package/examples/toolbar/paging/view/MainContainer.mjs +3 -0
  206. package/examples/treeAccordion/MainContainer.mjs +2 -2
  207. package/examples/worker/task/MainContainer.mjs +1 -0
  208. package/learn/Glossary.md +1 -0
  209. package/learn/UsingTheseTopics.md +1 -0
  210. package/learn/benefits/ConfigSystem.md +2 -0
  211. package/learn/benefits/Effort.md +1 -0
  212. package/learn/benefits/Features.md +1 -0
  213. package/learn/benefits/FormsEngine.md +1 -0
  214. package/learn/benefits/FourEnvironments.md +2 -0
  215. package/learn/benefits/Introduction.md +2 -0
  216. package/learn/benefits/MultiWindow.md +3 -1
  217. package/learn/benefits/OffTheMainThread.md +2 -0
  218. package/learn/benefits/Quick.md +2 -0
  219. package/learn/benefits/RPCLayer.md +2 -0
  220. package/learn/benefits/Speed.md +2 -0
  221. package/learn/comparisons/NeoVsAngular.md +90 -0
  222. package/learn/comparisons/NeoVsExtJs.md +178 -0
  223. package/learn/comparisons/NeoVsNextJs.md +124 -0
  224. package/learn/comparisons/NeoVsReact.md +95 -0
  225. package/learn/comparisons/NeoVsSolid.md +78 -0
  226. package/learn/comparisons/NeoVsVue.md +92 -0
  227. package/learn/comparisons/Overview.md +46 -0
  228. package/learn/gettingstarted/ComponentModels.md +2 -0
  229. package/learn/gettingstarted/Config.md +2 -0
  230. package/learn/gettingstarted/DescribingTheUI.md +2 -0
  231. package/learn/gettingstarted/Events.md +2 -0
  232. package/learn/gettingstarted/Extending.md +2 -0
  233. package/learn/gettingstarted/References.md +2 -0
  234. package/learn/gettingstarted/Setup.md +3 -2
  235. package/learn/gettingstarted/Workspaces.md +2 -0
  236. package/learn/guides/datahandling/Collections.md +1 -0
  237. package/learn/guides/datahandling/Records.md +1 -0
  238. package/learn/guides/datahandling/StateProviders.md +130 -16
  239. package/learn/guides/datahandling/Tables.md +1 -1
  240. package/learn/guides/fundamentals/ConfigSystemDeepDive.md +1 -0
  241. package/learn/guides/fundamentals/DeclarativeComponentTreesVsImperativeVdom.md +2 -0
  242. package/learn/guides/fundamentals/DeclarativeVDOMWithEffects.md +10 -8
  243. package/learn/guides/fundamentals/ExtendingNeoClasses.md +1 -0
  244. package/learn/guides/fundamentals/InstanceLifecycle.md +3 -1
  245. package/learn/guides/fundamentals/MainThreadAddons.md +2 -0
  246. package/learn/guides/specificfeatures/Mixins.md +3 -1
  247. package/learn/guides/specificfeatures/MultiWindow.md +3 -1
  248. package/learn/guides/specificfeatures/PortalApp.md +2 -0
  249. package/learn/guides/uibuildingblocks/ComponentsAndContainers.md +2 -0
  250. package/learn/guides/uibuildingblocks/CustomComponents.md +2 -0
  251. package/learn/guides/uibuildingblocks/Layouts.md +2 -0
  252. package/learn/guides/uibuildingblocks/WorkingWithVDom.md +2 -0
  253. package/learn/guides/userinteraction/Forms.md +2 -0
  254. package/learn/guides/userinteraction/events/CustomEvents.md +2 -1
  255. package/learn/guides/userinteraction/events/DomEvents.md +2 -0
  256. package/learn/javascript/ClassFeatures.md +4 -3
  257. package/learn/javascript/Classes.md +10 -13
  258. package/learn/javascript/Overrides.md +10 -6
  259. package/learn/javascript/Super.md +12 -8
  260. package/learn/tree.json +71 -64
  261. package/learn/tutorials/Earthquakes.md +2 -0
  262. package/learn/tutorials/RSP.md +3 -1
  263. package/learn/tutorials/TodoList.md +103 -7
  264. package/package.json +6 -4
  265. package/resources/scss/src/apps/email/ComposeView.scss +16 -0
  266. package/resources/scss/src/apps/email/MainView.scss +5 -0
  267. package/resources/scss/src/apps/portal/learn/ContentComponent.scss +5 -4
  268. package/src/DefaultConfig.mjs +12 -2
  269. package/src/Main.mjs +1 -0
  270. package/src/Neo.mjs +217 -166
  271. package/src/Xhr.mjs +1 -0
  272. package/src/button/Base.mjs +13 -0
  273. package/src/button/Effect.mjs +16 -2
  274. package/src/button/Split.mjs +2 -0
  275. package/src/calendar/store/Calendars.mjs +1 -0
  276. package/src/calendar/store/Colors.mjs +1 -0
  277. package/src/calendar/store/Events.mjs +1 -0
  278. package/src/calendar/view/DayComponent.mjs +2 -0
  279. package/src/calendar/view/EditEventContainer.mjs +4 -1
  280. package/src/calendar/view/MainContainer.mjs +13 -0
  281. package/src/calendar/view/MainContainerStateProvider.mjs +14 -28
  282. package/src/calendar/view/SettingsContainer.mjs +1 -0
  283. package/src/calendar/view/YearComponent.mjs +16 -0
  284. package/src/calendar/view/calendars/ColorsList.mjs +2 -0
  285. package/src/calendar/view/calendars/Container.mjs +2 -0
  286. package/src/calendar/view/calendars/EditContainer.mjs +1 -0
  287. package/src/calendar/view/month/Component.mjs +11 -0
  288. package/src/calendar/view/settings/GeneralContainer.mjs +1 -0
  289. package/src/calendar/view/settings/MonthContainer.mjs +1 -0
  290. package/src/calendar/view/settings/WeekContainer.mjs +1 -0
  291. package/src/calendar/view/settings/YearContainer.mjs +1 -0
  292. package/src/calendar/view/week/Component.mjs +15 -1
  293. package/src/calendar/view/week/TimeAxisComponent.mjs +4 -0
  294. package/src/code/LivePreview.mjs +51 -23
  295. package/src/collection/Base.mjs +7 -10
  296. package/src/collection/Filter.mjs +6 -0
  297. package/src/collection/Sorter.mjs +3 -0
  298. package/src/component/Base.mjs +104 -771
  299. package/src/component/Canvas.mjs +1 -0
  300. package/src/component/Chip.mjs +4 -0
  301. package/src/component/Circle.mjs +14 -0
  302. package/src/component/Clock.mjs +4 -0
  303. package/src/component/DateSelector.mjs +12 -0
  304. package/src/component/Gallery.mjs +11 -0
  305. package/src/component/Helix.mjs +24 -0
  306. package/src/component/Label.mjs +1 -0
  307. package/src/component/Legend.mjs +3 -0
  308. package/src/component/MagicMoveText.mjs +4 -0
  309. package/src/component/Progress.mjs +3 -0
  310. package/src/component/Splitter.mjs +3 -0
  311. package/src/component/StatusBadge.mjs +6 -0
  312. package/src/component/Timer.mjs +4 -0
  313. package/src/component/Toast.mjs +6 -0
  314. package/src/component/Video.mjs +1 -0
  315. package/src/component/mwc/Button.mjs +7 -0
  316. package/src/component/mwc/TextField.mjs +9 -0
  317. package/src/component/wrapper/AmChart.mjs +2 -0
  318. package/src/component/wrapper/GoogleMaps.mjs +3 -0
  319. package/src/component/wrapper/MapboxGL.mjs +5 -0
  320. package/src/component/wrapper/MonacoEditor.mjs +12 -0
  321. package/src/container/Accordion.mjs +2 -0
  322. package/src/container/Base.mjs +7 -3
  323. package/src/container/Panel.mjs +1 -0
  324. package/src/container/Viewport.mjs +1 -0
  325. package/src/controller/Application.mjs +1 -0
  326. package/src/controller/Base.mjs +1 -0
  327. package/src/controller/Component.mjs +1 -0
  328. package/src/core/Base.mjs +55 -3
  329. package/src/core/Compare.mjs +4 -7
  330. package/src/core/Config.mjs +65 -52
  331. package/src/core/Effect.mjs +79 -13
  332. package/src/core/EffectBatchManager.mjs +18 -19
  333. package/src/core/EffectManager.mjs +25 -3
  334. package/src/core/IdGenerator.mjs +13 -44
  335. package/src/data/Model.mjs +2 -0
  336. package/src/data/Store.mjs +7 -0
  337. package/src/data/connection/WebSocket.mjs +2 -0
  338. package/src/date/DayViewComponent.mjs +2 -0
  339. package/src/date/SelectorContainer.mjs +14 -0
  340. package/src/dialog/Base.mjs +8 -0
  341. package/src/draggable/DragZone.mjs +5 -0
  342. package/src/draggable/tree/DragZone.mjs +1 -0
  343. package/src/filter/BooleanContainer.mjs +2 -0
  344. package/src/filter/NumberContainer.mjs +3 -0
  345. package/src/filter/ToggleOperatorsButton.mjs +2 -0
  346. package/src/form/Fieldset.mjs +6 -0
  347. package/src/form/field/Base.mjs +7 -0
  348. package/src/form/field/CheckBox.mjs +18 -0
  349. package/src/form/field/Chip.mjs +1 -0
  350. package/src/form/field/ComboBox.mjs +8 -0
  351. package/src/form/field/Country.mjs +1 -0
  352. package/src/form/field/Currency.mjs +2 -0
  353. package/src/form/field/Date.mjs +4 -0
  354. package/src/form/field/Display.mjs +1 -0
  355. package/src/form/field/Email.mjs +1 -0
  356. package/src/form/field/FileUpload.mjs +7 -0
  357. package/src/form/field/Hidden.mjs +1 -0
  358. package/src/form/field/Number.mjs +7 -0
  359. package/src/form/field/Password.mjs +1 -0
  360. package/src/form/field/Phone.mjs +3 -0
  361. package/src/form/field/Picker.mjs +2 -0
  362. package/src/form/field/Radio.mjs +1 -0
  363. package/src/form/field/Range.mjs +3 -0
  364. package/src/form/field/Search.mjs +2 -0
  365. package/src/form/field/Text.mjs +32 -0
  366. package/src/form/field/TextArea.mjs +7 -0
  367. package/src/form/field/Time.mjs +6 -0
  368. package/src/form/field/Url.mjs +3 -0
  369. package/src/form/field/ZipCode.mjs +2 -0
  370. package/src/form/field/trigger/Base.mjs +3 -0
  371. package/src/form/field/trigger/Clear.mjs +2 -0
  372. package/src/form/field/trigger/CopyToClipboard.mjs +2 -0
  373. package/src/form/field/trigger/Date.mjs +1 -0
  374. package/src/form/field/trigger/Picker.mjs +1 -0
  375. package/src/form/field/trigger/Search.mjs +1 -0
  376. package/src/form/field/trigger/SpinDown.mjs +2 -0
  377. package/src/form/field/trigger/SpinUp.mjs +1 -0
  378. package/src/form/field/trigger/Time.mjs +2 -0
  379. package/src/functional/_export.mjs +6 -0
  380. package/src/functional/component/Base.mjs +499 -0
  381. package/src/functional/defineComponent.mjs +102 -0
  382. package/src/functional/useConfig.mjs +52 -0
  383. package/src/functional/useEvent.mjs +43 -0
  384. package/src/grid/Body.mjs +20 -1
  385. package/src/grid/Container.mjs +50 -60
  386. package/src/grid/ScrollManager.mjs +2 -0
  387. package/src/grid/VerticalScrollbar.mjs +2 -0
  388. package/src/grid/column/Base.mjs +2 -0
  389. package/src/grid/header/Button.mjs +7 -0
  390. package/src/grid/header/Toolbar.mjs +6 -0
  391. package/src/grid/plugin/AnimateRows.mjs +2 -0
  392. package/src/layout/Base.mjs +3 -0
  393. package/src/layout/Card.mjs +1 -0
  394. package/src/layout/Cube.mjs +11 -1
  395. package/src/layout/Fit.mjs +1 -0
  396. package/src/layout/Flexbox.mjs +7 -0
  397. package/src/layout/Form.mjs +2 -0
  398. package/src/layout/Grid.mjs +1 -0
  399. package/src/layout/HBox.mjs +1 -0
  400. package/src/layout/VBox.mjs +1 -0
  401. package/src/list/Base.mjs +13 -0
  402. package/src/list/Chip.mjs +1 -0
  403. package/src/list/Circle.mjs +2 -0
  404. package/src/list/Color.mjs +1 -0
  405. package/src/list/plugin/Animate.mjs +2 -0
  406. package/src/main/DeltaUpdates.mjs +1 -0
  407. package/src/main/DomEvents.mjs +2 -0
  408. package/src/main/addon/CloneNode.mjs +1 -0
  409. package/src/main/addon/Cookie.mjs +1 -0
  410. package/src/main/addon/GoogleMaps.mjs +1 -0
  411. package/src/main/addon/LocalStorage.mjs +1 -0
  412. package/src/main/addon/MapboxGL.mjs +1 -0
  413. package/src/main/addon/Markdown.mjs +1 -0
  414. package/src/main/addon/Navigator.mjs +1 -0
  415. package/src/main/addon/Popover.mjs +1 -0
  416. package/src/main/addon/Stylesheet.mjs +1 -0
  417. package/src/main/addon/WindowPosition.mjs +1 -0
  418. package/src/manager/Component.mjs +0 -71
  419. package/src/manager/VDomUpdate.mjs +235 -0
  420. package/src/menu/List.mjs +6 -0
  421. package/src/menu/Model.mjs +1 -0
  422. package/src/menu/Panel.mjs +3 -0
  423. package/src/menu/Store.mjs +1 -0
  424. package/src/mixin/DomEvents.mjs +130 -0
  425. package/src/mixin/VdomLifecycle.mjs +667 -0
  426. package/src/plugin/Base.mjs +1 -0
  427. package/src/plugin/Resizable.mjs +2 -0
  428. package/src/selection/Model.mjs +15 -18
  429. package/src/selection/grid/BaseModel.mjs +1 -0
  430. package/src/sitemap/Component.mjs +1 -0
  431. package/src/state/Provider.mjs +98 -70
  432. package/src/state/createHierarchicalDataProxy.mjs +39 -25
  433. package/src/tab/Container.mjs +6 -0
  434. package/src/tab/Strip.mjs +1 -0
  435. package/src/tab/header/Button.mjs +2 -0
  436. package/src/tab/header/EffectButton.mjs +2 -0
  437. package/src/tab/header/Toolbar.mjs +1 -0
  438. package/src/table/Body.mjs +3 -0
  439. package/src/table/Container.mjs +10 -0
  440. package/src/table/header/Button.mjs +8 -0
  441. package/src/table/header/Toolbar.mjs +5 -0
  442. package/src/table/plugin/CellEditing.mjs +1 -0
  443. package/src/toolbar/Base.mjs +4 -0
  444. package/src/toolbar/Breadcrumb.mjs +3 -0
  445. package/src/toolbar/Paging.mjs +5 -0
  446. package/src/tooltip/Base.mjs +2 -0
  447. package/src/tree/List.mjs +3 -0
  448. package/src/util/HashHistory.mjs +1 -0
  449. package/src/util/KeyNavigation.mjs +2 -0
  450. package/src/util/Matrix.mjs +1 -0
  451. package/src/util/VDom.mjs +7 -1
  452. package/src/util/VNode.mjs +7 -1
  453. package/src/util/vdom/TreeBuilder.mjs +129 -0
  454. package/src/vdom/Helper.mjs +35 -23
  455. package/src/vdom/VNode.mjs +4 -6
  456. package/src/worker/App.mjs +1 -0
  457. package/src/worker/Base.mjs +2 -0
  458. package/src/worker/Manager.mjs +2 -0
  459. package/src/worker/ServiceBase.mjs +6 -1
  460. package/test/siesta/siesta.js +5 -2
  461. package/test/siesta/tests/VdomCalendar.mjs +13 -9
  462. package/test/siesta/tests/core/Effect.mjs +10 -14
  463. package/test/siesta/tests/core/EffectBatching.mjs +25 -37
  464. package/test/siesta/tests/state/ProviderNestedDataConfigs.mjs +255 -0
  465. package/test/siesta/tests/state/createHierarchicalDataProxy.mjs +42 -55
  466. package/test/siesta/tests/vdom/VdomAsymmetricUpdates.mjs +366 -0
  467. package/test/siesta/tests/vdom/VdomRealWorldUpdates.mjs +249 -0
  468. package/learn/javascript/NewNode.md +0 -31
@@ -15,12 +15,7 @@ class MockStateProvider extends Base {
15
15
 
16
16
  #dataConfigs = {};
17
17
 
18
- construct(config) {
19
- super.construct(config);
20
- }
21
-
22
18
  afterSetData(value, oldValue) {
23
- console.log(value);
24
19
  if (value) {
25
20
  this.processDataObject(value);
26
21
  }
@@ -74,17 +69,15 @@ StartTest(t => {
74
69
  const provider = Neo.create(MockStateProvider, {data: {name: 'Neo', version: 10}});
75
70
  let effectRunCount = 0;
76
71
 
77
- const effect = new Effect({
78
- fn: () => {
79
- effectRunCount++;
80
- const proxy = createHierarchicalDataProxy(provider);
81
- if (effectRunCount === 1) {
82
- t.is(proxy.name, 'Neo', 'Should get name from proxy (initial)');
83
- t.is(proxy.version, 10, 'Should get version from proxy (initial)');
84
- } else if (effectRunCount === 2) {
85
- t.is(proxy.name, 'Neo.mjs', 'Should get name from proxy (updated)');
86
- t.is(proxy.version, 10, 'Should get version from proxy (unchanged)');
87
- }
72
+ const effect = new Effect(() => {
73
+ effectRunCount++;
74
+ const proxy = createHierarchicalDataProxy(provider);
75
+ if (effectRunCount === 1) {
76
+ t.is(proxy.name, 'Neo', 'Should get name from proxy (initial)');
77
+ t.is(proxy.version, 10, 'Should get version from proxy (initial)');
78
+ } else if (effectRunCount === 2) {
79
+ t.is(proxy.name, 'Neo.mjs', 'Should get name from proxy (updated)');
80
+ t.is(proxy.version, 10, 'Should get version from proxy (unchanged)');
88
81
  }
89
82
  });
90
83
 
@@ -102,17 +95,15 @@ StartTest(t => {
102
95
  const provider = Neo.create(MockStateProvider, {data: {user: {firstName: 'John', lastName: 'Doe'}}});
103
96
  let effectRunCount = 0;
104
97
 
105
- const effect = new Effect({
106
- fn: () => {
107
- effectRunCount++;
108
- const proxy = createHierarchicalDataProxy(provider);
109
- if (effectRunCount === 1) {
110
- t.is(proxy.user.firstName, 'John', 'Should get nested firstName (initial)');
111
- t.is(proxy.user.lastName, 'Doe', 'Should get nested lastName (initial)');
112
- } else if (effectRunCount === 2) {
113
- t.is(proxy.user.firstName, 'Jane', 'Should get nested firstName (updated)');
114
- t.is(proxy.user.lastName, 'Doe', 'Should get nested lastName (unchanged)');
115
- }
98
+ const effect = new Effect(() => {
99
+ effectRunCount++;
100
+ const proxy = createHierarchicalDataProxy(provider);
101
+ if (effectRunCount === 1) {
102
+ t.is(proxy.user.firstName, 'John', 'Should get nested firstName (initial)');
103
+ t.is(proxy.user.lastName, 'Doe', 'Should get nested lastName (initial)');
104
+ } else if (effectRunCount === 2) {
105
+ t.is(proxy.user.firstName, 'Jane', 'Should get nested firstName (updated)');
106
+ t.is(proxy.user.lastName, 'Doe', 'Should get nested lastName (unchanged)');
116
107
  }
117
108
  });
118
109
 
@@ -133,27 +124,25 @@ StartTest(t => {
133
124
 
134
125
  let effectRunCount = 0;
135
126
 
136
- const effect = new Effect({
137
- fn: () => {
138
- effectRunCount++;
139
- const proxy = createHierarchicalDataProxy(childProvider);
140
- if (effectRunCount === 1) {
141
- t.is(proxy.appTitle, 'My App', 'Should get appTitle from parent (initial)');
142
- t.is(proxy.user.firstName, 'Parent', 'Should get firstName from parent (initial)');
143
- t.is(proxy.user.lastName, 'Child', 'Should get lastName from child (initial)');
144
- } else if (effectRunCount === 2) {
145
- t.is(proxy.appTitle, 'New App Title', 'Should get appTitle from parent (updated)');
146
- t.is(proxy.user.firstName, 'Parent', 'Should get firstName from parent (unchanged)');
147
- t.is(proxy.user.lastName, 'Child', 'Should get lastName from child (unchanged)');
148
- } else if (effectRunCount === 3) {
149
- t.is(proxy.appTitle, 'New App Title', 'Should get appTitle from parent (unchanged)');
150
- t.is(proxy.user.firstName, 'Parent', 'Should get firstName from parent (unchanged)');
151
- t.is(proxy.user.lastName, 'New Child', 'Should get lastName from child (updated)');
152
- } else if (effectRunCount === 4) {
153
- t.is(proxy.appTitle, 'New App Title', 'Should get appTitle from parent (unchanged)');
154
- t.is(proxy.user.firstName, 'New Parent', 'Should get firstName from parent (updated)');
155
- t.is(proxy.user.lastName, 'New Child', 'Should get lastName from child (unchanged)');
156
- }
127
+ const effect = new Effect(() => {
128
+ effectRunCount++;
129
+ const proxy = createHierarchicalDataProxy(childProvider);
130
+ if (effectRunCount === 1) {
131
+ t.is(proxy.appTitle, 'My App', 'Should get appTitle from parent (initial)');
132
+ t.is(proxy.user.firstName, 'Parent', 'Should get firstName from parent (initial)');
133
+ t.is(proxy.user.lastName, 'Child', 'Should get lastName from child (initial)');
134
+ } else if (effectRunCount === 2) {
135
+ t.is(proxy.appTitle, 'New App Title', 'Should get appTitle from parent (updated)');
136
+ t.is(proxy.user.firstName, 'Parent', 'Should get firstName from parent (unchanged)');
137
+ t.is(proxy.user.lastName, 'Child', 'Should get lastName from child (unchanged)');
138
+ } else if (effectRunCount === 3) {
139
+ t.is(proxy.appTitle, 'New App Title', 'Should get appTitle from parent (unchanged)');
140
+ t.is(proxy.user.firstName, 'Parent', 'Should get firstName from parent (unchanged)');
141
+ t.is(proxy.user.lastName, 'New Child', 'Should get lastName from child (updated)');
142
+ } else if (effectRunCount === 4) {
143
+ t.is(proxy.appTitle, 'New App Title', 'Should get appTitle from parent (unchanged)');
144
+ t.is(proxy.user.firstName, 'New Parent', 'Should get firstName from parent (updated)');
145
+ t.is(proxy.user.lastName, 'New Child', 'Should get lastName from child (unchanged)');
157
146
  }
158
147
  });
159
148
 
@@ -178,13 +167,11 @@ StartTest(t => {
178
167
  const provider = Neo.create(MockStateProvider, {data: {foo: 'bar'}});
179
168
  let effectRunCount = 0;
180
169
 
181
- const effect = new Effect({
182
- fn: () => {
183
- effectRunCount++;
184
- const proxy = createHierarchicalDataProxy(provider);
185
- t.is(proxy.nonExistent, null, 'Should return null for non-existent property');
186
- t.is(proxy.foo, 'bar', 'Should still get existing data');
187
- }
170
+ const effect = new Effect(() => {
171
+ effectRunCount++;
172
+ const proxy = createHierarchicalDataProxy(provider);
173
+ t.is(proxy.nonExistent, null, 'Should return null for non-existent property');
174
+ t.is(proxy.foo, 'bar', 'Should still get existing data');
188
175
  });
189
176
 
190
177
  t.is(effectRunCount, 1, 'Effect should run once initially');
@@ -0,0 +1,366 @@
1
+ import Neo from '../../../../src/Neo.mjs';
2
+ import * as core from '../../../../src/core/_export.mjs';
3
+ import ComponentManager from '../../../../src/manager/Component.mjs';
4
+ import TreeBuilder from '../../../../src/util/vdom/TreeBuilder.mjs';
5
+ import VDomUpdate from '../../../../src/manager/VDomUpdate.mjs';
6
+ import VdomLifecycle from '../../../../src/mixin/VdomLifecycle.mjs';
7
+ import VdomHelper from '../../../../src/vdom/Helper.mjs';
8
+ import VDomUtil from '../../../../src/util/VDom.mjs';
9
+
10
+ // IMPORTANT: Test with the new standard renderer
11
+ Neo.config.useDomApiRenderer = true;
12
+ VdomHelper.onNeoConfigChange({useDomApiRenderer: true});
13
+
14
+ /**
15
+ * Creates a mock component object for testing.
16
+ * @param {string} id
17
+ * @param {string} parentId
18
+ * @param {Object} vdom
19
+ * @returns {Object} A mock component
20
+ */
21
+ const createMockComponent = (id, parentId, vdom) => {
22
+ const component = {
23
+ id,
24
+ parentId,
25
+ vdom,
26
+ // Add properties from VdomLifecycle that we need to test
27
+ isVdomUpdating: false,
28
+ // By adding the prototype methods to our mock instances, we can test
29
+ // the lifecycle logic without needing full component instantiation.
30
+ hasUpdateCollision: VdomLifecycle.prototype.hasUpdateCollision,
31
+ isParentUpdating : VdomLifecycle.prototype.isParentUpdating,
32
+ };
33
+ // Create the initial vnode from the vdom definition.
34
+ const { vnode } = VdomHelper.create({ vdom });
35
+ component.vnode = vnode;
36
+
37
+ // Register the component BEFORE syncing IDs. This is critical so that
38
+ // a parent's syncVdomIds call can find this component if it's a child.
39
+ ComponentManager.register(component);
40
+ VDomUtil.syncVdomIds(component.vnode, component.vdom);
41
+
42
+ return component;
43
+ };
44
+
45
+ StartTest(t => {
46
+
47
+ t.beforeEach(() => {
48
+ // Reset managers to ensure test isolation
49
+ VDomUpdate.mergedCallbackMap.clear();
50
+ VDomUpdate.postUpdateQueueMap.clear();
51
+ ComponentManager.wrapperNodes.clear();
52
+ ComponentManager.clear();
53
+ });
54
+
55
+ t.it('Should handle asymmetric update with depth 2 using DomApiRenderer', t => {
56
+ // 1. SETUP
57
+ // Create a parent and a child. The parent's vdom references the child via componentId.
58
+ const childVdomInitial = { id: 'child-1', cn: [{ tag: 'span', text: 'Initial' }] };
59
+ const parentVdom = {
60
+ id: 'parent-1',
61
+ cn: [{ componentId: 'child-1' }]
62
+ };
63
+
64
+ // Create components dependency-first (child before parent) to ensure
65
+ // component references can be resolved during VDOM/VNode processing.
66
+ // The `createMockComponent` factory now handles registration.
67
+ let child = createMockComponent('child-1', 'parent-1', childVdomInitial);
68
+ let parent = createMockComponent('parent-1', 'root', parentVdom);
69
+
70
+ // 2. SIMULATE A CHILD-INITIATED UPDATE
71
+ // The child's internal state changes, and it requests to be part of the parent's next update.
72
+ VDomUpdate.registerMerged(
73
+ parent.id,
74
+ child.id,
75
+ 1, // childUpdateDepth
76
+ 1 // distance
77
+ );
78
+
79
+ // The child's vdom has now changed. We update our mock to reflect this.
80
+ // By mutating the existing vdom object, we ensure stable IDs are preserved for diffing.
81
+ child.vdom.cn[0].text = 'Updated';
82
+
83
+ // 3. SIMULATE THE PARENT'S UPDATE LIFECYCLE
84
+ // The parent calculates the required depth for the update.
85
+ const adjustedDepth = VDomUpdate.getAdjustedUpdateDepth(parent.id);
86
+ t.is(adjustedDepth, 2, 'Adjusted update depth should be 2 to include direct children');
87
+
88
+ // The parent builds an asymmetric VDOM tree. TreeBuilder will find the updated
89
+ // child.vdom via the ComponentManager.
90
+ const newAsymmetricVdom = TreeBuilder.getVdomTree(parent.vdom, adjustedDepth);
91
+
92
+ // Verify the created tree has the child's *new* vdom
93
+ t.is(newAsymmetricVdom.cn[0].id, 'child-1', 'The child component VDOM is expanded in the asymmetric tree');
94
+ t.is(newAsymmetricVdom.cn[0].cn[0].text, 'Updated', 'The expanded VDOM reflects the childs updated state');
95
+
96
+ // 4. GENERATE DELTAS
97
+ // VdomHelper diffs the new, expanded tree against the parent's OLD vnode.
98
+ // The old vnode must also be expanded to the same depth to ensure a correct diff.
99
+ const oldAsymmetricVnode = TreeBuilder.getVnodeTree(parent.vnode, adjustedDepth);
100
+ const { deltas } = VdomHelper.update({
101
+ vdom : newAsymmetricVdom,
102
+ vnode: oldAsymmetricVnode
103
+ });
104
+
105
+ // 5. ASSERTIONS
106
+ // For useDomApiRenderer=true, a text change results in a delta updating
107
+ // the `textContent` property of the parent element's vnode.
108
+ t.is(deltas.length, 1, 'Should generate exactly one delta for the text change');
109
+ const spanVnode = oldAsymmetricVnode.childNodes[0].childNodes[0];
110
+ const delta = deltas[0];
111
+
112
+ t.is(delta.id, spanVnode.id, 'Delta targets the correct span element');
113
+ t.is(delta.textContent, 'Updated', 'The new text content should be correct');
114
+ t.is(Object.keys(delta).length, 2, 'Delta has the correct shape (id, textContent)');
115
+ });
116
+
117
+ t.it('Should handle nested asymmetric update (grandchild update)', t => {
118
+ // 1. SETUP
119
+ const grandchildVdomInitial = { id: 'grandchild-1', cn: [{ tag: 'span', text: 'Initial' }] };
120
+ const childVdom = {
121
+ id: 'child-1',
122
+ cn: [{ componentId: 'grandchild-1' }]
123
+ };
124
+ const parentVdom = {
125
+ id: 'parent-1',
126
+ cn: [{ componentId: 'child-1' }]
127
+ };
128
+
129
+ // Create components dependency-first (grandchild -> child -> parent) to ensure
130
+ // component references can be resolved during VDOM/VNode processing.
131
+ const grandchild = createMockComponent('grandchild-1', 'child-1', grandchildVdomInitial);
132
+ createMockComponent('child-1', 'parent-1', childVdom);
133
+ let parent = createMockComponent('parent-1', 'root', parentVdom);
134
+
135
+ // 2. SIMULATE A GRANDCHILD-INITIATED UPDATE
136
+ // The grandchild's state changes. It is at a distance of 2 from the updating parent.
137
+ VDomUpdate.registerMerged(
138
+ parent.id,
139
+ grandchild.id,
140
+ 1, // grandchild's own updateDepth
141
+ 2 // distance from parent
142
+ );
143
+
144
+ // The grandchild's vdom has now changed.
145
+ // By mutating the existing vdom object, we ensure stable IDs are preserved for diffing.
146
+ grandchild.vdom.cn[0].text = 'Updated';
147
+
148
+ // 3. SIMULATE THE PARENT'S UPDATE LIFECYCLE
149
+ // The required depth for the parent should be 3 to expand down to the grandchild.
150
+ const adjustedDepth = VDomUpdate.getAdjustedUpdateDepth(parent.id);
151
+ t.is(adjustedDepth, 3, 'Adjusted update depth should be 3 to include grandchild');
152
+
153
+ // The parent builds an asymmetric VDOM tree.
154
+ const newAsymmetricVdom = TreeBuilder.getVdomTree(parent.vdom, adjustedDepth);
155
+
156
+ // Verify the created tree has the grandchild's *new* vdom
157
+ const expandedChild = newAsymmetricVdom.cn[0];
158
+ const expandedGrandchild = expandedChild.cn[0];
159
+ t.is(expandedGrandchild.id, 'grandchild-1', 'The grandchild component VDOM is expanded in the asymmetric tree');
160
+ t.is(expandedGrandchild.cn[0].text, 'Updated', 'The expanded VDOM reflects the grandchilds updated state');
161
+
162
+ // 4. GENERATE DELTAS
163
+ const oldAsymmetricVnode = TreeBuilder.getVnodeTree(parent.vnode, adjustedDepth);
164
+ const { deltas } = VdomHelper.update({
165
+ vdom : newAsymmetricVdom,
166
+ vnode: oldAsymmetricVnode
167
+ });
168
+
169
+ // 5. ASSERTIONS
170
+ // For useDomApiRenderer=true, a text change results in a delta updating
171
+ // the `textContent` property of the parent element's vnode.
172
+ t.is(deltas.length, 1, 'Should generate exactly one delta for the text change');
173
+ const spanVnode = oldAsymmetricVnode.childNodes[0].childNodes[0].childNodes[0];
174
+ const delta = deltas[0];
175
+
176
+ t.is(delta.id, spanVnode.id, 'Delta targets the correct span element');
177
+ t.is(delta.textContent, 'Updated', 'The new text content should be correct');
178
+ t.is(Object.keys(delta).length, 2, 'Delta has the correct shape (id, textContent)');
179
+ });
180
+
181
+ t.it('Should handle structural change in a deeply nested component', t => {
182
+ // 1. SETUP
183
+ const grandchildVdomInitial = { id: 'grandchild-1', cn: [{ tag: 'span', text: 'Initial' }] };
184
+ const childVdom = {
185
+ id: 'child-1',
186
+ cn: [{ componentId: 'grandchild-1' }]
187
+ };
188
+ const parentVdom = {
189
+ id: 'parent-1',
190
+ cn: [{ componentId: 'child-1' }]
191
+ };
192
+
193
+ // Create components dependency-first
194
+ let grandchild = createMockComponent('grandchild-1', 'child-1', grandchildVdomInitial);
195
+ createMockComponent('child-1', 'parent-1', childVdom);
196
+ let parent = createMockComponent('parent-1', 'root', parentVdom);
197
+
198
+ // 2. SIMULATE A GRANDCHILD-INITIATED UPDATE
199
+ VDomUpdate.registerMerged(
200
+ parent.id,
201
+ grandchild.id,
202
+ 1, // grandchild's own updateDepth
203
+ 2 // distance from parent
204
+ );
205
+
206
+ // The grandchild's vdom has a structural change.
207
+ grandchild.vdom.cn.push({ id: 'new-node', tag: 'div', text: 'New Node' });
208
+
209
+ // 3. SIMULATE THE PARENT'S UPDATE LIFECYCLE
210
+ const adjustedDepth = VDomUpdate.getAdjustedUpdateDepth(parent.id);
211
+ t.is(adjustedDepth, 3, 'Adjusted update depth should be 3 to include grandchild');
212
+
213
+ const newAsymmetricVdom = TreeBuilder.getVdomTree(parent.vdom, adjustedDepth);
214
+ const oldAsymmetricVnode = TreeBuilder.getVnodeTree(parent.vnode, adjustedDepth);
215
+
216
+ // Verify the new VDOM structure
217
+ const expandedGrandchildVdom = newAsymmetricVdom.cn[0].cn[0];
218
+ t.is(expandedGrandchildVdom.cn.length, 2, 'New VDOM for grandchild has 2 children');
219
+ t.is(expandedGrandchildVdom.cn[1].id, 'new-node', 'New node is present in the asymmetric VDOM');
220
+
221
+ // 4. GENERATE DELTAS
222
+ const { deltas } = VdomHelper.update({
223
+ vdom : newAsymmetricVdom,
224
+ vnode: oldAsymmetricVnode
225
+ });
226
+
227
+ // 5. ASSERTIONS
228
+ t.is(deltas.length, 1, 'Should generate one delta for the insertion');
229
+ const delta = deltas[0];
230
+ const newNodeVdom = expandedGrandchildVdom.cn[1];
231
+
232
+ t.is(delta.action, 'insertNode', 'Delta action should be insertNode');
233
+ t.is(delta.parentId, grandchild.vdom.id, 'Delta parentId should be the grandchild');
234
+ t.is(delta.index, 1, 'Delta index should be 1');
235
+
236
+ // For DomApiRenderer, the vnode is passed directly.
237
+ t.is(delta.vnode.id, newNodeVdom.id, 'Inserted vnode has the correct ID');
238
+ t.is(delta.vnode.textContent, 'New Node', 'Inserted vnode has the correct text');
239
+ });
240
+
241
+ t.it('Should handle update collision (isParentUpdating)', t => {
242
+ // 1. SETUP
243
+ const childVdom = { id: 'child-1', text: 'child' };
244
+ const parentVdom = { id: 'parent-1', cn: [{ componentId: 'child-1' }] };
245
+
246
+ let child = createMockComponent('child-1', 'parent-1', childVdom);
247
+ let parent = createMockComponent('parent-1', 'root', parentVdom);
248
+
249
+ // 2. SIMULATE A PARENT UPDATE IN PROGRESS
250
+ // This is the state during a real update, before post-processing.
251
+ parent.isVdomUpdating = true;
252
+ VDomUpdate.registerInFlightUpdate(parent.id, 2);
253
+
254
+ // 3. SIMULATE A CHILD-INITIATED UPDATE (during the parent's update)
255
+ let hasCollision = child.isParentUpdating(child.parentId, () => {});
256
+
257
+ // 4. ASSERTIONS
258
+ t.ok(hasCollision, 'isParentUpdating should return true, detecting a collision');
259
+ const postUpdateQueue = VDomUpdate.postUpdateQueueMap.get(parent.id);
260
+ t.ok(postUpdateQueue, 'Parent should have a post-update queue');
261
+ t.is(postUpdateQueue.children.length, 1, 'Post-update queue should have one entry');
262
+ t.is(postUpdateQueue.children[0].childId, child.id, 'The queued item should be the child component');
263
+ });
264
+
265
+ t.it('Should not detect a collision if updateDepth is insufficient', t => {
266
+ // 1. SETUP
267
+ const grandchildVdom = { id: 'grandchild-1', text: 'grandchild' };
268
+ const childVdom = { id: 'child-1', cn: [{ componentId: 'grandchild-1' }] };
269
+ const parentVdom = { id: 'parent-1', cn: [{ componentId: 'child-1' }] };
270
+
271
+ let grandchild = createMockComponent('grandchild-1', 'child-1', grandchildVdom);
272
+ createMockComponent('child-1', 'parent-1', childVdom);
273
+ let parent = createMockComponent('parent-1', 'root', parentVdom);
274
+
275
+ // 2. SIMULATE A PARENT UPDATE IN PROGRESS
276
+ parent.isVdomUpdating = true;
277
+ VDomUpdate.registerInFlightUpdate(parent.id, 2);
278
+
279
+ // 3. SIMULATE A GRANDCHILD-INITIATED UPDATE
280
+ // The grandchild is at distance 2 from the parent. hasUpdateCollision(2, 2) should be false.
281
+ let hasCollision = grandchild.isParentUpdating(grandchild.parentId, () => {});
282
+
283
+ // 4. ASSERTIONS
284
+ t.notOk(hasCollision, 'isParentUpdating should return false, no collision detected');
285
+ const postUpdateQueue = VDomUpdate.postUpdateQueueMap.get(parent.id);
286
+ t.notOk(postUpdateQueue, 'Parent should not have a post-update queue');
287
+ });
288
+
289
+ t.it('Should handle merged updates from multiple non-contiguous children', t => {
290
+ // 1. SETUP
291
+ // Parent -> Child1 -> Grandchild1
292
+ // Parent -> Child2
293
+ // Parent -> Child3
294
+ const grandchildVdom = { id: 'grandchild-1', cn: [{ tag: 'span', text: 'Initial GC' }] };
295
+ const child1Vdom = { id: 'child-1', cn: [{ componentId: 'grandchild-1' }] };
296
+ const child2Vdom = { id: 'child-2', text: 'Initial C2' };
297
+ const child3Vdom = { id: 'child-3', text: 'Initial C3' };
298
+ const parentVdom = {
299
+ id: 'parent-1',
300
+ cn: [
301
+ { componentId: 'child-1' },
302
+ { componentId: 'child-2' },
303
+ { componentId: 'child-3' }
304
+ ]
305
+ };
306
+
307
+ // Create components
308
+ let grandchild1 = createMockComponent('grandchild-1', 'child-1', grandchildVdom);
309
+ createMockComponent('child-1', 'parent-1', child1Vdom);
310
+ createMockComponent('child-2', 'parent-1', child2Vdom);
311
+ let child3 = createMockComponent('child-3', 'parent-1', child3Vdom);
312
+ let parent = createMockComponent('parent-1', 'root', parentVdom);
313
+
314
+ // 2. SIMULATE MULTIPLE MERGED UPDATES
315
+ // Grandchild1 (at distance 2) requests an update
316
+ VDomUpdate.registerMerged(parent.id, grandchild1.id, 1, 2);
317
+ // Child3 (at distance 1) requests an update
318
+ VDomUpdate.registerMerged(parent.id, child3.id, 1, 1);
319
+
320
+ // Make the changes to the source vdoms
321
+ grandchild1.vdom.cn[0].text = 'Updated GC';
322
+ child3.vdom.text = 'Updated C3';
323
+
324
+ // 3. SIMULATE THE PARENT'S UPDATE LIFECYCLE
325
+ const adjustedDepth = VDomUpdate.getAdjustedUpdateDepth(parent.id);
326
+ // Depth for GC1 is 2(dist) + 1(depth) = 3. Depth for C3 is 1(dist) + 1(depth) = 2.
327
+ // The max should be taken.
328
+ t.is(adjustedDepth, 3, 'Adjusted update depth should be 3, the max required by children');
329
+
330
+ const newAsymmetricVdom = TreeBuilder.getVdomTree(parent.vdom, adjustedDepth);
331
+ const oldAsymmetricVnode = TreeBuilder.getVnodeTree(parent.vnode, adjustedDepth);
332
+
333
+ // Verify the new VDOM structure is correctly expanded.
334
+ // TreeBuilder expands based on depth, so non-updating siblings within the depth are also expanded.
335
+ const [expChild1, expChild2, expChild3] = newAsymmetricVdom.cn;
336
+ // Child1 should be expanded to reveal Grandchild1
337
+ t.is(expChild1.cn[0].id, 'grandchild-1', 'Child1 is expanded');
338
+ t.is(expChild1.cn[0].cn[0].text, 'Updated GC', 'Grandchild1 vdom is updated');
339
+ // Child2 is also expanded because the adjustedDepth of 3 is enough to reach it
340
+ t.notOk(expChild2.componentId, 'Child2 IS expanded due to update depth');
341
+ t.is(expChild2.text, 'Initial C2', 'Child2 is fully expanded');
342
+ // Child3 should be expanded as it requested the update
343
+ t.is(expChild3.text, 'Updated C3', 'Child3 vdom is updated');
344
+
345
+ // 4. GENERATE DELTAS
346
+ const { deltas } = VdomHelper.update({
347
+ vdom: newAsymmetricVdom,
348
+ vnode: oldAsymmetricVnode
349
+ });
350
+
351
+ // 5. ASSERTIONS
352
+ t.is(deltas.length, 2, 'Should generate two deltas for the two changes');
353
+
354
+ const gcSpanVnode = oldAsymmetricVnode.childNodes[0].childNodes[0].childNodes[0];
355
+ const c3Vnode = oldAsymmetricVnode.childNodes[2];
356
+
357
+ const delta1 = deltas.find(d => d.id === gcSpanVnode.id);
358
+ const delta2 = deltas.find(d => d.id === c3Vnode.id);
359
+
360
+ t.ok(delta1, 'A delta for the grandchild change should exist');
361
+ t.is(delta1.textContent, 'Updated GC', 'Grandchild text delta is correct');
362
+
363
+ t.ok(delta2, 'A delta for the child3 change should exist');
364
+ t.is(delta2.textContent, 'Updated C3', 'Child3 text delta is correct');
365
+ });
366
+ });