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/Neo.mjs
CHANGED
@@ -1,18 +1,46 @@
|
|
1
|
-
import DefaultConfig
|
2
|
-
import {isDescriptor}
|
1
|
+
import DefaultConfig from './DefaultConfig.mjs';
|
2
|
+
import {isDescriptor} from './core/ConfigSymbols.mjs';
|
3
3
|
|
4
4
|
const
|
5
5
|
camelRegex = /-./g,
|
6
6
|
configSymbol = Symbol.for('configSymbol'),
|
7
7
|
getSetCache = Symbol('getSetCache'),
|
8
|
+
cloneMap = {
|
9
|
+
Array(obj, deep, ignoreNeoInstances) {
|
10
|
+
return !deep ? [...obj] : [...obj.map(val => Neo.clone(val, deep, ignoreNeoInstances))]
|
11
|
+
},
|
12
|
+
Date(obj) {
|
13
|
+
return new Date(obj.valueOf())
|
14
|
+
},
|
15
|
+
Map(obj) {
|
16
|
+
return new Map(obj) // shallow copy
|
17
|
+
},
|
18
|
+
NeoInstance(obj, ignoreNeoInstances) {
|
19
|
+
return ignoreNeoInstances ? obj : Neo.cloneNeoInstance(obj)
|
20
|
+
},
|
21
|
+
Set(obj) {
|
22
|
+
return new Set(obj)
|
23
|
+
},
|
24
|
+
Object(obj, deep, ignoreNeoInstances) {
|
25
|
+
const out = {};
|
26
|
+
|
27
|
+
// Use Reflect.ownKeys() to include symbol properties (e.g., for config descriptors)
|
28
|
+
Reflect.ownKeys(obj).forEach(key => {
|
29
|
+
const value = obj[key];
|
30
|
+
out[key] = !deep ? value : Neo.clone(value, deep, ignoreNeoInstances)
|
31
|
+
});
|
32
|
+
|
33
|
+
return out
|
34
|
+
}
|
35
|
+
},
|
8
36
|
typeDetector = {
|
9
37
|
function: item => {
|
10
|
-
if (item.prototype?.constructor
|
38
|
+
if (item.prototype?.constructor?.isClass) {
|
11
39
|
return 'NeoClass'
|
12
40
|
}
|
13
41
|
},
|
14
42
|
object: item => {
|
15
|
-
if (item.constructor
|
43
|
+
if (item.constructor?.isClass && item instanceof Neo.core.Base) {
|
16
44
|
return 'NeoInstance'
|
17
45
|
}
|
18
46
|
}
|
@@ -23,7 +51,6 @@ const
|
|
23
51
|
* @module Neo
|
24
52
|
* @singleton
|
25
53
|
* @borrows Neo.core.Util.bindMethods as bindMethods
|
26
|
-
* @borrows Neo.core.Util.capitalize as capitalize
|
27
54
|
* @borrows Neo.core.Util.createStyleObject as createStyleObject
|
28
55
|
* @borrows Neo.core.Util.createStyles as createStyles
|
29
56
|
* @borrows Neo.core.Util.decamel as decamel
|
@@ -178,25 +205,7 @@ Neo = globalThis.Neo = Object.assign({
|
|
178
205
|
* @returns {Object|Array|*} the cloned input
|
179
206
|
*/
|
180
207
|
clone(obj, deep=false, ignoreNeoInstances=false) {
|
181
|
-
|
182
|
-
|
183
|
-
return {
|
184
|
-
Array : () => !deep ? [...obj] : [...obj.map(val => Neo.clone(val, deep, ignoreNeoInstances))],
|
185
|
-
Date : () => new Date(obj.valueOf()),
|
186
|
-
Map : () => new Map(obj), // shallow copy
|
187
|
-
NeoInstance: () => ignoreNeoInstances ? obj : this.cloneNeoInstance(obj),
|
188
|
-
Set : () => new Set(obj),
|
189
|
-
|
190
|
-
Object: () => {
|
191
|
-
out = {};
|
192
|
-
|
193
|
-
Object.entries(obj).forEach(([key, value]) => {
|
194
|
-
out[key] = !deep ? value : Neo.clone(value, deep, ignoreNeoInstances)
|
195
|
-
});
|
196
|
-
|
197
|
-
return out
|
198
|
-
}
|
199
|
-
}[Neo.typeOf(obj)]?.() || obj
|
208
|
+
return cloneMap[Neo.typeOf(obj)]?.(obj, deep, ignoreNeoInstances) || obj
|
200
209
|
},
|
201
210
|
|
202
211
|
/**
|
@@ -264,7 +273,7 @@ Neo = globalThis.Neo = Object.assign({
|
|
264
273
|
return null
|
265
274
|
}
|
266
275
|
|
267
|
-
className = config.className || config.module.prototype.className
|
276
|
+
className = config.className || config.module.prototype.className
|
268
277
|
}
|
269
278
|
|
270
279
|
if (!exists(className)) {
|
@@ -284,11 +293,195 @@ Neo = globalThis.Neo = Object.assign({
|
|
284
293
|
return instance
|
285
294
|
},
|
286
295
|
|
296
|
+
/**
|
297
|
+
* Defines a reactive configuration property on a target object (prototype or instance).
|
298
|
+
* This method creates getters and setters that fully participate in Neo.mjs's reactive config system,
|
299
|
+
* including lifecycle hooks.
|
300
|
+
*
|
301
|
+
* @param {Neo.core.Base} target - The instance or prototype on which to define the config.
|
302
|
+
* @param {String} key - The name of the config property (without the '_' suffix).
|
303
|
+
* @param {*} [initialValue] - The initial value for the config.
|
304
|
+
*/
|
305
|
+
createConfig(target, key, initialValue) {
|
306
|
+
if (Neo.hasPropertySetter(target, key)) {
|
307
|
+
throw(
|
308
|
+
`Invalid config in ${target.className}: '${key}_'. The config '${key}' is already defined as reactive by a parent class.
|
309
|
+
To override the default value, use '${key}' (without the underscore) in your static config.
|
310
|
+
If you intended to create custom logic, use the 'beforeGet${Neo.capitalize(key)}()', 'beforeSet${Neo.capitalize(key)}()', and 'afterSet${Neo.capitalize(key)}()' hooks instead of redefining the config.`
|
311
|
+
)
|
312
|
+
}
|
313
|
+
|
314
|
+
const
|
315
|
+
_key = '_' + key,
|
316
|
+
uKey = key[0].toUpperCase() + key.slice(1),
|
317
|
+
beforeGet = 'beforeGet' + uKey,
|
318
|
+
beforeSet = 'beforeSet' + uKey,
|
319
|
+
afterSet = 'afterSet' + uKey;
|
320
|
+
|
321
|
+
Neo[getSetCache] ??= {};
|
322
|
+
|
323
|
+
if (!Neo[getSetCache][key]) {
|
324
|
+
// Public Descriptor
|
325
|
+
Neo[getSetCache][key] = {
|
326
|
+
get() {
|
327
|
+
let me = this,
|
328
|
+
config = me.getConfig(key),
|
329
|
+
hasNewKey = Object.hasOwn(me[configSymbol], key),
|
330
|
+
newKey = me[configSymbol][key],
|
331
|
+
value = hasNewKey ? newKey : me[_key];
|
332
|
+
|
333
|
+
if (value instanceof Date) {
|
334
|
+
value = new Date(value.valueOf());
|
335
|
+
}
|
336
|
+
// new, explicit opt-in path
|
337
|
+
else if (config.cloneOnGet) {
|
338
|
+
const {cloneOnGet} = config;
|
339
|
+
|
340
|
+
if (cloneOnGet === 'deep') {
|
341
|
+
value = Neo.clone(value, true, true);
|
342
|
+
} else if (cloneOnGet === 'shallow') {
|
343
|
+
const type = Neo.typeOf(value);
|
344
|
+
|
345
|
+
if (type === 'Array') {
|
346
|
+
value = [...value];
|
347
|
+
} else if (type === 'Object') {
|
348
|
+
value = {...value};
|
349
|
+
}
|
350
|
+
}
|
351
|
+
}
|
352
|
+
// legacy behavior
|
353
|
+
else if (Array.isArray(value)) {
|
354
|
+
value = [...value];
|
355
|
+
}
|
356
|
+
|
357
|
+
if (hasNewKey) {
|
358
|
+
me[key] = value; // We do want to trigger the setter => beforeSet, afterSet
|
359
|
+
value = me[_key]; // Return the value parsed by the setter
|
360
|
+
delete me[configSymbol][key]
|
361
|
+
}
|
362
|
+
|
363
|
+
if (typeof me[beforeGet] === 'function') {
|
364
|
+
value = me[beforeGet](value)
|
365
|
+
}
|
366
|
+
|
367
|
+
return value
|
368
|
+
},
|
369
|
+
set(value) {
|
370
|
+
if (value === undefined) return;
|
371
|
+
|
372
|
+
const config = this.getConfig(key);
|
373
|
+
if (!config) return;
|
374
|
+
|
375
|
+
let me = this,
|
376
|
+
oldValue = config.get(), // Get the old value from the Config instance
|
377
|
+
{EffectBatchManager} = Neo.core,
|
378
|
+
isNewBatch = !EffectBatchManager?.isBatchActive();
|
379
|
+
|
380
|
+
// If a config change is not triggered via `core.Base#set()`, honor changes inside hooks.
|
381
|
+
isNewBatch && EffectBatchManager?.startBatch();
|
382
|
+
|
383
|
+
// 1. Prevent infinite loops:
|
384
|
+
// Immediately remove the pending value from the configSymbol to prevent a getter from
|
385
|
+
// recursively re-triggering this setter.
|
386
|
+
delete me[configSymbol][key];
|
387
|
+
|
388
|
+
switch (config.clone) {
|
389
|
+
case 'deep':
|
390
|
+
value = Neo.clone(value, true, true);
|
391
|
+
break;
|
392
|
+
case 'shallow':
|
393
|
+
value = Neo.clone(value, false, true);
|
394
|
+
break;
|
395
|
+
}
|
396
|
+
|
397
|
+
// 2. Create a temporary state for beforeSet hooks:
|
398
|
+
// Set the new value directly on the private backing property. This allows any beforeSet
|
399
|
+
// hook to access the new value of this and other configs within the same `set()` call.
|
400
|
+
me[_key] = value;
|
401
|
+
|
402
|
+
if (typeof me[beforeSet] === 'function') {
|
403
|
+
value = me[beforeSet](value, oldValue);
|
404
|
+
|
405
|
+
// If they don't return a value, that means no change
|
406
|
+
if (value === undefined) {
|
407
|
+
// Restore the original value if the update is canceled.
|
408
|
+
me[_key] = oldValue;
|
409
|
+
isNewBatch && EffectBatchManager?.endBatch();
|
410
|
+
return
|
411
|
+
}
|
412
|
+
}
|
413
|
+
|
414
|
+
// 3. Restore state for change detection:
|
415
|
+
// Revert the private backing property to its original value. This is crucial for the
|
416
|
+
// `config.set()` method to correctly detect if the value has actually changed.
|
417
|
+
me[_key] = oldValue;
|
418
|
+
|
419
|
+
// 4. Finalize the change:
|
420
|
+
// The config.set() method performs the final check and, if the value changed,
|
421
|
+
// triggers afterSet hooks and notifies subscribers.
|
422
|
+
if (config.set(value)) {
|
423
|
+
me[afterSet]?.(value, oldValue);
|
424
|
+
me.afterSetConfig?.(key, value, oldValue)
|
425
|
+
}
|
426
|
+
|
427
|
+
isNewBatch && EffectBatchManager?.endBatch()
|
428
|
+
}
|
429
|
+
};
|
430
|
+
|
431
|
+
// Private Descriptor
|
432
|
+
Neo[getSetCache][_key] = {
|
433
|
+
get() {
|
434
|
+
return this.getConfig(key)?.get()
|
435
|
+
},
|
436
|
+
set(value) {
|
437
|
+
this.getConfig(key)?.setRaw(value)
|
438
|
+
}
|
439
|
+
}
|
440
|
+
}
|
441
|
+
|
442
|
+
Object.defineProperty(target, key, Neo[getSetCache][key]);
|
443
|
+
Object.defineProperty(target, _key, Neo[getSetCache][_key]);
|
444
|
+
|
445
|
+
if (initialValue !== undefined) {
|
446
|
+
target[key] = initialValue
|
447
|
+
}
|
448
|
+
},
|
449
|
+
|
287
450
|
/**
|
288
451
|
*
|
289
452
|
*/
|
290
453
|
emptyFn() {},
|
291
454
|
|
455
|
+
/**
|
456
|
+
* Ensures a class is assigned to the Neo namespace only once, preventing duplicates.
|
457
|
+
* This is a lightweight version of `Neo.setupClass` for simple classes
|
458
|
+
* that do not extend `Neo.core.Base`.
|
459
|
+
* It follows a "first one wins" strategy.
|
460
|
+
*
|
461
|
+
* @param {Function|Object} module - The class constructor or singleton object to register.
|
462
|
+
* @param {String} classPath - The fully qualified name (e.g., 'Neo.core.Config').
|
463
|
+
* @param {Function} [onFirst] - An optional callback that runs only the first time the class is registered.
|
464
|
+
* @returns {Function|Object} The class or singleton from the Neo namespace (either the existing one or the newly set one).
|
465
|
+
*/
|
466
|
+
gatekeep(module, classPath, onFirst) {
|
467
|
+
const existingClass = Neo.ns(classPath, false);
|
468
|
+
|
469
|
+
if (existingClass) {
|
470
|
+
return existingClass
|
471
|
+
}
|
472
|
+
|
473
|
+
const
|
474
|
+
nsArray = classPath.split('.'),
|
475
|
+
className = nsArray.pop(),
|
476
|
+
parentNs = Neo.ns(nsArray, true);
|
477
|
+
|
478
|
+
parentNs[className] = module;
|
479
|
+
|
480
|
+
onFirst?.(module);
|
481
|
+
|
482
|
+
return parentNs[className]
|
483
|
+
},
|
484
|
+
|
292
485
|
/**
|
293
486
|
* Checks if there is a set method for a given property key inside the prototype chain
|
294
487
|
* @memberOf module:Neo
|
@@ -325,6 +518,10 @@ Neo = globalThis.Neo = Object.assign({
|
|
325
518
|
return Neo.merge(Neo.merge(target, defaults), source)
|
326
519
|
}
|
327
520
|
|
521
|
+
if (!target) {
|
522
|
+
return source
|
523
|
+
}
|
524
|
+
|
328
525
|
for (const key in source) {
|
329
526
|
const value = source[key];
|
330
527
|
|
@@ -338,6 +535,35 @@ Neo = globalThis.Neo = Object.assign({
|
|
338
535
|
return target
|
339
536
|
},
|
340
537
|
|
538
|
+
/**
|
539
|
+
* Merges a new value into an existing config value based on a specified strategy.
|
540
|
+
* This method is used during instance creation to apply merge strategies defined in config descriptors.
|
541
|
+
* @param {any} defaultValue - The default value of the config (from static config).
|
542
|
+
* @param {any} instanceValue - The value provided during instance creation.
|
543
|
+
* @param {string|Function} strategy - The merge strategy: 'shallow', 'deep', 'replace', or a custom function.
|
544
|
+
* @returns {any} The merged value.
|
545
|
+
*/
|
546
|
+
mergeConfig(defaultValue, instanceValue, strategy) {
|
547
|
+
const
|
548
|
+
defaultValueType = Neo.typeOf(defaultValue),
|
549
|
+
instanceValueType = Neo.typeOf(instanceValue);
|
550
|
+
|
551
|
+
if (strategy === 'shallow') {
|
552
|
+
if (defaultValueType === 'Object' && instanceValueType === 'Object') {
|
553
|
+
return {...defaultValue, ...instanceValue}
|
554
|
+
}
|
555
|
+
} else if (strategy === 'deep') {
|
556
|
+
if (defaultValueType === 'Object' && instanceValueType === 'Object') {
|
557
|
+
return Neo.merge(Neo.clone(defaultValue, true), instanceValue)
|
558
|
+
}
|
559
|
+
} else if (typeof strategy === 'function') {
|
560
|
+
return strategy(defaultValue, instanceValue)
|
561
|
+
}
|
562
|
+
|
563
|
+
// Default to 'replace' or if strategy is not recognized
|
564
|
+
return instanceValue
|
565
|
+
},
|
566
|
+
|
341
567
|
/**
|
342
568
|
* Maps a className string into a given or global namespace
|
343
569
|
* @example
|
@@ -438,36 +664,49 @@ Neo = globalThis.Neo = Object.assign({
|
|
438
664
|
},
|
439
665
|
|
440
666
|
/**
|
441
|
-
*
|
442
|
-
*
|
443
|
-
* This is the unified entry point for changing global framework configurations.
|
444
|
-
* The framework automatically handles the complex multi-threaded and multi-window
|
445
|
-
* synchronization (via App Workers and Shared Workers, if active), ensuring
|
446
|
-
* consistency across the entire application without boilerplate.
|
667
|
+
* This is the final and most critical step in the Neo.mjs class creation process.
|
668
|
+
* It is called at the end of every class module definition.
|
447
669
|
*
|
448
|
-
*
|
449
|
-
*
|
670
|
+
* `setupClass` performs several key operations:
|
671
|
+
* 1. **Mixed-Environment Gatekeeper:** It first checks if the class's namespace already exists.
|
672
|
+
* If it does, it immediately returns the existing class. This is the crucial "first comes wins"
|
673
|
+
* strategy that enables Neo.mjs to safely combine environments. For example, a bundled
|
674
|
+
* `dist/production` app can dynamically load an unbundled module from `dist/esm` at runtime.
|
675
|
+
* If that module imports a class already present in the main bundle, this check ensures the
|
676
|
+
* original, bundled class is used, preventing conflicts and maintaining application integrity.
|
677
|
+
* 1. **Configuration Merging:** It traverses the prototype chain to merge `static config`
|
678
|
+
* objects from parent classes into the current class, creating a unified `config`.
|
679
|
+
* 2. **Applying Overwrites:** It calls the static `applyOverwrites()` method on the class,
|
680
|
+
* allowing the global `Neo.overwrites` object to modify the class's default prototype
|
681
|
+
* configs. This is a key mechanism for external theming and configuration.
|
682
|
+
* 3. **Reactive Getter/Setter Generation:** For any config ending with an underscore (e.g., `myConfig_`),
|
683
|
+
* it automatically generates the corresponding public getter and setter. This enables optional
|
684
|
+
* lifecycle hooks that are called automatically if implemented on the class:
|
685
|
+
* - `beforeGetMyConfig(value)`
|
686
|
+
* - `beforeSetMyConfig(newValue, oldValue)`
|
687
|
+
* - `afterSetMyConfig(newValue, oldValue)`
|
688
|
+
* 4. **Prototype-based Configs:** Non-reactive configs (without an underscore) are set
|
689
|
+
* directly on the prototype for memory efficiency.
|
690
|
+
* 5. **Mixin Application:** It processes the `mixins` config to blend in functionality from
|
691
|
+
* other classes.
|
692
|
+
* 6. **Namespace Registration:** It registers the class in the global `Neo` namespace.
|
693
|
+
* 7. **Singleton Instantiation:** If the class is configured as a singleton, it creates the
|
694
|
+
* single instance.
|
450
695
|
*
|
451
696
|
* @memberOf module:Neo
|
452
|
-
* @function setGlobalConfig
|
453
|
-
* @param {Object} config The partial or full Neo.config object with changes to apply.
|
454
|
-
*/
|
455
|
-
|
456
|
-
/**
|
457
|
-
* Internally used at the end of each class / module definition
|
458
|
-
* @memberOf module:Neo
|
459
697
|
* @template T
|
460
|
-
* @param {T} cls
|
461
|
-
* @returns {T}
|
698
|
+
* @param {T} cls The class constructor to process.
|
699
|
+
* @returns {T} The processed and finalized class constructor or singleton instance.
|
462
700
|
*/
|
463
701
|
setupClass(cls) {
|
464
|
-
let
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
702
|
+
let baseConfig = null,
|
703
|
+
baseConfigDescriptors = null,
|
704
|
+
ntypeChain = [],
|
705
|
+
{ntypeMap} = Neo,
|
706
|
+
proto = cls.prototype || cls,
|
707
|
+
ns = Neo.ns(proto.constructor.config.className, false),
|
708
|
+
protos = [],
|
709
|
+
cfg, config, configDescriptors, ctor, hierarchyInfo, ntype;
|
471
710
|
|
472
711
|
/*
|
473
712
|
* If the namespace already exists, directly return it.
|
@@ -481,12 +720,16 @@ Neo = globalThis.Neo = Object.assign({
|
|
481
720
|
return ns
|
482
721
|
}
|
483
722
|
|
723
|
+
// Traverse the prototype chain to collect inherited configs and descriptors
|
484
724
|
while (proto.__proto__) {
|
485
725
|
ctor = proto.constructor;
|
486
726
|
|
727
|
+
// If a class in the prototype chain has already had its config applied,
|
728
|
+
// we can use its pre-processed config and descriptors as a base.
|
487
729
|
if (Object.hasOwn(ctor, 'classConfigApplied')) {
|
488
|
-
|
489
|
-
|
730
|
+
baseConfig = Neo.clone(ctor.config, true);
|
731
|
+
baseConfigDescriptors = Neo.clone(ctor.configDescriptors, true);
|
732
|
+
ntypeChain = [...ctor.ntypeChain];
|
490
733
|
break
|
491
734
|
}
|
492
735
|
|
@@ -494,29 +737,43 @@ Neo = globalThis.Neo = Object.assign({
|
|
494
737
|
proto = proto.__proto__
|
495
738
|
}
|
496
739
|
|
497
|
-
|
740
|
+
// Initialize accumulated config and descriptors
|
741
|
+
config = baseConfig || {};
|
742
|
+
configDescriptors = baseConfigDescriptors || {};
|
498
743
|
|
744
|
+
// Process each class in the prototype chain (from top to bottom)
|
499
745
|
protos.forEach(element => {
|
500
746
|
let mixins;
|
501
747
|
|
502
748
|
ctor = element.constructor;
|
503
|
-
|
504
|
-
cfg = ctor.config || {};
|
749
|
+
cfg = ctor.config || {};
|
505
750
|
|
506
751
|
if (Neo.overwrites) {
|
507
752
|
ctor.applyOverwrites?.(cfg)
|
508
753
|
}
|
509
754
|
|
755
|
+
// Process each config property defined in the current class's static config
|
510
756
|
Object.entries(cfg).forEach(([key, value]) => {
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
757
|
+
const
|
758
|
+
isReactive = key.slice(-1) === '_',
|
759
|
+
baseKey = isReactive ? key.slice(0, -1) : key;
|
760
|
+
|
761
|
+
// 1. Handle descriptors: If the value is a descriptor object, store it.
|
762
|
+
// The 'value' property of the descriptor is then used as the actual config value.
|
763
|
+
if (Neo.isObject(value) && value[isDescriptor] === true) {
|
764
|
+
ctor.configDescriptors ??= {};
|
765
|
+
ctor.configDescriptors[baseKey] = Neo.clone(value, true); // Deep clone to prevent mutation
|
766
|
+
value = value.value // Use the descriptor's value as the config value
|
516
767
|
}
|
517
768
|
|
518
|
-
//
|
519
|
-
|
769
|
+
// 2. Handle reactive vs. non-reactive configs: Generate getters/setters for reactive configs.
|
770
|
+
if (isReactive) {
|
771
|
+
delete cfg[key]; // Remove original key with underscore
|
772
|
+
cfg[baseKey] = value; // Use the potentially modified value
|
773
|
+
Neo.createConfig(element, baseKey)
|
774
|
+
}
|
775
|
+
// This part handles non-reactive configs (including those that were descriptors)
|
776
|
+
// If no property setter exists, define it directly on the prototype.
|
520
777
|
else if (!Neo.hasPropertySetter(element, key)) {
|
521
778
|
Object.defineProperty(element, key, {
|
522
779
|
enumerable: true,
|
@@ -526,6 +783,17 @@ Neo = globalThis.Neo = Object.assign({
|
|
526
783
|
}
|
527
784
|
});
|
528
785
|
|
786
|
+
// Merge configDescriptors: Apply "first-defined wins" strategy.
|
787
|
+
// If a descriptor for a key already exists (from a parent class), it is not overwritten.
|
788
|
+
if (ctor.configDescriptors) {
|
789
|
+
for (const key in ctor.configDescriptors) {
|
790
|
+
if (!Object.hasOwn(configDescriptors, key)) {
|
791
|
+
configDescriptors[key] = Neo.clone(ctor.configDescriptors[key], true) // Deep clone for immutability
|
792
|
+
}
|
793
|
+
}
|
794
|
+
}
|
795
|
+
|
796
|
+
// Process ntype and ntypeChain
|
529
797
|
if (Object.hasOwn(cfg, 'ntype')) {
|
530
798
|
ntype = cfg.ntype;
|
531
799
|
|
@@ -540,6 +808,7 @@ Neo = globalThis.Neo = Object.assign({
|
|
540
808
|
ntypeMap[ntype] = cfg.className
|
541
809
|
}
|
542
810
|
|
811
|
+
// Process mixins
|
543
812
|
mixins = Object.hasOwn(config, 'mixins') && config.mixins || [];
|
544
813
|
|
545
814
|
if (ctor.observable) {
|
@@ -551,7 +820,7 @@ Neo = globalThis.Neo = Object.assign({
|
|
551
820
|
}
|
552
821
|
|
553
822
|
if (mixins.length > 0) {
|
554
|
-
applyMixins(ctor, mixins);
|
823
|
+
applyMixins(ctor, mixins, cfg);
|
555
824
|
|
556
825
|
if (Neo.ns('Neo.core.Observable', false, ctor.prototype.mixins)) {
|
557
826
|
ctor.observable = true
|
@@ -561,29 +830,45 @@ Neo = globalThis.Neo = Object.assign({
|
|
561
830
|
delete cfg.mixins;
|
562
831
|
delete config.mixins;
|
563
832
|
|
564
|
-
|
833
|
+
// Hierarchical merging of static config values based on descriptors.
|
834
|
+
// This ensures that values are merged (e.g., shallow/deep) instead of simply overwritten.
|
835
|
+
Object.entries(cfg).forEach(([key, value]) => {
|
836
|
+
const descriptor = configDescriptors[key];
|
565
837
|
|
838
|
+
if (descriptor?.merge) {
|
839
|
+
config[key] = Neo.mergeConfig(config[key], value, descriptor.merge)
|
840
|
+
} else {
|
841
|
+
config[key] = value
|
842
|
+
}
|
843
|
+
});
|
844
|
+
|
845
|
+
// Assign final processed config and descriptors to the class constructor
|
566
846
|
Object.assign(ctor, {
|
567
847
|
classConfigApplied: true,
|
568
|
-
config : Neo.clone(config,
|
848
|
+
config : Neo.clone(config, true), // Deep clone final config for immutability
|
849
|
+
configDescriptors : Neo.clone(configDescriptors, true), // Deep clone final descriptors for immutability
|
569
850
|
isClass : true,
|
570
851
|
ntypeChain
|
571
852
|
});
|
572
853
|
|
854
|
+
// Apply to global namespace if not a singleton
|
573
855
|
!config.singleton && this.applyToGlobalNs(cls)
|
574
856
|
});
|
575
857
|
|
576
858
|
proto = cls.prototype || cls;
|
577
859
|
|
860
|
+
// Add is<Ntype> flags to the prototype
|
578
861
|
ntypeChain.forEach(ntype => {
|
579
862
|
proto[`is${Neo.capitalize(Neo.camel(ntype))}`] = true
|
580
863
|
});
|
581
864
|
|
865
|
+
// If it's a singleton, create and apply the instance to the global namespace
|
582
866
|
if (proto.singleton) {
|
583
867
|
cls = Neo.create(cls);
|
584
868
|
Neo.applyToGlobalNs(cls)
|
585
869
|
}
|
586
870
|
|
871
|
+
// Add class hierarchy information to the manager or a temporary map
|
587
872
|
hierarchyInfo = {
|
588
873
|
className : proto.className,
|
589
874
|
module : cls,
|
@@ -606,11 +891,12 @@ Neo = globalThis.Neo = Object.assign({
|
|
606
891
|
* @returns {String|null}
|
607
892
|
*/
|
608
893
|
typeOf(item) {
|
609
|
-
|
894
|
+
// Return null for null or undefined
|
895
|
+
if (item == null) {
|
610
896
|
return null
|
611
897
|
}
|
612
898
|
|
613
|
-
return typeDetector[typeof item]?.(item) || item.constructor
|
899
|
+
return typeDetector[typeof item]?.(item) || item.constructor?.name
|
614
900
|
}
|
615
901
|
}, Neo);
|
616
902
|
|
@@ -624,6 +910,7 @@ const ignoreMixin = [
|
|
624
910
|
'classConfigApplied',
|
625
911
|
'className',
|
626
912
|
'constructor',
|
913
|
+
'id',
|
627
914
|
'isClass',
|
628
915
|
'mixin',
|
629
916
|
'ntype',
|
@@ -636,9 +923,10 @@ const ignoreMixin = [
|
|
636
923
|
/**
|
637
924
|
* @param {Neo.core.Base} cls
|
638
925
|
* @param {Array} mixins
|
926
|
+
* @param {Object} classConfig
|
639
927
|
* @private
|
640
928
|
*/
|
641
|
-
function applyMixins(cls, mixins) {
|
929
|
+
function applyMixins(cls, mixins, classConfig) {
|
642
930
|
if (!Array.isArray(mixins)) {
|
643
931
|
mixins = [mixins];
|
644
932
|
}
|
@@ -660,119 +948,17 @@ function applyMixins(cls, mixins) {
|
|
660
948
|
}
|
661
949
|
|
662
950
|
mixinCls = Neo.ns(mixin);
|
663
|
-
mixinProto = mixinCls.prototype
|
951
|
+
mixinProto = mixinCls.prototype
|
664
952
|
}
|
665
953
|
|
666
954
|
mixinProto.className.split('.').reduce(mixReduce(mixinCls), mixinClasses);
|
667
955
|
|
668
|
-
Object.
|
956
|
+
Object.entries(Object.getOwnPropertyDescriptors(mixinProto)).forEach(mixinProperty(cls.prototype, mixinProto, classConfig))
|
669
957
|
}
|
670
958
|
|
671
959
|
cls.prototype.mixins = mixinClasses // todo: we should do a deep merge
|
672
960
|
}
|
673
961
|
|
674
|
-
/**
|
675
|
-
* Creates get / set methods for class configs ending with an underscore
|
676
|
-
* @param {Neo.core.Base} proto
|
677
|
-
* @param {String} key
|
678
|
-
* @private
|
679
|
-
* @tutorial 02_ClassSystem
|
680
|
-
*/
|
681
|
-
function autoGenerateGetSet(proto, key) {
|
682
|
-
if (Neo.hasPropertySetter(proto, key)) {
|
683
|
-
throw('Config ' + key + '_ (' + proto.className + ') already has a set method, use beforeGet, beforeSet & afterSet instead')
|
684
|
-
}
|
685
|
-
|
686
|
-
const
|
687
|
-
_key = '_' + key,
|
688
|
-
uKey = key[0].toUpperCase() + key.slice(1),
|
689
|
-
beforeGet = 'beforeGet' + uKey,
|
690
|
-
beforeSet = 'beforeSet' + uKey,
|
691
|
-
afterSet = 'afterSet' + uKey;
|
692
|
-
|
693
|
-
if (!Neo[getSetCache]) {
|
694
|
-
Neo[getSetCache] = {}
|
695
|
-
}
|
696
|
-
|
697
|
-
if (!Neo[getSetCache][key]) {
|
698
|
-
const publicDescriptor = {
|
699
|
-
get() {
|
700
|
-
let me = this,
|
701
|
-
hasNewKey = Object.hasOwn(me[configSymbol], key),
|
702
|
-
newKey = me[configSymbol][key],
|
703
|
-
value = hasNewKey ? newKey : me[_key];
|
704
|
-
|
705
|
-
if (Array.isArray(value)) {
|
706
|
-
if (key !== 'items') {
|
707
|
-
value = [...value]
|
708
|
-
}
|
709
|
-
} else if (value instanceof Date) {
|
710
|
-
value = new Date(value.valueOf())
|
711
|
-
}
|
712
|
-
|
713
|
-
if (hasNewKey) {
|
714
|
-
me[key] = value; // We do want to trigger the setter => beforeSet, afterSet
|
715
|
-
value = me[_key]; // Return the value parsed by the setter
|
716
|
-
delete me[configSymbol][key]
|
717
|
-
}
|
718
|
-
|
719
|
-
if (typeof me[beforeGet] === 'function') {
|
720
|
-
value = me[beforeGet](value)
|
721
|
-
}
|
722
|
-
|
723
|
-
return value
|
724
|
-
},
|
725
|
-
set(value) {
|
726
|
-
if (value === undefined) return;
|
727
|
-
|
728
|
-
const config = this.getConfig(key);
|
729
|
-
if (!config) return;
|
730
|
-
|
731
|
-
let me = this,
|
732
|
-
oldValue = config.get(); // Get the old value from the Config instance
|
733
|
-
|
734
|
-
// every set call has to delete the matching symbol
|
735
|
-
delete me[configSymbol][key];
|
736
|
-
|
737
|
-
if (key !== 'items' && key !== 'vnode') {
|
738
|
-
value = Neo.clone(value, true, true)
|
739
|
-
}
|
740
|
-
|
741
|
-
if (typeof me[beforeSet] === 'function') {
|
742
|
-
value = me[beforeSet](value, oldValue);
|
743
|
-
|
744
|
-
// If they don't return a value, that means no change
|
745
|
-
if (value === undefined) {
|
746
|
-
return
|
747
|
-
}
|
748
|
-
}
|
749
|
-
|
750
|
-
// Set the new value into the Config instance
|
751
|
-
// The config.set() method will return true if the value actually changed.
|
752
|
-
if (config.set(value)) {
|
753
|
-
me[afterSet]?.(value, oldValue);
|
754
|
-
me.afterSetConfig?.(key, value, oldValue)
|
755
|
-
}
|
756
|
-
}
|
757
|
-
};
|
758
|
-
|
759
|
-
const privateDescriptor = {
|
760
|
-
get() {
|
761
|
-
return this.getConfig(key)?.get();
|
762
|
-
},
|
763
|
-
set(value) {
|
764
|
-
this.getConfig(key)?.setRaw(value);
|
765
|
-
}
|
766
|
-
};
|
767
|
-
|
768
|
-
Neo[getSetCache][key] = publicDescriptor;
|
769
|
-
Neo[getSetCache][_key] = privateDescriptor;
|
770
|
-
}
|
771
|
-
|
772
|
-
Object.defineProperty(proto, key, Neo[getSetCache][key]);
|
773
|
-
Object.defineProperty(proto, _key, Neo[getSetCache][_key])
|
774
|
-
}
|
775
|
-
|
776
962
|
/**
|
777
963
|
* @param {Boolean} create
|
778
964
|
* @param {Object} current
|
@@ -791,9 +977,7 @@ function createArrayNs(create, current, prev) {
|
|
791
977
|
arrRoot = prev[arrDetails[0]]
|
792
978
|
}
|
793
979
|
|
794
|
-
if (!arrRoot)
|
795
|
-
return
|
796
|
-
}
|
980
|
+
if (!arrRoot) return;
|
797
981
|
|
798
982
|
for (; i < len; i++) {
|
799
983
|
arrItem = parseInt(arrDetails[i]);
|
@@ -827,12 +1011,27 @@ function exists(className) {
|
|
827
1011
|
/**
|
828
1012
|
* @param {Neo.core.Base} proto
|
829
1013
|
* @param {Neo.core.Base} mixinProto
|
1014
|
+
* @param {Object} classConfig
|
830
1015
|
* @returns {Function}
|
831
1016
|
* @private
|
832
1017
|
*/
|
833
|
-
function mixinProperty(proto, mixinProto) {
|
834
|
-
return function(key) {
|
835
|
-
if (
|
1018
|
+
function mixinProperty(proto, mixinProto, classConfig) {
|
1019
|
+
return function([key, descriptor]) {
|
1020
|
+
if (ignoreMixin.includes(key)) return;
|
1021
|
+
|
1022
|
+
// Mixins must not override existing class properties with a setter
|
1023
|
+
if (Neo.hasPropertySetter(proto, key)) return;
|
1024
|
+
|
1025
|
+
// Reactive neo configs, or public class fields defined via get() AND set()
|
1026
|
+
if (descriptor.get && descriptor.set) {
|
1027
|
+
Neo.createConfig(proto, key);
|
1028
|
+
|
1029
|
+
const mixinClassConfig = mixinProto.constructor.config;
|
1030
|
+
|
1031
|
+
if (Object.hasOwn(mixinClassConfig, key)) {
|
1032
|
+
classConfig[key] = mixinClassConfig[key];
|
1033
|
+
}
|
1034
|
+
|
836
1035
|
return
|
837
1036
|
}
|
838
1037
|
|
@@ -879,7 +1078,7 @@ function parseArrayFromString(str) {
|
|
879
1078
|
)
|
880
1079
|
}
|
881
1080
|
|
882
|
-
Neo.config
|
1081
|
+
Neo.config ??= {};
|
883
1082
|
|
884
1083
|
Neo.assignDefaults(Neo.config, DefaultConfig);
|
885
1084
|
|