@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,1002 @@
|
|
|
1
|
+
import type { BlockInterface, BlockOutletInterface, BlockRenderFactory } from "../contracts/BlockInterface";
|
|
2
|
+
import type { FragmentInterface, HtmlInterface, OneChildrenFactory, OneElementEventHandler, OneNodeInterface, OutputInterface, TextInterface, WrapperInterface, YieldInterface } from "../contracts/ElementInterface";
|
|
3
|
+
import type { LoopContextInterface } from "../contracts/LoopContextInterface";
|
|
4
|
+
import type { ReactiveChildrenFactory, ReactiveInterface } from "../contracts/ReactiveInterface";
|
|
5
|
+
import type { ViewControllerInterface, ViewType, ViewConfig, ViewRuntimeConfig, ViewControllerConfig } from "../contracts/ViewControllerInterface";
|
|
6
|
+
import type { ViewInterface, ViewRenderFactory } from "../contracts/ViewInterface";
|
|
7
|
+
import type { ViewStateInterface } from "../contracts/ViewStateInterface";
|
|
8
|
+
import type { OneObjectType } from "../types/utils";
|
|
9
|
+
import { ViewState } from "./ViewState";
|
|
10
|
+
import { LoopContext } from "./LoopContext";
|
|
11
|
+
import { Reactive } from "../elements/Reactive";
|
|
12
|
+
import BlockManager, { BlockManagerService } from "../services/BlockManager";
|
|
13
|
+
import { Component } from "../elements/Component";
|
|
14
|
+
import { generateUUID } from "../hellpers/utils";
|
|
15
|
+
import { Output } from "../elements/Output";
|
|
16
|
+
import { View } from "./View";
|
|
17
|
+
import { ApplicationInterface } from "../contracts/ApplicationInterface";
|
|
18
|
+
import { SectionConstruvtorArgs, SectionContentRenderer, SectionContentType, SectionInterface, SectionItemType } from "../contracts/views";
|
|
19
|
+
import { Section } from "./Section";
|
|
20
|
+
import { InitMode } from "../contracts/common";
|
|
21
|
+
import { Block, BlockOutlet, Fragment, Html, TextElement } from "../elements";
|
|
22
|
+
import { Wrapper } from "../elements/Wrapper";
|
|
23
|
+
import { YieldElement } from "../elements/Yield";
|
|
24
|
+
import { app } from "../hellpers/app";
|
|
25
|
+
import { ComponentInterface } from "../contracts/ComponentInterface";
|
|
26
|
+
|
|
27
|
+
type ElementChild = ReactiveInterface | ComponentInterface | HtmlInterface | TextInterface | FragmentInterface | OutputInterface | BlockOutletInterface | YieldInterface | OneNodeInterface;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* ViewController — the brain behind a View.
|
|
31
|
+
*
|
|
32
|
+
* Manages:
|
|
33
|
+
* - Reactive state (ViewState) — useState, subscribe, batch flush
|
|
34
|
+
* - DOM element tree — via Html, Reactive, TextElement, Fragment
|
|
35
|
+
* - Event delegation — addEventListener with centralized cleanup
|
|
36
|
+
* - Loop contexts — @foreach, @for, @while with LoopContext stack
|
|
37
|
+
* - Block management — for layout views with @useBlock
|
|
38
|
+
* - Lifecycle — setup, render, destroy
|
|
39
|
+
* - Reactive scheduling — batched DOM updates via requestAnimationFrame
|
|
40
|
+
*
|
|
41
|
+
* KEY DIFFERENCE from core/ViewController:
|
|
42
|
+
* OLD: render() returns HTML string → innerHTML → scan DOM for bindings
|
|
43
|
+
* NEW: render() builds element tree directly → Reactive regions update in-place
|
|
44
|
+
*
|
|
45
|
+
* Compiled .one output calls methods like:
|
|
46
|
+
* this.__ctrl__.addEventListener(el, 'click', handlers)
|
|
47
|
+
* this.__ctrl__.states.__.useState(0, 'count')
|
|
48
|
+
* this.__ctrl__.__foreach(items, (item, key, index, loop) => [...])
|
|
49
|
+
*/
|
|
50
|
+
export class ViewController implements ViewControllerInterface {
|
|
51
|
+
|
|
52
|
+
// ─── Identity ───────────────────────────────────────────────
|
|
53
|
+
oneType: OneObjectType = 'ViewController';
|
|
54
|
+
public viewId: string;
|
|
55
|
+
public path: string;
|
|
56
|
+
public viewType: ViewType;
|
|
57
|
+
|
|
58
|
+
// ─── Core References ────────────────────────────────────────
|
|
59
|
+
/** The View instance this controller manages */
|
|
60
|
+
public view: ViewInterface;
|
|
61
|
+
/** Reactive state manager */
|
|
62
|
+
public states: ViewState;
|
|
63
|
+
/** App container reference (set later by framework) */
|
|
64
|
+
private __App: ApplicationInterface;
|
|
65
|
+
|
|
66
|
+
// ─── View Hierarchy ─────────────────────────────────────────
|
|
67
|
+
/** Parent view controller (for nested views) */
|
|
68
|
+
public parent: ViewControllerInterface | null = null;
|
|
69
|
+
/** Child view controllers (for nested views) */
|
|
70
|
+
public children: ViewControllerInterface[] = [];
|
|
71
|
+
/** Layout (super) view's controller */
|
|
72
|
+
public superView: ViewControllerInterface | null = null;
|
|
73
|
+
/** Path to layout (super) view — e.g. 'layouts.main' */
|
|
74
|
+
public superViewPath: string | null = null;
|
|
75
|
+
/** Whether this controller IS a layout */
|
|
76
|
+
public isSuperView: boolean = false;
|
|
77
|
+
/** For layouts: the original page view's controller (for block mounting) */
|
|
78
|
+
public originView: ViewControllerInterface | null = null;
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
// ─── Data & Config ──────────────────────────────────────────
|
|
82
|
+
/** Raw input data from route/parent */
|
|
83
|
+
public data: Record<string, any> = {};
|
|
84
|
+
/** User-defined config from setup() */
|
|
85
|
+
private config: ViewConfig = {};
|
|
86
|
+
/** Typed runtime config from compiled $__setup__ */
|
|
87
|
+
private runtimeConfig: ViewRuntimeConfig | null = {};
|
|
88
|
+
/** Track own properties to avoid conflicts when setting user config */
|
|
89
|
+
private ownProperties: Set<string> = new Set(['__ctrl__']);
|
|
90
|
+
|
|
91
|
+
// ─── DOM & Rendering ────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
/** Root Html element — the container this view renders into */
|
|
95
|
+
private rootElement: HtmlInterface | null = null;
|
|
96
|
+
|
|
97
|
+
private parentElement: HtmlInterface | null = null; // For non-root views, track the parent element for correct mounting
|
|
98
|
+
|
|
99
|
+
/** Root element tree returned by renderFactory (Fragment, Html, etc.) */
|
|
100
|
+
private _rootTree: any = null;
|
|
101
|
+
/** Compiled render factory — produces the element tree */
|
|
102
|
+
private renderFactory: ViewRenderFactory | null = null;
|
|
103
|
+
/** Compiled prerender factory — produces the prerender element tree */
|
|
104
|
+
private prerenderFactory: ViewRenderFactory | null = null;
|
|
105
|
+
|
|
106
|
+
public childrenFactory: OneChildrenFactory | null = null;
|
|
107
|
+
|
|
108
|
+
/** Stored render output for lifecycle management */
|
|
109
|
+
public renderOutput: any = null;
|
|
110
|
+
/** Stored prerender output for hydration or caching */
|
|
111
|
+
public prerenderOutput: any = null;
|
|
112
|
+
/** DOM children — for compatibility with HtmlInterface */
|
|
113
|
+
public domChildren: Node[] = [];
|
|
114
|
+
/** Flag: currently in a virtual render (for hydration or caching) */
|
|
115
|
+
public isVirtualRendering: boolean = false;
|
|
116
|
+
public initMode: InitMode = 'create';
|
|
117
|
+
|
|
118
|
+
// ─── Reactive System ────────────────────────────────────────
|
|
119
|
+
/** Pending reactive updates — deduped, flushed in one RAF */
|
|
120
|
+
private pendingReactiveUpdates: Set<ReactiveInterface> = new Set();
|
|
121
|
+
private hasScheduledUpdate = false;
|
|
122
|
+
/** Centralized AbortController for all event listeners */
|
|
123
|
+
private eventAbortController: AbortController = new AbortController();
|
|
124
|
+
|
|
125
|
+
// ─── Loop ───────────────────────────────────────────────────
|
|
126
|
+
/** Current loop context stack (@foreach, @for, @while) */
|
|
127
|
+
public loopContext: LoopContext | null = null;
|
|
128
|
+
|
|
129
|
+
// ─── Layout (Sections & Blocks) ─────────────────────────────
|
|
130
|
+
/** Section management across views */
|
|
131
|
+
public sections: Map<string, SectionInterface> = new Map();
|
|
132
|
+
/** Block slots in layout views */
|
|
133
|
+
public blocks: Map<string, BlockInterface> = new Map();
|
|
134
|
+
|
|
135
|
+
public elements: Map<string, ElementChild> = new Map();
|
|
136
|
+
|
|
137
|
+
public preloadElement: WrapperInterface | null = null; // For pre-rendering elements before the main render
|
|
138
|
+
public mainElement: WrapperInterface | null = null; // The main rendered element tree
|
|
139
|
+
// ─── Lifecycle Flags ────────────────────────────────────────
|
|
140
|
+
/** Whether this view is currently active (mounted in DOM) */
|
|
141
|
+
public isActive: boolean = false;
|
|
142
|
+
/** Whether initial data has been committed */
|
|
143
|
+
private _isDataCommitted = false;
|
|
144
|
+
/** Whether the view has been mounted */
|
|
145
|
+
private _isMounted = false;
|
|
146
|
+
/** Whether the view has been fully destroyed */
|
|
147
|
+
private _isDestroyed = false;
|
|
148
|
+
/** For future use: scanning DOM for bindings */
|
|
149
|
+
protected isScanMode: boolean = false;
|
|
150
|
+
|
|
151
|
+
// --- Route params for reference in blocks and sections (set by ViewManager on navigate) ---
|
|
152
|
+
public urlPath: string | null = null;
|
|
153
|
+
|
|
154
|
+
public callingMethod: string | null = null; // For debugging: track which method is currently executing
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
// ─── Constructor ────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
constructor(view: ViewInterface, path: string = '', viewType: ViewType = 'view', viewId: string | null = null) {
|
|
160
|
+
this.__App = app<ApplicationInterface>();
|
|
161
|
+
this.view = view;
|
|
162
|
+
this.path = path;
|
|
163
|
+
this.viewType = viewType;
|
|
164
|
+
this.viewId = viewId ?? this.generateViewId();
|
|
165
|
+
this.states = new ViewState(this);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ─── Lifecycle ──────────────────────────────────────────────
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Setup — called by compiled $__setup__ with full config.
|
|
172
|
+
*
|
|
173
|
+
* Lưu config, extract metadata, lưu render factory.
|
|
174
|
+
* CHƯA gọi commitConstructorData — đợi ViewManager gọi commitData().
|
|
175
|
+
*/
|
|
176
|
+
setup(config: ViewRuntimeConfig): void {
|
|
177
|
+
this.runtimeConfig = config as ViewRuntimeConfig;
|
|
178
|
+
|
|
179
|
+
// Extract metadata
|
|
180
|
+
if (config.viewId) this.viewId = config.viewId;
|
|
181
|
+
if (config.path) this.path = config.path;
|
|
182
|
+
this.data = config.data || {};
|
|
183
|
+
this.superViewPath = config.superView || null;
|
|
184
|
+
|
|
185
|
+
// Store render factory
|
|
186
|
+
if (typeof config.render === 'function') {
|
|
187
|
+
this.setRenderFactory(config.render.bind(this));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (typeof config.prerender === 'function') {
|
|
191
|
+
this.prerenderFactory = config.prerender.bind(this);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
getConfig(key?: string, defaultValue?: any): ViewControllerConfig | any {
|
|
197
|
+
if (key) {
|
|
198
|
+
return this.runtimeConfig?.[key] ?? (this.config[key] ?? defaultValue);
|
|
199
|
+
}
|
|
200
|
+
return { ...this.runtimeConfig, ...this.config };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Set static view config shared by all instances of a compiled view class. */
|
|
204
|
+
setStaticConfig(config: ViewConfig): void {
|
|
205
|
+
this.config = config || {};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Set user-defined properties/methods on the View instance */
|
|
209
|
+
setUserDefinedConfig(userConfig: Record<string, any>): void {
|
|
210
|
+
for (const [key, value] of Object.entries(userConfig)) {
|
|
211
|
+
if (!this.ownProperties.has(key)) {
|
|
212
|
+
(this.view as any)[key] = typeof value === 'function' && value.bind
|
|
213
|
+
? value.bind(this.view)
|
|
214
|
+
: value;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** Set the compiled render factory */
|
|
220
|
+
setRenderFactory(factory: ViewRenderFactory): void {
|
|
221
|
+
this.renderFactory = factory;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Set root element — the container this view renders into */
|
|
225
|
+
setRootElement(rootElement: HtmlInterface): void {
|
|
226
|
+
this.rootElement = rootElement;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
setParentElement(parent: HtmlInterface | null): void {
|
|
230
|
+
this.parentElement = parent;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Render the view's element tree into rootElement.
|
|
234
|
+
*
|
|
235
|
+
* The compiled render factory (bound to this ViewController) returns
|
|
236
|
+
* the root element tree (typically a Fragment). We set its parent
|
|
237
|
+
* to rootElement, call render() to build DOM, and store the reference
|
|
238
|
+
* for start/stop lifecycle management.
|
|
239
|
+
*/
|
|
240
|
+
render(): any {
|
|
241
|
+
const oldCallingMethod = this.callingMethod;
|
|
242
|
+
this.callingMethod = 'render';
|
|
243
|
+
let output = this.renderFactory ? this.renderFactory() : null;
|
|
244
|
+
if (this.isVirtualRendering) {
|
|
245
|
+
return output;
|
|
246
|
+
}
|
|
247
|
+
this.renderOutput = output;
|
|
248
|
+
this.callingMethod = oldCallingMethod;
|
|
249
|
+
return output;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
prerender(): any {
|
|
253
|
+
const oldCallingMethod = this.callingMethod;
|
|
254
|
+
this.callingMethod = 'prerender';
|
|
255
|
+
let output = this.prerenderFactory ? this.prerenderFactory() : null;
|
|
256
|
+
if (this.isVirtualRendering) {
|
|
257
|
+
return output;
|
|
258
|
+
}
|
|
259
|
+
this.prerenderOutput = output;
|
|
260
|
+
this.callingMethod = oldCallingMethod;
|
|
261
|
+
return output;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
hydrate(): any {
|
|
265
|
+
this.initMode = 'hydrate';
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
hydrateRender(): any {
|
|
269
|
+
this.initMode = 'hydrate';
|
|
270
|
+
let output = this.render();
|
|
271
|
+
this.initMode = 'create';
|
|
272
|
+
return output;
|
|
273
|
+
}
|
|
274
|
+
hydratePrerender(): any {
|
|
275
|
+
this.initMode = 'hydrate';
|
|
276
|
+
let output = this.prerender();
|
|
277
|
+
this.initMode = 'create';
|
|
278
|
+
return output;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
mount(root: HTMLElement): void {
|
|
282
|
+
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
unmount(): void {
|
|
286
|
+
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Start — activate reactive subscriptions throughout the element tree.
|
|
291
|
+
* Called AFTER render() and commitData().
|
|
292
|
+
*
|
|
293
|
+
* Flow: render() → commitData() → start()
|
|
294
|
+
* This ensures initial state values are set before subscriptions fire.
|
|
295
|
+
*/
|
|
296
|
+
start(): void {
|
|
297
|
+
if (this._isDestroyed || !this._rootTree) return;
|
|
298
|
+
|
|
299
|
+
// Recursively start all children (Output, TextElement, Html, Reactive, Fragment)
|
|
300
|
+
if ('start' in this._rootTree && typeof this._rootTree.start === 'function') {
|
|
301
|
+
this._rootTree.start();
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Fire onMounted hook
|
|
305
|
+
if (typeof this.view.onMounted === 'function') {
|
|
306
|
+
try {
|
|
307
|
+
this.view.onMounted();
|
|
308
|
+
} catch (e) {
|
|
309
|
+
console.error(`[ViewController] onMounted error in "${this.path}":`, e);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Stop — deactivate reactive subscriptions (for caching/deactivation).
|
|
316
|
+
* DOM stays intact but reactive updates are paused.
|
|
317
|
+
*/
|
|
318
|
+
stop(): void {
|
|
319
|
+
if (this._isDestroyed || !this._rootTree) return;
|
|
320
|
+
|
|
321
|
+
// Recursively stop all children
|
|
322
|
+
if ('stop' in this._rootTree && typeof this._rootTree.stop === 'function') {
|
|
323
|
+
this._rootTree.stop();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Fire onDeactivated hook
|
|
327
|
+
if (typeof this.view.onDeactivated === 'function') {
|
|
328
|
+
try {
|
|
329
|
+
this.view.onDeactivated();
|
|
330
|
+
} catch (e) {
|
|
331
|
+
console.error(`[ViewController] onDeactivated error in "${this.path}":`, e);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** Full destroy — cleanup everything */
|
|
337
|
+
destroy(): void {
|
|
338
|
+
if (this._isDestroyed) return;
|
|
339
|
+
this._isDestroyed = true;
|
|
340
|
+
|
|
341
|
+
// Stop reactive subscriptions first
|
|
342
|
+
this.stop();
|
|
343
|
+
|
|
344
|
+
// Cancel pending updates
|
|
345
|
+
this.pendingReactiveUpdates.clear();
|
|
346
|
+
this.hasScheduledUpdate = false;
|
|
347
|
+
|
|
348
|
+
// Abort all event listeners
|
|
349
|
+
this.eventAbortController.abort();
|
|
350
|
+
|
|
351
|
+
// Destroy root tree
|
|
352
|
+
if (this._rootTree && 'destroy' in this._rootTree && typeof this._rootTree.destroy === 'function') {
|
|
353
|
+
this._rootTree.destroy();
|
|
354
|
+
}
|
|
355
|
+
this._rootTree = null;
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
// Destroy state
|
|
359
|
+
this.states.__.destroy();
|
|
360
|
+
|
|
361
|
+
// Cleanup loop context stack
|
|
362
|
+
this.loopContext = null;
|
|
363
|
+
|
|
364
|
+
// Fire onDestroy hook
|
|
365
|
+
if (typeof this.view.onDestroy === 'function') {
|
|
366
|
+
try {
|
|
367
|
+
this.view.onDestroy();
|
|
368
|
+
} catch (e) {
|
|
369
|
+
console.error(`[ViewController] onDestroy error in "${this.path}":`, e);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Nullify references
|
|
374
|
+
this.rootElement = null;
|
|
375
|
+
this.renderFactory = null;
|
|
376
|
+
this.prerenderFactory = null;
|
|
377
|
+
this._isMounted = false;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ─── Data Lifecycle ─────────────────────────────────────────
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Commit initial data — set initial state values.
|
|
384
|
+
* Called by ViewManager AFTER render + block mounting.
|
|
385
|
+
*
|
|
386
|
+
* Flow: commitConstructorData() → update$xxx(initial) → lockUpdateRealState()
|
|
387
|
+
*/
|
|
388
|
+
commitData(): void {
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Update data from external source (navigate same view, different params).
|
|
393
|
+
* Flow: unlock → updateVariableData(newData) → re-set states → lock
|
|
394
|
+
*/
|
|
395
|
+
updateData(newData: Record<string, any>): void {
|
|
396
|
+
this.runtimeConfig?.updateVariableData?.(newData);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Update single data item.
|
|
401
|
+
*/
|
|
402
|
+
updateDataItem(key: string, value: any): void {
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ─── Event Management ───────────────────────────────────────
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Register event listeners on an element.
|
|
409
|
+
* All listeners are tracked via AbortController for centralized cleanup.
|
|
410
|
+
*
|
|
411
|
+
* Handlers can be:
|
|
412
|
+
* - Direct function: (event) => { ... }
|
|
413
|
+
* - Object with handler + params: { handler: fn, params: [...] }
|
|
414
|
+
* - Object with string handler (method name on view): { handler: 'handleClick' }
|
|
415
|
+
*/
|
|
416
|
+
addEventListener(element: HTMLElement, event: string, handlers: OneElementEventHandler): void {
|
|
417
|
+
for (const h of handlers) {
|
|
418
|
+
let fn: EventListener;
|
|
419
|
+
|
|
420
|
+
if (typeof h === 'function') {
|
|
421
|
+
fn = h as EventListener;
|
|
422
|
+
} else if (typeof h === 'object' && h !== null) {
|
|
423
|
+
const handlerDef = h as { handler: string | ((event: Event) => any); params?: any[] };
|
|
424
|
+
if (typeof handlerDef.handler === 'string') {
|
|
425
|
+
// Method name on view
|
|
426
|
+
const methodName = handlerDef.handler;
|
|
427
|
+
const view = this.view;
|
|
428
|
+
fn = ((event: Event) => {
|
|
429
|
+
if (typeof (view as any)[methodName] === 'function') {
|
|
430
|
+
const params = handlerDef.params
|
|
431
|
+
? handlerDef.params.map(p => {
|
|
432
|
+
if (typeof p === 'function') return p(event);
|
|
433
|
+
return p === '@EVENT' ? event : p;
|
|
434
|
+
})
|
|
435
|
+
: [event];
|
|
436
|
+
(view as any)[methodName](...params);
|
|
437
|
+
}
|
|
438
|
+
}) as EventListener;
|
|
439
|
+
} else if (typeof handlerDef.handler === 'function') {
|
|
440
|
+
const originalHandler = handlerDef.handler;
|
|
441
|
+
if (handlerDef.params) {
|
|
442
|
+
fn = ((event: Event) => {
|
|
443
|
+
const params = handlerDef.params!.map((p: any) => {
|
|
444
|
+
if (typeof p === 'function') return p(event);
|
|
445
|
+
return p === '@EVENT' ? event : p;
|
|
446
|
+
});
|
|
447
|
+
(originalHandler as Function).apply(this.view, params);
|
|
448
|
+
}) as EventListener;
|
|
449
|
+
} else {
|
|
450
|
+
fn = originalHandler as EventListener;
|
|
451
|
+
}
|
|
452
|
+
} else {
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
} else {
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
element.addEventListener(event, fn, { signal: this.eventAbortController.signal });
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// ─── Reactive Scheduling ────────────────────────────────────
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Schedule a reactive region for re-render.
|
|
467
|
+
* Multiple calls in the same frame are batched into a single RAF.
|
|
468
|
+
*/
|
|
469
|
+
scheduleUpdate(reactive: ReactiveInterface): void {
|
|
470
|
+
if (this._isDestroyed) return;
|
|
471
|
+
|
|
472
|
+
this.pendingReactiveUpdates.add(reactive);
|
|
473
|
+
|
|
474
|
+
if (!this.hasScheduledUpdate) {
|
|
475
|
+
this.hasScheduledUpdate = true;
|
|
476
|
+
requestAnimationFrame(() => this.flushReactiveUpdates());
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
private flushReactiveUpdates(): void {
|
|
481
|
+
if (this._isDestroyed) return;
|
|
482
|
+
this.hasScheduledUpdate = false;
|
|
483
|
+
|
|
484
|
+
const updates = Array.from(this.pendingReactiveUpdates);
|
|
485
|
+
this.pendingReactiveUpdates.clear();
|
|
486
|
+
|
|
487
|
+
for (const reactive of updates) {
|
|
488
|
+
try {
|
|
489
|
+
reactive.render();
|
|
490
|
+
} catch (e) {
|
|
491
|
+
console.error(`[ViewController] Reactive update error in "${this.path}":`, e);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
section(name: string, config: { type: SectionItemType; contentType?: SectionContentType; stateKeys?: string[], [key: string]: any }, contentRenderFactory: SectionContentRenderer): SectionInterface {
|
|
498
|
+
if (this.sections.has(name)) {
|
|
499
|
+
const section = this.sections.get(name);
|
|
500
|
+
if (section) {
|
|
501
|
+
return section;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
config.ctx = this;
|
|
505
|
+
config.name = name;
|
|
506
|
+
config.renderFactory = contentRenderFactory;
|
|
507
|
+
const section = new Section(config as SectionConstruvtorArgs);
|
|
508
|
+
this.sections.set(name, section);
|
|
509
|
+
return section;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
// template methods called by compiled output — these build the element tree directly
|
|
514
|
+
|
|
515
|
+
block(id: string | null, name: string, contentRenderFactory: BlockRenderFactory): BlockInterface {
|
|
516
|
+
if (!id) {
|
|
517
|
+
id = `b-${name}`;
|
|
518
|
+
}
|
|
519
|
+
const existing = this.blocks.get(id);
|
|
520
|
+
const initMode = this.initMode;
|
|
521
|
+
if (existing) {
|
|
522
|
+
existing.contentRenderFactory = contentRenderFactory;
|
|
523
|
+
BlockManager.active(name, this.viewId);
|
|
524
|
+
existing.initMode = initMode;
|
|
525
|
+
return existing;
|
|
526
|
+
}
|
|
527
|
+
const block = new Block({ ctx: this, name, id, initMode, contentRenderFactory });
|
|
528
|
+
this.blocks.set(id, block);
|
|
529
|
+
BlockManager.add(block);
|
|
530
|
+
return block;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
blockOutlet(id: string | null = null, name: string, parentElement: HtmlInterface | null): BlockOutletInterface {
|
|
535
|
+
const initMode = this.initMode;
|
|
536
|
+
if(!id) {
|
|
537
|
+
id = `ob-${name}`;
|
|
538
|
+
}
|
|
539
|
+
const existing = this.elements.get(id);
|
|
540
|
+
if (existing instanceof BlockOutlet) {
|
|
541
|
+
existing.setParentElement(parentElement);
|
|
542
|
+
existing.initMode = initMode;
|
|
543
|
+
return existing;
|
|
544
|
+
}
|
|
545
|
+
const outlet = new BlockOutlet({ ctx: this, name, id, parentElement, initMode });
|
|
546
|
+
this.elements.set(id, outlet);
|
|
547
|
+
// do something if needed
|
|
548
|
+
return outlet;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
mountBlock(id: string | null = null, name: string, parent: HtmlInterface | null): BlockOutletInterface {
|
|
553
|
+
return this.blockOutlet(id, name, parent);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* @useBlock(name) — register a block slot in the layout.
|
|
559
|
+
* Creates a Reactive region between markers that will hold the block content.
|
|
560
|
+
* BlockManager.mountAll() later inserts the page view's block content here.
|
|
561
|
+
*/
|
|
562
|
+
useBlock(id: string | null = null, name: string, parent: HtmlInterface): BlockOutlet {
|
|
563
|
+
return this.blockOutlet(id, name, parent);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
yield(id: string, name: string, defaultValue: any = null, parentElement: HtmlInterface | null = null): YieldInterface {
|
|
567
|
+
const existing = this.elements.get(id);
|
|
568
|
+
if (existing instanceof YieldElement) {
|
|
569
|
+
existing.setParentElement(parentElement);
|
|
570
|
+
existing.defaultValue = defaultValue;
|
|
571
|
+
return existing;
|
|
572
|
+
}
|
|
573
|
+
const yieldEl = new YieldElement({ ctx: this, name, initMode: this.initMode, id, defaultValue });
|
|
574
|
+
yieldEl.setParentElement(parentElement);
|
|
575
|
+
this.elements.set(id, yieldEl);
|
|
576
|
+
return yieldEl;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
yieldContent(name: string, defaultValue: any = null): any {
|
|
580
|
+
return this.App.View?.yieldContent?.(name, defaultValue) ?? defaultValue;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
wrapper(factory: OneChildrenFactory): WrapperInterface {
|
|
584
|
+
const callingMethod = this.callingMethod;
|
|
585
|
+
let key: 'preloadElement' | 'mainElement' = callingMethod === 'prerender' ? 'preloadElement' : 'mainElement';
|
|
586
|
+
let wrapper = this[key];
|
|
587
|
+
if (!wrapper) {
|
|
588
|
+
wrapper = new Wrapper({ ctx: this, initMode: this.initMode, parentElement: this.parentElement, childrenFactory: factory });
|
|
589
|
+
this[key] = wrapper;
|
|
590
|
+
}else{
|
|
591
|
+
wrapper.setChildrenFactory(factory);
|
|
592
|
+
}
|
|
593
|
+
return wrapper;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
fragment(id: string | null = null, parentElement: HtmlInterface | null, childrenFactory: OneChildrenFactory): FragmentInterface {
|
|
598
|
+
if (!id) {
|
|
599
|
+
id = `fr-${generateUUID(5)}`;
|
|
600
|
+
}
|
|
601
|
+
const existing = this.elements.get(id);
|
|
602
|
+
if (existing instanceof Fragment) {
|
|
603
|
+
if(childrenFactory) {
|
|
604
|
+
existing.setChildrenFactory(childrenFactory);
|
|
605
|
+
}
|
|
606
|
+
return existing;
|
|
607
|
+
}
|
|
608
|
+
const initMode = this.initMode;
|
|
609
|
+
const fragment = new Fragment({ctx: this, id, initMode, parentElement, childrenFactory });
|
|
610
|
+
this.elements.set(id, fragment);
|
|
611
|
+
return fragment;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Template and Directive Helpers
|
|
617
|
+
* These methods are called by the compiled output for loops, conditionals, and other directives.
|
|
618
|
+
*/
|
|
619
|
+
|
|
620
|
+
html(id: string | null = null, tagName: string, parentElement: HtmlInterface | null, config: any, childrenFactory?: OneChildrenFactory): OneNodeInterface {
|
|
621
|
+
if (!id) {
|
|
622
|
+
id = `el-${tagName}-${generateUUID(5)}`;
|
|
623
|
+
}
|
|
624
|
+
const existing = this.elements.get(id);
|
|
625
|
+
if (existing instanceof Html) {
|
|
626
|
+
existing.updateConfig(config);
|
|
627
|
+
if(childrenFactory) {
|
|
628
|
+
existing.setChildrenFactory(childrenFactory);
|
|
629
|
+
}
|
|
630
|
+
return existing;
|
|
631
|
+
}
|
|
632
|
+
const element = new Html({ ctx: this, tagName, id, parentElement, initMode: this.initMode, config, childrenFactory });
|
|
633
|
+
this.elements.set(id, element);
|
|
634
|
+
return element;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
reactive(id: string | null, type: string, parentReactive: ReactiveInterface | null, parentElement: HtmlInterface | null, stateKeys: string[], childrenFactory: ReactiveChildrenFactory): ReactiveInterface {
|
|
639
|
+
// check
|
|
640
|
+
if (!id) {
|
|
641
|
+
id = `r-${type}-${generateUUID(5)}`;
|
|
642
|
+
}
|
|
643
|
+
const existing = this.elements.get(id);
|
|
644
|
+
if (existing instanceof Reactive) {
|
|
645
|
+
existing.setChildrenFactory(childrenFactory);
|
|
646
|
+
existing.setStateKeys(stateKeys);
|
|
647
|
+
return existing;
|
|
648
|
+
}
|
|
649
|
+
const reactive: ReactiveInterface = new Reactive({
|
|
650
|
+
type,
|
|
651
|
+
id,
|
|
652
|
+
ctx: this,
|
|
653
|
+
parentReactive,
|
|
654
|
+
parentElement,
|
|
655
|
+
stateKeys,
|
|
656
|
+
childrenFactory
|
|
657
|
+
});
|
|
658
|
+
this.elements.set(id, reactive);
|
|
659
|
+
return reactive;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
output(id: string | null, parent: HtmlInterface | null, isEscapeHTML: boolean = true, stateKeys: string[] = [], contentFactory: () => string = () => ''): OutputInterface {
|
|
663
|
+
if(!id) {
|
|
664
|
+
id = `o-${generateUUID(5)}`;
|
|
665
|
+
}
|
|
666
|
+
const existing = this.elements.get(id);
|
|
667
|
+
if (existing instanceof Output) {
|
|
668
|
+
existing.setStateKeys(stateKeys);
|
|
669
|
+
existing.contentFactory = contentFactory;
|
|
670
|
+
return existing;
|
|
671
|
+
}
|
|
672
|
+
const output = new Output({
|
|
673
|
+
ctx: this,
|
|
674
|
+
id,
|
|
675
|
+
parent,
|
|
676
|
+
stateKeys,
|
|
677
|
+
isEscapeHTML,
|
|
678
|
+
contentFactory,
|
|
679
|
+
});
|
|
680
|
+
this.elements.set(id, output);
|
|
681
|
+
return output;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
text(text: string): Text{
|
|
685
|
+
return document.createTextNode(text) as Text;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
include(
|
|
689
|
+
id: string | null = null,
|
|
690
|
+
path: string = '',
|
|
691
|
+
parentElement: HtmlInterface | null,
|
|
692
|
+
stateKeys: string[],
|
|
693
|
+
dataFactory: (parentElement: HtmlInterface | null) => Record<string, any>
|
|
694
|
+
): Component {
|
|
695
|
+
if(!id) {
|
|
696
|
+
id = `cpn-${generateUUID(5)}`;
|
|
697
|
+
}
|
|
698
|
+
const existing = this.elements.get(id);
|
|
699
|
+
if (existing instanceof Component) {
|
|
700
|
+
existing.setDataFactory(dataFactory);
|
|
701
|
+
if(stateKeys) {
|
|
702
|
+
existing.setStateKeys(stateKeys);
|
|
703
|
+
}
|
|
704
|
+
return existing;
|
|
705
|
+
}
|
|
706
|
+
let component = new Component({
|
|
707
|
+
ctx: this,
|
|
708
|
+
id: id ?? generateUUID(10),
|
|
709
|
+
stateKeys,
|
|
710
|
+
parent: parentElement,
|
|
711
|
+
dataFactory,
|
|
712
|
+
path,
|
|
713
|
+
type: 'default',
|
|
714
|
+
});
|
|
715
|
+
this.elements.set(id, component);
|
|
716
|
+
|
|
717
|
+
return component;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
includeIf(id: string | null = null, path: string, parentElement: HtmlInterface | null, stateKeys: string[], dataFactory: (parentElement: HtmlInterface | null) => Record<string, any>): Component {
|
|
721
|
+
if(!id) {
|
|
722
|
+
id = `c-${generateUUID(5)}`;
|
|
723
|
+
}
|
|
724
|
+
const existing = this.elements.get(id);
|
|
725
|
+
if (existing instanceof Component) {
|
|
726
|
+
existing.setDataFactory(dataFactory);
|
|
727
|
+
if(stateKeys) {
|
|
728
|
+
existing.setStateKeys(stateKeys);
|
|
729
|
+
}
|
|
730
|
+
return existing;
|
|
731
|
+
}
|
|
732
|
+
let component = new Component({
|
|
733
|
+
ctx: this,
|
|
734
|
+
id: id ?? generateUUID(10),
|
|
735
|
+
stateKeys,
|
|
736
|
+
parent: parentElement,
|
|
737
|
+
dataFactory,
|
|
738
|
+
path,
|
|
739
|
+
type: 'if',
|
|
740
|
+
});
|
|
741
|
+
this.elements.set(id, component);
|
|
742
|
+
return component;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
includeWhen(id: string | null, condition: { stateKeys: string[], checker: () => any }, path: string, parentElement: HtmlInterface | null, stateKeys: string[], dataFactory: (parentElement: HtmlInterface | null) => Record<string, any>): Component {
|
|
746
|
+
if(!id) {
|
|
747
|
+
id = `cpn-${generateUUID(5)}`;
|
|
748
|
+
}
|
|
749
|
+
const existing = this.elements.get(id);
|
|
750
|
+
if (existing instanceof Component) {
|
|
751
|
+
existing.setDataFactory(dataFactory);
|
|
752
|
+
existing.setCondition(condition);
|
|
753
|
+
if(stateKeys) {
|
|
754
|
+
existing.setStateKeys(stateKeys);
|
|
755
|
+
}
|
|
756
|
+
return existing;
|
|
757
|
+
}
|
|
758
|
+
let component = new Component({
|
|
759
|
+
ctx: this,
|
|
760
|
+
id: id ?? generateUUID(10),
|
|
761
|
+
stateKeys,
|
|
762
|
+
parent: parentElement,
|
|
763
|
+
dataFactory,
|
|
764
|
+
path,
|
|
765
|
+
type: 'when',
|
|
766
|
+
condition,
|
|
767
|
+
});
|
|
768
|
+
this.elements.set(id, component);
|
|
769
|
+
return component;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
extendView(path: string, data: Record<string, any> = {}): ViewInterface | null {
|
|
773
|
+
this.superViewPath = path;
|
|
774
|
+
const superView:ViewInterface = this.App.View?.view(path, data, true);
|
|
775
|
+
if(superView){
|
|
776
|
+
this.setSuperView(superView?.__ctrl__);
|
|
777
|
+
return superView;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// If super view not found, log error and return null
|
|
781
|
+
return superView;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
// ─── Loop Directives ────────────────────────────────────────
|
|
786
|
+
|
|
787
|
+
/** Create and push a new LoopContext onto the stack */
|
|
788
|
+
__setLoopContext(length: number): LoopContext {
|
|
789
|
+
const parent = this.loopContext;
|
|
790
|
+
this.loopContext = new LoopContext(parent);
|
|
791
|
+
this.loopContext.setCount(length);
|
|
792
|
+
return this.loopContext;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/** Pop the current LoopContext, restore parent */
|
|
796
|
+
__resetLoopContext(): LoopContext | null {
|
|
797
|
+
if (this.loopContext?.parent) {
|
|
798
|
+
const parent = this.loopContext.parent;
|
|
799
|
+
this.loopContext.parent = null;
|
|
800
|
+
Object.freeze(this.loopContext);
|
|
801
|
+
this.loopContext = parent;
|
|
802
|
+
} else if (this.loopContext) {
|
|
803
|
+
Object.freeze(this.loopContext);
|
|
804
|
+
this.loopContext = null;
|
|
805
|
+
}
|
|
806
|
+
return this.loopContext;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* @foreach directive — iterate over array or object.
|
|
811
|
+
* Returns array of children (not HTML string like the old system).
|
|
812
|
+
*
|
|
813
|
+
* @example Compiled output:
|
|
814
|
+
* ctrl.__foreach(items, (item, key, index, loop) => [
|
|
815
|
+
* em.h(ctrl, parent, 'div', {}, () => [em.t(`Item: ${item}`)])
|
|
816
|
+
* ])
|
|
817
|
+
*/
|
|
818
|
+
__foreach<T>(
|
|
819
|
+
list: T[] | Record<string, T>,
|
|
820
|
+
callback: (item: T, key: string, index: number, loop: LoopContext) => any
|
|
821
|
+
): any[] {
|
|
822
|
+
if (!list || typeof list !== 'object') return [];
|
|
823
|
+
|
|
824
|
+
const result: any[] = [];
|
|
825
|
+
try {
|
|
826
|
+
if (Array.isArray(list)) {
|
|
827
|
+
const loopCtx = this.__setLoopContext(list.length);
|
|
828
|
+
loopCtx.setType('increment');
|
|
829
|
+
list.forEach((item, index) => {
|
|
830
|
+
loopCtx.setCurrentTimes(index);
|
|
831
|
+
const output = callback(item, String(index), index, loopCtx);
|
|
832
|
+
if (output !== undefined && output !== null) {
|
|
833
|
+
if (Array.isArray(output)) result.push(...output);
|
|
834
|
+
else result.push(output);
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
} else {
|
|
838
|
+
const keys = Object.keys(list);
|
|
839
|
+
const loopCtx = this.__setLoopContext(keys.length);
|
|
840
|
+
loopCtx.setType('increment');
|
|
841
|
+
keys.forEach((key, index) => {
|
|
842
|
+
loopCtx.setCurrentTimes(index);
|
|
843
|
+
const output = callback((list as Record<string, T>)[key], key, index, loopCtx);
|
|
844
|
+
if (output !== undefined && output !== null) {
|
|
845
|
+
if (Array.isArray(output)) result.push(...output);
|
|
846
|
+
else result.push(output);
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
} catch (e) {
|
|
851
|
+
console.error(`[ViewController] @foreach error in "${this.path}":`, e);
|
|
852
|
+
} finally {
|
|
853
|
+
this.__resetLoopContext();
|
|
854
|
+
}
|
|
855
|
+
return result;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
__forelse<T>(list: T[], callback: (item: T, key: string, index: number, loop: LoopContext) => any, emptyCallback: () => any = () => []): any[] {
|
|
859
|
+
if (this.App.Helper.count(list) === 0) {
|
|
860
|
+
return emptyCallback();
|
|
861
|
+
}
|
|
862
|
+
return this.__foreach(list, callback);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
__each<T>(list: T[], callback: (item: T, key: string, index: number, loop: LoopContext) => any): any[] {
|
|
866
|
+
return this.__foreach(list, callback);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* @for directive
|
|
871
|
+
*/
|
|
872
|
+
__for(
|
|
873
|
+
loopType: 'increment' | 'decrement' = 'increment',
|
|
874
|
+
start: number = 0,
|
|
875
|
+
end: number = 0,
|
|
876
|
+
execute: (loop: LoopContext) => any = () => []
|
|
877
|
+
): any {
|
|
878
|
+
const loopCtx = this.__setLoopContext(end);
|
|
879
|
+
loopCtx.setType(loopType);
|
|
880
|
+
let result: any;
|
|
881
|
+
try {
|
|
882
|
+
result = typeof execute === 'function' ? execute(loopCtx) : [];
|
|
883
|
+
} catch (e) {
|
|
884
|
+
console.error(`[ViewController] @for error in "${this.path}":`, e);
|
|
885
|
+
result = [];
|
|
886
|
+
} finally {
|
|
887
|
+
this.__resetLoopContext();
|
|
888
|
+
}
|
|
889
|
+
return result;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* @while directive
|
|
894
|
+
*/
|
|
895
|
+
__while(execute: (loop: LoopContext) => any, maxIterations: number = 10000): any {
|
|
896
|
+
const loopCtx = this.__setLoopContext(-1);
|
|
897
|
+
loopCtx.setType('increment');
|
|
898
|
+
let result: any;
|
|
899
|
+
try {
|
|
900
|
+
result = typeof execute === 'function' ? execute(loopCtx) : [];
|
|
901
|
+
} catch (e) {
|
|
902
|
+
console.error(`[ViewController] @while error in "${this.path}":`, e);
|
|
903
|
+
result = [];
|
|
904
|
+
} finally {
|
|
905
|
+
if (loopCtx.count === -1) {
|
|
906
|
+
(loopCtx as any).count = loopCtx.iteration;
|
|
907
|
+
}
|
|
908
|
+
this.__resetLoopContext();
|
|
909
|
+
}
|
|
910
|
+
return result;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
// mounting
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
// ─── Config & App Access ────────────────────────────────────
|
|
918
|
+
|
|
919
|
+
setApp(app: ApplicationInterface): void {
|
|
920
|
+
this.__App = app;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
getParentView(): ViewInterface | null {
|
|
925
|
+
return this.parent ? this.parent.view : null;
|
|
926
|
+
}
|
|
927
|
+
getChildrenViews(): ViewInterface[] {
|
|
928
|
+
return this.children.map(child => child.view);
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
getSuperView(): ViewInterface | null {
|
|
932
|
+
return this.superView ? this.superView.view : null;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
getOriginView(): ViewInterface | null {
|
|
936
|
+
return this.originView?.view || null;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
setOriginView(origin: ViewControllerInterface): void {
|
|
940
|
+
this.originView = origin;
|
|
941
|
+
}
|
|
942
|
+
setSuperView(superView: ViewControllerInterface): void {
|
|
943
|
+
this.superView = superView;
|
|
944
|
+
}
|
|
945
|
+
setIsSuperView(isSuper: boolean): void {
|
|
946
|
+
this.isSuperView = isSuper;
|
|
947
|
+
}
|
|
948
|
+
setParent(parent: ViewControllerInterface): void {
|
|
949
|
+
this.parent = parent;
|
|
950
|
+
}
|
|
951
|
+
addChild(child: ViewControllerInterface): void {
|
|
952
|
+
this.children.push(child);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* For nested views: set the chain of super views up to the root, so each view has a reference to its layout parents.
|
|
958
|
+
* Called by ViewManager after creating the view and its super view(s).
|
|
959
|
+
*/
|
|
960
|
+
setChainFromOrigin(): void {
|
|
961
|
+
if(this.superView){
|
|
962
|
+
this.superView.setOriginView(this);
|
|
963
|
+
this.superView.setChainFromOrigin();
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* When a view is destroyed, it should eject itself from the origin chain to prevent memory leaks.
|
|
969
|
+
*/
|
|
970
|
+
ejectOriginChain(): void {
|
|
971
|
+
if(this.originView){
|
|
972
|
+
const origin = this.originView;
|
|
973
|
+
this.originView = null;
|
|
974
|
+
origin.ejectOriginChain();
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
get App(): ApplicationInterface {
|
|
979
|
+
return this.__App as ApplicationInterface;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
get isMounted(): boolean {
|
|
983
|
+
return this._isMounted;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
get isDestroyed(): boolean {
|
|
987
|
+
return this._isDestroyed;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
|
|
991
|
+
/** Whether this view has a super view (layout) */
|
|
992
|
+
get hasSuperView(): boolean {
|
|
993
|
+
return this.superViewPath !== null && this.superViewPath !== '';
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
// ─── Private ────────────────────────────────────────────────
|
|
998
|
+
|
|
999
|
+
private generateViewId(): string {
|
|
1000
|
+
return `v_${Date.now().toString(36)}_${Math.random().toString(36).substring(2, 8)}`;
|
|
1001
|
+
}
|
|
1002
|
+
}
|