neo.mjs 10.0.0-beta.5 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/RELEASE_NOTES/v10.0.0-beta.1.md +20 -0
- package/.github/RELEASE_NOTES/v10.0.0-beta.2.md +73 -0
- package/.github/RELEASE_NOTES/v10.0.0-beta.3.md +39 -0
- 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/RELEASE_NOTES/v10.0.0.md +52 -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 +11 -3
- 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 +29 -19
- 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/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/button/base/MainContainer.mjs +207 -0
- package/examples/functional/button/base/app.mjs +6 -0
- package/examples/functional/button/base/index.html +11 -0
- package/examples/functional/button/base/neo-config.json +6 -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/blog/v10-deep-dive-functional-components.md +293 -0
- package/learn/blog/v10-deep-dive-reactivity.md +522 -0
- package/learn/blog/v10-deep-dive-state-provider.md +432 -0
- package/learn/blog/v10-deep-dive-vdom-revolution.md +194 -0
- package/learn/blog/v10-post1-love-story.md +383 -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 +130 -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 +10 -8
- 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 +28 -2
- 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 -64
- package/learn/tutorials/Earthquakes.md +2 -0
- package/learn/tutorials/RSP.md +3 -1
- package/learn/tutorials/TodoList.md +103 -7
- package/package.json +8 -6
- 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 +219 -166
- package/src/Xhr.mjs +1 -0
- package/src/button/Base.mjs +13 -0
- package/src/button/Effect.mjs +16 -2
- 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 +7 -10
- package/src/collection/Filter.mjs +6 -0
- package/src/collection/Sorter.mjs +3 -0
- package/src/component/Abstract.mjs +412 -0
- package/src/component/Base.mjs +48 -1077
- 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 +7 -3
- 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 +86 -33
- package/src/core/Compare.mjs +4 -7
- package/src/core/Config.mjs +65 -52
- package/src/core/Effect.mjs +86 -24
- package/src/core/EffectManager.mjs +117 -8
- package/src/core/IdGenerator.mjs +13 -44
- package/src/core/Observable.mjs +69 -65
- 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 +43 -5
- 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/button/Base.mjs +384 -0
- package/src/functional/component/Base.mjs +405 -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 +50 -60
- 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/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 +18 -4
- 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 +320 -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 +670 -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 +129 -87
- package/src/state/createHierarchicalDataProxy.mjs +39 -25
- 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 +2 -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 +18 -5
- package/src/util/VNode.mjs +7 -1
- package/src/util/vdom/TreeBuilder.mjs +105 -0
- package/src/vdom/Helper.mjs +35 -23
- package/src/vdom/VNode.mjs +4 -6
- package/src/worker/App.mjs +1 -0
- package/src/worker/Base.mjs +2 -0
- package/src/worker/Manager.mjs +2 -0
- package/src/worker/ServiceBase.mjs +6 -1
- package/src/worker/mixin/RemoteMethodAccess.mjs +1 -6
- package/test/siesta/siesta.js +17 -2
- package/test/siesta/tests/VdomCalendar.mjs +19 -15
- package/test/siesta/tests/VdomHelper.mjs +7 -7
- package/test/siesta/tests/classic/Button.mjs +113 -0
- package/test/siesta/tests/core/Effect.mjs +10 -14
- package/test/siesta/tests/core/EffectBatching.mjs +72 -79
- package/test/siesta/tests/functional/Button.mjs +113 -0
- package/test/siesta/tests/state/ProviderNestedDataConfigs.mjs +314 -0
- package/test/siesta/tests/state/createHierarchicalDataProxy.mjs +42 -55
- package/test/siesta/tests/vdom/Advanced.mjs +14 -8
- package/test/siesta/tests/vdom/VdomAsymmetricUpdates.mjs +366 -0
- package/test/siesta/tests/vdom/VdomRealWorldUpdates.mjs +249 -0
- package/test/siesta/tests/vdom/layout/Cube.mjs +11 -7
- package/test/siesta/tests/vdom/table/Container.mjs +9 -5
- package/learn/javascript/NewNode.md +0 -31
- package/src/core/EffectBatchManager.mjs +0 -68
@@ -0,0 +1,113 @@
|
|
1
|
+
import Neo from '../../../../src/Neo.mjs';
|
2
|
+
import * as core from '../../../../src/core/_export.mjs';
|
3
|
+
import Button from '../../../../src/functional/button/Base.mjs';
|
4
|
+
import DomApiVnodeCreator from '../../../../src/vdom/util/DomApiVnodeCreator.mjs';
|
5
|
+
import VdomHelper from '../../../../src/vdom/Helper.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
|
+
// This ensures that the VdomHelper uses the correct renderer for the assertions.
|
12
|
+
Neo.config.useDomApiRenderer = true;
|
13
|
+
|
14
|
+
// Create a mock application context, as the component lifecycle requires it for updates.
|
15
|
+
const appName = 'FunctionalButtonTest';
|
16
|
+
Neo.apps = Neo.apps || {};
|
17
|
+
Neo.apps[appName] = {
|
18
|
+
name : appName,
|
19
|
+
fire : Neo.emptyFn,
|
20
|
+
isMounted: () => true,
|
21
|
+
rendering: false
|
22
|
+
};
|
23
|
+
|
24
|
+
StartTest(t => {
|
25
|
+
let button, vnode;
|
26
|
+
let testRun = 0;
|
27
|
+
|
28
|
+
t.beforeEach(async t => {
|
29
|
+
testRun++;
|
30
|
+
|
31
|
+
button = Neo.create(Button, {
|
32
|
+
appName,
|
33
|
+
id : 'my-button-' + testRun,
|
34
|
+
iconCls: 'fa fa-home',
|
35
|
+
text : 'Click me'
|
36
|
+
});
|
37
|
+
|
38
|
+
({vnode} = await button.render());
|
39
|
+
button.mounted = true; // Manually mount to enable updates in the test env
|
40
|
+
});
|
41
|
+
|
42
|
+
t.afterEach(t => {
|
43
|
+
button?.destroy();
|
44
|
+
button = null;
|
45
|
+
vnode = null;
|
46
|
+
});
|
47
|
+
|
48
|
+
t.it('should create initial vnode correctly', async t => {
|
49
|
+
t.expect(vnode.nodeName).toBe('button');
|
50
|
+
t.expect(vnode.className).toEqual(['neo-button', 'icon-left']);
|
51
|
+
t.expect(vnode.childNodes.length).toBe(2); // icon & text. badge & ripple have removeDom:true
|
52
|
+
|
53
|
+
const iconNode = vnode.childNodes[0];
|
54
|
+
t.expect(iconNode.className).toEqual(['neo-button-glyph', 'fa', 'fa-home']);
|
55
|
+
|
56
|
+
const textNode = vnode.childNodes[1];
|
57
|
+
t.expect(textNode.className).toEqual(['neo-button-text']);
|
58
|
+
t.expect(textNode.textContent).toBe('Click me');
|
59
|
+
});
|
60
|
+
|
61
|
+
t.it('should update vnode and create delta for a single config change', async t => {
|
62
|
+
const textNodeId = vnode.childNodes[1].id;
|
63
|
+
const {deltas} = await button.set({text: 'New Text'});
|
64
|
+
|
65
|
+
t.is(deltas.length, 1, 'Should generate exactly one delta');
|
66
|
+
const delta = deltas[0];
|
67
|
+
|
68
|
+
t.is(delta.id, textNodeId, 'Delta should target the text node');
|
69
|
+
t.is(delta.textContent, 'New Text', 'Delta textContent is correct');
|
70
|
+
});
|
71
|
+
|
72
|
+
t.it('should update vnode and create delta for multiple config changes', async t => {
|
73
|
+
const iconNodeId = vnode.childNodes[0].id;
|
74
|
+
const textNodeId = vnode.childNodes[1].id;
|
75
|
+
|
76
|
+
const {deltas} = await button.set({
|
77
|
+
iconCls: 'fa fa-user',
|
78
|
+
text : 'Submit'
|
79
|
+
});
|
80
|
+
|
81
|
+
t.is(deltas.length, 2, 'Should generate exactly two deltas');
|
82
|
+
|
83
|
+
const iconDelta = deltas.find(d => d.id === iconNodeId);
|
84
|
+
const textDelta = deltas.find(d => d.id === textNodeId);
|
85
|
+
|
86
|
+
t.ok(iconDelta, 'Should have a delta for the icon node');
|
87
|
+
t.isDeeply(iconDelta.cls.remove, ['fa-home'], 'Icon delta should remove old class');
|
88
|
+
t.isDeeply(iconDelta.cls.add, ['fa-user'], 'Icon delta should add new class');
|
89
|
+
|
90
|
+
t.ok(textDelta, 'Should have a delta for the text node');
|
91
|
+
t.is(textDelta.textContent, 'Submit', 'Text delta is correct');
|
92
|
+
});
|
93
|
+
|
94
|
+
t.it('should handle pressed state change', async t => {
|
95
|
+
t.notOk(vnode.className.includes('pressed'), 'Initial vnode should not have "pressed" class');
|
96
|
+
|
97
|
+
let updateData = await button.set({pressed: true});
|
98
|
+
|
99
|
+
t.is(updateData.deltas.length, 1, 'Should generate one delta for pressed: true');
|
100
|
+
let delta = updateData.deltas[0];
|
101
|
+
t.is(delta.id, button.id, 'Delta should target the button component root');
|
102
|
+
t.isDeeply(delta.cls.add, ['pressed'], 'Delta should add "pressed" class');
|
103
|
+
t.ok(updateData.vnode.className.includes('pressed'), 'Vnode should have "pressed" class');
|
104
|
+
|
105
|
+
updateData = await button.set({pressed: false});
|
106
|
+
|
107
|
+
t.is(updateData.deltas.length, 1, 'Should generate one delta for pressed: false');
|
108
|
+
delta = updateData.deltas[0];
|
109
|
+
t.is(delta.id, button.id, 'Delta should target the button component root');
|
110
|
+
t.isDeeply(delta.cls.remove, ['pressed'], 'Delta should remove "pressed" class');
|
111
|
+
t.notOk(updateData.vnode.className.includes('pressed'), 'Vnode should not have "pressed" class');
|
112
|
+
});
|
113
|
+
});
|
@@ -0,0 +1,314 @@
|
|
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('setData with a nested object should deep-merge and bubble reactivity', t => {
|
73
|
+
const component = Neo.create(MockComponent, {
|
74
|
+
stateProvider: {
|
75
|
+
data: {
|
76
|
+
user: {
|
77
|
+
firstname: 'John',
|
78
|
+
lastname : 'Doe'
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
});
|
83
|
+
|
84
|
+
const provider = component.getStateProvider();
|
85
|
+
let effectRunCount = 0;
|
86
|
+
|
87
|
+
provider.createBinding(component.id, 'user', data => {
|
88
|
+
effectRunCount++;
|
89
|
+
return data.user;
|
90
|
+
});
|
91
|
+
|
92
|
+
t.is(effectRunCount, 1, 'Effect ran initially');
|
93
|
+
t.isDeeply(proxyToObject(component.user), { firstname: 'John', lastname: 'Doe' }, 'Initial user object is correct');
|
94
|
+
|
95
|
+
// ACTION: Set data with a nested object. This should MERGE, not replace.
|
96
|
+
provider.setData({
|
97
|
+
user: { firstname: 'Jane' }
|
98
|
+
});
|
99
|
+
|
100
|
+
// ASSERT: The object was merged, and the old 'lastname' property is preserved.
|
101
|
+
t.is(effectRunCount, 2, 'Effect re-ran after setting the branch node');
|
102
|
+
|
103
|
+
const updatedUser = proxyToObject(component.user);
|
104
|
+
t.isDeeply(updatedUser, { firstname: 'Jane', lastname: 'Doe' }, 'User object should be deep-merged');
|
105
|
+
t.is(updatedUser.lastname, 'Doe', 'The "lastname" property should be preserved after merge');
|
106
|
+
|
107
|
+
// For contrast, let's show the path-based "bubbling" behavior which has the same outcome.
|
108
|
+
// First, reset the state.
|
109
|
+
provider.setData({
|
110
|
+
user: {
|
111
|
+
firstname: 'John',
|
112
|
+
lastname: 'Doe'
|
113
|
+
}
|
114
|
+
});
|
115
|
+
t.is(effectRunCount, 3, 'Effect ran after resetting state');
|
116
|
+
|
117
|
+
// ACTION: Set a leaf node using a path string.
|
118
|
+
provider.setData({
|
119
|
+
'user.firstname': 'Robert'
|
120
|
+
});
|
121
|
+
|
122
|
+
// ASSERT: The object was updated via bubbling, preserving the 'lastname' property.
|
123
|
+
t.is(effectRunCount, 4, 'Effect re-ran after setting a leaf node via path');
|
124
|
+
const mergedUser = proxyToObject(component.user);
|
125
|
+
t.isDeeply(mergedUser, { firstname: 'Robert', lastname: 'Doe' }, 'Path-based set should merge/preserve other properties');
|
126
|
+
t.is(mergedUser.lastname, 'Doe', 'The "lastname" property should be preserved');
|
127
|
+
|
128
|
+
component.destroy();
|
129
|
+
});
|
130
|
+
|
131
|
+
t.it('Formulas should react to leaf node changes via bubbling', t => {
|
132
|
+
let effectRunCount = 0;
|
133
|
+
|
134
|
+
const component = Neo.create(MockComponent, {
|
135
|
+
stateProvider: {
|
136
|
+
data: {
|
137
|
+
user: {
|
138
|
+
firstName: 'John',
|
139
|
+
lastName: 'Doe'
|
140
|
+
}
|
141
|
+
},
|
142
|
+
formulas: {
|
143
|
+
// This formula depends on the 'user' object.
|
144
|
+
fullName: data => {
|
145
|
+
effectRunCount++;
|
146
|
+
return `${data.user.firstName} ${data.user.lastName}`;
|
147
|
+
}
|
148
|
+
}
|
149
|
+
}
|
150
|
+
});
|
151
|
+
|
152
|
+
const provider = component.getStateProvider();
|
153
|
+
|
154
|
+
// The formula runs once on initialization.
|
155
|
+
t.is(effectRunCount, 1, 'Formula should run once initially');
|
156
|
+
t.is(provider.getData('fullName'), 'John Doe', 'Initial formula calculation is correct');
|
157
|
+
|
158
|
+
// Change a leaf property. This should trigger the formula that depends on the parent 'user' object.
|
159
|
+
provider.setData('user.firstName', 'Jane');
|
160
|
+
|
161
|
+
t.is(effectRunCount, 2, 'Formula should re-run after changing a leaf property');
|
162
|
+
t.is(provider.getData('fullName'), 'Jane Doe', 'Formula is correctly re-calculated');
|
163
|
+
|
164
|
+
component.destroy();
|
165
|
+
});
|
166
|
+
|
167
|
+
t.it('State Provider should handle deeply nested data changes', t => {
|
168
|
+
let effectRunCountL1 = 0,
|
169
|
+
effectRunCountL2 = 0,
|
170
|
+
effectRunCountL3 = 0;
|
171
|
+
|
172
|
+
const component = Neo.create(MockComponent, {
|
173
|
+
stateProvider: {
|
174
|
+
data: {
|
175
|
+
level1: {
|
176
|
+
level2: {
|
177
|
+
level3: {
|
178
|
+
value: 10
|
179
|
+
}
|
180
|
+
}
|
181
|
+
}
|
182
|
+
}
|
183
|
+
}
|
184
|
+
});
|
185
|
+
|
186
|
+
const provider = component.getStateProvider();
|
187
|
+
|
188
|
+
// Bindings to each level of the nested structure
|
189
|
+
provider.createBinding(component.id, 'level1', data => {
|
190
|
+
effectRunCountL1++;
|
191
|
+
return data.level1;
|
192
|
+
});
|
193
|
+
provider.createBinding(component.id, 'level2', data => {
|
194
|
+
effectRunCountL2++;
|
195
|
+
return data.level1.level2;
|
196
|
+
});
|
197
|
+
provider.createBinding(component.id, 'level3', data => {
|
198
|
+
effectRunCountL3++;
|
199
|
+
return data.level1.level2.level3;
|
200
|
+
});
|
201
|
+
|
202
|
+
t.is(effectRunCountL1, 1, 'L1 Effect ran once initially');
|
203
|
+
t.is(effectRunCountL2, 1, 'L2 Effect ran once initially');
|
204
|
+
t.is(effectRunCountL3, 1, 'L3 Effect ran once initially');
|
205
|
+
|
206
|
+
// Change the deepest leaf node
|
207
|
+
provider.setData('level1.level2.level3.value', 20);
|
208
|
+
|
209
|
+
t.is(effectRunCountL1, 2, 'L1 Effect re-ran due to bubbling');
|
210
|
+
t.is(effectRunCountL2, 2, 'L2 Effect re-ran due to bubbling');
|
211
|
+
t.is(effectRunCountL3, 2, 'L3 Effect re-ran due to bubbling');
|
212
|
+
|
213
|
+
const finalState = proxyToObject(component.level1);
|
214
|
+
t.is(finalState.level2.level3.value, 20, 'Deeply nested value is updated correctly');
|
215
|
+
|
216
|
+
component.destroy();
|
217
|
+
});
|
218
|
+
|
219
|
+
t.it('Formulas should react to deeply nested data changes', t => {
|
220
|
+
let formulaRunCount = 0;
|
221
|
+
|
222
|
+
const component = Neo.create(MockComponent, {
|
223
|
+
stateProvider: {
|
224
|
+
data: {
|
225
|
+
config: {
|
226
|
+
settings: {
|
227
|
+
a: 1,
|
228
|
+
b: 2
|
229
|
+
}
|
230
|
+
},
|
231
|
+
multiplier: 10
|
232
|
+
},
|
233
|
+
formulas: {
|
234
|
+
// This formula depends on multiple deeply nested properties
|
235
|
+
calculated: data => {
|
236
|
+
formulaRunCount++;
|
237
|
+
return (data.config.settings.a + data.config.settings.b) * data.multiplier;
|
238
|
+
}
|
239
|
+
}
|
240
|
+
}
|
241
|
+
});
|
242
|
+
|
243
|
+
const provider = component.getStateProvider();
|
244
|
+
|
245
|
+
t.is(formulaRunCount, 1, 'Formula ran once initially');
|
246
|
+
t.is(provider.getData('calculated'), 30, 'Initial formula calculation is correct');
|
247
|
+
|
248
|
+
// Change a deeply nested property
|
249
|
+
provider.setData('config.settings.a', 5);
|
250
|
+
|
251
|
+
t.is(formulaRunCount, 2, 'Formula re-ran after deep leaf change');
|
252
|
+
t.is(provider.getData('calculated'), 70, 'Formula re-calculated correctly');
|
253
|
+
|
254
|
+
// Change another property at a different level
|
255
|
+
provider.setData('multiplier', 2);
|
256
|
+
|
257
|
+
t.is(formulaRunCount, 3, 'Formula re-ran after sibling property change');
|
258
|
+
t.is(provider.getData('calculated'), 14, 'Formula re-calculated correctly again');
|
259
|
+
|
260
|
+
component.destroy();
|
261
|
+
});
|
262
|
+
|
263
|
+
t.it('Hierarchical providers should bubble reactivity from parent to child', t => {
|
264
|
+
let childFormulaRunCount = 0;
|
265
|
+
|
266
|
+
const parentComponent = Neo.create(MockComponent, {
|
267
|
+
stateProvider: {
|
268
|
+
data: {
|
269
|
+
user: {
|
270
|
+
name: 'Parent',
|
271
|
+
role: 'admin'
|
272
|
+
}
|
273
|
+
}
|
274
|
+
}
|
275
|
+
});
|
276
|
+
|
277
|
+
const childComponent = Neo.create(MockComponent, {
|
278
|
+
parentComponent, // Establishes the hierarchy
|
279
|
+
stateProvider: {
|
280
|
+
data: {
|
281
|
+
prefix: 'User:'
|
282
|
+
},
|
283
|
+
formulas: {
|
284
|
+
// This formula depends on the parent's 'user' object and the child's 'prefix'
|
285
|
+
display: data => {
|
286
|
+
childFormulaRunCount++;
|
287
|
+
return `${data.prefix} ${data.user.name} (${data.user.role})`;
|
288
|
+
}
|
289
|
+
}
|
290
|
+
}
|
291
|
+
});
|
292
|
+
|
293
|
+
const parentProvider = parentComponent.getStateProvider();
|
294
|
+
const childProvider = childComponent.getStateProvider();
|
295
|
+
|
296
|
+
t.is(childFormulaRunCount, 1, 'Child formula ran once initially');
|
297
|
+
t.is(childProvider.getData('display'), 'User: Parent (admin)', 'Initial hierarchical formula calculation is correct');
|
298
|
+
|
299
|
+
// Modify a leaf node in the PARENT provider
|
300
|
+
parentProvider.setData('user.role', 'editor');
|
301
|
+
|
302
|
+
t.is(childFormulaRunCount, 2, 'Child formula re-ran after parent data change');
|
303
|
+
t.is(childProvider.getData('display'), 'User: Parent (editor)', 'Hierarchical formula re-calculated correctly');
|
304
|
+
|
305
|
+
// Modify a leaf node in the CHILD provider to ensure it also works
|
306
|
+
childProvider.setData('prefix', 'Account:');
|
307
|
+
|
308
|
+
t.is(childFormulaRunCount, 3, 'Child formula re-ran after child data change');
|
309
|
+
t.is(childProvider.getData('display'), 'Account: Parent (editor)', 'Formula updates correctly from own data');
|
310
|
+
|
311
|
+
parentComponent.destroy();
|
312
|
+
childComponent.destroy();
|
313
|
+
});
|
314
|
+
});
|
@@ -15,12 +15,7 @@ class MockStateProvider extends Base {
|
|
15
15
|
|
16
16
|
#dataConfigs = {};
|
17
17
|
|
18
|
-
construct(config) {
|
19
|
-
super.construct(config);
|
20
|
-
}
|
21
|
-
|
22
18
|
afterSetData(value, oldValue) {
|
23
|
-
console.log(value);
|
24
19
|
if (value) {
|
25
20
|
this.processDataObject(value);
|
26
21
|
}
|
@@ -74,17 +69,15 @@ StartTest(t => {
|
|
74
69
|
const provider = Neo.create(MockStateProvider, {data: {name: 'Neo', version: 10}});
|
75
70
|
let effectRunCount = 0;
|
76
71
|
|
77
|
-
const effect = new Effect({
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
t.is(proxy.version, 10, 'Should get version from proxy (unchanged)');
|
87
|
-
}
|
72
|
+
const effect = new Effect(() => {
|
73
|
+
effectRunCount++;
|
74
|
+
const proxy = createHierarchicalDataProxy(provider);
|
75
|
+
if (effectRunCount === 1) {
|
76
|
+
t.is(proxy.name, 'Neo', 'Should get name from proxy (initial)');
|
77
|
+
t.is(proxy.version, 10, 'Should get version from proxy (initial)');
|
78
|
+
} else if (effectRunCount === 2) {
|
79
|
+
t.is(proxy.name, 'Neo.mjs', 'Should get name from proxy (updated)');
|
80
|
+
t.is(proxy.version, 10, 'Should get version from proxy (unchanged)');
|
88
81
|
}
|
89
82
|
});
|
90
83
|
|
@@ -102,17 +95,15 @@ StartTest(t => {
|
|
102
95
|
const provider = Neo.create(MockStateProvider, {data: {user: {firstName: 'John', lastName: 'Doe'}}});
|
103
96
|
let effectRunCount = 0;
|
104
97
|
|
105
|
-
const effect = new Effect({
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
t.is(proxy.user.lastName, 'Doe', 'Should get nested lastName (unchanged)');
|
115
|
-
}
|
98
|
+
const effect = new Effect(() => {
|
99
|
+
effectRunCount++;
|
100
|
+
const proxy = createHierarchicalDataProxy(provider);
|
101
|
+
if (effectRunCount === 1) {
|
102
|
+
t.is(proxy.user.firstName, 'John', 'Should get nested firstName (initial)');
|
103
|
+
t.is(proxy.user.lastName, 'Doe', 'Should get nested lastName (initial)');
|
104
|
+
} else if (effectRunCount === 2) {
|
105
|
+
t.is(proxy.user.firstName, 'Jane', 'Should get nested firstName (updated)');
|
106
|
+
t.is(proxy.user.lastName, 'Doe', 'Should get nested lastName (unchanged)');
|
116
107
|
}
|
117
108
|
});
|
118
109
|
|
@@ -133,27 +124,25 @@ StartTest(t => {
|
|
133
124
|
|
134
125
|
let effectRunCount = 0;
|
135
126
|
|
136
|
-
const effect = new Effect({
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
t.is(proxy.user.lastName, 'New Child', 'Should get lastName from child (unchanged)');
|
156
|
-
}
|
127
|
+
const effect = new Effect(() => {
|
128
|
+
effectRunCount++;
|
129
|
+
const proxy = createHierarchicalDataProxy(childProvider);
|
130
|
+
if (effectRunCount === 1) {
|
131
|
+
t.is(proxy.appTitle, 'My App', 'Should get appTitle from parent (initial)');
|
132
|
+
t.is(proxy.user.firstName, 'Parent', 'Should get firstName from parent (initial)');
|
133
|
+
t.is(proxy.user.lastName, 'Child', 'Should get lastName from child (initial)');
|
134
|
+
} else if (effectRunCount === 2) {
|
135
|
+
t.is(proxy.appTitle, 'New App Title', 'Should get appTitle from parent (updated)');
|
136
|
+
t.is(proxy.user.firstName, 'Parent', 'Should get firstName from parent (unchanged)');
|
137
|
+
t.is(proxy.user.lastName, 'Child', 'Should get lastName from child (unchanged)');
|
138
|
+
} else if (effectRunCount === 3) {
|
139
|
+
t.is(proxy.appTitle, 'New App Title', 'Should get appTitle from parent (unchanged)');
|
140
|
+
t.is(proxy.user.firstName, 'Parent', 'Should get firstName from parent (unchanged)');
|
141
|
+
t.is(proxy.user.lastName, 'New Child', 'Should get lastName from child (updated)');
|
142
|
+
} else if (effectRunCount === 4) {
|
143
|
+
t.is(proxy.appTitle, 'New App Title', 'Should get appTitle from parent (unchanged)');
|
144
|
+
t.is(proxy.user.firstName, 'New Parent', 'Should get firstName from parent (updated)');
|
145
|
+
t.is(proxy.user.lastName, 'New Child', 'Should get lastName from child (unchanged)');
|
157
146
|
}
|
158
147
|
});
|
159
148
|
|
@@ -178,13 +167,11 @@ StartTest(t => {
|
|
178
167
|
const provider = Neo.create(MockStateProvider, {data: {foo: 'bar'}});
|
179
168
|
let effectRunCount = 0;
|
180
169
|
|
181
|
-
const effect = new Effect({
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
t.is(proxy.foo, 'bar', 'Should still get existing data');
|
187
|
-
}
|
170
|
+
const effect = new Effect(() => {
|
171
|
+
effectRunCount++;
|
172
|
+
const proxy = createHierarchicalDataProxy(provider);
|
173
|
+
t.is(proxy.nonExistent, null, 'Should return null for non-existent property');
|
174
|
+
t.is(proxy.foo, 'bar', 'Should still get existing data');
|
188
175
|
});
|
189
176
|
|
190
177
|
t.is(effectRunCount, 1, 'Effect should run once initially');
|
@@ -1,6 +1,10 @@
|
|
1
|
-
import Neo
|
2
|
-
import * as core
|
3
|
-
import
|
1
|
+
import Neo from '../../../../src/Neo.mjs';
|
2
|
+
import * as core from '../../../../src/core/_export.mjs';
|
3
|
+
import StringFromVnode from '../../../../src/vdom/util/StringFromVnode.mjs';
|
4
|
+
import VdomHelper from '../../../../src/vdom/Helper.mjs';
|
5
|
+
|
6
|
+
// tests are designed for this rendering mode
|
7
|
+
Neo.config.useDomApiRenderer = false;
|
4
8
|
|
5
9
|
let deltas, output, vdom, vnode;
|
6
10
|
|
@@ -30,7 +34,7 @@ StartTest(t => {
|
|
30
34
|
]}
|
31
35
|
]};
|
32
36
|
|
33
|
-
vnode = VdomHelper.create(vdom);
|
37
|
+
vnode = VdomHelper.create({vdom}).vnode;
|
34
38
|
|
35
39
|
vdom =
|
36
40
|
{id: 'neo-calendar-week', cn: [
|
@@ -136,7 +140,7 @@ StartTest(t => {
|
|
136
140
|
{id: 'neo-column-2', cls: ['foo4'], cn: []}
|
137
141
|
]};
|
138
142
|
|
139
|
-
vnode = VdomHelper.create(vdom);
|
143
|
+
vnode = VdomHelper.create({vdom}).vnode;
|
140
144
|
|
141
145
|
vdom =
|
142
146
|
{id: 'neo-calendar-week', cn: [
|
@@ -246,7 +250,7 @@ StartTest(t => {
|
|
246
250
|
{id: 'neo-component-6'}
|
247
251
|
]};
|
248
252
|
|
249
|
-
vnode = VdomHelper.create(vdom);
|
253
|
+
vnode = VdomHelper.create({vdom}).vnode;
|
250
254
|
|
251
255
|
vdom =
|
252
256
|
{id: 'neo-container-1', cn: [
|
@@ -342,9 +346,11 @@ StartTest(t => {
|
|
342
346
|
]}
|
343
347
|
]};
|
344
348
|
|
345
|
-
vnode = VdomHelper.create(vdom);
|
349
|
+
const result = vnode = VdomHelper.create({vdom});
|
350
|
+
const outerHTML = result.outerHTML;
|
351
|
+
vnode = result.vnode;
|
346
352
|
|
347
|
-
t.is(
|
353
|
+
t.is(outerHTML.includes('static'), false, 'The generated DOM does not include "static"');
|
348
354
|
t.isDeeplyStrict(vnode.static, undefined, 'Top-level VNode did not get the static attribute');
|
349
355
|
t.isDeeplyStrict(vnode.childNodes[0].static, true, 'First VNode childNode got the static attribute');
|
350
356
|
|