diff --git a/packages/designer/src/builtin-simulator/host-view.tsx b/packages/designer/src/builtin-simulator/host-view.tsx index 8a5def39c..decdcf48d 100644 --- a/packages/designer/src/builtin-simulator/host-view.tsx +++ b/packages/designer/src/builtin-simulator/host-view.tsx @@ -1,9 +1,8 @@ import { Component } from 'react'; import { observer } from '@ali/lowcode-editor-core'; import { BuiltinSimulatorHost, BuiltinSimulatorProps } from './host'; -import { DocumentModel } from '../document'; -import { SimulatorContext } from './context'; import { BemTools } from './bem-tools'; +import { Project } from '../project'; import './host.less'; /* @@ -11,12 +10,11 @@ import './host.less'; Canvas(DeviceShell) 设备壳层,通过背景图片来模拟,通过设备预设样式改变宽度、高度及定位 CanvasViewport CanvasViewport 页面编排场景中宽高不可溢出 Canvas 区 Content(Shell) 内容外层,宽高紧贴 CanvasViewport,禁用边框,禁用 margin - ContentFrame 可设置宽高,在页面场景一般只设置框,高度拉伸贴合 Content - Auxiliary 辅助显示层,初始相对 Content 位置 0,0,紧贴 Canvas, 根据 Content 滚动位置,改变相对位置 + BemTools 辅助显示层,初始相对 Content 位置 0,0,紧贴 Canvas, 根据 Content 滚动位置,改变相对位置 */ type SimulatorHostProps = BuiltinSimulatorProps & { - documentContext: DocumentModel; + project: Project; onMount?: (host: BuiltinSimulatorHost) => void; }; @@ -24,8 +22,8 @@ export class BuiltinSimulatorHostView extends Component { readonly host: BuiltinSimulatorHost; constructor(props: any) { super(props); - const { documentContext } = this.props; - this.host = (documentContext.simulator as BuiltinSimulatorHost) || new BuiltinSimulatorHost(documentContext); + const { project } = this.props; + this.host = (project.simulator as BuiltinSimulatorHost) || new BuiltinSimulatorHost(project); this.host.setProps(this.props); } shouldComponentUpdate(nextProps: BuiltinSimulatorProps) { diff --git a/packages/designer/src/builtin-simulator/host.ts b/packages/designer/src/builtin-simulator/host.ts index 6ca5910e3..773e690c3 100644 --- a/packages/designer/src/builtin-simulator/host.ts +++ b/packages/designer/src/builtin-simulator/host.ts @@ -4,7 +4,17 @@ import Viewport from './viewport'; import { createSimulator } from './create-simulator'; import { Node, ParentalNode, DocumentModel, isNode, contains, isRootNode } from '../document'; import ResourceConsumer from './resource-consumer'; -import { AssetLevel, Asset, AssetList, assetBundle, assetItem, AssetType, isElement, isFormEvent } from '@ali/lowcode-utils'; +import { + AssetLevel, + Asset, + AssetList, + assetBundle, + assetItem, + AssetType, + isElement, + isFormEvent, + hasOwnProperty, +} from '@ali/lowcode-utils'; import { DragObjectType, isShaken, @@ -26,6 +36,7 @@ import { ComponentMetadata, ComponentSchema } from '@ali/lowcode-types'; import { BuiltinSimulatorRenderer } from './renderer'; import clipboard from '../designer/clipboard'; import { LiveEditing } from './live-editing/live-editing'; +import { Project } from '../project'; export interface LibraryItem { package: string; @@ -74,9 +85,9 @@ const defaultEnvironment = [ export class BuiltinSimulatorHost implements ISimulatorHost { readonly isSimulator = true; - constructor(readonly document: DocumentModel) {} + constructor(readonly project: Project) {} - readonly designer = this.document.designer; + readonly designer = this.project.designer; @computed get device(): string { return this.get('device') || 'default'; @@ -326,7 +337,12 @@ export class BuiltinSimulatorHost implements ISimulatorHost item.contains(targetElement)) as HTMLElement; + const rootElement = this.findDOMNodes(nodeInst.instance, node.componentMeta.rootSelector)?.find((item) => + item.contains(targetElement), + ) as HTMLElement; if (!rootElement) { return; } @@ -405,7 +423,6 @@ export class BuiltinSimulatorHost implements ISimulatorHost(); - setInstance(id: string, instances: ComponentInstance[] | null) { + @obx private instancesMap: { + [docId: string]: Map; + } = {}; + setInstance(docId: string, id: string, instances: ComponentInstance[] | null) { + if (hasOwnProperty(this.instancesMap, docId)) { + this.instancesMap[docId] = new Map(); + } if (instances == null) { - this.instancesMap.delete(id); + this.instancesMap[docId].delete(id); } else { - this.instancesMap.set(id, instances.slice()); + this.instancesMap[docId].set(id, instances.slice()); } } @@ -503,7 +525,9 @@ export class BuiltinSimulatorHost implements ISimulatorHost | null; findDOMNodes(instance: ComponentInstance): Array | null; getClientRects(element: Element | Text): DOMRect[]; diff --git a/packages/designer/src/designer/designer.ts b/packages/designer/src/designer/designer.ts index 3b09b1b3f..62260a8c4 100644 --- a/packages/designer/src/designer/designer.ts +++ b/packages/designer/src/designer/designer.ts @@ -348,7 +348,7 @@ export class Designer { @obx.ref private _simulatorProps?: object | ((document: DocumentModel) => object); - @computed get simulatorProps(): object | ((document: DocumentModel) => object) { + @computed get simulatorProps(): object | ((project: Project) => object) { return this._simulatorProps || {}; } diff --git a/packages/designer/src/document/document-model.ts b/packages/designer/src/document/document-model.ts index 8e0eb3911..5693c2574 100644 --- a/packages/designer/src/document/document-model.ts +++ b/packages/designer/src/document/document-model.ts @@ -41,7 +41,6 @@ export class DocumentModel { private nodesMap = new Map(); @obx.val private nodes = new Set(); private seqId = 0; - private _simulator?: ISimulatorHost; private emitter: EventEmitter; private rootNodeVisitorMap: { [visitorName: string]: any } = {}; private modalNodesManager: ModalNodesManager; @@ -50,7 +49,7 @@ export class DocumentModel { * 模拟器 */ get simulator(): ISimulatorHost | null { - return this._simulator || null; + return this.project.simulator; } get fileName(): string { @@ -320,27 +319,6 @@ export class DocumentModel { return !this.history.isSavePoint(); } - /** - * 提供给模拟器的参数 - */ - @computed get simulatorProps(): object { - let simulatorProps = this.designer.simulatorProps; - if (typeof simulatorProps === 'function') { - simulatorProps = simulatorProps(this); - } - return { - ...simulatorProps, - documentContext: this, - onMount: this.mountSimulator.bind(this), - }; - } - - private mountSimulator(simulator: ISimulatorHost) { - // TODO: 多设备 simulator 支持 - this._simulator = simulator; - // TODO: emit simulator mounted - } - // FIXME: does needed? getComponent(componentName: string): any { return this.simulator!.getComponent(componentName); @@ -498,17 +476,6 @@ export class DocumentModel { return this.rootNode; } - onRendererReady(fn: (args: any) => void): () => void { - this.emitter.on('lowcode_engine_renderer_ready', fn); - return () => { - this.emitter.removeListener('lowcode_engine_renderer_ready', fn); - }; - } - - setRendererReady(renderer: any) { - this.emitter.emit('lowcode_engine_renderer_ready', renderer); - } - acceptRootNodeVisitor( visitorName: string = 'default', visitorFn: (node: RootNode) => any ) { diff --git a/packages/designer/src/document/document-view.tsx b/packages/designer/src/document/document-view.tsx deleted file mode 100644 index 2565f34fc..000000000 --- a/packages/designer/src/document/document-view.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Component } from 'react'; -import classNames from 'classnames'; -import { observer } from '@ali/lowcode-editor-core'; -import { DocumentModel } from './document-model'; -import { BuiltinSimulatorHostView } from '../builtin-simulator'; - -@observer -export class DocumentView extends Component<{ document: DocumentModel }> { - shouldComponentUpdate() { - return false; - } - render() { - const { document } = this.props; - const simulatorProps = document.simulatorProps; - const Simulator = document.designer.simulatorComponent || BuiltinSimulatorHostView; - return ( -
- {/* 这一层将来做缩放用途 */} -
- -
- -
- ); - } -} - -class DocumentInfoView extends Component<{ document: DocumentModel }> { - render() { - return null; - } -} diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts index ba4db3378..b4a66dd9a 100644 --- a/packages/designer/src/document/node/node.ts +++ b/packages/designer/src/document/node/node.ts @@ -533,6 +533,9 @@ export class Node { if (stage !== TransformStage.Clone) { baseSchema.id = this.id; } + if (stage === TransformStage.Render) { + baseSchema.docId = this.document.id; + } if (this.isLeaf()) { baseSchema.children = this.props.get('children')?.export(stage); diff --git a/packages/designer/src/project/project-view.tsx b/packages/designer/src/project/project-view.tsx index 649358b62..5f2c05ddb 100644 --- a/packages/designer/src/project/project-view.tsx +++ b/packages/designer/src/project/project-view.tsx @@ -1,23 +1,22 @@ import { Component } from 'react'; import { observer } from '@ali/lowcode-editor-core'; import { Designer } from '../designer'; -import { DocumentView } from '../document'; -import { intl } from '../locale'; +import { BuiltinSimulatorHostView } from '../builtin-simulator'; import './project.less'; @observer export class ProjectView extends Component<{ designer: Designer }> { render() { const { designer } = this.props; - // TODO: support splitview - const opens = designer.project.documents.filter((doc) => doc.opened); + const project = designer.project; + const simulatorProps = project.simulatorProps; + const Simulator = designer.simulatorComponent || BuiltinSimulatorHostView; + return (
- {opens.length > 0 ? ( - opens.map((doc) => ) - ) : ( -
{intl('No opened document')}
- )} +
+ +
); } diff --git a/packages/designer/src/project/project.ts b/packages/designer/src/project/project.ts index e010c5476..b2be6eb90 100644 --- a/packages/designer/src/project/project.ts +++ b/packages/designer/src/project/project.ts @@ -3,6 +3,7 @@ import { obx, computed } from '@ali/lowcode-editor-core'; import { Designer } from '../designer'; import { DocumentModel, isDocumentModel } from '../document'; import { ProjectSchema, RootSchema } from '@ali/lowcode-types'; +import { ISimulatorHost } from '../simulator'; export class Project { private emitter = new EventEmitter(); @@ -12,6 +13,15 @@ export class Project { @obx.ref canvasDisplayMode: 'exclusive' | 'overview' = 'exclusive'; + private _simulator?: ISimulatorHost; + + /** + * 模拟器 + */ + get simulator(): ISimulatorHost | null { + return this._simulator || null; + } + // TODO: 考虑项目级别 History constructor(readonly designer: Designer, schema?: ProjectSchema) { @@ -102,7 +112,7 @@ export class Project { | string, ): any {} - open(doc?: string | DocumentModel | RootSchema): void { + open(doc?: string | DocumentModel | RootSchema): DocumentModel | null { if (!doc) { const got = this.documents.find((item) => item.isBlank()); if (got) { @@ -125,7 +135,7 @@ export class Project { return doc.open(); } - return; + return null; } if (isDocumentModel(doc)) { @@ -154,6 +164,37 @@ export class Project { }); } + /** + * 提供给模拟器的参数 + */ + @computed get simulatorProps(): object { + let simulatorProps = this.designer.simulatorProps; + if (typeof simulatorProps === 'function') { + simulatorProps = simulatorProps(this); + } + return { + ...simulatorProps, + project: this, + onMount: this.mountSimulator.bind(this), + }; + } + + private mountSimulator(simulator: ISimulatorHost) { + // TODO: 多设备 simulator 支持 + this._simulator = simulator; + } + + setRendererReady(renderer: any) { + this.emitter.emit('lowcode_engine_renderer_ready', renderer); + } + + onRendererReady(fn: (args: any) => void): () => void { + this.emitter.on('lowcode_engine_renderer_ready', fn); + return () => { + this.emitter.removeListener('lowcode_engine_renderer_ready', fn); + }; + } + onCurrentDocumentChange(fn: (doc: DocumentModel) => void): () => void { this.emitter.on('current-document.change', fn); return () => { diff --git a/packages/designer/src/simulator.ts b/packages/designer/src/simulator.ts index e383517e3..2247f1d0d 100644 --- a/packages/designer/src/simulator.ts +++ b/packages/designer/src/simulator.ts @@ -142,7 +142,7 @@ export interface ISimulatorHost

extends ISensor { computeComponentInstanceRect(instance: ComponentInstance, selector?: string): DOMRect | null; findDOMNodes(instance: ComponentInstance, selector?: string): Array | null; - + /** * 销毁 */ @@ -154,6 +154,7 @@ export function isSimulatorHost(obj: any): obj is ISimulatorHost { } export interface NodeInstance { + docId: string; nodeId: string; instance: T; node?: Node | null; diff --git a/packages/editor-core/src/utils/monitor.ts b/packages/editor-core/src/utils/monitor.ts index 66ec0f39b..80313a55b 100644 --- a/packages/editor-core/src/utils/monitor.ts +++ b/packages/editor-core/src/utils/monitor.ts @@ -1,4 +1,4 @@ -class Monitor { +export class Monitor { fn = (params: any) => { const { AES } = window as any; if (typeof AES.log === 'function') { diff --git a/packages/editor-preset-vision/src/editor.ts b/packages/editor-preset-vision/src/editor.ts index efc3accb5..ba570823f 100644 --- a/packages/editor-preset-vision/src/editor.ts +++ b/packages/editor-preset-vision/src/editor.ts @@ -25,10 +25,8 @@ export const designer = new Designer({ editor: editor }); editor.set(Designer, designer); editor.set('designer', designer); -designer.project.onCurrentDocumentChange((doc) => { - doc.onRendererReady(() => { - bus.emit(VE_EVENTS.VE_PAGE_PAGE_READY); - }); +designer.project.onRendererReady(() => { + bus.emit(VE_EVENTS.VE_PAGE_PAGE_READY); }); interface Variable { diff --git a/packages/react-simulator-renderer/src/renderer-view.tsx b/packages/react-simulator-renderer/src/renderer-view.tsx index ca00e07ee..81ecba8af 100644 --- a/packages/react-simulator-renderer/src/renderer-view.tsx +++ b/packages/react-simulator-renderer/src/renderer-view.tsx @@ -1,9 +1,7 @@ import LowCodeRenderer from '@ali/lowcode-react-renderer'; -import { isObject } from 'lodash'; import { ReactInstance, Fragment, Component, createElement } from 'react'; import { observer } from '@recore/obx-react'; -import { SimulatorRenderer } from './renderer'; -import { host } from './host'; +import { SimulatorRendererContainer, DocumentInstance } from './renderer'; import './renderer.less'; // patch cloneElement avoid lost keyProps @@ -36,12 +34,20 @@ const originCloneElement = window.React.cloneElement; return originCloneElement(child, props, ...rest); }; -export default class SimulatorRendererView extends Component<{ renderer: SimulatorRenderer }> { +export default class SimulatorRendererView extends Component<{ rendererContainer: SimulatorRendererContainer }> { render() { - const { renderer } = this.props; + const { rendererContainer } = this.props; return ( - - + + { + rendererContainer.redirect(currentPath); + }}> + {rendererContainer.getDocumentInstances().map((instance) => { + return + + + })} + ); } @@ -68,13 +74,13 @@ function getDeviceView(view: any, device: string, mode: string) { } @observer -class Layout extends Component<{ renderer: SimulatorRenderer }> { +class Layout extends Component<{ rendererContainer: SimulatorRendererContainer }> { shouldComponentUpdate() { return false; } render() { - const { renderer, children } = this.props; - const layout = renderer.layout; + const { rendererContainer, children } = this.props; + const layout = rendererContainer.layout; if (layout) { const { Component, props } = layout; @@ -86,26 +92,28 @@ class Layout extends Component<{ renderer: SimulatorRenderer }> { } @observer -class Renderer extends Component<{ renderer: SimulatorRenderer }> { +class Renderer extends Component<{ documentInstance: DocumentInstance }> { shouldComponentUpdate() { return false; } render() { - const { renderer } = this.props; - const { device, designMode } = renderer; + const { documentInstance } = this.props; + const { container } = documentInstance; + const { designMode, device } = container; + return ( { const { __id, __desingMode, ...viewProps } = props; viewProps.componentId = __id; - const leaf = host.document.getNode(__id); + const leaf = documentInstance.getNode(__id); viewProps._leaf = leaf; viewProps._componentName = leaf?.componentName; let _children = leaf?.isContainer() ? (children == null ? [] : Array.isArray(children) ? children : [children]) : children; @@ -158,7 +166,7 @@ class Renderer extends Component<{ renderer: SimulatorRenderer }> { ); }} onCompGetRef={(schema: any, ref: ReactInstance | null) => { - renderer.mountInstance(schema.id, ref); + documentInstance.mountInstance(schema.id, ref); }} //onCompGetCtx={(schema: any, ctx: object) => { // renderer.mountContext(schema.id, ctx); diff --git a/packages/react-simulator-renderer/src/renderer.ts b/packages/react-simulator-renderer/src/renderer.ts index dd578ce4b..f34d88b1c 100644 --- a/packages/react-simulator-renderer/src/renderer.ts +++ b/packages/react-simulator-renderer/src/renderer.ts @@ -10,120 +10,47 @@ import { reactFindDOMNodes, FIBER_KEY } from './utils/react-find-dom-nodes'; import { isESModule, isElement, cursor, setNativeSelection } from '@ali/lowcode-utils'; import { RootSchema, NpmInfo, ComponentSchema, TransformStage } from '@ali/lowcode-types'; // just use types -import { BuiltinSimulatorRenderer, NodeInstance, Component } from '@ali/lowcode-designer'; +import { BuiltinSimulatorRenderer, NodeInstance, Component, DocumentModel, Node } from '@ali/lowcode-designer'; import Slot from './builtin-components/slot'; import Leaf from './builtin-components/leaf'; -export class SimulatorRenderer implements BuiltinSimulatorRenderer { - readonly isSimulatorRenderer = true; - private dispose?: () => void; - - constructor() { - if (!host) { - return; - } - - this.dispose = host.connect(this, () => { - // sync layout config - - // sync schema - this._schema = host.document.export(1); - - // todo: split with others, not all should recompute - if (this._libraryMap !== host.libraryMap || this._componentsMap !== host.designer.componentsMap) { - this._libraryMap = host.libraryMap || {}; - this._componentsMap = host.designer.componentsMap; - this.buildComponents(); - } - - // sync designMode - this._designMode = host.designMode; - - // sync suspended - - // sync scope - - // sync device - this._device = host.device; - }); - host.componentsConsumer.consume(async (componentsAsset) => { - if (componentsAsset) { - await this.load(componentsAsset); - this.buildComponents(); - } - }); - host.injectionConsumer.consume((data) => { - // sync utils, i18n, contants,... config - this._appContext = { - utils: {}, - constants: { - name: 'demo', - }, - }; - }); - } - - @computed get layout(): any { - // TODO: parse layout Component - return null; - } +export class DocumentInstance { + private instancesMap = new Map(); @obx.ref private _schema?: RootSchema; @computed get schema(): any { return this._schema; } - private _libraryMap: { [key: string]: string } = {}; - private buildComponents() { - this._components = buildComponents(this._libraryMap, this._componentsMap); - } - @obx.ref private _components: any = {}; - @computed get components(): object { - // 根据 device 选择不同组件,进行响应式 - // 更好的做法是,根据 device 选择加载不同的组件资源,甚至是 simulatorUrl - return this._components; - } - // context from: utils、constants、history、location、match - @obx.ref private _appContext = {}; - @computed get context(): any { - return this._appContext; - } - @obx.ref private _designMode: string = 'design'; - @computed get designMode(): any { - return this._designMode; - } - @obx.ref private _device: string = 'default'; - @computed get device() { - return this._device; - } - @obx.ref private _componentsMap = {}; - @computed get componentsMap(): any { - return this._componentsMap; + + constructor(readonly container: SimulatorRendererContainer, readonly document: DocumentModel) { + this.dispose = host.connect(this, () => { + // sync layout config + + // sync schema + this._schema = host.document.export(1); + }) } + @computed get suspended(): any { return false; } @computed get scope(): any { return null; } - /** - * 加载资源 - */ - load(asset: Asset): Promise { - return loader.load(asset); - } - private instancesMap = new Map(); private unmountIntance(id: string, instance: ReactInstance) { const instances = this.instancesMap.get(id); if (instances) { const i = instances.indexOf(instance); if (i > -1) { instances.splice(i, 1); - host.setInstance(id, instances); + host.setInstance(this.document.id, id, instances); } } } + mountInstance(id: string, instance: ReactInstance | null) { + const docId = this.document.id; const instancesMap = this.instancesMap; if (instance == null) { let instances = this.instancesMap.get(id); @@ -131,10 +58,10 @@ export class SimulatorRenderer implements BuiltinSimulatorRenderer { instances = instances.filter(checkInstanceMounted); if (instances.length > 0) { instancesMap.set(id, instances); - host.setInstance(id, instances); + host.setInstance(this.document.id, id, instances); } else { instancesMap.delete(id); - host.setInstance(id, null); + host.setInstance(this.document.id, id, null); } } return; @@ -163,6 +90,7 @@ export class SimulatorRenderer implements BuiltinSimulatorRenderer { } (instance as any)[SYMBOL_VNID] = id; + (instance as any)[SYMBOL_VDID] = docId; let instances = this.instancesMap.get(id); if (instances) { const l = instances.length; @@ -179,37 +107,11 @@ export class SimulatorRenderer implements BuiltinSimulatorRenderer { instances = [instance]; } instancesMap.set(id, instances); - host.setInstance(id, instances); + host.setInstance(this.document.id, id, instances); } - private ctxMap = new Map(); - mountContext(id: string, ctx: object) { - this.ctxMap.set(id, ctx); - } - - getComponent(componentName: string) { - const paths = componentName.split('.'); - const subs: string[] = []; - - while (true) { - const component = this._components[componentName]; - if (component) { - return getSubComponent(component, subs); - } - - const sub = paths.pop(); - if (!sub) { - return null; - } - subs.unshift(sub); - componentName = paths.join('.'); - } - - return null; - } - - getComponentInstances(id: string): ReactInstance[] | null { - return this.instancesMap.get(id) || null; + mountContext(docId: string, id: string, ctx: object) { + // this.ctxMap.set(id, ctx); } createComponent(schema: ComponentSchema): Component | null { @@ -245,18 +147,18 @@ export class SimulatorRenderer implements BuiltinSimulatorRenderer { if (schema.children && schema.children.length > 0) { children = schema.children.map((item: any) => getElement(componentsMap, item, propsMap)); } - const _leaf = host.document.designer.currentDocument?.createNode(schema); - const node = host.document.createNode(schema); + const _leaf = this.document.designer.currentDocument?.createNode(schema); + const node = this.document.createNode(schema); let props = processPropsSchema(schema.props, propsMap); - props = host.document.designer.transformProps(props, node, TransformStage.Init); - props = host.document.designer.transformProps(props, node, TransformStage.Render); + props = this.document.designer.transformProps(props, node, TransformStage.Init); + props = this.document.designer.transformProps(props, node, TransformStage.Render); return createElement(Com, {...props, _leaf}, children); } - const renderer = this; + const container = this.container; class Com extends React.Component { render() { - const componentsMap = renderer.componentsMap; + const componentsMap = container.componentsMap; let children = null; if (_schema.children && Array.isArray(_schema.children)) { children = _schema.children?.map((item:any) => getElement(componentsMap, item, this.props)); @@ -268,6 +170,119 @@ export class SimulatorRenderer implements BuiltinSimulatorRenderer { return Com; } + getNode(id: string): Node | null { + return this.document.getNode(id); + } +} + +export class SimulatorRendererContainer implements BuiltinSimulatorRenderer { + readonly isSimulatorRenderer = true; + private dispose?: () => void; + + constructor() { + if (!host) { + return; + } + + this.dispose = host.connect(this, () => { + // sync layout config + // todo: split with others, not all should recompute + if (this._libraryMap !== host.libraryMap || this._componentsMap !== host.designer.componentsMap) { + this._libraryMap = host.libraryMap || {}; + this._componentsMap = host.designer.componentsMap; + this.buildComponents(); + } + + // sync designMode + this._designMode = host.designMode; + + // sync device + this._device = host.device; + }); + host.componentsConsumer.consume(async (componentsAsset) => { + if (componentsAsset) { + await this.load(componentsAsset); + this.buildComponents(); + } + }); + host.injectionConsumer.consume((data) => { + // sync utils, i18n, contants,... config + this._appContext = { + utils: {}, + constants: { + name: 'demo', + }, + }; + }); + } + + @computed get layout(): any { + // TODO: parse layout Component + return null; + } + + private _libraryMap: { [key: string]: string } = {}; + private buildComponents() { + this._components = buildComponents(this._libraryMap, this._componentsMap); + } + @obx.ref private _components: any = {}; + @computed get components(): object { + // 根据 device 选择不同组件,进行响应式 + // 更好的做法是,根据 device 选择加载不同的组件资源,甚至是 simulatorUrl + return this._components; + } + // context from: utils、constants、history、location、match + @obx.ref private _appContext = {}; + @computed get context(): any { + return this._appContext; + } + @obx.ref private _designMode: string = 'design'; + @computed get designMode(): any { + return this._designMode; + } + @obx.ref private _device: string = 'default'; + @computed get device() { + return this._device; + } + @obx.ref private _componentsMap = {}; + @computed get componentsMap(): any { + return this._componentsMap; + } + /** + * 加载资源 + */ + load(asset: Asset): Promise { + return loader.load(asset); + } + + private documentInstanceMap = new Map(); + + + redirect(path: string) { + + } + + getComponent(componentName: string) { + const paths = componentName.split('.'); + const subs: string[] = []; + + while (true) { + const component = this._components[componentName]; + if (component) { + return getSubComponent(component, subs); + } + + const sub = paths.pop(); + if (!sub) { + return null; + } + subs.unshift(sub); + componentName = paths.join('.'); + } + + return null; + } + getClosestNodeInstance(from: ReactInstance, nodeId?: string): NodeInstance | null { return getClosestNodeInstance(from, nodeId); } @@ -310,8 +325,8 @@ export class SimulatorRenderer implements BuiltinSimulatorRenderer { document.documentElement.classList.add('engine-page'); document.body.classList.add('engine-document'); // important! Stylesheet.invoke depends - reactRender(createElement(SimulatorRendererView, { renderer: this }), container); - host.document.setRendererReady(this); + reactRender(createElement(SimulatorRendererView, { rendererContainer: this }), container); + host.project.setRendererReady(this); } } @@ -417,6 +432,7 @@ function cacheReactKey(el: Element): Element { } const SYMBOL_VNID = Symbol('_LCNodeId'); +const SYMBOL_VDID = Symbol('_LCDocId'); function getClosestNodeInstance(from: ReactInstance, specId?: string): NodeInstance | null { let el: any = from; @@ -430,9 +446,11 @@ function getClosestNodeInstance(from: ReactInstance, specId?: string): NodeInsta while (el) { if (SYMBOL_VNID in el) { const nodeId = el[SYMBOL_VNID]; + const docId = el[SYMBOL_VDID]; if (!specId || specId === nodeId) { return { - nodeId: nodeId, + docId, + nodeId, instance: el, }; } @@ -450,9 +468,11 @@ function getNodeInstance(fiberNode: any, specId?: string): NodeInstance