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
@@ -13,8 +13,8 @@ The centerpiece of this release is the new reactive config system, internally na
|
|
13
13
|
// In ComponentA
|
14
14
|
const componentB = Neo.get('b');
|
15
15
|
|
16
|
-
// Subscribe to changes in componentB's '
|
17
|
-
this.cleanup = componentB.getConfig('
|
16
|
+
// Subscribe to changes in componentB's 'text' config
|
17
|
+
this.cleanup = componentB.getConfig('text').subscribe((newValue, oldValue) => {
|
18
18
|
this.someOtherProperty = newValue; // Reactively update
|
19
19
|
});
|
20
20
|
|
@@ -0,0 +1,70 @@
|
|
1
|
+
## Neo.mjs v10.0.0-beta.5 Release Notes
|
2
|
+
|
3
|
+
This beta release dramatically expands on the reactive programming model introduced in beta.4, delivering a powerful, end-to-end **Effect-Based Reactivity System**. This new system provides a declarative and highly efficient way to manage component state, create reactive data transformations, and build dynamic user interfaces with minimal boilerplate.
|
4
|
+
|
5
|
+
This release also includes significant enhancements to the framework's mixin system and state providers, further improving code organization and state management capabilities.
|
6
|
+
|
7
|
+
### Core Framework & Features
|
8
|
+
|
9
|
+
A core philosophy of Neo.mjs is to provide developers with powerful, flexible options. This release reinforces that principle by dramatically enhancing the framework's reactive core. These new features provide a more direct, declarative way to manage state and build UIs—offering a familiar entry point for developers from other ecosystems—while seamlessly integrating with the established power and productivity of Neo.mjs's declarative component tree architecture.
|
10
|
+
|
11
|
+
The pace of innovation in this release is a testament to the power and flexibility of the new architecture. The features introduced in this beta represent a quantum leap forward, enabling development patterns that are more declarative, more powerful, and more intuitive than ever before.
|
12
|
+
|
13
|
+
#### 1. The New Effect-Based Reactivity System
|
14
|
+
|
15
|
+
The core of this release is a new suite of classes (`core.Effect`, `core.EffectManager`, `core.BatchEffectManager`) that enable true "spreadsheet-style" reactivity. An "Effect" is a function that automatically re-runs whenever any of its reactive dependencies change.
|
16
|
+
|
17
|
+
* **Automatic Dependency Tracking**: The framework now automatically tracks which reactive configs are accessed within an Effect. You declare a config as reactive by adding a trailing underscore in the `static config` block (e.g., `myValue_`), and then access it without the underscore (`this.myValue`). You no longer need to manually subscribe to changes; the dependency graph is built and maintained for you.
|
18
|
+
* **Arbitrary Cross-Instance Bindings**: Since bindings are now powered by `core.Effect`, they can depend on any reactive config from any `core.Base` instance—not just components or state provider `data`. This allows for creating powerful, arbitrary data flows between any part of your application (e.g., binding a component's property to a data store's `count`).
|
19
|
+
```javascript
|
20
|
+
// A component can now bind its text to its provider's data AND a store's count
|
21
|
+
bind: {
|
22
|
+
text: data => `${data.title}: ${myStore.count} items`
|
23
|
+
}
|
24
|
+
```
|
25
|
+
* **Synchronous Batching (`core.BatchEffectManager`)**: Multiple state changes can be batched together into a single, atomic update. This prevents "glitching" (where the UI updates multiple times for a single user action) and ensures that effects run only once with the final, consistent state.
|
26
|
+
* **Foundation for Declarative VDOM**: The new Effect system lays the groundwork for a truly declarative component model. As a proof-of-concept, a new `button.Effect` class was created to demonstrate that a component's VDOM can now be generated by a single, reactive function that automatically re-runs when its state dependencies change. This powerful new paradigm, which will be fully realized in the upcoming **[Functional Components](https://github.com/neomjs/neo/issues/6992)** epic, will make component rendering logic more readable, centralized, and easily extensible.
|
27
|
+
* **Lifecycle-Aware Subscriptions**: A new `core.Base#observeConfig()` method provides a safe, convenient way to subscribe to config changes, automatically handling the cleanup of subscriptions when a component is destroyed to prevent memory leaks.
|
28
|
+
|
29
|
+
#### 2. Architectural Evolution: State Management Overhaul
|
30
|
+
|
31
|
+
`state.Provider` has been completely rewritten to be more powerful, explicit, and performant, leveraging the new Effect-based reactivity core. This is a significant architectural evolution from the previous version.
|
32
|
+
|
33
|
+
**The Old System:** Bindings received a single, implicitly merged `data` object, which was a combination of all state providers in the component's hierarchy.
|
34
|
+
|
35
|
+
**The New System:** The new model is more explicit and powerful, providing clearer data flow and eliminating "magic".
|
36
|
+
|
37
|
+
* **Reactive Data Proxies**: The `data` object of a provider is now a deep Proxy. You can now trigger reactive updates simply by direct assignment (`myProvider.data.foo = 'new value'`), and any component bound to that data will update automatically.
|
38
|
+
* **Intelligent Hierarchical Data Access**: The `data` object passed to bindings and formulas is now a smart proxy. It provides a unified view of the data from the entire provider hierarchy. When you read a property (`data.someValue`), the proxy intelligently searches up the component tree to find the closest provider that owns it. When you write a property using `myProvider.setData('someValue', ...)`, it intelligently finds the correct provider in the hierarchy that already owns the property and updates it there, preventing accidental name collisions and making state updates safe and predictable.
|
39
|
+
* **Formulas**: State providers now support formulas—functions that derive their value from other state properties (e.g., `fullName: data => `${data.firstName} ${data.lastName}``). These formulas are themselves reactive and will automatically re-calculate when their dependencies change.
|
40
|
+
* **Two-Way Bindings**: Two-way data bindings kept the same power as before, powered by the original config system.
|
41
|
+
|
42
|
+
#### 3. Enhanced Class System
|
43
|
+
|
44
|
+
* **Automatic Static Config Merging for Mixins**: The framework's mixin system is now significantly more powerful. Previously limited to methods, mixins can now contribute to the `static config` of a class. The configs are intelligently merged with a clear order of precedence:
|
45
|
+
1. The target class's own `static config` always has the highest priority.
|
46
|
+
2. For conflicting configs between mixins, the mixin that appears *earlier* in the `mixins` array wins.
|
47
|
+
3. Mixins on a base class take precedence over mixins on an extended class.
|
48
|
+
* **Granular Cloning Strategies**: The reactive config system now features `clone` and `cloneOnGet` descriptor flags, providing fine-grained control over how and when config values are cloned. This is critical for performance tuning and handling complex data types.
|
49
|
+
|
50
|
+
### New Learning Content
|
51
|
+
|
52
|
+
* **New Guide: "Declarative VDOM with Effects"**: A new guide has been added to the documentation to explain the principles of the new reactivity system and demonstrate how to build components using an Effect-based approach.
|
53
|
+
|
54
|
+
### Quality & Stability
|
55
|
+
* This release includes a comprehensive suite of new unit tests covering the Effect-based reactivity system, the `state.Provider`, and the enhanced `static config` merging. This ensures the stability and correctness of these foundational new features and helps prevent future regressions.
|
56
|
+
|
57
|
+
---
|
58
|
+
|
59
|
+
All changes combined in 1 commit: https://github.com/neomjs/neo/commit/2105ea95b8b498c73d9c54f6bffe8de8704dfe0d
|
60
|
+
|
61
|
+
This release represents a major leap forward for the Neo.mjs framework, providing a state-of-the-art reactive programming model that is both powerful and easy to use.
|
62
|
+
|
63
|
+
### Call To Action
|
64
|
+
|
65
|
+
We are incredibly excited for you to explore these new capabilities.
|
66
|
+
|
67
|
+
* **Explore Effects**: Try creating your own effects to manage component logic and state.
|
68
|
+
* **Leverage State Providers**: Experiment with the new Proxy-based data objects and formulas in `state.Provider`.
|
69
|
+
* **Read the New Guide**: Check out the [Declarative VDOM with Effects guide](https://github.com/neomjs/neo/blob/dev/learn/guides/fundamentals/DeclarativeVDOMWithEffects.md) to get started with the new paradigm.
|
70
|
+
* **Share Your Feedback**: Your insights are invaluable. Please share your experiences and report any issues via [GitHub Issues](https://github.com/neomjs/neo/issues) or our [Slack Channel](https://join.slack.com/t/neomjs/shared_invite/zt-6c50ueeu-3E1~M4T9xkNnb~M_prEEOA).
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# Neo.mjs v10.0.0-beta.6 Release Notes
|
2
|
+
|
3
|
+
This release represents a monumental leap forward, introducing a deeply re-architected reactivity system and a revolutionary VDOM engine that enables surgical, high-performance updates. Building on the foundation of `beta.5`, this version delivers a state-of-the-art developer experience with a new config system, synchronous batched effects, and the official debut of Functional Components.
|
4
|
+
|
5
|
+
## ✨ Highlights
|
6
|
+
|
7
|
+
### 🚀 Hyper-Optimized VDOM Update Engine
|
8
|
+
|
9
|
+
While Neo.mjs has long used a "scoped" VDOM model, this release introduces a revolutionary **update aggregation and orchestration engine**. This new architecture dramatically enhances performance in complex, real-world scenarios (#7076, #7077).
|
10
|
+
|
11
|
+
- **Centralized Orchestration:** The new **`Neo.manager.VDomUpdate`** singleton acts as a central orchestrator, intelligently merging multiple, simultaneous update requests from different components into a single, atomic transaction.
|
12
|
+
- **Combined & Asymmetric Payloads:** Imagine a toolbar and one of its ten buttons both change state in the same event loop. The new VDOM engine can now generate a **single, minimal, asymmetric VDOM payload** containing the toolbar's structure and *only* the updated button's new content. This results in one hyper-efficient update instead of multiple, potentially conflicting ones.
|
13
|
+
- **Key Benefits:** This new model drastically reduces worker traffic, minimizes payload size, and guarantees state integrity for all callbacks, even during complex, overlapping updates.
|
14
|
+
|
15
|
+
### 🧠 Fine-Grained Reactivity & The New Config System
|
16
|
+
|
17
|
+
The core of this release is a new, more powerful way to define and manage reactive state.
|
18
|
+
|
19
|
+
- **`@reactive` Decorator:** We have introduced a new `@reactive` decorator (or a `_` suffix as a non-decorator alternative) to explicitly flag configs as reactive. This improves code clarity and makes the developer's intent clear.
|
20
|
+
- **`Neo.createConfig()`:** The logic for creating reactive configs has been separated from the class system's `setupClass()`. The new `Neo.createConfig()` method allows for the creation of **named, instance-level reactive configs**, providing unparalleled flexibility for dynamic state management.
|
21
|
+
|
22
|
+
### ⚡�� Synchronous Effects & Atomic Batching
|
23
|
+
|
24
|
+
The effect system has been re-engineered for maximum control and performance.
|
25
|
+
|
26
|
+
- **Synchronous by Default:** Effects now run synchronously by default, providing a more predictable execution flow. The `lazy: true` option is still available for cases where initial execution needs to be deferred.
|
27
|
+
- **`EffectManager.pause()`:** The `EffectManager` can now be paused, giving developers fine-grained control over when dependency tracking is active.
|
28
|
+
- **Atomic Batching:** The `EffectBatchManager` ensures that all updates triggered by a single, top-level operation (like `setData()`) are collected, de-duplicated, and executed in a single, atomic batch.
|
29
|
+
|
30
|
+
### 💻 Functional Components & Unified VDOM Lifecycle
|
31
|
+
|
32
|
+
This release officially introduces a new, modern paradigm for building UIs in Neo.mjs, supported by a unified lifecycle.
|
33
|
+
|
34
|
+
- **New `VdomLifecycle` Mixin:** The core VDOM lifecycle logic has been extracted from `component.Base` into the new **`Neo.mixin.VdomLifecycle`**. This major architectural improvement allows both class-based and functional components to share the exact same robust and predictable lifecycle.
|
35
|
+
- **Declarative UI:** Create components as pure functions using the new `defineComponent` method. Manage state effortlessly with reactive hooks like `useConfig`.
|
36
|
+
- **Real-World Example:** The new **Email Application** (`apps/email/view/MainView.mjs`) is built entirely with functional components, serving as a comprehensive showcase of this new architecture.
|
37
|
+
|
38
|
+
### 📦 Core Enhancements & Stability
|
39
|
+
|
40
|
+
- **Enhanced Mixin System:** Mixins are now a first-class citizen for composing component features. They can now contribute directly to the `static config` of a class, including both reactive and non-reactive properties, which are intelligently merged with the class's own configs (#6905).
|
41
|
+
- **Improved Update Stability:** A small but critical change to `updateVdom()` now prevents it from queuing any updates before a component is fully constructed (`isConstructed` is true). This eliminates a whole class of potential race conditions and ensures updates only happen when a component is in a valid rendering state.
|
42
|
+
- **`Neo.gatekeep()`:** A new `Neo.gatekeep()` method has been introduced to standardize how singleton and module exports are handled, improving consistency and preventing duplicate instantiations (#7081, #7072).
|
43
|
+
|
44
|
+
### 📚 New Content & Developer Experience
|
45
|
+
|
46
|
+
- **Enhanced Live Previews:** The LivePreview engine has been overhauled to fully support functional components and modern JavaScript (#7069, #7073).
|
47
|
+
- **New Framework Comparisons:** The documentation now includes new, in-depth comparison articles against other popular frameworks (like React) to better highlight the unique architectural advantages of Neo.mjs.
|
48
|
+
- **New Tests & Learning Content:** This release is backed by a massive suite of new tests covering all aspects of the new reactivity model and VDOM engine. The learning content has also been significantly expanded.
|
@@ -0,0 +1,498 @@
|
|
1
|
+
# Epic: Functional Components
|
2
|
+
|
3
|
+
## Motivation
|
4
|
+
|
5
|
+
This initiative will establish a new, lightweight base class for components. This class will bypass the traditional `items` array and `layout` system. Instead, it will be driven by a central `Effect` that calls a VDOM-generating method (e.g., `createVdom()`) to declaratively build the component's UI based on its current state. This aligns with the reactive patterns of other popular frameworks and provides a more intuitive and familiar entry point for those developers.
|
6
|
+
|
7
|
+
This epic will be broken down into several sub-tickets to implement this new component architecture in an iterative and isolated manner.
|
8
|
+
|
9
|
+
Functional Components are an addition to, and not a replacement for declarative component trees based on `container.Base`: `items`.
|
10
|
+
|
11
|
+
---
|
12
|
+
|
13
|
+
## Two Modes of Functional Component Definition
|
14
|
+
|
15
|
+
Neo.mjs will offer two distinct ways to define functional components, catering to different developer preferences and needs. Both modes leverage the underlying `Neo.functional.component.Base` class and the `Neo.core.Effect` system for reactive rendering, but they provide different levels of abstraction and access to the framework's full power.
|
16
|
+
|
17
|
+
### 1. Beginner Mode: Pure Function with Hooks (e.g., `Neo.functional.defineComponent`)
|
18
|
+
|
19
|
+
This mode is designed for developers seeking a highly concise and familiar syntax, especially those coming from frameworks like React. Components are defined as plain JavaScript functions that return VDOM. State management is handled via dedicated hooks (e.g., `useConfig`).
|
20
|
+
|
21
|
+
**Characteristics:**
|
22
|
+
- **Concise Syntax:** Focus on the VDOM rendering logic.
|
23
|
+
- **Hook-based State:** State and side effects are managed through `use` hooks.
|
24
|
+
- **Simplified API:** Abstracts away class boilerplate.
|
25
|
+
- **Tier 1 Reactivity:** Primarily leverages `Neo.core.Config` for reactive values and `Neo.core.Effect` for re-rendering.
|
26
|
+
- **No Lifecycle Hooks:** Does not expose `beforeGet`, `beforeSet`, or `afterSet` lifecycle hooks directly on the component definition, as these are tied to the class-based `static config` system.
|
27
|
+
|
28
|
+
### 2. Medium Mode: Class-based Functional Component (Extending `Neo.functional.component.Base`)
|
29
|
+
|
30
|
+
This mode provides direct access to the underlying `Neo.functional.component.Base` class, allowing developers to define components using `static config` properties. This offers a more explicit and powerful way to define reactive components within the Neo.mjs class system.
|
31
|
+
|
32
|
+
**Characteristics:**
|
33
|
+
- **Explicit Configs:** State is defined via `static config` properties.
|
34
|
+
- **Full Two-Tier Reactivity:** Access to both `Neo.core.Config` (Tier 1) and the `autoGenerateGetSet` mechanism (Tier 2), including `afterSet` lifecycle hooks for imperative side effects.
|
35
|
+
- **Class-based Structure:** Familiar to developers comfortable with class-based component patterns.
|
36
|
+
|
37
|
+
---
|
38
|
+
|
39
|
+
## Sub-Ticket: Create `Neo.component.mixin.VdomLifecycle`
|
40
|
+
|
41
|
+
**Status:** Done
|
42
|
+
|
43
|
+
### 1. Summary
|
44
|
+
|
45
|
+
Create a new mixin named `Neo.component.mixin.VdomLifecycle`. This mixin will encapsulate the core VDOM rendering engine logic currently located in `Neo.component.Base`. This is the foundational step for enabling the new functional component base class and for cleaning up the existing component architecture.
|
46
|
+
|
47
|
+
### 2. Rationale
|
48
|
+
|
49
|
+
The new reactivity layer introduced in v10, centered around `Neo.core.Config` and `Neo.core.Effect`, allows for a highly efficient and declarative component model. The `src/button/Effect.mjs` class serves as a proof-of-concept for this pattern, where a single `Effect` replaces dozens of imperative `afterSet` hooks.
|
50
|
+
|
51
|
+
This new, simpler component architecture relies on the ability to compose features using Mixins. With the recent enhancement enabling mixins to merge their `static config` into a consuming class, we can now create self-contained "feature mixins" (e.g., for VDOM management).
|
52
|
+
|
53
|
+
Extracting the VDOM logic from `component.Base` into `Neo.component.mixin.VdomLifecycle` is the foundational step. It will:
|
54
|
+
- Improve code modularity and separation of concerns.
|
55
|
+
- Slim down `component.Base`, making it easier to understand and maintain.
|
56
|
+
- Provide a reusable piece of core machinery that can be used by the new `FunctionalComponentBase` without inheriting all of `component.Base`.
|
57
|
+
|
58
|
+
### 3. Scope & Implementation Plan
|
59
|
+
|
60
|
+
1. **Create File:** Create a new file at `src/mixin/VdomLifecycle.mjs`.
|
61
|
+
2. **Identify & Move Logic:** Move the properties and methods related to the VDOM rendering engine from `src/component/Base.mjs` into `Neo.component.mixin.VdomLifecycle`. The list of candidates we previously identified will be used as the basis for this refactoring.
|
62
|
+
3. **Refactor `component.Base`:** Modify `src/component/Base.mjs` to use the new `VdomLifecycle` mixin, ensuring all existing functionality remains intact.
|
63
|
+
|
64
|
+
### 4. Definition of Done
|
65
|
+
|
66
|
+
- `Neo.component.mixin.VdomLifecycle` is created and contains the extracted VDOM logic.
|
67
|
+
- `Neo.component.Base` uses the new mixin.
|
68
|
+
- All existing component-related tests pass without regression, confirming the refactoring is successful.
|
69
|
+
|
70
|
+
---
|
71
|
+
|
72
|
+
## Sub-Ticket: Create `Neo.functional.component.Base`
|
73
|
+
|
74
|
+
**Status:** Done
|
75
|
+
|
76
|
+
### 1. Summary
|
77
|
+
|
78
|
+
Create a new base class, `Neo.functional.component.Base`, which will serve as the foundational class for all functional components. This class provides the core reactive rendering mechanism and acts as the underlying base for both class-based functional components and the simpler, function-based "beginner mode" components.
|
79
|
+
|
80
|
+
### 2. Rationale
|
81
|
+
|
82
|
+
The primary goal of the Functional Components epic is to provide a simpler entry point into the Neo.mjs ecosystem while also offering the full power of its reactivity. This base class achieves this by providing a minimal, modern API for creating components, directly appealing to developers familiar with frameworks like React and Vue, and serving as the common foundation for different component definition styles.
|
83
|
+
|
84
|
+
### 3. Scope & Implementation Plan
|
85
|
+
|
86
|
+
1. **Create File:** Create a new file at `src/functional/component/Base.mjs`.
|
87
|
+
2. **Class Definition:**
|
88
|
+
* The class will extend `Neo.core.Base`.
|
89
|
+
* It will use the `Neo.component.mixin.VdomLifecycle` (created in the prerequisite ticket).
|
90
|
+
* It will **not** extend `Neo.component.Base` or `Neo.container.Base`.
|
91
|
+
3. **Core API:**
|
92
|
+
* It will introduce a new method for developers to implement: `createVdom()`. This method is responsible for returning the component's VDOM structure based on its current configs (state).
|
93
|
+
* In its `construct()` method, it will create a `Neo.core.Effect`. This effect will wrap a call to `this.createVdom()` and assign the result to `this.vdom`. This ensures that any time a config used within `createVdom()` is changed, the component automatically re-renders.
|
94
|
+
|
95
|
+
### 4. Example Usage (Class-based Functional Component)
|
96
|
+
|
97
|
+
```javascript
|
98
|
+
import FunctionalBase from 'neo/functional/component/Base.mjs';
|
99
|
+
|
100
|
+
class MyFunctionalButton extends FunctionalBase {
|
101
|
+
static config = {
|
102
|
+
className: 'MyApp.MyFunctionalButton',
|
103
|
+
text_ : 'Click Me'
|
104
|
+
}
|
105
|
+
|
106
|
+
createVdom() {
|
107
|
+
return {
|
108
|
+
tag : 'button',
|
109
|
+
text: this.text
|
110
|
+
};
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
// An instance of this component would render a button and
|
115
|
+
// automatically update its text whenever `myButton.text = 'new text'` is called.
|
116
|
+
```
|
117
|
+
|
118
|
+
### 5. Definition of Done
|
119
|
+
|
120
|
+
- `src/functional/component/Base.mjs` is created.
|
121
|
+
- The class works as described, using `VdomLifecycle` and a central `Effect`.
|
122
|
+
- A basic test case is created to verify that a simple `FunctionalBase` component can be rendered and updates when its configs change.
|
123
|
+
|
124
|
+
---
|
125
|
+
|
126
|
+
## Sub-Ticket: Create `Neo.functional.useConfig` Hook
|
127
|
+
|
128
|
+
**Status:** In Progress
|
129
|
+
|
130
|
+
### 1. Summary
|
131
|
+
|
132
|
+
Implement a `useConfig` hook for functional components, allowing developers to manage reactive state within their "render" functions in a React-like fashion.
|
133
|
+
|
134
|
+
### 2. Rationale
|
135
|
+
|
136
|
+
This hook provides a simplified entry point for developers familiar with `useState` from other frameworks. It leverages Neo.mjs's Tier 1 reactivity (`Neo.core.Config`) for state management without requiring class-based config definitions, making it ideal for the "beginner mode" functional component experience.
|
137
|
+
|
138
|
+
### 3. Scope & Implementation Plan
|
139
|
+
|
140
|
+
1. **Create File:** Create `src/functional/useConfig.mjs`.
|
141
|
+
2. **Implement `useConfig`:** The hook will return a `[value, setter]` tuple. The `setter` will update an internal `Neo.core.Config` instance.
|
142
|
+
3. **Lifecycle Management:** Ensure the `Neo.core.Config` instance is properly managed (created, updated, destroyed) in relation to the functional component's lifecycle. This will likely involve associating the `core.Config` instance with the `Neo.functional.component.Base` instance that is executing the "render" function.
|
143
|
+
|
144
|
+
### 4. Example Usage
|
145
|
+
|
146
|
+
```javascript
|
147
|
+
import { useConfig } from 'neo/functional/useConfig.mjs';
|
148
|
+
import { defineComponent } from 'neo/functional/defineComponent.mjs'; // Assuming this exists
|
149
|
+
|
150
|
+
const MyCounter = defineComponent({
|
151
|
+
className: 'MyApp.MyCounter',
|
152
|
+
createVdom: () => {
|
153
|
+
const [count, setCount] = useConfig(0);
|
154
|
+
|
155
|
+
return {
|
156
|
+
tag: 'button',
|
157
|
+
text: `Count: ${count}`,
|
158
|
+
listeners: {
|
159
|
+
click: () => setCount(count + 1)
|
160
|
+
}
|
161
|
+
};
|
162
|
+
}
|
163
|
+
});
|
164
|
+
```
|
165
|
+
|
166
|
+
### 5. Definition of Done
|
167
|
+
|
168
|
+
- `Neo.functional.useConfig` hook is implemented and tested.
|
169
|
+
- It correctly creates and manages reactive state via `Neo.core.Config`.
|
170
|
+
- Changes to the state trigger re-execution of the component's render function (via `Effect`).
|
171
|
+
|
172
|
+
---
|
173
|
+
|
174
|
+
## Sub-Ticket: Create `Neo.functional.defineComponent` Factory
|
175
|
+
|
176
|
+
**Status:** Done
|
177
|
+
|
178
|
+
### 1. Summary
|
179
|
+
|
180
|
+
Implement a factory function that allows developers to define functional components using a plain JavaScript function, abstracting away the underlying class creation.
|
181
|
+
|
182
|
+
### 2. Rationale
|
183
|
+
|
184
|
+
This factory further simplifies the developer experience for "beginner mode" functional components, making the syntax more concise and familiar to developers accustomed to functional component patterns in other frameworks. It acts as the bridge between a pure function definition and the underlying `Neo.functional.component.Base` class.
|
185
|
+
|
186
|
+
### 3. Scope & Implementation Plan
|
187
|
+
|
188
|
+
1. **Create File:** Create `src/functional/defineComponent.mjs`.
|
189
|
+
2. **Implement `defineComponent`:** The factory will accept a configuration object (including `className`, optional `ntype`, and the `createVdom` function).
|
190
|
+
3. **Internal Class Generation:** Internally, `defineComponent` will create a new class that extends `Neo.functional.component.Base`. It will apply the provided `className` and `ntype` to this new class.
|
191
|
+
4. **`createVdom` Method Assignment:** The developer's `createVdom` function will be assigned as the `createVdom` method of the generated class's prototype.
|
192
|
+
5. **Integration with `useConfig`:** Ensure that the context (`this`) within the developer's `createVdom` function (when executed by the generated component instance) allows `useConfig` to correctly associate state with that instance.
|
193
|
+
|
194
|
+
### 4. Example Usage
|
195
|
+
|
196
|
+
```javascript
|
197
|
+
import { defineComponent } from 'neo/functional/defineComponent.mjs';
|
198
|
+
import { useConfig } from 'neo/functional/useConfig.mjs';
|
199
|
+
|
200
|
+
const MyGreeting = defineComponent({
|
201
|
+
className: 'MyApp.MyGreeting',
|
202
|
+
createVdom: (config) => {
|
203
|
+
const [name, setName] = useConfig('World');
|
204
|
+
|
205
|
+
return {
|
206
|
+
tag: 'div',
|
207
|
+
text: `Hello, ${name}!`,
|
208
|
+
listeners: {
|
209
|
+
click: () => setName(name === 'World' ? 'Neo.mjs' : 'World')
|
210
|
+
}
|
211
|
+
};
|
212
|
+
}
|
213
|
+
});
|
214
|
+
```
|
215
|
+
|
216
|
+
### 5. Definition of Done
|
217
|
+
|
218
|
+
- `Neo.functional.defineComponent` factory is implemented and tested.
|
219
|
+
- It successfully generates functional component classes from plain functions.
|
220
|
+
- Generated components correctly utilize `useConfig` for state management.
|
221
|
+
|
222
|
+
---
|
223
|
+
|
224
|
+
# Sub-Ticket: Enhance `Neo.functional.component.Base` for Hook Support
|
225
|
+
|
226
|
+
**Status:** Done
|
227
|
+
|
228
|
+
## 1. Summary
|
229
|
+
|
230
|
+
This ticket covers the foundational work on `Neo.functional.component.Base` to enable the hook system (`useConfig`, `useEvent`, etc.) for beginner-mode functional components. The key challenge was to allow external hook functions to manage state on a component instance without exposing that state through the public API.
|
231
|
+
|
232
|
+
## 2. Rationale
|
233
|
+
|
234
|
+
The initial implementation of `functional.component.Base` was a minimal class with a `createVdom` method driven by an `Effect`. To support hooks like React's `useState`, we needed a mechanism to:
|
235
|
+
1. Associate state (like `Config` instances) with a specific component instance.
|
236
|
+
2. Track the order of hook calls within a single `createVdom` execution.
|
237
|
+
3. Provide a way for external hook functions to access this internal state securely.
|
238
|
+
|
239
|
+
Using protected properties (e.g., `_hooks`) was considered but deemed insufficient for true encapsulation. The chosen solution provides a robust, framework-private way to manage hook state.
|
240
|
+
|
241
|
+
## 3. Scope & Implementation Plan
|
242
|
+
|
243
|
+
1. **Introduce Symbols for State:**
|
244
|
+
* Create two `Symbol.for()` symbols: `hookIndexSymbol` and `hooksSymbol`.
|
245
|
+
* These symbols act as unique keys for properties on the component instance, making them accessible to any module that knows the symbol, but keeping them off the public API.
|
246
|
+
|
247
|
+
2. **Initialize State in `FunctionalBase`:**
|
248
|
+
* In the `construct()` method of `functional.component.Base`, use `Object.defineProperties` to add the symbol-keyed properties (`[hookIndexSymbol]` and `[hooksSymbol]`) to the component instance.
|
249
|
+
* These properties are configured to be non-enumerable (`enumerable: false`) to further hide them from standard object iteration.
|
250
|
+
* The `hookIndex` is reset to `0` at the beginning of every `vdomEffect` execution, ensuring a clean slate for each render.
|
251
|
+
|
252
|
+
3. **Component Registration:**
|
253
|
+
* Implement the `afterSetId()` and `destroy()` methods to correctly register and unregister the functional component instance with `Neo.manager.Component`. This makes functional components discoverable via `Neo.getComponent()`, integrating them fully into the framework's component model.
|
254
|
+
|
255
|
+
4. **Link Effect to Component:**
|
256
|
+
* Modify the `vdomEffect` creation to pass the component's ID (`this.id`) to the `Effect` constructor. This allows the `useConfig` hook to retrieve the currently rendering component instance by getting the active effect from `EffectManager` and looking up the component by its ID.
|
257
|
+
|
258
|
+
## 4. Definition of Done
|
259
|
+
|
260
|
+
- `functional.component.Base` is updated to use `Symbol.for()` to manage internal hook state.
|
261
|
+
- The component correctly registers and unregisters itself with `ComponentManager`.
|
262
|
+
- The `vdomEffect` is correctly associated with the component's ID.
|
263
|
+
- The implementation provides the necessary foundation for the `useConfig` hook to function correctly.
|
264
|
+
|
265
|
+
# Sub-Ticket: Enhance `Neo.core.Effect` Constructor
|
266
|
+
|
267
|
+
**Status:** Done
|
268
|
+
|
269
|
+
## 1. Summary
|
270
|
+
|
271
|
+
This ticket covers a minor but important enhancement to the `Neo.core.Effect` class to support the functional component hook system.
|
272
|
+
|
273
|
+
## 2. Rationale
|
274
|
+
|
275
|
+
To allow hooks like `useConfig` to identify which component is currently rendering, we needed a way to link an active `Effect` back to its owner component. The cleanest, most decoupled way to achieve this was to add an optional `componentId` to the `Effect`'s constructor.
|
276
|
+
|
277
|
+
Since `core.Effect` is a new class introduced in the v10 beta series, adding an optional parameter is a safe, non-breaking change.
|
278
|
+
|
279
|
+
## 3. Scope & Implementation Plan
|
280
|
+
|
281
|
+
1. **Update `Effect` Constructor:**
|
282
|
+
* Modify the `constructor` of `Neo.core.Effect` to accept an optional second parameter, `componentId`.
|
283
|
+
* If `componentId` is provided, store it on a public `this.componentId` property on the effect instance.
|
284
|
+
|
285
|
+
2. **Update `FunctionalBase`:**
|
286
|
+
* In `Neo.functional.component.Base`, update the creation of the `vdomEffect` to pass `this.id` as the second argument to the `Effect` constructor.
|
287
|
+
|
288
|
+
## 4. Definition of Done
|
289
|
+
|
290
|
+
- The `Neo.core.Effect` constructor is updated to accept an optional `componentId`.
|
291
|
+
- `functional.component.Base` correctly passes its ID when creating its `vdomEffect`.
|
292
|
+
- This change enables the `useConfig` hook to reliably get the current component instance.
|
293
|
+
|
294
|
+
## Sub-Ticket: Create Interoperability Layer
|
295
|
+
|
296
|
+
**Status:** To Do
|
297
|
+
|
298
|
+
### 1. Summary
|
299
|
+
|
300
|
+
Design and implement the mechanism that allows functional components to seamlessly host classic components (e.g., `Neo.grid.Container`) and vice-versa. This is critical for ensuring that developers can adopt the new functional paradigm without losing access to the existing library of powerful, classic components.
|
301
|
+
|
302
|
+
### 2. Rationale
|
303
|
+
|
304
|
+
A developer will inevitably need to mix and match component paradigms. For example, they might build the main structure of their application using new functional components but need to include a complex, data-driven component like the grid. Without a robust interoperability layer, this would be impossible, creating a fractured ecosystem.
|
305
|
+
|
306
|
+
The core challenge is that functional components define children declaratively as part of a VDOM tree, while classic components are instantiated via `Neo.create()` and managed within an `items` array. We need to bridge this gap.
|
307
|
+
|
308
|
+
### 3. Technical Challenges & Example
|
309
|
+
|
310
|
+
Consider a `FunctionalBase` component trying to render a classic grid:
|
311
|
+
|
312
|
+
```javascript
|
313
|
+
// Inside a FunctionalBase component...
|
314
|
+
createVdom() {
|
315
|
+
return {
|
316
|
+
tag: 'div',
|
317
|
+
cn: [
|
318
|
+
{tag: 'h1', text: 'My Classic Grid'},
|
319
|
+
{
|
320
|
+
// This is just a VDOM node, not an instance yet
|
321
|
+
module: Neo.grid.Container,
|
322
|
+
store: this.myStore,
|
323
|
+
columns: [...]
|
324
|
+
}
|
325
|
+
]
|
326
|
+
};
|
327
|
+
}
|
328
|
+
```
|
329
|
+
|
330
|
+
To make this work, the system (likely the `VdomLifecycle` mixin) must solve these problems when it processes the VDOM from `createVdom()`:
|
331
|
+
|
332
|
+
1. **Instantiation:** It must detect VDOM nodes that represent component definitions (e.g., via a `module` or `ntype` key) and automatically call `Neo.create()` to turn them into component instances. The logic inside `container.Base#createItem` is a good reference for this.
|
333
|
+
2. **Parent/Child Linking:** Once the classic component instance (the grid) is created, its `parentId` must be set to the `id` of the functional component that is rendering it. The `parent` property should also be correctly linked.
|
334
|
+
3. **Context Propagation:** This parent/child link is essential for context-aware features. The grid instance must be able to find its parent's controller or state provider via `getController()` and `getStateProvider()`.
|
335
|
+
|
336
|
+
### 4. Scope & Implementation Plan
|
337
|
+
|
338
|
+
- Enhance the `VdomLifecycle` mixin (or create a new helper) to traverse VDOM trees before they are sent to the renderer.
|
339
|
+
- This traversal logic will identify and instantiate component definitions.
|
340
|
+
- It will be responsible for setting `parentId` on the new child instances.
|
341
|
+
- Create test cases that verify a functional component can successfully render a classic `container.Base` and that the classic container can find its functional parent's controller.
|
342
|
+
|
343
|
+
### 5. Definition of Done
|
344
|
+
|
345
|
+
- A functional component can successfully render a classic component defined within its `createVdom` method.
|
346
|
+
- The classic component is correctly mounted in the DOM.
|
347
|
+
- The classic component can access its functional parent via `this.parent`.
|
348
|
+
- Context-aware features work across the boundary.
|
349
|
+
|
350
|
+
---
|
351
|
+
|
352
|
+
## Sub-Ticket: Encourage Pure VDOM Effects
|
353
|
+
|
354
|
+
**Status:** To Do
|
355
|
+
|
356
|
+
### 1. Summary
|
357
|
+
|
358
|
+
Define and promote best practices for writing "pure" VDOM-generating methods (e.g., `createVdom()`) within functional components. This ensures that the output of these methods is solely determined by their inputs (component configs) and that they produce no side effects, which is crucial for predictability and enabling future optimizations like memoization.
|
359
|
+
|
360
|
+
### 2. Rationale
|
361
|
+
|
362
|
+
The `Neo.core.Effect` system automatically re-executes VDOM-generating methods when their dependencies changes. For this system to be truly robust and performant, these methods should ideally be pure functions. Purity makes components easier to reason about, test, and debug. It also unlocks significant performance gains through memoization, as the output can be safely cached if inputs remain unchanged.
|
363
|
+
|
364
|
+
### 3. Scope & Implementation Plan
|
365
|
+
|
366
|
+
1. **Define Purity Guidelines:** Clearly document what constitutes a "pure" VDOM-generating method in the context of Neo.mjs functional components. This includes avoiding direct DOM manipulation, external state modification, or reliance on non-reactive global state within `createVdom()`.
|
367
|
+
2. **Documentation:** Add a section to the functional component documentation explaining the concept of pure effects and why it's important.
|
368
|
+
3. **Linting/Static Analysis (Optional, Future):** Explore the possibility of adding linting rules or static analysis checks to identify potential impurities in `createVdom()` methods.
|
369
|
+
|
370
|
+
### 4. Definition of Done
|
371
|
+
|
372
|
+
- Clear guidelines for writing pure VDOM-generating methods are documented.
|
373
|
+
- The documentation explains the benefits of purity and provides examples.
|
374
|
+
|
375
|
+
---
|
376
|
+
|
377
|
+
## Sub-Ticket: Implement Effect Memoization
|
378
|
+
|
379
|
+
**Status:** To Do
|
380
|
+
|
381
|
+
### 1. Summary
|
382
|
+
|
383
|
+
Enhance the `Neo.core.Effect` system (or provide a utility around it) to support memoization for VDOM-generating methods. This will significantly improve rendering performance by caching the VDOM output and preventing unnecessary re-executions when component configs (inputs) have not changed.
|
384
|
+
|
385
|
+
### 2. Rationale
|
386
|
+
|
387
|
+
Functional components, driven by `Neo.core.Effect`, re-generate their VDOM whenever a tracked config changes. While efficient, re-generating complex VDOM trees can still be computationally intensive. By memoizing the output of pure VDOM-generating methods, we can avoid redundant work. If the inputs to `createVdom()` are the same as the last execution, the cached VDOM can be returned directly, bypassing the VDOM generation and worker communication steps.
|
388
|
+
|
389
|
+
### 3. Scope & Implementation Plan
|
390
|
+
|
391
|
+
1. **Memoization Mechanism:** Design and implement a caching layer for `Neo.core.Effect` instances (or a new `MemoizedEffect` class). This mechanism will:
|
392
|
+
* Store the last computed VDOM output.
|
393
|
+
* Efficiently compare current inputs (tracked configs) with previous inputs to determine if re-execution is necessary.
|
394
|
+
* Invalidate the cache when inputs change.
|
395
|
+
2. **Integration:** Determine how developers will opt-in to memoization (e.g., a config on `FunctionalBase`, a decorator, or a utility function).
|
396
|
+
3. **Performance Testing:** Create benchmarks to measure the performance gains achieved through memoization, especially for components with complex VDOM structures or frequently updated but unchanged inputs.
|
397
|
+
|
398
|
+
### 4. Definition of Done
|
399
|
+
|
400
|
+
- A memoization mechanism for `Neo.core.Effect` is implemented.
|
401
|
+
- Functional components can leverage memoization to improve rendering performance.
|
402
|
+
- Performance benchmarks demonstrate measurable gains.
|
403
|
+
|
404
|
+
## Sub-Ticket: Proof of Concept: Beginner Mode Functional Component
|
405
|
+
|
406
|
+
**Status:** To Do
|
407
|
+
|
408
|
+
### 1. Summary
|
409
|
+
|
410
|
+
Create a simple, working example of a "Beginner Mode" functional component using `Neo.functional.defineComponent` and `Neo.functional.useConfig`.
|
411
|
+
|
412
|
+
### 2. Rationale
|
413
|
+
|
414
|
+
This PoC is crucial to validate the end-to-end developer experience for the simplified functional component definition. It will demonstrate that a developer can define a reactive component as a plain function, leveraging hooks for state, and that it renders correctly and updates reactively.
|
415
|
+
|
416
|
+
### 3. Scope & Implementation Plan
|
417
|
+
|
418
|
+
1. **Create a Simple Component:** Define a basic functional component (e.g., a counter or a text display) using the `defineComponent` factory and `useConfig` hook.
|
419
|
+
2. **Render the Component:** Instantiate and render this component within a test environment or a minimal application.
|
420
|
+
3. **Verify Reactivity:** Ensure that changes to the state managed by `useConfig` correctly trigger re-renders of the component.
|
421
|
+
|
422
|
+
### 4. Example Usage
|
423
|
+
|
424
|
+
```javascript
|
425
|
+
// In a component file (e.g., MyCounter.mjs)
|
426
|
+
import { defineComponent } from 'neo/functional/defineComponent.mjs';
|
427
|
+
import { useConfig } from 'neo/functional/useConfig.mjs';
|
428
|
+
|
429
|
+
export default defineComponent(function MyCounter(config) { // The functional component is now the function itself
|
430
|
+
const [count, setCount] = useConfig(0);
|
431
|
+
|
432
|
+
return {
|
433
|
+
tag: 'button',
|
434
|
+
text: `Count: ${count}`,
|
435
|
+
// No listeners property directly in VDOM for beginner mode
|
436
|
+
};
|
437
|
+
});
|
438
|
+
|
439
|
+
// In your app's MainView or a test file
|
440
|
+
// Neo.create(MyCounter, { id: 'my-counter-instance' });
|
441
|
+
```
|
442
|
+
|
443
|
+
### 5. Definition of Done
|
444
|
+
|
445
|
+
- A functional component defined as a plain function using `defineComponent` and `useConfig` is successfully rendered.
|
446
|
+
- The component's state updates reactively, and these updates are reflected in the DOM.
|
447
|
+
|
448
|
+
---
|
449
|
+
|
450
|
+
## Sub-Ticket: DOM Event Handling for Beginner Mode Functional Components
|
451
|
+
|
452
|
+
**Status:** To Do
|
453
|
+
|
454
|
+
### 1. Summary
|
455
|
+
|
456
|
+
Explore and define the idiomatic way for developers to handle DOM events within "Beginner Mode" functional components, ensuring integration with Neo.mjs's powerful, separate DOM event engine.
|
457
|
+
|
458
|
+
### 2. Rationale
|
459
|
+
|
460
|
+
Unlike some other frameworks, Neo.mjs has a dedicated and highly optimized DOM event management system that operates separately from the VDOM. Directly embedding `listeners` within the VDOM (as is common in React) is not the Neo.mjs way and can lead to confusion and suboptimal performance. This ticket aims to define a clear, intuitive, and performant pattern for event handling in the simplified functional component mode.
|
461
|
+
|
462
|
+
### 3. Scope & Implementation Plan
|
463
|
+
|
464
|
+
1. **Research Existing Patterns:** Analyze how Neo.mjs's DOM event engine (`Neo.manager.DomEvent`) is currently used and how it can be exposed in a functional, hook-like manner.
|
465
|
+
2. **Propose API:** Brainstorm and propose a `useEvent` hook or similar API that allows developers to attach event listeners to VDOM nodes declaratively within their `createVdom` function, without directly embedding `listeners` in the VDOM object.
|
466
|
+
3. **Integration:** Ensure the proposed API seamlessly integrates with the underlying `Neo.functional.component.Base` instance and the `Neo.manager.DomEvent`.
|
467
|
+
4. **Documentation:** Provide clear examples and guidelines for using the new event handling mechanism.
|
468
|
+
|
469
|
+
### 4. Example Usage (Conceptual)
|
470
|
+
|
471
|
+
```javascript
|
472
|
+
import { defineComponent } from 'neo/functional/defineComponent.mjs';
|
473
|
+
import { useConfig } from 'neo/functional/useConfig.mjs';
|
474
|
+
import { useEvent } from 'neo/functional/useEvent.mjs'; // New hook
|
475
|
+
|
476
|
+
export default defineComponent(function MyClickableDiv(config) {
|
477
|
+
const [count, setCount] = useConfig(0);
|
478
|
+
|
479
|
+
// Attach a click listener using the new hook
|
480
|
+
useEvent('click', (event) => {
|
481
|
+
setCount(count + 1);
|
482
|
+
console.log('Div clicked!', event);
|
483
|
+
});
|
484
|
+
|
485
|
+
return {
|
486
|
+
tag: 'div',
|
487
|
+
html: `Clicked ${count} times`,
|
488
|
+
// No listeners property directly in VDOM
|
489
|
+
};
|
490
|
+
});
|
491
|
+
```
|
492
|
+
|
493
|
+
### 5. Definition of Done
|
494
|
+
|
495
|
+
- A clear and idiomatic pattern for DOM event handling in "Beginner Mode" functional components is defined.
|
496
|
+
- Necessary hooks/utilities (e.g., `useEvent`) are designed.
|
497
|
+
- Documentation and examples are provided.
|
498
|
+
|