neo.mjs 10.0.0-beta.4 → 10.0.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/RELEASE_NOTES/v10.0.0-beta.4.md +2 -2
- package/.github/RELEASE_NOTES/v10.0.0-beta.5.md +70 -0
- package/.github/RELEASE_NOTES/v10.0.0-beta.6.md +48 -0
- package/.github/epic-functional-components.md +498 -0
- package/.github/ticket-asymmetric-vdom-updates.md +122 -0
- package/README.md +0 -3
- package/ServiceWorker.mjs +2 -2
- package/apps/colors/store/Colors.mjs +1 -0
- package/apps/colors/view/GridContainer.mjs +3 -0
- package/apps/colors/view/HeaderToolbar.mjs +2 -0
- package/apps/colors/view/Viewport.mjs +3 -0
- package/apps/covid/view/FooterContainer.mjs +3 -0
- package/apps/covid/view/GalleryContainer.mjs +2 -0
- package/apps/covid/view/GalleryContainerController.mjs +1 -0
- package/apps/covid/view/HeaderContainer.mjs +2 -0
- package/apps/covid/view/HelixContainer.mjs +2 -0
- package/apps/covid/view/HelixContainerController.mjs +1 -0
- package/apps/covid/view/MainContainer.mjs +3 -0
- package/apps/covid/view/TableContainer.mjs +3 -0
- package/apps/covid/view/TableContainerController.mjs +1 -0
- package/apps/covid/view/WorldMapContainer.mjs +2 -0
- package/apps/covid/view/country/Gallery.mjs +3 -0
- package/apps/covid/view/country/Helix.mjs +8 -0
- package/apps/covid/view/country/HistoricalDataTable.mjs +1 -0
- package/apps/covid/view/country/Table.mjs +2 -0
- package/apps/covid/view/mapboxGl/Component.mjs +1 -0
- package/apps/covid/view/mapboxGl/Container.mjs +2 -0
- package/apps/email/EPIC_PLAN.md +58 -0
- package/apps/email/neo-config.json +2 -2
- package/apps/email/store/Emails.mjs +11 -1
- package/apps/email/view/ComposeView.mjs +44 -0
- package/apps/email/view/MainView.mjs +89 -0
- package/apps/email/view/Viewport.mjs +4 -33
- package/apps/email/view/ViewportStateProvider.mjs +3 -3
- package/apps/form/store/SideNav.mjs +1 -0
- package/apps/form/view/FormContainer.mjs +1 -0
- package/apps/form/view/FormPageContainer.mjs +2 -0
- package/apps/form/view/SideNavList.mjs +1 -0
- package/apps/form/view/Viewport.mjs +3 -0
- package/apps/portal/childapps/preview/MainContainer.mjs +1 -0
- package/apps/portal/index.html +1 -1
- package/apps/portal/store/BlogPosts.mjs +2 -0
- package/apps/portal/store/Content.mjs +1 -0
- package/apps/portal/store/ContentSections.mjs +1 -0
- package/apps/portal/store/Examples.mjs +1 -0
- package/apps/portal/view/HeaderToolbar.mjs +1 -0
- package/apps/portal/view/Viewport.mjs +5 -0
- package/apps/portal/view/ViewportController.mjs +8 -2
- package/apps/portal/view/about/Container.mjs +2 -0
- package/apps/portal/view/about/MemberContainer.mjs +7 -0
- package/apps/portal/view/blog/Container.mjs +2 -0
- package/apps/portal/view/blog/List.mjs +2 -0
- package/apps/portal/view/examples/List.mjs +1 -0
- package/apps/portal/view/examples/TabContainer.mjs +4 -0
- package/apps/portal/view/home/ContentBox.mjs +3 -0
- package/apps/portal/view/home/FeatureSection.mjs +8 -0
- package/apps/portal/view/home/FooterContainer.mjs +4 -1
- package/apps/portal/view/home/MainContainer.mjs +2 -0
- package/apps/portal/view/home/parts/AfterMath.mjs +2 -0
- package/apps/portal/view/home/parts/BaseContainer.mjs +1 -0
- package/apps/portal/view/home/parts/Colors.mjs +4 -0
- package/apps/portal/view/home/parts/Features.mjs +2 -0
- package/apps/portal/view/home/parts/Helix.mjs +5 -0
- package/apps/portal/view/home/parts/How.mjs +4 -0
- package/apps/portal/view/home/parts/MainNeo.mjs +1 -0
- package/apps/portal/view/home/parts/References.mjs +2 -0
- package/apps/portal/view/learn/ContentComponent.mjs +11 -5
- package/apps/portal/view/learn/ContentTreeList.mjs +2 -0
- package/apps/portal/view/learn/CubeLayoutButton.mjs +1 -0
- package/apps/portal/view/learn/MainContainer.mjs +4 -0
- package/apps/portal/view/learn/PageContainer.mjs +2 -0
- package/apps/portal/view/learn/PageSectionsContainer.mjs +3 -0
- package/apps/portal/view/learn/PageSectionsList.mjs +1 -0
- package/apps/portal/view/services/Component.mjs +1 -0
- package/apps/realworld/api/Base.mjs +1 -0
- package/apps/realworld/view/HeaderComponent.mjs +4 -0
- package/apps/realworld/view/HomeComponent.mjs +7 -0
- package/apps/realworld/view/MainContainer.mjs +2 -0
- package/apps/realworld/view/MainContainerController.mjs +2 -0
- package/apps/realworld/view/article/CommentComponent.mjs +3 -0
- package/apps/realworld/view/article/Component.mjs +17 -10
- package/apps/realworld/view/article/CreateCommentComponent.mjs +2 -0
- package/apps/realworld/view/article/CreateComponent.mjs +5 -0
- package/apps/realworld/view/article/PreviewComponent.mjs +9 -0
- package/apps/realworld/view/article/TagListComponent.mjs +2 -0
- package/apps/realworld/view/user/ProfileComponent.mjs +7 -0
- package/apps/realworld/view/user/SettingsComponent.mjs +5 -0
- package/apps/realworld/view/user/SignUpComponent.mjs +3 -0
- package/apps/realworld2/api/Base.mjs +1 -0
- package/apps/realworld2/view/FooterComponent.mjs +1 -0
- package/apps/realworld2/view/HeaderToolbar.mjs +3 -0
- package/apps/realworld2/view/HomeContainer.mjs +1 -0
- package/apps/realworld2/view/MainContainer.mjs +2 -0
- package/apps/realworld2/view/MainContainerController.mjs +1 -0
- package/apps/realworld2/view/article/Helix.mjs +1 -0
- package/apps/realworld2/view/article/PreviewComponent.mjs +9 -0
- package/apps/realworld2/view/article/PreviewList.mjs +1 -0
- package/apps/realworld2/view/article/TagListComponent.mjs +2 -0
- package/apps/route/view/CenterContainer.mjs +1 -0
- package/apps/route/view/MainView.mjs +1 -0
- package/apps/sharedcovid/childapps/sharedcovidchart/MainContainer.mjs +1 -0
- package/apps/sharedcovid/childapps/sharedcovidgallery/MainContainer.mjs +1 -0
- package/apps/sharedcovid/childapps/sharedcovidhelix/MainContainer.mjs +1 -0
- package/apps/sharedcovid/childapps/sharedcovidmap/MainContainer.mjs +1 -0
- package/apps/sharedcovid/view/FooterContainer.mjs +3 -0
- package/apps/sharedcovid/view/GalleryContainer.mjs +2 -0
- package/apps/sharedcovid/view/GalleryContainerController.mjs +1 -0
- package/apps/sharedcovid/view/HeaderContainer.mjs +2 -0
- package/apps/sharedcovid/view/HelixContainer.mjs +2 -0
- package/apps/sharedcovid/view/HelixContainerController.mjs +1 -0
- package/apps/sharedcovid/view/MainContainer.mjs +3 -0
- package/apps/sharedcovid/view/TableContainer.mjs +3 -0
- package/apps/sharedcovid/view/TableContainerController.mjs +1 -0
- package/apps/sharedcovid/view/WorldMapContainer.mjs +2 -0
- package/apps/sharedcovid/view/country/Gallery.mjs +3 -0
- package/apps/sharedcovid/view/country/Helix.mjs +8 -0
- package/apps/sharedcovid/view/country/HistoricalDataTable.mjs +1 -0
- package/apps/sharedcovid/view/country/Table.mjs +2 -0
- package/apps/sharedcovid/view/mapboxGl/Component.mjs +1 -0
- package/apps/sharedcovid/view/mapboxGl/Container.mjs +2 -0
- package/apps/shareddialog/childapps/shareddialog2/view/MainContainer.mjs +2 -0
- package/apps/shareddialog/view/DemoDialog.mjs +2 -0
- package/apps/shareddialog/view/MainContainer.mjs +2 -0
- package/apps/shareddialog/view/MainContainerController.mjs +1 -0
- package/buildScripts/addReactiveTags.mjs +191 -0
- package/buildScripts/checkReactiveTags.mjs +160 -0
- package/docs/app/store/Api.mjs +1 -0
- package/docs/app/store/Examples.mjs +1 -0
- package/docs/app/view/ApiTreeList.mjs +1 -0
- package/docs/app/view/ContentTabContainer.mjs +2 -0
- package/docs/app/view/ExamplesTreeList.mjs +2 -0
- package/docs/app/view/HeaderContainer.mjs +3 -0
- package/docs/app/view/MainContainer.mjs +5 -0
- package/docs/app/view/classdetails/HeaderComponent.mjs +1 -0
- package/docs/app/view/classdetails/MainContainer.mjs +3 -0
- package/docs/app/view/classdetails/MembersList.mjs +5 -0
- package/docs/app/view/classdetails/SourceViewComponent.mjs +2 -0
- package/examples/ConfigurationViewport.mjs +14 -8
- package/examples/button/effect/MainContainer.mjs +207 -0
- package/examples/button/effect/app.mjs +6 -0
- package/examples/button/effect/index.html +11 -0
- package/examples/button/effect/neo-config.json +6 -0
- package/examples/calendar/weekview/MainContainer.mjs +4 -0
- package/examples/component/coronaGallery/CountryGallery.mjs +2 -0
- package/examples/component/coronaGallery/CountryStore.mjs +1 -0
- package/examples/component/coronaGallery/Viewport.mjs +3 -0
- package/examples/component/coronaGallery/ViewportController.mjs +1 -0
- package/examples/component/coronaHelix/CountryHelix.mjs +7 -0
- package/examples/component/coronaHelix/MainContainer.mjs +1 -0
- package/examples/component/gallery/ImageStore.mjs +1 -0
- package/examples/component/helix/ImageStore.mjs +1 -0
- package/examples/component/helix/Viewport.mjs +3 -0
- package/examples/component/helix/ViewportController.mjs +1 -0
- package/examples/component/multiWindowCoronaGallery/childapp/Viewport.mjs +1 -0
- package/examples/component/multiWindowHelix/childapp/Viewport.mjs +1 -0
- package/examples/component/wrapper/googleMaps/MapComponent.mjs +2 -0
- package/examples/core/config/MainContainer.mjs +2 -0
- package/examples/dialog/DemoDialog.mjs +2 -0
- package/examples/dialog/MainContainer.mjs +1 -0
- package/examples/form/field/color/MainStore.mjs +1 -0
- package/examples/functional/defineComponent/Component.mjs +18 -0
- package/examples/functional/defineComponent/MainContainer.mjs +41 -0
- package/examples/functional/defineComponent/app.mjs +6 -0
- package/examples/functional/defineComponent/index.html +11 -0
- package/examples/functional/defineComponent/neo-config.json +6 -0
- package/examples/functional/hostComponent/Component.mjs +32 -0
- package/examples/functional/hostComponent/MainContainer.mjs +48 -0
- package/examples/functional/hostComponent/app.mjs +6 -0
- package/examples/functional/hostComponent/index.html +11 -0
- package/examples/functional/hostComponent/neo-config.json +6 -0
- package/examples/grid/animatedRowSorting/Viewport.mjs +1 -1
- package/examples/grid/bigData/ControlsContainer.mjs +3 -0
- package/examples/grid/bigData/GridContainer.mjs +4 -2
- package/examples/grid/bigData/MainContainer.mjs +2 -0
- package/examples/grid/bigData/MainModel.mjs +1 -0
- package/examples/grid/bigData/MainStore.mjs +3 -0
- package/examples/grid/cellEditing/MainContainer.mjs +1 -1
- package/examples/grid/container/MainContainer.mjs +1 -1
- package/examples/grid/covid/GridContainer.mjs +3 -0
- package/examples/grid/covid/MainContainer.mjs +2 -0
- package/examples/grid/covid/Store.mjs +1 -0
- package/examples/grid/nestedRecordFields/EditUserDialog.mjs +3 -0
- package/examples/grid/nestedRecordFields/Viewport.mjs +3 -1
- package/examples/list/animate/List.mjs +4 -0
- package/examples/list/animate/MainContainer.mjs +2 -0
- package/examples/list/circle/MainStore.mjs +1 -0
- package/examples/list/color/MainStore.mjs +1 -0
- package/examples/preloadingAssets/view/MainContainer.mjs +2 -0
- package/examples/stateProvider/advanced/MainContainer.mjs +1 -0
- package/examples/stateProvider/dialog/EditUserDialog.mjs +2 -0
- package/examples/stateProvider/dialog/MainContainer.mjs +1 -0
- package/examples/stateProvider/extendedClass/MainContainer.mjs +2 -0
- package/examples/stateProvider/inline/MainContainer.mjs +1 -0
- package/examples/stateProvider/inlineNoStateProvider/MainContainer.mjs +1 -0
- package/examples/stateProvider/inlineNoStateProvider/MainContainerController.mjs +2 -0
- package/examples/stateProvider/multiWindow/EditUserDialog.mjs +3 -0
- package/examples/stateProvider/multiWindow/MainContainer.mjs +1 -0
- package/examples/stateProvider/multiWindow/Viewport.mjs +1 -0
- package/examples/stateProvider/nestedData/MainContainer.mjs +1 -0
- package/examples/stateProvider/table/MainContainer.mjs +1 -0
- package/examples/table/covid/MainContainer.mjs +2 -0
- package/examples/table/covid/Store.mjs +1 -0
- package/examples/table/covid/TableContainer.mjs +3 -0
- package/examples/table/nestedRecordFields/EditUserDialog.mjs +3 -0
- package/examples/table/nestedRecordFields/Viewport.mjs +1 -0
- package/examples/todoList/version1/MainComponent.mjs +1 -1
- package/examples/toolbar/breadcrumb/view/MainContainer.mjs +2 -0
- package/examples/toolbar/paging/store/Users.mjs +1 -0
- package/examples/toolbar/paging/view/AddUserDialog.mjs +3 -0
- package/examples/toolbar/paging/view/MainContainer.mjs +3 -0
- package/examples/treeAccordion/MainContainer.mjs +2 -2
- package/examples/worker/task/MainContainer.mjs +1 -0
- package/learn/Glossary.md +1 -0
- package/learn/UsingTheseTopics.md +1 -0
- package/learn/benefits/ConfigSystem.md +2 -0
- package/learn/benefits/Effort.md +1 -0
- package/learn/benefits/Features.md +1 -0
- package/learn/benefits/FormsEngine.md +1 -0
- package/learn/benefits/FourEnvironments.md +2 -0
- package/learn/benefits/Introduction.md +2 -0
- package/learn/benefits/MultiWindow.md +3 -1
- package/learn/benefits/OffTheMainThread.md +2 -0
- package/learn/benefits/Quick.md +2 -0
- package/learn/benefits/RPCLayer.md +2 -0
- package/learn/benefits/Speed.md +2 -0
- package/learn/comparisons/NeoVsAngular.md +90 -0
- package/learn/comparisons/NeoVsExtJs.md +178 -0
- package/learn/comparisons/NeoVsNextJs.md +124 -0
- package/learn/comparisons/NeoVsReact.md +95 -0
- package/learn/comparisons/NeoVsSolid.md +78 -0
- package/learn/comparisons/NeoVsVue.md +92 -0
- package/learn/comparisons/Overview.md +46 -0
- package/learn/gettingstarted/ComponentModels.md +2 -0
- package/learn/gettingstarted/Config.md +2 -0
- package/learn/gettingstarted/DescribingTheUI.md +2 -0
- package/learn/gettingstarted/Events.md +2 -0
- package/learn/gettingstarted/Extending.md +2 -0
- package/learn/gettingstarted/References.md +2 -0
- package/learn/gettingstarted/Setup.md +3 -2
- package/learn/gettingstarted/Workspaces.md +2 -0
- package/learn/guides/datahandling/Collections.md +1 -0
- package/learn/guides/datahandling/Records.md +1 -0
- package/learn/guides/datahandling/StateProviders.md +131 -16
- package/learn/guides/datahandling/Tables.md +1 -1
- package/learn/guides/fundamentals/ConfigSystemDeepDive.md +1 -0
- package/learn/guides/fundamentals/DeclarativeComponentTreesVsImperativeVdom.md +2 -0
- package/learn/guides/fundamentals/DeclarativeVDOMWithEffects.md +168 -0
- package/learn/guides/fundamentals/ExtendingNeoClasses.md +1 -0
- package/learn/guides/fundamentals/InstanceLifecycle.md +3 -1
- package/learn/guides/fundamentals/MainThreadAddons.md +2 -0
- package/learn/guides/specificfeatures/Mixins.md +3 -1
- package/learn/guides/specificfeatures/MultiWindow.md +3 -1
- package/learn/guides/specificfeatures/PortalApp.md +2 -0
- package/learn/guides/uibuildingblocks/ComponentsAndContainers.md +2 -0
- package/learn/guides/uibuildingblocks/CustomComponents.md +2 -0
- package/learn/guides/uibuildingblocks/Layouts.md +2 -0
- package/learn/guides/uibuildingblocks/WorkingWithVDom.md +2 -0
- package/learn/guides/userinteraction/Forms.md +2 -0
- package/learn/guides/userinteraction/events/CustomEvents.md +2 -1
- package/learn/guides/userinteraction/events/DomEvents.md +2 -0
- package/learn/javascript/ClassFeatures.md +4 -3
- package/learn/javascript/Classes.md +10 -13
- package/learn/javascript/Overrides.md +10 -6
- package/learn/javascript/Super.md +12 -8
- package/learn/tree.json +71 -63
- package/learn/tutorials/Earthquakes.md +2 -0
- package/learn/tutorials/RSP.md +3 -1
- package/learn/tutorials/TodoList.md +103 -7
- package/package.json +6 -4
- package/resources/scss/src/apps/email/ComposeView.scss +16 -0
- package/resources/scss/src/apps/email/MainView.scss +5 -0
- package/resources/scss/src/apps/portal/learn/ContentComponent.scss +5 -4
- package/src/DefaultConfig.mjs +12 -2
- package/src/Main.mjs +1 -0
- package/src/Neo.mjs +377 -178
- package/src/Xhr.mjs +1 -0
- package/src/button/Base.mjs +13 -0
- package/src/button/Effect.mjs +449 -0
- package/src/button/Split.mjs +2 -0
- package/src/calendar/store/Calendars.mjs +1 -0
- package/src/calendar/store/Colors.mjs +1 -0
- package/src/calendar/store/Events.mjs +1 -0
- package/src/calendar/view/DayComponent.mjs +2 -0
- package/src/calendar/view/EditEventContainer.mjs +4 -1
- package/src/calendar/view/MainContainer.mjs +13 -0
- package/src/calendar/view/MainContainerStateProvider.mjs +14 -28
- package/src/calendar/view/SettingsContainer.mjs +1 -0
- package/src/calendar/view/YearComponent.mjs +16 -0
- package/src/calendar/view/calendars/ColorsList.mjs +2 -0
- package/src/calendar/view/calendars/Container.mjs +2 -0
- package/src/calendar/view/calendars/EditContainer.mjs +1 -0
- package/src/calendar/view/month/Component.mjs +11 -0
- package/src/calendar/view/settings/GeneralContainer.mjs +1 -0
- package/src/calendar/view/settings/MonthContainer.mjs +1 -0
- package/src/calendar/view/settings/WeekContainer.mjs +1 -0
- package/src/calendar/view/settings/YearContainer.mjs +1 -0
- package/src/calendar/view/week/Component.mjs +15 -1
- package/src/calendar/view/week/TimeAxisComponent.mjs +4 -0
- package/src/code/LivePreview.mjs +51 -23
- package/src/collection/Base.mjs +14 -12
- package/src/collection/Filter.mjs +6 -0
- package/src/collection/Sorter.mjs +3 -0
- package/src/component/Base.mjs +156 -802
- package/src/component/Canvas.mjs +1 -0
- package/src/component/Chip.mjs +4 -0
- package/src/component/Circle.mjs +14 -0
- package/src/component/Clock.mjs +4 -0
- package/src/component/DateSelector.mjs +12 -0
- package/src/component/Gallery.mjs +11 -0
- package/src/component/Helix.mjs +24 -0
- package/src/component/Label.mjs +1 -0
- package/src/component/Legend.mjs +3 -0
- package/src/component/MagicMoveText.mjs +4 -0
- package/src/component/Progress.mjs +3 -0
- package/src/component/Splitter.mjs +3 -0
- package/src/component/StatusBadge.mjs +6 -0
- package/src/component/Timer.mjs +4 -0
- package/src/component/Toast.mjs +6 -0
- package/src/component/Video.mjs +1 -0
- package/src/component/mwc/Button.mjs +7 -0
- package/src/component/mwc/TextField.mjs +9 -0
- package/src/component/wrapper/AmChart.mjs +2 -0
- package/src/component/wrapper/GoogleMaps.mjs +3 -0
- package/src/component/wrapper/MapboxGL.mjs +5 -0
- package/src/component/wrapper/MonacoEditor.mjs +12 -0
- package/src/container/Accordion.mjs +2 -0
- package/src/container/Base.mjs +34 -26
- package/src/container/Panel.mjs +1 -0
- package/src/container/Viewport.mjs +1 -0
- package/src/controller/Application.mjs +1 -0
- package/src/controller/Base.mjs +1 -0
- package/src/controller/Component.mjs +1 -0
- package/src/core/Base.mjs +193 -22
- package/src/core/Compare.mjs +4 -7
- package/src/core/Config.mjs +137 -33
- package/src/core/Effect.mjs +193 -0
- package/src/core/EffectBatchManager.mjs +67 -0
- package/src/core/EffectManager.mjs +60 -0
- package/src/core/IdGenerator.mjs +13 -44
- package/src/data/Model.mjs +2 -0
- package/src/data/Store.mjs +7 -0
- package/src/data/connection/WebSocket.mjs +2 -0
- package/src/date/DayViewComponent.mjs +2 -0
- package/src/date/SelectorContainer.mjs +14 -0
- package/src/dialog/Base.mjs +8 -0
- package/src/draggable/DragZone.mjs +5 -0
- package/src/draggable/tree/DragZone.mjs +1 -0
- package/src/filter/BooleanContainer.mjs +2 -0
- package/src/filter/NumberContainer.mjs +3 -0
- package/src/filter/ToggleOperatorsButton.mjs +2 -0
- package/src/form/Fieldset.mjs +6 -0
- package/src/form/field/Base.mjs +7 -0
- package/src/form/field/CheckBox.mjs +18 -0
- package/src/form/field/Chip.mjs +1 -0
- package/src/form/field/ComboBox.mjs +8 -0
- package/src/form/field/Country.mjs +1 -0
- package/src/form/field/Currency.mjs +2 -0
- package/src/form/field/Date.mjs +4 -0
- package/src/form/field/Display.mjs +1 -0
- package/src/form/field/Email.mjs +1 -0
- package/src/form/field/FileUpload.mjs +7 -0
- package/src/form/field/Hidden.mjs +1 -0
- package/src/form/field/Number.mjs +7 -0
- package/src/form/field/Password.mjs +1 -0
- package/src/form/field/Phone.mjs +3 -0
- package/src/form/field/Picker.mjs +2 -0
- package/src/form/field/Radio.mjs +1 -0
- package/src/form/field/Range.mjs +3 -0
- package/src/form/field/Search.mjs +2 -0
- package/src/form/field/Text.mjs +32 -0
- package/src/form/field/TextArea.mjs +7 -0
- package/src/form/field/Time.mjs +6 -0
- package/src/form/field/Url.mjs +3 -0
- package/src/form/field/ZipCode.mjs +2 -0
- package/src/form/field/trigger/Base.mjs +3 -0
- package/src/form/field/trigger/Clear.mjs +2 -0
- package/src/form/field/trigger/CopyToClipboard.mjs +2 -0
- package/src/form/field/trigger/Date.mjs +1 -0
- package/src/form/field/trigger/Picker.mjs +1 -0
- package/src/form/field/trigger/Search.mjs +1 -0
- package/src/form/field/trigger/SpinDown.mjs +2 -0
- package/src/form/field/trigger/SpinUp.mjs +1 -0
- package/src/form/field/trigger/Time.mjs +2 -0
- package/src/functional/_export.mjs +6 -0
- package/src/functional/component/Base.mjs +499 -0
- package/src/functional/defineComponent.mjs +102 -0
- package/src/functional/useConfig.mjs +52 -0
- package/src/functional/useEvent.mjs +43 -0
- package/src/grid/Body.mjs +20 -1
- package/src/grid/Container.mjs +57 -63
- package/src/grid/ScrollManager.mjs +2 -0
- package/src/grid/VerticalScrollbar.mjs +2 -0
- package/src/grid/column/Base.mjs +2 -0
- package/src/grid/column/Component.mjs +1 -1
- package/src/grid/header/Button.mjs +7 -0
- package/src/grid/header/Toolbar.mjs +6 -0
- package/src/grid/plugin/AnimateRows.mjs +2 -0
- package/src/layout/Base.mjs +3 -0
- package/src/layout/Card.mjs +1 -0
- package/src/layout/Cube.mjs +11 -1
- package/src/layout/Fit.mjs +1 -0
- package/src/layout/Flexbox.mjs +7 -0
- package/src/layout/Form.mjs +2 -0
- package/src/layout/Grid.mjs +1 -0
- package/src/layout/HBox.mjs +1 -0
- package/src/layout/VBox.mjs +1 -0
- package/src/list/Base.mjs +13 -0
- package/src/list/Chip.mjs +1 -0
- package/src/list/Circle.mjs +2 -0
- package/src/list/Color.mjs +1 -0
- package/src/list/plugin/Animate.mjs +2 -0
- package/src/main/DeltaUpdates.mjs +1 -0
- package/src/main/DomEvents.mjs +2 -0
- package/src/main/addon/CloneNode.mjs +1 -0
- package/src/main/addon/Cookie.mjs +1 -0
- package/src/main/addon/GoogleMaps.mjs +1 -0
- package/src/main/addon/LocalStorage.mjs +1 -0
- package/src/main/addon/MapboxGL.mjs +1 -0
- package/src/main/addon/Markdown.mjs +1 -0
- package/src/main/addon/Navigator.mjs +1 -0
- package/src/main/addon/Popover.mjs +1 -0
- package/src/main/addon/Stylesheet.mjs +1 -0
- package/src/main/addon/WindowPosition.mjs +1 -0
- package/src/manager/Component.mjs +0 -71
- package/src/manager/VDomUpdate.mjs +235 -0
- package/src/menu/List.mjs +6 -0
- package/src/menu/Model.mjs +1 -0
- package/src/menu/Panel.mjs +3 -0
- package/src/menu/Store.mjs +1 -0
- package/src/mixin/DomEvents.mjs +130 -0
- package/src/mixin/VdomLifecycle.mjs +667 -0
- package/src/plugin/Base.mjs +1 -0
- package/src/plugin/Resizable.mjs +2 -0
- package/src/selection/Model.mjs +15 -18
- package/src/selection/grid/BaseModel.mjs +1 -0
- package/src/sitemap/Component.mjs +1 -0
- package/src/state/Provider.mjs +376 -457
- package/src/state/createHierarchicalDataProxy.mjs +138 -0
- package/src/tab/Container.mjs +6 -0
- package/src/tab/Strip.mjs +1 -0
- package/src/tab/header/Button.mjs +2 -0
- package/src/tab/header/EffectButton.mjs +77 -0
- package/src/tab/header/Toolbar.mjs +1 -0
- package/src/table/Body.mjs +3 -0
- package/src/table/Container.mjs +10 -0
- package/src/table/header/Button.mjs +8 -0
- package/src/table/header/Toolbar.mjs +5 -0
- package/src/table/plugin/CellEditing.mjs +1 -0
- package/src/toolbar/Base.mjs +4 -0
- package/src/toolbar/Breadcrumb.mjs +3 -0
- package/src/toolbar/Paging.mjs +5 -0
- package/src/tooltip/Base.mjs +2 -0
- package/src/tree/List.mjs +3 -0
- package/src/util/HashHistory.mjs +1 -0
- package/src/util/KeyNavigation.mjs +2 -0
- package/src/util/Matrix.mjs +1 -0
- package/src/util/VDom.mjs +7 -1
- package/src/util/VNode.mjs +7 -1
- package/src/util/vdom/TreeBuilder.mjs +129 -0
- package/src/vdom/Helper.mjs +44 -33
- package/src/vdom/VNode.mjs +5 -7
- package/src/worker/App.mjs +1 -5
- package/src/worker/Base.mjs +2 -0
- package/src/worker/Manager.mjs +2 -0
- package/src/worker/ServiceBase.mjs +6 -1
- package/test/siesta/siesta.js +36 -1
- package/test/siesta/tests/CollectionBase.mjs +10 -10
- package/test/siesta/tests/VdomCalendar.mjs +13 -9
- package/test/siesta/tests/VdomHelper.mjs +22 -59
- package/test/siesta/tests/config/AfterSetConfig.mjs +100 -0
- package/test/siesta/tests/{ReactiveConfigs.mjs → config/Basic.mjs} +58 -21
- package/test/siesta/tests/config/CircularDependencies.mjs +166 -0
- package/test/siesta/tests/config/CustomFunctions.mjs +69 -0
- package/test/siesta/tests/config/Hierarchy.mjs +94 -0
- package/test/siesta/tests/config/MemoryLeak.mjs +92 -0
- package/test/siesta/tests/config/MultiLevelHierarchy.mjs +85 -0
- package/test/siesta/tests/core/Effect.mjs +127 -0
- package/test/siesta/tests/core/EffectBatching.mjs +310 -0
- package/test/siesta/tests/neo/MixinStaticConfig.mjs +138 -0
- package/test/siesta/tests/state/Provider.mjs +537 -0
- package/test/siesta/tests/state/ProviderNestedDataConfigs.mjs +255 -0
- package/test/siesta/tests/state/createHierarchicalDataProxy.mjs +204 -0
- package/test/siesta/tests/vdom/VdomAsymmetricUpdates.mjs +366 -0
- package/test/siesta/tests/vdom/VdomRealWorldUpdates.mjs +249 -0
- package/learn/javascript/NewNode.md +0 -31
package/src/state/Provider.mjs
CHANGED
@@ -1,16 +1,19 @@
|
|
1
|
-
import Base
|
2
|
-
import ClassSystemUtil
|
3
|
-
import
|
4
|
-
import
|
1
|
+
import Base from '../core/Base.mjs';
|
2
|
+
import ClassSystemUtil from '../util/ClassSystem.mjs';
|
3
|
+
import Config from '../core/Config.mjs';
|
4
|
+
import Effect from '../core/Effect.mjs';
|
5
|
+
import EffectBatchManager from '../core/EffectBatchManager.mjs';
|
6
|
+
import Observable from '../core/Observable.mjs';
|
7
|
+
import {createHierarchicalDataProxy} from './createHierarchicalDataProxy.mjs';
|
8
|
+
import {isDescriptor} from '../core/ConfigSymbols.mjs';
|
5
9
|
|
6
|
-
const
|
7
|
-
twoWayBindingSymbol = Symbol.for('twoWayBinding'),
|
8
|
-
variableNameRegex = /^\w*/;
|
10
|
+
const twoWayBindingSymbol = Symbol.for('twoWayBinding');
|
9
11
|
|
10
12
|
/**
|
11
13
|
* An optional component state provider for adding bindings to configs
|
12
14
|
* @class Neo.state.Provider
|
13
15
|
* @extends Neo.core.Base
|
16
|
+
* @mixes Neo.core.Observable
|
14
17
|
*/
|
15
18
|
class Provider extends Base {
|
16
19
|
/**
|
@@ -31,100 +34,154 @@ class Provider extends Base {
|
|
31
34
|
* @protected
|
32
35
|
*/
|
33
36
|
ntype: 'state-provider',
|
34
|
-
/**
|
35
|
-
* @member {Object|null} bindings_=null
|
36
|
-
* @protected
|
37
|
-
*/
|
38
|
-
bindings_: null,
|
39
37
|
/**
|
40
38
|
* @member {Neo.component.Base|null} component=null
|
41
39
|
* @protected
|
42
40
|
*/
|
43
41
|
component: null,
|
44
42
|
/**
|
43
|
+
/**
|
44
|
+
* The core data object managed by this StateProvider.
|
45
|
+
* This object holds the reactive state that can be accessed and modified
|
46
|
+
* by components and formulas within the provider's hierarchy.
|
47
|
+
* Changes to properties within this data object will trigger reactivity.
|
48
|
+
* When new data is assigned, it will be deeply merged with existing data.
|
45
49
|
* @member {Object|null} data_=null
|
50
|
+
* @example
|
51
|
+
* data: {
|
52
|
+
* user: {
|
53
|
+
* firstName: 'John',
|
54
|
+
* lastName : 'Doe'
|
55
|
+
* },
|
56
|
+
* settings: {
|
57
|
+
* theme: 'dark'
|
58
|
+
* }
|
59
|
+
* }
|
60
|
+
* @reactive
|
46
61
|
*/
|
47
|
-
data_:
|
62
|
+
data_: {
|
63
|
+
[isDescriptor]: true,
|
64
|
+
merge : 'deep',
|
65
|
+
value : {}
|
66
|
+
},
|
48
67
|
/**
|
68
|
+
* Defines computed properties based on other data properties within the StateProvider hierarchy.
|
69
|
+
* Each formula is a function that receives a `data` argument, which is a hierarchical proxy
|
70
|
+
* allowing access to data from the current provider and all its parent providers.
|
71
|
+
* Changes to dependencies (accessed via `data.propertyName`) will automatically re-run the formula.
|
49
72
|
* @member {Object|null} formulas_=null
|
50
|
-
*
|
51
73
|
* @example
|
52
74
|
* data: {
|
53
|
-
* a: 1,
|
54
|
-
* b: 2
|
75
|
+
* a : 1,
|
76
|
+
* b : 2,
|
77
|
+
* total: 50
|
55
78
|
* }
|
56
79
|
* formulas: {
|
57
|
-
* aPlusB:
|
58
|
-
*
|
59
|
-
*
|
60
|
-
*
|
61
|
-
* },
|
62
|
-
* get(data) {
|
63
|
-
* return data.foo + data.bar
|
64
|
-
* }
|
65
|
-
* }
|
80
|
+
* aPlusB : (data) => data.a + data.b,
|
81
|
+
* aTimesB: (data) => data.a * data.b,
|
82
|
+
* // Accessing parent data (assuming a parent provider has a 'taxRate' property)
|
83
|
+
* totalWithTax: (data) => data.total * (1 + data.taxRate)
|
66
84
|
* }
|
85
|
+
* @reactive
|
67
86
|
*/
|
68
87
|
formulas_: null,
|
69
88
|
/**
|
70
89
|
* @member {Neo.state.Provider|null} parent_=null
|
90
|
+
* @reactive
|
71
91
|
*/
|
72
92
|
parent_: null,
|
73
93
|
/**
|
94
|
+
/**
|
95
|
+
* A collection of Neo.data.Store instances managed by this StateProvider.
|
96
|
+
* Stores are defined as config objects with a `module` property pointing
|
97
|
+
* to the store class, which will then be instantiated by the framework.
|
74
98
|
* @member {Object|null} stores_=null
|
99
|
+
* @example
|
100
|
+
* stores: {
|
101
|
+
* myUsers: {
|
102
|
+
* module: Neo.data.Store,
|
103
|
+
* model : 'MyApp.model.User',
|
104
|
+
* data : [{id: 1, name: 'John'}, {id: 2, name: 'Doe'}]
|
105
|
+
* },
|
106
|
+
* myCustomStore1: MyCustomStoreClass,
|
107
|
+
* myCustomStore2: {
|
108
|
+
* module : MyCustomStoreClass,
|
109
|
+
* autoLoad: true
|
110
|
+
* }
|
111
|
+
* }
|
112
|
+
* @reactive
|
75
113
|
*/
|
76
114
|
stores_: null
|
77
115
|
}
|
78
116
|
|
79
117
|
/**
|
80
|
-
* @
|
118
|
+
* @member {Map} #bindingEffects=new Map()
|
119
|
+
* @private
|
81
120
|
*/
|
82
|
-
|
83
|
-
Neo.currentWorker.isUsingStateProviders = true;
|
84
|
-
super.construct(config);
|
85
|
-
this.bindings = {}
|
86
|
-
}
|
87
|
-
|
121
|
+
#bindingEffects = new Map()
|
88
122
|
/**
|
89
|
-
*
|
90
|
-
* The method is used by setData() & setDataAtSameLevel()
|
91
|
-
* in case the data property does not exist yet.
|
92
|
-
* @param {String} key
|
93
|
-
* @param {*} value
|
123
|
+
* @member {Object} #dataConfigs={}
|
94
124
|
* @private
|
95
125
|
*/
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
data = me.getDataScope(key);
|
103
|
-
scope = data.scope;
|
104
|
-
|
105
|
-
scope[data.key] = value;
|
126
|
+
#dataConfigs = {}
|
127
|
+
/**
|
128
|
+
* @member {Map} #formulaEffects=new Map()
|
129
|
+
* @private
|
130
|
+
*/
|
131
|
+
#formulaEffects = new Map()
|
106
132
|
|
107
|
-
|
133
|
+
/**
|
134
|
+
* @param {Object} config
|
135
|
+
*/
|
136
|
+
construct(config) {
|
137
|
+
Neo.isUsingStateProviders = true;
|
138
|
+
super.construct(config)
|
108
139
|
}
|
109
140
|
|
110
141
|
/**
|
111
|
-
* Triggered after the data config got changed
|
142
|
+
* Triggered after the data config got changed.
|
143
|
+
* This method initializes the internal #dataConfigs map, converting each
|
144
|
+
* plain data property into a reactive Neo.core.Config instance.
|
112
145
|
* @param {Object|null} value
|
113
146
|
* @param {Object|null} oldValue
|
114
147
|
* @protected
|
115
148
|
*/
|
116
149
|
afterSetData(value, oldValue) {
|
117
|
-
value && this.
|
150
|
+
value && this.processDataObject(value)
|
118
151
|
}
|
119
152
|
|
120
153
|
/**
|
121
|
-
* Triggered after the formulas config got changed
|
122
|
-
*
|
123
|
-
*
|
154
|
+
* Triggered after the formulas config got changed.
|
155
|
+
* This method sets up reactive effects for each defined formula.
|
156
|
+
* Each formula function receives the hierarchical data proxy, allowing implicit dependency tracking.
|
157
|
+
* @param {Object|null} value The new formulas configuration.
|
158
|
+
* @param {Object|null} oldValue The old formulas configuration.
|
124
159
|
* @protected
|
125
160
|
*/
|
126
161
|
afterSetFormulas(value, oldValue) {
|
127
|
-
|
162
|
+
const me = this;
|
163
|
+
|
164
|
+
// Destroy old formula effects to prevent memory leaks and stale calculations.
|
165
|
+
me.#formulaEffects.forEach(effect => effect.destroy());
|
166
|
+
me.#formulaEffects.clear();
|
167
|
+
|
168
|
+
if (value) {
|
169
|
+
Object.entries(value).forEach(([formulaKey, formulaFn]) => {
|
170
|
+
// Create a new lazy Effect. It will not run until explicitly told to.
|
171
|
+
const effect = new Effect({
|
172
|
+
fn: () => {
|
173
|
+
const
|
174
|
+
hierarchicalData = me.getHierarchyData(),
|
175
|
+
result = formulaFn(hierarchicalData);
|
176
|
+
|
177
|
+
me.setData(formulaKey, result);
|
178
|
+
},
|
179
|
+
lazy: true
|
180
|
+
});
|
181
|
+
|
182
|
+
me.#formulaEffects.set(formulaKey, effect)
|
183
|
+
})
|
184
|
+
}
|
128
185
|
}
|
129
186
|
|
130
187
|
/**
|
@@ -133,17 +190,7 @@ class Provider extends Base {
|
|
133
190
|
* @protected
|
134
191
|
*/
|
135
192
|
beforeGetData(value) {
|
136
|
-
return
|
137
|
-
}
|
138
|
-
|
139
|
-
/**
|
140
|
-
* Triggered before the parent config gets changed
|
141
|
-
* @param {Neo.state.Provider|null} value
|
142
|
-
* @param {Neo.state.Provider|null} oldValue
|
143
|
-
* @protected
|
144
|
-
*/
|
145
|
-
beforeSetParent(value, oldValue) {
|
146
|
-
return value ? value : this.getParent()
|
193
|
+
return this.getHierarchyData()
|
147
194
|
}
|
148
195
|
|
149
196
|
/**
|
@@ -171,159 +218,92 @@ class Provider extends Base {
|
|
171
218
|
}
|
172
219
|
|
173
220
|
/**
|
174
|
-
*
|
175
|
-
*
|
176
|
-
* @returns {String}
|
177
|
-
*/
|
178
|
-
callFormatter(formatter, data=null) {
|
179
|
-
if (!data) {
|
180
|
-
data = this.getHierarchyData()
|
181
|
-
}
|
182
|
-
|
183
|
-
return formatter.call(this, data)
|
184
|
-
}
|
185
|
-
|
186
|
-
/**
|
187
|
-
* Registers a new binding in case a matching data property does exist.
|
188
|
-
* Otherwise, it will use the closest stateProvider with a match.
|
221
|
+
* Creates a new binding for a component's config to a data property.
|
222
|
+
* This now uses the Effect-based reactivity system.
|
189
223
|
* @param {String} componentId
|
190
|
-
* @param {String}
|
191
|
-
* @param {String} value
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
} else {
|
210
|
-
console.error('No state.Provider found with the specified data property', componentId, keyLeaf, value)
|
211
|
-
}
|
212
|
-
}
|
213
|
-
}
|
224
|
+
* @param {String} configKey The component config to bind (e.g., 'text').
|
225
|
+
* @param {String|Function} formatter The function that computes the value.
|
226
|
+
*/
|
227
|
+
createBinding(componentId, configKey, formatter) {
|
228
|
+
const
|
229
|
+
me = this,
|
230
|
+
effect = new Effect(() => {
|
231
|
+
const component = Neo.get(componentId);
|
232
|
+
|
233
|
+
if (component && !component.isDestroyed) {
|
234
|
+
const
|
235
|
+
hierarchicalData = me.getHierarchyData(),
|
236
|
+
newValue = Neo.isFunction(formatter) ? formatter.call(me, hierarchicalData) : hierarchicalData[formatter];
|
237
|
+
|
238
|
+
component._skipTwoWayPush = configKey;
|
239
|
+
component[configKey] = newValue;
|
240
|
+
delete component._skipTwoWayPush
|
241
|
+
}
|
242
|
+
});
|
214
243
|
|
215
|
-
|
216
|
-
* Registers a new binding in case a matching data property does exist.
|
217
|
-
* Otherwise, it will use the closest stateProvider with a match.
|
218
|
-
* @param {String} componentId
|
219
|
-
* @param {String} formatter
|
220
|
-
* @param {String} value
|
221
|
-
* @returns {String[]}
|
222
|
-
*/
|
223
|
-
createBindingByFormatter(componentId, formatter, value) {
|
224
|
-
let me = this,
|
225
|
-
formatterVars = me.getFormatterVariables(formatter);
|
244
|
+
me.#bindingEffects.set(componentId, effect);
|
226
245
|
|
227
|
-
|
228
|
-
|
246
|
+
// The effect observes the component's destruction to clean itself up.
|
247
|
+
me.observeConfig(componentId, 'isDestroying', (value) => {
|
248
|
+
if (value) {
|
249
|
+
effect.destroy();
|
250
|
+
me.#bindingEffects.delete(componentId)
|
251
|
+
}
|
229
252
|
});
|
230
253
|
|
231
|
-
|
254
|
+
// The effect is returned to be managed by the component.
|
255
|
+
return effect
|
232
256
|
}
|
233
257
|
|
234
258
|
/**
|
235
|
-
*
|
259
|
+
* Processes a component's `bind` configuration to create reactive bindings.
|
260
|
+
* It differentiates between store bindings and data bindings, and sets up two-way binding if specified.
|
261
|
+
* @param {Neo.component.Base} component The component instance whose bindings are to be created.
|
236
262
|
*/
|
237
263
|
createBindings(component) {
|
238
|
-
|
239
|
-
|
240
|
-
formatterVars;
|
241
|
-
|
242
|
-
if (Neo.isObject(value)) {
|
243
|
-
twoWayBinding = true;
|
244
|
-
value = value.value
|
245
|
-
}
|
264
|
+
let me = this,
|
265
|
+
hasTwoWayBinding = false;
|
246
266
|
|
247
|
-
|
248
|
-
|
267
|
+
Object.entries(component.bind || {}).forEach(([configKey, value]) => {
|
268
|
+
let key = value;
|
249
269
|
|
250
|
-
|
251
|
-
|
252
|
-
|
270
|
+
// If the binding value is an object, it might contain `twoWay` or a specific `key`.
|
271
|
+
if (Neo.isObject(value)) {
|
272
|
+
if (value.twoWay) {
|
273
|
+
hasTwoWayBinding = true
|
253
274
|
}
|
275
|
+
key = value.key
|
254
276
|
}
|
255
|
-
})
|
256
|
-
}
|
257
277
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
descriptor, keyValue, newPath;
|
266
|
-
|
267
|
-
Object.entries(config).forEach(([key, value]) => {
|
268
|
-
if (!key.startsWith('_')) {
|
269
|
-
descriptor = Object.getOwnPropertyDescriptor(root, key);
|
270
|
-
newPath = `${path}.${key}`
|
271
|
-
|
272
|
-
if (!(typeof descriptor === 'object' && typeof descriptor.set === 'function')) {
|
273
|
-
keyValue = config[key];
|
274
|
-
me.createDataProperty(key, newPath, root);
|
275
|
-
root[key] = keyValue
|
276
|
-
}
|
277
|
-
|
278
|
-
if (Neo.isObject(value)) {
|
279
|
-
me.createDataProperties(config[key], newPath)
|
280
|
-
}
|
278
|
+
// Determine if it's a store binding or a data binding.
|
279
|
+
if (me.isStoreValue(key)) {
|
280
|
+
// For store bindings, resolve the store and assign it to the component config.
|
281
|
+
me.resolveStore(component, configKey, key.substring(7)) // remove the "stores." prefix
|
282
|
+
} else {
|
283
|
+
// For data bindings, create an Effect to keep the component config in sync with the data.
|
284
|
+
me.createBinding(component.id, configKey, key, value.twoWay)
|
281
285
|
}
|
282
|
-
})
|
286
|
+
});
|
287
|
+
|
288
|
+
// Mark the component if it has any two-way bindings, for internal tracking.
|
289
|
+
if (hasTwoWayBinding) {
|
290
|
+
component[twoWayBindingSymbol] = true
|
291
|
+
}
|
283
292
|
}
|
284
293
|
|
285
294
|
/**
|
286
|
-
*
|
287
|
-
* @param {String} path
|
288
|
-
* @param {Object} root=this.data
|
295
|
+
* Destroys the state provider and cleans up all associated effects.
|
289
296
|
*/
|
290
|
-
|
291
|
-
|
297
|
+
destroy() {
|
298
|
+
const me = this;
|
292
299
|
|
293
|
-
|
294
|
-
|
295
|
-
}
|
296
|
-
|
297
|
-
Object.defineProperty(root, key, {
|
298
|
-
get() {
|
299
|
-
let value = root['_' + key];
|
300
|
+
me.#formulaEffects.forEach(effect => effect.destroy());
|
301
|
+
me.#formulaEffects.clear();
|
300
302
|
|
301
|
-
|
302
|
-
|
303
|
-
}
|
303
|
+
me.#bindingEffects.forEach(effect => effect.destroy());
|
304
|
+
me.#bindingEffects.clear();
|
304
305
|
|
305
|
-
|
306
|
-
},
|
307
|
-
|
308
|
-
set(value) {
|
309
|
-
let _key = `_${key}`,
|
310
|
-
oldValue = root[_key];
|
311
|
-
|
312
|
-
if (!root[_key]) {
|
313
|
-
Object.defineProperty(root, _key, {
|
314
|
-
enumerable: false,
|
315
|
-
value,
|
316
|
-
writable : true
|
317
|
-
})
|
318
|
-
} else {
|
319
|
-
root[_key] = value
|
320
|
-
}
|
321
|
-
|
322
|
-
if (!Neo.isEqual(value, oldValue)) {
|
323
|
-
me.onDataPropertyChange(path ? path : key, value, oldValue)
|
324
|
-
}
|
325
|
-
}
|
326
|
-
})
|
306
|
+
super.destroy()
|
327
307
|
}
|
328
308
|
|
329
309
|
/**
|
@@ -338,133 +318,52 @@ class Provider extends Base {
|
|
338
318
|
/**
|
339
319
|
* Access the closest data property inside the parent chain.
|
340
320
|
* @param {String} key
|
341
|
-
* @param {Neo.state.Provider} originStateProvider=this for internal usage only
|
342
321
|
* @returns {*} value
|
343
322
|
*/
|
344
|
-
getData(key
|
345
|
-
|
346
|
-
data = me.getDataScope(key),
|
347
|
-
{scope} = data,
|
348
|
-
keyLeaf = data.key,
|
349
|
-
parentStateProvider;
|
350
|
-
|
351
|
-
if (scope?.hasOwnProperty(keyLeaf)) {
|
352
|
-
return scope[keyLeaf]
|
353
|
-
}
|
323
|
+
getData(key) {
|
324
|
+
const ownerDetails = this.getOwnerOfDataProperty(key);
|
354
325
|
|
355
|
-
|
356
|
-
|
357
|
-
if (!parentStateProvider) {
|
358
|
-
console.error(`data property '${key}' does not exist.`, originStateProvider)
|
326
|
+
if (ownerDetails) {
|
327
|
+
return ownerDetails.owner.getDataConfig(ownerDetails.propertyName).get()
|
359
328
|
}
|
360
|
-
|
361
|
-
return parentStateProvider.getData(key, originStateProvider)
|
362
329
|
}
|
363
330
|
|
364
331
|
/**
|
365
|
-
*
|
366
|
-
*
|
367
|
-
*
|
368
|
-
* and 'baz' as the key.
|
369
|
-
* @param key
|
370
|
-
* @returns {Object}
|
332
|
+
* Retrieves the underlying core.Config instance for a given data property path.
|
333
|
+
* @param {String} path The full path of the data property (e.g., 'user.firstname').
|
334
|
+
* @returns {Neo.core.Config|null}
|
371
335
|
*/
|
372
|
-
|
373
|
-
|
374
|
-
keyLeaf = key,
|
375
|
-
{data} = me;
|
376
|
-
|
377
|
-
if (key.includes('.')) {
|
378
|
-
key = key.split('.');
|
379
|
-
keyLeaf = key.pop();
|
380
|
-
data = Neo.ns(key.join('.'), false, data)
|
381
|
-
}
|
382
|
-
|
383
|
-
return {
|
384
|
-
key : keyLeaf,
|
385
|
-
scope: data
|
386
|
-
}
|
336
|
+
getDataConfig(path) {
|
337
|
+
return this.#dataConfigs[path] || null
|
387
338
|
}
|
388
339
|
|
389
340
|
/**
|
390
|
-
*
|
391
|
-
* @
|
341
|
+
* Returns the merged, hierarchical data object as a reactive Proxy.
|
342
|
+
* @returns {Proxy}
|
392
343
|
*/
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
if (Neo.isFunction(value)) {
|
397
|
-
value = value.toString()
|
398
|
-
}
|
399
|
-
|
400
|
-
if (environment === 'dist/esm' || environment === 'dist/production') {
|
401
|
-
// See: https://github.com/neomjs/neo/issues/2371
|
402
|
-
// Inside dist/esm & dist/prod the formatter:
|
403
|
-
// data => DateUtil.convertToyyyymmdd(data.currentDate)
|
404
|
-
// will get minified to:
|
405
|
-
// e=>s.Z.convertToyyyymmdd(e.currentDate)
|
406
|
-
// The new strategy: find the first variable name => "e"
|
407
|
-
// Replace it with "data":
|
408
|
-
// data=>s.Z.convertToyyyymmdd(data.currentDate)
|
409
|
-
// From there we can use the dev mode regex again.
|
410
|
-
|
411
|
-
let dataName = value.match(variableNameRegex)[0],
|
412
|
-
variableRegExp = new RegExp(`(^|[^\\w.])(${dataName})(?!\\w)`, 'g');
|
413
|
-
|
414
|
-
value = value.replace(variableRegExp, '$1data')
|
415
|
-
}
|
416
|
-
|
417
|
-
let dataVars = value.match(dataVariableRegex) || [],
|
418
|
-
result = [];
|
419
|
-
|
420
|
-
dataVars.forEach(variable => {
|
421
|
-
// remove the "data." at the start
|
422
|
-
variable = variable.substr(5);
|
423
|
-
NeoArray.add(result, variable)
|
424
|
-
});
|
425
|
-
|
426
|
-
result.sort();
|
427
|
-
|
428
|
-
return result
|
344
|
+
getHierarchyData() {
|
345
|
+
return createHierarchicalDataProxy(this)
|
429
346
|
}
|
430
347
|
|
431
348
|
/**
|
432
|
-
*
|
433
|
-
* @param {
|
434
|
-
* @returns {
|
349
|
+
* Finds the state.Provider instance that owns a specific data property.
|
350
|
+
* @param {String} path The full path of the data property.
|
351
|
+
* @returns {{owner: Neo.state.Provider, propertyName: String}|null}
|
435
352
|
*/
|
436
|
-
|
437
|
-
let me
|
438
|
-
parent = me.getParent();
|
353
|
+
getOwnerOfDataProperty(path) {
|
354
|
+
let me = this;
|
439
355
|
|
440
|
-
if (
|
441
|
-
return {
|
442
|
-
...parent.getHierarchyData(data),
|
443
|
-
...me.getPlainData()
|
444
|
-
}
|
356
|
+
if (me.#dataConfigs[path]) {
|
357
|
+
return {owner: me, propertyName: path}
|
445
358
|
}
|
446
359
|
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
* This excludes the property getters & setters.
|
453
|
-
* @param {Object} data=this.data
|
454
|
-
* @returns {Object}
|
455
|
-
*/
|
456
|
-
getPlainData(data=this.data) {
|
457
|
-
let plainData = {};
|
458
|
-
|
459
|
-
Object.entries(data).forEach(([key, value]) => {
|
460
|
-
if (Neo.typeOf(value) === 'Object') {
|
461
|
-
plainData[key] = this.getPlainData(value)
|
462
|
-
} else {
|
463
|
-
plainData[key] = value
|
464
|
-
}
|
465
|
-
});
|
360
|
+
// Check for parent ownership
|
361
|
+
const parent = me.getParent();
|
362
|
+
if (parent) {
|
363
|
+
return parent.getOwnerOfDataProperty(path)
|
364
|
+
}
|
466
365
|
|
467
|
-
return
|
366
|
+
return null
|
468
367
|
}
|
469
368
|
|
470
369
|
/**
|
@@ -472,13 +371,22 @@ class Provider extends Base {
|
|
472
371
|
* @returns {Neo.state.Provider|null}
|
473
372
|
*/
|
474
373
|
getParent() {
|
475
|
-
let
|
374
|
+
let me = this;
|
476
375
|
|
477
|
-
|
478
|
-
|
376
|
+
// Access the internal value of the parent_ config directly.
|
377
|
+
// This avoids recursive calls to the getter.
|
378
|
+
if (me._parent) {
|
379
|
+
return me._parent
|
380
|
+
}
|
381
|
+
|
382
|
+
// If no explicit parent is set, try to find it dynamically via the component.
|
383
|
+
// Ensure this.component exists before trying to access its parent.
|
384
|
+
if (me.component) {
|
385
|
+
return me.component.parent?.getStateProvider() || null
|
479
386
|
}
|
480
387
|
|
481
|
-
|
388
|
+
// No explicit parent and no component to derive it from.
|
389
|
+
return null
|
482
390
|
}
|
483
391
|
|
484
392
|
/**
|
@@ -506,48 +414,111 @@ class Provider extends Base {
|
|
506
414
|
}
|
507
415
|
|
508
416
|
/**
|
509
|
-
*
|
510
|
-
*
|
417
|
+
* Checks if any data property in the hierarchy starts with the given path.
|
418
|
+
* This is used by the HierarchicalDataProxy to determine if it should return a nested proxy.
|
419
|
+
* @param {String} path The path to check (e.g., 'user').
|
420
|
+
* @returns {Boolean}
|
421
|
+
*/
|
422
|
+
hasNestedDataStartingWith(path) {
|
423
|
+
const pathWithDot = `${path}.`;
|
424
|
+
|
425
|
+
if (Object.keys(this.#dataConfigs).some(key => key.startsWith(pathWithDot))) {
|
426
|
+
return true
|
427
|
+
}
|
428
|
+
|
429
|
+
return this.getParent()?.hasNestedDataStartingWith(path) || false
|
430
|
+
}
|
431
|
+
|
432
|
+
/**
|
433
|
+
* Returns the top-level data keys for a given path within this provider's data.
|
434
|
+
* @param {String} path The path to get keys for (e.g., 'user.address').
|
435
|
+
* @returns {String[]}
|
436
|
+
*/
|
437
|
+
getTopLevelDataKeys(path) {
|
438
|
+
const
|
439
|
+
keys = new Set(),
|
440
|
+
pathPrefix = path ? `${path}.` : '';
|
441
|
+
|
442
|
+
for (const fullPath in this.#dataConfigs) {
|
443
|
+
if (fullPath.startsWith(pathPrefix)) {
|
444
|
+
const
|
445
|
+
relativePath = fullPath.substring(pathPrefix.length),
|
446
|
+
topLevelKey = relativePath.split('.')[0];
|
447
|
+
|
448
|
+
if (topLevelKey) {
|
449
|
+
keys.add(topLevelKey)
|
450
|
+
}
|
451
|
+
}
|
452
|
+
}
|
453
|
+
|
454
|
+
return Array.from(keys)
|
455
|
+
}
|
456
|
+
|
457
|
+
/**
|
458
|
+
* This is the core method for setting data, providing a single entry point for all data modifications.
|
459
|
+
* It handles multiple scenarios:
|
460
|
+
* 1. **Object-based updates:** If `key` is an object, it recursively calls itself for each key-value pair.
|
461
|
+
* 2. **Data Records:** If `value` is a `Neo.data.Record`, it is treated as an atomic value and set directly.
|
462
|
+
* 3. **Bubbling Reactivity:** For a given key (e.g., 'user.name'), it sets the leaf value and then "bubbles up"
|
463
|
+
* the change, creating new parent objects (e.g., 'user') to ensure that effects depending on any part
|
464
|
+
* of the path are triggered.
|
511
465
|
*
|
512
|
-
*
|
513
|
-
*
|
514
|
-
*
|
515
|
-
* @param {Object|String} key
|
516
|
-
* @param {*} value
|
517
|
-
* @param {Neo.state.Provider} [originStateProvider]
|
466
|
+
* All updates are batched by the public `setData` methods to ensure effects run only once.
|
467
|
+
* Use `setData()` or `setDataAtSameLevel()` instead of calling this method directly.
|
468
|
+
*
|
469
|
+
* @param {Object|String} key The property to set, or an object of key-value pairs.
|
470
|
+
* @param {*} value The new value.
|
471
|
+
* @param {Neo.state.Provider} [originStateProvider] The provider to start the search from for hierarchical updates.
|
518
472
|
* @protected
|
519
473
|
*/
|
520
474
|
internalSetData(key, value, originStateProvider) {
|
521
|
-
|
522
|
-
data, keyLeaf, parentStateProvider, scope;
|
475
|
+
const me = this;
|
523
476
|
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
477
|
+
// If the value is a Neo.data.Record, treat it as an atomic value
|
478
|
+
// and set it directly without further recursive processing of its properties.
|
479
|
+
if (Neo.isRecord(value)) {
|
480
|
+
const
|
481
|
+
ownerDetails = me.getOwnerOfDataProperty(key),
|
482
|
+
targetProvider = ownerDetails ? ownerDetails.owner : (originStateProvider || me);
|
483
|
+
|
484
|
+
me.#setConfigValue(targetProvider, key, value, null);
|
485
|
+
return
|
486
|
+
}
|
487
|
+
|
488
|
+
if (Neo.isObject(key)) {
|
529
489
|
Object.entries(key).forEach(([dataKey, dataValue]) => {
|
530
490
|
me.internalSetData(dataKey, dataValue, originStateProvider)
|
531
|
-
})
|
532
|
-
|
533
|
-
|
534
|
-
keyLeaf = data.key;
|
535
|
-
scope = data.scope;
|
491
|
+
});
|
492
|
+
return
|
493
|
+
}
|
536
494
|
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
495
|
+
const
|
496
|
+
ownerDetails = me.getOwnerOfDataProperty(key),
|
497
|
+
targetProvider = ownerDetails ? ownerDetails.owner : (originStateProvider || me);
|
498
|
+
|
499
|
+
me.#setConfigValue(targetProvider, key, value, null);
|
500
|
+
|
501
|
+
// Bubble up the change to parent configs to trigger their effects
|
502
|
+
let path = key,
|
503
|
+
latestValue = value;
|
504
|
+
|
505
|
+
while (path.includes('.')) {
|
506
|
+
const leafKey = path.split('.').pop();
|
507
|
+
path = path.substring(0, path.lastIndexOf('.'));
|
508
|
+
|
509
|
+
const parentConfig = targetProvider.getDataConfig(path);
|
510
|
+
|
511
|
+
if (parentConfig) {
|
512
|
+
const oldParentValue = parentConfig.get();
|
513
|
+
if (Neo.isObject(oldParentValue)) {
|
514
|
+
const newParentValue = { ...oldParentValue, [leafKey]: latestValue };
|
515
|
+
parentConfig.set(newParentValue);
|
516
|
+
latestValue = newParentValue;
|
548
517
|
} else {
|
549
|
-
|
518
|
+
break // Stop if parent is not an object
|
550
519
|
}
|
520
|
+
} else {
|
521
|
+
break // Stop if parent config does not exist
|
551
522
|
}
|
552
523
|
}
|
553
524
|
}
|
@@ -562,17 +533,15 @@ class Provider extends Base {
|
|
562
533
|
}
|
563
534
|
|
564
535
|
/**
|
565
|
-
*
|
566
|
-
* @
|
567
|
-
* @param {Boolean} [preventOriginalConfig] True prevents the instance from getting an originalConfig property
|
568
|
-
* @returns {Object} config
|
536
|
+
* Gets called after all constructors & configs are applied.
|
537
|
+
* @protected
|
569
538
|
*/
|
570
|
-
|
571
|
-
|
572
|
-
config.data = Neo.merge(Neo.clone(this.constructor.config.data, true) || {}, config.data)
|
573
|
-
}
|
539
|
+
onConstructed() {
|
540
|
+
super.onConstructed();
|
574
541
|
|
575
|
-
|
542
|
+
// After the provider is fully constructed and initial data is set,
|
543
|
+
// run the formula effects for the first time to compute their initial values.
|
544
|
+
this.#formulaEffects.forEach(effect => effect.run())
|
576
545
|
}
|
577
546
|
|
578
547
|
/**
|
@@ -581,124 +550,34 @@ class Provider extends Base {
|
|
581
550
|
* @param {*} oldValue
|
582
551
|
*/
|
583
552
|
onDataPropertyChange(key, value, oldValue) {
|
584
|
-
|
585
|
-
binding = me.bindings && Neo.ns(key, false, me.bindings),
|
586
|
-
component, config, hierarchyData, stateProvider;
|
587
|
-
|
588
|
-
if (binding) {
|
589
|
-
hierarchyData = {};
|
590
|
-
|
591
|
-
Object.entries(binding).forEach(([componentId, configObject]) => {
|
592
|
-
component = Neo.getComponent(componentId) || Neo.get(componentId); // timing issue: the cmp might not be registered inside manager.Component yet
|
593
|
-
config = {};
|
594
|
-
stateProvider = component.getStateProvider() || me;
|
595
|
-
|
596
|
-
if (!hierarchyData[stateProvider.id]) {
|
597
|
-
hierarchyData[stateProvider.id] = stateProvider.getHierarchyData()
|
598
|
-
}
|
599
|
-
|
600
|
-
Object.entries(configObject).forEach(([configField, formatter]) => {
|
601
|
-
// we can not call me.callFormatter(), since a data property inside a parent stateProvider
|
602
|
-
// could have changed which is relying on data properties inside a closer stateProvider
|
603
|
-
config[configField] = stateProvider.callFormatter(formatter, hierarchyData[stateProvider.id])
|
604
|
-
});
|
605
|
-
|
606
|
-
component?.set(config)
|
607
|
-
})
|
608
|
-
}
|
609
|
-
|
610
|
-
me.formulas && me.resolveFormulas({key, id: me.id, oldValue, value});
|
611
|
-
|
612
|
-
me.fire('dataPropertyChange', {key, id: me.id, oldValue, value})
|
553
|
+
// Can be overridden by subclasses
|
613
554
|
}
|
614
555
|
|
615
556
|
/**
|
616
|
-
*
|
617
|
-
*
|
618
|
-
* @param {
|
619
|
-
|
620
|
-
|
621
|
-
let me = this,
|
622
|
-
config = {};
|
623
|
-
|
624
|
-
if (component.bind) {
|
625
|
-
me.createBindings(component);
|
626
|
-
|
627
|
-
Object.entries(component.bind).forEach(([key, value]) => {
|
628
|
-
if (Neo.isObject(value)) {
|
629
|
-
value.key = me.getFormatterVariables(value.value)[0];
|
630
|
-
value = value.value
|
631
|
-
}
|
632
|
-
|
633
|
-
if (me.isStoreValue(value)) {
|
634
|
-
me.resolveStore(component, key, value.substring(7)) // remove the "stores." at the start
|
635
|
-
} else {
|
636
|
-
config[key] = me.callFormatter(value)
|
637
|
-
}
|
638
|
-
});
|
639
|
-
|
640
|
-
component.set(config)
|
641
|
-
}
|
642
|
-
}
|
643
|
-
|
644
|
-
/**
|
645
|
-
* Removes all bindings for a given component id inside this stateProvider as well as inside all parent stateProviders.
|
646
|
-
* @param {String} componentId
|
557
|
+
* Recursively processes a data object, creating or updating Neo.core.Config instances
|
558
|
+
* for each property and storing them in the #dataConfigs map.
|
559
|
+
* @param {Object} obj The data object to process.
|
560
|
+
* @param {String} [path=''] The current path prefix for nested objects.
|
561
|
+
* @protected
|
647
562
|
*/
|
648
|
-
|
563
|
+
processDataObject(obj, path = '') {
|
649
564
|
let me = this;
|
650
565
|
|
651
|
-
Object.entries(
|
652
|
-
|
653
|
-
});
|
566
|
+
Object.entries(obj).forEach(([key, value]) => {
|
567
|
+
const fullPath = path ? `${path}.${key}` : key;
|
654
568
|
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
* @param {Object} data data from event or null on initial call
|
661
|
-
*/
|
662
|
-
resolveFormulas(data) {
|
663
|
-
let me = this,
|
664
|
-
{formulas} = me,
|
665
|
-
initialRun = !data,
|
666
|
-
affectFormula, bindObject, fn, key, result, value;
|
667
|
-
|
668
|
-
if (formulas) {
|
669
|
-
if (!initialRun && (!data.key || !data.value)) {
|
670
|
-
console.warn('[StateProvider:formulas] missing key or value', data.key, data.value)
|
569
|
+
// Ensure a Config instance exists for the current fullPath
|
570
|
+
if (me.#dataConfigs[fullPath]) {
|
571
|
+
me.#dataConfigs[fullPath].set(value)
|
572
|
+
} else {
|
573
|
+
me.#dataConfigs[fullPath] = new Config(value)
|
671
574
|
}
|
672
575
|
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
// Check if the change affects a formula
|
677
|
-
if (!initialRun) {
|
678
|
-
affectFormula = Object.values(value.bind).includes(data.key)
|
679
|
-
}
|
680
|
-
|
681
|
-
if (affectFormula) {
|
682
|
-
// Create Bind-Object and fill with new values
|
683
|
-
bindObject = Neo.clone(value.bind);
|
684
|
-
fn = value.get;
|
685
|
-
|
686
|
-
Object.keys(bindObject).forEach((key, index) => {
|
687
|
-
bindObject[key] = me.getData(bindObject[key])
|
688
|
-
});
|
689
|
-
|
690
|
-
// Calc the formula
|
691
|
-
result = fn(bindObject);
|
692
|
-
|
693
|
-
// Assign if no error or null
|
694
|
-
if (isNaN(result)) {
|
695
|
-
me.setData(key, null)
|
696
|
-
} else {
|
697
|
-
me.setData(key, result)
|
698
|
-
}
|
699
|
-
}
|
576
|
+
// If the value is a plain object, recursively process its properties
|
577
|
+
if (Neo.typeOf(value) === 'Object') {
|
578
|
+
me.processDataObject(value, fullPath)
|
700
579
|
}
|
701
|
-
}
|
580
|
+
});
|
702
581
|
}
|
703
582
|
|
704
583
|
/**
|
@@ -714,24 +593,64 @@ class Provider extends Base {
|
|
714
593
|
}
|
715
594
|
}
|
716
595
|
|
596
|
+
/**
|
597
|
+
* Helper function to set a config value and trigger reactivity.
|
598
|
+
* This method creates a new Config instance if one doesn't exist for the given path,
|
599
|
+
* or updates an existing one. It also triggers binding effects and calls onDataPropertyChange.
|
600
|
+
* @param {Neo.state.Provider} provider The StateProvider instance owning the config.
|
601
|
+
* @param {String} path The full path of the data property (e.g., 'user.firstname').
|
602
|
+
* @param {*} newValue The new value to set.
|
603
|
+
* @param {*} oldVal The old value (optional, used for initial setup).
|
604
|
+
* @private
|
605
|
+
*/
|
606
|
+
#setConfigValue(provider, path, newValue, oldVal) {
|
607
|
+
let currentConfig = provider.getDataConfig(path),
|
608
|
+
oldValue = oldVal;
|
609
|
+
|
610
|
+
if (currentConfig) {
|
611
|
+
oldValue = currentConfig.get();
|
612
|
+
currentConfig.set(newValue);
|
613
|
+
} else {
|
614
|
+
currentConfig = new Config(newValue);
|
615
|
+
provider.#dataConfigs[path] = currentConfig;
|
616
|
+
// Trigger all binding effects to re-evaluate their dependencies
|
617
|
+
provider.#bindingEffects.forEach(effect => effect.run())
|
618
|
+
}
|
619
|
+
|
620
|
+
// Notify subscribers of the data property change.
|
621
|
+
provider.onDataPropertyChange(path, newValue, oldValue)
|
622
|
+
}
|
623
|
+
|
717
624
|
/**
|
718
625
|
* The method will assign all values to the closest stateProvider where it finds an existing key.
|
719
626
|
* In case no match is found inside the parent chain, a new data property will get generated.
|
627
|
+
*
|
628
|
+
* All updates within a single call are batched to ensure that reactive effects (bindings and formulas)
|
629
|
+
* are run only once.
|
630
|
+
*
|
720
631
|
* @param {Object|String} key
|
721
632
|
* @param {*} value
|
722
633
|
*/
|
723
634
|
setData(key, value) {
|
724
|
-
|
635
|
+
EffectBatchManager.startBatch();
|
636
|
+
this.internalSetData(key, value, this);
|
637
|
+
EffectBatchManager.endBatch()
|
725
638
|
}
|
726
639
|
|
727
640
|
/**
|
728
641
|
* Use this method instead of setData() in case you want to enforce
|
729
642
|
* setting all keys on this instance instead of looking for matches inside parent stateProviders.
|
643
|
+
*
|
644
|
+
* All updates within a single call are batched to ensure that reactive effects (bindings and formulas)
|
645
|
+
* are run only once.
|
646
|
+
*
|
730
647
|
* @param {Object|String} key
|
731
648
|
* @param {*} value
|
732
649
|
*/
|
733
650
|
setDataAtSameLevel(key, value) {
|
734
|
-
|
651
|
+
EffectBatchManager.startBatch();
|
652
|
+
this.internalSetData(key, value);
|
653
|
+
EffectBatchManager.endBatch()
|
735
654
|
}
|
736
655
|
}
|
737
656
|
|