2020-08-13 15:48:31 +08:00

203 lines
6.2 KiB
TypeScript

import LowCodeRenderer from '@ali/lowcode-react-renderer';
import { ReactInstance, Fragment, Component, createElement } from 'react';
import { observer } from '@recore/obx-react';
import { SimulatorRendererContainer, DocumentInstance } from './renderer';
import { Router, Route, Switch } from 'react-router';
import './renderer.less';
// patch cloneElement avoid lost keyProps
const originCloneElement = window.React.cloneElement;
(window as any).React.cloneElement = (child: any, { _leaf, ...props }: any = {}, ...rest: any[]) => {
if (child.ref && props.ref) {
const dRef = props.ref;
const cRef = child.ref;
props.ref = (x: any) => {
if (cRef) {
if (typeof cRef === 'function') {
cRef(x);
} else {
try {
cRef.current = x;
} catch (e) {}
}
}
if (dRef) {
if (typeof dRef === 'function') {
dRef(x);
} else {
try {
dRef.current = x;
} catch (e) {}
}
}
};
}
return originCloneElement(child, props, ...rest);
};
export default class SimulatorRendererView extends Component<{ rendererContainer: SimulatorRendererContainer }> {
render() {
const { rendererContainer } = this.props;
return (
<Router history={rendererContainer.history}>
<Layout rendererContainer={rendererContainer}>
<Routes rendererContainer={rendererContainer} />
</Layout>
</Router>
);
}
}
@observer
export class Routes extends Component<{ rendererContainer: SimulatorRendererContainer }> {
render() {
const { rendererContainer } = this.props;
return (
<Switch>
{rendererContainer.documentInstances.map((instance) => {
return (
<Route
path={instance.path}
key={instance.id}
render={(routeProps) => <Renderer documentInstance={instance} {...routeProps} />}
/>
);
})}
</Switch>
);
}
}
function ucfirst(s: string) {
return s.charAt(0).toUpperCase() + s.substring(1);
}
function getDeviceView(view: any, device: string, mode: string) {
if (!view || typeof view === 'string') {
return view;
}
// compatible vision Mobile | Preview
device = ucfirst(device);
if (device === 'Mobile' && view.hasOwnProperty(device)) {
view = view[device];
}
mode = ucfirst(mode);
if (mode === 'Preview' && view.hasOwnProperty(mode)) {
view = view[mode];
}
return view;
}
@observer
class Layout extends Component<{ rendererContainer: SimulatorRendererContainer }> {
shouldComponentUpdate() {
return false;
}
render() {
const { rendererContainer, children } = this.props;
const layout = rendererContainer.layout;
if (layout) {
const { Component, props } = layout;
return <Component props={props}>{children}</Component>;
}
return <Fragment>{children}</Fragment>;
}
}
@observer
class Renderer extends Component<{ documentInstance: DocumentInstance }> {
shouldComponentUpdate() {
return false;
}
render() {
const { documentInstance } = this.props;
const { container } = documentInstance;
const { designMode, device } = container;
return (
<LowCodeRenderer
schema={documentInstance.schema}
components={container.components}
appHelper={container.context}
designMode={designMode}
suspended={documentInstance.suspended}
self={documentInstance.scope}
customCreateElement={(Component: any, props: any, children: any) => {
const { __id, __desingMode, ...viewProps } = props;
viewProps.componentId = __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;
if (props.children && props.children.length) {
if (Array.isArray(props.children)) {
_children = Array.isArray(_children)
? _children.concat(props.children)
: props.children.unshift(_children);
} else {
(Array.isArray(_children) && _children.push(props.children)) ||
(_children = [_children].push(props.children));
}
}
// 如果是容器 && 无children && 高宽为空 增加一个占位容器,方便拖动
if (
leaf?.isContainer() &&
(_children == null || !_children.length) &&
(!viewProps.style || Object.keys(viewProps.style).length == 0)
) {
_children = (
<div
style={{
height: '66px',
backgroundColor: '#f0f0f0',
borderColor: '#a7b1bd',
border: '1px dotted',
color: '#a7b1bd',
textAlign: 'center',
lineHeight: '66px',
}}
>
</div>
);
}
if (viewProps._componentName === 'Menu') {
Object.assign(viewProps, {
_componentName: 'Menu',
className: '_css_pesudo_menu_kbrzyh0f',
context: { VE: (window as any).VisualEngine },
direction: undefined,
events: { ignored: true },
fieldId: 'menu_kbrzyh0f',
footer: '',
header: '',
mode: 'inline',
onItemClick: { ignored: true },
onSelect: { ignored: true },
popupAlign: 'follow',
selectMode: false,
triggerType: 'click',
});
}
return createElement(getDeviceView(Component, device, designMode), viewProps, _children);
}}
onCompGetRef={(schema: any, ref: ReactInstance | null) => {
documentInstance.mountInstance(schema.id, ref);
}}
//onCompGetCtx={(schema: any, ctx: object) => {
// documentInstance.mountContext(schema.id, ctx);
//}}
/>
);
}
}