@saolabs/client 1.0.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 +221 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins.d.ts +38 -0
- package/dist/plugins.d.ts.map +1 -0
- package/dist/plugins.js +61 -0
- package/dist/plugins.js.map +1 -0
- package/dist/src/core/app/Application.d.ts +159 -0
- package/dist/src/core/app/Application.d.ts.map +1 -0
- package/dist/src/core/app/Application.js +338 -0
- package/dist/src/core/app/Application.js.map +1 -0
- package/dist/src/core/bootstrap/app.d.ts +4 -0
- package/dist/src/core/bootstrap/app.d.ts.map +1 -0
- package/dist/src/core/bootstrap/app.js +49 -0
- package/dist/src/core/bootstrap/app.js.map +1 -0
- package/dist/src/core/bootstrap/providers/ApiServiceProvider.d.ts +9 -0
- package/dist/src/core/bootstrap/providers/ApiServiceProvider.d.ts.map +1 -0
- package/dist/src/core/bootstrap/providers/ApiServiceProvider.js +18 -0
- package/dist/src/core/bootstrap/providers/ApiServiceProvider.js.map +1 -0
- package/dist/src/core/bootstrap/providers/CoreServiceProvider.d.ts +6 -0
- package/dist/src/core/bootstrap/providers/CoreServiceProvider.d.ts.map +1 -0
- package/dist/src/core/bootstrap/providers/CoreServiceProvider.js +21 -0
- package/dist/src/core/bootstrap/providers/CoreServiceProvider.js.map +1 -0
- package/dist/src/core/bootstrap/providers/HelperServiceProvider.d.ts +7 -0
- package/dist/src/core/bootstrap/providers/HelperServiceProvider.d.ts.map +1 -0
- package/dist/src/core/bootstrap/providers/HelperServiceProvider.js +14 -0
- package/dist/src/core/bootstrap/providers/HelperServiceProvider.js.map +1 -0
- package/dist/src/core/bootstrap/providers/RouteServiceProvider.d.ts +9 -0
- package/dist/src/core/bootstrap/providers/RouteServiceProvider.d.ts.map +1 -0
- package/dist/src/core/bootstrap/providers/RouteServiceProvider.js +18 -0
- package/dist/src/core/bootstrap/providers/RouteServiceProvider.js.map +1 -0
- package/dist/src/core/bootstrap/providers/ServiceProvider.d.ts +38 -0
- package/dist/src/core/bootstrap/providers/ServiceProvider.d.ts.map +1 -0
- package/dist/src/core/bootstrap/providers/ServiceProvider.js +41 -0
- package/dist/src/core/bootstrap/providers/ServiceProvider.js.map +1 -0
- package/dist/src/core/bootstrap/providers/ViewServiceProvider.d.ts +9 -0
- package/dist/src/core/bootstrap/providers/ViewServiceProvider.d.ts.map +1 -0
- package/dist/src/core/bootstrap/providers/ViewServiceProvider.js +18 -0
- package/dist/src/core/bootstrap/providers/ViewServiceProvider.js.map +1 -0
- package/dist/src/core/bootstrap/providers/default-providers.d.ts +14 -0
- package/dist/src/core/bootstrap/providers/default-providers.d.ts.map +1 -0
- package/dist/src/core/bootstrap/providers/default-providers.js +79 -0
- package/dist/src/core/bootstrap/providers/default-providers.js.map +1 -0
- package/dist/src/core/bootstrap/providers/index.d.ts +10 -0
- package/dist/src/core/bootstrap/providers/index.d.ts.map +1 -0
- package/dist/src/core/bootstrap/providers/index.js +9 -0
- package/dist/src/core/bootstrap/providers/index.js.map +1 -0
- package/dist/src/core/bootstrap/providers/provider-order.d.ts +19 -0
- package/dist/src/core/bootstrap/providers/provider-order.d.ts.map +1 -0
- package/dist/src/core/bootstrap/providers/provider-order.js +51 -0
- package/dist/src/core/bootstrap/providers/provider-order.js.map +1 -0
- package/dist/src/core/cache/ViewCache.d.ts +46 -0
- package/dist/src/core/cache/ViewCache.d.ts.map +1 -0
- package/dist/src/core/cache/ViewCache.js +85 -0
- package/dist/src/core/cache/ViewCache.js.map +1 -0
- package/dist/src/core/cache/index.d.ts +2 -0
- package/dist/src/core/cache/index.d.ts.map +1 -0
- package/dist/src/core/cache/index.js +2 -0
- package/dist/src/core/cache/index.js.map +1 -0
- package/dist/src/core/contracts/ApiInterface.d.ts +11 -0
- package/dist/src/core/contracts/ApiInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/ApiInterface.js +2 -0
- package/dist/src/core/contracts/ApiInterface.js.map +1 -0
- package/dist/src/core/contracts/ApplicationInterface.d.ts +66 -0
- package/dist/src/core/contracts/ApplicationInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/ApplicationInterface.js +3 -0
- package/dist/src/core/contracts/ApplicationInterface.js.map +1 -0
- package/dist/src/core/contracts/BlockInterface.d.ts +67 -0
- package/dist/src/core/contracts/BlockInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/BlockInterface.js +2 -0
- package/dist/src/core/contracts/BlockInterface.js.map +1 -0
- package/dist/src/core/contracts/ComponentInterface.d.ts +24 -0
- package/dist/src/core/contracts/ComponentInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/ComponentInterface.js +2 -0
- package/dist/src/core/contracts/ComponentInterface.js.map +1 -0
- package/dist/src/core/contracts/ElementInterface.d.ts +117 -0
- package/dist/src/core/contracts/ElementInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/ElementInterface.js +2 -0
- package/dist/src/core/contracts/ElementInterface.js.map +1 -0
- package/dist/src/core/contracts/EventServiceInterface.d.ts +37 -0
- package/dist/src/core/contracts/EventServiceInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/EventServiceInterface.js +3 -0
- package/dist/src/core/contracts/EventServiceInterface.js.map +1 -0
- package/dist/src/core/contracts/HelperInterface.d.ts +231 -0
- package/dist/src/core/contracts/HelperInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/HelperInterface.js +3 -0
- package/dist/src/core/contracts/HelperInterface.js.map +1 -0
- package/dist/src/core/contracts/HttpServiceInterface.d.ts +38 -0
- package/dist/src/core/contracts/HttpServiceInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/HttpServiceInterface.js +3 -0
- package/dist/src/core/contracts/HttpServiceInterface.js.map +1 -0
- package/dist/src/core/contracts/LoggerServiceInterface.d.ts +37 -0
- package/dist/src/core/contracts/LoggerServiceInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/LoggerServiceInterface.js +3 -0
- package/dist/src/core/contracts/LoggerServiceInterface.js.map +1 -0
- package/dist/src/core/contracts/LoopContextInterface.d.ts +14 -0
- package/dist/src/core/contracts/LoopContextInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/LoopContextInterface.js +3 -0
- package/dist/src/core/contracts/LoopContextInterface.js.map +1 -0
- package/dist/src/core/contracts/MarkerInterface.d.ts +55 -0
- package/dist/src/core/contracts/MarkerInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/MarkerInterface.js +2 -0
- package/dist/src/core/contracts/MarkerInterface.js.map +1 -0
- package/dist/src/core/contracts/ReactiveInterface.d.ts +38 -0
- package/dist/src/core/contracts/ReactiveInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/ReactiveInterface.js +2 -0
- package/dist/src/core/contracts/ReactiveInterface.js.map +1 -0
- package/dist/src/core/contracts/RouterInterface.d.ts +58 -0
- package/dist/src/core/contracts/RouterInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/RouterInterface.js +2 -0
- package/dist/src/core/contracts/RouterInterface.js.map +1 -0
- package/dist/src/core/contracts/ServiceProviderInterface.d.ts +12 -0
- package/dist/src/core/contracts/ServiceProviderInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/ServiceProviderInterface.js +2 -0
- package/dist/src/core/contracts/ServiceProviderInterface.js.map +1 -0
- package/dist/src/core/contracts/StorageServiceInterface.d.ts +43 -0
- package/dist/src/core/contracts/StorageServiceInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/StorageServiceInterface.js +3 -0
- package/dist/src/core/contracts/StorageServiceInterface.js.map +1 -0
- package/dist/src/core/contracts/StoreServiceInterface.d.ts +45 -0
- package/dist/src/core/contracts/StoreServiceInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/StoreServiceInterface.js +3 -0
- package/dist/src/core/contracts/StoreServiceInterface.js.map +1 -0
- package/dist/src/core/contracts/ViewCacheInterface.d.ts +13 -0
- package/dist/src/core/contracts/ViewCacheInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/ViewCacheInterface.js +3 -0
- package/dist/src/core/contracts/ViewCacheInterface.js.map +1 -0
- package/dist/src/core/contracts/ViewControllerInterface.d.ts +115 -0
- package/dist/src/core/contracts/ViewControllerInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/ViewControllerInterface.js +2 -0
- package/dist/src/core/contracts/ViewControllerInterface.js.map +1 -0
- package/dist/src/core/contracts/ViewInterface.d.ts +24 -0
- package/dist/src/core/contracts/ViewInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/ViewInterface.js +2 -0
- package/dist/src/core/contracts/ViewInterface.js.map +1 -0
- package/dist/src/core/contracts/ViewManagerInterface.d.ts +44 -0
- package/dist/src/core/contracts/ViewManagerInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/ViewManagerInterface.js +2 -0
- package/dist/src/core/contracts/ViewManagerInterface.js.map +1 -0
- package/dist/src/core/contracts/ViewStateInterface.d.ts +40 -0
- package/dist/src/core/contracts/ViewStateInterface.d.ts.map +1 -0
- package/dist/src/core/contracts/ViewStateInterface.js +3 -0
- package/dist/src/core/contracts/ViewStateInterface.js.map +1 -0
- package/dist/src/core/contracts/common.d.ts +28 -0
- package/dist/src/core/contracts/common.d.ts.map +1 -0
- package/dist/src/core/contracts/common.js +29 -0
- package/dist/src/core/contracts/common.js.map +1 -0
- package/dist/src/core/contracts/utils.d.ts +20 -0
- package/dist/src/core/contracts/utils.d.ts.map +1 -0
- package/dist/src/core/contracts/utils.js +4 -0
- package/dist/src/core/contracts/utils.js.map +1 -0
- package/dist/src/core/contracts/views.d.ts +23 -0
- package/dist/src/core/contracts/views.d.ts.map +1 -0
- package/dist/src/core/contracts/views.js +2 -0
- package/dist/src/core/contracts/views.js.map +1 -0
- package/dist/src/core/elements/Block.d.ts +58 -0
- package/dist/src/core/elements/Block.d.ts.map +1 -0
- package/dist/src/core/elements/Block.js +77 -0
- package/dist/src/core/elements/Block.js.map +1 -0
- package/dist/src/core/elements/BlockOutlet.d.ts +34 -0
- package/dist/src/core/elements/BlockOutlet.d.ts.map +1 -0
- package/dist/src/core/elements/BlockOutlet.js +59 -0
- package/dist/src/core/elements/BlockOutlet.js.map +1 -0
- package/dist/src/core/elements/Component.d.ts +62 -0
- package/dist/src/core/elements/Component.d.ts.map +1 -0
- package/dist/src/core/elements/Component.js +72 -0
- package/dist/src/core/elements/Component.js.map +1 -0
- package/dist/src/core/elements/ElementManager.d.ts +23 -0
- package/dist/src/core/elements/ElementManager.d.ts.map +1 -0
- package/dist/src/core/elements/ElementManager.js +33 -0
- package/dist/src/core/elements/ElementManager.js.map +1 -0
- package/dist/src/core/elements/Fragment.d.ts +51 -0
- package/dist/src/core/elements/Fragment.d.ts.map +1 -0
- package/dist/src/core/elements/Fragment.js +133 -0
- package/dist/src/core/elements/Fragment.js.map +1 -0
- package/dist/src/core/elements/Html.d.ts +52 -0
- package/dist/src/core/elements/Html.d.ts.map +1 -0
- package/dist/src/core/elements/Html.js +299 -0
- package/dist/src/core/elements/Html.js.map +1 -0
- package/dist/src/core/elements/Output.d.ts +69 -0
- package/dist/src/core/elements/Output.d.ts.map +1 -0
- package/dist/src/core/elements/Output.js +121 -0
- package/dist/src/core/elements/Output.js.map +1 -0
- package/dist/src/core/elements/Reactive.d.ts +77 -0
- package/dist/src/core/elements/Reactive.d.ts.map +1 -0
- package/dist/src/core/elements/Reactive.js +199 -0
- package/dist/src/core/elements/Reactive.js.map +1 -0
- package/dist/src/core/elements/TextElement.d.ts +46 -0
- package/dist/src/core/elements/TextElement.d.ts.map +1 -0
- package/dist/src/core/elements/TextElement.js +94 -0
- package/dist/src/core/elements/TextElement.js.map +1 -0
- package/dist/src/core/elements/Wrapper.d.ts +50 -0
- package/dist/src/core/elements/Wrapper.d.ts.map +1 -0
- package/dist/src/core/elements/Wrapper.js +96 -0
- package/dist/src/core/elements/Wrapper.js.map +1 -0
- package/dist/src/core/elements/Yield.d.ts +34 -0
- package/dist/src/core/elements/Yield.d.ts.map +1 -0
- package/dist/src/core/elements/Yield.js +56 -0
- package/dist/src/core/elements/Yield.js.map +1 -0
- package/dist/src/core/elements/helpers.d.ts +8 -0
- package/dist/src/core/elements/helpers.d.ts.map +1 -0
- package/dist/src/core/elements/helpers.js +40 -0
- package/dist/src/core/elements/helpers.js.map +1 -0
- package/dist/src/core/elements/index.d.ts +12 -0
- package/dist/src/core/elements/index.d.ts.map +1 -0
- package/dist/src/core/elements/index.js +12 -0
- package/dist/src/core/elements/index.js.map +1 -0
- package/dist/src/core/hellpers/ApiClient.d.ts +58 -0
- package/dist/src/core/hellpers/ApiClient.d.ts.map +1 -0
- package/dist/src/core/hellpers/ApiClient.js +122 -0
- package/dist/src/core/hellpers/ApiClient.js.map +1 -0
- package/dist/src/core/hellpers/app.d.ts +3 -0
- package/dist/src/core/hellpers/app.d.ts.map +1 -0
- package/dist/src/core/hellpers/app.js +53 -0
- package/dist/src/core/hellpers/app.js.map +1 -0
- package/dist/src/core/hellpers/utils.d.ts +26 -0
- package/dist/src/core/hellpers/utils.d.ts.map +1 -0
- package/dist/src/core/hellpers/utils.js +96 -0
- package/dist/src/core/hellpers/utils.js.map +1 -0
- package/dist/src/core/routers/Router.d.ts +212 -0
- package/dist/src/core/routers/Router.d.ts.map +1 -0
- package/dist/src/core/routers/Router.js +623 -0
- package/dist/src/core/routers/Router.js.map +1 -0
- package/dist/src/core/routers/index.d.ts +3 -0
- package/dist/src/core/routers/index.d.ts.map +1 -0
- package/dist/src/core/routers/index.js +2 -0
- package/dist/src/core/routers/index.js.map +1 -0
- package/dist/src/core/services/BlockManager.d.ts +63 -0
- package/dist/src/core/services/BlockManager.d.ts.map +1 -0
- package/dist/src/core/services/BlockManager.js +191 -0
- package/dist/src/core/services/BlockManager.js.map +1 -0
- package/dist/src/core/services/DomService.d.ts +9 -0
- package/dist/src/core/services/DomService.d.ts.map +1 -0
- package/dist/src/core/services/DomService.js +18 -0
- package/dist/src/core/services/DomService.js.map +1 -0
- package/dist/src/core/services/EventService.d.ts +58 -0
- package/dist/src/core/services/EventService.d.ts.map +1 -0
- package/dist/src/core/services/EventService.js +212 -0
- package/dist/src/core/services/EventService.js.map +1 -0
- package/dist/src/core/services/HelperService.d.ts +443 -0
- package/dist/src/core/services/HelperService.d.ts.map +1 -0
- package/dist/src/core/services/HelperService.js +1650 -0
- package/dist/src/core/services/HelperService.js.map +1 -0
- package/dist/src/core/services/HttpService.d.ts +63 -0
- package/dist/src/core/services/HttpService.d.ts.map +1 -0
- package/dist/src/core/services/HttpService.js +182 -0
- package/dist/src/core/services/HttpService.js.map +1 -0
- package/dist/src/core/services/LoggerService.d.ts +56 -0
- package/dist/src/core/services/LoggerService.d.ts.map +1 -0
- package/dist/src/core/services/LoggerService.js +94 -0
- package/dist/src/core/services/LoggerService.js.map +1 -0
- package/dist/src/core/services/MarkerRegistry.d.ts +94 -0
- package/dist/src/core/services/MarkerRegistry.d.ts.map +1 -0
- package/dist/src/core/services/MarkerRegistry.js +199 -0
- package/dist/src/core/services/MarkerRegistry.js.map +1 -0
- package/dist/src/core/services/MarkerService.d.ts +299 -0
- package/dist/src/core/services/MarkerService.d.ts.map +1 -0
- package/dist/src/core/services/MarkerService.js +723 -0
- package/dist/src/core/services/MarkerService.js.map +1 -0
- package/dist/src/core/services/StorageService.d.ts +69 -0
- package/dist/src/core/services/StorageService.d.ts.map +1 -0
- package/dist/src/core/services/StorageService.js +243 -0
- package/dist/src/core/services/StorageService.js.map +1 -0
- package/dist/src/core/services/StoreService.d.ts +80 -0
- package/dist/src/core/services/StoreService.d.ts.map +1 -0
- package/dist/src/core/services/StoreService.js +301 -0
- package/dist/src/core/services/StoreService.js.map +1 -0
- package/dist/src/core/services/index.d.ts +9 -0
- package/dist/src/core/services/index.d.ts.map +1 -0
- package/dist/src/core/services/index.js +9 -0
- package/dist/src/core/services/index.js.map +1 -0
- package/dist/src/core/types/utils.d.ts +29 -0
- package/dist/src/core/types/utils.d.ts.map +1 -0
- package/dist/src/core/types/utils.js +22 -0
- package/dist/src/core/types/utils.js.map +1 -0
- package/dist/src/core/view/LoopContext.d.ts +38 -0
- package/dist/src/core/view/LoopContext.d.ts.map +1 -0
- package/dist/src/core/view/LoopContext.js +100 -0
- package/dist/src/core/view/LoopContext.js.map +1 -0
- package/dist/src/core/view/Section.d.ts +41 -0
- package/dist/src/core/view/Section.d.ts.map +1 -0
- package/dist/src/core/view/Section.js +67 -0
- package/dist/src/core/view/Section.js.map +1 -0
- package/dist/src/core/view/View.d.ts +63 -0
- package/dist/src/core/view/View.d.ts.map +1 -0
- package/dist/src/core/view/View.js +85 -0
- package/dist/src/core/view/View.js.map +1 -0
- package/dist/src/core/view/ViewController.d.ts +277 -0
- package/dist/src/core/view/ViewController.d.ts.map +1 -0
- package/dist/src/core/view/ViewController.js +862 -0
- package/dist/src/core/view/ViewController.js.map +1 -0
- package/dist/src/core/view/ViewManager.d.ts +141 -0
- package/dist/src/core/view/ViewManager.d.ts.map +1 -0
- package/dist/src/core/view/ViewManager.js +565 -0
- package/dist/src/core/view/ViewManager.js.map +1 -0
- package/dist/src/core/view/ViewState.d.ts +117 -0
- package/dist/src/core/view/ViewState.d.ts.map +1 -0
- package/dist/src/core/view/ViewState.js +410 -0
- package/dist/src/core/view/ViewState.js.map +1 -0
- package/dist/src/core/view/index.d.ts +6 -0
- package/dist/src/core/view/index.d.ts.map +1 -0
- package/dist/src/core/view/index.js +6 -0
- package/dist/src/core/view/index.js.map +1 -0
- package/index.ts +62 -0
- package/package.json +70 -0
- package/plugins.ts +77 -0
- package/src/core/app/Application.ts +410 -0
- package/src/core/bootstrap/app.ts +58 -0
- package/src/core/bootstrap/providers/ApiServiceProvider.ts +18 -0
- package/src/core/bootstrap/providers/CoreServiceProvider.ts +19 -0
- package/src/core/bootstrap/providers/HelperServiceProvider.ts +12 -0
- package/src/core/bootstrap/providers/RouteServiceProvider.ts +17 -0
- package/src/core/bootstrap/providers/ServiceProvider.ts +51 -0
- package/src/core/bootstrap/providers/ViewServiceProvider.ts +17 -0
- package/src/core/bootstrap/providers/default-providers.ts +94 -0
- package/src/core/bootstrap/providers/index.ts +9 -0
- package/src/core/bootstrap/providers/provider-order.test.ts +67 -0
- package/src/core/bootstrap/providers/provider-order.ts +66 -0
- package/src/core/cache/ViewCache.ts +102 -0
- package/src/core/cache/index.ts +1 -0
- package/src/core/contracts/ApiInterface.ts +11 -0
- package/src/core/contracts/ApplicationInterface.ts +79 -0
- package/src/core/contracts/BlockInterface.ts +76 -0
- package/src/core/contracts/ComponentInterface.ts +26 -0
- package/src/core/contracts/ElementInterface.ts +140 -0
- package/src/core/contracts/EventServiceInterface.ts +47 -0
- package/src/core/contracts/HelperInterface.ts +256 -0
- package/src/core/contracts/HttpServiceInterface.ts +51 -0
- package/src/core/contracts/LoggerServiceInterface.ts +48 -0
- package/src/core/contracts/LoopContextInterface.ts +15 -0
- package/src/core/contracts/MarkerInterface.ts +77 -0
- package/src/core/contracts/ReactiveInterface.ts +45 -0
- package/src/core/contracts/RouterInterface.ts +65 -0
- package/src/core/contracts/ServiceProviderInterface.ts +12 -0
- package/src/core/contracts/StorageServiceInterface.ts +48 -0
- package/src/core/contracts/StoreServiceInterface.ts +50 -0
- package/src/core/contracts/ViewCacheInterface.ts +14 -0
- package/src/core/contracts/ViewControllerInterface.ts +135 -0
- package/src/core/contracts/ViewInterface.ts +31 -0
- package/src/core/contracts/ViewManagerInterface.ts +45 -0
- package/src/core/contracts/ViewStateInterface.ts +47 -0
- package/src/core/contracts/common.ts +27 -0
- package/src/core/contracts/utils.ts +23 -0
- package/src/core/contracts/views.ts +23 -0
- package/src/core/elements/Block.ts +113 -0
- package/src/core/elements/BlockOutlet.ts +83 -0
- package/src/core/elements/Component.ts +114 -0
- package/src/core/elements/ElementManager.ts +48 -0
- package/src/core/elements/Fragment.ts +168 -0
- package/src/core/elements/Html.ts +364 -0
- package/src/core/elements/Output.ts +167 -0
- package/src/core/elements/Reactive.ts +253 -0
- package/src/core/elements/TextElement.ts +110 -0
- package/src/core/elements/Wrapper.ts +131 -0
- package/src/core/elements/Yield.ts +70 -0
- package/src/core/elements/helpers.ts +56 -0
- package/src/core/elements/index.ts +26 -0
- package/src/core/hellpers/ApiClient.ts +145 -0
- package/src/core/hellpers/app.ts +57 -0
- package/src/core/hellpers/utils.ts +109 -0
- package/src/core/routers/Router.ts +765 -0
- package/src/core/routers/index.ts +2 -0
- package/src/core/services/BlockManager.ts +210 -0
- package/src/core/services/DomService.ts +26 -0
- package/src/core/services/EventService.ts +266 -0
- package/src/core/services/HelperService.ts +1653 -0
- package/src/core/services/HttpService.ts +235 -0
- package/src/core/services/LoggerService.ts +139 -0
- package/src/core/services/MarkerRegistry.ts +239 -0
- package/src/core/services/MarkerService.ts +923 -0
- package/src/core/services/StorageService.ts +268 -0
- package/src/core/services/StoreService.ts +338 -0
- package/src/core/services/index.ts +8 -0
- package/src/core/types/utils.ts +30 -0
- package/src/core/view/LoopContext.ts +109 -0
- package/src/core/view/Section.ts +86 -0
- package/src/core/view/View.ts +109 -0
- package/src/core/view/ViewController.ts +1002 -0
- package/src/core/view/ViewManager.ts +700 -0
- package/src/core/view/ViewState.ts +453 -0
- package/src/core/view/index.ts +5 -0
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ViewManager — Orchestrateur quản lý view lifecycle.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* 1. Load view modules (dynamic import hoặc registry)
|
|
6
|
+
* 2. Invoke factory → get View instance
|
|
7
|
+
* 3. Resolve layout chain (if hasSuperView)
|
|
8
|
+
* 4. Mount vào container
|
|
9
|
+
* 5. commitData cho all views in chain
|
|
10
|
+
* 6. Start all views (activate reactivity)
|
|
11
|
+
* 7. Track active views for cleanup
|
|
12
|
+
*
|
|
13
|
+
* Khác core/ ViewManager:
|
|
14
|
+
* - Nhẹ hơn (~300 dòng thay vì 1145)
|
|
15
|
+
* - Tách rõ concern: ViewManager chỉ orchestrate, không render HTML string
|
|
16
|
+
* - Element tree rendering → ViewController.render()
|
|
17
|
+
* - Section system → Block/BlockOutlet
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { ApplicationInterface } from "../contracts/ApplicationInterface";
|
|
21
|
+
import { ActiveRouteInterface, RouterNavigationType } from "../contracts/RouterInterface";
|
|
22
|
+
import { FragmentInterface, HtmlInterface } from "../contracts/utils";
|
|
23
|
+
import type { ViewControllerConfig, ViewControllerInterface } from "../contracts/ViewControllerInterface";
|
|
24
|
+
import type { ViewInterface } from "../contracts/ViewInterface";
|
|
25
|
+
import type { ViewManagerInterface, ActiveViewInfo } from "../contracts/ViewManagerInterface";
|
|
26
|
+
import { BlockManager, BlockManagerService } from "../services/BlockManager";
|
|
27
|
+
import { Html } from "../elements/Html";
|
|
28
|
+
import { hasData } from "../hellpers/utils";
|
|
29
|
+
import logger from "../services/LoggerService";
|
|
30
|
+
import { StoreService } from "../services/StoreService";
|
|
31
|
+
import { View } from "./View";
|
|
32
|
+
import { InitMode, InitModes } from "../contracts/common";
|
|
33
|
+
import { OOTEnum } from "../types/utils";
|
|
34
|
+
import { WrapperInterface } from "../contracts/ElementInterface";
|
|
35
|
+
import { app } from "../hellpers/app";
|
|
36
|
+
|
|
37
|
+
type RenderPageViewSuccess = {
|
|
38
|
+
type: 'success';
|
|
39
|
+
message: string;
|
|
40
|
+
view: ViewInterface;
|
|
41
|
+
result: ViewInterface | unknown;
|
|
42
|
+
superView: ViewInterface | null;
|
|
43
|
+
finalView: ViewInterface;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
type RenderPageViewError = {
|
|
47
|
+
type: 'error';
|
|
48
|
+
message: string;
|
|
49
|
+
view: ViewInterface;
|
|
50
|
+
result: null;
|
|
51
|
+
superView: null;
|
|
52
|
+
finalView: ViewInterface;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
type RenderPageViewResult = RenderPageViewSuccess | RenderPageViewError;
|
|
56
|
+
|
|
57
|
+
function isRenderableObject(result: unknown): result is { oneType: string } {
|
|
58
|
+
return typeof result === 'object' && result !== null && 'oneType' in result;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export class ViewManager implements ViewManagerInterface {
|
|
62
|
+
/** DI container */
|
|
63
|
+
private App: ApplicationInterface | null = null;
|
|
64
|
+
|
|
65
|
+
private systemData: Record<string, any> = {}; // For internal use, not exposed to views
|
|
66
|
+
|
|
67
|
+
/** ROOT DOM container where views mount */
|
|
68
|
+
private container: HTMLElement | null = null;
|
|
69
|
+
private rootElement: Html | null = null; // Html wrapper for the root container
|
|
70
|
+
/** View module registry: name → factory or async loader */
|
|
71
|
+
private viewRegistry: Record<string, ((...args: any[]) => any) | (() => Promise<any>)> = {};
|
|
72
|
+
|
|
73
|
+
/** Currently mounted views (keyed by path) */
|
|
74
|
+
private activeViews: Map<string, ActiveViewInfo> = new Map();
|
|
75
|
+
|
|
76
|
+
/** The outermost active view (layout or page) */
|
|
77
|
+
private currentView: ActiveViewInfo | null = null;
|
|
78
|
+
|
|
79
|
+
/** Current layout path — for layout reuse detection */
|
|
80
|
+
private currentLayoutPath: string | null = null;
|
|
81
|
+
|
|
82
|
+
private currentLayoutView: ViewInterface | null = null; // Store the current layout view instance for reuse
|
|
83
|
+
private currentPageView: ViewInterface | null = null; // Store the current page view instance for reference in blocks and sections
|
|
84
|
+
private currentViewType: 'view' | 'layout' | null = null; // Track whether the current view is a page or layout for correct lifecycle handling
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
/** Current layout view info — reused if same layout */
|
|
88
|
+
private currentLayout: ActiveViewInfo | null = null;
|
|
89
|
+
|
|
90
|
+
private cachedLayouts: Map<string, ViewInterface> = new Map(); // Cache for previously mounted layouts
|
|
91
|
+
|
|
92
|
+
/** All views in the current mount chain (outermost → innermost) */
|
|
93
|
+
private viewStack: ViewInterface[] = [];
|
|
94
|
+
|
|
95
|
+
/** Whether the manager has been initialized */
|
|
96
|
+
private _isInitialized = false;
|
|
97
|
+
|
|
98
|
+
/** Render counter for debugging */
|
|
99
|
+
private renderCount = 0;
|
|
100
|
+
|
|
101
|
+
public store: StoreService = StoreService.instance("ViewManager");
|
|
102
|
+
|
|
103
|
+
public blockManager: BlockManagerService = BlockManager;
|
|
104
|
+
|
|
105
|
+
constructor(app?: ApplicationInterface) {
|
|
106
|
+
if (app) this.App = app;
|
|
107
|
+
}
|
|
108
|
+
isViewMounted(path: string): boolean {
|
|
109
|
+
throw new Error("Method not implemented.");
|
|
110
|
+
}
|
|
111
|
+
destroy(): void {
|
|
112
|
+
throw new Error("Method not implemented.");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
// ─── Configuration ──────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Set the DI container reference.
|
|
120
|
+
*/
|
|
121
|
+
setApp(app: ApplicationInterface): void {
|
|
122
|
+
this.App = app;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Set the root DOM container.
|
|
127
|
+
*/
|
|
128
|
+
setContainer(container: HTMLElement): void {
|
|
129
|
+
this.container = container;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get the root container element.
|
|
134
|
+
*/
|
|
135
|
+
getContainer(): HTMLElement | null {
|
|
136
|
+
return this.container;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Register view modules.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* viewManager.setViewRegistry({
|
|
144
|
+
* 'web.home': () => import('./views/web/home.js'),
|
|
145
|
+
* 'web.about': () => import('./views/web/about.js'),
|
|
146
|
+
* 'layouts.main': () => import('./views/layouts/main.js'),
|
|
147
|
+
* });
|
|
148
|
+
*/
|
|
149
|
+
setViewRegistry(registry: Record<string, ((...args: any[]) => any) | (() => Promise<any>)>): void {
|
|
150
|
+
this.viewRegistry = { ...this.viewRegistry, ...registry };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Register a single view module.
|
|
155
|
+
*/
|
|
156
|
+
registerView(name: string, loader: ((...args: any[]) => any) | (() => Promise<any>)): void {
|
|
157
|
+
this.viewRegistry[name] = loader;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Initialize the ViewManager.
|
|
162
|
+
*/
|
|
163
|
+
init(config?: { container?: HTMLElement | string; registry?: Record<string, any> }): void {
|
|
164
|
+
if (config?.container) {
|
|
165
|
+
if (typeof config.container === 'string') {
|
|
166
|
+
const found = document.querySelector(config.container);
|
|
167
|
+
if (found instanceof HTMLElement) {
|
|
168
|
+
this.container = found;
|
|
169
|
+
|
|
170
|
+
} else {
|
|
171
|
+
console.warn(`[ViewManager] Container selector "${config.container}" not found.`);
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
this.container = config.container instanceof HTMLElement ? config.container : document.body;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (!this.container) {
|
|
178
|
+
this.container = document.body; // Default to body if no container provided
|
|
179
|
+
}
|
|
180
|
+
this.rootElement = new Html({
|
|
181
|
+
ctx: this,
|
|
182
|
+
tagName: this.container.tagName.toLowerCase(),
|
|
183
|
+
element: this.container,
|
|
184
|
+
initMode: 'hydrate',
|
|
185
|
+
});
|
|
186
|
+
if (config?.registry) {
|
|
187
|
+
this.setViewRegistry(config.registry);
|
|
188
|
+
}
|
|
189
|
+
this._isInitialized = true;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
showError(message: string, details?: any) {
|
|
193
|
+
logger.error(message, details);
|
|
194
|
+
if (this.container) {
|
|
195
|
+
this.container.innerHTML = `<div style="padding: 20px; background: #fee; color: #900; border: 1px solid #900;">
|
|
196
|
+
<h2>Error</h2>
|
|
197
|
+
<p>${message}</p>
|
|
198
|
+
${details ? `<pre style="white-space: pre-wrap; background: #fdd; padding: 10px; border: 1px solid #900;">${JSON.stringify(details, null, 2)}</pre>` : ''}
|
|
199
|
+
</div>`;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ─── View Loading ───────────────────────────────────────────
|
|
204
|
+
hasView(name: string): boolean {
|
|
205
|
+
return Object.prototype.hasOwnProperty.call(this.viewRegistry, name);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
exists(name: string): boolean {
|
|
209
|
+
return this.hasView(name);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
view(name: string, data: Record<string, any>, cache: boolean): any {
|
|
213
|
+
try {
|
|
214
|
+
if (cache && this.store.has(name)) {
|
|
215
|
+
const cachedView = this.store.get<View>(name);
|
|
216
|
+
if (hasData(data)) {
|
|
217
|
+
cachedView?.__ctrl__.updateData(data);
|
|
218
|
+
}
|
|
219
|
+
return cachedView;
|
|
220
|
+
}
|
|
221
|
+
const factory = this.viewRegistry[name];
|
|
222
|
+
if (!factory || typeof factory !== 'function') {
|
|
223
|
+
logger.error(`View "${name}" not found in registry.`);
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
const view = factory(data ? { data } : {}, { App: this.App, View: this, ...this.systemData });
|
|
227
|
+
if (!view) {
|
|
228
|
+
logger.error(`Factory for view "${name}" did not return a valid view instance.`);
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
if (cache) {
|
|
232
|
+
this.store.set(name, view);
|
|
233
|
+
}
|
|
234
|
+
return view;
|
|
235
|
+
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
logger.error(`Error loading view ${name}:`, err);
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private createRenderPageViewError(view: ViewInterface, renderLevel: number, message?: string): RenderPageViewError {
|
|
244
|
+
return {
|
|
245
|
+
type: 'error',
|
|
246
|
+
message: message ?? (renderLevel === 0
|
|
247
|
+
? `View "${view.__ctrl__.path}" did not return any content from render().`
|
|
248
|
+
: `Nested view "${view.__ctrl__.path}" did not return any content from render().`),
|
|
249
|
+
view,
|
|
250
|
+
result: null,
|
|
251
|
+
superView: null,
|
|
252
|
+
finalView: view,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private createRenderPageViewSuccess(
|
|
257
|
+
view: ViewInterface,
|
|
258
|
+
result: ViewInterface | unknown,
|
|
259
|
+
superView: ViewInterface | null,
|
|
260
|
+
finalView: ViewInterface
|
|
261
|
+
): RenderPageViewSuccess {
|
|
262
|
+
return {
|
|
263
|
+
type: 'success',
|
|
264
|
+
message: '',
|
|
265
|
+
view,
|
|
266
|
+
result,
|
|
267
|
+
superView,
|
|
268
|
+
finalView,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private getRenderResultType(result: unknown): OOTEnum {
|
|
273
|
+
return isRenderableObject(result) ? result.oneType as OOTEnum : OOTEnum.UNKNOWN;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
async callViewRenderFactory(
|
|
278
|
+
view: ViewInterface,
|
|
279
|
+
method: 'render' | 'prerender' = 'render',
|
|
280
|
+
data: Record<string, any> = {},
|
|
281
|
+
mountRoot: HtmlInterface | null = null,
|
|
282
|
+
initMode: InitMode = InitModes.CREATE,
|
|
283
|
+
cache: boolean = false,
|
|
284
|
+
renderLevel: number = 0
|
|
285
|
+
): Promise<RenderPageViewResult> {
|
|
286
|
+
const ctrl = view.__ctrl__;
|
|
287
|
+
|
|
288
|
+
if (hasData(data)) {
|
|
289
|
+
ctrl.updateData(data);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const result = method === 'render'
|
|
293
|
+
? ctrl.render()
|
|
294
|
+
: (ctrl.prerender ? ctrl.prerender() : null);
|
|
295
|
+
|
|
296
|
+
if (!result) {
|
|
297
|
+
return this.createRenderPageViewError(view, renderLevel,
|
|
298
|
+
`View "${ctrl.path}" returned nothing from ${method}().`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const resultType = this.getRenderResultType(result);
|
|
302
|
+
|
|
303
|
+
if (resultType === OOTEnum.WRAPPER) {
|
|
304
|
+
return this.createRenderPageViewSuccess(view, result, null, view);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (resultType === OOTEnum.VIEW) {
|
|
308
|
+
const superResult = await this.renderPageView(
|
|
309
|
+
result as ViewInterface, {}, mountRoot, initMode, cache, renderLevel + 1
|
|
310
|
+
);
|
|
311
|
+
if (superResult.type === 'error') {
|
|
312
|
+
return { ...superResult, view };
|
|
313
|
+
}
|
|
314
|
+
return this.createRenderPageViewSuccess(
|
|
315
|
+
view,
|
|
316
|
+
result,
|
|
317
|
+
superResult.view,
|
|
318
|
+
superResult.finalView ?? (result as ViewInterface)
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return this.createRenderPageViewError(view, renderLevel,
|
|
323
|
+
`View "${ctrl.path}" returned invalid content (type: ${resultType}) from ${method}().`);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async renderPageView(
|
|
327
|
+
view: ViewInterface,
|
|
328
|
+
data: Record<string, any>,
|
|
329
|
+
mountRoot: HtmlInterface | null = null,
|
|
330
|
+
initMode: InitMode = InitModes.CREATE,
|
|
331
|
+
cache: boolean = false,
|
|
332
|
+
renderLevel: number = 0
|
|
333
|
+
): Promise<RenderPageViewResult> {
|
|
334
|
+
try {
|
|
335
|
+
const ctrl = view.__ctrl__;
|
|
336
|
+
if (hasData(data)) {
|
|
337
|
+
ctrl.updateData(data);
|
|
338
|
+
}
|
|
339
|
+
const config: ViewControllerConfig = ctrl.getConfig();
|
|
340
|
+
const hasAsyncData = config.hasAwaitData || config.hasFetchData;
|
|
341
|
+
|
|
342
|
+
// ── Case 1: Không có async data → render ngay ──
|
|
343
|
+
if (!hasAsyncData) {
|
|
344
|
+
return this.callViewRenderFactory(view, 'render', data, mountRoot, initMode, cache, renderLevel);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// ── Resolve fetch URL từ ViewController config hoặc fallback Router ──
|
|
348
|
+
const App = app() as ApplicationInterface;
|
|
349
|
+
const Http = App.Http;
|
|
350
|
+
const fetchConfig = config.fetch;
|
|
351
|
+
const fetchUrl = (config.hasAwaitData && fetchConfig?.url)? fetchConfig?.url : App.Router.getFullUrl();
|
|
352
|
+
|
|
353
|
+
// ── Case 2: Có async + có prerender → prerender skeleton trước, fetch sau ──
|
|
354
|
+
if (config.hasPrerender) {
|
|
355
|
+
const prerenderResult = await this.callViewRenderFactory(view, 'prerender', data, mountRoot, initMode, cache, renderLevel);
|
|
356
|
+
if (prerenderResult.type === 'error') {
|
|
357
|
+
logger.error(`Error prerendering view "${ctrl.path}":`, prerenderResult.message);
|
|
358
|
+
// Fallback: render trực tiếp không qua prerender
|
|
359
|
+
return this.callViewRenderFactory(view, 'render', data, mountRoot, initMode, cache, renderLevel);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Fire-and-forget: fetch data rồi re-render thay thế skeleton
|
|
363
|
+
// TODO: Sau khi render() xong, cần swap DOM từ preloadElement → mainElement
|
|
364
|
+
// ViewController.wrapper() tự động lưu prerender vào preloadElement, render vào mainElement
|
|
365
|
+
// Cần bổ sung logic: unmount preloadElement khỏi DOM → mount mainElement vào cùng vị trí
|
|
366
|
+
Http.get(fetchUrl).then(async (response: any) => {
|
|
367
|
+
const asyncData = response?.data ?? {};
|
|
368
|
+
if (hasData(asyncData)) {
|
|
369
|
+
ctrl.updateData(asyncData);
|
|
370
|
+
}
|
|
371
|
+
const finalResult = await this.callViewRenderFactory(view, 'render', asyncData, mountRoot, initMode, cache, renderLevel);
|
|
372
|
+
if (finalResult.type === 'error') {
|
|
373
|
+
logger.error(`Error rendering view "${ctrl.path}" after async data fetch:`, finalResult.message);
|
|
374
|
+
}
|
|
375
|
+
// TODO: swap preloadElement → mainElement trong DOM
|
|
376
|
+
// ctrl.preloadElement?.destroy();
|
|
377
|
+
// ctrl.mainElement?.setParentElement(mountRoot);
|
|
378
|
+
// ctrl.mainElement?.render();
|
|
379
|
+
}).catch((err: any) => {
|
|
380
|
+
logger.error(`Error fetching async data for view "${ctrl.path}":`, err);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// Return prerender result ngay — mountView sẽ mount skeleton
|
|
384
|
+
return prerenderResult;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ── Case 3: Có async + không prerender → await fetch rồi render ──
|
|
388
|
+
let asyncData: Record<string, any> = {};
|
|
389
|
+
try {
|
|
390
|
+
const response = await Http.get(fetchUrl);
|
|
391
|
+
asyncData = response?.data ?? {};
|
|
392
|
+
} catch (err) {
|
|
393
|
+
logger.error(`Error fetching async data for view "${ctrl.path}":`, err);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (!hasData(asyncData)) {
|
|
397
|
+
logger.warn(`View "${ctrl.path}" has async data config but fetch returned no data.`);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return this.callViewRenderFactory(view, 'render', asyncData, mountRoot, initMode, cache, renderLevel);
|
|
401
|
+
}
|
|
402
|
+
catch (err) {
|
|
403
|
+
logger.error(`Error rendering view ${view.__ctrl__.path}:`, err);
|
|
404
|
+
return this.createRenderPageViewError(view, renderLevel, `Error rendering view "${view.__ctrl__.path}".`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
// ─── Mount Orchestration ────────────────────────────────────
|
|
418
|
+
|
|
419
|
+
async mountView(name: string, data?: Record<string, any>, route?: ActiveRouteInterface, navigationType: RouterNavigationType = 'push'): Promise<any> {
|
|
420
|
+
// ── Phase 1: Load view ──
|
|
421
|
+
const view = this.view(name, data ?? {}, true);
|
|
422
|
+
if (!view) {
|
|
423
|
+
this.showError(`Failed to load view "${name}".`);
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const oldPageView = this.currentPageView;
|
|
428
|
+
const oldLayoutView = this.currentLayoutView;
|
|
429
|
+
const oldLayoutPath = this.currentLayoutPath;
|
|
430
|
+
|
|
431
|
+
view.__ctrl__.urlPath = route?.$urlPath ?? null;
|
|
432
|
+
|
|
433
|
+
if(oldLayoutView){
|
|
434
|
+
oldLayoutView.__ctrl__.stop();
|
|
435
|
+
}
|
|
436
|
+
oldPageView?.__ctrl__.stop();
|
|
437
|
+
|
|
438
|
+
// ── Phase 2: Render chain ──
|
|
439
|
+
const renderResult = await this.renderPageView(view, data ?? {}, this.rootElement, InitModes.CREATE, true);
|
|
440
|
+
if (renderResult.type === 'error') {
|
|
441
|
+
this.showError(renderResult.message);
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const pageView: ViewInterface = renderResult.view;
|
|
446
|
+
const finalView: ViewInterface = renderResult.finalView;
|
|
447
|
+
const hasSuperView = renderResult.superView !== null;
|
|
448
|
+
const newLayoutPath = hasSuperView ? finalView.__ctrl__.path : null;
|
|
449
|
+
|
|
450
|
+
// bắt đầu logic
|
|
451
|
+
|
|
452
|
+
// ── Phase 3: Classify mount scenario ──
|
|
453
|
+
const isSameLayout = hasSuperView
|
|
454
|
+
&& oldLayoutView !== null
|
|
455
|
+
&& finalView === oldLayoutView;
|
|
456
|
+
|
|
457
|
+
// ── Duplicate navigation guard ──
|
|
458
|
+
if (oldPageView === pageView
|
|
459
|
+
&& view.__ctrl__.urlPath === oldPageView?.__ctrl__?.urlPath) {
|
|
460
|
+
return renderResult;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// ══════════════════════════════════════════════════════════
|
|
464
|
+
// Case 1: Có superView + cùng layout instance (reuse layout)
|
|
465
|
+
// ══════════════════════════════════════════════════════════
|
|
466
|
+
if (hasSuperView && isSameLayout) {
|
|
467
|
+
// Cắt origin chain cũ → gắn page mới vào layout
|
|
468
|
+
oldLayoutView!.__ctrl__.ejectOriginChain();
|
|
469
|
+
pageView.__ctrl__.setChainFromOrigin();
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
// Mount block content mới vào layout outlets
|
|
473
|
+
this.blockManager.mountAll();
|
|
474
|
+
|
|
475
|
+
}
|
|
476
|
+
// ══════════════════════════════════════════════════════════
|
|
477
|
+
// Case 2: Có superView + layout khác (hoặc first mount có layout)
|
|
478
|
+
// ══════════════════════════════════════════════════════════
|
|
479
|
+
else if (hasSuperView) {
|
|
480
|
+
// Cleanup toàn bộ old views
|
|
481
|
+
oldLayoutView?.__ctrl__.ejectOriginChain();
|
|
482
|
+
this.stopPageView(oldPageView);
|
|
483
|
+
this.stopLayoutView(oldLayoutView);
|
|
484
|
+
this.blockManager.clearAllOutlets();
|
|
485
|
+
this.unmountLayoutDOM(oldLayoutView);
|
|
486
|
+
|
|
487
|
+
// Gắn origin chain cho layout mới
|
|
488
|
+
pageView.__ctrl__.setChainFromOrigin();
|
|
489
|
+
|
|
490
|
+
// Build DOM từ layout wrapper vào container
|
|
491
|
+
this.buildViewDOM(finalView);
|
|
492
|
+
|
|
493
|
+
// Mount block content vào layout outlets
|
|
494
|
+
this.blockManager.mountAll();
|
|
495
|
+
|
|
496
|
+
// Commit & Start toàn bộ chain
|
|
497
|
+
this.commitViewChain(pageView, finalView, true);
|
|
498
|
+
this.startViewChain(pageView, finalView, true);
|
|
499
|
+
}
|
|
500
|
+
// ══════════════════════════════════════════════════════════
|
|
501
|
+
// Case 3: Không có superView (standalone view)
|
|
502
|
+
// ══════════════════════════════════════════════════════════
|
|
503
|
+
else {
|
|
504
|
+
// Cleanup toàn bộ old views
|
|
505
|
+
oldLayoutView?.__ctrl__.ejectOriginChain();
|
|
506
|
+
this.stopPageView(oldPageView);
|
|
507
|
+
this.stopLayoutView(oldLayoutView);
|
|
508
|
+
this.blockManager.clearAllOutlets();
|
|
509
|
+
this.unmountLayoutDOM(oldLayoutView);
|
|
510
|
+
|
|
511
|
+
// Build DOM trực tiếp vào container
|
|
512
|
+
this.buildViewDOM(pageView);
|
|
513
|
+
|
|
514
|
+
// Commit & Start
|
|
515
|
+
this.commitViewChain(pageView, pageView, false);
|
|
516
|
+
this.startViewChain(pageView, pageView, false);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// ── Update state ──
|
|
520
|
+
this.currentPageView = pageView;
|
|
521
|
+
this.currentLayoutView = hasSuperView ? finalView : null;
|
|
522
|
+
this.currentLayoutPath = newLayoutPath;
|
|
523
|
+
this.currentViewType = hasSuperView ? 'layout' : 'view';
|
|
524
|
+
this.viewStack = hasSuperView ? [finalView, pageView] : [pageView];
|
|
525
|
+
this.renderCount++;
|
|
526
|
+
|
|
527
|
+
return renderResult;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// ─── DOM Building ───────────────────────────────────────────
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Build DOM từ finalView's Wrapper vào container.
|
|
534
|
+
* Wrapper.render() sẽ execute childrenFactory → tạo DOM tree.
|
|
535
|
+
*/
|
|
536
|
+
private buildViewDOM(finalView: ViewInterface): void {
|
|
537
|
+
const wrapper = finalView.__ctrl__.mainElement;
|
|
538
|
+
if (!wrapper) {
|
|
539
|
+
logger.error(`[ViewManager] finalView "${finalView.__ctrl__.path}" has no mainElement (Wrapper).`);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
wrapper.setParentElement(this.rootElement);
|
|
543
|
+
wrapper.render();
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// ─── Lifecycle: Stop ────────────────────────────────────────
|
|
547
|
+
|
|
548
|
+
private stopPageView(pageView: ViewInterface | null): void {
|
|
549
|
+
if (!pageView) return;
|
|
550
|
+
// Stop block content (tracked by BlockManager)
|
|
551
|
+
this.stopBlockContent();
|
|
552
|
+
// Fire onDeactivated hook
|
|
553
|
+
if (typeof pageView.onDeactivated === 'function') {
|
|
554
|
+
try { pageView.onDeactivated(); }
|
|
555
|
+
catch (e) { logger.error(`[ViewManager] onDeactivated error:`, e); }
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
private stopLayoutView(layoutView: ViewInterface | null): void {
|
|
560
|
+
if (!layoutView) return;
|
|
561
|
+
const wrapper = layoutView.__ctrl__.mainElement;
|
|
562
|
+
if (wrapper && typeof wrapper.stop === 'function') {
|
|
563
|
+
wrapper.stop();
|
|
564
|
+
}
|
|
565
|
+
if (typeof layoutView.onDeactivated === 'function') {
|
|
566
|
+
try { layoutView.onDeactivated(); }
|
|
567
|
+
catch (e) { logger.error(`[ViewManager] layout onDeactivated error:`, e); }
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
private stopBlockContent(): void {
|
|
572
|
+
// BlockManager tracks mounted children per outlet
|
|
573
|
+
for (const [name, children] of (this.blockManager as any).mountedChildren) {
|
|
574
|
+
if (Array.isArray(children)) {
|
|
575
|
+
for (const child of children) {
|
|
576
|
+
if (child && typeof child.stop === 'function') {
|
|
577
|
+
child.stop();
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// ─── Lifecycle: Start ───────────────────────────────────────
|
|
585
|
+
|
|
586
|
+
private startViewChain(pageView: ViewInterface, finalView: ViewInterface, hasSuperView: boolean): void {
|
|
587
|
+
if (hasSuperView) {
|
|
588
|
+
// 1. Start layout element tree
|
|
589
|
+
this.startLayoutView(finalView);
|
|
590
|
+
// 2. Start page's block content
|
|
591
|
+
this.startBlockContent();
|
|
592
|
+
// 3. Fire page onMounted
|
|
593
|
+
if (typeof pageView.onMounted === 'function') {
|
|
594
|
+
try { pageView.onMounted(); }
|
|
595
|
+
catch (e) { logger.error(`[ViewManager] page onMounted error:`, e); }
|
|
596
|
+
}
|
|
597
|
+
} else {
|
|
598
|
+
// Standalone view — start wrapper tree directly
|
|
599
|
+
const wrapper = pageView.__ctrl__.mainElement;
|
|
600
|
+
if (wrapper && typeof wrapper.start === 'function') {
|
|
601
|
+
wrapper.start();
|
|
602
|
+
}
|
|
603
|
+
if (typeof pageView.onMounted === 'function') {
|
|
604
|
+
try { pageView.onMounted(); }
|
|
605
|
+
catch (e) { logger.error(`[ViewManager] onMounted error:`, e); }
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
private startLayoutView(layoutView: ViewInterface): void {
|
|
611
|
+
const wrapper = layoutView.__ctrl__.mainElement;
|
|
612
|
+
if (wrapper && typeof wrapper.start === 'function') {
|
|
613
|
+
wrapper.start();
|
|
614
|
+
}
|
|
615
|
+
if (typeof layoutView.onMounted === 'function') {
|
|
616
|
+
try { layoutView.onMounted(); }
|
|
617
|
+
catch (e) { logger.error(`[ViewManager] layout onMounted error:`, e); }
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
private startBlockContent(): void {
|
|
622
|
+
for (const [name, children] of (this.blockManager as any).mountedChildren) {
|
|
623
|
+
if (Array.isArray(children)) {
|
|
624
|
+
for (const child of children) {
|
|
625
|
+
if (child && typeof child.start === 'function') {
|
|
626
|
+
child.start();
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// ─── Lifecycle: Commit Data ─────────────────────────────────
|
|
634
|
+
|
|
635
|
+
private commitViewChain(pageView: ViewInterface, finalView: ViewInterface, hasSuperView: boolean): void {
|
|
636
|
+
if (hasSuperView) {
|
|
637
|
+
finalView.__ctrl__.commitData();
|
|
638
|
+
pageView.__ctrl__.commitData();
|
|
639
|
+
} else {
|
|
640
|
+
pageView.__ctrl__.commitData();
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// ─── Unmount ────────────────────────────────────────────────
|
|
645
|
+
|
|
646
|
+
private unmountLayoutDOM(layoutView: ViewInterface | null): void {
|
|
647
|
+
if (!layoutView) return;
|
|
648
|
+
const wrapper = layoutView.__ctrl__.mainElement;
|
|
649
|
+
if (wrapper && typeof wrapper.destroy === 'function') {
|
|
650
|
+
wrapper.destroy();
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
unmountAll(): void {
|
|
655
|
+
this.stopPageView(this.currentPageView);
|
|
656
|
+
this.stopLayoutView(this.currentLayoutView);
|
|
657
|
+
this.blockManager.clearAllOutlets();
|
|
658
|
+
this.blockManager.destroy();
|
|
659
|
+
|
|
660
|
+
if (this.currentPageView) {
|
|
661
|
+
this.currentPageView.__ctrl__.destroy();
|
|
662
|
+
}
|
|
663
|
+
if (this.currentLayoutView) {
|
|
664
|
+
this.currentLayoutView.__ctrl__.destroy();
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
this.currentPageView = null;
|
|
668
|
+
this.currentLayoutView = null;
|
|
669
|
+
this.currentLayoutPath = null;
|
|
670
|
+
this.currentViewType = null;
|
|
671
|
+
this.activeViews.clear();
|
|
672
|
+
this.viewStack = [];
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
unmountView(path: string): void {
|
|
676
|
+
const info = this.activeViews.get(path);
|
|
677
|
+
if (!info) return;
|
|
678
|
+
this.activeViews.delete(path);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// ─── Getters ────────────────────────────────────────────────
|
|
682
|
+
|
|
683
|
+
getCurrentLayout(): ViewInterface | null {
|
|
684
|
+
return this.currentLayoutView;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
getCurrentView(): ViewInterface | null {
|
|
688
|
+
return this.currentPageView;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
getViewStack(): ViewInterface[] {
|
|
692
|
+
return this.viewStack;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
isInitialized(): boolean {
|
|
696
|
+
return this._isInitialized;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
}
|