neo.mjs 10.0.0-beta.5 → 10.0.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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 +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 +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 -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 +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 +217 -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/Base.mjs +104 -771
- 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 +55 -3
- package/src/core/Compare.mjs +4 -7
- package/src/core/Config.mjs +65 -52
- package/src/core/Effect.mjs +79 -13
- package/src/core/EffectBatchManager.mjs +18 -19
- package/src/core/EffectManager.mjs +25 -3
- 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 +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 +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 +98 -70
- 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 +7 -1
- package/src/util/VNode.mjs +7 -1
- package/src/util/vdom/TreeBuilder.mjs +129 -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/test/siesta/siesta.js +5 -2
- package/test/siesta/tests/VdomCalendar.mjs +13 -9
- package/test/siesta/tests/core/Effect.mjs +10 -14
- package/test/siesta/tests/core/EffectBatching.mjs +25 -37
- package/test/siesta/tests/state/ProviderNestedDataConfigs.mjs +255 -0
- package/test/siesta/tests/state/createHierarchicalDataProxy.mjs +42 -55
- 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
@@ -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');
|
@@ -0,0 +1,366 @@
|
|
1
|
+
import Neo from '../../../../src/Neo.mjs';
|
2
|
+
import * as core from '../../../../src/core/_export.mjs';
|
3
|
+
import ComponentManager from '../../../../src/manager/Component.mjs';
|
4
|
+
import TreeBuilder from '../../../../src/util/vdom/TreeBuilder.mjs';
|
5
|
+
import VDomUpdate from '../../../../src/manager/VDomUpdate.mjs';
|
6
|
+
import VdomLifecycle from '../../../../src/mixin/VdomLifecycle.mjs';
|
7
|
+
import VdomHelper from '../../../../src/vdom/Helper.mjs';
|
8
|
+
import VDomUtil from '../../../../src/util/VDom.mjs';
|
9
|
+
|
10
|
+
// IMPORTANT: Test with the new standard renderer
|
11
|
+
Neo.config.useDomApiRenderer = true;
|
12
|
+
VdomHelper.onNeoConfigChange({useDomApiRenderer: true});
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Creates a mock component object for testing.
|
16
|
+
* @param {string} id
|
17
|
+
* @param {string} parentId
|
18
|
+
* @param {Object} vdom
|
19
|
+
* @returns {Object} A mock component
|
20
|
+
*/
|
21
|
+
const createMockComponent = (id, parentId, vdom) => {
|
22
|
+
const component = {
|
23
|
+
id,
|
24
|
+
parentId,
|
25
|
+
vdom,
|
26
|
+
// Add properties from VdomLifecycle that we need to test
|
27
|
+
isVdomUpdating: false,
|
28
|
+
// By adding the prototype methods to our mock instances, we can test
|
29
|
+
// the lifecycle logic without needing full component instantiation.
|
30
|
+
hasUpdateCollision: VdomLifecycle.prototype.hasUpdateCollision,
|
31
|
+
isParentUpdating : VdomLifecycle.prototype.isParentUpdating,
|
32
|
+
};
|
33
|
+
// Create the initial vnode from the vdom definition.
|
34
|
+
const { vnode } = VdomHelper.create({ vdom });
|
35
|
+
component.vnode = vnode;
|
36
|
+
|
37
|
+
// Register the component BEFORE syncing IDs. This is critical so that
|
38
|
+
// a parent's syncVdomIds call can find this component if it's a child.
|
39
|
+
ComponentManager.register(component);
|
40
|
+
VDomUtil.syncVdomIds(component.vnode, component.vdom);
|
41
|
+
|
42
|
+
return component;
|
43
|
+
};
|
44
|
+
|
45
|
+
StartTest(t => {
|
46
|
+
|
47
|
+
t.beforeEach(() => {
|
48
|
+
// Reset managers to ensure test isolation
|
49
|
+
VDomUpdate.mergedCallbackMap.clear();
|
50
|
+
VDomUpdate.postUpdateQueueMap.clear();
|
51
|
+
ComponentManager.wrapperNodes.clear();
|
52
|
+
ComponentManager.clear();
|
53
|
+
});
|
54
|
+
|
55
|
+
t.it('Should handle asymmetric update with depth 2 using DomApiRenderer', t => {
|
56
|
+
// 1. SETUP
|
57
|
+
// Create a parent and a child. The parent's vdom references the child via componentId.
|
58
|
+
const childVdomInitial = { id: 'child-1', cn: [{ tag: 'span', text: 'Initial' }] };
|
59
|
+
const parentVdom = {
|
60
|
+
id: 'parent-1',
|
61
|
+
cn: [{ componentId: 'child-1' }]
|
62
|
+
};
|
63
|
+
|
64
|
+
// Create components dependency-first (child before parent) to ensure
|
65
|
+
// component references can be resolved during VDOM/VNode processing.
|
66
|
+
// The `createMockComponent` factory now handles registration.
|
67
|
+
let child = createMockComponent('child-1', 'parent-1', childVdomInitial);
|
68
|
+
let parent = createMockComponent('parent-1', 'root', parentVdom);
|
69
|
+
|
70
|
+
// 2. SIMULATE A CHILD-INITIATED UPDATE
|
71
|
+
// The child's internal state changes, and it requests to be part of the parent's next update.
|
72
|
+
VDomUpdate.registerMerged(
|
73
|
+
parent.id,
|
74
|
+
child.id,
|
75
|
+
1, // childUpdateDepth
|
76
|
+
1 // distance
|
77
|
+
);
|
78
|
+
|
79
|
+
// The child's vdom has now changed. We update our mock to reflect this.
|
80
|
+
// By mutating the existing vdom object, we ensure stable IDs are preserved for diffing.
|
81
|
+
child.vdom.cn[0].text = 'Updated';
|
82
|
+
|
83
|
+
// 3. SIMULATE THE PARENT'S UPDATE LIFECYCLE
|
84
|
+
// The parent calculates the required depth for the update.
|
85
|
+
const adjustedDepth = VDomUpdate.getAdjustedUpdateDepth(parent.id);
|
86
|
+
t.is(adjustedDepth, 2, 'Adjusted update depth should be 2 to include direct children');
|
87
|
+
|
88
|
+
// The parent builds an asymmetric VDOM tree. TreeBuilder will find the updated
|
89
|
+
// child.vdom via the ComponentManager.
|
90
|
+
const newAsymmetricVdom = TreeBuilder.getVdomTree(parent.vdom, adjustedDepth);
|
91
|
+
|
92
|
+
// Verify the created tree has the child's *new* vdom
|
93
|
+
t.is(newAsymmetricVdom.cn[0].id, 'child-1', 'The child component VDOM is expanded in the asymmetric tree');
|
94
|
+
t.is(newAsymmetricVdom.cn[0].cn[0].text, 'Updated', 'The expanded VDOM reflects the childs updated state');
|
95
|
+
|
96
|
+
// 4. GENERATE DELTAS
|
97
|
+
// VdomHelper diffs the new, expanded tree against the parent's OLD vnode.
|
98
|
+
// The old vnode must also be expanded to the same depth to ensure a correct diff.
|
99
|
+
const oldAsymmetricVnode = TreeBuilder.getVnodeTree(parent.vnode, adjustedDepth);
|
100
|
+
const { deltas } = VdomHelper.update({
|
101
|
+
vdom : newAsymmetricVdom,
|
102
|
+
vnode: oldAsymmetricVnode
|
103
|
+
});
|
104
|
+
|
105
|
+
// 5. ASSERTIONS
|
106
|
+
// For useDomApiRenderer=true, a text change results in a delta updating
|
107
|
+
// the `textContent` property of the parent element's vnode.
|
108
|
+
t.is(deltas.length, 1, 'Should generate exactly one delta for the text change');
|
109
|
+
const spanVnode = oldAsymmetricVnode.childNodes[0].childNodes[0];
|
110
|
+
const delta = deltas[0];
|
111
|
+
|
112
|
+
t.is(delta.id, spanVnode.id, 'Delta targets the correct span element');
|
113
|
+
t.is(delta.textContent, 'Updated', 'The new text content should be correct');
|
114
|
+
t.is(Object.keys(delta).length, 2, 'Delta has the correct shape (id, textContent)');
|
115
|
+
});
|
116
|
+
|
117
|
+
t.it('Should handle nested asymmetric update (grandchild update)', t => {
|
118
|
+
// 1. SETUP
|
119
|
+
const grandchildVdomInitial = { id: 'grandchild-1', cn: [{ tag: 'span', text: 'Initial' }] };
|
120
|
+
const childVdom = {
|
121
|
+
id: 'child-1',
|
122
|
+
cn: [{ componentId: 'grandchild-1' }]
|
123
|
+
};
|
124
|
+
const parentVdom = {
|
125
|
+
id: 'parent-1',
|
126
|
+
cn: [{ componentId: 'child-1' }]
|
127
|
+
};
|
128
|
+
|
129
|
+
// Create components dependency-first (grandchild -> child -> parent) to ensure
|
130
|
+
// component references can be resolved during VDOM/VNode processing.
|
131
|
+
const grandchild = createMockComponent('grandchild-1', 'child-1', grandchildVdomInitial);
|
132
|
+
createMockComponent('child-1', 'parent-1', childVdom);
|
133
|
+
let parent = createMockComponent('parent-1', 'root', parentVdom);
|
134
|
+
|
135
|
+
// 2. SIMULATE A GRANDCHILD-INITIATED UPDATE
|
136
|
+
// The grandchild's state changes. It is at a distance of 2 from the updating parent.
|
137
|
+
VDomUpdate.registerMerged(
|
138
|
+
parent.id,
|
139
|
+
grandchild.id,
|
140
|
+
1, // grandchild's own updateDepth
|
141
|
+
2 // distance from parent
|
142
|
+
);
|
143
|
+
|
144
|
+
// The grandchild's vdom has now changed.
|
145
|
+
// By mutating the existing vdom object, we ensure stable IDs are preserved for diffing.
|
146
|
+
grandchild.vdom.cn[0].text = 'Updated';
|
147
|
+
|
148
|
+
// 3. SIMULATE THE PARENT'S UPDATE LIFECYCLE
|
149
|
+
// The required depth for the parent should be 3 to expand down to the grandchild.
|
150
|
+
const adjustedDepth = VDomUpdate.getAdjustedUpdateDepth(parent.id);
|
151
|
+
t.is(adjustedDepth, 3, 'Adjusted update depth should be 3 to include grandchild');
|
152
|
+
|
153
|
+
// The parent builds an asymmetric VDOM tree.
|
154
|
+
const newAsymmetricVdom = TreeBuilder.getVdomTree(parent.vdom, adjustedDepth);
|
155
|
+
|
156
|
+
// Verify the created tree has the grandchild's *new* vdom
|
157
|
+
const expandedChild = newAsymmetricVdom.cn[0];
|
158
|
+
const expandedGrandchild = expandedChild.cn[0];
|
159
|
+
t.is(expandedGrandchild.id, 'grandchild-1', 'The grandchild component VDOM is expanded in the asymmetric tree');
|
160
|
+
t.is(expandedGrandchild.cn[0].text, 'Updated', 'The expanded VDOM reflects the grandchilds updated state');
|
161
|
+
|
162
|
+
// 4. GENERATE DELTAS
|
163
|
+
const oldAsymmetricVnode = TreeBuilder.getVnodeTree(parent.vnode, adjustedDepth);
|
164
|
+
const { deltas } = VdomHelper.update({
|
165
|
+
vdom : newAsymmetricVdom,
|
166
|
+
vnode: oldAsymmetricVnode
|
167
|
+
});
|
168
|
+
|
169
|
+
// 5. ASSERTIONS
|
170
|
+
// For useDomApiRenderer=true, a text change results in a delta updating
|
171
|
+
// the `textContent` property of the parent element's vnode.
|
172
|
+
t.is(deltas.length, 1, 'Should generate exactly one delta for the text change');
|
173
|
+
const spanVnode = oldAsymmetricVnode.childNodes[0].childNodes[0].childNodes[0];
|
174
|
+
const delta = deltas[0];
|
175
|
+
|
176
|
+
t.is(delta.id, spanVnode.id, 'Delta targets the correct span element');
|
177
|
+
t.is(delta.textContent, 'Updated', 'The new text content should be correct');
|
178
|
+
t.is(Object.keys(delta).length, 2, 'Delta has the correct shape (id, textContent)');
|
179
|
+
});
|
180
|
+
|
181
|
+
t.it('Should handle structural change in a deeply nested component', t => {
|
182
|
+
// 1. SETUP
|
183
|
+
const grandchildVdomInitial = { id: 'grandchild-1', cn: [{ tag: 'span', text: 'Initial' }] };
|
184
|
+
const childVdom = {
|
185
|
+
id: 'child-1',
|
186
|
+
cn: [{ componentId: 'grandchild-1' }]
|
187
|
+
};
|
188
|
+
const parentVdom = {
|
189
|
+
id: 'parent-1',
|
190
|
+
cn: [{ componentId: 'child-1' }]
|
191
|
+
};
|
192
|
+
|
193
|
+
// Create components dependency-first
|
194
|
+
let grandchild = createMockComponent('grandchild-1', 'child-1', grandchildVdomInitial);
|
195
|
+
createMockComponent('child-1', 'parent-1', childVdom);
|
196
|
+
let parent = createMockComponent('parent-1', 'root', parentVdom);
|
197
|
+
|
198
|
+
// 2. SIMULATE A GRANDCHILD-INITIATED UPDATE
|
199
|
+
VDomUpdate.registerMerged(
|
200
|
+
parent.id,
|
201
|
+
grandchild.id,
|
202
|
+
1, // grandchild's own updateDepth
|
203
|
+
2 // distance from parent
|
204
|
+
);
|
205
|
+
|
206
|
+
// The grandchild's vdom has a structural change.
|
207
|
+
grandchild.vdom.cn.push({ id: 'new-node', tag: 'div', text: 'New Node' });
|
208
|
+
|
209
|
+
// 3. SIMULATE THE PARENT'S UPDATE LIFECYCLE
|
210
|
+
const adjustedDepth = VDomUpdate.getAdjustedUpdateDepth(parent.id);
|
211
|
+
t.is(adjustedDepth, 3, 'Adjusted update depth should be 3 to include grandchild');
|
212
|
+
|
213
|
+
const newAsymmetricVdom = TreeBuilder.getVdomTree(parent.vdom, adjustedDepth);
|
214
|
+
const oldAsymmetricVnode = TreeBuilder.getVnodeTree(parent.vnode, adjustedDepth);
|
215
|
+
|
216
|
+
// Verify the new VDOM structure
|
217
|
+
const expandedGrandchildVdom = newAsymmetricVdom.cn[0].cn[0];
|
218
|
+
t.is(expandedGrandchildVdom.cn.length, 2, 'New VDOM for grandchild has 2 children');
|
219
|
+
t.is(expandedGrandchildVdom.cn[1].id, 'new-node', 'New node is present in the asymmetric VDOM');
|
220
|
+
|
221
|
+
// 4. GENERATE DELTAS
|
222
|
+
const { deltas } = VdomHelper.update({
|
223
|
+
vdom : newAsymmetricVdom,
|
224
|
+
vnode: oldAsymmetricVnode
|
225
|
+
});
|
226
|
+
|
227
|
+
// 5. ASSERTIONS
|
228
|
+
t.is(deltas.length, 1, 'Should generate one delta for the insertion');
|
229
|
+
const delta = deltas[0];
|
230
|
+
const newNodeVdom = expandedGrandchildVdom.cn[1];
|
231
|
+
|
232
|
+
t.is(delta.action, 'insertNode', 'Delta action should be insertNode');
|
233
|
+
t.is(delta.parentId, grandchild.vdom.id, 'Delta parentId should be the grandchild');
|
234
|
+
t.is(delta.index, 1, 'Delta index should be 1');
|
235
|
+
|
236
|
+
// For DomApiRenderer, the vnode is passed directly.
|
237
|
+
t.is(delta.vnode.id, newNodeVdom.id, 'Inserted vnode has the correct ID');
|
238
|
+
t.is(delta.vnode.textContent, 'New Node', 'Inserted vnode has the correct text');
|
239
|
+
});
|
240
|
+
|
241
|
+
t.it('Should handle update collision (isParentUpdating)', t => {
|
242
|
+
// 1. SETUP
|
243
|
+
const childVdom = { id: 'child-1', text: 'child' };
|
244
|
+
const parentVdom = { id: 'parent-1', cn: [{ componentId: 'child-1' }] };
|
245
|
+
|
246
|
+
let child = createMockComponent('child-1', 'parent-1', childVdom);
|
247
|
+
let parent = createMockComponent('parent-1', 'root', parentVdom);
|
248
|
+
|
249
|
+
// 2. SIMULATE A PARENT UPDATE IN PROGRESS
|
250
|
+
// This is the state during a real update, before post-processing.
|
251
|
+
parent.isVdomUpdating = true;
|
252
|
+
VDomUpdate.registerInFlightUpdate(parent.id, 2);
|
253
|
+
|
254
|
+
// 3. SIMULATE A CHILD-INITIATED UPDATE (during the parent's update)
|
255
|
+
let hasCollision = child.isParentUpdating(child.parentId, () => {});
|
256
|
+
|
257
|
+
// 4. ASSERTIONS
|
258
|
+
t.ok(hasCollision, 'isParentUpdating should return true, detecting a collision');
|
259
|
+
const postUpdateQueue = VDomUpdate.postUpdateQueueMap.get(parent.id);
|
260
|
+
t.ok(postUpdateQueue, 'Parent should have a post-update queue');
|
261
|
+
t.is(postUpdateQueue.children.length, 1, 'Post-update queue should have one entry');
|
262
|
+
t.is(postUpdateQueue.children[0].childId, child.id, 'The queued item should be the child component');
|
263
|
+
});
|
264
|
+
|
265
|
+
t.it('Should not detect a collision if updateDepth is insufficient', t => {
|
266
|
+
// 1. SETUP
|
267
|
+
const grandchildVdom = { id: 'grandchild-1', text: 'grandchild' };
|
268
|
+
const childVdom = { id: 'child-1', cn: [{ componentId: 'grandchild-1' }] };
|
269
|
+
const parentVdom = { id: 'parent-1', cn: [{ componentId: 'child-1' }] };
|
270
|
+
|
271
|
+
let grandchild = createMockComponent('grandchild-1', 'child-1', grandchildVdom);
|
272
|
+
createMockComponent('child-1', 'parent-1', childVdom);
|
273
|
+
let parent = createMockComponent('parent-1', 'root', parentVdom);
|
274
|
+
|
275
|
+
// 2. SIMULATE A PARENT UPDATE IN PROGRESS
|
276
|
+
parent.isVdomUpdating = true;
|
277
|
+
VDomUpdate.registerInFlightUpdate(parent.id, 2);
|
278
|
+
|
279
|
+
// 3. SIMULATE A GRANDCHILD-INITIATED UPDATE
|
280
|
+
// The grandchild is at distance 2 from the parent. hasUpdateCollision(2, 2) should be false.
|
281
|
+
let hasCollision = grandchild.isParentUpdating(grandchild.parentId, () => {});
|
282
|
+
|
283
|
+
// 4. ASSERTIONS
|
284
|
+
t.notOk(hasCollision, 'isParentUpdating should return false, no collision detected');
|
285
|
+
const postUpdateQueue = VDomUpdate.postUpdateQueueMap.get(parent.id);
|
286
|
+
t.notOk(postUpdateQueue, 'Parent should not have a post-update queue');
|
287
|
+
});
|
288
|
+
|
289
|
+
t.it('Should handle merged updates from multiple non-contiguous children', t => {
|
290
|
+
// 1. SETUP
|
291
|
+
// Parent -> Child1 -> Grandchild1
|
292
|
+
// Parent -> Child2
|
293
|
+
// Parent -> Child3
|
294
|
+
const grandchildVdom = { id: 'grandchild-1', cn: [{ tag: 'span', text: 'Initial GC' }] };
|
295
|
+
const child1Vdom = { id: 'child-1', cn: [{ componentId: 'grandchild-1' }] };
|
296
|
+
const child2Vdom = { id: 'child-2', text: 'Initial C2' };
|
297
|
+
const child3Vdom = { id: 'child-3', text: 'Initial C3' };
|
298
|
+
const parentVdom = {
|
299
|
+
id: 'parent-1',
|
300
|
+
cn: [
|
301
|
+
{ componentId: 'child-1' },
|
302
|
+
{ componentId: 'child-2' },
|
303
|
+
{ componentId: 'child-3' }
|
304
|
+
]
|
305
|
+
};
|
306
|
+
|
307
|
+
// Create components
|
308
|
+
let grandchild1 = createMockComponent('grandchild-1', 'child-1', grandchildVdom);
|
309
|
+
createMockComponent('child-1', 'parent-1', child1Vdom);
|
310
|
+
createMockComponent('child-2', 'parent-1', child2Vdom);
|
311
|
+
let child3 = createMockComponent('child-3', 'parent-1', child3Vdom);
|
312
|
+
let parent = createMockComponent('parent-1', 'root', parentVdom);
|
313
|
+
|
314
|
+
// 2. SIMULATE MULTIPLE MERGED UPDATES
|
315
|
+
// Grandchild1 (at distance 2) requests an update
|
316
|
+
VDomUpdate.registerMerged(parent.id, grandchild1.id, 1, 2);
|
317
|
+
// Child3 (at distance 1) requests an update
|
318
|
+
VDomUpdate.registerMerged(parent.id, child3.id, 1, 1);
|
319
|
+
|
320
|
+
// Make the changes to the source vdoms
|
321
|
+
grandchild1.vdom.cn[0].text = 'Updated GC';
|
322
|
+
child3.vdom.text = 'Updated C3';
|
323
|
+
|
324
|
+
// 3. SIMULATE THE PARENT'S UPDATE LIFECYCLE
|
325
|
+
const adjustedDepth = VDomUpdate.getAdjustedUpdateDepth(parent.id);
|
326
|
+
// Depth for GC1 is 2(dist) + 1(depth) = 3. Depth for C3 is 1(dist) + 1(depth) = 2.
|
327
|
+
// The max should be taken.
|
328
|
+
t.is(adjustedDepth, 3, 'Adjusted update depth should be 3, the max required by children');
|
329
|
+
|
330
|
+
const newAsymmetricVdom = TreeBuilder.getVdomTree(parent.vdom, adjustedDepth);
|
331
|
+
const oldAsymmetricVnode = TreeBuilder.getVnodeTree(parent.vnode, adjustedDepth);
|
332
|
+
|
333
|
+
// Verify the new VDOM structure is correctly expanded.
|
334
|
+
// TreeBuilder expands based on depth, so non-updating siblings within the depth are also expanded.
|
335
|
+
const [expChild1, expChild2, expChild3] = newAsymmetricVdom.cn;
|
336
|
+
// Child1 should be expanded to reveal Grandchild1
|
337
|
+
t.is(expChild1.cn[0].id, 'grandchild-1', 'Child1 is expanded');
|
338
|
+
t.is(expChild1.cn[0].cn[0].text, 'Updated GC', 'Grandchild1 vdom is updated');
|
339
|
+
// Child2 is also expanded because the adjustedDepth of 3 is enough to reach it
|
340
|
+
t.notOk(expChild2.componentId, 'Child2 IS expanded due to update depth');
|
341
|
+
t.is(expChild2.text, 'Initial C2', 'Child2 is fully expanded');
|
342
|
+
// Child3 should be expanded as it requested the update
|
343
|
+
t.is(expChild3.text, 'Updated C3', 'Child3 vdom is updated');
|
344
|
+
|
345
|
+
// 4. GENERATE DELTAS
|
346
|
+
const { deltas } = VdomHelper.update({
|
347
|
+
vdom: newAsymmetricVdom,
|
348
|
+
vnode: oldAsymmetricVnode
|
349
|
+
});
|
350
|
+
|
351
|
+
// 5. ASSERTIONS
|
352
|
+
t.is(deltas.length, 2, 'Should generate two deltas for the two changes');
|
353
|
+
|
354
|
+
const gcSpanVnode = oldAsymmetricVnode.childNodes[0].childNodes[0].childNodes[0];
|
355
|
+
const c3Vnode = oldAsymmetricVnode.childNodes[2];
|
356
|
+
|
357
|
+
const delta1 = deltas.find(d => d.id === gcSpanVnode.id);
|
358
|
+
const delta2 = deltas.find(d => d.id === c3Vnode.id);
|
359
|
+
|
360
|
+
t.ok(delta1, 'A delta for the grandchild change should exist');
|
361
|
+
t.is(delta1.textContent, 'Updated GC', 'Grandchild text delta is correct');
|
362
|
+
|
363
|
+
t.ok(delta2, 'A delta for the child3 change should exist');
|
364
|
+
t.is(delta2.textContent, 'Updated C3', 'Child3 text delta is correct');
|
365
|
+
});
|
366
|
+
});
|