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.
- package/.github/RELEASE_NOTES/v10.0.0-beta.4.md +2 -2
- package/.github/RELEASE_NOTES/v10.0.0-beta.5.md +70 -0
- package/.github/RELEASE_NOTES/v10.0.0-beta.6.md +48 -0
- package/.github/epic-functional-components.md +498 -0
- package/.github/ticket-asymmetric-vdom-updates.md +122 -0
- package/README.md +0 -3
- package/ServiceWorker.mjs +2 -2
- package/apps/colors/store/Colors.mjs +1 -0
- package/apps/colors/view/GridContainer.mjs +3 -0
- package/apps/colors/view/HeaderToolbar.mjs +2 -0
- package/apps/colors/view/Viewport.mjs +3 -0
- package/apps/covid/view/FooterContainer.mjs +3 -0
- package/apps/covid/view/GalleryContainer.mjs +2 -0
- package/apps/covid/view/GalleryContainerController.mjs +1 -0
- package/apps/covid/view/HeaderContainer.mjs +2 -0
- package/apps/covid/view/HelixContainer.mjs +2 -0
- package/apps/covid/view/HelixContainerController.mjs +1 -0
- package/apps/covid/view/MainContainer.mjs +3 -0
- package/apps/covid/view/TableContainer.mjs +3 -0
- package/apps/covid/view/TableContainerController.mjs +1 -0
- package/apps/covid/view/WorldMapContainer.mjs +2 -0
- package/apps/covid/view/country/Gallery.mjs +3 -0
- package/apps/covid/view/country/Helix.mjs +8 -0
- package/apps/covid/view/country/HistoricalDataTable.mjs +1 -0
- package/apps/covid/view/country/Table.mjs +2 -0
- package/apps/covid/view/mapboxGl/Component.mjs +1 -0
- package/apps/covid/view/mapboxGl/Container.mjs +2 -0
- package/apps/email/EPIC_PLAN.md +58 -0
- package/apps/email/neo-config.json +2 -2
- package/apps/email/store/Emails.mjs +11 -1
- package/apps/email/view/ComposeView.mjs +44 -0
- package/apps/email/view/MainView.mjs +89 -0
- package/apps/email/view/Viewport.mjs +4 -33
- package/apps/email/view/ViewportStateProvider.mjs +3 -3
- package/apps/form/store/SideNav.mjs +1 -0
- package/apps/form/view/FormContainer.mjs +1 -0
- package/apps/form/view/FormPageContainer.mjs +2 -0
- package/apps/form/view/SideNavList.mjs +1 -0
- package/apps/form/view/Viewport.mjs +3 -0
- package/apps/portal/childapps/preview/MainContainer.mjs +1 -0
- package/apps/portal/index.html +1 -1
- package/apps/portal/store/BlogPosts.mjs +2 -0
- package/apps/portal/store/Content.mjs +1 -0
- package/apps/portal/store/ContentSections.mjs +1 -0
- package/apps/portal/store/Examples.mjs +1 -0
- package/apps/portal/view/HeaderToolbar.mjs +1 -0
- package/apps/portal/view/Viewport.mjs +5 -0
- package/apps/portal/view/ViewportController.mjs +8 -2
- package/apps/portal/view/about/Container.mjs +2 -0
- package/apps/portal/view/about/MemberContainer.mjs +7 -0
- package/apps/portal/view/blog/Container.mjs +2 -0
- package/apps/portal/view/blog/List.mjs +2 -0
- package/apps/portal/view/examples/List.mjs +1 -0
- package/apps/portal/view/examples/TabContainer.mjs +4 -0
- package/apps/portal/view/home/ContentBox.mjs +3 -0
- package/apps/portal/view/home/FeatureSection.mjs +8 -0
- package/apps/portal/view/home/FooterContainer.mjs +4 -1
- package/apps/portal/view/home/MainContainer.mjs +2 -0
- package/apps/portal/view/home/parts/AfterMath.mjs +2 -0
- package/apps/portal/view/home/parts/BaseContainer.mjs +1 -0
- package/apps/portal/view/home/parts/Colors.mjs +4 -0
- package/apps/portal/view/home/parts/Features.mjs +2 -0
- package/apps/portal/view/home/parts/Helix.mjs +5 -0
- package/apps/portal/view/home/parts/How.mjs +4 -0
- package/apps/portal/view/home/parts/MainNeo.mjs +1 -0
- package/apps/portal/view/home/parts/References.mjs +2 -0
- package/apps/portal/view/learn/ContentComponent.mjs +11 -5
- package/apps/portal/view/learn/ContentTreeList.mjs +2 -0
- package/apps/portal/view/learn/CubeLayoutButton.mjs +1 -0
- package/apps/portal/view/learn/MainContainer.mjs +4 -0
- package/apps/portal/view/learn/PageContainer.mjs +2 -0
- package/apps/portal/view/learn/PageSectionsContainer.mjs +3 -0
- package/apps/portal/view/learn/PageSectionsList.mjs +1 -0
- package/apps/portal/view/services/Component.mjs +1 -0
- package/apps/realworld/api/Base.mjs +1 -0
- package/apps/realworld/view/HeaderComponent.mjs +4 -0
- package/apps/realworld/view/HomeComponent.mjs +7 -0
- package/apps/realworld/view/MainContainer.mjs +2 -0
- package/apps/realworld/view/MainContainerController.mjs +2 -0
- package/apps/realworld/view/article/CommentComponent.mjs +3 -0
- package/apps/realworld/view/article/Component.mjs +17 -10
- package/apps/realworld/view/article/CreateCommentComponent.mjs +2 -0
- package/apps/realworld/view/article/CreateComponent.mjs +5 -0
- package/apps/realworld/view/article/PreviewComponent.mjs +9 -0
- package/apps/realworld/view/article/TagListComponent.mjs +2 -0
- package/apps/realworld/view/user/ProfileComponent.mjs +7 -0
- package/apps/realworld/view/user/SettingsComponent.mjs +5 -0
- package/apps/realworld/view/user/SignUpComponent.mjs +3 -0
- package/apps/realworld2/api/Base.mjs +1 -0
- package/apps/realworld2/view/FooterComponent.mjs +1 -0
- package/apps/realworld2/view/HeaderToolbar.mjs +3 -0
- package/apps/realworld2/view/HomeContainer.mjs +1 -0
- package/apps/realworld2/view/MainContainer.mjs +2 -0
- package/apps/realworld2/view/MainContainerController.mjs +1 -0
- package/apps/realworld2/view/article/Helix.mjs +1 -0
- package/apps/realworld2/view/article/PreviewComponent.mjs +9 -0
- package/apps/realworld2/view/article/PreviewList.mjs +1 -0
- package/apps/realworld2/view/article/TagListComponent.mjs +2 -0
- package/apps/route/view/CenterContainer.mjs +1 -0
- package/apps/route/view/MainView.mjs +1 -0
- package/apps/sharedcovid/childapps/sharedcovidchart/MainContainer.mjs +1 -0
- package/apps/sharedcovid/childapps/sharedcovidgallery/MainContainer.mjs +1 -0
- package/apps/sharedcovid/childapps/sharedcovidhelix/MainContainer.mjs +1 -0
- package/apps/sharedcovid/childapps/sharedcovidmap/MainContainer.mjs +1 -0
- package/apps/sharedcovid/view/FooterContainer.mjs +3 -0
- package/apps/sharedcovid/view/GalleryContainer.mjs +2 -0
- package/apps/sharedcovid/view/GalleryContainerController.mjs +1 -0
- package/apps/sharedcovid/view/HeaderContainer.mjs +2 -0
- package/apps/sharedcovid/view/HelixContainer.mjs +2 -0
- package/apps/sharedcovid/view/HelixContainerController.mjs +1 -0
- package/apps/sharedcovid/view/MainContainer.mjs +3 -0
- package/apps/sharedcovid/view/TableContainer.mjs +3 -0
- package/apps/sharedcovid/view/TableContainerController.mjs +1 -0
- package/apps/sharedcovid/view/WorldMapContainer.mjs +2 -0
- package/apps/sharedcovid/view/country/Gallery.mjs +3 -0
- package/apps/sharedcovid/view/country/Helix.mjs +8 -0
- package/apps/sharedcovid/view/country/HistoricalDataTable.mjs +1 -0
- package/apps/sharedcovid/view/country/Table.mjs +2 -0
- package/apps/sharedcovid/view/mapboxGl/Component.mjs +1 -0
- package/apps/sharedcovid/view/mapboxGl/Container.mjs +2 -0
- package/apps/shareddialog/childapps/shareddialog2/view/MainContainer.mjs +2 -0
- package/apps/shareddialog/view/DemoDialog.mjs +2 -0
- package/apps/shareddialog/view/MainContainer.mjs +2 -0
- package/apps/shareddialog/view/MainContainerController.mjs +1 -0
- package/buildScripts/addReactiveTags.mjs +191 -0
- package/buildScripts/checkReactiveTags.mjs +160 -0
- package/docs/app/store/Api.mjs +1 -0
- package/docs/app/store/Examples.mjs +1 -0
- package/docs/app/view/ApiTreeList.mjs +1 -0
- package/docs/app/view/ContentTabContainer.mjs +2 -0
- package/docs/app/view/ExamplesTreeList.mjs +2 -0
- package/docs/app/view/HeaderContainer.mjs +3 -0
- package/docs/app/view/MainContainer.mjs +5 -0
- package/docs/app/view/classdetails/HeaderComponent.mjs +1 -0
- package/docs/app/view/classdetails/MainContainer.mjs +3 -0
- package/docs/app/view/classdetails/MembersList.mjs +5 -0
- package/docs/app/view/classdetails/SourceViewComponent.mjs +2 -0
- package/examples/ConfigurationViewport.mjs +14 -8
- package/examples/button/effect/MainContainer.mjs +207 -0
- package/examples/button/effect/app.mjs +6 -0
- package/examples/button/effect/index.html +11 -0
- package/examples/button/effect/neo-config.json +6 -0
- package/examples/calendar/weekview/MainContainer.mjs +4 -0
- package/examples/component/coronaGallery/CountryGallery.mjs +2 -0
- package/examples/component/coronaGallery/CountryStore.mjs +1 -0
- package/examples/component/coronaGallery/Viewport.mjs +3 -0
- package/examples/component/coronaGallery/ViewportController.mjs +1 -0
- package/examples/component/coronaHelix/CountryHelix.mjs +7 -0
- package/examples/component/coronaHelix/MainContainer.mjs +1 -0
- package/examples/component/gallery/ImageStore.mjs +1 -0
- package/examples/component/helix/ImageStore.mjs +1 -0
- package/examples/component/helix/Viewport.mjs +3 -0
- package/examples/component/helix/ViewportController.mjs +1 -0
- package/examples/component/multiWindowCoronaGallery/childapp/Viewport.mjs +1 -0
- package/examples/component/multiWindowHelix/childapp/Viewport.mjs +1 -0
- package/examples/component/wrapper/googleMaps/MapComponent.mjs +2 -0
- package/examples/core/config/MainContainer.mjs +2 -0
- package/examples/dialog/DemoDialog.mjs +2 -0
- package/examples/dialog/MainContainer.mjs +1 -0
- package/examples/form/field/color/MainStore.mjs +1 -0
- package/examples/functional/defineComponent/Component.mjs +18 -0
- package/examples/functional/defineComponent/MainContainer.mjs +41 -0
- package/examples/functional/defineComponent/app.mjs +6 -0
- package/examples/functional/defineComponent/index.html +11 -0
- package/examples/functional/defineComponent/neo-config.json +6 -0
- package/examples/functional/hostComponent/Component.mjs +32 -0
- package/examples/functional/hostComponent/MainContainer.mjs +48 -0
- package/examples/functional/hostComponent/app.mjs +6 -0
- package/examples/functional/hostComponent/index.html +11 -0
- package/examples/functional/hostComponent/neo-config.json +6 -0
- package/examples/grid/animatedRowSorting/Viewport.mjs +1 -1
- package/examples/grid/bigData/ControlsContainer.mjs +3 -0
- package/examples/grid/bigData/GridContainer.mjs +4 -2
- package/examples/grid/bigData/MainContainer.mjs +2 -0
- package/examples/grid/bigData/MainModel.mjs +1 -0
- package/examples/grid/bigData/MainStore.mjs +3 -0
- package/examples/grid/cellEditing/MainContainer.mjs +1 -1
- package/examples/grid/container/MainContainer.mjs +1 -1
- package/examples/grid/covid/GridContainer.mjs +3 -0
- package/examples/grid/covid/MainContainer.mjs +2 -0
- package/examples/grid/covid/Store.mjs +1 -0
- package/examples/grid/nestedRecordFields/EditUserDialog.mjs +3 -0
- package/examples/grid/nestedRecordFields/Viewport.mjs +3 -1
- package/examples/list/animate/List.mjs +4 -0
- package/examples/list/animate/MainContainer.mjs +2 -0
- package/examples/list/circle/MainStore.mjs +1 -0
- package/examples/list/color/MainStore.mjs +1 -0
- package/examples/preloadingAssets/view/MainContainer.mjs +2 -0
- package/examples/stateProvider/advanced/MainContainer.mjs +1 -0
- package/examples/stateProvider/dialog/EditUserDialog.mjs +2 -0
- package/examples/stateProvider/dialog/MainContainer.mjs +1 -0
- package/examples/stateProvider/extendedClass/MainContainer.mjs +2 -0
- package/examples/stateProvider/inline/MainContainer.mjs +1 -0
- package/examples/stateProvider/inlineNoStateProvider/MainContainer.mjs +1 -0
- package/examples/stateProvider/inlineNoStateProvider/MainContainerController.mjs +2 -0
- package/examples/stateProvider/multiWindow/EditUserDialog.mjs +3 -0
- package/examples/stateProvider/multiWindow/MainContainer.mjs +1 -0
- package/examples/stateProvider/multiWindow/Viewport.mjs +1 -0
- package/examples/stateProvider/nestedData/MainContainer.mjs +1 -0
- package/examples/stateProvider/table/MainContainer.mjs +1 -0
- package/examples/table/covid/MainContainer.mjs +2 -0
- package/examples/table/covid/Store.mjs +1 -0
- package/examples/table/covid/TableContainer.mjs +3 -0
- package/examples/table/nestedRecordFields/EditUserDialog.mjs +3 -0
- package/examples/table/nestedRecordFields/Viewport.mjs +1 -0
- package/examples/todoList/version1/MainComponent.mjs +1 -1
- package/examples/toolbar/breadcrumb/view/MainContainer.mjs +2 -0
- package/examples/toolbar/paging/store/Users.mjs +1 -0
- package/examples/toolbar/paging/view/AddUserDialog.mjs +3 -0
- package/examples/toolbar/paging/view/MainContainer.mjs +3 -0
- package/examples/treeAccordion/MainContainer.mjs +2 -2
- package/examples/worker/task/MainContainer.mjs +1 -0
- package/learn/Glossary.md +1 -0
- package/learn/UsingTheseTopics.md +1 -0
- package/learn/benefits/ConfigSystem.md +2 -0
- package/learn/benefits/Effort.md +1 -0
- package/learn/benefits/Features.md +1 -0
- package/learn/benefits/FormsEngine.md +1 -0
- package/learn/benefits/FourEnvironments.md +2 -0
- package/learn/benefits/Introduction.md +2 -0
- package/learn/benefits/MultiWindow.md +3 -1
- package/learn/benefits/OffTheMainThread.md +2 -0
- package/learn/benefits/Quick.md +2 -0
- package/learn/benefits/RPCLayer.md +2 -0
- package/learn/benefits/Speed.md +2 -0
- package/learn/comparisons/NeoVsAngular.md +90 -0
- package/learn/comparisons/NeoVsExtJs.md +178 -0
- package/learn/comparisons/NeoVsNextJs.md +124 -0
- package/learn/comparisons/NeoVsReact.md +95 -0
- package/learn/comparisons/NeoVsSolid.md +78 -0
- package/learn/comparisons/NeoVsVue.md +92 -0
- package/learn/comparisons/Overview.md +46 -0
- package/learn/gettingstarted/ComponentModels.md +2 -0
- package/learn/gettingstarted/Config.md +2 -0
- package/learn/gettingstarted/DescribingTheUI.md +2 -0
- package/learn/gettingstarted/Events.md +2 -0
- package/learn/gettingstarted/Extending.md +2 -0
- package/learn/gettingstarted/References.md +2 -0
- package/learn/gettingstarted/Setup.md +3 -2
- package/learn/gettingstarted/Workspaces.md +2 -0
- package/learn/guides/datahandling/Collections.md +1 -0
- package/learn/guides/datahandling/Records.md +1 -0
- package/learn/guides/datahandling/StateProviders.md +131 -16
- package/learn/guides/datahandling/Tables.md +1 -1
- package/learn/guides/fundamentals/ConfigSystemDeepDive.md +1 -0
- package/learn/guides/fundamentals/DeclarativeComponentTreesVsImperativeVdom.md +2 -0
- package/learn/guides/fundamentals/DeclarativeVDOMWithEffects.md +168 -0
- package/learn/guides/fundamentals/ExtendingNeoClasses.md +1 -0
- package/learn/guides/fundamentals/InstanceLifecycle.md +3 -1
- package/learn/guides/fundamentals/MainThreadAddons.md +2 -0
- package/learn/guides/specificfeatures/Mixins.md +3 -1
- package/learn/guides/specificfeatures/MultiWindow.md +3 -1
- package/learn/guides/specificfeatures/PortalApp.md +2 -0
- package/learn/guides/uibuildingblocks/ComponentsAndContainers.md +2 -0
- package/learn/guides/uibuildingblocks/CustomComponents.md +2 -0
- package/learn/guides/uibuildingblocks/Layouts.md +2 -0
- package/learn/guides/uibuildingblocks/WorkingWithVDom.md +2 -0
- package/learn/guides/userinteraction/Forms.md +2 -0
- package/learn/guides/userinteraction/events/CustomEvents.md +2 -1
- package/learn/guides/userinteraction/events/DomEvents.md +2 -0
- package/learn/javascript/ClassFeatures.md +4 -3
- package/learn/javascript/Classes.md +10 -13
- package/learn/javascript/Overrides.md +10 -6
- package/learn/javascript/Super.md +12 -8
- package/learn/tree.json +71 -63
- package/learn/tutorials/Earthquakes.md +2 -0
- package/learn/tutorials/RSP.md +3 -1
- package/learn/tutorials/TodoList.md +103 -7
- package/package.json +6 -4
- package/resources/scss/src/apps/email/ComposeView.scss +16 -0
- package/resources/scss/src/apps/email/MainView.scss +5 -0
- package/resources/scss/src/apps/portal/learn/ContentComponent.scss +5 -4
- package/src/DefaultConfig.mjs +12 -2
- package/src/Main.mjs +1 -0
- package/src/Neo.mjs +377 -178
- package/src/Xhr.mjs +1 -0
- package/src/button/Base.mjs +13 -0
- package/src/button/Effect.mjs +449 -0
- package/src/button/Split.mjs +2 -0
- package/src/calendar/store/Calendars.mjs +1 -0
- package/src/calendar/store/Colors.mjs +1 -0
- package/src/calendar/store/Events.mjs +1 -0
- package/src/calendar/view/DayComponent.mjs +2 -0
- package/src/calendar/view/EditEventContainer.mjs +4 -1
- package/src/calendar/view/MainContainer.mjs +13 -0
- package/src/calendar/view/MainContainerStateProvider.mjs +14 -28
- package/src/calendar/view/SettingsContainer.mjs +1 -0
- package/src/calendar/view/YearComponent.mjs +16 -0
- package/src/calendar/view/calendars/ColorsList.mjs +2 -0
- package/src/calendar/view/calendars/Container.mjs +2 -0
- package/src/calendar/view/calendars/EditContainer.mjs +1 -0
- package/src/calendar/view/month/Component.mjs +11 -0
- package/src/calendar/view/settings/GeneralContainer.mjs +1 -0
- package/src/calendar/view/settings/MonthContainer.mjs +1 -0
- package/src/calendar/view/settings/WeekContainer.mjs +1 -0
- package/src/calendar/view/settings/YearContainer.mjs +1 -0
- package/src/calendar/view/week/Component.mjs +15 -1
- package/src/calendar/view/week/TimeAxisComponent.mjs +4 -0
- package/src/code/LivePreview.mjs +51 -23
- package/src/collection/Base.mjs +14 -12
- package/src/collection/Filter.mjs +6 -0
- package/src/collection/Sorter.mjs +3 -0
- package/src/component/Base.mjs +156 -802
- package/src/component/Canvas.mjs +1 -0
- package/src/component/Chip.mjs +4 -0
- package/src/component/Circle.mjs +14 -0
- package/src/component/Clock.mjs +4 -0
- package/src/component/DateSelector.mjs +12 -0
- package/src/component/Gallery.mjs +11 -0
- package/src/component/Helix.mjs +24 -0
- package/src/component/Label.mjs +1 -0
- package/src/component/Legend.mjs +3 -0
- package/src/component/MagicMoveText.mjs +4 -0
- package/src/component/Progress.mjs +3 -0
- package/src/component/Splitter.mjs +3 -0
- package/src/component/StatusBadge.mjs +6 -0
- package/src/component/Timer.mjs +4 -0
- package/src/component/Toast.mjs +6 -0
- package/src/component/Video.mjs +1 -0
- package/src/component/mwc/Button.mjs +7 -0
- package/src/component/mwc/TextField.mjs +9 -0
- package/src/component/wrapper/AmChart.mjs +2 -0
- package/src/component/wrapper/GoogleMaps.mjs +3 -0
- package/src/component/wrapper/MapboxGL.mjs +5 -0
- package/src/component/wrapper/MonacoEditor.mjs +12 -0
- package/src/container/Accordion.mjs +2 -0
- package/src/container/Base.mjs +34 -26
- package/src/container/Panel.mjs +1 -0
- package/src/container/Viewport.mjs +1 -0
- package/src/controller/Application.mjs +1 -0
- package/src/controller/Base.mjs +1 -0
- package/src/controller/Component.mjs +1 -0
- package/src/core/Base.mjs +193 -22
- package/src/core/Compare.mjs +4 -7
- package/src/core/Config.mjs +137 -33
- package/src/core/Effect.mjs +193 -0
- package/src/core/EffectBatchManager.mjs +67 -0
- package/src/core/EffectManager.mjs +60 -0
- package/src/core/IdGenerator.mjs +13 -44
- package/src/data/Model.mjs +2 -0
- package/src/data/Store.mjs +7 -0
- package/src/data/connection/WebSocket.mjs +2 -0
- package/src/date/DayViewComponent.mjs +2 -0
- package/src/date/SelectorContainer.mjs +14 -0
- package/src/dialog/Base.mjs +8 -0
- package/src/draggable/DragZone.mjs +5 -0
- package/src/draggable/tree/DragZone.mjs +1 -0
- package/src/filter/BooleanContainer.mjs +2 -0
- package/src/filter/NumberContainer.mjs +3 -0
- package/src/filter/ToggleOperatorsButton.mjs +2 -0
- package/src/form/Fieldset.mjs +6 -0
- package/src/form/field/Base.mjs +7 -0
- package/src/form/field/CheckBox.mjs +18 -0
- package/src/form/field/Chip.mjs +1 -0
- package/src/form/field/ComboBox.mjs +8 -0
- package/src/form/field/Country.mjs +1 -0
- package/src/form/field/Currency.mjs +2 -0
- package/src/form/field/Date.mjs +4 -0
- package/src/form/field/Display.mjs +1 -0
- package/src/form/field/Email.mjs +1 -0
- package/src/form/field/FileUpload.mjs +7 -0
- package/src/form/field/Hidden.mjs +1 -0
- package/src/form/field/Number.mjs +7 -0
- package/src/form/field/Password.mjs +1 -0
- package/src/form/field/Phone.mjs +3 -0
- package/src/form/field/Picker.mjs +2 -0
- package/src/form/field/Radio.mjs +1 -0
- package/src/form/field/Range.mjs +3 -0
- package/src/form/field/Search.mjs +2 -0
- package/src/form/field/Text.mjs +32 -0
- package/src/form/field/TextArea.mjs +7 -0
- package/src/form/field/Time.mjs +6 -0
- package/src/form/field/Url.mjs +3 -0
- package/src/form/field/ZipCode.mjs +2 -0
- package/src/form/field/trigger/Base.mjs +3 -0
- package/src/form/field/trigger/Clear.mjs +2 -0
- package/src/form/field/trigger/CopyToClipboard.mjs +2 -0
- package/src/form/field/trigger/Date.mjs +1 -0
- package/src/form/field/trigger/Picker.mjs +1 -0
- package/src/form/field/trigger/Search.mjs +1 -0
- package/src/form/field/trigger/SpinDown.mjs +2 -0
- package/src/form/field/trigger/SpinUp.mjs +1 -0
- package/src/form/field/trigger/Time.mjs +2 -0
- package/src/functional/_export.mjs +6 -0
- package/src/functional/component/Base.mjs +499 -0
- package/src/functional/defineComponent.mjs +102 -0
- package/src/functional/useConfig.mjs +52 -0
- package/src/functional/useEvent.mjs +43 -0
- package/src/grid/Body.mjs +20 -1
- package/src/grid/Container.mjs +57 -63
- package/src/grid/ScrollManager.mjs +2 -0
- package/src/grid/VerticalScrollbar.mjs +2 -0
- package/src/grid/column/Base.mjs +2 -0
- package/src/grid/column/Component.mjs +1 -1
- package/src/grid/header/Button.mjs +7 -0
- package/src/grid/header/Toolbar.mjs +6 -0
- package/src/grid/plugin/AnimateRows.mjs +2 -0
- package/src/layout/Base.mjs +3 -0
- package/src/layout/Card.mjs +1 -0
- package/src/layout/Cube.mjs +11 -1
- package/src/layout/Fit.mjs +1 -0
- package/src/layout/Flexbox.mjs +7 -0
- package/src/layout/Form.mjs +2 -0
- package/src/layout/Grid.mjs +1 -0
- package/src/layout/HBox.mjs +1 -0
- package/src/layout/VBox.mjs +1 -0
- package/src/list/Base.mjs +13 -0
- package/src/list/Chip.mjs +1 -0
- package/src/list/Circle.mjs +2 -0
- package/src/list/Color.mjs +1 -0
- package/src/list/plugin/Animate.mjs +2 -0
- package/src/main/DeltaUpdates.mjs +1 -0
- package/src/main/DomEvents.mjs +2 -0
- package/src/main/addon/CloneNode.mjs +1 -0
- package/src/main/addon/Cookie.mjs +1 -0
- package/src/main/addon/GoogleMaps.mjs +1 -0
- package/src/main/addon/LocalStorage.mjs +1 -0
- package/src/main/addon/MapboxGL.mjs +1 -0
- package/src/main/addon/Markdown.mjs +1 -0
- package/src/main/addon/Navigator.mjs +1 -0
- package/src/main/addon/Popover.mjs +1 -0
- package/src/main/addon/Stylesheet.mjs +1 -0
- package/src/main/addon/WindowPosition.mjs +1 -0
- package/src/manager/Component.mjs +0 -71
- package/src/manager/VDomUpdate.mjs +235 -0
- package/src/menu/List.mjs +6 -0
- package/src/menu/Model.mjs +1 -0
- package/src/menu/Panel.mjs +3 -0
- package/src/menu/Store.mjs +1 -0
- package/src/mixin/DomEvents.mjs +130 -0
- package/src/mixin/VdomLifecycle.mjs +667 -0
- package/src/plugin/Base.mjs +1 -0
- package/src/plugin/Resizable.mjs +2 -0
- package/src/selection/Model.mjs +15 -18
- package/src/selection/grid/BaseModel.mjs +1 -0
- package/src/sitemap/Component.mjs +1 -0
- package/src/state/Provider.mjs +376 -457
- package/src/state/createHierarchicalDataProxy.mjs +138 -0
- package/src/tab/Container.mjs +6 -0
- package/src/tab/Strip.mjs +1 -0
- package/src/tab/header/Button.mjs +2 -0
- package/src/tab/header/EffectButton.mjs +77 -0
- package/src/tab/header/Toolbar.mjs +1 -0
- package/src/table/Body.mjs +3 -0
- package/src/table/Container.mjs +10 -0
- package/src/table/header/Button.mjs +8 -0
- package/src/table/header/Toolbar.mjs +5 -0
- package/src/table/plugin/CellEditing.mjs +1 -0
- package/src/toolbar/Base.mjs +4 -0
- package/src/toolbar/Breadcrumb.mjs +3 -0
- package/src/toolbar/Paging.mjs +5 -0
- package/src/tooltip/Base.mjs +2 -0
- package/src/tree/List.mjs +3 -0
- package/src/util/HashHistory.mjs +1 -0
- package/src/util/KeyNavigation.mjs +2 -0
- package/src/util/Matrix.mjs +1 -0
- package/src/util/VDom.mjs +7 -1
- package/src/util/VNode.mjs +7 -1
- package/src/util/vdom/TreeBuilder.mjs +129 -0
- package/src/vdom/Helper.mjs +44 -33
- package/src/vdom/VNode.mjs +5 -7
- package/src/worker/App.mjs +1 -5
- package/src/worker/Base.mjs +2 -0
- package/src/worker/Manager.mjs +2 -0
- package/src/worker/ServiceBase.mjs +6 -1
- package/test/siesta/siesta.js +36 -1
- package/test/siesta/tests/CollectionBase.mjs +10 -10
- package/test/siesta/tests/VdomCalendar.mjs +13 -9
- package/test/siesta/tests/VdomHelper.mjs +22 -59
- package/test/siesta/tests/config/AfterSetConfig.mjs +100 -0
- package/test/siesta/tests/{ReactiveConfigs.mjs → config/Basic.mjs} +58 -21
- package/test/siesta/tests/config/CircularDependencies.mjs +166 -0
- package/test/siesta/tests/config/CustomFunctions.mjs +69 -0
- package/test/siesta/tests/config/Hierarchy.mjs +94 -0
- package/test/siesta/tests/config/MemoryLeak.mjs +92 -0
- package/test/siesta/tests/config/MultiLevelHierarchy.mjs +85 -0
- package/test/siesta/tests/core/Effect.mjs +127 -0
- package/test/siesta/tests/core/EffectBatching.mjs +310 -0
- package/test/siesta/tests/neo/MixinStaticConfig.mjs +138 -0
- package/test/siesta/tests/state/Provider.mjs +537 -0
- package/test/siesta/tests/state/ProviderNestedDataConfigs.mjs +255 -0
- package/test/siesta/tests/state/createHierarchicalDataProxy.mjs +204 -0
- package/test/siesta/tests/vdom/VdomAsymmetricUpdates.mjs +366 -0
- package/test/siesta/tests/vdom/VdomRealWorldUpdates.mjs +249 -0
- package/learn/javascript/NewNode.md +0 -31
@@ -0,0 +1,255 @@
|
|
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
|
+
|
7
|
+
// IMPORTANT: This test file uses real components and expects them to render.
|
8
|
+
// We need to enable unitTestMode for isolation, but also allow VDOM updates.
|
9
|
+
Neo.config.unitTestMode = true;
|
10
|
+
Neo.config.allowVdomUpdatesInTests = true;
|
11
|
+
|
12
|
+
class MockComponent extends Component {
|
13
|
+
static config = {
|
14
|
+
className: 'Mock.Component',
|
15
|
+
appName: 'test-app',
|
16
|
+
user_: null,
|
17
|
+
userName_: null
|
18
|
+
}
|
19
|
+
}
|
20
|
+
Neo.setupClass(MockComponent);
|
21
|
+
|
22
|
+
// Helper function to convert a proxy to a plain object for deep comparison
|
23
|
+
function proxyToObject(proxy) {
|
24
|
+
if (proxy === null || typeof proxy !== 'object') {
|
25
|
+
return proxy;
|
26
|
+
}
|
27
|
+
// A simple way to deep-clone and remove proxy
|
28
|
+
return JSON.parse(JSON.stringify(proxy));
|
29
|
+
}
|
30
|
+
|
31
|
+
StartTest(t => {
|
32
|
+
t.it('State Provider should trigger parent effects when a leaf node changes (bubbling)', t => {
|
33
|
+
let effectRunCount = 0;
|
34
|
+
|
35
|
+
const component = Neo.create(MockComponent, {
|
36
|
+
stateProvider: {
|
37
|
+
data: {
|
38
|
+
user: {
|
39
|
+
name: 'John',
|
40
|
+
age: 30
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
});
|
45
|
+
|
46
|
+
const provider = component.getStateProvider();
|
47
|
+
|
48
|
+
// This binding depends on the 'user' object itself.
|
49
|
+
provider.createBinding(component.id, 'user', data => {
|
50
|
+
effectRunCount++;
|
51
|
+
return data.user;
|
52
|
+
});
|
53
|
+
|
54
|
+
t.is(effectRunCount, 1, 'Effect should run once initially');
|
55
|
+
t.isDeeply(proxyToObject(component.user), { name: 'John', age: 30 }, 'Initial user object is correct');
|
56
|
+
|
57
|
+
// Change a leaf property. This should trigger the effect for the parent 'user' object.
|
58
|
+
provider.setData('user.age', 31);
|
59
|
+
|
60
|
+
t.is(effectRunCount, 2, 'Effect should re-run after changing a leaf property (user.age)');
|
61
|
+
t.isDeeply(proxyToObject(component.user), { name: 'John', age: 31 }, 'User object is updated correctly in the component');
|
62
|
+
|
63
|
+
// Change another leaf property
|
64
|
+
provider.setData('user.name', 'Jane');
|
65
|
+
|
66
|
+
t.is(effectRunCount, 3, 'Effect should re-run after changing another leaf property (user.name)');
|
67
|
+
t.isDeeply(proxyToObject(component.user), { name: 'Jane', age: 31 }, 'User object is updated again');
|
68
|
+
|
69
|
+
component.destroy();
|
70
|
+
});
|
71
|
+
|
72
|
+
t.it('Formulas should react to leaf node changes via bubbling', t => {
|
73
|
+
let effectRunCount = 0;
|
74
|
+
|
75
|
+
const component = Neo.create(MockComponent, {
|
76
|
+
stateProvider: {
|
77
|
+
data: {
|
78
|
+
user: {
|
79
|
+
firstName: 'John',
|
80
|
+
lastName: 'Doe'
|
81
|
+
}
|
82
|
+
},
|
83
|
+
formulas: {
|
84
|
+
// This formula depends on the 'user' object.
|
85
|
+
fullName: data => {
|
86
|
+
effectRunCount++;
|
87
|
+
return `${data.user.firstName} ${data.user.lastName}`;
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
});
|
92
|
+
|
93
|
+
const provider = component.getStateProvider();
|
94
|
+
|
95
|
+
// The formula runs once on initialization.
|
96
|
+
t.is(effectRunCount, 1, 'Formula should run once initially');
|
97
|
+
t.is(provider.getData('fullName'), 'John Doe', 'Initial formula calculation is correct');
|
98
|
+
|
99
|
+
// Change a leaf property. This should trigger the formula that depends on the parent 'user' object.
|
100
|
+
provider.setData('user.firstName', 'Jane');
|
101
|
+
|
102
|
+
t.is(effectRunCount, 2, 'Formula should re-run after changing a leaf property');
|
103
|
+
t.is(provider.getData('fullName'), 'Jane Doe', 'Formula is correctly re-calculated');
|
104
|
+
|
105
|
+
component.destroy();
|
106
|
+
});
|
107
|
+
|
108
|
+
t.it('State Provider should handle deeply nested data changes', t => {
|
109
|
+
let effectRunCountL1 = 0,
|
110
|
+
effectRunCountL2 = 0,
|
111
|
+
effectRunCountL3 = 0;
|
112
|
+
|
113
|
+
const component = Neo.create(MockComponent, {
|
114
|
+
stateProvider: {
|
115
|
+
data: {
|
116
|
+
level1: {
|
117
|
+
level2: {
|
118
|
+
level3: {
|
119
|
+
value: 10
|
120
|
+
}
|
121
|
+
}
|
122
|
+
}
|
123
|
+
}
|
124
|
+
}
|
125
|
+
});
|
126
|
+
|
127
|
+
const provider = component.getStateProvider();
|
128
|
+
|
129
|
+
// Bindings to each level of the nested structure
|
130
|
+
provider.createBinding(component.id, 'level1', data => {
|
131
|
+
effectRunCountL1++;
|
132
|
+
return data.level1;
|
133
|
+
});
|
134
|
+
provider.createBinding(component.id, 'level2', data => {
|
135
|
+
effectRunCountL2++;
|
136
|
+
return data.level1.level2;
|
137
|
+
});
|
138
|
+
provider.createBinding(component.id, 'level3', data => {
|
139
|
+
effectRunCountL3++;
|
140
|
+
return data.level1.level2.level3;
|
141
|
+
});
|
142
|
+
|
143
|
+
t.is(effectRunCountL1, 1, 'L1 Effect ran once initially');
|
144
|
+
t.is(effectRunCountL2, 1, 'L2 Effect ran once initially');
|
145
|
+
t.is(effectRunCountL3, 1, 'L3 Effect ran once initially');
|
146
|
+
|
147
|
+
// Change the deepest leaf node
|
148
|
+
provider.setData('level1.level2.level3.value', 20);
|
149
|
+
|
150
|
+
t.is(effectRunCountL1, 2, 'L1 Effect re-ran due to bubbling');
|
151
|
+
t.is(effectRunCountL2, 2, 'L2 Effect re-ran due to bubbling');
|
152
|
+
t.is(effectRunCountL3, 2, 'L3 Effect re-ran due to bubbling');
|
153
|
+
|
154
|
+
const finalState = proxyToObject(component.level1);
|
155
|
+
t.is(finalState.level2.level3.value, 20, 'Deeply nested value is updated correctly');
|
156
|
+
|
157
|
+
component.destroy();
|
158
|
+
});
|
159
|
+
|
160
|
+
t.it('Formulas should react to deeply nested data changes', t => {
|
161
|
+
let formulaRunCount = 0;
|
162
|
+
|
163
|
+
const component = Neo.create(MockComponent, {
|
164
|
+
stateProvider: {
|
165
|
+
data: {
|
166
|
+
config: {
|
167
|
+
settings: {
|
168
|
+
a: 1,
|
169
|
+
b: 2
|
170
|
+
}
|
171
|
+
},
|
172
|
+
multiplier: 10
|
173
|
+
},
|
174
|
+
formulas: {
|
175
|
+
// This formula depends on multiple deeply nested properties
|
176
|
+
calculated: data => {
|
177
|
+
formulaRunCount++;
|
178
|
+
return (data.config.settings.a + data.config.settings.b) * data.multiplier;
|
179
|
+
}
|
180
|
+
}
|
181
|
+
}
|
182
|
+
});
|
183
|
+
|
184
|
+
const provider = component.getStateProvider();
|
185
|
+
|
186
|
+
t.is(formulaRunCount, 1, 'Formula ran once initially');
|
187
|
+
t.is(provider.getData('calculated'), 30, 'Initial formula calculation is correct');
|
188
|
+
|
189
|
+
// Change a deeply nested property
|
190
|
+
provider.setData('config.settings.a', 5);
|
191
|
+
|
192
|
+
t.is(formulaRunCount, 2, 'Formula re-ran after deep leaf change');
|
193
|
+
t.is(provider.getData('calculated'), 70, 'Formula re-calculated correctly');
|
194
|
+
|
195
|
+
// Change another property at a different level
|
196
|
+
provider.setData('multiplier', 2);
|
197
|
+
|
198
|
+
t.is(formulaRunCount, 3, 'Formula re-ran after sibling property change');
|
199
|
+
t.is(provider.getData('calculated'), 14, 'Formula re-calculated correctly again');
|
200
|
+
|
201
|
+
component.destroy();
|
202
|
+
});
|
203
|
+
|
204
|
+
t.it('Hierarchical providers should bubble reactivity from parent to child', t => {
|
205
|
+
let childFormulaRunCount = 0;
|
206
|
+
|
207
|
+
const parentComponent = Neo.create(MockComponent, {
|
208
|
+
stateProvider: {
|
209
|
+
data: {
|
210
|
+
user: {
|
211
|
+
name: 'Parent',
|
212
|
+
role: 'admin'
|
213
|
+
}
|
214
|
+
}
|
215
|
+
}
|
216
|
+
});
|
217
|
+
|
218
|
+
const childComponent = Neo.create(MockComponent, {
|
219
|
+
parentComponent, // Establishes the hierarchy
|
220
|
+
stateProvider: {
|
221
|
+
data: {
|
222
|
+
prefix: 'User:'
|
223
|
+
},
|
224
|
+
formulas: {
|
225
|
+
// This formula depends on the parent's 'user' object and the child's 'prefix'
|
226
|
+
display: data => {
|
227
|
+
childFormulaRunCount++;
|
228
|
+
return `${data.prefix} ${data.user.name} (${data.user.role})`;
|
229
|
+
}
|
230
|
+
}
|
231
|
+
}
|
232
|
+
});
|
233
|
+
|
234
|
+
const parentProvider = parentComponent.getStateProvider();
|
235
|
+
const childProvider = childComponent.getStateProvider();
|
236
|
+
|
237
|
+
t.is(childFormulaRunCount, 1, 'Child formula ran once initially');
|
238
|
+
t.is(childProvider.getData('display'), 'User: Parent (admin)', 'Initial hierarchical formula calculation is correct');
|
239
|
+
|
240
|
+
// Modify a leaf node in the PARENT provider
|
241
|
+
parentProvider.setData('user.role', 'editor');
|
242
|
+
|
243
|
+
t.is(childFormulaRunCount, 2, 'Child formula re-ran after parent data change');
|
244
|
+
t.is(childProvider.getData('display'), 'User: Parent (editor)', 'Hierarchical formula re-calculated correctly');
|
245
|
+
|
246
|
+
// Modify a leaf node in the CHILD provider to ensure it also works
|
247
|
+
childProvider.setData('prefix', 'Account:');
|
248
|
+
|
249
|
+
t.is(childFormulaRunCount, 3, 'Child formula re-ran after child data change');
|
250
|
+
t.is(childProvider.getData('display'), 'Account: Parent (editor)', 'Formula updates correctly from own data');
|
251
|
+
|
252
|
+
parentComponent.destroy();
|
253
|
+
childComponent.destroy();
|
254
|
+
});
|
255
|
+
});
|
@@ -0,0 +1,204 @@
|
|
1
|
+
import Neo from '../../../../src/Neo.mjs';
|
2
|
+
import * as core from '../../../../src/core/_export.mjs';
|
3
|
+
import {createHierarchicalDataProxy} from '../../../../src/state/createHierarchicalDataProxy.mjs';
|
4
|
+
import Effect from '../../../../src/core/Effect.mjs';
|
5
|
+
import EffectManager from '../../../../src/core/EffectManager.mjs';
|
6
|
+
import Config from '../../../../src/core/Config.mjs';
|
7
|
+
import Base from '../../../../src/core/Base.mjs';
|
8
|
+
|
9
|
+
// Mock StateProvider for testing purposes
|
10
|
+
class MockStateProvider extends Base {
|
11
|
+
static config = {
|
12
|
+
className: 'Mock.State.Provider',
|
13
|
+
data_: null
|
14
|
+
}
|
15
|
+
|
16
|
+
#dataConfigs = {};
|
17
|
+
|
18
|
+
afterSetData(value, oldValue) {
|
19
|
+
if (value) {
|
20
|
+
this.processDataObject(value);
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
processDataObject(obj, path = '') {
|
25
|
+
Object.entries(obj).forEach(([key, value]) => {
|
26
|
+
const fullPath = path ? `${path}.${key}` : key;
|
27
|
+
if (Neo.typeOf(value) === 'Object') {
|
28
|
+
this.processDataObject(value, fullPath);
|
29
|
+
} else {
|
30
|
+
const clonedValue = Neo.isObject(value) ? Neo.clone(value, true) : value; // Deep clone objects
|
31
|
+
this.#dataConfigs[fullPath] = new Config(clonedValue);
|
32
|
+
}
|
33
|
+
});
|
34
|
+
}
|
35
|
+
|
36
|
+
getDataConfig(path) {
|
37
|
+
return this.#dataConfigs[path] || null;
|
38
|
+
}
|
39
|
+
|
40
|
+
getOwnerOfDataProperty(path) {
|
41
|
+
if (this.#dataConfigs[path]) {
|
42
|
+
return {owner: this, propertyName: path};
|
43
|
+
}
|
44
|
+
const parent = this.getParent();
|
45
|
+
if (parent) {
|
46
|
+
return parent.getOwnerOfDataProperty(path);
|
47
|
+
}
|
48
|
+
return null;
|
49
|
+
}
|
50
|
+
|
51
|
+
hasNestedDataStartingWith(path) {
|
52
|
+
const pathWithDot = `${path}.`;
|
53
|
+
if (Object.keys(this.#dataConfigs).some(key => key.startsWith(pathWithDot))) {
|
54
|
+
return true;
|
55
|
+
}
|
56
|
+
const parent = this.getParent();
|
57
|
+
return parent ? parent.hasNestedDataStartingWith(path) : false;
|
58
|
+
}
|
59
|
+
|
60
|
+
getParent() {
|
61
|
+
// For testing, we'll manually set a parent
|
62
|
+
return this._parent || null;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
Neo.setupClass(MockStateProvider);
|
66
|
+
|
67
|
+
StartTest(t => {
|
68
|
+
t.it('should resolve data from a single provider', t => {
|
69
|
+
const provider = Neo.create(MockStateProvider, {data: {name: 'Neo', version: 10}});
|
70
|
+
let effectRunCount = 0;
|
71
|
+
|
72
|
+
const effect = new Effect(() => {
|
73
|
+
effectRunCount++;
|
74
|
+
const proxy = createHierarchicalDataProxy(provider);
|
75
|
+
if (effectRunCount === 1) {
|
76
|
+
t.is(proxy.name, 'Neo', 'Should get name from proxy (initial)');
|
77
|
+
t.is(proxy.version, 10, 'Should get version from proxy (initial)');
|
78
|
+
} else if (effectRunCount === 2) {
|
79
|
+
t.is(proxy.name, 'Neo.mjs', 'Should get name from proxy (updated)');
|
80
|
+
t.is(proxy.version, 10, 'Should get version from proxy (unchanged)');
|
81
|
+
}
|
82
|
+
});
|
83
|
+
|
84
|
+
t.is(effectRunCount, 1, 'Effect should run once initially');
|
85
|
+
t.is(effect.dependencies.size, 2, 'Effect should track 2 dependencies');
|
86
|
+
|
87
|
+
provider.getDataConfig('name').set('Neo.mjs');
|
88
|
+
t.is(effectRunCount, 2, 'Effect should re-run when dependency changes');
|
89
|
+
|
90
|
+
effect.destroy();
|
91
|
+
provider.destroy();
|
92
|
+
});
|
93
|
+
|
94
|
+
t.it('should resolve nested data from a single provider', t => {
|
95
|
+
const provider = Neo.create(MockStateProvider, {data: {user: {firstName: 'John', lastName: 'Doe'}}});
|
96
|
+
let effectRunCount = 0;
|
97
|
+
|
98
|
+
const effect = new Effect(() => {
|
99
|
+
effectRunCount++;
|
100
|
+
const proxy = createHierarchicalDataProxy(provider);
|
101
|
+
if (effectRunCount === 1) {
|
102
|
+
t.is(proxy.user.firstName, 'John', 'Should get nested firstName (initial)');
|
103
|
+
t.is(proxy.user.lastName, 'Doe', 'Should get nested lastName (initial)');
|
104
|
+
} else if (effectRunCount === 2) {
|
105
|
+
t.is(proxy.user.firstName, 'Jane', 'Should get nested firstName (updated)');
|
106
|
+
t.is(proxy.user.lastName, 'Doe', 'Should get nested lastName (unchanged)');
|
107
|
+
}
|
108
|
+
});
|
109
|
+
|
110
|
+
t.is(effectRunCount, 1, 'Effect should run once initially');
|
111
|
+
t.is(effect.dependencies.size, 2, 'Effect should track 2 nested dependencies');
|
112
|
+
|
113
|
+
provider.getDataConfig('user.firstName').set('Jane');
|
114
|
+
t.is(effectRunCount, 2, 'Effect should re-run when nested dependency changes');
|
115
|
+
|
116
|
+
effect.destroy();
|
117
|
+
provider.destroy();
|
118
|
+
});
|
119
|
+
|
120
|
+
t.it('should resolve data from parent providers', t => {
|
121
|
+
const parentProvider = Neo.create(MockStateProvider, {data: {appTitle: 'My App', user: {firstName: 'Parent'}}});
|
122
|
+
const childProvider = Neo.create(MockStateProvider, {data: {user: {lastName: 'Child'}}});
|
123
|
+
childProvider._parent = parentProvider; // Manually set parent
|
124
|
+
|
125
|
+
let effectRunCount = 0;
|
126
|
+
|
127
|
+
const effect = new Effect(() => {
|
128
|
+
effectRunCount++;
|
129
|
+
const proxy = createHierarchicalDataProxy(childProvider);
|
130
|
+
if (effectRunCount === 1) {
|
131
|
+
t.is(proxy.appTitle, 'My App', 'Should get appTitle from parent (initial)');
|
132
|
+
t.is(proxy.user.firstName, 'Parent', 'Should get firstName from parent (initial)');
|
133
|
+
t.is(proxy.user.lastName, 'Child', 'Should get lastName from child (initial)');
|
134
|
+
} else if (effectRunCount === 2) {
|
135
|
+
t.is(proxy.appTitle, 'New App Title', 'Should get appTitle from parent (updated)');
|
136
|
+
t.is(proxy.user.firstName, 'Parent', 'Should get firstName from parent (unchanged)');
|
137
|
+
t.is(proxy.user.lastName, 'Child', 'Should get lastName from child (unchanged)');
|
138
|
+
} else if (effectRunCount === 3) {
|
139
|
+
t.is(proxy.appTitle, 'New App Title', 'Should get appTitle from parent (unchanged)');
|
140
|
+
t.is(proxy.user.firstName, 'Parent', 'Should get firstName from parent (unchanged)');
|
141
|
+
t.is(proxy.user.lastName, 'New Child', 'Should get lastName from child (updated)');
|
142
|
+
} else if (effectRunCount === 4) {
|
143
|
+
t.is(proxy.appTitle, 'New App Title', 'Should get appTitle from parent (unchanged)');
|
144
|
+
t.is(proxy.user.firstName, 'New Parent', 'Should get firstName from parent (updated)');
|
145
|
+
t.is(proxy.user.lastName, 'New Child', 'Should get lastName from child (unchanged)');
|
146
|
+
}
|
147
|
+
});
|
148
|
+
|
149
|
+
t.is(effectRunCount, 1, 'Effect should run once initially');
|
150
|
+
t.is(effect.dependencies.size, 3, 'Effect should track 3 dependencies across hierarchy');
|
151
|
+
|
152
|
+
parentProvider.getDataConfig('appTitle').set('New App Title');
|
153
|
+
t.is(effectRunCount, 2, 'Effect should re-run when parent dependency changes');
|
154
|
+
|
155
|
+
childProvider.getDataConfig('user.lastName').set('New Child');
|
156
|
+
t.is(effectRunCount, 3, 'Effect should re-run when child dependency changes');
|
157
|
+
|
158
|
+
parentProvider.getDataConfig('user.firstName').set('New Parent');
|
159
|
+
t.is(effectRunCount, 4, 'Effect should re-run when parent nested dependency changes');
|
160
|
+
|
161
|
+
effect.destroy();
|
162
|
+
parentProvider.destroy();
|
163
|
+
childProvider.destroy();
|
164
|
+
});
|
165
|
+
|
166
|
+
t.it('should handle properties that are not data or nested paths', t => {
|
167
|
+
const provider = Neo.create(MockStateProvider, {data: {foo: 'bar'}});
|
168
|
+
let effectRunCount = 0;
|
169
|
+
|
170
|
+
const effect = new Effect(() => {
|
171
|
+
effectRunCount++;
|
172
|
+
const proxy = createHierarchicalDataProxy(provider);
|
173
|
+
t.is(proxy.nonExistent, null, 'Should return null for non-existent property');
|
174
|
+
t.is(proxy.foo, 'bar', 'Should still get existing data');
|
175
|
+
});
|
176
|
+
|
177
|
+
t.is(effectRunCount, 1, 'Effect should run once initially');
|
178
|
+
t.is(effect.dependencies.size, 1, 'Effect should only track existing data dependencies');
|
179
|
+
|
180
|
+
effect.destroy();
|
181
|
+
provider.destroy();
|
182
|
+
});
|
183
|
+
|
184
|
+
t.it('should not track dependencies when no effect is active', t => {
|
185
|
+
const provider = Neo.create(MockStateProvider, {data: {test: 123}});
|
186
|
+
const proxy = createHierarchicalDataProxy(provider);
|
187
|
+
|
188
|
+
// No active effect
|
189
|
+
t.is(EffectManager.getActiveEffect(), null, 'No active effect');
|
190
|
+
|
191
|
+
// Access property without active effect
|
192
|
+
const value = proxy.test;
|
193
|
+
t.is(value, 123, 'Should get value correctly');
|
194
|
+
|
195
|
+
// Verify no dependencies were added to a non-existent effect
|
196
|
+
const mockEffect = { addDependency: t.fail }; // If this is called, test fails
|
197
|
+
EffectManager.push(mockEffect); // Temporarily push a mock effect
|
198
|
+
EffectManager.pop(); // Immediately pop it
|
199
|
+
|
200
|
+
t.pass('No error when accessing proxy without active effect');
|
201
|
+
|
202
|
+
provider.destroy();
|
203
|
+
});
|
204
|
+
});
|