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
@@ -20,32 +20,39 @@ class Button extends Component {
20
20
  ntype: 'mwc-button',
21
21
  /**
22
22
  * @member {Boolean} dense_=false
23
+ * @reactive
23
24
  */
24
25
  dense_: false,
25
26
  /**
26
27
  * Shortcut for domListeners={click:handler}
27
28
  * A string based value assumes that the handlerFn lives inside a ComponentController
28
29
  * @member {Function|String|null} handler_=null
30
+ * @reactive
29
31
  */
30
32
  handler_: null,
31
33
  /**
32
34
  * @member {String} icon_=''
35
+ * @reactive
33
36
  */
34
37
  icon_: '',
35
38
  /**
36
39
  * @member {String} label_=''
40
+ * @reactive
37
41
  */
38
42
  label_: '',
39
43
  /**
40
44
  * @member {Boolean} outlined_=false
45
+ * @reactive
41
46
  */
42
47
  outlined_: false,
43
48
  /**
44
49
  * @member {Boolean} raised_=false
50
+ * @reactive
45
51
  */
46
52
  raised_: false,
47
53
  /**
48
54
  * @member {Boolean} unelevated_=false
55
+ * @reactive
49
56
  */
50
57
  unelevated_: false,
51
58
  /**
@@ -21,38 +21,47 @@ class TextField extends Component {
21
21
  /**
22
22
  * Displays a helper text below the field
23
23
  * @member {String} helper_=''
24
+ * @reactive
24
25
  */
25
26
  helper_: '',
26
27
  /**
27
28
  * @member {String} icon_=''
29
+ * @reactive
28
30
  */
29
31
  icon_: '',
30
32
  /**
31
33
  * @member {String} iconTrailing_=''
34
+ * @reactive
32
35
  */
33
36
  iconTrailing_: '',
34
37
  /**
35
38
  * @member {String} label_=''
39
+ * @reactive
36
40
  */
37
41
  label_: '',
38
42
  /**
39
43
  * @member {String} name_=''
44
+ * @reactive
40
45
  */
41
46
  name_: '',
42
47
  /**
43
48
  * @member {Boolean} outlined_=false
49
+ * @reactive
44
50
  */
45
51
  outlined_: false,
46
52
  /**
47
53
  * @member {String} placeholder_=''
54
+ * @reactive
48
55
  */
49
56
  placeholder_: '',
50
57
  /**
51
58
  * @member {Boolean} required_=false
59
+ * @reactive
52
60
  */
53
61
  required_: false,
54
62
  /**
55
63
  * @member {String} value_=''
64
+ * @reactive
56
65
  */
57
66
  value_: '',
58
67
  /**
@@ -21,11 +21,13 @@ class AmChart extends Component {
21
21
  /**
22
22
  * See: https://www.amcharts.com/docs/v4/
23
23
  * @member {Object} chartConfig_=null
24
+ * @reactive
24
25
  */
25
26
  chartConfig_: null,
26
27
  /**
27
28
  * Stores the chart data
28
29
  * @member {Array|null} chartData_=null
30
+ * @reactive
29
31
  */
30
32
  chartData_: null,
31
33
  /**
@@ -21,12 +21,14 @@ class GoogleMaps extends Base {
21
21
  /**
22
22
  * Specify lat & lng for the current focus position
23
23
  * @member {Object} center_={lat: -34.397, lng: 150.644}
24
+ * @reactive
24
25
  */
25
26
  center_: {lat: -34.397, lng: 150.644},
26
27
  /**
27
28
  * Prefer to use markerStoreConfig instead.
28
29
  * @member {Neo.data.Store|Object} markerStore_
29
30
  * @protected
31
+ * @reactive
30
32
  */
31
33
  markerStore_: {
32
34
  model: {
@@ -52,6 +54,7 @@ class GoogleMaps extends Base {
52
54
  },
53
55
  /**
54
56
  * @member {Number} zoom_=8
57
+ * @reactive
55
58
  */
56
59
  zoom_: 8
57
60
  }
@@ -25,10 +25,12 @@ class MapboxGL extends Component {
25
25
  accessToken: null,
26
26
  /**
27
27
  * @member {Array|null} chartData_=null
28
+ * @reactive
28
29
  */
29
30
  chartData_: null,
30
31
  /**
31
32
  * @member {Object} center_={lat: 0, lng: 0}
33
+ * @reactive
32
34
  */
33
35
  center_: {lat: 0, lng: 0},
34
36
  /**
@@ -47,6 +49,7 @@ class MapboxGL extends Component {
47
49
  * beforeId is a custom property which will get passed as the second param for:
48
50
  * https://docs.mapbox.com/mapbox-gl-js/api/#map#addlayer
49
51
  * @member {Object[]|null} layers_=null
52
+ * @reactive
50
53
  */
51
54
  layers_: null,
52
55
  /**
@@ -59,6 +62,7 @@ class MapboxGL extends Component {
59
62
  * id is a custom property which will get passed as the first param for:
60
63
  * https://docs.mapbox.com/mapbox-gl-js/api/#map#addsource
61
64
  * @member {Object[]|null} sources_=null
65
+ * @reactive
62
66
  */
63
67
  sources_: null,
64
68
  /**
@@ -73,6 +77,7 @@ class MapboxGL extends Component {
73
77
  /**
74
78
  *
75
79
  * @member {Number} zoom_=3
80
+ * @reactive
76
81
  */
77
82
  zoom_: 3
78
83
  }
@@ -47,34 +47,41 @@ class MonacoEditor extends Base {
47
47
  baseCls: ['neo-monaco-editor'],
48
48
  /**
49
49
  * @member {Boolean} contextmenu_=false
50
+ * @reactive
50
51
  */
51
52
  contextmenu_: false,
52
53
  /**
53
54
  * Options are: 'blink', 'expand', 'phase', 'smooth', 'solid'
54
55
  * @member {String} cursorBlinking_='blink'
56
+ * @reactive
55
57
  */
56
58
  cursorBlinking_: 'blink',
57
59
  /**
58
60
  * additional property to only use in combination with readOnly === true.
59
61
  * domReadOnly additionally sets the readonly attribute to the underlying textarea.
60
62
  * @member {Boolean} domReadOnly_=false
63
+ * @reactive
61
64
  */
62
65
  domReadOnly_: false,
63
66
  /**
64
67
  * Options are: 'vs', 'vs-dark', 'hc-black', 'hc-light'
65
68
  * @member {String} editorTheme_='vs'
69
+ * @reactive
66
70
  */
67
71
  editorTheme_: 'vs',
68
72
  /**
69
73
  * @member {Number} fontSize_=14
74
+ * @reactive
70
75
  */
71
76
  fontSize_: 14,
72
77
  /**
73
78
  * @member {String} language_='javascript'
79
+ * @reactive
74
80
  */
75
81
  language_: 'javascript',
76
82
  /**
77
83
  * @member {Object} minimap_={enabled: false}
84
+ * @reactive
78
85
  */
79
86
  minimap_: {enabled: false},
80
87
  /**
@@ -85,22 +92,27 @@ class MonacoEditor extends Base {
85
92
  * For initial options this config will win over related class configs.
86
93
  * However, run-time changes of related class configs will dynamically change their option values.
87
94
  * @member {Object} options_={}
95
+ * @reactive
88
96
  */
89
97
  options_: {},
90
98
  /**
91
99
  * @member {Boolean} readOnly_=false
100
+ * @reactive
92
101
  */
93
102
  readOnly_: false,
94
103
  /**
95
104
  * @member {Boolean} scrollBeyondLastLine_=false
105
+ * @reactive
96
106
  */
97
107
  scrollBeyondLastLine_: false,
98
108
  /**
99
109
  * @member {Boolean} showLineNumbers_=true
110
+ * @reactive
100
111
  */
101
112
  showLineNumbers_: true,
102
113
  /**
103
114
  * @member {String|String[]} value_=''
115
+ * @reactive
104
116
  */
105
117
  value_: ''
106
118
  }
@@ -29,10 +29,12 @@ class Accordion extends Panel {
29
29
  initialOpen_: [],
30
30
  /**
31
31
  * @member {Object} itemDefaults={ntype:'accordionitem'}
32
+ * @reactive
32
33
  */
33
34
  itemDefaults: {ntype: 'accordionitem'},
34
35
  /**
35
36
  * @member {Object[]} items=[]
37
+ * @reactive
36
38
  */
37
39
  items: [],
38
40
  /**
@@ -35,6 +35,7 @@ class Container extends Component {
35
35
  * Default configuration for child items within this container.
36
36
  * This config uses a descriptor to enable deep merging with instance based itemDefaults.
37
37
  * @member {Object} itemDefaults_={[isDescriptor]: true, merge: 'deep', value: null}
38
+ * @reactive
38
39
  */
39
40
  itemDefaults_: {
40
41
  [isDescriptor]: true,
@@ -91,6 +92,7 @@ class Container extends Component {
91
92
  * MyRedButton // you can drop imported modules directly into the items array
92
93
  * ]
93
94
  * });
95
+ * @reactive
94
96
  */
95
97
  items_: {
96
98
  [isDescriptor]: true,
@@ -104,6 +106,7 @@ class Container extends Component {
104
106
  * Meaning: onConstructed() is the latest life-cycle point.
105
107
  * You can use layout: 'base', in case you do not need a layout at all.
106
108
  * @member {Object|String|null} layout_={ntype: 'vbox', align: 'stretch'}
109
+ * @reactive
107
110
  */
108
111
  layout_: {
109
112
  ntype: 'vbox',
@@ -349,13 +352,14 @@ class Container extends Component {
349
352
 
350
353
  // Convenience logic, especially for moving components into different browser windows:
351
354
  // A component might rely on references & handler methods inside the previous controller realm
352
- if (!item.controller && !me.getController() && parent.getController()) {
355
+ // todo: We need ?. until functional.component.Base supports controllers
356
+ if (!item.controller && !me.getController() && parent.getController?.()) {
353
357
  item.controller = {parent: parent.getController()}
354
358
  }
355
359
  }
356
360
 
357
361
  item.set(config);
358
- item.getStateProvider()?.createBindings(item);
362
+ item.getStateProvider?.()?.createBindings(item);
359
363
  break
360
364
  }
361
365
 
@@ -437,7 +441,7 @@ class Container extends Component {
437
441
 
438
442
  // We need to add items into the vdom
439
443
  me.updateDepth = -1;
440
- me.update();
444
+ me.isConstructed && me.update();
441
445
 
442
446
  me.fire('itemsCreated', {id: me.id, items})
443
447
  }
@@ -36,6 +36,7 @@ class Panel extends Container {
36
36
  headers: null,
37
37
  /**
38
38
  * @member {Object} items={ntype: 'vbox', align: 'stretch'}
39
+ * @reactive
39
40
  */
40
41
  _layout: {
41
42
  ntype: 'vbox',
@@ -35,6 +35,7 @@ class Viewport extends Container {
35
35
  * true applies a main.addon.ResizeObserver and fires a custom resize event
36
36
  * which other instances can subscribe to.
37
37
  * @member {Boolean} monitorSize_=false
38
+ * @reactive
38
39
  */
39
40
  monitorSize_: false
40
41
  }
@@ -30,6 +30,7 @@ class Application extends Base {
30
30
  appThemeFolder: null,
31
31
  /**
32
32
  * @member {Neo.component.Base} mainView_=null
33
+ * @reactive
33
34
  */
34
35
  mainView_: null,
35
36
  /**
@@ -55,6 +55,7 @@ class Controller extends Base {
55
55
  * 'default' : 'handleOtherRoutes'
56
56
  * }
57
57
  * @member {Object} routes_={}
58
+ * @reactive
58
59
  */
59
60
  routes_: {}
60
61
  }
@@ -24,6 +24,7 @@ class Component extends Base {
24
24
  component: null,
25
25
  /**
26
26
  * @member {Neo.controller.Component|null} parent_=null
27
+ * @reactive
27
28
  */
28
29
  parent_: null,
29
30
  /**
package/src/core/Base.mjs CHANGED
@@ -58,7 +58,26 @@ class Base {
58
58
  */
59
59
  static overwrittenMethods = {}
60
60
  /**
61
- * Configs will get merged throughout the class hierarchy
61
+ * Defines the default configuration properties for the class. These configurations are
62
+ * merged throughout the class hierarchy and can be overridden at the instance level.
63
+ *
64
+ * There are two main types of configs:
65
+ *
66
+ * 1. **Reactive Configs:** Property names ending with a trailing underscore (e.g., `myConfig_`).
67
+ * The framework automatically generates a public getter and setter, removing the underscore
68
+ * from the property name (e.g., `this.myConfig`). This system enables powerful, optional
69
+ * lifecycle hooks that are called automatically if they are implemented on the class:
70
+ * - `beforeGetMyConfig(value)`: Executed before the getter returns. Can be used to dynamically modify the returned value.
71
+ * - `beforeSetMyConfig(newValue, oldValue)`: Executed before a new value is set. Can be used for validation or transformation. Returning `undefined` from this hook will cancel the update.
72
+ * - `afterSetMyConfig(newValue, oldValue)`: Executed after a new value has been successfully set. Ideal for triggering side effects.
73
+ *
74
+ * 2. **Non-Reactive (Prototype-based) Configs:** Property names without a trailing underscore.
75
+ * These are applied directly to the class's **prototype** during the `Neo.setupClass`
76
+ * process. This is highly memory-efficient as the value is shared across all instances.
77
+ * It also allows for powerful, application-wide modifications of default behaviors
78
+ * by using the `Neo.overwrites` mechanism, which modifies these prototype values at
79
+ * load time.
80
+ *
62
81
  * @returns {Object} config
63
82
  */
64
83
  static config = {
@@ -83,6 +102,7 @@ class Base {
83
102
  /**
84
103
  * The unique component id
85
104
  * @member {String|null} id_=null
105
+ * @reactive
86
106
  */
87
107
  id_: null,
88
108
  /**
@@ -104,6 +124,7 @@ class Base {
104
124
  * Effects can observe this config to clean themselves up.
105
125
  * @member {Boolean} isDestroying_=false
106
126
  * @protected
127
+ * @reactive
107
128
  */
108
129
  isDestroying_: false,
109
130
  /**
@@ -112,6 +133,7 @@ class Base {
112
133
  * Since not all classes use the Observable mixin, Neo will not fire an event.
113
134
  * method body.
114
135
  * @member {Boolean} isReady_=false
136
+ * @reactive
115
137
  */
116
138
  isReady_: false,
117
139
  /**
@@ -133,6 +155,7 @@ class Base {
133
155
  *
134
156
  * @member {Object|null} remote_=null
135
157
  * @protected
158
+ * @reactive
136
159
  */
137
160
  remote_: null
138
161
  }
@@ -290,9 +313,38 @@ class Base {
290
313
  }
291
314
 
292
315
  /**
293
- * Applying overwrites and adding overwrittenMethods to the class constructors
294
- * @param {Object} cfg
316
+ * This static method is called by `Neo.setupClass()` during the class creation process.
317
+ * It allows for modifying a class's default prototype-based configs from outside the
318
+ * class hierarchy, which is a powerful way to avoid boilerplate code.
319
+ *
320
+ * It looks for a matching entry in the global `Neo.overwrites` object based on the
321
+ * class's `className`. If found, it merges the properties from the overwrite object
322
+ * into the class's static `config`. This provides a powerful mechanism for theming
323
+ * or applying application-wide customizations to framework or library classes without
324
+ * needing to extend them.
325
+ *
326
+ * @example
327
+ * // Imagine you have hundreds of buttons in your app, and you want all of them
328
+ * // to have `labelPosition: 'top'` instead of the default `'left'`.
329
+ * // Instead of configuring each instance, you can define an overwrite.
330
+ *
331
+ * // inside an Overwrites.mjs file loaded by your app:
332
+ * Neo.overwrites = {
333
+ * Neo: {
334
+ * button: {
335
+ * Base: {
336
+ * labelPosition: 'top'
337
+ * }
338
+ * }
339
+ * }
340
+ * };
341
+ *
342
+ * // Now, every `Neo.button.Base` (and any class that extends it) will have this
343
+ * // new default value on its prototype.
344
+ *
345
+ * @param {Object} cfg The static `config` object of the class being processed.
295
346
  * @protected
347
+ * @static
296
348
  */
297
349
  static applyOverwrites(cfg) {
298
350
  let overwrites = Neo.ns(cfg.className, false, Neo.overwrites),
@@ -163,10 +163,7 @@ class Compare {
163
163
  }
164
164
  }
165
165
 
166
- const ns = Neo.ns('Neo.core', true);
167
- ns.Compare = Compare;
168
-
169
- // alias
170
- Neo.isEqual = Compare.isEqual;
171
-
172
- export default Compare;
166
+ export default Neo.gatekeep(Compare, 'Neo.core.Compare', () => {
167
+ // alias
168
+ Neo.isEqual = Compare.isEqual
169
+ });
@@ -14,13 +14,26 @@ import {isDescriptor} from './ConfigSymbols.mjs';
14
14
  */
15
15
  class Config {
16
16
  /**
17
- * A Set to store callback functions that subscribe to changes in this config's value.
17
+ * Stores all subscriptions for this Config instance.
18
+ * The data structure is a Map where:
19
+ * - The key is the ID of the subscription owner (e.g., a component's `id`).
20
+ * - The value is another Map (the subscriberMap).
21
+ *
22
+ * The nested subscriberMap is structured as:
23
+ * - The key is the callback function (`fn`).
24
+ * - The value is a Set of scopes (`scopeSet`).
25
+ *
26
+ * This nested structure `Map<string, Map<function, Set<scope>>>` is intentionally chosen
27
+ * to robustly handle the edge case where the same function is subscribed multiple times
28
+ * with different scopes, all under the same owner ID. It ensures that each
29
+ * `fn`-`scope` combination is unique and that cleanup is precise.
30
+ * @member {Object} #subscribers={}
18
31
  * @private
19
32
  */
20
33
  #subscribers = {}
21
34
  /**
22
35
  * The internal value of the config property.
23
- * @member #value
36
+ * @member {*} #value
24
37
  * @private
25
38
  */
26
39
  #value
@@ -67,7 +80,7 @@ class Config {
67
80
  get() {
68
81
  // Registers this Config instance as a dependency with the currently active Effect,
69
82
  // enabling automatic re-execution when this Config's value changes.
70
- EffectManager.getActiveEffect()?.addDependency(this);
83
+ EffectManager.addDependency(this);
71
84
  return this.#value
72
85
  }
73
86
 
@@ -75,52 +88,56 @@ class Config {
75
88
  * Initializes the `Config` instance using a descriptor object.
76
89
  * Extracts `clone`, `mergeStrategy` and `isEqual` from the descriptor.
77
90
  * The internal `#value` is NOT set by this method.
78
- * @param {Object} descriptor - The descriptor object for the config.
79
- * @param {any} descriptor.value - The default value for the config (not set by this method).
91
+ * @param {Object} descriptor - The descriptor object for the config.
80
92
  * @param {string} [descriptor.clone='deep'] - The clone strategy for set.
81
93
  * @param {string} [descriptor.cloneOnGet] - The clone strategy for get. Defaults to 'shallow' if clone is 'deep' or 'shallow', and 'none' if clone is 'none'.
82
- * @param {string} [descriptor.merge='deep'] - The merge strategy.
83
94
  * @param {Function} [descriptor.isEqual=Neo.isEqual] - The equality comparison function.
95
+ * @param {string} [descriptor.merge='deep'] - The merge strategy.
96
+ * @param {any} descriptor.value - The default value for the config (not set by this method).
84
97
  */
85
98
  initDescriptor({clone, cloneOnGet, isEqual, merge}) {
86
99
  let me = this;
87
100
 
88
101
  if (clone && clone !== me.clone) {
89
102
  Object.defineProperty(me, 'clone', {
90
- value: clone, writable: true, configurable: true, enumerable: true
103
+ configurable: true, enumerable: true, value: clone, writable: true
91
104
  })
92
105
  }
93
106
 
94
107
  if (cloneOnGet && cloneOnGet !== me.cloneOnGet) {
95
108
  Object.defineProperty(me, 'cloneOnGet', {
96
- value: cloneOnGet, writable: true, configurable: true, enumerable: true
109
+ configurable: true, enumerable: true, value: cloneOnGet, writable: true
97
110
  })
98
111
  }
99
112
 
100
113
  if (isEqual && isEqual !== me.isEqual) {
101
114
  Object.defineProperty(me, 'isEqual', {
102
- value: isEqual, writable: true, configurable: true, enumerable: true
115
+ configurable: true, enumerable: true, value: isEqual, writable: true
103
116
  })
104
117
  }
105
118
 
106
119
  if (merge && merge !== me.mergeStrategy) {
107
120
  Object.defineProperty(me, 'mergeStrategy', {
108
- value: merge, writable: true, configurable: true, enumerable: true
121
+ configurable: true, enumerable: true, value: merge, writable: true
109
122
  })
110
123
  }
111
124
  }
112
125
 
113
126
  /**
114
127
  * Notifies all subscribed callbacks about a change in the config's value.
128
+ * It iterates through the nested subscriber structure to ensure each callback
129
+ * is executed with its intended scope.
115
130
  * @param {any} newValue - The new value of the config.
116
131
  * @param {any} oldValue - The old value of the config.
117
132
  */
118
133
  notify(newValue, oldValue) {
119
134
  for (const id in this.#subscribers) {
120
135
  if (this.#subscribers.hasOwnProperty(id)) {
121
- const subscriberSet = this.#subscribers[id];
122
- for (const callback of subscriberSet) {
123
- callback(newValue, oldValue)
136
+ const subscriberMap = this.#subscribers[id];
137
+ for (const [fn, scopeSet] of subscriberMap) {
138
+ for (const scope of scopeSet) {
139
+ fn.call(scope || null, newValue, oldValue)
140
+ }
124
141
  }
125
142
  }
126
143
  }
@@ -164,12 +181,13 @@ class Config {
164
181
  /**
165
182
  * Subscribes a callback function to changes in this config's value.
166
183
  * The callback will be invoked with `(newValue, oldValue)` whenever the config changes.
167
- * @param {Object} options - An object containing the subscription details.
168
- * @param {String} options.id - The ID of the subscription owner (e.g., a Neo.core.Base instance's id).
169
- * @param {Function} options.fn - The callback function.
184
+ * @param {Object} options - An object containing the subscription details.
185
+ * @param {String} options.id - The ID of the subscription owner (e.g., a Neo.core.Base instance's id).
186
+ * @param {Function} options.fn - The callback function.
187
+ * @param {Object} [options.scope] - The scope to execute the callback in.
170
188
  * @returns {Function} A cleanup function to unsubscribe the callback.
171
189
  */
172
- subscribe({id, fn}) {
190
+ subscribe({id, fn, scope}) {
173
191
  if (typeof id !== 'string' || id.length === 0 || typeof fn !== 'function') {
174
192
  throw new Error([
175
193
  'Config.subscribe: options must be an object with a non-empty string `id` ',
@@ -179,18 +197,36 @@ class Config {
179
197
 
180
198
  const me = this;
181
199
 
200
+ // Get or create the top-level Map for the subscription owner.
182
201
  if (!me.#subscribers[id]) {
183
- me.#subscribers[id] = new Set()
202
+ me.#subscribers[id] = new Map()
184
203
  }
185
204
 
186
- me.#subscribers[id].add(fn);
205
+ const subscriberMap = me.#subscribers[id];
206
+
207
+ // Get or create the Set of scopes for the specific callback function.
208
+ if (!subscriberMap.has(fn)) {
209
+ subscriberMap.set(fn, new Set())
210
+ }
187
211
 
212
+ const scopeSet = subscriberMap.get(fn);
213
+ scopeSet.add(scope);
214
+
215
+ // The returned cleanup function is precise. It removes only the specific
216
+ // scope for the function, and cleans up the parent data structures
217
+ // (the Set and the Maps) only if they become empty.
188
218
  return () => {
189
- const subscriberSet = me.#subscribers[id];
190
- if (subscriberSet) {
191
- subscriberSet.delete(fn);
192
- if (subscriberSet.size === 0) {
193
- delete me.#subscribers[id]
219
+ const currentSubscriberMap = me.#subscribers[id];
220
+ if (currentSubscriberMap) {
221
+ const currentScopeSet = currentSubscriberMap.get(fn);
222
+ if (currentScopeSet) {
223
+ currentScopeSet.delete(scope);
224
+ if (currentScopeSet.size === 0) {
225
+ currentSubscriberMap.delete(fn);
226
+ if (currentSubscriberMap.size === 0) {
227
+ delete me.#subscribers[id]
228
+ }
229
+ }
194
230
  }
195
231
  }
196
232
  }
@@ -198,33 +234,10 @@ class Config {
198
234
  }
199
235
 
200
236
  Object.defineProperties(Config.prototype, {
201
- clone: {
202
- value: 'deep',
203
- writable: false,
204
- configurable: true,
205
- enumerable: false
206
- },
207
- cloneOnGet: {
208
- value: null,
209
- writable: false,
210
- configurable: true,
211
- enumerable: false
212
- },
213
- isEqual: {
214
- value: Neo.isEqual,
215
- writable: false,
216
- configurable: true,
217
- enumerable: false
218
- },
219
- mergeStrategy: {
220
- value: 'replace',
221
- writable: false,
222
- configurable: true,
223
- enumerable: false
224
- }
237
+ clone : {configurable: true, enumerable: false, value: 'deep', writable: false},
238
+ cloneOnGet : {configurable: true, enumerable: false, value: null, writable: false},
239
+ isEqual : {configurable: true, enumerable: false, value: Neo.isEqual, writable: false},
240
+ mergeStrategy: {configurable: true, enumerable: false, value: 'replace', writable: false}
225
241
  });
226
242
 
227
- const ns = Neo.ns('Neo.core', true);
228
- ns.Config = Config;
229
-
230
- export default Config;
243
+ export default Neo.gatekeep(Config, 'Neo.core.Config');