neo.mjs 10.0.0-beta.4 → 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 (485) hide show
  1. package/.github/RELEASE_NOTES/v10.0.0-beta.4.md +2 -2
  2. package/.github/RELEASE_NOTES/v10.0.0-beta.5.md +70 -0
  3. package/.github/RELEASE_NOTES/v10.0.0-beta.6.md +48 -0
  4. package/.github/epic-functional-components.md +498 -0
  5. package/.github/ticket-asymmetric-vdom-updates.md +122 -0
  6. package/README.md +0 -3
  7. package/ServiceWorker.mjs +2 -2
  8. package/apps/colors/store/Colors.mjs +1 -0
  9. package/apps/colors/view/GridContainer.mjs +3 -0
  10. package/apps/colors/view/HeaderToolbar.mjs +2 -0
  11. package/apps/colors/view/Viewport.mjs +3 -0
  12. package/apps/covid/view/FooterContainer.mjs +3 -0
  13. package/apps/covid/view/GalleryContainer.mjs +2 -0
  14. package/apps/covid/view/GalleryContainerController.mjs +1 -0
  15. package/apps/covid/view/HeaderContainer.mjs +2 -0
  16. package/apps/covid/view/HelixContainer.mjs +2 -0
  17. package/apps/covid/view/HelixContainerController.mjs +1 -0
  18. package/apps/covid/view/MainContainer.mjs +3 -0
  19. package/apps/covid/view/TableContainer.mjs +3 -0
  20. package/apps/covid/view/TableContainerController.mjs +1 -0
  21. package/apps/covid/view/WorldMapContainer.mjs +2 -0
  22. package/apps/covid/view/country/Gallery.mjs +3 -0
  23. package/apps/covid/view/country/Helix.mjs +8 -0
  24. package/apps/covid/view/country/HistoricalDataTable.mjs +1 -0
  25. package/apps/covid/view/country/Table.mjs +2 -0
  26. package/apps/covid/view/mapboxGl/Component.mjs +1 -0
  27. package/apps/covid/view/mapboxGl/Container.mjs +2 -0
  28. package/apps/email/EPIC_PLAN.md +58 -0
  29. package/apps/email/neo-config.json +2 -2
  30. package/apps/email/store/Emails.mjs +11 -1
  31. package/apps/email/view/ComposeView.mjs +44 -0
  32. package/apps/email/view/MainView.mjs +89 -0
  33. package/apps/email/view/Viewport.mjs +4 -33
  34. package/apps/email/view/ViewportStateProvider.mjs +3 -3
  35. package/apps/form/store/SideNav.mjs +1 -0
  36. package/apps/form/view/FormContainer.mjs +1 -0
  37. package/apps/form/view/FormPageContainer.mjs +2 -0
  38. package/apps/form/view/SideNavList.mjs +1 -0
  39. package/apps/form/view/Viewport.mjs +3 -0
  40. package/apps/portal/childapps/preview/MainContainer.mjs +1 -0
  41. package/apps/portal/index.html +1 -1
  42. package/apps/portal/store/BlogPosts.mjs +2 -0
  43. package/apps/portal/store/Content.mjs +1 -0
  44. package/apps/portal/store/ContentSections.mjs +1 -0
  45. package/apps/portal/store/Examples.mjs +1 -0
  46. package/apps/portal/view/HeaderToolbar.mjs +1 -0
  47. package/apps/portal/view/Viewport.mjs +5 -0
  48. package/apps/portal/view/ViewportController.mjs +8 -2
  49. package/apps/portal/view/about/Container.mjs +2 -0
  50. package/apps/portal/view/about/MemberContainer.mjs +7 -0
  51. package/apps/portal/view/blog/Container.mjs +2 -0
  52. package/apps/portal/view/blog/List.mjs +2 -0
  53. package/apps/portal/view/examples/List.mjs +1 -0
  54. package/apps/portal/view/examples/TabContainer.mjs +4 -0
  55. package/apps/portal/view/home/ContentBox.mjs +3 -0
  56. package/apps/portal/view/home/FeatureSection.mjs +8 -0
  57. package/apps/portal/view/home/FooterContainer.mjs +4 -1
  58. package/apps/portal/view/home/MainContainer.mjs +2 -0
  59. package/apps/portal/view/home/parts/AfterMath.mjs +2 -0
  60. package/apps/portal/view/home/parts/BaseContainer.mjs +1 -0
  61. package/apps/portal/view/home/parts/Colors.mjs +4 -0
  62. package/apps/portal/view/home/parts/Features.mjs +2 -0
  63. package/apps/portal/view/home/parts/Helix.mjs +5 -0
  64. package/apps/portal/view/home/parts/How.mjs +4 -0
  65. package/apps/portal/view/home/parts/MainNeo.mjs +1 -0
  66. package/apps/portal/view/home/parts/References.mjs +2 -0
  67. package/apps/portal/view/learn/ContentComponent.mjs +11 -5
  68. package/apps/portal/view/learn/ContentTreeList.mjs +2 -0
  69. package/apps/portal/view/learn/CubeLayoutButton.mjs +1 -0
  70. package/apps/portal/view/learn/MainContainer.mjs +4 -0
  71. package/apps/portal/view/learn/PageContainer.mjs +2 -0
  72. package/apps/portal/view/learn/PageSectionsContainer.mjs +3 -0
  73. package/apps/portal/view/learn/PageSectionsList.mjs +1 -0
  74. package/apps/portal/view/services/Component.mjs +1 -0
  75. package/apps/realworld/api/Base.mjs +1 -0
  76. package/apps/realworld/view/HeaderComponent.mjs +4 -0
  77. package/apps/realworld/view/HomeComponent.mjs +7 -0
  78. package/apps/realworld/view/MainContainer.mjs +2 -0
  79. package/apps/realworld/view/MainContainerController.mjs +2 -0
  80. package/apps/realworld/view/article/CommentComponent.mjs +3 -0
  81. package/apps/realworld/view/article/Component.mjs +17 -10
  82. package/apps/realworld/view/article/CreateCommentComponent.mjs +2 -0
  83. package/apps/realworld/view/article/CreateComponent.mjs +5 -0
  84. package/apps/realworld/view/article/PreviewComponent.mjs +9 -0
  85. package/apps/realworld/view/article/TagListComponent.mjs +2 -0
  86. package/apps/realworld/view/user/ProfileComponent.mjs +7 -0
  87. package/apps/realworld/view/user/SettingsComponent.mjs +5 -0
  88. package/apps/realworld/view/user/SignUpComponent.mjs +3 -0
  89. package/apps/realworld2/api/Base.mjs +1 -0
  90. package/apps/realworld2/view/FooterComponent.mjs +1 -0
  91. package/apps/realworld2/view/HeaderToolbar.mjs +3 -0
  92. package/apps/realworld2/view/HomeContainer.mjs +1 -0
  93. package/apps/realworld2/view/MainContainer.mjs +2 -0
  94. package/apps/realworld2/view/MainContainerController.mjs +1 -0
  95. package/apps/realworld2/view/article/Helix.mjs +1 -0
  96. package/apps/realworld2/view/article/PreviewComponent.mjs +9 -0
  97. package/apps/realworld2/view/article/PreviewList.mjs +1 -0
  98. package/apps/realworld2/view/article/TagListComponent.mjs +2 -0
  99. package/apps/route/view/CenterContainer.mjs +1 -0
  100. package/apps/route/view/MainView.mjs +1 -0
  101. package/apps/sharedcovid/childapps/sharedcovidchart/MainContainer.mjs +1 -0
  102. package/apps/sharedcovid/childapps/sharedcovidgallery/MainContainer.mjs +1 -0
  103. package/apps/sharedcovid/childapps/sharedcovidhelix/MainContainer.mjs +1 -0
  104. package/apps/sharedcovid/childapps/sharedcovidmap/MainContainer.mjs +1 -0
  105. package/apps/sharedcovid/view/FooterContainer.mjs +3 -0
  106. package/apps/sharedcovid/view/GalleryContainer.mjs +2 -0
  107. package/apps/sharedcovid/view/GalleryContainerController.mjs +1 -0
  108. package/apps/sharedcovid/view/HeaderContainer.mjs +2 -0
  109. package/apps/sharedcovid/view/HelixContainer.mjs +2 -0
  110. package/apps/sharedcovid/view/HelixContainerController.mjs +1 -0
  111. package/apps/sharedcovid/view/MainContainer.mjs +3 -0
  112. package/apps/sharedcovid/view/TableContainer.mjs +3 -0
  113. package/apps/sharedcovid/view/TableContainerController.mjs +1 -0
  114. package/apps/sharedcovid/view/WorldMapContainer.mjs +2 -0
  115. package/apps/sharedcovid/view/country/Gallery.mjs +3 -0
  116. package/apps/sharedcovid/view/country/Helix.mjs +8 -0
  117. package/apps/sharedcovid/view/country/HistoricalDataTable.mjs +1 -0
  118. package/apps/sharedcovid/view/country/Table.mjs +2 -0
  119. package/apps/sharedcovid/view/mapboxGl/Component.mjs +1 -0
  120. package/apps/sharedcovid/view/mapboxGl/Container.mjs +2 -0
  121. package/apps/shareddialog/childapps/shareddialog2/view/MainContainer.mjs +2 -0
  122. package/apps/shareddialog/view/DemoDialog.mjs +2 -0
  123. package/apps/shareddialog/view/MainContainer.mjs +2 -0
  124. package/apps/shareddialog/view/MainContainerController.mjs +1 -0
  125. package/buildScripts/addReactiveTags.mjs +191 -0
  126. package/buildScripts/checkReactiveTags.mjs +160 -0
  127. package/docs/app/store/Api.mjs +1 -0
  128. package/docs/app/store/Examples.mjs +1 -0
  129. package/docs/app/view/ApiTreeList.mjs +1 -0
  130. package/docs/app/view/ContentTabContainer.mjs +2 -0
  131. package/docs/app/view/ExamplesTreeList.mjs +2 -0
  132. package/docs/app/view/HeaderContainer.mjs +3 -0
  133. package/docs/app/view/MainContainer.mjs +5 -0
  134. package/docs/app/view/classdetails/HeaderComponent.mjs +1 -0
  135. package/docs/app/view/classdetails/MainContainer.mjs +3 -0
  136. package/docs/app/view/classdetails/MembersList.mjs +5 -0
  137. package/docs/app/view/classdetails/SourceViewComponent.mjs +2 -0
  138. package/examples/ConfigurationViewport.mjs +14 -8
  139. package/examples/button/effect/MainContainer.mjs +207 -0
  140. package/examples/button/effect/app.mjs +6 -0
  141. package/examples/button/effect/index.html +11 -0
  142. package/examples/button/effect/neo-config.json +6 -0
  143. package/examples/calendar/weekview/MainContainer.mjs +4 -0
  144. package/examples/component/coronaGallery/CountryGallery.mjs +2 -0
  145. package/examples/component/coronaGallery/CountryStore.mjs +1 -0
  146. package/examples/component/coronaGallery/Viewport.mjs +3 -0
  147. package/examples/component/coronaGallery/ViewportController.mjs +1 -0
  148. package/examples/component/coronaHelix/CountryHelix.mjs +7 -0
  149. package/examples/component/coronaHelix/MainContainer.mjs +1 -0
  150. package/examples/component/gallery/ImageStore.mjs +1 -0
  151. package/examples/component/helix/ImageStore.mjs +1 -0
  152. package/examples/component/helix/Viewport.mjs +3 -0
  153. package/examples/component/helix/ViewportController.mjs +1 -0
  154. package/examples/component/multiWindowCoronaGallery/childapp/Viewport.mjs +1 -0
  155. package/examples/component/multiWindowHelix/childapp/Viewport.mjs +1 -0
  156. package/examples/component/wrapper/googleMaps/MapComponent.mjs +2 -0
  157. package/examples/core/config/MainContainer.mjs +2 -0
  158. package/examples/dialog/DemoDialog.mjs +2 -0
  159. package/examples/dialog/MainContainer.mjs +1 -0
  160. package/examples/form/field/color/MainStore.mjs +1 -0
  161. package/examples/functional/defineComponent/Component.mjs +18 -0
  162. package/examples/functional/defineComponent/MainContainer.mjs +41 -0
  163. package/examples/functional/defineComponent/app.mjs +6 -0
  164. package/examples/functional/defineComponent/index.html +11 -0
  165. package/examples/functional/defineComponent/neo-config.json +6 -0
  166. package/examples/functional/hostComponent/Component.mjs +32 -0
  167. package/examples/functional/hostComponent/MainContainer.mjs +48 -0
  168. package/examples/functional/hostComponent/app.mjs +6 -0
  169. package/examples/functional/hostComponent/index.html +11 -0
  170. package/examples/functional/hostComponent/neo-config.json +6 -0
  171. package/examples/grid/animatedRowSorting/Viewport.mjs +1 -1
  172. package/examples/grid/bigData/ControlsContainer.mjs +3 -0
  173. package/examples/grid/bigData/GridContainer.mjs +4 -2
  174. package/examples/grid/bigData/MainContainer.mjs +2 -0
  175. package/examples/grid/bigData/MainModel.mjs +1 -0
  176. package/examples/grid/bigData/MainStore.mjs +3 -0
  177. package/examples/grid/cellEditing/MainContainer.mjs +1 -1
  178. package/examples/grid/container/MainContainer.mjs +1 -1
  179. package/examples/grid/covid/GridContainer.mjs +3 -0
  180. package/examples/grid/covid/MainContainer.mjs +2 -0
  181. package/examples/grid/covid/Store.mjs +1 -0
  182. package/examples/grid/nestedRecordFields/EditUserDialog.mjs +3 -0
  183. package/examples/grid/nestedRecordFields/Viewport.mjs +3 -1
  184. package/examples/list/animate/List.mjs +4 -0
  185. package/examples/list/animate/MainContainer.mjs +2 -0
  186. package/examples/list/circle/MainStore.mjs +1 -0
  187. package/examples/list/color/MainStore.mjs +1 -0
  188. package/examples/preloadingAssets/view/MainContainer.mjs +2 -0
  189. package/examples/stateProvider/advanced/MainContainer.mjs +1 -0
  190. package/examples/stateProvider/dialog/EditUserDialog.mjs +2 -0
  191. package/examples/stateProvider/dialog/MainContainer.mjs +1 -0
  192. package/examples/stateProvider/extendedClass/MainContainer.mjs +2 -0
  193. package/examples/stateProvider/inline/MainContainer.mjs +1 -0
  194. package/examples/stateProvider/inlineNoStateProvider/MainContainer.mjs +1 -0
  195. package/examples/stateProvider/inlineNoStateProvider/MainContainerController.mjs +2 -0
  196. package/examples/stateProvider/multiWindow/EditUserDialog.mjs +3 -0
  197. package/examples/stateProvider/multiWindow/MainContainer.mjs +1 -0
  198. package/examples/stateProvider/multiWindow/Viewport.mjs +1 -0
  199. package/examples/stateProvider/nestedData/MainContainer.mjs +1 -0
  200. package/examples/stateProvider/table/MainContainer.mjs +1 -0
  201. package/examples/table/covid/MainContainer.mjs +2 -0
  202. package/examples/table/covid/Store.mjs +1 -0
  203. package/examples/table/covid/TableContainer.mjs +3 -0
  204. package/examples/table/nestedRecordFields/EditUserDialog.mjs +3 -0
  205. package/examples/table/nestedRecordFields/Viewport.mjs +1 -0
  206. package/examples/todoList/version1/MainComponent.mjs +1 -1
  207. package/examples/toolbar/breadcrumb/view/MainContainer.mjs +2 -0
  208. package/examples/toolbar/paging/store/Users.mjs +1 -0
  209. package/examples/toolbar/paging/view/AddUserDialog.mjs +3 -0
  210. package/examples/toolbar/paging/view/MainContainer.mjs +3 -0
  211. package/examples/treeAccordion/MainContainer.mjs +2 -2
  212. package/examples/worker/task/MainContainer.mjs +1 -0
  213. package/learn/Glossary.md +1 -0
  214. package/learn/UsingTheseTopics.md +1 -0
  215. package/learn/benefits/ConfigSystem.md +2 -0
  216. package/learn/benefits/Effort.md +1 -0
  217. package/learn/benefits/Features.md +1 -0
  218. package/learn/benefits/FormsEngine.md +1 -0
  219. package/learn/benefits/FourEnvironments.md +2 -0
  220. package/learn/benefits/Introduction.md +2 -0
  221. package/learn/benefits/MultiWindow.md +3 -1
  222. package/learn/benefits/OffTheMainThread.md +2 -0
  223. package/learn/benefits/Quick.md +2 -0
  224. package/learn/benefits/RPCLayer.md +2 -0
  225. package/learn/benefits/Speed.md +2 -0
  226. package/learn/comparisons/NeoVsAngular.md +90 -0
  227. package/learn/comparisons/NeoVsExtJs.md +178 -0
  228. package/learn/comparisons/NeoVsNextJs.md +124 -0
  229. package/learn/comparisons/NeoVsReact.md +95 -0
  230. package/learn/comparisons/NeoVsSolid.md +78 -0
  231. package/learn/comparisons/NeoVsVue.md +92 -0
  232. package/learn/comparisons/Overview.md +46 -0
  233. package/learn/gettingstarted/ComponentModels.md +2 -0
  234. package/learn/gettingstarted/Config.md +2 -0
  235. package/learn/gettingstarted/DescribingTheUI.md +2 -0
  236. package/learn/gettingstarted/Events.md +2 -0
  237. package/learn/gettingstarted/Extending.md +2 -0
  238. package/learn/gettingstarted/References.md +2 -0
  239. package/learn/gettingstarted/Setup.md +3 -2
  240. package/learn/gettingstarted/Workspaces.md +2 -0
  241. package/learn/guides/datahandling/Collections.md +1 -0
  242. package/learn/guides/datahandling/Records.md +1 -0
  243. package/learn/guides/datahandling/StateProviders.md +131 -16
  244. package/learn/guides/datahandling/Tables.md +1 -1
  245. package/learn/guides/fundamentals/ConfigSystemDeepDive.md +1 -0
  246. package/learn/guides/fundamentals/DeclarativeComponentTreesVsImperativeVdom.md +2 -0
  247. package/learn/guides/fundamentals/DeclarativeVDOMWithEffects.md +168 -0
  248. package/learn/guides/fundamentals/ExtendingNeoClasses.md +1 -0
  249. package/learn/guides/fundamentals/InstanceLifecycle.md +3 -1
  250. package/learn/guides/fundamentals/MainThreadAddons.md +2 -0
  251. package/learn/guides/specificfeatures/Mixins.md +3 -1
  252. package/learn/guides/specificfeatures/MultiWindow.md +3 -1
  253. package/learn/guides/specificfeatures/PortalApp.md +2 -0
  254. package/learn/guides/uibuildingblocks/ComponentsAndContainers.md +2 -0
  255. package/learn/guides/uibuildingblocks/CustomComponents.md +2 -0
  256. package/learn/guides/uibuildingblocks/Layouts.md +2 -0
  257. package/learn/guides/uibuildingblocks/WorkingWithVDom.md +2 -0
  258. package/learn/guides/userinteraction/Forms.md +2 -0
  259. package/learn/guides/userinteraction/events/CustomEvents.md +2 -1
  260. package/learn/guides/userinteraction/events/DomEvents.md +2 -0
  261. package/learn/javascript/ClassFeatures.md +4 -3
  262. package/learn/javascript/Classes.md +10 -13
  263. package/learn/javascript/Overrides.md +10 -6
  264. package/learn/javascript/Super.md +12 -8
  265. package/learn/tree.json +71 -63
  266. package/learn/tutorials/Earthquakes.md +2 -0
  267. package/learn/tutorials/RSP.md +3 -1
  268. package/learn/tutorials/TodoList.md +103 -7
  269. package/package.json +6 -4
  270. package/resources/scss/src/apps/email/ComposeView.scss +16 -0
  271. package/resources/scss/src/apps/email/MainView.scss +5 -0
  272. package/resources/scss/src/apps/portal/learn/ContentComponent.scss +5 -4
  273. package/src/DefaultConfig.mjs +12 -2
  274. package/src/Main.mjs +1 -0
  275. package/src/Neo.mjs +377 -178
  276. package/src/Xhr.mjs +1 -0
  277. package/src/button/Base.mjs +13 -0
  278. package/src/button/Effect.mjs +449 -0
  279. package/src/button/Split.mjs +2 -0
  280. package/src/calendar/store/Calendars.mjs +1 -0
  281. package/src/calendar/store/Colors.mjs +1 -0
  282. package/src/calendar/store/Events.mjs +1 -0
  283. package/src/calendar/view/DayComponent.mjs +2 -0
  284. package/src/calendar/view/EditEventContainer.mjs +4 -1
  285. package/src/calendar/view/MainContainer.mjs +13 -0
  286. package/src/calendar/view/MainContainerStateProvider.mjs +14 -28
  287. package/src/calendar/view/SettingsContainer.mjs +1 -0
  288. package/src/calendar/view/YearComponent.mjs +16 -0
  289. package/src/calendar/view/calendars/ColorsList.mjs +2 -0
  290. package/src/calendar/view/calendars/Container.mjs +2 -0
  291. package/src/calendar/view/calendars/EditContainer.mjs +1 -0
  292. package/src/calendar/view/month/Component.mjs +11 -0
  293. package/src/calendar/view/settings/GeneralContainer.mjs +1 -0
  294. package/src/calendar/view/settings/MonthContainer.mjs +1 -0
  295. package/src/calendar/view/settings/WeekContainer.mjs +1 -0
  296. package/src/calendar/view/settings/YearContainer.mjs +1 -0
  297. package/src/calendar/view/week/Component.mjs +15 -1
  298. package/src/calendar/view/week/TimeAxisComponent.mjs +4 -0
  299. package/src/code/LivePreview.mjs +51 -23
  300. package/src/collection/Base.mjs +14 -12
  301. package/src/collection/Filter.mjs +6 -0
  302. package/src/collection/Sorter.mjs +3 -0
  303. package/src/component/Base.mjs +156 -802
  304. package/src/component/Canvas.mjs +1 -0
  305. package/src/component/Chip.mjs +4 -0
  306. package/src/component/Circle.mjs +14 -0
  307. package/src/component/Clock.mjs +4 -0
  308. package/src/component/DateSelector.mjs +12 -0
  309. package/src/component/Gallery.mjs +11 -0
  310. package/src/component/Helix.mjs +24 -0
  311. package/src/component/Label.mjs +1 -0
  312. package/src/component/Legend.mjs +3 -0
  313. package/src/component/MagicMoveText.mjs +4 -0
  314. package/src/component/Progress.mjs +3 -0
  315. package/src/component/Splitter.mjs +3 -0
  316. package/src/component/StatusBadge.mjs +6 -0
  317. package/src/component/Timer.mjs +4 -0
  318. package/src/component/Toast.mjs +6 -0
  319. package/src/component/Video.mjs +1 -0
  320. package/src/component/mwc/Button.mjs +7 -0
  321. package/src/component/mwc/TextField.mjs +9 -0
  322. package/src/component/wrapper/AmChart.mjs +2 -0
  323. package/src/component/wrapper/GoogleMaps.mjs +3 -0
  324. package/src/component/wrapper/MapboxGL.mjs +5 -0
  325. package/src/component/wrapper/MonacoEditor.mjs +12 -0
  326. package/src/container/Accordion.mjs +2 -0
  327. package/src/container/Base.mjs +34 -26
  328. package/src/container/Panel.mjs +1 -0
  329. package/src/container/Viewport.mjs +1 -0
  330. package/src/controller/Application.mjs +1 -0
  331. package/src/controller/Base.mjs +1 -0
  332. package/src/controller/Component.mjs +1 -0
  333. package/src/core/Base.mjs +193 -22
  334. package/src/core/Compare.mjs +4 -7
  335. package/src/core/Config.mjs +137 -33
  336. package/src/core/Effect.mjs +193 -0
  337. package/src/core/EffectBatchManager.mjs +67 -0
  338. package/src/core/EffectManager.mjs +60 -0
  339. package/src/core/IdGenerator.mjs +13 -44
  340. package/src/data/Model.mjs +2 -0
  341. package/src/data/Store.mjs +7 -0
  342. package/src/data/connection/WebSocket.mjs +2 -0
  343. package/src/date/DayViewComponent.mjs +2 -0
  344. package/src/date/SelectorContainer.mjs +14 -0
  345. package/src/dialog/Base.mjs +8 -0
  346. package/src/draggable/DragZone.mjs +5 -0
  347. package/src/draggable/tree/DragZone.mjs +1 -0
  348. package/src/filter/BooleanContainer.mjs +2 -0
  349. package/src/filter/NumberContainer.mjs +3 -0
  350. package/src/filter/ToggleOperatorsButton.mjs +2 -0
  351. package/src/form/Fieldset.mjs +6 -0
  352. package/src/form/field/Base.mjs +7 -0
  353. package/src/form/field/CheckBox.mjs +18 -0
  354. package/src/form/field/Chip.mjs +1 -0
  355. package/src/form/field/ComboBox.mjs +8 -0
  356. package/src/form/field/Country.mjs +1 -0
  357. package/src/form/field/Currency.mjs +2 -0
  358. package/src/form/field/Date.mjs +4 -0
  359. package/src/form/field/Display.mjs +1 -0
  360. package/src/form/field/Email.mjs +1 -0
  361. package/src/form/field/FileUpload.mjs +7 -0
  362. package/src/form/field/Hidden.mjs +1 -0
  363. package/src/form/field/Number.mjs +7 -0
  364. package/src/form/field/Password.mjs +1 -0
  365. package/src/form/field/Phone.mjs +3 -0
  366. package/src/form/field/Picker.mjs +2 -0
  367. package/src/form/field/Radio.mjs +1 -0
  368. package/src/form/field/Range.mjs +3 -0
  369. package/src/form/field/Search.mjs +2 -0
  370. package/src/form/field/Text.mjs +32 -0
  371. package/src/form/field/TextArea.mjs +7 -0
  372. package/src/form/field/Time.mjs +6 -0
  373. package/src/form/field/Url.mjs +3 -0
  374. package/src/form/field/ZipCode.mjs +2 -0
  375. package/src/form/field/trigger/Base.mjs +3 -0
  376. package/src/form/field/trigger/Clear.mjs +2 -0
  377. package/src/form/field/trigger/CopyToClipboard.mjs +2 -0
  378. package/src/form/field/trigger/Date.mjs +1 -0
  379. package/src/form/field/trigger/Picker.mjs +1 -0
  380. package/src/form/field/trigger/Search.mjs +1 -0
  381. package/src/form/field/trigger/SpinDown.mjs +2 -0
  382. package/src/form/field/trigger/SpinUp.mjs +1 -0
  383. package/src/form/field/trigger/Time.mjs +2 -0
  384. package/src/functional/_export.mjs +6 -0
  385. package/src/functional/component/Base.mjs +499 -0
  386. package/src/functional/defineComponent.mjs +102 -0
  387. package/src/functional/useConfig.mjs +52 -0
  388. package/src/functional/useEvent.mjs +43 -0
  389. package/src/grid/Body.mjs +20 -1
  390. package/src/grid/Container.mjs +57 -63
  391. package/src/grid/ScrollManager.mjs +2 -0
  392. package/src/grid/VerticalScrollbar.mjs +2 -0
  393. package/src/grid/column/Base.mjs +2 -0
  394. package/src/grid/column/Component.mjs +1 -1
  395. package/src/grid/header/Button.mjs +7 -0
  396. package/src/grid/header/Toolbar.mjs +6 -0
  397. package/src/grid/plugin/AnimateRows.mjs +2 -0
  398. package/src/layout/Base.mjs +3 -0
  399. package/src/layout/Card.mjs +1 -0
  400. package/src/layout/Cube.mjs +11 -1
  401. package/src/layout/Fit.mjs +1 -0
  402. package/src/layout/Flexbox.mjs +7 -0
  403. package/src/layout/Form.mjs +2 -0
  404. package/src/layout/Grid.mjs +1 -0
  405. package/src/layout/HBox.mjs +1 -0
  406. package/src/layout/VBox.mjs +1 -0
  407. package/src/list/Base.mjs +13 -0
  408. package/src/list/Chip.mjs +1 -0
  409. package/src/list/Circle.mjs +2 -0
  410. package/src/list/Color.mjs +1 -0
  411. package/src/list/plugin/Animate.mjs +2 -0
  412. package/src/main/DeltaUpdates.mjs +1 -0
  413. package/src/main/DomEvents.mjs +2 -0
  414. package/src/main/addon/CloneNode.mjs +1 -0
  415. package/src/main/addon/Cookie.mjs +1 -0
  416. package/src/main/addon/GoogleMaps.mjs +1 -0
  417. package/src/main/addon/LocalStorage.mjs +1 -0
  418. package/src/main/addon/MapboxGL.mjs +1 -0
  419. package/src/main/addon/Markdown.mjs +1 -0
  420. package/src/main/addon/Navigator.mjs +1 -0
  421. package/src/main/addon/Popover.mjs +1 -0
  422. package/src/main/addon/Stylesheet.mjs +1 -0
  423. package/src/main/addon/WindowPosition.mjs +1 -0
  424. package/src/manager/Component.mjs +0 -71
  425. package/src/manager/VDomUpdate.mjs +235 -0
  426. package/src/menu/List.mjs +6 -0
  427. package/src/menu/Model.mjs +1 -0
  428. package/src/menu/Panel.mjs +3 -0
  429. package/src/menu/Store.mjs +1 -0
  430. package/src/mixin/DomEvents.mjs +130 -0
  431. package/src/mixin/VdomLifecycle.mjs +667 -0
  432. package/src/plugin/Base.mjs +1 -0
  433. package/src/plugin/Resizable.mjs +2 -0
  434. package/src/selection/Model.mjs +15 -18
  435. package/src/selection/grid/BaseModel.mjs +1 -0
  436. package/src/sitemap/Component.mjs +1 -0
  437. package/src/state/Provider.mjs +376 -457
  438. package/src/state/createHierarchicalDataProxy.mjs +138 -0
  439. package/src/tab/Container.mjs +6 -0
  440. package/src/tab/Strip.mjs +1 -0
  441. package/src/tab/header/Button.mjs +2 -0
  442. package/src/tab/header/EffectButton.mjs +77 -0
  443. package/src/tab/header/Toolbar.mjs +1 -0
  444. package/src/table/Body.mjs +3 -0
  445. package/src/table/Container.mjs +10 -0
  446. package/src/table/header/Button.mjs +8 -0
  447. package/src/table/header/Toolbar.mjs +5 -0
  448. package/src/table/plugin/CellEditing.mjs +1 -0
  449. package/src/toolbar/Base.mjs +4 -0
  450. package/src/toolbar/Breadcrumb.mjs +3 -0
  451. package/src/toolbar/Paging.mjs +5 -0
  452. package/src/tooltip/Base.mjs +2 -0
  453. package/src/tree/List.mjs +3 -0
  454. package/src/util/HashHistory.mjs +1 -0
  455. package/src/util/KeyNavigation.mjs +2 -0
  456. package/src/util/Matrix.mjs +1 -0
  457. package/src/util/VDom.mjs +7 -1
  458. package/src/util/VNode.mjs +7 -1
  459. package/src/util/vdom/TreeBuilder.mjs +129 -0
  460. package/src/vdom/Helper.mjs +44 -33
  461. package/src/vdom/VNode.mjs +5 -7
  462. package/src/worker/App.mjs +1 -5
  463. package/src/worker/Base.mjs +2 -0
  464. package/src/worker/Manager.mjs +2 -0
  465. package/src/worker/ServiceBase.mjs +6 -1
  466. package/test/siesta/siesta.js +36 -1
  467. package/test/siesta/tests/CollectionBase.mjs +10 -10
  468. package/test/siesta/tests/VdomCalendar.mjs +13 -9
  469. package/test/siesta/tests/VdomHelper.mjs +22 -59
  470. package/test/siesta/tests/config/AfterSetConfig.mjs +100 -0
  471. package/test/siesta/tests/{ReactiveConfigs.mjs → config/Basic.mjs} +58 -21
  472. package/test/siesta/tests/config/CircularDependencies.mjs +166 -0
  473. package/test/siesta/tests/config/CustomFunctions.mjs +69 -0
  474. package/test/siesta/tests/config/Hierarchy.mjs +94 -0
  475. package/test/siesta/tests/config/MemoryLeak.mjs +92 -0
  476. package/test/siesta/tests/config/MultiLevelHierarchy.mjs +85 -0
  477. package/test/siesta/tests/core/Effect.mjs +127 -0
  478. package/test/siesta/tests/core/EffectBatching.mjs +310 -0
  479. package/test/siesta/tests/neo/MixinStaticConfig.mjs +138 -0
  480. package/test/siesta/tests/state/Provider.mjs +537 -0
  481. package/test/siesta/tests/state/ProviderNestedDataConfigs.mjs +255 -0
  482. package/test/siesta/tests/state/createHierarchicalDataProxy.mjs +204 -0
  483. package/test/siesta/tests/vdom/VdomAsymmetricUpdates.mjs +366 -0
  484. package/test/siesta/tests/vdom/VdomRealWorldUpdates.mjs +249 -0
  485. package/learn/javascript/NewNode.md +0 -31
@@ -0,0 +1,537 @@
1
+ import Neo from '../../../../src/Neo.mjs';
2
+ import * as core from '../../../../src/core/_export.mjs';
3
+ import InstanceManager from '../../../../src/manager/Instance.mjs';
4
+ import Component from '../../../../src/component/Base.mjs';
5
+ import StateProvider from '../../../../src/state/Provider.mjs';
6
+ import Store from '../../../../src/data/Store.mjs';
7
+
8
+
9
+ // Mock Component for testing purposes
10
+ class MockComponent extends Component {
11
+ static config = {
12
+ className : 'Mock.Component',
13
+ appName : 'test-app',
14
+ testConfig_: null
15
+ }
16
+ }
17
+ Neo.setupClass(MockComponent);
18
+
19
+ StartTest(t => {
20
+ // Helper function to convert a proxy to a plain object for deep comparison
21
+ function proxyToObject(proxy) {
22
+ return JSON.parse(JSON.stringify(proxy))
23
+ }
24
+
25
+ t.it('Provider should initialize with data and create configs', t => {
26
+ const component = Neo.create(MockComponent, {
27
+ stateProvider: {
28
+ data: {name: 'Test', value: 123, user: {firstName: 'John'}}
29
+ }
30
+ });
31
+ const provider = component.getStateProvider();
32
+
33
+ t.is(provider.getDataConfig('name').get(), 'Test', 'Name config should be created and have correct value');
34
+ t.is(provider.getDataConfig('value').get(), 123, 'Value config should be created and have correct value');
35
+ t.is(provider.getDataConfig('user.firstName').get(), 'John', 'Nested user.firstName config should be created');
36
+
37
+ component.destroy();
38
+ });
39
+
40
+ t.it('Provider should update data and trigger config changes', t => {
41
+ const component = Neo.create(MockComponent, {stateProvider: {data: {counter: 0}}});
42
+ const provider = component.getStateProvider();
43
+
44
+ let effectRunCount = 0;
45
+ provider.createBinding(component.id, 'testConfig', data => {
46
+ effectRunCount++;
47
+ return data.counter;
48
+ });
49
+
50
+ t.is(effectRunCount, 1, 'Binding effect should run once initially');
51
+ t.is(component.testConfig, 0, 'Component config should be initialized with data value');
52
+
53
+ component.setState('counter', 1);
54
+ t.is(effectRunCount, 2, 'Binding effect should re-run after setData');
55
+ t.is(component.testConfig, 1, 'Component config should be updated after setData');
56
+
57
+ component.setState({counter: 2});
58
+ t.is(effectRunCount, 3, 'Binding effect should re-run after setData with object');
59
+ t.is(component.testConfig, 2, 'Component config should be updated after setData with object');
60
+
61
+ component.destroy();
62
+ });
63
+
64
+ t.it('Provider should handle hierarchical data access', t => {
65
+ const parentComponent = Neo.create(MockComponent, {
66
+ stateProvider: {data: {appTitle: 'My App', user: {firstName: 'Parent'}}}
67
+ });
68
+ const childComponent = Neo.create(MockComponent, {
69
+ stateProvider: {data: {user: {lastName: 'Child'}}},
70
+ parentComponent: parentComponent
71
+ });
72
+
73
+ let effectRunCount = 0;
74
+ childComponent.getStateProvider().createBinding(childComponent.id, 'testConfig', data => {
75
+ effectRunCount++;
76
+ return `${data.appTitle} - ${data.user.firstName} ${data.user.lastName}`;
77
+ });
78
+
79
+ t.is(effectRunCount, 1, 'Binding effect should run once initially');
80
+ t.is(childComponent.testConfig, 'My App - Parent Child', 'Component config should reflect hierarchical data');
81
+
82
+ parentComponent.setState('appTitle', 'New App Title');
83
+ t.is(effectRunCount, 2, 'Binding effect should re-run after parent data change');
84
+ t.is(childComponent.testConfig, 'New App Title - Parent Child', 'Component config should update from parent data');
85
+
86
+ childComponent.setState('user.lastName', 'New Child');
87
+ t.is(effectRunCount, 3, 'Binding effect should re-run after child data change');
88
+ t.is(childComponent.testConfig, 'New App Title - Parent New Child', 'Component config should update from child data');
89
+
90
+ parentComponent.setState('user.firstName', 'New Parent');
91
+ t.is(effectRunCount, 4, 'Binding effect should re-run after parent nested data change');
92
+ t.is(childComponent.testConfig, 'New App Title - New Parent New Child', 'Component config should update from parent nested data');
93
+
94
+ parentComponent.destroy();
95
+ childComponent.destroy();
96
+ });
97
+
98
+ t.it('Provider should remove bindings on component destroy', t => {
99
+ const component = Neo.create(MockComponent, {stateProvider: {data: {test: 1}}});
100
+ const provider = component.getStateProvider();
101
+
102
+ let effectRunCount = 0;
103
+ const bindingEffect = provider.createBinding(component.id, 'testConfig', data => {
104
+ effectRunCount++;
105
+ return data.test;
106
+ });
107
+
108
+ t.is(effectRunCount, 1, 'Effect ran initially');
109
+ t.is(bindingEffect.isDestroyed, false, 'Binding effect should not be destroyed initially');
110
+
111
+ component.destroy();
112
+ t.is(bindingEffect.isDestroyed, true, 'Binding effect should be destroyed after component destroy');
113
+
114
+ provider.setData('test', 2);
115
+ t.is(effectRunCount, 1, 'Effect should not re-run after component destroyed');
116
+ });
117
+
118
+ t.it('Provider should remove bindings on provider destroy', t => {
119
+ const component = Neo.create(MockComponent, {stateProvider: {data: {test: 1}}});
120
+ const provider = component.getStateProvider();
121
+
122
+ let effectRunCount = 0;
123
+ const bindingEffect = provider.createBinding(component.id, 'testConfig', data => {
124
+ effectRunCount++;
125
+ return data.test;
126
+ });
127
+
128
+ t.is(effectRunCount, 1, 'Effect ran initially');
129
+ t.is(bindingEffect.isDestroyed, false, 'Binding effect should not be destroyed initially');
130
+
131
+ provider.destroy();
132
+ t.is(bindingEffect.isDestroyed, true, 'Binding effect should be destroyed after provider destroy');
133
+
134
+ // Attempt to change data after provider destroyed
135
+ component.setState('test', 2);
136
+ t.is(effectRunCount, 1, 'Effect should not re-run after provider destroyed');
137
+
138
+ component.destroy();
139
+ });
140
+
141
+ t.it('setData should create new data properties if they do not exist', t => {
142
+ const component = Neo.create(MockComponent, {stateProvider: {data: {}}});
143
+ const provider = component.getStateProvider();
144
+
145
+ let effectRunCount = 0;
146
+ provider.createBinding(component.id, 'testConfig', data => {
147
+ effectRunCount++;
148
+ return data.newProp;
149
+ });
150
+
151
+ t.is(effectRunCount, 1, 'Effect ran initially');
152
+ t.is(component.testConfig, undefined, 'Component config should be undefined initially');
153
+
154
+ component.setState('newProp', 'hello');
155
+ t.is(effectRunCount, 2, 'Effect re-ran after newProp was set');
156
+ t.is(component.testConfig, 'hello', 'Component config should update with newProp');
157
+ t.is(provider.getDataConfig('newProp').get(), 'hello', 'newProp config should exist');
158
+
159
+ component.setState('nested.newProp', 'world');
160
+ t.is(effectRunCount, 3, 'Effect re-ran after nested.newProp was set');
161
+ t.is(provider.getDataConfig('nested.newProp').get(), 'world', 'nested.newProp config should exist');
162
+
163
+ component.destroy();
164
+ });
165
+
166
+ t.it('Two-way binding should update state provider from component config changes', t => {
167
+ const component = Neo.create(MockComponent, {
168
+ stateProvider: {
169
+ data: {inputValue: 'initial'}
170
+ },
171
+ bind: {
172
+ testConfig: {key: 'inputValue', twoWay: true}
173
+ }
174
+ });
175
+ const provider = component.getStateProvider();
176
+
177
+ t.is(component.testConfig, 'initial', 'Component config should be initialized from state provider');
178
+ t.is(provider.getDataConfig('inputValue').get(), 'initial', 'State provider data should be initialized from component');
179
+
180
+ component.testConfig = 'updated from component';
181
+ t.is(provider.getDataConfig('inputValue').get(), 'updated from component', 'State provider data should update from component config change');
182
+
183
+ provider.setData('inputValue', 'updated from provider');
184
+ t.is(component.testConfig, 'updated from provider', 'Component config should update from state provider data change');
185
+
186
+ component.destroy();
187
+ });
188
+
189
+ t.it('Formulas should calculate correctly and react to dependencies', t => {
190
+ const component = Neo.create(MockComponent, {
191
+ stateProvider: {
192
+ data: {
193
+ price : 10,
194
+ quantity: 2
195
+ },
196
+ formulas: {
197
+ total(data) {return data.price * data.quantity},
198
+ discountedTotal: (data) => data.total * 0.9 // 10% discount
199
+ }
200
+ }
201
+ });
202
+ const provider = component.getStateProvider();
203
+
204
+ t.is(provider.getDataConfig('total').get(), 20, 'Initial total formula calculation is correct');
205
+ t.is(provider.getDataConfig('discountedTotal').get(), 18, 'Initial discountedTotal formula calculation is correct');
206
+
207
+ component.setState('price', 15);
208
+ t.is(provider.getDataConfig('total').get(), 30, 'Total formula updates when price changes');
209
+ t.is(provider.getDataConfig('discountedTotal').get(), 27, 'DiscountedTotal formula updates when total changes');
210
+
211
+ component.setState('quantity', 5);
212
+ t.is(provider.getDataConfig('total').get(), 75, 'Total formula updates when quantity changes');
213
+ t.is(provider.getDataConfig('discountedTotal').get(), 67.5, 'DiscountedTotal formula updates when total changes again');
214
+
215
+ component.destroy();
216
+ });
217
+
218
+ t.it('Store management should correctly bind components to stores and react to store changes', t => {
219
+ const store = Neo.create(Neo.data.Store, {
220
+ data : [{id: 1, name: 'Item 1'}, {id: 2, name: 'Item 2'}],
221
+ model: {fields: [{name: 'id', type: 'Int'}, {name: 'name', type: 'String'}]}
222
+ });
223
+
224
+ const component = Neo.create(MockComponent, {
225
+ stateProvider: {
226
+ stores: {
227
+ myStore: store
228
+ }
229
+ },
230
+ bind: {
231
+ testConfig: 'stores.myStore'
232
+ }
233
+ });
234
+
235
+ t.is(component.testConfig, store, 'Component config should be bound to the store instance');
236
+ t.is(component.testConfig.count, 2, 'Bound store should have correct initial count');
237
+
238
+ store.add({id: 3, name: 'Item 3'});
239
+ t.is(component.testConfig.count, 3, 'Bound store should reflect changes after adding a record');
240
+
241
+ store.remove(store.get(1));
242
+ t.is(component.testConfig.count, 2, 'Bound store should reflect changes after removing a record');
243
+
244
+ component.destroy();
245
+ store.destroy();
246
+ });
247
+
248
+ t.it('Store management with an inline store', t => {
249
+ const component = Neo.create(MockComponent, {
250
+ stateProvider: {
251
+ stores: {
252
+ myStore: {
253
+ module: Store,
254
+ data : [{id: 1, name: 'Item 1'}, {id: 2, name: 'Item 2'}],
255
+ model : {fields: [{name: 'id'}, {name: 'name'}]}
256
+ }
257
+ }
258
+ },
259
+ bind: {
260
+ testConfig: 'stores.myStore'
261
+ }
262
+ });
263
+
264
+ const store = component.getStateProvider().getStore('myStore');
265
+
266
+ t.is(component.testConfig, store, 'Component config should be bound to the store instance');
267
+ t.is(component.testConfig.count, 2, 'Bound store should have correct initial count');
268
+
269
+ store.add({id: 3, name: 'Item 3'});
270
+ t.is(component.testConfig.count, 3, 'Bound store should reflect changes after adding a record');
271
+
272
+ store.remove(store.get(1));
273
+ t.is(component.testConfig.count, 2, 'Bound store should reflect changes after removing a record');
274
+
275
+ component.destroy();
276
+ store.destroy();
277
+ });
278
+
279
+ t.it('Provider data_ config should deep merge class and instance level data', t => {
280
+ class ClassLevelProvider extends StateProvider {
281
+ static config = {
282
+ className: 'ClassLevelProvider',
283
+ data: {
284
+ a: 1,
285
+ b: {
286
+ c: 2,
287
+ d: 3
288
+ },
289
+ arr: [1, 2]
290
+ }
291
+ }
292
+ }
293
+ Neo.setupClass(ClassLevelProvider);
294
+
295
+ // Test 1: Instance with no data, should reflect class-level data
296
+ const provider1 = Neo.create(ClassLevelProvider);
297
+
298
+ t.isDeeply(proxyToObject(provider1.data), {
299
+ a: 1,
300
+ b: {
301
+ c: 2,
302
+ d: 3
303
+ },
304
+ arr: [1, 2]
305
+ }, 'Provider1 data should reflect class-level data when no instance data is provided');
306
+ provider1.destroy();
307
+
308
+ // Test 2: Instance with new top-level data
309
+ const provider2 = Neo.create(ClassLevelProvider, {
310
+ data: {
311
+ x: 10,
312
+ y: 20
313
+ }
314
+ });
315
+ t.isDeeplyStrict(proxyToObject(provider2.data), {
316
+ a: 1,
317
+ b: {
318
+ c: 2,
319
+ d: 3
320
+ },
321
+ arr: [1, 2],
322
+ x: 10,
323
+ y: 20
324
+ }, 'Provider2 data should deep merge new top-level instance data');
325
+ provider2.destroy();
326
+
327
+ // Test 3: Instance with overlapping data (deep merge)
328
+ const provider3 = Neo.create(ClassLevelProvider, {
329
+ data: {
330
+ b: {
331
+ e: 4
332
+ },
333
+ arr: [3, 4], // Array replacement, not merge
334
+ newProp: 'test'
335
+ }
336
+ });
337
+ t.isDeeplyStrict(proxyToObject(provider3.data), {
338
+ a: 1,
339
+ b: {
340
+ c: 2,
341
+ d: 3,
342
+ e: 4
343
+ },
344
+ arr: [3, 4], // Arrays are replaced by default merge strategy
345
+ newProp: 'test'
346
+ }, 'Provider3 data should deep merge overlapping instance data and replace arrays');
347
+ provider3.destroy();
348
+
349
+ // Test 4: Instance with overlapping data (deep merge) and modifying existing nested property
350
+ const provider4 = Neo.create(ClassLevelProvider, {
351
+ data: {
352
+ b: {
353
+ c: 99
354
+ }
355
+ }
356
+ });
357
+ t.isDeeplyStrict(proxyToObject(provider4.data), {
358
+ a: 1,
359
+ b: {
360
+ c: 99,
361
+ d: 3
362
+ },
363
+ arr: [1, 2]
364
+ }, 'Provider4 data should deep merge and modify existing nested property');
365
+ provider4.destroy();
366
+ });
367
+
368
+ t.it('Provider data_ config should deep merge across multi-level class inheritance', t => {
369
+ class GrandparentProvider extends StateProvider {
370
+ static config = {
371
+ className: 'GrandparentProvider',
372
+ data: {
373
+ app: {
374
+ name: 'My App',
375
+ version: '1.0.0'
376
+ },
377
+ user: {
378
+ role: 'guest',
379
+ settings: {
380
+ theme: 'dark'
381
+ }
382
+ }
383
+ }
384
+ }
385
+ }
386
+ Neo.setupClass(GrandparentProvider);
387
+
388
+ class ParentProvider extends GrandparentProvider {
389
+ static config = {
390
+ className: 'ParentProvider',
391
+ data: {
392
+ app: {
393
+ version: '1.1.0', // Overrides grandparent version
394
+ author: 'Neo'
395
+ },
396
+ user: {
397
+ id: 123,
398
+ settings: {
399
+ notifications: true // Adds to grandparent settings
400
+ }
401
+ },
402
+ newParentProp: 'parentValue'
403
+ }
404
+ }
405
+ }
406
+ Neo.setupClass(ParentProvider);
407
+
408
+ class ChildProvider extends ParentProvider {
409
+ static config = {
410
+ className: 'ChildProvider',
411
+ data: {
412
+ user: {
413
+ role: 'admin', // Overrides parent role
414
+ preferences: {
415
+ language: 'en'
416
+ }
417
+ },
418
+ newChildProp: 'childValue'
419
+ }
420
+ }
421
+ }
422
+ Neo.setupClass(ChildProvider);
423
+
424
+ // Test 1: Instance with no data, should reflect merged data from all levels
425
+ const provider1 = Neo.create(ChildProvider);
426
+ t.isDeeplyStrict(proxyToObject(provider1.data), {
427
+ app: {
428
+ name: 'My App',
429
+ version: '1.1.0',
430
+ author: 'Neo'
431
+ },
432
+ user: {
433
+ role: 'admin',
434
+ id: 123,
435
+ settings: {
436
+ theme: 'dark',
437
+ notifications: true
438
+ },
439
+ preferences: {
440
+ language: 'en'
441
+ }
442
+ },
443
+ newParentProp: 'parentValue',
444
+ newChildProp: 'childValue'
445
+ }, 'Provider1 data should reflect deep merge from all class inheritance levels');
446
+ provider1.destroy();
447
+
448
+ // Test 2: Instance with data overriding properties from different levels
449
+ const provider2 = Neo.create(ChildProvider, {
450
+ data: {
451
+ app: {
452
+ version: '2.0.0', // Overrides ParentProvider's version
453
+ status: 'beta'
454
+ },
455
+ user: {
456
+ id: 456, // Overrides ParentProvider's id
457
+ settings: {
458
+ theme: 'light', // Overrides GrandparentProvider's theme
459
+ notifications: false // Overrides ParentProvider's notifications
460
+ }
461
+ },
462
+ newChildProp: 'overriddenChildValue',
463
+ instanceOnlyProp: 'instanceValue'
464
+ }
465
+ });
466
+
467
+ t.isDeeplyStrict(proxyToObject(provider2.data), {
468
+ app: {
469
+ name: 'My App',
470
+ version: '2.0.0',
471
+ author: 'Neo',
472
+ status: 'beta'
473
+ },
474
+ user: {
475
+ role: 'admin',
476
+ id: 456,
477
+ settings: {
478
+ theme: 'light',
479
+ notifications: false
480
+ },
481
+ preferences: {
482
+ language: 'en'
483
+ }
484
+ },
485
+ newParentProp: 'parentValue',
486
+ newChildProp: 'overriddenChildValue',
487
+ instanceOnlyProp: 'instanceValue'
488
+ }, 'Provider2 data should reflect deep merge with instance overrides across inheritance');
489
+ provider2.destroy();
490
+ });
491
+
492
+ t.it('Formulas in nested providers should combine own and parent data', t => {
493
+ const parentComponent = Neo.create(MockComponent, {
494
+ stateProvider: {
495
+ data: {
496
+ basePrice: 100,
497
+ taxRate : 0.05
498
+ }
499
+ }
500
+ });
501
+
502
+ const childComponent = Neo.create(MockComponent, {
503
+ parentComponent,
504
+
505
+ stateProvider: {
506
+ data: {
507
+ itemQuantity: 2
508
+ },
509
+ formulas: {
510
+ // Formula combines parent's basePrice and taxRate with child's itemQuantity
511
+ totalCost: (data) => (data.basePrice * data.itemQuantity) * (1 + data.taxRate)
512
+ }
513
+ }
514
+ });
515
+
516
+ const parentProvider = parentComponent.getStateProvider();
517
+ const childProvider = childComponent.getStateProvider();
518
+
519
+ // Initial calculation
520
+ t.is(childProvider.getData('totalCost'), (100 * 2) * (1 + 0.05), 'Initial totalCost calculation is correct');
521
+
522
+ // Change parent data
523
+ parentProvider.setData('basePrice', 120);
524
+ t.is(childProvider.getData('totalCost'), (120 * 2) * (1 + 0.05), 'totalCost updates when parent basePrice changes');
525
+
526
+ // Change child data
527
+ childProvider.setData('itemQuantity', 3);
528
+ t.is(childProvider.getData('totalCost'), (120 * 3) * (1 + 0.05), 'totalCost updates when child itemQuantity changes');
529
+
530
+ // Change parent data again
531
+ parentProvider.setData('taxRate', 0.10);
532
+ t.is(childProvider.getData('totalCost'), (120 * 3) * (1 + 0.10), 'totalCost updates when parent taxRate changes');
533
+
534
+ parentComponent.destroy();
535
+ childComponent.destroy();
536
+ });
537
+ });