@saolabs/client 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -195
- package/dist/index.d.ts +20 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -3
- package/dist/index.js.map +1 -1
- package/dist/plugins.d.ts +2 -2
- package/dist/plugins.js +2 -2
- package/dist/src/core/app/Application.d.ts +25 -15
- package/dist/src/core/app/Application.d.ts.map +1 -1
- package/dist/src/core/app/Application.js +55 -18
- package/dist/src/core/app/Application.js.map +1 -1
- package/dist/src/core/bootstrap/app.d.ts +1 -1
- package/dist/src/core/bootstrap/app.d.ts.map +1 -1
- package/dist/src/core/bootstrap/app.js +77 -4
- package/dist/src/core/bootstrap/app.js.map +1 -1
- package/dist/src/core/bootstrap/bundle.d.ts +41 -0
- package/dist/src/core/bootstrap/bundle.d.ts.map +1 -0
- package/dist/src/core/bootstrap/bundle.js +98 -0
- package/dist/src/core/bootstrap/bundle.js.map +1 -0
- package/dist/src/core/bootstrap/providers/ApiServiceProvider.d.ts +1 -1
- package/dist/src/core/bootstrap/providers/ApiServiceProvider.d.ts.map +1 -1
- package/dist/src/core/bootstrap/providers/ApiServiceProvider.js +3 -3
- package/dist/src/core/bootstrap/providers/ApiServiceProvider.js.map +1 -1
- package/dist/src/core/bootstrap/providers/CoreServiceProvider.d.ts +1 -1
- package/dist/src/core/bootstrap/providers/CoreServiceProvider.d.ts.map +1 -1
- package/dist/src/core/bootstrap/providers/CoreServiceProvider.js +11 -8
- package/dist/src/core/bootstrap/providers/CoreServiceProvider.js.map +1 -1
- package/dist/src/core/bootstrap/providers/HelperServiceProvider.d.ts +3 -1
- package/dist/src/core/bootstrap/providers/HelperServiceProvider.d.ts.map +1 -1
- package/dist/src/core/bootstrap/providers/HelperServiceProvider.js +8 -3
- package/dist/src/core/bootstrap/providers/HelperServiceProvider.js.map +1 -1
- package/dist/src/core/bootstrap/providers/RouteServiceProvider.d.ts +2 -1
- package/dist/src/core/bootstrap/providers/RouteServiceProvider.d.ts.map +1 -1
- package/dist/src/core/bootstrap/providers/RouteServiceProvider.js +14 -4
- package/dist/src/core/bootstrap/providers/RouteServiceProvider.js.map +1 -1
- package/dist/src/core/bootstrap/providers/ServiceProvider.d.ts +4 -4
- package/dist/src/core/bootstrap/providers/ServiceProvider.d.ts.map +1 -1
- package/dist/src/core/bootstrap/providers/ServiceProvider.js +5 -5
- package/dist/src/core/bootstrap/providers/ServiceProvider.js.map +1 -1
- package/dist/src/core/bootstrap/providers/ViewServiceProvider.d.ts +1 -1
- package/dist/src/core/bootstrap/providers/ViewServiceProvider.d.ts.map +1 -1
- package/dist/src/core/bootstrap/providers/ViewServiceProvider.js +3 -3
- package/dist/src/core/bootstrap/providers/ViewServiceProvider.js.map +1 -1
- package/dist/src/core/bootstrap/providers/default-providers.d.ts +1 -1
- package/dist/src/core/bootstrap/providers/default-providers.d.ts.map +1 -1
- package/dist/src/core/bootstrap/providers/default-providers.js +44 -11
- package/dist/src/core/bootstrap/providers/default-providers.js.map +1 -1
- package/dist/src/core/bootstrap/providers/index.d.ts +9 -9
- package/dist/src/core/bootstrap/providers/index.d.ts.map +1 -1
- package/dist/src/core/bootstrap/providers/index.js +8 -8
- package/dist/src/core/bootstrap/providers/index.js.map +1 -1
- package/dist/src/core/bootstrap/providers/provider-order.d.ts +1 -1
- package/dist/src/core/bootstrap/providers/provider-order.d.ts.map +1 -1
- package/dist/src/core/bootstrap/ssr.d.ts +47 -0
- package/dist/src/core/bootstrap/ssr.d.ts.map +1 -0
- package/dist/src/core/bootstrap/ssr.js +129 -0
- package/dist/src/core/bootstrap/ssr.js.map +1 -0
- package/dist/src/core/cache/ViewCache.d.ts +1 -1
- package/dist/src/core/cache/ViewCache.d.ts.map +1 -1
- package/dist/src/core/cache/index.d.ts +1 -1
- package/dist/src/core/cache/index.d.ts.map +1 -1
- package/dist/src/core/cache/index.js +1 -1
- package/dist/src/core/cache/index.js.map +1 -1
- package/dist/src/core/contracts/ApplicationInterface.d.ts +15 -11
- package/dist/src/core/contracts/ApplicationInterface.d.ts.map +1 -1
- package/dist/src/core/contracts/BlockInterface.d.ts +10 -10
- package/dist/src/core/contracts/BlockInterface.d.ts.map +1 -1
- package/dist/src/core/contracts/BundleInterface.d.ts +38 -0
- package/dist/src/core/contracts/BundleInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/BundleInterface.js +2 -0
- package/dist/src/core/contracts/BundleInterface.js.map +1 -0
- package/dist/src/core/contracts/ComponentInterface.d.ts +7 -6
- package/dist/src/core/contracts/ComponentInterface.d.ts.map +1 -1
- package/dist/src/core/contracts/ElementInterface.d.ts +91 -27
- package/dist/src/core/contracts/ElementInterface.d.ts.map +1 -1
- package/dist/src/core/contracts/HeadServiceInterface.d.ts +37 -0
- package/dist/src/core/contracts/HeadServiceInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/HeadServiceInterface.js +3 -0
- package/dist/src/core/contracts/HeadServiceInterface.js.map +1 -0
- package/dist/src/core/contracts/HelperInterface.d.ts +9 -1
- package/dist/src/core/contracts/HelperInterface.d.ts.map +1 -1
- package/dist/src/core/contracts/HttpServiceInterface.d.ts +3 -0
- package/dist/src/core/contracts/HttpServiceInterface.d.ts.map +1 -1
- package/dist/src/core/contracts/MarkerInterface.d.ts +29 -44
- package/dist/src/core/contracts/MarkerInterface.d.ts.map +1 -1
- package/dist/src/core/contracts/ReactiveInterface.d.ts +9 -9
- package/dist/src/core/contracts/ReactiveInterface.d.ts.map +1 -1
- package/dist/src/core/contracts/RouterInterface.d.ts +5 -2
- package/dist/src/core/contracts/RouterInterface.d.ts.map +1 -1
- package/dist/src/core/contracts/SectionInterface.d.ts +48 -0
- package/dist/src/core/contracts/SectionInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/SectionInterface.js +2 -0
- package/dist/src/core/contracts/SectionInterface.js.map +1 -0
- package/dist/src/core/contracts/ServiceProviderInterface.d.ts +3 -3
- package/dist/src/core/contracts/ServiceProviderInterface.d.ts.map +1 -1
- package/dist/src/core/contracts/ViewControllerInterface.d.ts +116 -15
- package/dist/src/core/contracts/ViewControllerInterface.d.ts.map +1 -1
- package/dist/src/core/contracts/ViewInterface.d.ts +31 -5
- package/dist/src/core/contracts/ViewInterface.d.ts.map +1 -1
- package/dist/src/core/contracts/ViewManagerInterface.d.ts +47 -5
- package/dist/src/core/contracts/ViewManagerInterface.d.ts.map +1 -1
- package/dist/src/core/contracts/ViewStateInterface.d.ts +39 -2
- package/dist/src/core/contracts/ViewStateInterface.d.ts.map +1 -1
- package/dist/src/core/contracts/common.d.ts +3 -3
- package/dist/src/core/contracts/common.js +3 -3
- package/dist/src/core/contracts/utils.d.ts +19 -19
- package/dist/src/core/contracts/utils.d.ts.map +1 -1
- package/dist/src/core/contracts/views.d.ts +1 -22
- package/dist/src/core/contracts/views.d.ts.map +1 -1
- package/dist/src/core/devtools/hook.d.ts +66 -0
- package/dist/src/core/devtools/hook.d.ts.map +1 -0
- package/dist/src/core/devtools/hook.js +114 -0
- package/dist/src/core/devtools/hook.js.map +1 -0
- package/dist/src/core/devtools/index.d.ts +34 -0
- package/dist/src/core/devtools/index.d.ts.map +1 -0
- package/dist/src/core/devtools/index.js +33 -0
- package/dist/src/core/devtools/index.js.map +1 -0
- package/dist/src/core/devtools/inspector.d.ts +31 -0
- package/dist/src/core/devtools/inspector.d.ts.map +1 -0
- package/dist/src/core/devtools/inspector.js +249 -0
- package/dist/src/core/devtools/inspector.js.map +1 -0
- package/dist/src/core/elements/Block.d.ts +9 -7
- package/dist/src/core/elements/Block.d.ts.map +1 -1
- package/dist/src/core/elements/Block.js +33 -9
- package/dist/src/core/elements/Block.js.map +1 -1
- package/dist/src/core/elements/BlockOutlet.d.ts +19 -8
- package/dist/src/core/elements/BlockOutlet.d.ts.map +1 -1
- package/dist/src/core/elements/BlockOutlet.js +57 -13
- package/dist/src/core/elements/BlockOutlet.js.map +1 -1
- package/dist/src/core/elements/Component.d.ts +83 -11
- package/dist/src/core/elements/Component.d.ts.map +1 -1
- package/dist/src/core/elements/Component.js +334 -13
- package/dist/src/core/elements/Component.js.map +1 -1
- package/dist/src/core/elements/ForeachSlotCache.d.ts +95 -0
- package/dist/src/core/elements/ForeachSlotCache.d.ts.map +1 -0
- package/dist/src/core/elements/ForeachSlotCache.js +149 -0
- package/dist/src/core/elements/ForeachSlotCache.js.map +1 -0
- package/dist/src/core/elements/Fragment.d.ts +19 -13
- package/dist/src/core/elements/Fragment.d.ts.map +1 -1
- package/dist/src/core/elements/Fragment.js +43 -20
- package/dist/src/core/elements/Fragment.js.map +1 -1
- package/dist/src/core/elements/Html.d.ts +89 -12
- package/dist/src/core/elements/Html.d.ts.map +1 -1
- package/dist/src/core/elements/Html.js +707 -106
- package/dist/src/core/elements/Html.js.map +1 -1
- package/dist/src/core/elements/Output.d.ts +34 -10
- package/dist/src/core/elements/Output.d.ts.map +1 -1
- package/dist/src/core/elements/Output.js +141 -26
- package/dist/src/core/elements/Output.js.map +1 -1
- package/dist/src/core/elements/Reactive.d.ts +85 -14
- package/dist/src/core/elements/Reactive.d.ts.map +1 -1
- package/dist/src/core/elements/Reactive.js +315 -41
- package/dist/src/core/elements/Reactive.js.map +1 -1
- package/dist/src/core/elements/TextElement.d.ts +11 -7
- package/dist/src/core/elements/TextElement.d.ts.map +1 -1
- package/dist/src/core/elements/TextElement.js +18 -11
- package/dist/src/core/elements/TextElement.js.map +1 -1
- package/dist/src/core/elements/Wrapper.d.ts +24 -13
- package/dist/src/core/elements/Wrapper.d.ts.map +1 -1
- package/dist/src/core/elements/Wrapper.js +65 -8
- package/dist/src/core/elements/Wrapper.js.map +1 -1
- package/dist/src/core/elements/Yield.d.ts +10 -9
- package/dist/src/core/elements/Yield.d.ts.map +1 -1
- package/dist/src/core/elements/Yield.js +31 -19
- package/dist/src/core/elements/Yield.js.map +1 -1
- package/dist/src/core/elements/helpers.d.ts +7 -7
- package/dist/src/core/elements/helpers.d.ts.map +1 -1
- package/dist/src/core/elements/helpers.js +4 -4
- package/dist/src/core/elements/index.d.ts +12 -11
- package/dist/src/core/elements/index.d.ts.map +1 -1
- package/dist/src/core/elements/index.js +12 -11
- package/dist/src/core/elements/index.js.map +1 -1
- package/dist/src/core/elements/reconcileChildren.d.ts +7 -0
- package/dist/src/core/elements/reconcileChildren.d.ts.map +1 -0
- package/dist/src/core/elements/reconcileChildren.js +74 -0
- package/dist/src/core/elements/reconcileChildren.js.map +1 -0
- package/dist/src/core/helpers/ApiClient.d.ts.map +1 -0
- package/dist/src/core/{hellpers → helpers}/ApiClient.js +10 -1
- package/dist/src/core/helpers/ApiClient.js.map +1 -0
- package/dist/src/core/helpers/app.d.ts +3 -0
- package/dist/src/core/helpers/app.d.ts.map +1 -0
- package/dist/src/core/{hellpers → helpers}/app.js +11 -1
- package/dist/src/core/helpers/app.js.map +1 -0
- package/dist/src/core/helpers/transition.d.ts +34 -0
- package/dist/src/core/helpers/transition.d.ts.map +1 -0
- package/dist/src/core/helpers/transition.js +118 -0
- package/dist/src/core/helpers/transition.js.map +1 -0
- package/dist/src/core/{hellpers → helpers}/utils.d.ts +2 -2
- package/dist/src/core/helpers/utils.d.ts.map +1 -0
- package/dist/src/core/{hellpers → helpers}/utils.js +4 -4
- package/dist/src/core/helpers/utils.js.map +1 -0
- package/dist/src/core/helpers/view.d.ts +39 -0
- package/dist/src/core/helpers/view.d.ts.map +1 -0
- package/dist/src/core/helpers/view.js +286 -0
- package/dist/src/core/helpers/view.js.map +1 -0
- package/dist/src/core/routers/Router.d.ts +108 -7
- package/dist/src/core/routers/Router.d.ts.map +1 -1
- package/dist/src/core/routers/Router.js +444 -68
- package/dist/src/core/routers/Router.js.map +1 -1
- package/dist/src/core/routers/index.d.ts +2 -2
- package/dist/src/core/routers/index.d.ts.map +1 -1
- package/dist/src/core/routers/index.js +1 -1
- package/dist/src/core/routers/index.js.map +1 -1
- package/dist/src/core/services/AssetManager.d.ts +112 -0
- package/dist/src/core/services/AssetManager.d.ts.map +1 -0
- package/dist/src/core/services/AssetManager.js +361 -0
- package/dist/src/core/services/AssetManager.js.map +1 -0
- package/dist/src/core/services/BlockManager.d.ts +73 -2
- package/dist/src/core/services/BlockManager.d.ts.map +1 -1
- package/dist/src/core/services/BlockManager.js +270 -48
- package/dist/src/core/services/BlockManager.js.map +1 -1
- package/dist/src/core/services/DomService.js +3 -3
- package/dist/src/core/services/DomService.js.map +1 -1
- package/dist/src/core/services/HeadService.d.ts +24 -0
- package/dist/src/core/services/HeadService.d.ts.map +1 -0
- package/dist/src/core/services/HeadService.js +120 -0
- package/dist/src/core/services/HeadService.js.map +1 -0
- package/dist/src/core/services/HelperService.d.ts +14 -3
- package/dist/src/core/services/HelperService.d.ts.map +1 -1
- package/dist/src/core/services/HelperService.js +32 -2
- package/dist/src/core/services/HelperService.js.map +1 -1
- package/dist/src/core/services/HttpService.d.ts +8 -0
- package/dist/src/core/services/HttpService.d.ts.map +1 -1
- package/dist/src/core/services/HttpService.js +121 -47
- package/dist/src/core/services/HttpService.js.map +1 -1
- package/dist/src/core/services/MarkerRegistry.d.ts +51 -8
- package/dist/src/core/services/MarkerRegistry.d.ts.map +1 -1
- package/dist/src/core/services/MarkerRegistry.js +135 -14
- package/dist/src/core/services/MarkerRegistry.js.map +1 -1
- package/dist/src/core/services/PageCache.d.ts +101 -0
- package/dist/src/core/services/PageCache.d.ts.map +1 -0
- package/dist/src/core/services/PageCache.js +184 -0
- package/dist/src/core/services/PageCache.js.map +1 -0
- package/dist/src/core/services/SectionManager.d.ts +100 -0
- package/dist/src/core/services/SectionManager.d.ts.map +1 -0
- package/dist/src/core/services/SectionManager.js +408 -0
- package/dist/src/core/services/SectionManager.js.map +1 -0
- package/dist/src/core/services/StorageService.js +1 -1
- package/dist/src/core/services/TranslationService.d.ts +21 -0
- package/dist/src/core/services/TranslationService.d.ts.map +1 -0
- package/dist/src/core/services/TranslationService.js +95 -0
- package/dist/src/core/services/TranslationService.js.map +1 -0
- package/dist/src/core/services/index.d.ts +11 -8
- package/dist/src/core/services/index.d.ts.map +1 -1
- package/dist/src/core/services/index.js +11 -8
- package/dist/src/core/services/index.js.map +1 -1
- package/dist/src/core/types/utils.d.ts +8 -8
- package/dist/src/core/types/utils.d.ts.map +1 -1
- package/dist/src/core/view/LoopContext.d.ts +21 -1
- package/dist/src/core/view/LoopContext.d.ts.map +1 -1
- package/dist/src/core/view/LoopContext.js +36 -0
- package/dist/src/core/view/LoopContext.js.map +1 -1
- package/dist/src/core/view/ResourceScope.d.ts +38 -0
- package/dist/src/core/view/ResourceScope.d.ts.map +1 -0
- package/dist/src/core/view/ResourceScope.js +167 -0
- package/dist/src/core/view/ResourceScope.js.map +1 -0
- package/dist/src/core/view/Section.d.ts +17 -32
- package/dist/src/core/view/Section.d.ts.map +1 -1
- package/dist/src/core/view/Section.js +14 -55
- package/dist/src/core/view/Section.js.map +1 -1
- package/dist/src/core/view/View.d.ts +43 -7
- package/dist/src/core/view/View.d.ts.map +1 -1
- package/dist/src/core/view/View.js +22 -4
- package/dist/src/core/view/View.js.map +1 -1
- package/dist/src/core/view/ViewController.d.ts +313 -40
- package/dist/src/core/view/ViewController.d.ts.map +1 -1
- package/dist/src/core/view/ViewController.js +1002 -147
- package/dist/src/core/view/ViewController.js.map +1 -1
- package/dist/src/core/view/ViewManager.d.ts +318 -27
- package/dist/src/core/view/ViewManager.d.ts.map +1 -1
- package/dist/src/core/view/ViewManager.js +1199 -258
- package/dist/src/core/view/ViewManager.js.map +1 -1
- package/dist/src/core/view/ViewState.d.ts +185 -6
- package/dist/src/core/view/ViewState.d.ts.map +1 -1
- package/dist/src/core/view/ViewState.js +851 -123
- package/dist/src/core/view/ViewState.js.map +1 -1
- package/dist/src/core/view/index.d.ts +5 -5
- package/dist/src/core/view/index.d.ts.map +1 -1
- package/dist/src/core/view/index.js +5 -5
- package/dist/src/core/view/index.js.map +1 -1
- package/dist/src/testing/index.d.ts +69 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/testing/index.js +140 -0
- package/dist/src/testing/index.js.map +1 -0
- package/index.ts +29 -5
- package/package.json +30 -21
- package/plugins.ts +2 -2
- package/src/core/app/Application.ts +72 -31
- package/src/core/bootstrap/app.ts +84 -6
- package/src/core/bootstrap/bundle.ts +101 -0
- package/src/core/bootstrap/providers/ApiServiceProvider.ts +4 -4
- package/src/core/bootstrap/providers/CoreServiceProvider.ts +14 -8
- package/src/core/bootstrap/providers/HelperServiceProvider.ts +8 -3
- package/src/core/bootstrap/providers/RouteServiceProvider.ts +15 -4
- package/src/core/bootstrap/providers/ServiceProvider.ts +8 -8
- package/src/core/bootstrap/providers/ViewServiceProvider.ts +3 -3
- package/src/core/bootstrap/providers/default-providers.ts +51 -16
- package/src/core/bootstrap/providers/index.ts +9 -9
- package/src/core/bootstrap/providers/provider-order.test.ts +16 -27
- package/src/core/bootstrap/providers/provider-order.ts +1 -1
- package/src/core/bootstrap/ssr.ts +141 -0
- package/src/core/cache/ViewCache.ts +1 -1
- package/src/core/cache/index.ts +1 -1
- package/src/core/contracts/ApplicationInterface.ts +15 -11
- package/src/core/contracts/BlockInterface.ts +18 -18
- package/src/core/contracts/BundleInterface.ts +40 -0
- package/src/core/contracts/ComponentInterface.ts +7 -6
- package/src/core/contracts/ElementInterface.ts +95 -27
- package/src/core/contracts/HeadServiceInterface.ts +41 -0
- package/src/core/contracts/HelperInterface.ts +9 -1
- package/src/core/contracts/HttpServiceInterface.ts +3 -0
- package/src/core/contracts/MarkerInterface.ts +22 -49
- package/src/core/contracts/ReactiveInterface.ts +9 -9
- package/src/core/contracts/RouterInterface.ts +5 -2
- package/src/core/contracts/SectionInterface.ts +49 -0
- package/src/core/contracts/ServiceProviderInterface.ts +3 -3
- package/src/core/contracts/ViewControllerInterface.ts +124 -16
- package/src/core/contracts/ViewInterface.ts +34 -5
- package/src/core/contracts/ViewManagerInterface.ts +40 -5
- package/src/core/contracts/ViewStateInterface.ts +39 -2
- package/src/core/contracts/common.ts +3 -3
- package/src/core/contracts/utils.ts +19 -19
- package/src/core/contracts/views.ts +2 -22
- package/src/core/devtools/hook.ts +142 -0
- package/src/core/devtools/index.ts +38 -0
- package/src/core/devtools/inspector.ts +270 -0
- package/src/core/elements/Block.ts +43 -21
- package/src/core/elements/BlockOutlet.ts +61 -21
- package/src/core/elements/Component.ts +352 -18
- package/src/core/elements/ElementManager.ts +9 -9
- package/src/core/elements/ForeachSlotCache.ts +170 -0
- package/src/core/elements/Fragment.ts +50 -32
- package/src/core/elements/Html.ts +696 -117
- package/src/core/elements/Output.ts +147 -30
- package/src/core/elements/Reactive.ts +329 -52
- package/src/core/elements/TextElement.ts +25 -16
- package/src/core/elements/Wrapper.ts +84 -18
- package/src/core/elements/Yield.ts +35 -24
- package/src/core/elements/helpers.ts +12 -12
- package/src/core/elements/index.ts +25 -24
- package/src/core/elements/reconcileChildren.ts +70 -0
- package/src/core/{hellpers → helpers}/ApiClient.ts +8 -1
- package/src/core/{hellpers → helpers}/app.ts +13 -2
- package/src/core/helpers/transition.ts +127 -0
- package/src/core/{hellpers → helpers}/utils.ts +4 -4
- package/src/core/helpers/view.ts +283 -0
- package/src/core/routers/Router.ts +458 -64
- package/src/core/routers/index.ts +2 -2
- package/src/core/services/AssetManager.ts +387 -0
- package/src/core/services/BlockManager.ts +272 -50
- package/src/core/services/DomService.ts +3 -3
- package/src/core/services/HeadService.ts +124 -0
- package/src/core/services/HelperService.ts +34 -4
- package/src/core/services/HttpService.ts +116 -55
- package/src/core/services/MarkerRegistry.ts +142 -16
- package/src/core/services/PageCache.ts +239 -0
- package/src/core/services/SectionManager.ts +387 -0
- package/src/core/services/StorageService.ts +1 -1
- package/src/core/services/TranslationService.ts +91 -0
- package/src/core/services/index.ts +11 -8
- package/src/core/types/utils.ts +8 -8
- package/src/core/view/LoopContext.ts +38 -1
- package/src/core/view/ResourceScope.ts +174 -0
- package/src/core/view/Section.ts +21 -68
- package/src/core/view/View.ts +57 -8
- package/src/core/view/ViewController.ts +1070 -186
- package/src/core/view/ViewManager.ts +1346 -272
- package/src/core/view/ViewState.ts +834 -116
- package/src/core/view/index.ts +5 -5
- package/src/testing/index.ts +184 -0
- package/dist/src/core/hellpers/ApiClient.d.ts.map +0 -1
- package/dist/src/core/hellpers/ApiClient.js.map +0 -1
- package/dist/src/core/hellpers/app.d.ts +0 -3
- package/dist/src/core/hellpers/app.d.ts.map +0 -1
- package/dist/src/core/hellpers/app.js.map +0 -1
- package/dist/src/core/hellpers/utils.d.ts.map +0 -1
- package/dist/src/core/hellpers/utils.js.map +0 -1
- package/dist/src/core/services/MarkerService.d.ts +0 -299
- package/dist/src/core/services/MarkerService.d.ts.map +0 -1
- package/dist/src/core/services/MarkerService.js +0 -723
- package/dist/src/core/services/MarkerService.js.map +0 -1
- package/src/core/services/MarkerService.ts +0 -923
- /package/dist/src/core/{hellpers → helpers}/ApiClient.d.ts +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export interface TranslationConfig {
|
|
2
|
+
locale?: string;
|
|
3
|
+
fallbackLocale?: string;
|
|
4
|
+
messages?: Record<string, { json?: Record<string, string>; groups?: Record<string, any> }>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/** Synchronous lookup of dictionaries already present in the application bundle. */
|
|
8
|
+
export class TranslationService {
|
|
9
|
+
locale = 'en';
|
|
10
|
+
fallbackLocale = 'en';
|
|
11
|
+
private messages: NonNullable<TranslationConfig['messages']> = {};
|
|
12
|
+
|
|
13
|
+
init(config: TranslationConfig): void {
|
|
14
|
+
this.locale = config.locale || 'en';
|
|
15
|
+
this.fallbackLocale = config.fallbackLocale || 'en';
|
|
16
|
+
this.messages = config.messages || {};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
setLocale(locale: string): boolean {
|
|
20
|
+
if (!Object.prototype.hasOwnProperty.call(this.messages, locale)) throw new Error(`Locale is not bundled: ${locale}`);
|
|
21
|
+
if (locale === this.locale) return false;
|
|
22
|
+
this.locale = locale;
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
trans(key: string, replace: Record<string, any> = {}, locale = this.locale): string {
|
|
27
|
+
const value = this.lookup(key, locale) ?? this.lookup(key, this.fallbackLocale) ?? key;
|
|
28
|
+
return this.interpolate(value, replace);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
choice(key: string, count: number, replace: Record<string, any> = {}, locale = this.locale): string {
|
|
32
|
+
const local = this.lookup(key, locale);
|
|
33
|
+
const value = local ?? this.lookup(key, this.fallbackLocale) ?? key;
|
|
34
|
+
const pluralLocale = local === undefined ? this.fallbackLocale : locale;
|
|
35
|
+
const options = value.split('|');
|
|
36
|
+
let selected: string | undefined;
|
|
37
|
+
for (const option of options) {
|
|
38
|
+
const exact = option.match(/^\s*\{([^}]+)\}\s*(.*)$/s);
|
|
39
|
+
if (exact && exact[1].split(',').some(n => Number(n.trim()) === count)) selected = exact[2];
|
|
40
|
+
const range = option.match(/^\s*\[([^,]+),([^\]]+)\]\s*(.*)$/s);
|
|
41
|
+
if (range) {
|
|
42
|
+
const min = range[1].trim() === '*' ? -Infinity : Number(range[1]);
|
|
43
|
+
const max = range[2].trim() === '*' ? Infinity : Number(range[2]);
|
|
44
|
+
if (count >= min && count <= max) selected = range[3];
|
|
45
|
+
}
|
|
46
|
+
if (selected !== undefined) break;
|
|
47
|
+
}
|
|
48
|
+
if (selected === undefined) {
|
|
49
|
+
const forms = options.filter(option => !/^\s*[\{\[]/.test(option));
|
|
50
|
+
let index = 0;
|
|
51
|
+
try {
|
|
52
|
+
const rule = new Intl.PluralRules(pluralLocale.replace('_', '-'));
|
|
53
|
+
const category = rule.select(count);
|
|
54
|
+
const order = ['zero', 'one', 'two', 'few', 'many', 'other'];
|
|
55
|
+
const categories = rule.resolvedOptions().pluralCategories;
|
|
56
|
+
index = order.filter(c => categories.includes(c as Intl.LDMLPluralRule)).indexOf(category);
|
|
57
|
+
} catch { index = count === 1 ? 0 : 1; }
|
|
58
|
+
selected = forms[Math.min(Math.max(index, 0), forms.length - 1)] ?? options[0];
|
|
59
|
+
}
|
|
60
|
+
return this.interpolate(selected.trim(), { ...replace, count });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private lookup(key: string, locale: string): string | undefined {
|
|
64
|
+
const catalog = this.messages[locale];
|
|
65
|
+
if (!catalog) return undefined;
|
|
66
|
+
if (catalog.json && Object.prototype.hasOwnProperty.call(catalog.json, key)) return catalog.json[key];
|
|
67
|
+
const groups = catalog.groups || {};
|
|
68
|
+
// Longest group match also supports package::group.item and nested group paths.
|
|
69
|
+
const group = Object.keys(groups).filter(name => key.startsWith(name + '.')).sort((a, b) => b.length - a.length)[0];
|
|
70
|
+
if (!group) return undefined;
|
|
71
|
+
let value: any = groups[group];
|
|
72
|
+
for (const part of key.slice(group.length + 1).split('.')) {
|
|
73
|
+
if (!value || !Object.prototype.hasOwnProperty.call(value, part)) return undefined;
|
|
74
|
+
value = value[part];
|
|
75
|
+
}
|
|
76
|
+
return typeof value === 'string' ? value : undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private interpolate(value: string, replace: Record<string, any>): string {
|
|
80
|
+
const keys = Object.keys(replace).sort((a, b) => b.length - a.length);
|
|
81
|
+
for (const key of keys) {
|
|
82
|
+
const text = String(replace[key]);
|
|
83
|
+
const variants: Record<string, string> = {
|
|
84
|
+
[key]: text, [key.toUpperCase()]: text.toUpperCase(),
|
|
85
|
+
[key.charAt(0).toUpperCase() + key.slice(1)]: text.charAt(0).toUpperCase() + text.slice(1),
|
|
86
|
+
};
|
|
87
|
+
for (const [name, content] of Object.entries(variants)) value = value.split(':' + name).join(content);
|
|
88
|
+
}
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export * from "./
|
|
3
|
-
export * from "./
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
1
|
+
export * from "./AssetManager.js";
|
|
2
|
+
export * from "./BlockManager.js";
|
|
3
|
+
export * from "./EventService.js";
|
|
4
|
+
export * from "./HeadService.js";
|
|
5
|
+
export * from "./SectionManager.js";
|
|
6
|
+
export * from "./LoggerService.js";
|
|
7
|
+
export * from './HelperService.js';
|
|
8
|
+
export * from './HttpService.js';
|
|
9
|
+
export * from './MarkerRegistry.js';
|
|
10
|
+
export * from "./StoreService.js";
|
|
11
|
+
export * from "./StorageService.js";
|
package/src/core/types/utils.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type SaoObjectType = 'Application' | 'App' | 'ServiceProvider' | 'Html' | 'Reactive' | 'TextElement' | 'Fragment' | 'Wrapper' | 'Block' | 'BlockOutlet' | 'Output' | 'ViewController' | 'View' | 'Service' | 'Router' | 'App' | 'Component' | 'Unknown' | 'Yield' | '' | null; // Extendable for custom types
|
|
2
2
|
export enum OOTEnum {
|
|
3
3
|
APPLICATION = 'Application',
|
|
4
4
|
HTML = 'Html',
|
|
@@ -21,10 +21,10 @@ export enum OOTEnum {
|
|
|
21
21
|
}
|
|
22
22
|
// ─── Re-exports from contracts (backward compatibility) ─────────
|
|
23
23
|
|
|
24
|
-
export type {
|
|
25
|
-
export type { ReactiveRenderFn, ReactiveChildrenFactory, ReactiveConfig } from "../contracts/ReactiveInterface";
|
|
26
|
-
export type { BlockRenderFactory } from "../contracts/BlockInterface";
|
|
27
|
-
export type { ViewRenderFactory, ViewConstructor, ViewLifecycleHooks } from "../contracts/ViewInterface";
|
|
28
|
-
export type { StateListener, MultiKeyStateListener, StateItem } from "../contracts/ViewStateInterface";
|
|
29
|
-
export type { ServiceKey, ServiceFactory, ServiceBinding } from "../contracts/ApplicationInterface";
|
|
30
|
-
export type { ViewConfig, ViewRuntimeConfig } from "../contracts/ViewControllerInterface";
|
|
24
|
+
export type { SaoElementConfig, HtmlElementConfig, SaoElementEventHandler, SaoElementChildren, SaoChildrenFactoryOutput, SaoChildrenFactory } from "../contracts/ElementInterface.js";
|
|
25
|
+
export type { ReactiveRenderFn, ReactiveChildrenFactory, ReactiveConfig } from "../contracts/ReactiveInterface.js";
|
|
26
|
+
export type { BlockRenderFactory } from "../contracts/BlockInterface.js";
|
|
27
|
+
export type { ViewRenderFactory, ViewConstructor, ViewLifecycleHooks } from "../contracts/ViewInterface.js";
|
|
28
|
+
export type { StateListener, MultiKeyStateListener, StateItem } from "../contracts/ViewStateInterface.js";
|
|
29
|
+
export type { ServiceKey, ServiceFactory, ServiceBinding } from "../contracts/ApplicationInterface.js";
|
|
30
|
+
export type { ViewConfig, ViewRuntimeConfig } from "../contracts/ViewControllerInterface.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LoopContextInterface } from "../contracts/LoopContextInterface";
|
|
1
|
+
import type { LoopContextInterface } from "../contracts/LoopContextInterface.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* LoopContext — provides loop metadata for @foreach, @for, @while directives.
|
|
@@ -73,6 +73,43 @@ export class LoopContext implements LoopContextInterface {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Bản chụp BẤT BIẾN của trạng thái hiện tại — dùng cho `loop` mà template
|
|
78
|
+
* nhìn thấy trong `@foreach`.
|
|
79
|
+
*
|
|
80
|
+
* Vì sao cần: LoopContext là MỘT object dùng chung, bị mutate qua từng vòng
|
|
81
|
+
* (`setCurrentTimes`). Trong khi đó element con được tạo bởi
|
|
82
|
+
* `childrenFactory` — closure chạy MUỘN (lúc Html.renderChildren), SAU khi
|
|
83
|
+
* vòng lặp đã kết thúc. Closure bắt `loop` theo THAM CHIẾU nên mọi hàng đọc
|
|
84
|
+
* ra cùng một giá trị: trạng thái CUỐI cùng.
|
|
85
|
+
* Đo được: list 6 phần tử, `@click(remove(loop.index))` ở mọi hàng đều nhận
|
|
86
|
+
* index = 5 → bấm hàng nào cũng xoá phần tử cuối, rồi tắc hẳn.
|
|
87
|
+
* `__loopIndex` không dính vì nó là tham số theo từng lần gọi callback (mỗi
|
|
88
|
+
* closure một binding riêng), không phải object chia sẻ.
|
|
89
|
+
*
|
|
90
|
+
* `parent` cũng chụp đệ quy: loop lồng nhau có cùng vấn đề ở mức ngoài.
|
|
91
|
+
* Trả về plain object đã freeze — chỉ đọc, không có `next()` (không ai gọi
|
|
92
|
+
* `next()` trên `loop` của `@foreach`; `@for`/`@while` vẫn nhận context
|
|
93
|
+
* MUTABLE vì codegen của chúng cần `setCurrentTimes`/`next`).
|
|
94
|
+
*/
|
|
95
|
+
snapshot(): LoopContextInterface {
|
|
96
|
+
return Object.freeze({
|
|
97
|
+
index: this.index,
|
|
98
|
+
iteration: this.iteration,
|
|
99
|
+
count: this.count,
|
|
100
|
+
remaining: this.remaining,
|
|
101
|
+
first: this.first,
|
|
102
|
+
last: this.last,
|
|
103
|
+
odd: this.odd,
|
|
104
|
+
even: this.even,
|
|
105
|
+
depth: this.depth,
|
|
106
|
+
parent: this.parent ? this.parent.snapshot() : null,
|
|
107
|
+
next(): void {
|
|
108
|
+
console.warn('[LoopContext] `loop` trong @foreach là bản chụp chỉ đọc — next() không có tác dụng.');
|
|
109
|
+
},
|
|
110
|
+
}) as LoopContextInterface;
|
|
111
|
+
}
|
|
112
|
+
|
|
76
113
|
reset(): void {
|
|
77
114
|
this.iteration = 0;
|
|
78
115
|
this.index = 0;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { StateManagerInterface } from '../contracts/ViewStateInterface.js';
|
|
2
|
+
|
|
3
|
+
export type Cleanup = () => void;
|
|
4
|
+
export interface WatchContext {
|
|
5
|
+
readonly signal: AbortSignal;
|
|
6
|
+
/** False after a dependency change, pause, stop, or destroy. */
|
|
7
|
+
readonly current: boolean;
|
|
8
|
+
onCleanup(cleanup: Cleanup): void;
|
|
9
|
+
/** Apply an async result only while this run still owns the scope. */
|
|
10
|
+
commit(update: () => void): boolean;
|
|
11
|
+
}
|
|
12
|
+
export type WatchCallback = (values: Readonly<Record<string, any>>, context: WatchContext) =>
|
|
13
|
+
void | Cleanup | Promise<void | Cleanup>;
|
|
14
|
+
export interface WatchOptions {
|
|
15
|
+
immediate?: boolean;
|
|
16
|
+
/** dom (default) runs after bindings/regions; state runs before regions. */
|
|
17
|
+
flush?: 'state' | 'dom';
|
|
18
|
+
}
|
|
19
|
+
type WatchRecord = {
|
|
20
|
+
keys: string[];
|
|
21
|
+
callback: WatchCallback;
|
|
22
|
+
options: WatchOptions;
|
|
23
|
+
live: boolean;
|
|
24
|
+
dirty: boolean;
|
|
25
|
+
cancelQueued?: Cleanup;
|
|
26
|
+
disposeRun?: Cleanup;
|
|
27
|
+
off: Cleanup;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Explicit dependencies and ownership; no global hook order or implicit tracking. */
|
|
31
|
+
export class ResourceScope {
|
|
32
|
+
private records = new Set<WatchRecord>();
|
|
33
|
+
private cleanups = new Set<Cleanup>();
|
|
34
|
+
private active = false;
|
|
35
|
+
private destroyed = false;
|
|
36
|
+
|
|
37
|
+
constructor(
|
|
38
|
+
private states: StateManagerInterface,
|
|
39
|
+
private afterDom: (callback: () => void) => Cleanup,
|
|
40
|
+
private reportError: (error: unknown) => void = error => console.error('[ResourceScope]', error),
|
|
41
|
+
) {}
|
|
42
|
+
|
|
43
|
+
watch(keys: readonly string[], callback: WatchCallback, options: WatchOptions = {}): Cleanup {
|
|
44
|
+
if (this.destroyed) return () => {};
|
|
45
|
+
const record: WatchRecord = {
|
|
46
|
+
keys: [...new Set(keys)], callback, options: { ...options },
|
|
47
|
+
live: true, dirty: options.immediate !== false, off: () => {},
|
|
48
|
+
};
|
|
49
|
+
record.off = this.states.subscribe(record.keys, () => {
|
|
50
|
+
// Abort stale work immediately, even when the next run waits for DOM.
|
|
51
|
+
this.invalidate(record);
|
|
52
|
+
record.dirty = true;
|
|
53
|
+
this.schedule(record);
|
|
54
|
+
});
|
|
55
|
+
this.records.add(record);
|
|
56
|
+
this.schedule(record);
|
|
57
|
+
return () => {
|
|
58
|
+
if (!record.live) return;
|
|
59
|
+
record.live = false;
|
|
60
|
+
record.off();
|
|
61
|
+
this.invalidate(record);
|
|
62
|
+
this.records.delete(record);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Own a resource until scope destruction (pause-safe resources only). */
|
|
67
|
+
defer(cleanup: Cleanup): Cleanup {
|
|
68
|
+
let live = true;
|
|
69
|
+
const dispose = () => {
|
|
70
|
+
if (!live) return;
|
|
71
|
+
live = false;
|
|
72
|
+
this.cleanups.delete(dispose);
|
|
73
|
+
this.safely(cleanup);
|
|
74
|
+
};
|
|
75
|
+
if (this.destroyed) dispose();
|
|
76
|
+
else this.cleanups.add(dispose);
|
|
77
|
+
return dispose;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
resume(): void {
|
|
81
|
+
if (this.destroyed || this.active) return;
|
|
82
|
+
this.active = true;
|
|
83
|
+
for (const record of this.records) this.schedule(record);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
pause(): void {
|
|
87
|
+
if (!this.active) return;
|
|
88
|
+
this.active = false;
|
|
89
|
+
for (const record of this.records) {
|
|
90
|
+
if (record.disposeRun || record.cancelQueued) record.dirty = true;
|
|
91
|
+
this.invalidate(record);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
destroy(): void {
|
|
96
|
+
if (this.destroyed) return;
|
|
97
|
+
this.destroyed = true;
|
|
98
|
+
this.active = false;
|
|
99
|
+
for (const record of this.records) {
|
|
100
|
+
record.live = false;
|
|
101
|
+
record.off();
|
|
102
|
+
this.invalidate(record);
|
|
103
|
+
}
|
|
104
|
+
this.records.clear();
|
|
105
|
+
for (const cleanup of Array.from(this.cleanups).reverse()) cleanup();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private invalidate(record: WatchRecord): void {
|
|
109
|
+
record.cancelQueued?.();
|
|
110
|
+
record.cancelQueued = undefined;
|
|
111
|
+
const dispose = record.disposeRun;
|
|
112
|
+
record.disposeRun = undefined;
|
|
113
|
+
dispose?.();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private schedule(record: WatchRecord): void {
|
|
117
|
+
if (!this.active || !record.live || !record.dirty || record.cancelQueued) return;
|
|
118
|
+
if (record.options.flush === 'state') this.run(record);
|
|
119
|
+
else record.cancelQueued = this.afterDom(() => {
|
|
120
|
+
record.cancelQueued = undefined;
|
|
121
|
+
this.run(record);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private run(record: WatchRecord): void {
|
|
126
|
+
if (!this.active || !record.live || !record.dirty) return;
|
|
127
|
+
record.dirty = false;
|
|
128
|
+
const controller = new AbortController();
|
|
129
|
+
const cleanups: Cleanup[] = [];
|
|
130
|
+
const versions = record.keys.map(key => this.states.getStateVersion(key));
|
|
131
|
+
let current = true;
|
|
132
|
+
const isCurrent = () => current && record.keys.every((key, i) => this.states.getStateVersion(key) === versions[i]);
|
|
133
|
+
const onCleanup = (cleanup: Cleanup) => {
|
|
134
|
+
if (isCurrent()) cleanups.push(cleanup);
|
|
135
|
+
else this.safely(cleanup);
|
|
136
|
+
};
|
|
137
|
+
record.disposeRun = () => {
|
|
138
|
+
if (!current) return;
|
|
139
|
+
current = false;
|
|
140
|
+
controller.abort();
|
|
141
|
+
for (const cleanup of cleanups.splice(0).reverse()) this.safely(cleanup);
|
|
142
|
+
};
|
|
143
|
+
const context: WatchContext = {
|
|
144
|
+
signal: controller.signal,
|
|
145
|
+
get current() { return isCurrent(); },
|
|
146
|
+
onCleanup,
|
|
147
|
+
commit: update => {
|
|
148
|
+
if (!isCurrent()) return false;
|
|
149
|
+
update();
|
|
150
|
+
return true;
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
const values: Record<string, any> = {};
|
|
154
|
+
try {
|
|
155
|
+
for (const key of record.keys) values[key] = this.states.getStateByKey(key);
|
|
156
|
+
const result = record.callback(Object.freeze(values), context);
|
|
157
|
+
if (typeof result === 'function') onCleanup(result);
|
|
158
|
+
else if (result) Promise.resolve(result).then(cleanup => {
|
|
159
|
+
if (typeof cleanup === 'function') onCleanup(cleanup);
|
|
160
|
+
}, error => {
|
|
161
|
+
if (isCurrent() && !controller.signal.aborted) this.reportError(error);
|
|
162
|
+
});
|
|
163
|
+
} catch (error) {
|
|
164
|
+
const dispose = record.disposeRun;
|
|
165
|
+
record.disposeRun = undefined;
|
|
166
|
+
dispose?.();
|
|
167
|
+
this.reportError(error);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private safely(cleanup: Cleanup): void {
|
|
172
|
+
try { cleanup(); } catch (error) { this.reportError(error); }
|
|
173
|
+
}
|
|
174
|
+
}
|
package/src/core/view/Section.ts
CHANGED
|
@@ -1,86 +1,39 @@
|
|
|
1
|
-
import { HtmlInterface } from "../contracts/ElementInterface";
|
|
2
|
-
import { ViewControllerInterface } from "../contracts/ViewControllerInterface";
|
|
3
|
-
import { SectionConstruvtorArgs, SectionContentRenderer, SectionContentType, SectionInterface, SectionItemType } from "../contracts/
|
|
1
|
+
import { HtmlInterface } from "../contracts/ElementInterface.js";
|
|
2
|
+
import { ViewControllerInterface } from "../contracts/ViewControllerInterface.js";
|
|
3
|
+
import { SectionConstruvtorArgs, SectionContentRenderer, SectionContentType, SectionInterface, SectionItemType } from "../contracts/SectionInterface.js";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Section — a `@section(name, ...)` declaration.
|
|
7
|
+
*
|
|
8
|
+
* A thin data holder: it does NOT render or subscribe itself. Sections are
|
|
9
|
+
* declared by a page/layout (often before its matching @yield even exists —
|
|
10
|
+
* same cross-controller ordering as Block/BlockOutlet), so SectionManager
|
|
11
|
+
* owns evaluation, DOM insertion, and reactive re-apply (mirrors how
|
|
12
|
+
* BlockManager owns Block/BlockOutlet mounting instead of Block itself).
|
|
13
|
+
*/
|
|
5
14
|
export class Section implements SectionInterface {
|
|
6
15
|
ctx: ViewControllerInterface | undefined | null = null;
|
|
16
|
+
/** Owning view instance — keys this section as `name + viewId` in SectionManager, same as Block. */
|
|
17
|
+
viewId: string;
|
|
7
18
|
name: string;
|
|
8
19
|
type: SectionItemType;
|
|
9
20
|
contentType: SectionContentType;
|
|
10
|
-
stateKeys
|
|
11
|
-
|
|
12
|
-
renderFactory: SectionContentRenderer;
|
|
13
|
-
subscribeFn: (() => void) | null = null;
|
|
14
|
-
unsubscribeFn: (() => void) | null = null;
|
|
15
|
-
|
|
16
|
-
content: any = null; // Can be string or OneElementChildren depending on contentType
|
|
17
|
-
|
|
18
|
-
parentElement: HtmlInterface | undefined | null = null; // For dynamic/reactive sections that need a parent element reference
|
|
21
|
+
stateKeys: string[];
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
renderFactory: SectionContentRenderer;
|
|
21
24
|
|
|
22
25
|
constructor({ ctx, name, type = 'static', contentType = 'text', stateKeys, renderFactory }: SectionConstruvtorArgs) {
|
|
23
26
|
this.ctx = ctx ?? null;
|
|
27
|
+
this.viewId = ctx?.viewId ?? '';
|
|
24
28
|
this.name = name;
|
|
25
29
|
this.type = type;
|
|
26
30
|
this.contentType = contentType;
|
|
27
|
-
this.stateKeys = stateKeys;
|
|
31
|
+
this.stateKeys = stateKeys ?? [];
|
|
28
32
|
this.renderFactory = renderFactory;
|
|
29
|
-
|
|
30
|
-
this.initialize();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
initialize() {
|
|
34
|
-
if (this.type === 'static') {
|
|
35
|
-
this.render();
|
|
36
|
-
}
|
|
37
|
-
this.start();
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
render(parentElement?: HtmlInterface | null | undefined) {
|
|
42
|
-
this.parentElement = parentElement ?? null;
|
|
43
|
-
this.content = this.renderFactory(this.parentElement);
|
|
44
33
|
}
|
|
45
34
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
this.
|
|
49
|
-
if ((this.type === 'reactive' || this.type === 'dynamic') && this.stateKeys && this.ctx) {
|
|
50
|
-
this.subscribeFn = () => {
|
|
51
|
-
this.render(this.parentElement);
|
|
52
|
-
};
|
|
53
|
-
this.unsubscribeFn = this.ctx.states.subscribe(this.stateKeys, this.subscribeFn);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
stop() {
|
|
59
|
-
if (!this.isStarted) return;
|
|
60
|
-
this.isStarted = false;
|
|
61
|
-
if (this.unsubscribeFn) {
|
|
62
|
-
this.unsubscribeFn();
|
|
63
|
-
this.unsubscribeFn = null;
|
|
64
|
-
}
|
|
35
|
+
/** Evaluate current content. `parentElement` only meaningful for contentType 'html'. */
|
|
36
|
+
evaluate(parentElement?: HtmlInterface | null) {
|
|
37
|
+
return this.renderFactory(parentElement ?? null);
|
|
65
38
|
}
|
|
66
39
|
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Note: The "Section" concept is being phased out in favor of a more flexible Block/BlockOutlet system.
|
|
70
|
-
* Sections are still supported for backward compatibility but may be deprecated in future versions.
|
|
71
|
-
* For new development, consider using Blocks instead of Sections for better flexibility and composability.
|
|
72
|
-
*
|
|
73
|
-
* --- CHANGELOG ---
|
|
74
|
-
* vX.X.X - Introduced Block/BlockOutlet system as a more flexible alternative to Sections.
|
|
75
|
-
* - Sections are now considered legacy and may be deprecated in future versions.
|
|
76
|
-
* - ViewController's section management is being refactored to support Blocks instead of Sections.
|
|
77
|
-
* - Updated documentation and examples to encourage use of Blocks over Sections.
|
|
78
|
-
*
|
|
79
|
-
* --- FUTURE PLANS ---
|
|
80
|
-
* - Phase out Sections entirely in favor of Blocks, which offer more composability and dynamic capabilities.
|
|
81
|
-
* - Provide migration guides for existing users to transition from Sections to Blocks.
|
|
82
|
-
* - Enhance Block system with features like nested blocks, dynamic block registration, and improved lifecycle management.
|
|
83
|
-
*
|
|
84
|
-
* --- CURRENT RECOMMENDATION ---
|
|
85
|
-
* For new views, prefer using Blocks and BlockOutlets for layout composition instead of Sections. Sections will still work but are not recommended for new development.
|
|
86
|
-
*/
|
package/src/core/view/View.ts
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import type { ViewControllerInterface, ViewType } from "../contracts/ViewControllerInterface";
|
|
2
|
-
import type { ViewInterface, ViewLifecycleHooks } from "../contracts/ViewInterface";
|
|
3
|
-
import type {
|
|
4
|
-
import { ViewController } from "./ViewController";
|
|
1
|
+
import type { ViewControllerInterface, ViewType } from "../contracts/ViewControllerInterface.js";
|
|
2
|
+
import type { ViewInterface, ViewLifecycleHooks } from "../contracts/ViewInterface.js";
|
|
3
|
+
import type { SaoObjectType } from "../types/utils.js";
|
|
4
|
+
import { ViewController } from "./ViewController.js";
|
|
5
|
+
import type { WatchCallback, WatchOptions } from './ResourceScope.js';
|
|
6
|
+
|
|
7
|
+
/** Declared View API without the legacy catch-all that would hide misspelled members. */
|
|
8
|
+
type ViewMembers = {
|
|
9
|
+
[K in keyof View as string extends K ? never : number extends K ? never : K]: View[K];
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/** The exported object's methods run on the View after its members are merged. */
|
|
13
|
+
export type ViewUserConfig<T extends object> = T & ThisType<T & ViewMembers>;
|
|
5
14
|
|
|
6
15
|
/**
|
|
7
|
-
* View — the base class for all views in
|
|
16
|
+
* View — the base class for all views in SaoView.
|
|
8
17
|
*
|
|
9
18
|
* A View is the unit that the user writes. It contains:
|
|
10
19
|
* - User-defined properties (reactive state, computed values)
|
|
@@ -59,14 +68,54 @@ export class View implements ViewInterface, ViewLifecycleHooks {
|
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
// ─── Lifecycle Hooks ────────────────────────────────────────
|
|
62
|
-
// Users can override these in their view class
|
|
71
|
+
// Users can override these in their view class (mẫu examples/sao/app.sao).
|
|
72
|
+
// Cặp before/after cho mỗi transition:
|
|
73
|
+
mounting?(): void | Promise<void>;
|
|
74
|
+
mounted?(): void | Promise<void>;
|
|
75
|
+
starting?(): void | Promise<void>;
|
|
76
|
+
started?(): void | Promise<void>;
|
|
77
|
+
pausing?(): void | Promise<void>;
|
|
78
|
+
paused?(): void | Promise<void>;
|
|
79
|
+
resuming?(): void | Promise<void>;
|
|
80
|
+
resumed?(): void | Promise<void>;
|
|
81
|
+
stopping?(): void | Promise<void>;
|
|
82
|
+
stopped?(): void | Promise<void>;
|
|
83
|
+
unmounting?(): void | Promise<void>;
|
|
84
|
+
unmounted?(): void | Promise<void>;
|
|
85
|
+
destroying?(): void | Promise<void>;
|
|
86
|
+
destroyed?(): void | Promise<void>;
|
|
63
87
|
|
|
88
|
+
// Legacy alias (tương thích cũ):
|
|
64
89
|
onInit?(): void | Promise<void>;
|
|
65
90
|
onMounted?(): void | Promise<void>;
|
|
66
91
|
onUpdated?(): void | Promise<void>;
|
|
67
92
|
onDestroy?(): void | Promise<void>;
|
|
68
93
|
onActivated?(): void | Promise<void>;
|
|
69
94
|
onDeactivated?(): void | Promise<void>;
|
|
95
|
+
onPause?(): void | Promise<void>;
|
|
96
|
+
onResume?(): void | Promise<void>;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Phát sự kiện lên cha đã `@include` view này.
|
|
100
|
+
*
|
|
101
|
+
* Là method của View (không chỉ hàm trong scope compiled) để template gọi
|
|
102
|
+
* thẳng được: `@click(emit('edit', card['id']))` biên dịch thành
|
|
103
|
+
* `{ handler: 'emit' }`, và ViewController.addEventListener tra handler
|
|
104
|
+
* dạng chuỗi trên chính View.
|
|
105
|
+
*/
|
|
106
|
+
emit(event: string, ...args: any[]): any {
|
|
107
|
+
return this.__ctrl__.emit(event, ...args);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
watch(keys: readonly string[], callback: WatchCallback, options?: WatchOptions): () => void {
|
|
111
|
+
return this.__ctrl__.scope.watch(keys, callback, options);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
afterDom(callback: () => void): () => void {
|
|
115
|
+
return this.__ctrl__.afterDom(callback);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
get $scope() { return this.__ctrl__.scope; }
|
|
70
119
|
|
|
71
120
|
// ─── Convenience Accessors ──────────────────────────────────
|
|
72
121
|
|
|
@@ -87,10 +136,10 @@ export class View implements ViewInterface, ViewLifecycleHooks {
|
|
|
87
136
|
get $state(): any {
|
|
88
137
|
return this.__ctrl__.states;
|
|
89
138
|
}
|
|
90
|
-
get
|
|
139
|
+
get saoType(): SaoObjectType {
|
|
91
140
|
return 'View';
|
|
92
141
|
}
|
|
93
|
-
set
|
|
142
|
+
set saoType(value: SaoObjectType) {
|
|
94
143
|
// No-op setter to satisfy the Interface; this property is always 'View'
|
|
95
144
|
}
|
|
96
145
|
|