mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-02-09 07:35:38 +00:00
245 lines
6.8 KiB
TypeScript
245 lines
6.8 KiB
TypeScript
import { isJSBlock, isJSExpression, isJSSlot, isI18nData } from '@ali/lowcode-types';
|
|
import { isPlainObject, hasOwnProperty } from '@ali/lowcode-utils';
|
|
import { globalContext, Editor } from '@ali/lowcode-editor-core';
|
|
import { Designer, LiveEditing, TransformStage, Node } from '@ali/lowcode-designer';
|
|
import Outline, { OutlineBackupPane, getTreeMaster } from '@ali/lowcode-plugin-outline-pane';
|
|
import { toCss } from '@ali/vu-css-style';
|
|
import logger from '@ali/vu-logger';
|
|
import bus from './bus';
|
|
import { VE_EVENTS } from './base/const';
|
|
|
|
import DesignerPlugin from '@ali/lowcode-plugin-designer';
|
|
import { Skeleton, SettingsPrimaryPane } from '@ali/lowcode-editor-skeleton';
|
|
|
|
import { deepValueParser } from './deep-value-parser';
|
|
import { liveEditingRule, liveEditingSaveHander } from './vc-live-editing';
|
|
|
|
export const editor = new Editor();
|
|
globalContext.register(editor, Editor);
|
|
|
|
export const skeleton = new Skeleton(editor);
|
|
editor.set(Skeleton, skeleton);
|
|
editor.set('skeleton', skeleton);
|
|
|
|
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);
|
|
});
|
|
});
|
|
|
|
interface Variable {
|
|
type: 'variable';
|
|
variable: string;
|
|
value: any;
|
|
}
|
|
|
|
function isVariable(obj: any): obj is Variable {
|
|
return obj && obj.type === 'variable';
|
|
}
|
|
|
|
function upgradePropsReducer(props: any) {
|
|
if (!isPlainObject(props)) {
|
|
return props;
|
|
}
|
|
const newProps: any = {};
|
|
Object.entries<any>(props).forEach(([key, val]) => {
|
|
if (/^__slot__/.test(key) && val === true) {
|
|
return;
|
|
}
|
|
if (isJSBlock(val)) {
|
|
if (val.value.componentName === 'Slot') {
|
|
val = {
|
|
type: 'JSSlot',
|
|
title: (val.value.props as any)?.slotTitle,
|
|
value: val.value.children
|
|
};
|
|
} else {
|
|
val = val.value;
|
|
}
|
|
}
|
|
// todo: deep find
|
|
if (isVariable(val)) {
|
|
val = {
|
|
type: 'JSExpression',
|
|
value: val.variable,
|
|
mock: val.value,
|
|
};
|
|
}
|
|
newProps[key] = val;
|
|
});
|
|
return newProps;
|
|
}
|
|
// 升级 Props
|
|
designer.addPropsReducer(upgradePropsReducer, TransformStage.Init);
|
|
|
|
// 节点 props 初始化
|
|
designer.addPropsReducer((props, node) => {
|
|
// run initials
|
|
const initials = node.componentMeta.getMetadata().experimental?.initials;
|
|
if (initials) {
|
|
const newProps: any = {};
|
|
initials.forEach((item) => {
|
|
// FIXME! this implements SettingTarget
|
|
try {
|
|
// FIXME! item.name could be 'xxx.xxx'
|
|
const ov = props[item.name];
|
|
const v = item.initial(node as any, isJSExpression(ov) ? ov.mock : ov);
|
|
if (v !== undefined) {
|
|
newProps[item.name] = isJSExpression(ov) ? {
|
|
...ov,
|
|
mock: v,
|
|
} : v;
|
|
}
|
|
} catch (e) {
|
|
if (hasOwnProperty(props, item.name)) {
|
|
newProps[item.name] = props[item.name];
|
|
}
|
|
}
|
|
});
|
|
return newProps;
|
|
}
|
|
return props;
|
|
}, TransformStage.Init);
|
|
|
|
|
|
function filterReducer(props: any, node: Node): any {
|
|
const filters = node.componentMeta.getMetadata().experimental?.filters;
|
|
if (filters && filters.length) {
|
|
const newProps = { ...props };
|
|
filters.forEach((item) => {
|
|
// FIXME! item.name could be 'xxx.xxx'
|
|
if (!hasOwnProperty(newProps, item.name)) {
|
|
return;
|
|
}
|
|
try {
|
|
if (item.filter(node.settingEntry.getProp(item.name), props[item.name]) === false) {
|
|
delete newProps[item.name];
|
|
}
|
|
} catch (e) {
|
|
console.warn(e);
|
|
logger.trace(e);
|
|
}
|
|
});
|
|
return newProps;
|
|
}
|
|
return props;
|
|
}
|
|
designer.addPropsReducer(filterReducer, TransformStage.Save);
|
|
designer.addPropsReducer(filterReducer, TransformStage.Render);
|
|
|
|
function compatiableReducer(props: any) {
|
|
if (!isPlainObject(props)) {
|
|
return props;
|
|
}
|
|
const newProps: any = {};
|
|
Object.entries<any>(props).forEach(([key, val]) => {
|
|
if (isJSSlot(val)) {
|
|
val.value
|
|
val = {
|
|
type: 'JSBlock',
|
|
value: {
|
|
componentName: 'Slot',
|
|
children: val.value,
|
|
props: {
|
|
slotTitle: val.title,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
newProps[key] = val;
|
|
});
|
|
return newProps;
|
|
}
|
|
// FIXME: Dirty fix, will remove this reducer
|
|
designer.addPropsReducer(compatiableReducer, TransformStage.Save);
|
|
|
|
// 设计器组件样式处理
|
|
function stylePropsReducer(props: any, node: any) {
|
|
if (props && typeof props === 'object' && props.__style__) {
|
|
const cssId = '_style_pesudo_' + node.id.replace(/\$/g, '_');
|
|
const cssClass = '_css_pesudo_' + node.id.replace(/\$/g, '_');
|
|
const styleProp = props.__style__;
|
|
appendStyleNode(props, styleProp, cssClass, cssId);
|
|
}
|
|
if (props && typeof props === 'object' && props.pageStyle) {
|
|
const cssId = '_style_pesudo_engine-document';
|
|
const cssClass = 'engine-document';
|
|
const styleProp = props.pageStyle;
|
|
appendStyleNode(props, styleProp, cssClass, cssId);
|
|
}
|
|
if (props && typeof props === 'object' && props.containerStyle) {
|
|
const cssId = '_style_pesudo_' + node.id;
|
|
const cssClass = '_css_pesudo_' + node.id.replace(/\$/g, '_');
|
|
const styleProp = props.containerStyle;
|
|
appendStyleNode(props, styleProp, cssClass, cssId);
|
|
}
|
|
return props;
|
|
}
|
|
|
|
function appendStyleNode(props: any, styleProp: any, cssClass: string, cssId: string) {
|
|
const doc = designer.currentDocument?.simulator?.contentDocument;
|
|
if (!doc) {
|
|
return;
|
|
}
|
|
const dom = doc.getElementById(cssId);
|
|
if (dom) {
|
|
dom.parentNode?.removeChild(dom);
|
|
}
|
|
if (typeof styleProp === 'object') {
|
|
styleProp = toCss(styleProp);
|
|
}
|
|
if (typeof styleProp === 'string') {
|
|
const s = doc.createElement('style');
|
|
props.className = cssClass;
|
|
s.setAttribute('type', 'text/css');
|
|
s.setAttribute('id', cssId);
|
|
doc.getElementsByTagName('head')[0].appendChild(s);
|
|
|
|
s.appendChild(doc.createTextNode(styleProp.replace(/:root/g, '.' + cssClass)));
|
|
}
|
|
}
|
|
designer.addPropsReducer(stylePropsReducer, TransformStage.Render);
|
|
|
|
// 国际化 & Expression 渲染时处理
|
|
designer.addPropsReducer(deepValueParser, TransformStage.Render);
|
|
|
|
skeleton.add({
|
|
area: 'mainArea',
|
|
name: 'designer',
|
|
type: 'Widget',
|
|
content: DesignerPlugin,
|
|
});
|
|
skeleton.add({
|
|
area: 'rightArea',
|
|
name: 'settingsPane',
|
|
type: 'Panel',
|
|
content: SettingsPrimaryPane,
|
|
});
|
|
skeleton.add({
|
|
area: 'leftArea',
|
|
name: 'outlinePane',
|
|
type: 'PanelDock',
|
|
content: Outline,
|
|
panelProps: {
|
|
area: 'leftFixedArea',
|
|
},
|
|
});
|
|
skeleton.add({
|
|
area: 'rightArea',
|
|
name: 'backupOutline',
|
|
type: 'Panel',
|
|
props: {
|
|
condition: () => {
|
|
return designer.dragon.dragging && !getTreeMaster(designer).hasVisibleTreeBoard();
|
|
}
|
|
},
|
|
content: OutlineBackupPane,
|
|
});
|
|
|
|
LiveEditing.addLiveEditingSpecificRule(liveEditingRule);
|
|
LiveEditing.addLiveEditingSaveHandler(liveEditingSaveHander);
|