This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
Doric/doric-js/src/pattern/modular.ts
2021-05-18 17:26:30 +08:00

191 lines
4.5 KiB
TypeScript

import { Panel } from "../ui/panel"
import { Group } from "../ui/view"
import { ClassType } from "../util/types"
import { ViewHolder, ViewModel } from "./mvvm"
import { Provider } from "./provider"
export abstract class Module extends Panel {
superPanel?: ModularPanel
__provider?: Provider
get provider(): Provider | undefined {
return this.__provider || (this.superPanel?.provider)
}
set provider(provider: Provider | undefined) {
this.__provider = provider
}
private unmounted = false
mount() {
if (this.unmounted) {
this.unmounted = false
this.superPanel?.onStructureChanged(this, true)
this.onMounted()
}
}
unmount() {
if (!this.unmounted) {
this.unmounted = true
this.superPanel?.onStructureChanged(this, false)
this.onUnmounted()
}
}
get mounted() {
return !this.unmounted
}
/**
* Dispatch message to other modules.
* @param message which is sent out
*/
dispatchMessage(message: any) {
this.superPanel?.dispatchMessage(message)
}
/**
* Dispatched messages can be received by override this method.
* @param message recevied message
*/
onMessage(message: any) { }
/**
* Called when this module is mounted
*/
onMounted() { }
/**
* Called when this module is unmounted
*/
onUnmounted() { }
}
export abstract class VMModule<M extends Object, V extends ViewHolder> extends Module {
private vm?: ViewModel<M, V>
private vh?: V
abstract getViewModelClass(): ClassType<ViewModel<M, V>>
abstract getState(): M
abstract getViewHolderClass(): ClassType<V>
getViewModel() {
return this.vm
}
build(root: Group): void {
this.vh = new (this.getViewHolderClass())
this.vm = new (this.getViewModelClass())(this.getState(), this.vh)
this.vm.context = this.context
this.vm.attach(root)
}
}
export abstract class ModularPanel extends Module {
private modules: Panel[]
private scheduledRebuild?: NodeJS.Timeout
constructor() {
super()
this.modules = this.setupModules().map(e => {
const instance = new e
if (instance instanceof Module) {
instance.superPanel = this
}
return instance
})
}
/**
* @returns Class list of current submodules
*/
abstract setupModules(): ClassType<Panel>[]
/**
* @param root Current module's rootview
* @returns provided for submodules as their rootview
*/
abstract setupShelf(root: Group): Group
dispatchMessage(message: any) {
if (this.superPanel) {
this.superPanel.dispatchMessage(message)
} else {
this.onMessage(message)
}
}
get mountedModules() {
return this.modules.filter(e => !(e instanceof Module) || e.mounted)
}
onMessage(message: any) {
this.mountedModules.forEach(e => {
if (e instanceof Module) {
e.onMessage(message)
}
})
}
onStructureChanged(module: Module, mounted: boolean) {
if (this.superPanel) {
this.superPanel.onStructureChanged(module, mounted)
} else {
if (!!!this.scheduledRebuild) {
this.scheduledRebuild = setTimeout(() => {
this.scheduledRebuild = undefined
this.getRootView().children.length = 0
this.build(this.getRootView())
}, 0)
}
}
}
build(root: Group) {
const groupView = this.setupShelf(root)
this.mountedModules.forEach(e => {
Reflect.set(e, "__root__", groupView)
e.build(groupView)
})
}
onCreate() {
super.onCreate()
this.mountedModules.forEach(e => {
e.context = this.context
e.onCreate()
})
}
onDestroy() {
super.onDestroy()
this.mountedModules.forEach(e => {
e.onDestroy()
})
}
onShow() {
super.onShow()
this.mountedModules.forEach(e => {
e.onShow()
})
}
onHidden() {
super.onHidden()
this.mountedModules.forEach(e => {
e.onHidden()
})
}
onRenderFinished() {
super.onRenderFinished()
this.mountedModules.forEach(e => {
e.onRenderFinished()
})
}
}