import * as Page from "./types/page"; import { Options as ComponentOptions, Extension } from "./types/component"; import Loader from "./common/loader"; import factory from "./common/factory"; import BaseComponentRender from "./component/BaseComponentRender"; import BasePageRender from "./component/BasePageRender"; import EventEmitter from "./common/event-emitter"; import Context from "./common/context"; import PageRender from "./render/page"; // import FlowGraph from "./render/graph"; import { LOADED, LOADING } from "./constant/context"; import { Mode, Platform } from "./types/page"; import { getComponentByUid, getNearestComponentByHtmlElement } from "./common/util"; import DefaultRenderer from "./default-render"; // @ts-ignore 设置默认的内置渲染组件 factory.loaded("default", DefaultRenderer); type PageOptions = Page.Options; // export { factory, ComponentOptions, BaseComponentRender, PageOptions, Extension, FlowGraph }; export { factory, ComponentOptions, BaseComponentRender, PageOptions, Extension }; export default class Core extends EventEmitter { private $loader: Loader; public $el: HTMLElement; public $context: Context; public $page: BasePageRender | undefined; private $domEvent: Record = {}; constructor(el: HTMLElement) { super(); this.$el = el; this.$context = new Context(); this.$loader = new Loader(); } /** * 删除指定的组件实例 * @param uid */ public remove(uid: string) { const removeComponent = (list: BaseComponentRender[] | undefined) => { if (!list || list.length === 0) { return; } for (let i = list.length - 1; i >= 0; i--) { if (list[i].$options.uid === uid) { list[i].remove(); return; } else if (list[i].$children) { removeComponent(list[i].$children); } } }; removeComponent(this.$page?.$children); } /** * 加载某个页面 * @param url */ public load(url: string) { this.$loader.load(url).then((options) => { this.render(options); }); } /** * 渲染页面 */ public render(options: Page.Options) { this.dispose(); if (!options) { return; } if (this.$context) { this.$context.reset(); } // 加载事件 let loadingFn = (uid: string, percent: number) => { this.emit(LOADING, uid, percent); }; // 透传 loading 事件 this.$context.addListener(LOADING, loadingFn); // 透传 loaded 事件 this.$context.once(LOADED, (uid: string, percent: number) => { this.emit(LOADED); this.removeListener(LOADING, loadingFn); }); this.setMode(options.mode); this.setPlatform(options.platform); // 渲染 or 重绘 if (this.$page) { this.$page.rerender(options); } else { this.$page = new PageRender(this.$el, options, this.$context); this.bindDomEvent(this.$domEvent); } } /** * 绑定事件 */ private bindDomEvent(domEvent: Record) { if (!this.$page) { return; } // 进行DOM事件注册 Object.keys(domEvent).forEach((key: string) => { this.$domEvent[key].forEach((fn) => { this.$page!.addListener(key, fn); }); }); } /** * 注册DOM事件 * @param type * @param fn */ public registerEvent(type: string, fn: Function) { if (!this.$domEvent[type]) { this.$domEvent[type] = [fn]; } else if (this.$domEvent[type].indexOf((_fn: Function) => _fn !== fn) === -1) { this.$domEvent[type].push(fn); } else { return; } this.bindDomEvent({ type: [fn] }); } /** * 设置模式 * @param mode */ public setMode(mode: Mode) { this.$context.setMode(mode); } /** * 设置平台 * @param platform */ public setPlatform(platform: Platform) { this.$context.setPlatform(platform); } /** * 销毁页面 */ public dispose() { this.$page?.remove(); this.$context.reset(); } /** * 导出页面配置 */ public exportPage(): Page.Options | undefined { let exportJson = (children: Array) => { return children.map((child) => { let options = child.exportJson(); if (child.$children && child.$children.length > 0) { options.children = exportJson(child.$children); } return options; }); }; let options = this.$page?.exportJson(); if (options) { options.components = exportJson(this.$page?.$children || []); } return options; } /** * 全局混入 * @param component */ public mixins(component: Record) { this.$context.addMixins(component); } /** * 通过uid获取component实例 * @param uid */ public getComponentByUid(uid: string): BaseComponentRender | undefined { return getComponentByUid(uid, this.$page?.$children || []); } /** * 通过html节点获取最近的component * @param element */ public getNearestComponentByHtmlElement(element: HTMLElement) { return getNearestComponentByHtmlElement(element, this.$page?.$children || []); } }